From d6001be90c58d441be91668dc5240abe203e5b50 Mon Sep 17 00:00:00 2001 From: xCrystal Date: Wed, 26 Jul 2023 20:50:16 -0400 Subject: [PATCH] Sync with pokecrystal up to 8873506b1 --- README.md | 44 ++++++++++--------- constants/battle_anim_constants.asm | 3 ++ constants/battle_constants.asm | 2 +- constants/map_object_constants.asm | 1 + constants/script_constants.asm | 3 +- constants/sprite_anim_constants.asm | 3 ++ data/maps/attributes.asm | 28 ++++++------ data/types/names.asm | 13 +++--- docs/bugs_and_glitches.md | 28 ++++++++++++ engine/battle/ai/items.asm | 1 + engine/battle/core.asm | 4 +- engine/battle/effect_commands.asm | 10 ++--- engine/battle/move_effects/teleport.asm | 2 +- engine/battle/trainer_huds.asm | 20 ++++----- engine/battle_anims/core.asm | 6 +-- engine/events/field_moves.asm | 2 +- engine/games/slot_machine.asm | 14 +++--- engine/gfx/color.asm | 4 +- engine/gfx/mon_icons.asm | 4 +- engine/gfx/sprite_anims.asm | 6 +-- engine/menus/intro_menu.asm | 8 ++-- engine/menus/naming_screen.asm | 2 +- engine/overworld/map_objects.asm | 6 +-- engine/pokegear/pokegear.asm | 8 ++-- engine/pokemon/breeding.asm | 6 +-- engine/pokemon/health.asm | 4 +- macros/scripts/battle_anims.asm | 4 +- macros/scripts/oam_anims.asm | 2 + tools/dupeframes.py | 34 +++++++++++++++ tools/scan_includes.c | 58 +++++++++++++++++-------- 30 files changed, 213 insertions(+), 117 deletions(-) create mode 100755 tools/dupeframes.py diff --git a/README.md b/README.md index 0ec3669a5..297c35bc3 100644 --- a/README.md +++ b/README.md @@ -20,33 +20,35 @@ To set up the repository, see [INSTALL.md](INSTALL.md). - [**Documentation**][docs] - [**Wiki**][wiki] (includes [tutorials][tutorials]) - [**Symbols**][symbols] -- **Discord:** [pret][discord] -- **IRC:** [libera#pret][irc] -Other disassembly projects: +Other disassembly and/or decompilation projects: +* [**Pokémon Red and Blue**](https://github.com/pret/pokered) +* [**Pokémon Gold and Silver (Space World '97 demo)**](https://github.com/pret/pokegold-spaceworld) +* [**Pokémon Yellow**](https://github.com/pret/pokeyellow) +* [**Pokémon Trading Card Game**](https://github.com/pret/poketcg) +* [**Pokémon Pinball**](https://github.com/pret/pokepinball) +* [**Pokémon Stadium**](https://github.com/pret/pokestadium) +* [**Pokémon Gold and Silver**](https://github.com/pret/pokegold) +* [**Pokémon Ruby and Sapphire**](https://github.com/pret/pokeruby) +* [**Pokémon Pinball: Ruby & Sapphire**](https://github.com/pret/pokepinballrs) +* [**Pokémon FireRed and LeafGreen**](https://github.com/pret/pokefirered) +* [**Pokémon Emerald**](https://github.com/pret/pokeemerald) +* [**Pokémon Mystery Dungeon: Red Rescue Team**](https://github.com/pret/pmd-red) +* [**Pokémon Diamond and Pearl**](https://github.com/pret/pokediamond) +* [**Pokémon Platinum**](https://github.com/pret/pokeplatinum) +* [**Pokémon HeartGold and SoulSilver**](https://github.com/pret/pokeheartgold) +* [**Pokémon Mystery Dungeon: Explorers of Sky**](https://github.com/pret/pmd-sky) -- [**Pokémon Red/Blue**][pokered] -- [**Pokémon Yellow**][pokeyellow] -- [**Pokémon Gold/Silver**][pokegold] -- [**Pokémon Pinball**][pokepinball] -- [**Pokémon TCG**][poketcg] -- [**Pokémon Ruby**][pokeruby] -- [**Pokémon FireRed**][pokefirered] -- [**Pokémon Emerald**][pokeemerald] +## Contacts + +You can find us on: + +* [Discord (PRET, #pokecrystal)](https://discord.gg/d5dubZ3) +* [IRC](https://web.libera.chat/?#pret) -[pokered]: https://github.com/pret/pokered -[pokeyellow]: https://github.com/pret/pokeyellow -[pokegold]: https://github.com/pret/pokegold -[pokepinball]: https://github.com/pret/pokepinball -[poketcg]: https://github.com/pret/poketcg -[pokeruby]: https://github.com/pret/pokeruby -[pokefirered]: https://github.com/pret/pokefirered -[pokeemerald]: https://github.com/pret/pokeemerald [docs]: https://pret.github.io/pokecrystal/ [wiki]: https://github.com/pret/pokecrystal/wiki [tutorials]: https://github.com/pret/pokecrystal/wiki/Tutorials [symbols]: https://github.com/pret/pokecrystal/tree/symbols -[discord]: https://discord.gg/d5dubZ3 -[irc]: https://web.libera.chat/?#pret [ci]: https://github.com/pret/pokecrystal/actions [ci-badge]: https://github.com/pret/pokecrystal/actions/workflows/main.yml/badge.svg diff --git a/constants/battle_anim_constants.asm b/constants/battle_anim_constants.asm index bf6b45e5a..b82af5d21 100644 --- a/constants/battle_anim_constants.asm +++ b/constants/battle_anim_constants.asm @@ -713,6 +713,9 @@ DEF NUM_BATTLEANIMFRAMESETS EQU const_value const BATTLEANIMOAMSET_D7 DEF NUM_BATTLEANIMOAMSETS EQU const_value +assert NUM_BATTLEANIMOAMSETS <= FIRST_OAM_CMD, \ + "BATTLEANIMOAMSET_* constants overlap oam*_command constants" + ; BattleBGEffects indexes (see engine/battle_anims/bg_effects.asm) const_def 1 const ANIM_BG_FLASH_INVERTED diff --git a/constants/battle_constants.asm b/constants/battle_constants.asm index e6a1f3665..1d6871e68 100644 --- a/constants/battle_constants.asm +++ b/constants/battle_constants.asm @@ -90,7 +90,7 @@ DEF SPDSPCDV_SHINY EQU $AA const BATTLETYPE_FISH const BATTLETYPE_ROAMING const BATTLETYPE_CONTEST - const BATTLETYPE_SHINY + const BATTLETYPE_FORCESHINY const BATTLETYPE_TREE const BATTLETYPE_TRAP const BATTLETYPE_FORCEITEM diff --git a/constants/map_object_constants.asm b/constants/map_object_constants.asm index ada9a984f..e44c2825b 100644 --- a/constants/map_object_constants.asm +++ b/constants/map_object_constants.asm @@ -97,6 +97,7 @@ DEF RELATIVE_ATTRIBUTES EQU 1 << RELATIVE_ATTRIBUTES_F DEF ABSOLUTE_TILE_ID EQU 1 << ABSOLUTE_TILE_ID_F ; map_object struct members (see macros/ram.asm) +; struct initialized by object_event macro (see macros/scripts/maps.asm) rsreset DEF MAPOBJECT_OBJECT_STRUCT_ID rb ; 0 DEF MAPOBJECT_SPRITE rb ; 1 diff --git a/constants/script_constants.asm b/constants/script_constants.asm index 50df3c2a4..0eeff7c16 100644 --- a/constants/script_constants.asm +++ b/constants/script_constants.asm @@ -109,7 +109,8 @@ DEF CALLBACK_SIZE EQU 3 ; callback DEF WARP_EVENT_SIZE EQU 5 ; warp_event DEF COORD_EVENT_SIZE EQU 8 ; coord_event DEF BG_EVENT_SIZE EQU 5 ; bg_event -DEF OBJECT_EVENT_SIZE EQU 13 ; object_event +; An object_event is a map_object without its initial MAPOBJECT_OBJECT_STRUCT_ID or final padding +DEF OBJECT_EVENT_SIZE EQU MAPOBJECT_LENGTH - 3 ; 13 ; A coord_event for scene -1 will always activate, ; regardless of the map's scene variable value. diff --git a/constants/sprite_anim_constants.asm b/constants/sprite_anim_constants.asm index 6fe848af9..940727a47 100644 --- a/constants/sprite_anim_constants.asm +++ b/constants/sprite_anim_constants.asm @@ -370,3 +370,6 @@ DEF NUM_SPRITE_ANIM_FRAMESETS EQU const_value const SPRITE_ANIM_OAMSET_LEVEL_SELECTION_MENU_FEMALE_RIGHT_1 ; 9a const SPRITE_ANIM_OAMSET_LEVEL_SELECTION_MENU_FEMALE_RIGHT_2 ; 9b DEF NUM_SPRITE_ANIM_OAMSETS EQU const_value + +assert NUM_SPRITE_ANIM_OAMSETS <= FIRST_OAM_CMD, \ + "SPRITE_ANIM_OAMSET_* constants overlap oam*_command constants" diff --git a/data/maps/attributes.asm b/data/maps/attributes.asm index 6d736dd21..0b7df0803 100644 --- a/data/maps/attributes.asm +++ b/data/maps/attributes.asm @@ -31,52 +31,52 @@ MACRO connection ; Calculate tile offsets for source (current) and target maps DEF _src = 0 - DEF _tgt = (\4) + 3 + DEF _tgt = (\4) + MAP_CONNECTION_PADDING_WIDTH if _tgt < 0 DEF _src = -_tgt DEF _tgt = 0 endc if !STRCMP("\1", "north") - DEF _blk = \3_WIDTH * (\3_HEIGHT - 3) + _src + DEF _blk = \3_WIDTH * (\3_HEIGHT - MAP_CONNECTION_PADDING_WIDTH) + _src DEF _map = _tgt - DEF _win = (\3_WIDTH + 6) * \3_HEIGHT + 1 + DEF _win = (\3_WIDTH + MAP_CONNECTION_PADDING_WIDTH * 2) * \3_HEIGHT + 1 DEF _y = \3_HEIGHT * 2 - 1 DEF _x = (\4) * -2 - DEF _len = CURRENT_MAP_WIDTH + 3 - (\4) + DEF _len = CURRENT_MAP_WIDTH + MAP_CONNECTION_PADDING_WIDTH - (\4) if _len > \3_WIDTH DEF _len = \3_WIDTH endc elif !STRCMP("\1", "south") DEF _blk = _src - DEF _map = (CURRENT_MAP_WIDTH + 6) * (CURRENT_MAP_HEIGHT + 3) + _tgt - DEF _win = \3_WIDTH + 7 + DEF _map = (CURRENT_MAP_WIDTH + MAP_CONNECTION_PADDING_WIDTH * 2) * (CURRENT_MAP_HEIGHT + MAP_CONNECTION_PADDING_WIDTH) + _tgt + DEF _win = \3_WIDTH + MAP_CONNECTION_PADDING_WIDTH * 2 + 1 DEF _y = 0 DEF _x = (\4) * -2 - DEF _len = CURRENT_MAP_WIDTH + 3 - (\4) + DEF _len = CURRENT_MAP_WIDTH + MAP_CONNECTION_PADDING_WIDTH - (\4) if _len > \3_WIDTH DEF _len = \3_WIDTH endc elif !STRCMP("\1", "west") - DEF _blk = (\3_WIDTH * _src) + \3_WIDTH - 3 - DEF _map = (CURRENT_MAP_WIDTH + 6) * _tgt - DEF _win = (\3_WIDTH + 6) * 2 - 6 + DEF _blk = (\3_WIDTH * _src) + \3_WIDTH - MAP_CONNECTION_PADDING_WIDTH + DEF _map = (CURRENT_MAP_WIDTH + MAP_CONNECTION_PADDING_WIDTH * 2) * _tgt + DEF _win = (\3_WIDTH + MAP_CONNECTION_PADDING_WIDTH * 2) * 2 - MAP_CONNECTION_PADDING_WIDTH * 2 DEF _y = (\4) * -2 DEF _x = \3_WIDTH * 2 - 1 - DEF _len = CURRENT_MAP_HEIGHT + 3 - (\4) + DEF _len = CURRENT_MAP_HEIGHT + MAP_CONNECTION_PADDING_WIDTH - (\4) if _len > \3_HEIGHT DEF _len = \3_HEIGHT endc elif !STRCMP("\1", "east") DEF _blk = (\3_WIDTH * _src) - DEF _map = (CURRENT_MAP_WIDTH + 6) * _tgt + CURRENT_MAP_WIDTH + 3 - DEF _win = \3_WIDTH + 7 + DEF _map = (CURRENT_MAP_WIDTH + MAP_CONNECTION_PADDING_WIDTH * 2) * _tgt + CURRENT_MAP_WIDTH + MAP_CONNECTION_PADDING_WIDTH + DEF _win = \3_WIDTH + MAP_CONNECTION_PADDING_WIDTH * 2 + 1 DEF _y = (\4) * -2 DEF _x = 0 - DEF _len = CURRENT_MAP_HEIGHT + 3 - (\4) + DEF _len = CURRENT_MAP_HEIGHT + MAP_CONNECTION_PADDING_WIDTH - (\4) if _len > \3_HEIGHT DEF _len = \3_HEIGHT endc diff --git a/data/types/names.asm b/data/types/names.asm index 94fe67e13..2f83ba464 100644 --- a/data/types/names.asm +++ b/data/types/names.asm @@ -1,6 +1,7 @@ TypeNames: ; entries correspond to types (see constants/type_constants.asm) table_width 2, TypeNames + dw Normal dw Fighting dw Flying @@ -12,17 +13,13 @@ TypeNames: dw Ghost dw Steel assert_table_length UNUSED_TYPES + +rept UNUSED_TYPES_END - UNUSED_TYPES - 1 ; discount CURSE_TYPE dw Normal - dw Normal - dw Normal - dw Normal - dw Normal - dw Normal - dw Normal - dw Normal - dw Normal +endr dw CurseType assert_table_length UNUSED_TYPES_END + dw Fire dw Water dw Grass diff --git a/docs/bugs_and_glitches.md b/docs/bugs_and_glitches.md index dffc19355..804f9a88d 100644 --- a/docs/bugs_and_glitches.md +++ b/docs/bugs_and_glitches.md @@ -59,6 +59,7 @@ Fixes in the [multi-player battle engine](#multi-player-battle-engine) category - [AI makes a false assumption about `CheckTypeMatchup`](#ai-makes-a-false-assumption-about-checktypematchup) - [AI use of Full Heal or Full Restore does not cure Nightmare status](#ai-use-of-full-heal-or-full-restore-does-not-cure-nightmare-status) - [AI use of Full Heal does not cure confusion status](#ai-use-of-full-heal-does-not-cure-confusion-status) + - [AI might use its base reward value as an item](#ai-might-use-its-base-reward-value-as-an-item) - [Wild Pokémon can always Teleport regardless of level difference](#wild-pok%C3%A9mon-can-always-teleport-regardless-of-level-difference) - [`RIVAL2` has lower DVs than `RIVAL1`](#rival2-has-lower-dvs-than-rival1) - [`HELD_CATCH_CHANCE` has no effect](#held_catch_chance-has-no-effect) @@ -1455,6 +1456,33 @@ Pryce's dialog ("That BADGE will raise the SPECIAL stats of POKéMON.") implies ret ``` +### AI might use its base reward value as an item + +In the `AI_TryItem` routine, an item pointer is set to `wEnemyTrainerItem1` and then increments to `wEnemyTrainerItem2` to see if either of the AI's items are in the `AI_Items` list. However, if the AI has used its first item (or its first one is `ITEM_NONE`) and hasn't used its second item, the item pointer can increment from `wEnemyTrainerItem2` to `wEnemyTrainerBaseReward`. If the value at this address then matches an item in the `AI_Items` list, the AI could mistakenly use it. + +**Fix:** Edit `AI_TryItem` in [engine/battle/ai/items.asm](https://github.com/pret/pokecrystal/blob/master/engine/battle/ai/items.asm): + +```diff + AI_TryItem: + ... + ld a, [wTrainerClass] + dec a + ld hl, TrainerClassAttributes + TRNATTR_AI_ITEM_SWITCH + ld bc, NUM_TRAINER_ATTRIBUTES + call AddNTimes + ld b, h + ld c, l + ld hl, AI_Items +-; BUG: AI might use its base reward value as an item (see docs/bugs_and_glitches.md) +- ld de, wEnemyTrainerItem1 + .loop ++ ld de, wEnemyTrainerItem1 + ld a, [hl] + and a + inc a + ret z +``` + ### Wild Pokémon can always Teleport regardless of level difference diff --git a/engine/battle/ai/items.asm b/engine/battle/ai/items.asm index 3cff6eb3a..0f5d7607b 100644 --- a/engine/battle/ai/items.asm +++ b/engine/battle/ai/items.asm @@ -175,6 +175,7 @@ AI_TryItem: ld b, h ld c, l ld hl, AI_Items +; BUG: AI might use its base reward value as an item (see docs/bugs_and_glitches.md) ld de, wEnemyTrainerItem1 .loop ld a, [hl] diff --git a/engine/battle/core.asm b/engine/battle/core.asm index 206823434..b29b6e169 100644 --- a/engine/battle/core.asm +++ b/engine/battle/core.asm @@ -3522,7 +3522,7 @@ TryToRunAwayFromBattle: jp z, .cant_escape cp BATTLETYPE_CELEBI jp z, .cant_escape - cp BATTLETYPE_SHINY + cp BATTLETYPE_FORCESHINY jp z, .cant_escape cp BATTLETYPE_SUICUNE jp z, .cant_escape @@ -5954,7 +5954,7 @@ LoadEnemyMon: ; Forced shiny battle type ; Used by Red Gyarados at Lake of Rage - cp BATTLETYPE_SHINY + cp BATTLETYPE_FORCESHINY jr nz, .GenerateDVs ld b, ATKDEFDV_SHINY ; $ea diff --git a/engine/battle/effect_commands.asm b/engine/battle/effect_commands.asm index e22a3b2b3..4051f497b 100644 --- a/engine/battle/effect_commands.asm +++ b/engine/battle/effect_commands.asm @@ -4161,14 +4161,14 @@ RaiseStat: add hl, bc ld b, [hl] inc b - ld a, $d + ld a, MAX_STAT_LEVEL cp b jp c, .cant_raise_stat ld a, [wLoweredStat] and $f0 jr z, .got_num_stages inc b - ld a, $d + ld a, MAX_STAT_LEVEL cp b jr nc, .got_num_stages ld b, a @@ -4176,7 +4176,7 @@ RaiseStat: ld [hl], b push hl ld a, c - cp $5 + cp ACCURACY jr nc, .done_calcing_stats ld hl, wBattleMonStats + 1 ld de, wPlayerStats @@ -4684,7 +4684,7 @@ LowerStat: .got_num_stages ld [hl], b ld a, c - cp 5 + cp ACCURACY jr nc, .accuracy_evasion push hl @@ -4955,7 +4955,7 @@ SetBattleDraw: BattleCommand_ForceSwitch: ld a, [wBattleType] - cp BATTLETYPE_SHINY + cp BATTLETYPE_FORCESHINY jp z, .fail cp BATTLETYPE_TRAP jp z, .fail diff --git a/engine/battle/move_effects/teleport.asm b/engine/battle/move_effects/teleport.asm index 650ef5994..87d2c6571 100644 --- a/engine/battle/move_effects/teleport.asm +++ b/engine/battle/move_effects/teleport.asm @@ -1,6 +1,6 @@ BattleCommand_Teleport: ld a, [wBattleType] - cp BATTLETYPE_SHINY + cp BATTLETYPE_FORCESHINY jr z, .failed cp BATTLETYPE_TRAP jr z, .failed diff --git a/engine/battle/trainer_huds.asm b/engine/battle/trainer_huds.asm index d650f7a06..7c533bd18 100644 --- a/engine/battle/trainer_huds.asm +++ b/engine/battle/trainer_huds.asm @@ -20,11 +20,11 @@ ShowPlayerMonsRemaining: ld de, wPartyCount call StageBallTilesData ; ldpixel wPlaceBallsX, 12, 12 - ld a, 12 * 8 + ld a, 12 * TILE_WIDTH ld hl, wPlaceBallsX ld [hli], a ld [hl], a - ld a, 8 + ld a, TILE_WIDTH ld [wPlaceBallsDirection], a ld hl, wShadowOAMSprite00 jp LoadTrainerHudOAM @@ -36,10 +36,10 @@ ShowOTTrainerMonsRemaining: call StageBallTilesData ; ldpixel wPlaceBallsX, 9, 4 ld hl, wPlaceBallsX - ld a, 9 * 8 + ld a, 9 * TILE_WIDTH ld [hli], a - ld [hl], 4 * 8 - ld a, -8 + ld [hl], 4 * TILE_WIDTH + ld a, -TILE_WIDTH ld [wPlaceBallsDirection], a ld hl, wShadowOAMSprite00 + PARTY_LENGTH * SPRITEOAMSTRUCT_LENGTH jp LoadTrainerHudOAM @@ -182,10 +182,10 @@ LinkBattle_TrainerHuds: ld de, wPartyCount call StageBallTilesData ld hl, wPlaceBallsX - ld a, 10 * 8 + ld a, 10 * TILE_WIDTH ld [hli], a - ld [hl], 8 * 8 - ld a, 8 + ld [hl], 8 * TILE_WIDTH + ld a, TILE_WIDTH ld [wPlaceBallsDirection], a ld hl, wShadowOAMSprite00 call LoadTrainerHudOAM @@ -194,9 +194,9 @@ LinkBattle_TrainerHuds: ld de, wOTPartyCount call StageBallTilesData ld hl, wPlaceBallsX - ld a, 10 * 8 + ld a, 10 * TILE_WIDTH ld [hli], a - ld [hl], 13 * 8 + ld [hl], 13 * TILE_WIDTH ld hl, wShadowOAMSprite00 + PARTY_LENGTH * SPRITEOAMSTRUCT_LENGTH jp LoadTrainerHudOAM diff --git a/engine/battle_anims/core.asm b/engine/battle_anims/core.asm index 7043ea249..ca22fcf87 100644 --- a/engine/battle_anims/core.asm +++ b/engine/battle_anims/core.asm @@ -243,7 +243,7 @@ InitBattleAnimBuffer: add hl, bc ld a, [hli] ld d, a - ld a, (-10 * 8) + 4 + ld a, (-10 * TILE_WIDTH) + 4 sub d ld [wBattleAnimTempXCoord], a ld a, [hli] @@ -251,7 +251,7 @@ InitBattleAnimBuffer: ld a, [wBattleAnimTempFixY] cp $ff jr nz, .check_kinesis_softboiled_milkdrink - ld a, 5 * 8 + ld a, 5 * TILE_WIDTH add d jr .done @@ -270,7 +270,7 @@ InitBattleAnimBuffer: jr nz, .no_sub .do_sub pop af - sub 1 * 8 + sub 1 * TILE_WIDTH jr .done .no_sub diff --git a/engine/events/field_moves.asm b/engine/events/field_moves.asm index 5062937ed..9881c4f7c 100644 --- a/engine/events/field_moves.asm +++ b/engine/events/field_moves.asm @@ -347,7 +347,7 @@ FlyToAnim: ld [hl], SPRITE_ANIM_SEQ_FLY_TO ld hl, SPRITEANIMSTRUCT_VAR4 add hl, bc - ld [hl], 11 * 8 + ld [hl], 11 * TILE_WIDTH ld a, 64 ld [wFrameCounter], a .loop diff --git a/engine/games/slot_machine.asm b/engine/games/slot_machine.asm index befffabd3..6cbb9f368 100644 --- a/engine/games/slot_machine.asm +++ b/engine/games/slot_machine.asm @@ -675,7 +675,7 @@ Slots_InitReelTiles: ld [hl], d ld hl, REEL_X_COORD add hl, bc - ld [hl], 6 * 8 + ld [hl], 6 * TILE_WIDTH call .OAM ld bc, wReel2 @@ -693,7 +693,7 @@ Slots_InitReelTiles: ld [hl], d ld hl, REEL_X_COORD add hl, bc - ld [hl], 10 * 8 + ld [hl], 10 * TILE_WIDTH call .OAM ld bc, wReel3 @@ -711,7 +711,7 @@ Slots_InitReelTiles: ld [hl], d ld hl, REEL_X_COORD add hl, bc - ld [hl], 14 * 8 + ld [hl], 14 * TILE_WIDTH call .OAM ret @@ -779,7 +779,7 @@ Slots_UpdateReelPositionAndOAM: add hl, bc ld a, [hl] ld [wCurReelXCoord], a - ld a, 10 * 8 + ld a, 10 * TILE_WIDTH ld [wCurReelYCoord], a ld hl, REEL_POSITION add hl, bc @@ -2010,7 +2010,7 @@ Slots_AnimateGolem: jr c, .play_sound dec [hl] ld e, a - ld d, 14 * 8 + ld d, 14 * TILE_WIDTH farcall BattleAnim_Sine_e ld a, e ld hl, SPRITEANIMSTRUCT_YOFFSET @@ -2037,7 +2037,7 @@ Slots_AnimateGolem: ld a, [hl] inc [hl] inc [hl] - cp 9 * 8 + cp 9 * TILE_WIDTH jr nc, .restart and $3 ret nz @@ -2081,7 +2081,7 @@ Slots_AnimateChansey: add hl, bc ld a, [hl] inc [hl] - cp 13 * 8 + cp 13 * TILE_WIDTH jr z, .limit and $f ret nz diff --git a/engine/gfx/color.asm b/engine/gfx/color.asm index 3a859daab..a91816dc2 100644 --- a/engine/gfx/color.asm +++ b/engine/gfx/color.asm @@ -576,7 +576,7 @@ InitCGBPals:: ldh [rVBK], a ld a, 1 << rBGPI_AUTO_INCREMENT ldh [rBGPI], a - ld c, 4 * 8 + ld c, 4 * TILE_WIDTH .bgpals_loop ld a, LOW(PALRGB_WHITE) ldh [rBGPD], a @@ -586,7 +586,7 @@ InitCGBPals:: jr nz, .bgpals_loop ld a, 1 << rOBPI_AUTO_INCREMENT ldh [rOBPI], a - ld c, 4 * 8 + ld c, 4 * TILE_WIDTH .obpals_loop ld a, LOW(PALRGB_WHITE) ldh [rOBPD], a diff --git a/engine/gfx/mon_icons.asm b/engine/gfx/mon_icons.asm index dfc1fe5c9..6065252e8 100644 --- a/engine/gfx/mon_icons.asm +++ b/engine/gfx/mon_icons.asm @@ -197,8 +197,8 @@ MoveList_InitAnimatedMonIcon: ld [wCurIcon], a xor a call GetIconGFX - ld d, 3 * 8 + 2 ; depixel 3, 4, 2, 4 - ld e, 4 * 8 + 4 + ld d, 3 * TILE_WIDTH + 2 ; depixel 3, 4, 2, 4 + ld e, 4 * TILE_WIDTH + 4 ld a, SPRITE_ANIM_INDEX_PARTY_MON call _InitSpriteAnimStruct ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID diff --git a/engine/gfx/sprite_anims.asm b/engine/gfx/sprite_anims.asm index f02f9c6f7..579cc9b21 100644 --- a/engine/gfx/sprite_anims.asm +++ b/engine/gfx/sprite_anims.asm @@ -384,7 +384,7 @@ AnimSeq_SlotsChanseyEgg: ld hl, SPRITEANIMSTRUCT_XCOORD add hl, bc ld a, [hl] - cp 15 * 8 + cp 15 * TILE_WIDTH jr c, .move_right call DeinitializeSprite ld a, $4 @@ -686,7 +686,7 @@ AnimSeq_FlyLeaf: ld hl, SPRITEANIMSTRUCT_XCOORD add hl, bc ld a, [hl] - cp -9 * 8 + cp -9 * TILE_WIDTH jr nc, .delete_leaf inc [hl] inc [hl] @@ -715,7 +715,7 @@ AnimSeq_FlyTo: ld hl, SPRITEANIMSTRUCT_YCOORD add hl, bc ld a, [hl] - cp 10 * 8 + 4 + cp 10 * TILE_WIDTH + 4 ret z ld hl, SPRITEANIMSTRUCT_YCOORD diff --git a/engine/menus/intro_menu.asm b/engine/menus/intro_menu.asm index c52b2854b..5b14a4c47 100644 --- a/engine/menus/intro_menu.asm +++ b/engine/menus/intro_menu.asm @@ -825,10 +825,10 @@ Intro_PlacePlayerSprite: .sprites db 4 ; y pxl, x pxl, tile offset - db 9 * 8 + 4, 9 * 8, 0 - db 9 * 8 + 4, 10 * 8, 1 - db 10 * 8 + 4, 9 * 8, 2 - db 10 * 8 + 4, 10 * 8, 3 + db 9 * TILE_WIDTH + 4, 9 * TILE_WIDTH, 0 + db 9 * TILE_WIDTH + 4, 10 * TILE_WIDTH, 1 + db 10 * TILE_WIDTH + 4, 9 * TILE_WIDTH, 2 + db 10 * TILE_WIDTH + 4, 10 * TILE_WIDTH, 3 const_def diff --git a/engine/menus/naming_screen.asm b/engine/menus/naming_screen.asm index d4ee0d098..d04313cd6 100644 --- a/engine/menus/naming_screen.asm +++ b/engine/menus/naming_screen.asm @@ -365,7 +365,7 @@ NamingScreenJoypadLoop: depixel 10, 3 call NamingScreen_IsTargetBox jr nz, .got_cursor_position - ld d, 8 * 8 + ld d, 8 * TILE_WIDTH .got_cursor_position ld a, SPRITE_ANIM_INDEX_NAMING_SCREEN_CURSOR call InitSpriteAnimStruct diff --git a/engine/overworld/map_objects.asm b/engine/overworld/map_objects.asm index 0ec067b70..76935b596 100644 --- a/engine/overworld/map_objects.asm +++ b/engine/overworld/map_objects.asm @@ -880,12 +880,12 @@ MovementFunction_Shadow: add hl, de ld a, [hl] maskbits NUM_DIRECTIONS - ld d, 1 * 8 + 6 + ld d, 1 * TILE_WIDTH + 6 cp DOWN jr z, .ok cp UP jr z, .ok - ld d, 1 * 8 + 4 + ld d, 1 * TILE_WIDTH + 4 .ok ld hl, OBJECT_SPRITE_Y_OFFSET add hl, bc @@ -909,7 +909,7 @@ MovementFunction_Emote: ld [hl], 0 ld hl, OBJECT_SPRITE_Y_OFFSET add hl, bc - ld [hl], -2 * 8 + ld [hl], -2 * TILE_WIDTH ld hl, OBJECT_SPRITE_X_OFFSET add hl, bc ld [hl], 0 diff --git a/engine/pokegear/pokegear.asm b/engine/pokegear/pokegear.asm index 838403f46..066d1e39c 100644 --- a/engine/pokegear/pokegear.asm +++ b/engine/pokegear/pokegear.asm @@ -2302,10 +2302,10 @@ Pokedex_GetArea: .PlayerOAM: ; y pxl, x pxl, tile offset - db -1 * 8, -1 * 8, 0 ; top left - db -1 * 8, 0 * 8, 1 ; top right - db 0 * 8, -1 * 8, 2 ; bottom left - db 0 * 8, 0 * 8, 3 ; bottom right + db -1 * TILE_WIDTH, -1 * TILE_WIDTH, 0 ; top left + db -1 * TILE_WIDTH, 0 * TILE_WIDTH, 1 ; top right + db 0 * TILE_WIDTH, -1 * TILE_WIDTH, 2 ; bottom left + db 0 * TILE_WIDTH, 0 * TILE_WIDTH, 3 ; bottom right db $80 ; terminator .CheckPlayerLocation: diff --git a/engine/pokemon/breeding.asm b/engine/pokemon/breeding.asm index 58d40d2ca..32b08019b 100644 --- a/engine/pokemon/breeding.asm +++ b/engine/pokemon/breeding.asm @@ -769,9 +769,9 @@ EggHatch_CrackShell: ret nc swap a srl a - add 9 * 8 + 4 + add 9 * TILE_WIDTH + 4 ld d, a - ld e, 11 * 8 + ld e, 11 * TILE_WIDTH ld a, SPRITE_ANIM_INDEX_EGG_CRACK call InitSpriteAnimStruct ld hl, SPRITEANIMSTRUCT_TILE_ID @@ -828,7 +828,7 @@ Hatch_InitShellFragments: MACRO shell_fragment ; y tile, y pxl, x tile, x pxl, frameset offset, ??? - db (\1 * 8) % $100 + \2, (\3 * 8) % $100 + \4, \5 - SPRITE_ANIM_FRAMESET_EGG_HATCH_1, \6 + db (\1 * TILE_WIDTH) % $100 + \2, (\3 * TILE_WIDTH) % $100 + \4, \5 - SPRITE_ANIM_FRAMESET_EGG_HATCH_1, \6 ENDM .SpriteData: diff --git a/engine/pokemon/health.asm b/engine/pokemon/health.asm index d11a073b5..6bd11e3d2 100644 --- a/engine/pokemon/health.asm +++ b/engine/pokemon/health.asm @@ -53,7 +53,7 @@ HealPartyMon: ret ComputeHPBarPixels: -; e = bc * (6 * 8) / de +; e = bc * HP_BAR_LENGTH_PX / de ld a, b or c jr z, .zero @@ -64,7 +64,7 @@ ComputeHPBarPixels: ldh [hMultiplicand + 1], a ld a, c ldh [hMultiplicand + 2], a - ld a, 6 * 8 + ld a, HP_BAR_LENGTH_PX ldh [hMultiplier], a call Multiply ; We need de to be under 256 because hDivisor is only 1 byte. diff --git a/macros/scripts/battle_anims.asm b/macros/scripts/battle_anims.asm index d944b5ec3..9410f015e 100644 --- a/macros/scripts/battle_anims.asm +++ b/macros/scripts/battle_anims.asm @@ -18,8 +18,8 @@ MACRO anim_obj else ; LEGACY: Support the tile+offset format db \1 ; object - db (\2) * 8 + (\3) ; x_tile, x - db (\4) * 8 + (\5) ; y_tile, y + db (\2) * TILE_WIDTH + (\3) ; x_tile, x + db (\4) * TILE_WIDTH + (\5) ; y_tile, y db \6 ; param endc ENDM diff --git a/macros/scripts/oam_anims.asm b/macros/scripts/oam_anims.asm index f3fa9bfbe..2bd751cfb 100644 --- a/macros/scripts/oam_anims.asm +++ b/macros/scripts/oam_anims.asm @@ -36,3 +36,5 @@ ENDM MACRO oamdelete db oamdelete_command ENDM + +DEF FIRST_OAM_CMD EQU LOW(const_value + 1) diff --git a/tools/dupeframes.py b/tools/dupeframes.py new file mode 100755 index 000000000..0aba707e6 --- /dev/null +++ b/tools/dupeframes.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Usage: python dupeframes.py + +Check for duplicate frames in Pokemon sprites (gfx/pokemon/*/front.png). +""" + +import sys +import glob + +import png + +def check_duplicate_frames(filename): + with open(filename, 'rb') as file: + width, height, rows = png.Reader(file).asRGBA8()[:3] + rows = list(rows) + if height % width: + print(f'{filename} is not a vertical strip of square frames!', file=sys.stderr) + return + num_frames = height // width + frames = [rows[i*width:(i+1)*width] for i in range(num_frames)] + for i in range(num_frames): + for j in range(i + 1, num_frames): + if frames[i] == frames[j]: + print(f'{filename}: frame {j} is a duplicate of frame {i}', file=sys.stderr) + +def main(): + for filename in sorted(glob.glob('gfx/pokemon/*/front.png')): + check_duplicate_frames(filename) + +if __name__ == '__main__': + main() diff --git a/tools/scan_includes.c b/tools/scan_includes.c index e57ddc358..9ec5f3c5d 100644 --- a/tools/scan_includes.c +++ b/tools/scan_includes.c @@ -3,6 +3,8 @@ #include "common.h" +#include + void parse_args(int argc, char *argv[], bool *strict) { struct option long_options[] = { {"strict", no_argument, 0, 's'}, @@ -40,31 +42,47 @@ void scan_file(const char *filename, bool strict) { fclose(f); contents[size] = '\0'; - for (char *ptr = contents; ptr && ptr - contents < size; ptr++) { - bool is_incbin = false, is_include = false; + for (char *ptr = contents; ptr && ptr < contents + size; ptr++) { + ptr = strpbrk(ptr, ";\"Ii"); + if (!ptr) { + break; + } switch (*ptr) { case ';': - ptr = strchr(ptr, '\n'); - if (!ptr) { - fprintf(stderr, "%s: no newline at end of file\n", filename); - } - break; - case '"': - ptr++; - ptr = strchr(ptr, '"'); - if (ptr) { + // Skip comments until the end of the line + ptr += strcspn(ptr + 1, "\r\n"); + if (*ptr) { ptr++; - } else { - fprintf(stderr, "%s: unterminated string\n", filename); } break; + + case '"': + // Skip string literal until the closing quote + ptr += strcspn(ptr + 1, "\""); + if (*ptr) { + ptr++; + } + break; + case 'I': case 'i': - is_incbin = !strncmp(ptr, "INCBIN", 6) || !strncmp(ptr, "incbin", 6); - is_include = !strncmp(ptr, "INCLUDE", 7) || !strncmp(ptr, "include", 7); + /* empty statement between the label and the variable declaration */; + // Check that an INCLUDE/INCBIN starts as its own token + char before = ptr > contents ? *(ptr - 1) : '\n'; + if (!isspace((unsigned)before) && before != ':') { + break; + } + bool is_incbin = !strncmp(ptr, "INCBIN", 6) || !strncmp(ptr, "incbin", 6); + bool is_include = !strncmp(ptr, "INCLUDE", 7) || !strncmp(ptr, "include", 7); if (is_incbin || is_include) { - ptr = strchr(ptr, '"'); - if (ptr) { + // Check that an INCLUDE/INCBIN ends as its own token + ptr += is_include ? 7 : 6; + if (!isspace((unsigned)*ptr) && *ptr != '"') { + break; + } + ptr += strspn(ptr, " \t"); + if (*ptr == '"') { + // Print the file path and recursively scan INCLUDEs ptr++; char *include_path = ptr; size_t length = strcspn(ptr, "\""); @@ -74,6 +92,12 @@ void scan_file(const char *filename, bool strict) { if (is_include) { scan_file(include_path, strict); } + } else { + fprintf(stderr, "%s: no file path after INC%s\n", filename, is_include ? "LUDE" : "BIN"); + // Continue to process a comment + if (*ptr == ';') { + ptr--; + } } } break;