mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 23:11:15 +02:00
Add Pokemon Crystal as a sixth supported version
Crystal boots from a user-supplied ROM, imports a full cache and is playable: copyright, the Crystal intro movie, the animated title, gender select, Oak, and out into Johto. 122 of the cart's 169 script specials are implemented. Import and data - tools/make_crystal_manifest.py derives the manifest by importing make_gold_manifest as a library, with three additive keyword seams. Gold and Silver still regenerate byte-identical, which is the standing requirement for touching that generator. - crystal_symbol_deltas.py and crystal_movie_symbols.py carry the symbol delta: Crystal renames the credits mons, splits the trainer card, Pokegear and pack-pal blocks by gender, and replaces the intro and title outright. - Crystal-only manifest keys: engineFlagOrder (162 flags to Gold's 93, so the badge block sits one higher) and unownCharmap (the main charmap parser stops at the first newcharmap so the two cannot contaminate each other). Extractor - RomExtractorGen2 becomes three-edition. Crystal corrections: PAL_MAP_BANK 0x13, a flat PICS_FIX pic bank, audio bank 0x5e, the mapSongs id-100 hole, seven NPC trades, a TradeTexts stride of 8, the five Crystal tileset anim steps with per-row degrade, and the column-major trainer card portraits. - New: animated front sprites (frames, bitmasks, play and idle scripts), the Battle Tower roster, Kris assets, Mobile System GB art, and the Crystal intro and title via src/import/CrystalMovie.lua. Engine - GameVersion gains engine(id) and fixes(id). Gold and Silver keep their original bugs where the bug is not hardware dependent; Crystal gets the fixes Crystal shipped: Lucky Number boxes 10-14, surfing onto an NPC, and the Reflect and Light Screen defence overflow. - Crystal story: Suicune and Eusine, Celebi behind the GS Ball flag, the Ruins of Alph chambers, Buena, the Move Tutor, the Poke Seer, and the Battle Tower including the wInBattleTowerBattle badge-boost guard. - Kris and the gender flag, animated fronts in battle and the summary screen, and mon caught data. Verification - Every extracted asset is pixel-compared against pret's own source PNGs. - Gold caches are byte-identical before and after, file for file. - New Crystal suites plus a T2 Gen 2 tier; the full suite passes.
This commit is contained in:
@@ -103,6 +103,9 @@ local function applyLoaded(path, statusVerb)
|
||||
S.loadError = false
|
||||
S.allowSave = true
|
||||
end
|
||||
if Gen.of(S.save, S.version) == 2 then
|
||||
S.events = Catalog.gen2EventList(Gen.engineOf(S.save, S.version), S.modRoots)
|
||||
end
|
||||
local mapId = Gen.playerMap(S.save)
|
||||
S.mapId = mapId
|
||||
S.dirty = false
|
||||
@@ -169,16 +172,14 @@ function App.load(pathOverride, opts)
|
||||
for _, mod in ipairs(S.mods:status().loaded) do
|
||||
modRoots[#modRoots + 1] = mod.path
|
||||
end
|
||||
S.modRoots = modRoots
|
||||
if Gen.of(nil, opts.version) == 2 or require("src.core.GameVersion").generation() == 2 then
|
||||
S.events = Catalog.goldEventList(modRoots)
|
||||
S.events = Catalog.gen2EventList(Gen.engineOf(nil, opts.version), modRoots)
|
||||
else
|
||||
S.events = Catalog.scrapeEvents("data/scripts", "data/generated/trainer_headers.lua",
|
||||
nil, modRoots)
|
||||
end
|
||||
applyLoaded(pathOverride or SaveIO.defaultPath(), "Loaded")
|
||||
if Gen.of(S.save, S.version) == 2 then
|
||||
S.events = Catalog.goldEventList(modRoots)
|
||||
end
|
||||
end
|
||||
|
||||
-- Switch to another save file (Open button, drag-drop, or --save arg).
|
||||
|
||||
@@ -125,15 +125,8 @@ function Catalog.scrapeEvents(scriptDir, headerPath, listFiles, extraDirs)
|
||||
return sortedKeys(found)
|
||||
end
|
||||
|
||||
function Catalog.goldEventList(extraDirs)
|
||||
local names = {}
|
||||
local ok, flags = pcall(require, "src.core.gen2.FlagNames")
|
||||
if ok and flags and flags.events then
|
||||
for name in pairs(flags.events) do
|
||||
names[#names + 1] = name
|
||||
end
|
||||
end
|
||||
table.sort(names)
|
||||
function Catalog.gen2EventList(engine, extraDirs)
|
||||
local names = require("Gen2Flags").names(engine)
|
||||
local modFlags = Catalog.scrapeEvents(nil, nil, nil, extraDirs)
|
||||
local seen = {}
|
||||
for _, name in ipairs(names) do seen[name] = true end
|
||||
@@ -146,4 +139,8 @@ function Catalog.goldEventList(extraDirs)
|
||||
return names
|
||||
end
|
||||
|
||||
function Catalog.goldEventList(extraDirs)
|
||||
return Catalog.gen2EventList("gs", extraDirs)
|
||||
end
|
||||
|
||||
return Catalog
|
||||
|
||||
@@ -24,6 +24,37 @@ function Gen.of(save, version)
|
||||
return GameVersion.generation()
|
||||
end
|
||||
|
||||
local function versionOf(save, version)
|
||||
if type(save) == "table" and GameVersion.VERSIONS[save.version] then
|
||||
return save.version
|
||||
end
|
||||
if type(version) == "string" and GameVersion.VERSIONS[version] then
|
||||
return version
|
||||
end
|
||||
return GameVersion.get()
|
||||
end
|
||||
Gen.versionOf = versionOf
|
||||
|
||||
function Gen.engineOf(save, version)
|
||||
return GameVersion.engine(versionOf(save, version))
|
||||
end
|
||||
|
||||
function Gen.editionLabel(save, version)
|
||||
local info = GameVersion.info(versionOf(save, version))
|
||||
return tostring((info and info.label) or "GEN 2"):upper()
|
||||
end
|
||||
|
||||
function Gen.hasCaughtData(save, version)
|
||||
if Gen.of(save, version) ~= 2 then return false end
|
||||
return require("src.battle.gen2.Mon").hasCaughtData(versionOf(save, version))
|
||||
end
|
||||
|
||||
-- engine/menus/init_gender.asm:23-38
|
||||
function Gen.hasPlayerGender(save, version)
|
||||
if Gen.of(save, version) ~= 2 then return false end
|
||||
return Gen.engineOf(save, version) == "crystal"
|
||||
end
|
||||
|
||||
function Gen.ofState(S)
|
||||
if not S then return GameVersion.generation() end
|
||||
return Gen.of(S.save, S.version)
|
||||
@@ -110,10 +141,17 @@ function Gen.bindGoldData(data)
|
||||
end
|
||||
|
||||
function Gen.newGame(version)
|
||||
if (versionGeneration(version) or GameVersion.generation(version)) == 2 then
|
||||
return require("src.core.gen2.Save").newGame()
|
||||
local id = type(version) == "string" and GameVersion.VERSIONS[version] and version
|
||||
or nil
|
||||
if (versionGeneration(id) or GameVersion.generation()) == 2 then
|
||||
local Save2 = require("src.core.gen2.Save")
|
||||
local save = Save2.newGame({
|
||||
playerName = id and Save2.defaultPlayerName(id) or nil,
|
||||
})
|
||||
if id then save.version = id end
|
||||
return save
|
||||
end
|
||||
return require("src.core.SaveData").newGame()
|
||||
return require("src.core.SaveData").newGame({ version = id })
|
||||
end
|
||||
|
||||
function Gen.validate(save, data)
|
||||
@@ -223,6 +261,34 @@ function Gen.setCoins(save, amount)
|
||||
end
|
||||
end
|
||||
|
||||
-- constants/ram_constants.asm:176-177
|
||||
function Gen.playerGender(save)
|
||||
local stored = save and save.player and save.player.gender
|
||||
return stored == "female" and "female" or "male"
|
||||
end
|
||||
|
||||
function Gen.setPlayerGender(save, gender)
|
||||
save.player = save.player or {}
|
||||
save.player.gender = (gender == "female") and "female" or "male"
|
||||
return save.player.gender
|
||||
end
|
||||
|
||||
-- constants/landmark_constants.asm:3, :111-113
|
||||
function Gen.landmarkName(data, index)
|
||||
index = math.floor(tonumber(index) or 0)
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
if index == Mon.LANDMARK_EVENT then return "EVENT" end
|
||||
if index == Mon.LANDMARK_GIFT then return "GIFT" end
|
||||
if index == 0 then return "UNKNOWN" end
|
||||
local rows = data and data.gen2Landmarks and data.gen2Landmarks.landmarks
|
||||
for _, rec in pairs(rows or {}) do
|
||||
if type(rec) == "table" and rec.index == index then
|
||||
return (tostring(rec.name or rec.id):gsub("\n", " "))
|
||||
end
|
||||
end
|
||||
return ("#%d"):format(index)
|
||||
end
|
||||
|
||||
function Gen.dexOwnedKey(save)
|
||||
if Gen.of(save) == 2 then return "caught" end
|
||||
return "owned"
|
||||
@@ -317,14 +383,13 @@ function Gen.toggleBadge(save, id)
|
||||
return not on
|
||||
end
|
||||
|
||||
local function goldFlagId(name)
|
||||
local flags = require("src.core.gen2.FlagNames")
|
||||
return flags.events and flags.events[name]
|
||||
local function gen2FlagId(save, name)
|
||||
return require("Gen2Flags").byName(Gen.engineOf(save))[name]
|
||||
end
|
||||
|
||||
function Gen.getFlag(save, name)
|
||||
if Gen.of(save) == 2 then
|
||||
local id = goldFlagId(name)
|
||||
local id = gen2FlagId(save, name)
|
||||
if id then
|
||||
local Events2 = require("src.world.gen2.Events")
|
||||
local ev = Events2.new()
|
||||
@@ -338,7 +403,7 @@ end
|
||||
|
||||
function Gen.setFlag(save, name, on)
|
||||
if Gen.of(save) == 2 then
|
||||
local id = goldFlagId(name)
|
||||
local id = gen2FlagId(save, name)
|
||||
if id then
|
||||
local Events2 = require("src.world.gen2.Events")
|
||||
local ev = Events2.new()
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
-- ../pokecrystal/constants/event_flags.asm:1 against
|
||||
-- ../pokegold/constants/event_flags.asm:1
|
||||
|
||||
local Gen2Flags = {}
|
||||
|
||||
Gen2Flags.CRYSTAL_ADDED = {
|
||||
EVENT_ALAN_GAVE_FIRE_STONE = 257,
|
||||
EVENT_ANSWERED_DRAGON_MASTER_QUIZ_WRONG = 193,
|
||||
EVENT_AZALEA_TOWN_KURT = 1956,
|
||||
EVENT_BATTLE_TOWER_BATTLE_ROOM_YOUNGSTER = 1937,
|
||||
EVENT_BATTLE_TOWER_OPEN_CIVILIANS = 1999,
|
||||
EVENT_BEAT_BEAUTY_OLIVIA = 1473,
|
||||
EVENT_BEAT_BUG_CATCHER_WAYNE = 1472,
|
||||
EVENT_BEAT_CAMPER_QUENTIN = 1475,
|
||||
EVENT_BEAT_COOLTRAINERF_CARA = 1470,
|
||||
EVENT_BEAT_COOLTRAINERM_DARIN = 1469,
|
||||
EVENT_BEAT_FISHER_TULLY = 1108,
|
||||
EVENT_BEAT_FISHER_TULLY2 = 1119,
|
||||
EVENT_BEAT_FISHER_TULLY3 = 1120,
|
||||
EVENT_BEAT_POKEFANF_JAIME = 1474,
|
||||
EVENT_BEAT_POKEFANM_ALLAN = 1480,
|
||||
EVENT_BEAT_POKEFANM_REX = 1479,
|
||||
EVENT_BEAT_POKEMANIAC_MILLER = 1476,
|
||||
EVENT_BEAT_SAGE_GAKU = 1481,
|
||||
EVENT_BEAT_SAGE_KOJI = 1483,
|
||||
EVENT_BEAT_SAGE_MASA = 1482,
|
||||
EVENT_BEAT_SUPER_NERD_HUGH = 1477,
|
||||
EVENT_BEAT_SUPER_NERD_MARKUS = 1478,
|
||||
EVENT_BEAT_SUPER_NERD_STAN = 1409,
|
||||
EVENT_BEAT_TWINS_LEA_AND_PIA = 1471,
|
||||
EVENT_BUENA_OFFERED_HER_PHONE_NUMBER = 828,
|
||||
EVENT_BUENA_OFFERED_HER_PHONE_NUMBER_NO_BLUE_CARD = 670,
|
||||
EVENT_BUGGING_KURT_TOO_MUCH = 187,
|
||||
EVENT_BURNED_TOWER_1F_EUSINE = 1893,
|
||||
EVENT_BURNED_TOWER_1F_HIDDEN_ULTRA_BALL = 255,
|
||||
EVENT_BURNED_TOWER_1F_HP_UP = 1622,
|
||||
EVENT_BURNED_TOWER_MORTY = 1892,
|
||||
EVENT_CAN_GIVE_GS_BALL_TO_KURT = 190,
|
||||
EVENT_CIANWOOD_CITY_EUSINE = 1965,
|
||||
EVENT_COPYCAT_1 = 1774,
|
||||
EVENT_COPYCAT_2 = 1775,
|
||||
EVENT_DANA_GAVE_THUNDERSTONE = 258,
|
||||
EVENT_DARK_CAVE_VIOLET_ENTRANCE_DIRE_HIT = 1998,
|
||||
EVENT_DRAGONS_DEN_B1F_CALCIUM = 1983,
|
||||
EVENT_DRAGONS_DEN_B1F_MAX_ELIXER = 1984,
|
||||
EVENT_DRAGON_SHRINE_CLAIR = 1936,
|
||||
EVENT_ECRUTEAK_CITY_GRAMPS = 1961,
|
||||
EVENT_ECRUTEAK_GYM_GRAMPS = 1960,
|
||||
EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_WANDERING_SAGE = 1969,
|
||||
EVENT_ERIN_CALCIUM = 827,
|
||||
EVENT_EUSINE_IN_BURNED_TOWER = 1962,
|
||||
EVENT_FLORIA_AT_FLOWER_SHOP = 1896,
|
||||
EVENT_FLORIA_AT_SUDOWOODO = 1897,
|
||||
EVENT_FOREST_IS_RESTLESS = 192,
|
||||
EVENT_FOUGHT_EUSINE = 819,
|
||||
EVENT_FOUGHT_SUICUNE = 821,
|
||||
EVENT_GAVE_GS_BALL_TO_KURT = 191,
|
||||
EVENT_GINA_GAVE_LEAF_STONE = 256,
|
||||
EVENT_GOLDENROD_CITY_MOVE_TUTOR = 1898,
|
||||
EVENT_GOLDENROD_GAME_CORNER_MOVE_TUTOR = 1899,
|
||||
EVENT_GOLDENROD_SALE_OFF = 1776,
|
||||
EVENT_GOLDENROD_SALE_ON = 1777,
|
||||
EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_ULTRA_BALL = 1621,
|
||||
EVENT_GOT_CLEAR_BELL = 120,
|
||||
EVENT_GOT_DRATINI = 189,
|
||||
EVENT_GOT_GS_BALL_FROM_GOLDENROD_POKEMON_CENTER = 832,
|
||||
EVENT_GOT_ODD_EGG = 830,
|
||||
EVENT_GOT_RAINBOW_WING = 822,
|
||||
EVENT_HOLE_IN_BURNED_TOWER = 818,
|
||||
EVENT_HUEY_PROTEIN = 823,
|
||||
EVENT_ICE_PATH_1F_PROTEIN = 1982,
|
||||
EVENT_ILEX_FOREST_ANTIDOTE = 1978,
|
||||
EVENT_ILEX_FOREST_ETHER = 1979,
|
||||
EVENT_ILEX_FOREST_FARFETCHD = 1769,
|
||||
EVENT_ILEX_FOREST_KURT = 1957,
|
||||
EVENT_ILEX_FOREST_LASS = 1773,
|
||||
EVENT_ILEX_FOREST_X_ATTACK = 1977,
|
||||
EVENT_JOEY_HP_UP = 824,
|
||||
EVENT_KOJI_ALLOWS_YOU_PASSAGE_TO_TIN_TOWER = 820,
|
||||
EVENT_KURTS_HOUSE_GRANDDAUGHTER_1 = 1932,
|
||||
EVENT_KURTS_HOUSE_GRANDDAUGHTER_2 = 1933,
|
||||
EVENT_LAKE_OF_RAGE_ELIXER = 1605,
|
||||
EVENT_LAKE_OF_RAGE_ELIXIR_ON_STANDBY = 58,
|
||||
EVENT_MET_BUENA = 829,
|
||||
EVENT_MET_FLORIA = 185,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_IRON = 1992,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_MAX_POTION = 1958,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_NUGGET = 1959,
|
||||
EVENT_MOUNT_MORTAR_1F_INSIDE_ULTRA_BALL = 1993,
|
||||
EVENT_MOUNT_MORTAR_B1F_CARBOS = 1671,
|
||||
EVENT_MOUNT_MORTAR_B1F_FULL_RESTORE = 1994,
|
||||
EVENT_MOUNT_MORTAR_B1F_MAX_ETHER = 1995,
|
||||
EVENT_MOUNT_MORTAR_B1F_PP_UP = 1996,
|
||||
EVENT_OLIVINE_LIGHTHOUSE_5F_SUPER_REPEL = 1638,
|
||||
EVENT_PARRY_IRON = 826,
|
||||
EVENT_PICKED_UP_BERRY_FROM_KABUTO_ITEM_ROOM = 1944,
|
||||
EVENT_PICKED_UP_CHARCOAL_FROM_HO_OH_ITEM_ROOM = 1943,
|
||||
EVENT_PICKED_UP_ENERGYPOWDER_FROM_KABUTO_ITEM_ROOM = 1947,
|
||||
EVENT_PICKED_UP_ENERGY_ROOT_FROM_AERODACTYL_ITEM_ROOM = 1955,
|
||||
EVENT_PICKED_UP_GOLD_BERRY_FROM_AERODACTYL_ITEM_ROOM = 1952,
|
||||
EVENT_PICKED_UP_GOLD_BERRY_FROM_HO_OH_ITEM_ROOM = 1940,
|
||||
EVENT_PICKED_UP_HEAL_POWDER_FROM_AERODACTYL_ITEM_ROOM = 1954,
|
||||
EVENT_PICKED_UP_HEAL_POWDER_FROM_KABUTO_ITEM_ROOM = 1946,
|
||||
EVENT_PICKED_UP_MOON_STONE_FROM_AERODACTYL_ITEM_ROOM = 1953,
|
||||
EVENT_PICKED_UP_MYSTERYBERRY_FROM_HO_OH_ITEM_ROOM = 1941,
|
||||
EVENT_PICKED_UP_MYSTERYBERRY_FROM_OMANYTE_ITEM_ROOM = 1948,
|
||||
EVENT_PICKED_UP_MYSTIC_WATER_FROM_OMANYTE_ITEM_ROOM = 1949,
|
||||
EVENT_PICKED_UP_PSNCUREBERRY_FROM_KABUTO_ITEM_ROOM = 1945,
|
||||
EVENT_PICKED_UP_REVIVAL_HERB_FROM_HO_OH_ITEM_ROOM = 1942,
|
||||
EVENT_PICKED_UP_STARDUST_FROM_OMANYTE_ITEM_ROOM = 1950,
|
||||
EVENT_PICKED_UP_STAR_PIECE_FROM_OMANYTE_ITEM_ROOM = 1951,
|
||||
EVENT_PLAYERS_HOUSE_1F_NEIGHBOR = 1938,
|
||||
EVENT_PLAYERS_NEIGHBORS_HOUSE_NEIGHBOR = 1939,
|
||||
EVENT_RADIO_TOWER_5F_ULTRA_BALL = 1997,
|
||||
EVENT_RANG_CLEAR_BELL_1 = 1894,
|
||||
EVENT_RANG_CLEAR_BELL_2 = 1895,
|
||||
EVENT_ROUTE_30_ANTIDOTE = 1976,
|
||||
EVENT_ROUTE_31_POTION = 1710,
|
||||
EVENT_ROUTE_32_REPEL = 1713,
|
||||
EVENT_ROUTE_34_ILEX_FOREST_GATE_LASS = 1771,
|
||||
EVENT_ROUTE_34_ILEX_FOREST_GATE_TEACHER_BEHIND_COUNTER = 1770,
|
||||
EVENT_ROUTE_34_ILEX_FOREST_GATE_TEACHER_IN_WALKWAY = 1772,
|
||||
EVENT_ROUTE_34_NUGGET = 1980,
|
||||
EVENT_ROUTE_44_MAX_REPEL = 1981,
|
||||
EVENT_ROUTE_45_NUGGET = 1720,
|
||||
EVENT_ROUTE_46_X_SPEED = 1724,
|
||||
EVENT_RUINS_OF_ALPH_OUTSIDE_TOURIST_FISHER = 1934,
|
||||
EVENT_RUINS_OF_ALPH_OUTSIDE_TOURIST_YOUNGSTERS = 1935,
|
||||
EVENT_SAW_SUICUNE_AT_CIANWOOD_CITY = 1966,
|
||||
EVENT_SAW_SUICUNE_ON_ROUTE_36 = 1968,
|
||||
EVENT_SAW_SUICUNE_ON_ROUTE_42 = 1967,
|
||||
EVENT_SET_WHEN_FOUGHT_HO_OH = 1975,
|
||||
EVENT_SILVER_CAVE_ROOM_1_PROTEIN = 1690,
|
||||
EVENT_SILVER_CAVE_ROOM_1_ULTRA_BALL = 1985,
|
||||
EVENT_SILVER_CAVE_ROOM_2_CALCIUM = 1986,
|
||||
EVENT_SILVER_CAVE_ROOM_2_PP_UP = 1988,
|
||||
EVENT_SILVER_CAVE_ROOM_2_ULTRA_BALL = 1987,
|
||||
EVENT_SPROUT_TOWER_2F_X_ACCURACY = 1608,
|
||||
EVENT_TALKED_TO_FLORIA_AT_FLOWER_SHOP = 186,
|
||||
EVENT_TALKED_TO_RUINS_COWARD = 188,
|
||||
EVENT_TEAM_ROCKET_BASE_B1F_GUARD_SPEC = 1643,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_FULL_HEAL = 1647,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_PROTEIN = 1645,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_ULTRA_BALL = 1620,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_X_SPECIAL = 1646,
|
||||
EVENT_TIFFANY_GAVE_PINK_BOW = 260,
|
||||
EVENT_TIN_TOWER_1F_ENTEI = 1971,
|
||||
EVENT_TIN_TOWER_1F_EUSINE = 1973,
|
||||
EVENT_TIN_TOWER_1F_RAIKOU = 1972,
|
||||
EVENT_TIN_TOWER_1F_SUICUNE = 1970,
|
||||
EVENT_TIN_TOWER_1F_WISE_TRIO_1 = 1974,
|
||||
EVENT_TIN_TOWER_1F_WISE_TRIO_2 = 1989,
|
||||
EVENT_TIN_TOWER_4F_PP_UP = 1613,
|
||||
EVENT_TIN_TOWER_6F_MAX_POTION = 1990,
|
||||
EVENT_TIN_TOWER_9F_HP_UP = 1991,
|
||||
EVENT_TULLY_ASKED_FOR_PHONE_NUMBER = 655,
|
||||
EVENT_TULLY_GAVE_WATER_STONE = 259,
|
||||
EVENT_UNION_CAVE_1F_POTION = 1628,
|
||||
EVENT_UNION_CAVE_1F_X_ATTACK = 1627,
|
||||
EVENT_VANCE_CARBOS = 825,
|
||||
EVENT_VICTORY_ROAD_HP_UP = 1703,
|
||||
EVENT_WADE_HAS_BERRY = 811,
|
||||
EVENT_WADE_HAS_BITTER_BERRY = 814,
|
||||
EVENT_WADE_HAS_PRZCUREBERRY = 813,
|
||||
EVENT_WADE_HAS_PSNCUREBERRY = 812,
|
||||
EVENT_WALL_OPENED_IN_AERODACTYL_CHAMBER = 809,
|
||||
EVENT_WALL_OPENED_IN_HO_OH_CHAMBER = 806,
|
||||
EVENT_WALL_OPENED_IN_KABUTO_CHAMBER = 807,
|
||||
EVENT_WALL_OPENED_IN_OMANYTE_CHAMBER = 808,
|
||||
EVENT_WELCOMED_TO_POKECOM_CENTER = 810,
|
||||
EVENT_WHIRL_ISLAND_SW_ULTRA_BALL = 1680,
|
||||
EVENT_WILTON_HAS_GREAT_BALL = 816,
|
||||
EVENT_WILTON_HAS_POKE_BALL = 817,
|
||||
EVENT_WILTON_HAS_ULTRA_BALL = 815,
|
||||
EVENT_WISE_TRIOS_ROOM_WISE_TRIO_1 = 1963,
|
||||
EVENT_WISE_TRIOS_ROOM_WISE_TRIO_2 = 1964,
|
||||
}
|
||||
|
||||
Gen2Flags.CRYSTAL_REMOVED = {
|
||||
EVENT_ALAN_READY_FOR_REMATCH = true,
|
||||
EVENT_ANTHONY_READY_FOR_REMATCH = true,
|
||||
EVENT_ARNIE_READY_FOR_REMATCH = true,
|
||||
EVENT_BEAT_FISHER_CHRIS = true,
|
||||
EVENT_BEAT_FISHER_CHRIS2 = true,
|
||||
EVENT_BEAT_FISHER_CHRIS3 = true,
|
||||
EVENT_BEAT_SUPER_NERD_ERIC_UNUSED = true,
|
||||
EVENT_BETH_READY_FOR_REMATCH = true,
|
||||
EVENT_BEVERLY_READY_FOR_REMATCH = true,
|
||||
EVENT_BRENT_READY_FOR_REMATCH = true,
|
||||
EVENT_BURNED_TOWER_1F_BURN_HEAL = true,
|
||||
EVENT_BURNED_TOWER_1F_X_SPEED = true,
|
||||
EVENT_BURNED_TOWER_B1F_HIDDEN_BURN_HEAL = true,
|
||||
EVENT_BURNED_TOWER_B1F_HIDDEN_NUGGET = true,
|
||||
EVENT_BURNED_TOWER_B1F_HIDDEN_ULTRA_BALL = true,
|
||||
EVENT_BURNED_TOWER_B1F_HP_UP = true,
|
||||
EVENT_BURNED_TOWER_FIREBREATHER_DICK_ASHES = true,
|
||||
EVENT_BURNED_TOWER_FIREBREATHER_DICK_NORMAL = true,
|
||||
EVENT_CHAD_READY_FOR_REMATCH = true,
|
||||
EVENT_CHRIS_ASKED_FOR_PHONE_NUMBER = true,
|
||||
EVENT_CHRIS_READY_FOR_REMATCH = true,
|
||||
EVENT_DANA_READY_FOR_REMATCH = true,
|
||||
EVENT_DEREK_READY_FOR_REMATCH = true,
|
||||
EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_LEFT = true,
|
||||
EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_RIGHT = true,
|
||||
EVENT_ERIN_READY_FOR_REMATCH = true,
|
||||
EVENT_GAVEN_READY_FOR_REMATCH = true,
|
||||
EVENT_GINA_READY_FOR_REMATCH = true,
|
||||
EVENT_HUEY_READY_FOR_REMATCH = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_1 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_10 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_2 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_3 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_4 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_5 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_6 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_7 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_8 = true,
|
||||
EVENT_ILEX_FOREST_FARFETCHD_9 = true,
|
||||
EVENT_IRWIN_READY_FOR_REMATCH = true,
|
||||
EVENT_JACK_READY_FOR_REMATCH = true,
|
||||
EVENT_JOEY_READY_FOR_REMATCH = true,
|
||||
EVENT_JOSE_READY_FOR_REMATCH = true,
|
||||
EVENT_KENJI_READY_FOR_REMATCH = true,
|
||||
EVENT_LAKE_OF_RAGE_ETHER_ON_STANDBY = true,
|
||||
EVENT_LAKE_OF_RAGE_MAX_ETHER = true,
|
||||
EVENT_LIZ_READY_FOR_REMATCH = true,
|
||||
EVENT_MOUNT_MORTAR_B1F_FULL_HEAL = true,
|
||||
EVENT_OLIVINE_LIGHTHOUSE_5F_GREAT_BALL = true,
|
||||
EVENT_PARRY_READY_FOR_REMATCH = true,
|
||||
EVENT_RALPH_READY_FOR_REMATCH = true,
|
||||
EVENT_REENA_READY_FOR_REMATCH = true,
|
||||
EVENT_ROUTE_31_ANTIDOTE = true,
|
||||
EVENT_ROUTE_32_POTION = true,
|
||||
EVENT_ROUTE_45_X_SPECIAL = true,
|
||||
EVENT_ROUTE_46_DIRE_HIT = true,
|
||||
EVENT_SILVER_CAVE_ROOM_1_X_ACCURACY = true,
|
||||
EVENT_SPROUT_TOWER_2F_X_DEFEND = true,
|
||||
EVENT_TEAM_ROCKET_BASE_B1F_X_ACCURACY = true,
|
||||
EVENT_TEAM_ROCKET_BASE_B3F_DIRE_HIT = true,
|
||||
EVENT_TIFFANY_READY_FOR_REMATCH = true,
|
||||
EVENT_TIN_TOWER_4F_SUPER_POTION = true,
|
||||
EVENT_TODD_READY_FOR_REMATCH = true,
|
||||
EVENT_VANCE_READY_FOR_REMATCH = true,
|
||||
EVENT_VICTORY_ROAD_X_SPECIAL = true,
|
||||
EVENT_WADE_READY_FOR_REMATCH = true,
|
||||
EVENT_WHIRL_ISLAND_SW_GUARD_SPEC = true,
|
||||
EVENT_WILTON_READY_FOR_REMATCH = true,
|
||||
}
|
||||
|
||||
local cache = {}
|
||||
|
||||
function Gen2Flags.byName(engine)
|
||||
engine = (engine == "crystal") and "crystal" or "gs"
|
||||
if cache[engine] then return cache[engine] end
|
||||
local ids = {}
|
||||
local ok, flags = pcall(require, "src.core.gen2.FlagNames")
|
||||
if ok and type(flags) == "table" and type(flags.events) == "table" then
|
||||
for name, id in pairs(flags.events) do ids[name] = id end
|
||||
end
|
||||
if engine == "crystal" then
|
||||
for name in pairs(Gen2Flags.CRYSTAL_REMOVED) do ids[name] = nil end
|
||||
for name, id in pairs(Gen2Flags.CRYSTAL_ADDED) do ids[name] = id end
|
||||
end
|
||||
cache[engine] = ids
|
||||
return ids
|
||||
end
|
||||
|
||||
function Gen2Flags.names(engine)
|
||||
local names = {}
|
||||
for name in pairs(Gen2Flags.byName(engine)) do names[#names + 1] = name end
|
||||
table.sort(names)
|
||||
return names
|
||||
end
|
||||
|
||||
return Gen2Flags
|
||||
@@ -1141,4 +1141,74 @@ function Ops.setPokerus(S, mon, value)
|
||||
return Ops.mark(S, ("%s pokerus byte %d"):format(mon.species, want))
|
||||
end
|
||||
|
||||
-- engine/pokemon/caught_data.asm:169-172
|
||||
Ops.CAUGHT_TIMES = { "UNKNOWN", "MORN", "DAY", "NITE" }
|
||||
|
||||
local function caughtGuard(S, mon)
|
||||
if not mon then return false end
|
||||
if not Gen.hasCaughtData(S.save, S.version) then
|
||||
return Ops.say(S, "This game has no caught data")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Ops.setCaughtTime(S, mon, value)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local want = clamp(math.floor(tonumber(value) or 0), 0, 3)
|
||||
if want == (mon.caughtTime or 0) then
|
||||
return Ops.say(S, ("Caught time is already %s"):format(Ops.CAUGHT_TIMES[want + 1]))
|
||||
end
|
||||
mon.caughtTime = want
|
||||
return Ops.mark(S, ("%s caught time %s")
|
||||
:format(mon.species, Ops.CAUGHT_TIMES[want + 1]))
|
||||
end
|
||||
|
||||
-- constants/pokemon_data_constants.asm:120-121
|
||||
function Ops.setCaughtLevel(S, mon, value)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
local want = clamp(math.floor(tonumber(value) or 0), 0, Mon.CAUGHT_LEVEL_MASK)
|
||||
if want == (mon.caughtLevel or 0) then
|
||||
return Ops.say(S, ("Caught level is already %d"):format(want))
|
||||
end
|
||||
mon.caughtLevel = want
|
||||
return Ops.mark(S, ("%s caught level %d"):format(mon.species, want))
|
||||
end
|
||||
|
||||
-- constants/landmark_constants.asm:111-113
|
||||
function Ops.setCaughtLocation(S, mon, value)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
local span = Mon.CAUGHT_LOCATION_MASK + 1
|
||||
local want = math.floor(tonumber(value) or 0) % span
|
||||
if want == (mon.caughtLocation or 0) then
|
||||
return Ops.say(S, ("Caught location is already %s")
|
||||
:format(Gen.landmarkName(S.data, want)))
|
||||
end
|
||||
mon.caughtLocation = want
|
||||
return Ops.mark(S, ("%s caught at %s")
|
||||
:format(mon.species, Gen.landmarkName(S.data, want)))
|
||||
end
|
||||
|
||||
function Ops.setCaughtByGender(S, mon, gender)
|
||||
if not caughtGuard(S, mon) then return false end
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
local want = Mon.caughtGenderOf(gender)
|
||||
if want == mon.caughtByGender then
|
||||
return Ops.say(S, ("Caught by is already %s"):format(tostring(want or "none")))
|
||||
end
|
||||
mon.caughtByGender = want
|
||||
return Ops.mark(S, ("%s caught by %s"):format(mon.species, tostring(want or "none")))
|
||||
end
|
||||
|
||||
function Ops.setPlayerGender(S, gender)
|
||||
if not Gen.hasPlayerGender(S.save, S.version) then
|
||||
return Ops.say(S, "This game has no player gender")
|
||||
end
|
||||
if Gen.playerGender(S.save) == gender then
|
||||
return Ops.say(S, ("Player is already %s"):format(tostring(gender)))
|
||||
end
|
||||
return Ops.mark(S, ("Player gender %s"):format(Gen.setPlayerGender(S.save, gender)))
|
||||
end
|
||||
|
||||
return Ops
|
||||
|
||||
@@ -26,6 +26,7 @@ function State.new()
|
||||
-- App.load's opts; nil in a bare `love . --editor` run.
|
||||
version = nil,
|
||||
slotId = nil,
|
||||
modRoots = nil,
|
||||
-- Hosted inside the launcher process (Edit on a save row) rather than a
|
||||
-- standalone `--editor` window: Close returns to the launcher instead of
|
||||
-- quitting, and App calls onClose() to do it.
|
||||
|
||||
@@ -101,6 +101,31 @@ local function drawMoney(S, Kit, x, y, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
-- engine/menus/init_gender.asm:55-56
|
||||
local GENDERS = { { "BOY", "male" }, { "GIRL", "female" } }
|
||||
|
||||
local function trainerHeight(Kit, s, pad)
|
||||
return pad * 2 + Kit.textHeight("caption") + 10 * s + 28 * s
|
||||
end
|
||||
|
||||
local function drawTrainer(S, Kit, x, y, w, h)
|
||||
local s = Kit.scale
|
||||
local pad = 16 * s
|
||||
Kit.card(x, y, w, h)
|
||||
Kit.caption(x + pad, y + pad, "TRAINER")
|
||||
Kit.textRight("mono", tostring((S.save.player and S.save.player.name) or "?"),
|
||||
x + w - pad, y + pad, PAL.caption)
|
||||
local cy = y + pad + Kit.textHeight("caption") + 10 * s
|
||||
local chipW = (w - 2 * pad - 8 * s) / 2
|
||||
local gender = Gen.playerGender(S.save)
|
||||
for i, pair in ipairs(GENDERS) do
|
||||
if Kit.chip(x + pad + (i - 1) * (chipW + 8 * s), cy, chipW, 28 * s,
|
||||
pair[1], gender == pair[2], PAL.green, PAL.steel) then
|
||||
Ops.setPlayerGender(S, pair[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local BADGE_COLS = 4
|
||||
|
||||
local function badgeHeight(S, Kit, s, pad)
|
||||
@@ -273,6 +298,10 @@ function M.draw(S, Kit, x, y, w, h)
|
||||
local listH = 300 * s
|
||||
Kit.pushClip(x, y, w, h)
|
||||
local cy = y - off
|
||||
if Gen.hasPlayerGender(S.save, S.version) then
|
||||
local trainerH = trainerHeight(Kit, s, pad)
|
||||
drawTrainer(S, Kit, x, cy, w, trainerH); cy = cy + trainerH + gap
|
||||
end
|
||||
drawMoney(S, Kit, x, cy, w, moneyH); cy = cy + moneyH + gap
|
||||
drawPicker(S, Kit, x, cy, w, pickH); cy = cy + pickH + gap
|
||||
drawBadges(S, Kit, x, cy, w, badgeH); cy = cy + badgeH + gap
|
||||
@@ -294,8 +323,14 @@ function M.draw(S, Kit, x, y, w, h)
|
||||
-- made the old panel unusable.
|
||||
local moneyH = moneyHeight(Kit, s, pad, S)
|
||||
local badgeH = badgeHeight(S, Kit, s, pad)
|
||||
drawMoney(S, Kit, x, y, leftW, moneyH)
|
||||
drawPicker(S, Kit, x, y + moneyH + gap, leftW, h - moneyH - badgeH - 2 * gap)
|
||||
local topH = 0
|
||||
if Gen.hasPlayerGender(S.save, S.version) then
|
||||
topH = trainerHeight(Kit, s, pad) + gap
|
||||
drawTrainer(S, Kit, x, y, leftW, topH - gap)
|
||||
end
|
||||
drawMoney(S, Kit, x, y + topH, leftW, moneyH)
|
||||
drawPicker(S, Kit, x, y + topH + moneyH + gap, leftW,
|
||||
h - topH - moneyH - badgeH - 2 * gap)
|
||||
drawBadges(S, Kit, x, y + h - badgeH, leftW, badgeH)
|
||||
drawBag(S, Kit, bagX, y, listW, h)
|
||||
drawPc(S, Kit, pcX, y, listW, h)
|
||||
|
||||
@@ -147,6 +147,66 @@ local function drawDvRows(S, Kit, mon, cx, rowY, colW, rowH, rowGap)
|
||||
end
|
||||
end
|
||||
|
||||
-- engine/pokemon/caught_data.asm:168-199
|
||||
local CAUGHT_BY = { { "-", "none" }, { "BOY", "boy" }, { "GIRL", "girl" } }
|
||||
|
||||
local function drawCaughtRows(S, Kit, mon, cx, y, inner, row)
|
||||
local s = Kit.scale
|
||||
local chipH = 22 * s
|
||||
local gap = 6 * s
|
||||
local tinyH = Kit.textHeight("tiny")
|
||||
|
||||
Kit.text("tiny", "CAUGHT", cx, y + (row - tinyH) / 2, PAL.caption)
|
||||
local timeX = cx + 62 * s
|
||||
local timeW = math.max(34 * s, (cx + inner - timeX - 3 * gap) / 4)
|
||||
for i, label in ipairs(Ops.CAUGHT_TIMES) do
|
||||
if Kit.chip(timeX + (i - 1) * (timeW + gap), y + (row - chipH) / 2,
|
||||
timeW, chipH, label, (mon.caughtTime or 0) == i - 1, PAL.blue, PAL.steel) then
|
||||
Ops.setCaughtTime(S, mon, i - 1)
|
||||
end
|
||||
end
|
||||
y = y + row + gap
|
||||
|
||||
local btn = 24 * s
|
||||
local lvX = cx + inner - 2 * btn - gap
|
||||
if Kit.stepper(lvX, y + (row - btn) / 2, btn, btn, "-", { font = "small" }) then
|
||||
Ops.setCaughtLevel(S, mon, (mon.caughtLevel or 0) - 1)
|
||||
end
|
||||
if Kit.stepper(lvX + btn + gap, y + (row - btn) / 2, btn, btn, "+",
|
||||
{ font = "small" }) then
|
||||
Ops.setCaughtLevel(S, mon, (mon.caughtLevel or 0) + 1)
|
||||
end
|
||||
Kit.textRight("tiny", ("MET LV %d"):format(mon.caughtLevel or 0), lvX - 10 * s,
|
||||
y + (row - tinyH) / 2, PAL.text)
|
||||
Kit.text("tiny", "OT", cx, y + (row - tinyH) / 2, PAL.caption)
|
||||
local otX = cx + 26 * s
|
||||
local otW = math.max(30 * s, (inner * 0.42 - 26 * s - 2 * gap) / 3)
|
||||
for i, pair in ipairs(CAUGHT_BY) do
|
||||
if Kit.chip(otX + (i - 1) * (otW + gap), y + (row - chipH) / 2, otW, chipH,
|
||||
pair[1], (mon.caughtByGender or "none") == pair[2], PAL.blue, PAL.steel) then
|
||||
Ops.setCaughtByGender(S, mon, pair[2])
|
||||
end
|
||||
end
|
||||
y = y + row + gap
|
||||
|
||||
local whereX = cx + inner - 3 * btn - 2 * gap
|
||||
if Kit.stepper(whereX, y + (row - btn) / 2, btn, btn, "-", { font = "small" }) then
|
||||
Ops.setCaughtLocation(S, mon, (mon.caughtLocation or 0) - 1)
|
||||
end
|
||||
if Kit.stepper(whereX + btn + gap, y + (row - btn) / 2, btn, btn, "+",
|
||||
{ font = "small" }) then
|
||||
Ops.setCaughtLocation(S, mon, (mon.caughtLocation or 0) + 1)
|
||||
end
|
||||
if Kit.button(whereX + 2 * (btn + gap), y + (row - btn) / 2, btn, btn, "0",
|
||||
{ kind = "danger", font = "micro", radius = 6 * s }) then
|
||||
Ops.setCaughtLocation(S, mon, 0)
|
||||
end
|
||||
local where = ("WHERE %s"):format(Gen.landmarkName(S.data, mon.caughtLocation or 0))
|
||||
Kit.text("tiny", Kit.ellipsize("tiny", where, whereX - 10 * s - cx), cx,
|
||||
y + (row - tinyH) / 2, PAL.text)
|
||||
return y + row + gap
|
||||
end
|
||||
|
||||
local function drawMoveRows(S, Kit, mon, rightX, rowY, colW, rowH, rowGap)
|
||||
local s = Kit.scale
|
||||
for slot = 1, 4 do
|
||||
@@ -231,6 +291,7 @@ function MonEditor.draw(S, Kit, x, y, w, h)
|
||||
-- the field + Set row
|
||||
local extraH = 0
|
||||
if Gen.ofState(S) == 2 then extraH = 88 * s end
|
||||
if Gen.hasCaughtData(S.save, S.version) then extraH = extraH + 102 * s end
|
||||
local nickFieldH = 30 * s
|
||||
local contentH = pad + headerH + 18 * s
|
||||
+ capH + 10 * s + nickFieldH + 18 * s
|
||||
@@ -341,7 +402,7 @@ function MonEditor.draw(S, Kit, x, y, w, h)
|
||||
local colY = statsY + cellH + 18 * s
|
||||
if Gen.ofState(S) == 2 then
|
||||
local extraY = colY
|
||||
Kit.caption(cx, extraY, "GOLD")
|
||||
Kit.caption(cx, extraY, Gen.editionLabel(S.save, S.version))
|
||||
extraY = extraY + capH + 8 * s
|
||||
local row = 28 * s
|
||||
Kit.text("tiny", "HELD " .. tostring(mon.item or "none"), cx, extraY, PAL.text)
|
||||
@@ -371,7 +432,11 @@ function MonEditor.draw(S, Kit, x, y, w, h)
|
||||
if mon.unownLetter then bits[#bits + 1] = "Unown " .. tostring(mon.unownLetter) end
|
||||
Kit.text("tiny", table.concat(bits, " ") ~= "" and table.concat(bits, " ")
|
||||
or "gender/shiny follow DVs", cx, extraY, PAL.caption)
|
||||
colY = extraY + 22 * s
|
||||
extraY = extraY + 22 * s
|
||||
if Gen.hasCaughtData(S.save, S.version) then
|
||||
extraY = drawCaughtRows(S, Kit, mon, cx, extraY, inner, row)
|
||||
end
|
||||
colY = extraY
|
||||
end
|
||||
if narrow then
|
||||
-- stacked: DVs first, then moves, then the two actions side by side at
|
||||
|
||||
Reference in New Issue
Block a user