/* 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: _code_00197e30 (0000) 00197F70 0020: _unit_get_zoom_level (0000) 00197F90 0080: _code_00197f90 (0000) 00198010 0040: _code_00198010 (0000) 00198050 0020: _code_00198050 (0000) 00198070 0060: _code_00198070 (0000) 001980D0 0060: _code_001980d0 (0000) 00198130 0040: _code_00198130 (0000) 00198170 0020: _code_00198170 (0000) 00198190 0060: _code_00198190 (0000) 001981F0 0040: _code_001981f0 (0000) 00198230 0040: _code_00198230 (0000) 00198270 0190: _unit_animation_start_action (0000) 00198400 01c0: _code_00198400 (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: _code_00198e40 (0000) 00198FD0 00d0: _code_00198fd0 (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: _code_0019a170 (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: _code_0019a640 (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: _code_0019a820 (0000) 0019A9F0 0310: _code_0019a9f0 (0000) 0019AD00 02d0: _code_0019ad00 (0000) 0019AFD0 0050: _code_0019afd0 (0000) 0019B020 0040: _code_0019b020 (0000) 0019B060 0050: _code_0019b060 (0000) 0019B0B0 00b0: _code_0019b0b0 (0000) 0019B160 0050: _code_0019b160 (0000) 0019B1B0 0080: _code_0019b1b0 (0000) 0019B230 0050: _code_0019b230 (0000) 0019B280 0190: _code_0019b280 (0000) 0019B410 00b0: _code_0019b410 (0000) 0019B4C0 0100: _code_0019b4c0 (0000) 0019B5C0 0040: _unit_handle_region_destroyed (0000) 0019B600 0080: _code_0019b600 (0000) 0019B680 02a0: _code_0019b680 (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: _code_0019bf70 (0000) 0019C460 0200: _code_0019c460 (0000) 0019C660 0220: _code_0019c660 (0000) 0019C880 00d0: _unit_adjust_projectile_ray (0000) 0019C950 0200: _unit_render_debug (0000) 0019CB50 0580: _code_0019cb50 (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: _code_0019db80 (0000) 0019DC20 0040: _unit_drop_current_equipment (0000) 0019DC60 0050: _unit_can_use_weapon (0000) 0019DCB0 00d0: _unit_approve_weapon_swap (0000) 0019DD80 0170: _code_0019dd80 (0000) 0019DEF0 0100: _unit_drop_current_weapon (0000) 0019DFF0 0030: _code_0019dff0 (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: _code_0019ea70 (0000) 0019EB90 00a0: _unit_start_flaming_to_death (0000) 0019EC30 02e0: _code_0019ec30 (0000) 0019EF10 0090: _code_0019ef10 (0000) 0019EFA0 02e0: _code_0019efa0 (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: _code_001a0c90 (0000) 001A0CF0 0620: _code_001a0cf0 (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: _code_001a17d0 (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: _code_001a1f50 (0000) 001A2030 0040: _code_001a2030 (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 "unit_definitions.h" #include "ai/actors.h" #include "ai/ai_debug.h" #include "bitmaps/bitmaps.h" #include "cseries/errors.h" #include "cseries/profile.h" #include "effects/effects.h" #include "effects/material_effect_definitions.h" #include "game/cheats.h" #include "game/game_globals.h" #include "game/game_engine.h" #include "game/players.h" #include "hs/object_lists.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/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" /* ---------- constants */ enum { _unit_region_destroyed_head_bit = 9, }; enum { _equipment_powerup_none = 0, _equipment_powerup_grenade = 6, }; enum { _unit_add_weapon_normal = 0, _unit_add_weapon_starting, _unit_add_weapon_replace, }; 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, }; /* ---------- 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_animation_update_data { char state_desired; boolean crouching; }; typedef char unit_animation_update_data_size_assert[ sizeof(struct unit_animation_update_data) == 0x2 ? 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 player_died( long player_index); void actor_died( long actor_index); void actor_swarm_unit_died( long swarm_actor_index, long unit_index); void unit_detach_from_parent( long unit_index); void unit_start_running_blindly( 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_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); 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); void unit_animation_start_action( long unit_index, short action); static boolean code_00197e30( struct unit_acceleration_plan *plan, real delta_time, real position, real *new_position, real velocity, real *new_velocity); static void code_0019bf70( real position, real velocity, real maximum_velocity, real maximum_acceleration, struct unit_acceleration_plan *plan); static void code_0019c460( struct unit_acceleration_plan *first_plan, struct unit_acceleration_plan *second_plan, real maximum_velocity, real maximum_acceleration); static void code_0019b600( long unit_index, short material_type, long damage_effect_index); static void code_0019a170( long unit_index); static short code_0019a640( long unit_index); static short code_00198e40( short animation_impulse, short *interpolation_frame_count); static boolean code_00198fd0( long unit_index, long animation_impulse); static boolean code_00198170( struct unit_animation *animation); static boolean code_001981f0( short state); static short code_00198230( short new_state, short old_state); static boolean code_00198050( struct unit_animation *animation); static boolean code_00197f90( struct unit_animation *animation, short desired_state); static boolean code_00198070( struct unit_animation *animation); static long code_00198190( short state); static void code_0019b0b0( long unit_index, long animation_graph_index, short animation_index); short animation_update_internal( short animation_type, long animation_graph_index, struct animation_state *state, long *sound_index); static short code_0019b160( struct animation_state *animation, long animation_graph_index, long unit_index); static char const *code_0019dff0( long unit_index); static void code_0019ea70( long unit_index, real_vector2d const *alignment_vector); void player_control_set_desired_weapon( long unit_index, short desired_weapon_index); boolean ai_try_vehicle_eviction( long actor_index, long entering_unit_index, boolean immediate); boolean valid_real_normal2d( real_vector2d const *normal); void biped_stop_melee_attack( long unit_index); void first_person_weapon_message_from_unit( long unit_index, short message_type); void weapon_stop_reload( long weapon_index); boolean weapon_prevents_grenade_throwing( long weapon_index); static void unit_adjust_for_seat_change(long unit_index); static short unit_weapon_next_index(long unit_index, short current_index, short delta); static void unit_throw_grenade_move_to_hand(long unit_index); static boolean code_001980d0( struct unit_animation *animation); static boolean code_00198130( 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); boolean unit_animation_set_state( long unit_index, short new_state); short unit_update_animation( long unit_index, struct unit_animation_update_data const *data); static boolean unit_verify_inventory(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); static void code_0019b410( long unit_index); static void code_0019b4c0( 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); boolean game_team_is_enemy( short team_index0, short team_index1); boolean ai_handle_killing_spree( long unit_index, short killing_spree_count); void ai_create_mounted_weapons_for_unit( long unit_index); void unit_dialogue_determine_variant( long unit_index); void player_died( long player_index); void actor_died( long actor_index); void actor_swarm_unit_died( long swarm_actor_index, long unit_index); void unit_detach_from_parent( long unit_index); void unit_exit_seat_end( long unit_index); short actors_spawn_from_unit( long unit_index, long actor_variant_definition_index, short actor_count, real throw_velocity); short animation_choose_random_permutation( long animation_graph_index, short animation_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; } 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 && indexobject.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_indexunit.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_indexunit.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_indexunit.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_indexunit.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_indexunit.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_indexunit.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_indexunit.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_indexunit.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.ageweapon.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_indexunit.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_indexunit.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_typeunit.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_indexunit.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_indexunit.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; indexunit.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_limping_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_indexframe_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 identifies this private helper as unit_create_initial_weapons; the // January body below is independently proven against the Xbox object. void code_001a1f50( 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); 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_typeunit.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); code_001a1f50(unit_index); for (seat_index = 0; seat_indexunit.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; indexunit.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 code_0019b4c0( 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); if (TEST_FLAG(weapon->object.flags, _object_connected_to_map_bit)) { display_assert( 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)), "c:\\halo\\SOURCE\\units\\units.c", 8505, TRUE); system_exit(-1); } 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; 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(); } 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); } } 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; code_0019b4c0(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; } code_0019b410(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 code_0019b410( 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_indexunit.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; } 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; { 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; code_0019a170(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 = 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); 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) { long animation_graph_index; 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; code_0019b0b0( 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_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; } static void code_00198400( 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_slotanimations.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) { code_00198400(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_indexunit.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; code_0019b0b0( 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_limping_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) { code_0019ea70(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_limping_bit); } if (!biped_limping && unit_animation_set_state(unit_index, _unit_state_leap_start)) { if (alignment_vector) { code_0019ea70(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) { code_0019ea70(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) { code_0019ea70(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; } 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_indexunit.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); code_0019b0b0( 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); 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_indexunit.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_nearby_seat_none = 0, _unit_nearby_seat_occupied, _unit_nearby_seat_available, }; 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_indexunit.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_indexunit.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_ticksunit.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)) { boolean set = FALSE; real active_camo_change_this_tick; if (game_engine_running() && unit->unit.cause_for_camo_regrowth && unit->unit.cause_for_camo_regrowth==cause_for_camo_regrowth_shot_fired) { 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; } 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, 3) ? _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, 2, 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 previous_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_indexunit.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 && unit->unit.current_zoom_level>1) { scale = (real)unit->unit.current_zoom_level/(real)(unit->unit.current_zoom_level-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); previous_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 matrix; 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; unit_euler_aiming_update( &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, &previous_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.aiming_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 matrix; 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; unit_euler_aiming_update( &matrix, &unit->unit.aiming_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, 11); if (!jetpack_cheat_active) { if (allow_integrated_lights && TEST_FLAG(unit->unit.control_flags, _unit_control_integrated_light_bit)) { SET_FLAG(flags, 0, TRUE); } if (TEST_FLAG(unit->unit.control_flags, _unit_control_weapon_primary_trigger_bit)) { SET_FLAG(flags, 1, TRUE); } if (TEST_FLAG(unit->unit.control_flags, _unit_control_weapon_secondary_trigger_bit)) { SET_FLAG(flags, 2, 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, 3, TRUE); } if (unit_animation_busy(&unit->unit.animation) && !time_remaining) { SET_FLAG(flags, 4, TRUE); } if (unit->object.type==_object_type_biped && ((struct biped_datum *)unit)->biped.player_melee_ticks > 0) { SET_FLAG(flags, 4, TRUE); } if (unit->unit.current_zoom_level!=NONE) { SET_FLAG(flags, 6, TRUE); } } else { SET_FLAG(flags, 5, 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, 1)) { 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_indexunit.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, 0)) { 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)) { light_change_state = TRUE; if (TEST_FLAG(unit->unit.flags, _unit_integrated_light_on_bit)) { light_change_state = FALSE; } 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; } 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.yawx0) { 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.pitchy0) { 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); } 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=0 && base_weapon_indexobject.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 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; } 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_indexunit.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_indexunit.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, 1, 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 code_00198050( 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 code_00197f90( 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 code_00198070( 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 code_001980d0( 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 code_00198130( 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_indexunit_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)) { long weapon_class_index; for (weapon_class_index = 0; weapon_class_indexweapon_classes.count; ++weapon_class_index) { long 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_indexweapon_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=='\0' || !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 = TRUE; 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; showing_acceleration = FALSE; } } if (unit->unit.animation.state!=_unit_state_user_animation) { unit->unit.animation.state = NONE; } unit->unit.animation.weapon_type_index = weapon_type_index; unit->unit.animation.base_seat_index = seat_label_to_base_seat_index(seat_label); unit->unit.animation.weapon_index = weapon_class_index; SET_FLAG(unit->unit.animation.flags, _unit_animation_showing_acceleration_bit, showing_acceleration); } result = TRUE; break; } } } } } return result; } static boolean code_00197e30( 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_indexunit.weapon_object_indices[weapon_index]; if (weapon_object_index!=NONE) { item_in_unit_inventory(weapon_object_index, unit_index); } } code_0019a170(unit_index); return; } static void code_0019a170( 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 code_0019a640( long unit_index) { struct unit_datum *unit = unit_get(unit_index); short result = NONE; short weapon_index; for (weapon_index = 0; weapon_indexunit.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 = code_0019a640(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 code_00198e40( short animation_impulse, short *interpolation_frame_count) { short index = NONE; match_assert( "c:\\halo\\SOURCE\\units\\units.c", 5364, animation_impulse>=0 && animation_impulseunit.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: return result; default: break; } 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 = (short)animation_impulse; if (impulse_index>=12 && impulse_index<=13) { return (boolean)TEST_FLAG(seat->flags, _unit_seat_unknown8_bit); } } } } else { long impulse_index = (short)animation_impulse; if (impulse_index>=12 && impulse_index<=13) { return FALSE; } result = TRUE; } return result; } 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 (code_00198fd0(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 = code_00198e40( animation_impulse, &interpolation_frame_count); if (animation_type>=0 && animation_typeanimations.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); code_0019b0b0( 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) { code_0019ea70(unit_index, alignment_vector); } result = TRUE; } } } return result; } static void code_0019b600( 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; code_0019b600( unit_index, hit_material_type, damage_effect_index); } } } unit->unit.melee_attack_state = 0; return; } static void code_0019ea70( 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 code_0019bf70( 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) { code_0019bf70( -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_rootsecond_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_rootacceleration*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 code_0019c460( 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_timeacceleration_time>0.f && second_total_time 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 aiming_pitch_velocity; real aiming_yaw_velocity; real dot_product; real_euler_angles2d aiming_error; real_euler_angles2d 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; 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_yaw_velocity = rotated_angles.yaw - aiming_angles.yaw; aiming_pitch_velocity = rotated_angles.pitch - aiming_angles.pitch; } else { aiming_yaw_velocity = 0.f; aiming_pitch_velocity = 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; } } code_0019bf70( aiming_error.yaw, aiming_yaw_velocity, angular_velocity_limit, angular_acceleration_limit, &yaw_plan); code_0019bf70( aiming_error.pitch, aiming_pitch_velocity, angular_velocity_limit, angular_acceleration_limit, &pitch_plan); code_0019c460( &yaw_plan, &pitch_plan, angular_velocity_limit, angular_acceleration_limit); yaw_complete = code_00197e30( &yaw_plan, 1.f, aiming_error.yaw, &aiming_error.yaw, aiming_yaw_velocity, &end_angular_velocity.yaw); pitch_complete = code_00197e30( &pitch_plan, 1.f, aiming_error.pitch, &aiming_error.pitch, aiming_pitch_velocity, &end_angular_velocity.pitch); if (yaw_complete && pitch_complete) { *aiming_vector = clamped_desired_aiming_vector; *aiming_velocity = *global_zero_vector3d; } else { real_euler_angles2d end_aiming_angles; real_vector3d end_aiming_vector; 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 = PIN( dot_product3d(&further_vector, &end_aiming_vector), -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 code_00198170( 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 code_001981f0( 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 code_00198230( 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 code_00198190( 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 code_0019b0b0( 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 = ""; 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 code_0019b160( struct animation_state *animation, long animation_graph_index, long unit_index) { 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 *code_0019dff0( 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 const *data) { struct unit_datum *unit; struct unit_definition *unit_definition; short desired_state; word result; boolean apply_state; short desired_base_seat_index; 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 root_velocity; real_matrix4x3 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_stateobject.parent_object_index==NONE && !TEST_FLAG( unit->object.functions_active_flags, _unit_debug_function_active_bit)) { 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: case _unit_animation_state_suspicious: desired_base_seat_index = _unit_base_seat_alert; break; case _unit_animation_state_in_combat: { boolean use_combat_variant; use_combat_variant = data->crouching!=0; desired_base_seat_index = use_combat_variant + _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; default: 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 = code_00197f90( &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), code_0019dff0(unit_index), TRUE); } } } if (unit->unit.animation.soft_ping_animation.index!=NONE) { animation_update_result = code_0019b160( &unit->unit.animation.soft_ping_animation, unit_definition->object.animation_graph.index, unit_index); if (animation_update_result==2) { unit->unit.animation.soft_ping_animation.index = NONE; } } if (unit->object.animation.state.index!=NONE) { animation_update_result = code_0019b160( &unit->object.animation.state, unit->object.animation.animation_graph_index, unit_index); 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_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_limping_bit) || TEST_FLAG( biped_definition->biped.flags, _biped_has_no_dying_airborne_bit)) { destroy_unit: unit_destroy(unit_index); goto finish_dying_animation; } } start_limp_body: if (unit->object.type==_object_type_biped) { biped_start_limp_body_physics(unit_index); } goto finish_dying; 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, &root_velocity); object_get_world_matrix(unit_index, &world_matrix); matrix4x3_transform_vector( &world_matrix, &root_velocity, &root_velocity); unit_exit_seat_end(unit_index); add_vectors3d( &unit->object.translational_velocity, &root_velocity, &unit->object.translational_velocity); 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; case _unit_state_opening: case _unit_state_closing: --unit->object.animation.state.frame_index; break; case _unit_state_leap_start: result = TRUE; desired_state = _unit_state_leap_airborne; break; default: break; } if (!code_00198070(&unit->unit.animation)) { apply_state = TRUE; } } } if (unit->unit.animation.action_animation.index!=NONE) { animation_update_result = code_0019b160( &unit->unit.animation.action_animation, unit_definition->object.animation_graph.index, unit_index); if (animation_update_result==2) { object_start_interpolation(unit_index, 6); unit = unit_get(unit_index); unit->unit.animation.action = FALSE; unit->unit.animation.action_animation.index = NONE; } } if (unit->unit.animation.overlay_action_animation.index!=NONE) { animation_update_result = code_0019b160( &unit->unit.animation.overlay_action_animation, unit_definition->object.animation_graph.index, unit_index); switch (animation_update_result) { case 2: case 4: if (!code_00198050(&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 && code_00197f90(&unit->unit.animation, desired_state))) { unit_animation_set_state(unit_index, desired_state); } return result; } 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); 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 interpolation_frame_count; boolean old_state_is_none = unit->unit.animation.state == NONE; boolean changed_state = FALSE; boolean result = TRUE; (void)weapon_type; if (old_state_is_none || new_state != unit->unit.animation.state) { short animation_index; long weapon_class_animation_index = NONE; long 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_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_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_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_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_throw_grenade: weapon_class_animation_index = _unit_weapon_class_animation_throw_grenade; 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_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_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_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_indexanimations.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_indexanimations.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) { result = code_001981f0(new_state); } if (result) { short chosen_animation_index = animation_choose_random_permutation_internal( TRUE, unit_definition->object.animation_graph.index, animation_index); code_0019b0b0( unit_index, unit_definition->object.animation_graph.index, chosen_animation_index); interpolation_frame_count = code_00198230( new_state, unit->unit.animation.state); changed_state = TRUE; } } if (result) { short aiming_screen_animation_index = (short)code_00198190(new_state); if (old_state_is_none || aiming_screen_animation_index != (short)code_00198190( unit->unit.animation.state)) { short 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; } unit->unit.animation.aiming_screen_index = animation_choose_random_permutation_internal( TRUE, unit_definition->object.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)); } } if (old_state_is_none) { short 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; } unit->unit.animation.looking_screen_index = animation_choose_random_permutation_internal( TRUE, unit_definition->object.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, (short)interpolation_frame_count); } unit->unit.animation.state = (char)new_state; } 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; real_euler_angles2d relative_looking_angles; real_euler_angles2d relative_aiming_angles; 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_animation_index; long acceleration_animation_count; real *acceleration; acceleration = unit->unit.seat_acceleration.n; acceleration_animation_index = _unit_seat_animation_acceleration_front_back; acceleration_animation_count = _unit_seat_animation_push_impact - _unit_seat_animation_acceleration_front_back; do { if (acceleration_animation_index >= 0 && acceleration_animation_index < unit_seat->animations.count) { animation_index = animation_graph_animation_index_get( &unit_seat->animations)[acceleration_animation_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) * *acceleration, node_orientations); } } ++acceleration_animation_index; ++acceleration; } while (--acceleration_animation_count != 0); } if (!TEST_FLAG(unit_definition->unit.flags, _unit_has_no_aiming_bit) && code_00198170(&unit->unit.animation)) { 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) { struct animation *animation; 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; animation = TAG_BLOCK_GET_ELEMENT( &animation_graph->animations, unit->unit.animation.aiming_screen_index, struct animation); aiming_screen_apply( 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) && unit->unit.animation.looking_screen_index != NONE) { struct animation *animation; real_vector3d relative_looking_vector; struct animation_aiming_screen_bounds const *looking_bounds = &unit_seat->looking_screen_bounds; 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; animation = TAG_BLOCK_GET_ELEMENT( &animation_graph->animations, unit->unit.animation.looking_screen_index, struct animation); aiming_screen_apply( 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 && code_00198130(&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_indexcount); } } if (unit->unit.current_weapon_index!=NONE) { animation = &unit->unit.animation; weapon_ik_active = code_001980d0(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_indexcount); } 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_stateaiming_speed>=0 && control_data->aiming_speedcontrol_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_indexgrenade_index==NONE || (control_data->grenade_index>=0 && control_data->grenade_indexzoom_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_verify_inventory( 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; real_point3d origin; real_vector3d forward; real_vector3d left; real_vector3d up; real_point3d new_grenade_origin; real_point3d camera_position; 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) { object_get_origin(grenade_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) { 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); point_from_line3d(&camera_position, &forward, player_information->grenade_origin.i, &camera_position); point_from_line3d(&camera_position, &up, player_information->grenade_origin.j, &camera_position); point_from_line3d(&camera_position, &left, player_information->grenade_origin.k, &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; 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_indexobject.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 && indexunit.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, 1, 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_verify_inventory(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_verify_inventory(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_allowedunit.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; }