Yet another state system

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

Yet another state system

Post by HDPLocust »

Here simple state-system, working like average state automaton.
States can be required from files, returns a table with the methods.
Each state has its own local field, which does not affect the global variables and other states.
Example:

Code: Select all

state = require 'gamestate'

menu = {}
function menu:load(x, y)	--method calls every gamestate:set('statename', ...), can be called with any arguments
	--'self' is local namespase for that state
	self.x = x or 10
	self.y = y or 20
end

function menu:unload()	--method is called when this state is replaced by another, using gamestate:set('statename', ...).
	self.x, self.y = nil, nil
end

function menu:keypressed(key)
	if key == 'return' then state:set('game') end
end

function menu:draw()
	love.graphics.print('state: menu', self.x, self.y)
end

game = {}
function game:load(...)
	self.x = 10
	self.y = 20
end

function game:keypressed(key)
	if key == 'return' then state:set('menu', math.random(300), math.random(200)) end
end

function game:draw()
	love.graphics.print('state: game', self.x, self.y)
end

state:new('menu', menu)
state:new('game', game)
state:set('menu')

--if love[callbackname] is defined, is required to use state:[callbackname] method.
function love.keypressed(key)
	if key == 'escape' then love.event.quit() end
	state:keypressed(key)
end
Full list of callbacks here: https://love2d.org/wiki/love.

Easy example attached.
Upd: autotracker handlers.
Attachments
states.love
(2.55 KiB) Downloaded 296 times
Last edited by HDPLocust on Sat Jul 02, 2016 4:39 pm, edited 1 time in total.
Science and violence
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

Re: Yet another state system

Post by HDPLocust »

I use it for easy separate states to list of files and control every state without using global strings.

Next features in development - stack of states and multiple states in one time.
Science and violence
cval
Citizen
Posts: 58
Joined: Sun Apr 20, 2014 2:15 pm
Location: Ukraine

Re: Yet another state system

Post by cval »

The thing that kinda worries me with libraries and helpers that are appearing lately and that rely on, process or affect engine callbacks in any way, is that when people implement "keypressed" callback (for example), they sometimes forget about the fact that this function actually accepts and passes three arguments, according to wiki:

Code: Select all

love.keypressed( key, scancode, isrepeat )
"Scancode" appeared in 0.10 and whereas it's not really mandatory to write your library functions exactly like that (and if you aim for compatibility with older versions - not necessary at all), people who use it later in their project may experience bugs that sometimes aren't easy to track. You know, because their code may expect those omitted arguments and process it somehow to change state of their program flow.

Just wanted to point that out (=
Kinda personal preference actually, but it cost me a few hours of headache once just because i had to change similar piece of code in my project for it to work as intended.
User avatar
zorg
Party member
Posts: 3436
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Yet another state system

Post by zorg »

Calling functions the following way will ensure that a lib designer has no responsibility for any changes in passed arguments:

Code: Select all

a_function(...)
-- example:
function love.keypressed(...)
    currentState.keypressed(...)
end
-- where currentState has a function like this, though param names don't really matter all that much:
state.keypressed(key, scancode, isrepeat)
Granted this won't work if the function is not defined in the state, but that's another bag of tricks to deal with :3
(currentState.keypressed and currentState.keypressed(...) is one solution, but one could or it with an empty function too)
i do prefer hump gamestates myself though.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

Re: Yet another state system

Post by HDPLocust »

zorg wrote:Granted this won't work if the function is not defined in the state, but that's another bag of tricks to deal with :3
(currentState.keypressed and currentState.keypressed(...) is one solution, but one could or it with an empty function too)
cval wrote: "Scancode" appeared in 0.10 and whereas it's not really mandatory to write your library functions exactly like that (and if you aim for compatibility with older versions - not necessary at all), people who use it later in their project may experience bugs that sometimes aren't easy to track. You know, because their code may expect those omitted arguments and process it somehow to change state of their program flow.
I use function generation from list of callbacknames, and this tricks too.
Any callback called with multiple arguments, passes through "...", like original callbacks.

Code: Select all

--This simplifies the addition of new callbacks with a totally any arguments.
local callbacks = {
'keypressed', 'keyreleased', 'textinput', 'textedited',
'mousepressed', 'mousereleased', 'mousefocus', 'mousemoved', 'wheelmoved', 
'touchpressed', 'touchreleased', 'touchmoved', 
'directorydropped', 'filedropped', 
'quit', 'lowmemory', 
'focus', 'visible', 
'threaderror', 'errhand',
'update', 'draw',
'gamepadaxis', 'gamepadpressed', 'gamepadreleased', 
'joystickadded', 'joystickaxis', 'joystickhat',
'joystickpressed', 'joystickreleased', 'joystickremoved'
}
--Self and additional love callback initialisation
--(load/update/draw/keypressed/keyreleased etc)
function states:init()
	for _, k in ipairs(callbacks) do
		--initialisation self methods like self:update(dt)
		self[k] = function(self, ...)
			if self.current[k] and type(self.current[k]) == 'function' then
				--passed "self" and any number of arguments, with any names and etc.
				self.current[k](self.current, ...)
			end
		end
		--optional addition methods for love, if not defined
		if not love[k] then
			love[k] = function(...)
				self[k](self, ...)
			end
		end
	end
	return self
end
function anystate:keypressed(key, scancode, isRepeat) {code} end - works fine too.
In love 0.10. Indeed, in another version, they will not be passed arguments "scancode" and "isrepeat".
Heh.

I can't say that "the designer no responsibility".
Rather, it is the possibility of using the library on all versions of love.


Yea, callbacknames forbidden for use:

Code: Select all

--Function returns new state like object
local function state(t)
	return setmetatable({}, {
		--table with methods
		__index = t,
		--forbidding reserved names 
		__newindex = function(self, key, value)
				--checking if key is not named like any callback in list
				if not isReserved(key) then
					rawset(self, key, value)
				else
					error('Trying to modify reserved callback "'..key..'"', 2)
				end 
			end 
		}
	)
end
I'm sorry, I do not use github, and therefore the source code on pastebin.
Little comments included.
http://pastebin.com/9rgH4BAj
Science and violence
cval
Citizen
Posts: 58
Joined: Sun Apr 20, 2014 2:15 pm
Location: Ukraine

Re: Yet another state system

Post by cval »

If you really want to cover all callbacks (like, all of them that are defined in love.handlers) you can fill your callback names table with following code on table init. (i.e. you don't need to fill table manually)

Code: Select all

local i = 1
	for k,v in pairs(love.handlers) do
		print(k,v) -- this one just prints them
		callbacks[i] = tostring(k)
		i = i+1
	end
This way your lib will automatically track if there is a new callback/handler in upcoming updates.
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

Re: Yet another state system

Post by HDPLocust »

cval wrote:If you really want to cover all callbacks (like, all of them that are defined in love.handlers) you can fill your callback names table with following code on table init.

Code: Select all

for k,v in pairs(love.handlers) do
	print(k,v) -- this one just prints them
	table.insert(callbacks, tostring(k))
end
This way your lib will automatically track if there is a new callback/handler in upcoming updates.
Wow, nice!
I use it, thanks :3

Love handler no update/draw callbacks,
but they just can be added manually, and the rest of the track automatically.
Science and violence
cval
Citizen
Posts: 58
Joined: Sun Apr 20, 2014 2:15 pm
Location: Ukraine

Re: Yet another state system

Post by cval »

HDPLocust wrote:
Love handler no update/draw callbacks,
It is because of how love.run works by default, you can see that if update/draw it is defined by user, it will be called, otherwise it is skipped in run loop.
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

Re: Yet another state system

Post by HDPLocust »

cval wrote:
HDPLocust wrote:
Love handler no update/draw callbacks,
It is because of how love.run works by default, you can see that if update/draw it is defined by user, it will be called, otherwise it is skipped in run loop.
Every user i know, uses update and draw :3
Is no require to uses love.graphics.present and structuring code.
Hm, restrictions.
Science and violence
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

Re: Yet another state system

Post by HDPLocust »

So. Now it have state-stack with overriding. I need to make the doc.
Science and violence
Post Reply

Who is online

Users browsing this forum: No registered users and 48 guests