/* =============================================================================== deployable_antipersonnel - anti-personnel turret =============================================================================== */ object deployable_antipersonnel : deployable_turret { entity OnAcquireTarget(); boolean TargetIsValid( entity targetEnt ); void preinit(); void destroy(); void syncFields(); void OnPostDamage_AP( entity attacker, float oldHealth, float newHealth ); void OnPostDamage( entity attacker, float oldHealth, float newHealth ) { OnPostDamage_AP( attacker, oldHealth, newHealth ); } void OnSetTarget( entity oldTarget, entity newTarget ); void CheckRetribution( entity attacker ); float playerClass; void OnFullChargeTimeChanged(); boolean ShouldSpinBarrel(); void OnBeginAttack(); void OnEndAttack(); void SpinBarrelThread(); boolean CanFire(); void Fired(); void FireMissile(); void UpdateCharge(); float fullChargeTime; float cooldownTime; float chargeTime; float chargePerShot; float overheatPenalty; float spinBarrelJoint; vector barrelAngles; float tracerCounter; float tracerInterval; boolean barrelThreadActive; boolean updateChargeThreadActive; entity lockEntity; } void deployable_antipersonnel::preinit() { playerClass = sys.getTypeHandle( "idPlayer" ); fullChargeTime = -1; cooldownTime = -1; chargeTime = getFloatKeyWithDefault( "charge_time", 10 ); chargePerShot = getFloatKeyWithDefault( "charge_per_shot", 0.7 ); overheatPenalty = getFloatKeyWithDefault( "overheat_penalty", 5 ); spinBarrelJoint = -1; string barrelJointName = getKey( "joint_spin_barrel" ); if ( barrelJointName != "" ) { spinBarrelJoint = getJointHandle( barrelJointName ); } tracerCounter = 0; tracerInterval = sys.getEntityDefIntKey( projectileIndex, "tracer_interval" ); } void deployable_antipersonnel::destroy() { if ( lockEntity != $null_entity ) { lockEntity.vStopTargetLockAlarm( self ); lockEntity = $null_entity; } } void deployable_antipersonnel::syncFields() { syncBroadcast( "cooldownTime" ); sync( "fullChargeTime" ); syncCallback( "fullChargeTime", "OnFullChargeTimeChanged" ); } void deployable_antipersonnel::OnFullChargeTimeChanged() { if ( !updateChargeThreadActive ) { if ( myUser == sys.getLocalPlayer() ) { thread UpdateCharge(); } } } void deployable_antipersonnel::CheckRetribution( entity attacker ) { if ( !TargetIsValid( attacker ) ) { return; } if ( !InRange( attacker.getWorldOrigin() ) ) { return; } setTurretEnemy( attacker, retributionDelay ); } void deployable_antipersonnel::OnPostDamage_AP( entity attacker, float oldHealth, float newHealth ) { boolean oldDisabled = disabledState; UpdateState( newHealth ); if ( !disabledState ) { if ( getEnemy() == $null_entity && newHealth < oldHealth ) { CheckRetribution( attacker ); } } UpdateDisabledStats( attacker, oldDisabled ); } boolean deployable_antipersonnel::TargetIsValid( entity targetEnt ) { player testPlayer = targetEnt; if ( testPlayer == $null_entity ) { return false; } if ( testPlayer.getHealth() <= 0 ) { return false; } if ( testPlayer.getVehicle() != $null_entity ) { return false; } if ( testPlayer.isDisguised() ) { return false; } if ( getEntityAllegiance( testPlayer ) != TA_ENEMY ) { return false; } if ( testPlayer.isInvulnerable() ) { return false; } if ( !TraceCheck( testPlayer ) ) { return false; } return true; } entity deployable_antipersonnel::OnAcquireTarget() { if ( !finishedDeploying || disabledState ) { return $null_entity; } float i; float count; entity ent; entity targetingRangeEnt; vector targetDir; vector selfDir; entitiesOfClass( playerClass, 0 ); filterEntitiesByDisguiseAllegiance( TA_FLAG_ENEMY, 1 ); // only damage enemies filterEntitiesByRadius( getWorldOrigin(), maxRange, 1 ); // find entities within maxRange radius selfDir = getWorldAxis( 0 ); selfDir_z = 0; count = getBoundsCacheCount(); // number of remaining entities for ( i = 0; i < count; i++ ) { ent = getBoundsCacheEntity( i ); targetDir = sys.vecNormalize( ent.getWorldOrigin() - getWorldOrigin() ); targetDir_z = 0; if ( targetDir * selfDir < angleRange ) { continue; } if ( !TargetIsValid( ent ) ) { continue; } if ( !InRange( ent.getWorldOrigin() ) ) { continue; } if ( InFiringRange( ent.getWorldOrigin() ) ) { return ent; } if ( targetingRangeEnt == $null_entity ) { targetingRangeEnt = ent; } } return targetingRangeEnt; } void deployable_antipersonnel::UpdateCharge() { sys.assert( false ); updateChargeThreadActive = true; while ( true ) { if ( myUser != sys.getLocalPlayer() ) { break; } float time = sys.getTime(); float chargeAmount = ( chargeTime - ( fullChargeTime - time ) ) / chargeTime; if ( chargeAmount > 1 ) { chargeAmount = 1; } sys.setGUIFloat( GUI_GLOBALS_HANDLE, "weapons.energyBarCharge", chargeAmount ); sys.setGUIFloat( GUI_GLOBALS_HANDLE, "weapons.energyAvailable", time >= cooldownTime ); if ( chargeAmount == 1.f ) { break; } sys.waitFrame(); } updateChargeThreadActive = false; } boolean deployable_antipersonnel::CanFire() { if ( myUser == $null_entity ) { return true; } float time = sys.getTime(); float chargeAmount = ( chargeTime - ( fullChargeTime - time ) ) / chargeTime; if ( chargeAmount >= chargePerShot && time > cooldownTime ) { return true; } return false; } void deployable_antipersonnel::Fired() { if ( myUser == $null_entity ) { return; } if ( !sys.isClient() ) { float timePerShot = chargeTime * chargePerShot; if ( fullChargeTime < sys.getTime() ) { fullChargeTime = sys.getTime(); } fullChargeTime = fullChargeTime + timePerShot; float time = sys.getTime(); if ( fullChargeTime >= time + chargeTime - timePerShot ) { cooldownTime = sys.getTime() + overheatPenalty; } } if ( !updateChargeThreadActive ) { if ( myUser == sys.getLocalPlayer() ) { thread UpdateCharge(); } } } void deployable_antipersonnel::FireMissile() { float muzzleJoint; float forceTracer = TRACER_CHANCE; if ( tracerInterval > 0 ) { if ( tracerCounter % tracerInterval == 0 ) { forceTracer = TRACER_FORCE; } } entity controller = owner; if ( controller == $null_entity ) { controller = self; } launchBullet( controller, self, projectileIndex, spread, getJointPos( jointBarrel ), getJointAxis( jointBarrel, 0 ), forceTracer, false ); // hit scan tracerCounter++; playJointEffect( "fx_fire", jointBarrel, 0 ); if( numTracerJoints > 1 ) { muzzleJoint = int( sys.random( numTracerJoints ) ) + 1; } else { muzzleJoint = numTracerJoints; } playEffect( "fx_muzzle", getKey( "joint_tracer" + muzzleJoint ), 0 ); Fired(); } void deployable_antipersonnel::OnBeginAttack() { KillAttackThread(); thread AttackThread(); if ( lockEntity != $null_entity ) { lockEntity.vTargetLockAttack( self ); } attacking = true; if ( spinBarrelJoint != -1 && !barrelThreadActive ) { thread SpinBarrelThread(); } playCycle( ANIMCHANNEL_ALL, "fire" ); } void deployable_antipersonnel::OnEndAttack() { attacking = false; playAnim( ANIMCHANNEL_ALL, "base" ); KillAttackThread(); } boolean deployable_antipersonnel::ShouldSpinBarrel() { return attacking && sys.getTime() > cooldownTime; } void deployable_antipersonnel::SpinBarrelThread() { barrelThreadActive = true; float speed = 0.f; float maxSpeed = 900; float frameTime; while ( attacking ) { if ( ShouldSpinBarrel() ) { startSound( "snd_fire_start", SND_DEPLOYABLE_FIRE ); startSound( "snd_fire_far", SND_DEPLOYABLE_FIRE_FAR ); startSound( "snd_brass", SND_DEPLOYABLE_BRASS ); while ( ShouldSpinBarrel() ) { frameTime = sys.getFrameTime(); speed = speed + ( frameTime * 600 ); if ( speed > maxSpeed ) { speed = maxSpeed; } barrelAngles_y = ( barrelAngles_y + ( frameTime * speed ) ) % 360; setJointAngle( spinBarrelJoint, JOINTMOD_LOCAL, barrelAngles ); sys.waitFrame(); } startSound( "snd_brass_stop", SND_DEPLOYABLE_BRASS ); startSound( "snd_fire_stop", SND_DEPLOYABLE_FIRE ); startSound( "snd_fire_far_stop", SND_DEPLOYABLE_FIRE_FAR ); } while ( !ShouldSpinBarrel() ) { sys.waitFrame(); frameTime = sys.getFrameTime(); speed = speed - ( frameTime * 600 ); if ( speed < 0 ) { speed = 0; break; } barrelAngles_y = ( barrelAngles_y + ( frameTime * speed ) ) % 360; setJointAngle( spinBarrelJoint, JOINTMOD_LOCAL, barrelAngles ); } } barrelThreadActive = false; } void deployable_antipersonnel::OnSetTarget( entity oldTarget, entity newTarget ) { if ( g_aptWarning.getIntValue() == 0 ) { return; } if ( lockEntity != newTarget ) { if ( lockEntity != $null_entity ) { lockEntity.vStopTargetLockAlarm( self ); lockEntity = $null_entity; } if ( newTarget != $null_entity ) { lockEntity = newTarget; lockEntity.vTargetLockAlarm( self ); } } } /* =============================================================================== deployable_antipersonnel_salvage - anti-personnel turret that can't be destroyed starts with zero health though =============================================================================== */ object deployable_antipersonnel_salvage : deployable_antipersonnel { void init(); void UpdateState( float health ); void CreateDestroyThread() { } void OnPostDamage( entity attacker, float oldHealth, float newHealth ); }; void deployable_antipersonnel_salvage::init() { setHealth( 1 ); UpdateState( 1 ); } void deployable_antipersonnel_salvage::OnPostDamage( entity attacker, float oldHealth, float newHealth ) { if ( newHealth <= 0 ) { setHealth( 1 ); newHealth = 1; } OnPostDamage_AP( attacker, oldHealth, newHealth ); } void deployable_antipersonnel_salvage::UpdateState( float health ) { float frac = health / getMaxHealth(); float newState; player p = sys.getLocalPlayer(); boolean showHealthStatus = false; if ( p != $null_entity ) { team_base team = p.getGameTeam(); float allegiance = getEntityAllegiance( p ); if ( allegiance == TA_FRIEND ) { showHealthStatus = true; } } float healthStatusParm = 0; if ( frac < ( 2.f / 3.f ) ) { newState = DS_DAMAGED; healthStatusParm = frac * 2; } else { newState = DS_NORMAL; } if ( showHealthStatus && !disabledState ) { sys.setCMIconShaderParm( commandMapHandle, 5, healthStatusParm ); } SetDeployableState( newState ); }