diff options
author | BobTheBob <32057864+BobTheBob9@users.noreply.github.com> | 2021-08-31 23:14:58 +0100 |
---|---|---|
committer | BobTheBob <32057864+BobTheBob9@users.noreply.github.com> | 2021-08-31 23:14:58 +0100 |
commit | 9a96d0bff56f1969c68bb52a2f33296095bdc67d (patch) | |
tree | 4175928e488632705692e3cccafa1a38dd854615 /Northstar.CustomServers/scripts/vscripts/ai | |
parent | 27bd240871b7c0f2f49fef137718b2e3c208e3b4 (diff) | |
download | NorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.tar.gz NorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.zip |
move to new mod format
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/ai')
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 0429895b..00000000 --- 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 e3addf81..00000000 --- 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 c0d56de7..00000000 --- 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 638166c8..00000000 --- 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 2f1fdc96..00000000 --- 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 771fe6d9..00000000 --- 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 e6d3bcf0..00000000 --- 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 588b4d75..00000000 --- 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 fc8b7d1e..00000000 --- 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 4aa3ac30..00000000 --- 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 08598808..00000000 --- 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 0d4b43c9..00000000 --- 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 f1fbdb80..00000000 --- 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 3c2e36ce..00000000 --- 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 37b89169..00000000 --- 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 9717c76d..00000000 --- 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 37b89169..00000000 --- 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 7e4d2cdd..00000000 --- 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 c6e7f9f4..00000000 --- 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 214aff96..00000000 --- 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 f49560e0..00000000 --- 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 50b6cc75..00000000 --- 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 f8e0652c..00000000 --- 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 eca5849b..00000000 --- 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 67c68600..00000000 --- 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 847efa8b..00000000 --- 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 b93631ac..00000000 --- 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 9dbdd699..00000000 --- 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 347cb644..00000000 --- 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 |