Showing posts with label daemons. Show all posts
Showing posts with label daemons. Show all posts

Friday, March 22, 2013

"new fuse" progress

I really was liking this new fuse system, but the fact that using it seemed to limit even a small game to one UNDO was killing me. As far as I could tell, the problem was my call to "runevents" after an UNDO and other xverb commands. It just was taking up too much memory.

I decided just now that I'd code my own version of "runevents" that I'd run in such cases. I call it... fake_runevents!

The new "NEW_FUSE" code:
#ifclear NO_FUSES

#ifset NEW_FUSE
property fuse_length alias e_to

replace fuse
{
    type fuse
    size 0
    timer
    {
        if self.fuse_length
            return (self.fuse_length - counter)
        else
            return 0
    }
    fuse_length 0
    in_scope 0
    tick
    {
        local a
        a = self.timer
        if a <= 0
            self.fuse_length = 0
#ifset DEBUG
        if debug_flags & D_FUSES
        {
            print "[Running fuse "; number self; ":  timer = ";
            print number a; "]"
        }
#endif

        if a = 0
            Deactivate(self)
        return a
    }
}

property timer_start alias fuse_length
property fake_event alias s_to

replace daemon
{
    type daemon
    size 0
    in_scope 0
    timer
    {
        if self.timer_start
        {
            return (counter - self.timer_start)
        }
        else
            return 0
    }
}

object fuse_bucket
{}

routine fake_runevents
{
    local i
    for i in fuse_bucket
    {
!        if i.type = fuse
!        {
            run i.fake_event
!        }
    }
}
#endif  ! NEW_FUSE

!\
Activate - added a warning for when it is called before the player global has been set.
\!
replace Activate(a, set)                ! <set> is for fuses only
{
    local err
    if not player
        {
        Font(BOLD_ON)
        print "[WARNING:  The player global must be set before
        daemon (object "; number a;") can be activated.]"
        err = true
        }
#ifset NEW_FUSE
    move a to fuse_bucket
#endif
    a.in_scope = player
    a is active
    if a.type = fuse and not err
    {
        if set
#ifclear NEW_FUSE
            a.timer = set
#else
            a.fuse_length = (counter + set)
#endif

        run a.activate_event
    }
    elseif a.type = daemon and not err
    {
#ifclear NEW_FUSE
        if set and not a.#timer
#else
        if set and not a.#timer_start
#endif
        {
            Font(BOLD_ON)
            print "[WARNING:  Attempt to set nonexistent timer
                property on daemon (object "; number a; ")]"
            err = true
        }
        else
#ifclear NEW_FUSE
            a.timer = set
#else
            a.timer_start = (counter - set)
#endif
        run a.activate_event
    }
    elseif not err
    {
        Font(BOLD_ON)
        print "[WARNING:  Attempt to activate non-fuse/\
        daemon (object "; number a; ")]"
        err = true
    }

#ifset DEBUG
    if debug_flags & D_FUSES and not err
    {
        print "[Activating "; a.name; " "; number a;
        if a.type = fuse
            print " (timer = "; number a.timer; ")";
        print "]"
    }
#endif
    if err
        {
        Font(BOLD_OFF)
        "\npress a key to continue..."
        HiddenPause
        }
    return (not err)
}

replace Deactivate(a)
{
    local err

    remove a
    a.in_scope = 0
    a is not active

    if a.type ~= fuse and a.type ~= daemon
    {
        print "[WARNING:  Attempt to deactivate non-fuse/\
            daemon (object "; number a; ")]"
        err = true
    }
    else
    {
        run a.deactivate_event
    }

#ifset DEBUG
    if debug_flags & D_FUSES and not err
    {
        print "[Deactivating "; a.name; " "; number a; "]"
    }
#endif

    return (not err)
}
#endif ! ifclear NO_FUSES
So, you copy your "event in <fuse or daemon>" code to the fuse's fake_event property. Of course, like the old "new fuse" stuff, whether or not your code properly prints stuff will be based on whether your code uses the new counter-based timing effectively.

Anyhow, my guess is that "runevents" uses a fair amount of memory since it's doing a lot of FindObject-y stuff behind the scenes, and this method of helping Hugo find the fuses and daemons quicker seems to help my test game out a good amount.

Friday, September 14, 2012

library suggestions

So far, roodylib replaces 54 standard library routines or objects (that number might be off by some; I sort of counted carelessly by hand). Some of these cover justified inadequacies or bugs uncovered by others; some are only replaced since an object something inherits from was also replaced. Still, yet, there are those that I replace because I feel there is something wrong with the design of something, and as it is clear that much of Hugo is the result of fine reasoning, it is likely that my changes have flaws I have not yet considered.

Anyway, let's talk about some of these kinds of changes. For starters, I replaced the female_character class. In the standard library, that class gets its own type ('female_character"), so if you have a game where you are checking if objects are NPCs, you have to check for both "character" and "female_character". I changed it so female_character objects are of type "character", as you can always check for the female attribute if you are truly looking for a female NPC.

More recently, I was writing some code that assumed the player_character object was of type "player_character". Funnily enough, this was not the case (it is actually of type "character")! Now, maybe people won't agree with me on this one, but I feel the player_character should get its own type, so in roodylib, it currently does so.

Much further back, I changed up some of the vehicle class code so it'd be (hopefully) easier to change which directions can exit a vehicle:
replace vehicle
{
    type vehicle
    vehicle_verb "drive"            ! default verb
    prep "in", "out"                !   "     prepositions
    vehicle_move true               ! by default, always ready to move

#ifclear NO_VERBS
    before
    {
    parent(player) DoGo
        {
        if word[2] = "out" and object = self
            {
            object = out_obj
            return false
            }
        if (object ~= u_obj, out_obj) and object.type = direction
            {
            ! "To walk, you'll have to get out..."
            OMessage(vehicle, 1, self)
            return true
            }
        else
            return object
        }
    }
#endif
    is enterable, static
}
(DoGo also has some slightly different code, I believe)

Lately, I also was looking at something whose design I disagree with but will do nothing about. I was taking a look at GetInput, which is a short and simple routine:
routine GetInput(p)
{
    print p;
    input
}
Personally, I'd think that you'd only call GetInput when you want to also provide a prompt, so it should be something like this:

routine GetInput(p)
{
    if p
        print p;
    else
        print prompt;
    input
}
 My logic is, hey, only call GetInput when you want a prompt of some point. Otherwise, why not just call input directly? Thing is, the standard library (and other existing code) call GetInput without a prompt argument a lot. I could either change all of those library routines (breaking lots of game source, too, in the process, probably) or just ignore it. I'll just do the latter. I could add a slightly-similar-sounding routine to roodylib that behaves like I feel GetInput should, but hey, I admit this isn't an important problem...

So yeah, that.

In other news, I updated Activate, the routine for starting daemons, so it complains if it is called before the player global has been set (I've run into that problem once or twice):
replace Activate(a, set)                ! <set> is for fuses only
{
    local err
    if not player
        {
        Font(BOLD_ON)
        print "[WARNING:  The player global must be set before
        daemon (object "; number a;") can be activated.]"
        err = true
        }
    a.in_scope = player
    a is active
    if a.type = fuse and not err
    {
        if set
            a.timer = set

        run a.activate_event
    }
    elseif a.type = daemon and not err
    {
        if set and not a.#timer
        {
            Font(BOLD_ON)
            print "[WARNING:  Attempt to set nonexistent timer
                property on daemon (object "; number a; ")]"
            err = true
        }
        else
            a.timer = set

        run a.activate_event
    }
    elseif not err
    {
        Font(BOLD_ON)
        print "[WARNING:  Attempt to activate non-fuse/\
        daemon (object "; number a; ")]"
        err = true
    }

#ifset DEBUG
    if debug_flags & D_FUSES and not err
    {
        print "[Activating "; a.name; " "; number a;
        if a.type = fuse
            print " (timer = "; number a.timer; ")";
        print "]"
    }
#endif
    if err
        {
        Font(BOLD_OFF)
        "\npress a key to continue..."
        HiddenPause
        }
    return (not err)
}
So yeah, that's all some stuff.