Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix documentation about Magikarp length calculation and bugfix about "Magikarp lengths can be miscalculated" #1100

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions data/events/magikarp_lengths.asm
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ MagikarpLengths:
; [wMagikarpLength] = z * 100 + (bc - x) / y
; First argument is the bc threshold as well as x.
; Second argument is y.
; In reality, due to the bug at .BCLessThanDE,
; the threshold is determined by only register b.
dwb 110, 1 ; not used unless the bug is fixed
dwb 110, 1
dwb 310, 2
dwb 710, 4
dwb 2710, 20
Expand All @@ -17,4 +15,4 @@ MagikarpLengths:
dwb 64710, 20
dwb 65210, 5
dwb 65410, 2
dwb 65510, 1 ; not used
dwb 65510, 1
7 changes: 6 additions & 1 deletion docs/bugs_and_glitches.md
Original file line number Diff line number Diff line change
Expand Up @@ -2407,15 +2407,20 @@ CopyPokemonName_Buffer1_Buffer3:

### Magikarp lengths can be miscalculated

The function `CalcMagikarpLength` is supposed to determine a Magikarp's length by using the Trainer's ID and the Magikarp's DVs, `xor`ing the values, store the result in `bc`, use it as an index to select an entry from the `MagikarpLengths` table and calculate the Magikarp's length, but it doesn't work as intended. The local function `.BCLessThanDE` is supposed to compare the index (stored in `bc`) with a threshold (stored in `de`) from the table to select the correct entry, but it only compares the high bytes of both inputs, which means that the wrong entry might be selected.

This bug also causes the first entry of the `MagikarpLengths` table to go unused and `bc` values between 65280 and 65509 to use the wrong formula. As a side effect, the highest possible length is capped at 1625mm (before being converted to feet and inches).
Idain marked this conversation as resolved.
Show resolved Hide resolved

**Fix:** Edit `CalcMagikarpLength.BCLessThanDE` in [engine/events/magikarp.asm](https://github.com/pret/pokecrystal/blob/master/engine/events/magikarp.asm):

```diff
.BCLessThanDE:
-; BUG: Magikarp lengths can be miscalculated (see docs/bugs_and_glitches.md)
ld a, b
cp d
ret c
- ret c
- ret nc
+ ret nz
ld a, c
cp e
ret
Expand Down
47 changes: 23 additions & 24 deletions engine/events/magikarp.asm
Original file line number Diff line number Diff line change
Expand Up @@ -108,35 +108,34 @@ CalcMagikarpLength:
; de: wEnemyMonDVs
; bc: wPlayerID
Comment on lines 108 to 109
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
; de: wEnemyMonDVs
; bc: wPlayerID
; de: Magikarp's DVs (MON_DVS / wEnemyMonDVs )
; bc: Trainer ID (MON_ID / wPlayerID)

Those are only the inputs when called from LoadEnemyMon in engine/battle/core.asm. When called from CheckMagikarpLength, the inputs are MON_DVS and MON_ID. (This distinction is important, as MON_ID is the Original Trainer's ID, not the player's.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I'll address your suggestions when I get my laptop back.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be MON_OT_ID now.


; This function is poorly commented.

; In short, it generates a value between 190 and 1786 using
; a Magikarp's DVs and its trainer ID. This value is further
; filtered in LoadEnemyMon to make longer Magikarp even rarer.
; It generates a value between 190 and 1755 using a Magikarp's DVs
; and its trainer ID. This value is further filtered in LoadEnemyMon
; to make longer Magikarp even rarer.
Comment on lines +111 to +113
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
; It generates a value between 190 and 1755 using a Magikarp's DVs
; and its trainer ID. This value is further filtered in LoadEnemyMon
; to make longer Magikarp even rarer.
; It generates a length (in mm) between 190 and 1755 using a Magikarp's DVs
; and its Trainer ID.

The comment about being further filtered in LoadEnemyMon assumes this function is only called from LoadEnemyMon (ignoring the case when it is called from CheckMagikarpLength). IMO, what a calling function does with the return value is out of the scope of what belongs in comments on this function.


; The value is generated from a lookup table.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
; The value is generated from a lookup table.
; The length is generated from a lookup table.

; The index is determined by the dv xored with the player's trainer id.

; bc = rrc(dv[0]) ++ rrc(dv[1]) ^ rrc(id)
; The index is determined by the DV xored with the player's trainer ID
; and then stored in bc.
Comment on lines +116 to +117
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
; The index is determined by the DV xored with the player's trainer ID
; and then stored in bc.
; The index is determined by the DV xored with the Magikarp's Trainer ID
; and then stored in bc.

The player's Trainer ID is only used when called from LoadEnemyMon. When this function is called from CheckMagikarpLength, it uses Magikarp's Original Trainer's Trainer ID, not the player's. Even though the Magikarp technically doesn't have a Trainer ID when it is generated in the wild, I think it's clear enough what is being referred to if this is just generalized as "the Magikarp's Trainer ID".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it can feel misleading if we don't also clarify it uses the Player's ID when fighting against a wild Magikarp.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's completely reasonable. Perhaps something like this would be better

Suggested change
; The index is determined by the DV xored with the player's trainer ID
; and then stored in bc.
; The index is determined by the DV xored with a Trainer ID
; (the player's for wild Magikarp; the OT's when checking owned Magikarp)
; then stored in bc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds better.


; if bc < 10: [wMagikarpLength] = c + 190
; if bc ≥ $ff00: [wMagikarpLength] = c + 1370
; if bc < 10: [wMagikarpLength] = bc + 190
; if bc ≥ 65510: [wMagikarpLength] = bc - 65510 + 1600
; else: [wMagikarpLength] = z * 100 + (bc - x) / y

; X, Y, and Z depend on the value of b as follows:

; if b = 0: x = 310, y = 2, z = 3
; if b = 1: x = 710, y = 4, z = 4
; if b = 2-9: x = 2710, y = 20, z = 5
; if b = 10-29: x = 7710, y = 50, z = 6
; if b = 30-68: x = 17710, y = 100, z = 7
; if b = 69-126: x = 32710, y = 150, z = 8
; if b = 127-185: x = 47710, y = 150, z = 9
; if b = 186-224: x = 57710, y = 100, z = 10
; if b = 225-243: x = 62710, y = 50, z = 11
; if b = 244-251: x = 64710, y = 20, z = 12
; if b = 252-253: x = 65210, y = 5, z = 13
; if b = 254: x = 65410, y = 2, z = 14
; X, Y, and Z depend on the value of bc as follows:

; if bc = 10-109: x = 110, y = 1, z = 2
; if bc = 110-309: x = 310, y = 2, z = 3
; if bc = 310-709: x = 710, y = 4, z = 4
; if bc = 710-2709: x = 2710, y = 20, z = 5
; if bc = 2710-7709: x = 7710, y = 50, z = 6
; if bc = 7710-17709: x = 17710, y = 100, z = 7
; if bc = 17710-32709: x = 32710, y = 150, z = 8
; if bc = 32710-47709: x = 47710, y = 150, z = 9
; if bc = 47710-57709: x = 57710, y = 100, z = 10
; if bc = 57710-62709: x = 62710, y = 50, z = 11
; if bc = 62710-64709: x = 64710, y = 20, z = 12
; if bc = 64710-65209: x = 65210, y = 5, z = 13
; if bc = 65210-65409: x = 65410, y = 2, z = 14
; if bc = 65410-65509: x = 65510, y = 1, z = 15
Idain marked this conversation as resolved.
Show resolved Hide resolved

; bc = rrc(dv[0]) ++ rrc(dv[1]) ^ rrc(id)

Expand Down