Skip to content

Commit

Permalink
Fixes, example update
Browse files Browse the repository at this point in the history
  • Loading branch information
S0lll0s committed Feb 23, 2016
1 parent eba1ff7 commit ba0bfbd
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 27 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ All _States_ in the currently active _Stack_ will run in parallel (i.e. receive

Usage
-----
You can add and remove _Stacks_ (elements on the primary stack) using `pause` and `resume`.
You can add and remove _States_ (elements on the secondary stack) using `push` and `pop`.
Require `st8.lua` to and keep the return value around.
Call `hook()` to hook `st8.lua` up to the LÖVE handlers/callbacks.
Using `order(event, order)` the execution order for a single event can be changed, pass anything but `"bottom"` or `"bottom-up"` to execute the events top-down.
The *bottom-up* order is recommended for example for the `draw` callback.

All methods accept a variable number of arguments, these arguments will be passed on both to the last _State_(s) **and** the new ones.
`init` and `pause` accept lists or single _States_ as arguments.
You can add and remove _Stacks_ (elements on the primary stack) using `pause(state_or_stack)` and `resume()`.
You can add and remove _States_ (elements on the secondary stack) using `push(state)` and `pop()`.
You can swap States using `swap(old, new)` and remove a specific State using `remove(state)`.

All methods accept additional arguments, these will be passed on both to the last _State_(s) **and** the new ones.
`pause` accepts lists or single _States_ as arguments.

Events
------
Expand Down
5 changes: 3 additions & 2 deletions example/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ function textbox(text, x, y, r, g, b)
end

function love.load()
St8.init(require"states.menu") -- start running the MainMenu state
St8.hook() -- subscribe to LÖVE callbacks
St8.push(require"states.menu") -- start running the MainMenu state
St8.order("draw", "bottom") -- make draw run bottom-to-top so specific gamestates draw over general ones
font = love.graphics.getFont() -- let's not have _too_ much overhead
end
end
40 changes: 30 additions & 10 deletions example/states/game.lua
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
local Game, PlayerMoveState, EnemyMoveState, ExitOverlay = St8.new(), St8.new(), St8.new(), St8.new()
local Game, PlayerMoveState, EnemyMoveState, ExitOverlay = {}, {}, {}, {}
local player, enemy, xvel, yvel, score, time, etime

-- game
-- ┏━╸┏━┓┏┳┓┏━╸
-- ┃╺┓┣━┫┃┃┃┣╸
-- ┗━┛╹ ╹╹ ╹┗━╸
function Game:enter()
player = {x=100, y=100}
enemy = {x=400, y=400}
xvel, yvel = 0, 0
score, time, etime = 0, 0, 0

St8.push(EnemyMoveState)
end

function Game:draw()
love.graphics.setColor(70, 70, 70)
for x=0,800,20 do
Expand All @@ -17,15 +22,16 @@ function Game:draw()
for y=0,600,20 do
love.graphics.line(0, y, 800, y)
end

love.graphics.setColor(255, 255, 155)
love.graphics.circle("fill", player.x, player.y, 20)
love.graphics.setColor(255, 155, 155)
love.graphics.circle("fill", enemy.x, enemy.y, 15)

love.graphics.print("Score: "..score, 740, 20)
love.graphics.print("arrows - move; esc - back to main menu; p - pause", 10, 577)
end

-- using ':' so `self` becomes the previous callback's return value
function Game:keypressed(key)
if self then return self end -- skip handling if this key was handled already
Expand All @@ -36,36 +42,45 @@ function Game:keypressed(key)
end
end

-- enemy move
-- ┏━╸┏┓╻┏━╸┏┳┓╻ ╻ ┏┳┓┏━┓╻ ╻┏━╸
-- ┣╸ ┃┗┫┣╸ ┃┃┃┗┳┛ ┃┃┃┃ ┃┃┏┛┣╸
-- ┗━╸╹ ╹┗━╸╹ ╹ ╹ ╹ ╹┗━┛┗┛ ┗━╸
function EnemyMoveState:enter()
if math.pow(enemy.x-player.x,2)+math.pow(enemy.y-player.y,2) <= 900 then -- if touching
score = score+1
end
end

function EnemyMoveState:draw()
love.graphics.setColor(255, 255, 255)
love.graphics.circle("fill", enemy.x, enemy.y, 6)
end

function EnemyMoveState:update(dt)
time = time+dt
etime = etime+dt
if time > 2 then
time = 0
St8.pop(EnemyMoveState)
St8.push(PlayerMoveState)
St8.replace(EnemyMoveState, PlayerMoveState)
end
enemy.x, enemy.y = love.math.noise(1, etime/4)*800, love.math.noise(-1, etime/3)*600
end

-- player move
-- ┏━┓╻ ┏━┓╻ ╻┏━╸┏━┓ ┏┳┓┏━┓╻ ╻┏━╸
-- ┣━┛┃ ┣━┫┗┳┛┣╸ ┣┳┛ ┃┃┃┃ ┃┃┏┛┣╸
-- ╹ ┗━╸╹ ╹ ╹ ┗━╸╹┗╸ ╹ ╹┗━┛┗┛ ┗━╸
function PlayerMoveState:draw()
love.graphics.setColor(0, 0, 0)
love.graphics.circle("fill", player.x, player.y, 11)
end

function PlayerMoveState:update(dt)
time = time+dt
if time > 2 then
time = 0
St8.pop(PlayerMoveState)
St8.push(EnemyMoveState)
St8.replace(PlayerMoveState, EnemyMoveState)
end
xvel, yvel = xvel/1.25, yvel/1.25
if love.keyboard.isDown"left" then
Expand All @@ -83,15 +98,20 @@ function PlayerMoveState:update(dt)
player.x, player.y = player.x + xvel*dt, player.y + yvel*dt
end

-- exit overlay
-- ┏━╸╻ ╻╻╺┳╸ ┏━┓╻ ╻┏━╸┏━┓╻ ┏━┓╻ ╻
-- ┣╸ ┏╋┛┃ ┃ ┃ ┃┃┏┛┣╸ ┣┳┛┃ ┣━┫┗┳┛
-- ┗━╸╹ ╹╹ ╹ ┗━┛┗┛ ┗━╸╹┗╸┗━╸╹ ╹ ╹
function ExitOverlay:draw()
textbox("Really exit? y/n", 10, 10)
end

function ExitOverlay:keypressed(key)
if key == "y" then
St8.resume()
elseif key == "n" then
St8.pop(ExitOverlay)
St8.remove(ExitOverlay)
end
end

return Game
return Game
12 changes: 8 additions & 4 deletions example/states/menu.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
local MainMenu = St8.new()
local MainMenu = {}

-- menu
-- ┏┳┓┏━╸┏┓╻╻ ╻
-- ┃┃┃┣╸ ┃┗┫┃ ┃
-- ╹ ╹┗━╸╹ ╹┗━┛
function MainMenu:draw()
textbox("< press space to start a new game >", 100, 300)
textbox("o for options menu", 100, 317)
Expand All @@ -8,12 +12,12 @@ end
-- using ':' so `self` becomes the previous callback's return value
function MainMenu:keypressed(key)
if self then return self end -- skip handling if this key was handled already
if key == " " then

if key == "space" then
St8.pause(require"states.game")
elseif key == "o" then
St8.push(require"states.options", "pop")
end
end

return MainMenu
return MainMenu
8 changes: 6 additions & 2 deletions example/states/options.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
local OptionsMenu = St8.new()
local OptionsMenu = {}

-- options
-- ┏━┓┏━┓╺┳╸╻┏━┓┏┓╻┏━┓
-- ┃ ┃┣━┛ ┃ ┃┃ ┃┃┗┫┗━┓
-- ┗━┛╹ ╹ ╹┗━┛╹ ╹┗━┛
function OptionsMenu:draw()
textbox("Options: f to toggle fullscreen", 280, 150, 125, 20, 20)
textbox(" esc to resume whatever you were doing", 280, 167, 125, 20, 20)
Expand All @@ -16,4 +20,4 @@ function OptionsMenu:keypressed(key)
end
end

return OptionsMenu
return OptionsMenu
8 changes: 6 additions & 2 deletions example/states/pausemenu.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
local PauseMenu = St8.new()
local PauseMenu = {}

-- pause menu
-- ┏━┓┏━┓╻ ╻┏━┓┏━╸ ┏┳┓┏━╸┏┓╻╻ ╻
-- ┣━┛┣━┫┃ ┃┗━┓┣╸ ┃┃┃┣╸ ┃┗┫┃ ┃
-- ╹ ╹ ╹┗━┛┗━┛┗━╸ ╹ ╹┗━╸╹ ╹┗━┛
function PauseMenu:draw()
textbox("< p to resume game >", 280, 300)
textbox("o for options menu", 280, 317)
Expand All @@ -15,4 +19,4 @@ function PauseMenu:keypressed(key)
end
end

return PauseMenu
return PauseMenu
100 changes: 99 additions & 1 deletion spec/st8_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,111 @@ describe("St8", function ()
describe("when popping States", function ()
before_each(function ()
one, two, three = {}, {}, {}
stub_all(one, "exit", "draw")
stub_all(two, "exit", "draw")
stub_all(three, "exit", "draw")

St8.push(one)
end)

it("calls exit", function ()
stub(one, "exit")
St8.push(two)
St8.pop()

assert.stub(one.exit).was_not_called()
assert.stub(two.exit).was_called()
end)

it("pops the topmost State", function ()
St8.push(two)
St8.push(three)

St8.draw()

assert.stub( one.draw).was_called()
assert.stub( two.draw).was_called()
assert.stub(three.exit).was_not_called()
end)
end)

describe("when removing States", function ()
before_each(function ()
one, two, three = {}, {}, {}
stub_all(one, "exit", "draw")
stub_all(two, "exit", "draw")
stub_all(three, "exit", "draw")

St8.push(one)
St8.push(two)
St8.push(three)
end)

it("calls exit", function ()
St8.remove(one)

assert.stub( one.exit).was_called()
assert.stub( two.exit).was_not_called()
assert.stub(three.exit).was_not_called()
end)

it("doesn't screw up", function ()
St8.remove(two)

St8.draw()

assert.stub( one.exit).was_not_called()
assert.stub( two.exit).was_called()
assert.stub(three.exit).was_not_called()

assert.stub( one.draw).was_called()
assert.stub( two.draw).was_not_called()
assert.stub(three.draw).was_called()
end)

it("silently fails", function ()
assert.has_no.error(function ()
St8.remove(one)
St8.remove(one)
St8.remove(one)
end)

assert.stub(one.exit).was_called(1)
end)
end)

describe("when swapping States", function ()
before_each(function ()
one, two, three = {}, {}, {}
stub_all(one, "enter", "exit", "draw")
stub_all(two, "enter", "exit", "draw")
stub_all(three, "enter", "exit", "draw")

St8.push(one)
end)

it("calls enter and exit", function ()
St8.swap(one, two)

assert.stub(one.exit).was_called()
assert.stub(two.enter).was_called()

St8.swap(two, three)

assert.stub(one.exit).was_called(1)
assert.stub(two.enter).was_called(1)
assert.stub(two.exit).was_called(1)
assert.stub(three.enter).was_called(1)
end)

it("works", function ()
St8.push(three)
St8.swap(one, two)

St8.draw()

assert.stub( one.draw).was_not_called()
assert.stub( two.draw).was_called()
assert.stub(three.draw).was_called()
end)
end)

Expand Down
45 changes: 43 additions & 2 deletions st8.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
local St8 = {
_VERSION = "St8 v1.1",
_VERSION = "St8 v2.1",
_DESCRIPTION = "A tiny double-stacked state manging library for Lua/LÖVE",
_LICENSE = [[
MIT LICENSE
Expand Down Expand Up @@ -78,6 +78,15 @@ function St8.bind_instance(state)
return setmetatable(proxy, {__index=state})
end

function St8.debug_trace()
for i, stack in ipairs(stacks) do
print("Stack #" .. i .. ":")
for i,v in ipairs(stack) do
print("", i,v)
end
end
end

-- stack manipulation
-- ┏━┓╺┳╸┏━┓┏━╸╻┏ ┏┳┓┏━┓┏┓╻╻┏━┓╻ ╻╻ ┏━┓╺┳╸╻┏━┓┏┓╻
-- ┗━┓ ┃ ┣━┫┃ ┣┻┓ ┃┃┃┣━┫┃┗┫┃┣━┛┃ ┃┃ ┣━┫ ┃ ┃┃ ┃┃┗┫
Expand All @@ -97,7 +106,39 @@ function St8.pop(...)
error("no State left to pop")
end
local s = table.remove(stacks[#stacks])
return s.exit(...)
if s.exit then
return s.exit(...)
end
end

-- remove the specified State from current Stack
function St8.remove(state, ...)
for i, s in ipairs(stacks[#stacks]) do
if s == state then
table.remove(stacks[#stacks], i)
if s.exit then
return s.exit(...)
end
return
end
end
end

-- swap out the specified State on the current Stack
function St8.swap(old, new, ...)
for i, s in ipairs(stacks[#stacks]) do
if s == old then
stacks[#stacks][i] = new
if old.exit then
old.exit(...)
end
if new.enter then
return new.enter(...)
end
return
end
end
error("old State not on Stack")
end

-- pause the current Stack and run a new Stack or State
Expand Down

0 comments on commit ba0bfbd

Please sign in to comment.