mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
191 lines
6.3 KiB
Lua
191 lines
6.3 KiB
Lua
-- ../pokecrystal/home/text.asm:548 PromptText, :566 DoneText
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
love = love or require("tests.love_stub")
|
|
|
|
local Json = require("src.link.Json")
|
|
local Extractor = require("src.import.RomExtractorGen2")
|
|
local TextBox = require("src.render.TextBox")
|
|
local Sound = require("src.core.Sound")
|
|
local Vm = require("src.script.gen2.Vm")
|
|
local Events = require("src.world.gen2.Events")
|
|
local romText = require("src.core.RomText")
|
|
local CommonText = require("src.core.gen2.CommonText")
|
|
|
|
local presses = 0
|
|
local realPress, realPlay = Sound.playPress, Sound.play
|
|
Sound.playPress = function() presses = presses + 1 return nil end
|
|
Sound.play = function() return nil end
|
|
|
|
local function manifest(path)
|
|
local file = assert(io.open(path, "r"))
|
|
local data = assert(Json.decode(file:read("*a")))
|
|
file:close()
|
|
return data
|
|
end
|
|
|
|
do
|
|
local charmap = manifest("tools/rom_manifest_crystal.json").charmap
|
|
local function decode(bytes)
|
|
local rom = {}
|
|
function rom:byte(_, address) return bytes[address] or 0x50 end
|
|
function rom:word(_, address)
|
|
return (bytes[address] or 0) + (bytes[address + 1] or 0) * 0x100
|
|
end
|
|
local extractor = setmetatable({ rom = rom, edition = "crystal" }, Extractor)
|
|
return extractor:decodeGen2Text(0, 0, charmap)
|
|
end
|
|
local H, i = 0x87, 0xa8
|
|
eq(decode({ [0] = 0x00, H, i, 0x57 }), "Hi{DONE}",
|
|
"a `done` text carries {DONE}")
|
|
eq(decode({ [0] = 0x00, H, i, 0x58 }), "Hi{PROMPT}",
|
|
"a `prompt` text carries {PROMPT}")
|
|
eq(decode({ [0] = 0x00, H, i, 0x50, 0x50 }), "Hi",
|
|
"a `text_end` text carries nothing")
|
|
eq(decode({ [0] = 0x57 }), "", "an empty text stays empty")
|
|
end
|
|
|
|
eq(TextBox.ending("Hi{DONE}"), "done", "TextBox reads {DONE}")
|
|
eq(TextBox.ending("Hi{PROMPT}"), "prompt", "and {PROMPT}")
|
|
eq(TextBox.ending("Hi"), nil, "and nothing for an untagged text")
|
|
|
|
local function newGame()
|
|
local game = {
|
|
save = { player = {}, options = { textSpeed = "FAST" }, generation = 2 },
|
|
data = { text = {} },
|
|
}
|
|
game.stack = {
|
|
states = {},
|
|
push = function(self, s) table.insert(self.states, s) end,
|
|
pop = function(self) return table.remove(self.states) end,
|
|
top = function(self) return self.states[#self.states] end,
|
|
}
|
|
game.input = {
|
|
queue = {},
|
|
wasPressed = function(self, btn) return self.queue[btn] or false end,
|
|
isDown = function(self, btn) return self.queue[btn] or false end,
|
|
}
|
|
return game
|
|
end
|
|
|
|
local function runOut(box)
|
|
local frames = 0
|
|
while not box.done and frames < 600 do
|
|
box:update(1 / 60)
|
|
frames = frames + 1
|
|
end
|
|
end
|
|
|
|
local function finished(text, opts)
|
|
local game = newGame()
|
|
local box = TextBox.new(game, text, nil, opts)
|
|
game.stack:push(box)
|
|
runOut(box)
|
|
presses = 0
|
|
game.input.queue.a = true
|
|
box:update(1 / 60)
|
|
return box, presses
|
|
end
|
|
|
|
-- ../pokecrystal/maps/Route30.asm:302 YoungsterMikeySeenText
|
|
do
|
|
local box, beeps = finished("I'm gonna win,\nfor sure!{DONE}")
|
|
eq(box.waitButton, true, "an extracted `done` text is a WaitButton box")
|
|
check(not box:arrowVisible(), "and shows no blinking cursor")
|
|
eq(beeps, 0, "and closes silently")
|
|
eq(box.pages[1][1], "I'm gonna win,", "the marker never reaches the glyphs")
|
|
eq(box.pages[1][2], "for sure!", "on either line")
|
|
end
|
|
|
|
do
|
|
local box, beeps = finished("I'm gonna win,\nfor sure!{PROMPT}")
|
|
eq(box.waitButton, false, "an extracted `prompt` text is a PromptButton box")
|
|
check(box:arrowVisible(), "and shows the cursor")
|
|
eq(beeps, 1, "and beeps on the press")
|
|
eq(box.pages[1][2], "for sure!", "with the marker stripped")
|
|
end
|
|
|
|
do
|
|
local box = finished("Hi{DONE}", { waitButton = false })
|
|
eq(box.waitButton, false,
|
|
"a `done` text followed by promptbutton keeps the caller's cursor")
|
|
box = finished("Hi{PROMPT}", { waitButton = true })
|
|
eq(box.waitButton, false,
|
|
"but a `prompt` text always blinks, whatever the script does after it")
|
|
box = finished("Hi", { waitButton = true })
|
|
eq(box.waitButton, true, "an untagged text keeps the caller's flag")
|
|
box = finished("Hi")
|
|
eq(box.waitButton, nil, "and with no flag stays the Gen 1 default")
|
|
end
|
|
|
|
-- ../pokecrystal/engine/events/trainer_scripts.asm:19
|
|
local function boxFor(text, tail)
|
|
local scripts = {
|
|
generation = 2,
|
|
["s:t"] = {
|
|
{ op = "opentext" },
|
|
{ op = "writetext", text = "t:x" },
|
|
{ op = tail },
|
|
{ op = "closetext" },
|
|
{ op = "end" },
|
|
},
|
|
}
|
|
local game = newGame()
|
|
local box
|
|
local vm = Vm.new(scripts, { ["t:x"] = text }, Events.new(), {
|
|
showText = function(body, onDone, stay, hold, sfxWait, arrows)
|
|
box = TextBox.new(game, body, onDone, { waitButton = not arrows })
|
|
game.stack:push(box)
|
|
end,
|
|
})
|
|
check(vm:start("s:t"), "the script starts")
|
|
for _ = 1, 50 do vm:update() end
|
|
check(box, "the box went up")
|
|
runOut(box)
|
|
return box
|
|
end
|
|
|
|
check(not boxFor("Hi{DONE}", "waitbutton"):arrowVisible(),
|
|
"done + waitbutton: no cursor")
|
|
check(boxFor("Hi{DONE}", "promptbutton"):arrowVisible(),
|
|
"done + promptbutton: the cursor the script asked for")
|
|
check(boxFor("Hi{PROMPT}", "waitbutton"):arrowVisible(),
|
|
"prompt + waitbutton: the cursor the text asked for")
|
|
check(not boxFor("Hi", "waitbutton"):arrowVisible(),
|
|
"an untagged script text still follows the lookahead")
|
|
|
|
Sound.playPress, Sound.play = realPress, realPlay
|
|
|
|
do
|
|
local data = { text = {
|
|
SubTookDamageText = "The SUBSTITUTE\ntook damage for\v{TARGET}!{PROMPT}",
|
|
HitTimesText = "Hit {NUM} times!{DONE}",
|
|
} }
|
|
eq(romText(data, "SubTookDamageText", "fallback", "GEODUDE"),
|
|
"The SUBSTITUTE\ntook damage for\vGEODUDE!{PROMPT}",
|
|
"RomText does not count the terminator as a slot")
|
|
eq(romText(data, "HitTimesText", "fallback", 3), "Hit 3 times!{DONE}",
|
|
"and leaves it on the string for the box to read")
|
|
end
|
|
|
|
do
|
|
local pages = CommonText.pages("Coins:\n{NUM}{DONE}")
|
|
eq(pages[1][1], "Coins:", "CommonText pages drop the terminator")
|
|
eq(pages[1][2], "{NUM}", "and keep the runtime slots")
|
|
eq(CommonText.plain("Hi{PROMPT}"), "Hi", "CommonText.plain strips it")
|
|
eq(CommonText.plain(nil), nil, "and passes nil through")
|
|
end
|
|
|
|
do
|
|
local game = newGame()
|
|
eq(TextBox.substitute(game, "Hi{DONE}"), "Hi",
|
|
"substitute drops {DONE}")
|
|
eq(TextBox.substitute(game, "Hi{PROMPT}"), "Hi",
|
|
"and {PROMPT}")
|
|
end
|
|
|
|
T.finish("gen2 text terminator bug2045")
|