Files
Stefan MaierandClaude Opus 5.5 b6a943f1a6 Merge upstream 2026-10-10: network version 25, touch controls, in-game settings, FOV options, voice chat, votekick
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>
2026-10-10 08:51:46 +02:00

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