boiii-scripts/shared/weapons/multilockapguidance.gsc
2023-04-13 17:30:38 +02:00

495 lines
19 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\array_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\flagsys_shared;
#using scripts\shared\hud_util_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#using scripts\shared\abilities\_ability_util;
#precache( "string", "MP_CANNOT_LOCKON_TO_TARGET" );
#namespace multilockap_guidance;
//#define MAX_SIMULTANEOUS_LOCKONS 5 this number corresponds to a code define
function autoexec __init__sytem__() { system::register("multilockap_guidance",&__init__,undefined,undefined); }
function __init__()
{
callback::on_spawned( &on_player_spawned );
SetDvar( "scr_max_simLocks",3 );
}
function on_player_spawned()
{
self endon( "disconnect" );
self ClearAPTarget();
thread APToggleLoop();
self thread APFiredNotify();
}
function ClearAPTarget( weapon, whom )
{
if (!IsDefined(self.multiLockList))
{
self.multiLockList=[];
}
if (IsDefined(whom))
{
for(i=0;i<self.multiLockList.size;i++)
{
if (whom.apTarget == self.multiLockList[i].apTarget)
{
if (isdefined(self.multiLockList[i].apTarget))
{
self.multiLockList[i].apTarget notify( "missile_unlocked" );
}
self notify( "stop_sound"+whom.apSoundId );
self WeaponLockRemoveSlot(i);
ArrayRemoveValue(self.multiLockList,whom,false);
break;
}
}
}
else
{
for(i=0;i<self.multiLockList.size;i++)
{
self.multiLockList[i].apTarget notify( "missile_unlocked" );
self notify( "stop_sound"+self.multiLockList[i].apSoundId );
}
self.multiLockList = [];
}
if (self.multiLockList.size==0)
{
self StopRumble( "stinger_lock_rumble" );
self WeaponLockRemoveSlot(-1);
if (IsDefined(weapon))
{
if (IsDefined(weapon.lockonSeekerSearchSound))
self StopLocalSound( weapon.lockonSeekerSearchSound );
if (IsDefined(weapon.lockonSeekerLockedSound))
self StopLocalSound( weapon.lockonSeekerLockedSound );
}
self DestroyLockOnCanceledMessage();
}
}
function APFiredNotify()
{
self endon( "disconnect" );
self endon ( "death" );
while ( true )
{
self waittill( "missile_fire", missile, weapon );
if ( weapon.lockonType == "AP Multi" )
{
foreach(target in self.multiLockList)
{
if( isdefined(target.apTarget) && target.apLockFinalized )
{
target.apTarget notify( "stinger_fired_at_me", missile, weapon, self );
}
}
}
}
}
function APToggleLoop()
{
self endon( "disconnect" );
self endon ( "death" );
for (;;)
{
self waittill( "weapon_change", weapon );
while ( weapon.lockonType == "AP Multi" )
{
abort = false;
while( !( self PlayerAds() == 1.0 ) )
{
{wait(.05);};
currentWeapon = self GetCurrentWeapon();
if ( currentWeapon.lockonType != "AP Multi" )
{
abort = true;
break;
}
}
if ( abort )
{
break;
}
self thread APLockLoop(weapon);
while( ( self PlayerAds() == 1.0 ) )
{
{wait(.05);};
}
self notify( "ap_off" );
self ClearAPTarget(weapon);
weapon = self GetCurrentWeapon();
}
}
}
function APLockLoop(weapon)
{
self endon( "disconnect" );
self endon( "death" );
self endon( "ap_off" );
lockLength = self getLockOnSpeed();
self.multiLockList = [];
for (;;)
{
{wait(.05);};
//-------------------------
// Four possible states:
// No missile in the tube, so CLU will not search for targets.
// CLU has a lock.
// CLU is locking on to a target.
// CLU is searching for a target to begin locking on to.
//-------------------------
do
{
done=true;
foreach(target in self.multiLockList)
{
if ( target.apLockFinalized )
{
if ( ! IsStillValidTarget( weapon, target.apTarget ) )
{
self ClearAPTarget(weapon, target);
//start over because the array has changed and it'll mess up foreach
done = false;
break;
}
}
}
}
while (!done);
inLockingState=false;
do
{
done=true;
for(i=0;i<self.multiLockList.size;i++)
{
target=self.multiLockList[i];
if ( target.apLocking )
{
if ( ! IsStillValidTarget( weapon, target.apTarget ) )
{
self ClearAPTarget(weapon, target);
//start over because the array has changed and it'll mess up foreach
done=false;
break;
}
inLockingState=true;
timePassed = getTime() - target.apLockStartTime;
if ( timePassed < lockLength )
continue;
assert( isdefined( target.apTarget ) );
target.apLockFinalized = true;
target.apLocking = false;
target.apLockPending = false;
self WeaponLockFinalize( target.apTarget, i );
self thread SeekerSound( weapon.lockonSeekerLockedSound, weapon.lockonSeekerLockedSoundLoops, target.apSoundId );
target.apTarget notify( "missile_lock", self, weapon );
}
}
}
while (!done);
//if anyone is in the locking state, then don't allow pending to change state to locking (this will make locking sequential)
if (!inLockingState)
{
do
{
done=true;
for(i=0;i<self.multiLockList.size;i++)
{
target=self.multiLockList[i];
if ( target.apLockPending )
{
if ( ! IsStillValidTarget( weapon, target.apTarget ) )
{
self ClearAPTarget(weapon, target);
//start over because the array has changed and it'll mess up foreach
done=false;
break;
}
target.apLockStartTime = getTime();
target.apLockFinalized = false;
target.apLockPending = false;
target.apLocking = true;
self thread SeekerSound( weapon.lockonSeekerSearchSound, weapon.lockonSeekerSearchSoundLoops, target.apSoundId );
done=true;
break;
}
}
}
while (!done);
}
if (self.multiLockList.size>=GetDvarInt( "scr_max_simLocks" ) || self.multiLockList.size>=self GetWeaponAmmoClip(weapon))
{
continue;
}
bestTarget = self GetBestTarget(weapon);
if ( !IsDefined(bestTarget) && self.multiLockList.size==0 )
{
self DestroyLockOnCanceledMessage();
continue;
}
//append to the lock list if we have the space
if (IsDefined(bestTarget) && self.multiLockList.size<GetDvarInt( "scr_max_simLocks" ) && self.multiLockList.size<self GetWeaponAmmoClip(weapon))
{
self WeaponLockStart(bestTarget.apTarget,self.multiLockList.size);
self.multiLockList[self.multiLockList.size] = bestTarget;
}
}
}
function DestroyLockOnCanceledMessage()
{
if( isdefined( self.LockOnCanceledMessage ) )
self.LockOnCanceledMessage destroy();
}
function DisplayLockOnCanceledMessage()
{
if( isdefined( self.LockOnCanceledMessage ) )
return;
self.LockOnCanceledMessage = newclienthudelem( self );
self.LockOnCanceledMessage.fontScale = 1.25;
self.LockOnCanceledMessage.x = 0;
self.LockOnCanceledMessage.y = 50;
self.LockOnCanceledMessage.alignX = "center";
self.LockOnCanceledMessage.alignY = "top";
self.LockOnCanceledMessage.horzAlign = "center";
self.LockOnCanceledMessage.vertAlign = "top";
self.LockOnCanceledMessage.foreground = true;
self.LockOnCanceledMessage.hidewhendead = false;
self.LockOnCanceledMessage.hidewheninmenu = true;
self.LockOnCanceledMessage.archived = false;
self.LockOnCanceledMessage.alpha = 1.0;
self.LockOnCanceledMessage SetText( &"MP_CANNOT_LOCKON_TO_TARGET" );
}
function GetBestTarget(weapon)
{
playerTargets = GetPlayers();
vehicleTargets = target_getArray();
targetsAll = GetAITeamArray();
targetsAll = ArrayCombine(targetsAll, playerTargets, false, false);
targetsAll = ArrayCombine(targetsAll, vehicleTargets, false, false);
targetsValid = [];
for ( idx = 0; idx < targetsAll.size; idx++ )
{
if ( level.teamBased ) //team based game modes
{
if ( isdefined(targetsAll[idx].team) && targetsAll[idx].team != self.team)
{
if ( self InsideAPReticleNoLock( targetsAll[idx] ) )
{
if (self LockSightTest(targetsAll[idx]))
{
targetsValid[targetsValid.size] = targetsAll[idx];
}
}
}
}
else
{
if( self InsideAPReticleNoLock( targetsAll[idx] ) ) //Free for all
{
if( isdefined( targetsAll[idx].owner ) && self != targetsAll[idx].owner )
{
if (self LockSightTest(targetsAll[idx]))
{
targetsValid[targetsValid.size] = targetsAll[idx];
}
}
}
}
}
if ( targetsValid.size == 0 )
return undefined;
//find the best target that isn't already in the list
playerForward = AnglesToForward(self GetPlayerAngles());
dots=[];
for (i=0;i<targetsValid.size;i++)
{
newitem=spawnstruct();
newitem.index = i;
newitem.dot = VectorDot(playerForward,VectorNormalize(targetsValid[i].origin-self.origin));
array::insertion_sort(dots,&TargetInsertionSortCompare,newitem);
}
index=0;
foreach(dot in dots)
{
found = false;
foreach(lock in self.multiLockList)
{
if (lock.apTarget == targetsValid[dot.index])
{
found=true;
}
}
if (found)
{
continue;
}
newEntry = spawnstruct();
newEntry.apTarget = targetsValid[dot.index];
newEntry.apLockStartTime = getTime();
newEntry.apLockPending = true;
newEntry.apLocking = false;
newEntry.apLockFinalized = false;
newEntry.apLostSightlineTime = 0;
newEntry.apSoundId = RandomInt(0x7FFFFFFF);
return newEntry;
}
return undefined;
}
function TargetInsertionSortCompare(a, b)
{
if (a.dot<b.dot)
return -1;
if (a.dot>b.dot)
return 1;
return 0;
}
function InsideAPReticleNoLock( target )
{
radius = self getLockOnRadius();
return target_isincircle( target, self, 65, radius );
}
function InsideAPReticleLocked( target )
{
radius = (self getLockOnLossRadius());
return target_isincircle( target, self, 65, radius );
}
function IsStillValidTarget( weapon, ent )
{
if ( ! isdefined( ent ) )
return false;
if ( ! InsideAPReticleLocked( ent ) )
return false;
if ( !IsAlive( ent ) )
return false;
if (!LockSightTest(ent))
return false;
return true;
}
function SeekerSound( alias, looping, id )
{
self notify( "stop_sound"+id);
self endon ( "stop_sound"+id );
self endon( "disconnect" );
self endon ( "death" );
if (IsDefined(alias))
{
self PlayRumbleOnEntity( "stinger_lock_rumble" );
time = SoundGetPlaybackTime(alias)*0.001;
do
{
self playLocalSound( alias );
wait(time);
}
while (looping);
self StopRumble( "stinger_lock_rumble" );
}
}
function LockSightTest( target )
{
eyePos = self GetEye();
if ( !isdefined( target ) ) //targets can disapear during targeting.
return false;
if ( !IsAlive( target ) )
return false;
pos = target GetShootAtPos();
if (IsDefined(pos))
{
passed = BulletTracePassed( eyePos, pos, false, target, undefined, true, true );
if ( passed )
return true;
}
pos = target GetCentroid();
if (IsDefined(pos))
{
passed = BulletTracePassed( eyePos, pos, false, target, undefined, true, true );
if ( passed )
return true;
}
pos=target.origin;
passed = BulletTracePassed( eyePos, pos, false, target, undefined, true, true );
if ( passed )
return true;
return false;
}