mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-05 17:03:12 -04:00
solving more things and making the auto-player work almost perfectly
This commit is contained in:
+55
-1
@@ -252,7 +252,17 @@ M.POKEMON_TOWER_7F = {
|
||||
{ "show_object", "MR_FUJIS_HOUSE", "MRFUJISHOUSE_MR_FUJI" }, -- 5
|
||||
{ "hide_object", "SAFFRON_CITY", "SAFFRONCITY_ROCKET8" }, -- 6
|
||||
{ "show_object", "SAFFRON_CITY", "SAFFRONCITY_ROCKET9" }, -- 7
|
||||
{ "warp", "MR_FUJIS_HOUSE", 3, 3, "down" }, -- 8
|
||||
-- pokered warps to wDestinationWarpID $1 (0-based) -- the house's
|
||||
-- SECOND warp, the door mat at (3,7) -- facing UP
|
||||
-- (PokemonTower7FWarpToMrFujiHouseScript: SPRITE_FACING_UP +
|
||||
-- hWarpDestinationMap MR_FUJIS_HOUSE). Landing (3,3) instead put
|
||||
-- the player at the Pokédex table, and the route's first waypoint
|
||||
-- (3,7) then stepped onto a LIVE door mat and exited the house
|
||||
-- before ever talking to Fuji -- so the POKE_FLUTE was never
|
||||
-- collected and the Route 16 SNORLAX sealed the map. The arrival
|
||||
-- mat itself is inert until stepped off (warpEntryCell), which is
|
||||
-- what makes the vanilla coordinates safe.
|
||||
{ "warp", "MR_FUJIS_HOUSE", 3, 7, "up" }, -- 8
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -387,6 +397,50 @@ M.WARDENS_HOUSE = {
|
||||
-- -------------------------------------------------------------------
|
||||
|
||||
M.SILPH_CO_11F = {
|
||||
-- Giovanni's battle is a COORDINATE TRIGGER, not a talk.
|
||||
-- SilphCo11FDefaultScript (scripts/SilphCo11F.asm) checks
|
||||
-- .PlayerCoordsArray -- (6,13) and (7,12) -- every frame while
|
||||
-- EVENT_BEAT_SILPH_CO_GIOVANNI is unset: standing there shows his text,
|
||||
-- walks him three tiles down (.GiovanniMovement), and starts the fight.
|
||||
-- He also has no trainer-header entry, so sight engagement never fires
|
||||
-- either. Without this hook he was a talk-only statue four tiles away
|
||||
-- from anything the route (or a vanilla-faithful player walking the same
|
||||
-- line) would touch, and the whole Silph ending -- the flag, the Master
|
||||
-- Ball, the Saffron streets clearing -- silently never happened.
|
||||
--
|
||||
-- engageTrainer shows TEXT_SILPHCO11F_GIOVANNI as the battle text and,
|
||||
-- via victories.lua OPP_GIOVANNI#2, sets the event on a win; a loss
|
||||
-- sets nothing, so the trigger re-arms exactly as vanilla does.
|
||||
onStep = function(game, ow, x, y)
|
||||
if game.save.flags.EVENT_BEAT_SILPH_CO_GIOVANNI then return false end
|
||||
if not ((x == 6 and y == 13) or (x == 7 and y == 12)) then return false end
|
||||
local gio
|
||||
for _, npc in ipairs(ow.npcs) do
|
||||
if npc.def and npc.def.name == "SILPHCO11F_GIOVANNI" then gio = npc break end
|
||||
end
|
||||
if not gio or ow:trainerDefeated(gio) then return false end
|
||||
ow:scriptMove(gio, "down", 3, function()
|
||||
gio:facePlayer(ow.player)
|
||||
ow:engageTrainer(gio, function()
|
||||
-- SilphCo11FTeamRocketLeavesScript: Giovanni leaves the floor
|
||||
-- after the loss (the street rockets are handled by
|
||||
-- M.SAFFRON_CITY.onEnter in story4.lua).
|
||||
if game.save.flags.EVENT_BEAT_SILPH_CO_GIOVANNI then
|
||||
local Commands = require("src.script.Commands")
|
||||
local ctx = { game = game, save = game.save, overworld = ow }
|
||||
Commands.hide_object(ctx, "SILPH_CO_11F", "SILPHCO11F_GIOVANNI")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
return true
|
||||
end,
|
||||
onEnter = function(game, ow)
|
||||
if game.save.flags.EVENT_BEAT_SILPH_CO_GIOVANNI then
|
||||
local Commands = require("src.script.Commands")
|
||||
local ctx = { game = game, save = game.save, overworld = ow }
|
||||
Commands.hide_object(ctx, "SILPH_CO_11F", "SILPHCO11F_GIOVANNI")
|
||||
end
|
||||
end,
|
||||
talk = {
|
||||
TEXT_SILPHCO11F_SILPH_PRESIDENT = {
|
||||
{ "face_player" }, -- 1
|
||||
|
||||
@@ -633,4 +633,94 @@ M.BRUNOS_ROOM = e4ExitSeal("EVENT_BEAT_BRUNOS_ROOM_TRAINER_0", 0x24, 0x05,
|
||||
M.AGATHAS_ROOM = e4ExitSeal("EVENT_BEAT_AGATHAS_ROOM_TRAINER_0", 0x3b, 0x0e,
|
||||
"_AgathasRoomAgathaDontRunAwayText", "EVENT_AUTOWALKED_INTO_AGATHAS_ROOM")
|
||||
|
||||
-- -------------------------------------------------------------------
|
||||
-- Lance's room (scripts/LancesRoom.asm). Unlike the other three E4
|
||||
-- rooms this one gates its ENTRANCE, not its exit: the .blk ships with
|
||||
-- the arena doorway CLOSED (blocks $72/$73 at block (2,6)/(3,6), cells
|
||||
-- (4-7,12-13)), and LanceShowOrHideEntranceBlocks OPENS it ($31/$32)
|
||||
-- on every map load while EVENT_LANCES_ROOM_LOCK_DOOR is unset.
|
||||
-- Without this script the doorway never opened, so the whole arena --
|
||||
-- Lance AND both CHAMPIONS_ROOM warps at (5,0)/(6,0) -- was sealed off
|
||||
-- from the entrance hall and the league dead-ended here ("goto (6,11)
|
||||
-- unreachable on LANCES_ROOM").
|
||||
--
|
||||
-- LancesRoomDefaultScript's coordinate triggers, all inert once
|
||||
-- EVENT_BEAT_LANCE is set:
|
||||
-- (5,1)/(6,2) beside Lance -> his battle starts (a coordinate
|
||||
-- trigger, not a talk; victories.lua OPP_LANCE#1 sets
|
||||
-- EVENT_BEAT_LANCE on the win, so a loss re-arms)
|
||||
-- (5,11)/(6,11) the doorway -> CheckAndSetEvent
|
||||
-- EVENT_LANCES_ROOM_LOCK_DOOR: first crossing seals
|
||||
-- the door behind the player with SFX_GO_INSIDE
|
||||
-- (24,16) the entrance staircase -> WalkToLance: an auto-walk
|
||||
-- (up 12, left 12, down 7, left 6) landing on (6,11).
|
||||
-- It marches straight across the room's water decor:
|
||||
-- pokered's CollisionCheckOnLand skips collision
|
||||
-- entirely while simulated joypad states run, and our
|
||||
-- scriptMove is collision-free the same way.
|
||||
-- -------------------------------------------------------------------
|
||||
|
||||
local function lanceEntranceBlocks(game, ow)
|
||||
local locked = game.save.flags.EVENT_LANCES_ROOM_LOCK_DOOR
|
||||
ow:replaceBlock(2, 6, locked and 0x72 or 0x31)
|
||||
ow:replaceBlock(3, 6, locked and 0x73 or 0x32)
|
||||
end
|
||||
|
||||
local function lanceLockDoor(game, ow)
|
||||
if game.save.flags.EVENT_LANCES_ROOM_LOCK_DOOR then return end
|
||||
game.save.flags.EVENT_LANCES_ROOM_LOCK_DOOR = true
|
||||
require("src.core.Sound").play(game.data, "Go_Inside")
|
||||
lanceEntranceBlocks(game, ow)
|
||||
end
|
||||
|
||||
local function lanceWalkIn(game, ow)
|
||||
ow:scriptMove(ow.player, "up", 12, function()
|
||||
ow:scriptMove(ow.player, "left", 12, function()
|
||||
ow:scriptMove(ow.player, "down", 7, function()
|
||||
ow:scriptMove(ow.player, "left", 6, function()
|
||||
-- the walk lands on (6,11); vanilla's per-frame coord poll
|
||||
-- then locks the door at once. scriptMove landings do not
|
||||
-- fire onStep, so lock here.
|
||||
lanceLockDoor(game, ow)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
M.LANCES_ROOM = {
|
||||
onEnter = function(game, ow)
|
||||
lanceEntranceBlocks(game, ow)
|
||||
-- the warp arrival lands ON the staircase trigger, and onStep only
|
||||
-- fires for completed steps -- start the walk-in here, the same
|
||||
-- way Lorelei's auto walk-in runs from its onEnter
|
||||
if not game.save.flags.EVENT_BEAT_LANCE
|
||||
and ow.player.cellX == 24 and ow.player.cellY == 16 then
|
||||
lanceWalkIn(game, ow)
|
||||
end
|
||||
end,
|
||||
onStep = function(game, ow, x, y)
|
||||
if game.save.flags.EVENT_BEAT_LANCE then return false end
|
||||
if (x == 5 and y == 1) or (x == 6 and y == 2) then
|
||||
local lance
|
||||
for _, npc in ipairs(ow.npcs) do
|
||||
if npc.def and npc.def.name == "LANCESROOM_LANCE" then lance = npc break end
|
||||
end
|
||||
if not lance or ow:trainerDefeated(lance) then return false end
|
||||
lance:facePlayer(ow.player)
|
||||
ow:engageTrainer(lance, function() end)
|
||||
return true
|
||||
end
|
||||
if (x == 5 or x == 6) and y == 11 then
|
||||
lanceLockDoor(game, ow)
|
||||
return false
|
||||
end
|
||||
if x == 24 and y == 16 then
|
||||
lanceWalkIn(game, ow)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
|
||||
return M
|
||||
|
||||
@@ -92,6 +92,12 @@ local LAST_MAP_REWRITES = {
|
||||
UNDERGROUND_PATH_ROUTE_6 = { rules = { { map = "ROUTE_6" } } },
|
||||
UNDERGROUND_PATH_ROUTE_7 = { rules = { { map = "ROUTE_7" } } },
|
||||
UNDERGROUND_PATH_ROUTE_8 = { rules = { { map = "ROUTE_8" } } },
|
||||
-- DiglettsCaveRoute{2,11}_Script are the same pattern for the cave's two
|
||||
-- entrance houses. Without these, crossing the cave and taking the far
|
||||
-- house's LAST_MAP door warps you back to the route you ENTERED from --
|
||||
-- the whole cave was a one-way trip that always returned to its start.
|
||||
DIGLETTS_CAVE_ROUTE_2 = { rules = { { map = "ROUTE_2" } } },
|
||||
DIGLETTS_CAVE_ROUTE_11 = { rules = { { map = "ROUTE_11" } } },
|
||||
}
|
||||
|
||||
FieldDefaults.FIELD = {
|
||||
|
||||
@@ -1506,6 +1506,21 @@ function OverworldState:trySurf(fx, fy)
|
||||
end
|
||||
|
||||
function OverworldState:tryCut(fx, fy)
|
||||
-- UsedCut (engine/overworld/cut.asm) gates on the TILESET before
|
||||
-- anything else: only OVERWORLD (tree tile $3d) and GYM (plant tile
|
||||
-- $50) have cuttable anything. Matching raw block ids alone
|
||||
-- false-positives on every other tileset -- block ids are only
|
||||
-- meaningful within one tileset, so Route 23 (PLATEAU) had blocks
|
||||
-- matching a swap's `before`, and applying it wrote a block id that
|
||||
-- does not exist in PLATEAU's block table: the renderer indexed nil
|
||||
-- and the game crashed. The same false match is what made the bot
|
||||
-- chain-cut "ornamental bushes" around Saffron and Celadon.
|
||||
local ts = self.map.def.tileset
|
||||
local tile = self.map:cellTile(fx, fy)
|
||||
if not ((ts == "OVERWORLD" and tile == 0x3d)
|
||||
or (ts == "GYM" and tile == 0x50)) then
|
||||
return false
|
||||
end
|
||||
local bx, by = math.floor(fx / 2), math.floor(fy / 2)
|
||||
local block = self.map:blockAt(bx, by)
|
||||
local swap
|
||||
@@ -1591,6 +1606,15 @@ function OverworldState:useCutFieldMove()
|
||||
if not self:partyKnows("CUT") then return "no_badge" end
|
||||
local fx, fy = self.player:facingCell()
|
||||
if not self.map:inBounds(fx, fy) then return "nothing" end
|
||||
-- same tileset/tile gate as tryCut (UsedCut, engine/overworld/cut.asm):
|
||||
-- a tree BLOCK also contains fence/path cells, and facing those is
|
||||
-- "nothing to cut" in vanilla
|
||||
local ts = self.map.def.tileset
|
||||
local tile = self.map:cellTile(fx, fy)
|
||||
if not ((ts == "OVERWORLD" and tile == 0x3d)
|
||||
or (ts == "GYM" and tile == 0x50)) then
|
||||
return "nothing"
|
||||
end
|
||||
local bx, by = math.floor(fx / 2), math.floor(fy / 2)
|
||||
local block = self.map:blockAt(bx, by)
|
||||
local swap
|
||||
|
||||
+1577
-60
File diff suppressed because it is too large
Load Diff
+16
-3
@@ -192,21 +192,34 @@ check(actsOn.cut and actsOn.surf and actsOn.strength,
|
||||
"CUT/SURF/STRENGTH submenu entries appear once the badges are held")
|
||||
|
||||
-- ===========================================================================
|
||||
-- I: CUT from the party menu (CERULEAN_CITY cut tree @18,28, stand @17,28)
|
||||
-- I: CUT from the party menu. The Cerulean tree BLOCK (50, at block 9,14)
|
||||
-- spans two unwalkable cells: the fence half at (18,28) (tile $50) and the
|
||||
-- tree itself at (19,28) (tile $3d). UsedCut (engine/overworld/cut.asm)
|
||||
-- checks the facing TILE, so only the tree cell cuts -- stand above it at
|
||||
-- (19,27) facing down, the way a vanilla player does.
|
||||
-- ===========================================================================
|
||||
Game.save.party = { mkMon("BULBASAUR", "CUT") }
|
||||
Game.save.inventory = { CASCADEBADGE = true }
|
||||
ow = pushOW("CERULEAN_CITY", 17, 28, "right")
|
||||
ow = pushOW("CERULEAN_CITY", 19, 27, "down")
|
||||
check(ow.map:blockAt(9, 14) == 50, "cut tree block (50) present before CUT")
|
||||
|
||||
eq(ow:useCutFieldMove(), "ok", "useCutFieldMove ok when facing a cut tree")
|
||||
ow.player.facing = "up"
|
||||
eq(ow:useCutFieldMove(), "nothing", "useCutFieldMove nothing when not facing a tree")
|
||||
ow.player.facing = "right"
|
||||
ow.player.facing = "down"
|
||||
Game.save.inventory.CASCADEBADGE = nil
|
||||
eq(ow:useCutFieldMove(), "no_badge", "useCutFieldMove no_badge without CASCADEBADGE")
|
||||
Game.save.inventory.CASCADEBADGE = true
|
||||
|
||||
-- the fence half of the same tree block is NOT cuttable (tile $50 on
|
||||
-- OVERWORLD, not the $3d tree tile)
|
||||
popToOW()
|
||||
ow = pushOW("CERULEAN_CITY", 17, 28, "right")
|
||||
eq(ow:useCutFieldMove(), "nothing",
|
||||
"useCutFieldMove nothing when facing the tree block's fence cell")
|
||||
popToOW()
|
||||
ow = pushOW("CERULEAN_CITY", 19, 27, "down")
|
||||
|
||||
-- success path: facing the tree -> _UsedCutText, menu closes, tree replaced
|
||||
clearCaptured()
|
||||
local pmCut = PartyMenu.new(Game)
|
||||
|
||||
@@ -731,6 +731,9 @@ do
|
||||
{ map = "UNDERGROUND_PATH_ROUTE_6", route = "ROUTE_6", x = 17, y = 13 },
|
||||
{ map = "UNDERGROUND_PATH_ROUTE_7", route = "ROUTE_7", x = 5, y = 13 },
|
||||
{ map = "UNDERGROUND_PATH_ROUTE_8", route = "ROUTE_8", x = 13, y = 3 },
|
||||
-- DiglettsCaveRoute{2,11}_Script: same pattern for the cave's two ends
|
||||
{ map = "DIGLETTS_CAVE_ROUTE_2", route = "ROUTE_2", x = 12, y = 9 },
|
||||
{ map = "DIGLETTS_CAVE_ROUTE_11", route = "ROUTE_11", x = 4, y = 5 },
|
||||
}
|
||||
for _, c in ipairs(cases) do
|
||||
local rewrite = rewrites[c.map]
|
||||
|
||||
Reference in New Issue
Block a user