Files
Cesarus85--ringfall-mjolnir-vr/source/interface/first_person_weapons.c
T

3026 lines
98 KiB
C

/*
FIRST_PERSON_WEAPONS.C
symbols in this file:
000CBC60 0020:
_animation_update_render_only (0000)
000CBC80 0040:
_first_person_weapons_initialize (0000)
000CBCC0 0010:
_first_person_weapons_dispose (0000)
000CBCD0 0050:
_first_person_weapons_initialize_for_new_map (0000)
000CBD20 0010:
_first_person_weapons_dispose_from_old_map (0000)
000CBD30 00c0:
_code_000cbd30 (0000)
000CBDF0 0110:
_code_000cbdf0 (0000)
000CBF00 0120:
_code_000cbf00 (0000)
000CC020 0040:
_code_000cc020 (0000)
000CC060 00a0:
_code_000cc060 (0000)
000CC100 00b0:
_code_000cc100 (0000)
000CC1B0 00e0:
_code_000cc1b0 (0000)
000CC290 0060:
_code_000cc290 (0000)
000CC2F0 0040:
_code_000cc2f0 (0000)
000CC330 0080:
_code_000cc330 (0000)
000CC3B0 0290:
_first_person_weapon_draw (0000)
000CC640 0080:
_first_person_weapon_get_local_index (0000)
000CC6C0 00d0:
_first_person_weapon_get_marker_by_name (0000)
000CC790 00e0:
_first_person_weapon_center_flashlight (0000)
000CC870 00d0:
_first_person_weapon_adjust_light (0000)
000CC940 00c0:
_first_person_weapon_get_node_matrix (0000)
000CCA00 00b0:
_code_000cca00 (0000)
000CCAB0 0560:
_code_000ccab0 (0000)
000CD010 00b0:
_first_person_weapon_render_update (0000)
000CD0C0 0040:
_first_person_weapon_get_marker_by_name_render (0000)
000CD100 02b0:
_code_000cd100 (0000)
000CD3B0 0260:
_code_000cd3b0 (0000)
000CD610 0060:
_code_000cd610 (0000)
000CD670 0220:
_code_000cd670 (0000)
000CD890 0050:
_first_person_weapon_message_from_unit (0000)
000CD8E0 0040:
_first_person_weapon_message_from_weapon (0000)
000CD920 0170:
_code_000cd920 (0000)
000CDA90 0600:
_code_000cda90 (0000)
000CE090 00f0:
_first_person_weapons_update (0000)
0027076C 0015:
??_C@_0BF@MEAEHHHF@first_person_weapons?$AA@ (0000)
00270784 0030:
??_C@_0DA@LGOECHKB@c?3?2halo?2SOURCE?2interface?2first_p@ (0000)
002707B4 0015:
??_C@_0BF@LKJCKPFC@first?5person?5weapons?$AA@ (0000)
002707CC 0028:
??_C@_0CI@FFNKKBPO@first_person_weapon?9?$DOweapon_inde@ (0000)
002707F8 0059:
??_C@_0FJ@DFINBDBK@animation_graph_node_index?$DO?$DN0?5?$CG?$CG@ (0000)
00270854 000b:
??_C@_0L@HGCOMEEN@flashlight?$AA@ (0000)
00270860 0039:
??_C@_0DJ@KDPHKICA@node_index?$DO?$DN0?5?$CG?$CG?5node_index?$DManim@ (0000)
0027089C 0035:
??_C@_0DF@COECBIGL@local?5player?5?$CFd?0?5weapon?5?$CI0x?$CFx?$CJ?0?5@ (0000)
002708D8 00a5:
??_C@_0KF@OCJNMLNK@first_person_weapon?9?$DOshotgun_rel@ (0000)
00270980 0004:
__real@c1f00000 (0000)
00453CC8 0004:
_bss_00453cc8 (0000)
*/
/* ---------- headers */
#define REAL_MATH_EXTERNAL_SIGNED_ANGULAR_DIFFERENCE
#define REAL_MATH_EXTERNAL_REAL_LOCAL_RANDOM
#define REAL_MATH_EXTERNAL_REAL_LOCAL_RANDOM_RANGE
#include "interface/first_person_weapons.h"
#include "cache/predicted_resources.h"
#include "camera/director.h"
#include "cseries/cseries.h"
#include "cseries/errors.h"
#include "effects/effects.h"
#include "effects/particles.h"
#include "game/game_globals.h"
#include "game/player_control.h"
#include "game/players.h"
#include "items/weapon_datum_flags.h"
#include "items/weapon_definitions.h"
#include "items/weapons.h"
#include "models/model_animations.h"
#include "models/model_definitions.h"
#include "models/models.h"
#include "networking/network_connection.h"
#include "objects/objects.h"
#include "render/render.h"
#include "render/render_objects.h"
#include "saved games/game_state.h"
#include "scenario/scenario.h"
#include "sound/game_sound.h"
#include "sound/sound_manager.h"
#include "units/units.h"
#ifdef HALO_ANDROID
#include "halo_vr.h"
#endif
/* ---------- constants */
enum
{
_render_model_first_person_bit = 3,
};
enum
{
NUMBER_OF_FIRST_PERSON_WEAPON_OVERLAY_FRAMES = 9,
FIRST_PERSON_WEAPON_RELOAD_SETTLE_TICKS = 44,
};
#define FIRST_PERSON_WEAPON_FIRING_PUSH_BACK_VELOCITY 0.05f
enum first_person_weapon_state
{
_first_person_weapon_state_idle = 0,
_first_person_weapon_state_overheating,
_first_person_weapon_state_overheating_again,
_first_person_weapon_state_overheated,
_first_person_weapon_state_charged,
_first_person_weapon_state_posing,
_first_person_weapon_state_primary_fire,
_first_person_weapon_state_secondary_fire,
_first_person_weapon_state_primary_misfire,
_first_person_weapon_state_secondary_misfire,
_first_person_weapon_state_melee,
_first_person_weapon_state_light_on,
_first_person_weapon_state_light_off,
_first_person_weapon_state_reload_while_empty,
_first_person_weapon_state_reload_while_full,
_first_person_weapon_state_shotgun_enter_reload,
_first_person_weapon_state_shotgun_exit_reload_empty,
_first_person_weapon_state_shotgun_exit_reload_full,
_first_person_weapon_state_put_away,
_first_person_weapon_state_ready,
_first_person_weapon_state_throw_grenade,
_first_person_weapon_state_throw_grenade_overheated,
_first_person_weapon_state_overheated_exit,
_first_person_weapon_state_overheating_super_recoil,
NUMBER_OF_FIRST_PERSON_WEAPON_STATES,
};
enum
{
_render_model_effect_type_none = 0,
_render_model_effect_type_active_camouflage,
};
enum
{
animation_update_kind_render_only = 0,
};
enum animation_update_result
{
_animation_no_key_frame = 0,
_animation_key_frame,
_animation_will_restart_on_next_frame,
};
/* ---------- macros */
/* ---------- structures */
struct shader;
/* Kept TU-private because moving this recovered layout into a shared header perturbs VC7 output. */
struct animation_state_continuous
{
short index;
word pad;
real frame_index;
};
/* Recovered animation-graph block layouts used only by this translation unit. */
struct animation_graph_sound_reference
{
struct tag_reference sound;
long unused;
};
typedef char verify_animation_graph_sound_reference_size[
sizeof(struct animation_graph_sound_reference) == 0x14 ? 1 : -1];
struct animation_graph_first_person_weapon_animations
{
long unused[4];
struct tag_block animations; // animation_graph_animation_index
};
typedef char verify_animation_graph_first_person_weapon_animations_size[
sizeof(struct animation_graph_first_person_weapon_animations) == 0x1C ? 1 : -1];
/* Only the leading name is consumed here; the remaining bytes preserve the January layout. */
struct animation_graph_node
{
char name[TAG_STRING_LENGTH+1];
byte reserved0020[0x20];
};
typedef char verify_animation_graph_node_size[
sizeof(struct animation_graph_node) == 0x40 ? 1 : -1];
/* TU-private rendering packet layout, also recovered independently by the rendering owners. */
struct render_model_effect
{
short type;
word pad;
real intensity;
real parameter;
long source_object_index;
real_point3d source_object_centroid;
struct shader const *modifier_shader;
byte reserved0020[8]; /* render_animation modifier_animation */
};
typedef char verify_render_model_effect_size[
sizeof(struct render_model_effect) == 0x28 ? 1 : -1];
struct first_person_weapon
{
boolean visible;
byte reserved0001[3];
long unit_index;
long weapon_index;
short state;
short ticks_until_pose;
short ticks_idle;
short ticks_until_predict;
short overlay_animation_index;
struct animation_state state_animation;
struct animation_state moving_animation;
short reserved001e;
struct animation_state_continuous overcharged_jitter_animation;
real firing_push_back;
real firing_push_back_velocity;
real_vector2d position;
real_vector2d position_velocity;
real_vector2d turning;
real_vector2d turning_velocity;
boolean rendered;
byte reserved0051[3];
real_vector3d render_forward;
real_euler_angles2d render_facing;
real_euler_angles2d last_render_facing;
real_point3d render_position;
real_point3d last_render_position;
short interpolation_frame_index;
short interpolation_frame_count;
real_orientation node_orientations[MAXIMUM_NODES_PER_ANIMATION];
real_orientation original_node_orientations[MAXIMUM_NODES_PER_ANIMATION];
struct real_matrix4x3 node_matrices[MAXIMUM_NODES_PER_ANIMATION];
boolean weapon_node_remapping_table_valid;
byte reserved1d8d;
short weapon_node_remapping_table[MAXIMUM_NODES_PER_ANIMATION];
boolean hands_node_remapping_table_valid;
byte reserved1e0f;
short hands_node_remapping_table[MAXIMUM_NODES_PER_ANIMATION];
boolean shotgun_empty;
byte reserved1e91;
short shotgun_shells_to_reload;
short shotgun_reload_type;
short reserved1e96;
long current_sound_index;
short current_sound_state;
byte reserved1e9e[2];
};
typedef char verify_first_person_weapon_size[
sizeof(struct first_person_weapon) == 0x1EA0 ? 1 : -1];
typedef char verify_first_person_weapon_node_matrices_offset[
offsetof(struct first_person_weapon, node_matrices) == 0x108C ? 1 : -1];
typedef char verify_first_person_weapon_rendered_offset[
offsetof(struct first_person_weapon, rendered) == 0x50 ? 1 : -1];
typedef char verify_first_person_weapon_node_orientations_offset[
offsetof(struct first_person_weapon, node_orientations) == 0x8C ? 1 : -1];
typedef char verify_first_person_weapon_node_remapping_table_offset[
offsetof(struct first_person_weapon, weapon_node_remapping_table) == 0x1D8E ? 1 : -1];
typedef char verify_first_person_weapon_shotgun_empty_offset[
offsetof(struct first_person_weapon, shotgun_empty) == 0x1E90 ? 1 : -1];
typedef char verify_first_person_weapon_current_sound_index_offset[
offsetof(struct first_person_weapon, current_sound_index) == 0x1E98 ? 1 : -1];
typedef char verify_first_person_weapon_current_sound_state_offset[
offsetof(struct first_person_weapon, current_sound_state) == 0x1E9C ? 1 : -1];
typedef char verify_weapon_first_person_animations_index_offset[
offsetof(
struct weapon_definition,
weapon.interface_definition.first_person_animations.index) == 0x478 ? 1 : -1];
typedef char verify_weapon_flags_offset[
offsetof(struct weapon_datum, weapon.flags) == 0x1DC ? 1 : -1];
typedef char verify_weapon_type_offset[
offsetof(struct weapon_definition, weapon.weapon_type) == 0x4E2 ? 1 : -1];
typedef char verify_animation_graph_first_person_weapon_animations_offset[
offsetof(struct animation_graph, first_person_weapon_animations) == 0x48 ? 1 : -1];
typedef char verify_unit_flags_offset[
offsetof(struct unit_datum, unit.flags) == 0x1B4 ? 1 : -1];
typedef char verify_unit_active_camouflage_offset[
offsetof(struct unit_datum, unit.active_camouflage) == 0x32C ? 1 : -1];
typedef char verify_object_outgoing_function_values_offset[
offsetof(struct object_datum, object.outgoing_function_values) == 0xE4 ? 1 : -1];
typedef char verify_object_outgoing_change_colors_offset[
offsetof(struct object_datum, object.outgoing_change_colors) == 0x168 ? 1 : -1];
typedef char verify_game_globals_first_person_interface_offset[
offsetof(struct game_globals, first_person_interface) == 0x17C ? 1 : -1];
typedef char verify_animation_graph_nodes_offset[
offsetof(struct animation_graph, nodes) == 0x68 ? 1 : -1];
/* ---------- prototypes */
static void first_person_weapon_set_visibility(
short local_player_index,
boolean visible);
static void first_person_weapon_build_node_matrices(
short local_player_index);
static void first_person_weapon_predict(
short local_player_index);
static void first_person_weapon_start_interpolation(
short local_player_index,
short frame_count);
static void first_person_weapon_set_state(
short local_player_index,
short new_state,
boolean reset_sounds);
static void first_person_weapon_switch_weapons(
short local_player_index);
static void first_person_weapon_new_unit(
short local_player_index,
long unit_index);
static void first_person_weapon_message(
short local_player_index,
short message_type);
static void first_person_weapon_next_state(
short local_player_index);
static void first_person_weapon_update(
short local_player_index);
static boolean model_build_remapping_table_for_animation_graph(
long model_index,
long animation_graph_index,
short *node_remapping_table);
static void weapon_play_first_person_weapon_sound(
long weapon_index,
short message_type);
static long first_person_weapon_state_from_weapon_message(
short message_type);
static long first_person_animation_type_from_weapon_state(
short state);
static void model_remap_node_matrices_to_match_animation_graph(
long model_index,
struct real_matrix4x3 *model_node_matrices,
long animation_graph_index,
struct real_matrix4x3 const *node_matrices,
short const *node_remapping_table);
static struct first_person_weapon *first_person_weapon_get(
short local_player_index);
static short first_person_weapon_index_from_weapon_index(
long weapon_index);
static short first_person_weapon_index_from_unit_index(
long unit_index);
/* ---------- globals */
static struct first_person_weapon *first_person_weapons;
/* ---------- public code */
short animation_update_render_only(
long animation_graph_index,
struct animation_state *state,
long *sound_index)
{
return animation_update_internal(
0,
animation_graph_index,
state,
sound_index);
}
void first_person_weapons_initialize(
void)
{
first_person_weapons = (struct first_person_weapon *)game_state_malloc(
"first person weapons",
NULL,
sizeof(*first_person_weapons) * MAXIMUM_NUMBER_OF_LOCAL_PLAYERS);
match_assert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
240,
first_person_weapons);
return;
}
void first_person_weapons_dispose(
void)
{
return;
}
void first_person_weapons_initialize_for_new_map(
void)
{
short local_player_index;
for (local_player_index=0; local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS; local_player_index++)
{
csmemset(
&first_person_weapons[local_player_index],
0,
sizeof(first_person_weapons[local_player_index]));
first_person_weapons[local_player_index].unit_index= NONE;
first_person_weapons[local_player_index].current_sound_index= NONE;
first_person_weapons[local_player_index].current_sound_state= NONE;
}
return;
}
void first_person_weapons_dispose_from_old_map(
void)
{
return;
}
long first_person_weapon_get_local_index(
long weapon_index)
{
short local_player_index;
for (local_player_index=0; local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS; local_player_index++)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
if (first_person_weapon->weapon_index==weapon_index && first_person_weapon->visible)
{
break;
}
}
return (local_player_index==MAXIMUM_NUMBER_OF_LOCAL_PLAYERS) ? NONE : local_player_index;
}
#ifdef HALO_ANDROID
/* VR: the grenade the player holds (the left trigger, units.c) hangs from
the body, which the first-person view leaves out, and with it the grenade:
drawn instead in the left hand, as it is this frame, with the hands (which
are drawn over the world, and would hide it wherever they overlap) */
static void first_person_weapon_draw_held_grenade(
long unit_index,
struct render_lighting *lighting,
struct render_model_effect const *hands_effect)
{
struct unit_datum *unit= unit_get(unit_index);
struct object_datum *grenade;
struct object_definition *definition;
struct model *model;
struct render_model_effect model_effect;
real_matrix4x3 *node_matrices;
real_matrix4x3 hand, inverse, delta;
real_point3d position;
real_vector3d forward, up;
float hand_position[3], hand_forward[3], hand_up[3], scale;
short node_index;
if (unit->unit.grenade_object_index==NONE ||
unit->unit.grenade_throw_state!=_unit_grenade_throw_in_hand ||
!halo_vr_grenade_in_hand(render.local_player_index, hand_position, hand_forward, hand_up, &scale))
{
return;
}
grenade= object_get(unit->unit.grenade_object_index);
definition= object_definition_get(grenade->definition_index);
if (definition->object.model.index==NONE || TEST_FLAG(grenade->object.flags, _object_invisible_bit))
return;
model= model_definition_get(definition->object.model.index);
if (model->nodes.count<=0)
return;
node_matrices= object_get_node_matrices(unit->unit.grenade_object_index);
/* the whole grenade moved so its root lies in the hand, as the hand
turns (drawn only: the next tick puts it back on the body) */
position.x= hand_position[0];
position.y= hand_position[1];
position.z= hand_position[2];
forward.i= hand_forward[0];
forward.j= hand_forward[1];
forward.k= hand_forward[2];
up.i= hand_up[0];
up.j= hand_up[1];
up.k= hand_up[2];
matrix4x3_from_point_and_vectors(&hand, &position, &forward, &up);
hand.scale= scale;
matrix4x3_inverse(&node_matrices[0], &inverse);
matrix4x3_multiply(&hand, &inverse, &delta);
for (node_index= 0; node_index<model->nodes.count; node_index++)
{
real_matrix4x3 node= node_matrices[node_index];
matrix4x3_multiply(&delta, &node, &node_matrices[node_index]);
}
model_effect= *hands_effect;
render_model(
definition->object.model.index,
0.0f,
node_matrices,
grenade->object.region_permutations,
grenade->object.outgoing_change_colors,
grenade->object.outgoing_function_values,
lighting,
&render.camera.position,
0.0f,
&model_effect,
unit->unit.grenade_object_index,
grenade->object.forced_shader_permutation_index,
FLAG(_render_model_first_person_bit));
}
#endif
void first_person_weapon_draw(
void)
{
#ifdef HALO_ANDROID
/* VR: not in the scope's view, taken from the weapon itself */
if (halo_vr_scope_pass())
return;
#endif
if (render.local_player_index!=NONE)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(render.local_player_index);
if (local_player_get_player_index(render.local_player_index)!=NONE)
{
long unit_index= player_get(
local_player_get_player_index(render.local_player_index))->unit_index;
if (unit_index!=NONE &&
first_person_weapon->visible &&
first_person_weapon->unit_index!=NONE &&
first_person_weapon->weapon_index!=NONE
#ifdef HALO_ANDROID
/* VR: at a wheel or a gun the body's own hands hold them */
&& !(halo_vr_active() && render_object_vr_at_controls(unit_index))
#endif
)
{
struct unit_datum *unit= unit_get(unit_index);
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
if (weapon_definition->weapon.interface_definition.first_person_animations.index!=NONE)
{
struct real_matrix4x3 model_node_matrices[MAXIMUM_NODES_PER_ANIMATION];
struct render_model_effect model_effect;
struct game_globals_first_person_interface *first_person_interface=
TAG_BLOCK_GET_ELEMENT(
&scenario_get_game_globals()->first_person_interface,
0,
struct game_globals_first_person_interface);
struct animation_graph *animation_graph= animation_graph_definition_get(
weapon_definition->weapon.interface_definition.first_person_animations.index);
struct render_lighting *lighting= object_get_cached_render_lighting(unit_index, FLT_MAX);
model_effect.modifier_shader= NULL;
if (TEST_FLAG(unit->unit.flags, _unit_active_camouflaged_bit) ||
unit->unit.active_camouflage>0.0f)
{
model_effect.type= _render_model_effect_type_active_camouflage;
model_effect.intensity= unit->unit.active_camouflage;
model_effect.parameter= unit->unit.active_camouflage_super_amount;
model_effect.source_object_index= unit_index;
model_effect.source_object_centroid= render.camera.position;
}
else
{
model_effect.type= _render_model_effect_type_none;
}
if (first_person_weapon->weapon_node_remapping_table_valid &&
weapon_definition->weapon.interface_definition.first_person_model.index!=NONE)
{
model_remap_node_matrices_to_match_animation_graph(
weapon_definition->weapon.interface_definition.first_person_model.index,
model_node_matrices,
weapon_definition->weapon.interface_definition.first_person_animations.index,
first_person_weapon->node_matrices,
first_person_weapon->weapon_node_remapping_table);
render_model(
weapon_definition->weapon.interface_definition.first_person_model.index,
0.0f,
model_node_matrices,
NULL,
weapon->object.outgoing_change_colors,
weapon->object.outgoing_function_values,
lighting,
&render.camera.position,
0.0f,
&model_effect,
first_person_weapon->weapon_index,
0,
FLAG(_render_model_first_person_bit));
}
if (first_person_weapon->hands_node_remapping_table_valid &&
first_person_interface->hands.index!=NONE)
{
model_remap_node_matrices_to_match_animation_graph(
first_person_interface->hands.index,
model_node_matrices,
weapon_definition->weapon.interface_definition.first_person_animations.index,
first_person_weapon->node_matrices,
first_person_weapon->hands_node_remapping_table);
render_model(
first_person_interface->hands.index,
0.0f,
model_node_matrices,
NULL,
unit->object.outgoing_change_colors,
unit->object.outgoing_function_values,
lighting,
&render.camera.position,
0.0f,
&model_effect,
first_person_weapon->weapon_index,
0,
FLAG(_render_model_first_person_bit));
}
#ifdef HALO_ANDROID
if (halo_vr_active())
first_person_weapon_draw_held_grenade(unit_index, lighting, &model_effect);
#endif
}
}
}
}
return;
}
short first_person_weapon_get_marker_by_name(
long weapon_index,
char const *name,
struct object_marker *markers,
short maximum_marker_count)
{
short marker_count= 0;
struct weapon_datum *weapon= weapon_try_and_get(weapon_index);
if (weapon)
{
short local_player_index= first_person_weapon_index_from_weapon_index(weapon_index);
if (local_player_index!=NONE &&
director_get_perspective(local_player_index)==_director_perspective_first_person)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
if (first_person_weapon->weapon_node_remapping_table_valid &&
weapon_definition->weapon.interface_definition.first_person_model.index!=NONE &&
weapon_definition->weapon.interface_definition.first_person_animations.index!=NONE)
{
marker_count= model_get_marker_by_name(
weapon_definition->weapon.interface_definition.first_person_model.index,
name,
NULL,
first_person_weapon->weapon_node_remapping_table,
(short)animation_graph_definition_get(
weapon_definition->weapon.interface_definition.first_person_animations.index)->nodes.count,
first_person_weapon->node_matrices,
FALSE,
markers,
maximum_marker_count);
}
}
}
return marker_count;
}
void first_person_weapon_render_update(
void)
{
if (render.local_player_index!=NONE)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(render.local_player_index);
if (first_person_weapon->unit_index!=NONE &&
first_person_weapon->weapon_index!=NONE)
{
boolean visible= director_get_perspective(render.local_player_index)==_director_perspective_first_person &&
player_control_get_zoom_level(render.local_player_index)==NONE;
#ifdef HALO_ANDROID
/* VR: the weapon stays in the hand zoomed in (the scope is on it) */
if (halo_vr_active())
visible= director_get_perspective(render.local_player_index)==_director_perspective_first_person;
#endif
first_person_weapon_set_visibility(render.local_player_index, visible);
if (first_person_weapon->visible)
{
first_person_weapon_build_node_matrices(render.local_player_index);
}
}
}
return;
}
short first_person_weapon_get_marker_by_name_render(
long weapon_index,
char const *name,
struct object_marker *markers,
short maximum_marker_count)
{
if (render.local_player_index==first_person_weapon_index_from_weapon_index(weapon_index))
{
return first_person_weapon_get_marker_by_name(
weapon_index,
name,
markers,
maximum_marker_count);
}
return 0;
}
void first_person_weapon_center_flashlight(
long unit_index,
real_point3d *position,
real_vector3d *forward,
real_vector3d *up)
{
short local_player_index= first_person_weapon_index_from_unit_index(unit_index);
if (local_player_index!=NONE)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
if (first_person_weapon->visible)
{
struct object_marker marker;
if (first_person_weapon_get_marker_by_name(
first_person_weapon->weapon_index,
"flashlight",
&marker,
1)>0)
{
position->x= marker.matrix.position.x - marker.matrix.forward.i*0.5f;
position->y= marker.matrix.position.y - marker.matrix.forward.j*0.5f;
position->z= marker.matrix.position.z - marker.matrix.forward.k*0.5f;
*forward= marker.matrix.forward;
*up= marker.matrix.up;
}
}
}
return;
}
boolean first_person_weapon_adjust_light(
long weapon_index,
char const *marker_name,
real_point3d *position,
real_vector3d *forward,
real_vector3d *up)
{
struct weapon_datum *weapon= weapon_get(weapon_index);
struct unit_datum *unit= unit_get(weapon->object.parent_object_index);
long player_index= unit->unit.player_index;
boolean adjusted= FALSE;
if (player_index!=NONE)
{
struct player_datum *player= player_get(player_index);
short local_player_index= player->local_player_index;
struct object_marker marker;
if (local_player_index!=NONE &&
local_player_index==render.local_player_index &&
first_person_weapon_get(local_player_index)->visible &&
first_person_weapon_get_marker_by_name(weapon_index, marker_name, &marker, 1)>0)
{
*position= marker.matrix.position;
*forward= marker.matrix.forward;
*up= marker.matrix.up;
adjusted= TRUE;
}
}
return adjusted;
}
void first_person_weapon_message_from_unit(
long unit_index,
short message_type)
{
short local_player_index= first_person_weapon_index_from_unit_index(unit_index);
first_person_weapon_message(local_player_index, message_type);
if (local_player_index==NONE)
{
struct unit_datum *unit= unit_get(unit_index);
if (unit->unit.current_weapon_index!=NONE)
{
weapon_play_first_person_weapon_sound(
unit->unit.current_weapon_index,
message_type);
}
}
return;
}
void first_person_weapon_message_from_weapon(
long weapon_index,
short message_type)
{
short local_player_index= first_person_weapon_index_from_weapon_index(weapon_index);
first_person_weapon_message(local_player_index, message_type);
if (local_player_index==NONE)
{
weapon_play_first_person_weapon_sound(weapon_index, message_type);
}
return;
}
struct real_matrix4x3 *first_person_weapon_get_node_matrix(
short local_player_index,
short node_index)
{
struct first_person_weapon *first_person_weapon;
struct weapon_datum *weapon;
struct weapon_definition *weapon_definition;
struct animation_graph *animation_graph;
match_assert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
1433,
local_player_index>=0 && local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS);
first_person_weapon = &first_person_weapons[local_player_index];
weapon = weapon_get(first_person_weapon->weapon_index);
weapon_definition = weapon_definition_get(weapon->definition_index);
animation_graph = animation_graph_definition_get(
weapon_definition->weapon.interface_definition.first_person_animations.index);
match_assert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
718,
node_index>=0 && node_index<animation_graph->nodes.count);
return &first_person_weapon->node_matrices[node_index];
}
void first_person_weapons_update(
void)
{
short local_player_index;
for (local_player_index=0; local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS; local_player_index++)
{
long player_index= local_player_get_player_index(local_player_index);
if (player_index!=NONE)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
long unit_index= player_get(player_index)->unit_index;
if (first_person_weapon->unit_index!=unit_index)
{
first_person_weapon_new_unit(local_player_index, unit_index);
}
if (first_person_weapon->weapon_index==NONE)
{
first_person_weapon_switch_weapons(local_player_index);
}
first_person_weapon_update(local_player_index);
}
}
return;
}
/* ---------- private code */
static void first_person_weapon_set_visibility(
short local_player_index,
boolean visible)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
if (visible!=first_person_weapon->visible)
{
if (visible)
{
match_assert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
1555,
first_person_weapon->weapon_index!=NONE);
effects_start_on_first_person_weapon(local_player_index, first_person_weapon->weapon_index);
}
else
{
effects_stop_on_first_person_weapon(local_player_index);
particles_stop_on_first_person_weapon(local_player_index);
}
first_person_weapon->visible= visible;
}
return;
}
static void first_person_weapon_predict(
short local_player_index)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
if (first_person_weapon->weapon_index!=NONE)
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
predicted_resources_precache(&weapon_definition->weapon.predicted_resources);
}
first_person_weapon->ticks_until_predict= TICKS_PER_SECOND;
return;
}
static void first_person_weapon_start_interpolation(
short local_player_index,
short frame_count)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
struct animation_graph *animation_graph= animation_graph_definition_get(
weapon_definition->weapon.interface_definition.first_person_animations.index);
csmemcpy(
first_person_weapon->original_node_orientations,
first_person_weapon->node_orientations,
animation_graph->nodes.count*sizeof(real_orientation));
if (frame_count>=first_person_weapon->interpolation_frame_count-
first_person_weapon->interpolation_frame_index)
{
first_person_weapon->interpolation_frame_index= 0;
first_person_weapon->interpolation_frame_count= frame_count;
}
return;
}
static void first_person_weapon_set_state(
short local_player_index,
short new_state,
boolean reset_sounds)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
if (first_person_weapon->weapon_index!=NONE)
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
if (TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit))
{
switch (new_state)
{
case _first_person_weapon_state_ready:
new_state= _first_person_weapon_state_overheating_again;
break;
case _first_person_weapon_state_throw_grenade:
new_state= _first_person_weapon_state_throw_grenade_overheated;
break;
}
}
}
switch (new_state)
{
case _first_person_weapon_state_ready:
if (first_person_weapon->state==_first_person_weapon_state_ready)
{
new_state= NONE;
}
break;
case _first_person_weapon_state_light_off:
case _first_person_weapon_state_light_on:
if (first_person_weapon->state!=_first_person_weapon_state_idle &&
first_person_weapon->state!=_first_person_weapon_state_posing)
{
new_state= NONE;
}
break;
case _first_person_weapon_state_primary_fire:
case _first_person_weapon_state_secondary_fire:
case _first_person_weapon_state_primary_misfire:
case _first_person_weapon_state_secondary_misfire:
if (first_person_weapon->state!=_first_person_weapon_state_idle &&
first_person_weapon->state!=_first_person_weapon_state_posing &&
first_person_weapon->state!=_first_person_weapon_state_primary_fire &&
first_person_weapon->state!=_first_person_weapon_state_charged &&
first_person_weapon->state!=_first_person_weapon_state_shotgun_enter_reload &&
first_person_weapon->state!=_first_person_weapon_state_overheated_exit &&
first_person_weapon->state!=_first_person_weapon_state_shotgun_exit_reload_empty &&
first_person_weapon->state!=_first_person_weapon_state_shotgun_exit_reload_full &&
first_person_weapon->state!=_first_person_weapon_state_reload_while_empty &&
first_person_weapon->state!=_first_person_weapon_state_reload_while_full)
{
new_state= NONE;
}
break;
}
if (new_state!=NONE && first_person_weapon->weapon_index!=NONE)
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
short animation_type;
short interpolation_frame_count;
if (weapon_definition->weapon.weapon_type==_weapon_type_plasma_pistol &&
new_state==_first_person_weapon_state_overheated &&
!TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit))
{
new_state= _first_person_weapon_state_idle;
}
animation_type= (short)first_person_animation_type_from_weapon_state(new_state);
if (weapon_definition->weapon.weapon_type==_weapon_type_shotgun &&
first_person_weapon->state==_first_person_weapon_state_shotgun_exit_reload_empty)
{
interpolation_frame_count= 0;
}
else
{
switch (new_state)
{
case _first_person_weapon_state_overheated:
case _first_person_weapon_state_melee:
case _first_person_weapon_state_ready:
interpolation_frame_count= 0;
break;
case _first_person_weapon_state_primary_fire:
case _first_person_weapon_state_secondary_fire:
case _first_person_weapon_state_primary_misfire:
case _first_person_weapon_state_secondary_misfire:
interpolation_frame_count= 3;
break;
default:
interpolation_frame_count= 6;
break;
}
}
if (first_person_weapon->unit_index!=NONE &&
first_person_weapon->weapon_index!=NONE)
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
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 *first_person_weapon_animations=
TAG_BLOCK_GET_ELEMENT(
&animation_graph->first_person_weapon_animations,
0,
struct animation_graph_first_person_weapon_animations);
if (first_person_weapon_animations &&
animation_type>=0 &&
animation_type<first_person_weapon_animations->animations.count)
{
short animation_index= animation_graph_animation_index_get(
&first_person_weapon_animations->animations)[animation_type].animation_index;
if (animation_index!=NONE)
{
if (reset_sounds &&
first_person_weapon->current_sound_index!=NONE &&
first_person_weapon->current_sound_state!=_first_person_weapon_state_overheating)
{
sound_stop_impulse(first_person_weapon->current_sound_index);
first_person_weapon->current_sound_index= NONE;
first_person_weapon->current_sound_state= NONE;
}
if (interpolation_frame_count>0)
{
first_person_weapon_start_interpolation(
local_player_index,
interpolation_frame_count);
}
first_person_weapon->state= new_state;
first_person_weapon->state_animation.index= animation_index;
first_person_weapon->state_animation.frame_index= 0;
}
}
}
}
}
return;
}
#ifdef HALO_ANDROID
/* ---------- VR arms
The first-person skeleton hangs from the camera at the right controller
(halo_vr_weapon_camera), so the weapon and the right hand lie where they
should; its arms, though, reach up to shoulders that float with the weapon.
Here each arm's upper arm and forearm are turned (two-bone inverse
kinematics) to run from the player's shoulders, below the head, to its
wrist. The left wrist, unless the hand holds the weapon's foregrip, goes to
the left controller, its fingers with it. */
#define VR_METRES_PER_WORLD_UNIT 3.048f
struct vr_arm_nodes
{
long graph_index;
short upper[2];
short fore[2];
short wrist[2];
short gun;
/* the left hand's knuckles: index, middle and little finger */
short left_index;
short left_middle;
short left_pinky;
long barrel_logged_graph_index;
/* the whole pose in the weapon camera's frame, outside a melee attack */
boolean idle_pose_valid;
short idle_pose_count;
real_matrix4x3 idle_pose[MAXIMUM_NODES_PER_ANIMATION];
};
static struct vr_arm_nodes vr_arm_nodes = { NONE, { NONE, NONE }, { NONE, NONE }, { NONE, NONE }, NONE, NONE, NONE, NONE,
NONE };
static void vr_vector_scale_add(
real_vector3d *out,
real_vector3d const *vector,
real scale)
{
out->i += vector->i * scale;
out->j += vector->j * scale;
out->k += vector->k * scale;
}
/* turns the vector by the least rotation that takes from to to (unit) */
static void vr_rotate_between(
real_vector3d const *from,
real_vector3d const *to,
real_vector3d *v)
{
real_vector3d axis;
real_vector3d turned;
real sine, cosine, along;
cross_product3d(from, to, &axis);
sine = normalize3d(&axis);
if (sine < 1e-6f)
return;
cosine = dot_product3d(from, to);
cross_product3d(&axis, v, &turned);
along = dot_product3d(&axis, v);
v->i = v->i * cosine + turned.i * sine + axis.i * along * (1.f - cosine);
v->j = v->j * cosine + turned.j * sine + axis.j * along * (1.f - cosine);
v->k = v->k * cosine + turned.k * sine + axis.k * along * (1.f - cosine);
}
/* turns v about the unit axis by angle (radians) */
static void vr_rotate_about(
real_vector3d const *axis,
real angle,
real_vector3d *v)
{
real_vector3d turned;
real sin_angle = sine(angle);
real cos_angle = cosine(angle);
real along = dot_product3d(axis, v);
cross_product3d(axis, v, &turned);
v->i = v->i * cos_angle + turned.i * sin_angle + axis->i * along * (1.f - cos_angle);
v->j = v->j * cos_angle + turned.j * sin_angle + axis->j * along * (1.f - cos_angle);
v->k = v->k * cos_angle + turned.k * sin_angle + axis->k * along * (1.f - cos_angle);
}
/* A forearm turned only to point at the wrist keeps the animation's twist,
while the hand may have rolled half a turn from it: the skin between them
collapses. Twists the forearm about its bone by most of the hand's roll
from the animation (the hand's turn from old_wrist to new_wrist, applied to
the animated forearm old_forearm), as a real forearm twists along its
length. */
static void vr_twist_forearm(
real_matrix4x3 *forearm,
real_matrix4x3 const *old_forearm,
real_matrix4x3 const *old_wrist,
real_matrix4x3 const *new_wrist,
real_point3d const *wrist_position)
{
real_vector3d const *old_axes[3];
real_vector3d const *new_axes[3];
real_vector3d bone, turned_up, up, across;
real along, angle;
short axis;
vector_from_points3d(&forearm->position, wrist_position, &bone);
if (normalize3d(&bone) < 1e-6f)
return;
/* the animated forearm's up, turned as the hand turned */
old_axes[0] = &old_wrist->forward;
old_axes[1] = &old_wrist->left;
old_axes[2] = &old_wrist->up;
new_axes[0] = &new_wrist->forward;
new_axes[1] = &new_wrist->left;
new_axes[2] = &new_wrist->up;
turned_up.i = turned_up.j = turned_up.k = 0.f;
for (axis = 0; axis < 3; axis++)
vr_vector_scale_add(&turned_up, new_axes[axis], dot_product3d(&old_forearm->up, old_axes[axis]));
/* both ups across the bone, and the angle between them about it */
up = forearm->up;
vr_vector_scale_add(&up, &bone, -dot_product3d(&up, &bone));
vr_vector_scale_add(&turned_up, &bone, -dot_product3d(&turned_up, &bone));
if (normalize3d(&up) < 1e-4f || normalize3d(&turned_up) < 1e-4f)
return;
cross_product3d(&up, &turned_up, &across);
along = dot_product3d(&up, &turned_up);
angle = arctangent(dot_product3d(&across, &bone), along) * 0.6f;
vr_rotate_about(&bone, angle, &forearm->forward);
vr_rotate_about(&bone, angle, &forearm->left);
vr_rotate_about(&bone, angle, &forearm->up);
}
/* a node turned so that the bone to its child, from old to new direction */
static void vr_turn_bone(
real_matrix4x3 *node,
real_vector3d const *old_direction,
real_vector3d const *new_direction,
real_point3d const *position)
{
real_vector3d from = *old_direction;
real_vector3d to = *new_direction;
normalize3d(&from);
normalize3d(&to);
vr_rotate_between(&from, &to, &node->forward);
vr_rotate_between(&from, &to, &node->left);
vr_rotate_between(&from, &to, &node->up);
node->position = *position;
}
/* moves the descendants of a node that moved from old to new with it */
static void vr_carry_descendants(
long graph_index,
real_matrix4x3 *node_matrices,
short node_count,
short root,
real_matrix4x3 const *old_root,
real_matrix4x3 const *new_root)
{
real_matrix4x3 inverse;
real_matrix4x3 delta;
short node_index;
matrix4x3_inverse(old_root, &inverse);
matrix4x3_multiply(new_root, &inverse, &delta);
for (node_index = 0; node_index < node_count; node_index++)
{
short ancestor = animation_graph_node_parent(graph_index, node_index);
short steps;
/* parents come first; bounded in case of a malformed graph */
for (steps = 0; ancestor > 0 && ancestor != root && steps < node_count; steps++)
ancestor = animation_graph_node_parent(graph_index, ancestor);
if (ancestor == root && node_index != root)
{
real_matrix4x3 moved;
matrix4x3_multiply(&delta, &node_matrices[node_index], &moved);
node_matrices[node_index] = moved;
}
}
}
/* the upper arm turned about the elbow to point at the shoulder, keeping
its length (the free left arm: vr_left_hand_free) */
static void vr_point_upper_arm(
real_matrix4x3 *node_matrices,
short upper,
short fore,
real_point3d const *shoulder)
{
real_vector3d old_direction, new_direction;
real_point3d position;
real length;
vector_from_points3d(&node_matrices[upper].position, &node_matrices[fore].position, &old_direction);
vector_from_points3d(shoulder, &node_matrices[fore].position, &new_direction);
length = magnitude3d(&old_direction);
if (length < 1e-4f || normalize3d(&new_direction) < 1e-4f)
return;
point_from_line3d(&node_matrices[fore].position, &new_direction, -length, &position);
vr_turn_bone(&node_matrices[upper], &old_direction, &new_direction, &position);
}
/* one arm: upper arm and forearm from the shoulder to the wrist at target */
static void vr_arm(
real_matrix4x3 *node_matrices,
short upper,
short fore,
short wrist,
real_point3d shoulder,
real_point3d const *target,
real_vector3d const *pole)
{
real_vector3d old_upper, old_fore, reach, bend, new_upper, new_fore;
real upper_length, fore_length, distance, along, height;
real_point3d elbow;
vector_from_points3d(&node_matrices[upper].position, &node_matrices[fore].position, &old_upper);
vector_from_points3d(&node_matrices[fore].position, &node_matrices[wrist].position, &old_fore);
upper_length = magnitude3d(&old_upper);
fore_length = magnitude3d(&old_fore);
vector_from_points3d(&shoulder, target, &reach);
distance = normalize3d(&reach);
if (upper_length < 1e-4f || fore_length < 1e-4f || distance < 1e-4f)
return;
/* out of reach: the shoulder follows the hand (the arm stays whole) */
if (distance > (upper_length + fore_length) * 0.999f)
{
distance = (upper_length + fore_length) * 0.999f;
shoulder = *target;
point_from_line3d(target, &reach, -distance, &shoulder);
}
along = (upper_length * upper_length - fore_length * fore_length + distance * distance) / (2.f * distance);
height = upper_length * upper_length - along * along;
height = height > 0.f ? square_root(height) : 0.f;
bend = *pole;
vr_vector_scale_add(&bend, &reach, -dot_product3d(pole, &reach));
normalize3d(&bend);
point_from_line3d(&shoulder, &reach, along, &elbow);
point_from_line3d(&elbow, &bend, height, &elbow);
vector_from_points3d(&shoulder, &elbow, &new_upper);
vector_from_points3d(&elbow, target, &new_fore);
vr_turn_bone(&node_matrices[upper], &old_upper, &new_upper, &shoulder);
vr_turn_bone(&node_matrices[fore], &old_fore, &new_fore, &elbow);
}
/* an orthonormal frame from a direction and a second one across it */
static void vr_frame_from(
real_vector3d const *direction,
real_vector3d const *across,
real_vector3d frame[3])
{
frame[0] = *direction;
normalize3d(&frame[0]);
frame[1] = *across;
vr_vector_scale_add(&frame[1], &frame[0], -dot_product3d(across, &frame[0]));
normalize3d(&frame[1]);
cross_product3d(&frame[0], &frame[1], &frame[2]);
}
/* the free left hand: the wrist behind the controller's grip, the fingers
along the controller and the knuckles, little finger to index, up its
handle, as a hand holds it. The whole arm follows the hand as it is shaped
on the foregrip: bent to the shoulder instead (vr_arm), the arm's skin,
bound partly to the skeleton's root at the other hand, tears into ribbons
wherever the hand is far from where the animation has it. */
static void vr_left_hand_free(
long graph_index,
real_matrix4x3 *node_matrices,
short node_count,
float const hand_position[3],
float const hand_forward[3],
float const hand_up[3])
{
short wrist_index = vr_arm_nodes.wrist[0];
real_matrix4x3 old_wrist = node_matrices[wrist_index];
real_matrix4x3 *wrist = &node_matrices[wrist_index];
real_vector3d fingers, knuckles, controller_forward, controller_up;
real_vector3d from[3], to[3];
real_vector3d *axes[3];
short axis;
if (vr_arm_nodes.left_index == NONE || vr_arm_nodes.left_middle == NONE || vr_arm_nodes.left_pinky == NONE)
return;
vector_from_points3d(&old_wrist.position, &node_matrices[vr_arm_nodes.left_middle].position, &fingers);
vector_from_points3d(&node_matrices[vr_arm_nodes.left_pinky].position,
&node_matrices[vr_arm_nodes.left_index].position, &knuckles);
controller_forward.i = hand_forward[0];
controller_forward.j = hand_forward[1];
controller_forward.k = hand_forward[2];
controller_up.i = hand_up[0];
controller_up.j = hand_up[1];
controller_up.k = hand_up[2];
vr_frame_from(&fingers, &knuckles, from);
vr_frame_from(&controller_forward, &controller_up, to);
axes[0] = &wrist->forward;
axes[1] = &wrist->left;
axes[2] = &wrist->up;
for (axis = 0; axis < 3; axis++)
{
real_vector3d old_axis = *axes[axis];
real x = dot_product3d(&old_axis, &from[0]);
real y = dot_product3d(&old_axis, &from[1]);
real z = dot_product3d(&old_axis, &from[2]);
axes[axis]->i = to[0].i * x + to[1].i * y + to[2].i * z;
axes[axis]->j = to[0].j * x + to[1].j * y + to[2].j * z;
axes[axis]->k = to[0].k * x + to[1].k * y + to[2].k * z;
}
wrist->position.x = hand_position[0];
wrist->position.y = hand_position[1];
wrist->position.z = hand_position[2];
point_from_line3d(&wrist->position, &to[0], -0.07f / VR_METRES_PER_WORLD_UNIT, &wrist->position);
vr_carry_descendants(graph_index, node_matrices, node_count, wrist_index, &old_wrist, wrist);
/* the forearm and the upper arm with it, as they are on the foregrip */
{
real_matrix4x3 inverse, delta, moved;
short chain[2];
short link;
chain[0] = vr_arm_nodes.fore[0];
chain[1] = vr_arm_nodes.upper[0];
matrix4x3_inverse(&old_wrist, &inverse);
matrix4x3_multiply(wrist, &inverse, &delta);
for (link = 0; link < 2; link++)
{
if (chain[link] == NONE)
continue;
matrix4x3_multiply(&delta, &node_matrices[chain[link]], &moved);
node_matrices[chain[link]] = moved;
}
}
}
/* Through a melee attack the skeleton keeps the pose it had before, in the
hand: the attack's animation swings the gun before the camera and moves
both hands about it (the right one to the stock, the left one onto the
barrel), and the swing is the player's own. The pose is kept while the
weapon is held ready (store), and held through the attack (melee). */
static void vr_hold_pose(
real_matrix4x3 *node_matrices,
short node_count,
struct render_camera const *weapon_camera,
boolean store,
boolean melee)
{
real_matrix4x3 camera_matrix;
real_matrix4x3 inverse;
short node_index;
if (node_count > MAXIMUM_NODES_PER_ANIMATION)
return;
matrix4x3_from_point_and_vectors(&camera_matrix, &weapon_camera->position, &weapon_camera->forward,
&weapon_camera->up);
if (store)
{
matrix4x3_inverse(&camera_matrix, &inverse);
for (node_index = 0; node_index < node_count; node_index++)
matrix4x3_multiply(&inverse, &node_matrices[node_index], &vr_arm_nodes.idle_pose[node_index]);
vr_arm_nodes.idle_pose_count = node_count;
vr_arm_nodes.idle_pose_valid = TRUE;
}
else if (melee && vr_arm_nodes.idle_pose_valid && vr_arm_nodes.idle_pose_count == node_count)
{
for (node_index = 0; node_index < node_count; node_index++)
matrix4x3_multiply(&camera_matrix, &vr_arm_nodes.idle_pose[node_index], &node_matrices[node_index]);
}
}
/* While the animation turns the weapon away from where it aims (a reload,
drawing or putting it away), the laser follows the barrel: the gun node's
turn from its ready pose, applied to the laser at the ready. */
static void vr_laser_follows_barrel(
short local_player_index,
real_matrix4x3 const *node_matrices,
struct render_camera const *weapon_camera)
{
real_matrix4x3 camera_matrix, ready_gun, inverse, delta;
real_point3d start, moved_start;
real_vector3d direction, moved_direction;
float laser_start[3], laser_direction[3];
if (vr_arm_nodes.gun == NONE || !vr_arm_nodes.idle_pose_valid ||
!halo_vr_ready_laser(local_player_index, laser_start, laser_direction))
{
return;
}
matrix4x3_from_point_and_vectors(&camera_matrix, &weapon_camera->position, &weapon_camera->forward,
&weapon_camera->up);
matrix4x3_multiply(&camera_matrix, &vr_arm_nodes.idle_pose[vr_arm_nodes.gun], &ready_gun);
matrix4x3_inverse(&ready_gun, &inverse);
matrix4x3_multiply(&node_matrices[vr_arm_nodes.gun], &inverse, &delta);
start.x = laser_start[0];
start.y = laser_start[1];
start.z = laser_start[2];
direction.i = laser_direction[0];
direction.j = laser_direction[1];
direction.k = laser_direction[2];
matrix4x3_transform_point(&delta, &start, &moved_start);
matrix4x3_transform_vector(&delta, &direction, &moved_direction);
normalize3d(&moved_direction);
laser_start[0] = moved_start.x;
laser_start[1] = moved_start.y;
laser_start[2] = moved_start.z;
laser_direction[0] = moved_direction.i;
laser_direction[1] = moved_direction.j;
laser_direction[2] = moved_direction.k;
halo_vr_set_laser(local_player_index, laser_start, laser_direction);
}
static void first_person_weapon_vr_arms(
short local_player_index,
long graph_index,
real_matrix4x3 *node_matrices,
short node_count,
struct render_camera const *weapon_camera,
short state)
{
/* a grenade's throw is the player's own too */
boolean melee = state==_first_person_weapon_state_melee ||
state==_first_person_weapon_state_throw_grenade ||
state==_first_person_weapon_state_throw_grenade_overheated;
/* held ready: firing or not, the gun where it aims */
boolean ready = state==_first_person_weapon_state_idle ||
state==_first_person_weapon_state_primary_fire ||
state==_first_person_weapon_state_secondary_fire ||
state==_first_person_weapon_state_primary_misfire ||
state==_first_person_weapon_state_secondary_misfire;
static char const *const upper_names[2] = { "l upperarm", "r upperarm" };
static char const *const fore_names[2] = { "l forearm", "r forearm" };
static char const *const wrist_names[2] = { "l wriste", "r wriste" };
float head[3], body_forward[3];
real_vector3d forward, left, weapon_left, weapon_right;
real_point3d eye;
real_matrix4x3 animated_forearm[2];
real_matrix4x3 animated_wrist[2];
short side;
boolean gripping;
boolean left_free;
if (vr_arm_nodes.graph_index != graph_index)
{
vr_arm_nodes.graph_index = graph_index;
for (side = 0; side < 2; side++)
{
vr_arm_nodes.upper[side] = animation_graph_find_node(graph_index, upper_names[side]);
vr_arm_nodes.fore[side] = animation_graph_find_node(graph_index, fore_names[side]);
vr_arm_nodes.wrist[side] = animation_graph_find_node(graph_index, wrist_names[side]);
}
vr_arm_nodes.gun = animation_graph_find_node(graph_index, "gun");
vr_arm_nodes.left_index = animation_graph_find_node(graph_index, "l index low");
vr_arm_nodes.left_middle = animation_graph_find_node(graph_index, "l middlelow");
vr_arm_nodes.left_pinky = animation_graph_find_node(graph_index, "l pinky low");
vr_arm_nodes.idle_pose_valid = FALSE;
}
vr_hold_pose(node_matrices, node_count, weapon_camera, ready, melee);
if (vr_arm_nodes.gun != NONE && vr_arm_nodes.idle_pose_valid)
{
/* the gun at the ready, as drawn: where the weapon's shots and its
crosshair start (in a reload, where it was at the ready) */
real_matrix4x3 camera_matrix;
real_matrix4x3 const *ready_gun = &vr_arm_nodes.idle_pose[vr_arm_nodes.gun];
real_vector3d const *axes[3];
real_point3d gun;
float barrel[3], direction[3];
real best = 0.f;
short axis;
matrix4x3_from_point_and_vectors(&camera_matrix, &weapon_camera->position, &weapon_camera->forward,
&weapon_camera->up);
matrix4x3_transform_point(&camera_matrix, &ready_gun->position, &gun);
barrel[0] = gun.x;
barrel[1] = gun.y;
barrel[2] = gun.z;
/* the barrel: the gun node's axis nearest the camera's forward, in
the camera's frame (forward, left, up) */
axes[0] = &ready_gun->forward;
axes[1] = &ready_gun->left;
axes[2] = &ready_gun->up;
direction[0] = 1.f;
direction[1] = direction[2] = 0.f;
for (axis = 0; axis < 3; axis++)
{
real along = axes[axis]->i;
if (along * along > best * best)
{
real length = magnitude3d(axes[axis]);
real sign = along < 0.f ? -1.f : 1.f;
best = along;
if (length > 0.f)
{
direction[0] = sign * axes[axis]->i / length;
direction[1] = sign * axes[axis]->j / length;
direction[2] = sign * axes[axis]->k / length;
}
}
}
halo_vr_set_barrel(local_player_index, barrel, direction);
if (vr_arm_nodes.barrel_logged_graph_index != graph_index)
{
/* once per weapon (debug.txt) */
error(2, "VR barrel: %.1f degrees off the weapon camera; gun node %.2f %.2f %.2f m from it (forward, left, up)",
arccosine(PIN(direction[0], -1.f, 1.f)) * 180.f / _pi,
ready_gun->position.x * VR_METRES_PER_WORLD_UNIT, ready_gun->position.y * VR_METRES_PER_WORLD_UNIT,
ready_gun->position.z * VR_METRES_PER_WORLD_UNIT);
vr_arm_nodes.barrel_logged_graph_index = graph_index;
}
}
if (!ready && !melee)
vr_laser_follows_barrel(local_player_index, node_matrices, weapon_camera);
if (!halo_vr_body_frame(local_player_index, head, body_forward))
return;
eye.x = head[0];
eye.y = head[1];
eye.z = head[2];
forward.i = body_forward[0];
forward.j = body_forward[1];
forward.k = 0.f;
cross_product3d(global_up3d, &forward, &left);
cross_product3d(&weapon_camera->up, &weapon_camera->forward, &weapon_left);
weapon_right.i = -weapon_left.i;
weapon_right.j = -weapon_left.j;
weapon_right.k = -weapon_left.k;
/* the animated arms, for the forearms' twist */
for (side = 0; side < 2; side++)
{
if (vr_arm_nodes.fore[side] != NONE && vr_arm_nodes.wrist[side] != NONE)
{
animated_forearm[side] = node_matrices[vr_arm_nodes.fore[side]];
animated_wrist[side] = node_matrices[vr_arm_nodes.wrist[side]];
}
}
/* the left hand on the foregrip, or at the controller */
gripping = FALSE;
left_free = FALSE;
if (vr_arm_nodes.wrist[0] != NONE && vr_arm_nodes.wrist[1] != NONE)
{
real_matrix4x3 const *left_wrist = &node_matrices[vr_arm_nodes.wrist[0]];
real_matrix4x3 const *right_wrist = &node_matrices[vr_arm_nodes.wrist[1]];
real_vector3d between;
float foregrip[3], reference[3];
vector_from_points3d(&right_wrist->position, &left_wrist->position, &between);
foregrip[0] = left_wrist->position.x;
foregrip[1] = left_wrist->position.y;
foregrip[2] = left_wrist->position.z;
reference[0] = dot_product3d(&between, &weapon_camera->forward);
reference[1] = dot_product3d(&between, &weapon_right);
reference[2] = dot_product3d(&between, &weapon_camera->up);
gripping = halo_vr_two_hand(local_player_index, foregrip, reference);
}
if (!gripping && vr_arm_nodes.wrist[0] != NONE)
{
float hand_position[3], hand_forward[3], hand_up[3];
if (halo_vr_left_hand(local_player_index, hand_position, hand_forward, hand_up))
{
vr_left_hand_free(graph_index, node_matrices, node_count, hand_position, hand_forward, hand_up);
left_free = TRUE;
}
}
for (side = 0; side < 2; side++)
{
real_point3d shoulder;
real_vector3d pole;
real outward = side ? -1.f : 1.f; /* left is +left */
if (vr_arm_nodes.upper[side] == NONE || vr_arm_nodes.fore[side] == NONE || vr_arm_nodes.wrist[side] == NONE)
continue;
/* the shoulder: below the eyes, a little behind them, to the side */
shoulder = eye;
point_from_line3d(&shoulder, global_up3d, -0.25f / VR_METRES_PER_WORLD_UNIT, &shoulder);
point_from_line3d(&shoulder, &forward, -0.06f / VR_METRES_PER_WORLD_UNIT, &shoulder);
point_from_line3d(&shoulder, &left, outward * 0.19f / VR_METRES_PER_WORLD_UNIT, &shoulder);
/* the elbow bends down, out and back */
pole.i = -global_up3d->i;
pole.j = -global_up3d->j;
pole.k = -global_up3d->k;
vr_vector_scale_add(&pole, &left, outward * 0.6f);
vr_vector_scale_add(&pole, &forward, -0.3f);
if (!side && left_free)
{
/* the free left arm keeps its shape (vr_left_hand_free), but
for the upper arm, which points back at the shoulder */
vr_point_upper_arm(node_matrices, vr_arm_nodes.upper[side], vr_arm_nodes.fore[side], &shoulder);
continue;
}
vr_arm(node_matrices, vr_arm_nodes.upper[side], vr_arm_nodes.fore[side], vr_arm_nodes.wrist[side],
shoulder, &node_matrices[vr_arm_nodes.wrist[side]].position, &pole);
vr_twist_forearm(&node_matrices[vr_arm_nodes.fore[side]], &animated_forearm[side], &animated_wrist[side],
&node_matrices[vr_arm_nodes.wrist[side]], &node_matrices[vr_arm_nodes.wrist[side]].position);
}
}
#endif
static void first_person_weapon_build_node_matrices(
short local_player_index)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
struct render_camera const *camera= &render.camera;
#ifdef HALO_ANDROID
/* VR: posed from the right controller, so the weapon lies in the hand
(port/linux/src/vr.c) */
struct render_camera weapon_camera;
float weapon_position[3], weapon_forward[3], weapon_up[3];
if (first_person_weapon->weapon_index!=NONE)
{
/* once per skeleton: its nodes, for the arms (debug.txt) */
static long logged_graph_index= NONE;
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
long graph_index= weapon_definition_get(weapon->definition_index)->weapon.interface_definition.first_person_animations.index;
if (graph_index!=NONE && graph_index!=logged_graph_index)
{
short node_index;
logged_graph_index= graph_index;
for (node_index= 0; node_index<animation_graph_node_count(graph_index); node_index++)
{
error(2, "first-person node %d '%s' parent %d", node_index,
animation_graph_node_name(graph_index, node_index),
animation_graph_node_parent(graph_index, node_index));
}
}
}
if (halo_vr_weapon_camera(local_player_index, weapon_position, weapon_forward, weapon_up))
{
weapon_camera= render.camera;
weapon_camera.position.x= weapon_position[0];
weapon_camera.position.y= weapon_position[1];
weapon_camera.position.z= weapon_position[2];
weapon_camera.forward.i= weapon_forward[0];
weapon_camera.forward.j= weapon_forward[1];
weapon_camera.forward.k= weapon_forward[2];
weapon_camera.up.i= weapon_up[0];
weapon_camera.up.j= weapon_up[1];
weapon_camera.up.k= weapon_up[2];
camera= &weapon_camera;
}
#endif
if (!first_person_weapon->rendered)
{
euler_angles2d_from_vector3d(&first_person_weapon->render_facing, &camera->forward);
first_person_weapon->render_position= camera->position;
}
#ifdef HALO_LINUX
/* The native ports draw several frames per tick. The turning sway
(first_person_weapon_update) takes the facing change since the last
frame as a tick's worth: move the last facing on once a tick. */
{
static long last_render_ticks[MAXIMUM_LOCAL_PLAYERS] = { NONE, NONE, NONE, NONE };
if (!halo_interpolation_enabled() || last_render_ticks[local_player_index] != game_time_get())
{
first_person_weapon->last_render_facing= first_person_weapon->render_facing;
first_person_weapon->last_render_position= first_person_weapon->render_position;
last_render_ticks[local_player_index]= game_time_get();
}
}
#else
first_person_weapon->last_render_facing= first_person_weapon->render_facing;
first_person_weapon->last_render_position= first_person_weapon->render_position;
#endif
euler_angles2d_from_vector3d(&first_person_weapon->render_facing, &camera->forward);
first_person_weapon->render_position= camera->position;
first_person_weapon->render_forward= camera->forward;
first_person_weapon->rendered= TRUE;
if (first_person_weapon->weapon_index!=NONE &&
!weapon_try_and_get(first_person_weapon->weapon_index))
{
error(
_error_log,
"local player %d, weapon (0x%x), deleted unexpectedly",
local_player_index,
first_person_weapon->weapon_index);
first_person_weapon->weapon_index= NONE;
}
if (first_person_weapon->weapon_index!=NONE)
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
struct model *model= model_definition_get(
weapon_definition->weapon.interface_definition.first_person_model.index);
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 *first_person_weapon_animations=
TAG_BLOCK_GET_ELEMENT(
&animation_graph->first_person_weapon_animations,
0,
struct animation_graph_first_person_weapon_animations);
if (first_person_weapon_animations)
{
short firing_animation_index;
short overlay_animation_index;
if (first_person_weapon->state_animation.index!=NONE)
{
struct animation *state_animation= TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
first_person_weapon->state_animation.index,
struct animation);
animation_get_node_orientations(
NULL,
state_animation,
first_person_weapon->state_animation.frame_index,
first_person_weapon->node_orientations);
}
else
{
match_vassert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
1294,
FALSE,
NULL);
model_get_node_orientations(model, first_person_weapon->node_orientations);
}
firing_animation_index= VALID_INDEX(
_first_person_weapon_animation_ammunition,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_ammunition].animation_index :
(short)NONE;
if (firing_animation_index!=NONE)
{
struct animation *firing_animation= TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
firing_animation_index,
struct animation);
if (weapon_definition->weapon.weapon_type==_weapon_type_needler &&
(first_person_weapons->state==_first_person_weapon_state_reload_while_empty ||
first_person_weapons->state==_first_person_weapon_state_reload_while_full))
{
short frame_index= weapon->weapon.magazines[0].rounds_loaded;
short settle_ticks= weapon->weapon.magazines[0].original_time-
weapon->weapon.magazines[0].state_timer;
if (settle_ticks>=FIRST_PERSON_WEAPON_RELOAD_SETTLE_TICKS)
{
struct weapon_magazine_definition *magazine_definition= TAG_BLOCK_GET_ELEMENT(
&weapon_definition->weapon.magazines,
0,
struct weapon_magazine_definition);
real settle_fraction= (settle_ticks-FIRST_PERSON_WEAPON_RELOAD_SETTLE_TICKS)*0.2f;
short predicted_frame_index;
if (settle_fraction>1.0f)
{
settle_fraction= 1.0f;
}
predicted_frame_index= (short)MIN(
weapon->weapon.magazines[0].rounds_total,
magazine_definition->rounds_loaded_maximum);
frame_index= (short)((predicted_frame_index-frame_index)*settle_fraction)+
frame_index;
}
overlay_animation_apply(
firing_animation,
frame_index,
first_person_weapon->node_orientations);
}
else if (weapon->weapon.magazines[0].rounds_loaded<firing_animation->frame_count)
{
overlay_animation_apply(
firing_animation,
weapon->weapon.magazines[0].rounds_loaded,
first_person_weapon->node_orientations);
}
}
if (first_person_weapon->moving_animation.index!=NONE)
{
struct animation *moving_animation= TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
first_person_weapon->moving_animation.index,
struct animation);
overlay_animation_apply(
moving_animation,
first_person_weapon->moving_animation.frame_index,
first_person_weapon->node_orientations);
}
if (first_person_weapon->overcharged_jitter_animation.index!=NONE)
{
struct animation *overcharged_jitter_animation= TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
first_person_weapon->overcharged_jitter_animation.index,
struct animation);
overlay_animation_apply_continuous_scaled(
overcharged_jitter_animation,
first_person_weapon->overcharged_jitter_animation.frame_index,
weapon->weapon.overcharged+0.5f,
first_person_weapon->node_orientations);
}
overlay_animation_index= VALID_INDEX(
_first_person_weapon_animation_overlays,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_overlays].animation_index :
(short)NONE;
if (overlay_animation_index!=NONE)
{
struct animation *overlay_animation= TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
overlay_animation_index,
struct animation);
if (overlay_animation->frame_count>=NUMBER_OF_FIRST_PERSON_WEAPON_OVERLAY_FRAMES)
{
if (first_person_weapon->position.i>0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
0,
first_person_weapon->position.i,
first_person_weapon->node_orientations);
}
else if (first_person_weapon->position.i<0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
1,
-first_person_weapon->position.i,
first_person_weapon->node_orientations);
}
if (first_person_weapon->position.j>0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
3,
first_person_weapon->position.j,
first_person_weapon->node_orientations);
}
else if (first_person_weapon->position.j<0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
2,
-first_person_weapon->position.j,
first_person_weapon->node_orientations);
}
if (first_person_weapon->turning.i>0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
4,
first_person_weapon->turning.i,
first_person_weapon->node_orientations);
}
else if (first_person_weapon->turning.i<0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
5,
-first_person_weapon->turning.i,
first_person_weapon->node_orientations);
}
if (first_person_weapon->turning.j>0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
7,
first_person_weapon->turning.j,
first_person_weapon->node_orientations);
}
else if (first_person_weapon->turning.j<0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
6,
-first_person_weapon->turning.j,
first_person_weapon->node_orientations);
}
if (first_person_weapon->firing_push_back>0.0f)
{
overlay_animation_apply_scaled(
overlay_animation,
8,
first_person_weapon->firing_push_back,
first_person_weapon->node_orientations);
}
}
}
if (first_person_weapon->interpolation_frame_count>0)
{
interpolate_node_orientations(
(short)animation_graph->nodes.count,
first_person_weapon->original_node_orientations,
first_person_weapon->node_orientations,
first_person_weapon->interpolation_frame_index,
first_person_weapon->interpolation_frame_count);
}
}
}
animation_graph_node_matrices_from_orientations(
weapon_definition->weapon.interface_definition.first_person_animations.index,
first_person_weapon->node_matrices,
first_person_weapon->node_orientations,
&camera->position,
&camera->forward,
&camera->up);
#ifdef HALO_LINUX
/* the pose between the last two ticks (render_interpolation.c) */
render_interpolation_first_person(
local_player_index,
first_person_weapon->node_matrices,
(short)animation_graph->nodes.count,
camera);
#endif
#ifdef HALO_ANDROID
if (camera!=&render.camera)
{
first_person_weapon_vr_arms(
local_player_index,
weapon_definition->weapon.interface_definition.first_person_animations.index,
first_person_weapon->node_matrices,
(short)animation_graph->nodes.count,
camera,
first_person_weapon->state);
}
#endif
}
return;
}
static void first_person_weapon_message(
short local_player_index,
short message_type)
{
if (local_player_index!=NONE)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
short state= NONE;
switch (message_type)
{
case _first_person_weapon_message_drop:
first_person_weapon->weapon_index= NONE;
break;
case _first_person_weapon_message_ready:
first_person_weapon_switch_weapons(local_player_index);
break;
case _first_person_weapon_message_primary_fire:
first_person_weapon->firing_push_back_velocity+=
FIRST_PERSON_WEAPON_FIRING_PUSH_BACK_VELOCITY;
break;
case _first_person_weapon_message_reload_while_empty:
case _first_person_weapon_message_reload_while_full:
player_control_unzoom(first_person_weapon->unit_index);
break;
}
if (first_person_weapon->weapon_index!=NONE)
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
if (weapon->definition_index!=NONE)
{
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
switch (weapon_definition->weapon.weapon_type)
{
case _weapon_type_shotgun:
if (message_type==_first_person_weapon_message_reload_while_empty ||
message_type==_first_person_weapon_message_reload_while_full)
{
struct weapon_magazine_definition *magazine_definition= TAG_BLOCK_GET_ELEMENT(
&weapon_definition->weapon.magazines,
0,
struct weapon_magazine_definition);
short rounds_loaded= weapon->weapon.magazines[0].rounds_loaded;
short rounds_total= weapon->weapon.magazines[0].rounds_total;
if (first_person_weapon->state==_first_person_weapon_state_shotgun_enter_reload ||
first_person_weapon->state==_first_person_weapon_state_overheated_exit ||
first_person_weapon->state==_first_person_weapon_state_shotgun_exit_reload_empty ||
first_person_weapon->state==_first_person_weapon_state_shotgun_exit_reload_full ||
first_person_weapon->state==_first_person_weapon_state_reload_while_empty ||
first_person_weapon->state==_first_person_weapon_state_reload_while_full ||
weapon->weapon.magazines[0].state)
{
if (MIN(magazine_definition->rounds_loaded_maximum-rounds_loaded, rounds_total)==1)
{
first_person_weapon->shotgun_reload_type= _shotgun_reload_type_last_round;
}
else
{
first_person_weapon->shotgun_reload_type= NONE;
}
}
else
{
short shells_to_reload= (short)MIN(
magazine_definition->rounds_loaded_maximum-rounds_loaded,
rounds_total);
first_person_weapon->shotgun_shells_to_reload= shells_to_reload;
first_person_weapon->shotgun_empty= rounds_loaded==0;
first_person_weapon->shotgun_reload_type= (shells_to_reload!=1) ?
_shotgun_reload_type_first_round :
_shotgun_reload_type_first_and_last_round;
}
switch (first_person_weapon->shotgun_reload_type)
{
case _shotgun_reload_type_first_round:
case _shotgun_reload_type_first_and_last_round:
state= _first_person_weapon_state_shotgun_enter_reload;
break;
case NONE:
state= _first_person_weapon_state_reload_while_empty;
break;
}
}
break;
}
}
}
if (state==NONE)
{
state= (short)first_person_weapon_state_from_weapon_message(message_type);
}
if (state!=NONE)
{
first_person_weapon_set_state(local_player_index, state, TRUE);
}
switch (message_type)
{
case _first_person_weapon_message_ready:
first_person_weapon->interpolation_frame_count= 0;
break;
}
}
return;
}
static void first_person_weapon_switch_weapons(
short local_player_index)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
boolean visible= first_person_weapon->visible;
first_person_weapon->weapon_index= NONE;
if (visible)
{
first_person_weapon_set_visibility(local_player_index, FALSE);
}
if (first_person_weapon->unit_index!=NONE)
{
struct unit_datum *unit= unit_get(first_person_weapon->unit_index);
long weapon_index= unit_inventory_get_weapon(
first_person_weapon->unit_index,
unit->unit.current_weapon_index);
if (weapon_index!=NONE)
{
struct weapon_datum *weapon= weapon_get(weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
if (weapon_definition->weapon.interface_definition.first_person_model.index!=NONE &&
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 *first_person_weapon_animations=
TAG_BLOCK_GET_ELEMENT(
&animation_graph->first_person_weapon_animations,
0,
struct animation_graph_first_person_weapon_animations);
if (first_person_weapon_animations)
{
struct game_globals_first_person_interface *first_person_interface;
first_person_weapon->overlay_animation_index= NONE;
if (VALID_INDEX(
_first_person_weapon_animation_overlays,
first_person_weapon_animations->animations.count))
{
short overlay_animation_index= animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_overlays].animation_index;
if (overlay_animation_index!=NONE &&
TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
overlay_animation_index,
struct animation)->frame_count>=
NUMBER_OF_FIRST_PERSON_WEAPON_OVERLAY_FRAMES)
{
first_person_weapon->overlay_animation_index= overlay_animation_index;
}
}
first_person_interface= TAG_BLOCK_GET_ELEMENT(
&scenario_get_game_globals()->first_person_interface,
0,
struct game_globals_first_person_interface);
if (first_person_interface->hands.index!=NONE)
{
first_person_weapon->hands_node_remapping_table_valid=
model_build_remapping_table_for_animation_graph(
first_person_interface->hands.index,
weapon_definition->weapon.interface_definition.first_person_animations.index,
first_person_weapon->hands_node_remapping_table);
}
first_person_weapon->weapon_node_remapping_table_valid=
model_build_remapping_table_for_animation_graph(
weapon_definition->weapon.interface_definition.first_person_model.index,
weapon_definition->weapon.interface_definition.first_person_animations.index,
first_person_weapon->weapon_node_remapping_table);
if (first_person_weapon->weapon_node_remapping_table_valid &&
first_person_weapon->hands_node_remapping_table_valid)
{
first_person_weapon->weapon_index= weapon_index;
first_person_weapon->state= NONE;
first_person_weapon->state_animation.index= NONE;
first_person_weapon->moving_animation.index= NONE;
first_person_weapon->overcharged_jitter_animation.index= NONE;
first_person_weapon->firing_push_back= 0.0f;
first_person_weapon->firing_push_back_velocity= 0.0f;
first_person_weapon->ticks_idle= 0;
first_person_weapon->current_sound_index= NONE;
first_person_weapon->current_sound_state= NONE;
first_person_weapon_set_state(
local_player_index,
_first_person_weapon_state_idle,
TRUE);
first_person_weapon->interpolation_frame_count= 0;
if (visible)
{
first_person_weapon_set_visibility(local_player_index, TRUE);
}
}
}
}
}
}
}
first_person_weapon_predict(local_player_index);
return;
}
static void first_person_weapon_new_unit(
short local_player_index,
long unit_index)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
first_person_weapon->rendered= FALSE;
first_person_weapon->unit_index= unit_index;
first_person_weapon_switch_weapons(local_player_index);
return;
}
static void first_person_weapon_next_state(
short local_player_index)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
short new_state= NONE;
switch (first_person_weapon->state)
{
case _first_person_weapon_state_idle:
case _first_person_weapon_state_posing:
case _first_person_weapon_state_primary_fire:
case _first_person_weapon_state_secondary_fire:
case _first_person_weapon_state_primary_misfire:
case _first_person_weapon_state_secondary_misfire:
case _first_person_weapon_state_melee:
case _first_person_weapon_state_light_off:
case _first_person_weapon_state_light_on:
case _first_person_weapon_state_shotgun_exit_reload_empty:
case _first_person_weapon_state_shotgun_exit_reload_full:
case _first_person_weapon_state_ready:
case _first_person_weapon_state_throw_grenade:
case _first_person_weapon_state_overheated_exit:
new_state= _first_person_weapon_state_idle;
break;
case _first_person_weapon_state_overheating:
case _first_person_weapon_state_overheating_again:
case _first_person_weapon_state_throw_grenade_overheated:
case _first_person_weapon_state_overheating_super_recoil:
new_state= _first_person_weapon_state_overheated;
break;
case _first_person_weapon_state_put_away:
first_person_weapon->state_animation.frame_index--;
break;
case _first_person_weapon_state_shotgun_enter_reload:
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
if (weapon_definition->weapon.weapon_type==_weapon_type_shotgun &&
first_person_weapon->shotgun_reload_type==_shotgun_reload_type_first_and_last_round)
{
new_state= first_person_weapon->shotgun_empty ?
_first_person_weapon_state_shotgun_exit_reload_empty :
_first_person_weapon_state_shotgun_exit_reload_full;
}
else
{
new_state= _first_person_weapon_state_idle;
}
break;
}
case _first_person_weapon_state_reload_while_empty:
case _first_person_weapon_state_reload_while_full:
{
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
if (weapon_definition->weapon.weapon_type!=_weapon_type_shotgun ||
!first_person_weapon->shotgun_reload_type ||
first_person_weapon->shotgun_reload_type==NONE)
{
new_state= _first_person_weapon_state_idle;
}
else
{
match_assert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
799,
first_person_weapon->shotgun_reload_type == _shotgun_reload_type_first_and_last_round ||
first_person_weapon->shotgun_reload_type == _shotgun_reload_type_last_round);
new_state= first_person_weapon->shotgun_empty ?
_first_person_weapon_state_shotgun_exit_reload_empty :
_first_person_weapon_state_shotgun_exit_reload_full;
}
break;
}
}
if (new_state!=NONE)
{
first_person_weapon_set_state(local_player_index, new_state, FALSE);
}
return;
}
static void first_person_weapon_update(
short local_player_index)
{
struct first_person_weapon *first_person_weapon= first_person_weapon_get(local_player_index);
if (first_person_weapon->weapon_index!=NONE &&
!weapon_try_and_get(first_person_weapon->weapon_index))
{
error(
_error_log,
"local player %d, weapon (0x%x), deleted unexpectedly",
local_player_index,
first_person_weapon->weapon_index);
first_person_weapon->weapon_index= NONE;
}
if (first_person_weapon->unit_index!=NONE && first_person_weapon->weapon_index!=NONE)
{
struct unit_datum *unit= unit_get(first_person_weapon->unit_index);
struct weapon_datum *weapon= weapon_get(first_person_weapon->weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
struct animation_graph *animation_graph;
long sound_definition_index;
short animation_update_result;
boolean moving;
model_definition_get(weapon_definition->weapon.interface_definition.first_person_model.index);
animation_graph= animation_graph_definition_get(
weapon_definition->weapon.interface_definition.first_person_animations.index);
if (first_person_weapon->state==_first_person_weapon_state_overheated ||
first_person_weapon->state==_first_person_weapon_state_overheating)
{
if (TEST_FLAG(weapon->weapon.flags, _weapon_overheated_exit_bit))
{
first_person_weapon_set_state(
local_player_index,
_first_person_weapon_state_overheated_exit,
TRUE);
}
if (!TEST_FLAG(weapon->weapon.flags, _weapon_overheated_bit))
{
first_person_weapon_set_state(
local_player_index,
_first_person_weapon_state_idle,
TRUE);
}
}
animation_update_result= animation_update_render_only(
weapon_definition->weapon.interface_definition.first_person_animations.index,
&first_person_weapon->state_animation,
&sound_definition_index);
if (animation_update_result==_animation_key_frame)
{
/* a key frame does not advance the first-person weapon state */
}
else if (animation_update_result==_animation_will_restart_on_next_frame)
{
first_person_weapon_next_state(local_player_index);
}
if (sound_definition_index!=NONE &&
director_get_perspective(local_player_index)==_director_perspective_first_person)
{
first_person_weapon->current_sound_index= object_impulse_sound_new(
first_person_weapon->weapon_index,
sound_definition_index,
NONE,
global_origin3d,
global_forward3d,
1.0f);
first_person_weapon->current_sound_state= first_person_weapon->state;
}
moving= magnitude3d(&unit->unit.throttle)>0.1f;
if (unit_flying_through_air(first_person_weapon->unit_index))
{
moving= FALSE;
}
if (first_person_weapon->moving_animation.index!=NONE)
{
animation_update_render_only(
weapon_definition->weapon.interface_definition.first_person_animations.index,
&first_person_weapon->moving_animation,
NULL);
if (!moving)
{
if (first_person_weapon->state==_first_person_weapon_state_idle)
{
first_person_weapon_start_interpolation(local_player_index, 6);
}
first_person_weapon->moving_animation.index= NONE;
}
}
else if (moving)
{
struct animation_graph_first_person_weapon_animations *first_person_weapon_animations=
!animation_graph->first_person_weapon_animations.count ?
NULL :
TAG_BLOCK_GET_ELEMENT(
&animation_graph->first_person_weapon_animations,
0,
struct animation_graph_first_person_weapon_animations);
first_person_weapon->moving_animation.frame_index= 0;
/* no NULL test (January, and the later first-party build at /Od 0x61fcea): a non-NONE
* weapon_index is only committed by first_person_weapon_switch_weapons inside
* `if (animation_graph->first_person_weapon_animations.count)` for this same graph. The
* new-map reset leaves weapon_index 0 but unit_index NONE, and first_person_weapon_new_unit
* runs switch_weapons (which stores NONE first) as soon as it sets unit_index, so the NULL
* arm above cannot reach this read. */
first_person_weapon->moving_animation.index=
VALID_INDEX(
_first_person_weapon_animation_moving,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_moving].animation_index :
(short)NONE;
}
if (first_person_weapon->overcharged_jitter_animation.index==NONE)
{
if (first_person_weapon->state==_first_person_weapon_state_charged)
{
struct animation_graph_first_person_weapon_animations *first_person_weapon_animations=
!animation_graph->first_person_weapon_animations.count ?
NULL :
TAG_BLOCK_GET_ELEMENT(
&animation_graph->first_person_weapon_animations,
0,
struct animation_graph_first_person_weapon_animations);
first_person_weapon->overcharged_jitter_animation.frame_index= 0.0f;
/* no NULL test: the same switch_weapons invariant as the moving animation above
* (January and /Od 0x61fd96). */
first_person_weapon->overcharged_jitter_animation.index=
VALID_INDEX(
_first_person_weapon_animation_overcharged_jitter,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_overcharged_jitter].animation_index :
(short)NONE;
}
}
else if (first_person_weapon->state==_first_person_weapon_state_charged)
{
struct animation *animation= TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
first_person_weapon->overcharged_jitter_animation.index,
struct animation);
first_person_weapon->overcharged_jitter_animation.frame_index= (real)fmod(
(weapon->weapon.overcharged+1.0f)*2.0f+
first_person_weapon->overcharged_jitter_animation.frame_index,
(real)animation->frame_count);
}
else
{
first_person_weapon->overcharged_jitter_animation.index= NONE;
}
if (first_person_weapon->rendered)
{
real_vector2d turning;
accelerate_to_position(
&first_person_weapon->position.i,
&first_person_weapon->position_velocity.i,
unit->unit.throttle.i,
0.08f,
0.5f,
-1.0f,
1.0f,
FALSE);
accelerate_to_position(
&first_person_weapon->position.j,
&first_person_weapon->position_velocity.j,
unit->unit.throttle.j,
0.08f,
0.5f,
-1.0f,
1.0f,
FALSE);
turning.i= signed_angular_difference(
first_person_weapon->last_render_facing.yaw,
first_person_weapon->render_facing.yaw)*30.0f;
turning.j= signed_angular_difference(
first_person_weapon->last_render_facing.pitch,
first_person_weapon->render_facing.pitch)*-30.0f;
turning.i= PIN(turning.i, -1.0f, 1.0f);
turning.j= PIN(turning.j, -1.0f, 1.0f);
accelerate_to_position(
&first_person_weapon->turning.i,
&first_person_weapon->turning_velocity.i,
turning.i,
0.03f,
0.2f,
-1.0f,
1.0f,
FALSE);
accelerate_to_position(
&first_person_weapon->turning.j,
&first_person_weapon->turning_velocity.j,
turning.j,
0.03f,
0.2f,
-1.0f,
1.0f,
FALSE);
}
accelerate_to_position(
&first_person_weapon->firing_push_back,
&first_person_weapon->firing_push_back_velocity,
0.0f,
0.01f,
0.2f,
0.0f,
1.0f,
FALSE);
if (first_person_weapon->firing_push_back==1.0f)
{
first_person_weapon->firing_push_back_velocity= 0.0f;
}
if (first_person_weapon->interpolation_frame_count>0)
{
first_person_weapon->interpolation_frame_index++;
if (first_person_weapon->interpolation_frame_index>=
first_person_weapon->interpolation_frame_count)
{
first_person_weapon->interpolation_frame_count= 0;
}
}
if (player_control_get_autoaim_level(local_player_index)!=0.0f ||
player_control_get_zoom_level(local_player_index)!=NONE ||
first_person_weapon->firing_push_back!=0.0f ||
first_person_weapon->position.i!=0.0f ||
first_person_weapon->position.j!=0.0f ||
first_person_weapon->turning.i!=0.0f ||
first_person_weapon->turning.j!=0.0f)
{
first_person_weapon->ticks_idle= 0;
if (first_person_weapon->state==_first_person_weapon_state_posing)
{
first_person_weapon_set_state(
local_player_index,
_first_person_weapon_state_idle,
TRUE);
}
}
else if (first_person_weapon->state==_first_person_weapon_state_idle)
{
struct game_globals_player_information *player_information= TAG_BLOCK_GET_ELEMENT(
&scenario_get_game_globals()->player_information,
0,
struct game_globals_player_information);
if (!first_person_weapon->ticks_until_pose)
{
first_person_weapon->ticks_until_pose= (short)(real_local_random_range(
player_information->first_person_idle_time_lower_bound,
player_information->first_person_idle_time_upper_bound)*TICKS_PER_SECOND);
}
first_person_weapon->ticks_idle++;
if (first_person_weapon->ticks_idle>first_person_weapon->ticks_until_pose)
{
first_person_weapon->ticks_until_pose= 0;
if (real_local_random()>=player_information->first_person_idle_skip_fraction)
{
first_person_weapon_set_state(
local_player_index,
_first_person_weapon_state_posing,
TRUE);
}
}
}
else
{
first_person_weapon->ticks_idle= 0;
}
}
first_person_weapon->ticks_until_predict--;
if (first_person_weapon->ticks_until_predict<=0)
{
first_person_weapon_predict(local_player_index);
}
return;
}
static boolean model_build_remapping_table_for_animation_graph(
long model_index,
long animation_graph_index,
short *node_remapping_table)
{
struct model *model= model_definition_get(model_index);
struct animation_graph *animation_graph= animation_graph_definition_get(animation_graph_index);
boolean valid= TRUE;
short model_node_index;
for (model_node_index=0; model_node_index<model->nodes.count; model_node_index++)
{
struct model_node *model_node= TAG_BLOCK_GET_ELEMENT(
&model->nodes,
model_node_index,
struct model_node);
short animation_graph_node_index= NONE;
short node_index;
for (node_index=0; node_index<animation_graph->nodes.count; node_index++)
{
struct animation_graph_node *animation_graph_node= TAG_BLOCK_GET_ELEMENT(
&animation_graph->nodes,
node_index,
struct animation_graph_node);
if (!csstrcmp(model_node->name, animation_graph_node->name))
{
animation_graph_node_index= node_index;
break;
}
}
if (animation_graph_node_index!=NONE)
{
node_remapping_table[model_node_index]= animation_graph_node_index;
}
else
{
valid= FALSE;
}
}
return valid;
}
static void weapon_play_first_person_weapon_sound(
long weapon_index,
short message_type)
{
if (weapon_index!=NONE && message_type!=NONE && weapon_try_and_get(weapon_index))
{
struct weapon_datum *weapon= weapon_get(weapon_index);
struct weapon_definition *weapon_definition= weapon_definition_get(weapon->definition_index);
if (weapon_definition->weapon.interface_definition.first_person_animations.index!=NONE)
{
short state= (short)first_person_weapon_state_from_weapon_message(message_type);
if (state!=NONE)
{
short animation_type= (short)first_person_animation_type_from_weapon_state(state);
if (animation_type!=NONE)
{
struct animation_graph *animation_graph= animation_graph_definition_get(
weapon_definition->weapon.interface_definition.first_person_animations.index);
struct animation_graph_first_person_weapon_animations *first_person_weapon_animations=
!animation_graph->first_person_weapon_animations.count ?
NULL :
TAG_BLOCK_GET_ELEMENT(
&animation_graph->first_person_weapon_animations,
0,
struct animation_graph_first_person_weapon_animations);
short animation_index= (animation_type>=0 &&
animation_type<first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[animation_type].animation_index :
(short)NONE;
if (animation_index!=NONE)
{
short sound_index= TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
animation_index,
struct animation)->sound_index;
if (sound_index!=NONE)
{
long definition_index= TAG_BLOCK_GET_ELEMENT(
&animation_graph->sound_references,
sound_index,
struct animation_graph_sound_reference)->sound.index;
if (definition_index!=NONE)
{
object_impulse_sound_new(
weapon_index,
definition_index,
NONE,
global_origin3d,
global_forward3d,
1.0f);
}
}
}
}
}
}
}
return;
}
static long first_person_weapon_state_from_weapon_message(
short message_type)
{
switch (message_type)
{
case _first_person_weapon_message_primary_fire:
return _first_person_weapon_state_primary_fire;
case _first_person_weapon_message_secondary_fire:
return _first_person_weapon_state_secondary_fire;
case _first_person_weapon_message_primary_misfire:
return _first_person_weapon_state_primary_misfire;
case _first_person_weapon_message_secondary_misfire:
return _first_person_weapon_state_secondary_misfire;
case _first_person_weapon_message_melee:
return _first_person_weapon_state_melee;
case _first_person_weapon_message_light_on:
return _first_person_weapon_state_light_on;
case _first_person_weapon_message_light_off:
return _first_person_weapon_state_light_off;
case _first_person_weapon_message_reload_while_empty:
return _first_person_weapon_state_reload_while_empty;
case _first_person_weapon_message_reload_while_full:
return _first_person_weapon_state_reload_while_full;
case _first_person_weapon_message_put_away:
return _first_person_weapon_state_put_away;
case _first_person_weapon_message_ready:
return _first_person_weapon_state_ready;
case _first_person_weapon_message_charged:
return _first_person_weapon_state_charged;
case _first_person_weapon_message_overheating:
return _first_person_weapon_state_overheating;
case _first_person_weapon_message_throw_grenade:
return _first_person_weapon_state_throw_grenade;
case _first_person_weapon_message_overheating_super_recoil:
return _first_person_weapon_state_overheating_super_recoil;
}
return NONE;
}
static long first_person_animation_type_from_weapon_state(
short state)
{
switch (state)
{
case _first_person_weapon_state_idle:
return _first_person_weapon_animation_idle;
case _first_person_weapon_state_overheated:
return _first_person_weapon_animation_overheated;
case _first_person_weapon_state_charged:
return _first_person_weapon_animation_overcharged;
case _first_person_weapon_state_posing:
return _first_person_weapon_animation_posing;
case _first_person_weapon_state_primary_fire:
return _first_person_weapon_animation_primary_fire;
case _first_person_weapon_state_secondary_fire:
return _first_person_weapon_animation_secondary_fire;
case _first_person_weapon_state_primary_misfire:
return _first_person_weapon_animation_primary_misfire;
case _first_person_weapon_state_secondary_misfire:
return _first_person_weapon_animation_secondary_misfire;
case _first_person_weapon_state_melee:
return _first_person_weapon_animation_melee;
case _first_person_weapon_state_light_on:
return _first_person_weapon_animation_light_on;
case _first_person_weapon_state_light_off:
return _first_person_weapon_animation_light_off;
case _first_person_weapon_state_reload_while_empty:
return _first_person_weapon_animation_reload_while_empty;
case _first_person_weapon_state_reload_while_full:
return _first_person_weapon_animation_reload_while_full;
case _first_person_weapon_state_put_away:
return _first_person_weapon_animation_put_away;
case _first_person_weapon_state_ready:
return _first_person_weapon_animation_ready;
case _first_person_weapon_state_throw_grenade:
return _first_person_weapon_animation_throw_grenade;
case _first_person_weapon_state_throw_grenade_overheated:
return _first_person_weapon_animation_throw_grenade_overheated;
case _first_person_weapon_state_overheating:
return _first_person_weapon_animation_overheating;
case _first_person_weapon_state_overheating_again:
return _first_person_weapon_animation_overheating_again;
case _first_person_weapon_state_shotgun_enter_reload:
return _first_person_weapon_animation_shotgun_enter;
case _first_person_weapon_state_shotgun_exit_reload_empty:
return _first_person_weapon_animation_shotgun_exit_empty;
case _first_person_weapon_state_shotgun_exit_reload_full:
return _first_person_weapon_animation_shotgun_exit_full;
case _first_person_weapon_state_overheated_exit:
return _first_person_weapon_animation_overheated_exit;
case _first_person_weapon_state_overheating_super_recoil:
return _first_person_weapon_animation_overheated_supercharge_enter;
}
return NONE;
}
static void model_remap_node_matrices_to_match_animation_graph(
long model_index,
struct real_matrix4x3 *model_node_matrices,
long animation_graph_index,
struct real_matrix4x3 const *node_matrices,
short const *node_remapping_table)
{
struct model *model= model_definition_get(model_index);
struct animation_graph *animation_graph= animation_graph_definition_get(animation_graph_index);
short node_index;
for (node_index=0; node_index<model->nodes.count; node_index++)
{
short animation_graph_node_index= node_remapping_table[node_index];
match_assert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
1587,
animation_graph_node_index>=0 && animation_graph_node_index<animation_graph->nodes.count);
model_node_matrices[node_index]= node_matrices[animation_graph_node_index];
}
return;
}
static struct first_person_weapon *first_person_weapon_get(
short local_player_index)
{
match_assert(
"c:\\halo\\SOURCE\\interface\\first_person_weapons.c",
1433,
local_player_index>=0 && local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS);
return &first_person_weapons[local_player_index];
}
static short first_person_weapon_index_from_weapon_index(
long weapon_index)
{
short local_player_index;
for (local_player_index=0; local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS; local_player_index++)
{
long player_index= local_player_get_player_index(local_player_index);
if (player_index!=NONE)
{
long unit_index= player_get(player_index)->unit_index;
if (unit_index!=NONE)
{
struct unit_datum *unit= unit_get(unit_index);
if (unit->unit.current_weapon_index!=NONE &&
weapon_index==unit->unit.weapon_object_indices[unit->unit.current_weapon_index])
{
return local_player_index;
}
}
}
}
return NONE;
}
static short first_person_weapon_index_from_unit_index(
long unit_index)
{
short local_player_index;
for (local_player_index=0; local_player_index<MAXIMUM_NUMBER_OF_LOCAL_PLAYERS; local_player_index++)
{
long player_index= local_player_get_player_index(local_player_index);
if (player_index!=NONE && player_get(player_index)->unit_index==unit_index)
{
return local_player_index;
}
}
return NONE;
}