diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/battle_anim_commands.md | 2 | ||||
-rw-r--r-- | docs/bugs_and_glitches.md | 744 | ||||
-rw-r--r-- | docs/design_flaws.md | 452 | ||||
-rw-r--r-- | docs/event_commands.md | 32 | ||||
-rw-r--r-- | docs/images/hp_exp_bar_border_fix.png | bin | 187 -> 166 bytes | |||
-rw-r--r-- | docs/images/port_fix.png | bin | 1547 -> 1526 bytes | |||
-rw-r--r-- | docs/map_event_scripts.md (renamed from docs/map_scripts.md) | 83 | ||||
-rw-r--r-- | docs/menu.md | 48 | ||||
-rw-r--r-- | docs/movement_commands.md | 2 | ||||
-rw-r--r-- | docs/pic_animations.md | 6 |
10 files changed, 860 insertions, 509 deletions
diff --git a/docs/battle_anim_commands.md b/docs/battle_anim_commands.md index bd40e8a6d..86763b24a 100644 --- a/docs/battle_anim_commands.md +++ b/docs/battle_anim_commands.md @@ -88,7 +88,7 @@ Temporarily creates sprites from the top row of the player backpic, so that the ## `$DB`: `anim_checkpokeball` -Sets `BattleAnimVar` to the result of [GetPokeBallWobble](/engine/battle_anims/getpokeballwobble.asm). +Sets `BattleAnimVar` to the result of [GetPokeBallWobble](/engine/battle_anims/pokeball_wobble.asm). ## `$DC`: `anim_transform` diff --git a/docs/bugs_and_glitches.md b/docs/bugs_and_glitches.md index 7dfd8f421..3a6e5508c 100644 --- a/docs/bugs_and_glitches.md +++ b/docs/bugs_and_glitches.md @@ -15,6 +15,8 @@ These are known bugs and glitches in the original Pokémon Crystal game: code th - [A Pokémon that fainted from Pursuit will have its old status condition when revived](#a-pokémon-that-fainted-from-pursuit-will-have-its-old-status-condition-when-revived) - [Lock-On and Mind Reader don't always bypass Fly and Dig](#lock-on-and-mind-reader-dont-always-bypass-fly-and-dig) - [Beat Up can desynchronize link battles](#beat-up-can-desynchronize-link-battles) +- [Beat Up may fail to raise substitute](#beat-up-may-fail-to-raise-substitute) +- [Beat Up may trigger King's Rock even if it failed](#beat-up-may-trigger-kings-rock-even-if-it-failed) - [Present damage is incorrect in link battles](#present-damage-is-incorrect-in-link-battles) - ["Smart" AI encourages Mean Look if its own Pokémon is badly poisoned](#smart-ai-encourages-mean-look-if-its-own-pokémon-is-badly-poisoned) - [AI makes a false assumption about `CheckTypeMatchup`](#ai-makes-a-false-assumption-about-checktypematchup) @@ -33,6 +35,7 @@ These are known bugs and glitches in the original Pokémon Crystal game: code th - [Magikarp length limits have a unit conversion error](#magikarp-length-limits-have-a-unit-conversion-error) - [Magikarp lengths can be miscalculated](#magikarp-lengths-can-be-miscalculated) - [Battle transitions fail to account for the enemy's level](#battle-transitions-fail-to-account-for-the-enemys-level) +- [A "HOF Master!" title for 200-Time Famers is defined but inaccessible](#a-hof-master-title-for-200-time-famers-is-defined-but-inaccessible) - [Slot machine payout sound effects cut each other off](#slot-machine-payout-sound-effects-cut-each-other-off) - [Team Rocket battle music is not used for Executives or Scientists](#team-rocket-battle-music-is-not-used-for-executives-or-scientists) - [No bump noise if standing on tile `$3E`](#no-bump-noise-if-standing-on-tile-3e) @@ -41,17 +44,18 @@ These are known bugs and glitches in the original Pokémon Crystal game: code th - [Two tiles in the `port` tileset are drawn incorrectly](#two-tiles-in-the-port-tileset-are-drawn-incorrectly) - [`LoadMetatiles` wraps around past 128 blocks](#loadmetatiles-wraps-around-past-128-blocks) - [Surfing directly across a map connection does not load the new map](#surfing-directly-across-a-map-connection-does-not-load-the-new-map) -- [`Function6ec1` does not correctly limit object movement](#function6ec1-does-not-correctly-limit-object-movement) +- [Swimming NPCs aren't limited by their movement radius](#swimming-npcs-arent-limited-by-their-movement-radius) - [`CheckOwnMon` only checks the first five letters of OT names](#checkownmon-only-checks-the-first-five-letters-of-ot-names) - [Catching a Transformed Pokémon always catches a Ditto](#catching-a-transformed-pokémon-always-catches-a-ditto) - [Using a Park Ball in normal battles has a corrupt animation](#using-a-park-ball-in-normal-battles-has-a-corrupt-animation) - [`HELD_CATCH_CHANCE` has no effect](#held_catch_chance-has-no-effect) -- [Only the first three `EvosAttacks` evolution entries can have Stone compatibility reported correctly](#only-the-first-three-evosattacks-evolution-entries-can-have-stone-compatibility-reported-correctly) +- [Only the first three evolution entries can have Stone compatibility reported correctly](#only-the-first-three-evolution-entries-can-have-stone-compatibility-reported-correctly) +- [`EVOLVE_STAT` can break Stone compatibility reporting](#evolve_stat-can-break-stone-compatibility-reporting) - [`ScriptCall` can overflow `wScriptStack` and crash](#scriptcall-can-overflow-wscriptstack-and-crash) - [`LoadSpriteGFX` does not limit the capacity of `UsedSprites`](#loadspritegfx-does-not-limit-the-capacity-of-usedsprites) - [`ChooseWildEncounter` doesn't really validate the wild Pokémon species](#choosewildencounter-doesnt-really-validate-the-wild-pokémon-species) - [`TryObjectEvent` arbitrary code execution](#tryobjectevent-arbitrary-code-execution) -- [`Special_CheckBugContestContestantFlag` can read beyond its data table](#special_checkbugcontestcontestantflag-can-read-beyond-its-data-table) +- [`CheckBugContestContestantFlag` can read beyond its data table](#checkbugcontestcontestantflag-can-read-beyond-its-data-table) - [`ClearWRAM` only clears WRAM bank 1](#clearwram-only-clears-wram-bank-1) @@ -72,21 +76,21 @@ This is a bug with `SpeciesItemBoost` in [engine/battle/effect_commands.asm](/en **Fix:** -```asm +```diff ; Double the stat sla l rl h - - ld a, HIGH(MAX_STAT_VALUE) - cp h - jr c, .cap - ld a, LOW(MAX_STAT_VALUE) - cp l - ret nc - -.cap - ld h, HIGH(MAX_STAT_VALUE) - ld l, LOW(MAX_STAT_VALUE) ++ ++ ld a, HIGH(MAX_STAT_VALUE) ++ cp h ++ jr c, .cap ++ ld a, LOW(MAX_STAT_VALUE) ++ cp l ++ ret nc ++ ++.cap ++ ld h, HIGH(MAX_STAT_VALUE) ++ ld l, LOW(MAX_STAT_VALUE) ret ``` @@ -119,7 +123,7 @@ This is a bug with `DittoMetalPowder` in [engine/battle/effect_commands.asm](/en **Fix:** -```asm +```diff ld a, c srl a add c @@ -134,17 +138,17 @@ This is a bug with `DittoMetalPowder` in [engine/battle/effect_commands.asm](/en .done scf rr c - - ld a, HIGH(MAX_STAT_VALUE) - cp b - jr c, .cap - ld a, LOW(MAX_STAT_VALUE) - cp c - ret nc - -.cap - ld b, HIGH(MAX_STAT_VALUE) - ld c, LOW(MAX_STAT_VALUE) ++ ++ ld a, HIGH(MAX_STAT_VALUE) ++ cp b ++ jr c, .cap ++ ld a, LOW(MAX_STAT_VALUE) ++ cp c ++ ret nc ++ ++.cap ++ ld b, HIGH(MAX_STAT_VALUE) ++ ld c, LOW(MAX_STAT_VALUE) ret ``` @@ -155,35 +159,35 @@ This is a bug with `DittoMetalPowder` in [engine/battle/effect_commands.asm](/en ([Video](https://www.youtube.com/watch?v=zuCLMikWo4Y)) -This is a bug with `BattleCommand_BellyDrum` in [engine/battle/effect_commands.asm](/engine/battle/effect_commands.asm): +This is a bug with `BattleCommand_BellyDrum` in [engine/battle/move_effects/belly_drum.asm](/engine/battle/move_effects/belly_drum.asm): ```asm -BattleCommand_BellyDrum: ; 37c1a +BattleCommand_BellyDrum: ; bellydrum ; This command is buggy because it raises the user's attack ; before checking that it has enough HP to use the move. ; Swap the order of these two blocks to fix. call BattleCommand_AttackUp2 - ld a, [AttackMissed] + ld a, [wAttackMissed] and a jr nz, .failed - callab GetHalfMaxHP - callab CheckUserHasEnoughHP + callfar GetHalfMaxHP + callfar CheckUserHasEnoughHP jr nc, .failed ``` **Fix:** ```asm -BattleCommand_BellyDrum: ; 37c1a +BattleCommand_BellyDrum: ; bellydrum - callab GetHalfMaxHP - callab CheckUserHasEnoughHP + callfar GetHalfMaxHP + callfar CheckUserHasEnoughHP jr nc, .failed call BattleCommand_AttackUp2 - ld a, [AttackMissed] + ld a, [wAttackMissed] and a jr nz, .failed ``` @@ -242,7 +246,35 @@ DefenseDownHit: ([Video](https://www.youtube.com/watch?v=uRYyzKRatFk)) -*To do:* Identify specific code causing this bug and fix it. +This is a bug with `BattleCommand_Counter` in [engine/battle/move_effects/counter.asm](/engine/battle/move_effects/counter.asm) and `BattleCommand_MirrorCoat` in [engine/battle/move_effects/mirror_coat.asm](/engine/battle/move_effects/mirror_coat.asm): + +```asm + ; BUG: Move should fail with all non-damaging battle actions + ld hl, wCurDamage + ld a, [hli] + or [hl] + ret z +``` + +**Fix:** + +```diff + ld hl, wCurDamage + ld a, [hli] + or [hl] +- ret z ++ jp z, .failed +``` + +Add this to the end of each file: + +```diff ++.failed ++ ld a, 1 ++ ld [wEffectFailed], a ++ and a ++ ret +``` ## A Disabled but PP Up–enhanced move may not trigger Struggle @@ -256,12 +288,11 @@ This is a bug with `CheckPlayerHasUsableMoves` in [engine/battle/core.asm](/engi ```asm .done ; Bug: this will result in a move with PP Up confusing the game. - ; Replace with "and $3f" to fix. - and a + and a ; should be "and PP_MASK" ret nz .force_struggle - ld hl, BattleText_PkmnHasNoMovesLeft + ld hl, BattleText_MonHasNoMovesLeft call StdBattleTextBox ld c, 60 call DelayFrames @@ -269,7 +300,7 @@ This is a bug with `CheckPlayerHasUsableMoves` in [engine/battle/core.asm](/engi ret ``` -**Fix:** Change `and a` to `and $3f`. +**Fix:** Change `and a` to `and PP_MASK`. ## A Pokémon that fainted from Pursuit will have its old status condition when revived @@ -290,7 +321,7 @@ This bug affects Attract, Curse, Foresight, Mean Look, Mimic, Nightmare, Spider This is a bug with `CheckHiddenOpponent` in [engine/battle/effect_commands.asm](/engine/battle/effect_commands.asm): ```asm -CheckHiddenOpponent: ; 37daa +CheckHiddenOpponent: ; BUG: This routine should account for Lock-On and Mind Reader. ld a, BATTLE_VARS_SUBSTATUS3_OPP call GetBattleVar @@ -313,12 +344,12 @@ The code in `CheckHiddenOpponent` is completely redundant as `CheckHit` already ([Video](https://www.youtube.com/watch?v=202-iAsrIa8)) -This is a bug with `BattleCommand_BeatUp` in [engine/battle/effect_commands.asm](/engine/battle/effect_commands.asm): +This is a bug with `BattleCommand_BeatUp` in [engine/battle/move_effects/beat_up.asm](/engine/battle/move_effects/beat_up.asm): ```asm .got_mon ld a, [wd002] - ld hl, PartyMonNicknames + ld hl, wPartyMonNicknames call GetNick ld a, MON_HP call GetBeatupMonLocation @@ -327,11 +358,11 @@ This is a bug with `BattleCommand_BeatUp` in [engine/battle/effect_commands.asm] jp z, .beatup_fail ; fainted ld a, [wd002] ld c, a - ld a, [CurBattleMon] + ld a, [wCurBattleMon] ; BUG: this can desynchronize link battles ; Change "cp [hl]" to "cp c" to fix cp [hl] - ld hl, BattleMonStatus + ld hl, wBattleMonStatus jr z, .active_mon ld a, MON_STATUS call GetBeatupMonLocation @@ -344,6 +375,82 @@ This is a bug with `BattleCommand_BeatUp` in [engine/battle/effect_commands.asm] **Fix:** Change `cp [hl]` to `cp c`. +## Beat Up may fail to raise substitute + +*Fixing this bug will break compatibility with standard Pokémon Crystal for link battles.* +(Only the fixes denoted with "breaking" will actually break compatibility, the others just affect what's shown on the screen with the patched game) + +This is a bug in `BattleCommand_EndLoop` in [engine/battle/effect_commands.asm](/engine/battle/effect_commands.asm) that prevents the rest of the move's effect from being executed if the player or enemy only has one mon in their party while using Beat Up. + +It prevents the substitute from being raised and the King's Rock from working. + +```asm +.only_one_beatup + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + res SUBSTATUS_IN_LOOP, [hl] + call BattleCommand_BeatUpFailText + jp EndMoveEffect +``` + +**Fix (breaking):** Replace the last two lines with `ret`. +**Fix (cosmetics):** Call `BattleCommand_RaiseSub` before the `jp`. + +There's a similar oversight in `BattleCommand_FailureText` in [engine/battle/effect_commands.asm](/engine/battle/effect_commands.asm) that will prevent the substitute from being raised if Beat Up is protected against. + +```asm + cp EFFECT_MULTI_HIT + jr z, .multihit + cp EFFECT_DOUBLE_HIT + jr z, .multihit + cp EFFECT_POISON_MULTI_HIT + jr z, .multihit + jp EndMoveEffect + +.multihit + call BattleCommand_RaiseSub + jp EndMoveEffect +``` + +**Fix:** Check for `EFFECT_BEAT_UP` as well. + + +## Beat Up may trigger King's Rock even if it failed + +*Fixing this bug will break compatibility with standard Pokémon Crystal for link battles.* + +This is a bug in how `wAttackMissed` is never set by BeatUp, even when none of the 'mon have been able to attack (due to being fainted or having a status effect), the King's Rock may activate. + +This bug can be fixed in a plethora of ways, but the most straight-forward would be in `BattleCommand_BeatUpFailText` in [engine/battle/move_effects/beat_up.asm](/engine/battle/move_effects/beat_up.asm), as that's always ran before the king's rock effect. + +```asm +BattleCommand_BeatUpFailText: +; beatupfailtext + + ld a, [wBeatUpHitAtLeastOnce] + and a + ret nz + + jp PrintButItFailed +``` + +**Fix:** + +```diff +BattleCommand_BeatUpFailText: +; beatupfailtext + + ld a, [wBeatUpHitAtLeastOnce] + and a + ret nz ++ ++ inc a ++ ld [wAttackMissed], a + + jp PrintButItFailed +``` + + ## Present damage is incorrect in link battles *Fixing this bug will break compatibility with standard Pokémon Crystal for link battles.* @@ -352,10 +459,10 @@ This is a bug with `BattleCommand_BeatUp` in [engine/battle/effect_commands.asm] This bug existed for all battles in Gold and Silver, and was only fixed for single-player battles in Crystal to preserve link compatibility. -This is a bug with `BattleCommand_Present` in [engine/battle/effect_commands/present.asm](/engine/battle/effect_commands/present.asm): +This is a bug with `BattleCommand_Present` in [engine/battle/move_effects/present.asm](/engine/battle/move_effects/present.asm): ```asm -BattleCommand_Present: ; 37874 +BattleCommand_Present: ; present ld a, [wLinkMode] @@ -378,7 +485,7 @@ BattleCommand_Present: ; 37874 **Fix:** ```asm -BattleCommand_Present: ; 37874 +BattleCommand_Present: ; present push bc @@ -397,13 +504,13 @@ This is a bug with `AI_Smart_MeanLook` in [engine/battle/ai/scoring.asm](/engine ```asm ; 80% chance to greatly encourage this move if the enemy is badly poisoned (buggy). -; Should check PlayerSubStatus5 instead. - ld a, [EnemySubStatus5] +; Should check wPlayerSubStatus5 instead. + ld a, [wEnemySubStatus5] bit SUBSTATUS_TOXIC, a jr nz, .asm_38e26 ``` -**Fix:** Change `EnemySubStatus5` to `PlayerSubStatus5`. +**Fix:** Change `wEnemySubStatus5` to `wPlayerSubStatus5`. ## AI makes a false assumption about `CheckTypeMatchup` @@ -411,13 +518,13 @@ This is a bug with `AI_Smart_MeanLook` in [engine/battle/ai/scoring.asm](/engine In [engine/battle/effect_commands.asm](/engine/battle/effect_commands.asm): ```asm -BattleCheckTypeMatchup: ; 347c8 - ld hl, EnemyMonType1 +BattleCheckTypeMatchup: + ld hl, wEnemyMonType1 ld a, [hBattleTurn] and a jr z, CheckTypeMatchup - ld hl, BattleMonType1 -CheckTypeMatchup: ; 347d3 + ld hl, wBattleMonType1 +CheckTypeMatchup: ; There is an incorrect assumption about this function made in the AI related code: when ; the AI calls CheckTypeMatchup (not BattleCheckTypeMatchup), it assumes that placing the ; offensive type in a will make this function do the right thing. Since a is overwritten, @@ -442,25 +549,24 @@ CheckTypeMatchup: ; 347d3 This is a bug with `AI_HealStatus` in [engine/battle/ai/items.asm](/engine/battle/ai/items.asm): ```asm -AI_HealStatus: ; 384e0 - ld a, [CurOTMon] - ld hl, OTPartyMon1Status +AI_HealStatus: + ld a, [wCurOTMon] + ld hl, wOTPartyMon1Status ld bc, PARTYMON_STRUCT_LENGTH call AddNTimes xor a ld [hl], a - ld [EnemyMonStatus], a + ld [wEnemyMonStatus], a ; Bug: this should reset SUBSTATUS_NIGHTMARE too ; Uncomment the lines below to fix - ; ld hl, EnemySubStatus1 + ; ld hl, wEnemySubStatus1 ; res SUBSTATUS_NIGHTMARE, [hl] - ld hl, EnemySubStatus5 + ld hl, wEnemySubStatus5 res SUBSTATUS_TOXIC, [hl] ret -; 384f7 ``` -**Fix:** Uncomment `ld hl, EnemySubStatus1` and `res SUBSTATUS_NIGHTMARE, [hl]`. +**Fix:** Uncomment `ld hl, wEnemySubStatus1` and `res SUBSTATUS_NIGHTMARE, [hl]`. ## HP bar animation is slow for high HP @@ -495,15 +601,16 @@ This is a bug with `LongAnim_UpdateVariables` in [engine/battle/anim_hp_bar.asm] ([Video](https://www.youtube.com/watch?v=9KyNVIZxJvI)) -This is a bug with `ShortHPBar_CalcPixelFrame` in [engine/anim_hp_bar.asm](/engine/anim_hp_bar.asm): +This is a bug with `ShortHPBar_CalcPixelFrame` in [engine/battle/anim_hp_bar.asm](/engine/battle/anim_hp_bar.asm): ```asm ld b, 0 -; This routine is buggy. If [wCurHPAnimMaxHP] * [wCurHPBarPixels] is divisible -; by 48, the loop runs one extra time. To fix, uncomment the line below. +; This routine is buggy. If [wCurHPAnimMaxHP] * [wCurHPBarPixels] is +; divisible by HP_BAR_LENGTH_PX, the loop runs one extra time. +; To fix, uncomment the line below. .loop ld a, l - sub 6 * 8 + sub HP_BAR_LENGTH_PX ld l, a ld a, h sbc $0 @@ -523,12 +630,12 @@ This is a bug with `ShortHPBar_CalcPixelFrame` in [engine/anim_hp_bar.asm](/engi This can bring Pokémon straight from level 1 to 100 by gaining just a few experience points. -This is a bug with `CalcExpAtLevel` in [main.asm](/main.asm): +This is a bug with `CalcExpAtLevel` in [engine/pokemon/experience.asm](/engine/pokemon/experience.asm): ```asm -CalcExpAtLevel: ; 50e47 +CalcExpAtLevel: ; (a/b)*n**3 + c*n**2 + d*n - e - ld a, [BaseGrowthRate] + ld a, [wBaseGrowthRate] add a add a ld c, a @@ -539,23 +646,23 @@ CalcExpAtLevel: ; 50e47 **Fix:** -```asm -CalcExpAtLevel: ; 50e47 +```diff +CalcExpAtLevel: ; (a/b)*n**3 + c*n**2 + d*n - e - ld a, d - cp 1 - jr nz, .UseExpFormula -; Pokémon have 0 experience at level 1 - xor a - ld hl, hProduct - ld [hli], a - ld [hli], a - ld [hli], a - ld [hl], a - ret - -.UseExpFormula - ld a, [BaseGrowthRate] ++ ld a, d ++ cp 1 ++ jr nz, .UseExpFormula ++; Pokémon have 0 experience at level 1 ++ xor a ++ ld hl, hProduct ++ ld [hli], a ++ ld [hli], a ++ ld [hli], a ++ ld [hl], a ++ ret ++ ++.UseExpFormula + ld a, [wBaseGrowthRate] add a add a ld c, a @@ -569,45 +676,44 @@ CalcExpAtLevel: ; 50e47 ([Video](https://www.youtube.com/watch?v=o54VjpAEoO8)) -This is a bug with `Text_ABoostedStringBuffer2ExpPoints` and `Text_StringBuffer2ExpPoints` in [text/common_2.asm](/text/common_2.asm): +This is a bug with `Text_ABoostedStringBuffer2ExpPoints` and `Text_StringBuffer2ExpPoints` in [data/text/common_2.asm](/data/text/common_2.asm): ```asm Text_ABoostedStringBuffer2ExpPoints:: text_start line "a boosted" cont "@" - deciram StringBuffer2, 2, 4 + deciram wStringBuffer2, 2, 4 text " EXP. Points!" prompt Text_StringBuffer2ExpPoints:: text_start line "@" - deciram StringBuffer2, 2, 4 + deciram wStringBuffer2, 2, 4 text " EXP. Points!" prompt ``` -**Fix:** Change both `deciram StringBuffer2, 2, 4` to `deciram StringBuffer2, 2, 5`. +**Fix:** Change both `deciram wStringBuffer2, 2, 4` to `deciram wStringBuffer2, 2, 5`. ## BRN/PSN/PAR do not affect catch rate -This is a bug with `PokeBall` in [items/item_effects.asm](/items/item_effects.asm): +This is a bug with `PokeBallEffect` in [engine/items/item_effects.asm](/engine/items/item_effects.asm): ```asm -.statuscheck ; This routine is buggy. It was intended that SLP and FRZ provide a higher ; catch rate than BRN/PSN/PAR, which in turn provide a higher catch rate than ; no status effect at all. But instead, it makes BRN/PSN/PAR provide no ; benefit. ; Uncomment the line below to fix this. ld b, a - ld a, [EnemyMonStatus] + ld a, [wEnemyMonStatus] and 1 << FRZ | SLP ld c, 10 jr nz, .addstatus - ; ld a, [EnemyMonStatus] + ; ld a, [wEnemyMonStatus] and a ld c, 5 jr nz, .addstatus @@ -620,7 +726,7 @@ This is a bug with `PokeBall` in [items/item_effects.asm](/items/item_effects.as .max_1 ``` -**Fix:** Uncomment `ld a, [EnemyMonStatus]`. +**Fix:** Uncomment `ld a, [wEnemyMonStatus]`. ## Moon Ball does not boost catch rate @@ -639,7 +745,7 @@ MoonBallMultiplier: ; No Pokémon evolve with Burn Heal, ; so Moon Balls always have a catch rate of 1×. push bc - ld a, BANK(EvosAttacks) + ld a, BANK("Evolutions and Attacks") call GetFarByte cp MOON_STONE_RED ; BURN_HEAL pop bc @@ -701,29 +807,29 @@ FastBallMultiplier: *Fixing this bug will break compatibility with standard Pokémon Crystal for link battles.* -This is a bug with `ItemAttributes` in [items/attributes.asm](/items/attributes.asm): +This is a bug with `ItemAttributes` in [data/items/attributes.asm](/data/items/attributes.asm): ```asm -; DRAGON FANG +; DRAGON_FANG item_attribute 100, 0, 0, CANT_SELECT, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE ... -; DRAGON SCALE +; DRAGON_SCALE item_attribute 2100, HELD_DRAGON_BOOST, 10, CANT_SELECT, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE ``` -**Fix:** Move `HELD_DRAGON_BOOST` to the `DRAGON FANG` attributes and `0` to `DRAGON SCALE`. +**Fix:** Move `HELD_DRAGON_BOOST` to the `DRAGON_FANG` attributes and `0` to `DRAGON_SCALE`. ## Daisy's grooming doesn't always increase happiness -This is a bug with `MassageOrHaircut` in [engine/events/special.asm](/engine/events/special.asm): +This is a bug with `HaircutOrGrooming` in [engine/events/haircut.asm](/engine/events/haircut.asm): ```asm ; Bug: Subtracting $ff from $ff fails to set c. ; This can result in overflow into the next data array. -; In the case of getting a massage from Daisy, we bleed +; In the case of getting a grooming from Daisy, we bleed ; into CopyPokemonName_Buffer1_Buffer3, which passes ; $d0 to ChangeHappiness and returns $73 to the script. ; The end result is that there is a 0.4% chance your @@ -739,29 +845,36 @@ This is a bug with `MassageOrHaircut` in [engine/events/special.asm](/engine/eve .ok inc hl ld a, [hli] - ld [ScriptVar], a + ld [wScriptVar], a ld c, [hl] call ChangeHappiness ret ... -Data_DaisyMassage: ; 746b - db $ff, 2, HAPPINESS_MASSAGE ; 99.6% chance +INCLUDE "data/events/happiness_probabilities.asm" -CopyPokemonName_Buffer1_Buffer3: ; 746e - ld hl, StringBuffer1 - ld de, StringBuffer3 +CopyPokemonName_Buffer1_Buffer3: + ld hl, wStringBuffer1 + ld de, wStringBuffer3 ld bc, MON_NAME_LENGTH jp CopyBytes ``` -**Fix:** +In [data/events/happiness_probabilities.asm](/data/events/happiness_probabilities.asm): ```asm -Data_DaisyMassage: ; 746b - db $80, 2, HAPPINESS_MASSAGE ; 50% chance - db $ff, 2, HAPPINESS_MASSAGE ; 50% chance +HappinessData_DaisysGrooming: + db $ff, 2, HAPPINESS_GROOMING ; 99.6% chance +``` + +**Fix:** + +```diff +HappinessData_DaisysGrooming: +- db $ff, 2, HAPPINESS_GROOMING ; 99.6% chance ++ db $80, 2, HAPPINESS_GROOMING ; 50% chance ++ db $ff, 2, HAPPINESS_GROOMING ; 50% chance ``` @@ -771,19 +884,23 @@ This is a bug with `LoadEnemyMon.CheckMagikarpArea` in [engine/battle/core.asm]( ```asm .CheckMagikarpArea: -; The z checks are supposed to be nz -; Instead, all maps in GROUP_LAKE_OF_RAGE (mahogany area) -; and routes 20 and 44 are treated as Lake of Rage +; The "jr z" checks are supposed to be "jr nz". + +; Instead, all maps in GROUP_LAKE_OF_RAGE (Mahogany area) +; and Routes 20 and 44 are treated as Lake of Rage. ; This also means Lake of Rage Magikarp can be smaller than ones -; caught elsewhere rather than the other way around +; caught elsewhere rather than the other way around. + +; Intended behavior enforces a minimum size at Lake of Rage. +; The real behavior prevents a minimum size in the Lake of Rage area. -; Intended behavior enforces a minimum size at Lake of Rage -; The real behavior prevents size flooring in the Lake of Rage area - ld a, [MapGroup] +; Moreover, due to the check not being translated to feet+inches, all Magikarp +; smaller than 4'0" may be caught by the filter, a lot more than intended. + ld a, [wMapGroup] cp GROUP_LAKE_OF_RAGE jr z, .Happiness - ld a, [MapNumber] + ld a, [wMapNumber] cp MAP_LAKE_OF_RAGE jr z, .Happiness ``` @@ -797,31 +914,31 @@ This is a bug with `LoadEnemyMon.CheckMagikarpArea` in [engine/battle/core.asm]( ```asm ; Get Magikarp's length - ld de, EnemyMonDVs - ld bc, PlayerID + ld de, wEnemyMonDVs + ld bc, wPlayerID callfar CalcMagikarpLength -; No reason to keep going if length > 1536 (i.e. if length / 256 != 6) +; No reason to keep going if length > 1536 mm (i.e. if HIGH(length) > 6 feet) ld a, [wMagikarpLength] - cp HIGH(1536) ; this compares to 6'0'', should be cp 5 + cp HIGH(1536) ; should be "cp 5", since 1536 mm = 5'0", but HIGH(1536) = 6 jr nz, .CheckMagikarpArea ; 5% chance of skipping both size checks call Random cp 5 percent jr c, .CheckMagikarpArea -; Try again if length > 1615 +; Try again if length >= 1616 mm (i.e. if LOW(length) >= 3 inches) ld a, [wMagikarpLength + 1] - cp LOW(1616) ; this compares to 6'80'', should be cp 3 + cp LOW(1616) ; should be "cp 3", since 1616 mm = 5'3", but LOW(1616) = 80 jr nc, .GenerateDVs ; 20% chance of skipping this check call Random cp 20 percent - 1 jr c, .CheckMagikarpArea -; Try again if length > 1599 +; Try again if length >= 1600 mm (i.e. if LOW(length) >= 2 inches) ld a, [wMagikarpLength + 1] - cp LOW(1600) ; this compares to 6'64'', should be cp 2 + cp LOW(1600) ; should be "cp 2", since 1600 mm = 5'2", but LOW(1600) = 64 jr nc, .GenerateDVs ``` @@ -833,7 +950,7 @@ This is a bug with `LoadEnemyMon.CheckMagikarpArea` in [engine/battle/core.asm]( This is a bug with `CalcMagikarpLength.BCLessThanDE` in [engine/events/magikarp.asm](/engine/events/magikarp.asm): ```asm -.BCLessThanDE: ; fbc9a +.BCLessThanDE: ; Intention: Return bc < de. ; Reality: Return b < d. ld a, b @@ -843,7 +960,6 @@ This is a bug with `CalcMagikarpLength.BCLessThanDE` in [engine/events/magikarp. ld a, c cp e ret -; fbca1 ``` **Fix:** Delete `ret nc`. @@ -853,59 +969,80 @@ This is a bug with `CalcMagikarpLength.BCLessThanDE` in [engine/events/magikarp. ([Video](https://www.youtube.com/watch?v=eij_1060SMc)) -This is a bug with `StartTrainerBattle_DetermineWhichAnimation` in [engine/battle_start.asm](/engine/battle_start.asm): +This is a bug with `StartTrainerBattle_DetermineWhichAnimation` in [engine/battle/battle_transition.asm](/engine/battle/battle_transition.asm): ```asm -StartTrainerBattle_DetermineWhichAnimation: ; 8c365 (23:4365) +StartTrainerBattle_DetermineWhichAnimation: ; The screen flashes a different number of times depending on the level of ; your lead Pokemon relative to the opponent's. -; BUG: BattleMonLevel and EnemyMonLevel are not set at this point, so whatever +; BUG: wBattleMonLevel and wEnemyMonLevel are not set at this point, so whatever ; values happen to be there will determine the animation. ld de, 0 - ld a, [BattleMonLevel] + ld a, [wBattleMonLevel] add 3 - ld hl, EnemyMonLevel + ld hl, wEnemyMonLevel cp [hl] - jr nc, .okay - set 0, e -.okay - ld a, [wPermission] + jr nc, .not_stronger + set TRANS_STRONGER_F, e +.not_stronger + ld a, [wEnvironment] cp CAVE - jr z, .okay2 - cp PERM_5 - jr z, .okay2 + jr z, .cave + cp ENVIRONMENT_5 + jr z, .cave cp DUNGEON - jr z, .okay2 - set 1, e -.okay2 + jr z, .cave + set TRANS_NO_CAVE_F, e +.cave ld hl, .StartingPoints add hl, de ld a, [hl] ld [wJumptableIndex], a ret -; 8c38f (23:438f) -.StartingPoints: ; 8c38f - db 1, 9 - db 16, 24 -; 8c393 +.StartingPoints: +; entries correspond to TRANS_* constants + db BATTLETRANSITION_CAVE + db BATTLETRANSITION_CAVE_STRONGER + db BATTLETRANSITION_NO_CAVE + db BATTLETRANSITION_NO_CAVE_STRONGER ``` *To do:* Fix this bug. +## A "HOF Master!" title for 200-Time Famers is defined but inaccessible + +([Video](https://www.youtube.com/watch?v=iHkWubvxmSg)) + +This is a bug with `_HallOfFamePC.DisplayMonAndStrings` in [engine/events/halloffame.asm](/engine/events/halloffame.asm): + +```asm + ld a, [wHallOfFameTempWinCount] + cp HOF_MASTER_COUNT + 1 ; should be HOF_MASTER_COUNT + jr c, .print_num_hof + ld de, .HOFMaster + hlcoord 1, 2 + call PlaceString + hlcoord 13, 2 + jr .finish +``` + +**Fix:** Change `HOF_MASTER_COUNT + 1` to `HOF_MASTER_COUNT`. + + ## Slot machine payout sound effects cut each other off ([Video](https://www.youtube.com/watch?v=ojq3xqfRF6I)) -This is a bug with `Slots_PayoutAnim` in [engine/slot_machine.asm](/engine/slot_machine.asm): +This is a bug with `Slots_PayoutAnim` in [engine/games/slot_machine.asm](/engine/games/slot_machine.asm): ```asm .okay ld [hl], e dec hl ld [hl], d - ld a, [wcf64] + ld a, [wSlotsDelay] and $7 ret z ; ret nz would be more appropriate ld de, SFX_GET_COIN_FROM_SLOTS @@ -918,10 +1055,10 @@ This is a bug with `Slots_PayoutAnim` in [engine/slot_machine.asm](/engine/slot_ ## Team Rocket battle music is not used for Executives or Scientists -This is a bug with `PlayBattleMusic` in [main.asm](/main.asm): +This is a bug with `PlayBattleMusic` in [engine/battle/start_battle.asm](/engine/battle/start_battle.asm): ```asm - ; really, they should have included admins and scientists here too... + ; They should have included EXECUTIVEM, EXECUTIVEF, and SCIENTIST too... ld de, MUSIC_ROCKET_BATTLE cp GRUNTM jr z, .done @@ -931,24 +1068,24 @@ This is a bug with `PlayBattleMusic` in [main.asm](/main.asm): **Fix:** -```asm +```diff ld de, MUSIC_ROCKET_BATTLE cp GRUNTM jr z, .done cp GRUNTF jr z, .done - cp EXECUTIVEM - jr z, .done - cp EXECUTIVEF - jr z, .done - cp SCIENTIST - jr z, .done ++ cp EXECUTIVEM ++ jr z, .done ++ cp EXECUTIVEF ++ jr z, .done ++ cp SCIENTIST ++ jr z, .done ``` ## No bump noise if standing on tile `$3E` -This is a bug with `DoPlayerMovement.CheckWarp` in [engine/player_movement.asm](/engine/player_movement.asm): +This is a bug with `DoPlayerMovement.CheckWarp` in [engine/overworld/player_movement.asm](/engine/overworld/player_movement.asm): ```asm ; Bug: Since no case is made for STANDING here, it will check @@ -956,20 +1093,20 @@ This is a bug with `DoPlayerMovement.CheckWarp` in [engine/player_movement.asm]( ; This causes wd041 to be nonzero when standing on tile $3e, ; making bumps silent. - ld a, [WalkingDirection] + ld a, [wWalkingDirection] ; cp STANDING ; jr z, .not_warp ld e, a ld d, 0 ld hl, .EdgeWarps add hl, de - ld a, [PlayerStandingTile] + ld a, [wPlayerStandingTile] cp [hl] jr nz, .not_warp ld a, 1 ld [wd041], a - ld a, [WalkingDirection] + ld a, [wWalkingDirection] ; This is in the wrong place. cp STANDING jr z, .not_warp @@ -977,21 +1114,26 @@ This is a bug with `DoPlayerMovement.CheckWarp` in [engine/player_movement.asm]( **Fix:** -```asm - ld a, [WalkingDirection] - cp STANDING - jr z, .not_warp +```diff + ld a, [wWalkingDirection] +- ; cp STANDING +- ; jr z, .not_warp ++ cp STANDING ++ jr z, .not_warp ld e, a ld d, 0 ld hl, .EdgeWarps add hl, de - ld a, [PlayerStandingTile] + ld a, [wPlayerStandingTile] cp [hl] jr nz, .not_warp ld a, 1 ld [wd041], a - ld a, [WalkingDirection] + ld a, [wWalkingDirection] +- ; This is in the wrong place. +- cp STANDING +- jr z, .not_warp ``` @@ -1002,7 +1144,7 @@ This is a bug with `DoPlayerMovement.CheckWarp` in [engine/player_movement.asm]( The exact cause is unknown, but a workaround exists for `DexEntryScreen_MenuActionJumptable.Cry` in [engine/pokedex/pokedex.asm](/engine/pokedex/pokedex.asm): ```asm -.Cry: ; 40340 +.Cry: call Pokedex_GetSelectedMon ld a, [wd265] call GetCryIndex @@ -1015,8 +1157,8 @@ The exact cause is unknown, but a workaround exists for `DexEntryScreen_MenuActi **Workaround:** ```asm -.Cry: ; 40340 - ld a, [CurPartySpecies] +.Cry: + ld a, [wCurPartySpecies] call PlayMonCry ret ``` @@ -1051,7 +1193,7 @@ This bug prevents you from using blocksets with more than 128 blocks. In [home/map.asm](/home/map.asm): ```asm - ; Set hl to the address of the current metatile data ([TilesetBlocksAddress] + (a) tiles). + ; Set hl to the address of the current metatile data ([wTilesetBlocksAddress] + (a) tiles). ; This is buggy; it wraps around past 128 blocks. ; To fix, uncomment the line below. add a ; Comment or delete this line to fix the above bug. @@ -1061,10 +1203,10 @@ In [home/map.asm](/home/map.asm): add hl, hl add hl, hl add hl, hl - ld a, [TilesetBlocksAddress] + ld a, [wTilesetBlocksAddress] add l ld l, a - ld a, [TilesetBlocksAddress + 1] + ld a, [wTilesetBlocksAddress + 1] adc h ld h, a ``` @@ -1079,20 +1221,20 @@ In [home/map.asm](/home/map.asm): *To do:* Identify specific code causing this bug and fix it. -## `Function6ec1` does not correctly limit object movement +## Swimming NPCs aren't limited by their movement radius -This bug is why the Lapras in Union Cave, which uses `SPRITEMOVEDATA_LAPRAS`, is not restricted by its `1, 1` movement radius. +This bug is why the Lapras in [maps/UnionCaveB2F.asm](/maps/UnionCaveB2F.asm), which uses `SPRITEMOVEDATA_SWIM_WANDER`, is not restricted by its `1, 1` movement radius. -In [engine/npc_movement.asm](/engine/npc_movement.asm): +This is a bug with `CanObjectMoveInDirection` in [engine/overworld/npc_movement.asm](/engine/overworld/npc_movement.asm): ```asm ld hl, OBJECT_FLAGS1 add hl, bc - bit 4, [hl] ; lost, uncomment next line to fix -; jr nz, .resume + bit NOCLIP_TILES_F, [hl] ; lost, uncomment next line to fix + ; jr nz, .noclip_tiles ``` -**Fix:** Uncomment `jr nz, .resume`. +**Fix:** Uncomment `jr nz, .noclip_tiles`. ## `CheckOwnMon` only checks the first five letters of OT names @@ -1101,16 +1243,16 @@ In [engine/npc_movement.asm](/engine/npc_movement.asm): This bug can allow you to talk to Eusine in Celadon City and encounter Ho-Oh with only traded legendary beasts. -In [engine/search.asm](/engine/search.asm): +In [engine/pokemon/search.asm](/engine/pokemon/search.asm): ```asm ; check OT ; This only checks five characters, which is fine for the Japanese version, ; but in the English version the player name is 7 characters, so this is wrong. - ld hl, PlayerName + ld hl, wPlayerName -rept NAME_LENGTH_JAPANESE +- 2 ; should be PLAYER_NAME_LENGTH +- 2 +rept NAME_LENGTH_JAPANESE + -2 ; should be PLAYER_NAME_LENGTH + -2 ld a, [de] cp [hl] jr nz, .notfound @@ -1123,87 +1265,92 @@ endr ld a, [de] cp [hl] jr z, .found - -.notfound - pop de - pop hl - pop bc - and a - ret ``` -**Fix:** Change `rept NAME_LENGTH_JAPANESE +- 2` to `rept PLAYER_NAME_LENGTH +- 2`. +**Fix:** Change `rept NAME_LENGTH_JAPANESE + -2` to `rept PLAYER_NAME_LENGTH + -2`. ## Catching a Transformed Pokémon always catches a Ditto This bug can affect Mew or Pokémon other than Ditto that used Transform via Mirror Move or Sketch. -This is a bug with `PokeBall` in [items/item_effects.asm](/items/item_effects.asm): +This is a bug with `PokeBallEffect` in [engine/items/item_effects.asm](/engine/items/item_effects.asm): ```asm - ld hl, EnemySubStatus5 + ld hl, wEnemySubStatus5 ld a, [hl] push af set SUBSTATUS_TRANSFORMED, [hl] ; This code is buggy. Any wild Pokémon that has Transformed will be ; caught as a Ditto, even if it was something else like Mew. -; To fix, do not set [TempEnemyMonSpecies] to DITTO. +; To fix, do not set [wTempEnemyMonSpecies] to DITTO. bit SUBSTATUS_TRANSFORMED, a jr nz, .ditto jr .not_ditto .ditto ld a, DITTO - ld [TempEnemyMonSpecies], a + ld [wTempEnemyMonSpecies], a jr .load_data .not_ditto set SUBSTATUS_TRANSFORMED, [hl] ld hl, wEnemyBackupDVs - ld a, [EnemyMonDVs] + ld a, [wEnemyMonDVs] ld [hli], a - ld a, [EnemyMonDVs + 1] + ld a, [wEnemyMonDVs + 1] ld [hl], a .load_data - ld a, [TempEnemyMonSpecies] - ld [CurPartySpecies], a - ld a, [EnemyMonLevel] - ld [CurPartyLevel], a - callba LoadEnemyMon + ld a, [wTempEnemyMonSpecies] + ld [wCurPartySpecies], a + ld a, [wEnemyMonLevel] + ld [wCurPartyLevel], a + farcall LoadEnemyMon pop af - ld [EnemySubStatus5], a + ld [wEnemySubStatus5], a ``` **Fix:** -```asm - ld hl, EnemySubStatus5 +```diff + ld hl, wEnemySubStatus5 ld a, [hl] push af set SUBSTATUS_TRANSFORMED, [hl] +-; This code is buggy. Any wild Pokémon that has Transformed will be +-; caught as a Ditto, even if it was something else like Mew. +-; To fix, do not set [wTempEnemyMonSpecies] to DITTO. bit SUBSTATUS_TRANSFORMED, a - jr nz, .load_data - +- jr nz, .ditto +- jr .not_ditto ++ jr nz, .load_data + +-.ditto +- ld a, DITTO +- ld [wTempEnemyMonSpecies], a +- jr .load_data +- +-.not_ditto +- set SUBSTATUS_TRANSFORMED, [hl] ld hl, wEnemyBackupDVs - ld a, [EnemyMonDVs] + ld a, [wEnemyMonDVs] ld [hli], a - ld a, [EnemyMonDVs + 1] + ld a, [wEnemyMonDVs + 1] ld [hl], a .load_data - ld a, [TempEnemyMonSpecies] - ld [CurPartySpecies], a - ld a, [EnemyMonLevel] - ld [CurPartyLevel], a - callba LoadEnemyMon + ld a, [wTempEnemyMonSpecies] + ld [wCurPartySpecies], a + ld a, [wEnemyMonLevel] + ld [wCurPartyLevel], a + farcall LoadEnemyMon pop af - ld [EnemySubStatus5], a + ld [wEnemySubStatus5], a ``` @@ -1211,86 +1358,124 @@ This is a bug with `PokeBall` in [items/item_effects.asm](/items/item_effects.as ([Video](https://www.youtube.com/watch?v=v1ErZdLCIyU)) -This is a bug with `ParkBall` in [items/item_effects.asm](/items/item_effects.asm): +This is a bug with `PokeBallEffect` in [engine/items/item_effects.asm](/engine/items/item_effects.asm): ```asm .room_in_party xor a ld [wWildMon], a - ld a, [CurItem] + ld a, [wCurItem] cp PARK_BALL call nz, ReturnToBattle_UseBall ``` **Fix:** -```asm +```diff .room_in_party xor a ld [wWildMon], a - ld a, [BattleType] - cp BATTLETYPE_CONTEST +- ld a, [wCurItem] +- cp PARK_BALL ++ ld a, [wBattleType] ++ cp BATTLETYPE_CONTEST call nz, ReturnToBattle_UseBall ``` ## `HELD_CATCH_CHANCE` has no effect -This is a bug with `PokeBall` in [items/item_effects.asm](/items/item_effects.asm): +This is a bug with `PokeBallEffect` in [engine/items/item_effects.asm](/engine/items/item_effects.asm): ```asm - ; BUG: callba overwrites a, - ; and GetItemHeldEffect takes b anyway. - - ; This is probably the reason - ; the HELD_CATCH_CHANCE effect - ; is never used. - + ; BUG: farcall overwrites a, and GetItemHeldEffect takes b anyway. + ; This is probably the reason the HELD_CATCH_CHANCE effect is never used. ; Uncomment the line below to fix. - - ld a, [BattleMonItem] -; ld b, a - callba GetItemHeldEffect + ld d, a + push de + ld a, [wBattleMonItem] + ; ld b, a + farcall GetItemHeldEffect ld a, b cp HELD_CATCH_CHANCE + pop de + ld a, d + jr nz, .max_2 + add c + jr nc, .max_2 + ld a, $ff +.max_2 ``` **Fix:** Uncomment `ld b, a`. -## Only the first three `EvosAttacks` evolution entries can have Stone compatibility reported correctly +## Only the first three evolution entries can have Stone compatibility reported correctly -This is a bug with `PlacePartyMonEvoStoneCompatibility.DetermineCompatibility` in [engine/party_menu.asm](/engine/party_menu.asm): +This is a bug with `PlacePartyMonEvoStoneCompatibility.DetermineCompatibility` in [engine/pokemon/party_menu.asm](/engine/pokemon/party_menu.asm): ```asm -.DetermineCompatibility: ; 50268 - ld de, StringBuffer1 +.DetermineCompatibility: + ld de, wStringBuffer1 ld a, BANK(EvosAttacksPointers) ld bc, 2 call FarCopyBytes - ld hl, StringBuffer1 + ld hl, wStringBuffer1 ld a, [hli] ld h, [hl] ld l, a - ld de, StringBuffer1 - ld a, BANK(EvosAttacks) - ld bc, $a + ld de, wStringBuffer1 + ld a, BANK("Evolutions and Attacks") + ld bc, 10 call FarCopyBytes ``` -**Fix:** Change `ld bc, $a` to `ld bc, $10` to support up to five Stone entries. +**Fix:** Change `ld bc, 10` to `ld bc, wStringBuffer2 - wStringBuffer1` to support up to six Stone entries. + + +## `EVOLVE_STAT` can break Stone compatibility reporting + +This is a bug with `PlacePartyMonEvoStoneCompatibility.DetermineCompatibility` in [engine/pokemon/party_menu.asm](/engine/pokemon/party_menu.asm): + +```asm +.loop2 + ld a, [hli] + and a + jr z, .nope + inc hl + inc hl + cp EVOLVE_ITEM + jr nz, .loop2 +``` + +**Fix:** + +```asm +.loop2 + ld a, [hli] + and a + jr z, .nope ++ cp EVOLVE_STAT ++ jr nz, .not_four_bytes ++ inc hl ++.not_four_bytes + inc hl + inc hl + cp EVOLVE_ITEM + jr nz, .loop2 +``` ## `ScriptCall` can overflow `wScriptStack` and crash -In [engine/scripting.asm](/engine/scripting.asm): +In [engine/overworld/scripting.asm](/engine/overworld/scripting.asm): ```asm ScriptCall: ; Bug: The script stack has a capacity of 5 scripts, yet there is ; nothing to stop you from pushing a sixth script. The high part ; of the script address can then be overwritten by modifications -; to ScriptDelay, causing the script to return to the rst/interrupt +; to wScriptDelay, causing the script to return to the rst/interrupt ; space. push de @@ -1303,32 +1488,34 @@ ScriptCall: add hl, de add hl, de pop de - ld a, [ScriptBank] + ld a, [wScriptBank] ld [hli], a - ld a, [ScriptPos] + ld a, [wScriptPos] ld [hli], a - ld a, [ScriptPos + 1] + ld a, [wScriptPos + 1] ld [hl], a ld a, b - ld [ScriptBank], a + ld [wScriptBank], a ld a, e - ld [ScriptPos], a + ld [wScriptPos], a ld a, d - ld [ScriptPos + 1], a + ld [wScriptPos + 1], a ret ``` +*To do:* Fix this bug. + ## `LoadSpriteGFX` does not limit the capacity of `UsedSprites` -In [engine/overworld.asm](/engine/overworld.asm): +In [engine/overworld/overworld.asm](/engine/overworld/overworld.asm): ```asm -LoadSpriteGFX: ; 14306 +LoadSpriteGFX: ; Bug: b is not preserved, so it's useless as a next count. ; Uncomment the lines below to fix. - ld hl, UsedSprites + ld hl, wUsedSprites ld b, SPRITE_GFX_LIST_CAPACITY .loop ld a, [hli] @@ -1350,7 +1537,6 @@ LoadSpriteGFX: ; 14306 ; pop bc ld a, l ret -; 1431e ``` **Fix:** Uncomment `push bc` and `pop bc`. @@ -1358,14 +1544,13 @@ LoadSpriteGFX: ; 14306 ## `ChooseWildEncounter` doesn't really validate the wild Pokémon species -In [engine/wildmons.asm](/engine/wildmons.asm): +In [engine/overworld/wildmons.asm](/engine/overworld/wildmons.asm): ```asm -ChooseWildEncounter: ; 2a14f +ChooseWildEncounter: ... - ld a, b - ld [CurPartyLevel], a + ld [wCurPartyLevel], a ld b, [hl] ; ld a, b call ValidateTempWildMonSpecies @@ -1377,17 +1562,18 @@ ChooseWildEncounter: ; 2a14f ... -ValidateTempWildMonSpecies: ; 2a4a0 +ValidateTempWildMonSpecies: ; Due to a development oversight, this function is called with the wild Pokemon's level, not its species, in a. ``` **Fix:** -```asm +```diff ld a, b - ld [CurPartyLevel], a + ld [wCurPartyLevel], a ld b, [hl] - ld a, b +- ; ld a, b ++ ld a, b call ValidateTempWildMonSpecies jr c, .nowildbattle @@ -1397,7 +1583,7 @@ ValidateTempWildMonSpecies: ; 2a4a0 ## `TryObjectEvent` arbitrary code execution -In [engine/events.asm](/engine/events.asm): +In [engine/overworld/events.asm](/engine/overworld/events.asm): ```asm ; Bug: If IsInArray returns nc, data at bc will be executed as code. @@ -1423,15 +1609,16 @@ In [engine/events.asm](/engine/events.asm): **Fix:** Uncomment `pop bc`. -## `Special_CheckBugContestContestantFlag` can read beyond its data table +## `CheckBugContestContestantFlag` can read beyond its data table In [engine/events/bug_contest/contest_2.asm](/engine/events/bug_contest/contest_2.asm): ```asm -Special_CheckBugContestContestantFlag: ; 139ed +CheckBugContestContestantFlag: ; Checks the flag of the Bug Catching Contestant whose index is loaded in a. -; Bug: If a >= 10 when this is called, it will read beyond the table. +; Bug: If a >= NUM_BUG_CONTESTANTS when this is called, +; it will read beyond the table. ld hl, BugCatchingContestantEventFlagTable ld e, a @@ -1444,29 +1631,19 @@ Special_CheckBugContestContestantFlag: ; 139ed ld b, CHECK_FLAG call EventFlagAction ret -; 139fe -BugCatchingContestantEventFlagTable: ; 139fe - dw EVENT_BUG_CATCHING_CONTESTANT_1A - dw EVENT_BUG_CATCHING_CONTESTANT_2A - dw EVENT_BUG_CATCHING_CONTESTANT_3A - dw EVENT_BUG_CATCHING_CONTESTANT_4A - dw EVENT_BUG_CATCHING_CONTESTANT_5A - dw EVENT_BUG_CATCHING_CONTESTANT_6A - dw EVENT_BUG_CATCHING_CONTESTANT_7A - dw EVENT_BUG_CATCHING_CONTESTANT_8A - dw EVENT_BUG_CATCHING_CONTESTANT_9A - dw EVENT_BUG_CATCHING_CONTESTANT_10A -; 13a12 +INCLUDE "data/events/bug_contest_flags.asm" ``` +However, `a < NUM_BUG_CONTESTANTS` should always be true, so in practice this is not a problem. + ## `ClearWRAM` only clears WRAM bank 1 In [home/init.asm](/home/init.asm): ```asm -ClearWRAM:: ; 25a +ClearWRAM:: ; Wipe swappable WRAM banks (1-7) ; Assumes CGB or AGB @@ -1483,7 +1660,6 @@ ClearWRAM:: ; 25a cp 8 jr nc, .bank_loop ; Should be jr c ret -; 270 ``` **Fix:** Change `jr nc, .bank_loop` to `jr c, .bank_loop`. diff --git a/docs/design_flaws.md b/docs/design_flaws.md index 7dc4d13db..c7a032702 100644 --- a/docs/design_flaws.md +++ b/docs/design_flaws.md @@ -8,8 +8,9 @@ These are parts of the code that do not work *incorrectly*, like [bugs and glitc - [Pic banks are offset by `PICS_FIX`](#pic-banks-are-offset-by-pics_fix) - [`PokemonPicPointers` and `UnownPicPointers` are assumed to start at the same address](#pokemonpicpointers-and-unownpicpointers-are-assumed-to-start-at-the-same-address) - [Footprints are split into top and bottom halves](#footprints-are-split-into-top-and-bottom-halves) -- [Pokédex entry banks are derived from their species IDs](#pokédex-entry-banks-are-derived-from-their-species-ids) - [`ITEM_C3` and `ITEM_DC` break up the continuous sequence of TM items](#item_c3-and-item_dc-break-up-the-continuous-sequence-of-tm-items) +- [Pokédex entry banks are derived from their species IDs](#pokédex-entry-banks-are-derived-from-their-species-ids) +- [Identical sine wave code and data is repeated five times](#identical-sine-wave-code-and-data-is-repeated-five-times) - [`GetForestTreeFrame` works, but it's still bad](#getforesttreeframe-works-but-its-still-bad) @@ -24,10 +25,10 @@ dba_pic: MACRO ; dbw bank, address ENDM ``` -The offset is translated into a correct bank by `FixPicBank` in [engine/load_pics.asm](/engine/load_pics.asm): +The offset is translated into a correct bank by `FixPicBank` in [engine/gfx/load_pics.asm](/engine/gfx/load_pics.asm): ```asm -FixPicBank: ; 511c5 +FixPicBank: ; This is a thing for some reason. PICS_FIX EQU $36 @@ -35,7 +36,7 @@ GLOBAL PICS_FIX push hl push bc - sub BANK(Pics_1) - PICS_FIX + sub BANK("Pics 1") - PICS_FIX ld c, a ld b, 0 ld hl, .PicsBanks @@ -45,34 +46,34 @@ GLOBAL PICS_FIX pop hl ret -.PicsBanks: ; 511d4 - db BANK(Pics_1) + 0 - db BANK(Pics_1) + 1 - db BANK(Pics_1) + 2 - db BANK(Pics_1) + 3 - db BANK(Pics_1) + 4 - db BANK(Pics_1) + 5 - db BANK(Pics_1) + 6 - db BANK(Pics_1) + 7 - db BANK(Pics_1) + 8 - db BANK(Pics_1) + 9 - db BANK(Pics_1) + 10 - db BANK(Pics_1) + 11 - db BANK(Pics_1) + 12 - db BANK(Pics_1) + 13 - db BANK(Pics_1) + 14 - db BANK(Pics_1) + 15 - db BANK(Pics_1) + 16 - db BANK(Pics_1) + 17 - db BANK(Pics_1) + 18 - db BANK(Pics_1) + 19 - db BANK(Pics_1) + 20 - db BANK(Pics_1) + 21 - db BANK(Pics_1) + 22 - db BANK(Pics_1) + 23 +.PicsBanks: + db BANK("Pics 1") ; BANK("Pics 1") + 0 + db BANK("Pics 2") ; BANK("Pics 1") + 1 + db BANK("Pics 3") ; BANK("Pics 1") + 2 + db BANK("Pics 4") ; BANK("Pics 1") + 3 + db BANK("Pics 5") ; BANK("Pics 1") + 4 + db BANK("Pics 6") ; BANK("Pics 1") + 5 + db BANK("Pics 7") ; BANK("Pics 1") + 6 + db BANK("Pics 8") ; BANK("Pics 1") + 7 + db BANK("Pics 9") ; BANK("Pics 1") + 8 + db BANK("Pics 10") ; BANK("Pics 1") + 9 + db BANK("Pics 11") ; BANK("Pics 1") + 10 + db BANK("Pics 12") ; BANK("Pics 1") + 11 + db BANK("Pics 13") ; BANK("Pics 1") + 12 + db BANK("Pics 14") ; BANK("Pics 1") + 13 + db BANK("Pics 15") ; BANK("Pics 1") + 14 + db BANK("Pics 16") ; BANK("Pics 1") + 15 + db BANK("Pics 17") ; BANK("Pics 1") + 16 + db BANK("Pics 18") ; BANK("Pics 1") + 17 + db BANK("Pics 19") ; BANK("Pics 1") + 18 + db BANK("Pics 20") ; BANK("Pics 1") + 19 + db BANK("Pics 21") ; BANK("Pics 1") + 20 + db BANK("Pics 22") ; BANK("Pics 1") + 21 + db BANK("Pics 23") ; BANK("Pics 1") + 22 + db BANK("Pics 24") ; BANK("Pics 1") + 23 ``` -**Fix:** Use `dba` instead of `dba_pic`, and don't call `FixPicBank` to modify `a`. +**Fix:** Use `dba` instead of `dba_pic`, delete `FixPicBank`, and remove all four calls to `FixPicBank`. ## `PokemonPicPointers` and `UnownPicPointers` are assumed to start at the same address @@ -83,6 +84,7 @@ In [gfx/pics.asm](/gfx/pics.asm): ; PokemonPicPointers and UnownPicPointers are assumed to start at the same ; address, but in different banks. This is enforced in pokecrystal.link. + SECTION "Pic Pointers", ROMX INCLUDE "data/pokemon/pic_pointers.asm" @@ -106,18 +108,18 @@ ROMX $49 "Pics 2" ``` -Two routines in [engine/load_pics.asm](/engine/load_pics.asm) make this assumption; `GetFrontpicPointer`: +Two routines in [engine/gfx/load_pics.asm](/engine/gfx/load_pics.asm) make this assumption; `GetFrontpicPointer`: ```asm - ld a, [CurPartySpecies] + ld a, [wCurPartySpecies] cp UNOWN jr z, .unown - ld a, [CurPartySpecies] + ld a, [wCurPartySpecies] ld d, BANK(PokemonPicPointers) jr .ok .unown - ld a, [UnownLetter] + ld a, [wUnownLetter] ld d, BANK(UnownPicPointers) .ok @@ -130,9 +132,7 @@ Two routines in [engine/load_pics.asm](/engine/load_pics.asm) make this assumpti And `GetMonBackpic`: ```asm - ; These are assumed to be at the same - ; address in their respective banks. - GLOBAL PokemonPicPointers, UnownPicPointers + ; These are assumed to be at the same address in their respective banks. ld hl, PokemonPicPointers ; UnownPicPointers ld a, b ld d, BANK(PokemonPicPointers) @@ -152,21 +152,22 @@ Don't enforce `org $4000` in pokecrystal.link. Modify `GetFrontpicPointer`: -```asm - ld a, [CurPartySpecies] +```diff + ld a, [wCurPartySpecies] cp UNOWN jr z, .unown - ld a, [CurPartySpecies] - ld hl, PokemonPicPointers + ld a, [wCurPartySpecies] ++ ld hl, PokemonPicPointers ld d, BANK(PokemonPicPointers) jr .ok .unown - ld a, [UnownLetter] - ld hl, UnownPicPointers + ld a, [wUnownLetter] ++ ld hl, UnownPicPointers ld d, BANK(UnownPicPointers) .ok +- ld hl, PokemonPicPointers ; UnownPicPointers dec a ld bc, 6 call AddNTimes @@ -175,14 +176,15 @@ Modify `GetFrontpicPointer`: And `GetMonBackpic`: ```asm - GLOBAL PokemonPicPointers, UnownPicPointers +- ; These are assumed to be at the same address in their respective banks. +- ld hl, PokemonPicPointers ; UnownPicPointers ld a, b - ld hl, PokemonPicPointers ++ ld hl, PokemonPicPointers ld d, BANK(PokemonPicPointers) cp UNOWN jr nz, .ok ld a, c - ld hl, UnownPicPointers ++ ld hl, UnownPicPointers ld d, BANK(UnownPicPointers) .ok dec a @@ -234,7 +236,7 @@ INCBIN "gfx/footprints/wartortle.1bpp", footprint_bottom push hl ld e, l ld d, h - ld hl, VTiles2 tile $62 + ld hl, vTiles2 tile $62 lb bc, BANK(Footprints), 2 call Request1bpp pop hl @@ -246,7 +248,7 @@ INCBIN "gfx/footprints/wartortle.1bpp", footprint_bottom ld e, l ld d, h - ld hl, VTiles2 tile $64 + ld hl, vTiles2 tile $64 lb bc, BANK(Footprints), 2 call Request1bpp ``` @@ -269,12 +271,128 @@ INCBIN "gfx/footprints/wartortle.1bpp" Modify `Pokedex_LoadAnyFootprint`: -```asm +```diff +- push hl ld e, l ld d, h - ld hl, VTiles2 tile $62 - lb bc, BANK(Footprints), 4 + ld hl, vTiles2 tile $62 +- lb bc, BANK(Footprints), 2 ++ lb bc, BANK(Footprints), 4 call Request1bpp +- pop hl +- +- ; Whoever was editing footprints forgot to fix their +- ; tile editor. Now each bottom half is 8 tiles off. +- ld de, 8 tiles +- add hl, de +- +- ld e, l +- ld d, h +- ld hl, vTiles2 tile $64 +- lb bc, BANK(Footprints), 2 +- call Request1bpp +``` + + +## `ITEM_C3` and `ITEM_DC` break up the continuous sequence of TM items + +[constants/item_constants.asm](/constants/item_constants.asm) defined the 50 TMs in order with `add_tm`, but `ITEM_C3` and `ITEM_DC` break up that sequence. + +```asm + add_tm DYNAMICPUNCH ; bf + ... + add_tm ROLLOUT ; c2 + const ITEM_C3 ; c3 + add_tm ROAR ; c4 + ... + add_tm DIG ; db + const ITEM_DC ; dc + add_tm PSYCHIC_M ; dd + ... + add_tm NIGHTMARE ; f2 +NUM_TMS = const_value - TM01 - 2 ; discount ITEM_C3 and ITEM_DC +``` + +`GetTMHMNumber` and `GetNumberedTMHM` in [engine/items/items.asm](/engine/items/items.asm) have to compensate for this: + +```asm +GetTMHMNumber:: +; Return the number of a TM/HM by item id c. + ld a, c +; Skip any dummy items. + cp ITEM_C3 ; TM04-05 + jr c, .done + cp ITEM_DC ; TM28-29 + jr c, .skip + dec a +.skip + dec a +.done + sub TM01 + inc a + ld c, a + ret + +GetNumberedTMHM: +; Return the item id of a TM/HM by number c. + ld a, c +; Skip any gaps. + cp ITEM_C3 - (TM01 - 1) + jr c, .done + cp ITEM_DC - (TM01 - 1) - 1 + jr c, .skip_one +.skip_two + inc a +.skip_one + inc a +.done + add TM01 + dec a + ld c, a + ret +``` + +**Fix:** + +Move `ITEM_C3` and `ITEM_DC` above all the TMs in every table of item data. + +Modify engine/items/items.asm: + +```diff +GetTMHMNumber:: +; Return the number of a TM/HM by item id c. + ld a, c +-; Skip any dummy items. +- cp ITEM_C3 ; TM04-05 +- jr c, .done +- cp ITEM_DC ; TM28-29 +- jr c, .skip +- dec a +-.skip +- dec a +-.done + sub TM01 + inc a + ld c, a + ret + +GetNumberedTMHM: +; Return the item id of a TM/HM by number c. + ld a, c +-; Skip any gaps. +- cp ITEM_C3 - (TM01 - 1) +- jr c, .done +- cp ITEM_DC - (TM01 - 1) - 1 +- jr c, .skip_one +-.skip_two +- inc a +-.skip_one +- inc a +-.done + add TM01 + dec a + ld c, a + ret ``` @@ -285,7 +403,7 @@ Modify `Pokedex_LoadAnyFootprint`: Three separate routines do the same derivation; `GetDexEntryPointer` in [engine/pokedex/pokedex_2.asm](/engine/pokedex/pokedex_2.asm): ```asm -GetDexEntryPointer: ; 44333 +GetDexEntryPointer: ; return dex entry pointer b:de push hl ld hl, PokedexDataPointerTable @@ -301,7 +419,7 @@ GetDexEntryPointer: ; 44333 push de rlca rlca - and $3 + maskbits NUM_DEX_ENTRY_BANKS ld hl, .PokedexEntryBanks ld d, 0 ld e, a @@ -311,29 +429,23 @@ GetDexEntryPointer: ; 44333 pop hl ret -.PokedexEntryBanks: ; 44351 - -GLOBAL PokedexEntries1 -GLOBAL PokedexEntries2 -GLOBAL PokedexEntries3 -GLOBAL PokedexEntries4 - - db BANK(PokedexEntries1) - db BANK(PokedexEntries2) - db BANK(PokedexEntries3) - db BANK(PokedexEntries4) +.PokedexEntryBanks: + db BANK("Pokedex Entries 001-064") + db BANK("Pokedex Entries 065-128") + db BANK("Pokedex Entries 129-192") + db BANK("Pokedex Entries 193-251") ``` -`GetPokedexEntryBank` in [engine/item_effects.asm](/engine/item_effects.asm): +`GetPokedexEntryBank` in [engine/items/item_effects.asm](/engine/items/item_effects.asm): ```asm GetPokedexEntryBank: push hl push de - ld a, [EnemyMonSpecies] + ld a, [wEnemyMonSpecies] rlca rlca - and 3 + maskbits NUM_DEX_ENTRY_BANKS ld hl, .PokedexEntryBanks ld d, 0 ld e, a @@ -344,30 +456,24 @@ GetPokedexEntryBank: ret .PokedexEntryBanks: - -GLOBAL PokedexEntries1 -GLOBAL PokedexEntries2 -GLOBAL PokedexEntries3 -GLOBAL PokedexEntries4 - - db BANK(PokedexEntries1) - db BANK(PokedexEntries2) - db BANK(PokedexEntries3) - db BANK(PokedexEntries4) + db BANK("Pokedex Entries 001-064") + db BANK("Pokedex Entries 065-128") + db BANK("Pokedex Entries 129-192") + db BANK("Pokedex Entries 193-251") ``` -And `PokedexShow_GetDexEntryBank` in [engine/radio.asm](/engine/radio.asm): +And `PokedexShow_GetDexEntryBank` in [engine/pokegear/radio.asm](/engine/pokegear/radio.asm): ```asm PokedexShow_GetDexEntryBank: push hl push de - ld a, [CurPartySpecies] + ld a, [wCurPartySpecies] dec a rlca rlca - and 3 - ld hl, .pokedexbanks + maskbits NUM_DEX_ENTRY_BANKS + ld hl, .PokedexEntryBanks ld d, 0 ld e, a add hl, de @@ -376,105 +482,145 @@ PokedexShow_GetDexEntryBank: pop hl ret -.pokedexbanks - db BANK(PokedexEntries1) - db BANK(PokedexEntries2) - db BANK(PokedexEntries3) - db BANK(PokedexEntries4) +.PokedexEntryBanks: + db BANK("Pokedex Entries 001-064") + db BANK("Pokedex Entries 065-128") + db BANK("Pokedex Entries 129-192") + db BANK("Pokedex Entries 193-251") ``` **Fix:** Use `dba` instead of `dw` in `PokedexDataPointerTable`, and modify the code that accesses it to match. -## `ITEM_C3` and `ITEM_DC` break up the continuous sequence of TM items +## Identical sine wave code and data is repeated five times -[constants/item_constants.asm](/constants/item_constants.asm) defined the 50 TMs in order with `add_tm`, but `ITEM_C3` and `ITEM_DC` break up that sequence. +`_Sine` in [engine/math/sine.asm](/engine/math/sine.asm): ```asm - add_tm DYNAMICPUNCH ; $BF - ... - add_tm ROLLOUT ; $C2 - const ITEM_C3 ; $C3 - add_tm ROAR ; $C4 - ... - add_tm DIG ; $DB - const ITEM_DC ; $DC - add_tm PSYCHIC_M ; $DD - ... - add_tm NIGHTMARE ; $F2 -NUM_TMS = const_value - TM01 - 2 ; discount ITEM_C3 and ITEM_DC +_Sine:: +; a = d * sin(e * pi/32) + ld a, e + calc_sine_wave ``` -`GetTMHMNumber` and `GetNumberedTMHM` in [engine/items.asm](/engine/items.asm) have to compensate for this: +`Sprites_Cosine` and `Sprites_Sine` in [engine/gfx/sprites.asm](/engine/gfx/sprites.asm): ```asm -GetTMHMNumber:: ; d407 -; Return the number of a TM/HM by item id c. - ld a, c -; Skip any dummy items. - cp ITEM_C3 ; TM04-05 - jr c, .done - cp ITEM_DC ; TM28-29 - jr c, .skip - dec a -.skip - dec a -.done - sub TM01 - inc a - ld c, a - ret +Sprites_Cosine: +; a = d * cos(a * pi/32) + add %010000 ; cos(x) = sin(x + pi/2) + ; fallthrough +Sprites_Sine: +; a = d * sin(a * pi/32) + calc_sine_wave +``` -GetNumberedTMHM: ; d417 -; Return the item id of a TM/HM by number c. - ld a, c -; Skip any gaps. - cp ITEM_C3 - (TM01 - 1) - jr c, .done - cp ITEM_DC - (TM01 - 1) - 1 - jr c, .skip_one -.skip_two - inc a -.skip_one - inc a -.done - add TM01 - dec a - ld c, a - ret +`BattleAnim_Cosine` and `BattleAnim_Sine` in [engine/battle_anims/functions.asm](/engine/battle_anims/functions.asm): + +```asm +BattleAnim_Cosine: +; a = d * cos(a * pi/32) + add %010000 ; cos(x) = sin(x + pi/2) + ; fallthrough +BattleAnim_Sine: +; a = d * sin(a * pi/32) + calc_sine_wave BattleAnimSineWave + +... + +BattleAnimSineWave: + sine_table 32 ``` -**Fix:** +`StartTrainerBattle_DrawSineWave` in [engine/battle/battle_transition.asm](/engine/battle/battle_transition.asm): -Move `ITEM_C3` and `ITEM_DC` above all the TMs in every table of item data. +```asm +StartTrainerBattle_DrawSineWave: + calc_sine_wave +``` -Modify engine/items.asm: +And `CelebiEvent_Cosine` in [engine/events/celebi.asm](/engine/events/celebi.asm): ```asm -GetTMHMNumber:: ; d407 -; Return the number of a TM/HM by item id c. - ld a, c - sub TM01 +CelebiEvent_Cosine: +; a = d * cos(a * pi/32) + add %010000 ; cos(x) = sin(x + pi/2) + calc_sine_wave +``` + +They all rely on `calc_sine_wave` in [macros/code.asm](/macros/code.asm): + +```asm +calc_sine_wave: MACRO +; input: a = a signed 6-bit value +; output: a = d * sin(a * pi/32) + and %111111 + cp %100000 + jr nc, .negative\@ + call .apply\@ + ld a, h + ret +.negative\@ + and %011111 + call .apply\@ + ld a, h + xor $ff inc a - ld c, a ret - -GetNumberedTMHM: ; d417 -; Return the item id of a TM/HM by number c. - ld a, c - add TM01 - dec a - ld c, a +.apply\@ + ld e, a + ld a, d + ld d, 0 +if _NARG == 1 + ld hl, \1 +else + ld hl, .sinetable\@ +endc + add hl, de + add hl, de + ld e, [hl] + inc hl + ld d, [hl] + ld hl, 0 +.multiply\@ ; factor amplitude + srl a + jr nc, .even\@ + add hl, de +.even\@ + sla e + rl d + and a + jr nz, .multiply\@ ret +if _NARG == 0 +.sinetable\@ + sine_table 32 +endc +ENDM ``` +And on `sine_table` in [macros/data.asm](/macros/data.asm): + +```asm +sine_table: MACRO +; \1 samples of sin(x) from x=0 to x<32768 (pi radians) +x = 0 +rept \1 + dw (sin(x) + (sin(x) & $ff)) >> 8 ; round up +x = x + DIV(32768, \1) ; a circle has 65536 "degrees" +endr +ENDM +``` + +**Fix:** Edit [home/sine.asm](/home/sine.asm) to contain a single copy of the (co)sine code in bank 0, and call it from those five sites. + ## `GetForestTreeFrame` works, but it's still bad -In [engine/tileset_anims.asm](/engine/tileset_anims.asm): +In [engine/tilesets/tileset_anims.asm](/engine/tilesets/tileset_anims.asm): ```asm -GetForestTreeFrame: ; fc54c +GetForestTreeFrame: ; Return 0 if a is even, or 2 if odd. and a jr z, .even @@ -497,16 +643,14 @@ GetForestTreeFrame: ; fc54c .even xor a ret -; fc56d ``` **Fix:** ```asm -GetForestTreeFrame: ; fc54c +GetForestTreeFrame: ; Return 0 if a is even, or 2 if odd. and 1 add a ret -; fc56d ``` diff --git a/docs/event_commands.md b/docs/event_commands.md index 91486ad06..21c49dfee 100644 --- a/docs/event_commands.md +++ b/docs/event_commands.md @@ -1,6 +1,8 @@ # Event Commands -Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/scripting.asm:ScriptCommandTable](/engine/scripting.asm). +Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/overworld/scripting.asm:ScriptCommandTable](/engine/overworld/scripting.asm). + +Until this document is filled out, the [G/S Scripting Compendium](https://hax.iimarckus.org/files/scriptingcodes_eng.htm) has descriptions for most of these commands. It was written for G/S binary hacking and not Crystal assembly hacking, so it's not 100% accurate for pokecrystal. ## `$00`: <code>scall <i>script</i></code> @@ -15,17 +17,17 @@ Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/s ## `$05`: <code>ptjump <i>script</i></code> -## `$06`: <code>if_equal <i>byte</i>, <i>script</i></code> +## `$06`: <code>ifequal <i>byte</i>, <i>script</i></code> -## `$07`: <code>if_not_equal <i>byte</i>, <i>script</i></code> +## `$07`: <code>ifnotequal <i>byte</i>, <i>script</i></code> ## `$08`: <code>iffalse <i>script</i></code> ## `$09`: <code>iftrue <i>script</i></code> -## `$0A`: <code>if_greater_than <i>byte</i>, <i>script</i></code> +## `$0A`: <code>ifgreater <i>byte</i>, <i>script</i></code> -## `$0B`: <code>if_less_than <i>byte</i>, <i>script</i></code> +## `$0B`: <code>ifless <i>byte</i>, <i>script</i></code> ## `$0C`: <code>jumpstd <i>std_script</i></code> @@ -91,19 +93,15 @@ Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/s ## `$2B`: <code>checktime <i>time</i></code> -- **`checkmorn`:** `checktime MORN` -- **`checkday`:** `checktime DAY` -- **`checknite`:** `checktime NITE` - ## `$2C`: <code>checkpoke <i>mon_id</i></code> ## `$2D`: <code>givepoke <i>mon_id</i>, <i>level</i>[, <i>item</i>=0[, <i>trainer</i>=0, <i>ot_name</i>, <i>nickname</i>]]</code> ## `$2E`: <code>giveegg <i>mon_id</i>, <i>level</i></code> -## `$2F`: <code>givepokeitem <i>pointer</i></code> +## `$2F`: <code>givepokemail <i>pointer</i></code> -## `$30`: <code>checkpokeitem <i>pointer</i></code> +## `$30`: <code>checkpokemail <i>pointer</i></code> ## `$31`: <code>checkevent <i>event_flag</i></code> @@ -165,7 +163,7 @@ Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/s ## `$4E`: `yesorno` -## `$4F`: <code>loadmenudata <i>data_pointer</i></code> +## `$4F`: <code>loadmenu <i>menu_header</i></code> ## `$50`: `closewindow` @@ -211,9 +209,9 @@ Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/s ## `$65`: `scripttalkafter` -## `$66`: `end_if_just_battled` +## `$66`: `endifjustbattled` -## `$67`: `check_just_battled` +## `$67`: `checkjustbattled` ## `$68`: <code>setlasttalked <i>object_id</i></code> @@ -243,7 +241,7 @@ Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/s ## `$75`: <code>showemote <i>emote_id</i>, <i>object_id</i>, <i>length</i></code> -## `$76`: <code>spriteface <i>object_id</i>, <i>facing</i></code> +## `$76`: <code>turnobject <i>object_id</i>, <i>facing</i></code> ## `$77`: <code>follownotexact <i>object2</i>, <i>object1</i></code> @@ -301,7 +299,7 @@ Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/s ## `$92`: <code>reloadandreturn <i>which_method</i></code> -## `$93`: `end_all` +## `$93`: `endall` ## `$94`: <code>pokemart <i>dialog_id</i>, <i>mart_id</i></code> @@ -345,4 +343,4 @@ Defined in [macros/scripts/events.asm](/macros/scripts/events.asm) and [engine/s ## `$A8`: <code>wait <i>duration</i></code> -## `$A9`: `check_save` +## `$A9`: `checksave` diff --git a/docs/images/hp_exp_bar_border_fix.png b/docs/images/hp_exp_bar_border_fix.png Binary files differindex 26135576f..dd79d6636 100644 --- a/docs/images/hp_exp_bar_border_fix.png +++ b/docs/images/hp_exp_bar_border_fix.png diff --git a/docs/images/port_fix.png b/docs/images/port_fix.png Binary files differindex 3156b065d..45fa26338 100644 --- a/docs/images/port_fix.png +++ b/docs/images/port_fix.png diff --git a/docs/map_scripts.md b/docs/map_event_scripts.md index 2302257e7..a77f74503 100644 --- a/docs/map_scripts.md +++ b/docs/map_event_scripts.md @@ -1,30 +1,57 @@ -# Map Scripts +# Map Event Scripts -## <code>const_value set 2</code> +## Contents + +- [Object constants](#object-constants) +- [Map scripts](#map-scripts) + - [Scene scripts](#scene-scripts) + - [Callbacks](#callbacks) + - [Callback types](#callback-types) +- [Event scripts](#event-scripts) +- [Text](#text) +- [Movement data](#movement-data) +- [Map events](#map-events) + - [Warp events](#warp-events) + - [Coord events](#coord-events) + - [BG events](#bg-events) + - [BG event types](#bg-event-types) + - [Object events](#object-events) + - [Movement types](#movement-types) + - [Object types](#object-types) + + +## Object constants <pre> + const_def 2 ; object constants const <i>MAPNAME</i>_<i>OBJECTNAME</i> </pre> -## <code>MapName_MapScripts:</code> +## Map scripts +<pre> +<i>MapName</i>_MapScripts: +</pre> -### <code>.SceneScripts: db <i>N</i></code> + +### Scene scripts <pre> + db <i>N</i> ; scene scripts scene_script <i>script</i> </pre> -### <code>.MapCallbacks: db <i>N</i></code> +### Callbacks <pre> + db <i>N</i> ; callbacks callback <i>type</i>, <i>script</i> </pre> -Callback types: +#### Callback types - `MAPCALLBACK_NEWMAP` @@ -67,35 +94,38 @@ Callback types: [Movement commands](movement_commands.md) -## <code>MapName_MapEvents:</code> +## Map events -```asm - ; filler - db 0, 0 -``` +<pre> +<i>MapName</i>_MapEvents: + db 0, 0 ; filler +</pre> -### <code>.Warps: db <i>N</i></code> +### Warp events <pre> - warp_def <i>x</i>, <i>y</i>, <i>warp_id</i>, <i>map</i> + db <i>N</i> ; warp events + warp_event <i>x</i>, <i>y</i>, <i>map</i>, <i>warp_id</i> </pre> -### <code>.CoordEvents: db <i>N</i></code> +### Coord events <pre> + db <i>N</i> ; coord events coord_event <i>x</i>, <i>y</i>, <i>scene_id</i>, <i>script</i> </pre> -### <code>.BGEvents: db <i>N</i></code> +### BG events <pre> + db <i>N</i> ; bg events bg_event <i>x</i>, <i>y</i>, <i>type</i>, <i>script</i> </pre> -BG event types: +#### BG event types - `BGEVENT_READ` @@ -110,20 +140,21 @@ BG event types: - `BGEVENT_ITEM` <pre> - hiddenitem <i>event_flag</i>, <i>item_id</i> + hiddenitem <i>item_id</i>, <i>event_flag</i> </pre> - `BGEVENT_COPY` -### <code>.ObjectEvents: db <i>N</i></code> +### Object events <pre> + db <i>N</i> ; object events object_event <i>x</i>, <i>y</i>, <i>sprite</i>, <i>movement</i>, <i>rx</i>, <i>ry</i>, <i>h1</i>, <i>h2</i>, <i>palette</i>, <i>type</i>, <i>range</i>, <i>script</i>, <i>event_flag</i> </pre> -Movement types: +#### Movement types -- `SPRITEMOVEDATA_ITEM_TREE` +- `SPRITEMOVEDATA_STILL` - `SPRITEMOVEDATA_WANDER` @@ -137,7 +168,7 @@ Movement types: - `SPRITEMOVEDATA_SPINRANDOM_FAST` -- `SPRITEMOVEDATA_SNORLAX` +- `SPRITEMOVEDATA_BIGDOLLSYM` - `SPRITEMOVEDATA_POKEMON` @@ -151,22 +182,24 @@ Movement types: - `SPRITEMOVEDATA_SPINCLOCKWISE` +- `SPRITEMOVEDATA_BIGDOLLASYM` + - `SPRITEMOVEDATA_BIGDOLL` -- `SPRITEMOVEDATA_LAPRAS` +- `SPRITEMOVEDATA_SWIM_WANDER` -Object types: +#### Object types - `OBJECTTYPE_SCRIPT` - `OBJECTTYPE_ITEMBALL` <pre> - itemball <i>item_id</i> + itemball <i>item_id</i>[, <i>quantity</i>=1] </pre> - `OBJECTTYPE_TRAINER` <pre> - trainer <i>event_flag</i>, <i>group_id</i>, <i>trainer_id</i>, <i>seen_text</i>, <i>beaten_text</i>, <i>loss_text</i>, <i>script</i> + trainer <i>group_id</i>, <i>trainer_id</i>, <i>event_flag</i>, <i>seen_text</i>, <i>beaten_text</i>, <i>loss_text</i>, <i>script</i> </pre> diff --git a/docs/menu.md b/docs/menu.md index 71186a905..67403bf15 100644 --- a/docs/menu.md +++ b/docs/menu.md @@ -19,13 +19,13 @@ This is the only menu that does scrolling. It doesn't draw any `TextBox` around Structure: ```asm -.MenuDataHeader: +.MenuHeader: db MENU_BACKUP_TILES ; flags menu_coords 2, 4, SCREEN_WIDTH - 1, 13 - dw .MenuData2 + dw .MenuData db 1 ; default option -.MenuData2: +.MenuData: db 0 ; flags db 5, 0 ; rows, columns db 1 ; horizontal spacing @@ -35,7 +35,7 @@ Structure: dba Function3 ``` -`wMenuData2Flags`: +`wMenuDataFlags`: ``` 7: Select is functional @@ -48,14 +48,14 @@ Structure: 0: Call Function1 to display the cancel entry ``` -If the columns entry in `MenuDataHeader2` of a scrolling menu is 0, `Function2` isn't called either. It doesn't affect the position of the arrows. +If the columns entry in `MenuData` of a scrolling menu is 0, `Function2` isn't called either. It doesn't affect the position of the arrows. -Call state for functions in `MenuDataHeader2` of `ScrollingMenu`: +Call state for functions in `MenuData` of `ScrollingMenu`: ``` All of them: -[MenuSelection] = Current item. -1 is the CANCEL item. -[MenuSelectionQuantity] = Quantity of the current item. +[wMenuSelection] = Current item. -1 is the CANCEL item. +[wMenuSelectionQuantity] = Quantity of the current item. Function1: Called to display a menu entry. de = Cursor position in TileMap @@ -68,7 +68,7 @@ Function3: Called to display anything else, whenever the cursor is moved. There is no register of importance that should be preserved in any of these functions. -The `; horizontal spacing` item in each `MenuData2` is a misnomer. It changes how the `Items` struct looks. +The `; horizontal spacing` item in each `MenuData` is a misnomer. It changes how the `Items` struct looks. If it's 1: @@ -90,7 +90,7 @@ db -1 ; cancel ... ``` -In case it's 1, `[MenuSelectionQuantity]` will simply contain the next entry. +In case it's 1, `[wMenuSelectionQuantity]` will simply contain the next entry. ## `_2DMenu` @@ -99,14 +99,14 @@ This, like is implied by the name, is a 2-dimensional menu, where you can move y Structure: ```asm -.MenuDataHeader: +.MenuHeader: db MENU_BACKUP_TILES ; flags db 12, 08 ; start coords db 17, 19 ; end coords - dw .MenuData2 + dw .MenuData db 1 ; default option -.MenuData2: +.MenuData: db STATICMENU_CURSOR ; flags dn 2, 2 ; rows, columns db 6 ; spacing @@ -114,7 +114,7 @@ Structure: dba Function ``` -`wMenuData2Flags`: +`wMenuDataFlags`: ``` 7: Leave one tile of spacing between the left textbox border and the text, enabling the cursor. @@ -141,13 +141,13 @@ These are like the regular `VerticalMenu`, except they allow for creating slight Structure: ``` -.MenuDataHeader: +.MenuHeader: db MENU_BACKUP_TILES ; flags menu_coords 0, 0, 10, 7 - dw .MenuData2 + dw .MenuData db 1 ; default option -.MenuData2: +.MenuData: db STATICMENU_CURSOR | STATICMENU_DISABLE_B ; flags db 0 ; items dw Items @@ -155,7 +155,7 @@ Structure: dw StringPointers ``` -`wMenuData2Flags`: +`wMenuDataFlags`: ``` 7: Unused @@ -208,7 +208,7 @@ StringPointers: Call state for `DisplayFunction`: ``` -[MenuSelection] = Current item. -1 is the CANCEL item. +[wMenuSelection] = Current item. -1 is the CANCEL item. de = Cursor position in TileMap ``` @@ -219,20 +219,20 @@ This is the simplest menu. Like, the most boring. Nothing special. Just normal. Structure: ```asm -.MenuDataHeader: +.MenuHeader: db MENU_SPRITE_ANIMS | MENU_BACKUP_TILES ; flags menu_coords 12, 12, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1 - dw .MenuData2 + dw .MenuData db 1 ; default option -.MenuData2: +.MenuData: db STATICMENU_CURSOR ; flags db 2 ; # items db "GIVE@" db "TAKE@" ``` -`wMenuData2Flags`: +`wMenuDataFlags`: ``` 7: Leave one tile of spacing between the left textbox border and the text @@ -259,7 +259,7 @@ This is used in the menu for selecting the character's name. ## Misc/Generic -`MenuDataHeader` flags (`wMenuFlags`): +`MenuHeader` flags (`wMenuFlags`): ``` 7: Save a backup of the tiles diff --git a/docs/movement_commands.md b/docs/movement_commands.md index 10792c88b..794dd8a98 100644 --- a/docs/movement_commands.md +++ b/docs/movement_commands.md @@ -1,6 +1,6 @@ # Movement Commands -Defined in [macros/scripts/movement.asm](/macros/scripts/movement.asm) and [engine/movement.asm:MovementPointers](/engine/movement.asm). +Defined in [macros/scripts/movement.asm](/macros/scripts/movement.asm) and [engine/overworld/movement.asm:MovementPointers](/engine/overworld/movement.asm). ## `$00`−`$03`: <code>turn_head <i>direction</i></code> diff --git a/docs/pic_animations.md b/docs/pic_animations.md index 1075eb25b..c1a0db934 100644 --- a/docs/pic_animations.md +++ b/docs/pic_animations.md @@ -22,9 +22,9 @@ Animation data is in these files: - [gfx/pokemon/anims.asm](/gfx/pokemon/anims.asm): Main animations (played everywhere) -- [gfx/pokemon/extras.asm](/gfx/pokemon/extras.asm): - Extra animations, appended to the main animation. +- [gfx/pokemon/idles.asm](/gfx/pokemon/idles.asm): + Idle animations, appended to the main animation. Used in the status screen (blinking, tail wags etc.) -- [gfx/pokemon/unown_anims.asm](/gfx/pokemon/unown_anims.asm) and [gfx/pokemon/unown_extras.asm](/gfx/pokemon/unown_extras.asm): +- [gfx/pokemon/unown_anims.asm](/gfx/pokemon/unown_anims.asm) and [gfx/pokemon/unown_idles.asm](/gfx/pokemon/unown_idles.asm): Unown has its own animation data despite having an entry in the main tables. |