mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
Follow-up to #7635. Pass the firing bank explicitly through weapon_create() and beam_fire() instead of searching the ship's banks by weapon class, which could attribute substitution patterns to the wrong bank. Factor the substitution logic into get_weapon_substitution_tuple() and only apply linked/dual-fire pairing to ship-level fire, not turret fire. Skip substitution on multiplayer clients, since the server already resolved it. Derive turret bank indices from the weapon slot being fired rather than the turret's bank cursor, so ammo and substitution state are tracked on the bank that actually fired. Previously a single-selection turret could consume ammo from a bank it never fired, and non-ammo turrets (whose cursors stay at -1) would have skipped substitution entirely. Use the Player_ship object flag rather than Player_obj in ship_secondary_bank_can_dual_fire() so the server and clients agree on dual fire for remote players, and guard ship_secondary_has_ammo() against a negative bank index. Co-Authored-By: Claude Fable 5.1 <[email protected]>
314 lines
12 KiB
C++
314 lines
12 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __FS2_BEAM_WEAPON_HEADER_FILE
|
|
#define __FS2_BEAM_WEAPON_HEADER_FILE
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// BEAM WEAPON DEFINES/VARS
|
|
//
|
|
#include "globalincs/globals.h"
|
|
#include "model/model.h"
|
|
#include "utils/modular_curves.h"
|
|
|
|
// prototypes
|
|
class object;
|
|
class ship_subsys;
|
|
struct obj_pair;
|
|
struct beam_weapon_info;
|
|
struct vec3d;
|
|
|
|
enum class WeaponState : uint32_t
|
|
{
|
|
INVALID,
|
|
|
|
// States for laser weapons
|
|
NORMAL, //!< For laser weapons that have only one state
|
|
|
|
// Missile states following
|
|
FREEFLIGHT, //!< The initial flight state where the missile is "unpowered"
|
|
IGNITION, //!< The moment when the missile comes out of free flight
|
|
HOMED_FLIGHT, //!< The missile is homing in on its target
|
|
UNHOMED_FLIGHT, //!< The missile does not currently target an object
|
|
|
|
// Beam states following
|
|
WARMUP, //!< The beam is charging up
|
|
FIRING, //!< The beam is firing, and there's a beam and everything
|
|
PAUSED, //!< The beam is technically firing, but it's in between bursts
|
|
WARMDOWN, //!< The beam is cooling down
|
|
};
|
|
struct WeaponStateHash {
|
|
size_t operator()(const WeaponState& state) const {
|
|
return static_cast<size_t>(state);
|
|
}
|
|
};
|
|
|
|
typedef enum class BeamType {
|
|
DIRECT_FIRE, // unidirectional beam; used to be BEAM_TYPE_A
|
|
SLASHING, // "slash" in one direction; used to be BEAM_TYPE_B
|
|
TARGETING, // targeting lasers (only lasts one frame); used to be BEAM_TYPE_C
|
|
ANTIFIGHTER, // similar to the type A beams, but takes multiple shots and "chases" fighters around; used to be BEAM_TYPE_D
|
|
NORMAL_FIRE, // stupid beam. like type A, only it doesn't aim. it just shoots directly out of the turret; used to be BEAM_TYPE_E
|
|
OMNI // SCP type, highly flexible and configurable; used to be BEAM_TYPE_F
|
|
} BeamType;
|
|
|
|
// max # of "shots" an individual beam will take
|
|
#define MAX_BEAM_SHOTS 5
|
|
#define MAX_BEAMS 500
|
|
|
|
// apply damage
|
|
#define BEAM_DAMAGE_TIME 170
|
|
|
|
// uses to define beam behavior ahead of time - needed for multiplayer
|
|
typedef struct beam_info {
|
|
vec3d dir_a, dir_b; // direction vectors for beams
|
|
vec3d rot_axis;
|
|
ubyte shot_count; // # of shots
|
|
float shot_aim[MAX_BEAM_SHOTS]; // accuracy. this is a constant multiple of radius. anything < 1.0 will guarantee a hit
|
|
} beam_info;
|
|
|
|
#define BFIF_IS_FIGHTER_BEAM (1<<0)
|
|
#define BFIF_FORCE_FIRING (1<<1)
|
|
#define BFIF_TARGETING_COORDS (1<<2)
|
|
#define BFIF_FLOATING_BEAM (1<<3)
|
|
|
|
// to ensure validity of fire_info, the related fields MUST be provided
|
|
#define BFM_TURRET_FIRED 0 // objp, subsys, target
|
|
#define BFM_TURRET_FORCE_FIRED 1 // objp, subsys, target OR target_pos
|
|
#define BFM_FIGHTER_FIRED 2 // objp, subsys
|
|
#define BFM_SPAWNED 3 // starting pos, target OR target_pos
|
|
#define BFM_SEXP_FLOATING_FIRED 4 // starting pos, target OR target_pos
|
|
#define BFM_SUBSPACE_STRIKE 5 // starting pos, target
|
|
|
|
// pass to beam fire
|
|
typedef struct beam_fire_info {
|
|
int beam_info_index; // weapon info index
|
|
object *shooter; // whos shooting
|
|
vec3d local_fire_postion; // offset from the center of the object (for fighter beams only)
|
|
ship_subsys *turret; // where he's shooting from
|
|
float accuracy; // 0.0 to 1.0 (only really effects targeting on small ships)
|
|
object *target; // who's getting shot
|
|
ship_subsys *target_subsys; // (optional), specific subsystem to be targeted on the target
|
|
vec3d target_pos1; // if we're shooting off into space
|
|
vec3d target_pos2; // if we're shooting off into space (optional second point)
|
|
vec3d starting_pos; // starting positiong for floating beams -MageKing17
|
|
beam_info *beam_info_override; // (optional), pass this in to override all beam movement info (for multiplayer)
|
|
int num_shots; // (optional), only used for type D weapons
|
|
int bank; // which primary bank the beam is in (for fighters and turrets)
|
|
int point; // for fighters, which point on the bank it is from
|
|
int bfi_flags;
|
|
char team; // for floating beams, determines which team the beam is on
|
|
int burst_seed; // used for sharing random targets if part of the same burst
|
|
int fire_method;
|
|
float per_burst_rotation; // for type 5 beams
|
|
int burst_index;
|
|
} beam_fire_info;
|
|
|
|
typedef struct fighter_beam_fire_info {
|
|
int beam_info_index; // weapon info index
|
|
object *shooter; // whos shooting
|
|
vec3d local_fire_postion; // offset from the center of the object (for fighter beams only)
|
|
ship_subsys *turret; // where he's shooting from
|
|
float accuracy; // 0.0 to 1.0 (only really effects targeting on small ships)
|
|
object *target; // whos getting shot
|
|
ship_subsys *target_subsys; // (optional), specific subsystem to be targeted on the target
|
|
beam_info *beam_info_override; // (optional), pass this in to override all beam movement info (for multiplayer)
|
|
int num_shots; // (optional), only used for type D weapons
|
|
int fighter_beam_loop_sound; //loop sound used by fighter beams -Bobboau
|
|
int warmup_stamp;
|
|
int warmdown_stamp;
|
|
float life_left;
|
|
float life_total;
|
|
} fighter_beam_fire_info;
|
|
|
|
// max # of collisions we'll allow per frame
|
|
#define MAX_FRAME_COLLISIONS 10
|
|
|
|
// collision info
|
|
typedef struct beam_collision {
|
|
mc_info cinfo; // collision info
|
|
int c_objnum; // objnum of the guy we recently collided with
|
|
int c_sig; // object sig
|
|
int c_stamp; // when we should next apply damage
|
|
int quadrant; // shield quadrant this beam hits if any -Bobboau
|
|
bool is_exit_collision; //does this occur when the beam is exiting the ship
|
|
} beam_collision;
|
|
|
|
// beam flag defines
|
|
#define BF_SAFETY (1<<0) // if this is set, don't collide or render for this frame. lifetime still increases though
|
|
#define BF_SHRINK (1<<1) // if this is set, the beam is shrinking in width and approaching the warmdown phase
|
|
#define BF_FORCE_FIRING (1<<2)
|
|
#define BF_IS_FIGHTER_BEAM (1<<3)
|
|
#define BF_TARGETING_COORDS (1<<4)
|
|
#define BF_FLOATING_BEAM (1<<5)
|
|
#define BF_GROW (1<<6) // if this is set, the beam is growing in width
|
|
#define BF_FINISHED_GROWING (1<<7) // to stop grow from re-triggering
|
|
|
|
// beam struct (the actual weapon/object)
|
|
typedef struct beam {
|
|
// low-level data
|
|
int objnum; // our own objnum
|
|
int weapon_info_index;
|
|
int sig; // signature for the shooting object
|
|
object *objp; // the shooting object (who owns the turret that I am being fired from)
|
|
object *target; // target object
|
|
ship_subsys *target_subsys; // targeted subsys
|
|
vec3d target_pos1; // if we're targeting a location in space
|
|
vec3d target_pos2; // if we're targeting a location in space (optional second point)
|
|
int target_sig; // target sig
|
|
ship_subsys *subsys; // subsys its being fired from
|
|
beam *next, *prev; // link list stuff
|
|
vec3d local_fire_postion; // offset from the center of the object (for fighter beams only)
|
|
int framecount; // how many frames the beam has been active
|
|
int flags; // see BF_* defines
|
|
float current_width_factor; // applied to width due to shrink or grow
|
|
|
|
// beam info
|
|
int warmup_stamp; // timestamp for "warming up"
|
|
int warmdown_stamp; // timestamp for "warming down"
|
|
BeamType type; // the type of beam, direct-fire, slashing, etc
|
|
float life_left; // in seconds
|
|
float life_total; // total life
|
|
// this vector has very special meaning. BEFORE performing collision checks, it basically implies a "direction". meaning
|
|
// the vector between it and last_start is where the beam will be aiming. AFTER performing collision checks, it is the
|
|
// literal world collision point on the object (or meaningless, if we hit nothing). The function beam_move_all_pre() is
|
|
// responsible for then setting it up pre-collision time
|
|
vec3d last_shot;
|
|
vec3d last_start;
|
|
int shot_index; // for type D beam weapons
|
|
float beam_glow_frame; // what frame a beam glow animation is on
|
|
float beam_section_frame[MAX_BEAM_SECTIONS]; // what frame a beam section animation is on
|
|
|
|
// recent collisions
|
|
beam_collision r_collisions[MAX_FRAME_COLLISIONS]; // recent collisions
|
|
int r_collision_count; // # of recent collisions
|
|
|
|
// collision info for this frame
|
|
beam_collision f_collisions[MAX_FRAME_COLLISIONS]; // collisions for the current frame
|
|
int f_collision_count; // # of collisions we recorded this frame
|
|
|
|
// looping sound info, HANDLE
|
|
sound_handle beam_sound_loop; // invalid if none
|
|
|
|
// team
|
|
char team;
|
|
|
|
float range;
|
|
float damage_threshold;
|
|
|
|
// exactly how the beam will behave. by passing this is multiplayer from server to client, we can ensure that
|
|
// everything looks the same
|
|
beam_info binfo;
|
|
int bank;
|
|
|
|
int firingpoint;
|
|
float beam_collide_width;
|
|
float beam_light_width;
|
|
|
|
float u_offset_local;
|
|
|
|
bool rotates; // type 5s only, determines whether it rotates
|
|
float type5_rot_speed; // how fast it rotates if it does
|
|
|
|
WeaponState weapon_state;
|
|
|
|
modular_curves_entry_instance modular_curves_instance;
|
|
} beam;
|
|
|
|
extern std::array<beam, MAX_BEAMS> Beams; // all beams
|
|
extern int Beam_count;
|
|
|
|
#define BEAM_INDEX(beam) (static_cast<int>((beam)-Beams.data()))
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// BEAM WEAPON FUNCTIONS
|
|
//
|
|
|
|
// ---------------
|
|
// the next functions are probably the only ones anyone should care about calling. the rest require somewhat detailed knowledge of beam weapons
|
|
|
|
// fire a beam, returns objnum on success. the innards of the code handle all the rest, foo
|
|
int beam_fire(beam_fire_info *fire_info);
|
|
|
|
// fire a targeting beam, returns objnum on success. a much much simplified version of a beam weapon
|
|
// targeting lasers last _one_ frame. For a continuous stream - they must be created every frame.
|
|
// this allows it to work smoothly in multiplayer (detect "trigger down". every frame just create a targeting laser firing straight out of the
|
|
// object. this way you get all the advantages of nice rendering and collisions).
|
|
// NOTE : only references beam_info_index and shooter
|
|
int beam_fire_targeting(fighter_beam_fire_info *fire_info);
|
|
|
|
// return an object index of the guy who's firing this beam
|
|
int beam_get_parent(const object *bm);
|
|
|
|
// return weapon_info_index of beam
|
|
int beam_get_weapon_info_index(const object *bm);
|
|
|
|
// given a beam object, get the # of collisions which happened during the last collision check (typically, last frame)
|
|
int beam_get_num_collisions(int objnum);
|
|
|
|
// stuff collision info, returns 1 on success
|
|
int beam_get_collision(int objnum, int num, int *collision_objnum, mc_info **cinfo);
|
|
// ---------------
|
|
|
|
// init at game startup
|
|
void beam_init();
|
|
|
|
// initialize beam weapons for this level
|
|
void beam_level_init();
|
|
|
|
// shutdown beam weapons for this level
|
|
void beam_level_close();
|
|
|
|
// collide a beam with a ship, returns 1 if we can ignore all future collisions between the 2 objects
|
|
int beam_collide_ship(obj_pair *pair);
|
|
|
|
// collide a beam with an asteroid, returns 1 if we can ignore all future collisions between the 2 objects
|
|
int beam_collide_asteroid(obj_pair *pair);
|
|
|
|
// collide a beam with a missile, returns 1 if we can ignore all future collisions between the 2 objects
|
|
int beam_collide_missile(obj_pair *pair);
|
|
|
|
// collide a beam with debris, returns 1 if we can ignore all future collisions between the 2 objects
|
|
int beam_collide_debris(obj_pair *pair);
|
|
|
|
// collide a beam with a prop, returns 1 if we can ignore all future collisions between the 2 objects
|
|
int beam_collide_prop(obj_pair* pair);
|
|
|
|
// pre-move (before collision checking - but AFTER ALL OTHER OBJECTS HAVE BEEN MOVED)
|
|
void beam_move_all_pre();
|
|
|
|
// post-collision time processing for beams
|
|
void beam_move_all_post();
|
|
|
|
// render all beam weapons
|
|
void beam_render_all();
|
|
|
|
// delete all active beams
|
|
void beam_delete_all();
|
|
|
|
// early-out function for when adding object collision pairs, return 1 if the pair should be ignored
|
|
int beam_collide_early_out(object *a, object *b);
|
|
|
|
// pause all looping beam sounds
|
|
void beam_pause_sounds();
|
|
|
|
// unpause looping beam sounds
|
|
void beam_unpause_sounds();
|
|
|
|
void beam_calc_facing_pts(vec3d *top, vec3d *bot, vec3d *fvec, vec3d *pos, float w, float z_add);
|
|
|
|
// return the amount of damage which should be applied to a ship. basically, filters friendly fire damage
|
|
float beam_get_ship_damage(beam *b, object *objp, vec3d* hitpos = nullptr);
|
|
|
|
|
|
|
|
#endif
|