The hands are drawn over the world, so they hid the grenade wherever they
overlapped it, and turning the palm up showed it behind the hand. It is now
drawn in the first-person pass with them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The left trigger holds the grenade in the hand (the game keeps it there
past the throw animation); letting go throws it from the left hand, along
the fastest the hand moved over its last 150 ms, as hard as that was (full
at 4 m/s). Without a throw (vr.grenade_throw_speed, 1.5 m/s) the game
throws as before. The left hand's swing is no melee while throwing, and
the arms keep their pose through the throw animation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Halo draws its centre prompts as crosshair elements of the weapon HUD.
Only the reticle states now go to the crosshair layer; the message states
are drawn in the head-locked HUD, also while zoomed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The crosshair used to be cut out of the HUD image (an 80x80 region around
the centre) and moved to where the weapon aims, taking with it whatever
else lay there, such as part of the reload prompt. Now it is drawn alone
first (hud_draw_crosshair), captured into its layer, and the HUD is drawn
afterwards without it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The weapon's frames are derived in one place: the controller's, the
model's (turned so its barrel points where the controller does), and,
with both hands on it, both turned alike by the turn that lines the
model's grips up with the hands, measured in the model's frame (it was
applied in the controller's, which tipped the aim up when gripping).
- The crosshair runs along that aim from the muzzle, as the shots do.
- The scope keeps its place above and ahead of the hand, moved only
sideways over the barrel as drawn, facing back along the aim.
- Each weapon's barrel angle and gun node offset go to debug.txt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First-person models point their barrels in towards the screen's centre,
so the model pointed a few degrees left of where the controller aims and
shots, the crosshair and the scope went. The weapon camera is now turned
by the barrel's direction at the ready (the gun node's axis nearest the
camera's forward), which is logged per weapon, and the scope's disc sits
over the barrel as drawn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each weapon's first-person model sits differently in the hand, and the
pistol's barrel ran a few centimetres left of the controller's aim, so its
crosshair sat right of it. Shots, the crosshair and the laser now start at
the gun node as drawn at the ready, along the controller's aim.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The weapon's own crosshair (red on an enemy, as in the HUD) is taken
from the HUD's centre and floats where the weapon aims, as far away as
what it aims at, facing the eyes, as large as the HUD showed it
(vr.crosshair). The laser is off by default (vr.laser) and never shows
over the menus.
- Zoomed in, the first-person weapon stays in the hand (the game hides
it), the HUD's crosshair and zoom overlays give way to the scope, which
has a fine cross of its own, and the scope's disc is 10 cm across.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zoomed in, the player's view is drawn once more, from the weapon along
its aim through the zoom's field of view, into a small square of the
back buffer (the first-person weapon left out), and shown on a disc over
the barrel that magnifies what it covers of the view by the zoom's
magnification (vr.scope_size). The weapon's zoom mask and filters no
longer darken the HUD before the eyes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The part of the HUD moved to the wrist is now just around the motion
tracker's disc and its range (measured in a 640-wide HUD), larger on the
wrist and without what else the corner holds.
- The terminal's log lines, which the beta prints over the bottom left
of the screen, are left out in VR; the console, when open, still shows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While an animation turns the weapon away from where it aims (reloads,
drawing and putting it away, posing, venting), the laser follows the gun
node's turn from its ready pose. The ready pose is now kept only while
the weapon is held ready or firing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The melee animation moves both hands about the gun (the right one to the
stock, the left one onto the barrel) and the left arm with them; holding
only the gun still showed that. The whole skeleton now keeps its pose
from before the attack, in the hand. The melee diagnostics are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The free left arm keeps its shape on the foregrip, which left its upper
arm pointing anywhere (at the belly, looking down); it now turns about the
elbow to point back at the shoulder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Bent to the shoulder, the free left arm's skin, bound partly to the
skeleton's root at the other hand, tore into ribbons wherever the hand
was far from where the animation has it. The whole arm now follows the
hand as it is shaped on the foregrip; the shoulder IK is kept for the
right arm and the left one on the weapon.
- A melee attack aims where the head looks (the right controller aims
the weapon), and each one is logged; vr.melee_swing_speed is 1.5 m/s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The forearm, turned only to point at the wrist, kept the animation's
twist while the free hand could roll half a turn from it, and the skin
between them collapsed into a ribbon. It now takes most of the hand's roll
about its bone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The free left hand lines up with the controller as a hand holds it:
fingers along it, the knuckles (little finger to index) up its handle,
the wrist behind its grip.
- Thrusting a hand forward (vr.melee_swing_speed) is a melee attack; the
right grip still is too.
- Through a melee attack the gun stays in the hand; its animation would
swing it before the camera.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The first-person arms' upper arms and forearms are turned (two-bone
IK) to run from the player's shoulders, below the head, to the wrists,
instead of floating with the weapon.
- The left wrist follows the left controller, its fingers with it,
unless the grip closes at the weapon's foregrip (vr.two_hand_reach):
then the hand holds the weapon, which aims along both hands.
- Left controller: grip holds the weapon, Y switches weapons, X is the
flashlight, and held switches grenades.
- The first-person skeleton's nodes are logged once per weapon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With vr.aim_with = "controller" (the default), on foot:
- the unit's aiming vector comes from the right controller, while its
facing (the body, the view and the movement) keeps following the head;
- shots start at the muzzle of the weapon in the hand, kept inside the
level from the camera as before;
- the first-person weapon and hands are posed from the controller
(vr.weapon_forward/right/down place the model in the hand);
- the HUD's crosshair, which marks where the head looks, is left out, and
a laser runs from the weapon to what it aims at (vr.laser).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Android port gets an OpenXR session in the host (Khronos loader, core
OpenXR plus XR_KHR_opengl_es_enable): on a headset the game keeps its
640x480 layout drawn at the eye images' resolution, renders the player's
view once per eye from the tracked eye poses through asymmetric frusta,
and shows the HUD and menus as a head-locked or room-fixed quad layer.
The head aims the player; the right stick turns; the Touch controllers
map onto the Xbox pad; rumble goes to the controllers. Frames without a
player view (menus, loading, cinematics) go onto a screen in the room.
A marker file, xr_test, runs a self-test of the session, layers, colour
path and controllers without game data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Independently gate storage + K, OQ-4 P-a/P-b1, four static-local packets and comment-only disclosures. Preserve all 7641 exact owner rows, 71 valid parks and 1161 passing tests; code/data credit unchanged. G and game_engine admission remain held because current canonical loses populate_statistic_buffer. Existing collision static-local repair is not duplicated. No donor tree, held candidate, scorer or private asset imported.
Independently rebuilt and source-reviewed donor b659a777. Restores attested helper, clipping and color-call shapes; 743 meaningful / 752 padded bytes, 0 regressions. No new helpers or storage changes.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Independently verified donor 9f0b3af9413e6dc5c1be28786d22f0ea7df6c119. Restore the first-party-attested null-and-text-box-type invariant. The redundant check is genuine source evidence, not a fabricated allocator lever. Complete 608-byte section and 50 relocations match January; no new helper, owner, warning or exact regression.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native builds only (#ifdef HALO_LINUX); the matching MSVC build keeps the
original text and stays byte-identical.
- ui_widget_game_data_input_functions.c: the local machine limit follows
HALO_PORT_MAXIMUM_NETWORK_MACHINES and the struct network_game copy holds
HALO_PORT_MAXIMUM_NETWORK_PLAYERS players; its layout is checked against
halo_port_limits.h. The pregame screen keeps its three remote machine
panels and shows the first three remote machines instead of asserting
(sizing its arrays by the machine limit read uninitialized widget
pointers). netgame_prejoin_players scans every player slot, and the
player count texts have room for three digits. network_machine_is_valid
widens the char index before comparing it with 128.
- ui_widget_event_handler_functions.c: the join/unjoin, team swap and
countdown handlers use the port's players offset and player count
instead of 0x226/0x242/0x243 and 16.
- hud.c: hud_draw_players sizes its teammate buffer by the port's player
limit and bounds the fill (more than 16 teammates overflowed the stack).
- motion_sensor.c: multiplayer tests the (horizontal) range before a unit
takes a blip slot, so distant units no longer crowd nearby ones out of
the 16 slots.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Match _hud_update_weapon_local_player: 1472 credited bytes, 68 relocations. Default is unreachable over all 19 loop states. First-party evidence supports the ammo predicates and primary-trigger correction. Independently verified in the three-function batch with zero regressions.
Owner rulings 2026-09-24:
- Q1 YES (04, not 04b): _actor_aim_grenade in its natural /Od body (no goto),
keeping the BUG comment on January's vehicle-path read of the unassigned
aim_vector. Header-inline helpers replace the TU redirects and the NODUP
cross_product2d hand copy; the new COMDATs are identical to January's.
Unlocks collision_features, real_math and path_smoothing (config 03A) and
retires the stale collision_features rejection (04). The decals rejection,
path_structure_bsp and path_obstacle_avoidance are untouched.
- Q4 YES (A2): _first_person_weapon_update without the two NULL guards,
VALID_INDEX at all five /Od-attested sites, and the three magnitude
redirects removed. The weapon-switch invariant is documented in source.
- Q7 YES: _uncompress_int32_to_real_vector3d stages the conversion through a
real temp with a declaration-time initialiser (debug exact; verbatim in
Oct-2001 2276P and the Sept-2001 cache.exe).
Full gate: 383 -> 388 / 468 Halo objects, +3 functions (+2,211 meaningful
code), 0 regressions, parks 82 -> 80, admission 0 contradicted / 0 revoked,
fake-match leads 26, pytest 1,161 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zero credit; every packet was independently adversarially reviewed (22/22 approved).
- critic_3: 72 renames of placeholder/invented static function names across 20
objects to first-party names. Each rename has Sept-2001 linker-map alignment
(cachebeta xbe / retail cache exe, several also Aug-2001), relocation-masked
body identity and matching callers; cachebeta publics show every row was a
January static. hs's radiosity_hack_{start,save,find_point}_evaluate pairing
is proven by the Sept .rdata hs_function_definition pointers; players'
create_weapon also matches HCEX.pdb. Objects: ai_debug (reviewer-amended: a
dead stub removed, its comment re-attached), transport_endpoint_winsock,
transport_endpoint_set_winsock (+ parked.json key renames), profile,
progress_bar, network_connection, saved_game_files, rasterizer_xbox_profile,
xbox_sound_cache, game_engine_king, players, hs, hs_runtime, main, decals,
rasterizer_lights, rasterizer_xbox_dynavobgeom,
rasterizer_xbox_environment_fog, rasterizer_xbox_hardware_bitmaps,
render_cameras.
- hs_compile: whitespace_characters, eol_characters and hs_type_primitive_parsers
are external (cachebeta publics, HCEX DataIsGlobal); hs_compile_finish is
static with a TU-local prototype (no public; HCEX static function).
- bink_playback: bink_playback_idle -> bink_playback_update__internal
(first-party). The bink_decompress_audio_frame rename is NOT landed: it needs
an added call whose position is unattested, the same class canonical held for
hardware_geometry in 4d1ebf17.
Gate (full ninja, objdiff 3.3.1): objects 383, code 1,577,237, data 2,586,915
(all unchanged); stable diff 0 regressions; parks 83/0/0; admission 11/0/2/0;
fake scan 26; pytest 1161 passed; diff --check clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- geometry: drop the NODUP hand copy of plane2d_distance_to_point, so the
header inline is used (selection byte only; removes a latent
collision_bsp<->geometry LNK2005).
- collision_bsp: collision_bsp_usage_times is file-static (cachebeta publics).
- rasterizer_lights P1: HCEX file statics with January's .bss layout (the
load-bearing `= {0}` follows the VC7 initialised-statics law and precedent);
lands atomically with its 6 symbols.json rows.
- motion_sensor: .bss symbol layout fix (object_audit 44/2 -> 44/0).
- rasterizer_xbox_models: five D3D wrapper rows static.
All byte- and credit-neutral (gated with batch 5a); objects stay incomplete
on residual functions and held owner items.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
January's 26 stack-sentinel sites in hud_draw, hud_unit, hud_nav_points,
hud_weapon and motion_sensor share one inlined shape. Both asserts at each site
carry one line literal (a macro). January's 48-byte _check_stack_buffer has no
relocations anywhere, and the Sept-2001 map flags the helper as an inline COMDAT
('i hud_draw.obj'). hud_draw.h now carries the STACK_BUFFER constants, the
unchanged check_stack_buffer body as a header __inline, and the sentinel macro
match_assert_stack_frame (descriptive name: neither the /Od build nor HCEX has
the sentinel). The invented corrupt_stack_found/stack_buffer_checked gotos in
hud_unit are gone, and hud_nav_points and hud_weapon use the macro too (exact
callers). motion_sensor keeps its copy: its sentinel callers are residual, so a
new COMDAT there would break rule (i).
hud_unit (reviewer-amended 01RS + 02-04R): first-party names and storage; the
_fast_ftol rejection is retired (identical copy, pair links pass both ways).
DISCLOSED for an owner ruling: P7 drops rasterizer_xbox_models' dead
#include "interface/hud_draw.h" (project commit 635bd83d; no name used; raw-
and warning-inert). It is load-bearing only as declared-name shielding: the
inline adds ~9 names, which flips __rasterizer_model_transparent_geometry_submit.
Gate (full ninja, objdiff 3.3.1, 23 hud_draw.h consumers): Halo objects 378 ->
379; stable diff unchanged, 0 regressions; parks 86/0/0; admission 11/0/2/0;
pytest 1157 passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All 100 functions were strict exact and the 934-byte .data section was
identical to January's: same payload, flags and resolved destinations
for all 228 relocations. But 98 handler functions,
event_handler_function_list and new_campaign_profile_name were external
in our source. cachebeta.pdb publics show all 100 were file-static in
January, and no other January object references them. Adding `static`
to their prototypes and definitions keeps every function strict exact.
All 452 January symbols now match, and the .bss is identical.
The object also emits the three stock D3D SDK .rdata tables. Each is
byte-identical to January's selected copy (rasterizer_xbox_decals,
progress_bar, headers) and pair-links in both orders, like the 54
already-admitted objects that emit them. The rasterizer_xbox_profile
hold additionally rests on that object's own shared-declaration debt.
One pinned entry in config/semantic_data_matches.json.
- Canonical scorer (objdiff 3.3.1): credits 934 data bytes and clears
one more revoked row (4 -> 3).
- Pinned objdiff 3.6.0: a verified no-op.
- No code bytes, no config status change.
Verification: full ninja; stable diff vs frozen 8,245-function baseline
0 gained / 0 regressions; parks 103/0/0; fake-match scan 0 -> 0;
pytest 1154 passed; git diff --check clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>