Compare commits

...
Author SHA1 Message Date
Tyler Blount 6b7fc0b6a3 Don't reset target restrictions during table load
Prevents an issue with load order causing target restrictions to be reset.
2020-05-20 14:45:24 -06:00
Tyler Blount 2e4bef0d09 Remove unneeded code and some style changes 2020-05-20 12:25:13 -06:00
Tyler Blount 172952cb0f Correctly implement fixes from #73 2020-05-20 12:25:13 -06:00
Tyler Blount 72c27bdbe8 CI and Lock fixes 2020-05-20 12:25:13 -06:00
Tyler Blount bd4de16007 Use correct comparison 2020-05-20 12:25:13 -06:00
Tyler Blount 2e560f2ddb Fix some CI errors 2020-05-20 12:25:12 -06:00
Tyler Blount d35d3d25f8 Finally fix no dumbfire
also some miscellaneous changes
2020-05-20 12:25:12 -06:00
Tyler Blount 2d5111f14b Perhaps this works? 2020-05-20 12:25:12 -06:00
Tyler Blount d95754d6b4 No Dumbfire fix
Another fix for MLMs associated with the No Dumbfire flag.
2020-05-20 12:25:12 -06:00
Tyler Blount 5e3d4c6cfd More fixes
Contains changes related to failed CI builds and feedback.
2020-05-20 12:25:12 -06:00
Tyler Blount deca820c9e Lost fixes
These were lost during a rebase at one point. Will leave this commit as separate to avoid issues in the future.
2020-05-20 12:25:12 -06:00
Swifty 21c298271b Initial commit of multilock missile features. 2020-05-20 12:25:11 -06:00
9 changed files with 1427 additions and 42 deletions

No files matched your search

+1050 -21
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -18,6 +18,7 @@ void hud_init_missile_lock();
void hud_calculate_lock_position(float frametime);
void hud_calculate_lock_start_pos();
void hud_do_lock_indicator(float frametime);
void hud_do_lock_indicators(float frametime);
void hud_stop_looped_locking_sounds();
void hud_lock_reset(float lock_time_scale=1.0f);
@@ -54,7 +55,9 @@ public:
void initLoopLockedAnim(bool loop);
void render(float frametime) override;
void renderOld(float frametime);
void renderLockTriangles(int center_x, int center_y, float frametime);
void renderLockTrianglesNew(int center_x, int center_y, int start_timestamp);
void renderLockTrianglesOld(int center_x, int center_y, int radius);
void pageIn() override;
void initialize() override;
+1 -2
View File
@@ -3431,6 +3431,7 @@ void hud_show_targeting_gauges(float frametime)
vec3d target_pos;
hud_show_hostile_triangle();
hud_do_lock_indicators(frametime);
if (Player_ai->target_objnum == -1)
return;
@@ -3480,8 +3481,6 @@ void hud_show_targeting_gauges(float frametime)
// display the lock indicator
if (!Player->target_is_dying) {
hud_do_lock_indicator(frametime);
// update and render artillery
hud_artillery_update();
hud_artillery_render();
+7 -1
View File
@@ -781,10 +781,16 @@ void obj_player_fire_stuff( object *objp, control_info ci )
// single player and multiplayer masters do all of the following
if ( !MULTIPLAYER_CLIENT ) {
if ( ci.fire_secondary_count ) {
ship_fire_secondary( objp );
if ( !ship_start_secondary_fire(objp) ) {
ship_fire_secondary( objp );
}
// kill the secondary count
ci.fire_secondary_count = 0;
} else {
if ( ship_stop_secondary_fire(objp) ) {
ship_fire_secondary( objp );
}
}
}
+185 -17
View File
@@ -11844,33 +11844,34 @@ int ship_fire_secondary( object *obj, int allow_swarm )
// Ensure if this is a "require-lock" missile, that a lock actually exists
if ( wip->wi_flags[Weapon::Info_Flags::No_dumbfire] ) {
if ( aip->current_target_is_locked <= 0 ) {
if ( obj == Player_obj ) {
if ( !Weapon_energy_cheat ) {
if (!aip->current_target_is_locked && !ship_lock_present(shipp) && !(shipp->missile_locks_firing.size() > 0)) {
if (obj == Player_obj) {
if (!Weapon_energy_cheat) {
float max_dist;
max_dist = wip->lifetime * wip->max_speed;
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm]){
max_dist= wip->lssm_lock_range;
if (wip->wi_flags[Weapon::Info_Flags::Local_ssm]) {
max_dist = wip->lssm_lock_range;
}
if ((aip->target_objnum != -1) && (vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > max_dist)) {
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Too far from target to acquire lock", 487));
if ((aip->target_objnum != -1) &&
(vm_vec_dist_quick(&obj->pos, &Objects[aip->target_objnum].pos) > max_dist)) {
HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Too far from target to acquire lock", 487));
} else {
char missile_name[NAME_LENGTH];
strcpy_s(missile_name, wip->get_display_string());
end_string_at_first_hash_symbol(missile_name);
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot fire %s without a lock", 488), missile_name);
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Cannot fire %s without a lock", 488), missile_name);
}
snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::OUT_OF_MISSLES)) );
swp->next_secondary_fire_stamp[bank] = timestamp(800); // to avoid repeating messages
snd_play(gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::OUT_OF_MISSLES)));
swp->next_secondary_fire_stamp[bank] = timestamp(800); // to avoid repeating messages
return 0;
}
} else {
// multiplayer clients should always fire the weapon here, so return only if not
// a multiplayer client.
if ( !MULTIPLAYER_CLIENT ) {
if (!MULTIPLAYER_CLIENT) {
return 0;
}
}
@@ -11901,16 +11902,21 @@ int ship_fire_secondary( object *obj, int allow_swarm )
}
}
if ( !allow_swarm && obj == Player_obj ) {
ship_queue_missile_locks(shipp);
}
// if trying to fire a swarm missile, make sure being called from right place
if ( (wip->wi_flags[Weapon::Info_Flags::Swarm]) && !allow_swarm ) {
Assert(wip->swarm_count > 0);
if(wip->swarm_count <= 0){
if (wip->multi_lock && obj == Player_obj) {
shipp->num_swarm_missiles_to_fire = (int)shipp->missile_locks_firing.size();
} else if(wip->swarm_count <= 0){
shipp->num_swarm_missiles_to_fire = SWARM_DEFAULT_NUM_MISSILES_FIRED;
} else {
shipp->num_swarm_missiles_to_fire = wip->swarm_count;
}
shipp->swarm_missile_bank = bank;
return 1; // Note: Missiles didn't get fired, but the frame interval code will fire them.
}
@@ -11919,7 +11925,11 @@ int ship_fire_secondary( object *obj, int allow_swarm )
if ( (wip->wi_flags[Weapon::Info_Flags::Corkscrew]) && !allow_swarm ) {
//phreak 11-9-02
//changed this from 4 to custom number defined in tables
shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)wip->cs_num_fired);
if (wip->multi_lock && obj == Player_obj) {
shipp->num_corkscrew_to_fire = (ubyte)shipp->missile_locks_firing.size();
} else {
shipp->num_corkscrew_to_fire = (ubyte)(shipp->num_corkscrew_to_fire + (ubyte)wip->cs_num_fired);
}
shipp->corkscrew_missile_bank = bank;
return 1; // Note: Missiles didn't get fired, but the frame interval code will fire them.
}
@@ -11933,7 +11943,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
t = Weapon_info[weapon_idx].fire_wait; // They can fire 5 times a second
swp->burst_counter[bank_adjusted] = 0;
}
swp->next_secondary_fire_stamp[bank] = timestamp((int) (t * 1000.0f));
swp->next_secondary_fire_stamp[bank] = timestamp((int)(t * 1000.0f));
swp->last_secondary_fire_stamp[bank] = timestamp();
// Here is where we check if weapons subsystem is capable of firing the weapon.
@@ -11961,6 +11971,33 @@ int ship_fire_secondary( object *obj, int allow_swarm )
return 0; // we can make a quick out here!!!
}
int target_objnum;
ship_subsys *target_subsys;
int locked;
if ( obj == Player_obj ) {
// use missile lock slots
if ( shipp->missile_locks_firing.size() > 0 ) {
lock_info lock_data = shipp->missile_locks_firing.back();
if ( wip->multi_lock ) {
shipp->missile_locks_firing.pop_back();
}
target_objnum = OBJ_INDEX(lock_data.obj);
target_subsys = lock_data.subsys;
locked = 1;
} else {
target_objnum = -1;
target_subsys = nullptr;
locked = 0;
}
} else {
target_objnum = aip->target_objnum;
target_subsys = aip->targeted_subsys;
locked = aip->current_target_is_locked;
}
num_slots = pm->missile_banks[bank].num_slots;
// determine if there is enough ammo left to fire weapons on this bank. As with primary
@@ -12071,7 +12108,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
// create the weapon -- for multiplayer, the net_signature is assigned inside
// of weapon_create
weapon_num = weapon_create( &firing_pos, &firing_orient, weapon_idx, OBJ_INDEX(obj), -1, aip->current_target_is_locked);
weapon_num = weapon_create( &firing_pos, &firing_orient, weapon_idx, OBJ_INDEX(obj), -1, locked);
if (weapon_num == -1) {
// Weapon most likely failed to fire
@@ -12083,7 +12120,7 @@ int ship_fire_secondary( object *obj, int allow_swarm )
if (weapon_num >= 0) {
weapon_idx = Weapons[Objects[weapon_num].instance].weapon_info_index;
weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), aip->target_objnum, aip->current_target_is_locked, aip->targeted_subsys);
weapon_set_tracking_info(weapon_num, OBJ_INDEX(obj), target_objnum, locked, target_subsys);
has_fired = true;
// create the muzzle flash effect
@@ -19228,3 +19265,134 @@ bool ship::is_arriving(ship::warpstage stage, bool dock_leader_or_single)
Assertion(false, "ship::is_arriving didn't handle all possible states; get a coder!");
return false;
}
void ship_clear_lock(lock_info *slot) {
vec3d zero_vec = ZERO_VECTOR;
slot->accumulated_x_pixels = 0;
slot->accumulated_y_pixels = 0;
slot->catch_up_distance = 0.0f;
slot->catching_up = 0;
slot->current_target_sx = -1;
slot->current_target_sy = -1;
slot->dist_to_lock = 0.0f;
slot->indicator_start_x = -1;
slot->indicator_start_y = -1;
slot->indicator_visible = false;
slot->indicator_x = -1;
slot->indicator_y = -1;
slot->last_dist_to_target = 0.0f;
slot->locked_timestamp = 0;
slot->maintain_lock_count = 0;
slot->need_new_start_pos = false;
slot->target_in_lock_cone = false;
slot->world_pos = zero_vec;
slot->locked = false;
slot->obj = nullptr;
slot->subsys = nullptr;
slot->time_to_lock = -1.0f;
}
void ship_queue_missile_locks(ship *shipp)
{
shipp->missile_locks_firing.clear();
// queue up valid missile locks
std::copy_if(shipp->missile_locks.begin(), shipp->missile_locks.end(), std::back_inserter(shipp->missile_locks_firing), [](lock_info lock) { return lock.locked; });
}
bool ship_lock_present(ship *shipp)
{
return std::any_of(shipp->missile_locks.begin(), shipp->missile_locks.end(), [](lock_info lock) { return lock.locked; });
}
bool ship_start_secondary_fire(object* objp)
{
Assert( objp != nullptr);
if ( objp->type != OBJ_SHIP ) {
return false;
}
int n = objp->instance;
Assert( n >= 0 && n < MAX_SHIPS );
Assert( Ships[n].objnum == OBJ_INDEX(objp) );
ship *shipp = &Ships[n];
ship_info *sip = &Ship_info[shipp->ship_info_index];
ship_weapon* swp = &shipp->weapons;
int bank = swp->current_secondary_bank;
if ( bank < 0 || bank >= sip->num_secondary_banks ) {
return false;
}
Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < weapon_info_size()) );
weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
if ( wip->trigger_lock ) {
swp->flags.set(Ship::Weapon_Flags::Trigger_Lock);
return true;
}
return false;
}
bool ship_stop_secondary_fire(object* objp)
{
Assert( objp != nullptr);
if ( objp->type != OBJ_SHIP ) {
return false;
}
int n = objp->instance;
if ( n < 0 || n >= MAX_SHIPS ) {
return false;
}
Assert( Ships[n].objnum == OBJ_INDEX(objp) );
ship *shipp = &Ships[n];
ship_info *sip = &Ship_info[shipp->ship_info_index];
ship_weapon *swp = &shipp->weapons;
int bank = swp->current_secondary_bank;
if ( bank < 0 || bank >= sip->num_secondary_banks ) {
return false;
}
Assert( (swp->secondary_bank_weapons[bank] >= 0) && (swp->secondary_bank_weapons[bank] < weapon_info_size()) );
weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[bank]];
if ( wip->trigger_lock && swp->flags[Ship::Weapon_Flags::Trigger_Lock]) {
swp->flags.remove(Ship::Weapon_Flags::Trigger_Lock);
return true;
}
return false;
}
+46
View File
@@ -31,6 +31,7 @@
#include "weapon/trails.h"
#include "ship/ship_flags.h"
#include "weapon/weapon_flags.h"
#include "ai/ai.h"
#include <string>
#include <particle/ParticleManager.h>
@@ -283,6 +284,36 @@ typedef struct cockpit_display_info {
int size[2];
} cockpit_display_info;
// structure to keep track of ship locks
typedef struct lock_info {
object *obj;
ship_subsys *subsys;
vec3d world_pos;
int current_target_sx;
int current_target_sy;
bool locked;
int maintain_lock_count;
int indicator_x;
int indicator_y;
int indicator_start_x;
int indicator_start_y;
bool indicator_visible;
float time_to_lock;
float dist_to_lock;
int catching_up;
float catch_up_distance;
float last_dist_to_target;
double accumulated_x_pixels;
double accumulated_y_pixels;
bool need_new_start_pos;
bool target_in_lock_cone;
int locked_timestamp;
} lock_info;
// structure definition for a linked list of subsystems for a ship. Each subsystem has a pointer
// to the static data for the subsystem. The obj_subsystem data is defined and read in the model
// code. Other dynamic data (such as current_hits) should remain in this structure.
@@ -560,6 +591,9 @@ public:
vec3d wash_rot_axis;
int wash_timestamp;
SCP_vector<lock_info> missile_locks;
SCP_vector<lock_info> missile_locks_firing;
int num_swarm_missiles_to_fire; // number of swarm missiles that need to be launched
int next_swarm_fire; // timestamp of next swarm missile to fire
int next_swarm_path; // next path number for swarm missile to take
@@ -1409,6 +1443,9 @@ extern bool in_autoaim_fov(ship *shipp, int bank_to_fire, object *obj);
extern int ship_stop_fire_primary(object * obj);
extern int ship_fire_primary(object * objp, int stream_weapons, int force = 0);
extern int ship_fire_secondary(object * objp, int allow_swarm = 0 );
bool ship_start_secondary_fire(object* objp);
bool ship_stop_secondary_fire(object* objp);
extern int ship_launch_countermeasure(object *objp, int rand_val = -1);
// for special targeting lasers
@@ -1800,4 +1837,13 @@ extern void toggle_ignore_list_flag(Ship::Ship_Flags flag);
ship_subsys* ship_get_subsys_for_submodel(ship* shipp, int submodel);
// Clears a lock_info struct with defaults
void ship_clear_lock(lock_info *slot);
// queues up locks
void ship_queue_missile_locks(ship *shipp);
// snoops missile locks to see if any are ready to fire.
bool ship_lock_present(ship *shipp);
#endif
+1
View File
@@ -9,6 +9,7 @@ namespace Ship {
Beam_Free = 0, // if this is a beam weapon, its free to fire
Turret_Lock, // is this turret is free to fire or locked
Tagged_Only, // only fire if target is tagged
Trigger_Lock, // // indicates that the trigger is held down
NUM_VALUES
};
+30 -1
View File
@@ -38,6 +38,15 @@ extern int Num_weapon_subtypes;
#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
//particle names go here -nuke
#define PSPEW_NONE -1 //used to disable a spew, useful for xmts
#define PSPEW_DEFAULT 0 //std fs2 pspew
@@ -315,11 +324,24 @@ struct weapon_info
short swarm_count; // how many swarm missiles are fired for this weapon
int SwarmWait; // *Swarm firewait, default is 150 -Et1
int target_restrict;
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<int> ship_type_restrict;
SCP_vector<SCP_string> ship_type_restrict_temp;
bool trigger_lock; // Trigger must be held down and released to lock and fire.
bool launch_reset_locks; // Lock indicators reset after firing
// 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
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;
@@ -629,4 +651,11 @@ void validate_SSM_entries();
void shield_impact_explosion(vec3d *hitpos, object *objp, float radius, int idx);
// Swifty - return number of max simultaneous locks
int weapon_get_max_missile_seekers(weapon_info *wip);
// return if this weapon can lock on this ship type
bool weapon_can_lock_on_ship_type(weapon_info *wip, int ship_type);
#endif
+104
View File
@@ -1398,6 +1398,56 @@ int parse_weapon(int subtype, bool replace, const char *filename)
}
}
if (optional_string("+Target Lock Restriction:")) {
if (optional_string("current target, any subsystem")) {
wip->target_restrict = LR_CURRENT_TARGET_SUBSYS;
}
else if (optional_string("any target")) {
wip->target_restrict = LR_ANY_TARGETS;
}
else if (optional_string("current target only")) {
wip->target_restrict = LR_CURRENT_TARGET;
}
else {
wip->target_restrict = LR_CURRENT_TARGET;
}
}
if (optional_string("+Independent Seekers:")) {
stuff_boolean(&wip->multi_lock);
}
if (optional_string("+Trigger Hold:")) {
stuff_boolean(&wip->trigger_lock);
}
if (optional_string("+Reset On Launch:")) {
stuff_boolean(&wip->launch_reset_locks);
}
if (optional_string("+Max Seekers Per Target:")) {
stuff_int(&wip->max_seekers_per_target);
}
if (optional_string("+Max Active Seekers:")) {
stuff_int(&wip->max_seeking);
}
if (optional_string("+Ship Types:")) {
stuff_string_list(wip->ship_type_restrict_temp);
}
if (wip->is_locked_homing()) {
// locked homing missiles have a much longer lifespan than the AI think they do
wip->max_lifetime = wip->lifetime * LOCKED_HOMING_EXTENDED_LIFE_FACTOR;
@@ -3599,6 +3649,7 @@ void weapon_close()
void weapon_level_init()
{
int i;
extern int ships_inited;
// Reset everything between levels
Num_weapons = 0;
@@ -3610,6 +3661,19 @@ void weapon_level_init()
for (i = 0; i < weapon_info_size(); i++) {
Weapon_info[i].damage_type_idx = Weapon_info[i].damage_type_idx_sav;
Weapon_info[i].shockwave.damage_type_idx = Weapon_info[i].shockwave.damage_type_idx_sav;
if ( ships_inited ) {
// populate ship type lock restrictions
for ( int j = 0; j < (int)Weapon_info[i].ship_type_restrict_temp.size(); ++j ) {
int idx = ship_type_name_lookup((char*)Weapon_info[i].ship_type_restrict_temp[j].c_str());
if ( idx >= 0 ) {
Weapon_info[i].ship_type_restrict.push_back(idx);
}
}
Weapon_info[i].ship_type_restrict_temp.clear();
}
}
trail_level_init(); // reset all missile trails
@@ -7700,12 +7764,25 @@ void weapon_info::reset()
// *Default is 150 -Et1
this->SwarmWait = SWARM_MISSILE_DELAY;
this->target_restrict = LR_CURRENT_TARGET;
this->multi_lock = false;
this->trigger_lock = false;
this->launch_reset_locks = false;
this->max_seeking = 1;
this->max_seekers_per_target = 1;
this->ship_type_restrict.clear();
this->ship_type_restrict_temp.clear();
this->acquire_method = WLOCK_PIXEL;
this->min_lock_time = 0.0f;
this->lock_pixels_per_sec = 50;
this->catchup_pixels_per_sec = 50;
this->catchup_pixel_penalty = 50;
this->fov = 0; //should be cos(pi), not pi
this->seeker_strength = 1.0f;
this->lock_fov = 0.85f;
this->pre_launch_snd = gamesnd_id();
this->pre_launch_snd_min_interval = 0;
@@ -8094,3 +8171,30 @@ void weapon_spew_stats()
}
#endif
}
// Given a weapon, figure out how many independent locks we can have with it.
int weapon_get_max_missile_seekers(weapon_info *wip)
{
int max_target_locks;
if ( wip->multi_lock ) {
if ( wip->wi_flags[Weapon::Info_Flags::Swarm] ) {
max_target_locks = wip->swarm_count;
} else if ( wip->wi_flags[Weapon::Info_Flags::Corkscrew] ) {
max_target_locks = wip->cs_num_fired;
} else {
max_target_locks = 1;
}
} else {
max_target_locks = 1;
}
return max_target_locks;
}
bool weapon_can_lock_on_ship_type(weapon_info *wip, int ship_type)
{
// Determine if there are any restrictions, treating an empty list as true
return (wip->ship_type_restrict.size() == 0) ||
std::any_of(wip->ship_type_restrict.begin(), wip->ship_type_restrict.end(), [ship_type](int type) { return type == ship_type; });
}