/* 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 "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 "game/cheats.h" #include "game/game_globals.h" #include "game/game_engine.h" #include "game/players.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 "objects/damage.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 "scenario/scenario.h" #include "sound/game_sound.h" /* ---------- constants */ /* ---------- 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]; }; /* ---------- prototypes */ static char const *base_seat_label_get(short base_seat_index); static short seat_label_to_base_seat_index(char const *seat_label); static char const *base_weapon_label_get(short base_weapon_index); static void unit_refresh_illumination(long unit_index); static 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 unit_animation_busy(struct unit_animation *animation); static boolean unit_set_or_test_seat_and_weapon_label( long object_index, char const *seat_label, char const *weapon_label, boolean change_flag); static boolean unit_animation_set_state( long unit_index, short new_state); static boolean unit_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 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); /* ---------- 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_update( void) { unit_globals->next_timer = unit_globals->highest_timer; unit_globals->highest_timer = 0; unit_globals->used_time = 0; 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_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; } 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; } 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); long 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) { long selected_weapon_last_used_at_game_time; 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]; unit_get(unit_index); weapon_get(unit_index); if (current_weapon_last_used_at_game_time !=NONE && unit_can_use_weapon(unit_index, current_weapon_last_used_at_game_time)) { selected_weapon_last_used_at_game_time = unit->unit.weapon_last_used_at_game_time[inventory_index]; if (delta || selected_weapon_index == NONE || selected_weapon_last_used_at_game_time < current_weapon_last_used_at_game_time) { selected_weapon_index = inventory_index; } if (weapon_must_be_readied(current_weapon_last_used_at_game_time) || inventory_index != current_index) { break; } } if (delta>=0) { inventory_index = inventory_index == (MAXIMUM_WEAPONS_PER_UNIT-1) ? 0 : inventory_index+1; } else { inventory_index = inventory_index ? inventory_index-1 : (MAXIMUM_WEAPONS_PER_UNIT-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 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 unit_animation_set_state( long unit_index, short new_state) { short interpolation_frame_count; 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); boolean valid_state = unit->unit.animation.state!=NONE; boolean v1 = FALSE; boolean result = TRUE; if (unit->unit.animation.state!=NONE || new_state!=unit->unit.animation.state) { boolean valid_animation_index; long 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_diving_front; break; case _unit_state_move_back: weapon_class_animation_index = _unit_weapon_class_animation_diving_back; break; case _unit_state_move_left: weapon_class_animation_index = _unit_weapon_class_animation_diving_left; break; case _unit_state_move_right: weapon_class_animation_index = _unit_weapon_class_animation_diving_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; } } valid_animation_index = animation_index >= 0 && animation_index animations.count; if (debug_unit_animations && unit->object.type==_object_type_biped && !valid_animation_index) { /* if (weapon_class_animation_index == NONE) Format = &Format_; else Format = j_animation_list_get_string(word_12C47CC, weapon_class_animation_index); if (unit_seat_animation_index == -1) element_with_size_2 = weapon_class; else element_with_size_2 = j_animation_list_get_string(&dword_12C47D4, unit_seat_animation_index); v2 = sub_86262C(unit_definition + 56); v3 = j_tag_name_strip_path(v2); 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_index); */ } } if (result) { } return result; } 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) { real_vector3d unit_velocity; real_vector3d item_velocity; real_point3d camera_position; 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; 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); object_get_velocities(unit_index, &unit_velocity, NULL); add_vectors3d(&item_velocity, &unit_velocity, &item_velocity); item_accelerate(item_index, &item_velocity, FALSE); 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 collision_model_test_vector_result vector_result; real_point3d collision_origin; real_point3d collision_point; struct collision_model_instance instance; real_vector3d collision_vector; real_plane3d collision_plane; struct damage_data damage_data; 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) { 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 (0>vector_result.bsp_result.plane_designator) { 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 negative_angle_allowed = 1.f; real positive_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(1.f, negative_angle_bounds_dist / DEGREES_TO_RADIANS(13.5f)); positive_angle_allowed = MIN(1.f, 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 / DEGREES_TO_RADIANS(3.6f)); positive_angle_allowed = MIN(positive_velocity_bounds_dist / DEGREES_TO_RADIANS(3.6f), positive_angle_allowed); } if (negative_angle_allowed>=positive_angle_allowed) { if (positive_angle_allowed>-1.f) { real max_allowed = MIN(1.f, positive_angle_allowed); angular_acceleration_this_tick = real_random_range(-DEGREES_TO_RADIANS(1.2f) * max_allowed, DEGREES_TO_RADIANS(1.2f)); } else { angular_acceleration_this_tick = DEGREES_TO_RADIANS(1.2f); } } else if (negative_angle_allowed<-1.f) { angular_acceleration_this_tick = -DEGREES_TO_RADIANS(1.2f); } else { real min_allowed = MIN(1.f, negative_angle_allowed); angular_acceleration_this_tick = real_random_range(-DEGREES_TO_RADIANS(1.2f), DEGREES_TO_RADIANS(1.2f) * min_allowed); } unit->unit.run_blindly_angle_delta+=angular_acceleration_this_tick; } unit->unit.run_blindly_angle += unit->unit.run_blindly_angle_delta; unit->unit.run_blindly_angle += 2.f * (-PIN(unit->unit.run_blindly_angle, -_pi, _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; } 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; }