object deployable_ssm : deployable_base { void init(); void preinit(); void syncFields(); void destroy(); void Idle(); void Ready(); void Launch(); void Reloading(); void CheckReload(); void CheckLaunch(); boolean vTargetPlayerEligible( entity p ); boolean vTargetGetValid( vector pos ); void vTargetSetTarget( vector targetPos, entity targetEnt ); void vDoneDeploy(); void vOnDeploy(); boolean CalcFiringVelocity( vector targetPos, vector launchPos ); void PlayRaiseAnim(); void PlayLowerAnim(); void MissileCheck(); void OnMissileChanged(); void OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location ); void ClearFiringDecal(); void CreateFiringDecal(); target_marker vCreateTargetMarker(); float vGetFireSupportMode(); float projectileIndex; float launchJoint; float sync_reloadTime; float reloadTime; boolean hasTarget; vector target; entity targetEntity; void vStartMagogDrop(); void StartBarrelUpdateThread(); void BarrelUpdateThread(); void OnBarrelChanged(); entity barrelEntity; boolean barrelThreadActive; boolean dropping; float targetHeight; vector firingVelocity; entity missile; boolean missileActive; generic_target_marker firingDecal; entity firingMarker; } void deployable_ssm::syncFields() { syncBroadcast( "sync_reloadTime" ); syncBroadcast( "hasTarget" ); syncBroadcast( "target" ); syncBroadcast( "missile" ); syncBroadcast( "barrelEntity" ); syncCallback( "missile", "OnMissileChanged" ); syncCallback( "barrelEntity", "OnBarrelChanged" ); } float deployable_ssm::vGetFireSupportMode() { return TARGET_SSM; } void deployable_ssm::preinit() { float entityDeclIndex; reloadTime = getFloatKey( "reload_time" ); sync_reloadTime = 0; hasTarget = false; launchJoint = getJointHandle( getKey( "joint_launch" ) ); targetHeight = getFloatKey( "target_height" ); entityDeclIndex = sys.getDeclType( "entityDef" ); projectileIndex = sys.getDeclIndex( entityDeclIndex, getKey( "def_projectile" ) ); fsStatus = new fireSupportStatus; if ( !sys.isClient() ) { thread MissileCheck(); } } void deployable_ssm::init() { if ( !sys.isClient() ) { // HACK: barrel clip barrelEntity = sys.spawn( getKey( "def_barrel_clip" ) ); OnBarrelChanged(); } setState( "Idle" ); } void deployable_ssm::OnBarrelChanged() { float barrelJoint = getJointHandle( "barrel_base" ); if ( barrelJoint != INVALID_JOINT ) { vector org = getJointPos( barrelJoint ); vector ang = getJointAngle( barrelJoint ); barrelEntity.setContents( CONTENTS_PLAYERCLIP | CONTENTS_VEHICLECLIP | CONTENTS_FLYERHIVECLIP ); barrelEntity.unbind(); barrelEntity.setOrigin( org ); barrelEntity.setAngles( ang ); barrelEntity.bindToJoint( self, "barrel_base", 1 ); } } void deployable_ssm::StartBarrelUpdateThread() { if ( barrelThreadActive ) { return; } barrelThreadActive = true; thread BarrelUpdateThread(); } void deployable_ssm::BarrelUpdateThread() { float damageIndex = GetDamage( getKey( "dmg_crush" ) ); vector mins = barrelEntity.getMins(); vector maxs = barrelEntity.getMaxs(); while ( isAnimating() || dropping ) { barrelEntity.forceRunPhysics(); if ( !sys.isClient() ) { LocalBoundsDamage( mins, maxs, barrelEntity, self, damageIndex ); } sys.waitFrame(); } barrelThreadActive = false; } void deployable_ssm::vStartMagogDrop() { dropping = true; StartBarrelUpdateThread(); } void deployable_ssm::vOnDeploy() { dropping = false; thread DoDeploy(); } void deployable_ssm::destroy() { delete fsStatus; if ( !sys.isClient() && barrelEntity != $null_entity ) { barrelEntity.remove(); } ClearFiringDecal(); } void deployable_ssm::OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location ) { if ( !sys.isClient() && barrelEntity != $null_entity ) { barrelEntity.remove(); } DeployableBase_OnKilled( attacker, inflictor ); } void deployable_ssm::Idle() { } void deployable_ssm::OnMissileChanged() { if ( missile != $null_entity ) { CreateFiringDecal(); } else { ClearFiringDecal(); } } void deployable_ssm::vDoneDeploy() { PlayRaiseAnim(); SetDeployingFinished(); setState( "Ready" ); } void deployable_ssm::Ready() { hasTarget = false; fireSupportState = MPS_READY; while( 1 ) { CheckLaunch(); CheckReload(); sys.waitFrame(); } } void deployable_ssm::MissileCheck() { while ( true ) { if ( missile != $null_entity ) { missileActive = true; } else { if ( missileActive ) { missileActive = false; ClearFiringDecal(); } } sys.waitFrame(); } } void deployable_ssm::CheckLaunch() { if( hasTarget ) { setState( "Launch" ); } } void deployable_ssm::CheckReload() { if ( sys.getTime() < sync_reloadTime ) { setState( "Reloading" ); } } boolean deployable_ssm::CalcFiringVelocity( vector targetPos, vector launchPos ) { vector targetDiff = targetPos - launchPos; firingVelocity_x = 0; firingVelocity_y = 0; firingVelocity_z = sys.sqrt( 2 * 400 * targetHeight ); float upTime = firingVelocity_z / 400; float downTime = ( 2 * ( targetDiff_z - targetHeight ) ) / -400; if ( downTime < 0.f ) { return false; } downTime = sys.sqrt( downTime ); float t1 = upTime + downTime; vector temp = targetDiff; temp_z = 0.f; targetDiff = sys.vecNormalize( temp ); float diffX = sys.vecLength( temp ); firingVelocity = firingVelocity + ( ( diffX / t1 ) * targetDiff ); return true; } void deployable_ssm::Launch() { CreateFiringDecal(); vector missilePos; string anim; float channel; fireSupportState = MPS_FIRING; missilePos = getJointPos( launchJoint ); if ( CalcFiringVelocity( target, missilePos ) ) { if( !sys.isClient() ) { missile = launchMissileSimple( owner, self, $null_entity, projectileIndex, -1, 0.f, missilePos, firingVelocity ); missile.vSetNewTarget( targetEntity ); } } else { ClearFiringDecal(); } anim = getKey( "missile_anim" ); if ( anim != "" ) { channel = getIntKey( "missile_channel" ); playAnim( channel, anim ); StartBarrelUpdateThread(); } objManager.CPrintEvent( sys.localizeString( "game/hammer_fired" ), $null ); if ( !sys.isClient() ) { sync_reloadTime = sys.getTime() + reloadTime; } sys.wait( 3 ); setState( "Reloading" ); } void deployable_ssm::Reloading() { boolean playAnims = currentState == DS_NORMAL; if ( playAnims ) { PlayLowerAnim(); } fireSupportState = MPS_RELOADING; while ( 1 ) { if ( sys.getTime() > sync_reloadTime ) { break; } sys.waitFrame(); } float waitTime = getFloatKey( "stay_lowered_time" ); if( waitTime <= 0 ) { waitTime = 15; } sys.wait( waitTime ); if ( playAnims && currentState == DS_NORMAL ) { PlayRaiseAnim(); } setState( "Ready" ); } boolean deployable_ssm::vTargetPlayerEligible( entity p ) { if ( disabledState || !finishedDeploying ) { return 0.f; } return 1.f; } boolean deployable_ssm::vTargetGetValid( vector pos ) { return true; } void deployable_ssm::vTargetSetTarget( vector targetPos, entity targetEnt ) { hasTarget = true; target = targetPos; targetEntity = targetEnt; Base_TargetSetTarget( targetPos, targetEnt ); } void deployable_ssm::PlayLowerAnim() { startSound( "snd_lower", SND_ANY ); playAnim( ANIMCHANNEL_ALL, "lowering" ); StartBarrelUpdateThread(); waitUntil( !isAnimating() ); } void deployable_ssm::PlayRaiseAnim() { startSound( "snd_raise", SND_ANY ); playAnim( ANIMCHANNEL_ALL, "raising" ); StartBarrelUpdateThread(); waitUntil( !isAnimating() ); } target_marker deployable_ssm::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_ssm::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_ssm::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 ) { firingDecal = vCreateTargetMarker(); } firingDecal.SetTargetPosition( target ); }