lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Well, rather than describe how I do it, why not post some code? If
you've ever programmed in UnrealScript, this will feel rather
familiar.

Here's the implementation (using coroutines; the implementation was a
lot messier when we used lua 4!) and a sample blocking call:

-- Called from some external place, once per tick
function Ob:onTick(dt)
    local fn = rawget(self, 'current_statefunc')
    if fn then
        local bSuccess, result = coroutine.resume(fn, self, dt)
        if not bSuccess then
            Tracef("Error running state '%s':\n%s", self.current_state, result)
            self:setState(nil)
        else
            if coroutine.status(fn) == "dead" then
                self.current_statefunc = nil
            end
        end
    end
end

-- pass string new_state
-- Similar to Psychonauts API
function Ob:setState(new_state)
    -- Call endState__()
    if not self.inEndState and self.current_state then
        local lastStateName = self.current_state
        local endfunc = self['endState' .. self.current_state];
        self.inEndState = true
        if endfunc then endfunc(self, new_state) end
        self.inEndState = false

        -- If the endfunc hijacked the transition to new_state by
        -- calling setState(), don't continue on and call beginState;
        -- it will be a duplicate call.
        if self.current_state ~= lastStateName then return nil end
    end

    -- Call beginState__()
    if type(new_state) == "string" then
        self.current_state = new_state

        -- create a coroutine for the function Ob:stateFoo()
        -- this is the most important bit.
        local func = self['state' .. self.current_state]
        if func then
            self.current_statefunc = coroutine.create(func)
        end

        local beginfunc = self['beginState' .. self.current_state]
        if beginfunc then beginfunc(self) end
    else
        self.current_state = nil
        self.current_statefunc = nil
    end

    return true
end

function Ob:sleep(time)
    -- use ">=" so sleep() always yields at least once
    while time >= 0 do
        local _,dt = coroutine.yield()
        time = time - dt
    end
end

And some sample client code:

function Ob:stateWaitingToDie()
	self:sleep(self.active_lifetime)
	self:setActive(false)
	self:sleep(self.inactive_lifetime)
	self:killSelf()
end

p