Custom items

From ZeldaHacking Wiki
Jump to navigation Jump to search

Code

This section discusses the implementation of item objects.

TODO: Examples.

Item usage table

The file data/{game}/itemUsageTables.s(ages/seasons) determines what happens when you press a button to use an item.

The itemUsageParameterTable specifies:

  • Which items take priority over others?
  • Can the item be used simultaneously with other items?
  • Should the item be used whenever the button is detected as "pressed", or does it need to be released and then pressed?

Depending on the conditions specified, it will create a "parent item" object when the button is pressed (see below).

In the same file, the linkItemAnimationTable specifies:

  • What animation Link should perform when he initially uses the object
  • Whether to set wLinkUsingItem1 while using the item, which prevents Link from picking things up among other things
  • Some other things that are less well understood?

Item objects

Each item can have two different representations as an object:

  • "Parent" type (in object_code/common/itemParents): This is responsible for "high-level management" of item usage, ie. dealing with button presses and manipulating Link's animation. This is a sort of pseudo-object which has no physical representation, but nevertheless consumes an object slot.
  • "Physical" type (in object_code/common/items): The physical representation of an object, ie. the actual sword. Items with no physical representation separate from Link, like the shield, don't have these.

Sometimes these two object types are intricately tied together (ie. with swords), but sometimes are more loosely coupled (ie. seed physical objects are independent once spawned in). In either case though, the parent object is usually responsible for spawning in the physical object.

"Parent" type (pseudo-objects)

Code flow:

  • Calls checkUseItems (in code/parentItemUsage.s): Responsible for both creating parent items based on buttons pressed, and updating existing parent items each frame.
  • Calls parentItemUpdate (in code/parentItemUsage.s) for each parent item: Calls functions for each item type, ie. "parentItemCode_sword".
  • Item-specific functions (in object_code/common/itemParents: Responsible for creating the "physical" item, updating Link's animation, etc.

At some point in the item-specific function, it should spawn in the physical item type (unless there is no physical object, ie. with the shield).

"Physical" type

Code flow for each frame a physical item exists:

  • Calls updateItems (in code/updateItems.s): For each item, call itemCodeXX, where XX is the item index (ie. $05 for ITEM_SWORD). Most item logic goes here. For the sword, this handles charging logic, sound effects, sword level checks, etc.
  • Updates the other object types (enemies, parts, interactions).
  • Calls updateItemsPost (in code/updateItems.s): For each item, call itemCodeXXPost, analagous to itemCodeXX, but called after all other object types have been updated. The sword updates its animation here.

Collision handling

See: data/{game}/objectCollisionTable.s(ages/seasons)

TODO: More detail

Handy functions

For parent items:

  • parentItemLoadAnimationAndIncState: Call this in state 0 (initialization) to set Link's animation (determined by linkItemAnimationTable) and also immobilizes Link until clearParentItem is called.
  • specialObjectAnimate: Despite the name, this also works with parent items, so long as an animation was loaded first (see above).
  • clearParentItem: Deletes the parent item, releasing Link if he was immobilized. Call this when the item is no longer being used.
  • parentItemCheckButtonPressed: Checks whether the button (A or B) to use the object is currently pressed. The power bracelet uses this to delete itself when the button is released.

Graphics

There are several mechanisms for drawing items:

  • As treasure objects (when collected in-game, ie. from a chest)
  • As menu icons (when in the inventory)
  • As item objects (when used, ie. swinging the sword)

This section covers the first two.

Menu icons

In order to set an item's sprite in the inventory menu (for equippable items only):

  • Find a sprite to replace in gfx/{game}/spr_item_icons_1.png(ages/seasons) (or spr_item_icons_2, _3), and replace it with the desired sprite.
  • Locate the item's entry in data/{game}/treasureDisplayData.s(ages/seasons).
  • Set the second byte in the line to a value between $80 and $af in order to set which graphic in spr_item_icons it's using.
    • $80 is the first sprite in spr_item_icons_1, $90 is for icons_2, $a0 is for icons_3. Replace 0 with f to get the last sprite, or any value in-between.
  • The following byte in the line (3rd byte) determines the palette. Should be a value between $00 and $05.
  • The 4th and 5th bytes are the same as the 2nd and 3rd bytes, for sprites that use a second 8x16 tile. They can be left as $00 in most cases.

Here's an example where item $10 is set to use the 3rd sprite in spr_item_icons_3 ($a3), with the gold palette ($03). This replaces a harp song sprite (see the note below about that).

.db $00, $a3, $03, $00, $00, $ff, <TX_0900 ; TREASURE_10 (0x10)

Notes:

  • All graphics can be drawn as both sprites (when equipped in the top bar) or on the background layer (when in the inventory). For spr_item_icons_1.png, there is also a variant, spr_item_icons_1_spr.png, which is used exclusively when drawn as a sprite, while spr_item_icons_1.png is used exclusively when drawn on the background layer. For the other PNG files (_2, _3), the same graphic is used on both the sprite and bg layers. Unclear why this distinction exists, but it may be related to transparency handling.
  • If you're replacing the harp sprite in spr_item_icons_3, you'll notice that it doesn't work properly when equipped due to hardcoded behavior forcing it to use a different sprite on the top bar. Delete or comment the following code in code/bank2.s (this is how it looks in the hack-base branch):
	; Special behaviour for harp song icons: add 2 to the index so that the "smaller
	; version" of the icon is drawn. (spr_item_icons_3.bin has two versions of each
	; song)
	cp $a3
	jr c,+
	cp $af ; Power Glove sprite was moved to be after the harp
	jr z,+
	add $02
+
  • The hack-base branch also does shenanigans by replacing the boomerang and slingshot sprites with the L-2 versions as needed, in order to save space. This shouldn't be a problem as it only overwrites the graphics if you obtain the L-2 item versions.

Treasure sprite

This is the sprite shown for the treasure representation of the item (ie. when you open a chest).

Let's assume you've already replaced a sprite in one of the spr_item_icons files. Then:

  • Go to data/{game}/interactionData.s(ages/seasons)
  • Assuming you're on the hack-base branch, locate the following lines:
	; CROSSITEMS: Extra values for seasons items, for interaction $60 (INTERAC_TREASURE).
	m_InteractionSubidData $7c $10 $10 ; $7c (Magnet Gloves)
	m_InteractionSubidData $7c $02 $40 ; $7d (Slingshot)
	m_InteractionSubidData $e0 $00 $50 ; $7e (Hyper Slingshot)
	m_InteractionSubidData $84 $04 $40 ; $7f (Magic Boomerang)
	m_InteractionSubidData $7d $0e $50 ; $80 (Roc's Cape)
	m_InteractionSubidData $7d $10 $20 ; $81 (Rod of Seasons)
	m_InteractionSubidData $7d $14 $00 ; $82 (Fool's Ore)
  • Add an extra line below the last line above with the same format.
    • The first byte represents an index from data/{game}/objectGfxHeaders.s(ages/seasons). For example, in ages, the first byte should be $7c for spr_item_icons_1, $7d for icons_2, or $7e for icons_3 (for seasons it's $5f, $60, $61).
    • The second byte is two times the tile index within the file. IE. if it's the 4th tile in spr_item_icons_3.png (tile number $03 due to the zero-based index), this should be $03 * 2 = $06. This should be an even number between $00-$1e.
    • The first digit of the third byte is the palette (ie. 3 = gold).
    • The second digit of the third byte should be 0 for an 8x16 sprite, or 3 for a 16x16 sprite.
  • Here's an example of an added line, which uses spr_item_icons_3 ($7e in ages), the 4th sprite in the file (3 * 2 = $06), with the gold palette in an 8x16 configuration $30):
    • m_InteractionSubidData $7e $06 $30 ; $83 (Pegasus boots)
  • When you place the item in a chest in LynnaLab, set the "gfx" to be equal to the corresponding value in the comment ($83 in this example). You should see a preview of the sprite in LynnaLab (you may need to reload the project).