From df0be1cba67441a19f7b5dd937058043794efd1c Mon Sep 17 00:00:00 2001 From: 1jamie Date: Fri, 14 Aug 2026 18:43:38 -0500 Subject: [PATCH] Add Gold save-editor support, rearrange the launcher, and ship a patch-notes modal. The save editor now routes Gold vs RBY through a generation adapter so boxes, items, events, and maps write the right fields. The launcher layout is shuffled a bit, and patch notes can come from the updater, a packed file, or the iOS sidecar. --- main.lua | 22 - scripts/pack_love.sh | 6 + scripts/test.sh | 1 + src/battle/gen2/Mon.lua | 59 + src/core/Game2.lua | 7 +- src/core/gen2/FlagNames.lua | 1340 ++++++++++++++++ src/import/LauncherView.lua | 272 +++- src/import/RomImporter.lua | 4 +- src/ui/gen2/HallOfFame.lua | 2 +- src/ui/gen2/PackMenu.lua | 4 +- src/update/Check.lua | 15 +- src/update/PatchNotes.lua | 112 ++ src/update/check_worker.lua | 7 +- src/world/gen2/MapPreview.lua | 186 +++ tests/drivers/gold/flag_names.lua | 1342 +---------------- tests/engine/launcher_mod_scope_dropdown.lua | 62 + .../launcher_modal_focus_input_test.lua | 8 +- tests/engine/launcher_patch_notes.lua | 89 ++ tests/engine/update_check_tests.lua | 9 + tests/engine/update_tests.lua | 1 + tests/run_save_editor_tests.lua | 1 + tests/run_tests.lua | 2 + tests/save_editor_gen2_tests.lua | 352 +++++ tests/save_editor_mod_tests.lua | 37 +- tools/goldwalk/gen_flags.lua | 2 +- tools/save-editor/App.lua | 91 +- tools/save-editor/Catalog.lua | 24 +- tools/save-editor/Gen.lua | 350 +++++ tools/save-editor/MonOps.lua | 53 +- tools/save-editor/Ops.lua | 316 +++- tools/save-editor/panels/Boxes.lua | 19 +- tools/save-editor/panels/Dex.lua | 3 +- tools/save-editor/panels/Events.lua | 10 +- tools/save-editor/panels/Items.lua | 38 +- tools/save-editor/panels/MapBrowser.lua | 158 +- tools/save-editor/panels/MonEditor.lua | 57 +- 36 files changed, 3429 insertions(+), 1632 deletions(-) create mode 100644 src/core/gen2/FlagNames.lua create mode 100644 src/update/PatchNotes.lua create mode 100644 src/world/gen2/MapPreview.lua create mode 100644 tests/engine/launcher_mod_scope_dropdown.lua create mode 100644 tests/engine/launcher_patch_notes.lua create mode 100644 tests/save_editor_gen2_tests.lua create mode 100644 tools/save-editor/Gen.lua diff --git a/main.lua b/main.lua index 64a9489e..29f1e803 100644 --- a/main.lua +++ b/main.lua @@ -76,15 +76,6 @@ end local editorHost, editorVersion, editorWindow local closeEditor -- forward declaration: openEditor hands it to the editor --- tools/save-editor/ models the Gen 1 save and nothing else: a Gen 2 party row --- carries fields its MonOps and panels have no idea about (dvs, statExp, --- happiness, pokerus, caughtLevel), and SaveIO.save writes the WHOLE table --- back, so a Gold slot opened here comes out in a shape src/core/gen2/Save.lua --- then has to quarantine on the next boot. Refuse by name, the same way the --- .sav paths do (src/save_convert/SaveConvert.lua GEN2_SAV_UNSUPPORTED), so --- Red/Blue/Yellow slots are untouched. -local GEN2_NO_EDITOR = { gold = "Pokemon Gold" } - -- The editor's modules use flat names (require("Kit"), require("Party")), so -- their directories have to be on the require path. It must be -- love.filesystem's path, not package.path: in a packaged build these files @@ -137,11 +128,6 @@ local function openEditor(version, slotId) Importer.saveNotice = Importer.saveNotice or {} Importer.saveNotice[version] = { ok = false, text = text } end - local gen2Name = GEN2_NO_EDITOR[version] - if gen2Name then - refuse(gen2Name .. " uses a Gen 2 save; the save editor does not read one yet.") - return - end local SaveData = require("src.core.SaveData") local path = SaveData.slotDiskPath(version, slotId) if not path then @@ -320,14 +306,6 @@ function love.load(args) -- cache has to be mounted before the editor's Data:load. if editorMode then local version = os.getenv("POKEPORT_VERSION") or "red" - local gen2Name = GEN2_NO_EDITOR[version] - if gen2Name then - -- No launcher behind this run to carry a notice, so say it and stop - -- rather than open a Gen 2 slot on Gen 1 panels. - print(gen2Name .. " uses a Gen 2 save; the save editor does not read one yet.") - love.event.quit(1) - return - end require("src.core.GameVersion").set(version) require("src.import.CacheFs").mountVersion(version) addEditorRequirePath() diff --git a/scripts/pack_love.sh b/scripts/pack_love.sh index f22b9b74..beff791e 100755 --- a/scripts/pack_love.sh +++ b/scripts/pack_love.sh @@ -51,6 +51,12 @@ rm -f "$OUTPUT" tools/rom_manifest.json tools/rom_manifest_blue.json \ tools/rom_manifest_yellow.json tools/rom_manifest_gold.json \ -x '*.DS_Store' 'data/generated/*' 'assets/generated/*') +if [ -f "$ROOT/PATCH_NOTES.md" ]; then + (cd "$ROOT" && zip -q "$OUTPUT" PATCH_NOTES.md) +fi +if [ -f "$ROOT/mobile/ios/app-repo.json" ]; then + (cd "$ROOT" && zip -q "$OUTPUT" mobile/ios/app-repo.json) +fi if [ -n "$BUILD_INFO" ]; then [ -f "$BUILD_INFO" ] || fail "missing build-info: $BUILD_INFO" diff --git a/scripts/test.sh b/scripts/test.sh index 3b4913b2..cec1143c 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -135,6 +135,7 @@ if [ -f data/generated/maps.lua ]; then run_tier "T3 save editor: events + dex" "$LUA" tests/save_editor_task7_tests.lua run_tier "T3 save editor: map browser" "$LUA" tests/save_editor_task8_tests.lua run_tier "T3 save editor: mod awareness" "$LUA" tests/save_editor_mod_tests.lua + run_tier "T3 save editor: gold / gen2" "$LUA" tests/save_editor_gen2_tests.lua run_tier "T3 save editor: wheel scrolling" "$LUA" tests/save_editor_wheel_bug595_test.lua run_tier "T3 save editor: pad / NX input" "$LUA" tests/save_editor_pad_input_test.lua run_tier "T5 link (loopback lockstep)" "$LUA" tests/run_link_tests.lua diff --git a/src/battle/gen2/Mon.lua b/src/battle/gen2/Mon.lua index 89a37e0c..3caa03f5 100644 --- a/src/battle/gen2/Mon.lua +++ b/src/battle/gen2/Mon.lua @@ -98,10 +98,69 @@ function Mon.stats(baseStats, dvs, level, statExp) } end +-- The species string is the source of truth. `mon.name` is a copy of that +-- species' display name (GetPokemonName), kept so menus can print without a +-- Data lookup. It is NOT the nickname: an un-nicknamed mon has nickname nil +-- and prints this copy. Changing species without rewriting it leaves the +-- previous species' name on the party list and the SUMMARY's top line. +function Mon.syncIdentity(mon, data) + if type(mon) ~= "table" then return mon end + local def = data and data.pokemon and data.pokemon[mon.species] + if not def then return mon end + mon.name = def.name or mon.species + if def.types then mon.types = def.types end + if mon.dvs then + mon.gender = Mon.gender(def, mon.dvs, + { species = mon.species, level = mon.level }) + mon.shiny = Mon.isShiny(mon.dvs, + { species = mon.species, def = def, level = mon.level }) + if mon.species == Unown.SPECIES then + mon.unownLetter = Unown.letterFromDVs(mon.dvs) + else + mon.unownLetter = nil + end + end + return mon +end + +-- Every screen that prints a mon without a Data lookup should go through here: +-- nickname if the player set one, otherwise the species display copy `name`. +-- Skipping `name` and jumping to `species` is how a swapped mon can still +-- read as ABRA on one menu and RAYQUAZA on another. +function Mon.displayName(mon) + if type(mon) ~= "table" then return "?" end + return mon.nickname or mon.name or mon.species or "?" +end + +-- Party, boxes, both Day-Care sides, and a pending egg. Editor CONTINUE and +-- hydrate have to walk the same set: leaving dayCare.man.mon on the old +-- `name` is the ABRA bug in a second closet. +function Mon.eachSaveMon(save, fn) + if type(save) ~= "table" or type(fn) ~= "function" then return end + for _, mon in ipairs(save.party or {}) do fn(mon) end + for _, box in pairs(save.boxes or {}) do + if type(box) == "table" then + for _, mon in ipairs(box) do fn(mon) end + end + end + local dc = save.dayCare + if type(dc) == "table" then + if dc.man and dc.man.mon then fn(dc.man.mon) end + if dc.lady and dc.lady.mon then fn(dc.lady.mon) end + if dc.egg then fn(dc.egg) end + end + if save.daycare and save.daycare.mon then fn(save.daycare.mon) end +end + +function Mon.syncSaveIdentity(save, data) + Mon.eachSaveMon(save, function(mon) Mon.syncIdentity(mon, data) end) +end + function Mon.refreshStats(mon, data) if type(mon) ~= "table" then return mon end local def = data and data.pokemon and data.pokemon[mon.species] if not (def and def.baseStats) then return mon end + Mon.syncIdentity(mon, data) -- engine/pokemon/move_mon.asm:1402 local stats = Mon.stats(def.baseStats, mon.dvs, mon.level or 1, mon.statExp) mon.stats = stats diff --git a/src/core/Game2.lua b/src/core/Game2.lua index 7532437d..8533b903 100644 --- a/src/core/Game2.lua +++ b/src/core/Game2.lua @@ -283,6 +283,9 @@ function Game2:continueGame(save) local modsDiff = SaveData.modsDiff(save, activeMods) self.save = save self:adoptSave(save) + -- Editor species swaps used to leave mon.name on the previous species. + -- CONTINUE rewrites party, boxes, and Day-Care copies from the live record. + require("src.battle.gen2.Mon").syncSaveIdentity(save, self.data) -- options.lua wins over anything a save file carries: options are a display -- preference that survives New Game and is edited from the launcher, so a -- save written before they moved out must not drag old values back in. @@ -588,13 +591,13 @@ function Game2:useFieldItem(itemId) end if not allowed then self:say(("%s can't learn %s!"):format( - mon.nickname or mon.species or "?", moveName)) + require("src.battle.gen2.Mon").displayName(mon), moveName)) return end for _, move in ipairs(mon.moves or {}) do if move.id == moveId then self:say(("%s already knows %s!"):format( - mon.nickname or mon.species or "?", moveName)) + require("src.battle.gen2.Mon").displayName(mon), moveName)) return end end diff --git a/src/core/gen2/FlagNames.lua b/src/core/gen2/FlagNames.lua new file mode 100644 index 00000000..0d4e44b1 --- /dev/null +++ b/src/core/gen2/FlagNames.lua @@ -0,0 +1,1340 @@ +-- Generated by tools/goldwalk/gen_flags.lua from pret/pokegold. Do not edit. +-- +-- EVENT_* / ENGINE_* name -> the numeric id the cart's scripts carry, which is +-- what src/world/gen2/Events.lua and World:engineFlag key on. Verified against +-- the ROM by tests/gold_flag_names_test.lua. +return { + source = "pret/pokegold constants/{event,engine}_flags.asm", + events = { + EVENT_ALAN_ASKED_FOR_PHONE_NUMBER = 645, + EVENT_ALAN_READY_FOR_REMATCH = 646, + EVENT_ANTHONY_ASKED_FOR_PHONE_NUMBER = 635, + EVENT_ANTHONY_READY_FOR_REMATCH = 636, + EVENT_ARNIE_ASKED_FOR_PHONE_NUMBER = 643, + EVENT_ARNIE_READY_FOR_REMATCH = 644, + EVENT_AZALEA_TOWN_HIDDEN_FULL_HEAL = 177, + EVENT_AZALEA_TOWN_SLOWPOKES = 1785, + EVENT_AZALEA_TOWN_SLOWPOKETAIL_ROCKET = 1786, + EVENT_BEAT_BEAUTY_ANGELICA = 1207, + EVENT_BEAT_BEAUTY_BRENDA = 1201, + EVENT_BEAT_BEAUTY_CARLENE = 1204, + EVENT_BEAT_BEAUTY_CAROLINE = 1203, + EVENT_BEAT_BEAUTY_CASSIE = 1202, + EVENT_BEAT_BEAUTY_JACLYN = 1200, + EVENT_BEAT_BEAUTY_JESSICA = 1205, + EVENT_BEAT_BEAUTY_JULIA = 1210, + EVENT_BEAT_BEAUTY_JULIE = 1199, + EVENT_BEAT_BEAUTY_KENDRA = 1208, + EVENT_BEAT_BEAUTY_RACHAEL = 1206, + EVENT_BEAT_BEAUTY_SAMANTHA = 1198, + EVENT_BEAT_BEAUTY_THERESA = 1211, + EVENT_BEAT_BEAUTY_VALERIE = 1212, + EVENT_BEAT_BEAUTY_VERONICA = 1209, + EVENT_BEAT_BEAUTY_VICTORIA = 1197, + EVENT_BEAT_BIKER_BENNY = 1073, + EVENT_BEAT_BIKER_CHARLES = 1078, + EVENT_BEAT_BIKER_DWAYNE = 1075, + EVENT_BEAT_BIKER_GLENN = 1081, + EVENT_BEAT_BIKER_HARRIS = 1076, + EVENT_BEAT_BIKER_JOEL = 1080, + EVENT_BEAT_BIKER_KAZU = 1074, + EVENT_BEAT_BIKER_RILEY = 1079, + EVENT_BEAT_BIKER_ZEKE = 1077, + EVENT_BEAT_BIRD_KEEPER_ABE = 1020, + EVENT_BEAT_BIRD_KEEPER_BOB = 1029, + EVENT_BEAT_BIRD_KEEPER_BORIS = 1028, + EVENT_BEAT_BIRD_KEEPER_BRET = 1034, + EVENT_BEAT_BIRD_KEEPER_BRYAN = 1021, + EVENT_BEAT_BIRD_KEEPER_DENIS = 1024, + EVENT_BEAT_BIRD_KEEPER_HANK = 1026, + EVENT_BEAT_BIRD_KEEPER_JOSE = 1030, + EVENT_BEAT_BIRD_KEEPER_JOSE2 = 1032, + EVENT_BEAT_BIRD_KEEPER_JOSE3 = 1035, + EVENT_BEAT_BIRD_KEEPER_PERRY = 1033, + EVENT_BEAT_BIRD_KEEPER_PETER = 1031, + EVENT_BEAT_BIRD_KEEPER_ROD = 1019, + EVENT_BEAT_BIRD_KEEPER_ROY = 1027, + EVENT_BEAT_BIRD_KEEPER_THEO = 1022, + EVENT_BEAT_BIRD_KEEPER_TOBY = 1023, + EVENT_BEAT_BIRD_KEEPER_VANCE = 1025, + EVENT_BEAT_BIRD_KEEPER_VANCE2 = 1036, + EVENT_BEAT_BIRD_KEEPER_VANCE3 = 1037, + EVENT_BEAT_BLACKBELT_KENJI = 1195, + EVENT_BEAT_BLACKBELT_KENJI2 = 1188, + EVENT_BEAT_BLACKBELT_KENJI3 = 1190, + EVENT_BEAT_BLACKBELT_KIYO = 1193, + EVENT_BEAT_BLACKBELT_LAO = 1191, + EVENT_BEAT_BLACKBELT_LUNG = 1194, + EVENT_BEAT_BLACKBELT_NOB = 1192, + EVENT_BEAT_BLACKBELT_WAI = 1196, + EVENT_BEAT_BLACKBELT_YOSHI = 1189, + EVENT_BEAT_BLAINE = 1227, + EVENT_BEAT_BLUE = 1228, + EVENT_BEAT_BOARDER_BRAD = 1039, + EVENT_BEAT_BOARDER_DOUGLAS = 1040, + EVENT_BEAT_BOARDER_RONALD = 1038, + EVENT_BEAT_BROCK = 1221, + EVENT_BEAT_BUGSY = 1214, + EVENT_BEAT_BUG_CATCHER_AL = 1341, + EVENT_BEAT_BUG_CATCHER_ARNIE = 1343, + EVENT_BEAT_BUG_CATCHER_ARNIE2 = 1348, + EVENT_BEAT_BUG_CATCHER_ARNIE3 = 1349, + EVENT_BEAT_BUG_CATCHER_BENNY = 1340, + EVENT_BEAT_BUG_CATCHER_DON = 1336, + EVENT_BEAT_BUG_CATCHER_DOUG = 1347, + EVENT_BEAT_BUG_CATCHER_ED = 1338, + EVENT_BEAT_BUG_CATCHER_JOSH = 1342, + EVENT_BEAT_BUG_CATCHER_KEN = 1344, + EVENT_BEAT_BUG_CATCHER_ROB = 1337, + EVENT_BEAT_BUG_CATCHER_WADE = 1339, + EVENT_BEAT_BUG_CATCHER_WADE2 = 1345, + EVENT_BEAT_BUG_CATCHER_WADE3 = 1346, + EVENT_BEAT_BURGLAR_COREY = 1071, + EVENT_BEAT_BURGLAR_DUNCAN = 1069, + EVENT_BEAT_BURGLAR_EDDIE = 1070, + EVENT_BEAT_CAMPER_BARRY = 1054, + EVENT_BEAT_CAMPER_DALE = 1059, + EVENT_BEAT_CAMPER_DAVID = 1065, + EVENT_BEAT_CAMPER_DEAN = 1056, + EVENT_BEAT_CAMPER_ELLIOT = 1053, + EVENT_BEAT_CAMPER_HERVEY = 1058, + EVENT_BEAT_CAMPER_IVAN = 1052, + EVENT_BEAT_CAMPER_JERRY = 1067, + EVENT_BEAT_CAMPER_JOHN = 1066, + EVENT_BEAT_CAMPER_LEROY = 1064, + EVENT_BEAT_CAMPER_LLOYD = 1055, + EVENT_BEAT_CAMPER_ROLAND = 1050, + EVENT_BEAT_CAMPER_SID = 1057, + EVENT_BEAT_CAMPER_SPENCER = 1068, + EVENT_BEAT_CAMPER_TED = 1060, + EVENT_BEAT_CAMPER_THOMAS = 1063, + EVENT_BEAT_CAMPER_TODD = 1051, + EVENT_BEAT_CAMPER_TODD2 = 1061, + EVENT_BEAT_CAMPER_TODD3 = 1062, + EVENT_BEAT_CHAMPION_LANCE = 1468, + EVENT_BEAT_CHUCK = 1218, + EVENT_BEAT_CLAIR = 1220, + EVENT_BEAT_COOLTRAINERF_BETH = 1379, + EVENT_BEAT_COOLTRAINERF_BETH2 = 1382, + EVENT_BEAT_COOLTRAINERF_BETH3 = 1388, + EVENT_BEAT_COOLTRAINERF_CAROL = 1383, + EVENT_BEAT_COOLTRAINERF_CYBIL = 1386, + EVENT_BEAT_COOLTRAINERF_EMMA = 1385, + EVENT_BEAT_COOLTRAINERF_FRAN = 1373, + EVENT_BEAT_COOLTRAINERF_GWEN = 1371, + EVENT_BEAT_COOLTRAINERF_IRENE = 1376, + EVENT_BEAT_COOLTRAINERF_JENN = 1387, + EVENT_BEAT_COOLTRAINERF_JOYCE = 1378, + EVENT_BEAT_COOLTRAINERF_KATE = 1375, + EVENT_BEAT_COOLTRAINERF_KELLY = 1377, + EVENT_BEAT_COOLTRAINERF_LOIS = 1372, + EVENT_BEAT_COOLTRAINERF_LOLA = 1374, + EVENT_BEAT_COOLTRAINERF_MEGAN = 1381, + EVENT_BEAT_COOLTRAINERF_QUINN = 1384, + EVENT_BEAT_COOLTRAINERF_REENA = 1380, + EVENT_BEAT_COOLTRAINERF_REENA2 = 1389, + EVENT_BEAT_COOLTRAINERF_REENA3 = 1390, + EVENT_BEAT_COOLTRAINERM_AARON = 1353, + EVENT_BEAT_COOLTRAINERM_ALLEN = 1370, + EVENT_BEAT_COOLTRAINERM_ANDY = 1365, + EVENT_BEAT_COOLTRAINERM_BLAKE = 1362, + EVENT_BEAT_COOLTRAINERM_BRIAN = 1363, + EVENT_BEAT_COOLTRAINERM_CODY = 1355, + EVENT_BEAT_COOLTRAINERM_ERICK = 1364, + EVENT_BEAT_COOLTRAINERM_GAVEN = 1361, + EVENT_BEAT_COOLTRAINERM_GAVEN2 = 1357, + EVENT_BEAT_COOLTRAINERM_GAVEN3 = 1358, + EVENT_BEAT_COOLTRAINERM_JAKE = 1360, + EVENT_BEAT_COOLTRAINERM_KEVIN = 1368, + EVENT_BEAT_COOLTRAINERM_MIKE = 1356, + EVENT_BEAT_COOLTRAINERM_NICK = 1352, + EVENT_BEAT_COOLTRAINERM_PAUL = 1354, + EVENT_BEAT_COOLTRAINERM_RYAN = 1359, + EVENT_BEAT_COOLTRAINERM_SEAN = 1367, + EVENT_BEAT_COOLTRAINERM_STEVE = 1369, + EVENT_BEAT_COOLTRAINERM_TYLER = 1366, + EVENT_BEAT_ELITE_4_BRUNO = 1466, + EVENT_BEAT_ELITE_4_KAREN = 1467, + EVENT_BEAT_ELITE_4_KOGA = 1465, + EVENT_BEAT_ELITE_4_WILL = 1464, + EVENT_BEAT_ELITE_FOUR = 68, + EVENT_BEAT_ERIKA = 1224, + EVENT_BEAT_FALKNER = 1213, + EVENT_BEAT_FIREBREATHER_BILL = 1098, + EVENT_BEAT_FIREBREATHER_BURT = 1097, + EVENT_BEAT_FIREBREATHER_DICK = 1095, + EVENT_BEAT_FIREBREATHER_LYLE = 1101, + EVENT_BEAT_FIREBREATHER_NED = 1096, + EVENT_BEAT_FIREBREATHER_OTIS = 1094, + EVENT_BEAT_FIREBREATHER_RAY = 1100, + EVENT_BEAT_FIREBREATHER_WALT = 1099, + EVENT_BEAT_FISHER_ANDRE = 1109, + EVENT_BEAT_FISHER_ARNOLD = 1104, + EVENT_BEAT_FISHER_BARNEY = 1116, + EVENT_BEAT_FISHER_CHRIS = 1108, + EVENT_BEAT_FISHER_CHRIS2 = 1119, + EVENT_BEAT_FISHER_CHRIS3 = 1120, + EVENT_BEAT_FISHER_EDGAR = 1112, + EVENT_BEAT_FISHER_HENRY = 1106, + EVENT_BEAT_FISHER_JONAH = 1113, + EVENT_BEAT_FISHER_JUSTIN = 1102, + EVENT_BEAT_FISHER_KYLE = 1105, + EVENT_BEAT_FISHER_MARTIN = 1114, + EVENT_BEAT_FISHER_MARVIN = 1107, + EVENT_BEAT_FISHER_RALPH = 1103, + EVENT_BEAT_FISHER_RALPH2 = 1117, + EVENT_BEAT_FISHER_RALPH3 = 1118, + EVENT_BEAT_FISHER_RAYMOND = 1110, + EVENT_BEAT_FISHER_SCOTT = 1122, + EVENT_BEAT_FISHER_STEPHEN = 1115, + EVENT_BEAT_FISHER_WILTON = 1111, + EVENT_BEAT_FISHER_WILTON2 = 1121, + EVENT_BEAT_FISHER_WILTON3 = 1123, + EVENT_BEAT_GENTLEMAN_ALFRED = 1182, + EVENT_BEAT_GENTLEMAN_EDWARD = 1179, + EVENT_BEAT_GENTLEMAN_GREGORY = 1180, + EVENT_BEAT_GENTLEMAN_PRESTON = 1178, + EVENT_BEAT_GENTLEMAN_VIRGIL = 1181, + EVENT_BEAT_GUITARIST_CLYDE = 1171, + EVENT_BEAT_GUITARIST_VINCENT = 1172, + EVENT_BEAT_HIKER_ANTHONY = 1320, + EVENT_BEAT_HIKER_ANTHONY2 = 1316, + EVENT_BEAT_HIKER_ANTHONY3 = 1327, + EVENT_BEAT_HIKER_BAILEY = 1326, + EVENT_BEAT_HIKER_BENJAMIN = 1321, + EVENT_BEAT_HIKER_DANIEL = 1333, + EVENT_BEAT_HIKER_ERIK = 1322, + EVENT_BEAT_HIKER_JIM = 1332, + EVENT_BEAT_HIKER_KENNY = 1331, + EVENT_BEAT_HIKER_LEONARD = 1319, + EVENT_BEAT_HIKER_MICHAEL = 1323, + EVENT_BEAT_HIKER_NOLAND = 1329, + EVENT_BEAT_HIKER_PARRY = 1324, + EVENT_BEAT_HIKER_PARRY2 = 1334, + EVENT_BEAT_HIKER_PARRY3 = 1335, + EVENT_BEAT_HIKER_PHILLIP = 1318, + EVENT_BEAT_HIKER_RUSSELL = 1317, + EVENT_BEAT_HIKER_SIDNEY = 1330, + EVENT_BEAT_HIKER_TIM = 1328, + EVENT_BEAT_HIKER_TIMOTHY = 1325, + EVENT_BEAT_JANINE = 1225, + EVENT_BEAT_JASMINE = 1217, + EVENT_BEAT_JUGGLER_FRITZ = 1174, + EVENT_BEAT_JUGGLER_HORTON = 1175, + EVENT_BEAT_JUGGLER_IRWIN = 1173, + EVENT_BEAT_JUGGLER_IRWIN2 = 1176, + EVENT_BEAT_JUGGLER_IRWIN3 = 1177, + EVENT_BEAT_KIMONO_GIRL_KUNI = 1250, + EVENT_BEAT_KIMONO_GIRL_MIKI = 1251, + EVENT_BEAT_KIMONO_GIRL_NAOKO = 1247, + EVENT_BEAT_KIMONO_GIRL_SAYO = 1248, + EVENT_BEAT_KIMONO_GIRL_ZUKI = 1249, + EVENT_BEAT_LASS_ALICE = 1303, + EVENT_BEAT_LASS_BRIDGET = 1302, + EVENT_BEAT_LASS_CARRIE = 1301, + EVENT_BEAT_LASS_CONNIE = 1305, + EVENT_BEAT_LASS_CONNIE2 = 1312, + EVENT_BEAT_LASS_CONNIE3 = 1313, + EVENT_BEAT_LASS_DANA = 1310, + EVENT_BEAT_LASS_DANA2 = 1314, + EVENT_BEAT_LASS_DANA3 = 1315, + EVENT_BEAT_LASS_ELLEN = 1311, + EVENT_BEAT_LASS_KRISE = 1304, + EVENT_BEAT_LASS_LAURA = 1307, + EVENT_BEAT_LASS_LINDA = 1306, + EVENT_BEAT_LASS_MICHELLE = 1309, + EVENT_BEAT_LASS_SHANNON = 1308, + EVENT_BEAT_LTSURGE = 1223, + EVENT_BEAT_MEDIUM_BETHANY = 1421, + EVENT_BEAT_MEDIUM_DORIS = 1425, + EVENT_BEAT_MEDIUM_ETHEL = 1423, + EVENT_BEAT_MEDIUM_GRACE = 1420, + EVENT_BEAT_MEDIUM_MARGRET = 1422, + EVENT_BEAT_MEDIUM_MARTHA = 1419, + EVENT_BEAT_MEDIUM_REBECCA = 1424, + EVENT_BEAT_MISTY = 1222, + EVENT_BEAT_MORTY = 1216, + EVENT_BEAT_OFFICER_DIRK = 1351, + EVENT_BEAT_OFFICER_KEITH = 1350, + EVENT_BEAT_PICNICKER_BROOKE = 1152, + EVENT_BEAT_PICNICKER_CINDY = 1154, + EVENT_BEAT_PICNICKER_DEBRA = 1157, + EVENT_BEAT_PICNICKER_EDNA = 1163, + EVENT_BEAT_PICNICKER_ERIN = 1159, + EVENT_BEAT_PICNICKER_ERIN2 = 1167, + EVENT_BEAT_PICNICKER_ERIN3 = 1170, + EVENT_BEAT_PICNICKER_GINA = 1151, + EVENT_BEAT_PICNICKER_GINA2 = 1158, + EVENT_BEAT_PICNICKER_GINA3 = 1164, + EVENT_BEAT_PICNICKER_HEIDI = 1162, + EVENT_BEAT_PICNICKER_HOPE = 1155, + EVENT_BEAT_PICNICKER_KIM = 1153, + EVENT_BEAT_PICNICKER_LIZ = 1150, + EVENT_BEAT_PICNICKER_LIZ2 = 1160, + EVENT_BEAT_PICNICKER_LIZ3 = 1161, + EVENT_BEAT_PICNICKER_SHARON = 1156, + EVENT_BEAT_PICNICKER_TANYA = 1168, + EVENT_BEAT_PICNICKER_TIFFANY = 1169, + EVENT_BEAT_PICNICKER_TIFFANY2 = 1165, + EVENT_BEAT_PICNICKER_TIFFANY3 = 1166, + EVENT_BEAT_POKEFANF_BEVERLY = 1241, + EVENT_BEAT_POKEFANF_BEVERLY2 = 1243, + EVENT_BEAT_POKEFANF_BEVERLY3 = 1244, + EVENT_BEAT_POKEFANF_GEORGIA = 1245, + EVENT_BEAT_POKEFANF_RUTH = 1242, + EVENT_BEAT_POKEFANM_ALEX = 1240, + EVENT_BEAT_POKEFANM_BRANDON = 1235, + EVENT_BEAT_POKEFANM_CARTER = 1233, + EVENT_BEAT_POKEFANM_COLIN = 1237, + EVENT_BEAT_POKEFANM_DEREK = 1230, + EVENT_BEAT_POKEFANM_DEREK2 = 1238, + EVENT_BEAT_POKEFANM_DEREK3 = 1239, + EVENT_BEAT_POKEFANM_JEREMY = 1236, + EVENT_BEAT_POKEFANM_JOSHUA = 1232, + EVENT_BEAT_POKEFANM_ROBERT = 1231, + EVENT_BEAT_POKEFANM_TREVOR = 1234, + EVENT_BEAT_POKEFANM_WILLIAM = 1229, + EVENT_BEAT_POKEMANIAC_ANDREW = 1253, + EVENT_BEAT_POKEMANIAC_BEN = 1256, + EVENT_BEAT_POKEMANIAC_BRENT = 1257, + EVENT_BEAT_POKEMANIAC_BRENT2 = 1260, + EVENT_BEAT_POKEMANIAC_BRENT3 = 1261, + EVENT_BEAT_POKEMANIAC_CALVIN = 1254, + EVENT_BEAT_POKEMANIAC_DONALD = 1263, + EVENT_BEAT_POKEMANIAC_ETHAN = 1259, + EVENT_BEAT_POKEMANIAC_ISSAC = 1262, + EVENT_BEAT_POKEMANIAC_LARRY = 1252, + EVENT_BEAT_POKEMANIAC_RON = 1258, + EVENT_BEAT_POKEMANIAC_SHANE = 1255, + EVENT_BEAT_POKEMANIAC_ZACH = 1264, + EVENT_BEAT_PRYCE = 1219, + EVENT_BEAT_PSYCHIC_FIDEL = 1085, + EVENT_BEAT_PSYCHIC_FRANKLIN = 1083, + EVENT_BEAT_PSYCHIC_GILBERT = 1091, + EVENT_BEAT_PSYCHIC_GREG = 1086, + EVENT_BEAT_PSYCHIC_HERMAN = 1084, + EVENT_BEAT_PSYCHIC_JARED = 1092, + EVENT_BEAT_PSYCHIC_MARK = 1088, + EVENT_BEAT_PSYCHIC_NATHAN = 1082, + EVENT_BEAT_PSYCHIC_NORMAN = 1087, + EVENT_BEAT_PSYCHIC_PHIL = 1089, + EVENT_BEAT_PSYCHIC_RICHARD = 1090, + EVENT_BEAT_PSYCHIC_RODNEY = 1093, + EVENT_BEAT_RIVAL_IN_MT_MOON = 793, + EVENT_BEAT_ROCKET_EXECUTIVEF_1 = 1391, + EVENT_BEAT_ROCKET_EXECUTIVEF_2 = 1392, + EVENT_BEAT_ROCKET_EXECUTIVEM_1 = 1393, + EVENT_BEAT_ROCKET_EXECUTIVEM_2 = 1394, + EVENT_BEAT_ROCKET_EXECUTIVEM_3 = 1395, + EVENT_BEAT_ROCKET_EXECUTIVEM_4 = 1396, + EVENT_BEAT_ROCKET_GRUNTF_1 = 1296, + EVENT_BEAT_ROCKET_GRUNTF_2 = 1297, + EVENT_BEAT_ROCKET_GRUNTF_3 = 1298, + EVENT_BEAT_ROCKET_GRUNTF_4 = 1299, + EVENT_BEAT_ROCKET_GRUNTF_5 = 1300, + EVENT_BEAT_ROCKET_GRUNTM_1 = 1265, + EVENT_BEAT_ROCKET_GRUNTM_10 = 1274, + EVENT_BEAT_ROCKET_GRUNTM_11 = 1275, + EVENT_BEAT_ROCKET_GRUNTM_12 = 1276, + EVENT_BEAT_ROCKET_GRUNTM_13 = 1277, + EVENT_BEAT_ROCKET_GRUNTM_14 = 1278, + EVENT_BEAT_ROCKET_GRUNTM_15 = 1279, + EVENT_BEAT_ROCKET_GRUNTM_16 = 1280, + EVENT_BEAT_ROCKET_GRUNTM_17 = 1281, + EVENT_BEAT_ROCKET_GRUNTM_18 = 1282, + EVENT_BEAT_ROCKET_GRUNTM_19 = 1283, + EVENT_BEAT_ROCKET_GRUNTM_2 = 1266, + EVENT_BEAT_ROCKET_GRUNTM_20 = 1284, + EVENT_BEAT_ROCKET_GRUNTM_21 = 1285, + EVENT_BEAT_ROCKET_GRUNTM_22 = 1286, + EVENT_BEAT_ROCKET_GRUNTM_23 = 1287, + EVENT_BEAT_ROCKET_GRUNTM_24 = 1288, + EVENT_BEAT_ROCKET_GRUNTM_25 = 1289, + EVENT_BEAT_ROCKET_GRUNTM_26 = 1290, + EVENT_BEAT_ROCKET_GRUNTM_27 = 1291, + EVENT_BEAT_ROCKET_GRUNTM_28 = 1292, + EVENT_BEAT_ROCKET_GRUNTM_29 = 1293, + EVENT_BEAT_ROCKET_GRUNTM_3 = 1267, + EVENT_BEAT_ROCKET_GRUNTM_30 = 1294, + EVENT_BEAT_ROCKET_GRUNTM_31 = 1295, + EVENT_BEAT_ROCKET_GRUNTM_4 = 1268, + EVENT_BEAT_ROCKET_GRUNTM_5 = 1269, + EVENT_BEAT_ROCKET_GRUNTM_6 = 1270, + EVENT_BEAT_ROCKET_GRUNTM_7 = 1271, + EVENT_BEAT_ROCKET_GRUNTM_8 = 1272, + EVENT_BEAT_ROCKET_GRUNTM_9 = 1273, + EVENT_BEAT_SABRINA = 1226, + EVENT_BEAT_SAGE_CHOW = 1041, + EVENT_BEAT_SAGE_EDMOND = 1047, + EVENT_BEAT_SAGE_JEFFREY = 1045, + EVENT_BEAT_SAGE_JIN = 1043, + EVENT_BEAT_SAGE_LI = 1049, + EVENT_BEAT_SAGE_NEAL = 1048, + EVENT_BEAT_SAGE_NICO = 1042, + EVENT_BEAT_SAGE_PING = 1046, + EVENT_BEAT_SAGE_TROY = 1044, + EVENT_BEAT_SAILOR_ERNEST = 1401, + EVENT_BEAT_SAILOR_EUGENE = 1397, + EVENT_BEAT_SAILOR_GARRETT = 1403, + EVENT_BEAT_SAILOR_HARRY = 1406, + EVENT_BEAT_SAILOR_HUEY = 1398, + EVENT_BEAT_SAILOR_HUEY2 = 1407, + EVENT_BEAT_SAILOR_HUEY3 = 1408, + EVENT_BEAT_SAILOR_JEFF = 1402, + EVENT_BEAT_SAILOR_KENNETH = 1404, + EVENT_BEAT_SAILOR_KENT = 1400, + EVENT_BEAT_SAILOR_STANLY = 1405, + EVENT_BEAT_SAILOR_TERRELL = 1399, + EVENT_BEAT_SCHOOLBOY_ALAN = 1134, + EVENT_BEAT_SCHOOLBOY_ALAN2 = 1146, + EVENT_BEAT_SCHOOLBOY_ALAN3 = 1147, + EVENT_BEAT_SCHOOLBOY_BILLY = 1140, + EVENT_BEAT_SCHOOLBOY_CHAD = 1141, + EVENT_BEAT_SCHOOLBOY_CHAD2 = 1148, + EVENT_BEAT_SCHOOLBOY_CHAD3 = 1149, + EVENT_BEAT_SCHOOLBOY_DANNY = 1136, + EVENT_BEAT_SCHOOLBOY_DUDLEY = 1138, + EVENT_BEAT_SCHOOLBOY_JACK = 1132, + EVENT_BEAT_SCHOOLBOY_JACK2 = 1144, + EVENT_BEAT_SCHOOLBOY_JACK3 = 1145, + EVENT_BEAT_SCHOOLBOY_JOE = 1139, + EVENT_BEAT_SCHOOLBOY_JOHNNY = 1135, + EVENT_BEAT_SCHOOLBOY_KIP = 1133, + EVENT_BEAT_SCHOOLBOY_NATE = 1142, + EVENT_BEAT_SCHOOLBOY_RICKY = 1143, + EVENT_BEAT_SCHOOLBOY_TOMMY = 1137, + EVENT_BEAT_SCIENTIST_JED = 1185, + EVENT_BEAT_SCIENTIST_MARC = 1186, + EVENT_BEAT_SCIENTIST_MITCH = 1184, + EVENT_BEAT_SCIENTIST_RICH = 1187, + EVENT_BEAT_SCIENTIST_ROSS = 1183, + EVENT_BEAT_SKIER_CLARISSA = 1427, + EVENT_BEAT_SKIER_ROXANNE = 1426, + EVENT_BEAT_SUPER_NERD_ERIC = 1410, + EVENT_BEAT_SUPER_NERD_ERIC_UNUSED = 1409, + EVENT_BEAT_SUPER_NERD_GREGG = 1411, + EVENT_BEAT_SUPER_NERD_JAY = 1412, + EVENT_BEAT_SUPER_NERD_PAT = 1416, + EVENT_BEAT_SUPER_NERD_SAM = 1414, + EVENT_BEAT_SUPER_NERD_SHAWN = 1417, + EVENT_BEAT_SUPER_NERD_TERU = 1418, + EVENT_BEAT_SUPER_NERD_TOM = 1415, + EVENT_BEAT_SWIMMERF_BRIANA = 1018, + EVENT_BEAT_SWIMMERF_DAWN = 1011, + EVENT_BEAT_SWIMMERF_DENISE = 1004, + EVENT_BEAT_SWIMMERF_DIANA = 1017, + EVENT_BEAT_SWIMMERF_ELAINE = 1000, + EVENT_BEAT_SWIMMERF_JILL = 1008, + EVENT_BEAT_SWIMMERF_JODY = 1015, + EVENT_BEAT_SWIMMERF_KARA = 1005, + EVENT_BEAT_SWIMMERF_KATIE = 1010, + EVENT_BEAT_SWIMMERF_KAYLEE = 1002, + EVENT_BEAT_SWIMMERF_LISA = 1007, + EVENT_BEAT_SWIMMERF_LORI = 1014, + EVENT_BEAT_SWIMMERF_MARY = 1009, + EVENT_BEAT_SWIMMERF_NICOLE = 1013, + EVENT_BEAT_SWIMMERF_NIKKI = 1016, + EVENT_BEAT_SWIMMERF_PAULA = 1001, + EVENT_BEAT_SWIMMERF_SUSIE = 1003, + EVENT_BEAT_SWIMMERF_TARA = 1012, + EVENT_BEAT_SWIMMERF_WENDY = 1006, + EVENT_BEAT_SWIMMERM_BERKE = 1433, + EVENT_BEAT_SWIMMERM_CAMERON = 1444, + EVENT_BEAT_SWIMMERM_CHARLIE = 1431, + EVENT_BEAT_SWIMMERM_DARYL = 1438, + EVENT_BEAT_SWIMMERM_GEORGE = 1432, + EVENT_BEAT_SWIMMERM_HAL = 1436, + EVENT_BEAT_SWIMMERM_HAROLD = 1428, + EVENT_BEAT_SWIMMERM_JAMES = 1446, + EVENT_BEAT_SWIMMERM_JEROME = 1441, + EVENT_BEAT_SWIMMERM_KIRK = 1434, + EVENT_BEAT_SWIMMERM_LEWIS = 1447, + EVENT_BEAT_SWIMMERM_MATHEW = 1435, + EVENT_BEAT_SWIMMERM_PARKER = 1448, + EVENT_BEAT_SWIMMERM_PATON = 1437, + EVENT_BEAT_SWIMMERM_RANDALL = 1430, + EVENT_BEAT_SWIMMERM_RICK = 1443, + EVENT_BEAT_SWIMMERM_SETH = 1445, + EVENT_BEAT_SWIMMERM_SIMON = 1429, + EVENT_BEAT_SWIMMERM_TONY = 1440, + EVENT_BEAT_SWIMMERM_TUCKER = 1442, + EVENT_BEAT_SWIMMERM_WALTER = 1439, + EVENT_BEAT_TEACHER_COLETTE = 1461, + EVENT_BEAT_TEACHER_HILLARY = 1462, + EVENT_BEAT_TEACHER_SHIRLEY = 1463, + EVENT_BEAT_TWINS_AMY_AND_MAY = 1124, + EVENT_BEAT_TWINS_AMY_AND_MAY2 = 1127, + EVENT_BEAT_TWINS_ANN_AND_ANNE = 1125, + EVENT_BEAT_TWINS_ANN_AND_ANNE2 = 1126, + EVENT_BEAT_TWINS_JO_AND_ZOE = 1128, + EVENT_BEAT_TWINS_JO_AND_ZOE2 = 1129, + EVENT_BEAT_TWINS_MEG_AND_PEG = 1130, + EVENT_BEAT_TWINS_MEG_AND_PEG2 = 1131, + EVENT_BEAT_WHITNEY = 1215, + EVENT_BEAT_YOUNGSTER_ALBERT = 1451, + EVENT_BEAT_YOUNGSTER_GORDON = 1452, + EVENT_BEAT_YOUNGSTER_IAN = 1454, + EVENT_BEAT_YOUNGSTER_JASON = 1460, + EVENT_BEAT_YOUNGSTER_JIMMY = 1458, + EVENT_BEAT_YOUNGSTER_JOEY = 1449, + EVENT_BEAT_YOUNGSTER_JOEY2 = 1455, + EVENT_BEAT_YOUNGSTER_JOEY3 = 1456, + EVENT_BEAT_YOUNGSTER_MIKEY = 1450, + EVENT_BEAT_YOUNGSTER_OWEN = 1459, + EVENT_BEAT_YOUNGSTER_SAMUEL = 1453, + EVENT_BEAT_YOUNGSTER_WARREN = 1457, + EVENT_BEAY_SUPER_NERD_DAVE = 1413, + EVENT_BETH_ASKED_FOR_PHONE_NUMBER = 621, + EVENT_BETH_READY_FOR_REMATCH = 622, + EVENT_BEVERLY_ASKED_FOR_PHONE_NUMBER = 609, + EVENT_BEVERLY_READY_FOR_REMATCH = 610, + EVENT_BLACKTHORN_CITY_GRAMPS_BLOCKS_DRAGONS_DEN = 1868, + EVENT_BLACKTHORN_CITY_GRAMPS_NOT_BLOCKING_DRAGONS_DEN = 1869, + EVENT_BLACKTHORN_CITY_SANTOS_OF_SATURDAY = 1885, + EVENT_BLACKTHORN_CITY_SUPER_NERD_BLOCKS_GYM = 1763, + EVENT_BLACKTHORN_CITY_SUPER_NERD_DOES_NOT_BLOCK_GYM = 1764, + EVENT_BLUE_IN_CINNABAR = 1909, + EVENT_BOULDER_IN_BLACKTHORN_GYM_1 = 1798, + EVENT_BOULDER_IN_BLACKTHORN_GYM_2 = 1799, + EVENT_BOULDER_IN_BLACKTHORN_GYM_3 = 1800, + EVENT_BOULDER_IN_ICE_PATH_1 = 1801, + EVENT_BOULDER_IN_ICE_PATH_1A = 1805, + EVENT_BOULDER_IN_ICE_PATH_2 = 1802, + EVENT_BOULDER_IN_ICE_PATH_2A = 1806, + EVENT_BOULDER_IN_ICE_PATH_3 = 1803, + EVENT_BOULDER_IN_ICE_PATH_3A = 1807, + EVENT_BOULDER_IN_ICE_PATH_4 = 1804, + EVENT_BOULDER_IN_ICE_PATH_4A = 1808, + EVENT_BRENT_ASKED_FOR_PHONE_NUMBER = 657, + EVENT_BRENT_READY_FOR_REMATCH = 658, + EVENT_BRUNOS_ROOM_ENTRANCE_CLOSED = 781, + EVENT_BRUNOS_ROOM_EXIT_OPEN = 782, + EVENT_BUG_CATCHING_CONTESTANT_10A = 1823, + EVENT_BUG_CATCHING_CONTESTANT_10B = 1833, + EVENT_BUG_CATCHING_CONTESTANT_1A = 1814, + EVENT_BUG_CATCHING_CONTESTANT_1B = 1824, + EVENT_BUG_CATCHING_CONTESTANT_2A = 1815, + EVENT_BUG_CATCHING_CONTESTANT_2B = 1825, + EVENT_BUG_CATCHING_CONTESTANT_3A = 1816, + EVENT_BUG_CATCHING_CONTESTANT_3B = 1826, + EVENT_BUG_CATCHING_CONTESTANT_4A = 1817, + EVENT_BUG_CATCHING_CONTESTANT_4B = 1827, + EVENT_BUG_CATCHING_CONTESTANT_5A = 1818, + EVENT_BUG_CATCHING_CONTESTANT_5B = 1828, + EVENT_BUG_CATCHING_CONTESTANT_6A = 1819, + EVENT_BUG_CATCHING_CONTESTANT_6B = 1829, + EVENT_BUG_CATCHING_CONTESTANT_7A = 1820, + EVENT_BUG_CATCHING_CONTESTANT_7B = 1830, + EVENT_BUG_CATCHING_CONTESTANT_8A = 1821, + EVENT_BUG_CATCHING_CONTESTANT_8B = 1831, + EVENT_BUG_CATCHING_CONTESTANT_9A = 1822, + EVENT_BUG_CATCHING_CONTESTANT_9B = 1832, + EVENT_BURNED_TOWER_1F_BURN_HEAL = 1621, + EVENT_BURNED_TOWER_1F_HIDDEN_ETHER = 128, + EVENT_BURNED_TOWER_1F_X_SPEED = 1620, + EVENT_BURNED_TOWER_B1F_BEASTS_1 = 1866, + EVENT_BURNED_TOWER_B1F_BEASTS_2 = 1867, + EVENT_BURNED_TOWER_B1F_HIDDEN_BURN_HEAL = 130, + EVENT_BURNED_TOWER_B1F_HIDDEN_NUGGET = 131, + EVENT_BURNED_TOWER_B1F_HIDDEN_ULTRA_BALL = 129, + EVENT_BURNED_TOWER_B1F_HP_UP = 1622, + EVENT_BURNED_TOWER_B1F_TM_ENDURE = 1623, + EVENT_BURNED_TOWER_FIREBREATHER_DICK_ASHES = 1893, + EVENT_BURNED_TOWER_FIREBREATHER_DICK_NORMAL = 1892, + EVENT_CELADON_CITY_HIDDEN_PP_UP = 253, + EVENT_CERULEAN_GYM_ROCKET = 1901, + EVENT_CHAD_ASKED_FOR_PHONE_NUMBER = 651, + EVENT_CHAD_READY_FOR_REMATCH = 652, + EVENT_CHARCOAL_KILN_APPRENTICE = 1782, + EVENT_CHARCOAL_KILN_BOSS = 1783, + EVENT_CHARCOAL_KILN_FARFETCH_D = 1781, + EVENT_CHIKORITA_POKEBALL_IN_ELMS_LAB = 1602, + EVENT_CHRIS_ASKED_FOR_PHONE_NUMBER = 655, + EVENT_CHRIS_READY_FOR_REMATCH = 656, + EVENT_CIANWOOD_CITY_HIDDEN_MAX_ETHER = 179, + EVENT_CIANWOOD_CITY_HIDDEN_REVIVE = 178, + EVENT_CINNABAR_ISLAND_HIDDEN_RARE_CANDY = 254, + EVENT_CINNABAR_ROCKS_CLEARED = 215, + EVENT_CLEARED_NUGGET_BRIDGE = 216, + EVENT_CLEARED_RADIO_TOWER = 33, + EVENT_CLEARED_ROCKET_HIDEOUT = 34, + EVENT_CLEARED_SLOWPOKE_WELL = 43, + EVENT_CONTEST_OFFICER_HAS_BERRY = 790, + EVENT_CONTEST_OFFICER_HAS_EVERSTONE = 788, + EVENT_CONTEST_OFFICER_HAS_GOLD_BERRY = 789, + EVENT_CONTEST_OFFICER_HAS_SUN_STONE = 787, + EVENT_COPYCATS_HOUSE_2F_DOLL = 1907, + EVENT_COP_IN_ELMS_LAB = 1794, + EVENT_CYNDAQUIL_POKEBALL_IN_ELMS_LAB = 1600, + EVENT_DANA_ASKED_FOR_PHONE_NUMBER = 649, + EVENT_DANA_READY_FOR_REMATCH = 650, + EVENT_DARK_CAVE_BLACKTHORN_ENTRANCE_REVIVE = 1697, + EVENT_DARK_CAVE_BLACKTHORN_ENTRANCE_TM_SNORE = 1698, + EVENT_DARK_CAVE_VIOLET_ENTRANCE_FULL_HEAL = 1695, + EVENT_DARK_CAVE_VIOLET_ENTRANCE_HIDDEN_ELIXER = 157, + EVENT_DARK_CAVE_VIOLET_ENTRANCE_HYPER_POTION = 1696, + EVENT_DARK_CAVE_VIOLET_ENTRANCE_POTION = 1694, + EVENT_DAY_CARE_MAN_IN_DAY_CARE = 1765, + EVENT_DAY_CARE_MAN_ON_ROUTE_34 = 1766, + EVENT_DAY_CARE_MON_1 = 1767, + EVENT_DAY_CARE_MON_2 = 1768, + EVENT_DECIDED_TO_HELP_LANCE = 96, + EVENT_DECO_BED_1 = 676, + EVENT_DECO_BED_2 = 677, + EVENT_DECO_BED_3 = 678, + EVENT_DECO_BED_4 = 679, + EVENT_DECO_BIG_LAPRAS_DOLL = 721, + EVENT_DECO_BIG_ONIX_DOLL = 720, + EVENT_DECO_BIG_SNORLAX_DOLL = 719, + EVENT_DECO_BULBASAUR_DOLL = 699, + EVENT_DECO_CARPET_1 = 680, + EVENT_DECO_CARPET_2 = 681, + EVENT_DECO_CARPET_3 = 682, + EVENT_DECO_CARPET_4 = 683, + EVENT_DECO_CHARMANDER_DOLL = 700, + EVENT_DECO_CLEFAIRY_DOLL = 697, + EVENT_DECO_DIGLETT_DOLL = 703, + EVENT_DECO_FAMICOM = 691, + EVENT_DECO_GENGAR_DOLL = 707, + EVENT_DECO_GEODUDE_DOLL = 713, + EVENT_DECO_GOLD_TROPHY = 717, + EVENT_DECO_GRIMER_DOLL = 709, + EVENT_DECO_JIGGLYPUFF_DOLL = 698, + EVENT_DECO_MACHOP_DOLL = 714, + EVENT_DECO_MAGIKARP_DOLL = 705, + EVENT_DECO_N64 = 693, + EVENT_DECO_ODDISH_DOLL = 706, + EVENT_DECO_PIKACHU_DOLL = 695, + EVENT_DECO_PLANT_1 = 684, + EVENT_DECO_PLANT_2 = 685, + EVENT_DECO_PLANT_3 = 686, + EVENT_DECO_POLIWAG_DOLL = 702, + EVENT_DECO_POSTER_1 = 687, + EVENT_DECO_POSTER_2 = 688, + EVENT_DECO_POSTER_3 = 689, + EVENT_DECO_POSTER_4 = 690, + EVENT_DECO_SHELLDER_DOLL = 708, + EVENT_DECO_SILVER_TROPHY = 718, + EVENT_DECO_SNES = 692, + EVENT_DECO_SQUIRTLE_DOLL = 701, + EVENT_DECO_STARYU_DOLL = 704, + EVENT_DECO_SURFING_PIKACHU_DOLL = 696, + EVENT_DECO_TENTACOOL_DOLL = 715, + EVENT_DECO_UNOWN_DOLL = 712, + EVENT_DECO_VIRTUAL_BOY = 694, + EVENT_DECO_VOLTORB_DOLL = 710, + EVENT_DECO_WEEDLE_DOLL = 711, + EVENT_DEREK_ASKED_FOR_PHONE_NUMBER = 653, + EVENT_DEREK_READY_FOR_REMATCH = 654, + EVENT_DIGLETTS_CAVE_HIDDEN_MAX_REVIVE = 228, + EVENT_DOOR_10_OPEN = 736, + EVENT_DOOR_11_OPEN = 737, + EVENT_DOOR_1_OPEN = 727, + EVENT_DOOR_2_OPEN = 728, + EVENT_DOOR_3_OPEN = 729, + EVENT_DOOR_4_OPEN = 730, + EVENT_DOOR_5_OPEN = 731, + EVENT_DOOR_6_OPEN = 732, + EVENT_DOOR_7_OPEN = 733, + EVENT_DOOR_8_OPEN = 734, + EVENT_DOOR_9_OPEN = 735, + EVENT_DRAGONS_DEN_B1F_DRAGON_FANG = 1704, + EVENT_DRAGONS_DEN_B1F_HIDDEN_MAX_ELIXER = 162, + EVENT_DRAGONS_DEN_B1F_HIDDEN_MAX_POTION = 161, + EVENT_DRAGONS_DEN_B1F_HIDDEN_REVIVE = 160, + EVENT_DRAGONS_DEN_CLAIR = 1752, + EVENT_DUDE_TALKED_TO_YOU = 65, + EVENT_EARLS_ACADEMY_EARL = 1739, + EVENT_ECRUTEAK_CITY_HIDDEN_HYPER_POTION = 180, + EVENT_ECRUTEAK_POKE_CENTER_BILL = 1811, + EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_LEFT = 1894, + EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_RIGHT = 1895, + EVENT_ELMS_AIDE_IN_LAB = 1793, + EVENT_ELMS_AIDE_IN_VIOLET_POKEMON_CENTER = 1792, + EVENT_ELM_CALLED_ABOUT_STOLEN_POKEMON = 67, + EVENT_EMERGENCY_SWITCH = 726, + EVENT_ENABLE_DIPLOMA_PRINTING = 214, + EVENT_ERIN_ASKED_FOR_PHONE_NUMBER = 669, + EVENT_ERIN_READY_FOR_REMATCH = 670, + EVENT_EXPLODING_TRAP_1 = 745, + EVENT_EXPLODING_TRAP_10 = 754, + EVENT_EXPLODING_TRAP_11 = 755, + EVENT_EXPLODING_TRAP_12 = 756, + EVENT_EXPLODING_TRAP_13 = 757, + EVENT_EXPLODING_TRAP_14 = 758, + EVENT_EXPLODING_TRAP_15 = 759, + EVENT_EXPLODING_TRAP_16 = 760, + EVENT_EXPLODING_TRAP_17 = 761, + EVENT_EXPLODING_TRAP_18 = 762, + EVENT_EXPLODING_TRAP_19 = 763, + EVENT_EXPLODING_TRAP_2 = 746, + EVENT_EXPLODING_TRAP_20 = 764, + EVENT_EXPLODING_TRAP_21 = 765, + EVENT_EXPLODING_TRAP_22 = 766, + EVENT_EXPLODING_TRAP_3 = 747, + EVENT_EXPLODING_TRAP_4 = 748, + EVENT_EXPLODING_TRAP_5 = 749, + EVENT_EXPLODING_TRAP_6 = 750, + EVENT_EXPLODING_TRAP_7 = 751, + EVENT_EXPLODING_TRAP_8 = 752, + EVENT_EXPLODING_TRAP_9 = 753, + EVENT_FAST_SHIP_1F_GENTLEMAN = 1836, + EVENT_FAST_SHIP_B1F_SAILOR_LEFT = 1838, + EVENT_FAST_SHIP_B1F_SAILOR_RIGHT = 1839, + EVENT_FAST_SHIP_CABINS_NNW_NNE_NE_SAILOR = 1837, + EVENT_FAST_SHIP_CABINS_SE_SSE_CAPTAINS_CABIN_TWIN_1 = 1841, + EVENT_FAST_SHIP_CABINS_SE_SSE_CAPTAINS_CABIN_TWIN_2 = 1842, + EVENT_FAST_SHIP_CABINS_SE_SSE_GENTLEMAN = 1840, + EVENT_FAST_SHIP_DESTINATION_OLIVINE = 47, + EVENT_FAST_SHIP_FIRST_TIME = 48, + EVENT_FAST_SHIP_FOUND_GIRL = 50, + EVENT_FAST_SHIP_HAS_ARRIVED = 49, + EVENT_FAST_SHIP_INFORMED_ABOUT_LAZY_SAILOR = 52, + EVENT_FAST_SHIP_LAZY_SAILOR = 51, + EVENT_FAST_SHIP_PASSENGERS_EASTBOUND = 1850, + EVENT_FAST_SHIP_PASSENGERS_FIRST_TRIP = 1849, + EVENT_FAST_SHIP_PASSENGERS_WESTBOUND = 1851, + EVENT_FIRST_TIME_BANKING_WITH_MOM = 118, + EVENT_FOUGHT_HO_OH = 791, + EVENT_FOUGHT_LUGIA = 792, + EVENT_FOUGHT_SNORLAX = 1872, + EVENT_FOUGHT_SUDOWOODO = 42, + EVENT_FOUND_BERSERK_GENE_IN_CERULEAN_CITY = 250, + EVENT_FOUND_LEFTOVERS_IN_CELADON_CAFE = 249, + EVENT_FOUND_MACHINE_PART_IN_CERULEAN_GYM = 251, + EVENT_GAVEN_ASKED_FOR_PHONE_NUMBER = 619, + EVENT_GAVEN_READY_FOR_REMATCH = 620, + EVENT_GAVE_KENYA = 81, + EVENT_GAVE_KURT_BLK_APRICORN = 605, + EVENT_GAVE_KURT_BLU_APRICORN = 601, + EVENT_GAVE_KURT_GRN_APRICORN = 603, + EVENT_GAVE_KURT_PNK_APRICORN = 606, + EVENT_GAVE_KURT_RED_APRICORN = 600, + EVENT_GAVE_KURT_WHT_APRICORN = 604, + EVENT_GAVE_KURT_YLW_APRICORN = 602, + EVENT_GAVE_MYSTERY_EGG_TO_ELM = 31, + EVENT_GINA_ASKED_FOR_PHONE_NUMBER = 639, + EVENT_GINA_READY_FOR_REMATCH = 640, + EVENT_GOLDENROD_CITY_CIVILIANS = 1743, + EVENT_GOLDENROD_CITY_ROCKET_SCOUT = 1740, + EVENT_GOLDENROD_CITY_ROCKET_TAKEOVER = 1741, + EVENT_GOLDENROD_DEPT_STORE_5F_HAPPINESS_EVENT_LADY = 1891, + EVENT_GOLDENROD_DEPT_STORE_B1F_AMULET_COIN = 1654, + EVENT_GOLDENROD_DEPT_STORE_B1F_BURN_HEAL = 1655, + EVENT_GOLDENROD_DEPT_STORE_B1F_ETHER = 1653, + EVENT_GOLDENROD_DEPT_STORE_B1F_LAYOUT_1 = 772, + EVENT_GOLDENROD_DEPT_STORE_B1F_LAYOUT_2 = 773, + EVENT_GOLDENROD_DEPT_STORE_B1F_LAYOUT_3 = 774, + EVENT_GOLDENROD_DEPT_STORE_B1F_ULTRA_BALL = 1656, + EVENT_GOLDENROD_TRAIN_STATION_GENTLEMAN = 1865, + EVENT_GOLDENROD_UNDERGROUND_COIN_CASE = 1650, + EVENT_GOLDENROD_UNDERGROUND_GRAMPS = 1875, + EVENT_GOLDENROD_UNDERGROUND_GRANNY = 1874, + EVENT_GOLDENROD_UNDERGROUND_HIDDEN_ANTIDOTE = 141, + EVENT_GOLDENROD_UNDERGROUND_HIDDEN_PARLYZ_HEAL = 139, + EVENT_GOLDENROD_UNDERGROUND_HIDDEN_SUPER_POTION = 140, + EVENT_GOLDENROD_UNDERGROUND_OLDER_HAIRCUT_BROTHER = 1876, + EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_FULL_HEAL = 1652, + EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_HIDDEN_MAX_POTION = 142, + EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_HIDDEN_REVIVE = 143, + EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_SMOKE_BALL = 1651, + EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_BLOCKED_OFF = 775, + EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_MAX_ETHER = 1657, + EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_TM_SLEEP_TALK = 1658, + EVENT_GOLDENROD_UNDERGROUND_YOUNGER_HAIRCUT_BROTHER = 1877, + EVENT_GOT_A_POKEMON_FROM_ELM = 26, + EVENT_GOT_BERRY_FROM_ROUTE_30_HOUSE = 39, + EVENT_GOT_BICYCLE = 91, + EVENT_GOT_BLACKBELT_FROM_WESLEY = 107, + EVENT_GOT_BLACKGLASSES_IN_DARK_CAVE = 114, + EVENT_GOT_CALCIUM_FROM_ERIN = 617, + EVENT_GOT_CARBOS_FROM_VANCE = 615, + EVENT_GOT_CHARCOAL_IN_CHARCOAL_KILN = 94, + EVENT_GOT_CHIKORITA_FROM_ELM = 29, + EVENT_GOT_CLEANSE_TAG = 219, + EVENT_GOT_COINS_FROM_GAMBLER_AT_CELADON = 206, + EVENT_GOT_CYNDAQUIL_FROM_ELM = 27, + EVENT_GOT_EEVEE = 79, + EVENT_GOT_EVERSTONE_FROM_BILLS_GRANDPA = 800, + EVENT_GOT_EVERSTONE_FROM_ELM = 86, + EVENT_GOT_FIRE_STONE_FROM_BILLS_GRANDPA = 803, + EVENT_GOT_GOOD_ROD = 24, + EVENT_GOT_HARD_STONE_FROM_ARTHUR = 103, + EVENT_GOT_HM01_CUT = 16, + EVENT_GOT_HM02_FLY = 17, + EVENT_GOT_HM03_SURF = 18, + EVENT_GOT_HM04_STRENGTH = 19, + EVENT_GOT_HM05_FLASH = 20, + EVENT_GOT_HM06_WHIRLPOOL = 21, + EVENT_GOT_HM07_WATERFALL = 1672, + EVENT_GOT_HP_UP_FROM_JOEY = 614, + EVENT_GOT_HP_UP_FROM_RANDY = 82, + EVENT_GOT_HP_UP_FROM_VERMILION_GUY = 226, + EVENT_GOT_IRON_FROM_PARRY = 616, + EVENT_GOT_ITEMFINDER = 90, + EVENT_GOT_KENYA = 80, + EVENT_GOT_KINGS_ROCK_IN_SLOWPOKE_WELL = 115, + EVENT_GOT_LEAF_STONE_FROM_BILLS_GRANDPA = 801, + EVENT_GOT_LOST_ITEM_FROM_FAN_CLUB = 210, + EVENT_GOT_MAGNET_FROM_SUNNY = 105, + EVENT_GOT_MASTER_BALL_FROM_ELM = 124, + EVENT_GOT_METAL_COAT_FROM_GRANDPA_ON_SS_AQUA = 113, + EVENT_GOT_MIRACLE_SEED_IN_ROUTE_32 = 93, + EVENT_GOT_MYSTERY_EGG_FROM_MR_POKEMON = 30, + EVENT_GOT_MYSTIC_WATER_IN_CHERRYGROVE = 77, + EVENT_GOT_NUGGET_FROM_GUY = 200, + EVENT_GOT_OLD_ROD = 23, + EVENT_GOT_PASS_FROM_COPYCAT = 209, + EVENT_GOT_PINK_BOW_FROM_MARY = 72, + EVENT_GOT_PINK_BOW_FROM_TUSCANY = 101, + EVENT_GOT_POISON_BARB_FROM_FRIEDA = 99, + EVENT_GOT_PROTEIN_FROM_HUEY = 613, + EVENT_GOT_QUICK_CLAW = 87, + EVENT_GOT_RAINBOW_WING = 120, + EVENT_GOT_SECRETPOTION_FROM_PHARMACY = 35, + EVENT_GOT_SHARP_BEAK_FROM_MONICA = 111, + EVENT_GOT_SHUCKIE = 69, + EVENT_GOT_SILVER_WING = 121, + EVENT_GOT_SOFT_SAND_FROM_KATE = 112, + EVENT_GOT_SPELL_TAG_FROM_SANTOS = 109, + EVENT_GOT_SQUIRTBOTTLE = 92, + EVENT_GOT_SS_TICKET_FROM_ELM = 36, + EVENT_GOT_SUNNY_DAY_FROM_RADIO_TOWER = 71, + EVENT_GOT_SUPER_ROD = 25, + EVENT_GOT_THUNDERSTONE_FROM_BILLS_GRANDPA = 804, + EVENT_GOT_TM01_DYNAMICPUNCH = 10, + EVENT_GOT_TM02_HEADBUTT = 95, + EVENT_GOT_TM03_CURSE = 218, + EVENT_GOT_TM05_ROAR = 78, + EVENT_GOT_TM06_TOXIC = 221, + EVENT_GOT_TM07_ZAP_CANNON = 223, + EVENT_GOT_TM08_ROCK_SMASH = 75, + EVENT_GOT_TM10_HIDDEN_POWER = 88, + EVENT_GOT_TM12_SWEET_SCENT = 122, + EVENT_GOT_TM13_SNORE_FROM_MOOMOO_FARM = 62, + EVENT_GOT_TM16_ICY_WIND = 14, + EVENT_GOT_TM19_GIGA_DRAIN = 220, + EVENT_GOT_TM23_IRON_TAIL = 13, + EVENT_GOT_TM24_DRAGONBREATH = 15, + EVENT_GOT_TM29_PSYCHIC = 227, + EVENT_GOT_TM30_SHADOW_BALL = 12, + EVENT_GOT_TM31_MUD_SLAP = 8, + EVENT_GOT_TM36_SLUDGE_BOMB = 89, + EVENT_GOT_TM37_SANDSTORM = 117, + EVENT_GOT_TM42_DREAM_EATER = 224, + EVENT_GOT_TM45_ATTRACT = 11, + EVENT_GOT_TM47_STEEL_WING = 116, + EVENT_GOT_TM49_FURY_CUTTER = 9, + EVENT_GOT_TM50_NIGHTMARE = 83, + EVENT_GOT_TOGEPI_EGG_FROM_ELMS_AIDE = 45, + EVENT_GOT_TOTODILE_FROM_ELM = 28, + EVENT_GOT_TYROGUE_FROM_KIYO = 97, + EVENT_GOT_UP_GRADE = 222, + EVENT_GOT_WATER_STONE_FROM_BILLS_GRANDPA = 802, + EVENT_GUIDE_GENT_IN_HIS_HOUSE = 1790, + EVENT_GUIDE_GENT_VISIBLE_IN_CHERRYGROVE = 1791, + EVENT_HEALED_MOOMOO = 61, + EVENT_HERDED_FARFETCHD = 41, + EVENT_HUEY_ASKED_FOR_PHONE_NUMBER = 611, + EVENT_HUEY_READY_FOR_REMATCH = 612, + EVENT_ICE_PATH_1F_PP_UP = 1673, + EVENT_ICE_PATH_B1F_HIDDEN_MAX_POTION = 148, + EVENT_ICE_PATH_B1F_IRON = 1674, + EVENT_ICE_PATH_B2F_BLACKTHORN_SIDE_HIDDEN_ICE_HEAL = 150, + EVENT_ICE_PATH_B2F_BLACKTHORN_SIDE_TM_REST = 1677, + EVENT_ICE_PATH_B2F_MAHOGANY_SIDE_FULL_HEAL = 1675, + EVENT_ICE_PATH_B2F_MAHOGANY_SIDE_HIDDEN_CARBOS = 149, + EVENT_ICE_PATH_B2F_MAHOGANY_SIDE_MAX_POTION = 1676, + EVENT_ICE_PATH_B3F_NEVERMELTICE = 1678, + EVENT_ILEX_FOREST_APPRENTICE = 1779, + EVENT_ILEX_FOREST_CHARCOAL_MASTER = 1780, + EVENT_ILEX_FOREST_FARFETCHD_1 = 1769, + EVENT_ILEX_FOREST_FARFETCHD_10 = 1778, + EVENT_ILEX_FOREST_FARFETCHD_2 = 1770, + EVENT_ILEX_FOREST_FARFETCHD_3 = 1771, + EVENT_ILEX_FOREST_FARFETCHD_4 = 1772, + EVENT_ILEX_FOREST_FARFETCHD_5 = 1773, + EVENT_ILEX_FOREST_FARFETCHD_6 = 1774, + EVENT_ILEX_FOREST_FARFETCHD_7 = 1775, + EVENT_ILEX_FOREST_FARFETCHD_8 = 1776, + EVENT_ILEX_FOREST_FARFETCHD_9 = 1777, + EVENT_ILEX_FOREST_HIDDEN_ETHER = 136, + EVENT_ILEX_FOREST_HIDDEN_FULL_HEAL = 138, + EVENT_ILEX_FOREST_HIDDEN_SUPER_POTION = 137, + EVENT_ILEX_FOREST_REVIVE = 1649, + EVENT_INDIGO_PLATEAU_POKECENTER_RIVAL = 1915, + EVENT_INITIALIZED_EVENTS = 54, + EVENT_IRWIN_ASKED_FOR_PHONE_NUMBER = 641, + EVENT_IRWIN_READY_FOR_REMATCH = 642, + EVENT_JACK_ASKED_FOR_PHONE_NUMBER = 607, + EVENT_JACK_READY_FOR_REMATCH = 608, + EVENT_JASMINE_EXPLAINED_AMPHYS_SICKNESS = 55, + EVENT_JASMINE_RETURNED_TO_GYM = 32, + EVENT_JOEY_ASKED_FOR_PHONE_NUMBER = 627, + EVENT_JOEY_READY_FOR_REMATCH = 628, + EVENT_JOSE_ASKED_FOR_PHONE_NUMBER = 623, + EVENT_JOSE_READY_FOR_REMATCH = 624, + EVENT_KARENS_ROOM_ENTRANCE_CLOSED = 783, + EVENT_KARENS_ROOM_EXIT_OPEN = 784, + EVENT_KENJI_ASKED_FOR_PHONE_NUMBER = 665, + EVENT_KENJI_ON_BREAK = 618, + EVENT_KENJI_READY_FOR_REMATCH = 666, + EVENT_KOGAS_ROOM_ENTRANCE_CLOSED = 779, + EVENT_KOGAS_ROOM_EXIT_OPEN = 780, + EVENT_KURTS_HOUSE_KURT_1 = 1854, + EVENT_KURTS_HOUSE_KURT_2 = 1855, + EVENT_KURTS_HOUSE_SLOWPOKE = 1789, + EVENT_KURT_GAVE_YOU_LURE_BALL = 53, + EVENT_LAKE_OF_RAGE_ASKED_FOR_MAGIKARP = 57, + EVENT_LAKE_OF_RAGE_CIVILIANS = 1845, + EVENT_LAKE_OF_RAGE_ETHER_ON_STANDBY = 58, + EVENT_LAKE_OF_RAGE_EXPLAINED_WEIRD_MAGIKARP = 56, + EVENT_LAKE_OF_RAGE_HIDDEN_FULL_RESTORE = 181, + EVENT_LAKE_OF_RAGE_HIDDEN_MAX_POTION = 183, + EVENT_LAKE_OF_RAGE_HIDDEN_RARE_CANDY = 182, + EVENT_LAKE_OF_RAGE_LANCE = 1748, + EVENT_LAKE_OF_RAGE_MAX_ETHER = 1605, + EVENT_LAKE_OF_RAGE_RED_GYARADOS = 1873, + EVENT_LAKE_OF_RAGE_TM_DETECT = 1606, + EVENT_LAKE_OF_RAGE_WESLEY_OF_WEDNESDAY = 1884, + EVENT_LANCES_ROOM_ENTRANCE_CLOSED = 785, + EVENT_LANCES_ROOM_EXIT_OPEN = 786, + EVENT_LANCES_ROOM_OAK_AND_MARY = 1887, + EVENT_LANCE_HEALED_YOU_IN_TEAM_ROCKET_BASE = 76, + EVENT_LEARNED_HAIL_GIOVANNI = 767, + EVENT_LEARNED_RATICATE_TAIL = 770, + EVENT_LEARNED_SLOWPOKETAIL = 769, + EVENT_LEARNED_TO_CATCH_POKEMON = 66, + EVENT_LEFT_MONS_WITH_CONTEST_OFFICER = 776, + EVENT_LISTENED_TO_FAN_CLUB_PRESIDENT = 212, + EVENT_LISTENED_TO_FAN_CLUB_PRESIDENT_BUT_BAG_WAS_FULL = 211, + EVENT_LISTENED_TO_INITIAL_RADIO = 805, + EVENT_LIZ_ASKED_FOR_PHONE_NUMBER = 633, + EVENT_LIZ_READY_FOR_REMATCH = 634, + EVENT_MADE_UNOWN_APPEAR_IN_RUINS = 46, + EVENT_MADE_WHITNEY_CRY = 40, + EVENT_MAHOGANY_MART_LANCE_AND_DRAGONITE = 1749, + EVENT_MAHOGANY_MART_OWNERS = 1846, + EVENT_MAHOGANY_TOWN_POKEFAN_M_BLOCKS_EAST = 1878, + EVENT_MAHOGANY_TOWN_POKEFAN_M_BLOCKS_GYM = 1879, + EVENT_MANIA_TOOK_SHUCKIE_OR_LET_YOU_KEEP_HIM = 70, + EVENT_MET_ARTHUR_OF_THURSDAY = 102, + EVENT_MET_BILL = 1810, + EVENT_MET_BILLS_GRANDPA = 794, + EVENT_MET_COPYCAT_FOUND_OUT_ABOUT_LOST_ITEM = 207, + EVENT_MET_FRIEDA_OF_FRIDAY = 98, + EVENT_MET_MANAGER_AT_POWER_PLANT = 202, + EVENT_MET_MONICA_OF_MONDAY = 110, + EVENT_MET_REDS_MOM = 204, + EVENT_MET_ROCKET_GRUNT_AT_CERULEAN_GYM = 203, + EVENT_MET_SANTOS_OF_SATURDAY = 108, + EVENT_MET_SUNNY_OF_SUNDAY = 104, + EVENT_MET_TUSCANY_OF_TUESDAY = 100, + EVENT_MET_WESLEY_OF_WEDNESDAY = 106, + EVENT_MOUNT_MOON_SQUARE_HIDDEN_MOON_STONE = 236, + EVENT_MOUNT_MORTAR_1F_INSIDE_ESCAPE_ROPE = 1661, + EVENT_MOUNT_MORTAR_1F_INSIDE_HIDDEN_MAX_REPEL = 145, + EVENT_MOUNT_MORTAR_1F_INSIDE_HYPER_POTION = 1663, + EVENT_MOUNT_MORTAR_1F_INSIDE_MAX_REVIVE = 1662, + EVENT_MOUNT_MORTAR_1F_OUTSIDE_ETHER = 1659, + EVENT_MOUNT_MORTAR_1F_OUTSIDE_HIDDEN_HYPER_POTION = 144, + EVENT_MOUNT_MORTAR_1F_OUTSIDE_REVIVE = 1660, + EVENT_MOUNT_MORTAR_2F_INSIDE_DRAGON_SCALE = 1667, + EVENT_MOUNT_MORTAR_2F_INSIDE_ELIXER = 1668, + EVENT_MOUNT_MORTAR_2F_INSIDE_ESCAPE_ROPE = 1669, + EVENT_MOUNT_MORTAR_2F_INSIDE_HIDDEN_FULL_RESTORE = 146, + EVENT_MOUNT_MORTAR_2F_INSIDE_MAX_POTION = 1664, + EVENT_MOUNT_MORTAR_2F_INSIDE_RARE_CANDY = 1665, + EVENT_MOUNT_MORTAR_2F_INSIDE_TM_DEFENSE_CURL = 1666, + EVENT_MOUNT_MORTAR_B1F_FULL_HEAL = 1671, + EVENT_MOUNT_MORTAR_B1F_HIDDEN_MAX_REVIVE = 147, + EVENT_MOUNT_MORTAR_B1F_HYPER_POTION = 1670, + EVENT_MR_POKEMONS_HOUSE_OAK = 1737, + EVENT_MT_MOON_RIVAL = 1914, + EVENT_MT_MOON_SQUARE_CLEFAIRY = 1913, + EVENT_MT_MOON_SQUARE_ROCK = 1912, + EVENT_MYSTERY_GIFT_DELIVERY_GUY = 1809, + EVENT_NATIONAL_PARK_HIDDEN_FULL_HEAL = 132, + EVENT_NATIONAL_PARK_PARLYZ_HEAL = 1624, + EVENT_NATIONAL_PARK_TM_DIG = 1625, + EVENT_OLIVINE_GYM_JASMINE = 1747, + EVENT_OLIVINE_LIGHTHOUSE_3F_ETHER = 1636, + EVENT_OLIVINE_LIGHTHOUSE_5F_GREAT_BALL = 1638, + EVENT_OLIVINE_LIGHTHOUSE_5F_HIDDEN_HYPER_POTION = 133, + EVENT_OLIVINE_LIGHTHOUSE_5F_RARE_CANDY = 1637, + EVENT_OLIVINE_LIGHTHOUSE_5F_TM_SWAGGER = 1639, + EVENT_OLIVINE_LIGHTHOUSE_6F_SUPER_POTION = 1640, + EVENT_OLIVINE_LIGHTHOUSE_JASMINE = 1746, + EVENT_OLIVINE_PORT_HIDDEN_PROTEIN = 234, + EVENT_OLIVINE_PORT_PASSAGE_POKEFAN_M = 1843, + EVENT_OLIVINE_PORT_SAILOR_AT_GANGWAY = 1834, + EVENT_OLIVINE_PORT_SPRITES_AFTER_HALL_OF_FAME = 1848, + EVENT_OLIVINE_PORT_SPRITES_BEFORE_HALL_OF_FAME = 1847, + EVENT_OPENED_DOOR_TO_GIOVANNIS_OFFICE = 771, + EVENT_OPENED_DOOR_TO_ROCKET_HIDEOUT_TRANSMITTER = 768, + EVENT_OPENED_MT_SILVER = 1871, + EVENT_PARRY_ASKED_FOR_PHONE_NUMBER = 667, + EVENT_PARRY_READY_FOR_REMATCH = 668, + EVENT_PICKED_UP_FOCUS_BAND = 1917, + EVENT_PLAYERS_HOUSE_2F_BIG_DOLL = 1860, + EVENT_PLAYERS_HOUSE_2F_CONSOLE = 1857, + EVENT_PLAYERS_HOUSE_2F_DOLL_1 = 1858, + EVENT_PLAYERS_HOUSE_2F_DOLL_2 = 1859, + EVENT_PLAYERS_HOUSE_MOM_1 = 1735, + EVENT_PLAYERS_HOUSE_MOM_2 = 1736, + EVENT_PLAYERS_ROOM_POSTER = 716, + EVENT_RADIO_TOWER_BLACKBELT_BLOCKS_STAIRS = 1745, + EVENT_RADIO_TOWER_CIVILIANS_AFTER = 1744, + EVENT_RADIO_TOWER_ROCKET_TAKEOVER = 1742, + EVENT_RALPH_ASKED_FOR_PHONE_NUMBER = 631, + EVENT_RALPH_READY_FOR_REMATCH = 632, + EVENT_RECEIVED_CARD_KEY = 74, + EVENT_RED_IN_MT_SILVER = 1890, + EVENT_REENA_ASKED_FOR_PHONE_NUMBER = 625, + EVENT_REENA_READY_FOR_REMATCH = 626, + EVENT_REFUSED_TO_HELP_LANCE_AT_LAKE_OF_RAGE = 38, + EVENT_REFUSED_TO_TAKE_EGG_FROM_ELMS_AIDE = 44, + EVENT_RELEASED_THE_BEASTS = 123, + EVENT_RESTORED_POWER_TO_KANTO = 205, + EVENT_RETURNED_LOST_ITEM_TO_COPYCAT = 208, + EVENT_RETURNED_MACHINE_PART = 201, + EVENT_RIVAL_AZALEA_TOWN = 1727, + EVENT_RIVAL_BURNED_TOWER = 1733, + EVENT_RIVAL_CHERRYGROVE_CITY = 1726, + EVENT_RIVAL_DRAGONS_DEN = 1734, + EVENT_RIVAL_GOLDENROD_UNDERGROUND = 1729, + EVENT_RIVAL_NEW_BARK_TOWN = 1725, + EVENT_RIVAL_OLIVINE_CITY = 1731, + EVENT_RIVAL_SPROUT_TOWER = 1732, + EVENT_RIVAL_TEAM_ROCKET_BASE = 1728, + EVENT_RIVAL_VICTORY_ROAD = 1730, + EVENT_ROCK_TUNNEL_1F_ELIXER = 1918, + EVENT_ROCK_TUNNEL_1F_HIDDEN_X_ACCURACY = 231, + EVENT_ROCK_TUNNEL_1F_HIDDEN_X_DEFEND = 232, + EVENT_ROCK_TUNNEL_1F_TM_STEEL_WING = 1919, + EVENT_ROCK_TUNNEL_B1F_HIDDEN_MAX_POTION = 233, + EVENT_ROCK_TUNNEL_B1F_IRON = 1920, + EVENT_ROCK_TUNNEL_B1F_PP_UP = 1921, + EVENT_ROCK_TUNNEL_B1F_REVIVE = 1922, + EVENT_ROUTE_11_HIDDEN_REVIVE = 245, + EVENT_ROUTE_12_CALCIUM = 1928, + EVENT_ROUTE_12_HIDDEN_ELIXER = 243, + EVENT_ROUTE_12_NUGGET = 1929, + EVENT_ROUTE_13_HIDDEN_CALCIUM = 244, + EVENT_ROUTE_15_PP_UP = 1930, + EVENT_ROUTE_17_HIDDEN_MAX_ELIXER = 247, + EVENT_ROUTE_17_HIDDEN_MAX_ETHER = 246, + EVENT_ROUTE_24_ROCKET = 1900, + EVENT_ROUTE_25_HIDDEN_POTION = 248, + EVENT_ROUTE_25_MISTY_BOYFRIEND = 1902, + EVENT_ROUTE_25_PROTEIN = 1931, + EVENT_ROUTE_26_MAX_ELIXER = 1706, + EVENT_ROUTE_27_RARE_CANDY = 1708, + EVENT_ROUTE_27_TM_SOLARBEAM = 1707, + EVENT_ROUTE_28_HIDDEN_RARE_CANDY = 163, + EVENT_ROUTE_29_POTION = 1709, + EVENT_ROUTE_29_TUSCANY_OF_TUESDAY = 1881, + EVENT_ROUTE_2_CARBOS = 1925, + EVENT_ROUTE_2_DIRE_HIT = 1923, + EVENT_ROUTE_2_ELIXER = 1926, + EVENT_ROUTE_2_HIDDEN_FULL_HEAL = 238, + EVENT_ROUTE_2_HIDDEN_FULL_RESTORE = 239, + EVENT_ROUTE_2_HIDDEN_MAX_ETHER = 237, + EVENT_ROUTE_2_HIDDEN_REVIVE = 240, + EVENT_ROUTE_2_MAX_POTION = 1924, + EVENT_ROUTE_30_BATTLE = 1812, + EVENT_ROUTE_30_HIDDEN_POTION = 164, + EVENT_ROUTE_30_YOUNGSTER_JOEY = 1813, + EVENT_ROUTE_31_ANTIDOTE = 1710, + EVENT_ROUTE_31_POKE_BALL = 1711, + EVENT_ROUTE_32_FRIEDA_OF_FRIDAY = 1880, + EVENT_ROUTE_32_GREAT_BALL = 1712, + EVENT_ROUTE_32_HIDDEN_GREAT_BALL = 165, + EVENT_ROUTE_32_HIDDEN_SUPER_POTION = 166, + EVENT_ROUTE_32_POTION = 1713, + EVENT_ROUTE_34_HIDDEN_RARE_CANDY = 167, + EVENT_ROUTE_34_HIDDEN_SUPER_POTION = 168, + EVENT_ROUTE_35_NATIONAL_PARK_GATE_OFFICER_CONTEST_DAY = 1861, + EVENT_ROUTE_35_NATIONAL_PARK_GATE_OFFICER_NOT_CONTEST_DAY = 1862, + EVENT_ROUTE_35_NATIONAL_PARK_GATE_YOUNGSTER = 1844, + EVENT_ROUTE_35_TM_ROLLOUT = 1714, + EVENT_ROUTE_36_ARTHUR_OF_THURSDAY = 1882, + EVENT_ROUTE_36_NATIONAL_PARK_GATE_OFFICER_CONTEST_DAY = 1863, + EVENT_ROUTE_36_NATIONAL_PARK_GATE_OFFICER_NOT_CONTEST_DAY = 1864, + EVENT_ROUTE_36_SUDOWOODO = 1784, + EVENT_ROUTE_37_HIDDEN_ETHER = 169, + EVENT_ROUTE_37_SUNNY_OF_SUNDAY = 1883, + EVENT_ROUTE_39_HIDDEN_NUGGET = 170, + EVENT_ROUTE_40_HIDDEN_HYPER_POTION = 171, + EVENT_ROUTE_40_MONICA_OF_MONDAY = 1886, + EVENT_ROUTE_41_HIDDEN_MAX_ETHER = 172, + EVENT_ROUTE_42_HIDDEN_MAX_POTION = 173, + EVENT_ROUTE_42_SUPER_POTION = 1716, + EVENT_ROUTE_42_ULTRA_BALL = 1715, + EVENT_ROUTE_43_GATE_ROCKETS = 1756, + EVENT_ROUTE_43_MAX_ETHER = 1717, + EVENT_ROUTE_44_HIDDEN_ELIXER = 174, + EVENT_ROUTE_44_MAX_REVIVE = 1718, + EVENT_ROUTE_44_ULTRA_BALL = 1719, + EVENT_ROUTE_45_ELIXER = 1722, + EVENT_ROUTE_45_HIDDEN_PP_UP = 175, + EVENT_ROUTE_45_MAX_POTION = 1723, + EVENT_ROUTE_45_REVIVE = 1721, + EVENT_ROUTE_45_X_SPECIAL = 1720, + EVENT_ROUTE_46_DIRE_HIT = 1724, + EVENT_ROUTE_4_HIDDEN_ULTRA_BALL = 241, + EVENT_ROUTE_4_HP_UP = 1927, + EVENT_ROUTE_5_6_POKEFAN_M_BLOCKS_UNDERGROUND_PATH = 1905, + EVENT_ROUTE_9_HIDDEN_ETHER = 242, + EVENT_RUINS_OF_ALPH_INNER_CHAMBER_TOURISTS = 1797, + EVENT_RUINS_OF_ALPH_KABUTO_CHAMBER_RECEPTIONIST = 1870, + EVENT_RUINS_OF_ALPH_OUTSIDE_SCIENTIST = 1795, + EVENT_RUINS_OF_ALPH_RESEARCH_CENTER_SCIENTIST = 1796, + EVENT_SAFFRON_TRAIN_STATION_POPULATION = 1906, + EVENT_SEAFOAM_GYM_GYM_GUIDE = 1911, + EVENT_SECURITY_CAMERA_1 = 740, + EVENT_SECURITY_CAMERA_2 = 741, + EVENT_SECURITY_CAMERA_3 = 742, + EVENT_SECURITY_CAMERA_4 = 743, + EVENT_SECURITY_CAMERA_5 = 744, + EVENT_SHOWED_GROWLITHE_VULPIX_TO_BILLS_GRANDPA = 798, + EVENT_SHOWED_LICKITUNG_TO_BILLS_GRANDPA = 795, + EVENT_SHOWED_ODDISH_TO_BILLS_GRANDPA = 796, + EVENT_SHOWED_PICHU_TO_BILLS_GRANDPA = 799, + EVENT_SHOWED_STARYU_TO_BILLS_GRANDPA = 797, + EVENT_SHOWED_TOGEPI_TO_ELM = 85, + EVENT_SILVER_CAVE_ITEM_ROOMS_FULL_RESTORE = 1693, + EVENT_SILVER_CAVE_ITEM_ROOMS_MAX_REVIVE = 1692, + EVENT_SILVER_CAVE_OUTSIDE_HIDDEN_FULL_RESTORE = 184, + EVENT_SILVER_CAVE_ROOM_1_ESCAPE_ROPE = 1691, + EVENT_SILVER_CAVE_ROOM_1_HIDDEN_DIRE_HIT = 154, + EVENT_SILVER_CAVE_ROOM_1_HIDDEN_ULTRA_BALL = 155, + EVENT_SILVER_CAVE_ROOM_1_MAX_ELIXER = 1689, + EVENT_SILVER_CAVE_ROOM_1_X_ACCURACY = 1690, + EVENT_SILVER_CAVE_ROOM_2_HIDDEN_MAX_POTION = 156, + EVENT_SLOWPOKE_WELL_B1F_SUPER_POTION = 1634, + EVENT_SLOWPOKE_WELL_B2F_TM_RAIN_DANCE = 1635, + EVENT_SLOWPOKE_WELL_KURT = 1856, + EVENT_SLOWPOKE_WELL_ROCKETS = 1788, + EVENT_SLOWPOKE_WELL_SLOWPOKES = 1787, + EVENT_SOLVED_AERODACTYL_PUZZLE = 675, + EVENT_SOLVED_HO_OH_PUZZLE = 672, + EVENT_SOLVED_KABUTO_PUZZLE = 673, + EVENT_SOLVED_OMANYTE_PUZZLE = 674, + EVENT_SPROUT_TOWER_1F_PARLYZ_HEAL = 1607, + EVENT_SPROUT_TOWER_2F_X_DEFEND = 1608, + EVENT_SPROUT_TOWER_3F_ESCAPE_ROPE = 1610, + EVENT_SPROUT_TOWER_3F_POTION = 1609, + EVENT_SWITCH_1 = 723, + EVENT_SWITCH_2 = 724, + EVENT_SWITCH_3 = 725, + EVENT_TALKED_TO_FARMER_ABOUT_MOOMOO = 63, + EVENT_TALKED_TO_MOM_AFTER_MYSTERY_EGG_QUEST = 64, + EVENT_TALKED_TO_OAK_IN_KANTO = 225, + EVENT_TALKED_TO_SEAFOAM_GYM_GUIDE_ONCE = 213, + EVENT_TALKED_TO_WARDENS_GRANDDAUGHTER = 217, + EVENT_TEAM_ROCKET_BASE_B1F_HIDDEN_REVIVE = 134, + EVENT_TEAM_ROCKET_BASE_B1F_HYPER_POTION = 1641, + EVENT_TEAM_ROCKET_BASE_B1F_NUGGET = 1642, + EVENT_TEAM_ROCKET_BASE_B1F_X_ACCURACY = 1643, + EVENT_TEAM_ROCKET_BASE_B2F_DRAGONITE = 1759, + EVENT_TEAM_ROCKET_BASE_B2F_ELECTRODE_1 = 1760, + EVENT_TEAM_ROCKET_BASE_B2F_ELECTRODE_2 = 1761, + EVENT_TEAM_ROCKET_BASE_B2F_ELECTRODE_3 = 1762, + EVENT_TEAM_ROCKET_BASE_B2F_EXECUTIVE = 1757, + EVENT_TEAM_ROCKET_BASE_B2F_GRUNT_WITH_EXECUTIVE = 1758, + EVENT_TEAM_ROCKET_BASE_B2F_HIDDEN_FULL_HEAL = 135, + EVENT_TEAM_ROCKET_BASE_B2F_LANCE = 1750, + EVENT_TEAM_ROCKET_BASE_B2F_TM_THIEF = 1644, + EVENT_TEAM_ROCKET_BASE_B3F_DIRE_HIT = 1646, + EVENT_TEAM_ROCKET_BASE_B3F_EXECUTIVE = 1755, + EVENT_TEAM_ROCKET_BASE_B3F_FULL_HEAL = 1645, + EVENT_TEAM_ROCKET_BASE_B3F_ICE_HEAL = 1648, + EVENT_TEAM_ROCKET_BASE_B3F_LANCE_PASSWORDS = 1751, + EVENT_TEAM_ROCKET_BASE_B3F_PROTEIN = 1647, + EVENT_TEAM_ROCKET_BASE_POPULATION = 1754, + EVENT_TEAM_ROCKET_BASE_SECURITY_GRUNTS = 1753, + EVENT_TEAM_ROCKET_DISBANDED = 1889, + EVENT_TELEPORT_GUY = 1916, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_1 = 0, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_2 = 1, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_3 = 2, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_4 = 3, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_5 = 4, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_6 = 5, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_7 = 6, + EVENT_TEMPORARY_UNTIL_MAP_RELOAD_8 = 7, + EVENT_TIFFANY_ASKED_FOR_PHONE_NUMBER = 659, + EVENT_TIFFANY_READY_FOR_REMATCH = 660, + EVENT_TIN_TOWER_3F_FULL_HEAL = 1611, + EVENT_TIN_TOWER_4F_ESCAPE_ROPE = 1614, + EVENT_TIN_TOWER_4F_HIDDEN_MAX_POTION = 125, + EVENT_TIN_TOWER_4F_SUPER_POTION = 1613, + EVENT_TIN_TOWER_4F_ULTRA_BALL = 1612, + EVENT_TIN_TOWER_5F_HIDDEN_CARBOS = 127, + EVENT_TIN_TOWER_5F_HIDDEN_FULL_RESTORE = 126, + EVENT_TIN_TOWER_5F_RARE_CANDY = 1615, + EVENT_TIN_TOWER_7F_MAX_REVIVE = 1616, + EVENT_TIN_TOWER_8F_FULL_RESTORE = 1619, + EVENT_TIN_TOWER_8F_MAX_ELIXER = 1618, + EVENT_TIN_TOWER_8F_NUGGET = 1617, + EVENT_TIN_TOWER_ROOF_HO_OH = 1852, + EVENT_TODD_ASKED_FOR_PHONE_NUMBER = 637, + EVENT_TODD_READY_FOR_REMATCH = 638, + EVENT_TOGEPI_HATCHED = 84, + EVENT_TOHJO_FALLS_MOON_STONE = 1705, + EVENT_TOLD_ELM_ABOUT_TOGEPI_OVER_THE_PHONE = 119, + EVENT_TOTODILE_POKEBALL_IN_ELMS_LAB = 1601, + EVENT_TRAINERS_IN_CERULEAN_GYM = 1903, + EVENT_TURNED_OFF_SECURITY_CAMERAS = 739, + EVENT_UNCOVERED_STAIRCASE_IN_MAHOGANY_MART = 738, + EVENT_UNDERGROUND_PATH_HIDDEN_FULL_RESTORE = 229, + EVENT_UNDERGROUND_PATH_HIDDEN_X_SPECIAL = 230, + EVENT_UNION_CAVE_1F_AWAKENING = 1629, + EVENT_UNION_CAVE_1F_GREAT_BALL = 1626, + EVENT_UNION_CAVE_1F_POTION = 1627, + EVENT_UNION_CAVE_1F_X_ATTACK = 1628, + EVENT_UNION_CAVE_B1F_TM_SWIFT = 1630, + EVENT_UNION_CAVE_B1F_X_DEFEND = 1631, + EVENT_UNION_CAVE_B2F_ELIXER = 1632, + EVENT_UNION_CAVE_B2F_HYPER_POTION = 1633, + EVENT_UNION_CAVE_B2F_LAPRAS = 1888, + EVENT_USED_BASEMENT_KEY = 73, + EVENT_USED_THE_CARD_KEY_IN_THE_RADIO_TOWER = 37, + EVENT_VANCE_ASKED_FOR_PHONE_NUMBER = 661, + EVENT_VANCE_READY_FOR_REMATCH = 662, + EVENT_VERMILION_CITY_HIDDEN_FULL_HEAL = 252, + EVENT_VERMILION_CITY_SNORLAX = 1904, + EVENT_VERMILION_FAN_CLUB_DOLL = 1908, + EVENT_VERMILION_PORT_HIDDEN_IRON = 235, + EVENT_VERMILION_PORT_SAILOR_AT_GANGWAY = 1835, + EVENT_VICTORY_ROAD_FULL_HEAL = 1702, + EVENT_VICTORY_ROAD_FULL_RESTORE = 1701, + EVENT_VICTORY_ROAD_HIDDEN_FULL_HEAL = 159, + EVENT_VICTORY_ROAD_HIDDEN_MAX_POTION = 158, + EVENT_VICTORY_ROAD_MAX_REVIVE = 1700, + EVENT_VICTORY_ROAD_TM_EARTHQUAKE = 1699, + EVENT_VICTORY_ROAD_X_SPECIAL = 1703, + EVENT_VIOLET_CITY_EARL = 1738, + EVENT_VIOLET_CITY_HIDDEN_HYPER_POTION = 176, + EVENT_VIOLET_CITY_PP_UP = 1603, + EVENT_VIOLET_CITY_RARE_CANDY = 1604, + EVENT_VIRIDIAN_GYM_BLUE = 1910, + EVENT_WADE_ASKED_FOR_PHONE_NUMBER = 629, + EVENT_WADE_READY_FOR_REMATCH = 630, + EVENT_WARPED_FROM_ROUTE_35_NATIONAL_PARK_GATE = 722, + EVENT_WHIRL_ISLAND_B1F_CALCIUM = 1683, + EVENT_WHIRL_ISLAND_B1F_CARBOS = 1682, + EVENT_WHIRL_ISLAND_B1F_ESCAPE_ROPE = 1685, + EVENT_WHIRL_ISLAND_B1F_FULL_RESTORE = 1681, + EVENT_WHIRL_ISLAND_B1F_HIDDEN_FULL_RESTORE = 153, + EVENT_WHIRL_ISLAND_B1F_HIDDEN_RARE_CANDY = 151, + EVENT_WHIRL_ISLAND_B1F_HIDDEN_ULTRA_BALL = 152, + EVENT_WHIRL_ISLAND_B1F_NUGGET = 1684, + EVENT_WHIRL_ISLAND_B2F_FULL_RESTORE = 1686, + EVENT_WHIRL_ISLAND_B2F_MAX_ELIXER = 1688, + EVENT_WHIRL_ISLAND_B2F_MAX_REVIVE = 1687, + EVENT_WHIRL_ISLAND_LUGIA_CHAMBER_LUGIA = 1853, + EVENT_WHIRL_ISLAND_NE_ULTRA_BALL = 1679, + EVENT_WHIRL_ISLAND_SW_GUARD_SPEC = 1680, + EVENT_WILLS_ROOM_ENTRANCE_CLOSED = 777, + EVENT_WILLS_ROOM_EXIT_OPEN = 778, + EVENT_WILTON_ASKED_FOR_PHONE_NUMBER = 663, + EVENT_WILTON_READY_FOR_REMATCH = 664, + }, + engine = { + ENGINE_ALL_FRUIT_TREES = 83, + ENGINE_ALWAYS_ON_BIKE = 24, + ENGINE_BIKE_SHOP_CALL_ENABLED = 19, + ENGINE_BOULDERBADGE = 34, + ENGINE_BUG_CONTEST_TIMER = 16, + ENGINE_CASCADEBADGE = 35, + ENGINE_CAUGHT_POKERUS = 13, + ENGINE_CREDITS_SKIP = 15, + ENGINE_DAILY_BUG_CONTEST = 80, + ENGINE_DAISYS_GROOMING = 91, + ENGINE_DAY_CARE_LADY_HAS_MON = 7, + ENGINE_DAY_CARE_MAN_HAS_EGG = 5, + ENGINE_DAY_CARE_MAN_HAS_MON = 6, + ENGINE_DOWNHILL = 25, + ENGINE_EARTHBADGE = 41, + ENGINE_EXPN_CARD = 3, + ENGINE_FLYPOINT_AZALEA = 67, + ENGINE_FLYPOINT_BLACKTHORN = 74, + ENGINE_FLYPOINT_CELADON = 60, + ENGINE_FLYPOINT_CERULEAN = 55, + ENGINE_FLYPOINT_CHERRYGROVE = 65, + ENGINE_FLYPOINT_CIANWOOD = 68, + ENGINE_FLYPOINT_CINNABAR = 62, + ENGINE_FLYPOINT_DEBUG = 51, + ENGINE_FLYPOINT_ECRUTEAK = 71, + ENGINE_FLYPOINT_FUCHSIA = 61, + ENGINE_FLYPOINT_GOLDENROD = 69, + ENGINE_FLYPOINT_INDIGO_PLATEAU = 63, + ENGINE_FLYPOINT_LAKE_OF_RAGE = 73, + ENGINE_FLYPOINT_LAVENDER = 58, + ENGINE_FLYPOINT_MAHOGANY = 72, + ENGINE_FLYPOINT_NEW_BARK = 64, + ENGINE_FLYPOINT_OLIVINE = 70, + ENGINE_FLYPOINT_PALLET = 52, + ENGINE_FLYPOINT_PEWTER = 54, + ENGINE_FLYPOINT_PLAYERS_HOUSE = 50, + ENGINE_FLYPOINT_ROCK_TUNNEL = 56, + ENGINE_FLYPOINT_SAFFRON = 59, + ENGINE_FLYPOINT_SILVER_CAVE = 75, + ENGINE_FLYPOINT_UNUSED = 76, + ENGINE_FLYPOINT_VERMILION = 57, + ENGINE_FLYPOINT_VIOLET = 66, + ENGINE_FLYPOINT_VIRIDIAN = 53, + ENGINE_FOGBADGE = 29, + ENGINE_FOUGHT_IN_TRAINER_HALL_TODAY = 86, + ENGINE_GLACIERBADGE = 32, + ENGINE_GOLDENROD_DEPT_STORE_TM27_RETURN = 90, + ENGINE_GOLDENROD_UNDERGROUND_GOT_HAIRCUT = 89, + ENGINE_GOLDENROD_UNDERGROUND_MERCHANT_CLOSED = 85, + ENGINE_GOT_SHUCKIE_TODAY = 84, + ENGINE_HIVEBADGE = 27, + ENGINE_INDIGO_PLATEAU_RIVAL_FIGHT = 92, + ENGINE_KURT_MAKING_BALLS = 79, + ENGINE_LUCKY_NUMBER_SHOW = 77, + ENGINE_MAP_CARD = 1, + ENGINE_MARSHBADGE = 39, + ENGINE_MINERALBADGE = 30, + ENGINE_MOM_ACTIVE = 9, + ENGINE_MOM_SAVING_MONEY = 8, + ENGINE_MT_MOON_SQUARE_CLEFAIRY = 87, + ENGINE_PHONE_CARD = 2, + ENGINE_PLAINBADGE = 28, + ENGINE_POKEDEX = 11, + ENGINE_POKEGEAR = 4, + ENGINE_RADIO_CARD = 0, + ENGINE_RAINBOWBADGE = 37, + ENGINE_REACHED_GOLDENROD = 21, + ENGINE_RISINGBADGE = 33, + ENGINE_ROCKETS_IN_MAHOGANY = 22, + ENGINE_ROCKETS_IN_RADIO_TOWER = 18, + ENGINE_ROCKET_SIGNAL_ON_CH20 = 14, + ENGINE_SAFARI_ZONE = 17, + ENGINE_SOULBADGE = 38, + ENGINE_STORMBADGE = 31, + ENGINE_STRENGTH_ACTIVE = 23, + ENGINE_SWARM = 81, + ENGINE_THUNDERBADGE = 36, + ENGINE_TIME_CAPSULE = 82, + ENGINE_UNION_CAVE_LAPRAS = 88, + ENGINE_UNLOCKED_UNOWNS_A_TO_K = 42, + ENGINE_UNLOCKED_UNOWNS_L_TO_R = 43, + ENGINE_UNLOCKED_UNOWNS_S_TO_W = 44, + ENGINE_UNLOCKED_UNOWNS_UNUSED_4 = 46, + ENGINE_UNLOCKED_UNOWNS_UNUSED_5 = 47, + ENGINE_UNLOCKED_UNOWNS_UNUSED_6 = 48, + ENGINE_UNLOCKED_UNOWNS_UNUSED_7 = 49, + ENGINE_UNLOCKED_UNOWNS_X_TO_Z = 45, + ENGINE_UNOWN_DEX = 12, + ENGINE_UNUSED_STATUSFLAGS2_3 = 78, + ENGINE_UNUSED_STATUSFLAGS2_5 = 20, + ENGINE_UNUSED_TWO_DAY_TIMER_ON = 10, + ENGINE_VOLCANOBADGE = 40, + ENGINE_ZEPHYRBADGE = 26, + }, +} diff --git a/src/import/LauncherView.lua b/src/import/LauncherView.lua index 725c013c..05f66799 100644 --- a/src/import/LauncherView.lua +++ b/src/import/LauncherView.lua @@ -299,6 +299,11 @@ end local CART_DRAG_SLOP = 8 local TAU = math.pi * 2 +-- The 3D mesh is inset inside the hit box so yaw/pitch and the 1.05 hover +-- scale cannot climb into the title row (or the gear) on desktop, high-DPI, +-- or a portrait phone. Fraction of the shorter side, with a pixel floor. +local CART_MESH_PAD = 0.07 +local CART_MESH_PAD_MIN = 8 local function cartridgeState(imp, version) imp._cartridge = imp._cartridge or {} @@ -543,8 +548,11 @@ local function cartridgeButton(imp, x, y, w, h, key, version, gameName, action) Theme.A.focus, 2, Theme.cardRadius() + 2) end - local halfW, halfH = w / 2, h / 2 - local depth = math.max(8, w * 0.14) + local meshPad = math.max(CART_MESH_PAD_MIN, + math.floor(math.min(w, h) * CART_MESH_PAD)) + local halfW = math.max(1, w / 2 - meshPad) + local halfH = math.max(1, h / 2 - meshPad) + local depth = math.max(8, (halfW * 2) * 0.14) local project = function(px, py, pz) return cartProject(cx + pressX, cy + pressY, yaw, pitch, px * pressedScale, py * pressedScale, pz * pressedScale) @@ -638,14 +646,8 @@ local function modStatusColor(status) end -- MODS panel scope row: which game the list is answering for, plus dedicated Profile control (cycle + gear). -local function buildModScopeRow(imp, x, y, w, m) +local function modScopeOptions(imp) local GameVersion = require("src.core.GameVersion") - local LauncherMods = require("src.mods.LauncherMods") - local h = math.max(Kit.tapMin(), math.floor(26 * m.s)) - local gap = math.floor(6 * m.s) - local label = Strings("Show for:") - Kit.text("small", label, x, y + (h - Kit.textHeight("small")) / 2, PAL.muted) - local cx = x + Kit.textWidth("small", label) + math.floor(10 * m.s) local options = { { id = nil, label = Strings("All games") } } for _, version in ipairs(GameVersion.ORDER) do if imp.ready and imp.ready[version] then @@ -653,6 +655,33 @@ local function buildModScopeRow(imp, x, y, w, m) { id = version, label = GameVersion.info(version).label } end end + return options +end + +local function modScopeCurrentLabel(imp, options) + for _, opt in ipairs(options) do + if imp.modScope == opt.id then return opt.label end + end + return options[1] and options[1].label or Strings("All games") +end + +local function modScopeChipsWidth(options, gap, m) + local need = 0 + for i, opt in ipairs(options) do + need = need + Kit.textWidth("micro", opt.label) + math.floor(18 * m.s) + if i < #options then need = need + gap end + end + return need +end + +local function buildModScopeRow(imp, x, y, w, m) + local LauncherMods = require("src.mods.LauncherMods") + local h = math.max(Kit.tapMin(), math.floor(26 * m.s)) + local gap = math.floor(6 * m.s) + local label = Strings("Show for:") + Kit.text("small", label, x, y + (h - Kit.textHeight("small")) / 2, PAL.muted) + local cx = x + Kit.textWidth("small", label) + math.floor(10 * m.s) + local options = modScopeOptions(imp) -- Dedicated Profile control section (cycle button + gear icon button) on right side of Scope Bar local _, activeProf = LauncherMods.getProfiles() @@ -690,9 +719,12 @@ local function buildModScopeRow(imp, x, y, w, m) }) if #options >= 2 then - for _, opt in ipairs(options) do - local cw = Kit.textWidth("micro", opt.label) + math.floor(18 * m.s) - if cx + cw <= profX - gap then + local avail = profX - gap - cx + -- Chips stay when they all fit; otherwise they used to be skipped and + -- vanish off the portrait edge. Collapse to one menu in that case only. + if modScopeChipsWidth(options, gap, m) <= avail then + for _, opt in ipairs(options) do + local cw = Kit.textWidth("micro", opt.label) + math.floor(18 * m.s) if Kit.chip(cx, y, cw, h, opt.label, imp.modScope == opt.id, PAL.lineStrong, "mod-scope-" .. tostring(opt.id or "all")) then local want = opt.id @@ -701,6 +733,15 @@ local function buildModScopeRow(imp, x, y, w, m) end cx = cx + cw + gap end + elseif avail > 0 then + local shown = Kit.ellipsize("micro", modScopeCurrentLabel(imp, options), + math.max(0, avail - math.floor(18 * m.s))) + local cw = math.min(avail, + Kit.textWidth("micro", shown) + math.floor(18 * m.s)) + if Kit.chip(cx, y, cw, h, shown, true, PAL.lineStrong, "mod-scope-menu") then + queueAction(imp, "mod-scope-menu", + function() imp._modScopePopup = true end) + end end end return h + math.floor(8 * m.s) @@ -740,8 +781,8 @@ local function setPage(imp, key, v) imp._pages[key] = v end --- A hand-drawn X, for the same reason drawCheck exists below: the UI font has --- no guaranteed glyph, and the launcher ships no icon asset for it. +-- A hand-drawn X / check: the UI font has no guaranteed glyph for either, +-- and the launcher ships no icon asset for them. local function drawCross(x, y, size, color) love.graphics.push("all") love.graphics.setColor(color) @@ -752,6 +793,18 @@ local function drawCross(x, y, size, color) love.graphics.pop() end +local function drawCheck(x, y, size, color) + love.graphics.push("all") + love.graphics.setColor(color) + love.graphics.setLineWidth(math.max(2.2, size * 0.17)) + love.graphics.setLineJoin("bevel") + love.graphics.line( + x + size * 0.02, y + size * 0.52, + x + size * 0.38, y + size * 0.80, + x + size * 1.015, y + size * 0.18) + love.graphics.pop() +end + -- ------------------------------------------------------------- header -- Rail, logo row (settings and quit on the right), tab bar. -- Returns the y at which content may start. Its vertical arithmetic is @@ -1316,20 +1369,32 @@ local function buildGamePanel(imp, x, y, w, availH, m, version) or tostring(version) local ready = (not locked) and imp.ready[version] or false - -- title + status tag + -- title + status tag. Ready is a check chip (the font has no tick glyph); + -- missing ROM stays a yellow "ROM REQUIRED" tag so it still reads as an action. local titleH = Kit.textHeight("title") Kit.text("title", Kit.ellipsize("title", gameName, w * 0.6), x, y, PAL.heading) - local tagText, tagCol - if ready then tagText, tagCol = Strings("GOOD TO GO"), PAL.green - elseif imp.baseRoms and imp.baseRoms[version] then - tagText, tagCol = Strings("ROM FOUND"), PAL.green - elseif locked then tagText, tagCol = Strings("COMING SOON"), PAL.steel - else tagText, tagCol = Strings("ROM REQUIRED"), PAL.yellow end - local tagW = Kit.textWidth("micro", tagText) + math.floor(18 * m.s) local tagH = Kit.textHeight("micro") + math.floor(10 * m.s) local tagX = x + Kit.textWidth("title", Kit.ellipsize("title", gameName, w * 0.6)) + math.floor(12 * m.s) - Kit.tag(tagX, y + (titleH - tagH) / 2, tagW, tagH, tagText, tagCol) + local tagY = y + (titleH - tagH) / 2 + local tagW, tagCol + if ready then + tagCol = PAL.green + tagW = tagH + if love.graphics then + Theme.strokeRounded(tagX, tagY, tagW, tagH, tagCol, 0.7, 1) + local ck = math.floor(tagH * 0.55) + drawCheck(tagX + (tagW - ck) / 2, tagY + (tagH - ck) / 2, ck, tagCol) + end + else + local tagText + if imp.baseRoms and imp.baseRoms[version] then + tagText, tagCol = Strings("ROM FOUND"), PAL.green + elseif locked then tagText, tagCol = Strings("COMING SOON"), PAL.steel + else tagText, tagCol = Strings("ROM REQUIRED"), PAL.yellow end + tagW = Kit.textWidth("micro", tagText) + math.floor(18 * m.s) + Kit.tag(tagX, tagY, tagW, tagH, tagText, tagCol) + end if ready then local hint = Strings("(PRESS THE CART TO PLAY)") local hintX = tagX + tagW + math.floor(10 * m.s) @@ -1337,8 +1402,11 @@ local function buildGamePanel(imp, x, y, w, availH, m, version) Kit.text("micro", Kit.ellipsize("micro", hint, hintW), hintX, y + (titleH - Kit.textHeight("micro")) / 2, PAL.heading) end - local cy = y + titleH + math.floor(12 * m.s) - local remaining = availH - (titleH + math.floor(12 * m.s)) + -- Extra gap under the title when the cart is showing: 12px left the 3D + -- shell sitting on the hairline. Scaled, and still small on a phone. + local afterTitle = math.floor((ready and 22 or 12) * m.s) + local cy = y + titleH + afterTitle + local remaining = availH - (titleH + afterTitle) local gap = m.gap local lx, lw, rx2, rw @@ -1446,20 +1514,6 @@ local function currentSort(imp) return sortKey end --- A hand-drawn check mark: the UI font has no guaranteed glyph for one, and --- a tofu box on the "you already have this" signal would be worse than none. -local function drawCheck(x, y, size, color) - love.graphics.push("all") - love.graphics.setColor(color) - love.graphics.setLineWidth(math.max(2.2, size * 0.17)) - love.graphics.setLineJoin("bevel") - love.graphics.line( - x + size * 0.02, y + size * 0.52, - x + size * 0.38, y + size * 0.80, - x + size * 1.015, y + size * 0.18) - love.graphics.pop() -end - -- One compact coloured checkbox for each game. The cartridge colour carries -- the game identity even when the row is narrow. local function modGameCheckbox(x, y, size, checked, game, id) @@ -1978,21 +2032,47 @@ local TRUST_WARNING = "if you did not get this from bryanthaboi's github " .. "it might have been tampered with. go to the discord to verify " .. COMMUNITY_URL .. " (or click the logo above)" +-- Mark + optional updater + Patch notes. Chips match the mark's 22px +-- height so they do not read as bigger than the logo; the row can still +-- be tapMin tall for spacing. On a phone the notes chip drops onto a +-- second row rather than overflowing the mark. +local function footerLayout(imp, m, markW) + local markH = math.floor(22 * m.s) + local rowH = math.max(markH, Kit.tapMin()) + local notesLabel = Strings("Patch notes") + -- Tight chip, still enough for Kit.button's labelInset so the words survive. + local chipPad = math.floor(24 * m.s) + local nw = Kit.textWidth("micro", notesLabel) + chipPad + local upStatus, upLabel, upAction, upGlow = LauncherView._updateControl(imp) + local uw = upStatus + and (Kit.textWidth("micro", upLabel) + chipPad) or 0 + local gap = math.floor(10 * m.s) + local inner = m.w - 2 * m.pad + local topW = (markW or 0) + (upStatus and (gap + uw) or 0) + gap + nw + return { + rowH = rowH, chipH = markH, gap = gap, + notesLabel = notesLabel, nw = nw, + upStatus = upStatus, upLabel = upLabel, upAction = upAction, upGlow = upGlow, + uw = uw, wrap = topW > inner, + } +end + -- Pinned to the bottom of the window; returns the y it starts at, so the -- panels above know how much room they have. -- Deliberately compact: at a large UI scale the footer is pure overhead -- competing with the panel for a short window's height, so the mark and the -- link share one line and the trust warning is capped at a single line. local function footerHeight(imp, m) - -- Top pad + mark/update row + gap + the FULL wrapped trust message + - -- bottom pad. The message wraps to as many lines as it needs: truncating - -- a trust warning defeats its purpose, and the bottom pad is not optional - -- either (without it the last line sits flush on the window edge and its - -- lower half clips off). The row is tapMin tall because the small update - -- button rides beside the mark. - local rowH = math.max(math.floor(22 * m.s), Kit.tapMin()) - return math.floor(8 * m.s) + rowH + math.floor(6 * m.s) - + Kit.wrapHeight("micro", TRUST_WARNING, m.contentW) + -- Top pad + mark/update row + optional notes wrap row + gap + the FULL + -- wrapped trust message + bottom pad. The message wraps to as many lines + -- as it needs: truncating a trust warning defeats its purpose, and the + -- bottom pad is not optional either (without it the last line sits flush + -- on the window edge and its lower half clips off). The row is tapMin + -- tall because the small update button rides beside the mark. + local f = footerLayout(imp, m, math.floor(130 * m.s)) + local h = math.floor(8 * m.s) + f.rowH + math.floor(6 * m.s) + if f.wrap then h = h + f.chipH + math.floor(6 * m.s) end + return h + Kit.wrapHeight("micro", TRUST_WARNING, m.contentW) + math.floor(8 * m.s) end @@ -2009,19 +2089,17 @@ local function buildFooter(imp, m, y) local bw, bh = imp.bcg:getDimensions() local scale = math.min((130 * m.s) / bw, (22 * m.s) / bh) local dw, dh = bw * scale, bh * scale - local rowH = math.max(math.floor(22 * m.s), Kit.tapMin()) - -- The mark and the small self-update control share the row, centred as a - -- group. The updater moved down here from the header, where it overlapped - -- the wordmark on a phone; small on purpose, its glow still carries the - -- "act on me" signal. - local upStatus, upLabel, upAction, upGlow = LauncherView._updateControl(imp) - -- Kit.button insets its label 16*scale per side, so the width must budget - -- more than that or the label ellipsizes ("Check for updat..."). - local uw = upStatus - and (Kit.textWidth("micro", upLabel) + math.floor(36 * m.s)) or 0 - local groupW = dw + (upStatus and (math.floor(10 * m.s) + uw) or 0) - local bx = m.x + math.floor((m.w - groupW) / 2) + local f = footerLayout(imp, m, dw) + local rowH, gap, chipH = f.rowH, f.gap, f.chipH + -- The mark, the small self-update control, and Patch notes share the row, + -- centred as a group. The updater moved down here from the header, where + -- it overlapped the wordmark on a phone; small on purpose, its glow still + -- carries the "act on me" signal. Notes wrap under the mark on a phone. + local topW = dw + (f.upStatus and (gap + f.uw) or 0) + if not f.wrap then topW = topW + gap + f.nw end + local bx = m.x + math.floor((m.w - topW) / 2) local my = cy + math.floor((rowH - dh) / 2) + local chipY = cy + math.floor((rowH - chipH) / 2) local hot = Kit.hover(bx, my, dw, dh) love.graphics.setShader(imp.invertShader) love.graphics.setColor(1, 1, 1, hot and 1 or 0.85) @@ -2031,14 +2109,30 @@ local function buildFooter(imp, m, y) if Kit.press(bx, my, dw, dh) then queueAction(imp, "bcg", function() love.system.openURL(COMMUNITY_URL) end) end - if upStatus then - btn(imp, bx + dw + math.floor(10 * m.s), cy, uw, rowH, "updater", - upLabel, { - kind = upGlow and "warn" or "ghost", font = "micro", - glow = upGlow, action = upAction, + local cx = bx + dw + if f.upStatus then + cx = cx + gap + btn(imp, cx, chipY, f.uw, chipH, "updater", + f.upLabel, { + kind = f.upGlow and "warn" or "ghost", font = "micro", + glow = f.upGlow, action = f.upAction, }) + cx = cx + f.uw + end + local function notesBtn(x, y) + btn(imp, x, y, f.nw, chipH, "patch-notes", f.notesLabel, { + kind = "ghost", font = "micro", + action = function() imp._appPatchNotes = true end, + }) + end + if f.wrap then + cy = cy + rowH + gap + notesBtn(m.x + math.floor((m.w - f.nw) / 2), cy) + cy = cy + chipH + math.floor(6 * m.s) + else + notesBtn(cx + gap, chipY) + cy = cy + rowH + math.floor(6 * m.s) end - cy = cy + rowH + math.floor(6 * m.s) -- The trust message wraps in full, each line centred under the mark, and -- the URL inside it IS the link -- no separate link floating elsewhere. -- font:getWrap never splits an unspaced word, so the URL stays whole on @@ -2543,6 +2637,34 @@ local function buildSortModal(imp, m) action = function() imp._sortPopup = nil end }) end +-- Game-scope chooser used when the Show-for chips cannot all fit on the +-- mods toolbar (portrait phones). Same options as the chip row. +local function buildModScopeModal(imp, m) + local options = modScopeOptions(imp) + local pad = math.floor(18 * m.s) + local w = math.floor(360 * m.s) + local gap = math.floor(8 * m.s) + local h = pad + Kit.textHeight("button") + math.floor(12 * m.s) + + #options * (m.btnH + gap) + m.btnH + pad + local px, py, pw = modalPanel(m, w, h) + local cy = py + pad + Kit.text("button", Strings("Show for"), px + pad, cy, PAL.heading) + cy = cy + Kit.textHeight("button") + math.floor(12 * m.s) + for _, opt in ipairs(options) do + local key = tostring(opt.id or "all") + btn(imp, px + pad, cy, pw - 2 * pad, m.btnH, "scopepop-" .. key, opt.label, { + kind = (imp.modScope == opt.id) and "primary" or "ghost", font = "small", + action = function() + imp:_setModScope(opt.id) + imp._modScopePopup = nil + end }) + cy = cy + m.btnH + gap + end + btn(imp, px + pad, cy, pw - 2 * pad, m.btnH, "scopepop-close", + Strings("Close"), { font = "small", + action = function() imp._modScopePopup = nil end }) +end + -- Category filter for FIND MODS. Two columns, because an index can list -- enough categories to overflow a single stacked column on a short window. local function buildFilterModal(imp, m) @@ -3253,8 +3375,9 @@ end local function modalUp(imp) return (imp._settingsText or imp._settings or imp._rename or imp._indexPrompt or imp._modConfirm or imp._modReleaseNotes + or imp._appPatchNotes or imp._findDetails or imp._modVersions or imp._modDepResolver or imp._sortPopup - or imp._filterPopup or imp._indexManage or imp._modActions + or imp._filterPopup or imp._modScopePopup or imp._indexManage or imp._modActions or imp._modHeaderActionsPopup or imp._profilesPopup or imp._singleProfileActions or imp._profileSavePrompt or imp._profileRenamePrompt or imp._findEntry or imp._gameManage) ~= nil end @@ -3351,6 +3474,20 @@ local function buildModals(imp, m) return true end if imp._modConfirm then buildConfirmModal(imp, m) return true end + if imp._appPatchNotes then + local PatchNotes = require("src.update.PatchNotes") + local ModUpdate = require("src.mods.ModUpdate") + local raw, ver = PatchNotes.body(imp.Check) + local body = ModUpdate.cleanBody(raw or "", 0) + if body == "" then body = Strings("(No patch notes.)") end + local title = Strings("Patch notes") + if ver and ver ~= "" then + title = title .. " v" .. tostring(ver) + end + buildTextModal(imp, m, "patch-notes-modal", title, body, + function() imp._appPatchNotes = nil end) + return true + end if imp._modReleaseNotes then local ModUpdate = require("src.mods.ModUpdate") local n = imp._modReleaseNotes @@ -3380,6 +3517,7 @@ local function buildModals(imp, m) if imp._profilesPopup then buildProfilesModal(imp, m) return true end if imp._modHeaderActionsPopup then buildModHeaderActionsModal(imp, m) return true end if imp._sortPopup then buildSortModal(imp, m) return true end + if imp._modScopePopup then buildModScopeModal(imp, m) return true end if imp._filterPopup then buildFilterModal(imp, m) return true end if imp._indexManage then buildIndexesModal(imp, m) return true end if imp._modActions then buildModActionsModal(imp, m) return true end diff --git a/src/import/RomImporter.lua b/src/import/RomImporter.lua index 5311ee1e..4666f226 100644 --- a/src/import/RomImporter.lua +++ b/src/import/RomImporter.lua @@ -2820,7 +2820,7 @@ function RomImporter:keypressed(key) return end if self._modConfirm or self._modVersions or self._modReleaseNotes - or self._findDetails then + or self._findDetails or self._appPatchNotes then -- Focus navigation belongs to the visible modal as well as the launcher -- beneath it. Route arrows and an already-armed confirm before this guard -- returns; unarmed Enter still falls through to the modal guard. Keep this @@ -2833,6 +2833,8 @@ function RomImporter:keypressed(key) self._findDetails = nil elseif self._modReleaseNotes then self._modReleaseNotes = nil + elseif self._appPatchNotes then + self._appPatchNotes = nil else self._modConfirm = nil self._modVersions = nil diff --git a/src/ui/gen2/HallOfFame.lua b/src/ui/gen2/HallOfFame.lua index 6f8e3947..8d593003 100644 --- a/src/ui/gen2/HallOfFame.lua +++ b/src/ui/gen2/HallOfFame.lua @@ -154,7 +154,7 @@ function HallOfFame.monPlacements(mon, def) put(out, genderGlyph(mon.gender), 18, 13) -- (8,14) is a bare '/', so the nickname starts at (9,14). put(out, "/", 8, 14) - put(out, mon.nickname or mon.species, 9, 14) + put(out, mon.nickname or mon.name or mon.species, 9, 14) put(out, levelText(mon.level), 1, 16) end -- '' '№' '/' at (7,16), (8,16), (9,16), then five digits at (10,16). diff --git a/src/ui/gen2/PackMenu.lua b/src/ui/gen2/PackMenu.lua index e4c8d3a4..c5f50485 100644 --- a/src/ui/gen2/PackMenu.lua +++ b/src/ui/gen2/PackMenu.lua @@ -630,7 +630,7 @@ function PackMenu:openTeachParty(row) if not allowed then if game.say then game:say(("%s can't learn %s!"):format( - mon.nickname or mon.species or "?", moveName)) + require("src.battle.gen2.Mon").displayName(mon), moveName)) end return end @@ -638,7 +638,7 @@ function PackMenu:openTeachParty(row) if move.id == moveId then if game.say then game:say(("%s already knows %s!"):format( - mon.nickname or mon.species or "?", moveName)) + require("src.battle.gen2.Mon").displayName(mon), moveName)) end return end diff --git a/src/update/Check.lua b/src/update/Check.lua index c485336c..728789aa 100644 --- a/src/update/Check.lua +++ b/src/update/Check.lua @@ -71,6 +71,9 @@ function Check.parseRelease(jsonText, Json) payloadName = payloadName, payload = Check.pickAsset(doc.assets, payloadName), sums = Check.pickAsset(doc.assets, "sha256sums.txt"), + -- GitHub release body: already fetched with the update check, shown by + -- the launcher's Patch notes footer button. + notes = type(doc.body) == "string" and doc.body or "", } end @@ -135,6 +138,10 @@ local function drain() if stateCh then local msg = stateCh:pop() while msg do + if type(msg) == "table" and type(msg.notes) ~= "string" + and type(cache.notes) == "string" then + msg.notes = cache.notes + end cache = msg msg = stateCh:pop() end @@ -158,11 +165,11 @@ function Check.start() return end requested = true - cache = { status = "checking" } + cache = { status = "checking", notes = cache.notes, latest = cache.latest } cmdCh:push({ cmd = "check" }) end --- Current snapshot: { status, latest, progress, error }. status is one of +-- Current snapshot: { status, latest, progress, error, notes }. status is one of -- idle | checking | uptodate | available | downloading | ready | needs_full | error. function Check.state() drain() @@ -171,6 +178,7 @@ function Check.state() latest = cache.latest, progress = cache.progress, error = cache.error, + notes = cache.notes, } end @@ -180,7 +188,8 @@ function Check.download() drain() if not cmdCh then return end if cache.status ~= "available" then return end - cache = { status = "downloading", latest = cache.latest, progress = 0 } + cache = { status = "downloading", latest = cache.latest, progress = 0, + notes = cache.notes } cmdCh:push({ cmd = "download" }) end diff --git a/src/update/PatchNotes.lua b/src/update/PatchNotes.lua new file mode 100644 index 00000000..3288c4de --- /dev/null +++ b/src/update/PatchNotes.lua @@ -0,0 +1,112 @@ +-- Resolve the game's patch notes for the launcher footer modal. +-- +-- Sources, first hit wins: +-- 1. The GitHub body the self-updater already fetched (Check.parseRelease) +-- 2. PATCH_NOTES.md, if a build packed one +-- 3. mobile/ios/app-repo.json -- CI copies each release's notes into +-- versions[].localizedDescription, so a checkout already has them +-- on disk even when the updater has not run + +local Json = require("src.link.Json") + +local PatchNotes = {} + +PatchNotes.FILES = { + "PATCH_NOTES.md", + "assets/PATCH_NOTES.md", +} + +PatchNotes.REPO_FILES = { + "mobile/ios/app-repo.json", +} + +local function nonempty(s) + return type(s) == "string" and s:find("%S") +end + +function PatchNotes.fromCheck(Check) + if not (Check and Check.state) then return nil, nil end + local ok, st = pcall(Check.state) + st = (ok and type(st) == "table") and st or nil + if not st then return nil, nil end + if nonempty(st.notes) then + return st.notes, st.latest + end + return nil, st.latest +end + +local function readPath(path) + local fs = love and love.filesystem + if fs and fs.read then + local ok, text = pcall(fs.read, path) + if ok and nonempty(text) then return text end + end + local f = io.open(path, "rb") + if not f then return nil end + local text = f:read("*a") + f:close() + return nonempty(text) and text or nil +end + +function PatchNotes.fromFile() + for _, path in ipairs(PatchNotes.FILES) do + local text = readPath(path) + if text then return text end + end + return nil +end + +-- Newest-first list of { version, notes } from the iOS sidecar. +function PatchNotes.parseRepo(jsonText) + local doc = Json.decode(jsonText) + if type(doc) ~= "table" or type(doc.apps) ~= "table" then return {} end + local out = {} + for _, app in ipairs(doc.apps) do + if type(app) == "table" and type(app.versions) == "table" then + for _, row in ipairs(app.versions) do + if type(row) == "table" and nonempty(row.localizedDescription) then + out[#out + 1] = { + version = row.version, + notes = row.localizedDescription, + } + end + end + end + end + return out +end + +function PatchNotes.fromRepo(engine) + for _, path in ipairs(PatchNotes.REPO_FILES) do + local text = readPath(path) + if text then + local list = PatchNotes.parseRepo(text) + if #list == 0 then return nil, nil end + if engine and engine ~= "0.0.0-dev" then + for _, row in ipairs(list) do + if row.version == engine then + return row.notes, row.version + end + end + end + return list[1].notes, list[1].version + end + end + return nil, nil +end + +function PatchNotes.body(Check) + local notes, ver = PatchNotes.fromCheck(Check) + if notes then return notes, ver end + notes = PatchNotes.fromFile() + if notes then return notes, ver end + local Version = require("src.core.Version") + local engine = (Version and Version.engine) or "?" + notes, ver = PatchNotes.fromRepo(engine) + if notes then return notes, ver end + return "No patch notes loaded yet for gen1recomp v" .. engine .. ".\n\n" + .. "They appear here after the launcher checks GitHub for the latest " + .. "release.", engine +end + +return PatchNotes diff --git a/src/update/check_worker.lua b/src/update/check_worker.lua index 0efc7ada..d60a349b 100644 --- a/src/update/check_worker.lua +++ b/src/update/check_worker.lua @@ -45,7 +45,12 @@ local Boot = loadModule("src/update/Boot.lua") local cmdCh = love.thread.getChannel("update_check_cmd") local stateCh = love.thread.getChannel("update_check_state") -local function post(t) stateCh:push(t) end +local function post(t) + if pending and type(t) == "table" and t.notes == nil then + t.notes = pending.notes + end + stateCh:push(t) +end local osName = (love.system and love.system.getOS and love.system.getOS()) or "" local isWindows = osName == "Windows" diff --git a/src/world/gen2/MapPreview.lua b/src/world/gen2/MapPreview.lua new file mode 100644 index 00000000..27847d45 --- /dev/null +++ b/src/world/gen2/MapPreview.lua @@ -0,0 +1,186 @@ +-- Bake a Gen 2 map canvas without a World instance. The save editor's map +-- tab uses this so Gold rooms draw the same tiles the overworld would, +-- instead of the checkerboard fallback Map2 has no renderer for. + +local Assets = require("src.render.Assets") +local BorderFill = require("src.world.gen2.BorderFill") +local GbcPalette = require("src.render.GbcPalette") +local Palettes = require("src.world.gen2.Palettes") + +local MapPreview = {} + +local ROOF_TILESETS = { + TILESET_JOHTO = true, + TILESET_JOHTO_MODERN = true, + TILESET_KANTO = true, +} + +local function applyRoofOverlay(atlasPath, roofPath, tilesPerRow) + local atlasData = love.image.newImageData(Assets.resolve(atlasPath)) + local roofData = love.image.newImageData(Assets.resolve(roofPath)) + for t = 0, 8 do + local destId = 0x0a + t + local dx = (destId % tilesPerRow) * 8 + local dy = math.floor(destId / tilesPerRow) * 8 + local sx = t * 8 + for y = 0, 7 do + for x = 0, 7 do + local r, g, b, a = roofData:getPixel(sx + x, y) + atlasData:setPixel(dx + x, dy + y, r, g, b, a) + end + end + end + local image = love.graphics.newImage(atlasData) + image:setFilter("nearest", "nearest") + return image +end + +local function optionalTable(data, gen2Key, plainKey, generated) + if data[gen2Key] then return data[gen2Key] end + if data[plainKey] then return data[plainKey] end + local ok, mod = pcall(require, "data.generated." .. generated) + if ok and type(mod) == "table" then return mod end + return nil +end + +function MapPreview.baker(data) + data = data or {} + return { + tilesets = data.gen2Tilesets or data.tilesets or {}, + -- Data:load does not pull roofs.lua; the Gold cache still has it. + roofs = optionalTable(data, "gen2Roofs", "roofs", "roofs"), + palettes = optionalTable(data, "gen2Palettes", "palettes", "palettes"), + atlasCache = {}, + mapImages = {}, + } +end + +function MapPreview.atlasFor(baker, mapDef) + if not (baker and mapDef) then return nil, nil end + local tileset = baker.tilesets and baker.tilesets[mapDef.tileset] + if not tileset then return nil, nil end + local cacheKey = mapDef.tileset + local roofName = nil + local roofs = baker.roofs + if ROOF_TILESETS[mapDef.tileset] then + roofName = roofs and roofs.mapGroupRoofs and roofs.mapGroupRoofs[mapDef.group] + end + if roofName then cacheKey = cacheKey .. "|" .. roofName end + local cached = baker.atlasCache[cacheKey] + if cached then return cached, tileset end + + local tilesPerRow = tileset.tilesPerRow or 16 + local atlas + local roofSpec = roofName and roofs and roofs.roofs and roofs.roofs[roofName] + if roofSpec and roofSpec.image and love.image and love.image.newImageData then + local ok, img = pcall(applyRoofOverlay, tileset.image, roofSpec.image, tilesPerRow) + if ok then atlas = img end + end + if not atlas then + if not tileset.image then return nil, tileset end + local ok, img = pcall(Assets.image, tileset.image) + if not ok then return nil, tileset end + atlas = img + if atlas.setFilter then atlas:setFilter("nearest", "nearest") end + end + baker.atlasCache[cacheKey] = atlas + return atlas, tileset +end + +-- Same bake as World:bakeMapImage (src/world/gen2/World.lua), minus the +-- World fields the overworld keeps for anim overlays and cave flicker. +function MapPreview.bake(baker, map, daytime) + local atlas, tileset = MapPreview.atlasFor(baker, map.def) + if not atlas or not tileset then return nil end + if not (love.graphics and love.graphics.newCanvas) then return nil end + local blocks = tileset.blocks + local tilesPerRow = tileset.tilesPerRow or 16 + local pw, ph = map.width * 32, map.height * 32 + local okCanvas, canvas = pcall(love.graphics.newCanvas, pw, ph) + if not okCanvas or not canvas then return nil end + if canvas.setFilter then canvas:setFilter("nearest", "nearest") end + local quads = {} + local function quadFor(tile) + local q = quads[tile] + if q then return q end + local sx = (tile % tilesPerRow) * 8 + local sy = math.floor(tile / tilesPerRow) * 8 + q = love.graphics.newQuad(sx, sy, 8, 8, atlas:getDimensions()) + quads[tile] = q + return q + end + + local tilePalettes = tileset.tilePalettes + local bgSet = baker.palettes and daytime + and Palettes.bgSet(baker.palettes, map.def, daytime) or nil + local colored = bgSet and tilePalettes and GbcPalette.available() + local clearColor = { 0.15, 0.55, 0.25 } + if bgSet and bgSet[1] and bgSet[1][1] then + local c = GbcPalette.color(bgSet[1], 1) + clearColor = { c[1] / 255, c[2] / 255, c[3] / 255 } + end + + local function drawTiles(slot) + for by = 0, map.height - 1 do + for bx = 0, map.width - 1 do + local blockId = BorderFill.blockFor( + map.blocks[by * map.width + bx + 1], map.borderBlock) + local block = blocks and blocks[(blockId or 0) + 1] + if block then + for i = 0, 15 do + local tile = block[i + 1] or 0 + local tileSlot = tilePalettes and tilePalettes[tile + 1] or 1 + if not slot or tileSlot == slot then + local tx = bx * 32 + (i % 4) * 8 + local ty = by * 32 + math.floor(i / 4) * 8 + love.graphics.draw(atlas, quadFor(tile), tx, ty) + end + end + end + end + end + end + + local function paint() + love.graphics.clear(clearColor[1], clearColor[2], clearColor[3], 1) + love.graphics.setColor(1, 1, 1, 1) + love.graphics.push() + love.graphics.origin() + if colored then + for slot = 1, 8 do + GbcPalette.with(bgSet[slot], function() drawTiles(slot) end) + end + else + drawTiles(nil) + end + love.graphics.pop() + end + if canvas.renderTo then + canvas:renderTo(paint) + else + paint() + end + return canvas +end + +function MapPreview.imageFor(baker, map) + if not (baker and map and map.id) then return nil end + local cached = baker.mapImages[map.id] + if cached then return cached end + local img = MapPreview.bake(baker, map, "DAY") + baker.mapImages[map.id] = img or false + return img +end + +function MapPreview.renderer(baker, map) + local img = MapPreview.imageFor(baker, map) + if not img then return nil end + return { + draw = function(_, camX, camY) + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(img, -math.floor(camX or 0), -math.floor(camY or 0)) + end, + } +end + +return MapPreview diff --git a/tests/drivers/gold/flag_names.lua b/tests/drivers/gold/flag_names.lua index 0d4e44b1..685ab5ac 100644 --- a/tests/drivers/gold/flag_names.lua +++ b/tests/drivers/gold/flag_names.lua @@ -1,1340 +1,2 @@ --- Generated by tools/goldwalk/gen_flags.lua from pret/pokegold. Do not edit. --- --- EVENT_* / ENGINE_* name -> the numeric id the cart's scripts carry, which is --- what src/world/gen2/Events.lua and World:engineFlag key on. Verified against --- the ROM by tests/gold_flag_names_test.lua. -return { - source = "pret/pokegold constants/{event,engine}_flags.asm", - events = { - EVENT_ALAN_ASKED_FOR_PHONE_NUMBER = 645, - EVENT_ALAN_READY_FOR_REMATCH = 646, - EVENT_ANTHONY_ASKED_FOR_PHONE_NUMBER = 635, - EVENT_ANTHONY_READY_FOR_REMATCH = 636, - EVENT_ARNIE_ASKED_FOR_PHONE_NUMBER = 643, - EVENT_ARNIE_READY_FOR_REMATCH = 644, - EVENT_AZALEA_TOWN_HIDDEN_FULL_HEAL = 177, - EVENT_AZALEA_TOWN_SLOWPOKES = 1785, - EVENT_AZALEA_TOWN_SLOWPOKETAIL_ROCKET = 1786, - EVENT_BEAT_BEAUTY_ANGELICA = 1207, - EVENT_BEAT_BEAUTY_BRENDA = 1201, - EVENT_BEAT_BEAUTY_CARLENE = 1204, - EVENT_BEAT_BEAUTY_CAROLINE = 1203, - EVENT_BEAT_BEAUTY_CASSIE = 1202, - EVENT_BEAT_BEAUTY_JACLYN = 1200, - EVENT_BEAT_BEAUTY_JESSICA = 1205, - EVENT_BEAT_BEAUTY_JULIA = 1210, - EVENT_BEAT_BEAUTY_JULIE = 1199, - EVENT_BEAT_BEAUTY_KENDRA = 1208, - EVENT_BEAT_BEAUTY_RACHAEL = 1206, - EVENT_BEAT_BEAUTY_SAMANTHA = 1198, - EVENT_BEAT_BEAUTY_THERESA = 1211, - EVENT_BEAT_BEAUTY_VALERIE = 1212, - EVENT_BEAT_BEAUTY_VERONICA = 1209, - EVENT_BEAT_BEAUTY_VICTORIA = 1197, - EVENT_BEAT_BIKER_BENNY = 1073, - EVENT_BEAT_BIKER_CHARLES = 1078, - EVENT_BEAT_BIKER_DWAYNE = 1075, - EVENT_BEAT_BIKER_GLENN = 1081, - EVENT_BEAT_BIKER_HARRIS = 1076, - EVENT_BEAT_BIKER_JOEL = 1080, - EVENT_BEAT_BIKER_KAZU = 1074, - EVENT_BEAT_BIKER_RILEY = 1079, - EVENT_BEAT_BIKER_ZEKE = 1077, - EVENT_BEAT_BIRD_KEEPER_ABE = 1020, - EVENT_BEAT_BIRD_KEEPER_BOB = 1029, - EVENT_BEAT_BIRD_KEEPER_BORIS = 1028, - EVENT_BEAT_BIRD_KEEPER_BRET = 1034, - EVENT_BEAT_BIRD_KEEPER_BRYAN = 1021, - EVENT_BEAT_BIRD_KEEPER_DENIS = 1024, - EVENT_BEAT_BIRD_KEEPER_HANK = 1026, - EVENT_BEAT_BIRD_KEEPER_JOSE = 1030, - EVENT_BEAT_BIRD_KEEPER_JOSE2 = 1032, - EVENT_BEAT_BIRD_KEEPER_JOSE3 = 1035, - EVENT_BEAT_BIRD_KEEPER_PERRY = 1033, - EVENT_BEAT_BIRD_KEEPER_PETER = 1031, - EVENT_BEAT_BIRD_KEEPER_ROD = 1019, - EVENT_BEAT_BIRD_KEEPER_ROY = 1027, - EVENT_BEAT_BIRD_KEEPER_THEO = 1022, - EVENT_BEAT_BIRD_KEEPER_TOBY = 1023, - EVENT_BEAT_BIRD_KEEPER_VANCE = 1025, - EVENT_BEAT_BIRD_KEEPER_VANCE2 = 1036, - EVENT_BEAT_BIRD_KEEPER_VANCE3 = 1037, - EVENT_BEAT_BLACKBELT_KENJI = 1195, - EVENT_BEAT_BLACKBELT_KENJI2 = 1188, - EVENT_BEAT_BLACKBELT_KENJI3 = 1190, - EVENT_BEAT_BLACKBELT_KIYO = 1193, - EVENT_BEAT_BLACKBELT_LAO = 1191, - EVENT_BEAT_BLACKBELT_LUNG = 1194, - EVENT_BEAT_BLACKBELT_NOB = 1192, - EVENT_BEAT_BLACKBELT_WAI = 1196, - EVENT_BEAT_BLACKBELT_YOSHI = 1189, - EVENT_BEAT_BLAINE = 1227, - EVENT_BEAT_BLUE = 1228, - EVENT_BEAT_BOARDER_BRAD = 1039, - EVENT_BEAT_BOARDER_DOUGLAS = 1040, - EVENT_BEAT_BOARDER_RONALD = 1038, - EVENT_BEAT_BROCK = 1221, - EVENT_BEAT_BUGSY = 1214, - EVENT_BEAT_BUG_CATCHER_AL = 1341, - EVENT_BEAT_BUG_CATCHER_ARNIE = 1343, - EVENT_BEAT_BUG_CATCHER_ARNIE2 = 1348, - EVENT_BEAT_BUG_CATCHER_ARNIE3 = 1349, - EVENT_BEAT_BUG_CATCHER_BENNY = 1340, - EVENT_BEAT_BUG_CATCHER_DON = 1336, - EVENT_BEAT_BUG_CATCHER_DOUG = 1347, - EVENT_BEAT_BUG_CATCHER_ED = 1338, - EVENT_BEAT_BUG_CATCHER_JOSH = 1342, - EVENT_BEAT_BUG_CATCHER_KEN = 1344, - EVENT_BEAT_BUG_CATCHER_ROB = 1337, - EVENT_BEAT_BUG_CATCHER_WADE = 1339, - EVENT_BEAT_BUG_CATCHER_WADE2 = 1345, - EVENT_BEAT_BUG_CATCHER_WADE3 = 1346, - EVENT_BEAT_BURGLAR_COREY = 1071, - EVENT_BEAT_BURGLAR_DUNCAN = 1069, - EVENT_BEAT_BURGLAR_EDDIE = 1070, - EVENT_BEAT_CAMPER_BARRY = 1054, - EVENT_BEAT_CAMPER_DALE = 1059, - EVENT_BEAT_CAMPER_DAVID = 1065, - EVENT_BEAT_CAMPER_DEAN = 1056, - EVENT_BEAT_CAMPER_ELLIOT = 1053, - EVENT_BEAT_CAMPER_HERVEY = 1058, - EVENT_BEAT_CAMPER_IVAN = 1052, - EVENT_BEAT_CAMPER_JERRY = 1067, - EVENT_BEAT_CAMPER_JOHN = 1066, - EVENT_BEAT_CAMPER_LEROY = 1064, - EVENT_BEAT_CAMPER_LLOYD = 1055, - EVENT_BEAT_CAMPER_ROLAND = 1050, - EVENT_BEAT_CAMPER_SID = 1057, - EVENT_BEAT_CAMPER_SPENCER = 1068, - EVENT_BEAT_CAMPER_TED = 1060, - EVENT_BEAT_CAMPER_THOMAS = 1063, - EVENT_BEAT_CAMPER_TODD = 1051, - EVENT_BEAT_CAMPER_TODD2 = 1061, - EVENT_BEAT_CAMPER_TODD3 = 1062, - EVENT_BEAT_CHAMPION_LANCE = 1468, - EVENT_BEAT_CHUCK = 1218, - EVENT_BEAT_CLAIR = 1220, - EVENT_BEAT_COOLTRAINERF_BETH = 1379, - EVENT_BEAT_COOLTRAINERF_BETH2 = 1382, - EVENT_BEAT_COOLTRAINERF_BETH3 = 1388, - EVENT_BEAT_COOLTRAINERF_CAROL = 1383, - EVENT_BEAT_COOLTRAINERF_CYBIL = 1386, - EVENT_BEAT_COOLTRAINERF_EMMA = 1385, - EVENT_BEAT_COOLTRAINERF_FRAN = 1373, - EVENT_BEAT_COOLTRAINERF_GWEN = 1371, - EVENT_BEAT_COOLTRAINERF_IRENE = 1376, - EVENT_BEAT_COOLTRAINERF_JENN = 1387, - EVENT_BEAT_COOLTRAINERF_JOYCE = 1378, - EVENT_BEAT_COOLTRAINERF_KATE = 1375, - EVENT_BEAT_COOLTRAINERF_KELLY = 1377, - EVENT_BEAT_COOLTRAINERF_LOIS = 1372, - EVENT_BEAT_COOLTRAINERF_LOLA = 1374, - EVENT_BEAT_COOLTRAINERF_MEGAN = 1381, - EVENT_BEAT_COOLTRAINERF_QUINN = 1384, - EVENT_BEAT_COOLTRAINERF_REENA = 1380, - EVENT_BEAT_COOLTRAINERF_REENA2 = 1389, - EVENT_BEAT_COOLTRAINERF_REENA3 = 1390, - EVENT_BEAT_COOLTRAINERM_AARON = 1353, - EVENT_BEAT_COOLTRAINERM_ALLEN = 1370, - EVENT_BEAT_COOLTRAINERM_ANDY = 1365, - EVENT_BEAT_COOLTRAINERM_BLAKE = 1362, - EVENT_BEAT_COOLTRAINERM_BRIAN = 1363, - EVENT_BEAT_COOLTRAINERM_CODY = 1355, - EVENT_BEAT_COOLTRAINERM_ERICK = 1364, - EVENT_BEAT_COOLTRAINERM_GAVEN = 1361, - EVENT_BEAT_COOLTRAINERM_GAVEN2 = 1357, - EVENT_BEAT_COOLTRAINERM_GAVEN3 = 1358, - EVENT_BEAT_COOLTRAINERM_JAKE = 1360, - EVENT_BEAT_COOLTRAINERM_KEVIN = 1368, - EVENT_BEAT_COOLTRAINERM_MIKE = 1356, - EVENT_BEAT_COOLTRAINERM_NICK = 1352, - EVENT_BEAT_COOLTRAINERM_PAUL = 1354, - EVENT_BEAT_COOLTRAINERM_RYAN = 1359, - EVENT_BEAT_COOLTRAINERM_SEAN = 1367, - EVENT_BEAT_COOLTRAINERM_STEVE = 1369, - EVENT_BEAT_COOLTRAINERM_TYLER = 1366, - EVENT_BEAT_ELITE_4_BRUNO = 1466, - EVENT_BEAT_ELITE_4_KAREN = 1467, - EVENT_BEAT_ELITE_4_KOGA = 1465, - EVENT_BEAT_ELITE_4_WILL = 1464, - EVENT_BEAT_ELITE_FOUR = 68, - EVENT_BEAT_ERIKA = 1224, - EVENT_BEAT_FALKNER = 1213, - EVENT_BEAT_FIREBREATHER_BILL = 1098, - EVENT_BEAT_FIREBREATHER_BURT = 1097, - EVENT_BEAT_FIREBREATHER_DICK = 1095, - EVENT_BEAT_FIREBREATHER_LYLE = 1101, - EVENT_BEAT_FIREBREATHER_NED = 1096, - EVENT_BEAT_FIREBREATHER_OTIS = 1094, - EVENT_BEAT_FIREBREATHER_RAY = 1100, - EVENT_BEAT_FIREBREATHER_WALT = 1099, - EVENT_BEAT_FISHER_ANDRE = 1109, - EVENT_BEAT_FISHER_ARNOLD = 1104, - EVENT_BEAT_FISHER_BARNEY = 1116, - EVENT_BEAT_FISHER_CHRIS = 1108, - EVENT_BEAT_FISHER_CHRIS2 = 1119, - EVENT_BEAT_FISHER_CHRIS3 = 1120, - EVENT_BEAT_FISHER_EDGAR = 1112, - EVENT_BEAT_FISHER_HENRY = 1106, - EVENT_BEAT_FISHER_JONAH = 1113, - EVENT_BEAT_FISHER_JUSTIN = 1102, - EVENT_BEAT_FISHER_KYLE = 1105, - EVENT_BEAT_FISHER_MARTIN = 1114, - EVENT_BEAT_FISHER_MARVIN = 1107, - EVENT_BEAT_FISHER_RALPH = 1103, - EVENT_BEAT_FISHER_RALPH2 = 1117, - EVENT_BEAT_FISHER_RALPH3 = 1118, - EVENT_BEAT_FISHER_RAYMOND = 1110, - EVENT_BEAT_FISHER_SCOTT = 1122, - EVENT_BEAT_FISHER_STEPHEN = 1115, - EVENT_BEAT_FISHER_WILTON = 1111, - EVENT_BEAT_FISHER_WILTON2 = 1121, - EVENT_BEAT_FISHER_WILTON3 = 1123, - EVENT_BEAT_GENTLEMAN_ALFRED = 1182, - EVENT_BEAT_GENTLEMAN_EDWARD = 1179, - EVENT_BEAT_GENTLEMAN_GREGORY = 1180, - EVENT_BEAT_GENTLEMAN_PRESTON = 1178, - EVENT_BEAT_GENTLEMAN_VIRGIL = 1181, - EVENT_BEAT_GUITARIST_CLYDE = 1171, - EVENT_BEAT_GUITARIST_VINCENT = 1172, - EVENT_BEAT_HIKER_ANTHONY = 1320, - EVENT_BEAT_HIKER_ANTHONY2 = 1316, - EVENT_BEAT_HIKER_ANTHONY3 = 1327, - EVENT_BEAT_HIKER_BAILEY = 1326, - EVENT_BEAT_HIKER_BENJAMIN = 1321, - EVENT_BEAT_HIKER_DANIEL = 1333, - EVENT_BEAT_HIKER_ERIK = 1322, - EVENT_BEAT_HIKER_JIM = 1332, - EVENT_BEAT_HIKER_KENNY = 1331, - EVENT_BEAT_HIKER_LEONARD = 1319, - EVENT_BEAT_HIKER_MICHAEL = 1323, - EVENT_BEAT_HIKER_NOLAND = 1329, - EVENT_BEAT_HIKER_PARRY = 1324, - EVENT_BEAT_HIKER_PARRY2 = 1334, - EVENT_BEAT_HIKER_PARRY3 = 1335, - EVENT_BEAT_HIKER_PHILLIP = 1318, - EVENT_BEAT_HIKER_RUSSELL = 1317, - EVENT_BEAT_HIKER_SIDNEY = 1330, - EVENT_BEAT_HIKER_TIM = 1328, - EVENT_BEAT_HIKER_TIMOTHY = 1325, - EVENT_BEAT_JANINE = 1225, - EVENT_BEAT_JASMINE = 1217, - EVENT_BEAT_JUGGLER_FRITZ = 1174, - EVENT_BEAT_JUGGLER_HORTON = 1175, - EVENT_BEAT_JUGGLER_IRWIN = 1173, - EVENT_BEAT_JUGGLER_IRWIN2 = 1176, - EVENT_BEAT_JUGGLER_IRWIN3 = 1177, - EVENT_BEAT_KIMONO_GIRL_KUNI = 1250, - EVENT_BEAT_KIMONO_GIRL_MIKI = 1251, - EVENT_BEAT_KIMONO_GIRL_NAOKO = 1247, - EVENT_BEAT_KIMONO_GIRL_SAYO = 1248, - EVENT_BEAT_KIMONO_GIRL_ZUKI = 1249, - EVENT_BEAT_LASS_ALICE = 1303, - EVENT_BEAT_LASS_BRIDGET = 1302, - EVENT_BEAT_LASS_CARRIE = 1301, - EVENT_BEAT_LASS_CONNIE = 1305, - EVENT_BEAT_LASS_CONNIE2 = 1312, - EVENT_BEAT_LASS_CONNIE3 = 1313, - EVENT_BEAT_LASS_DANA = 1310, - EVENT_BEAT_LASS_DANA2 = 1314, - EVENT_BEAT_LASS_DANA3 = 1315, - EVENT_BEAT_LASS_ELLEN = 1311, - EVENT_BEAT_LASS_KRISE = 1304, - EVENT_BEAT_LASS_LAURA = 1307, - EVENT_BEAT_LASS_LINDA = 1306, - EVENT_BEAT_LASS_MICHELLE = 1309, - EVENT_BEAT_LASS_SHANNON = 1308, - EVENT_BEAT_LTSURGE = 1223, - EVENT_BEAT_MEDIUM_BETHANY = 1421, - EVENT_BEAT_MEDIUM_DORIS = 1425, - EVENT_BEAT_MEDIUM_ETHEL = 1423, - EVENT_BEAT_MEDIUM_GRACE = 1420, - EVENT_BEAT_MEDIUM_MARGRET = 1422, - EVENT_BEAT_MEDIUM_MARTHA = 1419, - EVENT_BEAT_MEDIUM_REBECCA = 1424, - EVENT_BEAT_MISTY = 1222, - EVENT_BEAT_MORTY = 1216, - EVENT_BEAT_OFFICER_DIRK = 1351, - EVENT_BEAT_OFFICER_KEITH = 1350, - EVENT_BEAT_PICNICKER_BROOKE = 1152, - EVENT_BEAT_PICNICKER_CINDY = 1154, - EVENT_BEAT_PICNICKER_DEBRA = 1157, - EVENT_BEAT_PICNICKER_EDNA = 1163, - EVENT_BEAT_PICNICKER_ERIN = 1159, - EVENT_BEAT_PICNICKER_ERIN2 = 1167, - EVENT_BEAT_PICNICKER_ERIN3 = 1170, - EVENT_BEAT_PICNICKER_GINA = 1151, - EVENT_BEAT_PICNICKER_GINA2 = 1158, - EVENT_BEAT_PICNICKER_GINA3 = 1164, - EVENT_BEAT_PICNICKER_HEIDI = 1162, - EVENT_BEAT_PICNICKER_HOPE = 1155, - EVENT_BEAT_PICNICKER_KIM = 1153, - EVENT_BEAT_PICNICKER_LIZ = 1150, - EVENT_BEAT_PICNICKER_LIZ2 = 1160, - EVENT_BEAT_PICNICKER_LIZ3 = 1161, - EVENT_BEAT_PICNICKER_SHARON = 1156, - EVENT_BEAT_PICNICKER_TANYA = 1168, - EVENT_BEAT_PICNICKER_TIFFANY = 1169, - EVENT_BEAT_PICNICKER_TIFFANY2 = 1165, - EVENT_BEAT_PICNICKER_TIFFANY3 = 1166, - EVENT_BEAT_POKEFANF_BEVERLY = 1241, - EVENT_BEAT_POKEFANF_BEVERLY2 = 1243, - EVENT_BEAT_POKEFANF_BEVERLY3 = 1244, - EVENT_BEAT_POKEFANF_GEORGIA = 1245, - EVENT_BEAT_POKEFANF_RUTH = 1242, - EVENT_BEAT_POKEFANM_ALEX = 1240, - EVENT_BEAT_POKEFANM_BRANDON = 1235, - EVENT_BEAT_POKEFANM_CARTER = 1233, - EVENT_BEAT_POKEFANM_COLIN = 1237, - EVENT_BEAT_POKEFANM_DEREK = 1230, - EVENT_BEAT_POKEFANM_DEREK2 = 1238, - EVENT_BEAT_POKEFANM_DEREK3 = 1239, - EVENT_BEAT_POKEFANM_JEREMY = 1236, - EVENT_BEAT_POKEFANM_JOSHUA = 1232, - EVENT_BEAT_POKEFANM_ROBERT = 1231, - EVENT_BEAT_POKEFANM_TREVOR = 1234, - EVENT_BEAT_POKEFANM_WILLIAM = 1229, - EVENT_BEAT_POKEMANIAC_ANDREW = 1253, - EVENT_BEAT_POKEMANIAC_BEN = 1256, - EVENT_BEAT_POKEMANIAC_BRENT = 1257, - EVENT_BEAT_POKEMANIAC_BRENT2 = 1260, - EVENT_BEAT_POKEMANIAC_BRENT3 = 1261, - EVENT_BEAT_POKEMANIAC_CALVIN = 1254, - EVENT_BEAT_POKEMANIAC_DONALD = 1263, - EVENT_BEAT_POKEMANIAC_ETHAN = 1259, - EVENT_BEAT_POKEMANIAC_ISSAC = 1262, - EVENT_BEAT_POKEMANIAC_LARRY = 1252, - EVENT_BEAT_POKEMANIAC_RON = 1258, - EVENT_BEAT_POKEMANIAC_SHANE = 1255, - EVENT_BEAT_POKEMANIAC_ZACH = 1264, - EVENT_BEAT_PRYCE = 1219, - EVENT_BEAT_PSYCHIC_FIDEL = 1085, - EVENT_BEAT_PSYCHIC_FRANKLIN = 1083, - EVENT_BEAT_PSYCHIC_GILBERT = 1091, - EVENT_BEAT_PSYCHIC_GREG = 1086, - EVENT_BEAT_PSYCHIC_HERMAN = 1084, - EVENT_BEAT_PSYCHIC_JARED = 1092, - EVENT_BEAT_PSYCHIC_MARK = 1088, - EVENT_BEAT_PSYCHIC_NATHAN = 1082, - EVENT_BEAT_PSYCHIC_NORMAN = 1087, - EVENT_BEAT_PSYCHIC_PHIL = 1089, - EVENT_BEAT_PSYCHIC_RICHARD = 1090, - EVENT_BEAT_PSYCHIC_RODNEY = 1093, - EVENT_BEAT_RIVAL_IN_MT_MOON = 793, - EVENT_BEAT_ROCKET_EXECUTIVEF_1 = 1391, - EVENT_BEAT_ROCKET_EXECUTIVEF_2 = 1392, - EVENT_BEAT_ROCKET_EXECUTIVEM_1 = 1393, - EVENT_BEAT_ROCKET_EXECUTIVEM_2 = 1394, - EVENT_BEAT_ROCKET_EXECUTIVEM_3 = 1395, - EVENT_BEAT_ROCKET_EXECUTIVEM_4 = 1396, - EVENT_BEAT_ROCKET_GRUNTF_1 = 1296, - EVENT_BEAT_ROCKET_GRUNTF_2 = 1297, - EVENT_BEAT_ROCKET_GRUNTF_3 = 1298, - EVENT_BEAT_ROCKET_GRUNTF_4 = 1299, - EVENT_BEAT_ROCKET_GRUNTF_5 = 1300, - EVENT_BEAT_ROCKET_GRUNTM_1 = 1265, - EVENT_BEAT_ROCKET_GRUNTM_10 = 1274, - EVENT_BEAT_ROCKET_GRUNTM_11 = 1275, - EVENT_BEAT_ROCKET_GRUNTM_12 = 1276, - EVENT_BEAT_ROCKET_GRUNTM_13 = 1277, - EVENT_BEAT_ROCKET_GRUNTM_14 = 1278, - EVENT_BEAT_ROCKET_GRUNTM_15 = 1279, - EVENT_BEAT_ROCKET_GRUNTM_16 = 1280, - EVENT_BEAT_ROCKET_GRUNTM_17 = 1281, - EVENT_BEAT_ROCKET_GRUNTM_18 = 1282, - EVENT_BEAT_ROCKET_GRUNTM_19 = 1283, - EVENT_BEAT_ROCKET_GRUNTM_2 = 1266, - EVENT_BEAT_ROCKET_GRUNTM_20 = 1284, - EVENT_BEAT_ROCKET_GRUNTM_21 = 1285, - EVENT_BEAT_ROCKET_GRUNTM_22 = 1286, - EVENT_BEAT_ROCKET_GRUNTM_23 = 1287, - EVENT_BEAT_ROCKET_GRUNTM_24 = 1288, - EVENT_BEAT_ROCKET_GRUNTM_25 = 1289, - EVENT_BEAT_ROCKET_GRUNTM_26 = 1290, - EVENT_BEAT_ROCKET_GRUNTM_27 = 1291, - EVENT_BEAT_ROCKET_GRUNTM_28 = 1292, - EVENT_BEAT_ROCKET_GRUNTM_29 = 1293, - EVENT_BEAT_ROCKET_GRUNTM_3 = 1267, - EVENT_BEAT_ROCKET_GRUNTM_30 = 1294, - EVENT_BEAT_ROCKET_GRUNTM_31 = 1295, - EVENT_BEAT_ROCKET_GRUNTM_4 = 1268, - EVENT_BEAT_ROCKET_GRUNTM_5 = 1269, - EVENT_BEAT_ROCKET_GRUNTM_6 = 1270, - EVENT_BEAT_ROCKET_GRUNTM_7 = 1271, - EVENT_BEAT_ROCKET_GRUNTM_8 = 1272, - EVENT_BEAT_ROCKET_GRUNTM_9 = 1273, - EVENT_BEAT_SABRINA = 1226, - EVENT_BEAT_SAGE_CHOW = 1041, - EVENT_BEAT_SAGE_EDMOND = 1047, - EVENT_BEAT_SAGE_JEFFREY = 1045, - EVENT_BEAT_SAGE_JIN = 1043, - EVENT_BEAT_SAGE_LI = 1049, - EVENT_BEAT_SAGE_NEAL = 1048, - EVENT_BEAT_SAGE_NICO = 1042, - EVENT_BEAT_SAGE_PING = 1046, - EVENT_BEAT_SAGE_TROY = 1044, - EVENT_BEAT_SAILOR_ERNEST = 1401, - EVENT_BEAT_SAILOR_EUGENE = 1397, - EVENT_BEAT_SAILOR_GARRETT = 1403, - EVENT_BEAT_SAILOR_HARRY = 1406, - EVENT_BEAT_SAILOR_HUEY = 1398, - EVENT_BEAT_SAILOR_HUEY2 = 1407, - EVENT_BEAT_SAILOR_HUEY3 = 1408, - EVENT_BEAT_SAILOR_JEFF = 1402, - EVENT_BEAT_SAILOR_KENNETH = 1404, - EVENT_BEAT_SAILOR_KENT = 1400, - EVENT_BEAT_SAILOR_STANLY = 1405, - EVENT_BEAT_SAILOR_TERRELL = 1399, - EVENT_BEAT_SCHOOLBOY_ALAN = 1134, - EVENT_BEAT_SCHOOLBOY_ALAN2 = 1146, - EVENT_BEAT_SCHOOLBOY_ALAN3 = 1147, - EVENT_BEAT_SCHOOLBOY_BILLY = 1140, - EVENT_BEAT_SCHOOLBOY_CHAD = 1141, - EVENT_BEAT_SCHOOLBOY_CHAD2 = 1148, - EVENT_BEAT_SCHOOLBOY_CHAD3 = 1149, - EVENT_BEAT_SCHOOLBOY_DANNY = 1136, - EVENT_BEAT_SCHOOLBOY_DUDLEY = 1138, - EVENT_BEAT_SCHOOLBOY_JACK = 1132, - EVENT_BEAT_SCHOOLBOY_JACK2 = 1144, - EVENT_BEAT_SCHOOLBOY_JACK3 = 1145, - EVENT_BEAT_SCHOOLBOY_JOE = 1139, - EVENT_BEAT_SCHOOLBOY_JOHNNY = 1135, - EVENT_BEAT_SCHOOLBOY_KIP = 1133, - EVENT_BEAT_SCHOOLBOY_NATE = 1142, - EVENT_BEAT_SCHOOLBOY_RICKY = 1143, - EVENT_BEAT_SCHOOLBOY_TOMMY = 1137, - EVENT_BEAT_SCIENTIST_JED = 1185, - EVENT_BEAT_SCIENTIST_MARC = 1186, - EVENT_BEAT_SCIENTIST_MITCH = 1184, - EVENT_BEAT_SCIENTIST_RICH = 1187, - EVENT_BEAT_SCIENTIST_ROSS = 1183, - EVENT_BEAT_SKIER_CLARISSA = 1427, - EVENT_BEAT_SKIER_ROXANNE = 1426, - EVENT_BEAT_SUPER_NERD_ERIC = 1410, - EVENT_BEAT_SUPER_NERD_ERIC_UNUSED = 1409, - EVENT_BEAT_SUPER_NERD_GREGG = 1411, - EVENT_BEAT_SUPER_NERD_JAY = 1412, - EVENT_BEAT_SUPER_NERD_PAT = 1416, - EVENT_BEAT_SUPER_NERD_SAM = 1414, - EVENT_BEAT_SUPER_NERD_SHAWN = 1417, - EVENT_BEAT_SUPER_NERD_TERU = 1418, - EVENT_BEAT_SUPER_NERD_TOM = 1415, - EVENT_BEAT_SWIMMERF_BRIANA = 1018, - EVENT_BEAT_SWIMMERF_DAWN = 1011, - EVENT_BEAT_SWIMMERF_DENISE = 1004, - EVENT_BEAT_SWIMMERF_DIANA = 1017, - EVENT_BEAT_SWIMMERF_ELAINE = 1000, - EVENT_BEAT_SWIMMERF_JILL = 1008, - EVENT_BEAT_SWIMMERF_JODY = 1015, - EVENT_BEAT_SWIMMERF_KARA = 1005, - EVENT_BEAT_SWIMMERF_KATIE = 1010, - EVENT_BEAT_SWIMMERF_KAYLEE = 1002, - EVENT_BEAT_SWIMMERF_LISA = 1007, - EVENT_BEAT_SWIMMERF_LORI = 1014, - EVENT_BEAT_SWIMMERF_MARY = 1009, - EVENT_BEAT_SWIMMERF_NICOLE = 1013, - EVENT_BEAT_SWIMMERF_NIKKI = 1016, - EVENT_BEAT_SWIMMERF_PAULA = 1001, - EVENT_BEAT_SWIMMERF_SUSIE = 1003, - EVENT_BEAT_SWIMMERF_TARA = 1012, - EVENT_BEAT_SWIMMERF_WENDY = 1006, - EVENT_BEAT_SWIMMERM_BERKE = 1433, - EVENT_BEAT_SWIMMERM_CAMERON = 1444, - EVENT_BEAT_SWIMMERM_CHARLIE = 1431, - EVENT_BEAT_SWIMMERM_DARYL = 1438, - EVENT_BEAT_SWIMMERM_GEORGE = 1432, - EVENT_BEAT_SWIMMERM_HAL = 1436, - EVENT_BEAT_SWIMMERM_HAROLD = 1428, - EVENT_BEAT_SWIMMERM_JAMES = 1446, - EVENT_BEAT_SWIMMERM_JEROME = 1441, - EVENT_BEAT_SWIMMERM_KIRK = 1434, - EVENT_BEAT_SWIMMERM_LEWIS = 1447, - EVENT_BEAT_SWIMMERM_MATHEW = 1435, - EVENT_BEAT_SWIMMERM_PARKER = 1448, - EVENT_BEAT_SWIMMERM_PATON = 1437, - EVENT_BEAT_SWIMMERM_RANDALL = 1430, - EVENT_BEAT_SWIMMERM_RICK = 1443, - EVENT_BEAT_SWIMMERM_SETH = 1445, - EVENT_BEAT_SWIMMERM_SIMON = 1429, - EVENT_BEAT_SWIMMERM_TONY = 1440, - EVENT_BEAT_SWIMMERM_TUCKER = 1442, - EVENT_BEAT_SWIMMERM_WALTER = 1439, - EVENT_BEAT_TEACHER_COLETTE = 1461, - EVENT_BEAT_TEACHER_HILLARY = 1462, - EVENT_BEAT_TEACHER_SHIRLEY = 1463, - EVENT_BEAT_TWINS_AMY_AND_MAY = 1124, - EVENT_BEAT_TWINS_AMY_AND_MAY2 = 1127, - EVENT_BEAT_TWINS_ANN_AND_ANNE = 1125, - EVENT_BEAT_TWINS_ANN_AND_ANNE2 = 1126, - EVENT_BEAT_TWINS_JO_AND_ZOE = 1128, - EVENT_BEAT_TWINS_JO_AND_ZOE2 = 1129, - EVENT_BEAT_TWINS_MEG_AND_PEG = 1130, - EVENT_BEAT_TWINS_MEG_AND_PEG2 = 1131, - EVENT_BEAT_WHITNEY = 1215, - EVENT_BEAT_YOUNGSTER_ALBERT = 1451, - EVENT_BEAT_YOUNGSTER_GORDON = 1452, - EVENT_BEAT_YOUNGSTER_IAN = 1454, - EVENT_BEAT_YOUNGSTER_JASON = 1460, - EVENT_BEAT_YOUNGSTER_JIMMY = 1458, - EVENT_BEAT_YOUNGSTER_JOEY = 1449, - EVENT_BEAT_YOUNGSTER_JOEY2 = 1455, - EVENT_BEAT_YOUNGSTER_JOEY3 = 1456, - EVENT_BEAT_YOUNGSTER_MIKEY = 1450, - EVENT_BEAT_YOUNGSTER_OWEN = 1459, - EVENT_BEAT_YOUNGSTER_SAMUEL = 1453, - EVENT_BEAT_YOUNGSTER_WARREN = 1457, - EVENT_BEAY_SUPER_NERD_DAVE = 1413, - EVENT_BETH_ASKED_FOR_PHONE_NUMBER = 621, - EVENT_BETH_READY_FOR_REMATCH = 622, - EVENT_BEVERLY_ASKED_FOR_PHONE_NUMBER = 609, - EVENT_BEVERLY_READY_FOR_REMATCH = 610, - EVENT_BLACKTHORN_CITY_GRAMPS_BLOCKS_DRAGONS_DEN = 1868, - EVENT_BLACKTHORN_CITY_GRAMPS_NOT_BLOCKING_DRAGONS_DEN = 1869, - EVENT_BLACKTHORN_CITY_SANTOS_OF_SATURDAY = 1885, - EVENT_BLACKTHORN_CITY_SUPER_NERD_BLOCKS_GYM = 1763, - EVENT_BLACKTHORN_CITY_SUPER_NERD_DOES_NOT_BLOCK_GYM = 1764, - EVENT_BLUE_IN_CINNABAR = 1909, - EVENT_BOULDER_IN_BLACKTHORN_GYM_1 = 1798, - EVENT_BOULDER_IN_BLACKTHORN_GYM_2 = 1799, - EVENT_BOULDER_IN_BLACKTHORN_GYM_3 = 1800, - EVENT_BOULDER_IN_ICE_PATH_1 = 1801, - EVENT_BOULDER_IN_ICE_PATH_1A = 1805, - EVENT_BOULDER_IN_ICE_PATH_2 = 1802, - EVENT_BOULDER_IN_ICE_PATH_2A = 1806, - EVENT_BOULDER_IN_ICE_PATH_3 = 1803, - EVENT_BOULDER_IN_ICE_PATH_3A = 1807, - EVENT_BOULDER_IN_ICE_PATH_4 = 1804, - EVENT_BOULDER_IN_ICE_PATH_4A = 1808, - EVENT_BRENT_ASKED_FOR_PHONE_NUMBER = 657, - EVENT_BRENT_READY_FOR_REMATCH = 658, - EVENT_BRUNOS_ROOM_ENTRANCE_CLOSED = 781, - EVENT_BRUNOS_ROOM_EXIT_OPEN = 782, - EVENT_BUG_CATCHING_CONTESTANT_10A = 1823, - EVENT_BUG_CATCHING_CONTESTANT_10B = 1833, - EVENT_BUG_CATCHING_CONTESTANT_1A = 1814, - EVENT_BUG_CATCHING_CONTESTANT_1B = 1824, - EVENT_BUG_CATCHING_CONTESTANT_2A = 1815, - EVENT_BUG_CATCHING_CONTESTANT_2B = 1825, - EVENT_BUG_CATCHING_CONTESTANT_3A = 1816, - EVENT_BUG_CATCHING_CONTESTANT_3B = 1826, - EVENT_BUG_CATCHING_CONTESTANT_4A = 1817, - EVENT_BUG_CATCHING_CONTESTANT_4B = 1827, - EVENT_BUG_CATCHING_CONTESTANT_5A = 1818, - EVENT_BUG_CATCHING_CONTESTANT_5B = 1828, - EVENT_BUG_CATCHING_CONTESTANT_6A = 1819, - EVENT_BUG_CATCHING_CONTESTANT_6B = 1829, - EVENT_BUG_CATCHING_CONTESTANT_7A = 1820, - EVENT_BUG_CATCHING_CONTESTANT_7B = 1830, - EVENT_BUG_CATCHING_CONTESTANT_8A = 1821, - EVENT_BUG_CATCHING_CONTESTANT_8B = 1831, - EVENT_BUG_CATCHING_CONTESTANT_9A = 1822, - EVENT_BUG_CATCHING_CONTESTANT_9B = 1832, - EVENT_BURNED_TOWER_1F_BURN_HEAL = 1621, - EVENT_BURNED_TOWER_1F_HIDDEN_ETHER = 128, - EVENT_BURNED_TOWER_1F_X_SPEED = 1620, - EVENT_BURNED_TOWER_B1F_BEASTS_1 = 1866, - EVENT_BURNED_TOWER_B1F_BEASTS_2 = 1867, - EVENT_BURNED_TOWER_B1F_HIDDEN_BURN_HEAL = 130, - EVENT_BURNED_TOWER_B1F_HIDDEN_NUGGET = 131, - EVENT_BURNED_TOWER_B1F_HIDDEN_ULTRA_BALL = 129, - EVENT_BURNED_TOWER_B1F_HP_UP = 1622, - EVENT_BURNED_TOWER_B1F_TM_ENDURE = 1623, - EVENT_BURNED_TOWER_FIREBREATHER_DICK_ASHES = 1893, - EVENT_BURNED_TOWER_FIREBREATHER_DICK_NORMAL = 1892, - EVENT_CELADON_CITY_HIDDEN_PP_UP = 253, - EVENT_CERULEAN_GYM_ROCKET = 1901, - EVENT_CHAD_ASKED_FOR_PHONE_NUMBER = 651, - EVENT_CHAD_READY_FOR_REMATCH = 652, - EVENT_CHARCOAL_KILN_APPRENTICE = 1782, - EVENT_CHARCOAL_KILN_BOSS = 1783, - EVENT_CHARCOAL_KILN_FARFETCH_D = 1781, - EVENT_CHIKORITA_POKEBALL_IN_ELMS_LAB = 1602, - EVENT_CHRIS_ASKED_FOR_PHONE_NUMBER = 655, - EVENT_CHRIS_READY_FOR_REMATCH = 656, - EVENT_CIANWOOD_CITY_HIDDEN_MAX_ETHER = 179, - EVENT_CIANWOOD_CITY_HIDDEN_REVIVE = 178, - EVENT_CINNABAR_ISLAND_HIDDEN_RARE_CANDY = 254, - EVENT_CINNABAR_ROCKS_CLEARED = 215, - EVENT_CLEARED_NUGGET_BRIDGE = 216, - EVENT_CLEARED_RADIO_TOWER = 33, - EVENT_CLEARED_ROCKET_HIDEOUT = 34, - EVENT_CLEARED_SLOWPOKE_WELL = 43, - EVENT_CONTEST_OFFICER_HAS_BERRY = 790, - EVENT_CONTEST_OFFICER_HAS_EVERSTONE = 788, - EVENT_CONTEST_OFFICER_HAS_GOLD_BERRY = 789, - EVENT_CONTEST_OFFICER_HAS_SUN_STONE = 787, - EVENT_COPYCATS_HOUSE_2F_DOLL = 1907, - EVENT_COP_IN_ELMS_LAB = 1794, - EVENT_CYNDAQUIL_POKEBALL_IN_ELMS_LAB = 1600, - EVENT_DANA_ASKED_FOR_PHONE_NUMBER = 649, - EVENT_DANA_READY_FOR_REMATCH = 650, - EVENT_DARK_CAVE_BLACKTHORN_ENTRANCE_REVIVE = 1697, - EVENT_DARK_CAVE_BLACKTHORN_ENTRANCE_TM_SNORE = 1698, - EVENT_DARK_CAVE_VIOLET_ENTRANCE_FULL_HEAL = 1695, - EVENT_DARK_CAVE_VIOLET_ENTRANCE_HIDDEN_ELIXER = 157, - EVENT_DARK_CAVE_VIOLET_ENTRANCE_HYPER_POTION = 1696, - EVENT_DARK_CAVE_VIOLET_ENTRANCE_POTION = 1694, - EVENT_DAY_CARE_MAN_IN_DAY_CARE = 1765, - EVENT_DAY_CARE_MAN_ON_ROUTE_34 = 1766, - EVENT_DAY_CARE_MON_1 = 1767, - EVENT_DAY_CARE_MON_2 = 1768, - EVENT_DECIDED_TO_HELP_LANCE = 96, - EVENT_DECO_BED_1 = 676, - EVENT_DECO_BED_2 = 677, - EVENT_DECO_BED_3 = 678, - EVENT_DECO_BED_4 = 679, - EVENT_DECO_BIG_LAPRAS_DOLL = 721, - EVENT_DECO_BIG_ONIX_DOLL = 720, - EVENT_DECO_BIG_SNORLAX_DOLL = 719, - EVENT_DECO_BULBASAUR_DOLL = 699, - EVENT_DECO_CARPET_1 = 680, - EVENT_DECO_CARPET_2 = 681, - EVENT_DECO_CARPET_3 = 682, - EVENT_DECO_CARPET_4 = 683, - EVENT_DECO_CHARMANDER_DOLL = 700, - EVENT_DECO_CLEFAIRY_DOLL = 697, - EVENT_DECO_DIGLETT_DOLL = 703, - EVENT_DECO_FAMICOM = 691, - EVENT_DECO_GENGAR_DOLL = 707, - EVENT_DECO_GEODUDE_DOLL = 713, - EVENT_DECO_GOLD_TROPHY = 717, - EVENT_DECO_GRIMER_DOLL = 709, - EVENT_DECO_JIGGLYPUFF_DOLL = 698, - EVENT_DECO_MACHOP_DOLL = 714, - EVENT_DECO_MAGIKARP_DOLL = 705, - EVENT_DECO_N64 = 693, - EVENT_DECO_ODDISH_DOLL = 706, - EVENT_DECO_PIKACHU_DOLL = 695, - EVENT_DECO_PLANT_1 = 684, - EVENT_DECO_PLANT_2 = 685, - EVENT_DECO_PLANT_3 = 686, - EVENT_DECO_POLIWAG_DOLL = 702, - EVENT_DECO_POSTER_1 = 687, - EVENT_DECO_POSTER_2 = 688, - EVENT_DECO_POSTER_3 = 689, - EVENT_DECO_POSTER_4 = 690, - EVENT_DECO_SHELLDER_DOLL = 708, - EVENT_DECO_SILVER_TROPHY = 718, - EVENT_DECO_SNES = 692, - EVENT_DECO_SQUIRTLE_DOLL = 701, - EVENT_DECO_STARYU_DOLL = 704, - EVENT_DECO_SURFING_PIKACHU_DOLL = 696, - EVENT_DECO_TENTACOOL_DOLL = 715, - EVENT_DECO_UNOWN_DOLL = 712, - EVENT_DECO_VIRTUAL_BOY = 694, - EVENT_DECO_VOLTORB_DOLL = 710, - EVENT_DECO_WEEDLE_DOLL = 711, - EVENT_DEREK_ASKED_FOR_PHONE_NUMBER = 653, - EVENT_DEREK_READY_FOR_REMATCH = 654, - EVENT_DIGLETTS_CAVE_HIDDEN_MAX_REVIVE = 228, - EVENT_DOOR_10_OPEN = 736, - EVENT_DOOR_11_OPEN = 737, - EVENT_DOOR_1_OPEN = 727, - EVENT_DOOR_2_OPEN = 728, - EVENT_DOOR_3_OPEN = 729, - EVENT_DOOR_4_OPEN = 730, - EVENT_DOOR_5_OPEN = 731, - EVENT_DOOR_6_OPEN = 732, - EVENT_DOOR_7_OPEN = 733, - EVENT_DOOR_8_OPEN = 734, - EVENT_DOOR_9_OPEN = 735, - EVENT_DRAGONS_DEN_B1F_DRAGON_FANG = 1704, - EVENT_DRAGONS_DEN_B1F_HIDDEN_MAX_ELIXER = 162, - EVENT_DRAGONS_DEN_B1F_HIDDEN_MAX_POTION = 161, - EVENT_DRAGONS_DEN_B1F_HIDDEN_REVIVE = 160, - EVENT_DRAGONS_DEN_CLAIR = 1752, - EVENT_DUDE_TALKED_TO_YOU = 65, - EVENT_EARLS_ACADEMY_EARL = 1739, - EVENT_ECRUTEAK_CITY_HIDDEN_HYPER_POTION = 180, - EVENT_ECRUTEAK_POKE_CENTER_BILL = 1811, - EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_LEFT = 1894, - EVENT_ECRUTEAK_TIN_TOWER_ENTRANCE_SAGE_RIGHT = 1895, - EVENT_ELMS_AIDE_IN_LAB = 1793, - EVENT_ELMS_AIDE_IN_VIOLET_POKEMON_CENTER = 1792, - EVENT_ELM_CALLED_ABOUT_STOLEN_POKEMON = 67, - EVENT_EMERGENCY_SWITCH = 726, - EVENT_ENABLE_DIPLOMA_PRINTING = 214, - EVENT_ERIN_ASKED_FOR_PHONE_NUMBER = 669, - EVENT_ERIN_READY_FOR_REMATCH = 670, - EVENT_EXPLODING_TRAP_1 = 745, - EVENT_EXPLODING_TRAP_10 = 754, - EVENT_EXPLODING_TRAP_11 = 755, - EVENT_EXPLODING_TRAP_12 = 756, - EVENT_EXPLODING_TRAP_13 = 757, - EVENT_EXPLODING_TRAP_14 = 758, - EVENT_EXPLODING_TRAP_15 = 759, - EVENT_EXPLODING_TRAP_16 = 760, - EVENT_EXPLODING_TRAP_17 = 761, - EVENT_EXPLODING_TRAP_18 = 762, - EVENT_EXPLODING_TRAP_19 = 763, - EVENT_EXPLODING_TRAP_2 = 746, - EVENT_EXPLODING_TRAP_20 = 764, - EVENT_EXPLODING_TRAP_21 = 765, - EVENT_EXPLODING_TRAP_22 = 766, - EVENT_EXPLODING_TRAP_3 = 747, - EVENT_EXPLODING_TRAP_4 = 748, - EVENT_EXPLODING_TRAP_5 = 749, - EVENT_EXPLODING_TRAP_6 = 750, - EVENT_EXPLODING_TRAP_7 = 751, - EVENT_EXPLODING_TRAP_8 = 752, - EVENT_EXPLODING_TRAP_9 = 753, - EVENT_FAST_SHIP_1F_GENTLEMAN = 1836, - EVENT_FAST_SHIP_B1F_SAILOR_LEFT = 1838, - EVENT_FAST_SHIP_B1F_SAILOR_RIGHT = 1839, - EVENT_FAST_SHIP_CABINS_NNW_NNE_NE_SAILOR = 1837, - EVENT_FAST_SHIP_CABINS_SE_SSE_CAPTAINS_CABIN_TWIN_1 = 1841, - EVENT_FAST_SHIP_CABINS_SE_SSE_CAPTAINS_CABIN_TWIN_2 = 1842, - EVENT_FAST_SHIP_CABINS_SE_SSE_GENTLEMAN = 1840, - EVENT_FAST_SHIP_DESTINATION_OLIVINE = 47, - EVENT_FAST_SHIP_FIRST_TIME = 48, - EVENT_FAST_SHIP_FOUND_GIRL = 50, - EVENT_FAST_SHIP_HAS_ARRIVED = 49, - EVENT_FAST_SHIP_INFORMED_ABOUT_LAZY_SAILOR = 52, - EVENT_FAST_SHIP_LAZY_SAILOR = 51, - EVENT_FAST_SHIP_PASSENGERS_EASTBOUND = 1850, - EVENT_FAST_SHIP_PASSENGERS_FIRST_TRIP = 1849, - EVENT_FAST_SHIP_PASSENGERS_WESTBOUND = 1851, - EVENT_FIRST_TIME_BANKING_WITH_MOM = 118, - EVENT_FOUGHT_HO_OH = 791, - EVENT_FOUGHT_LUGIA = 792, - EVENT_FOUGHT_SNORLAX = 1872, - EVENT_FOUGHT_SUDOWOODO = 42, - EVENT_FOUND_BERSERK_GENE_IN_CERULEAN_CITY = 250, - EVENT_FOUND_LEFTOVERS_IN_CELADON_CAFE = 249, - EVENT_FOUND_MACHINE_PART_IN_CERULEAN_GYM = 251, - EVENT_GAVEN_ASKED_FOR_PHONE_NUMBER = 619, - EVENT_GAVEN_READY_FOR_REMATCH = 620, - EVENT_GAVE_KENYA = 81, - EVENT_GAVE_KURT_BLK_APRICORN = 605, - EVENT_GAVE_KURT_BLU_APRICORN = 601, - EVENT_GAVE_KURT_GRN_APRICORN = 603, - EVENT_GAVE_KURT_PNK_APRICORN = 606, - EVENT_GAVE_KURT_RED_APRICORN = 600, - EVENT_GAVE_KURT_WHT_APRICORN = 604, - EVENT_GAVE_KURT_YLW_APRICORN = 602, - EVENT_GAVE_MYSTERY_EGG_TO_ELM = 31, - EVENT_GINA_ASKED_FOR_PHONE_NUMBER = 639, - EVENT_GINA_READY_FOR_REMATCH = 640, - EVENT_GOLDENROD_CITY_CIVILIANS = 1743, - EVENT_GOLDENROD_CITY_ROCKET_SCOUT = 1740, - EVENT_GOLDENROD_CITY_ROCKET_TAKEOVER = 1741, - EVENT_GOLDENROD_DEPT_STORE_5F_HAPPINESS_EVENT_LADY = 1891, - EVENT_GOLDENROD_DEPT_STORE_B1F_AMULET_COIN = 1654, - EVENT_GOLDENROD_DEPT_STORE_B1F_BURN_HEAL = 1655, - EVENT_GOLDENROD_DEPT_STORE_B1F_ETHER = 1653, - EVENT_GOLDENROD_DEPT_STORE_B1F_LAYOUT_1 = 772, - EVENT_GOLDENROD_DEPT_STORE_B1F_LAYOUT_2 = 773, - EVENT_GOLDENROD_DEPT_STORE_B1F_LAYOUT_3 = 774, - EVENT_GOLDENROD_DEPT_STORE_B1F_ULTRA_BALL = 1656, - EVENT_GOLDENROD_TRAIN_STATION_GENTLEMAN = 1865, - EVENT_GOLDENROD_UNDERGROUND_COIN_CASE = 1650, - EVENT_GOLDENROD_UNDERGROUND_GRAMPS = 1875, - EVENT_GOLDENROD_UNDERGROUND_GRANNY = 1874, - EVENT_GOLDENROD_UNDERGROUND_HIDDEN_ANTIDOTE = 141, - EVENT_GOLDENROD_UNDERGROUND_HIDDEN_PARLYZ_HEAL = 139, - EVENT_GOLDENROD_UNDERGROUND_HIDDEN_SUPER_POTION = 140, - EVENT_GOLDENROD_UNDERGROUND_OLDER_HAIRCUT_BROTHER = 1876, - EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_FULL_HEAL = 1652, - EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_HIDDEN_MAX_POTION = 142, - EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_HIDDEN_REVIVE = 143, - EVENT_GOLDENROD_UNDERGROUND_SWITCH_ROOM_ENTRANCES_SMOKE_BALL = 1651, - EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_BLOCKED_OFF = 775, - EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_MAX_ETHER = 1657, - EVENT_GOLDENROD_UNDERGROUND_WAREHOUSE_TM_SLEEP_TALK = 1658, - EVENT_GOLDENROD_UNDERGROUND_YOUNGER_HAIRCUT_BROTHER = 1877, - EVENT_GOT_A_POKEMON_FROM_ELM = 26, - EVENT_GOT_BERRY_FROM_ROUTE_30_HOUSE = 39, - EVENT_GOT_BICYCLE = 91, - EVENT_GOT_BLACKBELT_FROM_WESLEY = 107, - EVENT_GOT_BLACKGLASSES_IN_DARK_CAVE = 114, - EVENT_GOT_CALCIUM_FROM_ERIN = 617, - EVENT_GOT_CARBOS_FROM_VANCE = 615, - EVENT_GOT_CHARCOAL_IN_CHARCOAL_KILN = 94, - EVENT_GOT_CHIKORITA_FROM_ELM = 29, - EVENT_GOT_CLEANSE_TAG = 219, - EVENT_GOT_COINS_FROM_GAMBLER_AT_CELADON = 206, - EVENT_GOT_CYNDAQUIL_FROM_ELM = 27, - EVENT_GOT_EEVEE = 79, - EVENT_GOT_EVERSTONE_FROM_BILLS_GRANDPA = 800, - EVENT_GOT_EVERSTONE_FROM_ELM = 86, - EVENT_GOT_FIRE_STONE_FROM_BILLS_GRANDPA = 803, - EVENT_GOT_GOOD_ROD = 24, - EVENT_GOT_HARD_STONE_FROM_ARTHUR = 103, - EVENT_GOT_HM01_CUT = 16, - EVENT_GOT_HM02_FLY = 17, - EVENT_GOT_HM03_SURF = 18, - EVENT_GOT_HM04_STRENGTH = 19, - EVENT_GOT_HM05_FLASH = 20, - EVENT_GOT_HM06_WHIRLPOOL = 21, - EVENT_GOT_HM07_WATERFALL = 1672, - EVENT_GOT_HP_UP_FROM_JOEY = 614, - EVENT_GOT_HP_UP_FROM_RANDY = 82, - EVENT_GOT_HP_UP_FROM_VERMILION_GUY = 226, - EVENT_GOT_IRON_FROM_PARRY = 616, - EVENT_GOT_ITEMFINDER = 90, - EVENT_GOT_KENYA = 80, - EVENT_GOT_KINGS_ROCK_IN_SLOWPOKE_WELL = 115, - EVENT_GOT_LEAF_STONE_FROM_BILLS_GRANDPA = 801, - EVENT_GOT_LOST_ITEM_FROM_FAN_CLUB = 210, - EVENT_GOT_MAGNET_FROM_SUNNY = 105, - EVENT_GOT_MASTER_BALL_FROM_ELM = 124, - EVENT_GOT_METAL_COAT_FROM_GRANDPA_ON_SS_AQUA = 113, - EVENT_GOT_MIRACLE_SEED_IN_ROUTE_32 = 93, - EVENT_GOT_MYSTERY_EGG_FROM_MR_POKEMON = 30, - EVENT_GOT_MYSTIC_WATER_IN_CHERRYGROVE = 77, - EVENT_GOT_NUGGET_FROM_GUY = 200, - EVENT_GOT_OLD_ROD = 23, - EVENT_GOT_PASS_FROM_COPYCAT = 209, - EVENT_GOT_PINK_BOW_FROM_MARY = 72, - EVENT_GOT_PINK_BOW_FROM_TUSCANY = 101, - EVENT_GOT_POISON_BARB_FROM_FRIEDA = 99, - EVENT_GOT_PROTEIN_FROM_HUEY = 613, - EVENT_GOT_QUICK_CLAW = 87, - EVENT_GOT_RAINBOW_WING = 120, - EVENT_GOT_SECRETPOTION_FROM_PHARMACY = 35, - EVENT_GOT_SHARP_BEAK_FROM_MONICA = 111, - EVENT_GOT_SHUCKIE = 69, - EVENT_GOT_SILVER_WING = 121, - EVENT_GOT_SOFT_SAND_FROM_KATE = 112, - EVENT_GOT_SPELL_TAG_FROM_SANTOS = 109, - EVENT_GOT_SQUIRTBOTTLE = 92, - EVENT_GOT_SS_TICKET_FROM_ELM = 36, - EVENT_GOT_SUNNY_DAY_FROM_RADIO_TOWER = 71, - EVENT_GOT_SUPER_ROD = 25, - EVENT_GOT_THUNDERSTONE_FROM_BILLS_GRANDPA = 804, - EVENT_GOT_TM01_DYNAMICPUNCH = 10, - EVENT_GOT_TM02_HEADBUTT = 95, - EVENT_GOT_TM03_CURSE = 218, - EVENT_GOT_TM05_ROAR = 78, - EVENT_GOT_TM06_TOXIC = 221, - EVENT_GOT_TM07_ZAP_CANNON = 223, - EVENT_GOT_TM08_ROCK_SMASH = 75, - EVENT_GOT_TM10_HIDDEN_POWER = 88, - EVENT_GOT_TM12_SWEET_SCENT = 122, - EVENT_GOT_TM13_SNORE_FROM_MOOMOO_FARM = 62, - EVENT_GOT_TM16_ICY_WIND = 14, - EVENT_GOT_TM19_GIGA_DRAIN = 220, - EVENT_GOT_TM23_IRON_TAIL = 13, - EVENT_GOT_TM24_DRAGONBREATH = 15, - EVENT_GOT_TM29_PSYCHIC = 227, - EVENT_GOT_TM30_SHADOW_BALL = 12, - EVENT_GOT_TM31_MUD_SLAP = 8, - EVENT_GOT_TM36_SLUDGE_BOMB = 89, - EVENT_GOT_TM37_SANDSTORM = 117, - EVENT_GOT_TM42_DREAM_EATER = 224, - EVENT_GOT_TM45_ATTRACT = 11, - EVENT_GOT_TM47_STEEL_WING = 116, - EVENT_GOT_TM49_FURY_CUTTER = 9, - EVENT_GOT_TM50_NIGHTMARE = 83, - EVENT_GOT_TOGEPI_EGG_FROM_ELMS_AIDE = 45, - EVENT_GOT_TOTODILE_FROM_ELM = 28, - EVENT_GOT_TYROGUE_FROM_KIYO = 97, - EVENT_GOT_UP_GRADE = 222, - EVENT_GOT_WATER_STONE_FROM_BILLS_GRANDPA = 802, - EVENT_GUIDE_GENT_IN_HIS_HOUSE = 1790, - EVENT_GUIDE_GENT_VISIBLE_IN_CHERRYGROVE = 1791, - EVENT_HEALED_MOOMOO = 61, - EVENT_HERDED_FARFETCHD = 41, - EVENT_HUEY_ASKED_FOR_PHONE_NUMBER = 611, - EVENT_HUEY_READY_FOR_REMATCH = 612, - EVENT_ICE_PATH_1F_PP_UP = 1673, - EVENT_ICE_PATH_B1F_HIDDEN_MAX_POTION = 148, - EVENT_ICE_PATH_B1F_IRON = 1674, - EVENT_ICE_PATH_B2F_BLACKTHORN_SIDE_HIDDEN_ICE_HEAL = 150, - EVENT_ICE_PATH_B2F_BLACKTHORN_SIDE_TM_REST = 1677, - EVENT_ICE_PATH_B2F_MAHOGANY_SIDE_FULL_HEAL = 1675, - EVENT_ICE_PATH_B2F_MAHOGANY_SIDE_HIDDEN_CARBOS = 149, - EVENT_ICE_PATH_B2F_MAHOGANY_SIDE_MAX_POTION = 1676, - EVENT_ICE_PATH_B3F_NEVERMELTICE = 1678, - EVENT_ILEX_FOREST_APPRENTICE = 1779, - EVENT_ILEX_FOREST_CHARCOAL_MASTER = 1780, - EVENT_ILEX_FOREST_FARFETCHD_1 = 1769, - EVENT_ILEX_FOREST_FARFETCHD_10 = 1778, - EVENT_ILEX_FOREST_FARFETCHD_2 = 1770, - EVENT_ILEX_FOREST_FARFETCHD_3 = 1771, - EVENT_ILEX_FOREST_FARFETCHD_4 = 1772, - EVENT_ILEX_FOREST_FARFETCHD_5 = 1773, - EVENT_ILEX_FOREST_FARFETCHD_6 = 1774, - EVENT_ILEX_FOREST_FARFETCHD_7 = 1775, - EVENT_ILEX_FOREST_FARFETCHD_8 = 1776, - EVENT_ILEX_FOREST_FARFETCHD_9 = 1777, - EVENT_ILEX_FOREST_HIDDEN_ETHER = 136, - EVENT_ILEX_FOREST_HIDDEN_FULL_HEAL = 138, - EVENT_ILEX_FOREST_HIDDEN_SUPER_POTION = 137, - EVENT_ILEX_FOREST_REVIVE = 1649, - EVENT_INDIGO_PLATEAU_POKECENTER_RIVAL = 1915, - EVENT_INITIALIZED_EVENTS = 54, - EVENT_IRWIN_ASKED_FOR_PHONE_NUMBER = 641, - EVENT_IRWIN_READY_FOR_REMATCH = 642, - EVENT_JACK_ASKED_FOR_PHONE_NUMBER = 607, - EVENT_JACK_READY_FOR_REMATCH = 608, - EVENT_JASMINE_EXPLAINED_AMPHYS_SICKNESS = 55, - EVENT_JASMINE_RETURNED_TO_GYM = 32, - EVENT_JOEY_ASKED_FOR_PHONE_NUMBER = 627, - EVENT_JOEY_READY_FOR_REMATCH = 628, - EVENT_JOSE_ASKED_FOR_PHONE_NUMBER = 623, - EVENT_JOSE_READY_FOR_REMATCH = 624, - EVENT_KARENS_ROOM_ENTRANCE_CLOSED = 783, - EVENT_KARENS_ROOM_EXIT_OPEN = 784, - EVENT_KENJI_ASKED_FOR_PHONE_NUMBER = 665, - EVENT_KENJI_ON_BREAK = 618, - EVENT_KENJI_READY_FOR_REMATCH = 666, - EVENT_KOGAS_ROOM_ENTRANCE_CLOSED = 779, - EVENT_KOGAS_ROOM_EXIT_OPEN = 780, - EVENT_KURTS_HOUSE_KURT_1 = 1854, - EVENT_KURTS_HOUSE_KURT_2 = 1855, - EVENT_KURTS_HOUSE_SLOWPOKE = 1789, - EVENT_KURT_GAVE_YOU_LURE_BALL = 53, - EVENT_LAKE_OF_RAGE_ASKED_FOR_MAGIKARP = 57, - EVENT_LAKE_OF_RAGE_CIVILIANS = 1845, - EVENT_LAKE_OF_RAGE_ETHER_ON_STANDBY = 58, - EVENT_LAKE_OF_RAGE_EXPLAINED_WEIRD_MAGIKARP = 56, - EVENT_LAKE_OF_RAGE_HIDDEN_FULL_RESTORE = 181, - EVENT_LAKE_OF_RAGE_HIDDEN_MAX_POTION = 183, - EVENT_LAKE_OF_RAGE_HIDDEN_RARE_CANDY = 182, - EVENT_LAKE_OF_RAGE_LANCE = 1748, - EVENT_LAKE_OF_RAGE_MAX_ETHER = 1605, - EVENT_LAKE_OF_RAGE_RED_GYARADOS = 1873, - EVENT_LAKE_OF_RAGE_TM_DETECT = 1606, - EVENT_LAKE_OF_RAGE_WESLEY_OF_WEDNESDAY = 1884, - EVENT_LANCES_ROOM_ENTRANCE_CLOSED = 785, - EVENT_LANCES_ROOM_EXIT_OPEN = 786, - EVENT_LANCES_ROOM_OAK_AND_MARY = 1887, - EVENT_LANCE_HEALED_YOU_IN_TEAM_ROCKET_BASE = 76, - EVENT_LEARNED_HAIL_GIOVANNI = 767, - EVENT_LEARNED_RATICATE_TAIL = 770, - EVENT_LEARNED_SLOWPOKETAIL = 769, - EVENT_LEARNED_TO_CATCH_POKEMON = 66, - EVENT_LEFT_MONS_WITH_CONTEST_OFFICER = 776, - EVENT_LISTENED_TO_FAN_CLUB_PRESIDENT = 212, - EVENT_LISTENED_TO_FAN_CLUB_PRESIDENT_BUT_BAG_WAS_FULL = 211, - EVENT_LISTENED_TO_INITIAL_RADIO = 805, - EVENT_LIZ_ASKED_FOR_PHONE_NUMBER = 633, - EVENT_LIZ_READY_FOR_REMATCH = 634, - EVENT_MADE_UNOWN_APPEAR_IN_RUINS = 46, - EVENT_MADE_WHITNEY_CRY = 40, - EVENT_MAHOGANY_MART_LANCE_AND_DRAGONITE = 1749, - EVENT_MAHOGANY_MART_OWNERS = 1846, - EVENT_MAHOGANY_TOWN_POKEFAN_M_BLOCKS_EAST = 1878, - EVENT_MAHOGANY_TOWN_POKEFAN_M_BLOCKS_GYM = 1879, - EVENT_MANIA_TOOK_SHUCKIE_OR_LET_YOU_KEEP_HIM = 70, - EVENT_MET_ARTHUR_OF_THURSDAY = 102, - EVENT_MET_BILL = 1810, - EVENT_MET_BILLS_GRANDPA = 794, - EVENT_MET_COPYCAT_FOUND_OUT_ABOUT_LOST_ITEM = 207, - EVENT_MET_FRIEDA_OF_FRIDAY = 98, - EVENT_MET_MANAGER_AT_POWER_PLANT = 202, - EVENT_MET_MONICA_OF_MONDAY = 110, - EVENT_MET_REDS_MOM = 204, - EVENT_MET_ROCKET_GRUNT_AT_CERULEAN_GYM = 203, - EVENT_MET_SANTOS_OF_SATURDAY = 108, - EVENT_MET_SUNNY_OF_SUNDAY = 104, - EVENT_MET_TUSCANY_OF_TUESDAY = 100, - EVENT_MET_WESLEY_OF_WEDNESDAY = 106, - EVENT_MOUNT_MOON_SQUARE_HIDDEN_MOON_STONE = 236, - EVENT_MOUNT_MORTAR_1F_INSIDE_ESCAPE_ROPE = 1661, - EVENT_MOUNT_MORTAR_1F_INSIDE_HIDDEN_MAX_REPEL = 145, - EVENT_MOUNT_MORTAR_1F_INSIDE_HYPER_POTION = 1663, - EVENT_MOUNT_MORTAR_1F_INSIDE_MAX_REVIVE = 1662, - EVENT_MOUNT_MORTAR_1F_OUTSIDE_ETHER = 1659, - EVENT_MOUNT_MORTAR_1F_OUTSIDE_HIDDEN_HYPER_POTION = 144, - EVENT_MOUNT_MORTAR_1F_OUTSIDE_REVIVE = 1660, - EVENT_MOUNT_MORTAR_2F_INSIDE_DRAGON_SCALE = 1667, - EVENT_MOUNT_MORTAR_2F_INSIDE_ELIXER = 1668, - EVENT_MOUNT_MORTAR_2F_INSIDE_ESCAPE_ROPE = 1669, - EVENT_MOUNT_MORTAR_2F_INSIDE_HIDDEN_FULL_RESTORE = 146, - EVENT_MOUNT_MORTAR_2F_INSIDE_MAX_POTION = 1664, - EVENT_MOUNT_MORTAR_2F_INSIDE_RARE_CANDY = 1665, - EVENT_MOUNT_MORTAR_2F_INSIDE_TM_DEFENSE_CURL = 1666, - EVENT_MOUNT_MORTAR_B1F_FULL_HEAL = 1671, - EVENT_MOUNT_MORTAR_B1F_HIDDEN_MAX_REVIVE = 147, - EVENT_MOUNT_MORTAR_B1F_HYPER_POTION = 1670, - EVENT_MR_POKEMONS_HOUSE_OAK = 1737, - EVENT_MT_MOON_RIVAL = 1914, - EVENT_MT_MOON_SQUARE_CLEFAIRY = 1913, - EVENT_MT_MOON_SQUARE_ROCK = 1912, - EVENT_MYSTERY_GIFT_DELIVERY_GUY = 1809, - EVENT_NATIONAL_PARK_HIDDEN_FULL_HEAL = 132, - EVENT_NATIONAL_PARK_PARLYZ_HEAL = 1624, - EVENT_NATIONAL_PARK_TM_DIG = 1625, - EVENT_OLIVINE_GYM_JASMINE = 1747, - EVENT_OLIVINE_LIGHTHOUSE_3F_ETHER = 1636, - EVENT_OLIVINE_LIGHTHOUSE_5F_GREAT_BALL = 1638, - EVENT_OLIVINE_LIGHTHOUSE_5F_HIDDEN_HYPER_POTION = 133, - EVENT_OLIVINE_LIGHTHOUSE_5F_RARE_CANDY = 1637, - EVENT_OLIVINE_LIGHTHOUSE_5F_TM_SWAGGER = 1639, - EVENT_OLIVINE_LIGHTHOUSE_6F_SUPER_POTION = 1640, - EVENT_OLIVINE_LIGHTHOUSE_JASMINE = 1746, - EVENT_OLIVINE_PORT_HIDDEN_PROTEIN = 234, - EVENT_OLIVINE_PORT_PASSAGE_POKEFAN_M = 1843, - EVENT_OLIVINE_PORT_SAILOR_AT_GANGWAY = 1834, - EVENT_OLIVINE_PORT_SPRITES_AFTER_HALL_OF_FAME = 1848, - EVENT_OLIVINE_PORT_SPRITES_BEFORE_HALL_OF_FAME = 1847, - EVENT_OPENED_DOOR_TO_GIOVANNIS_OFFICE = 771, - EVENT_OPENED_DOOR_TO_ROCKET_HIDEOUT_TRANSMITTER = 768, - EVENT_OPENED_MT_SILVER = 1871, - EVENT_PARRY_ASKED_FOR_PHONE_NUMBER = 667, - EVENT_PARRY_READY_FOR_REMATCH = 668, - EVENT_PICKED_UP_FOCUS_BAND = 1917, - EVENT_PLAYERS_HOUSE_2F_BIG_DOLL = 1860, - EVENT_PLAYERS_HOUSE_2F_CONSOLE = 1857, - EVENT_PLAYERS_HOUSE_2F_DOLL_1 = 1858, - EVENT_PLAYERS_HOUSE_2F_DOLL_2 = 1859, - EVENT_PLAYERS_HOUSE_MOM_1 = 1735, - EVENT_PLAYERS_HOUSE_MOM_2 = 1736, - EVENT_PLAYERS_ROOM_POSTER = 716, - EVENT_RADIO_TOWER_BLACKBELT_BLOCKS_STAIRS = 1745, - EVENT_RADIO_TOWER_CIVILIANS_AFTER = 1744, - EVENT_RADIO_TOWER_ROCKET_TAKEOVER = 1742, - EVENT_RALPH_ASKED_FOR_PHONE_NUMBER = 631, - EVENT_RALPH_READY_FOR_REMATCH = 632, - EVENT_RECEIVED_CARD_KEY = 74, - EVENT_RED_IN_MT_SILVER = 1890, - EVENT_REENA_ASKED_FOR_PHONE_NUMBER = 625, - EVENT_REENA_READY_FOR_REMATCH = 626, - EVENT_REFUSED_TO_HELP_LANCE_AT_LAKE_OF_RAGE = 38, - EVENT_REFUSED_TO_TAKE_EGG_FROM_ELMS_AIDE = 44, - EVENT_RELEASED_THE_BEASTS = 123, - EVENT_RESTORED_POWER_TO_KANTO = 205, - EVENT_RETURNED_LOST_ITEM_TO_COPYCAT = 208, - EVENT_RETURNED_MACHINE_PART = 201, - EVENT_RIVAL_AZALEA_TOWN = 1727, - EVENT_RIVAL_BURNED_TOWER = 1733, - EVENT_RIVAL_CHERRYGROVE_CITY = 1726, - EVENT_RIVAL_DRAGONS_DEN = 1734, - EVENT_RIVAL_GOLDENROD_UNDERGROUND = 1729, - EVENT_RIVAL_NEW_BARK_TOWN = 1725, - EVENT_RIVAL_OLIVINE_CITY = 1731, - EVENT_RIVAL_SPROUT_TOWER = 1732, - EVENT_RIVAL_TEAM_ROCKET_BASE = 1728, - EVENT_RIVAL_VICTORY_ROAD = 1730, - EVENT_ROCK_TUNNEL_1F_ELIXER = 1918, - EVENT_ROCK_TUNNEL_1F_HIDDEN_X_ACCURACY = 231, - EVENT_ROCK_TUNNEL_1F_HIDDEN_X_DEFEND = 232, - EVENT_ROCK_TUNNEL_1F_TM_STEEL_WING = 1919, - EVENT_ROCK_TUNNEL_B1F_HIDDEN_MAX_POTION = 233, - EVENT_ROCK_TUNNEL_B1F_IRON = 1920, - EVENT_ROCK_TUNNEL_B1F_PP_UP = 1921, - EVENT_ROCK_TUNNEL_B1F_REVIVE = 1922, - EVENT_ROUTE_11_HIDDEN_REVIVE = 245, - EVENT_ROUTE_12_CALCIUM = 1928, - EVENT_ROUTE_12_HIDDEN_ELIXER = 243, - EVENT_ROUTE_12_NUGGET = 1929, - EVENT_ROUTE_13_HIDDEN_CALCIUM = 244, - EVENT_ROUTE_15_PP_UP = 1930, - EVENT_ROUTE_17_HIDDEN_MAX_ELIXER = 247, - EVENT_ROUTE_17_HIDDEN_MAX_ETHER = 246, - EVENT_ROUTE_24_ROCKET = 1900, - EVENT_ROUTE_25_HIDDEN_POTION = 248, - EVENT_ROUTE_25_MISTY_BOYFRIEND = 1902, - EVENT_ROUTE_25_PROTEIN = 1931, - EVENT_ROUTE_26_MAX_ELIXER = 1706, - EVENT_ROUTE_27_RARE_CANDY = 1708, - EVENT_ROUTE_27_TM_SOLARBEAM = 1707, - EVENT_ROUTE_28_HIDDEN_RARE_CANDY = 163, - EVENT_ROUTE_29_POTION = 1709, - EVENT_ROUTE_29_TUSCANY_OF_TUESDAY = 1881, - EVENT_ROUTE_2_CARBOS = 1925, - EVENT_ROUTE_2_DIRE_HIT = 1923, - EVENT_ROUTE_2_ELIXER = 1926, - EVENT_ROUTE_2_HIDDEN_FULL_HEAL = 238, - EVENT_ROUTE_2_HIDDEN_FULL_RESTORE = 239, - EVENT_ROUTE_2_HIDDEN_MAX_ETHER = 237, - EVENT_ROUTE_2_HIDDEN_REVIVE = 240, - EVENT_ROUTE_2_MAX_POTION = 1924, - EVENT_ROUTE_30_BATTLE = 1812, - EVENT_ROUTE_30_HIDDEN_POTION = 164, - EVENT_ROUTE_30_YOUNGSTER_JOEY = 1813, - EVENT_ROUTE_31_ANTIDOTE = 1710, - EVENT_ROUTE_31_POKE_BALL = 1711, - EVENT_ROUTE_32_FRIEDA_OF_FRIDAY = 1880, - EVENT_ROUTE_32_GREAT_BALL = 1712, - EVENT_ROUTE_32_HIDDEN_GREAT_BALL = 165, - EVENT_ROUTE_32_HIDDEN_SUPER_POTION = 166, - EVENT_ROUTE_32_POTION = 1713, - EVENT_ROUTE_34_HIDDEN_RARE_CANDY = 167, - EVENT_ROUTE_34_HIDDEN_SUPER_POTION = 168, - EVENT_ROUTE_35_NATIONAL_PARK_GATE_OFFICER_CONTEST_DAY = 1861, - EVENT_ROUTE_35_NATIONAL_PARK_GATE_OFFICER_NOT_CONTEST_DAY = 1862, - EVENT_ROUTE_35_NATIONAL_PARK_GATE_YOUNGSTER = 1844, - EVENT_ROUTE_35_TM_ROLLOUT = 1714, - EVENT_ROUTE_36_ARTHUR_OF_THURSDAY = 1882, - EVENT_ROUTE_36_NATIONAL_PARK_GATE_OFFICER_CONTEST_DAY = 1863, - EVENT_ROUTE_36_NATIONAL_PARK_GATE_OFFICER_NOT_CONTEST_DAY = 1864, - EVENT_ROUTE_36_SUDOWOODO = 1784, - EVENT_ROUTE_37_HIDDEN_ETHER = 169, - EVENT_ROUTE_37_SUNNY_OF_SUNDAY = 1883, - EVENT_ROUTE_39_HIDDEN_NUGGET = 170, - EVENT_ROUTE_40_HIDDEN_HYPER_POTION = 171, - EVENT_ROUTE_40_MONICA_OF_MONDAY = 1886, - EVENT_ROUTE_41_HIDDEN_MAX_ETHER = 172, - EVENT_ROUTE_42_HIDDEN_MAX_POTION = 173, - EVENT_ROUTE_42_SUPER_POTION = 1716, - EVENT_ROUTE_42_ULTRA_BALL = 1715, - EVENT_ROUTE_43_GATE_ROCKETS = 1756, - EVENT_ROUTE_43_MAX_ETHER = 1717, - EVENT_ROUTE_44_HIDDEN_ELIXER = 174, - EVENT_ROUTE_44_MAX_REVIVE = 1718, - EVENT_ROUTE_44_ULTRA_BALL = 1719, - EVENT_ROUTE_45_ELIXER = 1722, - EVENT_ROUTE_45_HIDDEN_PP_UP = 175, - EVENT_ROUTE_45_MAX_POTION = 1723, - EVENT_ROUTE_45_REVIVE = 1721, - EVENT_ROUTE_45_X_SPECIAL = 1720, - EVENT_ROUTE_46_DIRE_HIT = 1724, - EVENT_ROUTE_4_HIDDEN_ULTRA_BALL = 241, - EVENT_ROUTE_4_HP_UP = 1927, - EVENT_ROUTE_5_6_POKEFAN_M_BLOCKS_UNDERGROUND_PATH = 1905, - EVENT_ROUTE_9_HIDDEN_ETHER = 242, - EVENT_RUINS_OF_ALPH_INNER_CHAMBER_TOURISTS = 1797, - EVENT_RUINS_OF_ALPH_KABUTO_CHAMBER_RECEPTIONIST = 1870, - EVENT_RUINS_OF_ALPH_OUTSIDE_SCIENTIST = 1795, - EVENT_RUINS_OF_ALPH_RESEARCH_CENTER_SCIENTIST = 1796, - EVENT_SAFFRON_TRAIN_STATION_POPULATION = 1906, - EVENT_SEAFOAM_GYM_GYM_GUIDE = 1911, - EVENT_SECURITY_CAMERA_1 = 740, - EVENT_SECURITY_CAMERA_2 = 741, - EVENT_SECURITY_CAMERA_3 = 742, - EVENT_SECURITY_CAMERA_4 = 743, - EVENT_SECURITY_CAMERA_5 = 744, - EVENT_SHOWED_GROWLITHE_VULPIX_TO_BILLS_GRANDPA = 798, - EVENT_SHOWED_LICKITUNG_TO_BILLS_GRANDPA = 795, - EVENT_SHOWED_ODDISH_TO_BILLS_GRANDPA = 796, - EVENT_SHOWED_PICHU_TO_BILLS_GRANDPA = 799, - EVENT_SHOWED_STARYU_TO_BILLS_GRANDPA = 797, - EVENT_SHOWED_TOGEPI_TO_ELM = 85, - EVENT_SILVER_CAVE_ITEM_ROOMS_FULL_RESTORE = 1693, - EVENT_SILVER_CAVE_ITEM_ROOMS_MAX_REVIVE = 1692, - EVENT_SILVER_CAVE_OUTSIDE_HIDDEN_FULL_RESTORE = 184, - EVENT_SILVER_CAVE_ROOM_1_ESCAPE_ROPE = 1691, - EVENT_SILVER_CAVE_ROOM_1_HIDDEN_DIRE_HIT = 154, - EVENT_SILVER_CAVE_ROOM_1_HIDDEN_ULTRA_BALL = 155, - EVENT_SILVER_CAVE_ROOM_1_MAX_ELIXER = 1689, - EVENT_SILVER_CAVE_ROOM_1_X_ACCURACY = 1690, - EVENT_SILVER_CAVE_ROOM_2_HIDDEN_MAX_POTION = 156, - EVENT_SLOWPOKE_WELL_B1F_SUPER_POTION = 1634, - EVENT_SLOWPOKE_WELL_B2F_TM_RAIN_DANCE = 1635, - EVENT_SLOWPOKE_WELL_KURT = 1856, - EVENT_SLOWPOKE_WELL_ROCKETS = 1788, - EVENT_SLOWPOKE_WELL_SLOWPOKES = 1787, - EVENT_SOLVED_AERODACTYL_PUZZLE = 675, - EVENT_SOLVED_HO_OH_PUZZLE = 672, - EVENT_SOLVED_KABUTO_PUZZLE = 673, - EVENT_SOLVED_OMANYTE_PUZZLE = 674, - EVENT_SPROUT_TOWER_1F_PARLYZ_HEAL = 1607, - EVENT_SPROUT_TOWER_2F_X_DEFEND = 1608, - EVENT_SPROUT_TOWER_3F_ESCAPE_ROPE = 1610, - EVENT_SPROUT_TOWER_3F_POTION = 1609, - EVENT_SWITCH_1 = 723, - EVENT_SWITCH_2 = 724, - EVENT_SWITCH_3 = 725, - EVENT_TALKED_TO_FARMER_ABOUT_MOOMOO = 63, - EVENT_TALKED_TO_MOM_AFTER_MYSTERY_EGG_QUEST = 64, - EVENT_TALKED_TO_OAK_IN_KANTO = 225, - EVENT_TALKED_TO_SEAFOAM_GYM_GUIDE_ONCE = 213, - EVENT_TALKED_TO_WARDENS_GRANDDAUGHTER = 217, - EVENT_TEAM_ROCKET_BASE_B1F_HIDDEN_REVIVE = 134, - EVENT_TEAM_ROCKET_BASE_B1F_HYPER_POTION = 1641, - EVENT_TEAM_ROCKET_BASE_B1F_NUGGET = 1642, - EVENT_TEAM_ROCKET_BASE_B1F_X_ACCURACY = 1643, - EVENT_TEAM_ROCKET_BASE_B2F_DRAGONITE = 1759, - EVENT_TEAM_ROCKET_BASE_B2F_ELECTRODE_1 = 1760, - EVENT_TEAM_ROCKET_BASE_B2F_ELECTRODE_2 = 1761, - EVENT_TEAM_ROCKET_BASE_B2F_ELECTRODE_3 = 1762, - EVENT_TEAM_ROCKET_BASE_B2F_EXECUTIVE = 1757, - EVENT_TEAM_ROCKET_BASE_B2F_GRUNT_WITH_EXECUTIVE = 1758, - EVENT_TEAM_ROCKET_BASE_B2F_HIDDEN_FULL_HEAL = 135, - EVENT_TEAM_ROCKET_BASE_B2F_LANCE = 1750, - EVENT_TEAM_ROCKET_BASE_B2F_TM_THIEF = 1644, - EVENT_TEAM_ROCKET_BASE_B3F_DIRE_HIT = 1646, - EVENT_TEAM_ROCKET_BASE_B3F_EXECUTIVE = 1755, - EVENT_TEAM_ROCKET_BASE_B3F_FULL_HEAL = 1645, - EVENT_TEAM_ROCKET_BASE_B3F_ICE_HEAL = 1648, - EVENT_TEAM_ROCKET_BASE_B3F_LANCE_PASSWORDS = 1751, - EVENT_TEAM_ROCKET_BASE_B3F_PROTEIN = 1647, - EVENT_TEAM_ROCKET_BASE_POPULATION = 1754, - EVENT_TEAM_ROCKET_BASE_SECURITY_GRUNTS = 1753, - EVENT_TEAM_ROCKET_DISBANDED = 1889, - EVENT_TELEPORT_GUY = 1916, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_1 = 0, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_2 = 1, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_3 = 2, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_4 = 3, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_5 = 4, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_6 = 5, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_7 = 6, - EVENT_TEMPORARY_UNTIL_MAP_RELOAD_8 = 7, - EVENT_TIFFANY_ASKED_FOR_PHONE_NUMBER = 659, - EVENT_TIFFANY_READY_FOR_REMATCH = 660, - EVENT_TIN_TOWER_3F_FULL_HEAL = 1611, - EVENT_TIN_TOWER_4F_ESCAPE_ROPE = 1614, - EVENT_TIN_TOWER_4F_HIDDEN_MAX_POTION = 125, - EVENT_TIN_TOWER_4F_SUPER_POTION = 1613, - EVENT_TIN_TOWER_4F_ULTRA_BALL = 1612, - EVENT_TIN_TOWER_5F_HIDDEN_CARBOS = 127, - EVENT_TIN_TOWER_5F_HIDDEN_FULL_RESTORE = 126, - EVENT_TIN_TOWER_5F_RARE_CANDY = 1615, - EVENT_TIN_TOWER_7F_MAX_REVIVE = 1616, - EVENT_TIN_TOWER_8F_FULL_RESTORE = 1619, - EVENT_TIN_TOWER_8F_MAX_ELIXER = 1618, - EVENT_TIN_TOWER_8F_NUGGET = 1617, - EVENT_TIN_TOWER_ROOF_HO_OH = 1852, - EVENT_TODD_ASKED_FOR_PHONE_NUMBER = 637, - EVENT_TODD_READY_FOR_REMATCH = 638, - EVENT_TOGEPI_HATCHED = 84, - EVENT_TOHJO_FALLS_MOON_STONE = 1705, - EVENT_TOLD_ELM_ABOUT_TOGEPI_OVER_THE_PHONE = 119, - EVENT_TOTODILE_POKEBALL_IN_ELMS_LAB = 1601, - EVENT_TRAINERS_IN_CERULEAN_GYM = 1903, - EVENT_TURNED_OFF_SECURITY_CAMERAS = 739, - EVENT_UNCOVERED_STAIRCASE_IN_MAHOGANY_MART = 738, - EVENT_UNDERGROUND_PATH_HIDDEN_FULL_RESTORE = 229, - EVENT_UNDERGROUND_PATH_HIDDEN_X_SPECIAL = 230, - EVENT_UNION_CAVE_1F_AWAKENING = 1629, - EVENT_UNION_CAVE_1F_GREAT_BALL = 1626, - EVENT_UNION_CAVE_1F_POTION = 1627, - EVENT_UNION_CAVE_1F_X_ATTACK = 1628, - EVENT_UNION_CAVE_B1F_TM_SWIFT = 1630, - EVENT_UNION_CAVE_B1F_X_DEFEND = 1631, - EVENT_UNION_CAVE_B2F_ELIXER = 1632, - EVENT_UNION_CAVE_B2F_HYPER_POTION = 1633, - EVENT_UNION_CAVE_B2F_LAPRAS = 1888, - EVENT_USED_BASEMENT_KEY = 73, - EVENT_USED_THE_CARD_KEY_IN_THE_RADIO_TOWER = 37, - EVENT_VANCE_ASKED_FOR_PHONE_NUMBER = 661, - EVENT_VANCE_READY_FOR_REMATCH = 662, - EVENT_VERMILION_CITY_HIDDEN_FULL_HEAL = 252, - EVENT_VERMILION_CITY_SNORLAX = 1904, - EVENT_VERMILION_FAN_CLUB_DOLL = 1908, - EVENT_VERMILION_PORT_HIDDEN_IRON = 235, - EVENT_VERMILION_PORT_SAILOR_AT_GANGWAY = 1835, - EVENT_VICTORY_ROAD_FULL_HEAL = 1702, - EVENT_VICTORY_ROAD_FULL_RESTORE = 1701, - EVENT_VICTORY_ROAD_HIDDEN_FULL_HEAL = 159, - EVENT_VICTORY_ROAD_HIDDEN_MAX_POTION = 158, - EVENT_VICTORY_ROAD_MAX_REVIVE = 1700, - EVENT_VICTORY_ROAD_TM_EARTHQUAKE = 1699, - EVENT_VICTORY_ROAD_X_SPECIAL = 1703, - EVENT_VIOLET_CITY_EARL = 1738, - EVENT_VIOLET_CITY_HIDDEN_HYPER_POTION = 176, - EVENT_VIOLET_CITY_PP_UP = 1603, - EVENT_VIOLET_CITY_RARE_CANDY = 1604, - EVENT_VIRIDIAN_GYM_BLUE = 1910, - EVENT_WADE_ASKED_FOR_PHONE_NUMBER = 629, - EVENT_WADE_READY_FOR_REMATCH = 630, - EVENT_WARPED_FROM_ROUTE_35_NATIONAL_PARK_GATE = 722, - EVENT_WHIRL_ISLAND_B1F_CALCIUM = 1683, - EVENT_WHIRL_ISLAND_B1F_CARBOS = 1682, - EVENT_WHIRL_ISLAND_B1F_ESCAPE_ROPE = 1685, - EVENT_WHIRL_ISLAND_B1F_FULL_RESTORE = 1681, - EVENT_WHIRL_ISLAND_B1F_HIDDEN_FULL_RESTORE = 153, - EVENT_WHIRL_ISLAND_B1F_HIDDEN_RARE_CANDY = 151, - EVENT_WHIRL_ISLAND_B1F_HIDDEN_ULTRA_BALL = 152, - EVENT_WHIRL_ISLAND_B1F_NUGGET = 1684, - EVENT_WHIRL_ISLAND_B2F_FULL_RESTORE = 1686, - EVENT_WHIRL_ISLAND_B2F_MAX_ELIXER = 1688, - EVENT_WHIRL_ISLAND_B2F_MAX_REVIVE = 1687, - EVENT_WHIRL_ISLAND_LUGIA_CHAMBER_LUGIA = 1853, - EVENT_WHIRL_ISLAND_NE_ULTRA_BALL = 1679, - EVENT_WHIRL_ISLAND_SW_GUARD_SPEC = 1680, - EVENT_WILLS_ROOM_ENTRANCE_CLOSED = 777, - EVENT_WILLS_ROOM_EXIT_OPEN = 778, - EVENT_WILTON_ASKED_FOR_PHONE_NUMBER = 663, - EVENT_WILTON_READY_FOR_REMATCH = 664, - }, - engine = { - ENGINE_ALL_FRUIT_TREES = 83, - ENGINE_ALWAYS_ON_BIKE = 24, - ENGINE_BIKE_SHOP_CALL_ENABLED = 19, - ENGINE_BOULDERBADGE = 34, - ENGINE_BUG_CONTEST_TIMER = 16, - ENGINE_CASCADEBADGE = 35, - ENGINE_CAUGHT_POKERUS = 13, - ENGINE_CREDITS_SKIP = 15, - ENGINE_DAILY_BUG_CONTEST = 80, - ENGINE_DAISYS_GROOMING = 91, - ENGINE_DAY_CARE_LADY_HAS_MON = 7, - ENGINE_DAY_CARE_MAN_HAS_EGG = 5, - ENGINE_DAY_CARE_MAN_HAS_MON = 6, - ENGINE_DOWNHILL = 25, - ENGINE_EARTHBADGE = 41, - ENGINE_EXPN_CARD = 3, - ENGINE_FLYPOINT_AZALEA = 67, - ENGINE_FLYPOINT_BLACKTHORN = 74, - ENGINE_FLYPOINT_CELADON = 60, - ENGINE_FLYPOINT_CERULEAN = 55, - ENGINE_FLYPOINT_CHERRYGROVE = 65, - ENGINE_FLYPOINT_CIANWOOD = 68, - ENGINE_FLYPOINT_CINNABAR = 62, - ENGINE_FLYPOINT_DEBUG = 51, - ENGINE_FLYPOINT_ECRUTEAK = 71, - ENGINE_FLYPOINT_FUCHSIA = 61, - ENGINE_FLYPOINT_GOLDENROD = 69, - ENGINE_FLYPOINT_INDIGO_PLATEAU = 63, - ENGINE_FLYPOINT_LAKE_OF_RAGE = 73, - ENGINE_FLYPOINT_LAVENDER = 58, - ENGINE_FLYPOINT_MAHOGANY = 72, - ENGINE_FLYPOINT_NEW_BARK = 64, - ENGINE_FLYPOINT_OLIVINE = 70, - ENGINE_FLYPOINT_PALLET = 52, - ENGINE_FLYPOINT_PEWTER = 54, - ENGINE_FLYPOINT_PLAYERS_HOUSE = 50, - ENGINE_FLYPOINT_ROCK_TUNNEL = 56, - ENGINE_FLYPOINT_SAFFRON = 59, - ENGINE_FLYPOINT_SILVER_CAVE = 75, - ENGINE_FLYPOINT_UNUSED = 76, - ENGINE_FLYPOINT_VERMILION = 57, - ENGINE_FLYPOINT_VIOLET = 66, - ENGINE_FLYPOINT_VIRIDIAN = 53, - ENGINE_FOGBADGE = 29, - ENGINE_FOUGHT_IN_TRAINER_HALL_TODAY = 86, - ENGINE_GLACIERBADGE = 32, - ENGINE_GOLDENROD_DEPT_STORE_TM27_RETURN = 90, - ENGINE_GOLDENROD_UNDERGROUND_GOT_HAIRCUT = 89, - ENGINE_GOLDENROD_UNDERGROUND_MERCHANT_CLOSED = 85, - ENGINE_GOT_SHUCKIE_TODAY = 84, - ENGINE_HIVEBADGE = 27, - ENGINE_INDIGO_PLATEAU_RIVAL_FIGHT = 92, - ENGINE_KURT_MAKING_BALLS = 79, - ENGINE_LUCKY_NUMBER_SHOW = 77, - ENGINE_MAP_CARD = 1, - ENGINE_MARSHBADGE = 39, - ENGINE_MINERALBADGE = 30, - ENGINE_MOM_ACTIVE = 9, - ENGINE_MOM_SAVING_MONEY = 8, - ENGINE_MT_MOON_SQUARE_CLEFAIRY = 87, - ENGINE_PHONE_CARD = 2, - ENGINE_PLAINBADGE = 28, - ENGINE_POKEDEX = 11, - ENGINE_POKEGEAR = 4, - ENGINE_RADIO_CARD = 0, - ENGINE_RAINBOWBADGE = 37, - ENGINE_REACHED_GOLDENROD = 21, - ENGINE_RISINGBADGE = 33, - ENGINE_ROCKETS_IN_MAHOGANY = 22, - ENGINE_ROCKETS_IN_RADIO_TOWER = 18, - ENGINE_ROCKET_SIGNAL_ON_CH20 = 14, - ENGINE_SAFARI_ZONE = 17, - ENGINE_SOULBADGE = 38, - ENGINE_STORMBADGE = 31, - ENGINE_STRENGTH_ACTIVE = 23, - ENGINE_SWARM = 81, - ENGINE_THUNDERBADGE = 36, - ENGINE_TIME_CAPSULE = 82, - ENGINE_UNION_CAVE_LAPRAS = 88, - ENGINE_UNLOCKED_UNOWNS_A_TO_K = 42, - ENGINE_UNLOCKED_UNOWNS_L_TO_R = 43, - ENGINE_UNLOCKED_UNOWNS_S_TO_W = 44, - ENGINE_UNLOCKED_UNOWNS_UNUSED_4 = 46, - ENGINE_UNLOCKED_UNOWNS_UNUSED_5 = 47, - ENGINE_UNLOCKED_UNOWNS_UNUSED_6 = 48, - ENGINE_UNLOCKED_UNOWNS_UNUSED_7 = 49, - ENGINE_UNLOCKED_UNOWNS_X_TO_Z = 45, - ENGINE_UNOWN_DEX = 12, - ENGINE_UNUSED_STATUSFLAGS2_3 = 78, - ENGINE_UNUSED_STATUSFLAGS2_5 = 20, - ENGINE_UNUSED_TWO_DAY_TIMER_ON = 10, - ENGINE_VOLCANOBADGE = 40, - ENGINE_ZEPHYRBADGE = 26, - }, -} +-- Re-export the shipped table so drivers keep this path. +return dofile("src/core/gen2/FlagNames.lua") diff --git a/tests/engine/launcher_mod_scope_dropdown.lua b/tests/engine/launcher_mod_scope_dropdown.lua new file mode 100644 index 00000000..d1758c96 --- /dev/null +++ b/tests/engine/launcher_mod_scope_dropdown.lua @@ -0,0 +1,62 @@ +-- Mods "Show for" chips collapse to a dropdown when they cannot all fit. +-- Landscape / desktop keep the individual game chips. No pokered cite: the +-- launcher is port-only chrome. +-- luajit tests/engine/launcher_mod_scope_dropdown.lua + +package.path = "./?.lua;./?/init.lua;" .. package.path + +local T = require("tests.harness") +local check = T.check +love = love or require("tests.love_stub") + +love.graphics.setLineJoin = love.graphics.setLineJoin or function() end +love.graphics.newShader = love.graphics.newShader or function() return {} end + +local Kit = require("src.ui.kit.Kit") +local RomImporter = require("src.import.RomImporter") +local LauncherView = require("src.import.LauncherView") + +local function window(w, h) + love.graphics.getDimensions = function() return w, h end + love.graphics.getPixelDimensions = function() return w, h end +end + +local function navIds() + local ids = {} + for i = 1, (Kit._navPrevN or 0) do + local slot = Kit._nav[i] + if slot and slot.id then ids[slot.id] = true end + end + return ids +end + +local function drawMods(W, H) + window(W, H) + local imp = RomImporter.new(function() end, { launcher = true }) + imp.tab = "mods" + imp.ready = { red = true, blue = true, yellow = true, gold = true } + local ok, err = pcall(LauncherView.draw, imp) + check(ok, ("%dx%d mods draws: %s"):format(W, H, tostring(err))) + return navIds() +end + +local portrait = drawMods(360, 780) +check(portrait["mod-scope-menu"] == true, + "portrait: Show-for collapses to a dropdown when the chips cannot all fit") +check(portrait["mod-scope-gold"] == nil + and portrait["mod-scope-red"] == nil + and portrait["mod-scope-blue"] == nil + and portrait["mod-scope-yellow"] == nil, + "portrait: individual version chips are not drawn beside the dropdown") + +local desktop = drawMods(1280, 720) +check(desktop["mod-scope-menu"] == nil, + "desktop: chips fit, so there is no dropdown") +check(desktop["mod-scope-all"] == true + and desktop["mod-scope-red"] == true + and desktop["mod-scope-blue"] == true + and desktop["mod-scope-yellow"] == true + and desktop["mod-scope-gold"] == true, + "desktop: every Show-for chip stays reachable") + +print("ok launcher mod scope dropdown") diff --git a/tests/engine/launcher_modal_focus_input_test.lua b/tests/engine/launcher_modal_focus_input_test.lua index 3d394e4d..bb198000 100644 --- a/tests/engine/launcher_modal_focus_input_test.lua +++ b/tests/engine/launcher_modal_focus_input_test.lua @@ -23,7 +23,7 @@ local function importer(field) end local modalFields = { - "_modConfirm", "_modVersions", "_modReleaseNotes", "_findDetails", + "_modConfirm", "_modVersions", "_modReleaseNotes", "_appPatchNotes", "_findDetails", } for _, field in ipairs(modalFields) do @@ -77,6 +77,12 @@ do imp:keypressed("escape") eq(imp._modReleaseNotes, nil, "Escape closes release notes") end +do + resetFocus() + local imp = importer("_appPatchNotes") + imp:keypressed("escape") + eq(imp._appPatchNotes, nil, "Escape closes patch notes") +end do resetFocus() local imp = importer("_modVersions") diff --git a/tests/engine/launcher_patch_notes.lua b/tests/engine/launcher_patch_notes.lua new file mode 100644 index 00000000..ebdffb78 --- /dev/null +++ b/tests/engine/launcher_patch_notes.lua @@ -0,0 +1,89 @@ +-- Launcher footer Patch notes control. The GitHub release body is already +-- fetched by the updater; this is the in-app viewer for it. +-- luajit tests/engine/launcher_patch_notes.lua + +package.path = "./?.lua;./?/init.lua;" .. package.path + +local T = require("tests.harness") +local check, eq = T.check, T.eq +love = love or require("tests.love_stub") + +love.graphics.setLineJoin = love.graphics.setLineJoin or function() end +love.graphics.newShader = love.graphics.newShader or function() return {} end + +local Kit = require("src.ui.kit.Kit") +local RomImporter = require("src.import.RomImporter") +local LauncherView = require("src.import.LauncherView") +local PatchNotes = require("src.update.PatchNotes") + +local function window(w, h) + love.graphics.getDimensions = function() return w, h end + love.graphics.getPixelDimensions = function() return w, h end +end + +local function freshLauncher() + return RomImporter.new(function() end, { launcher = true }) +end + +local realPrint = love.graphics.print +local function drawAndCapture(imp) + local seen = {} + love.graphics.print = function(str, ...) + seen[#seen + 1] = tostring(str) + return realPrint(str, ...) + end + local ok, err = pcall(LauncherView.draw, imp) + love.graphics.print = realPrint + check(ok, "the frame draws: " .. tostring(err)) + return table.concat(seen, "\n") +end + +window(1280, 720) +local imp = freshLauncher() +local text = drawAndCapture(imp) +check(text:find("Patch notes", 1, true) ~= nil, + "desktop footer prints Patch notes") + +window(360, 780) +local phone = freshLauncher() +check(drawAndCapture(phone):find("Patch notes", 1, true) ~= nil, + "portrait footer still prints Patch notes") + +do + local body, ver = PatchNotes.body({ + state = function() + return { notes = "## Issues closed\n\n- #12 cart padding", latest = "1.4.2" } + end, + }) + eq(body, "## Issues closed\n\n- #12 cart padding", + "PatchNotes prefers the updater's GitHub body") + eq(ver, "1.4.2", "PatchNotes carries the release version") +end + +do + local body, ver = PatchNotes.body(nil) + check(type(body) == "string" and body:find("Issues closed", 1, true), + "without a check result PatchNotes uses the stashed iOS app-repo notes") + check(type(ver) == "string" and ver:find("^%d+%.%d+%.%d+$") ~= nil, + "stashed notes name a release version") +end + +do + local f = assert(io.open("mobile/ios/app-repo.json", "rb")) + local list = PatchNotes.parseRepo(f:read("*a")) + f:close() + check(#list >= 2, "app-repo.json stashes more than one release") + local notes, ver = PatchNotes.fromRepo(list[2].version) + eq(ver, list[2].version, "fromRepo can pick a specific stashed version") + eq(notes, list[2].notes, "fromRepo returns that version's notes") +end + +imp._appPatchNotes = true +local modal = drawAndCapture(imp) +check(modal:find("Patch notes", 1, true) ~= nil, "the modal titles itself") +check(modal:find("Close", 1, true) ~= nil, "the modal can be closed") + +imp:keypressed("escape") +eq(imp._appPatchNotes, nil, "Escape dismisses patch notes") + +T.finish("launcher patch notes") diff --git a/tests/engine/update_check_tests.lua b/tests/engine/update_check_tests.lua index 193ab118..1fab2838 100644 --- a/tests/engine/update_check_tests.lua +++ b/tests/engine/update_check_tests.lua @@ -32,6 +32,15 @@ eq(rel.payloadName, "gen1recomp-1.4.2.love", "payload name derived from version" eq(rel.payload.url, "http://x/love", "payload asset url picked") eq(rel.payload.size, 12345, "payload asset size picked") eq(rel.sums.url, "http://x/sums", "sums asset url picked") +eq(rel.notes, "", "missing release body becomes empty notes") + +local withNotes = Check.parseRelease(Json.encode({ + tag_name = "v1.4.2", + body = "## Issues closed\n\n- #1 cart padding", + assets = {}, +})) +eq(withNotes.notes, "## Issues closed\n\n- #1 cart padding", + "parseRelease keeps the GitHub release body") -- a newer release that ships no .love yet: parses, but the payload/sums are nil -- so the worker will route to needs_full rather than an in-place update diff --git a/tests/engine/update_tests.lua b/tests/engine/update_tests.lua index 2fb696ab..0621e130 100644 --- a/tests/engine/update_tests.lua +++ b/tests/engine/update_tests.lua @@ -287,6 +287,7 @@ eq(rel.payload.url, "http://x/love", "parseRelease picks the payload asset url") eq(rel.payload.size, 12345, "parseRelease picks the payload asset size") eq(rel.sums.url, "http://x/sums", "parseRelease picks the sums asset url") eq(rel.sums.size, 99, "parseRelease picks the sums asset size") +eq(rel.notes, "", "parseRelease treats a missing body as empty notes") -- a release with no .love yet still parses; payload/sums are nil so the worker -- routes to a full reinstall rather than an in-place update diff --git a/tests/run_save_editor_tests.lua b/tests/run_save_editor_tests.lua index 986e3dfc..f958d614 100644 --- a/tests/run_save_editor_tests.lua +++ b/tests/run_save_editor_tests.lua @@ -9,6 +9,7 @@ -- tests/save_editor_task7_tests.lua -- tests/save_editor_task8_tests.lua -- tests/save_editor_mod_tests.lua +-- tests/save_editor_gen2_tests.lua -- See tools/save-editor/README.md for the full list. -- -- All of them drive tools/save-editor/Ops.lua rather than clicking pixel diff --git a/tests/run_tests.lua b/tests/run_tests.lua index 149483aa..6e2ad123 100644 --- a/tests/run_tests.lua +++ b/tests/run_tests.lua @@ -3475,6 +3475,8 @@ do local lua = (arg and arg[-1]) or "luajit" local status = os.execute(("%q tests/save_editor_mod_tests.lua"):format(lua)) check(status == 0 or status == true, "save_editor_mod_tests suite") + status = os.execute(("%q tests/save_editor_gen2_tests.lua"):format(lua)) + check(status == 0 or status == true, "save_editor_gen2_tests suite") end -- ---------------------------------------------- input hold regressions diff --git a/tests/save_editor_gen2_tests.lua b/tests/save_editor_gen2_tests.lua new file mode 100644 index 00000000..97b67158 --- /dev/null +++ b/tests/save_editor_gen2_tests.lua @@ -0,0 +1,352 @@ +-- Headless Gold save-editor rules. Run from repo root: +-- luajit tests/save_editor_gen2_tests.lua +package.path = package.path .. ";./?.lua;./?/init.lua;./tools/save-editor/?.lua" + .. ";./tools/save-editor/panels/?.lua" + +local love_stub = require("tests.love_stub") +love = love_stub + +local passed, failed = 0, 0 + +local function check(cond, msg) + if cond then + passed = passed + 1 + else + failed = failed + 1 + print("FAIL: " .. msg) + end +end + +local function eq(a, b, msg) + check(a == b, msg .. string.format(" (got %s, want %s)", tostring(a), tostring(b))) +end + +print("== save editor gen2 tests ==") + +local Gen = require("Gen") +local Catalog = require("Catalog") +local MonOps = require("MonOps") +local Ops = require("Ops") +local State = require("State") +local Save2 = require("src.core.gen2.Save") +local SaveData = require("src.core.SaveData") +local GameVersion = require("src.core.GameVersion") + +local data = { + pokemon = { + CYNDAQUIL = { + id = "CYNDAQUIL", name = "CYNDAQUIL", dex = 155, + types = { "FIRE" }, + baseStats = { + hp = 39, attack = 52, defense = 43, speed = 65, + specialAttack = 60, specialDefense = 50, + }, + catchRate = 45, baseExp = 65, + growthRate = "MEDIUM_FAST", + levelMoves = { { level = 1, move = "TACKLE" } }, + genderRatio = 31, + }, + TOTODILE = { + id = "TOTODILE", name = "TOTODILE", dex = 158, + types = { "WATER" }, + baseStats = { + hp = 50, attack = 65, defense = 64, speed = 43, + specialAttack = 44, specialDefense = 48, + }, + catchRate = 45, baseExp = 66, + growthRate = "MEDIUM_FAST", + levelMoves = { { level = 1, move = "SCRATCH" } }, + genderRatio = 31, + }, + }, + moves = { + TACKLE = { pp = 35 }, + SCRATCH = { pp = 35 }, + }, + items = { + POTION = { pocket = "ITEM" }, + MASTER_BALL = { pocket = "BALL" }, + FLOWER_MAIL = { pocket = "ITEM" }, + }, + maps = {}, +} + +local function newState() + local S = State.new() + S.data = data + S.cat = Catalog.build(data) + S.save = Save2.newGame() + S.version = "gold" + Gen.ensureBoxes(S.save) + return S +end + +do + GameVersion.set("gold") + eq(Gen.of({ generation = 2 }), 2, "Gen.of generation field") + eq(Gen.of({ version = "gold" }), 2, "Gen.of version gold") + eq(Gen.of(SaveData.newGame()), 1, "Gen.of gen1 newGame") + eq(Gen.of(Save2.newGame()), 2, "Gen.of gold newGame") +end + +do + local S = newState() + check(Ops.speciesUsable(S, "CYNDAQUIL"), "spa/spd species is usable") + local S1 = State.new() + S1.data = { + pokemon = { + PIDGEY = { baseStats = { hp = 40, attack = 45, defense = 40, speed = 56, special = 35 } }, + BROKEN = { baseStats = { hp = 1 } }, + }, + } + check(Ops.speciesUsable(S1, "PIDGEY"), "gen1 special species is usable") + check(not Ops.speciesUsable(S1, "BROKEN"), "partial record is not usable") +end + +do + local S = newState() + Ops.partyAdd(S) + eq(#S.save.party, 1, "partyAdd on gold") + local mon = S.save.party[1] + eq(mon.species, "CYNDAQUIL", "first catalog species") + check(mon.experience ~= nil, "gold mon has experience") + check(mon.exp == nil or mon.experience ~= nil, "does not rely on gen1 exp") + check(mon.stats.specialAttack and mon.stats.specialDefense, + "gold stats have spa/spd") + check(mon.happiness ~= nil, "gold mon has happiness") + eq(mon.ot, S.save.player.name, "stampOT copies player name") + + Ops.setLevel(S, mon, 20) + eq(mon.level, 20, "setLevel 20") + check(mon.experience > 0, "experience resynced") + + Ops.setHappiness(S, mon, 200) + eq(mon.happiness, 200, "happiness 200") + Ops.setPokerus(S, mon, 15) + eq(mon.pokerus, 15, "pokerus byte") + Ops.setHeldItem(S, mon, "POTION") + eq(mon.item, "POTION", "held item") + + eq(mon.name, "CYNDAQUIL", "new mon copies species display name") + Ops.setSpecies(S, mon, "TOTODILE") + eq(mon.species, "TOTODILE", "setSpecies id") + eq(mon.name, "TOTODILE", "setSpecies rewrites the Gold display name") + check(mon.nickname == nil, "setSpecies does not invent a nickname") + eq(mon.types[1], "WATER", "setSpecies rewrites copied types") +end + +do + local S = newState() + Ops.partyAdd(S) + Ops.partyAdd(S) + local Mail = require("src.core.gen2.Mail") + Mail.set(S.save, 1, Mail.entry("FLOWER_MAIL", "hi", "GOLD", 1, "CYNDAQUIL")) + Mail.set(S.save, 2, Mail.entry("SURF_MAIL", "bye", "GOLD", 1, "CYNDAQUIL")) + S.selectedParty = 1 + Ops.partyMove(S, 1) + eq(Mail.state(S.save).party[1].message, "bye", "partyMove carries mail with the mon") + eq(Mail.state(S.save).party[2].message, "hi", "partyMove swaps the other letter") + S.selectedParty = 1 + check(Ops.partyRemove(S) == false, "partyRemove arms") + check(Ops.partyRemove(S) == true, "partyRemove commits") + eq(Mail.state(S.save).party[1].message, "hi", "partyRemove shifts leftover mail up") + check(Mail.state(S.save).party[2] == nil, "partyRemove clears the vacated slot") +end + +do + local S = newState() + Ops.partyAdd(S) + local mon = S.save.party[1] + local Mail = require("src.core.gen2.Mail") + Ops.setHeldItem(S, mon, "FLOWER_MAIL") + eq(mon.item, "FLOWER_MAIL", "held mail item") + local letter = Mail.state(S.save).party[1] + check(letter ~= nil, "giving mail writes sPartyMail") + eq(letter.species, "CYNDAQUIL", "new letter stamps current species") + Ops.setSpecies(S, mon, "TOTODILE") + eq(Mail.state(S.save).party[1].species, "TOTODILE", + "setSpecies updates the letter's species copy") + Ops.setHeldItem(S, mon, "POTION") + check(Mail.state(S.save).party[1] == nil, "non-mail held item drops the letter") +end + +do + local Mon = require("src.battle.gen2.Mon") + local S = newState() + local stale = Mon.new(data, "CYNDAQUIL", 5) + stale.species = "TOTODILE" + stale.name = "CYNDAQUIL" + stale.types = { "FIRE" } + S.save.dayCare = { man = { mon = stale }, lady = {} } + Mon.syncSaveIdentity(S.save, data) + eq(stale.name, "TOTODILE", "syncSaveIdentity rewrites Day-Care display name") + eq(stale.types[1], "WATER", "syncSaveIdentity rewrites Day-Care types") + eq(Mon.displayName({ nickname = nil, name = "ABRA", species = "RAYQUAZA" }), + "ABRA", "displayName prefers the species copy over the id") + eq(Mon.displayName({ nickname = "BOB", name = "ABRA", species = "RAYQUAZA" }), + "BOB", "displayName prefers nickname") +end + +do + local S = newState() + eq(Ops.boxCount(S), 14, "14 gold boxes") + eq(Ops.boxCapacity(S), 20, "20 per box") + Ops.boxAdd(S) + eq(#Ops.boxes(S)[1], 1, "boxAdd into box 1") +end + +do + local S = newState() + Ops.partyAdd(S) + S.save.party[1].hp = 0 + Ops.partyAdd(S) + S.selectedParty = 1 + S.selectedBox = 1 + local ok = Ops.deposit(S) + check(ok, "deposit fainted mon while a healthy remains") +end + +do + local S = newState() + Ops.partyAdd(S) + Ops.partyAdd(S) + S.selectedParty = 1 + S.selectedBox = 1 + local ok = Ops.deposit(S) + check(ok, "deposit one of two healthy mons") +end + +do + local S = newState() + Ops.partyAdd(S) + S.selectedParty = 1 + S.selectedBox = 1 + local ok = Ops.deposit(S) + check(not ok, "refuse depositing last healthy mon") + check(S.status:lower():find("last", 1, true) or S.status:find("POKéMON") + or S.status:find("POKEMON") or S.status:find("last"), + "deposit refusal names the last-healthy rule: " .. tostring(S.status)) + eq(#S.save.party, 1, "party still has the mon") +end + +do + local S = newState() + eq(Gen.money(S.save), 3000, "gold start money on player") + Ops.addMoney(S, 1000) + eq(S.save.player.money, 4000, "money writes player.money") + check(S.save.money == nil or S.save.money ~= 4000, "does not write save.money") + Ops.maxMoney(S) + eq(S.save.player.money, 999999, "money cap") + Ops.addCoins(S, 250) + eq(S.save.player.coins, 250, "coins write player.coins") +end + +do + local S = newState() + check(not Gen.hasBadge(S.save, "ZEPHYR"), "no zephyr yet") + Ops.toggleBadge(S, "ZEPHYR") + check(Gen.hasBadge(S.save, "ZEPHYR"), "zephyr earned") + check(S.save.player.badges.ZEPHYR, "stored on player.badges") + Ops.toggleBadge(S, "BOULDER") + check(S.save.player.kantoBadges.BOULDER, "kanto badge store") +end + +do + local S = newState() + Ops.dexOwned(S, "CYNDAQUIL", true) + check(S.save.pokedex.caught.CYNDAQUIL, "dex writes caught") + check(S.save.pokedex.owned == nil or S.save.pokedex.owned.CYNDAQUIL == nil, + "does not write owned on gold") + check(S.save.pokedex.seen.CYNDAQUIL, "owned implies seen") + local _, owned = Ops.dexCounts(S) + eq(owned, 1, "dexCounts reads caught") +end + +do + local S = newState() + local name = "EVENT_BEAT_FALKNER" + Ops.setFlag(S, name, true) + check(Gen.getFlag(S.save, name), "gold EVENT_ sets bitfield") + check(S.save.flags[name] == nil, "numeric flags are not string keys") + Ops.setFlag(S, "MOD_EDITMON_GIFT", true) + check(S.save.flags.MOD_EDITMON_GIFT, "mod flags stay named on gold") +end + +do + local S = newState() + S.mapId = "NEW_BARK_TOWN" + S.mapClickCell = { cx = 4, cy = 5 } + Ops.setPlayerHere(S) + eq(S.save.position.map, "NEW_BARK_TOWN", "position.map") + eq(S.save.position.x, 4, "position.x") + eq(S.save.position.y, 5, "position.y") + check(S.save.player.map == nil or S.save.player.map ~= "NEW_BARK_TOWN", + "does not write player.map on gold") +end + +do + local encoded = SaveData.encode(Save2.newGame()) + local back = SaveData.decode(encoded) + eq(back.generation, 2, "round-trip keeps generation 2") +end + +do + local names = Catalog.goldEventList() + local hasFalkner = false + for _, n in ipairs(names) do + if n == "EVENT_BEAT_FALKNER" then hasFalkner = true break end + end + check(hasFalkner, "gold event list includes EVENT_BEAT_FALKNER") +end + +do + local maps = Gen.maps({ gen2Maps = { AZALEA_GYM = true }, maps = { PALLET_TOWN = true } }) + check(maps.AZALEA_GYM, "Gen.maps includes gen2Maps") + check(maps.PALLET_TOWN, "Gen.maps keeps Data:load maps beside gen2Maps") + check(Gen.maps({ maps = { PALLET_TOWN = true } }).PALLET_TOWN, + "Gen.maps falls back to maps") + local mansion = Gen.maps({ + maps = { + CELADON_MANSION_2F = { id = "CELADON_MANSION_2F", width = 4, height = 5 }, + }, + gen2Maps = { + CELADON_MANSION_2F = { objects = { { name = "NPC" } } }, + BERRY_FARM = { id = "BERRY_FARM", width = 19, height = 12 }, + }, + }) + eq(mansion.CELADON_MANSION_2F.width, 4, + "Gen.maps keeps extractor width under a gen2Maps objects patch") + eq(mansion.CELADON_MANSION_2F.objects[1].name, "NPC", + "Gen.maps still applies the gen2Maps patch fields") + eq(mansion.BERRY_FARM.width, 19, "Gen.maps keeps mod maps only on gen2Maps") + local bound = Gen.bindGoldData({ maps = { A = true }, tilesets = { T = true } }) + check(bound.gen2Maps == bound.maps, "bindGoldData aliases gen2Maps") + check(bound.gen2Tilesets == bound.tilesets, "bindGoldData aliases gen2Tilesets") + check(Gen.tilesets({ gen2Tilesets = { TILESET_GYM = true } }).TILESET_GYM, + "Gen.tilesets prefers gen2Tilesets") +end + +do + local Map2 = require("src.world.gen2.Map") + local MapPreview = require("src.world.gen2.MapPreview") + local def = { + id = "AZALEA_GYM", tileset = "TILESET_GYM", + width = 1, height = 1, blocks = { 1 }, borderBlock = 1, + warps = {}, environment = "INDOOR", + } + local tileset = { + id = "TILESET_GYM", + image = "assets/generated/tilesets/gym.png", + tilesPerRow = 16, + blocks = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + } + local map = Map2.new(def, tileset) + check(map.renderer == nil, "Map2 does not ship a renderer") + local baker = MapPreview.baker({ tilesets = { TILESET_GYM = tileset } }) + local renderer = MapPreview.renderer(baker, map) + check(renderer ~= nil and renderer.draw ~= nil, + "MapPreview attaches a draw for a Gold map") +end + +print(string.format("save editor gen2 tests: %d passed, %d failed", passed, failed)) +if failed > 0 then os.exit(1) end diff --git a/tests/save_editor_mod_tests.lua b/tests/save_editor_mod_tests.lua index db679d9d..9ba1d482 100644 --- a/tests/save_editor_mod_tests.lua +++ b/tests/save_editor_mod_tests.lua @@ -142,6 +142,41 @@ local ok, err = pcall(function() local report = SaveData.validate(probe, Data) check(#report.lostMons == 0 and probe.party[1].species == "EDITMON", "validate keeps the modded mon while the mod is enabled") + + -- Gold-targeted mods: spa/spd records are usable, and a Gen 1-only + -- manifest stays out of Gold (no ROM cache / App.load("gold") required). + local ModTargets = require("src.mods.ModTargets") + local Ops = require("Ops") + check(not ModTargets.supports({}, "gold"), + "legacy gen1-only fixture does not support gold") + check(not ModTargets.supports({ games = { "red" } }, "gold"), + "explicit gen1 games list does not support gold") + check(ModTargets.supports({ games = { "gold" } }, "gold"), + "gold-targeted manifest supports gold") + check(ModTargets.supports({ gen2compat = true }, "gold"), + "gen2compat legacy still supports gold") + + local goldS = { + data = { + pokemon = { + EDITMON = { + baseStats = { + hp = 50, attack = 50, defense = 50, speed = 50, + specialAttack = 50, specialDefense = 50, + }, + }, + G1ONLY = { + baseStats = { + hp = 50, attack = 50, defense = 50, speed = 50, special = 50, + }, + }, + }, + }, + } + check(Ops.speciesUsable(goldS, "EDITMON"), + "spa/spd EDITMON is usable on gold") + check(Ops.speciesUsable(goldS, "G1ONLY"), + "gen1 special record remains usable (dual-key gate)") end) os.remove(MOD_ROOT .. "/main.lua") @@ -154,7 +189,7 @@ os.remove(tmpPath) love.filesystem = savedFS -- leave shared singletons the way we found them (the fixture merged one -- record into Data.pokemon) -Data.pokemon.EDITMON = nil +if Data.pokemon then Data.pokemon.EDITMON = nil end Assets.loader = savedBridge Assets.invalidate() Runtime.install(savedEvents, savedHooks, savedErrors) diff --git a/tools/goldwalk/gen_flags.lua b/tools/goldwalk/gen_flags.lua index e700cb88..92655db3 100644 --- a/tools/goldwalk/gen_flags.lua +++ b/tools/goldwalk/gen_flags.lua @@ -12,7 +12,7 @@ package.path = "./?.lua;./?/init.lua;" .. package.path local Flags = dofile("tools/goldwalk/flags.lua") local ROOT = arg[1] or "../pokegold" -local OUT = "tests/drivers/gold/flag_names.lua" +local OUT = "src/core/gen2/FlagNames.lua" local events, _ = Flags.parse(ROOT .. "/constants/event_flags.asm") local engine, _ = Flags.parse(ROOT .. "/constants/engine_flags.asm") diff --git a/tools/save-editor/App.lua b/tools/save-editor/App.lua index 7b5d3c21..260aadab 100644 --- a/tools/save-editor/App.lua +++ b/tools/save-editor/App.lua @@ -25,6 +25,7 @@ local State = require("State") local Kit = require("Kit") local Theme = require("Theme") local Ops = require("Ops") +local Gen = require("Gen") local PadInput = require("PadInput") local PAL = Theme.PAL @@ -87,56 +88,43 @@ local function applyLoaded(path, statusVerb) if save then S.save = save S.status = statusVerb .. " " .. path - S.mapId = save.player.map S.loadError = false S.allowSave = true elseif existed then - -- File is present but SaveIO.load couldn't decode it: treat it as a - -- real (corrupt) save, not a missing one. Editing a stub here is fine, - -- but Save must stay disabled so we never clobber the corrupt file - -- until the user fixes it and Reload succeeds. - S.save = require("src.core.SaveData").newGame() + S.save = Gen.newGame(S.version) S.status = "Corrupt save at " .. path .. " (" .. tostring(err) .. "), Save disabled, use Reload after fixing the file" - S.mapId = S.save.player.map S.loadError = true S.allowSave = false else - S.save = require("src.core.SaveData").newGame() + S.save = Gen.newGame(S.version) S.status = "No save at " .. path .. " (" .. tostring(err) .. "), editing new game stub" - S.mapId = S.save.player.map S.loadError = false S.allowSave = true end + local mapId = Gen.playerMap(S.save) + S.mapId = mapId S.dirty = false S._quitArmed = false S._openArmed = false S.editingMon = nil Ops.disarm(S) - local boxes = require("src.pokemon.Boxes").ensure(S.save) - -- Imported .sav box mons have no stat block (box_struct stops before - -- MON_STATS). The game derives them in SaveData.validate; the editor - -- only validates a copy, so hydrate here for Boxes/Party/MonEditor. - local Stats = require("src.pokemon.Stats") - local function ensureStats(mon) - Stats.ensure(Data.pokemon and Data.pokemon[mon.species], mon) - end - for _, mon in ipairs(S.save.party or {}) do ensureStats(mon) end - for _, box in ipairs(boxes) do - for _, mon in ipairs(box) do ensureStats(mon) end - end - if S.save.daycare and S.save.daycare.mon then - ensureStats(S.save.daycare.mon) - end - -- what the running game would quarantine, computed on a copy so the - -- editor never mutates the file behind the user's back - local SaveData = require("src.core.SaveData") + Gen.ensureBoxes(S.save) + Gen.hydrateSave(Data, S.save) local probe = require("src.mods.Merge").deepCopy(S.save) - S.validation = SaveData.validate(probe, Data) - if not SaveData.emptyReport(S.validation) then - S.status = S.status .. string.format(", game would quarantine: %d mons, %d items, %d maps", - #S.validation.lostMons, #S.validation.lostItems, #S.validation.remappedMaps) + S.validation = Gen.validate(probe, Data) + if not Gen.emptyReport(S.save, S.validation) then + if Gen.of(S.save, S.version) == 2 then + S.status = S.status .. string.format( + ", game would quarantine: %d script bytes, %d mail, %d events", + #(S.validation.lostScriptMem or {}), + #(S.validation.lostMail or {}), + #(S.validation.lostEvents or {})) + else + S.status = S.status .. string.format(", game would quarantine: %d mons, %d items, %d maps", + #S.validation.lostMons, #S.validation.lostItems, #S.validation.remappedMaps) + end end end @@ -152,6 +140,9 @@ function App.load(pathOverride, opts) S.slotId = opts.slotId S.embedded = opts.embedded or false S.onClose = opts.onClose + if opts.version then + require("src.core.GameVersion").set(opts.version) + end -- the same mod set the game loads, merged into Data before the catalogs -- build, so modded species/items/moves are editable and MonOps stops -- asserting on them @@ -163,6 +154,10 @@ function App.load(pathOverride, opts) -- loaded at least once, so it doubles as the "needs evicting" marker. if Data._pristineKeys then Data:unloadGenerated() end Data:load() + if Gen.of(nil, opts.version) == 2 + or require("src.core.GameVersion").generation() == 2 then + Gen.bindGoldData(Data) + end local ModLoader = require("src.mods.Loader") mods = ModLoader.new() mods:load(Data) @@ -174,9 +169,16 @@ function App.load(pathOverride, opts) for _, mod in ipairs(S.mods:status().loaded) do modRoots[#modRoots + 1] = mod.path end - S.events = Catalog.scrapeEvents("data/scripts", "data/generated/trainer_headers.lua", - nil, modRoots) + if Gen.of(nil, opts.version) == 2 or require("src.core.GameVersion").generation() == 2 then + S.events = Catalog.goldEventList(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). @@ -582,11 +584,9 @@ local function tabCount(id) return tostring(n) elseif id == "items" then local Bag = require("src.inventory.Bag") - return ("%d/%d"):format(Bag.slots(S.save), Bag.capacity(S.data)) + return ("%d/%d"):format(Bag.slots(S.save, S.data), Bag.capacity(S.data)) elseif id == "events" then - local n = 0 - for _ in pairs(S.save.flags or {}) do n = n + 1 end - return tostring(n) + return tostring(Gen.flagCount(S.save)) elseif id == "map" then -- map ids run long (REDS_HOUSE_2F); the rail is a summary, not a label return Kit.ellipsize("tiny", S.mapId or "", 110 * Kit.scale) @@ -602,21 +602,28 @@ end -- something. Returns what to draw plus the tab that owns the first problem, -- so the rail can reserve the pill's width before laying out the tiles. local function validationPill() - local SaveData = require("src.core.SaveData") local report = S.validation - if not report or SaveData.emptyReport(report) then + if not report or Gen.emptyReport(S.save, report) then return "Save validates clean", PAL.green, nil, true end local parts = {} local target local function add(n, singular, plural, tab) + n = n or 0 if n <= 0 then return end parts[#parts + 1] = ("%d %s"):format(n, n == 1 and singular or plural) target = target or tab end - add(#report.lostMons, "mon", "mons", "party") - add(#report.lostItems, "item", "items", "items") - add(#report.remappedMaps, "map", "maps", "map") + if Gen.of(S.save, S.version) == 2 then + add(#(report.lostScriptMem or {}), "script byte", "script bytes", "events") + add(#(report.lostMail or {}), "mail", "mail", "party") + add(#(report.lostEvents or {}), "event", "events", "events") + add(#(report.lostMapScenes or {}), "map scene", "map scenes", "map") + else + add(#(report.lostMons or {}), "mon", "mons", "party") + add(#(report.lostItems or {}), "item", "items", "items") + add(#(report.remappedMaps or {}), "map", "maps", "map") + end return "Would quarantine " .. table.concat(parts, ", "), PAL.yellow, target, false end diff --git a/tools/save-editor/Catalog.lua b/tools/save-editor/Catalog.lua index b2e61149..9ef209e7 100644 --- a/tools/save-editor/Catalog.lua +++ b/tools/save-editor/Catalog.lua @@ -91,7 +91,8 @@ function Catalog.scrapeEvents(scriptDir, headerPath, listFiles, extraDirs) end end - local dirs = { scriptDir } + local dirs = {} + if scriptDir then dirs[#dirs + 1] = scriptDir end for _, dir in ipairs(extraDirs or {}) do dirs[#dirs + 1] = dir end @@ -110,4 +111,25 @@ 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) + local modFlags = Catalog.scrapeEvents(nil, nil, nil, extraDirs) + local seen = {} + for _, name in ipairs(names) do seen[name] = true end + for _, name in ipairs(modFlags) do + if not seen[name] then + names[#names + 1] = name + seen[name] = true + end + end + return names +end + return Catalog diff --git a/tools/save-editor/Gen.lua b/tools/save-editor/Gen.lua new file mode 100644 index 00000000..9050fbd0 --- /dev/null +++ b/tools/save-editor/Gen.lua @@ -0,0 +1,350 @@ +-- Generation adapter for the save editor. Panels stay generation-blind; +-- Ops and App read Gold vs RBY through this module so a Gold write never +-- lands in Gen 1 fields (save.money, pokedex.owned, 12 boxes, ...). + +local GameVersion = require("src.core.GameVersion") + +local Gen = {} + +local function versionGeneration(version) + if type(version) ~= "string" then return nil end + local info = GameVersion.info(version) + if info then return info.generation or 1 end + return nil +end + +function Gen.of(save, version) + if type(save) == "table" then + if save.generation == 2 then return 2 end + local fromVersion = versionGeneration(save.version) + if fromVersion then return fromVersion end + end + local fromArg = versionGeneration(version) + if fromArg then return fromArg end + return GameVersion.generation() +end + +function Gen.ofState(S) + if not S then return GameVersion.generation() end + return Gen.of(S.save, S.version) +end + +function Gen.is2(save, version) + return Gen.of(save, version) == 2 +end + +-- Data:load writes Gold maps/tilesets to the Gen 1 keys; Game2 and the mod +-- merge write gen2Maps / gen2Tilesets. Overlay the gen2 table on the loaded +-- cache so a mod patch that landed on an empty gen2Maps (objects only, no +-- width) does not hide the extractor's record, and a new map like BERRY_FARM +-- still appears. +local function overlayRecords(base, overlay) + if not overlay then return base or {} end + if not base or base == overlay then return overlay end + local out = {} + for id, def in pairs(base) do out[id] = def end + for id, def in pairs(overlay) do + local prior = out[id] + if type(def) == "table" and type(prior) == "table" then + local merged = {} + for k, v in pairs(prior) do merged[k] = v end + for k, v in pairs(def) do merged[k] = v end + out[id] = merged + else + out[id] = def + end + end + return out +end + +function Gen.maps(data) + if type(data) ~= "table" then return {} end + return overlayRecords(data.maps, data.gen2Maps) +end + +function Gen.tilesets(data) + if type(data) ~= "table" then return {} end + return overlayRecords(data.tilesets, data.gen2Tilesets) +end + +-- Point the Gen 2 Data keys at the tables Data:load already filled, before +-- mods:load folds into gen2Maps. Same wiring Game2 does when it boots Gold. +function Gen.bindGoldData(data) + if type(data) ~= "table" then return data end + if data.maps and data.gen2Maps == nil then data.gen2Maps = data.maps end + if data.tilesets and data.gen2Tilesets == nil then + data.gen2Tilesets = data.tilesets + end + if data.palettes and data.gen2Palettes == nil then + data.gen2Palettes = data.palettes + end + if data.gen2Roofs == nil and data.roofs == nil then + local ok, roofs = pcall(require, "data.generated.roofs") + if ok and type(roofs) == "table" then + data.roofs = roofs + data.gen2Roofs = roofs + end + elseif data.roofs and data.gen2Roofs == nil then + data.gen2Roofs = data.roofs + end + return data +end + +function Gen.newGame(version) + if (versionGeneration(version) or GameVersion.generation(version)) == 2 then + return require("src.core.gen2.Save").newGame() + end + return require("src.core.SaveData").newGame() +end + +function Gen.validate(save, data) + if Gen.of(save) == 2 then + return require("src.core.gen2.Save").validate(save) + end + return require("src.core.SaveData").validate(save, data) +end + +function Gen.emptyReport(save, report) + if Gen.of(save) == 2 then + return require("src.core.gen2.Save").emptyReport(report) + end + return require("src.core.SaveData").emptyReport(report) +end + +function Gen.hydrateMon(data, mon) + if type(mon) ~= "table" then return mon end + local def = data and data.pokemon and data.pokemon[mon.species] + local gen2 = (mon.stats and mon.stats.specialAttack) + or (def and def.baseStats and def.baseStats.specialAttack) + or mon.experience ~= nil + if gen2 then + require("src.battle.gen2.Mon").refreshStats(mon, data) + else + require("src.pokemon.Stats").ensure(def, mon) + end + return mon +end + +-- Party, boxes, Day-Care. Gold's dayCare.man/lady/egg are not save.daycare. +function Gen.hydrateSave(data, save) + if type(save) ~= "table" then return save end + if Gen.of(save) == 2 then + local Mon = require("src.battle.gen2.Mon") + Mon.eachSaveMon(save, function(mon) Mon.refreshStats(mon, data) end) + return save + end + for _, mon in ipairs(save.party or {}) do Gen.hydrateMon(data, mon) end + for _, box in ipairs(save.boxes or {}) do + if type(box) == "table" then + for _, mon in ipairs(box) do Gen.hydrateMon(data, mon) end + end + end + if save.daycare and save.daycare.mon then + Gen.hydrateMon(data, save.daycare.mon) + end + return save +end + +function Gen.ensureBoxes(save) + if Gen.of(save) == 2 then + local Boxes2 = require("src.core.gen2.Boxes") + save.boxes = save.boxes or {} + for i = 1, Boxes2.NUM_BOXES do + save.boxes[i] = save.boxes[i] or {} + end + save.currentBox = math.max(1, math.min(Boxes2.NUM_BOXES, save.currentBox or 1)) + return save.boxes + end + return require("src.pokemon.Boxes").ensure(save) +end + +function Gen.boxCount(save) + if Gen.of(save) == 2 then + return require("src.core.gen2.Boxes").NUM_BOXES + end + return require("src.pokemon.Boxes").COUNT +end + +function Gen.boxCapacity(save) + if Gen.of(save) == 2 then + return require("src.core.gen2.Boxes").MONS_PER_BOX + end + return require("src.pokemon.Boxes").CAPACITY +end + +function Gen.money(save) + if Gen.of(save) == 2 then + return (save.player and save.player.money) or 0 + end + return save.money or 0 +end + +function Gen.setMoney(save, amount) + if Gen.of(save) == 2 then + save.player = save.player or {} + save.player.money = amount + else + save.money = amount + end +end + +function Gen.coins(save) + if Gen.of(save) == 2 then + return (save.player and save.player.coins) or 0 + end + return save.coins or 0 +end + +function Gen.setCoins(save, amount) + if Gen.of(save) == 2 then + save.player = save.player or {} + save.player.coins = amount + else + save.coins = amount + end +end + +function Gen.dexOwnedKey(save) + if Gen.of(save) == 2 then return "caught" end + return "owned" +end + +function Gen.playerMap(save) + if Gen.of(save) == 2 then + local p = save.position + if p and p.map then return p.map, p.x or 0, p.y or 0, p.facing end + return save.spawn, 0, 0 + end + local p = save.player or {} + return p.map, p.x or 0, p.y or 0 +end + +function Gen.setPlayerHere(save, mapId, x, y, facing) + if Gen.of(save) == 2 then + local prev = save.position or {} + save.position = { + map = mapId, + x = x, + y = y, + facing = facing or prev.facing or "down", + } + return + end + save.player = save.player or {} + save.player.map = mapId + save.player.x = x + save.player.y = y +end + +local JOHTO = { + "ZEPHYR", "HIVE", "PLAIN", "FOG", "MINERAL", "STORM", "GLACIER", "RISING", +} +local KANTO = { + "BOULDER", "CASCADE", "THUNDER", "RAINBOW", + "SOUL", "MARSH", "VOLCANO", "EARTH", +} + +function Gen.badgeIds(save, cat) + if Gen.of(save) == 2 then + local ids = {} + for _, name in ipairs(JOHTO) do ids[#ids + 1] = name end + for _, name in ipairs(KANTO) do ids[#ids + 1] = name end + return ids + end + local ids = {} + for _, id in ipairs((cat and cat.items) or {}) do + if tostring(id):find("BADGE", 1, true) then ids[#ids + 1] = id end + end + return ids +end + +local KANTO_SET = {} +for _, name in ipairs(KANTO) do KANTO_SET[name] = true end + +function Gen.hasBadge(save, id) + if Gen.of(save) == 2 then + local p = save.player or {} + local store = KANTO_SET[id] and (p.kantoBadges or {}) or (p.badges or {}) + if store[id] then return true end + local list = KANTO_SET[id] and KANTO or JOHTO + for index, name in ipairs(list) do + if name == id then return store[index] == true end + end + return false + end + return save.inventory and save.inventory[id] and true or false +end + +function Gen.toggleBadge(save, id) + if Gen.of(save) == 2 then + save.player = save.player or {} + local storeName = KANTO_SET[id] and "kantoBadges" or "badges" + save.player[storeName] = save.player[storeName] or {} + local store = save.player[storeName] + local on = Gen.hasBadge(save, id) + store[id] = (not on) and true or nil + for index, name in ipairs(KANTO_SET[id] and KANTO or JOHTO) do + if name == id then store[index] = nil end + end + return not on + end + local on = save.inventory[id] and true or false + save.inventory[id] = (not on) and 1 or nil + return not on +end + +local function goldFlagId(name) + local flags = require("src.core.gen2.FlagNames") + return flags.events and flags.events[name] +end + +function Gen.getFlag(save, name) + if Gen.of(save) == 2 then + local id = goldFlagId(name) + if id then + local Events2 = require("src.world.gen2.Events") + local ev = Events2.new() + ev:restore(save.events) + return ev:get(id) + end + return save.flags and save.flags[name] == true + end + return save.flags and save.flags[name] == true +end + +function Gen.setFlag(save, name, on) + if Gen.of(save) == 2 then + local id = goldFlagId(name) + if id then + local Events2 = require("src.world.gen2.Events") + local ev = Events2.new() + ev:restore(save.events) + ev:set(id, on and true or false) + save.events = ev:serialize() + return + end + save.flags = save.flags or {} + save.flags[name] = on and true or nil + return + end + save.flags = save.flags or {} + save.flags[name] = on and true or nil +end + +function Gen.flagCount(save) + if Gen.of(save) == 2 then + local n = 0 + for _ in pairs(save.events or {}) do n = n + 1 end + for _ in pairs(save.flags or {}) do n = n + 1 end + return n + end + local n = 0 + for _ in pairs(save.flags or {}) do n = n + 1 end + return n +end + +function Gen.exp(mon) + return mon.experience or mon.exp or 0 +end + +return Gen diff --git a/tools/save-editor/MonOps.lua b/tools/save-editor/MonOps.lua index 93630cb6..418c5f36 100644 --- a/tools/save-editor/MonOps.lua +++ b/tools/save-editor/MonOps.lua @@ -4,23 +4,40 @@ local Growth = require("src.pokemon.Growth") local MonOps = {} -function MonOps.create(data, species, level) +function MonOps.create(data, species, level, gen) + if gen == 2 then + local Mon = require("src.battle.gen2.Mon") + local mon = Mon.new(data, species, level) + assert(mon, "unknown species " .. tostring(species)) + return mon + end return Pokemon.new(data, species, level) end -function MonOps.recalc(data, mon) +function MonOps.recalc(data, mon, gen) + if gen == 2 or (mon.stats and mon.stats.specialAttack) or mon.experience then + require("src.battle.gen2.Mon").refreshStats(mon, data) + return + end local def = data.pokemon[mon.species] assert(def, "unknown species") mon.stats = Stats.calc(def, mon.level, mon.dvs, mon.statExp) mon.hp = math.max(0, math.min(mon.hp or mon.stats.hp, mon.stats.hp)) end -function MonOps.setLevel(data, mon, level) +function MonOps.setLevel(data, mon, level, gen) level = math.max(1, math.min(100, math.floor(level))) local def = data.pokemon[mon.species] mon.level = level + if gen == 2 or mon.experience ~= nil then + local Mon = require("src.battle.gen2.Mon") + local growth = Mon.growthFor(data, def.growthRate) + mon.experience = Mon.experienceForLevel(growth, level) + Mon.refreshStats(mon, data) + return + end mon.exp = Growth.expForLevel(def.growthRate, level) - MonOps.recalc(data, mon) + MonOps.recalc(data, mon, gen) end function MonOps.setMove(data, mon, slot, moveId) @@ -32,6 +49,7 @@ function MonOps.setMove(data, mon, slot, moveId) id = moveId, pp = mdef.pp + ((mon.moves[slot] and mon.moves[slot].ppUps) or 0) * math.floor(mdef.pp / 5), ppUps = mon.moves[slot] and mon.moves[slot].ppUps or nil, + maxPp = mdef.pp, } end @@ -42,19 +60,38 @@ function MonOps.syncHpDv(dvs) return dvs end -function MonOps.setDv(data, mon, key, value) +function MonOps.setDv(data, mon, key, value, gen) mon.dvs[key] = math.max(0, math.min(15, math.floor(value))) if key ~= "hp" then MonOps.syncHpDv(mon.dvs) end - MonOps.recalc(data, mon) + if gen == 2 or (mon.stats and mon.stats.specialAttack) then + local Mon = require("src.battle.gen2.Mon") + mon.dvs.hp = Mon.hpDV(mon.dvs) + local def = data.pokemon[mon.species] + if def then + mon.gender = Mon.gender(def, mon.dvs, { species = mon.species, level = mon.level }) + mon.shiny = Mon.isShiny(mon.dvs, { species = mon.species, def = def, level = mon.level }) + local Unown = require("src.core.gen2.Unown") + if mon.species == Unown.SPECIES then + mon.unownLetter = Unown.letterFromDVs(mon.dvs) + end + end + end + MonOps.recalc(data, mon, gen) end -- Keep level; resync exp to the species growth curve (species changes). -function MonOps.setSpecies(data, mon, species) +-- Gold also copies def.name onto mon.name: the party list and SUMMARY print +-- that field when nickname is nil, so leaving the previous species' name +-- made a swapped mon still read as ABRA (or whoever was added first). +function MonOps.setSpecies(data, mon, species, gen) assert(data.pokemon[species], "unknown species") mon.species = species - MonOps.setLevel(data, mon, mon.level) + MonOps.setLevel(data, mon, mon.level, gen) + if gen == 2 or mon.experience ~= nil or (mon.stats and mon.stats.specialAttack) then + require("src.battle.gen2.Mon").syncIdentity(mon, data) + end end return MonOps diff --git a/tools/save-editor/Ops.lua b/tools/save-editor/Ops.lua index 5da44907..36d13c64 100644 --- a/tools/save-editor/Ops.lua +++ b/tools/save-editor/Ops.lua @@ -17,6 +17,7 @@ local BoxesMod = require("src.pokemon.Boxes") local Bag = require("src.inventory.Bag") local MonOps = require("MonOps") local Charmap = require("src.save_convert.data.charmap") +local Gen = require("Gen") local Ops = {} @@ -35,6 +36,62 @@ local function clamp(n, lo, hi) end Ops.clamp = clamp +local function stampNewMon(S, mon) + if Gen.ofState(S) == 2 then + require("src.battle.gen2.Mon").stampOT(S.save, mon) + else + mon.ot = S.save.player.name + mon.otId = S.save.player.id + end + return mon +end + +local function createMon(S, species, level) + local mon = MonOps.create(S.data, species, level, Gen.ofState(S)) + return stampNewMon(S, mon) +end + +local function partySlot(S, mon) + for i, member in ipairs(S.save.party or {}) do + if member == mon then return i end + end +end + +-- Portrait mail (and CheckPokeMail) store species on the letter, not the mon. +local function partyMailEntry(S, slot) + local Mail = require("src.core.gen2.Mail") + return Mail.state(S.save).party[slot] +end + +local function syncPartyMailSpecies(S, mon) + if Gen.ofState(S) ~= 2 then return end + local slot = partySlot(S, mon) + if not slot then return end + local entry = partyMailEntry(S, slot) + if entry then entry.species = mon.species end +end + +local function syncPartyMailHeldItem(S, mon, prevItem, newItem) + if Gen.ofState(S) ~= 2 then return end + local slot = partySlot(S, mon) + if not slot then return end + local Mail = require("src.core.gen2.Mail") + if Mail.isMail(newItem) then + local prev = partyMailEntry(S, slot) + local player = S.save.player or {} + Mail.set(S.save, slot, Mail.entry( + newItem, + prev and prev.message or "", + tostring(mon.otName or mon.ot or player.name or ""):sub(1, Mail.AUTHOR_LENGTH), + mon.otId or player.id or 0, + mon.species)) + return + end + if Mail.isMail(prevItem) or partyMailEntry(S, slot) then + Mail.clear(S.save, slot) + end +end + local function now() if love and love.timer and love.timer.getTime then return love.timer.getTime() @@ -111,9 +168,7 @@ function Ops.partyAdd(S) return Ops.say(S, ("Party is full (%d/%d)"):format(#S.save.party, PartyMod.MAX)) end local species = S.cat.species[1] - local mon = MonOps.create(S.data, species, 5) - mon.ot = S.save.player.name - mon.otId = S.save.player.id + local mon = createMon(S, species, 5) table.insert(S.save.party, mon) S.selectedParty = #S.save.party S.editingMon = mon @@ -129,6 +184,11 @@ function Ops.partyRemove(S) return false end table.remove(S.save.party, index) + -- sPartyMail is keyed by party slot, not by mon: dropping a member without + -- shifting letters hands the next mon someone else's mail. + if Gen.ofState(S) == 2 then + require("src.core.gen2.Mail").removeSlot(S.save, index) + end if S.editingMon == mon then S.editingMon = nil end S.selectedParty = clamp(index, 1, math.max(#S.save.party, 1)) S.editingMon = S.save.party[S.selectedParty] @@ -144,6 +204,9 @@ function Ops.partyMove(S, delta) return Ops.say(S, delta < 0 and "Already the lead mon" or "Already the last mon") end party[i], party[j] = party[j], party[i] + if Gen.ofState(S) == 2 then + require("src.core.gen2.Mail").swapSlots(S.save, i, j) + end S.selectedParty = j return Ops.mark(S, ("Moved %s to slot %d"):format(party[j].species, j)) end @@ -157,7 +220,7 @@ function Ops.setLevel(S, mon, level) if want == mon.level then return Ops.say(S, want == 1 and "Level is already 1" or "Level is already 100") end - MonOps.setLevel(S.data, mon, want) + MonOps.setLevel(S.data, mon, want, Gen.ofState(S)) return Ops.mark(S, ("%s is now Lv%d"):format(mon.species, mon.level)) end @@ -172,15 +235,23 @@ end -- instead of trusting the list: without this, picking such a species walked -- Stats.calc into `speciesDef.baseStats[key]` on a nil and took the window -- down (#541). -local BASE_STAT_KEYS = { "hp", "attack", "defense", "speed", "special" } +local BASE_STAT_KEYS_G1 = { "hp", "attack", "defense", "speed", "special" } +local BASE_STAT_KEYS_G2 = { + "hp", "attack", "defense", "speed", "specialAttack", "specialDefense", +} + +local function baseStatsComplete(bs, keys) + for _, key in ipairs(keys) do + if type(bs[key]) ~= "number" then return false end + end + return true +end function Ops.speciesUsable(S, id) local def = id and S.data.pokemon[id] if type(def) ~= "table" or type(def.baseStats) ~= "table" then return false end - for _, key in ipairs(BASE_STAT_KEYS) do - if type(def.baseStats[key]) ~= "number" then return false end - end - return true + return baseStatsComplete(def.baseStats, BASE_STAT_KEYS_G1) + or baseStatsComplete(def.baseStats, BASE_STAT_KEYS_G2) end -- The one funnel every species change goes through (the picker, the stepper, @@ -199,14 +270,19 @@ function Ops.setSpecies(S, mon, id) end -- MonOps.recalc replaces mon.stats with a fresh table rather than editing -- it in place, so holding the old reference is a real rollback. - local wasSpecies, wasLevel, wasExp = mon.species, mon.level, mon.exp - local wasStats, wasHp = mon.stats, mon.hp - local ok, err = pcall(MonOps.setSpecies, S.data, mon, id) + local wasSpecies, wasLevel, wasExp, wasExperience = mon.species, mon.level, mon.exp, mon.experience + local wasStats, wasHp, wasName = mon.stats, mon.hp, mon.name + local wasTypes, wasGender, wasShiny, wasUnown, wasMaxHp = + mon.types, mon.gender, mon.shiny, mon.unownLetter, mon.maxHp + local ok, err = pcall(MonOps.setSpecies, S.data, mon, id, Gen.ofState(S)) if not ok then - mon.species, mon.level, mon.exp = wasSpecies, wasLevel, wasExp - mon.stats, mon.hp = wasStats, wasHp + mon.species, mon.level, mon.exp, mon.experience = wasSpecies, wasLevel, wasExp, wasExperience + mon.stats, mon.hp, mon.name = wasStats, wasHp, wasName + mon.types, mon.gender, mon.shiny, mon.unownLetter, mon.maxHp = + wasTypes, wasGender, wasShiny, wasUnown, wasMaxHp return Ops.say(S, ("Could not set %s: %s"):format(tostring(id), tostring(err))) end + syncPartyMailSpecies(S, mon) return Ops.mark(S, ("Species set to %s"):format(id)) end @@ -313,9 +389,9 @@ end -- the target is the box, not a mon. function Ops.openBoxAddPicker(S, Kit) local box = Ops.boxes(S)[S.selectedBox] - if #box >= BoxesMod.CAPACITY then + if #box >= Ops.boxCapacity(S) then return Ops.say(S, ("Box %d is full (%d/%d)") - :format(S.selectedBox, #box, BoxesMod.CAPACITY)) + :format(S.selectedBox, #box, Ops.boxCapacity(S))) end S.speciesPicker = { query = "", offset = 0, opened = true, mode = "box-add" } if Kit then Kit.focus = "species-picker" end -- soft keyboard rises (#529) @@ -327,17 +403,15 @@ end -- box mon and a party mon born in the editor are indistinguishable. function Ops.boxAddSpecies(S, id) local box = Ops.boxes(S)[S.selectedBox] - if #box >= BoxesMod.CAPACITY then + if #box >= Ops.boxCapacity(S) then return Ops.say(S, ("Box %d is full (%d/%d)") - :format(S.selectedBox, #box, BoxesMod.CAPACITY)) + :format(S.selectedBox, #box, Ops.boxCapacity(S))) end if not Ops.speciesUsable(S, id) then return Ops.say(S, ("%s has no usable base stats, cannot add it") :format(tostring(id))) end - local mon = MonOps.create(S.data, id, 5) - mon.ot = S.save.player.name - mon.otId = S.save.player.id + local mon = createMon(S, id, 5) table.insert(box, mon) S.selectedBoxSlot = #box S.editingMon = mon @@ -351,7 +425,7 @@ function Ops.setDv(S, mon, key, value) if want == mon.dvs[key] then return Ops.say(S, ("%s DV is already %d"):format(key, want)) end - MonOps.setDv(S.data, mon, key, want) + MonOps.setDv(S.data, mon, key, want, Gen.ofState(S)) return Ops.mark(S, ("%s DV %d (HP DV now %d)"):format(key, mon.dvs[key], mon.dvs.hp)) end @@ -382,7 +456,17 @@ end function Ops.resetMoves(S, mon) if not mon then return false end local def = S.data.pokemon[mon.species] - local learned = Pokemon.movesAtLevel(def, mon.level) + local gen = Gen.ofState(S) + local learned + if gen == 2 then + local Mon = require("src.battle.gen2.Mon") + learned = {} + for _, mv in ipairs(Mon.movesAtLevel(def, mon.level, S.data.moves)) do + learned[#learned + 1] = mv.id + end + else + learned = Pokemon.movesAtLevel(def, mon.level) + end mon.moves = {} for slot, id in ipairs(learned) do MonOps.setMove(S.data, mon, slot, id) @@ -397,6 +481,7 @@ function Ops.healMon(S, mon) return Ops.say(S, ("%s is already at full HP"):format(mon.species)) end mon.hp = mon.stats.hp + if mon.maxHp then mon.maxHp = mon.stats.hp end mon.status = nil for _, mv in ipairs(mon.moves or {}) do local def = S.data.moves[mv.id] @@ -554,27 +639,35 @@ function Ops.clearNickname(S, mon) end -- ------------------------------------------------------------------ boxes +function Ops.boxCount(S) + return Gen.boxCount(S.save) +end + +function Ops.boxCapacity(S) + return Gen.boxCapacity(S.save) +end + function Ops.boxes(S) - return BoxesMod.ensure(S.save) + return Gen.ensureBoxes(S.save) end function Ops.selectBox(S, index) - S.selectedBox = clamp(index, 1, BoxesMod.COUNT) + S.selectedBox = clamp(index, 1, Ops.boxCount(S)) S.selectedBoxSlot = 1 S.save.currentBox = S.selectedBox local box = Ops.boxes(S)[S.selectedBox] - S.status = ("Box %d (%d/%d)"):format(S.selectedBox, #box, BoxesMod.CAPACITY) + S.status = ("Box %d (%d/%d)"):format(S.selectedBox, #box, Ops.boxCapacity(S)) return true end function Ops.stepBox(S, delta) - local n = BoxesMod.COUNT + local n = Ops.boxCount(S) return Ops.selectBox(S, ((S.selectedBox - 1 + delta) % n) + 1) end function Ops.selectBoxSlot(S, index) local box = Ops.boxes(S)[S.selectedBox] - S.selectedBoxSlot = clamp(index, 1, BoxesMod.CAPACITY) + S.selectedBoxSlot = clamp(index, 1, Ops.boxCapacity(S)) local mon = box[S.selectedBoxSlot] S.editingMon = mon S.status = mon @@ -589,14 +682,12 @@ end -- chooses what lands in the box instead of always getting catalog entry #1. function Ops.boxAdd(S) local box = Ops.boxes(S)[S.selectedBox] - if #box >= BoxesMod.CAPACITY then + if #box >= Ops.boxCapacity(S) then return Ops.say(S, ("Box %d is full (%d/%d)") - :format(S.selectedBox, #box, BoxesMod.CAPACITY)) + :format(S.selectedBox, #box, Ops.boxCapacity(S))) end local species = S.cat.species[1] - local mon = MonOps.create(S.data, species, 5) - mon.ot = S.save.player.name - mon.otId = S.save.player.id + local mon = createMon(S, species, 5) table.insert(box, mon) S.selectedBoxSlot = #box S.editingMon = mon @@ -612,9 +703,16 @@ function Ops.withdraw(S) return Ops.say(S, ("Party is full (%d/%d), deposit one first") :format(#S.save.party, PartyMod.MAX)) end - table.remove(box, S.selectedBoxSlot) - table.insert(S.save.party, mon) - S.selectedBoxSlot = clamp(S.selectedBoxSlot, 1, math.max(#box, 1)) + if Gen.ofState(S) == 2 then + local Boxes2 = require("src.core.gen2.Boxes") + local ok, reason = Boxes2.canWithdraw(S.save, S.selectedBox, S.selectedBoxSlot) + if not ok then return Ops.say(S, reason) end + Boxes2.withdraw(S.save, S.selectedBox, S.selectedBoxSlot) + else + table.remove(box, S.selectedBoxSlot) + table.insert(S.save.party, mon) + end + S.selectedBoxSlot = clamp(S.selectedBoxSlot, 1, math.max(#Ops.boxes(S)[S.selectedBox], 1)) S.selectedParty = #S.save.party return Ops.mark(S, ("Withdrew %s to party slot %d"):format(mon.species, #S.save.party)) end @@ -639,6 +737,17 @@ function Ops.deposit(S) local i = S.selectedParty local mon = S.save.party[i] if not mon then return Ops.say(S, "No party slot selected") end + if Gen.ofState(S) == 2 then + local Boxes2 = require("src.core.gen2.Boxes") + local boxIndex = S.selectedBox or S.save.currentBox or 1 + local ok, reason = Boxes2.canDeposit(S.save, i, boxIndex) + if not ok then return Ops.say(S, reason) end + Boxes2.deposit(S.save, i, boxIndex) + S.selectedParty = clamp(i, 1, math.max(#S.save.party, 1)) + S.selectedBox = boxIndex + if S.editingMon == mon then S.editingMon = nil end + return Ops.mark(S, ("Deposited %s into box %d"):format(mon.species, boxIndex)) + end local boxNum = BoxesMod.deposit(S.save, mon) if not boxNum then return Ops.say(S, "Every box is full, release something first") @@ -652,12 +761,13 @@ end -- ------------------------------------------------------------------ items function Ops.addMoney(S, delta) - local want = clamp((S.save.money or 0) + delta, 0, Ops.MONEY_MAX) - if want == S.save.money then + local have = Gen.money(S.save) + local want = clamp(have + delta, 0, Ops.MONEY_MAX) + if want == have then return Ops.say(S, delta < 0 and "Money is already $0" or ("Money is already capped at $%d"):format(Ops.MONEY_MAX)) end - S.save.money = want + Gen.setMoney(S.save, want) return Ops.mark(S, ("Money set to $%d"):format(want)) end @@ -665,15 +775,29 @@ function Ops.maxMoney(S) return Ops.addMoney(S, Ops.MONEY_MAX) end +Ops.COIN_MAX = 9999 + +function Ops.addCoins(S, delta) + local have = Gen.coins(S.save) + local want = clamp(have + delta, 0, Ops.COIN_MAX) + if want == have then + return Ops.say(S, delta < 0 and "Coins are already 0" + or ("Coins are already capped at %d"):format(Ops.COIN_MAX)) + end + Gen.setCoins(S.save, want) + return Ops.mark(S, ("Coins set to %d"):format(want)) +end + function Ops.addToBag(S, id) if not id then return Ops.say(S, "Pick an item first") end - local capacity = Bag.capacity(S.data) + local pocket = Bag.pocketOf(id, S.data) + local capacity = Bag.capacity(S.data, pocket) if Bag.add(S.save, id, 1, S.data) then - return Ops.mark(S, ("Added %s to the bag (%d/%d slots)") - :format(id, Bag.slots(S.save), capacity)) + return Ops.mark(S, ("Added %s to the bag (%d/%d %s slots)") + :format(id, Bag.slots(S.save, S.data, pocket), capacity, pocket)) end - return Ops.say(S, ("Bag is full (%d/%d slots)") - :format(Bag.slots(S.save), capacity)) + return Ops.say(S, ("Bag is full (%d/%d %s slots)") + :format(Bag.slots(S.save, S.data, pocket), capacity, pocket)) end function Ops.bagAdjust(S, id, delta) @@ -715,6 +839,12 @@ end function Ops.addToPc(S, id) if not id then return Ops.say(S, "Pick an item first") end local pc = Ops.pcItems(S) + local n = 0 + for _ in pairs(pc) do n = n + 1 end + if not pc[id] and Gen.ofState(S) == 2 and n >= 50 then + return Ops.say(S, "PC item storage is full (50 stacks)") + end + local pc = Ops.pcItems(S) pc[id] = math.min(Ops.STACK_MAX, (pc[id] or 0) + 1) return Ops.mark(S, ("%s x%d in PC storage"):format(id, pc[id])) end @@ -749,27 +879,17 @@ function Ops.isBadgeId(id) end function Ops.badgeIds(S) - local ids = {} - for _, id in ipairs(S.cat.items) do - if Ops.isBadgeId(id) then ids[#ids + 1] = id end - end - return ids + return Gen.badgeIds(S.save, S.cat) end function Ops.toggleBadge(S, id) - -- #515: badges are truthy inventory entries written as 1 by the in-game - -- grant (checkVictoryRewards, src/world/OverworldController.lua) and by - -- GenSave's .sav import; read and write that same shape here, or a badge - -- earned in game reads as unowned and an editor-written boolean blows up - -- Bag.add's `(inv[id] or 0) + qty` (src/inventory/Bag.lua). - local on = S.save.inventory[id] and true or false - S.save.inventory[id] = (not on) and 1 or nil - return Ops.mark(S, ("%s %s"):format(id, on and "removed" or "earned")) + local nowOn = Gen.toggleBadge(S.save, id) + return Ops.mark(S, ("%s %s"):format(id, nowOn and "earned" or "removed")) end -- ----------------------------------------------------------------- events function Ops.setFlag(S, name, on) - S.save.flags[name] = on and true or nil + Gen.setFlag(S.save, name, on) return Ops.mark(S, ("%s = %s"):format(name, tostring(on and true or false))) end @@ -801,17 +921,19 @@ end -- -------------------------------------------------------------------- dex function Ops.dex(S) - S.save.pokedex = S.save.pokedex or { seen = {}, owned = {} } + local key = Gen.dexOwnedKey(S.save) + S.save.pokedex = S.save.pokedex or { seen = {}, [key] = {} } S.save.pokedex.seen = S.save.pokedex.seen or {} - S.save.pokedex.owned = S.save.pokedex.owned or {} + S.save.pokedex[key] = S.save.pokedex[key] or {} return S.save.pokedex end function Ops.dexCounts(S) local dex = Ops.dex(S) + local key = Gen.dexOwnedKey(S.save) local seen, owned = 0, 0 for _ in pairs(dex.seen) do seen = seen + 1 end - for _ in pairs(dex.owned) do owned = owned + 1 end + for _ in pairs(dex[key] or {}) do owned = owned + 1 end return seen, owned, #S.cat.species end @@ -819,25 +941,28 @@ end -- the game's own rule, enforced here so a hand-edited dex stays legal. function Ops.dexSeen(S, species, on) local dex = Ops.dex(S) + local key = Gen.dexOwnedKey(S.save) dex.seen[species] = on and true or nil - if not on then dex.owned[species] = nil end + if not on then dex[key][species] = nil end return Ops.mark(S, ("%s %s"):format(species, on and "marked seen" or "cleared")) end function Ops.dexOwned(S, species, on) local dex = Ops.dex(S) - dex.owned[species] = on and true or nil + local key = Gen.dexOwnedKey(S.save) + dex[key][species] = on and true or nil if on then dex.seen[species] = true end return Ops.mark(S, ("%s %s"):format(species, on and "marked owned" or "un-owned")) end function Ops.dexStamp(S) local dex = Ops.dex(S) + local key = Gen.dexOwnedKey(S.save) local n = 0 local function stamp(mon) - if not dex.owned[mon.species] then n = n + 1 end + if not dex[key][mon.species] then n = n + 1 end dex.seen[mon.species] = true - dex.owned[mon.species] = true + dex[key][mon.species] = true end for _, m in ipairs(S.save.party) do stamp(m) end for _, box in ipairs(S.save.boxes or {}) do @@ -855,9 +980,10 @@ end function Ops.dexOwnAll(S) local dex = Ops.dex(S) + local key = Gen.dexOwnedKey(S.save) for _, species in ipairs(S.cat.species) do dex.seen[species] = true - dex.owned[species] = true + dex[key][species] = true end return Ops.mark(S, ("Marked all %d species owned"):format(#S.cat.species)) end @@ -866,7 +992,8 @@ function Ops.dexClear(S) if not Ops.arm(S, "dex-clear", "Wipe the whole Pokedex? Click again to confirm") then return false end - S.save.pokedex = { seen = {}, owned = {} } + local key = Gen.dexOwnedKey(S.save) + S.save.pokedex = { seen = {}, [key] = {} } return Ops.mark(S, "Pokedex wiped") end @@ -927,6 +1054,11 @@ end -- OVERWORLD/PLATEAU tilesets, maps with connections, or fly spots the save -- has already visited. function Ops.isOutdoor(S, map) + if not map or not map.def then return false end + local Map2 = require("src.world.gen2.Map") + if Gen.ofState(S) == 2 and Map2.isOutdoor then + return Map2.isOutdoor(map.def) and true or false + end if map.def.tileset == "OVERWORLD" or map.def.tileset == "PLATEAU" then return true end @@ -937,15 +1069,17 @@ end function Ops.setPlayerHere(S) local cell = S.mapClickCell if not cell then return Ops.say(S, "Click a cell first") end - S.save.player.map = S.mapId - S.save.player.x = cell.cx - S.save.player.y = cell.cy + Gen.setPlayerHere(S.save, S.mapId, cell.cx, cell.cy) return Ops.mark(S, ("Player set to %s (%d,%d)"):format(S.mapId, cell.cx, cell.cy)) end function Ops.setLastOutdoor(S, map) local cell = S.mapClickCell if not cell then return Ops.say(S, "Click a cell first") end + if Gen.ofState(S) == 2 then + S.save.spawn = S.mapId + return Ops.mark(S, ("spawn set to %s"):format(S.mapId)) + end if not Ops.isOutdoor(S, map) then return Ops.say(S, S.mapId .. " doesn't look outdoor (no connections, not visited)") end @@ -956,8 +1090,50 @@ end function Ops.setLastHeal(S) local cell = S.mapClickCell if not cell then return Ops.say(S, "Click a cell first") end + if Gen.ofState(S) == 2 then + S.save.spawn = S.mapId + return Ops.mark(S, ("spawn set to %s"):format(S.mapId)) + end S.save.lastHeal = { map = S.mapId, x = cell.cx, y = cell.cy } return Ops.mark(S, ("lastHeal set to %s (%d,%d)"):format(S.mapId, cell.cx, cell.cy)) end +function Ops.setHeldItem(S, mon, id) + if not mon then return false end + if id == "" or id == nil then + if not mon.item then return Ops.say(S, "No held item to clear") end + local was = mon.item + mon.item = nil + syncPartyMailHeldItem(S, mon, was, nil) + return Ops.mark(S, ("Cleared held item (%s)"):format(was)) + end + if not S.data.items[id] then + return Ops.say(S, ("%s is not an item"):format(tostring(id))) + end + local was = mon.item + mon.item = id + syncPartyMailHeldItem(S, mon, was, id) + return Ops.mark(S, ("%s now holds %s"):format(mon.species, id)) +end + +function Ops.setHappiness(S, mon, value) + if not mon then return false end + local want = clamp(math.floor(value), 0, 255) + if want == (mon.happiness or 0) then + return Ops.say(S, ("Happiness is already %d"):format(want)) + end + mon.happiness = want + return Ops.mark(S, ("%s happiness %d"):format(mon.species, want)) +end + +function Ops.setPokerus(S, mon, value) + if not mon then return false end + local want = clamp(math.floor(value), 0, 255) + if want == (mon.pokerus or 0) then + return Ops.say(S, ("Pokerus is already %d"):format(want)) + end + mon.pokerus = want + return Ops.mark(S, ("%s pokerus byte %d"):format(mon.species, want)) +end + return Ops diff --git a/tools/save-editor/panels/Boxes.lua b/tools/save-editor/panels/Boxes.lua index e3975f9e..dddd7510 100644 --- a/tools/save-editor/panels/Boxes.lua +++ b/tools/save-editor/panels/Boxes.lua @@ -19,7 +19,6 @@ -- in the selected box as a Lv5 mon built by the same MonOps path partyAdd -- uses, so its stats, exp and moves are consistent. -local BoxesMod = require("src.pokemon.Boxes") local PartyMod = require("src.pokemon.Party") local Theme = require("Theme") local Ops = require("Ops") @@ -33,12 +32,12 @@ local function drawStrip(S, Kit, boxes, x, y, stripW, h) local s = Kit.scale local pad = 16 * s Kit.card(x, y, stripW, h) - Kit.caption(x + pad, y + pad, ("BOXES . %d"):format(BoxesMod.COUNT)) + Kit.caption(x + pad, y + pad, ("BOXES . %d"):format(Ops.boxCount(S))) local stripTop = y + pad + Kit.textHeight("caption") + 10 * s local stripInner = stripW - 2 * pad local bRowH = math.min(30 * s, math.max(22 * s, - (h - (stripTop - y) - pad - (BoxesMod.COUNT - 1) * 6 * s) / BoxesMod.COUNT)) - for i = 1, BoxesMod.COUNT do + (h - (stripTop - y) - pad - (Ops.boxCount(S) - 1) * 6 * s) / Ops.boxCount(S))) + for i = 1, Ops.boxCount(S) do local ry = stripTop + (i - 1) * (bRowH + 6 * s) if ry + bRowH > y + h - pad then break end if Kit.row(x + pad, ry, stripInner, bRowH, i == S.selectedBox, PAL.blue, 9 * s) then @@ -52,7 +51,7 @@ local function drawStrip(S, Kit, boxes, x, y, stripW, h) ry + (bRowH - Kit.textHeight("tiny")) / 2, PAL.caption) local mx = x + pad + stripInner - 10 * s - countW - 8 * s - 44 * s Kit.meter(mx, ry + (bRowH - 5 * s) / 2, 44 * s, 5 * s, - fill / BoxesMod.CAPACITY * 100, fill >= BoxesMod.CAPACITY and PAL.yellow or PAL.blue) + fill / Ops.boxCapacity(S) * 100, fill >= Ops.boxCapacity(S) and PAL.yellow or PAL.blue) end end @@ -66,7 +65,7 @@ local function drawGrid(S, Kit, box, gridX, y, gridW, h) Kit.text("tab", ("Box %d"):format(S.selectedBox), gx, y + gpad + (headH - Kit.textHeight("tab")) / 2, PAL.heading) local titleW = Kit.textWidth("tab", ("Box %d"):format(S.selectedBox)) - Kit.text("mono", ("%d/%d"):format(#box, BoxesMod.CAPACITY), + Kit.text("mono", ("%d/%d"):format(#box, Ops.boxCapacity(S)), gx + titleW + 14 * s, y + gpad + (headH - Kit.textHeight("mono")) / 2, PAL.caption) local navW = 34 * s if Kit.stepper(gx + ginner - 2 * navW - 8 * s, y + gpad, navW, headH, "<", @@ -102,7 +101,7 @@ local function drawGrid(S, Kit, box, gridX, y, gridW, h) end if Kit.button(gx + wdW + 10 * s, actY, addW, actH, addLabel, { font = "small", radius = 9 * s, - enabled = #box < BoxesMod.CAPACITY }) then + enabled = #box < Ops.boxCapacity(S) }) then Ops.openBoxAddPicker(S, Kit) end if Kit.button(gx + ginner - relW, actY, relW, actH, relLabel, @@ -119,7 +118,7 @@ local function drawGrid(S, Kit, box, gridX, y, gridW, h) -- of five slivers. local cols = math.max(2, math.min(COLS, math.floor((ginner + cellGap) / (86 * s + cellGap)))) - local rows = math.ceil(BoxesMod.CAPACITY / cols) + local rows = math.ceil(Ops.boxCapacity(S) / cols) local cellW = math.max(0, (ginner - cellGap * (cols - 1)) / cols) -- floor at Kit's 26px tap target so a short window shrinks the cells but -- never inverts them (#715); overflow clips inside the grid body rather @@ -128,7 +127,7 @@ local function drawGrid(S, Kit, box, gridX, y, gridW, h) math.min((gridH - cellGap * (rows - 1)) / rows, 110 * s)) Kit.pushClip(gx, gridTop, ginner, gridH) - for i = 1, BoxesMod.CAPACITY do + for i = 1, Ops.boxCapacity(S) do local cc = (i - 1) % cols local cr = math.floor((i - 1) / cols) local bx = gx + cc * (cellW + cellGap) @@ -220,7 +219,7 @@ function M.draw(S, Kit, x, y, w, h) local s = Kit.scale local gap = 20 * s - S.selectedBox = Ops.clamp(S.selectedBox or 1, 1, BoxesMod.COUNT) + S.selectedBox = Ops.clamp(S.selectedBox or 1, 1, Ops.boxCount(S)) S.save.currentBox = S.selectedBox local boxes = Ops.boxes(S) local box = boxes[S.selectedBox] diff --git a/tools/save-editor/panels/Dex.lua b/tools/save-editor/panels/Dex.lua index d7929485..f22c30ee 100644 --- a/tools/save-editor/panels/Dex.lua +++ b/tools/save-editor/panels/Dex.lua @@ -145,7 +145,8 @@ function M.draw(S, Kit, x, y, w, h) local rx = cx + ci * (colW + colGap) local ry = gridTop + ri * (rowH + rowGap) local isSeen = dex.seen[id] == true - local isOwned = dex.owned[id] == true + local ownedKey = require("Gen").dexOwnedKey(S.save) + local isOwned = dex[ownedKey] and dex[ownedKey][id] == true Theme.row(rx, ry, colW, rowH, 9 * s, 0.6) local def = S.data.pokemon[id] diff --git a/tools/save-editor/panels/Events.lua b/tools/save-editor/panels/Events.lua index 70b075f7..a0f26010 100644 --- a/tools/save-editor/panels/Events.lua +++ b/tools/save-editor/panels/Events.lua @@ -9,6 +9,7 @@ local Theme = require("Theme") local Ops = require("Ops") +local Gen = require("Gen") local PAL = Theme.PAL local M = {} @@ -50,7 +51,7 @@ local function buildRows(S) if contains(name, filter) then rows[#rows + 1] = { label = name, - checked = S.save.flags[name] == true, + checked = Gen.getFlag(S.save, name), set = function(on) Ops.setFlag(S, name, on) end, } end @@ -107,7 +108,12 @@ function M.draw(S, Kit, x, y, w, h) -- past the card edge. local pillH = 32 * s local px, py = cx, y + pad - for _, t in ipairs(SUB_TABS) do + local pills = SUB_TABS + if Gen.of(S.save) == 2 then + pills = { SUB_TABS[1] } + if S.eventsTab ~= "flags" then S.eventsTab = "flags" end + end + for _, t in ipairs(pills) do local pw = Kit.textWidth("small", t.label) + 32 * s if px > cx and px + pw > cx + inner then px = cx diff --git a/tools/save-editor/panels/Items.lua b/tools/save-editor/panels/Items.lua index 9b93d3a3..e43a8282 100644 --- a/tools/save-editor/panels/Items.lua +++ b/tools/save-editor/panels/Items.lua @@ -18,6 +18,7 @@ local Bag = require("src.inventory.Bag") local Theme = require("Theme") local Ops = require("Ops") +local Gen = require("Gen") local PAL = Theme.PAL local M = {} @@ -46,7 +47,11 @@ local function quantityRow(S, Kit, x, y, w, h, id, qty, selected, onMinus, onPlu local qtyW = Kit.textWidth("monoRow", qtyText) Kit.textRight("monoRow", qtyText, bx - 10 * s, y + (h - Kit.textHeight("monoRow")) / 2, PAL.heading) - Kit.text("mono", Kit.ellipsize("mono", id, bx - qtyW - 30 * s - (x + 10 * s)), + local label = id + if Gen.of(S.save) == 2 then + label = (Bag.pocketOf(id, S.data) or "ITEM") .. " " .. id + end + Kit.text("mono", Kit.ellipsize("mono", label, bx - qtyW - 30 * s - (x + 10 * s)), x + 10 * s, y + (h - Kit.textHeight("mono")) / 2, PAL.text) return clicked end @@ -55,9 +60,13 @@ end -- Each card is a function of its own rect so the wide (three column) and the -- stacked (#715) layouts are the same drawing code with different geometry. -local function moneyHeight(Kit, s, pad) - return pad * 2 + Kit.textHeight("caption") + 8 * s +local function moneyHeight(Kit, s, pad, S) + local h = pad * 2 + Kit.textHeight("caption") + 8 * s + Kit.textHeight("headline") + 10 * s + 30 * s + if S and Gen.of(S.save) == 2 then + h = h + 28 * s + end + return h end local function drawMoney(S, Kit, x, y, w, h) @@ -68,11 +77,19 @@ local function drawMoney(S, Kit, x, y, w, h) local maxW = 74 * s if Kit.button(x + w - pad - maxW, y + pad - 4 * s, maxW, 26 * s, "Max out", { kind = "accent", font = "tiny", radius = 7 * s, - enabled = (S.save.money or 0) < Ops.MONEY_MAX }) then + enabled = Gen.money(S.save) < Ops.MONEY_MAX }) then Ops.maxMoney(S) end - Kit.text("headline", ("$%d"):format(S.save.money or 0), x + pad, + Kit.text("headline", ("$%d"):format(Gen.money(S.save)), x + pad, y + pad + Kit.textHeight("caption") + 8 * s, PAL.yellow) + if Gen.of(S.save) == 2 then + Kit.text("mono", ("COINS %d"):format(Gen.coins(S.save)), x + pad + 160 * s, + y + pad + Kit.textHeight("caption") + 8 * s, PAL.muted) + if Kit.button(x + w - pad - 74 * s, y + pad + 26 * s, 74 * s, 22 * s, "+100 coins", + { kind = "ghost", font = "tiny", radius = 6 * s }) then + Ops.addCoins(S, 100) + end + end local mbY = y + h - pad - 30 * s local mbW = (w - 2 * pad - 3 * 8 * s) / 4 for i, delta in ipairs(MONEY_STEPS) do @@ -99,10 +116,7 @@ local function drawBadges(S, Kit, x, y, w, h) Kit.card(x, y, w, h) local earned = 0 for _, id in ipairs(badgeIds) do - -- #515: truthy check, not `== true` -- the in-game grant path stores a - -- number (see OverworldController.lua checkVictoryRewards), matching - -- src/inventory/Badges.lua's own truthy read. - if S.save.inventory[id] then earned = earned + 1 end + if Gen.hasBadge(S.save, id) then earned = earned + 1 end end Kit.caption(x + pad, y + pad, "BADGES") Kit.textRight("mono", ("%d/%d"):format(earned, #badgeIds), x + w - pad, @@ -112,7 +126,7 @@ local function drawBadges(S, Kit, x, y, w, h) for i, id in ipairs(badgeIds) do local bc = (i - 1) % BADGE_COLS local br = math.floor((i - 1) / BADGE_COLS) - local on = S.save.inventory[id] + local on = Gen.hasBadge(S.save, id) local short = id:gsub("BADGE$", "") if Kit.chip(x + pad + bc * (bW + 7 * s), bTop + br * (28 * s + 7 * s), bW, 28 * s, Kit.ellipsize("micro", short, bW - 8 * s), on, @@ -253,7 +267,7 @@ function M.draw(S, Kit, x, y, w, h) -- drag over their own bodies first. local off = Theme.clamp(S.itemsScroll or 0, 0, math.max(0, (S._itemsContentH or 0) - h)) - local moneyH = moneyHeight(Kit, s, pad) + local moneyH = moneyHeight(Kit, s, pad, S) local badgeH = badgeHeight(S, Kit, s, pad) local pickH = 280 * s local listH = 300 * s @@ -278,7 +292,7 @@ function M.draw(S, Kit, x, y, w, h) -- Money and badges are fixed-height so the picker gets every pixel left -- over: cycling through ~250 item ids in a two-row list was the thing that -- made the old panel unusable. - local moneyH = moneyHeight(Kit, s, pad) + 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) diff --git a/tools/save-editor/panels/MapBrowser.lua b/tools/save-editor/panels/MapBrowser.lua index 24c02d91..dd18d4cd 100644 --- a/tools/save-editor/panels/MapBrowser.lua +++ b/tools/save-editor/panels/MapBrowser.lua @@ -14,12 +14,18 @@ local MapLoader = require("src.world.MapLoader") local Warp = require("src.world.Warp") local Theme = require("Theme") local Ops = require("Ops") +local Gen = require("Gen") local PAL = Theme.PAL local MapBrowser = {} local CELL = 16 -- the walk grid; a cell is 16px of map art +local function playerPos(S) + local map, x, y = Gen.playerMap(S.save) + return map, x or 0, y or 0 +end + local function clampZoom(z) if z < 1 then return 1 end if z > 4 then return 4 end @@ -38,7 +44,7 @@ MapBrowser.centerOn = centerOn local function sortedMapIds(data) local ids = {} - for id in pairs(data.maps) do ids[#ids + 1] = id end + for id in pairs(Gen.maps(data)) do ids[#ids + 1] = id end table.sort(ids) return ids end @@ -52,7 +58,19 @@ local OUTSIDE_TILESETS = { OVERWORLD = true, PLATEAU = true } local function goToWarp(S, warp) local def = warp.def - local fromMap = S.data.maps[S.mapId] + if Gen.of(S.save) == 2 then + local dest = def.destMap or def.map + if dest then + S.mapId = dest + S.mapClickCell = nil + S._mapCenteredFor = dest + S.status = "Followed warp to " .. tostring(dest) + else + S.status = "Warp has no destination map" + end + return + end + local fromMap = Gen.maps(S.data)[S.mapId] if fromMap and OUTSIDE_TILESETS[fromMap.tileset] and def.destMap ~= "LAST_MAP" and def.destMap ~= S.mapId then S.save.lastOutdoor = { id = S.mapId, x = def.x, y = def.y } @@ -125,22 +143,25 @@ local function drawOverlays(S, map) return cx * CELL - S.mapCamX, cy * CELL - S.mapCamY, CELL, CELL end love.graphics.setColor(0.27, 0.59, 1, 0.55) - for _, wdef in ipairs(map.def.warps) do + for _, wdef in ipairs(map.def.warps or {}) do love.graphics.rectangle("line", cellRect(wdef.x, wdef.y)) end - if S.save.player.map == S.mapId then + local playerMap, px, py = playerPos(S) + if playerMap == S.mapId then love.graphics.setColor(1, 0.36, 0.4, 0.9) - love.graphics.rectangle("fill", cellRect(S.save.player.x, S.save.player.y)) + love.graphics.rectangle("fill", cellRect(px, py)) end - local heal = S.save.lastHeal - if heal and heal.map == S.mapId then - love.graphics.setColor(0.24, 0.88, 0.54, 0.9) - love.graphics.rectangle("line", cellRect(heal.x, heal.y)) - end - local out = S.save.lastOutdoor - if out and out.id == S.mapId then - love.graphics.setColor(1, 0.8, 0.02, 0.9) - love.graphics.rectangle("line", cellRect(out.x, out.y)) + if Gen.of(S.save) ~= 2 then + local heal = S.save.lastHeal + if heal and heal.map == S.mapId then + love.graphics.setColor(0.24, 0.88, 0.54, 0.9) + love.graphics.rectangle("line", cellRect(heal.x, heal.y)) + end + local out = S.save.lastOutdoor + if out and out.id == S.mapId then + love.graphics.setColor(1, 0.8, 0.02, 0.9) + love.graphics.rectangle("line", cellRect(out.x, out.y)) + end end if S.mapClickCell then love.graphics.setColor(1, 1, 0.35, 0.95) @@ -239,9 +260,13 @@ function MapBrowser.draw(S, Kit, x, y, w, h) #ids, perPage) if Kit.button(lr.x + pad, gotoY, listInner, gotoH, "Go to save location", { font = "small", radius = 9 * s }) then - MapBrowser.select(S, S.save.player.map) - Ops.say(S, ("Jumped to %s (%d,%d)"):format(S.save.player.map, - S.save.player.x, S.save.player.y)) + local pmap, px, py = playerPos(S) + if pmap then + MapBrowser.select(S, pmap) + Ops.say(S, ("Jumped to %s (%d,%d)"):format(pmap, px, py)) + else + Ops.say(S, "No player location on this save") + end end -- ---------------------------------------------------------- the viewport @@ -253,7 +278,32 @@ function MapBrowser.draw(S, Kit, x, y, w, h) Kit.text("monoBig", tostring(S.mapId), vx0, vr.y + vpad + (headH - Kit.textHeight("monoBig")) / 2, PAL.heading) - local ok, map = pcall(MapLoader.load, S.data, S.mapId) + local ok, map + if Gen.of(S.save) == 2 then + local def = Gen.maps(S.data)[S.mapId] + if def then + local Map2 = require("src.world.gen2.Map") + if type(def.width) ~= "number" or type(def.height) ~= "number" then + ok, map = false, "incomplete map record (missing width/height)" + else + local tileset = Gen.tilesets(S.data)[def.tileset] + ok, map = pcall(Map2.new, def, tileset or {}) + if ok and map and not map.renderer then + local MapPreview = require("src.world.gen2.MapPreview") + S._g2MapBaker = S._g2MapBaker or MapPreview.baker({ + tilesets = Gen.tilesets(S.data), + gen2Roofs = S.data.gen2Roofs, roofs = S.data.roofs, + gen2Palettes = S.data.gen2Palettes, palettes = S.data.palettes, + }) + map.renderer = MapPreview.renderer(S._g2MapBaker, map) + end + end + else + ok, map = false, "unknown map" + end + else + ok, map = pcall(MapLoader.load, S.data, S.mapId) + end if not ok then Kit.text("mono", "Failed to load map: " .. tostring(map), vx0, vr.y + vpad + headH + 20 * s, PAL.red) @@ -279,12 +329,13 @@ function MapBrowser.draw(S, Kit, x, y, w, h) local zBtn = 32 * s local rightEdge = vx0 + vinner local zoomW = 2 * zBtn + 56 * s + 12 * s + local pmap, px, py = playerPos(S) local showCenter = vinner >= zoomW + 10 * s + centerW + 160 * s if showCenter then if Kit.button(rightEdge - centerW, vr.y + vpad, centerW, headH, "Center on player", { kind = "accent", font = "small", radius = 7 * s }) then - if S.save.player.map == S.mapId then - centerOn(S, S.save.player.x, S.save.player.y) + if pmap == S.mapId then + centerOn(S, px, py) Ops.say(S, "Centred on the player") else Ops.say(S, "Player isn't on this map") @@ -312,10 +363,11 @@ function MapBrowser.draw(S, Kit, x, y, w, h) -- the panel has laid itself out. if S._mapCenteredFor ~= S.mapId then S._mapCenteredFor = S.mapId - if S.save.player.map == S.mapId then - centerOn(S, S.save.player.x, S.save.player.y) + if pmap == S.mapId then + centerOn(S, px, py) else - centerOn(S, map.widthCells / 2, map.heightCells / 2) + centerOn(S, (map.widthCells or map.width or 10) / 2, + (map.heightCells or map.height or 10) / 2) end end @@ -334,7 +386,24 @@ function MapBrowser.draw(S, Kit, x, y, w, h) love.graphics.push() love.graphics.translate(vx0, vy0) love.graphics.scale(S.mapZoom, S.mapZoom) - map.renderer:draw(S.mapCamX, S.mapCamY) + if map.renderer and map.renderer.draw then + map.renderer:draw(S.mapCamX, S.mapCamY) + else + local wc = map.widthCells or ((map.width or 8) * 2) + local hc = map.heightCells or ((map.height or 8) * 2) + for cy = 0, hc - 1 do + for cx = 0, wc - 1 do + if (cx + cy) % 2 == 0 then + love.graphics.setColor(0.18, 0.22, 0.32, 1) + else + love.graphics.setColor(0.14, 0.17, 0.26, 1) + end + love.graphics.rectangle("fill", + cx * CELL - S.mapCamX, cy * CELL - S.mapCamY, CELL, CELL) + end + end + love.graphics.setColor(1, 1, 1, 1) + end drawOverlays(S, map) love.graphics.pop() love.graphics.setScissor() @@ -402,20 +471,31 @@ function MapBrowser.draw(S, Kit, x, y, w, h) Kit.caption(sx0 + pad, sr.y + pad, "SPAWN POINTS") local sTop = sr.y + pad + Kit.textHeight("caption") + 12 * s local sInner = sr.w - 2 * pad - local player = S.save.player - local out = S.save.lastOutdoor - local heal = S.save.lastHeal - local spawns = { - { key = "PLAYER", color = PAL.red, - value = ("%s (%d,%d)"):format(player.map, player.x, player.y), - set = function() Ops.setPlayerHere(S) end }, - { key = "LAST HEAL", color = PAL.green, - value = heal and ("%s (%d,%d)"):format(heal.map, heal.x, heal.y) or "unset", - set = function() Ops.setLastHeal(S) end }, - { key = "LAST OUTDOOR", color = PAL.yellow, - value = out and ("%s (%d,%d)"):format(out.id, out.x, out.y) or "unset", - set = function() Ops.setLastOutdoor(S, map) end }, - } + local pmap2, px2, py2 = playerPos(S) + local playerValue = pmap2 and ("%s (%d,%d)"):format(pmap2, px2, py2) or "unset" + local spawns + if Gen.of(S.save) == 2 then + spawns = { + { key = "PLAYER", color = PAL.red, value = playerValue, + set = function() Ops.setPlayerHere(S) end }, + { key = "SPAWN", color = PAL.green, + value = tostring(S.save.spawn or "SPAWN_HOME"), + set = function() Ops.setLastHeal(S) end }, + } + else + local out = S.save.lastOutdoor + local heal = S.save.lastHeal + spawns = { + { key = "PLAYER", color = PAL.red, value = playerValue, + set = function() Ops.setPlayerHere(S) end }, + { key = "LAST HEAL", color = PAL.green, + value = heal and ("%s (%d,%d)"):format(heal.map, heal.x, heal.y) or "unset", + set = function() Ops.setLastHeal(S) end }, + { key = "LAST OUTDOOR", color = PAL.yellow, + value = out and ("%s (%d,%d)"):format(out.id, out.x, out.y) or "unset", + set = function() Ops.setLastOutdoor(S, map) end }, + } + end local spawnH = 62 * s for i, sp in ipairs(spawns) do local ry = sTop + (i - 1) * (spawnH + 8 * s) @@ -431,7 +511,7 @@ function MapBrowser.draw(S, Kit, x, y, w, h) sx0 + pad + 12 * s, ry + spawnH - 10 * s - Kit.textHeight("mono"), PAL.muted) end - local noteY = sTop + 3 * (spawnH + 8 * s) + 6 * s + local noteY = sTop + #spawns * (spawnH + 8 * s) + 6 * s Kit.textCenter("tiny", "Click a cell first. Warp cells follow the warp instead of selecting. " .. "Arrow keys / WASD pan, the wheel zooms.", diff --git a/tools/save-editor/panels/MonEditor.lua b/tools/save-editor/panels/MonEditor.lua index 8ccdc13d..b9286048 100644 --- a/tools/save-editor/panels/MonEditor.lua +++ b/tools/save-editor/panels/MonEditor.lua @@ -19,17 +19,26 @@ local Theme = require("Theme") local Ops = require("Ops") local PAL = Theme.PAL +local Gen = require("Gen") local MonEditor = {} local DV_KEYS = { "attack", "defense", "speed", "special" } -local STAT_KEYS = { +local STAT_KEYS_G1 = { { key = "HP", field = "hp" }, { key = "ATK", field = "attack" }, { key = "DEF", field = "defense" }, { key = "SPD", field = "speed" }, { key = "SPC", field = "special" }, } +local STAT_KEYS_G2 = { + { key = "HP", field = "hp" }, + { key = "ATK", field = "attack" }, + { key = "DEF", field = "defense" }, + { key = "SPA", field = "specialAttack" }, + { key = "SPD", field = "specialDefense" }, + { key = "SPE", field = "speed" }, +} -- Front sprites are read straight off the generated cache. One image per -- species, cached for the process: the old panel called newImage every frame, @@ -96,7 +105,7 @@ local function drawLevelRow(S, Kit, mon, lx0, ly) end lx = lx + bw + 8 * s end - Kit.text("mono", ("EXP %d"):format(mon.exp or 0), lx + 6 * s, + Kit.text("mono", ("EXP %d"):format(Gen.exp(mon)), lx + 6 * s, ly + (lh - Kit.textHeight("mono")) / 2, PAL.muted) return lh end @@ -106,7 +115,7 @@ end local function levelRowWidth(Kit, mon) local s = Kit.scale return 52 * s + 2 * (40 * s + 8 * s) + 58 * s + 8 * s + 2 * (40 * s + 8 * s) - + 6 * s + Kit.textWidth("mono", ("EXP %d"):format(mon.exp or 0)) + + 6 * s + Kit.textWidth("mono", ("EXP %d"):format(Gen.exp(mon))) end local function drawDvRows(S, Kit, mon, cx, rowY, colW, rowH, rowGap) @@ -179,7 +188,7 @@ function MonEditor.draw(S, Kit, x, y, w, h) local tw = math.min(w - 40 * s, 340 * s) Kit.textCenter("button", "Pick a slot on the left to inspect it. Every change here re-runs the " .. - "Gen1 stat formulas, so HP and stats stay legal.", + "stat formulas, so HP and stats stay legal.", x + (w - tw) / 2, y + h / 2 - Kit.textHeight("button"), tw, PAL.muted) return end @@ -220,10 +229,13 @@ function MonEditor.draw(S, Kit, x, y, w, h) end -- the nickname section: a caption line (with the Clear button on it) plus -- the field + Set row + local extraH = 0 + if Gen.ofState(S) == 2 then extraH = 88 * s end local nickFieldH = 30 * s local contentH = pad + headerH + 18 * s + capH + 10 * s + nickFieldH + 18 * s + capH + 10 * s + cellH + 18 * s + + extraH + colsH + pad -- Called before the widgets so this frame already draws at the updated @@ -311,8 +323,9 @@ function MonEditor.draw(S, Kit, x, y, w, h) local statsY = nickY + capH + 10 * s + nickFieldH + 18 * s Kit.caption(cx, statsY, "STATS . recalculated from level + DVs") statsY = statsY + capH + 10 * s + local STAT_KEYS = Gen.ofState(S) == 2 and STAT_KEYS_G2 or STAT_KEYS_G1 local gap = 12 * s - local cellW = (inner - gap * 4) / 5 + local cellW = (inner - gap * (#STAT_KEYS - 1)) / #STAT_KEYS for i, st in ipairs(STAT_KEYS) do local bx = cx + (i - 1) * (cellW + gap) Theme.row(bx, statsY, cellW, cellH, 10 * s, 0.6) @@ -326,6 +339,40 @@ function MonEditor.draw(S, Kit, x, y, w, h) -- --------------------------------------------------- DVs | moves split local colY = statsY + cellH + 18 * s + if Gen.ofState(S) == 2 then + local extraY = colY + Kit.caption(cx, extraY, "GOLD") + extraY = extraY + capH + 8 * s + local row = 28 * s + Kit.text("tiny", "HELD " .. tostring(mon.item or "none"), cx, extraY, PAL.text) + if Kit.button(cx + inner - 70 * s, extraY, 70 * s, row, "Clear item", + { kind = "danger", font = "tiny", radius = 6 * s }) then + Ops.setHeldItem(S, mon, nil) + end + extraY = extraY + row + 6 * s + Kit.text("tiny", ("HAPPINESS %d"):format(mon.happiness or 0), cx, extraY, PAL.text) + if Kit.stepper(cx + 140 * s, extraY, 28 * s, row, "-", { font = "small" }) then + Ops.setHappiness(S, mon, (mon.happiness or 0) - 10) + end + if Kit.stepper(cx + 174 * s, extraY, 28 * s, row, "+", { font = "small" }) then + Ops.setHappiness(S, mon, (mon.happiness or 0) + 10) + end + Kit.text("tiny", ("PKRS %d"):format(mon.pokerus or 0), cx + 220 * s, extraY, PAL.text) + if Kit.stepper(cx + 300 * s, extraY, 28 * s, row, "-", { font = "small" }) then + Ops.setPokerus(S, mon, (mon.pokerus or 0) - 1) + end + if Kit.stepper(cx + 334 * s, extraY, 28 * s, row, "+", { font = "small" }) then + Ops.setPokerus(S, mon, (mon.pokerus or 0) + 1) + end + extraY = extraY + row + 4 * s + local bits = {} + if mon.gender then bits[#bits + 1] = mon.gender end + if mon.shiny then bits[#bits + 1] = "shiny" end + 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 + end if narrow then -- stacked: DVs first, then moves, then the two actions side by side at -- full width (#715)