diff options
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut')
-rw-r--r-- | Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut b/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut new file mode 100644 index 000000000..9717c76d9 --- /dev/null +++ b/Northstar.CustomServers/scripts/vscripts/ai/_ai_soldiers.gnut @@ -0,0 +1,787 @@ +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 ) + } +} + |