Reconstruct remaining actors functions

This commit is contained in:
Jonas Volman committed 2026-09-02 21:18:27 -07:00
1 parent b106613588
commit 94de711dc1
9 files changed
+1094 -17

No files matched your search

+38
View File
@@ -2210,6 +2210,44 @@
},
"objdiff_percent": 54.82266
}
},
{
"unit": "source/ai/actors",
"function": "_actor_place",
"class": "register-allocation",
"evidence": "Fuzzy-parked after reconstructing the complete typed actor-placement path from January disassembly and independent HCEA behavior. The retained ordinary C validates and optionally upgrades the actor variant, builds the unit placement, reports January's exact unit/actor creation diagnostics, reads squad starting state, customizes and creates the actor, cleans up on failure, and verifies activation. Target is 800 padded bytes and the candidate 816, with all 41 relocation identities preserved; objdiff reports 90.887215 percent similarity. The common prefix and call topology are close, while the residual begins with VC7's choice of callee-saved registers and a four-byte frame-size difference. Bounded natural declaration-lifetime, branch, and local-placement forms did not close that allocation tie. No register hint, volatile access, barrier, pragma, forced inline, assembly, raw offset, fake dependency, or nonsensical branch is retained. Reopen only for authoritative January source/local records or a natural same-compiler donor explaining the remaining allocation.",
"measurements": {
"target": {
"size": 800,
"relocation_count": 41,
"normalized_sha256": "a4258acc637a3ae90493b151b147e75fea1243d941bcc699f9ec7463542ec5fb"
},
"base": {
"size": 816,
"relocation_count": 41,
"normalized_sha256": "ff120b98736b6eb6c41f5c41c8c5e33f6a4e15a5179d42a938b8c320f1ec6a2f"
},
"objdiff_percent": 90.887215
}
},
{
"unit": "source/ai/actors",
"function": "_actor_input_update",
"class": "unclassified",
"evidence": "Fuzzy-parked after reconstructing the full typed actor-input refresh from January disassembly, January relocation/assert/string topology, independent HCEA behavior, and the recovered Claude draft as secondary evidence. The retained ordinary C updates swarm centers and members, samples individual or parent-vehicle state, handles vehicle roles and encounter migration, diagnoses flying/3D-position mismatches, scans attached melee/projectile threats, refreshes biped path state and orientation vectors, validates every resulting normal, and records vitality. Target is 2384 padded bytes and the candidate 2400, with all 89 relocation identities preserved; objdiff reports 94.84425 percent similarity. The residual spans several VC7 allocation and instruction-scheduling choices rather than one isolated source construct, so it remains fail-closed as unclassified. Bounded natural condition, declaration-lifetime, encounter-migration, and vector-expression forms did not close it; an earlier equal-size form was rejected because it expressed January's behavior less faithfully. No register hint, volatile access, barrier, pragma, forced inline, assembly, raw offset, fake dependency, or nonsensical control flow is retained. Reopen for authoritative January source/local records or a natural same-compiler donor.",
"measurements": {
"target": {
"size": 2384,
"relocation_count": 89,
"normalized_sha256": "bf2e87448623ee925aa42e94ee93f1e1ebc439fd58971dba93e1a2b993c0d9b8"
},
"base": {
"size": 2400,
"relocation_count": 89,
"normalized_sha256": "7820324643af007011e488b773988db634efcf67378be6c4a78ff5ee20a1bb54"
},
"objdiff_percent": 94.84425
}
}
]
}
@@ -0,0 +1,105 @@
# `actors.obj` remaining-unwritten wave (2026-09-02)
## Scope and baseline
- Worktree: `worktrees/actors-remaining-wave-20260902`
- Branch: `agent/actors-remaining-wave-20260902`
- Pinned starting commit: `5d663917d602ae8a18ba6e0e0b38fc1a3f2d24f3`
- Owned implementation: `source/ai/actors.c`, plus the smallest owner-header
corrections needed by the recovered typed source and removal of one now-
redundant prototype from `source/ai/actions.c`.
- Baseline gate: 65 exact, 7 residual, 4 unwritten of 76 functions.
- Final gate: 67 exact, 9 residual, 0 unwritten of 76 functions.
The four missing functions were reconstructed smallest/high-confidence first.
January's split COFF object and its relocations, assertions, constants, and
diagnostic strings are the byte authority. The independent HCEA lifts in
`halocea-reference/src/actor_died.c`, `actor_new.c`, `actor_place.c`, and
`actor_input_update.c` supplied behavior and naming evidence. The recovered
Claude draft at `C:/halo-worktrees/claude-d3dx-projections-5/src/halo/ai/actors.c`
was treated only as secondary evidence and checked against January.
## Results
| Function | Target bytes | Result | Evidence |
| --- | ---: | --- | --- |
| `_actor_died` | 832 | exact | Full death-wildfire, grenade, weapon-load/ammo, actor deletion, and encounter-status path. |
| `_actor_new` | 944 | exact | Full allocation, actor/meta/state/control/emotion/debug initialization and actor-type initialization. |
| `_actor_place` | 800 | fuzzy parked | Complete typed placement path; 816-byte candidate, 41/41 relocation identities, 90.887215% objdiff. |
| `_actor_input_update` | 2,384 | fuzzy parked | Complete typed swarm/unit/vehicle/encounter/threat/path/orientation/vitality refresh; 2,400-byte candidate, 89/89 relocation identities, 94.84425% objdiff. |
The strict gain is two functions and 1,776 padded bytes. More importantly, all
four formerly unwritten functions now have complete, ordinary, typed C. No
inline assembly, forced inlining, register/volatile steering, barriers,
pragmas, raw-offset access, fake dependencies, or deliberately nonsensical
control flow were retained.
## Honest residuals
`actor_place` retains January's assertions, optional major upgrade, typed tag
lookups, object placement, exact failure diagnostics, squad/start-state
selection, customization, actor creation, cleanup, and activation check. Its
target/candidate section sizes are 800/816 with identical ordered relocation
destinations. The close common prefix diverges when VC7 chooses its callee-save
allocation and a four-byte-larger local frame. Natural declaration-lifetime,
branch, and local-placement trials did not close the residual, so it is parked
as `register-allocation`.
`actor_input_update` retains the complete January call and diagnostic topology:
swarm center/member sampling, individual and parent-vehicle sampling, AI driver
and gunner roles, fake-encounter migration/restoration, flying versus 3D firing
position diagnostics, attached melee/projectile threat scanning, biped path
state, orientation-frame construction, normal validation, and vitality. Its
target/candidate sizes are 2,384/2,400 with identical ordered relocation
destinations. Several natural condition, declaration-lifetime,
encounter-migration, and vector-expression forms were bounded and reverted.
An equal-size intermediate was also rejected because it represented January's
behavior less faithfully. Since the remainder spans allocation and scheduling
rather than one isolated source construct, the function is conservatively
parked as `unclassified`.
## Owner-header corrections and blast audit
- `actor_placement.h`: corrected `actor_place`'s squad index to the target's
full-width `long`. It has two direct C includers (`actors.c`, `encounters.c`).
- `actors.h`: placed the recovered `actor_clear_discarded_firing_positions`
declaration in the closest owning header because its implementation file has
no header. It has 42 direct C includers. The duplicate declaration formerly
kept in `actions.c` was removed; that file already includes `actors.h`, and
its isolated gate remains 58/63 exact with no changed verdict.
- `ai_debug.h`: gave the final debug counter its observed semantic name without
changing layout. It has 15 direct C includers.
- `ai_scenario_definitions.h`: named the braindead encounter flag and recovered
the exact 0x1C `actor_starting_location` layout with offset assertions. It has
13 direct C includers.
- `weapons.h`: placed `weapon_set_current_amount` in its owning header. It has
16 direct C includers, including `units.c`.
- `projectiles.h` was audited because actors now reads the existing typed
projectile flags, but no tracked header change was retained. It has seven
direct C includers, including `units.c`.
A temporary, semantically inert projectile-field offset assertion was removed
before admission. Its declaration position changed `actor_input_update`'s
normalized candidate hash and objdiff score even though the implementation and
layout were unchanged, reproducing the known VC7 header-position sensitivity.
The parking record uses the final no-extra-header-declaration build.
The final all-unit stable-verdict scan compares the pinned baseline and
candidate after full builds, so it covers transitive code-generation effects
from every header above. It records only the two intended `actors.obj` gains
and zero regressions. `units.obj` remains 189/189 exact.
## Verification
- `python tools/campaign/gate.py source/ai/actors --all`:
67 exact, 9 residual, 0 unwritten.
- `python -m tools.campaign.stable_verdicts diff ...`:
`_actor_new` +944, `_actor_died` +832; 2 gains / 1,776 bytes; zero
regressions.
- `python tools/campaign/gate.py source/units/units --all`:
189 exact, 0 residual, 0 unwritten.
- `ninja all_source`: passed.
- `python -m pytest -q`: 261 passed.
- `python -m tools.parked_functions`: both new parking records active, with no
stale or invalid measurements.
- `git diff --check`: passed.
-4
View File
@@ -631,10 +631,6 @@ short actor_select_firing_position(
struct actions_firing_position_search_workspace *workspace,
boolean *position_flags);
void actor_clear_discarded_firing_positions(
long actor_index,
boolean temporary_only);
void actor_discard_firing_position(
long actor_index,
short firing_position_index,
+1 -1
View File
@@ -9,7 +9,7 @@ struct actor_starting_location;
long actor_place(
long actor_variant_definition_index,
long encounter_index,
short squad_index,
long squad_index,
struct actor_starting_location *starting_location,
boolean upgrade_major,
short initial_variant);
+919 -5
View File
@@ -298,8 +298,11 @@ symbols in this file:
#include "encounters.h"
#include "cseries/errors.h"
#include "game/game.h"
#include "game/game_allegiance.h"
#include "game/players.h"
#include "items/equipment_definitions.h"
#include "items/projectiles.h"
#include "items/weapons.h"
#include "objects/damage.h"
#include "saved games/game_state.h"
#include "scenario/scenario.h"
@@ -345,6 +348,25 @@ enum actor_default_state
enum
{
_rgb_color_interpolation_hsv_bit = 0,
_ai_reference_squad_bit = 15,
};
/* vehicle_definition.flags; the shared vehicle tag header has not yet
* reconstructed these AI-facing constants. */
enum
{
_vehicle_ai_weapon_cannot_rotate_bit = 8,
_vehicle_ai_driver_enable_bit = 11,
_vehicle_ai_driver_flying_bit,
_vehicle_ai_driver_nondirectional_bit,
_vehicle_ai_driver_hovering_bit,
};
/* projectile_datum.flags; projectiles.c currently owns the otherwise private
* enum, so actors keeps the shared January bit spelling local for now. */
enum
{
_projectile_will_super_explode_bit = 7,
};
/* January names these limits in its assert and error strings; the shared
@@ -445,6 +467,20 @@ enum
((struct swarm_component_datum *)datum_get( \
swarm_component_data, (index)))
/* These object chains are polymorphic. The accessors preserve January's
* broad verification masks while giving each use site the complete layout it
* needs after checking object.type. */
#define actor_parent_vehicle_candidate_get(index) \
((struct unit_datum *)object_get(index))
#define actor_swarm_member_get(index) \
((struct biped_datum *)object_get_and_verify_type( \
(index), \
_object_mask_unit))
#define actor_attached_threat_get(index) \
((struct projectile_datum *)object_get(index))
#define realcmp(a, b) (fabs((a) - (b)) < _real_epsilon)
#define prop_orphaned(prop) \
((prop)->state >= _prop_state_uninspected_orphan && \
(prop)->state <= _prop_state_inspected_orphan)
@@ -521,10 +557,13 @@ typedef char actor_datum_meta_first_prop_index_offset_assert[
struct vehicle_definition
{
struct unit_definition unit;
byte __unknown2F0[0x94];
unsigned long flags;
byte __unknown2F4[0x90];
real ai_destination_radius;
};
typedef char vehicle_definition_flags_offset_assert[
offsetof(struct vehicle_definition, flags) == 0x2F0 ? 1 : -1];
typedef char vehicle_definition_ai_destination_radius_offset_assert[
offsetof(struct vehicle_definition, ai_destination_radius) == 0x384 ? 1 : -1];
@@ -539,12 +578,16 @@ struct ai_globals_service_data
boolean time_given_this_frame;
short last_highest_service_timer;
short current_highest_service_timer;
byte reserved008[0x3AC];
boolean grenades_enabled;
};
typedef char ai_globals_service_data_time_given_offset_assert[
offsetof(struct ai_globals_service_data, time_given_this_frame) == 0x3 ? 1 : -1];
typedef char ai_globals_service_data_current_highest_offset_assert[
offsetof(struct ai_globals_service_data, current_highest_service_timer) == 0x6 ? 1 : -1];
typedef char ai_globals_service_data_grenades_enabled_offset_assert[
offsetof(struct ai_globals_service_data, grenades_enabled) == 0x3B4 ? 1 : -1];
/* ai_profile.c owns the profile globals; actors bumps only the January
* authenticated per-tick meters, so model the 0x88-byte meter records that
@@ -570,10 +613,8 @@ typedef char ai_profile_meter_globals_units_active_offset_assert[
/* ---------- prototypes */
/* January keeps this function private to ACTORS.C. Its body remains one of
* this object's explicitly unwritten functions, so the forward declaration
* stays TU-local until the static definition is reconstructed. */
void actor_input_update(
/* January keeps this function private to ACTORS.C. */
static void actor_input_update(
long actor_index);
/* ---------- globals */
@@ -1558,6 +1599,419 @@ void actor_input_sample_position(
return;
}
static void actor_input_update(
long actor_index)
{
struct actor_datum *actor = actor_get(actor_index);
struct actor_definition *actor_definition =
actor_definition_get(actor->meta.definition_index);
if (actor->meta.swarm)
{
struct swarm_datum *swarm = swarm_get(actor->meta.swarm_cache_index);
real_point3d *swarm_center = &swarm->swarm_center;
long component_index;
*swarm_center = *global_origin3d;
for (
component_index = 0;
(short)component_index < swarm->unit_count;
component_index++)
{
struct swarm_component_datum *component =
swarm_component_get(
swarm->component_indices[(short)component_index]);
long component_datum_index =
swarm->component_indices[(short)component_index];
long unit_index = swarm->unit_indices[(short)component_index];
struct biped_datum *member = actor_swarm_member_get(unit_index);
struct swarm_component_datum *sampled_component =
swarm_component_get(component_datum_index);
long surface_index = NONE;
if (member->object.type == _object_type_biped)
{
surface_index = member->biped.support_surface_index;
}
object_get_origin(unit_index, &sampled_component->position);
sampled_component->surface_index = surface_index;
swarm_center->x += component->position.x;
swarm_center->y += component->position.y;
swarm_center->z += component->position.z;
}
if (swarm->unit_count > 0)
{
scale_vector3d(
(real_vector3d *)swarm_center,
1.f / (real)swarm->unit_count,
(real_vector3d *)swarm_center);
}
csmemset(&actor->input.position, 0, sizeof(actor->input));
actor->input.vehicle_index = NONE;
actor->input.pathfinding_surface_index = NONE;
if (actor->meta.swarm_unit_index == NONE)
{
return;
}
actor_input_sample_position(
actor_index,
actor->meta.swarm_unit_index,
&actor->input.position);
return;
}
{
struct unit_datum *unit = unit_get(actor->meta.unit_index);
long vehicle_index = unit->object.parent_object_index;
struct unit_datum *vehicle = vehicle_index == NONE
? NULL
: actor_parent_vehicle_candidate_get(vehicle_index);
real_point3d center_of_mass;
actor_input_sample_position(
actor_index,
actor->meta.unit_index,
&actor->input.position);
unit_get_center_of_mass(actor->meta.unit_index, &center_of_mass);
actor->input.underwater = scenario_location_underwater(
&actor->input.position.body_location,
&center_of_mass,
NULL);
actor->state.flying =
TEST_FLAG(actor_definition->flags, _actor_definition_flying_bit);
if (vehicle && vehicle->object.type == _object_type_vehicle)
{
struct vehicle_definition *vehicle_definition =
vehicle_specific_definition_get(vehicle->definition_index);
actor->input.vehicle_index = vehicle_index;
actor->input.vehicle_gunner_bombardment = FALSE;
actor->input.vehicle_gunner = FALSE;
actor->input.vehicle_driver_type = _actor_vehicle_driver_none;
if (vehicle->unit.driver_object_index == actor->meta.unit_index)
{
unsigned long vehicle_flags = vehicle_definition->flags;
actor->input.vehicle_driver_type =
_actor_vehicle_driver_unknown;
if (TEST_FLAG(vehicle_flags, _vehicle_ai_driver_enable_bit))
{
if (TEST_FLAG(vehicle_flags, _vehicle_ai_driver_flying_bit))
{
actor->state.flying = TRUE;
actor->input.vehicle_driver_type =
_actor_vehicle_driver_directional_flying;
}
else if (TEST_FLAG(
vehicle_flags,
_vehicle_ai_driver_nondirectional_bit))
{
actor->input.vehicle_driver_type =
TEST_FLAG(vehicle_flags, _vehicle_ai_driver_hovering_bit)
? _actor_vehicle_driver_hovering_ground
: _actor_vehicle_driver_nondirectional_ground;
}
}
}
if (vehicle->unit.gunner_object_index == actor->meta.unit_index)
{
actor->input.vehicle_gunner = TRUE;
actor->input.vehicle_gunner_bombardment =
actor_combat_get_firing_variant_definition(actor_index)->
ranged_combat.weapon_bombardment_range > 0.f;
}
actor->input.vehicle_passenger =
actor->input.vehicle_driver_type <=
_actor_vehicle_driver_unknown;
if (vehicle->unit.fake_encounter_index != NONE)
{
long actor_encounter_index = actor->meta.encounter_index;
boolean migrate = FALSE;
if (DATUM_INDEX_TO_ABSOLUTE_INDEX(actor_encounter_index) ==
vehicle->unit.fake_encounter_index)
{
if (vehicle->unit.fake_squad_index != NONE &&
actor->meta.squad_index != vehicle->unit.fake_squad_index)
{
struct encounter_datum *encounter =
encounter_get(actor_encounter_index);
migrate = TRUE;
if (encounter->follow_target_type > 0)
{
struct squad_datum *actor_squad =
encounter_get_squad(
encounter,
actor->meta.squad_index);
struct squad_datum *vehicle_squad =
encounter_get_squad(
encounter,
vehicle->unit.fake_squad_index);
if (actor_squad->automatic_migration_target)
{
migrate =
vehicle_squad->automatic_migration_target == FALSE;
}
}
}
}
else
{
migrate = TRUE;
}
if (migrate)
{
if (!actor->meta.stored_prevehicle_encounter)
{
actor->meta.prevehicle_encounter_index =
actor_encounter_index;
actor->meta.prevehicle_squad_index =
actor->meta.squad_index;
actor->meta.stored_prevehicle_encounter = TRUE;
if (actor_encounter_index != NONE)
{
encounter_get(actor_encounter_index)->
is_prevehicle_encounter = TRUE;
}
}
actor_change_encounter(
actor_index,
vehicle->unit.fake_encounter_index,
vehicle->unit.fake_squad_index);
}
}
}
else
{
boolean stored_prevehicle_encounter =
actor->meta.stored_prevehicle_encounter;
actor->input.vehicle_index = NONE;
actor->input.vehicle_driver_type = _actor_vehicle_driver_none;
actor->input.vehicle_passenger = FALSE;
actor->input.vehicle_gunner = FALSE;
if (stored_prevehicle_encounter)
{
actor_change_encounter(
actor_index,
actor->meta.prevehicle_encounter_index,
actor->meta.prevehicle_squad_index);
actor->meta.stored_prevehicle_encounter = FALSE;
}
}
if (actor->meta.encounter_index != NONE)
{
struct actor_debug_info *actor_debug_info =
&actor_debug_array[DATUM_INDEX_TO_ABSOLUTE_INDEX(actor_index)];
struct encounter_definition *encounter_definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters,
DATUM_INDEX_TO_ABSOLUTE_INDEX(
actor->meta.encounter_index),
struct encounter_definition);
if ((actor->state.flying &&
!TEST_FLAG(
encounter_definition->flags,
_encounter_3d_firing_positions_bit)) ||
(!actor->state.flying &&
TEST_FLAG(
encounter_definition->flags,
_encounter_3d_firing_positions_bit)))
{
if (actor_debug_info->firing_position_type_mismatch_ticks < 150)
{
actor_debug_info->firing_position_type_mismatch_ticks++;
if (actor_debug_info->firing_position_type_mismatch_ticks == 150)
{
if (actor->state.flying)
{
error(
_error_silent,
"actor %s is flying but was placed in a non-3d-firing-position encounter (%s)",
tag_get_name(actor->meta.definition_index),
encounter_definition->name);
}
else
{
error(
_error_silent,
"actor %s is not flying but was placed in a 3d-firing-position encounter (%s)",
tag_get_name(actor->meta.definition_index),
encounter_definition->name);
}
}
}
}
else
{
actor_debug_info->firing_position_type_mismatch_ticks = 0;
}
}
actor->input.burning_to_death = unit->unit.flaming_death_delay > 0;
actor->input.melee_attacker_attached = FALSE;
actor->input.delayed_attached_projectile_index = NONE;
{
long threat_index = unit->object.first_child_object_index;
while (threat_index != NONE)
{
struct projectile_datum *threat =
actor_attached_threat_get(threat_index);
if (threat->object.type == _object_type_biped &&
game_team_is_enemy(
actor->meta.team_index,
threat->object.owner_team_index))
{
actor->input.melee_attacker_attached = TRUE;
}
else if (threat->object.type == _object_type_projectile &&
(TEST_FLAG(
threat->projectile.flags,
_projectile_will_super_explode_bit) ||
(actor->danger_zone.danger_type ==
_actor_danger_zone_projectile &&
threat_index == actor->danger_zone.object_index)))
{
actor->input.delayed_attached_projectile_index =
threat_index;
}
threat_index = threat->object.next_object_index;
}
}
actor->input.in_midair = FALSE;
actor->input.pathfinding_surface_index = NONE;
if (unit->object.type == _object_type_biped &&
actor->input.vehicle_index == NONE)
{
struct biped_datum *biped = biped_get(actor->meta.unit_index);
if (biped->biped.airborne_ticks >= 6)
{
actor->input.in_midair = TRUE;
}
actor->input.pathfinding_surface_index =
biped->biped.pathfinding_surface_index;
actor->input.pathfinding_point = biped->biped.pathfinding_point;
}
unit_get_facing_vector(
actor->input.vehicle_driver_type > _actor_vehicle_driver_none
? actor->input.vehicle_index
: actor->meta.unit_index,
&actor->input.facing_vector);
if (!actor->state.flying)
{
if (normalize2d((real_vector2d *)&actor->input.facing_vector) > 0.f)
{
actor->input.facing_vector.k = 0.f;
}
else
{
actor->input.facing_vector = *global_forward3d;
}
}
if (actor->input.vehicle_gunner)
{
struct unit_datum *gunner_vehicle =
vehicle_get(actor->input.vehicle_index);
struct vehicle_definition *gunner_vehicle_definition =
vehicle_specific_definition_get(
gunner_vehicle->definition_index);
if (TEST_FLAG(
gunner_vehicle_definition->flags,
_vehicle_ai_weapon_cannot_rotate_bit))
{
unit_get_facing_vector(
actor->meta.unit_index,
&actor->input.aiming_vector);
}
else
{
actor->input.aiming_vector =
gunner_vehicle->unit.aiming_vector;
}
}
else
{
actor->input.aiming_vector = unit->unit.aiming_vector;
}
actor->input.looking_vector = unit->unit.looking_vector;
normalize3d(cross_product3d(
global_up3d,
&actor->input.looking_vector,
&actor->input.looking_left_vector));
cross_product3d(
&actor->input.looking_vector,
&actor->input.looking_left_vector,
&actor->input.looking_up_vector);
match_assert_valid_real_normal3d(
"c:\\halo\\SOURCE\\ai\\actors.c",
3313,
&actor->input.facing_vector);
match_assert_valid_real_normal3d(
"c:\\halo\\SOURCE\\ai\\actors.c",
3314,
&actor->input.aiming_vector);
match_assert_valid_real_normal3d(
"c:\\halo\\SOURCE\\ai\\actors.c",
3315,
&actor->input.looking_vector);
if (!actor->state.flying)
{
match_vassert(
"c:\\halo\\SOURCE\\ai\\actors.c",
3318,
valid_real_normal2d(
(real_vector2d *)&actor->input.facing_vector),
csprintf(
temporary,
"%s: assert_valid_real_normal2d(%f, %f)",
"(real_vector2d *) &actor->input.facing_vector",
actor->input.facing_vector.i,
actor->input.facing_vector.j));
match_assert(
"c:\\halo\\SOURCE\\ai\\actors.c",
3319,
realcmp(actor->input.facing_vector.k, 0.0f));
}
actor->input.body_vitality = unit->object.body_vitality;
actor->input.shield_vitality = unit->object.shield_vitality;
actor->input.recent_body_damage = unit->object.recent_body_damage;
actor->input.recent_shield_damage = unit->object.recent_shield_damage;
}
return;
}
static void actor_clear_orders(
long actor_index)
{
@@ -1840,6 +2294,157 @@ void actor_unit_control_stop_animation_impulse(
return;
}
long actor_new(
long actor_variant_definition_index)
{
struct actor_variant_definition *actor_variant_definition;
struct actor_definition *actor_definition;
struct actor_debug_info *actor_debug_info;
struct actor_datum *actor;
long actor_definition_index;
long actor_index = NONE;
if (actor_variant_definition_index != NONE)
{
actor_variant_definition = actor_variant_definition_get(actor_variant_definition_index);
actor_definition_index = actor_variant_definition->actor_reference.index;
if (actor_definition_index != NONE)
{
actor_definition = actor_definition_get(actor_definition_index);
actor_index = datum_new(actor_data);
if (actor_index != NONE)
{
actor = actor_get(actor_index);
actor->meta.swarm = TEST_FLAG(actor_definition->flags, _actor_definition_swarm_actor_bit);
actor->meta.variant_definition_index = actor_variant_definition_index;
actor->meta.definition_index = actor_definition_index;
actor->meta.type = actor_definition->type;
actor->meta.unit_index = NONE;
actor->meta.unique_leader = FALSE;
actor->meta.encounter_index = NONE;
actor->meta.squad_index = NONE;
actor->meta.platoon_index = NONE;
actor->meta.encounterless = FALSE;
actor->meta.disconnected_encounter_index = NONE;
actor->meta.disconnected_squad_index = NONE;
actor->meta.swarm_unit_count = 0;
actor->meta.swarm_original_unit_count = 0;
actor->meta.swarm_unit_index = NONE;
actor->meta.swarm_cache_index = NONE;
actor->meta.frozen = TRUE;
actor->meta.active = FALSE;
actor->meta.last_active_time = NONE;
actor->meta.dormant = TRUE;
actor->meta.dormant_desire = TRUE;
actor->meta.service_timer = 0;
actor->meta.first_prop_index = NONE;
actor->meta.interesting_orphan_index = NONE;
actor->firing_positions.current_position_index = NONE;
actor->state.initial_state = NONE;
actor->state.default_state = NONE;
actor->state.last_default_state_time = NONE;
actor->state.command_list_immediate = FALSE;
actor->state.command_list_index = NONE;
actor->state.last_command_list_time = NONE;
actor->state.action = _actor_action_none;
actor->state.mode = _actor_mode_alert;
actor->state.combat_status = _actor_combat_status_none;
actor->state.artificial_combat_status = _actor_combat_status_none;
actor->state.suspicion_combat_status = _actor_combat_status_none;
actor->state.uncertain_combat_timer = NONE;
actor->state.searching = FALSE;
actor->state.flying = TEST_FLAG(actor_definition->flags, _actor_definition_flying_bit);
actor->input.pathfinding_surface_index = NONE;
actor->input.vehicle_index = NONE;
actor->external_orders.defending = FALSE;
actor->external_orders.pursuit_is_coordinator = FALSE;
actor->external_orders.pursuit_group_prop_index = NONE;
actor->external_orders.desired_target_type = _actor_target_none;
actor->external_orders.conversation_index = NONE;
csmemset(&actor->emotions, 0, sizeof(actor->emotions));
actor->emotions.last_active_cover_seeking_time = NONE;
actor->emotions.last_melee_check_time = NONE;
actor->emotions.last_melee_attack_time = NONE;
actor->emotions.last_defensive_cover_seeking_time = NONE;
actor->emotions.last_vehicle_check_time = NONE;
actor->emotions.last_vehicle_charge_time = NONE;
actor->emotions.last_flee_failed_time = NONE;
actor->emotions.corpse_ignore_time = NONE;
actor->emotions.unopposable_friend_ignore_time = NONE;
actor->emotions.unopposable_retreat_prop_index = NONE;
actor->emotions.unopposable_retreat_start_time = NONE;
actor->emotions.original_body_vitality = 1.f;
actor->emotions.vehicle_ignore_index = NONE;
actor->emotions.vehicle_ignore_time = NONE;
actor->emotions.flee_with_friends_disable_time = NONE;
if (actor_definition->moving.glass_ignorance_chance > 0.f)
{
actor->emotions.ignorant_of_broken_surfaces =
real_seed_random(get_global_random_seed_address()) <
actor_definition->moving.glass_ignorance_chance;
}
actor->orders.look.primary_priority = _primary_priority_none;
actor->orders.move.destination.destination_type = _destination_none;
actor->control.path.destination_orders.destination_type = _destination_none;
actor->control.path.destination_orders.ignore_target_object_index = NONE;
actor->control.path.destination.surface_index = NONE;
csmemset(&actor->control.path.path, 0, sizeof(actor->control.path.path));
actor->control.moving = FALSE;
actor->control.moving_forced_by_aiming = FALSE;
actor->control.fire_state = _actor_fire_state_holding;
actor->control.fire_state_timer = 0;
actor->control.burst_disable_timer = 0;
actor->control.trigger_delay_timer = 0;
actor->control.blocked_communication_timer = 0;
actor->control.current_fire_target_timer = 0;
actor->control.current_fire_target_prop_index = NONE;
actor->control.grenade_last_check_time = NONE;
actor->control.grenade_current_prop_index = NONE;
csmemset(
actor->control.vector_avoidance_clear_times,
NONE,
sizeof(actor->control.vector_avoidance_clear_times));
actor->control.vector_avoidance_current_direction = NONE;
actor->control.vector_avoidance_sharp_turn_timer = NONE;
actor->control.secondary_look_type = _secondary_look_none;
actor->control.secondary_look_timer = 0;
actor->control.desired_aiming_vector = *global_forward3d;
actor->control.desired_facing_vector = *global_forward3d;
actor->control.desired_looking_vector = *global_forward3d;
actor->control.idle_vocalization_combat = FALSE;
actor->control.idle_vocalization_timer = TICKS_PER_SECOND;
actor->target.target_type = _actor_target_none;
actor->target.target_prop_index = NONE;
actor->target.target_last_visible_time = NONE;
actor->target.since_any_target_visible_timer = NONE;
actor_clear_discarded_firing_positions(actor_index, FALSE);
actor->firing_positions.pursuit_prop_index = NONE;
actor_debug_info =
&actor_debug_array[DATUM_INDEX_TO_ABSOLUTE_INDEX(actor_index)];
csmemset(actor_debug_info, 0, sizeof(*actor_debug_info));
actor_debug_info->last_path_refresh = NONE;
actor_debug_info->last_projectile_aiming_time = NONE;
actor_debug_info->last_vehicle_avoidance_time = NONE;
actor_debug_info->last_melee_time = NONE;
actor_debug_info->grenade_eval_time = NONE;
actor_debug_info->danger_avoidance_time = NONE;
actor_debug_info->charge_last_time = NONE;
actor_debug_info->field_19C = NONE;
actor_debug_info->vision_last_time = NONE;
actor_debug_info->num_debug_evaluations = NONE;
actor_type_initialize(actor_index);
}
}
}
return actor_index;
}
void actor_customize_unit(
long actor_variant_definition_index,
long unit_index)
@@ -2120,6 +2725,177 @@ long actor_create_for_unit(
return actor_index;
}
long actor_place(
long actor_variant_definition_index,
long encounter_index,
long squad_index,
struct actor_starting_location *starting_location,
boolean upgrade_major,
short initial_variant)
{
struct actor_variant_definition *actor_variant_definition;
struct actor_definition *actor_definition;
struct object_placement_data placement_data;
char encounter_name[256];
long unit_index;
match_assert("c:\\halo\\SOURCE\\ai\\actors.c", 603, starting_location);
objects_garbage_collection();
actor_variant_definition =
actor_variant_definition_get(actor_variant_definition_index);
if (upgrade_major)
{
actor_variant_definition_index =
actor_variant_definition->major_upgrade_reference.index;
match_assert(
"c:\\halo\\SOURCE\\ai\\actors.c",
613,
actor_variant_definition_index != NONE);
actor_variant_definition =
actor_variant_definition_get(actor_variant_definition_index);
}
actor_definition =
actor_definition_get(actor_variant_definition->actor_reference.index);
object_placement_data_new(
&placement_data,
actor_variant_definition->unit_reference.index,
NONE);
placement_data.position = starting_location->position;
vector3d_from_angle(&placement_data.forward, starting_location->facing);
placement_data.variant_number = initial_variant;
unit_index = object_new(&placement_data);
if (unit_index == NONE)
{
if (encounter_index == NONE)
{
csstrcpy(encounter_name, "<encounterless>");
}
else
{
ai_index_to_string(
DATUM_INDEX_NEW(
DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index),
FLAG(_ai_reference_squad_bit) |
(squad_index & UNSIGNED_CHAR_MAX)),
global_scenario_get(),
encounter_name,
sizeof(encounter_name));
}
error(
_error_silent,
"WARNING: cannot create unit for actor %s %s",
tag_name_strip_path(tag_get_name(actor_variant_definition_index)),
encounter_name);
return NONE;
}
if (encounter_index != NONE)
{
struct encounter_definition *encounter_definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters,
DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index),
struct encounter_definition);
if (encounter_definition)
{
/* Validate the squad reference before customization. */
(void)TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads,
squad_index,
struct squad_definition);
}
}
{
long actor_index;
boolean swarm =
TEST_FLAG(actor_definition->flags, _actor_definition_swarm_actor_bit);
boolean initially_braindead = FALSE;
short initial_state = actor_default_state_none;
short default_state = actor_default_state_none;
actor_customize_unit(actor_variant_definition_index, unit_index);
if (encounter_index != NONE)
{
struct encounter_definition *encounter_definition =
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters,
DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index),
struct encounter_definition);
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
&encounter_definition->squads,
squad_index,
struct squad_definition);
initial_state = squad_definition->initial_state;
default_state = squad_definition->default_state;
initially_braindead =
TEST_FLAG(encounter_definition->flags, _encounter_braindead_bit);
}
if (starting_location->initial_state > actor_default_state_none)
{
initial_state = starting_location->initial_state;
}
if (starting_location->default_state > actor_default_state_none)
{
default_state = starting_location->default_state;
}
actor_index = actor_create_for_unit(
swarm,
unit_index,
actor_variant_definition_index,
encounter_index,
squad_index,
FALSE,
NONE,
initially_braindead,
initial_state,
default_state,
starting_location->command_list_index,
starting_location->noncombat_sequence_id);
if (actor_index == NONE)
{
if (encounter_index == NONE)
{
csstrcpy(encounter_name, "<encounterless>");
}
else
{
ai_index_to_string(
DATUM_INDEX_NEW(
DATUM_INDEX_TO_ABSOLUTE_INDEX(encounter_index),
FLAG(_ai_reference_squad_bit) |
(squad_index & UNSIGNED_CHAR_MAX)),
global_scenario_get(),
encounter_name,
sizeof(encounter_name));
}
error(
_error_silent,
"WARNING: cannot create actor %s %s",
tag_name_strip_path(tag_get_name(actor_variant_definition_index)),
encounter_name);
object_delete(unit_index);
}
else
{
actor_verify_activation(actor_index);
}
return actor_index;
}
}
short actors_spawn_from_unit(
long unit_index,
long actor_variant_definition_index,
@@ -2559,6 +3335,144 @@ void actor_kill(
return;
}
void actor_died(
long actor_index)
{
struct actor_datum *actor = actor_get(actor_index);
struct actor_variant_definition *actor_variant_definition =
actor_variant_definition_get(actor->meta.variant_definition_index);
long encounter_index = actor->meta.encounter_index;
struct unit_datum *unit;
if (actor->state.mode == _actor_mode_combat &&
actor->state.combat_status >= _actor_combat_status_investigate)
{
unit = unit_get(actor->meta.unit_index);
if (unit_controllable(actor->meta.unit_index) &&
unit_inventory_get_weapon(
actor->meta.unit_index,
(word)unit_get(actor->meta.unit_index)->unit.current_weapon_index) != NONE &&
unit->unit.weapon_drop_delay_ticks > 0)
{
real death_wildfire_chance;
if (actor_variant_definition->ranged_combat.death_wildfire_chance < 0.1f)
{
death_wildfire_chance = 0.1f;
}
else if (actor_variant_definition->ranged_combat.death_wildfire_chance > 0.6f)
{
death_wildfire_chance = 0.6f;
}
else
{
death_wildfire_chance = actor_variant_definition->ranged_combat.death_wildfire_chance;
}
if (actor->emotions.berserk ||
(actor->control.current_fire_target_type > _actor_fire_target_none &&
actor->control.current_fire_target_distance < 3.f))
{
real boosted_chance = MIN(death_wildfire_chance * 4.f, 0.6f);
death_wildfire_chance = MAX(death_wildfire_chance, boosted_chance);
}
if (real_seed_random(get_global_random_seed_address()) < death_wildfire_chance)
{
real death_wildfire_time;
if (actor_variant_definition->ranged_combat.death_wildfire_time == 0.f)
{
death_wildfire_time = real_random_range(0.8f, 1.3f);
}
else if (actor_variant_definition->ranged_combat.death_wildfire_time < 0.8f)
{
death_wildfire_time = 0.8f;
}
else if (actor_variant_definition->ranged_combat.death_wildfire_time > 1.3f)
{
death_wildfire_time = 1.3f;
}
else
{
death_wildfire_time = actor_variant_definition->ranged_combat.death_wildfire_time;
}
{
short control_ticks = (short)(death_wildfire_time * TICKS_PER_SECOND);
unit_persistent_control(
actor->meta.unit_index,
control_ticks,
FLAG(_unit_control_weapon_primary_trigger_bit));
unit->unit.weapon_drop_delay_ticks = (char)control_ticks;
}
}
}
}
unit = unit_get(actor->meta.unit_index);
{
real grenade_roll = real_seed_random(get_global_random_seed_address());
long weapon_index = unit_inventory_get_weapon(
actor->meta.unit_index,
unit_get(actor->meta.unit_index)->unit.current_weapon_index);
if (!ai_globals->grenades_enabled ||
grenade_roll < actor_variant_definition->items.dont_drop_grenades_chance)
{
csmemset(unit->unit.grenade_counts, 0, sizeof(unit->unit.grenade_counts));
}
if (weapon_index != NONE)
{
if (actor_variant_definition->items.weapon_loaded_lower_bound > 0.f ||
actor_variant_definition->items.weapon_loaded_upper_bound > 0.f)
{
real weapon_loaded_upper_bound =
actor_variant_definition->items.weapon_loaded_upper_bound;
real weapon_loaded_lower_bound =
actor_variant_definition->items.weapon_loaded_lower_bound;
real amount = real_seed_random_range(
get_global_random_seed_address(),
weapon_loaded_lower_bound,
weapon_loaded_upper_bound);
weapon_set_current_amount(weapon_index, amount);
}
if (actor_variant_definition->items.weapon_ammo_lower_bound > 0 ||
actor_variant_definition->items.weapon_ammo_upper_bound > 0)
{
short rounds[2];
csmemset(rounds, 0, sizeof(rounds));
{
short weapon_ammo_upper_bound =
actor_variant_definition->items.weapon_ammo_upper_bound + 1;
short weapon_ammo_lower_bound =
actor_variant_definition->items.weapon_ammo_lower_bound;
rounds[0] = seed_random_range(
get_global_random_seed_address(),
weapon_ammo_lower_bound,
weapon_ammo_upper_bound);
}
weapon_set_total_rounds(weapon_index, rounds);
}
}
}
actor_delete(actor_index, TRUE);
if (encounter_index != NONE)
{
encounter_update_status(encounter_index);
}
return;
}
void actor_swarm_unit_died(
long actor_index,
long unit_index)
+4
View File
@@ -865,6 +865,10 @@ long actor_create_for_unit(
short default_state,
short initial_command_list_index,
char noncombat_sequence_id);
void actor_clear_discarded_firing_positions(
long actor_index,
boolean clear_temporary_only);
short actors_spawn_from_unit(
long unit_index,
long actor_variant_definition_index,
+1 -1
View File
@@ -514,7 +514,7 @@ struct actor_debug_info
real vision_last_maximum_distance;
real vision_last_perception_factor;
short num_debug_evaluations;
short evaluation_mode;
short firing_position_type_mismatch_ticks;
};
/* ---------- prototypes/AI_DEBUG.C */
+23 -6
View File
@@ -18,7 +18,7 @@ header included in hcex build.
enum
{
// only the two bits ai_debug.c reads are named; the rest are unidentified
_encounter_braindead_bit = 4,
_encounter_3d_firing_positions_bit = 5,
_encounter_manual_structure_bsp_index_bit = 6,
};
@@ -150,16 +150,33 @@ struct encounter_definition
struct tag_block player_starting_locations;
};
// element of squad_definition.starting_locations; the 0x1C size comes from the
// call site and the field at 0x18 from the string "reset the actor variant in
// all %d starting locations"
// element of squad_definition.starting_locations
struct actor_starting_location
{
unsigned char __unknown00[0x18];
real_point3d position;
real facing;
short cluster_index;
signed char noncombat_sequence_id;
byte flags;
short default_state;
short initial_state;
short actor_variant_index;
word pad;
short command_list_index;
};
typedef char actor_starting_location_size_assert[
sizeof(struct actor_starting_location) == 0x1C ? 1 : -1];
typedef char actor_starting_location_noncombat_sequence_id_offset_assert[
offsetof(struct actor_starting_location, noncombat_sequence_id) == 0x12 ? 1 : -1];
typedef char actor_starting_location_default_state_offset_assert[
offsetof(struct actor_starting_location, default_state) == 0x14 ? 1 : -1];
typedef char actor_starting_location_initial_state_offset_assert[
offsetof(struct actor_starting_location, initial_state) == 0x16 ? 1 : -1];
typedef char actor_starting_location_actor_variant_index_offset_assert[
offsetof(struct actor_starting_location, actor_variant_index) == 0x18 ? 1 : -1];
typedef char actor_starting_location_command_list_index_offset_assert[
offsetof(struct actor_starting_location, command_list_index) == 0x1A ? 1 : -1];
struct platoon_definition
{
char name[TAG_STRING_LENGTH+1];
+3
View File
@@ -262,6 +262,9 @@ boolean weapon_new(long weapon_index);
void weapon_export_function_values(long weapon_index);
void weapon_set_total_rounds(long weapon_index, short *rounds_array);
void weapon_set_current_amount(
long weapon_index,
real current_amount);
void weapon_build_weapon_interface_state(long weapon_index, struct weapon_interface_state *state);