summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/battle_anim_commands.md2
-rw-r--r--docs/bugs_and_glitches.md744
-rw-r--r--docs/design_flaws.md452
-rw-r--r--docs/event_commands.md32
-rw-r--r--docs/images/hp_exp_bar_border_fix.pngbin187 -> 166 bytes
-rw-r--r--docs/images/port_fix.pngbin1547 -> 1526 bytes
-rw-r--r--docs/map_event_scripts.md (renamed from docs/map_scripts.md)83
-rw-r--r--docs/menu.md48
-rw-r--r--docs/movement_commands.md2
-rw-r--r--docs/pic_animations.md6
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
index 26135576f..dd79d6636 100644
--- a/docs/images/hp_exp_bar_border_fix.png
+++ b/docs/images/hp_exp_bar_border_fix.png
Binary files differ
diff --git a/docs/images/port_fix.png b/docs/images/port_fix.png
index 3156b065d..45fa26338 100644
--- a/docs/images/port_fix.png
+++ b/docs/images/port_fix.png
Binary files differ
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.