244 lines
7.5 KiB
Text
244 lines
7.5 KiB
Text
-- Forge as a game (FORGE.md): the chooser, the engine callbacks, and the editing that
|
|
-- needs no pointer -- entities added, renamed and deleted, a rule built from the pickers, undo.
|
|
--
|
|
-- Unlike scene54 to 56 this does not set FORGE_LIBRARY: Forge installs its own callbacks, and
|
|
-- the scene drives them the way the engine would, two integers to onKeyPressed. What is
|
|
-- asserted is that every key reaches the description and that a rename reaches the rules.
|
|
dofile("Forge/Forge.singe")
|
|
|
|
local forgeUpdate = onOverlayUpdate
|
|
local frames = 0
|
|
local stage = 0
|
|
local opened = nil
|
|
|
|
|
|
local function key(code, character)
|
|
onKeyPressed(character or 0, code.value)
|
|
end
|
|
|
|
|
|
local function typeIn(text)
|
|
for c in string.gmatch(text, ".") do
|
|
key(SCANCODE.A, string.byte(c))
|
|
end
|
|
end
|
|
|
|
|
|
local function step()
|
|
if stage == 1 then
|
|
-- The chooser is up and offers a new game at least.
|
|
if FORGE.game ~= nil then
|
|
debugPrint("GAME FAIL the editor is open before anything was chosen")
|
|
end
|
|
debugPrint("GAME the chooser offers " .. #forgeFiles() .. " entries")
|
|
-- The last entry is always a new game.
|
|
for _ = 1, 20 do
|
|
key(SCANCODE.DOWN)
|
|
end
|
|
key(SCANCODE.RETURN)
|
|
if FORGE.game == nil then
|
|
debugPrint("GAME FAIL ENTER on the chooser opened nothing")
|
|
else
|
|
opened = FORGE.path
|
|
debugPrint("GAME opened " .. opened .. " with " .. #forgeRoom().entities .. " entities")
|
|
end
|
|
|
|
elseif stage == 2 then
|
|
-- Add an entity, name it, and make it a hero by typing the fields ENTER walks.
|
|
local count = #forgeRoom().entities
|
|
|
|
key(SCANCODE.A)
|
|
if #forgeRoom().entities ~= count + 1 then
|
|
debugPrint("GAME FAIL A did not add an entity")
|
|
end
|
|
key(SCANCODE.RETURN) -- id
|
|
typeIn("coin")
|
|
key(SCANCODE.RETURN) -- type
|
|
typeIn("hero")
|
|
key(SCANCODE.RETURN) -- x
|
|
typeIn("300")
|
|
key(SCANCODE.RETURN) -- y
|
|
typeIn("200")
|
|
key(SCANCODE.RETURN) -- z
|
|
key(SCANCODE.ESCAPE) -- untouched
|
|
local coin = forgeRoom().entities[#forgeRoom().entities]
|
|
|
|
if coin.id == "coin" and coin.type == "hero" and coin.x == 300 and coin.y == 200 then
|
|
debugPrint("GAME typed an entity in: " .. coin.id .. " the " .. coin.type .. " at " .. coin.x .. "," .. coin.y)
|
|
else
|
|
debugPrint("GAME FAIL the typed entity is " .. tostring(coin.id) .. " the " .. tostring(coin.type) .. " at " .. tostring(coin.x) .. "," .. tostring(coin.y))
|
|
end
|
|
|
|
elseif stage == 3 then
|
|
-- Renaming the hero has to reach every rule that names it.
|
|
key(SCANCODE.UP)
|
|
key(SCANCODE.UP)
|
|
key(SCANCODE.UP)
|
|
key(SCANCODE.UP)
|
|
key(SCANCODE.DOWN) -- ground, then hero
|
|
key(SCANCODE.RETURN)
|
|
typeIn("player")
|
|
key(SCANCODE.ESCAPE) -- ESC puts it back: the hero keeps its name
|
|
if forgeRoom().entities[FORGE.selected].id ~= "hero" then
|
|
debugPrint("GAME FAIL escape did not put the name back")
|
|
end
|
|
key(SCANCODE.RETURN)
|
|
typeIn("player")
|
|
key(SCANCODE.RETURN) -- commits, moves on to x
|
|
key(SCANCODE.ESCAPE)
|
|
local named = 0
|
|
|
|
for _, rule in ipairs(FORGE.game.rules) do
|
|
for _, item in ipairs(rule.act) do
|
|
if item.entity == "player" then
|
|
named = named + 1
|
|
end
|
|
end
|
|
end
|
|
if forgeRoom().entities[FORGE.selected].id == "player" and named == 3 then
|
|
debugPrint("GAME renamed the hero and " .. named .. " actions followed")
|
|
else
|
|
debugPrint("GAME FAIL rename: id " .. tostring(forgeRoom().entities[FORGE.selected].id) .. ", " .. named .. " actions renamed")
|
|
end
|
|
|
|
elseif stage == 4 then
|
|
-- A rule from the pickers: N, E picks an event, C a condition, T an action.
|
|
local rules = #FORGE.game.rules
|
|
|
|
key(SCANCODE.TAB) -- types,
|
|
key(SCANCODE.TAB) -- rules.
|
|
key(SCANCODE.N)
|
|
key(SCANCODE.E)
|
|
key(SCANCODE.DOWN)
|
|
key(SCANCODE.RETURN)
|
|
key(SCANCODE.C)
|
|
if FORGE.picker == nil then
|
|
debugPrint("GAME FAIL C opened no picker")
|
|
end
|
|
key(SCANCODE.DOWN)
|
|
key(SCANCODE.RETURN)
|
|
key(SCANCODE.T)
|
|
key(SCANCODE.RETURN)
|
|
local rule = FORGE.game.rules[#FORGE.game.rules]
|
|
|
|
if #FORGE.game.rules == rules + 1 and #rule.when == 1 and #rule.act == 1 and rule.on ~= "frame" then
|
|
debugPrint("GAME built a rule from the pickers: on " .. rule.on .. " when " .. rule.when[1][1] .. " then " .. rule.act[1][1])
|
|
else
|
|
debugPrint("GAME FAIL the picked rule has " .. #(rule.when or {}) .. " conditions and " .. #(rule.act or {}) .. " actions")
|
|
end
|
|
|
|
elseif stage == 5 then
|
|
-- Undo takes the action back, then the condition, the event, then the rule; redo brings
|
|
-- the rule back, and a change by hand ends the redo history.
|
|
local rules = #FORGE.game.rules
|
|
|
|
key(SCANCODE.U)
|
|
key(SCANCODE.U)
|
|
key(SCANCODE.U)
|
|
key(SCANCODE.U)
|
|
if #FORGE.game.rules == rules - 1 then
|
|
debugPrint("GAME three undos took the rule back out")
|
|
else
|
|
debugPrint("GAME FAIL after three undos there are " .. #FORGE.game.rules .. " rules, not " .. (rules - 1))
|
|
end
|
|
key(SCANCODE.R)
|
|
if (#FORGE.game.rules == rules) and (#FORGE.game.rules[rules].when == 0) then
|
|
debugPrint("GAME redo brought the empty rule back")
|
|
else
|
|
debugPrint("GAME FAIL redo left " .. #FORGE.game.rules .. " rules")
|
|
end
|
|
key(SCANCODE.U)
|
|
key(SCANCODE.N)
|
|
key(SCANCODE.R)
|
|
if #FORGE.redo == 0 and #FORGE.game.rules == rules then
|
|
debugPrint("GAME a new change emptied the redo history")
|
|
else
|
|
debugPrint("GAME FAIL redo history survived a change: " .. #FORGE.redo .. " kept")
|
|
end
|
|
key(SCANCODE.U)
|
|
|
|
elseif stage == 6 then
|
|
-- Duplicate and delete in the entity list.
|
|
key(SCANCODE.TAB) -- tracks,
|
|
key(SCANCODE.TAB) -- entities.
|
|
local count = #forgeRoom().entities
|
|
|
|
key(SCANCODE.D)
|
|
key(SCANCODE.DELETE)
|
|
if #forgeRoom().entities == count then
|
|
debugPrint("GAME duplicate then delete leaves " .. count .. " entities")
|
|
else
|
|
debugPrint("GAME FAIL duplicate then delete leaves " .. #forgeRoom().entities)
|
|
end
|
|
|
|
elseif stage == 7 then
|
|
-- ESC on unsaved work warns, S saves, ESC then leaves; the chooser lists the file.
|
|
key(SCANCODE.ESCAPE)
|
|
if FORGE.game == nil then
|
|
debugPrint("GAME FAIL one ESC closed unsaved work")
|
|
end
|
|
key(SCANCODE.S)
|
|
key(SCANCODE.ESCAPE)
|
|
if FORGE.game ~= nil then
|
|
debugPrint("GAME FAIL ESC did not close a saved description")
|
|
end
|
|
local listed = false
|
|
|
|
for _, entry in ipairs(forgeFiles()) do
|
|
if entry.path == opened then
|
|
listed = true
|
|
end
|
|
end
|
|
debugPrint("GAME closed; the chooser lists what was saved: " .. tostring(listed))
|
|
|
|
elseif stage == 8 then
|
|
-- Open it again from the chooser -- it is listed first -- and the rename is in the file.
|
|
for _ = 1, 20 do
|
|
key(SCANCODE.UP)
|
|
end
|
|
key(SCANCODE.RETURN)
|
|
if FORGE.game == nil then
|
|
debugPrint("GAME FAIL could not reopen the saved description")
|
|
else
|
|
local found = false
|
|
|
|
for _, entity in ipairs(forgeRoom().entities) do
|
|
if entity.id == "player" then
|
|
found = true
|
|
end
|
|
end
|
|
debugPrint("GAME reopened " .. FORGE.path .. "; the rename was saved: " .. tostring(found))
|
|
end
|
|
|
|
elseif stage == 9 then
|
|
-- B builds beside the runtime, which is what makes the result playable.
|
|
key(SCANCODE.B)
|
|
local runtime = io.open(singeGetDataPath() .. "Author.singe", "r")
|
|
|
|
if runtime == nil then
|
|
debugPrint("GAME FAIL build left no runtime beside the preview")
|
|
else
|
|
runtime:close()
|
|
debugPrint("GAME the build put the runtime beside the preview")
|
|
end
|
|
debugPrint("GAME RESULT done")
|
|
end
|
|
end
|
|
|
|
|
|
function onOverlayUpdate()
|
|
frames = frames + 1
|
|
if frames % 12 == 0 and stage < 10 then
|
|
stage = stage + 1
|
|
step()
|
|
end
|
|
forgeUpdate()
|
|
if frames == 6 or frames == 40 or frames == 60 or frames == 100 then
|
|
singeScreenshot()
|
|
end
|
|
if frames > 130 then
|
|
singeQuit()
|
|
end
|
|
|
|
return OVERLAY_UPDATED
|
|
end
|