Files
gen1recomp/data/scripts/story5.lua
T
bryanthaboi 15029d811f bug squashing
# Closed issues

CLOSES #17: Incorrect Character Visuals (Only with GBC filter)
CLOSES #23: Could you allow the player to change the order of the moves
CLOSES #24: Evolution music not playing during evolution
CLOSES #26: Standing on door glitch
CLOSES #27: Battle Intro text automatically continues
CLOSES #29: Visual bug when zoomed out
CLOSES #32: Team Rocket recruiter doesn't battle with you unless you speak with him first
CLOSES #33: Developer/Debug Console
CLOSES #35: Professor Oak's introduction Inaccuracies
CLOSES #36: Missing Pokemon Dex entries when picking starter + Rival Pathing issues
CLOSES #39: Guy who stops player from skipping brock doesn't bring you to brock's gym + doesn't leave once you've beaten brock
CLOSES #40: Bill cutscene is broken
CLOSES #41: Ticket guy failing to be a Ticket guy
CLOSES #42: S.S. anne odd behavior + Missing sailing away animation
CLOSES #43: Dig Attack animation appears to be glitched
CLOSES #44: Pokeball flashing doesn't appear to be accurate
CLOSES #45: Inaccurate Cut Animation
CLOSES #46: Dugtrio i caught in diglett cave has two of the same move
CLOSES #47: Rival ignores player in Lavender Tower
CLOSES #48: Healing pad in lavender tower does not function
CLOSES #49: Incorrect dialogue with parched security guard
CLOSES #50: (Game Breaking!) Rocket grunt guarding poster refuses to move
CLOSES #51: Badges showing up as items I can deposit in PC
CLOSES #52: Visual bug on Celadon Department Store roof (Red Filter)
CLOSES #54: Visual issue on route 15 + Fuchsia City
CLOSES #56: Running animation missing
CLOSES #57: Safari Zone does not display steps while you are inside of it
CLOSES #58: (Game Breaking!) Softlock at cycling road gate
CLOSES #59: Bike visual issues
CLOSES #60: Cycling road not forcing you to get on your bike
CLOSES #61: No keycard doors in Silph Co.
CLOSES #63: Missing teleporter animation
CLOSES #64: Inaccurate spinning
CLOSES #65: Reimplement unused Silph Co. Chief and Professor Oak trainer battles
2026-07-22 08:18:13 -04:00

558 lines
21 KiB
Lua

-- Gift NPCs and in-game trades. Each cites its pokered source.
local M = {}
local function text(game) return game.data.text end
local function push(game, s, done)
local TextBox = require("src.render.TextBox")
game.stack:push(TextBox.new(game, s, done))
end
-- fill the extracted text placeholders ({RAM:...}, {PLAYER})
local function fill(s, subs)
s = s:gsub("{PLAYER}", subs.player or "")
s = s:gsub("{RAM:[^}]*}", function() return subs.ram or "" end)
return s
end
-- One-time item gift, following the original text_asm flow:
-- pre text (optional) -> GiveItem (bag-full refusal keeps the flag
-- unset, talk again after making room) -> received text -> optional
-- explanation; repeat visits get the already text.
local function gift(opts)
return function(game, ow, npc, done)
local t = text(game)
local itemName = game.data.items[opts.item].name
local subs = { ram = itemName, player = game.save.player.name }
local function say(label, fallback, cb)
push(game, fill(t[label] or fallback, subs), cb)
end
if game.save.flags[opts.flag] then
say(opts.already or opts.explain, "It's a useful\nitem, isn't it?", done)
return
end
local function give()
if not require("src.inventory.Bag").add(game.save, opts.item, 1) then
say(opts.noRoom, "You have no room\nfor this item!", done)
return
end
game.save.flags[opts.flag] = true
local idef = game.data.items[opts.item]
require("src.core.Sound").play(game.data,
(idef and idef.keyItem) and "Get_Key_Item" or "Get_Item1")
say(opts.received, "{PLAYER} received\n{RAM:}!", function()
if opts.explain then
say(opts.explain, "", done)
else
done()
end
end)
end
if opts.pre then say(opts.pre, "", give) else give() end
end
end
-- Coin Case (scripts/CeladonDiner.asm, the busted gym guide)
M.CELADON_DINER = {
talk = {
TEXT_CELADONDINER_GYM_GUIDE = gift({
flag = "EVENT_GOT_COIN_CASE", item = "COIN_CASE",
pre = "_CeladonDinerGymGuideImFlatOutBustedText",
received = "_CeladonDinerGymGuideReceivedCoinCaseText",
noRoom = "_CeladonDinerGymGuideCoinCaseNoRoomText",
already = "_CeladonDinerGymGuideWinItBackText",
}),
},
}
-- TM18 Counter (scripts/CeladonMart3F.asm, the TV-game-shop clerk)
M.CELADON_MART_3F = {
talk = {
TEXT_CELADONMART3F_CLERK = gift({
flag = "EVENT_GOT_TM18", item = "TM_COUNTER",
pre = "_CeladonMart3FClerkTM18PreReceiveText",
received = "_CeladonMart3FClerkReceivedTM18Text",
explain = "_CeladonMart3FClerkTM18ExplanationText",
noRoom = "_CeladonMart3FClerkTM18NoRoomText",
}),
},
}
-- TM39 Swift (scripts/Route12Gate2F.asm)
M.ROUTE_12_GATE_2F = {
talk = {
TEXT_ROUTE12GATE2F_BRUNETTE_GIRL = gift({
flag = "EVENT_GOT_TM39", item = "TM_SWIFT",
pre = "_Route12Gate2FBrunetteGirlYouCanHaveThisText",
received = "_Route12Gate2FBrunetteGirlReceivedTM39Text",
explain = "_Route12Gate2FBrunetteGirlTM39ExplanationText",
noRoom = "_Route12Gate2FBrunetteGirlTM39NoRoomText",
}),
},
}
-- TM41 Softboiled (scripts/CeladonCity.asm, Gramps3)
M.CELADON_CITY = {
talk = {
TEXT_CELADONCITY_GRAMPS3 = gift({
flag = "EVENT_GOT_TM41", item = "TM_SOFTBOILED",
pre = "_CeladonCityGramps3Text",
received = "_CeladonCityGramps3ReceivedTM41Text",
explain = "_CeladonCityGramps3TM41ExplanationText",
noRoom = "_CeladonCityGramps3TM41NoRoomText",
}),
},
}
-- TM35 Metronome (scripts/CinnabarLabMetronomeRoom.asm)
M.CINNABAR_LAB_METRONOME_ROOM = {
talk = {
TEXT_CINNABARLABMETRONOMEROOM_SCIENTIST1 = gift({
flag = "EVENT_GOT_TM35", item = "TM_METRONOME",
pre = "_CinnabarLabMetronomeRoomScientist1Text",
received = "_CinnabarLabMetronomeRoomScientist1ReceivedTM35Text",
explain = "_CinnabarLabMetronomeRoomScientist1TM35ExplanationText",
noRoom = "_CinnabarLabMetronomeRoomScientist1TM35NoRoomText",
}),
},
}
-- TM42 Dream Eater (scripts/ViridianCity.asm, the fisher; no pre text)
M.VIRIDIAN_CITY = {
talk = {
TEXT_VIRIDIANCITY_FISHER = gift({
flag = "EVENT_GOT_TM42", item = "TM_DREAM_EATER",
received = "_ViridianCityFisherReceivedTM42Text",
explain = "_ViridianCityFisherTM42ExplanationText",
noRoom = "_ViridianCityFisherTM42NoRoomText",
}),
},
}
-- TM36 Selfdestruct (scripts/SilphCo2F.asm, the rescued worker)
M.SILPH_CO_2F = {
talk = {
TEXT_SILPHCO2F_SILPH_WORKER_F = gift({
flag = "EVENT_GOT_TM36", item = "TM_SELFDESTRUCT",
received = "_SilphCo2FSilphWorkerFReceivedTM36Text",
explain = "_SilphCo2FSilphWorkerFTM36ExplanationText",
noRoom = "_SilphCo2FSilphWorkerFTM36NoRoomText",
}),
},
}
-- Free POTION sample (scripts/Route1.asm; the original burns the flag
-- even on a full bag -- we keep the port's kinder halt-and-retry)
M.ROUTE_1 = {
talk = {
TEXT_ROUTE1_YOUNGSTER1 = gift({
flag = "EVENT_GOT_POTION_SAMPLE", item = "POTION",
pre = "_Route1Youngster1MartSampleText",
received = "_Route1Youngster1GotPotionText",
noRoom = "_Route1Youngster1NoRoomText",
already = "_Route1Youngster1AlsoGotPokeballsText",
}),
},
}
-- The three in-game trades the port was missing (data/events/trades.asm
-- indices are 1-based in data/generated/field.lua trades)
M.ROUTE_11_GATE_2F = {
talk = {
TEXT_ROUTE11GATE2F_YOUNGSTER = {
{ "face_player" },
{ "trade", 1, "EVENT_TRADED_NIDORINO_FOR_NIDORINA" }, -- TERRY
},
},
}
M.ROUTE_18_GATE_2F = {
talk = {
TEXT_ROUTE18GATE2F_YOUNGSTER = {
{ "face_player" },
{ "trade", 6, "EVENT_TRADED_SLOWBRO_FOR_LICKITUNG" }, -- MARC
},
},
}
M.UNDERGROUND_PATH_ROUTE_5 = {
talk = {
TEXT_UNDERGROUNDPATHROUTE5_LITTLE_GIRL = {
{ "face_player" },
{ "trade", 10, "EVENT_TRADED_NIDORAN_M_FOR_NIDORAN_F" }, -- SPOT
},
},
}
-- =====================================================================
-- Progression gates and rival ambushes
-- =====================================================================
local function inCoords(coords, x, y)
for _, c in ipairs(coords) do
if x == c[1] and y == c[2] then return true end
end
return false
end
-- coordinate block: show a line and push the player back one step
local function stepGate(opts)
return function(game, ow, x, y)
if not inCoords(opts.coords, x, y) then return false end
if not opts.blocked(game) then return false end
require("src.core.Sound").play(game.data, "Denied")
push(game, text(game)[opts.text] or opts.fallback,
function() ow:scriptMove(ow.player, opts.push, 1) end)
return true
end
end
-- Viridian Gym stays locked until the seven other badges are earned
-- (scripts/ViridianCity.asm ViridianCityCheckGymOpenScript: wObtained-
-- Badges == ~EARTHBADGE at (32,8) shoves the player off the door)
local SEVEN_BADGES = { "BOULDERBADGE", "CASCADEBADGE", "THUNDERBADGE",
"RAINBOWBADGE", "SOULBADGE", "MARSHBADGE",
"VOLCANOBADGE" }
local viridianGymLock = stepGate({
coords = { { 32, 8 } },
blocked = function(game)
for _, b in ipairs(SEVEN_BADGES) do
if not game.save.inventory[b] then return true end
end
return false
end,
text = "_ViridianCityGymLockedText",
fallback = "The GYM's doors\nare locked...",
push = "down",
})
-- story.lua's VIRIDIAN_CITY module owns the sleeping-old-man block;
-- chain it behind the gym lock (the registry keeps one onStep per map)
local viridianOldManStep = require("data.scripts.story").VIRIDIAN_CITY.onStep
M.VIRIDIAN_CITY.onStep = function(game, ow, x, y)
if viridianGymLock(game, ow, x, y) then return true end
return viridianOldManStep(game, ow, x, y)
end
-- Cinnabar Gym needs the SECRET KEY (scripts/CinnabarIsland.asm)
M.CINNABAR_ISLAND = {
onStep = stepGate({
coords = { { 18, 4 } },
blocked = function(game) return not game.save.inventory.SECRET_KEY end,
text = "_CinnabarIslandDoorIsLockedText",
fallback = "The door is\nlocked...",
push = "down",
}),
-- CinnabarIsland_Script line 6: ResetEvent EVENT_LAB_STILL_REVIVING_
-- FOSSIL on every (re)load of this map -- OverworldState:setMap runs
-- onEnter on every entry, not just the first, so this fires both
-- when the player walks out of the fossil lab back onto the island
-- and on any later re-entry (boat, Mansion exit, etc.), matching the
-- oracle's every-load map script. Paired with the deposit/pending/
-- ready state machine in data/scripts/story2.lua's
-- TEXT_CINNABARLABFOSSILROOM_SCIENTIST1.
onEnter = function(game, ow)
if game.save.flags.EVENT_LAB_STILL_REVIVING_FOSSIL then
game.save.flags.EVENT_LAB_STILL_REVIVING_FOSSIL = nil
end
end,
}
-- Pewter's youngster stops you leaving east before Brock is beaten and
-- escorts you to the gym (scripts/PewterCity.asm
-- PewterCityCheckPlayerLeavingEastScript /
-- PewterCityYoungsterShowsPlayerGymScript, engine/events/pewter_guys.asm
-- PewterGymGuyCoords): the "follow me" lines, then the player is walked
-- west along the road to the front of the PEWTER_GYM door at (16,17).
M.PEWTER_CITY = {
onStep = function(game, ow, x, y)
if game.save.flags.EVENT_BEAT_BROCK then return false end
if ow.runner:isRunning() or #ow.scriptMoves > 0 then return false end
if not inCoords({ { 35, 17 }, { 36, 17 }, { 37, 18 }, { 37, 19 } }, x, y) then
return false
end
local t = text(game)
-- walk up onto the road row (y=17), west to one tile east of the gym
-- door, then drop below the door and turn to face it
local steps = {}
for _ = 1, y - 17 do steps[#steps + 1] = "up" end
for _ = 1, x - 17 do steps[#steps + 1] = "left" end
steps[#steps + 1] = "down"
steps[#steps + 1] = "left"
local function walk(i)
if not steps[i] then
ow.player.facing = "up"
return
end
ow:scriptMove(ow.player, steps[i], 1, function() walk(i + 1) end)
end
push(game, t._PewterCityYoungsterYoureATrainerFollowMeText
or "Hey! You're a\ntrainer, right?", function()
push(game, t._PewterCityYoungsterGoTakeOnBrockText
or "Go take on BROCK\nat the GYM first!", function()
walk(1)
end)
end)
return true
end,
}
-- Rival ambush: show the hidden rival, walk him up to the player, run
-- the battle rows, march him back and hide him. On a loss the walk is
-- skipped (the blackout rebuilds the map mid-script).
local function runAmbush(game, ow, rows, playerFacing)
if ow.runner:isRunning() then return false end
ow.player.facing = playerFacing
-- the rival encounter sting (MUSIC_MEET_RIVAL); the battle music
-- takes over and the map theme returns after the victory jingle
require("src.core.Music").play(game.data, "Music_MeetRival")
ow.runner:run(rows)
return true
end
-- Route 22 rival, both visits (scripts/Route22.asm). pokered arms
-- EVENT_ROUTE22_RIVAL_WANTS_BATTLE in Oak's lab (expired by Pewter
-- Gym) and again in Viridian Gym; we derive the same windows from the
-- surrounding story flags so old saves work too.
local function route22Scene(n, objIndex, objName, oppClass, baseParty, beatFlag, py)
return {
{ "show_object", "ROUTE_22", objName }, -- 1
{ "move_npc_to", objIndex, 28, py }, -- 2
{ "face_object", objIndex, "right" }, -- 3
{ "show_text", "_Route22RivalBeforeBattleText" .. n }, -- 4
{ "rival_battle", oppClass, baseParty }, -- 5
{ "jump_if_false", 11 }, -- 6
{ "set_flag", beatFlag }, -- 7
{ "show_text", "_Route22Rival" .. n .. "DefeatedText" }, -- 8
{ "show_text", "_Route22RivalAfterBattleText" .. n }, -- 9
{ "move_npc_to", objIndex, 25, 5 }, -- 10
{ "hide_object", "ROUTE_22", objName }, -- 11
}
end
M.ROUTE_22 = {
onStep = function(game, ow, x, y)
if not inCoords({ { 29, 4 }, { 29, 5 } }, x, y) then return false end
local f = game.save.flags
if f.EVENT_GOT_POKEDEX and not f.EVENT_BEAT_BROCK
and not f.EVENT_BEAT_ROUTE22_RIVAL_1ST_BATTLE then
return runAmbush(game, ow,
route22Scene(1, 1, "ROUTE22_RIVAL1", "OPP_RIVAL1", 4,
"EVENT_BEAT_ROUTE22_RIVAL_1ST_BATTLE", y), "left")
end
if f.EVENT_BEAT_GIOVANNI and not f.EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE then
return runAmbush(game, ow,
route22Scene(2, 2, "ROUTE22_RIVAL2", "OPP_RIVAL2", 10,
"EVENT_BEAT_ROUTE22_RIVAL_2ND_BATTLE", y), "left")
end
return false
end,
}
-- Cerulean City: the Nugget Bridge rival ambush (CeruleanCityCoords2),
-- the TM28 rocket thief with his forced-fight cells (Coords1), and the
-- cave guard who steps aside once you are Champion.
local function ceruleanRivalScene(px, py)
return {
{ "show_object", "CERULEAN_CITY", "CERULEANCITY_RIVAL" }, -- 1
{ "move_npc_to", 1, px, py - 1 }, -- 2
{ "face_object", 1, "down" }, -- 3
{ "show_text", "_CeruleanCityRivalPreBattleText" }, -- 4
{ "rival_battle", "OPP_RIVAL1", 7 }, -- 5
{ "jump_if_false", 10 }, -- 6
{ "set_flag", "EVENT_BEAT_CERULEAN_RIVAL" }, -- 7
{ "show_text", "_CeruleanCityRivalDefeatedText" }, -- 8
{ "move_npc_to", 1, 20, 2 }, -- 9
{ "hide_object", "CERULEAN_CITY", "CERULEANCITY_RIVAL" }, -- 10
}
end
-- scripts/CeruleanCity.asm CeruleanCityRocketText: fight the thief,
-- then he returns TM28 (DIG) and hurries off
local rocketRows = {
{ "face_player" }, -- 1
{ "check_flag", "EVENT_GOT_TM28" }, -- 2
{ "jump_if_true", 15 }, -- 3
{ "check_flag", "EVENT_BEAT_CERULEAN_ROCKET_THIEF" }, -- 4
{ "jump_if_true", 9 }, -- 5
{ "show_text", "_CeruleanCityRocketText" }, -- 6
{ "start_battle", "trainer", "OPP_ROCKET", 5 }, -- 7
{ "jump_if_false", 18 }, -- 8
{ "show_text", "_CeruleanCityRocketIllReturnTheTMText" }, -- 9
{ "set_flag", "EVENT_BEAT_CERULEAN_ROCKET_THIEF" }, -- 10
{ "give_item", "TM_DIG", 1, false }, -- 11 (row 13 prints)
{ "set_flag", "EVENT_GOT_TM28" }, -- 12
{ "show_text", "_CeruleanCityRocketReceivedTM28Text" }, -- 13
{ "show_text", "_CeruleanCityRocketIBetterGetMovingText" }, -- 14
{ "hide_object", "CERULEAN_CITY", "CERULEANCITY_ROCKET" }, -- 15
-- CeruleanHideRocket (scripts/CeruleanCity_2.asm) does all three behind
-- one fade: the ROCKET goes, GUARD1 (28,12) appears and GUARD2 (27,12)
-- disappears. GUARD2 stands on the only walkable neighbour of the
-- trashed house's south door, which is one of the two ways through the
-- fence dividing Cerulean, so this swap is what reconnects the city.
-- Bill's ticket performs the same swap (data/scripts/story.lua), and
-- both are idempotent, so whichever the player reaches first opens it.
{ "show_object", "CERULEAN_CITY", "CERULEANCITY_GUARD1" }, -- 16
{ "hide_object", "CERULEAN_CITY", "CERULEANCITY_GUARD2" }, -- 17
}
M.CERULEAN_CITY = {
talk = {
TEXT_CERULEANCITY_ROCKET = rocketRows,
},
onEnter = function(game, ow)
if game.save.flags.EVENT_BEAT_CHAMPION_RIVAL then
local Commands = require("src.script.Commands")
Commands.hide_object({ game = game, save = game.save, overworld = ow },
"CERULEAN_CITY", "CERULEANCITY_SUPER_NERD3")
end
end,
onStep = function(game, ow, x, y)
local f = game.save.flags
if not f.EVENT_BEAT_CERULEAN_ROCKET_THIEF
and inCoords({ { 30, 7 }, { 30, 9 } }, x, y) then
if ow.runner:isRunning() then return false end
local rocket = ow:npcByIndex(2)
if rocket then
ow.player.facing = y < 8 and "down" or "up"
ow.runner:run(rocketRows, { npc = rocket })
return true
end
return false
end
if not f.EVENT_BEAT_CERULEAN_RIVAL
and inCoords({ { 20, 6 }, { 21, 6 } }, x, y) then
return runAmbush(game, ow, ceruleanRivalScene(x, y), "up")
end
return false
end,
}
-- The Pewter museum's fossil exhibits (engine/events/hidden_events/
-- museum_fossils.asm: DisplayMonFrontSpriteInBox + the plaque text)
M.MUSEUM_1F = {
onInteract = function(game, ow, fx, fy)
if ow.player.facing ~= "up" then return false end
local PicBox = require("src.ui.PicBox")
local t = text(game)
if fx == 2 and fy == 3 then
game.stack:push(PicBox.new(game,
"assets/generated/battle/front/fossilaerodactyl.png",
t._AerodactylFossilText or "AERODACTYL Fossil"))
return true
end
if fx == 2 and fy == 6 then
game.stack:push(PicBox.new(game,
"assets/generated/battle/front/fossilkabutops.png",
t._KabutopsFossilText or "KABUTOPS Fossil"))
return true
end
return false
end,
}
-- The Pewter Center's singing JIGGLYPUFF (scripts/PewterPokecenter.asm
-- plays MUSIC_JIGGLYPUFF_SONG, then the map theme resumes)
M.PEWTER_POKECENTER = {
talk = {
TEXT_PEWTERPOKECENTER_JIGGLYPUFF = function(game, ow, npc, done)
require("src.core.Music").playOnce(game.data, "Music_JigglypuffSong")
push(game, text(game)._PewterPokecenterJigglypuffText
or "JIGGLYPUFF: Puu\npupuu!", done)
end,
},
}
-- Cycling Road gate guards (scripts/Route16Gate1F.asm /
-- Route18Gate1F.asm): without a BICYCLE in the bag the guard stops you
-- on the west-side cells and walks you back
local function bikeGateGuard(coords, stopText, explainText)
return function(game, ow, x, y)
if game.save.inventory.BICYCLE then return false end
if not inCoords(coords, x, y) then return false end
-- walk the player up to the tile beside the counter, no further:
-- (matchedY - closestY) tiles, 0 when already next to it
-- (Route16Gate1FDefaultScript's wCoordIndex-1). Forcing a fixed one
-- tile up from the counter-adjacent row shoved the player onto the
-- impassable desk and boxed them in.
local closestY = coords[1][2]
for _, c in ipairs(coords) do
if c[2] < closestY then closestY = c[2] end
end
local dist = y - closestY
local t = text(game)
push(game, t[stopText] or "Hey! Wait up!", function()
push(game, t[explainText] or "You need a\nBICYCLE for\nCYCLING ROAD!", function()
if dist > 0 then
ow:scriptMove(ow.player, "up", dist)
end
end)
end)
return true
end
end
M.ROUTE_16_GATE_1F = {
onStep = bikeGateGuard(
{ { 4, 7 }, { 4, 8 }, { 4, 9 }, { 4, 10 } },
"_Route16Gate1FGuardWaitUpText",
"_Route16Gate1FGuardNoPedestriansAllowedText"),
}
M.ROUTE_18_GATE_1F = {
onStep = bikeGateGuard(
{ { 4, 3 }, { 4, 4 }, { 4, 5 }, { 4, 6 } },
"_Route18Gate1FGuardExcuseMeText",
"_Route18Gate1FGuardYouNeedABicycleText"),
}
-- Silph Co. 7F rival ambush (scripts/SilphCo7F.asm
-- SilphCo7FDefaultScript: coords (3,2)/(3,3), the rival at (3,7) walks
-- up, MUSIC_MEET_RIVAL, OPP_RIVAL2 parties 7-9 by starter, then he
-- wishes you luck, walks off right and disappears; one-time via
-- EVENT_BEAT_SILPH_CO_RIVAL)
M.SILPH_CO_7F = {
onStep = function(game, ow, x, y)
if game.save.flags.EVENT_BEAT_SILPH_CO_RIVAL then return false end
if not inCoords({ { 3, 2 }, { 3, 3 } }, x, y) then return false end
return runAmbush(game, ow, {
{ "show_object", "SILPH_CO_7F", "SILPHCO7F_RIVAL" }, -- 1
{ "show_text", "_SilphCo7FRivalText" }, -- 2
{ "move_npc_to", 9, 3, y + 1 }, -- 3
{ "face_object", 9, "up" }, -- 4
{ "show_text", "_SilphCo7FRivalWaitedHereText" }, -- 5
{ "rival_battle", "OPP_RIVAL2", 7 }, -- 6
{ "jump_if_false", 12 }, -- 7
{ "set_flag", "EVENT_BEAT_SILPH_CO_RIVAL" }, -- 8
{ "show_text", "_SilphCo7FRivalDefeatedText" }, -- 9
{ "show_text", "_SilphCo7FRivalGoodLuckToYouText" }, -- 10
{ "move_npc_to", 9, 5, y + 1 }, -- 11
{ "hide_object", "SILPH_CO_7F", "SILPHCO7F_RIVAL" }, -- 12
}, "down")
end,
}
-- S.S. Anne 2F rival ambush (scripts/SSAnne2F.asm; coords 36/37,8)
M.SS_ANNE_2F = {
onStep = function(game, ow, x, y)
if game.save.flags.EVENT_BEAT_SS_ANNE_RIVAL then return false end
if not inCoords({ { 36, 8 }, { 37, 8 } }, x, y) then return false end
local onLeft = x == 36
return runAmbush(game, ow, {
{ "show_object", "SS_ANNE_2F", "SSANNE2F_RIVAL" }, -- 1
{ "move_npc_to", 2, 36, onLeft and 7 or 8 }, -- 2
{ "face_object", 2, onLeft and "down" or "right" }, -- 3
{ "show_text", "_SSAnne2FRivalText" }, -- 4
{ "rival_battle", "OPP_RIVAL2", 1 }, -- 5
{ "jump_if_false", 10 }, -- 6
{ "set_flag", "EVENT_BEAT_SS_ANNE_RIVAL" }, -- 7
{ "show_text", "_SSAnne2FRivalDefeatedText" }, -- 8
{ "move_npc_to", 2, 36, 4 }, -- 9
{ "hide_object", "SS_ANNE_2F", "SSANNE2F_RIVAL" }, -- 10
}, onLeft and "up" or "left")
end,
}
return M