Compare commits

...
Author SHA1 Message Date
JohnAFernandez 0feab10244 Upgrade velocity compression in OO packet
Previously, the velocity could be off by up to 1 m/s.  This gives that packing function one more byte to work with, so that we have a lot more accuracy to work with.
2021-03-20 06:49:11 -04:00
JohnAFernandez b342ec50eb Properly Set net_signatures for wing ships
Previously wing ships were being given a net signature that was one too large.
2021-03-20 06:49:11 -04:00
Goober5000 61ef36b238 allow two SEXPs to be used in multiplayer that previously couldn't be
1) The ship-vanish SEXP (and script function) was already multiplayer-ready
2) The is-ship-visible SEXP was enhanced to add a second argument, which defaults to the player's ship in single-player mode
2021-03-20 06:49:11 -04:00
JohnAFernandez fe84d11025 Hopefully Temporary Disable Support ship in TvT
Support ship is not fully support per-team.  Logic for TvT has to be added otherwise players can "steal" each othere's support ships during the mission.
2021-03-20 06:49:11 -04:00
JohnAFernandez 735e00d839 Fix Missile Syncing Between Server and Client 2021-03-20 06:49:11 -04:00
JohnAFernandez 8bbe4733fb Fix implementation of multi_pack_unpack_desired_vel_and_desired_rotvel 2021-03-19 23:34:15 -04:00
JohnAFernandez 56f7dc2f8c Multi Version bump for 21_2
since many of the fixes and features require a multi bump.
2021-03-19 23:33:50 -04:00
12 changed files with 217 additions and 58 deletions

No files matched your search

+8 -3
View File
@@ -4236,9 +4236,13 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int spec
// also, if multiplayer, set the parse object's net signature to be wing's net signature
// base + total_arrived_count (before adding 1)
if (Game_mode & GM_MULTIPLAYER)
// Cyborg -- The original ships in the wave have their net_signature set at mission parse
// so only do this if this is a subsequent wave.
if (Game_mode & GM_MULTIPLAYER && wingp->num_waves > 1)
{
p_objp->net_signature = (ushort) (wingp->net_signature + wingp->total_arrived_count);
// Cyborg -- Also, then we need to subtract the original wave's number of fighters
// and also subtract 1 to use the wing's starting signature
p_objp->net_signature = (ushort) (wingp->net_signature + wingp->total_arrived_count - (wingp->wave_count + 1));
}
@@ -4683,7 +4687,8 @@ void parse_wing(mission *pm)
int next_signature;
wingp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
next_signature = wingp->net_signature + (wingp->wave_count * wingp->num_waves);
// Cyborg -- Subtract one because the original wave already has its signatures set.
next_signature = wingp->net_signature + (wingp->wave_count * (wingp->num_waves - 1));
if ( next_signature > SHIP_SIG_MAX )
Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX);
multi_set_network_signature( (ushort)next_signature, MULTI_SIG_SHIP );
+4
View File
@@ -569,6 +569,10 @@ void process_packet_normal(ubyte* data, header *header_info)
process_ship_kill_packet( data, header_info );
break;
case WEAPON_KILL:
process_weapon_kill_packet(data, header_info);
break;
case WING_CREATE:
process_wing_create_packet( data, header_info );
break;
+3 -1
View File
@@ -66,9 +66,10 @@ class player;
// version 51 - 9/20/2020 Object Update Packet Upgrade: Waypoints, subsystem rotation, bandwidth improvements, bugfixes
// version 52 - 10/9/2020 Dumbfire Rollback, increases accuracy of high ping, or delayed packet primary fire for clients.
// version 53 - 12/2/2020 big set of packet fixes/upgrades
// version 54 - datepending - Fixes and features for FSO 21_2
// STANDALONE_ONLY
#define MULTI_FS_SERVER_VERSION 53
#define MULTI_FS_SERVER_VERSION 54
#define MULTI_FS_SERVER_COMPATIBLE_VERSION MULTI_FS_SERVER_VERSION
@@ -252,6 +253,7 @@ class player;
#define REINFORCEMENT_AVAIL 0xDB // a reinforcement is available
#define LIGHTNING_PACKET 0xDC // lightning bolt packet for multiplayer nebula
#define BYTES_SENT 0xDD // how much data we've sent/received
#define WEAPON_KILL 0xDE // get rid of this weapon on the client.
#define GAME_ACTIVE 0xE1 // info on an active game server
#define GAME_QUERY 0xE2 // request for a list of active game servers
+122 -11
View File
@@ -2700,6 +2700,75 @@ void process_ship_kill_packet( ubyte *data, header *hinfo )
}
}
constexpr int WK_FLAGS_WEAPON_WEAPON = (1<<0);
//simply removes the weapon from existence on the client.
void send_weapon_kill_packet(object* objp)
{
Assert(objp != nullptr);
Assert(objp->type == OBJ_WEAPON);
Assert(MULTIPLAYER_MASTER);
if (objp == nullptr || objp->type != OBJ_WEAPON || MULTIPLAYER_CLIENT) {
return;
}
ubyte data[MAX_PACKET_SIZE], flags = 0;
int packet_size;
BUILD_HEADER(WEAPON_KILL);
ADD_USHORT(objp->net_signature);
if (objp->instance > -1) {
if (Weapons[objp->instance].weapon_flags[Weapon::Weapon_Flags::Destroyed_by_weapon])
flags |= WK_FLAGS_WEAPON_WEAPON;
}
ADD_DATA(flags);
multi_io_send_to_all_reliable(data, packet_size);
}
void process_weapon_kill_packet(ubyte *data, header *hinfo )
{
// only masters should receive this packet.
Assert(MULTIPLAYER_CLIENT);
int offset = HEADER_LENGTH;
ushort missile_net_signature;
ubyte flags;
GET_USHORT(missile_net_signature);
GET_DATA(flags);
PACKET_SET_SIZE();
// get the object pointer for the missile
object* missile = multi_get_network_object(missile_net_signature);
// make sure it's valid before implementing.
if (missile == nullptr || missile->type != OBJ_WEAPON || missile->instance < 0) {
return;
}
// set any optional packets.
if (flags & WK_FLAGS_WEAPON_WEAPON) {
Weapons[missile->instance].weapon_flags.set(Weapon::Weapon_Flags::Destroyed_by_weapon);
}
Weapons[missile->instance].lifeleft = -1.0f;
if (Weapon_info[Weapons[missile->instance].weapon_info_index].weapon_hitpoints > 0.0f) {
missile->hull_strength = -1.0f;
}
// kill the weapon!
if (Weapon_info[Weapons[missile->instance].weapon_info_index].subtype & WP_MISSILE) {
weapon_detonate(missile);
}
else {
missile->flags.set(Object::Object_Flags::Should_be_dead);
}
}
// send a packet indicating a ship should be created
void send_ship_create_packet( object *objp, bool is_support )
{
@@ -7428,11 +7497,12 @@ void process_debrief_info( ubyte *data, header *hinfo )
debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
}
constexpr ubyte HWIF_BIG_UPDATE = (1 << 0);
// sends homing information to all clients. We only need signature and num_missiles (because of hornets).
// sends homing_object and homing_subsystem to all clients.
void send_homing_weapon_info( int weapon_num )
{
ubyte data[MAX_PACKET_SIZE];
ubyte data[MAX_PACKET_SIZE], flags = 0;
int s_index;
int packet_size;
object *homing_object;
@@ -7461,11 +7531,26 @@ void send_homing_weapon_info( int weapon_num )
}
}
if (!wp->weapon_flags[Weapon::Weapon_Flags::Multi_Update_Sent] || IS_MAT_NULL(&Objects[wp->objnum].phys_info.ai_desired_orient)) {
flags |= HWIF_BIG_UPDATE;
wp->weapon_flags.set(Weapon::Weapon_Flags::Multi_Update_Sent);
}
BUILD_HEADER(HOMING_WEAPON_UPDATE);
ADD_DATA(flags);
ADD_USHORT( Objects[wp->objnum].net_signature );
ADD_USHORT( homing_signature );
ADD_SHORT( static_cast<short>(s_index) );
ADD_VECTOR( wp->homing_pos);
if (flags & HWIF_BIG_UPDATE) {
fix current_lifetime = Missiontime - wp->creation_time;
ADD_DATA(current_lifetime);
ADD_VECTOR(Objects[wp->objnum].pos);
ADD_FLOAT(wp->launch_speed);
ADD_ORIENT(Objects[wp->objnum].orient);
}
multi_io_send_to_all(data, packet_size);
}
@@ -7473,23 +7558,37 @@ void send_homing_weapon_info( int weapon_num )
// packet contains information for multiple weapons (like hornets).
void process_homing_weapon_info( ubyte *data, header *hinfo )
{
ubyte flags;
int offset;
fix missile_lifetime;
ushort weapon_signature, homing_signature;
short h_subsys;
float launch_speed;
vec3d missile_pos, homing_goal;
object *homing_object, *weapon_objp;
weapon *wp;
matrix orient_in;
offset = HEADER_LENGTH;
// get the data for the packet
GET_DATA(flags);
GET_USHORT( weapon_signature );
GET_USHORT( homing_signature );
GET_SHORT( h_subsys );
GET_VECTOR( homing_goal );
if (flags & HWIF_BIG_UPDATE) {
GET_DATA(missile_lifetime);
GET_VECTOR(missile_pos);
GET_FLOAT(launch_speed);
GET_ORIENT(orient_in);
}
PACKET_SET_SIZE();
// deal with changing this weapons homing information
weapon_objp = multi_get_network_object( weapon_signature );
if ( weapon_objp == NULL ) {
if ( weapon_objp == nullptr ) {
nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
return;
}
@@ -7498,31 +7597,43 @@ void process_homing_weapon_info( ubyte *data, header *hinfo )
// be sure that we can find these weapons and
homing_object = multi_get_network_object( homing_signature );
if ( homing_object == NULL ) {
if ( homing_object == nullptr ) {
wp->homing_object = &obj_used_list;
wp->homing_subsys = nullptr;
wp->target_num = -1;
wp->target_sig = -1;
nprintf(("Network", "Couldn't find homing object for homing update\n"));
return;
}
}
if ( homing_object->type == OBJ_WEAPON ) {
auto flags = Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags;
auto flags_check = Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags;
// Assert( (flags & WIF_BOMB) || (flags & WIF_CMEASURE) );
if ( !((flags[Weapon::Info_Flags::Bomb, Weapon::Info_Flags::Cmeasure])) ) {
if ( !((flags_check[Weapon::Info_Flags::Bomb, Weapon::Info_Flags::Cmeasure])) ) {
nprintf(("Network", "Homing object is invalid for homing update\n"));
return;
}
}
wp->homing_object = homing_object;
wp->homing_subsys = NULL;
wp->homing_subsys = nullptr;
wp->target_num = OBJ_INDEX(homing_object);
wp->target_sig = homing_object->signature;
wp->homing_pos = homing_goal;
if ( h_subsys != -1 ) {
Assert( homing_object->type == OBJ_SHIP );
wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
}
if (flags & HWIF_BIG_UPDATE) {
wp->creation_time = Missiontime + missile_lifetime;
weapon_objp->pos = missile_pos;
weapon_objp->orient = orient_in;
wp->launch_speed = launch_speed;
}
if ( homing_object->type == OBJ_SHIP ) {
nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
}
+5
View File
@@ -295,6 +295,11 @@ void send_object_update_packet(int force_all = 0);
// send a packet indicating a ship has been killed
void send_ship_kill_packet( object *ship_obj, object *other_objp, float percent_killed, int self_destruct );
// send a packet indicating that a weapon collided
void send_weapon_kill_packet(object* objp);
void process_weapon_kill_packet(ubyte* data, header* hinfo);
// send a packet indicating a wing of ships should be created
void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count );
+30 -21
View File
@@ -3522,25 +3522,25 @@ int multi_pack_unpack_vel( int write, ubyte *data, matrix *orient, physics_info
f = vm_vec_dot( &orient->vec.fvec, &pi->vel );
// Cyborg17 - using round here allows us keep part of the decimal accuracy that would have been dropped with just fl2i
a = fl2i(round(r * 2.0f));
b = fl2i(round(u * 2.0f));
c = fl2i(round(f * 4.0f));
CAP(a,-512,511);
CAP(b,-512,511);
CAP(c,-2048,2047);
bitbuffer_put( &buf, (uint)a, 10 );
bitbuffer_put( &buf, (uint)b, 10 );
bitbuffer_put( &buf, (uint)c, 12 );
a = fl2i(round(r * 16.0f));
b = fl2i(round(u * 16.0f));
c = fl2i(round(f * 32.0f));
CAP(a,-2048,2047);
CAP(b,-2048,2047);
CAP(c,-4096,4095);
bitbuffer_put( &buf, (uint)a, 13 );
bitbuffer_put( &buf, (uint)b, 13 );
bitbuffer_put( &buf, (uint)c, 14 );
return bitbuffer_write_flush(&buf);
} else {
// unpack velocity
a = bitbuffer_get_signed(&buf,10);
b = bitbuffer_get_signed(&buf,10);
c = bitbuffer_get_signed(&buf,12);
r = i2fl(a)/2.0f;
u = i2fl(b)/2.0f;
f = i2fl(c)/4.0f;
a = bitbuffer_get_signed(&buf,13);
b = bitbuffer_get_signed(&buf,13);
c = bitbuffer_get_signed(&buf,14);
r = i2fl(a)/16.0f;
u = i2fl(b)/16.0f;
f = i2fl(c)/32.0f;
// Convert into world coordinates
vm_vec_zero(&pi->vel);
@@ -3600,6 +3600,11 @@ int multi_pack_unpack_desired_vel_and_desired_rotvel( int write, bool full_physi
int a = 0, b = 0, c = 0;
int d = 0, e = 0, f = 0;
// we could have reduced it to 8 when full physics is false, and saved the byte,
// but this ends up allowing us more accuracy during capship warpin/out
const int z_bitcount = (full_physics) ? 9 : 16;
const int z_capfactor = (full_physics) ? 255 : 32640;
if ( write ) {
if (full_physics) {
// output desired rotational velocity
@@ -3632,18 +3637,19 @@ int multi_pack_unpack_desired_vel_and_desired_rotvel( int write, bool full_physi
if (pi->max_vel.xyz.y > 0.0f) {
e = fl2i(round( (local_desired_vel->xyz.y / pi->max_vel.xyz.y) * 7.0f));
}
float z_divisor = MAX(pi->afterburner_max_vel.xyz.z, pi->max_vel.xyz.z);
// for z velocity, take into account afterburner.
if (pi->max_vel.xyz.z > 0.0f) {
f = fl2i(round( (local_desired_vel->xyz.z / pi->afterburner_max_vel.xyz.z) * 255.0f));
if (z_divisor > 0.0f) {
f = fl2i(round( (local_desired_vel->xyz.z / z_divisor) * 255.0f));
}
CAP(d,-7,7);
CAP(e,-7,7);
CAP(f,-255,255);
CAP(f,-z_capfactor,z_capfactor);
bitbuffer_put( &buf, (uint)d,4);
bitbuffer_put( &buf, (uint)e,4);
bitbuffer_put( &buf, (uint)f,9);
bitbuffer_put( &buf, (uint)f,z_bitcount); // either 9 or 16 bits.
return bitbuffer_write_flush(&buf);
} else {
@@ -3662,10 +3668,13 @@ int multi_pack_unpack_desired_vel_and_desired_rotvel( int write, bool full_physi
// unpack desired velocity
d = bitbuffer_get_signed(&buf,4);
e = bitbuffer_get_signed(&buf,4);
f = bitbuffer_get_signed(&buf,9);
f = bitbuffer_get_signed(&buf,z_bitcount); // either 9 or 16 bits.
local_desired_vel->xyz.x = pi->max_vel.xyz.x * i2fl(d)/7.0f;
local_desired_vel->xyz.y = pi->max_vel.xyz.y * i2fl(e)/7.0f;
local_desired_vel->xyz.z = pi->afterburner_max_vel.xyz.z * i2fl(f)/255.0f;
float z_multiplier = MAX(pi->afterburner_max_vel.xyz.z, pi->max_vel.xyz.z);
local_desired_vel->xyz.z = z_multiplier * i2fl(f)/255.0f;
return bitbuffer_read_flush(&buf);
+2 -1
View File
@@ -94,7 +94,8 @@ int collide_weapon_weapon( obj_pair * pair )
bool b_override = Script_system.IsConditionOverride(CHA_COLLIDEWEAPON, B);
Script_system.RemHookVars({ "Self", "Object", "Weapon", "WeaponB", "Hitpos" });
if(!a_override && !b_override)
// damage calculation should not be done on clients, the server will tell the client version of the bomb when to die
if(!a_override && !b_override && !MULTIPLAYER_CLIENT)
{
float aDamage = wipA->damage;
if (wipB->armor_type_idx >= 0)
+3 -8
View File
@@ -6834,14 +6834,9 @@ int sexp_is_ship_visible(int node)
if (viewer_entry)
viewer_shipp = viewer_entry->shipp;
}
else
{
// if the second argument is not supplied, default to the player, per retail
if (Game_mode & GM_MULTIPLAYER)
Warning(LOCATION, "In multiplayer, is-ship-visible must have two arguments!");
else
viewer_shipp = Player_ship;
}
// if the second argument is not supplied, and we're single-player, default to the player
else if (!(Game_mode & GM_MULTIPLAYER))
viewer_shipp = Player_ship;
// get ship's *radar* visiblity
if (viewer_shipp)
+8
View File
@@ -231,6 +231,10 @@ ADE_VIRTVAR(HomingPosition, l_Weapon, "vector", "Position that weapon will home
{
wp->homing_pos = vmd_zero_vector;
}
// need to update the position for multiplayer.
if (Game_mode & GM_MULTIPLAYER) {
wp->weapon_flags.set(Weapon::Weapon_Flags::Multi_homing_update_needed);
}
}
return ade_set_args(L, "o", l_Vector.Set(wp->homing_pos));
@@ -269,6 +273,10 @@ ADE_VIRTVAR(HomingSubsystem, l_Weapon, "subsystem", "Subsystem that weapon will
wp->homing_pos = vmd_zero_vector;
wp->homing_subsys = NULL;
}
// need to update the position for multiplayer.
if (Game_mode & GM_MULTIPLAYER) {
wp->weapon_flags.set(Weapon::Weapon_Flags::Multi_homing_update_needed);
}
}
return ade_set_args(L, "o", l_Subsystem.Set(ship_subsys_h(wp->homing_object, wp->homing_subsys)));
+3 -2
View File
@@ -17286,10 +17286,11 @@ int is_support_allowed(object *objp, bool do_simple_check)
{
// multiplayer version behaves differently. Depending on mode:
// 1) coop mode -- only available to friendly
// 2) team v team mode -- availble to either side
// 2) team v team mode -- Cyborg17 Has an incomplete implementation for now. Enemy teams can basically "steal" the opponent's SS and break multi.
// A fix is on the todo list.
// 3) dogfight -- never
if(Netgame.type_flags & NG_TYPE_DOGFIGHT)
if(!MULTI_COOP)
{
return 0;
}
+2 -1
View File
@@ -99,9 +99,10 @@ namespace Weapon {
Locked_when_fired, // fired with a lock
Destroyed_by_weapon, // destroyed by damage from other weapon
Spawned, //Spawned from a spawning type weapon
Homing_update_needed, // this is a newly spawned homing weapon which needs to update client machines
No_homing, // this weapon should ignore any homing behavior it'd usually have
Overridden_homing, // Homing is overridden by an external source (probably scripting)
Multi_homing_update_needed, // this is a newly spawned homing weapon which needs to update client machines
Multi_Update_Sent, // Marks this missile as already being updated once by the server
NUM_VALUES
};
+27 -10
View File
@@ -4484,10 +4484,6 @@ void weapon_home(object *obj, int num, float frame_time)
return;
}
}
else if (MULTIPLAYER_MASTER && (wip->is_locked_homing()) && (wp->weapon_flags[Weapon::Weapon_Flags::Homing_update_needed])) {
wp->weapon_flags.remove(Weapon::Weapon_Flags::Homing_update_needed);
send_homing_weapon_info(num);
}
if (wip->acceleration_time > 0.0f) {
if (Missiontime - wp->creation_time < fl2f(wip->acceleration_time)) {
@@ -4703,7 +4699,8 @@ void weapon_home(object *obj, int num, float frame_time)
if (hobjp->radius < 40.0f) {
target_pos = hobjp->pos;
wp->homing_pos = target_pos;
} else if (pick_homing_point || (dist < 500.0f) || (rand_chance(flFrametime, 2.0f))) {
// only recalculate a homing point if you are not a client. You will get the new point from the server.
} else if (pick_homing_point || (!MULTIPLAYER_CLIENT && ((dist < 500.0f) || (rand_chance(flFrametime, 2.0f))))) {
if (hobjp->type == OBJ_SHIP) {
if (!pick_homing_point) {
@@ -4871,8 +4868,24 @@ void weapon_update_missiles(object* obj, float frame_time) {
// a single player or multiplayer server function -- it affects actual weapon movement.
if (wip->is_homing() && !(wp->weapon_flags[Weapon::Weapon_Flags::No_homing])) {
vec3d pos_hold = wp->homing_pos;
int target_hold = wp->target_sig;
weapon_home(obj, obj->instance, frame_time);
// tell the server to send an update of the missile.
if (MULTIPLAYER_MASTER && !IS_VEC_NULL(&wp->homing_pos)) {
wp->weapon_flags.set(Weapon::Weapon_Flags::Multi_homing_update_needed);
// if the first update for a weapon has already been sent, then we do not need to send any others unless the homing_pos
// drastically changes.
if (wp->weapon_flags[Weapon::Weapon_Flags::Multi_Update_Sent]) {
vm_vec_sub2(&pos_hold, &wp->homing_pos);
if ((vm_vec_mag(&pos_hold) < 1.0f) && (wp->target_sig == target_hold)) {
wp->weapon_flags.remove(Weapon::Weapon_Flags::Multi_homing_update_needed);
}
}
}
// If this is a swarm type missile,
if (wp->swarm_info_ptr != nullptr) {
swarm_update_direction(obj, wp->swarm_info_ptr.get());
@@ -5133,9 +5146,13 @@ void weapon_process_post(object * obj, float frame_time)
if ( wp->lifeleft < 0.0f ) {
if ( wip->subtype & WP_MISSILE ) {
if(Game_mode & GM_MULTIPLAYER){
if ( !MULTIPLAYER_CLIENT || (MULTIPLAYER_CLIENT && (wp->lifeleft < -2.0f)) || (MULTIPLAYER_CLIENT && (wip->wi_flags[Weapon::Info_Flags::Child]))) { // don't call this function multiplayer client -- host will send this packet to us
if ( !MULTIPLAYER_CLIENT || (MULTIPLAYER_CLIENT && (wip->wi_flags[Weapon::Info_Flags::Child]))) { // don't call this function multiplayer client -- host will send this packet to us
weapon_detonate(obj);
}
if (MULTIPLAYER_MASTER) {
send_weapon_kill_packet(obj);
}
} else {
weapon_detonate(obj);
}
@@ -5145,9 +5162,12 @@ void weapon_process_post(object * obj, float frame_time)
} else {
obj->flags.set(Object::Object_Flags::Should_be_dead);
}
return;
}
else if ((MULTIPLAYER_MASTER) && (wip->is_locked_homing() && wp->weapon_flags[Weapon::Weapon_Flags::Multi_homing_update_needed])) {
send_homing_weapon_info(obj->instance);
wp->weapon_flags.remove(Weapon::Weapon_Flags::Multi_homing_update_needed);
}
// plot homing missiles on the radar
if (((wip->wi_flags[Weapon::Info_Flags::Bomb]) || (wip->wi_flags[Weapon::Info_Flags::Shown_on_radar])) && !(wip->wi_flags[Weapon::Info_Flags::Dont_show_on_radar])) {
@@ -5551,9 +5571,6 @@ void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_o
// put a sanity check in the color changing laser code that was broken by this code.
if (target_is_locked && (wp->target_num != -1) && (wip->is_locked_homing()) ) {
wp->lifeleft *= LOCKED_HOMING_EXTENDED_LIFE_FACTOR;
if (MULTIPLAYER_MASTER) {
wp->weapon_flags.set(Weapon::Weapon_Flags::Homing_update_needed);
}
}
ai_update_danger_weapon(target_objnum, weapon_objnum);