diff --git a/config/parked.json b/config/parked.json index c8376995..ae1d3820 100644 --- a/config/parked.json +++ b/config/parked.json @@ -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", diff --git a/source/game/aim_assist.c b/source/game/aim_assist.c index 111c8473..89f16887 100644 --- a/source/game/aim_assist.c +++ b/source/game/aim_assist.c @@ -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, ¶meters)) + if (unit_get_aim_assist_parameters(aiming_unit_index, player_control_get_zoom_level(local_player_index), ¶meters)) { struct observer_result const *camera= observer_get_camera(local_player_index); struct aim_assist_target target; if (aim_assist(¶meters, &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; } diff --git a/source/game/game_engine_king.c b/source/game/game_engine_king.c index c5691cb1..046e9c6e 100644 --- a/source/game/game_engine_king.c +++ b/source/game/game_engine_king.c @@ -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; } diff --git a/source/units/biped_limp_noodle.c b/source/units/biped_limp_noodle.c index b398d2ee..52ce4dad 100644 --- a/source/units/biped_limp_noodle.c +++ b/source/units/biped_limp_noodle.c @@ -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; diff --git a/source/units/unit_dialogue.c b/source/units/unit_dialogue.c index 5151ce33..cab1975a 100644 --- a/source/units/unit_dialogue.c +++ b/source/units/unit_dialogue.c @@ -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; diff --git a/source/units/vehicles.c b/source/units/vehicles.c index 019a725d..a7b1aa73 100644 --- a/source/units/vehicles.c +++ b/source/units/vehicles.c @@ -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(¤t_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(¤t_rotation, ¤t_rotation); + matrix3x3_multiply(&desired_rotation, ¤t_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