aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers
diff options
context:
space:
mode:
authorDBmaoha <56738369+DBmaoha@users.noreply.github.com>2023-04-29 01:55:50 +0800
committerGeckoEidechse <gecko.eidechse+git@pm.me>2023-04-29 18:27:27 +0200
commitf0b10e1d05a23562e0af7a76af520a3f40ef730c (patch)
tree60b314c0ede0a3ecba018a5428e650e29a6aa8be /Northstar.CustomServers
parentc89364cdef3b78c09723733e759d124c8dd76838 (diff)
downloadNorthstarMods-f0b10e1d05a23562e0af7a76af520a3f40ef730c.tar.gz
NorthstarMods-f0b10e1d05a23562e0af7a76af520a3f40ef730c.zip
Improve AI spawn functions (#626)v1.13.2-rc2v1.13.2-rc1v1.13.21.13.X
* initial commit * remove modded contents * Update _ai_gamemodes.gnut * restore HandleScoreEvent() checks * fix squad minimap icon * fix compile error * Update _gamemode_aitdm.nut * update formatting as requested changes * add missing reaper highlight * Formatting * Newline test * Did this work? --------- Co-authored-by: F1F7Y <64418963+F1F7Y@users.noreply.github.com> (cherry picked from commit 5fbd1edf7942a542605f6274604ed0e0ba9f20b7)
Diffstat (limited to 'Northstar.CustomServers')
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut115
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut146
2 files changed, 174 insertions, 87 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
index 0fad768c..78a9a208 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
@@ -1,7 +1,6 @@
global function AiGameModes_Init
-global function AiGameModes_SetGruntWeapons
-global function AiGameModes_SetSpectreWeapons
+global function AiGameModes_SetNPCWeapons
global function AiGameModes_SpawnDropShip
global function AiGameModes_SpawnDropPod
@@ -15,25 +14,20 @@ const INTRO_DROPSHIP_CUTOFF = 2000
struct
{
- array< string > gruntWeapons = [ "mp_weapon_rspn101" ]
- array< string > spectreWeapons = [ "mp_weapon_hemlok_smg" ]
+ table< string, array<string> > npcWeaponsTable // npcs have their default weapon in aisettings file
} file
void function AiGameModes_Init()
{
-
}
//------------------------------------------------------
-void function AiGameModes_SetGruntWeapons( array< string > weapons )
+void function AiGameModes_SetNPCWeapons( string npcClass, array<string> weapons )
{
- file.gruntWeapons = weapons
-}
-
-void function AiGameModes_SetSpectreWeapons( array< string > weapons )
-{
- file.spectreWeapons = weapons
+ if ( !( npcClass in file.npcWeaponsTable ) )
+ file.npcWeaponsTable[ npcClass ] <- []
+ file.npcWeaponsTable[ npcClass ] = weapons
}
//------------------------------------------------------
@@ -59,7 +53,7 @@ void function AiGameModes_SpawnDropShip( vector pos, vector rot, int team, int c
foreach ( guy in guys )
{
- ReplaceWeapon( guy, file.gruntWeapons[ RandomInt( file.gruntWeapons.len() ) ], [] )
+ SetUpNPCWeapons( guy )
guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
}
@@ -70,29 +64,21 @@ void function AiGameModes_SpawnDropShip( vector pos, vector rot, int team, int c
void function AiGameModes_SpawnDropPod( vector pos, vector rot, int team, string content /*( ͡° ͜ʖ ͡°)*/, void functionref( array<entity> guys ) squadHandler = null )
{
- string squadName = MakeSquadName( team, UniqueString( "" ) )
- array<entity> guys
-
entity pod = CreateDropPod( pos, <0,0,0> )
InitFireteamDropPod( pod )
-
+
+ waitthread LaunchAnimDropPod( pod, "pod_testpath", pos, rot )
+
+ string squadName = MakeSquadName( team, UniqueString( "" ) )
+ array<entity> guys
for ( int i = 0; i < 4 ;i++ )
{
entity npc = CreateNPC( content, team, pos,<0,0,0> )
DispatchSpawn( npc )
SetSquad( npc, squadName )
- switch ( content )
- {
- case "npc_soldier":
- ReplaceWeapon( npc, file.gruntWeapons[ RandomInt( file.gruntWeapons.len() ) ], [] )
- break
-
- case "npc_spectre":
- ReplaceWeapon( npc, file.spectreWeapons[ RandomInt( file.spectreWeapons.len() ) ], [] )
- break
- }
+ SetUpNPCWeapons( npc )
npc.SetParent( pod, "ATTACH", true )
@@ -100,25 +86,26 @@ void function AiGameModes_SpawnDropPod( vector pos, vector rot, int team, string
guys.append( npc )
}
- // The order here is different so we can show on minimap while were still falling
+ ActivateFireteamDropPod( pod, guys )
+
+ // start searching for enemies
if ( squadHandler != null )
thread squadHandler( guys )
-
- waitthread LaunchAnimDropPod( pod, "pod_testpath", pos, rot )
-
- ActivateFireteamDropPod( pod, guys )
}
+const float REAPER_WARPFALL_DELAY = 4.7 // same as fd does
void function AiGameModes_SpawnReaper( vector pos, vector rot, int team, string aiSettings = "", void functionref( entity reaper ) reaperHandler = null )
{
- thread Reaper_Spawnpoint( pos, team, 11.2 )
+ float reaperLandTime = REAPER_WARPFALL_DELAY + 1.2 // reaper takes ~1.2s to warpfall
+ thread HotDrop_Spawnpoint( pos, team, reaperLandTime, false, damagedef_reaper_fall )
- wait 10
- // spawn reapers right before it warpfalls, or round_end clean up will crash the game
+ wait REAPER_WARPFALL_DELAY
entity reaper = CreateSuperSpectre( team, pos, rot )
+ reaper.EndSignal( "OnDestroy" )
// reaper highlight
Highlight_SetFriendlyHighlight( reaper, "sp_enemy_pilot" )
- reaper.Highlight_SetParam( 1, 0, < 3,3,3 > )
+ reaper.Highlight_SetParam( 1, 0, < 1,1,1 > )
+ SetDefaultMPEnemyHighlight( reaper )
Highlight_SetEnemyHighlight( reaper, "enemy_titan" )
SetSpawnOption_Titanfall( reaper )
@@ -127,15 +114,18 @@ void function AiGameModes_SpawnReaper( vector pos, vector rot, int team, string
if ( aiSettings != "" )
SetSpawnOption_AISettings( reaper, aiSettings )
+ HideName( reaper ) // prevent flash a name onto it
DispatchSpawn( reaper )
-
+
+ reaper.WaitSignal( "WarpfallComplete" )
+ ShowName( reaper ) // show name again after drop
if ( reaperHandler != null )
thread reaperHandler( reaper )
}
// copied from cl_replacement_titan_hud.gnut
-void function Reaper_Spawnpoint( vector origin, int team, float impactTime, bool hasFriendlyWarning = false )
+void function HotDrop_Spawnpoint( vector origin, int team, float impactTime, bool hasFriendlyWarning = false, int damageDef = -1 )
{
array<entity> targetEffects = []
vector surfaceNormal = < 0, 0, 1 >
@@ -146,32 +136,50 @@ void function Reaper_Spawnpoint( vector origin, int team, float impactTime, bool
{
entity effectFriendly = StartParticleEffectInWorld_ReturnEntity( index, origin, surfaceNormal )
SetTeam( effectFriendly, team )
- EffectSetControlPointVector( effectFriendly, 1, < 128,188,255 > )
+ EffectSetControlPointVector( effectFriendly, 1, FRIENDLY_COLOR_FX )
effectFriendly.kv.VisibilityFlags = ENTITY_VISIBLE_TO_FRIENDLY
+ effectFriendly.DisableHibernation() // prevent it from fading out
targetEffects.append( effectFriendly )
}
entity effectEnemy = StartParticleEffectInWorld_ReturnEntity( index, origin, surfaceNormal )
SetTeam( effectEnemy, team )
- EffectSetControlPointVector( effectEnemy, 1, < 255,99,0 > )
+ EffectSetControlPointVector( effectEnemy, 1, ENEMY_COLOR_FX )
effectEnemy.kv.VisibilityFlags = ENTITY_VISIBLE_TO_ENEMY
+ effectEnemy.DisableHibernation() // prevent it from fading out
targetEffects.append( effectEnemy )
-
+
+ // so enemy npcs will mostly avoid them
+ entity damageAreaInfo
+ if ( damageDef > -1 )
+ {
+ damageAreaInfo = CreateEntity( "info_target" )
+ DispatchSpawn( damageAreaInfo )
+ AI_CreateDangerousArea_DamageDef( damageDef, damageAreaInfo, team, true, true )
+ }
+
wait impactTime
+ // clean up
foreach( entity targetEffect in targetEffects )
{
if ( IsValid( targetEffect ) )
EffectStop( targetEffect )
}
+ if ( IsValid( damageAreaInfo ) )
+ damageAreaInfo.Destroy()
}
// including aisettings stuff specifically for at bounty titans
+const float TITANFALL_WARNING_DURATION = 5.0
void function AiGameModes_SpawnTitan( vector pos, vector rot, int team, string setFile, string aiSettings = "", void functionref( entity titan ) titanHandler = null )
{
entity titan = CreateNPCTitan( setFile, TEAM_BOTH, pos, rot )
SetSpawnOption_Titanfall( titan )
SetSpawnOption_Warpfall( titan )
+
+ // modified: do a hotdrop spawnpoint warning
+ thread HotDrop_Spawnpoint( pos, team, TITANFALL_WARNING_DURATION, false, damagedef_titan_fall )
if ( aiSettings != "" )
SetSpawnOption_AISettings( titan, aiSettings )
@@ -182,12 +190,27 @@ void function AiGameModes_SpawnTitan( vector pos, vector rot, int team, string s
thread titanHandler( titan )
}
-// entity.ReplaceActiveWeapon gave grunts archers sometimes, this is my replacement for it
-void function ReplaceWeapon( entity guy, string weapon, array<string> mods )
+void function SetUpNPCWeapons( entity guy )
{
- guy.TakeActiveWeapon()
- guy.GiveWeapon( weapon, mods )
- guy.SetActiveWeaponByName( weapon )
+ string className = guy.GetClassName()
+
+ array<string> mainWeapons
+ if ( className in file.npcWeaponsTable )
+ mainWeapons = file.npcWeaponsTable[ className ]
+
+ if ( mainWeapons.len() == 0 ) // no valid weapons
+ return
+
+ // take off existing main weapons, or sometimes they'll have a archer by default
+ foreach ( entity weapon in guy.GetMainWeapons() )
+ guy.TakeWeapon( weapon.GetWeaponClassName() )
+
+ if ( mainWeapons.len() > 0 )
+ {
+ string weaponName = mainWeapons[ RandomInt( mainWeapons.len() ) ]
+ guy.GiveWeapon( weaponName )
+ guy.SetActiveWeaponByName( weaponName )
+ }
}
// Checks if we can spawn a dropship at a node, this should guarantee dropship ziplines
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
index fae778d6..702e4500 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
@@ -1,22 +1,36 @@
untyped
global function GamemodeAITdm_Init
-const SQUADS_PER_TEAM = 3
+// these are now default settings
+const int SQUADS_PER_TEAM = 4
-const REAPERS_PER_TEAM = 2
+const int REAPERS_PER_TEAM = 2
-const LEVEL_SPECTRES = 125
-const LEVEL_STALKERS = 380
-const LEVEL_REAPERS = 500
+const int LEVEL_SPECTRES = 125
+const int LEVEL_STALKERS = 380
+const int LEVEL_REAPERS = 500
+
+// add settings
+global function AITdm_SetSquadsPerTeam
+global function AITdm_SetReapersPerTeam
+global function AITdm_SetLevelSpectres
+global function AITdm_SetLevelStalkers
+global function AITdm_SetLevelReapers
struct
{
// Due to team based escalation everything is an array
- array< int > levels = [ LEVEL_SPECTRES, LEVEL_SPECTRES ]
+ array< int > levels = [] // Initilazed in `Spawner_Threaded`
array< array< string > > podEntities = [ [ "npc_soldier" ], [ "npc_soldier" ] ]
array< bool > reapers = [ false, false ]
-} file
+ // default settings
+ int squadsPerTeam = SQUADS_PER_TEAM
+ int reapersPerTeam = REAPERS_PER_TEAM
+ int levelSpectres = LEVEL_SPECTRES
+ int levelStalkers = LEVEL_STALKERS
+ int levelReapers = LEVEL_REAPERS
+} file
void function GamemodeAITdm_Init()
{
@@ -34,18 +48,47 @@ void function GamemodeAITdm_Init()
if ( GetCurrentPlaylistVarInt( "aitdm_archer_grunts", 0 ) == 0 )
{
- AiGameModes_SetGruntWeapons( [ "mp_weapon_rspn101", "mp_weapon_dmr", "mp_weapon_r97", "mp_weapon_lmg" ] )
- AiGameModes_SetSpectreWeapons( [ "mp_weapon_hemlok_smg", "mp_weapon_doubletake", "mp_weapon_mastiff" ] )
+ AiGameModes_SetNPCWeapons( "npc_soldier", [ "mp_weapon_rspn101", "mp_weapon_dmr", "mp_weapon_r97", "mp_weapon_lmg" ] )
+ AiGameModes_SetNPCWeapons( "npc_spectre", [ "mp_weapon_hemlok_smg", "mp_weapon_doubletake", "mp_weapon_mastiff" ] )
+ AiGameModes_SetNPCWeapons( "npc_stalker", [ "mp_weapon_hemlok_smg", "mp_weapon_lstar", "mp_weapon_mastiff" ] )
}
else
{
- AiGameModes_SetGruntWeapons( [ "mp_weapon_rocket_launcher" ] )
- AiGameModes_SetSpectreWeapons( [ "mp_weapon_rocket_launcher" ] )
+ AiGameModes_SetNPCWeapons( "npc_soldier", [ "mp_weapon_rocket_launcher" ] )
+ AiGameModes_SetNPCWeapons( "npc_spectre", [ "mp_weapon_rocket_launcher" ] )
+ AiGameModes_SetNPCWeapons( "npc_stalker", [ "mp_weapon_rocket_launcher" ] )
}
ScoreEvent_SetupEarnMeterValuesForMixedModes()
}
+// add settings
+void function AITdm_SetSquadsPerTeam( int squads )
+{
+ file.squadsPerTeam = squads
+}
+
+void function AITdm_SetReapersPerTeam( int reapers )
+{
+ file.reapersPerTeam = reapers
+}
+
+void function AITdm_SetLevelSpectres( int level )
+{
+ file.levelSpectres = level
+}
+
+void function AITdm_SetLevelStalkers( int level )
+{
+ file.levelStalkers = level
+}
+
+void function AITdm_SetLevelReapers( int level )
+{
+ file.levelReapers = level
+}
+//
+
// Starts skyshow, this also requiers AINs but doesn't crash if they're missing
void function OnPrematchStart()
{
@@ -74,11 +117,9 @@ void function HandleScoreEvent( entity victim, entity attacker, var damageInfo )
// Basic checks
if ( victim == attacker || !( attacker.IsPlayer() || attacker.IsTitan() ) || GetGameState() != eGameState.Playing )
return
-
// Hacked spectre filter
if ( victim.GetOwner() == attacker )
return
-
// NPC titans without an owner player will not count towards any team's score
if ( attacker.IsNPC() && attacker.IsTitan() && !IsValid( GetPetTitanOwner( attacker ) ) )
return
@@ -193,7 +234,7 @@ void function SpawnIntroBatch_Threaded( int team )
int ships = shipNodes.len()
- for ( int i = 0; i < SQUADS_PER_TEAM; i++ )
+ for ( int i = 0; i < file.squadsPerTeam; i++ )
{
if ( pods != 0 || ships == 0 )
{
@@ -238,6 +279,7 @@ void function Spawner_Threaded( int team )
// used to index into escalation arrays
int index = team == TEAM_MILITIA ? 0 : 1
+ file.levels = [ file.levelSpectres, file.levelSpectres ] // due we added settings, should init levels here!
while( true )
{
@@ -252,7 +294,7 @@ void function Spawner_Threaded( int team )
if ( file.reapers[ index ] )
{
array< entity > points = SpawnPoints_GetDropPod()
- if ( reaperCount < REAPERS_PER_TEAM )
+ if ( reaperCount < file.reapersPerTeam )
{
entity node = points[ GetSpawnPointIndex( points, team ) ]
waitthread AiGameModes_SpawnReaper( node.GetOrigin(), node.GetAngles(), team, "npc_super_spectre_aitdm", ReaperHandler )
@@ -260,7 +302,7 @@ void function Spawner_Threaded( int team )
}
// NORMAL SPAWNS
- if ( count < SQUADS_PER_TEAM * 4 - 2 )
+ if ( count < file.squadsPerTeam * 4 - 2 )
{
string ent = file.podEntities[ index ][ RandomInt( file.podEntities[ index ].len() ) ]
@@ -306,19 +348,19 @@ void function Escalate( int team )
// Based on score escalate a team
switch ( file.levels[ index ] )
{
- case LEVEL_SPECTRES:
- file.levels[ index ] = LEVEL_STALKERS
+ case file.levelSpectres:
+ file.levels[ index ] = file.levelStalkers
file.podEntities[ index ].append( "npc_spectre" )
SetGlobalNetInt( defcon, 2 )
return
- case LEVEL_STALKERS:
- file.levels[ index ] = LEVEL_REAPERS
+ case file.levelStalkers:
+ file.levels[ index ] = file.levelReapers
file.podEntities[ index ].append( "npc_stalker" )
SetGlobalNetInt( defcon, 3 )
return
- case LEVEL_REAPERS:
+ case file.levelReapers:
file.reapers[ index ] = true
SetGlobalNetInt( defcon, 4 )
return
@@ -355,30 +397,47 @@ int function GetSpawnPointIndex( array< entity > points, int team )
// AI can also flee deeper into their zone suggesting someone spent way too much time on this
void function SquadHandler( array<entity> guys )
{
+ int team = guys[0].GetTeam()
+ // show the squad enemy radar
+ array<entity> players = GetPlayerArrayOfEnemies( team )
+ foreach ( entity guy in guys )
+ {
+ if ( IsAlive( guy ) )
+ {
+ foreach ( player in players )
+ guy.Minimap_AlwaysShow( 0, player )
+ }
+ }
+
// Not all maps have assaultpoints / have weird assault points ( looking at you ac )
// So we use enemies with a large radius
- array< entity > points = GetNPCArrayOfEnemies( guys[0].GetTeam() )
-
- if ( points.len() == 0 )
+ while ( GetNPCArrayOfEnemies( team ).len() == 0 ) // if we can't find any enemy npcs, keep waiting
+ WaitFrame()
+
+ // our waiting is end, check if any soldiers left
+ bool squadAlive = false
+ foreach ( entity guy in guys )
+ {
+ if ( IsAlive( guy ) )
+ squadAlive = true
+ else
+ guys.removebyvalue( guy )
+ }
+ if ( !squadAlive )
return
+
+ array<entity> points = GetNPCArrayOfEnemies( team )
vector point
point = points[ RandomInt( points.len() ) ].GetOrigin()
- array<entity> players = GetPlayerArrayOfEnemies( guys[0].GetTeam() )
-
- // Setup AI
+ // Setup AI, first assault point
foreach ( guy in guys )
{
guy.EnableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_HAND_SIGNALS | NPC_ALLOW_FLEE )
guy.AssaultPoint( point )
guy.AssaultSetGoalRadius( 1600 ) // 1600 is minimum for npc_stalker, works fine for others
-
- // show on enemy radar
- foreach ( player in players )
- guy.Minimap_AlwaysShow( 0, player )
-
-
+
//thread AITdm_CleanupBoredNPCThread( guy )
}
@@ -396,16 +455,21 @@ void function SquadHandler( array<entity> guys )
// Stop func if our squad has been killed off
if ( guys.len() == 0 )
return
+ }
+
+ // Get point and send our whole squad to it
+ points = GetNPCArrayOfEnemies( team )
+ if ( points.len() == 0 ) // can't find any points here
+ continue
- // Get point and send guy to it
- points = GetNPCArrayOfEnemies( guy.GetTeam() )
- if ( points.len() == 0 )
- continue
-
- point = points[ RandomInt( points.len() ) ].GetOrigin()
-
- guy.AssaultPoint( point )
+ point = points[ RandomInt( points.len() ) ].GetOrigin()
+
+ foreach ( guy in guys )
+ {
+ if ( IsAlive( guy ) )
+ guy.AssaultPoint( point )
}
+
wait RandomFloatRange(5.0,15.0)
}
}