Cite as
ryandwyer/perfect-dark
Original
https://gitlab.com/ryandwyer/perfect-dark
Pinned
ryandwyer/perfect-dark @ 46d3393bed1a3d5fbefbdba6df7237579b5c66e0
Accessed
2026-10-22
Licence
MIT (LICENSE, "Copyright (c) 2022 Ryan Dwyer"), for the decompilers' own work; the game is Rare's and Nintendo's
Credit
Ryan Dwyer's matching C decompilation of Perfect Dark, with its contributors. Names and comments quoted in perfect-dark claims are theirs.
Notes
The README calls it "a complete decompilation" and "a matching decompilation", building six ROMs: ntsc-beta, ntsc-1.0 ("the initial, buggy release"), ntsc-final ("NTSC 8.7 final"), pal-beta, pal-final, and jpn-final. The Makefile builds `ROMID ?= ntsc-final` by default (`VERSION = 2`); claims read the code that build compiles, so `#if VERSION` branches for other ROMs are named where they differ. The build extracts assets from a copy of the ROM; the atlas fetches only the game code (src/game/), headers, the Makefile, the README, and the license, not src/assets, src/setups (stage data), the libraries, or tools. github.com/n64decomp/perfect_dark mirrors this repo but stood at 169ed48bdcbfb3b568b028bd5bebb27680073514 (2025-03-15) on 2026-10-22, 8 commits behind, so claims cite GitLab. No file the claims read is marked non-matching (no GLOBAL_ASM or NON_MATCHING in src/game/).

Cited on

Claims (8)

The Perfect Dark decompilation builds six ROMs and defaults to ntsc-final, which its README describes as "NTSC 8.7 final" beside ntsc-1.0, "the initial, buggy release"; the Makefile sets VERSION = 2 for it.README.md L5README.md L34–35Makefile L17Makefile L87–88Code
Perfect Dark's standard autoaim, for a gun whose autoaim is on, scores each character on screen and takes the highest score. It skips teammates, the dead and dying, and anyone not holding a gun (unless flagged CHRCFLAG_FORCEAUTOAIM). A candidate needs a clear line from the player, its screen center in the middle half of the screen's width, and its height reaching into the band from 17.5% to 82.5% of the screen's height; the screen's center must lie within 1.5 of its screen widths of its middle, times a solo scale (perfect-dark/autoaim-scale). The score is 1 when the screen's center x lies between the target's left and right screen edges, and otherwise falls off with the gap over that margin. The walk runs from the end of a list props_sort orders by prop->z, largest first, which the decompilers' comment calls "furthest first"; since a score of 1 ends the walk, the nearest such target wins on that reading. Doom traces ahead and 5.625° to each side for a target, and Duke Nukem 3D's aim takes the target within 8.4° of the facing that is nearest along it.src/game/prop.c L60–64src/game/prop.c L94–98src/game/prop.c L2699–2725src/game/chr.c L5014–5025src/game/prop.c L2412–2417src/game/prop.c L2441–2458src/game/prop.c L2474–2508Code
In solo Perfect Dark the autoaim margin (perfect-dark/autoaim) is multiplied by g_AutoAimScale, which lv_update_solo_handicaps sets by difficulty: 1.5 on Agent, 0.75 on Special Agent, 0.2 on Perfect Agent, and 1 on the Perfect Dark difficulty; the ntsc-final build takes the non-Japanese values (g_Jpn is false). Co-op and Counter-Operative use their own values.src/game/lv.c L1840–1864src/game/lv.c L1867–1881src/game/lv.c L1884–1898src/game/lv.c L1901–1911src/game/lang.c L53–54src/game/lang.c L66src/game/prop.c L2451–2453Code
Perfect Dark's autoaim moves the crosshair, not the view: once a target is picked, bmove_update_autoaim_x_prop and _y_prop set the aim point to the target's screen center (clamped to the window), and a new target replaces the old only after a 30/60 s hold; bgun_swivel then eases the crosshair toward the point, keeping a fraction crossdamp of the gap each 1/240 s (a unit the decompilers' comments give). On NTSC that fraction, autoaimdamp, falls by 0.00032 a 60th of a second while autoaim holds a target, from at most 0.979 (it climbs back there while none is held) to 0.963, so the pull strengthens over up to 50/60 s (computed); at 0.963 the crosshair closes 14% of the gap each 60th of a second (1 − 0.963⁴, computed). The shot leaves through the crosshair's position plus a random spread. Doom's bullet keeps the player's facing and takes only a vertical slope, and Duke Nukem 3D turns the shot itself to the target's angle in the call that fires it.src/game/prop.c L2736–2743src/game/bondmove.c L97–112src/include/constants.h L118–119src/game/bondmove.c L1763–1791src/game/bondgun.c L4964–4976src/include/types.h L112–113src/game/bondgun.c L5123–5139Code
Perfect Dark's chr_damage scales a hit by the body part (hitpart) it struck: a head hit is multiplied by 4 and a torso hit doubled, the limbs and pelvis keep the damage, and a hit on the held gun or a hat deals none. Outside normal multiplayer a gun shot fired by a player multiplies a head hit by a further 25 (headshotdamagescale), 100 in all, unless a shield took it; a Skedar's head is only doubled, and its tail takes 10 times. A hit with no part (HITPART_GENERAL, as when a guard's bullet hits the player) is halved and then doubled as the torso, so it keeps its damage. Doom's P_LineAttack passes a bullet's damage to the thing it hits with no body part.src/game/chraction.c L4262–4273src/game/chraction.c L4368–4370src/game/chraction.c L4390–4400src/game/chraction.c L4413–4422src/game/chraction.c L4707–4751src/include/constants.h L1396–1413src/game/chraction.c L10361–10385Code
The Falcon 2's single shot in Perfect Dark carries a damage of 1 (invfunc_falcon2_singleshot), read by gset_get_damage. A guard adds each hit to its damage against a maxdamage that chr_allocate sets to 4 (a level's AI script can set another), and chr_react_to_damage starts its death once damage reaches it; so on Perfect Agent a default guard takes 4 Falcon 2 shots to a limb, 2 to the torso, or 1 to the head (computed). The player's health starts at 1, and a hit takes damage × 0.125 from it, so a guard's Falcon 2 bullet takes an eighth on Perfect Agent (computed). Each Doom pistol bullet deals 5, 10, or 15 against a zombieman's 20.src/game/invitems.c L485–495src/include/types.h L2920–2924src/game/gset.c L475–483src/game/gset.c L505–507src/game/chr.c L1085src/game/chr.c L1127src/game/chraicommands.c L3147–3150src/game/chraction.c L4965src/game/chraction.c L3571–3572src/game/chraction.c L5077–5078src/game/playermgr.c L257src/game/chraction.c L4795–4812Code
Solo Perfect Dark scales damage by difficulty in lv_update_solo_handicaps, which runs every tick: a guard's hit on the player is multiplied by 0.5 on Agent, 0.6 on Special Agent, and 1 on Perfect Agent (and on the Perfect Dark difficulty, times its own enemy-damage setting); the player's shots are doubled on Agent; and explosions hurt the player 0.25 times on Agent and 0.75 times on Special Agent. On Agent the scales for the damage the player takes, from guards and from explosions, shrink further once health plus shield is at or below 0.6, falling linearly to half at 0.125, so 0.25 for a guard's hit (computed); the player's ×2 on its own shots stays fixed. Doom halves all damage to the player on its easiest skill, sk_baby.src/game/lv.c L1840–1866src/game/lv.c L1867–1876src/game/lv.c L1884–1893src/game/lv.c L1901–1910src/game/lv.c L2297src/game/chraction.c L4390–4404src/game/pdmode.c L27–33Code
A Perfect Dark combat simulant keeps a shootdelaytimer60 per target: it resets to 0 when the simulant picks a new target, counts up in 60ths of a second while the target is in sight, and counts back down to 0 while it isn't. Unless a burst or the Cyclone's secondary discharge is already under way (burstsdone above 0 or cyclonedischarging), which keeps it firing without the test, the simulant opens fire only once the timer reaches its difficulty's shootdelay60: 90 (1.5 s) for Meat, 60 for Easy, 30 for Normal, 15 for Hard, and 0 for Perfect and Dark, on NTSC; the target must also be within 45/256 of a turn (63°, computed) of its facing, unless the simulant is dizzy. Doom gives each monster a reactiontime at spawn (8 for the zombieman, 0 on Nightmare), counted down once per A_Chase call, and refuses a ranged attack until it reaches 0, unless the monster has been hit since it last checked (MF_JUSTHIT).src/game/bot.c L87–95src/game/bot.c L45–46src/include/constants.h L118–119src/game/bot.c L1365–1388src/include/types.h L116src/game/bot.c L3600–3608src/game/chraction.c L13931src/game/chraction.c L13979–13984Code