Why Changing Halo PC Vehicle Placements Didn't Change the Vehicles
While building custom Race tracks for Halo PC, I wanted each track to control its own vehicle layout.
That meant choosing which vehicles appeared, how many each team received, and where they spawned. The position side was straightforward enough. Halo PC stores vehicle placements in the scenario tag, alongside a palette index identifying the vehicle assigned to each one.
So, the obvious plan was to rewrite those placements.
It was also wrong.
The values changed correctly in memory, and the debug output showed the new palette indices exactly where expected. Halo PC Race simply ignored them when deciding which vehicles to create.
Finding the structure that Race actually reads took the investigation somewhere else entirely.
The reasonable first assumption
The scenario tag contains a vehicles block beginning at offset:
0x240 Each entry is 0x78 bytes and contains the vehicle's position, rotation, team value, spawn flags, and palette index. The palette index sits at the beginning of the entry:
ScenarioVehicle + 0x00 = vehicle palette index
ScenarioVehicle + 0x08 = position
ScenarioVehicle + 0x14 = rotation On paper, this looked like the right place to work. Change the palette index from a Warthog to a Rocket Warthog, and the next vehicle spawned at that location should become a Rocket Warthog.
The position writes worked exactly as expected, but the type writes did not affect the vehicles Halo PC Race created.
I could replace every stored palette index, read each one back, and confirm that the new values remained in memory. Yet Halo PC Race continued to spawn the vehicle type selected by the active gametype.
At first, I suspected another scenario-side structure was overriding it.
The Race vehicle flags were another dead end
Halo PC scenarios also contain netgame flags with type 4, commonly described as Race vehicle markers. These are separate from the ordinary vehicle placements, so they looked like another possible source of vehicle-type information.
Perhaps each flag paired a location with a vehicle category. Race could then ignore the placement type and read its choice from the corresponding flag instead.
Well, that theory did not survive inspection.
I scanned every 16-bit field from offset +0x10 through +0x92 across all 16 type-4 flags on the test map. None varied between flags, and a per-flag vehicle selector has to vary somewhere.
The positions were useful, but the remaining data provided no type information, while nearest-placement comparisons failed to reveal a clean relationship with scenario vehicle entries.
The type-4 flags appeared to be position markers, which meant the actual vehicle choice still had to come from somewhere else.
The clue was in the gametype
The breakthrough came from testing the loaded Race variant without any scenario-editing script active.
The map contained six native chaingun Warthog placements. The active Halo PC Race variant requested seven Warthogs, and a 12-player test produced seven of them.
That result ruled out the native per-type placement count as the controlling limit. Halo PC had created more Warthogs than the scenario contained Warthog-specific placements.
The important distinction became clear:
The scenario supplies available positions.
The loaded gametype supplies vehicle types and counts.
Halo PC Race treats the scenario's vehicle placements as a shared, fungible position pool. It takes whatever mix the gametype requests and assigns those vehicles to as many available positions as it needs.
That also explained an earlier, confusing result. When I changed every placement to Rocket Warthog while the gametype allowed only chaingun Warthogs, Halo PC still spawned a chaingun Warthog.
The palette writes were real. They were simply irrelevant to Race.
Finding the loaded Halo PC gametype
The next task was locating the active gametype variant in the retail Halo PC dedicated server.
This distinction matters because the project runs against retail haloded.exe, not the Halo Custom Edition dedicated server. Community references often document equivalent Custom Edition structures, but their absolute addresses do not apply to this binary.
For retail Halo PC, the loaded gametype begins at:
0x671340 I verified the location using several fields at once rather than trusting a single plausible value:
The UTF-16 name matched the loaded HRLRace variant.
The gametype field at +0x30 contained 5, meaning Race.
The HUD indicator at +0x3C matched Nav Points.
The score limit at +0x58 matched the value selected in the editor.
The address also remained stable across four server restarts during the vehicle-field tests.
The red and blue vehicle settings live at:
0x671340 + 0x60 = red vehicle counts
0x671340 + 0x64 = blue vehicle counts Each team therefore receives one packed DWORD containing its complete vehicle configuration for the current Halo PC gametype.
Mapping the packed vehicle counts
The vehicle DWORD was not a simple enabled-or-disabled bitmask. It contains a collection of three-bit fields, with each field holding a literal count from 0 through 7.
I mapped the fields by changing one vehicle category at a time in the variant editor, restarting the server, and comparing the resulting DWORD against a true zero-count baseline.
The final retail Halo PC layout is:
Bits 0-3 unknown, observed baseline value 8
Bits 4-6 Warthog
Bits 7-9 Ghost
Bits 10-12 Tank / Scorpion
Bits 13-15 Rocket Warthog
Bits 16-18 Banshee
Bits 19-21 Turret Each category receives three bits, so its maximum representable count is seven. That matches the limit exposed by the gametype editor.
The lower four bits are different. They have remained at a baseline value of 8 throughout testing, but their purpose is still unknown, so the script leaves them alone.
Proving the values were live
Reading a convincing structure is useful, but it does not prove that Halo PC consults it during Race setup.
So, I wrote a temporary Phasor script that changed both team DWORDs directly. The requested mix was five Warthogs and one Ghost per team.
The resulting value was:
Baseline 8
Warthog: 5 × 16 80
Ghost: 1 × 128 128
---
Total 216 (0xD8) There was no gametype-file edit and no server restart. I reloaded the script, started a fresh round, and Halo PC Race spawned five Warthogs and one Ghost for each team.
That was the confirmation I needed: the DWORDs were not merely copies for display or networking. Halo PC's native Race logic was reading them while building the vehicle set.
Turning the discovery into a module
The production module now accepts a per-track configuration like this:
GametypeVehicles.new({
red = {
warthog = 5,
ghost = 1,
},
blue = {
warthog = 5,
ghost = 1,
},
}) It uses read-modify-write operations so the unidentified lower bits and every unconfigured vehicle category remain unchanged. Invalid category names or counts outside the 0-to-7 range produce warnings instead of corrupting the packed value.
The module applies its settings during both script load and the start of each new game. Writing at load time handles a script reloaded during an active round, while writing again at round start keeps the desired values in place for later rounds.
On unload, it restores the original DWORD snapshots.
Of course, restoring the settings does not remove vehicles that have already spawned. Those remain until Halo PC performs its normal round cleanup.
The count is a ceiling, not a command
One question remained after finding the count fields: if the gametype requests seven Warthogs, does Halo PC create all seven immediately?
No. Even with the count set to seven, vehicles appeared progressively as players joined, and their selection depended on proximity to player spawn positions.
The packed value therefore acts as a ceiling. It tells Halo PC Race how many vehicles of each category may exist, but another native system decides when and where to instantiate them.
That behaviour is still unresolved. Finding it will require following references to the two vehicle DWORDs in Ghidra and locating the per-join or per-spawn trigger that consumes them.
There is a second limit too. Raising a category above seven is not a matter of changing one comparison, because the three-bit fields are packed directly beside one another. Widening one would shift every field that follows and require corresponding changes wherever Halo PC extracts those values.
For now, seven per category is the real limit.
What Halo PC Race actually reads
The original vehicle-placement theory was reasonable because the scenario contains a type field, the writes succeeded, and other systems use similar data directly. Race does something different.
The scenario provides a collection of positions. The loaded Halo PC gametype supplies the requested vehicle mix through two packed DWORDs, and Race combines the two at runtime.
That separation is useful now that it is understood. A track script can relocate the available positions independently from the gametype module that selects vehicle types and quantities.
It also leaves one particularly interesting problem for later: making Halo PC Race create the entire requested fleet immediately instead of waiting for players to join.
Honestly, solving the vehicle selection was only the beginning. Every spawned vehicle still shares Halo PC's fixed 2,048-object table with checkpoint markers, portal art, equipment, and everything else on the map.
That became the next limit.