mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
bfba1f7bb7
* main menu scrollable when over 8 items * buggies * more buggies * Lorelei, Bruno, and Agatha now push their AfterBattle text right after a win
236 lines
9.4 KiB
Lua
236 lines
9.4 KiB
Lua
-- Vanilla values for the data.field / Data.constants keys this milestone
|
|
-- lifts out of src/world/ literals. The importer does not stamp them yet,
|
|
-- so every read site folds its own table over these and behaves exactly as
|
|
-- the literal did on a stale cache; seed() fills the gaps in Data before
|
|
-- the mod merge so a mod's patch has a vanilla base to merge over instead
|
|
-- of replacing Kanto wholesale.
|
|
-- Pure Lua, no love.*, so the headless loader and offline tools can require it.
|
|
|
|
local FieldDefaults = {}
|
|
|
|
-- ------- data.field
|
|
|
|
-- SGB overworld palette (engine/gfx/palettes.asm SetPal_Overworld): towns
|
|
-- own theirs, routes PAL_ROUTE, interiors inherit the last outdoor map,
|
|
-- with the Pokemon Tower / cave tileset and Elite Four cases on top.
|
|
local PALETTES = {
|
|
byMap = {
|
|
PALLET_TOWN = "PALLET", VIRIDIAN_CITY = "VIRIDIAN",
|
|
PEWTER_CITY = "PEWTER", CERULEAN_CITY = "CERULEAN",
|
|
LAVENDER_TOWN = "LAVENDER", VERMILION_CITY = "VERMILION",
|
|
CELADON_CITY = "CELADON", FUCHSIA_CITY = "FUCHSIA",
|
|
CINNABAR_ISLAND = "CINNABAR", INDIGO_PLATEAU = "INDIGO",
|
|
SAFFRON_CITY = "SAFFRON",
|
|
LORELEIS_ROOM = "PALLET", BRUNOS_ROOM = "CAVE",
|
|
},
|
|
-- Pokemon Tower / Agatha, then the caves
|
|
byTileset = { CEMETERY = "GRAYMON", CAVERN = "CAVE" },
|
|
byPrefix = { { prefix = "ROUTE_", palette = "ROUTE" } },
|
|
default = "ROUTE",
|
|
}
|
|
|
|
-- data/tilesets/bookshelf_tile_ids.asm: tileset id + collision tile ->
|
|
-- what facing up into it prints. `kind` names an engine flavor (the
|
|
-- vanilla five); mods author `text` (a data.text key) or `screen`
|
|
-- (a screens-registry id) instead.
|
|
local BOOKSHELVES = {
|
|
PLATEAU = { [0x30] = { kind = "statues" } },
|
|
HOUSE = { [0x3D] = { screen = "TownMap" }, [0x1E] = { kind = "books" } },
|
|
MANSION = { [0x32] = { kind = "books" } },
|
|
REDS_HOUSE_1 = { [0x32] = { kind = "books" } },
|
|
LAB = { [0x28] = { kind = "books" } },
|
|
LOBBY = { [0x16] = { kind = "elevator" }, [0x50] = { kind = "stuff" },
|
|
[0x52] = { kind = "stuff" } },
|
|
GYM = { [0x1D] = { kind = "books" } },
|
|
DOJO = { [0x1D] = { kind = "books" } },
|
|
GATE = { [0x22] = { kind = "books" } },
|
|
MART = { [0x54] = { kind = "stuff" }, [0x55] = { kind = "stuff" } },
|
|
POKECENTER = { [0x54] = { kind = "stuff" }, [0x55] = { kind = "stuff" } },
|
|
SHIP = { [0x36] = { kind = "books" } },
|
|
}
|
|
|
|
-- Rod tables (item_effects.asm ItemUseOldRod/GoodRod, data/wild/good_rod.asm).
|
|
-- The rejection-loop odds stay engine behavior: they are Gen-1 mechanics,
|
|
-- not content. perMap names the field key holding the per-map groups.
|
|
local FISHING = {
|
|
OLD_ROD = { always = { species = "MAGIKARP", level = 5 } },
|
|
GOOD_ROD = { pool = { { species = "GOLDEEN", level = 10 },
|
|
{ species = "POLIWAG", level = 10 } } },
|
|
SUPER_ROD = { perMap = "superRod" },
|
|
}
|
|
|
|
-- The step counter gates on EVENT_IN_SAFARI_ZONE, not the map, so every
|
|
-- interior counts and the gate itself never does (home/overworld.asm).
|
|
local SAFARI = {
|
|
stepMaps = {
|
|
"SAFARI_ZONE_CENTER", "SAFARI_ZONE_EAST",
|
|
"SAFARI_ZONE_NORTH", "SAFARI_ZONE_WEST",
|
|
"SAFARI_ZONE_CENTER_REST_HOUSE", "SAFARI_ZONE_EAST_REST_HOUSE",
|
|
"SAFARI_ZONE_NORTH_REST_HOUSE", "SAFARI_ZONE_WEST_REST_HOUSE",
|
|
"SAFARI_ZONE_SECRET_HOUSE",
|
|
},
|
|
exitWarp = { map = "SAFARI_ZONE_GATE", x = 4, y = 3, facing = "down" },
|
|
}
|
|
|
|
-- home/overworld.asm LoadPlayerSpriteGraphics / LoadSurfingPlayerSprite-
|
|
-- Graphics / player_animations.asm LoadBirdSpriteGraphics
|
|
local PLAYER_SPRITES = {
|
|
walk = "SPRITE_RED", surf = "SPRITE_SEEL",
|
|
bike = "SPRITE_RED_BIKE", fly = "SPRITE_BIRD",
|
|
}
|
|
|
|
-- Route22Gate_Script rewrites wLastMap from the player's Y every frame, so
|
|
-- the north exit leaves onto Route 23 and the south onto Route 22. Rules
|
|
-- are ordered, first match wins, the last row is the default.
|
|
local LAST_MAP_REWRITES = {
|
|
ROUTE_22_GATE = { axis = "y", rules = { { below = 4, map = "ROUTE_23" },
|
|
{ map = "ROUTE_22" } } },
|
|
-- UndergroundPathRoute{5,6,7,8}_Script force wLastMap to their own route on
|
|
-- map load, so crossing the tunnel and taking the far building's LAST_MAP
|
|
-- exit lands you on that route instead of the one you entered from.
|
|
UNDERGROUND_PATH_ROUTE_5 = { rules = { { map = "ROUTE_5" } } },
|
|
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 = {
|
|
palettes = PALETTES,
|
|
bookshelves = BOOKSHELVES,
|
|
fishing = FISHING,
|
|
safari = SAFARI,
|
|
playerSprites = PLAYER_SPRITES,
|
|
lastMapRewrites = LAST_MAP_REWRITES,
|
|
-- CheckIfInOutsideMap: what counts as "outside" for the wLastMap memory
|
|
outsideTilesets = { "OVERWORLD", "PLATEAU" },
|
|
-- the Route 16/18 gate scripts `res BIT_ALWAYS_ON_BIKE` every frame
|
|
forcedMovement = { clearMaps = { "ROUTE_16_GATE_1F", "ROUTE_18_GATE_1F" } },
|
|
-- the one-shot flag the gate's pass text is gated on; a gate a mod adds
|
|
-- gets "PASSED_<mapId>" instead of this pre-v2 spelling
|
|
badgeGates = { ROUTE_22_GATE = { passedFlag = "PASSED_ROUTE22_GATE" } },
|
|
-- VermilionGymSetDoorTile opens the motorized door once both locks are hit
|
|
hiddenExtras = {
|
|
trashCans = { map = "VERMILION_GYM",
|
|
doorBlock = { bx = 2, by = 2, block = 5 } },
|
|
},
|
|
-- IsSurfingAllowed refuses SURF on the B4F stairs square until both
|
|
-- plug boulders are down (engine/overworld/field_move_messages.asm).
|
|
-- B3F currents set BIT_FORCED_WARP so the south-edge water warps fire
|
|
-- without a held d-pad (scripts/SeafoamIslandsB3F.asm).
|
|
seafoam = {
|
|
SEAFOAM_ISLANDS_B3F = { setsForcedWarp = true },
|
|
SEAFOAM_ISLANDS_B4F = {
|
|
surfBlocked = { { x = 7, y = 11, untilEvents = {
|
|
"EVENT_SEAFOAM4_BOULDER1_DOWN_HOLE",
|
|
"EVENT_SEAFOAM4_BOULDER2_DOWN_HOLE" } } },
|
|
},
|
|
},
|
|
}
|
|
|
|
-- ------- Data.constants
|
|
|
|
FieldDefaults.CONSTANTS = {
|
|
world = {
|
|
poisonStepInterval = 4, -- ApplyOutOfBattlePoisonDamage
|
|
poisonDamage = 1,
|
|
blackoutMoneyDivisor = 2,
|
|
daycareExpPerStep = 1,
|
|
neighborHops = 2, -- connection hops drawn around the current map
|
|
stepFrames = 16, -- 1px per frame, 16 frames per tile
|
|
bikeStepFrames = 8, -- the bicycle doubles walking speed
|
|
turnFrames = 2, -- the extra OverworldLoop pass after a turn
|
|
},
|
|
-- cumulative slot thresholds out of 256 (engine/battle/wild_encounters.asm)
|
|
encounterBuckets = { 51, 102, 141, 166, 191, 216, 229, 242, 253, 256 },
|
|
-- the badge each HM's field move is gated on; distinct from
|
|
-- constants.hmMoves, which is the forget-gate move set
|
|
hmBadges = {
|
|
CUT = { badge = "CASCADEBADGE" }, SURF = { badge = "SOULBADGE" },
|
|
STRENGTH = { badge = "RAINBOWBADGE" }, FLY = { badge = "THUNDERBADGE" },
|
|
FLASH = { badge = "BOULDERBADGE" },
|
|
},
|
|
}
|
|
|
|
-- ------- accessors
|
|
|
|
-- data.field[key] with the vanilla table as the stale-cache fallback
|
|
function FieldDefaults.field(data, key)
|
|
local field = data and data.field
|
|
local value = field and field[key]
|
|
if value ~= nil then return value end
|
|
return FieldDefaults.FIELD[key]
|
|
end
|
|
|
|
local function walk(node, n, ...)
|
|
for i = 1, n do
|
|
if type(node) ~= "table" then return nil end
|
|
node = node[(select(i, ...))]
|
|
end
|
|
return node
|
|
end
|
|
|
|
-- one leaf inside a field sub-table, falling back per path so a cache that
|
|
-- stamps the record but not this key still resolves the vanilla value
|
|
function FieldDefaults.fieldValue(data, key, ...)
|
|
local n = select("#", ...)
|
|
local value = walk(data and data.field and data.field[key], n, ...)
|
|
if value ~= nil then return value end
|
|
return walk(FieldDefaults.FIELD[key], n, ...)
|
|
end
|
|
|
|
function FieldDefaults.constant(data, key)
|
|
local constants = data and data.constants
|
|
local value = constants and constants[key]
|
|
if value ~= nil then return value end
|
|
return FieldDefaults.CONSTANTS[key]
|
|
end
|
|
|
|
-- one world constant, falling back per key so a cache that stamps half of
|
|
-- constants.world still resolves the other half
|
|
function FieldDefaults.world(data, key)
|
|
local world = data and data.constants and data.constants.world
|
|
local value = world and world[key]
|
|
if value ~= nil then return value end
|
|
return FieldDefaults.CONSTANTS.world[key]
|
|
end
|
|
|
|
-- ------- seeding
|
|
|
|
-- fill-if-absent, never overwrite: an importer that learns to stamp one of
|
|
-- these silently takes over, and re-running is a no-op. Lists are leaves.
|
|
local function fill(dst, src)
|
|
for key, value in pairs(src) do
|
|
if dst[key] == nil then
|
|
if type(value) == "table" then
|
|
local copy = {}
|
|
fill(copy, value)
|
|
dst[key] = copy
|
|
else
|
|
dst[key] = value
|
|
end
|
|
elseif type(value) == "table" and type(dst[key]) == "table"
|
|
and #value == 0 then
|
|
fill(dst[key], value)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Called before the mod merge (Data:seedDefaults): puts the vanilla values
|
|
-- in data.field / data.constants so mod.content.field:patch("palettes", ...)
|
|
-- deep-merges over Kanto instead of replacing it.
|
|
function FieldDefaults.seed(data)
|
|
data.field = data.field or {}
|
|
data.constants = data.constants or {}
|
|
fill(data.field, FieldDefaults.FIELD)
|
|
fill(data.constants, FieldDefaults.CONSTANTS)
|
|
return data
|
|
end
|
|
|
|
return FieldDefaults
|