While a hand-aimed weapon is zoomed, a pass between the eyes and the HUD
renders the view along the gun into a corner of the back buffer, sized
to the scope's 768x768 image. It goes through a sight mask (round, or
the sniper rifle's wide one) onto a layer held at the gun, at the PC
mod HaloCEVR's per-weapon offsets. The eyes never zoom: the first-person
gun stays in the hand and the HUD layer drops the zoom mask.
vr.scope and vr.scope_size are the settings; vr.diag_zoom_seconds zooms
unattended for dumps (vr-scope.bmp). The docs now cover the VR controls,
gestures, weapon feel and scope, and credit LivingFray's HaloCEVR for
their designs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After the designs of LivingFray's PC mod HaloCEVR, re-implemented here.
vr.controls "vr" (the default) lays the headset's controllers out for VR
over the Xbox pad the game reads: right trigger fire, left trigger zoom,
A jump, B reload and action, X switch grenades, Y switch weapons, right
bumper grenade, left bumper flashlight, right stick click melee, left
stick click crouch, menu pause, view back, held a second to recentre.
"pad" keeps the Xbox layout. The host now reports each controller's own
buttons (hand_buttons) beside the pad it built.
Gestures, taken into the game's own control flags in
handle_one_player_input (before a cutscene's inhibition clears them):
- a hand swung up or down faster than vr.melee_speed (2.5 m/s) melees;
- the off hand brought within vr.flashlight_distance (0.2 m) of the
head's middle toggles the flashlight;
- ducking vr.crouch_height (0.15 m) below the recentred height crouches,
standing again within 5 cm;
- the weapon hand's grip at either shoulder (vr.holsters) switches
weapons, with a tick as the hand enters a holster;
- the off hand's grip holds the gun in both hands (vr.two_handed "grip",
within 80 cm; "auto" keeps the earlier proximity test);
- palms together and the off hand's grip moves the weapon to that hand.
vr.left_handed (or that swap) puts the weapon in the left hand: the
weapon hand is a variable throughout, its trigger fires and its bumper
throws grenades, and the first-person model is mirrored across the
weapon camera's upright plane with its winding turned over; the IK then
reaches the gun arm from the left shoulder. vr.move_relative moves
relative to the head or a hand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Android renderer answered each visibility test (lens-flare
occlusion) by mapping the counter buffer for its one word, and each map
waits for the GPU's queue to drain; on the Steam Frame's Zink it also
flushes and logs a performance warning. In stereo, with the flare tests
of two eyes, that took 45% of the game thread: 13-20 frames a second in
the first level's cryo bay. All counters are now read together into a
copy the first time a result is asked for after a test ends (one wait a
frame), and the same scene runs at the headset's 72.
The headset's HUD pass took the planar fog, whose animation offset only
a structure pass consumes, and the next frame's assertion stopped the
game in fogged clusters; the HUD pass leaves the fog alone.
In VR the head's turns count as looking for the scripts that wait for
the player to look (the first level's look test).
The host's thread sampler takes fractions of a second.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While gameplay is in stereo, the player's facing follows the head: the
runtime's frame is begun in player_control_update, so aim and eyes use
the same predicted pose, and the facing becomes the head's direction
turned by a heading of the headset's own. The right stick turns that
heading (vr.snap_turn, 30 degrees, or vr.smooth_turn_speed) instead of
the game; both sticks pressed together recentre. When the game turns
the player itself (a script, a respawn, another pad), the heading takes
it up. View magnetism is off while the head aims, so the view never
turns under it; pitch stops at the game's 85.5 degrees.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the view's magnetism is turned off for the mouse: the bullets still bend toward the target, as with a controller, so shots land as they did.
Co-authored-by: Cursor <cursoragent@cursor.com>
When the mouse moved after the right stick, the controller's magnetism and autoaim leave the player be (input.mouse_aim_assist, off by default). In lockstep games the autoaim stays, since every machine simulates every shot alike.
Co-authored-by: Cursor <cursoragent@cursor.com>
Recover transparent geometry submit (+1,285 meaningful code, zero exact losses); reconcile COMMON ownership and Q10 85+EXT1 credit, actions/main data, and separately disclosed denominator corrections. Preserve canonical-only repairs and all held research. C09 is ordinary disclosed cleanup, not a claim of proven scaffolding intent. Clean rebuild, 1,663 tests, parks, audits, provider links and independent EXT1 check pass. Local reconciliation only.
Import only independently reviewed production changes. Preserve canonical physics and virtual-keyboard repairs; keep B3 and all unresolved holds out.
Record exactly four approved canary debits (6987 meaningful bytes), six Halo admissions and two revocations. Keep vendor ioinit/storage work separate. Full build, 8252-row sweep, 1318 tests, park/admission/fake audits and 621-object binding pass within approved debit bounds.
Do not publish donor research dumps or private assets. README and unrelated user research remain untouched. See frontier_publication_reconciliation_20260927.md for complete evidence limits and accounting.
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.
The 128-bit team mask is indexed by team; a player whose team is not
set (NONE) would read and write outside it, where the 32-bit mask only
shifted. Such a player now counts on its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native builds (HALO_LINUX) size the multiplayer game engine for
HALO_PORT_MAXIMUM_NETWORK_PLAYERS; the matching build keeps the January
text in #else branches and stays byte-identical.
- game_engine.c: MULTIPLAYER_MAXIMUM_PLAYERS follows the port limit; the
postgame statistic buffers hold an entry per player; global_goal has a
slot per player (slayer kill-in-order uses the absolute player index)
and the goal loops are bounded by the array instead of the address of
global_variant; game_engine_get_place uses a 128-bit team mask;
places past the 16th get code-generated English ordinals in the HUD
score messages, scoreboard and postgame instead of asserting or
clamping to "16th"; a free-for-all team is the player's absolute index.
- slayer, oddball, king, race: per-team and per-player score arrays follow
the port limit (oddball's ball arrays stay at 16); January size asserts
are kept for the matching build. Race reads the lap flags of the player
rather than of the player's team index.
- game_allegiance.c: a team past the 10-team table is not its own enemy,
like teams 0..9, so a free-for-all suicide no longer scores as a kill.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Xbox game allows 16 players on 4 machines (4 split screen players
each). The native builds raise both limits to 128, keeping 4 players per
machine, and leave the byte-matching build unchanged: every change is
under HALO_LINUX (the MSVC objects compare identical).
- Limits: every local copy of the player and machine limits, and the
bare 4s and 16s that stood for them, follow halo_port_limits.h. The
player data array, the machine-to-player table (and its reset) and the
per-tick update arrays grow with them.
- Records: the copies of struct network_game agree on the larger layout
and check it against halo_port_limits.h; maximum_player_count becomes
a byte (128 does not fit a signed char); network_player_is_valid
checks the machine against the machine limit, not the split screen
one; a machine's input carries MAXIMUM_LOCAL_PLAYERS actions (16/4
would have become 128/128).
- Protocol: a message holds at most 4095 bytes (its header has a 12-bit
length), so the 13 KB game settings record is sent in 3.5 KB pieces
that clients reassemble; the per-tick update of 128 players (3,857
bytes) fits. Buffers and the packet codec's limits grow to match, and
the game search uses protocol version 2 so these builds and the Xbox
game never see each other's games.
- Connections: the host accepts a client connection per machine (the
split screen count stood in for it), FD_SETSIZE is 256, stream writes
send all of a message or drop a peer that stops reading instead of
corrupting the stream, and the input and update queues are larger.
- Host: lobby changes are sent at most every 250 ms instead of on every
change (a full lobby would otherwise send ~650 MB), in-game joins wait
in a queue instead of being dropped, the random name and colour
pickers stop looping forever once names or colours run out, machines
get 60 s to load a map, and the debug dump lists only the slots in
use (128 empty slots took seconds to log).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zero credit; both packets passed adversarial review and are byte-inert on
their complete consumer lists.
- game_allegiance.h owns the allegiance incident constants in HCEX's form
(unnamed enum, no count); game_allegiance.c's private enum with the
unattested tag and count is removed. 12 consumers are raw-identical apart
from C1 $L jump-table label renumbering in 5 objects (not scored).
- real_math.h P1a:
- seven assert macro families recovered from January string literals and
validated by converting hand expansions at 16 January sites;
- the nonuniform_cubic_spline body is replaced by the January-exact glow.c
body (this removes an uninitialised read);
- path_obstacle_avoidance loses its TU-local duplicate macro.
The complete 273-consumer list shows 0 changed rows. The count-selected
transport_endpoint.h hunk is held for the owner (review_real_math_h_real_math_P1).
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>
- rasterizer_xbox_dynavobgeom (admission, reviewer-amended): genuine shared
declarations replace the caller-local debug-options, window and pixel-shader
views; /Od-attested vector_from_points3d/dot_product3d calls and HCEX locals
in the unlit draw; the HCEX function-static `warned` latch; MAX() for the
/Od ?: store; nine D3DINLINE wrappers static per cachebeta; rejection retired.
- damage (reviewer B+): _object_damage_body exact (1360); four statics per
cachebeta; park retired. The AOE header-helper form (patch A) stays held.
- players: January-static storage (3 rows) plus the COMMON tentative
definitions of players_globals/team_data (January linker_common records).
- hs: 839 January-static storage flags and the /Od-attested static helper
hs_enumerate_scenario_data replacing 11 hand expansions (reviewer-amended;
the real_random_range cleanup stays held with the P1 class).
- hs_scenario_definitions: first-party static byte_swap_script_syntax_data.
- player_profile (HCEX profile_color_table) and main (/Od main_exit shape):
reviewed zero-credit packets; three parks re-baselined.
Gate (full ninja, objdiff 3.3.1): Halo objects 377 -> 378; stable diff +1
strict / 1,360 padded, 0 regressions; parks 86/0/0; admission 11/0/3/0; fake
scan 26; pytest 1157 passed; diff --check clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shared-header prerequisite packet:
- real_math.h: distance2d/distance3d become square_root(distance_squared*(a,b))
(/Od build, no local); project_point2d takes short mapping locals and a
single ?: store (/Od-attested, equal to January's selected copy);
lock/unlock_global_random_seed move from the 2026-09-04 reconstruction
header random_math.h into real_math.h's existing RANDOM_MATH.C prototype
section (main.c calls them without random_math.h).
- action_vehicle, path_obstacles, decals drop their '#define x x_inline'
renames and hand-written NODUP helper copies; the header inlines now emit
byte-identical copies of January's selected sections.
- object_lights and players adopt the /Od caller spellings (0x5d0d50).
Full ninja + rename-stable 8,245-function diff: +2 exact
(_player_set_action_result, _convex_hull2d_perimeter; park retired),
0 regressions. _ai_test_line_of_sight park remeasured (size/relocations
unchanged, fuzzy 97.98 -> 97.92).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whole-object admission of three objects whose functions were already
strict exact (13/13, 15/15, 16/16). No new code bytes are credited.
Symbol ownership (January cachebeta.pdb publics, no cross-TU refs):
- symbols.json marks _action_flee_find_flee_position,
_action_flee_current_position_exposed, _action_flee_at_flee_position
and _action_guard_speak_post_combat static, matching January's
storage class.
- aim_assist.c: compute_composite_attenuation is public in January, so
drop `static` from its prototype and definition.
Genuine helper-provider repairs (selected-provider LNK2005 fixes):
- actor_moving.c drops its hand-written NODUP arccosine and the
arccosine_inline rename; the one acos site becomes
arccosine(facing_dot). The /Od build (halo_cache_symbols.exe) calls
an actor_moving-local arccosine COMDAT at exactly this site
(0x465510 -> 0x46d330), and January's actor_moving owns _arccosine.
All 36 actor_moving rows are unchanged, including the 2,752-byte
_actor_move_calculate_movement and _arccosine.
- path_obstacles.c drops its hand-written NODUP set_real_point2d and
rename; its project_point3d copy now calls set_real_point2d as the
real_math.h inline does. All 16 rows unchanged.
After both, no object on the board defines _arccosine or
_set_real_point2d NODUP.
Verification: full ninja; stable diff gained 0 / regressions 0;
parks 103 active / 0 stale / 0 invalid; admission audit 39 candidates,
0 contradicted, 0 revoked; object audit PASS and selected-provider link
PASS (both orders) for all three; fake scan and castcheck unchanged;
pytest 1154 passed; halobetacache linked files 332 -> 335.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Structural lane batch 6: +1,632 padded / +1,590 meaningful bytes strict
exact (4 functions), zero stable-verdict regressions across the full
board (the header consumer sweep).
- rasterizer_geometry.h/.c: uncompress_int32_to_real_vector3d returns
real_vector3d by value. The debug build shows the struct-return copy
at all 16 call sites, and HCEX lists a single parameter. Callers in
rasterizer.c, rasterizer_lights.c and rasterizer_xbox_lights.c assign
the result directly and drop their scratch buffers. Exact:
_compress_real_vector3d_to_int32 and _clamp. The parked
_rasterizer_sun_glow_draw is re-measured (same size, fuzzy
95.815 -> 95.817).
- game/players.h: declare players_debug_render(void), a public players.c
function that render_debug.c called undeclared (C4013). Disclosure: the
by-value prototype drops one declared name, which alone flips
_rasterizer_frame_statistics_draw's field-load order. This genuine
missing prototype restores that TU's name count. Both edits are
independently evidenced and landed together; the full build shows no
regression.
- weapons.c: _weapon_magazine_finish_reload reads magazine->rounds_loaded
(/Od 0x6a4590).
- rasterizer_xbox_hardware_geometry.c: _rasterizer_triangle_buffer_new
follows the debug build (0x7eaca0), including the index-buffer Unlock
with its HRESULT check. This removes the _ReadWriteBarrier/pragma/goto
steering.
- parked.json: unpark the three newly exact parked functions.
Gate: ninja PASS, parks 109/0/0, admission 0 contradicted/0 revoked,
provider link PASS, casts and fake scan unchanged, pytest 1152 passed /
5 skipped / 26 subtests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>