diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 13cc4e5f..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.5.2 + ref: v0.6.1 repository: gbdev/rgbds - name: Install rgbds diff --git a/INSTALL.md b/INSTALL.md index 95ce870e..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.5.2**. +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.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 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.5.2**. +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.5.2** 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.5.2** 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.5.2**. +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.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.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.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.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.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.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.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.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.5.2/ +make RGBDS=rgbds-0.6.1/ ``` diff --git a/Makefile b/Makefile index 1be1bf21..1705b6b6 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,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 @@ -83,26 +83,27 @@ $(kep_obj): RGBASMFLAGS += -D _KEP -D _DEBUG 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) | 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, $(kep_obj), $(eval $(call DEP,$(obj),$(obj:_kep.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) $(preinclude_deps) | rgbdscheck.o + $(RGBASM) $(RGBASMFLAGS) $< > $@ endif @@ -121,13 +122,13 @@ kep_opt = -jsv -n 0 -k 01 -l 0x33 -m 0x13 -r 03 -t "KEP Hack" ### 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/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/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/README.md b/README.md index ab7cd740..ad3e31b3 100644 --- a/README.md +++ b/README.md @@ -297,6 +297,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] @@ -319,6 +321,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 diff --git a/audio.asm b/audio.asm index b02c9685..81482c19 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/constants/event_constants.asm b/constants/event_constants.asm index 58cb9b35..5afe55f4 100644 --- a/constants/event_constants.asm +++ b/constants/event_constants.asm @@ -746,7 +746,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 @@ -766,7 +767,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/constants/map_constants.asm b/constants/map_constants.asm index b972dead..fdcef86a 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 VERMILION_CITY, 18, 20 ; $04 - mapconst LAVENDER_TOWN, 9, 10 ; $05 - mapconst CELADON_CITY, 18, 25 ; $06 - mapconst CELADON_UNIVERSITY_OUTSIDE, 20, 21 ; $07 - mapconst FUCHSIA_CITY, 18, 20 ; $08 - mapconst SAFFRON_CITY, 18, 20 ; $0A - mapconst CINNABAR_ISLAND, 15, 14 ; $0A - mapconst INDIGO_PLATEAU, 9, 10 ; $0B - mapconst CITRINE_CITY, 18, 20 ; $0C + 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 VERMILION_CITY, 20, 18 ; $04 + map_const LAVENDER_TOWN, 10, 9 ; $05 + map_const CELADON_CITY, 25, 18 ; $06 + map_const CELADON_UNIVERSITY_OUTSIDE, 21, 20 ; $07 + map_const FUCHSIA_CITY, 20, 18 ; $08 + map_const SAFFRON_CITY, 20, 18 ; $0A + map_const CINNABAR_ISLAND, 14, 15 ; $0A + map_const INDIGO_PLATEAU, 10, 9 ; $0B + map_const CITRINE_CITY, 20, 18 ; $0C DEF NUM_CITY_MAPS EQU const_value DEF FIRST_ROUTE_MAP EQU const_value - mapconst ROUTE_1, 18, 10 ; $0D - mapconst ROUTE_2, 36, 10 ; $0E - mapconst ROUTE_3, 9, 35 ; $0F - mapconst ROUTE_4, 9, 45 ; $10 - mapconst ROUTE_5, 18, 10 ; $11 - mapconst ROUTE_6, 18, 10 ; $12 - mapconst ROUTE_7, 9, 10 ; $13 - mapconst ROUTE_8, 9, 30 ; $14 - mapconst ROUTE_9, 13, 30 ; $15 - mapconst ROUTE_10, 40, 10 ; $16 - mapconst ROUTE_11, 9, 30 ; $17 - mapconst ROUTE_12, 54, 10 ; $18 - mapconst ROUTE_13, 9, 30 ; $19 - mapconst ROUTE_14, 27, 10 ; $1A - mapconst ROUTE_15, 9, 30 ; $1B - mapconst ROUTE_16, 9, 20 ; $1C - mapconst ROUTE_17, 72, 10 ; $1D - mapconst ROUTE_18, 9, 25 ; $1E - mapconst ROUTE_19, 27, 10 ; $1F - mapconst ROUTE_20, 15, 50 ; $20 - mapconst ROUTE_21, 45, 10 ; $21 - mapconst ROUTE_22, 9, 20 ; $22 - mapconst ROUTE_23, 72, 10 ; $23 - mapconst ROUTE_24, 18, 10 ; $24 - mapconst ROUTE_25, 9, 37 ; $25 + map_const ROUTE_1, 10, 18 ; $0D + map_const ROUTE_2, 10, 36 ; $0E + map_const ROUTE_3, 35, 9 ; $0F + map_const ROUTE_4, 45, 9 ; $10 + map_const ROUTE_5, 10, 18 ; $11 + map_const ROUTE_6, 10, 18 ; $12 + map_const ROUTE_7, 10, 9 ; $13 + map_const ROUTE_8, 30, 9 ; $14 + map_const ROUTE_9, 30, 13 ; $15 + map_const ROUTE_10, 10, 40 ; $16 + map_const ROUTE_11, 30, 9 ; $17 + map_const ROUTE_12, 10, 54 ; $18 + map_const ROUTE_13, 30, 9 ; $19 + map_const ROUTE_14, 10, 27 ; $1A + map_const ROUTE_15, 30, 9 ; $1B + map_const ROUTE_16, 20, 9 ; $1C + map_const ROUTE_17, 10, 72 ; $1D + map_const ROUTE_18, 25, 9 ; $1E + map_const ROUTE_19, 10, 27 ; $1F + map_const ROUTE_20, 50, 15 ; $20 + map_const ROUTE_21, 10, 45 ; $21 + map_const ROUTE_22, 20, 9 ; $22 + map_const ROUTE_23, 10, 72 ; $23 + map_const ROUTE_24, 10, 18 ; $24 + map_const ROUTE_25, 37, 9 ; $25 DEF FIRST_INDOOR_MAP EQU const_value - mapconst REDS_HOUSE_1F, 4, 4 ; $26 - mapconst REDS_HOUSE_2F, 4, 4 ; $27 - mapconst BLUES_HOUSE, 4, 4 ; $28 - mapconst OAKS_LAB, 6, 7 ; $29 - mapconst VIRIDIAN_POKECENTER, 4, 7 ; $2A - mapconst VIRIDIAN_MART, 4, 4 ; $2B - mapconst VIRIDIAN_SCHOOL_HOUSE, 4, 4 ; $2C - mapconst VIRIDIAN_NICKNAME_HOUSE, 4, 4 ; $2D - mapconst VIRIDIAN_GYM, 9, 10 ; $2E - mapconst DIGLETTS_CAVE_ROUTE_2, 4, 4 ; $2F - mapconst VIRIDIAN_FOREST_NORTH_GATE, 4, 5 ; $30 - mapconst ROUTE_2_TRADE_HOUSE, 4, 4 ; $31 - mapconst ROUTE_2_GATE, 4, 5 ; $32 - mapconst VIRIDIAN_FOREST_SOUTH_GATE, 4, 5 ; $33 - mapconst VIRIDIAN_FOREST, 24, 17 ; $34 - mapconst MUSEUM_1F, 4, 10 ; $35 - mapconst MUSEUM_2F, 4, 7 ; $36 - mapconst PEWTER_GYM, 7, 5 ; $37 - mapconst PEWTER_NIDORAN_HOUSE, 4, 4 ; $38 - mapconst PEWTER_MART, 4, 4 ; $39 - mapconst PEWTER_SPEECH_HOUSE, 4, 4 ; $3A - mapconst PEWTER_POKECENTER, 4, 7 ; $3B - mapconst MT_MOON_1F, 18, 20 ; $3C - mapconst MT_MOON_B1F, 14, 14 ; $3D - mapconst MT_MOON_B2F, 18, 20 ; $3E - mapconst CERULEAN_TRASHED_HOUSE, 4, 4 ; $3F - mapconst CERULEAN_TRADE_HOUSE, 4, 4 ; $40 - mapconst CERULEAN_POKECENTER, 4, 7 ; $41 - mapconst CERULEAN_GYM, 7, 5 ; $42 - mapconst BIKE_SHOP, 4, 4 ; $43 - mapconst CERULEAN_MART, 4, 4 ; $44 - mapconst MT_MOON_POKECENTER, 4, 7 ; $45 - mapconst VIRIDIAN_PRE_GYM, 7, 5 ; $46 - was CERULEAN_TRASHED_HOUSE_COPY - mapconst ROUTE_5_GATE, 3, 4 ; $47 - mapconst UNDERGROUND_PATH_ROUTE_5, 4, 4 ; $48 - mapconst DAYCARE, 4, 4 ; $49 - mapconst ROUTE_6_GATE, 3, 4 ; $4A - mapconst UNDERGROUND_PATH_ROUTE_6, 4, 4 ; $4B - mapconst CELESTE_HILL, 12, 12 ; $4C - Was copy of above & unused - mapconst ROUTE_7_GATE, 4, 3 ; $4D - mapconst UNDERGROUND_PATH_ROUTE_7, 4, 4 ; $4E - mapconst UNDERGROUND_PATH_ROUTE_7_COPY, 4, 4 ; $4F - Unused - mapconst ROUTE_8_GATE, 4, 3 ; $50 - mapconst UNDERGROUND_PATH_ROUTE_8, 4, 4 ; $51 - mapconst ROCK_TUNNEL_POKECENTER, 4, 7 ; $52 - mapconst ROCK_TUNNEL_1F, 18, 20 ; $53 - mapconst POWER_PLANT, 18, 20 ; $54 - mapconst ROUTE_11_GATE_1F, 5, 4 ; $55 - mapconst DIGLETTS_CAVE_ROUTE_11, 4, 4 ; $56 - mapconst ROUTE_11_GATE_2F, 4, 4 ; $57 - mapconst ROUTE_12_GATE_1F, 4, 5 ; $58 - mapconst BILLS_HOUSE, 4, 7 ; $59 - mapconst VERMILION_POKECENTER, 4, 7 ; $5A - mapconst POKEMON_FAN_CLUB, 4, 4 ; $5B - mapconst VERMILION_MART, 4, 4 ; $5C - mapconst VERMILION_GYM, 9, 5 ; $5D - mapconst VERMILION_PIDGEY_HOUSE, 4, 4 ; $5E - mapconst VERMILION_DOCK, 6, 14 ; $5F - mapconst SS_ANNE_1F, 9, 20 ; $60 - mapconst SS_ANNE_2F, 9, 20 ; $61 - mapconst SS_ANNE_3F, 3, 10 ; $62 - mapconst SS_ANNE_B1F, 4, 15 ; $63 - mapconst SS_ANNE_BOW, 7, 10 ; $64 - mapconst SS_ANNE_KITCHEN, 8, 7 ; $65 - mapconst SS_ANNE_CAPTAINS_ROOM, 4, 3 ; $66 - mapconst SS_ANNE_1F_ROOMS, 8, 12 ; $67 - mapconst SS_ANNE_2F_ROOMS, 8, 12 ; $68 - mapconst SS_ANNE_B1F_ROOMS, 8, 12 ; $69 - mapconst SILPH_GAUNTLET_1F, 9, 10 ; $6A - Was UNUSED_MAP_69 - mapconst SILPH_GAUNTLET_2F, 10, 13 ; $6B - Was UNUSED_MAP_6A - mapconst SILPH_GAUNTLET_3F, 13, 17 ; $6C - Was UNUSED_MAP_6B - mapconst VICTORY_ROAD_1F, 9, 10 ; $6D - mapconst SILPH_GAUNTLET_4F, 9, 10 ; $6E - Was UNUSED_MAP_6D - mapconst SILPH_GAUNTLET_5F, 9, 10 ; $6F - Was UNUSED_MAP_6E - mapconst SILPH_GAUNTLET_6F, 12, 12 ; $70 - Was UNUSED_MAP_6F - mapconst SILPH_GAUNTLET_7F, 5, 6 ; $71 - Was UNUSED_MAP_70 - mapconst LANCES_ROOM, 13, 13 ; $72 - mapconst FARAWAY_ISLAND_OUTSIDE, 18, 15 ; $73 - Was UNUSED_MAP_72 - mapconst FARAWAY_ISLAND_INSIDE, 11, 15 ; $74 - Was UNUSED_MAP_73 - mapconst POKEMON_MANSION_B2F, 12, 15 ; $75 - Was UNUSED_MAP_74 - mapconst MT_MOON_CRATER, 25, 25 ; $76 - Was UNUSED_MAP_75 - mapconst HALL_OF_FAME, 4, 5 ; $77 - mapconst UNDERGROUND_PATH_NORTH_SOUTH, 24, 4 ; $78 - mapconst CHAMPIONS_ROOM, 4, 4 ; $79 - mapconst UNDERGROUND_PATH_WEST_EAST, 4, 25 ; $7A - mapconst CELADON_MART_1F, 4, 10 ; $7B - mapconst CELADON_MART_2F, 4, 10 ; $7C - mapconst CELADON_MART_3F, 4, 10 ; $7D - mapconst CELADON_MART_4F, 4, 10 ; $7E - mapconst CELADON_MART_ROOF, 4, 10 ; $7F - mapconst CELADON_MART_ELEVATOR, 2, 2 ; $80 - mapconst CELADON_MANSION_1F, 6, 4 ; $81 - mapconst CELADON_MANSION_2F, 6, 4 ; $82 - mapconst CELADON_MANSION_3F, 6, 4 ; $83 - mapconst CELADON_MANSION_ROOF, 6, 4 ; $84 - mapconst CELADON_MANSION_ROOF_HOUSE, 4, 4 ; $85 - mapconst CELADON_POKECENTER, 4, 7 ; $86 - mapconst CELADON_GYM, 9, 5 ; $87 - mapconst GAME_CORNER, 9, 10 ; $88 - mapconst CELADON_MART_5F, 4, 10 ; $89 - mapconst GAME_CORNER_PRIZE_ROOM, 4, 5 ; $8A - mapconst CELADON_DINER, 4, 5 ; $8B - mapconst CELADON_CHIEF_HOUSE, 4, 4 ; $8C - mapconst CELADON_HOTEL, 4, 7 ; $8D - mapconst LAVENDER_POKECENTER, 4, 7 ; $8E - mapconst POKEMON_TOWER_1F, 9, 10 ; $8F - mapconst POKEMON_TOWER_2F, 9, 10 ; $90 - mapconst POKEMON_TOWER_3F, 9, 10 ; $91 - Currently unused - mapconst POKEMON_TOWER_4F, 9, 10 ; $92 - Currently unused - mapconst POKEMON_TOWER_5F, 9, 10 ; $93 - Currently unused - mapconst POKEMON_TOWER_6F, 9, 10 ; $94 - mapconst POKEMON_TOWER_7F, 9, 10 ; $95 - mapconst MR_FUJIS_HOUSE, 4, 4 ; $96 - mapconst LAVENDER_MART, 4, 4 ; $97 - mapconst LAVENDER_CUBONE_HOUSE, 4, 4 ; $98 - mapconst FUCHSIA_MART, 4, 4 ; $99 - mapconst FUCHSIA_BILLS_GRANDPAS_HOUSE, 4, 4 ; $9A - mapconst FUCHSIA_POKECENTER, 4, 7 ; $9B - mapconst WARDENS_HOUSE, 4, 5 ; $9C - mapconst SAFARI_ZONE_GATE, 3, 4 ; $9D - mapconst FUCHSIA_GYM, 9, 5 ; $9E - mapconst FUCHSIA_MEETING_ROOM, 4, 7 ; $9F - mapconst SEAFOAM_ISLANDS_B1F, 9, 15 ; $A0 - mapconst SEAFOAM_ISLANDS_B2F, 9, 15 ; $A1 - mapconst SEAFOAM_ISLANDS_B3F, 9, 15 ; $A2 - mapconst SEAFOAM_ISLANDS_B4F, 9, 15 ; $A3 - mapconst VERMILION_OLD_ROD_HOUSE, 4, 4 ; $A4 - Now unused - mapconst FUCHSIA_GOOD_ROD_HOUSE, 4, 4 ; $A5 - mapconst POKEMON_MANSION_1F, 14, 15 ; $A6 - mapconst CINNABAR_GYM, 9, 10 ; $A7 - mapconst CINNABAR_LAB, 4, 9 ; $A8 - mapconst CINNABAR_LAB_TRADE_ROOM, 4, 4 ; $A9 - mapconst CINNABAR_LAB_METRONOME_ROOM, 4, 4 ; $AA - mapconst CINNABAR_LAB_FOSSIL_ROOM, 4, 4 ; $AB - mapconst CINNABAR_POKECENTER, 4, 7 ; $AC - mapconst CINNABAR_MART, 4, 4 ; $AD - mapconst CINNABAR_MART_COPY, 4, 4 ; $AE - Unused - mapconst INDIGO_PLATEAU_LOBBY, 6, 8 ; $AF - mapconst COPYCATS_HOUSE_1F, 4, 4 ; $B0 - mapconst COPYCATS_HOUSE_2F, 4, 4 ; $B1 - mapconst FIGHTING_DOJO, 6, 5 ; $B2 - mapconst SAFFRON_GYM, 9, 10 ; $B3 - mapconst SAFFRON_PIDGEY_HOUSE, 4, 4 ; $B4 - mapconst SAFFRON_MART, 4, 4 ; $B5 - mapconst SILPH_CO_1F, 9, 15 ; $B6 - mapconst SAFFRON_POKECENTER, 4, 7 ; $B7 - mapconst MR_PSYCHICS_HOUSE, 4, 4 ; $B8 - mapconst ROUTE_15_GATE_1F, 5, 4 ; $B9 - mapconst ROUTE_15_GATE_2F, 4, 4 ; $BA - mapconst ROUTE_16_GATE_1F, 7, 4 ; $BB - mapconst ROUTE_16_GATE_2F, 4, 4 ; $BC - mapconst ROUTE_16_FLY_HOUSE, 4, 4 ; $BD - mapconst ROUTE_12_SUPER_ROD_HOUSE, 4, 4 ; $BE - mapconst ROUTE_18_GATE_1F, 5, 4 ; $BF - mapconst ROUTE_18_GATE_2F, 4, 4 ; $C0 - mapconst SEAFOAM_ISLANDS_1F, 9, 15 ; $C1 - mapconst ROUTE_22_GATE, 4, 5 ; $C2 - mapconst VICTORY_ROAD_2F, 9, 15 ; $C3 - mapconst ROUTE_12_GATE_2F, 4, 4 ; $C4 - mapconst VERMILION_TRADE_HOUSE, 4, 4 ; $C5 - mapconst DIGLETTS_CAVE, 18, 20 ; $C6 - mapconst VICTORY_ROAD_3F, 9, 15 ; $C7 - mapconst ROCKET_HIDEOUT_B1F, 14, 11 ; $C8 - mapconst ROCKET_HIDEOUT_B2F, 14, 15 ; $C9 - Currently unused - mapconst ROCKET_HIDEOUT_B3F, 14, 15 ; $CA - Currently unused - mapconst ROCKET_HIDEOUT_B4F, 12, 15 ; $CB - Currently unused - mapconst ROCKET_HIDEOUT_ELEVATOR, 4, 3 ; $CC - Currently unused - mapconst UNUSED_MAP_CC, 0, 0 ; $CD - Unused - mapconst UNUSED_MAP_CD, 0, 0 ; $CE - Unused - mapconst UNUSED_MAP_CE, 0, 0 ; $CF - Unused - mapconst SILPH_CO_2F, 9, 15 ; $D0 - mapconst SILPH_CO_3F, 9, 15 ; $D1 - mapconst SILPH_CO_4F, 9, 15 ; $D2 - mapconst SILPH_CO_5F, 9, 15 ; $D3 - mapconst SILPH_CO_6F, 9, 13 ; $D4 - mapconst SILPH_CO_7F, 9, 13 ; $D5 - mapconst SILPH_CO_8F, 9, 13 ; $D6 - mapconst POKEMON_MANSION_2F, 14, 15 ; $D7 - mapconst POKEMON_MANSION_3F, 9, 15 ; $D8 - mapconst POKEMON_MANSION_B1F, 14, 15 ; $D9 - mapconst SAFARI_ZONE_EAST, 13, 15 ; $DA - mapconst SAFARI_ZONE_NORTH, 18, 20 ; $DB - mapconst SAFARI_ZONE_WEST, 13, 15 ; $DC - mapconst SAFARI_ZONE_CENTER, 13, 15 ; $DD - mapconst SAFARI_ZONE_CENTER_REST_HOUSE, 4, 4 ; $DE - mapconst SAFARI_ZONE_SECRET_HOUSE, 4, 4 ; $DF - mapconst SAFARI_ZONE_WEST_REST_HOUSE, 4, 4 ; $E0 - mapconst SAFARI_ZONE_EAST_REST_HOUSE, 4, 4 ; $E1 - mapconst SAFARI_ZONE_NORTH_REST_HOUSE, 4, 4 ; $E2 - mapconst CERULEAN_CAVE_2F, 9, 15 ; $E3 - mapconst CERULEAN_CAVE_B1F, 9, 15 ; $E4 - mapconst CERULEAN_CAVE_1F, 9, 15 ; $E5 - mapconst NAME_RATERS_HOUSE, 4, 4 ; $E6 - mapconst CERULEAN_BADGE_HOUSE, 4, 4 ; $E7 - mapconst CINNABAR_VOLCANO, 12, 14 ; $E8 - Was UNUSED_MAP_E7 - mapconst ROCK_TUNNEL_B1F, 18, 20 ; $E9 - mapconst SILPH_CO_9F, 9, 13 ; $EA - mapconst SILPH_CO_10F, 9, 8 ; $EB - mapconst SILPH_CO_11F, 9, 9 ; $EC - mapconst SILPH_CO_ELEVATOR, 2, 2 ; $ED - mapconst UNUSED_MAP_ED, 0, 0 ; $EE - Unused - mapconst UNDERWATER_TUNNEL, 9, 15 ; $EF - Was UNUSED_MAP_EE - mapconst TRADE_CENTER, 4, 5 ; $F0 - mapconst COLOSSEUM, 4, 5 ; $F1 - mapconst CELADON_UNIVERSITY_INSIDE, 10, 11 ; $F2 - Was UNUSED_MAP_F1 - mapconst CELADON_UNIVERSITY_POKECENTER, 4, 7 ; $F3 - Was UNUSED_MAP_F2 - mapconst BILLS_GARDEN, 10, 10 ; $F4 - Was UNUSED_MAP_F3 - mapconst LORELEIS_ROOM, 6, 5 ; $F5 - mapconst BRUNOS_ROOM, 6, 5 ; $F6 - mapconst AGATHAS_ROOM, 6, 5 ; $F7 - Maps F8 - FE currently have no data, bear in mind we need room for everything in citrine + map_const REDS_HOUSE_1F, 4, 4 ; $26 + map_const REDS_HOUSE_2F, 4, 4 ; $27 + map_const BLUES_HOUSE, 4, 4 ; $28 + map_const OAKS_LAB, 7, 6 ; $29 + map_const VIRIDIAN_POKECENTER, 7, 4 ; $2A + map_const VIRIDIAN_MART, 4, 4 ; $2B + map_const VIRIDIAN_SCHOOL_HOUSE, 4, 4 ; $2C + map_const VIRIDIAN_NICKNAME_HOUSE, 4, 4 ; $2D + map_const VIRIDIAN_GYM, 10, 9 ; $2E + map_const DIGLETTS_CAVE_ROUTE_2, 4, 4 ; $2F + map_const VIRIDIAN_FOREST_NORTH_GATE, 5, 4 ; $30 + map_const ROUTE_2_TRADE_HOUSE, 4, 4 ; $31 + map_const ROUTE_2_GATE, 5, 4 ; $32 + map_const VIRIDIAN_FOREST_SOUTH_GATE, 5, 4 ; $33 + map_const VIRIDIAN_FOREST, 17, 24 ; $34 + map_const MUSEUM_1F, 10, 4 ; $35 + map_const MUSEUM_2F, 7, 4 ; $36 + map_const PEWTER_GYM, 5, 7 ; $37 + map_const PEWTER_NIDORAN_HOUSE, 4, 4 ; $38 + map_const PEWTER_MART, 4, 4 ; $39 + map_const PEWTER_SPEECH_HOUSE, 4, 4 ; $3A + map_const PEWTER_POKECENTER, 7, 4 ; $3B + map_const MT_MOON_1F, 20, 18 ; $3C + map_const MT_MOON_B1F, 14, 14 ; $3D + map_const MT_MOON_B2F, 20, 18 ; $3E + map_const CERULEAN_TRASHED_HOUSE, 4, 4 ; $3F + map_const CERULEAN_TRADE_HOUSE, 4, 4 ; $40 + map_const CERULEAN_POKECENTER, 7, 4 ; $41 + map_const CERULEAN_GYM, 5, 7 ; $42 + map_const BIKE_SHOP, 4, 4 ; $43 + map_const CERULEAN_MART, 4, 4 ; $44 + map_const MT_MOON_POKECENTER, 7, 4 ; $45 + map_const VIRIDIAN_PRE_GYM, 5, 7 ; $46 - was CERULEAN_TRASHED_HOUSE_COPY + map_const ROUTE_5_GATE, 4, 3 ; $47 + map_const UNDERGROUND_PATH_ROUTE_5, 4, 4 ; $48 + map_const DAYCARE, 4, 4 ; $49 + map_const ROUTE_6_GATE, 4, 3 ; $4A + map_const UNDERGROUND_PATH_ROUTE_6, 4, 4 ; $4B + map_const CELESTE_HILL, 12, 12 ; $4C - Was copy of above & unused + map_const ROUTE_7_GATE, 3, 4 ; $4D + map_const UNDERGROUND_PATH_ROUTE_7, 4, 4 ; $4E + map_const UNDERGROUND_PATH_ROUTE_7_COPY, 4, 4 ; $4F - Unused + map_const ROUTE_8_GATE, 3, 4 ; $50 + map_const UNDERGROUND_PATH_ROUTE_8, 4, 4 ; $51 + map_const ROCK_TUNNEL_POKECENTER, 7, 4 ; $52 + map_const ROCK_TUNNEL_1F, 20, 18 ; $53 + map_const POWER_PLANT, 20, 18 ; $54 + map_const ROUTE_11_GATE_1F, 4, 5 ; $55 + map_const DIGLETTS_CAVE_ROUTE_11, 4, 4 ; $56 + map_const ROUTE_11_GATE_2F, 4, 4 ; $57 + map_const ROUTE_12_GATE_1F, 5, 4 ; $58 + map_const BILLS_HOUSE, 7, 4 ; $59 + map_const VERMILION_POKECENTER, 7, 4 ; $5A + map_const POKEMON_FAN_CLUB, 4, 4 ; $5B + map_const VERMILION_MART, 4, 4 ; $5C + map_const VERMILION_GYM, 5, 9 ; $5D + map_const VERMILION_PIDGEY_HOUSE, 4, 4 ; $5E + map_const VERMILION_DOCK, 14, 6 ; $5F + map_const SS_ANNE_1F, 20, 9 ; $60 + map_const SS_ANNE_2F, 20, 9 ; $61 + map_const SS_ANNE_3F, 10, 3 ; $62 + map_const SS_ANNE_B1F, 15, 4 ; $63 + map_const SS_ANNE_BOW, 10, 7 ; $64 + map_const SS_ANNE_KITCHEN, 7, 8 ; $65 + map_const SS_ANNE_CAPTAINS_ROOM, 3, 4 ; $66 + map_const SS_ANNE_1F_ROOMS, 12, 8 ; $67 + map_const SS_ANNE_2F_ROOMS, 12, 8 ; $68 + map_const SS_ANNE_B1F_ROOMS, 12, 8 ; $69 + map_const SILPH_GAUNTLET_1F, 10, 9 ; $6A - Was UNUSED_MAP_69 + map_const SILPH_GAUNTLET_2F, 13, 10 ; $6B - Was UNUSED_MAP_6A + map_const SILPH_GAUNTLET_3F, 17, 13 ; $6C - Was UNUSED_MAP_6B + map_const VICTORY_ROAD_1F, 10, 9 ; $6D + map_const SILPH_GAUNTLET_4F, 10, 9 ; $6E - Was UNUSED_MAP_6D + map_const SILPH_GAUNTLET_5F, 10, 9 ; $6F - Was UNUSED_MAP_6E + map_const SILPH_GAUNTLET_6F, 12, 12 ; $70 - Was UNUSED_MAP_6F + map_const SILPH_GAUNTLET_7F, 6, 5 ; $71 - Was UNUSED_MAP_70 + map_const LANCES_ROOM, 13, 13 ; $72 + map_const FARAWAY_ISLAND_OUTSIDE, 15, 18 ; $73 - Was UNUSED_MAP_72 + map_const FARAWAY_ISLAND_INSIDE, 15, 11 ; $74 - Was UNUSED_MAP_73 + map_const POKEMON_MANSION_B2F, 15, 12 ; $75 - Was UNUSED_MAP_74 + map_const MT_MOON_CRATER, 25, 25 ; $76 - Was UNUSED_MAP_75 + map_const HALL_OF_FAME, 5, 4 ; $77 + map_const UNDERGROUND_PATH_NORTH_SOUTH, 4, 24 ; $78 ; UndergroundPathNorthSouth.blk is actually 4x23 + map_const CHAMPIONS_ROOM, 4, 4 ; $79 + map_const UNDERGROUND_PATH_WEST_EAST, 25, 4 ; $7A + map_const CELADON_MART_1F, 10, 4 ; $7B + map_const CELADON_MART_2F, 10, 4 ; $7C + map_const CELADON_MART_3F, 10, 4 ; $7D + map_const CELADON_MART_4F, 10, 4 ; $7E + map_const CELADON_MART_ROOF, 10, 4 ; $7F + map_const CELADON_MART_ELEVATOR, 2, 2 ; $80 + map_const CELADON_MANSION_1F, 4, 6 ; $81 + map_const CELADON_MANSION_2F, 4, 6 ; $82 + map_const CELADON_MANSION_3F, 4, 6 ; $83 + map_const CELADON_MANSION_ROOF, 4, 6 ; $84 + map_const CELADON_MANSION_ROOF_HOUSE, 4, 4 ; $85 + map_const CELADON_POKECENTER, 7, 4 ; $86 + map_const CELADON_GYM, 5, 9 ; $87 + map_const GAME_CORNER, 10, 9 ; $88 + map_const CELADON_MART_5F, 10, 4 ; $89 + map_const GAME_CORNER_PRIZE_ROOM, 5, 4 ; $8A + map_const CELADON_DINER, 5, 4 ; $8B + map_const CELADON_CHIEF_HOUSE, 4, 4 ; $8C + map_const CELADON_HOTEL, 7, 4 ; $8D + map_const LAVENDER_POKECENTER, 7, 4 ; $8E + map_const POKEMON_TOWER_1F, 10, 9 ; $8F + map_const POKEMON_TOWER_2F, 10, 9 ; $90 + map_const POKEMON_TOWER_3F, 10, 9 ; $91 - Currently unused + map_const POKEMON_TOWER_4F, 10, 9 ; $92 - Currently unused + map_const POKEMON_TOWER_5F, 10, 9 ; $93 - Currently unused + map_const POKEMON_TOWER_6F, 10, 9 ; $94 + map_const POKEMON_TOWER_7F, 10, 9 ; $95 + map_const MR_FUJIS_HOUSE, 4, 4 ; $96 + map_const LAVENDER_MART, 4, 4 ; $97 + map_const LAVENDER_CUBONE_HOUSE, 4, 4 ; $98 + map_const FUCHSIA_MART, 4, 4 ; $99 + map_const FUCHSIA_BILLS_GRANDPAS_HOUSE, 4, 4 ; $9A + map_const FUCHSIA_POKECENTER, 7, 4 ; $9B + map_const WARDENS_HOUSE, 5, 4 ; $9C + map_const SAFARI_ZONE_GATE, 4, 3 ; $9D + map_const FUCHSIA_GYM, 5, 9 ; $9E + map_const FUCHSIA_MEETING_ROOM, 7, 4 ; $9F + map_const SEAFOAM_ISLANDS_B1F, 15, 9 ; $A0 + map_const SEAFOAM_ISLANDS_B2F, 15, 9 ; $A1 + map_const SEAFOAM_ISLANDS_B3F, 15, 9 ; $A2 + map_const SEAFOAM_ISLANDS_B4F, 15, 9 ; $A3 + map_const VERMILION_OLD_ROD_HOUSE, 4, 4 ; $A4 - Now unused + map_const FUCHSIA_GOOD_ROD_HOUSE, 4, 4 ; $A5 + map_const POKEMON_MANSION_1F, 15, 14 ; $A6 + map_const CINNABAR_GYM, 10, 9 ; $A7 + map_const CINNABAR_LAB, 9, 4 ; $A8 + map_const CINNABAR_LAB_TRADE_ROOM, 4, 4 ; $A9 + map_const CINNABAR_LAB_METRONOME_ROOM, 4, 4 ; $AA + map_const CINNABAR_LAB_FOSSIL_ROOM, 4, 4 ; $AB + map_const CINNABAR_POKECENTER, 7, 4 ; $AC + map_const CINNABAR_MART, 4, 4 ; $AD + map_const CINNABAR_MART_COPY, 4, 4 ; $AE - Unused + map_const INDIGO_PLATEAU_LOBBY, 8, 6 ; $AF + map_const COPYCATS_HOUSE_1F, 4, 4 ; $B0 + map_const COPYCATS_HOUSE_2F, 4, 4 ; $B1 + map_const FIGHTING_DOJO, 5, 6 ; $B2 + map_const SAFFRON_GYM, 10, 9 ; $B3 + map_const SAFFRON_PIDGEY_HOUSE, 4, 4 ; $B4 + map_const SAFFRON_MART, 4, 4 ; $B5 + map_const SILPH_CO_1F, 15, 9 ; $B6 + map_const SAFFRON_POKECENTER, 7, 4 ; $B7 + map_const MR_PSYCHICS_HOUSE, 4, 4 ; $B8 + map_const ROUTE_15_GATE_1F, 4, 5 ; $B9 + map_const ROUTE_15_GATE_2F, 4, 4 ; $BA + map_const ROUTE_16_GATE_1F, 4, 7 ; $BB + map_const ROUTE_16_GATE_2F, 4, 4 ; $BC + map_const ROUTE_16_FLY_HOUSE, 4, 4 ; $BD + map_const ROUTE_12_SUPER_ROD_HOUSE, 4, 4 ; $BE + map_const ROUTE_18_GATE_1F, 4, 5 ; $BF + map_const ROUTE_18_GATE_2F, 4, 4 ; $C0 + map_const SEAFOAM_ISLANDS_1F, 15, 9 ; $C1 + map_const ROUTE_22_GATE, 5, 4 ; $C2 + map_const VICTORY_ROAD_2F, 15, 9 ; $C3 + map_const ROUTE_12_GATE_2F, 4, 4 ; $C4 + map_const VERMILION_TRADE_HOUSE, 4, 4 ; $C5 + map_const DIGLETTS_CAVE, 20, 18 ; $C6 + map_const VICTORY_ROAD_3F, 15, 9 ; $C7 + map_const ROCKET_HIDEOUT_B1F, 11, 14 ; $C8 + map_const ROCKET_HIDEOUT_B2F, 15, 14 ; $C9 - Currently unused + map_const ROCKET_HIDEOUT_B3F, 15, 14 ; $CA - Currently unused + map_const ROCKET_HIDEOUT_B4F, 15, 12 ; $CB - Currently unused + map_const ROCKET_HIDEOUT_ELEVATOR, 3, 4 ; $CC - Currently unused + map_const UNUSED_MAP_CC, 0, 0 ; $CD - Unused + map_const UNUSED_MAP_CD, 0, 0 ; $CE - Unused + map_const UNUSED_MAP_CE, 0, 0 ; $CF - Unused + map_const SILPH_CO_2F, 15, 9 ; $D0 + map_const SILPH_CO_3F, 15, 9 ; $D1 + map_const SILPH_CO_4F, 15, 9 ; $D2 + map_const SILPH_CO_5F, 15, 9 ; $D3 + map_const SILPH_CO_6F, 13, 9 ; $D4 + map_const SILPH_CO_7F, 13, 9 ; $D5 + map_const SILPH_CO_8F, 13, 9 ; $D6 + map_const POKEMON_MANSION_2F, 15, 14 ; $D7 + map_const POKEMON_MANSION_3F, 15, 9 ; $D8 + map_const POKEMON_MANSION_B1F, 15, 14 ; $D9 + map_const SAFARI_ZONE_EAST, 15, 13 ; $DA + map_const SAFARI_ZONE_NORTH, 20, 18 ; $DB + map_const SAFARI_ZONE_WEST, 15, 13 ; $DC + map_const SAFARI_ZONE_CENTER, 15, 13 ; $DD + map_const SAFARI_ZONE_CENTER_REST_HOUSE, 4, 4 ; $DE + map_const SAFARI_ZONE_SECRET_HOUSE, 4, 4 ; $DF + map_const SAFARI_ZONE_WEST_REST_HOUSE, 4, 4 ; $E0 + map_const SAFARI_ZONE_EAST_REST_HOUSE, 4, 4 ; $E1 + map_const SAFARI_ZONE_NORTH_REST_HOUSE, 4, 4 ; $E2 + map_const CERULEAN_CAVE_2F, 15, 9 ; $E3 + map_const CERULEAN_CAVE_B1F, 15, 9 ; $E4 + map_const CERULEAN_CAVE_1F, 15, 9 ; $E5 + map_const NAME_RATERS_HOUSE, 4, 4 ; $E6 + map_const CERULEAN_BADGE_HOUSE, 4, 4 ; $E7 + map_const CINNABAR_VOLCANO, 14, 12 ; $E8 - Was UNUSED_MAP_E7 + map_const ROCK_TUNNEL_B1F, 20, 18 ; $E9 + map_const SILPH_CO_9F, 13, 9 ; $EA + map_const SILPH_CO_10F, 8, 9 ; $EB + map_const SILPH_CO_11F, 9, 9 ; $EC + map_const SILPH_CO_ELEVATOR, 2, 2 ; $ED + map_const UNUSED_MAP_ED, 0, 0 ; $EE - Unused + map_const UNDERWATER_TUNNEL, 15, 9 ; $EF - Was UNUSED_MAP_EE + map_const TRADE_CENTER, 5, 4 ; $F0 + map_const COLOSSEUM, 5, 4 ; $F1 + map_const CELADON_UNIVERSITY_INSIDE, 11, 10 ; $F2 - Was UNUSED_MAP_F1 + map_const CELADON_UNIVERSITY_POKECENTER, 7, 4 ; $F3 - Was UNUSED_MAP_F2 + map_const BILLS_GARDEN, 10, 10 ; $F4 - Was UNUSED_MAP_F3 + map_const LORELEIS_ROOM, 5, 6 ; $F5 + map_const BRUNOS_ROOM, 5, 6 ; $F6 + map_const AGATHAS_ROOM, 5, 6 ; $F7 - Maps F8 - FE currently have no data, bear in mind we need room for everything in citrine DEF NUM_MAPS EQU const_value ; Indoor maps, such as houses, use this as the Map ID in their exit warps diff --git a/constants/map_object_constants.asm b/constants/map_object_constants.asm index a043671c..406e351d 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/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/constants/move_constants.asm b/constants/move_constants.asm index 8e5e8856..4461a870 100644 --- a/constants/move_constants.asm +++ b/constants/move_constants.asm @@ -191,14 +191,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 @@ -210,18 +210,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/constants/pokemon_data_constants.asm b/constants/pokemon_data_constants.asm index 5f207682..0468c007 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/constants/script_constants.asm b/constants/script_constants.asm index 9dd6ee84..d36cfd3c 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 @@ -57,7 +57,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 const TRADE_DIALOGSET_SELF const TRADE_DIALOGSET_REGION ; consistency with LGPE diff --git a/constants/sprite_set_constants.asm b/constants/sprite_set_constants.asm new file mode 100644 index 00000000..56e4ccbb --- /dev/null +++ b/constants/sprite_set_constants.asm @@ -0,0 +1,41 @@ +; 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 + +; each sprite set has 9 walking sprites and 2 still sprites +DEF SPRITE_SET_LENGTH EQU 9 + 2 diff --git a/constants/type_constants.asm b/constants/type_constants.asm index 494e6dce..6f8a3f77 100644 --- a/constants/type_constants.asm +++ b/constants/type_constants.asm @@ -13,7 +13,9 @@ DEF PHYSICAL EQU const_value const GHOST ; $08 const STEEL ; $09 +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/battle_anims/subanimations.asm b/data/battle_anims/subanimations.asm index d67d4641..74097e22 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,8 +546,8 @@ Subanimation22: db FRAMEBLOCK_47, BASECOORD_89, FRAMEBLOCKMODE_00 db FRAMEBLOCK_47, BASECOORD_B0, FRAMEBLOCKMODE_00 -Subanimation2d: - subanim SUBANIMTYPE_COORDFLIP, 6 +Subanim_0CirclesCentering: + 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 @@ -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/events/trades.asm b/data/events/trades.asm index 26831cc3..1b531536 100644 --- a/data/events/trades.asm +++ b/data/events/trades.asm @@ -2,35 +2,35 @@ TradeMons: ; entries correspond to TRADE_FOR_* constants table_width 3 + NAME_LENGTH, TradeMons ; give mon, get mon, dialog id, nickname - db POLIWHIRL, JYNX, TRADE_DIALOGSET_CASUAL, "LOLA@@@@@@@" ; Jynx trade replaces the useless Nidorino one - db ABRA, MR_MIME, TRADE_DIALOGSET_CASUAL, "MARCEL@@@@@" - db BUTTERFREE, BEEDRILL, TRADE_DIALOGSET_HAPPY, "CHIKUCHIKU@" - db HAUNTER, KADABRA, TRADE_DIALOGSET_POLITE, "HARRY@@@@@@" ; New trade to complete the 4 main trade evos - db SPEAROW, FARFETCHD, TRADE_DIALOGSET_HAPPY, "DUX@@@@@@@@" - db PERSIAN, TAUROS, TRADE_DIALOGSET_CASUAL, "JIMBO@@@@@@" ; From JP Blue - db MACHOKE, HAUNTER, TRADE_DIALOGSET_POLITE, "MICHELLE@@@" ; From JP Blue - db KADABRA, GRAVELER, TRADE_DIALOGSET_POLITE, "JENNY@@@@@@" ; From JP Blue - db VENONAT, TANGELA, TRADE_DIALOGSET_HAPPY, "CRINKLES@@@" - db CUBONE, MACHOKE, TRADE_DIALOGSET_HAPPY, "RICKY@@@@@@" ; From Yellow - db NO_MON, NO_MON, TRADE_DIALOGSET_SELF, "Unseen@@@@@" ; Celadon Uni Trader - db RATICATE, RATICATE_A, TRADE_DIALOGSET_REGION, "RATICATE@@@" ; Viridian - LGPE trades begin here - db GOLEM, GOLEM_A, TRADE_DIALOGSET_REGION, "GOLEM@@@@@@" ; Pewter - db NINETALES, NINETALES_A, TRADE_DIALOGSET_REGION, "NINETALES@@" ; Cerulean - db PERSIAN, PERSIAN_A, TRADE_DIALOGSET_REGION, "PERSIAN@@@@" ; Vermillion - db DUGTRIO, DUGTRIO_A, TRADE_DIALOGSET_REGION, "DUGTRIO@@@@" ; Lavender - db SANDSLASH, SANDSLASH_A, TRADE_DIALOGSET_REGION, "SANDSLASH@@" ; Celadon - db RAICHU, RAICHU_A, TRADE_DIALOGSET_REGION, "RAICHU@@@@@" ; Saffron - db MAROWAK, MAROWAK_A, TRADE_DIALOGSET_REGION, "MAROWAK@@@@" ; Fuschia - db MUK, MUK_A, TRADE_DIALOGSET_REGION, "MUK-A@@@@@@" ; Cinnabar - db EXEGGUTOR, EXEGGUTOR_A, TRADE_DIALOGSET_REGION, "EXEGGUTOR@@" ; Indigo - db TAUROS, TAUROS_P, TRADE_DIALOGSET_TAUROSC, "TAUROS@@@@@" ; S.S. Anne begins here - db TAUROS, TAUROS_PA, TRADE_DIALOGSET_TAUROSA, "TAUROS@@@@@" - db TAUROS, TAUROS_PB, TRADE_DIALOGSET_TAUROSB, "TAUROS@@@@@" - db RAPIDASH, RAPIDASH_G, TRADE_DIALOGSET_REGION, "RAPIDASH@@@" - db SLOWBRO, SLOWBRO_G, TRADE_DIALOGSET_REGION, "SLOWBRO@@@@" - db SLOWKING, SLOWKING_G, TRADE_DIALOGSET_REGION, "SLOWKING@@@" - db WEEZING, WEEZING_G, TRADE_DIALOGSET_REGION, "WEEZING@@@@" - db MR_MIME, MR_RIME, TRADE_DIALOGSET_CASUAL, "MR.RIME@@@@" - db PERSIAN, PERRSERKER, TRADE_DIALOGSET_HAPPY, "PERRSERKER@" - db MADAAMU, SIRFETCHD, TRADE_DIALOGSET_HAPPY, "SIRFETCH'D@" + db POLIWHIRL, JYNX, TRADE_DIALOGSET_CASUAL, "LOLA@@@@@@@" ; Jynx trade replaces the useless Nidorino one + db ABRA, MR_MIME, TRADE_DIALOGSET_CASUAL, "MARCEL@@@@@" + db BUTTERFREE, BEEDRILL, TRADE_DIALOGSET_HAPPY, "CHIKUCHIKU@" + db HAUNTER, KADABRA, TRADE_DIALOGSET_EVOLUTION, "HARRY@@@@@@" ; New trade to complete the 4 main trade evos + db SPEAROW, FARFETCHD, TRADE_DIALOGSET_HAPPY, "DUX@@@@@@@@" + db PERSIAN, TAUROS, TRADE_DIALOGSET_CASUAL, "JIMBO@@@@@@" ; From JP Blue + db MACHOKE, HAUNTER, TRADE_DIALOGSET_EVOLUTION, "MICHELLE@@@" ; From JP Blue + db KADABRA, GRAVELER, TRADE_DIALOGSET_EVOLUTION, "JENNY@@@@@@" ; From JP Blue + db VENONAT, TANGELA, TRADE_DIALOGSET_HAPPY, "CRINKLES@@@" + db CUBONE, MACHOKE, TRADE_DIALOGSET_HAPPY, "RICKY@@@@@@" ; From Yellow + db NO_MON, NO_MON, TRADE_DIALOGSET_SELF, "Unseen@@@@@" ; Celadon Uni Trader + db RATICATE, RATICATE_A, TRADE_DIALOGSET_REGION, "RATICATE@@@" ; Viridian - LGPE trades begin here + db GOLEM, GOLEM_A, TRADE_DIALOGSET_REGION, "GOLEM@@@@@@" ; Pewter + db NINETALES, NINETALES_A, TRADE_DIALOGSET_REGION, "NINETALES@@" ; Cerulean + db PERSIAN, PERSIAN_A, TRADE_DIALOGSET_REGION, "PERSIAN@@@@" ; Vermillion + db DUGTRIO, DUGTRIO_A, TRADE_DIALOGSET_REGION, "DUGTRIO@@@@" ; Lavender + db SANDSLASH, SANDSLASH_A, TRADE_DIALOGSET_REGION, "SANDSLASH@@" ; Celadon + db RAICHU, RAICHU_A, TRADE_DIALOGSET_REGION, "RAICHU@@@@@" ; Saffron + db MAROWAK, MAROWAK_A, TRADE_DIALOGSET_REGION, "MAROWAK@@@@" ; Fuschia + db MUK, MUK_A, TRADE_DIALOGSET_REGION, "MUK-A@@@@@@" ; Cinnabar + db EXEGGUTOR, EXEGGUTOR_A, TRADE_DIALOGSET_REGION, "EXEGGUTOR@@" ; Indigo + db TAUROS, TAUROS_P, TRADE_DIALOGSET_TAUROSC, "TAUROS@@@@@" ; S.S. Anne begins here + db TAUROS, TAUROS_PA, TRADE_DIALOGSET_TAUROSA, "TAUROS@@@@@" + db TAUROS, TAUROS_PB, TRADE_DIALOGSET_TAUROSB, "TAUROS@@@@@" + db RAPIDASH, RAPIDASH_G, TRADE_DIALOGSET_REGION, "RAPIDASH@@@" + db SLOWBRO, SLOWBRO_G, TRADE_DIALOGSET_REGION, "SLOWBRO@@@@" + db SLOWKING, SLOWKING_G, TRADE_DIALOGSET_REGION, "SLOWKING@@@" + db WEEZING, WEEZING_G, TRADE_DIALOGSET_REGION, "WEEZING@@@@" + db MR_MIME, MR_RIME, TRADE_DIALOGSET_CASUAL, "MR.RIME@@@@" + db PERSIAN, PERRSERKER, TRADE_DIALOGSET_HAPPY, "PERRSERKER@" + db MADAAMU, SIRFETCHD, TRADE_DIALOGSET_HAPPY, "SIRFETCH'D@" assert_table_length NUM_NPC_TRADES diff --git a/data/maps/sprite_sets.asm b/data/maps/sprite_sets.asm index e9602f5e..36c66d52 100644 --- a/data/maps/sprite_sets.asm +++ b/data/maps/sprite_sets.asm @@ -1,74 +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 $04 ; VERMILION_CITY - db $03 ; LAVENDER_TOWN - db $05 ; CELADON_CITY - db $05 ; CELADON_UNIVERSITY_OUTSIDE - db $0a ; FUCHSIA_CITY - db $07 ; SAFFRON_CITY - db $01 ; CINNABAR_ISLAND - db $06 ; INDIGO_PLATEAU - db $01 ; CITRINE_CITY - 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_VERMILION ; VERMILION_CITY + db SPRITESET_LAVENDER ; LAVENDER_TOWN + db SPRITESET_CELADON ; CELADON_CITY + db SPRITESET_CELADON ; CELADON_UNIVERSITY_OUTSIDE + db SPRITESET_FUCHSIA ; FUCHSIA_CITY + db SPRITESET_SAFFRON ; SAFFRON_CITY + db SPRITESET_PALLET_VIRIDIAN ; CINNABAR_ISLAND + db SPRITESET_INDIGO ; INDIGO_PLATEAU + db SPRITESET_PALLET_VIRIDIAN ; CITRINE_CITY + 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: + table_width SPRITE_SET_LENGTH, SpriteSets -; each sprite set has 9 walking sprites and 2 still sprites -DEF SPRITE_SET_LENGTH EQU 9 + 2 - -; sprite set $01 - Viridian - table_width 1 +; SPRITESET_PALLET_VIRIDIAN db SPRITE_BLUE db SPRITE_YOUNGSTER db SPRITE_GIRL @@ -80,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 - Cerulean - table_width 1 +; SPRITESET_PEWTER_CERULEAN db SPRITE_YOUNGSTER db SPRITE_ROCKET db SPRITE_SUPER_NERD @@ -95,10 +91,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_COOLTRAINER_M db SPRITE_POKE_BALL db SPRITE_FAKE_TREE ; fake tree, used to be unused sleeping gambler - assert_table_length SPRITE_SET_LENGTH -; sprite set $03 - Lavender Town - table_width 1 +; SPRITESET_LAVENDER db SPRITE_LITTLE_GIRL db SPRITE_GIRL db SPRITE_SUPER_NERD @@ -110,10 +104,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_GUARD db SPRITE_POKE_BALL db SPRITE_FAKE_TREE ; fake tree, used to be unused sleeping gambler - assert_table_length SPRITE_SET_LENGTH -; sprite set $04 - Vermillion City - table_width 1 +; SPRITESET_VERMILION db SPRITE_BEAUTY db SPRITE_SUPER_NERD db SPRITE_YOUNGSTER @@ -125,10 +117,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_COOLTRAINER_M db SPRITE_POKE_BALL db SPRITE_FAKE_TREE ; fake tree, used to be unused sleeping gambler - assert_table_length SPRITE_SET_LENGTH -; sprite set $05 - Celadon City - table_width 1 +; SPRITESET_CELADON db SPRITE_LITTLE_GIRL db SPRITE_COOLTRAINER_F ; Original: SPRITE_LITTLE_BOY db SPRITE_GIRL @@ -140,10 +130,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_ROCKET db SPRITE_SEEL ; Original: SPRITE_POKE_BALL (static sprite) db SPRITE_SNORLAX - assert_table_length SPRITE_SET_LENGTH -; sprite set $06 - Indigo Plateau - table_width 1 +; SPRITESET_INDIGO db SPRITE_YOUNGSTER db SPRITE_GYM_GUIDE db SPRITE_MONSTER @@ -155,10 +143,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_GAMBLER db SPRITE_POKE_BALL db SPRITE_FAKE_TREE ; fake tree, used to be unused sleeping gambler - assert_table_length SPRITE_SET_LENGTH -; sprite set $07 - Saffron - table_width 1 +; SPRITESET_SAFFRON db SPRITE_ROCKET db SPRITE_SCIENTIST db SPRITE_SILPH_WORKER @@ -170,10 +156,8 @@ DEF SPRITE_SET_LENGTH EQU 9 + 2 db SPRITE_MONSTER db SPRITE_POKE_BALL db SPRITE_FAKE_TREE ; fake tree, used to be unused sleeping gambler - assert_table_length SPRITE_SET_LENGTH -; sprite set $08 - Silence Bridge - table_width 1 +; SPRITESET_SILENCE_BRIDGE db SPRITE_BIKER db SPRITE_SUPER_NERD db SPRITE_MIDDLE_AGED_MAN @@ -185,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 - Cycling Road - table_width 1 +; SPRITESET_CYCLING_ROAD db SPRITE_BIKER db SPRITE_COOLTRAINER_M db SPRITE_SILPH_WORKER @@ -200,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 - Fuschia - table_width 1 +; SPRITESET_FUCHSIA db SPRITE_BIRD db SPRITE_COOLTRAINER_M db SPRITE_FAIRY @@ -215,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/data/moves/animations.asm b/data/moves/animations.asm index 0fa307a6..824359a6 100644 --- a/data/moves/animations.asm +++ b/data/moves/animations.asm @@ -205,7 +205,7 @@ AttackAnimationPointers: dw SleepEnemyAnim dw ConfusedPlayerAnim dw ConfusedEnemyAnim - dw FaintAnim + dw SlideDownAnim dw BallTossAnim dw BallShakeAnim dw BallPoofAnim @@ -238,164 +238,167 @@ 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 BulletPunchAnim: 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 NightSlashAnim: 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 FairyWindAnim: 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: @@ -411,9 +414,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: @@ -423,13 +426,13 @@ 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 IronHeadAnim: 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 @@ -447,16 +450,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: @@ -467,34 +470,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: @@ -505,18 +508,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: @@ -526,40 +529,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 @@ -567,50 +570,50 @@ 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 BrutalSwingAnim: 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 @@ -618,29 +621,29 @@ 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 DrainingKissAnim: 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: @@ -651,25 +654,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: @@ -679,48 +682,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: @@ -736,13 +739,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: @@ -760,7 +763,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 @@ -773,12 +776,12 @@ AgilityAnim: FeintAttackAnim: 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: @@ -792,13 +795,13 @@ 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 MetalSoundAnim: ScreechAnim: - battle_anim SCREECH, SUBANIM_12, 1, 6 + battle_anim SCREECH, SUBANIM_0_HEART_1_MUSIC, 1, 6 db -1 ; end DoubleTeamAnim: @@ -810,7 +813,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: @@ -823,7 +826,7 @@ RecoverAnim: IronDefenseAnim: 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 @@ -836,8 +839,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 @@ -855,34 +858,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 @@ -894,8 +897,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 @@ -904,7 +907,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: @@ -918,89 +921,89 @@ 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 MagnetBombAnim: 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 @@ -1008,7 +1011,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: @@ -1021,60 +1024,60 @@ 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 SweetKissAnim: CharmAnim: 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 DazzleGleamAnim: @@ -1086,7 +1089,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 @@ -1099,91 +1102,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: @@ -1207,19 +1210,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: @@ -1230,7 +1233,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 @@ -1242,7 +1245,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 @@ -1253,40 +1256,40 @@ 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: +SlideDownAnim: battle_anim DIG, SE_SLIDE_MON_DOWN db -1 ; end @@ -1295,9 +1298,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/data/pokemon/dex_entries.asm b/data/pokemon/dex_entries.asm index 86368913..aabf1e26 100644 --- a/data/pokemon/dex_entries.asm +++ b/data/pokemon/dex_entries.asm @@ -258,7 +258,7 @@ PokedexEntryPointers: ; string: species name ; height in feet, inches -; weight in pounds +; weight in tenths of a pound ; text entry BulbasaurDexEntry: diff --git a/data/trainers/names.asm b/data/trainers/names.asm index 07d69ef7..3633d0e7 100644 --- a/data/trainers/names.asm +++ b/data/trainers/names.asm @@ -1,52 +1,54 @@ 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 "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@" - db "YUJIROU@" - db "STUDENT@" - db "FIREFIGHTER@" - db "JUNIOR@" - db "JACK@" + 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 "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" + li "YUJIROU" + li "STUDENT" + li "FIREFIGHTER" + li "JUNIOR" + li "JACK" + assert_list_length NUM_TRAINERS diff --git a/data/trainers/parties.asm b/data/trainers/parties.asm index f45ec869..eb3e92aa 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 @@ -50,6 +51,7 @@ TrainerDataPointers: dw FirefighterData dw JuniorData dw JackData + assert_table_length NUM_TRAINERS ; if first byte != $FF, then ; first byte is level (of all pokemon on this team) diff --git a/data/types/names.asm b/data/types/names.asm index fb5ca4a9..07561c15 100644 --- a/data/types/names.asm +++ b/data/types/names.asm @@ -12,7 +12,7 @@ TypeNames: dw .Ghost dw .Steel -REPT FIRE - STEEL - 1 +REPT UNUSED_TYPES_END - UNUSED_TYPES dw .Normal ENDR diff --git a/engine/battle/animations.asm b/engine/battle/animations.asm index a85fa459..57cfff46 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: INCBIN "gfx/slots/slots_2.2bpp" @@ -1109,7 +1110,7 @@ AnimationWaterDropletsEverywhere: ; in Surf/Mist/Toxic. xor a ld [wWhichBattleAnimTileset], a - call LoadAnimationTileset + call LoadMoveAnimationTiles ld d, 32 ld a, -16 ld [wBaseCoordX], a @@ -1628,7 +1629,7 @@ _AnimationShootBallsUpward: push bc xor a ld [wWhichBattleAnimTileset], a - call LoadAnimationTileset + call LoadMoveAnimationTiles pop bc ld d, $7a ; ball tile ld hl, wShadowOAM @@ -2071,7 +2072,7 @@ InitMultipleObjectsOAM: push bc push de ld [wWhichBattleAnimTileset], a - call LoadAnimationTileset + call LoadMoveAnimationTiles pop de pop bc xor a diff --git a/engine/battle/core.asm b/engine/battle/core.asm index 29b69e71..51f5c6b7 100644 --- a/engine/battle/core.asm +++ b/engine/battle/core.asm @@ -3353,7 +3353,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 @@ -3437,7 +3437,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 b48a9137..36a9e956 100644 --- a/engine/battle/trainer_ai.asm +++ b/engine/battle/trainer_ai.asm @@ -750,7 +750,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 diff --git a/engine/debug/debug_menu.asm b/engine/debug/debug_menu.asm index 84f55cb4..486d389c 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 619ffc36..e362624e 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 202fb7be..042429de 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/splash.asm b/engine/movie/splash.asm index 7e75906e..0abb7b81 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/movie/title.asm b/engine/movie/title.asm index ac5546c6..869b5f1c 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 @@ -395,5 +397,8 @@ PrintGameVersionOnTitleScreen: VersionOnTitleScreenText: db $61,$62,$63,$64,$65,$66,$67,$68,"@" ; "Blue Version" -NintenText: db "NINTEN@" -SonyText: db "SONY@" +DebugNewGamePlayerName: + db "NINTEN@" + +DebugNewGameRivalName: + db "SONY@" diff --git a/engine/overworld/cut.asm b/engine/overworld/cut.asm index 6020b8f1..acbc1edf 100644 --- a/engine/overworld/cut.asm +++ b/engine/overworld/cut.asm @@ -116,8 +116,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/engine/overworld/map_sprites.asm b/engine/overworld/map_sprites.asm index fa3a37dc..8acbb04f 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] @@ -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 @@ -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 @@ -309,11 +309,11 @@ 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 - ld a, $10 + ld a, SPRITESTATEDATA2_LENGTH add l ld l, a xor a @@ -322,20 +322,20 @@ 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. .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/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 diff --git a/home.asm b/home.asm index 08496b1e..8c996094 100644 --- a/home.asm +++ b/home.asm @@ -1,6 +1,3 @@ -INCLUDE "constants.asm" - - SECTION "NULL", ROM0 NULL:: 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 diff --git a/constants.asm b/includes.asm similarity index 76% rename from constants.asm rename to includes.asm index a307549b..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" @@ -28,6 +41,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/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/macros/wram.asm b/macros/ram.asm similarity index 100% rename from macros/wram.asm rename to macros/ram.asm diff --git a/main.asm b/main.asm index 89da2734..980171e7 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 b9e06dfa..21cdc557 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 24040b14..cfeb5997 100644 --- a/ram.asm +++ b/ram.asm @@ -1,6 +1,4 @@ -INCLUDE "constants.asm" - -INCLUDE "macros/wram.asm" +INCLUDE "macros/ram.asm" INCLUDE "ram/vram.asm" diff --git a/ram/wram.asm b/ram/wram.asm index a393461e..a22fb445 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 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/scripts/HallOfFame.asm b/scripts/HallOfFame.asm index 8aefb311..9386758c 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 9bc75e34..033c1558 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: diff --git a/text.asm b/text.asm index 47f90bd6..dc134245 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/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/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/free_space.awk b/tools/free_space.awk new file mode 100755 index 00000000..0e157858 --- /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/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/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/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); 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..e6426aaf 100644 --- 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[len('pokered_obj :='):].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("