| Damage formula | - Link's health is
HeartValues, whose high nibble is the heart containers less one and whose low nibble is the full hearts, plus a partial heart in HeartPartial from $00 to $FF; a new file starts at $22 and $FF, three containers and three hearts. Code - A monster's contact damage comes from
ObjTypeToDamagePoints, one byte per object type, whose low nibble is whole hearts and whose high nibble is sixteenths of a heart; of its 93 entries, 30 are half a heart, 24 one heart, 18 two hearts, and 3 four hearts. Code - Each level of ring halves the damage before it is subtracted, so the first ring takes half and the second a quarter. Code
- When the partial heart can't cover the damage, the game takes a full heart and refills the partial heart to
$FF rather than $100, so each such borrow costs Link an extra 1/256 of a heart. Code - The wooden, white, and magical swords take
$10, $20, and $40 from a monster's hit points; fire takes $10, a bomb $40, and the boomerang none, only stunning. Code
| | | | | | - In both US releases a hit copies the enemy's damage value to Link as a negative number, and Link's one active ring changes it: Power Rings L1–L3 add 2, 4, and 8 units, Armor Rings L1–L3 take 1, 2, and 3 off, the Blue Ring halves it with an arithmetic shift that rounds toward more damage (3 units become 2), the Green Ring takes three quarters (rounded toward less damage), and the Cursed Ring doubles it. A hit always costs at least one unit, and the Protection Ring makes every hit 8. Health then drops a quarter heart per 2 units, so a unit is an eighth of a heart (computed, from the quarter hearts a comment gives). Link's Awakening DX's health is in eighths by its annotators' comment (
$08 = 1 heart), and its Blue Tunic also halves damage. Code
| | |
| Destructible terrain | | | | | | | | | |
| Dodge roll | | | | | | | | | |
| DPCM samples | - The game keeps 7 DMC samples in three tables of address, length, and rate bytes, and a request byte picks the entry by its lowest set bit. Code
- The samples are sound effects; boss code requests
$10 for the Aquamentus, Gleeok, and Ganon roar (entry 4, rate $0E), $20 for the Dodongo and Gohma roar (entry 5), and $02 for a boss hit (entry 1), and DeleteSlot requests $08, which the disassembly labels the "hurt" sound (entry 3). Code
| | | | | | | | |
| Evasive hop | | | | | - In the N64 NTSC 1.0 code, each update sorts the control stick into one of four quarters relative to the way Link's model faces: forward, left, backward, or right, each 90° wide and centered on its direction, after turning the stick angle by the camera's. Below a magnitude of 55 the stick counts as no direction. Code
- In the N64 NTSC 1.0 code, the action handler the decomp calls Player_ActionHandler_10 acts on a press of A, except in a room of the indoors type, on floor type 7, or on a floor with floor effect 1 (both unnamed): with the stick backward, left, or right it starts a hop that way; with the stick forward or neutral, only while Z-targeting, it starts a jump slash if Link holds a melee weapon and can use items, and a roll otherwise. Code
- In the N64 NTSC 1.0 code, the action that holds Link standing while he Z-targets (Player_Action_808407CC in the decomp) offers the hop handler to A, and hands control to the idle action once Z-targeting ends; the idle action's own handler list leaves the hop handler out. Code
- In the N64 NTSC 1.0 code, the backward hop (a backflip, by its animation) launches Link at a vertical velocity of 5.8 and a horizontal speed of 6.0 aimed straight behind his facing, and a sideways hop at 3.5 up and 8.5 aimed 90° to the left or right. The vertical value is multiplied by a water factor, sWaterSpeedFactor, which this pass didn't read. Code
| | | | |
| Health upgrade | | | | | | | | | |
| Invincibility frames | - A monster that touches Link while his invincibility timer is above 0 does nothing; a hit sets the timer to 24 and his shove distance to 32 px. Code
DecrementInvincibilityTimer lowers an object's invincibility timer by 1 only on frames when FrameCounter is even, so a timer of lasts frames. Code- While Link holds the clock,
BeginUpdateWorld adds 16 to his invincibility timer every frame, and monsters' contact checks return no collision. Code - Measured in Rev 1, a Tektite held against Link hurts him every 47 or 48 frames, taking half a heart each time; from three hearts, five hits leave
$20 and $7D, 3/256 of a heart under a half heart. Verified - A weapon that hurts a monster sets the monster's invincibility timer to 16, which lasts 32 frames, and shoves it 64 px in the weapon's direction, unless the monster is already invincible or immune to the weapon's damage type. Code
| | | | | | | | |
| Item drop table | - Whenever Link is hurt, the game zeroes
WorldKillCount, HelpDropCount, and HelpDropValue, the counters behind the fairy and the 10th-kill drops. Code SetUpDroppedItem sorts a dead monster's type into one of four drop groups by three type lists, with every unlisted type in the fourth; seven types, among them the Gels, the Keese, and the Like Like, drop nothing. CodeDropItemTable holds ten items for each group, and the kill cycle picks the column; group A's row is heart, rupee, heart, rupee, fairy, rupee, heart, heart, rupee, rupee. Code- In
TakeItem, item $18 is a rupee, $0F five rupees, $22 a heart, $23 a fairy, $21 the clock, and $00 four bombs; a heart restores 1 heart and a fairy 3. Code - Outside the special cases, a drop happens only when the dead monster's slot's byte of
Random is below its group's rate: $50, $98, $68, and $68, or 80, 152, 104, and 104 in 256. Code - Each kill advances
WorldKillCycle from 0 to 9 and back to 0 before the drop is chosen, except kills of types $5D, $14, and $1C, which the comments call a Child Gel and a Red Keese. Code - When
WorldKillCount, the kills since Link was last hurt, equals 16 and the monster can drop items, the drop is a fairy, whatever its random byte. Code - The 10th kill since Link was last hurt brings a guaranteed drop of 5 rupees, or of bombs when that 10th kill was by bomb, and the count starts again from 0. Code
- A dropped item lasts 255 decrements of its lifetime, one every other frame, about 510 frames, and Link can't take it until the 16th decrement, about 32 frames. Code
- Called in Rev 1 for every group, kill-cycle column, and value of the random byte,
SetUpDroppedItem drops in 800, 1,520, 1,040, and 1,040 of 2,560 cases for groups A to D, each the item its column names; it gives a fairy at a kill count of 16, and 5 rupees or bombs at a help count of 10. Verified - The Speed Demos Archive Knowledge Base states that 10 kills without being hit force a 5-rupee drop, or a bomb when the 10th is killed by bomb, and that 16 kills in a row force a fairy, only when the 16th enemy can drop items. Wiki
| | | | | | | | - When a monster dies, its death effect calls the drop routine with the monster's drop table: 6 for Octoroks, ChuChus, Leevers, and Peahats. Unless the room disables random drops, the routine adds the table's 16 weights to an active Picolyte's row, then adds 5 to the heart weight at 8 health (one heart) or less, 3 to bombs with none carried, 3 to arrows with none, and 1 to the 5-rupee weight at 10 rupees or fewer; a weight below 0 (the tables rule an entry out with −999) counts as 0. One
Random() value then picks the entry: its bits 24–27 choose where to start, and the value modulo the weights' sum walks the 16 entries from there. Table 6 weighs nothing 73, a green rupee 25, a heart 5, bombs 1, and arrows 1, so with no Picolyte and no need bonus an Octorok drops nothing about 70% of the time (computed). Bombs and arrows drop only once Link has the bag or the bow. A Link to the Past instead steps each prize pack through 8 prizes in order. Code
|
| Keys and locked doors | | | | | | | | | |
| Kinstone fusion | | | | | | | | | - Each Kinstone fuser in The Minish Cap has a data entry whose second byte, which the decompilers call its stability, is the percent chance it offers a fusion:
GetFusionToOffer returns no fusion when that byte is at most Random() % 100, none before Link has the Kinstone Bag, and none before the story's progress reaches the entry's first byte. The one call the atlas read is in the Blade Brothers' first action, which sets them up. An entry of 0xFF in a fuser's list stands for a shared fusion: from a random start among 18, the first not yet fused. In the US ROM's table, of the 119 fusers after entry 0, 77 have 100, 20 have 50, 21 have 30, and one has 60 (computed). Code
|
| Knockback | - A shoved object moves 4 px a frame, 1 px of shove distance at a time, and the shove ends early when the object reaches the room's edge or, aligned to the grid, an unwalkable tile. Code
Walker_Move moves a walking object by its shove whenever one is set, in place of its walking, so Link can't steer during a shove. Code- When a monster hits Link, the shove points away from the monster along one axis: the horizontal one when Link is between grid squares and faces left or right, the vertical one when he is between squares facing up or down. The monster then turns around, unless bit 7 of its object attributes is set (a flag the disassembly's comments name "reverse after hit Link") or it is a Vire (object type
$12). Code - Measured in Rev 1, each of the Tektite's first three hits shoves Link 32 px to the right in 8 frames. Verified
| | | | | | | | - While Link's knockback duration is nonzero, a routine that his normal state calls each frame counts it one step toward 0 and, if it's still nonzero, moves him
0x280 along the knockback direction; the code's walking speed kWalkSpeed is Q_8_8(1.25), 0x140, so the atlas reads the push as twice walking speed, though the move routine wasn't read. His normal state skips the code that reads the d-pad while the duration is nonzero, so the player can't steer. The collision loop stores the direction for both parties, Link's the reverse of the toucher's. Among the cancels: a jump, a dash, a roll, a fall, swimming (a swim state above 1), a held object (states 1–4), and climbing unless the push is south. After an Octorok's touch (duration 8) that's 7 moves (computed). Link's Awakening's push runs a 16-frame countdown. Code
|
| Lock-on targeting | | | | | - In the N64 NTSC 1.0 code, pressing Z locks Link onto the actor Navi hovers over, unless that actor has lock-on disabled; with nothing to lock onto, Z starts "parallel" mode instead. Under the Hold setting the lock lasts while Z is held; under Switch, a flag keeps it after Z is let go. Code
- In the N64 NTSC 1.0 code, unless an earlier branch applies (hookshot flight, pushing or pulling, and a few others), while Link has a focus actor his update makes that actor the main camera's view target and picks a mode for it: talk mode if Link's actor has the talk flag set, else, with the friendly-focus flag set, boomerang-follow mode if a boomerang is out or the friendly Z-target mode, and otherwise the unfriendly Z-target mode. In parallel mode it picks Z-aim mode when either of two checks the decomp leaves unnamed (func_8002DD78, func_808334B4) passes, else Z-wall-climb mode with an unnamed state flag (stateFlags1 bit 21) set, else the Z-parallel mode. Code
- In the N64 NTSC 1.0 code, while Link has a lock-on target and the reticle counter (
reticleSpinCounter, which the decompilers' comment says counts up while a reticle is active) isn't 0, Link's model turns each update toward the locked-on actor's focus point, and in parallel mode toward the parallel angle; the direction he moves in is a separate yaw, which the stick sets relative to the camera. Two state flags the decomp leaves unnamed skip the turn, and the Z-target run and the roll set one of them. Code - In the N64 NTSC 1.0 code, the jump slash started from the Z-targeting A press (stick forward or neutral, holding a melee weapon) sends Link forward along his facing at a speed of 5.0 with a vertical velocity of 5.0. Code
| | | | |
| Magic meter | | | - Each magic item's cost comes from a table with three columns, and one byte,
$7EF37B, picks the column for every item. The rods cost 16, 8, or 4; the medallions 32, 16, or 8; the powder 8, 4, or 2; the lamp 4, 2, or 2. Code
| | | | | | |
| Music driver | | | | | | | | | |
| Password and battery save | - The Legend of Zelda keeps three save files in battery-backed cartridge RAM; each holds an 8-byte name, a 40-byte item block, 384 bytes of world flags, and four single bytes (whether the slot is in use, one unlabeled byte, the death count, and the quest), with a 16-bit checksum that is the plain sum of those 436 bytes, and, kept apart and outside the sum, two marker bytes, $5A and $A5. Code
- The Legend of Zelda saves in two steps; it first writes the whole file to a second copy, file B, sums it, stores that sum, and marks B uncommitted, and on the next step it sums B again and, if the sums agree, clears file A's markers, copies B over A, writes A's markers and sum, and marks B committed. Code
- When its title demo or its file select screen starts, The Legend of Zelda checks each of the three slots; a file B still marked uncommitted whose sum matches is copied over file A, finishing a save that was cut off, and a file A whose markers are not $5A and $A5 or whose sum doesn't match is formatted, its name set to spaces and every other byte to 0. Code
| | | | | | | | |
| Raster effects | uses | | | | | | | | |
| Sailing | | | | | | | | - In the decompilation's C++ for the boat (d_a_ship, which doesn't yet match the game's bytes as a whole; the USA GZLE01 build), procSteerMove, while the boat isn't airborne (daSFLG_FLY_e off), takes the gap between the wind vector's yaw (cM_atan2s of its x and z) and the boat's facing shape_angle.y, as a 16-bit angle from 0 to 0x8000, and turns it into a wind factor: 1 − gap/0x10000 below 0x4000 (90°), 0.75 − (gap − 0x4000) × 5.493164e-05 up to 0x6000 (135°), then 0.3 − (gap − 0x6000) × 0.00032967035, floored at 0. So the factor is 1 with the wind along the boat's facing, 0.875 at 45°, 0.75 at 90°, 0.3 at 135°, and 0 from 140° to 180° (computed). The target speed is the wind power divided by 0.5, capped at 1, times the factor times 55. The branches then run in this order, one per update. First, on the body animation's frame 7, if speedF is below twice the target (at most 80) it is raised to that and mTargetSpeed is set to the target (10000 if the boat is being moved by force), and nothing else changes speedF that update. Otherwise, while mTargetSpeed is below speedF, firstDecrementShipSpeed lowers speedF toward mTargetSpeed (a tenth of the gap, at most 5, at least 1) and sets mTargetSpeed to 10000 once the gap is under 3; this comes before the sail check, so it applies with the sail flag off too. Otherwise, with the sail-on flag off or a target of 0, decrementShipSpeed eases speedF toward 10 × the stick's value × the cosine of its angle (a twentieth of the gap, at most 0.1, at least 0.015). Otherwise decrementShipSpeed eases it down to a lower target, and cLib_addCalc up to a higher one (a tenth of the gap, at most 0.5, at least 0.1). Code
- In the decompilation's C++ for the boat (d_a_ship, not yet matching as a whole; the USA build), procSteerMove moves the unnamed angle m0366, which the atlas reads as the tiller, toward mStickMVal × 0x2000 × −sin(mStickMAng) with cLib_addCalcAngleS(…, 4, l_HIO.tiller_speed, 0x100), tiller_speed being 700, and, unless the boat is airborne, passes it to setMoveAngle. Unless the boat is being moved by force (checkForceMove), setMoveAngle sets a rate of 4 − |speedF| / 55 × 3, held between 0.1 and 3.6, blends it toward 3.6 by the unnamed counter m0388 over 15 (3.6 once m0388 reaches 15), and turns the facing by −rate × (m0366 >> 6). With m0388 at 0 and a tiller of 0x2000, that is about 461 units (2.5°) an update at rest and 128 units (0.7°) at speed 55 (computed): the boat turns slower the faster it sails. Code
- In the decompilation's C++ for the boat (d_a_ship, not yet matching as a whole; the USA build), procSteerMove allows a hop when no event runs, Link isn't barred from control (checkNoControll), the body animation is FN_MAST_ON2 at frame 7 or later, the boat isn't airborne, its forward speed mFwdVel is over 16.5, it isn't being moved by force, and the sailing target speed is at least 11. Then the R trigger's lock (mDoCPd_R_LOCK_TRIGGER) sets the airborne and jump flags and an upward speed equal to the target speed, held between 15 and 40, and sets mJumpRate to (speed.y − 11) × 0.014545455. Code
| |
| Status bar split | | | | | | | | | |
| Sword beam | | | | | | | | | |
| Time of day | | | | | | - In the N64 US 1.0 code the clock is a 16-bit count in which a day is 0x10000 units, so a game minute is about 45.5 units (computed; the macro CLOCK_TIME_MINUTE truncates it to 45). Each play update, Environment_UpdateTime adds the time speed R_TIME_SPEED and then, if that speed isn't 0, the save's timeSpeedOffset. It adds only when time isn't stopped, the game isn't paused or over, no textbox is open (or its message id is 0xF7, 0x20D2, 0x140C, or 0x100 through 0x200, or the game mode is the end credits), the debug frame advance (FrameAdvance_IsEnabled) is off, no transition is under way (transitionMode off, or a game mode other than normal) and none has been triggered, and no cutscene runs. From 18:00 until 6:00 it sets the save's isNight flag. Code
- In the N64 US 1.0 code each scene's header sets its time speed, and a header value of 0xFF gives 0, a scene where time stands still. While Link's ocarina slot is empty, a nonzero scene speed becomes 5, under a decompilers' comment that reads "Increase time speed during first cycle"; the scene's speed becomes R_TIME_SPEED only while Sun's Song is inactive. The Inverted Song of Time is offered only where the scene's restriction flags allow it (otherwise text 0x1B95 and nothing) and the speed isn't 0; accepting it sets the offset to −2, or back to 0 if it was already set, and the update adds the offset only while the speed isn't 0. The game updates once every 3 video frames, set by GameState_SetFramerateDivisor(3) into a field whose comment reads "how many VIs should elapse before next swap". Assuming NTSC's 59.94 video frames a second (the atlas's assumption, not in the code), that is about 20 updates a second, and at speed 5 a game minute takes about 0.46 s, a day about 656 s, and the three days about 33 minutes (computed). The usual speed of a scene is in its header, ROM data the decompilation doesn't hold. Code
- In the N64 US 1.0 code, answering yes to the Song of Time's prompt runs Sram_SaveEndOfCycle, whose comment says it is "Used by Song of Time (when clicking "Yes") and (indirectly) by the "Dawn of the New Day" cutscene". The function sets the day counter to 0 and the clock one unit before 6:00, clears the Inverted Song's offset, and adds one to a reset count capped at 999, but on the song's path the message code saves the day, the time, and the cutscene index before the call and puts them back right after it; where the cycle returns to the first dawn is untraced. It sets the rupees Link carries to 0 and every ammo count to 0 except the pictograph box's, and turns any bottle holding an item from ITEM_POTION_RED through ITEM_OBABA_DRINK back into an empty bottle. It sets a sword at or below the Razor Sword back to the Kokiri Sword (or the Gilded Sword if a stolen-item slot holds it), removes dungeon keys, boss keys, stray fairies, and the pictograph, and clears the event-info flags. Scene flags (switches, chests, collectibles) and week-event flags survive only where a per-scene or per-flag persistence table marks them. Among the rest, it resets five minigame high scores and the three Deku Playground scores, but not the bank balance, which is kept in the high-score slot HS_BANK_RUPEES. Code
- In the N64 US 1.0 code, accepting the Song of Double Time's prompt sets the clock to 6:00 if the save's isNight flag is set and to 18:00 if not, and turns off the moon's countdown timer; this code doesn't change the day counter. The three-day events actor (En_Test4) then sees the clock cross 6:00 and, in its no-cutscene branch, adds a day; that the jump to exactly 6:00 trips its crossing test is the atlas's reading. The song isn't offered on the third night (day 3 with isNight set) or where a scene's restriction flags forbid it. Code
- In the N64 US 1.0 code, the actor En_Test4, which its file's comment describes as "Three-Day Events: day-night transitions", compares each update the clock with the next transition time (6:00 by night, 18:00 by day) by multiplying the 16-bit differences of the current and previous times from it and treating a product of 0 or less as a crossing (the atlas's reading of the test), unless a transition, a cutscene, setup actors, a room load, or the debug camera is under way. When the clock crosses 6:00 with the day counter at 3, it calls Interface_StartMoonCrash, which sets the day to 4 and the clock to 6:00 plus 10 units and starts a white fade to a Termina Field entrance. On days 1 and 2 the same crossing adds a day when no transition cutscene is set or the telescope is on; the other branch (z_en_test4.c L446-L470), which sets a flag the decomp names EVENTINF_TRIGGER_DAYTELOP for the Dawn of a New Day screen, is untraced. Play_Init spawns this actor in dungeon and boss rooms on any day but 0; elsewhere it comes from scene data the atlas hasn't read. Code
| | | |
| Wind | | | | | | | | - In the USA build, dKyw_wind_set, when no actor has set a wind vector override (mpWindVecOverride), takes the wind's strength from the room's file list (dStage_FileList_dt_GlobalWindLevel): level 0 gives 0.3, level 1 gives 0.6, level 2 gives 0.9, any other level 0. A custom wind power above 0 replaces it, an event wind setting of 2 makes it 0, and it is capped at 1. While mInitAnimTimer is nonzero the wind vector and power jump to the new values; otherwise each call moves each vector component a tenth of the way (at most 2, at least 0.001) and the power a tenth of the way (at most 1, at least 0.005). Since the boat, in the decompilers' not-yet-matching C++ for d_a_ship (procSteerMove), divides the power by 0.5 and caps it at 1, a level-0 room tops out at 0.6 of the boat's 55, 33 units an update, and levels 1 and 2 at the full 55 (computed, once the power has settled). Code
- In the USA build, the wind-direction picker dOperate_wind_c (which the atlas reads as the Wind's Requiem's, from its TAKT_WIND sounds; "takt" is the baton) sets mDeg on opening from the current wind vector's yaw and then, with the stick pushed to at least 0.9, sets mDeg to one of eight compass values, 0 to 315 in 45-degree steps. A confirms it: windSet passes dKyw_tact_wind_set a pitch of 0 and a yaw of 180° − mDeg (0 gives −0x8000, 90 gives 0x4000, 270 gives −0x4000). dKyw_tact_wind_set stores both angles for the environment and in the save (dComIfGs_setWindX, setWindY) and sets the tact-wind flag; dKyw_wind_set then builds the wind vector as (cos pitch × cos yaw, sin pitch, cos pitch × sin yaw). dKyw_wind_init reloads the saved angles unless both are −1. B cancels the picker. Code
| |