mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 15:00:27 +02:00
248 OpenCE commits (network version 25): touch controls and touch menus for phones, single-player in-game Settings, opt-in FOV/viewmodel settings, voice chat and votes to kick, lens flare culling, memory leak fixes (render targets, gamepads, the Android audio stream), profiling builds, the HALO_ANDROID split into HALO_ARM64_GUEST / HALO_GLES / HALO_ANDROID. Conflicts and how they were resolved: - README.md: ours kept whole. Upstream's README changes (portable build, profiling builds) went into docs/OPENCE_README.md: upstream's new text with our header note, links rewritten to ../ and our "made from your own copy" wording. - port/android/README.md: both merged; upstream's touch, WSL and save notes taken, with our application id in every adb path; our VR section, signing.properties and wording kept; the stale "How it works" list dropped as upstream dropped it. - Launcher icons (mipmap-*/ic_launcher_foreground.png, art/android-icon.png): ours (the Ringfall art). Upstream's helmet monochrome layers, drawable/ic_launcher_background.xml and its ic_launcher.xml were left out; our ic_launcher.xml, values/ic_launcher_background.xml and tools/android_icon.py stay. (port/assets/icon and tools/app_icon.py come in for the desktop builds' window icon only; do not run app_icon.py, it would write the helmet into the Android resources.) - port/android/host/host_main.c: both config helpers kept, and upstream's emulator write tracking (native_bridge_active); upstream's display width block dropped in favour of ours (640 in VR) and its display density for the touch controls moved into our flat (non-VR) branch. - port/linux/game/menu_functions.c: platform_text_field(typing, password) from upstream; the VR keyboard launch for text fields kept after it. - port/linux/game/network_distributed.c: upstream's profile_scope wrappers, with network_vr_tick and bots_tick kept in their places. - port/linux/src/dsound_sdl.c: both (our debug.audio_log, upstream's audio.output_device). - port/linux/src/port_config.c: our temp-and-rename config write kept, under upstream's HALO_ARM64_GUEST (it is guest file I/O). - port/linux/src/sdl_platform.c/.h: upstream's (Android menu pointer from the touchscreen, Steam keyboard, scoreboard pointer); our quit marker for the Steam Frame launcher, clipboard_seen and VR frame hooks kept. Our flat-mouse menu pointer (an emulator aid) is replaced by upstream's touch pointer, which also drives the menus on the emulator. - port/linux/src/d3d8_gl.c: halo_ui_pointer_update is upstream's, with the VR controller ray taking over while VR is active (the touchscreen then is not the pointer); our persistent-buffer writes kept for stream and index uploads, now writing upstream's exact length and under HALO_GLES like the code around them (stream_reserve, index_upload, mirror_refresh too); multiview uniforms stay under HALO_ANDROID, the counter binding under HALO_GLES. Visibility counters: ours kept (the snapshot ring with multiview views, host_gl_counters_snapshot) over upstream's per-frame glCopyBufferSubData into counter_snapshots: ours reads finished frames without stalling and counts both eyes' samples once; upstream's desktop query batches come in unchanged. - port/linux/src/xinput_sdl.c: both (VR controllers and touch controls). In VR the touch controls neither show nor act (touch_input_gamepad, touch_input_controls, touch move/look skipped); rumble goes to the controllers in VR, to the phone otherwise. - source/interface/first_person_weapons.c: both (VR scope pass check and display.viewmodel_visible). - source/interface/ui_widget.c: upstream's reworked menu targets taken (ui_mouse_fit_button_targets, merged rows, widened values), which do what our icon_x0 key picking and the 12-unit value arrows did; our VR label distance (25) kept, and a VR ray counts as a coarse pointer, so it gets the taller legend areas and the widened values (not the merged rows: a row's label still presses A). Our join code and VR options panels kept; their early return now forgets the frame's targets too. The on-screen keyboard: upstream's virtual_keyboard_click for taps and the mouse, ours (hover highlights the key) for the VR ray only. - source/main/main.c: both (VR phases around upstream's profile scopes). VR guards for new upstream features: - view_fov.c: display.fov, display.viewmodel_fov and display.viewmodel_visible do nothing while VR is active (the headset gives each eye's frustum; the weapon is in the hand). - Touch controls: off in VR (xinput_sdl.c), the menus' touch pointer too (d3d8_gl.c); the overlay stays hidden as the game never reports a scene. - Lens flare culling (744040c8,b1a71709) uses global_window_parameters' frustum, which in VR is each eye's (per-eye passes) or the union frustum of the multiview centre camera: no change needed. - In-game Settings: VR options still open with Y on the main and pause menus (checked flat with debug.vr_options). - The back button change (enableOnBackInvokedCallback=false) keeps the legacy KEYCODE_BACK path, which targetSdk 35 already had: no change on Quest or Frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3527 lines
107 KiB
C
3527 lines
107 KiB
C
/*
|
|
WEAPONS.C
|
|
|
|
symbols in this file:
|
|
000EA4D0 0010:
|
|
_animation_convert_frame_to_pal (0000)
|
|
000EA4E0 0010:
|
|
_animation_key_frame_index (0000)
|
|
000EA4F0 0020:
|
|
_animation_update (0000)
|
|
000EA510 0020:
|
|
_animation_choose_random_permutation (0000)
|
|
000EA530 0010:
|
|
_weapons_initialize (0000)
|
|
000EA540 0010:
|
|
_weapons_initialize_for_new_map (0000)
|
|
000EA550 0010:
|
|
_weapons_dispose_from_old_map (0000)
|
|
000EA560 0010:
|
|
_weapons_dispose (0000)
|
|
000EA570 00d0:
|
|
_weapon_place (0000)
|
|
000EA640 0050:
|
|
_weapon_preprocess_node_orientations (0000)
|
|
000EA690 0030:
|
|
_weapon_get_label (0000)
|
|
000EA6C0 0020:
|
|
_weapon_set_integrated_light_power (0000)
|
|
000EA6E0 0080:
|
|
_weapon_estimate_time_to_target (0000)
|
|
000EA760 0090:
|
|
_weapon_can_be_fired (0000)
|
|
000EA7F0 0030:
|
|
_weapon_useful (0000)
|
|
000EA820 0070:
|
|
_weapon_compute_movement_penalty (0000)
|
|
000EA890 0010:
|
|
_weapon_melee_attack (0000)
|
|
000EA8A0 0030:
|
|
_weapon_must_be_readied (0000)
|
|
000EA8D0 0030:
|
|
_weapon_is_flag (0000)
|
|
000EA900 0050:
|
|
_weapon_prevents_grenade_throwing (0000)
|
|
000EA950 01b0:
|
|
_weapon_get_first_person_animation_time (0000)
|
|
000EAB00 0030:
|
|
_weapon_overcharged (0000)
|
|
000EAB30 0050:
|
|
_code_000eab30 (0000)
|
|
000EAB80 0050:
|
|
_code_000eab80 (0000)
|
|
000EABD0 0050:
|
|
_code_000eabd0 (0000)
|
|
000EAC20 0040:
|
|
_code_000eac20 (0000)
|
|
000EAC60 0030:
|
|
_code_000eac60 (0000)
|
|
000EAC90 0040:
|
|
_code_000eac90 (0000)
|
|
000EACD0 0050:
|
|
_code_000eacd0 (0000)
|
|
000EAD20 0090:
|
|
_code_000ead20 (0000)
|
|
000EADB0 00f0:
|
|
_code_000eadb0 (0000)
|
|
000EAEA0 0050:
|
|
_code_000eaea0 (0000)
|
|
000EAEF0 00f0:
|
|
_code_000eaef0 (0000)
|
|
000EAFE0 0070:
|
|
_code_000eafe0 (0000)
|
|
000EB050 0040:
|
|
_code_000eb050 (0000)
|
|
000EB090 0090:
|
|
_code_000eb090 (0000)
|
|
000EB120 0080:
|
|
_code_000eb120 (0000)
|
|
000EB1A0 0050:
|
|
_code_000eb1a0 (0000)
|
|
000EB1F0 0020:
|
|
_code_000eb1f0 (0000)
|
|
000EB210 0020:
|
|
_code_000eb210 (0000)
|
|
000EB230 01b0:
|
|
_code_000eb230 (0000)
|
|
000EB3E0 0120:
|
|
_weapon_set_total_rounds (0000)
|
|
000EB500 0010:
|
|
_power (0000)
|
|
000EB510 0010:
|
|
_random (0000)
|
|
000EB520 0190:
|
|
_weapon_new (0000)
|
|
000EB6B0 0060:
|
|
_weapon_delete (0000)
|
|
000EB710 0390:
|
|
_weapon_export_function_values (0000)
|
|
000EBAA0 0220:
|
|
_weapon_handle_potential_inventory_item (0000)
|
|
000EBCC0 00a0:
|
|
_weapon_owner_update (0000)
|
|
000EBD60 0140:
|
|
_weapon_build_weapon_interface_state (0000)
|
|
000EBEA0 0080:
|
|
_weapon_reloading (0000)
|
|
000EBF20 0070:
|
|
_weapon_rotate_zoom_level (0000)
|
|
000EBF90 0160:
|
|
_weapon_get_zoom_magnification (0000)
|
|
000EC0F0 0050:
|
|
_weapon_get_field_of_view (0000)
|
|
000EC140 0060:
|
|
_weapon_prevents_melee_attack (0000)
|
|
000EC1A0 0160:
|
|
_code_000ec1a0 (0000)
|
|
000EC300 00e0:
|
|
_code_000ec300 (0000)
|
|
000EC3E0 00c0:
|
|
_code_000ec3e0 (0000)
|
|
000EC4A0 0080:
|
|
_code_000ec4a0 (0000)
|
|
000EC520 00c0:
|
|
_code_000ec520 (0000)
|
|
000EC5E0 0090:
|
|
_code_000ec5e0 (0000)
|
|
000EC670 0060:
|
|
_code_000ec670 (0000)
|
|
000EC6D0 0060:
|
|
_code_000ec6d0 (0000)
|
|
000EC730 0190:
|
|
_code_000ec730 (0000)
|
|
000EC8C0 00a0:
|
|
_code_000ec8c0 (0000)
|
|
000EC960 0030:
|
|
_code_000ec960 (0000)
|
|
000EC990 0160:
|
|
_weapon_set_current_amount (0000)
|
|
000ECAF0 0080:
|
|
_weapon_ready (0000)
|
|
000ECB70 00a0:
|
|
_weapon_put_away (0000)
|
|
000ECC10 0110:
|
|
_weapon_aim (0000)
|
|
000ECD20 0010:
|
|
_weapon_stop_reload (0000)
|
|
000ECD30 0050:
|
|
_code_000ecd30 (0000)
|
|
000ECD80 0720:
|
|
_code_000ecd80 (0000)
|
|
000ED4A0 07c0:
|
|
_code_000ed4a0 (0000)
|
|
000EDC60 0270:
|
|
_code_000edc60 (0000)
|
|
000EDED0 00d0:
|
|
_code_000eded0 (0000)
|
|
000EDFA0 0100:
|
|
_code_000edfa0 (0000)
|
|
000EE0A0 0080:
|
|
_code_000ee0a0 (0000)
|
|
000EE120 0af0:
|
|
_weapon_update (0000)
|
|
00279248 000e:
|
|
??_C@_0O@NGIMIMAN@weapon_update?$AA@ (0000)
|
|
00279258 0010:
|
|
??_C@_0BA@HKDAKBBH@?$HOsecondary?9blur?$AA@ (0000)
|
|
00279268 000e:
|
|
??_C@_0O@MICLHJCM@?$HOprimary?9blur?$AA@ (0000)
|
|
00279278 001f:
|
|
??_C@_0BP@EKCOHDKN@c?3?2halo?2SOURCE?2items?2weapons?4c?$AA@ (0000)
|
|
00279298 004b:
|
|
??_C@_0EL@OJBCMDA@trigger_index?$DO?$DN0?5?$CG?$CG?5trigger_inde@ (0000)
|
|
002792E8 004e:
|
|
??_C@_0EO@GOIBLAPE@magazine_index?$DO?$DN0?5?$CG?$CG?5magazine_in@ (0000)
|
|
00279338 0033:
|
|
??_C@_0DD@MMDDCEO@new_state?$DO?$DN0?5?$CG?$CG?5new_state?$DMNUMBER@ (0000)
|
|
00279370 0048:
|
|
??_C@_0EI@IEKIMHHO@trigger_index?$DO?$DN0?5?$CG?$CG?5trigger_inde@ (0000)
|
|
002793B8 000d:
|
|
??_C@_0N@PABHPCND@rounds_array?$AA@ (0000)
|
|
002793C8 001e:
|
|
??_C@_0BO@MNJMFFGN@?$CBweapon_is_flag?$CIweapon_index?$CJ?$AA@ (0000)
|
|
002793E8 001f:
|
|
??_C@_0BP@BIBKIDBE@weapon?9?$DOweapon?4primary_trigger?$AA@ (0000)
|
|
00279408 0013:
|
|
??_C@_0BD@CJIOBLLL@magnification?$DO0?40f?$AA@ (0000)
|
|
0027941C 000e:
|
|
??_C@_0O@NODOJABJ@magnification?$AA@ (0000)
|
|
0027942C 0004:
|
|
__real@40470d23 (0000)
|
|
00279430 0004:
|
|
__real@3d00adfd (0000)
|
|
00279434 0012:
|
|
??_C@_0BC@IMIGIGHG@secondary?5trigger?$AA@ (0000)
|
|
00279448 0004:
|
|
__real@3d2aaaab (0000)
|
|
00307140 0600:
|
|
_data_00307140 (0000)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "weapons.h"
|
|
|
|
#include "equipment.h"
|
|
#include "weapon_datum_flags.h"
|
|
#include "weapon_definitions.h"
|
|
#include "weapon_export_function_mode.h"
|
|
#include "projectile_definitions.h"
|
|
#include "projectiles.h"
|
|
|
|
#include "ai/actors.h"
|
|
#include "ai/ai.h"
|
|
#include "cache/cache_files.h"
|
|
#include "cseries/errors.h"
|
|
#include "cseries/profile.h"
|
|
#include "effects/effect_definitions.h"
|
|
#include "effects/effects.h"
|
|
#include "game/cheats.h"
|
|
#include "game/game.h"
|
|
#include "game/game_engine.h"
|
|
#include "game/players.h"
|
|
#include "interface/first_person_weapons.h"
|
|
#include "math/periodic_functions.h"
|
|
#include "models/model_animation_definitions.h"
|
|
#include "objects/damage.h"
|
|
#include "scenario/scenario.h"
|
|
#include "sound/game_sound.h"
|
|
#include "sound/sound_definitions.h"
|
|
#include "units/unit_definitions.h"
|
|
#include "units/units.h"
|
|
#ifdef HALO_ANDROID
|
|
#include "halo_vr.h"
|
|
#include "physics/collisions.h"
|
|
#endif
|
|
|
|
#ifdef HALO_PORT_MAXIMUM_NETWORK_PLAYERS
|
|
#include "bitmaps/bitmap_group.h"
|
|
#include "cache/texture_cache.h"
|
|
#include "effects/contrail_definitions.h"
|
|
#endif
|
|
|
|
/* port/linux/game/pal_tags.c's */
|
|
short pal_tags_first_person_frames(long graph_index, short animation_index, short frames);
|
|
|
|
/* ---------- constants */
|
|
|
|
enum weapon_trigger_flags
|
|
{
|
|
_weapon_trigger_released_since_last_shot_bit = 0,
|
|
_weapon_trigger_was_down_bit,
|
|
_weapon_trigger_toggled_bit,
|
|
_weapon_trigger_useless_bit,
|
|
_weapon_trigger_blurred_bit,
|
|
_weapon_trigger_fired_before_charging_bit,
|
|
NUMBER_OF_WEAPON_TRIGGER_DATUM_FLAGS,
|
|
};
|
|
|
|
enum
|
|
{
|
|
MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON = 2,
|
|
/* port: the magazines a weapon holds (struct _weapon_datum) */
|
|
MAXIMUM_NUMBER_OF_MAGAZINES_PER_WEAPON = 2,
|
|
};
|
|
|
|
/* TU-local copies: no shared header declares these tag/runtime enumerations yet.
|
|
Names follow the HCEA database enumerations; ai.c and actors.c carry their own
|
|
ai unit effect copies, and first_person_weapons.c an animation update result copy. */
|
|
enum trigger_distribution_function
|
|
{
|
|
_trigger_distribution_point = 0,
|
|
_trigger_distribution_horizontal_fan,
|
|
NUMBER_OF_TRIGGER_DISTRIBUTION_FUNCTIONS,
|
|
};
|
|
|
|
enum trigger_firing_effect_type
|
|
{
|
|
_trigger_firing_effect = 0,
|
|
_trigger_overheated_effect,
|
|
_trigger_empty_effect,
|
|
NUMBER_OF_TRIGGER_FIRING_EFFECTS,
|
|
};
|
|
|
|
enum weapon_overcharged_action
|
|
{
|
|
_trigger_overcharged_none = 0,
|
|
_trigger_overcharged_explodes,
|
|
_trigger_overcharged_fire,
|
|
NUMBER_OF_TRIGGER_OVERCHARGED_ACTIONS,
|
|
};
|
|
|
|
enum weapon_secondary_trigger_mode
|
|
{
|
|
_weapon_secondary_trigger_normal = 0,
|
|
_weapon_secondary_trigger_slaved_to_primary,
|
|
_weapon_secondary_trigger_inhibits_primary,
|
|
_weapon_secondary_trigger_loads_alternate_ammunition,
|
|
_weapon_secondary_trigger_loads_multiple_primary_ammunition,
|
|
NUMBER_OF_WEAPON_SECONDARY_TRIGGER_MODES,
|
|
};
|
|
|
|
enum weapon_magazine_flags
|
|
{
|
|
_weapon_magazine_wastes_rounds_when_reloaded_bit = 0,
|
|
_weapon_magazine_must_be_chambered_every_shot_bit,
|
|
NUMBER_OF_WEAPON_MAGAZINE_FLAGS,
|
|
};
|
|
|
|
enum animation_update_result
|
|
{
|
|
_animation_running = 0,
|
|
_animation_key_frame,
|
|
_animation_will_restart_on_next_frame,
|
|
_animation_restarted,
|
|
_animation_looped,
|
|
NUMBER_OF_ANIMATION_UPDATE_RESULTS,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_ai_unit_effect_bump = 0,
|
|
_ai_unit_effect_shooting,
|
|
_ai_unit_effect_death_scream,
|
|
_ai_unit_effect_magic_sight,
|
|
NUMBER_OF_AI_UNIT_EFFECTS,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
/* ---------- structures */
|
|
|
|
struct weapon_interface_magazine_state
|
|
{
|
|
boolean reloading;
|
|
boolean can_fire;
|
|
short rounds_loaded;
|
|
short rounds_loaded_maximum;
|
|
short rounds_remaining;
|
|
short rounds_remaining_maximum;
|
|
};
|
|
|
|
struct weapon_interface_state
|
|
{
|
|
real heat;
|
|
real age;
|
|
boolean overheated;
|
|
byte _pad09;
|
|
short magazine_count;
|
|
struct weapon_interface_magazine_state magazines[2];
|
|
};
|
|
|
|
struct animation_graph_weapon_animations
|
|
{
|
|
long unused1[4];
|
|
struct tag_block animations;
|
|
};
|
|
|
|
struct animation_graph_first_person_weapon_animations
|
|
{
|
|
long unused1[4];
|
|
struct tag_block animations;
|
|
};
|
|
|
|
/* TU-local: weapon_trigger_definition.firing_effects element; no shared header declares it yet. */
|
|
struct trigger_firing_effect
|
|
{
|
|
short shots_lower_bound;
|
|
short shots_upper_bound;
|
|
long unused[8];
|
|
struct tag_reference effects[NUMBER_OF_TRIGGER_FIRING_EFFECTS];
|
|
struct tag_reference damage_effects[NUMBER_OF_TRIGGER_FIRING_EFFECTS];
|
|
};
|
|
|
|
/* ---------- prototypes */
|
|
|
|
static short weapon_magazine_count(
|
|
struct weapon_definition const *weapon_definition);
|
|
static short weapon_trigger_count(
|
|
struct weapon_definition const *weapon_definition);
|
|
static boolean weapon_trigger_magazine_valid(
|
|
struct weapon_definition const *weapon_definition,
|
|
struct weapon_trigger_definition const *trigger_definition);
|
|
static struct weapon_trigger *weapon_trigger_get(
|
|
struct weapon_datum *weapon,
|
|
short trigger_index);
|
|
static struct weapon_magazine *weapon_magazine_get(
|
|
struct weapon_datum *weapon,
|
|
short magazine_index);
|
|
static real weapon_trigger_get_charged_fraction(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
|
|
static boolean weapon_busy(
|
|
long weapon_index);
|
|
static boolean weapon_magazine_state_change_ok(
|
|
long weapon_index);
|
|
static long weapon_get_effect_object_index(
|
|
long weapon_index);
|
|
static long weapon_get_owner_object_index(
|
|
long weapon_index);
|
|
static long weapon_get_projectile_owner_object_index(
|
|
long weapon_index);
|
|
static boolean weapon_trigger_can_fire_again(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_magazine_idle(
|
|
long weapon_index,
|
|
short magazine_index);
|
|
static long weapon_effect_looping_new(
|
|
long weapon_index,
|
|
long effect_index);
|
|
static void weapon_detonate(
|
|
long weapon_index);
|
|
static void weapon_trigger_change_state(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
short new_state,
|
|
short new_state_timer);
|
|
static void weapon_trigger_start_ejection_port(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
boolean chamber);
|
|
static void weapon_state_key_frame(
|
|
long weapon_index);
|
|
static boolean weapon_magazine_state_interruptable(
|
|
short old_state,
|
|
short new_state);
|
|
static long weapon_effect_new(
|
|
long weapon_index,
|
|
long effect_index,
|
|
real effect_scale,
|
|
real effect_error);
|
|
static void weapon_magazine_start_chamber(
|
|
long weapon_index,
|
|
short magazine_index);
|
|
static void weapon_magazine_finish_chamber(
|
|
long weapon_index,
|
|
short magazine_index);
|
|
static void weapon_trigger_fully_charged(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_trigger_idle(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_trigger_locked(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_trigger_recover(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_trigger_finish_tracking(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_reset(
|
|
long weapon_index);
|
|
|
|
static boolean weapon_state_interruptable(
|
|
short old_state,
|
|
short new_state);
|
|
static boolean weapon_set_state(
|
|
long weapon_index,
|
|
short new_state,
|
|
boolean immediate);
|
|
|
|
static void weapon_magazine_finish_reload(
|
|
long weapon_index,
|
|
short magazine_index);
|
|
static void weapon_magazine_start_reload(
|
|
long weapon_index,
|
|
short magazine_index,
|
|
boolean unknown);
|
|
static void weapon_state_next(
|
|
long weapon_index);
|
|
|
|
static void projectile_distribute(
|
|
real_vector3d *forward,
|
|
real_vector3d *up,
|
|
short distribution_function,
|
|
real distribution_angle,
|
|
short projectile_index,
|
|
short projectile_count);
|
|
static void trigger_create_projectiles(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_trigger_fire(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_trigger_begin_firing(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
boolean force);
|
|
static void weapon_trigger_overload(
|
|
long weapon_index,
|
|
long trigger_index);
|
|
static void weapon_trigger_release_charge(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
static void weapon_trigger_overcharged(
|
|
long weapon_index,
|
|
short trigger_index);
|
|
|
|
/* ---------- globals */
|
|
|
|
struct weapons_globals
|
|
{
|
|
char *blurred_permutation_names[2];
|
|
struct profile_section update_profile;
|
|
};
|
|
|
|
static struct weapons_globals data_00307140 =
|
|
{
|
|
{"~primary-blur", "~secondary-blur"},
|
|
{"weapon_update", NONE, TRUE}
|
|
};
|
|
|
|
/* ---------- public code */
|
|
|
|
void weapons_initialize(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void weapons_initialize_for_new_map(
|
|
void)
|
|
{
|
|
struct tag_iterator iterator;
|
|
long weapon_definition_index;
|
|
|
|
/* port: a weapon holds two magazines and two triggers. A weapon tag with
|
|
more is cut down to two here, before any weapon is made, or every loop
|
|
over them writes past the weapon. Tool never builds more, so the
|
|
released maps are untouched. A trigger's magazine past the magazines is
|
|
only reported here. The weapon skips it where it fires. */
|
|
tag_iterator_new(&iterator, WEAPON_DEFINITION_TAG);
|
|
while ((weapon_definition_index= tag_iterator_next(&iterator))!=NONE)
|
|
{
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon_definition_index);
|
|
short trigger_index;
|
|
|
|
if (weapon_definition->weapon.magazines.count<0 ||
|
|
weapon_definition->weapon.magazines.count>MAXIMUM_NUMBER_OF_MAGAZINES_PER_WEAPON)
|
|
{
|
|
error(_error_silent, "weapon tag 0x%08lX has %ld magazines (cut to %d)",
|
|
(unsigned long)weapon_definition_index, weapon_definition->weapon.magazines.count, MAXIMUM_NUMBER_OF_MAGAZINES_PER_WEAPON);
|
|
weapon_definition->weapon.magazines.count= weapon_definition->weapon.magazines.count<0 ? 0 : MAXIMUM_NUMBER_OF_MAGAZINES_PER_WEAPON;
|
|
}
|
|
|
|
if (weapon_definition->weapon.triggers.count<0 ||
|
|
weapon_definition->weapon.triggers.count>MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON)
|
|
{
|
|
error(_error_silent, "weapon tag 0x%08lX has %ld triggers (cut to %d)",
|
|
(unsigned long)weapon_definition_index, weapon_definition->weapon.triggers.count, MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON);
|
|
weapon_definition->weapon.triggers.count= weapon_definition->weapon.triggers.count<0 ? 0 : MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON;
|
|
}
|
|
|
|
for (trigger_index= 0; trigger_index<weapon_trigger_count(weapon_definition); trigger_index++)
|
|
{
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
if (!weapon_trigger_magazine_valid(weapon_definition, trigger_definition))
|
|
{
|
|
error(_error_silent, "weapon tag 0x%08lX trigger #%d has bad magazine #%d (it won't fire)",
|
|
(unsigned long)weapon_definition_index, trigger_index, trigger_definition->magazine_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void weapons_dispose_from_old_map(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void weapons_dispose(
|
|
void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef HALO_PORT_MAXIMUM_NETWORK_PLAYERS
|
|
/* A trail's first draw requests its bitmap asynchronously and is skipped
|
|
until that read finishes. Warm only the projectile trails of a weapon that
|
|
has been created or readied, before its first shot. Keep ordinary
|
|
streaming nonblocking and leave the original trail tags and lifetime alone. */
|
|
static void weapon_precache_projectile_trails(
|
|
long definition_index)
|
|
{
|
|
if (definition_index != NONE)
|
|
{
|
|
struct weapon_definition *definition = weapon_definition_get(definition_index);
|
|
short trigger_index;
|
|
|
|
for (trigger_index = 0; trigger_index < definition->weapon.triggers.count; trigger_index++)
|
|
{
|
|
struct weapon_trigger_definition *trigger = TAG_BLOCK_GET_ELEMENT(
|
|
&definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
if (trigger->projectile.index != NONE)
|
|
{
|
|
struct projectile_definition *projectile = projectile_definition_get(trigger->projectile.index);
|
|
short attachment_index;
|
|
|
|
for (attachment_index = 0; attachment_index < projectile->object.attachments.count; attachment_index++)
|
|
{
|
|
struct object_attachment_definition *attachment = TAG_BLOCK_GET_ELEMENT(
|
|
&projectile->object.attachments, attachment_index, struct object_attachment_definition);
|
|
|
|
if (attachment->type.group_tag == CONTRAIL_DEFINITION_TAG && attachment->type.index != NONE)
|
|
{
|
|
struct contrail_definition *contrail = contrail_definition_get(attachment->type.index);
|
|
|
|
if (contrail->bitmap.index != NONE)
|
|
{
|
|
struct bitmap_group *bitmap = bitmap_group_get(contrail->bitmap.index);
|
|
short bitmap_index;
|
|
|
|
for (bitmap_index = 0; bitmap_index < bitmap->bitmaps.count; bitmap_index++)
|
|
{
|
|
_texture_cache_bitmap_get_hardware_format(
|
|
TAG_BLOCK_GET_ELEMENT(&bitmap->bitmaps, bitmap_index, struct bitmap_data),
|
|
FALSE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void weapon_place(
|
|
long weapon_index,
|
|
struct scenario_weapon_datum *scenario_weapon)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
if (weapon_definition->weapon.magazines.count>0)
|
|
{
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, 0, struct weapon_magazine_definition);
|
|
|
|
weapon->weapon.magazines[0].rounds_total = MIN(scenario_weapon->rounds_total, magazine_definition->rounds_total_maximum);
|
|
weapon->weapon.magazines[0].rounds_loaded = MIN(scenario_weapon->rounds_loaded, magazine_definition->rounds_loaded_maximum);
|
|
}
|
|
|
|
SET_FLAG(weapon->object.flags, _object_at_rest_bit, TEST_FLAG(scenario_weapon->flags, _weapon_created_at_rest_bit));
|
|
SET_FLAG(weapon->object.flags, _object_cannot_be_garbage_bit, TRUE);
|
|
SET_FLAG(weapon->item.flags, _item_does_not_accelerate_bit, !TEST_FLAG(scenario_weapon->flags, _weapon_does_accelerate_bit));
|
|
|
|
if (!TEST_FLAG(scenario_weapon->flags, _weapon_created_at_rest_bit))
|
|
weapon->object.position.z += 0.05f;
|
|
|
|
return;
|
|
}
|
|
|
|
void weapon_ready(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum* weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
#ifdef HALO_PORT_MAXIMUM_NETWORK_PLAYERS
|
|
weapon_precache_projectile_trails(weapon->definition_index);
|
|
#endif
|
|
weapon_reset(weapon_index);
|
|
weapon_set_state(weapon_index, _weapon_state_ready, TRUE);
|
|
first_person_weapon_message_from_weapon(weapon_index, _first_person_weapon_message_ready);
|
|
weapon_effect_new(weapon_index, weapon_definition->weapon.ready_effect.index, 0.f, 0.f);
|
|
weapon->weapon.state_timer = weapon_get_first_person_animation_time(weapon_index, 0, _first_person_weapon_animation_ready, NONE);
|
|
|
|
return;
|
|
}
|
|
|
|
boolean weapon_put_away(
|
|
long weapon_index,
|
|
boolean immediate)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
boolean put_away = FALSE;
|
|
|
|
if ((immediate || !weapon_busy(weapon_index)) && weapon_set_state(weapon_index, _weapon_state_put_away, immediate))
|
|
{
|
|
weapon->weapon.control_flags = 0;
|
|
weapon_reset(weapon_index);
|
|
|
|
if (weapon->weapon.overheated_effect_index != NONE)
|
|
{
|
|
effect_delete(weapon->weapon.overheated_effect_index);
|
|
weapon->weapon.overheated_effect_index = NONE;
|
|
}
|
|
|
|
first_person_weapon_message_from_weapon(weapon_index, 11);
|
|
put_away = TRUE;
|
|
}
|
|
|
|
return put_away;
|
|
}
|
|
|
|
boolean weapon_aim(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
real_point3d const *origin,
|
|
real_point3d const *target_point,
|
|
boolean lob,
|
|
real_vector3d *result_aim_vector,
|
|
real *result_ticks,
|
|
real *result_distance,
|
|
boolean *result_linear)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
boolean result = FALSE;
|
|
|
|
if (trigger_index>=0 && trigger_index<weapon_definition->weapon.triggers.count)
|
|
{
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
projectile_aim(projectile_definition_get(trigger_definition->projectile.index), origin, target_point, NULL, NULL, NULL, NULL, lob, result_aim_vector, NULL, result_ticks, result_distance, result_linear);
|
|
match_assert_valid_real_normal3d("c:\\halo\\SOURCE\\items\\weapons.c", 1301, result_aim_vector);
|
|
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean weapon_is_flag(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
return (weapon_definition->weapon.flags>>3)&1;
|
|
}
|
|
|
|
boolean weapon_must_be_readied(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
return (weapon_definition->weapon.flags>>3)&1;
|
|
}
|
|
|
|
boolean weapon_overcharged(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
return weapon->weapon.triggers[0].state==_trigger_charging || weapon->weapon.triggers[0].state==_trigger_charged;
|
|
}
|
|
|
|
void weapon_stop_reload(
|
|
long weapon_index)
|
|
{
|
|
weapon_reset(weapon_index);
|
|
|
|
return;
|
|
}
|
|
|
|
char const *weapon_get_label(
|
|
long weapon_index)
|
|
{
|
|
char const *label = "";
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
label = weapon_definition->weapon.label;
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
boolean weapon_useful(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
if (weapon->weapon.age>=1.0f)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void weapon_set_integrated_light_power(
|
|
long weapon_index,
|
|
real light_power)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
weapon->weapon.integrated_light_power = light_power;
|
|
|
|
return;
|
|
}
|
|
|
|
boolean weapon_prevents_grenade_throwing(
|
|
long weapon_index)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
result = (weapon_definition->weapon.flags>>6)&1;
|
|
if (weapon->weapon.state>=_weapon_state_primary_reload && weapon->weapon.state<=_weapon_state_put_away)
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean weapon_prevents_melee_attack(
|
|
long weapon_index)
|
|
{
|
|
boolean result = TRUE;
|
|
|
|
if (weapon_index!=NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
result = (weapon_definition->weapon.flags>>9)&1;
|
|
if (weapon_overcharged(weapon_index))
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void weapon_melee_attack(
|
|
long weapon_index)
|
|
{
|
|
return;
|
|
}
|
|
|
|
boolean weapon_new(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
short magazine_index;
|
|
short trigger_index;
|
|
|
|
weapon->weapon.state = _weapon_state_idle;
|
|
weapon->weapon.overheated_effect_index = NONE;
|
|
|
|
for (magazine_index = 0; magazine_index<weapon_magazine_count(weapon_definition); ++magazine_index)
|
|
{
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
|
|
if (!magazine)
|
|
continue;
|
|
magazine->rounds_loaded = MIN(magazine_definition->rounds_total_initial, magazine_definition->rounds_loaded_maximum);
|
|
magazine->rounds_total = magazine_definition->rounds_total_initial-magazine->rounds_loaded;
|
|
}
|
|
|
|
for (trigger_index = 0; trigger_index<weapon_trigger_count(weapon_definition); ++trigger_index)
|
|
{
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
|
|
(void)TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
if (!trigger)
|
|
continue;
|
|
trigger->charging_effect_index = NONE;
|
|
trigger->idle_ticks = 127;
|
|
}
|
|
|
|
#ifdef HALO_PORT_MAXIMUM_NETWORK_PLAYERS
|
|
weapon_precache_projectile_trails(weapon->definition_index);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
void weapon_set_total_rounds(
|
|
long weapon_index,
|
|
short *rounds_array)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
short magazine_index;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\items\\weapons.c", 3082, rounds_array);
|
|
|
|
for (magazine_index = 0; magazine_index<weapon_magazine_count(weapon_definition); ++magazine_index)
|
|
{
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
|
|
if (!magazine)
|
|
continue;
|
|
magazine->rounds_total = MIN(magazine_definition->rounds_total_maximum, rounds_array[magazine_index]);
|
|
magazine->rounds_loaded = MIN(magazine->rounds_loaded, magazine->rounds_total);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void weapon_delete(
|
|
long weapon_index)
|
|
{
|
|
if (game_engine_running())
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\items\\weapons.c", 234, !weapon_is_flag(weapon_index));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void weapon_export_function_values(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct object_datum *object= (struct object_datum *)weapon;
|
|
real *function_values;
|
|
short *function_modes;
|
|
long function_index;
|
|
|
|
while (TEST_FLAG(object->object.flags, _object_invisible_bit) && object->object.parent_object_index!=NONE)
|
|
{
|
|
object= object_get(object->object.parent_object_index);
|
|
}
|
|
|
|
function_values= object->object.incoming_function_values;
|
|
function_modes= weapon_definition->weapon.function_modes;
|
|
|
|
for (function_index= NUMBER_OF_INCOMING_OBJECT_FUNCTIONS; function_index; --function_index, ++function_modes, ++function_values)
|
|
{
|
|
if (*function_modes!=_weapon_function_none)
|
|
{
|
|
real function_value= 0.0f;
|
|
|
|
switch (*function_modes)
|
|
{
|
|
case _weapon_function_ready:
|
|
function_value= 1.0f;
|
|
break;
|
|
|
|
case _weapon_function_heat:
|
|
function_value= weapon->weapon.heat;
|
|
break;
|
|
|
|
case _weapon_function_overheated:
|
|
if (TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit) && weapon_definition->weapon.heat_recovery_threshold!=1.0f)
|
|
{
|
|
function_value= (weapon->weapon.heat-weapon_definition->weapon.heat_recovery_threshold)/(1.0f-weapon_definition->weapon.heat_recovery_threshold);
|
|
}
|
|
break;
|
|
|
|
case _weapon_function_illumination:
|
|
{
|
|
short trigger_index;
|
|
|
|
for (trigger_index= 0; trigger_index<weapon_trigger_count(weapon_definition); ++trigger_index)
|
|
{
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
|
|
if (!trigger)
|
|
continue;
|
|
if (trigger_definition->charging_time>0.0f)
|
|
{
|
|
real charged_illumination= weapon_trigger_get_charged_fraction(weapon_index, trigger_index)*trigger_definition->charged_illumination;
|
|
|
|
function_value= MAX(function_value, charged_illumination);
|
|
}
|
|
|
|
if (trigger->state==_trigger_charged)
|
|
{
|
|
function_value= MAX(function_value, (1.0f-trigger_definition->charged_illumination)*weapon->weapon.overcharged+trigger_definition->charged_illumination);
|
|
}
|
|
|
|
function_value= MAX(function_value, trigger->illumination);
|
|
trigger->illumination= function_value;
|
|
}
|
|
|
|
function_value= MAX(function_value, weapon_definition->weapon.heat_illumination*weapon->weapon.heat);
|
|
}
|
|
break;
|
|
|
|
case _weapon_function_primary_ammunition:
|
|
case _weapon_function_secondary_ammunition:
|
|
{
|
|
short magazine_index= (short)(*function_modes-_weapon_function_primary_ammunition);
|
|
|
|
if (magazine_index<weapon_definition->weapon.magazines.count)
|
|
{
|
|
struct weapon_magazine_definition *magazine_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
|
|
if (magazine_definition->rounds_loaded_maximum)
|
|
{
|
|
function_value= (real)weapon->weapon.magazines[magazine_index].rounds_loaded/magazine_definition->rounds_loaded_maximum;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _weapon_function_primary_ejection_port:
|
|
case _weapon_function_secondary_ejection_port:
|
|
{
|
|
short trigger_index= (short)(*function_modes-_weapon_function_primary_ejection_port);
|
|
|
|
if (trigger_index<weapon_definition->weapon.triggers.count)
|
|
{
|
|
function_value= weapon->weapon.triggers[trigger_index].ejection_port_position;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _weapon_function_primary_rate_of_fire:
|
|
case _weapon_function_secondary_rate_of_fire:
|
|
{
|
|
short trigger_index= (short)(*function_modes-_weapon_function_primary_rate_of_fire);
|
|
|
|
if (trigger_index<weapon_definition->weapon.triggers.count)
|
|
{
|
|
function_value= weapon->weapon.triggers[trigger_index].rate_of_fire;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _weapon_function_primary_firing:
|
|
case _weapon_function_secondary_firing:
|
|
{
|
|
short trigger_index= (short)(*function_modes-_weapon_function_primary_firing);
|
|
|
|
if (trigger_index<weapon_definition->weapon.triggers.count)
|
|
{
|
|
function_value= weapon->weapon.triggers[trigger_index].rate_of_fire;
|
|
|
|
if (game_time_get()-weapon->weapon.game_time_last_fired>1)
|
|
{
|
|
function_value= 0.0f;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _weapon_function_primary_charged:
|
|
case _weapon_function_secondary_charged:
|
|
{
|
|
short trigger_index= (short)(*function_modes-_weapon_function_primary_charged);
|
|
|
|
if (trigger_index<weapon_definition->weapon.triggers.count)
|
|
{
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
|
|
function_value= weapon_trigger_get_charged_fraction(weapon_index, trigger_index);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _weapon_function_integrated_light:
|
|
function_value= weapon->weapon.integrated_light_power;
|
|
break;
|
|
|
|
case _weapon_function_age:
|
|
function_value= weapon->weapon.age;
|
|
break;
|
|
}
|
|
|
|
*function_values= function_value;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean weapon_handle_potential_inventory_item(
|
|
long weapon_index,
|
|
long item_object_index,
|
|
short local_player_index,
|
|
short *rounds_picked_up)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct item_datum *item = item_get(item_object_index);
|
|
long item_definition_index = item->definition_index;
|
|
boolean handled = FALSE;
|
|
short magazine_index;
|
|
|
|
for (magazine_index = 0; magazine_index<weapon_magazine_count(weapon_definition); ++magazine_index)
|
|
{
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
|
|
if (magazine && magazine->rounds_total<magazine_definition->rounds_total_maximum)
|
|
{
|
|
short rounds_needed = magazine_definition->rounds_total_maximum-magazine->rounds_total;
|
|
short rounds_taken = 0;
|
|
|
|
if (weapon->definition_index==item_definition_index)
|
|
{
|
|
struct weapon_datum *item_weapon = weapon_get(item_object_index);
|
|
struct weapon_magazine *item_magazine = weapon_magazine_get(item_weapon, magazine_index);
|
|
|
|
rounds_taken = MIN(item_magazine->rounds_total, rounds_needed);
|
|
|
|
if (rounds_taken>0)
|
|
{
|
|
item_magazine->rounds_total -= rounds_taken;
|
|
|
|
if (weapon_definition->weapon.pickup_sound.index!=NONE && local_player_index!=NONE)
|
|
unspatialized_impulse_sound_new(weapon_definition->weapon.pickup_sound.index, 1.0f);
|
|
|
|
if (item_magazine->rounds_total==0)
|
|
object_delete(item_object_index);
|
|
}
|
|
|
|
handled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
short ammunition_index;
|
|
|
|
for (ammunition_index = 0; ammunition_index<magazine_definition->ammunition_objects.count; ++ammunition_index)
|
|
{
|
|
struct weapon_ammunition_object *ammunition_object = TAG_BLOCK_GET_ELEMENT(&magazine_definition->ammunition_objects, ammunition_index, struct weapon_ammunition_object);
|
|
|
|
if (ammunition_object->object.index==item_definition_index)
|
|
{
|
|
rounds_taken = MIN(ammunition_object->rounds, rounds_needed);
|
|
|
|
if (rounds_taken>0)
|
|
{
|
|
if (local_player_index!=NONE)
|
|
equipment_definition_handle_pickup(ammunition_object->object.index);
|
|
|
|
object_delete(item_object_index);
|
|
handled = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
magazine->rounds_total += rounds_taken;
|
|
*rounds_picked_up = rounds_taken;
|
|
}
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
short animation_choose_random_permutation(
|
|
long animation_graph_index,
|
|
short animation_index)
|
|
{
|
|
return animation_choose_random_permutation_internal(TRUE, animation_graph_index, animation_index);
|
|
}
|
|
|
|
short animation_update(
|
|
long animation_graph_index,
|
|
struct animation_state *state,
|
|
long *sound_index)
|
|
{
|
|
return animation_update_internal(TRUE, animation_graph_index, state, sound_index);
|
|
}
|
|
|
|
short animation_key_frame_index(
|
|
struct animation const *animation)
|
|
{
|
|
return animation->private_key_frame_index;
|
|
}
|
|
|
|
short animation_convert_frame_to_pal(
|
|
struct animation const *animation,
|
|
short frame_index)
|
|
{
|
|
return frame_index;
|
|
}
|
|
|
|
boolean weapon_reloading(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
boolean result = FALSE;
|
|
|
|
if (weapon_definition->weapon.magazines.count>0)
|
|
{
|
|
if (weapon_magazine_get(weapon, 0)->state==_magazine_reloading)
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
short weapon_rotate_zoom_level(
|
|
long weapon_index,
|
|
short zoom_level)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
if (!weapon_reloading(weapon_index))
|
|
{
|
|
if (zoom_level>=0 && zoom_level<weapon_definition->weapon.zoom_level_count-1)
|
|
zoom_level++;
|
|
else
|
|
zoom_level = zoom_level==weapon_definition->weapon.zoom_level_count-1 ? NONE : 0;
|
|
}
|
|
|
|
return zoom_level;
|
|
}
|
|
|
|
real weapon_get_zoom_magnification(
|
|
long weapon_index,
|
|
short zoom_level)
|
|
{
|
|
real magnification = 1.0f;
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
if (zoom_level>=0 && zoom_level<weapon_definition->weapon.zoom_level_count)
|
|
{
|
|
real zoom_fraction = weapon_definition->weapon.zoom_level_count>1 ? (real)zoom_level/(weapon_definition->weapon.zoom_level_count-1) : 0.0f;
|
|
real minimum_magnification = weapon_definition->weapon.zoom_magnification_minimum>0.0f ? weapon_definition->weapon.zoom_magnification_minimum : 1.0f;
|
|
real maximum_magnification = weapon_definition->weapon.zoom_magnification_maximum>0.0f ? weapon_definition->weapon.zoom_magnification_maximum : 1.0f;
|
|
|
|
magnification = power(maximum_magnification/minimum_magnification, zoom_fraction)*minimum_magnification;
|
|
|
|
match_assert_valid_real("c:\\halo\\SOURCE\\items\\weapons.c", 1442, magnification);
|
|
match_assert("c:\\halo\\SOURCE\\items\\weapons.c", 1443, magnification>0.0f);
|
|
}
|
|
|
|
return magnification;
|
|
}
|
|
|
|
real weapon_get_field_of_view(
|
|
long weapon_index,
|
|
real field_of_view,
|
|
short zoom_level)
|
|
{
|
|
real result = field_of_view;
|
|
real magnification = weapon_get_zoom_magnification(weapon_index, zoom_level);
|
|
|
|
if (magnification!=1.0f)
|
|
{
|
|
real zoom_field_of_view = field_of_view/magnification;
|
|
|
|
if (zoom_field_of_view>_pi/100.f && zoom_field_of_view<_pi-_pi/100.f)
|
|
result = zoom_field_of_view;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void weapon_build_weapon_interface_state(
|
|
long weapon_index,
|
|
struct weapon_interface_state *state)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
short magazine_index;
|
|
|
|
/* port: every caller's state is unset stack, so magazines past the
|
|
weapon's own count read as empty rather than as whatever was there */
|
|
csmemset(state, 0, sizeof(*state));
|
|
state->heat = weapon->weapon.heat;
|
|
state->age = weapon->weapon.age;
|
|
state->overheated = TEST_FLAG(weapon->weapon.flags, 0);
|
|
/* port: the state's magazines are the two the hud keeps on its stack,
|
|
so no more than two are filled in or counted */
|
|
state->magazine_count = (short)MIN(weapon_magazine_count(weapon_definition), (short)NUMBEROF(state->magazines));
|
|
|
|
for (magazine_index = 0; magazine_index<state->magazine_count; ++magazine_index)
|
|
{
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
long reloading;
|
|
|
|
if (!magazine)
|
|
continue;
|
|
reloading = magazine->state==_magazine_reloading || magazine->state==_magazine_chambering;
|
|
state->magazines[magazine_index].reloading = reloading;
|
|
state->magazines[magazine_index].can_fire = magazine->state==_magazine_idle;
|
|
state->magazines[magazine_index].rounds_loaded = magazine->rounds_loaded;
|
|
state->magazines[magazine_index].rounds_loaded_maximum = magazine_definition->rounds_loaded_maximum;
|
|
state->magazines[magazine_index].rounds_remaining = magazine->rounds_total;
|
|
state->magazines[magazine_index].rounds_remaining_maximum = magazine_definition->rounds_total_maximum;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void weapon_set_current_amount(
|
|
long weapon_index,
|
|
real amount)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
boolean uses_age = FALSE;
|
|
|
|
if (weapon_definition->weapon.magazines.count==0)
|
|
{
|
|
uses_age = TRUE;
|
|
}
|
|
else
|
|
{
|
|
short trigger_index;
|
|
|
|
for (trigger_index = 0; trigger_index<weapon_trigger_count(weapon_definition); ++trigger_index)
|
|
{
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
if (trigger_definition->age_generated_per_round>0.0f)
|
|
{
|
|
uses_age = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (amount<0.0f)
|
|
amount = 0.0f;
|
|
else if (amount>1.0f)
|
|
amount = 1.0f;
|
|
|
|
if (uses_age)
|
|
{
|
|
weapon->weapon.age = 1.0f-amount;
|
|
}
|
|
else if (weapon_definition->weapon.magazines.count>0)
|
|
{
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, 0, struct weapon_magazine_definition);
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, 0);
|
|
short rounds_loaded;
|
|
|
|
amount = magazine_definition->rounds_loaded_maximum*amount;
|
|
rounds_loaded = (short)fast_ftol(amount);
|
|
|
|
magazine->rounds_total += rounds_loaded-magazine->rounds_loaded;
|
|
magazine->rounds_loaded = rounds_loaded;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
short weapon_get_first_person_animation_time(
|
|
long weapon_index,
|
|
short mode,
|
|
short animation_type,
|
|
short shotgun_reload_type)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
short time = 0;
|
|
|
|
if (weapon_definition->weapon.interface_definition.first_person_animations.index!=NONE)
|
|
{
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(weapon_definition->weapon.interface_definition.first_person_animations.index);
|
|
|
|
if (animation_graph->first_person_weapon_animations.count)
|
|
{
|
|
struct animation_graph_first_person_weapon_animations *weapon_animations = TAG_BLOCK_GET_ELEMENT(&animation_graph->first_person_weapon_animations, 0, struct animation_graph_first_person_weapon_animations);
|
|
|
|
if (weapon_animations && animation_type>=0 && animation_type<weapon_animations->animations.count)
|
|
{
|
|
short animation_index = animation_graph_animation_index_get(&weapon_animations->animations)[animation_type].animation_index;
|
|
|
|
if (animation_index!=NONE)
|
|
{
|
|
struct animation *animation = TAG_BLOCK_GET_ELEMENT(&animation_graph->animations, animation_index, struct animation);
|
|
|
|
switch (mode)
|
|
{
|
|
case _weapon_first_person_animation_time_frame_count:
|
|
time = animation->frame_count;
|
|
/* port: a PAL map's animation, the NTSC maps' frame count (port/linux/game/pal_tags.c) */
|
|
time = pal_tags_first_person_frames(weapon_definition->weapon.interface_definition.first_person_animations.index,
|
|
animation_index, time);
|
|
break;
|
|
|
|
case _weapon_first_person_animation_time_private_key_frame:
|
|
time = animation->private_key_frame_index;
|
|
break;
|
|
|
|
default:
|
|
match_vassert("c:\\halo\\SOURCE\\items\\weapons.c", 1588, FALSE, NULL);
|
|
break;
|
|
}
|
|
|
|
if (mode==_weapon_first_person_animation_time_frame_count && weapon_definition->weapon.weapon_type==_weapon_type_shotgun)
|
|
{
|
|
struct animation *shotgun_enter = TAG_BLOCK_GET_ELEMENT(&animation_graph->animations, _first_person_weapon_animation_shotgun_enter<weapon_animations->animations.count ? animation_graph_animation_index_get(&weapon_animations->animations)[_first_person_weapon_animation_shotgun_enter].animation_index : NONE, struct animation);
|
|
struct animation *shotgun_exit_empty = TAG_BLOCK_GET_ELEMENT(&animation_graph->animations, _first_person_weapon_animation_shotgun_exit_empty<weapon_animations->animations.count ? animation_graph_animation_index_get(&weapon_animations->animations)[_first_person_weapon_animation_shotgun_exit_empty].animation_index : NONE, struct animation);
|
|
struct animation *shotgun_exit_full = TAG_BLOCK_GET_ELEMENT(&animation_graph->animations, _first_person_weapon_animation_shotgun_exit_full<weapon_animations->animations.count ? animation_graph_animation_index_get(&weapon_animations->animations)[_first_person_weapon_animation_shotgun_exit_full].animation_index : NONE, struct animation);
|
|
|
|
/* January resolves all three reload variants, but both handled phases use the enter animation. */
|
|
(void)shotgun_exit_empty;
|
|
(void)shotgun_exit_full;
|
|
switch (shotgun_reload_type)
|
|
{
|
|
case _shotgun_reload_type_first_round:
|
|
time = shotgun_enter->frame_count;
|
|
break;
|
|
|
|
case _shotgun_reload_type_first_and_last_round:
|
|
time = shotgun_enter->frame_count;
|
|
break;
|
|
}
|
|
/* port: the NTSC maps' frame count, as above */
|
|
if ((shotgun_reload_type == _shotgun_reload_type_first_round ||
|
|
shotgun_reload_type == _shotgun_reload_type_first_and_last_round) &&
|
|
_first_person_weapon_animation_shotgun_enter < weapon_animations->animations.count)
|
|
{
|
|
time = pal_tags_first_person_frames(
|
|
weapon_definition->weapon.interface_definition.first_person_animations.index,
|
|
animation_graph_animation_index_get(&weapon_animations->animations)[_first_person_weapon_animation_shotgun_enter].animation_index,
|
|
time);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return time;
|
|
}
|
|
|
|
real weapon_estimate_time_to_target(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
real distance)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
real result = 0.0f;
|
|
|
|
if (trigger_index>=0 && trigger_index<weapon_definition->weapon.triggers.count)
|
|
{
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
result = projectile_estimate_time_to_target(projectile_definition_get(trigger_definition->projectile.index), distance);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean weapon_can_be_fired(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
boolean result;
|
|
|
|
if (weapon->weapon.age>=1.0f)
|
|
{
|
|
result = FALSE;
|
|
}
|
|
else if (game_engine_running() &&
|
|
weapon_definition->weapon.magazines.count>0 &&
|
|
TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, 0, struct weapon_magazine_definition)->rounds_loaded_maximum>0 &&
|
|
!weapon->weapon.magazines[0].rounds_loaded &&
|
|
!weapon->weapon.magazines[0].rounds_total)
|
|
{
|
|
result = FALSE;
|
|
}
|
|
else
|
|
{
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
real weapon_compute_movement_penalty(
|
|
long weapon_index,
|
|
boolean forward,
|
|
boolean airborne)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
real penalty;
|
|
|
|
if (forward)
|
|
penalty = weapon_definition->weapon.forward_movement_penalty;
|
|
else
|
|
penalty = weapon_definition->weapon.sideways_movement_penalty;
|
|
|
|
switch (weapon_definition->weapon.movement_penalty_mode)
|
|
{
|
|
case 1:
|
|
if (!airborne)
|
|
penalty = 0.0f;
|
|
break;
|
|
|
|
case 2:
|
|
if ((weapon->weapon.magazines[0].state==_magazine_reloading || weapon->weapon.magazines[1].state==_magazine_reloading) && !airborne)
|
|
penalty = 0.0f;
|
|
break;
|
|
}
|
|
|
|
return penalty;
|
|
}
|
|
|
|
void weapon_owner_update(
|
|
long weapon_index,
|
|
word control_flags,
|
|
real primary_trigger)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
weapon->weapon.control_flags = control_flags;
|
|
weapon->weapon.primary_trigger = transition_function_evaluate(4, primary_trigger);
|
|
match_assert_valid_real("c:\\halo\\SOURCE\\items\\weapons.c", 1199, weapon->weapon.primary_trigger);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_magazine_finish_reload(
|
|
long weapon_index,
|
|
short magazine_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
long rounds_to_load;
|
|
short rounds_loaded;
|
|
|
|
if (!magazine)
|
|
return;
|
|
|
|
if (TEST_FLAG(magazine_definition->flags, 0))
|
|
magazine->rounds_loaded = 0;
|
|
|
|
rounds_to_load = magazine_definition->rounds_reloaded>magazine->rounds_total ? magazine->rounds_total : magazine_definition->rounds_reloaded;
|
|
rounds_loaded = magazine->rounds_loaded+rounds_to_load;
|
|
if (rounds_loaded>magazine_definition->rounds_loaded_maximum)
|
|
rounds_loaded = magazine_definition->rounds_loaded_maximum;
|
|
|
|
if (!cheat.infinite_ammo && TEST_FLAG(weapon->item.flags, 1))
|
|
magazine->rounds_total = magazine->rounds_total-rounds_loaded+magazine->rounds_loaded;
|
|
|
|
magazine->rounds_loaded = rounds_loaded;
|
|
magazine->state = _magazine_unchambered;
|
|
magazine->state_timer = 0;
|
|
|
|
if (magazine->rounds_total>0 &&
|
|
magazine->rounds_loaded<magazine_definition->rounds_loaded_maximum &&
|
|
!TEST_FLAG(magazine_definition->flags, 0) &&
|
|
!(weapon->weapon.control_flags & 0x26))
|
|
{
|
|
weapon_magazine_start_reload(weapon_index, magazine_index, FALSE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_magazine_start_chamber(
|
|
long weapon_index,
|
|
short magazine_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
|
|
if (magazine &&
|
|
weapon_magazine_state_interruptable(magazine->state, _magazine_chambering) &&
|
|
weapon_magazine_state_change_ok(weapon_index))
|
|
{
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.magazines,
|
|
magazine_index,
|
|
struct weapon_magazine_definition);
|
|
|
|
weapon_set_state(
|
|
weapon_index,
|
|
(short)(_weapon_state_primary_chamber + magazine_index),
|
|
FALSE);
|
|
weapon_effect_new(
|
|
weapon_index,
|
|
magazine_definition->chambering_effect.index,
|
|
0.0f,
|
|
0.0f);
|
|
magazine->state = _magazine_chambering;
|
|
magazine->state_timer = (short)(magazine_definition->chamber_time * TICKS_PER_SECOND);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_magazine_finish_chamber(
|
|
long weapon_index,
|
|
short magazine_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.magazines,
|
|
magazine_index,
|
|
struct weapon_magazine_definition);
|
|
|
|
(void)magazine;
|
|
(void)magazine_definition;
|
|
weapon_magazine_idle(weapon_index, magazine_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_fully_charged(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.triggers,
|
|
trigger_index,
|
|
struct weapon_trigger_definition);
|
|
|
|
(void)trigger;
|
|
weapon_trigger_change_state(
|
|
weapon_index,
|
|
trigger_index,
|
|
_trigger_charged,
|
|
(short)(trigger_definition->charged_time * TICKS_PER_SECOND));
|
|
weapon_set_state(
|
|
weapon_index,
|
|
(short)(_weapon_state_primary_charged + trigger_index),
|
|
TRUE);
|
|
first_person_weapon_message_from_weapon(
|
|
weapon_index,
|
|
_first_person_weapon_message_charged);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_idle(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.triggers,
|
|
trigger_index,
|
|
struct weapon_trigger_definition);
|
|
|
|
(void)trigger;
|
|
(void)trigger_definition;
|
|
weapon_trigger_change_state(weapon_index, trigger_index, _trigger_idle, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_locked(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
weapon_trigger_change_state(weapon_index, trigger_index, _trigger_locked, NONE);
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_recover(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.triggers,
|
|
trigger_index,
|
|
struct weapon_trigger_definition);
|
|
|
|
(void)trigger_definition;
|
|
if (!trigger)
|
|
return;
|
|
trigger->idle_ticks = 0;
|
|
weapon_trigger_idle(weapon_index, trigger_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_finish_tracking(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.triggers,
|
|
trigger_index,
|
|
struct weapon_trigger_definition);
|
|
|
|
(void)trigger;
|
|
(void)trigger_definition;
|
|
weapon->weapon.tracked_object_index = NONE;
|
|
weapon_trigger_recover(weapon_index, trigger_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_magazine_start_reload(
|
|
long weapon_index,
|
|
short magazine_index,
|
|
boolean unknown)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
|
|
if (!magazine)
|
|
return;
|
|
|
|
switch (magazine->state)
|
|
{
|
|
case _magazine_idle:
|
|
case _magazine_unchambered:
|
|
if (weapon_magazine_state_change_ok(weapon_index))
|
|
{
|
|
if (magazine->rounds_total>0 && magazine->rounds_loaded<magazine_definition->rounds_loaded_maximum)
|
|
{
|
|
short reload_type = NONE;
|
|
|
|
weapon_set_state(weapon_index, (short)(_weapon_state_primary_reload+magazine_index), FALSE);
|
|
weapon_effect_new(weapon_index, magazine_definition->reloading_effect.index, 0.0f, 0.0f);
|
|
first_person_weapon_message_from_weapon(weapon_index, (short)(9+(magazine->rounds_loaded!=0)));
|
|
|
|
if (weapon_definition->weapon.weapon_type==1)
|
|
{
|
|
if (unknown)
|
|
reload_type = magazine_definition->rounds_loaded_maximum-magazine->rounds_loaded==1 ? 2 : 0;
|
|
else
|
|
reload_type = magazine_definition->rounds_loaded_maximum-magazine->rounds_loaded==1 ? 1 : NONE;
|
|
}
|
|
|
|
magazine->state = _magazine_reloading;
|
|
magazine->original_time = magazine->state_timer = weapon_get_first_person_animation_time(weapon_index, 0, _first_person_weapon_animation_reload_while_empty, reload_type);
|
|
}
|
|
|
|
weapon->weapon.flags &= ~FLAG(3);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_state_next(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
if (weapon->weapon.state<_weapon_state_primary_charged ||
|
|
(weapon->weapon.state>_weapon_state_secondary_charged && weapon->weapon.state!=_weapon_state_put_away))
|
|
weapon_set_state(weapon_index, _weapon_state_idle, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
static struct weapon_trigger *weapon_trigger_get(
|
|
struct weapon_datum *weapon,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_definition const *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\items\\weapons.c", 1639, trigger_index>=0 && trigger_index<weapon_definition->weapon.triggers.count);
|
|
|
|
/* port: no trigger past the weapon's two. The callers skip it. */
|
|
if (trigger_index<0 || trigger_index>=(short)NUMBEROF(weapon->weapon.triggers))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &weapon->weapon.triggers[trigger_index];
|
|
}
|
|
|
|
static struct weapon_magazine *weapon_magazine_get(
|
|
struct weapon_datum *weapon,
|
|
short magazine_index)
|
|
{
|
|
struct weapon_definition const *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\items\\weapons.c", 1650, magazine_index>=0 && magazine_index<weapon_definition->weapon.magazines.count);
|
|
|
|
/* port: no magazine past the weapon's two (a trigger's magazine is the
|
|
map's). The callers skip it. */
|
|
if (magazine_index<0 || magazine_index>=(short)NUMBEROF(weapon->weapon.magazines))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &weapon->weapon.magazines[magazine_index];
|
|
}
|
|
|
|
/* port: the magazines and triggers to go through. A tag's count past the
|
|
weapon's two would write past the weapon (weapons_initialize_for_new_map
|
|
cuts it already; this keeps every loop in the weapon anyway). */
|
|
static short weapon_magazine_count(
|
|
struct weapon_definition const *weapon_definition)
|
|
{
|
|
long count = weapon_definition->weapon.magazines.count;
|
|
|
|
return (short)(count<0 ? 0 : MIN(count, MAXIMUM_NUMBER_OF_MAGAZINES_PER_WEAPON));
|
|
}
|
|
|
|
static short weapon_trigger_count(
|
|
struct weapon_definition const *weapon_definition)
|
|
{
|
|
long count = weapon_definition->weapon.triggers.count;
|
|
|
|
return (short)(count<0 ? 0 : MIN(count, MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON));
|
|
}
|
|
|
|
/* port: whether a trigger's magazine is none or one the weapon has. The
|
|
index is the map's, and the weapon takes rounds out of it. */
|
|
static boolean weapon_trigger_magazine_valid(
|
|
struct weapon_definition const *weapon_definition,
|
|
struct weapon_trigger_definition const *trigger_definition)
|
|
{
|
|
return trigger_definition->magazine_index==NONE ||
|
|
(trigger_definition->magazine_index>=0 && trigger_definition->magazine_index<weapon_magazine_count(weapon_definition));
|
|
}
|
|
|
|
static real weapon_trigger_get_charged_fraction(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
if (!trigger)
|
|
return 0.0f;
|
|
|
|
switch (trigger->state)
|
|
{
|
|
case _trigger_charging:
|
|
return 1.0f-(trigger->state_timer*(1.0f/TICKS_PER_SECOND))/trigger_definition->charging_time;
|
|
|
|
case _trigger_charged:
|
|
return 1.0f;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
static boolean weapon_busy(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
return
|
|
weapon->weapon.triggers[0].state != _trigger_idle ||
|
|
weapon->weapon.triggers[1].state != _trigger_idle ||
|
|
weapon->weapon.magazines[0].state != _magazine_idle ||
|
|
weapon->weapon.magazines[1].state != _magazine_idle ||
|
|
weapon->weapon.state != _weapon_state_idle;
|
|
}
|
|
|
|
static boolean weapon_magazine_state_change_ok(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
return
|
|
weapon->weapon.triggers[0].state==_trigger_idle &&
|
|
weapon->weapon.triggers[1].state==_trigger_idle &&
|
|
weapon->weapon.state == _weapon_state_idle;
|
|
}
|
|
|
|
static long weapon_get_effect_object_index(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
long result = weapon_index;
|
|
|
|
if (TEST_FLAG(weapon->object.flags, _object_invisible_bit) && weapon->object.parent_object_index!=NONE)
|
|
{
|
|
result = weapon->object.parent_object_index;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static long weapon_get_owner_object_index(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
long result = NONE;
|
|
|
|
if (weapon->object.parent_object_index!=NONE && unit_try_and_get(weapon->object.parent_object_index))
|
|
{
|
|
result = weapon->object.parent_object_index;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static long weapon_get_projectile_owner_object_index(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
long parent_object_index = weapon->object.parent_object_index;
|
|
struct unit_datum *unit = NULL;
|
|
long result = NONE;
|
|
|
|
if (parent_object_index != NONE)
|
|
{
|
|
unit = unit_try_and_get(parent_object_index);
|
|
if (unit)
|
|
{
|
|
result = weapon->object.parent_object_index;
|
|
if (unit->unit.gunner_object_index != NONE)
|
|
result = unit->unit.gunner_object_index;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean weapon_trigger_can_fire_again(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.triggers,
|
|
trigger_index,
|
|
struct weapon_trigger_definition);
|
|
boolean result = FALSE;
|
|
real fraction;
|
|
real rate_of_fire;
|
|
real required_ticks;
|
|
char ticks_since_fire;
|
|
|
|
if (!trigger)
|
|
return FALSE;
|
|
|
|
fraction = TEST_FLAG(trigger_definition->flags, _weapon_trigger_analog_rate_of_fire_bit)
|
|
? weapon->weapon.primary_trigger
|
|
: trigger->rate_of_fire;
|
|
rate_of_fire = (trigger_definition->final_rate_of_fire - trigger_definition->initial_rate_of_fire) *
|
|
fraction + trigger_definition->initial_rate_of_fire;
|
|
required_ticks = rate_of_fire > 0.0001f ? TICKS_PER_SECOND / rate_of_fire : 0.0f;
|
|
|
|
if (weapon_definition->weapon.age_rate_of_fire_penalty > 0.0f)
|
|
{
|
|
required_ticks = (weapon->weapon.age * weapon_definition->weapon.age_rate_of_fire_penalty + 1.0f) *
|
|
required_ticks;
|
|
}
|
|
|
|
ticks_since_fire = trigger->idle_ticks;
|
|
if ((real)ticks_since_fire + 1.0f >= required_ticks)
|
|
result = TRUE;
|
|
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_latched_bit) &&
|
|
TEST_FLAG(weapon->item.flags, _item_belongs_to_player_bit) &&
|
|
!TEST_FLAG(trigger->flags, _weapon_trigger_released_since_last_shot_bit))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void weapon_magazine_idle(
|
|
long weapon_index,
|
|
short magazine_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.magazines,
|
|
magazine_index,
|
|
struct weapon_magazine_definition);
|
|
|
|
(void)magazine_definition;
|
|
if (!magazine)
|
|
return;
|
|
magazine->state = _magazine_idle;
|
|
magazine->state_timer = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
static long weapon_effect_looping_new(
|
|
long weapon_index,
|
|
long effect_index)
|
|
{
|
|
long result = NONE;
|
|
|
|
if (effect_index != NONE)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
long effect_object_index = weapon_index;
|
|
|
|
if (TEST_FLAG(weapon->object.flags, _object_invisible_bit) &&
|
|
weapon->object.parent_object_index != NONE)
|
|
{
|
|
effect_object_index = weapon->object.parent_object_index;
|
|
}
|
|
|
|
weapon_get_owner_object_index(weapon_index);
|
|
if (effect_object_index != NONE)
|
|
result = effect_new_looping(effect_index, effect_object_index, NONE, NONE, NONE);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void weapon_detonate(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
weapon_effect_new(
|
|
weapon_index,
|
|
weapon_definition->weapon.detonation_effect.index,
|
|
0.0f,
|
|
0.0f);
|
|
object_delete(weapon_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_change_state(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
short new_state,
|
|
short new_state_timer)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\items\\weapons.c",
|
|
0xA11,
|
|
trigger_index>=0 && trigger_index<MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\items\\weapons.c",
|
|
0xA12,
|
|
new_state>=0 && new_state<NUMBER_OF_TRIGGER_STATES);
|
|
|
|
/* port: the asserts only log in release. No trigger past the two. */
|
|
if (trigger_index<0 || trigger_index>=MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON)
|
|
{
|
|
return;
|
|
}
|
|
|
|
weapon->weapon.triggers[trigger_index].state = (char)new_state;
|
|
weapon->weapon.triggers[trigger_index].state_timer = new_state_timer;
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_start_ejection_port(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
boolean chamber)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger *trigger = weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&weapon_definition->weapon.triggers,
|
|
trigger_index,
|
|
struct weapon_trigger_definition);
|
|
|
|
if (trigger && trigger_definition->ejection_port_recovery_time > 0.0f)
|
|
{
|
|
if ((TEST_FLAG(
|
|
trigger_definition->flags,
|
|
_weapon_trigger_ejection_port_during_chamber_animation_bit) && chamber) ||
|
|
(!TEST_FLAG(
|
|
trigger_definition->flags,
|
|
_weapon_trigger_ejection_port_during_chamber_animation_bit) && !chamber))
|
|
{
|
|
trigger->ejection_port_position = 1.0f;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_state_key_frame(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
|
|
(void)weapon_definition_get(weapon->definition_index);
|
|
switch (weapon->weapon.state)
|
|
{
|
|
case _weapon_state_primary_chamber:
|
|
weapon_trigger_start_ejection_port(weapon_index, 0, TRUE);
|
|
break;
|
|
|
|
case _weapon_state_secondary_chamber:
|
|
weapon_trigger_start_ejection_port(weapon_index, 1, TRUE);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean weapon_magazine_state_interruptable(
|
|
short old_state,
|
|
short new_state)
|
|
{
|
|
boolean interruptable = FALSE;
|
|
|
|
(void)new_state;
|
|
switch (old_state)
|
|
{
|
|
case _magazine_idle:
|
|
case _magazine_unchambered:
|
|
interruptable = TRUE;
|
|
break;
|
|
}
|
|
|
|
return interruptable;
|
|
}
|
|
|
|
static long weapon_effect_new(
|
|
long weapon_index,
|
|
long effect_index,
|
|
real effect_scale,
|
|
real effect_error)
|
|
{
|
|
long result = NONE;
|
|
|
|
if (effect_index!=NONE)
|
|
{
|
|
long effect_object_index = weapon_get_effect_object_index(weapon_index);
|
|
long object_index = weapon_get_owner_object_index(weapon_index);
|
|
long group_tag = tag_get_group_tag(effect_index);
|
|
|
|
if (group_tag!=EFFECT_DEFINITION_TAG)
|
|
{
|
|
match_vassert("c:\\halo\\SOURCE\\items\\weapons.c", 2514, group_tag==SOUND_DEFINITION_TAG, NULL);
|
|
|
|
if (group_tag==SOUND_DEFINITION_TAG)
|
|
{
|
|
object_impulse_sound_new(object_index, effect_index, NONE, global_origin3d, global_forward3d, effect_scale);
|
|
result = NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = effect_new_from_object(effect_index, object_index, effect_object_index, NONE, effect_scale, effect_error, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// TODO: finish
|
|
static void weapon_reset(
|
|
long weapon_index)
|
|
{
|
|
short magazine_index;
|
|
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
|
|
for (magazine_index = 0; magazine_index<weapon_trigger_count(weapon_definition); ++magazine_index)
|
|
{
|
|
struct weapon_trigger* trigger = weapon_trigger_get(weapon, magazine_index);
|
|
struct weapon_trigger_definition *trigger_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, magazine_index, struct weapon_trigger_definition);
|
|
|
|
if (!trigger)
|
|
continue;
|
|
trigger->state = _trigger_uninitialized;
|
|
trigger->state_timer = 0;
|
|
}
|
|
|
|
for (magazine_index = 0; magazine_index<weapon_magazine_count(weapon_definition); ++magazine_index)
|
|
{
|
|
struct weapon_magazine *magazine = weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_magazine_definition *magazine_definition = TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
|
|
if (!magazine)
|
|
continue;
|
|
if (magazine->state==_magazine_reloading)
|
|
{
|
|
if (2*magazine->state_timer<weapon_get_first_person_animation_time(weapon_index, 0, _first_person_weapon_animation_reload_while_empty, NONE))
|
|
{
|
|
weapon_magazine_finish_reload(weapon_index, magazine_index);
|
|
}
|
|
}
|
|
|
|
magazine->state = _magazine_idle;
|
|
magazine->state_timer = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean weapon_state_interruptable(
|
|
short old_state,
|
|
short new_state)
|
|
{
|
|
boolean interruptable = FALSE;
|
|
long state = old_state;
|
|
|
|
if (state!=_weapon_state_idle)
|
|
{
|
|
if (state>_weapon_state_idle && state<=_weapon_state_secondary_recoil)
|
|
interruptable = new_state >= old_state;
|
|
}
|
|
else
|
|
{
|
|
interruptable = TRUE;
|
|
}
|
|
|
|
return interruptable;
|
|
}
|
|
|
|
void weapon_preprocess_node_orientations(
|
|
long weapon_index,
|
|
struct real_orientation *node_orientations)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(weapon_definition->object.animation_graph.index);
|
|
|
|
if (animation_graph->weapon_animations.count)
|
|
TAG_BLOCK_GET_ELEMENT(&animation_graph->weapon_animations, 0, struct animation_graph_weapon_animations);
|
|
|
|
return;
|
|
}
|
|
|
|
static boolean weapon_set_state(
|
|
long weapon_index,
|
|
short new_state,
|
|
boolean immediate)
|
|
{
|
|
struct weapon_datum *weapon = weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition = weapon_definition_get(weapon->definition_index);
|
|
boolean busy = FALSE;
|
|
|
|
if (immediate || weapon_state_interruptable(weapon->weapon.state, new_state))
|
|
{
|
|
long owner_object_index;
|
|
|
|
if (weapon_definition->object.animation_graph.index!=NONE)
|
|
{
|
|
struct animation_graph *animation_graph = animation_graph_definition_get(weapon_definition->object.animation_graph.index);
|
|
|
|
if (animation_graph->weapon_animations.count)
|
|
{
|
|
struct animation_graph_weapon_animations *weapon_animation = TAG_BLOCK_GET_ELEMENT(&animation_graph->weapon_animations, 0, struct animation_graph_weapon_animations);
|
|
|
|
if (weapon_animation)
|
|
{
|
|
short animation_index;
|
|
|
|
switch (new_state)
|
|
{
|
|
case _weapon_state_idle: animation_index = 0; break;
|
|
case _weapon_state_primary_recoil: animation_index = 9; break;
|
|
case _weapon_state_secondary_recoil: animation_index = 10; break;
|
|
case _weapon_state_primary_chamber: animation_index = 5; break;
|
|
case _weapon_state_secondary_chamber: animation_index = 6; break;
|
|
case _weapon_state_primary_reload:
|
|
case _weapon_state_secondary_reload: animation_index = 3; break;
|
|
case _weapon_state_primary_charged:
|
|
case _weapon_state_secondary_charged: animation_index = 8; break;
|
|
case _weapon_state_ready: animation_index = 1; break;
|
|
case _weapon_state_put_away: animation_index = 2; break;
|
|
default: goto skip_animation;
|
|
}
|
|
|
|
if (animation_index<weapon_animation->animations.count)
|
|
animation_index = animation_graph_animation_index_get(&weapon_animation->animations)[animation_index].animation_index;
|
|
else
|
|
animation_index = NONE;
|
|
|
|
if (animation_index!=NONE || new_state==_weapon_state_idle)
|
|
{
|
|
long graph_index = weapon_definition->object.animation_graph.index;
|
|
|
|
weapon->object.animation.state.index = animation_choose_random_permutation_internal(TRUE, graph_index, animation_index);
|
|
weapon->object.animation.state.frame_index = 0;
|
|
weapon->weapon.state = (char)new_state;
|
|
}
|
|
|
|
skip_animation:;
|
|
}
|
|
}
|
|
}
|
|
|
|
owner_object_index = weapon_get_owner_object_index(weapon_index);
|
|
if (unit_try_and_get(owner_object_index))
|
|
{
|
|
unit_handle_weapon_state_change(owner_object_index, new_state);
|
|
}
|
|
|
|
busy = TRUE;
|
|
}
|
|
|
|
return busy;
|
|
}
|
|
|
|
static void projectile_distribute(
|
|
real_vector3d *forward,
|
|
real_vector3d *up,
|
|
short distribution_function,
|
|
real distribution_angle,
|
|
short projectile_index,
|
|
short projectile_count)
|
|
{
|
|
real offset;
|
|
real angle;
|
|
|
|
if (projectile_count&1)
|
|
{
|
|
if (projectile_index==0)
|
|
{
|
|
offset= 0.0f;
|
|
}
|
|
else
|
|
{
|
|
short step= projectile_index-1;
|
|
|
|
if (step&1)
|
|
{
|
|
step>>= 1;
|
|
}
|
|
else
|
|
{
|
|
step= -(step>>1);
|
|
}
|
|
offset= step;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
offset= (projectile_index>>1)-0.5f;
|
|
if (projectile_index&1)
|
|
{
|
|
offset= -offset;
|
|
}
|
|
}
|
|
|
|
angle= offset*distribution_angle;
|
|
switch (distribution_function)
|
|
{
|
|
case _trigger_distribution_horizontal_fan:
|
|
rotate_vector_about_axis(forward, up, sine(angle), cosine(angle));
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void trigger_create_projectiles(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
long owner_object_index= weapon_get_owner_object_index(weapon_index);
|
|
char const *trigger_marker_names[MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON];
|
|
struct object_marker markers[MAXIMUM_MARKERS_PER_OBJECT];
|
|
short marker_count;
|
|
short marker_index;
|
|
|
|
if (!trigger)
|
|
{
|
|
return;
|
|
}
|
|
|
|
trigger_marker_names[0]= "primary trigger";
|
|
trigger_marker_names[1]= "secondary trigger";
|
|
|
|
marker_count= object_get_marker_by_name(weapon_get_effect_object_index(weapon_index), trigger_marker_names[trigger_index], markers, MAXIMUM_MARKERS_PER_OBJECT);
|
|
if (!marker_count)
|
|
{
|
|
marker_count= 1;
|
|
}
|
|
if (!TEST_FLAG(trigger_definition->flags, _weapon_trigger_uses_weapon_origin_bit))
|
|
{
|
|
marker_count= 1;
|
|
}
|
|
|
|
for (marker_index= 0; marker_index<marker_count; marker_index++)
|
|
{
|
|
real_point3d origin= markers[marker_index].matrix.position;
|
|
real_vector3d forward= markers[marker_index].matrix.forward;
|
|
real velocity= 0.0f;
|
|
real error= 0.0f;
|
|
struct unit_datum *unit= unit_try_and_get(owner_object_index);
|
|
long target_object_index= NONE;
|
|
long projectile_definition_index;
|
|
short projectile_count;
|
|
#ifdef HALO_ANDROID
|
|
/* VR: fired from the muzzle of the weapon in the hand */
|
|
boolean vr_from_muzzle= FALSE;
|
|
#endif
|
|
|
|
if (!TEST_FLAG(trigger_definition->flags, _weapon_trigger_projectiles_cannot_be_aimed_bit) &&
|
|
unit &&
|
|
!TEST_FLAG(unit->object.damage_flags, _object_dead_bit))
|
|
{
|
|
struct unit_definition *unit_definition= unit_definition_get(unit->definition_index);
|
|
long player_index= unit->unit.player_index;
|
|
long actor_index= unit->unit.actor_index;
|
|
boolean adjust_origin;
|
|
boolean use_aiming_vector;
|
|
|
|
if (unit->unit.gunner_object_index!=NONE)
|
|
{
|
|
struct unit_datum *gunner= unit_get(unit->unit.gunner_object_index);
|
|
|
|
player_index= gunner->unit.player_index;
|
|
actor_index= gunner->unit.actor_index;
|
|
}
|
|
|
|
adjust_origin= TEST_FLAG(unit_definition->unit.flags, _unit_fires_from_camera_bit);
|
|
use_aiming_vector= TRUE;
|
|
if (actor_index!=NONE && actor_firing_blindly(actor_index))
|
|
{
|
|
use_aiming_vector= FALSE;
|
|
}
|
|
if (unit->unit.gunner_object_index!=NONE)
|
|
{
|
|
use_aiming_vector= FALSE;
|
|
}
|
|
|
|
unit_adjust_projectile_ray(owner_object_index, &origin, &forward, &velocity, adjust_origin, use_aiming_vector);
|
|
|
|
if (player_index!=NONE)
|
|
{
|
|
real_vector3d right;
|
|
real_vector3d up;
|
|
|
|
cross_product3d(global_up3d, &forward, &right);
|
|
if (normalize3d(&right)==0.0f)
|
|
{
|
|
right= *global_left3d;
|
|
}
|
|
cross_product3d(&forward, &right, &up);
|
|
normalize3d(&up);
|
|
|
|
point_from_line3d(
|
|
&origin,
|
|
&forward,
|
|
trigger_definition->first_person_weapon_offset.x,
|
|
&origin);
|
|
point_from_line3d(
|
|
&origin,
|
|
&right,
|
|
trigger_definition->first_person_weapon_offset.y,
|
|
&origin);
|
|
point_from_line3d(
|
|
&origin,
|
|
&up,
|
|
trigger_definition->first_person_weapon_offset.z,
|
|
&origin);
|
|
#ifdef HALO_ANDROID
|
|
if (unit->object.parent_object_index == NONE && unit->unit.gunner_object_index == NONE)
|
|
{
|
|
/* VR: from the muzzle of the weapon in the right hand; the
|
|
projectile is kept inside the level from the camera below,
|
|
so a hand through a wall does not fire through it */
|
|
real vr_origin[3];
|
|
short vr_local_player_index = player_get(player_index)->local_player_index;
|
|
|
|
/* (a VR player of another machine: from the hand
|
|
their machine says, network_vr.c) */
|
|
if (vr_local_player_index != NONE ? halo_vr_weapon_origin(vr_local_player_index, vr_origin) :
|
|
halo_vr_network_weapon_origin(player_index, owner_object_index, vr_origin))
|
|
{
|
|
origin.x = vr_origin[0];
|
|
origin.y = vr_origin[1];
|
|
origin.z = vr_origin[2];
|
|
vr_from_muzzle = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
target_object_index= player_aim_projectile(player_index, &origin, &forward);
|
|
}
|
|
else if (actor_index!=NONE)
|
|
{
|
|
target_object_index= actor_aim_projectile(actor_index, &origin, &forward, &error);
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_uses_weapon_origin_bit))
|
|
{
|
|
origin= markers[marker_index].matrix.position;
|
|
}
|
|
|
|
if (trigger_index==0 && weapon->weapon.alternate_shots_loaded>0)
|
|
{
|
|
struct weapon_trigger_definition *secondary_trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, 1, struct weapon_trigger_definition);
|
|
short alternate_shots_loaded= weapon->weapon.alternate_shots_loaded;
|
|
|
|
projectile_definition_index= secondary_trigger_definition->projectile.index;
|
|
if (weapon_definition->weapon.secondary_trigger_mode==_weapon_secondary_trigger_loads_multiple_primary_ammunition)
|
|
{
|
|
alternate_shots_loaded++;
|
|
}
|
|
projectile_count= trigger_definition->projectiles_per_shot*alternate_shots_loaded;
|
|
weapon->weapon.alternate_shots_loaded= 0;
|
|
}
|
|
else
|
|
{
|
|
projectile_definition_index= trigger_definition->projectile.index;
|
|
projectile_count= trigger_definition->projectiles_per_shot;
|
|
}
|
|
|
|
if (projectile_definition_index!=NONE)
|
|
{
|
|
long projectile_owner_object_index= weapon_get_projectile_owner_object_index(weapon_index);
|
|
real_vector3d first_projectile_forward;
|
|
short projectile_index;
|
|
|
|
for (projectile_index= 0; projectile_index<projectile_count; projectile_index++)
|
|
{
|
|
struct object_placement_data data;
|
|
boolean tracer= FALSE;
|
|
boolean inside_bsp;
|
|
long projectile_object_index;
|
|
|
|
object_placement_data_new(&data, trigger_definition->projectile.index, projectile_owner_object_index);
|
|
data.position= origin;
|
|
data.forward= forward;
|
|
|
|
if (trigger->rate_of_fire==0.0f || trigger->sequential_non_tracer_rounds++>=trigger_definition->rounds_between_tracers)
|
|
{
|
|
tracer= TRUE;
|
|
trigger->sequential_non_tracer_rounds= 0;
|
|
}
|
|
|
|
if (error==0.0f)
|
|
{
|
|
real fraction= TEST_FLAG(trigger_definition->flags, _weapon_trigger_analog_rate_of_fire_bit) ?
|
|
weapon->weapon.primary_trigger :
|
|
trigger->error;
|
|
|
|
error= (1.0f-fraction)*trigger_definition->projectile_error_angle_lower_bound + fraction*trigger_definition->projectile_error_angle_upper_bound;
|
|
}
|
|
|
|
if (!TEST_FLAG(trigger_definition->flags, _weapon_trigger_use_error_when_unzoomed_bit) ||
|
|
!TEST_FLAG(weapon->weapon.control_flags, _weapon_control_zoomed_bit))
|
|
{
|
|
random_vector_in_cone3d(&data.forward, trigger_definition->projectile_error_inner_cone_angle, error, &data.forward);
|
|
}
|
|
|
|
if (projectile_index==0)
|
|
{
|
|
first_projectile_forward= data.forward;
|
|
}
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_projectiles_have_identical_error_bit))
|
|
{
|
|
data.forward= first_projectile_forward;
|
|
}
|
|
|
|
normalize3d(perpendicular3d(&data.forward, &data.up));
|
|
projectile_distribute(&data.forward, &data.up, trigger_definition->projectile_distribution_function, trigger_definition->projectile_distribution_angle, projectile_index, projectile_count);
|
|
scale_vector3d(&data.forward, velocity, &data.translational_velocity);
|
|
|
|
inside_bsp= unit && unit->unit.player_index!=NONE;
|
|
if (inside_bsp)
|
|
{
|
|
SET_FLAG(data.flags, _new_object_never_automatically_delete_bit, TRUE);
|
|
}
|
|
|
|
projectile_object_index= object_new(&data);
|
|
if (projectile_object_index!=NONE)
|
|
{
|
|
if (inside_bsp)
|
|
{
|
|
real_point3d camera_position;
|
|
|
|
unit_get_camera_position(owner_object_index, &camera_position);
|
|
#ifdef HALO_ANDROID
|
|
if (vr_from_muzzle)
|
|
{
|
|
/* VR: from the eyes down to the hand the line runs through
|
|
the player's own body, which the game's test hits, moving the
|
|
shot up towards the eyes (it then flies parallel to the aim,
|
|
above it): only the level stops it, a hand through a wall */
|
|
struct collision_result collision;
|
|
struct object_datum *projectile_object= object_get(projectile_object_index);
|
|
|
|
if (collision_test_line(FLAG(_collision_test_structure_bit) | FLAG(_collision_test_front_facing_surfaces_bit),
|
|
&camera_position, &projectile_object->object.position, owner_object_index, &collision) &&
|
|
collision.location.cluster_index!=NONE)
|
|
{
|
|
object_translate(projectile_object_index, &collision.point, &collision.location);
|
|
object_compute_node_matrices(projectile_object_index);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
object_force_inside_bsp(projectile_object_index, &camera_position);
|
|
}
|
|
if (target_object_index!=NONE)
|
|
{
|
|
projectile_set_target_object_index(projectile_object_index, target_object_index);
|
|
}
|
|
if (!tracer)
|
|
{
|
|
projectile_kill_tracer(projectile_object_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_fire(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
long owner_object_index= weapon_get_owner_object_index(weapon_index);
|
|
long damage_effect_index= NONE;
|
|
long effect_index= NONE;
|
|
real effect_error= 0.0f;
|
|
real effect_scale= 0.0f;
|
|
boolean fired= FALSE;
|
|
boolean misfired= FALSE;
|
|
boolean loads_alternate_ammunition= FALSE;
|
|
|
|
if (!trigger)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (trigger_index==1 &&
|
|
(weapon_definition->weapon.secondary_trigger_mode==_weapon_secondary_trigger_loads_alternate_ammunition ||
|
|
weapon_definition->weapon.secondary_trigger_mode==_weapon_secondary_trigger_loads_multiple_primary_ammunition))
|
|
{
|
|
loads_alternate_ammunition= TRUE;
|
|
}
|
|
|
|
if (trigger_definition->magazine_index!=NONE)
|
|
{
|
|
/* port: a magazine the weapon doesn't have is never fired from. Its
|
|
index is the map's, and the rounds are taken out of it here. */
|
|
struct weapon_magazine *magazine= weapon_trigger_magazine_valid(weapon_definition, trigger_definition) ?
|
|
weapon_magazine_get(weapon, trigger_definition->magazine_index) : NULL;
|
|
struct weapon_magazine_definition *magazine_definition= magazine ?
|
|
TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, trigger_definition->magazine_index, struct weapon_magazine_definition) : NULL;
|
|
|
|
if (magazine &&
|
|
(!loads_alternate_ammunition || weapon->weapon.alternate_shots_loaded<weapon_definition->weapon.maximum_alternate_shots_loaded))
|
|
{
|
|
if ((magazine->rounds_loaded>=trigger_definition->rounds_per_shot || TEST_FLAG(trigger_definition->flags, _weapon_trigger_can_fire_with_partial_ammunition_bit)) &&
|
|
!(TEST_FLAG(weapon_definition->weapon.flags, _weapon_cannot_fire_at_maximum_age_bit) && weapon->weapon.age>=1.0f) &&
|
|
(magazine->rounds_loaded>=trigger_definition->minimum_rounds_loaded_per_shot || !TEST_FLAG(trigger->flags, _weapon_trigger_released_since_last_shot_bit)))
|
|
{
|
|
if (!cheat.bottomless_clip && (magazine->rounds_loaded-= trigger_definition->rounds_per_shot)<=0)
|
|
{
|
|
magazine->rounds_loaded= 0;
|
|
}
|
|
else if (TEST_FLAG(magazine_definition->flags, _weapon_magazine_must_be_chambered_every_shot_bit))
|
|
{
|
|
magazine->state= _magazine_unchambered;
|
|
magazine->state_timer= 0;
|
|
}
|
|
|
|
fired= TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fired= TRUE;
|
|
}
|
|
|
|
if (cheat.bottomless_clip)
|
|
{
|
|
fired= TRUE;
|
|
}
|
|
|
|
if (trigger_definition->firing_effects.count>0)
|
|
{
|
|
struct trigger_firing_effect *firing_effect;
|
|
short effect_type;
|
|
|
|
if (trigger->firing_effect_shots_remaining<=0)
|
|
{
|
|
short starting_firing_effect_index= trigger->firing_effect_index;
|
|
short firing_effect_index;
|
|
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_random_firing_effects_bit))
|
|
{
|
|
firing_effect_index= random()%trigger_definition->firing_effects.count;
|
|
}
|
|
else
|
|
{
|
|
firing_effect_index= starting_firing_effect_index;
|
|
}
|
|
|
|
do
|
|
{
|
|
struct trigger_firing_effect *next_firing_effect;
|
|
|
|
if (trigger->firing_effects_used_flags==FLAG(trigger_definition->firing_effects.count)-1)
|
|
{
|
|
trigger->firing_effects_used_flags= 0;
|
|
}
|
|
|
|
do
|
|
{
|
|
firing_effect_index++;
|
|
if (firing_effect_index>=trigger_definition->firing_effects.count)
|
|
{
|
|
firing_effect_index= 0;
|
|
}
|
|
}
|
|
while (TEST_FLAG(trigger->firing_effects_used_flags, firing_effect_index));
|
|
|
|
next_firing_effect= TAG_BLOCK_GET_ELEMENT(&trigger_definition->firing_effects, firing_effect_index, struct trigger_firing_effect);
|
|
trigger->firing_effect_index= firing_effect_index;
|
|
SET_FLAG(trigger->firing_effects_used_flags, firing_effect_index, TRUE);
|
|
trigger->firing_effect_shots_remaining= random_range(next_firing_effect->shots_lower_bound, next_firing_effect->shots_upper_bound);
|
|
}
|
|
while (trigger->firing_effect_shots_remaining<=0 && firing_effect_index!=starting_firing_effect_index);
|
|
}
|
|
|
|
trigger->firing_effect_shots_remaining--;
|
|
firing_effect= TAG_BLOCK_GET_ELEMENT(&trigger_definition->firing_effects, trigger->firing_effect_index, struct trigger_firing_effect);
|
|
|
|
if (weapon_definition->weapon.age_misfire_start>0.0f &&
|
|
weapon_definition->weapon.age_misfire_start<1.0f &&
|
|
weapon->weapon.age>weapon_definition->weapon.age_misfire_start)
|
|
{
|
|
real misfire_chance= ((weapon->weapon.age-weapon_definition->weapon.age_misfire_start)*weapon_definition->weapon.age_misfire_chance)/(1.0f-weapon_definition->weapon.age_misfire_start);
|
|
|
|
if (trigger->state==_trigger_spewing)
|
|
{
|
|
misfire_chance*= 2.0f;
|
|
}
|
|
if (real_random()<misfire_chance)
|
|
{
|
|
misfired= TRUE;
|
|
}
|
|
}
|
|
|
|
if (!fired)
|
|
{
|
|
effect_type= _trigger_empty_effect;
|
|
effect_scale= 1.0f;
|
|
effect_error= 0.0f;
|
|
}
|
|
else if (misfired)
|
|
{
|
|
effect_type= _trigger_overheated_effect;
|
|
effect_scale= trigger->rate_of_fire;
|
|
effect_error= 0.0f;
|
|
}
|
|
else
|
|
{
|
|
effect_type= _trigger_firing_effect;
|
|
effect_scale= trigger->rate_of_fire;
|
|
if (weapon_definition->weapon.heat_overheated_threshold==0.0f)
|
|
{
|
|
effect_error= 0.0f;
|
|
}
|
|
else
|
|
{
|
|
effect_error= weapon->weapon.heat/weapon_definition->weapon.heat_overheated_threshold;
|
|
}
|
|
}
|
|
|
|
effect_index= firing_effect->effects[effect_type].index;
|
|
damage_effect_index= firing_effect->damage_effects[effect_type].index;
|
|
}
|
|
|
|
if (fired)
|
|
{
|
|
if (TEST_FLAG(weapon->item.flags, _item_belongs_to_player_bit) && game_engine_running())
|
|
{
|
|
long player_index= player_index_from_unit_index(owner_object_index);
|
|
|
|
if (player_index!=NONE)
|
|
{
|
|
game_engine_weapon_fired(player_index);
|
|
}
|
|
}
|
|
|
|
weapon->weapon.game_time_last_fired= game_time_get();
|
|
|
|
first_person_weapon_message_from_weapon(weapon_index, misfired ?
|
|
(trigger_index ? _first_person_weapon_message_secondary_misfire : _first_person_weapon_message_primary_misfire) :
|
|
(trigger_index ? _first_person_weapon_message_secondary_fire : _first_person_weapon_message_primary_fire));
|
|
weapon_trigger_start_ejection_port(weapon_index, trigger_index, FALSE);
|
|
|
|
if (trigger_definition->illumination_recovery_time>0.0f)
|
|
{
|
|
trigger->illumination= 1.0f;
|
|
}
|
|
|
|
if (!cheat.bottomless_clip)
|
|
{
|
|
weapon->weapon.heat+= trigger_definition->heat_generated_per_round;
|
|
}
|
|
|
|
if (!TEST_FLAG(weapon->item.flags, _item_belongs_to_player_bit) && weapon->weapon.heat>weapon_definition->weapon.heat_overheated_threshold)
|
|
{
|
|
weapon->weapon.heat= weapon_definition->weapon.heat_overheated_threshold;
|
|
}
|
|
else if (weapon->weapon.heat>1.0f)
|
|
{
|
|
weapon->weapon.heat= 1.0f;
|
|
}
|
|
|
|
if (TEST_FLAG(weapon->item.flags, _item_belongs_to_player_bit) && !cheat.infinite_ammo)
|
|
{
|
|
weapon->weapon.age+= trigger_definition->age_generated_per_round;
|
|
if (weapon->weapon.age>1.0f)
|
|
{
|
|
weapon->weapon.age= 1.0f;
|
|
}
|
|
}
|
|
|
|
weapon_set_state(weapon_index, trigger_index ? _weapon_state_secondary_recoil : _weapon_state_primary_recoil, FALSE);
|
|
|
|
if (!misfired)
|
|
{
|
|
if (loads_alternate_ammunition)
|
|
{
|
|
weapon->weapon.alternate_shots_loaded++;
|
|
}
|
|
else
|
|
{
|
|
trigger_create_projectiles(weapon_index, trigger_index);
|
|
ai_handle_unit_effect(owner_object_index, _ai_unit_effect_shooting, trigger_definition->firing_noise);
|
|
}
|
|
}
|
|
|
|
if (owner_object_index!=NONE && damage_effect_index!=NONE)
|
|
{
|
|
struct unit_datum *unit= unit_get(owner_object_index);
|
|
struct damage_data damage;
|
|
|
|
damage_data_new(&damage, damage_effect_index);
|
|
SET_FLAG(damage.flags, _damage_from_weapon_bit, TRUE);
|
|
negate_vector3d(&unit->unit.aiming_vector, &damage.direction);
|
|
damage.epicenter= unit->object.bounding_sphere_center;
|
|
damage.origin= damage.epicenter;
|
|
object_cause_damage(&damage, owner_object_index, NONE, NONE, NONE, NULL);
|
|
}
|
|
|
|
if (weapon_definition->weapon.weapon_type==_weapon_type_plasma_pistol && trigger_index==1)
|
|
{
|
|
SET_FLAG(weapon->weapon.flags, _weapon_overheat_recoil_bit, TRUE);
|
|
}
|
|
}
|
|
|
|
if (weapon->weapon.heat>weapon_definition->weapon.heat_detonation_threshold && real_random()<weapon_definition->weapon.overheated_explosion_fraction)
|
|
{
|
|
weapon_detonate(weapon_index);
|
|
}
|
|
|
|
if (!fired)
|
|
{
|
|
weapon_trigger_change_state(weapon_index, trigger_index, _trigger_locked, NONE);
|
|
}
|
|
else if (trigger->state!=_trigger_spewing || misfired)
|
|
{
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_tracks_projectile_bit))
|
|
{
|
|
weapon_trigger_change_state(weapon_index, trigger_index, _trigger_tracking, NONE);
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_recover(weapon_index, trigger_index);
|
|
}
|
|
}
|
|
|
|
SET_FLAG(trigger->flags, _weapon_trigger_released_since_last_shot_bit, FALSE);
|
|
weapon_effect_new(weapon_index, effect_index, effect_scale, effect_error);
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_begin_firing(
|
|
long weapon_index,
|
|
short trigger_index,
|
|
boolean force)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
boolean can_fire= TRUE;
|
|
|
|
if (!trigger)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (trigger_definition->magazine_index!=NONE)
|
|
{
|
|
/* port: and not from a magazine the weapon doesn't have */
|
|
struct weapon_magazine *magazine= weapon_trigger_magazine_valid(weapon_definition, trigger_definition) ?
|
|
weapon_magazine_get(weapon, trigger_definition->magazine_index) : NULL;
|
|
struct weapon_magazine_definition *magazine_definition= magazine ?
|
|
TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, trigger_definition->magazine_index, struct weapon_magazine_definition) : NULL;
|
|
|
|
(void)magazine_definition;
|
|
if (!magazine || magazine->state!=_magazine_idle)
|
|
{
|
|
can_fire= FALSE;
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit))
|
|
{
|
|
can_fire= FALSE;
|
|
}
|
|
|
|
if (scenario_location_underwater(&weapon->object.location, &weapon->object.position, NULL))
|
|
{
|
|
can_fire= FALSE;
|
|
}
|
|
|
|
if (can_fire)
|
|
{
|
|
if (!force && trigger_definition->charging_time>0.0f)
|
|
{
|
|
if (TEST_FLAG(weapon_definition->weapon.flags, _weapon_cannot_fire_at_maximum_age_bit) && weapon->weapon.age>=1.0f)
|
|
{
|
|
weapon_trigger_fire(weapon_index, trigger_index);
|
|
}
|
|
else
|
|
{
|
|
if (weapon_definition->weapon.triggers.count>1)
|
|
{
|
|
trigger->charging_effect_index= weapon_effect_new(weapon_index, trigger_definition->charging_effect.index, 0.0f, 0.0f);
|
|
}
|
|
else if (trigger->rate_of_fire>0.0f)
|
|
{
|
|
SET_FLAG(trigger->flags, _weapon_trigger_fired_before_charging_bit, TRUE);
|
|
weapon_trigger_fire(weapon_index, trigger_index);
|
|
}
|
|
else
|
|
{
|
|
SET_FLAG(trigger->flags, _weapon_trigger_fired_before_charging_bit, FALSE);
|
|
}
|
|
|
|
weapon_trigger_change_state(weapon_index, trigger_index, _trigger_charging, (short)(trigger_definition->charging_time*TICKS_PER_SECOND));
|
|
}
|
|
}
|
|
else if (!force && trigger_definition->overloading_time>0.0f)
|
|
{
|
|
weapon_trigger_change_state(weapon_index, trigger_index, _trigger_overloading, (short)(trigger_definition->overloading_time*TICKS_PER_SECOND));
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_fire(weapon_index, trigger_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_overload(
|
|
long weapon_index,
|
|
long trigger_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
if (trigger_index+1<weapon_definition->weapon.triggers.count)
|
|
{
|
|
weapon_trigger_fire(weapon_index, (short)(trigger_index+1));
|
|
}
|
|
|
|
weapon_trigger_change_state(weapon_index, (short)trigger_index, _trigger_overloading, (short)(trigger_definition->overloading_time*TICKS_PER_SECOND));
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_release_charge(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
if (trigger_definition->spew_time>0.0f)
|
|
{
|
|
weapon_trigger_change_state(weapon_index, trigger_index, _trigger_spewing, (short)(trigger_definition->spew_time*TICKS_PER_SECOND));
|
|
}
|
|
else
|
|
{
|
|
if (weapon_definition->weapon.triggers.count>1)
|
|
{
|
|
weapon_trigger_fire(weapon_index, 1);
|
|
}
|
|
weapon_trigger_recover(weapon_index, trigger_index);
|
|
}
|
|
|
|
if (trigger)
|
|
{
|
|
trigger->rate_of_fire= 0.0f;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void weapon_trigger_overcharged(
|
|
long weapon_index,
|
|
short trigger_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
switch (trigger_definition->overcharged_action)
|
|
{
|
|
case _trigger_overcharged_explodes:
|
|
weapon_detonate(weapon_index);
|
|
break;
|
|
|
|
case _trigger_overcharged_fire:
|
|
weapon_trigger_release_charge(weapon_index, trigger_index);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean weapon_update(
|
|
long weapon_index)
|
|
{
|
|
struct weapon_datum *weapon= weapon_get(weapon_index);
|
|
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
|
|
boolean triggers_down[MAXIMUM_NUMBER_OF_TRIGGERS_PER_WEAPON];
|
|
short magazine_index;
|
|
short trigger_index;
|
|
|
|
profile_enter(data_00307140.update_profile);
|
|
|
|
if (weapon->weapon.tracked_object_index!=NONE && !object_try_and_get_and_verify_type(weapon->weapon.tracked_object_index, _object_mask_all))
|
|
{
|
|
weapon->weapon.tracked_object_index= NONE;
|
|
}
|
|
|
|
if (weapon_definition->object.animation_graph.index!=NONE && weapon->object.animation.state.index!=NONE)
|
|
{
|
|
switch (animation_update_internal(TRUE, weapon_definition->object.animation_graph.index, &weapon->object.animation.state, NULL))
|
|
{
|
|
case _animation_key_frame:
|
|
weapon_state_key_frame(weapon_index);
|
|
break;
|
|
|
|
case _animation_will_restart_on_next_frame:
|
|
weapon_state_next(weapon_index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(weapon_definition->weapon.flags, _weapon_detonates_when_dropped_bit) && weapon->object.parent_object_index==NONE)
|
|
{
|
|
item_detonate(weapon_index);
|
|
}
|
|
|
|
if (weapon->weapon.integrated_light_power>0.0f)
|
|
{
|
|
struct unit_datum *unit;
|
|
|
|
if (!(weapon->object.parent_object_index!=NONE &&
|
|
(unit= unit_try_and_get(weapon->object.parent_object_index))!=NULL &&
|
|
unit->definition_index!=NONE &&
|
|
TEST_FLAG(unit_definition_get(unit->definition_index)->unit.flags, _unit_integrated_light_controls_weapon_directly_bit)))
|
|
{
|
|
weapon->weapon.integrated_light_power-= (1.0f/24.0f);
|
|
if (weapon->weapon.integrated_light_power<0.0f)
|
|
{
|
|
weapon->weapon.integrated_light_power= 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (weapon->weapon.heat>0.0f)
|
|
{
|
|
if (weapon->weapon.heat>=weapon_definition->weapon.heat_overheated_threshold && !TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit))
|
|
{
|
|
SET_FLAG(weapon->weapon.flags, _weapon_overheated_bit, TRUE);
|
|
if (weapon_definition->weapon.weapon_type==_weapon_type_plasma_pistol && TEST_FLAG(weapon->weapon.flags, _weapon_overheat_recoil_bit))
|
|
{
|
|
SET_FLAG(weapon->weapon.flags, _weapon_overheat_recoil_bit, FALSE);
|
|
first_person_weapon_message_from_weapon(weapon_index, _first_person_weapon_message_overheating_super_recoil);
|
|
}
|
|
else
|
|
{
|
|
first_person_weapon_message_from_weapon(weapon_index, _first_person_weapon_message_overheating);
|
|
}
|
|
weapon->weapon.overheated_effect_index= weapon_effect_looping_new(weapon_index, weapon_definition->weapon.overheated_effect.index);
|
|
}
|
|
|
|
if (weapon->weapon.overcharged==0.0f)
|
|
{
|
|
real heat_loss= weapon_definition->weapon.heat_loss_per_second*(1.0f/TICKS_PER_SECOND);
|
|
|
|
if (weapon_definition->weapon.age_heat_recovery_penalty>0.0f)
|
|
{
|
|
heat_loss*= 1.0f-weapon->weapon.age*weapon_definition->weapon.age_heat_recovery_penalty;
|
|
}
|
|
|
|
weapon->weapon.heat-= heat_loss;
|
|
if (weapon->weapon.heat<0.0f)
|
|
{
|
|
weapon->weapon.heat= 0.0f;
|
|
}
|
|
|
|
if (TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit) &&
|
|
!TEST_FLAG(weapon->weapon.flags, _weapon_overheated_exit_bit) &&
|
|
(weapon->weapon.heat-weapon_definition->weapon.heat_recovery_threshold)/heat_loss<=1.0f)
|
|
{
|
|
SET_FLAG(weapon->weapon.flags, _weapon_overheated_exit_bit, TRUE);
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit) && weapon->weapon.heat<weapon_definition->weapon.heat_recovery_threshold)
|
|
{
|
|
weapon->weapon.flags&= ~(FLAG(_weapon_overheated_bit)|FLAG(_weapon_overheated_exit_bit));
|
|
if (weapon->weapon.overheated_effect_index!=NONE)
|
|
{
|
|
effect_stop(weapon->weapon.overheated_effect_index, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
weapon->weapon.overcharged= 0.0f;
|
|
if (weapon->weapon.state_timer>0)
|
|
{
|
|
weapon->weapon.state_timer--;
|
|
}
|
|
|
|
if (!TEST_FLAG(weapon->weapon.control_flags, _weapon_control_user_busy_bit) && weapon->weapon.state_timer<=0)
|
|
{
|
|
triggers_down[0]= TEST_FLAG(weapon->weapon.control_flags, _weapon_control_primary_trigger_bit);
|
|
triggers_down[1]= TEST_FLAG(weapon_definition->weapon.flags, _weapon_secondary_trigger_overrides_grenades_bit) && TEST_FLAG(weapon->weapon.control_flags, _weapon_control_secondary_trigger_bit);
|
|
}
|
|
else
|
|
{
|
|
triggers_down[0]= FALSE;
|
|
triggers_down[1]= FALSE;
|
|
}
|
|
|
|
switch (weapon_definition->weapon.secondary_trigger_mode)
|
|
{
|
|
case _weapon_secondary_trigger_slaved_to_primary:
|
|
if (triggers_down[1] && weapon_definition->weapon.triggers.count>0 && weapon->weapon.triggers[0].rate_of_fire!=1.0f)
|
|
{
|
|
triggers_down[1]= FALSE;
|
|
}
|
|
break;
|
|
|
|
case _weapon_secondary_trigger_inhibits_primary:
|
|
if (triggers_down[1])
|
|
{
|
|
triggers_down[0]= FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (TEST_FLAG(weapon->weapon.control_flags, _weapon_control_reload_bit) && weapon_definition->weapon.magazines.count>0)
|
|
{
|
|
SET_FLAG(weapon->weapon.flags, _weapon_needs_to_reload_bit, TRUE);
|
|
}
|
|
if (TEST_FLAG(weapon->weapon.flags, _weapon_needs_to_reload_bit))
|
|
{
|
|
weapon_magazine_start_reload(weapon_index, 0, TRUE);
|
|
}
|
|
|
|
for (magazine_index= 0; magazine_index<weapon_magazine_count(weapon_definition); magazine_index++)
|
|
{
|
|
struct weapon_magazine *magazine= weapon_magazine_get(weapon, magazine_index);
|
|
struct weapon_magazine_definition *magazine_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.magazines, magazine_index, struct weapon_magazine_definition);
|
|
|
|
if (!magazine)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (magazine_definition->rounds_recharged_per_second>0 && magazine->rounds_loaded<magazine_definition->rounds_loaded_maximum)
|
|
{
|
|
short rounds_recharged= magazine_definition->rounds_recharged_per_second/TICKS_PER_SECOND;
|
|
short fractional_rounds_recharged= magazine_definition->rounds_recharged_per_second%TICKS_PER_SECOND;
|
|
|
|
magazine->rounds_loaded+= rounds_recharged;
|
|
magazine->rounds_fractional_recharged+= fractional_rounds_recharged;
|
|
if (magazine->rounds_fractional_recharged>=TICKS_PER_SECOND)
|
|
{
|
|
magazine->rounds_loaded++;
|
|
magazine->rounds_fractional_recharged-= TICKS_PER_SECOND;
|
|
}
|
|
if (magazine->rounds_loaded>magazine_definition->rounds_loaded_maximum)
|
|
{
|
|
magazine->rounds_loaded= magazine_definition->rounds_loaded_maximum;
|
|
}
|
|
}
|
|
|
|
if (magazine->state_timer)
|
|
{
|
|
magazine->state_timer--;
|
|
}
|
|
|
|
switch (magazine->state)
|
|
{
|
|
case _magazine_reloading:
|
|
if (magazine->state_timer-1<=0)
|
|
{
|
|
weapon_magazine_finish_reload(weapon_index, magazine_index);
|
|
}
|
|
break;
|
|
|
|
case _magazine_unchambered:
|
|
weapon_magazine_start_chamber(weapon_index, magazine_index);
|
|
break;
|
|
|
|
case _magazine_chambering:
|
|
if (!magazine->state_timer)
|
|
{
|
|
weapon_magazine_finish_chamber(weapon_index, magazine_index);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (trigger_index= 0; trigger_index<weapon_trigger_count(weapon_definition); trigger_index++)
|
|
{
|
|
struct weapon_trigger *trigger= weapon_trigger_get(weapon, trigger_index);
|
|
struct weapon_trigger_definition *trigger_definition= TAG_BLOCK_GET_ELEMENT(&weapon_definition->weapon.triggers, trigger_index, struct weapon_trigger_definition);
|
|
|
|
if (!trigger)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_analog_rate_of_fire_bit) && TEST_FLAG(weapon->item.flags, _item_belongs_to_player_bit))
|
|
{
|
|
triggers_down[trigger_index]= weapon->weapon.primary_trigger>0.05f;
|
|
}
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_sticks_when_dropped_bit) && weapon->object.parent_object_index==NONE)
|
|
{
|
|
triggers_down[trigger_index]= TRUE;
|
|
}
|
|
|
|
if (trigger->state_timer)
|
|
{
|
|
trigger->state_timer--;
|
|
}
|
|
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_toggles_bit))
|
|
{
|
|
if (!TEST_FLAG(trigger->flags, _weapon_trigger_was_down_bit) && triggers_down[trigger_index])
|
|
{
|
|
trigger->flags^= FLAG(_weapon_trigger_toggled_bit);
|
|
}
|
|
SET_FLAG(trigger->flags, _weapon_trigger_was_down_bit, triggers_down[trigger_index]);
|
|
triggers_down[trigger_index]= TEST_FLAG(trigger->flags, _weapon_trigger_toggled_bit);
|
|
}
|
|
|
|
if (!triggers_down[trigger_index])
|
|
{
|
|
SET_FLAG(trigger->flags, _weapon_trigger_released_since_last_shot_bit, TRUE);
|
|
}
|
|
|
|
if (trigger->ejection_port_position>0.0f)
|
|
{
|
|
trigger->ejection_port_position-= trigger_definition->runtime_ejection_port_recovery_time;
|
|
if (trigger->ejection_port_position<=0.0f)
|
|
{
|
|
trigger->ejection_port_position= 0.0f;
|
|
}
|
|
}
|
|
|
|
if (trigger->illumination>0.0f)
|
|
{
|
|
trigger->illumination-= trigger_definition->runtime_illumination_recovery_time;
|
|
if (trigger->illumination<=0.0f)
|
|
{
|
|
trigger->illumination= 0.0f;
|
|
}
|
|
}
|
|
|
|
switch (trigger->state)
|
|
{
|
|
case _trigger_idle:
|
|
if (!TEST_FLAG(weapon->weapon.control_flags, _weapon_control_user_busy_bit) &&
|
|
weapon->object.parent_object_index!=NONE &&
|
|
trigger_definition->magazine_index!=NONE &&
|
|
weapon_trigger_magazine_valid(weapon_definition, trigger_definition)) /* port: one it has */
|
|
{
|
|
struct weapon_magazine *magazine= weapon_magazine_get(weapon, trigger_definition->magazine_index);
|
|
|
|
if (magazine &&
|
|
((magazine->rounds_loaded<trigger_definition->rounds_per_shot && !TEST_FLAG(trigger_definition->flags, _weapon_trigger_can_fire_with_partial_ammunition_bit)) ||
|
|
magazine->rounds_loaded<trigger_definition->minimum_rounds_loaded_per_shot ||
|
|
magazine->rounds_loaded==0))
|
|
{
|
|
weapon_magazine_start_reload(weapon_index, trigger_definition->magazine_index, TRUE);
|
|
}
|
|
}
|
|
|
|
if (triggers_down[trigger_index] && weapon_trigger_can_fire_again(weapon_index, trigger_index))
|
|
{
|
|
weapon_trigger_begin_firing(weapon_index, trigger_index, FALSE);
|
|
}
|
|
else if (trigger->idle_ticks<127)
|
|
{
|
|
trigger->idle_ticks++;
|
|
}
|
|
break;
|
|
|
|
case _trigger_spewing:
|
|
if (trigger->state_timer)
|
|
{
|
|
weapon_trigger_begin_firing(weapon_index, trigger_index, TRUE);
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_recover(weapon_index, trigger_index);
|
|
}
|
|
break;
|
|
|
|
case _trigger_overloading:
|
|
if (!triggers_down[trigger_index])
|
|
{
|
|
weapon_trigger_begin_firing(weapon_index, trigger_index, TRUE);
|
|
}
|
|
else if (!trigger->state_timer && weapon->weapon.alternate_shots_loaded<weapon_definition->weapon.maximum_alternate_shots_loaded)
|
|
{
|
|
weapon_trigger_overload(weapon_index, trigger_index);
|
|
}
|
|
break;
|
|
|
|
case _trigger_charging:
|
|
if (trigger->state_timer)
|
|
{
|
|
if (!triggers_down[trigger_index])
|
|
{
|
|
if (trigger_index==0 && weapon_definition->weapon.triggers.count>1 && !TEST_FLAG(trigger->flags, _weapon_trigger_fired_before_charging_bit))
|
|
{
|
|
weapon_trigger_begin_firing(weapon_index, trigger_index, TRUE);
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_idle(weapon_index, trigger_index);
|
|
}
|
|
|
|
if (trigger->charging_effect_index!=NONE)
|
|
{
|
|
effect_stop(trigger->charging_effect_index, TRUE);
|
|
trigger->charging_effect_index= NONE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_fully_charged(weapon_index, trigger_index);
|
|
}
|
|
break;
|
|
|
|
case _trigger_charged:
|
|
if (triggers_down[trigger_index])
|
|
{
|
|
weapon->weapon.overcharged= 1.0f-(trigger->state_timer*(1.0f/TICKS_PER_SECOND))/trigger_definition->charged_time;
|
|
if (trigger->state_timer)
|
|
{
|
|
/* port: this never checked for no magazine. With none (NONE) it
|
|
reads the slot just before the magazines, which is still in
|
|
the weapon, so that is kept as it was. Any other index must
|
|
be a magazine the weapon has. */
|
|
struct weapon_magazine *magazine= trigger_definition->magazine_index==NONE ?
|
|
&weapon->weapon.magazines[trigger_definition->magazine_index] :
|
|
weapon_trigger_magazine_valid(weapon_definition, trigger_definition) ?
|
|
weapon_magazine_get(weapon, trigger_definition->magazine_index) : NULL;
|
|
|
|
if (magazine &&
|
|
magazine->rounds_loaded<trigger_definition->rounds_per_shot && !TEST_FLAG(trigger_definition->flags, _weapon_trigger_can_fire_with_partial_ammunition_bit))
|
|
{
|
|
weapon_trigger_release_charge(weapon_index, trigger_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_overcharged(weapon_index, trigger_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_release_charge(weapon_index, trigger_index);
|
|
}
|
|
break;
|
|
|
|
case _trigger_recovering:
|
|
if (!trigger->state_timer)
|
|
{
|
|
if (TEST_FLAG(trigger_definition->flags, _weapon_trigger_latched_bit) &&
|
|
TEST_FLAG(weapon->item.flags, _item_belongs_to_player_bit) &&
|
|
!TEST_FLAG(trigger->flags, _weapon_trigger_released_since_last_shot_bit))
|
|
{
|
|
weapon_trigger_locked(weapon_index, trigger_index);
|
|
}
|
|
else
|
|
{
|
|
weapon_trigger_idle(weapon_index, trigger_index);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _trigger_tracking:
|
|
if (!triggers_down[trigger_index] || weapon->weapon.tracked_object_index==NONE)
|
|
{
|
|
weapon_trigger_finish_tracking(weapon_index, trigger_index);
|
|
}
|
|
break;
|
|
|
|
case _trigger_locked:
|
|
if (!triggers_down[trigger_index])
|
|
{
|
|
weapon_trigger_idle(weapon_index, trigger_index);
|
|
}
|
|
break;
|
|
|
|
case _trigger_uninitialized:
|
|
if (!trigger->state_timer)
|
|
{
|
|
weapon_trigger_idle(weapon_index, trigger_index);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
match_vassert("c:\\halo\\SOURCE\\items\\weapons.c", 778, FALSE, NULL);
|
|
break;
|
|
}
|
|
|
|
if (triggers_down[trigger_index])
|
|
{
|
|
trigger->rate_of_fire+= trigger_definition->runtime_rate_of_fire_acceleration_time;
|
|
if (trigger->rate_of_fire>1.0f)
|
|
{
|
|
trigger->rate_of_fire= 1.0f;
|
|
}
|
|
|
|
if (trigger_definition->blurred_rate_of_fire!=0.0f &&
|
|
!TEST_FLAG(trigger->flags, _weapon_trigger_blurred_bit) &&
|
|
trigger->rate_of_fire>trigger_definition->blurred_rate_of_fire)
|
|
{
|
|
object_permute_region(weapon_get_effect_object_index(weapon_index), data_00307140.blurred_permutation_names[trigger_index], NONE, TRUE);
|
|
SET_FLAG(trigger->flags, _weapon_trigger_blurred_bit, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trigger->rate_of_fire-= trigger_definition->runtime_rate_of_fire_deceleration_time;
|
|
if (trigger->rate_of_fire<0.0f)
|
|
{
|
|
trigger->rate_of_fire= 0.0f;
|
|
}
|
|
|
|
if (TEST_FLAG(trigger->flags, _weapon_trigger_blurred_bit) && trigger->rate_of_fire<trigger_definition->blurred_rate_of_fire)
|
|
{
|
|
object_permute_region(weapon_get_effect_object_index(weapon_index), data_00307140.blurred_permutation_names[trigger_index], NONE, FALSE);
|
|
SET_FLAG(trigger->flags, _weapon_trigger_blurred_bit, FALSE);
|
|
}
|
|
}
|
|
|
|
if (trigger->state==_trigger_spewing || trigger->state==_trigger_recovering || triggers_down[trigger_index])
|
|
{
|
|
trigger->error+= trigger_definition->runtime_error_acceleration_time;
|
|
if (trigger->error>1.0f)
|
|
{
|
|
trigger->error= 1.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trigger->error-= trigger_definition->runtime_error_deceleration_time;
|
|
if (trigger->error<0.0f)
|
|
{
|
|
trigger->error= 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
profile_exit(data_00307140.update_profile);
|
|
|
|
return TRUE;
|
|
}
|