aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/ai
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
commit9a96d0bff56f1969c68bb52a2f33296095bdc67d (patch)
tree4175928e488632705692e3cccafa1a38dd854615 /Northstar.CustomServers/scripts/vscripts/ai
parent27bd240871b7c0f2f49fef137718b2e3c208e3b4 (diff)
downloadNorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.tar.gz
NorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.zip
move to new mod format
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/ai')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_chatter.gnut129
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_cloak_drone.gnut678
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_drone.gnut1388
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_emp_titans.gnut181
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_gunship.gnut97
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_lethality.gnut97
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_faces.gnut226
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_jobs.gnut600
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_marvins.gnut141
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_spectres.gnut7
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_titans.gnut395
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_nuke_titans.gnut129
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_personal_shield.gnut371
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_pilots.gnut808
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_sniper_titans.gnut1
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut787
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers_mp.gnut1
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn.gnut696
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn_content.gnut879
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_spectre.gnut131
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_stalker.gnut606
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut261
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_suicide_spectres.gnut576
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_turret.gnut24
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_ai_utility.gnut558
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_droppod.gnut6
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_droppod_fireteam.gnut246
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_squad_spawn.gnut167
-rw-r--r--Northstar.CustomServers/scripts/vscripts/ai/_titan_npc_behavior.gnut404
29 files changed, 0 insertions, 10590 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_chatter.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_chatter.gnut
deleted file mode 100644
index 0429895b1..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_chatter.gnut
+++ /dev/null
@@ -1,129 +0,0 @@
-global function DialogueChatter_Init
-
-global function TitanVO_AlertTitansIfTargetWasKilled
-global function TitanVO_TellPlayersThatAreAlsoFightingThisTarget
-global function TitanVO_AlertTitansTargetingThisTitanOfRodeo
-global function TitanVO_DelayedTitanDown
-
-const TITAN_VO_DIST_SQR = 2000 * 2000
-
-const CHATTER_TIME_LAPSE = 30.0
-//const CHATTER_TIME_LAPSE = 5.0 //For testing
-//const CHATTER_TIME_LAPSE = 8.0 //For testing
-//const CHATTER_TIME_LAPSE = 15.0 //For testing
-
-void function DialogueChatter_Init()
-{
-}
-
-void function TitanVO_TellPlayersThatAreAlsoFightingThisTarget( entity attacker, entity soul )
-{
- int voEnum
- if ( attacker.IsTitan() )
- voEnum = eTitanVO.FRIENDLY_TITAN_HELPING
- else
- voEnum = eTitanVO.PILOT_HELPING
-
- bool atackerIsTitan = attacker.IsTitan()
- int attackerTeam = attacker.GetTeam()
- array<entity> players = GetPlayerArray()
- foreach ( player in players )
- {
- if ( !player.IsTitan() )
- continue
-
- if ( player.GetTeam() != attackerTeam )
- continue
- // attacker gets a score callout
- if ( player == attacker )
- continue
-
- if ( soul != player.p.currentTargetPlayerOrSoul_Ent )
- continue
-
- float timeDif = Time() - player.p.currentTargetPlayerOrSoul_LastHitTime
- if ( timeDif > CURRENT_TARGET_FORGET_TIME )
- continue
-
- // alert other player that cared about this target
- Remote_CallFunction_Replay( player, "SCB_TitanDialogue", voEnum )
- }
-}
-
-void function TitanVO_AlertTitansTargetingThisTitanOfRodeo( entity rodeoer, entity soul )
-{
- int team = rodeoer.GetTeam()
-
- array<entity> players = GetPlayerArray()
- foreach ( player in players )
- {
- if ( !player.IsTitan() )
- continue
-
- if ( player.GetTeam() != team )
- continue
-
- if ( soul != player.p.currentTargetPlayerOrSoul_Ent )
- continue
-
- // if we havent hurt the target recently then forget about it
- if ( Time() - player.p.currentTargetPlayerOrSoul_LastHitTime > CURRENT_TARGET_FORGET_TIME )
- continue
-
- Remote_CallFunction_Replay( player, "SCB_TitanDialogue", eTitanVO.FRIENDLY_RODEOING_ENEMY )
- }
-}
-
-void function TitanVO_DelayedTitanDown( entity ent )
-{
- vector titanOrigin = ent.GetOrigin()
- int team = ent.GetTeam()
-
- wait 0.9
-
- array<entity> playerArray = GetPlayerArray()
- float dist = TITAN_VO_DIST_SQR
-
- foreach ( player in playerArray )
- {
- // only titans get BB vo
- if ( !player.IsTitan() )
- continue
-
- if ( DistanceSqr( titanOrigin, player.GetOrigin() ) > dist )
- continue
-
- if ( player.GetTeam() != team )
- Remote_CallFunction_Replay( player, "SCB_TitanDialogue", eTitanVO.ENEMY_TITAN_DEAD )
- else
- Remote_CallFunction_Replay( player, "SCB_TitanDialogue", eTitanVO.FRIENDLY_TITAN_DEAD )
- }
-}
-
-
-void function TitanVO_AlertTitansIfTargetWasKilled( entity victim, entity attacker )
-{
- array<entity> enemyPlayers = GetPlayerArrayOfEnemies( victim.GetTeam() )
-
- if ( victim.IsTitan() )
- victim = victim.GetTitanSoul()
-
- foreach ( player in enemyPlayers )
- {
- if ( !player.IsTitan() )
- continue
-
- // attacker gets a score callout
- if ( player == attacker )
- continue
-
- if ( victim != player.p.currentTargetPlayerOrSoul_Ent )
- continue
-
- if ( Time() - player.p.currentTargetPlayerOrSoul_LastHitTime > CURRENT_TARGET_FORGET_TIME )
- continue
-
- // alert other player that cared about this target
- Remote_CallFunction_Replay( player, "SCB_TitanDialogue", eTitanVO.ENEMY_TARGET_ELIMINATED )
- }
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_cloak_drone.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_cloak_drone.gnut
deleted file mode 100644
index e3addf812..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_cloak_drone.gnut
+++ /dev/null
@@ -1,678 +0,0 @@
-untyped
-
-global function CloakDrone_Init
-
-global function SpawnCloakDrone
-global function GetNPCCloakedDrones
-global function RemoveLeftoverCloakedDrones
-const FX_DRONE_CLOAK_BEAM = $"P_drone_cloak_beam"
-
-const float CLOAK_DRONE_REACHED_HARVESTER_DIST = 1300.0
-
-struct
-{
- int cloakedDronesManagedEntArrayID
- table<entity,string> cloakedDroneClaimedSquadList
-} file
-
-struct CloakDronePath
-{
- vector start
- vector goal
- bool goalValid = false
- float lastHeight
-}
-
-function CloakDrone_Init()
-{
- PrecacheParticleSystem( FX_DRONE_CLOAK_BEAM )
-
- file.cloakedDronesManagedEntArrayID = CreateScriptManagedEntArray()
-
- RegisterSignal( "DroneCleanup" )
- RegisterSignal( "DroneCrashing" )
-}
-
-entity function SpawnCloakDrone( int team, vector origin, vector angles, vector towerOrigin )
-{
- int droneCount = GetNPCCloakedDrones().len()
-
- // add some minor randomness to the spawn location as well as an offset based on number of drones in the world.
- origin += < RandomIntRange( -64, 64 ), RandomIntRange( -64, 64 ), 300 + (droneCount * 128) >
-
- entity cloakedDrone = CreateGenericDrone( team, origin, angles )
- SetSpawnOption_AISettings( cloakedDrone, "npc_drone_cloaked" )
-
- //these enable global damage callbacks for the cloakedDrone
- cloakedDrone.s.isHidden <- false
- cloakedDrone.s.fx <- null
- cloakedDrone.s.towerOrigin <- towerOrigin
-
- DispatchSpawn( cloakedDrone )
- SetTeam( cloakedDrone, team )
- SetTargetName( cloakedDrone, "Cloak Drone" )
- cloakedDrone.SetTitle( "#NPC_CLOAK_DRONE" )
- cloakedDrone.SetMaxHealth( 250 )
- cloakedDrone.SetHealth( 250 )
- cloakedDrone.SetTakeDamageType( DAMAGE_YES )
- cloakedDrone.SetDamageNotifications( true )
- cloakedDrone.SetDeathNotifications( true )
- cloakedDrone.Solid()
- cloakedDrone.Show()
- cloakedDrone.EnableNPCFlag( NPC_IGNORE_ALL )
-
- EmitSoundOnEntity( cloakedDrone, CLOAKED_DRONE_HOVER_LOOP_SFX )
- EmitSoundOnEntity( cloakedDrone, CLOAKED_DRONE_LOOPING_SFX )
- EmitSoundOnEntity( cloakedDrone, CLOAKED_DRONE_WARP_IN_SFX )
-
- cloakedDrone.s.fx = CreateDroneCloakBeam( cloakedDrone )
-
- SetVisibleEntitiesInConeQueriableEnabled( cloakedDrone, true )
-
- thread CloakedDronePathThink( cloakedDrone )
- thread CloakedDroneCloakThink( cloakedDrone )
-
- #if R1_VGUI_MINIMAP
- cloakedDrone.Minimap_SetDefaultMaterial( $"vgui/hud/cloak_drone_minimap_orange" )
- #endif
- cloakedDrone.Minimap_SetAlignUpright( true )
- cloakedDrone.Minimap_AlwaysShow( TEAM_IMC, null )
- cloakedDrone.Minimap_AlwaysShow( TEAM_MILITIA, null )
- cloakedDrone.Minimap_SetObjectScale( MINIMAP_CLOAKED_DRONE_SCALE )
- cloakedDrone.Minimap_SetZOrder( MINIMAP_Z_NPC )
-
- ShowName( cloakedDrone )
-
- AddToGlobalCloakedDroneList( cloakedDrone )
- return cloakedDrone
-}
-
-function AddToGlobalCloakedDroneList( cloakedDrone )
-{
- AddToScriptManagedEntArray( file.cloakedDronesManagedEntArrayID, cloakedDrone )
-}
-
-array<entity> function GetNPCCloakedDrones()
-{
- return GetScriptManagedEntArray( file.cloakedDronesManagedEntArrayID )
-}
-
-function RemoveLeftoverCloakedDrones()
-{
- array<entity> droneArray = GetNPCCloakedDrones()
- foreach ( cloakedDrone in droneArray )
- {
- thread CloakedDroneWarpOutAndDestroy( cloakedDrone )
- }
-}
-
-void function CloakedDroneWarpOutAndDestroy( entity cloakedDrone )
-{
- cloakedDrone.EndSignal( "OnDestroy" )
- cloakedDrone.EndSignal( "OnDeath" )
- cloakedDrone.SetInvulnerable()
-
- CloakedDroneWarpOut( cloakedDrone, cloakedDrone.GetOrigin() )
- cloakedDrone.Destroy()
-}
-
-/************************************************************************************************\
-
- ###### ## ####### ### ## ## #### ## ## ######
-## ## ## ## ## ## ## ## ## ## ### ## ## ##
-## ## ## ## ## ## ## ## ## #### ## ##
-## ## ## ## ## ## ##### ## ## ## ## ## ####
-## ## ## ## ######### ## ## ## ## #### ## ##
-## ## ## ## ## ## ## ## ## ## ## ### ## ##
- ###### ######## ####### ## ## ## ## #### ## ## ######
-
-\************************************************************************************************/
-//HACK - this should probably move into code
-function CloakedDroneCloakThink( cloakedDrone )
-{
- expect entity( cloakedDrone )
-
- cloakedDrone.EndSignal( "OnDestroy" )
- cloakedDrone.EndSignal( "OnDeath" )
- cloakedDrone.EndSignal( "DroneCrashing" )
- cloakedDrone.EndSignal( "DroneCleanup" )
-
- wait 2 // wait a few seconds since it would start cloaking before picking an npc to follow
- // some npcs might not be picked since they where already cloaked by accident.
-
- CloakerThink( cloakedDrone, 400.0, [ "any" ], < 0, 0, -350 >, CloakDroneShouldCloakGuy, 1.5 )
-}
-
-function CloakDroneShouldCloakGuy( cloakedDrone, guy )
-{
- expect entity( guy )
- if ( !( guy.IsTitan() || IsSpectre( guy ) || IsGrunt( guy ) || IsSuperSpectre( guy ) ) )
- return false
-
- if ( guy.GetTargetName() == "empTitan" )
- return false
-
- if ( IsSniperSpectre( guy ) )
- return false
-
- if ( IsValid( GetRodeoPilot( guy ) ) )
- return false
-
- if ( cloakedDrone.s.isHidden )
- return false
-
- if ( StatusEffect_Get( guy, eStatusEffect.sonar_detected ) )
- return false
-
- // if ( !cloakedDrone.CanSee( guy ) )
- // return false
-
- return true
-}
-
-/************************************************************************************************\
-
-######## ### ######## ## ## #### ## ## ######
-## ## ## ## ## ## ## ## ### ## ## ##
-## ## ## ## ## ## ## ## #### ## ##
-######## ## ## ## ######### ## ## ## ## ## ####
-## ######### ## ## ## ## ## #### ## ##
-## ## ## ## ## ## ## ## ### ## ##
-## ## ## ## ## ## #### ## ## ######
-
-\************************************************************************************************/
-//HACK -> this should probably move into code
-const VALIDPATHFRAC = 0.99
-
-void function CloakedDronePathThink( entity cloakedDrone )
-{
- cloakedDrone.EndSignal( "OnDestroy" )
- cloakedDrone.EndSignal( "OnDeath" )
- cloakedDrone.EndSignal( "DroneCrashing" )
- cloakedDrone.EndSignal( "DroneCleanup" )
-
- entity goalNPC = null
- entity previousNPC = null
- vector spawnOrigin = cloakedDrone.GetOrigin()
- vector lastOrigin = cloakedDrone.GetOrigin()
- float stuckDistSqr = 64.0*64.0
- float targetLostTime = Time()
- array<entity> claimedGuys = []
-
- while( 1 )
- {
- while( goalNPC == null )
- {
- wait 1.0
- array<entity> testArray = GetNPCArrayEx( "any", cloakedDrone.GetTeam(), TEAM_ANY, < 0, 0, 0 >, -1 )
-
- // remove guys already being followed by an cloakedDrone
- // or in other ways not suitable
- array<entity> NPCs = []
- foreach ( guy in testArray )
- {
- if ( !IsAlive( guy ) )
- continue
-
- //Only cloak titans, spectres, grunts,
- if ( !( guy.IsTitan() || IsSpectre( guy ) || IsGrunt( guy ) || IsSuperSpectre( guy ) ) )
- continue
-
- //Don't cloak arc titans
- if ( guy.GetTargetName() == "empTitan" )
- continue
-
- if ( IsSniperSpectre( guy ) )
- continue
-
- if ( IsFragDrone( guy ) )
- continue
-
- if ( guy == previousNPC )
- continue
-
- if ( guy.ContextAction_IsBusy() )
- continue
-
- if ( guy.GetParent() != null )
- continue
-
- if ( IsCloaked( guy ) )
- continue
-
- if ( IsSquadCenterClose( guy ) == false )
- continue
-
- if ( "cloakedDrone" in guy.s && IsAlive( expect entity( guy.s.cloakedDrone ) ) )
- continue
-
- if ( CloakedDroneIsSquadClaimed( expect string( guy.kv.squadname ) ) )
- continue
-
- if ( IsValid( GetRodeoPilot( guy ) ) )
- continue
-
- if ( StatusEffect_Get( guy, eStatusEffect.sonar_detected ) )
- continue
-
- NPCs.append( guy )
- }
-
- if ( NPCs.len() == 0 )
- {
- previousNPC = null
-
- if ( Time() - targetLostTime > 10 )
- {
- // couldn't find anything to cloak for 10 seconds so we'll warp out until we find something
- if ( cloakedDrone.s.isHidden == false )
- CloakedDroneWarpOut( cloakedDrone, spawnOrigin )
- }
- continue
- }
-
- goalNPC = FindBestCloakTarget( NPCs, cloakedDrone.GetOrigin(), cloakedDrone )
- Assert( goalNPC )
- }
-
- CloakedDroneClaimSquad( cloakedDrone, expect string( goalNPC.kv.squadname ) )
-
- waitthread CloakedDronePathFollowNPC( cloakedDrone, goalNPC )
-
- CloakedDroneReleaseSquad( cloakedDrone )
-
- previousNPC = goalNPC
- goalNPC = null
- targetLostTime = Time()
-
- float distSqr = DistanceSqr( lastOrigin, cloakedDrone.GetOrigin() )
- if ( distSqr < stuckDistSqr )
- CloakedDroneWarpOut( cloakedDrone, spawnOrigin )
-
- lastOrigin = cloakedDrone.GetOrigin()
- }
-}
-
-void function CloakedDroneClaimSquad( entity cloakedDrone, string squadname )
-{
- if ( GetNPCSquadSize( squadname ) )
- file.cloakedDroneClaimedSquadList[ cloakedDrone ] <- squadname
-}
-
-void function CloakedDroneReleaseSquad( entity cloakedDrone )
-{
- if ( cloakedDrone in file.cloakedDroneClaimedSquadList )
- delete file.cloakedDroneClaimedSquadList[ cloakedDrone ]
-}
-
-bool function CloakedDroneIsSquadClaimed( string squadname )
-{
- table<entity,string> cloneTable = clone file.cloakedDroneClaimedSquadList
- foreach ( entity cloakedDrone, squad in cloneTable )
- {
- if ( !IsAlive( cloakedDrone ) )
- delete file.cloakedDroneClaimedSquadList[ cloakedDrone ]
- else if ( squad == squadname )
- return true
- }
- return false
-}
-
-void function CloakedDronePathFollowNPC( entity cloakedDrone, entity goalNPC )
-{
- cloakedDrone.EndSignal( "OnDestroy" )
- cloakedDrone.EndSignal( "OnDeath" )
- cloakedDrone.EndSignal( "DroneCrashing" )
- goalNPC.EndSignal( "OnDeath" )
- goalNPC.EndSignal( "OnDestroy" )
-
- if ( !( "cloakedDrone" in goalNPC.s ) )
- goalNPC.s.cloakedDrone <- null
- goalNPC.s.cloakedDrone = cloakedDrone
-
- OnThreadEnd(
- function() : ( goalNPC )
- {
- if ( IsAlive( goalNPC ) )
- goalNPC.s.cloakedDrone = null
- }
- )
-
- int droneTeam = cloakedDrone.GetTeam()
-
- //vector maxs = < 64, 64, 53.5 >//bigger than model to compensate for large effect
- //vector mins = < -64, -64, -64 >
-
- vector maxs = < 32, 32, 32 >//bigger than model to compensate for large effect
- vector mins = < -32, -32, -32 >
-
- int mask = cloakedDrone.GetPhysicsSolidMask()
-
- float defaultHeight = 300
- array<float> traceHeightsLow = [ -75.0, -150.0, -250.0 ]
- array<float> traceHeightsHigh = [ 150.0, 300.0, 800.0, 1500.0 ]
-
- float waitTime = 0.25
-
- CloakDronePath path
- path.goalValid = false
- path.lastHeight = defaultHeight
-
- //If drone is following titan wait for titan to leave bubble shield.
- if ( goalNPC.IsTitan() )
- WaitTillHotDropComplete( goalNPC )
-
- while( goalNPC.GetTeam() == droneTeam )
- {
- if ( IsValid( GetRodeoPilot( goalNPC ) ) )
- return
-
- //If our target npc gets revealed by a sonar pulse, ditch that chump.
- if ( StatusEffect_Get( goalNPC, eStatusEffect.sonar_detected ) )
- return
-
- float minDist = CLOAK_DRONE_REACHED_HARVESTER_DIST * CLOAK_DRONE_REACHED_HARVESTER_DIST
- float distToGenerator = DistanceSqr( goalNPC.GetOrigin(), cloakedDrone.s.towerOrigin )
- //if we've gotten our npc to the generator, go find someone farther out to escort.
- if ( distToGenerator <= minDist )
- return
-
- //DebugDrawCircleOnEnt( goalNPC, 20, 255, 0, 0, 0.1 )
-
- float startTime = Time()
- path.goalValid = false
-
- CloakedDroneFindPathDefault( path, defaultHeight, mins, maxs, cloakedDrone, goalNPC, mask )
-
- //find a new path if necessary
- if ( !path.goalValid )
- {
- //lets check some heights and see if any are valid
- CloakedDroneFindPathHorizontal( path, traceHeightsLow, defaultHeight, mins, maxs, cloakedDrone, goalNPC, mask )
-
- if ( !path.goalValid )
- {
- //OK so no way to directly go to those heights - lets see if we can move vertically down,
- CloakedDroneFindPathVertical( path, traceHeightsLow, defaultHeight, mins, maxs, cloakedDrone, goalNPC, mask )
-
- if ( !path.goalValid )
- {
- //still no good...lets check up
- CloakedDroneFindPathHorizontal( path, traceHeightsHigh, defaultHeight, mins, maxs, cloakedDrone, goalNPC, mask )
-
- if ( !path.goalValid )
- {
- //no direct shots up - lets try moving vertically up first
- CloakedDroneFindPathVertical( path, traceHeightsHigh, defaultHeight, mins, maxs, cloakedDrone, goalNPC, mask )
- }
- }
- }
- }
-
- // if we can't find a valid path find a new goal
- if ( !path.goalValid )
- {
- waitthread CloakedDroneWarpOut( cloakedDrone, GetCloakTargetOrigin( goalNPC ) + < 0, 0, defaultHeight > )
- CloakedDroneWarpIn( cloakedDrone, GetCloakTargetOrigin( goalNPC ) + < 0, 0, defaultHeight > )
- continue
- }
-
- if ( cloakedDrone.s.isHidden == true )
- CloakedDroneWarpIn( cloakedDrone, cloakedDrone.GetOrigin() )
-
- thread AssaultOrigin( cloakedDrone, path.goal )
-
- float endTime = Time()
- float elapsedTime = endTime - startTime
- if ( elapsedTime < waitTime )
- wait waitTime - elapsedTime
- }
-}
-
-bool function CloakedDroneFindPathDefault( CloakDronePath path, float defaultHeight, vector mins, vector maxs, entity cloakedDrone, entity goalNPC, int mask )
-{
- vector offset = < 0, 0, defaultHeight >
- path.start = ( cloakedDrone.GetOrigin() ) + < 0, 0, 32 > //Offset so path start is just above drone instead at bottom of drone.
- path.goal = GetCloakTargetOrigin( goalNPC ) + offset
-
- //find out if we can get there using the default height
- TraceResults result = TraceHull( path.start, path.goal, mins, maxs, [ cloakedDrone, goalNPC ] , mask, TRACE_COLLISION_GROUP_NONE )
- //DebugDrawLine( path.start, path.goal, 50, 0, 0, true, 1.0 )
- if ( result.fraction >= VALIDPATHFRAC )
- {
- path.lastHeight = defaultHeight
- path.goalValid = true
- }
-
- return path.goalValid
-}
-
-bool function CloakedDroneFindPathHorizontal( CloakDronePath path, array<float> traceHeights, float defaultHeight, vector mins, vector maxs, entity cloakedDrone, entity goalNPC, int mask )
-{
- wait 0.1
-
- vector offset
- float testHeight
-
- //slight optimization... recheck if the last time was also not the default height
- if ( path.lastHeight != defaultHeight )
- {
- offset = < 0, 0, defaultHeight + path.lastHeight >
- path.start = ( cloakedDrone.GetOrigin() )
- path.goal = GetCloakTargetOrigin( goalNPC ) + offset
-
- TraceResults result = TraceHull( path.start, path.goal, mins, maxs, [ cloakedDrone, goalNPC ], mask, TRACE_COLLISION_GROUP_NONE )
- //DebugDrawLine( path.start, path.goal, 0, 255, 0, true, 1.0 )
- if ( result.fraction >= VALIDPATHFRAC )
- {
- path.goalValid = true
- return path.goalValid
- }
- }
-
- for ( int i = 0; i < traceHeights.len(); i++ )
- {
- testHeight = traceHeights[ i ]
- if ( path.lastHeight == testHeight )
- continue
-
-// wait 0.1
-
- offset = < 0, 0, defaultHeight + testHeight >
- path.start = ( cloakedDrone.GetOrigin() ) + ( testHeight > 0 ? < 0, 0, 0 > : < 0, 0, 32 > ) //Check from the top or bottom of the drone depending on if the drone is going up or down
- path.goal = GetCloakTargetOrigin( goalNPC ) + offset
-
- TraceResults result = TraceHull( path.start, path.goal, mins, maxs, [ cloakedDrone, goalNPC ], mask, TRACE_COLLISION_GROUP_NONE )
- if ( result.fraction < VALIDPATHFRAC )
- {
- //DebugDrawLine( path.start, path.goal, 200, 0, 0, true, 3.0 )
- continue
- }
-
- //DebugDrawLine( path.start, path.goal, 0, 255, 0, true, 3.0 )
-
- path.lastHeight = testHeight
- path.goalValid = true
- break
- }
-
- return path.goalValid
-}
-
-bool function CloakedDroneFindPathVertical( CloakDronePath path, array<float> traceHeights, float defaultHeight, vector mins, vector maxs, entity cloakedDrone, entity goalNPC, int mask )
-{
- vector offset
- vector origin
- float testHeight
-
- for ( int i = 0; i < traceHeights.len(); i++ )
- {
- wait 0.1
-
- testHeight = traceHeights[ i ]
- origin = cloakedDrone.GetOrigin()
- offset = < 0, 0, defaultHeight + testHeight >
- path.start = < origin.x, origin.y, defaultHeight + testHeight >
- path.goal = GetCloakTargetOrigin( goalNPC ) + offset
-
- TraceResults result = TraceHull( path.start, path.goal, mins, maxs, [ cloakedDrone, goalNPC ], mask, TRACE_COLLISION_GROUP_NONE )
- //DebugDrawLine( path.start, path.goal, 50, 50, 100, true, 1.0 )
- if ( result.fraction < VALIDPATHFRAC )
- continue
-
- //ok so it's valid - lets see if we can move to it from where we are
-// wait 0.1
-
- path.goal = < path.start.x, path.start.y, path.start.z >
- path.start = cloakedDrone.GetOrigin()
-
- result = TraceHull( path.start, path.goal, mins, maxs, [ cloakedDrone, goalNPC ], mask, TRACE_COLLISION_GROUP_NONE )
- //DebugDrawLine( path.start, path.goal, 255, 255, 0, true, 1.0 )
- if ( result.fraction < VALIDPATHFRAC )
- continue
-
- path.lastHeight = testHeight
- path.goalValid = true
- break
- }
-
- return path.goalValid
-}
-
-void function CloakedDroneWarpOut( entity cloakedDrone, vector origin )
-{
- if ( cloakedDrone.s.isHidden == false )
- {
- // only do this if we are not already hidden
- FadeOutSoundOnEntity( cloakedDrone, CLOAKED_DRONE_LOOPING_SFX, 0.5 )
- FadeOutSoundOnEntity( cloakedDrone, CLOAKED_DRONE_HOVER_LOOP_SFX, 0.5 )
- EmitSoundOnEntity( cloakedDrone, CLOAKED_DRONE_WARP_OUT_SFX )
-
- cloakedDrone.s.fx.Fire( "StopPlayEndCap" )
- cloakedDrone.SetTitle( "" )
- cloakedDrone.s.isHidden = true
- cloakedDrone.NotSolid()
- cloakedDrone.Minimap_Hide( TEAM_IMC, null )
- cloakedDrone.Minimap_Hide( TEAM_MILITIA, null )
- cloakedDrone.SetNoTarget( true )
- // let the beam fx end
-
- if ( "smokeEffect" in cloakedDrone.s )
- {
- cloakedDrone.s.smokeEffect.Kill_Deprecated_UseDestroyInstead()
- delete cloakedDrone.s.smokeEffect
- }
- UntrackAllToneMarks( cloakedDrone )
-
- wait 0.3 // wait a bit before hidding the done so that the fx looks better
- cloakedDrone.Hide()
- }
-
- wait 2.0
-
- cloakedDrone.DisableBehavior( "Follow" )
- thread AssaultOrigin( cloakedDrone, origin )
- cloakedDrone.SetOrigin( origin )
-}
-
-void function CloakedDroneWarpIn( entity cloakedDrone, vector origin )
-{
- cloakedDrone.DisableBehavior( "Follow" )
- cloakedDrone.SetOrigin( origin )
- PutEntityInSafeSpot( cloakedDrone, cloakedDrone, null, cloakedDrone.GetOrigin() + <0, 0, 32>, cloakedDrone.GetOrigin() )
- thread AssaultOrigin( cloakedDrone, origin )
-
- EmitSoundOnEntity( cloakedDrone, CLOAKED_DRONE_HOVER_LOOP_SFX )
- EmitSoundOnEntity( cloakedDrone, CLOAKED_DRONE_LOOPING_SFX )
- EmitSoundOnEntity( cloakedDrone, CLOAKED_DRONE_WARP_IN_SFX )
-
- cloakedDrone.Show()
- cloakedDrone.s.fx.Fire( "start" )
- cloakedDrone.SetTitle( "#NPC_CLOAK_DRONE" )
- cloakedDrone.s.isHidden = false
- cloakedDrone.Solid()
- cloakedDrone.Minimap_AlwaysShow( TEAM_IMC, null )
- cloakedDrone.Minimap_AlwaysShow( TEAM_MILITIA, null )
- cloakedDrone.SetNoTarget( false )
-}
-
-
-entity function CreateDroneCloakBeam( entity cloakedDrone )
-{
- entity fx = PlayLoopFXOnEntity( FX_DRONE_CLOAK_BEAM, cloakedDrone, "", null, < 90, 0, 0 > )//, visibilityFlagOverride = null, visibilityFlagEntOverride = null )
- return fx
-}
-
-entity function FindBestCloakTarget( array<entity> npcArray, vector origin, entity drone )
-{
- entity selectedNPC = null
- float maxDist = 10000 * 10000
- float minDist = 1300 * 1300
- float highestScore = -1
-
- foreach ( npc in npcArray )
- {
- float score = 0
- float distToGenerator = DistanceSqr( npc.GetOrigin(), drone.s.towerOrigin )
- if ( distToGenerator > minDist )
- {
- // only give dist bonus if we aren't to close to the generator.
- local dist = DistanceSqr( npc.GetOrigin(), origin )
- score = GraphCapped( dist, maxDist, minDist, 0, 1 )
- }
-
- if ( npc.IsTitan() )
- {
- score += 0.75
- if ( IsArcTitan( npc ) )
- score -= 0.1
- if ( IsMortarTitan( npc ) )
- score -= 0.2
-// if ( IsNukeTitan( npc ) )
-// score += 0.1
- }
- if ( score > highestScore )
- {
- highestScore = score
- selectedNPC = npc
- }
- }
-
- return selectedNPC
-}
-
-vector function GetCloakTargetOrigin( entity npc )
-{
- // returns the center of squad if the npc is in one
- // else returns a good spot to cloak a titan
-
- vector origin
-
- if ( GetNPCSquadSize( npc.kv.squadname ) == 0 )
- {
- origin = npc.GetOrigin() + npc.GetNPCVelocity()
- }
- else
- origin = npc.GetSquadCentroid()
-
- Assert( origin.x < ( 16384 * 100 ) );
-
- // defensive hack
- if ( origin.x > ( 16384 * 100 ) )
- origin = npc.GetOrigin()
-
- return origin
-}
-
-function IsSquadCenterClose( npc, dist = 256 )
-{
- // return true if there is no squad
- if ( GetNPCSquadSize( npc.kv.squadname ) == 0 )
- return true
-
- // return true if the squad isn't too spread out.
- if ( DistanceSqr( npc.GetSquadCentroid(), npc.GetOrigin() ) <= ( dist * dist ) )
- return true
-
- return false
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_drone.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_drone.gnut
deleted file mode 100644
index c0d56de73..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_drone.gnut
+++ /dev/null
@@ -1,1388 +0,0 @@
-untyped
-
-global function AiDrone_Init
-
-global function CreateDroneSquadString
-global function SetDroneSquadStringForOwner
-global function GetDroneSquadStringFromOwner
-global function DroneGruntThink
-global function RunDroneTypeThink
-global function DroneHasNoOwner
-global function CreateSingleDroneRope
-global function DroneDialogue
-global function IsDroneRebooting
-global function DroneOnLeeched
-global function SetRepairDroneTarget
-
-global const DRONE_SHIELD_COOLDOWN = 8
-global const DRONE_SHIELD_WALL_HEALTH = 200
-global const DRONE_SHIELD_WALL_RADIUS_TITAN = 200
-global const DRONE_SHIELD_WALL_RADIUS_HUMAN = 90
-global const DRONE_SHIELD_WALL_HEIGHT_TITAN = 450
-global const DRONE_SHIELD_WALL_HEIGHT_HUMAN = 190
-global const DRONE_SHIELD_WALL_FOV_TITAN = 115
-global const DRONE_SHIELD_WALL_FOV_HUMAN = 105
-global const DRONE_MINIMUM_DEPLOY_CLEARANCE_FROM_GROUND = 120
-global const MIN_DRONE_SHIELD_FROM_OWNER_DIST = 256 //if shield drone gets more than this distance away from host, will drop shield
-global const MIN_DRONE_SHIELD_FROM_OWNER_DIST_TITAN = 400 //if shield drone gets more than this distance away from host, will drop shield
-global const DRONE_LEASH_DISTANCE_SQR = 589824 // Further than this distance, drones will disengage from combat and go back to their owner.
-
-global const SOUND_DRONE_EXPLODE_DEFAULT = "Drone_DeathExplo"
-global const SOUND_DRONE_EXPLODE_CLOAK = "Drone_DeathExplo"
-
-global const FX_DRONE_SHIELD_WALL_TITAN = $"P_drone_shield_wall_XO"
-const FX_DRONE_SHIELD_WALL_HUMAN = $"P_drone_shield_wall"
-global const FX_DRONE_EXPLOSION = $"P_drone_exp_md"
-global const FX_DRONE_R_EXPLOSION = $"P_drone_exp_rocket"
-global const FX_DRONE_P_EXPLOSION = $"P_drone_exp_plasma"
-global const FX_DRONE_W_EXPLOSION = $"P_drone_exp_worker"
-global const FX_DRONE_SHIELD_ROPE_GLOW = $"acl_light_white"
-
-function AiDrone_Init()
-{
- PrecacheParticleSystem( FX_DRONE_EXPLOSION )
- PrecacheParticleSystem( FX_DRONE_R_EXPLOSION )
- PrecacheParticleSystem( FX_DRONE_P_EXPLOSION )
- PrecacheParticleSystem( FX_DRONE_W_EXPLOSION )
- PrecacheParticleSystem( FX_DRONE_SHIELD_WALL_TITAN )
- PrecacheParticleSystem( FX_DRONE_SHIELD_WALL_HUMAN )
- PrecacheParticleSystem( FX_DRONE_SHIELD_ROPE_GLOW )
-
- PrecacheModel( $"models/robots/drone_air_attack/drone_air_attack_rockets.mdl" )
- PrecacheModel( $"models/robots/drone_air_attack/drone_air_attack_plasma.mdl" )
-
- PrecacheMaterial( $"cable/cable_selfillum.vmt" )
- PrecacheModel( $"cable/cable_selfillum.vmt" )
- AddDeathCallback( "npc_drone", DroneDeath )
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Fallback behavior if we can't find a valid owner for an orphan Drone
-function DroneHasNoOwner( entity drone )
-{
- switch ( GetDroneType( drone ) )
- {
- case "drone_type_shield":
- //Transform into a Rocket drone and find some buddies
- thread DroneTransformsToRocketClass( drone )
- break
-
- case "drone_type_engineer_combat":
- case "drone_type_engineer_shield":
- EngineerDroneHasNoOwner( drone )
- break
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-void function DroneTransformsToRocketClass( entity drone )
-{
- if ( !IsAlive( drone ) )
- return
-
- drone.EndSignal( "OnDeath" )
- drone.EndSignal( "OnDestroy" )
-
- wait 1.5
-
- // dont do it if we're parented for some reason
- if ( IsValid( drone.GetParent() ) )
- return
-
- DroneDialogue( drone, "transform_shield_to_assault" )
- wait 3
-
- // dont do it if we're parented for some reason
- if ( IsValid( drone.GetParent() ) )
- return
-
- int team = drone.GetTeam()
- int health = drone.GetHealth()
- vector origin = drone.GetOrigin()
- vector angles = drone.GetAngles()
- angles.x = 0
- angles.z = 0
-
- entity newDrone = CreateRocketDrone( team, origin, angles )
- DispatchSpawn( newDrone )
- newDrone.SetHealth( health )
-
- entity enemy = drone.GetEnemy()
- if ( IsAlive( enemy ) )
- newDrone.SetEnemyLKP( enemy, enemy.GetOrigin() )
-
- drone.TransferChildrenTo( newDrone )
-
- drone.Destroy()
-
-}
-
-function EngineerDroneHasNoOwner( drone )
-{
- //TODO: Should probably protect nearest ally, and return to Engineer when he gets close.
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Change drone type on spawn or during gameplay (may transform from one to the other eventually)
-function RunDroneTypeThink( drone )
-{
- expect entity( drone )
- #if DEV
- Assert( !( "RunDroneTypeThink" in drone.s ), "Already ran drone think!" )
- drone.s.RunDroneTypeThink <- true
- #endif
- ////initialize it's type only after the anim is complete
- //local delay = drone.GetSequenceDuration( spawnAnimDrone )
- drone.EndSignal( "OnDeath" )
-
- switch ( GetDroneType( drone ) )
- {
- case "drone_type_beam":
- case "drone_type_rocket":
- case "drone_type_plasma":
- local owner = drone.GetFollowTarget()
- if ( IsValid( owner ) )
- owner.Signal( "OnEndFollow" )
- DroneRocketThink( drone ) //may delay if it's waiting for a spawn anim to finish
- break
-
- case "drone_type_shield":
- DroneShieldThink( drone ) //may delay if it's waiting for a spawn anim to finish
- break
-
- case "drone_type_engineer_combat":
- EngineerCombatDroneThink( drone ) //may delay if it's waiting for a spawn anim to finish
- break
-
- case "drone_type_engineer_shield":
- EngineerShieldDroneThink( drone ) //may delay if it's waiting for a spawn anim to finish
- break
-
- case "drone_type_repair":
- RepairDroneThink( drone )
- break
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function DroneRocketThink( entity drone )
-{
- drone.EndSignal( "OnDeath" )
-
- entity owner
- entity currentTarget
- local accuracyMultiplierBase = drone.kv.AccuracyMultiplier
- local accuracyMultiplierAgainstDrones = 100
-
- //--------------------------------------------
- // transform if this used to be a shield drone
- //--------------------------------------------
- RemoveDroneRopes( drone )
- drone.SetAttackMode( true )
-
- while ( true )
- {
- wait 0.25
-
- //----------------------------------
- // Get owner and current enemy
- //----------------------------------
- currentTarget = drone.GetEnemy()
- owner = drone.GetFollowTarget()
-
- //----------------------------------
- // Free roam if owner is dead or HasEnemy
- //----------------------------------
- if ( ( !IsAlive( owner ) ) || ( currentTarget != null ) )
- {
- drone.DisableBehavior( "Follow" )
- }
-
- //---------------------------------------------------------------------
- // If owner is alive and no enemies in sight, go back and follow owner
- //----------------------------------------------------------------------
- if ( IsAlive( owner ) )
- {
- local distSqr = DistanceSqr( owner.GetOrigin(), drone.GetOrigin() )
-
- if ( currentTarget == null || distSqr > DRONE_LEASH_DISTANCE_SQR )
- {
- drone.ClearEnemy()
- drone.EnableBehavior( "Follow" )
- }
- }
-
- //----------------------------------------------
- // Jack up accuracy if targeting another drone
- //----------------------------------------------
- if ( ( currentTarget != null ) && ( IsAirDrone( currentTarget ) ) )
- {
- drone.kv.AccuracyMultiplier = accuracyMultiplierAgainstDrones
- }
- else
- {
- drone.kv.AccuracyMultiplier = accuracyMultiplierBase
- }
- }
-
-}
-
-function ShieldDroneShieldsUser( entity drone )
-{
- for ( ;; )
- {
- var player = drone.WaitSignal( "OnPlayerUse" ).player
-
- Assert( false, "REMOVED; see mp_pilot_ability_shield to ressurect" )
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function DroneShieldThink( drone )
-{
- expect entity( drone )
- if ( !IsValid( drone ) )
- return
- drone.EndSignal( "OnDestroy" )
- drone.EndSignal( "OnDeath" )
- //drone.EndSignal( "OnNewOwner" )
-
- entity owner
- local newOwner
- string ownerSquadName = ""
- local distSq
- local distSqHuman = MIN_DRONE_SHIELD_FROM_OWNER_DIST * MIN_DRONE_SHIELD_FROM_OWNER_DIST
- local distSqTitan = MIN_DRONE_SHIELD_FROM_OWNER_DIST_TITAN * MIN_DRONE_SHIELD_FROM_OWNER_DIST_TITAN
- bool titanStateCurrent = false
- bool titanStatePrevious = false
- bool titanStateChanged = false
- local e = {}
- e.droneShieldTable <- null
-
- drone.SetUsePrompts( "#SHIELD_DRONE_HOLD_USE", "#SHIELD_DRONE_PRESS_USE" )
-
- //------------------------------------------
- // Cleanup shield if Drone dies
- //------------------------------------------
- OnThreadEnd(
- function() : ( e, drone )
- {
- DroneShieldDestroy( e.droneShieldTable )
- if ( IsAlive( drone ) )
- thread ShieldDroneLandsAfterLeaderDeath( drone )
- }
- )
-
- thread ShieldDroneShieldsUser( drone )
-
- //------------------------------------------
- // Drone tentacles/ropes
- //------------------------------------------
- local droneRopeTable = CreateDroneRopes( drone )
- if ( !( "droneRopeTable" in drone.s ) )
- drone.s.droneRopeTable <- null
- drone.s.droneRopeTable = droneRopeTable
-
- //------------------------------------------
- // Drone shield think loop
- //------------------------------------------
-
- while ( true )
- {
- wait 0.25
-
- if ( GetDroneType( drone ) != "drone_type_shield" )
- {
- DroneShieldDestroy( e.droneShieldTable )
- break
- }
-
- //------------------------------------------
- // If rebooting from EMP blast, get rid of shield
- //------------------------------------------
- if ( IsDroneRebooting( drone ) )
- {
- DroneShieldDestroy( e.droneShieldTable )
- continue
- }
- //------------------------------------------
- // If owner dead, kill shield until new owner found
- //------------------------------------------
- owner = drone.GetFollowTarget()
- if ( !IsAlive( owner ) )
- {
- DroneShieldDestroy( e.droneShieldTable )
- break
- }
-
- //------------------------------------------
- // Still no valid owner? End this thread
- //------------------------------------------
- if ( !IsValid( owner ) )
- break
-
- //ownerSquadName = owner.Get( "squadname" )
-
- //------------------------------------------
- // Owner is valid. Is it differnt owner?
- //------------------------------------------
- if ( newOwner != owner )
- {
- //Kill current shield since it will get redeployed on new owner
- DroneShieldDestroy( e.droneShieldTable )
- }
-
- //------------------------------------------
- // Owner is valid. Has owner changed Titan state?
- //------------------------------------------
- newOwner = owner
- titanStatePrevious = titanStateCurrent //previous state is whatever current was set to last loop around
-
- if ( owner.IsTitan() )
- {
- distSq = distSqTitan //adjust min dist for shield based on titan state
- titanStateCurrent = true //toggle so we can see if owner just changed state
- }
- else
- {
- distSq = distSqHuman
- titanStateCurrent = false
- }
-
- if ( titanStateCurrent != titanStatePrevious )
- titanStateChanged = true
- else
- titanStateChanged = false
-
- //--------------------------------------------------------------------------------------
- // We have a valid owner and a valid shield, continue unless we have changed Titan state
- //--------------------------------------------------------------------------------------
- if ( ( DroneShieldExists( e.droneShieldTable ) ) && ( !titanStateChanged ) )
- continue
-
- //------------------------------------------
- // Too far away from owner, destoy shield
- //------------------------------------------
- if ( DistanceSqr( drone.GetOrigin(), owner.GetOrigin() ) > distSq )
- {
- //printl( "Drone is too far away from host to create a shield")
- DroneShieldDestroy( e.droneShieldTable )
- continue
- }
-
- //------------------------------------------
- // Owner embarked/disembarked in a Titan, destroy shield
- //------------------------------------------
- if ( titanStateChanged )
- {
- //printl( "Drone host embarked/disembarked a Titan, destroying shield")
- DroneShieldDestroy( e.droneShieldTable )
- continue
- }
- //----------------------------------------------------------
- // Valid owner, valid dist, etc...make a shield for the current owner
- //-----------------------------------------------------------
- e.droneShieldTable = MakeDroneShield( drone, owner )
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function EngineerCombatDroneThink( entity drone )
-{
- if ( !IsValid( drone ) )
- return
-
- drone.EndSignal( "OnDeath" )
-
- entity owner
- local currentTarget
- local accuracyMultiplierPlayers = 50
- local accuracyMultiplierAgainstNPC = 90
-
- //--------------------------------------------
- // transform if this used to be a shield drone
- //--------------------------------------------
- RemoveDroneRopes( drone )
- drone.SetAttackMode( true )
-
- while ( true )
- {
- wait 0.25
-
- //----------------------------------
- // Get owner and current enemy
- //----------------------------------
- currentTarget = drone.GetEnemy()
- owner = drone.GetFollowTarget()
-
- //----------------------------------
- // Free roam if owner is dead or HasEnemy
- //----------------------------------
- if ( ( !IsAlive( owner ) ) || ( currentTarget != null ) )
- {
- drone.DisableBehavior( "Follow" )
- }
-
- //---------------------------------------------------------------------
- // If owner is alive and no enemies in sight, go back and follow owner
- //----------------------------------------------------------------------
- if ( IsAlive( owner ) )
- {
- float distSqr = DistanceSqr( owner.GetOrigin(), drone.GetOrigin() )
-
- if ( currentTarget == null || distSqr > DRONE_LEASH_DISTANCE_SQR )
- {
- drone.ClearEnemy()
- drone.EnableBehavior( "Follow" )
- }
- }
-
- //----------------------------------------------
- // Jack up accuracy if targeting another drone
- //----------------------------------------------
- if ( ( currentTarget != null ) && ( currentTarget.IsNPC() ) )
- {
- drone.kv.AccuracyMultiplier = accuracyMultiplierAgainstNPC
- }
- else
- {
- drone.kv.AccuracyMultiplier = accuracyMultiplierPlayers
- }
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function EngineerShieldDroneThink( drone )
-{
- if ( !IsValid( drone ) )
- return
- drone.EndSignal( "OnDestroy" )
- drone.EndSignal( "OnDeath" )
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function IsDroneRebooting( drone )
-{
- if ( !( "rebooting" in drone.s ) )
- return false
-
- return drone.s.rebooting
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// HACK: may just use generic function "CreateShield()" from particle_wall.nut, but just in prototype mode now
-function MakeDroneShield( drone, owner )
-{
- expect entity( owner )
-
- if ( !( "shieldTable" in drone.s ) )
- drone.s.shieldTable <- null
- else
- DroneShieldDestroy( drone.s.shieldTable )
-
- //------------------------------
- // Shield vars
- //------------------------------
- vector origin = owner.GetOrigin()
- vector angles = owner.GetAngles() + Vector( 0, 0, 180 )
- local attachmentTag
- local DroneShieldTable = {}
- DroneShieldTable.vortexSphere <- null
- DroneShieldTable.shieldWallFX = null
- DroneShieldTable.shieldRopes <- null
-
- asset shieldFx
- float wallFOV
- float shieldWallRadius
- float shieldWallHeight
- if ( owner.IsTitan() )
- {
- shieldWallRadius = DRONE_SHIELD_WALL_RADIUS_TITAN
- shieldFx = FX_DRONE_SHIELD_WALL_TITAN
- wallFOV = DRONE_SHIELD_WALL_FOV_TITAN
- shieldWallHeight = DRONE_SHIELD_WALL_HEIGHT_TITAN
- }
- else
- {
- shieldWallRadius = DRONE_SHIELD_WALL_RADIUS_HUMAN
- shieldFx = FX_DRONE_SHIELD_WALL_HUMAN
- wallFOV = DRONE_SHIELD_WALL_FOV_HUMAN
- shieldWallHeight = DRONE_SHIELD_WALL_HEIGHT_HUMAN
- }
-
- local Spawn
- //------------------------------
- // Vortex to block the actual bullets
- //------------------------------
- entity vortexSphere = CreateEntity( "vortex_sphere" )
-
- vortexSphere.kv.spawnflags = SF_ABSORB_BULLETS | SF_BLOCK_OWNER_WEAPON | SF_BLOCK_NPC_WEAPON_LOF | SF_ABSORB_CYLINDER
- vortexSphere.kv.enabled = 0
- vortexSphere.kv.radius = shieldWallRadius
- vortexSphere.kv.height = shieldWallHeight
- vortexSphere.kv.bullet_fov = wallFOV
- vortexSphere.kv.physics_pull_strength = 25
- vortexSphere.kv.physics_side_dampening = 6
- vortexSphere.kv.physics_fov = 360
- vortexSphere.kv.physics_max_mass = 2
- vortexSphere.kv.physics_max_size = 6
-
- vortexSphere.SetAngles( angles ) // viewvec?
- vortexSphere.SetOrigin( origin + Vector( 0, 0, shieldWallRadius - 64 ) )
- vortexSphere.SetMaxHealth( DRONE_SHIELD_WALL_HEALTH )
- vortexSphere.SetHealth( DRONE_SHIELD_WALL_HEALTH )
-
- if ( IsSingleplayer() )
- {
- thread PROTO_VortexSlowsPlayers( vortexSphere, owner )
- }
-
- DispatchSpawn( vortexSphere )
-
- vortexSphere.Fire( "Enable" )
-
- vortexSphere.SetInvulnerable() // make particle wall invulnerable to weapon damage. It will still drain over time
-
- // Shield wall fx control point
- entity cpoint = CreateEntity( "info_placement_helper" )
- SetTargetName( cpoint, UniqueString( "shield_wall_controlpoint" ) )
- DispatchSpawn( cpoint )
-
- //------------------------------------------
- // Shield wall fx for visuals/health drain
- //------------------------------------------
-
- entity shieldWallFX = PlayFXWithControlPoint( shieldFx, origin + Vector( 0, 0, -64 ), cpoint, -1, null, angles )
- vortexSphere.e.shieldWallFX = shieldWallFX
- SetVortexSphereShieldWallCPoint( vortexSphere, cpoint )
-
- entity mover = CreateScriptMover()
- mover.SetOrigin( owner.GetOrigin() )
- mover.SetAngles( owner.GetAngles() )
-
- //-----------------------
- // Attach shield to owner
- //------------------------
- vortexSphere.SetParent( mover )
- shieldWallFX.SetParent( mover )
-
- thread ShieldMoverFollowsOwner( owner, mover, vortexSphere, shieldWallFX )
-
- //-----------------------
- // Rope attach to shield
- //------------------------
- local ropeAttachOrigin1 = PositionOffsetFromEnt( owner, shieldWallRadius -16, wallFOV -16, 128 )
- local ropeAttachOrigin2 = PositionOffsetFromEnt( owner, shieldWallRadius -16, ( ( wallFOV - 16) * -1 ), 128 )
- if ( owner.IsTitan() )
- {
- ropeAttachOrigin1 = PositionOffsetFromEnt( owner, shieldWallRadius - 78, wallFOV + 22, 256 )
- ropeAttachOrigin2 = PositionOffsetFromEnt( owner, shieldWallRadius - 78, -( wallFOV + 22), 256 )
- }
-
- local shieldRopes = []
- local shieldRope1 = CreateSingleDroneRope( drone, "ROPE_0", false )
- local shieldRope2 = CreateSingleDroneRope( drone, "ROPE_0", false )
- shieldRopes.append( shieldRope1 )
- shieldRopes.append( shieldRope2 )
- entity ropeEnt1 = CreateEntity( "info_target" )
- entity ropeEnt2 = CreateEntity( "info_target" )
- ropeEnt1.SetOrigin( ropeAttachOrigin1 )
- ropeEnt2.SetOrigin( ropeAttachOrigin2 )
- ropeEnt1.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT
- ropeEnt2.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT
- DispatchSpawn( ropeEnt1 )
- DispatchSpawn( ropeEnt2 )
-
- ropeEnt1.SetParent( vortexSphere )
- ropeEnt2.SetParent( vortexSphere )
- shieldRope1.s.ropeEnd.SetOrigin( ropeEnt1.GetOrigin() )
- shieldRope2.s.ropeEnd.SetOrigin( ropeEnt2.GetOrigin() )
- shieldRope1.s.ropeEnd.SetParent( ropeEnt1 )
- shieldRope2.s.ropeEnd.SetParent( ropeEnt2 )
-
- PlayFXOnEntity( FX_DRONE_SHIELD_ROPE_GLOW, ropeEnt1 )
- PlayFXOnEntity( FX_DRONE_SHIELD_ROPE_GLOW, ropeEnt2 )
-
- //-----------------------
- // DroneShieldTable
- //------------------------
- DroneShieldTable.vortexSphere = vortexSphere
- DroneShieldTable.shieldWallFX = shieldWallFX
- DroneShieldTable.shieldRopes = shieldRopes
-
- //-----------------------
- // Health and cleanup
- //------------------------
- drone.s.shieldTable = DroneShieldTable
- UpdateShieldWallColorForFrac( shieldWallFX, 1.0 )
-
- return DroneShieldTable
-}
-
-void function ShieldMoverFollowsOwner( entity owner, entity mover, entity vortexSphere, entity shieldWallFX )
-{
- vortexSphere.EndSignal( "OnDestroy" )
- shieldWallFX.EndSignal( "OnDestroy" )
- owner.EndSignal( "OnDeath" )
- mover.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( mover )
- {
- if ( IsValid( mover ) )
- mover.Destroy()
- }
- )
-
- for ( ;; )
- {
- UpdateMoverPosition( mover, owner )
- }
-}
-
-void function UpdateMoverPosition( entity mover, entity owner )
-{
- vector origin = owner.GetOrigin()
- mover.NonPhysicsMoveTo( origin, 0.1, 0.0, 0.0 )
- mover.NonPhysicsRotateTo( owner.GetAngles(), 0.75, 0.0, 0.0 )
- WaitFrame()
-}
-
-void function PROTO_VortexSlowsPlayers( entity vortexSphere, entity owner )
-{
- vortexSphere.EndSignal( "OnDestroy" )
- owner.EndSignal( "OnDeath" )
-
- float radius = float(vortexSphere.kv.radius )
- float height = float(vortexSphere.kv.height )
- float bullet_fov = float( vortexSphere.kv.bullet_fov )
- float dot = cos( bullet_fov * 0.5 )
-
- for ( ;; )
- {
- vector origin = vortexSphere.GetOrigin()
- vector angles = vortexSphere.GetAngles()
- vector forward = AnglesToForward( angles )
- int team = owner.GetTeam()
-
- foreach ( player in GetPlayerArray() )
- {
- if ( player.GetTeam() == team )
- continue
- VortexStunCheck( player, origin, height, radius, bullet_fov, dot, forward )
- }
- WaitFrame()
- }
-}
-
-void function VortexStunCheck( entity player, vector origin, float height, float radius, float bullet_fov, float dot, vector forward )
-{
- if ( Time() - player.p.lastDroneShieldStunPushTime < 1.75 )
- return
-
- vector playerOrg = player.GetOrigin()
- float dist2d = Distance2D( playerOrg, origin )
-
- if ( dist2d > radius + 5 )
- return
- if ( dist2d < radius - 15 )
- return
-
- float heightOffset = fabs( playerOrg.z - origin.z )
-
- if ( heightOffset < 0 || heightOffset > height )
- return
-
- vector dif = Normalize( playerOrg - origin )
-
- if ( DotProduct2D( dif, forward ) < dot )
- return
-
- const float VORTEX_STUN_DURATION = 1.0
- GiveEMPStunStatusEffects( player, VORTEX_STUN_DURATION + 0.5 )
- float strength = 0.4
- StatusEffect_AddTimed( player, eStatusEffect.emp, strength, VORTEX_STUN_DURATION, 0.5 )
- thread TempLossOfAirControl( player, VORTEX_STUN_DURATION )
- vector velocity = forward * 300
- velocity.z = 400
- player.p.lastDroneShieldStunPushTime = Time()
-
- EmitSoundOnEntityOnlyToPlayer( player, player, "explo_proximityemp_impact_3p" )
- player.SetVelocity( velocity )
-}
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function CreateDroneRopes( drone )
-{
- local droneRopeTable = {}
- droneRopeTable.rope01 <- CreateSingleDroneRope( drone, "ROPE_0" )
- droneRopeTable.rope02 <- CreateSingleDroneRope( drone, "ROPE_0" )
- droneRopeTable.rope03 <- CreateSingleDroneRope( drone, "ROPE_1" )
- droneRopeTable.rope04 <- CreateSingleDroneRope( drone, "ROPE_2" )
- droneRopeTable.rope05 <- CreateSingleDroneRope( drone, "ROPE_3" )
- droneRopeTable.rope06 <- CreateSingleDroneRope( drone, "ROPE_4" )
-
- return droneRopeTable
-}
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function RemoveDroneRopes( entity drone )
-{
- if ( !( "droneRopeTable" in drone.s ) )
- return
-
- local droneRopeTable = drone.s.droneRopeTable
- if ( IsValid( droneRopeTable.rope01.s.ropeEnd ) )
- droneRopeTable.rope01.s.ropeEnd.Destroy()
- if ( IsValid( droneRopeTable.rope02.s.ropeEnd ) )
- droneRopeTable.rope02.s.ropeEnd.Destroy()
- if ( IsValid( droneRopeTable.rope03.s.ropeEnd ) )
- droneRopeTable.rope03.s.ropeEnd.Destroy()
- if ( IsValid( droneRopeTable.rope04.s.ropeEnd ) )
- droneRopeTable.rope04.s.ropeEnd.Destroy()
- if ( IsValid( droneRopeTable.rope05.s.ropeEnd ) )
- droneRopeTable.rope05.s.ropeEnd.Destroy()
- if ( IsValid( droneRopeTable.rope06.s.ropeEnd ) )
- droneRopeTable.rope06.s.ropeEnd.Destroy()
- if ( IsValid( droneRopeTable.rope01 ) )
- droneRopeTable.rope01.Destroy()
- if ( IsValid( droneRopeTable.rope02 ) )
- droneRopeTable.rope02.Destroy()
- if ( IsValid( droneRopeTable.rope03 ) )
- droneRopeTable.rope03.Destroy()
- if ( IsValid( droneRopeTable.rope04 ) )
- droneRopeTable.rope04.Destroy()
- if ( IsValid( droneRopeTable.rope05 ) )
- droneRopeTable.rope05.Destroy()
- if ( IsValid( droneRopeTable.rope06 ) )
- droneRopeTable.rope06.Destroy()
-
-}
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function CreateSingleDroneRope( drone, attachTag, dangling = true )
-{
- local subdivisions = 15 // 25
- local slack = 200 // 25
- string startpointName = UniqueString( "rope_startpoint" )
- string endpointName = UniqueString( "rope_endpoint" )
-
- local attach_id = drone.LookupAttachment( attachTag )
- Assert( attach_id > 0, "Invalid attachment: " + attachTag )
- local attachPos = drone.GetAttachmentOrigin( attach_id )
-
- entity rope_start = CreateEntity( "move_rope" )
- SetTargetName( rope_start, startpointName )
- rope_start.kv.NextKey = endpointName
- rope_start.kv.MoveSpeed = 32
- rope_start.kv.Slack = slack
- rope_start.kv.Subdiv = subdivisions
- rope_start.kv.Width = "1"
- rope_start.kv.TextureScale = "1"
- rope_start.kv.RopeMaterial = "cable/cable_selfillum.vmt"
- rope_start.kv.PositionInterpolator = 2
- rope_start.kv.dangling = dangling
- rope_start.SetOrigin( attachPos )
- rope_start.SetParent( drone, attachTag )
-
- entity rope_end = CreateEntity( "keyframe_rope" )
- SetTargetName( rope_end, endpointName )
- rope_end.kv.MoveSpeed = 32
- rope_end.kv.Slack = slack
- rope_end.kv.Subdiv = subdivisions
- rope_end.kv.Width = "1"
- rope_end.kv.TextureScale = "1"
- rope_end.kv.RopeMaterial = "cable/cable_selfillum.vmt"
- rope_end.SetOrigin( attachPos )
-
- DispatchSpawn( rope_start )
- DispatchSpawn( rope_end )
-
- rope_start.s.ropeEnd <- rope_end
-
- return rope_start
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function DroneShieldDestroy( DroneShieldTable )
-{
- if ( !IsValid( DroneShieldTable ) )
- return
-
- local vortexSphere = DroneShieldTable.vortexSphere
- local shieldWallFX = DroneShieldTable.shieldWallFX
- local ropes = DroneShieldTable.shieldRopes
-
- StopShieldWallFX( expect entity( vortexSphere ) )
- if ( IsValid( vortexSphere ) )
- vortexSphere.Destroy()
-
- if ( !IsValid( ropes ) )
- return
-
- foreach ( rope in ropes )
- {
- if ( IsValid( rope.s.ropeEnd ) )
- rope.s.ropeEnd.Destroy()
- if ( IsValid( rope ) )
- rope.Destroy()
- }
-
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function DroneShieldExists( DroneShieldTable )
-{
- if ( !IsValid( DroneShieldTable) )
- return false
-
- Assert( "vortexSphere" in DroneShieldTable, "DroneShieldTable doesn't contain any valid entries for vortexSphere." )
- Assert( "shieldWallFX" in DroneShieldTable, "DroneShieldTable doesn't contain any valid entries for shieldWallFX." )
-
- if ( ( IsValid( DroneShieldTable.vortexSphere ) ) && ( IsValid( DroneShieldTable.shieldWallFX ) ) )
- return true
-
- return false
-}
-
-void function DroneThrow( entity npc, entity drone, string spawnAnimDrone )
-{
- drone.EndSignal( "OnDeath" )
-
- drone.EnableNPCFlag( NPC_DISABLE_SENSING )
-
-// EmitSoundOnEntity( drone, "Drone_Power_On" )
-
- #if GRUNTCHATTER_ENABLED
- if ( NPC_GruntChatterSPEnabled( npc ) )
- GruntChatter_TryFriendlyEquipmentDeployed( npc, "npc_drone" )
- #endif
-
- vector origin = npc.GetOrigin()
- vector angles = npc.GetAngles()
-
- //animate the drone properly from the npc's hand
- PlayAnimTeleport( drone, spawnAnimDrone, origin, angles )
-
- if ( IsAlive( npc ) )
- {
- entity enemy = npc.GetEnemy()
- if ( IsAlive( enemy ) )
- drone.SetEnemyLKP( enemy, npc.GetEnemyLKP() )
- }
-
- drone.DisableNPCFlag( NPC_DISABLE_SENSING )
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#if !SP
-void function DroneCleanupOnOwnerDeath_Thread( entity owner, entity drone )
-{
- drone.EndSignal( "OnDestroy" )
- drone.EndSignal( "OnDeath" )
-
- for ( ; ; )
- {
- if ( !IsAlive( owner ) )
- break
-
- WaitFrame()
- }
-
- wait RandomFloatRange( 2.0, 10.0 )
- drone.Die()
-}
-#endif // #if !SP
-
-entity function SpawnDroneFromNPC( entity npc, string aiSettings )
-{
- //he's busy right now
- if ( !IsAlive( npc ) || !npc.IsInterruptable() )
- return null
-
- vector origin = npc.GetOrigin()
- vector angles = npc.GetAngles()
- int team = npc.GetTeam()
- entity owner = npc
- vector deployOrigin = PositionOffsetFromEnt( npc, 64, 0, 0 )
- float verticalClearance = GetVerticalClearance( deployOrigin )
- string spawnAnimDrone
- string spawnAnimSoldier
-
- //-------------------------------------------------------------------
- // Make sure enough clearance to spawn drone, and get correct anim
- //-------------------------------------------------------------------
- if ( verticalClearance >= 256 )
- {
- spawnAnimDrone = "dr_activate_drone_spin"
- spawnAnimSoldier = "pt_activate_drone_spin"
- }
- else if ( ( verticalClearance < 256 ) && ( verticalClearance > DRONE_MINIMUM_DEPLOY_CLEARANCE_FROM_GROUND ) )
- {
- spawnAnimDrone = "dr_activate_drone_indoor"
- spawnAnimSoldier = "pt_activate_drone_indoor"
- }
- else
- {
- printt( "NPC at ", npc.GetOrigin(), " couldn't spawn drone because there is less than ", DRONE_MINIMUM_DEPLOY_CLEARANCE_FROM_GROUND, " units of clearance from his origin." )
- return null
- }
-
- //------------------------------------------
- // NPC throws drone into air
- //------------------------------------------
- entity drone = CreateNPC( "npc_drone", team, origin, angles )
- SetSpawnOption_AISettings( drone, aiSettings )
- DispatchSpawn( drone )
-
- if ( !IsAlive( drone ) )
- return null
-
- drone.NotSolid()
- thread PlayAnim( npc, spawnAnimSoldier, origin, angles )
- thread DroneSolidDelayed( drone )
- thread DroneThrow( npc, drone, spawnAnimDrone )
-
-#if !SP
- thread DroneCleanupOnOwnerDeath_Thread( npc, drone )
-#endif // #if !SP
-
- npc.EnableNPCFlag( NPC_PAIN_IN_SCRIPTED_ANIM )
-
- return drone
-}
-
-void function DroneSolidDelayed( entity drone )
-{
- drone.EndSignal( "OnDestroy" )
- wait 3.0 // wait for custom scale to finish in the animation
- drone.Solid()
-}
-
-void function ShieldDroneLandsAfterLeaderDeath( entity drone )
-{
- Assert( IsNewThread(), "Must be threaded off" )
- drone.EndSignal( "OnDeath" )
-
- drone.DisableBehavior( "Follow" )
- //SetTeam( drone, TEAM_UNASSIGNED )
- vector start = drone.GetOrigin()
- vector end = start + Vector(0,0,-5000)
- vector mins = drone.GetBoundingMins()
- vector maxs = drone.GetBoundingMaxs()
-
- TraceResults traceResult = TraceHull( start, end, mins, maxs, null, TRACE_MASK_NPCWORLDSTATIC, TRACE_COLLISION_GROUP_NONE )
- if ( traceResult.fraction >= 1.0 )
- {
- // cant touch ground
- drone.Die()
- return
- }
-
- RemoveDroneRopes( drone )
-
- //drone.SetUsable()
- drone.AssaultPoint( traceResult.endPos )
- //drone.SetInvulnerable()
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function CreateDroneSquadString( owner )
-{
- Assert( IsValid( owner ), "Trying to MakeDroneSquad name for an invalid entity." )
-
- local squadName
-
- if ( owner.IsPlayer() )
- squadName = "player" + owner.entindex() + "droneSquad"
- else if ( owner.IsNPC() )
- squadName = "npc" + owner.entindex() + "droneSquad"
- else
- Assert( 0, "Trying to CreateDroneSquadString for a non-NPC non-player entity at " + owner.GetOrigin() )
-
- return squadName
-}
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function SetDroneSquadStringForOwner( owner, squadName )
-{
- Assert( IsValid( owner ), "Trying to SetDroneSquadStringForOwner name on an invalid entity." )
-
- if ( !( "squadNameDrones" in owner.s ) )
- owner.s.squadNameDrones <- null
-
- owner.s.squadNameDrones = squadName
-}
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function GetDroneSquadStringFromOwner( owner )
-{
- Assert( IsValid( owner ), "Trying to GetDroneSquadStringFromOwner name on an invalid entity." )
- if ( !( "squadNameDrones" in owner.s ) )
- return null
- else
- return owner.s.squadNameDrones
-}
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// DroneGrunt deploys drone after cooldown when drone is destroyed
-function DroneGruntThink( entity npc, string aiSettings )
-{
- if ( !IsValid( npc ) )
- return
-
- npc.EndSignal( "OnDestroy" )
- npc.EndSignal( "OnDeath" )
-
- entity drone
- float spawnCooldown
- entity closestEnemy
- npc.EnableNPCFlag( NPC_USE_SHOOTING_COVER | NPC_CROUCH_COMBAT )
-
- while ( true )
- {
- //if ( npc.GetNPCState() == "idle" )
- //{
- // npc.WaitSignal( "OnStateChange" )
- // continue
- //}
-
- wait ( RandomFloatRange( 0, 1.0 ) )
-
- //dont do stuff when animating on a parent
- if ( npc.GetParent() )
- continue
-
- // Don't deploy if would hit ceiling, droppod, etc
- if ( !DroneHasEnoughRoomToDeployFromNPC( npc ) )
- continue
-
- entity enemy = npc.GetEnemy()
- if ( !IsAlive( enemy ) )
- continue
-
- //vector pos = npc.LastKnownPosition( enemy )
- //if ( !WithinEngagementRange( npc, pos ) )
- // continue
-
- drone = SpawnDroneFromNPC( npc, aiSettings )
- if ( drone == null )
- continue
-
- waitthread DroneWaitTillDeadOrHacked( drone )
-
- wait 15
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function DroneHasEnoughRoomToDeployFromNPC( npc )
-{
- expect entity( npc )
-
- if ( !IsValid( npc ) )
- return false
- //-----------------------------------------------
- // Grunt throws drone a bit in front of him
- //-----------------------------------------------
- vector deployOrigin = PositionOffsetFromEnt( npc, 64, 0, 0 )
-
- if ( GetVerticalClearance( deployOrigin ) < DRONE_MINIMUM_DEPLOY_CLEARANCE_FROM_GROUND )
- return false
- else
- return true
-
-}
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function DroneWaitTillDeadOrHacked( drone )
-{
- drone.EndSignal( "OnDestroy" )
- drone.EndSignal( "OnDeath" )
- drone.EndSignal( "OnNewOwner" )
-
- WaitForever()
-}
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-void function DroneDeath( entity drone, var damageInfo )
-{
- local deathFX
-
- switch ( GetDroneType( drone ) )
- {
- case "drone_type_rocket":
- deathFX = FX_DRONE_R_EXPLOSION
- break
- case "drone_type_plasma":
- deathFX = FX_DRONE_P_EXPLOSION
- break
- case "drone_type_marvin":
- deathFX = FX_DRONE_W_EXPLOSION
- break
- case "drone_type_shield":
- case "drone_type_engineer_shield":
- case "drone_type_engineer_combat":
- default:
- deathFX = FX_DRONE_EXPLOSION
- break
- }
-
- // Explosion effect
- entity explosion = CreateEntity( "info_particle_system" )
- explosion.SetOrigin( drone.GetWorldSpaceCenter() )
- explosion.SetAngles( drone.GetAngles() )
- explosion.SetValueForEffectNameKey( deathFX )
- explosion.kv.start_active = 1
- DispatchSpawn( explosion )
-
- local deathSound
-
- // this sound get should be moved to ai settings file
- switch ( GetDroneType( drone ) )
- {
- case "drone_type_rocket":
- case "drone_type_plasma":
- case "drone_type_marvin":
- case "drone_type_shield":
- case "drone_type_engineer_shield":
- case "drone_type_engineer_combat":
- deathSound = SOUND_DRONE_EXPLODE_DEFAULT
- break
- default:
- deathSound = SOUND_DRONE_EXPLODE_DEFAULT
- break
- }
-
- EmitSoundAtPosition( TEAM_UNASSIGNED, drone.GetOrigin(), deathSound )
- explosion.Kill_Deprecated_UseDestroyInstead( 3 )
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-function DroneDialogue( drone, event, player = null )
-{
- expect entity( drone )
- expect entity( player )
-
- if ( !IsAlive( drone ) )
- return
-
- if ( player != null )
- {
- if ( !IsAlive( player ) )
- return
- }
-
- local alias
- bool playToPlayerOnly = true
-
- switch ( event )
- {
- case "smoke_deploy":
- //Foreign entity attached, deploying countermeasures.
- alias = "diag_gs_drone_detectEnemyRodeo"
- break
- case "hack_success":
- //New host accepted.
- alias = "diag_gs_drone_hostAcceptNew"
-
- //Foreign host accepted.
- if ( CoinFlip() )
- alias = "diag_gs_drone_hostAcceptForeign"
- break
- case "transform_shield_to_assault":
- //Drone host eliminated, engaging assault mode
- alias = "diag_gs_drone_elimHost"
- playToPlayerOnly = false
- break
- default:
- Assert( 0, "Invalid DroneDialogue event: " + event )
- }
-
- if ( playToPlayerOnly )
- EmitSoundOnEntityOnlyToPlayer( drone, player, alias )
- else
- EmitSoundOnEntity( drone, alias )
-
-
-/*
-Hostiles detected, marking targets
-diag_gs_drone_detectHostileTargets
-
-Drone targets marked
-diag_gs_drone_targetsMarked
-
-Escort drone destroyed
-diag_gs_drone_escortDestroyed
-
-Multiple escort drones combined. Shield radius increased
-diag_gs_drone_combinedShieldRadius
-
-Multiple escort drones combined. Projectile accuracy increased
-diag_gs_drone_combinedWpnAccuracy
-
-Recharging drone shield
-diag_gs_drone_rechargingShield
-
-Target lost
-diag_gs_drone_targetLost
-
-Target acquired
-diag_gs_drone_targetAcquired
-*/
-
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-function DroneOnLeeched( drone, player )
-{
- //global behavior when this npc gets leeched
- delaythread ( 1 ) DroneDialogue( drone, "hack_success", player )
-}
-
-function DroneSelfDestruct( drone, delay )
-{
- drone.EndSignal( "OnDeath" )
- wait delay
- drone.Die()
-}
-
-function RepairDroneThink( entity drone )
-{
- drone.EndSignal( "OnDeath" )
- local attachID
- EmitSoundOnEntity( drone, "colony_spectre_initialize_beep" )
- thread DroneSelfDestruct( drone, 60 )
-
- for ( ;; )
- {
- if ( drone.e.repairSoul == null )
- {
- wait 1
- continue
- }
-
- string attachName = "HIJACK"
- entity repairTitan = drone.e.repairSoul.GetTitan()
-
- /*
- if ( IsSoul( repairTarget ) )
- {
- repairTarget = repairTarget.GetTitan()
- attachName = "HIJACK"
- }
- else
- {
- Assert( !repairTarget.IsTitan() )
- attachName = "ORIGIN"
- }
-
- if ( !IsAlive( repairTitan ) )
- {
- wait 2
- continue
- }
- */
-
- drone.SetOwner( repairTitan )
-
- if ( DroneCanRepairTarget( drone, repairTitan, attachName ) )
- {
- // close enough to repair?
- //P_wpn_defender_beam
- waitthread DroneRepairsTarget( drone, repairTitan, attachName )
- }
- WaitFrame()
- }
-}
-
-bool function DroneCanRepairTarget( drone, ent, attachName )
-{
- expect entity( ent )
-
- if ( !IsAlive( ent ) )
- return false
-
- if ( ent.GetHealth() >= ent.GetMaxHealth() )
- return false
-
- local attachID = ent.LookupAttachment( attachName )
- local origin = ent.GetAttachmentOrigin( attachID )
- local droneOrigin = drone.GetOrigin()
- if ( Distance( droneOrigin, origin ) > 600 )
- return false
-
- float trace = TraceLineSimple( droneOrigin, origin, ent )
- return trace == 1.0
-}
-
-function DroneRepairsTarget( drone, ent, attachName )
-{
- expect entity( drone )
- expect entity( ent )
-
- drone.EndSignal( "OnDestroy" )
- EmitSoundOnEntity( drone, "EMP_Titan_Electrical_Field" )
-
- OnThreadEnd(
- function() : ( drone )
- {
- if ( IsValid( drone ) )
- StopSoundOnEntity( drone, "EMP_Titan_Electrical_Field" )
- }
- )
-
- int followBehavior = GetDefaultNPCFollowBehavior( drone )
- drone.SetOwner( ent )
- drone.InitFollowBehavior( ent, followBehavior )
- drone.EnableBehavior( "Follow" )
-
- for ( ;; )
- {
- if ( !DroneCanRepairTarget( drone, ent, attachName ) )
- return
-
- DroneRepairFX( drone, ent, attachName )
-
- local maxHealth = ent.GetMaxHealth()
- local healAmount = maxHealth * 0.015 // 0.005
- float healTime = RandomFloatRange( 0.8, 1.2 )
-
- for ( float i = 0.0; i < healTime; i++ )
- {
- if ( !IsAlive( ent ) )
- return
-
- local newHealth = ent.GetHealth() + healAmount
- newHealth = min( newHealth, maxHealth )
- ent.SetHealth( newHealth )
- WaitFrame()
- }
- }
-}
-
-function DroneRepairFX( drone, ent, attachName )
-{
- // Control point sets the end position of the effect
- entity cpEnd = CreateEntity( "info_placement_helper" )
- SetTargetName( cpEnd, UniqueString( "arc_cannon_beam_cpEnd" ) )
- cpEnd.SetParent( ent, attachName, false, 0.0 )
- DispatchSpawn( cpEnd )
-
- entity zapBeam = CreateEntity( "info_particle_system" )
- zapBeam.kv.cpoint1 = cpEnd.GetTargetName()
-
- zapBeam.SetValueForEffectNameKey( ARC_CANNON_BEAM_EFFECT )
- zapBeam.kv.start_active = 0
- zapBeam.SetOwner( drone )
- zapBeam.kv.VisibilityFlags = (ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_ENEMY)
- zapBeam.SetParent( drone, "ORIGIN", false, 0.0 )
- DispatchSpawn( zapBeam )
-
- zapBeam.Fire( "Start" )
- zapBeam.Fire( "StopPlayEndCap", "", 2.0 )
- zapBeam.Kill_Deprecated_UseDestroyInstead( 2.0 )
- cpEnd.Kill_Deprecated_UseDestroyInstead( 2.0 )
-}
-
-
-function SetRepairDroneTarget( entity drone, entity repairTitan )
-{
- Assert( IsAlive( repairTitan ), "Repair target " + repairTitan + " is dead" )
- Assert( repairTitan.IsTitan() )
- drone.e.repairSoul = repairTitan.GetTitanSoul()
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_emp_titans.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_emp_titans.gnut
deleted file mode 100644
index 638166c83..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_emp_titans.gnut
+++ /dev/null
@@ -1,181 +0,0 @@
-untyped
-
-global function EmpTitans_Init
-
-global function EMPTitanThinkConstant
-
-const DAMAGE_AGAINST_TITANS = 150
-const DAMAGE_AGAINST_PILOTS = 40
-
-const EMP_DAMAGE_TICK_RATE = 0.3
-const FX_EMP_FIELD = $"P_xo_emp_field"
-const FX_EMP_FIELD_1P = $"P_body_emp_1P"
-
-function EmpTitans_Init()
-{
- AddDamageCallbackSourceID( eDamageSourceId.titanEmpField, EmpField_DamagedEntity )
- PrecacheParticleSystem( FX_EMP_FIELD )
- PrecacheParticleSystem( FX_EMP_FIELD_1P )
-
- RegisterSignal( "StopEMPField" )
-}
-
-void function EMPTitanThinkConstant( entity titan )
-{
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
- titan.EndSignal( "Doomed" )
- titan.EndSignal( "StopEMPField" )
-
- //We don't want pilots accidently rodeoing an electrified titan.
- DisableTitanRodeo( titan )
-
- //Used to identify this titan as an arc titan
- SetTargetName( titan, "empTitan" )
-
- //Wait for titan to stand up and exit bubble shield before deploying arc ability.
- WaitTillHotDropComplete( titan )
-
- if ( HasSoul( titan ) )
- {
- entity soul = titan.GetTitanSoul()
- soul.EndSignal( "StopEMPField" )
- }
-
- local attachment = GetEMPAttachmentForTitan( titan )
-
- local attachID = titan.LookupAttachment( attachment )
-
- EmitSoundOnEntity( titan, "EMP_Titan_Electrical_Field" )
-
- array<entity> particles = []
-
- //emp field fx
- vector origin = titan.GetAttachmentOrigin( attachID )
- if ( titan.IsPlayer() )
- {
- entity particleSystem = CreateEntity( "info_particle_system" )
- particleSystem.kv.start_active = 1
- particleSystem.kv.VisibilityFlags = ENTITY_VISIBLE_TO_OWNER
- particleSystem.SetValueForEffectNameKey( FX_EMP_FIELD_1P )
-
- particleSystem.SetOrigin( origin )
- particleSystem.SetOwner( titan )
- DispatchSpawn( particleSystem )
- particleSystem.SetParent( titan, GetEMPAttachmentForTitan( titan ) )
- particles.append( particleSystem )
- }
-
- entity particleSystem = CreateEntity( "info_particle_system" )
- particleSystem.kv.start_active = 1
- if ( titan.IsPlayer() )
- particleSystem.kv.VisibilityFlags = (ENTITY_VISIBLE_TO_FRIENDLY | ENTITY_VISIBLE_TO_ENEMY) // everyone but owner
- else
- particleSystem.kv.VisibilityFlags = ENTITY_VISIBLE_TO_EVERYONE
- particleSystem.SetValueForEffectNameKey( FX_EMP_FIELD )
- particleSystem.SetOwner( titan )
- particleSystem.SetOrigin( origin )
- DispatchSpawn( particleSystem )
- particleSystem.SetParent( titan, GetEMPAttachmentForTitan( titan ) )
- particles.append( particleSystem )
-
- titan.SetDangerousAreaRadius( ARC_TITAN_EMP_FIELD_RADIUS )
-
- OnThreadEnd(
- function () : ( titan, particles )
- {
- if ( IsValid( titan ) )
- {
- StopSoundOnEntity( titan, "EMP_Titan_Electrical_Field" )
- EnableTitanRodeo( titan ) //Make the arc titan rodeoable now that it is no longer electrified.
- }
-
- foreach ( particleSystem in particles )
- {
- if ( IsValid_ThisFrame( particleSystem ) )
- {
- particleSystem.ClearParent()
- particleSystem.Fire( "StopPlayEndCap" )
- particleSystem.Kill_Deprecated_UseDestroyInstead( 1.0 )
- }
- }
- }
- )
-
- wait RandomFloat( EMP_DAMAGE_TICK_RATE )
-
- while ( true )
- {
- origin = titan.GetAttachmentOrigin( attachID )
-
- RadiusDamage(
- origin, // center
- titan, // attacker
- titan, // inflictor
- DAMAGE_AGAINST_PILOTS, // damage
- DAMAGE_AGAINST_TITANS, // damageHeavyArmor
- ARC_TITAN_EMP_FIELD_INNER_RADIUS, // innerRadius
- ARC_TITAN_EMP_FIELD_RADIUS, // outerRadius
- SF_ENVEXPLOSION_NO_DAMAGEOWNER, // flags
- 0, // distanceFromAttacker
- DAMAGE_AGAINST_PILOTS, // explosionForce
- DF_ELECTRICAL | DF_STOPS_TITAN_REGEN, // scriptDamageFlags
- eDamageSourceId.titanEmpField ) // scriptDamageSourceIdentifier
-
- wait EMP_DAMAGE_TICK_RATE
- }
-}
-
-void function EmpField_DamagedEntity( entity target, var damageInfo )
-{
- if ( !IsAlive( target ) )
- return
-
- entity titan = DamageInfo_GetAttacker( damageInfo )
-
- if ( !IsValid( titan ) )
- return
-
- local className = target.GetClassName()
- if ( className == "rpg_missile" || className == "npc_turret_sentry" || className == "grenade" )
- {
- DamageInfo_SetDamage( damageInfo, 0 )
- return
- }
-
- if ( DamageInfo_GetDamage( damageInfo ) <= 0 )
- return
-
- if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS )
- return
-
- if ( target.IsPlayer() )
- {
- if ( !titan.IsPlayer() && IsArcTitan( titan ) )
- {
- if ( !titan.s.electrocutedPlayers.contains( target ) )
- titan.s.electrocutedPlayers.append( target )
- }
-
- const ARC_TITAN_SCREEN_EFFECTS = 0.085
- const ARC_TITAN_EMP_DURATION = 0.35
- const ARC_TITAN_EMP_FADEOUT_DURATION = 0.35
-
- local attachID = titan.LookupAttachment( "hijack" )
- local origin = titan.GetAttachmentOrigin( attachID )
- local distSqr = DistanceSqr( origin, target.GetOrigin() )
-
- local minDist = ARC_TITAN_EMP_FIELD_INNER_RADIUS_SQR
- local maxDist = ARC_TITAN_EMP_FIELD_RADIUS_SQR
- local empFxHigh = ARC_TITAN_SCREEN_EFFECTS
- local empFxLow = ( ARC_TITAN_SCREEN_EFFECTS * 0.6 )
- float screenEffectAmplitude = GraphCapped( distSqr, minDist, maxDist, empFxHigh, empFxLow )
-
- StatusEffect_AddTimed( target, eStatusEffect.emp, screenEffectAmplitude, ARC_TITAN_EMP_DURATION, ARC_TITAN_EMP_FADEOUT_DURATION )
- }
-}
-
-string function GetEMPAttachmentForTitan( entity titan )
-{
- return "hijack"
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_gunship.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_gunship.gnut
deleted file mode 100644
index 2f1fdc96f..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_gunship.gnut
+++ /dev/null
@@ -1,97 +0,0 @@
-untyped
-
-global function AiGunship_Init
-
-global function GunshipThink
-
-global const SOUND_GUNSHIP_HOVER = "Gunship_Hover"
-global const SOUND_GUNSHIP_EXPLODE_DEFAULT = "Gunship_Explode"
-global const FX_GUNSHIP_EXPLOSION = $"P_veh_exp_crow"
-
-function AiGunship_Init()
-{
- PrecacheParticleSystem( FX_GUNSHIP_EXPLOSION )
- AddDeathCallback( "npc_gunship", GunshipDeath )
-}
-
-
-function GunshipThink( gunship )
-{
- gunship.EndSignal( "OnDeath" )
-
- entity owner
- entity currentTarget
- local accuracyMultiplierBase = gunship.kv.AccuracyMultiplier
- local accuracyMultiplierAgainstDrones = 100
-
- while( true )
- {
- wait 0.25
-
- //----------------------------------
- // Get owner and current enemy
- //----------------------------------
- currentTarget = expect entity( gunship.GetEnemy() )
- owner = expect entity( gunship.GetFollowTarget() )
-
- //----------------------------------
- // Free roam if owner is dead or HasEnemy
- //----------------------------------
- if ( ( !IsAlive( owner ) ) || ( currentTarget != null ) )
- {
- gunship.DisableBehavior( "Follow" )
- }
-
- //---------------------------------------------------------------------
- // If owner is alive and no enemies in sight, go back and follow owner
- //----------------------------------------------------------------------
- if ( ( IsAlive( owner ) ) && ( currentTarget == null ) )
- {
- gunship.EnableBehavior( "Follow" )
- }
-
-
- //----------------------------------------------
- // Jack up accuracy if targeting a small target (like a drone)
- //----------------------------------------------
- if ( ( currentTarget != null ) && ( IsAirDrone( currentTarget ) ) )
- {
- gunship.kv.AccuracyMultiplier = accuracyMultiplierAgainstDrones
- }
- else
- {
- gunship.kv.AccuracyMultiplier = accuracyMultiplierBase
- }
- }
-
-}
-
-
-void function GunshipDeath( entity gunship, var damageInfo )
-{
- /*
- Script errors
-
- // Explosion effect
- entity explosion = CreateEntity( "info_particle_system" )
- explosion.SetOrigin( gunship.GetWorldSpaceCenter() )
- explosion.SetAngles( gunship.GetAngles() )
- explosion.SetValueForEffectNameKey( FX_GUNSHIP_EXPLOSION )
- explosion.kv.start_active = 1
- DispatchSpawn( explosion )
- EmitSoundAtPosition( TEAM_UNASSIGNED, gunship.GetOrigin(), SOUND_GUNSHIP_EXPLODE_DEFAULT )
- explosion.destroy( 3 )
-
- gunship.Destroy()
-
- P_veh_exp_hornet, TAG_ORIGIN, attach
-
- */
-
- //TEMP
- PlayFX( FX_GUNSHIP_EXPLOSION, gunship.GetOrigin() )
- EmitSoundAtPosition( TEAM_UNASSIGNED, gunship.GetOrigin(), "Goblin_Dropship_Explode" )
- gunship.Destroy()
-}
-
-
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_lethality.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_lethality.gnut
deleted file mode 100644
index 771fe6d93..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_lethality.gnut
+++ /dev/null
@@ -1,97 +0,0 @@
-untyped
-
-global enum eAILethality
-{
- VeryLow,
- Low,
- Medium,
- High,
- VeryHigh
-}
-
-global function SetAILethality
-
-global function UpdateNPCForAILethality
-
-function SetAILethality( aiLethality )
-{
- Assert( IsMultiplayer() )
- level.nv.aiLethality = aiLethality
-
- switch ( aiLethality )
- {
- case eAILethality.Medium:
- break
-
- case eAILethality.High:
- NPCSetAimConeFocusParams( 6, 2.5 )
- NPCSetAimPatternFocusParams( 4, 0.3, 0.8 )
- break
- case eAILethality.VeryHigh:
- NPCSetAimConeFocusParams( 5, 2.0 )
- NPCSetAimPatternFocusParams( 4, 0.3, 0.8 )
- break
- }
-
- // reset ai lethality
-
- array<entity> npcs = GetNPCArray()
- foreach ( npc in npcs )
- {
- UpdateNPCForAILethality( npc )
- }
-}
-
-
-function SetTitanAccuracyAndProficiency( entity npcTitan )
-{
- Assert( IsMultiplayer() )
- int lethality = Riff_AILethality()
- float accuracyMultiplier = 1.0
- int weaponProficiency = eWeaponProficiency.GOOD
-
- entity player = GetPetTitanOwner( npcTitan )
- entity soul = npcTitan.GetTitanSoul()
-
- // auto titans have lower proficiency
- if ( player && soul == null)
- {
- soul = player.GetTitanSoul() // in mid transfer
- }
-
- if ( IsValid( soul ) )
- {
- if ( SoulHasPassive( soul, ePassives.PAS_ENHANCED_TITAN_AI ) )
- {
- weaponProficiency = eWeaponProficiency.GOOD
- }
- else if ( player )
- {
- weaponProficiency = eWeaponProficiency.AVERAGE
- entity ordnanceWeapon = npcTitan.GetOffhandWeapon( OFFHAND_ORDNANCE )
- if ( IsValid( ordnanceWeapon ) )
- ordnanceWeapon.AllowUse( false )
-
- entity centerWeapon = npcTitan.GetOffhandWeapon( OFFHAND_TITAN_CENTER )
- if ( IsValid( centerWeapon ) )
- centerWeapon.AllowUse( false )
- }
- }
-
- npcTitan.kv.AccuracyMultiplier = accuracyMultiplier
- npcTitan.kv.WeaponProficiency = weaponProficiency
-}
-
-function UpdateNPCForAILethality( entity npc )
-{
- Assert( IsMultiplayer() )
- if ( npc.IsTitan() )
- {
- SetTitanAccuracyAndProficiency( npc )
- return
- }
-
- if ( IsMinion( npc ) )
- SetProficiency( npc )
-}
-
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_faces.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_faces.gnut
deleted file mode 100644
index e6d3bcf0a..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_faces.gnut
+++ /dev/null
@@ -1,226 +0,0 @@
-untyped
-
-global function MarvinFaces_Init
-
-global function MarvinFace
-global function MarvinThinksAwhile
-global function MarvinFaceExists
-global function SetMarvinBodyType
-global function MarvinSetFace
-
-function MarvinFaces_Init()
-{
-
- RegisterSignal( "StopThinking" )
-
- AddSpawnCallback( "npc_marvin", MarvinSpawnCallback )
-
- SetupMarvinFaces()
-}
-
-function SetupMarvinFaces()
-{
- // setup marvin face mappings
- level.marvinFaces <- {}
- level.marvinFaces[ MARVIN_TYPE_WORKER ] <-
- {
- none = 0
- happy = 1
- sad = 2
- angry = 3
- think1 = 4
- think2 = 5
- question = 6
- }
-
- // Use the yellow worker marvin skins since shooters are from SP and shooter value = 0 which is LevelEd's default.
- // Real shooter skins are 7-13. If we want real skins, we can add them here and adjust the marvin spawn points per level.
- level.marvinFaces[ MARVIN_TYPE_SHOOTER ] <-
- {
- none = 0
- happy = 1
- sad = 2
- angry = 3
- think1 = 4
- think2 = 5
- question = 6
- }
-
- level.marvinFaces[ MARVIN_TYPE_FIREFIGHTER ] <-
- {
- none = 14
- happy = 15
- sad = 16
- angry = 17
- think1 = 18
- think2 = 19
- question = 20
- }
-
- // No idea what this type of marvin is...legacy stuff. Just make them yellow.
- level.marvinFaces[ MARVIN_TYPE_MARVINONE ] <-
- {
- none = 0
- happy = 1
- sad = 2
- angry = 3
- think1 = 4
- think2 = 5
- question = 6
- }
-
-// Nothing uses this except debug statements that are commented out
-/*
- level.marvinFaceNames <- {}
- // invert map for tests
- foreach ( key, val in level.marvinFace )
- {
- level.marvinFaceNames[ val ] <- key
- }
-*/
-}
-
-void function MarvinFace( entity marvin )
-{
- thread MarvinFaceThink( marvin )
-}
-
-void function MarvinFaceThink( entity marvin )
-{
- //printl( "Setting up marvin face for " + marvin )
- for ( ;; )
- {
- waitthread MarvinUndamagedFacePicker( marvin )
-
-// printl( "damaged " + marvin )
- if ( !IsAlive( marvin ) )
- break
-
- waitthread MarvinWounded( marvin )
-
- if ( !IsAlive( marvin ) )
- break
- }
-
- if ( IsValid_ThisFrame( marvin ) )
- MarvinSetFace( marvin, "none" )
-}
-
-function MarvinWounded( marvin )
-{
- marvin.EndSignal( "OnDeath" )
- MarvinSetFace( marvin, "sad" )
- wait 2.3
- waitthread MarvinThinksAwhile( marvin, RandomFloatRange( 2, 4 ) )
-}
-
-void function EntSignals( entity ent, string signal )
-{
- if ( IsValid_ThisFrame( ent ) )
- ent.Signal( signal )
-}
-
-function MarvinThinksAwhile( marvin, time )
-{
- expect entity( marvin )
-
- marvin.EndSignal( "StopThinking" )
- delaythread( time ) EntSignals( marvin, "StopThinking" )
-
- // think for a bit
- for ( ;; )
- {
- MarvinSetFace( marvin, "think1" )
- wait 0.4
- MarvinSetFace( marvin, "think2" )
- wait 0.4
- }
-}
-
-function MarvinUndamagedFacePicker( marvin )
-{
- marvin.EndSignal( "OnDeath" )
- marvin.EndSignal( "OnDamaged" )
- local i
-
- for ( ;; )
- {
- if ( !marvin.GetEnemy() )
- {
- MarvinSetFace( marvin, "happy" )
- marvin.WaitSignal( "OnFoundEnemy" )
- }
-
- waitthread MarvinThinksAwhile( marvin, RandomFloatRange( 2, 4 ) )
-
- if ( marvin.GetEnemy() )
- {
- MarvinSetFace( marvin, "angry" )
- marvin.WaitSignal( "OnLostEnemy" )
- }
- }
-}
-
-function MarvinSetFace( self, face )
-{
-// printl( self + " got face " + face )
- Assert( MarvinFaceExists( self, face ), "No face " + face + " in level.marvinFace" )
-
- //prin( "Changing " + self + " face from " + level.marvinFaceNames[ skin ] + " to " + face )
- self.SetSkin( GetMarvinFace( self, face ) )
- self.Signal( "StopThinking" )
-}
-
-function MarvinFaceExists( npc_marvin, face )
-{
- local marvinType = GetMarvinBodyType( npc_marvin )
-
- if ( marvinType in level.marvinFaces )
- return true
-
-// return ( face in level.marvinFaces[ marvinType ] )
-}
-
-function GetMarvinFace( npc_marvin, face )
-{
- local marvinType = GetMarvinBodyType( npc_marvin )
-
- Assert( MarvinFaceExists( npc_marvin, face ), "No face " + face + " in level.marvinFace" )
-
- local faceID = level.marvinFaces[ marvinType ][ face ]
-
- return faceID
-}
-
-function SetMarvinBodyType( npc_marvin )
-{
- if( "bodytype" in npc_marvin.s )
- {
- Assert( npc_marvin.s.bodytype >= MARVIN_TYPE_SHOOTER && npc_marvin.s.bodytype <= MARVIN_TYPE_FIREFIGHTER, "Specified invalid body type index " + npc_marvin.s.bodytype + ", Use values from 0-2 instead." )
-
- switch( npc_marvin.s.bodytype )
- {
- case MARVIN_TYPE_FIREFIGHTER:
- local index = npc_marvin.FindBodyGroup( "firefighter" )
- local state = 1
- npc_marvin.SetBodygroup( index, state )
- break
- }
- }
-}
-
-function GetMarvinBodyType( npc_marvin )
-{
- local bodyType = MARVIN_TYPE_WORKER
-
- if( "bodytype" in npc_marvin.s )
- bodyType = npc_marvin.s.bodytype
-
- return bodyType
-}
-
-void function MarvinSpawnCallback( entity npc_marvin )
-{
- SetMarvinBodyType( npc_marvin )
- npc_marvin.SetDeathNotifications( true ) //Primarily so we can do HandleDeathPackage for Marvins. Can just add a deathcallback if this is too expensive
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_jobs.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_jobs.gnut
deleted file mode 100644
index 588b4d75e..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvin_jobs.gnut
+++ /dev/null
@@ -1,600 +0,0 @@
-
-/*
- ToDo:
- -if marvin has no jobs to go to make him to back to spawn position instead of standing at last node
-*/
-
-global function MarvinJobs_Init
-global function MarvinJobThink
-global function GetMarvinType
-
-const DEBUG_MARVIN_JOBS = false
-const MAX_JOB_SEARCH_DIST_SQR = 1000 * 1000
-const JOB_NODE_COOLDOWN_TIME = 15.0
-
-struct MarvinJob
-{
- string validMarvinType
- entity node
- entity user
- string jobType
- bool tempJob
- float nextUsableTime = 0
- entity barrel
-}
-
-struct
-{
- array<MarvinJob> marvinJobs
- table<string,void functionref( entity,MarvinJob)> jobFunctions
-} file
-
-
-
-
-// ██╗███╗ ██╗██╗████████╗
-// ██║████╗ ██║██║╚══██╔══╝
-// ██║██╔██╗ ██║██║ ██║
-// ██║██║╚██╗██║██║ ██║
-// ██║██║ ╚████║██║ ██║
-// ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝
-
-void function MarvinJobs_Init()
-{
- file.jobFunctions[ "welding" ] <- SimpleJobAnims
- file.jobFunctions[ "welding_under" ] <- SimpleJobAnims
- file.jobFunctions[ "window" ] <- SimpleJobAnims
- file.jobFunctions[ "fightFire" ] <- SimpleJobAnims
- file.jobFunctions[ "barrel_pickup" ] <- MarvinPicksUpBarrel
- file.jobFunctions[ "barrel_putdown" ] <- MarvinPutsDownBarrel
- file.jobFunctions[ "repair_over_edge" ] <- SimpleJobAnims
- file.jobFunctions[ "repair_above" ] <- SimpleJobAnims
- file.jobFunctions[ "repair_under" ] <- SimpleJobAnims
- file.jobFunctions[ "datacards" ] <- SimpleJobAnims
-
- file.jobFunctions[ "drone_welding" ] <- SimpleJobAnims
- file.jobFunctions[ "drone_inspect" ] <- SimpleJobAnims
-
- RegisterSignal( "pickup_barrel" )
- RegisterSignal( "putdown_barrel" )
- RegisterSignal( "JobStarted" )
- RegisterSignal( "StopDoingJobs" )
-
- AddSpawnCallback( "script_marvin_job", InitMarvinJob )
-
- AddCallback_EntitiesDidLoad( MarvinJobsEntitiesDidLoad )
-}
-
-void function InitMarvinJob( entity node )
-{
- Assert( node.HasKey( "job" ) )
- Assert( node.kv.job != "" )
- Assert( string( node.kv.job ) in file.jobFunctions, "Marvin job node at " + node.GetOrigin() + " has unhandled job type " + string( node.kv.job ) )
- string editorClass = GetEditorClass( node )
-
- // Drop node to ground for certain types or if checked on the entity
- if ( editorClass == "" )
- {
- if ( !node.HasKey( "hover" ) || node.kv.hover != "1" )
- DropToGround( node )
- }
-
- if ( DEBUG_MARVIN_JOBS )
- DebugDrawAngles( node.GetOrigin(), node.GetAngles() )
-
- // Create marvin job struct
- MarvinJob marvinJob
- marvinJob.node = node
- marvinJob.jobType = string( node.kv.job )
- marvinJob.tempJob = node.HasKey( "tempJob" ) && node.kv.tempJob == "1"
-
- if ( marvinJob.jobType == "barrel_pickup" )
- marvinJob.barrel = CreateBarrel( node )
-
- // Set what marvin_type of NPC can use this job
- switch ( editorClass )
- {
- case "script_marvin_drone_job":
- marvinJob.validMarvinType = "marvin_type_drone"
- break
- default:
- marvinJob.validMarvinType = "marvin_type_walker"
- break
- }
-
- file.marvinJobs.append( marvinJob )
-}
-
-void function MarvinJobsEntitiesDidLoad()
-{
- if ( DEBUG_MARVIN_JOBS )
- DebugMarvinJobs()
-}
-
-
-
-
-
-// ████████╗██╗ ██╗██╗███╗ ██╗██╗ ██╗
-// ╚══██╔══╝██║ ██║██║████╗ ██║██║ ██╔╝
-// ██║ ███████║██║██╔██╗ ██║█████╔╝
-// ██║ ██╔══██║██║██║╚██╗██║██╔═██╗
-// ██║ ██║ ██║██║██║ ╚████║██║ ██╗
-// ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝
-
-void function MarvinJobThink( entity marvin )
-{
- EndSignal( marvin, "OnDeath" )
- EndSignal( marvin, "OnDestroy" )
- EndSignal( marvin, "StopDoingJobs" )
-
- // Wait a frame because npcs that are spawned at map load may run this function before job nodes are finished being initialized
- WaitFrame()
-
- // Get all jobs this marvin can do
- array<MarvinJob> jobs = GetJobsForMarvin( marvin )
- if ( jobs.len() == 0 )
- return
-
- OnThreadEnd(
- function() : ( marvin )
- {
- Assert( !IsAlive( marvin ), "MarvinJobThink ended but the marvin is still alive" )
- }
- )
-
- while ( true )
- {
- foreach ( MarvinJob job in jobs )
- {
- waitthread MarvinDoJob( marvin, job )
- WaitFrame()
- }
-
- jobs.randomize()
- WaitFrame()
- }
-}
-
-void function MarvinDoJob( entity marvin, MarvinJob job )
-{
- Assert( IsAlive( marvin ), "Marvin " + marvin + " is not alive" )
- EndSignal( marvin, "OnFailedToPath" )
- EndSignal( marvin, "OnDeath" )
-
- // Don't do a job that's already in use or not ready to be used again
- if ( IsValid( job.user ) || Time() < job.nextUsableTime )
- return
-
- // Don't use a barrel put down job if you can'r carrying a barrel
- if ( job.jobType == "barrel_putdown" && !IsValid( marvin.ai.carryBarrel ) )
- return
-
- // If you're carrying a barrel, only do a barrel put down job
- if ( IsValid( marvin.ai.carryBarrel ) && job.jobType != "barrel_putdown" )
- return
-
- OnThreadEnd(
- function() : ( job )
- {
- job.user = null
- job.nextUsableTime = Time() + JOB_NODE_COOLDOWN_TIME
- }
- )
-
- // Default walk anim
- MarvinDefaultMoveAnim( marvin )
-
- // Node gets occupied
- job.user = marvin
-
- if ( DEBUG_MARVIN_JOBS )
- DebugDrawLine( marvin.GetWorldSpaceCenter(), job.node.GetOrigin(), 255, 0, 0, true, 3.0 )
-
- // Run the job function
- thread DontDisableJobOnPathFailOrDeath( marvin, job )
- waitthread file.jobFunctions[ job.jobType ]( marvin, job )
- if ( IsValid( marvin ) )
- marvin.Anim_Stop()
-}
-
-void function DontDisableJobOnPathFailOrDeath( entity marvin, MarvinJob job )
-{
- EndSignal( marvin, "JobStarted" )
- WaitSignal( marvin, "OnFailedToPath", "OnDeath" )
- job.nextUsableTime = Time()
-}
-
-
-
-
-
-// ██╗ ██████╗ ██████╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗
-// ██║██╔═══██╗██╔══██╗ ██╔════╝██║ ██║████╗ ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝
-// ██║██║ ██║██████╔╝ █████╗ ██║ ██║██╔██╗ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗
-// ██ ██║██║ ██║██╔══██╗ ██╔══╝ ██║ ██║██║╚██╗██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║
-// ╚█████╔╝╚██████╔╝██████╔╝ ██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║
-// ╚════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝
-
-void function SimpleJobAnims( entity marvin, MarvinJob job )
-{
- // Get the anims to use for the job
- array<string> anims
- switch ( job.jobType )
- {
- // Marvin jobs
- case "welding":
- anims.append( "mv_idle_weld" )
- break
- case "welding_under":
- anims.append( "mv_weld_under" )
- anims.append( "mv_weld_under" )
- anims.append( "mv_weld_under_stumble" )
- break
- case "window":
- anims.append( "mv_idle_wash_window_noloop" )
- anims.append( "mv_idle_buff_window_noloop" )
- break
- case "fightFire":
- anims.append( "mv_fireman_idle" )
- anims.append( "mv_fireman_shift" )
- break
- case "repair_over_edge":
- anims.append( "mv_repair_overedge" )
- anims.append( "mv_repair_overedge" )
- anims.append( "mv_repair_overedge_stumble" )
- break
- case "repair_above":
- anims.append( "mv_repair_ship_above" )
- break
- case "repair_under":
- anims.append( "mv_repair_under" )
- anims.append( "mv_repair_under_stumble" )
- break
- case "datacards":
- anims.append( "mv_job_replace_datacards" )
- break
-
- // Marvin drone jobs
- case "drone_welding":
- anims.append( "dw_jobs_welding_wallpanel" )
- break
- case "drone_inspect":
- anims.append( "inspect1" )
- anims.append( "inspect2" )
- break
- }
- Assert( anims.len() > 0 )
-
- if ( IsMarvinWalker( marvin ) )
- waitthread MarvinRunToAnimStart( marvin, anims[0], job.node )
- else
- waitthread MarvinFlyToAnimStart( marvin, anims[0], job.node )
-
- Signal( marvin, "JobStarted" )
-
- while ( true )
- {
- anims.randomize()
- foreach ( string anim in anims )
- {
- float animLength = marvin.GetSequenceDuration( anim ) // wait anim length because some anims may be looping so we can't wait for them to end
-
- if ( IsMarvinDrone( marvin ) )
- thread PlayAnimTeleport( marvin, anim, job.node )
- else
- thread PlayAnim( marvin, anim, job.node, null, 0.6 )
-
- wait animLength
- }
- if ( job.tempJob )
- break
- }
-}
-
-void function MarvinPicksUpBarrel( entity marvin, MarvinJob job )
-{
- // Don't try to pick up a barrel if there isn't one nearby
- if ( !IsValid( job.barrel ) )
- return
- if ( Distance( job.node.GetOrigin(), job.barrel.GetOrigin() ) > 25 )
- return
-
- EndSignal( job.barrel, "OnDestroy" )
-
- entity info_target = CreateEntity( "info_target" )
- DispatchSpawn( info_target )
-
- OnThreadEnd(
- function () : ( info_target )
- {
- info_target.Destroy()
- }
- )
-
- vector barrelFlatAngles = job.barrel.GetAngles()
- barrelFlatAngles.x = 0
- barrelFlatAngles.z = 0
-
- info_target.SetOrigin( job.barrel.GetOrigin() )
- info_target.SetAngles( barrelFlatAngles )
-
- DropToGround( info_target )
-
- if ( info_target.GetOrigin().z < -MAX_WORLD_COORD )
- return // Fell through map
-
- if ( DEBUG_MARVIN_JOBS )
- thread DrawAnglesForMovingEnt( info_target, 30.0 )
-
-
- // Go to the barrel
- MarvinRunToAnimStart( marvin, "mv_carry_barrel_pickup", info_target )
-
- // Try to pick it up
- thread PlayAnim( marvin, "mv_carry_barrel_pickup", info_target, null, 0.6 )
-
- // Wait until animation should pick up the barrel
- marvin.WaitSignal( "pickup_barrel" )
-
- // Get attachment info
- string attachment = "PROPGUN"
- int attachIndex = marvin.LookupAttachment( attachment )
- vector attachOrigin = marvin.GetAttachmentOrigin( attachIndex )
-
- // Make sure the barrel is close when it's time to parent the barrel
- if ( Distance( attachOrigin, job.barrel.GetOrigin() ) > 25 )
- {
- marvin.Anim_Stop()
- return
- }
-
- // Marvin picks up the barrel and carries it
- thread MarvinCarryBarrel( marvin, job.barrel )
-
- marvin.WaitSignal( "OnAnimationDone" )
-}
-
-void function MarvinCarryBarrel( entity marvin, entity barrel )
-{
- marvin.EndSignal( "OnDeath" )
- marvin.EndSignal( "OnDamaged" )
- marvin.EndSignal( "putdown_barrel" )
-
- OnThreadEnd(
- function () : ( marvin, barrel )
- {
- if ( IsValid( barrel ) )
- {
- barrel.kv.solid = SOLID_VPHYSICS
- barrel.ClearParent()
- barrel.SetOwner( null )
- EntFireByHandle( barrel, "wake", "", 0, null, null )
- EntFireByHandle( barrel, "enablemotion", "", 0, null, null )
- }
-
- if ( IsAlive( marvin ) )
- {
- MarvinDefaultMoveAnim( marvin )
- marvin.ClearIdleAnim()
- marvin.ai.carryBarrel = null
- }
- }
- )
-
- string attachment = "PROPGUN"
- marvin.SetMoveAnim( "mv_carry_barrel_walk" )
- marvin.SetIdleAnim( "mv_carry_barrel_idle" )
- barrel.SetParent( marvin, attachment, false, 0.5 )
- barrel.SetOwner( marvin )
-
- barrel.kv.solid = 0 // not solid
-
- marvin.ai.carryBarrel = barrel
-
- WaitSignal( marvin, "OnDestroy" )
-}
-
-void function MarvinPutsDownBarrel( entity marvin, MarvinJob job )
-{
- Assert( IsValid( marvin.ai.carryBarrel ) )
-
- // Don't place a barrel here if there is already one
- if ( IsValid( job.barrel ) )
- {
- if ( Distance( job.node.GetOrigin(), job.barrel.GetOrigin() ) <= 25 )
- return
- }
-
- EndSignal( marvin.ai.carryBarrel, "OnDestroy" )
-
- marvin.SetMoveAnim( "mv_carry_barrel_walk" )
- marvin.SetIdleAnim( "mv_carry_barrel_idle" )
-
- // Walk to the put down spot
- MarvinRunToAnimStart( marvin, "mv_carry_barrel_putdown", job.node )
-
- // Put down the barrel
- thread PlayAnim( marvin, "mv_carry_barrel_putdown", job.node, null, 0.6 )
-
- // Wait for release
- marvin.WaitSignal( "putdown_barrel" )
-
- marvin.WaitSignal( "OnAnimationDone" )
-}
-
-
-
-
-// ██╗ ██╗████████╗██╗██╗ ██╗████████╗██╗ ██╗
-// ██║ ██║╚══██╔══╝██║██║ ██║╚══██╔══╝╚██╗ ██╔╝
-// ██║ ██║ ██║ ██║██║ ██║ ██║ ╚████╔╝
-// ██║ ██║ ██║ ██║██║ ██║ ██║ ╚██╔╝
-// ╚██████╔╝ ██║ ██║███████╗██║ ██║ ██║
-// ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝
-
-bool function IsMarvinWalker( entity marvin )
-{
- return GetMarvinType( marvin ) == "marvin_type_walker"
-}
-
-bool function IsMarvinDrone( entity marvin )
-{
- return GetMarvinType( marvin ) == "marvin_type_drone"
-}
-
-string function GetMarvinType( entity npc )
-{
- var marvinType = npc.Dev_GetAISettingByKeyField( "marvin_type" )
- if ( marvinType == null )
- return "not_marvin"
-
- return expect string( marvinType )
-}
-
-bool function IsJobNode( entity node )
-{
- if ( node.GetClassName() == "script_marvin_job" )
- return true
- if ( GetEditorClass( node ) == "script_marvin_drone_job" )
- return true
- return false
-}
-
-void function MarvinDefaultMoveAnim( entity marvin )
-{
- if ( IsMarvinWalker( marvin ) )
- {
- marvin.SetNPCMoveSpeedScale( 1.0 )
- marvin.SetMoveAnim( "walk_all" )
- }
-}
-
-array<MarvinJob> function GetJobsForMarvin( entity marvin )
-{
- string marvinType = GetMarvinType( marvin )
-
- // Get jobs this marvin links to, if any, and randomize
- array<MarvinJob> linkedJobs
- array<entity> linkedEnts = marvin.GetLinkEntArray()
- foreach ( entity ent in linkedEnts )
- {
- if ( IsJobNode( ent ) )
- {
- MarvinJob linkedJob = GetMarvinJobForNode( ent )
- Assert( IsValid( linkedJob.node ) )
-
- // Error if we are linking to the wrong type of job node
- Assert( marvinType == linkedJob.validMarvinType, "npc_marvin at " + marvin.GetOrigin() + " links to a marvin job of the wrong marvin_type" )
-
- linkedJobs.append( linkedJob )
- }
- }
- linkedJobs.randomize()
-
- // If marvin was linked to jobs we only consider those
- if ( marvin.HasKey( "LinkedJobsOnly" ) && marvin.kv.LinkedJobsOnly == "1" )
- {
- Assert( linkedJobs.len() > 0, "marvin at " + marvin.GetOrigin() + " has LinkedJobsOnly marked but does not link to any job nodes" )
- return linkedJobs
- }
-
- // Add all jobs within valid distance and randomize
- array<MarvinJob> jobs
- foreach ( MarvinJob marvinJob in file.marvinJobs )
- {
- if ( marvinType != marvinJob.validMarvinType )
- continue
-
- // Don't re-add a job that was linked to
- if ( linkedJobs.contains( marvinJob ) )
- continue
-
- // Teleport nodes are for special case jobs with no nav mesh do son't consider them automatically
- if ( marvinJob.node.HasKey( "teleport" ) && marvinJob.node.kv.teleport == "1" )
- continue
-
- // Only search for jobs within a max distance
- if ( DistanceSqr( marvinJob.node.GetOrigin(), marvin.GetOrigin() ) <= MAX_JOB_SEARCH_DIST_SQR )
- jobs.append( marvinJob )
- }
-
- // Randomize the order so the marvin does them out of order
- jobs.randomize()
-
- // Add the linked jobs to the list, and put them at the beginning of the priority
- foreach ( MarvinJob linkedJob in linkedJobs )
- jobs.insert( 0, linkedJob )
-
- // Debug draw jobs this marvin can take
- if ( DEBUG_MARVIN_JOBS )
- {
- foreach ( MarvinJob job in jobs )
- {
- if ( linkedJobs.contains( job ) )
- DebugDrawLine( marvin.GetOrigin(), job.node.GetOrigin(), 255, 255, 0, true, 10.0 )
- else
- DebugDrawLine( marvin.GetOrigin(), job.node.GetOrigin(), 200, 200, 200, true, 10.0 )
- }
- }
-
- return jobs
-}
-
-void function DebugMarvinJobs()
-{
- while ( true )
- {
- foreach ( MarvinJob marvinJob in file.marvinJobs )
- {
- string appendText = "AVAILABLE"
- float timeTillNextUse = marvinJob.nextUsableTime - Time()
- if ( IsValid( marvinJob.user ) )
- appendText = "RESERVED"
- else if ( timeTillNextUse > 0 )
- appendText = format( "%.1f", timeTillNextUse )
- DebugDrawText( marvinJob.node.GetOrigin(), marvinJob.jobType + " (" + appendText + ")", true, 0.1 )
- }
- wait 0.05
- }
-}
-
-MarvinJob function GetMarvinJobForNode( entity node )
-{
- MarvinJob marvinJob
- foreach ( MarvinJob marvinJob in file.marvinJobs )
- {
- if ( marvinJob.node == node )
- return marvinJob
- }
- return marvinJob
-}
-
-entity function CreateBarrel( entity node )
-{
- return CreatePropPhysics( node.GetModelName(), node.GetOrigin(), node.GetAngles() )
-}
-
-void function MarvinRunToAnimStart( entity marvin, string anim, entity jobNode )
-{
- if ( jobNode.HasKey( "teleport" ) && jobNode.kv.teleport == "1" )
- wait 0.1
- else
- RunToAnimStartPos( marvin, anim, jobNode )
-}
-
-void function MarvinFlyToAnimStart( entity marvin, string anim, entity jobNode )
-{
- if ( jobNode.HasKey( "teleport" ) && jobNode.kv.teleport == "1" )
- {
- wait 0.1
- return
- }
-
- AnimRefPoint animStartInfo = marvin.Anim_GetStartForRefPoint( anim, jobNode.GetOrigin(), jobNode.GetAngles() )
-
- marvin.AssaultPoint( animStartInfo.origin )
- marvin.AssaultSetAngles( animStartInfo.angles, true )
- marvin.AssaultSetArrivalTolerance( 16 )
- marvin.WaitSignal( "OnFinishedAssault" )
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvins.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvins.gnut
deleted file mode 100644
index fc8b7d1ee..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_marvins.gnut
+++ /dev/null
@@ -1,141 +0,0 @@
-untyped
-
-global function AiMarvins_Init
-
-
-function AiMarvins_Init()
-{
- FlagInit( "Disable_Marvins" )
- FlagSet( "Disable_Marvins" )
-
- level.livingMarvins <- {}
- AddSpawnCallback( "npc_marvin", LivingMarvinSpawned )
-
- AddCallback_EntitiesDidLoad( EntitiesDidLoad )
-}
-
-void function EntitiesDidLoad()
-{
- if ( IsAutoPopulateEnabled() == false )
- return
-
- FlagEnd( "disable_npcs" )
-
- array<entity> marvin_spawners = GetEntArrayByClass_Expensive( "info_spawnpoint_marvin" )
-
- if ( !marvin_spawners.len() )
- return
-
- for ( ;; )
- {
- wait 3
-
- if ( !Flag( "Disable_Marvins" ) )
- {
- if ( TotalLivingMarvins() < 5 )
- {
- SpawnRandomMarvin( marvin_spawners )
- }
- }
- }
-}
-
-void function LivingMarvinSpawned( entity self )
-{
- level.livingMarvins[ self ] <- self
-}
-
-function TotalLivingMarvins()
-{
- local count = 0
- foreach ( entity marvin in clone level.livingMarvins )
- {
- if ( IsAlive( marvin ) )
- {
- count++
- continue
- }
-
- // cleanup dead marvins
- delete level.livingMarvins[ marvin ]
- }
- return count
-}
-
-entity function SpawnRandomMarvin( array<entity> marvin_spawners )
-{
- marvin_spawners.randomize()
- entity spawnpoint = marvin_spawners[0] // if no valid spawn is found use this one
- for ( int i = 0; i < marvin_spawners.len(); i++ )
- {
- if ( IsMarvinSpawnpointValid( marvin_spawners[ i ] ) )
- {
- spawnpoint = marvin_spawners[ i ]
- break
- }
- }
-
- entity marvin = SpawnAmbientMarvin( spawnpoint )
- return marvin
-}
-
-bool function IsMarvinSpawnpointValid( entity spawnpoint )
-{
- // ensure spawnpoint is not occupied (i.e. would spawn inside another player or object )
- if ( spawnpoint.IsOccupied() )
- return false
-
- bool visible = spawnpoint.IsVisibleToEnemies( TEAM_IMC ) || spawnpoint.IsVisibleToEnemies( TEAM_MILITIA )
- if ( visible )
- return false
-
- return true
-}
-
-entity function SpawnAmbientMarvin( entity spawnpoint )
-{
- entity npc_marvin = CreateEntity( "npc_marvin" )
- SetTargetName( npc_marvin, UniqueString( "mp_random_marvin") )
- npc_marvin.SetOrigin( spawnpoint.GetOrigin() )
- npc_marvin.SetAngles( spawnpoint.GetAngles() )
- //npc_marvin.kv.rendercolor = "255 255 255"
- npc_marvin.kv.health = -1
- npc_marvin.kv.max_health = -1
- npc_marvin.kv.spawnflags = 516 // Fall to ground, Fade Corpse
- //npc_marvin.kv.FieldOfView = 0.5
- //npc_marvin.kv.FieldOfViewAlert = 0.2
- npc_marvin.kv.AccuracyMultiplier = 1.0
- npc_marvin.kv.physdamagescale = 1.0
- npc_marvin.kv.WeaponProficiency = eWeaponProficiency.GOOD
-
- Marvin_SetModels( npc_marvin, spawnpoint )
-
- DispatchSpawn( npc_marvin )
-
- SetTeam( npc_marvin, TEAM_UNASSIGNED )
-
- return npc_marvin
-}
-
-function Marvin_SetModels( entity npc_marvin, entity spawnpoint )
-{
- //default
- npc_marvin.s.bodytype <- MARVIN_TYPE_WORKER
-
- // set body and head based on KVP
- if ( spawnpoint.HasKey( "bodytype" ) )
- {
- local bodytype = spawnpoint.GetValueForKey( "bodytype" ).tointeger()
-
- Assert( bodytype >= MARVIN_TYPE_SHOOTER && bodytype <= MARVIN_TYPE_FIREFIGHTER, "Specified invalid body type index " + bodytype + " for info_spawnpoint_marvin " + spawnpoint + ", Use values from 0-2 instead." )
-
- npc_marvin.s.bodytype = bodytype
- }
-
-
- if ( spawnpoint.HasKey( "headtype" ) )
- {
- local headtype = spawnpoint.GetValueForKey( "headtype" )
- npc_marvin.kv.body = headtype
- }
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_spectres.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_spectres.gnut
deleted file mode 100644
index 4aa3ac302..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_spectres.gnut
+++ /dev/null
@@ -1,7 +0,0 @@
-global function MortarSpectreGetSquadFiringPositions
-
-array<vector> function MortarSpectreGetSquadFiringPositions(vector origin, vector testTarget)
-{
- array< vector > ret
- return ret
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_titans.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_titans.gnut
deleted file mode 100644
index 08598808a..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_mortar_titans.gnut
+++ /dev/null
@@ -1,395 +0,0 @@
-untyped
-
-global function MortarTitanThink
-global function MortarTitans_Init
-
-global function MortarTitanDeathCleanup
-global function MortarMissileFiredCallback
-global function MoveToMortarPosition
-
-global function MortarTitanKneelToAttack
-
-global function MortarTitanAttack
-
-global function MortarTitanStopAttack
-
-//global function MortarAIWaitToEngage
-
-const float MORTAR_TITAN_ABORT_ATTACK_HEALTH_FRAC = 0.90 // will stop mortar attack if he's health gets below 90% of his current health.
-const float MORTAR_TITAN_POSITION_SEARCH_RANGE = 1024 //3072 // How far away from his spawn point a mortar titan will look for positions to mortar from.
-const float MORTAR_TITAN_ENGAGE_DELAY = 3.0 // How long before a mortar titan start to attack the generator if he's taken damage getting to his mortar position.
-const float MORTAR_TITAN_REENGAGE_DELAY = 7.0 // How long before a mortar titan goes back to attacking the generator after breaking of an attack.
-
-// --------------------------------------------------------------------
-// MORTAR TITAN LOGIC
-// --------------------------------------------------------------------
-
-function MortarTitans_Init()
-{
- RegisterSignal( "InterruptMortarAttack" )
- RegisterSignal( "BeginMortarAttack" )
-}
-
-void function MortarTitanDeathCleanup( entity titan )
-{
- titan.EndSignal( "OnSyncedMeleeVictim" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( titan )
- {
- entity animEnt = titan.ai.carryBarrel
-
- if ( IsValid( animEnt ) )
- animEnt.Destroy()
-
- if ( IsAlive( titan ) )
- {
- titan.Signal( "InterruptMortarAttack" )
- titan.Anim_Stop()
- }
- }
- )
-
- WaitForever()
-}
-
-void function MortarMissileFiredCallback( entity missile, entity weaponOwner )
-{
- thread MortarMissileThink( missile, weaponOwner )
-}
-
-void function MortarMissileThink( entity missile, entity weaponOwner )
-{
- Assert( IsValid( missile ) )
-
- missile.EndSignal( "OnDestroy" )
- missile.EndSignal( "OnDeath" )
-
- if ( !IsValid( weaponOwner.ai.mortarTarget ) )
- return
-
- entity targetEnt = weaponOwner.ai.mortarTarget
-
- missile.DamageAliveOnly( true )
- missile.kv.lifetime = 6.0
- missile.s.mortar <- true
- vector startPos = missile.GetOrigin()
-
- // made a hacky way to get the mortar arc to go higher and still have it hit it's target.
-
- float dist = Distance( startPos, targetEnt.GetOrigin() )
-
- // radius tightens over time
- float radius = GraphCapped( Time() - weaponOwner.ai.spawnTime, 60.0, 180.0, 220, 100 )
- missile.SetMissileTarget( targetEnt, < RandomFloatRange( -radius, radius ), RandomFloatRange( -radius, radius ), 0 > )
-
- string sound = "weapon_spectremortar_projectile"
- if ( weaponOwner.IsTitan() )
- sound = "Weapon_FlightCore_Incoming_Projectile"
-
- EmitSoundAtPosition( weaponOwner.GetTeam(), targetEnt.GetOrigin(), sound )
-
- float homingSpeedMin = 10.0
- float homingSpeedMax = Graph( dist, 2500, 7000, 400, 200 )
- float estTravelTime = GraphCapped( dist, 0, 7000, 0, 5 )
-
- float startTime = Time()
- while( true )
- {
- float frac = min( 1, pow( ( Time() - startTime ) / estTravelTime, 2.0 ) )
-
- if ( frac > 1.0 )
- break
-
- float homingSpeed = GraphCapped( frac, 0, 1, homingSpeedMin, homingSpeedMax )
-
- missile.SetHomingSpeeds( homingSpeed, 0 )
-
- wait 0.25
- }
-
- missile.ClearMissileTargetPosition()
-}
-
-void function MoveToMortarPosition( entity titan, vector origin, entity target )
-{
- titan.EndSignal( "OnSyncedMeleeVictim" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- titan.SetLookDistOverride( 320 )
- titan.SetHearingSensitivity( 0 )
- titan.EnableNPCMoveFlag( NPCMF_PREFER_SPRINT )
-
- local animEnt = titan.ai.carryBarrel
-
- local dir = target.GetOrigin() - origin
- local dist = dir.Norm()
- local angles = VectorToAngles( dir )
- angles.x = 0
- angles.z = 0
-
- float frac = TraceLineSimple( origin + < 0, 0, 32 >, origin + < 0, 0, -32 >, titan )
- if ( frac > 0 && frac < 1 )
- origin = origin + < 0, 0, 32 > - < 0, 0, 64 * frac >
-
- animEnt.SetOrigin( origin )
- animEnt.SetAngles( angles )
-
- float goalRadius = titan.GetMinGoalRadius()
-
- OnThreadEnd(
- function() : ( titan )
- {
- if ( !IsValid( titan ) )
- return
-
- local classname = titan.GetClassName()
- titan.DisableLookDistOverride()
- titan.SetHearingSensitivity( 1 )
- titan.DisableNPCMoveFlag( NPCMF_PREFER_SPRINT )
- }
- )
-
- local tries = 0
- while( true )
- {
- local dist = Distance( titan.GetOrigin(), origin )
- if ( dist <= goalRadius * 2 )
- break
-
- printt( "Mortar titan moving toward his goal", dist, tries++ )
- titan.AssaultPoint( origin )
- titan.AssaultSetGoalRadius( goalRadius )
-
- local result = WaitSignal( titan, "OnFinishedAssault", "OnEnterGoalRadius" )
- }
-}
-
-void function MortarTitanKneelToAttack( entity titan )
-{
- titan.EndSignal( "OnSyncedMeleeVictim" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- entity animEnt = titan.ai.carryBarrel
- waitthread PlayAnim( titan, "at_mortar_stand2knee", animEnt )
-}
-
-function MortarTitanAttack( entity titan, entity target )
-{
- titan.EndSignal( "OnSyncedMeleeVictim" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
- titan.EndSignal( "InterruptMortarAttack" )
-
- OnThreadEnd(
- function() : ( titan )
- {
- if ( !IsValid( titan ) )
- return
-
- if ( "selectedPosition" in titan.s )
- {
- titan.s.selectedPosition.inUse = false
- delete titan.s.selectedPosition
- }
-
- if ( IsAlive( titan ) )
- thread MortarTitanAttackEnd( titan )
- }
- )
-
- titan.ai.mortarTarget = target
- entity animEnt = titan.ai.carryBarrel
-
- entity weapon = titan.GetActiveWeapon()
-
- while ( weapon.IsWeaponOffhand() )
- {
- WaitFrame()
- weapon = titan.GetActiveWeapon()
- }
-
- weapon.SetMods( [ "coop_mortar_titan" ] )
-
- while( true )
- {
- waitthread PlayAnim( titan, "at_mortar_knee", animEnt )
- }
-}
-
-function MortarTitanAttackEnd( entity titan )
-{
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- entity animEnt = titan.ai.carryBarrel
-
- // remove the mortar mod, we do this so that we don't get mortar sound and fx when firing normal
- entity weapon = titan.GetActiveWeapon()
-
- while ( weapon.IsWeaponOffhand() )
- {
- WaitFrame()
- weapon = titan.GetActiveWeapon()
- }
-
- weapon.SetMods( [] )
-
- WaitEndFrame() // if I didn't add this PlayAnim, below, would return immediately for some unknown reason.
-
- if ( IsValid( animEnt ) && IsAlive( titan ) )
- waitthread PlayAnim( titan, "at_mortar_knee2stand", animEnt )
-}
-
-function MortarTitanStopAttack( titan )
-{
- titan.Signal( "InterruptMortarAttack" )
-}
-
-function MortarTitanStopAttack_Internal( titan )
-{
- titan.Signal( "InterruptMortarAttack" )
- titan.Anim_Stop()
-}
-
-void function MortarAIWaitToEngage( entity titan, float timeFrame, int minDamage = 75 )
-{
- entity soul = titan.GetTitanSoul()
- float endtime = Time() + timeFrame
- int lastHealth = titan.GetHealth() + soul.GetShieldHealth()
- float tickTime = 1.0
-
- while ( Time() < endtime )
- {
- wait tickTime
-
- int currentHealth = titan.GetHealth() + soul.GetShieldHealth()
- if ( lastHealth > ( currentHealth + minDamage ) ) // add minDamage so that we ignore low amounts of damage.
- {
- lastHealth = currentHealth
- endtime = Time() + timeFrame
- }
- }
-}
-
-
-/*******************************************************************\
- MORTAR TITANS
-\*******************************************************************/
-//Function assumes that given Titan is spawned as npc_titan_atlas_tracker_mortar. Changing the Titan's AISettings post-spawn
-//disrupts the Titan's titanfall animations and can result in the Titan landing outside the level.
-void function MortarTitanThink( entity titan, entity generator )
-{
- titan.EndSignal( "OnSyncedMeleeVictim" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- entity soul = titan.GetTitanSoul()
- soul.EndSignal( "OnDestroy" )
-
- titan.ai.carryBarrel = CreateScriptRef()
- titan.TakeWeaponNow( titan.GetActiveWeapon().GetWeaponClassName() )
- titan.GiveWeapon( "mp_titanweapon_rocketeer_rocketstream" )
- titan.SetActiveWeaponByName( "mp_titanweapon_rocketeer_rocketstream" )
- titan.SetScriptName( "mortar_titan" )
-
- entity weapon = titan.GetActiveWeapon()
- weapon.w.missileFiredCallback = MortarMissileFiredCallback
- thread MortarTitanDeathCleanup( titan )
-
- WaitTillHotDropComplete( titan )
-
- float minEngagementDuration = 5
- StationaryAIPosition ornull mortarPosition = GetRandomStationaryPosition( titan.GetOrigin(), MORTAR_TITAN_POSITION_SEARCH_RANGE, eStationaryAIPositionTypes.MORTAR_TITAN )
- while ( mortarPosition == null )
- {
- // incase all stationary titan positions are in use wait for one to become available
- wait 5
- mortarPosition = GetRandomStationaryPosition( titan.GetOrigin(), MORTAR_TITAN_POSITION_SEARCH_RANGE, eStationaryAIPositionTypes.MORTAR_TITAN )
- }
-
- expect StationaryAIPosition( mortarPosition )
-
- ClaimStationaryAIPosition( mortarPosition )
-
- OnThreadEnd(
- function() : ( mortarPosition )
- {
- // release mortar position when dead
- ReleaseStationaryAIPosition( mortarPosition )
- }
- )
-
- float minDamage = 75 // so that the titan doesn't care about small amounts of damage.
-
- while( true )
- {
- vector origin = mortarPosition.origin
-
- float startHealth = float( titan.GetHealth() + soul.GetShieldHealth() )
- waitthread MoveToMortarPosition( titan, origin, generator )
-
- if ( startHealth > ( ( titan.GetHealth() + soul.GetShieldHealth() ) + minDamage ) || !titan.IsInterruptable() )
- {
- // we took damage getting to the mortar location lets wait until we stop taking damage
- waitthread MortarAIWaitToEngage( titan, MORTAR_TITAN_ENGAGE_DELAY )
- continue
- }
-
- waitthread MortarTitanKneelToAttack( titan )
- thread MortarTitanAttack( titan, generator )
-
- wait minEngagementDuration // aways mortar the target for a while before potentially breaking out
-
- // wait for interruption
- waitthread WaitForInteruption( titan )
-
- MortarTitanStopAttack_Internal( titan )
-
- // lets wait until we stop taking damage before going back to attacking the generator
- waitthread MortarAIWaitToEngage( titan, MORTAR_TITAN_REENGAGE_DELAY )
- }
-}
-
-void function WaitForInteruption( entity titan )
-{
- Assert( IsNewThread(), "Must be threaded off" )
-
- titan.EndSignal( "OnSyncedMeleeVictim" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
- titan.EndSignal( "InterruptMortarAttack" )
-
- entity soul = titan.GetTitanSoul()
- soul.EndSignal( "OnDestroy" )
-
- float playerProximityDistSqr = pow( 256, 2 )
- float healthBreakOff = ( titan.GetHealth() + soul.GetShieldHealth() ) * MORTAR_TITAN_ABORT_ATTACK_HEALTH_FRAC
-
- while( true )
- {
- if ( IsEnemyWithinDist( titan, playerProximityDistSqr ) )
- break
- if ( ( titan.GetHealth() + soul.GetShieldHealth() ) < healthBreakOff )
- break
- wait 1
- }
-}
-
-bool function IsEnemyWithinDist( entity titan, float dist )
-{
- vector origin = titan.GetOrigin()
- array<entity> players = GetPlayerArrayOfEnemies_Alive( titan.GetTeam() )
-
- foreach( player in players )
- {
- if ( DistanceSqr( player.GetOrigin(), origin ) < dist )
- return true
- }
-
- return false
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_nuke_titans.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_nuke_titans.gnut
deleted file mode 100644
index 0d4b43c92..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_nuke_titans.gnut
+++ /dev/null
@@ -1,129 +0,0 @@
-untyped
-
-global function NukeTitanThink
-
-global function AutoTitan_SelfDestruct
-
-const NUKE_TITAN_PLAYER_DETECT_RANGE = 500
-const NUKE_TITAN_RANGE_CHECK_SLEEP_SECS = 1.0
-
-void function AutoTitan_SelfDestruct( entity titan )
-{
- if ( titan.ContextAction_IsBusy() )
- titan.ContextAction_ClearBusy()
-
- thread TitanEjectPlayer( titan )
-}
-
-void function NukeTitanThink( entity titan, entity generator )
-{
- //Function assumes that given Titan is spawned as npc_titan_ogre_meteor_nuke. Changing the Titan's AISettings post-spawn
- //disrupts the Titan's titanfall animations and can result in the Titan landing outside the level.
- NPC_SetNuclearPayload( titan )
- AddEntityCallback_OnPostDamaged( titan, AutoTitan_NuclearPayload_PostDamageCallback )
-
- WaitTillHotDropComplete( titan )
-
- thread NukeTitanSeekOutGenerator( titan, generator )
-}
-
-
-void function NukeTitanSeekOutGenerator( entity titan, entity generator )
-{
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
- titan.EndSignal( "Doomed" )
-
- WaitSignal( titan, "FD_ReachedHarvester", "OnFailedToPath" )
-
- float goalRadius = 100
- float checkRadiusSqr = 400 * 400
-
- //array<vector> pos = NavMesh_RandomPositions( generator.GetOrigin(), HULL_TITAN, 5, 250, 350 )
- array<vector> pos = NavMesh_GetNeighborPositions( generator.GetOrigin(), HULL_TITAN, 5 )
- pos = ArrayClosestVector( pos, titan.GetOrigin() )
-
- array<vector> validPos
- foreach ( point in pos )
- {
- if ( DistanceSqr( generator.GetOrigin(), point ) <= checkRadiusSqr && NavMesh_IsPosReachableForAI( titan, point ) )
- {
- validPos.append( point )
- //DebugDrawSphere( point, 32, 255, 0, 0, true, 60 )
- }
- }
-
- int posLen = validPos.len()
- while( posLen >= 1 )
- {
- titan.SetEnemy( generator )
- thread AssaultOrigin( titan, validPos[0], goalRadius )
- titan.AssaultSetFightRadius( goalRadius )
-
- wait 0.5
-
- if ( DistanceSqr( titan.GetOrigin(), generator.GetOrigin() ) > checkRadiusSqr )
- continue
-
- break
- }
-
- thread AutoTitan_SelfDestruct( titan )
-}
-
-// intercept damage to nuke titans in damage callback so we can nuke them before death 100% of the time
-void function AutoTitan_NuclearPayload_PostDamageCallback( entity titan, var damageInfo )
-{
- if ( !IsAlive( titan ) )
- return
-
- entity titanOwner = titan.GetBossPlayer()
- if ( IsValid( titanOwner ) )
- {
- Assert( titanOwner.IsPlayer() )
- Assert( GetPlayerTitanInMap( titanOwner ) == titan )
- return
- }
-
- int nuclearPayload = NPC_GetNuclearPayload( titan )
- if ( nuclearPayload == 0 )
- return
-
- if ( !GetDoomedState( titan ) )
- return
-
- if ( titan.GetTitanSoul().IsEjecting() )
- return
-
- // Nuke eject as soon as the titan enters doom state.
- if ( !( "doomedStateNukeTriggerHealth" in titan.s ) )
- {
- titan.s.doomedStateNukeTriggerHealth <- titan.GetMaxHealth()
- }
-
- if ( titan.GetHealth() > titan.s.doomedStateNukeTriggerHealth )
- {
- //printt( "titan health:", titan.GetHealth(), "health to nuke:", titan.s.doomedStateNukeTriggerHealth )
- return
- }
-
- printt( "NUKE TITAN DOOMED TRIGGER HEALTH REACHED, NUKING! Health:", titan.s.doomedStateNukeTriggerHealth )
-
- thread AutoTitan_SelfDestruct( titan )
-}
-
-function AutoTitan_CanDoRangeCheck( autoTitan )
-{
- if ( !( "nextPlayerTitanRangeCheckTime" in autoTitan.s ) )
- autoTitan.s.nextPlayerTitanRangeCheckTime <- -1
-
- if ( Time() < autoTitan.s.nextPlayerTitanRangeCheckTime )
- {
- return false
- }
- else
- {
- autoTitan.s.nextPlayerTitanRangeCheckTime = Time() + NUKE_TITAN_RANGE_CHECK_SLEEP_SECS
- return true
- }
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_personal_shield.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_personal_shield.gnut
deleted file mode 100644
index f1fbdb80f..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_personal_shield.gnut
+++ /dev/null
@@ -1,371 +0,0 @@
-global function AiPersonalShield
-global function ActivatePersonalShield
-const FX_DRONE_SHIELD_WALL_HUMAN = $"P_drone_shield_wall_sm"
-const SHIELD_BREAK_FX = $"P_xo_armor_break_CP"
-const SHIELD_HEALTH = 620
-global const AI_PERSONAL_SHIELD_PAIN_SHIELD_STYLE = true
-const float PERSONAL_SHIELD_HEALTH_FRAC_DAMAGED = 0.5 // below what frac of total health will the personal shield owner want to chatter about shield damage?
-
-struct
-{
- table<entity, entity> npcVortexSpheres
-} file
-
-
-void function AiPersonalShield()
-{
- PrecacheParticleSystem( FX_DRONE_SHIELD_WALL_HUMAN )
- PrecacheParticleSystem( SHIELD_BREAK_FX )
- AddSyncedMeleeServerCallback( GetSyncedMeleeChooser( "human", "human" ), DisableShieldOnExecution )
-}
-
-void function DisableShieldOnExecution( SyncedMeleeChooser actions, SyncedMelee action, entity player, entity target )
-{
- if ( !( target in file.npcVortexSpheres ) )
- return
-
- entity vortex = file.npcVortexSpheres[ target ]
- vortex.Destroy()
-}
-
-void function ActivatePersonalShield( entity owner )
-{
- owner.EndSignal( "OnDeath" )
- for ( ;; )
- {
- waitthread ActivatePersonalShield_Recreate( owner )
-
- // got stunned? make new shield after awhile
- wait 15
- }
-}
-
-void function ShieldProtectsOwnerFromMelee( entity ent, var damageInfo )
-{
- entity attacker = DamageInfo_GetAttacker( damageInfo )
- if ( !IsAlive( attacker ) )
- return
- if ( !attacker.IsPlayer() )
- return
- if ( !IsPilot( attacker ) )
- return
- entity weapon = DamageInfo_GetWeapon( damageInfo )
- if ( !IsValid( weapon ) )
- weapon = attacker.GetActiveWeapon()
- if ( !IsValid( weapon ) )
- return
- var weaponType = weapon.GetWeaponInfoFileKeyField( "weaponType" )
- if ( weaponType != "melee" )
- return
-
- Assert( ent in file.npcVortexSpheres )
- entity vortexSphere = file.npcVortexSpheres[ ent ]
-
- float radius = float( vortexSphere.kv.radius )
- float height = float( vortexSphere.kv.height )
- float bullet_fov = float( vortexSphere.kv.bullet_fov )
- float dot = cos( bullet_fov * 0.5 )
-
- vector origin = vortexSphere.GetOrigin()
- vector angles = vortexSphere.GetAngles()
- vector forward = AnglesToForward( angles )
- int team = vortexSphere.GetTeam()
-
- if ( ProtectedFromShield( attacker, origin, height, radius, bullet_fov, dot, forward ) )
- {
- DamageInfo_SetDamage( damageInfo, 0 )
- StunPushBack( attacker, forward )
- }
-}
-
-entity function ActivatePersonalShield_Recreate( entity owner )
-{
- if ( !AI_PERSONAL_SHIELD_PAIN_SHIELD_STYLE )
- AddEntityCallback_OnDamaged( owner, ShieldProtectsOwnerFromMelee )
- //------------------------------
- // Shield vars
- //------------------------------
- vector origin = owner.GetOrigin()
- vector angles = owner.GetAngles() + Vector( 0, 0, 180 )
-
- float shieldWallRadius = 45 // 90
- asset shieldFx = FX_DRONE_SHIELD_WALL_HUMAN
- float wallFOV = DRONE_SHIELD_WALL_FOV_HUMAN
- float shieldWallHeight = 102
-
- //------------------------------
- // Vortex to block the actual bullets
- //------------------------------
- entity vortexSphere = CreateEntity( "vortex_sphere" )
-
- Assert( !( owner in file.npcVortexSpheres ), owner + " already has a shield" )
- file.npcVortexSpheres[ owner ] <- vortexSphere
- vortexSphere.kv.spawnflags = SF_ABSORB_BULLETS | SF_BLOCK_OWNER_WEAPON | SF_BLOCK_NPC_WEAPON_LOF | SF_ABSORB_CYLINDER
- vortexSphere.kv.enabled = 0
- vortexSphere.kv.radius = shieldWallRadius
- vortexSphere.kv.height = shieldWallHeight
- vortexSphere.kv.bullet_fov = wallFOV
- vortexSphere.kv.physics_pull_strength = 25
- vortexSphere.kv.physics_side_dampening = 6
- vortexSphere.kv.physics_fov = 360
- vortexSphere.kv.physics_max_mass = 2
- vortexSphere.kv.physics_max_size = 6
-
- StatusEffect_AddEndless( vortexSphere, eStatusEffect.destroyed_by_emp, 1.0 )
-
- vortexSphere.SetAngles( angles ) // viewvec?
- vortexSphere.SetOrigin( origin + Vector( 0, 0, shieldWallRadius - 64 ) )
- vortexSphere.SetMaxHealth( SHIELD_HEALTH )
- vortexSphere.SetHealth( SHIELD_HEALTH )
- SetTeam( vortexSphere, owner.GetTeam() )
-
- thread PROTO_VortexSlowsPlayers_PersonalShield( owner, vortexSphere )
-
- DispatchSpawn( vortexSphere )
-
- EntFireByHandle( vortexSphere, "Enable", "", 0, null, null )
-
- vortexSphere.SetTakeDamageType( DAMAGE_YES )
- vortexSphere.ClearInvulnerable() // make particle wall invulnerable to weapon damage. It will still drain over time
-
- //------------------------------------------
- // Shield wall fx for visuals/health drain
- //------------------------------------------
- entity cpoint = CreateEntity( "info_placement_helper" )
- SetTargetName( cpoint, UniqueString( "shield_wall_controlpoint" ) )
- DispatchSpawn( cpoint )
-
- entity mover = CreateScriptMover()
- mover.SetOrigin( owner.GetOrigin() )
- vector moverAngles = owner.GetAngles()
- mover.SetAngles( AnglesCompose( moverAngles, <0,0,180> ) )
-
- int fxid = GetParticleSystemIndex( FX_DRONE_SHIELD_WALL_HUMAN )
- entity shieldWallFX = StartParticleEffectOnEntity_ReturnEntity( mover, fxid, FX_PATTACH_ABSORIGIN_FOLLOW, 0 )
- shieldWallFX.DisableHibernation()
- EffectSetControlPointEntity( shieldWallFX, 0, mover )
-
- //thread DrawArrowOnTag( mover )
- vortexSphere.e.shieldWallFX = shieldWallFX
- vector color = GetShieldTriLerpColor( 0.0 )
-
- cpoint.SetOrigin( color )
- EffectSetControlPointEntity( shieldWallFX, 1, cpoint )
- SetVortexSphereShieldWallCPoint( vortexSphere, cpoint )
-
- #if GRUNTCHATTER_ENABLED
- // have to do this, vortex shield isn't an entity that works with AddEntityCallback_OnDamaged
- thread PersonalShieldOwner_ReactsToDamage( owner, vortexSphere )
- #endif
-
- //-----------------------
- // Attach shield to owner
- //------------------------
- vortexSphere.SetParent( mover )
-
- vortexSphere.EndSignal( "OnDestroy" )
- Assert( IsAlive( owner ) )
- owner.EndSignal( "OnDeath" )
- owner.EndSignal( "ArcStunned" )
- mover.EndSignal( "OnDestroy" )
- #if MP
- shieldWallFX.EndSignal( "OnDestroy" )
- #endif
-
- OnThreadEnd(
- function() : ( owner, mover, vortexSphere )
- {
- delete file.npcVortexSpheres[ owner ]
- if ( IsValid( owner ) )
- {
- owner.kv.defenseActive = false
- if ( !AI_PERSONAL_SHIELD_PAIN_SHIELD_STYLE )
- RemoveEntityCallback_OnDamaged( owner, ShieldProtectsOwnerFromMelee )
- }
-
- StopShieldWallFX( vortexSphere )
-
- if ( IsValid( vortexSphere ) )
- vortexSphere.Destroy()
-
- if ( IsValid( mover ) )
- {
- //PlayFX( SHIELD_BREAK_FX, mover.GetOrigin(), mover.GetAngles() )
- mover.Destroy()
- }
- }
- )
-
- owner.kv.defenseActive = true
-
- for ( ;; )
- {
- Assert( IsAlive( owner ) )
- UpdateShieldPosition( mover, owner )
-
- #if MP
- if ( IsCloaked( owner ) )
- EntFireByHandle( shieldWallFX, "Stop", "", 0, null, null )
- else
- EntFireByHandle( shieldWallFX, "Start", "", 0, null, null )
- #endif
- }
-}
-
-#if GRUNTCHATTER_ENABLED
-void function PersonalShieldOwner_ReactsToDamage( entity owner, entity vortexSphere )
-{
- EndSignal( owner, "OnDeath" )
- EndSignal( vortexSphere, "OnDestroy" )
-
- float alertHealth = vortexSphere.GetMaxHealth() * PERSONAL_SHIELD_HEALTH_FRAC_DAMAGED
-
- while ( vortexSphere.GetHealth() >= alertHealth )
- wait 0.25
-
- GruntChatter_TryPersonalShieldDamaged( owner ) //Commenting out to unblock tree. See bug 186062
-}
-#endif
-
-float function GetYawForEnemyOrLKP( entity owner )
-{
- entity enemy = owner.GetEnemy()
- if ( !IsValid( enemy ) )
- return owner.GetAngles().y
-
- vector ornull lkp = owner.LastKnownPosition( enemy )
- if ( lkp == null )
- return owner.GetAngles().y
-
- expect vector( lkp )
- vector dif = lkp - owner.GetOrigin()
- return VectorToAngles( dif ).y
-}
-
-void function UpdateShieldPosition( entity mover, entity owner )
-{
- mover.NonPhysicsMoveTo( owner.GetOrigin(), 0.1, 0.0, 0.0 )
- vector angles = owner.EyeAngles()
- float yaw = angles.y
- yaw %= 360
- mover.NonPhysicsRotateTo( <0,yaw,180>, 1.35, 0, 0 )
-
-// float yaw = GetYawForEnemyOrLKP( owner )
-// float boost = sin( Time() * 1.5 ) * 65
-// yaw += boost
-// yaw %= 360
-// mover.NonPhysicsRotateTo( <0,yaw,0>, 0.95, 0, 0 )
-
- WaitFrame()
-}
-
-void function PROTO_VortexSlowsPlayers_PersonalShield( entity owner, entity vortexSphere )
-{
- owner.EndSignal( "OnDeath" )
- vortexSphere.EndSignal( "OnDestroy" )
-
- float radius = float(vortexSphere.kv.radius )
- float height = float(vortexSphere.kv.height )
- float bullet_fov = float( vortexSphere.kv.bullet_fov )
- float dot = cos( bullet_fov * 0.5 )
-
- for ( ;; )
- {
- vector origin = vortexSphere.GetOrigin()
- vector angles = vortexSphere.GetAngles()
- vector forward = AnglesToForward( angles )
- int team = vortexSphere.GetTeam()
-
- foreach ( player in GetPlayerArray() )
- {
- if ( !IsAlive( player ) )
- continue
- if ( player.GetTeam() == team )
- continue
- if ( VortexStunCheck_PersonalShield( player, origin, height, radius, bullet_fov, dot, forward ) )
- {
- player.p.lastDroneShieldStunPushTime = Time()
-
- if ( AI_PERSONAL_SHIELD_PAIN_SHIELD_STYLE )
- {
- Explosion_DamageDefSimple( damagedef_shield_captain_arc_shield, player.GetOrigin(),owner, owner, player.GetOrigin() )
- }
- }
- }
- WaitFrame()
- }
-}
-
-bool function ProtectedFromShield( entity player, vector origin, float height, float radius, float bullet_fov, float dotLimit, vector forward )
-{
- vector playerOrg = player.GetOrigin()
- vector dif = Normalize( playerOrg - origin )
-
- float dot = DotProduct2D( dif, forward )
- return dot >= dotLimit
-}
-
-bool function VortexStunCheck_PersonalShield( entity player, vector origin, float height, float radius, float bullet_fov, float dot, vector forward )
-{
- if ( !IsPilot( player ) )
- return false
-
- if ( player.IsGodMode() )
- return false
-
- if ( AI_PERSONAL_SHIELD_PAIN_SHIELD_STYLE )
- {
- if ( Time() - player.p.lastDroneShieldStunPushTime < 1.00 )
- return false
- }
- else
- {
- if ( Time() - player.p.lastDroneShieldStunPushTime < 1.75 )
- return false
- }
-
- vector playerOrg = player.GetOrigin()
- float dist2d = Distance2D( playerOrg, origin )
-
- if ( dist2d > radius + 5 )
- return false
- if ( dist2d < radius - 15 )
- return false
-
- float heightOffset = fabs( playerOrg.z - origin.z )
-
- if ( heightOffset < 0 || heightOffset > height )
- return false
-
- if ( !ProtectedFromShield( player, origin, height, radius, bullet_fov, dot, forward ) )
- return false
-
- if ( AI_PERSONAL_SHIELD_PAIN_SHIELD_STYLE )
- {
- const float VORTEX_STUN_DURATION = 1.0
- GiveEMPStunStatusEffects( player, VORTEX_STUN_DURATION + 0.5 )
- float strength = 0.4
- StatusEffect_AddTimed( player, eStatusEffect.emp, strength, VORTEX_STUN_DURATION, 0.5 )
- EmitSoundOnEntityOnlyToPlayer( player, player, "flesh_electrical_damage_1p" )
- }
- else
- {
- StunPushBack( player, forward )
- }
-
- return true
-}
-
-void function StunPushBack( entity player, vector forward )
-{
- const float VORTEX_STUN_DURATION = 1.0
- GiveEMPStunStatusEffects( player, VORTEX_STUN_DURATION + 0.5 )
- float strength = 0.4
- StatusEffect_AddTimed( player, eStatusEffect.emp, strength, VORTEX_STUN_DURATION, 0.5 )
- thread TempLossOfAirControl( player, VORTEX_STUN_DURATION )
- vector velocity = forward * 300
- velocity.z = 400
-
- EmitSoundOnEntityOnlyToPlayer( player, player, "flesh_electrical_damage_1p" )
- player.SetVelocity( velocity )
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_pilots.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_pilots.gnut
deleted file mode 100644
index 3c2e36ce0..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_pilots.gnut
+++ /dev/null
@@ -1,808 +0,0 @@
-untyped
-
-global const NPC_TITAN_PILOT_PROTOTYPE = 0
-global function AiPilots_Init
-
-global function CaptainThink
-
-
-#if NPC_TITAN_PILOT_PROTOTYPE
-global function NpcPilotCallTitanThink
-global function NpcPilotStopCallTitanThink
-global function NpcPilotCallsInAndEmbarksTitan
-global function NpcPilotRunsToAndEmbarksFallingTitan
-global function NpcPilotCallsInTitan
-global function NpcPilotRunsToEmbarkTitan
-global function NpcPilotEmbarksTitan
-global function NpcPilotDisembarksTitan
-global function NpcPilotBecomesTitan
-global function NpcTitanBecomesPilot
-global function TitanHasNpcPilot
-global function NpcPilotGetPetTitan
-global function NpcPilotSetPetTitan
-#endif
-
-global function NpcSetNextTitanRespawnAvailable
-global function NpcResetNextTitanRespawnAvailable
-
-global function AddCallback_OnNpcTitanBecomesPilot
-global function AddCallback_OnNpcPilotBecomesTitan
-
-global struct NPCPilotStruct
-{
- bool isValid = false
-
- int team
- int spawnflags
- float accuracy
- float proficieny
- float health
- float physDamageScale
- string weapon
- string squadName
-
- asset modelAsset
- string title
-
- bool isInvulnerable
-}
-
-const NPC_NEXT_TITANTIME_RESET = -1
-const NPC_NEXT_TITANTIME_MIN = 45
-const NPC_NEXT_TITANTIME_MAX = 60
-const NPC_NEXT_TITANTIME_INTERUPT = 15
-
-function AiPilots_Init()
-{
- RegisterSignal( "grenade_throw" )
- RegisterSignal( "NpcPilotBecomesTitan" )
- RegisterSignal( "NpcTitanBecomesPilot" )
- RegisterSignal( "StopCallTitanThink" )
- RegisterSignal( "NpcTitanRespawnAvailableUpdated" )
-
- level.onNpcPilotBecomesTitanCallbacks <- []
- level.onNpcTitanBecomesPilotCallbacks <- []
-
-}
-
-function ScriptCallback_OnNpcPilotBecomesTitan( pilot, titan )
-{
- local result = { pilot = pilot, titan = titan }
- Signal( pilot, "NpcPilotBecomesTitan", result )
- Signal( titan, "NpcPilotBecomesTitan", result )
-
- foreach ( callbackFunc in level.onNpcPilotBecomesTitanCallbacks )
- {
- callbackFunc( pilot, titan )
- }
-}
-
-function ScriptCallback_OnNpcTitanBecomesPilot( pilot, titan )
-{
- local result = { pilot = pilot, titan = titan }
- Signal( pilot, "NpcTitanBecomesPilot", result )
- Signal( titan, "NpcTitanBecomesPilot", result )
-
- foreach ( callbackFunc in level.onNpcTitanBecomesPilotCallbacks )
- {
- callbackFunc( pilot, titan )
- }
-}
-
-function AddCallback_OnNpcPilotBecomesTitan( callbackFunc )
-{
- Assert( "onNpcPilotBecomesTitanCallbacks" in level )
- AssertParameters( callbackFunc, 2, "pilotNPC, titanNPC" )
-
- level.onNpcPilotBecomesTitanCallbacks.append( callbackFunc )
-}
-
-function AddCallback_OnNpcTitanBecomesPilot( callbackFunc )
-{
- Assert( "onNpcTitanBecomesPilotCallbacks" in level )
- AssertParameters( callbackFunc, 2, "pilotNPC, titanNPC" )
-
- level.onNpcTitanBecomesPilotCallbacks.append( callbackFunc )
-}
-
-function NpcSetNextTitanRespawnAvailable( npc, time )
-{
- Assert( "nextTitanRespawnAvailable" in npc.s )
- npc.s.nextTitanRespawnAvailable = time
- npc.Signal( "NpcTitanRespawnAvailableUpdated" )
-}
-
-function NpcResetNextTitanRespawnAvailable( npc )
-{
- Assert( "nextTitanRespawnAvailable" in npc.s )
- npc.s.nextTitanRespawnAvailable = NPC_NEXT_TITANTIME_RESET
- npc.Signal( "NpcTitanRespawnAvailableUpdated" )
-}
-
-function NpcPilotStopCallTitanThink( pilot )
-{
- pilot.Signal( "StopCallTitanThink" )
-}
-
-/************************************************************************************************\
-
-######## #### ## ####### ######## ######## ## ## #### ## ## ## ##
-## ## ## ## ## ## ## ## ## ## ## ### ## ## ##
-## ## ## ## ## ## ## ## ## ## ## #### ## ## ##
-######## ## ## ## ## ## ## ######### ## ## ## ## #####
-## ## ## ## ## ## ## ## ## ## ## #### ## ##
-## ## ## ## ## ## ## ## ## ## ## ### ## ##
-## #### ######## ####### ## ## ## ## #### ## ## ## ##
-
-\************************************************************************************************/
-function CaptainThink( entity npc )
-{
- npc.EndSignal( "OnDestroy" )
- npc.EndSignal( "OnDeath" )
-
- Assert( !( "nextTitanRespawnAvailable" in npc.s ) )
- Assert( !( "petTitan" in npc.s ) )
-
- npc.s.petTitan <- null
- npc.s.nextTitanRespawnAvailable <- null
-
- //wait for in combat...
- WaitForNpcInCombat( npc )
-
- //... before we call in a titan
- if ( npc.s.nextTitanRespawnAvailable == null )
- npc.s.nextTitanRespawnAvailable = Time() + RandomFloatRange( 2, 10 )
-
- WaitEndFrame() //wait a frame for things like petTitan and nextTitanRespawnAvailable to have a chance to be set from custom scripts
- #if NPC_TITAN_PILOT_PROTOTYPE
- thread NpcPilotCallTitanThink( npc )
- #endif
-}
-
-#if NPC_TITAN_PILOT_PROTOTYPE
-
-function NpcPilotCallTitanThink( entity pilot )
-{
- Assert( pilot.IsNPC() )
- Assert( IsAlive( pilot ) )
- Assert ( !pilot.IsTitan() )
-
- pilot.EndSignal( "OnDestroy" )
- pilot.EndSignal( "OnDeath" )
- pilot.Signal( "StopCallTitanThink" )
- pilot.EndSignal( "StopCallTitanThink" )
-
-
- string title = pilot.GetTitle() + "'s Titan"
- local count = 1 //1 titan call in at a time
-
- while ( true ) //this loop usually only happens once, unless the titan called in is destroyed before the living pilot can get to it
- {
- entity titan = NpcPilotGetPetTitan( pilot )
- if ( !IsAlive( titan ) )
- {
- //wait for ready titan
- waitthread __WaitforTitanCallinReady( pilot )
-
- //ready to call in - look for a good spot
- SpawnPointFP spawnPoint
- while ( true )
- {
- wait ( RandomFloatRange( 1, 2 ) )
-
- //dont do stuff when animating on a parent
- if ( pilot.GetParent() )
- continue
-
- //Don't deploy if too close to an enemy
- if ( HasEnemyWithinDist( pilot, 300.0 ) )
- continue
-
- // DO the opposite - only deploy if has an enemy within this distance
- // if ( !HasEnemyWithinDist( pilot, 2000.0 ) )
- // continue
-
- //don't do stuff if you dont have a spawnPoint
- spawnPoint = FindSpawnPointForNpcCallin( pilot, TITAN_MEDIUM_AJAX_MODEL, HOTDROP_TURBO_ANIM )
- if ( !spawnPoint.valid )
- continue
-
- break
- }
-
- //call in a titan, run to it, and embark
- //in SP by default, the friendlys do NOT do the beacon tell
- titan = NpcPilotCallsInAndEmbarksTitan( pilot, spawnPoint.origin, spawnPoint.angles )
- titan.SetTitle( title )
- }
- else
- {
- Assert( IsAlive( titan ) )
-
- if ( HasEnemyRodeo( titan ) )
- {
- while ( HasEnemyRodeo( titan ) )
- {
- WaitSignal( titan.GetTitanSoul(), "RodeoRiderChanged", "OnDestroy" )
- }
-
- wait 4 //don't pop back in immediately
- }
-
- if ( !IsAlive( titan ) )
- continue //the titan didn't make it, lets loop back up and try again
-
- if ( titan.GetTitanSoul().IsDoomed() )
- {
- titan.WaitSignal( "OnDestroy" )
- continue //the titan didn't make it, lets loop back up and try again
- }
-
- //start running to titan as it kneels
- thread NpcPilotRunsToEmbarkTitan( pilot, titan )
- thread __TitanKneelsForPilot( pilot, titan )
- wait 2.0 //wait for titan to be in position
-
- if ( !IsAlive( titan ) )
- continue //the titan didn't make it, lets loop back up and try again
-
- //run to the titan
- waitthread NpcPilotRunsToEmbarkTitan( pilot, titan )
-
- if ( !IsAlive( titan ) )
- continue //the titan didn't make it, lets loop back up and try again
-
- //embark titan
- thread NpcPilotEmbarksTitan( pilot, titan )
- }
-
- local result = WaitSignal( titan, "NpcPilotBecomesTitan", "OnDeath", "OnDestroy" )
- if ( result.signal != "NpcPilotBecomesTitan" )
- continue //the titan didn't make it, lets loop back up and try again
- }
-}
-
-/************************************************************************************************\
-
- ###### ### ## ## #### ## ## ######## #### ######## ### ## ##
-## ## ## ## ## ## ## ### ## ## ## ## ## ## ### ##
-## ## ## ## ## ## #### ## ## ## ## ## ## #### ##
-## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
-## ######### ## ## ## ## #### ## ## ## ######### ## ####
-## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ###
- ###### ## ## ######## ######## #### ## ## ## #### ## ## ## ## ##
-
-\************************************************************************************************/
-
-
-entity function NpcPilotCallsInAndEmbarksTitan( entity pilot, vector origin, vector angles )
-{
- entity titan = NpcPilotCallsInTitan( pilot, origin, angles )
- thread NpcPilotRunsToAndEmbarksFallingTitan( pilot, titan )
-
- return titan
-}
-
-function NpcPilotRunsToAndEmbarksFallingTitan( entity pilot, entity titan )
-{
- titan.EndSignal( "OnDeath" )
-
- //wait for it to land
- waitthread WaitTillHotDropComplete( titan )
- ShowName( titan )
-
- if ( !IsAlive( titan ) )
- return
- titan.EndSignal( "OnDeath" )
-
- //titan is alive on land so clean it up on thread end
- OnThreadEnd(
- function () : ( titan )
- {
- if ( !IsAlive( titan ) )
- return
-
- SetStanceStand( titan.GetTitanSoul() )
-
- //the pilot never made it to embark - lets stand our titan up so he can fight
- if ( !TitanHasNpcPilot( titan ) )
- {
- thread PlayAnimGravity( titan, "at_hotdrop_quickstand" )
- HideName( titan )
- }
- }
- )
-
- //if the pilot has died, early out
- if ( !IsAlive( pilot ) )
- return
-
- pilot.EndSignal( "OnDeath" )
-
- //run to the titan
- waitthread NpcPilotRunsToEmbarkTitan( pilot, titan )
-
- //embark titan
- waitthread NpcPilotEmbarksTitan( pilot, titan )
-}
-
-entity function NpcPilotCallsInTitan( entity pilot, vector origin, vector angles )
-{
- Assert( !pilot.IsTitan() )
- Assert( IsAlive( pilot ) )
- Assert( !NpcPilotGetPetTitan( pilot ) )
-
- //reset the next titan callin timer
- NpcResetNextTitanRespawnAvailable( pilot )
-
- //spawn a titan
- array<string> settingsArray = GetAllowedTitanAISettings()
-
- string titanSettings = settingsArray.getrandom()
- entity titan = CreateNPC( "npc_titan", pilot.GetTeam(), origin, angles )
- SetSpawnOption_AISettings( titan, titanSettings )
- DispatchSpawn( titan )
-
- NpcPilotSetPetTitan( pilot, titan )
-
- //call it in
- thread NPCTitanHotdrops( titan, false, "at_hotdrop_drop_2knee_turbo_upgraded" )
- thread __TitanKneelOrStandAfterDropin( titan, pilot )
-
- //get the titan ready to be embarked
- SetStanceKneel( titan.GetTitanSoul() )
- titan.SetTitle( pilot.GetTitle() + "'s Titan" )
- UpdateEnemyMemoryFromTeammates( titan )
-
- return titan
-}
-
-void function __TitanKneelOrStandAfterDropin( entity titan, entity pilot )
-{
- Assert( IsAlive( titan ) )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- titan.WaitSignal( "TitanHotDropComplete" )
-
- if ( IsAlive( pilot ) )
- thread PlayAnimGravity( titan, "at_MP_embark_idle" )
- //else the titan will automatically stand up
-}
-
-//HACK -> this behavior should be completely in code
-void function NpcPilotRunsToEmbarkTitan( entity pilot, entity titan )
-{
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
- pilot.EndSignal( "OnDeath" )
- pilot.EndSignal( "OnDestroy" )
-
- pilot.SetNoTarget( true )
- pilot.Anim_Stop()
- pilot.DisableNPCMoveFlag( NPCMF_INDOOR_ACTIVITY_OVERRIDE )
- pilot.EnableNPCMoveFlag( NPCMF_IGNORE_CLUSTER_DANGER_TIME | NPCMF_PREFER_SPRINT )
- pilot.DisableArrivalOnce( true )
- bool canMoveAndShoot = pilot.GetCapabilityFlag( bits_CAP_MOVE_SHOOT )
- pilot.SetCapabilityFlag( bits_CAP_MOVE_SHOOT, false )
-
- OnThreadEnd(
- function () : ( pilot, canMoveAndShoot )
- {
- if ( !IsAlive( pilot ) )
- return
-
- pilot.SetNoTarget( false )
- pilot.EnableNPCMoveFlag( NPCMF_INDOOR_ACTIVITY_OVERRIDE )
- pilot.DisableNPCMoveFlag( NPCMF_IGNORE_CLUSTER_DANGER_TIME | NPCMF_PREFER_SPRINT )
- pilot.SetCapabilityFlag( bits_CAP_MOVE_SHOOT, canMoveAndShoot )
- }
- )
-
- local titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() )
- local embarkSet = FindBestEmbarkForNpcAnim( pilot, titan )
- string pilotAnim = GetAnimFromAlias( titanSubClass, embarkSet.animSet.thirdPersonKneelingAlias )
-
- pilot.ClearAllEnemyMemory()
- waitthread RunToAnimStartForced_Deprecated( pilot, pilotAnim, titan, "hijack" )
-}
-
-/************************************************************************************************\
-
- ###### ## ## #### ######## ###### ## ##
-## ## ## ## ## ## ## ## ## ## ##
-## ## ## ## ## ## ## ## ##
- ###### ## ## ## ## ## ## #########
- ## ## ## ## ## ## ## ## ##
-## ## ## ## ## ## ## ## ## ## ##
- ###### ### ### #### ## ###### ## ##
-
-\************************************************************************************************/
-function NpcPilotEmbarksTitan( entity pilot, entity titan )
-{
- Assert( IsAlive( pilot ) )
- Assert( IsAlive( titan ) )
- Assert( !pilot.IsTitan() )
- Assert( titan.IsTitan() )
-
- titan.EndSignal( "OnDestroy" )
- titan.EndSignal( "OnDeath" )
-
- OnThreadEnd(
- function () : ( titan, pilot )
- {
- if ( IsAlive( titan ) )
- {
- if ( titan.ContextAction_IsBusy() )
- titan.ContextAction_ClearBusy()
- titan.ClearInvulnerable()
-
- Assert( !IsAlive( pilot ) )
- }
- }
- )
-
- local isInvulnerable = pilot.IsInvulnerable()
- pilot.SetInvulnerable()
- titan.SetInvulnerable()
-
- local titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() )
- local embarkSet = FindBestEmbark( pilot, titan )
-
- while ( embarkSet == null )
- {
- wait 1.0
- embarkSet = FindBestEmbark( pilot, titan )
- }
-
- local pilotAnim = GetAnimFromAlias( titanSubClass, embarkSet.animSet.thirdPersonKneelingAlias )
- local titanAnim = embarkSet.animSet.titanKneelingAnim
-
- if ( !titan.ContextAction_IsBusy() ) //might be set from kneeling
- titan.ContextAction_SetBusy()
- pilot.ContextAction_SetBusy()
-
- if ( IsCloaked( pilot ) )
- pilot.SetCloakDuration( 0, 0, 1.5 )
-
- //pilot.SetParent( titan, "hijack", false, 0.5 ) //the time is just in case their not exactly at the right starting position
- EmitSoundOnEntity( titan, embarkSet.audioSet.thirdPersonKneelingAudioAlias )
- thread PlayAnim( pilot, pilotAnim, titan, "hijack" )
- waitthread PlayAnim( titan, titanAnim )
-
- if ( !isInvulnerable )
- pilot.ClearInvulnerable()
-
- NpcPilotBecomesTitan( pilot, titan )
-}
-
-entity function NpcPilotDisembarksTitan( entity titan )
-{
- Assert( titan.IsTitan() )
- Assert( TitanHasNpcPilot( titan ) )
-
- entity pilot = NpcTitanBecomesPilot( titan )
- Assert( !pilot.IsTitan() )
-
- NpcPilotSetPetTitan( pilot, titan )
-
- thread __NpcPilotDisembarksTitan( pilot, titan )
-
- return pilot
-}
-
-function __NpcPilotDisembarksTitan( pilot, titan )
-{
- expect entity( pilot )
- expect entity( titan )
-
- titan.ContextAction_SetBusy()
- pilot.ContextAction_SetBusy()
-
- if ( pilot.GetTitle() != "" )
- {
- titan.SetTitle( pilot.GetTitle() + "'s Titan" )
- }
-
- local isInvulnerable = pilot.IsInvulnerable()
- pilot.SetInvulnerable()
- titan.SetInvulnerable()
-
- local pilot3pAnim, pilot3pAudio, titanDisembarkAnim
- local titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() )
- local standing = titan.GetTitanSoul().GetStance() >= STANCE_STANDING // STANCE_STANDING = 2, STANCE_STAND = 3
-
- if ( standing )
- {
- titanDisembarkAnim = "at_dismount_stand"
- pilot3pAnim = "pt_dismount_" + titanSubClass + "_stand"
- pilot3pAudio = titanSubClass + "_Disembark_Standing_3P"
- }
- else
- {
- titanDisembarkAnim = "at_dismount_crouch"
- pilot3pAnim = "pt_dismount_" + titanSubClass + "_crouch"
- pilot3pAudio = titanSubClass + "_Disembark_Kneeling_3P"
- }
-
-// pilot.SetParent( titan, "hijack" )
- EmitSoundOnEntity( titan, pilot3pAudio )
- thread PlayAnim( titan, titanDisembarkAnim )
- waitthread PlayAnim( pilot, pilot3pAnim, titan, "hijack" )
-
- //pilot.ClearParent()
- titan.ContextAction_ClearBusy()
- pilot.ContextAction_ClearBusy()
- if ( !isInvulnerable )
- pilot.ClearInvulnerable()
- titan.ClearInvulnerable()
-
- if ( !standing )
- SetStanceKneel( titan.GetTitanSoul() )
-}
-
-void function NpcPilotBecomesTitan( entity pilot, entity titan )
-{
- Assert( IsAlive( pilot ) )
- Assert( IsAlive( titan ) )
- Assert( IsGrunt( pilot ) || IsPilotElite( pilot ) )
- Assert( titan.IsTitan() )
-
- entity titanSoul = titan.GetTitanSoul()
-
- titanSoul.soul.seatedNpcPilot.isValid = true
-
- titanSoul.soul.seatedNpcPilot.team = pilot.GetTeam()
- titanSoul.soul.seatedNpcPilot.spawnflags = expect int( pilot.kv.spawnflags )
- titanSoul.soul.seatedNpcPilot.accuracy = expect float( pilot.kv.AccuracyMultiplier )
- titanSoul.soul.seatedNpcPilot.proficieny = expect float( pilot.kv.WeaponProficiency )
- titanSoul.soul.seatedNpcPilot.health = expect float( pilot.kv.max_health )
- titanSoul.soul.seatedNpcPilot.physDamageScale = expect float( pilot.kv.physdamagescale )
- titanSoul.soul.seatedNpcPilot.weapon = pilot.GetMainWeapons()[0].GetWeaponClassName()
- titanSoul.soul.seatedNpcPilot.squadName = expect string( pilot.kv.squadname )
-
- titanSoul.soul.seatedNpcPilot.modelAsset = pilot.GetModelName()
- titanSoul.soul.seatedNpcPilot.title = pilot.GetTitle()
-
- titanSoul.soul.seatedNpcPilot.isInvulnerable = pilot.IsInvulnerable()
-
- titan.SetTitle( titanSoul.soul.seatedNpcPilot.title )
-
- thread __TitanPilotRodeoCounter( titan )
-
- ScriptCallback_OnNpcPilotBecomesTitan( pilot, titan )
-
- pilot.Destroy()
-}
-
-entity function NpcTitanBecomesPilot( entity titan )
-{
- Assert( IsValid( titan ) )
- Assert( titan.IsTitan() )
-
- entity titanSoul = titan.GetTitanSoul()
- titanSoul.soul.seatedNpcPilot.isValid = false
-
- string weapon = titanSoul.soul.seatedNpcPilot.weapon
- string squadName = titanSoul.soul.seatedNpcPilot.squadName
- asset model = titanSoul.soul.seatedNpcPilot.modelAsset
- string title = titanSoul.soul.seatedNpcPilot.title
- int team = titanSoul.soul.seatedNpcPilot.team
- vector origin = titan.GetOrigin()
- vector angles = titan.GetAngles()
- entity pilot = CreateElitePilot( team, origin, angles )
-
- SetSpawnOption_Weapon( pilot, weapon )
- SetSpawnOption_SquadName( pilot, squadName )
- pilot.SetValueForModelKey( model )
- DispatchSpawn( pilot )
- pilot.SetModel( model ) // this is a hack, trying to avoid having a model spawn option because its easy to abuse
-
- NpcPilotSetPetTitan( pilot, titan )
- NpcResetNextTitanRespawnAvailable( pilot )
-
- pilot.kv.spawnflags = titanSoul.soul.seatedNpcPilot.spawnflags
- pilot.kv.AccuracyMultiplier = titanSoul.soul.seatedNpcPilot.accuracy
- pilot.kv.WeaponProficiency = titanSoul.soul.seatedNpcPilot.proficieny
- pilot.kv.health = titanSoul.soul.seatedNpcPilot.health
- pilot.kv.max_health = titanSoul.soul.seatedNpcPilot.health
- pilot.kv.physDamageScale = titanSoul.soul.seatedNpcPilot.physDamageScale
-
- if ( titanSoul.soul.seatedNpcPilot.isInvulnerable )
- pilot.SetInvulnerable()
-
- titan.SetOwner( pilot )
- NPCFollowsNPC( titan, pilot )
-
- UpdateEnemyMemoryFromTeammates( pilot )
- thread __TitanStanceThink( pilot, titan )
-
- ScriptCallback_OnNpcTitanBecomesPilot( pilot, titan )
-
- return pilot
-}
-
-bool function TitanHasNpcPilot( entity titan )
-{
- Assert( titan.IsTitan() )
-
- entity titanSoul = titan.GetTitanSoul()
- if ( !IsValid( titanSoul ) )
- return false
-
- if ( !titanSoul.soul.seatedNpcPilot.isValid )
- return false
-
- return true
-}
-
-entity function NpcPilotGetPetTitan( entity pilot )
-{
- Assert( !pilot.IsTitan() )
- Assert( "petTitan" in pilot.s )
-
- if ( !IsAlive( expect entity( pilot.s.petTitan ) ) )
- return null
-
- Assert( pilot.s.petTitan.IsTitan() )
- return expect entity( pilot.s.petTitan )
-}
-
-void function NpcPilotSetPetTitan( entity pilot, entity titan )
-{
- Assert( !pilot.IsTitan() )
- Assert( titan.IsTitan() )
- Assert( "petTitan" in pilot.s )
-
- pilot.s.petTitan = titan
- pilot.Signal( "PetTitanUpdated" )
-}
-#endif // NPC_TITAN_PILOT_PROTOTYPE
-
-function __TitanStanceThink( entity pilot, entity titan )
-{
- if ( !IsAlive( titan ) )
- return
-
- if ( titan.GetTitanSoul().IsDoomed() )
- return
-
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
- titan.EndSignal( "NpcPilotBecomesTitan" )
-
- WaittillAnimDone( titan ) //wait for disembark anim
-
- // kneel in certain circumstances
- while ( IsAlive( pilot ) )
- {
- if ( !ChangedStance( titan ) )
- waitthread TitanWaitsToChangeStance_or_PilotDeath( pilot, titan )
- }
-
- if ( titan.GetTitanSoul().GetStance() < STANCE_STANDING )
- {
- while ( !TitanCanStand( titan ) )
- wait 2
-
- TitanStandUp( titan )
- }
-}
-
-function TitanWaitsToChangeStance_or_PilotDeath( pilot, titan )
-{
- pilot.EndSignal( "OnDeath" )
- pilot.EndSignal( "OnDestroy" )
-
- TitanWaitsToChangeStance( titan )
-}
-
-/************************************************************************************************\
-
-######## ####### ####### ## ######
- ## ## ## ## ## ## ## ##
- ## ## ## ## ## ## ##
- ## ## ## ## ## ## ######
- ## ## ## ## ## ## ##
- ## ## ## ## ## ## ## ##
- ## ####### ####### ######## ######
-
-\************************************************************************************************/
-
-function __WaitforTitanCallinReady( entity pilot )
-{
- pilot.EndSignal( "OnDeath" )
- pilot.EndSignal( "OnDestroy" )
-
- //HACK TODO: handle eTitanAvailability.Default vs custom and none, AND ALSO make a way to kill this thread
-
- while ( true )
- {
- if ( pilot.s.nextTitanRespawnAvailable == NPC_NEXT_TITANTIME_RESET )
- pilot.s.nextTitanRespawnAvailable = Time() + RandomFloatRange( NPC_NEXT_TITANTIME_MIN, NPC_NEXT_TITANTIME_MAX ) //this is just a random number - maybe in the future it will be based on the npc's kills...maybe also on the players if it's a slot
-
- if ( pilot.s.nextTitanRespawnAvailable <= Time() )
- break
-
- float delay = max( pilot.s.nextTitanRespawnAvailable - Time(), 0.1 ) //make sure min delay of 0.1 to account for floating point error
-
- thread SetSignalDelayed( pilot, "NpcTitanRespawnAvailableUpdated", delay )
- pilot.WaitSignal( "NpcTitanRespawnAvailableUpdated" )
-
- //keep looping backup just in case this value changes outside this function, we get an update
- continue
- }
-
- Assert( Time() >= pilot.s.nextTitanRespawnAvailable )
- Assert( pilot.s.nextTitanRespawnAvailable != NPC_NEXT_TITANTIME_RESET )
-}
-
-function __TitanKneelsForPilot( pilot, titan )
-{
- expect entity( pilot )
- expect entity( titan )
-
- pilot.EndSignal( "OnDeath" )
- pilot.EndSignal( "OnDestroy" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function () : ( pilot, titan )
- {
- if ( !IsAlive( titan ) )
- return
-
- SetStanceStand( titan.GetTitanSoul() )
-
- //the pilot never made it to embark - lets stand our titan up so he can fight
- if ( !IsAlive( pilot ) )
- {
- thread PlayAnimGravity( titan, "at_hotdrop_quickstand" )
- HideName( titan )
- titan.ContextAction_ClearBusy()
- }
- }
- )
-
- if ( !titan.ContextAction_IsBusy() ) //might be set from kneeling
- titan.ContextAction_SetBusy()
- SetStanceKneel( titan.GetTitanSoul() )
-
- waitthread PlayAnimGravity( titan, "at_MP_stand2knee_straight" )
- waitthread PlayAnim( titan, "at_MP_embark_idle" )
-}
-
-function HasEnemyRodeo( titan )
-{
- expect entity( titan )
-
- if ( !IsAlive( titan ) )
- return false
-
- if ( IsValid( GetEnemyRodeoPilot( titan ) ) )
- return true
-
- return false
-}
-
-function __TitanPilotRodeoCounter( entity titan )
-{
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- while ( true )
- {
- while ( !HasEnemyRodeo( titan ) )
- titan.GetTitanSoul().WaitSignal( "RodeoRiderChanged" )
-
- wait RandomFloatRange( 3, 6 ) //give some time for debounce in case the rider jumps right off
- if ( !HasEnemyRodeo( titan ) )
- continue
-
- #if NPC_TITAN_PILOT_PROTOTYPE
- thread NpcPilotDisembarksTitan( titan )
- return
- #endif
- }
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_sniper_titans.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_sniper_titans.gnut
deleted file mode 100644
index 37b891699..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_sniper_titans.gnut
+++ /dev/null
@@ -1 +0,0 @@
-//fuck \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut
deleted file mode 100644
index 9717c76d9..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut
+++ /dev/null
@@ -1,787 +0,0 @@
-untyped
-
-global const RPG_USE_ALWAYS = 2
-
-global const STANDARDGOALRADIUS = 100
-
-global function AiSoldiers_Init
-
-global function MakeSquadName
-global function GetPlayerSpectreSquadName
-global function disable_npcs
-global function disable_new_npcs
-global function Disable_IMC
-global function Disable_MILITIA
-
-global function CommonMinionInit
-global function DisableMinionUsesHeavyWeapons
-global function SetupMinionForRPGs
-global function IsNPCSpawningEnabled
-global function EnableAutoPopulate
-global function DisableAutoPopulate
-global function OnEnemyChanged_MinionSwitchToHeavyArmorWeapon
-global function OnEnemyChanged_MinionUpdateAimSettingsForEnemy
-global function OnEnemyChanged_TryHeavyArmorWeapon
-global function ResetNPCs
-global function IsValidRocketTarget
-global function GetMilitiaTitle
-
-global function AssaultOrigin
-global function SquadAssaultOrigin
-
-global function ClientCommand_SpawnViewGrunt
-
-global function OnSoldierSeeEnemy
-global function TryFriendlyPassingNearby
-
-global function OnSpectreSeeEnemy
-
-global function onlyimc // debug
-global function onlymilitia // debug
-
-global function SetGlobalNPCHealth //debug
-
-
-//=========================================================
-// MP ai soldier
-//
-//=========================================================
-
-struct
-{
- int militiaTitlesIndex
- array<string> militiaTitles
-} file
-
-function AiSoldiers_Init()
-{
- level.COOP_AT_WEAPON_RATES <- {}
- level.COOP_AT_WEAPON_RATES[ "mp_weapon_rocket_launcher" ] <- 0.5
- level.COOP_AT_WEAPON_RATES[ "mp_weapon_smr" ] <- 0.4
- level.COOP_AT_WEAPON_RATES[ "mp_weapon_mgl" ] <- 0.1
-
- PrecacheSprite( $"sprites/glow_05.vmt" )
- FlagInit( "disable_npcs" )
- FlagInit( "Disable_IMC" )
- FlagInit( "Disable_MILITIA" )
-
- level.onlySpawn <- null
-
- level.spectreSpawnStyle <- eSpectreSpawnStyle.MORE_FOR_ENEMY_TITANS
-
- FlagInit( "AllSpectre" )
- FlagInit( "AllSpectreIMC" )
- FlagInit( "AllSpectreMilitia" )
- FlagInit( "NoSpectreIMC" )
- FlagInit( "NoSpectreMilitia" )
-
- RegisterSignal( "OnSendAIToAssaultPoint" )
-
- InitMilitiaTitles()
-
- AddCallback_OnClientConnecting( AiSoldiers_InitPlayer )
-
- if ( GetDeveloperLevel() > 0 )
- AddClientCommandCallback( "SpawnViewGrunt", ClientCommand_SpawnViewGrunt )
-
-}
-
-bool function ClientCommand_SpawnViewGrunt( entity player, array<string> args )
-{
- int team = args[0].tointeger()
- if ( GetDeveloperLevel() < 1 )
- return true
-
- vector origin = player.EyePosition()
- vector angles = player.EyeAngles()
- vector forward = AnglesToForward( angles )
- TraceResults result = TraceLine( origin, origin + forward * 2000, player )
- angles.x = 0
- angles.z = 0
-
- entity guy = CreateSoldier( team, result.endPos, angles )
- DispatchSpawn( guy )
- return true
-}
-
-// debug commands
-function onlyimc()
-{
- level.onlySpawn = TEAM_IMC
- printt( "Only spawning IMC AI" )
-}
-
-// debug commands
-function onlymilitia()
-{
- level.onlySpawn = TEAM_MILITIA
- printt( "Only spawning Militia AI" )
-}
-
-//////////////////////////////////////////////////////////
-void function AiSoldiers_InitPlayer( entity player )
-{
- player.s.next_ai_callout_time <- -1
-
- string squadName = GetPlayerSpectreSquadName( player )
- player.p.spectreSquad = squadName
-}
-
-//////////////////////////////////////////////////////////
-string function MakeSquadName( int team, string msg )
-{
- string teamStr
-
- if ( team == TEAM_IMC )
- teamStr = "imc"
- else if ( team == TEAM_MILITIA )
- teamStr = "militia"
- else
- teamStr = "default"
-
- return "squad_" + teamStr + msg
-}
-
-//////////////////////////////////////////////////////////
-
-
-//////////////////////////////////////////////////////////
-// common init for grunts and spectres
-void function CommonMinionInit( entity npc )
-{
- RandomizeHead( npc )
-
- if ( IsMultiplayer() )
- {
- npc.kv.alwaysAlert = 1
- npc.EnableNPCFlag( NPC_STAY_CLOSE_TO_SQUAD | NPC_NEW_ENEMY_FROM_SOUND )
- npc.DisableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE )
- }
-
- npc.s.cpState <- eNPCStateCP.NONE
-
- if ( npc.kv.alwaysalert.tointeger() == 1 )
- npc.SetDefaultSchedule( "SCHED_ALERT_SCAN" )
-}
-
-function SetupMinionForRPGs( entity soldier )
-{
- soldier.SetEnemyChangeCallback( OnEnemyChanged_MinionSwitchToHeavyArmorWeapon )
-}
-
-
-void function OnSoldierSeeEnemy( entity guy )
-{
- guy.EndSignal( "OnDeath" )
-
- if ( NPC_GruntChatterSPEnabled( guy ) )
- return
-
- while ( true )
- {
- var results = WaitSignal( guy, "OnSeeEnemy" )
-
- if ( !IsValid( guy ) )
- return
-
- TrySpottedCallout( guy, expect entity( results.activator ) )
- }
-}
-
-void function TryFriendlyPassingNearby( entity grunt )
-{
- grunt.EndSignal( "OnDeath" )
-
- if ( NPC_GruntChatterSPEnabled( grunt ) )
- return
-
- while ( true )
- {
- wait 5
-
- if ( !IsValid( grunt ) )
- return
-
- #if GRUNT_CHATTER_MP_ENABLED
- // only do this a minute into the match
- if ( Time() > 60.0 && TryFriendlyCallout( grunt, "pilot", "bc_reactFriendlyPilot" , 500 ) )
- continue
- if ( TryFriendlyCallout( grunt, "titan", "bc_reactTitanfallFriendlyArrives" , 500 ) )
- continue
- if ( TryFriendlyCallout( grunt, "npc_super_spectre", "bc_reactReaperFriendlyArrives" , 500 ) )
- continue
- if ( TryFriendlyCallout( grunt, "npc_frag_drone", "bc_reactTickSpawnFriendly" , 500 ) )
- continue
- if ( IsAlive( grunt.GetEnemy() ) )
- {
- entity enemy = grunt.GetEnemy()
- if ( enemy.IsTitan() )
- PlayGruntChatterMPLine( grunt, "bc_generalCombatTitan" )
- else
- PlayGruntChatterMPLine( grunt, "bc_generalCombat" )
- }
- else
- {
- PlayGruntChatterMPLine( grunt, "bc_generalNonCombat" )
- }
- #endif
- }
-}
-
-#if GRUNT_CHATTER_MP_ENABLED
-bool function TryFriendlyCallout( entity grunt, string npcClassname, string callout, float dist )
-{
- array<entity> nearbyFriendlies
- float distSq = dist*dist
- if ( npcClassname == "pilot" )
- {
- array<entity> players = GetPlayerArrayOfTeam_AlivePilots( grunt.GetTeam() )
- foreach( p in players )
- {
- if ( DistanceSqr( p.GetOrigin(), grunt.GetOrigin() ) > distSq )
- continue
- nearbyFriendlies.append( p )
- }
- }
- else if ( npcClassname == "titan" )
- {
- nearbyFriendlies = GetNPCArrayEx( "npc_titan", grunt.GetTeam(), TEAM_ANY, grunt.GetOrigin(), dist )
- array<entity> players = GetPlayerArrayOfTeam_Alive( grunt.GetTeam() )
- foreach( p in players )
- {
- if ( !p.IsTitan() )
- continue
- if ( DistanceSqr( p.GetOrigin(), grunt.GetOrigin() ) > distSq )
- continue
- nearbyFriendlies.append( p )
- }
- }
- else
- {
- nearbyFriendlies = GetNPCArrayEx( npcClassname, grunt.GetTeam(), TEAM_ANY, grunt.GetOrigin(), dist )
- }
-
- foreach ( friendly in nearbyFriendlies )
- {
- if ( !IsAlive( friendly ) )
- continue
-
- if ( GetDoomedState( friendly ) )
- continue
-
- PlayGruntChatterMPLine( grunt, callout )
- return true
- }
-
- return false
-}
-#endif
-
-void function OnSpectreSeeEnemy( entity guy )
-{
- guy.EndSignal( "OnDeath" )
-
- while ( true )
- {
- var results = WaitSignal( guy, "OnGainEnemyLOS" )
-
- TrySpottedCallout( guy, expect entity( results.activator ) )
- }
-}
-
-
-//////////////////////////////////////////////////////////
-bool function IsValidRocketTarget( entity enemy )
-{
- return enemy.GetArmorType() == ARMOR_TYPE_HEAVY
-}
-
-//////////////////////////////////////////////////////////
-function DisableMinionUsesHeavyWeapons( entity soldier )
-{
- soldier.SetEnemyChangeCallback( OnEnemyChanged_MinionUpdateAimSettingsForEnemy )
-}
-
-void function OnEnemyChanged_MinionSwitchToHeavyArmorWeapon( entity soldier )
-{
- OnEnemyChanged_TryHeavyArmorWeapon( soldier )
- OnEnemyChanged_MinionUpdateAimSettingsForEnemy( soldier )
-}
-
-//////////////////////////////////////////////////////////
-void function OnEnemyChanged_MinionUpdateAimSettingsForEnemy( entity soldier )
-{
- SetProficiency( soldier )
-}
-
-
-bool function AssignNPCAppropriateWeaponFromWeapons( entity npc, array<entity> weapons, bool isRocketTarget )
-{
- // first try to find an appropriate weapon
- foreach ( weapon in weapons )
- {
- bool isAntiTitan = weapon.GetWeaponType() == WT_ANTITITAN
- if ( isAntiTitan == isRocketTarget )
- {
- // found a weapon to use
- npc.SetActiveWeaponByName( weapon.GetWeaponClassName() )
- return true
- }
- }
- return false
-}
-
-//////////////////////////////////////////////////////////
-void function OnEnemyChanged_TryHeavyArmorWeapon( entity npc )
-{
- entity enemy = npc.GetEnemy()
- if ( !IsAlive( enemy ) )
- return
-
- array<entity> weapons = npc.GetMainWeapons()
-
- // do we have a weapon to switch to?
- if ( !weapons.len() )
- return
-
- entity activeWeapon = npc.GetActiveWeapon()
- bool isRocketTarget = IsValidRocketTarget( enemy )
-
- if ( activeWeapon == null )
- {
- if ( AssignNPCAppropriateWeaponFromWeapons( npc, weapons, isRocketTarget ) )
- return
-
- // if that fails, use the first weapon, so we do consistent behavior
- npc.SetActiveWeaponByName( weapons[0].GetWeaponClassName() )
- return
- }
-
- bool isActiveWeapon_AntiTitan = activeWeapon.GetWeaponType() == WT_ANTITITAN
-
- // already using an appropriate weapon?
- if ( isActiveWeapon_AntiTitan == isRocketTarget )
- return
-
- AssignNPCAppropriateWeaponFromWeapons( npc, weapons, isRocketTarget )
-}
-
-const float NPC_CLOSE_DISTANCE_SQR_THRESHOLD = 1000.0 * 1000.0
-
-//////////////////////////////////////////////////////////
-void function TrySpottedCallout( entity guy, entity enemy )
-{
- if ( !IsAlive( guy ) )
- return
-
- if ( !IsAlive( enemy ) )
- return
-
- float distanceSqr = DistanceSqr( guy.GetOrigin(), enemy.GetOrigin() )
- bool isClose = distanceSqr <= NPC_CLOSE_DISTANCE_SQR_THRESHOLD
-
- if ( enemy.IsTitan() )
- {
- if ( IsSpectre( guy ) ) //Spectre callouts
- {
- #if SPECTRE_CHATTER_MP_ENABLED
- PlaySpectreChatterMPLine( guy, "diag_imc_spectre_gs_spotclosetitancall_01" )
- #else
- if ( isClose )
- PlaySpectreChatterToAll( "spectre_gs_spotclosetitancall_01", guy )
- else
- PlaySpectreChatterToAll( "spectre_gs_spotfartitan_1_1", guy )
- #endif
-
- }
- else //Grunt callouts
- {
- #if GRUNT_CHATTER_MP_ENABLED
- PlayGruntChatterMPLine( guy, "bc_enemytitanspotcall" )
- #endif
- }
- }
- else if ( enemy.IsPlayer() )
- {
- if ( IsSpectre( guy ) ) //Spectre callouts
- {
- #if SPECTRE_CHATTER_MP_ENABLED
- PlaySpectreChatterMPLine( guy, "diag_imc_spectre_gs_engagepilotenemy_01_1" )
- #else
- if ( isClose )
- PlaySpectreChatterToAll( "spectre_gs_engagepilotenemy_01_1", guy )
- else
- PlaySpectreChatterToAll( "spectre_gs_spotenemypilot_01_1", guy )
- #endif
- }
- else //Grunt callouts
- {
- #if GRUNT_CHATTER_MP_ENABLED
- if ( isClose )
- PlayGruntChatterMPLine( guy, "bc_spotenemypilot" )
- else
- PlayGruntChatterMPLine( guy, "bc_engagepilotenemy" )
- #endif
- }
- }
- else if ( IsSuperSpectre( enemy ) )
- {
- if ( !IsSpectre( guy ) ) //Spectre callouts
- {
- #if GRUNT_CHATTER_MP_ENABLED
- PlayGruntChatterMPLine( guy, "bc_reactEnemyReaper" )
- #endif
- }
- }
- else
- {
- if ( !IsSpectre( guy ) ) //Spectre callouts
- {
- #if GRUNT_CHATTER_MP_ENABLED
- PlayGruntChatterMPLine( guy, "bc_reactEnemySpotted" )
- #endif
- }
- }
-}
-
-
-//////////////////////////////////////////////////////////
-string function GetPlayerSpectreSquadName( entity player )
-{
- return "player" + player.entindex() + "spectreSquad"
-}
-
-
-//////////////////////////////////////////////////////////
-
-string function GetMilitiaTitle()
-{
- file.militiaTitlesIndex++
- if ( file.militiaTitlesIndex >= file.militiaTitles.len() )
- file.militiaTitlesIndex = 0
-
- return file.militiaTitles[ file.militiaTitlesIndex ]
-}
-
-void function InitMilitiaTitles()
-{
- file.militiaTitles = [
- "#NPC_MILITIA_NAME_AND_RANK_0",
- "#NPC_MILITIA_NAME_AND_RANK_1",
- "#NPC_MILITIA_NAME_AND_RANK_2",
- "#NPC_MILITIA_NAME_AND_RANK_3",
- "#NPC_MILITIA_NAME_AND_RANK_4",
- "#NPC_MILITIA_NAME_AND_RANK_5",
- "#NPC_MILITIA_NAME_AND_RANK_6",
- "#NPC_MILITIA_NAME_AND_RANK_7",
- "#NPC_MILITIA_NAME_AND_RANK_8",
- "#NPC_MILITIA_NAME_AND_RANK_9",
- "#NPC_MILITIA_NAME_AND_RANK_10",
- "#NPC_MILITIA_NAME_AND_RANK_11",
- "#NPC_MILITIA_NAME_AND_RANK_12",
- "#NPC_MILITIA_NAME_AND_RANK_13",
- "#NPC_MILITIA_NAME_AND_RANK_14",
- "#NPC_MILITIA_NAME_AND_RANK_15",
- "#NPC_MILITIA_NAME_AND_RANK_16",
- "#NPC_MILITIA_NAME_AND_RANK_17",
- "#NPC_MILITIA_NAME_AND_RANK_18",
- "#NPC_MILITIA_NAME_AND_RANK_19",
- "#NPC_MILITIA_NAME_AND_RANK_20",
- "#NPC_MILITIA_NAME_AND_RANK_21",
- "#NPC_MILITIA_NAME_AND_RANK_22",
- "#NPC_MILITIA_NAME_AND_RANK_23",
- "#NPC_MILITIA_NAME_AND_RANK_24",
- "#NPC_MILITIA_NAME_AND_RANK_25",
- "#NPC_MILITIA_NAME_AND_RANK_26",
- "#NPC_MILITIA_NAME_AND_RANK_27",
- "#NPC_MILITIA_NAME_AND_RANK_28",
- "#NPC_MILITIA_NAME_AND_RANK_29",
- "#NPC_MILITIA_NAME_AND_RANK_30",
- "#NPC_MILITIA_NAME_AND_RANK_31",
- "#NPC_MILITIA_NAME_AND_RANK_32",
- "#NPC_MILITIA_NAME_AND_RANK_33",
- "#NPC_MILITIA_NAME_AND_RANK_34",
- "#NPC_MILITIA_NAME_AND_RANK_35",
- "#NPC_MILITIA_NAME_AND_RANK_36",
- "#NPC_MILITIA_NAME_AND_RANK_37",
- "#NPC_MILITIA_NAME_AND_RANK_38",
- "#NPC_MILITIA_NAME_AND_RANK_39",
- "#NPC_MILITIA_NAME_AND_RANK_40",
- "#NPC_MILITIA_NAME_AND_RANK_41",
- "#NPC_MILITIA_NAME_AND_RANK_42",
- "#NPC_MILITIA_NAME_AND_RANK_43",
- "#NPC_MILITIA_NAME_AND_RANK_44",
- "#NPC_MILITIA_NAME_AND_RANK_45",
- "#NPC_MILITIA_NAME_AND_RANK_46",
- "#NPC_MILITIA_NAME_AND_RANK_47",
- "#NPC_MILITIA_NAME_AND_RANK_48",
- "#NPC_MILITIA_NAME_AND_RANK_49",
- "#NPC_MILITIA_NAME_AND_RANK_50",
- "#NPC_MILITIA_NAME_AND_RANK_51",
- "#NPC_MILITIA_NAME_AND_RANK_52",
- "#NPC_MILITIA_NAME_AND_RANK_53",
- "#NPC_MILITIA_NAME_AND_RANK_54",
- "#NPC_MILITIA_NAME_AND_RANK_55",
- "#NPC_MILITIA_NAME_AND_RANK_56",
- "#NPC_MILITIA_NAME_AND_RANK_57",
- "#NPC_MILITIA_NAME_AND_RANK_58",
- "#NPC_MILITIA_NAME_AND_RANK_59",
- "#NPC_MILITIA_NAME_AND_RANK_60",
- "#NPC_MILITIA_NAME_AND_RANK_61",
- "#NPC_MILITIA_NAME_AND_RANK_62",
- "#NPC_MILITIA_NAME_AND_RANK_63",
- "#NPC_MILITIA_NAME_AND_RANK_64",
- "#NPC_MILITIA_NAME_AND_RANK_65",
- "#NPC_MILITIA_NAME_AND_RANK_66",
- "#NPC_MILITIA_NAME_AND_RANK_67",
- "#NPC_MILITIA_NAME_AND_RANK_68",
- "#NPC_MILITIA_NAME_AND_RANK_69",
- "#NPC_MILITIA_NAME_AND_RANK_70",
- "#NPC_MILITIA_NAME_AND_RANK_71",
- "#NPC_MILITIA_NAME_AND_RANK_72",
- "#NPC_MILITIA_NAME_AND_RANK_73",
- "#NPC_MILITIA_NAME_AND_RANK_74",
- "#NPC_MILITIA_NAME_AND_RANK_75",
- "#NPC_MILITIA_NAME_AND_RANK_76",
- "#NPC_MILITIA_NAME_AND_RANK_77",
- "#NPC_MILITIA_NAME_AND_RANK_78",
- "#NPC_MILITIA_NAME_AND_RANK_79",
- "#NPC_MILITIA_NAME_AND_RANK_80",
- "#NPC_MILITIA_NAME_AND_RANK_81",
- "#NPC_MILITIA_NAME_AND_RANK_82",
- "#NPC_MILITIA_NAME_AND_RANK_83",
- "#NPC_MILITIA_NAME_AND_RANK_84",
- "#NPC_MILITIA_NAME_AND_RANK_85",
- "#NPC_MILITIA_NAME_AND_RANK_86",
- "#NPC_MILITIA_NAME_AND_RANK_87",
- "#NPC_MILITIA_NAME_AND_RANK_88",
- "#NPC_MILITIA_NAME_AND_RANK_89",
- "#NPC_MILITIA_NAME_AND_RANK_90",
- "#NPC_MILITIA_NAME_AND_RANK_91",
- "#NPC_MILITIA_NAME_AND_RANK_92",
- "#NPC_MILITIA_NAME_AND_RANK_93",
- "#NPC_MILITIA_NAME_AND_RANK_94",
- "#NPC_MILITIA_NAME_AND_RANK_95"
- "#NPC_MILITIA_NAME_AND_RANK_96",
- "#NPC_MILITIA_NAME_AND_RANK_97",
- "#NPC_MILITIA_NAME_AND_RANK_98",
- "#NPC_MILITIA_NAME_AND_RANK_99"
- ]
-
- file.militiaTitles.randomize()
- file.militiaTitlesIndex = 0
-}
-
-//////////////////////////////////////////////////////////
-function disable_npcs()
-{
- FlagSet( "disable_npcs" )
- printl( "disabling_npcs" )
- array<entity> guys = GetNPCArray()
- foreach ( guy in guys )
- {
- if ( guy.GetClassName() == "npc_turret_mega" )
- continue
- if ( guy.GetClassName() == "npc_turret_sentry" )
- continue
- if ( guy.GetClassName() == "npc_titan" )
- continue
-
- guy.Destroy()
- }
-}
-//////////////////////////////////////////////////////////
-// //hack - we want to toggle new AI on and off through the dev menu even though playlist defaults to use them all the time
-function disable_new_npcs()
-{
- array<entity> guys = GetNPCArray()
- foreach ( guy in guys )
- {
- if ( guy.GetClassName() == "npc_turret_mega" )
- continue
- if ( guy.GetClassName() == "npc_turret_sentry" )
- continue
- if ( guy.GetClassName() == "npc_titan" )
- continue
-
- guy.Destroy()
- }
-}
-
-function ResetNPCs()
-{
- array<entity> guys = GetNPCArray()
- foreach ( guy in guys )
- {
- if ( guy.GetClassName() == "npc_turret_mega" )
- continue
- if ( guy.GetClassName() == "npc_turret_sentry" )
- continue
-
- if ( guy.GetClassName() == "npc_titan" && IsValid( guy.GetTitanSoul() ) )
- {
- guy.GetTitanSoul().Destroy()
- }
-
- guy.Destroy()
- }
-}
-
-//////////////////////////////////////////////////////////
-function Disable_IMC()
-{
- DisableAutoPopulate( TEAM_IMC )
- printl( "Disable_IMC" )
- array<entity> guys = GetNPCArray()
- foreach ( guy in guys )
- {
- if ( guy.GetTeam() == TEAM_IMC )
- guy.Kill_Deprecated_UseDestroyInstead()
- }
-}
-
-
-//////////////////////////////////////////////////////////
-function Disable_MILITIA()
-{
- DisableAutoPopulate( TEAM_MILITIA )
- printl( "Disable_MILITIA" )
- array<entity> guys = GetNPCArray()
- foreach ( guy in guys )
- {
- if ( guy.GetTeam() == TEAM_MILITIA )
- guy.Kill_Deprecated_UseDestroyInstead()
- }
-}
-
-//////////////////////////////////////////////////////////
-function IsNPCSpawningEnabled()
-{
- if ( Riff_AllowNPCs() != eAllowNPCs.Default )
- {
- if ( Riff_AllowNPCs() == eAllowNPCs.None )
- return false
-
- return true
- }
-
- return true
-}
-
-
-function DisableAutoPopulate( team )
-{
- switch ( team )
- {
- case TEAM_IMC:
- FlagSet( "Disable_IMC" )
- break
-
- case TEAM_MILITIA:
- FlagSet( "Disable_MILITIA" )
- break
-
- default:
- Assert( 0, "team number " + team + " not setup for autoPopulation.")
- break
- }
-}
-
-function EnableAutoPopulate( team )
-{
- switch ( team )
- {
- case TEAM_IMC:
- FlagClear( "Disable_IMC" )
- break
-
- case TEAM_MILITIA:
- FlagClear( "Disable_MILITIA" )
- break
-
- default:
- Assert( 0, "team number " + team + " not setup for autoPopulation.")
- break
- }
-}
-
-//////////////////////////////////////////////////////////
-
-
-function GuyTeleportsOnPathFail( guy, origin )
-{
- expect entity( guy )
-
- guy.EndSignal( "OnFailedToPath" )
-
- local e = {}
- e.waited <- false
- OnThreadEnd(
- function() : ( guy, origin, e )
- {
- if ( !IsAlive( guy ) )
- return
-
- // wait was cut off
- if ( !e.waited )
- guy.SetOrigin( origin )
- }
- )
-
- wait 2
- e.waited = true
-}
-
-void function SquadAssaultOrigin( array<entity> group, vector origin, float radius = STANDARDGOALRADIUS )
-{
- foreach ( member in group )
- {
- thread AssaultOrigin( member, origin, radius )
- }
-}
-
-void function AssaultOrigin( entity guy, vector origin, float radius = STANDARDGOALRADIUS )
-{
- waitthread SendAIToAssaultPoint( guy, origin, <0,0,0>, radius )
-}
-
-void function SendAIToAssaultPoint( entity guy, vector origin, vector angles, float radius = STANDARDGOALRADIUS )
-{
- Assert( IsAlive( guy ) )
- guy.Signal( "OnSendAIToAssaultPoint" )
- guy.Anim_Stop() // in case we were doing an anim already
- guy.EndSignal( "OnDeath" )
- guy.EndSignal( "OnSendAIToAssaultPoint" )
-
- bool allowFlee = guy.GetNPCFlag( NPC_ALLOW_FLEE )
- bool allowHandSignal = guy.GetNPCFlag( NPC_ALLOW_HAND_SIGNALS )
-
- OnThreadEnd(
- function() : ( guy, allowFlee, allowHandSignal )
- {
- if ( IsAlive( guy ) )
- {
- guy.SetNPCFlag( NPC_ALLOW_FLEE, allowFlee )
- guy.SetNPCFlag( NPC_ALLOW_HAND_SIGNALS, allowHandSignal )
- }
- }
- )
-
- guy.DisableNPCFlag( NPC_ALLOW_FLEE | NPC_ALLOW_HAND_SIGNALS )
- guy.AssaultPoint( origin )
- guy.AssaultSetGoalRadius( radius )
- guy.WaitSignal( "OnFinishedAssault" )
-
-}
-
-function SetGlobalNPCHealth( healthValue ) //Debug, for trailer team
-{
- array<entity> npcArray = GetNPCArray()
-
- foreach ( npc in npcArray )
- {
- npc.SetMaxHealth( healthValue )
- npc.SetHealth( healthValue )
- }
-}
-
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers_mp.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers_mp.gnut
deleted file mode 100644
index 37b891699..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers_mp.gnut
+++ /dev/null
@@ -1 +0,0 @@
-//fuck \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn.gnut
deleted file mode 100644
index 7e4d2cddf..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn.gnut
+++ /dev/null
@@ -1,696 +0,0 @@
-untyped
-
-global function AiSpawn_Init
-
-global function __GetWeaponModel
-global function AssaultLinkedMoveTarget
-global function AssaultMoveTarget
-global function AutoSquadnameAssignment
-global function CreateArcTitan
-global function CreateAtlas
-global function CreateElitePilot
-global function CreateElitePilotAssassin
-global function CreateFragDrone
-global function CreateFragDroneCan
-global function CreateGenericDrone
-global function CreateGunship
-global function CreateHenchTitan
-global function CreateMarvin
-global function CreateNPC
-global function CreateNPCFromAISettings
-global function CreateNPCTitan
-global function CreateOgre
-global function CreateProwler
-global function CreateRocketDrone
-global function CreateRocketDroneGrunt
-global function CreateShieldDrone
-global function CreateShieldDroneGrunt
-global function CreateSoldier
-global function CreateSpectre
-global function CreateStalker
-global function CreateStryder
-global function CreateSuperSpectre
-global function CreateWorkerDrone
-global function CreateZombieStalker
-global function CreateZombieStalkerMossy
-global function StopAssaultMoveTarget
-
-global const HACK_CAP_BACK1 = $"models/sandtrap/sandtrap_wall_bracket.mdl"
-global const HACK_CAP_BACK2 = $"models/pipes/pipe_modular_grey_bracket_cap.mdl"
-global const HACK_CAP_BACK3 = $"models/lamps/office_lights_hanging_wire.mdl"
-global const HACK_DRONE_BACK1 = $"models/Weapons/ammoboxes/backpack_single.mdl"
-global const HACK_DRONE_BACK2 = $"models/barriers/fence_wire_holder_double.mdl"
-global const DEFAULT_TETHER_RADIUS = 1500
-global const DEFAULT_COVER_BEHAVIOR_CYLINDER_HEIGHT = 512
-
-struct
-{
- array<string> moveTargetClasses
-} file
-
-void function AiSpawn_Init()
-{
- PrecacheModel( HACK_CAP_BACK1 )
- PrecacheModel( HACK_CAP_BACK2 )
- PrecacheModel( HACK_CAP_BACK3 )
- PrecacheModel( HACK_DRONE_BACK1 )
- PrecacheModel( HACK_DRONE_BACK2 )
- PrecacheModel( TEAM_IMC_GRUNT_MODEL )
- PrecacheModel( TEAM_IMC_GRUNT_MODEL_LMG )
- PrecacheModel( TEAM_IMC_GRUNT_MODEL_RIFLE )
- PrecacheModel( TEAM_IMC_GRUNT_MODEL_ROCKET )
- PrecacheModel( TEAM_IMC_GRUNT_MODEL_SHOTGUN )
- PrecacheModel( TEAM_IMC_GRUNT_MODEL_SMG )
-
- PrecacheModel( TEAM_MIL_GRUNT_MODEL )
- PrecacheModel( TEAM_MIL_GRUNT_MODEL_LMG )
- PrecacheModel( TEAM_MIL_GRUNT_MODEL_RIFLE )
- PrecacheModel( TEAM_MIL_GRUNT_MODEL_ROCKET )
- PrecacheModel( TEAM_MIL_GRUNT_MODEL_SHOTGUN )
- PrecacheModel( TEAM_MIL_GRUNT_MODEL_SMG )
-
- file.moveTargetClasses = [ "info_move_target", "info_move_animation" ]
- foreach ( movetargetClass in file.moveTargetClasses )
- {
- AddSpawnCallbackEditorClass( "info_target", movetargetClass, InitInfoMoveTargetFlags )
- }
-
- RegisterSignal( "StopAssaultMoveTarget" )
- RegisterSignal( "OnFinishedAssaultChain" )
-
- AiSpawnContent_Init()
- #if DEV
- // just to insure that ai settings are being setup properly.
- InitNpcSettingsFileNamesForDevMenu()
- SetupSpawnAIButtons( TEAM_MILITIA )
- AddCallback_EntitiesDidLoad( AiSpawn_EntitiesDidLoad )
- #endif
-}
-
-void function AiSpawn_EntitiesDidLoad()
-{
- #if DEV
- // On load in dev, verify that subclass matches leveled_aisettings. Subclass is being eradicated.
- foreach ( spawner in GetSpawnerArrayByClassName( "npc_titan" ) )
- {
- table spawnerKeyValues = spawner.GetSpawnEntityKeyValues()
- if ( "model" in spawnerKeyValues )
- {
- switch ( spawnerKeyValues.model.tolower() )
- {
- case "models/titans/atlas/atlas_titan.mdl":
- case "models/titans/ogre/ogre_titan.mdl":
- case "models/titans/stryder/stryder_titan.mdl":
- CodeWarning( "Titan has deprecated model at " + spawnerKeyValues.origin )
- break
- }
- }
- }
-
- foreach ( model in GetEntArrayByClass_Expensive( "prop_dynamic" ) )
- {
- switch ( model.GetModelName() )
- {
- case $"models/titans/atlas/atlas_titan.mdl":
- case $"models/titans/ogre/ogre_titan.mdl":
- case $"models/titans/stryder/stryder_titan.mdl":
- CodeWarning( "Prop has deprecated model at " + model.GetOrigin() )
- break
- }
- }
-
- if ( IsSingleplayer() )
- {
- foreach ( spawner in GetSpawnerArrayByClassName( "npc_titan" ) )
- {
- table kvs = spawner.GetSpawnEntityKeyValues()
- vector origin = StringToVector( expect string( kvs.origin ) )
- if ( !( "leveled_aisettings" in kvs ) )
- {
- CodeWarning( "Titan Spawner at " + origin + " has no leveled_aisettings" )
- continue
- }
-
- string aiSettings = expect string( kvs.leveled_aisettings )
- string playerSettings = expect string( Dev_GetAISettingByKeyField_Global( aiSettings, "npc_titan_player_settings" ) )
- string playerModel = expect string( GetPlayerSettingsFieldForClassName( playerSettings, "bodymodel" ) )
- string npcModel = expect string( kvs.model )
- if ( npcModel != playerModel )
- CodeWarning( "Titan spawner at " + origin + " has model " + npcModel + " that does not match player settings model " + playerModel )
- }
- }
-
- #endif
-
- table<entity, bool> foundSpawners
- // precache weapons from the AI
- foreach ( aiSettings in GetAllNPCSettings() )
- {
- // any of these spawned in the level?
- string baseClass = expect string( Dev_GetAISettingByKeyField_Global( aiSettings, "BaseClass" ) )
- array<entity> spawners = GetSpawnerArrayByClassName( baseClass )
-
- foreach ( spawner in spawners )
- {
- if ( spawner in foundSpawners )
- continue
- foundSpawners[ spawner ] <- true
- // this may be set on the entity in leveled
- table kvs = spawner.GetSpawnEntityKeyValues()
- if ( !( "subclass" in kvs ) )
- continue
-
- string origin = expect string( spawner.GetSpawnEntityKeyValues().origin )
- string subclass = expect string( spawner.GetSpawnEntityKeyValues().subclass )
- CodeWarning( "NPC spawner at " + origin + " has subclass " + subclass + ". Replace deprecated subclass key with leveled_aisettings." )
- }
- }
-
-}
-
-const ESCALATION_INCOMBAT_TIMEOUT = 180
-const ESCALATION_FRACTION_DEAD = 0.5
-
-
-/************************************************************************************************\
-
-######## #### ######## ### ## ##
- ## ## ## ## ## ### ##
- ## ## ## ## ## #### ##
- ## ## ## ## ## ## ## ##
- ## ## ## ######### ## ####
- ## ## ## ## ## ## ###
- ## #### ## ## ## ## ##
-
-\************************************************************************************************/
-
-//////////////////////////////////////////////////////////
-
-entity function CreateHenchTitan( string titanType, vector origin, vector angles )
-{
- entity npc = CreateNPCTitan( titanType, TEAM_IMC, origin, angles, [] )
- string settings = expect string( Dev_GetPlayerSettingByKeyField_Global( titanType, "sp_aiSettingsFile" ) )
- SetSpawnOption_AISettings( npc, settings )
- SetSpawnOption_Titanfall( npc )
- SetSpawnOption_Alert( npc )
- SetSpawnOption_NPCTitan( npc, TITAN_HENCH )
- npc.ai.titanSpawnLoadout.setFile = titanType
- OverwriteLoadoutWithDefaultsForSetFile( npc.ai.titanSpawnLoadout )
- return npc
-}
-
-entity function CreateAtlas( int team, vector origin, vector angles, array<string> settingsMods = [] )
-{
- entity npc = CreateNPCTitan( "titan_atlas", team, origin, angles, settingsMods )
- SetSpawnOption_AISettings( npc, "npc_titan_atlas" )
- return npc
-}
-
-entity function CreateStryder( int team, vector origin, vector angles, array<string> settingsMods = [] )
-{
- entity npc = CreateNPCTitan( "titan_stryder", team, origin, angles, settingsMods )
- SetSpawnOption_AISettings( npc, "npc_titan_stryder" )
- return npc
-}
-
-entity function CreateOgre( int team, vector origin, vector angles, array<string> settingsMods = [] )
-{
- entity npc = CreateNPCTitan( "titan_ogre", team, origin, angles, settingsMods )
- SetSpawnOption_AISettings( npc, "npc_titan_ogre" )
- return npc
-}
-
-entity function CreateArcTitan( int team, vector origin, vector angles, array<string> settingsMods = [] )
-{
- entity npc = CreateNPCTitan( "titan_stryder", team, origin, angles, settingsMods )
- SetSpawnOption_AISettings( npc, "npc_titan_arc" )
- return npc
-}
-
-entity function CreateNPCTitan( string settings, int team, vector origin, vector angles, array<string> settingsMods = [] )
-{
- entity npc = CreateEntity( "npc_titan" )
- npc.kv.origin = origin
- npc.kv.angles = Vector( 0, angles.y, 0 )
- npc.kv.teamnumber = team
- SetTitanSettings( npc.ai.titanSettings, settings, settingsMods )
- return npc
-}
-
-entity function CreateSpectre( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_spectre", team, origin, angles )
-}
-
-entity function CreateStalker( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_stalker", team, origin, angles )
-}
-
-entity function CreateZombieStalker( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_stalker", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_stalker_zombie" )
- return npc
-}
-
-entity function CreateZombieStalkerMossy( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_stalker", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_stalker_zombie_mossy" )
- return npc
-}
-
-entity function CreateSuperSpectre( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_super_spectre", team, origin, angles )
-}
-
-entity function CreateGunship( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_gunship", team, origin, angles )
-}
-
-entity function CreateSoldier( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_soldier", team, origin, angles )
-}
-
-entity function CreateProwler( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_prowler", team, origin, angles )
-}
-
-entity function CreateRocketDroneGrunt( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_soldier", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_soldier_drone_summoner_rocket" )
- return npc
-}
-
-entity function CreateShieldDroneGrunt( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_soldier", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_soldier_drone_summoner" )
- return npc
-}
-
-entity function CreateElitePilot( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_pilot_elite", team, origin, angles )
-}
-
-entity function CreateElitePilotAssassin( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_pilot_elite", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_pilot_elite_assassin" )
- return npc
-}
-
-entity function CreateFragDrone( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_frag_drone", team, origin, angles )
-}
-
-entity function CreateFragDroneCan( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_frag_drone", team, origin, angles )
- npc.ai.fragDroneArmed = false
- return npc
-}
-
-entity function CreateRocketDrone( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_drone", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_drone_rocket" )
- return npc
-}
-
-entity function CreateShieldDrone( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_drone", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_drone_shield" )
- return npc
-}
-
-entity function CreateGenericDrone( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_drone", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_drone" )
- return npc
-}
-
-entity function CreateWorkerDrone( int team, vector origin, vector angles )
-{
- entity npc = CreateNPC( "npc_drone", team, origin, angles )
- SetSpawnOption_AISettings( npc, "npc_drone_worker" )
- return npc
-}
-
-entity function CreateMarvin( int team, vector origin, vector angles )
-{
- return CreateNPC( "npc_marvin", team, origin, angles )
-}
-
-entity function CreateNPC( baseClass, team, origin, angles )
-{
- entity npc = CreateEntity( expect string( baseClass ) )
- npc.kv.teamnumber = team
- npc.kv.origin = origin
- npc.kv.angles = angles
-
- return npc
-}
-
-entity function CreateNPCFromAISettings( string aiSettings, int team, vector origin, vector angles )
-{
- string baseClass = expect string( Dev_GetAISettingByKeyField_Global( aiSettings, "BaseClass" ) )
- entity npc = CreateNPC( baseClass, team, origin, angles )
- SetSpawnOption_AISettings( npc, aiSettings )
- return npc
-}
-
-
-
-/************************************************************************************************\
-
- ###### ####### ## ## ## ## ####### ## ##
-## ## ## ## ### ### ### ### ## ## ### ##
-## ## ## #### #### #### #### ## ## #### ##
-## ## ## ## ### ## ## ### ## ## ## ## ## ##
-## ## ## ## ## ## ## ## ## ## ####
-## ## ## ## ## ## ## ## ## ## ## ###
- ###### ####### ## ## ## ## ####### ## ##
-
-\************************************************************************************************/
-
-entity function GetTargetOrLink( entity npc )
-{
- string target = npc.GetTarget_Deprecated()
- if ( target != "" )
- return GetEnt( target )
-
- array<entity> links = npc.GetLinkEntArray()
- if ( links.len() )
- return links.getrandom()
-
- return null
-}
-
-bool function IsMoveTarget( entity ent )
-{
- if ( !ent.HasKey( "editorclass" ) )
- return false
-
- string editorClass = expect string( ent.kv.editorclass )
- foreach ( moveTargetClass in file.moveTargetClasses )
- {
- if ( editorClass == moveTargetClass )
- return true
- }
- return false
-}
-
-bool function IsPotentialThreatTarget( entity ent )
-{
- if ( !ent.HasKey( "editorclass" ) )
- return false
-
- string editorClass = expect string( ent.kv.editorclass )
- if ( editorClass == "info_potential_threat_target" )
- return true
-
- return false
-}
-
-function AssaultLinkedMoveTarget( entity npc )
-{
- entity ent = GetTargetOrLink( npc )
- if ( ent == null )
- return
- if ( !IsMoveTarget( ent ) )
- return
-
- AssaultMoveTarget( npc, ent )
-}
-
-function AssaultMoveTarget( entity npc, entity ent )
-{
- npc.EndSignal( "OnDeath" )
- npc.EndSignal( "OnDestroy" )
- npc.EndSignal( "StopAssaultMoveTarget" )
- ent.EndSignal( "OnDestroy" )
-
- Assert( IsMoveTarget( ent ) )
-
- OnThreadEnd(
- function() : ( npc )
- {
- if ( IsAlive( npc ) )
- {
- Signal( npc, "OnFinishedAssaultChain" )
- }
- }
- )
-
- for ( ;; )
- {
- vector origin = ent.GetOrigin()
- vector angles = ent.GetAngles()
- float radius = 750
- float height = 750
-
- if ( ent.HasKey( "script_predelay" ) )
- {
- float time = float( ent.GetValueForKey( "script_predelay" ) )
- if ( time > 0.0 )
- wait time
- }
-
- if ( ent.HasKey( "script_goal_radius" ) )
- radius = float( ent.kv.script_goal_radius )
-
- if ( ent.HasKey( "script_goal_height" ) )
- height = float( ent.kv.script_goal_height )
-
- npc.AssaultPointClamped( origin )
- npc.AssaultSetGoalRadius( radius )
- npc.AssaultSetGoalHeight( height )
-
- if ( ent.HasKey( "face_angles" ) && ent.kv.face_angles == "1" )
- npc.AssaultSetAngles( angles, true )
-
- if ( ent.HasKey( "script_fight_radius" ) )
- {
- float fightRadius = float( ent.kv.script_fight_radius )
- npc.AssaultSetFightRadius( fightRadius )
- }
-
- if ( npc.IsLinkedToEnt( ent ) && ent.HasKey( "unlink" ) && ent.kv.unlink == "1" )
- npc.UnlinkFromEnt( ent )
-
- array<entity> entChildren = ent.GetLinkEntArray()
-
- bool finalDestination = entChildren.len() == 0
- npc.AssaultSetFinalDestination( finalDestination ) // this doesn't seem to make any difference as far as I can tell. Bug #117062
-
- if ( ent.HasKey( "clear_potential_threat_pos" ) && int( ent.kv.clear_potential_threat_pos ) == 1 )
- npc.ClearPotentialThreatPos()
-
- foreach ( ent in entChildren )
- {
- if ( IsPotentialThreatTarget( ent ) )
- {
- npc.SetPotentialThreatPos( ent.GetOrigin() )
- break
- }
- }
-
- table results
-
- bool skipRunto = ent.HasKey( "skip_runto" ) && int( ent.kv.skip_runto ) == 1
- if ( !skipRunto )
- {
- // If pathing fails we retry waiting for the other signals for 3 seconds.
- // This solves an issue with npc that failed to path because they where falling.
-
- const float RETRY_TIME = 3.0
- float waitStartTime = Time()
-
- while( true )
- {
- // activator, caller, self, signal, value
- results = WaitSignal( npc, "OnFinishedAssault", "OnEnterGoalRadius", "OnFailedToPath" )
-
- if ( results.signal != "OnFailedToPath" || waitStartTime + RETRY_TIME < Time() )
- break
- }
- }
-
- if ( ent.HasKey( "scr_signal" ) )
- Signal( npc, ent.GetValueForKey( "scr_signal" ), { nodeSignal = results.signal, node = ent } )
-
- if ( ent.HasKey( "leveled_animation" ) )
- {
- string animation = expect string( ent.kv.leveled_animation )
- Assert( npc.Anim_HasSequence( animation ), "Npc " + npc + " with model " + npc.GetModelName() + " does not have animation sequence " + animation )
- if ( skipRunto )
- waitthread PlayAnimTeleport( npc, animation, ent )
- else
- waitthread PlayAnimRun( npc, animation, ent, false )
- }
-
- if ( ent.HasKey( "scr_flag_set" ) )
- FlagSet( ent.GetValueForKey( "scr_flag_set" ) )
-
- if ( ent.HasKey( "scr_flag_clear" ) )
- FlagClear( ent.GetValueForKey( "scr_flag_clear" ) )
-
- if ( ent.HasKey( "scr_flag_wait" ) )
- FlagWait( ent.GetValueForKey( "scr_flag_wait" ) )
-
- if ( ent.HasKey( "scr_flag_wait_clear" ) )
- FlagWaitClear( ent.GetValueForKey( "scr_flag_wait_clear" ) )
-
- if ( ent.HasKey( "path_wait" ) )
- {
- float time = float( ent.GetValueForKey( "path_wait" ) )
- if ( time > 0.0 )
- wait time
- }
-
- if ( ent.HasKey( "disable_assault_on_goal" ) && int( ent.kv.disable_assault_on_goal ) == 1 )
- npc.DisableBehavior( "Assault" )
-
- if ( entChildren.len() == 0 )
- return
-
- entChildren.randomize()
- ent = null
- foreach ( child in entChildren )
- {
- if ( IsMoveTarget( child ) )
- {
- ent = child
- break
- }
- }
-
- if ( ent == null )
- return
- }
-}
-
-void function StopAssaultMoveTarget( entity npc )
-{
- npc.Signal( "StopAssaultMoveTarget" )
-}
-
-void function InitInfoMoveTargetFlags( entity infoMoveTarget )
-{
- #if DEV
- if ( infoMoveTarget.HasKey( "script_goal_radius" ) )
- {
- int radius = int( infoMoveTarget.kv.script_goal_radius )
- if ( radius < 64 )
- CodeWarning( "move target at " + infoMoveTarget.GetOrigin() + " had goal radius " + radius + " which is less than minimum 64" )
- }
- #endif
- if ( infoMoveTarget.HasKey( "scr_flag_set" ) )
- FlagInit( infoMoveTarget.GetValueForKey( "scr_flag_set" ) )
- if ( infoMoveTarget.HasKey( "scr_flag_clear" ) )
- FlagInit( infoMoveTarget.GetValueForKey( "scr_flag_clear" ) )
- if ( infoMoveTarget.HasKey( "scr_flag_wait" ) )
- FlagInit( infoMoveTarget.GetValueForKey( "scr_flag_wait" ) )
- if ( infoMoveTarget.HasKey( "scr_flag_wait_clear" ) )
- FlagInit( infoMoveTarget.GetValueForKey( "scr_flag_wait_clear" ) )
-
- if ( infoMoveTarget.HasKey( "scr_signal" ) )
- RegisterSignal( infoMoveTarget.GetValueForKey( "scr_signal" ) )
-}
-
-/************************************************************************************************\
-
-######## ####### ####### ## ######
- ## ## ## ## ## ## ## ##
- ## ## ## ## ## ## ##
- ## ## ## ## ## ## ######
- ## ## ## ## ## ## ##
- ## ## ## ## ## ## ## ##
- ## ####### ####### ######## ######
-
-\************************************************************************************************/
-asset function __GetWeaponModel( weapon )
-{
- switch ( weapon )
- {
- case "mp_weapon_rspn101":
- return $"models/weapons/rspn101/r101_ab_01.mdl"//$"models/weapons/rspn101/w_rspn101.mdl" --> this is the one I want to spawn, but I get a vague code error when I try
- break
-
- default:
- Assert( 0, "weapon: " + weapon + " not handled to return a model" )
- break
- }
- unreachable
-}
-
-void function AutoSquadnameAssignment( entity npc )
-{
- int team = npc.GetTeam()
- switch ( npc.GetClassName() )
- {
- case "npc_turret_sentry":
- case "npc_turret_mega":
- case "npc_dropship":
- case "npc_dropship_hero":
- return
- }
-
- switch ( npc.GetTeam() )
- {
- case TEAM_IMC:
- case TEAM_MILITIA:
- int index = svGlobal.npcsSpawnedThisFrame_scriptManagedArray[ team ]
- if ( GetScriptManagedEntArrayLen( index ) == 0 )
- {
- thread AutosquadnameAssignment_Thread( index, npc, team )
- }
-
- AddToScriptManagedEntArray( index, npc )
- break
-
- default:
- break
- }
-}
-
-void function AutosquadnameAssignment_Thread( int scriptManagedArrayIndex, entity npc, int team )
-{
- WaitEndFrame() // wait for everybody to spawn this frame
-
- array<entity> entities = GetScriptManagedEntArray( scriptManagedArrayIndex )
- if ( entities.len() <= 1 )
- {
- foreach ( npc in entities )
- {
- RemoveFromScriptManagedEntArray( scriptManagedArrayIndex, npc )
- }
- return
- }
-
- string squadName = UniqueString( "autosquad_team_" + team )
-
- foreach ( npc in entities )
- {
- RemoveFromScriptManagedEntArray( scriptManagedArrayIndex, npc )
- if ( !IsValid( npc ) )
- continue
- if ( npc.kv.squadname != "" )
- continue
- if ( !IsAlive( npc ) )
- continue
- SetSquad( npc, squadName )
- }
- Assert( GetScriptManagedEntArrayLen( scriptManagedArrayIndex ) == 0 )
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn_content.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn_content.gnut
deleted file mode 100644
index c6e7f9f4e..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_spawn_content.gnut
+++ /dev/null
@@ -1,879 +0,0 @@
-untyped
-
-global const PROTOTYPE_DEFAULT_TITAN_RODEO_SLOTS = 3 // Todo: remove and set this in titan_base.set
-
-global function CommonNPCOnSpawned
-global function ShouldSpawn
-global function AiSpawnContent_Init
-global function FixupTitle
-
-struct
-{
- array<string> pilotAntiTitanWeapons
- int nextAntiTitanWeaponAutoAssign
-} file
-
-function AiSpawnContent_Init()
-{
- RegisterSignal( "Stop_SimulateGrenadeThink" )
-
- if ( IsMultiplayer() )
- file.pilotAntiTitanWeapons = [ "mp_weapon_rocket_launcher" ]
-
- #if DEV
- if ( IsSingleplayer() )
- {
- array<string> aiSettings = GetAllowedTitanAISettings()
- foreach ( npcSettings in aiSettings )
- {
- asset npcModel = Dev_GetAISettingAssetByKeyField_Global( npcSettings, "DefaultModelName" )
- string playerSettings = expect string( Dev_GetAISettingByKeyField_Global( npcSettings, "npc_titan_player_settings" ) )
- asset playerModel = GetPlayerSettingsAssetForClassName( playerSettings, "bodymodel" )
-
- Assert( npcModel == playerModel, "NPC settings " + npcSettings + " has model " + npcModel + ", which does not match player model for same titan " + playerModel )
- }
- }
- #endif
-}
-
-
-function CommonNPCOnSpawned( entity npc )
-{
- npc.ai.spawnTime = Time()
- npc.ai.spawnOrigin = npc.GetOrigin()
-
- if ( npc.HasKey( "script_goal_radius" ) )
- {
- var radius = npc.kv.script_goal_radius
- if ( radius != null && radius != "" )
- {
- npc.AssaultSetGoalRadius( int( radius ) )
- npc.AssaultPoint( npc.GetOrigin() )
- }
- }
-
- if ( npc.HasKey( "script_goal_height" ) )
- {
- var height = npc.kv.script_goal_height
- if ( height != null && height != "" )
- {
- npc.AssaultSetGoalHeight( int( height ) )
- }
- }
-
- if ( npc.HasKey( "script_flag_killed" ) )
- {
- thread SetupFlagKilledForNPC( npc )
- }
-
- string aisetting = GetDefaultAISetting( npc )
-
- SetAISettingsWrapper( npc, aisetting )
-
- Assert( !npc.executedSpawnOptions, npc + " tried to spawn twice?" )
- npc.executedSpawnOptions = true
-
- if ( npc.Dev_GetAISettingByKeyField( "SpawnLimping" ) )
- npc.SetActivityModifier( ACT_MODIFIER_STAGGER, true )
-
- InitHighlightSettings( npc )
-
- if ( npc.Dev_GetAISettingByKeyField( "DrawTargetHealthBar" ) )
- npc.SetValidHealthBarTarget( true )
-
- // baseclass logic
- if ( npc.IsTitan() )
- {
- if ( !SpawnWithoutSoul( npc ) )
- {
- CreateTitanSoul( npc )
- }
- }
-
- if ( npc.GetTeam() <= 0 )
- {
- SetTeam( npc, expect int( npc.kv.teamnumber.tointeger() ) )
- }
-
- if ( IsMinion( npc ) )
- {
- SetupMinionForRPGs( npc )
- CommonMinionInit( npc )
- }
- else if ( !npc.IsTitan() )
- {
- npc.SetEnemyChangeCallback( OnEnemyChanged_MinionUpdateAimSettingsForEnemy )
- }
-
- if ( npc.GetTitle() == "" )
- {
- var title = npc.GetSettingTitle()
- if ( title != null && title != "" )
- npc.SetTitle( title )
- }
-
- // start alert
- if ( npc.mySpawnOptions_alert != null )
- npc.kv.alwaysalert = npc.mySpawnOptions_alert
- else if ( npc.HasKey( "start_alert") )
- npc.kv.alwaysalert = npc.kv.start_alert
-
- npc.kv.physdamagescale = 1.0
-
- if ( npc.HasKey( "script_buddha" ) && npc.kv.script_buddha == "1" )
- {
- npc.ai.buddhaMode = true
- }
-
- if ( npc.IsTitan() )
- {
- // set boss titan type before setting proficiency for Titans
- if ( npc.HasKey( "TitanType" ) )
- {
- npc.ai.bossTitanType = int( npc.kv.TitanType )
-
- // this is to get rid of all weak titans
- if ( npc.ai.bossTitanType == TITAN_WEAK )
- {
- CodeWarning( "Spawned weak Titan at " + npc.GetOrigin() + ". Change TitanType to Henchman Titan." )
-
- // GetSettingsTitle() is causing a script error. Removed for now.
- // CodeWarning( "Spawned weak Titan " + npc.GetSettingsTitle() + " at " + npc.GetOrigin() + ". Change TitanType to Henchman Titan." )
- npc.ai.bossTitanType = TITAN_HENCH
- }
- }
-
- if ( npc.HasKey( "disable_vdu" ) )
- npc.ai.bossTitanVDUEnabled = int( npc.kv.disable_vdu ) == 0
- }
-
- // Set proficiency before giving weapons
- SPMP_UpdateNPCProficiency( npc )
-
- if ( npc.IsTitan() )
- {
- UpdateTitanMinimapStatusToOtherPlayers( npc )
- CommonNPCTitanOnSpawned( npc )
-// Assert( npc.Dev_GetAISettingByKeyField( "footstep_type" ) != "", "NPC " + npc + " has no footstep type set" )
- }
- else
- {
- UpdateAIMinimapStatusToOtherPlayers( npc )
-
- if ( npc.ai.mySpawnOptions_weapon != null )
- {
- array<entity> weapons = npc.GetMainWeapons()
- TakeWeaponsForArray( npc, weapons )
-
- NPCDefaultWeapon spawnoptionsweapon = expect NPCDefaultWeapon( npc.ai.mySpawnOptions_weapon )
- npc.GiveWeapon( spawnoptionsweapon.wep, spawnoptionsweapon.mods )
- }
-
- entity weapon = npc.GetActiveWeapon()
- if ( weapon != null && weapon.GetWeaponType() == WT_SIDEARM )
- npc.DisableNPCFlag( NPC_CROUCH_COMBAT )
- }
-
- if ( npc.HasKey( "drop_battery" ) )
- {
- npc.ai.shouldDropBattery = (npc.kv.drop_battery == "1")
- }
-
- switch ( npc.GetClassName() )
- {
- case "npc_bullseye":
- npc.NotSolid()
- npc.SetInvulnerable()
- break
-
- case "npc_drone":
- InitMinimapSettings( npc )
-
- if ( GetMarvinType( npc ) == "marvin_type_drone" )
- {
- thread MarvinJobThink( npc )
- return
- }
-
- npc.s.rebooting <- null
- npc.ai.preventOwnerDamage = true
- npc.s.lastSmokeDeployTime <- Time()
-
- thread RunDroneTypeThink( npc )
-
- switch ( GetDroneType( npc ) )
- {
- case "drone_type_engineer_combat":
- npc.kv.rendercolor = "0 0 0"
- break
-
- case "drone_type_engineer_shield":
- npc.kv.rendercolor = "255 255 255"
- break
- }
- break
-
- case "npc_dropship":
- npc.SetSkin( 1 ) //Use skin where the lights are on for dropship.
- npc.EnableRenderAlways()
- npc.SetAimAssistAllowed( false )
- //npc.kv.CollisionGroup = TRACE_COLLISION_GROUP_BLOCK_WEAPONS
- AddAnimEvent( npc, "dropship_warpout", WarpoutEffect )
-
- InitLeanDropship( npc )
- break
-
-
- case "npc_frag_drone":
- MakeSuicideSpectre( npc )
- break
-
- case "npc_gunship":
- InitMinimapSettings( npc )
- EmitSoundOnEntity( npc, SOUND_GUNSHIP_HOVER )
-
- npc.ai.preventOwnerDamage = true
- npc.s.rebooting <- null
- npc.s.plantedMinesManagedEntArrayID <- CreateScriptManagedEntArray()
-
- npc.kv.crashOnDeath = false
- //npc.kv.secondaryWeaponName = "mp_weapon_gunship_missile"
-
- EnableLeeching( npc )
- npc.SetUsableByGroup( "enemies pilot" )
-
- thread GunshipThink( npc )
- break
-
- case "npc_marvin":
- asset model = npc.GetModelName()
- npc.EnableNPCFlag( NPC_DISABLE_SENSING ) // don't do traces to look for enemies or players
- thread MarvinFace( npc )
- thread MarvinJobThink( npc )
- break
-
- case "npc_pilot_elite":
- npc.kv.physdamagescale = 1.0
- npc.kv.WeaponProficiency = eWeaponProficiency.VERYGOOD
- break
-
- case "npc_prowler":
- npc.kv.disengageEnemyDist = 1500
- npc.DisableNPCFlag( NPC_ALLOW_FLEE ) //HACK until we get a way to make last guy not run away and hide
- //SetSquad( npc, spawnOptions.squadName ) //not sure why this is here - jake had it in his original spawn func, so I'm keeping it
- //SetNPCSquadMode( spawnOptions.squadName, SQUAD_MODE_MULTIPRONGED_ATTACK )
- break
-
- case "npc_soldier":
-
- InitMinimapSettings( npc )
-
- SetHumanRagdollImpactTable( npc )
-
- npc.EnableNPCFlag( NPC_CROUCH_COMBAT )
-
- thread OnSoldierSeeEnemy( npc )
- thread TryFriendlyPassingNearby( npc )
-
- int team = npc.GetTeam()
-
- //grunt specific
- npc.SetDoFaceAnimations( true ) //HACK: assumption that militia are the only grunt models with faces ( will need a better thing for R2 )
-
- bool alreadyGaveASecondary = false;
- entity weapon = npc.GetActiveWeapon()
- string weaponSubClass
- if ( weapon )
- weaponSubClass = string( weapon.GetWeaponInfoFileKeyField( "weaponSubClass" ) )
-
- #if SP
- if ( weaponSubClass == "sniper" )
- {
- if ( AssignDefaultNPCSidearm( npc ) )
- alreadyGaveASecondary = true
- }
- #endif
-
- if ( !alreadyGaveASecondary && SP_GetPilotAntiTitanWeapon( npc ) == null )
- TryAutoAssignAntiTitanWeapon( npc )
-
- if ( npc.Dev_GetAISettingByKeyField( "PersonalShield" ) != null )
- {
- npc.DisableNPCFlag( NPC_ALLOW_FLEE | NPC_ALLOW_HAND_SIGNALS | NPC_USE_SHOOTING_COVER | NPC_CROUCH_COMBAT )
- thread ActivatePersonalShield( npc )
- }
-
- if ( npc.ai.droneSpawnAISettings != "" )
- {
- thread DroneGruntThink( npc, npc.ai.droneSpawnAISettings )
- }
-
- AssignGruntModelForWeaponClass( npc, weapon, weaponSubClass )
-
- break
-
- case "npc_spectre":
- InitMinimapSettings( npc )
- thread OnSpectreSeeEnemy( npc )
-
- if ( IsMultiplayer() )
- {
- npc.EnableNPCFlag( NPC_CROUCH_COMBAT )
- //Only enable spectre hacking if the playlist var is enabled
- if ( ( npc.GetTeam() == TEAM_IMC || npc.GetTeam() == TEAM_MILITIA ) && GetCurrentPlaylistVarInt( "enable_spectre_hacking", 0 ) == 1 )
- {
- EnableLeeching( npc )
- npc.SetUsableByGroup( "enemies pilot" )
- }
- }
- else
- {
- EnableLeeching( npc )
- npc.SetUsableByGroup( "enemies pilot" )
-
- if ( npc.HasKey( "carrying_battery" ) )
- {
- if ( npc.kv.carrying_battery == "1" )
- {
- thread NPCCarriesBattery( npc )
- }
- }
- }
-
- if ( SP_GetPilotAntiTitanWeapon( npc ) == null )
- TryAutoAssignAntiTitanWeapon( npc )
- break
-
- case "npc_stalker":
- InitMinimapSettings( npc )
-
- if ( IsSingleplayer() && npc.kv.squadname != "" )
- SetNPCSquadMode( npc.kv.squadname, SQUAD_MODE_MULTIPRONGED_ATTACK )
-
- break
-
- case "npc_super_spectre":
-
- InitMinimapSettings( npc )
-
- npc.GiveOffhandWeapon( "mp_weapon_spectre_spawner", 0 )
-
- DisableLeeching( npc )
-
- npc.SetCapabilityFlag( bits_CAP_NO_HIT_SQUADMATES, false )
-
- npc.ai.preventOwnerDamage = true
-
- npc.SetDeathNotifications( true )
-
- AddAnimEvent( npc, "SuperSpectre_OnGroundSlamImpact", SuperSpectre_OnGroundSlamImpact )
- AddAnimEvent( npc, "SuperSpectre_OnGroundLandImpact", SuperSpectre_OnGroundLandImpact )
-
- thread SuperSpectreThink( npc )
-
- SuperSpectreIntro( npc )
- break
-
-
- case "npc_titan":
- InitMinimapSettings( npc )
-
- // used so the titan can stand/kneel without cutting off functionality
- npc.s.standQueued <- false
- npc.ai.preventOwnerDamage = true
- if ( IsMultiplayer() )
- {
- npc.e.hasDefaultEnemyHighlight = true
- SetDefaultMPEnemyHighlight( npc )
- }
- break
-
-
- case "npc_turret_mega":
- InitMinimapSettings( npc )
- npc.EnableNPCFlag( NPC_AIM_DIRECT_AT_ENEMY )
- npc.SetAimAssistAllowed( false )
- #if R1_VGUI_MINIMAP
- npc.Minimap_SetDefaultMaterial( GetMinimapMaterial( "turret_neutral" ) )
- npc.Minimap_SetFriendlyMaterial( GetMinimapMaterial( "turret_friendly" ) )
- npc.Minimap_SetEnemyMaterial( GetMinimapMaterial( "turret_enemy" ) )
- npc.Minimap_SetBossPlayerMaterial( GetMinimapMaterial( "turret_friendly" ) )
- #endif
- break
-
- case "npc_turret_sentry":
- InitMinimapSettings( npc )
- npc.SetAimAssistAllowed( false )
- break
-
- }
-
- thread AssaultLinkedMoveTarget( npc )
-
- FixupTitle( npc )
- #if DEV
- // stop all the wandering in sp_enemies.
- if ( GetMapName() == "sp_enemies" )
- npc.DisableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE )
- #endif
-}
-
-void function FixupTitle( entity npc )
-{
- if ( IsMultiplayer() )
- return
-
- if ( !npc.IsTitan() )
- npc.SetTitle( "" )
- /*
- if ( npc.GetTitle() == "" )
- return
- switch ( npc.GetTeam() )
- {
- case TEAM_UNASSIGNED:
- case TEAM_MILITIA:
- break
- default:
- npc.SetTitle( "" )
- break
- }
- */
-}
-
-var function GetTitanHotdropSetting( entity npc )
-{
- if ( npc.mySpawnOptions_titanfallSpawn != null )
- return "titanfall"
- if ( npc.mySpawnOptions_warpfallSpawn != null )
- return "warpfall"
-
- if ( npc.HasKey( "script_hotdrop" ) )
- {
- switch ( npc.kv.script_hotdrop )
- {
- case "0":
- return null
- case "3":
- case "1":
- return "titanfall"
- case "4":
- case "2":
- return "warpfall"
- }
- }
-
- return null
-}
-
-function CommonNPCTitanOnSpawned( entity npc )
-{
- if ( npc.ai.titanSpawnLoadout.primary != "" )
- {
- Assert( npc.GetMainWeapons().len() == 0 )
-
- // if designer overwrites weapons, apply them
- GiveTitanLoadout( npc, npc.ai.titanSpawnLoadout )
- }
- else if ( npc.GetMainWeapons().len() == 0 )
- {
- GiveTitanLoadout( npc, npc.ai.titanSpawnLoadout )
- }
-
- //Assert( npc.ai.titanSpawnLoadout.setFile == npc.ai.titanSettings.titanSetFile )
- string playerSettings = expect string( npc.Dev_GetAISettingByKeyField( "npc_titan_player_settings" ) )
- asset modelName = GetPlayerSettingsAssetForClassName( playerSettings, "bodymodel" )
- if ( npc.GetModelName() != modelName )
- npc.SetModel( modelName )
-// Assert( npc.GetModelName() == modelName )
-
- int camoIndex = GetTitanCamoIndexFromLoadoutAndPrimeStatus( npc.ai.titanSpawnLoadout )
- int skinIndex = GetTitanSkinIndexFromLoadoutAndPrimeStatus( npc.ai.titanSpawnLoadout )
- int decalIndex = GetTitanDecalIndexFromLoadoutAndPrimeStatus ( npc.ai.titanSpawnLoadout )
-
- if ( camoIndex > 0 )
- {
- npc.SetSkin( TITAN_SKIN_INDEX_CAMO )
- npc.SetCamo( camoIndex )
- }
- else
- {
- int skin
- if ( npc.HasKey( "modelskin" ) )
- skin = expect int( npc.kv.modelskin.tointeger() )
-
- if ( skinIndex > 0 )
- {
- Assert( skin == 0, "Both npc.kv.modelskin and skinIndex were > 0. Pick one." )
- skin = skinIndex
- }
-
- if ( skin > 0 )
- npc.SetSkin( skin )
- }
-
- npc.SetDecal( decalIndex )
-
- #if HAS_BOSS_AI
- if ( IsMercTitan( npc ) )
- {
- array<entity> weapons = GetPrimaryWeapons( npc )
- Assert( weapons.len() == 1 )
- string character = GetMercCharacterForWeapon( weapons[0].GetWeaponClassName() )
- npc.ai.bossCharacterName = character
- npc.ai.mercCharacterID = GetBossTitanID( character )
-
- int id = GetBossTitanID( character )
- string title = GetBossTitleFromID( id )
-
- npc.SetTitle( title )
- }
- #endif
-
- // force sp titans to use specific loadouts
- if ( !IsMultiplayer() )
- ResetTitanLoadoutFromPrimary( npc )
-
- npc.EnableNPCFlag( NPC_NO_MOVING_PLATFORM_DEATH )
- //npc.DisableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE )
-
- if ( IsMultiplayer() )
- {
- npc.kv.alwaysalert = 1
- }
- else
- {
- if ( npc.GetAIClass() == AIC_TITAN_BUDDY )
- {
- npc.DisableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE )
- array<entity> enemies = GetNPCArrayEx( "any", TEAM_ANY, npc.GetTeam(), npc.GetOrigin(), 4000 )
- if ( enemies.len() > 0 )
- npc.SetAlert()
-
- if ( npc.GetTitanSoul() )
- {
- // create buddy titan dialogue ent
- // it will be transfered during embark and disembark automatically
- entity dialogueEnt = CreateScriptMover()
- dialogueEnt.DisableHibernation()
- dialogueEnt.SetParent( npc, "HEADFOCUS", false, 0 )
- npc.GetTitanSoul().SetTitanSoulNetEnt( "dialogueEnt", dialogueEnt )
- }
- }
- }
-
- local maxHealth = GetPlayerSettingsFieldForClassName_Health( npc.ai.titanSettings.titanSetFile ) //FD META - TO UPDATE with NPC equivalent of .GetPlayerModHealth()
- if ( npc.ai.titanSpawnLoadout.setFileMods.contains( "fd_health_upgrade" ) )
- maxHealth += 2500
- //TEMP - GetPlayerSettingsFieldForClassName_Health doesn't return modded values.
- if ( IsHardcoreGameMode() )
- maxHealth *= 0.5
-
- // this will override whatever health is set in the aisettings txt file.
- npc.SetMaxHealth( maxHealth )
- npc.SetHealth( maxHealth )
- npc.SetValidHealthBarTarget( true )
-
- #if HAS_BOSS_AI
- UpdateMercTitanHealthForDifficulty( npc )
- #endif
-
- switch ( GetTitanHotdropSetting( npc ) )
- {
- case "titanfall":
- thread NPCTitanHotdrops( npc, true )
- break
-
- case "warpfall":
- thread NPCTitanHotdrops( npc, true, "at_hotdrop_drop_2knee_turbo_upgraded" )
- break
- }
-
- // TODO: Have code allow us to put this in titan_base.set
- npc.SetNumRodeoSlots( PROTOTYPE_DEFAULT_TITAN_RODEO_SLOTS )
-
- if ( IsValid( npc.mySpawnOptions_ownerPlayer ) )
- {
- entity soul = npc.GetTitanSoul()
- entity player = expect entity( npc.mySpawnOptions_ownerPlayer )
-
- if ( IsValid( soul ) )
- {
- soul.soul.lastOwner = player
- SoulBecomesOwnedByPlayer( soul, player )
- }
-
- SetupAutoTitan( npc, player )
- }
-
- if ( npc.HasKey( "disable_offhand_ordnance" ) )
- {
- if ( bool( npc.kv.disable_offhand_ordnance ) )
- {
- npc.TakeOffhandWeapon( OFFHAND_ORDNANCE )
- }
- }
-
- if ( npc.HasKey( "disable_offhand_defense" ) )
- {
- if ( bool( npc.kv.disable_offhand_defense ) )
- {
- npc.TakeOffhandWeapon( OFFHAND_SPECIAL )
- }
- }
-
- if ( npc.HasKey( "disable_offhand_tactical" ) )
- {
- if ( bool( npc.kv.disable_offhand_tactical ) )
- {
- entity weapon = npc.GetOffhandWeapon( OFFHAND_ANTIRODEO )
- if ( weapon && weapon.GetWeaponClassName() == "mp_titanability_hover" )
- npc.SetAllowSpecialJump( false )
-
- npc.TakeOffhandWeapon( OFFHAND_ANTIRODEO )
- }
- }
-
- if ( npc.HasKey( "disable_offhand_core" ) )
- {
- if ( bool( npc.kv.disable_offhand_core ) )
- {
- npc.TakeOffhandWeapon( OFFHAND_EQUIPMENT )
- }
- }
-
- if ( npc.HasKey( "follow_mode" ) )
- {
- if ( bool( npc.kv.follow_mode ) )
- {
- entity player = GetPlayerArray()[0] // gross
- int followBehavior = GetDefaultNPCFollowBehavior( npc )
- npc.InitFollowBehavior( player, followBehavior )
- npc.EnableBehavior( "Follow" )
- npc.DisableBehavior( "Assault" )
- }
- }
-
- var hasTraverse = npc.Dev_GetAISettingByKeyField( "can_traverse" )
- if ( hasTraverse == null || expect int( hasTraverse ) == 0 )
- {
- npc.SetCapabilityFlag( bits_CAP_MOVE_TRAVERSE, false )
- }
-
- entity soul = npc.GetTitanSoul()
- if ( IsValid( soul ) )
- {
- soul.soul.titanLoadout = npc.ai.titanSpawnLoadout
- }
-}
-
-function ShouldSpawn( team, forced )
-{
- //we're not allowed to spawn AI at all - return false
- if ( !IsNPCSpawningEnabled() && !forced )
- {
- printt( "WARNING: tried to spawn an NPC but NPC Spawning is Disabled." )
- return false
- }
- return true
-}
-
-
-function HACK_DroneGruntModel( grunt )
-{
- string tag = "CHESTFOCUS"
- int attachID = expect int( grunt.LookupAttachment( tag ) )
- vector origin = expect vector( grunt.GetAttachmentOrigin( attachID ) )
- vector angles = expect vector( grunt.GetAttachmentAngles( attachID ) )
- vector forward = AnglesToForward( angles )
- vector right = AnglesToRight( angles )
- vector up = AnglesToUp( angles )
-
- vector angles1 = AnglesCompose( angles, Vector( 0, -90, 90 ) )
- vector origin1 = origin + ( forward * -4 ) + ( up * -1.5 )
- entity back1 = CreatePropDynamic( HACK_DRONE_BACK1, origin1, angles1 )
- back1.SetParent( grunt, tag, true, 0 )
-
- vector angles2 = AnglesCompose( angles, Vector( 0, -90, 0 ) )
- vector origin2 = origin + ( forward * -9 ) + ( up * 11 ) + ( right * -1 )
- entity back2 = CreatePropDynamic( HACK_DRONE_BACK2, origin2, angles2 )
- back2.SetParent( grunt, tag, true, 0 )
-}
-
-void function TryAutoAssignAntiTitanWeapon( entity npc )
-{
- // disabling this while anti titan weapons settle down
- if ( !IsMultiplayer() )
- return
-
- if ( file.pilotAntiTitanWeapons.len() == 0 )
- return
-
- Assert( !HasAntiTitanWeapon( npc ) )
-
- // each 4th npc gets a rocket
- file.nextAntiTitanWeaponAutoAssign--
- if ( file.nextAntiTitanWeaponAutoAssign > 0 )
- return
-
- file.nextAntiTitanWeaponAutoAssign = 3
-
- string weapon = file.pilotAntiTitanWeapons.getrandom()
- npc.GiveWeapon( weapon )
-
- if ( IsGrunt( npc ) )
- {
- // show rockets on the back
- switch ( npc.GetTeam() )
- {
- case TEAM_IMC:
- npc.SetModel( TEAM_IMC_GRUNT_MODEL_ROCKET )
- break
-
-#if SP
- case TEAM_MILITIA:
- npc.SetModel( TEAM_MIL_GRUNT_MODEL_ROCKET )
- break
-#endif
- }
- }
-}
-
-function SpawnWithoutSoul( ent )
-{
- if ( ent.HasKey( "noSoul" ) )
- {
- return ent.kv.noSoul
- }
-
- return "spawnWithoutSoul" in ent.s
-}
-
-function DisableAimAssisst( self )
-{
- self.SetAimAssistAllowed( false )
-}
-
-void function SuperSpectreIntro( entity npc )
-{
- bool warpfall
- if ( npc.mySpawnOptions_warpfallSpawn != null )
- warpfall = true
- else if ( npc.HasKey( "script_hotdrop" ) && npc.kv.script_hotdrop.tolower() == "warpfall" )
- warpfall = true
-
- if ( warpfall )
- thread SuperSpectre_WarpFall( npc )
-}
-
-void function AssignGruntModelForWeaponClass( entity npc, entity weapon, string weaponSubClass )
-{
- // We only have IMC grunt models for weapon class
- if ( !npc.Dev_GetAISettingByKeyField( "IsGenericGrunt" ) )
- return
-
- asset model
-
- switch ( npc.GetTeam() )
- {
-//#if SP
- case TEAM_MILITIA:
- switch ( weaponSubClass )
- {
- case "lmg":
- case "sniper":
- model = TEAM_MIL_GRUNT_MODEL_LMG
- break
-
- case "rocket":
- case "shotgun":
- case "projectile_shotgun":
- model = TEAM_MIL_GRUNT_MODEL_SHOTGUN
- break
-
- case "handgun":
- case "smg":
- case "sidearm":
- model = TEAM_MIL_GRUNT_MODEL_SMG
- break
-
- case "rifle":
- default:
- model = TEAM_MIL_GRUNT_MODEL_RIFLE
- break
- }
- break
-//#endif
-
- case TEAM_IMC:
- default:
- switch ( weaponSubClass )
- {
- case "lmg":
- case "sniper":
- model = TEAM_IMC_GRUNT_MODEL_LMG
- break
-
- case "rocket":
- case "shotgun":
- case "projectile_shotgun":
- model = TEAM_IMC_GRUNT_MODEL_SHOTGUN
- break
-
- case "handgun":
- case "smg":
- case "sidearm":
- model = TEAM_IMC_GRUNT_MODEL_SMG
- break
-
- case "rifle":
- default:
-#if SP
- model = TEAM_IMC_GRUNT_MODEL_RIFLE
-#else
- // no shotgun/smg grunts in MP right now
- switch ( RandomInt( 3 ) )
- {
- case 0:
- model = TEAM_IMC_GRUNT_MODEL_RIFLE
- break
- case 1:
- model = TEAM_IMC_GRUNT_MODEL_SHOTGUN
- break
- case 2:
- model = TEAM_IMC_GRUNT_MODEL_SMG
- break
- }
-#endif
- break
- }
- break
-
- }
-
- if ( model != $"" )
- {
- npc.SetModel( model )
- return
- }
-
- if ( IsValid( weapon ) )
- CodeWarning( "Grunt at " + npc.GetOrigin() + " couldnt get assigned a body model for weapon " + weapon.GetWeaponClassName() + " because that weapon is missing or has invalid weaponSubClass field" )
- else
- CodeWarning( "Grunt at " + npc.GetOrigin() + " has no weapon" )
-}
-
-
-entity function SP_GetPilotAntiTitanWeapon( entity ent )
-{
- array<entity> weaponsArray = ent.GetMainWeapons()
- foreach ( weapon in weaponsArray )
- {
- foreach ( weaponName in file.pilotAntiTitanWeapons )
- {
- if ( weapon.GetWeaponClassName() == weaponName )
- return weapon
- }
- }
-
- return null
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_spectre.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_spectre.gnut
deleted file mode 100644
index 214aff96e..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_spectre.gnut
+++ /dev/null
@@ -1,131 +0,0 @@
-global function AiSpectre_Init
-global function NPCCarriesBattery
-
-void function AiSpectre_Init()
-{
- //AddDamageCallback( "npc_spectre", SpectreOnDamaged )
- AddDeathCallback( "npc_spectre", SpectreOnDeath )
- //AddSpawnCallback( "npc_spectre", SpectreOnSpawned )
-
- #if !SPECTRE_CHATTER_MP_ENABLED
- AddCallback_OnPlayerKilled( SpectreChatter_OnPlayerKilled )
- AddCallback_OnNPCKilled( SpectreChatter_OnNPCKilled )
- #endif
-}
-
-void function SpectreOnSpawned( entity npc )
-{
-
-}
-
-void function SpectreOnDeath( entity npc, var damageInfo )
-{
- if ( !IsValidHeadShot( damageInfo, npc ) )
- return
-
- // Set these so cl_player knows to kill the eye glow and play the right SFX
- DamageInfo_AddCustomDamageType( damageInfo, DF_HEADSHOT )
- DamageInfo_AddCustomDamageType( damageInfo, DF_KILLSHOT )
-// EmitSoundOnEntityExceptToPlayer( npc, attacker, "SuicideSpectre.BulletImpact_HeadShot_3P_vs_3P" )
-
- int bodyGroupIndex = npc.FindBodyGroup( "removableHead" )
- int stateIndex = 1 // 0 = show, 1 = hide
- npc.SetBodygroup( bodyGroupIndex, stateIndex )
-
- DamageInfo_SetDamage( damageInfo, npc.GetMaxHealth() )
-
-}
-
-// All damage to spectres comes here for modification and then either branches out to other npc types (Suicide, etc) for custom stuff or it just continues like normal.
-void function SpectreOnDamaged( entity npc, var damageInfo )
-{
-
-}
-
-void function SpectreChatter_OnPlayerKilled( entity playerKilled, entity attacker, var damageInfo )
-{
- if ( !IsSpectre( attacker ) )
- return
-
- if ( playerKilled.IsTitan() )
- thread PlaySpectreChatterAfterDelay( attacker, "spectre_gs_gruntkillstitan_02_1" )
- else
- thread PlaySpectreChatterAfterDelay( attacker, "spectre_gs_killenemypilot_01_1" )
-
-}
-
-void function SpectreChatter_OnNPCKilled( entity npcKilled, entity attacker, var damageInfo )
-{
- if ( IsSpectre( npcKilled ) )
- {
- string deadGuySquadName = expect string( npcKilled.kv.squadname )
- if ( deadGuySquadName == "" )
- return
-
- array<entity> squad = GetNPCArrayBySquad( deadGuySquadName )
-
- entity speakingSquadMate = null
-
- foreach( squadMate in squad )
- {
- if ( IsSpectre( squadMate ) )
- {
- speakingSquadMate = squadMate
- break
- }
- }
- if ( speakingSquadMate == null )
- return
-
- if ( squad.len() == 1 )
- thread PlaySpectreChatterAfterDelay( speakingSquadMate, "spectre_gs_squaddeplete_01_1" )
- else if ( squad.len() > 0 )
- thread PlaySpectreChatterAfterDelay( speakingSquadMate, "spectre_gs_allygrundown_05_1" )
- }
- else
- {
- if ( !IsSpectre( attacker ) )
- return
-
- if ( npcKilled.IsTitan() )
- thread PlaySpectreChatterAfterDelay( attacker, "spectre_gs_gruntkillstitan_02_1" )
- }
-}
-
-void function PlaySpectreChatterAfterDelay( entity spectre, string chatterLine, float delay = 0.3 )
-{
- wait delay
-
- if ( !IsAlive( spectre ) ) //Really this is just an optimization thing, if the spectre is dead no point in running the same check for every player nearby in ShouldPlaySpectreChatterMPLine
- return
-
- PlaySpectreChatterToAll( chatterLine, spectre )
-}
-
-void function NPCCarriesBattery( entity npc )
-{
- entity battery = Rodeo_CreateBatteryPack()
- battery.SetParent( npc, "BATTERY_ATTACH" )
- battery.MarkAsNonMovingAttachment()
- thread SpectreBatteryThink( npc, battery )
-}
-
-void function SpectreBatteryThink( entity npc, entity battery )
-{
- battery.EndSignal( "OnDestroy" )
- npc.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( battery )
- {
- if ( IsValid( battery ) )
- {
- battery.ClearParent()
- battery.SetAngles( < 0,0,0 > )
- battery.SetVelocity( < 0,0,200 > )
- }
- }
- )
-
- npc.WaitSignal( "OnDeath" )
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_stalker.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_stalker.gnut
deleted file mode 100644
index f49560e02..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_stalker.gnut
+++ /dev/null
@@ -1,606 +0,0 @@
-global function AiStalker_Init
-global function GetDeathForce
-global function StalkerGearOverloads
-global function StalkerMeltingDown
-
-global function IsStalkerLimbBlownOff
-
-const float STALKER_DAMAGE_REQUIRED_TO_HEADSHOT = 0.3
-//
-// Base npc script shared between all npc types (regular, suicide, etc.)
-//
-
-const STALKER_REACTOR_CRITIMPACT_SOUND_1P_VS_3P = "ai_stalker_bulletimpact_nukecrit_1p_vs_3p"
-const STALKER_REACTOR_CRITIMPACT_SOUND_3P_VS_3P = "ai_stalker_bulletimpact_nukecrit_3p_vs_3p"
-const STALKER_REACTOR_CRITICAL_SOUND = "ai_stalker_nukedestruct_warmup_3p"
-const STALKER_REACTOR_CRITICAL_FX = $"P_spectre_suicide_warn"
-
-void function AiStalker_Init()
-{
- PrecacheImpactEffectTable( "exp_stalker_powersupply" )
- PrecacheImpactEffectTable( "exp_small_stalker_powersupply" )
- PrecacheParticleSystem( STALKER_REACTOR_CRITICAL_FX )
- AddDamageCallback( "npc_stalker", StalkerOnDamaged )
- AddDeathCallback( "npc_stalker", StalkerOnDeath )
- AddSpawnCallback( "npc_stalker", StalkerOnSpawned )
-}
-
-void function StalkerOnSpawned( entity npc )
-{
- StalkerOnSpawned_Think( npc )
-}
-
-void function StalkerOnSpawned_Think( entity npc )
-{
- npc.SetCanBeMeleeExecuted( false )
-
- for ( int hitGroup = 0; hitGroup < HITGROUP_COUNT; hitGroup++ )
- {
- npc.ai.stalkerHitgroupDamageAccumulated[ hitGroup ] <- 0
- npc.ai.stalkerHitgroupLastHitTime[ hitGroup ] <- 0
- }
-
- if ( npc.Dev_GetAISettingByKeyField( "ScriptSpawnAsCrawler" ) == 1 )
- {
- EnableStalkerCrawlingBehavior( npc )
- PlayCrawlingAnim( npc, "ACT_RUN" )
- npc.Anim_Stop() // start playing a crawl anim then cut it off so it doesnt loop
- }
-}
-
-void function StalkerOnDeath( entity npc, var damageInfo )
-{
- thread StalkerOnDeath_Internal( npc, damageInfo )
-
- #if MP
- int sourceId = DamageInfo_GetDamageSourceIdentifier( damageInfo )
- if ( sourceId == eDamageSourceId.damagedef_titan_step )
- {
- Explosion_DamageDefSimple(
- damagedef_stalker_powersupply_explosion_large_at,
- npc.GetOrigin(),
- npc,
- npc,
- npc.GetOrigin()
- )
- }
- #endif
-
-}
-
-void function StalkerOnDeath_Internal( entity npc, var damageInfo )
-{
- int customDamageFlags = DamageInfo_GetCustomDamageType( damageInfo )
- bool allowDismemberment = bool( customDamageFlags & DF_DISMEMBERMENT )
- if ( allowDismemberment )
- {
- int hitGroup = GetHitGroupFromDamageInfo( npc, damageInfo )
- if ( hitGroup >= HITGROUP_GENERIC )
- {
- entity attacker = DamageInfo_GetAttacker( damageInfo )
- TryDismemberStalker( npc, damageInfo, attacker, hitGroup )
- }
- }
-
- if ( IsCrawling( npc ) )
- {
- WaitFrame() // or head won't disappear
- if ( IsValid( npc ) )
- npc.BecomeRagdoll( Vector( 0, 0, 0 ), false )
- return
- }
-}
-
-
-// All damage to stalkers comes here for modification and then either branches out to other npc types (Suicide, etc) for custom stuff or it just continues like normal.
-void function StalkerOnDamaged( entity npc, var damageInfo )
-{
- StalkerOnDamaged_Internal( npc, damageInfo )
-}
-
-void function StalkerOnDamaged_Internal( entity npc, var damageInfo )
-{
- if ( !IsAlive( npc ) )
- return
-
- if ( StalkerMeltingDown( npc ) )
- {
- DamageInfo_ScaleDamage( damageInfo, 0.0 )
- return
- }
-
- // can't shoot, don't blow off limbs
- if ( IsCrawling( npc ) )
- {
- if ( Time() - npc.ai.startCrawlingTime < 0.75 )
- {
- DamageInfo_SetDamage( damageInfo, 0 )
- return
- }
- }
-
- int hitGroup = GetHitGroupFromDamageInfo( npc, damageInfo )
- if ( hitGroup < HITGROUP_GENERIC )
- hitGroup = HITGROUP_GENERIC
-
- float damage = DamageInfo_GetDamage( damageInfo )
-
- // limb dead yet?
- npc.ai.stalkerHitgroupDamageAccumulated[ hitGroup ] += int( damage )
- npc.ai.stalkerHitgroupLastHitTime[ hitGroup ] = Time()
-
- entity attacker = DamageInfo_GetAttacker( damageInfo )
-
- if ( PlayerHitGear( npc, damageInfo, hitGroup ) )
- {
- // don't die from damage
- float damage = DamageInfo_GetDamage( damageInfo )
- damage = npc.GetHealth() - 1.0
- DamageInfo_SetDamage( damageInfo, damage )
-
- thread StalkerGearOverloads( npc, attacker )
- return
- }
-
- int customDamageFlags = DamageInfo_GetCustomDamageType( damageInfo )
- bool allowDismemberment = bool( customDamageFlags & DF_DISMEMBERMENT )
- if ( !allowDismemberment )
- return
-
- bool canBeStaggered = TryDismemberStalker( npc, damageInfo, attacker, hitGroup )
-
- if ( canBeStaggered && !IsCrawling( npc ) && !npc.ai.transitioningToCrawl )
- {
- if ( npc.GetHealth().tofloat() / npc.GetMaxHealth().tofloat() <= 0.5 )
- {
- thread AttemptStandToStaggerAnimation( npc )
- npc.SetActivityModifier( ACT_MODIFIER_STAGGER, true )
- }
- }
-}
-
-bool function TryDismemberStalker( entity npc, var damageInfo, entity attacker, int hitGroup )
-{
- string fpSound
- string tpSound
-
- switch ( hitGroup )
- {
- case HITGROUP_CHEST:
- case HITGROUP_STOMACH:
- fpSound = "AndroidArmored.BulletImpact_1P_vs_3P"
- tpSound = "AndroidArmored.BulletImpact_3P_vs_3P"
- break
-
- default:
- fpSound = "AndroidVulnerable.BulletImpact_1P_vs_3P"
- tpSound = "AndroidVulnerable.BulletImpact_3P_vs_3P"
- break
- }
-
- if ( IsAlive( attacker ) && attacker.IsPlayer() )
- {
- EmitSoundOnEntityOnlyToPlayer( npc, attacker, fpSound )
- EmitSoundOnEntityExceptToPlayer( npc, attacker, tpSound )
- }
- else
- {
- EmitSoundOnEntity( npc, tpSound )
- }
-
- bool justAFleshWound = true
-
- switch ( hitGroup )
- {
- case HITGROUP_HEAD:
- thread StalkerHeadShot( npc, damageInfo, hitGroup )
- justAFleshWound = false
- break
-
- case HITGROUP_LEFTARM:
- if ( StalkerLimbBlownOff( npc, damageInfo, hitGroup, 0.085, "left_arm", [ "left_arm", "l_hand" ], "Spectre.Arm.Explode" ) )
- {
- npc.SetActivityModifier( ACT_MODIFIER_ONEHANDED, true )
-
- // Some of his synced melees depend on using his left arm
- npc.SetCapabilityFlag( bits_CAP_SYNCED_MELEE_ATTACK, false )
- }
- break
-
- case HITGROUP_LEFTLEG:
- justAFleshWound = TryLegBlownOff( npc, damageInfo, hitGroup, 0.17, "left_leg", [ "left_leg", "foot_L_sole" ], "Spectre.Leg.Explode" )
- break
-
- case HITGROUP_RIGHTLEG:
- justAFleshWound = TryLegBlownOff( npc, damageInfo, hitGroup, 0.17, "right_leg", [ "right_leg", "foot_R_sole" ], "Spectre.Leg.Explode" )
- break
- }
-
- return justAFleshWound
-}
-
-bool function PlayerHitGear( entity npc, var damageInfo, int hitGroup )
-{
- entity attacker = DamageInfo_GetAttacker( damageInfo )
-
- if ( !attacker.IsPlayer() )
- return false
-
- if ( hitGroup != HITGROUP_GEAR )
- return false
-
- if ( !( DamageInfo_GetCustomDamageType( damageInfo ) & DF_BULLET ) )
- return false
-
- return true
-}
-
-int function GetHitGroupFromDamageInfo( entity npc, var damageInfo )
-{
- int hitGroup = DamageInfo_GetHitGroup( damageInfo )
-
- if ( hitGroup <= HITGROUP_GENERIC )
- {
- int hitBox = DamageInfo_GetHitBox( damageInfo )
- if ( hitBox >= 0 )
- return GetHitgroupForHitboxOnEntity( npc, hitBox )
- }
-
- return hitGroup
-}
-
-bool function StalkerMeltingDown( entity npc )
-{
- int bodyGroup = npc.FindBodyGroup( "gear" )
- Assert( bodyGroup != -1 )
-
- // gear already blown up?
- return npc.GetBodyGroupState( bodyGroup ) != 0
-}
-
-void function StalkerGearOverloads( entity npc, entity attacker = null )
-{
- Assert( !StalkerMeltingDown( npc ) )
-
- if ( !IsCrawling( npc ) && StalkerCanCrawl( npc ) )
- thread FallAndBecomeCrawlingStalker( npc )
-
- int bodyGroup = npc.FindBodyGroup( "gear" )
-
- // hide gear
- npc.SetBodygroup( bodyGroup, 1 )
-
- string attachment = "CHESTFOCUS"
-
- npc.EndSignal( "OnDestroy" )
- npc.EndSignal( "OnDeath" )
-
- entity nukeFXInfoTarget = CreateEntity( "info_target" )
- nukeFXInfoTarget.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT
- DispatchSpawn( nukeFXInfoTarget )
-
- nukeFXInfoTarget.SetParent( npc, attachment )
-
- if ( attacker != null )
- {
- EmitSoundOnEntityOnlyToPlayer( nukeFXInfoTarget, attacker, STALKER_REACTOR_CRITIMPACT_SOUND_1P_VS_3P )
- EmitSoundOnEntityExceptToPlayer( nukeFXInfoTarget, attacker, STALKER_REACTOR_CRITIMPACT_SOUND_3P_VS_3P )
- }
- else
- {
- EmitSoundOnEntity( nukeFXInfoTarget, STALKER_REACTOR_CRITIMPACT_SOUND_3P_VS_3P )
- }
-
- EmitSoundOnEntity( nukeFXInfoTarget, STALKER_REACTOR_CRITICAL_SOUND )
-
- AI_CreateDangerousArea_DamageDef( damagedef_stalker_powersupply_explosion_small, nukeFXInfoTarget, TEAM_INVALID, true, false )
-
- entity fx = PlayFXOnEntity( STALKER_REACTOR_CRITICAL_FX, nukeFXInfoTarget )
-
- OnThreadEnd(
- function() : ( nukeFXInfoTarget, fx, npc, attacker )
- {
- if ( IsValid( npc ) )
- StopSoundOnEntity( nukeFXInfoTarget, STALKER_REACTOR_CRITICAL_SOUND )
-
- if ( IsValid( nukeFXInfoTarget ) )
- nukeFXInfoTarget.Destroy()
-
- if ( IsValid( fx ) )
- fx.Destroy()
-
- if ( IsAlive( npc ) )
- {
- entity damageAttacker
- if ( IsValid( attacker ) )
- damageAttacker = attacker
- else
- damageAttacker = npc
-
- vector force = GetDeathForce()
- npc.Die( damageAttacker, npc, { force = force, scriptType = DF_GIB, damageSourceId = eDamageSourceId.suicideSpectreAoE } )
- }
- }
- )
-
- wait 1.0
-
- float duration = 2.1
- float endTime = Time() + duration
- float startTime = Time()
-
- int tagID = npc.LookupAttachment( "CHESTFOCUS" )
-
- for ( ;; )
- {
- float timePassed = Time() - startTime
- float explodeMin = Graph( timePassed, 0, duration, 0.4, 0.1 )
- float explodeMax = explodeMin + Graph( timePassed, 0, duration, 0.21, 0.1 )
- wait RandomFloatRange( explodeMin, explodeMax )
-
- entity damageAttacker = GetNPCAttackerEnt( npc, attacker )
-
- // origin = npc.GetWorldSpaceCenter()
- vector origin = npc.GetAttachmentOrigin( tagID )
-
- if ( Time() >= endTime )
- {
- Explosion_DamageDefSimple( damagedef_stalker_powersupply_explosion_large, origin, damageAttacker, npc, origin )
- break
- }
- else
- {
- Explosion_DamageDefSimple( damagedef_stalker_powersupply_explosion_small, origin, damageAttacker, npc, origin )
- }
- }
-}
-
-bool function StalkerCanCrawl( entity npc )
-{
- if ( !IsAlive( npc ) )
- return false
-
- if ( npc.Anim_IsActive() )
- return false
-
- return true
-}
-
-bool function TryLegBlownOff( entity npc, var damageInfo, int hitGroup, float limbHealthPercentOfMax, string leg, array<string> fxTags, string sound )
-{
- if ( IsCrawling( npc ) )
- {
- // can blow off leg if stalker is already crawling
- StalkerLimbBlownOff( npc, damageInfo, hitGroup, limbHealthPercentOfMax, leg, fxTags, sound )
- return true
- }
-
- if ( !StalkerCanCrawl( npc ) )
- return true
-
- if ( StalkerLimbBlownOff( npc, damageInfo, hitGroup, limbHealthPercentOfMax, leg, fxTags, sound ) )
- {
- thread FallAndBecomeCrawlingStalker( npc )
- return false
- }
-
- return true
-}
-
-void function EnableStalkerCrawlingBehavior( entity npc )
-{
- Assert( StalkerCanCrawl( npc ) )
- Assert( !IsCrawling( npc ) )
-
- DisableLeeching( npc )
-
- DisableMinionUsesHeavyWeapons( npc )
-
- string crawlingSettings = string ( npc.Dev_GetAISettingByKeyField( "crawlingSettingsWrapper" ) )
-
- // Changing the setting file includes changing the behavior file to "behavior_stalker_crawling"
- SetAISettingsWrapper( npc, crawlingSettings )
-
- npc.ai.crawling = true
- npc.ai.startCrawlingTime = Time()
- npc.DisableGrappleAttachment()
- npc.EnableNPCMoveFlag( NPCMF_DISABLE_ARRIVALS )
- npc.SetCapabilityFlag( bits_CAP_MOVE_TRAVERSE | bits_CAP_MOVE_SHOOT | bits_CAP_WEAPON_RANGE_ATTACK1 | bits_CAP_AIM_GUN, false )
- npc.SetActivityModifier( ACT_MODIFIER_CRAWL, true )
- npc.SetActivityModifier( ACT_MODIFIER_STAGGER, false )
- npc.SetCanBeGroundExecuted( true )
- npc.ClearMoveAnim()
-
- npc.SetHealth( npc.GetMaxHealth() * 0.5 )
-
- npc.SetAimAssistForcePullPitchEnabled( true )
-
- thread SelfTerminateAfterDelay( npc )
-}
-
-void function SelfTerminateAfterDelay( entity npc )
-{
- const float lifeSupportDuration = 8
- float deathTime = Time() + (lifeSupportDuration * 2)
-
- npc.EndSignal( "OnDeath" )
- for ( ;; )
- {
- entity enemy = npc.GetEnemy()
- if ( IsAlive( enemy ) )
- {
- if ( Distance( npc.GetEnemyLKP(), npc.GetOrigin() ) < 500 )
- {
- if ( npc.TimeSinceSeen( enemy ) < 3 )
- deathTime = max( Time() + lifeSupportDuration, deathTime )
- }
- }
-
- if ( Time() > deathTime )
- {
- npc.Die()
- return
- }
-
- wait 1.0
- }
-}
-
-void function FallAndBecomeCrawlingStalker( entity npc )
-{
- // finish what he's doing
- npc.EndSignal( "OnDeath" )
-
- npc.ai.transitioningToCrawl = true
-
- // Workaround for Bug 114372
- WaitFrame()
-
- for ( ;; )
- {
- if ( npc.IsInterruptable() )
- break
- WaitFrame()
- }
-
- if ( !StalkerCanCrawl( npc ) )
- return
-
- if ( IsCrawling( npc ) )
- return
-
- EnableStalkerCrawlingBehavior( npc )
-
- npc.Anim_Stop() // stop leeching, etc.
-
- PlayCrawlingAnim( npc, "ACT_STAND_TO_CRAWL" )
-}
-
-void function PlayCrawlingAnim( entity npc, string animation )
-{
- npc.Anim_ScriptedPlayActivityByName( animation, true, 0.1 )
- npc.UseSequenceBounds( true )
-}
-
-void function AttemptStandToStaggerAnimation( entity npc )
-{
- // Check if we are already staggered
- if ( npc.IsActivityModifierActive( ACT_MODIFIER_STAGGER ) )
- return
-
- if ( !npc.IsInterruptable() )
- return
-
- if ( npc.ContextAction_IsBusy() )
- return
-
- // Are we blocking additional pain animations
- if ( npc.GetNPCFlag( NPC_NO_PAIN ) )
- return
-
- // finish what he's doing
- npc.EndSignal( "OnDeath" )
-
- // Workaround for Bug 114372
- WaitFrame()
-
- for ( ;; )
- {
- if ( npc.IsInterruptable() )
- break
-
- WaitFrame()
- }
-
- if ( IsCrawling( npc ) || npc.ai.transitioningToCrawl )
- return
-
- npc.Anim_ScriptedPlayActivityByName( "ACT_STAND_TO_STAGGER", true, 0.1 )
- npc.UseSequenceBounds( true )
- npc.EnableNPCFlag( NPC_PAIN_IN_SCRIPTED_ANIM )
-}
-
-bool function IsStalkerLimbBlownOff( entity npc, string limbName )
-{
- int bodyGroup = npc.FindBodyGroup( limbName )
- if ( npc.GetBodyGroupState( bodyGroup ) != 0 )
- return true
-
- return false
-}
-
-bool function StalkerLimbBlownOff( entity npc, var damageInfo, int hitGroup, float limbHealthPercentOfMax, string limbName, array<string> fxTags, string sound )
-{
- int damageSourceId = DamageInfo_GetDamageSourceIdentifier( damageInfo )
- switch ( damageSourceId )
- {
- case eDamageSourceId.mp_weapon_grenade_emp:
- case eDamageSourceId.mp_weapon_proximity_mine:
- return false
- }
-
- int bodyGroup = npc.FindBodyGroup( limbName )
- if ( bodyGroup == -1 )
- return false
-
- if ( IsStalkerLimbBlownOff( npc, limbName ) )
- return false
-
- EmitSoundOnEntity( npc, sound )
-
- // blow off limb
- npc.SetBodygroup( bodyGroup, 1 )
-
- return true
-}
-
-void function StalkerHeadShot( entity npc, var damageInfo, int hitGroup )
-{
- // random chance to blow up head
-// if ( DamageInfo_GetDamage( damageInfo ) < 100 && RandomFloat( 100 ) <= 66 )
-// return
-
- if ( !IsValidHeadShot( damageInfo, npc ) )
- return
-
- // only players score headshots
- entity attacker = DamageInfo_GetAttacker( damageInfo )
- if ( !IsAlive( attacker ) )
- return
- if ( !attacker.IsPlayer() )
- return
-
- if ( DamageInfo_GetDamage( damageInfo ) < npc.GetHealth() )
- {
- // force lethal if we have done more than this much damage
- if ( npc.ai.stalkerHitgroupDamageAccumulated[ hitGroup ] < npc.GetMaxHealth() * STALKER_DAMAGE_REQUIRED_TO_HEADSHOT )
- return
- }
-
- npc.Anim_Stop() // stop leeching, etc.
- npc.ClearParent()
-
- //DisableLeeching( npc )
-
- // No pain anims
- //DamageInfo_AddDamageFlags( damageInfo, DAMAGEFLAG_NOPAIN )
-
- // Set these so cl_player knows to kill the eye glow and play the right SFX
- DamageInfo_AddCustomDamageType( damageInfo, DF_HEADSHOT )
- DamageInfo_AddCustomDamageType( damageInfo, DF_KILLSHOT )
-
- EmitSoundOnEntityExceptToPlayer( npc, attacker, "SuicideSpectre.BulletImpact_HeadShot_3P_vs_3P" )
-
- int bodyGroupIndex = npc.FindBodyGroup( "removableHead" )
- int stateIndex = 1 // 0 = show, 1 = hide
- npc.SetBodygroup( bodyGroupIndex, stateIndex )
-
- DamageInfo_SetDamage( damageInfo, npc.GetMaxHealth() )
-}
-
-vector function GetDeathForce()
-{
- vector angles = <RandomFloatRange(-45,-75),RandomFloat(360),0>
- vector forward = AnglesToForward( angles )
- return forward * RandomFloatRange( 0.25, 0.75 )
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut
deleted file mode 100644
index 50b6cc759..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_stationary_firing_positions.gnut
+++ /dev/null
@@ -1,261 +0,0 @@
-global function AddStationaryAIPosition //Add stationary positions to pending list.
-global function AddTestTargetPosForStationaryPositionValidation //Add test target location for validating stationary positions.
-global function ValidateAndFinalizePendingStationaryPositions //Runs error-checking/validation logic on stationary positions and finalizes them for use by AI.
-global function GetRandomStationaryPosition
-global function GetClosestAvailableStationaryPosition
-global function ClaimStationaryAIPosition
-global function ReleaseStationaryAIPosition
-
-global enum eStationaryAIPositionTypes
-{
- MORTAR_TITAN,
- MORTAR_SPECTRE,
- SNIPER_TITAN,
- LAUNCHER_REAPER
-}
-
-global struct StationaryAIPosition
-{
- vector origin
- bool inUse
-}
-
-global struct ArrayDistanceEntryForStationaryAIPosition
-{
- float distanceSqr
- StationaryAIPosition& ent
- vector origin
-}
-
-struct
-{
- array<vector> validationTestTargets
- table<int, array<vector> > pendingPositions
- table<int, array<StationaryAIPosition> > stationaryPositions
-} file
-
-void function AddTestTargetPosForStationaryPositionValidation( vector origin )
-{
- file.validationTestTargets.append( origin )
-}
-
-void function AddStationaryAIPosition( vector origin, int type )
-{
- AddPendingStationaryAIPosition_Internal( origin, type )
-}
-
-void function AddStationaryAIPosition_Internal( vector origin, int type )
-{
- StationaryAIPosition pos
- pos.origin = origin
- pos.inUse = false
-
- //Throw warnings for bad positions
- foreach ( vector testTarget in file.validationTestTargets )
- {
- switch( type )
- {
- case eStationaryAIPositionTypes.MORTAR_TITAN:
- if ( NavMesh_ClampPointForHullWithExtents( origin, HULL_TITAN, <100, 100, 20> ) == null )
- {
- CodeWarning( "Mortar Titan Firing Position at " + origin + " does not have enough space to accomidate Titan, skipping." )
- return
- }
- break
-
- #if MP
- case eStationaryAIPositionTypes.MORTAR_SPECTRE:
-
- array<vector> testLocations = MortarSpectreGetSquadFiringPositions( origin, testTarget )
-
- foreach ( vector testLocation in testLocations )
- {
- if ( NavMesh_ClampPointForHullWithExtents( testLocation, HULL_HUMAN, <100, 100, 20> ) == null )
- {
- CodeWarning( "Mortar Spectre Firing Position at " + origin + " does not have enough space to accomidate squad, skipping." )
- return
- }
- }
-
- break
- #endif //MP
-
- case eStationaryAIPositionTypes.SNIPER_TITAN:
- if ( NavMesh_ClampPointForHullWithExtents( origin, HULL_TITAN, <100, 100, 20> ) == null )
- {
- CodeWarning( "Sniper Titan Firing Position at " + origin + " does not have enough space to accomidate Titan, skipping." )
- return
- }
- break
-
- case eStationaryAIPositionTypes.LAUNCHER_REAPER:
- if ( NavMesh_ClampPointForHullWithExtents( origin, HULL_MEDIUM, <100, 100, 20> ) == null )
- {
- CodeWarning( "Tick Launching Reaper Firing Position at " + origin + " does not have enough space to accomidate Reaper, skipping." )
- return
- }
- break
- }
- }
-
- if ( !( type in file.stationaryPositions ) )
- {
- file.stationaryPositions[ type ] <- []
- }
-
- file.stationaryPositions[ type ].append( pos )
-}
-
-//Function tests stationary AI positions for given type relative to given mortar target.
-void function AddPendingStationaryAIPosition_Internal( vector origin, int type )
-{
- if ( !( type in file.pendingPositions ) )
- file.pendingPositions[ type ] <- []
-
- //Add position to table so we can validate and add it when all entities finish loading.
- file.pendingPositions[ type ].append( origin )
-}
-
-void function ValidateAndFinalizePendingStationaryPositions()
-{
-
- Assert( file.validationTestTargets.len(), "Test targets are required to validate stationary positions. Use AddTestTargetPosForStationaryPositionValidation to add them before running validation." )
-
- foreach ( type, origins in file.pendingPositions )
- {
- //Make sure we have pending positions for given ai type.
- Assert( file.pendingPositions[ type ].len(), "Stationary Positions for type " + type + " could not be found in this map. Add Some." )
-
- foreach ( vector origin in origins )
- {
- AddStationaryAIPosition_Internal( origin, type )
- }
-
- //Make sure we have positions for given AI type after we validate and finalize positions.
- Assert( file.stationaryPositions[ type ].len(), "No valid stationary positions for type " + type + " remain after validation. Adjust positions and retry." )
- }
-}
-
-StationaryAIPosition function GetClosestAvailableStationaryPosition( vector origin, float maxDist, int type )
-{
-
- array<StationaryAIPosition> resultArray = []
- float maxDistSqr = maxDist * maxDist
-
- array<StationaryAIPosition> positions = file.stationaryPositions[type]
-
- array<ArrayDistanceEntryForStationaryAIPosition> allResults = ArrayDistanceResultsForStationaryAIPosition( positions, origin )
- allResults.sort( DistanceCompareClosestForStationaryAIPosition )
-
- //Remove all in use stationary positions up front.
- array<ArrayDistanceEntryForStationaryAIPosition> freePositions
- foreach ( result in allResults )
- {
- StationaryAIPosition position = result.ent
- if ( position.inUse )
- continue
-
- freePositions.append( result )
- }
-
- //Tell us if all spots for a given AI type are taken.
- Assert( freePositions.len() > 0, "Could not find free mortar positions for type " + type + ", all positions are currently in use. Add more AddStationaryTitanPosition to the map." )
-
- foreach( result in freePositions )
- {
- StationaryAIPosition position = result.ent
-
- // if too far, throw warning and continue search beyond maxDist
- if ( result.distanceSqr > maxDistSqr )
- {
- CodeWarning( "Couldn't find a mortar position within " + maxDist + " units for type " + type + " around " + origin.tostring() + " that wasn't in use. Expanding Search. Add more AddStationaryTitanPositions to the map near this point." )
- }
-
- return position
- }
-
- unreachable
-}
-
-StationaryAIPosition function GetRandomStationaryPosition( vector origin, float maxDist, int type )
-{
- array<StationaryAIPosition> resultArray = []
- array<StationaryAIPosition> positions = file.stationaryPositions[type]
-
- //Remove all in use stationary positions up front.
- array<StationaryAIPosition> freePositions
- foreach ( position in positions )
- {
- if ( position.inUse )
- continue
-
- freePositions.append( position )
- }
-
- //Tell us if all spots for a given AI type are taken.
- Assert( freePositions.len() > 0, "Could not find free mortar positions for type " + type + ", all positions are currently in use. Add more AddStationaryTitanPosition to the map." )
-
- int attemptCount = 1
- while ( resultArray.len() == 0 )
- {
-
- //Expand our search radius each time we reattempt our search.
- float maxDistSqr = ( maxDist * attemptCount ) * ( maxDist * attemptCount )
-
- foreach( position in freePositions )
- {
- float dist = Distance2DSqr( origin, position.origin )
- if ( dist <= maxDistSqr )
- resultArray.append( position )
- }
-
- if ( resultArray.len() == 0 )
- {
- CodeWarning( "Couldn't find a mortar position within " + maxDist + " units for type " + type + " around " + origin.tostring() + " that wasn't in use. Expanding Search. Add more AddStationaryTitanPositions to the map near this point." )
- attemptCount += 1
- }
- }
-
- return resultArray.getrandom()
-}
-
-void function ClaimStationaryAIPosition( StationaryAIPosition stationaryTitanPositions )
-{
- Assert( stationaryTitanPositions.inUse == false )
- stationaryTitanPositions.inUse = true
-}
-
-void function ReleaseStationaryAIPosition( StationaryAIPosition stationaryTitanPositions )
-{
- Assert( stationaryTitanPositions.inUse == true )
- stationaryTitanPositions.inUse = false
-}
-
-array<ArrayDistanceEntryForStationaryAIPosition> function ArrayDistanceResultsForStationaryAIPosition( array<StationaryAIPosition> entArray, vector origin )
-{
- array<ArrayDistanceEntryForStationaryAIPosition> allResults
-
- foreach ( ent in entArray )
- {
- ArrayDistanceEntryForStationaryAIPosition entry
-
- vector entOrigin = ent.origin
- entry.distanceSqr = DistanceSqr( entOrigin, origin )
- entry.ent = ent
- entry.origin = entOrigin
-
- allResults.append( entry )
- }
-
- return allResults
-}
-
-int function DistanceCompareClosestForStationaryAIPosition( ArrayDistanceEntryForStationaryAIPosition a, ArrayDistanceEntryForStationaryAIPosition b )
-{
- if ( a.distanceSqr > b.distanceSqr )
- return 1
- else if ( a.distanceSqr < b.distanceSqr )
- return -1
-
- return 0;
-} \ No newline at end of file
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 )
- }
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_turret.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_turret.gnut
deleted file mode 100644
index eca5849bf..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_turret.gnut
+++ /dev/null
@@ -1,24 +0,0 @@
-global function AiTurret_Init
-global function GetMegaTurretLinkedToPanel
-global function MegaTurretUsabilityFunc
-global function SetUsePromptForPanel
-
-void function AiTurret_Init()
-{
-
-}
-
-entity function GetMegaTurretLinkedToPanel(entity panel)
-{
- return null
-}
-
-string function MegaTurretUsabilityFunc(var turret, var panel)
-{
- return "pilot"
-}
-
-void function SetUsePromptForPanel(var panel, var turret)
-{
-
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_utility.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_utility.gnut
deleted file mode 100644
index 67c686003..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_ai_utility.gnut
+++ /dev/null
@@ -1,558 +0,0 @@
-untyped
-
-globalize_all_functions
-
-function AiUtility_Init()
-{
- RegisterSignal( "OnNewOwner" )
- RegisterSignal( "squadInCombat" )
- RegisterSignal( "OnEndFollow" )
- RegisterSignal( "OnStunned" )
-
-}
-////////////////////////////////////////////////////////////////////////////////
-// Cloaks npc forever (to be used by anim events)
-function NpcCloakOn( npc )
-{
- //SetCloakDuration( fade in, duration, fade out )
- npc.SetCloakDuration( 2.0, -1, 0 )
- EmitSoundOnEntity( npc, CLOAKED_DRONE_CLOAK_START_SFX )
- EmitSoundOnEntity( npc, CLOAKED_DRONE_CLOAK_LOOP_SFX )
- npc.Minimap_Hide( TEAM_IMC, null )
- npc.Minimap_Hide( TEAM_MILITIA, null )
-}
-////////////////////////////////////////////////////////////////////////////////
-// De-cloaks npc
-function NpcCloakOff( npc)
-{
- npc.SetCloakDuration( 0, 0, 1.5 )
- StopSoundOnEntity( npc, CLOAKED_DRONE_CLOAK_LOOP_SFX )
- npc.Minimap_AlwaysShow( TEAM_IMC, null )
- npc.Minimap_AlwaysShow( TEAM_MILITIA, null )
-}
-
-int function GetDefaultNPCFollowBehavior( npc )
-{
- switch ( npc.GetAIClass() )
- {
- case AIC_FLYING_DRONE:
- return AIF_SUPPORT_DRONE
-
- case AIC_VEHICLE:
- return AIF_GUNSHIP
-
- case AIC_TITAN:
- case AIC_TITAN_BUDDY:
- return AIF_TITAN_FOLLOW_PILOT
- }
-
- return AIF_FIRETEAM
-}
-
-void function DieOnPlayerDisconnect( entity npc, entity player )
-{
- Assert( IsNewThread(), "Must be threaded off" )
- Assert( npc.IsNPC() )
- Assert( player.IsPlayer() )
- Assert( IsAlive( npc ) )
- Assert( npc.GetBossPlayer() == player )
- Assert( !IsDisconnected( player ) )
- npc.EndSignal( "OnDeath" )
-
- player.WaitSignal( "OnDestroy" )
-
- // my boss quit the server!
- if ( IsAlive( npc ) && npc.GetBossPlayer() == player )
- npc.Die()
-}
-
-void function NPCFollowsPlayer( entity npc, entity leader )
-{
- Assert( IsAlive( npc ) )
- Assert( leader.IsPlayer() )
-
- npc.SetBossPlayer( leader )
-
- // team
- SetTeam( npc, leader.GetTeam() )
-
- if ( IsSpectre( npc ) )
- {
- string squadName = GetPlayerSpectreSquadName( leader )
- SetSquad( npc, squadName )
- }
-
- thread DieOnPlayerDisconnect( npc, leader )
- #if SP
- Highlight_SetFriendlyHighlight( npc, "friendly_ai" )
- #else
- Highlight_SetOwnedHighlight( npc, "friendly_ai" )
- #endif
-
- NpcFollowsEntity( npc, leader )
-}
-
-void function NPCFollowsNPC( entity npc, entity leader )
-{
- Assert( IsAlive( npc ) )
- Assert( IsAlive( leader ) )
- Assert( leader.IsNPC() )
-
- // team
- SetTeam( npc, leader.GetTeam() )
-
- // squad
- string squadNameOwner = expect string( leader.Get( "squadname" ) )
- if ( squadNameOwner != "" && leader.GetClassName() == npc.GetClassName() )
- SetSquad( npc, squadNameOwner )
-
- NpcFollowsEntity( npc, leader )
-}
-
-void function NpcFollowsEntity( entity npc, entity leader )
-{
- // stop scripted things
- if ( IsMultiplayer() )
- npc.Signal( "StopHardpointBehavior" )
-
- if ( leader.IsPlayer() && leader.p.followPlayerOverride != null )
- {
- leader.p.followPlayerOverride( npc, leader )
- return
- }
-
- // follow!
- int followBehavior = GetDefaultNPCFollowBehavior( npc )
- npc.InitFollowBehavior( leader, followBehavior )
- npc.DisableBehavior( "Assault" )
- npc.DisableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_USE_SHOOTING_COVER )
- npc.EnableBehavior( "Follow" )
-}
-
-
-/////////////////////////////////////////////////////////////////////////////////////////////////
-bool function HasEnemyWithinDist( entity npc, float dist )
-{
- float distSq = dist * dist
-
- array<entity> enemies
- entity closestEnemy = npc.GetClosestEnemy()
- if ( closestEnemy )
- enemies.append( closestEnemy )
-
- entity currentEnemy = npc.GetEnemy()
- if ( currentEnemy && currentEnemy != closestEnemy )
- enemies.append( currentEnemy )
-
- if ( !enemies.len() )
- return false
-
- vector origin = npc.GetOrigin()
- foreach ( enemy in enemies )
- {
- if ( DistanceSqr( origin, enemy.GetOrigin() ) < distSq )
- return true
- }
-
- return false
-}
-
-SpawnPointFP function FindSpawnPointForNpcCallin( entity npc, asset model, string anim )
-{
- float yaw = npc.EyeAngles().y
-
- vector npcView = AnglesToForward( npc.EyeAngles() )
- FlightPath flightPath = GetAnalysisForModel( model, anim )
-
- CallinData drop
- InitCallinData( drop )
- SetCallinStyle( drop, eDropStyle.NEAREST_YAW_FALLBACK )
- SetCallinOwnerEyePos( drop, npc.EyePosition() )
- drop.dist = 800
- drop.origin = npc.GetOrigin() + npcView * 250
- drop.yaw = yaw
-
- vector angles = Vector( 0, yaw, 0 )
- SpawnPointFP spawnPoint = GetSpawnPointForStyle( flightPath, drop )
- if ( spawnPoint.valid )
- return spawnPoint
-
- //if it didn't find one where he was looking - try near him
- drop.origin = npc.GetOrigin()
- spawnPoint = GetSpawnPointForStyle( flightPath, drop )
-
- return spawnPoint
-}
-
-function WaitForSquadInCombat( squad )
-{
- local master = {}
-
- //when the thread ends, let child threads now
- OnThreadEnd(
- function() : ( master )
- {
- Signal( master, "OnDestroy" )
- }
- )
-
- // this internal function keeps track of each guy
- local combatTracker =
- function( guy, master )
- {
- expect entity( guy )
- expect entity( master )
-
- EndSignal( master, "OnDestroy" )
- EndSignal( guy, "OnDeath", "OnDestroy" )
- if ( !IsAlive( guy ) )
- return
-
- while ( guy.GetNPCState() != "combat" )
- guy.WaitSignal( "OnStateChange" )
-
- Signal( master, "squadInCombat" )
- }
-
- foreach ( guy in squad )
- {
- thread combatTracker( guy, master )
- }
-
- WaitSignal( master, "squadInCombat" )
-}
-
-function WaitForNpcInCombat( npc )
-{
- while ( npc.GetNPCState() != "combat" )
- npc.WaitSignal( "OnStateChange" )
-}
-
-int function GetNpcHullType( entity npc )
-{
- string aiSettings = npc.GetAISettingsName()
- return int ( Dev_GetAISettingByKeyField_Global( aiSettings, "HullType" ) )
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////
-// "SPAWN AI" DEV MENU Fuctions
-//////////////////////////////////////////////////////////////////////////////////////////////////////
-const float CROSSHAIR_VERT_OFFSET = 32
-
-vector function GetPlayerCrosshairOriginRaw( entity player )
-{
- vector angles = player.EyeAngles()
- vector forward = AnglesToForward( angles )
- vector origin = player.EyePosition()
-
- vector start = origin
- vector end = origin + forward * 50000
- TraceResults result = TraceLine( start, end )
- vector crosshairOrigin = result.endPos
-
- return crosshairOrigin
-}
-
-vector function GetPlayerCrosshairOrigin( entity player )
-{
- return (GetPlayerCrosshairOriginRaw( player ) + Vector( 0, 0, CROSSHAIR_VERT_OFFSET ))
-}
-
-void function DEV_SpawnBTAtCrosshair( bool hotdrop = false )
-{
- DisablePrecacheErrors()
- wait 0.2
- entity player = GetPlayerArray()[ 0 ]
-
- entity pet_titan = player.GetPetTitan()
- if ( IsValid(pet_titan) )
- pet_titan.Destroy()
-
- vector origin = GetPlayerCrosshairOrigin( player )
- vector angles = Vector( 0, 0, 0 )
-
- TitanLoadoutDef loadout = GetTitanLoadoutForCurrentMap()
- entity npc = CreateAutoTitanForPlayer_FromTitanLoadout( player, loadout, origin, angles )
-
- SetSpawnOption_AISettings( npc, "npc_titan_buddy" )
-
- DispatchSpawn( npc )
-
- SetPlayerPetTitan( player, npc )
-
- if ( hotdrop )
- thread NPCTitanHotdrops( npc, false )
-}
-
-void function DEV_SpawnAllNPCsWithTeam( int team )
-{
- printt( "script thread DEV_SpawnAllNPCsWithTeam( " + team + " )" )
- Assert( IsNewThread(), "Must be threaded off due to precache issues" )
- bool restoreHostThreadMode = GetConVarInt( "host_thread_mode" ) != 0
- if ( restoreHostThreadMode )
- {
- DisablePrecacheErrors()
- wait 0.5
- }
-
- entity player = GetPlayerArray()[ 0 ]
- vector origin = GetPlayerCrosshairOrigin( player )
- array<string> aiSettings = GetAllNPCSettings()
-
- foreach ( settings in aiSettings )
- {
- vector angles = < 0, RandomFloat( 360 ), 0 >
- entity npc = CreateNPCFromAISettings( settings, team, origin, angles )
- DispatchSpawn( npc )
- }
-
- if ( restoreHostThreadMode )
- {
- wait 0.2
- RestorePrecacheErrors()
- }
-}
-
-void function DEV_SpawnNPCWithWeaponAtCrosshair( string baseClass, string aiSettings, int team, string weaponName = "" )
-{
- printt( "script thread DEV_SpawnNPCWithWeaponAtCrosshair( \"" + baseClass + "\", \"" + aiSettings + "\", " + team + ", \"" + weaponName + "\")" )
- Assert( IsNewThread(), "Must be threaded off due to precache issues" )
- bool restoreHostThreadMode = GetConVarInt( "host_thread_mode" ) != 0
- entity npc = DEV_SpawnNPCWithWeaponAtCrosshairStart( restoreHostThreadMode, baseClass, aiSettings, team, weaponName )
- DispatchSpawn( npc )
- DEV_SpawnNPCWithWeaponAtCrosshairEnd( restoreHostThreadMode )
-}
-
-void function DEV_SpawnMercTitanAtCrosshair( string mercName )
-{
- printt( "script thread DEV_SpawnMercTitanAtCrosshair( \"" + mercName + "\")" )
- Assert( IsNewThread(), "Must be threaded off due to precache issues" )
- TitanLoadoutDef ornull loadout = GetTitanLoadoutForBossCharacter( mercName )
- if ( loadout == null )
- return
- expect TitanLoadoutDef( loadout )
- string baseClass = "npc_titan"
- string aiSettings = GetNPCSettingsFileForTitanPlayerSetFile( loadout.setFile )
-
- bool restoreHostThreadMode = GetConVarInt( "host_thread_mode" ) != 0
- entity npc = DEV_SpawnNPCWithWeaponAtCrosshairStart( restoreHostThreadMode, baseClass, aiSettings, TEAM_IMC )
- SetSpawnOption_NPCTitan( npc, TITAN_MERC )
- SetSpawnOption_TitanLoadout( npc, loadout )
- npc.ai.bossTitanPlayIntro = false
-
- DispatchSpawn( npc )
- DEV_SpawnNPCWithWeaponAtCrosshairEnd( restoreHostThreadMode )
-}
-
-void function DEV_SpawnWeaponAtCrosshair( string weaponName )
-{
- printt( "script thread DEV_SpawnWeaponAtCrosshair( \"" + weaponName + "\")" )
-
- Assert( IsNewThread(), "Must be threaded off due to precache issues" )
-
- entity player = GetPlayerArray()[ 0 ]
- if ( !IsValid( player ) )
- return
- vector origin = GetPlayerCrosshairOrigin( player )
- vector angles = Vector( 0, 0, 0 )
- entity weapon = CreateWeaponEntityByNameWithPhysics( weaponName, origin, angles )
-
-#if SP
- bool isTitanWeapon = weaponName.find( "mp_titanweapon_" ) != null
- if ( isTitanWeapon )
- thread TitanLoadoutWaitsForPickup( weapon, SPTitanLoadoutPickup )
-#endif
-
-}
-
-string function GetAISettingsFromPlayerSetFile( string playerSetfile )
-{
- TitanLoadoutDef ornull loadout = GetTitanLoadoutForColumn( "setFile", playerSetfile )
- Assert( loadout != null, "Couldn't find loadout with set file " + playerSetfile )
- expect TitanLoadoutDef( loadout )
-
- return expect string( Dev_GetPlayerSettingByKeyField_Global( playerSetfile, GetAISettingsStringForMode() ) )
-}
-
-
-void function DEV_SpawnBossTitanAtCrosshair( string playerSetfile )
-{
- string aiSettings = GetAISettingsFromPlayerSetFile( playerSetfile )
- printt( "script thread DEV_SpawnBossTitanAtCrosshair( \"" + aiSettings + "\")" )
- Assert( IsNewThread(), "Must be threaded off due to precache issues" )
-
- string baseClass = "npc_titan"
- bool restoreHostThreadMode = GetConVarInt( "host_thread_mode" ) != 0
- entity npc = DEV_SpawnNPCWithWeaponAtCrosshairStart( restoreHostThreadMode, baseClass, aiSettings, TEAM_IMC )
- SetSpawnOption_NPCTitan( npc, TITAN_BOSS )
-// SetSpawnOption_TitanLoadout( npc, loadout )
-
- string builtInLoadout = expect string( Dev_GetAISettingByKeyField_Global( aiSettings, "npc_titan_player_settings" ) )
-// SetTitanSettings( npc.ai.titanSettings, builtInLoadout )
- npc.ai.titanSpawnLoadout.setFile = builtInLoadout
- OverwriteLoadoutWithDefaultsForSetFile( npc.ai.titanSpawnLoadout ) // get the entire loadout, including defensive and tactical
-
- DispatchSpawn( npc )
- DEV_SpawnNPCWithWeaponAtCrosshairEnd( restoreHostThreadMode )
-}
-
-entity function DEV_SpawnNPCWithWeaponAtCrosshairStart( bool restoreHostThreadMode, string baseClass, string aiSettings, int team, string weaponName = "" )
-{
- if ( restoreHostThreadMode )
- {
- DisablePrecacheErrors()
- wait 0.5
- }
-
- float time = Time()
- for ( ;; )
- {
- if ( Time() > time )
- break
- WaitFrame()
- }
- entity player = GetPlayerArray()[ 0 ]
- if ( !IsValid( player ) )
- return
-
- vector origin = GetPlayerCrosshairOrigin( player )
- vector angles = Vector( 0, 0, 0 )
-
- entity npc = CreateNPC( baseClass, team, origin, angles )
- if ( IsTurret( npc ) )
- npc.kv.origin -= Vector( 0, 0, CROSSHAIR_VERT_OFFSET )
- SetSpawnOption_AISettings( npc, aiSettings )
-
- if ( npc.GetClassName() == "npc_soldier" || npc.GetClassName() == "npc_spectre" )
- npc.kv.squadname = "crosshairSpawnSquad_team_" + team + "_" + baseClass + "_" + aiSettings
-
- if ( weaponName != "" )
- SetSpawnOption_Weapon( npc, weaponName )
-
- if ( npc.GetClassName() == "npc_titan" )
- {
- string builtInLoadout = expect string( Dev_GetAISettingByKeyField_Global( aiSettings, "npc_titan_player_settings" ) )
- SetTitanSettings( npc.ai.titanSettings, builtInLoadout )
- npc.ai.titanSpawnLoadout.setFile = builtInLoadout
- OverwriteLoadoutWithDefaultsForSetFile( npc.ai.titanSpawnLoadout ) // get the entire loadout, including defensive and tactical
- }
-
- return npc
-}
-
-void function DEV_SpawnNPCWithWeaponAtCrosshairEnd( bool restoreHostThreadMode )
-{
- if ( restoreHostThreadMode )
- {
- wait 0.2
- RestorePrecacheErrors()
- }
-}
-
-
-function SetAISettingsWrapper( entity npc, string settings )
-{
- npc.SetAISettings( settings )
- Assert( settings.find( npc.GetClassName() ) == 0, "NPC classname " + npc.GetClassName() + " not found in " + settings )
-
- if ( IsSingleplayer() )
- {
- FixupTitle( npc )
- }
-}
-
-bool function WithinEngagementRange( entity npc, vector origin )
-{
- entity weapon = npc.GetActiveWeapon()
- if ( weapon == null )
- return false
-
- float dist = Distance( npc.GetOrigin(), origin )
- if ( dist < weapon.GetWeaponInfoFileKeyField( "npc_min_engage_range" ) )
- return false
-
- return dist <= weapon.GetWeaponInfoFileKeyField( "npc_max_engage_range" )
-}
-
-
-function DEV_AITitanDuel()
-{
- thread DEV_AITitanDuelThread()
-}
-
-entity function DEV_AITitanDuelSpawn( entity player, int team, vector origin, vector angles, aiSetting )
-{
- entity titan = CreateNPC( "npc_titan", team, origin, angles )
- SetSpawnOption_AISettings( titan, aiSetting )
- DispatchSpawn( titan )
-
- vector ornull clampedPos = NavMesh_ClampPointForAI( origin, titan )
- if ( clampedPos != null )
- {
- titan.SetOrigin( expect vector( clampedPos ) )
- }
- else
- {
- array<entity> spawnpoints = SpawnPoints_GetTitan()
- if ( spawnpoints.len() )
- {
- entity spawnpoint = GetClosest( spawnpoints, origin )
- titan.SetOrigin( spawnpoint.GetOrigin() )
- }
- }
-
- return titan
-}
-
-function DEV_AITitanDuelThread()
-{
- DisablePrecacheErrors()
- wait 0.5
-
- array<string> aiSettings = GetAllowedTitanAISettings()
-
- aiSettings.randomize()
-
- entity player = GetPlayerArray()[ 0 ]
-
- entity imcTitan = null
- entity militiaTitan = null
-
- int currentSetting = 0
-
-
- while ( 1 )
- {
- if ( !IsValid( imcTitan ) )
- {
- vector origin = GetPlayerCrosshairOrigin( player ) + < -300, -300, 0 >
- vector angles = Vector( 0, 0, 0 )
-
- imcTitan = DEV_AITitanDuelSpawn( player, TEAM_IMC, origin, angles, aiSettings[currentSetting] )
- currentSetting = (currentSetting + 1) % aiSettings.len()
-
- if ( IsValid( militiaTitan ) )
- {
- imcTitan.SetEnemyLKP( militiaTitan, militiaTitan.GetOrigin() )
- militiaTitan.SetEnemyLKP( imcTitan, imcTitan.GetOrigin() )
- }
- }
-
- if ( !IsValid( militiaTitan ) )
- {
- vector origin = GetPlayerCrosshairOrigin( player ) + < 300, 300, 0 >
- vector angles = Vector( 0, 180, 0 )
-
- militiaTitan = DEV_AITitanDuelSpawn( player, TEAM_MILITIA, origin, angles, aiSettings[currentSetting] )
- currentSetting = (currentSetting + 1) % aiSettings.len()
-
- if ( IsValid( imcTitan ) )
- {
- militiaTitan.SetEnemyLKP( imcTitan, imcTitan.GetOrigin() )
- imcTitan.SetEnemyLKP( militiaTitan, militiaTitan.GetOrigin() )
- }
- }
-
- wait 2
- }
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_droppod.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_droppod.gnut
deleted file mode 100644
index 847efa8b8..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_droppod.gnut
+++ /dev/null
@@ -1,6 +0,0 @@
-global function DropPod_Init
-
-void function DropPod_Init()
-{
-
-} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_droppod_fireteam.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_droppod_fireteam.gnut
deleted file mode 100644
index b93631ac8..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_droppod_fireteam.gnut
+++ /dev/null
@@ -1,246 +0,0 @@
-global function DropPodFireteam_Init
-
-global function InitFireteamDropPod
-global function ActivateFireteamDropPod
-global function DropPodActiveThink
-
-global function CreateDropPodDoor
-
-const DP_ARM_MODEL = $"models/vehicle/droppod_fireteam/droppod_fireteam_arm.mdl"
-const DP_DOOR_MODEL = $"models/vehicle/droppod_fireteam/droppod_fireteam_door.mdl"
-
-global enum eDropPodFlag
-{
- DISSOLVE_AFTER_DISEMBARKS = (1<<0)
-}
-
-struct DroppodStruct
-{
- entity door
- bool openDoor = false
- int numGuys = 0
- int flags = 0
-}
-
-struct
-{
- table<entity, DroppodStruct> droppodTable
-}
-file
-
-void function DropPodFireteam_Init()
-{
- RegisterSignal( "OpenDoor" )
-
- PrecacheModel( DP_DOOR_MODEL )
- PrecacheModel( DP_ARM_MODEL )
-}
-
-void function InitFireteamDropPod( entity pod, int flags = 0 )
-{
- pod.NotSolid()
-
- DroppodStruct droppodData
- droppodData.flags = flags
- droppodData.door = CreateDropPodDoor( pod )
- file.droppodTable[ pod ] <- droppodData
-
- pod.Anim_Play( "idle" )
-}
-
-void function ActivateFireteamDropPod( entity pod, array<entity> guys )
-{
- DroppodStruct droppodData = file.droppodTable[ pod ]
- droppodData.openDoor = true
- pod.Signal( "OpenDoor" )
-
- if ( guys.len() >= 1 )
- {
- SetAnim( guys[0], "drop_pod_exit_anim", "pt_dp_exit_a" )
- SetAnim( guys[0], "drop_pod_idle_anim", "pt_dp_idle_a" )
- }
-
- if ( guys.len() >= 2 )
- {
- SetAnim( guys[1], "drop_pod_exit_anim", "pt_dp_exit_b" )
- SetAnim( guys[1], "drop_pod_idle_anim", "pt_dp_idle_b" )
- }
-
- if ( guys.len() >= 3 )
- {
- SetAnim( guys[2], "drop_pod_exit_anim", "pt_dp_exit_c" )
- SetAnim( guys[2], "drop_pod_idle_anim", "pt_dp_idle_c" )
- }
-
- if ( guys.len() >= 4 )
- {
- SetAnim( guys[3], "drop_pod_exit_anim", "pt_dp_exit_d" )
- SetAnim( guys[3], "drop_pod_idle_anim", "pt_dp_idle_d" )
- }
-
- foreach ( guy in guys )
- {
- if ( IsAlive( guy ) )
- {
- guy.MakeVisible()
- entity weapon = guy.GetActiveWeapon()
- if ( IsValid( weapon ) )
- weapon.MakeVisible()
-
- thread GuyHangsInPod( guy, pod )
- }
- }
-
- thread DropPodActiveThink( pod )
-}
-
-void function DropPodActiveThink( entity pod )
-{
- DroppodStruct droppodData = file.droppodTable[ pod ]
-
- OnThreadEnd(
- function() : ( pod )
- {
- DroppodStruct droppodData = file.droppodTable[pod]
- if ( droppodData.flags & eDropPodFlag.DISSOLVE_AFTER_DISEMBARKS )
- CleanupFireteamPod( pod )
- else
- delaythread( 10 ) CleanupFireteamPod( pod )
- }
- )
-
- pod.EndSignal( "OnDestroy" )
-
- if ( DropPodDoorInGround( pod ) )
- droppodData.door.Destroy()
- else
- DropPodOpenDoor( pod, droppodData.door )
-
- while ( droppodData.numGuys )
- WaitFrame()
-}
-
-bool function DropPodDoorInGround( entity pod )
-{
- string attachment = "hatch"
- int attachIndex = pod.LookupAttachment( attachment )
- vector end = pod.GetAttachmentOrigin( attachIndex )
-
- string originAttachment = "origin"
- int originAttachIndex = pod.LookupAttachment( originAttachment )
- vector start = pod.GetAttachmentOrigin( originAttachIndex )
-
- TraceResults result = TraceLine( start, end, pod, TRACE_MASK_SOLID, TRACE_COLLISION_GROUP_NONE )
-
- return result.fraction < 1.0
-}
-
-void function CleanupFireteamPod( entity pod )
-{
- DroppodStruct droppodData = file.droppodTable[ pod ]
-
- if ( !IsValid( pod ) )
- return
-
- if ( IsValid( droppodData.door ) )
- droppodData.door.Dissolve( ENTITY_DISSOLVE_CORE, Vector( 0, 0, 0 ), 500 )
-
- EmitSoundAtPosition( TEAM_UNASSIGNED, pod.GetOrigin(), "droppod_dissolve" )
-
- delete file.droppodTable[ pod ]
-
- pod.NotSolid()
- foreach( ent in pod.e.attachedEnts )
- {
- ent.NotSolid()
- }
- pod.Dissolve( ENTITY_DISSOLVE_CORE, Vector( 0, 0, 0 ), 500 )
-}
-
-entity function CreateDropPodDoor( entity pod )
-{
- string attachment = "hatch"
- int attachIndex = pod.LookupAttachment( attachment )
- vector origin = pod.GetAttachmentOrigin( attachIndex )
- vector angles = pod.GetAttachmentAngles( attachIndex )
-
- entity prop_physics = CreateEntity( "prop_physics" )
- SetTargetName( prop_physics, "door" + UniqueString() )
- prop_physics.SetValueForModelKey( DP_DOOR_MODEL )
- // Start Asleep
- // Debris - Don't collide with the player or other debris
- // Generate output on +USE
- prop_physics.kv.spawnflags = 261 // non solid for now
- prop_physics.kv.fadedist = -1
- prop_physics.kv.physdamagescale = 0.1
- prop_physics.kv.inertiaScale = 1.0
- prop_physics.kv.renderamt = 0
- prop_physics.kv.rendercolor = "255 255 255"
-
- DispatchSpawn( prop_physics )
-
- prop_physics.SetOrigin( origin )
- prop_physics.SetAngles( angles )
- prop_physics.SetParent( pod, "HATCH", false )
- prop_physics.MarkAsNonMovingAttachment()
-
- return prop_physics
-}
-
-void function DropPodOpenDoor( entity pod, entity door )
-{
- door.ClearParent()
- door.SetVelocity( door.GetForwardVector() * 500 )
- EmitSoundOnEntity( pod, "droppod_door_open" )
-}
-
-void function GuyHangsInPod( entity guy, entity pod )
-{
- DroppodStruct droppodData = file.droppodTable[ pod ]
-
- guy.EndSignal( "OnDeath" )
- guy.EndSignal( "OnDestroy" )
-
- OnThreadEnd(
- function() : ( droppodData )
- {
- droppodData.numGuys--
- }
- )
-
- droppodData.numGuys++
-
- string idleAnim
- string exitAnim
-
- if ( !droppodData.openDoor )
- {
- guy.SetEfficientMode( true )
-
- guy.SetParent( pod, "ATTACH", false )
-
- idleAnim = expect string( GetAnim( guy, "drop_pod_idle_anim" ) )
- if ( guy.LookupSequence( idleAnim ) != -1 )
- guy.Anim_ScriptedPlay( idleAnim )
-
- pod.WaitSignal( "OpenDoor" )
-
- //wait POST_TURRET_DELAY
-
- guy.SetEfficientMode( false )
- }
-
-
- guy.SetParent( pod, "ATTACH", false )
-
- exitAnim = expect string ( GetAnim( guy, "drop_pod_exit_anim" ) )
- bool exitAnimExists = guy.LookupSequence( exitAnim ) != -1
- if ( exitAnimExists )
- guy.Anim_ScriptedPlay( exitAnim )
-
- guy.ClearParent()
-
- if ( exitAnimExists )
- WaittillAnimDone( guy )
- guy.Signal( "npc_deployed" )
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_squad_spawn.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_squad_spawn.gnut
deleted file mode 100644
index 9dbdd699d..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_squad_spawn.gnut
+++ /dev/null
@@ -1,167 +0,0 @@
-
-
-
-global function GetNPCBaseClassFromSpawnFunc
-
-global function CreateZipLineSquadDropTable
-
-string function GetNPCBaseClassFromSpawnFunc( entity functionref( int, vector, vector ) spawnFunc )
-{
- // temp spawn a guy to get his baseclass.
- entity npc = spawnFunc( TEAM_IMC, Vector(0,0,0), Vector(0,0,0) )
- string baseClass = npc.GetClassName()
- npc.Destroy()
- return baseClass
-}
-
-
-
-void function DropOffAISide_NPCThink( entity npc, int index, entity dropship, string attach )
-{
- npc.EndSignal( "OnDeath" )
-
- //init
- npc.SetParent( dropship, attach )
- npc.SetEfficientMode( true )
-
- //deploy
- array<string> deployAnims = DropOffAISide_GetDeployAnims()
- array<float> seekTimes = DropOffAISide_GetSeekTimes()
-
- thread PlayAnimTeleport( npc, deployAnims[ index ], dropship, attach )
- npc.Anim_SetInitialTime( seekTimes[ index ] )
- WaittillAnimDone( npc )
-
- npc.SetEfficientMode( false )
-
- //disperse
- array<string> disperseAnims = DropOffAISide_GetDisperseAnims()
- vector origin = HackGetDeltaToRef( npc.GetOrigin(), npc.GetAngles(), npc, disperseAnims[ index ] ) + Vector( 0,0,2 )
- waitthread PlayAnimGravity( npc, disperseAnims[ index ], origin, npc.GetAngles() )
-}
-
-void function DropOffAISide_WarpOutShip( entity dropship, vector origin, vector angles )
-{
- wait 1.5
- dropship.EndSignal( "OnDeath" )
-
- string anim = "cd_dropship_rescue_side_end"
- thread PlayAnim( dropship, anim, origin, angles )
-
- //blend
- wait dropship.GetSequenceDuration( anim ) - 0.2
-
- dropship.Hide()
- thread WarpoutEffect( dropship )
-}
-
-float function GetInstantSpawnRadius( entity npc )
-{
- float radius = 64
-
- if ( npc )
- {
- switch ( npc.GetClassName() )
- {
- case "npc_gunship":
- case "npc_dropship":
- radius = 512
- break
-
- case "npc_titan":
- radius = 256
- break
-
- case "npc_super_spectre":
- case "npc_prowler":
- radius = 128
- break
-
- default:
- radius = 64
- break
- }
- }
-
- return radius
-}
-
-
-
-
-/************************************************************************************************\
-
-## ## #### ###### ###### ######## ####### ####### ## ######
-### ### ## ## ## ## ## ## ## ## ## ## ## ## ##
-#### #### ## ## ## ## ## ## ## ## ## ##
-## ### ## ## ###### ## ## ## ## ## ## ## ######
-## ## ## ## ## ## ## ## ## ## ## ##
-## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
-## ## #### ###### ###### ## ####### ####### ######## ######
-
-\************************************************************************************************/
-
-
-
-
-array<string> function DropOffAISide_GetIdleAnims()
-{
- array<string> anims = [
- "pt_ds_side_intro_gen_idle_A", //standing right
- "pt_ds_side_intro_gen_idle_B", //standing left
- "pt_ds_side_intro_gen_idle_C", //sitting right
- "pt_ds_side_intro_gen_idle_D" ] //sitting left
-
- return anims
-}
-
-array<string> function DropOffAISide_GetDeployAnims()
-{
- array<string> anims = [
- "pt_generic_side_jumpLand_A", //standing right
- "pt_generic_side_jumpLand_B", //standing left
- "pt_generic_side_jumpLand_C", //sitting right
- "pt_generic_side_jumpLand_D" ] //sitting left
-
- return anims
-}
-
-array<string> function DropOffAISide_GetDisperseAnims()
-{
- array<string> anims = [
- "React_signal_thatway", //standing right
- "React_spot_radio2", //standing left
- "stand_2_run_45R", //sitting right
- "stand_2_run_45L" ] //sitting left
-
- return anims
-}
-
-array<float> function DropOffAISide_GetSeekTimes()
-{
- array<float> anims = [
- 9.75, //standing right
- 10.0, //standing left
- 10.5, //sitting right
- 11.25 ] //sitting left
-
- return anims
-}
-
-
-CallinData function CreateZipLineSquadDropTable( int team, int count, vector origin, vector angles, string squadName = "" )
-{
- if ( squadName == "" )
- squadName = MakeSquadName( team, UniqueString( "ZiplineTable" ) )
-
- CallinData drop
- drop.origin = origin
- drop.yaw = angles.y
- drop.dist = 768
- drop.team = team
- drop.squadname = squadName
- SetDropTableSpawnFuncs( drop, CreateSoldier, count )
- SetCallinStyle( drop, eDropStyle.ZIPLINE_NPC )
-
- return drop
-}
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_titan_npc_behavior.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_titan_npc_behavior.gnut
deleted file mode 100644
index 347cb6441..000000000
--- a/Northstar.CustomServers/scripts/vscripts/ai/_titan_npc_behavior.gnut
+++ /dev/null
@@ -1,404 +0,0 @@
-untyped
-
-global function TitanNpcBehavior_Init
-
-global function TitanNPC_Think
-global function TitanNPC_WaitForBubbleShield_StartAutoTitanBehavior
-global function TitanStandUp
-global function TitanKneel
-global function GetBubbleShieldDuration
-global function ShowMainTitanWeapons
-
-global function ChangedStance
-
-global function TitanWaitsToChangeStance
-global function ShouldBecomeAutoTitan
-
-function TitanNpcBehavior_Init()
-{
- FlagInit( "DisableTitanKneelingEmbark" )
-
- RegisterSignal( "TitanStopsThinking" )
- RegisterSignal( "RodeoRiderChanged" )
-
- if ( IsMultiplayer() )
- {
- AddCallback_OnTitanBecomesPilot( OnClassChangeBecomePilot )
- AddCallback_OnPilotBecomesTitan( OnClassChangeBecomeTitan )
- }
-}
-
-void function OnClassChangeBecomePilot( entity player, entity titan )
-{
- entity soul = titan.GetTitanSoul()
- if ( !SoulHasPassive( soul, ePassives.PAS_ENHANCED_TITAN_AI ) )
- {
- entity ordnanceWeapon = titan.GetOffhandWeapon( OFFHAND_ORDNANCE )
- if ( IsValid( ordnanceWeapon ) )
- ordnanceWeapon.AllowUse( false )
-
- entity centerWeapon = titan.GetOffhandWeapon( OFFHAND_TITAN_CENTER )
- if ( IsValid( centerWeapon ) )
- centerWeapon.AllowUse( false )
- }
-}
-
-void function OnClassChangeBecomeTitan( entity player, entity titan )
-{
- entity soul = player.GetTitanSoul()
-
- entity ordnanceWeapon = player.GetOffhandWeapon( OFFHAND_ORDNANCE )
- if ( IsValid( ordnanceWeapon ) )
- ordnanceWeapon.AllowUse( true )
-
- entity centerWeapon = player.GetOffhandWeapon( OFFHAND_TITAN_CENTER )
- if ( IsValid( centerWeapon ) )
- centerWeapon.AllowUse( true )
-}
-
-float function GetBubbleShieldDuration( entity player )
-{
- if ( PlayerHasPassive( player, ePassives.PAS_LONGER_BUBBLE ) )
- return EMBARK_TIMEOUT + 10.0
- else
- return EMBARK_TIMEOUT
-
- unreachable
-}
-
-void function TitanNPC_WaitForBubbleShield_StartAutoTitanBehavior( entity titan )
-{
- Assert( IsAlive( titan ) )
-
- titan.Signal( "TitanStopsThinking" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "TitanStopsThinking" )
- titan.EndSignal( "ContextAction_SetBusy" )
-
- entity bossPlayer = titan.GetBossPlayer()
- if ( !bossPlayer )
- return
-
- OnThreadEnd(
- function () : ( titan )
- {
- if ( IsAlive( titan ) )
- {
- titan.SetNoTarget( false )
- thread TitanNPC_Think( titan )
- }
- }
- )
-
- titan.EndSignal( "ChangedTitanMode" )
-
- float timeout
- if ( SoulHasPassive( titan.GetTitanSoul(), ePassives.PAS_BUBBLESHIELD ) )
- {
- entity player = titan.GetBossPlayer()
- timeout = GetBubbleShieldDuration( player )
- }
- else
- {
- timeout = 0
- }
-
- wait timeout
-}
-
-function TitanNPC_Think( entity titan )
-{
- entity soul = titan.GetTitanSoul()
-
- // JFS - Shouldn't have to check for presence of soul.
- // The real fix for next game would be to make sure no other script can run between transferring away a titan's soul and destroying the titan.
- // This particular bug occurred if TitanNPC_WaitForBubbleShield_StartAutoTitanBehavior() was called before soul transferred from npc to player,
- // in which case the soul transfer killed the thread via Signal( "TitanStopsThinking" ), which causes the OnThreadEnd() to run TitanNPC_Think().
- if ( !IsValid( soul ) )
- return;
-
- if ( soul.capturable || !ShouldBecomeAutoTitan( titan ) )
- {
- // capturable titan just kneels
- if ( soul.GetStance() > STANCE_KNEELING )
- thread TitanKneel( titan )
- return
- }
-
- Assert( IsAlive( titan ) )
-
- if ( !TitanCanStand( titan ) )// sets the var
- {
- // try to put the titan on the navmesh
- vector ornull clampedPos = NavMesh_ClampPointForAIWithExtents( titan.GetOrigin(), titan, < 100, 100, 100 > )
- if ( clampedPos != null )
- {
- expect vector( clampedPos )
- titan.SetOrigin( clampedPos )
- TitanCanStand( titan )
- }
- }
-
- if ( !titan.GetBossPlayer() )
- {
- titan.Signal( "TitanStopsThinking" )
- return
- }
-
- if ( "disableAutoTitanConversation" in titan.s ) //At this point the Titan has stood up and is ready to talk
- delete titan.s.disableAutoTitanConversation
-
- titan.EndSignal( "TitanStopsThinking" )
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "player_embarks_titan" )
-
- // kneel in certain circumstances
- for ( ;; )
- {
- if ( !ChangedStance( titan ) )
- waitthread TitanWaitsToChangeStance( titan )
- }
-}
-
-bool function ChangedStance( entity titan )
-{
- if ( GetEmbarkDisembarkPlayer( titan ) )
- return false
-
- local soul = titan.GetTitanSoul()
-
- // in a scripted sequence?
- if ( IsValid( titan.GetParent() ) )
- return false
-
- if ( soul.GetStance() > STANCE_KNEELING )
- {
- if ( TitanShouldKneel( titan ) )
- {
- //waitthread PlayAnimGravity( titan, "at_MP_stand2knee_straight" )
- waitthread KneelToShowRider( titan )
- thread PlayAnim( titan, "at_MP_embark_idle_blended" )
- SetStanceKneel( soul )
- return true
- }
- }
- else
- {
- if ( !TitanShouldKneel( titan ) && TitanCanStand( titan ) )
- {
- waitthread TitanStandUp( titan )
- return true
- }
-
- if ( soul.GetStance() == STANCE_KNEEL )
- {
- thread PlayAnim( titan, "at_MP_embark_idle_blended" )
- }
- }
-
- return false
-}
-
-function TitanShouldKneel( entity titan )
-{
- local soul = titan.GetTitanSoul()
-
- if ( soul.capturable )
- return true
-
- //if( HasEnemyRodeoRiders( titan ) )
- // return true
- if ( !TitanCanStand( titan ) )
- return false
-
- if ( !ShouldBecomeAutoTitan( titan ) )
- return true
-
- return false
-}
-
-function TitanWaitsToChangeStance( titan )
-{
- local soul = titan.GetTitanSoul()
- soul.EndSignal( "RodeoRiderChanged" )
-
- titan.EndSignal( "OnAnimationInterrupted" )
- titan.EndSignal( "OnAnimationDone" )
-
- WaitForever()
-}
-
-function TitanStandUp( titan )
-{
- local soul = titan.GetTitanSoul()
- // stand up
- titan.s.standQueued = false
- ShowMainTitanWeapons( titan )
- titan.Anim_Stop()
- waitthread PlayAnimGravity( titan, "at_hotdrop_quickstand" )
- Assert( soul == titan.GetTitanSoul() )
- SetStanceStand( soul )
-}
-
-
-void function TitanKneel( entity titan )
-{
- titan.EndSignal( "TitanStopsThinking" )
- titan.EndSignal( "OnDeath" )
- Assert( IsAlive( titan ) )
- local soul = titan.GetTitanSoul()
-
- waitthread KneelToShowRider( titan )
-
- thread PlayAnim( titan, "at_MP_embark_idle_blended" )
- SetStanceKneel( soul )
-}
-
-
-/*
-function TitanWaittillShouldStand( entity titan )
-{
- //Don't wait if player is dead - titan should just stand up immediately
- local player = titan.GetBossPlayer()
- if ( !IsAlive( player ) )
- return
-
- player.EndSignal( "OnDeath" )
-
- for ( ;; )
- {
- if ( TitanCanStand( titan ) )
- break
-
- wait 5
- }
- if ( titan.s.standQueued )
- return
-
- titan.WaitSignal( "titanStand" )
-}
-*/
-
-void function KneelToShowRider( entity titan )
-{
- entity soul = titan.GetTitanSoul()
- entity player = soul.GetBossPlayer()
- local animation
- local yawDif
-
- //if ( IsAlive( player ) )
- //{
- // local table = GetFrontRightDots( titan, player )
- //
- // local dotForward = Table.dotForward
- // local dotRight = Table.dotRight
- //
- //// DebugDrawLine( titanOrg, titanOrg + titan.GetForwardVector() * 200, 255, 0, 0, true, 5 )
- //// DebugDrawLine( titanOrg, titanOrg + vecToEnt * 200, 0, 255, 0, true, 5 )
- //
- // if ( dotForward > 0.88 )
- // {
- // animation = "at_MP_stand2knee_L90"
- // yawDif = 0
- // }
- // else
- // if ( dotForward < -0.88 )
- // {
- // animation = "at_MP_stand2knee_R90"
- // yawDif = 180
- // }
- // else
- // if ( dotRight > 0 )
- // {
- // animation = "at_MP_stand2knee_straight"
- // yawDif = 90
- // }
- // else
- // {
- // animation = "at_MP_stand2knee_180"
- // yawDif = -90
- // }
- //}
- //else
- {
- animation = "at_MP_stand2knee_straight"
- yawDif = 0
- }
-
- thread HideOgreMainWeaponFromEnemies( titan )
-
- if ( !IsAlive( player ) )
- {
- waitthread PlayAnimGravity( titan, animation )
- return
- }
-
- local titanOrg = titan.GetOrigin()
- local playerOrg = player.GetOrigin()
-
- /*
- local vec = playerOrg - titanOrg
- vec.z = 0
-
- local angles = VectorToAngles( vec )
-
- angles.y += yawDif
- */
-
- local angles = titan.GetAngles()
-
- titan.Anim_ScriptedPlayWithRefPoint( animation, titanOrg, angles, 0.5 )
- titan.Anim_EnablePlanting()
-
- WaittillAnimDone( titan )
-}
-
-function HideOgreMainWeaponFromEnemies( titan )
-{
- expect entity( titan )
-
- titan.EndSignal( "OnDeath" )
- titan.EndSignal( "OnDestroy" )
-
- wait 1.0
-
- entity soul = titan.GetTitanSoul()
-
- Assert( IsValid( soul ) )
-
- local titanSubClass = GetSoulTitanSubClass( soul )
- if ( titanSubClass == "ogre" )
- {
- if ( IsValid( GetEnemyRodeoPilot( titan ) ) )
- HideMainWeaponsFromEnemies( titan )
- }
-}
-
-function HideMainWeaponsFromEnemies( titan )
-{
- local weapons = titan.GetMainWeapons()
- foreach ( weapon in weapons )
- weapon.kv.visibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY
-}
-
-function ShowMainTitanWeapons( titan )
-{
- local weapons = titan.GetMainWeapons()
- foreach ( weapon in weapons )
- {
- weapon.kv.visibilityFlags = ENTITY_VISIBLE_TO_EVERYONE
- }
-}
-
-bool function ShouldBecomeAutoTitan( entity titan )
-{
- entity soul = titan.GetTitanSoul()
-
- if ( soul != null )
- {
- if ( SoulHasPassive( soul, ePassives.PAS_ENHANCED_TITAN_AI ) )
- return true
- }
-
- return ( !PROTO_AutoTitansDisabled() )
-} \ No newline at end of file