From 63a7eb2cf545281cbeaf289b261f9345d9249819 Mon Sep 17 00:00:00 2001 From: SatoMew Date: Sat, 10 Sep 2022 18:44:31 +0100 Subject: [PATCH 01/26] Improve NPC trade labels and comments (#384) Co-authored-by: Rangi <35663410+Rangi42@users.noreply.github.com> --- constants/script_constants.asm | 4 ++-- data/events/trades.asm | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/constants/script_constants.asm b/constants/script_constants.asm index d4543f05..afd1258b 100644 --- a/constants/script_constants.asm +++ b/constants/script_constants.asm @@ -22,7 +22,7 @@ DEF SLOTS_SOMEONESKEYS EQU $ff const_def const TRADE_FOR_TERRY const TRADE_FOR_MARCEL - const TRADE_FOR_CHIKUCHIKU + const TRADE_FOR_CHIKUCHIKU ; unused const TRADE_FOR_SAILOR const TRADE_FOR_DUX const TRADE_FOR_MARC @@ -36,7 +36,7 @@ DEF NUM_NPC_TRADES EQU const_value ; InGameTradeTextPointers indexes (see engine/events/in_game_trades.asm) const_def const TRADE_DIALOGSET_CASUAL - const TRADE_DIALOGSET_POLITE + const TRADE_DIALOGSET_EVOLUTION const TRADE_DIALOGSET_HAPPY ; badges diff --git a/data/events/trades.asm b/data/events/trades.asm index 560abfa1..b230a918 100644 --- a/data/events/trades.asm +++ b/data/events/trades.asm @@ -2,14 +2,18 @@ TradeMons: ; entries correspond to TRADE_FOR_* constants table_width 3 + NAME_LENGTH, TradeMons ; give mon, get mon, dialog id, nickname - db NIDORINO, NIDORINA, TRADE_DIALOGSET_CASUAL, "TERRY@@@@@@" - db ABRA, MR_MIME, TRADE_DIALOGSET_CASUAL, "MARCEL@@@@@" - db BUTTERFREE, BEEDRILL, TRADE_DIALOGSET_HAPPY, "CHIKUCHIKU@" - db PONYTA, SEEL, TRADE_DIALOGSET_CASUAL, "SAILOR@@@@@" - db SPEAROW, FARFETCHD, TRADE_DIALOGSET_HAPPY, "DUX@@@@@@@@" - db SLOWBRO, LICKITUNG, TRADE_DIALOGSET_CASUAL, "MARC@@@@@@@" - db POLIWHIRL, JYNX, TRADE_DIALOGSET_POLITE, "LOLA@@@@@@@" - db RAICHU, ELECTRODE, TRADE_DIALOGSET_POLITE, "DORIS@@@@@@" - db VENONAT, TANGELA, TRADE_DIALOGSET_HAPPY, "CRINKLES@@@" - db NIDORAN_M, NIDORAN_F, TRADE_DIALOGSET_HAPPY, "SPOT@@@@@@@" + ; The two instances of TRADE_DIALOGSET_EVOLUTION are a leftover + ; from the Japanese Blue trades, which used species that evolve. + ; Japanese Red and Green used TRADE_DIALOGSET_CASUAL, and had + ; the same species as English Red and Blue. + db NIDORINO, NIDORINA, TRADE_DIALOGSET_CASUAL, "TERRY@@@@@@" + db ABRA, MR_MIME, TRADE_DIALOGSET_CASUAL, "MARCEL@@@@@" + db BUTTERFREE, BEEDRILL, TRADE_DIALOGSET_HAPPY, "CHIKUCHIKU@" ; unused + db PONYTA, SEEL, TRADE_DIALOGSET_CASUAL, "SAILOR@@@@@" + db SPEAROW, FARFETCHD, TRADE_DIALOGSET_HAPPY, "DUX@@@@@@@@" + db SLOWBRO, LICKITUNG, TRADE_DIALOGSET_CASUAL, "MARC@@@@@@@" + db POLIWHIRL, JYNX, TRADE_DIALOGSET_EVOLUTION, "LOLA@@@@@@@" + db RAICHU, ELECTRODE, TRADE_DIALOGSET_EVOLUTION, "DORIS@@@@@@" + db VENONAT, TANGELA, TRADE_DIALOGSET_HAPPY, "CRINKLES@@@" + db NIDORAN_M, NIDORAN_F, TRADE_DIALOGSET_HAPPY, "SPOT@@@@@@@" assert_table_length NUM_NPC_TRADES From 0c4d2e178ebbcee1d0b8de6354fef2d1134d90bc Mon Sep 17 00:00:00 2001 From: Rangi Date: Sat, 10 Sep 2022 13:52:11 -0400 Subject: [PATCH 02/26] Link to the symbols branch --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 950b8b99..706ab86c 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ To set up the repository, see [**INSTALL.md**](INSTALL.md). ## See also +- [**Wiki**][wiki] (includes [tutorials][tutorials]) +- [**Symbols**][symbols] - **Discord:** [pret][discord] - **IRC:** [libera#pret][irc] @@ -37,6 +39,9 @@ Other disassembly projects: [pokeruby]: https://github.com/pret/pokeruby [pokefirered]: https://github.com/pret/pokefirered [pokeemerald]: https://github.com/pret/pokeemerald +[wiki]: https://github.com/pret/pokered/wiki +[tutorials]: https://github.com/pret/pokered/wiki/Tutorials +[symbols]: https://github.com/pret/pokered/tree/symbols [discord]: https://discord.gg/d5dubZ3 [irc]: https://web.libera.chat/?#pret [ci]: https://github.com/pret/pokered/actions From 74cee737f6990fd770838eb8fe4d090028a76bd9 Mon Sep 17 00:00:00 2001 From: vulcandth Date: Sun, 11 Sep 2022 00:05:04 -0500 Subject: [PATCH 03/26] Rename `macros/wram.asm` to `macros/ram.asm` (#388) --- constants/map_object_constants.asm | 4 ++-- constants/pokemon_data_constants.asm | 2 +- macros/{wram.asm => ram.asm} | 0 ram.asm | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename macros/{wram.asm => ram.asm} (100%) diff --git a/constants/map_object_constants.asm b/constants/map_object_constants.asm index 5c666d83..377edcea 100644 --- a/constants/map_object_constants.asm +++ b/constants/map_object_constants.asm @@ -1,4 +1,4 @@ -; spritestatedata1 struct members (see macros/wram.asm) +; spritestatedata1 struct members (see macros/ram.asm) const_def const SPRITESTATEDATA1_PICTUREID ; 0 const SPRITESTATEDATA1_MOVEMENTSTATUS ; 1 @@ -18,7 +18,7 @@ const SPRITESTATEDATA1_0F ; f DEF SPRITESTATEDATA1_LENGTH EQU const_value -; spritestatedata2 struct members (see macros/wram.asm) +; spritestatedata2 struct members (see macros/ram.asm) const_def const SPRITESTATEDATA2_WALKANIMATIONCOUNTER ; 0 const SPRITESTATEDATA2_01 ; 1 diff --git a/constants/pokemon_data_constants.asm b/constants/pokemon_data_constants.asm index 1fd7ce19..028d6711 100644 --- a/constants/pokemon_data_constants.asm +++ b/constants/pokemon_data_constants.asm @@ -23,7 +23,7 @@ DEF BASE_TMHM rb (NUM_TM_HM + 7) / 8 rb_skip DEF BASE_DATA_SIZE EQU _RS -; party_struct members (see macros/wram.asm) +; party_struct members (see macros/ram.asm) rsreset DEF MON_SPECIES rb DEF MON_HP rw diff --git a/macros/wram.asm b/macros/ram.asm similarity index 100% rename from macros/wram.asm rename to macros/ram.asm diff --git a/ram.asm b/ram.asm index 24040b14..077ae799 100644 --- a/ram.asm +++ b/ram.asm @@ -1,6 +1,6 @@ INCLUDE "constants.asm" -INCLUDE "macros/wram.asm" +INCLUDE "macros/ram.asm" INCLUDE "ram/vram.asm" From e9e769fba15c2373d43f37721a45427a9218c154 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 11 Sep 2022 15:00:06 -0400 Subject: [PATCH 04/26] Copy pokecrystal's `map_const` macro Fixes #386 --- constants/map_constants.asm | 502 ++++++++++++++++++------------------ 1 file changed, 251 insertions(+), 251 deletions(-) diff --git a/constants/map_constants.asm b/constants/map_constants.asm index 48b25461..644cfab4 100644 --- a/constants/map_constants.asm +++ b/constants/map_constants.asm @@ -1,7 +1,7 @@ -MACRO mapconst +MACRO map_const const \1 - DEF \1_HEIGHT EQU \2 - DEF \1_WIDTH EQU \3 + DEF \1_WIDTH EQU \2 + DEF \1_HEIGHT EQU \3 ENDM ; map ids @@ -16,257 +16,257 @@ ENDM ; Each map also has associated data in maps.asm. ; Order: towns/cities, then routes, then indoor/dungeon maps const_def - mapconst PALLET_TOWN, 9, 10 ; $00 - mapconst VIRIDIAN_CITY, 18, 20 ; $01 - mapconst PEWTER_CITY, 18, 20 ; $02 - mapconst CERULEAN_CITY, 18, 20 ; $03 - mapconst LAVENDER_TOWN, 9, 10 ; $04 - mapconst VERMILION_CITY, 18, 20 ; $05 - mapconst CELADON_CITY, 18, 25 ; $06 - mapconst FUCHSIA_CITY, 18, 20 ; $07 - mapconst CINNABAR_ISLAND, 9, 10 ; $08 - mapconst INDIGO_PLATEAU, 9, 10 ; $09 - mapconst SAFFRON_CITY, 18, 20 ; $0A + map_const PALLET_TOWN, 10, 9 ; $00 + map_const VIRIDIAN_CITY, 20, 18 ; $01 + map_const PEWTER_CITY, 20, 18 ; $02 + map_const CERULEAN_CITY, 20, 18 ; $03 + map_const LAVENDER_TOWN, 10, 9 ; $04 + map_const VERMILION_CITY, 20, 18 ; $05 + map_const CELADON_CITY, 25, 18 ; $06 + map_const FUCHSIA_CITY, 20, 18 ; $07 + map_const CINNABAR_ISLAND, 10, 9 ; $08 + map_const INDIGO_PLATEAU, 10, 9 ; $09 + map_const SAFFRON_CITY, 20, 18 ; $0A DEF NUM_CITY_MAPS EQU const_value - mapconst UNUSED_MAP_0B, 0, 0 ; $0B + map_const UNUSED_MAP_0B, 0, 0 ; $0B DEF FIRST_ROUTE_MAP EQU const_value - mapconst ROUTE_1, 18, 10 ; $0C - mapconst ROUTE_2, 36, 10 ; $0D - mapconst ROUTE_3, 9, 35 ; $0E - mapconst ROUTE_4, 9, 45 ; $0F - mapconst ROUTE_5, 18, 10 ; $10 - mapconst ROUTE_6, 18, 10 ; $11 - mapconst ROUTE_7, 9, 10 ; $12 - mapconst ROUTE_8, 9, 30 ; $13 - mapconst ROUTE_9, 9, 30 ; $14 - mapconst ROUTE_10, 36, 10 ; $15 - mapconst ROUTE_11, 9, 30 ; $16 - mapconst ROUTE_12, 54, 10 ; $17 - mapconst ROUTE_13, 9, 30 ; $18 - mapconst ROUTE_14, 27, 10 ; $19 - mapconst ROUTE_15, 9, 30 ; $1A - mapconst ROUTE_16, 9, 20 ; $1B - mapconst ROUTE_17, 72, 10 ; $1C - mapconst ROUTE_18, 9, 25 ; $1D - mapconst ROUTE_19, 27, 10 ; $1E - mapconst ROUTE_20, 9, 50 ; $1F - mapconst ROUTE_21, 45, 10 ; $20 - mapconst ROUTE_22, 9, 20 ; $21 - mapconst ROUTE_23, 72, 10 ; $22 - mapconst ROUTE_24, 18, 10 ; $23 - mapconst ROUTE_25, 9, 30 ; $24 + map_const ROUTE_1, 10, 18 ; $0C + map_const ROUTE_2, 10, 36 ; $0D + map_const ROUTE_3, 35, 9 ; $0E + map_const ROUTE_4, 45, 9 ; $0F + map_const ROUTE_5, 10, 18 ; $10 + map_const ROUTE_6, 10, 18 ; $11 + map_const ROUTE_7, 10, 9 ; $12 + map_const ROUTE_8, 30, 9 ; $13 + map_const ROUTE_9, 30, 9 ; $14 + map_const ROUTE_10, 10, 36 ; $15 + map_const ROUTE_11, 30, 9 ; $16 + map_const ROUTE_12, 10, 54 ; $17 + map_const ROUTE_13, 30, 9 ; $18 + map_const ROUTE_14, 10, 27 ; $19 + map_const ROUTE_15, 30, 9 ; $1A + map_const ROUTE_16, 20, 9 ; $1B + map_const ROUTE_17, 10, 72 ; $1C + map_const ROUTE_18, 25, 9 ; $1D + map_const ROUTE_19, 10, 27 ; $1E + map_const ROUTE_20, 50, 9 ; $1F + map_const ROUTE_21, 10, 45 ; $20 + map_const ROUTE_22, 20, 9 ; $21 + map_const ROUTE_23, 10, 72 ; $22 + map_const ROUTE_24, 10, 18 ; $23 + map_const ROUTE_25, 30, 9 ; $24 DEF FIRST_INDOOR_MAP EQU const_value - mapconst REDS_HOUSE_1F, 4, 4 ; $25 - mapconst REDS_HOUSE_2F, 4, 4 ; $26 - mapconst BLUES_HOUSE, 4, 4 ; $27 - mapconst OAKS_LAB, 6, 5 ; $28 - mapconst VIRIDIAN_POKECENTER, 4, 7 ; $29 - mapconst VIRIDIAN_MART, 4, 4 ; $2A - mapconst VIRIDIAN_SCHOOL_HOUSE, 4, 4 ; $2B - mapconst VIRIDIAN_NICKNAME_HOUSE, 4, 4 ; $2C - mapconst VIRIDIAN_GYM, 9, 10 ; $2D - mapconst DIGLETTS_CAVE_ROUTE_2, 4, 4 ; $2E - mapconst VIRIDIAN_FOREST_NORTH_GATE, 4, 5 ; $2F - mapconst ROUTE_2_TRADE_HOUSE, 4, 4 ; $30 - mapconst ROUTE_2_GATE, 4, 5 ; $31 - mapconst VIRIDIAN_FOREST_SOUTH_GATE, 4, 5 ; $32 - mapconst VIRIDIAN_FOREST, 24, 17 ; $33 - mapconst MUSEUM_1F, 4, 10 ; $34 - mapconst MUSEUM_2F, 4, 7 ; $35 - mapconst PEWTER_GYM, 7, 5 ; $36 - mapconst PEWTER_NIDORAN_HOUSE, 4, 4 ; $37 - mapconst PEWTER_MART, 4, 4 ; $38 - mapconst PEWTER_SPEECH_HOUSE, 4, 4 ; $39 - mapconst PEWTER_POKECENTER, 4, 7 ; $3A - mapconst MT_MOON_1F, 18, 20 ; $3B - mapconst MT_MOON_B1F, 14, 14 ; $3C - mapconst MT_MOON_B2F, 18, 20 ; $3D - mapconst CERULEAN_TRASHED_HOUSE, 4, 4 ; $3E - mapconst CERULEAN_TRADE_HOUSE, 4, 4 ; $3F - mapconst CERULEAN_POKECENTER, 4, 7 ; $40 - mapconst CERULEAN_GYM, 7, 5 ; $41 - mapconst BIKE_SHOP, 4, 4 ; $42 - mapconst CERULEAN_MART, 4, 4 ; $43 - mapconst MT_MOON_POKECENTER, 4, 7 ; $44 - mapconst CERULEAN_TRASHED_HOUSE_COPY, 4, 4 ; $45 - mapconst ROUTE_5_GATE, 3, 4 ; $46 - mapconst UNDERGROUND_PATH_ROUTE_5, 4, 4 ; $47 - mapconst DAYCARE, 4, 4 ; $48 - mapconst ROUTE_6_GATE, 3, 4 ; $49 - mapconst UNDERGROUND_PATH_ROUTE_6, 4, 4 ; $4A - mapconst UNDERGROUND_PATH_ROUTE_6_COPY, 4, 4 ; $4B - mapconst ROUTE_7_GATE, 4, 3 ; $4C - mapconst UNDERGROUND_PATH_ROUTE_7, 4, 4 ; $4D - mapconst UNDERGROUND_PATH_ROUTE_7_COPY, 4, 4 ; $4E - mapconst ROUTE_8_GATE, 4, 3 ; $4F - mapconst UNDERGROUND_PATH_ROUTE_8, 4, 4 ; $50 - mapconst ROCK_TUNNEL_POKECENTER, 4, 7 ; $51 - mapconst ROCK_TUNNEL_1F, 18, 20 ; $52 - mapconst POWER_PLANT, 18, 20 ; $53 - mapconst ROUTE_11_GATE_1F, 5, 4 ; $54 - mapconst DIGLETTS_CAVE_ROUTE_11, 4, 4 ; $55 - mapconst ROUTE_11_GATE_2F, 4, 4 ; $56 - mapconst ROUTE_12_GATE_1F, 4, 5 ; $57 - mapconst BILLS_HOUSE, 4, 4 ; $58 - mapconst VERMILION_POKECENTER, 4, 7 ; $59 - mapconst POKEMON_FAN_CLUB, 4, 4 ; $5A - mapconst VERMILION_MART, 4, 4 ; $5B - mapconst VERMILION_GYM, 9, 5 ; $5C - mapconst VERMILION_PIDGEY_HOUSE, 4, 4 ; $5D - mapconst VERMILION_DOCK, 6, 14 ; $5E - mapconst SS_ANNE_1F, 9, 20 ; $5F - mapconst SS_ANNE_2F, 9, 20 ; $60 - mapconst SS_ANNE_3F, 3, 10 ; $61 - mapconst SS_ANNE_B1F, 4, 15 ; $62 - mapconst SS_ANNE_BOW, 7, 10 ; $63 - mapconst SS_ANNE_KITCHEN, 8, 7 ; $64 - mapconst SS_ANNE_CAPTAINS_ROOM, 4, 3 ; $65 - mapconst SS_ANNE_1F_ROOMS, 8, 12 ; $66 - mapconst SS_ANNE_2F_ROOMS, 8, 12 ; $67 - mapconst SS_ANNE_B1F_ROOMS, 8, 12 ; $68 - mapconst UNUSED_MAP_69, 0, 0 ; $69 - mapconst UNUSED_MAP_6A, 0, 0 ; $6A - mapconst UNUSED_MAP_6B, 0, 0 ; $6B - mapconst VICTORY_ROAD_1F, 9, 10 ; $6C - mapconst UNUSED_MAP_6D, 0, 0 ; $6D - mapconst UNUSED_MAP_6E, 0, 0 ; $6E - mapconst UNUSED_MAP_6F, 0, 0 ; $6F - mapconst UNUSED_MAP_70, 0, 0 ; $70 - mapconst LANCES_ROOM, 13, 13 ; $71 - mapconst UNUSED_MAP_72, 0, 0 ; $72 - mapconst UNUSED_MAP_73, 0, 0 ; $73 - mapconst UNUSED_MAP_74, 0, 0 ; $74 - mapconst UNUSED_MAP_75, 0, 0 ; $75 - mapconst HALL_OF_FAME, 4, 5 ; $76 - mapconst UNDERGROUND_PATH_NORTH_SOUTH, 24, 4 ; $77 - mapconst CHAMPIONS_ROOM, 4, 4 ; $78 - mapconst UNDERGROUND_PATH_WEST_EAST, 4, 25 ; $79 - mapconst CELADON_MART_1F, 4, 10 ; $7A - mapconst CELADON_MART_2F, 4, 10 ; $7B - mapconst CELADON_MART_3F, 4, 10 ; $7C - mapconst CELADON_MART_4F, 4, 10 ; $7D - mapconst CELADON_MART_ROOF, 4, 10 ; $7E - mapconst CELADON_MART_ELEVATOR, 2, 2 ; $7F - mapconst CELADON_MANSION_1F, 6, 4 ; $80 - mapconst CELADON_MANSION_2F, 6, 4 ; $81 - mapconst CELADON_MANSION_3F, 6, 4 ; $82 - mapconst CELADON_MANSION_ROOF, 6, 4 ; $83 - mapconst CELADON_MANSION_ROOF_HOUSE, 4, 4 ; $84 - mapconst CELADON_POKECENTER, 4, 7 ; $85 - mapconst CELADON_GYM, 9, 5 ; $86 - mapconst GAME_CORNER, 9, 10 ; $87 - mapconst CELADON_MART_5F, 4, 10 ; $88 - mapconst GAME_CORNER_PRIZE_ROOM, 4, 5 ; $89 - mapconst CELADON_DINER, 4, 5 ; $8A - mapconst CELADON_CHIEF_HOUSE, 4, 4 ; $8B - mapconst CELADON_HOTEL, 4, 7 ; $8C - mapconst LAVENDER_POKECENTER, 4, 7 ; $8D - mapconst POKEMON_TOWER_1F, 9, 10 ; $8E - mapconst POKEMON_TOWER_2F, 9, 10 ; $8F - mapconst POKEMON_TOWER_3F, 9, 10 ; $90 - mapconst POKEMON_TOWER_4F, 9, 10 ; $91 - mapconst POKEMON_TOWER_5F, 9, 10 ; $92 - mapconst POKEMON_TOWER_6F, 9, 10 ; $93 - mapconst POKEMON_TOWER_7F, 9, 10 ; $94 - mapconst MR_FUJIS_HOUSE, 4, 4 ; $95 - mapconst LAVENDER_MART, 4, 4 ; $96 - mapconst LAVENDER_CUBONE_HOUSE, 4, 4 ; $97 - mapconst FUCHSIA_MART, 4, 4 ; $98 - mapconst FUCHSIA_BILLS_GRANDPAS_HOUSE, 4, 4 ; $99 - mapconst FUCHSIA_POKECENTER, 4, 7 ; $9A - mapconst WARDENS_HOUSE, 4, 5 ; $9B - mapconst SAFARI_ZONE_GATE, 3, 4 ; $9C - mapconst FUCHSIA_GYM, 9, 5 ; $9D - mapconst FUCHSIA_MEETING_ROOM, 4, 7 ; $9E - mapconst SEAFOAM_ISLANDS_B1F, 9, 15 ; $9F - mapconst SEAFOAM_ISLANDS_B2F, 9, 15 ; $A0 - mapconst SEAFOAM_ISLANDS_B3F, 9, 15 ; $A1 - mapconst SEAFOAM_ISLANDS_B4F, 9, 15 ; $A2 - mapconst VERMILION_OLD_ROD_HOUSE, 4, 4 ; $A3 - mapconst FUCHSIA_GOOD_ROD_HOUSE, 4, 4 ; $A4 - mapconst POKEMON_MANSION_1F, 14, 15 ; $A5 - mapconst CINNABAR_GYM, 9, 10 ; $A6 - mapconst CINNABAR_LAB, 4, 9 ; $A7 - mapconst CINNABAR_LAB_TRADE_ROOM, 4, 4 ; $A8 - mapconst CINNABAR_LAB_METRONOME_ROOM, 4, 4 ; $A9 - mapconst CINNABAR_LAB_FOSSIL_ROOM, 4, 4 ; $AA - mapconst CINNABAR_POKECENTER, 4, 7 ; $AB - mapconst CINNABAR_MART, 4, 4 ; $AC - mapconst CINNABAR_MART_COPY, 4, 4 ; $AD - mapconst INDIGO_PLATEAU_LOBBY, 6, 8 ; $AE - mapconst COPYCATS_HOUSE_1F, 4, 4 ; $AF - mapconst COPYCATS_HOUSE_2F, 4, 4 ; $B0 - mapconst FIGHTING_DOJO, 6, 5 ; $B1 - mapconst SAFFRON_GYM, 9, 10 ; $B2 - mapconst SAFFRON_PIDGEY_HOUSE, 4, 4 ; $B3 - mapconst SAFFRON_MART, 4, 4 ; $B4 - mapconst SILPH_CO_1F, 9, 15 ; $B5 - mapconst SAFFRON_POKECENTER, 4, 7 ; $B6 - mapconst MR_PSYCHICS_HOUSE, 4, 4 ; $B7 - mapconst ROUTE_15_GATE_1F, 5, 4 ; $B8 - mapconst ROUTE_15_GATE_2F, 4, 4 ; $B9 - mapconst ROUTE_16_GATE_1F, 7, 4 ; $BA - mapconst ROUTE_16_GATE_2F, 4, 4 ; $BB - mapconst ROUTE_16_FLY_HOUSE, 4, 4 ; $BC - mapconst ROUTE_12_SUPER_ROD_HOUSE, 4, 4 ; $BD - mapconst ROUTE_18_GATE_1F, 5, 4 ; $BE - mapconst ROUTE_18_GATE_2F, 4, 4 ; $BF - mapconst SEAFOAM_ISLANDS_1F, 9, 15 ; $C0 - mapconst ROUTE_22_GATE, 4, 5 ; $C1 - mapconst VICTORY_ROAD_2F, 9, 15 ; $C2 - mapconst ROUTE_12_GATE_2F, 4, 4 ; $C3 - mapconst VERMILION_TRADE_HOUSE, 4, 4 ; $C4 - mapconst DIGLETTS_CAVE, 18, 20 ; $C5 - mapconst VICTORY_ROAD_3F, 9, 15 ; $C6 - mapconst ROCKET_HIDEOUT_B1F, 14, 15 ; $C7 - mapconst ROCKET_HIDEOUT_B2F, 14, 15 ; $C8 - mapconst ROCKET_HIDEOUT_B3F, 14, 15 ; $C9 - mapconst ROCKET_HIDEOUT_B4F, 12, 15 ; $CA - mapconst ROCKET_HIDEOUT_ELEVATOR, 4, 3 ; $CB - mapconst UNUSED_MAP_CC, 0, 0 ; $CC - mapconst UNUSED_MAP_CD, 0, 0 ; $CD - mapconst UNUSED_MAP_CE, 0, 0 ; $CE - mapconst SILPH_CO_2F, 9, 15 ; $CF - mapconst SILPH_CO_3F, 9, 15 ; $D0 - mapconst SILPH_CO_4F, 9, 15 ; $D1 - mapconst SILPH_CO_5F, 9, 15 ; $D2 - mapconst SILPH_CO_6F, 9, 13 ; $D3 - mapconst SILPH_CO_7F, 9, 13 ; $D4 - mapconst SILPH_CO_8F, 9, 13 ; $D5 - mapconst POKEMON_MANSION_2F, 14, 15 ; $D6 - mapconst POKEMON_MANSION_3F, 9, 15 ; $D7 - mapconst POKEMON_MANSION_B1F, 14, 15 ; $D8 - mapconst SAFARI_ZONE_EAST, 13, 15 ; $D9 - mapconst SAFARI_ZONE_NORTH, 18, 20 ; $DA - mapconst SAFARI_ZONE_WEST, 13, 15 ; $DB - mapconst SAFARI_ZONE_CENTER, 13, 15 ; $DC - mapconst SAFARI_ZONE_CENTER_REST_HOUSE, 4, 4 ; $DD - mapconst SAFARI_ZONE_SECRET_HOUSE, 4, 4 ; $DE - mapconst SAFARI_ZONE_WEST_REST_HOUSE, 4, 4 ; $DF - mapconst SAFARI_ZONE_EAST_REST_HOUSE, 4, 4 ; $E0 - mapconst SAFARI_ZONE_NORTH_REST_HOUSE, 4, 4 ; $E1 - mapconst CERULEAN_CAVE_2F, 9, 15 ; $E2 - mapconst CERULEAN_CAVE_B1F, 9, 15 ; $E3 - mapconst CERULEAN_CAVE_1F, 9, 15 ; $E4 - mapconst NAME_RATERS_HOUSE, 4, 4 ; $E5 - mapconst CERULEAN_BADGE_HOUSE, 4, 4 ; $E6 - mapconst UNUSED_MAP_E7, 0, 0 ; $E7 - mapconst ROCK_TUNNEL_B1F, 18, 20 ; $E8 - mapconst SILPH_CO_9F, 9, 13 ; $E9 - mapconst SILPH_CO_10F, 9, 8 ; $EA - mapconst SILPH_CO_11F, 9, 9 ; $EB - mapconst SILPH_CO_ELEVATOR, 2, 2 ; $EC - mapconst UNUSED_MAP_ED, 0, 0 ; $ED - mapconst UNUSED_MAP_EE, 0, 0 ; $EE - mapconst TRADE_CENTER, 4, 5 ; $EF - mapconst COLOSSEUM, 4, 5 ; $F0 - mapconst UNUSED_MAP_F1, 0, 0 ; $F1 - mapconst UNUSED_MAP_F2, 0, 0 ; $F2 - mapconst UNUSED_MAP_F3, 0, 0 ; $F3 - mapconst UNUSED_MAP_F4, 0, 0 ; $F4 - mapconst LORELEIS_ROOM, 6, 5 ; $F5 - mapconst BRUNOS_ROOM, 6, 5 ; $F6 - mapconst AGATHAS_ROOM, 6, 5 ; $F7 + map_const REDS_HOUSE_1F, 4, 4 ; $25 + map_const REDS_HOUSE_2F, 4, 4 ; $26 + map_const BLUES_HOUSE, 4, 4 ; $27 + map_const OAKS_LAB, 5, 6 ; $28 + map_const VIRIDIAN_POKECENTER, 7, 4 ; $29 + map_const VIRIDIAN_MART, 4, 4 ; $2A + map_const VIRIDIAN_SCHOOL_HOUSE, 4, 4 ; $2B + map_const VIRIDIAN_NICKNAME_HOUSE, 4, 4 ; $2C + map_const VIRIDIAN_GYM, 10, 9 ; $2D + map_const DIGLETTS_CAVE_ROUTE_2, 4, 4 ; $2E + map_const VIRIDIAN_FOREST_NORTH_GATE, 5, 4 ; $2F + map_const ROUTE_2_TRADE_HOUSE, 4, 4 ; $30 + map_const ROUTE_2_GATE, 5, 4 ; $31 + map_const VIRIDIAN_FOREST_SOUTH_GATE, 5, 4 ; $32 + map_const VIRIDIAN_FOREST, 17, 24 ; $33 + map_const MUSEUM_1F, 10, 4 ; $34 + map_const MUSEUM_2F, 7, 4 ; $35 + map_const PEWTER_GYM, 5, 7 ; $36 + map_const PEWTER_NIDORAN_HOUSE, 4, 4 ; $37 + map_const PEWTER_MART, 4, 4 ; $38 + map_const PEWTER_SPEECH_HOUSE, 4, 4 ; $39 + map_const PEWTER_POKECENTER, 7, 4 ; $3A + map_const MT_MOON_1F, 20, 18 ; $3B + map_const MT_MOON_B1F, 14, 14 ; $3C + map_const MT_MOON_B2F, 20, 18 ; $3D + map_const CERULEAN_TRASHED_HOUSE, 4, 4 ; $3E + map_const CERULEAN_TRADE_HOUSE, 4, 4 ; $3F + map_const CERULEAN_POKECENTER, 7, 4 ; $40 + map_const CERULEAN_GYM, 5, 7 ; $41 + map_const BIKE_SHOP, 4, 4 ; $42 + map_const CERULEAN_MART, 4, 4 ; $43 + map_const MT_MOON_POKECENTER, 7, 4 ; $44 + map_const CERULEAN_TRASHED_HOUSE_COPY, 4, 4 ; $45 + map_const ROUTE_5_GATE, 4, 3 ; $46 + map_const UNDERGROUND_PATH_ROUTE_5, 4, 4 ; $47 + map_const DAYCARE, 4, 4 ; $48 + map_const ROUTE_6_GATE, 4, 3 ; $49 + map_const UNDERGROUND_PATH_ROUTE_6, 4, 4 ; $4A + map_const UNDERGROUND_PATH_ROUTE_6_COPY, 4, 4 ; $4B + map_const ROUTE_7_GATE, 3, 4 ; $4C + map_const UNDERGROUND_PATH_ROUTE_7, 4, 4 ; $4D + map_const UNDERGROUND_PATH_ROUTE_7_COPY, 4, 4 ; $4E + map_const ROUTE_8_GATE, 3, 4 ; $4F + map_const UNDERGROUND_PATH_ROUTE_8, 4, 4 ; $50 + map_const ROCK_TUNNEL_POKECENTER, 7, 4 ; $51 + map_const ROCK_TUNNEL_1F, 20, 18 ; $52 + map_const POWER_PLANT, 20, 18 ; $53 + map_const ROUTE_11_GATE_1F, 4, 5 ; $54 + map_const DIGLETTS_CAVE_ROUTE_11, 4, 4 ; $55 + map_const ROUTE_11_GATE_2F, 4, 4 ; $56 + map_const ROUTE_12_GATE_1F, 5, 4 ; $57 + map_const BILLS_HOUSE, 4, 4 ; $58 + map_const VERMILION_POKECENTER, 7, 4 ; $59 + map_const POKEMON_FAN_CLUB, 4, 4 ; $5A + map_const VERMILION_MART, 4, 4 ; $5B + map_const VERMILION_GYM, 5, 9 ; $5C + map_const VERMILION_PIDGEY_HOUSE, 4, 4 ; $5D + map_const VERMILION_DOCK, 14, 6 ; $5E + map_const SS_ANNE_1F, 20, 9 ; $5F + map_const SS_ANNE_2F, 20, 9 ; $60 + map_const SS_ANNE_3F, 10, 3 ; $61 + map_const SS_ANNE_B1F, 15, 4 ; $62 + map_const SS_ANNE_BOW, 10, 7 ; $63 + map_const SS_ANNE_KITCHEN, 7, 8 ; $64 + map_const SS_ANNE_CAPTAINS_ROOM, 3, 4 ; $65 + map_const SS_ANNE_1F_ROOMS, 12, 8 ; $66 + map_const SS_ANNE_2F_ROOMS, 12, 8 ; $67 + map_const SS_ANNE_B1F_ROOMS, 12, 8 ; $68 + map_const UNUSED_MAP_69, 0, 0 ; $69 + map_const UNUSED_MAP_6A, 0, 0 ; $6A + map_const UNUSED_MAP_6B, 0, 0 ; $6B + map_const VICTORY_ROAD_1F, 10, 9 ; $6C + map_const UNUSED_MAP_6D, 0, 0 ; $6D + map_const UNUSED_MAP_6E, 0, 0 ; $6E + map_const UNUSED_MAP_6F, 0, 0 ; $6F + map_const UNUSED_MAP_70, 0, 0 ; $70 + map_const LANCES_ROOM, 13, 13 ; $71 + map_const UNUSED_MAP_72, 0, 0 ; $72 + map_const UNUSED_MAP_73, 0, 0 ; $73 + map_const UNUSED_MAP_74, 0, 0 ; $74 + map_const UNUSED_MAP_75, 0, 0 ; $75 + map_const HALL_OF_FAME, 5, 4 ; $76 + map_const UNDERGROUND_PATH_NORTH_SOUTH, 4, 24 ; $77 + map_const CHAMPIONS_ROOM, 4, 4 ; $78 + map_const UNDERGROUND_PATH_WEST_EAST, 25, 4 ; $79 + map_const CELADON_MART_1F, 10, 4 ; $7A + map_const CELADON_MART_2F, 10, 4 ; $7B + map_const CELADON_MART_3F, 10, 4 ; $7C + map_const CELADON_MART_4F, 10, 4 ; $7D + map_const CELADON_MART_ROOF, 10, 4 ; $7E + map_const CELADON_MART_ELEVATOR, 2, 2 ; $7F + map_const CELADON_MANSION_1F, 4, 6 ; $80 + map_const CELADON_MANSION_2F, 4, 6 ; $81 + map_const CELADON_MANSION_3F, 4, 6 ; $82 + map_const CELADON_MANSION_ROOF, 4, 6 ; $83 + map_const CELADON_MANSION_ROOF_HOUSE, 4, 4 ; $84 + map_const CELADON_POKECENTER, 7, 4 ; $85 + map_const CELADON_GYM, 5, 9 ; $86 + map_const GAME_CORNER, 10, 9 ; $87 + map_const CELADON_MART_5F, 10, 4 ; $88 + map_const GAME_CORNER_PRIZE_ROOM, 5, 4 ; $89 + map_const CELADON_DINER, 5, 4 ; $8A + map_const CELADON_CHIEF_HOUSE, 4, 4 ; $8B + map_const CELADON_HOTEL, 7, 4 ; $8C + map_const LAVENDER_POKECENTER, 7, 4 ; $8D + map_const POKEMON_TOWER_1F, 10, 9 ; $8E + map_const POKEMON_TOWER_2F, 10, 9 ; $8F + map_const POKEMON_TOWER_3F, 10, 9 ; $90 + map_const POKEMON_TOWER_4F, 10, 9 ; $91 + map_const POKEMON_TOWER_5F, 10, 9 ; $92 + map_const POKEMON_TOWER_6F, 10, 9 ; $93 + map_const POKEMON_TOWER_7F, 10, 9 ; $94 + map_const MR_FUJIS_HOUSE, 4, 4 ; $95 + map_const LAVENDER_MART, 4, 4 ; $96 + map_const LAVENDER_CUBONE_HOUSE, 4, 4 ; $97 + map_const FUCHSIA_MART, 4, 4 ; $98 + map_const FUCHSIA_BILLS_GRANDPAS_HOUSE, 4, 4 ; $99 + map_const FUCHSIA_POKECENTER, 7, 4 ; $9A + map_const WARDENS_HOUSE, 5, 4 ; $9B + map_const SAFARI_ZONE_GATE, 4, 3 ; $9C + map_const FUCHSIA_GYM, 5, 9 ; $9D + map_const FUCHSIA_MEETING_ROOM, 7, 4 ; $9E + map_const SEAFOAM_ISLANDS_B1F, 15, 9 ; $9F + map_const SEAFOAM_ISLANDS_B2F, 15, 9 ; $A0 + map_const SEAFOAM_ISLANDS_B3F, 15, 9 ; $A1 + map_const SEAFOAM_ISLANDS_B4F, 15, 9 ; $A2 + map_const VERMILION_OLD_ROD_HOUSE, 4, 4 ; $A3 + map_const FUCHSIA_GOOD_ROD_HOUSE, 4, 4 ; $A4 + map_const POKEMON_MANSION_1F, 15, 14 ; $A5 + map_const CINNABAR_GYM, 10, 9 ; $A6 + map_const CINNABAR_LAB, 9, 4 ; $A7 + map_const CINNABAR_LAB_TRADE_ROOM, 4, 4 ; $A8 + map_const CINNABAR_LAB_METRONOME_ROOM, 4, 4 ; $A9 + map_const CINNABAR_LAB_FOSSIL_ROOM, 4, 4 ; $AA + map_const CINNABAR_POKECENTER, 7, 4 ; $AB + map_const CINNABAR_MART, 4, 4 ; $AC + map_const CINNABAR_MART_COPY, 4, 4 ; $AD + map_const INDIGO_PLATEAU_LOBBY, 8, 6 ; $AE + map_const COPYCATS_HOUSE_1F, 4, 4 ; $AF + map_const COPYCATS_HOUSE_2F, 4, 4 ; $B0 + map_const FIGHTING_DOJO, 5, 6 ; $B1 + map_const SAFFRON_GYM, 10, 9 ; $B2 + map_const SAFFRON_PIDGEY_HOUSE, 4, 4 ; $B3 + map_const SAFFRON_MART, 4, 4 ; $B4 + map_const SILPH_CO_1F, 15, 9 ; $B5 + map_const SAFFRON_POKECENTER, 7, 4 ; $B6 + map_const MR_PSYCHICS_HOUSE, 4, 4 ; $B7 + map_const ROUTE_15_GATE_1F, 4, 5 ; $B8 + map_const ROUTE_15_GATE_2F, 4, 4 ; $B9 + map_const ROUTE_16_GATE_1F, 4, 7 ; $BA + map_const ROUTE_16_GATE_2F, 4, 4 ; $BB + map_const ROUTE_16_FLY_HOUSE, 4, 4 ; $BC + map_const ROUTE_12_SUPER_ROD_HOUSE, 4, 4 ; $BD + map_const ROUTE_18_GATE_1F, 4, 5 ; $BE + map_const ROUTE_18_GATE_2F, 4, 4 ; $BF + map_const SEAFOAM_ISLANDS_1F, 15, 9 ; $C0 + map_const ROUTE_22_GATE, 5, 4 ; $C1 + map_const VICTORY_ROAD_2F, 15, 9 ; $C2 + map_const ROUTE_12_GATE_2F, 4, 4 ; $C3 + map_const VERMILION_TRADE_HOUSE, 4, 4 ; $C4 + map_const DIGLETTS_CAVE, 20, 18 ; $C5 + map_const VICTORY_ROAD_3F, 15, 9 ; $C6 + map_const ROCKET_HIDEOUT_B1F, 15, 14 ; $C7 + map_const ROCKET_HIDEOUT_B2F, 15, 14 ; $C8 + map_const ROCKET_HIDEOUT_B3F, 15, 14 ; $C9 + map_const ROCKET_HIDEOUT_B4F, 15, 12 ; $CA + map_const ROCKET_HIDEOUT_ELEVATOR, 3, 4 ; $CB + map_const UNUSED_MAP_CC, 0, 0 ; $CC + map_const UNUSED_MAP_CD, 0, 0 ; $CD + map_const UNUSED_MAP_CE, 0, 0 ; $CE + map_const SILPH_CO_2F, 15, 9 ; $CF + map_const SILPH_CO_3F, 15, 9 ; $D0 + map_const SILPH_CO_4F, 15, 9 ; $D1 + map_const SILPH_CO_5F, 15, 9 ; $D2 + map_const SILPH_CO_6F, 13, 9 ; $D3 + map_const SILPH_CO_7F, 13, 9 ; $D4 + map_const SILPH_CO_8F, 13, 9 ; $D5 + map_const POKEMON_MANSION_2F, 15, 14 ; $D6 + map_const POKEMON_MANSION_3F, 15, 9 ; $D7 + map_const POKEMON_MANSION_B1F, 15, 14 ; $D8 + map_const SAFARI_ZONE_EAST, 15, 13 ; $D9 + map_const SAFARI_ZONE_NORTH, 20, 18 ; $DA + map_const SAFARI_ZONE_WEST, 15, 13 ; $DB + map_const SAFARI_ZONE_CENTER, 15, 13 ; $DC + map_const SAFARI_ZONE_CENTER_REST_HOUSE, 4, 4 ; $DD + map_const SAFARI_ZONE_SECRET_HOUSE, 4, 4 ; $DE + map_const SAFARI_ZONE_WEST_REST_HOUSE, 4, 4 ; $DF + map_const SAFARI_ZONE_EAST_REST_HOUSE, 4, 4 ; $E0 + map_const SAFARI_ZONE_NORTH_REST_HOUSE, 4, 4 ; $E1 + map_const CERULEAN_CAVE_2F, 15, 9 ; $E2 + map_const CERULEAN_CAVE_B1F, 15, 9 ; $E3 + map_const CERULEAN_CAVE_1F, 15, 9 ; $E4 + map_const NAME_RATERS_HOUSE, 4, 4 ; $E5 + map_const CERULEAN_BADGE_HOUSE, 4, 4 ; $E6 + map_const UNUSED_MAP_E7, 0, 0 ; $E7 + map_const ROCK_TUNNEL_B1F, 20, 18 ; $E8 + map_const SILPH_CO_9F, 13, 9 ; $E9 + map_const SILPH_CO_10F, 8, 9 ; $EA + map_const SILPH_CO_11F, 9, 9 ; $EB + map_const SILPH_CO_ELEVATOR, 2, 2 ; $EC + map_const UNUSED_MAP_ED, 0, 0 ; $ED + map_const UNUSED_MAP_EE, 0, 0 ; $EE + map_const TRADE_CENTER, 5, 4 ; $EF + map_const COLOSSEUM, 5, 4 ; $F0 + map_const UNUSED_MAP_F1, 0, 0 ; $F1 + map_const UNUSED_MAP_F2, 0, 0 ; $F2 + map_const UNUSED_MAP_F3, 0, 0 ; $F3 + map_const UNUSED_MAP_F4, 0, 0 ; $F4 + map_const LORELEIS_ROOM, 5, 6 ; $F5 + map_const BRUNOS_ROOM, 5, 6 ; $F6 + map_const AGATHAS_ROOM, 5, 6 ; $F7 DEF NUM_MAPS EQU const_value ; Indoor maps, such as houses, use this as the Map ID in their exit warps From cab3ccdbbd661aa75b9861aeaaa11fd0446c8113 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 11 Sep 2022 15:00:31 -0400 Subject: [PATCH 05/26] Comment about UndergroundPathNorthSouth.blk size Fixes #387 --- constants/map_constants.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants/map_constants.asm b/constants/map_constants.asm index 644cfab4..dad63579 100644 --- a/constants/map_constants.asm +++ b/constants/map_constants.asm @@ -138,7 +138,7 @@ DEF FIRST_INDOOR_MAP EQU const_value map_const UNUSED_MAP_74, 0, 0 ; $74 map_const UNUSED_MAP_75, 0, 0 ; $75 map_const HALL_OF_FAME, 5, 4 ; $76 - map_const UNDERGROUND_PATH_NORTH_SOUTH, 4, 24 ; $77 + map_const UNDERGROUND_PATH_NORTH_SOUTH, 4, 24 ; $77 ; UndergroundPathNorthSouth.blk is actually 4x23 map_const CHAMPIONS_ROOM, 4, 4 ; $78 map_const UNDERGROUND_PATH_WEST_EAST, 25, 4 ; $79 map_const CELADON_MART_1F, 10, 4 ; $7A From d809d3d59c0c1016b89332be7f6f3003efa7a9f9 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 11 Sep 2022 15:02:51 -0400 Subject: [PATCH 06/26] Add `UNUSED_TYPE`/`UNUSED_TYPE_END` constants Fixes #385 --- constants/type_constants.asm | 2 ++ data/types/names.asm | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/constants/type_constants.asm b/constants/type_constants.asm index e9df7faf..8226af1a 100644 --- a/constants/type_constants.asm +++ b/constants/type_constants.asm @@ -12,7 +12,9 @@ DEF PHYSICAL EQU const_value const BUG ; $07 const GHOST ; $08 +DEF UNUSED_TYPES EQU const_value const_next 20 +DEF UNUSED_TYPES_END EQU const_value DEF SPECIAL EQU const_value const FIRE ; $14 diff --git a/data/types/names.asm b/data/types/names.asm index b63c072e..87da5c5f 100644 --- a/data/types/names.asm +++ b/data/types/names.asm @@ -11,7 +11,7 @@ TypeNames: dw .Bug dw .Ghost -REPT FIRE - GHOST - 1 +REPT UNUSED_TYPES_END - UNUSED_TYPES dw .Normal ENDR From 3520a4c99a530c7464a7e9e5b956cfcdbe4b28d6 Mon Sep 17 00:00:00 2001 From: Rangi Date: Thu, 15 Sep 2022 18:10:45 -0400 Subject: [PATCH 07/26] Distinguish Elite 4 "meta" constants from event constants Fixes #390 --- constants/event_constants.asm | 6 ++++-- scripts/HallOfFame.asm | 2 +- scripts/IndigoPlateauLobby.asm | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/constants/event_constants.asm b/constants/event_constants.asm index 9e2ebcc6..7bcef25c 100644 --- a/constants/event_constants.asm +++ b/constants/event_constants.asm @@ -715,7 +715,8 @@ ; Indigo Plateau events const_next $8E0 - const ELITE4_EVENTS_START +DEF INDIGO_PLATEAU_EVENTS_START EQU const_value + const_skip const EVENT_BEAT_LORELEIS_ROOM_TRAINER_0 const_skip 4 const EVENT_AUTOWALKED_INTO_LORELEIS_ROOM @@ -735,7 +736,8 @@ const_skip const EVENT_BEAT_CHAMPION_RIVAL const_skip 5 - const ELITE4_CHAMPION_EVENTS_END + const_skip +DEF INDIGO_PLATEAU_EVENTS_END EQU const_value - 1 ; Victory Road 1F events const_next $910 diff --git a/scripts/HallOfFame.asm b/scripts/HallOfFame.asm index fd13719b..45cef29c 100644 --- a/scripts/HallOfFame.asm +++ b/scripts/HallOfFame.asm @@ -40,7 +40,7 @@ HallofFameRoomScript2: ld [wLancesRoomCurScript], a ld [wHallOfFameCurScript], a ; Elite 4 events - ResetEventRange ELITE4_EVENTS_START, ELITE4_CHAMPION_EVENTS_END, 1 + ResetEventRange INDIGO_PLATEAU_EVENTS_START, INDIGO_PLATEAU_EVENTS_END, 1 xor a ld [wHallOfFameCurScript], a ld a, PALLET_TOWN diff --git a/scripts/IndigoPlateauLobby.asm b/scripts/IndigoPlateauLobby.asm index a9b549e3..d5c89536 100644 --- a/scripts/IndigoPlateauLobby.asm +++ b/scripts/IndigoPlateauLobby.asm @@ -11,7 +11,7 @@ IndigoPlateauLobby_Script: res 1, [hl] ret z ; Elite 4 events - ResetEventRange ELITE4_EVENTS_START, EVENT_LANCES_ROOM_LOCK_DOOR + ResetEventRange INDIGO_PLATEAU_EVENTS_START, EVENT_LANCES_ROOM_LOCK_DOOR ret IndigoPlateauLobby_TextPointers: From 208ea072375937c7258b4841a23185e82fb563b1 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 18 Sep 2022 23:25:22 -0400 Subject: [PATCH 08/26] Update and add more tool scripts --- tools/free_space.awk | 70 ++ tools/free_space.py | 69 ++ tools/mapreader.py | 173 +++ tools/palfix.py | 77 ++ tools/pic.py | 0 tools/png.py | 2357 +++++++++++++++++++++++++++++++++++++++++ tools/rgb555.py | 38 + tools/sym_comments.py | 52 + tools/toc.py | 99 ++ tools/unique.py | 106 ++ tools/unnamed.py | 197 ++-- tools/used_space.py | 64 ++ 12 files changed, 3207 insertions(+), 95 deletions(-) create mode 100755 tools/free_space.awk create mode 100755 tools/free_space.py create mode 100644 tools/mapreader.py create mode 100755 tools/palfix.py mode change 100644 => 100755 tools/pic.py create mode 100644 tools/png.py create mode 100755 tools/rgb555.py create mode 100755 tools/sym_comments.py create mode 100755 tools/toc.py create mode 100755 tools/unique.py create mode 100755 tools/used_space.py diff --git a/tools/free_space.awk b/tools/free_space.awk new file mode 100755 index 00000000..aa48c3e3 --- /dev/null +++ b/tools/free_space.awk @@ -0,0 +1,70 @@ +#!/usr/bin/gawk -f + +# Usage: tools/free_space.awk [BANK=] pokered.map + +# The BANK argument allows printing free space in one, all, or none of the ROM's banks. +# Valid arguments are numbers (in decimal "42" or hexadecimal "0x2a"), "all" or "none". +# If not specified, defaults to "none". +# The `BANK` argument MUST be before the map file name, otherwise it has no effect! +# Yes: tools/free_space.awk BANK=all pokered.map +# No: tools/free_space.awk pokered.map BANK=42 + +# Copyright (c) 2020, Eldred Habert. +# SPDX-License-Identifier: MIT + +BEGIN { + nb_banks = 0 + free = 0 + rom_bank = 0 # Safety net for malformed files + + # Default settings + # Variables assigned via the command-line (except through `-v`) are *after* `BEGIN` + BANK="none" +} + +# Only accept ROM banks, ignore everything else +toupper($0) ~ /^[ \t]*ROM[0X][ \t]+BANK[ \t]+#/ { + nb_banks++ + rom_bank = 1 + split($0, fields, /[ \t]/) + bank_num = strtonum(substr(fields[3], 2)) +} + +function register_bank(amount) { + free += amount + rom_bank = 0 # Reject upcoming banks by default + + if (BANK ~ /all/ || BANK == bank_num) { + printf "Bank %3d: %5d/16384 (%.2f%%)\n", bank_num, amount, amount * 100 / 16384 + } +} + +rom_bank && toupper($0) ~ /^[ \t]*EMPTY/ { + # Empty bank + register_bank(16384) +} +rom_bank && toupper($0) ~ /^[ \t]*SLACK:[ \t]/ { + if ($2 ~ /\$[0-9A-F]+/) { + register_bank(strtonum("0x" substr($2, 2))) + } else { + printf "Malformed slack line? \"%s\" does not start with '$'\n", $2 + } +} + +END { + # Determine number of banks, by rounding to upper power of 2 + total_banks = 2 # Smallest size is 2 banks + while (total_banks < nb_banks) { + total_banks *= 2 + } + + # RGBLINK omits "trailing" ROM banks, so fake them + bank_num = nb_banks + while (bank_num < total_banks) { + register_bank(16384) + bank_num++ + } + + total = total_banks * 16384 + printf "Free space: %5d/%5d (%.2f%%)\n", free, total, free * 100 / total +} diff --git a/tools/free_space.py b/tools/free_space.py new file mode 100755 index 00000000..3bf7433e --- /dev/null +++ b/tools/free_space.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python3 free_space.py [BANK=none] [pokered.map] + +Calculate the free space in the ROM or its individual banks. + +The BANK argument allows printing free space in one, all, or none of the ROM's banks. +Valid arguments are numbers (in decimal "42" or hexadecimal "0x2A"), "all" or "none". +If not specified, defaults to "none". +""" + +import sys + +from mapreader import MapReader + +def main(): + print_bank = 'none' + filename = 'pokered.map' + + for arg in sys.argv[1:]: + if arg.startswith('BANK='): + print_bank = arg.split('=', 1)[-1] + else: + filename = arg + + if print_bank not in {'all', 'none'}: + try: + print_bank = (int(print_bank[2:], 16) + if print_bank.startswith('0x') or print_bank.startswith('0X') + else int(print_bank)) + except ValueError: + error = f'Error: invalid BANK: {print_bank}' + if print_bank.isalnum(): + error += f' (did you mean: 0x{print_bank}?)' + print(error, file=sys.stderr) + sys.exit(1) + + num_banks = 0x80 + bank_size = 0x4000 # bytes + total_size = num_banks * bank_size + + reader = MapReader() + with open(filename, 'r', encoding='utf-8') as file: + reader.read_map_data(file.readlines()) + + free_space = 0 + per_bank = [] + default_bank_data = {'sections': [], 'used': 0, 'slack': bank_size} + for bank in range(num_banks): + bank_data = reader.bank_data['ROM0 bank' if bank == 0 else 'ROMX bank'] + data = bank_data.get(bank, default_bank_data) + used, slack = data['used'], data['slack'] + per_bank.append((used, slack)) + free_space += slack + + free_percent = 100 * free_space / total_size + print(f'Free space: {free_space}/{total_size} ({free_percent:.2f}%)') + if print_bank != 'none': + print() + print('bank, used, free') + for bank in range(num_banks): + used, slack = per_bank[bank] + if print_bank in {'all', bank}: + print(f'${bank:02X}, {used}, {slack}') + +if __name__ == '__main__': + main() diff --git a/tools/mapreader.py b/tools/mapreader.py new file mode 100644 index 00000000..0ab2739a --- /dev/null +++ b/tools/mapreader.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- + +# A library for parsing the pokered.map file output by rgbds. + +import re + +class MapReader: + + # {'ROM Bank': { 0: { 'sections': [ { 'beg': 1234, + # 'end': 5678, + # 'name': 'Section001', + # 'symbols': [ { 'symbol': 'Function1234', + # 'address: 1234, + # }, + # ] + # }, + # ], + # 'used': 1234, + # 'slack': 4567, + # }, + # }, + # 'OAM': { 'sections': [ { 'beg': 1234, + # 'end': 5678, + # 'name': 'Section002', + # 'symbols': [ { 'symbol': 'Data1234', + # 'address: 1234, + # }, + # ] + # }, + # ], + # 'used': 1234, + # 'slack': 4567, + # }, + # } + # + bank_data = {} + + bank_types = { + 'HRAM' : { 'size': 0x80, 'banked': False, }, + 'OAM' : { 'size': 0xA0, 'banked': False, }, + 'ROM0 bank': { 'size': 0x4000, 'banked': True, }, + 'ROMX bank': { 'size': 0x4000, 'banked': True, }, + 'SRAM bank': { 'size': 0x2000, 'banked': True, }, + 'VRAM bank': { 'size': 0x1000, 'banked': True, }, + 'WRAM bank': { 'size': 0x2000, 'banked': True, }, + } + + # FSM states + INIT, BANK, SECTION = range(3) + + # $506D-$519A ($012E bytes) ["Type Matchups"] + section_header_regex = re.compile('\$([0-9A-Fa-f]{4})-\$([0-9A-Fa-f]{4}) \(.*\) \["(.*)"\]') + # $506D = TypeMatchups + section_data_regex = re.compile('\$([0-9A-Fa-f]{4}) = (.*)') + # $3ED2 bytes + slack_regex = re.compile('\$([0-9A-Fa-f]{4}) bytes?') + + def __init__(self, *args, **kwargs): + self.__dict__.update(kwargs) + + def _parse_init(self, line): + + line = line.split(':', 1)[0] + parts = line.split(' #', 1) + + if (parts[0] in self.bank_types): + self._cur_bank_name = parts[0] + self._cur_bank_type = self.bank_types[self._cur_bank_name] + if (self._cur_bank_type['banked'] and len(parts) > 1): + parts[1] = parts[1].split(':', 1)[0] + parts[1] = parts[1].split(' ', 1)[0] + self._cur_bank = int(parts[1], 10) + if self._cur_bank_name not in self.bank_data: + self.bank_data[self._cur_bank_name] = {} + if self._cur_bank_type['banked']: + if self._cur_bank not in self.bank_data[self._cur_bank_name]: + self.bank_data[self._cur_bank_name][self._cur_bank] = {} + self._cur_data = self.bank_data[self._cur_bank_name][self._cur_bank] + else: + self._cur_data = self.bank_data[self._cur_bank_name] + + if ({} == self._cur_data): + self._cur_data['sections'] = [] + self._cur_data['used'] = 0 + self._cur_data['slack'] = self._cur_bank_type['size'] + return True + + return False + + def _parse_section_header(self, header): + + section_data = self.section_header_regex.match(header) + if section_data is not None: + beg = int(section_data.group(1), 16) + end = int(section_data.group(2), 16) + name = section_data.group(3) + self._cur_section = {'beg': beg, 'end': end, 'name': name, 'symbols': []} + self._cur_data['sections'].append(self._cur_section) + return True + return False + + def _parse_slack(self, data): + + slack_data = self.slack_regex.match(data) + slack_bytes = int(slack_data.group(1), 16) + self._cur_data['slack'] = slack_bytes + + used_bytes = 0 + + for s in self._cur_data['sections']: + used_bytes += s['end'] - s['beg'] + 1 + + self._cur_data['used'] = used_bytes + + def read_map_data(self, map): + + if type(map) is str: + map = map.split('\n') + + self._state = MapReader.INIT + self._cur_bank_name = '' + self._cur_bank_type = {} + self._cur_bank = 0 + self._cur_data = {} + + for line in map: + + line = line.rstrip() + if (MapReader.INIT == self._state): + + if (self._parse_init(line)): + self._state = MapReader.BANK + + elif (MapReader.BANK == self._state or MapReader.SECTION == self._state): + + if ('' == line): + self._state = MapReader.INIT + else: + + line = line.lstrip() + parts = line.split(': ', 1) + + if (MapReader.SECTION == self._state): + section_data = self.section_data_regex.match(parts[0]) + if section_data is not None: + address = int(section_data.group(1), 16) + name = section_data.group(2) + self._cur_section['symbols'].append({'name': name, 'address': address}) + continue + + if ('SECTION' == parts[0]): + if (self._parse_section_header(parts[1])): + self._state = MapReader.SECTION + elif ('SLACK' == parts[0]): + self._parse_slack(parts[1]) + self._state = MapReader.INIT + elif ('EMPTY' == parts[0]): + self._cur_data = {'sections': [], 'used': 0, 'slack': self._cur_bank_type['size']} + self._state = MapReader.INIT + + else: + pass + + for k, v in self.bank_data.items(): + if (self.bank_types[k]['banked']): + for _, vv in v.items(): + vv['sections'].sort(key=lambda x: x['beg']) + for vvv in vv['sections']: + vvv['symbols'].sort(key=lambda x: x['address']) + else: + v['sections'].sort(key=lambda x: x['beg']) + for vv in v['sections']: + vv['symbols'].sort(key=lambda x: x['address']) diff --git a/tools/palfix.py b/tools/palfix.py new file mode 100755 index 00000000..e7841958 --- /dev/null +++ b/tools/palfix.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python palfix.py image.png + +Fix the palette format of the input image. Colored images (Gen 2 Pokémon or +trainer sprites) will become indexed, with a palette sorted {white, light +color, dark color, black}. Grayscale images (all Gen 1 images) will become +two-bit grayscale. +""" + +import sys + +import png + +def rgb8_to_rgb5(c): + r, g, b = c + return (r // 8, g // 8, b // 8) + +def rgb5_to_rgb8(c): + r, g, b = c + return (r * 8 + r // 4, g * 8 + g // 4, b * 8 + b // 4) + +def invert(c): + r, g, b = c + return (31 - r, 31 - g, 31 - b) + +def luminance(c): + r, g, b = c + return 0.299 * r**2 + 0.587 * g**2 + 0.114 * b**2 + +def rgb5_pixels(row): + yield from (rgb8_to_rgb5(row[x:x+3]) for x in range(0, len(row), 4)) + +def is_grayscale(palette): + return (palette == ((31, 31, 31), (21, 21, 21), (10, 10, 10), (0, 0, 0)) or + palette == ((31, 31, 31), (20, 20, 20), (10, 10, 10), (0, 0, 0))) + +def fix_pal(filename): + with open(filename, 'rb') as file: + width, height, rows = png.Reader(file).asRGBA8()[:3] + rows = list(rows) + b_and_w = {(0, 0, 0), (31, 31, 31)} + colors = {c for row in rows for c in rgb5_pixels(row)} - b_and_w + if not colors: + colors = {(21, 21, 21), (10, 10, 10)} + elif len(colors) == 1: + c = colors.pop() + colors = {c, invert(c)} + elif len(colors) != 2: + return False + palette = tuple(sorted(colors | b_and_w, key=luminance, reverse=True)) + assert len(palette) == 4 + rows = [list(map(palette.index, rgb5_pixels(row))) for row in rows] + if is_grayscale(palette): + rows = [[3 - c for c in row] for row in rows] + writer = png.Writer(width, height, greyscale=True, bitdepth=2, compression=9) + else: + palette = tuple(map(rgb5_to_rgb8, palette)) + writer = png.Writer(width, height, palette=palette, bitdepth=8, compression=9) + with open(filename, 'wb') as file: + writer.write(file, rows) + return True + +def main(): + if len(sys.argv) < 2: + print(f'Usage: {sys.argv[0]} pic.png', file=sys.stderr) + sys.exit(1) + for filename in sys.argv[1:]: + if not filename.lower().endswith('.png'): + print(f'{filename} is not a .png file!', file=sys.stderr) + elif not fix_pal(filename): + print(f'{filename} has too many colors!', file=sys.stderr) + +if __name__ == '__main__': + main() diff --git a/tools/pic.py b/tools/pic.py old mode 100644 new mode 100755 diff --git a/tools/png.py b/tools/png.py new file mode 100644 index 00000000..dd9746bf --- /dev/null +++ b/tools/png.py @@ -0,0 +1,2357 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# png.py - PNG encoder/decoder in pure Python +# +# Copyright (C) 2006 Johann C. Rocholl +# Portions Copyright (C) 2009 David Jones +# And probably portions Copyright (C) 2006 Nicko van Someren +# +# Original concept by Johann C. Rocholl. +# +# LICENCE (MIT) +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +The ``png`` module can read and write PNG files. + +Installation and Overview +------------------------- + +``pip install pypng`` + +For help, type ``import png; help(png)`` in your python interpreter. + +A good place to start is the :class:`Reader` and :class:`Writer` classes. + +Coverage of PNG formats is fairly complete; +all allowable bit depths (1/2/4/8/16/24/32/48/64 bits per pixel) and +colour combinations are supported: + +- greyscale (1/2/4/8/16 bit); +- RGB, RGBA, LA (greyscale with alpha) with 8/16 bits per channel; +- colour mapped images (1/2/4/8 bit). + +Interlaced images, +which support a progressive display when downloading, +are supported for both reading and writing. + +A number of optional chunks can be specified (when writing) +and understood (when reading): ``tRNS``, ``bKGD``, ``gAMA``. + +The ``sBIT`` chunk can be used to specify precision for +non-native bit depths. + +Requires Python 3.5 or higher. +Installation is trivial, +but see the ``README.txt`` file (with the source distribution) for details. + +Full use of all features will need some reading of the PNG specification +http://www.w3.org/TR/2003/REC-PNG-20031110/. + +The package also comes with command line utilities. + +- ``pripamtopng`` converts + `Netpbm `_ PAM/PNM files to PNG; +- ``pripngtopam`` converts PNG to file PAM/PNM. + +There are a few more for simple PNG manipulations. + +Spelling and Terminology +------------------------ + +Generally British English spelling is used in the documentation. +So that's "greyscale" and "colour". +This not only matches the author's native language, +it's also used by the PNG specification. + +Colour Models +------------- + +The major colour models supported by PNG (and hence by PyPNG) are: + +- greyscale; +- greyscale--alpha; +- RGB; +- RGB--alpha. + +Also referred to using the abbreviations: L, LA, RGB, RGBA. +Each letter codes a single channel: +*L* is for Luminance or Luma or Lightness (greyscale images); +*A* stands for Alpha, the opacity channel +(used for transparency effects, but higher values are more opaque, +so it makes sense to call it opacity); +*R*, *G*, *B* stand for Red, Green, Blue (colour image). + +Lists, arrays, sequences, and so on +----------------------------------- + +When getting pixel data out of this module (reading) and +presenting data to this module (writing) there are +a number of ways the data could be represented as a Python value. + +The preferred format is a sequence of *rows*, +which each row being a sequence of *values*. +In this format, the values are in pixel order, +with all the values from all the pixels in a row +being concatenated into a single sequence for that row. + +Consider an image that is 3 pixels wide by 2 pixels high, and each pixel +has RGB components: + +Sequence of rows:: + + list([R,G,B, R,G,B, R,G,B], + [R,G,B, R,G,B, R,G,B]) + +Each row appears as its own list, +but the pixels are flattened so that three values for one pixel +simply follow the three values for the previous pixel. + +This is the preferred because +it provides a good compromise between space and convenience. +PyPNG regards itself as at liberty to replace any sequence type with +any sufficiently compatible other sequence type; +in practice each row is an array (``bytearray`` or ``array.array``). + +To allow streaming the outer list is sometimes +an iterator rather than an explicit list. + +An alternative format is a single array holding all the values. + +Array of values:: + + [R,G,B, R,G,B, R,G,B, + R,G,B, R,G,B, R,G,B] + +The entire image is one single giant sequence of colour values. +Generally an array will be used (to save space), not a list. + +The top row comes first, +and within each row the pixels are ordered from left-to-right. +Within a pixel the values appear in the order R-G-B-A +(or L-A for greyscale--alpha). + +There is another format, which should only be used with caution. +It is mentioned because it is used internally, +is close to what lies inside a PNG file itself, +and has some support from the public API. +This format is called *packed*. +When packed, each row is a sequence of bytes (integers from 0 to 255), +just as it is before PNG scanline filtering is applied. +When the bit depth is 8 this is the same as a sequence of rows; +when the bit depth is less than 8 (1, 2 and 4), +several pixels are packed into each byte; +when the bit depth is 16 each pixel value is decomposed into 2 bytes +(and `packed` is a misnomer). +This format is used by the :meth:`Writer.write_packed` method. +It isn't usually a convenient format, +but may be just right if the source data for +the PNG image comes from something that uses a similar format +(for example, 1-bit BMPs, or another PNG file). +""" + +__version__ = "0.0.21" + +import collections +import io # For io.BytesIO +import itertools +import math +# http://www.python.org/doc/2.4.4/lib/module-operator.html +import operator +import re +import struct +import sys +# http://www.python.org/doc/2.4.4/lib/module-warnings.html +import warnings +import zlib + +from array import array + + +__all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array'] + + +# The PNG signature. +# http://www.w3.org/TR/PNG/#5PNG-file-signature +signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10) + +# The xstart, ystart, xstep, ystep for the Adam7 interlace passes. +adam7 = ((0, 0, 8, 8), + (4, 0, 8, 8), + (0, 4, 4, 8), + (2, 0, 4, 4), + (0, 2, 2, 4), + (1, 0, 2, 2), + (0, 1, 1, 2)) + + +def adam7_generate(width, height): + """ + Generate the coordinates for the reduced scanlines + of an Adam7 interlaced image + of size `width` by `height` pixels. + + Yields a generator for each pass, + and each pass generator yields a series of (x, y, xstep) triples, + each one identifying a reduced scanline consisting of + pixels starting at (x, y) and taking every xstep pixel to the right. + """ + + for xstart, ystart, xstep, ystep in adam7: + if xstart >= width: + continue + yield ((xstart, y, xstep) for y in range(ystart, height, ystep)) + + +# Models the 'pHYs' chunk (used by the Reader) +Resolution = collections.namedtuple('_Resolution', 'x y unit_is_meter') + + +def group(s, n): + return list(zip(* [iter(s)] * n)) + + +def isarray(x): + return isinstance(x, array) + + +def check_palette(palette): + """ + Check a palette argument (to the :class:`Writer` class) for validity. + Returns the palette as a list if okay; + raises an exception otherwise. + """ + + # None is the default and is allowed. + if palette is None: + return None + + p = list(palette) + if not (0 < len(p) <= 256): + raise ProtocolError( + "a palette must have between 1 and 256 entries," + " see https://www.w3.org/TR/PNG/#11PLTE") + seen_triple = False + for i, t in enumerate(p): + if len(t) not in (3, 4): + raise ProtocolError( + "palette entry %d: entries must be 3- or 4-tuples." % i) + if len(t) == 3: + seen_triple = True + if seen_triple and len(t) == 4: + raise ProtocolError( + "palette entry %d: all 4-tuples must precede all 3-tuples" % i) + for x in t: + if int(x) != x or not(0 <= x <= 255): + raise ProtocolError( + "palette entry %d: " + "values must be integer: 0 <= x <= 255" % i) + return p + + +def check_sizes(size, width, height): + """ + Check that these arguments, if supplied, are consistent. + Return a (width, height) pair. + """ + + if not size: + return width, height + + if len(size) != 2: + raise ProtocolError( + "size argument should be a pair (width, height)") + if width is not None and width != size[0]: + raise ProtocolError( + "size[0] (%r) and width (%r) should match when both are used." + % (size[0], width)) + if height is not None and height != size[1]: + raise ProtocolError( + "size[1] (%r) and height (%r) should match when both are used." + % (size[1], height)) + return size + + +def check_color(c, greyscale, which): + """ + Checks that a colour argument for transparent or background options + is the right form. + Returns the colour + (which, if it's a bare integer, is "corrected" to a 1-tuple). + """ + + if c is None: + return c + if greyscale: + try: + len(c) + except TypeError: + c = (c,) + if len(c) != 1: + raise ProtocolError("%s for greyscale must be 1-tuple" % which) + if not is_natural(c[0]): + raise ProtocolError( + "%s colour for greyscale must be integer" % which) + else: + if not (len(c) == 3 and + is_natural(c[0]) and + is_natural(c[1]) and + is_natural(c[2])): + raise ProtocolError( + "%s colour must be a triple of integers" % which) + return c + + +class Error(Exception): + def __str__(self): + return self.__class__.__name__ + ': ' + ' '.join(self.args) + + +class FormatError(Error): + """ + Problem with input file format. + In other words, PNG file does not conform to + the specification in some way and is invalid. + """ + + +class ProtocolError(Error): + """ + Problem with the way the programming interface has been used, + or the data presented to it. + """ + + +class ChunkError(FormatError): + pass + + +class Default: + """The default for the greyscale parameter.""" + + +class Writer: + """ + PNG encoder in pure Python. + """ + + def __init__(self, width=None, height=None, + size=None, + greyscale=Default, + alpha=False, + bitdepth=8, + palette=None, + transparent=None, + background=None, + gamma=None, + compression=None, + interlace=False, + planes=None, + colormap=None, + maxval=None, + chunk_limit=2**20, + x_pixels_per_unit=None, + y_pixels_per_unit=None, + unit_is_meter=False): + """ + Create a PNG encoder object. + + Arguments: + + width, height + Image size in pixels, as two separate arguments. + size + Image size (w,h) in pixels, as single argument. + greyscale + Pixels are greyscale, not RGB. + alpha + Input data has alpha channel (RGBA or LA). + bitdepth + Bit depth: from 1 to 16 (for each channel). + palette + Create a palette for a colour mapped image (colour type 3). + transparent + Specify a transparent colour (create a ``tRNS`` chunk). + background + Specify a default background colour (create a ``bKGD`` chunk). + gamma + Specify a gamma value (create a ``gAMA`` chunk). + compression + zlib compression level: 0 (none) to 9 (more compressed); + default: -1 or None. + interlace + Create an interlaced image. + chunk_limit + Write multiple ``IDAT`` chunks to save memory. + x_pixels_per_unit + Number of pixels a unit along the x axis (write a + `pHYs` chunk). + y_pixels_per_unit + Number of pixels a unit along the y axis (write a + `pHYs` chunk). Along with `x_pixel_unit`, this gives + the pixel size ratio. + unit_is_meter + `True` to indicate that the unit (for the `pHYs` + chunk) is metre. + + The image size (in pixels) can be specified either by using the + `width` and `height` arguments, or with the single `size` + argument. + If `size` is used it should be a pair (*width*, *height*). + + The `greyscale` argument indicates whether input pixels + are greyscale (when true), or colour (when false). + The default is true unless `palette=` is used. + + The `alpha` argument (a boolean) specifies + whether input pixels have an alpha channel (or not). + + `bitdepth` specifies the bit depth of the source pixel values. + Each channel may have a different bit depth. + Each source pixel must have values that are + an integer between 0 and ``2**bitdepth-1``, where + `bitdepth` is the bit depth for the corresponding channel. + For example, 8-bit images have values between 0 and 255. + PNG only stores images with bit depths of + 1,2,4,8, or 16 (the same for all channels). + When `bitdepth` is not one of these values or where + channels have different bit depths, + the next highest valid bit depth is selected, + and an ``sBIT`` (significant bits) chunk is generated + that specifies the original precision of the source image. + In this case the supplied pixel values will be rescaled to + fit the range of the selected bit depth. + + The PNG file format supports many bit depth / colour model + combinations, but not all. + The details are somewhat arcane + (refer to the PNG specification for full details). + Briefly: + Bit depths < 8 (1,2,4) are only allowed with greyscale and + colour mapped images; + colour mapped images cannot have bit depth 16. + + For colour mapped images + (in other words, when the `palette` argument is specified) + the `bitdepth` argument must match one of + the valid PNG bit depths: 1, 2, 4, or 8. + (It is valid to have a PNG image with a palette and + an ``sBIT`` chunk, but the meaning is slightly different; + it would be awkward to use the `bitdepth` argument for this.) + + The `palette` option, when specified, + causes a colour mapped image to be created: + the PNG colour type is set to 3; + `greyscale` must not be true; `alpha` must not be true; + `transparent` must not be set. + The bit depth must be 1,2,4, or 8. + When a colour mapped image is created, + the pixel values are palette indexes and + the `bitdepth` argument specifies the size of these indexes + (not the size of the colour values in the palette). + + The palette argument value should be a sequence of 3- or + 4-tuples. + 3-tuples specify RGB palette entries; + 4-tuples specify RGBA palette entries. + All the 4-tuples (if present) must come before all the 3-tuples. + A ``PLTE`` chunk is created; + if there are 4-tuples then a ``tRNS`` chunk is created as well. + The ``PLTE`` chunk will contain all the RGB triples in the same + sequence; + the ``tRNS`` chunk will contain the alpha channel for + all the 4-tuples, in the same sequence. + Palette entries are always 8-bit. + + If specified, the `transparent` and `background` parameters must be + a tuple with one element for each channel in the image. + Either a 3-tuple of integer (RGB) values for a colour image, or + a 1-tuple of a single integer for a greyscale image. + + If specified, the `gamma` parameter must be a positive number + (generally, a `float`). + A ``gAMA`` chunk will be created. + Note that this will not change the values of the pixels as + they appear in the PNG file, + they are assumed to have already + been converted appropriately for the gamma specified. + + The `compression` argument specifies the compression level to + be used by the ``zlib`` module. + Values from 1 to 9 (highest) specify compression. + 0 means no compression. + -1 and ``None`` both mean that the ``zlib`` module uses + the default level of compression (which is generally acceptable). + + If `interlace` is true then an interlaced image is created + (using PNG's so far only interlace method, *Adam7*). + This does not affect how the pixels should be passed in, + rather it changes how they are arranged into the PNG file. + On slow connexions interlaced images can be + partially decoded by the browser to give + a rough view of the image that is + successively refined as more image data appears. + + .. note :: + + Enabling the `interlace` option requires the entire image + to be processed in working memory. + + `chunk_limit` is used to limit the amount of memory used whilst + compressing the image. + In order to avoid using large amounts of memory, + multiple ``IDAT`` chunks may be created. + """ + + # At the moment the `planes` argument is ignored; + # its purpose is to act as a dummy so that + # ``Writer(x, y, **info)`` works, where `info` is a dictionary + # returned by Reader.read and friends. + # Ditto for `colormap`. + + width, height = check_sizes(size, width, height) + del size + + if not is_natural(width) or not is_natural(height): + raise ProtocolError("width and height must be integers") + if width <= 0 or height <= 0: + raise ProtocolError("width and height must be greater than zero") + # http://www.w3.org/TR/PNG/#7Integers-and-byte-order + if width > 2 ** 31 - 1 or height > 2 ** 31 - 1: + raise ProtocolError("width and height cannot exceed 2**31-1") + + if alpha and transparent is not None: + raise ProtocolError( + "transparent colour not allowed with alpha channel") + + # bitdepth is either single integer, or tuple of integers. + # Convert to tuple. + try: + len(bitdepth) + except TypeError: + bitdepth = (bitdepth, ) + for b in bitdepth: + valid = is_natural(b) and 1 <= b <= 16 + if not valid: + raise ProtocolError( + "each bitdepth %r must be a positive integer <= 16" % + (bitdepth,)) + + # Calculate channels, and + # expand bitdepth to be one element per channel. + palette = check_palette(palette) + alpha = bool(alpha) + colormap = bool(palette) + if greyscale is Default and palette: + greyscale = False + greyscale = bool(greyscale) + if colormap: + color_planes = 1 + planes = 1 + else: + color_planes = (3, 1)[greyscale] + planes = color_planes + alpha + if len(bitdepth) == 1: + bitdepth *= planes + + bitdepth, self.rescale = check_bitdepth_rescale( + palette, + bitdepth, + transparent, alpha, greyscale) + + # These are assertions, because above logic should have + # corrected or raised all problematic cases. + if bitdepth < 8: + assert greyscale or palette + assert not alpha + if bitdepth > 8: + assert not palette + + transparent = check_color(transparent, greyscale, 'transparent') + background = check_color(background, greyscale, 'background') + + # It's important that the true boolean values + # (greyscale, alpha, colormap, interlace) are converted + # to bool because Iverson's convention is relied upon later on. + self.width = width + self.height = height + self.transparent = transparent + self.background = background + self.gamma = gamma + self.greyscale = greyscale + self.alpha = alpha + self.colormap = colormap + self.bitdepth = int(bitdepth) + self.compression = compression + self.chunk_limit = chunk_limit + self.interlace = bool(interlace) + self.palette = palette + self.x_pixels_per_unit = x_pixels_per_unit + self.y_pixels_per_unit = y_pixels_per_unit + self.unit_is_meter = bool(unit_is_meter) + + self.color_type = (4 * self.alpha + + 2 * (not greyscale) + + 1 * self.colormap) + assert self.color_type in (0, 2, 3, 4, 6) + + self.color_planes = color_planes + self.planes = planes + # :todo: fix for bitdepth < 8 + self.psize = (self.bitdepth / 8) * self.planes + + def write(self, outfile, rows): + """ + Write a PNG image to the output file. + `rows` should be an iterable that yields each row + (each row is a sequence of values). + The rows should be the rows of the original image, + so there should be ``self.height`` rows of + ``self.width * self.planes`` values. + If `interlace` is specified (when creating the instance), + then an interlaced PNG file will be written. + Supply the rows in the normal image order; + the interlacing is carried out internally. + + .. note :: + + Interlacing requires the entire image to be in working memory. + """ + + # Values per row + vpr = self.width * self.planes + + def check_rows(rows): + """ + Yield each row in rows, + but check each row first (for correct width). + """ + for i, row in enumerate(rows): + try: + wrong_length = len(row) != vpr + except TypeError: + # When using an itertools.ichain object or + # other generator not supporting __len__, + # we set this to False to skip the check. + wrong_length = False + if wrong_length: + # Note: row numbers start at 0. + raise ProtocolError( + "Expected %d values but got %d values, in row %d" % + (vpr, len(row), i)) + yield row + + if self.interlace: + fmt = 'BH'[self.bitdepth > 8] + a = array(fmt, itertools.chain(*check_rows(rows))) + return self.write_array(outfile, a) + + nrows = self.write_passes(outfile, check_rows(rows)) + if nrows != self.height: + raise ProtocolError( + "rows supplied (%d) does not match height (%d)" % + (nrows, self.height)) + return nrows + + def write_passes(self, outfile, rows): + """ + Write a PNG image to the output file. + + Most users are expected to find the :meth:`write` or + :meth:`write_array` method more convenient. + + The rows should be given to this method in the order that + they appear in the output file. + For straightlaced images, this is the usual top to bottom ordering. + For interlaced images the rows should have been interlaced before + passing them to this function. + + `rows` should be an iterable that yields each row + (each row being a sequence of values). + """ + + # Ensure rows are scaled (to 4-/8-/16-bit), + # and packed into bytes. + + if self.rescale: + rows = rescale_rows(rows, self.rescale) + + if self.bitdepth < 8: + rows = pack_rows(rows, self.bitdepth) + elif self.bitdepth == 16: + rows = unpack_rows(rows) + + return self.write_packed(outfile, rows) + + def write_packed(self, outfile, rows): + """ + Write PNG file to `outfile`. + `rows` should be an iterator that yields each packed row; + a packed row being a sequence of packed bytes. + + The rows have a filter byte prefixed and + are then compressed into one or more IDAT chunks. + They are not processed any further, + so if bitdepth is other than 1, 2, 4, 8, 16, + the pixel values should have been scaled + before passing them to this method. + + This method does work for interlaced images but it is best avoided. + For interlaced images, the rows should be + presented in the order that they appear in the file. + """ + + self.write_preamble(outfile) + + # http://www.w3.org/TR/PNG/#11IDAT + if self.compression is not None: + compressor = zlib.compressobj(self.compression) + else: + compressor = zlib.compressobj() + + # data accumulates bytes to be compressed for the IDAT chunk; + # it's compressed when sufficiently large. + data = bytearray() + + # raise i scope out of the for loop. set to -1, because the for loop + # sets i to 0 on the first pass + i = -1 + for i, row in enumerate(rows): + # Add "None" filter type. + # Currently, it's essential that this filter type be used + # for every scanline as + # we do not mark the first row of a reduced pass image; + # that means we could accidentally compute + # the wrong filtered scanline if we used + # "up", "average", or "paeth" on such a line. + data.append(0) + data.extend(row) + if len(data) > self.chunk_limit: + compressed = compressor.compress(data) + if len(compressed): + write_chunk(outfile, b'IDAT', compressed) + data = bytearray() + + compressed = compressor.compress(bytes(data)) + flushed = compressor.flush() + if len(compressed) or len(flushed): + write_chunk(outfile, b'IDAT', compressed + flushed) + # http://www.w3.org/TR/PNG/#11IEND + write_chunk(outfile, b'IEND') + return i + 1 + + def write_preamble(self, outfile): + # http://www.w3.org/TR/PNG/#5PNG-file-signature + outfile.write(signature) + + # http://www.w3.org/TR/PNG/#11IHDR + write_chunk(outfile, b'IHDR', + struct.pack("!2I5B", self.width, self.height, + self.bitdepth, self.color_type, + 0, 0, self.interlace)) + + # See :chunk:order + # http://www.w3.org/TR/PNG/#11gAMA + if self.gamma is not None: + write_chunk(outfile, b'gAMA', + struct.pack("!L", int(round(self.gamma * 1e5)))) + + # See :chunk:order + # http://www.w3.org/TR/PNG/#11sBIT + if self.rescale: + write_chunk( + outfile, b'sBIT', + struct.pack('%dB' % self.planes, + * [s[0] for s in self.rescale])) + + # :chunk:order: Without a palette (PLTE chunk), + # ordering is relatively relaxed. + # With one, gAMA chunk must precede PLTE chunk + # which must precede tRNS and bKGD. + # See http://www.w3.org/TR/PNG/#5ChunkOrdering + if self.palette: + p, t = make_palette_chunks(self.palette) + write_chunk(outfile, b'PLTE', p) + if t: + # tRNS chunk is optional; + # Only needed if palette entries have alpha. + write_chunk(outfile, b'tRNS', t) + + # http://www.w3.org/TR/PNG/#11tRNS + if self.transparent is not None: + if self.greyscale: + fmt = "!1H" + else: + fmt = "!3H" + write_chunk(outfile, b'tRNS', + struct.pack(fmt, *self.transparent)) + + # http://www.w3.org/TR/PNG/#11bKGD + if self.background is not None: + if self.greyscale: + fmt = "!1H" + else: + fmt = "!3H" + write_chunk(outfile, b'bKGD', + struct.pack(fmt, *self.background)) + + # http://www.w3.org/TR/PNG/#11pHYs + if (self.x_pixels_per_unit is not None and + self.y_pixels_per_unit is not None): + tup = (self.x_pixels_per_unit, + self.y_pixels_per_unit, + int(self.unit_is_meter)) + write_chunk(outfile, b'pHYs', struct.pack("!LLB", *tup)) + + def write_array(self, outfile, pixels): + """ + Write an array that holds all the image values + as a PNG file on the output file. + See also :meth:`write` method. + """ + + if self.interlace: + if type(pixels) != array: + # Coerce to array type + fmt = 'BH'[self.bitdepth > 8] + pixels = array(fmt, pixels) + return self.write_passes( + outfile, + self.array_scanlines_interlace(pixels) + ) + else: + return self.write_passes( + outfile, + self.array_scanlines(pixels) + ) + + def array_scanlines(self, pixels): + """ + Generates rows (each a sequence of values) from + a single array of values. + """ + + # Values per row + vpr = self.width * self.planes + stop = 0 + for y in range(self.height): + start = stop + stop = start + vpr + yield pixels[start:stop] + + def array_scanlines_interlace(self, pixels): + """ + Generator for interlaced scanlines from an array. + `pixels` is the full source image as a single array of values. + The generator yields each scanline of the reduced passes in turn, + each scanline being a sequence of values. + """ + + # http://www.w3.org/TR/PNG/#8InterlaceMethods + # Array type. + fmt = 'BH'[self.bitdepth > 8] + # Value per row + vpr = self.width * self.planes + + # Each iteration generates a scanline starting at (x, y) + # and consisting of every xstep pixels. + for lines in adam7_generate(self.width, self.height): + for x, y, xstep in lines: + # Pixels per row (of reduced image) + ppr = int(math.ceil((self.width - x) / float(xstep))) + # Values per row (of reduced image) + reduced_row_len = ppr * self.planes + if xstep == 1: + # Easy case: line is a simple slice. + offset = y * vpr + yield pixels[offset: offset + vpr] + continue + # We have to step by xstep, + # which we can do one plane at a time + # using the step in Python slices. + row = array(fmt) + # There's no easier way to set the length of an array + row.extend(pixels[0:reduced_row_len]) + offset = y * vpr + x * self.planes + end_offset = (y + 1) * vpr + skip = self.planes * xstep + for i in range(self.planes): + row[i::self.planes] = \ + pixels[offset + i: end_offset: skip] + yield row + + +def write_chunk(outfile, tag, data=b''): + """ + Write a PNG chunk to the output file, including length and + checksum. + """ + + data = bytes(data) + # http://www.w3.org/TR/PNG/#5Chunk-layout + outfile.write(struct.pack("!I", len(data))) + outfile.write(tag) + outfile.write(data) + checksum = zlib.crc32(tag) + checksum = zlib.crc32(data, checksum) + checksum &= 2 ** 32 - 1 + outfile.write(struct.pack("!I", checksum)) + + +def write_chunks(out, chunks): + """Create a PNG file by writing out the chunks.""" + + out.write(signature) + for chunk in chunks: + write_chunk(out, *chunk) + + +def rescale_rows(rows, rescale): + """ + Take each row in rows (an iterator) and yield + a fresh row with the pixels scaled according to + the rescale parameters in the list `rescale`. + Each element of `rescale` is a tuple of + (source_bitdepth, target_bitdepth), + with one element per channel. + """ + + # One factor for each channel + fs = [float(2 ** s[1] - 1)/float(2 ** s[0] - 1) + for s in rescale] + + # Assume all target_bitdepths are the same + target_bitdepths = set(s[1] for s in rescale) + assert len(target_bitdepths) == 1 + (target_bitdepth, ) = target_bitdepths + typecode = 'BH'[target_bitdepth > 8] + + # Number of channels + n_chans = len(rescale) + + for row in rows: + rescaled_row = array(typecode, iter(row)) + for i in range(n_chans): + channel = array( + typecode, + (int(round(fs[i] * x)) for x in row[i::n_chans])) + rescaled_row[i::n_chans] = channel + yield rescaled_row + + +def pack_rows(rows, bitdepth): + """Yield packed rows that are a byte array. + Each byte is packed with the values from several pixels. + """ + + assert bitdepth < 8 + assert 8 % bitdepth == 0 + + # samples per byte + spb = int(8 / bitdepth) + + def make_byte(block): + """Take a block of (2, 4, or 8) values, + and pack them into a single byte. + """ + + res = 0 + for v in block: + res = (res << bitdepth) + v + return res + + for row in rows: + a = bytearray(row) + # Adding padding bytes so we can group into a whole + # number of spb-tuples. + n = float(len(a)) + extra = math.ceil(n / spb) * spb - n + a.extend([0] * int(extra)) + # Pack into bytes. + # Each block is the samples for one byte. + blocks = group(a, spb) + yield bytearray(make_byte(block) for block in blocks) + + +def unpack_rows(rows): + """Unpack each row from being 16-bits per value, + to being a sequence of bytes. + """ + for row in rows: + fmt = '!%dH' % len(row) + yield bytearray(struct.pack(fmt, *row)) + + +def make_palette_chunks(palette): + """ + Create the byte sequences for a ``PLTE`` and + if necessary a ``tRNS`` chunk. + Returned as a pair (*p*, *t*). + *t* will be ``None`` if no ``tRNS`` chunk is necessary. + """ + + p = bytearray() + t = bytearray() + + for x in palette: + p.extend(x[0:3]) + if len(x) > 3: + t.append(x[3]) + if t: + return p, t + return p, None + + +def check_bitdepth_rescale( + palette, bitdepth, transparent, alpha, greyscale): + """ + Returns (bitdepth, rescale) pair. + """ + + if palette: + if len(bitdepth) != 1: + raise ProtocolError( + "with palette, only a single bitdepth may be used") + (bitdepth, ) = bitdepth + if bitdepth not in (1, 2, 4, 8): + raise ProtocolError( + "with palette, bitdepth must be 1, 2, 4, or 8") + if transparent is not None: + raise ProtocolError("transparent and palette not compatible") + if alpha: + raise ProtocolError("alpha and palette not compatible") + if greyscale: + raise ProtocolError("greyscale and palette not compatible") + return bitdepth, None + + # No palette, check for sBIT chunk generation. + + if greyscale and not alpha: + # Single channel, L. + (bitdepth,) = bitdepth + if bitdepth in (1, 2, 4, 8, 16): + return bitdepth, None + if bitdepth > 8: + targetbitdepth = 16 + elif bitdepth == 3: + targetbitdepth = 4 + else: + assert bitdepth in (5, 6, 7) + targetbitdepth = 8 + return targetbitdepth, [(bitdepth, targetbitdepth)] + + assert alpha or not greyscale + + depth_set = tuple(set(bitdepth)) + if depth_set in [(8,), (16,)]: + # No sBIT required. + (bitdepth, ) = depth_set + return bitdepth, None + + targetbitdepth = (8, 16)[max(bitdepth) > 8] + return targetbitdepth, [(b, targetbitdepth) for b in bitdepth] + + +# Regex for decoding mode string +RegexModeDecode = re.compile("(LA?|RGBA?);?([0-9]*)", flags=re.IGNORECASE) + + +def from_array(a, mode=None, info={}): + """ + Create a PNG :class:`Image` object from a 2-dimensional array. + One application of this function is easy PIL-style saving: + ``png.from_array(pixels, 'L').save('foo.png')``. + + Unless they are specified using the *info* parameter, + the PNG's height and width are taken from the array size. + The first axis is the height; the second axis is the + ravelled width and channel index. + The array is treated is a sequence of rows, + each row being a sequence of values (``width*channels`` in number). + So an RGB image that is 16 pixels high and 8 wide will + occupy a 2-dimensional array that is 16x24 + (each row will be 8*3 = 24 sample values). + + *mode* is a string that specifies the image colour format in a + PIL-style mode. It can be: + + ``'L'`` + greyscale (1 channel) + ``'LA'`` + greyscale with alpha (2 channel) + ``'RGB'`` + colour image (3 channel) + ``'RGBA'`` + colour image with alpha (4 channel) + + The mode string can also specify the bit depth + (overriding how this function normally derives the bit depth, + see below). + Appending ``';16'`` to the mode will cause the PNG to be + 16 bits per channel; + any decimal from 1 to 16 can be used to specify the bit depth. + + When a 2-dimensional array is used *mode* determines how many + channels the image has, and so allows the width to be derived from + the second array dimension. + + The array is expected to be a ``numpy`` array, + but it can be any suitable Python sequence. + For example, a list of lists can be used: + ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``. + The exact rules are: ``len(a)`` gives the first dimension, height; + ``len(a[0])`` gives the second dimension. + It's slightly more complicated than that because + an iterator of rows can be used, and it all still works. + Using an iterator allows data to be streamed efficiently. + + The bit depth of the PNG is normally taken from + the array element's datatype + (but if *mode* specifies a bitdepth then that is used instead). + The array element's datatype is determined in a way which + is supposed to work both for ``numpy`` arrays and for Python + ``array.array`` objects. + A 1 byte datatype will give a bit depth of 8, + a 2 byte datatype will give a bit depth of 16. + If the datatype does not have an implicit size, + like the above example where it is a plain Python list of lists, + then a default of 8 is used. + + The *info* parameter is a dictionary that can + be used to specify metadata (in the same style as + the arguments to the :class:`png.Writer` class). + For this function the keys that are useful are: + + height + overrides the height derived from the array dimensions and + allows *a* to be an iterable. + width + overrides the width derived from the array dimensions. + bitdepth + overrides the bit depth derived from the element datatype + (but must match *mode* if that also specifies a bit depth). + + Generally anything specified in the *info* dictionary will + override any implicit choices that this function would otherwise make, + but must match any explicit ones. + For example, if the *info* dictionary has a ``greyscale`` key then + this must be true when mode is ``'L'`` or ``'LA'`` and + false when mode is ``'RGB'`` or ``'RGBA'``. + """ + + # We abuse the *info* parameter by modifying it. Take a copy here. + # (Also typechecks *info* to some extent). + info = dict(info) + + # Syntax check mode string. + match = RegexModeDecode.match(mode) + if not match: + raise Error("mode string should be 'RGB' or 'L;16' or similar.") + + mode, bitdepth = match.groups() + if bitdepth: + bitdepth = int(bitdepth) + + # Colour format. + if 'greyscale' in info: + if bool(info['greyscale']) != ('L' in mode): + raise ProtocolError("info['greyscale'] should match mode.") + info['greyscale'] = 'L' in mode + + alpha = 'A' in mode + if 'alpha' in info: + if bool(info['alpha']) != alpha: + raise ProtocolError("info['alpha'] should match mode.") + info['alpha'] = alpha + + # Get bitdepth from *mode* if possible. + if bitdepth: + if info.get("bitdepth") and bitdepth != info['bitdepth']: + raise ProtocolError( + "bitdepth (%d) should match bitdepth of info (%d)." % + (bitdepth, info['bitdepth'])) + info['bitdepth'] = bitdepth + + # Fill in and/or check entries in *info*. + # Dimensions. + width, height = check_sizes( + info.get("size"), + info.get("width"), + info.get("height")) + if width: + info["width"] = width + if height: + info["height"] = height + + if "height" not in info: + try: + info['height'] = len(a) + except TypeError: + raise ProtocolError( + "len(a) does not work, supply info['height'] instead.") + + planes = len(mode) + if 'planes' in info: + if info['planes'] != planes: + raise Error("info['planes'] should match mode.") + + # In order to work out whether we the array is 2D or 3D we need its + # first row, which requires that we take a copy of its iterator. + # We may also need the first row to derive width and bitdepth. + a, t = itertools.tee(a) + row = next(t) + del t + + testelement = row + if 'width' not in info: + width = len(row) // planes + info['width'] = width + + if 'bitdepth' not in info: + try: + dtype = testelement.dtype + # goto the "else:" clause. Sorry. + except AttributeError: + try: + # Try a Python array.array. + bitdepth = 8 * testelement.itemsize + except AttributeError: + # We can't determine it from the array element's datatype, + # use a default of 8. + bitdepth = 8 + else: + # If we got here without exception, + # we now assume that the array is a numpy array. + if dtype.kind == 'b': + bitdepth = 1 + else: + bitdepth = 8 * dtype.itemsize + info['bitdepth'] = bitdepth + + for thing in ["width", "height", "bitdepth", "greyscale", "alpha"]: + assert thing in info + + return Image(a, info) + + +# So that refugee's from PIL feel more at home. Not documented. +fromarray = from_array + + +class Image: + """A PNG image. You can create an :class:`Image` object from + an array of pixels by calling :meth:`png.from_array`. It can be + saved to disk with the :meth:`save` method. + """ + + def __init__(self, rows, info): + """ + .. note :: + + The constructor is not public. Please do not call it. + """ + + self.rows = rows + self.info = info + + def save(self, file): + """Save the image to the named *file*. + + See `.write()` if you already have an open file object. + + In general, you can only call this method once; + after it has been called the first time the PNG image is written, + the source data will have been streamed, and + cannot be streamed again. + """ + + w = Writer(**self.info) + + with open(file, 'wb') as fd: + w.write(fd, self.rows) + + def write(self, file): + """Write the image to the open file object. + + See `.save()` if you have a filename. + + In general, you can only call this method once; + after it has been called the first time the PNG image is written, + the source data will have been streamed, and + cannot be streamed again. + """ + + w = Writer(**self.info) + w.write(file, self.rows) + + +class Reader: + """ + Pure Python PNG decoder in pure Python. + """ + + def __init__(self, _guess=None, filename=None, file=None, bytes=None): + """ + The constructor expects exactly one keyword argument. + If you supply a positional argument instead, + it will guess the input type. + Choose from the following keyword arguments: + + filename + Name of input file (a PNG file). + file + A file-like object (object with a read() method). + bytes + ``bytes`` or ``bytearray`` with PNG data. + + """ + keywords_supplied = ( + (_guess is not None) + + (filename is not None) + + (file is not None) + + (bytes is not None)) + if keywords_supplied != 1: + raise TypeError("Reader() takes exactly 1 argument") + + # Will be the first 8 bytes, later on. See validate_signature. + self.signature = None + self.transparent = None + # A pair of (len,type) if a chunk has been read but its data and + # checksum have not (in other words the file position is just + # past the 4 bytes that specify the chunk type). + # See preamble method for how this is used. + self.atchunk = None + + if _guess is not None: + if isarray(_guess): + bytes = _guess + elif isinstance(_guess, str): + filename = _guess + elif hasattr(_guess, 'read'): + file = _guess + + if bytes is not None: + self.file = io.BytesIO(bytes) + elif filename is not None: + self.file = open(filename, "rb") + elif file is not None: + self.file = file + else: + raise ProtocolError("expecting filename, file or bytes array") + + def chunk(self, lenient=False): + """ + Read the next PNG chunk from the input file; + returns a (*type*, *data*) tuple. + *type* is the chunk's type as a byte string + (all PNG chunk types are 4 bytes long). + *data* is the chunk's data content, as a byte string. + + If the optional `lenient` argument evaluates to `True`, + checksum failures will raise warnings rather than exceptions. + """ + + self.validate_signature() + + # http://www.w3.org/TR/PNG/#5Chunk-layout + if not self.atchunk: + self.atchunk = self._chunk_len_type() + if not self.atchunk: + raise ChunkError("No more chunks.") + length, type = self.atchunk + self.atchunk = None + + data = self.file.read(length) + if len(data) != length: + raise ChunkError( + 'Chunk %s too short for required %i octets.' + % (type, length)) + checksum = self.file.read(4) + if len(checksum) != 4: + raise ChunkError('Chunk %s too short for checksum.' % type) + verify = zlib.crc32(type) + verify = zlib.crc32(data, verify) + verify = struct.pack('!I', verify) + if checksum != verify: + (a, ) = struct.unpack('!I', checksum) + (b, ) = struct.unpack('!I', verify) + message = ("Checksum error in %s chunk: 0x%08X != 0x%08X." + % (type.decode('ascii'), a, b)) + if lenient: + warnings.warn(message, RuntimeWarning) + else: + raise ChunkError(message) + return type, data + + def chunks(self): + """Return an iterator that will yield each chunk as a + (*chunktype*, *content*) pair. + """ + + while True: + t, v = self.chunk() + yield t, v + if t == b'IEND': + break + + def undo_filter(self, filter_type, scanline, previous): + """ + Undo the filter for a scanline. + `scanline` is a sequence of bytes that + does not include the initial filter type byte. + `previous` is decoded previous scanline + (for straightlaced images this is the previous pixel row, + but for interlaced images, it is + the previous scanline in the reduced image, + which in general is not the previous pixel row in the final image). + When there is no previous scanline + (the first row of a straightlaced image, + or the first row in one of the passes in an interlaced image), + then this argument should be ``None``. + + The scanline will have the effects of filtering removed; + the result will be returned as a fresh sequence of bytes. + """ + + # :todo: Would it be better to update scanline in place? + result = scanline + + if filter_type == 0: + return result + + if filter_type not in (1, 2, 3, 4): + raise FormatError( + 'Invalid PNG Filter Type. ' + 'See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters .') + + # Filter unit. The stride from one pixel to the corresponding + # byte from the previous pixel. Normally this is the pixel + # size in bytes, but when this is smaller than 1, the previous + # byte is used instead. + fu = max(1, self.psize) + + # For the first line of a pass, synthesize a dummy previous + # line. An alternative approach would be to observe that on the + # first line 'up' is the same as 'null', 'paeth' is the same + # as 'sub', with only 'average' requiring any special case. + if not previous: + previous = bytearray([0] * len(scanline)) + + # Call appropriate filter algorithm. Note that 0 has already + # been dealt with. + fn = (None, + undo_filter_sub, + undo_filter_up, + undo_filter_average, + undo_filter_paeth)[filter_type] + fn(fu, scanline, previous, result) + return result + + def _deinterlace(self, raw): + """ + Read raw pixel data, undo filters, deinterlace, and flatten. + Return a single array of values. + """ + + # Values per row (of the target image) + vpr = self.width * self.planes + + # Values per image + vpi = vpr * self.height + # Interleaving writes to the output array randomly + # (well, not quite), so the entire output array must be in memory. + # Make a result array, and make it big enough. + if self.bitdepth > 8: + a = array('H', [0] * vpi) + else: + a = bytearray([0] * vpi) + source_offset = 0 + + for lines in adam7_generate(self.width, self.height): + # The previous (reconstructed) scanline. + # `None` at the beginning of a pass + # to indicate that there is no previous line. + recon = None + for x, y, xstep in lines: + # Pixels per row (reduced pass image) + ppr = int(math.ceil((self.width - x) / float(xstep))) + # Row size in bytes for this pass. + row_size = int(math.ceil(self.psize * ppr)) + + filter_type = raw[source_offset] + source_offset += 1 + scanline = raw[source_offset: source_offset + row_size] + source_offset += row_size + recon = self.undo_filter(filter_type, scanline, recon) + # Convert so that there is one element per pixel value + flat = self._bytes_to_values(recon, width=ppr) + if xstep == 1: + assert x == 0 + offset = y * vpr + a[offset: offset + vpr] = flat + else: + offset = y * vpr + x * self.planes + end_offset = (y + 1) * vpr + skip = self.planes * xstep + for i in range(self.planes): + a[offset + i: end_offset: skip] = \ + flat[i:: self.planes] + + return a + + def _iter_bytes_to_values(self, byte_rows): + """ + Iterator that yields each scanline; + each scanline being a sequence of values. + `byte_rows` should be an iterator that yields + the bytes of each row in turn. + """ + + for row in byte_rows: + yield self._bytes_to_values(row) + + def _bytes_to_values(self, bs, width=None): + """Convert a packed row of bytes into a row of values. + Result will be a freshly allocated object, + not shared with the argument. + """ + + if self.bitdepth == 8: + return bytearray(bs) + if self.bitdepth == 16: + return array('H', + struct.unpack('!%dH' % (len(bs) // 2), bs)) + + assert self.bitdepth < 8 + if width is None: + width = self.width + # Samples per byte + spb = 8 // self.bitdepth + out = bytearray() + mask = 2**self.bitdepth - 1 + shifts = [self.bitdepth * i + for i in reversed(list(range(spb)))] + for o in bs: + out.extend([mask & (o >> i) for i in shifts]) + return out[:width] + + def _iter_straight_packed(self, byte_blocks): + """Iterator that undoes the effect of filtering; + yields each row as a sequence of packed bytes. + Assumes input is straightlaced. + `byte_blocks` should be an iterable that yields the raw bytes + in blocks of arbitrary size. + """ + + # length of row, in bytes + rb = self.row_bytes + a = bytearray() + # The previous (reconstructed) scanline. + # None indicates first line of image. + recon = None + for some_bytes in byte_blocks: + a.extend(some_bytes) + while len(a) >= rb + 1: + filter_type = a[0] + scanline = a[1: rb + 1] + del a[: rb + 1] + recon = self.undo_filter(filter_type, scanline, recon) + yield recon + if len(a) != 0: + # :file:format We get here with a file format error: + # when the available bytes (after decompressing) do not + # pack into exact rows. + raise FormatError('Wrong size for decompressed IDAT chunk.') + assert len(a) == 0 + + def validate_signature(self): + """ + If signature (header) has not been read then read and + validate it; otherwise do nothing. + No signature (empty read()) will raise EOFError; + An invalid signature will raise FormatError. + EOFError is raised to make possible the case where + a program can read multiple PNG files from the same stream. + The end of the stream can be distinguished from non-PNG files + or corrupted PNG files. + """ + + if self.signature: + return + self.signature = self.file.read(8) + if len(self.signature) == 0: + raise EOFError("End of PNG stream.") + if self.signature != signature: + raise FormatError("PNG file has invalid signature.") + + def preamble(self, lenient=False): + """ + Extract the image metadata by reading + the initial part of the PNG file up to + the start of the ``IDAT`` chunk. + All the chunks that precede the ``IDAT`` chunk are + read and either processed for metadata or discarded. + + If the optional `lenient` argument evaluates to `True`, + checksum failures will raise warnings rather than exceptions. + """ + + self.validate_signature() + + while True: + if not self.atchunk: + self.atchunk = self._chunk_len_type() + if self.atchunk is None: + raise FormatError('This PNG file has no IDAT chunks.') + if self.atchunk[1] == b'IDAT': + return + self.process_chunk(lenient=lenient) + + def _chunk_len_type(self): + """ + Reads just enough of the input to + determine the next chunk's length and type; + return a (*length*, *type*) pair where *type* is a byte sequence. + If there are no more chunks, ``None`` is returned. + """ + + x = self.file.read(8) + if not x: + return None + if len(x) != 8: + raise FormatError( + 'End of file whilst reading chunk length and type.') + length, type = struct.unpack('!I4s', x) + if length > 2 ** 31 - 1: + raise FormatError('Chunk %s is too large: %d.' % (type, length)) + # Check that all bytes are in valid ASCII range. + # https://www.w3.org/TR/2003/REC-PNG-20031110/#5Chunk-layout + type_bytes = set(bytearray(type)) + if not(type_bytes <= set(range(65, 91)) | set(range(97, 123))): + raise FormatError( + 'Chunk %r has invalid Chunk Type.' + % list(type)) + return length, type + + def process_chunk(self, lenient=False): + """ + Process the next chunk and its data. + This only processes the following chunk types: + ``IHDR``, ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``, ``pHYs``. + All other chunk types are ignored. + + If the optional `lenient` argument evaluates to `True`, + checksum failures will raise warnings rather than exceptions. + """ + + type, data = self.chunk(lenient=lenient) + method = '_process_' + type.decode('ascii') + m = getattr(self, method, None) + if m: + m(data) + + def _process_IHDR(self, data): + # http://www.w3.org/TR/PNG/#11IHDR + if len(data) != 13: + raise FormatError('IHDR chunk has incorrect length.') + (self.width, self.height, self.bitdepth, self.color_type, + self.compression, self.filter, + self.interlace) = struct.unpack("!2I5B", data) + + check_bitdepth_colortype(self.bitdepth, self.color_type) + + if self.compression != 0: + raise FormatError( + "Unknown compression method %d" % self.compression) + if self.filter != 0: + raise FormatError( + "Unknown filter method %d," + " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." + % self.filter) + if self.interlace not in (0, 1): + raise FormatError( + "Unknown interlace method %d, see " + "http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods" + " ." + % self.interlace) + + # Derived values + # http://www.w3.org/TR/PNG/#6Colour-values + colormap = bool(self.color_type & 1) + greyscale = not(self.color_type & 2) + alpha = bool(self.color_type & 4) + color_planes = (3, 1)[greyscale or colormap] + planes = color_planes + alpha + + self.colormap = colormap + self.greyscale = greyscale + self.alpha = alpha + self.color_planes = color_planes + self.planes = planes + self.psize = float(self.bitdepth) / float(8) * planes + if int(self.psize) == self.psize: + self.psize = int(self.psize) + self.row_bytes = int(math.ceil(self.width * self.psize)) + # Stores PLTE chunk if present, and is used to check + # chunk ordering constraints. + self.plte = None + # Stores tRNS chunk if present, and is used to check chunk + # ordering constraints. + self.trns = None + # Stores sBIT chunk if present. + self.sbit = None + + def _process_PLTE(self, data): + # http://www.w3.org/TR/PNG/#11PLTE + if self.plte: + warnings.warn("Multiple PLTE chunks present.") + self.plte = data + if len(data) % 3 != 0: + raise FormatError( + "PLTE chunk's length should be a multiple of 3.") + if len(data) > (2 ** self.bitdepth) * 3: + raise FormatError("PLTE chunk is too long.") + if len(data) == 0: + raise FormatError("Empty PLTE is not allowed.") + + def _process_bKGD(self, data): + try: + if self.colormap: + if not self.plte: + warnings.warn( + "PLTE chunk is required before bKGD chunk.") + self.background = struct.unpack('B', data) + else: + self.background = struct.unpack("!%dH" % self.color_planes, + data) + except struct.error: + raise FormatError("bKGD chunk has incorrect length.") + + def _process_tRNS(self, data): + # http://www.w3.org/TR/PNG/#11tRNS + self.trns = data + if self.colormap: + if not self.plte: + warnings.warn("PLTE chunk is required before tRNS chunk.") + else: + if len(data) > len(self.plte) / 3: + # Was warning, but promoted to Error as it + # would otherwise cause pain later on. + raise FormatError("tRNS chunk is too long.") + else: + if self.alpha: + raise FormatError( + "tRNS chunk is not valid with colour type %d." % + self.color_type) + try: + self.transparent = \ + struct.unpack("!%dH" % self.color_planes, data) + except struct.error: + raise FormatError("tRNS chunk has incorrect length.") + + def _process_gAMA(self, data): + try: + self.gamma = struct.unpack("!L", data)[0] / 100000.0 + except struct.error: + raise FormatError("gAMA chunk has incorrect length.") + + def _process_sBIT(self, data): + self.sbit = data + if (self.colormap and len(data) != 3 or + not self.colormap and len(data) != self.planes): + raise FormatError("sBIT chunk has incorrect length.") + + def _process_pHYs(self, data): + # http://www.w3.org/TR/PNG/#11pHYs + self.phys = data + fmt = "!LLB" + if len(data) != struct.calcsize(fmt): + raise FormatError("pHYs chunk has incorrect length.") + self.x_pixels_per_unit, self.y_pixels_per_unit, unit = \ + struct.unpack(fmt, data) + self.unit_is_meter = bool(unit) + + def read(self, lenient=False): + """ + Read the PNG file and decode it. + Returns (`width`, `height`, `rows`, `info`). + + May use excessive memory. + + `rows` is a sequence of rows; + each row is a sequence of values. + + If the optional `lenient` argument evaluates to True, + checksum failures will raise warnings rather than exceptions. + """ + + def iteridat(): + """Iterator that yields all the ``IDAT`` chunks as strings.""" + while True: + type, data = self.chunk(lenient=lenient) + if type == b'IEND': + # http://www.w3.org/TR/PNG/#11IEND + break + if type != b'IDAT': + continue + # type == b'IDAT' + # http://www.w3.org/TR/PNG/#11IDAT + if self.colormap and not self.plte: + warnings.warn("PLTE chunk is required before IDAT chunk") + yield data + + self.preamble(lenient=lenient) + raw = decompress(iteridat()) + + if self.interlace: + def rows_from_interlace(): + """Yield each row from an interlaced PNG.""" + # It's important that this iterator doesn't read + # IDAT chunks until it yields the first row. + bs = bytearray(itertools.chain(*raw)) + arraycode = 'BH'[self.bitdepth > 8] + # Like :meth:`group` but + # producing an array.array object for each row. + values = self._deinterlace(bs) + vpr = self.width * self.planes + for i in range(0, len(values), vpr): + row = array(arraycode, values[i:i+vpr]) + yield row + rows = rows_from_interlace() + else: + rows = self._iter_bytes_to_values(self._iter_straight_packed(raw)) + info = dict() + for attr in 'greyscale alpha planes bitdepth interlace'.split(): + info[attr] = getattr(self, attr) + info['size'] = (self.width, self.height) + for attr in 'gamma transparent background'.split(): + a = getattr(self, attr, None) + if a is not None: + info[attr] = a + if getattr(self, 'x_pixels_per_unit', None): + info['physical'] = Resolution(self.x_pixels_per_unit, + self.y_pixels_per_unit, + self.unit_is_meter) + if self.plte: + info['palette'] = self.palette() + return self.width, self.height, rows, info + + def read_flat(self): + """ + Read a PNG file and decode it into a single array of values. + Returns (*width*, *height*, *values*, *info*). + + May use excessive memory. + + `values` is a single array. + + The :meth:`read` method is more stream-friendly than this, + because it returns a sequence of rows. + """ + + x, y, pixel, info = self.read() + arraycode = 'BH'[info['bitdepth'] > 8] + pixel = array(arraycode, itertools.chain(*pixel)) + return x, y, pixel, info + + def palette(self, alpha='natural'): + """ + Returns a palette that is a sequence of 3-tuples or 4-tuples, + synthesizing it from the ``PLTE`` and ``tRNS`` chunks. + These chunks should have already been processed (for example, + by calling the :meth:`preamble` method). + All the tuples are the same size: + 3-tuples if there is no ``tRNS`` chunk, + 4-tuples when there is a ``tRNS`` chunk. + + Assumes that the image is colour type + 3 and therefore a ``PLTE`` chunk is required. + + If the `alpha` argument is ``'force'`` then an alpha channel is + always added, forcing the result to be a sequence of 4-tuples. + """ + + if not self.plte: + raise FormatError( + "Required PLTE chunk is missing in colour type 3 image.") + plte = group(array('B', self.plte), 3) + if self.trns or alpha == 'force': + trns = array('B', self.trns or []) + trns.extend([255] * (len(plte) - len(trns))) + plte = list(map(operator.add, plte, group(trns, 1))) + return plte + + def asDirect(self): + """ + Returns the image data as a direct representation of + an ``x * y * planes`` array. + This removes the need for callers to deal with + palettes and transparency themselves. + Images with a palette (colour type 3) are converted to RGB or RGBA; + images with transparency (a ``tRNS`` chunk) are converted to + LA or RGBA as appropriate. + When returned in this format the pixel values represent + the colour value directly without needing to refer + to palettes or transparency information. + + Like the :meth:`read` method this method returns a 4-tuple: + + (*width*, *height*, *rows*, *info*) + + This method normally returns pixel values with + the bit depth they have in the source image, but + when the source PNG has an ``sBIT`` chunk it is inspected and + can reduce the bit depth of the result pixels; + pixel values will be reduced according to the bit depth + specified in the ``sBIT`` chunk. + PNG nerds should note a single result bit depth is + used for all channels: + the maximum of the ones specified in the ``sBIT`` chunk. + An RGB565 image will be rescaled to 6-bit RGB666. + + The *info* dictionary that is returned reflects + the `direct` format and not the original source image. + For example, an RGB source image with a ``tRNS`` chunk + to represent a transparent colour, + will start with ``planes=3`` and ``alpha=False`` for the + source image, + but the *info* dictionary returned by this method + will have ``planes=4`` and ``alpha=True`` because + an alpha channel is synthesized and added. + + *rows* is a sequence of rows; + each row being a sequence of values + (like the :meth:`read` method). + + All the other aspects of the image data are not changed. + """ + + self.preamble() + + # Simple case, no conversion necessary. + if not self.colormap and not self.trns and not self.sbit: + return self.read() + + x, y, pixels, info = self.read() + + if self.colormap: + info['colormap'] = False + info['alpha'] = bool(self.trns) + info['bitdepth'] = 8 + info['planes'] = 3 + bool(self.trns) + plte = self.palette() + + def iterpal(pixels): + for row in pixels: + row = [plte[x] for x in row] + yield array('B', itertools.chain(*row)) + pixels = iterpal(pixels) + elif self.trns: + # It would be nice if there was some reasonable way + # of doing this without generating a whole load of + # intermediate tuples. But tuples does seem like the + # easiest way, with no other way clearly much simpler or + # much faster. (Actually, the L to LA conversion could + # perhaps go faster (all those 1-tuples!), but I still + # wonder whether the code proliferation is worth it) + it = self.transparent + maxval = 2 ** info['bitdepth'] - 1 + planes = info['planes'] + info['alpha'] = True + info['planes'] += 1 + typecode = 'BH'[info['bitdepth'] > 8] + + def itertrns(pixels): + for row in pixels: + # For each row we group it into pixels, then form a + # characterisation vector that says whether each + # pixel is opaque or not. Then we convert + # True/False to 0/maxval (by multiplication), + # and add it as the extra channel. + row = group(row, planes) + opa = map(it.__ne__, row) + opa = map(maxval.__mul__, opa) + opa = list(zip(opa)) # convert to 1-tuples + yield array( + typecode, + itertools.chain(*map(operator.add, row, opa))) + pixels = itertrns(pixels) + targetbitdepth = None + if self.sbit: + sbit = struct.unpack('%dB' % len(self.sbit), self.sbit) + targetbitdepth = max(sbit) + if targetbitdepth > info['bitdepth']: + raise Error('sBIT chunk %r exceeds bitdepth %d' % + (sbit, self.bitdepth)) + if min(sbit) <= 0: + raise Error('sBIT chunk %r has a 0-entry' % sbit) + if targetbitdepth: + shift = info['bitdepth'] - targetbitdepth + info['bitdepth'] = targetbitdepth + + def itershift(pixels): + for row in pixels: + yield [p >> shift for p in row] + pixels = itershift(pixels) + return x, y, pixels, info + + def _as_rescale(self, get, targetbitdepth): + """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`.""" + + width, height, pixels, info = get() + maxval = 2**info['bitdepth'] - 1 + targetmaxval = 2**targetbitdepth - 1 + factor = float(targetmaxval) / float(maxval) + info['bitdepth'] = targetbitdepth + + def iterscale(): + for row in pixels: + yield [int(round(x * factor)) for x in row] + if maxval == targetmaxval: + return width, height, pixels, info + else: + return width, height, iterscale(), info + + def asRGB8(self): + """ + Return the image data as an RGB pixels with 8-bits per sample. + This is like the :meth:`asRGB` method except that + this method additionally rescales the values so that + they are all between 0 and 255 (8-bit). + In the case where the source image has a bit depth < 8 + the transformation preserves all the information; + where the source image has bit depth > 8, then + rescaling to 8-bit values loses precision. + No dithering is performed. + Like :meth:`asRGB`, + an alpha channel in the source image will raise an exception. + + This function returns a 4-tuple: + (*width*, *height*, *rows*, *info*). + *width*, *height*, *info* are as per the :meth:`read` method. + + *rows* is the pixel data as a sequence of rows. + """ + + return self._as_rescale(self.asRGB, 8) + + def asRGBA8(self): + """ + Return the image data as RGBA pixels with 8-bits per sample. + This method is similar to :meth:`asRGB8` and :meth:`asRGBA`: + The result pixels have an alpha channel, *and* + values are rescaled to the range 0 to 255. + The alpha channel is synthesized if necessary + (with a small speed penalty). + """ + + return self._as_rescale(self.asRGBA, 8) + + def asRGB(self): + """ + Return image as RGB pixels. + RGB colour images are passed through unchanged; + greyscales are expanded into RGB triplets + (there is a small speed overhead for doing this). + + An alpha channel in the source image will raise an exception. + + The return values are as for the :meth:`read` method except that + the *info* reflect the returned pixels, not the source image. + In particular, + for this method ``info['greyscale']`` will be ``False``. + """ + + width, height, pixels, info = self.asDirect() + if info['alpha']: + raise Error("will not convert image with alpha channel to RGB") + if not info['greyscale']: + return width, height, pixels, info + info['greyscale'] = False + info['planes'] = 3 + + if info['bitdepth'] > 8: + def newarray(): + return array('H', [0]) + else: + def newarray(): + return bytearray([0]) + + def iterrgb(): + for row in pixels: + a = newarray() * 3 * width + for i in range(3): + a[i::3] = row + yield a + return width, height, iterrgb(), info + + def asRGBA(self): + """ + Return image as RGBA pixels. + Greyscales are expanded into RGB triplets; + an alpha channel is synthesized if necessary. + The return values are as for the :meth:`read` method except that + the *info* reflect the returned pixels, not the source image. + In particular, for this method + ``info['greyscale']`` will be ``False``, and + ``info['alpha']`` will be ``True``. + """ + + width, height, pixels, info = self.asDirect() + if info['alpha'] and not info['greyscale']: + return width, height, pixels, info + typecode = 'BH'[info['bitdepth'] > 8] + maxval = 2**info['bitdepth'] - 1 + maxbuffer = struct.pack('=' + typecode, maxval) * 4 * width + + if info['bitdepth'] > 8: + def newarray(): + return array('H', maxbuffer) + else: + def newarray(): + return bytearray(maxbuffer) + + if info['alpha'] and info['greyscale']: + # LA to RGBA + def convert(): + for row in pixels: + # Create a fresh target row, then copy L channel + # into first three target channels, and A channel + # into fourth channel. + a = newarray() + convert_la_to_rgba(row, a) + yield a + elif info['greyscale']: + # L to RGBA + def convert(): + for row in pixels: + a = newarray() + convert_l_to_rgba(row, a) + yield a + else: + assert not info['alpha'] and not info['greyscale'] + # RGB to RGBA + + def convert(): + for row in pixels: + a = newarray() + convert_rgb_to_rgba(row, a) + yield a + info['alpha'] = True + info['greyscale'] = False + info['planes'] = 4 + return width, height, convert(), info + + +def decompress(data_blocks): + """ + `data_blocks` should be an iterable that + yields the compressed data (from the ``IDAT`` chunks). + This yields decompressed byte strings. + """ + + # Currently, with no max_length parameter to decompress, + # this routine will do one yield per IDAT chunk: Not very + # incremental. + d = zlib.decompressobj() + # Each IDAT chunk is passed to the decompressor, then any + # remaining state is decompressed out. + for data in data_blocks: + # :todo: add a max_length argument here to limit output size. + yield bytearray(d.decompress(data)) + yield bytearray(d.flush()) + + +def check_bitdepth_colortype(bitdepth, colortype): + """ + Check that `bitdepth` and `colortype` are both valid, + and specified in a valid combination. + Returns (None) if valid, raise an Exception if not valid. + """ + + if bitdepth not in (1, 2, 4, 8, 16): + raise FormatError("invalid bit depth %d" % bitdepth) + if colortype not in (0, 2, 3, 4, 6): + raise FormatError("invalid colour type %d" % colortype) + # Check indexed (palettized) images have 8 or fewer bits + # per pixel; check only indexed or greyscale images have + # fewer than 8 bits per pixel. + if colortype & 1 and bitdepth > 8: + raise FormatError( + "Indexed images (colour type %d) cannot" + " have bitdepth > 8 (bit depth %d)." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." + % (bitdepth, colortype)) + if bitdepth < 8 and colortype not in (0, 3): + raise FormatError( + "Illegal combination of bit depth (%d)" + " and colour type (%d)." + " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." + % (bitdepth, colortype)) + + +def is_natural(x): + """A non-negative integer.""" + try: + is_integer = int(x) == x + except (TypeError, ValueError): + return False + return is_integer and x >= 0 + + +def undo_filter_sub(filter_unit, scanline, previous, result): + """Undo sub filter.""" + + ai = 0 + # Loops starts at index fu. Observe that the initial part + # of the result is already filled in correctly with + # scanline. + for i in range(filter_unit, len(result)): + x = scanline[i] + a = result[ai] + result[i] = (x + a) & 0xff + ai += 1 + + +def undo_filter_up(filter_unit, scanline, previous, result): + """Undo up filter.""" + + for i in range(len(result)): + x = scanline[i] + b = previous[i] + result[i] = (x + b) & 0xff + + +def undo_filter_average(filter_unit, scanline, previous, result): + """Undo up filter.""" + + ai = -filter_unit + for i in range(len(result)): + x = scanline[i] + if ai < 0: + a = 0 + else: + a = result[ai] + b = previous[i] + result[i] = (x + ((a + b) >> 1)) & 0xff + ai += 1 + + +def undo_filter_paeth(filter_unit, scanline, previous, result): + """Undo Paeth filter.""" + + # Also used for ci. + ai = -filter_unit + for i in range(len(result)): + x = scanline[i] + if ai < 0: + a = c = 0 + else: + a = result[ai] + c = previous[ai] + b = previous[i] + p = a + b - c + pa = abs(p - a) + pb = abs(p - b) + pc = abs(p - c) + if pa <= pb and pa <= pc: + pr = a + elif pb <= pc: + pr = b + else: + pr = c + result[i] = (x + pr) & 0xff + ai += 1 + + +def convert_la_to_rgba(row, result): + for i in range(3): + result[i::4] = row[0::2] + result[3::4] = row[1::2] + + +def convert_l_to_rgba(row, result): + """ + Convert a grayscale image to RGBA. + This method assumes the alpha channel in result is + already correctly initialized. + """ + for i in range(3): + result[i::4] = row + + +def convert_rgb_to_rgba(row, result): + """ + Convert an RGB image to RGBA. + This method assumes the alpha channel in result is + already correctly initialized. + """ + for i in range(3): + result[i::4] = row[i::3] + + +# Only reason to include this in this module is that +# several utilities need it, and it is small. +def binary_stdin(): + """ + A sys.stdin that returns bytes. + """ + + return sys.stdin.buffer + + +def binary_stdout(): + """ + A sys.stdout that accepts bytes. + """ + + stdout = sys.stdout.buffer + + # On Windows the C runtime file orientation needs changing. + if sys.platform == "win32": + import msvcrt + import os + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + + return stdout + + +def cli_open(path): + if path == "-": + return binary_stdin() + return open(path, "rb") + + +def main(argv): + """ + Run command line PNG. + Which reports version. + """ + print(__version__, __file__) + + +if __name__ == '__main__': + try: + main(sys.argv) + except Error as e: + print(e, file=sys.stderr) diff --git a/tools/rgb555.py b/tools/rgb555.py new file mode 100755 index 00000000..bf650313 --- /dev/null +++ b/tools/rgb555.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python rgb555.py image.png + +Round all colors of the input image to RGB555. +""" + +import sys + +import png + +def rgb8_to_rgb5(c): + return (c & 0b11111000) | (c >> 5) + +def round_pal(filename): + with open(filename, 'rb') as file: + width, height, rows = png.Reader(file).asRGBA8()[:3] + rows = list(rows) + for row in rows: + del row[3::4] + rows = [[rgb8_to_rgb5(c) for c in row] for row in rows] + writer = png.Writer(width, height, greyscale=False, bitdepth=8, compression=9) + with open(filename, 'wb') as file: + writer.write(file, rows) + +def main(): + if len(sys.argv) < 2: + print(f'Usage: {sys.argv[0]} pic.png', file=sys.stderr) + sys.exit(1) + for filename in sys.argv[1:]: + if not filename.lower().endswith('.png'): + print(f'{filename} is not a .png file!', file=sys.stderr) + round_pal(filename) + +if __name__ == '__main__': + main() diff --git a/tools/sym_comments.py b/tools/sym_comments.py new file mode 100755 index 00000000..0f060e5c --- /dev/null +++ b/tools/sym_comments.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python sym_comments.py file.asm [pokered.sym] > file_commented.asm + +Comments each label in file.asm with its bank:address from the sym file. +""" + +import sys +import re + +def main(): + if len(sys.argv) not in {2, 3}: + print(f'Usage: {sys.argv[0]} file.asm [pokered.sym] > file_commented.asm', file=sys.stderr) + sys.exit(1) + + wram_name = sys.argv[1] + sym_name = sys.argv[2] if len(sys.argv) == 3 else 'pokered.sym' + + sym_def_rx = re.compile(r'(^{sym})(:.*)|(^\.{sym})(.*)'.format(sym=r'[A-Za-z_][A-Za-z0-9_#@]*')) + + sym_addrs = {} + with open(sym_name, 'r', encoding='utf-8') as file: + for line in file: + line = line.split(';', 1)[0].rstrip() + parts = line.split(' ', 1) + if len(parts) != 2: + continue + addr, sym = parts + sym_addrs[sym] = addr + + with open(wram_name, 'r', encoding='utf-8') as file: + cur_label = None + for line in file: + line = line.rstrip() + if (m = re.match(sym_def_rx, line)): + sym, rest = m.group(1), m.group(2) + if sym is None and rest is None: + sym, rest = m.group(3), m.group(4) + key = sym + if not sym.startswith('.'): + cur_label = sym + elif cur_label: + key = cur_label + sym + if key in sym_addrs: + addr = sym_addrs[key] + line = sym + rest + ' ; ' + addr + print(line) + +if __name__ == '__main__': + main() diff --git a/tools/toc.py b/tools/toc.py new file mode 100755 index 00000000..d9a3d963 --- /dev/null +++ b/tools/toc.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python toc.py file.md + +Replace a "## TOC" heading in a Markdown file with a table of contents, +generated from the other headings in the file. Supports multiple files. +Headings must start with "##" signs to be detected. +""" + +import sys +import re +from collections import namedtuple +from urllib.parse import quote + +toc_name = 'Contents' +valid_toc_headings = {'## TOC', '##TOC'} + +TocItem = namedtuple('TocItem', ['name', 'anchor', 'level']) +punctuation_rx = re.compile(r'[^\w\- ]+') +numbered_heading_rx = re.compile(r'^[0-9]+\. ') +specialchar_rx = re.compile(r'[⅔]+') + +def name_to_anchor(name): + # GitHub's algorithm for generating anchors from headings + # https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/toc_filter.rb + anchor = name.strip().lower() # lowercase + anchor = re.sub(punctuation_rx, '', anchor) # remove punctuation + anchor = anchor.replace(' ', '-') # replace spaces with dash + anchor = re.sub(specialchar_rx, '', anchor) # remove misc special chars + anchor = quote(anchor) # url encode + return anchor + +def get_toc_index(lines): + toc_index = None + for i, line in enumerate(lines): + if line.rstrip() in valid_toc_headings: + toc_index = i + break + return toc_index + +def get_toc_items(lines, toc_index): + for i, line in enumerate(lines): + if i <= toc_index: + continue + if line.startswith('##'): + name = line.lstrip('#') + level = len(line) - len(name) - len('##') + name = name.strip() + anchor = name_to_anchor(name) + yield TocItem(name, anchor, level) + +def toc_string(toc_items): + lines = [f'## {toc_name}', ''] + for name, anchor, level in toc_items: + padding = ' ' * level + if re.match(numbered_heading_rx, name): + bullet, name = name.split('.', 1) + bullet += '.' + name = name.lstrip() + else: + bullet = '-' + lines.append(f'{padding}{bullet} [{name}](#{anchor})') + return '\n'.join(lines) + '\n' + +def add_toc(filename): + with open(filename, 'r', encoding='utf-8') as file: + lines = file.readlines() + toc_index = get_toc_index(lines) + if toc_index is None: + return None # no TOC heading + toc_items = list(get_toc_items(lines, toc_index)) + if not toc_items: + return False # no content headings + with open(filename, 'w', encoding='utf-8') as file: + for i, line in enumerate(lines): + if i == toc_index: + file.write(toc_string(toc_items)) + else: + file.write(line) + return True # OK + +def main(): + if len(sys.argv) < 2: + print(f'Usage: {sys.argv[0]} file.md', file=sys.stderr) + sys.exit(1) + for filename in sys.argv[1:]: + print(filename) + result = add_toc(filename) + if result is None: + print('Warning: No "## TOC" heading found', file=sys.stderr) + elif result is False: + print('Warning: No content headings found', file=sys.stderr) + else: + print('OK') + +if __name__ == '__main__': + main() diff --git a/tools/unique.py b/tools/unique.py new file mode 100755 index 00000000..34530a3e --- /dev/null +++ b/tools/unique.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python unique.py [-f|--flip] [-x|--cross] image.png + +Erase duplicate tiles from an input image. +-f or --flip counts X/Y mirrored tiles as duplicates. +-x or --cross erases with a cross instead of a blank tile. +""" + +import sys + +import png + +def rgb5_pixels(row): + yield from (tuple(c // 8 for c in row[x:x+3]) for x in range(0, len(row), 4)) + +def rgb8_pixels(row): + yield from (c * 8 + c // 4 for pixel in row for c in pixel) + +def gray_pixels(row): + yield from (pixel[0] // 10 for pixel in row) + +def rows_to_tiles(rows, width, height): + assert len(rows) == height and len(rows[0]) == width + yield from (tuple(tuple(row[x:x+8]) for row in rows[y:y+8]) + for y in range(0, height, 8) for x in range(0, width, 8)) + +def tiles_to_rows(tiles, width, height): + assert width % 8 == 0 and height % 8 == 0 + width, height = width // 8, height // 8 + tiles = list(tiles) + assert len(tiles) == width * height + tile_rows = (tiles[y:y+width] for y in range(0, width * height, width)) + yield from ([tile[y][x] for tile in tile_row for x in range(8)] + for tile_row in tile_rows for y in range(8)) + +def tile_variants(tile, flip): + yield tile + if flip: + yield tile[::-1] + yield tuple(row[::-1] for row in tile) + yield tuple(row[::-1] for row in tile[::-1]) + +def unique_tiles(tiles, flip, cross): + if cross: + blank = [[(0, 0, 0)] * 8 for _ in range(8)] + for y in range(8): + blank[y][y] = blank[y][7 - y] = (31, 31, 31) + blank = tuple(tuple(row) for row in blank) + else: + blank = tuple(tuple([(31, 31, 31)] * 8) for _ in range(8)) + seen = set() + for tile in tiles: + if any(variant in seen for variant in tile_variants(tile, flip)): + yield blank + else: + yield tile + seen.add(tile) + +def is_grayscale(colors): + return (colors.issubset({(31, 31, 31), (21, 21, 21), (10, 10, 10), (0, 0, 0)}) or + colors.issubset({(31, 31, 31), (20, 20, 20), (10, 10, 10), (0, 0, 0)})) + +def erase_duplicates(filename, flip, cross): + with open(filename, 'rb') as file: + width, height, rows = png.Reader(file).asRGBA8()[:3] + rows = [list(rgb5_pixels(row)) for row in rows] + if width % 8 or height % 8: + return False + tiles = unique_tiles(rows_to_tiles(rows, width, height), flip, cross) + rows = list(tiles_to_rows(tiles, width, height)) + if is_grayscale({c for row in rows for c in row}): + rows = [list(gray_pixels(row)) for row in rows] + writer = png.Writer(width, height, greyscale=True, bitdepth=2, compression=9) + else: + rows = [list(rgb8_pixels(row)) for row in rows] + writer = png.Writer(width, height, greyscale=False, bitdepth=8, compression=9) + with open(filename, 'wb') as file: + writer.write(file, rows) + return True + +def main(): + flip = cross = False + while True: + if len(sys.argv) < 2: + print(f'Usage: {sys.argv[0]} [-f|--flip] [-x|--cross] tileset.png', file=sys.stderr) + sys.exit(1) + elif sys.argv[1] in {'-f', '--flip'}: + flip = True + elif sys.argv[1] in {'-x', '--cross'}: + cross = True + elif sys.argv[1] in {'-fx', '-xf'}: + flip = cross = True + else: + break + sys.argv.pop(1) + for filename in sys.argv[1:]: + if not filename.lower().endswith('.png'): + print(f'{filename} is not a .png file!', file=sys.stderr) + elif not erase_duplicates(filename, flip, cross): + print(f'{filename} is not divisible into 8x8 tiles!', file=sys.stderr) + +if __name__ == '__main__': + main() diff --git a/tools/unnamed.py b/tools/unnamed.py index c5544437..265f1452 100755 --- a/tools/unnamed.py +++ b/tools/unnamed.py @@ -1,130 +1,137 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- -from sys import stderr, exit -from subprocess import Popen, PIPE -from struct import unpack, calcsize -from enum import Enum +""" +Usage: unnamed.py [-h] [-r rootdir] [-l count] pokered.sym -class symtype(Enum): - LOCAL = 0 - IMPORT = 1 - EXPORT = 2 +Parse the symfile to find unnamed symbols. +""" -def unpack_file(fmt, file): - size = calcsize(fmt) - return unpack(fmt, file.read(size)) +import sys +import argparse +import subprocess +import struct +import enum +import signal + +class symtype(enum.Enum): + LOCAL = 0 + IMPORT = 1 + EXPORT = 2 + +def unpack_from(fmt, file): + size = struct.calcsize(fmt) + return struct.unpack(fmt, file.read(size)) def read_string(file): - buf = bytearray() - while True: - b = file.read(1) - if b is None or b == b'\0': - return buf.decode() - else: - buf += b - + buf = bytearray() + while True: + b = file.read(1) + if b is None or b == b'\0': + return buf.decode() + buf += b # Fix broken pipe when using `head` -from signal import signal, SIGPIPE, SIG_DFL -signal(SIGPIPE,SIG_DFL) +signal.signal(signal.SIGPIPE, signal.SIG_DFL) -import argparse -parser = argparse.ArgumentParser(description="Parse the symfile to find unnamed symbols") -parser.add_argument('symfile', type=argparse.FileType('r'), help="the list of symbols") -parser.add_argument('-r', '--rootdir', type=str, help="scan the output files to obtain a list of files with unnamed symbols (NOTE: will rebuild objects as necessary)") -parser.add_argument('-l', '--list', type=int, default=0, help="output this many of each file's unnamed symbols (NOTE: requires -r)") +parser = argparse.ArgumentParser(description='Parse the symfile to find unnamed symbols') +parser.add_argument('symfile', type=argparse.FileType('r'), + help='the list of symbols') +parser.add_argument('-r', '--rootdir', type=str, + help='scan the output files to obtain a list of files with unnamed symbols (note: will rebuild objects as necessary)') +parser.add_argument('-l', '--list', type=int, default=0, + help="output this many of each file's unnamed symbols (note: requires -r)") args = parser.parse_args() # Get list of object files objects = None if args.rootdir: - for line in Popen(["make", "-C", args.rootdir, "-s", "-p", "DEBUG=1"], - stdout=PIPE).stdout.read().decode().split("\n"): - if line.startswith("pokered_obj := "): - objects = line[15:].strip().split() - break - else: - print("Error: Object files not found!", file=stderr) - exit(1) + for line in subprocess.Popen(['make', '-C', args.rootdir, '-s', '-p', 'DEBUG=1'], + stdout=subprocess.PIPE).stdout.read().decode().split('\n'): + if line.startswith('pokered_obj := '): + objects = line[19:].strip().split() + break + else: + print('Error: Object files not found!', file=sys.stderr) + sys.exit(1) # Scan all unnamed symbols from the symfile symbols_total = 0 symbols = set() for line in args.symfile: - line = line.split(";")[0].strip() - split = line.split(" ") - if len(split) < 2: - continue + line = line.split(';')[0].strip() + split = line.split(' ') + if len(split) < 2: + continue - symbols_total += 1 + symbols_total += 1 - symbol = " ".join(split[1:]).strip() - if symbol[-3:].lower() == split[0][-3:].lower(): - symbols.add(symbol) + symbol = ' '.join(split[1:]).strip() + if symbol[-3:].lower() == split[0][-3:].lower(): + symbols.add(symbol) # If no object files were provided, just print what we know and exit -print("Unnamed pokered symbols: %d (%.2f%% complete)" % (len(symbols), - (symbols_total - len(symbols)) / symbols_total * 100)) +unnamed_percent = 100 * (symbols_total - len(symbols)) / symbols_total +print(f'Unnamed pokered symbols: {len(symbols)} ({unnamed_percent:.2f}% complete)') if not objects: - for sym in symbols: - print(sym) - exit() + for sym in symbols: + print(sym) + sys.exit() # Count the amount of symbols in each file -files = {} +file_symbols = {} for objfile in objects: - f = open(objfile, "rb") - obj_ver = None + with open(objfile, 'rb') as file: + obj_ver = None - magic = unpack_file("4s", f)[0] - if magic == b'RGB6': - obj_ver = 6 - elif magic == b'RGB9': - obj_ver = 10 + unpack_file("= 2 else 'pokered.map' + outfile = sys.argv[2] if len(sys.argv) >= 3 else 'used_space.png' + + num_banks = 0x80 + bank_mask = 0x3FFF + bank_size = 0x4000 # bytes + + bpp = 8 # bytes per pixel + height = 256 # pixels + assert bank_size % bpp == 0 and (bank_size // bpp) % height == 0 + + pixels_per_bank = bank_size // bpp # 2048 pixels + bank_width = pixels_per_bank // height # 8 pixels + width = bank_width * num_banks # 1024 pixels + + reader = MapReader() + with open(mapfile, 'r', encoding='utf-8') as file: + reader.read_map_data(file.readlines()) + + hit_data = [] + default_bank_data = {'sections': [], 'used': 0, 'slack': bank_size} + for bank in range(num_banks): + hits = [0] * pixels_per_bank + bank_data = reader.bank_data['ROM0 bank' if bank == 0 else 'ROMX bank'] + data = bank_data.get(bank, default_bank_data) + for s in data['sections']: + beg = s['beg'] & bank_mask + end = s['end'] & bank_mask + for i in range(beg, end + 1): + hits[i // bpp] += 1 + hit_data.append(hits) + + pixels = [[(0xFF, 0xFF, 0xFF)] * width for _ in range(height)] + for bank, hits in enumerate(hit_data): + hue = 0 if bank == 0 else 210 if bank % 2 else 270 + for i, hit in enumerate(hits): + x, y = i % bank_width + bank * bank_width, i // bank_width + hls = (hue / 360, 1 - (85 * hit / bpp) / 100, 1) + rgb = tuple(int(c * 0xFF) for c in hls_to_rgb(*hls)) + pixels[y][x] = rgb + + png_data = [tuple(c for pixel in row for c in pixel) for row in pixels] + with open(outfile, 'wb') as file: + writer = png.Writer(width, height, greyscale=False, bitdepth=8, compression=9) + writer.write(file, png_data) + +if __name__ == '__main__': + main() From fa7d76f39ef2ca94f1b4a1ba01039f748664f523 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 18 Sep 2022 23:59:03 -0400 Subject: [PATCH 09/26] Fix tools/unnamed.py and add tools/consts.py --- tools/consts.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/unnamed.py | 4 ++-- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100755 tools/consts.py diff --git a/tools/consts.py b/tools/consts.py new file mode 100755 index 00000000..37c85951 --- /dev/null +++ b/tools/consts.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python consts.py constants/some_constants.asm + +View numeric values of `const`ants. +""" + +import sys +import re + +const_value = 0 +const_inc = 1 + +def asm_int(s): + base = {'$': 16, '&': 8, '%': 2}.get(s[0], 10) + return int(s if base == 10 else s[1:], base) + +def print_const(s, v): + print(f'{s} == {v} == ${v:x}') + +def parse_for_constants(line): + global const_value, const_inc + if not (m := re.match(r'^\s+([A-Za-z_][A-Za-z0-9_@#]*)(?:\s+([^;\\n]+))?', line)): + return + macro, rest = m.groups() + args = [arg.strip() for arg in rest.split(',')] if rest else [] + if args and not args[-1]: + args = args[:-1] + if macro == 'const_def': + const_value = asm_int(args[0]) if len(args) >= 1 else 0 + const_inc = asm_int(args[1]) if len(args) >= 2 else 1 + elif macro == 'const': + print_const(args[0], const_value) + const_value += const_inc + elif macro == 'shift_const': + print_const(args[0], 1 << const_value) + print_const(args[0] + '_F', const_value) + const_value += const_inc + elif macro == 'const_skip': + const_value += const_inc * (asm_int(args[0]) if len(args) >= 1 else 1) + elif macro == 'const_next': + const_value = asm_int(args[0]) + +def main(): + if len(sys.argv) < 2: + print(f'Usage: {sys.argv[0]} constants/some_constants.asm', file=sys.stderr) + sys.exit(1) + for filename in sys.argv[1:]: + with open(filename, 'r', encoding='utf-8') as file: + for line in file: + parse_for_constants(line) + +if __name__ == '__main__': + main() diff --git a/tools/unnamed.py b/tools/unnamed.py index 265f1452..9dcf5cb3 100755 --- a/tools/unnamed.py +++ b/tools/unnamed.py @@ -48,8 +48,8 @@ objects = None if args.rootdir: for line in subprocess.Popen(['make', '-C', args.rootdir, '-s', '-p', 'DEBUG=1'], stdout=subprocess.PIPE).stdout.read().decode().split('\n'): - if line.startswith('pokered_obj := '): - objects = line[19:].strip().split() + if line.startswith('pokered_obj :='): + objects = line[len('pokered_obj :='):].strip().split() break else: print('Error: Object files not found!', file=sys.stderr) From d2be79adf1c366b28821817cefaab9066668b205 Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Sat, 24 Sep 2022 11:30:29 -0400 Subject: [PATCH 10/26] Add constants and table macros for the map sprite sets (#392) Fixes #391 --- constants.asm | 1 + constants/sprite_set_constants.asm | 38 +++++++ data/maps/sprite_sets.asm | 160 +++++++++++++---------------- engine/overworld/map_sprites.asm | 72 +++++++------ ram/wram.asm | 4 +- 5 files changed, 150 insertions(+), 125 deletions(-) create mode 100644 constants/sprite_set_constants.asm diff --git a/constants.asm b/constants.asm index a307549b..b9445784 100644 --- a/constants.asm +++ b/constants.asm @@ -28,6 +28,7 @@ INCLUDE "constants/map_constants.asm" INCLUDE "constants/map_data_constants.asm" INCLUDE "constants/map_object_constants.asm" INCLUDE "constants/hide_show_constants.asm" +INCLUDE "constants/sprite_set_constants.asm" INCLUDE "constants/credits_constants.asm" INCLUDE "constants/audio_constants.asm" INCLUDE "constants/music_constants.asm" diff --git a/constants/sprite_set_constants.asm b/constants/sprite_set_constants.asm new file mode 100644 index 00000000..4f617c4a --- /dev/null +++ b/constants/sprite_set_constants.asm @@ -0,0 +1,38 @@ +; sprite set ids +; indexes for SpriteSets (see data/maps/sprite_sets.asm) +; values for MapSpriteSets and SplitMapSpriteSets (see data/maps/sprite_sets.asm) + const_def 1 + const SPRITESET_PALLET_VIRIDIAN ; 01 + const SPRITESET_PEWTER_CERULEAN ; 02 + const SPRITESET_LAVENDER ; 03 + const SPRITESET_VERMILION ; 04 + const SPRITESET_CELADON ; 05 + const SPRITESET_INDIGO ; 06 + const SPRITESET_SAFFRON ; 07 + const SPRITESET_SILENCE_BRIDGE ; 08 + const SPRITESET_CYCLING_ROAD ; 09 + const SPRITESET_FUCHSIA ; 0a +DEF NUM_SPRITE_SETS EQU const_value - 1 + +; split sprite set ids +; indexes for SplitMapSpriteSets (see data/maps/sprite_sets.asm) +; values for MapSpriteSets (see data/maps/sprite_sets.asm) + const_next $f1 +DEF FIRST_SPLIT_SET EQU const_value + const SPLITSET_ROUTE_2 ; f1 + const SPLITSET_ROUTE_10 ; f2 + const SPLITSET_ROUTE_11 ; f3 + const SPLITSET_ROUTE_12 ; f4 + const SPLITSET_ROUTE_15 ; f5 + const SPLITSET_ROUTE_16 ; f6 + const SPLITSET_ROUTE_18 ; f7 + const SPLITSET_ROUTE_20 ; f8 + const SPLITSET_ROUTE_5 ; f9 + const SPLITSET_ROUTE_6 ; fa + const SPLITSET_ROUTE_7 ; fb + const SPLITSET_ROUTE_8 ; fc +DEF NUM_SPLIT_SETS EQU const_value - FIRST_SPLIT_SET + +; split directions +DEF EAST_WEST EQU 1 +DEF NORTH_SOUTH EQU 2 diff --git a/data/maps/sprite_sets.asm b/data/maps/sprite_sets.asm index 755ed2fc..23274aa2 100644 --- a/data/maps/sprite_sets.asm +++ b/data/maps/sprite_sets.asm @@ -1,73 +1,72 @@ +; Valid sprite IDs for each outdoor map. + MapSpriteSets: table_width 1, MapSpriteSets - db $01 ; PALLET_TOWN - db $01 ; VIRIDIAN_CITY - db $02 ; PEWTER_CITY - db $02 ; CERULEAN_CITY - db $03 ; LAVENDER_TOWN - db $04 ; VERMILION_CITY - db $05 ; CELADON_CITY - db $0a ; FUCHSIA_CITY - db $01 ; CINNABAR_ISLAND - db $06 ; INDIGO_PLATEAU - db $07 ; SAFFRON_CITY - db $01 ; unused map ID - db $01 ; ROUTE_1 - db $f1 ; ROUTE_2 - db $02 ; ROUTE_3 - db $02 ; ROUTE_4 - db $f9 ; ROUTE_5 - db $fa ; ROUTE_6 - db $fb ; ROUTE_7 - db $fc ; ROUTE_8 - db $02 ; ROUTE_9 - db $f2 ; ROUTE_10 - db $f3 ; ROUTE_11 - db $f4 ; ROUTE_12 - db $08 ; ROUTE_13 - db $08 ; ROUTE_14 - db $f5 ; ROUTE_15 - db $f6 ; ROUTE_16 - db $09 ; ROUTE_17 - db $f7 ; ROUTE_18 - db $0a ; ROUTE_19 - db $f8 ; ROUTE_20 - db $01 ; ROUTE_21 - db $01 ; ROUTE_22 - db $06 ; ROUTE_23 - db $02 ; ROUTE_24 - db $02 ; ROUTE_25 + db SPRITESET_PALLET_VIRIDIAN ; PALLET_TOWN + db SPRITESET_PALLET_VIRIDIAN ; VIRIDIAN_CITY + db SPRITESET_PEWTER_CERULEAN ; PEWTER_CITY + db SPRITESET_PEWTER_CERULEAN ; CERULEAN_CITY + db SPRITESET_LAVENDER ; LAVENDER_TOWN + db SPRITESET_VERMILION ; VERMILION_CITY + db SPRITESET_CELADON ; CELADON_CITY + db SPRITESET_FUCHSIA ; FUCHSIA_CITY + db SPRITESET_PALLET_VIRIDIAN ; CINNABAR_ISLAND + db SPRITESET_INDIGO ; INDIGO_PLATEAU + db SPRITESET_SAFFRON ; SAFFRON_CITY + db SPRITESET_PALLET_VIRIDIAN ; UNUSED_MAP_0B + db SPRITESET_PALLET_VIRIDIAN ; ROUTE_1 + db SPLITSET_ROUTE_2 ; ROUTE_2 + db SPRITESET_PEWTER_CERULEAN ; ROUTE_3 + db SPRITESET_PEWTER_CERULEAN ; ROUTE_4 + db SPLITSET_ROUTE_5 ; ROUTE_5 + db SPLITSET_ROUTE_6 ; ROUTE_6 + db SPLITSET_ROUTE_7 ; ROUTE_7 + db SPLITSET_ROUTE_8 ; ROUTE_8 + db SPRITESET_PEWTER_CERULEAN ; ROUTE_9 + db SPLITSET_ROUTE_10 ; ROUTE_10 + db SPLITSET_ROUTE_11 ; ROUTE_11 + db SPLITSET_ROUTE_12 ; ROUTE_12 + db SPRITESET_SILENCE_BRIDGE ; ROUTE_13 + db SPRITESET_SILENCE_BRIDGE ; ROUTE_14 + db SPLITSET_ROUTE_15 ; ROUTE_15 + db SPLITSET_ROUTE_16 ; ROUTE_16 + db SPRITESET_CYCLING_ROAD ; ROUTE_17 + db SPLITSET_ROUTE_18 ; ROUTE_18 + db SPRITESET_FUCHSIA ; ROUTE_19 + db SPLITSET_ROUTE_20 ; ROUTE_20 + db SPRITESET_PALLET_VIRIDIAN ; ROUTE_21 + db SPRITESET_PALLET_VIRIDIAN ; ROUTE_22 + db SPRITESET_INDIGO ; ROUTE_23 + db SPRITESET_PEWTER_CERULEAN ; ROUTE_24 + db SPRITESET_PEWTER_CERULEAN ; ROUTE_25 assert_table_length FIRST_INDOOR_MAP -DEF EAST_WEST EQU 1 -DEF NORTH_SOUTH EQU 2 - ; Format: -; 00: determines whether the map is split EAST_WEST or NORTH_SOUTH -; 01: coordinate of dividing line -; 02: sprite set ID if in the West or North side -; 03: sprite set ID if in the East or South side +; #1: whether the map is split EAST_WEST or NORTH_SOUTH +; #2: coordinate of dividing line +; #3: sprite set ID if on the west or north side +; #4: sprite set ID if on the east or south side SplitMapSpriteSets: - db NORTH_SOUTH, 37, $02, $01 ; $f1 - db NORTH_SOUTH, 50, $02, $03 ; $f2 - db EAST_WEST, 57, $04, $08 ; $f3 - db NORTH_SOUTH, 21, $03, $08 ; $f4 - db EAST_WEST, 8, $0A, $08 ; $f5 - db EAST_WEST, 24, $09, $05 ; $f6 - db EAST_WEST, 34, $09, $0A ; $f7 - db EAST_WEST, 53, $01, $0A ; $f8 - db NORTH_SOUTH, 33, $02, $07 ; $f9 - db NORTH_SOUTH, 2, $07, $04 ; $fa - db EAST_WEST, 17, $05, $07 ; $fb - db EAST_WEST, 3, $07, $03 ; $fc + table_width 4, SplitMapSpriteSets + db NORTH_SOUTH, 37, SPRITESET_PEWTER_CERULEAN, SPRITESET_PALLET_VIRIDIAN ; SPLITSET_ROUTE_2 + db NORTH_SOUTH, 50, SPRITESET_PEWTER_CERULEAN, SPRITESET_LAVENDER ; SPLITSET_ROUTE_10 + db EAST_WEST, 57, SPRITESET_VERMILION, SPRITESET_SILENCE_BRIDGE ; SPLITSET_ROUTE_11 + db NORTH_SOUTH, 21, SPRITESET_LAVENDER, SPRITESET_SILENCE_BRIDGE ; SPLITSET_ROUTE_12 + db EAST_WEST, 8, SPRITESET_FUCHSIA, SPRITESET_SILENCE_BRIDGE ; SPLITSET_ROUTE_15 + db EAST_WEST, 24, SPRITESET_CYCLING_ROAD, SPRITESET_CELADON ; SPLITSET_ROUTE_16 + db EAST_WEST, 34, SPRITESET_CYCLING_ROAD, SPRITESET_FUCHSIA ; SPLITSET_ROUTE_18 + db EAST_WEST, 53, SPRITESET_PALLET_VIRIDIAN, SPRITESET_FUCHSIA ; SPLITSET_ROUTE_20 + db NORTH_SOUTH, 33, SPRITESET_PEWTER_CERULEAN, SPRITESET_SAFFRON ; SPLITSET_ROUTE_5 + db NORTH_SOUTH, 2, SPRITESET_SAFFRON, SPRITESET_VERMILION ; SPLITSET_ROUTE_6 + db EAST_WEST, 17, SPRITESET_CELADON, SPRITESET_SAFFRON ; SPLITSET_ROUTE_7 + db EAST_WEST, 3, SPRITESET_SAFFRON, SPRITESET_LAVENDER ; SPLITSET_ROUTE_8 + assert_table_length NUM_SPLIT_SETS SpriteSets: + ; each sprite set has 9 walking sprites and 2 still sprites + table_width 9 + 2, SpriteSets -; each sprite set has 9 walking sprites and 2 still sprites -DEF SPRITE_SET_LENGTH EQU 9 + 2 - -; sprite set $01 - table_width 1 +; SPRITESET_PALLET_VIRIDIAN db SPRITE_BLUE db SPRITE_YOUNGSTER db SPRITE_GIRL @@ -79,10 +78,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_SWIMMER db SPRITE_POKE_BALL db SPRITE_GAMBLER_ASLEEP - assert_table_length SPRITE_SET_LENGTH -; sprite set $02 - table_width 1 +; SPRITESET_PEWTER_CERULEAN db SPRITE_YOUNGSTER db SPRITE_ROCKET db SPRITE_SUPER_NERD @@ -94,10 +91,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_COOLTRAINER_M db SPRITE_POKE_BALL db SPRITE_UNUSED_GAMBLER_ASLEEP_2 - assert_table_length SPRITE_SET_LENGTH -; sprite set $03 - table_width 1 +; SPRITESET_LAVENDER db SPRITE_LITTLE_GIRL db SPRITE_GIRL db SPRITE_SUPER_NERD @@ -109,10 +104,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_GUARD db SPRITE_POKE_BALL db SPRITE_UNUSED_GAMBLER_ASLEEP_2 - assert_table_length SPRITE_SET_LENGTH -; sprite set $04 - table_width 1 +; SPRITESET_VERMILION db SPRITE_BEAUTY db SPRITE_SUPER_NERD db SPRITE_YOUNGSTER @@ -124,10 +117,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_COOLTRAINER_M db SPRITE_POKE_BALL db SPRITE_UNUSED_GAMBLER_ASLEEP_2 - assert_table_length SPRITE_SET_LENGTH -; sprite set $05 - table_width 1 +; SPRITESET_CELADON db SPRITE_LITTLE_GIRL db SPRITE_LITTLE_BOY db SPRITE_GIRL @@ -139,10 +130,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_ROCKET db SPRITE_POKE_BALL db SPRITE_SNORLAX - assert_table_length SPRITE_SET_LENGTH -; sprite set $06 - table_width 1 +; SPRITESET_INDIGO db SPRITE_YOUNGSTER db SPRITE_GYM_GUIDE db SPRITE_MONSTER @@ -154,10 +143,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_GAMBLER db SPRITE_POKE_BALL db SPRITE_UNUSED_GAMBLER_ASLEEP_2 - assert_table_length SPRITE_SET_LENGTH -; sprite set $07 - table_width 1 +; SPRITESET_SAFFRON db SPRITE_ROCKET db SPRITE_SCIENTIST db SPRITE_SILPH_WORKER @@ -169,10 +156,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_MONSTER db SPRITE_POKE_BALL db SPRITE_UNUSED_GAMBLER_ASLEEP_2 - assert_table_length SPRITE_SET_LENGTH -; sprite set $08 - table_width 1 +; SPRITESET_SILENCE_BRIDGE db SPRITE_BIKER db SPRITE_SUPER_NERD db SPRITE_MIDDLE_AGED_MAN @@ -184,10 +169,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_MONSTER db SPRITE_POKE_BALL db SPRITE_SNORLAX - assert_table_length SPRITE_SET_LENGTH -; sprite set $09 - table_width 1 +; SPRITESET_CYCLING_ROAD db SPRITE_BIKER db SPRITE_COOLTRAINER_M db SPRITE_SILPH_WORKER @@ -199,10 +182,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_SUPER_NERD db SPRITE_POKE_BALL db SPRITE_SNORLAX - assert_table_length SPRITE_SET_LENGTH -; sprite set $0a - table_width 1 +; SPRITESET_FUCHSIA db SPRITE_BIRD db SPRITE_COOLTRAINER_M db SPRITE_FAIRY @@ -214,4 +195,5 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_YOUNGSTER db SPRITE_POKE_BALL db SPRITE_FOSSIL - assert_table_length SPRITE_SET_LENGTH + + assert_table_length NUM_SPRITE_SETS diff --git a/engine/overworld/map_sprites.asm b/engine/overworld/map_sprites.asm index fa3a37dc..5a4c27c3 100644 --- a/engine/overworld/map_sprites.asm +++ b/engine/overworld/map_sprites.asm @@ -11,7 +11,7 @@ InitMapSprites:: call InitOutsideMapSprites ret c ; return if the map is an outside map (already handled by above call) -; if the map is an inside map (i.e. mapID >= $25) +; if the map is an inside map (i.e. mapID >= FIRST_INDOOR_MAP) ld hl, wSpritePlayerStateData1PictureID ld de, wSpritePlayerStateData2PictureID ; Loop to copy picture IDs from [x#SPRITESTATEDATA1_PICTUREID] @@ -19,10 +19,10 @@ InitMapSprites:: .copyPictureIDLoop ld a, [hl] ; a = [x#SPRITESTATEDATA1_PICTUREID] ld [de], a ; [x#SPRITESTATEDATA2_PICTUREID] = a - ld a, $10 + ld a, SPRITESTATEDATA1_LENGTH add e ld e, a - ld a, $10 + ld a, SPRITESTATEDATA1_LENGTH add l ld l, a jr nz, .copyPictureIDLoop @@ -37,7 +37,7 @@ LoadMapSpriteTilePatterns: ret .spritesExist ld c, a ; c = [wNumSprites] - ld b, $10 ; number of sprite slots + ld b, NUM_SPRITESTATEDATA_STRUCTS ld hl, wSpritePlayerStateData2PictureID xor a ldh [hFourTileSpriteCount], a @@ -47,7 +47,7 @@ LoadMapSpriteTilePatterns: ld a, [hli] ; a = [x#SPRITESTATEDATA2_PICTUREID] ld [hld], a ; [x#SPRITESTATEDATA2_IMAGEBASEOFFSET] = a ld a, l - add $10 + add SPRITESTATEDATA1_LENGTH ld l, a dec b jr nz, .copyPictureIDLoop @@ -69,17 +69,17 @@ LoadMapSpriteTilePatterns: cp [hl] ; do the picture ID's match? jp z, .alreadyLoaded ld a, e - add $10 + add SPRITESTATEDATA1_LENGTH ld e, a jr .checkIfAlreadyLoadedLoop .notAlreadyLoaded ld de, wSpritePlayerStateData2ImageBaseOffset - ld b, $01 + ld b, 1 ; loop to find the highest tile pattern VRAM slot (among the first 10 slots) used by a previous sprite slot ; this is done in order to find the first free VRAM slot available .findNextVRAMSlotLoop ld a, e - add $10 + add SPRITESTATEDATA1_LENGTH ld e, a ld a, l cp e ; reached current slot? @@ -187,7 +187,7 @@ LoadMapSpriteTilePatterns: jr nz, .loadWhileLCDOn pop af pop hl - set 3, h ; add $800 to hl + set 3, h ; add $80 tiles to hl push hl ld h, d ld l, e @@ -200,7 +200,7 @@ LoadMapSpriteTilePatterns: .loadWhileLCDOn pop af pop hl - set 3, h ; add $800 to hl + set 3, h ; add $80 tiles to hl ld b, a swap c call CopyVideoData ; load tile pattern data for sprite when walking @@ -214,18 +214,18 @@ LoadMapSpriteTilePatterns: ld [hl], a ; [x#SPRITESTATEDATA2_IMAGEBASEOFFSET] = a .nextSpriteSlot ld a, l - add $10 + add SPRITESTATEDATA2_LENGTH ld l, a dec c jp nz, .loadTilePatternLoop ld hl, wSpritePlayerStateData2PictureID - ld b, $10 + ld b, NUM_SPRITESTATEDATA_STRUCTS ; the pictures IDs stored at [x#SPRITESTATEDATA2_PICTUREID] are no longer needed, ; so zero them .zeroStoredPictureIDLoop xor a ld [hl], a ; [x#SPRITESTATEDATA2_PICTUREID] - ld a, $10 + ld a, SPRITESTATEDATA2_LENGTH add l ld l, a dec b @@ -264,7 +264,7 @@ InitOutsideMapSprites: inc h .noCarry ld a, [hl] ; a = spriteSetID - cp $f0 ; does the map have 2 sprite sets? + cp FIRST_SPLIT_SET - 1 ; does the map have 2 sprite sets? call nc, GetSplitMapSpriteSetID ; if so, choose the appropriate one ld b, a ; b = spriteSetID ld a, [wFontLoaded] @@ -300,7 +300,7 @@ InitOutsideMapSprites: ; with picture IDs. This is done so that LoadMapSpriteTilePatterns will ; load tile patterns for all sprite pictures in the sprite set. .loadSpriteSetLoop - ld a, $10 + ld a, SPRITESTATEDATA2_LENGTH add l ld l, a ld a, [de] ; sprite picture ID from sprite set @@ -313,7 +313,7 @@ InitOutsideMapSprites: jr nz, .loadSpriteSetLoop ld b, 4 ; 4 remaining sprite slots .zeroRemainingSlotsLoop ; loop to zero the picture ID's of the remaining sprite slots - ld a, $10 + ld a, SPRITESTATEDATA2_LENGTH add l ld l, a xor a @@ -335,7 +335,7 @@ InitOutsideMapSprites: .zeroVRAMSlotsLoop xor a ld [hl], a ; [x#SPRITESTATEDATA2_IMAGEBASEOFFSET] - ld a, $10 + ld a, SPRITESTATEDATA2_LENGTH add l ld l, a dec b @@ -366,14 +366,14 @@ InitOutsideMapSprites: inc c .skipGettingPictureIndex push hl - inc h - ld a, $0e + inc h ; HIGH(wSpriteStateData2) + ld a, SPRITESTATEDATA2_IMAGEBASEOFFSET - SPRITESTATEDATA1_PICTUREID add l ld l, a ld a, c ; a = VRAM slot (zero if sprite slot is not used) ld [hl], a ; [x#SPRITESTATEDATA2_IMAGEBASEOFFSET] pop hl - ld a, $10 + ld a, SPRITESTATEDATA1_LENGTH add l ld l, a and a @@ -384,7 +384,7 @@ InitOutsideMapSprites: ; Chooses the correct sprite set ID depending on the player's position within ; the map for maps with two sprite sets. GetSplitMapSpriteSetID: - cp $f8 + cp SPLITSET_ROUTE_20 jr z, .route20 ld hl, SplitMapSpriteSets and $0f @@ -396,8 +396,8 @@ GetSplitMapSpriteSetID: jr nc, .noCarry inc h .noCarry - ld a, [hli] ; determines whether the map is split East/West or North/South - cp $01 + ld a, [hli] ; whether the map is split EAST_WEST or NORTH_SOUTH + cp EAST_WEST ld a, [hli] ; position of dividing line ld b, a jr z, .eastWestDivide @@ -409,35 +409,39 @@ GetSplitMapSpriteSetID: .compareCoord cp b jr c, .loadSpriteSetID -; if in the East side or South side +; if in the east side or south side inc hl .loadSpriteSetID ld a, [hl] ret -; Uses sprite set $01 for West side and $0A for East side. +; Uses sprite set SPRITESET_PALLET_VIRIDIAN for west side and SPRITESET_FUCHSIA for east side. ; Route 20 is a special case because the two map sections have a more complex ; shape instead of the map simply being split horizontally or vertically. .route20 ld hl, wXCoord + ; Use SPRITESET_PALLET_VIRIDIAN if X < 43 ld a, [hl] - cp $2b - ld a, $01 + cp 43 + ld a, SPRITESET_PALLET_VIRIDIAN ret c + ; Use SPRITESET_FUCHSIA if X >= 62. ld a, [hl] - cp $3e - ld a, $0a + cp 62 + ld a, SPRITESET_FUCHSIA ret nc + ; If 55 <= X < 62, split Y at 8; else 43 <= X < 55, so split Y at 13 ld a, [hl] - cp $37 - ld b, $08 + cp 55 + ld b, 8 jr nc, .next - ld b, $0d + ld b, 13 .next + ; Use SPRITESET_FUCHSIA if Y < split; else use SPRITESET_PALLET_VIRIDIAN ld a, [wYCoord] cp b - ld a, $0a + ld a, SPRITESET_FUCHSIA ret c - ld a, $01 + ld a, SPRITESET_PALLET_VIRIDIAN ret INCLUDE "data/maps/sprite_sets.asm" diff --git a/ram/wram.asm b/ram/wram.asm index 3becc82a..4923ebca 100644 --- a/ram/wram.asm +++ b/ram/wram.asm @@ -101,7 +101,7 @@ wSpriteStateData1:: ; - E ; - F wSpritePlayerStateData1:: spritestatedata1 wSpritePlayerStateData1 ; player is struct 0 -; wSprite02StateData1 - wSprite15StateData1 +; wSprite01StateData1 - wSprite15StateData1 FOR n, 1, NUM_SPRITESTATEDATA_STRUCTS wSprite{02d:n}StateData1:: spritestatedata1 wSprite{02d:n}StateData1 ENDR @@ -127,7 +127,7 @@ wSpriteStateData2:: ; - E: sprite image base offset (in video ram, player always has value 1, used to compute sprite image index) ; - F wSpritePlayerStateData2:: spritestatedata2 wSpritePlayerStateData2 ; player is struct 0 -; wSprite02StateData2 - wSprite15StateData2 +; wSprite01StateData2 - wSprite15StateData2 FOR n, 1, NUM_SPRITESTATEDATA_STRUCTS wSprite{02d:n}StateData2:: spritestatedata2 wSprite{02d:n}StateData2 ENDR From c299cf10675164502a7c487a29014b2927a403c0 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sat, 24 Sep 2022 11:39:39 -0400 Subject: [PATCH 11/26] Use a SPRITE_SET_LENGTH constant --- constants/sprite_set_constants.asm | 3 +++ data/maps/sprite_sets.asm | 3 +-- engine/overworld/map_sprites.asm | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/constants/sprite_set_constants.asm b/constants/sprite_set_constants.asm index 4f617c4a..56e4ccbb 100644 --- a/constants/sprite_set_constants.asm +++ b/constants/sprite_set_constants.asm @@ -36,3 +36,6 @@ DEF NUM_SPLIT_SETS EQU const_value - FIRST_SPLIT_SET ; split directions DEF EAST_WEST EQU 1 DEF NORTH_SOUTH EQU 2 + +; each sprite set has 9 walking sprites and 2 still sprites +DEF SPRITE_SET_LENGTH EQU 9 + 2 diff --git a/data/maps/sprite_sets.asm b/data/maps/sprite_sets.asm index 23274aa2..76ddee34 100644 --- a/data/maps/sprite_sets.asm +++ b/data/maps/sprite_sets.asm @@ -63,8 +63,7 @@ SplitMapSpriteSets: assert_table_length NUM_SPLIT_SETS SpriteSets: - ; each sprite set has 9 walking sprites and 2 still sprites - table_width 9 + 2, SpriteSets + table_width SPRITE_SET_LENGTH, SpriteSets ; SPRITESET_PALLET_VIRIDIAN db SPRITE_BLUE diff --git a/engine/overworld/map_sprites.asm b/engine/overworld/map_sprites.asm index 5a4c27c3..8acbb04f 100644 --- a/engine/overworld/map_sprites.asm +++ b/engine/overworld/map_sprites.asm @@ -283,7 +283,7 @@ InitOutsideMapSprites: sla a sla a add c - add b ; a = (spriteSetID - 1) * 11 + add b ; a = (spriteSetID - 1) * SPRITE_SET_LENGTH ld de, SpriteSets ; add a to de to get offset of sprite set add e @@ -309,7 +309,7 @@ InitOutsideMapSprites: inc de inc bc ld a, l - cp $bd ; reached 11th sprite slot? + cp 11 * SPRITESTATEDATA2_LENGTH + SPRITESTATEDATA2_PICTUREID ; reached 11th sprite slot? jr nz, .loadSpriteSetLoop ld b, 4 ; 4 remaining sprite slots .zeroRemainingSlotsLoop ; loop to zero the picture ID's of the remaining sprite slots @@ -322,13 +322,13 @@ InitOutsideMapSprites: jr nz, .zeroRemainingSlotsLoop ld a, [wNumSprites] push af ; save number of sprites - ld a, 11 ; 11 sprites in sprite set + ld a, SPRITE_SET_LENGTH ; 11 sprites in sprite set ld [wNumSprites], a call LoadMapSpriteTilePatterns pop af ld [wNumSprites], a ; restore number of sprites ld hl, wSprite01StateData2ImageBaseOffset - ld b, $0f + ld b, NUM_SPRITESTATEDATA_STRUCTS - 1 ; The VRAM tile pattern slots that LoadMapSpriteTilePatterns set are in the ; order of the map's sprite set, not the order of the actual sprites loaded ; for the current map. So, they are not needed and are zeroed by this loop. From 0f5a9eb1465c76426034a68caa2ff350d29716e0 Mon Sep 17 00:00:00 2001 From: BlueZangoose <113498143+BlueZangoose@users.noreply.github.com> Date: Sun, 25 Sep 2022 19:17:39 +0100 Subject: [PATCH 12/26] Change names of move subanimations to be meaningful (#389) Co-authored-by: Rangi --- Makefile | 4 +- constants/move_animation_constants.asm | 175 ++++--- data/battle_anims/subanimations.asm | 344 ++++++------ data/moves/animations.asm | 493 +++++++++--------- engine/battle/animations.asm | 31 +- engine/movie/splash.asm | 8 +- engine/overworld/cut.asm | 4 +- .../{attack_anim_1.png => move_anim_0.png} | Bin .../{attack_anim_2.png => move_anim_1.png} | Bin 9 files changed, 533 insertions(+), 526 deletions(-) rename gfx/battle/{attack_anim_1.png => move_anim_0.png} (100%) rename gfx/battle/{attack_anim_2.png => move_anim_1.png} (100%) diff --git a/Makefile b/Makefile index 636f5c3d..286b2b9d 100644 --- a/Makefile +++ b/Makefile @@ -153,8 +153,8 @@ pokeblue_vc_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "POKEMON BLUE" ### Misc file-specific graphics rules -gfx/battle/attack_anim_1.2bpp: tools/gfx += --trim-whitespace -gfx/battle/attack_anim_2.2bpp: tools/gfx += --trim-whitespace +gfx/battle/move_anim_0.2bpp: tools/gfx += --trim-whitespace +gfx/battle/move_anim_1.2bpp: tools/gfx += --trim-whitespace gfx/intro/blue_jigglypuff_1.2bpp: rgbgfx += -h gfx/intro/blue_jigglypuff_2.2bpp: rgbgfx += -h diff --git a/constants/move_animation_constants.asm b/constants/move_animation_constants.asm index 3d62cd19..2057541b 100644 --- a/constants/move_animation_constants.asm +++ b/constants/move_animation_constants.asm @@ -45,93 +45,96 @@ DEF FIRST_SE_ID EQU const_value ; subanimations that are part of move animations ; SubanimationPointers indexes (see data/battle_anims/subanimations.asm) + +; The 0 or 1 in the name of a subanim indicates whether to use tileset 0 or 1 data/moves/animations.asm. +; "Both" indicates either can be used for different images using the same animation. const_def - const SUBANIM_00 - const SUBANIM_01 - const SUBANIM_02 - const SUBANIM_03 - const SUBANIM_04 - const SUBANIM_05 - const SUBANIM_06 - const SUBANIM_07 - const SUBANIM_08 - const SUBANIM_09 - const SUBANIM_0A - const SUBANIM_0B - const SUBANIM_0C - const SUBANIM_0D - const SUBANIM_0E - const SUBANIM_0F - const SUBANIM_10 - const SUBANIM_11 - const SUBANIM_12 - const SUBANIM_13 - const SUBANIM_14 - const SUBANIM_15 - const SUBANIM_16 - const SUBANIM_17 - const SUBANIM_18 - const SUBANIM_19 - const SUBANIM_1A - const SUBANIM_1B - const SUBANIM_1C - const SUBANIM_1D - const SUBANIM_1E - const SUBANIM_1F - const SUBANIM_20 - const SUBANIM_21 - const SUBANIM_22 - const SUBANIM_23 - const SUBANIM_24 - const SUBANIM_25 - const SUBANIM_26 - const SUBANIM_27 - const SUBANIM_28 - const SUBANIM_29 - const SUBANIM_2A - const SUBANIM_2B - const SUBANIM_2C - const SUBANIM_2D - const SUBANIM_2E - const SUBANIM_2F - const SUBANIM_30 - const SUBANIM_31 - const SUBANIM_32 - const SUBANIM_33 - const SUBANIM_34 - const SUBANIM_35 - const SUBANIM_36 - const SUBANIM_37 - const SUBANIM_38 - const SUBANIM_39 - const SUBANIM_3A - const SUBANIM_3B - const SUBANIM_3C - const SUBANIM_3D - const SUBANIM_3E - const SUBANIM_3F - const SUBANIM_40 - const SUBANIM_41 - const SUBANIM_42 - const SUBANIM_43 - const SUBANIM_44 - const SUBANIM_45 - const SUBANIM_46 - const SUBANIM_47 - const SUBANIM_48 - const SUBANIM_49 - const SUBANIM_4A - const SUBANIM_4B - const SUBANIM_4C - const SUBANIM_4D - const SUBANIM_4E - const SUBANIM_4F - const SUBANIM_50 - const SUBANIM_51 - const SUBANIM_52 - const SUBANIM_53 - const SUBANIM_54 - const SUBANIM_55 + const SUBANIM_0_STAR + const SUBANIM_0_STAR_TWICE + const SUBANIM_0_STAR_THRICE + const SUBANIM_0_STAR_DESCENDING + const SUBANIM_1_STAR_BIG_MOVING + const SUBANIM_1_STAR_BIG + const SUBANIM_0_BALL_TOSS_HIGH + const SUBANIM_0_BALL_TOSS_MIDDLE + const SUBANIM_0_BALL_TOSS_LOW + const SUBANIM_0_BALL_SHAKE_ENEMY + const SUBANIM_0_BALL_POOF_ENEMY + const SUBANIM_0_BALL_BLOCK + const SUBANIM_1_FLAME_COLUMN_1 + const SUBANIM_1_FLAME_COLUMN_2 + const SUBANIM_1_FLAME_COLUMN_3 + const SUBANIM_0_SCRATCHES + const SUBANIM_1_TORNADO + const SUBANIM_1_FLAMES + const SUBANIM_0_HEART_1_MUSIC + const SUBANIM_1_BLOB_TOSS + const SUBANIM_1_BLOB_DRIP_ENEMY + const SUBANIM_1_SHOUT + const SUBANIM_0_SLICE + const SUBANIM_0_BIRDIES_CIRCLING_ENEMY + const SUBANIM_1_SWORDS_CIRCLING + const SUBANIM_1_CLOUD_TOSS + const SUBANIM_0_WATER_COLUMNS + const SUBANIM_1_SEED_TOSS + const SUBANIM_1_SEED_LAND + const SUBANIM_0_ROCKS_LIFT + const SUBANIM_0_ROCKS_TOSS + const SUBANIM_1_FLAME_BEAM + const SUBANIM_1_FLAME_STAR + const SUBANIM_0_CIRCLES_1_SQUARES_CENTERING_ENEMY + const SUBANIM_0_CIRCLE_1_SQUARE_TOSS_BACK + const SUBANIM_0_BIND + const SUBANIM_0_STATUS_PARALYZED + const SUBANIM_0_STATUS_CONFUSED + const SUBANIM_0_STATUS_CONFUSED_ENEMY + const SUBANIM_0_STATUS_POISONED + const SUBANIM_1_SAND + const SUBANIM_1_LIGHTNING_BALL + const SUBANIM_0_SLICE_BOTH_SIDES + const SUBANIM_1_LIGHTNING + const SUBANIM_0_WATER_DROPLETS + const SUBANIM_0_CIRCLES_CENTERING + const SUBANIM_0_BEAM + const SUBANIM_0_ICE_RISE + const SUBANIM_0_ROCKS_FALL_ENEMY + const SUBANIM_0_SOUND_WAVE + const SUBANIM_0_CIRCLE_1_SQUARE_HALF_TOSS ; unused + const SUBANIM_1_BARRIER + const SUBANIM_1_SELFDESTRUCT + const SUBANIM_0_WATER_BUBBLES + const SUBANIM_0_CIRCLES_FALLING + const SUBANIM_0_STRING_SHOT + const SUBANIM_0_ICE_FALL + const SUBANIM_0_CIRCLE_1_SQUARE_APPEARS ; unused + const SUBANIM_0_STATUS_SLEEP + const SUBANIM_0_STATUS_SLEEP_ENEMY + const SUBANIM_0_WATER_1_FIRE_BARRIER ; unused + const SUBANIM_0_WATER_1_FIRE_GEYSER ; unused + const SUBANIM_1_STAR_BIG_TOSS + const SUBANIM_1_STARS_SMALL_TOSS + const SUBANIM_1_MUSIC_CIRCLING_ENEMY + const SUBANIM_1_CIRCLE_BLACK_TOSS + const SUBANIM_1_EXPLOSION_SMALL_ENEMY + const SUBANIM_0_CIRCLE_1_SQUARE_CLOSING + const SUBANIM_1_LEAVES_TOSS + const SUBANIM_0_HORN_JAB_TWICE + const SUBANIM_0_HORN_JAB_THRICE + const SUBANIM_0_BALL_POOF + const SUBANIM_2_TRADE_BALL_DROP + const SUBANIM_2_TRADE_BALL_SHAKE + const SUBANIM_2_TRADE_BALL_APPEAR + const SUBANIM_2_TRADE_BALL_POOF + const SUBANIM_0_EGG_SHAKING + const SUBANIM_1_TRIANGLE_TOSS + const SUBANIM_1_SPHERE_BIG + const SUBANIM_1_SPHERE_BIG_RISE + const SUBANIM_1_SPHERE_BIG_FALL + const SUBANIM_0_SHELL + const SUBANIM_0_COIN_BOUNCE + const SUBANIM_0_SAFARI_ROCK + const SUBANIM_0_SAFARI_BAIT + const SUBANIM_0_STAR_HIGH DEF NUM_SUBANIMS EQU const_value ; types of subanimations diff --git a/data/battle_anims/subanimations.asm b/data/battle_anims/subanimations.asm index d67d4641..574047c5 100644 --- a/data/battle_anims/subanimations.asm +++ b/data/battle_anims/subanimations.asm @@ -1,91 +1,91 @@ SubanimationPointers: table_width 2, SubanimationPointers - dw Subanimation00 - dw Subanimation01 - dw Subanimation02 - dw Subanimation03 - dw Subanimation04 - dw Subanimation05 - dw Subanimation06 - dw Subanimation07 - dw Subanimation08 - dw Subanimation09 - dw Subanimation0a - dw Subanimation0b - dw Subanimation0c - dw Subanimation0d - dw Subanimation0e - dw Subanimation0f - dw Subanimation10 - dw Subanimation11 - dw Subanimation12 - dw Subanimation13 - dw Subanimation14 - dw Subanimation15 - dw Subanimation16 - dw Subanimation17 - dw Subanimation18 - dw Subanimation19 - dw Subanimation1a - dw Subanimation1b - dw Subanimation1c - dw Subanimation1d - dw Subanimation1e - dw Subanimation1f - dw Subanimation20 - dw Subanimation21 - dw Subanimation22 - dw Subanimation23 - dw Subanimation24 - dw Subanimation25 - dw Subanimation26 - dw Subanimation27 - dw Subanimation28 - dw Subanimation29 - dw Subanimation2a - dw Subanimation2b - dw Subanimation2c - dw Subanimation2d - dw Subanimation2e - dw Subanimation2f - dw Subanimation30 - dw Subanimation31 - dw Subanimation32 - dw Subanimation33 - dw Subanimation34 - dw Subanimation35 - dw Subanimation36 - dw Subanimation37 - dw Subanimation38 - dw Subanimation39 - dw Subanimation3a - dw Subanimation3b - dw Subanimation3c - dw Subanimation3d - dw Subanimation3e - dw Subanimation3f - dw Subanimation40 - dw Subanimation41 - dw Subanimation42 - dw Subanimation43 - dw Subanimation44 - dw Subanimation45 - dw Subanimation46 - dw Subanimation47 - dw Subanimation48 - dw Subanimation49 - dw Subanimation4a - dw Subanimation4b - dw Subanimation4c - dw Subanimation4d - dw Subanimation4e - dw Subanimation4f - dw Subanimation50 - dw Subanimation51 - dw Subanimation52 - dw Subanimation53 - dw Subanimation54 - dw Subanimation55 + dw Subanim_0Star + dw Subanim_0StarTwice + dw Subanim_0StarThrice + dw Subanim_0StarDescending + dw Subanim_1StarBigMoving + dw Subanim_1StarBig + dw Subanim_0BallTossHigh + dw Subanim_0BallTossMiddle + dw Subanim_0BallTossLow + dw Subanim_0BallShakeEnemy + dw Subanim_0BallPoofEnemy + dw Subanim_0BallBlock + dw Subanim_1FlameColumn1 + dw Subanim_1FlameColumn2 + dw Subanim_1FlameColumn3 + dw Subanim_0Scratches + dw Subanim_1Tornado + dw Subanim_1Flames + dw Subanim_0Heart_1Music + dw Subanim_1BlobToss + dw Subanim_1BlobDripEnemy + dw Subanim_1Shout + dw Subanim_0Slice + dw Subanim_0BirdiesCirclingEnemy + dw Subanim_1SwordsCircling + dw Subanim_1CloudToss + dw Subanim_0WaterColumns + dw Subanim_1SeedToss + dw Subanim_1SeedLand + dw Subanim_0RocksLift + dw Subanim_0RocksToss + dw Subanim_1FlameBeam + dw Subanim_1FlameStar + dw Subanim_0Circles_1Squares_CenteringEnemy + dw Subanim_0Circle_1Square_TossBack + dw Subanim_0Bind + dw Subanim_0StatusParalyzed + dw Subanim_0StatusConfused + dw Subanim_0StatusConfusedEnemy + dw Subanim_0StatusPoisoned + dw Subanim_1Sand + dw Subanim_1LightningBall + dw Subanim_0SliceBothSides + dw Subanim_1Lightning + dw Subanim_0WaterDroplets + dw Subanim_0CirclesCentering + dw Subanim_0Beam + dw Subanim_0IceRise + dw Subanim_0RocksFallEnemy + dw Subanim_0SoundWave + dw Subanim_0Circle_1Square_HalfToss ; unused + dw Subanim_1Barrier + dw Subanim_1Selfdestruct + dw Subanim_0WaterBubbles + dw Subanim_0CirclesFalling + dw Subanim_0StringShot + dw Subanim_0IceFall + dw Subanim_0Circle_1Square_Appears ; unused + dw Subanim_0StatusSleep + dw Subanim_0StatusSleepEnemy + dw Subanim_0Water_1Fire_Barrier ; unused + dw Subanim_0Water_1Fire_Geyser ; unused + dw Subanim_1StarBigToss + dw Subanim_1StarsSmallToss + dw Subanim_1MusicCirclingEnemy + dw Subanim_1CircleBlackToss + dw Subanim_1ExplosionSmallEnemy + dw Subanim_0Circle_1Square_Closing + dw Subanim_1LeavesToss + dw Subanim_0HornJabTwice + dw Subanim_0HornJabThrice + dw Subanim_0BallPoof + dw Subanim_2TradeBallDrop + dw Subanim_2TradeBallShake + dw Subanim_2TradeBallAppear + dw Subanim_2TradeBallPoof + dw Subanim_0EggShaking + dw Subanim_1TriangleToss + dw Subanim_1SphereBig + dw Subanim_1SphereBigRise + dw Subanim_1SphereBigFall + dw Subanim_0Shell + dw Subanim_0CoinBounce + dw Subanim_0SafariRock + dw Subanim_0SafariBait + dw Subanim_0StarHigh assert_table_length NUM_SUBANIMS ; format: @@ -98,17 +98,17 @@ MACRO subanim db (\1 << 5) | \2 ENDM -Subanimation04: +Subanim_1StarBigMoving: subanim SUBANIMTYPE_HFLIP, 3 db FRAMEBLOCK_02, BASECOORD_1A, FRAMEBLOCKMODE_00 db FRAMEBLOCK_02, BASECOORD_10, FRAMEBLOCKMODE_00 db FRAMEBLOCK_02, BASECOORD_03, FRAMEBLOCKMODE_00 -Subanimation05: +Subanim_1StarBig: subanim SUBANIMTYPE_HFLIP, 1 db FRAMEBLOCK_02, BASECOORD_10, FRAMEBLOCKMODE_00 -Subanimation08: +Subanim_0BallTossLow: subanim SUBANIMTYPE_NORMAL, 11 db FRAMEBLOCK_03, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_44, FRAMEBLOCKMODE_00 @@ -122,7 +122,7 @@ Subanimation08: db FRAMEBLOCK_03, BASECOORD_A1, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_34, FRAMEBLOCKMODE_00 -Subanimation07: +Subanim_0BallTossMiddle: subanim SUBANIMTYPE_NORMAL, 11 db FRAMEBLOCK_03, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_A2, FRAMEBLOCKMODE_00 @@ -136,7 +136,7 @@ Subanimation07: db FRAMEBLOCK_03, BASECOORD_A6, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_34, FRAMEBLOCKMODE_00 -Subanimation06: +Subanim_0BallTossHigh: subanim SUBANIMTYPE_NORMAL, 11 db FRAMEBLOCK_03, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_A2, FRAMEBLOCKMODE_00 @@ -150,14 +150,14 @@ Subanimation06: db FRAMEBLOCK_03, BASECOORD_A9, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_34, FRAMEBLOCKMODE_00 -Subanimation09: +Subanim_0BallShakeEnemy: subanim SUBANIMTYPE_NORMAL, 4 db FRAMEBLOCK_03, BASECOORD_21, FRAMEBLOCKMODE_04 db FRAMEBLOCK_04, BASECOORD_21, FRAMEBLOCKMODE_04 db FRAMEBLOCK_03, BASECOORD_21, FRAMEBLOCKMODE_04 db FRAMEBLOCK_05, BASECOORD_21, FRAMEBLOCKMODE_04 -Subanimation0a: +Subanim_0BallPoofEnemy: subanim SUBANIMTYPE_HFLIP, 6 db FRAMEBLOCK_06, BASECOORD_1B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_07, BASECOORD_1B, FRAMEBLOCKMODE_00 @@ -166,18 +166,18 @@ Subanimation0a: db FRAMEBLOCK_0A, BASECOORD_15, FRAMEBLOCKMODE_00 db FRAMEBLOCK_0A, BASECOORD_15, FRAMEBLOCKMODE_00 -Subanimation0b: +Subanim_0BallBlock: subanim SUBANIMTYPE_NORMAL, 4 db FRAMEBLOCK_01, BASECOORD_2D, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_2F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_35, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_4D, FRAMEBLOCKMODE_00 -Subanimation55: +Subanim_0StarHigh: subanim SUBANIMTYPE_HFLIP, 1 db FRAMEBLOCK_01, BASECOORD_9D, FRAMEBLOCKMODE_00 -Subanimation11: +Subanim_1Flames: subanim SUBANIMTYPE_HFLIP, 12 db FRAMEBLOCK_0B, BASECOORD_26, FRAMEBLOCKMODE_00 db FRAMEBLOCK_0C, BASECOORD_26, FRAMEBLOCKMODE_00 @@ -192,7 +192,7 @@ Subanimation11: db FRAMEBLOCK_0B, BASECOORD_27, FRAMEBLOCKMODE_00 db FRAMEBLOCK_0C, BASECOORD_27, FRAMEBLOCKMODE_00 -Subanimation2b: +Subanim_1Lightning: subanim SUBANIMTYPE_HFLIP, 11 db FRAMEBLOCK_0D, BASECOORD_03, FRAMEBLOCKMODE_03 db FRAMEBLOCK_0E, BASECOORD_03, FRAMEBLOCKMODE_03 @@ -206,7 +206,7 @@ Subanimation2b: db FRAMEBLOCK_11, BASECOORD_1B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_11, BASECOORD_1B, FRAMEBLOCKMODE_00 -Subanimation2c: +Subanim_0WaterDroplets: subanim SUBANIMTYPE_HFLIP, 12 db FRAMEBLOCK_12, BASECOORD_01, FRAMEBLOCKMODE_00 db FRAMEBLOCK_12, BASECOORD_0F, FRAMEBLOCKMODE_00 @@ -221,7 +221,7 @@ Subanimation2c: db FRAMEBLOCK_16, BASECOORD_38, FRAMEBLOCKMODE_00 db FRAMEBLOCK_16, BASECOORD_38, FRAMEBLOCKMODE_00 -Subanimation12: +Subanim_0Heart_1Music: subanim SUBANIMTYPE_COORDFLIP, 9 db FRAMEBLOCK_17, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_17, BASECOORD_39, FRAMEBLOCKMODE_00 @@ -233,35 +233,35 @@ Subanimation12: db FRAMEBLOCK_17, BASECOORD_3F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_17, BASECOORD_1F, FRAMEBLOCKMODE_00 -Subanimation00: +Subanim_0Star: subanim SUBANIMTYPE_HFLIP, 1 db FRAMEBLOCK_01, BASECOORD_17, FRAMEBLOCKMODE_00 -Subanimation01: +Subanim_0StarTwice: subanim SUBANIMTYPE_HFLIP, 2 db FRAMEBLOCK_01, BASECOORD_0F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_01, BASECOORD_1D, FRAMEBLOCKMODE_00 -Subanimation02: +Subanim_0StarThrice: subanim SUBANIMTYPE_HFLIP, 3 db FRAMEBLOCK_01, BASECOORD_12, FRAMEBLOCKMODE_00 db FRAMEBLOCK_01, BASECOORD_15, FRAMEBLOCKMODE_00 db FRAMEBLOCK_01, BASECOORD_1C, FRAMEBLOCKMODE_00 -Subanimation03: +Subanim_0StarDescending: subanim SUBANIMTYPE_HFLIP, 4 db FRAMEBLOCK_01, BASECOORD_0B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_01, BASECOORD_11, FRAMEBLOCKMODE_00 db FRAMEBLOCK_01, BASECOORD_18, FRAMEBLOCKMODE_00 db FRAMEBLOCK_01, BASECOORD_1D, FRAMEBLOCKMODE_00 -Subanimation0c: +Subanim_1FlameColumn1: subanim SUBANIMTYPE_HFLIP, 3 db FRAMEBLOCK_0C, BASECOORD_20, FRAMEBLOCKMODE_00 db FRAMEBLOCK_0C, BASECOORD_21, FRAMEBLOCKMODE_00 db FRAMEBLOCK_0C, BASECOORD_23, FRAMEBLOCKMODE_00 -Subanimation0d: +Subanim_1FlameColumn2: subanim SUBANIMTYPE_HFLIP, 6 db FRAMEBLOCK_0C, BASECOORD_20, FRAMEBLOCKMODE_02 db FRAMEBLOCK_0C, BASECOORD_15, FRAMEBLOCKMODE_00 @@ -270,7 +270,7 @@ Subanimation0d: db FRAMEBLOCK_0C, BASECOORD_23, FRAMEBLOCKMODE_02 db FRAMEBLOCK_0C, BASECOORD_19, FRAMEBLOCKMODE_00 -Subanimation0e: +Subanim_1FlameColumn3: subanim SUBANIMTYPE_HFLIP, 9 db FRAMEBLOCK_0C, BASECOORD_20, FRAMEBLOCKMODE_02 db FRAMEBLOCK_0C, BASECOORD_15, FRAMEBLOCKMODE_02 @@ -282,7 +282,7 @@ Subanimation0e: db FRAMEBLOCK_0C, BASECOORD_19, FRAMEBLOCKMODE_02 db FRAMEBLOCK_0C, BASECOORD_0C, FRAMEBLOCKMODE_00 -Subanimation1f: +Subanim_1FlameBeam: subanim SUBANIMTYPE_REVERSE, 5 db FRAMEBLOCK_0C, BASECOORD_30, FRAMEBLOCKMODE_03 db FRAMEBLOCK_0C, BASECOORD_40, FRAMEBLOCKMODE_03 @@ -290,7 +290,7 @@ Subanimation1f: db FRAMEBLOCK_0C, BASECOORD_42, FRAMEBLOCKMODE_03 db FRAMEBLOCK_0C, BASECOORD_21, FRAMEBLOCKMODE_00 -Subanimation2e: +Subanim_0Beam: subanim SUBANIMTYPE_HVFLIP, 14 db FRAMEBLOCK_18, BASECOORD_43, FRAMEBLOCKMODE_02 db FRAMEBLOCK_75, BASECOORD_52, FRAMEBLOCKMODE_04 @@ -307,14 +307,14 @@ Subanimation2e: db FRAMEBLOCK_1E, BASECOORD_43, FRAMEBLOCKMODE_02 db FRAMEBLOCK_75, BASECOORD_1B, FRAMEBLOCKMODE_00 -Subanimation2f: +Subanim_0IceRise: subanim SUBANIMTYPE_HFLIP, 4 db FRAMEBLOCK_1F, BASECOORD_24, FRAMEBLOCKMODE_00 db FRAMEBLOCK_20, BASECOORD_20, FRAMEBLOCKMODE_00 db FRAMEBLOCK_21, BASECOORD_1A, FRAMEBLOCKMODE_00 db FRAMEBLOCK_22, BASECOORD_15, FRAMEBLOCKMODE_00 -Subanimation30: +Subanim_0RocksFallEnemy: subanim SUBANIMTYPE_HFLIP, 18 db FRAMEBLOCK_23, BASECOORD_00, FRAMEBLOCKMODE_02 db FRAMEBLOCK_23, BASECOORD_02, FRAMEBLOCKMODE_02 @@ -335,7 +335,7 @@ Subanimation30: db FRAMEBLOCK_24, BASECOORD_28, FRAMEBLOCKMODE_00 db FRAMEBLOCK_24, BASECOORD_28, FRAMEBLOCKMODE_00 -Subanimation0f: +Subanim_0Scratches: subanim SUBANIMTYPE_HFLIP, 12 db FRAMEBLOCK_26, BASECOORD_0E, FRAMEBLOCKMODE_02 db FRAMEBLOCK_26, BASECOORD_16, FRAMEBLOCKMODE_02 @@ -350,7 +350,7 @@ Subanimation0f: db FRAMEBLOCK_29, BASECOORD_16, FRAMEBLOCKMODE_02 db FRAMEBLOCK_29, BASECOORD_1C, FRAMEBLOCKMODE_00 -Subanimation16: +Subanim_0Slice: subanim SUBANIMTYPE_HFLIP, 12 db FRAMEBLOCK_2A, BASECOORD_05, FRAMEBLOCKMODE_00 db FRAMEBLOCK_2B, BASECOORD_05, FRAMEBLOCKMODE_02 @@ -365,7 +365,7 @@ Subanimation16: db FRAMEBLOCK_2C, BASECOORD_00, FRAMEBLOCKMODE_02 db FRAMEBLOCK_2C, BASECOORD_00, FRAMEBLOCKMODE_00 -Subanimation10: +Subanim_1Tornado: subanim SUBANIMTYPE_REVERSE, 8 db FRAMEBLOCK_2D, BASECOORD_44, FRAMEBLOCKMODE_00 db FRAMEBLOCK_2E, BASECOORD_45, FRAMEBLOCKMODE_00 @@ -376,7 +376,7 @@ Subanimation10: db FRAMEBLOCK_2D, BASECOORD_2F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_2E, BASECOORD_1A, FRAMEBLOCKMODE_00 -Subanimation31: +Subanim_0SoundWave: subanim SUBANIMTYPE_HVFLIP, 10 db FRAMEBLOCK_2F, BASECOORD_46, FRAMEBLOCKMODE_00 db FRAMEBLOCK_2F, BASECOORD_4A, FRAMEBLOCKMODE_00 @@ -389,7 +389,7 @@ Subanimation31: db FRAMEBLOCK_2F, BASECOORD_2E, FRAMEBLOCKMODE_00 db FRAMEBLOCK_2F, BASECOORD_51, FRAMEBLOCKMODE_00 -Subanimation13: +Subanim_1BlobToss: subanim SUBANIMTYPE_REVERSE, 6 db FRAMEBLOCK_30, BASECOORD_31, FRAMEBLOCKMODE_00 db FRAMEBLOCK_30, BASECOORD_32, FRAMEBLOCKMODE_00 @@ -398,7 +398,7 @@ Subanimation13: db FRAMEBLOCK_30, BASECOORD_0F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_30, BASECOORD_10, FRAMEBLOCKMODE_00 -Subanimation14: +Subanim_1BlobDripEnemy: subanim SUBANIMTYPE_HFLIP, 9 db FRAMEBLOCK_30, BASECOORD_10, FRAMEBLOCKMODE_00 db FRAMEBLOCK_30, BASECOORD_10, FRAMEBLOCKMODE_03 @@ -410,7 +410,7 @@ Subanimation14: db FRAMEBLOCK_31, BASECOORD_22, FRAMEBLOCKMODE_04 db FRAMEBLOCK_31, BASECOORD_27, FRAMEBLOCKMODE_00 -Subanimation41: +Subanim_1CircleBlackToss: subanim SUBANIMTYPE_REVERSE, 5 db FRAMEBLOCK_03, BASECOORD_31, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_32, FRAMEBLOCKMODE_00 @@ -418,32 +418,32 @@ Subanimation41: db FRAMEBLOCK_03, BASECOORD_0E, FRAMEBLOCKMODE_00 db FRAMEBLOCK_03, BASECOORD_10, FRAMEBLOCKMODE_00 -Subanimation42: +Subanim_1ExplosionSmallEnemy: subanim SUBANIMTYPE_HFLIP, 3 db FRAMEBLOCK_SMALL_BLACK_CIRCLE, BASECOORD_08, FRAMEBLOCKMODE_00 db FRAMEBLOCK_LARGE_BLACK_CIRCLE, BASECOORD_08, FRAMEBLOCKMODE_00 db FRAMEBLOCK_5A, BASECOORD_08, FRAMEBLOCKMODE_00 -Subanimation15: +Subanim_1Shout: subanim SUBANIMTYPE_HVFLIP, 2 db FRAMEBLOCK_35, BASECOORD_52, FRAMEBLOCKMODE_00 db FRAMEBLOCK_35, BASECOORD_53, FRAMEBLOCKMODE_00 -Subanimation17: +Subanim_0BirdiesCirclingEnemy: subanim SUBANIMTYPE_HFLIP, 4 db FRAMEBLOCK_36, BASECOORD_54, FRAMEBLOCKMODE_00 db FRAMEBLOCK_36, BASECOORD_55, FRAMEBLOCKMODE_00 db FRAMEBLOCK_37, BASECOORD_56, FRAMEBLOCKMODE_00 db FRAMEBLOCK_37, BASECOORD_57, FRAMEBLOCKMODE_00 -Subanimation18: +Subanim_1SwordsCircling: subanim SUBANIMTYPE_ENEMY, 4 db FRAMEBLOCK_36, BASECOORD_54, FRAMEBLOCKMODE_00 db FRAMEBLOCK_36, BASECOORD_55, FRAMEBLOCKMODE_00 db FRAMEBLOCK_37, BASECOORD_56, FRAMEBLOCKMODE_00 db FRAMEBLOCK_37, BASECOORD_57, FRAMEBLOCKMODE_00 -Subanimation40: +Subanim_1MusicCirclingEnemy: subanim SUBANIMTYPE_HFLIP, 6 db FRAMEBLOCK_17, BASECOORD_54, FRAMEBLOCKMODE_00 db FRAMEBLOCK_17, BASECOORD_55, FRAMEBLOCKMODE_00 @@ -452,7 +452,7 @@ Subanimation40: db FRAMEBLOCK_17, BASECOORD_57, FRAMEBLOCKMODE_00 db FRAMEBLOCK_17, BASECOORD_13, FRAMEBLOCKMODE_00 -Subanimation19: +Subanim_1CloudToss: subanim SUBANIMTYPE_REVERSE, 12 db FRAMEBLOCK_38, BASECOORD_31, FRAMEBLOCKMODE_00 db FRAMEBLOCK_39, BASECOORD_31, FRAMEBLOCKMODE_00 @@ -467,7 +467,7 @@ Subanimation19: db FRAMEBLOCK_38, BASECOORD_10, FRAMEBLOCKMODE_00 db FRAMEBLOCK_39, BASECOORD_10, FRAMEBLOCKMODE_00 -Subanimation1a: +Subanim_0WaterColumns: subanim SUBANIMTYPE_HFLIP, 16 db FRAMEBLOCK_3A, BASECOORD_08, FRAMEBLOCKMODE_00 db FRAMEBLOCK_3B, BASECOORD_08, FRAMEBLOCKMODE_00 @@ -486,20 +486,20 @@ Subanimation1a: db FRAMEBLOCK_3E, BASECOORD_0B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_3F, BASECOORD_0B, FRAMEBLOCKMODE_00 -Subanimation1b: +Subanim_1SeedToss: subanim SUBANIMTYPE_REVERSE, 4 db FRAMEBLOCK_40, BASECOORD_31, FRAMEBLOCKMODE_00 db FRAMEBLOCK_40, BASECOORD_32, FRAMEBLOCKMODE_00 db FRAMEBLOCK_40, BASECOORD_92, FRAMEBLOCKMODE_00 db FRAMEBLOCK_40, BASECOORD_15, FRAMEBLOCKMODE_00 -Subanimation1c: +Subanim_1SeedLand: subanim SUBANIMTYPE_HFLIP, 3 db FRAMEBLOCK_41, BASECOORD_58, FRAMEBLOCKMODE_00 db FRAMEBLOCK_41, BASECOORD_59, FRAMEBLOCKMODE_00 db FRAMEBLOCK_41, BASECOORD_21, FRAMEBLOCKMODE_00 -Subanimation1d: +Subanim_0RocksLift: subanim SUBANIMTYPE_ENEMY, 15 db FRAMEBLOCK_24, BASECOORD_9A, FRAMEBLOCKMODE_00 db FRAMEBLOCK_23, BASECOORD_1B, FRAMEBLOCKMODE_02 @@ -517,22 +517,22 @@ Subanimation1d: db FRAMEBLOCK_25, BASECOORD_62, FRAMEBLOCKMODE_04 db FRAMEBLOCK_25, BASECOORD_99, FRAMEBLOCKMODE_03 -Subanimation1e: +Subanim_0RocksToss: subanim SUBANIMTYPE_NORMAL, 1 db FRAMEBLOCK_25, BASECOORD_75, FRAMEBLOCKMODE_00 -Subanimation20: +Subanim_1FlameStar: subanim SUBANIMTYPE_HFLIP, 2 db FRAMEBLOCK_42, BASECOORD_07, FRAMEBLOCKMODE_00 db FRAMEBLOCK_43, BASECOORD_07, FRAMEBLOCKMODE_00 -Subanimation21: +Subanim_0Circles_1Squares_CenteringEnemy: subanim SUBANIMTYPE_HFLIP, 3 db FRAMEBLOCK_44, BASECOORD_00, FRAMEBLOCKMODE_00 db FRAMEBLOCK_45, BASECOORD_08, FRAMEBLOCKMODE_00 db FRAMEBLOCK_46, BASECOORD_10, FRAMEBLOCKMODE_02 -Subanimation22: +Subanim_0Circle_1Square_TossBack: subanim SUBANIMTYPE_REVERSE, 11 db FRAMEBLOCK_47, BASECOORD_10, FRAMEBLOCKMODE_00 db FRAMEBLOCK_47, BASECOORD_56, FRAMEBLOCKMODE_00 @@ -546,7 +546,7 @@ Subanimation22: db FRAMEBLOCK_47, BASECOORD_89, FRAMEBLOCKMODE_00 db FRAMEBLOCK_47, BASECOORD_B0, FRAMEBLOCKMODE_00 -Subanimation2d: +Subanim_0CirclesCentering: subanim SUBANIMTYPE_COORDFLIP, 6 db FRAMEBLOCK_44, BASECOORD_64, FRAMEBLOCKMODE_00 db FRAMEBLOCK_45, BASECOORD_65, FRAMEBLOCKMODE_00 @@ -555,15 +555,15 @@ Subanimation2d: db FRAMEBLOCK_47, BASECOORD_66, FRAMEBLOCKMODE_00 db FRAMEBLOCK_47, BASECOORD_66, FRAMEBLOCKMODE_00 -Subanimation39: +Subanim_0Circle_1Square_Appears: subanim SUBANIMTYPE_COORDFLIP, 1 db FRAMEBLOCK_47, BASECOORD_67, FRAMEBLOCKMODE_00 -Subanimation4e: +Subanim_1SphereBig: subanim SUBANIMTYPE_HFLIP, 1 db FRAMEBLOCK_71, BASECOORD_0F, FRAMEBLOCKMODE_03 -Subanimation4f: +Subanim_1SphereBigRise: subanim SUBANIMTYPE_HFLIP, 7 db FRAMEBLOCK_71, BASECOORD_0F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_71, BASECOORD_08, FRAMEBLOCKMODE_00 @@ -573,7 +573,7 @@ Subanimation4f: db FRAMEBLOCK_73, BASECOORD_95, FRAMEBLOCKMODE_00 db FRAMEBLOCK_74, BASECOORD_95, FRAMEBLOCKMODE_00 -Subanimation50: +Subanim_1SphereBigFall: subanim SUBANIMTYPE_HFLIP, 8 db FRAMEBLOCK_74, BASECOORD_95, FRAMEBLOCKMODE_00 db FRAMEBLOCK_73, BASECOORD_95, FRAMEBLOCKMODE_00 @@ -584,7 +584,7 @@ Subanimation50: db FRAMEBLOCK_71, BASECOORD_0F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_71, BASECOORD_16, FRAMEBLOCKMODE_00 -Subanimation29: +Subanim_1LightningBall: subanim SUBANIMTYPE_HFLIP, 29 db FRAMEBLOCK_SMALL_BLACK_CIRCLE, BASECOORD_0F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_4A, BASECOORD_68, FRAMEBLOCKMODE_03 @@ -616,63 +616,63 @@ Subanimation29: db FRAMEBLOCK_4D, BASECOORD_2A, FRAMEBLOCKMODE_03 db FRAMEBLOCK_LARGE_BLACK_CIRCLE, BASECOORD_6B, FRAMEBLOCKMODE_00 -Subanimation2a: +Subanim_0SliceBothSides: subanim SUBANIMTYPE_HFLIP, 4 db FRAMEBLOCK_4E, BASECOORD_2B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_4F, BASECOORD_2B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_50, BASECOORD_2B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_50, BASECOORD_2B, FRAMEBLOCKMODE_00 -Subanimation23: +Subanim_0Bind: subanim SUBANIMTYPE_HFLIP, 2 db FRAMEBLOCK_51, BASECOORD_2D, FRAMEBLOCKMODE_00 db FRAMEBLOCK_51, BASECOORD_6E, FRAMEBLOCKMODE_00 -Subanimation24: +Subanim_0StatusParalyzed: subanim SUBANIMTYPE_ENEMY, 2 db FRAMEBLOCK_51, BASECOORD_2D, FRAMEBLOCKMODE_00 db FRAMEBLOCK_51, BASECOORD_6E, FRAMEBLOCKMODE_00 -Subanimation25: +Subanim_0StatusConfused: subanim SUBANIMTYPE_COORDFLIP, 2 db FRAMEBLOCK_52, BASECOORD_71, FRAMEBLOCKMODE_00 db FRAMEBLOCK_52, BASECOORD_72, FRAMEBLOCKMODE_00 -Subanimation26: +Subanim_0StatusConfusedEnemy: subanim SUBANIMTYPE_NORMAL, 2 db FRAMEBLOCK_52, BASECOORD_01, FRAMEBLOCKMODE_00 db FRAMEBLOCK_52, BASECOORD_2C, FRAMEBLOCKMODE_00 -Subanimation3a: +Subanim_0StatusSleep: subanim SUBANIMTYPE_COORDFLIP, 3 db FRAMEBLOCK_53, BASECOORD_71, FRAMEBLOCKMODE_00 db FRAMEBLOCK_53, BASECOORD_7F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_53, BASECOORD_81, FRAMEBLOCKMODE_00 -Subanimation3b: +Subanim_0StatusSleepEnemy: subanim SUBANIMTYPE_NORMAL, 3 db FRAMEBLOCK_53, BASECOORD_01, FRAMEBLOCKMODE_00 db FRAMEBLOCK_53, BASECOORD_15, FRAMEBLOCKMODE_00 db FRAMEBLOCK_53, BASECOORD_2C, FRAMEBLOCKMODE_00 -Subanimation27: +Subanim_0StatusPoisoned: subanim SUBANIMTYPE_ENEMY, 2 db FRAMEBLOCK_54, BASECOORD_01, FRAMEBLOCKMODE_00 db FRAMEBLOCK_54, BASECOORD_2C, FRAMEBLOCKMODE_00 -Subanimation28: +Subanim_1Sand: subanim SUBANIMTYPE_HVFLIP, 3 db FRAMEBLOCK_55, BASECOORD_73, FRAMEBLOCKMODE_03 db FRAMEBLOCK_56, BASECOORD_73, FRAMEBLOCKMODE_03 db FRAMEBLOCK_57, BASECOORD_73, FRAMEBLOCKMODE_00 -Subanimation32: +Subanim_0Circle_1Square_HalfToss: subanim SUBANIMTYPE_COORDFLIP, 3 db FRAMEBLOCK_47, BASECOORD_74, FRAMEBLOCKMODE_00 db FRAMEBLOCK_47, BASECOORD_43, FRAMEBLOCKMODE_00 db FRAMEBLOCK_47, BASECOORD_75, FRAMEBLOCKMODE_00 -Subanimation33: +Subanim_1Barrier: subanim SUBANIMTYPE_HVFLIP, 6 db FRAMEBLOCK_58, BASECOORD_76, FRAMEBLOCKMODE_00 db FRAMEBLOCK_34, BASECOORD_76, FRAMEBLOCKMODE_00 @@ -681,7 +681,7 @@ Subanimation33: db FRAMEBLOCK_58, BASECOORD_76, FRAMEBLOCKMODE_00 db FRAMEBLOCK_34, BASECOORD_76, FRAMEBLOCKMODE_00 -Subanimation3c: +Subanim_0Water_1Fire_Barrier: subanim SUBANIMTYPE_COORDFLIP, 7 db FRAMEBLOCK_59, BASECOORD_79, FRAMEBLOCKMODE_03 db FRAMEBLOCK_59, BASECOORD_7B, FRAMEBLOCKMODE_03 @@ -691,7 +691,7 @@ Subanimation3c: db FRAMEBLOCK_59, BASECOORD_7C, FRAMEBLOCKMODE_03 db FRAMEBLOCK_59, BASECOORD_76, FRAMEBLOCKMODE_00 -Subanimation3d: +Subanim_0Water_1Fire_Geyser: subanim SUBANIMTYPE_NORMAL, 8 db FRAMEBLOCK_3A, BASECOORD_4D, FRAMEBLOCKMODE_00 db FRAMEBLOCK_3B, BASECOORD_4D, FRAMEBLOCKMODE_00 @@ -702,7 +702,7 @@ Subanimation3d: db FRAMEBLOCK_3E, BASECOORD_4D, FRAMEBLOCKMODE_00 db FRAMEBLOCK_3F, BASECOORD_4D, FRAMEBLOCKMODE_00 -Subanimation34: +Subanim_1Selfdestruct: subanim SUBANIMTYPE_HVFLIP, 21 db FRAMEBLOCK_SMALL_BLACK_CIRCLE, BASECOORD_7D, FRAMEBLOCKMODE_00 db FRAMEBLOCK_LARGE_BLACK_CIRCLE, BASECOORD_7D, FRAMEBLOCKMODE_00 @@ -726,14 +726,14 @@ Subanimation34: db FRAMEBLOCK_LARGE_BLACK_CIRCLE, BASECOORD_82, FRAMEBLOCKMODE_00 db FRAMEBLOCK_5A, BASECOORD_82, FRAMEBLOCKMODE_00 -Subanimation35: +Subanim_0WaterBubbles: subanim SUBANIMTYPE_HVFLIP, 4 db FRAMEBLOCK_5B, BASECOORD_83, FRAMEBLOCKMODE_03 db FRAMEBLOCK_5C, BASECOORD_84, FRAMEBLOCKMODE_03 db FRAMEBLOCK_5D, BASECOORD_85, FRAMEBLOCKMODE_03 db FRAMEBLOCK_5E, BASECOORD_09, FRAMEBLOCKMODE_00 -Subanimation36: +Subanim_0CirclesFalling: subanim SUBANIMTYPE_HFLIP, 8 db FRAMEBLOCK_5F, BASECOORD_2A, FRAMEBLOCKMODE_00 db FRAMEBLOCK_5F, BASECOORD_00, FRAMEBLOCKMODE_00 @@ -744,7 +744,7 @@ Subanimation36: db FRAMEBLOCK_62, BASECOORD_2A, FRAMEBLOCKMODE_00 db FRAMEBLOCK_62, BASECOORD_00, FRAMEBLOCKMODE_00 -Subanimation37: +Subanim_0StringShot: subanim SUBANIMTYPE_HVFLIP, 10 db FRAMEBLOCK_63, BASECOORD_89, FRAMEBLOCKMODE_00 db FRAMEBLOCK_64, BASECOORD_75, FRAMEBLOCKMODE_00 @@ -757,7 +757,7 @@ Subanimation37: db FRAMEBLOCK_65, BASECOORD_88, FRAMEBLOCKMODE_00 db FRAMEBLOCK_65, BASECOORD_1A, FRAMEBLOCKMODE_00 -Subanimation38: +Subanim_0IceFall: subanim SUBANIMTYPE_HFLIP, 16 db FRAMEBLOCK_66, BASECOORD_8A, FRAMEBLOCKMODE_00 db FRAMEBLOCK_66, BASECOORD_33, FRAMEBLOCKMODE_00 @@ -776,7 +776,7 @@ Subanimation38: db FRAMEBLOCK_66, BASECOORD_1E, FRAMEBLOCKMODE_04 db FRAMEBLOCK_67, BASECOORD_29, FRAMEBLOCKMODE_00 -Subanimation3e: +Subanim_1StarBigToss: subanim SUBANIMTYPE_REVERSE, 18 db FRAMEBLOCK_02, BASECOORD_31, FRAMEBLOCKMODE_00 db FRAMEBLOCK_34, BASECOORD_31, FRAMEBLOCKMODE_00 @@ -797,7 +797,7 @@ Subanimation3e: db FRAMEBLOCK_34, BASECOORD_10, FRAMEBLOCKMODE_00 db FRAMEBLOCK_02, BASECOORD_10, FRAMEBLOCKMODE_00 -Subanimation3f: +Subanim_1StarsSmallToss: subanim SUBANIMTYPE_COORDFLIP, 18 db FRAMEBLOCK_68, BASECOORD_4B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_68, BASECOORD_8C, FRAMEBLOCKMODE_00 @@ -818,7 +818,7 @@ Subanimation3f: db FRAMEBLOCK_68, BASECOORD_23, FRAMEBLOCKMODE_00 db FRAMEBLOCK_68, BASECOORD_1F, FRAMEBLOCKMODE_00 -Subanimation44: +Subanim_1LeavesToss: subanim SUBANIMTYPE_HVFLIP, 12 db FRAMEBLOCK_69, BASECOORD_4B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_69, BASECOORD_8C, FRAMEBLOCKMODE_00 @@ -833,20 +833,20 @@ Subanimation44: db FRAMEBLOCK_69, BASECOORD_0C, FRAMEBLOCKMODE_00 db FRAMEBLOCK_69, BASECOORD_06, FRAMEBLOCKMODE_00 -Subanimation43: +Subanim_0Circle_1Square_Closing: subanim SUBANIMTYPE_ENEMY, 3 db FRAMEBLOCK_6A, BASECOORD_07, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6B, BASECOORD_0F, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6C, BASECOORD_17, FRAMEBLOCKMODE_00 -Subanimation45: +Subanim_0HornJabTwice: subanim SUBANIMTYPE_HVFLIP, 4 db FRAMEBLOCK_6D, BASECOORD_8B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6D, BASECOORD_84, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6D, BASECOORD_63, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6D, BASECOORD_8C, FRAMEBLOCKMODE_00 -Subanimation46: +Subanim_0HornJabThrice: subanim SUBANIMTYPE_HVFLIP, 6 db FRAMEBLOCK_6D, BASECOORD_8B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6D, BASECOORD_84, FRAMEBLOCKMODE_00 @@ -855,13 +855,13 @@ Subanimation46: db FRAMEBLOCK_6D, BASECOORD_0A, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6D, BASECOORD_89, FRAMEBLOCKMODE_00 -Subanimation47: +Subanim_0BallPoof: subanim SUBANIMTYPE_HVFLIP, 3 db FRAMEBLOCK_06, BASECOORD_82, FRAMEBLOCKMODE_00 db FRAMEBLOCK_07, BASECOORD_82, FRAMEBLOCKMODE_00 db FRAMEBLOCK_08, BASECOORD_96, FRAMEBLOCKMODE_00 -Subanimation48: +Subanim_2TradeBallDrop: subanim SUBANIMTYPE_NORMAL, 6 db FRAMEBLOCK_03, BASECOORD_41, FRAMEBLOCKMODE_04 db FRAMEBLOCK_03, BASECOORD_48, FRAMEBLOCKMODE_04 @@ -870,24 +870,24 @@ Subanimation48: db FRAMEBLOCK_05, BASECOORD_48, FRAMEBLOCKMODE_04 db FRAMEBLOCK_03, BASECOORD_48, FRAMEBLOCKMODE_03 -Subanimation49: +Subanim_2TradeBallShake: subanim SUBANIMTYPE_NORMAL, 4 db FRAMEBLOCK_04, BASECOORD_48, FRAMEBLOCKMODE_04 db FRAMEBLOCK_03, BASECOORD_48, FRAMEBLOCKMODE_04 db FRAMEBLOCK_05, BASECOORD_48, FRAMEBLOCKMODE_04 db FRAMEBLOCK_03, BASECOORD_48, FRAMEBLOCKMODE_03 -Subanimation4a: +Subanim_2TradeBallAppear: subanim SUBANIMTYPE_NORMAL, 1 db FRAMEBLOCK_04, BASECOORD_84, FRAMEBLOCKMODE_03 -Subanimation4b: +Subanim_2TradeBallPoof: subanim SUBANIMTYPE_NORMAL, 3 db FRAMEBLOCK_06, BASECOORD_72, FRAMEBLOCKMODE_00 db FRAMEBLOCK_07, BASECOORD_72, FRAMEBLOCKMODE_00 db FRAMEBLOCK_08, BASECOORD_72, FRAMEBLOCKMODE_00 -Subanimation4c: +Subanim_0EggShaking: subanim SUBANIMTYPE_COORDFLIP, 8 db FRAMEBLOCK_6F, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6E, BASECOORD_30, FRAMEBLOCKMODE_00 @@ -898,7 +898,7 @@ Subanimation4c: db FRAMEBLOCK_70, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_6E, BASECOORD_30, FRAMEBLOCKMODE_00 -Subanimation4d: +Subanim_1TriangleToss: subanim SUBANIMTYPE_HVFLIP, 6 db FRAMEBLOCK_32, BASECOORD_4B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_33, BASECOORD_4F, FRAMEBLOCKMODE_00 @@ -907,7 +907,7 @@ Subanimation4d: db FRAMEBLOCK_32, BASECOORD_19, FRAMEBLOCKMODE_00 db FRAMEBLOCK_33, BASECOORD_0D, FRAMEBLOCKMODE_00 -Subanimation51: +Subanim_0Shell: subanim SUBANIMTYPE_ENEMY, 6 db FRAMEBLOCK_76, BASECOORD_1B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_34, BASECOORD_1B, FRAMEBLOCKMODE_00 @@ -916,7 +916,7 @@ Subanimation51: db FRAMEBLOCK_76, BASECOORD_1B, FRAMEBLOCKMODE_00 db FRAMEBLOCK_34, BASECOORD_1B, FRAMEBLOCKMODE_00 -Subanimation52: +Subanim_0CoinBounce: subanim SUBANIMTYPE_HFLIP, 7 db FRAMEBLOCK_77, BASECOORD_25, FRAMEBLOCKMODE_00 db FRAMEBLOCK_77, BASECOORD_9B, FRAMEBLOCKMODE_00 @@ -926,7 +926,7 @@ Subanimation52: db FRAMEBLOCK_77, BASECOORD_50, FRAMEBLOCKMODE_00 db FRAMEBLOCK_77, BASECOORD_8C, FRAMEBLOCKMODE_00 -Subanimation53: +Subanim_0SafariRock: subanim SUBANIMTYPE_NORMAL, 12 db FRAMEBLOCK_78, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_78, BASECOORD_A2, FRAMEBLOCKMODE_00 @@ -941,7 +941,7 @@ Subanimation53: db FRAMEBLOCK_78, BASECOORD_34, FRAMEBLOCKMODE_00 db FRAMEBLOCK_01, BASECOORD_9E, FRAMEBLOCKMODE_00 -Subanimation54: +Subanim_0SafariBait: subanim SUBANIMTYPE_NORMAL, 11 db FRAMEBLOCK_79, BASECOORD_30, FRAMEBLOCKMODE_00 db FRAMEBLOCK_79, BASECOORD_A2, FRAMEBLOCKMODE_00 diff --git a/data/moves/animations.asm b/data/moves/animations.asm index e30e4379..0a8db8f5 100644 --- a/data/moves/animations.asm +++ b/data/moves/animations.asm @@ -225,161 +225,164 @@ MACRO battle_anim ENDC ENDM +; The 0 or 1 in the name of a subanim indicates whether to use tileset 0 or 1. +; "Both" indicates either can be used for different images using the same animation. + ZigZagScreenAnim: battle_anim NO_MOVE, SE_WAVY_SCREEN db -1 ; end PoundAnim: StruggleAnim: - battle_anim POUND, SUBANIM_01, 0, 8 + battle_anim POUND, SUBANIM_0_STAR_TWICE, 0, 8 db -1 ; end KarateChopAnim: - battle_anim KARATE_CHOP, SUBANIM_03, 0, 8 + battle_anim KARATE_CHOP, SUBANIM_0_STAR_DESCENDING, 0, 8 db -1 ; end DoubleSlapAnim: - battle_anim DOUBLESLAP, SUBANIM_01, 0, 5 - battle_anim DOUBLESLAP, SUBANIM_01, 0, 5 + battle_anim DOUBLESLAP, SUBANIM_0_STAR_TWICE, 0, 5 + battle_anim DOUBLESLAP, SUBANIM_0_STAR_TWICE, 0, 5 db -1 ; end CometPunchAnim: - battle_anim COMET_PUNCH, SUBANIM_02, 0, 4 - battle_anim COMET_PUNCH, SUBANIM_02, 0, 4 + battle_anim COMET_PUNCH, SUBANIM_0_STAR_THRICE, 0, 4 + battle_anim COMET_PUNCH, SUBANIM_0_STAR_THRICE, 0, 4 db -1 ; end MegaPunchAnim: - battle_anim MEGA_PUNCH, SUBANIM_04, 1, 6 + battle_anim MEGA_PUNCH, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end PayDayAnim: - battle_anim POUND, SUBANIM_01, 0, 8 - battle_anim PAY_DAY, SUBANIM_52, 0, 4 + battle_anim POUND, SUBANIM_0_STAR_TWICE, 0, 8 + battle_anim PAY_DAY, SUBANIM_0_COIN_BOUNCE, 0, 4 db -1 ; end FirePunchAnim: - battle_anim FIRE_PUNCH, SUBANIM_02, 0, 6 - battle_anim NO_MOVE, SUBANIM_11, 1, 6 + battle_anim FIRE_PUNCH, SUBANIM_0_STAR_THRICE, 0, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAMES, 1, 6 db -1 ; end IcePunchAnim: - battle_anim ICE_PUNCH, SUBANIM_02, 0, 6 - battle_anim NO_MOVE, SUBANIM_2F, 0, 16 + battle_anim ICE_PUNCH, SUBANIM_0_STAR_THRICE, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_ICE_RISE, 0, 16 db -1 ; end ThunderPunchAnim: - battle_anim THUNDERPUNCH, SUBANIM_02, 0, 6 + battle_anim THUNDERPUNCH, SUBANIM_0_STAR_THRICE, 0, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_2B, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_LIGHTNING, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end ScratchAnim: - battle_anim SCRATCH, SUBANIM_0F, 0, 6 + battle_anim SCRATCH, SUBANIM_0_SCRATCHES, 0, 6 db -1 ; end VicegripAnim: - battle_anim VICEGRIP, SUBANIM_2A, 0, 8 + battle_anim VICEGRIP, SUBANIM_0_SLICE_BOTH_SIDES, 0, 8 db -1 ; end GuillotineAnim: - battle_anim GUILLOTINE, SUBANIM_2A, 0, 6 + battle_anim GUILLOTINE, SUBANIM_0_SLICE_BOTH_SIDES, 0, 6 db -1 ; end RazorWindAnim: - battle_anim RAZOR_WIND, SUBANIM_16, 0, 4 + battle_anim RAZOR_WIND, SUBANIM_0_SLICE, 0, 4 db -1 ; end SwordsDanceAnim: - battle_anim SWORDS_DANCE, SUBANIM_18, 1, 6 - battle_anim SWORDS_DANCE, SUBANIM_18, 1, 6 - battle_anim SWORDS_DANCE, SUBANIM_18, 1, 6 + battle_anim SWORDS_DANCE, SUBANIM_1_SWORDS_CIRCLING, 1, 6 + battle_anim SWORDS_DANCE, SUBANIM_1_SWORDS_CIRCLING, 1, 6 + battle_anim SWORDS_DANCE, SUBANIM_1_SWORDS_CIRCLING, 1, 6 db -1 ; end CutAnim: battle_anim CUT, SE_DARK_SCREEN_FLASH - battle_anim NO_MOVE, SUBANIM_16, 0, 4 + battle_anim NO_MOVE, SUBANIM_0_SLICE, 0, 4 db -1 ; end GustAnim: - battle_anim GUST, SUBANIM_10, 1, 6 - battle_anim NO_MOVE, SUBANIM_02, 0, 6 + battle_anim GUST, SUBANIM_1_TORNADO, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_STAR_THRICE, 0, 6 db -1 ; end WingAttackAnim: - battle_anim WING_ATTACK, SUBANIM_04, 1, 6 + battle_anim WING_ATTACK, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end WhirlwindAnim: - battle_anim WHIRLWIND, SUBANIM_10, 1, 6 + battle_anim WHIRLWIND, SUBANIM_1_TORNADO, 1, 6 battle_anim NO_MOVE, SE_SLIDE_ENEMY_MON_OFF db -1 ; end FlyAnim: - battle_anim FLY, SUBANIM_04, 1, 6 + battle_anim FLY, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_SHOW_MON_PIC db -1 ; end BindAnim: - battle_anim BIND, SUBANIM_23, 0, 4 - battle_anim BIND, SUBANIM_23, 0, 4 + battle_anim BIND, SUBANIM_0_BIND, 0, 4 + battle_anim BIND, SUBANIM_0_BIND, 0, 4 db -1 ; end SlamAnim: - battle_anim SLAM, SUBANIM_02, 0, 6 + battle_anim SLAM, SUBANIM_0_STAR_THRICE, 0, 6 db -1 ; end VineWhipAnim: - battle_anim VINE_WHIP, SUBANIM_16, 0, 1 - battle_anim NO_MOVE, SUBANIM_01, 0, 8 + battle_anim VINE_WHIP, SUBANIM_0_SLICE, 0, 1 + battle_anim NO_MOVE, SUBANIM_0_STAR_TWICE, 0, 8 db -1 ; end StompAnim: - battle_anim STOMP, SUBANIM_05, 1, 8 + battle_anim STOMP, SUBANIM_1_STAR_BIG, 1, 8 db -1 ; end DoubleKickAnim: - battle_anim DOUBLE_KICK, SUBANIM_01, 0, 8 - battle_anim DOUBLE_KICK, SUBANIM_01, 0, 8 + battle_anim DOUBLE_KICK, SUBANIM_0_STAR_TWICE, 0, 8 + battle_anim DOUBLE_KICK, SUBANIM_0_STAR_TWICE, 0, 8 db -1 ; end MegaKickAnim: - battle_anim MEGA_KICK, SUBANIM_04, 1, 6 + battle_anim MEGA_KICK, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end JumpKickAnim: - battle_anim JUMP_KICK, SUBANIM_04, 1, 6 + battle_anim JUMP_KICK, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end RollingKickAnim: battle_anim ROLLING_KICK, SE_DARK_SCREEN_FLASH - battle_anim NO_MOVE, SUBANIM_04, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end SandAttackAnim: - battle_anim SAND_ATTACK, SUBANIM_28, 1, 6 + battle_anim SAND_ATTACK, SUBANIM_1_SAND, 1, 6 db -1 ; end HeadButtAnim: - battle_anim HEADBUTT, SUBANIM_05, 1, 6 + battle_anim HEADBUTT, SUBANIM_1_STAR_BIG, 1, 6 db -1 ; end HornAttackAnim: - battle_anim HORN_ATTACK, SUBANIM_45, 0, 6 - battle_anim NO_MOVE, SUBANIM_05, 1, 6 + battle_anim HORN_ATTACK, SUBANIM_0_HORN_JAB_TWICE, 0, 6 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG, 1, 6 db -1 ; end FuryAttackAnim: - battle_anim FURY_ATTACK, SUBANIM_46, 0, 2 - battle_anim NO_MOVE, SUBANIM_46, 0, 2 + battle_anim FURY_ATTACK, SUBANIM_0_HORN_JAB_THRICE, 0, 2 + battle_anim NO_MOVE, SUBANIM_0_HORN_JAB_THRICE, 0, 2 db -1 ; end HornDrillAnim: - battle_anim HORN_DRILL, SUBANIM_05, 1, 2 - battle_anim NO_MOVE, SUBANIM_05, 1, 2 - battle_anim NO_MOVE, SUBANIM_05, 1, 2 - battle_anim NO_MOVE, SUBANIM_05, 1, 2 - battle_anim NO_MOVE, SUBANIM_05, 1, 2 + battle_anim HORN_DRILL, SUBANIM_1_STAR_BIG, 1, 2 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG, 1, 2 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG, 1, 2 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG, 1, 2 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG, 1, 2 db -1 ; end TackleAnim: @@ -395,9 +398,9 @@ BodySlamAnim: db -1 ; end WrapAnim: - battle_anim WRAP, SUBANIM_23, 0, 4 - battle_anim WRAP, SUBANIM_23, 0, 4 - battle_anim WRAP, SUBANIM_23, 0, 4 + battle_anim WRAP, SUBANIM_0_BIND, 0, 4 + battle_anim WRAP, SUBANIM_0_BIND, 0, 4 + battle_anim WRAP, SUBANIM_0_BIND, 0, 4 db -1 ; end TakeDownAnim: @@ -407,12 +410,12 @@ TakeDownAnim: db -1 ; end ThrashAnim: - battle_anim THRASH, SUBANIM_04, 1, 6 + battle_anim THRASH, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end DoubleEdgeAnim: battle_anim LEECH_SEED, SE_LIGHT_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_2D, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLES_CENTERING, 0, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE battle_anim NO_MOVE, SE_MOVE_MON_HORIZONTALLY battle_anim DOUBLE_EDGE, SE_DARK_SCREEN_FLASH @@ -430,16 +433,16 @@ TailWhipAnim: db -1 ; end PoisonStingAnim: - battle_anim POISON_STING, SUBANIM_00, 0, 6 + battle_anim POISON_STING, SUBANIM_0_STAR, 0, 6 db -1 ; end TwineedleAnim: - battle_anim TWINEEDLE, SUBANIM_01, 0, 5 - battle_anim TWINEEDLE, SUBANIM_01, 0, 5 + battle_anim TWINEEDLE, SUBANIM_0_STAR_TWICE, 0, 5 + battle_anim TWINEEDLE, SUBANIM_0_STAR_TWICE, 0, 5 db -1 ; end PinMissileAnim: - battle_anim PIN_MISSILE, SUBANIM_01, 0, 3 + battle_anim PIN_MISSILE, SUBANIM_0_STAR_TWICE, 0, 3 db -1 ; end LeerAnim: @@ -450,34 +453,34 @@ LeerAnim: db -1 ; end BiteAnim: - battle_anim BITE, SUBANIM_02, 0, 8 + battle_anim BITE, SUBANIM_0_STAR_THRICE, 0, 8 db -1 ; end GrowlAnim: - battle_anim GROWL, SUBANIM_12, 1, 6 + battle_anim GROWL, SUBANIM_0_HEART_1_MUSIC, 1, 6 db -1 ; end RoarAnim: - battle_anim ROAR, SUBANIM_15, 1, 6 - battle_anim ROAR, SUBANIM_15, 1, 6 - battle_anim ROAR, SUBANIM_15, 1, 6 + battle_anim ROAR, SUBANIM_1_SHOUT, 1, 6 + battle_anim ROAR, SUBANIM_1_SHOUT, 1, 6 + battle_anim ROAR, SUBANIM_1_SHOUT, 1, 6 db -1 ; end SingAnim: - battle_anim SING, SUBANIM_12, 1, 6 - battle_anim NO_MOVE, SUBANIM_40, 1, 16 - battle_anim NO_MOVE, SUBANIM_40, 1, 16 + battle_anim SING, SUBANIM_0_HEART_1_MUSIC, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_MUSIC_CIRCLING_ENEMY, 1, 16 + battle_anim NO_MOVE, SUBANIM_1_MUSIC_CIRCLING_ENEMY, 1, 16 db -1 ; end SupersonicAnim: - battle_anim SUPERSONIC, SUBANIM_31, 0, 6 + battle_anim SUPERSONIC, SUBANIM_0_SOUND_WAVE, 0, 6 db -1 ; end SonicBoomAnim: - battle_anim ROAR, SUBANIM_15, 1, 6 - battle_anim ROAR, SUBANIM_15, 1, 6 - battle_anim GUST, SUBANIM_10, 1, 6 - battle_anim NO_MOVE, SUBANIM_05, 1, 6 + battle_anim ROAR, SUBANIM_1_SHOUT, 1, 6 + battle_anim ROAR, SUBANIM_1_SHOUT, 1, 6 + battle_anim GUST, SUBANIM_1_TORNADO, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG, 1, 6 db -1 ; end DisableAnim: @@ -488,18 +491,18 @@ DisableAnim: db -1 ; end AcidAnim: - battle_anim ACID, SUBANIM_13, 1, 6 - battle_anim ACID, SUBANIM_14, 1, 6 + battle_anim ACID, SUBANIM_1_BLOB_TOSS, 1, 6 + battle_anim ACID, SUBANIM_1_BLOB_DRIP_ENEMY, 1, 6 db -1 ; end EmberAnim: - battle_anim EMBER, SUBANIM_11, 1, 6 + battle_anim EMBER, SUBANIM_1_FLAMES, 1, 6 db -1 ; end FlamethrowerAnim: - battle_anim FLAMETHROWER, SUBANIM_1F, 1, 6 - battle_anim FLAMETHROWER, SUBANIM_0C, 1, 6 - battle_anim FLAMETHROWER, SUBANIM_0D, 1, 6 + battle_anim FLAMETHROWER, SUBANIM_1_FLAME_BEAM, 1, 6 + battle_anim FLAMETHROWER, SUBANIM_1_FLAME_COLUMN_1, 1, 6 + battle_anim FLAMETHROWER, SUBANIM_1_FLAME_COLUMN_2, 1, 6 db -1 ; end MistAnim: @@ -509,40 +512,40 @@ MistAnim: db -1 ; end WaterGunAnim: - battle_anim WATER_GUN, SUBANIM_2C, 0, 6 + battle_anim WATER_GUN, SUBANIM_0_WATER_DROPLETS, 0, 6 db -1 ; end HydroPumpAnim: - battle_anim HYDRO_PUMP, SUBANIM_1A, 0, 6 - battle_anim HYDRO_PUMP, SUBANIM_1A, 0, 6 + battle_anim HYDRO_PUMP, SUBANIM_0_WATER_COLUMNS, 0, 6 + battle_anim HYDRO_PUMP, SUBANIM_0_WATER_COLUMNS, 0, 6 db -1 ; end SurfAnim: battle_anim SURF, SE_WATER_DROPLETS_EVERYWHERE - battle_anim HYDRO_PUMP, SUBANIM_1A, 0, 6 + battle_anim HYDRO_PUMP, SUBANIM_0_WATER_COLUMNS, 0, 6 db -1 ; end IceBeamAnim: - battle_anim ICE_BEAM, SUBANIM_2E, 0, 3 - battle_anim NO_MOVE, SUBANIM_2F, 0, 16 + battle_anim ICE_BEAM, SUBANIM_0_BEAM, 0, 3 + battle_anim NO_MOVE, SUBANIM_0_ICE_RISE, 0, 16 db -1 ; end BlizzardAnim: - battle_anim BLIZZARD, SUBANIM_38, 0, 4 - battle_anim HYDRO_PUMP, SUBANIM_38, 0, 4 + battle_anim BLIZZARD, SUBANIM_0_ICE_FALL, 0, 4 + battle_anim HYDRO_PUMP, SUBANIM_0_ICE_FALL, 0, 4 db -1 ; end PsyBeamAnim: - battle_anim PSYBEAM, SUBANIM_2E, 0, 3 + battle_anim PSYBEAM, SUBANIM_0_BEAM, 0, 3 battle_anim NO_MOVE, SE_FLASH_SCREEN_LONG db -1 ; end BubbleBeamAnim: - battle_anim BUBBLEBEAM, SUBANIM_35, 0, 18 + battle_anim BUBBLEBEAM, SUBANIM_0_WATER_BUBBLES, 0, 18 db -1 ; end AuroraBeamAnim: - battle_anim AURORA_BEAM, SUBANIM_2E, 0, 3 + battle_anim AURORA_BEAM, SUBANIM_0_BEAM, 0, 3 battle_anim NO_MOVE, SE_DELAY_ANIMATION_10 battle_anim NO_MOVE, SE_DELAY_ANIMATION_10 db -1 ; end @@ -550,49 +553,49 @@ AuroraBeamAnim: HyperBeamAnim: battle_anim LEECH_SEED, SE_DARK_SCREEN_PALETTE battle_anim NO_MOVE, SE_SPIRAL_BALLS_INWARD - battle_anim HYPER_BEAM, SUBANIM_2E, 0, 2 + battle_anim HYPER_BEAM, SUBANIM_0_BEAM, 0, 2 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH - battle_anim MEGA_PUNCH, SUBANIM_04, 1, 6 + battle_anim MEGA_PUNCH, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end PeckAnim: - battle_anim PECK, SUBANIM_01, 0, 8 + battle_anim PECK, SUBANIM_0_STAR_TWICE, 0, 8 db -1 ; end DrillPeckAnim: - battle_anim DRILL_PECK, SUBANIM_04, 1, 6 + battle_anim DRILL_PECK, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end SubmissionAnim: battle_anim SUBMISSION, SE_SLIDE_MON_OFF - battle_anim NO_MOVE, SUBANIM_01, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_STAR_TWICE, 0, 6 battle_anim NO_MOVE, SE_SHOW_MON_PIC db -1 ; end LowKickAnim: battle_anim LOW_KICK, SE_SLIDE_MON_OFF - battle_anim NO_MOVE, SUBANIM_04, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_SHOW_MON_PIC db -1 ; end CounterAnim: battle_anim COUNTER, SE_SLIDE_MON_OFF - battle_anim NO_MOVE, SUBANIM_04, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_SHOW_MON_PIC db -1 ; end SeismicTossAnim: battle_anim NO_MOVE, SE_BLINK_ENEMY_MON - battle_anim BARRAGE, SUBANIM_4E, 1, 1 + battle_anim BARRAGE, SUBANIM_1_SPHERE_BIG, 1, 1 battle_anim NO_MOVE, SE_HIDE_ENEMY_MON_PIC battle_anim NO_MOVE, SE_SLIDE_MON_OFF - battle_anim SEISMIC_TOSS, SUBANIM_4F, 1, 2 + battle_anim SEISMIC_TOSS, SUBANIM_1_SPHERE_BIG_RISE, 1, 2 battle_anim NO_MOVE, SE_DELAY_ANIMATION_10 battle_anim NO_MOVE, SE_DELAY_ANIMATION_10 battle_anim NO_MOVE, SE_SHOW_MON_PIC - battle_anim SEISMIC_TOSS, SUBANIM_50, 1, 1 + battle_anim SEISMIC_TOSS, SUBANIM_1_SPHERE_BIG_FALL, 1, 1 battle_anim NO_MOVE, SE_SHOW_ENEMY_MON_PIC battle_anim NO_MOVE, SE_SHAKE_SCREEN db -1 ; end @@ -600,28 +603,28 @@ SeismicTossAnim: StrengthAnim: battle_anim LEECH_SEED, SE_MOVE_MON_HORIZONTALLY battle_anim NO_MOVE, SE_RESET_MON_POSITION - battle_anim FIRE_PUNCH, SUBANIM_04, 1, 6 + battle_anim FIRE_PUNCH, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end AbsorbAnim: battle_anim ABSORB, SE_LIGHT_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_21, 0, 6 - battle_anim NO_MOVE, SUBANIM_22, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLES_1_SQUARES_CENTERING_ENEMY, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_TOSS_BACK, 0, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end MegaDrainAnim: battle_anim MEGA_DRAIN, SE_LIGHT_SCREEN_PALETTE battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH - battle_anim NO_MOVE, SUBANIM_21, 0, 6 - battle_anim NO_MOVE, SUBANIM_22, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLES_1_SQUARES_CENTERING_ENEMY, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_TOSS_BACK, 0, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end LeechSeedAnim: - battle_anim LEECH_SEED, SUBANIM_1B, 1, 6 - battle_anim STUN_SPORE, SUBANIM_1C, 1, 21 + battle_anim LEECH_SEED, SUBANIM_1_SEED_TOSS, 1, 6 + battle_anim STUN_SPORE, SUBANIM_1_SEED_LAND, 1, 21 db -1 ; end GrowthAnim: @@ -632,25 +635,25 @@ GrowthAnim: RazorLeafAnim: battle_anim RAZOR_LEAF, SE_LEAVES_FALLING - battle_anim SWIFT, SUBANIM_44, 1, 1 - battle_anim RAZOR_WIND, SUBANIM_16, 0, 1 + battle_anim SWIFT, SUBANIM_1_LEAVES_TOSS, 1, 1 + battle_anim RAZOR_WIND, SUBANIM_0_SLICE, 0, 1 db -1 ; end SolarBeamAnim: - battle_anim SOLARBEAM, SUBANIM_2E, 0, 6 - battle_anim NO_MOVE, SUBANIM_01, 0, 6 + battle_anim SOLARBEAM, SUBANIM_0_BEAM, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_STAR_TWICE, 0, 6 db -1 ; end PoisonPowderAnim: - battle_anim POISONPOWDER, SUBANIM_36, 0, 6 + battle_anim POISONPOWDER, SUBANIM_0_CIRCLES_FALLING, 0, 6 db -1 ; end StunSporeAnim: - battle_anim STUN_SPORE, SUBANIM_36, 0, 6 + battle_anim STUN_SPORE, SUBANIM_0_CIRCLES_FALLING, 0, 6 db -1 ; end SleepPowderAnim: - battle_anim SLEEP_POWDER, SUBANIM_36, 0, 6 + battle_anim SLEEP_POWDER, SUBANIM_0_CIRCLES_FALLING, 0, 6 db -1 ; end PetalDanceAnim: @@ -660,48 +663,48 @@ PetalDanceAnim: db -1 ; end StringShotAnim: - battle_anim STRING_SHOT, SUBANIM_37, 0, 8 + battle_anim STRING_SHOT, SUBANIM_0_STRING_SHOT, 0, 8 db -1 ; end DragonRageAnim: - battle_anim DRAGON_RAGE, SUBANIM_1F, 1, 6 - battle_anim NO_MOVE, SUBANIM_0C, 1, 6 - battle_anim NO_MOVE, SUBANIM_0D, 1, 6 - battle_anim NO_MOVE, SUBANIM_0E, 1, 6 + battle_anim DRAGON_RAGE, SUBANIM_1_FLAME_BEAM, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_COLUMN_1, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_COLUMN_2, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_COLUMN_3, 1, 6 db -1 ; end FireSpinAnim: - battle_anim FIRE_SPIN, SUBANIM_0C, 1, 6 - battle_anim NO_MOVE, SUBANIM_0D, 1, 6 - battle_anim NO_MOVE, SUBANIM_0E, 1, 6 + battle_anim FIRE_SPIN, SUBANIM_1_FLAME_COLUMN_1, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_COLUMN_2, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_COLUMN_3, 1, 6 db -1 ; end ThunderShockAnim: - battle_anim THUNDERSHOCK, SUBANIM_29, 1, 2 + battle_anim THUNDERSHOCK, SUBANIM_1_LIGHTNING_BALL, 1, 2 db -1 ; end ThunderBoltAnim: - battle_anim THUNDERBOLT, SUBANIM_29, 1, 1 - battle_anim THUNDERBOLT, SUBANIM_29, 1, 1 + battle_anim THUNDERBOLT, SUBANIM_1_LIGHTNING_BALL, 1, 1 + battle_anim THUNDERBOLT, SUBANIM_1_LIGHTNING_BALL, 1, 1 db -1 ; end ThunderWaveAnim: - battle_anim THUNDER_WAVE, SUBANIM_29, 1, 2 - battle_anim NO_MOVE, SUBANIM_23, 0, 2 - battle_anim NO_MOVE, SUBANIM_23, 0, 4 + battle_anim THUNDER_WAVE, SUBANIM_1_LIGHTNING_BALL, 1, 2 + battle_anim NO_MOVE, SUBANIM_0_BIND, 0, 2 + battle_anim NO_MOVE, SUBANIM_0_BIND, 0, 4 db -1 ; end ThunderAnim: battle_anim THUNDER, SE_DARK_SCREEN_PALETTE battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH - battle_anim NO_MOVE, SUBANIM_2B, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_LIGHTNING, 1, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH - battle_anim THUNDERBOLT, SUBANIM_29, 1, 2 + battle_anim THUNDERBOLT, SUBANIM_1_LIGHTNING_BALL, 1, 2 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end RockThrowAnim: - battle_anim ROCK_THROW, SUBANIM_30, 0, 4 + battle_anim ROCK_THROW, SUBANIM_0_ROCKS_FALL_ENEMY, 0, 4 db -1 ; end EarthquakeAnim: @@ -717,13 +720,13 @@ FissureAnim: db -1 ; end DigAnim: - battle_anim DIG, SUBANIM_04, 1, 6 + battle_anim DIG, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_SLIDE_MON_UP db -1 ; end ToxicAnim: battle_anim SURF, SE_WATER_DROPLETS_EVERYWHERE - battle_anim TOXIC, SUBANIM_14, 1, 6 + battle_anim TOXIC, SUBANIM_1_BLOB_DRIP_ENEMY, 1, 6 db -1 ; end ConfusionAnim: @@ -741,7 +744,7 @@ HypnosisAnim: MeditateAnim: battle_anim MEDITATE, SE_LIGHT_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_43, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_CLOSING, 1, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end @@ -753,12 +756,12 @@ AgilityAnim: QuickAttackAnim: battle_anim QUICK_ATTACK, SE_SLIDE_MON_OFF - battle_anim NO_MOVE, SUBANIM_04, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_SHOW_MON_PIC db -1 ; end RageAnim: - battle_anim RAGE, SUBANIM_01, 0, 6 + battle_anim RAGE, SUBANIM_0_STAR_TWICE, 0, 6 db -1 ; end TeleportAnim: @@ -772,12 +775,12 @@ NightShadeAnim: db -1 ; end MimicAnim: - battle_anim MIMIC, SUBANIM_21, 1, 6 - battle_anim MIMIC, SUBANIM_22, 1, 6 + battle_anim MIMIC, SUBANIM_0_CIRCLES_1_SQUARES_CENTERING_ENEMY, 1, 6 + battle_anim MIMIC, SUBANIM_0_CIRCLE_1_SQUARE_TOSS_BACK, 1, 6 db -1 ; end ScreechAnim: - battle_anim SCREECH, SUBANIM_12, 1, 6 + battle_anim SCREECH, SUBANIM_0_HEART_1_MUSIC, 1, 6 db -1 ; end DoubleTeamAnim: @@ -789,7 +792,7 @@ DoubleTeamAnim: battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE battle_anim DOUBLE_TEAM, SE_SHAKE_BACK_AND_FORTH battle_anim NO_MOVE, SE_SHOW_MON_PIC - battle_anim BARRIER, SUBANIM_33, 1, 6 + battle_anim BARRIER, SUBANIM_1_BARRIER, 1, 6 db -1 ; end RecoverAnim: @@ -801,7 +804,7 @@ RecoverAnim: HardenAnim: battle_anim HARDEN, SE_LIGHT_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_43, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_CLOSING, 1, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end @@ -814,8 +817,8 @@ MinimizeAnim: db -1 ; end SmokeScreenAnim: - battle_anim SMOKESCREEN, SUBANIM_28, 1, 6 - battle_anim NO_MOVE, SUBANIM_0A, 0, 4 + battle_anim SMOKESCREEN, SUBANIM_1_SAND, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_BALL_POOF_ENEMY, 0, 4 battle_anim NO_MOVE, SE_DARKEN_MON_PALETTE battle_anim NO_MOVE, SE_DELAY_ANIMATION_10 battle_anim NO_MOVE, SE_DELAY_ANIMATION_10 @@ -833,34 +836,34 @@ SmokeScreenAnim: ConfuseRayAnim: battle_anim CONFUSE_RAY, SE_DARK_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_3E, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_STAR_BIG_TOSS, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end WithdrawAnim: battle_anim DEFENSE_CURL, SE_LIGHT_SCREEN_PALETTE battle_anim NO_MOVE, SE_SLIDE_MON_DOWN - battle_anim NO_MOVE, SUBANIM_51, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_SHELL, 0, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE battle_anim NO_MOVE, SE_SHOW_MON_PIC db -1 ; end DefenseCurlAnim: battle_anim DEFENSE_CURL, SE_LIGHT_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_43, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_CLOSING, 0, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end BarrierAnim: - battle_anim BARRIER, SUBANIM_33, 1, 6 - battle_anim BARRIER, SUBANIM_33, 1, 6 + battle_anim BARRIER, SUBANIM_1_BARRIER, 1, 6 + battle_anim BARRIER, SUBANIM_1_BARRIER, 1, 6 db -1 ; end LightScreenAnim: battle_anim NO_MOVE, SE_LIGHT_SCREEN_PALETTE - battle_anim LIGHT_SCREEN, SUBANIM_33, 1, 6 - battle_anim LIGHT_SCREEN, SUBANIM_33, 1, 6 + battle_anim LIGHT_SCREEN, SUBANIM_1_BARRIER, 1, 6 + battle_anim LIGHT_SCREEN, SUBANIM_1_BARRIER, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end @@ -872,8 +875,8 @@ HazeAnim: ReflectAnim: battle_anim NO_MOVE, SE_DARK_SCREEN_PALETTE - battle_anim REFLECT, SUBANIM_33, 1, 6 - battle_anim REFLECT, SUBANIM_33, 1, 6 + battle_anim REFLECT, SUBANIM_1_BARRIER, 1, 6 + battle_anim REFLECT, SUBANIM_1_BARRIER, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end @@ -882,7 +885,7 @@ FocusEnergyAnim: db -1 ; end BideAnim: - battle_anim BIDE, SUBANIM_04, 1, 6 + battle_anim BIDE, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end MetronomeAnim: @@ -896,88 +899,88 @@ MetronomeAnim: db -1 ; end MirrorMoveAnim: - battle_anim MIRROR_MOVE, SUBANIM_01, 0, 8 + battle_anim MIRROR_MOVE, SUBANIM_0_STAR_TWICE, 0, 8 db -1 ; end SelfdestructAnim: - battle_anim SELFDESTRUCT, SUBANIM_34, 1, 3 + battle_anim SELFDESTRUCT, SUBANIM_1_SELFDESTRUCT, 1, 3 db -1 ; end EggBombAnim: - battle_anim EGG_BOMB, SUBANIM_41, 1, 4 - battle_anim EGG_BOMB, SUBANIM_42, 1, 4 + battle_anim EGG_BOMB, SUBANIM_1_CIRCLE_BLACK_TOSS, 1, 4 + battle_anim EGG_BOMB, SUBANIM_1_EXPLOSION_SMALL_ENEMY, 1, 4 db -1 ; end LickAnim: - battle_anim SLUDGE, SUBANIM_14, 1, 6 + battle_anim SLUDGE, SUBANIM_1_BLOB_DRIP_ENEMY, 1, 6 db -1 ; end SmogAnim: battle_anim LEECH_SEED, SE_DARKEN_MON_PALETTE - battle_anim SMOG, SUBANIM_19, 1, 6 + battle_anim SMOG, SUBANIM_1_CLOUD_TOSS, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end SludgeAnim: - battle_anim SLUDGE, SUBANIM_13, 1, 6 - battle_anim SLUDGE, SUBANIM_14, 1, 6 + battle_anim SLUDGE, SUBANIM_1_BLOB_TOSS, 1, 6 + battle_anim SLUDGE, SUBANIM_1_BLOB_DRIP_ENEMY, 1, 6 db -1 ; end BoneClubAnim: - battle_anim BONE_CLUB, SUBANIM_02, 0, 8 + battle_anim BONE_CLUB, SUBANIM_0_STAR_THRICE, 0, 8 db -1 ; end FireBlastAnim: - battle_anim FIRE_BLAST, SUBANIM_1F, 1, 6 - battle_anim NO_MOVE, SUBANIM_20, 1, 6 - battle_anim NO_MOVE, SUBANIM_20, 1, 6 - battle_anim NO_MOVE, SUBANIM_0C, 1, 6 - battle_anim NO_MOVE, SUBANIM_0D, 1, 6 + battle_anim FIRE_BLAST, SUBANIM_1_FLAME_BEAM, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_STAR, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_STAR, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_COLUMN_1, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_FLAME_COLUMN_2, 1, 6 db -1 ; end WaterfallAnim: battle_anim LEECH_SEED, SE_SLIDE_MON_DOWN - battle_anim HYDRO_PUMP, SUBANIM_1A, 0, 6 - battle_anim NO_MOVE, SUBANIM_02, 0, 8 + battle_anim HYDRO_PUMP, SUBANIM_0_WATER_COLUMNS, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_STAR_THRICE, 0, 8 battle_anim NO_MOVE, SE_SLIDE_MON_UP db -1 ; end ClampAnim: - battle_anim CLAMP, SUBANIM_2A, 0, 8 - battle_anim CONSTRICT, SUBANIM_23, 0, 6 - battle_anim CONSTRICT, SUBANIM_23, 0, 6 + battle_anim CLAMP, SUBANIM_0_SLICE_BOTH_SIDES, 0, 8 + battle_anim CONSTRICT, SUBANIM_0_BIND, 0, 6 + battle_anim CONSTRICT, SUBANIM_0_BIND, 0, 6 db -1 ; end SwiftAnim: - battle_anim SWIFT, SUBANIM_3F, 1, 3 + battle_anim SWIFT, SUBANIM_1_STARS_SMALL_TOSS, 1, 3 db -1 ; end SkullBashAnim: - battle_anim SKULL_BASH, SUBANIM_05, 1, 6 + battle_anim SKULL_BASH, SUBANIM_1_STAR_BIG, 1, 6 db -1 ; end SpikeCannonAnim: - battle_anim SPIKE_CANNON, SUBANIM_04, 1, 4 + battle_anim SPIKE_CANNON, SUBANIM_1_STAR_BIG_MOVING, 1, 4 db -1 ; end ConstrictAnim: - battle_anim CONSTRICT, SUBANIM_23, 0, 6 - battle_anim CONSTRICT, SUBANIM_23, 0, 6 - battle_anim CONSTRICT, SUBANIM_23, 0, 6 + battle_anim CONSTRICT, SUBANIM_0_BIND, 0, 6 + battle_anim CONSTRICT, SUBANIM_0_BIND, 0, 6 + battle_anim CONSTRICT, SUBANIM_0_BIND, 0, 6 db -1 ; end AmnesiaAnim: - battle_anim AMNESIA, SUBANIM_25, 0, 8 - battle_anim AMNESIA, SUBANIM_25, 0, 8 + battle_anim AMNESIA, SUBANIM_0_STATUS_CONFUSED, 0, 8 + battle_anim AMNESIA, SUBANIM_0_STATUS_CONFUSED, 0, 8 db -1 ; end KinesisAnim: - battle_anim KINESIS, SUBANIM_01, 0, 8 + battle_anim KINESIS, SUBANIM_0_STAR_TWICE, 0, 8 db -1 ; end SoftboiledAnim: battle_anim LEECH_SEED, SE_SLIDE_MON_HALF_OFF - battle_anim SOFTBOILED, SUBANIM_4C, 0, 8 + battle_anim SOFTBOILED, SUBANIM_0_EGG_SHAKING, 0, 8 battle_anim NO_MOVE, SE_LIGHT_SCREEN_PALETTE battle_anim NO_MOVE, SE_SPIRAL_BALLS_INWARD battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE @@ -985,7 +988,7 @@ SoftboiledAnim: db -1 ; end HiJumpKickAnim: - battle_anim HI_JUMP_KICK, SUBANIM_04, 1, 6 + battle_anim HI_JUMP_KICK, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end GlareAnim: @@ -998,58 +1001,58 @@ GlareAnim: DreamEaterAnim: battle_anim DREAM_EATER, SE_FLASH_SCREEN_LONG battle_anim DREAM_EATER, SE_DARK_SCREEN_PALETTE - battle_anim DREAM_EATER, SUBANIM_02, 0, 8 + battle_anim DREAM_EATER, SUBANIM_0_STAR_THRICE, 0, 8 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end PoisonGasAnim: - battle_anim POISON_GAS, SUBANIM_19, 1, 6 + battle_anim POISON_GAS, SUBANIM_1_CLOUD_TOSS, 1, 6 db -1 ; end BarrageAnim: - battle_anim BARRAGE, SUBANIM_41, 1, 3 - battle_anim NO_MOVE, SUBANIM_55, 0, 5 + battle_anim BARRAGE, SUBANIM_1_CIRCLE_BLACK_TOSS, 1, 3 + battle_anim NO_MOVE, SUBANIM_0_STAR_HIGH, 0, 5 db -1 ; end LeechLifeAnim: - battle_anim LEECH_LIFE, SUBANIM_02, 0, 8 + battle_anim LEECH_LIFE, SUBANIM_0_STAR_THRICE, 0, 8 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH - battle_anim NO_MOVE, SUBANIM_21, 0, 6 - battle_anim NO_MOVE, SUBANIM_22, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLES_1_SQUARES_CENTERING_ENEMY, 0, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_TOSS_BACK, 0, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH db -1 ; end LovelyKissAnim: - battle_anim LOVELY_KISS, SUBANIM_12, 0, 6 + battle_anim LOVELY_KISS, SUBANIM_0_HEART_1_MUSIC, 0, 6 db -1 ; end SkyAttackAnim: battle_anim SKY_ATTACK, SE_SQUISH_MON_PIC battle_anim NO_MOVE, SE_SHOOT_BALLS_UPWARD - battle_anim HI_JUMP_KICK, SUBANIM_04, 1, 6 + battle_anim HI_JUMP_KICK, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_SHOW_MON_PIC db -1 ; end TransformAnim: - battle_anim TRANSFORM, SUBANIM_21, 1, 6 - battle_anim TRANSFORM, SUBANIM_22, 1, 4 - battle_anim NO_MOVE, SUBANIM_47, 0, 8 + battle_anim TRANSFORM, SUBANIM_0_CIRCLES_1_SQUARES_CENTERING_ENEMY, 1, 6 + battle_anim TRANSFORM, SUBANIM_0_CIRCLE_1_SQUARE_TOSS_BACK, 1, 4 + battle_anim NO_MOVE, SUBANIM_0_BALL_POOF, 0, 8 battle_anim NO_MOVE, SE_TRANSFORM_MON db -1 ; end BubbleAnim: - battle_anim BUBBLE, SUBANIM_35, 0, 22 + battle_anim BUBBLE, SUBANIM_0_WATER_BUBBLES, 0, 22 db -1 ; end DizzyPunchAnim: - battle_anim DIZZY_PUNCH, SUBANIM_17, 0, 6 - battle_anim DIZZY_PUNCH, SUBANIM_17, 0, 6 - battle_anim DIZZY_PUNCH, SUBANIM_17, 0, 6 - battle_anim DOUBLESLAP, SUBANIM_02, 0, 6 + battle_anim DIZZY_PUNCH, SUBANIM_0_BIRDIES_CIRCLING_ENEMY, 0, 6 + battle_anim DIZZY_PUNCH, SUBANIM_0_BIRDIES_CIRCLING_ENEMY, 0, 6 + battle_anim DIZZY_PUNCH, SUBANIM_0_BIRDIES_CIRCLING_ENEMY, 0, 6 + battle_anim DOUBLESLAP, SUBANIM_0_STAR_THRICE, 0, 6 db -1 ; end SporeAnim: - battle_anim SPORE, SUBANIM_36, 0, 6 + battle_anim SPORE, SUBANIM_0_CIRCLES_FALLING, 0, 6 db -1 ; end FlashAnim: @@ -1060,7 +1063,7 @@ FlashAnim: db -1 ; end PsywaveAnim: - battle_anim SUPERSONIC, SUBANIM_31, 0, 6 + battle_anim SUPERSONIC, SUBANIM_0_SOUND_WAVE, 0, 6 battle_anim CONFUSION, SE_WAVY_SCREEN db -1 ; end @@ -1073,91 +1076,91 @@ AcidArmorAnim: db -1 ; end CrabHammerAnim: - battle_anim CRABHAMMER, SUBANIM_05, 1, 6 - battle_anim NO_MOVE, SUBANIM_2A, 0, 6 + battle_anim CRABHAMMER, SUBANIM_1_STAR_BIG, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_SLICE_BOTH_SIDES, 0, 6 db -1 ; end ExplosionAnim: - battle_anim EXPLOSION, SUBANIM_34, 1, 3 + battle_anim EXPLOSION, SUBANIM_1_SELFDESTRUCT, 1, 3 db -1 ; end FurySwipesAnim: - battle_anim FURY_SWIPES, SUBANIM_0F, 0, 4 + battle_anim FURY_SWIPES, SUBANIM_0_SCRATCHES, 0, 4 db -1 ; end BonemerangAnim: - battle_anim BONEMERANG, SUBANIM_02, 0, 6 + battle_anim BONEMERANG, SUBANIM_0_STAR_THRICE, 0, 6 db -1 ; end RestAnim: - battle_anim REST, SUBANIM_3A, 0, 16 - battle_anim REST, SUBANIM_3A, 0, 16 + battle_anim REST, SUBANIM_0_STATUS_SLEEP, 0, 16 + battle_anim REST, SUBANIM_0_STATUS_SLEEP, 0, 16 db -1 ; end RockSlideAnim: - battle_anim ROCK_SLIDE, SUBANIM_1D, 0, 4 - battle_anim ROCK_SLIDE, SUBANIM_1E, 0, 3 - battle_anim HYPER_FANG, SUBANIM_04, 1, 6 + battle_anim ROCK_SLIDE, SUBANIM_0_ROCKS_LIFT, 0, 4 + battle_anim ROCK_SLIDE, SUBANIM_0_ROCKS_TOSS, 0, 3 + battle_anim HYPER_FANG, SUBANIM_1_STAR_BIG_MOVING, 1, 6 db -1 ; end HyperFangAnim: - battle_anim HYPER_FANG, SUBANIM_02, 0, 6 + battle_anim HYPER_FANG, SUBANIM_0_STAR_THRICE, 0, 6 db -1 ; end SharpenAnim: battle_anim SHARPEN, SE_LIGHT_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_43, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_CLOSING, 1, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end ConversionAnim: battle_anim CONVERSION, SE_DARK_SCREEN_FLASH - battle_anim NO_MOVE, SUBANIM_21, 1, 6 - battle_anim NO_MOVE, SUBANIM_22, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLES_1_SQUARES_CENTERING_ENEMY, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_TOSS_BACK, 1, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH db -1 ; end TriAttackAnim: battle_anim TRI_ATTACK, SE_DARK_SCREEN_FLASH - battle_anim NO_MOVE, SUBANIM_4D, 1, 6 + battle_anim NO_MOVE, SUBANIM_1_TRIANGLE_TOSS, 1, 6 battle_anim NO_MOVE, SE_DARK_SCREEN_FLASH db -1 ; end SuperFangAnim: battle_anim LEECH_SEED, SE_DARK_SCREEN_PALETTE - battle_anim SUPER_FANG, SUBANIM_04, 1, 6 + battle_anim SUPER_FANG, SUBANIM_1_STAR_BIG_MOVING, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end SlashAnim: - battle_anim SLASH, SUBANIM_0F, 0, 6 + battle_anim SLASH, SUBANIM_0_SCRATCHES, 0, 6 db -1 ; end SubstituteAnim: battle_anim SUBSTITUTE, SE_SLIDE_MON_OFF - battle_anim NO_MOVE, SUBANIM_47, 0, 8 + battle_anim NO_MOVE, SUBANIM_0_BALL_POOF, 0, 8 battle_anim NO_MOVE, SE_SUBSTITUTE_MON db -1 ; end BallTossAnim: - battle_anim NO_MOVE, SUBANIM_06, 0, 3 + battle_anim NO_MOVE, SUBANIM_0_BALL_TOSS_HIGH, 0, 3 db -1 ; end GreatTossAnim: - battle_anim NO_MOVE, SUBANIM_07, 0, 3 + battle_anim NO_MOVE, SUBANIM_0_BALL_TOSS_MIDDLE, 0, 3 db -1 ; end UltraTossAnim: - battle_anim NO_MOVE, SUBANIM_08, 0, 2 + battle_anim NO_MOVE, SUBANIM_0_BALL_TOSS_LOW, 0, 2 db -1 ; end BallShakeAnim: - battle_anim NO_MOVE, SUBANIM_09, 0, 4 + battle_anim NO_MOVE, SUBANIM_0_BALL_SHAKE_ENEMY, 0, 4 db -1 ; end BallPoofAnim: - battle_anim NO_MOVE, SUBANIM_0A, 0, 4 + battle_anim NO_MOVE, SUBANIM_0_BALL_POOF_ENEMY, 0, 4 db -1 ; end ShowPicAnim: @@ -1181,19 +1184,19 @@ EnemyHUDShakeAnim: db -1 ; end TradeBallDropAnim: - battle_anim NO_MOVE, SUBANIM_48, 2, 6 + battle_anim NO_MOVE, SUBANIM_2_TRADE_BALL_DROP, 2, 6 db -1 ; end TradeBallAppear1Anim: - battle_anim NO_MOVE, SUBANIM_49, 2, 4 + battle_anim NO_MOVE, SUBANIM_2_TRADE_BALL_SHAKE, 2, 4 db -1 ; end TradeBallAppear2Anim: - battle_anim NO_MOVE, SUBANIM_4A, 2, 6 + battle_anim NO_MOVE, SUBANIM_2_TRADE_BALL_APPEAR, 2, 6 db -1 ; end TradeBallPoofAnim: - battle_anim NO_MOVE, SUBANIM_4B, 2, 6 + battle_anim NO_MOVE, SUBANIM_2_TRADE_BALL_POOF, 2, 6 db -1 ; end XStatItemAnim: @@ -1204,7 +1207,7 @@ XStatItemAnim: ShrinkingSquareAnim: battle_anim NO_MOVE, SE_LIGHT_SCREEN_PALETTE - battle_anim NO_MOVE, SUBANIM_43, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_CLOSING, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end @@ -1216,7 +1219,7 @@ XStatItemBlackAnim: ShrinkingSquareBlackAnim: battle_anim NO_MOVE, SE_DARKEN_MON_PALETTE - battle_anim NO_MOVE, SUBANIM_43, 1, 6 + battle_anim NO_MOVE, SUBANIM_0_CIRCLE_1_SQUARE_CLOSING, 1, 6 battle_anim NO_MOVE, SE_RESET_SCREEN_PALETTE db -1 ; end @@ -1227,37 +1230,37 @@ UnusedAnim: db -1 ; end ParalyzeAnim: - battle_anim BIND, SUBANIM_24, 0, 4 - battle_anim BIND, SUBANIM_24, 0, 4 + battle_anim BIND, SUBANIM_0_STATUS_PARALYZED, 0, 4 + battle_anim BIND, SUBANIM_0_STATUS_PARALYZED, 0, 4 db -1 ; end PoisonAnim: - battle_anim BIND, SUBANIM_27, 0, 8 - battle_anim BIND, SUBANIM_27, 0, 8 + battle_anim BIND, SUBANIM_0_STATUS_POISONED, 0, 8 + battle_anim BIND, SUBANIM_0_STATUS_POISONED, 0, 8 db -1 ; end SleepPlayerAnim: - battle_anim REST, SUBANIM_3A, 0, 16 - battle_anim REST, SUBANIM_3A, 0, 16 + battle_anim REST, SUBANIM_0_STATUS_SLEEP, 0, 16 + battle_anim REST, SUBANIM_0_STATUS_SLEEP, 0, 16 db -1 ; end SleepEnemyAnim: - battle_anim REST, SUBANIM_3B, 0, 16 - battle_anim REST, SUBANIM_3B, 0, 16 + battle_anim REST, SUBANIM_0_STATUS_SLEEP_ENEMY, 0, 16 + battle_anim REST, SUBANIM_0_STATUS_SLEEP_ENEMY, 0, 16 db -1 ; end ConfusedPlayerAnim: - battle_anim AMNESIA, SUBANIM_25, 0, 8 - battle_anim AMNESIA, SUBANIM_25, 0, 8 + battle_anim AMNESIA, SUBANIM_0_STATUS_CONFUSED, 0, 8 + battle_anim AMNESIA, SUBANIM_0_STATUS_CONFUSED, 0, 8 db -1 ; end ConfusedEnemyAnim: - battle_anim AMNESIA, SUBANIM_26, 0, 8 - battle_anim AMNESIA, SUBANIM_26, 0, 8 + battle_anim AMNESIA, SUBANIM_0_STATUS_CONFUSED_ENEMY, 0, 8 + battle_anim AMNESIA, SUBANIM_0_STATUS_CONFUSED_ENEMY, 0, 8 db -1 ; end BallBlockAnim: - battle_anim NO_MOVE, SUBANIM_0B, 0, 3 + battle_anim NO_MOVE, SUBANIM_0_BALL_BLOCK, 0, 3 db -1 ; end FaintAnim: @@ -1269,9 +1272,9 @@ ShakeScreenAnim: db -1 ; end ThrowRockAnim: - battle_anim BARRAGE, SUBANIM_53, 0, 3 + battle_anim BARRAGE, SUBANIM_0_SAFARI_ROCK, 0, 3 db -1 ; end ThrowBaitAnim: - battle_anim BARRAGE, SUBANIM_54, 0, 3 + battle_anim BARRAGE, SUBANIM_0_SAFARI_BAIT, 0, 3 db -1 ; end diff --git a/engine/battle/animations.asm b/engine/battle/animations.asm index 789dcb8a..fcfec14d 100644 --- a/engine/battle/animations.asm +++ b/engine/battle/animations.asm @@ -249,7 +249,7 @@ PlayAnimation: push af ld a, [wAnimPalette] ldh [rOBP0], a - call LoadAnimationTileset + call LoadMoveAnimationTiles vc_hook Reduce_move_anim_flashing_Mega_Punch_Self_Destruct_Explosion call LoadSubanimation call PlaySubanimation @@ -352,11 +352,11 @@ GetSubanimationTransform2: ret ; loads tile patterns for battle animations -LoadAnimationTileset: +LoadMoveAnimationTiles: ld a, [wWhichBattleAnimTileset] add a add a - ld hl, AnimationTilesetPointers + ld hl, MoveAnimationTilesPointers ld e, a ld d, 0 add hl, de @@ -367,7 +367,7 @@ LoadAnimationTileset: ld a, [hl] ld d, a ; de = address of tileset ld hl, vSprites tile $31 - ld b, BANK(AnimationTileset1) ; ROM bank + ld b, BANK(MoveAnimationTiles0) ; ROM bank ld a, [wTempTilesetNumTiles] ld c, a ; number of tiles jp CopyVideoData ; load tileset @@ -378,17 +378,18 @@ MACRO anim_tileset db -1 ; padding ENDM -AnimationTilesetPointers: +MoveAnimationTilesPointers: ; number of tiles, gfx pointer - anim_tileset 79, AnimationTileset1 - anim_tileset 79, AnimationTileset2 - anim_tileset 64, AnimationTileset1 + anim_tileset 79, MoveAnimationTiles0 + anim_tileset 79, MoveAnimationTiles1 + anim_tileset 64, MoveAnimationTiles2 -AnimationTileset1: - INCBIN "gfx/battle/attack_anim_1.2bpp" +MoveAnimationTiles0: +MoveAnimationTiles2: + INCBIN "gfx/battle/move_anim_0.2bpp" -AnimationTileset2: - INCBIN "gfx/battle/attack_anim_2.2bpp" +MoveAnimationTiles1: + INCBIN "gfx/battle/move_anim_1.2bpp" SlotMachineTiles2: IF DEF(_RED) @@ -1114,7 +1115,7 @@ AnimationWaterDropletsEverywhere: ; in Surf/Mist/Toxic. xor a ld [wWhichBattleAnimTileset], a - call LoadAnimationTileset + call LoadMoveAnimationTiles ld d, 32 ld a, -16 ld [wBaseCoordX], a @@ -1634,7 +1635,7 @@ _AnimationShootBallsUpward: push bc xor a ld [wWhichBattleAnimTileset], a - call LoadAnimationTileset + call LoadMoveAnimationTiles pop bc ld d, $7a ; ball tile ld hl, wShadowOAM @@ -2074,7 +2075,7 @@ InitMultipleObjectsOAM: push bc push de ld [wWhichBattleAnimTileset], a - call LoadAnimationTileset + call LoadMoveAnimationTiles pop de pop bc xor a diff --git a/engine/movie/splash.asm b/engine/movie/splash.asm index 2373a0f9..6e483e99 100644 --- a/engine/movie/splash.asm +++ b/engine/movie/splash.asm @@ -3,13 +3,13 @@ LoadShootingStarGraphics: ldh [rOBP0], a ld a, $a4 ldh [rOBP1], a - ld de, AnimationTileset2 tile 3 ; star tile (top left quadrant) + ld de, MoveAnimationTiles1 tile 3 ; star tile (top left quadrant) ld hl, vChars1 tile $20 - lb bc, BANK(AnimationTileset2), 1 + lb bc, BANK(MoveAnimationTiles1), 1 call CopyVideoData - ld de, AnimationTileset2 tile 19 ; star tile (bottom left quadrant) + ld de, MoveAnimationTiles1 tile 19 ; star tile (bottom left quadrant) ld hl, vChars1 tile $21 - lb bc, BANK(AnimationTileset2), 1 + lb bc, BANK(MoveAnimationTiles1), 1 call CopyVideoData ld de, FallingStar ld hl, vChars1 tile $22 diff --git a/engine/overworld/cut.asm b/engine/overworld/cut.asm index 4a337e8d..8ae9ed65 100644 --- a/engine/overworld/cut.asm +++ b/engine/overworld/cut.asm @@ -112,8 +112,8 @@ InitCutAnimOAM: ret LoadCutGrassAnimationTilePattern: - ld de, AnimationTileset2 tile 6 ; tile depicting a leaf - lb bc, BANK(AnimationTileset2), 1 + ld de, MoveAnimationTiles1 tile 6 ; tile depicting a leaf + lb bc, BANK(MoveAnimationTiles1), 1 jp CopyVideoData WriteCutOrBoulderDustAnimationOAMBlock: diff --git a/gfx/battle/attack_anim_1.png b/gfx/battle/move_anim_0.png similarity index 100% rename from gfx/battle/attack_anim_1.png rename to gfx/battle/move_anim_0.png diff --git a/gfx/battle/attack_anim_2.png b/gfx/battle/move_anim_1.png similarity index 100% rename from gfx/battle/attack_anim_2.png rename to gfx/battle/move_anim_1.png From 3e72d0834efd0a02383fa4fd960ed8e563b0e8c4 Mon Sep 17 00:00:00 2001 From: Vortyne <104168801+Vortyne@users.noreply.github.com> Date: Mon, 26 Sep 2022 11:12:43 -0400 Subject: [PATCH 13/26] Bug: `SUBANIMTYPE_COORDFLIP` should be `SUBANIMTYPE_HVFLIP` (#394) Co-authored-by: Rangi <35663410+Rangi42@users.noreply.github.com> --- data/battle_anims/subanimations.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/battle_anims/subanimations.asm b/data/battle_anims/subanimations.asm index 574047c5..74097e22 100644 --- a/data/battle_anims/subanimations.asm +++ b/data/battle_anims/subanimations.asm @@ -547,7 +547,7 @@ Subanim_0Circle_1Square_TossBack: db FRAMEBLOCK_47, BASECOORD_B0, FRAMEBLOCKMODE_00 Subanim_0CirclesCentering: - subanim SUBANIMTYPE_COORDFLIP, 6 + subanim SUBANIMTYPE_COORDFLIP, 6 ; should be SUBANIMTYPE_HVFLIP db FRAMEBLOCK_44, BASECOORD_64, FRAMEBLOCKMODE_00 db FRAMEBLOCK_45, BASECOORD_65, FRAMEBLOCKMODE_00 db FRAMEBLOCK_46, BASECOORD_66, FRAMEBLOCKMODE_00 From 742dd00eb735b3e548bc35f70f23d43ae91ad215 Mon Sep 17 00:00:00 2001 From: Vortyne <104168801+Vortyne@users.noreply.github.com> Date: Mon, 26 Sep 2022 14:53:16 -0400 Subject: [PATCH 14/26] Bug: Sprites can receive the wrong movement byte (#395) Co-authored-by: Rangi <35663410+Rangi42@users.noreply.github.com> --- engine/overworld/movement.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/overworld/movement.asm b/engine/overworld/movement.asm index e08da61a..20de17a1 100644 --- a/engine/overworld/movement.asm +++ b/engine/overworld/movement.asm @@ -117,7 +117,7 @@ UpdateNPCSprite: dec a add a ld hl, wMapSpriteData - add l + add l ; should be adc ld l, a ld a, [hl] ; read movement byte 2 ld [wCurSpriteMovement2], a From 22859c4bb70dba17994c9b47b07f657ea082875d Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Mon, 26 Sep 2022 15:17:26 -0400 Subject: [PATCH 15/26] Revert PR #395 --- engine/overworld/movement.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/overworld/movement.asm b/engine/overworld/movement.asm index 20de17a1..e08da61a 100644 --- a/engine/overworld/movement.asm +++ b/engine/overworld/movement.asm @@ -117,7 +117,7 @@ UpdateNPCSprite: dec a add a ld hl, wMapSpriteData - add l ; should be adc + add l ld l, a ld a, [hl] ; read movement byte 2 ld [wCurSpriteMovement2], a From 2b52ceb718b55dce038db24d177715ae4281d065 Mon Sep 17 00:00:00 2001 From: Rangi Date: Fri, 30 Sep 2022 16:57:47 -0400 Subject: [PATCH 16/26] `startswith` works with a tuple --- tools/free_space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/free_space.py b/tools/free_space.py index 3bf7433e..553e1352 100755 --- a/tools/free_space.py +++ b/tools/free_space.py @@ -28,7 +28,7 @@ def main(): if print_bank not in {'all', 'none'}: try: print_bank = (int(print_bank[2:], 16) - if print_bank.startswith('0x') or print_bank.startswith('0X') + if print_bank.startswith(('0x', '0X')) else int(print_bank)) except ValueError: error = f'Error: invalid BANK: {print_bank}' From f58dbe3849ecb6c47a703c674458fd63436db946 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 2 Oct 2022 21:14:23 -0400 Subject: [PATCH 17/26] Require rgbds 0.6.0 --- .github/workflows/main.yml | 2 +- INSTALL.md | 22 ++--- Makefile | 22 ++--- audio.asm | 3 - home.asm | 3 - constants.asm => includes.asm | 15 ++- macros.asm | 14 --- main.asm | 3 - maps.asm | 3 - ram.asm | 2 - rgbdscheck.asm | 12 +-- text.asm | 3 - tools/free_space.awk | 2 +- tools/free_space.py | 69 -------------- tools/mapreader.py | 173 ---------------------------------- tools/used_space.py | 64 ------------- vc/pokeblue.constants.asm | 2 - vc/pokered.constants.asm | 2 - 18 files changed, 43 insertions(+), 373 deletions(-) rename constants.asm => includes.asm (78%) delete mode 100644 macros.asm delete mode 100755 tools/free_space.py delete mode 100644 tools/mapreader.py delete mode 100755 tools/used_space.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 13cc4e5f..805aea49 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@master with: path: rgbds - ref: v0.5.2 + ref: v0.6.0 repository: gbdev/rgbds - name: Install rgbds diff --git a/INSTALL.md b/INSTALL.md index 95ce870e..c8965f40 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -42,9 +42,9 @@ Run setup and leave the default settings. At the "**Select Packages**" step, cho Double click on the text that says "**Skip**" next to each package to select the most recent version to install. -Then follow the [**rgbds** install instructions](https://rgbds.gbdev.io/install#pre-built) for Windows with Cygwin to install **rgbds 0.5.2**. +Then follow the [**rgbds** install instructions](https://rgbds.gbdev.io/install#pre-built) for Windows with Cygwin to install **rgbds 0.6.0**. -**Note:** If you already have an older rgbds, you will need to update to 0.5.2. Ignore this if you have never installed rgbds before. If a version newer than 0.5.2 does not work, try downloading 0.5.2. +**Note:** If you already have an older rgbds, you will need to update to 0.6.0. Ignore this if you have never installed rgbds before. If a version newer than 0.6.0 does not work, try downloading 0.6.0. Now open the **Cygwin terminal** and enter the following commands. @@ -67,7 +67,7 @@ Install [**Homebrew**](https://brew.sh/). Follow the official instructions. Open **Terminal** and prepare to enter commands. -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for macOS to install **rgbds 0.5.2**. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for macOS to install **rgbds 0.6.0**. Now you're ready to [build **pokered**](#build-pokered). @@ -84,7 +84,7 @@ To install the software required for **pokered**: sudo apt-get install make gcc git ``` -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.5.2** from source. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. ### OpenSUSE @@ -94,7 +94,7 @@ To install the software required for **pokered**: sudo zypper install make gcc git ``` -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.5.2** from source. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. ### Arch Linux @@ -104,9 +104,9 @@ To install the software required for **pokered**: sudo pacman -S make gcc git ``` -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for Arch Linux to install **rgbds 0.5.2**. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for Arch Linux to install **rgbds 0.6.0**. -If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.5.2** from source. +If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. ### Termux @@ -122,7 +122,7 @@ To install **rgbds**: sudo apt install rgbds ``` -If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.5.2** from source. +If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. ### Other distros @@ -133,7 +133,7 @@ If your distro is not listed here, try to find the required software in its repo - `git` - `rgbds` -If `rgbds` is not available, you'll need to follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.5.2** from source. +If `rgbds` is not available, you'll need to follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. Now you're ready to [build **pokered**](#build-pokered). @@ -155,8 +155,8 @@ make ### Build with a local rgbds version -If you have different projects that require different versions of `rgbds`, it might not be convenient to install rgbds 0.5.2 globally. Instead, you can put its files in a directory within pokered, such as `pokered/rgbds-0.5.2/`. Then specify it when you run `make`: +If you have different projects that require different versions of `rgbds`, it might not be convenient to install rgbds 0.6.0 globally. Instead, you can put its files in a directory within pokered, such as `pokered/rgbds-0.6.0/`. Then specify it when you run `make`: ```bash -make RGBDS=rgbds-0.5.2/ +make RGBDS=rgbds-0.6.0/ ``` diff --git a/Makefile b/Makefile index 286b2b9d..39176c78 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ tools: $(MAKE) -C tools/ -RGBASMFLAGS = -h -L -Weverything -Wnumeric-string=2 -Wtruncation=1 +RGBASMFLAGS = -hL -Q8 -P includes.asm -Weverything -Wnumeric-string=2 -Wtruncation=1 # Create a sym/map for debug purposes if `make` run with `DEBUG=1` ifeq ($(DEBUG),1) RGBASMFLAGS += -E @@ -107,7 +107,7 @@ rgbdscheck.o: rgbdscheck.asm # As a side effect, they're evaluated immediately instead of when the rule is invoked. # It doesn't look like $(shell) can be deferred so there might not be a better way. define DEP -$1: $2 $$(shell tools/scan_includes $2) | rgbdscheck.o +$1: $2 $$(shell tools/scan_includes $2) | includes.asm rgbdscheck.o $$(RGBASM) $$(RGBASMFLAGS) -o $$@ $$< endef @@ -125,8 +125,8 @@ $(foreach obj, $(pokered_vc_obj), $(eval $(call DEP,$(obj),$(obj:_red_vc.o=.asm) $(foreach obj, $(pokeblue_vc_obj), $(eval $(call DEP,$(obj),$(obj:_blue_vc.o=.asm)))) # Dependencies for VC files that need to run scan_includes -%.constants.sym: %.constants.asm $(shell tools/scan_includes %.constants.asm) | rgbdscheck.o - $(RGBASM) $< > $@ +%.constants.sym: %.constants.asm $(shell tools/scan_includes %.constants.asm) | includes.asm rgbdscheck.o + $(RGBASM) $(RGBASMFLAGS) $< > $@ endif @@ -156,13 +156,13 @@ pokeblue_vc_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "POKEMON BLUE" gfx/battle/move_anim_0.2bpp: tools/gfx += --trim-whitespace gfx/battle/move_anim_1.2bpp: tools/gfx += --trim-whitespace -gfx/intro/blue_jigglypuff_1.2bpp: rgbgfx += -h -gfx/intro/blue_jigglypuff_2.2bpp: rgbgfx += -h -gfx/intro/blue_jigglypuff_3.2bpp: rgbgfx += -h -gfx/intro/red_nidorino_1.2bpp: rgbgfx += -h -gfx/intro/red_nidorino_2.2bpp: rgbgfx += -h -gfx/intro/red_nidorino_3.2bpp: rgbgfx += -h -gfx/intro/gengar.2bpp: rgbgfx += -h +gfx/intro/blue_jigglypuff_1.2bpp: rgbgfx += -Z +gfx/intro/blue_jigglypuff_2.2bpp: rgbgfx += -Z +gfx/intro/blue_jigglypuff_3.2bpp: rgbgfx += -Z +gfx/intro/red_nidorino_1.2bpp: rgbgfx += -Z +gfx/intro/red_nidorino_2.2bpp: rgbgfx += -Z +gfx/intro/red_nidorino_3.2bpp: rgbgfx += -Z +gfx/intro/gengar.2bpp: rgbgfx += -Z gfx/intro/gengar.2bpp: tools/gfx += --remove-duplicates --preserve=0x19,0x76 gfx/credits/the_end.2bpp: tools/gfx += --interleave --png=$< diff --git a/audio.asm b/audio.asm index e934c440..47ea6031 100644 --- a/audio.asm +++ b/audio.asm @@ -1,6 +1,3 @@ -INCLUDE "constants.asm" - - SECTION "Sound Effect Headers 1", ROMX INCLUDE "audio/headers/sfxheaders1.asm" diff --git a/home.asm b/home.asm index ee507c21..4090a274 100644 --- a/home.asm +++ b/home.asm @@ -1,6 +1,3 @@ -INCLUDE "constants.asm" - - SECTION "NULL", ROM0 NULL:: diff --git a/constants.asm b/includes.asm similarity index 78% rename from constants.asm rename to includes.asm index b9445784..29acbc6c 100644 --- a/constants.asm +++ b/includes.asm @@ -1,6 +1,19 @@ INCLUDE "charmap.asm" -INCLUDE "macros.asm" +INCLUDE "macros/asserts.asm" +INCLUDE "macros/const.asm" +INCLUDE "macros/predef.asm" +INCLUDE "macros/farcall.asm" +INCLUDE "macros/data.asm" +INCLUDE "macros/code.asm" +INCLUDE "macros/gfx.asm" +INCLUDE "macros/coords.asm" +INCLUDE "macros/vc.asm" + +INCLUDE "macros/scripts/audio.asm" +INCLUDE "macros/scripts/maps.asm" +INCLUDE "macros/scripts/events.asm" +INCLUDE "macros/scripts/text.asm" INCLUDE "constants/hardware_constants.asm" INCLUDE "constants/oam_constants.asm" diff --git a/macros.asm b/macros.asm deleted file mode 100644 index 466cc1ad..00000000 --- a/macros.asm +++ /dev/null @@ -1,14 +0,0 @@ -INCLUDE "macros/asserts.asm" -INCLUDE "macros/const.asm" -INCLUDE "macros/predef.asm" -INCLUDE "macros/farcall.asm" -INCLUDE "macros/data.asm" -INCLUDE "macros/code.asm" -INCLUDE "macros/gfx.asm" -INCLUDE "macros/coords.asm" -INCLUDE "macros/vc.asm" - -INCLUDE "macros/scripts/audio.asm" -INCLUDE "macros/scripts/maps.asm" -INCLUDE "macros/scripts/events.asm" -INCLUDE "macros/scripts/text.asm" diff --git a/main.asm b/main.asm index 31506a29..11ee2fa9 100644 --- a/main.asm +++ b/main.asm @@ -1,6 +1,3 @@ -INCLUDE "constants.asm" - - SECTION "bank1", ROMX INCLUDE "data/sprites/facings.asm" diff --git a/maps.asm b/maps.asm index 4f00f451..fb003f2a 100644 --- a/maps.asm +++ b/maps.asm @@ -1,6 +1,3 @@ -INCLUDE "constants.asm" - - SECTION "Maps 1", ROMX INCLUDE "data/maps/headers/CeladonCity.asm" diff --git a/ram.asm b/ram.asm index 077ae799..cfeb5997 100644 --- a/ram.asm +++ b/ram.asm @@ -1,5 +1,3 @@ -INCLUDE "constants.asm" - INCLUDE "macros/ram.asm" diff --git a/rgbdscheck.asm b/rgbdscheck.asm index a836a156..e06e1707 100644 --- a/rgbdscheck.asm +++ b/rgbdscheck.asm @@ -1,18 +1,16 @@ MAJOR EQU 0 -MINOR EQU 5 -PATCH EQU 2 +MINOR EQU 6 +PATCH EQU 0 -MACRO wrong_rgbds - fail "pokered requires rgbds v0.5.2 or newer." -ENDM +WRONG_RGBDS EQUS "fail \"pokered requires rgbds v0.6.0 or newer.\"" IF !DEF(__RGBDS_MAJOR__) || !DEF(__RGBDS_MINOR__) || !DEF(__RGBDS_PATCH__) - wrong_rgbds + WRONG_RGBDS ELSE IF (__RGBDS_MAJOR__ < MAJOR) || \ (__RGBDS_MAJOR__ == MAJOR && __RGBDS_MINOR__ < MINOR) || \ (__RGBDS_MAJOR__ == MAJOR && __RGBDS_MINOR__ == MINOR && __RGBDS_PATCH__ < PATCH) || \ (__RGBDS_MAJOR__ == MAJOR && __RGBDS_MINOR__ == MINOR && __RGBDS_PATCH__ == PATCH && DEF(__RGBDS_RC__)) - wrong_rgbds + WRONG_RGBDS ENDC ENDC diff --git a/text.asm b/text.asm index c5020bba..79a03166 100644 --- a/text.asm +++ b/text.asm @@ -1,6 +1,3 @@ -INCLUDE "constants.asm" - - SECTION "Text 1", ROMX INCLUDE "data/text/text_1.asm" diff --git a/tools/free_space.awk b/tools/free_space.awk index aa48c3e3..0e157858 100755 --- a/tools/free_space.awk +++ b/tools/free_space.awk @@ -39,7 +39,7 @@ function register_bank(amount) { } } -rom_bank && toupper($0) ~ /^[ \t]*EMPTY/ { +rom_bank && toupper($0) ~ /^[ \t]*EMPTY$/ { # Empty bank register_bank(16384) } diff --git a/tools/free_space.py b/tools/free_space.py deleted file mode 100755 index 553e1352..00000000 --- a/tools/free_space.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -Usage: python3 free_space.py [BANK=none] [pokered.map] - -Calculate the free space in the ROM or its individual banks. - -The BANK argument allows printing free space in one, all, or none of the ROM's banks. -Valid arguments are numbers (in decimal "42" or hexadecimal "0x2A"), "all" or "none". -If not specified, defaults to "none". -""" - -import sys - -from mapreader import MapReader - -def main(): - print_bank = 'none' - filename = 'pokered.map' - - for arg in sys.argv[1:]: - if arg.startswith('BANK='): - print_bank = arg.split('=', 1)[-1] - else: - filename = arg - - if print_bank not in {'all', 'none'}: - try: - print_bank = (int(print_bank[2:], 16) - if print_bank.startswith(('0x', '0X')) - else int(print_bank)) - except ValueError: - error = f'Error: invalid BANK: {print_bank}' - if print_bank.isalnum(): - error += f' (did you mean: 0x{print_bank}?)' - print(error, file=sys.stderr) - sys.exit(1) - - num_banks = 0x80 - bank_size = 0x4000 # bytes - total_size = num_banks * bank_size - - reader = MapReader() - with open(filename, 'r', encoding='utf-8') as file: - reader.read_map_data(file.readlines()) - - free_space = 0 - per_bank = [] - default_bank_data = {'sections': [], 'used': 0, 'slack': bank_size} - for bank in range(num_banks): - bank_data = reader.bank_data['ROM0 bank' if bank == 0 else 'ROMX bank'] - data = bank_data.get(bank, default_bank_data) - used, slack = data['used'], data['slack'] - per_bank.append((used, slack)) - free_space += slack - - free_percent = 100 * free_space / total_size - print(f'Free space: {free_space}/{total_size} ({free_percent:.2f}%)') - if print_bank != 'none': - print() - print('bank, used, free') - for bank in range(num_banks): - used, slack = per_bank[bank] - if print_bank in {'all', bank}: - print(f'${bank:02X}, {used}, {slack}') - -if __name__ == '__main__': - main() diff --git a/tools/mapreader.py b/tools/mapreader.py deleted file mode 100644 index 0ab2739a..00000000 --- a/tools/mapreader.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: utf-8 -*- - -# A library for parsing the pokered.map file output by rgbds. - -import re - -class MapReader: - - # {'ROM Bank': { 0: { 'sections': [ { 'beg': 1234, - # 'end': 5678, - # 'name': 'Section001', - # 'symbols': [ { 'symbol': 'Function1234', - # 'address: 1234, - # }, - # ] - # }, - # ], - # 'used': 1234, - # 'slack': 4567, - # }, - # }, - # 'OAM': { 'sections': [ { 'beg': 1234, - # 'end': 5678, - # 'name': 'Section002', - # 'symbols': [ { 'symbol': 'Data1234', - # 'address: 1234, - # }, - # ] - # }, - # ], - # 'used': 1234, - # 'slack': 4567, - # }, - # } - # - bank_data = {} - - bank_types = { - 'HRAM' : { 'size': 0x80, 'banked': False, }, - 'OAM' : { 'size': 0xA0, 'banked': False, }, - 'ROM0 bank': { 'size': 0x4000, 'banked': True, }, - 'ROMX bank': { 'size': 0x4000, 'banked': True, }, - 'SRAM bank': { 'size': 0x2000, 'banked': True, }, - 'VRAM bank': { 'size': 0x1000, 'banked': True, }, - 'WRAM bank': { 'size': 0x2000, 'banked': True, }, - } - - # FSM states - INIT, BANK, SECTION = range(3) - - # $506D-$519A ($012E bytes) ["Type Matchups"] - section_header_regex = re.compile('\$([0-9A-Fa-f]{4})-\$([0-9A-Fa-f]{4}) \(.*\) \["(.*)"\]') - # $506D = TypeMatchups - section_data_regex = re.compile('\$([0-9A-Fa-f]{4}) = (.*)') - # $3ED2 bytes - slack_regex = re.compile('\$([0-9A-Fa-f]{4}) bytes?') - - def __init__(self, *args, **kwargs): - self.__dict__.update(kwargs) - - def _parse_init(self, line): - - line = line.split(':', 1)[0] - parts = line.split(' #', 1) - - if (parts[0] in self.bank_types): - self._cur_bank_name = parts[0] - self._cur_bank_type = self.bank_types[self._cur_bank_name] - if (self._cur_bank_type['banked'] and len(parts) > 1): - parts[1] = parts[1].split(':', 1)[0] - parts[1] = parts[1].split(' ', 1)[0] - self._cur_bank = int(parts[1], 10) - if self._cur_bank_name not in self.bank_data: - self.bank_data[self._cur_bank_name] = {} - if self._cur_bank_type['banked']: - if self._cur_bank not in self.bank_data[self._cur_bank_name]: - self.bank_data[self._cur_bank_name][self._cur_bank] = {} - self._cur_data = self.bank_data[self._cur_bank_name][self._cur_bank] - else: - self._cur_data = self.bank_data[self._cur_bank_name] - - if ({} == self._cur_data): - self._cur_data['sections'] = [] - self._cur_data['used'] = 0 - self._cur_data['slack'] = self._cur_bank_type['size'] - return True - - return False - - def _parse_section_header(self, header): - - section_data = self.section_header_regex.match(header) - if section_data is not None: - beg = int(section_data.group(1), 16) - end = int(section_data.group(2), 16) - name = section_data.group(3) - self._cur_section = {'beg': beg, 'end': end, 'name': name, 'symbols': []} - self._cur_data['sections'].append(self._cur_section) - return True - return False - - def _parse_slack(self, data): - - slack_data = self.slack_regex.match(data) - slack_bytes = int(slack_data.group(1), 16) - self._cur_data['slack'] = slack_bytes - - used_bytes = 0 - - for s in self._cur_data['sections']: - used_bytes += s['end'] - s['beg'] + 1 - - self._cur_data['used'] = used_bytes - - def read_map_data(self, map): - - if type(map) is str: - map = map.split('\n') - - self._state = MapReader.INIT - self._cur_bank_name = '' - self._cur_bank_type = {} - self._cur_bank = 0 - self._cur_data = {} - - for line in map: - - line = line.rstrip() - if (MapReader.INIT == self._state): - - if (self._parse_init(line)): - self._state = MapReader.BANK - - elif (MapReader.BANK == self._state or MapReader.SECTION == self._state): - - if ('' == line): - self._state = MapReader.INIT - else: - - line = line.lstrip() - parts = line.split(': ', 1) - - if (MapReader.SECTION == self._state): - section_data = self.section_data_regex.match(parts[0]) - if section_data is not None: - address = int(section_data.group(1), 16) - name = section_data.group(2) - self._cur_section['symbols'].append({'name': name, 'address': address}) - continue - - if ('SECTION' == parts[0]): - if (self._parse_section_header(parts[1])): - self._state = MapReader.SECTION - elif ('SLACK' == parts[0]): - self._parse_slack(parts[1]) - self._state = MapReader.INIT - elif ('EMPTY' == parts[0]): - self._cur_data = {'sections': [], 'used': 0, 'slack': self._cur_bank_type['size']} - self._state = MapReader.INIT - - else: - pass - - for k, v in self.bank_data.items(): - if (self.bank_types[k]['banked']): - for _, vv in v.items(): - vv['sections'].sort(key=lambda x: x['beg']) - for vvv in vv['sections']: - vvv['symbols'].sort(key=lambda x: x['address']) - else: - v['sections'].sort(key=lambda x: x['beg']) - for vv in v['sections']: - vv['symbols'].sort(key=lambda x: x['address']) diff --git a/tools/used_space.py b/tools/used_space.py deleted file mode 100755 index 1ad4eadd..00000000 --- a/tools/used_space.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -Usage: python used_space.py [pokered.map] [used_space.png] - -Generate a PNG visualizing the space used by each bank in the ROM. -""" - -import sys -from colorsys import hls_to_rgb - -import png -from mapreader import MapReader - -def main(): - mapfile = sys.argv[1] if len(sys.argv) >= 2 else 'pokered.map' - outfile = sys.argv[2] if len(sys.argv) >= 3 else 'used_space.png' - - num_banks = 0x80 - bank_mask = 0x3FFF - bank_size = 0x4000 # bytes - - bpp = 8 # bytes per pixel - height = 256 # pixels - assert bank_size % bpp == 0 and (bank_size // bpp) % height == 0 - - pixels_per_bank = bank_size // bpp # 2048 pixels - bank_width = pixels_per_bank // height # 8 pixels - width = bank_width * num_banks # 1024 pixels - - reader = MapReader() - with open(mapfile, 'r', encoding='utf-8') as file: - reader.read_map_data(file.readlines()) - - hit_data = [] - default_bank_data = {'sections': [], 'used': 0, 'slack': bank_size} - for bank in range(num_banks): - hits = [0] * pixels_per_bank - bank_data = reader.bank_data['ROM0 bank' if bank == 0 else 'ROMX bank'] - data = bank_data.get(bank, default_bank_data) - for s in data['sections']: - beg = s['beg'] & bank_mask - end = s['end'] & bank_mask - for i in range(beg, end + 1): - hits[i // bpp] += 1 - hit_data.append(hits) - - pixels = [[(0xFF, 0xFF, 0xFF)] * width for _ in range(height)] - for bank, hits in enumerate(hit_data): - hue = 0 if bank == 0 else 210 if bank % 2 else 270 - for i, hit in enumerate(hits): - x, y = i % bank_width + bank * bank_width, i // bank_width - hls = (hue / 360, 1 - (85 * hit / bpp) / 100, 1) - rgb = tuple(int(c * 0xFF) for c in hls_to_rgb(*hls)) - pixels[y][x] = rgb - - png_data = [tuple(c for pixel in row for c in pixel) for row in pixels] - with open(outfile, 'wb') as file: - writer = png.Writer(width, height, greyscale=False, bitdepth=8, compression=9) - writer.write(file, png_data) - -if __name__ == '__main__': - main() diff --git a/vc/pokeblue.constants.asm b/vc/pokeblue.constants.asm index f89c4fd9..c32c39e6 100644 --- a/vc/pokeblue.constants.asm +++ b/vc/pokeblue.constants.asm @@ -1,5 +1,3 @@ -INCLUDE "constants.asm" - ; These are all the asm constants needed to make the blue_vc patch. MACRO vc_const diff --git a/vc/pokered.constants.asm b/vc/pokered.constants.asm index cb7efa96..646444ee 100644 --- a/vc/pokered.constants.asm +++ b/vc/pokered.constants.asm @@ -1,5 +1,3 @@ -INCLUDE "constants.asm" - ; These are all the asm constants needed to make the red_vc patch. MACRO vc_const From c37cc15f44380af3fdd9030fc196b4951cd6920c Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 2 Oct 2022 22:34:06 -0400 Subject: [PATCH 18/26] Update tools/unnamed.py for rgbds 0.6.0 object files --- tools/unnamed.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/unnamed.py b/tools/unnamed.py index 9dcf5cb3..e6426aaf 100755 --- a/tools/unnamed.py +++ b/tools/unnamed.py @@ -90,14 +90,14 @@ for objfile in objects: elif magic == b'RGB9': obj_ver = 10 + unpack_from(' Date: Fri, 14 Oct 2022 16:21:42 -0400 Subject: [PATCH 19/26] Add `COUNTOF` macro to tools/common.h --- tools/common.h | 2 ++ tools/make_patch.c | 2 +- tools/pkmncompress.c | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/tools/common.h b/tools/common.h index f04e917b..2c690423 100644 --- a/tools/common.h +++ b/tools/common.h @@ -19,6 +19,8 @@ #error Define USAGE_OPTS before including common.h! #endif +#define COUNTOF(...) (sizeof(__VA_ARGS__) / sizeof(*(__VA_ARGS__))) + #define error_exit(...) exit((fprintf(stderr, PROGRAM_NAME ": " __VA_ARGS__), 1)) noreturn void usage_exit(int status) { diff --git a/tools/make_patch.c b/tools/make_patch.c index 71c6b455..ecfd67b2 100644 --- a/tools/make_patch.c +++ b/tools/make_patch.c @@ -165,7 +165,7 @@ int strfind(const char *s, const char *list[], int count) { return -1; } -#define vstrfind(s, ...) strfind(s, (const char *[]){__VA_ARGS__}, sizeof (const char *[]){__VA_ARGS__} / sizeof(const char *)) +#define vstrfind(s, ...) strfind(s, (const char *[]){__VA_ARGS__}, COUNTOF((const char *[]){__VA_ARGS__})) int parse_arg_value(const char *arg, bool absolute, const struct Symbol *symbols, const char *patch_name) { // Comparison operators for "ConditionValueB" evaluate to their particular values diff --git a/tools/pkmncompress.c b/tools/pkmncompress.c index f8899b1e..0e20df4f 100644 --- a/tools/pkmncompress.c +++ b/tools/pkmncompress.c @@ -32,7 +32,7 @@ void write_bit(int bit) { } void compress_plane(uint8_t *plane, int width) { - static int nybble_lookup[2][0x10] = { + static int gray_codes[2][0x10] = { {0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4, 0xC, 0xD, 0xF, 0xE, 0xA, 0xB, 0x9, 0x8}, {0x8, 0x9, 0xB, 0xA, 0xE, 0xF, 0xD, 0xC, 0x4, 0x5, 0x7, 0x6, 0x2, 0x3, 0x1, 0x0}, }; @@ -44,10 +44,10 @@ void compress_plane(uint8_t *plane, int width) { } int j = i / width + m * width * 8; int nybble_hi = (plane[j] >> 4) & 0xF; - int code_1 = nybble_lookup[nybble_lo & 1][nybble_hi]; + int code_hi = gray_codes[nybble_lo & 1][nybble_hi]; nybble_lo = plane[j] & 0xF; - int code_2 = nybble_lookup[nybble_hi & 1][nybble_lo]; - plane[j] = (code_1 << 4) | code_2; + int code_lo = gray_codes[nybble_hi & 1][nybble_lo]; + plane[j] = (code_hi << 4) | code_lo; } } @@ -105,7 +105,7 @@ int interpret_compress(uint8_t *plane1, uint8_t *plane2, int mode, int order, in } cur_bit = 7; cur_byte = 0; - memset(compressed, 0, sizeof(compressed) / sizeof(*compressed)); + memset(compressed, 0, COUNTOF(compressed)); compressed[0] = (width << 4) | width; write_bit(order); uint8_t bit_groups[0x1000] = {0}; @@ -113,7 +113,7 @@ int interpret_compress(uint8_t *plane1, uint8_t *plane2, int mode, int order, in for (int plane = 0; plane < 2; plane++) { int type = 0; int nums = 0; - memset(bit_groups, 0, sizeof(bit_groups) / sizeof(*bit_groups)); + memset(bit_groups, 0, COUNTOF(bit_groups)); for (int x = 0; x < width; x++) { for (int bit = 0; bit < 8; bit += 2) { for (int y = 0, byte = x * width * 8; y < width * 8; y++, byte++) { @@ -129,7 +129,7 @@ int interpret_compress(uint8_t *plane1, uint8_t *plane2, int mode, int order, in write_bit(0); } type = 1; - memset(bit_groups, 0, sizeof(bit_groups) / sizeof(*bit_groups)); + memset(bit_groups, 0, COUNTOF(bit_groups)); index = 0; } else { if (!type) { @@ -171,7 +171,7 @@ int compress(uint8_t *data, int width) { plane1[i] = data[i * 2]; plane2[i] = data[i * 2 + 1]; } - uint8_t current[sizeof(compressed) / sizeof(*compressed)] = {0}; + uint8_t current[COUNTOF(compressed)] = {0}; int compressed_size = -1; for (int mode = 1; mode < 4; mode++) { for (int order = 0; order < 2; order++) { @@ -181,12 +181,12 @@ int compress(uint8_t *data, int width) { int new_size = interpret_compress(plane1, plane2, mode, order, width); if (compressed_size == -1 || new_size < compressed_size) { compressed_size = new_size; - memset(current, 0, sizeof(current) / sizeof(*current)); + memset(current, 0, COUNTOF(current)); memcpy(current, compressed, compressed_size / 8); } } } - memset(compressed, 0, sizeof(compressed) / sizeof(*compressed)); + memset(compressed, 0, COUNTOF(compressed)); memcpy(compressed, current, compressed_size / 8); free(plane1); free(plane2); From 6bb3d82e7a11d2fbdada3e5a57e51d9ec7fff142 Mon Sep 17 00:00:00 2001 From: mid-kid Date: Tue, 25 Oct 2022 18:02:29 +0200 Subject: [PATCH 20/26] Fix preinclude dependencies From pokecrystal: - 0425018d1ce5b3b726e3cd83f59f14ead9171c92 - 8b429e2a545e87407a3b8c7b92f7ceafc455e390 --- Makefile | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 39176c78..215aa768 100644 --- a/Makefile +++ b/Makefile @@ -103,20 +103,21 @@ $(pokeblue_vc_obj): RGBASMFLAGS += -D _BLUE -D _BLUE_VC rgbdscheck.o: rgbdscheck.asm $(RGBASM) -o $@ $< -# The dep rules have to be explicit or else missing files won't be reported. -# As a side effect, they're evaluated immediately instead of when the rule is invoked. -# It doesn't look like $(shell) can be deferred so there might not be a better way. -define DEP -$1: $2 $$(shell tools/scan_includes $2) | includes.asm rgbdscheck.o - $$(RGBASM) $$(RGBASMFLAGS) -o $$@ $$< -endef - # Build tools when building the rom. # This has to happen before the rules are processed, since that's when scan_includes is run. ifeq (,$(filter clean tidy tools,$(MAKECMDGOALS))) $(info $(shell $(MAKE) -C tools)) +# The dep rules have to be explicit or else missing files won't be reported. +# As a side effect, they're evaluated immediately instead of when the rule is invoked. +# It doesn't look like $(shell) can be deferred so there might not be a better way. +preinclude_deps := includes.asm $(shell tools/scan_includes includes.asm) +define DEP +$1: $2 $$(shell tools/scan_includes $2) $(preinclude_deps) | rgbdscheck.o + $$(RGBASM) $$(RGBASMFLAGS) -o $$@ $$< +endef + # Dependencies for objects (drop _red and _blue from asm file basenames) $(foreach obj, $(pokered_obj), $(eval $(call DEP,$(obj),$(obj:_red.o=.asm)))) $(foreach obj, $(pokeblue_obj), $(eval $(call DEP,$(obj),$(obj:_blue.o=.asm)))) @@ -125,7 +126,7 @@ $(foreach obj, $(pokered_vc_obj), $(eval $(call DEP,$(obj),$(obj:_red_vc.o=.asm) $(foreach obj, $(pokeblue_vc_obj), $(eval $(call DEP,$(obj),$(obj:_blue_vc.o=.asm)))) # Dependencies for VC files that need to run scan_includes -%.constants.sym: %.constants.asm $(shell tools/scan_includes %.constants.asm) | includes.asm rgbdscheck.o +%.constants.sym: %.constants.asm $(shell tools/scan_includes %.constants.asm) $(preinclude_deps) | rgbdscheck.o $(RGBASM) $(RGBASMFLAGS) $< > $@ endif From 53805947c3bc30c491b4f2a820c27e152dfa4b89 Mon Sep 17 00:00:00 2001 From: Rangi Date: Sat, 3 Dec 2022 11:11:13 -0500 Subject: [PATCH 21/26] Use rgbds 0.6.1 (0.6.0 also still works) --- .github/workflows/main.yml | 2 +- INSTALL.md | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 805aea49..9b3efa30 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@master with: path: rgbds - ref: v0.6.0 + ref: v0.6.1 repository: gbdev/rgbds - name: Install rgbds diff --git a/INSTALL.md b/INSTALL.md index c8965f40..e0204866 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -42,9 +42,9 @@ Run setup and leave the default settings. At the "**Select Packages**" step, cho Double click on the text that says "**Skip**" next to each package to select the most recent version to install. -Then follow the [**rgbds** install instructions](https://rgbds.gbdev.io/install#pre-built) for Windows with Cygwin to install **rgbds 0.6.0**. +Then follow the [**rgbds** install instructions](https://rgbds.gbdev.io/install#pre-built) for Windows with Cygwin to install **rgbds 0.6.1**. -**Note:** If you already have an older rgbds, you will need to update to 0.6.0. Ignore this if you have never installed rgbds before. If a version newer than 0.6.0 does not work, try downloading 0.6.0. +**Note:** If you already have an installed rgbds older than 0.6.0, you will need to update to 0.6.0 or 0.6.1. Ignore this if you have never installed rgbds before. If a version newer than 0.6.1 does not work, try downloading 0.6.1. Now open the **Cygwin terminal** and enter the following commands. @@ -67,7 +67,7 @@ Install [**Homebrew**](https://brew.sh/). Follow the official instructions. Open **Terminal** and prepare to enter commands. -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for macOS to install **rgbds 0.6.0**. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for macOS to install **rgbds 0.6.1**. Now you're ready to [build **pokered**](#build-pokered). @@ -84,7 +84,7 @@ To install the software required for **pokered**: sudo apt-get install make gcc git ``` -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.1** from source. ### OpenSUSE @@ -94,7 +94,7 @@ To install the software required for **pokered**: sudo zypper install make gcc git ``` -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.1** from source. ### Arch Linux @@ -104,9 +104,9 @@ To install the software required for **pokered**: sudo pacman -S make gcc git ``` -Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for Arch Linux to install **rgbds 0.6.0**. +Then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#pre-built) for Arch Linux to install **rgbds 0.6.1**. -If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. +If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.1** from source. ### Termux @@ -122,7 +122,7 @@ To install **rgbds**: sudo apt install rgbds ``` -If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. +If you want to compile and install **rgbds** yourself instead, then follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.1** from source. ### Other distros @@ -133,7 +133,7 @@ If your distro is not listed here, try to find the required software in its repo - `git` - `rgbds` -If `rgbds` is not available, you'll need to follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.0** from source. +If `rgbds` is not available, you'll need to follow the [**rgbds** instructions](https://rgbds.gbdev.io/install#building-from-source) to build **rgbds 0.6.1** from source. Now you're ready to [build **pokered**](#build-pokered). @@ -155,8 +155,8 @@ make ### Build with a local rgbds version -If you have different projects that require different versions of `rgbds`, it might not be convenient to install rgbds 0.6.0 globally. Instead, you can put its files in a directory within pokered, such as `pokered/rgbds-0.6.0/`. Then specify it when you run `make`: +If you have different projects that require different versions of `rgbds`, it might not be convenient to install rgbds 0.6.1 globally. Instead, you can put its files in a directory within pokered, such as `pokered/rgbds-0.6.1/`. Then specify it when you run `make`: ```bash -make RGBDS=rgbds-0.6.0/ +make RGBDS=rgbds-0.6.1/ ``` From 20ebfd079f1331af5cc4f0edea35163b5b35612d Mon Sep 17 00:00:00 2001 From: vulcandth Date: Wed, 28 Dec 2022 21:47:27 -0600 Subject: [PATCH 22/26] Add table assertion to `TrainerDataPointers` (#403) * Add table assertion to `TrainerDataPointers` * Add table assertion to `TrainerNamePointers` --- data/trainers/name_pointers.asm | 2 ++ data/trainers/parties.asm | 2 ++ 2 files changed, 4 insertions(+) diff --git a/data/trainers/name_pointers.asm b/data/trainers/name_pointers.asm index 6317f54a..914fe9d4 100644 --- a/data/trainers/name_pointers.asm +++ b/data/trainers/name_pointers.asm @@ -2,6 +2,7 @@ TrainerNamePointers: ; These are only used for trainers' defeat speeches. ; They were originally shortened variants of the trainer class names ; in the Japanese versions, but are now redundant with TrainerNames. + table_width 2, TrainerNamePointers dw .YoungsterName dw .BugCatcherName dw .LassName @@ -49,6 +50,7 @@ TrainerNamePointers: dw wTrainerName dw wTrainerName dw wTrainerName + assert_table_length NUM_TRAINERS .YoungsterName: db "YOUNGSTER@" .BugCatcherName: db "BUG CATCHER@" diff --git a/data/trainers/parties.asm b/data/trainers/parties.asm index dddcf653..b2ad5e08 100644 --- a/data/trainers/parties.asm +++ b/data/trainers/parties.asm @@ -1,4 +1,5 @@ TrainerDataPointers: + table_width 2, TrainerDataPointers dw YoungsterData dw BugCatcherData dw LassData @@ -46,6 +47,7 @@ TrainerDataPointers: dw ChannelerData dw AgathaData dw LanceData + assert_table_length NUM_TRAINERS ; if first byte != $FF, then ; first byte is level (of all pokemon on this team) From e076ee0a40b2a21183e2cc535519e64b706b5407 Mon Sep 17 00:00:00 2001 From: dannye <33dannye@gmail.com> Date: Wed, 28 Dec 2022 21:48:55 -0600 Subject: [PATCH 23/26] Add list assertion to TrainerNames --- data/trainers/names.asm | 96 +++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/data/trainers/names.asm b/data/trainers/names.asm index d8e1f550..dc6ded77 100644 --- a/data/trainers/names.asm +++ b/data/trainers/names.asm @@ -1,48 +1,50 @@ TrainerNames:: - db "YOUNGSTER@" - db "BUG CATCHER@" - db "LASS@" - db "SAILOR@" - db "JR.TRAINER♂@" - db "JR.TRAINER♀@" - db "POKéMANIAC@" - db "SUPER NERD@" - db "HIKER@" - db "BIKER@" - db "BURGLAR@" - db "ENGINEER@" - db "JUGGLER@" - db "FISHERMAN@" - db "SWIMMER@" - db "CUE BALL@" - db "GAMBLER@" - db "BEAUTY@" - db "PSYCHIC@" - db "ROCKER@" - db "JUGGLER@" - db "TAMER@" - db "BIRD KEEPER@" - db "BLACKBELT@" - db "RIVAL1@" - db "PROF.OAK@" - db "CHIEF@" - db "SCIENTIST@" - db "GIOVANNI@" - db "ROCKET@" - db "COOLTRAINER♂@" - db "COOLTRAINER♀@" - db "BRUNO@" - db "BROCK@" - db "MISTY@" - db "LT.SURGE@" - db "ERIKA@" - db "KOGA@" - db "BLAINE@" - db "SABRINA@" - db "GENTLEMAN@" - db "RIVAL2@" - db "RIVAL3@" - db "LORELEI@" - db "CHANNELER@" - db "AGATHA@" - db "LANCE@" + list_start TrainerNames + li "YOUNGSTER" + li "BUG CATCHER" + li "LASS" + li "SAILOR" + li "JR.TRAINER♂" + li "JR.TRAINER♀" + li "POKéMANIAC" + li "SUPER NERD" + li "HIKER" + li "BIKER" + li "BURGLAR" + li "ENGINEER" + li "JUGGLER" + li "FISHERMAN" + li "SWIMMER" + li "CUE BALL" + li "GAMBLER" + li "BEAUTY" + li "PSYCHIC" + li "ROCKER" + li "JUGGLER" + li "TAMER" + li "BIRD KEEPER" + li "BLACKBELT" + li "RIVAL1" + li "PROF.OAK" + li "CHIEF" + li "SCIENTIST" + li "GIOVANNI" + li "ROCKET" + li "COOLTRAINER♂" + li "COOLTRAINER♀" + li "BRUNO" + li "BROCK" + li "MISTY" + li "LT.SURGE" + li "ERIKA" + li "KOGA" + li "BLAINE" + li "SABRINA" + li "GENTLEMAN" + li "RIVAL2" + li "RIVAL3" + li "LORELEI" + li "CHANNELER" + li "AGATHA" + li "LANCE" + assert_list_length NUM_TRAINERS From 5c4795d5bc19edd872adf7b17d0c6264eecd6459 Mon Sep 17 00:00:00 2001 From: SatoMew Date: Sun, 26 Feb 2023 16:14:21 +0000 Subject: [PATCH 24/26] Complete and improve debug name labels (#408) --- engine/debug/debug_menu.asm | 10 ++++++---- engine/menus/main_menu.asm | 1 + engine/movie/oak_speech/oak_speech.asm | 10 ++++++---- engine/movie/title.asm | 23 ++++++++++++++--------- home/init.asm | 2 +- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/engine/debug/debug_menu.asm b/engine/debug/debug_menu.asm index f2297129..907acb8a 100644 --- a/engine/debug/debug_menu.asm +++ b/engine/debug/debug_menu.asm @@ -2,12 +2,14 @@ DebugMenu: IF DEF(_DEBUG) call ClearScreen - ld hl, DebugPlayerName + ; These debug names are used for TestBattle. + ; StartNewGameDebug uses the debug names from PrepareOakSpeech. + ld hl, DebugBattlePlayerName ld de, wPlayerName ld bc, NAME_LENGTH call CopyData - ld hl, DebugRivalName + ld hl, DebugBattleRivalName ld de, wRivalName ld bc, NAME_LENGTH call CopyData @@ -57,10 +59,10 @@ IF DEF(_DEBUG) set 1, [hl] jp StartNewGameDebug -DebugPlayerName: +DebugBattlePlayerName: db "Tom@" -DebugRivalName: +DebugBattleRivalName: db "Juerry@" DebugMenuOptions: diff --git a/engine/menus/main_menu.asm b/engine/menus/main_menu.asm index d94c65eb..024def28 100644 --- a/engine/menus/main_menu.asm +++ b/engine/menus/main_menu.asm @@ -309,6 +309,7 @@ LinkCanceledText: StartNewGame: ld hl, wd732 res 1, [hl] + ; fallthrough StartNewGameDebug: call OakSpeech ld c, 20 diff --git a/engine/movie/oak_speech/oak_speech.asm b/engine/movie/oak_speech/oak_speech.asm index 93cdfaab..3a08372d 100644 --- a/engine/movie/oak_speech/oak_speech.asm +++ b/engine/movie/oak_speech/oak_speech.asm @@ -1,4 +1,4 @@ -SetDefaultNames: +PrepareOakSpeech: ld a, [wLetterPrintingDelayFlags] push af ld a, [wOptions] @@ -22,11 +22,13 @@ SetDefaultNames: ld a, [wOptionsInitialized] and a call z, InitOptions - ld hl, NintenText + ; These debug names are used for StartNewGameDebug. + ; TestBattle uses the debug names from DebugMenu. + ld hl, DebugNewGamePlayerName ld de, wPlayerName ld bc, NAME_LENGTH call CopyData - ld hl, SonyText + ld hl, DebugNewGameRivalName ld de, wRivalName ld bc, NAME_LENGTH jp CopyData @@ -40,7 +42,7 @@ OakSpeech: call PlayMusic call ClearScreen call LoadTextBoxTilePatterns - call SetDefaultNames + call PrepareOakSpeech predef InitPlayerData2 ld hl, wNumBoxItems ld a, POTION diff --git a/engine/movie/title.asm b/engine/movie/title.asm index 1ee0cc25..ad5f6ebf 100644 --- a/engine/movie/title.asm +++ b/engine/movie/title.asm @@ -1,15 +1,17 @@ -; copy text of fixed length NAME_LENGTH (like player name, rival name, mon names, ...) -CopyFixedLengthText: +CopyDebugName: ld bc, NAME_LENGTH jp CopyData -SetDefaultNamesBeforeTitlescreen:: - ld hl, NintenText +PrepareTitleScreen:: + ; These debug names are already copied later in PrepareOakSpeech. + ; Removing the unused copies below has no apparent impact. + ; CopyDebugName can also be safely deleted afterwards. + ld hl, DebugNewGamePlayerName ld de, wPlayerName - call CopyFixedLengthText - ld hl, SonyText + call CopyDebugName + ld hl, DebugNewGameRivalName ld de, wRivalName - call CopyFixedLengthText + call CopyDebugName xor a ldh [hWY], a ld [wLetterPrintingDelayFlags], a @@ -405,5 +407,8 @@ IF DEF(_BLUE) db $61,$62,$63,$64,$65,$66,$67,$68,"@" ; "Blue Version" ENDC -NintenText: db "NINTEN@" -SonyText: db "SONY@" +DebugNewGamePlayerName: + db "NINTEN@" + +DebugNewGameRivalName: + db "SONY@" diff --git a/home/init.asm b/home/init.asm index 81e75586..db21efbc 100644 --- a/home/init.asm +++ b/home/init.asm @@ -116,7 +116,7 @@ DEF rLCDC_DEFAULT EQU %11100011 ld a, rLCDC_DEFAULT ldh [rLCDC], a - jp SetDefaultNamesBeforeTitlescreen + jp PrepareTitleScreen ClearVram:: ld hl, VRAM_Begin From a76ae055cc306f922e84d546b2f54f32e35a6a11 Mon Sep 17 00:00:00 2001 From: RainbowMetalPigeon <118711812+RainbowMetalPigeon@users.noreply.github.com> Date: Sat, 25 Mar 2023 20:28:55 -0700 Subject: [PATCH 25/26] Dex weights are in tenths of a pound --- data/pokemon/dex_entries.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/pokemon/dex_entries.asm b/data/pokemon/dex_entries.asm index c99087e2..ceb431b7 100644 --- a/data/pokemon/dex_entries.asm +++ b/data/pokemon/dex_entries.asm @@ -194,7 +194,7 @@ PokedexEntryPointers: ; string: species name ; height in feet, inches -; weight in pounds +; weight in tenths of a pound ; text entry RhydonDexEntry: From 17eaf761e38cea827320f984cfaaa1527276ab15 Mon Sep 17 00:00:00 2001 From: Vortyne <104168801+Vortyne@users.noreply.github.com> Date: Sat, 25 Mar 2023 23:31:59 -0400 Subject: [PATCH 26/26] Name some unnamed animation constants (#396) --- constants/move_constants.asm | 14 +++++++------- data/moves/animations.asm | 4 ++-- engine/battle/core.asm | 4 ++-- engine/battle/effects.asm | 16 ++++++++-------- engine/battle/trainer_ai.asm | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/constants/move_constants.asm b/constants/move_constants.asm index 570be95e..3376c1af 100644 --- a/constants/move_constants.asm +++ b/constants/move_constants.asm @@ -178,14 +178,14 @@ DEF NUM_ATTACKS EQU const_value - 1 const SHOWPIC_ANIM const STATUS_AFFECTED_ANIM const ANIM_A8 - const ANIM_A9 + const ENEMY_HUD_SHAKE_ANIM const TRADE_BALL_DROP_ANIM const TRADE_BALL_SHAKE_ANIM const TRADE_BALL_TILT_ANIM const TRADE_BALL_POOF_ANIM const XSTATITEM_ANIM ; use X Attack/Defense/Speed/Special - const ANIM_AF - const ANIM_B0 + const XSTATITEM_DUPLICATE_ANIM + const SHRINKING_SQUARE_ANIM const ANIM_B1 const ANIM_B2 const ANIM_B3 @@ -197,18 +197,18 @@ DEF NUM_ATTACKS EQU const_value - 1 const ANIM_B9 const BURN_PSN_ANIM ; Plays when a monster is burned or poisoned const ANIM_BB - const ANIM_BC + const SLP_PLAYER_ANIM const SLP_ANIM ; sleeping monster - const ANIM_BE + const CONF_PLAYER_ANIM const CONF_ANIM ; confused monster - const ANIM_C0 + const SLIDE_DOWN_ANIM const TOSS_ANIM ; toss Poké Ball const SHAKE_ANIM ; shaking Poké Ball when catching monster const POOF_ANIM ; puff of smoke const BLOCKBALL_ANIM ; trainer knocks away Poké Ball const GREATTOSS_ANIM ; toss Great Ball const ULTRATOSS_ANIM ; toss Ultra Ball or Master Ball - const ANIM_C7 + const SHAKE_SCREEN_ANIM const HIDEPIC_ANIM ; monster disappears const ROCK_ANIM ; throw rock const BAIT_ANIM ; throw bait diff --git a/data/moves/animations.asm b/data/moves/animations.asm index 0a8db8f5..29a78477 100644 --- a/data/moves/animations.asm +++ b/data/moves/animations.asm @@ -192,7 +192,7 @@ AttackAnimationPointers: dw SleepEnemyAnim dw ConfusedPlayerAnim dw ConfusedEnemyAnim - dw FaintAnim + dw SlideDownAnim dw BallTossAnim dw BallShakeAnim dw BallPoofAnim @@ -1263,7 +1263,7 @@ BallBlockAnim: battle_anim NO_MOVE, SUBANIM_0_BALL_BLOCK, 0, 3 db -1 ; end -FaintAnim: +SlideDownAnim: battle_anim DIG, SE_SLIDE_MON_DOWN db -1 ; end diff --git a/engine/battle/core.asm b/engine/battle/core.asm index f84b759c..4a0263e2 100644 --- a/engine/battle/core.asm +++ b/engine/battle/core.asm @@ -3327,7 +3327,7 @@ CheckPlayerStatusConditions: ; fast asleep xor a ld [wAnimationType], a - ld a, SLP_ANIM - 1 + ld a, SLP_PLAYER_ANIM call PlayMoveAnimation ld hl, FastAsleepText call PrintText @@ -3411,7 +3411,7 @@ CheckPlayerStatusConditions: call PrintText xor a ld [wAnimationType], a - ld a, CONF_ANIM - 1 + ld a, CONF_PLAYER_ANIM call PlayMoveAnimation call BattleRandom cp 50 percent + 1 ; chance to hurt itself diff --git a/engine/battle/effects.asm b/engine/battle/effects.asm index c68d6731..aaaf20d0 100644 --- a/engine/battle/effects.asm +++ b/engine/battle/effects.asm @@ -123,12 +123,12 @@ PoisonEffect: dec de ldh a, [hWhoseTurn] and a - ld b, ANIM_C7 + ld b, SHAKE_SCREEN_ANIM ld hl, wPlayerBattleStatus3 ld a, [de] ld de, wPlayerToxicCounter jr nz, .ok - ld b, ANIM_A9 + ld b, ENEMY_HUD_SHAKE_ANIM ld hl, wEnemyBattleStatus3 ld de, wEnemyToxicCounter .ok @@ -232,14 +232,14 @@ FreezeBurnParalyzeEffect: ld a, 1 << PAR ld [wEnemyMonStatus], a call QuarterSpeedDueToParalysis ; quarter speed of affected mon - ld a, ANIM_A9 + ld a, ENEMY_HUD_SHAKE_ANIM call PlayBattleAnimation jp PrintMayNotAttackText ; print paralysis text .burn1 ld a, 1 << BRN ld [wEnemyMonStatus], a call HalveAttackDueToBurn ; halve attack of affected mon - ld a, ANIM_A9 + ld a, ENEMY_HUD_SHAKE_ANIM call PlayBattleAnimation ld hl, BurnedText jp PrintText @@ -247,7 +247,7 @@ FreezeBurnParalyzeEffect: call ClearHyperBeam ; resets hyper beam (recharge) condition from target ld a, 1 << FRZ ld [wEnemyMonStatus], a - ld a, ANIM_A9 + ld a, ENEMY_HUD_SHAKE_ANIM call PlayBattleAnimation ld hl, FrozenText jp PrintText @@ -802,7 +802,7 @@ ThrashPetalDanceEffect: inc a ld [de], a ; set thrash/petal dance counter to 2 or 3 at random ldh a, [hWhoseTurn] - add ANIM_B0 + add SHRINKING_SQUARE_ANIM jp PlayBattleAnimation2 SwitchAndTeleportEffect: @@ -1002,7 +1002,7 @@ ChargeEffect: jr z, .chargeEffect ld hl, wEnemyBattleStatus1 ld de, wEnemyMoveEffect - ld b, ANIM_AF + ld b, XSTATITEM_DUPLICATE_ANIM .chargeEffect set CHARGING_UP, [hl] ld a, [de] @@ -1016,7 +1016,7 @@ ChargeEffect: cp DIG jr nz, .notDigOrFly set INVULNERABLE, [hl] ; mon is now invulnerable to typical attacks (fly/dig) - ld b, ANIM_C0 + ld b, SLIDE_DOWN_ANIM .notDigOrFly xor a ld [wAnimationType], a diff --git a/engine/battle/trainer_ai.asm b/engine/battle/trainer_ai.asm index 2ef60f5b..e21a3492 100644 --- a/engine/battle/trainer_ai.asm +++ b/engine/battle/trainer_ai.asm @@ -713,7 +713,7 @@ AIIncreaseStat: ld a, [hl] push af push hl - ld a, ANIM_AF + ld a, XSTATITEM_DUPLICATE_ANIM ld [hli], a ld [hl], b callfar StatModifierUpEffect