Files
astromaddie--HaloCE-VR/source/units/units.c
T
MadisonandClaude Opus 5.5 06a0e85c18 Aim with the right hand in the headset (vr.aim = "hand"; untested)
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>
2026-10-02 09:34:10 +09:00

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);
}