aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/gamemodes
diff options
context:
space:
mode:
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/gamemodes')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_frontline.gnut1
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_gamemodes.gnut6
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_capture_point.gnut1
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_frontline.gnut159
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_aitdm.nut12
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_at.nut18
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut12
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_cp.nut12
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut512
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_fd.nut12
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ffa.nut14
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut235
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_mfd.nut230
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ps.nut12
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut137
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_tdm.nut19
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ttdm.nut6
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_hardpoints.gnut33
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_riff_floor_is_lava.nut102
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_spawnpoints.gnut0
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemode_mfd.nut66
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut816
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes_custom.gnut20
23 files changed, 2435 insertions, 0 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_frontline.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_frontline.gnut
new file mode 100644
index 000000000..37b891699
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_frontline.gnut
@@ -0,0 +1 @@
+//fuck \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_gamemodes.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
new file mode 100644
index 000000000..cf7f7e150
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_ai_gamemodes.gnut
@@ -0,0 +1,6 @@
+global function AiGameModes_Init
+
+void function AiGameModes_Init()
+{
+
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_capture_point.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_capture_point.gnut
new file mode 100644
index 000000000..37b891699
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_capture_point.gnut
@@ -0,0 +1 @@
+//fuck \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_frontline.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_frontline.gnut
new file mode 100644
index 000000000..7ece7dc16
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_frontline.gnut
@@ -0,0 +1,159 @@
+untyped
+
+
+global function GetFrontline
+global function SetFrontline
+global function AddCalculateFrontlineCallback
+
+const DEBUG_FRONTLINE = false
+
+global struct Frontline
+{
+ vector origin = Vector( 0.0, 0.0, 0.0 )
+ vector combatDir = Vector( 0.0, 0.0, 0.0 )
+ vector line = Vector( 0.0, 0.0, 0.0 )
+ vector friendlyCenter = Vector( 0.0, 0.0, 0.0 )
+ vector enemyCenter = Vector( 0.0, 0.0, 0.0 )
+ float lastCalcTime = -1.0
+}
+
+struct
+{
+ Frontline frontline
+ array<void functionref()> calculateFrontlineCallbacks
+} file
+
+Frontline function GetFrontline( team )
+{
+ if ( file.frontline.lastCalcTime < Time() )
+ {
+ CalculateFrontline()
+ file.frontline.lastCalcTime = Time()
+ }
+
+ Frontline fl
+ fl = clone file.frontline
+
+ if ( team == TEAM_MILITIA )
+ {
+ fl.combatDir *= -1.0
+ vector temp = fl.friendlyCenter
+ fl.friendlyCenter = fl.enemyCenter
+ fl.enemyCenter = temp
+ }
+
+ return fl
+}
+
+void function AddCalculateFrontlineCallback( void functionref() callbackFunc )
+{
+ // Check if this function has already been added
+ #if DEV
+ foreach ( func in file.calculateFrontlineCallbacks )
+ {
+ Assert( func != callbackFunc )
+ }
+ #endif
+
+ file.calculateFrontlineCallbacks.append( callbackFunc )
+}
+
+void function CalculateFrontline()
+{
+ #if DEV
+ float debugTime = 0.2
+ #endif
+
+ if ( file.calculateFrontlineCallbacks.len() > 0 )
+ {
+ foreach ( callbackFunc in file.calculateFrontlineCallbacks )
+ {
+ callbackFunc()
+ }
+ }
+ else
+ {
+ vector militiaCenter = CalculateWeightedTeamCenter( TEAM_MILITIA )
+ vector imcCenter = CalculateWeightedTeamCenter( TEAM_IMC )
+
+ file.frontline.friendlyCenter = imcCenter // friendlyCenter is for TEAM_IMC by default
+ file.frontline.enemyCenter = militiaCenter
+
+ file.frontline.origin = ( militiaCenter + imcCenter ) * 0.5
+ file.frontline.combatDir = Normalize( militiaCenter - imcCenter ) // combatDir is for TEAM_IMC by default
+ file.frontline.line = CrossProduct( file.frontline.combatDir, Vector( 0.0, 0.0, 1.0 ) )
+
+ #if DEV
+ if ( DEBUG_FRONTLINE )
+ {
+ DrawBox( militiaCenter, Vector( -8.0, -8.0, -8.0 ), Vector( 8.0, 8.0, 8.0 ), 255, 102, 0, true, debugTime )
+ DrawBox( imcCenter, Vector( -8.0, -8.0, -8.0 ), Vector( 8.0, 8.0, 8.0 ), 0, 0, 255, true, debugTime )
+ DebugDrawLine( militiaCenter, imcCenter, 0, 255, 0, true, debugTime )
+ }
+ #endif
+ }
+
+ #if DEV
+ if ( DEBUG_FRONTLINE )
+ {
+ DrawBox( file.frontline.origin, Vector( -32.0, -32.0, -32.0 ), Vector( 32.0, 32.0, 32.0 ), 255, 0, 0, true, debugTime )
+ DebugDrawLine( file.frontline.origin - file.frontline.line * 500.0, file.frontline.origin + file.frontline.line * 500.0, 255, 0, 0, true, debugTime )
+ }
+ #endif
+}
+
+void function SetFrontline( vector origin, vector combatDir )
+{
+ file.frontline.origin = origin
+ file.frontline.combatDir = combatDir
+ file.frontline.line = CrossProduct( file.frontline.combatDir, Vector( 0.0, 0.0, 1.0 ) )
+}
+
+vector function CalculateWeightedTeamCenter( int team )
+{
+ array<entity> teamPlayers = GetPlayerArrayOfTeam_Alive( team )
+ int teamPlayersCount = teamPlayers.len()
+
+ if ( teamPlayersCount == 0 )
+ return Vector( 0.0, 0.0, 0.0 )
+
+ // find minimum distances between teammates
+ array<float> minTeammateDistances// = arrayofsize( teamPlayersCount, 99999.0 )
+ minTeammateDistances.resize( teamPlayersCount, 99999.0 )
+
+ for ( int i = 0; i < teamPlayersCount; i++ )
+ {
+ entity playerI = teamPlayers[ i ]
+
+ for ( int j = i + 1; j < teamPlayersCount; j++ )
+ {
+ entity playerJ = teamPlayers[ j ]
+ float distanceBetweenPlayers = Distance( playerI.GetOrigin(), playerJ.GetOrigin() )
+
+ if ( distanceBetweenPlayers < minTeammateDistances[ i ] )
+ minTeammateDistances[ i ] = distanceBetweenPlayers
+
+ if ( distanceBetweenPlayers < minTeammateDistances[ j ] )
+ minTeammateDistances[ j ] = distanceBetweenPlayers
+ }
+ }
+
+ vector weightedOrgSum = Vector( 0.0, 0.0, 0.0 )
+ float weightSum = 0.0
+ float weight = 0.0
+ float halfPi = 1.57 // passing a fraction of this value into sin which gives us the first part of a sin wave from 0 - 1
+ float maxPossibleDistance = MAX_WORLD_RANGE
+ float magicNumber = 14.0 // magic number gives the desired falloff
+
+ // calculate a weighted origin based on how close players are to teammates
+ foreach ( index, player in teamPlayers )
+ {
+ float radians = halfPi * ( minTeammateDistances[ index ] / maxPossibleDistance ) // radians will be a value between 0 - halfPi
+ weight = pow( ( 1.0 - sin( radians ) ), magicNumber ) // pow squashes the result so the curve has the falloff that's desired
+
+ weightedOrgSum += player.GetOrigin() * weight
+ weightSum += weight
+ }
+
+ return weightedOrgSum / weightSum
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_aitdm.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
new file mode 100644
index 000000000..a30944cf3
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_aitdm.nut
@@ -0,0 +1,12 @@
+global function GamemodeAITdm_Init
+global function RateSpawnpoints_Frontline
+
+void function GamemodeAITdm_Init()
+{
+
+}
+
+void function RateSpawnpoints_Frontline(int _0, array<entity> _1, int _2, entity _3)
+{
+
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_at.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_at.nut
new file mode 100644
index 000000000..b75ed51b6
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_at.nut
@@ -0,0 +1,18 @@
+global function GamemodeAt_Init
+global function RateSpawnpoints_AT
+global function RateSpawnpoints_SpawnZones
+
+void function GamemodeAt_Init()
+{
+
+}
+
+void function RateSpawnpoints_AT( int checkclass, array<entity> spawnpoints, int team, entity player )
+{
+ RateSpawnpoints_Generic( checkclass, spawnpoints, team, player ) // temp
+}
+
+void function RateSpawnpoints_SpawnZones( int checkclass, array<entity> spawnpoints, int team, entity player )
+{
+ RateSpawnpoints_Generic( checkclass, spawnpoints, team, player ) // temp
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
new file mode 100644
index 000000000..d8ccfc424
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_coliseum.nut
@@ -0,0 +1,12 @@
+global function GamemodeColiseum_Init
+global function GamemodeColiseum_CustomIntro
+
+void function GamemodeColiseum_Init()
+{
+
+}
+
+void function GamemodeColiseum_CustomIntro(entity _0)
+{
+
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_cp.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_cp.nut
new file mode 100644
index 000000000..2fa7e4ebe
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_cp.nut
@@ -0,0 +1,12 @@
+global function GamemodeCP_Init
+global function RateSpawnpoints_CP
+
+void function GamemodeCP_Init()
+{
+
+}
+
+void function RateSpawnpoints_CP(int _0, array<entity> _1, int _2, entity _3)
+{
+
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut
new file mode 100644
index 000000000..e710a9118
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut
@@ -0,0 +1,512 @@
+untyped
+// this needs a refactor lol
+
+global function CaptureTheFlag_Init
+global function RateSpawnpoints_CTF
+
+const array<string> SWAP_FLAG_MAPS = [
+ "mp_forwardbase_kodai",
+ "mp_lf_meadow"
+]
+
+struct {
+ entity imcFlagSpawn
+ entity imcFlag
+ entity imcFlagReturnTrigger
+
+ entity militiaFlagSpawn
+ entity militiaFlag
+ entity militiaFlagReturnTrigger
+
+ array<entity> imcCaptureAssistList
+ array<entity> militiaCaptureAssistList
+} file
+
+void function CaptureTheFlag_Init()
+{
+ PrecacheModel( CTF_FLAG_MODEL )
+ PrecacheModel( CTF_FLAG_BASE_MODEL )
+
+ CaptureTheFlagShared_Init()
+ SetSwitchSidesBased( true )
+ SetSuddenDeathBased( true )
+ //SetSpawnsUseFrontline( true )
+
+ AddCallback_OnClientConnected( CTFInitPlayer )
+
+ AddCallback_GameStateEnter( eGameState.Prematch, CreateFlags )
+ AddCallback_OnTouchHealthKit( "item_flag", OnFlagCollected )
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+ AddCallback_OnPilotBecomesTitan( DropFlagForBecomingTitan )
+
+ RegisterSignal( "FlagReturnEnded" )
+ RegisterSignal( "ResetDropTimeout" )
+
+ // setup stuff for the functions in sh_gamemode_ctf
+ // don't really like using level for stuff but just how it be
+ level.teamFlags <- {}
+
+ // setup score event earnmeter values
+ ScoreEvent_SetEarnMeterValues( "KillPilot", 0.05, 0.20 )
+ ScoreEvent_SetEarnMeterValues( "Headshot", 0.0, 0.02 )
+ ScoreEvent_SetEarnMeterValues( "FirstStrike", 0.0, 0.05 )
+ ScoreEvent_SetEarnMeterValues( "KillTitan", 0.0, 0.25 )
+ ScoreEvent_SetEarnMeterValues( "PilotBatteryStolen", 0.0, 0.35 )
+
+ ScoreEvent_SetEarnMeterValues( "FlagCarrierKill", 0.0, 0.20 )
+ ScoreEvent_SetEarnMeterValues( "FlagTaken", 0.0, 0.10 )
+ ScoreEvent_SetEarnMeterValues( "FlagCapture", 0.0, 0.30 )
+ ScoreEvent_SetEarnMeterValues( "FlagCaptureAssist", 0.0, 0.20 )
+ ScoreEvent_SetEarnMeterValues( "FlagReturn", 0.0, 0.20 )
+}
+
+void function RateSpawnpoints_CTF( int checkClass, array<entity> spawnpoints, int team, entity player )
+{
+ // ok this is the 3rd time rewriting this due to not understanding ctf spawns properly
+ // legit just
+ // if there are no enemies in base, spawn them in base
+ // if there are, spawn them outside of it ( but ideally still close )
+ // max distance away should be like, angel city markets
+
+ array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
+ array<entity> enemyPlayers = GetPlayerArrayOfTeam_Alive( GetOtherTeam( team ) )
+
+ vector startSpawnAverage
+ bool enemyInBase = false
+ foreach ( entity startSpawn in startSpawns )
+ {
+ startSpawnAverage += startSpawn.GetOrigin()
+
+ foreach ( entity enemy in enemyPlayers )
+ {
+ if ( Distance( startSpawn.GetOrigin(), enemy.GetOrigin() ) <= 1000.0 )
+ {
+ enemyInBase = true
+ break
+ }
+ }
+ }
+
+ startSpawnAverage /= startSpawns.len()
+
+ print( "spawn for " + player + " is there an enemy in base?" + enemyInBase )
+
+ foreach ( entity spawn in spawnpoints )
+ {
+ float rating = 0.0
+
+ bool isStart = false
+ foreach ( entity startSpawn in startSpawns )
+ {
+ if ( Distance2D( spawn.GetOrigin(), startSpawn.GetOrigin() ) < 1500.0 ) // this was for some reason the only distance i could get to work
+ {
+ isStart = true
+ break
+ }
+ }
+
+ if ( isStart )
+ {
+ if ( !enemyInBase )
+ rating = 1000 + RandomFloat( 100.0 )
+ else
+ rating = -1000.0
+ }
+ else if ( !isStart && enemyInBase )
+ {
+ entity friendlyFlag
+ entity enemyFlag
+ if ( team == TEAM_IMC )
+ {
+ friendlyFlag = file.imcFlagSpawn
+ enemyFlag = file.militiaFlagSpawn
+ }
+ else
+ {
+ friendlyFlag = file.militiaFlagSpawn
+ enemyFlag = file.imcFlagSpawn
+ }
+
+ float dist = Distance2D( spawn.GetOrigin(), enemyFlag.GetOrigin() )
+ float flagDist = Distance2D( startSpawnAverage, enemyFlag.GetOrigin() )
+
+ if ( dist < ( flagDist / 2 ) ) // spawns shouldn't be closer to enemies than they are to us
+ rating = -1000.0
+ if ( dist > flagDist * 1.1 ) // spawn is behind startspawns
+ rating = -1000.0
+ else
+ {
+ rating = dist // closer spawns are better
+
+ foreach( entity enemy in enemyPlayers ) // reduce rating if enemies are near by
+ if ( Distance( enemy.GetOrigin(), spawn.GetOrigin() ) < 500.0 )
+ rating /= 2
+ }
+ }
+
+ spawn.CalculateRating( checkClass, team, rating, rating )
+ }
+}
+
+void function CTFInitPlayer( entity player )
+{
+ if ( !IsValid( file.imcFlagSpawn ) )
+ return
+
+ vector imcSpawn = file.imcFlagSpawn.GetOrigin()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_IMC, imcSpawn.x, imcSpawn.y, imcSpawn.z )
+
+ vector militiaSpawn = file.militiaFlagSpawn.GetOrigin()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_MILITIA, militiaSpawn.x, militiaSpawn.y, militiaSpawn.z )
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( GetFlagForTeam( GetOtherTeam( victim.GetTeam() ) ).GetParent() == victim )
+ {
+ if ( victim != attacker && attacker.IsPlayer() )
+ AddPlayerScore( attacker, "FlagCarrierKill", victim )
+
+ DropFlag( victim )
+ }
+}
+
+void function CreateFlags()
+{
+ if ( IsValid( file.imcFlagSpawn ) )
+ {
+ file.imcFlagSpawn.Destroy()
+ file.imcFlag.Destroy()
+ file.imcFlagReturnTrigger.Destroy()
+
+ file.militiaFlagSpawn.Destroy()
+ file.militiaFlag.Destroy()
+ file.militiaFlagReturnTrigger.Destroy()
+ }
+
+ foreach ( entity spawn in GetEntArrayByClass_Expensive( "info_spawnpoint_flag" ) )
+ {
+ // on some maps flags are on the opposite side from what they should be
+ // 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 : switchedSides
+
+ int flagTeam = spawn.GetTeam()
+ if ( shouldSwap )
+ {
+ flagTeam = GetOtherTeam( flagTeam )
+ SetTeam( spawn, flagTeam )
+ }
+
+ // create flag base
+ entity base = CreatePropDynamic( CTF_FLAG_BASE_MODEL, spawn.GetOrigin(), spawn.GetAngles(), 0 )
+ SetTeam( base, spawn.GetTeam() )
+ svGlobal.flagSpawnPoints[ flagTeam ] = base
+
+ // create flag
+ entity flag = CreateEntity( "item_flag" )
+ flag.SetValueForModelKey( CTF_FLAG_MODEL )
+ SetTeam( flag, flagTeam )
+ flag.MarkAsNonMovingAttachment()
+ DispatchSpawn( flag )
+ 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 > )
+
+ flag.s.canTake <- true
+ flag.s.playersReturning <- []
+
+ level.teamFlags[ flag.GetTeam() ] <- flag
+
+ entity returnTrigger = CreateEntity( "trigger_cylinder" )
+ SetTeam( returnTrigger, flagTeam )
+ returnTrigger.SetRadius( CTF_GetFlagReturnRadius() )
+ returnTrigger.SetAboveHeight( CTF_GetFlagReturnRadius() )
+ returnTrigger.SetBelowHeight( CTF_GetFlagReturnRadius() )
+
+ returnTrigger.SetEnterCallback( OnPlayerEntersFlagReturnTrigger )
+ returnTrigger.SetLeaveCallback( OnPlayerExitsFlagReturnTrigger )
+
+ DispatchSpawn( returnTrigger )
+
+ thread TrackFlagReturnTrigger( flag, returnTrigger )
+
+ if ( flagTeam == TEAM_IMC )
+ {
+ file.imcFlagSpawn = base
+ file.imcFlag = flag
+ file.imcFlagReturnTrigger = returnTrigger
+
+ SetGlobalNetEnt( "imcFlag", file.imcFlag )
+ SetGlobalNetEnt( "imcFlagHome", file.imcFlagSpawn )
+ }
+ else
+ {
+ file.militiaFlagSpawn = base
+ file.militiaFlag = flag
+ file.militiaFlagReturnTrigger = returnTrigger
+
+ SetGlobalNetEnt( "milFlag", file.militiaFlag )
+ SetGlobalNetEnt( "milFlagHome", file.militiaFlagSpawn )
+ }
+ }
+
+ foreach ( entity player in GetPlayerArray() )
+ CTFInitPlayer( player )
+}
+
+void function TrackFlagReturnTrigger( entity flag, entity returnTrigger )
+{
+ // this is a bit of a hack, it seems parenting the return trigger to the flag actually sets the pickup radius of the flag to be the same as the trigger
+ // this isn't wanted since only pickups should use that additional radius
+ flag.EndSignal( "OnDestroy" )
+
+ while ( true )
+ {
+ returnTrigger.SetOrigin( flag.GetOrigin() )
+ WaitFrame()
+ }
+}
+
+void function SetFlagStateForTeam( int team, int state )
+{
+ if ( state == eFlagState.Away ) // we tell the client the flag is the player carrying it if they're carrying it
+ SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", ( team == TEAM_IMC ? file.imcFlag : file.militiaFlag ).GetParent() )
+ else
+ SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", team == TEAM_IMC ? file.imcFlag : file.militiaFlag )
+
+ SetGlobalNetInt( team == TEAM_IMC ? "imcFlagState" : "milFlagState", state )
+}
+
+bool function OnFlagCollected( entity player, entity flag )
+{
+ if ( !IsAlive( player ) || flag.GetParent() != null || player.IsTitan() || player.IsPhaseShifted() )
+ return false
+
+ if ( player.GetTeam() != flag.GetTeam() && flag.s.canTake )
+ GiveFlag( player, flag ) // pickup enemy flag
+ else if ( player.GetTeam() == flag.GetTeam() && IsFlagHome( flag ) && PlayerHasEnemyFlag( player ) )
+ CaptureFlag( player, GetFlagForTeam( GetOtherTeam( flag.GetTeam() ) ) ) // cap the flag
+
+ return false // don't wanna delete the flag entity
+}
+
+void function GiveFlag( entity player, entity flag )
+{
+ print( player + " picked up the flag!" )
+ flag.Signal( "ResetDropTimeout" )
+
+ flag.SetParent( player, "FLAG" )
+ thread DropFlagIfPhased( player, flag )
+
+ // do notifications
+ MessageToPlayer( player, eEventNotifications.YouHaveTheEnemyFlag )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_GrabFlag" )
+ AddPlayerScore( player, "FlagTaken", player )
+ PlayFactionDialogueToPlayer( "ctf_flagPickupYou", player )
+
+ MessageToTeam( player.GetTeam(), eEventNotifications.PlayerHasEnemyFlag, player, player )
+ EmitSoundOnEntityToTeamExceptPlayer( flag, "UI_CTF_3P_TeamGrabFlag", player.GetTeam(), player )
+ PlayFactionDialogueToTeamExceptPlayer( "ctf_flagPickupFriendly", player.GetTeam(), player )
+
+ MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerHasFriendlyFlag, player, player )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_EnemyGrabFlag", flag.GetTeam() )
+
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.Away ) // used for held
+}
+
+void function DropFlagIfPhased( entity player, entity flag )
+{
+ player.EndSignal( "StartPhaseShift" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ DropFlag( player, true )
+ })
+
+ while( flag.GetParent() == player )
+ WaitFrame()
+}
+
+void function DropFlagForBecomingTitan( entity pilot, entity titan )
+{
+ DropFlag( pilot, true )
+}
+
+void function DropFlag( entity player, bool realDrop = true )
+{
+ entity flag = GetFlagForTeam( GetOtherTeam( player.GetTeam() ) )
+
+ if ( flag.GetParent() != player )
+ return
+
+ print( player + " dropped the flag!" )
+
+ flag.ClearParent()
+ flag.SetAngles( < 0, 0, 0 > )
+ flag.SetVelocity( < 0, 0, 0 > )
+
+ if ( realDrop )
+ {
+ // start drop timeout countdown
+ thread TrackFlagDropTimeout( flag )
+
+ // add to capture assists
+ if ( player.GetTeam() == TEAM_IMC )
+ file.imcCaptureAssistList.append( player )
+ else
+ file.militiaCaptureAssistList.append( player )
+
+ // do notifications
+ MessageToPlayer( player, eEventNotifications.YouDroppedTheEnemyFlag )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagDrop" )
+
+ MessageToTeam( player.GetTeam(), eEventNotifications.PlayerDroppedEnemyFlag, player, player )
+ // todo need a sound here maybe
+
+ MessageToTeam( GetOtherTeam( player.GetTeam() ), eEventNotifications.PlayerDroppedFriendlyFlag, player, player )
+ // todo need a sound here maybe
+ }
+
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.Home ) // used for return prompt
+}
+
+void function TrackFlagDropTimeout( entity flag )
+{
+ flag.EndSignal( "ResetDropTimeout" )
+
+ wait CTF_GetDropTimeout()
+
+ ResetFlag( flag )
+}
+
+void function ResetFlag( entity flag )
+{
+ // ensure we can't pickup the flag after it's been dropped but before it's been reset
+ flag.s.canTake = false
+
+ if ( flag.GetParent() != null )
+ DropFlag( flag.GetParent(), false )
+
+ entity spawn
+ if ( flag.GetTeam() == TEAM_IMC )
+ spawn = file.imcFlagSpawn
+ else
+ spawn = file.militiaFlagSpawn
+
+ flag.SetOrigin( spawn.GetOrigin() + < 0, 0, spawn.GetBoundingMaxs().z + 1 > )
+
+ // we can take it again now
+ flag.s.canTake = true
+
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.None ) // used for home
+
+ flag.Signal( "ResetDropTimeout" )
+}
+
+void function CaptureFlag( entity player, entity flag )
+{
+ // reset flag
+ ResetFlag( flag )
+
+ print( player + " captured the flag!" )
+
+ // score
+ int team = player.GetTeam()
+ AddTeamScore( team, 1 )
+ AddPlayerScore( player, "FlagCapture", player )
+ player.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 ) // add 1 to captures on scoreboard
+
+ array<entity> assistList
+ if ( player.GetTeam() == TEAM_IMC )
+ assistList = file.imcCaptureAssistList
+ else
+ assistList = file.militiaCaptureAssistList
+
+ foreach( entity assistPlayer in assistList )
+ if ( player != assistPlayer )
+ AddPlayerScore( assistPlayer, "FlagCaptureAssist", player )
+
+ assistList.clear()
+
+ // notifs
+ MessageToPlayer( player, eEventNotifications.YouCapturedTheEnemyFlag )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_PlayerScore" )
+
+ MessageToTeam( team, eEventNotifications.PlayerCapturedEnemyFlag, player, player )
+ EmitSoundOnEntityToTeamExceptPlayer( flag, "UI_CTF_3P_TeamScore", player.GetTeam(), player )
+
+ MessageToTeam( GetOtherTeam( team ), eEventNotifications.PlayerCapturedFriendlyFlag, player, player )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyScore", flag.GetTeam() )
+
+ if ( GameRules_GetTeamScore( team ) == GameMode_GetRoundScoreLimit( GAMETYPE ) - 1 )
+ {
+ PlayFactionDialogueToTeam( "ctf_notifyWin1more", team )
+ PlayFactionDialogueToTeam( "ctf_notifyLose1more", GetOtherTeam( team ) )
+ }
+}
+
+void function OnPlayerEntersFlagReturnTrigger( entity trigger, entity player )
+{
+ entity flag
+ if ( trigger.GetTeam() == TEAM_IMC )
+ flag = file.imcFlag
+ else
+ flag = file.militiaFlag
+
+ if ( !player.IsPlayer() || !player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
+ return
+
+ thread TryReturnFlag( player, flag )
+}
+
+void function OnPlayerExitsFlagReturnTrigger( entity trigger, entity player )
+{
+ entity flag
+ if ( trigger.GetTeam() == TEAM_IMC )
+ flag = file.imcFlag
+ else
+ flag = file.militiaFlag
+
+ if ( !player.IsPlayer() || !player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
+ return
+
+ player.Signal( "FlagReturnEnded" )
+}
+
+void function TryReturnFlag( entity player, entity flag )
+{
+ // start return progress bar
+ Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StartReturnFlagProgressBar", Time() + CTF_GetFlagReturnTime() )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagReturnMeter" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ // cleanup
+ Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StopReturnFlagProgressBar" )
+ StopSoundOnEntity( player, "UI_CTF_1P_FlagReturnMeter" )
+ })
+
+ player.EndSignal( "FlagReturnEnded" )
+ player.EndSignal( "OnDeath" )
+
+ wait CTF_GetFlagReturnTime()
+
+ // flag return succeeded
+ // return flag
+ ResetFlag( flag )
+
+ // do notifications for return
+ MessageToPlayer( player, eEventNotifications.YouReturnedFriendlyFlag )
+ AddPlayerScore( player, "FlagReturn", player )
+ player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, 1 )
+
+ MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerReturnedFriendlyFlag, null, player )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_TeamReturnsFlag", flag.GetTeam() )
+ PlayFactionDialogueToTeam( "ctf_flagReturnedFriendly", flag.GetTeam() )
+
+ 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/scripts/vscripts/gamemodes/_gamemode_fd.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_fd.nut
new file mode 100644
index 000000000..b5f700e51
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_fd.nut
@@ -0,0 +1,12 @@
+global function GamemodeFD_Init
+global function RateSpawnpoints_FD
+
+void function GamemodeFD_Init()
+{
+
+}
+
+void function RateSpawnpoints_FD(int _0, array<entity> _1, int _2, entity _3)
+{
+
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ffa.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ffa.nut
new file mode 100644
index 000000000..3292693a8
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ffa.nut
@@ -0,0 +1,14 @@
+global function FFA_Init
+
+void function FFA_Init()
+{
+ Evac_SetEnabled( false )
+
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() )
+ AddTeamScore( attacker.GetTeam(), 1 )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut
new file mode 100644
index 000000000..18cf97359
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_lts.nut
@@ -0,0 +1,235 @@
+untyped
+global function GamemodeLts_Init
+
+struct {
+ entity lastDamageInfoVictim
+ entity lastDamageInfoAttacker
+ int lastDamageInfoMethodOfDeath
+ float lastDamageInfoTime
+
+ bool shouldDoHighlights
+} file
+
+void function GamemodeLts_Init()
+{
+ // gamemode settings
+ SetShouldUsePickLoadoutScreen( true )
+ SetRoundBased( true )
+ SetRespawnsEnabled( false )
+ Riff_ForceSetEliminationMode( eEliminationMode.PilotsTitans )
+ SetServerVar( "roundWinningKillReplayEnabled", true ) // really ought to get a function for setting this
+
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+ AddDeathCallback( "npc_titan", OnTitanKilled )
+
+ AddDamageCallback( "player", OnPlayerDamaged )
+ AddDamageCallback( "npc_titan", OnTitanDamaged )
+
+ AddCallback_OnPilotBecomesTitan( GamemodeLTS_RefreshHighlight )
+ AddCallback_OnTitanBecomesPilot( GamemodeLTS_RefreshHighlight )
+
+ ClassicMP_SetCustomIntro( GamemodeLTS_Intro, 0.0 )
+}
+
+void function GamemodeLTS_Intro()
+{
+ AddCallback_GameStateEnter( eGameState.Prematch, GamemodeLTS_IntroOnPrematchStart )
+}
+
+void function GamemodeLTS_IntroOnPrematchStart()
+{
+ ClassicMP_OnIntroStarted()
+
+ SetGameState( eGameState.Playing )
+ foreach ( entity player in GetPlayerArray() )
+ thread GamemodeLTS_IntroSpawnPlayer( player )
+
+ ClassicMP_OnIntroFinished()
+
+ SetKillcamsEnabled( true )
+ file.shouldDoHighlights = false
+ thread GamemodeLTS_PlayingThink()
+}
+
+void function GamemodeLTS_IntroSpawnPlayer( entity player )
+{
+ if ( IsAlive( player ) )
+ {
+ player.Die()
+ WaitFrame()
+ }
+
+ RespawnAsTitan( player, false )
+
+ while ( !player.IsTitan() )
+ WaitFrame()
+
+ TryGameModeAnnouncement( player )
+}
+
+void function GamemodeLTS_PlayingThink()
+{
+ WaitFrame() // due to how this is all written the prematch callbacks might not've run by the time this starts
+ // so we need to wait a frame to ensure they've been run so gameEndTime is set
+ svGlobal.levelEnt.EndSignal( "RoundEnd" ) // end this on round end
+
+ float endTime = expect float ( GetServerVar( "gameEndTime" ) )
+ print( "ENDTIME " + endTime )
+
+ // wait until 30sec left
+ wait endTime - 30 - Time()
+ foreach ( entity player in GetPlayerArray() )
+ {
+ // warn there's 30 seconds left
+ Remote_CallFunction_NonReplay( player, "ServerCallback_LTSThirtySecondWarning" )
+
+ // do highlights
+ file.shouldDoHighlights = true
+ GamemodeLTS_RefreshHighlight( player, null )
+ }
+
+ wait endTime - Time()
+ thread CheckTitansForDraw() // need to thread this so we don't accidentally signal roundend in the same thread that'll be ended when we hit roundend
+}
+
+void function GamemodeLTS_RefreshHighlight( entity player, entity titan )
+{
+ if ( !file.shouldDoHighlights )
+ return
+
+ Highlight_SetEnemyHighlight( player, "enemy_sonar" ) // i think this needs a different effect, this works for now tho
+
+ if ( player.GetPetTitan() != null )
+ Highlight_SetEnemyHighlight( player.GetPetTitan(), "enemy_sonar" )
+}
+
+void function CheckTeamTitans( int team )
+{
+ if ( GetGameState() != eGameState.Playing )
+ return
+
+ array<entity> teamPlayers = GetPlayerArrayOfTeam( team )
+
+ int numLivingTitans = 0
+ int numLivingPlayers = 0
+ foreach ( entity player in teamPlayers )
+ {
+ // wouldn't it be easier just to only track and increment numLivingTitans if the owner is alive?
+ // yes it would
+ // but for some reason this is not how respawn does it
+ if ( IsAlive( player ) )
+ numLivingPlayers++
+
+ if ( IsAlive( player.GetPetTitan() ) || player.IsTitan() )
+ numLivingTitans++
+ }
+
+ if ( numLivingPlayers == 0 || numLivingTitans == 0 )
+ {
+ SetKillcamsEnabled( false ) // make sure killcams can't interrupt the round winning kill replay
+ //SetRoundWinningKillReplayInfo( file.lastDamageInfoVictim, file.lastDamageInfoAttacker, file.lastDamageInfoMethodOfDeath, file.lastDamageInfoTime )
+ SetWinner( GetOtherTeam( team ), "#GAMEMODE_ENEMY_TITANS_DESTROYED", "#GAMEMODE_FRIENDLY_TITANS_DESTROYED" )
+ }
+}
+
+void function CheckTitansForDraw()
+{
+ int militiaLivingTitans
+ int imcLivingTitans
+
+ float militiaCombinedHealth
+ float imcCombinedHealth
+
+ foreach ( entity player in GetPlayerArray() )
+ {
+ // only need to track titans for this, can assume that neither team has lost due to titan death if the round is still going
+ entity titan = IsAlive( player.GetPetTitan() ) ? player.GetPetTitan() : player
+ if ( titan.IsPlayer() && !titan.IsTitan() )
+ continue
+
+ if ( IsAlive( titan ) )
+ if ( player.GetTeam() == TEAM_MILITIA )
+ {
+ // doomed is counted as 0 health in this
+ militiaCombinedHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan )
+ militiaLivingTitans++
+ }
+ else
+ {
+ // doomed is counted as 0 health in this
+ imcCombinedHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan )
+ imcLivingTitans++
+ }
+ }
+
+ SetKillcamsEnabled( false )
+ //SetRoundWinningKillReplayInfo( null, null, 0, 0 ) // make sure we don't do a replay
+
+ // default if both teams are equal
+ int winner = TEAM_UNASSIGNED
+
+ string winnerSubstr
+ string loserSubstr
+
+ if ( militiaLivingTitans != imcLivingTitans ) // one team has a titan lead
+ {
+ winnerSubstr = "#GAMEMODE_TITAN_TITAN_ADVANTAGE"
+ loserSubstr = "#GAMEMODE_TITAN_TITAN_DISADVANTAGE"
+
+ winner = militiaLivingTitans > imcLivingTitans ? TEAM_MILITIA : TEAM_IMC
+ }
+ else if ( militiaCombinedHealth != imcCombinedHealth ) // one team has a health lead
+ {
+ winnerSubstr = "#GAMEMODE_TITAN_DAMAGE_ADVANTAGE"
+ loserSubstr = "#GAMEMODE_TITAN_DAMAGE_DISADVANTAGE"
+
+ winner = militiaCombinedHealth > imcCombinedHealth ? TEAM_MILITIA : TEAM_IMC
+ }
+
+ print( "CheckTitansForDraw(): " + winner )
+ SetWinner( winner, winnerSubstr, loserSubstr )
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ file.lastDamageInfoVictim = victim
+ file.lastDamageInfoAttacker = DamageInfo_GetAttacker( damageInfo )
+ file.lastDamageInfoMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo )
+ file.lastDamageInfoTime = Time()
+
+ if ( !victim.isSpawning )
+ CheckTeamTitans( victim.GetTeam() )
+}
+
+void function OnTitanKilled( entity titan, var damageInfo )
+{
+ file.lastDamageInfoVictim = titan.GetOwner()
+ file.lastDamageInfoAttacker = DamageInfo_GetAttacker( damageInfo )
+ file.lastDamageInfoMethodOfDeath = DamageInfo_GetDamageSourceIdentifier( damageInfo )
+ file.lastDamageInfoTime = Time()
+
+ if ( IsPetTitan( titan ) && !titan.GetBossPlayer().isSpawning )
+ CheckTeamTitans( titan.GetTeam() )
+}
+
+void function AddToDamageStat( var damageInfo )
+{
+ // todo: this needs to not count selfdamage
+ entity attacker = DamageInfo_GetAttacker( damageInfo )
+ float amount = DamageInfo_GetDamage( damageInfo )
+
+ if ( attacker.IsPlayer() )
+ attacker.AddToPlayerGameStat( PGS_ASSAULT_SCORE, amount ) // titan damage on
+}
+
+void function OnPlayerDamaged( entity player, var damageInfo )
+{
+ if ( player.IsTitan() )
+ AddToDamageStat( damageInfo )
+}
+
+void function OnTitanDamaged( entity titan, var damageInfo )
+{
+ if ( IsPetTitan( titan ) )
+ AddToDamageStat( damageInfo )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_mfd.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_mfd.nut
new file mode 100644
index 000000000..6e8e9fa37
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_mfd.nut
@@ -0,0 +1,230 @@
+untyped
+global function GamemodeMfd_Init
+
+struct {
+ entity imcLastMark
+ entity militiaLastMark
+} file
+
+void function GamemodeMfd_Init()
+{
+ GamemodeMfdShared_Init()
+
+ RegisterSignal( "MarkKilled" )
+
+ AddCallback_OnPlayerKilled( UpdateMarksForKill )
+ AddCallback_GameStateEnter( eGameState.Playing, CreateInitialMarks )
+}
+
+void function CreateInitialMarks()
+{
+ entity imcMark = CreateEntity( MARKER_ENT_CLASSNAME )
+ imcMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT
+ SetTeam( imcMark, TEAM_IMC )
+ SetTargetName( imcMark, MARKET_ENT_MARKED_NAME ) // why is it market_ent lol
+ DispatchSpawn( imcMark )
+ FillMFDMarkers( imcMark )
+
+ entity imcPendingMark = CreateEntity( MARKER_ENT_CLASSNAME )
+ imcPendingMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT
+ SetTeam( imcPendingMark, TEAM_IMC )
+ SetTargetName( imcPendingMark, MARKET_ENT_PENDING_MARKED_NAME )
+ DispatchSpawn( imcPendingMark )
+ FillMFDMarkers( imcPendingMark )
+
+ entity militiaMark = CreateEntity( MARKER_ENT_CLASSNAME )
+ militiaMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT
+ SetTeam( militiaMark, TEAM_MILITIA )
+ SetTargetName( militiaMark, MARKET_ENT_MARKED_NAME )
+ DispatchSpawn( militiaMark )
+ FillMFDMarkers( militiaMark )
+
+ entity militiaPendingMark = CreateEntity( MARKER_ENT_CLASSNAME )
+ militiaPendingMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT
+ SetTeam( militiaPendingMark, TEAM_MILITIA )
+ SetTargetName( militiaPendingMark, MARKET_ENT_PENDING_MARKED_NAME )
+ DispatchSpawn( militiaPendingMark )
+ FillMFDMarkers( militiaPendingMark )
+
+ thread MFDThink()
+}
+
+void function MFDThink()
+{
+ svGlobal.levelEnt.EndSignal( "GameStateChanged" )
+
+ entity imcMark
+ entity militiaMark
+
+ while ( true )
+ {
+ if ( !TargetsMarkedImmediately() )
+ wait MFD_BETWEEN_MARKS_TIME
+
+ // wait for enough players to spawn
+ array<entity> imcPlayers
+ array<entity> militiaPlayers
+ while ( imcPlayers.len() == 0 || militiaPlayers.len() == 0 )
+ {
+ imcPlayers = GetPlayerArrayOfTeam( TEAM_IMC )
+ militiaPlayers = GetPlayerArrayOfTeam( TEAM_MILITIA )
+
+ WaitFrame()
+ }
+
+ // get marks, wanna increment the mark each mark, reset on player change
+ int imcIndex = imcPlayers.find( imcMark )
+ if ( imcIndex == -1 ) // last mark
+ imcIndex = 0
+ else
+ imcIndex = ( imcIndex + 1 ) % imcPlayers.len()
+
+ imcMark = imcPlayers[ imcIndex ]
+
+ int militiaIndex = militiaPlayers.find( imcMark )
+ if ( militiaIndex == -1 ) // last mark
+ militiaIndex = 0
+ else
+ militiaIndex = ( militiaIndex + 1 ) % militiaPlayers.len()
+
+ militiaMark = militiaPlayers[ militiaIndex ]
+
+ level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark )
+ level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark )
+
+ foreach ( entity player in GetPlayerArray() )
+ {
+ Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_MFD_StartNewMarkCountdown", Time() + MFD_COUNTDOWN_TIME )
+ }
+
+ // reset if mark leaves
+ bool shouldReset
+ float endTime = Time() + MFD_COUNTDOWN_TIME
+ while ( endTime > Time() || ( !IsAlive( imcMark ) || !IsAlive( militiaMark ) ) )
+ {
+ if ( !IsValid( imcMark ) || !IsValid( militiaMark ) )
+ {
+ shouldReset = true
+ break
+ }
+
+ WaitFrame()
+ }
+
+ if ( shouldReset )
+ continue
+
+ waitthread MarkPlayers( imcMark, militiaMark )
+ }
+}
+
+void function MarkPlayers( entity imcMark, entity militiaMark )
+{
+ imcMark.EndSignal( "OnDestroy" )
+ imcMark.EndSignal( "Disconnected" )
+
+ militiaMark.EndSignal( "OnDestroy" )
+ militiaMark.EndSignal( "Disconnected" )
+
+ OnThreadEnd( function() : ( imcMark, militiaMark )
+ {
+ // clear marks
+ level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null )
+ level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null )
+
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" )
+ })
+
+ // clear pending marks
+ level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null )
+ level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null )
+
+ // set marks
+ level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark )
+ level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark )
+
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" )
+
+ // wait until mark dies
+ entity deadMark = expect entity( svGlobal.levelEnt.WaitSignal( "MarkKilled" ).mark )
+
+ // award points
+ entity livingMark = GetMarked( GetOtherTeam( deadMark.GetTeam() ) )
+ livingMark.SetPlayerGameStat( PGS_DEFENSE_SCORE, livingMark.GetPlayerGameStat( PGS_DEFENSE_SCORE ) + 1 )
+ AddTeamScore( livingMark.GetTeam(), 1 )
+}
+
+void function UpdateMarksForKill( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim == GetMarked( victim.GetTeam() ) )
+ {
+ svGlobal.levelEnt.Signal( "MarkKilled", { mark = victim } )
+
+ if ( attacker.IsPlayer() )
+ attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 )
+ }
+}
+
+/*
+void function MarkPlayers()
+{
+ // todo: need to handle disconnecting marks
+ if ( !TargetsMarkedImmediately() )
+ wait MFD_BETWEEN_MARKS_TIME
+
+
+ // wait until we actually have 2 valid players
+ array<entity> imcPlayers
+ array<entity> militiaPlayers
+ while ( imcPlayers.len() == 0 || militiaPlayers.len() == 0 )
+ {
+ imcPlayers = GetPlayerArrayOfTeam( TEAM_IMC )
+ militiaPlayers = GetPlayerArrayOfTeam( TEAM_MILITIA )
+
+ WaitFrame()
+ }
+
+ // decide marks
+ entity imcMark = imcPlayers[ RandomInt( imcPlayers.len() ) ]
+ level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark )
+
+ entity militiaMark = militiaPlayers[ RandomInt( militiaPlayers.len() ) ]
+ level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark )
+
+ foreach ( entity player in GetPlayerArray() )
+ {
+ Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_MFD_StartNewMarkCountdown", Time() + MFD_COUNTDOWN_TIME )
+ }
+
+ wait MFD_COUNTDOWN_TIME
+
+ while ( !IsAlive( imcMark ) || !IsAlive( militiaMark ) )
+ WaitFrame()
+
+ // clear pending marks
+ level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null )
+ level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null )
+
+ // set marks
+ level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark )
+ level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark )
+
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" )
+
+ while ( IsAlive( imcMark ) && IsAlive( militiaMark ) )
+ WaitFrame()
+
+ // clear marks
+ level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null )
+ level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null )
+
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" )
+
+ thread MarkPlayers()
+}*/ \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ps.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ps.nut
new file mode 100644
index 000000000..3a852f918
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ps.nut
@@ -0,0 +1,12 @@
+global function GamemodePs_Init
+
+void function GamemodePs_Init()
+{
+ AddCallback_OnPlayerKilled( GiveScoreForPlayerKill )
+}
+
+void function GiveScoreForPlayerKill( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() )
+ AddTeamScore( attacker.GetTeam(), 1 )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut
new file mode 100644
index 000000000..9c70cfb90
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_speedball.nut
@@ -0,0 +1,137 @@
+global function GamemodeSpeedball_Init
+
+struct {
+ entity flagBase
+ entity flag
+ entity flagCarrier
+} file
+
+void function GamemodeSpeedball_Init()
+{
+ PrecacheModel( CTF_FLAG_MODEL )
+ PrecacheModel( CTF_FLAG_BASE_MODEL )
+
+ // gamemode settings
+ SetRoundBased( true )
+ SetRespawnsEnabled( false )
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+ Riff_ForceSetEliminationMode( eEliminationMode.Pilots )
+ SetServerVar( "roundWinningKillReplayEnabled", true ) // really ought to get a function for setting this
+
+ AddSpawnCallbackEditorClass( "script_ref", "info_speedball_flag", CreateFlag )
+
+ AddCallback_GameStateEnter( eGameState.Playing, ResetFlag )
+ AddCallback_OnTouchHealthKit( "item_flag", OnFlagCollected )
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+ SetTimeoutWinnerDecisionFunc( TimeoutCheckFlagHolder )
+
+ ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, NOINTRO_INTRO_LENGTH )
+}
+
+void function CreateFlag( entity flagSpawn )
+{
+ entity flagBase = CreatePropDynamic( CTF_FLAG_BASE_MODEL, flagSpawn.GetOrigin(), flagSpawn.GetAngles() )
+
+ entity flag = CreateEntity( "item_flag" )
+ flag.SetValueForModelKey( CTF_FLAG_MODEL )
+ flag.MarkAsNonMovingAttachment()
+ DispatchSpawn( flag )
+ flag.SetModel( CTF_FLAG_MODEL )
+ flag.SetOrigin( flagBase.GetOrigin() + < 0, 0, flagBase.GetBoundingMaxs().z + 1 > )
+ flag.SetVelocity( < 0, 0, 1 > )
+
+ file.flag = flag
+ file.flagBase = flagBase
+}
+
+bool function OnFlagCollected( entity player, entity flag )
+{
+ if ( !IsAlive( player ) || flag.GetParent() != null || player.IsTitan() || player.IsPhaseShifted() )
+ return false
+
+ GiveFlag( player )
+ return false // so flag ent doesn't despawn
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( file.flagCarrier == victim )
+ DropFlag()
+
+ if ( victim.IsPlayer() && GetGameState() == eGameState.Playing )
+ {
+ // this REALLY ought to be an elimationmode thing rather than gamemode-based
+ int livingPlayers
+ foreach ( entity player in GetPlayerArrayOfTeam( victim.GetTeam() ) )
+ if ( IsAlive( player ) )
+ livingPlayers++
+
+ if ( livingPlayers == 0 )
+ SetWinner( GetOtherTeam( victim.GetTeam() ) )
+ else if ( livingPlayers == 1 )
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SPEEDBALL_LastPlayer", player.GetTeam() != victim.GetTeam() )
+ }
+}
+
+void function GiveFlag( entity player )
+{
+ file.flag.SetParent( player, "FLAG" )
+ file.flagCarrier = player
+ SetGlobalNetEnt( "flagCarrier", player )
+ thread DropFlagIfPhased( player )
+
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_GrabFlag" )
+ foreach ( entity otherPlayer in GetPlayerArray() )
+ {
+ MessageToPlayer( otherPlayer, eEventNotifications.SPEEDBALL_FlagPickedUp, player )
+
+ if ( otherPlayer.GetTeam() == player.GetTeam() )
+ EmitSoundOnEntityToTeamExceptPlayer( file.flag, "UI_CTF_3P_TeamGrabFlag", player.GetTeam(), player )
+ }
+}
+
+void function DropFlagIfPhased( entity player )
+{
+ player.EndSignal( "StartPhaseShift" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ if ( file.flag.GetParent() == player )
+ DropFlag()
+ })
+
+ while( file.flag.GetParent() == player )
+ WaitFrame()
+}
+
+void function DropFlag()
+{
+ file.flag.ClearParent()
+ file.flag.SetAngles( < 0, 0, 0 > )
+ SetGlobalNetEnt( "flagCarrier", file.flag )
+ EmitSoundOnEntityOnlyToPlayer( file.flagCarrier, file.flagCarrier, "UI_CTF_1P_FlagDrop" )
+
+ foreach ( entity player in GetPlayerArray() )
+ MessageToPlayer( player, eEventNotifications.SPEEDBALL_FlagDropped, file.flagCarrier )
+
+ file.flagCarrier = null
+}
+
+void function ResetFlag()
+{
+ file.flag.ClearParent()
+ file.flag.SetAngles( < 0, 0, 0 > )
+ file.flag.SetVelocity( < 0, 0, 1 > ) // hack: for some reason flag won't have gravity if i don't do this
+ file.flag.SetOrigin( file.flagBase.GetOrigin() + < 0, 0, file.flagBase.GetBoundingMaxs().z * 2 > )
+ file.flagCarrier = null
+ SetGlobalNetEnt( "flagCarrier", file.flag )
+}
+
+int function TimeoutCheckFlagHolder()
+{
+ if ( file.flagCarrier == null )
+ return TEAM_UNASSIGNED
+
+ return file.flagCarrier.GetTeam()
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_tdm.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_tdm.nut
new file mode 100644
index 000000000..9e80b8635
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_tdm.nut
@@ -0,0 +1,19 @@
+global function GamemodeTdm_Init
+global function RateSpawnpoints_Directional
+
+void function GamemodeTdm_Init()
+{
+ AddCallback_OnPlayerKilled( GiveScoreForPlayerKill )
+}
+
+void function GiveScoreForPlayerKill( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() )
+ AddTeamScore( attacker.GetTeam(), 1 )
+}
+
+void function RateSpawnpoints_Directional( int checkclass, array<entity> spawnpoints, int team, entity player )
+{
+ // temp
+ RateSpawnpoints_Generic( checkclass, spawnpoints, team, player )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ttdm.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
new file mode 100644
index 000000000..92119c1cc
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ttdm.nut
@@ -0,0 +1,6 @@
+global function GamemodeTTDM_Init
+
+void function GamemodeTTDM_Init()
+{
+
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_hardpoints.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_hardpoints.gnut
new file mode 100644
index 000000000..dab433f1e
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_hardpoints.gnut
@@ -0,0 +1,33 @@
+global function Hardpoints_Init
+global function CapturePoint_GetStartProgress
+global function CapturePoint_GetCappingTeam
+global function CapturePoint_GetOwningTeam
+global function CapturePoint_GetGoalProgress
+
+global array<entity> HARDPOINTS
+
+void function Hardpoints_Init()
+{
+
+
+}
+
+float function CapturePoint_GetStartProgress( entity hardpoint )
+{
+ return 0.5
+}
+
+int function CapturePoint_GetCappingTeam( entity hardpoint )
+{
+ return 0
+}
+
+int function CapturePoint_GetOwningTeam( entity hardpoint )
+{
+ return 0
+}
+
+float function CapturePoint_GetGoalProgress( entity hardpoint )
+{
+ return 1.0
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_riff_floor_is_lava.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_riff_floor_is_lava.nut
new file mode 100644
index 000000000..b660e89ff
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_riff_floor_is_lava.nut
@@ -0,0 +1,102 @@
+global function RiffFloorIsLava_Init
+
+void function RiffFloorIsLava_Init()
+{
+ AddCallback_OnPlayerRespawned( FloorIsLava_PlayerRespawned )
+
+ AddSpawnCallback( "env_fog_controller", InitLavaFogController )
+ AddCallback_EntitiesDidLoad( CreateCustomSpawns )
+}
+
+void function InitLavaFogController( entity fogController )
+{
+ fogController.kv.fogztop = GetVisibleFogTop()
+ fogController.kv.fogzbottom = GetVisibleFogBottom()
+ fogController.kv.foghalfdisttop = "60000"
+ fogController.kv.foghalfdistbottom = "200"
+ fogController.kv.fogdistoffset = "0"
+ fogController.kv.fogdensity = ".85"
+
+ fogController.kv.forceontosky = true
+ //fogController.kv.foghalfdisttop = "10000"
+}
+
+void function CreateCustomSpawns()
+{
+ thread CreateCustomSpawns_Threaded()
+}
+
+void function CreateCustomSpawns_Threaded()
+{
+ WaitEndFrame() // wait for spawns to clear
+
+ float raycastTop = GetLethalFogTop() + 2500.0
+ array< vector > raycastPositions
+ foreach ( entity hardpoint in GetEntArrayByClass_Expensive( "info_hardpoint" ) )
+ {
+ if ( !hardpoint.HasKey( "hardpointGroup" ) )
+ continue
+
+ //if ( hardpoint.kv.hardpointGroup != "A" && hardpoint.kv.hardpointGroup != "B" && hardpoint.kv.hardpointGroup != "C" )
+ if ( hardpoint.kv.hardpointGroup != "B" ) // roughly map center
+ continue
+
+ vector pos = hardpoint.GetOrigin()
+ for ( int x = -2000; x < 2000; x += 200 )
+ for ( int y = -2000; y < 2000; y += 200 )
+ raycastPositions.append( < x, y, raycastTop > )
+ }
+
+ int validSpawnsCreated = 0
+ foreach ( vector raycastPos in raycastPositions )
+ {
+ //vector hardpoint = validHardpoints[ RandomInt( validHardpoints.len() ) ].GetOrigin()
+ //float a = RandomFloat( 1 ) * 2 * PI
+ //float r = 1000.0 * sqrt( RandomFloat( 1 ) )
+ //
+ //vector castStart = < hardpoint.x + r * cos( a ), hardpoint.y + r * sin( a ), >
+ //vector castEnd = < hardpoint.x + r * cos( a ), hardpoint.y + r * sin( a ), GetLethalFogBottom() >
+
+ TraceResults trace = TraceLine( raycastPos, < raycastPos.x, raycastPos.y, GetLethalFogBottom() >, [], TRACE_MASK_SOLID, TRACE_COLLISION_GROUP_NONE ) // should only hit world
+ print( "raycast: " + trace.endPos )
+ if ( trace.endPos.z >= GetLethalFogTop() )
+ {
+ print( "creating floor is lava spawn at " + trace.endPos )
+ validSpawnsCreated++
+
+ // valid spot, create a spawn
+ entity spawnpoint = CreateEntity( "info_spawnpoint_human" )
+ spawnpoint.SetOrigin( trace.endPos )
+ spawnpoint.kv.ignoreGamemode = 1
+ DispatchSpawn( spawnpoint )
+ }
+ }
+}
+
+void function FloorIsLava_PlayerRespawned( entity player )
+{
+ thread FloorIsLava_ThinkForPlayer( player )
+}
+
+void function FloorIsLava_ThinkForPlayer( entity player )
+{
+ player.EndSignal( "OnDestroy" )
+ player.EndSignal( "OnDeath" )
+
+ while ( true )
+ {
+ WaitFrame()
+
+ if ( player.GetOrigin().z < GetLethalFogTop() )
+ {
+ // do damage
+ float damageMultiplier = 0.08
+ if ( player.IsTitan() )
+ damageMultiplier *= 0.05
+
+ player.TakeDamage( player.GetMaxHealth() * damageMultiplier, null, null, { damageSourceId = eDamageSourceId.floor_is_lava } )
+
+ wait 0.1
+ }
+ }
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_spawnpoints.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_spawnpoints.gnut
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_spawnpoints.gnut
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemode_mfd.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemode_mfd.nut
new file mode 100644
index 000000000..4410a5136
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemode_mfd.nut
@@ -0,0 +1,66 @@
+untyped
+
+global function GamemodeMfdShared_Init
+global function GetMarked
+global function GetPendingMarked
+global function FillMFDMarkers
+global function TargetsMarkedImmediately
+global function IsTitanMarkedForDeathMode
+
+void function GamemodeMfdShared_Init()
+{
+ // mfd mfdActiveMarkedPlayerEnt are server side entities with a boss player that marks the marked
+ level.mfdActiveMarkedPlayerEnt <- {}
+ level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ] <- null
+ level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ] <- null
+
+ level.mfdPendingMarkedPlayerEnt <- {}
+ level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ] <- null
+ level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ] <- null
+
+ SetWaveSpawnInterval( 8.0 )
+}
+
+entity function GetMarked( int team )
+{
+ if ( IsValid( level.mfdActiveMarkedPlayerEnt[ team ] ) )
+ return expect entity( level.mfdActiveMarkedPlayerEnt[ team ] ).GetOwner()
+
+ return null
+}
+
+entity function GetPendingMarked( int team )
+{
+ if ( IsValid( level.mfdPendingMarkedPlayerEnt[ team ] ) )
+ return expect entity( level.mfdPendingMarkedPlayerEnt[ team ] ).GetOwner()
+
+ return null
+}
+
+function FillMFDMarkers( entity ent ) //Ent used for kill replay related issues...
+{
+ print( "FillMFDMarkers " + ent )
+
+ if ( ent.GetTargetName() == MARKET_ENT_MARKED_NAME )
+ {
+ Assert( ent.GetTeam() != TEAM_UNASSIGNED )
+ level.mfdActiveMarkedPlayerEnt[ ent.GetTeam() ] = ent
+ }
+ else if ( ent.GetTargetName() == MARKET_ENT_PENDING_MARKED_NAME )
+ {
+ Assert( ent.GetTeam() != TEAM_UNASSIGNED )
+ level.mfdPendingMarkedPlayerEnt[ ent.GetTeam() ] = ent
+ }
+
+ return
+}
+
+function TargetsMarkedImmediately()
+{
+ return IsRoundBased() && IsPilotEliminationBased()
+}
+
+bool function IsTitanMarkedForDeathMode()
+{
+ return GetCurrentPlaylistVarInt( "titan_marked_for_death", 0 ) == 1
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut
new file mode 100644
index 000000000..df7acb78e
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes.gnut
@@ -0,0 +1,816 @@
+
+global function GameModes_Init
+
+global function GameMode_Create
+global function GameMode_SetName
+global function GameMode_SetGameModeAttackAnnouncement
+global function GameMode_SetGameModeDefendAnnouncement
+global function GameMode_SetAttackDesc
+global function GameMode_SetDefendDesc
+global function GameMode_SetIcon
+global function GameMode_SetDefaultScoreLimits
+global function GameMode_AddScoreboardColumnData
+global function GameMode_SetGameModeAnnouncement
+global function GameMode_SetDefaultTimeLimits
+global function GameMode_SetDesc
+global function GameMode_SetColor
+global function GameMode_SetSuddenDeath
+
+global function GameMode_GetScoreLimit
+global function GameMode_GetRoundScoreLimit
+global function GameMode_GetTimeLimit
+global function GameMode_GetRoundTimeLimit
+global function GameMode_GetGameModeAnnouncement
+global function GameMode_GetGameModeAttackAnnouncement
+global function GameMode_GetGameModeDefendAnnouncement
+global function GameMode_GetDesc
+global function GameMode_GetName
+global function GameMode_GetIcon
+global function GameMode_GetColor
+global function GameMode_GetAttackDesc
+global function GameMode_GetDefendDesc
+global function GameMode_GetPilotSpawnpointsRatingFunc
+global function GameMode_GetTitanSpawnpointsRatingFunc
+global function GameMode_GetScoreCompareFunc
+global function GameMode_GetSuddenDeathEnabled
+global function GameMode_GetEvacEnabled
+global function GameMode_GetGameEndingWarning
+global function GameMode_GetGameEndingConversation
+global function GameMode_GetScoreboardColumnTitles
+global function GameMode_GetScoreboardColumnScoreTypes
+global function GameMode_GetScoreboardColumnNumDigits
+global function GameMode_GetCustomIntroAnnouncement
+global function GameMode_RunServerInits
+global function GameMode_RunClientInits
+global function GameMode_RunSharedInits
+global function GameMode_IsDefined
+
+global function GameMode_AddServerInit
+global function GameMode_AddClientInit
+global function GameMode_AddSharedInit
+global function GameMode_SetScoreCompareFunc
+global function GameMode_SetPilotSpawnpointsRatingFunc
+global function GameMode_SetTitanSpawnpointsRatingFunc
+global function GameMode_SetCustomIntroAnnouncement
+
+global function GameMode_GetGameModeId
+
+global function GameMode_SetEvacEnabled
+
+global function GameMode_GetLoadoutSelectTime
+
+global struct GamemodeSettings
+{
+ string name = ""
+ string name_localized = "Undefined Game Mode"
+ string desc_localized = "Undefined Game Mode Description"
+ string desc_attack = ""
+ string desc_defend = ""
+ string gameModeAnnoucement = ""
+ string gameModeAttackAnnoucement = ""
+ string gameModeDefendAnnoucement = ""
+ asset icon = $"ui/menu/playlist/classic"
+ array<int> color = [127, 127, 127, 255]
+ array< void functionref() > serverInits
+ array< void functionref() > clientInits
+ array< void functionref() > sharedInits
+ void functionref( int, array<entity>, int, entity ) pilotSpawnpointRatingFunc
+ void functionref( int, array<entity>, int, entity ) titanSpawnpointRatingFunc
+ IntFromEntityCompare scoreCompareFunc
+ int defaultScoreLimit = 100
+ int defaultTimeLimit = 10
+ int defaultRoundScoreLimit = 5
+ float defaultRoundTimeLimit = 5.0
+ bool evacEnabled = true
+ string gameModeEndingWarning = "#GAMEMODE_END_IN_N_SECONDS"
+ string gameModeEndingConversation = ""
+ bool suddenDeathEnabled = false
+ array<string> scoreboardColumnTitles
+ array<int> scoreboardColumnScoreTypes
+ array<int> scoreboardColumnNumDigits
+ void functionref(entity) customIntroAnnouncementFunc
+}
+
+
+
+// Don't remove items from this list once the game is in production
+// Durango online analytics needs the numbers for each mode to stay the same
+// DO NOT CHANGE THESE VALUES AFTER THEY HAVE GONE LIVE
+global enum eGameModes
+{
+ invalid = -1,
+ TEAM_DEATHMATCH_ID = 0,
+ CAPTURE_POINT_ID = 1,
+ ATTRITION_ID = 2,
+ CAPTURE_THE_FLAG_ID = 3,
+ MARKED_FOR_DEATH_ID = 4,
+ LAST_TITAN_STANDING_ID = 5,
+ WINGMAN_LAST_TITAN_STANDING_ID = 6,
+ PILOT_SKIRMISH_ID = 7,
+ MARKED_FOR_DEATH_PRO_ID = 8,
+ COOPERATIVE_ID = 9,
+ GAMEMODE_SP_ID = 10,
+ TITAN_BRAWL_ID = 11,
+ FFA_ID = 12,
+ PROTOTYPE2 = 13,
+ WINGMAN_PILOT_SKIRMISH_ID = 14,
+ PROTOTYPE3 = 15,
+ PROTOTYPE4 = 16,
+ FREE_AGENCY_ID = 17,
+ PROTOTYPE6 = 18,
+ COLISEUM_ID = 19,
+ PROTOTYPE7 = 20,
+ AI_TDM_ID = 21,
+ PROTOTYPE8 = 22,
+ PROTOTYPE9 = 23,
+ SPEEDBALL_ID = 24,
+ PROTOTYPE10 = 25,
+ PROTOTYPE11 = 26,
+ PROTOTYPE12 = 27,
+ FD_ID = 28,
+ PROTOTYPE14 = 29,
+}
+
+const table<string, int> gameModesStringToIdMap = {
+ [ TEAM_DEATHMATCH ] = eGameModes.TEAM_DEATHMATCH_ID,
+ [ PILOT_SKIRMISH ] = eGameModes.PILOT_SKIRMISH_ID,
+ [ CAPTURE_POINT ] = eGameModes.CAPTURE_POINT_ID,
+ [ ATTRITION ] = eGameModes.ATTRITION_ID,
+ [ CAPTURE_THE_FLAG ] = eGameModes.CAPTURE_THE_FLAG_ID,
+ [ LAST_TITAN_STANDING ] = eGameModes.LAST_TITAN_STANDING_ID,
+ [ GAMEMODE_SP ] = eGameModes.GAMEMODE_SP_ID,
+ [ FFA ] = eGameModes.FFA_ID,
+ [ COLISEUM ] = eGameModes.COLISEUM_ID,
+ [ AI_TDM ] = eGameModes.AI_TDM_ID,
+ [ SPEEDBALL ] = eGameModes.SPEEDBALL_ID,
+ [ MARKED_FOR_DEATH ] = eGameModes.MARKED_FOR_DEATH_ID,
+ [ TITAN_BRAWL ] = eGameModes.TITAN_BRAWL_ID,
+ [ FREE_AGENCY ] = eGameModes.FREE_AGENCY_ID,
+ [ FD ] = eGameModes.FD_ID,
+ [ FD_EASY ] = eGameModes.FD_ID,
+ [ FD_NORMAL ] = eGameModes.FD_ID,
+ [ FD_HARD ] = eGameModes.FD_ID,
+ [ FD_MASTER ] = eGameModes.FD_ID,
+ [ FD_INSANE ] = eGameModes.FD_ID,
+}
+
+struct
+{
+ table< string, GamemodeSettings > gameModeDefs
+} file
+
+void function GameModes_Init()
+{
+ string gameMode
+
+ gameMode = GAMEMODE_SP
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#GAMEMODE_SOLO" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/coop" ) //HACK TODO: get a sp icon
+ GameMode_SetDesc( gameMode, "#GAMEMODE_SOLO_HINT" )
+ GameMode_SetDefaultScoreLimits( gameMode, 0, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 0, 0.0 )
+
+ gameMode = CAPTURE_POINT
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_hardpoint" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "hp_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_CP" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_hardpoint_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/cp" )
+ GameMode_SetDefaultScoreLimits( gameMode, 500, 500 )
+ GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_ASSAULT", PGS_ASSAULT_SCORE, 4 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEFENSE", PGS_DEFENSE_SCORE, 4 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_SetColor( gameMode, [46, 188, 180, 255] )
+
+ gameMode = LAST_TITAN_STANDING
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_last_titan_standing" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "lts_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_LTS" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_last_titan_standing_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/lts" )
+ GameMode_SetDefaultScoreLimits( gameMode, 0, 4 )
+ GameMode_SetDefaultTimeLimits( gameMode, 5, 4.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_DAMAGE", PGS_ASSAULT_SCORE, 6 )
+ GameMode_SetColor( gameMode, [223, 94, 0, 255] )
+
+ gameMode = ATTRITION
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_attrition" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "bh_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_AT" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_attrition_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/at" )
+ GameMode_SetDefaultScoreLimits( gameMode, 5000, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 4 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_BONUS", PGS_SCORE, 4 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_SetColor( gameMode, [88, 172, 67, 255] )
+
+ gameMode = TEAM_DEATHMATCH
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_pilot_hunter" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "phunt_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_TDM" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_pilot_hunter_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" )
+ GameMode_SetDefaultScoreLimits( gameMode, 50, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_ASSISTS", PGS_ASSISTS, 2 )
+ GameMode_SetColor( gameMode, [212, 83, 152, 255] )
+
+ gameMode = AI_TDM
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_aitdm" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "gnrc_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_TDM" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_aitdm_hint" )
+ GameMode_SetIcon( gameMode, FFA_MODE_ICON )
+ GameMode_SetDefaultScoreLimits( gameMode, 1, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 3 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 1 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_GRUNT_KILLS", PGS_NPC_KILLS, 2 )
+ // GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 )
+ GameMode_SetColor( gameMode, [200, 40, 40, 255] )
+
+ gameMode = COLISEUM
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_coliseum" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "gnrc_modeDesc" ) //TODO: This is just the mode name as opposed to instructions...
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_PS" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_coliseum_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" )
+ GameMode_SetDefaultScoreLimits( gameMode, 15, 2 )
+ GameMode_SetDefaultTimeLimits( gameMode, 0, 4.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_SetColor( gameMode, [151, 71, 175, 255] )
+
+ gameMode = PILOT_SKIRMISH
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_pilot_skirmish" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "pvp_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_PS" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_pilot_skirmish_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" )
+ GameMode_SetDefaultScoreLimits( gameMode, 100, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_ASSISTS", PGS_ASSISTS, 2 )
+ GameMode_SetColor( gameMode, [207, 191, 59, 255] )
+
+ gameMode = CAPTURE_THE_FLAG
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_capture_the_flag" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "ctf_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_CTF" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_capture_the_flag_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/ctf" )
+ GameMode_SetSuddenDeath( gameMode, true )
+ GameMode_SetDefaultScoreLimits( gameMode, 0, 5 )
+ GameMode_SetDefaultTimeLimits( gameMode, 0, 3.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_CAPTURES", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_RETURNS", PGS_DEFENSE_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_SetColor( gameMode, [61, 117, 193, 255] )
+
+ gameMode = FFA
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_ffa" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "ffa_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_FFA" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_ffa_hint" )
+ GameMode_SetIcon( gameMode, FFA_MODE_ICON )
+ GameMode_SetDefaultScoreLimits( gameMode, 10, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 10, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 2 )
+ GameMode_SetColor( gameMode, [147, 204, 57, 255] )
+
+ gameMode = FREE_AGENCY
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_free_agents" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "freea_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_FREE_AGENCY" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_free_agents_hint" )
+ GameMode_SetIcon( gameMode, FFA_MODE_ICON )
+ GameMode_SetDefaultScoreLimits( gameMode, 10, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 2 )
+ GameMode_SetColor( gameMode, [127, 127, 127, 255] )
+
+ gameMode = SPEEDBALL
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_speedball" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "gnrc_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_CTF" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_speedball_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/ctf" )
+ GameMode_SetDefaultScoreLimits( gameMode, 0, 5 )
+ GameMode_SetDefaultTimeLimits( gameMode, 0, 1.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_FLAGS_SECURED", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 )
+ GameMode_SetColor( gameMode, [225, 141, 8, 255] )
+
+ gameMode = MARKED_FOR_DEATH
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_marked_for_death" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "mfd_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_MFD" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_marked_for_death_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/mfd" )
+ GameMode_SetDefaultScoreLimits( gameMode, 10, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 10, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_MFD_SCORE", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_MFD_MARKS_OUTLASTED", PGS_DEFENSE_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_SetColor( gameMode, [127, 127, 127, 255] )
+
+ gameMode = TITAN_BRAWL
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_titan_brawl" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "lts_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_TTDM" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_titan_brawl_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/lts" )
+ GameMode_SetDefaultScoreLimits( gameMode, 30, 0 )
+ GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_DAMAGE", PGS_ASSAULT_SCORE, 6 )
+ GameMode_SetColor( gameMode, [83, 212, 152, 255] )
+
+ gameMode = FD
+ GameMode_Create( gameMode )
+ GameMode_SetName( gameMode, "#PL_fd" )
+ #if FACTION_DIALOGUE_ENABLED
+ GameMode_SetGameModeAnnouncement( gameMode, "fd_modeDesc" )
+ #else
+ GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_PS" )
+ #endif
+ GameMode_SetDesc( gameMode, "#PL_fd_hint" )
+ GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" )
+ GameMode_SetSuddenDeath( gameMode, true )
+ GameMode_SetDefaultScoreLimits( gameMode, 0, 5 )
+ GameMode_SetDefaultTimeLimits( gameMode, 0, 5.0 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TOTAL_SCORE", PGS_DETONATION_SCORE, 4 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_COMBAT_SCORE", PGS_ASSAULT_SCORE, 4 )
+ //GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_HEALING_SCORE", PGS_DISTANCE_SCORE, 3 )
+ GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SUPPORT_SCORE", PGS_DEFENSE_SCORE, 4 )
+
+ #if DEVSCRIPTS
+ DevGameModes_Init()
+ #endif
+
+ #if SERVER || CLIENT
+ InitCustomGamemodes()
+ GameModes_Init_SV_CL()
+ #endif
+
+ ////
+ GameMode_VerifyModes()
+}
+
+// TODO: scoreboards
+
+/*************************************************************
+ Setters
+*************************************************************/
+
+GamemodeSettings function GameMode_Create( string gameModeName )
+{
+ Assert( !(gameModeName in file.gameModeDefs), "Gametype already defined!" )
+
+ GamemodeSettings settings
+ file.gameModeDefs[gameModeName] <- settings
+
+ return file.gameModeDefs[gameModeName]
+}
+
+void function GameMode_SetName( string gameModeName, string nameText )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut (" + gameModeName + ")" )
+ file.gameModeDefs[gameModeName].name_localized = nameText
+}
+
+void function GameMode_SetGameModeAnnouncement( string gameModeName, string gameModeAnnoucement ) //Note: Still need to register the conversation
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].gameModeAnnoucement = gameModeAnnoucement
+}
+
+void function GameMode_SetGameModeAttackAnnouncement( string gameModeName, string gameModeAttackAnnoucement ) //Note: Still need to register the conversation
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].gameModeAttackAnnoucement = gameModeAttackAnnoucement
+}
+
+void function GameMode_SetGameModeDefendAnnouncement( string gameModeName, string gameModeDefendAnnoucement )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) //Note: Still need to register the conversation
+ file.gameModeDefs[gameModeName].gameModeDefendAnnoucement = gameModeDefendAnnoucement
+}
+
+void function GameMode_SetDesc( string gameModeName, string descText )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].desc_localized = descText
+}
+
+void function GameMode_SetAttackDesc( string gameModeName, string descText )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].desc_attack = descText
+}
+
+void function GameMode_SetDefendDesc( string gameModeName, string descText )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].desc_defend = descText
+}
+
+void function GameMode_SetIcon( string gameModeName, asset icon )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].icon = icon
+}
+
+void function GameMode_SetColor( string gameModeName, array<int> color )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].color = color
+}
+
+void function GameMode_SetSuddenDeath( string gameModeName, bool state )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].suddenDeathEnabled = state
+}
+
+void function GameMode_AddServerInit( string gameModeName, void functionref() func )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].serverInits.append( func )
+}
+
+void function GameMode_AddClientInit( string gameModeName, void functionref() func )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].clientInits.append( func )
+}
+
+void function GameMode_AddSharedInit( string gameModeName, void functionref() func )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].sharedInits.append( func )
+}
+
+void function GameMode_SetPilotSpawnpointsRatingFunc( string gameModeName, void functionref( int, array<entity>, int, entity ) func )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].pilotSpawnpointRatingFunc = func
+}
+
+void function GameMode_SetTitanSpawnpointsRatingFunc( string gameModeName, void functionref( int, array<entity>, int, entity ) func )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].titanSpawnpointRatingFunc = func
+}
+
+void function GameMode_SetScoreCompareFunc( string gameModeName, int functionref( entity, entity ) func )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].scoreCompareFunc = func
+}
+
+void function GameMode_SetDefaultScoreLimits( string gameModeName, int scoreLimit, int roundScoreLimit )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].defaultScoreLimit = scoreLimit
+ file.gameModeDefs[gameModeName].defaultRoundScoreLimit = roundScoreLimit
+}
+
+void function GameMode_SetDefaultTimeLimits( string gameModeName, int timeLimit, float roundTimeLimit )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].defaultTimeLimit = timeLimit
+ file.gameModeDefs[gameModeName].defaultRoundTimeLimit = roundTimeLimit
+}
+
+void function GameMode_AddScoreboardColumnData( string gameModeName, string title, int scoreType, int numDigits )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].scoreboardColumnTitles.append( title )
+ file.gameModeDefs[gameModeName].scoreboardColumnScoreTypes.append( scoreType )
+ file.gameModeDefs[gameModeName].scoreboardColumnNumDigits.append( numDigits )
+}
+
+void function GameMode_SetEvacEnabled( string gameModeName, bool value )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].evacEnabled = value
+}
+
+void function GameMode_SetGameEndingWarning( string gameModeName, string warning )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].gameModeEndingWarning = warning
+}
+
+void function GameMode_SetGameEndingConversation( string gameModeName, string conversation )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].gameModeEndingConversation = conversation
+}
+
+void function GameMode_SetCustomIntroAnnouncement( string gameModeName, void functionref(entity) func )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ file.gameModeDefs[gameModeName].customIntroAnnouncementFunc = func
+}
+
+/*************************************************************
+ Getters
+*************************************************************/
+
+int function GameMode_GetScoreLimit( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return GetCurrentPlaylistVarInt( "scorelimit", file.gameModeDefs[gameModeName].defaultScoreLimit )
+}
+
+int function GameMode_GetRoundScoreLimit( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return GetCurrentPlaylistVarInt( "roundscorelimit", file.gameModeDefs[gameModeName].defaultRoundScoreLimit )
+}
+
+int function GameMode_GetTimeLimit( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return GetCurrentPlaylistVarInt( "timelimit", file.gameModeDefs[gameModeName].defaultTimeLimit )
+}
+
+float function GameMode_GetRoundTimeLimit( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return GetCurrentPlaylistVarFloat( "roundtimelimit", file.gameModeDefs[gameModeName].defaultRoundTimeLimit )
+}
+
+string function GameMode_GetGameModeAnnouncement( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].gameModeAnnoucement
+}
+
+string function GameMode_GetGameModeAttackAnnouncement( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].gameModeAttackAnnoucement
+}
+
+string function GameMode_GetGameModeDefendAnnouncement( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].gameModeDefendAnnoucement
+}
+
+string function GameMode_GetDesc( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].desc_localized
+}
+
+string function GameMode_GetName( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].name_localized
+}
+
+asset function GameMode_GetIcon( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].icon
+}
+
+array<int> function GameMode_GetColor( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].color
+}
+
+string function GameMode_GetAttackDesc( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].desc_attack
+}
+
+string function GameMode_GetDefendDesc( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].desc_defend
+}
+
+void functionref( int, array<entity>, int, entity ) function GameMode_GetPilotSpawnpointsRatingFunc( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ Assert( file.gameModeDefs[gameModeName].pilotSpawnpointRatingFunc != null, "No respawn func set for " + gameModeName )
+ return file.gameModeDefs[gameModeName].pilotSpawnpointRatingFunc
+}
+
+void functionref( int, array<entity>, int, entity ) function GameMode_GetTitanSpawnpointsRatingFunc( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ Assert( file.gameModeDefs[gameModeName].titanSpawnpointRatingFunc != null, "No respawn func set for " + gameModeName )
+ return file.gameModeDefs[gameModeName].titanSpawnpointRatingFunc
+}
+
+IntFromEntityCompare function GameMode_GetScoreCompareFunc( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].scoreCompareFunc
+}
+
+bool function GameMode_GetSuddenDeathEnabled( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].suddenDeathEnabled
+}
+
+bool function GameMode_GetEvacEnabled( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].evacEnabled
+}
+
+string function GameMode_GetGameEndingWarning( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].gameModeEndingWarning
+}
+
+string function GameMode_GetGameEndingConversation( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].gameModeEndingConversation
+}
+
+array<string> function GameMode_GetScoreboardColumnTitles( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].scoreboardColumnTitles
+}
+
+array<int> function GameMode_GetScoreboardColumnScoreTypes( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].scoreboardColumnScoreTypes
+}
+
+array<int> function GameMode_GetScoreboardColumnNumDigits( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].scoreboardColumnNumDigits
+}
+
+void functionref(entity) function GameMode_GetCustomIntroAnnouncement( string gameModeName )
+{
+ Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+ return file.gameModeDefs[gameModeName].customIntroAnnouncementFunc
+}
+
+/*************************************************************
+
+*************************************************************/
+void function GameMode_RunServerInits()
+{
+ Assert( GAMETYPE in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+
+ foreach ( initFunc in file.gameModeDefs[GAMETYPE].serverInits )
+ {
+ initFunc()
+ }
+}
+
+void function GameMode_RunClientInits()
+{
+ Assert( GAMETYPE in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+
+ foreach ( initFunc in file.gameModeDefs[GAMETYPE].clientInits )
+ {
+ initFunc()
+ }
+}
+
+void function GameMode_RunSharedInits()
+{
+ Assert( GAMETYPE in file.gameModeDefs, "No MP Gametype specified in _settings.nut" )
+
+ foreach ( initFunc in file.gameModeDefs[GAMETYPE].sharedInits )
+ {
+ initFunc()
+ }
+}
+
+void function GameMode_VerifyModes()
+{
+ foreach ( gameModeName, gameModeData in file.gameModeDefs )
+ {
+ int gameModeId = GameMode_GetGameModeId( gameModeName )
+ bool foundGameModeIdString = false
+ foreach ( idString, gameModeEnumId in eGameModes )
+ {
+ if ( gameModeEnumId != gameModeId )
+ continue
+
+ foundGameModeIdString = true
+ break
+ }
+ Assert( foundGameModeIdString, "GAMEMODE not defined properly in eGameModes!" )
+
+ GAMETYPE_TEXT[gameModeName] <- gameModeData.name_localized
+ GAMETYPE_DESC[gameModeName] <- gameModeData.desc_localized
+ GAMETYPE_ICON[gameModeName] <- gameModeData.icon
+ GAMETYPE_COLOR[gameModeName] <- gameModeData.color
+ #if CLIENT
+ PrecacheHUDMaterial( GAMETYPE_ICON[gameModeName] )
+ #endif
+ }
+}
+
+int function GameMode_GetGameModeId( string gameModeName )
+{
+ if ( gameModeName in gameModesStringToIdMap )
+ return gameModesStringToIdMap[gameModeName]
+
+ #if DEVSCRIPTS
+ if ( gameModeName in devGameModesStringToIdMap )
+ return devGameModesStringToIdMap[gameModeName]
+ #endif
+
+ Assert( false, "GAMEMODE " + gameModeName + " not defined in gameModesStringToIdMap" )
+
+ return 0
+}
+
+bool function GameMode_IsDefined( string gameModeName )
+{
+ return (gameModeName in file.gameModeDefs)
+}
+
+float function GameMode_GetLoadoutSelectTime()
+{
+ return GetCurrentPlaylistVarFloat( "pick_loadout_time", 5.0 )
+} \ No newline at end of file
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes_custom.gnut b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes_custom.gnut
new file mode 100644
index 000000000..51f8bf9e3
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/sh_gamemodes_custom.gnut
@@ -0,0 +1,20 @@
+untyped
+global function InitCustomGamemodes
+global function AddCallback_OnCustomGamemodesInit
+
+struct {
+ array<void functionref()> onCustomGamemodesInitCallbacks
+} file
+
+void function InitCustomGamemodes()
+{
+ print( "InitCustomGamemodes" )
+
+ foreach ( void functionref() callback in file.onCustomGamemodesInitCallbacks )
+ callback()
+}
+
+void function AddCallback_OnCustomGamemodesInit( void functionref() callback )
+{
+ file.onCustomGamemodesInitCallbacks.append( callback )
+} \ No newline at end of file