Fix Karate Master gate

This commit is contained in:
johnjohto
2026-07-31 09:47:53 -04:00
parent 4c5f776128
commit 84bf25fcd5
5 changed files with 139 additions and 12 deletions
+23
View File
@@ -171,6 +171,28 @@ local function dojoBall(species, ownBall, otherBall, askKey)
end end
end end
-- FightingDojoDefaultScript does not use the Master's facing direction for
-- this battle. It checks exactly the cell left of him, turns both sprites
-- toward one another, and then displays his trainer text. Keeping this out
-- of trainer sight also lets him keep his original downward-facing pose.
local function dojoMasterGate(game, ow, x, y)
if x ~= 4 or y ~= 3 or game.save.flags.EVENT_BEAT_KARATE_MASTER then
return false
end
local master
for _, npc in ipairs(ow.npcs) do
if npc.def and npc.def.name == "FIGHTINGDOJO_KARATE_MASTER" then
master = npc
break
end
end
if not master or ow:trainerDefeated(master) then return false end
ow.player.facing = "right"
master:facePlayer(ow.player)
ow:engageTrainer(master)
return true
end
M.FIGHTING_DOJO = { M.FIGHTING_DOJO = {
talk = { talk = {
TEXT_FIGHTINGDOJO_HITMONLEE_POKE_BALL = TEXT_FIGHTINGDOJO_HITMONLEE_POKE_BALL =
@@ -197,6 +219,7 @@ M.FIGHTING_DOJO = {
end end
return false return false
end, end,
onStep = dojoMasterGate,
} }
-- ------------------------------------------------------------------- -- -------------------------------------------------------------------
+3 -3
View File
@@ -144,8 +144,8 @@ end
-- no def_trainers row (DisplayTextID routes to his ASM script), so the -- no def_trainers row (DisplayTextID routes to his ASM script), so the
-- extractor emits headers only for the four blackbelts ([2]..[5]). Seed -- extractor emits headers only for the four blackbelts ([2]..[5]). Seed
-- object index [1] so he behaves like the real leader: -- object index [1] so he behaves like the real leader:
-- * range 4 (matches the strongest blackbelt) -> CheckFightingMapTrainers -- * range 0: FightingDojoDefaultScript, not trainer sight, starts his
-- spots the player in his DOWN line and he challenges on sight (#197), -- battle from the single tile to his left (#495),
-- * battle = his pre-battle challenge, won = "Hwa! Arrgh! Beaten!", -- * battle = his pre-battle challenge, won = "Hwa! Arrgh! Beaten!",
-- * after = the "Stay and train at Karate with us!" re-talk line. -- * after = the "Stay and train at Karate with us!" re-talk line.
-- Deliberately NO `event`: EVENT_BEAT_KARATE_MASTER is owned by -- Deliberately NO `event`: EVENT_BEAT_KARATE_MASTER is owned by
@@ -160,7 +160,7 @@ function Data:seedFightingDojoKarateMaster()
headers.FightingDojo = headers.FightingDojo or {} headers.FightingDojo = headers.FightingDojo or {}
if headers.FightingDojo[1] then return end if headers.FightingDojo[1] then return end
headers.FightingDojo[1] = { headers.FightingDojo[1] = {
range = 4, range = 0,
battle = "_FightingDojoKarateMasterText", battle = "_FightingDojoKarateMasterText",
won = "_FightingDojoKarateMasterDefeatedText", won = "_FightingDojoKarateMasterDefeatedText",
after = "_FightingDojoKarateMasterStayAndTrainWithUsText", after = "_FightingDojoKarateMasterStayAndTrainWithUsText",
+11 -9
View File
@@ -1,6 +1,6 @@
-- Driver: Fighting Dojo Karate Master bundle (#197). -- Driver: Fighting Dojo Karate Master bundle (#197).
-- Six sub-bugs live in FIGHTING_DOJO (scripts/FightingDojo.asm): -- Six sub-bugs live in FIGHTING_DOJO (scripts/FightingDojo.asm):
-- BUG1 no aggro -- the master has no trainer header so range=0 -- BUG1 gate -- the master stops the player on the tile to his left
-- BUG2 no speech -- no won text + no prize dialogue after the win -- BUG2 no speech -- no won text + no prize dialogue after the win
-- BUG3 wrong re-talk -- shows the pre-battle challenge, not the after line -- BUG3 wrong re-talk -- shows the pre-battle challenge, not the after line
-- BUG4 (verify) -- the ball ask() is the Gen1 descriptor, not a dex entry -- BUG4 (verify) -- the ball ask() is the Gen1 descriptor, not a dex entry
@@ -112,21 +112,23 @@ return function(game)
------------------------------------------------------------------ ------------------------------------------------------------------
local hdr = game.data:trainerHeader("FightingDojo", 1) local hdr = game.data:trainerHeader("FightingDojo", 1)
check(hdr ~= nil, "BUG1/2/3: Karate Master trainer header (index 1) exists") check(hdr ~= nil, "BUG1/2/3: Karate Master trainer header (index 1) exists")
check(hdr and (hdr.range or 0) > 0, "BUG1: master has a sight range") check(hdr and (hdr.range or 0) == 0,
"BUG1: master relies on the exact-tile gate, not trainer sight")
check(hdr and hdr.won ~= nil, "BUG2: master has a won (defeat) text") check(hdr and hdr.won ~= nil, "BUG2: master has a won (defeat) text")
check(hdr and hdr.after ~= nil, "BUG3: master has an after (re-talk) text") check(hdr and hdr.after ~= nil, "BUG3: master has an after (re-talk) text")
------------------------------------------------------------------ ------------------------------------------------------------------
-- BUG1: sight aggro. Stand directly below the master (5,3 faces DOWN, -- BUG1: exact gate. Start just north of the tile to the Master's left,
-- range 4) with the four blackbelts pre-cleared so only he can engage. -- then step down once. The four blackbelts are cleared so only he reacts.
------------------------------------------------------------------ ------------------------------------------------------------------
local ow = resetDojo(5, 4, "up", {}) local ow = resetDojo(4, 2, "down", {})
U.shot(game, DIR .. "/dojo_1_before.png") U.shot(game, DIR .. "/dojo_1_before.png")
U.wait(20) -- the idle sight scan runs every frame U.tap(game, "down")
local engaged = ow.engaging or (ow.emote ~= nil) U.wait(30)
U.log("aggro engaging:", tostring(ow.engaging), "emote:", tostring(ow.emote ~= nil)) local engaged = topIsTextBox()
U.log("gate dialogue open:", tostring(engaged))
U.shot(game, DIR .. "/dojo_2_aggro.png") U.shot(game, DIR .. "/dojo_2_aggro.png")
check(engaged, "BUG1: Karate Master aggros on sight") check(engaged, "BUG1: Karate Master stops the player at his left")
------------------------------------------------------------------ ------------------------------------------------------------------
-- BUG3: talk to the already-beaten master -> "Stay and train..." and -- BUG3: talk to the already-beaten master -> "Stay and train..." and
@@ -0,0 +1,44 @@
-- Driver: Fighting Dojo Karate Master gate (#495).
--
-- Run:
-- POKEPORT_DRIVER=tests/drivers/fighting_dojo_gate_bug495_test.lua \
-- POKEPORT_TOUCH=0 POKEPORT_VERSION=yellow love .
--
-- The scene starts one tile north of the only trigger tile. The reported
-- behaviour is an interaction timing and position question for a human to
-- observe, not an assertion this driver can decide.
return function(game)
local U = dofile("tests/drivers/util.lua")
local function check(label, ok)
U.log(ok and "PASS" or "FAIL", label)
return ok
end
U.teleport(game, "FIGHTING_DOJO", 4, 2, "down")
local ow = game.stack:top()
local master
for _, npc in ipairs(ow.npcs) do
if npc.def and npc.def.name == "FIGHTINGDOJO_KARATE_MASTER" then
master = npc
break
end
end
-- Fixture checks only: the room, player, and Master must be in the layout
-- that makes the reported tile meaningful.
check("Fighting Dojo is loaded", ow.map.id == "FIGHTING_DOJO")
check("player starts north of the trigger tile",
ow.player.cellX == 4 and ow.player.cellY == 2)
check("Karate Master is at (5,3)",
master and master.cellX == 5 and master.cellY == 3)
U.log("Issue #495: Karate Master gate")
U.log("Do this: press Down once, onto the tile left of the Master.")
U.log("Right: he turns left and starts his challenge on that tile only.")
U.log("Wrong: nothing happens here, while standing below him triggers a battle.")
while true do
coroutine.yield()
end
end
+58
View File
@@ -0,0 +1,58 @@
-- FightingDojoDefaultScript gates the Karate Master at the one tile to his
-- left. He is not a regular sight-line trainer (#495).
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local Data = require("src.core.Data")
if not Data.maps then Data:load() end
local S = require("tests.harness").suite("parity Fighting Dojo gate")
local check, eq = S.check, S.eq
local script = require("data.scripts.story4").FIGHTING_DOJO.onStep
local function scene()
local master = {
def = { name = "FIGHTINGDOJO_KARATE_MASTER" },
facing = "down",
facePlayer = function(self, player)
self.facing = player.cellX < 5 and "left" or "right"
end,
}
local ow = {
npcs = { master },
player = { cellX = 4, cellY = 3, facing = "down" },
trainerDefeated = function() return false end,
engageTrainer = function(self, npc) self.engaged = npc end,
}
return { save = { flags = {} } }, ow, master
end
local header = Data:trainerHeader("FightingDojo", 1)
eq(header and header.range, 0,
"Karate Master has no generic trainer sight range")
do
local game, ow = scene()
check(not script(game, ow, 5, 4),
"standing below the downward-facing Master does not trigger him")
check(ow.engaged == nil, "the below tile does not begin a battle")
end
do
local game, ow, master = scene()
check(script(game, ow, 4, 3),
"the tile immediately left of the Master triggers his script")
eq(ow.engaged, master, "the Master starts the battle")
eq(master.facing, "left", "the Master turns toward the player")
eq(ow.player.facing, "right", "the player turns toward the Master")
end
do
local game, ow = scene()
game.save.flags.EVENT_BEAT_KARATE_MASTER = true
check(not script(game, ow, 4, 3),
"the gate stays inactive after the Karate Master is beaten")
end
S.finish()