summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRangi <remy.oukaour+rangi42@gmail.com>2018-05-20 12:14:00 -0400
committerRangi <remy.oukaour+rangi42@gmail.com>2018-05-20 12:14:00 -0400
commit77d410003ed000636dced37e50357d63fbfb2919 (patch)
treec5749f87516cb7e782a59696d4786827723a7d46
parentbd9d49624a458d319d126ff3bdcbc2420ae6d38c (diff)
Add a new Pack pocket (incomplete)
-rw-r--r--Add-a-new-Pack-pocket.md404
-rw-r--r--Tutorials.md1
-rw-r--r--screenshots/berry-pocket.pngbin0 -> 2079 bytes
-rw-r--r--screenshots/gfx-pack-pack.pngbin0 -> 303 bytes
-rw-r--r--screenshots/gfx-pack-pack_f.pngbin0 -> 385 bytes
-rw-r--r--screenshots/gfx-pack-pack_menu.pngbin0 -> 383 bytes
-rw-r--r--screenshots/unused-pack_menu-tiles.pngbin0 -> 391 bytes
7 files changed, 405 insertions, 0 deletions
diff --git a/Add-a-new-Pack-pocket.md b/Add-a-new-Pack-pocket.md
new file mode 100644
index 0000000..20ca578
--- /dev/null
+++ b/Add-a-new-Pack-pocket.md
@@ -0,0 +1,404 @@
+This tutorial is for how to add a new Pack pocket. As an example, we'll add a Berry Pocket.
+
+
+## Contents
+
+1. [Define pocket-related constants](#1-define-pocket-related-constants)
+2. [Give the pocket a name](#2-give-the-pocket-a-name)
+3. [Update the item types](#3-update-the-item-types)
+4. [Update the Pack graphics](#4-update-the-pack-graphics)
+5. [Add space in WRAM for the new pocket](#5-add-space-in-wram-for-the-new-pocket)
+6. [Initialize the new WRAM](#6-initialize-the-new-wram)
+7. [Update `HasNoItems` and `CheckRegisteredItem` to check the new pocket](#7-update-hasnoitems-and-checkregistereditem-to-check-the-new-pocket)
+8. [Update standard inventory routines](#8-update-standard-inventory-routines)
+9. [Update the Pack engine](#9-update-the-pack-engine)
+10. [Update the Crystal-only Pack engine](#10-update-the-crystal-only-pack-engine)
+
+
+## 1. Define pocket-related constants
+
+Edit [constants/item_data_constants.asm](../blob/master/constants/item_data_constants.asm):
+
+```diff
+ ; item types
+ const_def 1
+ const ITEM ; 1
+ const KEY_ITEM ; 2
+ const BALL ; 3
+ const TM_HM ; 4
++ const BERRIES ; 5
+
+ ...
+
+ ; pack pockets
+ const_def
+ const ITEM_POCKET ; 0
+ const BALL_POCKET ; 1
+ const KEY_ITEM_POCKET ; 2
+ const TM_HM_POCKET ; 3
++ const BERRY_POCKET ; 4
+ NUM_POCKETS EQU const_value
+
+ MAX_ITEMS EQU 20
+ MAX_BALLS EQU 12
+ MAX_KEY_ITEMS EQU 25
+ MAX_PC_ITEMS EQU 50
++MAX_BERRIES EQU 17
+```
+
+Notice that the "item types" constants are in a different order than the "pack pockets" constants. So don't mix them up.
+
+The `MAX_*` constants are the storage capacities of their respective pockets. We defined `MAX_BERRIES` as 17 because there are 17 items that will go in the Berry pocket, as we'll see later; that way you can conveniently carry one stack of each Berry. (Likewise, `MAX_BALLS` is 12, and 12 items belong in the Ball pocket.)
+
+
+## 2. Give the pocket a name
+
+Edit [data/items/pocket_names.asm](../blob/master/data/items/pocket_names.asm):
+
+```diff
+ ItemPocketNames:
+ ; entries correspond to item type constants
+ dw .Item
+ dw .Key
+ dw .Ball
+ dw .TM
++ dw .Berry
+
+ .Item: db "ITEM POCKET@"
+ .Key: db "KEY POCKET@"
+ .Ball: db "BALL POCKET@"
+ .TM: db "TM POCKET@"
++.Berry: db "BERRY POCKET@"
+```
+
+The phrase "`the BERRY POCKET.`" has to fit on one line for the message "*PLAYER* put the *ITEM* in the *POCKET*." So a pocket name can be up to 13 characters long, plus a "@" at the end.
+
+
+## 3. Update the item types
+
+Edit the types in [data/items/attributes.asm](../blob/master/data/items/attributes.asm), changing the relevant items from `ITEM` to `BERRIES`:
+
+- `BERRY`
+- `GOLD_BERRY`
+- `PSNCUREBERRY`
+- `PRZCUREBERRY`
+- `BURNT_BERRY`
+- `ICE_BERRY`
+- `BITTER_BERRY`
+- `MINT_BERRY`
+- `MIRACLEBERRY`
+- `MYSTERYBERRY`
+- `RED_APRICORN`
+- `BLU_APRICORN`
+- `YLW_APRICORN`
+- `GRN_APRICORN`
+- `WHT_APRICORN`
+- `BLK_APRICORN`
+- `PNK_APRICORN`
+
+Note that all of these items have the `CANT_SELECT` property. The only items without it are `BICYCLE`, `ITEMFINDER`, `OLD_ROD`, `GOOD_ROD`, and `SUPER_ROD`, which are all key items. I haven't tried creating a non-Key item that can be registered to the Select button, but suspect that it would cause glitches.
+
+
+## 4. Update the Pack graphics
+
+Edit [gfx/pack/pack.png](../blob/master/gfx/pack/pack.png):
+
+![gfx/pack/pack.png](screenshots/gfx-pack-pack.png)
+
+Edit [gfx/pack/pack_f.png](../blob/master/gfx/pack/pack_f.png):
+
+![gfx/pack/pack_f.png](screenshots/gfx-pack-pack_f.png)
+
+And edit [gfx/pack/pack_menu.png](../blob/master/gfx/pack/pack_menu.png):
+
+![gfx/pack/pack_menu.png](screenshots/gfx-pack-pack_menu.png)
+
+The pack.png and pack_f.png graphics are in this order, from top to bottom: Key Items, Items, TM/HM, Balls. We've split the Balls pocket in half and turned the right half into a Berry pocket, with its graphic appended to the bottom of the image. (Note that this doesn't match the order of either set of constants. We'll see later that the offsets of each pocket's individual picture are hard-coded in a table, not calculated directly from a constant.)
+
+The pack_menu.png graphic is a set of 80 tiles in no particular order, except for the 20 sequential tiles that form the "◀▶ POCKET ▼▲ ITEMS" header. We've added the new "Berries" subtitle sequentially, but that's just to be neat; we'll see later that the individual tiles of the subtitles are all hard-coded, so you can fit them into any of the unused tiles in the original:
+
+![gfx/pack/pack_menu.png](screenshots/unused-pack_menu-tiles.png)
+
+
+## 5. Add space in WRAM for the new pocket
+
+Edit [wram.asm](../blob/master/wram.asm):
+
+```diff
+ wPCItemsCursor:: db
+ wPartyMenuCursor:: db
+ wItemsPocketCursor:: db
+ wKeyItemsPocketCursor:: db
+ wBallsPocketCursor:: db
+ wTMHMPocketCursor:: db
++wBerryPocketCursor:: db
+
+ wPCItemsScrollPosition:: db
+ wPartyMenuScrollPosition:: db ; unused
+ wItemsPocketScrollPosition:: db
+ wKeyItemsPocketScrollPosition:: db
+ wBallsPocketScrollPosition:: db
+ wTMHMPocketScrollPosition:: db
++wBerryPocketScrollPosition:: db
+
+ ...
+
++wDudeNumBerries::
+ wDudeNumKeyItems:: db ; d292
+ wDudeKeyItems:: ds 18
+ wDudeKeyItemsEnd:: db
+
+ ...
+
+ wCmdQueue:: ds CMDQUEUE_CAPACITY * CMDQUEUE_ENTRY_SIZE
+
+- ds 40
+
+ ...
+
+ wNumBalls:: db ; d8d7
+ wBalls:: ds MAX_BALLS * 2 + 1 ; d8d8
+ wBallsEnd::
+
++wNumBerries:: db
++wBerries:: ds MAX_BERRIES * 2 + 1
++wBerriesEnd::
+```
+
+Every pocket has a <code>w<i>Name</i>PocketCursor</code> and a <code>w<i>Name</i>PocketScrollPosition</code> byte for scrolling through it, a <code>wNum<i>Names</i></code> byte that stores how many items are in it, and a <code>w<i>Names</i></code>–<code>w<i>Names</i>End</code> array for storing its items. `wTMsHMs` and `wKeyItems` are special, but normal item-storage pockets like `wItems` and `wBalls` have two bytes per possible item (one for the ID, one for the quantity) plus a byte for the end-of-list marker.
+
+`wDudeNumBerries` just shares space with `wDudeNumKeyItems` because the catch tutorial dude only shows his Item and Ball pockets. It's probably possible to remove the data and code for his Key Item pocket entirely, but that's beyond the scope of this tutorial, and not necessary unless you really need some extra space.
+
+Speaking of extra space: we used a total of 37 more WRAM bytes for the Berry pocket's data. Its WRAM bank has barely any free space left, so in general you'll need to remove as many bytes as you add. There was an unused "`ds 40`" that conveniently saved 40 bytes. If you want larger pockets and need to free up more space, shortly below the pockets is an unused "`ds 13`", then "`ds 49`" after the <code>w<i>MapName</i>SceneID</code> bytes, then "`ds 100`" after the <code>w<i>Trainer</i>FightCount</code> bytes, and a "`ds 23`" after `wPhoneList`. Any of those can be reduced to make more room for Pack pockets (or for more map scene IDs, or extending `wEventFlags` if you raise `NUM_EVENTS`, or so on).
+
+
+## 6. Initialize the new WRAM
+
+Edit [engine/menus/intro_menu.asm](../blob/master/engine/menus/intro_menu.asm):
+
+```diff
+ ld hl, wNumItems
+ call .InitList
+
+ ld hl, wNumKeyItems
+ call .InitList
+
+ ld hl, wNumBalls
+ call .InitList
++
++ ld hl, wNumBerries
++ call .InitList
+
+ ld hl, wPCItems
+ call .InitList
+```
+
+As the `.InitList` comment explains, this "loads 0 in the count and −1 in the first item slot."
+
+
+## 7. Update `HasNoItems` to check the new pocket
+
+Edit [engine/menus/start_menu.asm](../blob/master/engine/menus/start_menu.asm):
+
+```diff
+ HasNoItems: ; 129d5
+ ld a, [wNumItems]
+ and a
+ ret nz
+ ld a, [wNumKeyItems]
+ and a
+ ret nz
+ ld a, [wNumBalls]
+ and a
+ ret nz
++ ld a, [wNumBerries]
++ and a
++ ret nz
+ ld hl, wTMsHMs
+ ld b, NUM_TMS + NUM_HMS
+ .loop
+ ld a, [hli]
+ and a
+ jr nz, .done
+ dec b
+ jr nz, .loop
+ scf
+ ret
+ .done
+ and a
+ ret
+```
+
+Now edit [engine/overworld/select_menu.asm](../blob/master/engine/overworld/select_menu.asm):
+
+```diff
+ .Pockets:
+ ; entries correspond to *_POCKET constants
+ dw .CheckItem
+ dw .CheckBall
+ dw .CheckKeyItem
+ dw .CheckTMHM
++ dw .CheckBerry
+
+ ...
+
+ .CheckBall:
+ ld hl, wNumBalls
++.StandardCheck:
+ call .CheckRegisteredNo
+ jr nc, .NoRegisteredItem
+ inc hl
+ ld e, a
+ ld d, 0
+ add hl, de
+ add hl, de
+ call .IsSameItem
+ jr c, .NoRegisteredItem
+ ret
++
++.CheckBerry:
++ ld hl, wNumBerries
++ jr .StandardCheck
+```
+
+There's no such thing as a Ball or Berry that can be registered, so I haven't actually tested this code; if there's a bug with the built-in handling of registered Balls, it will also apply to Berries. You're better off leaving Key items as the only type that can be registered.
+
+
+## 8. Update standard inventory routines
+
+Edit [engine/items/items.asm](../blob/master/engine/items/items.asm). Four routines here need updating, so let's take them one by one.
+
+Edit `_ReceiveItem`:
+
+```diff
+ _ReceiveItem:: ; d1d5
+ ...
+
+ .Pockets: ; d1e9
+ ; entries correspond to item types
+ dw .Item
+ dw .KeyItem
+ dw .Ball
+ dw .TMHM
++ dw .Berry
+
+ ...
+
+ .Ball: ; d1fb
+ ld hl, wNumBalls
+ jp PutItemInPocket
++
++.Berry:
++ ld hl, wNumBerries
++ jp PutItemInPocket
+```
+
+Edit `_TossItem`:
+
+```diff
+ _TossItem:: ; d20d
+ ...
+
+ .Pockets:
+ ; entries correspond to item types
+ dw .Item
+ dw .KeyItem
+ dw .Ball
+ dw .TMHM
++ dw .Berry
+
+ .Ball: ; d228
+ ld hl, wNumBalls
+ jp RemoveItemFromPocket
++
++.Berry:
++ ld hl, wNumBerries
++ jp RemoveItemFromPocket
+```
+
+Edit `_CheckItem`:
+
+```diff
+ _CheckItem:: ; d244
+ ...
+
+ .Pockets:
+ ; entries correspond to item types
+ dw .Item
+ dw .KeyItem
+ dw .Ball
+ dw .TMHM
++ dw .Berry
+
+ .Ball: ; d25f
+ ld hl, wNumBalls
+ jp CheckTheItem
++
++.Berry:
++ ld hl, wNumBerries
++ jp CheckTheItem
+```
+
+And edit `GetPocketCapacity`:
+
+```diff
+ GetPocketCapacity: ; d283
+ ld c, MAX_ITEMS
+ ld a, e
+ cp LOW(wNumItems)
+ jr nz, .not_bag
+ ld a, d
+ cp HIGH(wNumItems)
+ ret z
+
+ .not_bag
+ ld c, MAX_PC_ITEMS
+ ld a, e
+ cp LOW(wPCItems)
+ jr nz, .not_pc
+ ld a, d
+ cp HIGH(wPCItems)
+ ret z
+
+ .not_pc
++ ld c, MAX_BERRIES
++ ld a, e
++ cp LOW(wBerries)
++ jr nz, .not_berries
++ ld a, d
++ cp HIGH(wBerries)
++ ret z
++
++.not_berries
+ ld c, MAX_BALLS
+ ret
+```
+
+In most of these cases we just based the Berry pocket case on the Ball pocket case. The TM/HM and Key Item pockets are special, and the Item pocket is sometimes treated as a "default" case with different handling. But the Ball pocket is non-default and just stores regular items, so it's safe to copy.
+
+
+## 9. Update the Pack engine
+
+Edit [engine/items/pack.asm](../blob/master/engine/items/pack.asm). This will take a while. My own procedure was to carefully go down the file, noticing repetitive lines that handle each pocket case by case, and adding similar cases for the Berry pocket. There are also a few tricky parts where the code is optimized for having exactly four pockets (a nice round power of 2), so adding a fifth needs some extra code.
+
+TODO
+
+
+## 10. Update the Crystal-only Pack engine
+
+Edit [engine/items/pack_kris.asm](../blob/master/engine/items/pack_kris.asm).
+
+```diff
+ PackFGFXPointers: ; 48e93
+ dw PackFGFX + (15 tiles) * 1 ; ITEM_POCKET
+ dw PackFGFX + (15 tiles) * 3 ; BALL_POCKET
+ dw PackFGFX + (15 tiles) * 0 ; KEY_ITEM_POCKET
+ dw PackFGFX + (15 tiles) * 2 ; TM_HM_POCKET
++ dw PackFGFX + (15 tiles) * 4 ; BERRY_POCKET
+```
+
+This is just like `PackGFXPointers`, but for the Crystal-only girl's Pack.
+
+We're finally done!
+
+![Screenshot](screenshots/berry-pocket.png)
diff --git a/Tutorials.md b/Tutorials.md
index b3e5914..a8d7fda 100644
--- a/Tutorials.md
+++ b/Tutorials.md
@@ -18,6 +18,7 @@ Tutorials may use diff syntax to show edits:
- [Item (up to 254)](Add-different-kinds-of-new-items)
- [TM (up to 120)](Add-a-new-TM)
- [Music song](Add-a-new-music-song)
+- [Pack pocket](Add-a-new-Pack-pocket)
**Upgrades to existing features:**
diff --git a/screenshots/berry-pocket.png b/screenshots/berry-pocket.png
new file mode 100644
index 0000000..a335fa7
--- /dev/null
+++ b/screenshots/berry-pocket.png
Binary files differ
diff --git a/screenshots/gfx-pack-pack.png b/screenshots/gfx-pack-pack.png
new file mode 100644
index 0000000..986fc7a
--- /dev/null
+++ b/screenshots/gfx-pack-pack.png
Binary files differ
diff --git a/screenshots/gfx-pack-pack_f.png b/screenshots/gfx-pack-pack_f.png
new file mode 100644
index 0000000..8bbf48d
--- /dev/null
+++ b/screenshots/gfx-pack-pack_f.png
Binary files differ
diff --git a/screenshots/gfx-pack-pack_menu.png b/screenshots/gfx-pack-pack_menu.png
new file mode 100644
index 0000000..b24c054
--- /dev/null
+++ b/screenshots/gfx-pack-pack_menu.png
Binary files differ
diff --git a/screenshots/unused-pack_menu-tiles.png b/screenshots/unused-pack_menu-tiles.png
new file mode 100644
index 0000000..240dc92
--- /dev/null
+++ b/screenshots/unused-pack_menu-tiles.png
Binary files differ