First Commit

Upload literally everything from the pokecrystal16 expand-move-ID branch
This commit is contained in:
Zeta_Null 2023-09-10 12:35:35 -04:00
commit 2f8a41f833
4618 changed files with 480386 additions and 0 deletions

1
docs/_config.yml Normal file
View file

@ -0,0 +1 @@
markdown: gfm

112
docs/assets/css/style.scss Normal file
View file

@ -0,0 +1,112 @@
---
---
@import "{{ site.theme }}";
@media (prefers-color-scheme: dark) {
body { color: #a9bacb; background-color: #14171a; }
a,
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link { color: #3689b2; }
.markdown-body hr { background-color: #64757f; }
.markdown-body blockquote { color: #667380; border-left-color: #3d454b; }
.markdown-body kbd { color: #8896a4; background-color: #181d20; border-color: #393f46; border-bottom-color: #4e565f; box-shadow: inset 0 -1px 0 #4e565f; }
.markdown-body h1,
.markdown-body h2 { border-bottom: 1px solid #64757f; }
.markdown-body h6 { color: #717d89; } /* ok */
.markdown-body table th,
.markdown-body table td { border-color: #30373c; }
.markdown-body table tr { background-color: #14171a; border-top-color: #393f46; }
.markdown-body table tr:nth-child(2n) { background-color: #23292d; }
.markdown-body img { background-color: #14171a; }
.markdown-body span.frame > span { border-color: #30373c; }
.markdown-body span.frame span span { color: #a9bacb; }
.markdown-body code,
.markdown-body tt { background-color: rgba(229,225,221,0.05); }
.markdown-body .highlight pre,
.markdown-body pre { background-color: #23292d; }
.markdown-body .csv-data .blob-num { background-color: #14171a; }
.markdown-body .csv-data th { background-color: #23292d; }
.highlight { background-color: #181d20; }
.highlight .w { color: #242424; } /* Text.Whitespace */
.highlight .err { color: #f61717; background-color: #330000; } /* Error */
.highlight .gi { color: #ffffff; background-color: #225522; } /* Generic.Inserted */
.highlight .gd { color: #ffffff; background-color: #662222; } /* Generic.Deleted */
.highlight .ge { color: #ffffff; } /* Generic.Emph */
.highlight .gr { color: #b22518; } /* Generic.Error */
.highlight .gh { color: #253b22; } /* Generic.Heading */
.highlight .go { color: #777777; } /* Generic.Output */
.highlight .gp { color: #cccccc; } /* Generic.Prompt */
.highlight .gu { color: #555555; } /* Generic.Subheading */
.highlight .gt { color: #aa0000; } /* Generic.Traceback */
.highlight .c { color: #5f5a60; } /* Comment */
.highlight .cm { color: #5f5a60; } /* Comment.Multiline */
.highlight .cp { color: #5f5a60; } /* Comment.Preproc */
.highlight .c1 { color: #5f5a60; } /* Comment.Single */
.highlight .cs { color: #5f5a60; } /* Comment.Special */
.highlight .cd { color: #5f5a60; }
.highlight .k { color: #ffffff; } /* Keyword */
.highlight .kc { color: #ffffff; } /* Keyword.Constant */
.highlight .kd { color: #ffffff; } /* Keyword.Declaration */
.highlight .kn { color: #ffffff; } /* Keyword.Namespace */
.highlight .kp { color: #ffffff; } /* Keyword.Pseudo */
.highlight .kr { color: #ffffff; } /* Keyword.Reserved */
.highlight .kt { color: #ffffff; } /* Keyword.Type */
.highlight .kv { color: #ffffff; }
.highlight .o { color: #ffffff; } /* Operator */
.highlight .ow { color: #aa22ff; } /* Operator.Word */
.highlight .m { color: #cf6a4c; } /* Literal.Number */
.highlight .mb { color: #cf6a4c; } /* Literal.Number.Bin */
.highlight .mf { color: #cf6a4c; } /* Literal.Number.Float */
.highlight .mh { color: #cf6a4c; } /* Literal.Number.Hex */
.highlight .mi { color: #cf6a4c; } /* Literal.Number.Integer */
.highlight .mo { color: #cf6a4c; } /* Literal.Number.Oct */
.highlight .mx { color: #cf6a4c; }
.highlight .il { color: #cf6a4c; } /* Literal.Number.Integer.Long */
.highlight .s { color: #8f9d6a; } /* Literal.String */
.highlight .sb { color: #8f9d6a; } /* Literal.String.Backtick */
.highlight .sc { color: #8f9d6a; } /* Literal.String.Char */
.highlight .sd { color: #8f9d6a; } /* Literal.String.Doc */
.highlight .s2 { color: #8f9d6a; } /* Literal.String.Double */
.highlight .se { color: #8f9d6a; } /* Literal.String.Escape */
.highlight .sh { color: #8f9d6a; } /* Literal.String.Heredoc */
.highlight .si { color: #8f9d6a; } /* Literal.String.Interpol */
.highlight .sx { color: #8f9d6a; } /* Literal.String.Other */
.highlight .sr { color: #e9c062; } /* Literal.String.Regex */
.highlight .s1 { color: #8f9d6a; } /* Literal.String.Single */
.highlight .ss { color: #daefa3; } /* Literal.String.Symbol */
.highlight .vc { color: #7587a6; } /* Name.Variable.Class */
.highlight .vg { color: #7587a6; } /* Name.Variable.Global */
.highlight .vi { color: #7587a6; } /* Name.Variable.Instance */
.highlight .na { color: #f9ee98; } /* Name.Attribute */
.highlight .nb { color: #cda869; } /* Name.Builtin */
.highlight .nc { color: #9b859d; } /* Name.Class */
.highlight .no { color: #9b859d; } /* Name.Constant */
.highlight .nd { color: #7587a6; } /* Name.Decorator */
.highlight .ni { color: #cf6a4c; } /* Name.Entity */
.highlight .ne { color: #9b703f; } /* Name.Exception */
.highlight .nf { color: #9b703f; } /* Name.Function */
.highlight .nl { color: #9b703f; } /* Name.Label */
.highlight .nn { color: #9b859d; } /* Name.Namespace */
.highlight .nt { color: #cda869; } /* Name.Tag */
.highlight .nv { color: #7587a6; } /* Name.Variable */
.highlight .bp { color: #00aaaa; } /* Name.Builtin.Pseudo */
}

View file

@ -0,0 +1,261 @@
# Battle Animation Commands
Defined in [macros/scripts/battle_anims.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/battle_anims.asm) and [engine/battle_anims/anim_commands.asm:BattleAnimCommands](https://github.com/pret/pokecrystal/blob/master/engine/battle_anims/anim_commands.asm).
## `$00``$CF`: <code>anim_wait <i>length</i></code>
- *length*: duration in frames
## `$D0`: <code>anim_obj <i>object</i>, <i>x</i>, <i>y</i>, <i>param</i></code>
***Alternate*: <code>anim_obj <i>object</i>, <i>x_tile</i>, <i>x</i>, <i>y_tile</i>, <i>y</i>, <i>param</i></code>**
Spawns an *object* at coordinate (*x*, *y*).
- *object*: `ANIM_OBJ` constants (see [constants/battle_anim_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/battle_anim_constants.asm))
- *x*: the x position in pixels
- *y*: the y position in pixels
- *x_tile*: an added x position in tiles (8 pixels)
- *y_tile*: an added y position in tiles (8 pixels)
- *param*: modifies the behavior of *object*. The meaning differs for each object.
The y position also depends on the y offset defined by the object.
- *TODO: what happens for x/y values greater than 160/144 respectively? Is it 1:1 with screen coordinates?*
- *TODO: how are the x/y values mirrored when the opponent is attacking?*
- *TODO: useful positions*
- *TODO: document each object*
## `$D1`: <code>anim_1gfx <i>gfx</i></code>
## `$D2`: <code>anim_2gfx <i>gfx1</i>, <i>gfx2</i></code>
## `$D3`: <code>anim_3gfx <i>gfx1</i>, <i>gfx2</i>, <i>gfx3</i></code>
## `$D4`: <code>anim_4gfx <i>gfx1</i>, <i>gfx2</i>, <i>gfx3</i>, <i>gfx4</i></code>
## `$D5`: <code>anim_5gfx <i>gfx1</i>, <i>gfx2</i>, <i>gfx3</i>, <i>gfx4</i>, <i>gfx5</i></code>
Loads 1-5 sets of graphics. Will overwrite any previously loaded sets.
- *gfx*: `ANIM_GFX` constants (see [constants/battle_anim_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/battle_anim_constants.asm))
Caveats:
- These will override any currently-loaded GFX.
## `$D6`: <code>anim_incobj <i>object_id</i></code>
Increments an object's state.
- *object_id*: the nth object in order of creation
Objects are state machines. `anim_incobj` progresses the state of an object.
## `$D7`: <code>anim_setobj <i>object_id</i>, <i>state</i></code>
Sets an object's state to a specific value.
- *object_id*: the nth object in order of creation
- *state*: the state index
Objects are state machines. `anim_setobj` changes the state of an object.
## `$D8`: <code>anim_incbgeffect <i>bg_effect</i></code>
Increments a bg effect's state.
- *bg_effect*: `ANIM_BG` constants (see [constants/battle_anim_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/battle_anim_constants.asm))
Since there can't be two of the same bg effect, the effect type is used. This is distinct from `anim_incobj`.
## `$D9`: `anim_battlergfx_2row`
## `$DA`: `anim_battlergfx_1row`
Loads animation graphics for the bottom one or two rows of the enemy pokemon and the top one or two rows of the player's.
These graphics are identified through `ANIM_GFX_ENEMYFEET` and `ANIM_GFX_PLAYERHEAD`.
Caveats:
- Doesn't work with `anim_4gfx` and `anim_5gfx`.
- This overwrites previously loaded animation graphics if you've loaded more than 53 tiles (2row) or 66 tiles (1row).
## `$DB`: `anim_checkpokeball`
Sets `BattleAnimVar` to the result of [GetPokeBallWobble](https://github.com/pret/pokecrystal/blob/master/engine/battle_anims/pokeball_wobble.asm).
## `$DC`: `anim_transform`
## `$DD`: `anim_raisesub`
## `$DE`: `anim_dropsub`
## `$DF`: `anim_resetobp0`
Resets rOBP0 to the default (`q0123` or `%00011011`).
## `$E0`: <code>anim_sound <i>duration</i>, <i>tracks</i>, <i>sound_id</i></code>
Plays a sound.
## `$E1`: <code>anim_cry <i>pitch</i></code>
Plays the user's cry.
## `$E2`: `anim_minimizeopp`
## `$E3`: `anim_oamon`
## `$E4`: `anim_oamoff`
## `$E5`: `anim_clearobjs`
Removes all active objects.
## `$E6`: `anim_beatup`
## `$E7`: `anim_0xe7`
Does nothing. Unused.
## `$E8`: `anim_updateactorpic`
## `$E9`: `anim_minimize`
## `$EA`: `anim_0xea`
Does nothing. Unused.
## `$EB`: `anim_0xeb`
Does nothing. Unused.
## `$EC`: `anim_0xec`
Does nothing. Unused.
## `$ED`: `anim_0xed`
Does nothing. Unused.
## `$EE`: <code>anim_if_param_and <i>value</i>, <i>address</i></code>
## `$EF`: <code>anim_jumpuntil <i>address</i></code>
Jumps to another script and decrements `param` until it reaches 0. Similar to `anim_loop`.
## `$F0`: <code>anim_bgeffect <i>bg_effect</i>, <i>unknown1</i>, <i>unknown2</i>, <i>unknown3</i></code>
- *bg_effect*: `ANIM_BG` constants (see [constants/battle_anim_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/battle_anim_constants.asm))
## `$F1`: <code>anim_bgp <i>colors</i></code>
Sets `rBGP` to *colors*.
## `$F2`: <code>anim_obp0 <i>colors</i></code>
Sets `rOBP0` to *colors*.
## `$F3`: <code>anim_obp1 <i>colors</i></code>
Sets `rOBP1` to *colors*.
## `$F4`: `anim_keepsprites`
Causes only the palettes to be cleared from the OAM memory when the animation ends, instead of clearing all of the OAM memory. This causes all objects to start using palette 0 (monochrome) when the animation script ends, and whatever objects were on the screen before the last `anim_ret` will stay on the screen.
This is only used for the Poke Ball animation.
## `$F5`: `anim_0xf5`
Does nothing. Unused.
## `$F6`: `anim_0xf6`
Does nothing. Unused.
## `$F7`: `anim_0xf7`
Does nothing. Unused.
## `$F8`: <code>anim_if_param_equal <i>value</i>, <i>address</i></code>
Jumps to another script if `wBattleAnimParam` is equal to *value*.
## `$F9`: <code>anim_setvar <i>value</i></code>
Sets `BattleAnimVar` to *value*.
## `$FA`: `anim_incvar`
Increments `BattleAnimVar` by 1.
## `$FB`: <code>anim_if_var_equal <i>value</i>, <i>address</i></code>
Jumps to another script if `BattleAnimVar` is equal to *value*.
## `$FC`: <code>anim_jump <i>address</i></code>
Jumps to another script.
## `$FD`: <code>anim_loop <i>count</i>, <i>address</i></code>
Jumps to another script up to *count* times. Then does nothing, allowing execution to continue.
## `$FE`: <code>anim_call <i>address</i></code>
Calls a script.
There is no call stack. The return address is overwritten, so the maximum call depth is 1.
## `$FF`: `anim_ret`
Ends the script and returns to where it was called from. If there was no caller, the animation ends.

2700
docs/bugs_and_glitches.md Normal file

File diff suppressed because it is too large Load diff

852
docs/design_flaws.md Normal file
View file

@ -0,0 +1,852 @@
# Design Flaws
These are parts of the code that do not work *incorrectly*, like [bugs and glitches](https://github.com/pret/pokecrystal/blob/master/docs/bugs_and_glitches.md), but that clearly exist just to work around a problem. In other words, with a slightly different design, the code would not need to exist at all. Design flaws may be exceptions to a usual rule, such as "tables of pointers in different banks use `dba`" ([one exception](#pic-banks-are-offset-by-pics_fix), [and another](#pok%C3%A9dex-entry-banks-are-derived-from-their-species-ids)) or "graphics used as a unit are stored and loaded contiguously" ([a notable exception](#footprints-are-split-into-top-and-bottom-halves)).
## Contents
- [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)
- [Music IDs $64 and $80 or above have special behavior](#music-ids-64-and-80-or-above-have-special-behavior)
- [`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%C3%A9dex-entry-banks-are-derived-from-their-species-ids)
- [The 6-bit caught level can only record up to level 63](#the-6-bit-caught-level-can-only-record-up-to-level-63)
- [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)
- [The overworld scripting engine assumes no more than 127 banks](#the-overworld-scripting-engine-assumes-no-more-than-127-banks)
## Pic banks are offset by `PICS_FIX`
[data/pokemon/pic_pointers.asm](https://github.com/pret/pokecrystal/blob/master/data/pokemon/pic_pointers.asm), [data/pokemon/unown_pic_pointers.asm](https://github.com/pret/pokecrystal/blob/master/data/pokemon/unown_pic_pointers.asm), and [data/trainers/pic_pointers.asm](https://github.com/pret/pokecrystal/blob/master/data/trainers/pic_pointers.asm) all have to use `dba_pic` instead of `dba`. This is a macro in [macros/data.asm](https://github.com/pret/pokecrystal/blob/master/macros/data.asm) that offsets banks by `PICS_FIX`:
```asm
MACRO dba_pic ; dbw bank, address
db BANK(\1) - PICS_FIX
dw \1
ENDM
```
The offset is translated into a correct bank by `FixPicBank` in [engine/gfx/load_pics.asm](https://github.com/pret/pokecrystal/blob/master/engine/gfx/load_pics.asm):
```asm
FixPicBank:
; This is a thing for some reason.
DEF PICS_FIX EQU $36
GLOBAL PICS_FIX
push hl
push bc
sub BANK("Pics 1") - PICS_FIX
ld c, a
ld b, 0
ld hl, .PicsBanks
add hl, bc
ld a, [hl]
pop bc
pop hl
ret
.PicsBanks:
db BANK("Pics 1") ; BANK("Pics 1") + 0
...
db BANK("Pics 24") ; BANK("Pics 1") + 23
```
**Fix:** Delete `FixPicBank` and remove all four calls to `FixPicBank` in [engine/gfx/load_pics.asm](https://github.com/pret/pokecrystal/blob/master/engine/gfx/load_pics.asm). Then use `dba` instead of `dba_pic` everywhere.
## `PokemonPicPointers` and `UnownPicPointers` are assumed to start at the same address
`GetFrontpicPointer` and `GetMonBackpic` in [engine/gfx/load_pics.asm](https://github.com/pret/pokecrystal/blob/master/engine/gfx/load_pics.asm) make this assumption, which has to be accounted for in the data files.
In [gfx/pics.asm](https://github.com/pret/pokecrystal/blob/master/gfx/pics.asm):
```asm
; PokemonPicPointers and UnownPicPointers are assumed to start at the same
; address, but in different banks. This is enforced in layout.link.
SECTION "Pic Pointers", ROMX
INCLUDE "data/pokemon/pic_pointers.asm"
SECTION "Unown Pic Pointers", ROMX
INCLUDE "data/pokemon/unown_pic_pointers.asm"
```
In [layout.link](https://github.com/pret/pokecrystal/blob/master/layout.link):
```
ROMX $48
org $4000
"Pic Pointers"
"Pics 1"
ROMX $49
org $4000
"Unown Pic Pointers"
"Pics 2"
```
**Fix:**
Don't enforce `org $4000` in [layout.link](https://github.com/pret/pokecrystal/blob/master/layout.link).
Edit `GetFrontpicPointer`:
```diff
ld a, [wCurPartySpecies]
cp UNOWN
jr z, .unown
+ ld hl, PokemonPicPointers
ld a, [wCurPartySpecies]
ld d, BANK(PokemonPicPointers)
jr .ok
.unown
+ ld hl, UnownPicPointers
ld a, [wUnownLetter]
ld d, BANK(UnownPicPointers)
.ok
- ; These are assumed to be at the same address in their respective banks.
- assert PokemonPicPointers == UnownPicPointers
- ld hl, PokemonPicPointers
dec a
ld bc, 6
call AddNTimes
```
And `GetMonBackpic`:
```diff
- ; These are assumed to be at the same address in their respective banks.
- assert PokemonPicPointers == UnownPicPointers
ld hl, PokemonPicPointers
ld a, b
ld d, BANK(PokemonPicPointers)
cp UNOWN
jr nz, .ok
+ ld hl, UnownPicPointers
ld a, c
ld d, BANK(UnownPicPointers)
.ok
dec a
ld bc, 6
call AddNTimes
```
## Footprints are split into top and bottom halves
In [gfx/footprints.asm](https://github.com/pret/pokecrystal/blob/master/gfx/footprints.asm):
```asm
; Footprints are 2x2 tiles each, but are stored as a 16x64-tile image
; (32 rows of 8 footprints per row).
; That means there's a row of the top two tiles for eight footprints,
; then a row of the bottom two tiles for those eight footprints.
; These macros help extract the first and the last two tiles, respectively.
DEF footprint_top EQUS "0, 2 * LEN_1BPP_TILE"
DEF footprint_bottom EQUS "2 * LEN_1BPP_TILE, 2 * LEN_1BPP_TILE"
Footprints:
; Entries correspond to Pokémon species, two apiece, 8 tops then 8 bottoms
table_width LEN_1BPP_TILE * 4, Footprints
; 001-008 top halves
INCBIN "gfx/footprints/bulbasaur.1bpp", footprint_top
INCBIN "gfx/footprints/ivysaur.1bpp", footprint_top
INCBIN "gfx/footprints/venusaur.1bpp", footprint_top
INCBIN "gfx/footprints/charmander.1bpp", footprint_top
INCBIN "gfx/footprints/charmeleon.1bpp", footprint_top
INCBIN "gfx/footprints/charizard.1bpp", footprint_top
INCBIN "gfx/footprints/squirtle.1bpp", footprint_top
INCBIN "gfx/footprints/wartortle.1bpp", footprint_top
; 001-008 bottom halves
INCBIN "gfx/footprints/bulbasaur.1bpp", footprint_bottom
INCBIN "gfx/footprints/ivysaur.1bpp", footprint_bottom
INCBIN "gfx/footprints/venusaur.1bpp", footprint_bottom
INCBIN "gfx/footprints/charmander.1bpp", footprint_bottom
INCBIN "gfx/footprints/charmeleon.1bpp", footprint_bottom
INCBIN "gfx/footprints/charizard.1bpp", footprint_bottom
INCBIN "gfx/footprints/squirtle.1bpp", footprint_bottom
INCBIN "gfx/footprints/wartortle.1bpp", footprint_bottom
...
```
`Pokedex_LoadAnyFootprint` in [engine/pokedex/pokedex.asm](https://github.com/pret/pokecrystal/blob/master/engine/pokedex/pokedex.asm) has to load the halves separately.
**Fix:**
Store footprints contiguously:
```asm
Footprints:
table_width LEN_1BPP_TILE * 4, Footprints
INCBIN "gfx/footprints/bulbasaur.1bpp"
INCBIN "gfx/footprints/ivysaur.1bpp"
INCBIN "gfx/footprints/venusaur.1bpp"
INCBIN "gfx/footprints/charmander.1bpp"
INCBIN "gfx/footprints/charmeleon.1bpp"
INCBIN "gfx/footprints/charizard.1bpp"
INCBIN "gfx/footprints/squirtle.1bpp"
INCBIN "gfx/footprints/wartortle.1bpp"
...
```
Edit `Pokedex_LoadAnyFootprint`:
```diff
ld a, [wTempSpecies]
dec a
and %111
swap a ; * $10
+ add a, a
ld l, a
ld h, 0
add hl, de
ld de, Footprints
add hl, de
- push hl
ld e, l
ld d, h
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
```
## Music IDs $64 and $80 or above have special behavior
If a map's music ID in [data/maps/maps.asm](https://github.com/pret/pokecrystal/blob/master/master/data/maps/maps.asm) is $64 (the value of `MUSIC_MAHOGANY_MART` or `MUSIC_SUICUNE_BATTLE`) it will play either `MUSIC_ROCKET_HIDEOUT` or `MUSIC_CHERRYGROVE_CITY`. Moreover, if a map's music ID is $80 or above (the value of `RADIO_TOWER_MUSIC`) it might play `MUSIC_ROCKET_OVERTURE` or something else. This is caused by `GetMapMusic` in [home/map.asm](https://github.com/pret/pokecrystal/blob/master/master/home/map.asm).
**Fix:**
Replace `RADIO_TOWER_MUSIC | MUSIC_GOLDENROD_CITY` with `MUSIC_RADIO_TOWER` in [data/maps/maps.asm](https://github.com/pret/pokecrystal/blob/master/master/data/maps/maps.asm).
Redefine the special music constants in [constants/music_constants.asm](https://github.com/pret/pokecrystal/blob/master/master/constants/music_constants.asm):
```diff
-; GetMapMusic picks music for this value (see home/map.asm)
-DEF MUSIC_MAHOGANY_MART EQU $64
+; GetMapMusic picks music for these values (see home/map.asm)
+DEF MUSIC_MAHOGANY_MART EQU $fc
+DEF MUSIC_RADIO_TOWER EQU $fd
; ExitPokegearRadio_HandleMusic uses these values
DEF RESTART_MAP_MUSIC EQU $fe
DEF ENTER_MAP_MUSIC EQU $ff
-
-; GetMapMusic picks music for this bit flag
-DEF RADIO_TOWER_MUSIC_F EQU 7
-DEF RADIO_TOWER_MUSIC EQU 1 << RADIO_TOWER_MUSIC_F
```
Edit `GetMapMusic`:
```diff
GetMapMusic::
push hl
push bc
ld de, MAP_MUSIC
call GetMapField
ld a, c
cp MUSIC_MAHOGANY_MART
jr z, .mahoganymart
- bit RADIO_TOWER_MUSIC_F, c
- jr nz, .radiotower
+ cp MUSIC_RADIO_TOWER
+ jr z, .radiotower
farcall Function8b342
ld e, c
ld d, 0
.done
pop bc
pop hl
ret
.radiotower
ld a, [wStatusFlags2]
bit STATUSFLAGS2_ROCKETS_IN_RADIO_TOWER_F, a
jr z, .clearedradiotower
ld de, MUSIC_ROCKET_OVERTURE
jr .done
.clearedradiotower
- ; the rest of the byte
- ld a, c
- and RADIO_TOWER_MUSIC - 1
- ld e, a
- ld d, 0
+ ld de, MUSIC_GOLDENROD_CITY
jr .done
.mahoganymart
ld a, [wStatusFlags2]
bit STATUSFLAGS2_ROCKETS_IN_MAHOGANY_F, a
jr z, .clearedmahogany
ld de, MUSIC_ROCKET_HIDEOUT
jr .done
.clearedmahogany
ld de, MUSIC_CHERRYGROVE_CITY
jr .done
```
## `ITEM_C3` and `ITEM_DC` break up the continuous sequence of TM items
[constants/item_constants.asm](https://github.com/pret/pokecrystal/blob/master/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
DEF NUM_TMS EQU const_value - TM01 - 2 ; discount ITEM_C3 and ITEM_DC
```
`GetTMHMNumber` and `GetNumberedTMHM` in [engine/items/items.asm](https://github.com/pret/pokecrystal/blob/master/engine/items/items.asm) have to compensate for this.
> There was originally a good reason for these two gaps!
>
> Pokémon traded from RBY to GSC have their catch rate interpreted as their new held item. This was planned early on in development, so some items were given indexes corresponding to appropriate Gen 1 catch rates:
>
> - $03 = 3: `BRIGHTPOWDER` is for Articuno, Zapdos, Moltres, and Mewtwo
> - $1E = 30: `LUCKY_PUNCH` is for Chansey
> - $23 = 35: `METAL_POWDER` is for Ditto
> - $3C = 60: `SILVER_LEAF` is for 10 Pokémon
> - $4B = 75: `GOLD_LEAF` is for 13 Pokémon
> - $96 = 150: `MYSTERYBERRY` is for Clefairy
> - $AA = 170: `POLKADOT_BOW` is for Jigglypuff
> - $B4 = 180: `BRICK_PIECE` is for Machop
>
> Yellow was also being developed then, and it did the reverse, altering some Pokémon's data after they're caught to correspond to appropriate Gen 2 items:
>
> - Starter Pikachu's catch rate byte is overwritten with 163 = $A3 for `LIGHT_BALL`
> - Wild-caught Kadabra's catch rate byte is overwritten with 96 = $60 for `TWISTEDSPOON`
>
> (Yellow also directly changed Dragonair's catch rate to 27 and Dragonite's to 9, but this seems to have been only for adjusting their difficulty, since those meaninglessly correspond to `PROTEIN` and `ANTIDOTE`.)
>
> Most catch rates were left as gaps in the item list, and transformed into held items via the `TimeCapsule_CatchRateItems` table in [data/items/catch_rate_items.asm](https://github.com/pret/pokecrystal/blob/master/data/items/catch_rate_items.asm). For example, the 52 Pokémon with catch rate 45 would hold the gap `ITEM_2D`, except that gets transformed into `BITTER_BERRY`.
>
> But a few Pokémon end up with weird items. Abra has a catch rate of 200, or $C8; and Krabby, Horsea, Goldeen, and Staryu have a catch rate of 225, or $E1. Those indexes correspond to the items `TM_PSYCH_UP` and `TM_ICE_PUNCH`, which seem like random choices—because they are.
>
> The TMs and HMs span from indexes $BF to $F9. However, as we can see in [pokegold-spaceworld](https://github.com/pret/pokegold-spaceworld/blob/master/constants/item_constants.asm), they *originally* spanned $C4 to $FF. For some reason they were shifted down by 5 during development.
>
> Before the index shift, the gap `ITEM_C3` would have been at index $C8, and `ITEM_DC` at $E1. In other words, they would have neatly corresponded to the catch rates for those five Pokémon! Then they would have held `BERRY` when traded through the Time Capsule (since the gap items get transformed via `TimeCapsule_CatchRateItems`).
**Fix:**
Move `ITEM_C3` and `ITEM_DC` above all the TMs in every table of item data.
Edit [engine/items/items.asm](https://github.com/pret/pokecrystal/blob/master/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
```
## Pokédex entry banks are derived from their species IDs
`PokedexDataPointerTable` in [data/pokemon/dex_entry_pointers.asm](https://github.com/pret/pokecrystal/blob/master/data/pokemon/dex_entry_pointers.asm) is a table of `dw`, not `dba`, yet there are four banks used for Pokédex entries. The correct bank is derived from the species ID at the beginning of each Pokémon's base stats. (This is the only use the base stat species ID has.)
Three separate routines do the same derivation: `GetDexEntryPointer` in [engine/pokedex/pokedex_2.asm](https://github.com/pret/pokecrystal/blob/master/engine/pokedex/pokedex_2.asm):
```asm
GetDexEntryPointer:
; return dex entry pointer b:de
push hl
ld hl, PokedexDataPointerTable
ld a, b
dec a
ld d, 0
ld e, a
add hl, de
add hl, de
ld e, [hl]
inc hl
ld d, [hl]
push de
rlca
rlca
maskbits NUM_DEX_ENTRY_BANKS
ld hl, .PokedexEntryBanks
ld d, 0
ld e, a
add hl, de
ld b, [hl]
pop de
pop hl
ret
.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")
```
`HeavyBall_GetDexEntryBank` in [engine/items/item_effects.asm](https://github.com/pret/pokecrystal/blob/master/engine/items/item_effects.asm):
```asm
HeavyBall_GetDexEntryBank:
push hl
push de
ld a, [wEnemyMonSpecies]
rlca
rlca
maskbits NUM_DEX_ENTRY_BANKS
ld hl, .PokedexEntryBanks
ld d, 0
ld e, a
add hl, de
ld a, [hl]
pop de
pop hl
ret
.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")
```
And `PokedexShow_GetDexEntryBank` in [engine/pokegear/radio.asm](https://github.com/pret/pokecrystal/blob/master/engine/pokegear/radio.asm):
```asm
PokedexShow_GetDexEntryBank:
push hl
push de
ld a, [wCurPartySpecies]
dec a
rlca
rlca
maskbits NUM_DEX_ENTRY_BANKS
ld hl, .PokedexEntryBanks
ld d, 0
ld e, a
add hl, de
ld a, [hl]
pop de
pop hl
ret
.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`. Make sure to edit the `table_width` line to specify a width of 3 instead of 2.
Delete `HeavyBall_GetDexEntryBank` and `PokedexShow_GetDexEntryBank`. You can also delete `NUM_DEX_ENTRY_BANKS` from [constants/pokemon_data_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/pokemon_data_constants.asm).
Edit [engine/pokedex/pokedex_2.asm](https://github.com/pret/pokecrystal/blob/master/engine/pokedex/pokedex_2.asm):
```diff
GetDexEntryPointer:
; return dex entry pointer b:de
push hl
ld hl, PokedexDataPointerTable
ld a, b
dec a
ld d, 0
ld e, a
add hl, de
add hl, de
- ld e, [hl]
- inc hl
- ld d, [hl]
- push de
- rlca
- rlca
- maskbits NUM_DEX_ENTRY_BANKS
- ld hl, .PokedexEntryBanks
- ld d, 0
- ld e, a
- add hl, de
- ld b, [hl]
- pop de
+ add hl, de
+ ; b = bank
+ ld a, [hli]
+ ld b, a
+ ; de = address
+ ld a, [hli]
+ ld e, a
+ ld d, [hl]
pop hl
ret
-
-.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")
```
Edit [engine/items/item_effects.asm](https://github.com/pret/pokecrystal/blob/master/engine/items/item_effects.asm):
```diff
HeavyBallMultiplier:
; subtract 20 from catch rate if weight < 102.4 kg
; else add 0 to catch rate if weight < 204.8 kg
; else add 20 to catch rate if weight < 307.2 kg
; else add 30 to catch rate if weight < 409.6 kg
; else add 40 to catch rate
ld a, [wEnemyMonSpecies]
ld hl, PokedexDataPointerTable
dec a
ld e, a
ld d, 0
add hl, de
add hl, de
+ add hl, de
+ ; d = bank, hl = address
+ ld a, BANK(PokedexDataPointerTable)
+ call GetFarByte
+ push af
+ inc hl
ld a, BANK(PokedexDataPointerTable)
call GetFarWord
+ pop de
.SkipText:
- call HeavyBall_GetDexEntryBank
+ ld a, d
call GetFarByte
inc hl
cp "@"
jr nz, .SkipText
- call HeavyBall_GetDexEntryBank
+ ld a, d
push bc
inc hl
inc hl
call GetFarWord
```
And edit [engine/pokegear/radio.asm](https://github.com/pret/pokecrystal/blob/master/engine/pokegear/radio.asm):
```diff
PokedexShow2:
ld a, [wCurPartySpecies]
dec a
ld hl, PokedexDataPointerTable
ld c, a
ld b, 0
add hl, bc
add hl, bc
+ add hl, bc
+ ; b = bank
+ ld a, BANK(PokedexDataPointerTable)
+ call GetFarByte
+ ld b, a
+ inc hl
+ ; hl = address
ld a, BANK(PokedexDataPointerTable)
call GetFarWord
- call PokedexShow_GetDexEntryBank
+ ld a, b
push af
push hl
call CopyDexEntryPart1
```
## The 6-bit caught level can only record up to level 63
Crystal added the Poké Seer, who tells you your Pokémon's caught data: where it was caught, what time, and at what level. The status screen also displays the gender of its Original Trainer, since Crystal added player genders. This data is packed into two previously-unused bytes in the `box_struct`; from [macros/ram.asm](https://github.com/pret/pokecrystal/blob/master/macros/ram.asm):
```asm
MACRO box_struct
\1Species:: db
...
\1CaughtData::
\1CaughtTime::
\1CaughtLevel:: db
\1CaughtGender::
\1CaughtLocation:: db
\1Level:: db
\1BoxEnd::
ENDM
```
These four pieces of data are packed into two bytes using the bitmasks in [constants/pokemon_data_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/pokemon_data_constants.asm):
```asm
DEF CAUGHT_TIME_MASK EQU %11000000
DEF CAUGHT_LEVEL_MASK EQU %00111111
DEF CAUGHT_GENDER_MASK EQU %10000000
DEF CAUGHT_LOCATION_MASK EQU %01111111
```
The caught level only uses six bits, so it can only record levels as high as 2^6 1 = 63. If a Pokémon is caught at level 64 or higher, its level overflows into the two bits used for the caught time, before the actual caught time is stored in the same byte with a bitwise `or` operation. For example, a Pokémon caught at level 70 (`%01000110`) in the morning (`%00000000`) would be reported as caught at level 6 (`%000110` in the low six bits) during the day (`%01` in the high two bits).
This limitation is probably why Lugia and Ho-Oh are both encountered at level 60 in Crystal, instead of level 70 in GS.
**Possible fixes:**
- Record any level higher than 63 as level 0, and have the Poké Seer report 0 as "very high".
- Use seven bits for the level (which can store up to level 2^7 1 = 127) and one for the time, simply recording 0 for morning or day and 1 for night.
- Move some data around into unused bits elsewhere in the `box_struct`, such as the high bit of `MON_LEVEL`, or the three high bits of `MON_EXP`.
- Add another byte for more caught data, making the `box_struct` larger; this would affect PC Box storage.
- Free up some other bytes in the `box_struct` (e.g. by [replacing](https://github.com/pret/pokecrystal/wiki/Replace-stat-experience-with-EVs) 2-byte stat experience with 1-byte EVs).
## Identical sine wave code and data is repeated five times
`_Sine` in [engine/math/sine.asm](https://github.com/pret/pokecrystal/blob/master/engine/math/sine.asm):
```asm
_Sine::
; a = d * sin(e * pi/32)
ld a, e
calc_sine_wave
```
`Sprites_Cosine` and `Sprites_Sine` in [engine/gfx/sprites.asm](https://github.com/pret/pokecrystal/blob/master/engine/gfx/sprites.asm):
```asm
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
```
`BattleAnim_Cosine` and `BattleAnim_Sine` in [engine/battle_anims/functions.asm](https://github.com/pret/pokecrystal/blob/master/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
```
`StartTrainerBattle_DrawSineWave` in [engine/battle/battle_transition.asm](https://github.com/pret/pokecrystal/blob/master/engine/battle/battle_transition.asm):
```asm
StartTrainerBattle_DrawSineWave:
calc_sine_wave
```
And `CelebiEvent_Cosine` in [engine/events/celebi.asm](https://github.com/pret/pokecrystal/blob/master/engine/events/celebi.asm):
```asm
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](https://github.com/pret/pokecrystal/blob/master/macros/code.asm):
```asm
MACRO calc_sine_wave
; 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
ret
.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](https://github.com/pret/pokecrystal/blob/master/macros/data.asm):
```asm
MACRO sine_table
; \1 samples of sin(x) from x=0 to x<0.5 turns (pi radians)
for x, \1
dw sin(x * 0.5 / (\1))
endr
ENDM
```
**Fix:** Edit [home/sine.asm](https://github.com/pret/pokecrystal/blob/master/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
The routine `GetForestTreeFrame` in [engine/tilesets/tileset_anims.asm](https://github.com/pret/pokecrystal/blob/master/engine/tilesets/tileset_anims.asm) is hilariously inefficient.
**Fix:**
Edit `GetForestTreeFrame`:
```diff
GetForestTreeFrame:
; 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
+ and 1
+ add a
ret
```
## The overworld scripting engine assumes no more than 127 banks
The `CallCallback` and `ExitScriptSubroutine` functions in [engine/overworld/scripting.asm](https://github.com/pret/pokecrystal/blob/master/engine/overworld/scripting.asm) use the highest bit of the bank value, to store whether a certain script stack position should be treated as a return from a callback. However, it seems it was opted to explicitly use the `endcallback` command for this purpose, instead.
As such, this bit serves no purpose but to make map scripts living in the higher banks of mappers such as Japanese Crystal's MBC30 crash for weird reasons.
**Fix:**
Remove the bit mask for the bank value in `ExitScriptSubroutine`:
```diff
ExitScriptSubroutine:
...
add hl, de
ld a, [hli]
ld b, a
- and $7f
ld [wScriptBank], a
ld a, [hli]
ld e, a
```
And in `CallCallback`:
```diff
CallCallback::
- ld a, [wScriptBank]
- or $80
- ld [wScriptBank], a
jp ScriptCall
```

563
docs/event_commands.md Normal file
View file

@ -0,0 +1,563 @@
# Event Commands
Defined in [macros/scripts/events.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/events.asm) and [engine/overworld/scripting.asm:ScriptCommandTable](https://github.com/pret/pokecrystal/blob/master/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>
## `$01`: <code>farscall <i>script</i></code>
## `$02`: <code>memcall <i>script</i></code>
## `$03`: <code>sjump <i>script</i></code>
## `$04`: <code>farsjump <i>script</i></code>
## `$05`: <code>memjump <i>script</i></code>
## `$06`: <code>ifequal <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>ifgreater <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>
## `$0D`: <code>callstd <i>std_script</i></code>
## `$0E`: <code>callasm <i>asm</i></code>
## `$0F`: <code>special <i>special_pointer</i></code>
## `$10`: <code>memcallasm <i>asm</i></code>
## `$11`: <code>checkmapscene <i>map</i></code>
## `$12`: <code>setmapscene <i>map</i>, <i>scene_id</i></code>
## `$13`: `checkscene`
## `$14`: <code>setscene <i>scene_id</i></code>
## `$15`: <code>setval <i>value</i></code>
<code>[wScriptVar] = <i>value</i></code>
## `$16`: <code>addval <i>value</i></code>
<code>[wScriptVar] += <i>value</i></code>
## `$17`: <code>random <i>value</i></code>
## `$18`: `checkver`
## `$19`: <code>readmem <i>address</i></code>
<code>[wScriptVar] = [<i>address</i>]</code>
## `$1A`: <code>writemem <i>address</i></code>
<code>[<i>address</i>] = [wScriptVar]</code>
## `$1B`: <code>loadmem <i>address</i>, <i>value</i></code>
<code>[<i>address</i>] = <i>value</i></code>
## `$1C`: <code>readvar <i>variable</i></code>
<code>[wScriptVar] = GetVarAction(<i>variable</i>)</code>
## `$1D`: <code>writevar <i>variable</i></code>
<code>GetVarAction(<i>variable</i>) = [wScriptVar]</code>
## `$1E`: <code>loadvar <i>variable</i>, <i>value</i></code>
<code>GetVarAction(<i>variable</i>) = <i>value</i></code>
## `$1F`: <code>giveitem <i>item_id</i>[, <i>quantity</i>=1]</code>
## `$20`: <code>takeitem <i>item_id</i>[, <i>quantity</i>=1]</code>
## `$21`: <code>checkitem <i>item_id</i></code>
## `$22`: <code>givemoney <i>account</i>, <i>value</i></code>
## `$23`: <code>takemoney <i>account</i>, <i>value</i></code>
## `$24`: <code>checkmoney <i>account</i>, <i>value</i></code>
## `$25`: <code>givecoins <i>value</i></code>
## `$26`: <code>takecoins <i>value</i></code>
## `$27`: <code>checkcoins <i>value</i></code>
## `$28`: <code>addcellnum <i>contact_id</i></code>
## `$29`: <code>delcellnum <i>contact_id</i></code>
## `$2A`: <code>checkcellnum <i>contact_id</i></code>
## `$2B`: <code>checktime <i>time</i></code>
## `$2C`: <code>checkpoke <i>mon_id</i></code>
## `$2D`: <code>givepoke <i>mon_id</i>, <i>level</i>[, <i>item</i>=0[, <i>ot_name</i>, <i>nickname</i>]]</code>
## `$2E`: <code>giveegg <i>mon_id</i>, <i>level</i></code>
## `$2F`: <code>givepokemail <i>pointer</i></code>
## `$30`: <code>checkpokemail <i>pointer</i></code>
## `$31`: <code>checkevent <i>event_flag</i></code>
## `$32`: <code>clearevent <i>event_flag</i></code>
## `$33`: <code>setevent <i>event_flag</i></code>
## `$34`: <code>checkflag <i>engine_flag</i></code>
## `$35`: <code>clearflag <i>engine_flag</i></code>
## `$36`: <code>setflag <i>engine_flag</i></code>
## `$37`: `wildon`
## `$38`: `wildoff`
## `$39`: <code>xycompare <i>pointer</i></code>
## `$3A`: <code>warpmod <i>warp_id</i>, <i>map</i></code>
## `$3B`: <code>blackoutmod <i>map</i></code>
## `$3C`: <code>warp <i>map</i>, <i>x</i>, <i>y</i></code>
## `$3D`: <code>getmoney <i>string_buffer</i>, <i>account</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = PrintNum(GetMoneyAccount(<i>account</i>))</code>
## `$3E`: <code>getcoins <i>string_buffer</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = PrintNum([wCoins])</code>
## `$3F`: <code>getnum <i>string_buffer</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = PrintNum([wScriptVar])</code>
## `$40`: <code>getmonname <i>string_buffer</i>, <i>mon_id</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = GetPokemonName(<i>mon_id</i>)</code>
If <code><i>mon_id</i></code> = `USE_SCRIPT_VAR`, then it uses `[wScriptVar]` instead.
## `$41`: <code>getitemname <i>string_buffer</i>, <i>item_id</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = GetItemName(<i>item_id</i>)</code>
If <code><i>item_id</i></code> = `USE_SCRIPT_VAR`, then it uses `[wScriptVar]` instead.
## `$42`: <code>getcurlandmarkname <i>string_buffer</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = GetLandmarkName(GetWorldMapLocation())</code>
## `$43`: <code>gettrainername <i>string_buffer</i>, <i>trainer_group</i>, <i>trainer_id</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = GetTrainerName(<i>trainer_group</i>, <i>trainer_id</i>)</code>
## `$44`: <code>getstring <i>string_buffer</i>, <i>text_pointer</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = CopyName1([wScriptBank], <i>text_pointer</i>)</code>
## `$45`: `itemnotify`
## `$46`: `pocketisfull`
## `$47`: `opentext`
## `$48`: <code>refreshscreen [<i>dummy</i>=0]</code>
## `$49`: `closetext`
## `$4A`: <code>writeunusedbyte <i>byte</i></code>
<code>[<i>wUnusedScriptByte</i>] = <i>byte</i></code>
## `$4B`: <code>farwritetext <i>text_pointer</i></code>
## `$4C`: <code>writetext <i>text_pointer</i></code>
## `$4D`: <code>repeattext <i>byte1</i>, <i>byte2</i></code>
## `$4E`: `yesorno`
## `$4F`: <code>loadmenu <i>menu_header</i></code>
## `$50`: `closewindow`
## `$51`: <code>jumptextfaceplayer <i>text_pointer</i></code>
## `$52`: <code>farjumptext <i>text_pointer</i></code>
## `$53`: <code>jumptext <i>text_pointer</i></code>
## `$54`: `waitbutton`
## `$55`: `promptbutton`
## `$56`: <code>pokepic <i>mon_id</i></code>
## `$57`: `closepokepic`
## `$58`: `_2dmenu`
## `$59`: `verticalmenu`
## `$5A`: `loadpikachudata`
## `$5B`: `randomwildmon`
## `$5C`: `loadtemptrainer`
`[wOtherTrainer] = [wTempTrainer]`
## `$5D`: <code>loadwildmon <i>mon_id</i>, <i>level</i></code>
## `$5E`: <code>loadtrainer <i>trainer_group</i>, <i>trainer_id</i></code>
## `$5F`: `startbattle`
## `$60`: `reloadmapafterbattle`
## `$61`: <code>catchtutorial <i>byte</i></code>
## `$62`: <code>trainertext <i>text_id</i></code>
## `$63`: <code>trainerflagaction <i>action</i></code>
## `$64`: <code>winlosstext <i>win_text_pointer</i>, <i>loss_text_pointer</i></code>
## `$65`: `scripttalkafter`
## `$66`: `endifjustbattled`
## `$67`: `checkjustbattled`
## `$68`: <code>setlasttalked <i>object_id</i></code>
## `$69`: <code>applymovement <i>object_id</i>, <i>data_pointer</i></code>
## `$6A`: <code>applymovementlasttalked <i>data_pointer</i></code>
## `$6B`: `faceplayer`
## `$6C`: <code>faceobject <i>object1</i>, <i>object2</i></code>
## `$6D`: <code>variablesprite <i>variable_sprite_id</i>, <i>sprite_id</i></code>
## `$6E`: <code>disappear <i>object_id</i></code>
## `$6F`: <code>appear <i>object_id</i></code>
## `$70`: <code>follow <i>object2</i>, <i>object1</i></code>
## `$71`: `stopfollow`
## `$72`: <code>moveobject <i>object_id</i>, <i>x</i>, <i>y</i></code>
## `$73`: <code>writeobjectxy <i>object_id</i></code>
## `$74`: <code>loademote <i>emote_id</i></code>
## `$75`: <code>showemote <i>emote_id</i>, <i>object_id</i>, <i>length</i></code>
## `$76`: <code>turnobject <i>object_id</i>, <i>facing</i></code>
## `$77`: <code>follownotexact <i>object2</i>, <i>object1</i></code>
## `$78`: <code>earthquake <i>param</i></code>
## `$79`: <code>changemapblocks <i>blockdata_pointer</i></code>
<code>ChangeMap(<i>blockdata_pointer</i>)</code>
## `$7A`: <code>changeblock <i>x</i>, <i>y</i>, <i>block</i></code>
## `$7B`: `reloadmap`
## `$7C`: `reloadmappart`
## `$7D`: <code>writecmdqueue <i>queue_pointer</i></code>
## `$7E`: <code>delcmdqueue <i>byte</i></code>
## `$7F`: <code>playmusic <i>music_id</i></code>
## `$80`: `encountermusic`
## `$81`: <code>musicfadeout <i>music_id</i>, <i>length</i></code>
## `$82`: `playmapmusic`
## `$83`: `dontrestartmapmusic`
## `$84`: <code>cry <i>mon_id</i></code>
## `$85`: <code>playsound <i>sfx_id</i></code>
## `$86`: `waitsfx`
## `$87`: `warpsound`
## `$88`: `specialsound`
## `$89`: <code>autoinput <i>input_pointer</i></code>
## `$8A`: <code>newloadmap <i>which_method</i></code>
## `$8B`: <code>pause <i>length</i></code>
## `$8C`: <code>deactivatefacing <i>length</i></code>
## `$8D`: <code>sdefer <i>script</i></code>
## `$8E`: `warpcheck`
## `$8F`: <code>stopandsjump <i>script</i></code>
## `$90`: `endcallback`
## `$91`: `end`
## `$92`: <code>reloadend <i>which_method</i></code>
## `$93`: `endall`
## `$94`: <code>pokemart <i>mart_type</i>, <i>mart_id</i></code>
## `$95`: <code>elevator <i>floor_list</i></code>
## `$96`: <code>trade <i>trade_id</i></code>
## `$97`: <code>askforphonenumber <i>contact_id</i></code>
## `$98`: <code>phonecall <i>call_id</i></code>
## `$99`: `hangup`
## `$9A`: <code>describedecoration <i>byte</i></code>
## `$9B`: <code>fruittree <i>tree_id</i></code>
## `$9C`: <code>specialphonecall <i>call_id</i></code>
## `$9D`: `checkphonecall`
## `$9E`: <code>verbosegiveitem <i>item_id</i>[, <i>quantity</i>=1]</code>
## `$9F`: <code>verbosegiveitemvar <i>item_id</i>, <i>variable</i></code>
## `$A0`: <code>swarm <i>swarm_id</i>, <i>map</i></code>
## `$A1`: `halloffame`
## `$A2`: `credits`
## `$A3`: <code>warpfacing <i>facing</i>, <i>map</i>, <i>x</i>, <i>y</i></code>
## `$A4`: <code>battletowertext <i>bttext_id</i></code>
## `$A5`: <code>getlandmarkname <i>string_buffer</i>, <i>landmark_id</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = GetLandmarkName(<i>landmark_id</i>)</code>
## `$A6`: <code>gettrainerclassname <i>string_buffer</i>, <i>trainer_group</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = GetName(TRAINER_NAME, <i>trainer_group</i>)</code>
## `$A7`: <code>getname <i>string_buffer</i>, <i>type</i>, <i>id</i></code>
<code>GetStringBuffer(<i>string_buffer</i>) = GetName(<i>type</i>, <i>id</i>)</code>
## `$A8`: <code>wait <i>duration</i></code>
## `$A9`: `checksave`

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

BIN
docs/images/port.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

30
docs/index.md Normal file
View file

@ -0,0 +1,30 @@
These pages are for documenting pieces of the [pokecrystal](https://github.com/pret/pokecrystal) disassembly project. For more information, please see its [README.md](https://github.com/pret/pokecrystal/blob/master/README.md) and [wiki](https://github.com/pret/pokecrystal/wiki).
## Issues with the source code
- [bugs_and_glitches.md](bugs_and_glitches.md)
- [design_flaws.md](design_flaws.md)
## Map event scripts
- [map_event_scripts.md](map_event_scripts.md)
- [event_commands.md](event_commands.md)
- [movement_commands.md](movement_commands.md)
- [text_commands.md](text_commands.md)
- [map_setup_scripts.md](map_setup_scripts.md)
## Other types of scripts
- [battle_anim_commands.md](battle_anim_commands.md)
- [move_effect_commands.md](move_effect_commands.md)
- [music_commands.md](music_commands.md)
- [vc_patch.md](vc_patch.md)
## Other subsystems
- [pic_animations.md](pic_animations.md)
- [menus.md](menus.md)

236
docs/map_event_scripts.md Normal file
View file

@ -0,0 +1,236 @@
# Map Event Scripts
## 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)
- [Template](#template)
## Object constants
<pre>
object_const_def
const <i>MAPNAME</i>_<i>OBJECTNAME</i>
</pre>
## Map scripts
<pre>
<i>MapName</i>_MapScripts:
</pre>
### Scene scripts
<pre>
def_scene_scripts
scene_script <i>script</i>, SCENE_<i>MAPNAME</i>_<i>SCENE_NAME</i>
</pre>
### Callbacks
<pre>
def_callbacks
callback <i>type</i>, <i>script</i>
</pre>
#### Callback types
- `MAPCALLBACK_NEWMAP`
- `MAPCALLBACK_TILES`
- `MAPCALLBACK_OBJECTS`
- `MAPCALLBACK_SPRITES`
- `MAPCALLBACK_CMDQUEUE`
<pre>
callback MAPCALLBACK_CMDQUEUE, <i>MapName</i>BouldersCallback
<i>MapName</i>BouldersCallback:
writecmdqueue .BoulderCmdQueue
endcallback
.BoulderCmdQueue:
cmdqueue CMDQUEUE_STONETABLE, .BoulderTable
.BoulderTable:
stonetable <i>warp_id</i>, <i>person</i>, <i>script</i>
db -1 ; end
</pre>
## Event scripts
[Event commands](event_commands.md)
## Text
[Text commands](text_commands.md)
## Movement data
[Movement commands](movement_commands.md)
## Map events
<pre>
<i>MapName</i>_MapEvents:
db 0, 0 ; filler
</pre>
### Warp events
<pre>
def_warp_events
warp_event <i>x</i>, <i>y</i>, <i>map</i>, <i>warp_id</i>
</pre>
### Coord events
<pre>
def_coord_events
coord_event <i>x</i>, <i>y</i>, <i>scene_id</i>, <i>script</i>
</pre>
### BG events
<pre>
def_bg_events
bg_event <i>x</i>, <i>y</i>, <i>type</i>, <i>script</i>
</pre>
#### BG event types
- `BGEVENT_READ`
- `BGEVENT_UP/DOWN/LEFT/RIGHT`
- `BGEVENT_IFSET/IFNOTSET`
<pre>
conditional_event <i>event_flag</i>, <i>script</i>
</pre>
- `BGEVENT_ITEM`
<pre>
hiddenitem <i>item_id</i>, <i>event_flag</i>
</pre>
- `BGEVENT_COPY`
### Object events
<pre>
def_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
- `SPRITEMOVEDATA_STILL`
- `SPRITEMOVEDATA_WANDER`
- `SPRITEMOVEDATA_SPINRANDOM_SLOW`
- `SPRITEMOVEDATA_WALK_UP_DOWN`
- `SPRITEMOVEDATA_WALK_LEFT_RIGHT`
- `SPRITEMOVEDATA_STANDING_UP/DOWN/LEFT/RIGHT`
- `SPRITEMOVEDATA_SPINRANDOM_FAST`
- `SPRITEMOVEDATA_BIGDOLLSYM`
- `SPRITEMOVEDATA_POKEMON`
- `SPRITEMOVEDATA_SUDOWOODO`
- `SPRITEMOVEDATA_SMASHABLE_ROCK`
- `SPRITEMOVEDATA_STRENGTH_BOULDER`
- `SPRITEMOVEDATA_SPINCOUNTERCLOCKWISE`
- `SPRITEMOVEDATA_SPINCLOCKWISE`
- `SPRITEMOVEDATA_BIGDOLLASYM`
- `SPRITEMOVEDATA_BIGDOLL`
- `SPRITEMOVEDATA_SWIM_WANDER`
#### Object types
- `OBJECTTYPE_SCRIPT`
- `OBJECTTYPE_ITEMBALL`
<pre>
itemball <i>item_id</i>[, <i>quantity</i>=1]
</pre>
- `OBJECTTYPE_TRAINER`
<pre>
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>
## Template
<pre>
object_const_def
; const <i>MAPNAME</i>_<i>OBJECTNAME</i>
<i>MapName</i>_MapScripts:
def_scene_scripts
; scene_script <i>script</i>, SCENE_<i>MAPNAME</i>_<i>SCENE_NAME</i>
def_callbacks
; callback <i>type</i>, <i>script</i>
<i>MapName</i>_MapEvents:
db 0, 0 ; filler
def_warp_events
; warp_event <i>x</i>, <i>y</i>, <i>map</i>, <i>warp_id</i>
def_coord_events
; coord_event <i>x</i>, <i>y</i>, <i>scene_id</i>, <i>script</i>
def_bg_events
; bg_event <i>x</i>, <i>y</i>, <i>type</i>, <i>script</i>
def_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>

144
docs/map_setup_scripts.md Normal file
View file

@ -0,0 +1,144 @@
# Map Setup Commands
Defined in [macros/scripts/map_setup.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/map_setup.asm) and [data/maps/setup_script_pointers.asm:MapSetupCommands](https://github.com/pret/pokecrystal/blob/master/data/maps/setup_script_pointers.asm).
## `$00`: `map_enable_lcd`
## `$01`: `map_disable_lcd`
## `$02`: `map_init_sound`
## `$03`: `map_play_music`
## `$04`: `map_restart_music`
## `$05`: `map_fade_to_music`
## `$06`: `map_fade_music_and_palettes`
## `$07`: `map_play_music_bike`
## `$08`: `map_force_music`
## `$09`: `map_fade_in_to_music`
## `$0A`: `map_load_block_data`
## `$0B`: `map_load_connection_block_data`
## `$0C`: `map_save_screen`
## `$0D`: `map_buffer_screen`
## `$0E`: `map_load_graphics`
## `$0F`: `map_load_tileset`
## `$10`: `map_load_time_of_day`
## `$11`: `map_load_palettes`
## `$12`: `map_load_wild_mon_data`
## `$13`: `map_refresh_sprites`
## `$14`: `map_handle_new`
## `$15`: `map_handle_continue`
## `$16`: `map_load_objects`
## `$17`: `map_enter_spawn_point`
## `$18`: `map_enter_connection`
## `$19`: `map_enter_warp`
## `$1A`: `map_load_attributes`
## `$1B`: `map_load_attributes_no_objects`
## `$1C`: `map_clear_bg_palettes`
## `$1D`: `map_fade_out_palettes`
## `$1E`: `map_fade_in_palettes`
## `$1F`: `map_get_screen_coords`
## `$20`: `map_get_warp_dest_coords`
## `$21`: `map_spawn_in_facing_down`
## `$22`: `map_spawn_player`
## `$23`: `map_refresh_player_coords`
## `$24`: `map_reset_player_object_action`
## `$25`: `map_skip_update_sprites`
## `$26`: `map_update_roam_mons`
## `$27`: `map_jump_roam_mons`
## `$28`: `map_fade_out_music`
## `$29`: `map_activate_anims`
## `$2A`: `map_suspend_anims`
## `$2B`: `map_apply_palettes`
## `$2C`: `map_enable_text_acceleration`
## `$2D`: `map_init_name_sign`
## `$FF`: `map_end`

297
docs/menus.md Normal file
View file

@ -0,0 +1,297 @@
# Menu Data
## Contents
- [ScrollingMenu](#scrollingmenu)
- [\_2DMenu](#_2dmenu)
- [DoNthMenu/SetUpMenu](#donthmenusetupmenu)
- [VerticalMenu](#verticalmenu)
- [Misc/Generic](#miscgeneric)
## `ScrollingMenu`
This is the only menu that does scrolling. It doesn't draw any `Textbox` around the menu.
Structure:
```asm
.MenuHeader:
db MENU_BACKUP_TILES ; flags
menu_coords 2, 4, SCREEN_WIDTH - 1, 13
dw .MenuData
db 1 ; default option
.MenuData:
db 0 ; flags
db 5, 0 ; rows, columns
db SCROLLINGMENU_ITEMS_NORMAL ; item format
dba Items
dba Function1
dba Function2
dba Function3
```
`wMenuDataFlags`:
```
7: Select is functional
6: Start is functional
5: Call Function3
4: Show arrows on the right-hand side
3: D-Left is functional
2: D-Right is functional
1: Call Function3 only if [wSwitchItem] is 0
0: Call Function1 to display the cancel entry
```
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 `MenuData` of `ScrollingMenu`:
```
All of them:
[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
Function2: Called to display the quantity of a menu entry.
de = Cursor position in TileMap + columns
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 `; item format` entry in each `MenuData` changes how the `Items` struct looks.
If it's `SCROLLINGMENU_ITEMS_NORMAL` (1):
```
db entries not including cancel
db entry1
db entry2
db -1 ; cancel
...
```
If it's `SCROLLINGMENU_ITEMS_QUANTITY` (2):
```
db entries not including cancel
db entry1, quantity1
db entry2, quantity2
db -1 ; cancel
...
```
In case it's 1, `[wMenuSelectionQuantity]` will simply contain the next entry.
## `_2DMenu`
This, like is implied by the name, is a 2-dimensional menu, where you can move your cursor in all 4 directions. It's only used for the battle menus as well as Earl's academy.
Structure:
```asm
.MenuHeader:
db MENU_BACKUP_TILES ; flags
db 12, 08 ; start coords
db 17, 19 ; end coords
dw .MenuData
db 1 ; default option
.MenuData:
db STATICMENU_CURSOR ; flags
dn 2, 2 ; rows, columns
db 6 ; spacing
dba Strings
dba Function
```
`wMenuDataFlags`:
```
7: Leave one tile of spacing between the left textbox border and the text, enabling the cursor.
6: Don't leave one tile of spacing between the top textbox border and the text
5: Set bits 4 and 5 in w2DMenuFlags1 (Wrap around horizontally and vertically)
4: Unused
3: Unused
2: Unused
1: Select is functional
0: Disable B button
```
The bank for the `Strings` is generated when you call `_2DMenu`, and as such it doesn't really matter what bank you specify there (unless you callba `_2DMenu_` directly, of course).
`; spacing` is not a misnomer here, it's used to define how much space there is between columns.
`Function` is called after printing all the strings. `hl` will be pointed at the row below the last in the menu in `TileMap`. We don't know of its purpose, since it's never actually used anywhere. Only the bank number is always set to the same bank as the menu, but not used otherwise, since the address is 0.
## `DoNthMenu`/`SetUpMenu`
These are like the regular `VerticalMenu`, except they allow for creating slightly more "dynamic" menus, where the options aren't predefined, much like the `ScrollingMenu`.
Structure:
```
.MenuHeader:
db MENU_BACKUP_TILES ; flags
menu_coords 0, 0, 10, 7
dw .MenuData
db 1 ; default option
.MenuData:
db STATICMENU_CURSOR | STATICMENU_DISABLE_B ; flags
db 0 ; items
dw Items
dw DisplayFunction
dw StringPointers
```
`wMenuDataFlags`:
```
7: Unused
6: Unused
5: Set bit 5 in w2DMenuFlags1 (Wrap around vertically)
4: Unused
3: Start is functional
2: D-left and D-right are functional
1: Unused
0: Unused
```
The `; items` entry should be left empty, as it's autogenerated with the `Items` array in `GetMenuIndexSet`.
The bottom coord (`07` in the example) is autogenerated regardless of what you specify when building the MenuBox in `AutomaticGetMenuBottomCoord`, which also calculates the `bc` passed to MenuBox, which is useless because it's calculated again by MenuBox in `GetMenuBoxDims`.
`[wWhichIndexSet]` decides which menu is used through `GetMenuIndexSet`. You can define multiple menus at the Items pointer as such:
```
Items:
db entries not including cancel
db entry1, entry2, entry3
db -1 ; cancel
db entries not including cancel for 2nd menu
db entry1, entry2, entry3, entry4
db -1 ; cancel
```
This is actively used in `MainMenu`.
`StringPointers` isn't handled by `DoNthMenu` internally. It's handled by different `DisplayFunction`s. A custom one could choose to completely ignore it.
`StringPointers` struct handled through `PlaceNthMenuStrings` as `DisplayFunction` and `MenuJumptable`:
```asm
StringPointers:
dw FunctionToCall, PointerToString ; index 1
dw FunctionToCall, PointerToString ; index 2
...
```
`StringPointers` struct handled through `PlaceMenuStrings`:
```asm
StringPointers:
db "STRING1@"
db "STRING2@"
...
```
Call state for `DisplayFunction`:
```
[wMenuSelection] = Current item. -1 is the CANCEL item.
de = Cursor position in TileMap
```
## `VerticalMenu`
This is the simplest menu. Like, the most boring. Nothing special. Just normal. …nooooooormal…
Structure:
```asm
.MenuHeader:
db MENU_SPRITE_ANIMS | MENU_BACKUP_TILES ; flags
menu_coords 12, 12, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1
dw .MenuData
db 1 ; default option
.MenuData:
db STATICMENU_CURSOR ; flags
db 2 ; # items
db "GIVE@"
db "TAKE@"
```
`wMenuDataFlags`:
```
7: Leave one tile of spacing between the left textbox border and the text
6: Don't leave one tile of spacing between the top textbox border and the text
5: Set bit 5 in w2DMenuFlags1 (Wrap around vertically)
4: Place menubox "title". See notes.
3: Unused
2: Unused
1: Select is functional
0: Disable B button
```
If bit 4 is set, a string at the end of the items array will be put at an offset from the start coord of the menu box. This string is defined like this:
```asm
db 2 ; # items
db "GIVE@"
db "TAKE@"
db 2 ; x offset
db "TEST@"
```
This is used in the menu for selecting the character's name.
## Misc/Generic
`MenuHeader` flags (`wMenuFlags`):
```
7: Save a backup of the tiles
6: Save a backup of the tiles
5: Unused
4: Set bit 6 in w2DMenuFlags1 (Enable sprite animations)
3: Disable click sound
2: Unused
1: Unused
0: Call RestoreTileBackup when exiting the menu. This bit depends on whether bit 6 or 7 are set.
```
`w2DMenuFlags1`:
```
7: Disable checking of wMenuJoypadFilter
6: Enable sprite animations
5: Wrap around vertically
4: Wrap around horizontally
3: Set bit 7 in w2DMenuFlags2 and exit the loop if bit 5 is disabled and we tried to go too far down
2: Set bit 7 in w2DMenuFlags2 and exit the loop if bit 5 is disabled and we tried to go too far up
1: Set bit 7 in w2DMenuFlags2 and exit the loop if bit 4 is disabled and we tried to go too far left
0: Set bit 7 in w2DMenuFlags2 and exit the loop if bit 4 is disabled and we tried to go too far right
```
`w2DMenuFlags2`:
```
7: ?????
6: ?????
5: ?????
4: ?????
3: ?????
2: ?????
1: ?????
0: ?????
```

View file

@ -0,0 +1,534 @@
# Move Effect Commands
Defined in [macros/scripts/battle_commands.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/battle_commands.asm) and [data/battle/effect_command_pointers.asm:BattleCommandPointers](https://github.com/pret/pokecrystal/blob/master/data/battle/effect_command_pointers.asm).
## `$01`: `checkturn`
## `$02`: `checkobedience`
## `$03`: `usedmovetext`
## `$04`: `doturn`
## `$05`: `critical`
## `$06`: `damagestats`
## `$07`: `stab`
## `$08`: `damagevariation`
## `$09`: `checkhit`
## `$0A`: `lowersub`
## `$0B`: `hittargetnosub`
## `$0C`: `raisesub`
## `$0D`: `failuretext`
## `$0E`: `applydamage`
## `$0F`: `criticaltext`
## `$10`: `supereffectivetext`
## `$11`: `checkfaint`
## `$12`: `buildopponentrage`
## `$13`: `poisontarget`
## `$14`: `sleeptarget`
## `$15`: `draintarget`
## `$16`: `eatdream`
## `$17`: `burntarget`
## `$18`: `freezetarget`
## `$19`: `paralyzetarget`
## `$1A`: `selfdestruct`
## `$1B`: `mirrormove`
## `$1C`: `statup`
## `$1D`: `statdown`
## `$1E`: `payday`
## `$1F`: `conversion`
## `$20`: `resetstats`
## `$21`: `storeenergy`
## `$22`: `unleashenergy`
## `$23`: `forceswitch`
## `$24`: `endloop`
## `$25`: `flinchtarget`
## `$26`: `ohko`
## `$27`: `recoil`
## `$28`: `mist`
## `$29`: `focusenergy`
## `$2A`: `confuse`
## `$2B`: `confusetarget`
## `$2C`: `heal`
## `$2D`: `transform`
## `$2E`: `screen`
## `$2F`: `poison`
## `$30`: `paralyze`
## `$31`: `substitute`
## `$32`: `rechargenextturn`
## `$33`: `mimic`
## `$34`: `metronome`
## `$35`: `leechseed`
## `$36`: `splash`
## `$37`: `disable`
## `$38`: `cleartext`
## `$39`: `charge`
## `$3A`: `checkcharge`
## `$3B`: `traptarget`
## `$3C`: `effect0x3c`
## `$3D`: `rampage`
## `$3E`: `checkrampage`
## `$3F`: `constantdamage`
## `$40`: `counter`
## `$41`: `encore`
## `$42`: `painsplit`
## `$43`: `snore`
## `$44`: `conversion2`
## `$45`: `lockon`
## `$46`: `sketch`
## `$47`: `defrostopponent`
## `$48`: `sleeptalk`
## `$49`: `destinybond`
## `$4A`: `spite`
## `$4B`: `falseswipe`
## `$4C`: `healbell`
## `$4D`: `kingsrock`
## `$4E`: `triplekick`
## `$4F`: `kickcounter`
## `$50`: `thief`
## `$51`: `arenatrap`
## `$52`: `nightmare`
## `$53`: `defrost`
## `$54`: `curse`
## `$55`: `protect`
## `$56`: `spikes`
## `$57`: `foresight`
## `$58`: `perishsong`
## `$59`: `startsandstorm`
## `$5A`: `endure`
## `$5B`: `checkcurl`
## `$5C`: `rolloutpower`
## `$5D`: `effect0x5d`
## `$5E`: `furycutter`
## `$5F`: `attract`
## `$60`: `happinesspower`
## `$61`: `present`
## `$62`: `damagecalc`
## `$63`: `frustrationpower`
## `$64`: `safeguard`
## `$65`: `checksafeguard`
## `$66`: `getmagnitude`
## `$67`: `batonpass`
## `$68`: `pursuit`
## `$69`: `clearhazards`
## `$6A`: `healmorn`
## `$6B`: `healday`
## `$6C`: `healnite`
## `$6D`: `hiddenpower`
## `$6E`: `startrain`
## `$6F`: `startsun`
## `$70`: `attackup`
## `$71`: `defenseup`
## `$72`: `speedup`
## `$73`: `specialattackup`
## `$74`: `specialdefenseup`
## `$75`: `accuracyup`
## `$76`: `evasionup`
## `$77`: `attackup2`
## `$78`: `defenseup2`
## `$79`: `speedup2`
## `$7A`: `specialattackup2`
## `$7B`: `specialdefenseup2`
## `$7C`: `accuracyup2`
## `$7D`: `evasionup2`
## `$7E`: `attackdown`
## `$7F`: `defensedown`
## `$80`: `speeddown`
## `$81`: `specialattackdown`
## `$82`: `specialdefensedown`
## `$83`: `accuracydown`
## `$84`: `evasiondown`
## `$85`: `attackdown2`
## `$86`: `defensedown2`
## `$87`: `speeddown2`
## `$88`: `specialattackdown2`
## `$89`: `specialdefensedown2`
## `$8A`: `accuracydown2`
## `$8B`: `evasiondown2`
## `$8C`: `statupmessage`
## `$8D`: `statdownmessage`
## `$8E`: `statupfailtext`
## `$8F`: `statdownfailtext`
## `$90`: `effectchance`
## `$91`: `statdownanim`
## `$92`: `statupanim`
## `$93`: `switchturn`
## `$94`: `fakeout`
## `$95`: `bellydrum`
## `$96`: `psychup`
## `$97`: `rage`
## `$98`: `doubleflyingdamage`
## `$99`: `doubleundergrounddamage`
## `$9A`: `mirrorcoat`
## `$9B`: `checkfuturesight`
## `$9C`: `futuresight`
## `$9D`: `doubleminimizedamage`
## `$9E`: `skipsuncharge`
## `$9F`: `thunderaccuracy`
## `$A0`: `teleport`
## `$A1`: `beatup`
## `$A2`: `ragedamage`
## `$A3`: `resettypematchup`
## `$A4`: `allstatsup`
## `$A5`: `bidefailtext`
## `$A6`: `raisesubnoanim`
## `$A7`: `lowersubnoanim`
## `$A8`: `beatupfailtext`
## `$A9`: `clearmissdamage`
## `$AA`: `movedelay`
## `$AB`: `hittarget`
## `$AC`: `tristatuschance`
## `$AD`: `supereffectivelooptext`
## `$AE`: `startloop`
## `$AF`: `curl`
## `$FE`: `endturn`
## `$FF`: `endmove`

123
docs/movement_commands.md Normal file
View file

@ -0,0 +1,123 @@
# Movement Commands
Defined in [macros/scripts/movement.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/movement.asm) and [engine/overworld/movement.asm:MovementPointers](https://github.com/pret/pokecrystal/blob/master/engine/overworld/movement.asm).
## `$00``$03`: <code>turn_head <i>direction</i></code>
## `$04``$07`: <code>turn_step <i>direction</i></code>
## `$08``$0B`: <code>slow_step <i>direction</i></code>
## `$0C``$0F`: <code>step <i>direction</i></code>
## `$10``$13`: <code>big_step <i>direction</i></code>
## `$14``$17`: <code>slow_slide_step <i>direction</i></code>
## `$18``$1B`: <code>slide_step <i>direction</i></code>
## `$1C``$1F`: <code>fast_slide_step <i>direction</i></code>
## `$20``$23`: <code>turn_away <i>direction</i></code>
## `$24``$27`: <code>turn_in <i>direction</i></code>
## `$28``$2B`: <code>turn_waterfall <i>direction</i></code>
## `$2C``$2F`: <code>slow_jump_step <i>direction</i></code>
## `$30``$33`: <code>jump_step <i>direction</i></code>
## `$34``$37`: <code>fast_jump_step <i>direction</i></code>
## `$38`: `remove_sliding`
## `$39`: `set_sliding`
## `$3A`: `remove_fixed_facing`
## `$3B`: `fix_facing`
## `$3C`: `show_object`
## `$3D`: `hide_object`
## `$3E``$46`: <code>step_sleep <i>length</i></code>
## `$47`: `step_end`
## `$48`: <code>step_48 <i>param</i></code>
## `$49`: `remove_object`
## `$4A`: `step_loop`
## `$4B`: `step_4b`
## `$4C`: `teleport_from`
## `$4D`: `teleport_to`
## `$4E`: `skyfall`
## `$4F`: <code>step_dig <i>length</i></code>
## `$50`: `step_bump`
## `$51`: `fish_got_bite`
## `$52`: `fish_cast_rod`
## `$53`: `hide_emote`
## `$54`: `show_emote`
## `$55`: <code>step_shake <i>displacement</i></code>
## `$56`: `tree_shake`
## `$57`: <code>rock_smash <i>length</i></code>
## `$58`: <code>return_dig <i>length</i></code>
## `$59`: `skyfall_top`

290
docs/music_commands.md Normal file
View file

@ -0,0 +1,290 @@
# Music Commands
Defined in [macros/scripts/audio.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/audio.asm) and [audio/engine.asm:MusicCommands](https://github.com/pret/pokecrystal/blob/master/audio/engine.asm).
Note: Commands that are intended for the song channels (1-4) can be used by the sound effect channels (5-8) if the sound effect channel exits sound effect mode with the `toggle_sfx` command.
## <code>channel_count <i>n</i></code>
Used at the start of each sound header to specify how many channels are used in the sound.
`n`: Number of channels [`1`, `4`]
## <code>channel <i>index</i>, <i>address</i></code>
Used for each channel in a sound header.
`index`: Channel number [`1`, `8`]
`address`: Pointer to the sound data
## <code>note <i>pitch</i>, <i>length</i></code>
Play a basic note. Used by channels 1-3.
`pitch`: Pitch of note (see [constants/audio_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/audio_constants.asm))
`length`: Length of note in "ticks" [`1`, `16`]. The exact duration of a tick is dependant on the current "speed" (see `note_type` and `drum_speed`) and the current "tempo" (see `tempo`).
## <code>drum_note <i>instrument</i>, <i>length</i></code>
Play a predefined drum note. Used by channel 4.
`instrument`: Instrument ID [`1`, `12`] (see `toggle_noise`)
`length`: Length of note [`1`, `16`]
## <code>rest <i>length</i></code>
Basic rest. Used by channels 1-4.
`length`: Length of rest [`1`, `16`]
## <code>square_note <i>length</i>, <i>volume</i>, <i>fade</i>, <i>frequency</i></code>
Sound effect square note. Used by channels 5-7.
`length`: Length of note [`0`, `255`]
`volume`: Initial volume [`0`, `15`]
`fade`: Volume fade [`-7`, `7`]
`frequency`: Note frequency [`0`, `65535`]
## <code>noise_note <i>length</i>, <i>volume</i>, <i>fade</i>, <i>frequency</i></code>
Sound effect noise note. Used by channel 8.
`length`: Length of note [`0`, `255`]
`volume`: Initial volume [`0`, `15`]
`fade`: Volume fade [`-7`, `7`]
`frequency`: Note frequency [`0`, `255`]
## `$D0``$D7`: <code>octave <i>n</i></code>
Set the octave for the notes played on the current channel. Used by channels 1-3.
`n`: New octave [`1`, `8`]
## `$D8`: <code>note_type <i>length</i>, <i>volume</i>, <i>fade/wave_instrument</i></code>
Set persistent note properties. Used by channels 1-3.
`length`: Base note length [`1`, `15`] (`12` is often used for 4/4 common time because `12` is factorable by both `3` and `4`. Therefore it works very well for quarter notes, eighth notes, sixteenth notes, and triplets.)
`volume`: Initial volume [`0`, `15`] for channels 1-2, [`0`, `3`] for channel 3 (see `volume_envelope`)
`fade`: Volume fade [`-7`, `7`] (applies to channels 1-2)
`wave_instrument`: Wave instrument ID (applies to channel 3) (see [audio/wave_samples.asm](https://github.com/pret/pokecrystal/blob/master/audio/wave_samples.asm))
## `$D8`: <code>drum_speed <i>length</i></code>
Set persistent note properties. Used by channel 4.
`length`: Base note length [`1`, `15`] (use `12` for common time)
## `$D9`: <code>transpose <i>num_octaves</i>, <i>num_pitches</i></code>
Transpose all notes played on the current channel. Used by channels 1-3.
`num_octaves`: Number of octaves to subtract from each note
`num_pitches`: Number of pitches to add to each note
## `$DA`: <code>tempo <i>tempo</i></code>
Set the tempo for all playing channels. This should only be used by channel 1.
The formula to convert from this tempo to BPM is: BPM = 19200 / `tempo`
This formula also works backwards to convert BPM to tempo: `tempo` = 19200 / BPM
Only set or change this value when all playing channels are triggering a note or rest at the same time, otherwise desyncs may happen.
## `$DB`: <code>duty_cycle <i>duty_cycle</i></code>
Set the square duty (sound) for the current channel. Used by channels 1-2.
The only accepted values are 0-3.
- 0 = 12.5% waveform: `_______¯`
- 1 = 25% waveform: `______¯¯`
- 2 = 50% waveform: `____¯¯¯¯`
- 3 = 75% waveform: `__¯¯¯¯¯¯` (sounds the same as 25%)
To change the sound for channel 3, use `note_type` or `volume_envelope`.
## `$DC`: <code>volume_envelope <i>volume</i>, <i>fade/wave_instrument</i></code>
Set the volume envelope for the current channel. Used by channels 1-3.
`volume`: Initial volume [`0`, `15`] for channels 1-2, [`0`, `3`] for channel 3
`fade`: Volume fade [`-7`, `7`] (applies to channels 1-2)
`wave_instrument`: Wave instrument ID (applies to channel 3) (see [audio/wave_samples.asm](https://github.com/pret/pokecrystal/blob/master/audio/wave_samples.asm))
For channel 3, the only accepted `volume` values are 0-3.
- 0 = Mute
- 1 = 100% volume
- 2 = 50% volume
- 3 = 25% volume
Note about `fade`: A positive value means a decrease in volume; a negative value means an increase in volume. A small magnitude means a quick change; a large magnitude means a slow change. It is stored in signed magnitude representation, so a value of `8` is the same as (negative) `0`.
## `$DD`: <code>pitch_sweep <i>length</i>, <i>pitch_change</i></code>
Set pitch sweep properties. Used by channel 5.
`length`: Duration of effect [`0`, `15`]
`pitch_change`: Extent of effect [`-7`, `8`] Note: `8` is used in place of `0`
## `$DE`: <code>duty_cycle_pattern <i>a</i>, <i>b</i>, <i>c</i>, <i>d</i></code>
Set duty cycle pattern (ie, pulse width modulation). Used by channels 5-6.
This cycles the channel through 4 duty cycles, one per frame.
Each argument defines a duty cycle, same as the `duty_cycle` command.
## `$DF`: <code>toggle_sfx</code>
Toggle between pitch-based songs and frequency-based sound effects. Can be used by any channel.
Note: Similar to the pokered command `execute_music`, however `execute_music` can only be used on channels 5-8 and can not be disabled for the duration of the sound once it is enabled.
## `$E0`: <code>pitch_slide <i>duration</i>, <i>octave</i>, <i>pitch</i></code>
Bend the pitch of the next note played, and only that note. Used by channel 1.
`duration`: Duration of the target note after slide effect
`octave`: Target octave
`pitch`: Target pitch (see [constants/audio_constants.asm](https://github.com/pret/pokecrystal/blob/master/constants/audio_constants.asm))
## `$E1`: <code>vibrato <i>delay</i>, <i>extent</i>, <i>rate</i></code>
Apply vibrato to current channel. Used by channels 1-3.
`delay`: Delay until vibrato effect begins for each note [`0`, `255`]
`extent`: Amplitude of vibrato [`0`, `15`]
`rate`: Frequency of vibrato [`0`, `15`]
## `$E2`: <code>unknownmusic0xe2 <i>unknown</i></code>
## `$E3`: <code>toggle_noise <i>id</i></code>
Set the "drum kit" to be used if it is currently unset. Mute the channel otherwise. Used by channel 4.
`id`: Drum kit ID [`0`, `5`] (see [audio/drumkits.asm](https://github.com/pret/pokecrystal/blob/master/audio/drumkits.asm))
Note: The drum kit ID is initially unset at the start of a song. When muting the channel, the `id` argument must not be present.
## `$E4`: <code>force_stereo_panning <i>left_enable</i>, <i>right_enable</i></code>
Set left/right stereo output for the current channel, regardless of user's stereo setting. Used by channels 1-4.
`left_enable`: `TRUE`/`FALSE`
`right_enable`: `TRUE`/`FALSE`
## `$E5`: <code>volume <i>left_volume</i>, <i>right_volume</i></code>
Set master volume for left/right speakers. Typically only used by channel 1.
`left_volume`: Left speaker volume [`0`, `7`]
`right_volume`: Right speaker volume [`0`, `7`]
Note: Minimum volume, `0`, is not muted.
## `$E6`: <code>pitch_offset <i>pitch_offset</i></code>
Adjust the pitch of all notes on the current channel. Used by channels 1-3.
`pitch_offset`: Frequency adjustment of each pitch
Note: Similar to the pokered command `toggle_perfect_pitch`. `toggle_perfect_pitch` can be replaced with a combination of `pitch_offset 1` and `pitch_offset 0`.
## `$E7`: <code>unknownmusic0xe7 <i>unknown</i></code>
## `$E8`: <code>unknownmusic0xe8 <i>unknown</i></code>
## `$E9`: <code>tempo_relative <i>value</i></code>
## `$EA`: <code>restart_channel <i>address</i></code>
## `$EB`: <code>new_song <i>id</i></code>
## `$EC`: <code>sfx_priority_on</code>
## `$ED`: <code>sfx_priority_off</code>
## `$EE`: <code>unknownmusic0xee <i>address</i></code>
## `$EF`: <code>stereo_panning <i>left_enable</i>, <i>right_enable</i></code>
Set left/right stereo output for the current channel, if the user has stereo mode enabled. Used by channels 1-4.
`left_enable`: `TRUE`/`FALSE`
`right_enable`: `TRUE`/`FALSE`
## `$F0`: <code>sfx_toggle_noise <i>id</i></code>
Set the "drum kit" to be used if it is currently unset. Mute the channel otherwise. Used by channel 8.
`id`: Drum kit ID [`0`, `5`] (see [audio/drumkits.asm](https://github.com/pret/pokecrystal/blob/master/audio/drumkits.asm))
Note: The drum kit ID is initially unset at the start of a song. When muting the channel, the `id` argument must not be present.
## `$F1`: <code>music0xf1</code>
## `$F2`: <code>music0xf2</code>
## `$F3`: <code>music0xf3</code>
## `$F4`: <code>music0xf4</code>
## `$F5`: <code>music0xf5</code>
## `$F6`: <code>music0xf6</code>
## `$F7`: <code>music0xf7</code>
## `$F8`: <code>music0xf8</code>
## `$F9`: <code>unknownmusic0xf9</code>
## `$FA`: <code>set_condition <i>condition</i></code>
## `$FB`: <code>sound_jump_if <i>condition</i>, <i>address</i></code>
## `$FC`: <code>sound_jump <i>address</i></code>
## `$FD`: <code>sound_loop <i>count</i>, <i>address</i></code>
Execute a branch of sound commands a total of `count` times.
`count`: Number of times to execute the loop (including the first execution) (use `0` for an infinite loop)
`address`: Pointer to the start of the loop of sound commands
## `$FE`: <code>sound_call <i>address</i></code>
Execute a branch of sound commands, returning to the call point once a `sound_ret` command is reached.
`address`: Pointer to the branch of sound commands to call
## `$FF`: <code>sound_ret</code>
Return to the caller (ie, `sound_call`) if in a sub branch. End the sound otherwise.

30
docs/pic_animations.md Normal file
View file

@ -0,0 +1,30 @@
# Pic Animations
Defined in [macros/scripts/pic_anims.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/pic_anims.asm).
Pic animations are assembled in 3 parts:
- Top-level animations:
- <code>frame <i>N</i>, <i>duration</i></code>: Frame #0 is the original pic (no change)
- <code>setrepeat <i>N</i></code>: Sets the number of times to repeat
- <code>dorepeat <i>I</i></code>: Repeats from command #<i>I</i> (with the first command being #0)
- `endanim`
- Bitmasks:
Layered over the pic to designate affected tiles
- Frame definitions:
first byte is the bitmask used for this frame
following bytes are tile ids mapped to each bit in the mask
Animation data is in these files:
- [gfx/pokemon/anims.asm](https://github.com/pret/pokecrystal/blob/master/gfx/pokemon/anims.asm):
Main animations (played everywhere)
- [gfx/pokemon/idles.asm](https://github.com/pret/pokecrystal/blob/master/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](https://github.com/pret/pokecrystal/blob/master/gfx/pokemon/unown_anims.asm) and [gfx/pokemon/unown_idles.asm](https://github.com/pret/pokecrystal/blob/master/gfx/pokemon/unown_idles.asm):
Unown has its own animation data despite having an entry in the main tables.

179
docs/text_commands.md Normal file
View file

@ -0,0 +1,179 @@
# Text Commands
Defined in [macros/scripts/text.asm](https://github.com/pret/pokecrystal/blob/master/macros/scripts/text.asm) and [home/text.asm:TextCommands](https://github.com/pret/pokecrystal/blob/master/home/text.asm).
## `$00`: `text_start`
Start writing text until `"@"`. The text can use [control characters](#control-characters).
## `$01`: <code>text_ram <i>address</i></code>
Write text from a RAM address.
## `$02`: <code>text_bcd <i>address</i>, <i>flags</i></code>
Write [BCD][bcd] from an address, typically RAM.
[bcd]: https://en.wikipedia.org/wiki/Binary-coded_decimal
## `$03`: <code>text_move <i>address</i></code>
Move to a new tile.
## `$04`: <code>text_box <i>address</i>, <i>height</i>, <i>width</i></code>
Draw a box.
## `$05`: `text_low`
Write text at (1, 16).
## `$06`: `text_promptbutton`
Wait for button press; show arrow.
## `$07`: `text_scroll`
Pushes text up two lines and sets the `bc` cursor to the border tile below the
first character column of the text box.
## `$08`: `text_asm`
Start interpreting assembly code.
## `$09`: <code>text_decimal <i>address</i>, <i>bytes</i>, <i>digits</i></code>
Read *bytes* bytes from *address* and print them as a *digits*-digit number.
## `$0A`: `text_pause`
Pause for 30 frames unless A or B is pressed.
## `$0B`: `sound_dex_fanfare_50_79`
Play `SFX_DEX_FANFARE_50_79`.
## `$0C`: <code>text_dots <i>n</i></code>
Print *n* `"…"`s, pausing for 10 frames after each; interrupt if A or B is pressed.
## `$0D`: `text_waitbutton`
Wait for button press; don't show arrow.
## `$0E`: `sound_dex_fanfare_20_49`
Play `SFX_DEX_FANFARE_20_49`.
## `$0F`: `sound_item`
Play `SFX_ITEM`.
## `$10`: `sound_caught_mon`
Play `SFX_CAUGHT_MON`.
## `$11`: `sound_dex_fanfare_80_109`
Play `SFX_DEX_FANFARE_80_109`.
## `$12`: `sound_fanfare`
Play `SFX_FANFARE`.
## `$13`: `sound_slot_machine_start`
Play `SFX_SLOT_MACHINE_START`.
## `$14`: <code>text_buffer <i>id</i></code>
Write text from one of the following addresses (listed in [data/text_buffers.asm](https://github.com/pret/pokecrystal/blob/master/data/text_buffers.asm)):
0. `wStringBuffer3`
1. `wStringBuffer4`
2. `wStringBuffer5`
3. `wStringBuffer2`
4. `wStringBuffer1`
5. `wEnemyMonNickname`
6. `wBattleMonNickname`
## `$15`: `text_today`
Print the weekday.
## `$16`: <code>text_far <i>address</i></code>
Write text from a different bank.
## `$50`: `text_end`
Stops processing text commands.
# Control characters
These get interpreted in the context of printing regular text. Macros exist to conveniently place the control characters.
## `$00`: <code>text <i>text</i></code>
Start writing text until `"@"`. (Not actually a control character, but shorter than `text_start` followed by `db`.)
## `$4E`, `"<NEXT>"`: <code>next <i>text</i></code>
Move a line down.
## `$4F`, `"<LINE>"`: <code>line <i>text</i></code>
Start writing at the bottom line.
## `$50`, `"@"`: <code>page <i>text</i></code>
Start a new Pokédex page.
## `$51`, `"<PARA>"`: <code>para <i>text</i></code>
Start a new paragraph.
## `$55`, `"<CONT>"`: <code>cont <i>text</i></code>
Scroll to the next line.
## `$57`, `"<DONE>"`: `done`
End a text box.
## `$58`, `"<PROMPT>"`: `prompt`
Prompt the player to end a text box (initiating some other event).

124
docs/vc_patch.md Normal file
View file

@ -0,0 +1,124 @@
# Nintendo 2DS/3DS Virtual Console Patch
The Nintendo Virtual Console is an emulator on the 2DS and 3DS consoles. It can emulate the Game Boy Color (among other consoles), while applying enhancements or modifications to some games, such as replacing Link Cable functionality with the DS' Wireless Link capabilities, or disabling Game Boy Printer features.
Game-specific enhancements are determined by a `.patch` file corresponding to the `.gbc` ROM file. These files are bundled together in a `.cia` file; creating such a file is outside the scope of this project.
## Build pokecrystal11.patch
To build **pokecrystal11.patch**:
```bash
make crystal11_vc
```
This will also create two ROM files, **pokecrystal11.gbc** and **pokecrystal11_vc.gbc**. The pokecrystal11_vc.gbc file has the patches already applied to it; do *not* use this file! The ROM file and patch file must share the same name, so use pokecrystal11.patch together with pokecrystal11.gbc.
## Custom files
There are a few files involved with building the `.patch` file, in addition to the ones used for building ROMs.
### vc/pokecrystal11.patch.template
The `.patch.template` file is the basis for the `.patch` file. Many numeric values in the `.patch` file are derived from the values of labels, constants, and ROM content; these values are abstracted into *commands* that get evaluated by `tools/make_patch` to output symbolic names as their actual values, formatted to match the original `.patch` file.
### vc/pokecrystal11.constants.asm
The `.constants.asm` file is used to create a `.constants.sym` file. Typical `.sym` files only list the values of *labels* (ROM banks and addresses); this file is used to list *constants* that are needed by the `.patch.template`. Any constants that the `.patch.template` refers to must be explicitly printed here with the `vc_const` macro.
### tools/make_patch.c
The program used to convert a `.patch.template` into a `.patch` file.
To convert `vc.patch.template` into `vc.patch`:
```bash
tools/make_patch labels.sym constants.sym patched.gbc original.gbc vc.patch.template vc.patch
```
For example, this is what `make crystal11_vc` does:
```bash
tools/make_patch pokecrystal11_vc.sym vc/pokecrystal11.constants.sym pokecrystal11_vc.gbc pokecrystal11.gbc vc/pokecrystal11.patch.template pokecrystal11.patch
```
## Patch types
**Hooks** do not directly modify the ROM; they just identify locations within the ROM code. When the emulated code execution reaches a hook, the emulator performs an emulation function. For example, the `Enable_GS_Ball_mobile_event` hook is located after the code to add a new Hall of Fame entry, and causes the emulator to edit the save file to enable the GS Ball event.
Hooks are defined with the `vc_hook` macro, which defines a label starting with "`.VC_`" for the patch template file to use.
**Patches** directly modify the contents of the ROM. This is done before emulation begins. For example, the `print_forbid_1` patch modifies an "`and A_BUTTON`" instruction to "`and 0`", so pressing A will not print Unown on the Game Boy Printer.
Patches are defined with the `vc_patch` and `vc_patch_end` macros; `vc_patch` defines a label starting with "`.VC_`", `vc_patch_end` defines a corresponding label with "`_End`" appended. Between these two macros, the code or data is conditionally different depending on whether or not a patch file is being built.
The sole purpose of creating `pokecrystal11_vc.gbc` and `pokecrystal11_vc.sym` is to make these labels and modifications available to `make_patch` for use in the patch template.
## Patch template syntax
**Comments** start at a semicolon "`;`" and continue until the end of the line. They are output as-is, without interpreting commands.
**Patch names** are contained in "`[`" brackets "`]`". They are output as-is, without interpreting commands.
Patch names also set the **current patch label**. This is the label starting with "`.VC_`" followed by the patch name, with any invalid characters (not letters "`A-Z`", digits "`0-9`", or underscore "`_`") converted to underscores "`_`". These labels are conditionally defined only when building the patch file with the `vc_hook` and `vc_patch` macros. For example, the patch name "`[fight begin]`" corresponds to the patch label "`.VC_fight_begin`", generated by the "`vc_hook fight_begin`" macro.
Patch names may designate an alternate for the label with an at-sign "`@`". This allows the label in the assembly source to have a more descriptive name, while still reproducing the original `.patch` file. For example, the patch name "`[BiographySave_ret@Enable_GS_Ball_mobile_event]`" corresponds to the label "`.VC_Enable_GS_Ball_mobile_event`" but is output as "`[BiographySave_ret]`".
**Commands** are contained in "`{`" braces "`}`". They are not output themselves, but may produce their own output when interpreted.
Commands are interpreted with a series of arguments, separated by whitespace (spaces, tabs, or newlines). Leading and trailing whitespace is ignored; for example, "`{ hex @ 4 }`" is interpreted the same as "`{hex @ 4}`".
Command names have variants to allow reproducing the exact formatting in a `.patch` file. If the command name is all lowercase, the output byte values use lowercase for hexadecimal digits A-F; if it is all uppercase, they use uppercase.
Some commands may output a **value series**, which is a series of two-digit hexadecimal bytes separated by spaces, preceded by a decimal count and a colon "`:`": "<code>a*N*: <i>v1</i> <i>v2</i> [...] <i>vN</i></code>". These commands have additional variants: if the command name ends in a slash "`/`", the count and colon are not output; or else, if it ends in an underscore "`_`", a space is output after the colon; otherwise, the count and colon are output without a space.
**Arguments** evaluate to numeric values. They may be any of the following:
- Literal numbers in decimal (base 10, e.g. "`42`"), hexadecimal (base 16, e.g. "`0x2a`"), or octal (base 8, e.g. "`052`"). They may start with a plus sign "`+`". Numbers should not be negative.
- Comparison operators: "`==`" is 0, "`>`" is 1, "`<`" is 2, "`>=`" is 3, "`<=`" is 4, "`!=`" is 5, and "`||`" is 0x11.
- Symbol names from the two `.sym` files provided to `make_patch` may evaluate as their relative address or their absolute offset, depending on the command. (Addresses are relative to the symbol's bank for ROM addresses, or to 0x8000, the start of all RAM, for RAM addresses.) They may also be followed by a plus sign and a literal number that gets added to the value.
- "`@`" evaluates as the address or absolute offset of the current patch/hook label, depending on the command.
Any other characters are output as-is.
## Patch template commands
### <code>{patch[ <i>offset</i>[ <i>length</i>]]}</code>
Outputs the bytes of the current patch as a value series, or as a hexadecimal number if there is only one byte. The bytes are found between the current patch label, and the label which is the current patch label plus "`_End`". An optional first argument is an *offset* to add to the current patch label before gathering the contents between it and the end label. An optional second argument is a *length* of bytes to output instead of the length between the start and end labels.
For example, if "`{patch}`" outputs "`a3:ab cd ef`", then "`{patch +1}`" outputs "`a2:cd ef`", and "`{patch 0 1}`" outputs "`0xab`".
Converting the patch template will print a warning if any differences exist between the original and patched ROMs, which are not covered by "`patch`" commands.
### <code>{dws <i>args</i>...}</code>
Outputs its arguments as a value series of little-endian 16-bit words. Symbol names or "`@`" are evaluated as their relative address.
For example, if "`{dws 42 0xabcd wCurSpecies}`" outputs "`a6:2a 00 cd ab 60 cf`", then "`{dws >= wCurSpecies+3}`" outputs "`a4:04 00 63 cf`".
### <code>{db <i>arg</i>}</code>
Outputs its argument as a single-byte value series. Symbol names or "`@`" are evaluated as their relative address.
For example, "`{db 0xEF}`" outputs "`a1:ef`".
### <code>{hex <i>arg</i>[ <i>padding</i>]}</code>
Outputs its first argument as a hexadecimal number. An optional second argument is the minimum length in digits; values shorter than it will be padded with leading zeros. Symbol names or "`@`" are evaluated as their absolute offset, or as their relative address if the command name ends in a tilde "`~`".
For example, if "`{hex @}`" outputs "`0x6789`", then "`{hex @+1 5}`" outputs "`0x0678a`".
This command has extra variants to reproduce inconsistent output casing: "`Hex`" prints the last three digits in lowercase and the rest uppercase; "`HEx`" prints the last two digits in lowercase and the rest uppercase; "`hEX`" prints the last three digits in uppercase and the rest lowercase; and "`heX`" prints the last two digits in uppercase and the rest lowercase.