diff options
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/ai/_ai_suicide_spectres.gnut')
-rw-r--r-- | Northstar.CustomServers/scripts/vscripts/ai/_ai_suicide_spectres.gnut | 576 |
1 files changed, 0 insertions, 576 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_suicide_spectres.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_suicide_spectres.gnut deleted file mode 100644 index f8e0652ce..000000000 --- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_suicide_spectres.gnut +++ /dev/null @@ -1,576 +0,0 @@ -global function SuicideSpectres_Init -global function MakeSuicideSpectre -global function SpectreSuicideOnDamaged -global function GetNPCAttackerEnt - -const FX_SPECTRE_EXPLOSION = $"P_drone_frag_exp" - -// -// Suicide spectre script -// - -const SPECTRE_EXPLOSION_DELAY = 0.25 // Delay for the first spectre in a chain to start exploding. -const SPECTRE_DAMAGE_MULTIPLIER_BODY = 1.5 -const SPECTRE_DAMAGE_MULTIPLIER_HEAD = 6.0 -const SPECTRE_DAMAGE_MULTIPLIER_SMART_PISTOL = 2.0 -const SPECTRE_HEADSHOT_KEEP_WALKING_CHANCE = 100 // 35% chance to keep walking after a headshot to add variety - -struct -{ - int chainExplosionIndex - float lastChainExplosionTime - - table< string, array<string> > spectreAnims - float nextOverloadTime - -} file - -const SFX_TICK_OVERLOAD = "corporate_spectre_overload_beep" -const SFX_TICK_EXPLODE = "corporate_spectre_death_explode" - -const SFX_FRAGDRONE_OVERLOAD = "weapon_sentryfragdrone_preexplo" -const SFX_FRAGDRONE_EXPLODE = "weapon_sentryfragdrone_explo" -const SFX_FRAGDRONE_SUPERPURSUIT = "weapon_sentryfragdrone_superpursuit" - -const CHAIN_EXPLOSION_MAXINDEX = 10 - - -void function SuicideSpectres_Init() -{ - RegisterSignal( "SuicideSpectreForceExplode" ) - RegisterSignal( "SuicideSpectreExploding" ) - RegisterSignal( "SuicideGotEnemy" ) - RegisterSignal( "SuicideLostEnemy" ) - - PrecacheParticleSystem( FX_SPECTRE_EXPLOSION ) - - file.spectreAnims[ "spectreSearch" ] <- [] - file.spectreAnims[ "spectreSearch" ].append( "sp_suicide_spectre_search" ) - file.spectreAnims[ "spectreSearch" ].append( "sp_suicide_spectre_search_B" ) - file.spectreAnims[ "spectreSearch" ].append( "sp_suicide_spectre_search_C" ) - - AddDamageCallback( "npc_frag_drone", SpectreSuicideOnDamaged_Callback ) - AddDeathCallback( "npc_frag_drone", FragDroneDeath ) -} - -/************************************************************************************************\ - - ###### ######## ######## ## ## ######## -## ## ## ## ## ## ## ## -## ## ## ## ## ## ## - ###### ###### ## ## ## ######## - ## ## ## ## ## ## -## ## ## ## ## ## ## - ###### ######## ## ####### ## - -\************************************************************************************************/ -void function MakeSuicideSpectre( entity spectre ) -{ - spectre.SetAimAssistAllowed( true ) - spectre.SetAllowMelee( false ) - DisableLeeching( spectre ) - - spectre.SetNPCMoveSpeedScale( 1.0 ) - - spectre.EnableNPCMoveFlag( NPCMF_IGNORE_CLUSTER_DANGER_TIME | NPCMF_PREFER_SPRINT ) - spectre.DisableNPCMoveFlag( NPCMF_FOLLOW_SAFE_PATHS | NPCMF_INDOOR_ACTIVITY_OVERRIDE ) - - spectre.kv.allowShoot = 0 - - // Frag drones do suicide spectre behavior but we don't want them doing the enemy changed sounds so filter them out - if ( !IsFragDrone( spectre ) && !IsTick( spectre ) ) - spectre.SetEnemyChangeCallback( SuicideSpectreEnemyChanged ) - - spectre.SetLookDistOverride( SPECTRE_MAX_SIGHT_DIST ) - //spectre.SetHearingSensitivity( 10 ) //1 is default - spectre.EnableNPCFlag( NPC_MUTE_TEAMMATE ) - - spectre.ai.suicideSpectreExplosionDelay = -1 - - thread SpectreWaitToExplode( spectre ) - AddAnimEvent( spectre, "frag_drone_armed", FragDroneArmed ) -} - -void function FragDroneArmed( entity npc ) -{ - npc.ai.fragDroneArmed = true -} - -void function FragDroneDeath( entity spectre, var damageInfo ) -{ - FragDroneDeath_Think( spectre, damageInfo ) -} - -// for reloadscripts -void function FragDroneDeath_Think( entity spectre, var damageInfo ) -{ - vector pos = spectre.GetOrigin() - int tagID = spectre.LookupAttachment( "CHESTFOCUS" ) - vector fxOrg = spectre.GetAttachmentOrigin( tagID ) - string expSFX - if ( spectre.mySpawnOptions_aiSettings == "npc_frag_drone_throwable" ) - expSFX = SFX_FRAGDRONE_EXPLODE - else - expSFX = SFX_TICK_EXPLODE - int expFX = GetParticleSystemIndex( FX_SPECTRE_EXPLOSION ) - - entity attacker = DamageInfo_GetAttacker( damageInfo ) - entity attackerEnt = GetNPCAttackerEnt( spectre, attacker ) - - int team = GetExplosionTeamBasedOnGamemode( spectre ) - - int damageDef = GetDamageDefForFragDrone( spectre ) - - RadiusDamage_DamageDefSimple( damageDef, pos, attackerEnt, spectre, 0 ) - EmitSoundAtPosition( spectre.GetTeam(), pos, expSFX ) - CreateShake( pos, 10, 105, 1.25, 768 ) - StartParticleEffectInWorld( expFX, fxOrg, Vector( 0, 0, 0 ) ) - - spectre.Gib( <0, 0, 100> ) //Used to do .Destroy() on the frag drones immediately, but this meant you can't display the obiturary correctly. Instead, since it's dead already just hide it -} - -entity function GetNPCAttackerEnt( entity npc, entity attacker ) -{ - entity owner = npc.GetBossPlayer() - bool ownerIsPlayer = owner != null && owner.IsPlayer() - - if ( IsMultiplayer() ) - return ownerIsPlayer ? owner : npc - - if ( !IsAlive( attacker ) ) - return npc - - // dont give player credit, since that does some bad things - if ( ownerIsPlayer ) - return owner - - if ( attacker.IsPlayer() ) - return GetEnt( "worldspawn" ) - - return attacker -} - - -int function GetDamageDefForFragDrone( entity drone ) -{ - var damageDef = drone.Dev_GetAISettingByKeyField( "damageDefOverride" ) - if ( damageDef != null ) - { - expect string( damageDef ) - return eDamageSourceId[ damageDef ] - } - - entity owner = drone.GetBossPlayer() - if ( owner != null && owner.IsPlayer() ) - return damagedef_frag_drone_throwable_PLAYER - - return damagedef_frag_drone_throwable_NPC -} - -void function SuicideSpectreEnemyChanged( entity spectre ) -{ - // Spectre "Speaks" - if ( ( RandomFloat( 1.0 ) ) < 0.02 ) - EmitSoundOnEntity( spectre, "diag_imc_spectre_gs_spotenemypilot_01_1" ) -} - -/************************************************************************************************\ - -######## ######## ####### ## ## #### ## ## #### ######## ## ## -## ## ## ## ## ## ## ## ## ### ### ## ## ## ## -## ## ## ## ## ## ## ## ## #### #### ## ## #### -######## ######## ## ## ### ## ## ### ## ## ## ## -## ## ## ## ## ## ## ## ## ## ## ## ## -## ## ## ## ## ## ## ## ## ## ## ## ## -## ## ## ####### ## ## #### ## ## #### ## ## - -\************************************************************************************************/ -void function SpectreWaitToExplode( entity spectre ) -{ - Assert( spectre.IsNPC() ) - spectre.EndSignal( "OnDeath" ) - - waitthread SuicideSpectre_WaittillNearEnemyOrExploding( spectre ) - - if ( spectre.ai.suicideSpectreExplodingAttacker == null ) - { - // not exploding, so overload - spectre.ai.suicideSpectreExplosionDelay = GetSpectreExplosionTime( spectre ) - waitthread SpectreOverloads( spectre ) - } - - if ( spectre.ai.suicideSpectreExplosionDelay > 0 ) - wait spectre.ai.suicideSpectreExplosionDelay - - entity attacker = spectre.ai.suicideSpectreExplodingAttacker - if ( !IsValid( attacker ) ) - { - entity lastAttacker = GetLastAttacker( spectre ) - if ( IsValid( lastAttacker ) ) - { - attacker = lastAttacker - } - else - { - attacker = spectre - } - } - - vector force = GetDeathForce() - - Assert( !attacker.IsProjectile(), "Suicide Spectre attacker was a projectile! Type: " + attacker.ProjectileGetWeaponClassName() ) - - // JFS: sometimes the attacker is a projectile, which can cause a script error. - // The real solution is to figure out which weapon is passing in the projectile as the attacker and correct that. - if ( attacker.IsProjectile() ) - { - attacker = spectre - } - - spectre.Die( attacker, attacker, { force = force, scriptType = DF_DOOMED_HEALTH_LOSS, damageSourceId = eDamageSourceId.suicideSpectreAoE } ) -} - -void function SetSuicideSpectreExploding( entity spectre, entity attacker, float explodingTime ) -{ - Assert( spectre.ai.suicideSpectreExplodingAttacker == null ) - spectre.ai.suicideSpectreExplodingAttacker = attacker - spectre.ai.suicideSpectreExplosionDelay = explodingTime - - spectre.Signal( "SuicideSpectreExploding" ) -} - -float function GetSpectreExplosionTime( entity spectre ) -{ - if ( Time() - file.lastChainExplosionTime > 1.0 ) - file.chainExplosionIndex = 0 - - float waitTime = file.chainExplosionIndex * 0.14 // RandomFloatRange( CHAIN_EXPLOSION_INTERVALMIN, CHAIN_EXPLOSION_INTERVALMAX ) - file.lastChainExplosionTime = Time() - file.chainExplosionIndex++ - return waitTime -} - -void function SuicideSpectre_WaittillNearEnemyOrExploding( entity spectre ) -{ - spectre.EndSignal( "OnDeath" ) - spectre.EndSignal( "SuicideSpectreExploding" ) - spectre.EndSignal( "SuicideSpectreForceExplode" ) - - bool pursuitSoundPlaying = false - - float minScale = expect float( spectre.Dev_GetAISettingByKeyField( "minSpeedScale" ) ) - float maxScale = expect float( spectre.Dev_GetAISettingByKeyField( "maxSpeedScale" ) ) - - while ( true ) - { - wait 0.1 - - if ( !spectre.ai.fragDroneArmed ) - continue - - if ( spectre.ai.suicideSpectreExplodingAttacker != null ) - return - - //If spectre is not interrruptable, don't bother - if ( !spectre.IsInterruptable() ) - continue - - //If spectre is parented, don't bother - if ( IsValid( spectre.GetParent() ) ) - continue - - // speed up when near enemy - entity enemy = spectre.GetEnemy() - if ( IsAlive( enemy ) ) - { - float dist = Distance( enemy.GetOrigin(), spectre.GetOrigin() ) - float maxDist = 850 - if ( spectre.mySpawnOptions_aiSettings == "npc_frag_drone_throwable" ) - { - if ( dist < maxDist ) - { - if ( pursuitSoundPlaying == false ) - { - EmitSoundOnEntity( spectre, SFX_FRAGDRONE_SUPERPURSUIT ) - pursuitSoundPlaying = true - } - } - else - { - if ( pursuitSoundPlaying == true ) - { - StopSoundOnEntity( spectre, SFX_FRAGDRONE_SUPERPURSUIT ) - pursuitSoundPlaying = false - } - } - } - float speed = GraphCapped( dist, 200, 850, maxScale, minScale ) - spectre.SetNPCMoveSpeedScale( speed ) - } - - // offset the overload time - if ( Time() < file.nextOverloadTime ) - continue - - entity attacker = SuicideSpectre_NearEnemy( spectre ) - if ( attacker != null ) - { - //SetSuicideSpectreOverloading( spectre, attacker ) - //Assert( 0 ) // never reached - return - } - } -} - -entity function SuicideSpectre_NearEnemy( entity spectre ) -{ - // See if any player is close eneough to trigger self-destruct - array<entity> enemies - entity closestEnemy = spectre.GetClosestEnemy() - if ( closestEnemy ) - enemies.append( closestEnemy ) - - entity currentEnemy = spectre.GetEnemy() - if ( currentEnemy && currentEnemy != closestEnemy ) - enemies.append( currentEnemy ) - - vector origin = spectre.GetOrigin() - float dist = expect float( spectre.Dev_GetAISettingByKeyField( "suicideExplosionDistance" ) ) - foreach ( enemy in enemies ) - { - if ( !IsAlive( enemy ) ) - continue - if ( enemy.IsCloaked( true ) ) - continue - if ( enemy.GetNoTarget() ) - continue - if ( enemy.IsPlayer() && enemy.IsPhaseShifted() ) - continue - - vector enemyOrigin = enemy.GetOrigin() - - if ( Distance( origin, enemyOrigin ) > dist ) - continue - - float heightDiff = enemyOrigin.z - origin.z - - // dont explode because you jump over me or I am on the floor above you - if ( fabs( heightDiff ) > 40 ) - { - // unless enemy is standing on something slightly above you and there is a clear trace - float curTime = Time() - float timeDiff = curTime - spectre.ai.suicideSpectreExplosionTraceTime - const float TRACE_INTERVAL = 2 - - if ( heightDiff > 0 && timeDiff > TRACE_INTERVAL && enemy.IsOnGround() && spectre.CanSee( enemy ) ) - { - spectre.ai.suicideSpectreExplosionTraceTime = curTime - float frac = TraceHullSimple( origin, < origin.x, origin.y, enemyOrigin.z >, spectre.GetBoundingMins(), spectre.GetBoundingMaxs(), spectre ) - if ( frac == 1.0 ) - return enemy - } - continue - } - - return enemy - } - - return null -} - -void function SpectreOverloads( entity spectre ) -{ - spectre.EndSignal( "SuicideSpectreExploding" ) - file.nextOverloadTime = Time() + 0.05 - - #if MP - var chaseTime = spectre.Dev_GetAISettingByKeyField( "SuicideChaseTime" ) - if ( chaseTime != null ) - { - float maxScale = expect float( spectre.Dev_GetAISettingByKeyField( "maxSpeedScale" ) ) - spectre.SetNPCMoveSpeedScale( maxScale ) - - expect float( chaseTime ) - float endChaseTime = Time() + chaseTime - - for ( ;; ) - { - if ( Time() >= endChaseTime ) - break - - if ( !IsAlive( spectre.GetEnemy() ) ) - break - - entity nearEnemy = SuicideSpectre_NearEnemy( spectre ) - if ( IsAlive( nearEnemy ) ) - { - if ( nearEnemy.IsTitan() && spectre.IsInterruptable() ) - { - JumpAtTitan( spectre, nearEnemy ) - spectre.ai.suicideSpectreExplosionDelay = 0.0 - return - } - break - } - - WaitFrame() - } - } - #endif - - for ( ;; ) - { - #if SP - if ( spectre.IsInterruptable() && !spectre.Anim_IsActive() ) - break - #elseif MP - if ( spectre.IsInterruptable() && !spectre.Anim_IsActive() && spectre.IsOnGround() ) - break - #endif - - WaitFrame() - } - - string overloadSF - bool isFragDrone = spectre.mySpawnOptions_aiSettings == "npc_frag_drone_throwable" - if ( isFragDrone ) - overloadSF = SFX_FRAGDRONE_OVERLOAD - else - overloadSF = SFX_TICK_OVERLOAD - // Overload Sound - EmitSoundOnEntity( spectre, overloadSF ) - - AI_CreateDangerousArea_DamageDef( damagedef_frag_drone_explode, spectre, TEAM_INVALID, true, false ) - - // Cleanup on thread end - OnThreadEnd( - function() : ( spectre, overloadSF ) - { - if ( IsValid( spectre ) ) - { - StopSoundOnEntity( spectre, overloadSF ) - } - } - ) - - bool jumpAtTitans = spectre.Dev_GetAISettingByKeyField( "JumpAtTitans" ) == null || spectre.Dev_GetAISettingByKeyField( "JumpAtTitans" ) == 1 - - entity enemy = spectre.GetEnemy() - if ( enemy && enemy.IsTitan() && jumpAtTitans && !spectre.IsInterruptable() ) - { - JumpAtTitan( spectre, enemy ) - } - else - { - string anim = "sp_suicide_spectre_explode_stand" - var overrideAnim = spectre.Dev_GetAISettingByKeyField( "OverrideOverloadAnim" ) - - if ( overrideAnim != null ) - { - anim = expect string( overrideAnim ) - } - - waitthread PlayAnim( spectre, anim ) - - if ( !isFragDrone ) - wait 0.25 - } -} - -void function JumpAtTitan( entity spectre, entity enemy ) -{ - vector myOrigin = spectre.GetOrigin() - vector dirToEnemy = enemy.EyePosition() - myOrigin - - float dist = Length( dirToEnemy ) - if ( dist > 0 ) - { - const float MAX_DIST = 100 - dirToEnemy *= min( MAX_DIST, dist ) / dist - } - - vector refOrigin = myOrigin + Vector( dirToEnemy.x, dirToEnemy.y, 256 ) - vector refAngles = spectre.GetAngles() + Vector( 0, 180, 0 ) - spectre.Anim_ScriptedPlayWithRefPoint( "sd_jump_explode", refOrigin, refAngles, 0.3 ) - WaittillAnimDone( spectre ) - return -} - -int function GetExplosionTeamBasedOnGamemode( entity spectre ) -{ - return spectre.GetTeam() -} - - -/************************************************************************************************\ - -######## ### ## ## ### ###### ######## -## ## ## ## ### ### ## ## ## ## ## -## ## ## ## #### #### ## ## ## ## -## ## ## ## ## ### ## ## ## ## #### ###### -## ## ######### ## ## ######### ## ## ## -## ## ## ## ## ## ## ## ## ## ## -######## ## ## ## ## ## ## ###### ######## - -\************************************************************************************************/ -void function SpectreSuicideOnDamaged_Callback( entity spectre, var damageInfo ) -{ - SpectreSuicideOnDamaged( spectre, damageInfo ) -} - - -void function SpectreSuicideOnDamaged( entity spectre, var damageInfo ) -{ - //Assert( IsSuicideSpectre( spectre ) ) - - int damageType = DamageInfo_GetCustomDamageType( damageInfo ) - DamageInfo_SetCustomDamageType( damageInfo, damageType ) - - if ( !IsAlive( spectre ) ) - return - - - entity attacker = DamageInfo_GetAttacker( damageInfo ) - entity inflictor = DamageInfo_GetInflictor( damageInfo ) - float damage = DamageInfo_GetDamage( damageInfo ) - int damageSourceId = DamageInfo_GetDamageSourceIdentifier( damageInfo ) - - // Calculate build time credit - if ( attacker.IsPlayer() ) - { - if ( GameModeRulesShouldGiveTimerCredit( attacker, spectre, damageInfo ) && !TitanDamageRewardsTitanCoreTime() ) - { - float timerCredit = CalculateBuildTimeCredit( attacker, spectre, damage, spectre.GetHealth(), spectre.GetMaxHealth(), "spectre_kill_credit", 9 ) - if ( timerCredit ) - DecrementBuildTimer( attacker, timerCredit ) - } - } - - // No pain anims for suicide spectres - DamageInfo_AddDamageFlags( damageInfo, DAMAGEFLAG_NOPAIN ) - - - spectre.Signal( "SuicideSpectreExploding" ) - - if ( !IsValid( inflictor ) || !inflictor.IsPlayer() ) - { - if ( spectre.ai.suicideSpectreExplodingAttacker == null ) - { - if ( spectre.GetHealth() - damage <= 0 || ( IsValid( inflictor ) && IsTick( inflictor ) ) ) - { - float explosionTime = GetSpectreExplosionTime( spectre ) - SetSuicideSpectreExploding( spectre, attacker, explosionTime ) - DamageInfo_SetDamage( damageInfo, 0 ) - return - } - } - else - { - // already exploding - DamageInfo_SetDamage( damageInfo, 0 ) - return - } - - DamageInfo_SetDamage( damageInfo, damage ) - } -} |