diff --git a/src/world/OverworldController.lua b/src/world/OverworldController.lua index fe8ab8d1..2de18dd9 100644 --- a/src/world/OverworldController.lua +++ b/src/world/OverworldController.lua @@ -37,6 +37,10 @@ local mapScripts -- registry of hand-ported map scripts local COMPASS = { up = "north", down = "south", left = "west", right = "east" } local DIRVEC = { up = { 0, -1 }, down = { 0, 1 }, left = { -1, 0 }, right = { 1, 0 } } +-- pokered's wNumberOfNoRandomBattleStepsLeft: three completed steps +-- after a wild battle before another random battle can start. +local WILD_ENCOUNTER_GRACE_STEPS = 3 + -- Fly animation coord paths (engine/overworld/player_animations.asm): -- y/x pairs in GB screen pixels, one pair every 3 frames (DoFlyAnimation's -- Delay3). The port anchors a path on the player's own position instead @@ -224,6 +228,8 @@ function OverworldState:enter(mapId, x, y, facing, opts) -- a fresh entry, or a stale flag can freeze player input forever self.engaging = false self.emote = nil + -- volatile WRAM state in pokered; never serialize across save/load + self.wildEncounterGraceSteps = 0 -- survives save/load: a loaded game may start inside a building whose -- exit mat is a LAST_MAP warp self.lastOutdoor = Game.save.lastOutdoor @@ -3476,6 +3482,10 @@ end function OverworldState:onStepComplete() local p = self.player + local suppressWildEncounter = self.wildEncounterGraceSteps > 0 + if suppressWildEncounter then + self.wildEncounterGraceSteps = self.wildEncounterGraceSteps - 1 + end self.todSteps = (self.todSteps or 0) + 1 -- UpdatePikachuHappinessAndMood rides the step counter (poison.asm) require("src.world.PikachuFollower").onStep(Game.save) @@ -3588,6 +3598,9 @@ function OverworldState:onStepComplete() -- wild encounters in grass, on water while surfing, or -- on indoor -- maps whose tileset is not FOREST -- on EVERY tile -- (wild_encounters.asm: caves, towers, the Mansion, Power Plant) + -- The cooldown is checked after all other step processing so repel and + -- movement systems continue to advance during the protected steps. + if suppressWildEncounter then return end local encDef = Game.data.encounters[self.map.id] local enc local indoor = Game.data.field.indoorEncounters @@ -3974,6 +3987,9 @@ end -- battle is optional; when given, Oak's Lab OPP_RIVAL1 losses skip the -- blackout (pret HandlePlayerBlackOut) so the map script can HealParty. function OverworldState:afterBattle(result, battle) + if battle and battle.kind == "wild" then + self.wildEncounterGraceSteps = WILD_ENCOUNTER_GRACE_STEPS + end local lead = Game.save.party[1] Logger.info("battle over: %s (lead %s %d/%d)", tostring(result), lead and lead.species or "-", lead and lead.hp or 0, diff --git a/tests/mod_world_tests.lua b/tests/mod_world_tests.lua index 60aefb97..08319cb4 100644 --- a/tests/mod_world_tests.lua +++ b/tests/mod_world_tests.lua @@ -823,6 +823,29 @@ do and caught.enemy.mon.species == "TANGELA", "and it is the species the authored encounter table names") + -- Trainer battles do not arm the wild-encounter cooldown. + walker.wildEncounterGraceSteps = 0 + walker:afterBattle("win", { kind = "trainer" }) + check(walker.wildEncounterGraceSteps == 0, + "trainer battles do not start the wild encounter grace period") + + -- pokered grants three completed steps after a wild battle before the + -- next random battle can start (end_of_battle.asm + home/overworld.asm). + local finishedWild = caught + walker:afterBattle("run", finishedWild) + caught = nil + withBuses(function(_, hooks) + hooks:wrap("encounter.roll", function() + return { species = "TANGELA", level = 5 } + end, 0, "grace-period") + for step = 1, 3 do + pcall(walker.onStepComplete, walker) + check(caught == nil, "wild encounter grace period blocks step " .. step) + end + pcall(walker.onStepComplete, walker) + check(caught ~= nil, "wild encounter is eligible on step 4") + end) + -- the same walk with an encounter.roll wrapper never starts a battle withBuses(function(_, hooks) hooks:wrap("encounter.roll", function() return nil end, 0, "nuzlocke")