mirror of
https://github.com/astromaddie/HaloCE-VR.git
synced 2026-10-20 07:00:14 +02:00
With vr.aim "hand", on foot the right controller's aim sets the player's facing, so shots, grenades and melee follow it; in a vehicle's or a turret's seat the head still aims. The left stick is turned to move relative to the head rather than the aim. In a local game the shots start at the hand (unit_adjust_projectile_ray) unless a wall stands between it and the unit's eye. The first-person weapon is posed from a camera placed so that its model sits in the hand (vr.weapon_offset_*, to tune in the headset). The HUD crosshair gives way to a reticle on its own OpenXR layer where the hand's aim meets the world, about a degree across. Head aiming stays the default until this is checked in the headset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11700 lines
302 KiB
C
11700 lines
302 KiB
C
/*
|
|
UNITS.C
|
|
|
|
symbols in this file:
|
|
001977E0 0040:
|
|
_units_initialize (0000)
|
|
00197820 0020:
|
|
_units_initialize_for_new_map (0000)
|
|
00197840 0010:
|
|
_units_dispose_from_old_map (0000)
|
|
00197850 0010:
|
|
_units_dispose (0000)
|
|
00197860 0020:
|
|
_unit_kill (0000)
|
|
00197880 0020:
|
|
_unit_kill_silent (0000)
|
|
001978A0 0020:
|
|
_unit_kill_no_statistics (0000)
|
|
001978C0 0010:
|
|
_unit_delete (0000)
|
|
001978D0 0020:
|
|
_units_update (0000)
|
|
001978F0 0180:
|
|
_unit_export_function_values (0000)
|
|
00197A70 0070:
|
|
_unit_persistent_control (0000)
|
|
00197AE0 01e0:
|
|
_unit_get_seat_entrance_point (0000)
|
|
00197CC0 00e0:
|
|
_unit_get_melee_range_and_ticks (0000)
|
|
00197DA0 0090:
|
|
_unit_get_animation_frames_remaining (0000)
|
|
00197E30 0140:
|
|
_unit_euler_axis_doplan (0000)
|
|
00197F70 0020:
|
|
_unit_get_zoom_level (0000)
|
|
00197F90 0080:
|
|
_unit_animation_state_interruptable (0000)
|
|
00198010 0040:
|
|
_unit_animation_busy (0000)
|
|
00198050 0020:
|
|
_unit_animation_overlay_action_loops (0000)
|
|
00198070 0060:
|
|
_unit_animation_state_loops (0000)
|
|
001980D0 0060:
|
|
_unit_animation_weapon_ik (0000)
|
|
00198130 0040:
|
|
_unit_animation_vehicle_ik (0000)
|
|
00198170 0020:
|
|
_unit_animation_aiming_screen (0000)
|
|
00198190 0060:
|
|
_unit_animation_state_get_aiming_screen_index (0000)
|
|
001981F0 0040:
|
|
_unit_animation_state_can_be_entered_without_animation (0000)
|
|
00198230 0040:
|
|
_unit_animation_compute_interpolation_frame_count (0000)
|
|
00198270 0190:
|
|
_unit_animation_start_action (0000)
|
|
00198400 01c0:
|
|
_unit_animation_start_overlay_action (0000)
|
|
001985C0 00c0:
|
|
_unit_can_enter_seat (0000)
|
|
00198680 0070:
|
|
_unit_impulse (0000)
|
|
001986F0 00d0:
|
|
_unit_handle_weapon_state_change (0000)
|
|
001987C0 0250:
|
|
_unit_record_damage (0000)
|
|
00198A10 00b0:
|
|
_unit_test_spawning (0000)
|
|
00198AC0 0020:
|
|
_unit_destroy (0000)
|
|
00198AE0 0040:
|
|
_unit_get_head_position (0000)
|
|
00198B20 01a0:
|
|
_unit_get_camera_position (0000)
|
|
00198CC0 0140:
|
|
_unit_estimate_position (0000)
|
|
00198E00 0040:
|
|
_unit_get_center_of_mass (0000)
|
|
00198E40 0190:
|
|
_unit_animation_impulse_get_index (0000)
|
|
00198FD0 00d0:
|
|
_unit_can_play_animation_impulse (0000)
|
|
001990A0 00c0:
|
|
_unit_test_animation_impulse (0000)
|
|
00199160 0080:
|
|
_unit_get_aiming_unit_index (0000)
|
|
001991E0 0030:
|
|
_unit_get_aiming_vector (0000)
|
|
00199210 0030:
|
|
_unit_get_looking_vector (0000)
|
|
00199240 0020:
|
|
_unit_get_facing_vector (0000)
|
|
00199260 00b0:
|
|
_unit_inventory_next_grenade (0000)
|
|
00199310 0020:
|
|
_unit_controllable (0000)
|
|
00199330 0040:
|
|
_unit_set_controllable (0000)
|
|
00199370 0040:
|
|
_unit_set_possessed (0000)
|
|
001993B0 0060:
|
|
_unit_is_busy (0000)
|
|
00199410 0050:
|
|
_unit_scripting_set_emotion_animation (0000)
|
|
00199460 0080:
|
|
_unit_scripting_suspended (0000)
|
|
001994E0 0040:
|
|
_unit_scripting_can_blink (0000)
|
|
00199520 0050:
|
|
_unit_scripting_doesnt_drop_items (0000)
|
|
00199570 0110:
|
|
_unit_scripting_vehicle_test_seat_list (0000)
|
|
00199680 00a0:
|
|
_unit_scripting_vehicle_test_seat (0000)
|
|
00199720 0080:
|
|
_unit_scripting_unit_riders (0000)
|
|
001997A0 0030:
|
|
_unit_scripting_unit_driver (0000)
|
|
001997D0 0030:
|
|
_unit_scripting_unit_gunner (0000)
|
|
00199800 0160:
|
|
_unit_shield_sapping_update (0000)
|
|
00199960 00f0:
|
|
_units_debug_get_next_unit (0000)
|
|
00199A50 00d0:
|
|
_units_debug_get_closest_unit (0000)
|
|
00199B20 0120:
|
|
_unit_debug_ninja_rope (0000)
|
|
00199C40 0060:
|
|
_unit_set_user_animation (0000)
|
|
00199CA0 0070:
|
|
_any_unit_is_dangerous (0000)
|
|
00199D10 00a0:
|
|
_unit_can_see_point (0000)
|
|
00199DB0 0040:
|
|
_unit_driven_by_ai (0000)
|
|
00199DF0 0040:
|
|
_unit_gunned_by_ai (0000)
|
|
00199E30 0040:
|
|
_unit_set_desired_flashlight_state (0000)
|
|
00199E70 0030:
|
|
_unit_get_current_flashlight_state (0000)
|
|
00199EA0 0140:
|
|
_unit_detach_from_parent (0000)
|
|
00199FE0 0070:
|
|
_unit_seat_filled (0000)
|
|
0019A050 0060:
|
|
_unit_seat_is_driver (0000)
|
|
0019A0B0 0060:
|
|
_unit_seat_is_gunner (0000)
|
|
0019A110 0060:
|
|
_unit_seat_allow_noncombatants (0000)
|
|
0019A170 00e0:
|
|
_unit_update_driver_and_gunner (0000)
|
|
0019A250 0020:
|
|
_unit_get_current_equipment (0000)
|
|
0019A270 0100:
|
|
_unit_add_grenade_to_inventory (0000)
|
|
0019A370 0090:
|
|
_unit_add_grenade_type_to_inventory (0000)
|
|
0019A400 0120:
|
|
_unit_add_equipment_to_inventory (0000)
|
|
0019A520 0040:
|
|
_unit_delete_current_equipment (0000)
|
|
0019A560 0080:
|
|
_unit_delete_all_weapons (0000)
|
|
0019A5E0 0060:
|
|
_unit_has_weapon_definition_index (0000)
|
|
0019A640 0030:
|
|
_unit_first_free_weapon_index (0000)
|
|
0019A670 0070:
|
|
_unit_get_weapon_count (0000)
|
|
0019A6E0 0070:
|
|
_unit_approve_weapon_pickup (0000)
|
|
0019A750 0070:
|
|
_unit_get_grenade_count (0000)
|
|
0019A7C0 0060:
|
|
_unit_get_current_grenade_type (0000)
|
|
0019A820 01d0:
|
|
_unit_throw_grenade_move_to_hand (0000)
|
|
0019A9F0 0310:
|
|
_unit_throw_grenade_release (0000)
|
|
0019AD00 02d0:
|
|
_unit_seat_update (0000)
|
|
0019AFD0 0050:
|
|
_base_seat_label_get (0000)
|
|
0019B020 0040:
|
|
_seat_label_to_base_seat_index (0000)
|
|
0019B060 0050:
|
|
_base_weapon_label_get (0000)
|
|
0019B0B0 00b0:
|
|
_unit_set_animation (0000)
|
|
0019B160 0050:
|
|
_unit_animation_update (0000)
|
|
0019B1B0 0080:
|
|
_unit_refresh_illumination (0000)
|
|
0019B230 0050:
|
|
_unit_get_weapon (0000)
|
|
0019B280 0190:
|
|
_unit_drop_item (0000)
|
|
0019B410 00b0:
|
|
_unit_drop_grenades (0000)
|
|
0019B4C0 0100:
|
|
_unit_drop_inventory_weapons (0000)
|
|
0019B5C0 0040:
|
|
_unit_handle_region_destroyed (0000)
|
|
0019B600 0080:
|
|
_unit_melee_sound (0000)
|
|
0019B680 02a0:
|
|
_unit_cause_continuous_melee_damage (0000)
|
|
0019B920 0040:
|
|
_unit_set_enterable_by_player (0000)
|
|
0019B960 0030:
|
|
_unit_set_emotion (0000)
|
|
0019B990 0040:
|
|
_unit_aim_without_turning (0000)
|
|
0019B9D0 0070:
|
|
_unit_get_custom_animation_time (0000)
|
|
0019BA40 0030:
|
|
_unit_is_playing_custom_animation (0000)
|
|
0019BA70 0170:
|
|
_unit_start_user_animation (0000)
|
|
0019BBE0 0060:
|
|
_unit_set_mouth_aperture (0000)
|
|
0019BC40 0060:
|
|
_unit_inventory_get_must_be_readied_weapon (0000)
|
|
0019BCA0 0040:
|
|
_unit_has_weapon (0000)
|
|
0019BCE0 0060:
|
|
_unit_has_weapon_with_flag (0000)
|
|
0019BD40 00d0:
|
|
_unit_start_running_blindly (0000)
|
|
0019BE10 0030:
|
|
_unit_stop_running_blindly (0000)
|
|
0019BE40 0100:
|
|
_unit_flame_to_death (0000)
|
|
0019BF40 0030:
|
|
_unit_flying_through_air (0000)
|
|
0019BF70 04f0:
|
|
_unit_euler_axis_buildplan (0000)
|
|
0019C460 0200:
|
|
_unit_euler_axis_couple (0000)
|
|
0019C660 0220:
|
|
_unit_set_or_test_seat_and_weapon_label (0000)
|
|
0019C880 00d0:
|
|
_unit_adjust_projectile_ray (0000)
|
|
0019C950 0200:
|
|
_unit_render_debug (0000)
|
|
0019CB50 0580:
|
|
_unit_animation_set_state (0000)
|
|
0019D0D0 0020:
|
|
_unit_abort_animation (0000)
|
|
0019D0F0 0290:
|
|
_unit_find_nearby_seat (0000)
|
|
0019D380 0420:
|
|
_unit_clip_to_aiming_bounds (0000)
|
|
0019D7A0 0060:
|
|
_unit_inventory_get_weapon (0000)
|
|
0019D800 00b0:
|
|
_unit_set_actively_controlled (0000)
|
|
0019D8B0 01a0:
|
|
_vehicle_scripting_find_available_seats (0000)
|
|
0019DA50 0020:
|
|
_unit_open (0000)
|
|
0019DA70 0020:
|
|
_unit_close (0000)
|
|
0019DA90 0040:
|
|
_unit_overcharged (0000)
|
|
0019DAD0 0030:
|
|
_unit_set_seat (0000)
|
|
0019DB00 0080:
|
|
_units_set_desired_flashlight_state (0000)
|
|
0019DB80 00a0:
|
|
_unit_get_seat_label (0000)
|
|
0019DC20 0040:
|
|
_unit_drop_current_equipment (0000)
|
|
0019DC60 0050:
|
|
_unit_can_use_weapon (0000)
|
|
0019DCB0 00d0:
|
|
_unit_approve_weapon_swap (0000)
|
|
0019DD80 0170:
|
|
_unit_weapon_next_index (0000)
|
|
0019DEF0 0100:
|
|
_unit_drop_current_weapon (0000)
|
|
0019DFF0 0030:
|
|
_unit_get_current_weapon_label (0000)
|
|
0019E020 0020:
|
|
_scripting_set_magic_base_seat (0000)
|
|
0019E040 0030:
|
|
_unit_scripting_set_seat (0000)
|
|
0019E070 00c0:
|
|
_unit_handle_deleted_object (0000)
|
|
0019E130 0250:
|
|
_unit_cause_melee_damage (0000)
|
|
0019E380 0640:
|
|
_unit_cause_player_melee_damage (0000)
|
|
0019E9C0 0030:
|
|
_unit_stop_custom_animation (0000)
|
|
0019E9F0 0080:
|
|
_unit_custom_animation_at_frame (0000)
|
|
0019EA70 0120:
|
|
_unit_align_facing (0000)
|
|
0019EB90 00a0:
|
|
_unit_start_flaming_to_death (0000)
|
|
0019EC30 02e0:
|
|
_unit_running_blind (0000)
|
|
0019EF10 0090:
|
|
_unit_vectors_are_valid (0000)
|
|
0019EFA0 02e0:
|
|
_unit_verify_vectors (0000)
|
|
0019F280 03a0:
|
|
_unit_control (0000)
|
|
0019F620 0780:
|
|
_unit_preprocess_node_orientations (0000)
|
|
0019FDA0 0180:
|
|
_unit_postprocess_node_matrices (0000)
|
|
0019FF20 06d0:
|
|
_unit_euler_aiming_update (0000)
|
|
001A05F0 0090:
|
|
_unit_has_animation_to_enter_seat (0000)
|
|
001A0680 0500:
|
|
_unit_update_animation (0000)
|
|
001A0B80 00c0:
|
|
_unit_unzoom (0000)
|
|
001A0C40 0050:
|
|
_unit_get_zoom_magnification (0000)
|
|
001A0C90 0060:
|
|
_unit_integrated_night_vision_is_active (0000)
|
|
001A0CF0 0620:
|
|
_unit_ping_animation (0000)
|
|
001A1310 0120:
|
|
_unit_start_animation_impulse (0000)
|
|
001A1430 0020:
|
|
_unit_inventory_next_weapon (0000)
|
|
001A1450 0110:
|
|
_unit_melee_attack_begin (0000)
|
|
001A1560 0090:
|
|
_unit_leap_begin (0000)
|
|
001A15F0 00b0:
|
|
_unit_unsuspecting (0000)
|
|
001A16A0 0130:
|
|
_unit_add_weapon_to_inventory (0000)
|
|
001A17D0 01b0:
|
|
_unit_ready_desired_weapon (0000)
|
|
001A1980 01d0:
|
|
_unit_throw_grenade_begin (0000)
|
|
001A1B50 0030:
|
|
_scripting_magic_melee_attack (0000)
|
|
001A1B80 0240:
|
|
_unit_impact_melee_damage (0000)
|
|
001A1DC0 0140:
|
|
_unit_place (0000)
|
|
001A1F00 0050:
|
|
_unit_solo_player_integrated_night_vision_is_active (0000)
|
|
001A1F50 00e0:
|
|
_unit_add_initial_weapons (0000)
|
|
001A2030 0040:
|
|
_unit_adjust_for_seat_change (0000)
|
|
001A2070 0400:
|
|
_unit_new (0000)
|
|
001A2470 0250:
|
|
_unit_enter_seat (0000)
|
|
001A26C0 0290:
|
|
_unit_exit_seat_end (0000)
|
|
001A2950 0270:
|
|
_unit_died (0000)
|
|
001A2BC0 0130:
|
|
_unit_scripting_enter_vehicle (0000)
|
|
001A2CF0 0180:
|
|
_vehicle_scripting_load_magic (0000)
|
|
001A2E70 0110:
|
|
_unit_try_and_exit_seat (0000)
|
|
001A2F80 1730:
|
|
_unit_update (0000)
|
|
001A46B0 0640:
|
|
_unit_damage_aftermath (0000)
|
|
001A4CF0 0100:
|
|
_vehicle_scripting_unload (0000)
|
|
001A4DF0 0040:
|
|
_unit_scripting_exit_vehicle (0000)
|
|
002A4DCC 0006:
|
|
??_C@_05PJLLPND@stand?$AA@ (0000)
|
|
002A4DD4 000c:
|
|
??_C@_0M@CHPCMCIL@unit_update?$AA@ (0000)
|
|
002A4DE0 000d:
|
|
??_C@_0N@KFMOFEIC@unit_globals?$AA@ (0000)
|
|
002A4DF0 001d:
|
|
??_C@_0BN@LFBAPBJD@c?3?2halo?2SOURCE?2units?2units?4c?$AA@ (0000)
|
|
002A4E10 000d:
|
|
??_C@_0N@IKNEFIIG@unit?5globals?$AA@ (0000)
|
|
002A4E20 0044:
|
|
??_C@_0EE@MJKOADBA@VALID_FLAGS?$CIpersistent_control_f@ (0000)
|
|
002A4E64 000c:
|
|
??_C@_0M@GBEBNJOA@?5enter?9hint?$AA@ (0000)
|
|
002A4E70 0010:
|
|
??_C@_0BA@EKKAICAF@animation_state?$AA@ (0000)
|
|
002A4E80 0014:
|
|
??_C@_0BE@DJIPKBF@MISSING?3?5?$CFs?5?8?$CFs?5?$CFs?8?$AA@ (0000)
|
|
002A4E94 001e:
|
|
??_C@_0BO@COLKAFDO@best_new_attacker_index?$CB?$DNNONE?$AA@ (0000)
|
|
002A4EB8 0051:
|
|
??_C@_0FB@GIBLCPPG@?$CIestimate_mode?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIestimat@ (0000)
|
|
002A4F0C 0024:
|
|
??_C@_0CE@EFKICMFK@body_position?5?$CG?$CG?5estimated_posit@ (0000)
|
|
002A4F30 0005:
|
|
??_C@_04IEJGKNJ@body?$AA@ (0000)
|
|
002A4F38 004c:
|
|
??_C@_0EM@FBFJECKJ@animation_impulse?$DO?$DN0?5?$CG?$CG?5animatio@ (0000)
|
|
002A4F84 003f:
|
|
??_C@_0DP@JJACFPIN@current_index?$DO?$DN0?5?$CG?$CG?5current_inde@ (0000)
|
|
002A4FC4 0029:
|
|
??_C@_0CJ@EPNCNOAE@couldn?8t?5find?5the?5emotion?5animat@ (0000)
|
|
002A4FF0 0031:
|
|
??_C@_0DB@OCHKLFEM@index?$DO?$DN0?5?$CG?$CG?5index?$DMNUMBER_OF_UNIT@ (0000)
|
|
002A5024 0004:
|
|
__real@3ca3d70b (0000)
|
|
002A5028 0049:
|
|
??_C@_0EJ@NJDBEHGI@equipment_definition?9?$DOequipment?4@ (0000)
|
|
002A5078 0045:
|
|
??_C@_0EF@EDBHKOAK@?$CIgrenade_type?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CIgrenade_@ (0000)
|
|
002A50C0 0011:
|
|
??_C@_0BB@JFCLDGNC@grenade_count?$DO?$DN0?$AA@ (0000)
|
|
002A50D8 0049:
|
|
??_C@_0EJ@KGELGNBO@equipment_definition?9?$DOequipment?4@ (0000)
|
|
002A5128 0046:
|
|
??_C@_0EG@DMMDGKIO@equipment_definition?9?$DOequipment?4@ (0000)
|
|
002A5170 0055:
|
|
??_C@_0FF@BHHPBICM@grenade_type?$DN?$DNNONE?5?$HM?$HM?5?$CIgrenade_t@ (0000)
|
|
002A51C8 0091:
|
|
??_C@_0JB@KAHIGLEC@unit?9?$DOunit?4current_grenade_index@ (0000)
|
|
002A525C 000a:
|
|
??_C@_09KBFFALMN@left?5hand?$AA@ (0000)
|
|
002A5268 003e:
|
|
??_C@_0DO@JIFEEFPA@unit?9?$DOunit?4grenade_counts?$FLunit?9?$DO@ (0000)
|
|
002A52A8 0065:
|
|
??_C@_0GF@CHMEGMC@unit?9?$DOunit?4current_grenade_index@ (0000)
|
|
002A5310 0040:
|
|
??_C@_0EA@DPJNOHJJ@base_seat_index?$DO?$DN0?5?$CG?$CG?5base_seat_@ (0000)
|
|
002A5350 0046:
|
|
??_C@_0EG@PHNFDNJE@base_weapon_index?$DO?$DN0?5?$CG?$CG?5base_wea@ (0000)
|
|
002A5398 0008:
|
|
??_C@_07GDJPGPDM@unarmed?$AA@ (0000)
|
|
002A53A0 0011:
|
|
??_C@_0BB@FJNDJOGI@?$CFs?3?5animation?5?$CFs?$AA@ (0000)
|
|
002A53B4 002b:
|
|
??_C@_0CL@FEKGNBAM@index?$DO?$DN0?5?$CG?$CG?5index?$DMMAXIMUM_WEAPON@ (0000)
|
|
002A53E0 002d:
|
|
??_C@_0CN@HHCMEDCD@item?9?$DOobject?4parent_object_index@ (0000)
|
|
002A5410 0038:
|
|
??_C@_0DI@FHACCLPK@a?5?$CFs?5tried?5to?5drop?5a?5?$CFs?5which?5wa@ (0000)
|
|
002A5448 003c:
|
|
??_C@_0DM@HPMFIBJI@WARNING?3?5?$CFs?5tried?5to?5die?5from?5fl@ (0000)
|
|
002A5484 001e:
|
|
??_C@_0BO@JNFPEPKG@plan?9?$DOcoast_t?5?$CL?5actual_t?5?$DO?$DN?5t?$AA@ (0000)
|
|
002A54A4 0013:
|
|
??_C@_0BD@PPEPHKIE@plan?9?$DOcoast_t?5?$DO?$DN?50?$AA@ (0000)
|
|
002A54B8 0011:
|
|
??_C@_0BB@MPHLKAIB@coasting_vel?5?$DM?50?$AA@ (0000)
|
|
002A54CC 0007:
|
|
??_C@_06IGEDIHAH@t?5?$DO?$DN?50?$AA@ (0000)
|
|
002A54D4 000a:
|
|
??_C@_09FAMPJHMJ@disc?5?$DO?$DN?50?$AA@ (0000)
|
|
002A54E0 001a:
|
|
??_C@_0BK@JAFCIDEL@REAL_MAX?5?$CB?$DN?5plan?9?$DOdecel_t?$AA@ (0000)
|
|
002A54FC 001a:
|
|
??_C@_0BK@KHOHGFFP@REAL_MAX?5?$CB?$DN?5plan?9?$DOdecel_a?$AA@ (0000)
|
|
002A5518 001a:
|
|
??_C@_0BK@JCCGJEF@REAL_MAX?5?$CB?$DN?5plan?9?$DOcoast_t?$AA@ (0000)
|
|
002A5534 001a:
|
|
??_C@_0BK@LLGCLIBF@REAL_MAX?5?$CB?$DN?5plan?9?$DOaccel_t?$AA@ (0000)
|
|
002A5550 001a:
|
|
??_C@_0BK@IMNHFOAB@REAL_MAX?5?$CB?$DN?5plan?9?$DOaccel_a?$AA@ (0000)
|
|
002A556C 0014:
|
|
??_C@_0BE@OJLFFFPC@plan?9?$DOinitial_v?5?$DM?50?$AA@ (0000)
|
|
002A5580 001a:
|
|
??_C@_0BK@KCJNEIPK@plan?9?$DOinitial_p?5?$DO?5?91e?903f?$AA@ (0000)
|
|
002A559C 0004:
|
|
__real@ba83126f (0000)
|
|
002A55A0 003b:
|
|
??_C@_0DL@NDCNCHBI@?$CIadjust_plan?9?$DOaccel_t?5?$DO?$DN?50?$CJ?5?$CG?$CG?5?$CI@ (0000)
|
|
002A55DC 0010:
|
|
??_C@_0BA@JMNOFACH@t_extension?5?$DO?50?$AA@ (0000)
|
|
002A55EC 0017:
|
|
??_C@_0BH@KDNEJEKJ@MISSING?3?5?$CFs?5?8?$CFs?5?$CFs?5?$CFs?8?$AA@ (0000)
|
|
002A5604 001a:
|
|
??_C@_0BK@GEHICBJO@parent_seat_index?5?$CB?$DN?5NULL?$AA@ (0000)
|
|
002A5620 001b:
|
|
??_C@_0BL@MGKMGDBI@relative_aiming_angles?4yaw?$AA@ (0000)
|
|
002A563C 001d:
|
|
??_C@_0BN@LENPEPCG@relative_aiming_angles?4pitch?$AA@ (0000)
|
|
002A565C 0011:
|
|
??_C@_0BB@DNAJOPNC@?$CGrelative_vector?$AA@ (0000)
|
|
002A5670 0074:
|
|
??_C@_0HE@KJEMFFL@?$CIseat_desire_type?5?$DN?$DN?5NONE?$CJ?5?$HM?$HM?5?$CI?$CI@ (0000)
|
|
002A56E4 0014:
|
|
??_C@_0BE@BPHFKLIJ@seat_substring_name?$AA@ (0000)
|
|
002A56F8 003b:
|
|
??_C@_0DL@HBBDCAFF@current_index?$DO?$DN0?5?$CG?$CG?5current_inde@ (0000)
|
|
002A5734 0004:
|
|
__real@3d0f5c29 (0000)
|
|
002A5738 0016:
|
|
??_C@_0BG@JEMBMHGH@?$CGunit?9?$DOobject?4forward?$AA@ (0000)
|
|
002A5750 0011:
|
|
??_C@_0BB@GELHHNAL@?$CGunit?9?$DOobject?4up?$AA@ (0000)
|
|
002A5764 0011:
|
|
??_C@_0BB@LPKMKLOA@alignment_vector?$AA@ (0000)
|
|
002A5778 0004:
|
|
__real@3cab92a6 (0000)
|
|
002A577C 0004:
|
|
__real@bcab92a6 (0000)
|
|
002A5780 0004:
|
|
__real@417ea5dd (0000)
|
|
002A5784 0004:
|
|
__real@3e567750 (0000)
|
|
002A5788 0004:
|
|
__real@4087cfed (0000)
|
|
002A578C 0033:
|
|
??_C@_0DD@FIEJMHNA@unit_verify_vectors?5FAILURE?0?5see@ (0000)
|
|
002A57C0 0038:
|
|
??_C@_0DI@OGIFHHM@?5?5looking?5vector?5?$CF08X?5?$CF08X?5?$CF08X?5@ (0000)
|
|
002A57F8 0037:
|
|
??_C@_0DH@EJEBMEHF@?5?5aiming?5vector?5?$CF08X?5?$CF08X?5?$CF08X?5v@ (0000)
|
|
002A5830 004f:
|
|
??_C@_0EP@KHDNKPNP@?5?5desired?5facing?5?$CF08X?5?$CF08X?5?$CF08X?0@ (0000)
|
|
002A5880 0044:
|
|
??_C@_0EE@NPCICDEJ@?5?5object?3?5pos?5?$CF08X?5?$CF08X?5?$CF08X?0?5fw@ (0000)
|
|
002A58C4 001f:
|
|
??_C@_0BP@KOGKNNHJ@?5?5warning?0?5hex?5dump?5follows?4?4?4?$AA@ (0000)
|
|
002A58E4 002c:
|
|
??_C@_0CM@MJPCCGNM@?5?5looking?5vector?5?$CFf?5?$CFf?5?$CFf?5veloci@ (0000)
|
|
002A5910 002b:
|
|
??_C@_0CL@ECJMNHKM@?5?5aiming?5vector?5?$CFf?5?$CFf?5?$CFf?5velocit@ (0000)
|
|
002A593C 003d:
|
|
??_C@_0DN@FMBPFOHH@?5?5desired?5facing?5?$CFf?5?$CFf?5?$CFf?0?5aimin@ (0000)
|
|
002A597C 003a:
|
|
??_C@_0DK@HCAIDIOB@?$CK?$CK?$CK?$CK?5unit_verify_vectors?3?5proble@ (0000)
|
|
002A59B8 000d:
|
|
??_C@_0N@GHHFFCIH@unit?9control?$AA@ (0000)
|
|
002A59C8 001e:
|
|
??_C@_0BO@MDANOMAD@control_data?9?$DOprimary_trigger?$AA@ (0000)
|
|
002A59E8 0040:
|
|
??_C@_0EA@IBGHKCGO@control_data?9?$DOzoom_level?$DN?$DNNONE?5?$HM@ (0000)
|
|
002A5A28 0082:
|
|
??_C@_0IC@CHONDMKL@control_data?9?$DOgrenade_index?$DN?$DNNON@ (0000)
|
|
002A5AB0 007b:
|
|
??_C@_0HL@IFOOEEOJ@control_data?9?$DOweapon_index?$DN?$DNNONE@ (0000)
|
|
002A5B2C 001e:
|
|
??_C@_0BO@CKDHHENP@?$CGcontrol_data?9?$DOlooking_vector?$AA@ (0000)
|
|
002A5B4C 001d:
|
|
??_C@_0BN@CPLNIAKI@?$CGcontrol_data?9?$DOaiming_vector?$AA@ (0000)
|
|
002A5B6C 001d:
|
|
??_C@_0BN@PJECCKBL@?$CGcontrol_data?9?$DOfacing_vector?$AA@ (0000)
|
|
002A5B90 0047:
|
|
??_C@_0EH@EBEINPCA@VALID_FLAGS?$CIcontrol_data?9?$DOcontro@ (0000)
|
|
002A5BD8 0059:
|
|
??_C@_0FJ@GIIHEDDP@control_data?9?$DOaiming_speed?$DO?$DN0?5?$CG?$CG@ (0000)
|
|
002A5C38 0062:
|
|
??_C@_0GC@CEOKMDLJ@control_data?9?$DOanimation_state?$DO?$DN0@ (0000)
|
|
002A5C9C 002b:
|
|
??_C@_0CL@MKJGLHIO@magnitude3d?$CI?$CGcontrol_data?9?$DOthrot@ (0000)
|
|
002A5CC8 001c:
|
|
??_C@_0BM@MNPGDCFG@relative_looking_angles?4yaw?$AA@ (0000)
|
|
002A5CE4 001e:
|
|
??_C@_0BO@MGEJEHJF@relative_looking_angles?4pitch?$AA@ (0000)
|
|
002A5D04 0019:
|
|
??_C@_0BJ@IONAJDB@?$CGrelative_looking_vector?$AA@ (0000)
|
|
002A5D20 0018:
|
|
??_C@_0BI@PHNGNHBO@?$CGrelative_aiming_vector?$AA@ (0000)
|
|
002A5D38 001a:
|
|
??_C@_0BK@KJJPMJMN@?$CGunit?9?$DOunit?4aiming_vector?$AA@ (0000)
|
|
002A5D54 0016:
|
|
??_C@_0BG@OEBDAJFJ@unit?9preprocess?9nodes?$AA@ (0000)
|
|
002A5D6C 000e:
|
|
??_C@_0O@ODHCBCIJ@aiming_vector?$AA@ (0000)
|
|
002A5D80 0061:
|
|
??_C@_0GB@FBDPEFM@?$CIend_aiming_angles?4pitch?5?$DO?$DN?5aimi@ (0000)
|
|
002A5DE8 005d:
|
|
??_C@_0FN@IHCHGFNM@?$CIend_aiming_angles?4yaw?5?$DO?$DN?5aiming@ (0000)
|
|
002A5E48 002b:
|
|
??_C@_0CL@BAPCPLDO@end_aiming_angles?4yaw?5?$DO?$DN?5aiming_@ (0000)
|
|
002A5E74 002b:
|
|
??_C@_0CL@ONCNMLAC@end_aiming_angles?4yaw?5?$DM?$DN?5aiming_@ (0000)
|
|
002A5EA0 002f:
|
|
??_C@_0CP@FHKMNBOC@desired_aiming_angles?4yaw?5?$DO?$DN?5aim@ (0000)
|
|
002A5ED0 002f:
|
|
??_C@_0CP@KKHDOBNO@desired_aiming_angles?4yaw?5?$DM?$DN?5aim@ (0000)
|
|
002A5F00 004f:
|
|
??_C@_0EP@KBPKPPJL@?$CIangular_acceleration_limit?5?$DO?50?4@ (0000)
|
|
002A5F50 001f:
|
|
??_C@_0BP@DPNLKBBM@angular_velocity_limit?5?$DO?$DN?50?40f?$AA@ (0000)
|
|
002A5F70 001e:
|
|
??_C@_0BO@GAMKEDMN@desired_base_seat_index?$CB?$DNNONE?$AA@ (0000)
|
|
002A5F90 0038:
|
|
??_C@_0DI@EIIFDJIB@desired_state?$DO?$DN0?5?$CG?$CG?5desired_stat@ (0000)
|
|
002A5FC8 0008:
|
|
__real@4001475cd2000000 (0000)
|
|
002A5FD0 0027:
|
|
??_C@_0CH@KNBNIKNG@unit?9?$DOobject?4parent_object_index@ (0000)
|
|
002A5FF8 0021:
|
|
??_C@_0CB@MFPLMKIO@unit?9?$DOunit?4feign_death_timer?5?$DO?50@ (0000)
|
|
002A601C 0010:
|
|
??_C@_0BA@JFPDPOCG@unit?9update?9end?$AA@ (0000)
|
|
002A602C 0024:
|
|
??_C@_0CE@EIFKFLM@game_globals_first_person_interf@ (0000)
|
|
002A6050 002c:
|
|
??_C@_0CM@JCKFNLKN@player?5illumination?3?5self?5?$CF?42f?5a@ (0000)
|
|
002A607C 0017:
|
|
??_C@_0BH@GFOLCKHG@unit?9update?9postvector?$AA@ (0000)
|
|
002A6094 001b:
|
|
??_C@_0BL@BHGJMDEB@?$CGunit?9?$DOunit?4looking_vector?$AA@ (0000)
|
|
002A60B0 0014:
|
|
??_C@_0BE@MNAOFPAE@unit?9update?9look?93d?$AA@ (0000)
|
|
002A60C4 0017:
|
|
??_C@_0BH@HICAMLFB@unit?9update?9look?9euler?$AA@ (0000)
|
|
002A60DC 0015:
|
|
??_C@_0BF@NGCFEELO@unit?9update?9look?9set?$AA@ (0000)
|
|
002A60F4 0013:
|
|
??_C@_0BD@DPFKBAB@unit?9update?9aim?93d?$AA@ (0000)
|
|
002A6108 0016:
|
|
??_C@_0BG@NOLLOOND@unit?9update?9aim?9euler?$AA@ (0000)
|
|
002A6120 0014:
|
|
??_C@_0BE@KGIBELMP@unit?9update?9aim?9set?$AA@ (0000)
|
|
002A6134 0022:
|
|
??_C@_0CC@HPCCHHEP@?$CGunit?9?$DOunit?4desired_aiming_vecto@ (0000)
|
|
002A6158 0017:
|
|
??_C@_0BH@PCBGODII@unit?9update?9prevectors?$AA@ (0000)
|
|
002A6170 0029:
|
|
??_C@_0CJ@GMJCHFEH@?$CGgunner_unit?9?$DOunit?4desired_aimin@ (0000)
|
|
002A619C 0029:
|
|
??_C@_0CJ@KNNFHIN@?$CGdriver_unit?9?$DOunit?4desired_facin@ (0000)
|
|
002A61C8 0012:
|
|
??_C@_0BC@FNMDNBAN@unit?9update?9begin?$AA@ (0000)
|
|
002A61DC 0024:
|
|
??_C@_0CE@MIPDNKIJ@p?$CFd?3?5body?5?$CF?42f?5shld?5?$CF?42f?5from?5?$CFs@ (0000)
|
|
00315C08 061c:
|
|
_magic_base_animation_seat_index (0000)
|
|
_base_seat_labels (0604)
|
|
004CCB18 0004:
|
|
_bss_004ccb18 (0000)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "units.h"
|
|
|
|
#include "bipeds.h"
|
|
#include "biped_definitions.h"
|
|
#include "dialogue_definitions.h"
|
|
#include "unit_definitions.h"
|
|
|
|
#include "math/real_math.h"
|
|
#include "ai/actors.h"
|
|
#include "ai/ai_debug.h"
|
|
#include "ai/ai.h"
|
|
#include "bitmaps/bitmaps.h"
|
|
#include "cseries/errors.h"
|
|
#include "cseries/profile.h"
|
|
#include "devices/device_machines.h"
|
|
#include "effects/effects.h"
|
|
#include "effects/material_effect_definitions.h"
|
|
#include "game/cheats.h"
|
|
#include "game/game_allegiance.h"
|
|
#include "game/game_globals.h"
|
|
#include "game/game_engine.h"
|
|
#include "game/players.h"
|
|
#include "hs/object_lists.h"
|
|
#include "interface/first_person_weapons.h"
|
|
#include "items/equipment.h"
|
|
#include "items/equipment_definitions.h"
|
|
#include "items/projectiles.h"
|
|
#include "items/weapon_definitions.h"
|
|
#include "items/weapons.h"
|
|
#include "main/console.h"
|
|
#include "models/model_animation_definitions.h"
|
|
#include "models/model_definitions.h"
|
|
#include "objects/damage.h"
|
|
#include "objects/damage_effect_definitions.h"
|
|
#include "objects/object_lights.h"
|
|
#include "physics/breakable_surfaces.h"
|
|
#include "physics/collision_bsp.h"
|
|
#include "physics/collision_models.h"
|
|
#include "physics/collision_usage.h"
|
|
#include "physics/collisions.h"
|
|
#include "physics/physics_definitions.h"
|
|
#include "render/render.h"
|
|
#include "render/render_debug.h"
|
|
#include "scenario/scenario.h"
|
|
#include "saved games/game_state.h"
|
|
#include "sound/game_sound.h"
|
|
#include "vehicles.h"
|
|
#include "halo_vr.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
_unit_region_destroyed_head_bit = 9,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_unit_weapon_overlay_primary_recoil = 1,
|
|
_unit_weapon_overlay_secondary_recoil,
|
|
_unit_weapon_overlay_primary_charged,
|
|
_unit_weapon_overlay_secondary_charged,
|
|
_unit_weapon_overlay_primary_chamber,
|
|
_unit_weapon_overlay_secondary_chamber,
|
|
};
|
|
|
|
enum
|
|
{
|
|
NUMBER_OF_UNIT_ANIMATION_IMPULSES = 14,
|
|
_unit_seat_unknown8_bit = 8,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_vehicle_seat_desire_not_driver,
|
|
_vehicle_seat_desire_gunner,
|
|
_vehicle_seat_desire_passenger,
|
|
_vehicle_seat_desire_driver,
|
|
_vehicle_seat_desire_any,
|
|
NUMBER_OF_VEHICLE_SEAT_DESIRE_TYPES,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_unit_record_damage_driver_seat_type = 9,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_unit_debug_function_active_bit = 2,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_unit_damage_animation_soft_ping = 0,
|
|
_unit_damage_animation_hard_ping,
|
|
_unit_damage_animation_soft_kill,
|
|
_unit_damage_animation_hard_kill,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_unit_damage_direction_front = 0,
|
|
_unit_damage_direction_left,
|
|
_unit_damage_direction_right,
|
|
_unit_damage_direction_back,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_unit_damage_part_head = 2,
|
|
};
|
|
|
|
/* January computes these direction-cone limits from single-precision pi, then
|
|
promotes the rounded results for the double-precision fabs comparisons. */
|
|
#define UNIT_DAMAGE_REAR_CONE_ANGLE 0.7853981852531433
|
|
#define UNIT_DAMAGE_FRONT_CONE_ANGLE 2.159845009446144
|
|
|
|
#define UNIT_DAMAGE_AFTERMATH_ANIMATION_FLAGS_MASK \
|
|
(FLAG(_object_being_damaged_region_destroyed_bit) | \
|
|
FLAG(_object_being_damaged_shield_depleted_bit) | \
|
|
FLAG(_object_being_damaged_force_hard_ping_bit))
|
|
|
|
enum
|
|
{
|
|
_damage_definition_pings_resistant_units_bit = 2,
|
|
_damage_definition_does_not_ping_units_bit = 4,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
#define unit_get_current_weapon_index(unit_index) unit_inventory_get_weapon((unit_index), unit_get((unit_index))->unit.current_weapon_index)
|
|
#define unit_get_desired_weapon_index(unit_index) unit_inventory_get_weapon((unit_index), unit_get((unit_index))->unit.desired_weapon_index)
|
|
|
|
/* ---------- structures */
|
|
|
|
struct unit_globals
|
|
{
|
|
short next_timer;
|
|
short highest_timer;
|
|
boolean used_time;
|
|
char pad[3];
|
|
};
|
|
|
|
struct unit_acceleration_plan
|
|
{
|
|
boolean at_rest;
|
|
char pad[3];
|
|
real initial_position;
|
|
real initial_velocity;
|
|
real acceleration;
|
|
real acceleration_time;
|
|
real coast_time;
|
|
real deceleration;
|
|
real deceleration_time;
|
|
};
|
|
typedef char unit_acceleration_plan_size_check[
|
|
sizeof(struct unit_acceleration_plan) == 0x20 ? 1 : -1];
|
|
|
|
struct unit_control_data
|
|
{
|
|
char animation_state;
|
|
char aiming_speed;
|
|
word control_flags;
|
|
short weapon_index;
|
|
short grenade_index;
|
|
short zoom_level;
|
|
short pad;
|
|
real_vector3d throttle;
|
|
real primary_trigger;
|
|
real_vector3d facing_vector;
|
|
real_vector3d aiming_vector;
|
|
real_vector3d looking_vector;
|
|
};
|
|
|
|
typedef char unit_control_data_size_assert[
|
|
sizeof(struct unit_control_data) == 0x40 ? 1 : -1];
|
|
|
|
struct unit_initial_weapon
|
|
{
|
|
struct tag_reference weapon;
|
|
long unused[5];
|
|
};
|
|
|
|
typedef char unit_initial_weapon_size_assert[
|
|
sizeof(struct unit_initial_weapon) == 0x24 ? 1 : -1];
|
|
|
|
/* This tag-block element layout is carried locally because this bounded wave
|
|
* does not own the shared game-globals header. */
|
|
struct game_globals_falling_damage
|
|
{
|
|
byte unused0[0x48];
|
|
long unknown48;
|
|
byte unused4c[0x20];
|
|
struct tag_reference flaming_death_damage_effect;
|
|
byte unused1[0x1C];
|
|
};
|
|
typedef char game_globals_falling_damage_size_check[
|
|
sizeof(struct game_globals_falling_damage) == 0x98 ? 1 : -1];
|
|
typedef char game_globals_flaming_death_offset_check[
|
|
offsetof(struct game_globals_falling_damage, flaming_death_damage_effect) == 0x6C ? 1 : -1];
|
|
|
|
/* ---------- prototypes */
|
|
|
|
void unit_detach_from_parent(
|
|
long unit_index);
|
|
void unit_start_flaming_to_death(
|
|
long unit_index,
|
|
long attacker_object_index);
|
|
void unit_flame_to_death(
|
|
long unit_index);
|
|
boolean unit_unsuspecting(
|
|
long unit_index,
|
|
real_point3d const *point);
|
|
void unit_cause_melee_damage(
|
|
long unit_index,
|
|
boolean melee_hit,
|
|
long target_object_index,
|
|
short node_index,
|
|
short region_index,
|
|
short material_index,
|
|
real_vector3d const *object_normal);
|
|
|
|
static char const *base_seat_label_get(short base_seat_index);
|
|
static short seat_label_to_base_seat_index(char const *seat_label);
|
|
static char const *base_weapon_label_get(short base_weapon_index);
|
|
|
|
static void unit_refresh_illumination(long unit_index);
|
|
|
|
static boolean unit_euler_axis_doplan(
|
|
struct unit_acceleration_plan *plan,
|
|
real delta_time,
|
|
real position,
|
|
real *new_position,
|
|
real velocity,
|
|
real *new_velocity);
|
|
static void unit_euler_axis_buildplan(
|
|
real position,
|
|
real velocity,
|
|
real maximum_velocity,
|
|
real maximum_acceleration,
|
|
struct unit_acceleration_plan *plan);
|
|
static void unit_euler_axis_couple(
|
|
struct unit_acceleration_plan *first_plan,
|
|
struct unit_acceleration_plan *second_plan,
|
|
real maximum_velocity,
|
|
real maximum_acceleration);
|
|
|
|
static void unit_melee_sound(
|
|
long unit_index,
|
|
short material_type,
|
|
long damage_effect_index);
|
|
static void unit_update_driver_and_gunner(
|
|
long unit_index);
|
|
static short unit_first_free_weapon_index(
|
|
long unit_index);
|
|
|
|
static short unit_animation_impulse_get_index(
|
|
short animation_impulse,
|
|
short *interpolation_frame_count);
|
|
static boolean unit_can_play_animation_impulse(
|
|
long unit_index,
|
|
short animation_impulse);
|
|
|
|
static boolean unit_animation_aiming_screen(
|
|
struct unit_animation *animation);
|
|
static boolean unit_animation_state_can_be_entered_without_animation(
|
|
short state);
|
|
static short unit_animation_compute_interpolation_frame_count(
|
|
short new_state,
|
|
short old_state);
|
|
static boolean unit_animation_overlay_action_loops(
|
|
struct unit_animation *animation);
|
|
static boolean unit_animation_state_interruptable(
|
|
struct unit_animation *animation,
|
|
short desired_state);
|
|
static boolean unit_animation_state_loops(
|
|
struct unit_animation *animation);
|
|
static long unit_animation_state_get_aiming_screen_index(
|
|
short state);
|
|
static void unit_set_animation(
|
|
long unit_index,
|
|
long animation_graph_index,
|
|
short animation_index);
|
|
static short unit_animation_update(
|
|
long unit_index,
|
|
long animation_graph_index,
|
|
struct animation_state *animation);
|
|
static char const *unit_get_current_weapon_label(
|
|
long unit_index);
|
|
static void unit_align_facing(
|
|
long unit_index,
|
|
real_vector2d const *alignment_vector);
|
|
|
|
static short unit_weapon_next_index(long unit_index, short current_index, short delta);
|
|
static void unit_ready_desired_weapon(
|
|
long unit_index,
|
|
boolean immediate);
|
|
|
|
static void unit_throw_grenade_move_to_hand(long unit_index);
|
|
|
|
|
|
static boolean unit_animation_weapon_ik(
|
|
struct unit_animation *animation);
|
|
static boolean unit_animation_vehicle_ik(
|
|
struct unit_animation *animation);
|
|
static boolean unit_animation_busy(struct unit_animation *animation);
|
|
|
|
static boolean unit_set_or_test_seat_and_weapon_label(
|
|
long object_index,
|
|
char const *seat_label,
|
|
char const *weapon_label,
|
|
boolean change_flag);
|
|
|
|
static boolean unit_animation_set_state(
|
|
long unit_index,
|
|
short new_state);
|
|
|
|
static boolean unit_vectors_are_valid(long unit_index);
|
|
static void unit_throw_grenade_release(long unit_index, boolean premature);
|
|
|
|
static void unit_seat_update(long object_index);
|
|
|
|
static char const *unit_get_seat_label(long object_index);
|
|
|
|
static void unit_cause_continuous_melee_damage(long unit_index);
|
|
|
|
static long unit_get_weapon(struct unit_datum *unit, short index);
|
|
static void unit_drop_item(long unit_index, long item_index);
|
|
/* port/linux/game/network_objects.c's */
|
|
boolean network_objects_creating_host_object(void);
|
|
/* network_game_globals.c's */
|
|
boolean network_game_distributed_client(void);
|
|
static void unit_drop_grenades(
|
|
long unit_index);
|
|
static void unit_drop_inventory_weapons(
|
|
long unit_index);
|
|
|
|
static void unit_verify_vectors(long unit_index, char const *debugstring);
|
|
static void unit_running_blind(long unit_index, real_vector3d *run_vector);
|
|
|
|
static boolean unit_integrated_night_vision_is_active(long unit_index);
|
|
|
|
void unit_detach_from_parent(
|
|
long unit_index);
|
|
|
|
extern char const *base_seat_labels[NUMBER_OF_UNIT_BASE_SEATS];
|
|
|
|
/* ---------- globals */
|
|
|
|
short magic_base_animation_seat_index = NONE;
|
|
|
|
static struct unit_globals *unit_globals;
|
|
|
|
static struct profile_section unit_update_section = {"unit_update", NONE, TRUE};
|
|
|
|
boolean debug_objects_unit_mouth_apeture;
|
|
boolean debug_objects_unit_seats;
|
|
boolean debug_objects_unit_vectors;
|
|
boolean stun_enable;
|
|
boolean debug_damage_taken;
|
|
boolean debug_unit_illumination;
|
|
boolean debug_unit_animations;
|
|
boolean debug_unit_all_animations;
|
|
|
|
/* ---------- public code */
|
|
|
|
void units_initialize(
|
|
void)
|
|
{
|
|
unit_globals = (struct unit_globals *)game_state_malloc("unit globals", NULL, sizeof(*unit_globals));
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 264, unit_globals);
|
|
|
|
return;
|
|
}
|
|
|
|
void units_initialize_for_new_map(
|
|
void)
|
|
{
|
|
memset(unit_globals, 0, offsetof(struct unit_globals, used_time));
|
|
|
|
return;
|
|
}
|
|
|
|
void units_dispose_from_old_map(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void units_dispose(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void unit_kill(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->object.damage_flags, _object_die_act_of_god_bit, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_kill_silent(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->object.damage_flags, _object_die_act_of_god_silent_bit, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_kill_no_statistics(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->object.damage_flags, _object_die_act_of_god_no_statistics_bit, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_delete(
|
|
long unit_index)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void unit_handle_region_destroyed(
|
|
long object_index,
|
|
short region_index,
|
|
unsigned long damage_flags)
|
|
{
|
|
struct unit_datum *unit = unit_get(object_index);
|
|
|
|
if (!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
unit_scream(
|
|
object_index,
|
|
TEST_FLAG(damage_flags, _unit_region_destroyed_head_bit) ?
|
|
_unit_scream_destroyed_head :
|
|
_unit_scream_destroyed_limb);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void units_update(
|
|
void)
|
|
{
|
|
unit_globals->next_timer = unit_globals->highest_timer;
|
|
unit_globals->highest_timer = 0;
|
|
unit_globals->used_time = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
enum
|
|
{
|
|
_unit_function_none = 0,
|
|
_unit_function_driver_seat_power,
|
|
_unit_function_gunner_seat_power,
|
|
_unit_function_aiming_change,
|
|
_unit_function_mouth_aperture,
|
|
_unit_function_integrated_light_power,
|
|
_unit_function_can_blink,
|
|
_unit_function_shield_sapping,
|
|
NUMBER_OF_UNIT_FUNCTION_MODES,
|
|
};
|
|
|
|
void unit_export_function_values(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(
|
|
unit->definition_index);
|
|
long function_index;
|
|
|
|
for (function_index = 0;
|
|
function_index < NUMBEROF(unit->object.incoming_function_values);
|
|
function_index++)
|
|
{
|
|
if (unit_definition->unit.function_modes[function_index] !=
|
|
_unit_function_none)
|
|
{
|
|
real value = 0.f;
|
|
|
|
switch (unit_definition->unit.function_modes[function_index])
|
|
{
|
|
case _unit_function_driver_seat_power:
|
|
value = unit->unit.seat_power[0];
|
|
break;
|
|
case _unit_function_gunner_seat_power:
|
|
value = unit->unit.seat_power[1];
|
|
break;
|
|
case _unit_function_aiming_change:
|
|
value = unit->unit.aiming_change * (1.f / 255.f);
|
|
break;
|
|
case _unit_function_mouth_aperture:
|
|
value = unit->unit.mouth_aperture;
|
|
break;
|
|
case _unit_function_integrated_light_power:
|
|
value = unit->unit.integrated_light_power;
|
|
break;
|
|
case _unit_function_can_blink:
|
|
value =
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit) &&
|
|
!TEST_FLAG(unit->unit.flags, _unit_cannot_blink_bit) ?
|
|
1.f : 0.f;
|
|
break;
|
|
case _unit_function_shield_sapping:
|
|
{
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(
|
|
unit->object.animation.animation_graph_index);
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->object.animation.state.index,
|
|
struct animation);
|
|
short private_loop_frame_index =
|
|
animation->private_loop_frame_index;
|
|
boolean before_private_loop;
|
|
|
|
/*
|
|
The January executable compares and divides the animation index here.
|
|
That is almost certainly an original bug: frame_index would measure
|
|
progress through the animation. Preserve index for exact behavior; a
|
|
corrected build may replace both uses below with state.frame_index.
|
|
*/
|
|
before_private_loop =
|
|
unit->object.animation.state.index <
|
|
private_loop_frame_index;
|
|
if (!before_private_loop)
|
|
{
|
|
value = 1.f - unit->unit.shield_sap_timeout * (1.f / 90.f);
|
|
}
|
|
else
|
|
{
|
|
value =
|
|
(real)unit->object.animation.state.index /
|
|
(real)private_loop_frame_index;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
unit->object.incoming_function_values[function_index] = value;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_persistent_control(
|
|
long unit_index,
|
|
long control_ticks,
|
|
unsigned long persistent_control_flags)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 1541, VALID_FLAGS(persistent_control_flags, NUMBER_OF_UNIT_CONTROL_FLAGS));
|
|
|
|
unit->unit.persistent_control_flags = persistent_control_flags;
|
|
unit->unit.persistent_control_timer = control_ticks;
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_get_seat_entrance_point(
|
|
long unit_index,
|
|
long target_unit_index,
|
|
short seat_index,
|
|
real_point3d *entry_position,
|
|
real_point3d *exit_position,
|
|
real_point3d *seat_transform)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
struct model *model = model_definition_get(
|
|
unit_definition->object.model.index);
|
|
real_matrix4x3 entrance_matrix;
|
|
struct object_marker seat_marker;
|
|
real_matrix4x3 root_matrix;
|
|
struct object_marker enter_hint_marker;
|
|
char enter_hint_marker_name[256];
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
struct unit_datum *target_unit = unit_get(target_unit_index);
|
|
struct unit_definition *target_unit_definition =
|
|
unit_definition_get(target_unit->definition_index);
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&target_unit_definition->unit.seats,
|
|
seat_index,
|
|
struct unit_seat);
|
|
short animation_seat_index;
|
|
boolean result;
|
|
|
|
result = FALSE;
|
|
animation_seat_index = 0;
|
|
if (animation_graph->unit_seats.count > 0)
|
|
{
|
|
do
|
|
{
|
|
struct animation_graph_unit_seat *animation_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
animation_seat_index,
|
|
struct animation_graph_unit_seat);
|
|
|
|
if (!_stricmp(animation_seat->label, seat->label))
|
|
{
|
|
if (animation_seat_index != NONE)
|
|
{
|
|
animation_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
animation_seat_index,
|
|
struct animation_graph_unit_seat);
|
|
|
|
if (animation_seat->animations.count >
|
|
_unit_seat_animation_seat_enter)
|
|
{
|
|
short animation_index = animation_graph_animation_index_get(
|
|
&animation_seat->animations)
|
|
[_unit_seat_animation_seat_enter].animation_index;
|
|
|
|
if (animation_index != NONE)
|
|
{
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
|
|
object_get_marker_by_name(
|
|
target_unit_index,
|
|
seat->marker_name,
|
|
&seat_marker,
|
|
1);
|
|
animation_get_root_matrix(model, animation, 0, &root_matrix);
|
|
matrix4x3_multiply(
|
|
&seat_marker.matrix,
|
|
&root_matrix,
|
|
&entrance_matrix);
|
|
|
|
csstrcpy(enter_hint_marker_name, seat->marker_name);
|
|
csstrcat(enter_hint_marker_name, " enter-hint");
|
|
object_get_marker_by_name(
|
|
target_unit_index,
|
|
enter_hint_marker_name,
|
|
&enter_hint_marker,
|
|
1);
|
|
|
|
if (exit_position)
|
|
{
|
|
*exit_position = seat_marker.matrix.position;
|
|
}
|
|
if (entry_position)
|
|
{
|
|
*entry_position = entrance_matrix.position;
|
|
}
|
|
if (seat_transform)
|
|
{
|
|
*seat_transform = enter_hint_marker.matrix.position;
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
animation_seat_index++;
|
|
}
|
|
while (animation_seat_index < animation_graph->unit_seats.count);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_get_melee_range_and_ticks(
|
|
long unit_index,
|
|
boolean secondary,
|
|
short *melee_tick,
|
|
real *attack_time,
|
|
short *frame_count,
|
|
real *damage_time)
|
|
{
|
|
boolean result;
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
struct animation_graph *animation_graph;
|
|
struct animation_graph_unit_seat *animation_seat;
|
|
struct animation_graph_weapon_class *weapon_class;
|
|
long weapon_class_animation_index;
|
|
short animation_index;
|
|
|
|
model_definition_get(unit_definition->object.model.index);
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
animation_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
weapon_class = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_seat->weapon_classes,
|
|
unit->unit.animation.weapon_index,
|
|
struct animation_graph_weapon_class);
|
|
|
|
weapon_class_animation_index = secondary ?
|
|
_unit_weapon_class_animation_melee_airborne :
|
|
_unit_weapon_class_animation_melee_attack;
|
|
if (VALID_INDEX(
|
|
weapon_class_animation_index,
|
|
weapon_class->animations.count))
|
|
{
|
|
animation_index = animation_graph_animation_index_get(
|
|
&weapon_class->animations)
|
|
[weapon_class_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
|
|
result = FALSE;
|
|
if (animation_index != NONE)
|
|
{
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
|
|
animation_get_x_offsets(animation, attack_time, damage_time);
|
|
if (melee_tick)
|
|
{
|
|
*melee_tick = animation->private_key_frame_index;
|
|
}
|
|
if (frame_count)
|
|
{
|
|
*frame_count = animation->frame_count;
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_set_user_animation(
|
|
long unit_index,
|
|
long animation_index,
|
|
short index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
animation_graph_definition_get(unit_definition->object.animation_graph.index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 6689, index>=0 && index<NUMBER_OF_UNIT_USER_ANIMATIONS);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
short unit_get_animation_frames_remaining(
|
|
long unit_index,
|
|
short *animation_state)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(unit->object.animation.animation_graph_index);
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(&animation_graph->animations, unit->object.animation.state.index, struct animation);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 1848, animation_state);
|
|
|
|
*animation_state = unit->unit.animation.state;
|
|
|
|
return animation->frame_count - unit->object.animation.state.frame_index;
|
|
}
|
|
|
|
short unit_get_zoom_level(
|
|
long unit_index)
|
|
{
|
|
return unit_get(unit_index)->unit.current_zoom_level;
|
|
}
|
|
|
|
real unit_get_zoom_magnification(
|
|
long unit_index,
|
|
short zoom_level)
|
|
{
|
|
real magnification = 1.f;
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long weapon_index = unit_inventory_get_weapon(unit_index, unit->unit.current_weapon_index);
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
magnification = weapon_get_zoom_magnification(weapon_index, zoom_level);
|
|
}
|
|
|
|
return magnification;
|
|
}
|
|
|
|
|
|
boolean unit_controllable(
|
|
long unit_index)
|
|
{
|
|
return TEST_FLAG(unit_get(unit_index)->unit.flags, _unit_controllable_bit);
|
|
}
|
|
|
|
boolean unit_is_busy(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
return unit_animation_busy(&unit->unit.animation);
|
|
}
|
|
|
|
void unit_set_enterable_by_player(
|
|
long unit_index,
|
|
boolean enterable_by_player)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
SET_FLAG(unit->unit.flags, _unit_not_enterable_by_player_bit, !enterable_by_player);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_aim_without_turning(
|
|
long unit_index,
|
|
boolean aim_without_turning)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
SET_FLAG(unit->unit.flags, _unit_aim_without_turning_bit, aim_without_turning);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_set_controllable(
|
|
long unit_index,
|
|
boolean controllable)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_controllable_bit, controllable);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_set_possessed(
|
|
long unit_index,
|
|
boolean possessed)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_possessed_by_recording_bit, possessed);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_scripting_can_blink(
|
|
long unit_index,
|
|
boolean can_blink)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_cannot_blink_bit, !can_blink);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_set_desired_flashlight_state(
|
|
long unit_index,
|
|
boolean desired_state)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (desired_state)
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_desired_integrated_light_on_bit, TRUE);
|
|
}
|
|
else
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_desired_integrated_light_off_bit, TRUE);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void units_set_desired_flashlight_state(
|
|
long object_list_index,
|
|
boolean desired_state)
|
|
{
|
|
long reference_index;
|
|
long unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (unit_index!=NONE)
|
|
{
|
|
if (unit_try_and_get(unit_index)!=NULL && unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (desired_state)
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_desired_integrated_light_on_bit, TRUE);
|
|
}
|
|
else
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_desired_integrated_light_off_bit, TRUE);
|
|
}
|
|
}
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_driven_by_ai(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->unit.driver_object_index!=NONE)
|
|
{
|
|
unit = unit_get(unit->unit.driver_object_index);
|
|
}
|
|
|
|
return unit->unit.actor_index!=NONE;
|
|
}
|
|
|
|
boolean unit_gunned_by_ai(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->unit.gunner_object_index!=NONE)
|
|
{
|
|
unit = unit_get(unit->unit.gunner_object_index);
|
|
}
|
|
|
|
return unit->unit.actor_index!=NONE;
|
|
}
|
|
|
|
short unit_test_spawning(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short spawned_actor_count = 0;
|
|
|
|
if (!TEST_FLAG(unit->unit.flags, _unit_spawned_actors_bit))
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (unit_definition->unit.spawned_actor_variant.index!=NONE)
|
|
{
|
|
spawned_actor_count = random_range(
|
|
unit_definition->unit.spawn_actor_lower_bound,
|
|
unit_definition->unit.spawn_actor_upper_bound + 1);
|
|
|
|
if (spawned_actor_count>0)
|
|
{
|
|
spawned_actor_count = actors_spawn_from_unit(
|
|
unit_index,
|
|
unit_definition->unit.spawned_actor_variant.index,
|
|
spawned_actor_count,
|
|
unit_definition->unit.spawn_throw_velocity * 0.033333335f);
|
|
}
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_spawned_actors_bit, TRUE);
|
|
}
|
|
}
|
|
|
|
return spawned_actor_count;
|
|
}
|
|
|
|
long unit_scripting_unit_riders(
|
|
long unit_index)
|
|
{
|
|
long object_list_index = NONE;
|
|
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
object_list_index = object_list_new();
|
|
|
|
if (object_list_index!=NONE)
|
|
{
|
|
long child_object_index = unit->object.first_child_object_index;
|
|
|
|
while (child_object_index!=NONE)
|
|
{
|
|
struct object_datum *child_object = object_get(child_object_index);
|
|
|
|
if (TEST_FLAG(_object_mask_unit, child_object->object.type))
|
|
{
|
|
struct unit_datum *rider = (struct unit_datum *)child_object;
|
|
|
|
if (rider->unit.parent_seat_index!=NONE)
|
|
{
|
|
object_list_add(object_list_index, child_object_index);
|
|
}
|
|
}
|
|
|
|
child_object_index = child_object->object.next_object_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
return object_list_index;
|
|
}
|
|
|
|
boolean unit_scripting_vehicle_test_seat_list(
|
|
long vehicle_index,
|
|
char const *seat_name,
|
|
long object_list_index)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (vehicle_index!=NONE)
|
|
{
|
|
short seat_index;
|
|
struct unit_datum *vehicle = unit_get(vehicle_index);
|
|
struct unit_definition *vehicle_definition = unit_definition_get(vehicle->definition_index);
|
|
|
|
for (seat_index = 0; seat_index<vehicle_definition->unit.seats.count; ++seat_index)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&vehicle_definition->unit.seats, seat_index, struct unit_seat);
|
|
|
|
if (!_stricmp(seat_name, seat->label))
|
|
{
|
|
struct object_iterator iterator;
|
|
struct unit_datum *unit;
|
|
|
|
object_iterator_new(&iterator, _object_mask_unit, 0);
|
|
unit = object_iterator_next(&iterator);
|
|
|
|
while (unit)
|
|
{
|
|
if (unit->object.parent_object_index==vehicle_index &&
|
|
unit->unit.parent_seat_index==seat_index)
|
|
{
|
|
long reference_index;
|
|
long object_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (object_index!=NONE && iterator.index!=object_index)
|
|
{
|
|
object_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
if (iterator.index==object_index)
|
|
{
|
|
result = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
unit = object_iterator_next(&iterator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_scripting_vehicle_test_seat(
|
|
long vehicle_index,
|
|
char const *seat_name,
|
|
long unit_index)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (vehicle_index!=NONE && unit_index!=NONE)
|
|
{
|
|
short seat_index;
|
|
struct unit_datum *vehicle = unit_get(vehicle_index);
|
|
struct unit_definition *vehicle_definition = unit_definition_get(vehicle->definition_index);
|
|
|
|
for (seat_index = 0; seat_index<vehicle_definition->unit.seats.count; ++seat_index)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&vehicle_definition->unit.seats, seat_index, struct unit_seat);
|
|
|
|
if (!_stricmp(seat_name, seat->label))
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->object.parent_object_index==vehicle_index && unit->unit.parent_seat_index==seat_index)
|
|
{
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_seat_filled(
|
|
long unit_index,
|
|
short seat_index)
|
|
{
|
|
struct object_iterator iterator;
|
|
struct unit_datum *unit;
|
|
boolean filled = FALSE;
|
|
|
|
object_iterator_new(&iterator, _object_mask_unit, 0);
|
|
|
|
while ((unit = object_iterator_next(&iterator))!=NULL)
|
|
{
|
|
if (unit->object.parent_object_index==unit_index && unit->unit.parent_seat_index==seat_index)
|
|
{
|
|
filled = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return filled;
|
|
}
|
|
|
|
boolean unit_seat_is_driver(
|
|
long unit_index,
|
|
short seat_index)
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit_get(unit_index)->definition_index);
|
|
boolean is_driver = FALSE;
|
|
|
|
if (seat_index>=0 && seat_index<unit_definition->unit.seats.count)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&unit_definition->unit.seats, seat_index, struct unit_seat);
|
|
is_driver = TEST_FLAG(seat->flags, _unit_seat_driver_bit);
|
|
}
|
|
|
|
return is_driver;
|
|
}
|
|
|
|
boolean unit_seat_is_gunner(
|
|
long unit_index,
|
|
short seat_index)
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit_get(unit_index)->definition_index);
|
|
boolean is_gunner = FALSE;
|
|
|
|
if (seat_index>=0 && seat_index<unit_definition->unit.seats.count)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&unit_definition->unit.seats, seat_index, struct unit_seat);
|
|
is_gunner = TEST_FLAG(seat->flags, _unit_seat_gunner_bit);
|
|
}
|
|
|
|
return is_gunner;
|
|
}
|
|
|
|
boolean unit_seat_allow_noncombatants(
|
|
long unit_index,
|
|
short seat_index)
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit_get(unit_index)->definition_index);
|
|
boolean allow_noncombatants = FALSE;
|
|
|
|
if (seat_index>=0 && seat_index<unit_definition->unit.seats.count)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&unit_definition->unit.seats, seat_index, struct unit_seat);
|
|
allow_noncombatants = TEST_FLAG(seat->flags, _unit_seat_allows_noncombatants_bit);
|
|
}
|
|
|
|
return allow_noncombatants;
|
|
}
|
|
|
|
void unit_delete_current_equipment(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->unit.equipment_object_index!=NONE)
|
|
{
|
|
object_delete(unit->unit.equipment_object_index);
|
|
unit->unit.equipment_object_index = NONE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_delete_all_weapons(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short weapon_index;
|
|
|
|
for (weapon_index = 0; weapon_index<MAXIMUM_WEAPONS_PER_UNIT; ++weapon_index)
|
|
{
|
|
if (unit->unit.weapon_object_indices[weapon_index]!=NONE && weapon_index!=unit->unit.current_weapon_index)
|
|
{
|
|
object_delete(unit->unit.weapon_object_indices[weapon_index]);
|
|
unit->unit.weapon_object_indices[weapon_index] = NONE;
|
|
|
|
if (weapon_index==unit->unit.desired_weapon_index)
|
|
{
|
|
unit->unit.desired_weapon_index = NONE;
|
|
}
|
|
|
|
if (weapon_index==unit->unit.current_weapon_index)
|
|
{
|
|
unit->unit.current_weapon_index = NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
short unit_get_weapon_count(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long weapon_count = 0;
|
|
short weapon_index;
|
|
|
|
for (weapon_index = 0; weapon_index<MAXIMUM_WEAPONS_PER_UNIT; ++weapon_index)
|
|
{
|
|
long weapon_object_index = unit->unit.weapon_object_indices[weapon_index];
|
|
|
|
if (weapon_object_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_object_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
if (!TEST_FLAG(weapon_definition->weapon.flags, _weapon_doesnt_count_toward_maximum_bit))
|
|
{
|
|
++weapon_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
return weapon_count;
|
|
}
|
|
|
|
boolean unit_approve_weapon_pickup(
|
|
long unit_index,
|
|
long weapon_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
long *inventory_weapon_index;
|
|
long remaining_weapon_count;
|
|
boolean approved;
|
|
|
|
weapon_definition_get(weapon->definition_index);
|
|
approved = TRUE;
|
|
inventory_weapon_index = unit->unit.weapon_object_indices;
|
|
remaining_weapon_count = MAXIMUM_WEAPONS_PER_UNIT;
|
|
|
|
while (remaining_weapon_count--)
|
|
{
|
|
if (*inventory_weapon_index!=NONE &&
|
|
weapon->definition_index==weapon_get(*inventory_weapon_index)->definition_index)
|
|
{
|
|
approved = FALSE;
|
|
}
|
|
|
|
++inventory_weapon_index;
|
|
}
|
|
|
|
return approved;
|
|
}
|
|
|
|
boolean unit_approve_weapon_swap(
|
|
long unit_index,
|
|
long weapon_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
boolean approved;
|
|
short inventory_index;
|
|
|
|
weapon_definition_get(weapon->definition_index);
|
|
approved = TRUE;
|
|
if (unit_get_current_weapon_index(unit_index)!=NONE)
|
|
{
|
|
for (inventory_index = 0;
|
|
inventory_index<MAXIMUM_WEAPONS_PER_UNIT;
|
|
++inventory_index)
|
|
{
|
|
long inventory_weapon_index =
|
|
unit->unit.weapon_object_indices[inventory_index];
|
|
|
|
if (inventory_weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *inventory_weapon =
|
|
weapon_get(inventory_weapon_index);
|
|
|
|
if (weapon->definition_index==inventory_weapon->definition_index &&
|
|
(inventory_index!=unit->unit.current_weapon_index ||
|
|
!(inventory_weapon->weapon.age>0.f) ||
|
|
!(weapon->weapon.age<inventory_weapon->weapon.age)))
|
|
{
|
|
approved = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return approved;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
boolean unit_has_weapon_definition_index(
|
|
long unit_index,
|
|
long weapon_definition_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short weapon_index;
|
|
|
|
for (weapon_index = 0; weapon_index<MAXIMUM_WEAPONS_PER_UNIT; ++weapon_index)
|
|
{
|
|
long weapon_object_index = unit->unit.weapon_object_indices[weapon_index];
|
|
|
|
if (weapon_object_index!=NONE && weapon_get(weapon_object_index)->definition_index==weapon_definition_index)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
boolean unit_has_weapon_with_flag(
|
|
long unit_index,
|
|
long flag_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
boolean has_weapon = FALSE;
|
|
long weapon_index;
|
|
|
|
for (weapon_index = 0; weapon_index<MAXIMUM_WEAPONS_PER_UNIT; ++weapon_index)
|
|
{
|
|
long weapon_object_index = unit->unit.weapon_object_indices[weapon_index];
|
|
|
|
if (weapon_object_index!=NONE && TEST_FLAG(weapon_get(weapon_object_index)->weapon.flags, flag_index))
|
|
{
|
|
has_weapon = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return has_weapon;
|
|
}
|
|
|
|
short unit_get_grenade_count(
|
|
long unit_index,
|
|
short grenade_type)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (grenade_type!=NONE)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 7847, grenade_type==NONE || (grenade_type>=0 && grenade_type<NUMBER_OF_UNIT_GRENADE_TYPES));
|
|
|
|
return unit->unit.grenade_counts[grenade_type];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
short unit_get_current_grenade_type(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 7864, unit->unit.current_grenade_index==NONE || (unit->unit.current_grenade_index>=0 && unit->unit.current_grenade_index<NUMBER_OF_UNIT_GRENADE_TYPES));
|
|
|
|
return unit->unit.current_grenade_index;
|
|
}
|
|
|
|
short unit_inventory_next_grenade(
|
|
long unit_index,
|
|
short current_index,
|
|
short delta)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short next_index = NONE;
|
|
short index;
|
|
|
|
if (current_index==NONE)
|
|
{
|
|
current_index = 0;
|
|
}
|
|
else
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 5694, current_index>=0 && current_index<NUMBER_OF_UNIT_GRENADE_TYPES);
|
|
}
|
|
|
|
index = current_index;
|
|
|
|
do
|
|
{
|
|
if (unit->unit.grenade_counts[index]>0)
|
|
{
|
|
next_index = index;
|
|
|
|
if (index!=current_index || delta==0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (delta<0)
|
|
{
|
|
index = index==0 ? NUMBER_OF_UNIT_GRENADE_TYPES-1 : index-1;
|
|
}
|
|
else
|
|
{
|
|
index = index==NUMBER_OF_UNIT_GRENADE_TYPES-1 ? 0 : index+1;
|
|
}
|
|
} while (index!=current_index);
|
|
|
|
return next_index;
|
|
}
|
|
|
|
short unit_inventory_get_must_be_readied_weapon(
|
|
long unit_index)
|
|
{
|
|
short must_be_readied_weapon_index = NONE;
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short index;
|
|
|
|
for (index = 0; index<MAXIMUM_WEAPONS_PER_UNIT; ++index)
|
|
{
|
|
long weapon_index = unit->unit.weapon_object_indices[index];
|
|
|
|
if (weapon_index!=NONE && weapon_must_be_readied(weapon_index))
|
|
{
|
|
must_be_readied_weapon_index = index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return must_be_readied_weapon_index;
|
|
}
|
|
|
|
short unit_add_grenade_type_to_inventory(
|
|
long unit_index,
|
|
short grenade_type,
|
|
short grenade_count)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 7309, grenade_count>=0);
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 7310, (grenade_type >= 0) && (grenade_type < NUMBER_OF_UNIT_GRENADE_TYPES));
|
|
|
|
unit->unit.grenade_counts[grenade_type] += grenade_count;
|
|
unit->unit.desired_grenade_index = grenade_type;
|
|
unit->unit.current_grenade_index = grenade_type;
|
|
|
|
return unit->unit.grenade_counts[grenade_type];
|
|
}
|
|
|
|
boolean unit_add_grenade_to_inventory(
|
|
long unit_index,
|
|
long equipment_index)
|
|
{
|
|
struct item_datum *equipment = equipment_get(equipment_index);
|
|
struct equipment_definition *equipment_definition =
|
|
equipment_definition_get(equipment->definition_index);
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct game_globals_grenade *grenade = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->grenades,
|
|
equipment_definition->equipment.grenade_type,
|
|
struct game_globals_grenade);
|
|
long local_player_index;
|
|
boolean result = FALSE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
7282,
|
|
equipment_definition->equipment.powerup_type==_equipment_powerup_grenade);
|
|
|
|
if (grenade &&
|
|
unit->unit.grenade_counts[equipment_definition->equipment.grenade_type] <
|
|
grenade->maximum_count)
|
|
{
|
|
++unit->unit.grenade_counts[equipment_definition->equipment.grenade_type];
|
|
|
|
if (player_index_from_unit_index(unit_index)==NONE)
|
|
{
|
|
local_player_index = NONE;
|
|
}
|
|
else
|
|
{
|
|
local_player_index =
|
|
player_get(player_index_from_unit_index(unit_index))->local_player_index;
|
|
}
|
|
|
|
if (local_player_index!=NONE)
|
|
{
|
|
equipment_handle_pickup(equipment_index);
|
|
}
|
|
|
|
object_delete(equipment_index);
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_add_equipment_to_inventory(
|
|
long unit_index,
|
|
long equipment_index,
|
|
short replace)
|
|
{
|
|
struct item_datum *equipment = equipment_get(equipment_index);
|
|
struct equipment_definition *equipment_definition =
|
|
equipment_definition_get(equipment->definition_index);
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long local_player_index;
|
|
boolean result = FALSE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
7329,
|
|
equipment_definition->equipment.powerup_type!=_equipment_powerup_none);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
7330,
|
|
equipment_definition->equipment.powerup_type!=_equipment_powerup_grenade);
|
|
|
|
if (unit->unit.equipment_object_index!=NONE && replace==TRUE)
|
|
{
|
|
object_delete(unit->unit.equipment_object_index);
|
|
unit->unit.equipment_object_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.equipment_object_index==NONE)
|
|
{
|
|
object_disconnect_from_map(equipment_index);
|
|
object_set_visibility(equipment_index, FALSE);
|
|
|
|
if (player_index_from_unit_index(unit_index)==NONE)
|
|
{
|
|
local_player_index = NONE;
|
|
}
|
|
else
|
|
{
|
|
local_player_index =
|
|
player_get(player_index_from_unit_index(unit_index))->local_player_index;
|
|
}
|
|
|
|
if (local_player_index!=NONE)
|
|
{
|
|
equipment_handle_pickup(equipment_index);
|
|
}
|
|
|
|
item_in_unit_inventory(equipment_index, unit_index);
|
|
unit->unit.equipment_object_index = equipment_index;
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void unit_scripting_doesnt_drop_items(
|
|
long object_list_index)
|
|
{
|
|
long reference_index;
|
|
long object_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (object_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(object_index);
|
|
|
|
if (unit)
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_doesnt_drop_items_bit, TRUE);
|
|
}
|
|
|
|
object_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_scripting_set_emotion_animation(
|
|
long unit_index,
|
|
char const *animation_name)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short animation_index = animation_graph_get_animation_by_name(unit->object.animation.animation_graph_index, animation_name);
|
|
|
|
if (animation_index!=NONE)
|
|
{
|
|
unit->unit.override_emotion_animation_index = animation_index;
|
|
}
|
|
else
|
|
{
|
|
console_warning("couldn't find the emotion animation '%s'", animation_name);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_scripting_suspended(
|
|
long unit_index,
|
|
boolean suspended)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_suspended_bit, suspended);
|
|
unit->object.translational_velocity = *global_zero_vector3d;
|
|
|
|
if (unit->object.type==_object_type_biped)
|
|
{
|
|
struct biped_datum *biped = biped_get(unit_index);
|
|
SET_FLAG(biped->biped.flags, _biped_airborne_bit, FALSE);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean any_unit_is_dangerous(
|
|
void)
|
|
{
|
|
struct object_iterator iterator;
|
|
struct unit_datum *unit;
|
|
|
|
object_iterator_new(&iterator, _object_mask_unit, 1);
|
|
|
|
while ((unit = object_iterator_next(&iterator))!=NULL)
|
|
{
|
|
if ((unit->unit.animation.state==_unit_state_throw_grenade && unit->unit.grenade_throw_state!=_unit_grenade_throw_ending) ||
|
|
((unit->unit.animation.state==_unit_state_dying || unit->unit.animation.state==_unit_state_dying_airborne) &&
|
|
!TEST_FLAG(unit->unit.animation.flags, _unit_animation_ignore_translation_bit)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
boolean unit_custom_animation_at_frame(
|
|
long unit_index,
|
|
long animation_graph_index,
|
|
char const *animation_name,
|
|
boolean interpolate,
|
|
short frame_index)
|
|
{
|
|
boolean success = FALSE;
|
|
|
|
if (unit_start_user_animation(unit_index, animation_graph_index, animation_name, interpolate))
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(unit->object.animation.animation_graph_index);
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(&animation_graph->animations, unit->object.animation.state.index, struct animation);
|
|
|
|
if (frame_index>=0 && frame_index<animation->frame_count)
|
|
{
|
|
unit->object.animation.state.frame_index = frame_index;
|
|
success = TRUE;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
long unit_get_current_equipment(
|
|
long unit_index)
|
|
{
|
|
return unit_get(unit_index)->unit.equipment_object_index;
|
|
}
|
|
|
|
void unit_drop_current_equipment(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long equipment_index = unit->unit.equipment_object_index;
|
|
|
|
if (equipment_index!=NONE)
|
|
{
|
|
unit_drop_item(unit_index, equipment_index);
|
|
unit->unit.equipment_object_index = NONE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_can_use_weapon(
|
|
long unit_index,
|
|
long weapon_index)
|
|
{
|
|
boolean can_use_weapon;
|
|
char const *seat_label;
|
|
char const *weapon_label;
|
|
|
|
unit_get(unit_index);
|
|
weapon_get(weapon_index);
|
|
|
|
seat_label = unit_get_seat_label(unit_index);
|
|
weapon_label = weapon_get_label(weapon_index);
|
|
|
|
can_use_weapon = unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
seat_label,
|
|
weapon_label,
|
|
FALSE);
|
|
|
|
if (can_use_weapon)
|
|
{
|
|
can_use_weapon = game_engine_allow_pick_up(unit_index, weapon_index);
|
|
}
|
|
|
|
return can_use_weapon;
|
|
}
|
|
|
|
boolean unit_overcharged(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long weapon_index = unit_inventory_get_weapon(unit_index, unit->unit.current_weapon_index);
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
return weapon_overcharged(weapon_index);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
boolean unit_solo_player_integrated_night_vision_is_active(
|
|
void)
|
|
{
|
|
boolean active = FALSE;
|
|
|
|
if (local_player_count()==1)
|
|
{
|
|
long player_index = local_player_get_player_index(local_player_get_next(NONE));
|
|
|
|
if (player_index!=NONE)
|
|
{
|
|
long unit_index = player_get(player_index)->unit_index;
|
|
|
|
if (unit_index!=NONE)
|
|
{
|
|
active = unit_integrated_night_vision_is_active(unit_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return active;
|
|
}
|
|
|
|
// HCEX_Release.pdb and the September 2001 map both name this private
|
|
// helper unit_add_initial_weapons (file-static; no cachebeta public).
|
|
static void unit_add_initial_weapons(
|
|
long unit_index)
|
|
{
|
|
short initial_weapon_index;
|
|
struct object_placement_data placement_data;
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
/* (a client of the distributed netcode making the host's unit: its
|
|
weapons are the host's objects, port/linux/game/network_objects.c) */
|
|
if (network_objects_creating_host_object())
|
|
return;
|
|
for (initial_weapon_index = 0;
|
|
initial_weapon_index < unit_definition->unit.initial_weapons.count;
|
|
initial_weapon_index++)
|
|
{
|
|
struct unit_initial_weapon *initial_weapon = TAG_BLOCK_GET_ELEMENT(
|
|
&unit_definition->unit.initial_weapons,
|
|
initial_weapon_index,
|
|
struct unit_initial_weapon);
|
|
|
|
if (initial_weapon->weapon.index!=NONE)
|
|
{
|
|
long weapon_index;
|
|
|
|
object_placement_data_new(
|
|
&placement_data,
|
|
initial_weapon->weapon.index,
|
|
unit_index);
|
|
weapon_index = object_new(&placement_data);
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
if ((game_engine_running() &&
|
|
unit_has_weapon_definition_index(unit_index, weapon->definition_index)) ||
|
|
!unit_add_weapon_to_inventory(unit_index, weapon_index, FALSE))
|
|
{
|
|
object_delete(weapon_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_new(
|
|
long unit_index)
|
|
{
|
|
boolean result;
|
|
short seat_index;
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
result = FALSE;
|
|
|
|
if (unit_definition->object.animation_graph.index!=NONE)
|
|
{
|
|
unit->unit.equipment_object_index = NONE;
|
|
csmemset(
|
|
unit->unit.weapon_object_indices,
|
|
NONE,
|
|
sizeof(unit->unit.weapon_object_indices));
|
|
|
|
unit->unit.current_weapon_index = NONE;
|
|
unit->unit.desired_weapon_index = NONE;
|
|
unit->unit.current_grenade_index = NONE;
|
|
unit->unit.desired_grenade_index = NONE;
|
|
unit->unit.current_zoom_level = NONE;
|
|
unit->unit.desired_zoom_level = NONE;
|
|
unit->unit.player_index = NONE;
|
|
unit->unit.actor_index = NONE;
|
|
unit->unit.swarm_actor_index = NONE;
|
|
unit->unit.swarm_next_unit_index = NONE;
|
|
unit->unit.swarm_prev_unit_index = NONE;
|
|
unit->unit.parent_seat_index = NONE;
|
|
unit->unit.driver_object_index = NONE;
|
|
unit->unit.gunner_object_index = NONE;
|
|
|
|
unit->unit.animation.flags = 0;
|
|
unit->unit.animation.seat_index = NONE;
|
|
unit->unit.animation.weapon_index = NONE;
|
|
unit->unit.animation.weapon_type_index = NONE;
|
|
unit->unit.animation.state = NONE;
|
|
unit->unit.animation.action = 0;
|
|
unit->unit.animation.overlay_action = 0;
|
|
unit->unit.animation.aiming_screen_index = NONE;
|
|
unit->unit.animation.looking_screen_index = NONE;
|
|
unit->unit.animation.action_animation.index = NONE;
|
|
unit->unit.animation.overlay_action_animation.index = NONE;
|
|
unit->unit.animation.soft_ping_animation.index = NONE;
|
|
unit->unit.animation.base_seat_index = _unit_base_seat_stand;
|
|
unit->unit.animation.last_ping_animation_index = NONE;
|
|
unit->unit.animation.emotion_index = NONE;
|
|
unit->unit.override_emotion_animation_index = NONE;
|
|
unit->unit.magic_seat_index = NONE;
|
|
unit->unit.animation.aiming_with_euler_screen = FALSE;
|
|
csmemset(
|
|
&unit->unit.animation.aiming_screen_bounds,
|
|
0,
|
|
sizeof(unit->unit.animation.aiming_screen_bounds));
|
|
unit->unit.animation.looking_with_euler_screen = FALSE;
|
|
csmemset(
|
|
&unit->unit.animation.looking_screen_bounds,
|
|
0,
|
|
sizeof(unit->unit.animation.looking_screen_bounds));
|
|
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
393,
|
|
&unit->object.forward);
|
|
|
|
unit->unit.desired_facing_vector =
|
|
unit->unit.desired_aiming_vector =
|
|
unit->unit.aiming_vector =
|
|
unit->unit.desired_looking_vector =
|
|
unit->unit.looking_vector = unit->object.forward;
|
|
|
|
unit->unit.persistent_control_timer = 0;
|
|
unit->unit.dialogue_index = NONE;
|
|
SET_FLAG(unit->unit.flags, _unit_must_set_up_dialogue_bit, TRUE);
|
|
csmemset(&unit->unit.speech, 0, sizeof(unit->unit.speech));
|
|
unit->unit.speech.last_speech_finished_time = NONE;
|
|
unit_dialogue_determine_variant(unit_index);
|
|
csmemset(unit->unit.attackers, NONE, sizeof(unit->unit.attackers));
|
|
|
|
unit->unit.last_damage_category = 0;
|
|
unit->unit.delayed_damage_timer = 0;
|
|
unit->unit.delayed_damage_peak = 0.f;
|
|
unit->unit.killing_spree_count = 0;
|
|
unit->unit.delayed_damage_attacker_object_index = NONE;
|
|
unit->unit.time_of_death = NONE;
|
|
unit->unit.fake_encounter_index = NONE;
|
|
unit->unit.fake_squad_index = NONE;
|
|
unit->unit.integrated_light_battery = 1.f;
|
|
unit->unit.flaming_death_delay = 0;
|
|
unit->unit.flaming_death_attacker_object_index = NONE;
|
|
unit->unit.killing_spree_last_time = NONE;
|
|
|
|
if (unit_definition->unit.grenade_type>=0 &&
|
|
unit_definition->unit.grenade_type<NUMBER_OF_UNIT_GRENADE_TYPES &&
|
|
unit_definition->unit.grenade_count>=0)
|
|
{
|
|
unit->unit.grenade_counts[unit_definition->unit.grenade_type] =
|
|
(char)unit_definition->unit.grenade_count;
|
|
}
|
|
|
|
unit->object.flags |=
|
|
FLAG(_object_dynamic_lighting_recompute_bit) |
|
|
FLAG(_object_static_lighting_recompute_bit);
|
|
|
|
if (unit_definition->unit.feign_death_threshold>0.f &&
|
|
unit_definition->unit.feign_death_time>0.f &&
|
|
unit_definition->unit.feign_death_chance>0.f)
|
|
{
|
|
SET_FLAG(
|
|
unit->unit.flags,
|
|
_unit_feign_death_allowed_bit,
|
|
real_seed_random(get_global_random_seed_address()) <
|
|
unit_definition->unit.feign_death_chance);
|
|
}
|
|
|
|
if (!game_engine_running() &&
|
|
(unit->object.owner_team_index==0 ||
|
|
unit->object.owner_team_index==NONE))
|
|
{
|
|
unit->object.owner_team_index = unit_definition->unit.default_team;
|
|
}
|
|
|
|
unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
base_seat_labels[_unit_base_seat_stand],
|
|
NULL,
|
|
TRUE);
|
|
unit_add_initial_weapons(unit_index);
|
|
|
|
for (seat_index = 0;
|
|
seat_index<unit_definition->unit.seats.count;
|
|
seat_index++)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&unit_definition->unit.seats,
|
|
seat_index,
|
|
struct unit_seat);
|
|
|
|
if (seat->built_in_actor_reference.index!=NONE)
|
|
{
|
|
ai_create_mounted_weapons_for_unit(unit_index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_has_weapon(
|
|
long unit_index,
|
|
long weapon_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long index;
|
|
boolean has_weapon = FALSE;
|
|
|
|
for (index = 0; index<MAXIMUM_WEAPONS_PER_UNIT; ++index)
|
|
{
|
|
if (unit->unit.weapon_object_indices[index]==weapon_index)
|
|
{
|
|
has_weapon = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return has_weapon;
|
|
}
|
|
|
|
void unit_set_mouth_aperture(
|
|
long unit_index,
|
|
real mouth_aperture)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
real *current_mouth_aperture = &unit->unit.mouth_aperture;
|
|
|
|
*current_mouth_aperture += PIN(mouth_aperture - *current_mouth_aperture, -0.3f, 0.3f);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_destroy(
|
|
long unit_index)
|
|
{
|
|
object_destroy(unit_index);
|
|
unit_test_spawning(unit_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_drop_inventory_weapons(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct weapon_datum *weapon;
|
|
long *weapon_object_index;
|
|
long weapon_index;
|
|
short inventory_index;
|
|
|
|
unit = unit_get(unit_index);
|
|
inventory_index = 0;
|
|
weapon_object_index = unit->unit.weapon_object_indices;
|
|
do
|
|
{
|
|
weapon_index = *weapon_object_index;
|
|
if (weapon_index != NONE &&
|
|
inventory_index != unit->unit.current_weapon_index)
|
|
{
|
|
weapon = weapon_get(weapon_index);
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
8505,
|
|
!TEST_FLAG(weapon->object.flags, _object_connected_to_map_bit),
|
|
csprintf(
|
|
temporary,
|
|
"a %s tried to drop a %s which was connected to the map.",
|
|
tag_get_name(unit->definition_index),
|
|
tag_get_name(weapon->definition_index)));
|
|
|
|
unit_drop_item(unit_index, weapon_index);
|
|
if (inventory_index == unit->unit.desired_weapon_index)
|
|
unit->unit.desired_weapon_index = unit->unit.current_weapon_index;
|
|
|
|
*weapon_object_index = NONE;
|
|
if (!weapon_can_be_fired(weapon_index))
|
|
object_delete(weapon_index);
|
|
}
|
|
|
|
inventory_index++;
|
|
weapon_object_index++;
|
|
} while (inventory_index < MAXIMUM_WEAPONS_PER_UNIT);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_died(
|
|
long unit_index,
|
|
boolean feigned)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct unit_datum *fresh_unit;
|
|
struct unit_definition *unit_definition;
|
|
struct actor_datum *actor;
|
|
long equipment_index;
|
|
|
|
unit = unit_get(unit_index);
|
|
if (!feigned)
|
|
{
|
|
unit->unit.feign_death_timer = 0;
|
|
}
|
|
else
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
5099,
|
|
unit->unit.feign_death_timer > 0);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
if (real_seed_random(get_global_random_seed_address()) <
|
|
unit_definition->unit.feign_death_repeat_chance)
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_feign_death_allowed_bit, TRUE);
|
|
}
|
|
else
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_feign_death_allowed_bit, FALSE);
|
|
}
|
|
}
|
|
|
|
if (!feigned)
|
|
{
|
|
object_set_garbage(unit_index, TRUE);
|
|
if (unit->unit.player_index != NONE)
|
|
{
|
|
player_died(unit->unit.player_index);
|
|
unit->unit.player_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.actor_index != NONE)
|
|
{
|
|
actor = actor_get(unit->unit.actor_index);
|
|
unit->unit.fake_encounter_index = actor->meta.encounter_index;
|
|
unit->unit.fake_squad_index = actor->meta.squad_index;
|
|
actor_died(unit->unit.actor_index);
|
|
unit->unit.actor_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.swarm_actor_index != NONE)
|
|
{
|
|
actor = actor_get(unit->unit.swarm_actor_index);
|
|
unit->unit.fake_encounter_index = actor->meta.encounter_index;
|
|
unit->unit.fake_squad_index = actor->meta.squad_index;
|
|
actor_swarm_unit_died(unit->unit.swarm_actor_index, unit_index);
|
|
unit->unit.swarm_actor_index = NONE;
|
|
}
|
|
|
|
unit->unit.time_of_death = game_time_get();
|
|
}
|
|
|
|
unit->unit.flags &= ~(
|
|
FLAG(_unit_actively_controlled_bit) |
|
|
FLAG(_unit_active_camouflaged_bit));
|
|
unit->unit.control_flags = 0;
|
|
if (unit->unit.current_weapon_index != NONE)
|
|
{
|
|
weapon_owner_update(
|
|
unit_inventory_get_weapon(
|
|
unit_index,
|
|
(word)unit_get(unit_index)->unit.current_weapon_index),
|
|
0,
|
|
0.0f);
|
|
}
|
|
|
|
fresh_unit = unit_get(unit_index);
|
|
fresh_unit->unit.flags &= ~FLAG(_unit_running_blindly_bit);
|
|
if (unit->object.parent_object_index != NONE)
|
|
{
|
|
if (unit->unit.parent_seat_index != NONE)
|
|
unit_exit_seat_end(unit_index);
|
|
else
|
|
unit_detach_from_parent(unit_index);
|
|
}
|
|
|
|
unit->unit.speech.queued.priority = 0;
|
|
unit_drop_inventory_weapons(unit_index);
|
|
fresh_unit = unit_get(unit_index);
|
|
if (fresh_unit->unit.equipment_object_index != NONE)
|
|
{
|
|
equipment_index = fresh_unit->unit.equipment_object_index;
|
|
unit_drop_item(unit_index, equipment_index);
|
|
fresh_unit->unit.equipment_object_index = NONE;
|
|
}
|
|
|
|
unit_drop_grenades(unit_index);
|
|
if (unit->unit.weapon_drop_delay_ticks == 0)
|
|
unit_drop_current_weapon(unit_index, TRUE);
|
|
|
|
unit->unit.animation.overlay_action_animation.index = NONE;
|
|
unit->unit.animation.action_animation.index = NONE;
|
|
unit->unit.melee_attack_state = 0;
|
|
if (unit->unit.grenade_throw_state == 1)
|
|
unit->unit.grenade_throw_state = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_drop_grenades(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct object_placement_data placement_data;
|
|
unsigned long grenade_count_negative_base;
|
|
char *grenade_count;
|
|
long grenade_object_index;
|
|
long grenade_type_count;
|
|
struct game_globals_grenade *grenade;
|
|
|
|
unit = unit_get(unit_index);
|
|
grenade_count = unit->unit.grenade_counts;
|
|
grenade_count_negative_base =
|
|
0u - (unsigned long)unit->unit.grenade_counts;
|
|
grenade_type_count = NUMBER_OF_UNIT_GRENADE_TYPES;
|
|
do
|
|
{
|
|
grenade = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->grenades,
|
|
grenade_count_negative_base + (unsigned long)grenade_count,
|
|
struct game_globals_grenade);
|
|
while (*grenade_count > 0)
|
|
{
|
|
object_placement_data_new(
|
|
&placement_data,
|
|
grenade->item.index,
|
|
unit_index);
|
|
grenade_object_index = object_new(&placement_data);
|
|
if (grenade_object_index != NONE)
|
|
{
|
|
object_disconnect_from_map(grenade_object_index);
|
|
unit_drop_item(unit_index, grenade_object_index);
|
|
}
|
|
|
|
(*grenade_count)--;
|
|
}
|
|
|
|
grenade_count++;
|
|
} while (--grenade_type_count);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_get_facing_vector(
|
|
long unit_index,
|
|
real_vector3d *facing_vector)
|
|
{
|
|
object_get_orientation(unit_index, facing_vector, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_get_center_of_mass(
|
|
long unit_index,
|
|
real_point3d *center_of_mass)
|
|
{
|
|
struct object_marker body_marker;
|
|
|
|
object_get_marker_by_name(unit_index, "body", &body_marker, 1);
|
|
*center_of_mass = body_marker.matrix.position;
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_get_aiming_vector(
|
|
long unit_index,
|
|
real_vector3d *aiming_vector)
|
|
{
|
|
*aiming_vector = unit_get(unit_index)->unit.aiming_vector;
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_can_see_point(
|
|
long unit_index,
|
|
real_point3d const *point,
|
|
real field_of_view)
|
|
{
|
|
boolean can_see_point = FALSE;
|
|
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct object_marker head_marker;
|
|
real_vector3d direction;
|
|
|
|
object_get_marker_by_name(unit_index, "head", &head_marker, 1);
|
|
vector_from_points3d(&head_marker.matrix.position, point, &direction);
|
|
normalize3d(&direction);
|
|
|
|
if (dot_product3d(&direction, &unit->unit.looking_vector)>cosine(field_of_view))
|
|
{
|
|
can_see_point = TRUE;
|
|
}
|
|
}
|
|
|
|
return can_see_point;
|
|
}
|
|
|
|
boolean unit_has_animation_to_enter_seat(
|
|
long unit_index,
|
|
long target_unit_index,
|
|
short seat_index)
|
|
{
|
|
struct unit_datum *target_unit = unit_get(target_unit_index);
|
|
struct unit_definition *target_unit_definition = unit_definition_get(target_unit->definition_index);
|
|
boolean has_animation = FALSE;
|
|
|
|
if (seat_index>=0 && seat_index<target_unit_definition->unit.seats.count)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->object.type==_object_type_vehicle)
|
|
{
|
|
has_animation = TRUE;
|
|
}
|
|
else
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&target_unit_definition->unit.seats, seat_index, struct unit_seat);
|
|
|
|
if (unit_set_or_test_seat_and_weapon_label(unit_index, seat->label, NULL, FALSE))
|
|
{
|
|
has_animation = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return has_animation;
|
|
}
|
|
|
|
boolean unit_can_enter_seat(
|
|
long unit_index,
|
|
long target_unit_index,
|
|
short seat_index,
|
|
long *occupant_unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_datum *target_unit = unit_get(target_unit_index);
|
|
long occupant_index = NONE;
|
|
boolean result = TRUE;
|
|
long child_object_index;
|
|
|
|
if (unit_index==target_unit_index)
|
|
{
|
|
result = FALSE;
|
|
}
|
|
|
|
child_object_index = target_unit->object.first_child_object_index;
|
|
|
|
while (child_object_index!=NONE)
|
|
{
|
|
struct object_datum *child_object = object_get(child_object_index);
|
|
|
|
if (TEST_FLAG(_object_mask_unit, child_object->object.type))
|
|
{
|
|
struct unit_datum *child_unit = unit_get(child_object_index);
|
|
|
|
if (child_unit->unit.parent_seat_index==seat_index)
|
|
{
|
|
occupant_index = child_object_index;
|
|
result = FALSE;
|
|
}
|
|
else if (unit->unit.player_index!=NONE &&
|
|
game_team_is_enemy(
|
|
unit->object.owner_team_index,
|
|
child_object->object.owner_team_index))
|
|
{
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
child_object_index = child_object->object.next_object_index;
|
|
}
|
|
|
|
if (occupant_unit_index)
|
|
{
|
|
*occupant_unit_index = occupant_index;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void unit_adjust_for_seat_change(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
unit->unit.desired_weapon_index = unit_weapon_next_index(
|
|
unit_index,
|
|
unit->unit.current_weapon_index,
|
|
0);
|
|
unit_ready_desired_weapon(unit_index, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_enter_seat(
|
|
long unit_index,
|
|
long target_unit_index,
|
|
short seat_index)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (unit_can_enter_seat(unit_index, target_unit_index, seat_index, NULL))
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
real_vector3d offset;
|
|
struct unit_definition *unit_definition;
|
|
struct animation_graph *animation_graph;
|
|
struct animation_graph_unit_seat *animation_seat;
|
|
long animation_graph_index;
|
|
|
|
{
|
|
struct unit_datum *target_unit = unit_get(target_unit_index);
|
|
struct unit_definition *target_unit_definition =
|
|
unit_definition_get(target_unit->definition_index);
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&target_unit_definition->unit.seats,
|
|
seat_index,
|
|
struct unit_seat);
|
|
real_point3d origin;
|
|
struct object_marker seat_marker;
|
|
struct unit_datum *updated_unit;
|
|
long weapon_index;
|
|
char const *weapon_label;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
4245,
|
|
unit->object.parent_object_index==NONE);
|
|
|
|
object_get_origin(unit_index, &origin);
|
|
object_get_marker_by_name(
|
|
target_unit_index,
|
|
seat->marker_name,
|
|
&seat_marker,
|
|
1);
|
|
vector_from_points3d(
|
|
&seat_marker.matrix.position,
|
|
&origin,
|
|
&offset);
|
|
matrix4x3_inverse_transform_vector(
|
|
&seat_marker.matrix,
|
|
&offset,
|
|
&offset);
|
|
object_attach_to_marker(
|
|
target_unit_index,
|
|
seat->marker_name,
|
|
unit_index,
|
|
"");
|
|
|
|
unit->unit.parent_seat_index = seat_index;
|
|
unit->object.parent_object_index = target_unit_index;
|
|
unit_update_driver_and_gunner(target_unit_index);
|
|
|
|
updated_unit = unit_get(unit_index);
|
|
updated_unit->unit.desired_weapon_index = unit_weapon_next_index(
|
|
unit_index,
|
|
updated_unit->unit.current_weapon_index,
|
|
0);
|
|
unit_ready_desired_weapon(unit_index, TRUE);
|
|
weapon_index = unit_get_current_weapon_index(unit_index);
|
|
weapon_label = weapon_index==NONE ?
|
|
"unarmed" : weapon_get_label(weapon_index);
|
|
if (!unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
seat->label,
|
|
weapon_label,
|
|
TRUE))
|
|
{
|
|
unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
seat->label,
|
|
NULL,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
animation_graph_index = unit_definition->object.animation_graph.index;
|
|
animation_graph = animation_graph_definition_get(
|
|
animation_graph_index);
|
|
animation_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
if (animation_seat->animations.count>_unit_seat_animation_seat_enter)
|
|
{
|
|
short animation_index = animation_graph_animation_index_get(
|
|
&animation_seat->animations)
|
|
[_unit_seat_animation_seat_enter].animation_index;
|
|
|
|
if (animation_index!=NONE)
|
|
{
|
|
object_start_interpolation(unit_index, 6);
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
animation_index = animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
animation_index);
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
unit_set_animation(
|
|
unit_index,
|
|
animation_graph_index,
|
|
animation_index);
|
|
unit->unit.animation.state = _unit_state_entering_seat;
|
|
object_offset_interpolation(unit_index, &offset);
|
|
object_compute_node_matrices_recursive(unit_index);
|
|
}
|
|
}
|
|
|
|
ai_handle_enter_vehicle(unit_index, target_unit_index);
|
|
unit_unzoom(unit_index);
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void unit_exit_seat_end(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long parent_unit_index = unit->object.parent_object_index;
|
|
|
|
if (parent_unit_index!=NONE && unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
struct unit_datum *parent_unit = unit_get(parent_unit_index);
|
|
struct unit_definition *parent_unit_definition =
|
|
unit_definition_get(parent_unit->definition_index);
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&parent_unit_definition->unit.seats,
|
|
unit->unit.parent_seat_index,
|
|
struct unit_seat);
|
|
real_matrix4x3 *unit_node_matrix = object_get_node_matrix(unit_index, 0);
|
|
struct object_marker seat_marker;
|
|
real_vector3d relative_position;
|
|
real_vector3d exit_offset;
|
|
struct unit_definition *unit_definition;
|
|
struct model *model;
|
|
struct model_node *root_node;
|
|
real_point3d root_translation;
|
|
real_matrix4x3 const *root_inverse_matrix;
|
|
real_matrix4x3 const *updated_unit_node_matrix;
|
|
real_point3d position;
|
|
real_matrix4x3 matrix;
|
|
struct real_orientation *node_orientations;
|
|
|
|
object_get_marker_by_name(
|
|
parent_unit_index,
|
|
seat->marker_name,
|
|
&seat_marker,
|
|
1);
|
|
vector_from_points3d(
|
|
&seat_marker.matrix.position,
|
|
&unit_node_matrix->position,
|
|
&relative_position);
|
|
matrix4x3_inverse_transform_vector(
|
|
&seat_marker.matrix,
|
|
&relative_position,
|
|
&exit_offset);
|
|
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
model = model_definition_get(unit_definition->object.model.index);
|
|
root_node = TAG_BLOCK_GET_ELEMENT(&model->nodes, 0, struct model_node);
|
|
root_inverse_matrix = &root_node->runtime_default_inverse_matrix;
|
|
root_translation = root_node->default_translation;
|
|
|
|
if (parent_unit->unit.driver_object_index==unit_index &&
|
|
parent_unit->unit.animation.state!=_unit_state_opening &&
|
|
unit->object.parent_object_index!=NONE)
|
|
{
|
|
unit_animation_set_state(
|
|
unit->object.parent_object_index,
|
|
_unit_state_opening);
|
|
}
|
|
|
|
unit->unit.last_vehicle_index = parent_unit_index;
|
|
unit->unit.game_time_at_last_vehicle_exit = game_time_get();
|
|
if (unit->unit.driver_object_index==unit_index)
|
|
{
|
|
unit->unit.driver_object_index = NONE;
|
|
}
|
|
if (unit->unit.gunner_object_index==unit_index)
|
|
{
|
|
unit->unit.gunner_object_index = NONE;
|
|
}
|
|
|
|
object_detach(unit_index);
|
|
position.x = relative_position.i + unit->object.position.x;
|
|
position.y = relative_position.j + unit->object.position.y;
|
|
position.z = relative_position.k + unit->object.position.z -
|
|
root_translation.z;
|
|
object_set_position(unit_index, &position, NULL, NULL);
|
|
|
|
updated_unit_node_matrix = object_get_node_matrix(unit_index, 0);
|
|
matrix4x3_multiply(
|
|
updated_unit_node_matrix,
|
|
root_inverse_matrix,
|
|
&matrix);
|
|
unit->object.forward = matrix.forward;
|
|
unit->object.up = matrix.up;
|
|
object_set_visibility(unit_index, TRUE);
|
|
unit->unit.parent_seat_index = NONE;
|
|
unit->unit.animation.base_seat_index = _unit_base_seat_stand;
|
|
|
|
if (parent_unit->unit.driver_object_index==unit_index)
|
|
{
|
|
parent_unit->unit.driver_object_index = NONE;
|
|
}
|
|
if (parent_unit->unit.gunner_object_index==unit_index)
|
|
{
|
|
parent_unit->unit.gunner_object_index = NONE;
|
|
}
|
|
|
|
unit_update_driver_and_gunner(parent_unit_index);
|
|
unit_adjust_for_seat_change(unit_index);
|
|
|
|
{
|
|
struct unit_animation_update_data animation_update_data;
|
|
|
|
animation_update_data.state_desired = _unit_state_airborne;
|
|
animation_update_data.crouching = FALSE;
|
|
unit_update_animation(unit_index, &animation_update_data);
|
|
}
|
|
node_orientations = object_header_block_get(
|
|
unit_index,
|
|
&unit->object.original_node_orientations);
|
|
node_orientations->translation = root_translation;
|
|
|
|
if (unit->object.type==_object_type_biped)
|
|
{
|
|
biped_exit_seat_end(unit_index, parent_unit_index);
|
|
}
|
|
object_compute_node_matrices_recursive(unit_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_impulse(
|
|
long unit_index,
|
|
long impulse_index,
|
|
real_vector3d const *impulse,
|
|
real magnitude)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (unit_definition->object.physics.index!=NONE)
|
|
{
|
|
struct physics_definition *physics_definition = physics_definition_get(unit_definition->object.physics.index);
|
|
real scale = magnitude/physics_definition->mass;
|
|
|
|
unit->object.translational_velocity.i += scale*impulse->i;
|
|
unit->object.translational_velocity.j += scale*impulse->j;
|
|
unit->object.translational_velocity.k += scale*impulse->k;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_animation_start_action(
|
|
long unit_index,
|
|
short action)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (action==0)
|
|
{
|
|
unit->unit.animation.action = 0;
|
|
unit->unit.animation.action_animation.index = NONE;
|
|
}
|
|
else
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(unit_definition->object.animation_graph.index);
|
|
struct animation_graph_unit_seat *unit_seat = TAG_BLOCK_GET_ELEMENT(&animation_graph->unit_seats, unit->unit.animation.seat_index, struct animation_graph_unit_seat);
|
|
struct animation_graph_weapon_class *weapon_class = TAG_BLOCK_GET_ELEMENT(&unit_seat->weapon_classes, unit->unit.animation.weapon_index, struct animation_graph_weapon_class);
|
|
struct animation_graph_weapon_type *weapon_type = TAG_BLOCK_GET_ELEMENT(&weapon_class->weapon_types, unit->unit.animation.weapon_type_index, struct animation_graph_weapon_type);
|
|
long action_index = action;
|
|
long animation_graph_index;
|
|
short animation_index = NONE;
|
|
short weapon_class_animation_index = NONE;
|
|
short weapon_type_animation_index = NONE;
|
|
short interpolation_frame_count;
|
|
|
|
switch (action_index)
|
|
{
|
|
case 1:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_disarm;
|
|
break;
|
|
case 2:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_drop;
|
|
break;
|
|
case 3:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_ready;
|
|
break;
|
|
case 4:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_put_away;
|
|
break;
|
|
case 5:
|
|
weapon_type_animation_index = 0;
|
|
goto lookup_weapon_type_animation;
|
|
case 6:
|
|
weapon_type_animation_index = 1;
|
|
goto lookup_weapon_type_animation;
|
|
case 7:
|
|
weapon_type_animation_index = 8;
|
|
goto lookup_weapon_type_animation;
|
|
case 8:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_throw_grenade;
|
|
break;
|
|
case 9:
|
|
weapon_type_animation_index = 9;
|
|
lookup_weapon_type_animation:
|
|
if (weapon_type_animation_index<weapon_type->animations.count)
|
|
{
|
|
animation_index = animation_graph_animation_index_get(&weapon_type->animations)[weapon_type_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (weapon_class_animation_index!=NONE)
|
|
{
|
|
if (weapon_class_animation_index<weapon_class->animations.count)
|
|
{
|
|
animation_index = animation_graph_animation_index_get(&weapon_class->animations)[weapon_class_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
}
|
|
|
|
interpolation_frame_count = action_index==7 ? 0 : 6;
|
|
|
|
if (animation_index!=NONE)
|
|
{
|
|
if (interpolation_frame_count>0)
|
|
{
|
|
object_start_interpolation(unit_index, interpolation_frame_count);
|
|
}
|
|
|
|
animation_graph_index = unit_definition->object.animation_graph.index;
|
|
unit->unit.animation.action_animation.index = animation_choose_random_permutation_internal(TRUE, animation_graph_index, animation_index);
|
|
unit->unit.animation.action_animation.frame_index = 0;
|
|
unit->unit.animation.action = (char)action;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_animation_start_overlay_action(
|
|
long unit_index,
|
|
short overlay_action)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (overlay_action >= unit->unit.animation.overlay_action)
|
|
{
|
|
switch (unit->unit.animation.state)
|
|
{
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(unit_definition->object.animation_graph.index);
|
|
struct animation_graph_unit_seat *unit_seat = TAG_BLOCK_GET_ELEMENT(&animation_graph->unit_seats, unit->unit.animation.seat_index, struct animation_graph_unit_seat);
|
|
struct animation_graph_weapon_class *weapon_class = TAG_BLOCK_GET_ELEMENT(&unit_seat->weapon_classes, unit->unit.animation.weapon_index, struct animation_graph_weapon_class);
|
|
struct animation_graph_weapon_type *weapon_type = TAG_BLOCK_GET_ELEMENT(&weapon_class->weapon_types, unit->unit.animation.weapon_type_index, struct animation_graph_weapon_type);
|
|
short anim_slot = NONE;
|
|
short animation_index;
|
|
|
|
switch (overlay_action)
|
|
{
|
|
case 1: anim_slot = _unit_weapon_class_animation_diving_front; break;
|
|
case 2: anim_slot = _unit_weapon_class_animation_diving_back; break;
|
|
case 3: anim_slot = _unit_weapon_class_animation_diving_left; break;
|
|
case 4: anim_slot = _unit_weapon_class_animation_diving_right; break;
|
|
case 5: anim_slot = _unit_weapon_class_animation_turning_left; break;
|
|
case 6: anim_slot = _unit_weapon_class_animation_turning_right; break;
|
|
}
|
|
|
|
animation_index = (anim_slot>=0 && anim_slot<weapon_type->animations.count) ? animation_graph_animation_index_get(&weapon_type->animations)[anim_slot].animation_index : NONE;
|
|
|
|
if (animation_index != NONE)
|
|
{
|
|
long graph_index = unit_definition->object.animation_graph.index;
|
|
|
|
unit->unit.animation.overlay_action_animation.index = animation_choose_random_permutation_internal(TRUE, graph_index, animation_index);
|
|
unit->unit.animation.overlay_action_animation.frame_index = 0;
|
|
unit->unit.animation.overlay_action = (char)overlay_action;
|
|
}
|
|
else if (debug_unit_animations && unit->object.type==_object_type_biped && unit->unit.animation.aiming_screen_index==NONE)
|
|
{
|
|
console_warning("MISSING: %s '%s %s'",
|
|
tag_name_strip_path(unit_definition->object.animation_graph.name),
|
|
unit_seat->label,
|
|
weapon_class->label,
|
|
weapon_type->label,
|
|
animation_list_get_string(&weapon_type_animation_list, anim_slot));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_handle_weapon_state_change(
|
|
long unit_index,
|
|
short weapon_state)
|
|
{
|
|
short action = NONE;
|
|
short overlay_action = NONE;
|
|
|
|
switch (weapon_state)
|
|
{
|
|
case 5:
|
|
action = 5;
|
|
break;
|
|
case 6:
|
|
action = 6;
|
|
break;
|
|
case 3:
|
|
overlay_action = _unit_weapon_overlay_primary_chamber;
|
|
break;
|
|
case 4:
|
|
overlay_action = _unit_weapon_overlay_secondary_chamber;
|
|
break;
|
|
case 1:
|
|
overlay_action = _unit_weapon_overlay_primary_recoil;
|
|
break;
|
|
case 2:
|
|
overlay_action = _unit_weapon_overlay_secondary_recoil;
|
|
break;
|
|
case 7:
|
|
overlay_action = _unit_weapon_overlay_primary_charged;
|
|
break;
|
|
case 8:
|
|
overlay_action = _unit_weapon_overlay_secondary_charged;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (action != NONE)
|
|
{
|
|
unit_animation_start_action(unit_index, action);
|
|
}
|
|
else if (overlay_action != NONE)
|
|
{
|
|
unit_animation_start_overlay_action(unit_index, overlay_action);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_scripting_set_seat(
|
|
long unit_index,
|
|
char const *seat_label)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
unit->unit.magic_seat_index = seat_label_to_base_seat_index(seat_label);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_handle_deleted_object(
|
|
long unit_index,
|
|
long deleted_object_index)
|
|
{
|
|
struct unit_datum *unit;
|
|
short weapon_index;
|
|
long *weapon_object_index;
|
|
|
|
unit = unit_get(unit_index);
|
|
if (unit->unit.grenade_object_index==deleted_object_index)
|
|
{
|
|
unit->unit.grenade_object_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.driver_object_index==deleted_object_index)
|
|
{
|
|
unit->unit.driver_object_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.gunner_object_index==deleted_object_index)
|
|
{
|
|
unit->unit.gunner_object_index = NONE;
|
|
}
|
|
|
|
weapon_index = 0;
|
|
weapon_object_index = unit->unit.weapon_object_indices;
|
|
do
|
|
{
|
|
if (*weapon_object_index==deleted_object_index)
|
|
{
|
|
*weapon_object_index = NONE;
|
|
if (weapon_index==unit->unit.desired_weapon_index)
|
|
{
|
|
unit->unit.desired_weapon_index = NONE;
|
|
}
|
|
|
|
if (weapon_index==unit->unit.current_weapon_index)
|
|
{
|
|
unit->unit.current_weapon_index = NONE;
|
|
}
|
|
}
|
|
|
|
++weapon_index;
|
|
++weapon_object_index;
|
|
}
|
|
while (weapon_index<MAXIMUM_WEAPONS_PER_UNIT);
|
|
|
|
if (unit->unit.current_weapon_index==NONE)
|
|
{
|
|
unit->unit.desired_weapon_index = unit_weapon_next_index(unit_index, NONE, 0);
|
|
}
|
|
|
|
if (unit->unit.equipment_object_index==deleted_object_index)
|
|
{
|
|
unit->unit.equipment_object_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.delayed_damage_attacker_object_index==deleted_object_index)
|
|
{
|
|
unit->unit.delayed_damage_attacker_object_index = NONE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_get_looking_vector(
|
|
long unit_index,
|
|
real_vector3d *looking_vector)
|
|
{
|
|
*looking_vector = unit_get(unit_index)->unit.looking_vector;
|
|
|
|
return;
|
|
}
|
|
|
|
long unit_scripting_unit_driver(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
long driver_object_index = NONE;
|
|
|
|
if (unit)
|
|
{
|
|
driver_object_index = unit->unit.driver_object_index;
|
|
}
|
|
|
|
return driver_object_index;
|
|
}
|
|
|
|
long unit_scripting_unit_gunner(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
long gunner_object_index = NONE;
|
|
|
|
if (unit)
|
|
{
|
|
gunner_object_index = unit->unit.gunner_object_index;
|
|
}
|
|
|
|
return gunner_object_index;
|
|
}
|
|
|
|
void unit_shield_sapping_update(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
|
|
if (unit->unit.animation.state == _unit_state_shield_sapping)
|
|
{
|
|
struct animation_graph *animation_graph =
|
|
animation_graph_definition_get(
|
|
unit->object.animation.animation_graph_index);
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->object.animation.state.index,
|
|
struct animation);
|
|
|
|
/*
|
|
* Original bug: January compares the animation index to the loop
|
|
* frame. A behavior-corrected build would use state.frame_index.
|
|
*/
|
|
if (unit->object.animation.state.index >=
|
|
animation->private_loop_frame_index)
|
|
{
|
|
struct data_iterator iterator;
|
|
struct player_datum *player;
|
|
boolean shield_sapped = FALSE;
|
|
|
|
data_iterator_new(&iterator, player_data);
|
|
while ((player = data_iterator_next(&iterator)) != NULL)
|
|
{
|
|
if (player->unit_index != NONE)
|
|
{
|
|
struct unit_datum *player_unit =
|
|
unit_get(player->unit_index);
|
|
|
|
if (distance_squared3d(
|
|
&player_unit->object.bounding_sphere_center,
|
|
&unit->object.bounding_sphere_center) < 16.f)
|
|
{
|
|
struct damage_data damage_data;
|
|
|
|
damage_data_new(
|
|
&damage_data,
|
|
unit_definition->unit.melee_damage.index);
|
|
damage_data.owner_object_index = unit_index;
|
|
object_cause_damage(
|
|
&damage_data,
|
|
player->unit_index,
|
|
NONE,
|
|
NONE,
|
|
NONE,
|
|
NULL);
|
|
shield_sapped = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!shield_sapped)
|
|
{
|
|
unit->unit.shield_sap_timeout++;
|
|
}
|
|
else
|
|
{
|
|
unit->unit.shield_sap_timeout = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_detach_from_parent(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->object.parent_object_index != NONE)
|
|
{
|
|
real_point3d parent_origin;
|
|
real_point3d unit_origin;
|
|
real_vector3d velocity;
|
|
|
|
unit_get(unit->object.parent_object_index);
|
|
object_get_origin(
|
|
unit->object.parent_object_index,
|
|
&parent_origin);
|
|
object_get_origin(unit_index, &unit_origin);
|
|
vector_from_points3d(&parent_origin, &unit_origin, &velocity);
|
|
if (normalize3d(&velocity) == 0.f)
|
|
{
|
|
velocity = unit->object.forward;
|
|
}
|
|
scale_vector3d(&velocity, 0.020000001f, &velocity);
|
|
|
|
object_detach(unit_index);
|
|
unit_origin = unit->object.position;
|
|
scenario_ensure_point_within_world(&unit_origin);
|
|
object_set_position(unit_index, &unit_origin, NULL, NULL);
|
|
|
|
SET_FLAG(unit->object.flags, _object_at_rest_bit, FALSE);
|
|
SET_FLAG(unit->unit.flags, _unit_attached_melee_attack_bit, FALSE);
|
|
add_vectors3d(
|
|
&velocity,
|
|
&unit->object.translational_velocity,
|
|
&unit->object.translational_velocity);
|
|
object_set_visibility(unit_index, TRUE);
|
|
object_compute_node_matrices(unit_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
long units_debug_get_next_unit(
|
|
long current_object_index)
|
|
{
|
|
struct object_iterator iterator;
|
|
struct unit_datum *unit;
|
|
long result = NONE;
|
|
|
|
if (current_object_index != NONE)
|
|
{
|
|
object_iterator_new(&iterator, _object_mask_unit, 0);
|
|
|
|
while ((unit = (struct unit_datum *)object_iterator_next(&iterator)) != NULL)
|
|
{
|
|
if (iterator.index == current_object_index)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
while ((unit = (struct unit_datum *)object_iterator_next(&iterator)) != NULL)
|
|
{
|
|
if (unit->unit.actor_index == NONE &&
|
|
unit->unit.swarm_actor_index == NONE &&
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
result = iterator.index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result == NONE)
|
|
{
|
|
object_iterator_new(&iterator, _object_mask_unit, 0);
|
|
|
|
while ((unit = (struct unit_datum *)object_iterator_next(&iterator)) != NULL)
|
|
{
|
|
if (unit->unit.actor_index == NONE &&
|
|
unit->unit.swarm_actor_index == NONE &&
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
return iterator.index;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
long units_debug_get_closest_unit(
|
|
long reference_object_index)
|
|
{
|
|
struct object_iterator iterator;
|
|
struct object_datum *object;
|
|
long closest_index = NONE;
|
|
real closest_distance = REAL_MAX;
|
|
|
|
object_iterator_new(&iterator, _object_mask_biped, 0);
|
|
while ((object = (struct object_datum *)object_iterator_next(&iterator)) != NULL)
|
|
{
|
|
if (iterator.index != reference_object_index &&
|
|
!TEST_FLAG(object->object.damage_flags, _object_dead_bit))
|
|
{
|
|
real distance;
|
|
|
|
if (reference_object_index != NONE)
|
|
{
|
|
real_point3d reference_origin;
|
|
real_point3d object_origin;
|
|
|
|
object_get_origin(reference_object_index, &reference_origin);
|
|
object_get_origin(iterator.index, &object_origin);
|
|
distance = distance3d(&reference_origin, &object_origin);
|
|
}
|
|
else
|
|
{
|
|
distance = 0.f;
|
|
}
|
|
|
|
if (distance < closest_distance)
|
|
{
|
|
closest_index = iterator.index;
|
|
closest_distance = distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
return closest_index;
|
|
}
|
|
|
|
boolean unit_get_current_flashlight_state(
|
|
long unit_index)
|
|
{
|
|
boolean flashlight_on = FALSE;
|
|
|
|
if (unit_index!=NONE)
|
|
{
|
|
flashlight_on = TEST_FLAG(unit_get(unit_index)->unit.flags, _unit_integrated_light_on_bit);
|
|
}
|
|
|
|
return flashlight_on;
|
|
}
|
|
|
|
void unit_start_running_blindly(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (!TEST_FLAG(unit->unit.flags, _unit_running_blindly_bit))
|
|
{
|
|
real angle_range;
|
|
real_vector3d run_vector;
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_running_blindly_bit, TRUE);
|
|
if (unit->unit.actor_index != NONE)
|
|
{
|
|
if (actor_get_running_blind_vector(unit->unit.actor_index, &run_vector))
|
|
{
|
|
unit->unit.run_blindly_angle = 0.0f;
|
|
angle_range = DEGREES_TO_RADIANS(25.0f);
|
|
goto randomize_angle;
|
|
}
|
|
}
|
|
|
|
{
|
|
real_euler_angles2d facing_angles;
|
|
|
|
euler_angles2d_from_vector3d(&facing_angles, &unit->object.forward);
|
|
if (facing_angles.yaw > _pi)
|
|
facing_angles.yaw -= 2.0f * _pi;
|
|
unit->unit.run_blindly_angle = facing_angles.yaw;
|
|
angle_range = DEGREES_TO_RADIANS(100.0f);
|
|
}
|
|
|
|
randomize_angle:
|
|
unit->unit.run_blindly_angle += real_seed_random_range(
|
|
get_global_random_seed_address(),
|
|
-angle_range,
|
|
angle_range);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_stop_running_blindly(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_running_blindly_bit, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
long unit_get_aiming_unit_index(
|
|
long unit_index)
|
|
{
|
|
long aiming_unit_index = unit_index;
|
|
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->object.parent_object_index!=NONE && unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
struct unit_datum *parent_unit = unit_get(unit->object.parent_object_index);
|
|
struct unit_definition *parent_unit_definition = unit_definition_get(parent_unit->definition_index);
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&parent_unit_definition->unit.seats, unit->unit.parent_seat_index, struct unit_seat);
|
|
|
|
if (TEST_FLAG(seat->flags, _unit_seat_invisible_bit) || TEST_FLAG(seat->flags, _unit_seat_gunner_bit))
|
|
{
|
|
aiming_unit_index = unit->object.parent_object_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
return aiming_unit_index;
|
|
}
|
|
|
|
void unit_set_emotion(
|
|
long unit_index,
|
|
word emotion_index)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
unit_get(unit_index)->unit.animation.emotion_index = emotion_index;
|
|
object_start_interpolation(unit_index, 6);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_is_playing_custom_animation(
|
|
long unit_index)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
return unit_get(unit_index)->unit.animation.state==_unit_state_user_animation;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
short unit_get_custom_animation_time(
|
|
long unit_index)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->unit.animation.state==_unit_state_user_animation)
|
|
{
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(unit->object.animation.animation_graph_index);
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(&animation_graph->animations, unit->object.animation.state.index, struct animation);
|
|
return MAX(animation->frame_count - unit->object.animation.state.frame_index - 2, 0);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
boolean unit_start_user_animation(
|
|
long unit_index,
|
|
long animation_graph_index,
|
|
char const *animation_name,
|
|
boolean interpolate)
|
|
{
|
|
boolean animation_started = FALSE;
|
|
|
|
if (unit_index != NONE)
|
|
{
|
|
if (animation_graph_index != NONE)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct animation_graph *animation_graph;
|
|
short base_animation_index;
|
|
|
|
unit = unit_get(unit_index);
|
|
(void)unit_definition_get(unit->definition_index);
|
|
animation_graph =
|
|
animation_graph_definition_get(animation_graph_index);
|
|
base_animation_index = animation_graph_get_animation_by_name(
|
|
animation_graph_index,
|
|
animation_name);
|
|
|
|
if (base_animation_index != NONE)
|
|
{
|
|
struct animation *animation;
|
|
short animation_index =
|
|
animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
base_animation_index);
|
|
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
|
|
if (animation->type != _animation_overlay &&
|
|
animation->type == _animation_base)
|
|
{
|
|
boolean animation_allowed = TRUE;
|
|
|
|
if (unit->unit.animation.state ==
|
|
_unit_state_user_animation &&
|
|
unit->object.animation.state.index != NONE)
|
|
{
|
|
struct animation *current_animation =
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->object.animation.state.index,
|
|
struct animation);
|
|
|
|
if (current_animation->runtime_parent_animation_index ==
|
|
animation->runtime_parent_animation_index)
|
|
{
|
|
if (unit->object.animation.state.frame_index + 2 ==
|
|
current_animation->private_key_frame_index)
|
|
{
|
|
--unit->object.animation.state.frame_index;
|
|
animation_allowed = FALSE;
|
|
}
|
|
|
|
else if (unit->object.animation.state.frame_index <
|
|
current_animation->private_key_frame_index)
|
|
{
|
|
animation_allowed = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (animation_allowed)
|
|
{
|
|
if (interpolate)
|
|
{
|
|
object_start_interpolation(unit_index, 6);
|
|
}
|
|
|
|
unit->unit.animation.state =
|
|
_unit_state_user_animation;
|
|
unit_set_animation(
|
|
unit_index,
|
|
animation_graph_index,
|
|
animation_index);
|
|
SET_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_postpone_weapon_ik_until_interpolation_ends_bit,
|
|
TRUE);
|
|
object_compute_node_matrices_recursive(unit_index);
|
|
animation_started = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
console_warning(
|
|
"the animation '%s' doesn't exist in the graph '%s'",
|
|
animation_name,
|
|
tag_get_name(animation_graph_index));
|
|
}
|
|
}
|
|
}
|
|
|
|
return animation_started;
|
|
}
|
|
|
|
boolean unit_set_seat(
|
|
long unit_index,
|
|
char const *seat_label)
|
|
{
|
|
boolean seat_set = FALSE;
|
|
|
|
if (unit_set_or_test_seat_and_weapon_label(unit_index, seat_label, NULL, TRUE))
|
|
{
|
|
seat_set = TRUE;
|
|
}
|
|
|
|
return seat_set;
|
|
}
|
|
|
|
boolean unit_flying_through_air(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
boolean flying_through_air = FALSE;
|
|
|
|
if (unit->object.type==_object_type_biped)
|
|
{
|
|
flying_through_air = biped_flying_through_air(unit_index);
|
|
}
|
|
|
|
return flying_through_air;
|
|
}
|
|
|
|
void unit_stop_custom_animation(
|
|
long unit_index)
|
|
{
|
|
if (unit_index!=NONE && unit_get(unit_index)->unit.animation.state==_unit_state_user_animation)
|
|
{
|
|
unit_animation_set_state(unit_index, _unit_state_idle);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_melee_attack_begin(
|
|
long unit_index,
|
|
boolean continuous,
|
|
real_vector2d const *alignment_vector)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
char previous_animation_state = unit->unit.animation.state;
|
|
boolean result = FALSE;
|
|
|
|
switch (previous_animation_state)
|
|
{
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
boolean limping = FALSE;
|
|
short new_animation_state;
|
|
|
|
if (unit->object.type==_object_type_biped)
|
|
{
|
|
struct biped_datum *biped = (struct biped_datum *)unit;
|
|
limping = TEST_FLAG(
|
|
biped->biped.flags,
|
|
_biped_airborne_bit);
|
|
}
|
|
|
|
if (continuous)
|
|
{
|
|
new_animation_state = _unit_state_melee_continuous;
|
|
}
|
|
else if (previous_animation_state==_unit_state_leap_airborne)
|
|
{
|
|
new_animation_state = _unit_state_leap_melee;
|
|
}
|
|
else
|
|
{
|
|
new_animation_state = limping ?
|
|
_unit_state_melee_airborne :
|
|
_unit_state_melee_attack;
|
|
}
|
|
|
|
if (unit_animation_set_state(unit_index, new_animation_state) || continuous)
|
|
{
|
|
if (TEST_FLAG(
|
|
unit_definition->unit.flags,
|
|
_unit_melee_attack_is_fatal_bit))
|
|
{
|
|
unit->unit.animation.state = _unit_state_dying;
|
|
}
|
|
|
|
if (alignment_vector)
|
|
{
|
|
unit_align_facing(unit_index, alignment_vector);
|
|
}
|
|
|
|
if (continuous)
|
|
{
|
|
unit->unit.melee_attack_state = 4;
|
|
unit->unit.melee_continuous_damage_effect_timer = 0;
|
|
}
|
|
else
|
|
{
|
|
unit->unit.melee_attack_state = 1;
|
|
}
|
|
result = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_leap_begin(
|
|
long unit_index,
|
|
real_vector2d const *alignment_vector)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
boolean result = FALSE;
|
|
|
|
switch (unit->unit.animation.state)
|
|
{
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
boolean biped_limping = FALSE;
|
|
|
|
if (unit->object.type==_object_type_biped)
|
|
{
|
|
biped_limping = TEST_FLAG(
|
|
((struct biped_datum *)unit)->biped.flags,
|
|
_biped_airborne_bit);
|
|
}
|
|
|
|
if (!biped_limping &&
|
|
unit_animation_set_state(unit_index, _unit_state_leap_start))
|
|
{
|
|
if (alignment_vector)
|
|
{
|
|
unit_align_facing(unit_index, alignment_vector);
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_throw_grenade_begin(
|
|
long unit_index,
|
|
real_vector2d const *alignment_vector)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
long weapon_index = unit_get_current_weapon_index(unit_index);
|
|
boolean result = FALSE;
|
|
|
|
if (unit_get_grenade_count(
|
|
unit_index,
|
|
unit_get_current_grenade_type(unit_index)) > 0)
|
|
{
|
|
switch (unit->unit.animation.state)
|
|
{
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
break;
|
|
|
|
default:
|
|
if (!weapon_prevents_grenade_throwing(weapon_index))
|
|
{
|
|
struct animation_graph *animation_graph;
|
|
struct animation *animation;
|
|
struct game_globals_grenade *grenade;
|
|
|
|
if (weapon_index != NONE)
|
|
{
|
|
weapon_stop_reload(weapon_index);
|
|
}
|
|
|
|
biped_stop_melee_attack(unit_index);
|
|
unit->unit.animation.action = 0;
|
|
unit->unit.animation.action_animation.index = NONE;
|
|
|
|
if (unit_animation_set_state(
|
|
unit_index,
|
|
_unit_state_throw_grenade))
|
|
{
|
|
unit->unit.grenade_throw_state =
|
|
_unit_grenade_throw_wind_up;
|
|
unit->unit.grenade_throw_ticks = 0;
|
|
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->object.animation.state.index,
|
|
struct animation);
|
|
unit->unit.grenade_throw_full_power_ticks =
|
|
animation->private_key_frame_index -
|
|
unit->object.animation.state.frame_index + 1;
|
|
|
|
if (alignment_vector)
|
|
{
|
|
unit_align_facing(unit_index, alignment_vector);
|
|
}
|
|
else
|
|
{
|
|
real_vector2d forward;
|
|
|
|
forward.i = unit->unit.aiming_vector.i;
|
|
forward.j = unit->unit.aiming_vector.j;
|
|
if (normalize2d(&forward) > 0.f)
|
|
{
|
|
unit_align_facing(unit_index, &forward);
|
|
}
|
|
}
|
|
|
|
first_person_weapon_message_from_unit(unit_index, 0x11);
|
|
player_control_unzoom(unit_index);
|
|
|
|
grenade = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->grenades,
|
|
unit->unit.current_grenade_index,
|
|
struct game_globals_grenade);
|
|
if (grenade->throwing_effect.index != NONE)
|
|
{
|
|
effect_new_from_object(
|
|
grenade->throwing_effect.index,
|
|
unit_index,
|
|
unit_index,
|
|
NONE,
|
|
0.f,
|
|
0.f,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void scripting_magic_melee_attack(
|
|
void)
|
|
{
|
|
unit_melee_attack_begin(player_get(0)->unit_index, FALSE, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_impact_melee_damage(
|
|
long unit_index,
|
|
long target_object_index,
|
|
short node_index,
|
|
short region_index,
|
|
short material_index,
|
|
real_point3d const *position,
|
|
real_vector3d const *object_normal,
|
|
struct location const *location)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
struct object_datum *target_object = object_get(target_object_index);
|
|
|
|
if (TEST_FLAG(
|
|
unit_definition->unit.flags,
|
|
_unit_impact_melee_die_on_shield_bit) &&
|
|
target_object->object.type==_object_type_biped &&
|
|
target_object->object.shield_vitality>0.f)
|
|
{
|
|
struct unit_definition *target_unit_definition =
|
|
unit_definition_get(target_object->definition_index);
|
|
|
|
if (TEST_FLAG(
|
|
target_unit_definition->unit.flags,
|
|
_unit_shields_fry_infection_forms_bit))
|
|
{
|
|
unit_cause_melee_damage(
|
|
unit_index,
|
|
TRUE,
|
|
target_object_index,
|
|
node_index,
|
|
region_index,
|
|
material_index,
|
|
object_normal);
|
|
object_deplete_body(unit_index);
|
|
object_delete(unit_index);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(
|
|
unit_definition->unit.flags,
|
|
_unit_impact_melee_attaches_bit) &&
|
|
TEST_FLAG(
|
|
_object_mask_biped | _object_mask_vehicle,
|
|
target_object->object.type) &&
|
|
!TEST_FLAG(target_object->object.damage_flags, _object_dead_bit))
|
|
{
|
|
long parent_object_index = target_object->object.parent_object_index;
|
|
|
|
while (parent_object_index!=NONE)
|
|
{
|
|
struct object_datum *parent_object = object_get(parent_object_index);
|
|
|
|
if (parent_object_index==unit_index ||
|
|
parent_object->object.type!=_object_type_vehicle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
parent_object_index = parent_object->object.parent_object_index;
|
|
}
|
|
|
|
{
|
|
real_vector3d left;
|
|
real_vector3d *forward = &unit->object.forward;
|
|
|
|
unit->object.translational_velocity = *global_zero_vector3d;
|
|
unit->object.angular_velocity = *global_zero_vector3d;
|
|
*forward = *object_normal;
|
|
forward->i = -forward->i;
|
|
forward->j = -forward->j;
|
|
forward->k = -forward->k;
|
|
|
|
cross_product3d(&unit->object.up, forward, &left);
|
|
if (normalize3d(&left)==0.f)
|
|
{
|
|
cross_product3d(global_up3d, forward, &left);
|
|
if (normalize3d(&left)==0.f)
|
|
{
|
|
left = *global_forward3d;
|
|
}
|
|
}
|
|
|
|
cross_product3d(forward, &left, &unit->object.up);
|
|
object_translate(unit_index, position, location);
|
|
object_attach_to_node(target_object_index, unit_index, node_index);
|
|
SET_FLAG(unit->object.flags, _object_at_rest_bit, TRUE);
|
|
SET_FLAG(unit->unit.flags, _unit_attached_melee_attack_bit, TRUE);
|
|
unit_melee_attack_begin(unit_index, TRUE, FALSE);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_ping_animation(
|
|
long unit_index,
|
|
boolean killed,
|
|
boolean feign_death,
|
|
boolean suppress_random_death_frame,
|
|
boolean suppress_hard_ping,
|
|
boolean force_hard_ping,
|
|
real damage_direction_angle,
|
|
short damage_part,
|
|
real_vector2d const *alignment_vector);
|
|
|
|
enum
|
|
{
|
|
_scenario_unit_dead_bit = 0,
|
|
};
|
|
|
|
struct scenario_unit_datum
|
|
{
|
|
real body_vitality;
|
|
unsigned long flags;
|
|
};
|
|
typedef char scenario_unit_datum_size_check[
|
|
sizeof(struct scenario_unit_datum) == 0x08 ? 1 : -1];
|
|
|
|
void unit_place(
|
|
long unit_index,
|
|
struct scenario_unit_datum const *scenario_unit)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
|
|
if (scenario_unit->body_vitality>0.f)
|
|
{
|
|
unit->object.body_vitality = scenario_unit->body_vitality;
|
|
}
|
|
|
|
if (TEST_FLAG(scenario_unit->flags, _scenario_unit_dead_bit))
|
|
{
|
|
unit_ping_animation(
|
|
unit_index,
|
|
TRUE,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE,
|
|
NONE,
|
|
FALSE);
|
|
|
|
if (unit->unit.animation.state==_unit_state_dying)
|
|
{
|
|
unsigned long object_flags;
|
|
unsigned long unit_flags;
|
|
long death_frame_index;
|
|
struct animation_graph *animation_graph;
|
|
struct animation *animation;
|
|
|
|
unit_delete_all_weapons(unit_index);
|
|
csmemset(
|
|
unit->unit.grenade_counts,
|
|
0,
|
|
sizeof(unit->unit.grenade_counts));
|
|
|
|
if (unit->unit.equipment_object_index!=NONE)
|
|
{
|
|
object_delete(unit->unit.equipment_object_index);
|
|
unit->unit.equipment_object_index = NONE;
|
|
}
|
|
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->object.animation.state.index,
|
|
struct animation);
|
|
object_flags = unit->object.flags;
|
|
death_frame_index = MAX(0, animation->frame_count - 4);
|
|
SET_FLAG(unit->object.damage_flags, _object_dead_bit, TRUE);
|
|
unit_flags = unit->unit.flags | FLAG(_unit_placed_here_dead_bit);
|
|
unit->object.animation.state.frame_index = (short)death_frame_index;
|
|
unit->unit.flags = unit_flags;
|
|
unit->object.flags = object_flags | FLAG(_object_cannot_be_garbage_bit);
|
|
unit->unit.time_of_death = game_time_get();
|
|
unit->object.body_vitality = 0.f;
|
|
unit->object.shield_vitality = 0.f;
|
|
object_deplete_shield(unit_index);
|
|
object_compute_node_matrices_recursive(unit_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_scripting_enter_vehicle(
|
|
long unit_index,
|
|
long vehicle_index,
|
|
char const *seat_name)
|
|
{
|
|
if (unit_index!=NONE && vehicle_index!=NONE && csstrlen(seat_name)>0)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
if (unit->object.parent_object_index!=NONE)
|
|
{
|
|
if (unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
unit_exit_seat_end(unit_index);
|
|
}
|
|
|
|
if (unit->object.parent_object_index!=NONE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
{
|
|
struct unit_datum *vehicle = unit_get(vehicle_index);
|
|
struct unit_definition *vehicle_definition = unit_definition_get(vehicle->definition_index);
|
|
short seat_index;
|
|
|
|
for (seat_index = 0; seat_index<vehicle_definition->unit.seats.count; ++seat_index)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&vehicle_definition->unit.seats,
|
|
seat_index,
|
|
struct unit_seat);
|
|
|
|
if (!_stricmp(seat_name, seat->label) &&
|
|
!unit_seat_filled(vehicle_index, seat_index) &&
|
|
(unit->object.type==_object_type_vehicle ||
|
|
unit_set_or_test_seat_and_weapon_label(unit_index, seat->label, NULL, FALSE)))
|
|
{
|
|
unit_enter_seat(unit_index, vehicle_index, seat_index);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_try_and_exit_seat(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
boolean result = FALSE;
|
|
|
|
if (unit->object.parent_object_index!=NONE &&
|
|
unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
if (unit->object.type==_object_type_vehicle)
|
|
{
|
|
unit_exit_seat_end(unit_index);
|
|
}
|
|
else if (!unit_animation_busy(&unit->unit.animation))
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(unit_definition->object.animation_graph.index);
|
|
struct animation_graph_unit_seat *unit_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
|
|
if (unit_seat->animations.count>8)
|
|
{
|
|
short animation_index = animation_graph_animation_index_get(&unit_seat->animations)[8].animation_index;
|
|
|
|
if (animation_index!=NONE)
|
|
{
|
|
struct unit_datum *parent_unit = unit_get(unit->object.parent_object_index);
|
|
|
|
if (parent_unit->unit.driver_object_index==unit_index)
|
|
{
|
|
unit_open(unit->object.parent_object_index);
|
|
}
|
|
|
|
animation_index = animation_choose_random_permutation(
|
|
unit_definition->object.animation_graph.index,
|
|
animation_index);
|
|
|
|
unit_set_animation(
|
|
unit_index,
|
|
unit_definition->object.animation_graph.index,
|
|
animation_index);
|
|
object_set_visibility(unit_index, TRUE);
|
|
unit->unit.animation.state = _unit_state_exiting_seat;
|
|
ai_handle_exit_vehicle(
|
|
unit_index,
|
|
unit->object.parent_object_index);
|
|
result = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
short vehicle_scripting_unload(
|
|
long vehicle_index,
|
|
char const *seat_name)
|
|
{
|
|
long unloaded_count = 0;
|
|
|
|
if (vehicle_index!=NONE)
|
|
{
|
|
struct unit_datum *vehicle = unit_get(vehicle_index);
|
|
struct unit_definition *vehicle_definition = unit_definition_get(vehicle->definition_index);
|
|
boolean unload_all = !seat_name || csstrlen(seat_name)==0;
|
|
struct object_iterator iterator;
|
|
struct unit_datum *unit;
|
|
|
|
object_iterator_new(&iterator, _object_mask_unit, 0);
|
|
|
|
while ((unit = object_iterator_next(&iterator))!=NULL)
|
|
{
|
|
if (unit->object.parent_object_index==vehicle_index)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&vehicle_definition->unit.seats,
|
|
unit->unit.parent_seat_index,
|
|
struct unit_seat);
|
|
char lower_seat_name[256];
|
|
|
|
csstrcpy(lower_seat_name, seat->label);
|
|
strlwr(lower_seat_name);
|
|
|
|
if (unload_all || strstr(lower_seat_name, seat_name))
|
|
{
|
|
if (unit_try_and_exit_seat(iterator.index))
|
|
{
|
|
++unloaded_count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (short)unloaded_count;
|
|
}
|
|
|
|
void unit_scripting_exit_vehicle(
|
|
long unit_index)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->object.parent_object_index!=NONE && unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
unit_try_and_exit_seat(unit_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_adjust_projectile_ray(
|
|
long unit_index,
|
|
real_point3d *origin,
|
|
real_vector3d *direction,
|
|
real *velocity,
|
|
boolean adjust_origin,
|
|
boolean use_aiming_vector)
|
|
{
|
|
struct unit_datum *unit;
|
|
real projection;
|
|
real_vector3d relative;
|
|
|
|
unit = unit_get(unit_index);
|
|
if (use_aiming_vector)
|
|
{
|
|
*direction = unit->unit.aiming_vector;
|
|
}
|
|
|
|
if (adjust_origin)
|
|
{
|
|
real_point3d camera_position;
|
|
|
|
unit_get_camera_position(unit_index, &camera_position);
|
|
#ifdef HALO_VR
|
|
/* shots from the hand that aims (port/linux/game/vr_render.c) */
|
|
vr_render_hand_origin(unit_index, &camera_position);
|
|
#endif
|
|
vector_from_points3d(&camera_position, origin, &relative);
|
|
projection =
|
|
((relative.i*direction->i + relative.k*direction->k) +
|
|
relative.j*direction->j);
|
|
point_from_line3d(&camera_position, direction, projection, origin);
|
|
}
|
|
|
|
{
|
|
real_vector3d object_velocity;
|
|
|
|
object_get_velocities(unit_index, &object_velocity, NULL);
|
|
*velocity = dot_product3d(direction, &object_velocity);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_render_debug(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct unit_definition *unit_definition;
|
|
real_point3d camera_position;
|
|
real_point3d origin;
|
|
|
|
unit = unit_get(unit_index);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (debug_objects_unit_vectors)
|
|
{
|
|
unit_get_camera_position(unit_index, &camera_position);
|
|
object_get_origin(unit_index, &origin);
|
|
origin.z += 0.1f;
|
|
|
|
render_debug_vector(
|
|
TRUE,
|
|
&camera_position,
|
|
&unit->unit.aiming_vector,
|
|
1.f,
|
|
global_real_argb_white);
|
|
render_debug_vector(
|
|
TRUE,
|
|
&camera_position,
|
|
&unit->unit.desired_aiming_vector,
|
|
0.5f,
|
|
global_real_argb_red);
|
|
render_debug_vector(
|
|
TRUE,
|
|
&origin,
|
|
&unit->object.forward,
|
|
1.f,
|
|
global_real_argb_white);
|
|
render_debug_vector(
|
|
TRUE,
|
|
&origin,
|
|
&unit->unit.desired_facing_vector,
|
|
0.5f,
|
|
global_real_argb_red);
|
|
}
|
|
|
|
if (debug_objects_unit_seats)
|
|
{
|
|
long player_unit_index =
|
|
player_get(local_player_get_player_index(render.local_player_index))->unit_index;
|
|
|
|
if (player_unit_index!=NONE)
|
|
{
|
|
short seat_index;
|
|
|
|
for (seat_index = 0;
|
|
seat_index<unit_definition->unit.seats.count;
|
|
seat_index++)
|
|
{
|
|
real_point3d seat_position;
|
|
|
|
if (unit_get_seat_entrance_point(
|
|
player_unit_index,
|
|
unit_index,
|
|
seat_index,
|
|
&camera_position,
|
|
&origin,
|
|
&seat_position))
|
|
{
|
|
render_debug_point(
|
|
TRUE,
|
|
&camera_position,
|
|
0.25f,
|
|
global_real_argb_red);
|
|
render_debug_point(
|
|
TRUE,
|
|
&origin,
|
|
0.25f,
|
|
global_real_argb_blue);
|
|
render_debug_point(
|
|
TRUE,
|
|
&seat_position,
|
|
0.25f,
|
|
global_real_argb_yellow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (debug_objects_unit_mouth_apeture)
|
|
{
|
|
struct object_marker marker;
|
|
real mouth_aperture;
|
|
|
|
/*
|
|
* BUG (original): January ignores the marker count and consumes the
|
|
* output even when the requested head marker is absent. A safe,
|
|
* intentionally nonmatching build should render only when this call
|
|
* returns a value greater than zero.
|
|
*/
|
|
object_get_marker_by_name(unit_index, "head", &marker, 1);
|
|
mouth_aperture = unit->unit.mouth_aperture;
|
|
origin = marker.matrix.position;
|
|
render_debug_string_at_point(
|
|
FALSE,
|
|
&origin,
|
|
csprintf(temporary, "%.2f", mouth_aperture),
|
|
global_real_argb_orange);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_abort_animation(
|
|
long unit_index)
|
|
{
|
|
unit_animation_set_state(unit_index, _unit_state_idle);
|
|
|
|
return;
|
|
}
|
|
|
|
short unit_find_nearby_seat(
|
|
long unit_index,
|
|
long target_unit_index,
|
|
short *parent_seat_index)
|
|
{
|
|
enum
|
|
{
|
|
_unit_seat_requires_driver_bit = 9,
|
|
};
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_datum *target_unit = unit_get(target_unit_index);
|
|
struct unit_definition *target_unit_definition =
|
|
unit_definition_get(target_unit->definition_index);
|
|
short best_state = _unit_nearby_seat_none;
|
|
short best_seat_index = NONE;
|
|
boolean best_seat_is_driver;
|
|
|
|
if (!TEST_FLAG(target_unit->object.damage_flags, _object_dead_bit) &&
|
|
!TEST_FLAG(target_unit->unit.flags, _unit_not_enterable_by_player_bit))
|
|
{
|
|
short seat_index;
|
|
real best_distance = REAL_MAX;
|
|
best_seat_is_driver = FALSE;
|
|
|
|
for (seat_index = 0;
|
|
seat_index < target_unit_definition->unit.seats.count;
|
|
seat_index++)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&target_unit_definition->unit.seats,
|
|
seat_index,
|
|
struct unit_seat);
|
|
real_point3d unit_entrance_position;
|
|
real_point3d seat_position;
|
|
|
|
if (unit_get_seat_entrance_point(
|
|
unit_index,
|
|
target_unit_index,
|
|
seat_index,
|
|
&unit_entrance_position,
|
|
&seat_position,
|
|
NULL))
|
|
{
|
|
real entrance_distance = distance3d(
|
|
&unit_entrance_position,
|
|
&unit->object.bounding_sphere_center);
|
|
real seat_distance = distance3d(
|
|
&seat_position,
|
|
&unit->object.bounding_sphere_center);
|
|
real distance = MIN(entrance_distance, seat_distance);
|
|
|
|
if (distance < 1.f &&
|
|
(!TEST_FLAG(seat->flags, _unit_seat_requires_driver_bit) ||
|
|
target_unit->unit.driver_object_index != NONE) &&
|
|
seat->label[0] != 0 &&
|
|
unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
seat->label,
|
|
NULL,
|
|
FALSE))
|
|
{
|
|
long occupant_unit_index = NONE;
|
|
short seat_state = _unit_nearby_seat_none;
|
|
|
|
if (unit_can_enter_seat(
|
|
unit_index,
|
|
target_unit_index,
|
|
seat_index,
|
|
&occupant_unit_index))
|
|
{
|
|
seat_state = _unit_nearby_seat_available;
|
|
}
|
|
else if (occupant_unit_index != NONE)
|
|
{
|
|
struct unit_datum *occupant = unit_get(occupant_unit_index);
|
|
|
|
if (occupant->unit.actor_index != NONE &&
|
|
ai_try_vehicle_eviction(
|
|
occupant->unit.actor_index,
|
|
unit_index,
|
|
FALSE))
|
|
{
|
|
seat_state = _unit_nearby_seat_occupied;
|
|
}
|
|
}
|
|
|
|
if (seat_state != _unit_nearby_seat_none)
|
|
{
|
|
boolean seat_is_driver = TEST_FLAG(
|
|
seat->flags,
|
|
_unit_seat_driver_bit);
|
|
real distance_scale = 1.f;
|
|
|
|
if (best_seat_is_driver && !seat_is_driver)
|
|
distance_scale = 1.5f;
|
|
|
|
if (best_seat_index == NONE ||
|
|
seat_state > best_state ||
|
|
distance * distance_scale < best_distance)
|
|
{
|
|
best_state = seat_state;
|
|
best_seat_index = seat_index;
|
|
best_seat_is_driver = seat_is_driver;
|
|
best_distance = distance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
4088,
|
|
parent_seat_index != NULL);
|
|
*parent_seat_index = best_seat_index;
|
|
|
|
return best_state;
|
|
}
|
|
|
|
short vehicle_scripting_find_available_seats(
|
|
long vehicle_index,
|
|
char const *seat_substring_name,
|
|
short seat_desire_type,
|
|
short *seat_indices,
|
|
short maximum_seat_count)
|
|
{
|
|
struct unit_datum *vehicle = unit_get(vehicle_index);
|
|
struct unit_definition *vehicle_definition = unit_definition_get(vehicle->definition_index);
|
|
boolean match_all_seats;
|
|
short available_seat_count;
|
|
short seat_index;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 6031, seat_substring_name);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
6032,
|
|
(seat_desire_type == NONE) || ((seat_desire_type >= 0) && (seat_desire_type < NUMBER_OF_VEHICLE_SEAT_DESIRE_TYPES)));
|
|
|
|
match_all_seats = !seat_substring_name || csstrlen(seat_substring_name)==0;
|
|
available_seat_count = 0;
|
|
|
|
for (seat_index = 0; seat_index<vehicle_definition->unit.seats.count; ++seat_index)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&vehicle_definition->unit.seats,
|
|
seat_index,
|
|
struct unit_seat);
|
|
boolean seat_matches_desire = TRUE;
|
|
char lower_seat_name[256];
|
|
|
|
if (available_seat_count>=maximum_seat_count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
csstrcpy(lower_seat_name, seat->label);
|
|
strlwr(lower_seat_name);
|
|
|
|
if (!match_all_seats && !strstr(lower_seat_name, seat_substring_name))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (seat_desire_type)
|
|
{
|
|
case _vehicle_seat_desire_not_driver:
|
|
seat_matches_desire = !TEST_FLAG(seat->flags, _unit_seat_driver_bit);
|
|
break;
|
|
|
|
case _vehicle_seat_desire_gunner:
|
|
seat_matches_desire = TEST_FLAG(seat->flags, _unit_seat_gunner_bit);
|
|
break;
|
|
|
|
case _vehicle_seat_desire_passenger:
|
|
seat_matches_desire =
|
|
!TEST_FLAG(seat->flags, _unit_seat_driver_bit) &&
|
|
!TEST_FLAG(seat->flags, _unit_seat_gunner_bit);
|
|
break;
|
|
|
|
case _vehicle_seat_desire_driver:
|
|
seat_matches_desire = TEST_FLAG(seat->flags, _unit_seat_driver_bit);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (seat_matches_desire && !unit_seat_filled(vehicle_index, seat_index))
|
|
{
|
|
seat_indices[available_seat_count++] = seat_index;
|
|
}
|
|
}
|
|
|
|
return available_seat_count;
|
|
}
|
|
|
|
short vehicle_scripting_load_magic(
|
|
long vehicle_index,
|
|
char const *seat_name,
|
|
long object_list_index)
|
|
{
|
|
long loaded_count = 0;
|
|
|
|
if (vehicle_index!=NONE)
|
|
{
|
|
struct unit_datum *vehicle = unit_get(vehicle_index);
|
|
struct unit_definition *vehicle_definition = unit_definition_get(vehicle->definition_index);
|
|
short available_seat_indices[16];
|
|
short available_seat_count;
|
|
long reference_index;
|
|
long unit_index;
|
|
|
|
available_seat_count = vehicle_scripting_find_available_seats(
|
|
vehicle_index,
|
|
seat_name,
|
|
NONE,
|
|
available_seat_indices,
|
|
NUMBEROF(available_seat_indices));
|
|
unit_index = object_list_get_first(object_list_index, &reference_index);
|
|
|
|
while (unit_index!=NONE)
|
|
{
|
|
struct object_datum *object = object_get(unit_index);
|
|
|
|
if (TEST_FLAG(_object_mask_unit, object->object.type) &&
|
|
!TEST_FLAG(vehicle->object.damage_flags, _object_dead_bit))
|
|
{
|
|
struct unit_datum *unit = (struct unit_datum *)object;
|
|
short available_seat_index;
|
|
|
|
for (available_seat_index = 0;
|
|
available_seat_index<available_seat_count;
|
|
++available_seat_index)
|
|
{
|
|
short seat_index = available_seat_indices[available_seat_index];
|
|
|
|
if (seat_index!=NONE)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&vehicle_definition->unit.seats,
|
|
seat_index,
|
|
struct unit_seat);
|
|
|
|
if ((unit->object.type==_object_type_vehicle ||
|
|
unit_set_or_test_seat_and_weapon_label(unit_index, seat->label, NULL, FALSE)))
|
|
{
|
|
if (unit->object.parent_object_index!=NONE)
|
|
{
|
|
if (unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
unit_exit_seat_end(unit_index);
|
|
}
|
|
}
|
|
|
|
if (unit->object.parent_object_index==NONE &&
|
|
unit_enter_seat(unit_index, vehicle_index, seat_index))
|
|
{
|
|
available_seat_indices[available_seat_index] = NONE;
|
|
++loaded_count;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unit_index = object_list_get_next(object_list_index, &reference_index);
|
|
}
|
|
}
|
|
|
|
return (short)loaded_count;
|
|
}
|
|
|
|
void unit_open(
|
|
long unit_index)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
unit_animation_set_state(unit_index, _unit_state_opening);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_close(
|
|
long unit_index)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
unit_animation_set_state(unit_index, _unit_state_closing);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void scripting_set_magic_base_seat(
|
|
char const *seat_name)
|
|
{
|
|
magic_base_animation_seat_index = seat_label_to_base_seat_index(seat_name);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_update(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
boolean used_time = FALSE;
|
|
boolean jetpack_cheat_active = FALSE;
|
|
boolean light_change_state = FALSE;
|
|
boolean allow_integrated_lights = game_engine_allow_integrated_lights(unit_index);
|
|
|
|
profile_enter(unit_update_section);
|
|
|
|
unit_verify_vectors(unit_index, "unit-update-begin");
|
|
|
|
++unit->unit.timer;
|
|
|
|
if (unit_globals->used_time!=0 || unit->unit.timer <= unit_globals->next_timer)
|
|
{
|
|
long timer;
|
|
|
|
if (unit_globals->highest_timer>unit->unit.timer)
|
|
{
|
|
timer = unit_globals->highest_timer;
|
|
}
|
|
else
|
|
{
|
|
timer = unit->unit.timer;
|
|
}
|
|
|
|
unit_globals->highest_timer = timer;
|
|
}
|
|
else
|
|
{
|
|
used_time = TRUE;
|
|
unit_globals->used_time = TRUE;
|
|
unit->unit.timer = 0;
|
|
}
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_running_blindly_bit))
|
|
{
|
|
unit_running_blind(unit_index, &unit->unit.desired_facing_vector);
|
|
unit->unit.desired_aiming_vector = unit->unit.desired_facing_vector;
|
|
unit->unit.desired_looking_vector = unit->unit.desired_facing_vector;
|
|
unit->unit.throttle = *global_forward3d;
|
|
unit->unit.control_flags = 0;
|
|
}
|
|
else if (!TEST_FLAG(unit->unit.flags, _unit_actively_controlled_bit))
|
|
{
|
|
unit->unit.desired_looking_vector = unit->object.forward;
|
|
unit->unit.desired_aiming_vector = unit->object.forward;
|
|
unit->unit.desired_facing_vector = unit->object.forward;
|
|
unit->unit.throttle = *global_zero_vector3d;
|
|
unit->unit.control_flags = 0;
|
|
}
|
|
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_simple_creature_bit))
|
|
{
|
|
boolean v4 = FALSE;
|
|
|
|
if (unit->unit.persistent_control_timer>0)
|
|
{
|
|
unit->unit.control_flags = unit->unit.persistent_control_flags|unit->unit.control_flags;
|
|
if (TEST_FLAG(unit->unit.persistent_control_flags, _unit_control_weapon_primary_trigger_bit))
|
|
{
|
|
SET_FLAG(unit->unit.control_flags, _unit_control_weapon_primary_trigger_bit, (unit->unit.persistent_control_timer%7)==0);
|
|
unit->unit.primary_trigger = 1.f;
|
|
}
|
|
else
|
|
{
|
|
unit->unit.primary_trigger = 0.f;
|
|
}
|
|
|
|
if (--unit->unit.persistent_control_timer==0)
|
|
{
|
|
unit->unit.persistent_control_flags = 0;
|
|
}
|
|
}
|
|
|
|
if (!TEST_FLAG(unit->unit.flags, _unit_possessed_by_recording_bit))
|
|
{
|
|
if (unit->unit.driver_object_index!=NONE &&
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
struct unit_datum *driver_unit = unit_get(unit->unit.driver_object_index);
|
|
|
|
unit->object.owner_team_index = driver_unit->object.owner_team_index;
|
|
v4 = TRUE;
|
|
|
|
if (driver_unit->unit.player_index!=NONE ||
|
|
driver_unit->unit.animation.state!=_unit_state_exiting_seat &&
|
|
driver_unit->unit.animation.state!=_unit_state_entering_seat)
|
|
{
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
696,
|
|
&driver_unit->unit.desired_facing_vector
|
|
);
|
|
unit->unit.control_flags |= driver_unit->unit.control_flags&UNIT_CONTROL_DRIVER_MASK;
|
|
unit->unit.desired_facing_vector = driver_unit->unit.desired_facing_vector;
|
|
unit->unit.throttle = driver_unit->unit.throttle;
|
|
}
|
|
}
|
|
|
|
if (unit->unit.gunner_object_index!=NONE &&
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
struct unit_datum *gunner_unit = unit_get(unit->unit.gunner_object_index);
|
|
|
|
if (!v4)
|
|
{
|
|
unit->object.owner_team_index = gunner_unit->object.owner_team_index;
|
|
}
|
|
|
|
if (gunner_unit->unit.player_index!=NONE ||
|
|
gunner_unit->unit.animation.state!=_unit_state_exiting_seat &&
|
|
gunner_unit->unit.animation.state!=_unit_state_entering_seat)
|
|
{
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
720,
|
|
&gunner_unit->unit.desired_aiming_vector
|
|
);
|
|
|
|
unit->unit.desired_aiming_vector = gunner_unit->unit.desired_aiming_vector;
|
|
unit->unit.desired_looking_vector = gunner_unit->unit.desired_aiming_vector;
|
|
unit->unit.control_flags |= gunner_unit->unit.control_flags&UNIT_CONTROL_GUNNER_MASK;
|
|
unit->unit.primary_trigger = gunner_unit->unit.primary_trigger;
|
|
}
|
|
}
|
|
|
|
if ((unit->unit.control_flags&UNIT_CONTROL_GUNNER_MASK)!=0)
|
|
{
|
|
unit->unit.gunner_inactive_ticks = 0;
|
|
}
|
|
else if (unit->unit.gunner_inactive_ticks<CHAR_MAX)
|
|
{
|
|
++unit->unit.gunner_inactive_ticks;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Jetpack cheat combo:
|
|
* Press the following keys while the jetpack cheat is active
|
|
* Grenade throw button
|
|
* Primary weapon fire button
|
|
*/
|
|
if (cheat.jetpack &&
|
|
TEST_FLAG(unit->unit.control_flags, _unit_control_weapon_primary_trigger_bit) &&
|
|
TEST_FLAG(unit->unit.control_flags, _unit_control_throw_grenade_bit))
|
|
{
|
|
jetpack_cheat_active = TRUE;
|
|
}
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_active_camouflaged_bit))
|
|
{
|
|
real active_camo_change_this_tick;
|
|
|
|
if (game_engine_running() &&
|
|
unit->unit.cause_for_camo_regrowth)
|
|
{
|
|
if (unit->unit.cause_for_camo_regrowth==cause_for_camo_regrowth_shot_fired)
|
|
{
|
|
boolean set = FALSE;
|
|
long weapon_index = unit_get_current_weapon_index(unit_index);
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
if (weapon_definition->weapon.active_camo_regrowth_rate!=0.f)
|
|
{
|
|
active_camo_change_this_tick = weapon_definition->weapon.active_camo_regrowth_rate;
|
|
set = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!set)
|
|
active_camo_change_this_tick = 1.f/120.f;
|
|
}
|
|
else
|
|
active_camo_change_this_tick = 1.f/120.f;
|
|
}
|
|
else
|
|
active_camo_change_this_tick = 1.f/120.f;
|
|
|
|
unit->unit.active_camouflage += active_camo_change_this_tick;
|
|
|
|
if (unit->unit.active_camouflage>1.f)
|
|
{
|
|
unit->unit.active_camouflage = 1.f;
|
|
unit->unit.cause_for_camo_regrowth = cause_for_camo_regrowth_default;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unit->unit.active_camouflage -= 1.f/120.f;
|
|
|
|
if (unit->unit.active_camouflage<0.f)
|
|
{
|
|
unit->unit.active_camouflage = 0.f;
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_super_camouflaged_bit))
|
|
{
|
|
unit->unit.active_camouflage_super_amount += 1.f/90.f;
|
|
|
|
if (unit->unit.active_camouflage_super_amount>1.f)
|
|
{
|
|
unit->unit.active_camouflage_super_amount = 1.f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unit->unit.active_camouflage_super_amount -= 1.f/90.f;
|
|
|
|
if (unit->unit.active_camouflage_super_amount<0.f)
|
|
{
|
|
unit->unit.active_camouflage_super_amount = 0.f;
|
|
}
|
|
}
|
|
|
|
// Update stun timer
|
|
if (unit->unit.body_stun_ticks>0)
|
|
{
|
|
unit->unit.body_stun_ticks--;
|
|
// Stop stunning when tick timer is done
|
|
if (unit->unit.body_stun_ticks==0)
|
|
{
|
|
unit->unit.body_stun = 0.f;
|
|
}
|
|
}
|
|
|
|
// Drop weapon when drop delay is done
|
|
if (unit->unit.weapon_drop_delay_ticks>0 && --unit->unit.weapon_drop_delay_ticks==0)
|
|
{
|
|
unit_drop_current_weapon(unit_index, TRUE);
|
|
}
|
|
|
|
// Update feign death timer
|
|
if (unit->unit.feign_death_timer>0 &&
|
|
TEST_FLAG(unit->object.flags, _object_at_rest_bit))
|
|
{
|
|
unit->unit.feign_death_timer--;
|
|
|
|
// Die when the unit has met it's end
|
|
if (!unit->unit.feign_death_timer)
|
|
{
|
|
if (unit->object.body_vitality>0.f)
|
|
{
|
|
short new_state = TEST_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_fallen_on_front_bit) ? _unit_state_resurrect_front : _unit_state_resurrect_back;
|
|
|
|
SET_FLAG(unit->object.damage_flags, _object_dead_bit, FALSE);
|
|
|
|
unit_set_actively_controlled(unit_index, TRUE);
|
|
unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
base_seat_label_get(_unit_animation_state_suspicious),
|
|
NULL,
|
|
TRUE
|
|
);
|
|
unit_animation_set_state(unit_index, new_state);
|
|
|
|
SET_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_ignore_translation_bit,
|
|
FALSE);
|
|
|
|
if (unit->object.type==_object_type_biped)
|
|
{
|
|
biped_stop_limp_body_physics(unit_index);
|
|
}
|
|
|
|
unit_scream(unit_index, _unit_scream_resurrection);
|
|
}
|
|
else
|
|
{
|
|
unit_died(unit_index, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_has_no_aiming_bit))
|
|
{
|
|
real aim_scale;
|
|
real aiming_velocity_limit;
|
|
real aiming_angular_acceleration_limit;
|
|
real_vector3d last_aiming_vector;
|
|
|
|
if (!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
if (TEST_FLAG(unit->object.damage_flags, _object_cannot_hold_weapon_bit))
|
|
{
|
|
unit_drop_current_weapon(unit_index, TRUE);
|
|
}
|
|
else if (unit->unit.desired_weapon_index!=unit->unit.current_weapon_index &&
|
|
!unit_animation_busy(&unit->unit.animation))
|
|
{
|
|
long weapon_index = unit_get_desired_weapon_index(unit_index);
|
|
|
|
if (weapon_index!=NONE && unit_can_use_weapon(unit_index, weapon_index))
|
|
{
|
|
unit_ready_desired_weapon(unit_index, TRUE);
|
|
}
|
|
}
|
|
|
|
if (unit->unit.desired_grenade_index!=unit->unit.current_grenade_index &&
|
|
!unit_animation_busy(&unit->unit.animation))
|
|
{
|
|
short grenade_index = unit_inventory_next_grenade(unit_index, unit->unit.desired_grenade_index, 0);
|
|
|
|
if (grenade_index!=NONE)
|
|
{
|
|
unit->unit.current_grenade_index = grenade_index;
|
|
}
|
|
}
|
|
|
|
if (cheat.infinite_ammo && unit->unit.player_index!=NONE)
|
|
{
|
|
short grenade_index;
|
|
|
|
for (grenade_index = 0; grenade_index<NUMBEROF(unit->unit.grenade_counts); ++grenade_index)
|
|
{
|
|
unit->unit.grenade_counts[grenade_index] = unit->unit.grenade_counts[grenade_index]>1 ? unit->unit.grenade_counts[grenade_index] : 1;
|
|
}
|
|
|
|
if (unit->unit.desired_grenade_index==NONE)
|
|
{
|
|
unit->unit.desired_grenade_index = 0;
|
|
}
|
|
}
|
|
|
|
if (unit->unit.desired_zoom_level!=unit->unit.current_zoom_level)
|
|
{
|
|
long local_player_index;
|
|
|
|
unit->unit.current_zoom_level = unit->unit.desired_zoom_level;
|
|
|
|
if (unit->unit.desired_zoom_level==NONE)
|
|
{
|
|
unit->unit.integrated_night_vision_power = 0.f;
|
|
}
|
|
|
|
if (player_index_from_unit_index(unit_index)!=NONE)
|
|
{
|
|
local_player_index = player_get(player_index_from_unit_index(unit_index))->local_player_index;
|
|
}
|
|
else
|
|
{
|
|
local_player_index = NONE;
|
|
}
|
|
|
|
if (local_player_index!=NONE)
|
|
{
|
|
long weapon_index = unit_get_current_weapon_index(unit_index);
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
long sound_index = unit->unit.current_zoom_level==NONE ? weapon_definition->weapon.zoom_out_sound.index : weapon_definition->weapon.zoom_in_sound.index;
|
|
real scale = 1.f;
|
|
|
|
if (unit->unit.current_zoom_level!=NONE && weapon_definition->weapon.zoom_level_count>1)
|
|
{
|
|
scale = (real)unit->unit.current_zoom_level/(real)(weapon_definition->weapon.zoom_level_count-1);
|
|
}
|
|
|
|
if (sound_index!=NONE)
|
|
{
|
|
unspatialized_impulse_sound_new(sound_index, scale);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unit_verify_vectors(unit_index, "unit-update-prevectors");
|
|
|
|
if (unit->unit.aiming_speed==_unit_aiming_speed_casual)
|
|
{
|
|
aim_scale = unit_definition->unit.casual_aiming_modifier;
|
|
}
|
|
else
|
|
{
|
|
aim_scale = 1.f;
|
|
}
|
|
|
|
aiming_velocity_limit = aim_scale*unit_definition->unit.aiming_velocity_maximum/TICKS_PER_SECOND;
|
|
aiming_angular_acceleration_limit = aim_scale*unit_definition->unit.aiming_acceleration_maximum/(TICKS_PER_SECOND*TICKS_PER_SECOND);
|
|
last_aiming_vector = unit->unit.aiming_vector;
|
|
|
|
if (aiming_velocity_limit==0.f && aiming_angular_acceleration_limit==0.f)
|
|
{
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 993, &unit->unit.desired_aiming_vector);
|
|
|
|
unit->unit.aiming_vector = unit->unit.desired_aiming_vector;
|
|
unit_clip_to_aiming_bounds(unit_index, &unit->unit.aiming_vector, TRUE);
|
|
unit->unit.aiming_velocity = *global_zero_vector3d;
|
|
unit_verify_vectors(unit_index, "unit-update-aim-set");
|
|
}
|
|
else if (unit->unit.animation.aiming_with_euler_screen)
|
|
{
|
|
real_matrix4x3 object_matrix;
|
|
|
|
object_matrix.scale = 1.f;
|
|
object_get_orientation(unit_index, &object_matrix.forward, &object_matrix.up);
|
|
cross_product3d(&object_matrix.up, &object_matrix.forward, &object_matrix.left);
|
|
object_matrix.position = *global_origin3d;
|
|
|
|
unit_euler_aiming_update(
|
|
&object_matrix,
|
|
&unit->unit.aiming_vector,
|
|
&unit->unit.desired_aiming_vector,
|
|
&unit->unit.aiming_velocity,
|
|
&unit->unit.animation.aiming_screen_bounds,
|
|
aiming_velocity_limit,
|
|
aiming_angular_acceleration_limit);
|
|
|
|
unit_verify_vectors(unit_index, "unit-update-aim-euler");
|
|
}
|
|
else
|
|
{
|
|
angular_accelerate_to_position(
|
|
&unit->unit.aiming_vector,
|
|
&unit->unit.desired_aiming_vector,
|
|
&unit->unit.aiming_velocity,
|
|
aiming_velocity_limit,
|
|
aiming_angular_acceleration_limit);
|
|
unit_verify_vectors(unit_index, "unit-update-aim-3d");
|
|
}
|
|
|
|
{
|
|
real angle = angle_between_vectors3d(&unit->unit.aiming_vector, &last_aiming_vector);
|
|
real change = angle / (unit_definition->unit.aiming_velocity_maximum/TICKS_PER_SECOND);
|
|
|
|
unit->unit.aiming_change = (byte)(PIN(change, 0.f, 1.f) * 255.f);
|
|
}
|
|
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 1032, &unit->unit.aiming_vector);
|
|
|
|
{
|
|
real looking_velocity_limit = (aim_scale*unit_definition->unit.looking_velocity_maximum)/((real)TICKS_PER_SECOND);
|
|
real looking_angular_acceleration_limit = (aim_scale*unit_definition->unit.looking_acceleration_maximum)/900.f;
|
|
|
|
if (looking_velocity_limit==0.f && looking_angular_acceleration_limit==0.f)
|
|
{
|
|
unit->unit.looking_vector = unit->unit.desired_looking_vector;
|
|
unit_clip_to_aiming_bounds(unit_index, &unit->unit.looking_vector, FALSE);
|
|
unit->unit.looking_velocity = *global_zero_vector3d;
|
|
|
|
unit_verify_vectors(unit_index, "unit-update-look-set");
|
|
}
|
|
else if (unit->unit.animation.looking_with_euler_screen)
|
|
{
|
|
real_matrix4x3 object_matrix;
|
|
|
|
object_matrix.scale = 1.f;
|
|
object_get_orientation(unit_index, &object_matrix.forward, &object_matrix.up);
|
|
cross_product3d(&object_matrix.up, &object_matrix.forward, &object_matrix.left);
|
|
object_matrix.position = *global_origin3d;
|
|
|
|
unit_euler_aiming_update(
|
|
&object_matrix,
|
|
&unit->unit.looking_vector,
|
|
&unit->unit.desired_looking_vector,
|
|
&unit->unit.looking_velocity,
|
|
&unit->unit.animation.looking_screen_bounds,
|
|
looking_velocity_limit,
|
|
looking_angular_acceleration_limit);
|
|
|
|
unit_verify_vectors(unit_index, "unit-update-look-euler");
|
|
}
|
|
else
|
|
{
|
|
angular_accelerate_to_position(
|
|
&unit->unit.looking_vector,
|
|
&unit->unit.desired_looking_vector,
|
|
&unit->unit.looking_velocity,
|
|
looking_velocity_limit,
|
|
looking_angular_acceleration_limit);
|
|
unit_verify_vectors(unit_index, "unit-update-look-3d");
|
|
}
|
|
}
|
|
|
|
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 1076, &unit->unit.looking_vector);
|
|
|
|
unit_verify_vectors(unit_index, "unit-update-postvector");
|
|
|
|
if (!jetpack_cheat_active)
|
|
{
|
|
boolean throw_grenade = TEST_FLAG(unit->unit.control_flags, _unit_control_throw_grenade_bit);
|
|
|
|
switch (unit->unit.grenade_throw_state)
|
|
{
|
|
case _unit_grenade_throw_idle:
|
|
if (throw_grenade)
|
|
{
|
|
unit_throw_grenade_begin(unit_index, NULL);
|
|
}
|
|
break;
|
|
case _unit_grenade_throw_wind_up:
|
|
if (unit->object.animation.state.frame_index>=2)
|
|
{
|
|
unit_throw_grenade_move_to_hand(unit_index);
|
|
}
|
|
break;
|
|
case _unit_grenade_throw_ending:
|
|
if (unit->unit.animation.state!=_unit_state_throw_grenade && !throw_grenade)
|
|
{
|
|
unit->unit.grenade_throw_state = _unit_grenade_throw_idle;
|
|
}
|
|
break;
|
|
case _unit_grenade_throw_in_hand:
|
|
++unit->unit.grenade_throw_ticks;
|
|
if (unit->unit.animation.state!=_unit_state_throw_grenade)
|
|
{
|
|
unit_throw_grenade_release(unit_index, TRUE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (unit->unit.current_weapon_index!=NONE)
|
|
{
|
|
long flags = 0;
|
|
real primary_trigger = unit->unit.primary_trigger;
|
|
|
|
if (unit->unit.current_weapon_index==unit->unit.desired_weapon_index)
|
|
{
|
|
boolean const time_remaining = unit->unit.persistent_control_timer>0 && TEST_FLAG(unit->unit.persistent_control_flags, _unit_control_weapon_primary_trigger_bit);
|
|
|
|
if (!jetpack_cheat_active)
|
|
{
|
|
if (allow_integrated_lights && TEST_FLAG(unit->unit.control_flags, _unit_control_integrated_light_bit))
|
|
{
|
|
SET_FLAG(flags, _weapon_control_integrated_light_bit, TRUE);
|
|
}
|
|
if (TEST_FLAG(unit->unit.control_flags, _unit_control_weapon_primary_trigger_bit))
|
|
{
|
|
SET_FLAG(flags, _weapon_control_primary_trigger_bit, TRUE);
|
|
}
|
|
if (TEST_FLAG(unit->unit.control_flags, _unit_control_weapon_secondary_trigger_bit))
|
|
{
|
|
SET_FLAG(flags, _weapon_control_secondary_trigger_bit, TRUE);
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(unit_definition_get(unit->definition_index)->unit.flags, _unit_integrated_light_controls_weapon_directly_bit))
|
|
{
|
|
weapon_set_integrated_light_power(unit_get_current_weapon_index(unit_index), unit->unit.integrated_light_power);
|
|
}
|
|
|
|
if (TEST_FLAG(unit->unit.control_flags, _unit_control_weapon_reload_bit))
|
|
{
|
|
SET_FLAG(flags, _weapon_control_reload_bit, TRUE);
|
|
}
|
|
if (unit_animation_busy(&unit->unit.animation) && !time_remaining)
|
|
{
|
|
SET_FLAG(flags, _weapon_control_user_busy_bit, TRUE);
|
|
}
|
|
if (unit->object.type==_object_type_biped && ((struct biped_datum *)unit)->biped.player_melee_ticks > 0)
|
|
{
|
|
SET_FLAG(flags, _weapon_control_user_busy_bit, TRUE);
|
|
}
|
|
if (unit->unit.current_zoom_level!=NONE)
|
|
{
|
|
SET_FLAG(flags, _weapon_control_zoomed_bit, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SET_FLAG(flags, _weapon_control_user_switching_weapons_bit, TRUE);
|
|
}
|
|
|
|
weapon_owner_update(unit_get_current_weapon_index(unit_index), flags, primary_trigger);
|
|
}
|
|
}
|
|
|
|
|
|
match_assert_valid_real_vector3d_axes2("c:\\halo\\SOURCE\\units\\units.c", 1155, &unit->object.forward, &unit->object.up);
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 1156, &unit->unit.aiming_vector);
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 1157, &unit->unit.looking_vector);
|
|
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_simple_creature_bit))
|
|
{
|
|
short seat_index;
|
|
|
|
if (TEST_FLAG(unit->unit.animation.flags, _unit_animation_showing_acceleration_bit))
|
|
{
|
|
unit_seat_update(unit_index);
|
|
unit->unit.seat_acceleration.i = unit->unit.seat_desired_acceleration.i*0.3f + unit->unit.seat_acceleration.i*0.7f;
|
|
unit->unit.seat_acceleration.j = unit->unit.seat_desired_acceleration.j*0.3f + unit->unit.seat_acceleration.j*0.7f;
|
|
unit->unit.seat_acceleration.k = unit->unit.seat_desired_acceleration.k*0.3f + unit->unit.seat_acceleration.k*0.7f;
|
|
}
|
|
|
|
for (seat_index = 0; seat_index<unit_definition->unit.powered_seats.count; ++seat_index)
|
|
{
|
|
boolean v96 = FALSE;
|
|
struct powered_seat_definition *powered_seat = TAG_BLOCK_GET_ELEMENT(&unit_definition->unit.powered_seats, seat_index, struct powered_seat_definition);
|
|
|
|
if (seat_index==0)
|
|
{
|
|
if (unit->unit.driver_object_index!=NONE || TEST_FLAG(unit->unit.flags, _unit_actively_controlled_bit))
|
|
{
|
|
v96 = TRUE;
|
|
}
|
|
else
|
|
{
|
|
v96 = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
v96 = unit->unit.gunner_object_index!=NONE && unit->unit.gunner_object_index!=unit->unit.driver_object_index;
|
|
}
|
|
|
|
if (TEST_FLAG(unit->object.damage_flags, _object_dead_bit) || !v96)
|
|
{
|
|
if (unit->unit.seat_power[seat_index] != 0.f)
|
|
{
|
|
unit->unit.seat_power[seat_index] -= 1.f / (powered_seat->powerdown_time * TICKS_PER_SECOND);
|
|
|
|
if (unit->unit.seat_power[seat_index]<0.f)
|
|
{
|
|
unit->unit.seat_power[seat_index] = 0.f;
|
|
}
|
|
}
|
|
}
|
|
else if (unit->unit.seat_power[seat_index] != 1.f)
|
|
{
|
|
unit->unit.seat_power[seat_index] += 1.f / (powered_seat->powerup_time * TICKS_PER_SECOND);
|
|
|
|
if (unit->unit.seat_power[seat_index]>1.f)
|
|
{
|
|
unit->unit.seat_power[seat_index] = 1.f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unit->unit.delayed_damage_timer>0)
|
|
{
|
|
unit->unit.delayed_damage_timer--;
|
|
|
|
if (unit->unit.delayed_damage_timer==0)
|
|
{
|
|
ai_handle_damage(
|
|
unit_index,
|
|
unit->unit.delayed_damage_attacker_object_index,
|
|
unit->unit.last_damage_category,
|
|
unit->unit.delayed_damage_peak,
|
|
NULL,
|
|
TRUE);
|
|
|
|
unit->unit.last_damage_category = 0;
|
|
unit->unit.delayed_damage_attacker_object_index = NONE;
|
|
unit->unit.delayed_damage_peak = 0.f;
|
|
}
|
|
}
|
|
|
|
unit_cause_continuous_melee_damage(unit_index);
|
|
unit_dialogue_update(unit_index);
|
|
|
|
if (used_time || unit->unit.player_index!=NONE)
|
|
{
|
|
unit_refresh_illumination(unit_index);
|
|
|
|
if (debug_unit_illumination)
|
|
{
|
|
if (unit->unit.player_index !=NONE)
|
|
{
|
|
long current_time = game_time_get();
|
|
static long last_time = NONE;
|
|
|
|
if (current_time >= last_time+TICKS_PER_SECOND)
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"player illumination: self %.2f ambient %.2f",
|
|
unit->unit.self_illumination,
|
|
unit->unit.ambient_illumination);
|
|
last_time = current_time;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unit->unit.flaming_death_delay>0)
|
|
{
|
|
if (--unit->unit.flaming_death_delay==0)
|
|
{
|
|
unit_flame_to_death(unit_index);
|
|
}
|
|
}
|
|
|
|
interpolate_scalar(&unit->unit.mouth_aperture, 0.f, 0.1f);
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_desired_integrated_light_on_bit))
|
|
{
|
|
if (!TEST_FLAG(unit->unit.flags, _unit_integrated_light_on_bit))
|
|
{
|
|
light_change_state = TRUE;
|
|
}
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_desired_integrated_light_on_bit, FALSE);
|
|
}
|
|
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_desired_integrated_light_off_bit))
|
|
{
|
|
if (TEST_FLAG(unit->unit.flags, _unit_integrated_light_on_bit))
|
|
{
|
|
light_change_state = TRUE;
|
|
}
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_desired_integrated_light_off_bit, FALSE);
|
|
}
|
|
|
|
if (allow_integrated_lights &&
|
|
TEST_FLAG(unit->unit.control_flags, _unit_control_integrated_light_bit) ||
|
|
unit->unit.integrated_light_battery <= 0.f ||
|
|
light_change_state)
|
|
{
|
|
boolean const active = unit_integrated_night_vision_is_active(unit_index);
|
|
|
|
if (active && TEST_FLAG(unit->unit.control_flags, _unit_control_integrated_light_bit))
|
|
{
|
|
long effect_index;
|
|
struct game_globals *game_globals;
|
|
struct game_globals_first_person_interface *game_globals_first_person_interface;
|
|
|
|
game_globals = scenario_get_game_globals();
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 1302, game_globals);
|
|
game_globals_first_person_interface = TAG_BLOCK_GET_ELEMENT(&game_globals->first_person_interface, 0, struct game_globals_first_person_interface);
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 1304, game_globals_first_person_interface);
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_integrated_night_vision_on_bit))
|
|
{
|
|
effect_index = game_globals_first_person_interface->night_vision_on_off_effect.index;
|
|
}
|
|
else
|
|
{
|
|
effect_index = game_globals_first_person_interface->night_vision_off_on_effect.index;
|
|
}
|
|
|
|
if (effect_index!=NONE)
|
|
{
|
|
effect_new_from_object(effect_index, unit_index, unit_index, NONE, 0.f, 0.f, NULL, NULL);
|
|
}
|
|
|
|
unit->unit.flags ^= FLAG(_unit_integrated_night_vision_on_bit);
|
|
}
|
|
|
|
if (!(active && TEST_FLAG(unit->unit.control_flags, _unit_control_integrated_light_bit)) &&
|
|
(TEST_FLAG(unit->unit.flags, _unit_integrated_light_on_bit) || unit->unit.integrated_light_battery > 0.2f) &&
|
|
unit->object.parent_object_index==NONE)
|
|
{
|
|
effect_new_from_object(unit_definition->unit.integrated_light_toggle_effect.index, unit_index, unit_index, NONE, 0.f, 0.f, NULL, NULL);
|
|
unit->unit.flags ^= FLAG(_unit_integrated_light_on_bit);
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_integrated_light_on_bit))
|
|
{
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_integrated_light_fucking_lasts_forever_bit))
|
|
{
|
|
unit->unit.integrated_light_battery = unit->unit.integrated_light_battery - 1.f/(2.f*TICKS_PER_MINUTE);
|
|
}
|
|
|
|
if (unit->object.parent_object_index!=NONE || TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
SET_FLAG(unit->unit.flags, _unit_integrated_light_on_bit, FALSE);
|
|
}
|
|
|
|
if (unit->unit.integrated_light_power!=1.f)
|
|
{
|
|
unit->unit.integrated_light_power += 1.f/6.f;
|
|
|
|
if (unit->unit.integrated_light_power>1.f)
|
|
{
|
|
unit->unit.integrated_light_power = 1.f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (unit->unit.integrated_light_battery < 1.f)
|
|
{
|
|
unit->unit.integrated_light_battery += 1.f/900.f;
|
|
}
|
|
|
|
if (unit->unit.integrated_light_power != 0.f)
|
|
{
|
|
unit->unit.integrated_light_power -= 1.f/24.f;
|
|
|
|
if (unit->unit.integrated_light_power<0.f)
|
|
{
|
|
unit->unit.integrated_light_power = 0.f;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (unit_integrated_night_vision_is_active(unit_index))
|
|
{
|
|
if (TEST_FLAG(unit->unit.flags, _unit_integrated_night_vision_on_bit))
|
|
{
|
|
if (unit->unit.integrated_night_vision_power!=1.f)
|
|
{
|
|
unit->unit.integrated_night_vision_power += 1.f/12.f;
|
|
|
|
if (unit->unit.integrated_night_vision_power>1.f)
|
|
{
|
|
unit->unit.integrated_night_vision_power = 1.f;
|
|
}
|
|
}
|
|
}
|
|
else if (unit->unit.integrated_night_vision_power!=0.f)
|
|
{
|
|
unit->unit.integrated_night_vision_power -= 1.f/24.f;
|
|
|
|
if (unit->unit.integrated_night_vision_power<0.f)
|
|
{
|
|
unit->unit.integrated_night_vision_power = 0.f;
|
|
}
|
|
}
|
|
}
|
|
|
|
unit_verify_vectors(unit_index, "unit-update-end");
|
|
|
|
profile_exit(unit_update_section);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void unit_unzoom(
|
|
long unit_index)
|
|
{
|
|
boolean play_sound;
|
|
boolean current_weapon_zooms;
|
|
long current_weapon_index;
|
|
long local_player_index;
|
|
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (player_index_from_unit_index(unit_index)==NONE)
|
|
{
|
|
local_player_index = NONE;
|
|
}
|
|
else
|
|
{
|
|
local_player_index = player_get(player_index_from_unit_index(unit_index))->local_player_index;
|
|
}
|
|
|
|
current_weapon_zooms = unit->unit.current_zoom_level!=NONE;
|
|
|
|
if (local_player_index!=NONE && current_weapon_zooms)
|
|
{
|
|
current_weapon_index = unit_get_current_weapon_index(unit_index);
|
|
|
|
if (current_weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(current_weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
long zoom_out_sound_index = weapon_definition->weapon.zoom_out_sound.index;
|
|
|
|
play_sound = zoom_out_sound_index!=NONE;
|
|
|
|
if (play_sound)
|
|
{
|
|
unspatialized_impulse_sound_new(zoom_out_sound_index, 1.f);
|
|
}
|
|
}
|
|
}
|
|
|
|
unit->unit.current_zoom_level = NONE;
|
|
unit->unit.desired_zoom_level = NONE;
|
|
unit->unit.integrated_night_vision_power = 0.f;
|
|
player_control_unzoom(unit_index);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_record_damage(
|
|
long unit_index,
|
|
real damage_amount,
|
|
short damage_type,
|
|
boolean notify_ai,
|
|
long attacker_player_index,
|
|
short attacker_team,
|
|
long killing_object_index)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct unit_datum *attacker_unit;
|
|
long attacker_unit_index;
|
|
long player_unit_index;
|
|
long controlling_unit_index;
|
|
long game_time;
|
|
short attacker_index;
|
|
short preserved_attacker_index;
|
|
short best_new_attacker_index;
|
|
boolean found_attacker;
|
|
|
|
unit = unit_get(unit_index);
|
|
found_attacker = FALSE;
|
|
game_time = game_time_get();
|
|
for (attacker_index = 0;
|
|
attacker_index < MAXIMUM_ATTACKERS_PER_UNIT;
|
|
attacker_index++)
|
|
{
|
|
struct unit_attacker *attacker =
|
|
&unit->unit.attackers[attacker_index];
|
|
|
|
if ((attacker_player_index != NONE &&
|
|
attacker->player_index == attacker_player_index) ||
|
|
attacker->object_index == killing_object_index)
|
|
{
|
|
attacker->game_time_stamp = game_time;
|
|
found_attacker = TRUE;
|
|
attacker->damage_inflicted += damage_amount;
|
|
}
|
|
}
|
|
|
|
if (!found_attacker)
|
|
{
|
|
best_new_attacker_index = NONE;
|
|
for (attacker_index = 0;
|
|
attacker_index < MAXIMUM_ATTACKERS_PER_UNIT;
|
|
attacker_index++)
|
|
{
|
|
if (unit->unit.attackers[attacker_index].game_time_stamp == NONE)
|
|
{
|
|
best_new_attacker_index = attacker_index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (best_new_attacker_index == NONE)
|
|
{
|
|
preserved_attacker_index = 0;
|
|
for (attacker_index = 1;
|
|
attacker_index < MAXIMUM_ATTACKERS_PER_UNIT;
|
|
attacker_index++)
|
|
{
|
|
if (unit->unit.attackers[attacker_index].damage_inflicted >
|
|
unit->unit.attackers[preserved_attacker_index].damage_inflicted)
|
|
{
|
|
preserved_attacker_index = attacker_index;
|
|
}
|
|
}
|
|
|
|
best_new_attacker_index = NONE;
|
|
for (attacker_index = 0;
|
|
attacker_index < MAXIMUM_ATTACKERS_PER_UNIT;
|
|
attacker_index++)
|
|
{
|
|
if (attacker_index != preserved_attacker_index &&
|
|
(best_new_attacker_index == NONE ||
|
|
unit->unit.attackers[attacker_index].game_time_stamp <
|
|
unit->unit.attackers[best_new_attacker_index].game_time_stamp))
|
|
{
|
|
best_new_attacker_index = attacker_index;
|
|
}
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
4972,
|
|
best_new_attacker_index!=NONE);
|
|
}
|
|
|
|
unit->unit.attackers[best_new_attacker_index].player_index = attacker_player_index;
|
|
unit->unit.attackers[best_new_attacker_index].object_index = killing_object_index;
|
|
unit->unit.attackers[best_new_attacker_index].damage_inflicted = damage_amount;
|
|
unit->unit.attackers[best_new_attacker_index].game_time_stamp = game_time;
|
|
}
|
|
|
|
attacker_unit = NULL;
|
|
attacker_unit_index = killing_object_index;
|
|
if (notify_ai && attacker_team != NONE &&
|
|
game_team_is_enemy(unit->object.owner_team_index, attacker_team))
|
|
{
|
|
if (attacker_player_index != NONE)
|
|
{
|
|
player_unit_index = player_get(attacker_player_index)->unit_index;
|
|
if (player_unit_index != NONE)
|
|
{
|
|
attacker_unit_index = player_unit_index;
|
|
attacker_unit = unit_get(player_unit_index);
|
|
}
|
|
}
|
|
|
|
if (attacker_unit == NULL)
|
|
{
|
|
attacker_unit_index = killing_object_index;
|
|
attacker_unit = unit_try_and_get(killing_object_index);
|
|
}
|
|
|
|
if (attacker_unit != NULL)
|
|
{
|
|
if (damage_type == _unit_record_damage_driver_seat_type)
|
|
controlling_unit_index = attacker_unit->unit.driver_object_index;
|
|
else
|
|
controlling_unit_index = attacker_unit->unit.gunner_object_index;
|
|
|
|
if (controlling_unit_index != NONE)
|
|
{
|
|
attacker_unit_index = controlling_unit_index;
|
|
attacker_unit = unit_get(controlling_unit_index);
|
|
}
|
|
|
|
if (!TEST_FLAG(attacker_unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
game_time = game_time_get();
|
|
if (attacker_unit->unit.killing_spree_last_time == NONE ||
|
|
attacker_unit->unit.killing_spree_last_time + 120 < game_time)
|
|
{
|
|
attacker_unit->unit.killing_spree_count = 0;
|
|
}
|
|
|
|
attacker_unit->unit.killing_spree_count++;
|
|
attacker_unit->unit.killing_spree_last_time = game_time;
|
|
if (ai_handle_killing_spree(
|
|
attacker_unit_index,
|
|
attacker_unit->unit.killing_spree_count))
|
|
{
|
|
attacker_unit->unit.killing_spree_count = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_ping_animation(
|
|
long unit_index,
|
|
boolean killed,
|
|
boolean feign_death,
|
|
boolean suppress_random_death_frame,
|
|
boolean suppress_hard_ping,
|
|
boolean force_hard_ping,
|
|
real damage_direction_angle,
|
|
short damage_part,
|
|
real_vector2d const *alignment_vector)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct unit_definition *unit_definition;
|
|
struct animation_graph *animation_graph;
|
|
boolean ping;
|
|
boolean hard_ping;
|
|
short damage_direction;
|
|
short damage_animation_index;
|
|
short animation_index;
|
|
short damage_animation_type;
|
|
short animation_state;
|
|
boolean apply_animation;
|
|
struct biped_datum *biped;
|
|
struct biped_definition *biped_definition;
|
|
struct animation *animation;
|
|
short quarter_frame_count;
|
|
short random_frame;
|
|
long front_animation_index;
|
|
word animation_flags;
|
|
|
|
unit = unit_get(unit_index);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (killed)
|
|
{
|
|
feign_death = FALSE;
|
|
ping = TRUE;
|
|
|
|
if (unit_definition->unit.hard_death_threshold>0.f &&
|
|
unit->object.current_body_damage>
|
|
unit_definition->unit.hard_death_threshold)
|
|
{
|
|
hard_ping = TRUE;
|
|
}
|
|
else
|
|
{
|
|
hard_ping = FALSE;
|
|
}
|
|
}
|
|
else if (feign_death)
|
|
{
|
|
killed = TRUE;
|
|
ping = TRUE;
|
|
hard_ping = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ping =
|
|
unit->object.current_body_damage>
|
|
unit_definition->unit.soft_ping_threshold ||
|
|
unit->object.current_shield_damage>
|
|
unit_definition->unit.soft_ping_threshold;
|
|
hard_ping =
|
|
unit->object.current_body_damage>
|
|
unit_definition->unit.hard_ping_threshold;
|
|
|
|
if (suppress_hard_ping ||
|
|
TEST_FLAG(unit->unit.flags, _unit_ignore_hard_pings_bit))
|
|
{
|
|
hard_ping = FALSE;
|
|
}
|
|
}
|
|
|
|
if (force_hard_ping)
|
|
{
|
|
hard_ping = TRUE;
|
|
ping = TRUE;
|
|
}
|
|
|
|
if (damage_part==NONE)
|
|
{
|
|
damage_part = 0;
|
|
}
|
|
|
|
if (fabs(damage_direction_angle)<UNIT_DAMAGE_REAR_CONE_ANGLE)
|
|
{
|
|
damage_direction = _unit_damage_direction_back;
|
|
}
|
|
else if (fabs(damage_direction_angle)>UNIT_DAMAGE_FRONT_CONE_ANGLE)
|
|
{
|
|
damage_direction = _unit_damage_direction_front;
|
|
}
|
|
else if (damage_direction_angle>0.f)
|
|
{
|
|
damage_direction = _unit_damage_direction_left;
|
|
}
|
|
else
|
|
{
|
|
damage_direction = _unit_damage_direction_right;
|
|
}
|
|
|
|
if (game_engine_running() &&
|
|
damage_part==_unit_damage_part_head &&
|
|
hard_ping &&
|
|
killed)
|
|
{
|
|
damage_direction = _unit_damage_direction_left;
|
|
}
|
|
|
|
if (!ping && !killed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
|
|
if (!hard_ping && !killed)
|
|
{
|
|
if (unit->unit.animation.soft_ping_animation.index!=NONE &&
|
|
unit->unit.animation.soft_ping_animation.frame_index<=
|
|
unit_definition->unit.runtime_soft_ping_minimum_interrupt_ticks)
|
|
{
|
|
return;
|
|
}
|
|
|
|
damage_animation_index = build_damage_animation_index(
|
|
_unit_damage_animation_soft_ping,
|
|
damage_direction,
|
|
damage_part);
|
|
|
|
{
|
|
long animation_graph_index;
|
|
long selected_damage_animation_index;
|
|
|
|
if (damage_animation_index>=0 &&
|
|
damage_animation_index<
|
|
animation_graph->unit_damage_animations.count)
|
|
{
|
|
selected_damage_animation_index =
|
|
animation_graph_animation_index_get(
|
|
&animation_graph->unit_damage_animations)
|
|
[damage_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
selected_damage_animation_index = NONE;
|
|
}
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
|
|
animation_index = animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
selected_damage_animation_index);
|
|
}
|
|
|
|
if (animation_index==NONE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
unit->unit.animation.soft_ping_animation.index = animation_index;
|
|
unit->unit.animation.soft_ping_animation.frame_index = 0;
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
animation_state = killed ? _unit_state_dying : _unit_state_hard_ping;
|
|
|
|
if (killed)
|
|
{
|
|
damage_animation_type =
|
|
hard_ping ?
|
|
_unit_damage_animation_hard_kill :
|
|
_unit_damage_animation_soft_kill;
|
|
}
|
|
else
|
|
{
|
|
damage_animation_type = _unit_damage_animation_hard_ping;
|
|
}
|
|
|
|
apply_animation =
|
|
killed ||
|
|
unit_animation_state_interruptable(
|
|
&unit->unit.animation,
|
|
animation_state);
|
|
|
|
if (unit->unit.animation.state==_unit_state_hard_ping &&
|
|
unit->object.animation.state.frame_index>
|
|
unit_definition->unit.runtime_hard_ping_minimum_interrupt_ticks)
|
|
{
|
|
apply_animation = TRUE;
|
|
}
|
|
|
|
if (!killed)
|
|
{
|
|
if (TEST_FLAG(
|
|
unit->object.damage_flags,
|
|
_object_dead_bit))
|
|
{
|
|
apply_animation = FALSE;
|
|
}
|
|
|
|
if (unit->object.parent_object_index!=NONE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!apply_animation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (killed)
|
|
{
|
|
char const *seat_label =
|
|
base_seat_labels[_unit_base_seat_stand];
|
|
|
|
unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
seat_label,
|
|
unit_get_current_weapon_label(unit_index),
|
|
TRUE);
|
|
}
|
|
|
|
if (animation_state==_unit_state_dying &&
|
|
unit->object.type==_object_type_biped)
|
|
{
|
|
biped = biped_get(unit_index);
|
|
biped_definition = biped_definition_get(biped->definition_index);
|
|
|
|
if (TEST_FLAG(biped->biped.flags, _biped_airborne_bit) &&
|
|
!TEST_FLAG(
|
|
biped_definition->biped.flags,
|
|
_biped_has_no_dying_airborne_bit))
|
|
{
|
|
animation_state = _unit_state_dying_airborne;
|
|
|
|
if (unit_animation_set_state(
|
|
unit_index,
|
|
_unit_state_dying_airborne))
|
|
{
|
|
goto apply_alignment;
|
|
}
|
|
}
|
|
}
|
|
|
|
damage_animation_index = build_damage_animation_index(
|
|
damage_animation_type,
|
|
damage_direction,
|
|
damage_part);
|
|
|
|
{
|
|
long animation_graph_index;
|
|
long selected_damage_animation_index;
|
|
|
|
if (damage_animation_index>=0 &&
|
|
damage_animation_index<
|
|
animation_graph->unit_damage_animations.count)
|
|
{
|
|
selected_damage_animation_index =
|
|
animation_graph_animation_index_get(
|
|
&animation_graph->unit_damage_animations)
|
|
[damage_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
selected_damage_animation_index = NONE;
|
|
}
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
|
|
animation_index = animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
selected_damage_animation_index);
|
|
}
|
|
|
|
if (animation_index!=NONE)
|
|
{
|
|
if (unit->unit.animation.state==_unit_state_throw_grenade)
|
|
{
|
|
unit_throw_grenade_release(unit_index, TRUE);
|
|
}
|
|
|
|
object_start_interpolation(unit_index, 3);
|
|
unit->unit.animation.state = (char)animation_state;
|
|
unit_set_animation(
|
|
unit_index,
|
|
unit_definition->object.animation_graph.index,
|
|
animation_index);
|
|
SET_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_postpone_weapon_ik_until_interpolation_ends_bit,
|
|
TRUE);
|
|
|
|
if (killed)
|
|
{
|
|
if (!suppress_random_death_frame && !feign_death)
|
|
{
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
quarter_frame_count = animation->frame_count>>2;
|
|
random_frame = random_range(
|
|
quarter_frame_count,
|
|
(animation->frame_count>>1) + quarter_frame_count);
|
|
unit->unit.weapon_drop_delay_ticks = (char)random_frame;
|
|
unit->unit.weapon_drop_delay_ticks =
|
|
MAX((char)random_frame, 1);
|
|
}
|
|
else
|
|
{
|
|
unit->unit.weapon_drop_delay_ticks = 0;
|
|
}
|
|
}
|
|
|
|
if (damage_direction!=_unit_damage_direction_front)
|
|
{
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
|
|
if (build_damage_animation_index(
|
|
damage_animation_type,
|
|
_unit_damage_direction_front,
|
|
damage_part)>=0 &&
|
|
build_damage_animation_index(
|
|
damage_animation_type,
|
|
_unit_damage_direction_front,
|
|
damage_part)<
|
|
animation_graph->unit_damage_animations.count)
|
|
{
|
|
front_animation_index =
|
|
animation_graph_animation_index_get(
|
|
&animation_graph->unit_damage_animations)
|
|
[build_damage_animation_index(
|
|
damage_animation_type,
|
|
_unit_damage_direction_front,
|
|
damage_part)].animation_index;
|
|
}
|
|
else
|
|
{
|
|
front_animation_index = NONE;
|
|
}
|
|
|
|
if (animation->runtime_parent_animation_index==
|
|
front_animation_index)
|
|
{
|
|
damage_direction = _unit_damage_direction_front;
|
|
}
|
|
}
|
|
|
|
if (killed)
|
|
{
|
|
SET_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_fallen_on_front_bit,
|
|
damage_direction==_unit_damage_direction_back);
|
|
}
|
|
}
|
|
else if (killed)
|
|
{
|
|
animation_flags = unit->unit.animation.flags;
|
|
|
|
animation_flags &=
|
|
~FLAG(_unit_animation_fallen_on_front_bit);
|
|
animation_flags |=
|
|
FLAG(_unit_animation_ignore_translation_bit);
|
|
unit->unit.animation.flags = animation_flags;
|
|
|
|
if (TEST_FLAG(
|
|
unit_definition->unit.flags,
|
|
_unit_is_destroyed_after_dying_bit))
|
|
{
|
|
unit_destroy(unit_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
apply_alignment:
|
|
if (alignment_vector!=NULL &&
|
|
!TEST_FLAG(
|
|
unit_definition->unit.flags,
|
|
_unit_does_not_reorient_during_pings_bit) &&
|
|
unit->object.type==_object_type_biped &&
|
|
unit->object.parent_object_index==NONE &&
|
|
(hard_ping || killed))
|
|
{
|
|
real_vector2d transformed_alignment;
|
|
|
|
switch (damage_direction)
|
|
{
|
|
case _unit_damage_direction_back:
|
|
transformed_alignment = *alignment_vector;
|
|
break;
|
|
|
|
case _unit_damage_direction_front:
|
|
transformed_alignment.i = -alignment_vector->i;
|
|
transformed_alignment.j = -alignment_vector->j;
|
|
break;
|
|
|
|
case _unit_damage_direction_left:
|
|
transformed_alignment.i = -alignment_vector->j;
|
|
transformed_alignment.j = alignment_vector->i;
|
|
break;
|
|
|
|
case _unit_damage_direction_right:
|
|
{
|
|
real negated_i;
|
|
|
|
negated_i = -alignment_vector->i;
|
|
transformed_alignment.i = alignment_vector->j;
|
|
transformed_alignment.j = negated_i;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
display_assert(
|
|
NULL,
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
4562,
|
|
TRUE);
|
|
system_exit(-1);
|
|
break;
|
|
}
|
|
|
|
unit_align_facing(unit_index, &transformed_alignment);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_damage_aftermath(
|
|
long unit_index,
|
|
struct damage_data *damage_data,
|
|
unsigned long damage_flags,
|
|
real shield_damage,
|
|
real body_damage,
|
|
real body_damage_multiplier,
|
|
short body_part)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct unit_definition *unit_definition;
|
|
struct damage_definition *damage_definition;
|
|
real total_damage;
|
|
boolean lethal;
|
|
boolean feigned;
|
|
boolean instantaneous;
|
|
|
|
unit = unit_get(unit_index);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
damage_definition = &damage_effect_definition_get(
|
|
damage_data->definition_index)->damage;
|
|
total_damage = shield_damage + body_damage;
|
|
lethal = TEST_FLAG(
|
|
damage_flags,
|
|
_object_being_damaged_body_depleted_bit);
|
|
feigned = FALSE;
|
|
|
|
(void)body_damage_multiplier;
|
|
|
|
if (debug_damage_taken && unit->unit.player_index != NONE)
|
|
{
|
|
char const *owner_name = "<unknown>";
|
|
char const *damage_name = "<unknown>";
|
|
|
|
if (damage_data->owner_object_index != NONE)
|
|
{
|
|
struct object_datum *owner_object =
|
|
object_get(damage_data->owner_object_index);
|
|
struct object_definition *owner_definition =
|
|
object_definition_get(owner_object->definition_index);
|
|
char const *separator = strrchr(
|
|
owner_definition->object.model.name,
|
|
'\\');
|
|
|
|
if (separator)
|
|
{
|
|
owner_name = separator + 1;
|
|
}
|
|
}
|
|
|
|
if (damage_data->definition_index != NONE)
|
|
{
|
|
char const *tag_name =
|
|
tag_get_name(damage_data->definition_index);
|
|
char const *separator = strrchr(
|
|
tag_name,
|
|
'\\');
|
|
|
|
if (separator)
|
|
{
|
|
damage_name = separator + 1;
|
|
}
|
|
}
|
|
|
|
console_printf(
|
|
FALSE,
|
|
"p%d: body %.2f shld %.2f from %s %s",
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(unit->unit.player_index),
|
|
body_damage,
|
|
shield_damage,
|
|
owner_name,
|
|
damage_name);
|
|
}
|
|
|
|
{
|
|
real vitality =
|
|
unit->object.recent_shield_damage +
|
|
unit->object.recent_body_damage;
|
|
|
|
if (vitality > 0.f)
|
|
{
|
|
unit->unit.last_damage_category = damage_definition->category;
|
|
unit->unit.delayed_damage_timer = 45;
|
|
if (vitality < unit->unit.delayed_damage_peak)
|
|
{
|
|
vitality = unit->unit.delayed_damage_peak;
|
|
}
|
|
unit->unit.delayed_damage_peak = vitality;
|
|
|
|
if (damage_data->owner_object_index != NONE)
|
|
{
|
|
unit->unit.delayed_damage_attacker_object_index =
|
|
damage_data->owner_object_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_active_camouflaged_bit))
|
|
{
|
|
unit->unit.active_camouflage -=
|
|
damage_definition->active_camouflage_damage;
|
|
|
|
if (unit->unit.active_camouflage < 0.f)
|
|
{
|
|
unit->unit.active_camouflage = 0.f;
|
|
}
|
|
}
|
|
|
|
instantaneous =
|
|
lethal &&
|
|
damage_definition->instantaneous_acceleration >= 2.f;
|
|
|
|
if (!lethal &&
|
|
TEST_FLAG(unit->unit.flags, _unit_feign_death_allowed_bit) &&
|
|
unit_definition->unit.feign_death_threshold > 0.f &&
|
|
unit_definition->unit.feign_death_time > 0.f &&
|
|
unit->object.body_vitality > 0.f &&
|
|
unit->object.recent_body_damage >
|
|
unit_definition->unit.feign_death_threshold)
|
|
{
|
|
real feign_death_ticks =
|
|
(real_random_range(0.f, 1.f) +
|
|
unit_definition->unit.feign_death_time) * TICKS_PER_SECOND;
|
|
|
|
SET_FLAG(unit->object.damage_flags, _object_dead_bit, TRUE);
|
|
feigned = TRUE;
|
|
unit->unit.feign_death_timer = (short)MAX(
|
|
1.f,
|
|
feign_death_ticks);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x1284,
|
|
unit->unit.feign_death_timer > 0);
|
|
}
|
|
|
|
if (!TEST_FLAG(
|
|
damage_data->flags,
|
|
_damage_silent_bit) &&
|
|
(lethal ||
|
|
feigned ||
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit)) &&
|
|
!TEST_FLAG(unit->unit.flags, _unit_impervious_bit) &&
|
|
!TEST_FLAG(
|
|
damage_definition->flags,
|
|
_damage_definition_does_not_ping_units_bit))
|
|
{
|
|
real_vector2d damage_direction;
|
|
real_vector2d unit_forward;
|
|
boolean resists_pings;
|
|
boolean animation_flag;
|
|
boolean direction_valid;
|
|
real angle;
|
|
|
|
damage_direction.i = damage_data->direction.i;
|
|
damage_direction.j = damage_data->direction.j;
|
|
unit_forward.i = unit->object.forward.i;
|
|
unit_forward.j = unit->object.forward.j;
|
|
resists_pings = FALSE;
|
|
animation_flag = FALSE;
|
|
direction_valid = FALSE;
|
|
angle = 0.f;
|
|
|
|
if (normalize2d(&damage_direction) > 0.f &&
|
|
normalize2d(&unit_forward) > 0.f)
|
|
{
|
|
angle = signed_angle_between_vectors2d(
|
|
&damage_direction,
|
|
&unit_forward);
|
|
direction_valid = TRUE;
|
|
}
|
|
|
|
if (TEST_FLAG(
|
|
unit_definition->unit.flags,
|
|
_unit_resists_pings_bit) &&
|
|
!TEST_FLAG(
|
|
damage_definition->flags,
|
|
_damage_definition_pings_resistant_units_bit))
|
|
{
|
|
resists_pings = TRUE;
|
|
}
|
|
|
|
if (unit->unit.flaming_death_delay > 0)
|
|
{
|
|
resists_pings = TRUE;
|
|
}
|
|
|
|
if (damage_flags & UNIT_DAMAGE_AFTERMATH_ANIMATION_FLAGS_MASK)
|
|
{
|
|
animation_flag = TRUE;
|
|
}
|
|
|
|
unit_ping_animation(
|
|
unit_index,
|
|
lethal,
|
|
feigned,
|
|
instantaneous,
|
|
resists_pings,
|
|
animation_flag,
|
|
angle,
|
|
body_part,
|
|
direction_valid ? &damage_direction : NULL);
|
|
}
|
|
|
|
{
|
|
long owner_player_index = damage_data->owner_player_index;
|
|
long player_index = unit->unit.player_index;
|
|
|
|
if (owner_player_index != NONE && player_index != NONE)
|
|
{
|
|
game_engine_player_damaged_player(
|
|
owner_player_index,
|
|
player_index,
|
|
TEST_FLAG(
|
|
damage_flags,
|
|
_object_being_damaged_by_friendly_bit));
|
|
}
|
|
}
|
|
|
|
if (damage_data->owner_player_index != NONE ||
|
|
damage_data->owner_object_index != NONE)
|
|
{
|
|
unit_record_damage(
|
|
unit_index,
|
|
total_damage,
|
|
damage_definition->category,
|
|
lethal,
|
|
damage_data->owner_player_index,
|
|
damage_data->owner_team_index,
|
|
damage_data->owner_object_index);
|
|
}
|
|
|
|
if (!TEST_FLAG(
|
|
damage_data->flags,
|
|
_damage_silent_bit) &&
|
|
(TEST_FLAG(
|
|
damage_flags,
|
|
_object_being_damaged_body_depleted_bit) ||
|
|
body_damage > 0.f ||
|
|
shield_damage > 0.f))
|
|
{
|
|
unit_make_damage_sound(
|
|
unit_index,
|
|
damage_data,
|
|
lethal | feigned,
|
|
TEST_FLAG(
|
|
damage_flags,
|
|
_object_being_damaged_killed_instantly_bit),
|
|
body_damage,
|
|
shield_damage);
|
|
}
|
|
|
|
if (body_damage > 0.f || shield_damage > 0.f)
|
|
{
|
|
unit_unzoom(unit_index);
|
|
}
|
|
|
|
if (unit->object.type == _object_type_biped)
|
|
{
|
|
if (lethal)
|
|
{
|
|
ai_handle_death(
|
|
unit_index,
|
|
damage_data->owner_object_index,
|
|
damage_definition->category);
|
|
}
|
|
else if (!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
ai_handle_damage(
|
|
unit_index,
|
|
damage_data->owner_object_index,
|
|
damage_definition->category,
|
|
total_damage,
|
|
&damage_data->direction,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
if (unit->unit.player_index != NONE &&
|
|
damage_definition->stun > 0.f &&
|
|
(game_engine_running() || stun_enable))
|
|
{
|
|
struct game_globals_player_information *player_information =
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->player_information,
|
|
0,
|
|
struct game_globals_player_information);
|
|
real stun_increment =
|
|
damage_definition->stun * damage_data->scale;
|
|
real maximum_stun =
|
|
damage_definition->maximum_stun * damage_data->scale;
|
|
short stun_ticks;
|
|
short minimum_stun_ticks;
|
|
short maximum_stun_ticks;
|
|
|
|
if (stun_increment < 0.f)
|
|
{
|
|
stun_increment = 0.f;
|
|
}
|
|
if (maximum_stun < 0.f)
|
|
{
|
|
maximum_stun = 0.f;
|
|
}
|
|
else if (maximum_stun >= 1.f)
|
|
{
|
|
maximum_stun = 1.f;
|
|
}
|
|
|
|
if (unit->unit.body_stun < maximum_stun)
|
|
{
|
|
unit->unit.body_stun += stun_increment;
|
|
if (unit->unit.body_stun > maximum_stun)
|
|
{
|
|
unit->unit.body_stun = maximum_stun;
|
|
}
|
|
}
|
|
|
|
stun_ticks = (short)(
|
|
damage_definition->stun_time * TICKS_PER_SECOND);
|
|
minimum_stun_ticks = (short)(
|
|
player_information->minimum_stun_time * TICKS_PER_SECOND);
|
|
maximum_stun_ticks = (short)(
|
|
player_information->maximum_stun_time * TICKS_PER_SECOND);
|
|
|
|
if (unit->unit.body_stun_ticks < minimum_stun_ticks)
|
|
{
|
|
unit->unit.body_stun_ticks = minimum_stun_ticks;
|
|
}
|
|
unit->unit.body_stun_ticks += stun_ticks;
|
|
if (unit->unit.body_stun_ticks > maximum_stun_ticks)
|
|
{
|
|
unit->unit.body_stun_ticks = maximum_stun_ticks;
|
|
}
|
|
}
|
|
|
|
if (lethal || feigned)
|
|
{
|
|
unit_died(unit_index, feigned);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_flame_to_death(
|
|
long unit_index)
|
|
{
|
|
struct game_globals_falling_damage *falling_damage;
|
|
struct unit_datum *unit;
|
|
|
|
unit = unit_get(unit_index);
|
|
falling_damage = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->falling_damage,
|
|
0,
|
|
struct game_globals_falling_damage);
|
|
|
|
{
|
|
struct unit_datum *running_unit = unit_get(unit_index);
|
|
|
|
running_unit->unit.flags &= ~FLAG(_unit_running_blindly_bit);
|
|
}
|
|
unit->unit.flags &= ~FLAG(_unit_ignore_hard_pings_bit);
|
|
unit->object.damage_flags &= ~FLAG(_object_cannot_take_damage_bit);
|
|
|
|
if (falling_damage &&
|
|
falling_damage->flaming_death_damage_effect.index != NONE)
|
|
{
|
|
struct object_datum *attacker = object_try_and_get(
|
|
unit->unit.flaming_death_attacker_object_index);
|
|
struct damage_data damage_data;
|
|
|
|
damage_data_new(
|
|
&damage_data,
|
|
falling_damage->flaming_death_damage_effect.index);
|
|
|
|
if (attacker)
|
|
{
|
|
damage_data.owner_player_index = attacker->object.owner_player_index;
|
|
if (attacker->object.owner_object_index == NONE)
|
|
{
|
|
damage_data.owner_object_index =
|
|
unit->unit.flaming_death_attacker_object_index;
|
|
}
|
|
else
|
|
{
|
|
damage_data.owner_object_index =
|
|
attacker->object.owner_object_index;
|
|
}
|
|
damage_data.owner_team_index = attacker->object.owner_team_index;
|
|
}
|
|
|
|
object_cause_damage(
|
|
&damage_data,
|
|
unit_index,
|
|
NONE,
|
|
NONE,
|
|
NONE,
|
|
NULL);
|
|
}
|
|
|
|
if (!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"WARNING: %s tried to die from flaming to death but couldn't",
|
|
tag_name_strip_path(tag_get_name(unit->definition_index)));
|
|
SET_FLAG(unit->object.damage_flags, _object_die_act_of_god_bit, TRUE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_get_head_position(
|
|
long unit_index,
|
|
union real_point3d *head_position)
|
|
{
|
|
struct object_marker head_marker;
|
|
|
|
object_get_marker_by_name(unit_index, "head", &head_marker, 1);
|
|
*head_position = head_marker.matrix.position;
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_get_camera_position(
|
|
long unit_index,
|
|
real_point3d *camera_position)
|
|
{
|
|
struct object_marker marker;
|
|
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (unit->object.parent_object_index!=NONE || TEST_FLAG(unit->object.damage_flags, _object_dead_bit) || unit->object.type!=_object_type_biped)
|
|
{
|
|
if (unit->object.parent_object_index==NONE)
|
|
{
|
|
if (unit->unit.gunner_object_index==NONE)
|
|
{
|
|
object_get_marker_by_name(unit_index, "head", &marker, 1);
|
|
*camera_position = marker.matrix.position;
|
|
}
|
|
else
|
|
{
|
|
struct unit_datum *gunner_unit = unit_get(unit->unit.gunner_object_index);
|
|
struct unit_seat *parent_seat = TAG_BLOCK_GET_ELEMENT(&unit_definition->unit.seats, gunner_unit->unit.parent_seat_index, struct unit_seat);
|
|
|
|
object_get_marker_by_name(unit_index, parent_seat->marker_name, &marker, 1);
|
|
*camera_position = marker.matrix.position;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct object_datum *object = object_get(unit->object.parent_object_index);
|
|
|
|
*camera_position = object->object.position;
|
|
|
|
if (TEST_FLAG(_object_mask_unit, object->object.type) && unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
struct unit_definition *parent_unit_definition = unit_definition_get(object->definition_index);
|
|
struct unit_seat *parent_seat = TAG_BLOCK_GET_ELEMENT(&parent_unit_definition->unit.seats, unit->unit.parent_seat_index, struct unit_seat);
|
|
|
|
if (object->object.type!=_object_type_vehicle || parent_seat->camera.marker_name[0])
|
|
{
|
|
object_get_marker_by_name(unit->object.parent_object_index, parent_seat->camera.marker_name, &marker, 1);
|
|
*camera_position = marker.matrix.position;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
biped_get_sight_position(unit_index, 0.f, NULL, NULL, NULL, camera_position);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_estimate_position(
|
|
long unit_index,
|
|
short estimate_mode,
|
|
real_point3d const *body_position,
|
|
real_vector3d *desired_facing,
|
|
real_vector3d *desired_gun_offset,
|
|
real_point3d *estimated_position)
|
|
{
|
|
struct unit_datum *unit;
|
|
real_point3d reference_position;
|
|
|
|
unit = unit_get(unit_index);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
5297,
|
|
body_position && estimated_position);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
5298,
|
|
(estimate_mode >= 0) &&
|
|
(estimate_mode < NUMBER_OF_UNIT_ESTIMATE_POSITION_MODES));
|
|
|
|
if (unit->object.parent_object_index == NONE &&
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
if (unit->object.type == _object_type_biped)
|
|
{
|
|
biped_get_sight_position(
|
|
unit_index,
|
|
estimate_mode,
|
|
body_position,
|
|
desired_facing,
|
|
desired_gun_offset,
|
|
estimated_position);
|
|
|
|
return;
|
|
}
|
|
}
|
|
else if (unit->object.type == _object_type_biped &&
|
|
unit->object.parent_object_index != NONE)
|
|
{
|
|
struct object_datum *parent_object =
|
|
object_get(unit->object.parent_object_index);
|
|
|
|
if (parent_object->object.type == _object_type_vehicle &&
|
|
vehicle_find_pathfinding_surface_index(
|
|
unit->object.parent_object_index,
|
|
&reference_position) != NONE)
|
|
{
|
|
goto apply_delta;
|
|
}
|
|
}
|
|
|
|
object_get_origin(unit_index, &reference_position);
|
|
|
|
apply_delta:
|
|
unit_get_camera_position(unit_index, estimated_position);
|
|
add_vectors3d(
|
|
vector_from_points3d(
|
|
&reference_position,
|
|
body_position,
|
|
(real_vector3d *)&reference_position),
|
|
(real_vector3d *)estimated_position,
|
|
(real_vector3d *)estimated_position);
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_debug_ninja_rope(
|
|
long unit_index)
|
|
{
|
|
struct collision_result collision;
|
|
real_point3d origin;
|
|
real_vector3d direction;
|
|
struct unit_datum *unit;
|
|
|
|
unit = unit_get(unit_index);
|
|
unit_get_camera_position(unit_index, &origin);
|
|
scale_vector3d(&unit->unit.aiming_vector, 25.f, &direction);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
6648,
|
|
global_current_collision_user_depth <
|
|
MAXIMUM_COLLISION_USER_STACK_DEPTH);
|
|
global_current_collision_users[global_current_collision_user_depth++] = 21;
|
|
|
|
if (collision_test_vector(
|
|
FLAG(_collision_test_back_facing_surfaces_bit) |
|
|
FLAG(_collision_test_structure_bit),
|
|
&origin,
|
|
&direction,
|
|
unit_index,
|
|
&collision))
|
|
{
|
|
if (collision.plane.n.k > 0.95f)
|
|
{
|
|
collision.point.z += 0.25f;
|
|
object_translate(unit_index, &collision.point, NULL);
|
|
}
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
6657,
|
|
global_current_collision_user_depth > 1);
|
|
--global_current_collision_user_depth;
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_clip_to_aiming_bounds(
|
|
long unit_index,
|
|
real_vector3d *vector,
|
|
boolean use_aiming_screen)
|
|
{
|
|
|
|
boolean aiming;
|
|
real_rectangle2d *bounds;
|
|
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
boolean result = FALSE;
|
|
|
|
if (use_aiming_screen)
|
|
{
|
|
aiming = unit->unit.animation.aiming_with_euler_screen;
|
|
bounds = &unit->unit.animation.aiming_screen_bounds;
|
|
}
|
|
else
|
|
{
|
|
aiming = unit->unit.animation.looking_with_euler_screen;
|
|
bounds = &unit->unit.animation.looking_screen_bounds;
|
|
}
|
|
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 5608, vector);
|
|
|
|
if (aiming)
|
|
{
|
|
real_matrix4x3 matrix;
|
|
real_euler_angles2d relative_aiming_angles;
|
|
real_vector3d relative_vector;
|
|
|
|
matrix.scale = 1.f;
|
|
object_get_orientation(unit_index, &matrix.forward, &matrix.up);
|
|
cross_product3d(&matrix.up, &matrix.forward, &matrix.left);
|
|
matrix.position = *global_origin3d;
|
|
matrix4x3_inverse_transform_normal(&matrix, vector, &relative_vector);
|
|
|
|
match_assert_valid_real_vector3d("c:\\halo\\SOURCE\\units\\units.c", 5626, &relative_vector);
|
|
|
|
euler_angles2d_from_vector3d(&relative_aiming_angles, &relative_vector);
|
|
|
|
match_assert_valid_real("c:\\halo\\SOURCE\\units\\units.c", 5629, relative_aiming_angles.pitch);
|
|
match_assert_valid_real("c:\\halo\\SOURCE\\units\\units.c", 5630, relative_aiming_angles.yaw);
|
|
|
|
if (relative_aiming_angles.yaw<bounds->x0)
|
|
{
|
|
relative_aiming_angles.yaw = bounds->x0;
|
|
result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (relative_aiming_angles.yaw>bounds->x1)
|
|
{
|
|
relative_aiming_angles.yaw = bounds->x1;
|
|
result = TRUE;
|
|
}
|
|
}
|
|
|
|
if (relative_aiming_angles.pitch<bounds->y0)
|
|
{
|
|
result = TRUE;
|
|
relative_aiming_angles.pitch = bounds->y0;
|
|
}
|
|
else
|
|
{
|
|
if (relative_aiming_angles.pitch>bounds->y1)
|
|
{
|
|
result = TRUE;
|
|
relative_aiming_angles.pitch = bounds->y1;
|
|
}
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
match_assert_valid_real("c:\\halo\\SOURCE\\units\\units.c", 5661, relative_aiming_angles.pitch);
|
|
match_assert_valid_real("c:\\halo\\SOURCE\\units\\units.c", 5662, relative_aiming_angles.yaw);
|
|
vector3d_from_euler_angles2d(&relative_vector, &relative_aiming_angles);
|
|
|
|
match_assert_valid_real_vector3d("c:\\halo\\SOURCE\\units\\units.c", 5665, &relative_vector);
|
|
matrix4x3_transform_normal(&matrix, &relative_vector, vector);
|
|
match_assert_valid_real_vector3d("c:\\halo\\SOURCE\\units\\units.c", 5667, vector);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
long unit_inventory_get_weapon(
|
|
long unit_index,
|
|
short index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
return unit_get_weapon(unit, index);
|
|
}
|
|
|
|
static void unit_ready_desired_weapon(
|
|
long unit_index,
|
|
boolean immediate)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
long desired_weapon_index = unit_get_desired_weapon_index(unit_index);
|
|
long current_weapon_index = unit_get_current_weapon_index(unit_index);
|
|
|
|
if (current_weapon_index!=NONE && weapon_put_away(current_weapon_index, immediate))
|
|
{
|
|
object_detach(current_weapon_index);
|
|
object_disconnect_from_map(current_weapon_index);
|
|
object_activate(current_weapon_index);
|
|
object_set_visibility(current_weapon_index, FALSE);
|
|
item_in_unit_inventory(current_weapon_index, unit_index);
|
|
unit->unit.current_weapon_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.current_weapon_index==NONE)
|
|
{
|
|
if (desired_weapon_index!=NONE)
|
|
{
|
|
struct animation_graph *animation_graph;
|
|
struct animation_graph_unit_seat *unit_seat;
|
|
struct animation_graph_weapon_class *weapon_class;
|
|
|
|
unit_set_or_test_seat_and_weapon_label(unit_index, unit_get_seat_label(unit_index), weapon_get_label(desired_weapon_index), TRUE);
|
|
|
|
animation_graph = animation_graph_definition_get(unit_definition->object.animation_graph.index);
|
|
unit_seat = TAG_BLOCK_GET_ELEMENT(&animation_graph->unit_seats, unit->unit.animation.seat_index, struct animation_graph_unit_seat);
|
|
weapon_class = TAG_BLOCK_GET_ELEMENT(&unit_seat->weapon_classes, unit->unit.animation.weapon_index, struct animation_graph_weapon_class);
|
|
|
|
object_reconnect_to_map(desired_weapon_index, NULL);
|
|
object_set_visibility(desired_weapon_index, TRUE);
|
|
object_attach_to_marker(unit_index, weapon_class->hand_marker_name, desired_weapon_index, weapon_class->grip_marker_name);
|
|
unit->unit.current_weapon_index = unit->unit.desired_weapon_index;
|
|
|
|
if (unit->unit.desired_weapon_index!=NONE)
|
|
{
|
|
unit->unit.weapon_last_used_at_game_time[unit->unit.current_weapon_index] = game_time_get();
|
|
}
|
|
|
|
weapon_ready(desired_weapon_index);
|
|
}
|
|
else
|
|
{
|
|
unit_set_or_test_seat_and_weapon_label(unit_index, unit_get_seat_label(unit_index), base_weapon_label_get(_unit_base_weapon_none), TRUE);
|
|
unit->unit.current_weapon_index = NONE;
|
|
}
|
|
}
|
|
|
|
unit_unzoom(unit_index);
|
|
|
|
return;
|
|
}
|
|
|
|
short unit_inventory_next_weapon(
|
|
long unit_index,
|
|
short current_index,
|
|
short delta)
|
|
{
|
|
return unit_weapon_next_index(
|
|
unit_index,
|
|
current_index,
|
|
delta);
|
|
}
|
|
|
|
boolean unit_drop_current_weapon(
|
|
long unit_index,
|
|
boolean immediate)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
long current_weapon_index = unit_get_current_weapon_index(unit_index);
|
|
short next_index = unit_weapon_next_index(unit_index, unit->unit.current_weapon_index, 1);
|
|
|
|
boolean result = FALSE;
|
|
|
|
if (current_weapon_index != NONE &&
|
|
(next_index != unit->unit.current_weapon_index || immediate) &&
|
|
!TEST_FLAG(object_get(current_weapon_index)->object.flags, _object_invisible_bit) &&
|
|
weapon_put_away(current_weapon_index, immediate))
|
|
{
|
|
first_person_weapon_message_from_unit(unit_index, 13);
|
|
unit_drop_item(unit_index, current_weapon_index);
|
|
unit->unit.weapon_object_indices[unit->unit.current_weapon_index] = NONE;
|
|
unit->unit.current_weapon_index = NONE;
|
|
unit->unit.desired_weapon_index = unit_weapon_next_index(unit_index, NONE, 0);
|
|
|
|
if (!weapon_can_be_fired(current_weapon_index))
|
|
{
|
|
object_delete(current_weapon_index);
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
char const *base_seat_labels[NUMBER_OF_UNIT_BASE_SEATS] = {"asleep", "alert", "stand", "crouch", "flee", "flaming"};
|
|
|
|
static char const *base_seat_label_get(
|
|
short base_seat_index)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 8214, base_seat_index>=0 && base_seat_index<NUMBER_OF_UNIT_BASE_SEATS);
|
|
return base_seat_labels[base_seat_index];
|
|
}
|
|
|
|
static short seat_label_to_base_seat_index(
|
|
char const *seat_label)
|
|
{
|
|
short seat_index;
|
|
short result = NONE;
|
|
|
|
for (seat_index = 0; seat_index<NUMBER_OF_UNIT_BASE_SEATS; ++seat_index)
|
|
{
|
|
if (!_stricmp(seat_label, base_seat_labels[seat_index]))
|
|
{
|
|
result = seat_index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static char const *base_weapon_label_get(
|
|
short base_weapon_index)
|
|
{
|
|
char const *base_weapon_labels[NUMBER_OF_UNIT_BASE_WEAPONS] = {"unarmed"};
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 8266, base_weapon_index>=0 && base_weapon_index<NUMBER_OF_UNIT_BASE_WEAPONS);
|
|
|
|
return base_weapon_labels[base_weapon_index];
|
|
}
|
|
|
|
static void unit_refresh_illumination(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_datum *parent_unit = unit_try_and_get(unit->object.parent_object_index);
|
|
|
|
if (parent_unit==NULL)
|
|
{
|
|
real_rgb_color color;
|
|
lights_illumination_at_point(&unit->object.position, &unit->object.location, &color);
|
|
unit->unit.ambient_illumination = real_rgb_color_brightness(&color);
|
|
unit->unit.self_illumination = object_get_self_illumination(unit_index);
|
|
}
|
|
else
|
|
{
|
|
unit->unit.ambient_illumination = parent_unit->unit.ambient_illumination;
|
|
unit->unit.self_illumination = parent_unit->unit.self_illumination;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static short unit_weapon_next_index(
|
|
long unit_index,
|
|
short current_index,
|
|
short delta)
|
|
{
|
|
short inventory_index;
|
|
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short selected_weapon_index = NONE;
|
|
|
|
if (current_index==NONE)
|
|
{
|
|
current_index = 0;
|
|
}
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 7744, current_index>=0 && current_index<MAXIMUM_WEAPONS_PER_UNIT);
|
|
|
|
inventory_index = current_index;
|
|
|
|
do
|
|
{
|
|
long current_weapon_index = unit->unit.weapon_object_indices[inventory_index];
|
|
|
|
if (current_weapon_index != NONE && unit_can_use_weapon(unit_index, current_weapon_index))
|
|
{
|
|
if (delta ||
|
|
selected_weapon_index == NONE ||
|
|
unit->unit.weapon_last_used_at_game_time[selected_weapon_index] <
|
|
unit->unit.weapon_last_used_at_game_time[inventory_index])
|
|
{
|
|
selected_weapon_index = inventory_index;
|
|
}
|
|
|
|
if (weapon_must_be_readied(unit->unit.weapon_object_indices[inventory_index]) ||
|
|
inventory_index != current_index)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (delta<0)
|
|
{
|
|
inventory_index =
|
|
inventory_index == 0 ?
|
|
MAXIMUM_WEAPONS_PER_UNIT-1 :
|
|
inventory_index-1;
|
|
}
|
|
else
|
|
{
|
|
inventory_index = inventory_index == (MAXIMUM_WEAPONS_PER_UNIT-1) ? 0 : inventory_index+1;
|
|
}
|
|
|
|
}
|
|
while (inventory_index != current_index);
|
|
|
|
return selected_weapon_index;
|
|
}
|
|
|
|
static void unit_throw_grenade_move_to_hand(
|
|
long unit_index)
|
|
{
|
|
long object_index;
|
|
|
|
struct object_placement_data placement_data;
|
|
struct object_marker marker;
|
|
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
struct game_globals_grenade *grenades = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->grenades,
|
|
unit->unit.current_grenade_index,
|
|
struct game_globals_grenade);
|
|
|
|
if ((
|
|
unit->unit.player_index==NONE ||
|
|
!cheat.infinite_ammo && !game_engine_infinite_grenades(unit->unit.player_index)) &&
|
|
(unit->unit.actor_index==NONE ||
|
|
!actor_has_unlimited_grenades(unit->unit.actor_index)))
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 7966, unit->unit.current_grenade_index>=0 && unit->unit.current_grenade_index<NUMBER_OF_UNIT_GRENADE_TYPES);
|
|
/* port: a distributed client's grenade counts are the host's (what
|
|
every unit carries, network_objects.c), which can arrive between a
|
|
throw starting and the grenade reaching the hand, the throw already
|
|
counted there: the host's count stands */
|
|
if (network_game_distributed_client())
|
|
{
|
|
if (unit->unit.grenade_counts[unit->unit.current_grenade_index] > 0)
|
|
--unit->unit.grenade_counts[unit->unit.current_grenade_index];
|
|
}
|
|
else
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 7967, unit->unit.grenade_counts[unit->unit.current_grenade_index]>0);
|
|
--unit->unit.grenade_counts[unit->unit.current_grenade_index];
|
|
}
|
|
}
|
|
|
|
object_get_marker_by_name(unit_index, "left hand", &marker, 1);
|
|
object_placement_data_new(&placement_data, grenades->projectile.index, unit_index);
|
|
SET_FLAG(placement_data.flags, _new_object_never_automatically_delete_bit, TRUE);
|
|
placement_data.forward= unit_get(unit_index)->unit.aiming_vector;
|
|
normalize3d(perpendicular3d(&placement_data.forward, &placement_data.up));
|
|
|
|
placement_data.position = marker.matrix.position;
|
|
object_index = object_new(&placement_data);
|
|
|
|
if (object_index!=NONE)
|
|
{
|
|
object_attach_to_node(unit_index, object_index, marker.node_index);
|
|
unit->unit.grenade_object_index = object_index;
|
|
unit->unit.grenade_throw_state = _unit_grenade_throw_in_hand;
|
|
}
|
|
else
|
|
{
|
|
unit->unit.grenade_throw_state = _unit_grenade_throw_ending;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean unit_animation_overlay_action_loops(
|
|
struct unit_animation *animation)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (animation->state>=_unit_state_turn_right &&
|
|
animation->state<=_unit_state_move_front)
|
|
{
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_animation_state_interruptable(
|
|
struct unit_animation *animation,
|
|
short desired_state)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
switch (animation->state)
|
|
{
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
if (desired_state!=_unit_state_hard_ping)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
{
|
|
long state = desired_state;
|
|
|
|
if (state<_unit_state_dying_airborne ||
|
|
state>_unit_state_dying)
|
|
{
|
|
goto state_not_allowed;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case _unit_state_turn_left:
|
|
case _unit_state_turn_right:
|
|
case _unit_state_opening:
|
|
case _unit_state_closing:
|
|
if (desired_state!=_unit_state_idle)
|
|
{
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_user_animation:
|
|
state_not_allowed:
|
|
result = FALSE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_animation_state_loops(
|
|
struct unit_animation *animation)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
switch (animation->state)
|
|
{
|
|
case _unit_state_gesture:
|
|
case _unit_state_turn_left:
|
|
case _unit_state_turn_right:
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_user_animation:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
result = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_animation_weapon_ik(
|
|
struct unit_animation *animation)
|
|
{
|
|
boolean result = animation->soft_ping_animation.index==NONE;
|
|
|
|
if (animation->action)
|
|
{
|
|
result = FALSE;
|
|
}
|
|
|
|
switch (animation->state)
|
|
{
|
|
case _unit_state_flying_front:
|
|
case _unit_state_flying_back:
|
|
case _unit_state_flying_left:
|
|
case _unit_state_flying_right:
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_user_animation:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_airborne:
|
|
case _unit_state_leap_melee:
|
|
result = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_animation_vehicle_ik(
|
|
struct unit_animation *animation)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
switch (animation->state)
|
|
{
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
result = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_animation_busy(
|
|
struct unit_animation *animation)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
switch (animation->state)
|
|
{
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
result = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_set_or_test_seat_and_weapon_label(
|
|
long object_index,
|
|
char const *seat_label,
|
|
char const *weapon_label,
|
|
boolean change_flag)
|
|
{
|
|
short seat_index;
|
|
|
|
struct unit_datum *unit = unit_get(object_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(unit_definition->object.animation_graph.index);
|
|
boolean result = FALSE;
|
|
|
|
for (seat_index = 0; seat_index<animation_graph->unit_seats.count; ++seat_index)
|
|
{
|
|
struct animation_graph_unit_seat *unit_seat = TAG_BLOCK_GET_ELEMENT(&animation_graph->unit_seats, seat_index, struct animation_graph_unit_seat);
|
|
|
|
if (!seat_label || !_stricmp(seat_label, unit_seat->label))
|
|
{
|
|
short weapon_class_index;
|
|
|
|
for (weapon_class_index = 0; weapon_class_index<unit_seat->weapon_classes.count; ++weapon_class_index)
|
|
{
|
|
short weapon_type_index;
|
|
|
|
struct animation_graph_weapon_class *weapon_class = TAG_BLOCK_GET_ELEMENT(&unit_seat->weapon_classes, weapon_class_index, struct animation_graph_weapon_class);
|
|
|
|
for (weapon_type_index = 0; weapon_type_index<weapon_class->weapon_types.count; ++weapon_type_index)
|
|
{
|
|
struct animation_graph_weapon_type *weapon_type = TAG_BLOCK_GET_ELEMENT(&weapon_class->weapon_types, weapon_type_index, struct animation_graph_weapon_type);
|
|
|
|
if (!weapon_label ||
|
|
!strcmp(weapon_label, "unarmed") &&
|
|
weapon_type->label[0]=='\0'||
|
|
!_stricmp(weapon_label, weapon_type->label))
|
|
{
|
|
if (change_flag)
|
|
{
|
|
long anim_2 =
|
|
unit_seat->animations.count <= 2 ?
|
|
NONE :
|
|
animation_graph_animation_index_get(&unit_seat->animations)[2].animation_index;
|
|
boolean showing_acceleration;
|
|
|
|
if (anim_2==NONE)
|
|
{
|
|
long anim_3 =
|
|
unit_seat->animations.count <= 3 ?
|
|
NONE :
|
|
animation_graph_animation_index_get(&unit_seat->animations)[3].animation_index;
|
|
|
|
if (anim_3==NONE)
|
|
{
|
|
long anim_4 =
|
|
unit_seat->animations.count <= 4 ?
|
|
NONE :
|
|
animation_graph_animation_index_get(&unit_seat->animations)[4].animation_index;
|
|
|
|
if (anim_4==NONE)
|
|
{
|
|
showing_acceleration = FALSE;
|
|
goto acceleration_determined;
|
|
}
|
|
}
|
|
}
|
|
showing_acceleration = TRUE;
|
|
acceleration_determined:
|
|
|
|
if (unit->unit.animation.state!=_unit_state_user_animation)
|
|
{
|
|
unit->unit.animation.state = NONE;
|
|
}
|
|
|
|
unit->unit.animation.seat_index = seat_index;
|
|
unit->unit.animation.base_seat_index = seat_label_to_base_seat_index(seat_label);
|
|
unit->unit.animation.weapon_index = weapon_class_index;
|
|
unit->unit.animation.weapon_type_index = weapon_type_index;
|
|
|
|
SET_FLAG(unit->unit.animation.flags, _unit_animation_showing_acceleration_bit, showing_acceleration);
|
|
}
|
|
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_euler_axis_doplan(
|
|
struct unit_acceleration_plan *plan,
|
|
real delta_time,
|
|
real position,
|
|
real *new_position,
|
|
real velocity,
|
|
real *new_velocity)
|
|
{
|
|
boolean result = plan->at_rest;
|
|
real current_position = position;
|
|
real current_velocity = velocity;
|
|
real time;
|
|
real remaining_time;
|
|
|
|
if (result)
|
|
{
|
|
goto store_result;
|
|
}
|
|
remaining_time = delta_time;
|
|
if (!(remaining_time > 0.f))
|
|
{
|
|
goto store_result;
|
|
}
|
|
|
|
if (plan->acceleration_time>0.f)
|
|
{
|
|
time = MIN(remaining_time, plan->acceleration_time);
|
|
current_position +=
|
|
(time*plan->acceleration*0.5f + current_velocity)*time;
|
|
current_velocity += time*plan->acceleration;
|
|
remaining_time -= time;
|
|
}
|
|
|
|
if (!(remaining_time > 0.f))
|
|
{
|
|
goto store_result;
|
|
}
|
|
|
|
if (plan->coast_time>0.f)
|
|
{
|
|
time = MIN(remaining_time, plan->coast_time);
|
|
current_position += current_velocity*time;
|
|
remaining_time -= time;
|
|
}
|
|
|
|
if (!(remaining_time > 0.f))
|
|
{
|
|
goto store_result;
|
|
}
|
|
|
|
if (plan->deceleration_time>0.f)
|
|
{
|
|
time = MIN(remaining_time, plan->deceleration_time);
|
|
current_position +=
|
|
(0.5f*time*plan->deceleration + current_velocity)*time;
|
|
current_velocity += time*plan->deceleration;
|
|
remaining_time -= time;
|
|
}
|
|
|
|
if (remaining_time > 0.f)
|
|
{
|
|
result = TRUE;
|
|
}
|
|
|
|
store_result:
|
|
*new_position = current_position;
|
|
*new_velocity = current_velocity;
|
|
|
|
return result;
|
|
}
|
|
void unit_set_actively_controlled(
|
|
long unit_index,
|
|
boolean actively_controlled)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short weapon_index;
|
|
|
|
if (unit->unit.actor_index!=NONE ||
|
|
unit->unit.swarm_actor_index!=NONE ||
|
|
unit->unit.player_index!=NONE)
|
|
{
|
|
actively_controlled = TRUE;
|
|
}
|
|
|
|
actively_controlled =
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit) &&
|
|
actively_controlled;
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_actively_controlled_bit, actively_controlled);
|
|
SET_FLAG(unit->unit.flags, _unit_controllable_bit, actively_controlled);
|
|
|
|
for (weapon_index = 0; weapon_index<MAXIMUM_WEAPONS_PER_UNIT; ++weapon_index)
|
|
{
|
|
long weapon_object_index = unit->unit.weapon_object_indices[weapon_index];
|
|
|
|
if (weapon_object_index!=NONE)
|
|
{
|
|
item_in_unit_inventory(weapon_object_index, unit_index);
|
|
}
|
|
}
|
|
|
|
unit_update_driver_and_gunner(unit_index);
|
|
|
|
return;
|
|
}
|
|
static void unit_update_driver_and_gunner(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
long child_object_index = unit->object.first_child_object_index;
|
|
|
|
while (child_object_index!=NONE)
|
|
{
|
|
struct object_datum *child_object = object_get(child_object_index);
|
|
|
|
if (TEST_FLAG(_object_mask_unit, child_object->object.type))
|
|
{
|
|
struct unit_datum *child_unit = (struct unit_datum *)child_object;
|
|
|
|
if (child_unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(
|
|
&unit_definition->unit.seats,
|
|
child_unit->unit.parent_seat_index,
|
|
struct unit_seat);
|
|
|
|
if (TEST_FLAG(seat->flags, _unit_seat_driver_bit) &&
|
|
!TEST_FLAG(unit->unit.flags, _unit_actively_controlled_bit) &&
|
|
unit->unit.driver_object_index==NONE)
|
|
{
|
|
unit->unit.driver_object_index = child_object_index;
|
|
|
|
if (TEST_FLAG(seat->flags, _unit_seat_gunner_bit) &&
|
|
unit->unit.gunner_object_index==NONE)
|
|
{
|
|
unit->unit.gunner_object_index = child_object_index;
|
|
}
|
|
}
|
|
else if (TEST_FLAG(seat->flags, _unit_seat_gunner_bit) &&
|
|
(unit->unit.gunner_object_index==NONE ||
|
|
unit->unit.gunner_object_index==unit->unit.driver_object_index))
|
|
{
|
|
unit->unit.gunner_object_index = child_object_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
child_object_index = child_object->object.next_object_index;
|
|
}
|
|
|
|
return;
|
|
}
|
|
static short unit_first_free_weapon_index(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short result = NONE;
|
|
short weapon_index;
|
|
|
|
for (weapon_index = 0; weapon_index<MAXIMUM_WEAPONS_PER_UNIT; ++weapon_index)
|
|
{
|
|
if (unit->unit.weapon_object_indices[weapon_index]==NONE)
|
|
{
|
|
result = weapon_index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
boolean unit_add_weapon_to_inventory(
|
|
long unit_index,
|
|
long weapon_index,
|
|
long is_starting_weapon)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
short inventory_index;
|
|
boolean added = FALSE;
|
|
short mode = (short)is_starting_weapon;
|
|
|
|
if (TEST_FLAG(weapon->object.flags, _object_connected_to_map_bit) &&
|
|
weapon->object.parent_object_index==NONE &&
|
|
unit_can_use_weapon(unit_index, weapon_index) &&
|
|
game_engine_picking_up(unit_index, weapon_index))
|
|
{
|
|
if (mode==_unit_add_weapon_replace)
|
|
{
|
|
unit_delete_all_weapons(unit_index);
|
|
}
|
|
|
|
inventory_index = unit_first_free_weapon_index(unit_index);
|
|
|
|
if (inventory_index!=NONE)
|
|
{
|
|
object_disconnect_from_map(weapon_index);
|
|
object_set_visibility(weapon_index, FALSE);
|
|
item_in_unit_inventory(weapon_index, unit_index);
|
|
|
|
unit->unit.weapon_object_indices[inventory_index] = weapon_index;
|
|
unit->unit.weapon_last_used_at_game_time[inventory_index] = 0;
|
|
|
|
switch (mode)
|
|
{
|
|
case _unit_add_weapon_normal:
|
|
unit->unit.desired_weapon_index =
|
|
unit_weapon_next_index(unit_index, unit->unit.current_weapon_index, 0);
|
|
break;
|
|
|
|
case _unit_add_weapon_starting:
|
|
if (!TEST_FLAG(unit->unit.control_flags, _unit_control_weapon_primary_trigger_bit))
|
|
{
|
|
player_control_set_desired_weapon(unit_index, inventory_index);
|
|
}
|
|
|
|
case _unit_add_weapon_replace:
|
|
unit->unit.desired_weapon_index = inventory_index;
|
|
break;
|
|
}
|
|
|
|
added = TRUE;
|
|
}
|
|
}
|
|
|
|
return added;
|
|
}
|
|
static short unit_animation_impulse_get_index(
|
|
short animation_impulse,
|
|
short *interpolation_frame_count)
|
|
{
|
|
short index = NONE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
5364,
|
|
animation_impulse>=0 && animation_impulse<NUMBER_OF_UNIT_ANIMATION_IMPULSES);
|
|
|
|
switch (animation_impulse)
|
|
{
|
|
case 0:
|
|
index = _unit_weapon_class_animation_berserk;
|
|
break;
|
|
case 1:
|
|
index = _unit_weapon_class_animation_signal_move;
|
|
break;
|
|
case 2:
|
|
index = _unit_weapon_class_animation_signal_attack;
|
|
break;
|
|
case 3:
|
|
index = _unit_weapon_class_animation_signal_warn;
|
|
break;
|
|
case 4:
|
|
index = _unit_weapon_class_animation_surprise_front;
|
|
break;
|
|
case 5:
|
|
index = _unit_weapon_class_animation_surprise_back;
|
|
break;
|
|
case 6:
|
|
index = _unit_weapon_class_animation_evade_left;
|
|
break;
|
|
case 7:
|
|
index = _unit_weapon_class_animation_evade_right;
|
|
break;
|
|
case 8:
|
|
index = _unit_weapon_class_animation_diving_front;
|
|
break;
|
|
case 9:
|
|
index = _unit_weapon_class_animation_diving_back;
|
|
break;
|
|
case 10:
|
|
index = _unit_weapon_class_animation_diving_left;
|
|
break;
|
|
case 11:
|
|
index = _unit_weapon_class_animation_diving_right;
|
|
break;
|
|
case 12:
|
|
index = _unit_weapon_class_animation_celebrate;
|
|
break;
|
|
case 13:
|
|
index = _unit_weapon_class_animation_panic;
|
|
break;
|
|
default:
|
|
display_assert(NULL, "c:\\halo\\SOURCE\\units\\units.c", 5383, TRUE);
|
|
system_exit(-1);
|
|
break;
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
5385,
|
|
index!=NONE);
|
|
|
|
if (interpolation_frame_count!=NULL)
|
|
{
|
|
switch (animation_impulse)
|
|
{
|
|
case 4:
|
|
case 5:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
*interpolation_frame_count = 3;
|
|
break;
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 6:
|
|
case 7:
|
|
case 12:
|
|
case 13:
|
|
*interpolation_frame_count = 6;
|
|
break;
|
|
default:
|
|
display_assert(NULL, "c:\\halo\\SOURCE\\units\\units.c", 5412, TRUE);
|
|
system_exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
static boolean unit_can_play_animation_impulse(
|
|
long unit_index,
|
|
short animation_impulse)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
boolean result = FALSE;
|
|
|
|
switch (unit->unit.animation.state)
|
|
{
|
|
case _unit_state_hard_ping:
|
|
case _unit_state_dying_airborne:
|
|
case _unit_state_dying:
|
|
case _unit_state_entering_seat:
|
|
case _unit_state_exiting_seat:
|
|
case _unit_state_ai_impulse:
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_resurrect_front:
|
|
case _unit_state_resurrect_back:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
result = FALSE;
|
|
break;
|
|
|
|
default:
|
|
if (unit->object.parent_object_index!=NONE)
|
|
{
|
|
struct unit_datum *parent_unit;
|
|
struct unit_definition *parent_unit_definition;
|
|
struct unit_seat *seat;
|
|
|
|
if (unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
parent_unit = unit_try_and_get(unit->object.parent_object_index);
|
|
if (parent_unit!=NULL)
|
|
{
|
|
long impulse_index;
|
|
|
|
parent_unit_definition = unit_definition_get(parent_unit->definition_index);
|
|
seat = TAG_BLOCK_GET_ELEMENT(
|
|
&parent_unit_definition->unit.seats,
|
|
unit->unit.parent_seat_index,
|
|
struct unit_seat);
|
|
impulse_index = animation_impulse;
|
|
|
|
if (impulse_index>=12 &&
|
|
impulse_index<=13)
|
|
{
|
|
result = (boolean)TEST_FLAG(seat->flags, _unit_seat_unknown8_bit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
long impulse_index = animation_impulse;
|
|
|
|
if (impulse_index>=12 &&
|
|
impulse_index<=13)
|
|
{
|
|
result = FALSE;
|
|
}
|
|
else
|
|
{
|
|
result = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean unit_test_animation_impulse(
|
|
long unit_index,
|
|
short animation_impulse)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition;
|
|
struct animation_graph *animation_graph;
|
|
struct animation_graph_unit_seat *unit_seat;
|
|
struct animation_graph_weapon_class *weapon_class;
|
|
short interpolation_frame_count;
|
|
short animation_type;
|
|
|
|
if (unit_can_play_animation_impulse(unit_index, animation_impulse))
|
|
{
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
unit_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
weapon_class = TAG_BLOCK_GET_ELEMENT(
|
|
&unit_seat->weapon_classes,
|
|
unit->unit.animation.weapon_index,
|
|
struct animation_graph_weapon_class);
|
|
animation_type = unit_animation_impulse_get_index(
|
|
animation_impulse,
|
|
&interpolation_frame_count);
|
|
|
|
if (animation_type>=0 && animation_type<weapon_class->animations.count)
|
|
{
|
|
animation_type =
|
|
animation_graph_animation_index_get(&weapon_class->animations)
|
|
[animation_type].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_type = NONE;
|
|
}
|
|
|
|
return (boolean)(animation_type!=NONE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
boolean unit_start_animation_impulse(
|
|
long unit_index,
|
|
short animation_impulse,
|
|
real_vector2d *alignment_vector)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition;
|
|
struct animation_graph *animation_graph;
|
|
struct animation_graph_unit_seat *unit_seat;
|
|
struct animation_graph_weapon_class *weapon_class;
|
|
short interpolation_frame_count;
|
|
short animation_type;
|
|
short animation_index;
|
|
long animation_graph_index;
|
|
boolean result = FALSE;
|
|
|
|
if (unit_can_play_animation_impulse(unit_index, animation_impulse))
|
|
{
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
unit_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
weapon_class = TAG_BLOCK_GET_ELEMENT(
|
|
&unit_seat->weapon_classes,
|
|
unit->unit.animation.weapon_index,
|
|
struct animation_graph_weapon_class);
|
|
animation_type = unit_animation_impulse_get_index(
|
|
animation_impulse,
|
|
&interpolation_frame_count);
|
|
|
|
if (animation_type>=0 &&
|
|
animation_type<weapon_class->animations.count)
|
|
{
|
|
animation_index =
|
|
animation_graph_animation_index_get(&weapon_class->animations)
|
|
[animation_type].animation_index;
|
|
|
|
if (animation_index!=NONE)
|
|
{
|
|
object_start_interpolation(
|
|
unit_index,
|
|
interpolation_frame_count);
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
animation_index =
|
|
animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
animation_index);
|
|
unit_set_animation(
|
|
unit_index,
|
|
unit_definition->object.animation_graph.index,
|
|
animation_index);
|
|
SET_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_postpone_weapon_ik_until_interpolation_ends_bit,
|
|
TRUE);
|
|
unit->unit.animation.state = _unit_state_ai_impulse;
|
|
|
|
if (alignment_vector!=NULL &&
|
|
unit->object.type==_object_type_biped &&
|
|
unit->object.parent_object_index==NONE)
|
|
{
|
|
unit_align_facing(unit_index, alignment_vector);
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
static void unit_melee_sound(
|
|
long unit_index,
|
|
short material_type,
|
|
long damage_effect_index)
|
|
{
|
|
struct material_definition *material_definition =
|
|
scenario_material_definition_get(material_type);
|
|
|
|
if (material_definition->melee_hit_sound.index!=NONE)
|
|
{
|
|
object_impulse_sound_new(
|
|
unit_index,
|
|
material_definition->melee_hit_sound.index,
|
|
NONE,
|
|
global_origin3d,
|
|
global_forward3d,
|
|
1.f);
|
|
}
|
|
|
|
if (damage_effect_index!=NONE)
|
|
{
|
|
struct damage_effect_definition *damage_effect_definition =
|
|
damage_effect_definition_get(damage_effect_index);
|
|
|
|
if (damage_effect_definition->sound.index!=NONE)
|
|
{
|
|
object_impulse_sound_new(
|
|
unit_index,
|
|
damage_effect_definition->sound.index,
|
|
NONE,
|
|
global_origin3d,
|
|
global_forward3d,
|
|
1.f);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
void unit_cause_melee_damage(
|
|
long unit_index,
|
|
boolean melee_hit,
|
|
long target_object_index,
|
|
short node_index,
|
|
short region_index,
|
|
short material_index,
|
|
real_vector3d const *object_normal)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
|
|
if (unit_definition->unit.melee_damage.index!=NONE)
|
|
{
|
|
struct object_marker marker;
|
|
real_point3d melee_position;
|
|
long damage_effect_index;
|
|
|
|
if (object_get_marker_by_name(unit_index, "melee", &marker, 1)==1)
|
|
{
|
|
struct collision_result collision;
|
|
real_vector3d collision_vector;
|
|
real_point3d const *collision_origin;
|
|
|
|
melee_position = marker.matrix.position;
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
8640,
|
|
global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH);
|
|
|
|
global_current_collision_users[global_current_collision_user_depth++] = 7;
|
|
collision_origin = &unit->object.bounding_sphere_center;
|
|
vector_from_points3d(collision_origin, &melee_position, &collision_vector);
|
|
if (collision_test_vector(
|
|
_collision_test_for_projectiles_flags,
|
|
collision_origin,
|
|
&collision_vector,
|
|
NONE,
|
|
&collision))
|
|
{
|
|
melee_position = *collision_origin;
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
8648,
|
|
global_current_collision_user_depth > 1);
|
|
--global_current_collision_user_depth;
|
|
}
|
|
else
|
|
{
|
|
melee_position = unit->object.bounding_sphere_center;
|
|
}
|
|
|
|
damage_effect_index = unit_definition->unit.melee_damage.index;
|
|
{
|
|
long weapon_index = unit_inventory_get_weapon(
|
|
unit_index,
|
|
unit_get(unit_index)->unit.current_weapon_index);
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_definition *weapon_definition =
|
|
weapon_definition_get(weapon_get(weapon_index)->definition_index);
|
|
|
|
if (TEST_FLAG(
|
|
weapon_definition->weapon.flags,
|
|
_weapon_non_players_use_melee_damage_bit))
|
|
{
|
|
damage_effect_index =
|
|
weapon_definition->weapon.melee_attack_damage.index;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
struct damage_data damage_data;
|
|
|
|
damage_data_new(&damage_data, damage_effect_index);
|
|
damage_data.location = unit->object.location;
|
|
damage_data.owner_object_index = unit_index;
|
|
damage_data.owner_team_index = unit->object.owner_team_index;
|
|
damage_data.owner_player_index = unit->unit.player_index;
|
|
damage_data.origin = melee_position;
|
|
damage_data.epicenter = unit->object.bounding_sphere_center;
|
|
|
|
if (target_object_index==NONE)
|
|
{
|
|
area_of_effect_cause_damage(&damage_data, NONE);
|
|
}
|
|
else
|
|
{
|
|
object_cause_damage(
|
|
&damage_data,
|
|
target_object_index,
|
|
node_index,
|
|
region_index,
|
|
material_index,
|
|
object_normal);
|
|
}
|
|
|
|
if (!melee_hit && damage_data.material_type!=NONE)
|
|
{
|
|
long hit_material_type = damage_data.material_type;
|
|
|
|
unit_melee_sound(
|
|
unit_index,
|
|
hit_material_type,
|
|
damage_effect_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
unit->unit.melee_attack_state = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/* insert verbatim into source/units/units.c immediately after the closing
|
|
brace of unit_cause_melee_damage (line 8403) and before
|
|
'static void unit_align_facing(' */
|
|
enum
|
|
{
|
|
_collision_result_breakable_surface_bit = 3,
|
|
};
|
|
|
|
void unit_cause_player_melee_damage(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct unit_definition *unit_definition;
|
|
real_vector3d *facing;
|
|
long best_object_index;
|
|
short hit_material_type;
|
|
short breakable_surface_index;
|
|
long breakable_surface_seed_surface_index;
|
|
long melee_response_effect_index;
|
|
real_point3d ray_origin;
|
|
short best_object_type;
|
|
real best_object_fraction;
|
|
long outer_index;
|
|
long outer_count;
|
|
|
|
unit = unit_get(unit_index);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
best_object_index = NONE;
|
|
hit_material_type = NONE;
|
|
breakable_surface_index = NONE;
|
|
melee_response_effect_index = NONE;
|
|
|
|
{
|
|
struct object_marker marker;
|
|
|
|
object_get_marker_by_name(unit_index, "head", &marker, 1);
|
|
ray_origin = marker.matrix.position;
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
8729,
|
|
global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH);
|
|
global_current_collision_users[global_current_collision_user_depth++] = 8;
|
|
facing = &unit->unit.aiming_vector;
|
|
|
|
{
|
|
real_vector3d perpendicular;
|
|
real_vector3d cross;
|
|
struct collision_result collision;
|
|
real_vector3d ray;
|
|
|
|
normalize3d(perpendicular3d(facing, &perpendicular));
|
|
cross_product3d(facing, &perpendicular, &cross);
|
|
|
|
outer_index = -2;
|
|
outer_count = 5;
|
|
do
|
|
{
|
|
long inner_index = -2;
|
|
long inner_count = 5;
|
|
|
|
do
|
|
{
|
|
double inner_component_i = cross.i * (real)inner_index;
|
|
|
|
ray.i = facing->i * 0.8f +
|
|
((real)inner_component_i + (real)outer_index * perpendicular.i) * 0.1f;
|
|
inner_component_i = cross.j * (real)inner_index;
|
|
ray.j = unit->unit.aiming_vector.j * 0.8f +
|
|
((real)inner_component_i + (real)outer_index * perpendicular.j) * 0.1f;
|
|
ray.k = unit->unit.aiming_vector.k * 0.8f +
|
|
((real)outer_index * perpendicular.k + (real)inner_index * cross.k) * 0.1f;
|
|
|
|
if (collision_test_vector(
|
|
_collision_test_for_projectiles_flags,
|
|
&ray_origin,
|
|
&ray,
|
|
unit_index,
|
|
&collision))
|
|
{
|
|
switch (collision.type)
|
|
{
|
|
case _collision_result_structure:
|
|
if (best_object_index==NONE)
|
|
{
|
|
hit_material_type = collision.material_type;
|
|
if (TEST_FLAG(
|
|
collision.flags,
|
|
_collision_result_breakable_surface_bit))
|
|
{
|
|
breakable_surface_index =
|
|
collision.breakable_surface_index;
|
|
breakable_surface_seed_surface_index =
|
|
collision.surface_index;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _collision_result_object:
|
|
{
|
|
long hit_object_index = collision.object_index;
|
|
struct object_datum *hit_object = object_get(hit_object_index);
|
|
|
|
if (hit_object->object.type!=_object_type_weapon &&
|
|
hit_object->object.parent_object_index!=NONE)
|
|
{
|
|
hit_object_index = hit_object->object.parent_object_index;
|
|
hit_object = object_get(hit_object_index);
|
|
}
|
|
|
|
if (best_object_index==NONE ||
|
|
(hit_object->object.type==_object_type_biped &&
|
|
best_object_type==_object_type_biped &&
|
|
best_object_fraction>collision.t) ||
|
|
(hit_object->object.type==_object_type_biped &&
|
|
best_object_type!=_object_type_biped))
|
|
{
|
|
best_object_index = hit_object_index;
|
|
best_object_type = hit_object->object.type;
|
|
hit_material_type = collision.material_type;
|
|
best_object_fraction = collision.t;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
++inner_index;
|
|
--inner_count;
|
|
}
|
|
while (inner_count!=0);
|
|
|
|
++outer_index;
|
|
--outer_count;
|
|
}
|
|
while (outer_count!=0);
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
8797,
|
|
global_current_collision_user_depth > 1);
|
|
--global_current_collision_user_depth;
|
|
|
|
{
|
|
short index = unit_get(unit_index)->unit.current_weapon_index;
|
|
struct unit_datum *current_unit;
|
|
long melee_damage_effect_index = NONE;
|
|
|
|
current_unit = unit_get(unit_index);
|
|
if (index!=NONE)
|
|
{
|
|
long weapon_index;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
8371,
|
|
index>=0 && index<MAXIMUM_WEAPONS_PER_UNIT);
|
|
weapon_index = current_unit->unit.weapon_object_indices[index];
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_definition *weapon_definition =
|
|
weapon_definition_get(weapon_get(weapon_index)->definition_index);
|
|
|
|
melee_damage_effect_index =
|
|
weapon_definition->weapon.melee_attack_damage.index;
|
|
melee_response_effect_index =
|
|
weapon_definition->weapon.melee_attack_response.index;
|
|
}
|
|
}
|
|
|
|
if (melee_damage_effect_index==NONE)
|
|
{
|
|
melee_damage_effect_index = unit_definition->unit.melee_damage.index;
|
|
}
|
|
|
|
if (best_object_index!=NONE)
|
|
{
|
|
struct object_datum *best_object = object_get(best_object_index);
|
|
|
|
if (best_object->object.type==_object_type_vehicle)
|
|
{
|
|
struct object_definition *best_object_definition =
|
|
object_definition_get(best_object->definition_index);
|
|
real_vector3d acceleration;
|
|
real acceleration_scale =
|
|
best_object_definition->object.acceleration_scale * 0.035f;
|
|
|
|
acceleration.i = acceleration_scale * unit->unit.aiming_vector.i;
|
|
acceleration.j = acceleration_scale * unit->unit.aiming_vector.j;
|
|
acceleration.k = acceleration_scale * unit->unit.aiming_vector.k;
|
|
vehicle_accelerate(best_object_index, &acceleration);
|
|
}
|
|
}
|
|
|
|
if (melee_damage_effect_index!=NONE)
|
|
{
|
|
struct damage_data damage_data;
|
|
|
|
damage_data_new(&damage_data, melee_damage_effect_index);
|
|
SET_FLAG(damage_data.flags, _damage_area_of_effect_bit, TRUE);
|
|
damage_data.location = unit->object.location;
|
|
damage_data.owner_object_index = unit_index;
|
|
damage_data.owner_team_index = unit->object.owner_team_index;
|
|
damage_data.owner_player_index = unit->unit.player_index;
|
|
damage_data.origin = ray_origin;
|
|
damage_data.epicenter = unit->object.bounding_sphere_center;
|
|
damage_data.direction = unit->unit.aiming_vector;
|
|
damage_data.material_type = hit_material_type;
|
|
|
|
if (best_object_index==NONE)
|
|
{
|
|
if (breakable_surface_index!=NONE)
|
|
{
|
|
breakable_surface_damage(
|
|
breakable_surface_index,
|
|
&damage_data,
|
|
breakable_surface_seed_surface_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct object_datum *best_object = object_get(best_object_index);
|
|
struct game_globals_player_information *player_information;
|
|
|
|
if (best_object->object.type==_object_type_machine)
|
|
{
|
|
machine_try_to_open_with_damage(best_object_index);
|
|
}
|
|
|
|
player_information = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->player_information,
|
|
0,
|
|
struct game_globals_player_information);
|
|
if (player_information->run_forward_speed>0.f)
|
|
{
|
|
damage_data.scale =
|
|
dot_product3d(
|
|
&unit->object.translational_velocity,
|
|
&unit->object.forward) * 30.f /
|
|
player_information->run_forward_speed;
|
|
if (damage_data.scale<0.f)
|
|
{
|
|
damage_data.scale = 0.f;
|
|
}
|
|
else if (damage_data.scale>1.f)
|
|
{
|
|
damage_data.scale = 1.f;
|
|
}
|
|
}
|
|
|
|
if (unit->object.type==_object_type_biped &&
|
|
biped_get(unit_index)->biped.airborne_ticks>15)
|
|
{
|
|
damage_data.scale = 1.5f;
|
|
}
|
|
|
|
if (object_get(best_object_index)->object.type==_object_type_biped)
|
|
{
|
|
object_cause_damage(
|
|
&damage_data,
|
|
best_object_index,
|
|
NONE,
|
|
NONE,
|
|
NONE,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hit_material_type!=NONE)
|
|
{
|
|
unit_melee_sound(
|
|
unit_index,
|
|
hit_material_type,
|
|
melee_damage_effect_index);
|
|
if (melee_response_effect_index!=NONE)
|
|
{
|
|
struct damage_data damage_data;
|
|
|
|
damage_data_new(&damage_data, melee_response_effect_index);
|
|
damage_data.direction.i = -unit->unit.aiming_vector.i;
|
|
damage_data.epicenter = unit->object.bounding_sphere_center;
|
|
damage_data.direction.j = -unit->unit.aiming_vector.j;
|
|
damage_data.direction.k = -unit->unit.aiming_vector.k;
|
|
SET_FLAG(
|
|
damage_data.flags,
|
|
_damage_from_weapon_bit,
|
|
TRUE);
|
|
damage_data.origin = unit->object.bounding_sphere_center;
|
|
object_cause_damage(
|
|
&damage_data,
|
|
unit_index,
|
|
NONE,
|
|
NONE,
|
|
NONE,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
unit->unit.melee_attack_state = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_align_facing(
|
|
long unit_index,
|
|
real_vector2d const *alignment_vector)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
if (unit->object.parent_object_index==NONE)
|
|
{
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
9353,
|
|
valid_real_normal2d(alignment_vector),
|
|
csprintf(
|
|
temporary,
|
|
"%s: assert_valid_real_normal2d(%f, %f)",
|
|
"alignment_vector",
|
|
alignment_vector->i,
|
|
alignment_vector->j));
|
|
|
|
set_real_vector3d(
|
|
&unit->object.forward,
|
|
alignment_vector->i,
|
|
alignment_vector->j,
|
|
0.f);
|
|
unit->object.up = *global_up3d;
|
|
|
|
match_assert_valid_real_vector3d_axes2(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
9357,
|
|
&unit->object.forward,
|
|
&unit->object.up);
|
|
}
|
|
|
|
return;
|
|
}
|
|
void unit_start_flaming_to_death(
|
|
long unit_index,
|
|
long attacker_object_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
unit_drop_current_weapon(unit_index, TRUE);
|
|
|
|
SET_FLAG(unit->unit.flags, _unit_ignore_hard_pings_bit, TRUE);
|
|
SET_FLAG(unit->object.damage_flags, _object_dead_bit, FALSE);
|
|
SET_FLAG(unit->object.damage_flags, _object_cannot_take_damage_bit, TRUE);
|
|
|
|
if (unit->unit.flaming_death_delay==0)
|
|
{
|
|
short random_delay =
|
|
seed_random_range(get_global_random_seed_address(), 60, 150);
|
|
long delay;
|
|
|
|
if (random_delay<1)
|
|
{
|
|
delay = 1;
|
|
}
|
|
else if (random_delay>255)
|
|
{
|
|
delay = 255;
|
|
}
|
|
else
|
|
{
|
|
delay = random_delay;
|
|
}
|
|
|
|
unit->unit.flaming_death_delay = (byte)delay;
|
|
unit->unit.flaming_death_attacker_object_index = attacker_object_index;
|
|
|
|
unit_start_running_blindly(unit_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_euler_axis_buildplan(
|
|
real position,
|
|
real velocity,
|
|
real maximum_velocity,
|
|
real maximum_acceleration,
|
|
struct unit_acceleration_plan *plan)
|
|
{
|
|
boolean moving_positive;
|
|
real acceleration_time;
|
|
real coasting_velocity;
|
|
real discriminant;
|
|
real first_root;
|
|
real half_time;
|
|
real half_velocity;
|
|
real maximum_velocity_time;
|
|
real negative_acceleration;
|
|
real second_root;
|
|
real second_time;
|
|
real stopping_position;
|
|
real time;
|
|
|
|
plan->acceleration = REAL_MAX;
|
|
plan->acceleration_time = REAL_MAX;
|
|
plan->coast_time = REAL_MAX;
|
|
plan->deceleration = REAL_MAX;
|
|
plan->deceleration_time = REAL_MAX;
|
|
plan->initial_position = position;
|
|
plan->initial_velocity = velocity;
|
|
plan->at_rest = fabs(position)<0.001f && fabs(velocity)<0.001f;
|
|
|
|
if (plan->at_rest)
|
|
{
|
|
plan->acceleration = 0.f;
|
|
plan->acceleration_time = 0.f;
|
|
plan->coast_time = 0.f;
|
|
plan->deceleration = 0.f;
|
|
plan->deceleration_time = 0.f;
|
|
goto plan_complete;
|
|
}
|
|
|
|
time = fabs(velocity) / maximum_acceleration;
|
|
moving_positive = velocity>0.f;
|
|
half_time = time*0.5f;
|
|
if (half_time*velocity*0.5f + position<0.f)
|
|
{
|
|
unit_euler_axis_buildplan(
|
|
-position,
|
|
-velocity,
|
|
maximum_velocity,
|
|
maximum_acceleration,
|
|
plan);
|
|
plan->initial_position *= -1.f;
|
|
plan->initial_velocity *= -1.f;
|
|
plan->acceleration *= -1.f;
|
|
plan->deceleration *= -1.f;
|
|
goto validate_plan;
|
|
}
|
|
|
|
half_velocity = velocity*0.5f;
|
|
stopping_position = half_velocity*time + position;
|
|
if (stopping_position<0.f)
|
|
{
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x7b7,
|
|
plan->initial_position > -0.001f,
|
|
"plan->initial_p > -1e-03f");
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x7b8,
|
|
plan->initial_velocity < 0.f,
|
|
"plan->initial_v < 0");
|
|
plan->acceleration = 0.f;
|
|
plan->acceleration_time = 0.f;
|
|
plan->deceleration =
|
|
plan->initial_velocity*plan->initial_velocity /
|
|
(plan->initial_position + plan->initial_position);
|
|
plan->deceleration_time =
|
|
-(plan->initial_velocity / plan->deceleration);
|
|
plan->coast_time = 0.f;
|
|
goto validate_plan;
|
|
}
|
|
|
|
if (moving_positive)
|
|
{
|
|
acceleration_time = square_root(stopping_position / maximum_acceleration);
|
|
}
|
|
else
|
|
{
|
|
negative_acceleration = -maximum_acceleration;
|
|
velocity += velocity;
|
|
position =
|
|
velocity*velocity - negative_acceleration*stopping_position*4.f;
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x7eb,
|
|
position >= 0.f,
|
|
"disc >= 0");
|
|
second_root = square_root(position);
|
|
first_root =
|
|
(-velocity - second_root) /
|
|
(negative_acceleration + negative_acceleration);
|
|
second_time =
|
|
(second_root - velocity) /
|
|
(negative_acceleration + negative_acceleration);
|
|
if (first_root>=0.f &&
|
|
(second_time<0.f || first_root<second_time))
|
|
{
|
|
acceleration_time = first_root;
|
|
}
|
|
else if (0.f>second_time)
|
|
{
|
|
acceleration_time = 0.f;
|
|
goto apply_velocity_limit;
|
|
}
|
|
else
|
|
{
|
|
acceleration_time = second_time;
|
|
}
|
|
}
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x7fa,
|
|
acceleration_time >= 0.f,
|
|
"t >= 0");
|
|
|
|
apply_velocity_limit:
|
|
if (maximum_velocity>0.f)
|
|
{
|
|
maximum_velocity_time =
|
|
(moving_positive ?
|
|
maximum_velocity :
|
|
maximum_velocity + plan->initial_velocity) /
|
|
maximum_acceleration;
|
|
maximum_velocity_time = MAX(0.f, maximum_velocity_time);
|
|
first_root = MIN(acceleration_time, maximum_velocity_time);
|
|
}
|
|
else
|
|
{
|
|
first_root = acceleration_time;
|
|
}
|
|
|
|
plan->acceleration = -maximum_acceleration;
|
|
plan->deceleration = maximum_acceleration;
|
|
if (moving_positive)
|
|
{
|
|
plan->acceleration_time = first_root + time;
|
|
plan->deceleration_time = first_root;
|
|
}
|
|
else
|
|
{
|
|
plan->acceleration_time = first_root;
|
|
plan->deceleration_time = first_root + time;
|
|
}
|
|
|
|
if (first_root<acceleration_time)
|
|
{
|
|
coasting_velocity =
|
|
plan->acceleration*plan->acceleration_time +
|
|
plan->initial_velocity;
|
|
discriminant =
|
|
((acceleration_time - first_root)*coasting_velocity)*2.f -
|
|
(acceleration_time - first_root)*
|
|
(acceleration_time - first_root)*maximum_acceleration;
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x850,
|
|
coasting_velocity < 0.f,
|
|
"coasting_vel < 0");
|
|
plan->coast_time = discriminant / coasting_velocity;
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x852,
|
|
plan->coast_time >= 0.f,
|
|
"plan->coast_t >= 0");
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x853,
|
|
plan->coast_time + first_root >= acceleration_time,
|
|
"plan->coast_t + actual_t >= t");
|
|
}
|
|
else
|
|
{
|
|
plan->coast_time = 0.f;
|
|
}
|
|
|
|
validate_plan:
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x85c,
|
|
REAL_MAX != plan->acceleration,
|
|
"REAL_MAX != plan->accel_a");
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x85d,
|
|
REAL_MAX != plan->acceleration_time,
|
|
"REAL_MAX != plan->accel_t");
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x85e,
|
|
REAL_MAX != plan->coast_time,
|
|
"REAL_MAX != plan->coast_t");
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x85f,
|
|
REAL_MAX != plan->deceleration,
|
|
"REAL_MAX != plan->decel_a");
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x860,
|
|
REAL_MAX != plan->deceleration_time,
|
|
"REAL_MAX != plan->decel_t");
|
|
|
|
plan_complete:
|
|
return;
|
|
}
|
|
static void unit_euler_axis_couple(
|
|
struct unit_acceleration_plan *first_plan,
|
|
struct unit_acceleration_plan *second_plan,
|
|
real maximum_velocity,
|
|
real maximum_acceleration)
|
|
{
|
|
real acceleration_time;
|
|
real adjustment;
|
|
real discriminant;
|
|
real first_total_time;
|
|
real peak_velocity;
|
|
real second_total_time;
|
|
real time_extension;
|
|
real time_factor;
|
|
struct unit_acceleration_plan *adjust_plan = NULL;
|
|
|
|
if (first_plan->at_rest || second_plan->at_rest)
|
|
{
|
|
goto overlap_complete;
|
|
}
|
|
|
|
first_total_time =
|
|
first_plan->deceleration_time +
|
|
first_plan->coast_time +
|
|
first_plan->acceleration_time;
|
|
second_total_time =
|
|
second_plan->deceleration_time +
|
|
second_plan->coast_time +
|
|
second_plan->acceleration_time;
|
|
if (first_plan->acceleration_time>0.f &&
|
|
first_total_time<second_total_time)
|
|
{
|
|
time_extension = second_total_time - first_total_time;
|
|
adjust_plan = first_plan;
|
|
}
|
|
else if (second_plan->acceleration_time>0.f &&
|
|
second_total_time<first_total_time)
|
|
{
|
|
time_extension = first_total_time - second_total_time;
|
|
adjust_plan = second_plan;
|
|
}
|
|
|
|
if (!adjust_plan)
|
|
{
|
|
goto overlap_complete;
|
|
}
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x8ae,
|
|
time_extension > 0.f,
|
|
"t_extension > 0");
|
|
time_factor =
|
|
(time_extension + adjust_plan->coast_time) *
|
|
maximum_acceleration;
|
|
peak_velocity =
|
|
adjust_plan->acceleration_time*adjust_plan->acceleration +
|
|
adjust_plan->initial_velocity;
|
|
discriminant =
|
|
time_factor*time_factor -
|
|
(real)((-time_extension)*fabs(peak_velocity)) *
|
|
maximum_acceleration*4.f;
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x8c4,
|
|
discriminant >= 0.f,
|
|
"disc >= 0");
|
|
adjustment =
|
|
(square_root(discriminant) - time_factor) /
|
|
(maximum_acceleration + maximum_acceleration);
|
|
adjustment = MIN(
|
|
adjustment,
|
|
MIN(adjust_plan->acceleration_time, adjust_plan->deceleration_time));
|
|
if (!(adjustment>0.f))
|
|
{
|
|
goto overlap_complete;
|
|
}
|
|
|
|
acceleration_time = adjust_plan->acceleration_time - adjustment;
|
|
peak_velocity =
|
|
acceleration_time*adjust_plan->acceleration +
|
|
adjust_plan->initial_velocity;
|
|
adjust_plan->acceleration_time = acceleration_time;
|
|
adjust_plan->deceleration_time -= adjustment;
|
|
adjust_plan->coast_time =
|
|
((peak_velocity + peak_velocity +
|
|
adjustment*adjust_plan->acceleration)*adjustment) /
|
|
peak_velocity;
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x8d8,
|
|
(acceleration_time >= 0.f) &&
|
|
(adjust_plan->deceleration_time >= 0.f),
|
|
"(adjust_plan->accel_t >= 0) && (adjust_plan->decel_t >= 0)");
|
|
|
|
overlap_complete:
|
|
return;
|
|
}
|
|
void unit_euler_aiming_update(
|
|
real_matrix4x3 const *orientation,
|
|
real_vector3d *aiming_vector,
|
|
real_vector3d const *desired_aiming_vector,
|
|
real_vector3d *aiming_velocity,
|
|
real_rectangle2d const *aiming_bounds,
|
|
real angular_velocity_limit,
|
|
real angular_acceleration_limit)
|
|
{
|
|
boolean aiming_yaw_in_bounds;
|
|
boolean pitch_complete;
|
|
boolean yaw_complete;
|
|
boolean yaw_wraps;
|
|
real angle;
|
|
real dot_product;
|
|
real_euler_angles2d aiming_error;
|
|
real_euler_angles2d aiming_angular_velocity;
|
|
real_euler_angles2d aiming_angles;
|
|
real_euler_angles2d end_aiming_angles;
|
|
real_euler_angles2d end_angular_velocity;
|
|
real_euler_angles2d desired_aiming_angles;
|
|
real_vector3d clamped_desired_aiming_vector;
|
|
real_vector3d local_aiming_vector;
|
|
real_vector3d local_desired_aiming_vector;
|
|
real_vector3d end_aiming_vector;
|
|
struct unit_acceleration_plan pitch_plan;
|
|
struct unit_acceleration_plan yaw_plan;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2402,
|
|
angular_velocity_limit >= 0.0f);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2403,
|
|
(angular_acceleration_limit > 0.0f) &&
|
|
(angular_acceleration_limit < 10000.0f));
|
|
|
|
if (orientation)
|
|
{
|
|
matrix4x3_inverse_transform_normal(
|
|
orientation,
|
|
aiming_vector,
|
|
&local_aiming_vector);
|
|
matrix4x3_inverse_transform_normal(
|
|
orientation,
|
|
desired_aiming_vector,
|
|
&local_desired_aiming_vector);
|
|
}
|
|
else
|
|
{
|
|
local_aiming_vector = *aiming_vector;
|
|
local_desired_aiming_vector = *desired_aiming_vector;
|
|
}
|
|
|
|
yaw_wraps =
|
|
(aiming_bounds->x1 - aiming_bounds->x0) - 2.f * _pi >
|
|
-_real_epsilon;
|
|
euler_angles2d_from_vector3d(&aiming_angles, &local_aiming_vector);
|
|
euler_angles2d_from_vector3d(
|
|
&desired_aiming_angles,
|
|
&local_desired_aiming_vector);
|
|
|
|
aiming_yaw_in_bounds = TRUE;
|
|
if (yaw_wraps)
|
|
{
|
|
if (desired_aiming_angles.yaw < aiming_bounds->x0)
|
|
{
|
|
desired_aiming_angles.yaw += 2.f * _pi;
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2433,
|
|
desired_aiming_angles.yaw <= aiming_bounds->x1);
|
|
}
|
|
else if (desired_aiming_angles.yaw > aiming_bounds->x1)
|
|
{
|
|
desired_aiming_angles.yaw -= 2.f * _pi;
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2438,
|
|
desired_aiming_angles.yaw >= aiming_bounds->x0);
|
|
}
|
|
}
|
|
else if (desired_aiming_angles.yaw < aiming_bounds->x0)
|
|
{
|
|
desired_aiming_angles.yaw = aiming_bounds->x0;
|
|
aiming_yaw_in_bounds = FALSE;
|
|
}
|
|
else if (desired_aiming_angles.yaw > aiming_bounds->x1)
|
|
{
|
|
desired_aiming_angles.yaw = aiming_bounds->x1;
|
|
aiming_yaw_in_bounds = FALSE;
|
|
}
|
|
|
|
if (desired_aiming_angles.pitch < aiming_bounds->y0)
|
|
{
|
|
desired_aiming_angles.pitch = aiming_bounds->y0;
|
|
}
|
|
else if (desired_aiming_angles.pitch > aiming_bounds->y1)
|
|
{
|
|
desired_aiming_angles.pitch = aiming_bounds->y1;
|
|
}
|
|
else if (aiming_yaw_in_bounds)
|
|
{
|
|
clamped_desired_aiming_vector = *desired_aiming_vector;
|
|
goto desired_aiming_vector_ready;
|
|
}
|
|
|
|
vector3d_from_euler_angles2d(
|
|
&clamped_desired_aiming_vector,
|
|
&desired_aiming_angles);
|
|
if (orientation)
|
|
{
|
|
matrix4x3_transform_normal(
|
|
orientation,
|
|
&clamped_desired_aiming_vector,
|
|
&clamped_desired_aiming_vector);
|
|
normalize3d(&clamped_desired_aiming_vector);
|
|
}
|
|
|
|
desired_aiming_vector_ready:
|
|
{
|
|
real angular_speed;
|
|
real_vector3d velocity_axis;
|
|
|
|
velocity_axis = *aiming_velocity;
|
|
angular_speed = normalize3d(&velocity_axis);
|
|
if (angular_speed != 0.f)
|
|
{
|
|
real_euler_angles2d rotated_angles;
|
|
real_vector3d rotated_aiming_vector;
|
|
|
|
rotated_aiming_vector = local_aiming_vector;
|
|
rotate_vector_about_axis(
|
|
&rotated_aiming_vector,
|
|
&velocity_axis,
|
|
sine(angular_speed),
|
|
cosine(angular_speed));
|
|
euler_angles2d_from_vector3d(&rotated_angles, &rotated_aiming_vector);
|
|
aiming_angular_velocity.yaw = rotated_angles.yaw - aiming_angles.yaw;
|
|
aiming_angular_velocity.pitch = rotated_angles.pitch - aiming_angles.pitch;
|
|
}
|
|
else
|
|
{
|
|
aiming_angular_velocity.yaw = 0.f;
|
|
aiming_angular_velocity.pitch = 0.f;
|
|
}
|
|
}
|
|
|
|
aiming_error.yaw = aiming_angles.yaw - desired_aiming_angles.yaw;
|
|
aiming_error.pitch = aiming_angles.pitch - desired_aiming_angles.pitch;
|
|
if (yaw_wraps)
|
|
{
|
|
if (aiming_error.yaw > _pi)
|
|
{
|
|
aiming_error.yaw -= 2.f * _pi;
|
|
}
|
|
else if (aiming_error.yaw < -_pi)
|
|
{
|
|
aiming_error.yaw += 2.f * _pi;
|
|
}
|
|
}
|
|
|
|
unit_euler_axis_buildplan(
|
|
aiming_error.yaw,
|
|
aiming_angular_velocity.yaw,
|
|
angular_velocity_limit,
|
|
angular_acceleration_limit,
|
|
&yaw_plan);
|
|
unit_euler_axis_buildplan(
|
|
aiming_error.pitch,
|
|
aiming_angular_velocity.pitch,
|
|
angular_velocity_limit,
|
|
angular_acceleration_limit,
|
|
&pitch_plan);
|
|
unit_euler_axis_couple(
|
|
&yaw_plan,
|
|
&pitch_plan,
|
|
angular_velocity_limit,
|
|
angular_acceleration_limit);
|
|
|
|
yaw_complete = unit_euler_axis_doplan(
|
|
&yaw_plan,
|
|
1.f,
|
|
aiming_error.yaw,
|
|
&aiming_error.yaw,
|
|
aiming_angular_velocity.yaw,
|
|
&end_angular_velocity.yaw);
|
|
pitch_complete = unit_euler_axis_doplan(
|
|
&pitch_plan,
|
|
1.f,
|
|
aiming_error.pitch,
|
|
&aiming_error.pitch,
|
|
aiming_angular_velocity.pitch,
|
|
&end_angular_velocity.pitch);
|
|
|
|
if (yaw_complete && pitch_complete)
|
|
{
|
|
*aiming_vector = clamped_desired_aiming_vector;
|
|
*aiming_velocity = *global_zero_vector3d;
|
|
}
|
|
else
|
|
{
|
|
end_aiming_angles.yaw = aiming_error.yaw + desired_aiming_angles.yaw;
|
|
end_aiming_angles.pitch = aiming_error.pitch + desired_aiming_angles.pitch;
|
|
|
|
if (yaw_wraps)
|
|
{
|
|
if (end_aiming_angles.yaw < aiming_bounds->x0)
|
|
{
|
|
end_aiming_angles.yaw += 2.f * _pi;
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2725,
|
|
end_aiming_angles.yaw <= aiming_bounds->x1);
|
|
}
|
|
else if (end_aiming_angles.yaw > aiming_bounds->x1)
|
|
{
|
|
end_aiming_angles.yaw -= 2.f * _pi;
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2730,
|
|
end_aiming_angles.yaw >= aiming_bounds->x0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
end_aiming_angles.yaw = PIN(
|
|
end_aiming_angles.yaw,
|
|
aiming_bounds->x0,
|
|
aiming_bounds->x1);
|
|
}
|
|
|
|
end_aiming_angles.pitch = PIN(
|
|
end_aiming_angles.pitch,
|
|
aiming_bounds->y0,
|
|
aiming_bounds->y1);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2739,
|
|
(end_aiming_angles.yaw >= aiming_bounds->x0) &&
|
|
(end_aiming_angles.yaw <= aiming_bounds->x1));
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2740,
|
|
(end_aiming_angles.pitch >= aiming_bounds->y0) &&
|
|
(end_aiming_angles.pitch <= aiming_bounds->y1));
|
|
|
|
vector3d_from_euler_angles2d(&end_aiming_vector, &end_aiming_angles);
|
|
{
|
|
real_euler_angles2d further_angles;
|
|
real_vector3d further_vector;
|
|
|
|
further_angles.yaw =
|
|
end_angular_velocity.yaw + end_aiming_angles.yaw;
|
|
further_angles.pitch =
|
|
end_angular_velocity.pitch + end_aiming_angles.pitch;
|
|
vector3d_from_euler_angles2d(&further_vector, &further_angles);
|
|
|
|
dot_product = dot_product3d(&end_aiming_vector, &further_vector);
|
|
dot_product = PIN(dot_product, -1.f, 1.f);
|
|
cross_product3d(
|
|
&end_aiming_vector,
|
|
&further_vector,
|
|
aiming_velocity);
|
|
}
|
|
normalize3d(aiming_velocity);
|
|
angle = MIN(arccosine(dot_product), angular_velocity_limit);
|
|
scale_vector3d(aiming_velocity, angle, aiming_velocity);
|
|
|
|
if (orientation)
|
|
{
|
|
matrix4x3_transform_normal(
|
|
orientation,
|
|
&end_aiming_vector,
|
|
aiming_vector);
|
|
normalize3d(aiming_vector);
|
|
}
|
|
else
|
|
{
|
|
*aiming_vector = end_aiming_vector;
|
|
}
|
|
}
|
|
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
2793,
|
|
aiming_vector);
|
|
|
|
return;
|
|
}
|
|
static boolean unit_animation_aiming_screen(
|
|
struct unit_animation *animation)
|
|
{
|
|
boolean result = !animation->action;
|
|
|
|
if (animation->state>=_unit_state_hard_ping)
|
|
{
|
|
if (animation->state<=_unit_state_resurrect_back ||
|
|
animation->state==_unit_state_leap_melee)
|
|
{
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
static boolean unit_animation_state_can_be_entered_without_animation(
|
|
short state)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
switch (state)
|
|
{
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_melee_continuous:
|
|
case _unit_state_throw_grenade:
|
|
case _unit_state_leap_start:
|
|
case _unit_state_leap_melee:
|
|
result = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
static short unit_animation_compute_interpolation_frame_count(
|
|
short new_state,
|
|
short old_state)
|
|
{
|
|
short result = 6;
|
|
|
|
if ((new_state==_unit_state_idle ||
|
|
new_state==_unit_state_turn_left ||
|
|
new_state==_unit_state_turn_right) &&
|
|
(old_state==_unit_state_idle ||
|
|
old_state==_unit_state_turn_left ||
|
|
old_state==_unit_state_turn_right))
|
|
{
|
|
result = 1;
|
|
}
|
|
|
|
if (new_state==_unit_state_land_hard ||
|
|
new_state==_unit_state_land_soft)
|
|
{
|
|
result = 2;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
static long unit_animation_state_get_aiming_screen_index(
|
|
short state)
|
|
{
|
|
long result = NONE;
|
|
|
|
switch (state)
|
|
{
|
|
case _unit_state_idle:
|
|
case _unit_state_turn_left:
|
|
case _unit_state_turn_right:
|
|
case _unit_state_flying_front:
|
|
case _unit_state_flying_back:
|
|
case _unit_state_flying_left:
|
|
case _unit_state_flying_right:
|
|
case _unit_state_airborne:
|
|
case _unit_state_land_soft:
|
|
case _unit_state_land_hard:
|
|
case _unit_state_opening:
|
|
case _unit_state_closing:
|
|
result = _unit_weapon_class_animation_aiming_still;
|
|
break;
|
|
case _unit_state_move_front:
|
|
case _unit_state_move_back:
|
|
case _unit_state_move_left:
|
|
case _unit_state_move_right:
|
|
case _unit_state_stunned_move_front:
|
|
case _unit_state_stunned_move_back:
|
|
case _unit_state_stunned_move_left:
|
|
case _unit_state_stunned_move_right:
|
|
case _unit_state_slide_front:
|
|
case _unit_state_slide_back:
|
|
case _unit_state_slide_left:
|
|
case _unit_state_slide_right:
|
|
result = _unit_weapon_class_animation_aiming_moving;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
static void unit_set_animation(
|
|
long unit_index,
|
|
long animation_graph_index,
|
|
short animation_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
unit->object.animation.animation_graph_index = animation_graph_index;
|
|
unit->object.animation.state.index = animation_index;
|
|
unit->object.animation.state.frame_index = 0;
|
|
|
|
if (debug_unit_all_animations)
|
|
{
|
|
char const *animation_name = "<none>";
|
|
|
|
if (animation_graph_index != NONE)
|
|
{
|
|
struct animation_graph *animation_graph =
|
|
animation_graph_definition_get(animation_graph_index);
|
|
|
|
if (animation_index != NONE)
|
|
{
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
animation_name = animation->name;
|
|
}
|
|
}
|
|
|
|
if (ai_debug.selected_actor_index == NONE ||
|
|
unit->unit.actor_index == ai_debug.selected_actor_index ||
|
|
unit->unit.swarm_actor_index == ai_debug.selected_actor_index)
|
|
{
|
|
console_printf(
|
|
FALSE,
|
|
"%s: animation %s",
|
|
tag_name_strip_path(tag_get_name(unit->definition_index)),
|
|
animation_name);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static short unit_animation_update(
|
|
long unit_index,
|
|
long animation_graph_index,
|
|
struct animation_state *animation)
|
|
{
|
|
long sound_index;
|
|
short result = animation_update_internal(
|
|
1,
|
|
animation_graph_index,
|
|
animation,
|
|
&sound_index);
|
|
|
|
if (sound_index!=NONE)
|
|
{
|
|
object_impulse_sound_new(
|
|
unit_index,
|
|
sound_index,
|
|
0,
|
|
global_origin3d,
|
|
global_forward3d,
|
|
1.f);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static char const *unit_get_current_weapon_label(
|
|
long unit_index)
|
|
{
|
|
long weapon_index = unit_inventory_get_weapon(
|
|
unit_index,
|
|
unit_get(unit_index)->unit.current_weapon_index);
|
|
char const *weapon_label;
|
|
|
|
if (weapon_index==NONE)
|
|
{
|
|
weapon_label = "unarmed";
|
|
}
|
|
else
|
|
{
|
|
weapon_label = weapon_get_label(weapon_index);
|
|
}
|
|
|
|
return weapon_label;
|
|
}
|
|
|
|
short unit_update_animation(
|
|
long unit_index,
|
|
struct unit_animation_update_data *data)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct unit_definition *unit_definition;
|
|
short desired_state;
|
|
word result;
|
|
boolean apply_state;
|
|
short animation_update_result;
|
|
boolean can_change_animation;
|
|
struct biped_datum *biped;
|
|
struct biped_definition *biped_definition;
|
|
struct model *model;
|
|
struct animation_graph *animation_graph;
|
|
struct animation *animation;
|
|
struct unit_datum *parent_unit;
|
|
struct unit_definition *parent_definition;
|
|
struct unit_seat *seat;
|
|
real_vector3d exit_velocity;
|
|
real_matrix4x3 old_world_matrix;
|
|
|
|
unit = unit_get(unit_index);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
desired_state = data->state_desired;
|
|
result = 0;
|
|
apply_state = FALSE;
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0xb61,
|
|
desired_state>=0 && desired_state<NUMBER_OF_UNIT_STATES);
|
|
|
|
if (unit->object.parent_object_index==NONE &&
|
|
!TEST_FLAG(
|
|
unit->object.damage_flags,
|
|
_object_dead_bit))
|
|
{
|
|
short desired_base_seat_index = NONE;
|
|
|
|
switch (unit->unit.animation.desired_state)
|
|
{
|
|
case _unit_animation_state_asleep:
|
|
desired_base_seat_index = _unit_base_seat_asleep;
|
|
break;
|
|
case _unit_animation_state_alert:
|
|
desired_base_seat_index = _unit_base_seat_alert;
|
|
break;
|
|
case _unit_animation_state_suspicious:
|
|
desired_base_seat_index = _unit_base_seat_alert;
|
|
break;
|
|
case _unit_animation_state_in_combat:
|
|
desired_base_seat_index =
|
|
(data->crouching != FALSE) + _unit_base_seat_stand;
|
|
break;
|
|
case _unit_animation_state_wary:
|
|
desired_base_seat_index = _unit_base_seat_stand;
|
|
break;
|
|
case _unit_animation_state_flee:
|
|
desired_base_seat_index = _unit_base_seat_flee;
|
|
break;
|
|
case _unit_animation_state_flaming:
|
|
desired_base_seat_index = _unit_base_seat_flaming;
|
|
break;
|
|
}
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0xb73,
|
|
desired_base_seat_index!=NONE);
|
|
|
|
if (unit->unit.player_index!=NONE &&
|
|
magic_base_animation_seat_index!=NONE)
|
|
{
|
|
if (magic_base_animation_seat_index<0)
|
|
{
|
|
desired_base_seat_index = _unit_base_seat_asleep;
|
|
}
|
|
else
|
|
{
|
|
desired_base_seat_index = MIN(
|
|
magic_base_animation_seat_index,
|
|
NUMBER_OF_UNIT_ANIMATION_STATES-1);
|
|
}
|
|
}
|
|
|
|
if (unit->unit.magic_seat_index!=NONE)
|
|
{
|
|
desired_base_seat_index = unit->unit.magic_seat_index;
|
|
}
|
|
if (TEST_FLAG(
|
|
unit->unit.control_flags,
|
|
_unit_control_force_alert_bit))
|
|
{
|
|
desired_base_seat_index = _unit_base_seat_alert;
|
|
}
|
|
if (unit->unit.flaming_death_delay>0)
|
|
{
|
|
desired_base_seat_index = _unit_base_seat_flaming;
|
|
}
|
|
|
|
if (unit->unit.animation.base_seat_index!=desired_base_seat_index)
|
|
{
|
|
can_change_animation = unit_animation_state_interruptable(
|
|
&unit->unit.animation,
|
|
desired_state);
|
|
if (can_change_animation)
|
|
{
|
|
unit_set_or_test_seat_and_weapon_label(
|
|
unit_index,
|
|
base_seat_label_get(desired_base_seat_index),
|
|
unit_get_current_weapon_label(unit_index),
|
|
TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unit->unit.animation.soft_ping_animation.index!=NONE)
|
|
{
|
|
animation_update_result = unit_animation_update(
|
|
unit_index,
|
|
unit_definition->object.animation_graph.index,
|
|
&unit->unit.animation.soft_ping_animation);
|
|
if (animation_update_result==2)
|
|
{
|
|
unit->unit.animation.soft_ping_animation.index = NONE;
|
|
}
|
|
}
|
|
|
|
if (unit->object.animation.state.index!=NONE)
|
|
{
|
|
animation_update_result = unit_animation_update(
|
|
unit_index,
|
|
unit->object.animation.animation_graph_index,
|
|
&unit->object.animation.state);
|
|
|
|
if (animation_update_result==1)
|
|
{
|
|
switch (unit->unit.animation.state)
|
|
{
|
|
case _unit_state_throw_grenade:
|
|
unit_throw_grenade_release(unit_index, FALSE);
|
|
break;
|
|
case _unit_state_melee_attack:
|
|
case _unit_state_melee_airborne:
|
|
case _unit_state_leap_melee:
|
|
unit_cause_melee_damage(
|
|
unit_index,
|
|
FALSE,
|
|
NONE,
|
|
NONE,
|
|
NONE,
|
|
NONE,
|
|
FALSE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (animation_update_result==2)
|
|
{
|
|
switch (unit->unit.animation.state)
|
|
{
|
|
case _unit_state_leap_start:
|
|
result = TRUE;
|
|
desired_state = _unit_state_leap_airborne;
|
|
break;
|
|
|
|
case _unit_state_dying:
|
|
if (TEST_FLAG(
|
|
unit_definition->unit.flags,
|
|
_unit_is_destroyed_after_dying_bit))
|
|
{
|
|
if (TEST_FLAG(unit->object.flags, _object_at_rest_bit))
|
|
{
|
|
goto destroy_unit;
|
|
}
|
|
if (unit->object.type!=_object_type_biped)
|
|
{
|
|
goto finish_dying;
|
|
}
|
|
|
|
biped = biped_get(unit_index);
|
|
biped_definition =
|
|
biped_definition_get(biped->definition_index);
|
|
if (!TEST_FLAG(biped->biped.flags, _biped_airborne_bit) ||
|
|
TEST_FLAG(
|
|
biped_definition->biped.flags,
|
|
_biped_has_no_dying_airborne_bit))
|
|
{
|
|
destroy_unit:
|
|
unit_destroy(unit_index);
|
|
goto finish_dying_animation;
|
|
}
|
|
}
|
|
|
|
if (unit->object.type==_object_type_biped)
|
|
{
|
|
biped_start_limp_body_physics(unit_index);
|
|
}
|
|
|
|
finish_dying:
|
|
SET_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_ignore_translation_bit,
|
|
TRUE);
|
|
--unit->object.animation.state.frame_index;
|
|
finish_dying_animation:
|
|
break;
|
|
|
|
case _unit_state_exiting_seat:
|
|
model = model_definition_get(
|
|
unit_definition->object.model.index);
|
|
animation_graph = animation_graph_definition_get(
|
|
unit->object.animation.animation_graph_index);
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->object.animation.state.index,
|
|
struct animation);
|
|
|
|
animation_get_root_velocity(
|
|
model,
|
|
animation,
|
|
unit->object.animation.state.frame_index,
|
|
&exit_velocity);
|
|
matrix4x3_transform_vector(
|
|
object_get_world_matrix(unit_index, &old_world_matrix),
|
|
&exit_velocity,
|
|
&exit_velocity);
|
|
unit_exit_seat_end(unit_index);
|
|
add_vectors3d(
|
|
&unit->object.translational_velocity,
|
|
&exit_velocity,
|
|
&unit->object.translational_velocity);
|
|
break;
|
|
|
|
case _unit_state_opening:
|
|
case _unit_state_closing:
|
|
--unit->object.animation.state.frame_index;
|
|
break;
|
|
|
|
case _unit_state_entering_seat:
|
|
parent_unit = unit_get(unit->object.parent_object_index);
|
|
parent_definition = unit_definition_get(parent_unit->definition_index);
|
|
seat = TAG_BLOCK_GET_ELEMENT(
|
|
&parent_definition->unit.seats,
|
|
unit->unit.parent_seat_index,
|
|
struct unit_seat);
|
|
|
|
object_set_visibility(
|
|
unit_index,
|
|
!TEST_FLAG(seat->flags, _unit_seat_invisible_bit));
|
|
if (parent_unit->unit.driver_object_index==unit_index)
|
|
{
|
|
unit_close(unit->object.parent_object_index);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!unit_animation_state_loops(&unit->unit.animation))
|
|
{
|
|
apply_state = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unit->unit.animation.action_animation.index!=NONE)
|
|
{
|
|
animation_update_result = unit_animation_update(
|
|
unit_index,
|
|
unit_definition->object.animation_graph.index,
|
|
&unit->unit.animation.action_animation);
|
|
if (animation_update_result==2)
|
|
{
|
|
struct unit_datum *interpolated_unit;
|
|
|
|
object_start_interpolation(unit_index, 6);
|
|
interpolated_unit = unit_get(unit_index);
|
|
interpolated_unit->unit.animation.action = FALSE;
|
|
interpolated_unit->unit.animation.action_animation.index = NONE;
|
|
}
|
|
}
|
|
|
|
if (unit->unit.animation.overlay_action_animation.index!=NONE)
|
|
{
|
|
animation_update_result = unit_animation_update(
|
|
unit_index,
|
|
unit_definition->object.animation_graph.index,
|
|
&unit->unit.animation.overlay_action_animation);
|
|
|
|
switch (animation_update_result)
|
|
{
|
|
case 2:
|
|
case 4:
|
|
if (!unit_animation_overlay_action_loops(&unit->unit.animation))
|
|
{
|
|
unit->unit.animation.overlay_action = FALSE;
|
|
unit->unit.animation.overlay_action_animation.index = NONE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (apply_state ||
|
|
(desired_state!=unit->unit.animation.state &&
|
|
unit_animation_state_interruptable(&unit->unit.animation, desired_state)))
|
|
{
|
|
unit_animation_set_state(unit_index, desired_state);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean unit_animation_set_state(
|
|
long unit_index,
|
|
short new_state)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
struct animation_graph *animation_graph =
|
|
animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
struct animation_graph_unit_seat *unit_seat =
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
struct animation_graph_weapon_class *weapon_class =
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&unit_seat->weapon_classes,
|
|
unit->unit.animation.weapon_index,
|
|
struct animation_graph_weapon_class);
|
|
long animation_graph_index;
|
|
short interpolation_frame_count;
|
|
boolean old_state_is_none;
|
|
boolean changed_state;
|
|
boolean result;
|
|
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_class->weapon_types,
|
|
unit->unit.animation.weapon_type_index,
|
|
struct animation_graph_weapon_type);
|
|
|
|
old_state_is_none = unit->unit.animation.state == NONE;
|
|
changed_state = FALSE;
|
|
result = TRUE;
|
|
|
|
if (old_state_is_none ||
|
|
new_state != unit->unit.animation.state)
|
|
{
|
|
short animation_index;
|
|
short weapon_class_animation_index = NONE;
|
|
short unit_seat_animation_index = NONE;
|
|
|
|
if (unit->unit.animation.state ==
|
|
_unit_state_throw_grenade)
|
|
{
|
|
unit_throw_grenade_release(unit_index, TRUE);
|
|
}
|
|
|
|
switch (new_state)
|
|
{
|
|
case _unit_state_idle:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_idle;
|
|
break;
|
|
case _unit_state_gesture:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_gesture;
|
|
break;
|
|
case _unit_state_turn_left:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_turning_left;
|
|
break;
|
|
case _unit_state_turn_right:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_turning_right;
|
|
break;
|
|
case _unit_state_move_front:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_front;
|
|
break;
|
|
case _unit_state_move_back:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_back;
|
|
break;
|
|
case _unit_state_move_left:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_left;
|
|
break;
|
|
case _unit_state_move_right:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_right;
|
|
break;
|
|
case _unit_state_stunned_move_front:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_wounded_front;
|
|
break;
|
|
case _unit_state_stunned_move_back:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_wounded_back;
|
|
break;
|
|
case _unit_state_stunned_move_left:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_wounded_left;
|
|
break;
|
|
case _unit_state_stunned_move_right:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_moving_wounded_right;
|
|
break;
|
|
case _unit_state_slide_front:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_sliding_front;
|
|
break;
|
|
case _unit_state_slide_back:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_sliding_back;
|
|
break;
|
|
case _unit_state_slide_left:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_sliding_left;
|
|
break;
|
|
case _unit_state_slide_right:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_sliding_right;
|
|
break;
|
|
case _unit_state_airborne:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_airborne;
|
|
break;
|
|
case _unit_state_land_soft:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_land_soft;
|
|
break;
|
|
case _unit_state_land_hard:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_land_hard;
|
|
break;
|
|
case _unit_state_throw_grenade:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_throw_grenade;
|
|
break;
|
|
case _unit_state_melee_attack:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_melee_attack;
|
|
break;
|
|
case _unit_state_melee_airborne:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_melee_airborne;
|
|
break;
|
|
case _unit_state_melee_continuous:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_melee_continuous;
|
|
break;
|
|
case _unit_state_resurrect_front:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_resurrect_front;
|
|
break;
|
|
case _unit_state_resurrect_back:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_resurrect_back;
|
|
break;
|
|
case _unit_state_feeding:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_feeding;
|
|
break;
|
|
case _unit_state_leap_start:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_leap_start;
|
|
break;
|
|
case _unit_state_leap_airborne:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_leap_airborne;
|
|
break;
|
|
case _unit_state_leap_melee:
|
|
weapon_class_animation_index = _unit_weapon_class_animation_leap_melee;
|
|
break;
|
|
case _unit_state_flying_front:
|
|
unit_seat_animation_index = _unit_seat_animation_flying_front;
|
|
break;
|
|
case _unit_state_flying_back:
|
|
unit_seat_animation_index = _unit_seat_animation_flying_back;
|
|
break;
|
|
case _unit_state_flying_left:
|
|
unit_seat_animation_index = _unit_seat_animation_flying_left;
|
|
break;
|
|
case _unit_state_flying_right:
|
|
unit_seat_animation_index = _unit_seat_animation_flying_right;
|
|
break;
|
|
case _unit_state_dying_airborne:
|
|
unit_seat_animation_index = _unit_seat_animation_airborne_dead;
|
|
break;
|
|
case _unit_state_dying:
|
|
unit_seat_animation_index = _unit_seat_animation_landing_dead;
|
|
break;
|
|
case _unit_state_opening:
|
|
unit_seat_animation_index = _unit_seat_animation_opening;
|
|
break;
|
|
case _unit_state_closing:
|
|
unit_seat_animation_index = _unit_seat_animation_closing;
|
|
break;
|
|
case _unit_state_hovering:
|
|
unit_seat_animation_index = _unit_seat_animation_hovering;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Get weapon class animation
|
|
if (weapon_class_animation_index!=NONE)
|
|
{
|
|
if (weapon_class_animation_index>=0 && weapon_class_animation_index<weapon_class->animations.count)
|
|
{
|
|
animation_index = animation_graph_animation_index_get(&weapon_class->animations)[weapon_class_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
}
|
|
// Get unit seat animation
|
|
else
|
|
{
|
|
if (unit_seat_animation_index!=NONE)
|
|
{
|
|
if (unit_seat_animation_index>=0 && unit_seat_animation_index<unit_seat->animations.count)
|
|
{
|
|
animation_index = animation_graph_animation_index_get(&unit_seat->animations)[unit_seat_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
}
|
|
|
|
if (debug_unit_animations &&
|
|
unit->object.type == _object_type_biped &&
|
|
animation_index == NONE)
|
|
{
|
|
char const *weapon_animation_name;
|
|
char const *seat_or_weapon_class_name;
|
|
|
|
if (weapon_class_animation_index != NONE)
|
|
{
|
|
weapon_animation_name = animation_list_get_string(
|
|
&weapon_class_animation_list,
|
|
weapon_class_animation_index);
|
|
}
|
|
else
|
|
{
|
|
weapon_animation_name = "";
|
|
}
|
|
|
|
if (unit_seat_animation_index != NONE)
|
|
{
|
|
seat_or_weapon_class_name = animation_list_get_string(
|
|
&unit_seat_animation_list,
|
|
unit_seat_animation_index);
|
|
}
|
|
else
|
|
{
|
|
seat_or_weapon_class_name = weapon_class->label;
|
|
}
|
|
|
|
console_warning(
|
|
"MISSING: %s '%s %s %s'",
|
|
tag_name_strip_path(
|
|
unit_definition->object.animation_graph.name),
|
|
unit_seat->label,
|
|
seat_or_weapon_class_name,
|
|
weapon_animation_name);
|
|
}
|
|
|
|
if (animation_index == NONE)
|
|
{
|
|
if (!unit_animation_state_can_be_entered_without_animation(new_state))
|
|
{
|
|
result = FALSE;
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
{
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
animation_index =
|
|
animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
animation_index);
|
|
unit_set_animation(
|
|
unit_index,
|
|
unit_definition->object.animation_graph.index,
|
|
animation_index);
|
|
|
|
interpolation_frame_count =
|
|
unit_animation_compute_interpolation_frame_count(
|
|
new_state,
|
|
unit->unit.animation.state);
|
|
|
|
changed_state = TRUE;
|
|
}
|
|
}
|
|
|
|
{
|
|
short aiming_screen_animation_index =
|
|
(short)unit_animation_state_get_aiming_screen_index(new_state);
|
|
|
|
if (old_state_is_none ||
|
|
aiming_screen_animation_index !=
|
|
(short)unit_animation_state_get_aiming_screen_index(
|
|
unit->unit.animation.state))
|
|
{
|
|
long animation_index;
|
|
|
|
if (aiming_screen_animation_index >= 0 &&
|
|
aiming_screen_animation_index <
|
|
weapon_class->animations.count)
|
|
{
|
|
animation_index =
|
|
animation_graph_animation_index_get(
|
|
&weapon_class->animations)
|
|
[aiming_screen_animation_index].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
unit->unit.animation.aiming_screen_index =
|
|
animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
animation_index);
|
|
|
|
if (debug_unit_animations &&
|
|
unit->object.type == _object_type_biped &&
|
|
unit->unit.animation.aiming_screen_index == NONE &&
|
|
aiming_screen_animation_index != NONE)
|
|
{
|
|
console_warning(
|
|
"MISSING: %s '%s %s %s'",
|
|
tag_name_strip_path(
|
|
unit_definition->object.animation_graph.name),
|
|
unit_seat->label,
|
|
weapon_class->label,
|
|
animation_list_get_string(
|
|
&weapon_class_animation_list,
|
|
aiming_screen_animation_index));
|
|
}
|
|
|
|
interpolation_frame_count = 6;
|
|
changed_state = TRUE;
|
|
|
|
if (old_state_is_none)
|
|
{
|
|
long animation_index;
|
|
|
|
if (_unit_seat_animation_looking >= 0 &&
|
|
_unit_seat_animation_looking <
|
|
unit_seat->animations.count)
|
|
{
|
|
animation_index =
|
|
animation_graph_animation_index_get(
|
|
&unit_seat->animations)
|
|
[_unit_seat_animation_looking].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
|
|
animation_graph_index =
|
|
unit_definition->object.animation_graph.index;
|
|
unit->unit.animation.looking_screen_index =
|
|
animation_choose_random_permutation_internal(
|
|
TRUE,
|
|
animation_graph_index,
|
|
animation_index);
|
|
|
|
if (debug_unit_animations &&
|
|
unit->object.type == _object_type_biped &&
|
|
unit->unit.animation.looking_screen_index == NONE)
|
|
{
|
|
console_warning(
|
|
"MISSING: %s '%s %s'",
|
|
tag_name_strip_path(
|
|
unit_definition->object.animation_graph.name),
|
|
unit_seat->label,
|
|
animation_list_get_string(
|
|
&unit_seat_animation_list,
|
|
_unit_seat_animation_looking));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (changed_state)
|
|
{
|
|
object_start_interpolation(
|
|
unit_index,
|
|
interpolation_frame_count);
|
|
}
|
|
|
|
unit->unit.animation.state = (char)new_state;
|
|
}
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
void unit_preprocess_node_orientations(
|
|
long unit_index,
|
|
struct real_orientation *node_orientations)
|
|
{
|
|
struct unit_datum *unit;
|
|
struct animation_graph *animation_graph;
|
|
struct animation_graph_unit_seat *unit_seat;
|
|
real_matrix4x3 matrix;
|
|
struct unit_definition *unit_definition;
|
|
|
|
unit = unit_get(unit_index);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
|
|
if (unit->unit.animation.action_animation.index != NONE)
|
|
{
|
|
replacement_animation_apply(
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->unit.animation.action_animation.index,
|
|
struct animation),
|
|
unit->unit.animation.action_animation.frame_index,
|
|
node_orientations);
|
|
}
|
|
|
|
if (unit->unit.animation.overlay_action_animation.index != NONE)
|
|
{
|
|
overlay_animation_apply(
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->unit.animation.overlay_action_animation.index,
|
|
struct animation),
|
|
unit->unit.animation.overlay_action_animation.frame_index,
|
|
node_orientations);
|
|
}
|
|
|
|
if (unit->unit.animation.soft_ping_animation.index != NONE)
|
|
{
|
|
overlay_animation_apply(
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->unit.animation.soft_ping_animation.index,
|
|
struct animation),
|
|
unit->unit.animation.soft_ping_animation.frame_index,
|
|
node_orientations);
|
|
}
|
|
|
|
unit_verify_vectors(unit_index, "unit-preprocess-nodes");
|
|
unit->unit.animation.aiming_with_euler_screen = FALSE;
|
|
unit->unit.animation.looking_with_euler_screen = FALSE;
|
|
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_simple_creature_bit) &&
|
|
unit->unit.animation.seat_index != NONE)
|
|
{
|
|
unit_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
|
|
if (unit->unit.animation.emotion_index != NONE)
|
|
{
|
|
short animation_index;
|
|
|
|
if (_unit_seat_animation_emotions < unit_seat->animations.count)
|
|
{
|
|
animation_index = animation_graph_animation_index_get(
|
|
&unit_seat->animations)[_unit_seat_animation_emotions].animation_index;
|
|
}
|
|
else
|
|
{
|
|
animation_index = NONE;
|
|
}
|
|
|
|
if (unit->unit.override_emotion_animation_index != NONE)
|
|
{
|
|
animation_index = unit->unit.override_emotion_animation_index;
|
|
}
|
|
|
|
if (animation_index != NONE)
|
|
{
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
|
|
if (unit->unit.animation.emotion_index >= 0 &&
|
|
unit->unit.animation.emotion_index < animation->frame_count)
|
|
{
|
|
overlay_animation_apply(
|
|
animation,
|
|
unit->unit.animation.emotion_index,
|
|
node_orientations);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unit->unit.mouth_aperture > 0.f &&
|
|
_unit_seat_animation_mouth_aperture < unit_seat->animations.count)
|
|
{
|
|
short animation_index;
|
|
|
|
animation_index = animation_graph_animation_index_get(
|
|
&unit_seat->animations)[_unit_seat_animation_mouth_aperture].animation_index;
|
|
|
|
if (animation_index != NONE)
|
|
{
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
|
|
overlay_animation_apply_scaled(
|
|
animation,
|
|
0,
|
|
unit->unit.mouth_aperture,
|
|
node_orientations);
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(
|
|
unit->unit.animation.flags,
|
|
_unit_animation_showing_acceleration_bit))
|
|
{
|
|
short animation_index;
|
|
long acceleration_index;
|
|
|
|
for (acceleration_index = 0;
|
|
acceleration_index < _unit_seat_animation_push_impact -
|
|
_unit_seat_animation_acceleration_front_back;
|
|
acceleration_index++)
|
|
{
|
|
if (_unit_seat_animation_acceleration_front_back +
|
|
acceleration_index >= 0 &&
|
|
_unit_seat_animation_acceleration_front_back +
|
|
acceleration_index < unit_seat->animations.count)
|
|
{
|
|
animation_index = animation_graph_animation_index_get(
|
|
&unit_seat->animations)[
|
|
_unit_seat_animation_acceleration_front_back +
|
|
acceleration_index].animation_index;
|
|
|
|
if (animation_index != NONE)
|
|
{
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
|
|
overlay_animation_apply_continuous(
|
|
animation,
|
|
(animation->frame_count - 1) *
|
|
unit->unit.seat_acceleration.n[acceleration_index],
|
|
node_orientations);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_has_no_aiming_bit) &&
|
|
unit_animation_aiming_screen(&unit->unit.animation))
|
|
{
|
|
real_euler_angles2d relative_aiming_angles;
|
|
struct animation_graph_weapon_class *weapon_class =
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&unit_seat->weapon_classes,
|
|
unit->unit.animation.weapon_index,
|
|
struct animation_graph_weapon_class);
|
|
|
|
relative_aiming_angles = *global_zero_angles2d;
|
|
|
|
if (unit->unit.animation.aiming_screen_index != NONE)
|
|
{
|
|
real_vector3d relative_aiming_vector;
|
|
struct animation_aiming_screen_bounds const *aiming_bounds =
|
|
&weapon_class->aiming_screen_bounds;
|
|
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
1648,
|
|
&unit->unit.aiming_vector);
|
|
|
|
matrix.scale = 1.f;
|
|
object_get_orientation(
|
|
unit_index,
|
|
&matrix.forward,
|
|
&matrix.up);
|
|
cross_product3d(
|
|
&matrix.up,
|
|
&matrix.forward,
|
|
&matrix.left);
|
|
matrix.position = *global_origin3d;
|
|
matrix4x3_inverse_transform_normal(
|
|
&matrix,
|
|
&unit->unit.aiming_vector,
|
|
&relative_aiming_vector);
|
|
|
|
match_assert_valid_real_vector3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
1661,
|
|
&relative_aiming_vector);
|
|
|
|
euler_angles2d_from_vector3d(
|
|
&relative_aiming_angles,
|
|
&relative_aiming_vector);
|
|
|
|
match_assert_valid_real(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
1664,
|
|
relative_aiming_angles.pitch);
|
|
match_assert_valid_real(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
1665,
|
|
relative_aiming_angles.yaw);
|
|
|
|
unit->unit.animation.aiming_with_euler_screen = TRUE;
|
|
unit->unit.animation.aiming_screen_bounds.x0 =
|
|
-(aiming_bounds->negative_yaw_frame_count *
|
|
aiming_bounds->negative_yaw_delta);
|
|
unit->unit.animation.aiming_screen_bounds.x1 =
|
|
aiming_bounds->positive_yaw_frame_count *
|
|
aiming_bounds->positive_yaw_delta;
|
|
unit->unit.animation.aiming_screen_bounds.y0 =
|
|
-(aiming_bounds->negative_pitch_frame_count *
|
|
aiming_bounds->negative_pitch_delta);
|
|
unit->unit.animation.aiming_screen_bounds.y1 =
|
|
aiming_bounds->positive_pitch_frame_count *
|
|
aiming_bounds->positive_pitch_delta;
|
|
aiming_screen_apply(
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->unit.animation.aiming_screen_index,
|
|
struct animation),
|
|
aiming_bounds,
|
|
relative_aiming_angles.yaw,
|
|
relative_aiming_angles.pitch,
|
|
node_orientations);
|
|
}
|
|
|
|
if (unit->unit.current_weapon_index != NONE ||
|
|
unit->unit.player_index != NONE)
|
|
{
|
|
struct animation_aiming_screen_bounds const *looking_bounds =
|
|
&unit_seat->looking_screen_bounds;
|
|
|
|
if (unit->unit.animation.looking_screen_index != NONE)
|
|
{
|
|
real_vector3d relative_looking_vector;
|
|
real_euler_angles2d relative_looking_angles;
|
|
|
|
matrix.scale = 1.f;
|
|
object_get_orientation(
|
|
unit_index,
|
|
&matrix.forward,
|
|
&matrix.up);
|
|
cross_product3d(
|
|
&matrix.up,
|
|
&matrix.forward,
|
|
&matrix.left);
|
|
matrix.position = *global_origin3d;
|
|
matrix4x3_inverse_transform_normal(
|
|
&matrix,
|
|
&unit->unit.looking_vector,
|
|
&relative_looking_vector);
|
|
|
|
match_assert_valid_real_vector3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
1703,
|
|
&relative_looking_vector);
|
|
|
|
euler_angles2d_from_vector3d(
|
|
&relative_looking_angles,
|
|
&relative_looking_vector);
|
|
relative_looking_angles.yaw -= relative_aiming_angles.yaw;
|
|
relative_looking_angles.pitch -= relative_aiming_angles.pitch;
|
|
|
|
match_assert_valid_real(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
1710,
|
|
relative_looking_angles.pitch);
|
|
match_assert_valid_real(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
1711,
|
|
relative_looking_angles.yaw);
|
|
|
|
unit->unit.animation.looking_with_euler_screen = TRUE;
|
|
unit->unit.animation.looking_screen_bounds.x0 =
|
|
-(looking_bounds->negative_yaw_frame_count *
|
|
looking_bounds->negative_yaw_delta);
|
|
unit->unit.animation.looking_screen_bounds.x1 =
|
|
looking_bounds->positive_yaw_frame_count *
|
|
looking_bounds->positive_yaw_delta;
|
|
unit->unit.animation.looking_screen_bounds.y0 =
|
|
-(looking_bounds->negative_pitch_frame_count *
|
|
looking_bounds->negative_pitch_delta);
|
|
unit->unit.animation.looking_screen_bounds.y1 =
|
|
looking_bounds->positive_pitch_frame_count *
|
|
looking_bounds->positive_pitch_delta;
|
|
aiming_screen_apply(
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
unit->unit.animation.looking_screen_index,
|
|
struct animation),
|
|
looking_bounds,
|
|
relative_looking_angles.yaw,
|
|
relative_looking_angles.pitch,
|
|
node_orientations);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_postprocess_node_matrices(
|
|
long unit_index,
|
|
real_matrix4x3 *node_matrices)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition =
|
|
unit_definition_get(unit->definition_index);
|
|
struct animation_graph *animation_graph;
|
|
struct animation_graph_unit_seat *unit_seat;
|
|
struct animation_graph_weapon_class *weapon_class;
|
|
struct animation_graph_ik_point *ik_point;
|
|
struct animation_graph_ik_point *weapon_ik_point;
|
|
struct unit_animation *animation;
|
|
struct tag_block *ik_points;
|
|
struct tag_block *weapon_ik_points;
|
|
short ik_point_index;
|
|
long ik_element_index;
|
|
boolean weapon_ik_active;
|
|
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_simple_creature_bit) &&
|
|
unit->unit.animation.seat_index!=NONE)
|
|
{
|
|
animation_graph = animation_graph_definition_get(
|
|
unit_definition->object.animation_graph.index);
|
|
unit_seat = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->unit_seats,
|
|
unit->unit.animation.seat_index,
|
|
struct animation_graph_unit_seat);
|
|
weapon_class = TAG_BLOCK_GET_ELEMENT(
|
|
&unit_seat->weapon_classes,
|
|
unit->unit.animation.weapon_index,
|
|
struct animation_graph_weapon_class);
|
|
|
|
if (unit->object.parent_object_index!=NONE &&
|
|
unit_animation_vehicle_ik(&unit->unit.animation))
|
|
{
|
|
ik_points = &unit_seat->ik_points;
|
|
ik_point_index = 0;
|
|
if (ik_points->count>0)
|
|
{
|
|
ik_element_index = 0;
|
|
do
|
|
{
|
|
ik_point = TAG_BLOCK_GET_ELEMENT(
|
|
ik_points,
|
|
ik_element_index,
|
|
struct animation_graph_ik_point);
|
|
|
|
object_inverse_kinematics(
|
|
unit_index,
|
|
ik_point->marker_name,
|
|
unit->object.parent_object_index,
|
|
ik_point->attached_to_marker_name,
|
|
node_matrices);
|
|
|
|
++ik_point_index;
|
|
ik_element_index = ik_point_index;
|
|
}
|
|
while (ik_element_index<ik_points->count);
|
|
}
|
|
}
|
|
|
|
if (unit->unit.current_weapon_index!=NONE)
|
|
{
|
|
animation = &unit->unit.animation;
|
|
weapon_ik_active = unit_animation_weapon_ik(animation);
|
|
if (weapon_ik_active)
|
|
{
|
|
long weapon_ik_point_index = 0;
|
|
weapon_ik_points = &weapon_class->ik_points;
|
|
if (weapon_ik_points->count>0)
|
|
{
|
|
ik_element_index = 0;
|
|
do
|
|
{
|
|
weapon_ik_point = TAG_BLOCK_GET_ELEMENT(
|
|
weapon_ik_points,
|
|
ik_element_index,
|
|
struct animation_graph_ik_point);
|
|
|
|
object_inverse_kinematics(
|
|
unit_index,
|
|
weapon_ik_point->marker_name,
|
|
unit_get_current_weapon_index(unit_index),
|
|
weapon_ik_point->attached_to_marker_name,
|
|
node_matrices);
|
|
|
|
++weapon_ik_point_index;
|
|
ik_element_index = (short)weapon_ik_point_index;
|
|
}
|
|
while (ik_element_index<weapon_ik_points->count);
|
|
}
|
|
|
|
SET_FLAG(
|
|
animation->flags,
|
|
_unit_animation_postpone_weapon_ik_until_interpolation_ends_bit,
|
|
FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void unit_control(
|
|
long unit_index,
|
|
struct unit_control_data const *control_data)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e1,
|
|
magnitude3d(&control_data->throttle)<=3.0f);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e2,
|
|
control_data->animation_state>=0 &&
|
|
control_data->animation_state<NUMBER_OF_UNIT_ANIMATION_STATES);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e3,
|
|
control_data->aiming_speed>=0 &&
|
|
control_data->aiming_speed<NUMBER_OF_UNIT_AIMING_SPEEDS);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e4,
|
|
VALID_FLAGS(control_data->control_flags, NUMBER_OF_UNIT_CONTROL_FLAGS));
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e5,
|
|
&control_data->facing_vector);
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e6,
|
|
&control_data->aiming_vector);
|
|
match_assert_valid_real_normal3d(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e7,
|
|
&control_data->looking_vector);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e8,
|
|
control_data->weapon_index==NONE ||
|
|
(control_data->weapon_index>=0 &&
|
|
control_data->weapon_index<MAXIMUM_WEAPONS_PER_UNIT));
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5e9,
|
|
control_data->grenade_index==NONE ||
|
|
(control_data->grenade_index>=0 &&
|
|
control_data->grenade_index<NUMBER_OF_UNIT_GRENADE_TYPES));
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5ea,
|
|
control_data->zoom_level==NONE || (control_data->zoom_level>=0));
|
|
match_assert_valid_real(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
0x5eb,
|
|
control_data->primary_trigger);
|
|
|
|
unit->unit.throttle = control_data->throttle;
|
|
unit->unit.primary_trigger = control_data->primary_trigger;
|
|
unit->unit.aiming_speed = control_data->aiming_speed;
|
|
if (control_data->weapon_index!=NONE)
|
|
{
|
|
unit->unit.desired_weapon_index = control_data->weapon_index;
|
|
}
|
|
if (control_data->grenade_index!=NONE)
|
|
{
|
|
unit->unit.desired_grenade_index = (char)control_data->grenade_index;
|
|
}
|
|
unit->unit.desired_zoom_level = (char)control_data->zoom_level;
|
|
unit->unit.control_flags = control_data->control_flags;
|
|
unit->unit.desired_looking_vector = control_data->looking_vector;
|
|
unit->unit.desired_aiming_vector = control_data->aiming_vector;
|
|
unit->unit.desired_facing_vector = control_data->facing_vector;
|
|
unit->unit.animation.desired_state = control_data->animation_state;
|
|
|
|
unit_verify_vectors(unit_index, "unit-control");
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean unit_vectors_are_valid(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
boolean result = valid_real_normal3d(&unit->unit.desired_facing_vector) &&
|
|
valid_real_normal3d(&unit->unit.desired_aiming_vector) &&
|
|
valid_real_normal3d(&unit->unit.desired_looking_vector) &&
|
|
valid_real_vector3d_axes2(&unit->object.forward, &unit->object.up) &&
|
|
valid_real_normal3d(&unit->unit.aiming_vector) &&
|
|
valid_real_normal3d(&unit->unit.looking_vector);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void unit_throw_grenade_release(
|
|
long unit_index,
|
|
boolean premature)
|
|
{
|
|
real_vector3d initial_velocity;
|
|
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (unit->unit.grenade_throw_state==_unit_grenade_throw_in_hand)
|
|
{
|
|
if (unit->unit.grenade_object_index!=NONE)
|
|
{
|
|
long grenade_index = unit->unit.grenade_object_index;
|
|
|
|
object_detach(grenade_index);
|
|
|
|
// Get new grenade origin from the actor
|
|
if (unit->unit.actor_index!=NONE)
|
|
{
|
|
real_point3d origin;
|
|
|
|
object_get_origin(unit->unit.grenade_object_index, &origin);
|
|
actor_aim_grenade(unit->unit.actor_index, &origin, &initial_velocity);
|
|
}
|
|
// Calculate the new position if unit is not an actor
|
|
else
|
|
{
|
|
if (unit->unit.player_index!=NONE)
|
|
{
|
|
real_vector3d forward;
|
|
real_vector3d left;
|
|
real_vector3d up;
|
|
real_point3d camera_position;
|
|
double origin_scale;
|
|
struct game_globals_player_information* player_information = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario_get_game_globals()->player_information,
|
|
0,
|
|
struct game_globals_player_information);
|
|
|
|
forward = unit->unit.aiming_vector;
|
|
|
|
if (normalize3d(cross_product3d(global_up3d, &forward, &up))==0.f)
|
|
{
|
|
up = *global_up3d;
|
|
}
|
|
|
|
normalize3d(cross_product3d(&forward, &up, &left));
|
|
|
|
unit_get_camera_position(unit_index, &camera_position);
|
|
origin_scale = player_information->grenade_origin.i;
|
|
point_from_line3d(&camera_position, &forward, (real)origin_scale, &camera_position);
|
|
origin_scale = player_information->grenade_origin.j;
|
|
point_from_line3d(&camera_position, &up, (real)origin_scale, &camera_position);
|
|
origin_scale = player_information->grenade_origin.k;
|
|
point_from_line3d(&camera_position, &left, (real)origin_scale, &camera_position);
|
|
|
|
object_translate(grenade_index, &camera_position, NULL);
|
|
}
|
|
|
|
{
|
|
real scale = unit_definition->unit.grenade_velocity/TICKS_PER_SECOND;
|
|
scale_vector3d(&unit->unit.aiming_vector, scale, &initial_velocity);
|
|
}
|
|
}
|
|
|
|
if (premature)
|
|
{
|
|
real power = (real)unit->unit.grenade_throw_ticks/(real)unit->unit.grenade_throw_full_power_ticks;
|
|
|
|
if (power < 1.f)
|
|
{
|
|
real_vector3d aiming_vector;
|
|
|
|
real range = real_random_range(0.020000001f, 0.046666667f);
|
|
|
|
scale_vector3d(&unit->unit.aiming_vector, range, &aiming_vector);
|
|
scale_vector3d(&initial_velocity, power, &initial_velocity);
|
|
point_from_line3d((real_point3d *)&initial_velocity, &aiming_vector, 1.f-power, (real_point3d *)&initial_velocity);
|
|
}
|
|
}
|
|
|
|
{
|
|
struct object_datum *grenade = object_get(grenade_index);
|
|
|
|
subtract_vectors3d(&initial_velocity, &grenade->object.translational_velocity, &initial_velocity);
|
|
projectile_accelerate(grenade_index, &initial_velocity);
|
|
}
|
|
|
|
unit->unit.grenade_throw_state = _unit_grenade_throw_ending;
|
|
unit->unit.grenade_object_index = NONE;
|
|
|
|
{
|
|
real_point3d new_grenade_origin;
|
|
|
|
unit_get_camera_position(unit_index, &new_grenade_origin);
|
|
|
|
if (!object_force_inside_bsp(grenade_index, &new_grenade_origin))
|
|
{
|
|
object_delete(grenade_index);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unit->unit.grenade_throw_state = _unit_grenade_throw_ending;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_seat_update(
|
|
long object_index)
|
|
{
|
|
real_vector3d *acceleration_scale;
|
|
real_vector3d new_velocity;
|
|
struct object_marker seat_marker;
|
|
|
|
struct unit_datum *unit = unit_get(object_index);
|
|
boolean found = FALSE;
|
|
|
|
if (unit->object.parent_object_index!=NONE && unit->unit.parent_seat_index!=NONE)
|
|
{
|
|
struct unit_definition *parent_unit_definition = unit_definition_get(unit_get(unit->object.parent_object_index)->definition_index);
|
|
struct unit_seat *seat = TAG_BLOCK_GET_ELEMENT(&parent_unit_definition->unit.seats, unit->unit.parent_seat_index, struct unit_seat);
|
|
|
|
if (object_get_marker_by_name(unit->object.parent_object_index, seat->marker_name, &seat_marker, 1))
|
|
{
|
|
object_get_origin(unit->object.parent_object_index, &seat_marker.matrix.position);
|
|
acceleration_scale = &seat->acceleration_scale;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
seat_marker.matrix.position = unit->object.position;
|
|
seat_marker.matrix.forward = unit->object.forward;
|
|
seat_marker.matrix.up = unit->object.up;
|
|
acceleration_scale = &unit_definition->unit.acceleration_scale;
|
|
found = TRUE;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
real_vector3d vp;
|
|
real_vector3d last_velocity;
|
|
last_velocity.i = seat_marker.matrix.position.x-unit->unit.seat_last_position.x;
|
|
last_velocity.j = seat_marker.matrix.position.y-unit->unit.seat_last_position.y;
|
|
last_velocity.k = seat_marker.matrix.position.z-unit->unit.seat_last_position.z;
|
|
|
|
new_velocity.i = last_velocity.i - unit->unit.seat_last_velocity.i;
|
|
new_velocity.j = last_velocity.j - unit->unit.seat_last_velocity.j;
|
|
new_velocity.k = last_velocity.k - unit->unit.seat_last_velocity.k;
|
|
|
|
cross_product3d(&seat_marker.matrix.up, &seat_marker.matrix.forward, &vp);
|
|
|
|
unit->unit.seat_desired_acceleration.i = (dot_product3d(&new_velocity, &seat_marker.matrix.forward) * acceleration_scale->i) + 0.5f;
|
|
unit->unit.seat_desired_acceleration.j = (dot_product3d(&new_velocity, &vp) * acceleration_scale->j) + 0.5f;
|
|
unit->unit.seat_desired_acceleration.k = (dot_product3d(&new_velocity, &seat_marker.matrix.up) * acceleration_scale->k) + 0.5f;
|
|
|
|
unit->unit.seat_desired_acceleration.i = PIN(unit->unit.seat_desired_acceleration.i, 0.f, 1.f);
|
|
unit->unit.seat_desired_acceleration.j = PIN(unit->unit.seat_desired_acceleration.j, 0.f, 1.f);
|
|
unit->unit.seat_desired_acceleration.k = PIN(unit->unit.seat_desired_acceleration.k, 0.f, 1.f);
|
|
|
|
unit->unit.seat_last_position = seat_marker.matrix.position;
|
|
unit->unit.seat_last_velocity = last_velocity;
|
|
}
|
|
else
|
|
{
|
|
unit->unit.seat_desired_acceleration.k = 0.5f;
|
|
unit->unit.seat_desired_acceleration.j = 0.5f;
|
|
unit->unit.seat_desired_acceleration.i = 0.5f;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static char const *unit_get_seat_label(
|
|
long object_index)
|
|
{
|
|
char const *result;
|
|
|
|
struct unit_datum *unit = unit_get(object_index);
|
|
|
|
if (unit->object.parent_object_index==NONE || unit->unit.parent_seat_index==NONE)
|
|
{
|
|
short base_seat_index = unit->unit.animation.base_seat_index;
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 8214, base_seat_index>=0 && base_seat_index<NUMBER_OF_UNIT_BASE_SEATS);
|
|
result = base_seat_labels[base_seat_index];
|
|
}
|
|
else
|
|
{
|
|
struct unit_datum *parent_unit = unit_get(unit->object.parent_object_index);
|
|
struct unit_definition *parent_unit_definition = unit_definition_get(parent_unit->definition_index);
|
|
result = TAG_BLOCK_GET_ELEMENT(&parent_unit_definition->unit.seats, unit->unit.parent_seat_index, struct unit_seat)->label;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static long unit_get_weapon(
|
|
struct unit_datum *unit,
|
|
short index)
|
|
{
|
|
long result = NONE;
|
|
|
|
if (index!=NONE)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 8371, index>=0 && index<MAXIMUM_WEAPONS_PER_UNIT);
|
|
result = unit->unit.weapon_object_indices[index];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void unit_drop_item(
|
|
long unit_index,
|
|
long item_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct item_datum *item = item_get(item_index);
|
|
|
|
if (item->object.parent_object_index==NONE)
|
|
{
|
|
object_reconnect_to_map(item_index, 0);
|
|
object_set_visibility(item_index, 1);
|
|
object_attach_to_marker(unit_index, "left hand", item_index, "");
|
|
}
|
|
else
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 8396, item->object.parent_object_index==unit_index)
|
|
}
|
|
|
|
item_in_unit_inventory(item_index, NONE);
|
|
object_detach(item_index);
|
|
|
|
item->object.translational_velocity = *global_zero_vector3d;
|
|
item->object.angular_velocity = *global_zero_vector3d;
|
|
|
|
{
|
|
real_vector3d item_velocity;
|
|
|
|
random_vector_in_cone3d(&unit->unit.aiming_vector, 0.f, 0.39269909f, &item_velocity);
|
|
scale_vector3d(&item_velocity, real_random_range(0.026666667f, 0.040000003f), &item_velocity);
|
|
{
|
|
real_vector3d unit_velocity;
|
|
object_get_velocities(unit_index, &unit_velocity, NULL);
|
|
add_vectors3d(&item_velocity, &unit_velocity, &item_velocity);
|
|
}
|
|
|
|
item->item.ignore_object_index = unit_index;
|
|
item_accelerate(item_index, &item_velocity, FALSE);
|
|
}
|
|
|
|
{
|
|
real_point3d camera_position;
|
|
unit_get_camera_position(unit_index, &camera_position);
|
|
|
|
if (!object_force_inside_bsp(item_index, &camera_position) && !game_engine_running())
|
|
{
|
|
object_delete(item_index);
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(unit->unit.flags, _unit_doesnt_drop_items_bit))
|
|
{
|
|
object_delete(item_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// TODO: Fix
|
|
static void unit_cause_continuous_melee_damage(
|
|
long unit_index)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (unit->unit.melee_attack_state==4 &&
|
|
unit->object.parent_object_index!=NONE &&
|
|
unit_definition->unit.melee_damage.index!=NONE)
|
|
{
|
|
struct collision_model_test_vector_result vector_result;
|
|
struct damage_data damage_data;
|
|
real_plane3d collision_plane;
|
|
real_point3d collision_point;
|
|
struct collision_model_instance instance;
|
|
real_point3d collision_origin;
|
|
real_vector3d collision_vector;
|
|
boolean collision_passed = FALSE;
|
|
|
|
if (unit->unit.melee_continuous_damage_effect_timer==0)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 8941, global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH);
|
|
|
|
global_current_collision_users[global_current_collision_user_depth++] = 8;
|
|
|
|
if (collision_model_instance_new(&instance, unit->object.parent_object_index))
|
|
{
|
|
object_get_origin(unit_index, &collision_origin);
|
|
scale_vector3d(&unit->object.forward, 0.2f, &collision_vector);
|
|
point_from_line3d(&collision_origin, &collision_vector, -0.5f, &collision_origin);
|
|
|
|
if (collision_model_test_vector(
|
|
&instance,
|
|
FLAG(_collision_test_front_facing_surfaces_bit) | FLAG(_collision_test_back_facing_surfaces_bit),
|
|
&collision_origin,
|
|
&collision_vector,
|
|
&vector_result))
|
|
{
|
|
point_from_line3d(&collision_origin, &collision_vector, vector_result.bsp_result.t, &collision_point);
|
|
matrix4x3_transform_plane(&instance.matrices[vector_result.node_index], vector_result.bsp_result.plane, &collision_plane);
|
|
|
|
if (vector_result.bsp_result.plane_designator & LONG_MIN)
|
|
{
|
|
plane3d_negate(&collision_plane, &collision_plane);
|
|
}
|
|
|
|
collision_passed = TRUE;
|
|
}
|
|
}
|
|
|
|
match_assert("c:\\halo\\SOURCE\\units\\units.c", 8965, global_current_collision_user_depth > 1);
|
|
--global_current_collision_user_depth;
|
|
}
|
|
|
|
damage_data_new(&damage_data, unit_definition->unit.melee_damage.index);
|
|
|
|
damage_data.owner_object_index = unit_index;
|
|
damage_data.owner_team_index = unit->object.owner_team_index;
|
|
damage_data.owner_player_index = unit->unit.player_index;
|
|
damage_data.scale = 1.f/30.f;
|
|
|
|
if (collision_passed)
|
|
{
|
|
damage_data.epicenter = collision_point;
|
|
damage_data.origin = collision_point;
|
|
damage_data.direction = unit->object.forward;
|
|
|
|
SET_FLAG(damage_data.flags, _damage_create_localized_effect_bit, TRUE);
|
|
unit->unit.melee_continuous_damage_effect_timer = 10;
|
|
|
|
object_cause_damage(
|
|
&damage_data,
|
|
unit->object.parent_object_index,
|
|
vector_result.node_index,
|
|
vector_result.region_index,
|
|
vector_result.bsp_result.material_index,
|
|
&collision_plane.n);
|
|
}
|
|
else
|
|
{
|
|
object_cause_damage(
|
|
&damage_data,
|
|
unit->object.parent_object_index,
|
|
NONE,
|
|
NONE,
|
|
NONE,
|
|
NULL);
|
|
}
|
|
|
|
--unit->unit.melee_continuous_damage_effect_timer;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_verify_vectors(
|
|
long unit_index,
|
|
char const *debugstring)
|
|
{
|
|
char buffer[512];
|
|
|
|
if (!unit_vectors_are_valid(unit_index))
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
ai_debug_describe_actor(
|
|
unit->unit.actor_index==NONE ? unit->unit.swarm_actor_index : unit->unit.actor_index,
|
|
unit_index,
|
|
1,
|
|
buffer,
|
|
NUMBEROF(buffer)
|
|
);
|
|
error(_error_silent, "**** unit_verify_vectors: problems with %s at location %s", buffer, debugstring);
|
|
error(
|
|
_error_silent,
|
|
" object: pos %f %f %f, fwd %f %f %f, up %f %f %f",
|
|
unit->object.position.x,
|
|
unit->object.position.y,
|
|
unit->object.position.z,
|
|
unit->object.forward.i,
|
|
unit->object.forward.j,
|
|
unit->object.forward.k,
|
|
unit->object.up.i,
|
|
unit->object.up.j,
|
|
unit->object.up.k
|
|
);
|
|
error(
|
|
_error_silent,
|
|
" desired facing %f %f %f, aiming %f %f %f, looking %f %f %f",
|
|
unit->unit.desired_facing_vector.i,
|
|
unit->unit.desired_facing_vector.j,
|
|
unit->unit.desired_facing_vector.k,
|
|
unit->unit.desired_aiming_vector.i,
|
|
unit->unit.desired_aiming_vector.j,
|
|
unit->unit.desired_aiming_vector.k,
|
|
unit->unit.desired_looking_vector.i,
|
|
unit->unit.desired_looking_vector.j,
|
|
unit->unit.desired_looking_vector.k
|
|
);
|
|
error(
|
|
_error_silent,
|
|
" aiming vector %f %f %f velocity %f %f %f",
|
|
unit->unit.aiming_vector.i,
|
|
unit->unit.aiming_vector.j,
|
|
unit->unit.aiming_vector.k,
|
|
unit->unit.aiming_velocity.i,
|
|
unit->unit.aiming_velocity.j,
|
|
unit->unit.aiming_velocity.k
|
|
);
|
|
error(
|
|
_error_silent,
|
|
" looking vector %f %f %f velocity %f %f %f",
|
|
unit->unit.looking_vector.i,
|
|
unit->unit.looking_vector.j,
|
|
unit->unit.looking_vector.k,
|
|
unit->unit.looking_velocity.i,
|
|
unit->unit.looking_velocity.j,
|
|
unit->unit.looking_velocity.k
|
|
);
|
|
|
|
error(_error_silent, " warning, hex dump follows...");
|
|
|
|
error(
|
|
_error_silent,
|
|
" object: pos %08X %08X %08X, fwd %08X %08X %08X, up %08X %08X %08X",
|
|
*(long *)&unit->object.position.x,
|
|
*(long *)&unit->object.position.y,
|
|
*(long *)&unit->object.position.z,
|
|
*(long *)&unit->object.forward.i,
|
|
*(long *)&unit->object.forward.j,
|
|
*(long *)&unit->object.forward.k,
|
|
*(long *)&unit->object.up.i,
|
|
*(long *)&unit->object.up.j,
|
|
*(long *)&unit->object.up.k
|
|
);
|
|
error(
|
|
_error_silent,
|
|
" desired facing %08X %08X %08X, aiming %08X %08X %08X, looking %08X %08X %08X",
|
|
*(long *)&unit->unit.desired_facing_vector.i,
|
|
*(long *)&unit->unit.desired_facing_vector.j,
|
|
*(long *)&unit->unit.desired_facing_vector.k,
|
|
*(long *)&unit->unit.desired_aiming_vector.i,
|
|
*(long *)&unit->unit.desired_aiming_vector.j,
|
|
*(long *)&unit->unit.desired_aiming_vector.k,
|
|
*(long *)&unit->unit.desired_looking_vector.i,
|
|
*(long *)&unit->unit.desired_looking_vector.j,
|
|
*(long *)&unit->unit.desired_looking_vector.k
|
|
);
|
|
error(
|
|
_error_silent,
|
|
" aiming vector %08X %08X %08X velocity %08X %08X %08X",
|
|
*(long *)&unit->unit.aiming_vector.i,
|
|
*(long *)&unit->unit.aiming_vector.j,
|
|
*(long *)&unit->unit.aiming_vector.k,
|
|
*(long *)&unit->unit.aiming_velocity.i,
|
|
*(long *)&unit->unit.aiming_velocity.j,
|
|
*(long *)&unit->unit.aiming_velocity.k
|
|
);
|
|
error(
|
|
_error_silent,
|
|
" looking vector %08X %08X %08X velocity %08X %08X %08X",
|
|
*(long *)&unit->unit.looking_vector.i,
|
|
*(long *)&unit->unit.looking_vector.j,
|
|
*(long *)&unit->unit.looking_vector.k,
|
|
*(long *)&unit->unit.looking_velocity.i,
|
|
*(long *)&unit->unit.looking_velocity.j,
|
|
*(long *)&unit->unit.looking_velocity.k
|
|
);
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\units\\units.c",
|
|
594,
|
|
unit_vectors_are_valid(unit_index),
|
|
"unit_verify_vectors FAILURE, see above for details"
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void unit_running_blind(
|
|
long unit_index,
|
|
real_vector3d *run_vector)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
boolean actor_controlled = FALSE;
|
|
|
|
if (unit->unit.actor_index==NONE || !actor_get_running_blind_vector(unit->unit.actor_index, run_vector))
|
|
{
|
|
*run_vector = *global_forward3d;
|
|
}
|
|
else
|
|
{
|
|
actor_controlled = TRUE;
|
|
}
|
|
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 9541, run_vector);
|
|
|
|
{
|
|
real angular_acceleration_this_tick;
|
|
|
|
real positive_angle_allowed = 1.f;
|
|
real negative_angle_allowed = 1.f;
|
|
|
|
if (actor_controlled)
|
|
{
|
|
real negative_angle_bounds_dist = DEGREES_TO_RADIANS(45)-unit->unit.run_blindly_angle;
|
|
real positive_angle_bounds_dist = DEGREES_TO_RADIANS(45)+unit->unit.run_blindly_angle;
|
|
|
|
negative_angle_allowed = MIN(negative_angle_allowed, negative_angle_bounds_dist / DEGREES_TO_RADIANS(13.5f));
|
|
positive_angle_allowed = MIN(positive_angle_allowed, positive_angle_bounds_dist / DEGREES_TO_RADIANS(13.5f));
|
|
}
|
|
|
|
{
|
|
real negative_velocity_bounds_dist = DEGREES_TO_RADIANS(12.f)-unit->unit.run_blindly_angle_delta;
|
|
real positive_velocity_bounds_dist = DEGREES_TO_RADIANS(12.f)+unit->unit.run_blindly_angle_delta;
|
|
|
|
negative_angle_allowed = MIN(negative_angle_allowed, negative_velocity_bounds_dist * 15.915494f);
|
|
positive_angle_allowed = MIN(positive_angle_allowed, positive_velocity_bounds_dist * 15.915494f);
|
|
|
|
}
|
|
|
|
if (negative_angle_allowed<positive_angle_allowed)
|
|
{
|
|
if (negative_angle_allowed<-1.f)
|
|
{
|
|
angular_acceleration_this_tick = -0.020943951f;
|
|
}
|
|
else
|
|
{
|
|
real min_allowed = MIN(1.f, negative_angle_allowed);
|
|
angular_acceleration_this_tick = real_random_range(-0.020943951f, 0.020943951f * min_allowed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (positive_angle_allowed<-1.f)
|
|
{
|
|
angular_acceleration_this_tick = 0.020943951f;
|
|
}
|
|
else
|
|
{
|
|
real max_allowed = MIN(1.f, positive_angle_allowed);
|
|
angular_acceleration_this_tick = real_random_range(-0.020943951f * max_allowed, 0.020943951f);
|
|
}
|
|
}
|
|
|
|
unit->unit.run_blindly_angle_delta+=angular_acceleration_this_tick;
|
|
}
|
|
|
|
unit->unit.run_blindly_angle += unit->unit.run_blindly_angle_delta;
|
|
|
|
if (unit->unit.run_blindly_angle < -_pi)
|
|
{
|
|
unit->unit.run_blindly_angle += 2.f*_pi;
|
|
}
|
|
else if (unit->unit.run_blindly_angle > _pi)
|
|
{
|
|
unit->unit.run_blindly_angle -= 2.f*_pi;
|
|
}
|
|
|
|
rotate_vector_about_axis(run_vector, global_up3d, sine(unit->unit.run_blindly_angle), cosine(unit->unit.run_blindly_angle));
|
|
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\units\\units.c", 9612, run_vector)
|
|
|
|
return;
|
|
}
|
|
|
|
boolean unit_unsuspecting(
|
|
long unit_index,
|
|
real_point3d const *point)
|
|
{
|
|
struct unit_datum *unit = unit_try_and_get(unit_index);
|
|
|
|
if (unit!=NULL &&
|
|
unit->object.type==_object_type_biped)
|
|
{
|
|
struct unit_definition *unit_definition = unit_definition_get(unit->definition_index);
|
|
|
|
if (!TEST_FLAG(unit_definition->unit.flags, _unit_never_unsuspecting_bit))
|
|
{
|
|
real_vector3d direction;
|
|
|
|
vector_from_points3d(point, &unit->object.bounding_sphere_center, &direction);
|
|
|
|
if (dot_product3d(&direction, &unit->unit.looking_vector)>0.f)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
char const *base_seat_label =
|
|
base_seat_labels[_unit_base_seat_asleep];
|
|
|
|
if (csstrcmp(base_seat_label, unit_get_seat_label(unit_index))==0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static boolean unit_integrated_night_vision_is_active(
|
|
long unit_index)
|
|
{
|
|
boolean active = FALSE;
|
|
|
|
if (unit_get(unit_index)->unit.current_zoom_level!=NONE)
|
|
{
|
|
long current_weapon_index = unit_get_current_weapon_index(unit_index);
|
|
|
|
if (current_weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(current_weapon_index);
|
|
struct weapon_definition* weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
if (TEST_FLAG(weapon_definition->weapon.flags, _weapon_enables_unit_integrated_night_vision_bit))
|
|
{
|
|
active = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return active;
|
|
}
|
|
|
|
/* Verify the public seat-helper declaration without perturbing this legacy
|
|
* translation unit's authenticated function-declaration order. */
|
|
/* the distributed netcode (port/linux/game/network_objects.c): a client's
|
|
unit carries the host's weapons, the same objects, moved in and out as the
|
|
host's unit had them (the host has applied the game's rules) */
|
|
|
|
void unit_network_forget_weapon(long unit_index, short slot);
|
|
|
|
/* the weapon into the slot, as unit_add_weapon_to_inventory puts one in */
|
|
void unit_network_add_weapon(
|
|
long unit_index,
|
|
long weapon_index,
|
|
short slot)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
if (weapon->object.parent_object_index != NONE)
|
|
object_detach(weapon_index);
|
|
if (TEST_FLAG(weapon->object.flags, _object_connected_to_map_bit))
|
|
object_disconnect_from_map(weapon_index);
|
|
object_set_visibility(weapon_index, FALSE);
|
|
item_in_unit_inventory(weapon_index, unit_index);
|
|
unit->unit.weapon_object_indices[slot] = weapon_index;
|
|
unit->unit.weapon_last_used_at_game_time[slot] = 0;
|
|
}
|
|
|
|
/* the slot's weapon out onto the ground, as unit_drop_current_weapon drops
|
|
one (where the host has it the objects' states say) */
|
|
void unit_network_drop_weapon(
|
|
long unit_index,
|
|
short slot)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
long weapon_index = unit->unit.weapon_object_indices[slot];
|
|
|
|
if (weapon_index == NONE)
|
|
return;
|
|
if (unit->unit.current_weapon_index == slot)
|
|
first_person_weapon_message_from_unit(unit_index, 13);
|
|
unit_drop_item(unit_index, weapon_index);
|
|
unit_network_forget_weapon(unit_index, slot);
|
|
}
|
|
|
|
/* the slot emptied, its weapon about to be deleted */
|
|
void unit_network_forget_weapon(
|
|
long unit_index,
|
|
short slot)
|
|
{
|
|
struct unit_datum *unit = unit_get(unit_index);
|
|
|
|
unit->unit.weapon_object_indices[slot] = NONE;
|
|
if (unit->unit.current_weapon_index == slot)
|
|
unit->unit.current_weapon_index = NONE;
|
|
if (unit->unit.desired_weapon_index == slot || unit->unit.desired_weapon_index == NONE)
|
|
unit->unit.desired_weapon_index = unit_weapon_next_index(unit_index, NONE, 0);
|
|
}
|