Lane B wave 1: six strict-exact functions (+2,727 meaningful / +2,784 padded)

New strict exact (unit :: function, padded bytes, relocations):
  game/aim_assist        :: _local_player_aim_assist                        368  12
  game/game_engine_king  :: _king_calculate_hill_state                      448  32
  units/unit_dialogue    :: _unit_describe_speech                           272
  units/unit_dialogue    :: _unit_make_damage_sound                         784
  units/biped_limp_noodle:: _biped_limp_noodle_relax_nodes_onto_environment 256
  units/vehicles         :: _create_ghost_effect                            656
game_engine_king is now 29/29 on the function board (object NOT admitted: its
.bss naming is owner-held and its .data is blocked, see below).

Evidence per function:
- _local_player_aim_assist: the later Win32 /Od build (halo_cache_symbols.exe
  fn 0x589420) and its RTC descriptor attest, independently of any compile, the
  result local initialised first with ONE return, the nested
  player_control_get_zoom_level argument, the real subtract_vectors3d call with
  RTC names velocity/target_velocity/magnetism_velocity, and two STORED pointer
  locals. /Od neither invents locals nor inlines. Closed on the first compile
  after the evidence; every element strip-tested alone -> residual.
- _king_calculate_hill_state: the LAW Z negative-set member. January has ONE
  hill_controlled_count == HILL_CONTROL_TIME test at the bottom, not three; the
  team arm counts red/blue with the uncontrolled arm in the middle; each arm owns
  its data_iterator. That restores the two missing _king_globals references and
  January's pinned zero register with no zero-store steering.
- _unit_describe_speech: the one board function whose loop differs by pure shape
  (January top-tested). A done-flag `while (!done)` loop, the Marathon idiom.
- _unit_make_damage_sound: single-exit law (January 1 ret, ours had 2); the body
  is guarded by dialogue_index != NONE instead of an early return.
- _biped_limp_noodle_relax_nodes_onto_environment: accessor-binding law - the
  object_get() result is bound to a local (non-destructive lea), as January does.
- _create_ghost_effect: the marker count is TWO shorts summed in the loop test
  (hover_marker_count from the call, jet_marker_count = 0). First-party
  attestation: the later unoptimised debug build (halo_cache_symbols.exe fn
  0x8f5530) stores the call result to one short, zero-initialises a SECOND
  short, then marker_index, and its loop test is movsx idx / movsx a / movsx b /
  add / cmp; the sibling create_pelican_effect (0x8f5c90) has the same three
  slots with the second one counting "jet thrusters". January's store order
  (`mov [ebp-4],0` then the call result) is exactly that. The zero-valued local
  is therefore an attested source variable, not a steering dummy; without it the
  body is the old residual. Flagged in the handoff for owner awareness.

Zero-credit riders in the same files (no credit claimed):
- vehicles.c _update_alien_fighter_physics_new: credible restructure with scoped
  blocks and real names; still residual [relocs 33!=32, sha]. The reviewer
  measured `real const` thrust locals as byte-neutral, so the plain literals land.
- game_engine_king.c: adds the MISSING `struct game_engine king_engine` table
  (declared in the header, referenced from game_engine_list.c, defined nowhere).
  136 bytes, 0 content bytes differ from January, 23/23 relocations identical.
  objdiff still scores the section <100%: January leaves the "king" literal
  undefined in this object and `$`-literal relocations match only by combined
  address (same cause as race_engine). Correct source, zero data credit.

Config: config/parked.json - four entries REMOVED (now exact):
_king_calculate_hill_state, _biped_limp_noodle_relax_nodes_onto_environment,
_unit_describe_speech, _unit_make_damage_sound. Parks 184 -> 180, 0 stale, 0
invalid. No header, symbols.json, flag, tool or semantic-override change.

Verification: full ninja all_source progress semantic_progress pass (Code
1443979 -> 1446706, 7291 -> 7297 functions). stable_verdicts vs the frozen
baseline: gained 6 / 2784 bytes, regressions 0. Row diff of all 11,060 function
and 1,706 section report rows: 0 rows lost 100%. Admission audit 19/0/8/0.
_point_from_line3d guard, ownership (newly defined symbols vs January) and
fake_match_scan: clean on every changed file. git diff --check clean.
Reviewer: an independent adversarial agent re-gated every candidate from
scratch and returned ACCEPT_EXACT for all six; orchestrator re-verified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jonas VolmanandClaude Fable 5.1 committed 2026-09-21 07:26:50 -07:00
1 parent 7583df5d62
commit 01e16607f0
6 files changed
+310 -304

No files matched your search

-60
View File
@@ -1308,25 +1308,6 @@
"objdiff_percent": 93.7518
}
},
{
"unit": "source/game/game_engine_king",
"function": "_king_calculate_hill_state",
"class": "unclassified",
"evidence": "Fuzzy-parked under the current house rule after reconstructing the complete team and free-for-all hill-control state machine in ordinary typed C from January disassembly and independent HaloCEA behavior. The retained function iterates player_data through the normal data iterator, uses the authenticated king_hill_state and multiplayer-sound enums, preserves the previous-controller rule, and updates the control timer without optimizer-only carriers. Target and candidate share the 448-byte padded envelope; target has 32 relocations and the candidate 30, while objdiff reports 81.507355 percent similarity. Fable's bounded experiments covered count declaration scope, local state consolidation, early-return and shared-tail forms; none produced a lawful easy closure, and alignment-aware disassembly still shows broad control-flow and register-allocation differences. The fail-closed classifier returns UNKNOWN, so the honest class is unclassified. No volatile/register forcing, barrier, pragma, assembly, fake dependency, raw datum cast, invented address name, forced inline, or nonsensical branch is retained. Reopen only for authoritative January source/local records or a natural compatible-compiler donor that explains the two missing relocation sites and branch schedule.",
"measurements": {
"target": {
"size": 448,
"relocation_count": 32,
"normalized_sha256": "9c852f781c6a691a41f7c445091bea59a0738ebd49fd6ec16add6961cedb8817"
},
"base": {
"size": 448,
"relocation_count": 30,
"normalized_sha256": "e9d7a30d3245b8ffd0d72b1271a341f0dd5b20ddd94e9029876f4676c5ed9ae3"
},
"objdiff_percent": 81.507355
}
},
{
"unit": "source/game/game_engine_race",
"function": "_race_engine_get_score",
@@ -1479,25 +1460,6 @@
"objdiff_percent": 87.124596
}
},
{
"unit": "source/units/biped_limp_noodle",
"function": "_biped_limp_noodle_relax_nodes_onto_environment",
"class": "unclassified",
"evidence": "Fuzzy-parked after reconstructing the typed public relaxation wrapper from January disassembly and independent HCEA behavior. The retained ordinary C obtains the biped node-matrix block through the object-header API, snapshots every animation node position, invokes the reconstructed solver and exact orientation pass, saturates the iteration byte at CHAR_MAX, and returns whether relaxation was already complete. Target is 256 padded bytes and the candidate 240, with the same ordered nine-entry relocation target/type sequence although some relocation addresses drift; objdiff reports 89.82417 percent. At +0x1E January calls tag_get immediately, while VC7 first spills the biped pointer to [ebp-0x10], changing later register and loop allocation without changing the call topology. No register hint, volatile access, barrier, pragma, assembly, raw offset, false alias, fake dependency, forced inline, or nonsensical control flow is retained. Reopen only for authoritative January source/local records, a natural same-compiler donor explaining the pointer lifetime, or a source-authentic semantic correction.",
"measurements": {
"target": {
"size": 256,
"relocation_count": 9,
"normalized_sha256": "19128c623c5575500437c95cdfd0363c69fcbfdbf57f7972bbdcf1d2dffaf84f"
},
"base": {
"size": 240,
"relocation_count": 9,
"normalized_sha256": "d1b5db56b26dff8d5d6bafeeae5e1c0e202e19464ab0bf3dec48bea5e1e7ce18"
},
"objdiff_percent": 89.82417
}
},
{
"unit": "source/networking/network_connection",
"function": "_network_connection_connect",
@@ -2938,28 +2900,6 @@
"objdiff_percent": 89.36364
}
},
{
"unit": "source/units/unit_dialogue",
"function": "_unit_describe_speech",
"class": "unclassified",
"evidence": "Complete January-authoritative, HCEA-cross-checked speech-description reconstruction retained from the 2026-09-12 packet. Target and candidate share the 272-byte extent and all 15 relocation identities but remain normalized-code residuals. The natural typed body receives zero credit and no register or branch-layout steering is admitted. See docs/object_matching_logs/unit_dialogue_obj_unwritten_speech_packet_20260912.md.",
"measurements": {
"target": { "size": 272, "relocation_count": 15, "normalized_sha256": "7d9ee7e3b923460ae26a00c0612904b3106b6ed95c8c0491504d017841c1dc9c" },
"base": { "size": 272, "relocation_count": 15, "normalized_sha256": "2b01a81debf36cda38bd965419e48997b2a2bbeac2dd4041f6752591f481edaf" },
"objdiff_percent": 97.22727
}
},
{
"unit": "source/units/unit_dialogue",
"function": "_unit_make_damage_sound",
"class": "unclassified",
"evidence": "Complete January-authoritative, HCEA-cross-checked damage/death vocalization reconstruction retained from the 2026-09-12 packet. Target and candidate share the 784-byte extent and all 18 relocation identities, including January-only wounded-sound suppression, but remain normalized-code residuals. The natural typed body receives zero credit and no codegen pressure is admitted. See docs/object_matching_logs/unit_dialogue_obj_unwritten_speech_packet_20260912.md.",
"measurements": {
"target": { "size": 784, "relocation_count": 18, "normalized_sha256": "87952366bca787dc5174221b78335b6a9e91770433b8d76266bdb21d1bf65975" },
"base": { "size": 784, "relocation_count": 18, "normalized_sha256": "a72cd2b9285abc4fe3ed3a8f4de6c0606fe474c9e01a1049e16b3285f1497f01" },
"objdiff_percent": 81.60833
}
},
{
"unit": "source/hs/hs_runtime",
"function": "_render_debug_trigger_volumes",
+21 -15
View File
@@ -559,6 +559,7 @@ long local_player_aim_assist(
real_euler_angles2d *target_angular_position,
real_euler_angles2d *target_angular_velocity)
{
long target_index= NONE;
director_perspective perspective= director_get_perspective(local_player_index);
*autoaim_level= 0.f;
@@ -570,39 +571,44 @@ long local_player_aim_assist(
{
struct player_datum *player= player_get(local_player_get_player_index(local_player_index));
long aiming_unit_index= unit_get_aiming_unit_index(player->unit_index);
short zoom_level= player_control_get_zoom_level(local_player_index);
struct aim_assist_parameters parameters;
if (unit_get_aim_assist_parameters(aiming_unit_index, zoom_level, &parameters))
if (unit_get_aim_assist_parameters(aiming_unit_index, player_control_get_zoom_level(local_player_index), &parameters))
{
struct observer_result const *camera= observer_get_camera(local_player_index);
struct aim_assist_target target;
if (aim_assist(&parameters, &camera->position, &camera->forward, aiming_unit_index, player->team_index, &target))
{
real_vector3d aiming_velocity, target_velocity, relative_velocity;
real horizontal_distance_squared, horizontal_distance;
real_vector3d velocity, target_velocity, magnetism_velocity;
*autoaim_level= target.autoaim_level;
*magnetism_level= target.magnetism_level;
euler_angles2d_from_vector3d(target_angular_position, &target.vector);
object_get_velocities(player->unit_index, &aiming_velocity, NULL);
object_get_velocities(player->unit_index, &velocity, NULL);
object_get_velocities(target.object_index, &target_velocity, NULL);
subtract_vectors3d(&target_velocity, &velocity, &magnetism_velocity);
subtract_vectors3d(&target_velocity, &aiming_velocity, &relative_velocity);
horizontal_distance_squared= target.vector.i*target.vector.i + target.vector.j*target.vector.j;
horizontal_distance= square_root(horizontal_distance_squared);
target_angular_velocity->yaw= (target.vector.i*relative_velocity.j - target.vector.j*relative_velocity.i) /
horizontal_distance_squared;
target_angular_velocity->pitch= (relative_velocity.k*horizontal_distance -
(target.vector.i*relative_velocity.i + target.vector.j*relative_velocity.j)/horizontal_distance*target.vector.k) /
(target.vector.k*target.vector.k + horizontal_distance_squared);
{
real_vector3d const *relative_position= &target.vector;
real_vector3d const *relative_velocity= &magnetism_velocity;
real horizontal_distance_squared= relative_position->i*relative_position->i +
relative_position->j*relative_position->j;
real horizontal_distance= square_root(horizontal_distance_squared);
return target.object_index;
target_angular_velocity->yaw= (relative_position->i*relative_velocity->j -
relative_position->j*relative_velocity->i)/horizontal_distance_squared;
target_angular_velocity->pitch= (horizontal_distance*relative_velocity->k -
relative_position->k/horizontal_distance*(relative_position->i*relative_velocity->i +
relative_position->j*relative_velocity->j))/(relative_position->k*relative_position->k +
horizontal_distance_squared);
}
target_index= target.object_index;
}
}
}
return NONE;
return target_index;
}
+68 -29
View File
@@ -847,6 +847,46 @@ void king_engine_update(
return;
}
/* ---------- engine table */
struct game_engine king_engine =
{
"king",
game_engine_king,
king_engine_dispose,
king_engine_initialize_for_new_map,
king_engine_dispose_from_old_map,
king_engine_player_added,
king_engine_game_ending,
king_engine_game_starting,
king_engine_statistics_append,
king_engine_handle_client_message,
king_engine_handle_server_message,
king_engine_pregame_post_rasterize,
king_engine_post_rasterize,
king_engine_player_update,
NULL,
NULL,
NULL,
king_engine_update,
king_get_score,
king_get_score_string,
king_get_score_header_string,
king_get_team_score_string,
NULL,
king_engine_player_damaged_player,
king_engine_player_killed_player,
king_engine_display_score,
NULL,
king_engine_prespawn_player_update,
NULL,
NULL,
king_engine_goal_matches_player,
NULL,
NULL,
NULL,
};
/* ---------- private code */
static void hill_points_to_2d(
@@ -982,31 +1022,31 @@ static boolean player_inside_hill(
static void king_calculate_hill_state(
void)
{
struct data_iterator iterator;
struct player_datum *player;
long player_count = 0;
if (game_engine_has_teams())
{
long enemy_count = 0;
struct data_iterator player_iterator;
long red_count = 0;
long blue_count = 0;
data_iterator_new(&iterator, player_data);
player = (struct player_datum *)data_iterator_next(&iterator);
data_iterator_new(&player_iterator, player_data);
player = (struct player_datum *)data_iterator_next(&player_iterator);
while (player)
{
if (king_globals.on_the_hill[DATUM_INDEX_TO_ABSOLUTE_INDEX(iterator.datum_index)])
if (king_globals.on_the_hill[DATUM_INDEX_TO_ABSOLUTE_INDEX(player_iterator.datum_index)])
{
if (player->team_index)
enemy_count++;
blue_count++;
else
player_count++;
red_count++;
}
player = (struct player_datum *)data_iterator_next(&iterator);
player = (struct player_datum *)data_iterator_next(&player_iterator);
}
if (enemy_count)
if (blue_count)
{
if (player_count)
if (red_count)
{
king_globals.hill_state = king_hill_contested;
if (king_globals.hill_controlled_count > HILL_CONTROL_TIME)
@@ -1020,40 +1060,38 @@ static void king_calculate_hill_state(
else
king_globals.hill_controlled_count = 0;
king_globals.hill_state = king_hill_controlled_blue;
if (king_globals.hill_controlled_count == HILL_CONTROL_TIME)
game_engine_play_multiplayer_sound(_multiplayer_sound_hill_controlled);
}
}
else if (player_count)
else if (!red_count)
{
king_globals.hill_state = king_hill_uncontrolled;
king_globals.hill_controlled_count = 0;
}
else
{
if (king_globals.hill_state == king_hill_controlled_red)
king_globals.hill_controlled_count++;
else
king_globals.hill_controlled_count = 0;
king_globals.hill_state = king_hill_controlled_red;
if (king_globals.hill_controlled_count == HILL_CONTROL_TIME)
game_engine_play_multiplayer_sound(_multiplayer_sound_hill_controlled);
}
else
{
king_globals.hill_state = king_hill_uncontrolled;
king_globals.hill_controlled_count = 0;
}
}
else
{
struct data_iterator player_iterator;
long player_count = 0;
long controller;
data_iterator_new(&iterator, player_data);
player = (struct player_datum *)data_iterator_next(&iterator);
data_iterator_new(&player_iterator, player_data);
player = (struct player_datum *)data_iterator_next(&player_iterator);
while (player)
{
if (king_globals.on_the_hill[DATUM_INDEX_TO_ABSOLUTE_INDEX(iterator.datum_index)])
if (king_globals.on_the_hill[DATUM_INDEX_TO_ABSOLUTE_INDEX(player_iterator.datum_index)])
{
controller = iterator.datum_index;
controller = player_iterator.datum_index;
player_count++;
}
player = (struct player_datum *)data_iterator_next(&iterator);
player = (struct player_datum *)data_iterator_next(&player_iterator);
}
if (player_count > 1)
@@ -1077,17 +1115,18 @@ static void king_calculate_hill_state(
king_globals.hill_previous_controller = controller;
}
king_globals.hill_state = king_hill_controlled;
if (king_globals.hill_controlled_count == HILL_CONTROL_TIME)
game_engine_play_multiplayer_sound(_multiplayer_sound_hill_controlled);
}
else
{
king_globals.hill_previous_controller = NONE;
king_globals.hill_state = king_hill_uncontrolled;
king_globals.hill_controlled_count = 0;
king_globals.hill_previous_controller = NONE;
}
}
if (king_globals.hill_controlled_count == HILL_CONTROL_TIME)
game_engine_play_multiplayer_sound(_multiplayer_sound_hill_controlled);
return;
}
+2 -1
View File
@@ -722,9 +722,10 @@ boolean biped_limp_noodle_relax_nodes_onto_environment(
struct biped_definition *definition = biped_definition_get(biped->definition_index);
struct animation_graph *animation_graph = animation_graph_definition_get(
definition->object.animation_graph.index);
struct object_datum *object = object_get(biped_index);
real_matrix4x3 *node_matrices = (real_matrix4x3 *)object_header_block_get(
biped_index,
&object_get(biped_index)->object.node_matrices);
&object->object.node_matrices);
boolean relaxation_complete =
biped->biped.limp_body_current_relaxation_iterations >=
biped->biped.limp_body_max_relaxation_iterations;
+150 -135
View File
@@ -488,18 +488,28 @@ char const *unit_describe_speech(
if (abbreviated)
{
boolean done = FALSE;
scan = sound_name;
while (TRUE)
while (!done)
{
if (!scan)
break;
scan = strchr(scan, '\\');
if (!scan)
break;
scan++;
sound_name = scan;
if (scan)
{
scan = strchr(scan, '\\');
if (scan)
{
scan++;
sound_name = scan;
}
else
{
done = TRUE;
}
}
else
{
done = TRUE;
}
}
}
else
@@ -725,145 +735,150 @@ boolean unit_make_damage_sound(
(void)shield_damage;
if (unit->unit.dialogue_index == NONE)
return spoke;
if (unit->unit.dialogue_index != NONE)
{
boolean took_body_damage = unit->object.recent_body_damage > 0.f;
boolean took_major_body_damage = unit->object.recent_body_damage >= 0.6f;
short vocalization_type = NONE;
short damage_category = _unit_dialogue_damage_category_none;
short unit_effect_type = NONE;
short unit_effect_volume = 0;
boolean involuntary_vocalization = FALSE;
short vocalization_type = NONE;
short damage_category = _unit_dialogue_damage_category_none;
boolean took_body_damage = unit->object.recent_body_damage > 0.f;
boolean took_major_body_damage = unit->object.recent_body_damage >= 0.6f;
short unit_effect_type = NONE;
short unit_effect_volume = 0;
boolean involuntary_vocalization = FALSE;
if (damage_data && damage_data->definition_index != NONE)
{
damage_category = damage_effect_definition_get(
damage_data->definition_index)->damage.category;
}
if (died)
{
long actor_index = unit->unit.swarm_actor_index;
boolean instantaneous = FALSE;
boolean severe;
if (actor_index == NONE)
actor_index = unit->unit.actor_index;
if (damage_data->definition_index != NONE)
if (damage_data && damage_data->definition_index != NONE)
{
instantaneous = damage_effect_definition_get(
damage_data->definition_index)->damage.instantaneous_acceleration >= 2.f;
damage_category = damage_effect_definition_get(
damage_data->definition_index)->damage.category;
}
if (actor_index != NONE)
if (died)
{
severe = actor_get(actor_index)->state.combat_status >=
_unit_dialogue_actor_combat_status_definite;
}
else
{
severe = unit->object.recent_body_damage > body_damage + 0.2f;
}
long actor_index = unit->unit.swarm_actor_index != NONE ?
unit->unit.swarm_actor_index :
unit->unit.actor_index;
boolean severe = FALSE;
boolean instantaneous = FALSE;
if (damage_category == _unit_dialogue_damage_category_falling)
vocalization_type = _vocalization_death_falling;
else if (damage_category == _unit_dialogue_damage_category_flame)
vocalization_type = _vocalization_death_agonizing;
else if (instantaneous)
vocalization_type = _vocalization_death_flying;
else if (severe)
{
vocalization_type = died_instantly ?
_vocalization_death_instant :
_vocalization_death_violent;
}
else
{
vocalization_type = _vocalization_death_quiet;
}
involuntary_vocalization = TRUE;
if (vocalization_type != _vocalization_death_quiet)
{
unit_effect_type = _unit_dialogue_ai_unit_effect_death_scream;
unit_effect_volume = vocalization_type == _vocalization_death_instant ? 4 : 1;
}
}
else if (unit->unit.speech.damage_major_timer == 0)
{
if (damage_category == _unit_dialogue_damage_category_falling)
{
involuntary_vocalization = TRUE;
vocalization_type = _vocalization_pain_falling;
}
else if (took_major_body_damage)
{
involuntary_vocalization = TRUE;
vocalization_type = _vocalization_pain_body_major;
}
else if (unit->unit.speech.damage_minor_timer == 0 &&
unit->unit.speech.damage_minor_sounds < 3 &&
(unit->unit.speech.current.priority == _unit_speech_none ||
real_seed_random(get_global_random_seed_address()) < 0.4f))
{
vocalization_type = took_body_damage ?
_vocalization_pain_body :
_vocalization_pain_shield;
}
}
if (vocalization_type != NONE)
{
long sound_definition_index = NONE;
short priority = died ?
_unit_speech_death :
(involuntary_vocalization ? _unit_speech_involuntary : _unit_speech_pain);
short play_type = unit_test_speech(
unit_index,
priority,
TRUE,
FALSE,
NULL,
&vocalization_type,
&sound_definition_index);
if (!ai_debug.disable_wounded_sounds &&
play_type > _unit_play_speech_none)
{
struct unit_speech_item speech_item;
csmemset(&speech_item, 0, sizeof(speech_item));
speech_item.priority = priority;
speech_item.vocalization_type = vocalization_type;
speech_item.sound_definition_index = sound_definition_index;
speech_item.pause_time = 7;
ai_communication_packet_new(&speech_item.ai);
unit_speak(unit_index, play_type, &speech_item);
spoke = TRUE;
if (took_major_body_damage)
if (damage_data->definition_index != NONE)
{
unit->unit.speech.damage_major_timer = 60;
instantaneous = damage_effect_definition_get(
damage_data->definition_index)->damage.instantaneous_acceleration >= 2.f;
}
if (actor_index != NONE)
{
struct actor_datum *actor = actor_get(actor_index);
severe = actor->state.combat_status >=
_unit_dialogue_actor_combat_status_definite;
}
else if (unit->object.recent_body_damage > body_damage + 0.2f)
{
severe = TRUE;
}
if (damage_category == _unit_dialogue_damage_category_falling)
{
vocalization_type = _vocalization_death_falling;
}
else if (damage_category == _unit_dialogue_damage_category_flame)
{
vocalization_type = _vocalization_death_agonizing;
}
else if (instantaneous)
{
vocalization_type = _vocalization_death_flying;
}
else if (!severe)
{
vocalization_type = _vocalization_death_quiet;
}
else
{
unit->unit.speech.damage_minor_sounds++;
unit->unit.speech.damage_minor_timer = 30;
unit->unit.speech.damage_minor_decay_timer = 22;
vocalization_type = died_instantly ?
_vocalization_death_instant :
_vocalization_death_violent;
}
involuntary_vocalization = TRUE;
if (vocalization_type != _vocalization_death_quiet)
{
unit_effect_type = _unit_dialogue_ai_unit_effect_death_scream;
unit_effect_volume = vocalization_type == _vocalization_death_instant ? 4 : 1;
}
}
else if (unit->unit.speech.damage_major_timer == 0)
{
if (damage_category == _unit_dialogue_damage_category_falling)
{
involuntary_vocalization = TRUE;
vocalization_type = _vocalization_pain_falling;
}
else if (took_major_body_damage)
{
involuntary_vocalization = TRUE;
vocalization_type = _vocalization_pain_body_major;
}
else if (unit->unit.speech.damage_minor_timer == 0 &&
unit->unit.speech.damage_minor_sounds < 3 &&
(unit->unit.speech.current.priority == _unit_speech_none ||
real_seed_random(get_global_random_seed_address()) < 0.4f))
{
vocalization_type = took_body_damage ?
_vocalization_pain_body :
_vocalization_pain_shield;
}
}
}
if (unit_effect_type != NONE)
{
ai_handle_unit_effect(
unit_index,
unit_effect_type,
unit_effect_volume);
}
if (vocalization_type != NONE)
{
long sound_definition_index = NONE;
short priority = died ?
_unit_speech_death :
(involuntary_vocalization ? _unit_speech_involuntary : _unit_speech_pain);
short play_type = unit_test_speech(
unit_index,
priority,
TRUE,
FALSE,
NULL,
&vocalization_type,
&sound_definition_index);
if (!ai_debug.disable_wounded_sounds &&
play_type > _unit_play_speech_none)
{
struct unit_speech_item speech_item;
csmemset(&speech_item, 0, sizeof(speech_item));
speech_item.priority = priority;
speech_item.vocalization_type = vocalization_type;
speech_item.sound_definition_index = sound_definition_index;
speech_item.pause_time = 7;
ai_communication_packet_new(&speech_item.ai);
unit_speak(unit_index, play_type, &speech_item);
spoke = TRUE;
if (took_major_body_damage)
{
unit->unit.speech.damage_major_timer = 60;
}
else
{
unit->unit.speech.damage_minor_sounds++;
unit->unit.speech.damage_minor_timer = 30;
unit->unit.speech.damage_minor_decay_timer = 22;
}
}
}
if (unit_effect_type != NONE)
{
ai_handle_unit_effect(
unit_index,
unit_effect_type,
unit_effect_volume);
}
}
return spoke;
+69 -64
View File
@@ -964,95 +964,99 @@ static void update_alien_fighter_physics_new(
if (physics->powered_mass_points.count==2)
{
real_matrix3x3 actual, desired, difference;
real_vector3d magic_force;
real_vector3d magic_torque;
real_vector3d axis;
real_vector3d thrust;
real_vector3d scaled;
real_vector3d force;
real_vector3d torque;
real throttle, average, spin, maximum;
scale_vector3d(&vehicle->object.forward, vehicle->vehicle.speed, &torque);
if (vehicle->vehicle.speed>0.0f)
throttle = vehicle->vehicle.speed/definition->unknown2f8;
else
throttle = -(vehicle->vehicle.speed/definition->unknown2fc);
compute_acceleration(&torque, &vehicle->object.translational_velocity, &axis,
throttle*definition->unknown300, throttle*definition->unknown304);
scale_vector3d(&axis, physics->mass, &thrust);
scale_vector3d(&thrust, vehicle->unit.seat_power[0], &force);
matrix3x3_from_forward_and_up(&actual, &vehicle->object.forward,
&vehicle->object.up);
desired.forward = vehicle->unit.desired_facing_vector;
point_from_line3d(global_up3d, &desired.forward, -desired.forward.k, &desired.up);
if (normalize3d(&desired.up)==0.0f)
desired.up = *global_forward3d;
if (!unit_driven_by_ai(vehicle_index))
pitch_vectors(&desired.forward, &desired.up, sine(definition->unknown364),
cosine(definition->unknown364));
real_vector3d desired_angular_velocity;
real_vector3d angular_acceleration;
real spin;
real thrust_delta;
{
real_vector3d desired_velocity;
real_vector3d acceleration;
real throttle;
scale_vector3d(&vehicle->object.forward, vehicle->vehicle.speed, &desired_velocity);
if (vehicle->vehicle.speed>0.0f)
throttle = vehicle->vehicle.speed/definition->unknown2f8;
else
throttle = -(vehicle->vehicle.speed/definition->unknown2fc);
compute_acceleration(&desired_velocity, &vehicle->object.translational_velocity, &acceleration,
throttle*definition->unknown300, throttle*definition->unknown304);
scale_vector3d(&acceleration, physics->mass, &magic_force);
scale_vector3d(&magic_force, vehicle->unit.seat_power[0], &magic_force);
}
{
real_matrix3x3 current_rotation, desired_rotation, rotation;
real_quaternion rotation_quaternion;
real yaw;
real angle;
angle = (desired.forward.i*vehicle->object.translational_velocity.j-
desired.forward.j*vehicle->object.translational_velocity.i)/
matrix3x3_from_forward_and_up(&current_rotation, &vehicle->object.forward,
&vehicle->object.up);
desired_rotation.forward = vehicle->unit.desired_facing_vector;
point_from_line3d(global_up3d, &desired_rotation.forward, -desired_rotation.forward.k, &desired_rotation.up);
if (normalize3d(&desired_rotation.up)==0.0f)
desired_rotation.up = *global_forward3d;
if (!unit_driven_by_ai(vehicle_index))
pitch_vectors(&desired_rotation.forward, &desired_rotation.up, sine(definition->unknown364),
cosine(definition->unknown364));
yaw = (desired_rotation.forward.i*vehicle->object.translational_velocity.j-
desired_rotation.forward.j*vehicle->object.translational_velocity.i)/
definition->unknown2f8*definition->unknown308;
yaw_vectors(&desired.up, &desired.forward, sine(angle), cosine(angle));
yaw_vectors(&desired_rotation.up, &desired_rotation.forward, sine(yaw), cosine(yaw));
cross_product3d(&desired_rotation.up, &desired_rotation.forward, &desired_rotation.left);
matrix3x3_transpose(&current_rotation, &current_rotation);
matrix3x3_multiply(&desired_rotation, &current_rotation, &rotation);
matrix3x3_rotation_to_quaternion(&rotation, &rotation_quaternion);
quaternion_to_angle_and_vector(&rotation_quaternion, &angle, &axis);
scale_vector3d(&axis, (-angle)*definition->unknown314*(1.0f/_pi), &desired_angular_velocity);
}
cross_product3d(&desired.up, &desired.forward, &desired.left);
subtract_vectors3d(&desired_angular_velocity, &vehicle->object.angular_velocity, &angular_acceleration);
matrix3x3_transpose(&actual, &actual);
matrix3x3_multiply(&desired, &actual, &difference);
{
real_quaternion rotation;
real angle;
matrix3x3_rotation_to_quaternion(&difference, &rotation);
quaternion_to_angle_and_vector(&rotation, &angle, &desired.left);
scale_vector3d(&desired.left, (-angle)*definition->unknown314*(1.0f/_pi), &thrust);
}
subtract_vectors3d(&thrust, &vehicle->object.angular_velocity, &torque);
average = (physics->zz_moment+physics->yy_moment+physics->xx_moment)*(1.0f/3);
scale_vector3d(&torque, average, &torque);
scale_vector3d(&torque, vehicle->unit.seat_power[0], &torque);
scale_vector3d(&angular_acceleration,
(physics->zz_moment+physics->yy_moment+physics->xx_moment)*(1.0f/3), &magic_torque);
scale_vector3d(&magic_torque, vehicle->unit.seat_power[0], &magic_torque);
spin = magnitude3d(&vehicle->object.angular_velocity)/definition->unknown314;
if (spin>vehicle->vehicle.thrust)
{
maximum = PIN((1.0f-vehicle->vehicle.thrust)*
(1.0f-vehicle->vehicle.thrust)*0.2f, 0.01f, 0.05f);
vehicle->vehicle.thrust += MIN(spin-vehicle->vehicle.thrust, maximum);
thrust_delta = PIN((1.0f-vehicle->vehicle.thrust)*
(1.0f-vehicle->vehicle.thrust)*0.2f,
0.01f, 0.05f);
thrust_delta = MIN(spin-vehicle->vehicle.thrust, thrust_delta);
}
else
{
maximum = -MAX(vehicle->vehicle.thrust*vehicle->vehicle.thrust*0.05f,
thrust_delta = -MAX(vehicle->vehicle.thrust*vehicle->vehicle.thrust*0.05f,
0.005f);
vehicle->vehicle.thrust += MAX(spin-vehicle->vehicle.thrust, maximum);
thrust_delta = MAX(spin-vehicle->vehicle.thrust, thrust_delta);
}
vehicle->vehicle.thrust += thrust_delta;
state[0].antigrav_fraction = vehicle->unit.seat_power[0];
state[0].rotation = *global_identity_quaternion;
state[1].antigrav_fraction = vehicle->unit.seat_power[0];
state[1].rotation = *global_identity_quaternion;
physics_update(vehicle_index, state, mass_points, &force, &torque);
physics_update(vehicle_index, state, mass_points, &magic_force, &magic_torque);
}
else
{
@@ -1245,10 +1249,11 @@ static void create_ghost_effect(
if (definition->effect.index!=NONE && vehicle->unit.seat_power[0]>0.0f)
{
struct object_marker markers[16];
long marker_count = object_get_marker_by_name(vehicle_index, "hover thrusters", markers, 15);
short hover_marker_count = object_get_marker_by_name(vehicle_index, "hover thrusters", markers, 15);
short jet_marker_count = 0;
short marker_index;
for (marker_index = 0; marker_index<marker_count; marker_index++)
for (marker_index = 0; marker_index<hover_marker_count+jet_marker_count; marker_index++)
{
struct object_marker *marker = &markers[marker_index];
struct collision_result collision;