2017-12-12 17:50:34 -08:00
|
|
|
|
# Bugs and Glitches
|
2017-12-12 17:15:07 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Belly Drum sharply boosts Attack even with under 50% HP
|
|
|
|
|
|
|
|
|
|
([Video](https://www.youtube.com/watch?v=zuCLMikWo4Y))
|
|
|
|
|
|
|
|
|
|
This is a bug with `BattleCommand_BellyDrum` in [battle/effect_commands.asm](battle/effect_commands.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
BattleCommand_BellyDrum: ; 37c1a
|
|
|
|
|
; 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]
|
|
|
|
|
and a
|
|
|
|
|
jr nz, .failed
|
|
|
|
|
|
|
|
|
|
callab GetHalfMaxHP
|
|
|
|
|
callab CheckUserHasEnoughHP
|
|
|
|
|
jr nc, .failed
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
BattleCommand_BellyDrum: ; 37c1a
|
|
|
|
|
; bellydrum
|
|
|
|
|
callab GetHalfMaxHP
|
|
|
|
|
callab CheckUserHasEnoughHP
|
|
|
|
|
jr nc, .failed
|
|
|
|
|
|
|
|
|
|
call BattleCommand_AttackUp2
|
|
|
|
|
ld a, [AttackMissed]
|
|
|
|
|
and a
|
|
|
|
|
jr nz, .failed
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## HP bar animation is slower with more HP
|
|
|
|
|
|
|
|
|
|
([Video](https://www.youtube.com/watch?v=SE-BfsFgZVM))
|
|
|
|
|
|
|
|
|
|
This is a bug with `LongAnim_UpdateVariables` in [engine/anim_hp_bar.asm](engine/anim_hp_bar.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
; This routine is buggy. The result from ComputeHPBarPixels is stored
|
|
|
|
|
; in e. However, the pop de opcode deletes this result before it is even
|
|
|
|
|
; used. The game then proceeds as though it never deleted that output.
|
|
|
|
|
; To fix, uncomment the line below.
|
|
|
|
|
call ComputeHPBarPixels
|
|
|
|
|
; ld a, e
|
|
|
|
|
pop bc
|
|
|
|
|
pop de
|
|
|
|
|
pop hl
|
|
|
|
|
ld a, e ; Comment or delete this line to fix the above bug.
|
|
|
|
|
ld hl, wCurHPBarPixels
|
|
|
|
|
cp [hl]
|
|
|
|
|
jr z, .loop
|
|
|
|
|
ld [hl], a
|
|
|
|
|
and a
|
|
|
|
|
ret
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Move `ld a, e` to right after `call ComputeHPBarPixels`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Experience underflow for level 1 Pokémon with Medium-Slow growth rate
|
|
|
|
|
|
|
|
|
|
([Video](https://www.youtube.com/watch?v=SXH8u0plHrE))
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
CalcExpAtLevel: ; 50e47
|
|
|
|
|
; (a/b)*n**3 + c*n**2 + d*n - e
|
|
|
|
|
ld a, [BaseGrowthRate]
|
|
|
|
|
add a
|
|
|
|
|
add a
|
|
|
|
|
ld c, a
|
|
|
|
|
ld b, 0
|
|
|
|
|
ld hl, GrowthRates
|
|
|
|
|
add hl, bc
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
CalcExpAtLevel: ; 50e47
|
|
|
|
|
; (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]
|
|
|
|
|
add a
|
|
|
|
|
add a
|
|
|
|
|
ld c, a
|
|
|
|
|
ld b, 0
|
|
|
|
|
ld hl, GrowthRates
|
|
|
|
|
add hl, bc
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Five-digit experience gain is printed incorrectly
|
|
|
|
|
|
|
|
|
|
([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):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Text_ABoostedStringBuffer2ExpPoints::
|
|
|
|
|
text ""
|
|
|
|
|
line "a boosted"
|
|
|
|
|
cont "@"
|
|
|
|
|
deciram StringBuffer2, 2, 4
|
|
|
|
|
text " EXP. Points!"
|
|
|
|
|
prompt
|
|
|
|
|
|
|
|
|
|
Text_StringBuffer2ExpPoints::
|
|
|
|
|
text ""
|
|
|
|
|
line "@"
|
|
|
|
|
deciram StringBuffer2, 2, 4
|
|
|
|
|
text " EXP. Points!"
|
|
|
|
|
prompt
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Change `deciram StringBuffer2, 2, 4` to `deciram StringBuffer2, 2, 5`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Present damage is incorrect in link battles
|
|
|
|
|
|
|
|
|
|
([Video](https://www.youtube.com/watch?v=XJaQoKtrEuw))
|
|
|
|
|
|
|
|
|
|
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 [battle/effects/present.asm](battle/effects/present.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
BattleCommand_Present: ; 37874
|
|
|
|
|
; present
|
|
|
|
|
|
|
|
|
|
ld a, [wLinkMode]
|
|
|
|
|
cp LINK_COLOSSEUM
|
|
|
|
|
jr z, .colosseum_skippush
|
|
|
|
|
push bc
|
|
|
|
|
push de
|
|
|
|
|
.colosseum_skippush
|
|
|
|
|
|
|
|
|
|
call BattleCommand_Stab
|
|
|
|
|
|
|
|
|
|
ld a, [wLinkMode]
|
|
|
|
|
cp LINK_COLOSSEUM
|
|
|
|
|
jr z, .colosseum_skippop
|
|
|
|
|
pop de
|
|
|
|
|
pop bc
|
|
|
|
|
.colosseum_skippop
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
BattleCommand_Present: ; 37874
|
|
|
|
|
; present
|
|
|
|
|
|
|
|
|
|
push bc
|
|
|
|
|
push de
|
|
|
|
|
call BattleCommand_Stab
|
|
|
|
|
pop de
|
|
|
|
|
pop bc
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## BRN/PSN/PAR do not affect catch rate
|
|
|
|
|
|
|
|
|
|
This is a bug with `PokeBall` in [items/item_effects.asm](items/item_effects.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]
|
|
|
|
|
and 1 << FRZ | SLP
|
|
|
|
|
ld c, 10
|
|
|
|
|
jr nz, .addstatus
|
|
|
|
|
; ld a, [EnemyMonStatus]
|
|
|
|
|
and a
|
|
|
|
|
ld c, 5
|
|
|
|
|
jr nz, .addstatus
|
|
|
|
|
ld c, 0
|
|
|
|
|
.addstatus
|
|
|
|
|
ld a, b
|
|
|
|
|
add c
|
|
|
|
|
jr nc, .max_1
|
|
|
|
|
ld a, $ff
|
|
|
|
|
.max_1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Uncomment `ld a, [EnemyMonStatus]`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Moon Ball does not boost catch rate
|
|
|
|
|
|
|
|
|
|
This is a bug with `MoonBallMultiplier` in [items/item_effects.asm](items/item_effects.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
MoonBallMultiplier:
|
|
|
|
|
; This function is buggy.
|
|
|
|
|
; Intent: multiply catch rate by 4 if mon evolves with moon stone
|
|
|
|
|
; Reality: no boost
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
; Moon Stone's constant from Pokémon Red is used.
|
|
|
|
|
; No Pokémon evolve with Burn Heal,
|
|
|
|
|
; so Moon Balls always have a catch rate of 1×.
|
|
|
|
|
push bc
|
|
|
|
|
ld a, BANK(EvosAttacks)
|
|
|
|
|
call GetFarByte
|
|
|
|
|
cp MOON_STONE_RED ; BURN_HEAL
|
|
|
|
|
pop bc
|
|
|
|
|
ret nz
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Change `MOON_STONE_RED` to `MOON_STONE`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Love Ball boosts catch rate for the wrong gender
|
|
|
|
|
|
|
|
|
|
This is a bug with `LoveBallMultiplier` in [items/item_effects.asm](items/item_effects.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
LoveBallMultiplier:
|
|
|
|
|
; This function is buggy.
|
|
|
|
|
; Intent: multiply catch rate by 8 if mons are of same species, different sex
|
|
|
|
|
; Reality: multiply catch rate by 8 if mons are of same species, same sex
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
ld a, d
|
|
|
|
|
pop de
|
|
|
|
|
cp d
|
|
|
|
|
pop bc
|
|
|
|
|
ret nz ; for the intended effect, this should be "ret z"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Change `ret nz` to `ret z`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Fast Ball only boosts catch rate for three Pokémon
|
|
|
|
|
|
|
|
|
|
This is a bug with `FastBallMultiplier` in [items/item_effects.asm](items/item_effects.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
FastBallMultiplier:
|
|
|
|
|
; This function is buggy.
|
|
|
|
|
; Intent: multiply catch rate by 4 if enemy mon is in one of the three
|
|
|
|
|
; FleeMons tables.
|
|
|
|
|
; Reality: multiply catch rate by 4 if enemy mon is one of the first three in
|
|
|
|
|
; the first FleeMons table.
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
inc hl
|
|
|
|
|
cp -1
|
|
|
|
|
jr z, .next
|
|
|
|
|
cp c
|
|
|
|
|
jr nz, .next ; for the intended effect, this should be "jr nz, .loop"
|
|
|
|
|
sla b
|
|
|
|
|
jr c, .max
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Change `jr nz, .next` to `jr nz, .loop`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Friend Ball catches sent to the PC overwrite the wrong Pokémon's happiness
|
|
|
|
|
|
|
|
|
|
This is a bug with `PokeBall` in [items/item_effects.asm](items/item_effects.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
ld a, [CurItem]
|
|
|
|
|
cp FRIEND_BALL
|
|
|
|
|
jr nz, .SkipBoxMonFriendBall
|
|
|
|
|
; Bug: overwrites the happiness of the first mon in the box!
|
|
|
|
|
ld a, FRIEND_BALL_HAPPINESS
|
|
|
|
|
ld [sBoxMon1Happiness], a
|
|
|
|
|
.SkipBoxMonFriendBall:
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`sBoxMon1Happiness` is written *before* the Friend Ball Pokémon is deposited.
|
|
|
|
|
|
|
|
|
|
|
2017-12-12 18:29:53 -08:00
|
|
|
|
## Dragon Scale, not Dragon Fang, boosts Dragon-type moves
|
2017-12-12 17:15:07 -08:00
|
|
|
|
|
|
|
|
|
This is a bug with `ItemAttributes` in [items/item_attributes.asm](items/item_attributes.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
; DRAGON FANG
|
|
|
|
|
item_attribute 100, 0, 0, CANT_SELECT, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
; 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`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Daisy's massages don't always increase happiness
|
|
|
|
|
|
|
|
|
|
This is a bug with `MassageOrHaircut` in [event/special.asm](event/special.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
|
|
|
|
|
; 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
|
|
|
|
|
; Pokemon's happiness will not change at all.
|
|
|
|
|
.loop
|
|
|
|
|
sub [hl]
|
|
|
|
|
jr c, .ok
|
|
|
|
|
inc hl
|
|
|
|
|
inc hl
|
|
|
|
|
inc hl
|
|
|
|
|
jr .loop
|
|
|
|
|
|
|
|
|
|
.ok
|
|
|
|
|
inc hl
|
|
|
|
|
ld a, [hli]
|
|
|
|
|
ld [ScriptVar], a
|
|
|
|
|
ld c, [hl]
|
|
|
|
|
call ChangeHappiness
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
Data_DaisyMassage: ; 746b
|
|
|
|
|
db $ff, 2, HAPPINESS_MASSAGE ; 99.6% chance
|
|
|
|
|
|
|
|
|
|
CopyPokemonName_Buffer1_Buffer3: ; 746e
|
|
|
|
|
ld hl, StringBuffer1
|
|
|
|
|
ld de, StringBuffer3
|
|
|
|
|
ld bc, PKMN_NAME_LENGTH
|
|
|
|
|
jp CopyBytes
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Data_DaisyMassage: ; 746b
|
|
|
|
|
db $80, 2, HAPPINESS_MASSAGE ; 50% chance
|
|
|
|
|
db $ff, 2, HAPPINESS_MASSAGE ; 50% chance
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## No bump noise if standing on tile `$3E`
|
|
|
|
|
|
|
|
|
|
This is a bug with `DoPlayerMovement.CheckWarp` in [engine/player_movement.asm](engine/player_movement.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
; Bug: Since no case is made for STANDING here, it will check
|
|
|
|
|
; [.edgewarps + $ff]. This resolves to $3e at $8035a.
|
|
|
|
|
; This causes wd041 to be nonzero when standing on tile $3e,
|
|
|
|
|
; making bumps silent.
|
|
|
|
|
|
|
|
|
|
ld a, [WalkingDirection]
|
|
|
|
|
ld e, a
|
|
|
|
|
ld d, 0
|
|
|
|
|
ld hl, .EdgeWarps
|
|
|
|
|
add hl, de
|
|
|
|
|
ld a, [PlayerStandingTile]
|
|
|
|
|
cp [hl]
|
|
|
|
|
jr nz, .not_warp
|
|
|
|
|
|
|
|
|
|
ld a, 1
|
|
|
|
|
ld [wd041], a
|
|
|
|
|
ld a, [WalkingDirection]
|
|
|
|
|
cp STANDING
|
|
|
|
|
jr z, .not_warp
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
ld a, [WalkingDirection]
|
|
|
|
|
cp STANDING
|
|
|
|
|
jr z, .not_warp
|
|
|
|
|
ld e, a
|
|
|
|
|
ld d, 0
|
|
|
|
|
ld hl, .EdgeWarps
|
|
|
|
|
add hl, de
|
|
|
|
|
ld a, [PlayerStandingTile]
|
|
|
|
|
cp [hl]
|
|
|
|
|
jr nz, .not_warp
|
|
|
|
|
|
|
|
|
|
ld a, 1
|
|
|
|
|
ld [wd041], a
|
|
|
|
|
ld a, [WalkingDirection]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `CheckOwnMon` only checks the first five letters of OT names
|
|
|
|
|
|
|
|
|
|
([Video](https://www.youtube.com/watch?v=GVTTmReM4nQ))
|
|
|
|
|
|
|
|
|
|
This bug can allow you to talk to Eusine in Celadon City and encounter Ho-Oh with only traded legendary beasts.
|
|
|
|
|
|
|
|
|
|
[engine/search.asm](engine/search.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
|
|
|
|
|
|
2017-12-12 17:21:08 -08:00
|
|
|
|
rept NAME_LENGTH_JAPANESE +- 2 ; should be PLAYER_NAME_LENGTH +- 2
|
2017-12-12 17:15:07 -08:00
|
|
|
|
ld a, [de]
|
|
|
|
|
cp [hl]
|
|
|
|
|
jr nz, .notfound
|
|
|
|
|
cp "@"
|
|
|
|
|
jr z, .found ; reached end of string
|
|
|
|
|
inc hl
|
|
|
|
|
inc de
|
|
|
|
|
endr
|
|
|
|
|
|
|
|
|
|
ld a, [de]
|
|
|
|
|
cp [hl]
|
|
|
|
|
jr z, .found
|
|
|
|
|
|
|
|
|
|
.notfound
|
|
|
|
|
pop de
|
|
|
|
|
pop hl
|
|
|
|
|
pop bc
|
|
|
|
|
and a
|
|
|
|
|
ret
|
|
|
|
|
```
|
|
|
|
|
|
2017-12-12 17:21:08 -08:00
|
|
|
|
**Fix:** Change `rept NAME_LENGTH_JAPANESE +- 2` to `rept PLAYER_NAME_LENGTH +- 2`.
|
2017-12-12 17:15:07 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `HELD_CATCH_CHANCE` has no effect
|
|
|
|
|
|
|
|
|
|
This is a bug with `PokeBall` in [items/item_effects.asm](items/item_effects.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
; BUG: callba 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 a, b
|
|
|
|
|
cp HELD_CATCH_CHANCE
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Uncomment `ld b, a`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `ScriptCall` can overflow `wScriptStack` and crash
|
|
|
|
|
|
|
|
|
|
[engine/scripting.asm](engine/scripting.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
|
|
|
|
|
; space.
|
|
|
|
|
|
|
|
|
|
push de
|
|
|
|
|
ld hl, wScriptStackSize
|
|
|
|
|
ld e, [hl]
|
|
|
|
|
inc [hl]
|
|
|
|
|
ld d, $0
|
|
|
|
|
ld hl, wScriptStack
|
|
|
|
|
add hl, de
|
|
|
|
|
add hl, de
|
|
|
|
|
add hl, de
|
|
|
|
|
pop de
|
|
|
|
|
ld a, [ScriptBank]
|
|
|
|
|
ld [hli], a
|
|
|
|
|
ld a, [ScriptPos]
|
|
|
|
|
ld [hli], a
|
|
|
|
|
ld a, [ScriptPos + 1]
|
|
|
|
|
ld [hl], a
|
|
|
|
|
ld a, b
|
|
|
|
|
ld [ScriptBank], a
|
|
|
|
|
ld a, e
|
|
|
|
|
ld [ScriptPos], a
|
|
|
|
|
ld a, d
|
|
|
|
|
ld [ScriptPos + 1], a
|
|
|
|
|
ret
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `LoadSpriteGFX` does not limit the capacity of `UsedSprites`
|
|
|
|
|
|
|
|
|
|
[engine/overworld.asm](engine/overworld.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
LoadSpriteGFX: ; 14306
|
|
|
|
|
; Bug: b is not preserved, so
|
|
|
|
|
; it's useless as a next count.
|
|
|
|
|
|
|
|
|
|
ld hl, UsedSprites
|
|
|
|
|
ld b, SPRITE_GFX_LIST_CAPACITY
|
|
|
|
|
.loop
|
|
|
|
|
ld a, [hli]
|
|
|
|
|
and a
|
|
|
|
|
jr z, .done
|
|
|
|
|
push hl
|
|
|
|
|
call .LoadSprite
|
|
|
|
|
pop hl
|
|
|
|
|
ld [hli], a
|
|
|
|
|
dec b
|
|
|
|
|
jr nz, .loop
|
|
|
|
|
|
|
|
|
|
.done
|
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
.LoadSprite:
|
|
|
|
|
call GetSprite
|
|
|
|
|
ld a, l
|
|
|
|
|
ret
|
|
|
|
|
; 1431e
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`GetSprite` modifies `b`. Surround it with `push bc`/`pop bc` to fix.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `ChooseWildEncounter` doesn't really validate the wild Pokémon species
|
|
|
|
|
|
|
|
|
|
[engine/wildmons.asm](engine/wildmons.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
ChooseWildEncounter: ; 2a14f
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
ld a, b
|
|
|
|
|
ld [CurPartyLevel], a
|
|
|
|
|
ld b, [hl]
|
|
|
|
|
; ld a, b
|
|
|
|
|
call ValidateTempWildMonSpecies
|
|
|
|
|
jr c, .nowildbattle
|
|
|
|
|
|
|
|
|
|
ld a, b ; This is in the wrong place.
|
|
|
|
|
cp UNOWN
|
|
|
|
|
jr nz, .done
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
ValidateTempWildMonSpecies: ; 2a4a0
|
|
|
|
|
; Due to a development oversight, this function is called with the wild Pokemon's level, not its species, in a.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
ld a, b
|
|
|
|
|
ld [CurPartyLevel], a
|
|
|
|
|
ld b, [hl]
|
|
|
|
|
ld a, b
|
|
|
|
|
call ValidateTempWildMonSpecies
|
|
|
|
|
jr c, .nowildbattle
|
|
|
|
|
|
|
|
|
|
cp UNOWN
|
|
|
|
|
jr nz, .done
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## `TryObjectEvent` arbitrary code execution
|
|
|
|
|
|
|
|
|
|
[engine/events.asm](engine/events.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
; Bug: If IsInArray returns nc, data at bc will be executed as code.
|
|
|
|
|
push bc
|
|
|
|
|
ld de, 3
|
|
|
|
|
ld hl, .pointers
|
|
|
|
|
call IsInArray
|
|
|
|
|
jr nc, .nope_bugged
|
|
|
|
|
pop bc
|
|
|
|
|
|
|
|
|
|
inc hl
|
|
|
|
|
ld a, [hli]
|
|
|
|
|
ld h, [hl]
|
|
|
|
|
ld l, a
|
|
|
|
|
jp hl
|
|
|
|
|
|
|
|
|
|
.nope_bugged
|
|
|
|
|
; pop bc
|
|
|
|
|
xor a
|
|
|
|
|
ret
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Uncomment `pop bc`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `Special_CheckBugContestContestantFlag` can read beyond its data table
|
|
|
|
|
|
|
|
|
|
[event/bug_contest_2.asm](event/bug_contest_2.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Special_CheckBugContestContestantFlag: ; 139ed
|
|
|
|
|
; 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.
|
|
|
|
|
|
|
|
|
|
ld hl, BugCatchingContestantEventFlagTable
|
|
|
|
|
ld e, a
|
|
|
|
|
ld d, 0
|
|
|
|
|
add hl, de
|
|
|
|
|
add hl, de
|
|
|
|
|
ld e, [hl]
|
|
|
|
|
inc hl
|
|
|
|
|
ld d, [hl]
|
|
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `ClearWRAM` only clears WRAM bank 1
|
|
|
|
|
|
|
|
|
|
[home/init.asm](home/init.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
ClearWRAM:: ; 25a
|
|
|
|
|
; Wipe swappable WRAM banks (1-7)
|
|
|
|
|
; Assumes CGB or AGB
|
|
|
|
|
|
|
|
|
|
ld a, 1
|
|
|
|
|
.bank_loop
|
|
|
|
|
push af
|
|
|
|
|
ld [rSVBK], a
|
|
|
|
|
xor a
|
|
|
|
|
ld hl, $d000
|
|
|
|
|
ld bc, $1000
|
|
|
|
|
call ByteFill
|
|
|
|
|
pop af
|
|
|
|
|
inc a
|
|
|
|
|
cp 8
|
|
|
|
|
jr nc, .bank_loop ; Should be jr c
|
|
|
|
|
ret
|
|
|
|
|
; 270
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:** Change `jr nc, .bank_loop` to `jr c, .bank_loop`.
|
2017-12-12 18:16:15 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## `GetForestTreeFrame` works, but it's still bad
|
|
|
|
|
|
|
|
|
|
[tilesets/animations.asm](tilesets/animations.asm):
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
GetForestTreeFrame: ; fc54c
|
|
|
|
|
; Return 0 if a is even, or 2 if odd.
|
|
|
|
|
and a
|
|
|
|
|
jr z, .even
|
|
|
|
|
cp 1
|
|
|
|
|
jr z, .odd
|
|
|
|
|
cp 2
|
|
|
|
|
jr z, .even
|
|
|
|
|
cp 3
|
|
|
|
|
jr z, .odd
|
|
|
|
|
cp 4
|
|
|
|
|
jr z, .even
|
|
|
|
|
cp 5
|
|
|
|
|
jr z, .odd
|
|
|
|
|
cp 6
|
|
|
|
|
jr z, .even
|
|
|
|
|
.odd
|
|
|
|
|
ld a, 2
|
|
|
|
|
scf
|
|
|
|
|
ret
|
|
|
|
|
.even
|
|
|
|
|
xor a
|
|
|
|
|
ret
|
|
|
|
|
; fc56d
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Fix:**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
GetForestTreeFrame: ; fc54c
|
|
|
|
|
; Return 0 if a is even, or 2 if odd.
|
|
|
|
|
and 1
|
|
|
|
|
add a
|
|
|
|
|
ret
|
|
|
|
|
; fc56d
|
|
|
|
|
```
|