etqw-sdk/base/script/deployables/artillery.script

739 lines
17 KiB
Plaintext

object deployable_artillery : deployable_base {
void preinit();
void init();
void destroy();
void syncFields();
void vDoneDeploy();
void TurningThread();
void ServerIdle();
void ServerAiming();
void ServerFiring();
void ClientIdle();
void ClientFiring();
void StopRotationSounds();
void vTargetSetTarget( vector targetPos, entity targetEnt );
boolean vTargetGetValid( vector targetPos );
boolean vTargetPlayerEligible( entity p );
float vGetFireSupportMode();
//
boolean InRange( vector targetPos );
boolean InFiringRange( vector targetPos ) { return InRange( targetPos ); }
void UpdateTurning();
boolean UpdateYaw();
boolean UpdatePitch();
void ResetAngles();
void TurnToward( float yaw, float pitch );
boolean PitchValid( float pitch );
boolean CalcTargetAngles( vector targetPos );
string MuzzleForMissile( float missileIndex );
// utility funcs ( none of these should be blocking )
void Launch( float missileIndex );
void FireMissile( float missileIndex );
void LaunchForEffect();
float GetMissileBarrelLength( float index );
void SetMissileBarrelLength( float index, float value );
float GetMissileSideOffset( float index );
void SetMissileSideOffset( float index, float value );
void OnFireSupportStateChanged();
target_marker vCreateTargetMarker();
void ClearFiringDecal();
void CreateFiringDecal();
void ReloadThread( float delay );
float recycle;
float fireCount;
float reloadWait;
float numMissiles;
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;
vector firingOffset;
vector firingVelocity;
boolean hasTarget;
entity targetPlayerEnemy;
vector targetLocation;
float projectileIndex;
float projectileForEffectIndex;
generic_target_marker firingDecal;
entity firingMarker;
boolean anglesLocked;
}
float deployable_artillery::GetMissileBarrelLength( float index ) {
return getFloatKey( "missile" + ( index + 1 ) + "_barrel_length" );
}
void deployable_artillery::SetMissileBarrelLength( float index, float value ) {
setKey( "missile" + ( index + 1 ) + "_barrel_length", value );
}
float deployable_artillery::GetMissileSideOffset( float index ) {
return getFloatKey( "missile" + ( index + 1 ) + "_side_offset" );
}
void deployable_artillery::SetMissileSideOffset( float index, float value ) {
setKey( "missile" + ( index + 1 ) + "_side_offset", value );
}
void deployable_artillery::OnFireSupportStateChanged() {
if ( fireSupportState == MPS_FIRING ) {
if ( clientState != ART_CS_FIRING ) {
setState( "ClientFiring" );
}
} else {
if ( clientState != ART_CS_IDLE ) {
setState( "ClientIdle" );
}
}
}
void deployable_artillery::syncFields() {
syncBroadcast( "nextFire" );
syncBroadcast( "targetLocation" );
syncBroadcast( "fireSupportState" );
syncBroadcast( "idealYaw" );
syncBroadcast( "idealPitch" );
syncBroadcast( "oldIdealYaw" );
syncBroadcast( "oldIdealPitch" );
syncCallback( "fireSupportState", "OnFireSupportStateChanged" );
sync( "currentYaw" );
sync( "currentPitch" );
}
void deployable_artillery::destroy() {
ClearFiringDecal();
delete fsStatus;
}
float deployable_artillery::vGetFireSupportMode() {
return TARGET_ARTILLERY;
}
void deployable_artillery::preinit() {
recycle = getFloatKey( "missile_recycle" );
fireCount = getFloatKey( "missile_firecount" );
reloadWait = getFloatKey( "missile_reload" );
numMissiles = getFloatKey( "num_missiles" );
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" ) );
projectileIndex = GetEntityDef( getKey( "def_projectile" ) );
projectileForEffectIndex = GetEntityDef( getKey( "def_projectile_foreffect" ) );
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();
}
void deployable_artillery::vDoneDeploy() {
vector barrel;
float i;
float muzzleHandle;
vector pitchJointPos;
float sideOffset;
float barrelLength;
pitchJointPos = getLocalJointPos( jointPitch );
barrel = getLocalJointPos( jointBarrel ) - pitchJointPos;
barrelPitch = sys.atan2( barrel_z, barrel_x );
for ( i = 0; i < numMissiles; i++ ) {
muzzleHandle = getJointHandle( MuzzleForMissile( i ) );
barrel = getLocalJointPos( muzzleHandle ) - pitchJointPos;
sideOffset = barrel_y;
barrel_y = 0;
barrelLength = sys.vecLength( barrel );
SetMissileSideOffset( i, sideOffset );
SetMissileBarrelLength( i, barrelLength );
}
vector angles = getAngles();
baseYaw = angles_y;
SetDeployingFinished();
}
void deployable_artillery::init() {
fadeSound( SND_DEPLOYABLE_YAW, -60.f, 0.f );
fadeSound( SND_DEPLOYABLE_PITCH, -60.f, 0.f );
startSound( "snd_turret_yaw", SND_DEPLOYABLE_YAW );
startSound( "snd_turret_pitch", SND_DEPLOYABLE_PITCH );
if ( sys.isClient() ) {
setState( "ClientIdle" );
} else {
setState( "ServerIdle" );
}
}
// ==========================================
// States
// ==========================================
void deployable_artillery::TurningThread() {
while( true ) {
sys.waitFrame();
if ( disabledState ) {
StopRotationSounds();
} else {
UpdateTurning();
}
}
}
void deployable_artillery::ClientIdle() {
clientState = ART_CS_IDLE;
ClearFiringDecal();
}
void deployable_artillery::ServerIdle() {
hasTarget = false;
ResetAngles();
ClearFiringDecal();
}
void deployable_artillery::ReloadThread( float delay ) {
fireSupportState = MPS_RELOADING;
sys.wait( delay );
fireSupportState = MPS_READY;
}
void deployable_artillery::StopRotationSounds() {
DEPLOYABLE_STOP_YAW_SOUND
DEPLOYABLE_STOP_PITCH_SOUND
}
void deployable_artillery::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 );
DEPLOYABLE_PLAY_YAW_SOUND
} else {
DEPLOYABLE_STOP_YAW_SOUND
}
if ( !pitchDone ) {
angles_x = currentPitch;
angles_y = 0;
angles_z = 0;
setJointAngle( jointPitch, JOINTMOD_LOCAL, angles );
DEPLOYABLE_PLAY_PITCH_SOUND
} else {
DEPLOYABLE_STOP_PITCH_SOUND
}
if ( yawDone && pitchDone ) {
anglesLocked = true;
} else {
anglesLocked = false;
}
}
void deployable_artillery::ServerAiming() {
fireSupportState = MPS_FIRING_PREPARE;
while ( !anglesLocked ) {
sys.waitFrame();
if ( disabledState ) {
fireSupportState = MPS_READY;
setState( "ServerIdle" );
}
}
setState( "ServerFiring" );
}
void deployable_artillery::ClientFiring() {
float nextMiniFire = 0;
float nextMiniCount = 0;
float baseFireTime = 0;
CreateFiringDecal();
clientState = ART_CS_FIRING;
if ( projectileForEffectIndex >= 0 ) {
LaunchForEffect();
sys.wait( getFloatKey( "foreffect_wait" ) );
}
while( true ) {
if ( nextFire < sys.getTime() ) {
baseFireTime = sys.getTime();
nextFire = baseFireTime + recycle;
nextMiniCount = 0;
nextMiniFire = baseFireTime + getFloatKey( "missile" + ( nextMiniCount + 1 ) + "_delay" );
}
if ( nextMiniCount < numMissiles ) {
if( nextMiniFire < sys.getTime() ) {
Launch( nextMiniCount );
nextMiniCount++;
nextMiniFire = baseFireTime + getFloatKey( "missile" + ( nextMiniCount + 1 ) + "_delay" );
}
}
sys.waitFrame();
}
}
void deployable_artillery::ServerFiring() {
float count = fireCount;
float nextMiniFire = 0;
float nextMiniCount = 0;
float baseFireTime = 0;
fireSupportState = MPS_FIRING;
nextFire = sys.getTime() + 1.f;
if ( projectileForEffectIndex >= 0 ) {
LaunchForEffect();
sys.wait( getFloatKey( "foreffect_wait" ) );
}
objManager.PlaySoundForPlayer( getKey( "snd_firing" ), owner );
while( true ) {
if ( disabledState ) {
break;
}
if ( nextFire < sys.getTime() ) {
baseFireTime = sys.getTime();
nextFire = baseFireTime + recycle;
count = count - 1;
if ( count < 0 ) {
break;
}
nextMiniCount = 0;
nextMiniFire = baseFireTime + getFloatKey( "missile" + ( nextMiniCount + 1 ) + "_delay" );
}
if ( nextMiniCount < numMissiles ) {
if ( nextMiniFire != 0 && nextMiniFire < sys.getTime() ) {
Launch( nextMiniCount );
nextMiniCount++;
nextMiniFire = baseFireTime + getFloatKey( "missile" + ( nextMiniCount + 1 ) + "_delay" );
}
}
sys.waitFrame();
}
nextFire = 0;
thread ReloadThread( reloadWait );
setState( "ServerIdle" );
}
// ==========================================
// Utility Funcs
// ==========================================
string deployable_artillery::MuzzleForMissile( float missileIndex ) {
return getKey( "missile" + ( missileIndex + 1 ) + "_barrel" );
}
void deployable_artillery::FireMissile( float missileIndex ) {
vector org;
org = getJointPos( jointPitch ) + firingOffset + ( getWorldAxis( 1 ) * GetMissileSideOffset( missileIndex ) );
launchMissileSimple( owner, self, targetPlayerEnemy, projectileIndex, -1, spread, org, firingVelocity );
playEffect( "fx_fire", MuzzleForMissile( missileIndex ), 0 );
}
void deployable_artillery::Launch( float missileIndex ) {
string anim;
float channel;
FireMissile( missileIndex );
anim = getKey( "missile" + ( missileIndex + 1 ) + "_anim" );
if ( anim != "" ) {
channel = getIntKey( "missile" + ( missileIndex + 1 ) + "_channel" );
playAnim( channel, anim );
}
}
void deployable_artillery::LaunchForEffect() {
string anim;
float channel;
vector org;
org = getJointPos( jointPitch ) + firingOffset + ( getWorldAxis( 1 ) * GetMissileSideOffset( 0 ) );
launchMissileSimple( owner, self, $null_entity, projectileForEffectIndex, -1, 0.0, org, firingVelocity );
// objManager.PlaySoundForPlayer( getKey( "snd_effect" ), owner );
playEffect( "fx_fire", MuzzleForMissile( 0 ), 0 );
anim = getKey( "missile1_anim" );
if ( anim != "" ) {
channel = getIntKey( "missile1_channel" );
playAnim( channel, anim );
}
}
// ==========================================
// ==========================================
boolean deployable_artillery::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 ) ) * 180.f );
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_artillery::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 ) ) * 180.f );
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_artillery::TurnToward( float yaw, float pitch ) {
oldIdealYaw = currentYaw;
oldIdealPitch = currentPitch;
idealYaw = sys.angleNormalize180( yaw );
idealPitch = sys.angleNormalize180( pitch );
anglesLocked = false;
}
void deployable_artillery::ResetAngles() {
TurnToward( 0, 0 );
}
void deployable_artillery::vTargetSetTarget( vector targetPos, entity targetEnt ) {
float firingPitch;
float t;
vector gravity;
vector velocity;
vector temp;
float i;
if( !CalcTargetAngles( targetPos ) ) {
return;
}
gravity_x = 0;
gravity_y = 0;
gravity_z = -1066;
firingPitch = targetPitch - barrelPitch;
for ( i = 0; i < numMissiles; i++ ) {
firingVelocity = targetDiff * ( sys.cos( targetPitch ) * missileSpeed );
firingVelocity_z = sys.sin( targetPitch ) * missileSpeed;
t = GetMissileBarrelLength( i ) / missileSpeed;
gravity = gravity * t * t * 0.5f;
temp = firingVelocity * t;
firingOffset = temp + gravity;
firingVelocity = firingVelocity + ( gravity * t );
}
TurnToward( targetYaw - baseYaw, -firingPitch );
targetPlayerEnemy = owner.getEnemy();
owner.lastValidTarget = targetPos;
targetLocation = targetPos;
Base_TargetSetTarget( targetPos, targetEnt );
CreateFiringDecal();
setState( "ServerAiming" );
}
boolean deployable_artillery::vTargetGetValid( vector targetPos ) {
if ( !InRange( targetPos ) ) {
return 0;
}
return CalcTargetAngles( targetPos );
}
boolean deployable_artillery::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 = ( 1066 * 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_artillery::PitchValid( float pitch ) {
return pitch < maxPitch && pitch > minPitch;
}
boolean deployable_artillery::InRange( vector targetPos ) {
return true;
}
boolean deployable_artillery::vTargetPlayerEligible( entity p ) {
if ( disabledState || !finishedDeploying ) {
return 0.f;
}
return 1.f;
}
target_marker deployable_artillery::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" ) );
if ( vGetFireSupportMode() == TARGET_ROCKETS ) {
marker.SetLocalOnly( true );
}
return marker;
}
void deployable_artillery::ClearFiringDecal() {
if ( firingDecal != $null_entity ) {
delete firingDecal;
}
if ( firingMarker != $null_entity ) {
thread G_DelayRemoveEntity( firingMarker, 5.f );
firingMarker = $null_entity;
}
}
void deployable_artillery::CreateFiringDecal() {
if ( !sys.isClient() ) {
firingMarker = G_CreateFiringMarker( self, firingMarker, targetLocation );
}
if ( vGetFireSupportMode() == TARGET_ROCKETS ) {
return;
}
entity p = sys.getLocalPlayer();
if ( p == $null_entity ) {
return;
}
if ( getEntityAllegiance( p ) != TA_FRIEND ) {
return;
}
if ( firingDecal == $null_entity ) {
firingDecal = vCreateTargetMarker();
}
if ( firingDecal != $null_entity ) {
firingDecal.SetTargetPosition( targetLocation );
}
}
object deployable_rockets : deployable_artillery {
float vGetFireSupportMode();
}
float deployable_rockets::vGetFireSupportMode() {
return TARGET_ROCKETS;
}