650 lines
14 KiB
Plaintext
650 lines
14 KiB
Plaintext
|
|
||
|
object deployable_ssg : deployable_base {
|
||
|
void preinit();
|
||
|
void init();
|
||
|
void destroy();
|
||
|
void syncFields();
|
||
|
|
||
|
void TurningThread();
|
||
|
|
||
|
void ServerIdle();
|
||
|
void ServerAiming();
|
||
|
void ServerFiring();
|
||
|
|
||
|
void ClientIdle();
|
||
|
void ClientFiring();
|
||
|
|
||
|
boolean vTargetPlayerEligible( entity p );
|
||
|
boolean vTargetGetValid( vector pos );
|
||
|
void vTargetSetTarget( vector targetPos, entity targetEnt );
|
||
|
void vDoneDeploy();
|
||
|
|
||
|
float vGetFireSupportMode();
|
||
|
|
||
|
void InitTargetting( vector targetPos );
|
||
|
|
||
|
void ClearFiringDecal();
|
||
|
void CreateFiringDecal();
|
||
|
|
||
|
void ReloadThread( float delay );
|
||
|
|
||
|
void OnMissileChanged();
|
||
|
void OnFireSupportStateChanged();
|
||
|
|
||
|
//
|
||
|
void UpdateTurning();
|
||
|
boolean UpdateYaw();
|
||
|
boolean UpdatePitch();
|
||
|
void ResetAngles();
|
||
|
void TurnToward( float yaw, float pitch );
|
||
|
boolean PitchValid( float pitch );
|
||
|
boolean CalcTargetAngles( vector targetPos );
|
||
|
|
||
|
// utility funcs ( none of these should be blocking )
|
||
|
void Launch();
|
||
|
void FireMissile();
|
||
|
|
||
|
void StopRotationSounds();
|
||
|
void StopYawSound();
|
||
|
void StopPitchSound();
|
||
|
void PlayYawSound();
|
||
|
void PlayPitchSound();
|
||
|
|
||
|
void MissileCheck();
|
||
|
|
||
|
float recycle;
|
||
|
float fireCount;
|
||
|
float reloadWait;
|
||
|
float fireSyncDelay;
|
||
|
float nextFire;
|
||
|
|
||
|
float jointYaw;
|
||
|
float idealYaw;
|
||
|
float currentYaw;
|
||
|
float oldIdealYaw;
|
||
|
float minYawRate;
|
||
|
float maxYawRate;
|
||
|
|
||
|
float jointPitch;
|
||
|
float idealPitch;
|
||
|
float currentPitch;
|
||
|
float oldIdealPitch;
|
||
|
float minPitchRate;
|
||
|
float maxPitchRate;
|
||
|
|
||
|
float jointBarrel;
|
||
|
|
||
|
float minPitch;
|
||
|
float maxPitch;
|
||
|
|
||
|
float minRange;
|
||
|
float maxRange;
|
||
|
|
||
|
float missileSpeed;
|
||
|
|
||
|
float spread;
|
||
|
|
||
|
float clientState;
|
||
|
|
||
|
float targetYaw;
|
||
|
float targetPitch;
|
||
|
vector targetDiff;
|
||
|
|
||
|
float baseYaw;
|
||
|
float barrelPitch;
|
||
|
float barrelLength;
|
||
|
vector firingOffset;
|
||
|
vector firingVelocity;
|
||
|
boolean hasTarget;
|
||
|
float projectileIndex;
|
||
|
|
||
|
entity targetCapturer;
|
||
|
|
||
|
boolean playingPitchSound;
|
||
|
boolean playingYawSound;
|
||
|
boolean anglesLocked;
|
||
|
|
||
|
entity missile;
|
||
|
boolean missileActive;
|
||
|
|
||
|
vector target;
|
||
|
|
||
|
generic_target_marker firingDecal;
|
||
|
entity firingMarker;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::syncFields() {
|
||
|
syncBroadcast( "target" );
|
||
|
syncBroadcast( "nextFire" );
|
||
|
syncBroadcast( "fireSupportState" );
|
||
|
syncBroadcast( "idealYaw" );
|
||
|
syncBroadcast( "idealPitch" );
|
||
|
syncBroadcast( "oldIdealYaw" );
|
||
|
syncBroadcast( "oldIdealPitch" );
|
||
|
syncBroadcast( "missile" );
|
||
|
|
||
|
sync( "currentYaw" );
|
||
|
sync( "currentPitch" );
|
||
|
|
||
|
syncCallback( "missile", "OnMissileChanged" );
|
||
|
syncCallback( "fireSupportState", "OnFireSupportStateChanged" );
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::MissileCheck() {
|
||
|
while ( true ) {
|
||
|
if ( missile != $null_entity ) {
|
||
|
missileActive = true;
|
||
|
} else {
|
||
|
if ( missileActive ) {
|
||
|
missileActive = false;
|
||
|
ClearFiringDecal();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sys.waitFrame();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::destroy() {
|
||
|
delete fsStatus;
|
||
|
ClearFiringDecal();
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ReloadThread( float delay ) {
|
||
|
fireSupportState = MPS_RELOADING;
|
||
|
|
||
|
sys.wait( delay );
|
||
|
|
||
|
fireSupportState = MPS_READY;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::OnMissileChanged() {
|
||
|
if ( missile != $null_entity ) {
|
||
|
CreateFiringDecal();
|
||
|
} else {
|
||
|
ClearFiringDecal();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::OnFireSupportStateChanged() {
|
||
|
if ( fireSupportState == MPS_FIRING ) {
|
||
|
if ( clientState != ART_CS_FIRING ) {
|
||
|
setState( "ClientFiring" );
|
||
|
}
|
||
|
} else {
|
||
|
if ( clientState != ART_CS_IDLE ) {
|
||
|
setState( "ClientIdle" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float deployable_ssg::vGetFireSupportMode() {
|
||
|
return TARGET_SSM;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::preinit() {
|
||
|
vector barrel;
|
||
|
float entityDeclIndex;
|
||
|
float i;
|
||
|
float muzzleHandle;
|
||
|
|
||
|
recycle = getFloatKey( "missile_recycle" );
|
||
|
fireCount = getFloatKey( "missile_firecount" );
|
||
|
reloadWait = getFloatKey( "missile_reload" );
|
||
|
fireSyncDelay = getFloatKey( "sync_delay" );
|
||
|
spread = getFloatKey( "spread" );
|
||
|
|
||
|
if ( fireCount <= 0 ) {
|
||
|
fireCount = 6;
|
||
|
}
|
||
|
if ( reloadWait <= 0 ) {
|
||
|
reloadWait = 30;
|
||
|
}
|
||
|
if ( fireSyncDelay <= 0 ) {
|
||
|
fireSyncDelay = 0.5;
|
||
|
}
|
||
|
|
||
|
jointYaw = getJointHandle( getKey( "joint_yaw" ) );
|
||
|
jointPitch = getJointHandle( getKey( "joint_pitch" ) );
|
||
|
jointBarrel = getJointHandle( getKey( "joint_barrel" ) );
|
||
|
|
||
|
entityDeclIndex = sys.getDeclType( "entityDef" );
|
||
|
projectileIndex = sys.getDeclIndex( entityDeclIndex, getKey( "def_projectile" ) );
|
||
|
|
||
|
barrel = getLocalJointPos( jointBarrel ) - getLocalJointPos( jointPitch );
|
||
|
barrelPitch = sys.atan2( barrel_z, barrel_x );
|
||
|
barrelLength = sys.vecLength( barrel );
|
||
|
|
||
|
currentYaw = 0;
|
||
|
idealYaw = 0;
|
||
|
oldIdealYaw = 0;
|
||
|
maxYawRate = getFloatKey( "max_yaw_turn" );
|
||
|
minYawRate = getFloatKey( "min_yaw_turn" );
|
||
|
|
||
|
currentPitch = 0;
|
||
|
idealPitch = 0;
|
||
|
oldIdealPitch = 0;
|
||
|
maxPitchRate = getFloatKey( "max_pitch_turn" );
|
||
|
minPitchRate = getFloatKey( "min_pitch_turn" );
|
||
|
|
||
|
minPitch = getFloatKey( "min_pitch" );
|
||
|
maxPitch = getFloatKey( "max_pitch" );
|
||
|
|
||
|
minRange = getFloatKey( "range_min" );
|
||
|
maxRange = getFloatKey( "range_max" );
|
||
|
|
||
|
missileSpeed = getFloatKey( "missile_speed" );
|
||
|
|
||
|
hasTarget = false;
|
||
|
|
||
|
nextFire = 0;
|
||
|
|
||
|
fsStatus = new fireSupportStatus;
|
||
|
|
||
|
thread TurningThread();
|
||
|
thread MissileCheck();
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::init() {
|
||
|
if ( sys.isClient() ) {
|
||
|
setState( "ClientIdle" );
|
||
|
} else {
|
||
|
setState( "ServerIdle" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ==========================================
|
||
|
// States
|
||
|
// ==========================================
|
||
|
|
||
|
void deployable_ssg::TurningThread() {
|
||
|
while( true ) {
|
||
|
sys.waitFrame();
|
||
|
|
||
|
if ( disabledState ) {
|
||
|
StopRotationSounds();
|
||
|
} else {
|
||
|
UpdateTurning();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ClientIdle() {
|
||
|
clientState = ART_CS_IDLE;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ServerIdle() {
|
||
|
hasTarget = false;
|
||
|
|
||
|
ResetAngles();
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::UpdateTurning() {
|
||
|
boolean yawDone;
|
||
|
boolean pitchDone;
|
||
|
vector angles;
|
||
|
|
||
|
yawDone = UpdateYaw();
|
||
|
pitchDone = UpdatePitch();
|
||
|
|
||
|
if ( !yawDone ) {
|
||
|
angles_x = 0;
|
||
|
angles_y = currentYaw;
|
||
|
angles_z = 0;
|
||
|
setJointAngle( jointYaw, JOINTMOD_LOCAL, angles );
|
||
|
|
||
|
PlayYawSound();
|
||
|
} else {
|
||
|
StopYawSound();
|
||
|
}
|
||
|
|
||
|
if ( !pitchDone ) {
|
||
|
angles_x = currentPitch;
|
||
|
angles_y = 0;
|
||
|
angles_z = 0;
|
||
|
setJointAngle( jointPitch, JOINTMOD_LOCAL, angles );
|
||
|
|
||
|
PlayPitchSound();
|
||
|
} else {
|
||
|
StopPitchSound();
|
||
|
}
|
||
|
|
||
|
if ( yawDone && pitchDone ) {
|
||
|
anglesLocked = true;
|
||
|
} else {
|
||
|
anglesLocked = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ServerAiming() {
|
||
|
fireSupportState = MPS_FIRING_PREPARE;
|
||
|
|
||
|
while ( !anglesLocked ) {
|
||
|
sys.waitFrame();
|
||
|
|
||
|
if ( disabledState ) {
|
||
|
setState( "ServerIdle" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
setState( "ServerFiring" );
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ClientFiring() {
|
||
|
clientState = ART_CS_FIRING;
|
||
|
|
||
|
while( true ) {
|
||
|
if ( sys.getTime() > nextFire ) {
|
||
|
Launch();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
sys.waitFrame();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ServerFiring() {
|
||
|
fireSupportState = MPS_FIRING;
|
||
|
|
||
|
nextFire = sys.getTime() + 2.f;
|
||
|
|
||
|
while( true ) {
|
||
|
if ( disabledState ) {
|
||
|
ClearFiringDecal();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( sys.getTime() > nextFire ) {
|
||
|
Launch();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
sys.waitFrame();
|
||
|
}
|
||
|
|
||
|
sys.wait( 2.f );
|
||
|
|
||
|
nextFire = 0;
|
||
|
|
||
|
thread ReloadThread( reloadWait );
|
||
|
|
||
|
setState( "ServerIdle" );
|
||
|
}
|
||
|
|
||
|
// ==========================================
|
||
|
// Utility Funcs
|
||
|
// ==========================================
|
||
|
|
||
|
void deployable_ssg::FireMissile() {
|
||
|
vector org = getJointPos( jointPitch ) + firingOffset;
|
||
|
|
||
|
missile = launchMissileSimple( owner, self, $null_entity, projectileIndex, -1, 0.f, org, firingVelocity );
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::Launch() {
|
||
|
FireMissile();
|
||
|
|
||
|
string anim = getKey( "missile_anim" );
|
||
|
if ( anim != "" ) {
|
||
|
playAnim( getIntKey( "missile_channel" ), anim );
|
||
|
}
|
||
|
|
||
|
objManager.CPrintEvent( sys.localizeString( "game/ssg_fired" ), $null );
|
||
|
|
||
|
team_base team = getGameTeam();
|
||
|
objManager.PlaySound( team.getKey( "snd_ssg_fired" ), team );
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::StopYawSound() {
|
||
|
if ( playingYawSound ) {
|
||
|
playingYawSound = false;
|
||
|
startSound( "snd_turret_stop", SND_DEPLOYABLE_YAW );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::StopPitchSound() {
|
||
|
if ( playingPitchSound ) {
|
||
|
playingPitchSound = false;
|
||
|
startSound( "snd_barrel_stop", SND_DEPLOYABLE_PITCH );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::PlayYawSound() {
|
||
|
if ( !playingYawSound ) {
|
||
|
playingYawSound = true;
|
||
|
startSound( "snd_turret_start", SND_DEPLOYABLE_YAW );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::PlayPitchSound() {
|
||
|
if ( !playingPitchSound ) {
|
||
|
playingPitchSound = true;
|
||
|
startSound( "snd_barrel_start", SND_DEPLOYABLE_PITCH );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ==========================================
|
||
|
// ==========================================
|
||
|
|
||
|
boolean deployable_ssg::UpdateYaw() {
|
||
|
float ang;
|
||
|
float maxTurn;
|
||
|
float frac;
|
||
|
|
||
|
if ( idealYaw == currentYaw ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ang = sys.angleNormalize180( idealYaw - currentYaw );
|
||
|
frac = sys.sin( sys.fabs( ang / sys.angleNormalize180( idealYaw - oldIdealYaw ) ) * MATH_PI );
|
||
|
|
||
|
maxTurn = ( minYawRate + ( maxYawRate - minYawRate ) * frac ) * sys.getFrameTime();
|
||
|
|
||
|
if ( ang < -maxTurn ) {
|
||
|
currentYaw = currentYaw - maxTurn;
|
||
|
} else if ( ang > maxTurn ) {
|
||
|
currentYaw = currentYaw + maxTurn;
|
||
|
} else {
|
||
|
currentYaw = idealYaw;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
boolean deployable_ssg::UpdatePitch() {
|
||
|
float ang;
|
||
|
float maxTurn;
|
||
|
float frac;
|
||
|
|
||
|
if ( idealPitch == currentPitch ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ang = sys.angleNormalize180( idealPitch - currentPitch );
|
||
|
frac = sys.sin( sys.fabs( ang / sys.angleNormalize180( idealPitch - oldIdealPitch ) ) * MATH_PI );
|
||
|
|
||
|
maxTurn = ( minPitchRate + ( maxPitchRate - minPitchRate ) * frac ) * sys.getFrameTime();
|
||
|
|
||
|
if ( ang < -maxTurn ) {
|
||
|
currentPitch = currentPitch - maxTurn;
|
||
|
} else if ( ang > maxTurn ) {
|
||
|
currentPitch = currentPitch + maxTurn;
|
||
|
} else {
|
||
|
currentPitch = idealPitch;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::TurnToward( float yaw, float pitch ) {
|
||
|
oldIdealYaw = currentYaw;
|
||
|
oldIdealPitch = currentPitch;
|
||
|
idealYaw = sys.angleNormalize180( yaw );
|
||
|
idealPitch = sys.angleNormalize180( pitch );
|
||
|
anglesLocked = false;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ResetAngles() {
|
||
|
TurnToward( 0, 0 );
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::InitTargetting( vector targetPos ) {
|
||
|
float firingPitch;
|
||
|
float t;
|
||
|
vector gravity = '0 0 -1066';
|
||
|
vector velocity;
|
||
|
vector temp;
|
||
|
float i;
|
||
|
|
||
|
if ( !CalcTargetAngles( targetPos ) ) {
|
||
|
hasTarget = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( sys.doClientSideStuff() ) {
|
||
|
CreateFiringDecal();
|
||
|
}
|
||
|
|
||
|
firingPitch = targetPitch - barrelPitch;
|
||
|
|
||
|
firingVelocity = targetDiff * ( sys.cos( targetPitch ) * missileSpeed );
|
||
|
firingVelocity_z = sys.sin( targetPitch ) * missileSpeed;
|
||
|
|
||
|
t = barrelLength / missileSpeed;
|
||
|
|
||
|
gravity = gravity * t * t * 0.5f;
|
||
|
|
||
|
temp = firingVelocity * t;
|
||
|
|
||
|
firingOffset = temp + gravity;
|
||
|
firingVelocity = firingVelocity + ( gravity * t );
|
||
|
|
||
|
TurnToward( targetYaw - baseYaw, -firingPitch );
|
||
|
}
|
||
|
|
||
|
boolean deployable_ssg::CalcTargetAngles( vector targetPos ) {
|
||
|
vector barrelOrg;
|
||
|
vector temp;
|
||
|
float diffY;
|
||
|
float diffX;
|
||
|
float rootA;
|
||
|
float count;
|
||
|
float root1;
|
||
|
float root2;
|
||
|
|
||
|
barrelOrg = getJointPos( jointPitch );
|
||
|
|
||
|
targetDiff = targetPos - barrelOrg;
|
||
|
|
||
|
targetYaw = sys.angleNormalize180( sys.atan2( targetDiff_y, targetDiff_x ) );
|
||
|
|
||
|
temp = targetDiff;
|
||
|
|
||
|
diffY = temp_z;
|
||
|
temp_z = 0.f;
|
||
|
targetDiff = sys.vecNormalize( temp );
|
||
|
diffX = sys.vecLength( temp );
|
||
|
|
||
|
// FIXME: Expose default gravity
|
||
|
rootA = ( 400 * diffX * diffX ) / ( 2 * missileSpeed * missileSpeed );
|
||
|
|
||
|
count = sys.solveRoots( rootA, -diffX, rootA + diffY );
|
||
|
if ( count == 0 ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( count == 1 ) {
|
||
|
targetPitch = sys.atan( sys.getRoot( 0 ) );
|
||
|
return PitchValid( targetPitch );
|
||
|
}
|
||
|
|
||
|
targetPitch = sys.atan( sys.getRoot( 0 ) );
|
||
|
if ( PitchValid( targetPitch ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
targetPitch = sys.atan( sys.getRoot( 1 ) );
|
||
|
return PitchValid( targetPitch );
|
||
|
}
|
||
|
|
||
|
boolean deployable_ssg::PitchValid( float pitch ) {
|
||
|
return pitch < maxPitch && pitch > minPitch;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::StopRotationSounds() {
|
||
|
StopYawSound();
|
||
|
StopPitchSound();
|
||
|
}
|
||
|
|
||
|
boolean deployable_ssg::vTargetPlayerEligible( entity p ) {
|
||
|
if ( disabledState || !finishedDeploying ) {
|
||
|
return 0.f;
|
||
|
}
|
||
|
|
||
|
return 1.f;
|
||
|
}
|
||
|
|
||
|
boolean deployable_ssg::vTargetGetValid( vector pos ) {
|
||
|
return CalcTargetAngles( pos );
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::vTargetSetTarget( vector targetPos, entity targetEnt ) {
|
||
|
hasTarget = true;
|
||
|
target = targetPos;
|
||
|
|
||
|
InitTargetting( targetPos );
|
||
|
Base_TargetSetTarget( targetPos, targetEnt );
|
||
|
|
||
|
setState( "ServerAiming" );
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::vDoneDeploy() {
|
||
|
vector angles = getAngles();
|
||
|
baseYaw = angles_y;
|
||
|
SetDeployingFinished();
|
||
|
}
|
||
|
|
||
|
target_marker deployable_ssg::vCreateTargetMarker() {
|
||
|
string entityDef = getKey( "def_marker" );
|
||
|
if ( entityDef == "" ) {
|
||
|
return $null;
|
||
|
}
|
||
|
|
||
|
generic_target_marker marker = new generic_target_marker;
|
||
|
marker.Init( self, entityDef, getKey( "mtr_marker_cm" ), getFloatKey( "cm_marker_sort" ) );
|
||
|
return marker;
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::ClearFiringDecal() {
|
||
|
if ( firingDecal != $null_entity ) {
|
||
|
delete firingDecal;
|
||
|
}
|
||
|
|
||
|
if ( !sys.isClient() ) {
|
||
|
if ( firingMarker != $null_entity ) {
|
||
|
thread G_DelayRemoveEntity( firingMarker, 5.f );
|
||
|
firingMarker = $null_entity;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deployable_ssg::CreateFiringDecal() {
|
||
|
if ( !sys.isClient() ) {
|
||
|
firingMarker = G_CreateFiringMarker( self, firingMarker, target );
|
||
|
}
|
||
|
|
||
|
entity p = sys.getLocalPlayer();
|
||
|
if ( p == $null_entity ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( getEntityAllegiance( p ) != TA_FRIEND ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( firingDecal == $null_entity ) {
|
||
|
firingDecal = vCreateTargetMarker();
|
||
|
}
|
||
|
firingDecal.SetTargetPosition( target );
|
||
|
}
|