aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2022-02-17 00:39:55 +0000
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2022-02-17 00:39:55 +0000
commit072c0b798a188995770d4db1ad67c95e99d46be0 (patch)
tree68d0af73236dd9bea1f9e213ef19ca3734190366
parentc477356ca5cea97dd3d13a853b150c3ce3c544a0 (diff)
downloadNorthstarMods-072c0b798a188995770d4db1ad67c95e99d46be0.tar.gz
NorthstarMods-072c0b798a188995770d4db1ad67c95e99d46be0.zip
new spawnzone-based ctf spawn algo
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut81
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut138
2 files changed, 142 insertions, 77 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
index a0a7243b..b4ab26ea 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut
@@ -40,6 +40,7 @@ void function CaptureTheFlag_Init()
AddCallback_OnPlayerKilled( OnPlayerKilled )
AddCallback_OnPilotBecomesTitan( DropFlagForBecomingTitan )
+ SetSpawnZoneRatingFunc( DecideSpawnZone_CTF )
AddSpawnpointValidationRule( VerifyCTFSpawnpoint )
RegisterSignal( "FlagReturnEnded" )
@@ -65,69 +66,7 @@ void function CaptureTheFlag_Init()
void function RateSpawnpoints_CTF( int checkClass, array<entity> spawnpoints, int team, entity player )
{
- // ctf spawn algo iteration 4 i despise extistence
- array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
- array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( team ) )
- array<entity> enemyPlayers = GetPlayerArrayOfTeam_Alive( team )
-
- // get average startspawn position and max dist between spawns
- // could probably cache this, tbh, not like it should change outside of halftimes
- vector averageFriendlySpawns
- float averageFriendlySpawnDist
-
- int averageDistCount
-
- foreach ( entity spawn in startSpawns )
- {
- foreach ( entity otherSpawn in startSpawns )
- {
- float dist = Distance2D( spawn.GetOrigin(), otherSpawn.GetOrigin() )
- averageFriendlySpawnDist += dist
- averageDistCount++
- }
-
- averageFriendlySpawns += spawn.GetOrigin()
- }
-
- averageFriendlySpawns /= startSpawns.len()
- averageFriendlySpawnDist /= averageDistCount
-
- // get average enemy startspawn position
- vector averageEnemySpawns
-
- foreach ( entity spawn in enemyStartSpawns )
- averageEnemySpawns += spawn.GetOrigin()
-
- averageEnemySpawns /= enemyStartSpawns.len()
-
- // from here, rate spawns
- float baseDistance = Distance2D( averageFriendlySpawns, averageEnemySpawns )
- float spawnIterations = ( baseDistance / averageFriendlySpawnDist ) / 2
-
- foreach ( entity spawn in spawnpoints )
- {
- // ratings should max/min out at 100 / -100
- // start by prioritizing closer spawns, but not so much that enemies won't really affect them
- float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, spawn.GetOrigin() ) / baseDistance )
- float remainingZonePower = 1.0 // this is used to ensure that players that are in multiple zones at once shouldn't affect all those zones too hard
-
- for ( int i = 0; i < spawnIterations; i++ )
- {
- vector zonePos = averageFriendlySpawns + Normalize( averageEnemySpawns - averageFriendlySpawns ) * ( i * averageFriendlySpawnDist )
-
- float zonePower
- foreach ( entity otherPlayer in enemyPlayers )
- if ( Distance2D( otherPlayer.GetOrigin(), zonePos ) < averageFriendlySpawnDist )
- zonePower += 1.0 / enemyPlayers.len()
-
- zonePower = min( zonePower, remainingZonePower )
- remainingZonePower -= zonePower
- // scale rating based on distance between spawn and zone, baring in mind max 100 rating
- rating -= ( zonePower * 100 ) * ( 1.0 - Distance2D( spawn.GetOrigin(), zonePos ) / baseDistance )
- }
-
- spawn.CalculateRating( checkClass, player.GetTeam(), rating, rating )
- }
+ RateSpawnpoints_SpawnZones( checkClass, spawnpoints, team, player )
}
bool function VerifyCTFSpawnpoint( entity spawnpoint, int team )
@@ -179,13 +118,6 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
}
}
-void function SetupFlagMinimapIcon( entity flag )
-{
- flag.Minimap_AlwaysShow( TEAM_IMC, null )
- flag.Minimap_AlwaysShow( TEAM_MILITIA, null )
- flag.Minimap_SetAlignUpright( true )
-}
-
void function CreateFlags()
{
if ( IsValid( file.imcFlagSpawn ) )
@@ -205,7 +137,7 @@ void function CreateFlags()
// likely this is because respawn uses distance checks from spawns to check this in official
// but i don't like doing that so just using a list of maps to swap them on lol
bool switchedSides = HasSwitchedSides() == 1
- bool shouldSwap = SWAP_FLAG_MAPS.contains( GetMapName() ) || switchedSides
+ bool shouldSwap = SWAP_FLAG_MAPS.contains( GetMapName() ) ? !switchedSides : switchedSides
int flagTeam = spawn.GetTeam()
if ( shouldSwap )
@@ -228,7 +160,6 @@ void function CreateFlags()
flag.SetModel( CTF_FLAG_MODEL )
flag.SetOrigin( spawn.GetOrigin() + < 0, 0, base.GetBoundingMaxs().z * 2 > ) // ensure flag doesn't spawn clipped into geometry
flag.SetVelocity( < 0, 0, 1 > )
- SetFlagStateForTeam( flag.GetTeam(), eFlagState.None ) // reset flag state to prevent half-time oddities
flag.s.canTake <- true
flag.s.playersReturning <- []
@@ -252,7 +183,6 @@ void function CreateFlags()
{
file.imcFlagSpawn = base
file.imcFlag = flag
- SetupFlagMinimapIcon( file.imcFlag )
file.imcFlagReturnTrigger = returnTrigger
SetGlobalNetEnt( "imcFlag", file.imcFlag )
@@ -262,7 +192,6 @@ void function CreateFlags()
{
file.militiaFlagSpawn = base
file.militiaFlag = flag
- SetupFlagMinimapIcon( file.militiaFlag )
file.militiaFlagReturnTrigger = returnTrigger
SetGlobalNetEnt( "milFlag", file.militiaFlag )
@@ -344,7 +273,7 @@ void function DropFlagIfPhased( entity player, entity flag )
DropFlag( player, true )
})
- while( IsValid( flag ) && flag.GetParent() == player )
+ while( flag.GetParent() == player )
WaitFrame()
}
@@ -529,4 +458,4 @@ void function TryReturnFlag( entity player, entity flag )
MessageToTeam( GetOtherTeam( flag.GetTeam() ), eEventNotifications.PlayerReturnedEnemyFlag, null, player )
EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyReturnsFlag", GetOtherTeam( flag.GetTeam() ) )
PlayFactionDialogueToTeam( "ctf_flagReturnedEnemy", GetOtherTeam( flag.GetTeam() ) )
-}
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
index 2b1a0713..3cc4556b 100644
--- a/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
+++ b/Northstar.CustomServers/mod/scripts/vscripts/mp/spawn.nut
@@ -21,6 +21,7 @@ global function SetShouldCreateMinimapSpawnZones
global function CreateTeamSpawnZoneEntity
global function RateSpawnpoints_SpawnZones
global function DecideSpawnZone_Generic
+global function DecideSpawnZone_CTF
struct NoSpawnArea
{
@@ -590,7 +591,7 @@ entity function DecideSpawnZone_Generic( array<entity> spawnzones, int team )
array<entity> possibleZones
foreach ( entity spawnzone in spawnStateSpawnzones.mapSpawnzoneTriggers )
{
- // don't remeber if you can do a "value in table.values" sorta thing in squirrel so doing manual lookup
+ // don't remember if you can do a "value in table.values" sorta thing in squirrel so doing manual lookup
bool spawnzoneTaken = false
foreach ( int otherTeam, entity otherSpawnzone in spawnStateSpawnzones.activeTeamSpawnzones )
{
@@ -670,4 +671,139 @@ entity function DecideSpawnZone_Generic( array<entity> spawnzones, int team )
}
return spawnStateSpawnzones.activeTeamSpawnzones[ team ]
+}
+
+// ideally this should be in the gamemode_ctf file, but would need refactors to expose more stuff that's not available there rn
+entity function DecideSpawnZone_CTF( array<entity> spawnzones, int team )
+{
+ if ( spawnzones.len() == 0 )
+ return null
+
+ int otherTeam = GetOtherTeam( team )
+ array<entity> enemyPlayers = GetPlayerArrayOfTeam( otherTeam )
+
+ // get average team startspawn positions
+ int spawnCompareTeam = team
+ if ( HasSwitchedSides() )
+ spawnCompareTeam = GetOtherTeam( team )
+
+ array<entity> startSpawns = SpawnPoints_GetPilotStart( spawnCompareTeam )
+ array<entity> enemyStartSpawns = SpawnPoints_GetPilotStart( GetOtherTeam( spawnCompareTeam ) )
+
+ if ( startSpawns.len() == 0 || enemyStartSpawns.len() == 0 ) // ensure we don't crash
+ return null
+
+ // get average startspawn position and max dist between spawns
+ // could probably cache this, tbh, not like it should change outside of halftimes
+ vector averageFriendlySpawns
+ foreach ( entity spawn in startSpawns )
+ averageFriendlySpawns += spawn.GetOrigin()
+
+ averageFriendlySpawns /= startSpawns.len()
+
+ // get average enemy startspawn position
+ vector averageEnemySpawns
+ foreach ( entity spawn in enemyStartSpawns )
+ averageEnemySpawns += spawn.GetOrigin()
+
+ averageEnemySpawns /= enemyStartSpawns.len()
+
+ float baseDistance = Distance2D( averageFriendlySpawns, averageEnemySpawns )
+
+ // find new zone
+ array<entity> possibleZones
+ foreach ( entity spawnzone in spawnStateSpawnzones.mapSpawnzoneTriggers )
+ {
+ // can't choose zone if another team has it
+ if ( otherTeam in spawnStateSpawnzones.activeTeamSpawnzones && spawnStateSpawnzones.activeTeamSpawnzones[ otherTeam ] == spawnzone )
+ continue
+
+ // check zone validity
+ bool spawnzoneEvil = false
+ foreach ( entity player in enemyPlayers )
+ {
+ // couldn't get IsTouching, GetTouchingEntities or enter callbacks to work in testing, so doing this
+ if ( spawnzone.ContainsPoint( player.GetOrigin() ) )
+ {
+ spawnzoneEvil = true
+ break
+ }
+ }
+
+ // don't choose spawnzones that are closer to enemy base than friendly base
+ if ( !spawnzoneEvil && Distance2D( spawnzone.GetOrigin(), averageFriendlySpawns ) > Distance2D( spawnzone.GetOrigin(), averageEnemySpawns ) )
+ spawnzoneEvil = true
+
+ if ( spawnzoneEvil )
+ continue
+
+ // rate spawnzone based on distance to frontline
+ Frontline frontline = GetFrontline( team )
+
+ // prefer spawns close to base pos
+ float rating = 10 * ( 1.0 - Distance2D( averageFriendlySpawns, spawnzone.GetOrigin() ) / baseDistance )
+
+ if ( frontline.friendlyCenter != < 0, 0, 0 > )
+ {
+ // rate based on distance to frontline, and then prefer spawns in the same dir from the frontline as the combatdir
+ rating += rating * ( 1.0 - ( Distance2D( spawnzone.GetOrigin(), frontline.friendlyCenter ) / baseDistance ) )
+ rating *= fabs( frontline.combatDir.y - Normalize( spawnzone.GetOrigin() - averageFriendlySpawns ).y )
+
+ // reduce rating based on players that can currently see the zone
+ bool hasAppliedInitialLoss = false
+ foreach ( entity player in enemyPlayers )
+ {
+ // don't trace here, just do an angle check
+ if ( PlayerCanSee( player, spawnzone, false, 65 ) && Distance2D( player.GetOrigin(), spawnzone.GetOrigin() ) <= 2000.0 )
+ {
+ float distFrac = TraceLineSimple( player.GetOrigin(), spawnzone.GetOrigin(), player )
+
+ if ( distFrac >= 0.65 )
+ {
+ // give a fairly large loss if literally anyone can see it
+ if ( !hasAppliedInitialLoss )
+ {
+ rating *= 0.8
+ hasAppliedInitialLoss = true
+ }
+
+ rating *= ( 1.0 / enemyPlayers.len() ) * distFrac
+ }
+ }
+ }
+ }
+
+ spawnzone.s.spawnzoneRating = rating
+ possibleZones.append( spawnzone )
+ }
+
+ if ( possibleZones.len() == 0 )
+ return null
+
+ possibleZones.sort( int function( entity a, entity b )
+ {
+ if ( a.s.spawnzoneRating > b.s.spawnzoneRating )
+ return -1
+
+ if ( b.s.spawnzoneRating > a.s.spawnzoneRating )
+ return 1
+
+ return 0
+ } )
+ entity chosenZone = possibleZones[ minint( RandomInt( 3 ), possibleZones.len() - 1 ) ]
+
+ if ( spawnStateSpawnzones.shouldCreateMinimapSpawnzones )
+ {
+ entity oldEnt
+ if ( team in spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts )
+ oldEnt = spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ]
+
+ spawnStateSpawnzones.activeTeamSpawnzoneMinimapEnts[ team ] <- CreateTeamSpawnZoneEntity( chosenZone, team )
+ if ( IsValid( oldEnt ) )
+ oldEnt.Destroy()
+ }
+
+ spawnStateSpawnzones.activeTeamSpawnzones[ team ] <- chosenZone
+
+ return spawnStateSpawnzones.activeTeamSpawnzones[ team ]
} \ No newline at end of file