/* * 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 _WEAPON_H #define _WEAPON_H #include "globalincs/globals.h" #include "globalincs/systemvars.h" #include "globalincs/pstypes.h" #include "actions/Program.h" #include "decals/decals.h" #include "gamesnd/gamesnd.h" #include "graphics/2d.h" #include "graphics/color.h" #include "graphics/generic.h" #include "model/model.h" #include "model/animation/modelanimation.h" #include "particle/ParticleManager.h" #include "weapon/beam.h" #include "weapon/shockwave.h" #include "weapon/swarm.h" #include "weapon/trails.h" #include "weapon/weapon_flags.h" #include "model/modelrender.h" #include "render/3d.h" #include "utils/modular_curves.h" #include #include class object; class ship; class ship_weapon; class ship_subsys; #define WP_UNUSED -1 #define WP_LASER 0 // PLEASE NOTE that this flag specifies ballistic primaries as well - Goober5000 #define WP_MISSILE 1 #define WP_BEAM 2 // This is only set for beams in the #Beam Weapons section. Many beams are found in the #Primary Weapons section and therefore have the subtype WP_LASER. // Use the Weapon_Info::Beam flag or .is_beam() to properly check whether a weapon is a beam. extern const char *Weapon_subtype_names[]; extern int Num_weapon_subtypes; #define WRT_NONE -1 #define WRT_LASER 1 #define WRT_POF 2 // constants for weapon lock acquire methods #define WLOCK_PIXEL 0 #define WLOCK_TIMER 1 // constants for weapon lock restrictions #define LR_CURRENT_TARGET 0 // Only lock current target and subsystem #define LR_CURRENT_TARGET_SUBSYS 1 // #define LR_ANY_TARGETS 2 // enum for multilock object type restriction enum class LR_Objecttypes { LRO_SHIPS, LRO_WEAPONS }; constexpr int BANK_SWITCH_DELAY = 250; // after switching banks, 1/4 second delay until player can fire // scale factor for supercaps taking damage from weapons which are not "supercap" weapons #define SUPERCAP_DAMAGE_SCALE 0.25f // default amount of time to wait after firing before a remote detonated missile can be detonated #define DEFAULT_REMOTE_DETONATE_TRIGGER_WAIT 0.5f // default value weapon max range is set to if there's not a tabled range value. #define WEAPON_DEFAULT_TABLED_MAX_RANGE 999999999.9f #define MAX_SPAWN_TYPES_PER_WEAPON 5 // Bitmask filter applied during proximity detonation, evaluated relative to the launcher's team. // 0 = no relation filter (any relation passes). namespace Weapon::Proximity { constexpr uint8_t Relation_Hostile = 1 << 0; // launcher's team attacks target's team constexpr uint8_t Relation_Friendly = 1 << 1; // same team as launcher constexpr uint8_t Relation_Neutral = 1 << 2; // neither attacks the other and not same team } // homing missiles have an extended lifetime so they don't appear to run out of gas before they can hit a moving target at extreme // range. Check the comment in weapon_set_tracking_info() for more details #define LOCKED_HOMING_EXTENDED_LIFE_FACTOR 1.2f struct homing_cache_info { TIMESTAMP next_update; vec3d expected_pos; bool valid; bool skip_future_refinements; }; typedef struct weapon { int weapon_info_index; // index into weapon_info array int objnum; // object number for this weapon int model_instance_num; // model instance number, if we have any intrinsic-moving submodels int team; // The team of the ship that fired this int species; // The species of the ship that fired thisz float lifeleft; // life left on this weapon vec3d start_pos; int target_num; // Object index of target int target_sig; // So we know if the target is the same one we've been tracking fix creation_time; // time at which created, stuffed Missiontime flagset weapon_flags; // bit flags defining behavior, see WF_xxxx object* homing_object; // object this weapon is homing on. ship_subsys* homing_subsys; // subsystem this weapon is homing on vec3d homing_pos; // world position missile is homing on std::unique_ptr swarm_info_ptr; // index into swarm missile info, -1 if not WIF_SWARM int missile_list_index; // index for missiles into Missile_obj_list, -1 weapon not missile trail *trail_ptr; // NULL if no trail, otherwise a pointer to its trail ship_subsys *turret_subsys; // points to turret that fired weapon, otherwise NULL int group_id; // Which group this is in. float det_range; //How far from start_pos it blows up // Stuff for thruster glows int thruster_bitmap; // What frame the current thruster bitmap is at for this weapon float thruster_frame; // Used to keep track of which frame the animation should be on. int thruster_glow_bitmap; // What frame the current thruster engine glow bitmap is at for this weapon float thruster_glow_frame; // Used to keep track of which frame the engine glow animation should be on. float thruster_glow_noise; // Noise for current frame // laser stuff float laser_bitmap_frame; // these are used to keep track of which frame the relevant animations should be on float laser_headon_bitmap_frame; float laser_glow_bitmap_frame; float laser_glow_headon_bitmap_frame; int pick_big_attack_point_timestamp; // Timestamp at which to pick a new point to attack. vec3d big_attack_point; // Target-relative location of attack point. std::unique_ptr homing_cache_ptr; SCP_vector* cmeasure_ignore_list; int cmeasure_timer; // corkscrew info (taken out for now) short cscrew_index; // corkscrew info index // flak info short flak_index; // flak info index //local ssm stuff fix lssm_warpout_time; //time at which the missile warps out fix lssm_warpin_time; //time at which the missile warps back in int lssm_stage; //what stage its in 1=just launched, 2=warping out. 3=warped out, 4=warping back in, 5=terminal dive int lssm_warp_idx; //warphole index float lssm_warp_time; //length of time warphole stays open float lssm_warp_pct; //how much of the warphole's life should be dedicated to stage 2 vec3d lssm_target_pos; // weapon transparency info ubyte alpha_backward; // 1 = move in reverse (ascending in value) float alpha_current; // the current alpha value float weapon_max_vel; // might just as well store the data here float launch_speed; // the initial forward speed (can vary due to additive velocity or acceleration) // currently only gets set when weapon_info->acceleration_time is used TIMESTAMP last_spawn_time[MAX_SPAWN_TYPES_PER_WEAPON]; // used for continuous child spawn mc_info* collisionInfo; // The last collision of this weapon or NULL if it had none sound_handle hud_in_flight_snd_sig; // Signature of the sound played while the weapon is in flight WeaponState weapon_state; // The current state of the weapon float beam_per_shot_rot; // for type 5 beams modular_curves_entry_instance modular_curves_instance; TIMESTAMP mine_chase_expires; // when the active chase expires; TIMESTAMP::invalid() = not chasing TIMESTAMP mine_chase_cooldown_expires; // when the post-chase cooldown ends; TIMESTAMP::invalid() = no cooldown active } weapon; // info specific to beam weapons typedef struct beam_weapon_section_info { float width; // width of the section float flicker; // how much it flickers (0.0 to 1.0) float z_add; // is this necessary? float tile_factor; // texture tile factor -Bobboau int tile_type; // is this beam tiled by it's length, or not float translation; // makes the beam texture move -Bobboau generic_anim texture; // texture anim/bitmap } beam_weapon_section_info; enum class Type5BeamRotAxis { STARTPOS_OFFSET, ENDPOS_OFFSET, STARTPOS_NO_OFFSET, ENDPOS_NO_OFFSET, CENTER, UNSPECIFIED }; enum class Type5BeamPos { RANDOM_OUTSIDE, RANDOM_INSIDE, CENTER, SAME_RANDOM }; typedef struct type5_beam_info { bool no_translate; // true if the end pos parameters were left unspecified Type5BeamPos start_pos; // whether it starts from the center or a type 0 or 1 beam kind of random Type5BeamPos end_pos; // same as above but but with an extra 'same random as start' option vec3d start_pos_offset; // position simply added to start pos (possibly manipulated by the bools below) vec3d end_pos_offset; // position simply added to end pos (possibly manipulated by the bools below) vec3d start_pos_rand; // same as above but a randomly chosen between defined value for each axis and its negative vec3d end_pos_rand; int slash_pos_curve_idx; bool target_orient_positions; // if true, offsets are oriented relative to the target, else the shooter's pov bool target_scale_positions; // if true, offsets are scaled by target radius, else its a fixed span from the shooters pov // regardless of distance float continuous_rot; // radians per sec rotation over beam lifetime int rot_curve_idx; Type5BeamRotAxis continuous_rot_axis; // axis around which do continuous rotation SCP_vector burst_rot_pattern; // radians to rotate for each beam in a burst, will also make spawned and ssb beams fire // this many beams simultaneously with the defined rotations Type5BeamRotAxis burst_rot_axis; // axis around which to do burst rotation float per_burst_rot; // radians to rotate for each burst, or each shot if no burst Type5BeamRotAxis per_burst_rot_axis; // axis around which to do per burst rotation } type5_beam_info; typedef struct beam_weapon_info { BeamType beam_type; // beam type float beam_life; // how long it lasts int beam_warmup; // how long it takes to warmup (in ms) int beam_warmdown; // how long it takes to warmdown (in ms) float beam_muzzle_radius; // muzzle glow radius particle::ParticleEffectHandle beam_muzzle_effect; SCP_map> beam_iff_miss_factor; // magic # which makes beams miss more. by parent iff and player skill level gamesnd_id beam_loop_sound; // looping beam sound gamesnd_id beam_warmup_sound; // warmup sound gamesnd_id beam_warmdown_sound; // warmdown sound int beam_num_sections; // the # of visible "sections" on the beam generic_anim beam_glow; // muzzle glow bitmap float glow_length; // (DahBlount) determines the length the muzzle glow when using a directional glow bool directional_glow; // (DahBlount) makes the muzzle glow render to a poly that is oriented along the direction of fire int beam_shots; // # of shots the beam takes float beam_shrink_factor; // what percentage of total beam lifetime when the beam starts shrinking float beam_shrink_pct; // what percent/second the beam shrinks at float beam_initial_width; // what percentage of total beam width the beam has initially float beam_grow_factor; // what percentage of total beam lifetime when the beam starts growing float beam_grow_pct; // what percent/second the beam grows at beam_weapon_section_info sections[MAX_BEAM_SECTIONS]; // info on the visible sections of the beam float range; // how far it will shoot-Bobboau float damage_threshold; // point at wich damage will start being atenuated from 0.0 to 1.0 float beam_width; // width of the beam (for certain collision checks) bool beam_light_flicker; // If beam light is affected by the flickering bool beam_light_as_multiplier; // If beam light is used directly or multiplies the section width flagset flags; type5_beam_info t5info; // type 5 beams only } beam_weapon_info; typedef struct spawn_weapon_info { short spawn_wep_index; // weapon info index of the child weapon, during parsing instead an index into Spawn_names // MAY BE -1, if parsed weapon could not be found short spawn_count; // Number of weapons of spawn_type to spawn. float spawn_angle; // Angle to spawn the child weapons in. default is 180 float spawn_min_angle; // Angle of spawning 'deadzone' inside spawn angle. Default 0. float spawn_interval; // How often to do continuous spawn, negative is no continuous spawn float spawn_interval_delay; // A delay before starting continuous spawn float spawn_chance; // Liklihood of spawning on every spawn interval particle::ParticleEffectHandle spawn_effect; // Effect for continuous spawnings bool spawn_aimed; // If true, aim child toward the parent's homing_object instead of a random cone; default false float spawn_aim_lead; // Seconds of constant-velocity lead extrapolation for aimed spawns; 0.0 = no lead; default 0.0 } spawn_weapon_info; // use this to extend a beam to "infinity" #define BEAM_FAR_LENGTH 30000.0f extern weapon Weapons[MAX_WEAPONS]; #define WEAPON_TITLE_LEN 48 enum InFlightSoundType { TARGETED, UNTARGETED, ALWAYS }; #define MAX_SUBSTITUTION_PATTERNS 10 enum class LockRestrictionType { TYPE, CLASS, SPECIES, IFF }; typedef std::pair lock_restriction; enum class HomingAcquisitionType { CLOSEST, RANDOM, }; enum class HitType { SHIELD, SUBSYS, HULL, NONE, }; constexpr size_t NumHitTypes = static_cast>(HitType::NONE); enum class SpecialImpactCondition { DEBRIS, ASTEROID, EMPTY_SPACE, }; using ImpactCondition = std::variant; struct ConditionData { ImpactCondition condition = SpecialImpactCondition::EMPTY_SPACE; HitType hit_type = HitType::NONE; float damage = 0.0f; float health = 1.0f; float max_health = 1.0f; }; struct ConditionalImpact { particle::ParticleEffectHandle effect; std::optional pokethrough_effect; ::util::ParsedRandomFloatRange min_health_threshold; // factor, 0-1 ::util::ParsedRandomFloatRange max_health_threshold; // factor, 0-1 ::util::ParsedRandomFloatRange min_damage_hits_ratio; // factor ::util::ParsedRandomFloatRange max_damage_hits_ratio; // factor ::util::ParsedRandomFloatRange min_angle_threshold; // in degrees ::util::ParsedRandomFloatRange max_angle_threshold; // in degrees float laser_pokethrough_threshold; // factor, 0-1 bool dinky; bool disable_if_player_parent; bool disable_on_subsys_passthrough; bool disable_main_on_pokethrough; }; enum class FiringPattern { STANDARD, CYCLE_FORWARD, CYCLE_REVERSE, RANDOM_EXHAUSTIVE, RANDOM_NONREPEATING, RANDOM_REPEATING, }; float weapon_get_lifetime_pct(const weapon& wp); float weapon_get_age(const weapon& wp); float weapon_get_viewing_angle(const weapon& wp); float weapon_get_apparent_size(const weapon& wp); float beam_get_warmup_lifetime_pct(const beam& wp); float beam_get_warmdown_lifetime_pct(const beam& wp); float beam_get_warmdown_age(const beam& wp); struct WeaponLaunchCurveData { int num_firepoints; float distance_to_target; float target_radius; float target_forward_speed; }; struct weapon_info; extern SCP_vector Weapon_info; struct weapon_info { char name[NAME_LENGTH]; // name of this weapon SCP_string display_name; // display name of this weapon char title[WEAPON_TITLE_LEN]; // official title of weapon (used by tooltips) std::unique_ptr desc; // weapon's description (used by tooltips) char altSubsysName[NAME_LENGTH]; // rename turret to this if this is the turrets first weapon char pofbitmap_name[MAX_FILENAME_LEN]; // Name of the pof representing this if POF, or bitmap filename if bitmap int model_num; // modelnum of weapon -- -1 if no model char external_model_name[MAX_FILENAME_LEN]; //the model rendered on the weapon points of a ship int external_model_num; // modelnum of the model rendered on the weapon points of a ship; -1 if $External Model File: was not given std::unique_ptr tech_desc; // weapon's description (in tech database) char tech_anim_filename[MAX_FILENAME_LEN]; // weapon's tech room animation char tech_title[NAME_LENGTH]; // weapon's name (in tech database) char tech_model[MAX_FILENAME_LEN]; //Image to display in the techroom (TODO) or the weapon selection screen if the ANI isn't specified/missing int hud_target_lod; // LOD to use when rendering weapon model to the hud targetbox int num_detail_levels; // number of LODs defined in table (optional) int detail_distance[MAX_MODEL_DETAIL_LEVELS]; // LOD distances define in table (optional) int subtype; // one of the WP_* macros above int render_type; // rendering method, laser, pof, avi vec3d closeup_pos; // position for camera to set an offset for viewing the weapon model float closeup_zoom; // zoom when using weapon model in closeup view in loadout selection std::optional icon_closeup_pos; // icon-specific position for camera for viewing the weapon model std::optional icon_closeup_zoom; // icon-specific zoom for viewing the weapon model char hud_filename[MAX_FILENAME_LEN]; //Name of image to display on HUD in place of text int hud_image_index; //teh index of the image generic_anim laser_bitmap; // bitmap for a laser generic_anim laser_headon_bitmap; // optional bitmap for when viewed from ahead/behind generic_anim laser_glow_bitmap; // optional laser glow bitmap generic_anim laser_glow_headon_bitmap; // optional headon for the glow float laser_headon_switch_rate; // how smooth vs sudden the transition between the headon and main bitmap should be float laser_headon_switch_ang; // at what angle float laser_length; bool laser_length_by_frametime; vec3d bitmap_color; // color modifier for main laser bitmap, unlike the 'laser colors' which affect the glow bitmap color laser_color_1; // for cycling between glow colors color laser_color_2; // for cycling between glow colors float laser_head_radius, laser_tail_radius; float laser_glow_length_scale; float laser_glow_head_scale; float laser_glow_tail_scale; float laser_min_pixel_size; vec3d laser_pos_offset; float collision_radius_override; // overrides the radius for the purposes of collision bool light_color_set; hdr_color light_color; //For the light cast by the projectile float light_radius; float max_speed; // max speed of the weapon float acceleration_time; // how many seconds to reach max speed (secondaries only) float vel_inherit_amount; // how much of the parent ship's velocity is inherited (0.0..1.0) float free_flight_time; float free_flight_speed_factor; float gravity_const; // multiplier applied to gravity, if any in the mission float mass; // mass of the weapon float fire_wait; // fire rate -- amount of time before you can refire the weapon float max_delay; // max time to delay a shot (DahBlount) float min_delay; // min time to delay a shot (DahBlount) float damage; // damage of weapon (for missile, damage within inner radius) float damage_time; // point in the lifetime of the weapon at which damage starts to attenuate. This applies to non-beam primaries. (DahBlount) float atten_damage; // The damage to attenuate to. (DahBlount) float damage_incidence_max; // dmg multipler when weapon hits dead-on (perpindicular) float damage_incidence_min; // dmg multipler when weapon hits glancing (parallel) shockwave_create_info shockwave; shockwave_create_info dinky_shockwave; fix arm_time; float arm_dist; float arm_radius; float det_range; float det_radius; //How far from target or target subsystem it blows up float proximity_radius; // Scan radius for proximity detonation; 0 = disabled. $MineInfo: defaults this to 50. SCP_vector proximity_iff; // IFF indices that trigger detonation; empty = any IFF SCP_vector proximity_species; // species indices that trigger detonation; empty = any species SCP_vector proximity_type; // ship type indices that trigger detonation; empty = any type SCP_vector proximity_class; // ship class (ship_info) indices that trigger detonation; empty = any class uint8_t proximity_relation_mask; // bitmask of Weapon::Proximity::Relation_Flags; 0 = any relation float proximity_detonate_chance; // probability [0,1] that a proximity contact actually triggers detonation; default 1.0 float proximity_stealth_multiplier; // scale factor applied to proximity_radius for stealth ships; 0.0 = undetectable, 1.0 = normal range; default 1.0 float mine_sensors_range; // range at which mine shows as a distorted blip; 0.0f = never. Always finite for mines after parsing. float mine_targetable_range; // range at which mine is fully targetable; 0.0f = never. Always finite for mines after parsing. float mine_chase_duration; // seconds mine chases the triggering ship after proximity contact; 0.0 = detonate immediately (default) bool mine_detonates_on_chase_timeout; // if true, detonate when chase timer expires; if false, give up and return to stationary; default true float mine_chase_cooldown; // seconds after a chase ends before proximity scanning re-enables; default 5.0 float flak_detonation_accuracy; //How far away from a target a flak shell will blow up. Standard is 65.0f float flak_targeting_accuracy; //Determines the amount of jitter applied to flak targeting. USE WITH CAUTION! float untargeted_flak_range_penalty; //Untargeted flak shells detonate after travelling max range - this parameter. Default 20.0f float armor_factor, shield_factor, subsystem_factor; // in 0.0..2.0, scale of damage done to type of thing float life_min; float life_max; float lifetime; // How long this weapon will live for float energy_consumed; // Energy used up when weapon is fired flagset wi_flags; // bit flags defining behavior, see WIF_xxxx float turn_time; float turn_accel_time; float cargo_size; // cargo space taken up by individual weapon (missiles only) float autoaim_fov; // the weapon specific auto-aim field of view float rearm_rate; // rate per second at which secondary weapons are loaded during rearming bool disallow_rearm; // if true, support ships cannot rearm this weapon int reloaded_per_batch; // number of munitions rearmed per batch float weapon_range; // max range weapon can be effectively fired. (May be less than life * speed) float optimum_range; // causes ai fighters to prefer this distance when attacking with the weapon float weapon_min_range; // *Minimum weapon range, default is 0 -Et1 flagset aiming_flags; float minimum_convergence_distance; float convergence_distance; bool pierce_objects; bool spawn_children_on_pierce; // spawn weapons int num_spawn_weapons_defined; int maximum_children_spawned; // An upper bound for the total number of spawned children, used by multi spawn_weapon_info spawn_info[MAX_SPAWN_TYPES_PER_WEAPON]; float lifetime_variation_factor_when_child; // swarm count short swarm_count; // how many swarm missiles are fired for this weapon int SwarmWait; // *Swarm firewait, default is 150 -Et1 int target_restrict; LR_Objecttypes target_restrict_objecttypes; bool multi_lock; int max_seeking; // how many seekers can be active at a time if multilock is enabled. A value of one will lock stuff up one by one. int max_seekers_per_target; // how many seekers can be attached to a target. SCP_vector ship_restrict; // list of pairs of types of restrictions, and the specific restriction of that type SCP_vector> ship_restrict_strings; // the above but the specific restrictions are the parsed strings (instead of looked up indicies) bool trigger_lock; // Trigger must be held down and released to lock and fire. bool launch_reset_locks; // Lock indicators reset after firing HomingAcquisitionType auto_target_method; // Specific to ASPECT homing missiles. int acquire_method; float min_lock_time; // minimum time (in seconds) to achieve lock int lock_pixels_per_sec; // pixels/sec moved while locking int catchup_pixels_per_sec; // pixels/sec moved while catching-up for a lock int catchup_pixel_penalty; // number of extra pixels to move while locking as a penalty for catching up for a lock float lock_fov; // Specific to HEAT homing missiles. float fov; // Seeker strength - for countermeasures overhaul. float seeker_strength; gamesnd_id pre_launch_snd; int pre_launch_snd_min_interval; //Minimum interval in ms between the last time the pre-launch sound was played and the next time it can play, as a limiter in case the player is pumping the trigger gamesnd_id launch_snd; gamesnd_id cockpit_launch_snd; gamesnd_id impact_snd; gamesnd_id disarmed_impact_snd; gamesnd_id shield_impact_snd; gamesnd_id flyby_snd; // whizz-by sound, transmitted through weapon's portable atmosphere. gamesnd_id ambient_snd; gamesnd_id start_firing_snd; gamesnd_id loop_firing_snd; gamesnd_id linked_loop_firing_snd; gamesnd_id end_firing_snd; gamesnd_id hud_tracking_snd; // Sound played when the player is tracking a target with this weapon gamesnd_id hud_locked_snd; // Sound played when the player is locked onto a target with this weapon gamesnd_id hud_in_flight_snd; // Sound played while the player has this weapon in flight InFlightSoundType in_flight_play_type; // The status when the sound should be played // Specific to weapons with TRAILS: trail_info tr_info; char icon_filename[MAX_FILENAME_LEN]; // filename for icon that is displayed in weapon selection char anim_filename[MAX_FILENAME_LEN]; // filename for animation that plays in weapon selection int selection_effect; color fs2_effect_grid_color; // color of the grid effect in the weapon selection screen color fs2_effect_scanline_color; // color of the scanline effect in the weapon selection screen int fs2_effect_grid_density; // density of the grid effect in the weapon selection screen color fs2_effect_wireframe_color; // color of the wireframe effect in the weapon selection screen float shield_impact_effect_radius; // shield surface effect radius float shield_impact_explosion_radius; // shield-specific particle effect radius particle::ParticleEffectHandle impact_weapon_expl_effect; // Impact particle effect particle::ParticleEffectHandle dinky_impact_weapon_expl_effect; // Dinky impact particle effect particle::ParticleEffectHandle flash_impact_weapon_expl_effect; particle::ParticleEffectHandle piercing_impact_effect; particle::ParticleEffectHandle piercing_impact_secondary_effect; SCP_map> conditional_impacts; particle::ParticleEffectHandle muzzle_effect; // Particle effect for the various states, WeaponState::NORMAL is the state for the whole lifetime, even for missiles SCP_unordered_map state_effects; // EMP effect float emp_intensity; // intensity of the EMP effect float emp_time; // time of the EMP effect // Recoil effect float recoil_modifier; float shudder_modifier; // Energy suck effect float weapon_reduce; // how much energy removed from weapons systems float afterburner_reduce; // how much energy removed from weapons systems // Vampirism Effect Multiplier float vamp_regen; // Factor by which a vampiric weapon will multiply the healing done to the shooter // tag stuff float tag_time; // how long the tag lasts int tag_level; // tag level (1 - 3) float field_of_fire; //cone the weapon will fire in, 0 is strait all the time-Bobboau float fof_spread_rate; //How quickly the FOF will spread for each shot (primary weapons only, this doesn't really make sense for turrets) float fof_reset_rate; //How quickly the FOF spread will reset over time (primary weapons only, this doesn't really make sense for turrets) float max_fof_spread; //The maximum fof increase that the shots can spread to FiringPattern firing_pattern; int shots; //the number of shots that will be fired at a time, //only realy usefull when used with FOF to make a shot gun effect //now also used for weapon point cycleing int cycle_multishot; //ugly hack -- used to control multishot if the weapon uses any non-standard firing pattern, since $shots is used for fire point number // Corkscrew info - phreak 11/9/02 int cs_num_fired; float cs_radius; float cs_twist; bool cs_crotate; int cs_delay; bool cs_random_angle; float cs_angle; //electronics info - phreak 5/3/03 int elec_time; //how long it lasts, in milliseconds float elec_eng_mult; //multiplier on engine subsystem float elec_weap_mult; //multiplier on weapon subsystem and turrets float elec_beam_mult; //used instead of elec_weap_mult if turret is a beam turret float elec_sensors_mult; //multiplier on sensors and awacs int elec_randomness; //disruption time lasts + or - this value from whats calculated. time in milliseconds int elec_use_new_style; //use new style electronics parameters // SSM int SSM_index; // wich entry in the SSM,tbl it uses -Bobboau //local ssm info int lssm_warpout_delay; //delay between launch and warpout (ms) int lssm_warpin_delay; //delay between warpout and warpin (ms) float lssm_stage5_vel; //velocity during final stage float lssm_warpin_radius; float lssm_lock_range; int lssm_warpeffect; //Which fireballtype is used for the warp effect // Beam weapon effect beam_weapon_info b_info; // this must be valid if the weapon is a beam weapon WIF_BEAM or WIF_BEAM_SMALL // now using new particle spew struct -nuke SCP_vector particle_spewers; // Countermeasure information float cm_aspect_effectiveness; float cm_heat_effectiveness; float cm_effective_rad; float cm_detonation_rad; vec3d cm_launch_vec; float cm_launch_speed; float cm_launch_variance; bool cm_kill_single; // should the countermeasure kill just the single decoyed missile within CMEASURE_DETONATE_DISTANCE? int cmeasure_timer_interval; // how many milliseconds between pulses int cmeasure_firewait; // delay in milliseconds between countermeasure firing --wookieejedi bool cmeasure_use_firewait; // if set to true, then countermeasure will use specified firewait instead of default --wookieejedi float cmeasure_failure_delay_multiplier_ai; // multiplier for firewait between failed countermeasure launches, next launch try = this value * firewait --wookieejedi float cmeasure_success_delay_multiplier_ai; // multiplier for firewait between successful countermeasure launches, next launch try = this value * firewait --wookieejedi float weapon_submodel_rotate_accell; float weapon_submodel_rotate_vel; int damage_type_idx; int damage_type_idx_sav; // stored value from table used to reset damage_type_idx int armor_type_idx; // Weapon armor type // transparency/alpha info float alpha_max; // maximum alpha value to use float alpha_min; // minimum alpha value to use float alpha_cycle; // cycle between max and min by this much each frame int weapon_hitpoints; int burst_shots; // always 1 less than the actual burst length; 0 = no burst, 1 = two-shot burst, 2 = 3-shot, etc float burst_delay; flagset burst_flags; // Thruster effects generic_anim thruster_flame; generic_anim thruster_glow; float thruster_glow_factor; float target_lead_scaler; int num_targeting_priorities; int targeting_priorities[32]; // Optional weapon failures float failure_rate; std::unique_ptr failure_sub_name; int failure_sub; // the optional pattern of weapons that this weapon will fire size_t num_substitution_patterns; int weapon_substitution_pattern[MAX_SUBSTITUTION_PATTERNS]; //weapon_indexes char weapon_substitution_pattern_names[MAX_SUBSTITUTION_PATTERNS][NAME_LENGTH]; // weapon names so that we can generate the indexs after sort int score; //Optional score for destroying the weapon SCP_map custom_data; SCP_vector custom_strings; decals::creation_info impact_decal; actions::ProgramSet on_create_program; animation::ModelAnimationSet animations; enum class WeaponLaunchCurveOutputs { // outputs FIRE_WAIT_MULT, SHOTS_MULT, BURST_SHOTS_MULT, BURST_DELAY_MULT, VELOCITY_MULT, LIFE_MULT, FAILURE_RATE_MULT, FOF_MULT, NUM_VALUES }; private: static constexpr auto weapon_launch_modular_curves_definition = make_modular_curve_definition( std::array { std::pair {"Fire Wait Mult", WeaponLaunchCurveOutputs::FIRE_WAIT_MULT}, std::pair {"Shots Mult", WeaponLaunchCurveOutputs::SHOTS_MULT}, std::pair {"Burst Shots Mult", WeaponLaunchCurveOutputs::BURST_SHOTS_MULT}, std::pair {"Burst Delay Mult", WeaponLaunchCurveOutputs::BURST_DELAY_MULT}, std::pair {"Velocity Mult", WeaponLaunchCurveOutputs::VELOCITY_MULT}, std::pair {"Life Mult", WeaponLaunchCurveOutputs::LIFE_MULT}, std::pair {"Failure Rate Mult", WeaponLaunchCurveOutputs::FAILURE_RATE_MULT}, std::pair {"FoF Mult", WeaponLaunchCurveOutputs::FOF_MULT}, }, std::pair {"Num Firepoints", modular_curves_submember_input<&WeaponLaunchCurveData::num_firepoints>{}}, std::pair {"Distance to Target", modular_curves_submember_input<&WeaponLaunchCurveData::distance_to_target>{}}, std::pair {"Target Radius", modular_curves_submember_input<&WeaponLaunchCurveData::target_radius>{}}, std::pair {"Target Forward Speed", modular_curves_submember_input<&WeaponLaunchCurveData::target_forward_speed>{}} ); public: enum class WeaponCurveOutputs { // outputs LASER_LENGTH_MULT, LASER_RADIUS_MULT, LASER_HEAD_RADIUS_MULT, LASER_TAIL_RADIUS_MULT, LASER_OFFSET_X_MULT, LASER_OFFSET_Y_MULT, LASER_OFFSET_Z_MULT, LASER_HEADON_SWITCH_ANG_MULT, LASER_HEADON_SWITCH_RATE_MULT, LASER_ANIM_STATE, LASER_ANIM_STATE_ADD, LASER_ALPHA_MULT, LASER_BITMAP_R_MULT, LASER_BITMAP_G_MULT, LASER_BITMAP_B_MULT, LASER_GLOW_R_MULT, LASER_GLOW_G_MULT, LASER_GLOW_B_MULT, LIGHT_INTENSITY_MULT, LIGHT_RADIUS_MULT, LIGHT_R_MULT, LIGHT_G_MULT, LIGHT_B_MULT, DET_RADIUS_MULT, TURN_RATE_MULT, SPAWN_RATE_MULT, SPAWN_COUNT_MULT, NUM_VALUES }; private: static constexpr auto weapon_modular_curves_definition = make_modular_curve_definition( std::array { std::pair {"Laser Length Mult", WeaponCurveOutputs::LASER_LENGTH_MULT}, std::pair {"Laser Radius Mult", WeaponCurveOutputs::LASER_RADIUS_MULT}, std::pair {"Laser Head Radius Mult", WeaponCurveOutputs::LASER_HEAD_RADIUS_MULT}, std::pair {"Laser Tail Radius Mult", WeaponCurveOutputs::LASER_TAIL_RADIUS_MULT}, std::pair {"Laser Offset X Mult", WeaponCurveOutputs::LASER_OFFSET_X_MULT}, std::pair {"Laser Offset Y Mult", WeaponCurveOutputs::LASER_OFFSET_Y_MULT}, std::pair {"Laser Offset Z Mult", WeaponCurveOutputs::LASER_OFFSET_Z_MULT}, std::pair {"Laser Headon Transition Angle Mult", WeaponCurveOutputs::LASER_HEADON_SWITCH_ANG_MULT}, std::pair {"Laser Headon Transition Rate Mult", WeaponCurveOutputs::LASER_HEADON_SWITCH_RATE_MULT}, std::pair {"Laser Animation State", WeaponCurveOutputs::LASER_ANIM_STATE}, std::pair {"Laser Animation State Add", WeaponCurveOutputs::LASER_ANIM_STATE_ADD}, std::pair {"Laser Alpha Mult", WeaponCurveOutputs::LASER_ALPHA_MULT}, std::pair {"Laser Bitmap R Mult", WeaponCurveOutputs::LASER_BITMAP_R_MULT}, std::pair {"Laser Bitmap G Mult", WeaponCurveOutputs::LASER_BITMAP_G_MULT}, std::pair {"Laser Bitmap B Mult", WeaponCurveOutputs::LASER_BITMAP_B_MULT}, std::pair {"Laser Glow R Mult", WeaponCurveOutputs::LASER_GLOW_R_MULT}, std::pair {"Laser Glow G Mult", WeaponCurveOutputs::LASER_GLOW_G_MULT}, std::pair {"Laser Glow B Mult", WeaponCurveOutputs::LASER_GLOW_B_MULT}, std::pair {"Light Intensity Mult", WeaponCurveOutputs::LIGHT_INTENSITY_MULT}, std::pair {"Light Radius Mult", WeaponCurveOutputs::LIGHT_RADIUS_MULT}, std::pair {"Light R Mult", WeaponCurveOutputs::LIGHT_R_MULT}, std::pair {"Light G Mult", WeaponCurveOutputs::LIGHT_G_MULT}, std::pair {"Light B Mult", WeaponCurveOutputs::LIGHT_B_MULT}, std::pair {"Detonation Radius Mult", WeaponCurveOutputs::DET_RADIUS_MULT}, std::pair {"Turn Rate Mult", WeaponCurveOutputs::TURN_RATE_MULT}, std::pair {"Child Spawn Rate Mult", WeaponCurveOutputs::SPAWN_RATE_MULT}, std::pair {"Child Spawn Count Mult", WeaponCurveOutputs::SPAWN_COUNT_MULT}, }, std::pair {"Lifetime", modular_curves_functional_input{}}, std::pair {"Age", modular_curves_functional_input{}}, std::pair {"Base Velocity", modular_curves_submember_input<&weapon::weapon_max_vel>{}}, std::pair {"Base Damage", modular_curves_submember_input<&weapon::weapon_info_index, &Weapon_info, &weapon_info::damage>{}}, std::pair {"Max Hitpoints", modular_curves_submember_input<&weapon::weapon_info_index, &Weapon_info, &weapon_info::weapon_hitpoints>{}}, std::pair {"Current Hitpoints", modular_curves_submember_input<&weapon::objnum, &Objects, &object::hull_strength>{}}, std::pair {"Hitpoints Fraction", modular_curves_math_input< modular_curves_submember_input<&weapon::objnum, &Objects, &object::hull_strength>, modular_curves_submember_input<&weapon::weapon_info_index, &Weapon_info, &weapon_info::weapon_hitpoints>, ModularCurvesMathOperators::division >{}}, std::pair {"Parent Radius", modular_curves_submember_input<&weapon::objnum, &Objects, &object::parent, &Objects, &object::radius>{}}, std::pair {"Viewing Angle", modular_curves_functional_input{}}, std::pair {"Apparent Size", modular_curves_functional_input{}} ); public: enum class WeaponHitCurveOutputs { // outputs DAMAGE_MULT, HULL_DAMAGE_MULT, SHIELD_DAMAGE_MULT, SUBSYS_DAMAGE_MULT, MASS_MULT, NUM_VALUES }; private: static constexpr auto weapon_hit_modular_curves_definition = weapon_modular_curves_definition.derive_modular_curves_input_only_subset( std::pair {"Target Hitpoints", modular_curves_submember_input<&object::hull_strength>{}}, std::pair {"Target Radius", modular_curves_submember_input<&object::radius>{}} ).derive_modular_curves_subset( std::array { std::pair {"Damage Mult", WeaponHitCurveOutputs::DAMAGE_MULT}, std::pair {"Hull Damage Mult", WeaponHitCurveOutputs::HULL_DAMAGE_MULT}, std::pair {"Shield Damage Mult", WeaponHitCurveOutputs::SHIELD_DAMAGE_MULT}, std::pair {"Subsystem Damage Mult", WeaponHitCurveOutputs::SUBSYS_DAMAGE_MULT}, std::pair {"Mass Mult", WeaponHitCurveOutputs::MASS_MULT}, }, std::pair {"Dot", modular_curves_self_input{}} ); public: enum class BeamCurveOutputs { // outputs BEAM_WIDTH_MULT, BEAM_ALPHA_MULT, BEAM_ANIM_STATE, GLOW_RADIUS_MULT, GLOW_ALPHA_MULT, GLOW_ANIM_STATE, END_POSITION_BY_VELOCITY, NUM_VALUES }; private: static constexpr auto beam_modular_curves_definition = make_modular_curve_definition( std::array { std::pair {"Beam Width Mult", BeamCurveOutputs::BEAM_WIDTH_MULT}, std::pair {"Beam Alpha Mult", BeamCurveOutputs::BEAM_ALPHA_MULT}, std::pair {"Beam Anim State", BeamCurveOutputs::BEAM_ANIM_STATE}, std::pair {"Muzzle Glow Radius Mult", BeamCurveOutputs::GLOW_RADIUS_MULT}, std::pair {"Muzzle Glow Alpha Mult", BeamCurveOutputs::GLOW_ALPHA_MULT}, std::pair {"Muzzle Glow Anim State", BeamCurveOutputs::GLOW_ANIM_STATE}, std::pair {"Move End Position by Target Velocity", BeamCurveOutputs::END_POSITION_BY_VELOCITY} }, std::pair {"Beam Lifetime", modular_curves_math_input< modular_curves_math_input< modular_curves_submember_input<&beam::life_total>, modular_curves_submember_input<&beam::life_left>, ModularCurvesMathOperators::subtraction >, modular_curves_submember_input<&beam::life_total>, ModularCurvesMathOperators::division >{}}, std::pair {"Beam Age", modular_curves_math_input< modular_curves_submember_input<&beam::life_total>, modular_curves_submember_input<&beam::life_left>, ModularCurvesMathOperators::subtraction >{}}, std::pair {"Beam Total Life", modular_curves_submember_input<&beam::life_total>{}}, std::pair {"Warmup Lifetime", modular_curves_functional_input{}}, std::pair {"Warmdown Lifetime", modular_curves_functional_input{}}, std::pair {"Warmdown Age", modular_curves_functional_input{}}, std::pair {"Parent Radius", modular_curves_submember_input<&beam::objnum, &Objects, &object::parent, &Objects, &object::radius>{}} ); public: enum class BeamHitCurveOutputs { // outputs DAMAGE_MULT, HULL_DAMAGE_MULT, SHIELD_DAMAGE_MULT, SUBSYS_DAMAGE_MULT, MASS_MULT, NUM_VALUES }; private: static constexpr auto beam_hit_modular_curves_definition = beam_modular_curves_definition .derive_modular_curves_subset( std::array{ std::pair{"Damage Mult", BeamHitCurveOutputs::DAMAGE_MULT}, std::pair{"Hull Damage Mult", BeamHitCurveOutputs::HULL_DAMAGE_MULT}, std::pair{"Shield Damage Mult", BeamHitCurveOutputs::SHIELD_DAMAGE_MULT}, std::pair{"Subsystem Damage Mult", BeamHitCurveOutputs::SUBSYS_DAMAGE_MULT}, std::pair{"Mass Mult", BeamHitCurveOutputs::MASS_MULT}, }, std::pair{"Target Hitpoints", modular_curves_submember_input<&object::hull_strength>{}}, std::pair{"Target Radius", modular_curves_submember_input<&object::radius>{}} ); public: MODULAR_CURVE_SET(weapon_launch_curves, weapon_info::weapon_launch_modular_curves_definition); MODULAR_CURVE_SET(weapon_curves, weapon_info::weapon_modular_curves_definition); MODULAR_CURVE_SET(weapon_hit_curves, weapon_info::weapon_hit_modular_curves_definition); MODULAR_CURVE_SET(beam_curves, weapon_info::beam_modular_curves_definition); MODULAR_CURVE_SET(beam_hit_curves, weapon_info::beam_hit_modular_curves_definition); weapon_info(); inline bool is_primary() const { return subtype == WP_LASER || subtype == WP_BEAM; } // either of these is allowed in a primary bank inline bool is_secondary() const { return subtype == WP_MISSILE; } inline bool is_beam() const { return subtype == WP_BEAM || wi_flags[Weapon::Info_Flags::Beam]; } inline bool is_non_beam_primary() const { return subtype == WP_LASER && !wi_flags[Weapon::Info_Flags::Beam]; } inline bool is_homing() const { return wi_flags.any_of(Weapon::Info_Flags::Homing_heat,Weapon::Info_Flags::Homing_aspect,Weapon::Info_Flags::Homing_javelin); } inline bool is_locked_homing() const { return wi_flags.any_of(Weapon::Info_Flags::Homing_aspect,Weapon::Info_Flags::Homing_javelin); } inline bool hurts_big_ships() const { return wi_flags.any_of(Weapon::Info_Flags::Bomb,Weapon::Info_Flags::Beam,Weapon::Info_Flags::Huge,Weapon::Info_Flags::Big_only); } inline bool is_interceptable() const { return wi_flags.any_of(Weapon::Info_Flags::Fighter_Interceptable,Weapon::Info_Flags::Turret_Interceptable); } inline bool is_mine() const { return wi_flags[Weapon::Info_Flags::Mine]; } const char* get_display_name() const; bool has_display_name() const; void reset(); void clone(const weapon_info& other); }; extern special_flag_def_list_new&> Weapon_Info_Flags[]; extern const size_t num_weapon_info_flags; // Data structure to track the active missiles typedef struct missile_obj { missile_obj *next, *prev; int flags, objnum; } missile_obj; extern missile_obj Missile_obj_list; extern int Num_weapons; extern int First_secondary_index; extern int Default_cmeasure_index; extern SCP_vector Player_weapon_precedence; // Vector of weapon types, precedence list for player weapon selection #define WEAPON_INDEX(wp) (static_cast((wp)-Weapons)) #define WEAPON_INFO_INDEX(wip) (static_cast((wip)-Weapon_info.data())) typedef struct tracking_info { ship_subsys *subsys; int objnum; bool locked; tracking_info() : subsys(nullptr), objnum(-1), locked(false) {} } tracking_info; int weapon_info_lookup(const char *name); inline int weapon_info_size() { return static_cast(Weapon_info.size()); } void weapon_init(); // called at game startup void weapon_post_ship_init(); // called after ship_init() to resolve mine proximity ship type/class names void weapon_close(); // called at game shutdown void weapon_level_init(); // called before the start of each level void weapon_render(object* obj, model_draw_list *scene); void weapon_delete( object * obj ); void weapon_process_pre( object *obj, float frame_time); void weapon_process_post( object *obj, float frame_time); //Call before weapons_page_in to mark a weapon as used void weapon_mark_as_used(int weapon_id); // Helper functions for l_Weaponclass.isWeaponUsed bool weapon_page_in(int weapon_type); bool weapon_used(int weapon_type); // Group_id: If you should quad lasers, they should all have the same group id. // This will be used to optimize lighting, since each group only needs to cast one light. // Call this to get a new group id, then pass it to each weapon_create call for all the // weapons in the group. Number will be between 0 and WEAPON_MAX_GROUP_IDS and will // get reused. int weapon_create_group_id(); // How many unique groups of weapons there can be at one time. #define WEAPON_MAX_GROUP_IDS 256 // Passing a group_id of -1 means it isn't in a group. See weapon_create_group_id for more // help on weapon groups. int weapon_create( const vec3d *pos, const matrix *orient, int weapon_type, int parent_obj, int group_id=-1, bool is_locked = false, bool is_spawned = false, ship_weapon *src_swp = nullptr, int src_pbank = -1, int src_sbank = -1, const WeaponLaunchCurveData& launch_curve_data = WeaponLaunchCurveData { 0, 0.f, 0.f, 0.f }); void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_objnum, int target_is_locked = 0, ship_subsys *target_subsys = NULL); inline void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, tracking_info &tinfo) { weapon_set_tracking_info(weapon_objnum, parent_objnum, tinfo.objnum, tinfo.locked, tinfo.subsys); } // gets the substitution pattern pointer for a given weapon // returns [use_substitution, substituted_weapon_info_index] std::tuple get_weapon_substitution_tuple(int weapon_info_index, ship_weapon *swp, int pbank, int sbank, ship *shipp_to_check); bool weapon_armed(weapon *wp, bool hit_target); void maybe_play_conditional_impacts(const std::array, NumHitTypes>& impact_data, const object* weapon_objp, const object* impacted_objp, bool armed_weapon, int submodel, const vec3d* hitpos, const vec3d* local_hitpos = nullptr, const vec3d* hit_normal = nullptr); bool weapon_hit( object* weapon_obj, object* impacted_obj, const vec3d* hitpos, int quadrant = -1 ); void spawn_child_weapons( object *objp, int spawn_index_override = -1); // call to detonate a weapon. essentially calls weapon_hit() with other_obj as NULL, and sends a packet in multiplayer void weapon_detonate(object *objp); void weapon_area_apply_blast(const vec3d *force_apply_pos, object *ship_objp, const vec3d *blast_pos, float blast, bool make_shockwave); int weapon_area_calc_damage(const object *objp, const vec3d *pos, float inner_rad, float outer_rad, float max_blast, float max_damage, float *blast, float *damage, float limit); void find_homing_object_cmeasures(const SCP_vector &cmeasure_list); // THE FOLLOWING FUNCTION IS IN SHIP.CPP!!!! // JAS - figure out which thruster bitmap will get rendered next // time around. ship_render needs to have shipp->thruster_bitmap set to // a valid bitmap number, or -1 if we shouldn't render thrusters. // This does basically the same thing as ship_do_thruster_frame, except it // operates on a weapon. This is in the ship code because it needs // the same thruster animation info as the ship stuff, and I would // rather extern this one function than all the thruster animation stuff. void ship_do_weapon_thruster_frame( weapon *weaponp, object *objp, float frametime ); // call to get the "color" of the laser at the given moment (since glowing lasers can cycle colors) void weapon_get_laser_color(color *c, object *objp); void weapon_hit_do_sound(const object *hit_obj, const weapon_info *wip, const vec3d *hitpos, bool is_armed, int quadrant); void weapon_do_electronics_effect(const object *ship_objp, const vec3d *blast_pos, int wi_index); int weapon_get_random_player_usable_weapon(); // return a scale factor for damage which should be applied for 2 collisions float weapon_get_damage_scale(const weapon_info *wip, const object *wep, const object *target); // Pauses all running weapon sounds void weapon_pause_sounds(); // Unpauses all running weapon sounds void weapon_unpause_sounds(); // Called by hudartillery.cpp after SSMs have been parsed to make sure that $SSM: entries defined in weapons are valid. void validate_SSM_entries(); void shield_impact_explosion(const vec3d& hitpos, const vec3d& hitdir, const object *objp, const object *weapon_objp, float radius, particle::ParticleEffectHandle handle); // Swifty - return number of max simultaneous locks int weapon_get_max_missile_seekers(weapon_info *wip); // return if this weapon can lock on this target, based on its type, class, species or iff bool weapon_target_satisfies_lock_restrictions(weapon_info *wip, object* target); // return if this weapon has iff restrictions, and should ignore normal iff targeting restrictions bool weapon_has_iff_restrictions(weapon_info* wip); // whether secondary weapon wip on shooter is in range of target_world_pos bool weapon_secondary_world_pos_in_range(object* shooter, weapon_info* wip, vec3d* target_world_pos); // Return whether shooter is in range, fov, etc of target_subsys, for multilock // also returns the dot to the subsys in out_dot // While single target missiles will check these properties as well separately, this function is ONLY used by multilock bool weapon_multilock_can_lock_on_subsys(object* shooter, object* target, ship_subsys* target_subsys, weapon_info* wip, float* out_dot = nullptr); // Return whether shooter is in fov, etc of a target, for multilock // does NOT check range // also returns the dot to the subsys in out_dot // While single target missiles will check these properties as well separately, this function is ONLY used by multilock bool weapon_multilock_can_lock_on_target(object* shooter, object* target_objp, weapon_info* wip, float* out_dot = nullptr, bool checkWeapons = false); // Return whether the weapon has a target it is currently homing on bool weapon_has_homing_object(weapon* wp); // Variant type for weapon stat values: numeric, boolean, or string using weapon_stat_value = std::variant; // Returns a map of combat stats for a single weapon (keyed by column name). // Works for any weapon type (primary, secondary, beam). SCP_map weapon_get_stats(const weapon_info &wi); // Returns MediaVP-style human-readable text block for a single weapon. SCP_string weapon_get_stats_text(const weapon_info &wi); #endif