aboutsummaryrefslogtreecommitdiff
path: root/Northstar.Custom/mod/scripts/vscripts/gamemodes
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
commit9a96d0bff56f1969c68bb52a2f33296095bdc67d (patch)
tree4175928e488632705692e3cccafa1a38dd854615 /Northstar.Custom/mod/scripts/vscripts/gamemodes
parent27bd240871b7c0f2f49fef137718b2e3c208e3b4 (diff)
downloadNorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.tar.gz
NorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.zip
move to new mod format
Diffstat (limited to 'Northstar.Custom/mod/scripts/vscripts/gamemodes')
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_arena.gnut185
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut195
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut201
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_gg.gnut114
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut179
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_kr.gnut126
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_sbox.gnut49
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_tt.gnut71
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/_riff_instagib.gnut65
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_arena.gnut40
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut96
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_gg.gnut26
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_inf.gnut75
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_kr.gnut241
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_tt.gnut26
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_arena_loadouts.gnut98
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_arena.gnut58
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_bomb.gnut0
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut114
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut47
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut73
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut165
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut58
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut55
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_sbox.gnut20
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut36
-rw-r--r--Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_riff_floor_is_lava.nut262
27 files changed, 2675 insertions, 0 deletions
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_arena.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_arena.gnut
new file mode 100644
index 00000000..1765fd9b
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_arena.gnut
@@ -0,0 +1,185 @@
+global function GameModeArena_Init
+
+struct {
+ bool inBuyPhase
+
+ entity imcBoostStore
+ entity militiaBoostStore
+
+ entity imcShield
+ entity militiaShield
+} file
+
+void function GameModeArena_Init()
+{
+ SetSpawnpointGamemodeOverride( TEAM_DEATHMATCH )
+
+ SetShouldUseRoundWinningKillReplay( true )
+ SetRoundBased( true )
+ SetRespawnsEnabled( false )
+ SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+ Riff_ForceBoostAvailability( eBoostAvailability.Disabled )
+ Riff_ForceSetEliminationMode( eEliminationMode.Pilots )
+
+ ClassicMP_SetCustomIntro( GameModeArena_BuyPhaseSetup, 30.0 )
+ AddCallback_EntitiesDidLoad( CreateBoostStores )
+ AddCallback_OnPlayerRespawned( SetupArenaLoadoutForPlayer )
+}
+
+// intro / buy phase
+
+void function GameModeArena_BuyPhaseSetup()
+{
+ AddCallback_OnClientConnected( SpawnPlayerIntoArenasIntro )
+ AddCallback_GameStateEnter( eGameState.Prematch, void function() { thread BuyPhase() } )
+}
+
+void function SpawnPlayerIntoArenasIntro( entity player )
+{
+ if ( GetGameState() == eGameState.Prematch )
+ RespawnAsPilot( player )
+}
+
+void function CreateBoostStores()
+{
+ array<entity> startspawns = GetEntArrayByClass_Expensive( "info_spawnpoint_human_start" ) // easier to do this than use a spawn callback imo
+
+ vector imcAverageOrigin
+ float imcAverageAngle
+ int imcNumSpawns
+
+ vector militiaAverageOrigin
+ float militiaAverageAngle
+ int militiaNumSpawns
+
+ foreach ( entity startspawn in startspawns )
+ {
+ if ( !startspawn.HasKey( "gamemode_tdm" ) || startspawn.kv.gamemode_tdm == "0" )
+ continue
+
+ if ( startspawn.GetTeam() == TEAM_IMC )
+ {
+ imcAverageOrigin += startspawn.GetOrigin()
+ imcAverageAngle += startspawn.GetAngles().y
+ imcNumSpawns++
+ }
+ else
+ {
+ militiaAverageOrigin += startspawn.GetOrigin()
+ militiaAverageAngle += startspawn.GetAngles().y
+ militiaNumSpawns++
+ }
+ }
+
+ // create imc boost store
+ vector finalPositionImc = < imcAverageOrigin.x / imcNumSpawns, imcAverageOrigin.y / imcNumSpawns, imcAverageOrigin.z / imcNumSpawns >
+ finalPositionImc += ( 200 * AnglesToForward( < 0, imcAverageAngle / imcNumSpawns, 0 > ) )
+ CreateBoostStoreLocation( TEAM_IMC, finalPositionImc, < 0, 0, 0 >, true )
+
+ vector finalPositionMilitia = < militiaAverageOrigin.x / militiaNumSpawns, militiaAverageOrigin.y / militiaNumSpawns, militiaAverageOrigin.z / militiaNumSpawns >
+ finalPositionMilitia += ( 200 * AnglesToForward( < 0, militiaAverageAngle / militiaNumSpawns, 0 > ) )
+ CreateBoostStoreLocation( TEAM_MILITIA, finalPositionMilitia, < 0, 0, 0 >, true )
+
+ // createbooststorelocation is void so have to do this
+ // also boost store code is just fully fucked lol, teams only get set on open so can't compare teams at this point
+ // sorry if someone else makes their own boost stores lol this'll just break
+ // if there's some way to get the invisible crates used for boost stores i will be very happy
+
+ if ( GetBoostStores().len() != 2 )
+ print( "_gamemode_arena.gnut: there are more than 2 boost stores, very bad no good" )
+
+ file.imcBoostStore = GetBoostStores()[0]
+ file.militiaBoostStore = GetBoostStores()[1]
+}
+
+void function BuyPhase()
+{
+ ClassicMP_OnIntroStarted()
+
+ foreach ( entity player in GetPlayerArray() )
+ {
+ ScreenFadeFromBlack( player )
+ RespawnAsPilot( player )
+
+ AddMoneyToPlayer( player, GetCashBoostForRoundCount( GetRoundsPlayed() ) )
+ }
+
+ SetJoinInProgressBonus( GetCashBoostForRoundCount( GetRoundsPlayed() ) )
+
+ // sort of a hack, set up a new intro here, so dropship intro only ever plays once
+
+ //file.imcShield = CreateBubbleShieldWithSettings( TEAM_IMC, file.imcBoostStore.GetOrigin(), <0,0,0>, null, 15.0 )
+ //file.militiaShield = CreateBubbleShieldWithSettings( TEAM_MILITIA, file.militiaBoostStore.GetOrigin(), <0,0,0>, null, 15.0 )
+
+ entity imcShield = CreateEntity( "prop_dynamic" )
+ imcShield.SetValueForModelKey( $"models/fx/xo_shield.mdl" )
+ imcShield.kv.solid = 0
+ imcShield.kv.rendercolor = "255 255 255" // white
+ imcShield.kv.modelscale = 2.25
+ imcShield.SetOrigin( file.imcBoostStore.GetOrigin() )
+ DispatchSpawn( imcShield )
+
+ entity militiaShield = CreateEntity( "prop_dynamic" )
+ militiaShield.SetValueForModelKey( $"models/fx/xo_shield.mdl" )
+ militiaShield.kv.solid = 0
+ militiaShield.kv.rendercolor = "255 255 255" // white
+ militiaShield.kv.modelscale = 2.25
+ militiaShield.SetOrigin( file.militiaBoostStore.GetOrigin() )
+ DispatchSpawn( militiaShield )
+
+ // current problem, there is seemingly no way of getting a shield we can resize which actually resizes the collision
+ // could probably just damage players that try to leave lol
+
+ OpenBoostStores()
+
+ thread DamageLeavingPlayers( imcShield.GetOrigin(), militiaShield.GetOrigin() )
+
+ wait 30.0 // intro length
+
+ CloseBoostStores()
+ imcShield.Destroy()
+ militiaShield.Destroy()
+
+ foreach ( entity player in GetPlayerArray() )
+ if ( player.GetMainWeapons().len() != 3 )
+ player.GiveWeapon( "mp_weapon_semipistol" )
+
+ ClassicMP_OnIntroFinished()
+}
+
+void function DamageLeavingPlayers( vector imcOrigin, vector militiaOrigin )
+{
+ while ( GetGameState() == eGameState.Prematch )
+ {
+ wait 0.5
+ foreach ( entity player in GetPlayerArray() )
+ {
+ vector pos = imcOrigin
+ if ( player.GetTeam() == TEAM_MILITIA )
+ pos = militiaOrigin
+
+ if ( Distance( player.GetOrigin(), pos ) > 510.0 ) // roughly the size of the shield
+ player.TakeDamage( 25, svGlobal.worldspawn, svGlobal.worldspawn, {} )
+ }
+ }
+}
+
+void function SetupArenaLoadoutForPlayer( entity player )
+{
+ PilotLoadoutDef playerLoadout = clone GetActivePilotLoadout( player )
+
+ if ( GetGameState() == eGameState.Prematch ) // buy phase
+ {
+ playerLoadout.primary = ""
+ playerLoadout.primaryMods = []
+ playerLoadout.secondary = ""
+ playerLoadout.secondaryMods = []
+ playerLoadout.weapon3 = ""
+ playerLoadout.weapon3Mods = []
+ playerLoadout.ordnance = ""
+ playerLoadout.special = ""
+ }
+
+ GivePilotLoadout( player, playerLoadout )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut
new file mode 100644
index 00000000..b59cd2dd
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball.gnut
@@ -0,0 +1,195 @@
+untyped
+global function GamemodeFastball_Init
+
+struct {
+ // first panel is a, second is b, third is c
+ array<Point> panelSpawns
+} file
+
+void function GamemodeFastball_Init()
+{
+ // used for intro
+ PrecacheModel( $"models/titans/buddy/titan_buddy.mdl" )
+ PrecacheParticleSystem( $"P_BT_eye_SM" )
+
+ // used for respawn
+ PrecacheParticleSystem( $"P_pod_screen_lasers_OUT" )
+
+ SetShouldUseRoundWinningKillReplay( true )
+ SetRoundBased( true )
+ SetRespawnsEnabled( false )
+ Riff_ForceSetEliminationMode( eEliminationMode.Pilots )
+
+ // implementing intro in a different file because it'll likely be big
+ ClassicMP_SetCustomIntro( GamemodeFastballIntroSetup, 14.5 ) // bit of a guess number
+ AddCallback_EntitiesDidLoad( SpawnPanelsForLevel )
+ AddCallback_GameStateEnter( eGameState.Prematch, ResetPanels )
+ SetTimeoutWinnerDecisionFunc( FastballDecideWinner )
+
+ AddCallback_OnClientConnected( FastballInitPlayer )
+ AddCallback_OnPlayerKilled( FastballOnPlayerKilled ) // move this to a system in _gamestate soon!!
+
+ // setup spawns
+ // first is a, second is b, third is c
+
+ FastballAddBuddySpawnForLevel( "mp_angel_city", TEAM_IMC, < 2281.39, -3333.06, 200.031 >, < 0, 91.23, 0 > )
+ FastballAddBuddySpawnForLevel( "mp_angel_city", TEAM_MILITIA, < -4139.57, 4684.4, 41.0313 >, <0, -14.326, 0 > )
+ FastballAddPanelSpawnsForLevel( "mp_angel_city", [
+ < 2055.94, 2040.03, 128.031 >, < 0, -180, 0 >,
+ < -274.757, 2255.22, 400.031 >, < 0, -176.251, 0 >,
+ < -3208.28, 2741.17, 128.031 >, < 0, -0.0821686, 0 >
+ ])
+
+ FastballAddBuddySpawnForLevel( "mp_thaw", TEAM_MILITIA, < 2049.29, -4085.22, -274.839 >, < 0, 89.2991, 0 > )
+ FastballAddBuddySpawnForLevel( "mp_thaw", TEAM_IMC, < 834.484, 2664.28, -380.515 >, < 0.532141, -90.875, -0.542593 > )
+ FastballAddPanelSpawnsForLevel( "mp_thaw", [
+ < -1026.71, -1691.93, -319.969 >, < 0, 90, 0 >,
+ < 1836.07, -538.823, -64.1245 >, < 0, -135, 0 >,
+ < 2840.15, 1321.17, -63.9688 >, < 0, 0, 0 >
+ ])
+
+ FastballAddBuddySpawnForLevel( "mp_wargames", TEAM_MILITIA, < -4848.87, 682.17, -127.969 >, < 0, 0, 0 > )
+ FastballAddBuddySpawnForLevel( "mp_wargames", TEAM_IMC, < 2960.78, 1229.36, -127.969 >, < 7.89891e-005, 146.505, 1.38387e-005 > )
+ FastballAddPanelSpawnsForLevel( "mp_wargames", [
+ < -691.961, 1888.56, 112.031 >, < 0, 90, 0 >,
+ < -1072.03, -508.229, -127.969 >, < 0, 180, 0 >,
+ < -24.291, -1403.69, -119.969 >, < 0, -90, 0 >
+ ])
+
+ FastballAddBuddySpawnForLevel( "mp_eden", TEAM_MILITIA, < -2404.4, 1738.07, 167.767 >, <0, -40.0894, 0> )
+ FastballAddBuddySpawnForLevel( "mp_eden", TEAM_IMC, < 5248.01, 414.698, 77.1051 >, < 0, 180, 0 > )
+ FastballAddPanelSpawnsForLevel( "mp_eden", [
+ < 704.333, 1530.18, 144.031 >, < 0, 90, 0 >,
+ < -108.43, 272.638, 72.0313 >, < 0, -90, 0 >,
+ <1044.25, -1145.68, 68.0313>, < 0, 180, 0 >
+ ])
+
+ FastballAddBuddySpawnForLevel( "mp_black_water_canal", TEAM_MILITIA, < 1222.88, -5050.63, -187.763 >, < 0, 45, 0 > )
+ FastballAddBuddySpawnForLevel( "mp_black_water_canal", TEAM_IMC, < -235.375, 4736.75, -255.969 >, < 0, -90, 0 > )
+ FastballAddPanelSpawnsForLevel( "mp_black_water_canal", [
+ < 1566.13, -731.539, -63.9688 >, < 0, -90, 0 >,
+ < 502.603, 1102.92, 260.031 >, < 0, 152.328, 0 >,
+ < 2337.37, 2099.91, -26.9688 >, < 0, 0, 0 >
+ ])
+
+ FastballAddBuddySpawnForLevel( "mp_grave", TEAM_MILITIA, < 11026.8, -5163.18, 1885.64 >, < 0, 155.05, 0 > )
+ FastballAddBuddySpawnForLevel( "mp_grave", TEAM_IMC, < -1952, -3120, 1993.33 >, < 0, 0, 0 > )
+ FastballAddPanelSpawnsForLevel( "mp_grave", [
+ < 5204.54, -2726.54, 2376.03 >, < 0, -90, 0 >,
+ < 6001.58, -4126.61, 2252.03 >, < 0, -135, 0 >,
+ < 3595.96, -4568.04, 2376.03 >, < 0, -135, 0 >
+ ])
+}
+
+void function SpawnPanelsForLevel()
+{
+ int panelId
+ foreach ( Point panelSpawn in file.panelSpawns )
+ {
+ entity panel = CreatePanel( panelSpawn.origin, panelSpawn.angles )
+ panel.s.panelId <- panelId++
+ }
+}
+
+void function ResetPanels()
+{
+ foreach ( entity panel in GetAllControlPanels() )
+ SetTeam( panel, TEAM_UNASSIGNED )
+}
+
+void function FastballInitPlayer( entity player )
+{
+ foreach ( entity panel in GetAllControlPanels() )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_FastballUpdatePanelRui", panel.GetEncodedEHandle(), panel.s.panelId )
+}
+
+void function FastballAddPanelSpawnsForLevel( string level, array<vector> positionsAndOrigins )
+{
+ if ( GetMapName() != level )
+ return
+
+ for ( int i = 0; i < positionsAndOrigins.len(); i += 2 )
+ {
+ Point spawnPoint
+ spawnPoint.origin = positionsAndOrigins[ i ]
+ spawnPoint.angles = positionsAndOrigins[ i + 1 ]
+
+ file.panelSpawns.append( spawnPoint )
+ }
+}
+
+entity function CreatePanel( vector origin, vector angles )
+{
+ entity panel = CreateEntity( "prop_control_panel" )
+ panel.SetValueForModelKey( $"models/communication/terminal_usable_imc_01.mdl" )
+ panel.SetOrigin( origin )
+ panel.SetAngles( angles )
+ panel.kv.solid = SOLID_VPHYSICS
+ DispatchSpawn( panel )
+
+ panel.SetModel( $"models/communication/terminal_usable_imc_01.mdl" )
+ panel.s.onPlayerFinishesUsing_func = FastballOnPanelHacked
+
+ Highlight_SetNeutralHighlight( panel, "sp_enemy_pilot" )
+
+ return panel
+}
+
+// control panel code isn't very statically typed, pain
+function FastballOnPanelHacked( panel, player, success )
+{
+ expect entity( panel )
+ expect entity( player )
+ expect bool( success )
+
+ if ( !success )
+ return
+
+ print( panel + " was hacked by " + player )
+ PanelFlipsToPlayerTeamAndUsableByEnemies( panel, player )
+ player.SetPlayerGameStat( PGS_ASSAULT_SCORE, player.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 )
+
+ foreach ( entity otherPlayer in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_FastballPanelHacked", panel.GetEncodedEHandle(), panel.s.panelId, player.GetEncodedEHandle() )
+
+ // respawn dead players
+ foreach ( entity deadPlayer in GetPlayerArrayOfTeam( player.GetTeam() ) )
+ {
+ if ( !IsAlive( deadPlayer ) )
+ {
+ deadPlayer.SetOrigin( player.GetOrigin() )
+ deadPlayer.RespawnPlayer( null )
+ Remote_CallFunction_NonReplay( deadPlayer, "ServerCallback_FastballRespawnPlayer" )
+ }
+ }
+}
+
+void function FastballOnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( !victim.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( GetPlayerArrayOfTeam_Alive( victim.GetTeam() ).len() == 0 )
+ SetWinner( GetOtherTeam( victim.GetTeam() ) )
+}
+
+int function FastballDecideWinner()
+{
+ int militiaPanels
+ int imcPanels
+
+ foreach ( entity panel in GetAllControlPanels() )
+ {
+ if ( panel.GetTeam() == TEAM_MILITIA )
+ militiaPanels++
+ else if ( panel.GetTeam() == TEAM_IMC )
+ imcPanels++
+ }
+
+ if ( militiaPanels > imcPanels )
+ return TEAM_MILITIA
+ else if ( imcPanels > militiaPanels )
+ return TEAM_IMC
+
+ return TEAM_UNASSIGNED
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut
new file mode 100644
index 00000000..75482558
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut
@@ -0,0 +1,201 @@
+untyped
+global function GamemodeFastballIntroSetup
+global function FastballAddBuddySpawnForLevel
+
+struct {
+ float introStartTime
+
+ table< int, Point > buddySpawns
+ entity militiaBuddy
+ entity imcBuddy
+
+ table<entity, bool> playersHoldingJump
+} file
+
+void function GamemodeFastballIntroSetup()
+{
+ RegisterSignal( "fastball_start_throw" )
+ RegisterSignal( "fastball_release" )
+
+ AddCallback_GameStateEnter( eGameState.Prematch, OnPrematchStart )
+ AddCallback_OnClientConnected( AddPlayerToFastballIntro )
+}
+
+void function FastballAddBuddySpawnForLevel( string level, int team, vector origin, vector angles )
+{
+ if ( GetMapName() != level )
+ return
+
+ Point spawnPoint
+ spawnPoint.origin = origin
+ spawnPoint.angles = angles
+
+ file.buddySpawns[ team ] <- spawnPoint
+}
+
+void function OnPrematchStart()
+{
+ ClassicMP_OnIntroStarted()
+
+ file.introStartTime = Time()
+ file.playersHoldingJump = {} // clear it
+
+ Point militiaBuddySpawn
+ Point imcBuddySpawn
+
+ // figure out positions if there's none manually specified
+ if ( file.buddySpawns.len() == 0 )
+ {
+ array<entity> militiaSpawns
+ array<entity> imcSpawns
+
+ foreach ( entity spawnpoint in GetEntArrayByClass_Expensive( "info_spawnpoint_titan_start" ) )
+ {
+ // trace from top to bottom
+ float result = TraceHullSimple( spawnpoint.GetOrigin() + < 0, 0, 250 >, spawnpoint.GetOrigin(), < -200, -200, 0 >, < 200, 200, 400 >, null )
+
+ // don't need to trace much, as long as it's over 0 that means it fits
+ // cases where it's over 0 but less than 1 are usually caused by terrain
+ if ( result > 0 )
+ {
+ if ( spawnpoint.GetTeam() == TEAM_MILITIA )
+ militiaSpawns.append( spawnpoint )
+ else
+ imcSpawns.append( spawnpoint )
+ }
+ }
+
+ entity milititaSpawnEnt = militiaSpawns[ RandomInt( militiaSpawns.len() ) ]
+ militiaBuddySpawn.origin = milititaSpawnEnt.GetOrigin()
+ militiaBuddySpawn.angles = milititaSpawnEnt.GetAngles()
+
+ entity imcSpawnEnt = imcSpawns[ RandomInt( imcSpawns.len() ) ]
+ imcBuddySpawn.origin = imcSpawnEnt.GetOrigin()
+ imcBuddySpawn.angles = imcSpawnEnt.GetAngles()
+ }
+ else
+ {
+ militiaBuddySpawn = file.buddySpawns[ TEAM_MILITIA ]
+ imcBuddySpawn = file.buddySpawns[ TEAM_IMC ]
+ }
+
+ file.militiaBuddy = CreatePropDynamic( $"models/titans/buddy/titan_buddy.mdl" )
+ file.militiaBuddy.SetOrigin( militiaBuddySpawn.origin )
+ file.militiaBuddy.SetAngles( militiaBuddySpawn.angles )
+ thread AnimateBuddy( file.militiaBuddy )
+
+ file.imcBuddy = CreatePropDynamic( $"models/titans/buddy/titan_buddy.mdl" )
+ file.imcBuddy.SetOrigin( imcBuddySpawn.origin )
+ file.imcBuddy.SetAngles( imcBuddySpawn.angles )
+ thread AnimateBuddy( file.imcBuddy )
+
+ foreach ( entity player in GetPlayerArray() )
+ thread FastballPlayer( player )
+}
+
+void function AnimateBuddy( entity buddy )
+{
+ print( "buddy spawn at " + buddy.GetOrigin() + " " + buddy.GetAngles() )
+
+ thread PlayAnim( buddy, "bt_beacon_fastball_throw_end" )
+
+ // play dialogue at the right time
+ buddy.WaitSignal( "fastball_start_throw" )
+ float diagDuration = EmitSoundOnEntity( buddy, "diag_sp_spoke1_BE117_04_01_mcor_bt" ) // trust me
+ StartParticleEffectOnEntity( buddy, GetParticleSystemIndex( $"P_BT_eye_SM" ), FX_PATTACH_POINT_FOLLOW, buddy.LookupAttachment( "EYEGLOW" ) )
+
+ wait diagDuration
+ if ( GetGameState() != eGameState.Playing )
+ ClassicMP_OnIntroFinished()
+
+ buddy.WaitSignal( "fastball_release" )
+ wait 5.0
+ buddy.Destroy()
+}
+
+void function AddPlayerToFastballIntro( entity player )
+{
+ if ( GetGameState() == eGameState.Prematch )
+ thread FastballPlayer( player )
+}
+
+void function FastballPlayer( entity player )
+{
+ player.EndSignal( "OnDeath" )
+ player.EndSignal( "OnDestroy" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
+ player.ClearParent()
+ ClearPlayerAnimViewEntity( player )
+ player.DeployWeapon()
+ player.PlayerCone_Disable()
+ })
+
+ FirstPersonSequenceStruct throwSequence
+ throwSequence.attachment = "REF"
+ throwSequence.useAnimatedRefAttachment = true
+ throwSequence.hideProxy = true
+ throwSequence.viewConeFunction = ViewConeFastball // this seemingly does not trigger for some reason
+ throwSequence.firstPersonAnim = "ptpov_beacon_fastball_throw_end"
+ // mp models seemingly have no 3p animation for this
+ throwSequence.firstPersonBlendOutTime = 0.0
+ throwSequence.teleport = true
+ throwSequence.setInitialTime = Time() - file.introStartTime
+
+ // get our buddy
+ entity buddy
+ if ( player.GetTeam() == TEAM_MILITIA )
+ buddy = file.militiaBuddy
+ else
+ buddy = file.imcBuddy
+
+ // respawn the player
+ player.SetOrigin( buddy.GetOrigin() )
+ player.RespawnPlayer( null )
+ player.Hide()
+ player.HolsterWeapon()
+
+ // hide hud, fade screen out from black
+ AddCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING )
+ ScreenFadeFromBlack( player, 0.5, 0.5 )
+
+ // start fp sequence
+ thread FirstPersonSequence( throwSequence, player, buddy )
+
+ // manually do this because i can't get viewconefastball to work
+ player.PlayerCone_FromAnim()
+ player.PlayerCone_SetMinYaw( -50 )
+ player.PlayerCone_SetMaxYaw( 25 )
+ player.PlayerCone_SetMinPitch( -15 )
+ player.PlayerCone_SetMaxPitch( 15 )
+
+ buddy.WaitSignal( "fastball_start_throw" )
+ // lock in their final angles at this point
+ vector throwVel = AnglesToForward( player.EyeAngles() ) * 950
+ throwVel.z = 675.0
+
+ // wait for it to finish
+ buddy.WaitSignal( "fastball_release" )
+
+ if ( player.IsInputCommandHeld( IN_JUMP ) )
+ throwVel.z = 850.0
+
+ // have to correct this manually here since due to no 3p animation our position isn't set right during this sequence
+ player.SetOrigin( buddy.GetAttachmentOrigin( buddy.LookupAttachment( "FASTBALL_R" ) ) )
+ player.Show()
+ player.SetVelocity( throwVel )
+
+ TryGameModeAnnouncement( player )
+}
+
+void function PlayerHoldingJumpInIntro( entity player )
+{
+ file.playersHoldingJump[ player ] <- true
+}
+
+void function PlayerNoLongerHoldingJumpInIntro( entity player )
+{
+ file.playersHoldingJump[ player ] <- false
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_gg.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_gg.gnut
new file mode 100644
index 00000000..b9ee40f1
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_gg.gnut
@@ -0,0 +1,114 @@
+global function GamemodeGG_Init
+
+void function GamemodeGG_Init()
+{
+ SetSpawnpointGamemodeOverride( FFA )
+
+ SetShouldUseRoundWinningKillReplay( true )
+ Evac_SetEnabled( false )
+ SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period
+ SetWeaponDropsEnabled( false )
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+ Riff_ForceBoostAvailability( eBoostAvailability.Disabled )
+
+ AddCallback_OnPlayerRespawned( OnPlayerRespawned )
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+
+ AddCallback_GameStateEnter( eGameState.WinnerDetermined, OnWinnerDetermined )
+}
+
+void function OnPlayerRespawned( entity player )
+{
+ UpdateLoadout( player )
+ thread OnPlayerRespawned_Threaded( player )
+}
+
+void function OnPlayerRespawned_Threaded( entity player )
+{
+ // bit of a hack, need to rework earnmeter code to have better support for completely disabling it
+ // rn though this just waits for earnmeter code to set the mode before we set it back
+ WaitFrame()
+ PlayerEarnMeter_SetMode( player, eEarnMeterMode.DISABLED )
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( !victim.IsPlayer() || !attacker.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( attacker == victim ) // suicide
+ {
+ string message = victim.GetPlayerName() + " committed suicide."
+ foreach ( entity player in GetPlayerArray() )
+ SendHudMessage( player, message, -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 )
+
+ if ( GameRules_GetTeamScore( victim.GetTeam() ) != 0 )
+ {
+ AddTeamScore( victim.GetTeam(), -1 ) // get absolutely fucking destroyed lol
+ victim.AddToPlayerGameStat( PGS_ASSAULT_SCORE, -1 )
+ }
+ }
+ else
+ {
+ if ( DamageInfo_GetDamageSourceIdentifier( damageInfo ) != eDamageSourceId.melee_pilot_emptyhanded )
+ {
+ AddTeamScore( attacker.GetTeam(), 1 )
+ attacker.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 )
+ UpdateLoadout( attacker )
+ }
+
+ if ( DamageInfo_GetDamageSourceIdentifier( damageInfo ) == eDamageSourceId.human_execution )
+ {
+ string message = victim.GetPlayerName() + " got executed."
+ foreach ( entity player in GetPlayerArray() )
+ SendHudMessage( player, message, -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 )
+
+ if ( GameRules_GetTeamScore( victim.GetTeam() ) != 0 )
+ {
+ AddTeamScore( victim.GetTeam(), -1 ) // get absolutely fucking destroyed lol
+ victim.AddToPlayerGameStat( PGS_ASSAULT_SCORE, -1 )
+ }
+ }
+ }
+}
+
+void function UpdateLoadout( entity player )
+{
+ // todo: honestly, this should be reworked to use PilotLoadoutDefs instead of directly modifying weapons and shit
+
+ int currentWeaponIndex = GameRules_GetTeamScore( player.GetTeam() )
+ array<GunGameWeapon> weapons = GetGunGameWeapons()
+
+ if ( currentWeaponIndex >= weapons.len() )
+ currentWeaponIndex = weapons.len() - 1
+
+ if ( currentWeaponIndex > 18 ) // play end of game music for special weapons
+ PlayMusicToAll( eMusicPieceID.LEVEL_LAST_MINUTE ) // this *shouldn't* overlap if done multiple times
+
+ GunGameWeapon weapon = weapons[ currentWeaponIndex ]
+
+ foreach ( entity weapon in player.GetMainWeapons() )
+ player.TakeWeaponNow( weapon.GetWeaponClassName() )
+
+ foreach ( entity weapon in player.GetOffhandWeapons() )
+ player.TakeWeaponNow( weapon.GetWeaponClassName() )
+
+ if ( weapon.offhandSlot != -1 )
+ {
+ // TEMP: give archer so player so player has a weapon which lets them use offhands
+ // need to replace this with a custom empty weapon at some point
+ player.GiveWeapon( "mp_weapon_rocket_launcher" )
+
+ player.GiveOffhandWeapon( weapon.weapon, weapon.offhandSlot, weapon.mods )
+ }
+ else
+ player.GiveWeapon( weapon.weapon, weapon.mods )
+
+ player.GiveOffhandWeapon( "melee_pilot_emptyhanded", OFFHAND_MELEE )
+}
+
+void function OnWinnerDetermined()
+{
+ SetRespawnsEnabled( false )
+ SetKillcamsEnabled( false )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut
new file mode 100644
index 00000000..8706d53b
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_inf.gnut
@@ -0,0 +1,179 @@
+global function GamemodeInfection_Init
+
+struct {
+ bool hasHadFirstInfection = false
+ array<entity> playersToNotifyOfInfection
+} file
+
+void function GamemodeInfection_Init()
+{
+ SetSpawnpointGamemodeOverride( FFA )
+ SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period
+ SetWeaponDropsEnabled( false )
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+ Riff_ForceBoostAvailability( eBoostAvailability.Disabled )
+
+ AddCallback_OnClientConnected( InfectionInitPlayer )
+ AddCallback_OnPlayerKilled( InfectionOnPlayerKilled )
+ AddCallback_OnPlayerRespawned( RespawnInfected )
+ AddCallback_GameStateEnter( eGameState.Playing, SelectFirstInfected )
+
+ SetTimeoutWinnerDecisionFunc( TimeoutCheckSurvivors )
+}
+
+void function InfectionInitPlayer( entity player )
+{
+ if ( GetGameState() < eGameState.Playing )
+ SetTeam( player, INFECTION_TEAM_SURVIVOR )
+ else
+ InfectPlayer( player )
+}
+
+void function SelectFirstInfected()
+{
+ thread SelectFirstInfectedDelayed()
+}
+
+void function SelectFirstInfectedDelayed()
+{
+ wait 10.0 + RandomFloat( 5.0 )
+
+ array<entity> players = GetPlayerArray()
+ entity infected = players[ RandomInt( players.len() ) ]
+
+ InfectPlayer( infected )
+ RespawnInfected( infected )
+}
+
+void function InfectionOnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( !victim.IsPlayer() || GetGameState() != eGameState.Playing )
+ return
+
+ if ( victim.GetTeam() == INFECTION_TEAM_SURVIVOR )
+ InfectPlayer( victim )
+
+ if ( attacker.IsPlayer() )
+ attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 )
+}
+
+void function InfectPlayer( entity player )
+{
+ SetTeam( player, INFECTION_TEAM_INFECTED )
+ player.SetPlayerGameStat( PGS_ASSAULT_SCORE, 0 ) // reset kills
+ file.playersToNotifyOfInfection.append( player )
+
+ // check how many survivors there are
+ array<entity> survivors = GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR )
+ if ( survivors.len() == 0 )
+ SetWinner( INFECTION_TEAM_INFECTED )
+ else if ( survivors.len() == 1 )
+ SetLastSurvivor( survivors[ 0 ] )
+
+ if ( !file.hasHadFirstInfection )
+ {
+ file.hasHadFirstInfection = true
+
+ foreach ( entity otherPlayer in GetPlayerArray() )
+ if ( player != otherPlayer )
+ Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_AnnounceFirstInfected", player.GetEncodedEHandle() )
+
+ PlayMusicToAll( eMusicPieceID.GAMEMODE_1 )
+ }
+}
+
+void function RespawnInfected( entity player )
+{
+ if ( player.GetTeam() != INFECTION_TEAM_INFECTED )
+ return
+
+ // notify newly infected players of infection
+ if ( file.playersToNotifyOfInfection.contains( player ) )
+ {
+ Remote_CallFunction_NonReplay( player, "ServerCallback_YouAreInfected" )
+ file.playersToNotifyOfInfection.remove( file.playersToNotifyOfInfection.find( player ) )
+ }
+
+ // set camo to pond scum
+ player.SetSkin( 1 )
+ player.SetCamo( 110 )
+
+ // stats for infected
+ StimPlayer( player, 9999.9 ) // can't do endless since we don't get the visual effect in endless
+ player.kv.airAcceleration = 2500
+
+ // scale health with num of infected, with 50 as base health
+ player.SetMaxHealth( ( GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ).len() + 1 ) * 10 )
+
+ // set loadout
+ foreach ( entity weapon in player.GetMainWeapons() )
+ player.TakeWeaponNow( weapon.GetWeaponClassName() )
+
+ foreach ( entity weapon in player.GetOffhandWeapons() )
+ player.TakeWeaponNow( weapon.GetWeaponClassName() )
+
+ // TEMP: give archer so player so player has a weapon which lets them use offhands
+ // need to replace this with a custom empty weapon at some point
+ //player.GiveWeapon( "mp_weapon_rocket_launcher" )
+ player.GiveWeapon( "mp_weapon_mgl" )
+ player.GiveOffhandWeapon( "melee_pilot_emptyhanded", OFFHAND_MELEE )
+
+ thread PlayInfectedSounds( player )
+}
+
+void function PlayInfectedSounds( entity player )
+{
+ player.EndSignal( "OnDeath" )
+ player.EndSignal( "OnDestroy" )
+
+ float nextRandomSound
+ while ( true )
+ {
+ WaitFrame()
+
+ int meleeState = player.PlayerMelee_GetState()
+ if ( nextRandomSound < Time() || meleeState != 0 )
+ {
+ string selectedSound
+ if ( CoinFlip() )
+ selectedSound = "prowler_vocal_attack"
+ else
+ selectedSound = "prowler_vocal_attackmiss"
+
+ bool canSeeSurvivor
+ foreach ( entity survivor in GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ) )
+ if ( TraceLineSimple( player.GetOrigin(), survivor.GetOrigin(), survivor ) == 1.0 )
+ canSeeSurvivor = true
+
+ // _int sounds are less agressive so only play them if we aren't in some sorta fight
+ if ( player.GetHealth() == player.GetMaxHealth() || !canSeeSurvivor || meleeState != 0 )
+ selectedSound += "_int"
+
+ EmitSoundOnEntity( player, selectedSound )
+
+ nextRandomSound = Time() + max( 2.5, RandomFloat( 12.0 ) )
+ while ( player.PlayerMelee_GetState() != 0 ) // need to ensure this is updated
+ WaitFrame()
+ }
+ }
+}
+
+void function SetLastSurvivor( entity player )
+{
+ foreach ( entity otherPlayer in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_AnnounceLastSurvivor", player.GetEncodedEHandle() )
+
+ Highlight_SetEnemyHighlight( player, "enemy_sonar" )
+ thread CreateTitanForPlayerAndHotdrop( player, GetTitanReplacementPoint( player, false ) )
+
+ if ( GameTime_TimeLeftSeconds() > 45 )
+ SetServerVar( "gameEndTime", Time() + 45.0 )
+}
+
+int function TimeoutCheckSurvivors()
+{
+ if ( GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ).len() > 0 )
+ return INFECTION_TEAM_SURVIVOR
+
+ return INFECTION_TEAM_INFECTED
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_kr.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_kr.gnut
new file mode 100644
index 00000000..cf9d6bc5
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_kr.gnut
@@ -0,0 +1,126 @@
+global function GamemodeKR_Init
+
+struct {
+ float currentHighestKillraceAmount
+ int currentKillraceScore
+ entity currentRacer
+ array<vector> flagSpawnPoints
+} file
+
+void function GamemodeKR_Init()
+{
+ PrecacheModel( CTF_FLAG_MODEL )
+
+ SetSpawnpointGamemodeOverride( FFA )
+
+ Evac_SetEnabled( false )
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+ Riff_ForceBoostAvailability( eBoostAvailability.Disabled )
+
+ AddSpawnCallback( "info_hardpoint", AddFlagSpawnPoint )
+
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+ AddCallback_OnTouchHealthKit( "item_flag", StartPlayerKillrace )
+
+ AddCallback_GameStateEnter( eGameState.Playing, StartKillraceSpawnThink )
+}
+
+void function AddFlagSpawnPoint( entity hardpoint )
+{
+ if ( hardpoint.HasKey( "hardpointGroup" ) && ( hardpoint.kv.hardpointGroup == "A" || hardpoint.kv.hardpointGroup == "B" || hardpoint.kv.hardpointGroup == "C" ) )
+ file.flagSpawnPoints.append( hardpoint.GetOrigin() )
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( !victim.IsPlayer() || !attacker.IsPlayer() || attacker == victim )
+ return
+
+ float killRaceTime = attacker.GetPlayerNetTime( "killRaceTime" ) + 5.0
+ attacker.SetPlayerNetTime( "killRaceTime", killRaceTime )
+ if ( killRaceTime > file.currentHighestKillraceAmount )
+ file.currentHighestKillraceAmount = killRaceTime
+ if ( file.currentRacer != null )
+ file.currentKillraceScore++
+}
+
+bool function StartPlayerKillrace( entity player, entity flag )
+{
+ float killRaceTime = player.GetPlayerNetTime( "killRaceTime" )
+ if ( killRaceTime > 0.0 )
+ {
+ thread PlayerKillrace( player, killRaceTime )
+ return true // delete the flag entity
+ }
+
+ return false // keep it alive
+}
+
+void function PlayerKillrace( entity player, float raceTime )
+{
+ file.currentKillraceScore = 0
+ file.currentRacer = player
+ int oldMaxHealth = player.GetMaxHealth()
+
+ player.SetMaxHealth( oldMaxHealth * 10 )
+ player.SetHealth( player.GetMaxHealth() )
+
+ foreach ( entity weapon in player.GetMainWeapons() )
+ foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) )
+ weapon.AddMod( mod )
+
+ foreach ( entity otherPlayer in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_NewKillRacer", player.GetEncodedEHandle(), Time() + raceTime )
+
+ float raceEnd = Time() + raceTime
+ while ( raceEnd > Time() && IsAlive( player ) )
+ WaitFrame()
+
+ player.SetPlayerNetTime( "killRaceTime", 0.0 )
+ player.SetMaxHealth( oldMaxHealth )
+
+ foreach ( entity weapon in player.GetMainWeapons() )
+ foreach ( string mod in GetWeaponBurnMods( weapon.GetWeaponClassName() ) )
+ weapon.RemoveMod( mod )
+
+ foreach ( entity otherPlayer in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( otherPlayer, "ServerCallback_EndKillrace", player.GetEncodedEHandle(), file.currentKillraceScore )
+
+ if ( GameRules_GetTeamScore( player.GetTeam() ) < file.currentKillraceScore )
+ {
+ GameRules_SetTeamScore( player.GetTeam(), file.currentKillraceScore )
+ player.SetPlayerGameStat( PGS_ASSAULT_SCORE, file.currentKillraceScore )
+ }
+
+ thread KillraceSpawnThink() // go to spawn next flag
+}
+
+void function StartKillraceSpawnThink()
+{
+ thread KillraceSpawnThink()
+}
+
+void function KillraceSpawnThink()
+{
+ file.currentHighestKillraceAmount = 0
+ file.currentRacer = null
+ file.currentKillraceScore = 0
+ float time = Time()
+ while ( time + 20.0 > Time() && file.currentHighestKillraceAmount < 25 )
+ WaitFrame()
+
+ vector spawnpos = file.flagSpawnPoints[ RandomInt( file.flagSpawnPoints.len() ) ]
+ foreach ( entity player in GetPlayerArray() )
+ Remote_CallFunction_NonReplay( player, "ServerCallback_FlagSpawnIncoming", spawnpos.x, spawnpos.y, spawnpos.z, Time() + 15 )
+
+ wait 15.0
+
+ // create a flag
+ entity flag = CreateEntity( "item_flag" )
+ flag.SetValueForModelKey( CTF_FLAG_MODEL )
+ SetTargetName( flag, "krflag" )
+ DispatchSpawn( flag )
+ flag.SetModel( CTF_FLAG_MODEL )
+ flag.SetOrigin( spawnpos + < 0, 0, flag.GetBoundingMaxs().z / 2 > ) // get it out of the ground
+ flag.SetVelocity( < 0, 0, 1 > ) // make it do gravity again
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_sbox.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_sbox.gnut
new file mode 100644
index 00000000..27581aea
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_sbox.gnut
@@ -0,0 +1,49 @@
+untyped
+global function GamemodeSbox_Init
+
+struct {
+ array<entity> spawnpoints
+} file
+
+void function GamemodeSbox_Init()
+{
+ SetConVarInt( "sv_cheats", 1 ) // cheats on by default
+
+ // cache spawnpoints
+ //file.spawnpoints = GetEntArrayByClass_Expensive( "info_spawnpoint_human" )
+ // todo just use a spawn callback for this rather than weird late cache in spawn
+
+ AddCallback_OnClientConnected( SboxSpawnPlayer )
+ AddDeathCallback( "player", SboxRespawnPlayer )
+}
+
+void function SboxSpawnPlayer( entity player )
+{
+ if ( player.GetPlayerSettings() == "spectator" ) // if they haven't spawned yet
+ player.SetPlayerSettings( "pilot_grapple_male" )
+
+ if ( file.spawnpoints.len() == 0 ) // have to cache late rather than on init due to spawnpoints not existing in init
+ file.spawnpoints = GetEntArrayByClass_Expensive( "info_spawnpoint_human" )
+
+ if ( GetGameState() != eGameState.Playing ) // hacky but can't set this in init either
+ SetGameState( eGameState.Playing )
+
+ entity spawnpoint = file.spawnpoints[ RandomInt( file.spawnpoints.len() ) ]
+
+ ScreenFadeFromBlack( player, 0.0, 0.0 ) // HACK before non-classicmp intros are ready, remove the blackscreen we get from waitingforplayers
+ player.RespawnPlayer( spawnpoint )
+ player.GiveWeapon( "mp_weapon_toolgun" )
+}
+
+void function SboxRespawnPlayer( entity player, var damageInfo )
+{
+ thread SboxRespawnPlayerThreaded( player )
+}
+
+void function SboxRespawnPlayerThreaded( entity player )
+{
+ // todo: replace this with real respawn logic when that's ready
+
+ wait 2.5
+ SboxSpawnPlayer( player )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_tt.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_tt.gnut
new file mode 100644
index 00000000..6a53ef87
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_gamemode_tt.gnut
@@ -0,0 +1,71 @@
+global function GamemodeTT_Init
+
+struct {
+ entity lastPlayerDropped
+} file
+
+void function GamemodeTT_Init()
+{
+ SetSpawnpointGamemodeOverride( TEAM_DEATHMATCH )
+ Riff_ForceTitanAvailability( eTitanAvailability.Never )
+
+ AddCallback_GameStateEnter( eGameState.Playing, OnEnterPlaying )
+
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+ AddDeathCallback( "npc_titan", OnTitanKilled )
+}
+
+void function OnEnterPlaying()
+{
+ thread DropRandomTitan()
+}
+
+void function DropRandomTitan()
+{
+ array<entity> players = GetPlayerArray()
+
+ if ( players.len() == 1 )
+ file.lastPlayerDropped = null // don't wanna loop forever if only 1 player
+
+ entity titanPlayer
+ do {
+ titanPlayer = players[ RandomInt( players.len() ) ]
+ } while ( titanPlayer == file.lastPlayerDropped )
+
+ DropTitanForPlayer( titanPlayer, 5.0 )
+}
+
+void function DropTitanForPlayer( entity player, float delay )
+{
+ wait delay
+
+ file.lastPlayerDropped = player
+ CreateTitanForPlayerAndHotdrop( player, GetTitanReplacementPoint( player, false ) )
+}
+
+void function AttemptToDropTitanForKill( entity victim, entity attacker, var damageInfo )
+{
+ if ( !victim.IsTitan() )
+ return
+
+ if ( !attacker.IsPlayer() || victim == attacker )
+ thread DropRandomTitan()
+ else
+ thread DropTitanForPlayer( attacker, 2.0 )
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( victim.IsTitan() )
+ AttemptToDropTitanForKill( victim, attacker, damageInfo )
+ else if ( attacker.IsTitan() )
+ {
+ AddTeamScore( attacker.GetTeam(), 1 )
+ }
+}
+
+void function OnTitanKilled( entity victim, var damageInfo )
+{
+ if ( IsPetTitan( victim ) )
+ AttemptToDropTitanForKill( victim, DamageInfo_GetAttacker( damageInfo ), damageInfo )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/_riff_instagib.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_riff_instagib.gnut
new file mode 100644
index 00000000..b3868359
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/_riff_instagib.gnut
@@ -0,0 +1,65 @@
+global function RiffInstagib_Init
+
+struct {
+ table<entity, int> playerWeapons
+ array<string> instagibWeapons
+} file
+
+void function RiffInstagib_Init()
+{
+ if ( GetCurrentPlaylistVarInt( "riff_instagib", 0 ) == 0 )
+ return
+
+ SetLoadoutGracePeriodEnabled( false )
+ SetWeaponDropsEnabled( false )
+
+ file.instagibWeapons = [
+ "mp_weapon_sniper",
+ "mp_weapon_wingman",
+ "mp_weapon_defender",
+ "mp_weapon_arena3",
+ "mp_weapon_wingman_n",
+ "mp_weapon_doubletake",
+ ]
+ file.instagibWeapons.randomize()
+
+ AddCallback_OnPlayerRespawned( InstagibSetWeapons )
+ AddCallback_OnPlayerKilled( InstagibCycleWeaponsForKill )
+ AddCallback_OnClientDisconnected( InstagibCleanupClient )
+}
+
+void function InstagibSetWeapons( entity player )
+{
+ if ( !( player in file.playerWeapons ) )
+ file.playerWeapons[ player ] <- 0
+
+ player.SetMaxHealth( 1 )
+ InstagibUpdateWeapons( player )
+}
+
+void function InstagibUpdateWeapons( entity player )
+{
+ foreach( entity weapon in player.GetMainWeapons() )
+ player.TakeWeaponNow( weapon.GetWeaponClassName() )
+
+ player.TakeWeaponNow( player.GetOffhandWeapon( OFFHAND_RIGHT ).GetWeaponClassName() )
+ if ( !HasOffhandWeapon( player, "mp_weapon_grenade_sonar" ) )
+ player.GiveOffhandWeapon( "mp_weapon_grenade_sonar", OFFHAND_RIGHT )
+
+ player.GiveWeapon( file.instagibWeapons[ file.playerWeapons[ player ] ] )
+}
+
+void function InstagibCycleWeaponsForKill( entity victim, entity attacker, var damageInfo )
+{
+ if ( !victim.IsPlayer() || !attacker.IsPlayer() || victim == attacker )
+ return
+
+ file.playerWeapons[ attacker ] = ( file.playerWeapons[ attacker ] + 1 ) % file.instagibWeapons.len()
+ InstagibUpdateWeapons( attacker )
+}
+
+void function InstagibCleanupClient( entity player )
+{
+ if ( player in file.playerWeapons )
+ delete file.playerWeapons[ player ]
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_arena.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_arena.gnut
new file mode 100644
index 00000000..632a7efc
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_arena.gnut
@@ -0,0 +1,40 @@
+global function ClGameModeArena_Init
+global function ServerCallback_CreateMoneyParticles
+
+struct {
+ var moneyRui
+} file
+
+void function ClGameModeArena_Init()
+{
+ AddCallback_OnClientScriptInit( CreateArenaUI )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_fd_intro_easy", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_fd_intro_easy", TEAM_IMC )
+}
+
+void function CreateArenaUI( entity player )
+{
+ AddEventNotificationCallback( eEventNotifications.FD_BoughtItem, void function( entity e, var v ) {} )
+
+ var rui = CreateCockpitRui( $"ui/fd_score_splash.rpak", 500 )
+ RuiTrackInt( rui, "pointValue", player, RUI_TRACK_SCRIPT_NETWORK_VAR_INT, GetNetworkedVariableIndex( "FD_money" ) )
+ RuiTrackInt( rui, "pointStack", player, RUI_TRACK_SCRIPT_NETWORK_VAR_INT, GetNetworkedVariableIndex( "FD_money256" ) )
+ file.moneyRui = rui
+}
+
+void function ServerCallback_CreateMoneyParticles( int playerHandle, int amount )
+{
+ // largely taken from cl_gamemode_fd
+ entity player = GetEntityFromEncodedEHandle( playerHandle )
+
+ vector randDir2D = < RandomFloatRange( -1, 1 ), 1, 0 >
+ randDir2D = Normalize( randDir2D )
+
+ var rui = RuiCreate( $"ui/at_score_popup.rpak", clGlobal.topoFullScreen, RUI_DRAW_HUD, 100 )
+ RuiSetInt( rui, "scoreVal", amount )
+ RuiSetGameTime( rui, "startTime", Time() )
+ RuiSetFloat3( rui, "pos", player.EyePosition() )
+ RuiSetFloat2( rui, "driftDir", randDir2D )
+ RuiSetBool( rui, "showNormalPoints", false )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut
new file mode 100644
index 00000000..80dc548a
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_fastball.gnut
@@ -0,0 +1,96 @@
+global function ClGamemodeFastball_Init
+global function ServerCallback_FastballUpdatePanelRui
+global function ServerCallback_FastballPanelHacked
+global function ServerCallback_FastballRespawnPlayer
+
+struct {
+ var panelARui
+ var panelBRui
+ var panelCRui
+} file
+
+void function ClGamemodeFastball_Init()
+{
+ ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_lts.rpak" )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "Music_Beacon_14_BTThrowThruFirstCrane", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "Music_Beacon_14_BTThrowThruFirstCrane", TEAM_MILITIA )
+
+ AddCallback_OnClientScriptInit( FastballCreateRui )
+}
+
+void function FastballCreateRui( entity player )
+{
+ file.panelARui = CreateCockpitRui( $"ui/cp_hardpoint_marker.rpak", 200 )
+ file.panelBRui = CreateCockpitRui( $"ui/cp_hardpoint_marker.rpak", 200 )
+ file.panelCRui = CreateCockpitRui( $"ui/cp_hardpoint_marker.rpak", 200 )
+
+}
+
+void function ServerCallback_FastballUpdatePanelRui( int panelHandle, int id )
+{
+ entity panel = GetEntityFromEncodedEHandle( panelHandle )
+ var rui
+ if ( id == 0 )
+ rui = file.panelARui
+ else if ( id == 1 )
+ rui = file.panelBRui
+ else if ( id == 2 )
+ rui = file.panelCRui
+
+ RuiSetInt( rui, "hardpointId", id )
+ RuiTrackFloat3( rui, "pos", panel, RUI_TRACK_OVERHEAD_FOLLOW )
+ RuiSetInt( rui, "viewerTeam", GetLocalClientPlayer().GetTeam() )
+ ////RuiTrackInt( rui, "cappingTeam", null, RUI_TRACK_SCRIPT_NETWORK_VAR_GLOBAL_INT, GetNetworkedVariableIndex( "panel" + id + "progress" ) )
+ RuiTrackInt( rui, "hardpointTeamRelation", panel, RUI_TRACK_TEAM_RELATION_VIEWPLAYER )
+
+ RuiSetBool( rui, "isVisible", true )
+}
+
+void function ServerCallback_FastballPanelHacked( int panelHandle, int id, int capturingPlayerHandle )
+{
+ ServerCallback_FastballUpdatePanelRui( panelHandle, id ) // may not be necessary, just wanna ensure this is always right
+
+ entity panel = GetEntityFromEncodedEHandle( panelHandle )
+ entity capturingPlayer = GetEntityFromEncodedEHandle( capturingPlayerHandle )
+
+ if ( capturingPlayer == GetLocalViewPlayer() )
+ return
+
+ string panelIdString
+ if ( id == 0 )
+ panelIdString = "A"
+ if ( id == 1 )
+ panelIdString = "B"
+ else if ( id == 2 )
+ panelIdString = "C"
+
+ AnnouncementData announcement = Announcement_Create( Localize( "#FASTBALL_PANEL_CAPTURED", capturingPlayer.GetPlayerName(), panelIdString ) )
+
+ if ( capturingPlayer.GetTeam() == GetLocalViewPlayer().GetTeam() )
+ Announcement_SetTitleColor( announcement, < 0, 0, 1 > )
+ else
+ Announcement_SetTitleColor( announcement, < 1, 0, 0 > )
+
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( GetLocalViewPlayer(), announcement )
+}
+
+void function ServerCallback_FastballRespawnPlayer()
+{
+ thread FastballRespawnPlayerEffects_Threaded()
+}
+
+void function FastballRespawnPlayerEffects_Threaded()
+{
+ // sometimes this seems to get called before the player has respawned clientside, so we just wait until the client thinks they're alive
+ entity player = GetLocalViewPlayer()
+
+ while ( !IsAlive( player ) )
+ WaitFrame()
+
+ StartParticleEffectOnEntity( player.GetCockpit(), GetParticleSystemIndex( $"P_pod_screen_lasers_OUT" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_gg.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_gg.gnut
new file mode 100644
index 00000000..de8a3449
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_gg.gnut
@@ -0,0 +1,26 @@
+global function ClGamemodeGG_Init
+
+void function ClGamemodeGG_Init()
+{
+ // add ffa gamestate asset
+ ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ffa.rpak" )
+
+ // add music for mode, this is copied directly from the ffa/fra music registered in cl_music.gnut
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_MILITIA )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_inf.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_inf.gnut
new file mode 100644
index 00000000..56763bd4
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_inf.gnut
@@ -0,0 +1,75 @@
+global function ClGamemodeInfection_Init
+global function ServerCallback_YouAreInfected
+global function ServerCallback_AnnounceFirstInfected
+global function ServerCallback_AnnounceLastSurvivor
+
+void function ClGamemodeInfection_Init()
+{
+ //ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ffa.rpak" )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_beacon_8a_jumpingsuccess", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_beacon_8a_jumpingsuccess", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "Music_Beacon_24_BTLob", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "Music_Beacon_24_BTLob", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_titanwar_lastminute", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_titanwar_lastminute", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_IMC )
+}
+
+void function ServerCallback_YouAreInfected()
+{
+ // heavily based on mfd code
+ entity localPlayer = GetLocalViewPlayer()
+
+ StartParticleEffectOnEntity( localPlayer.GetCockpit(), GetParticleSystemIndex( $"P_MFD" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 )
+ EmitSoundOnEntity( localPlayer, "UI_InGame_MarkedForDeath_PlayerMarked" )
+ HideEventNotification()
+ AnnouncementData announcement = Announcement_Create( "#INFECTION_YOU_ARE_INFECTED" )
+ Announcement_SetSubText( announcement, "#INFECTION_KILL_SURVIVORS" )
+ Announcement_SetTitleColor( announcement, <1,0,0> )
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( localPlayer, announcement )
+}
+
+void function ServerCallback_AnnounceFirstInfected( int survivorEHandle )
+{
+ entity player = GetEntityFromEncodedEHandle( survivorEHandle )
+
+ AnnouncementData announcement = Announcement_Create( Localize( "#INFECTION_FIRST_INFECTED", player.GetPlayerName() ) )
+ //Announcement_SetSubText( announcement, "#INFECTION_KILL_LAST_SURVIVOR" )
+ Announcement_SetTitleColor( announcement, <1,0,0> )
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( GetLocalViewPlayer(), announcement )
+}
+
+void function ServerCallback_AnnounceLastSurvivor( int survivorEHandle )
+{
+ entity player = GetEntityFromEncodedEHandle( survivorEHandle )
+
+ string announcementString = Localize( "#INFECTION_LAST_SURVIVOR", player.GetPlayerName() )
+ string announcementSubString = "#INFECTION_KILL_LAST_SURVIVOR"
+ if ( player == GetLocalViewPlayer() )
+ {
+ announcementString = "#INFECTION_YOU_ARE_LAST_SURVIVOR"
+ announcementSubString = "#INFECTION_SURVIVE_LAST_SURVIVOR"
+ }
+
+ AnnouncementData announcement = Announcement_Create( announcementString )
+ Announcement_SetSubText( announcement, announcementSubString )
+ Announcement_SetTitleColor( announcement, <1,0,0> )
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 ) //Be higher priority than Titanfall ready indicator etc
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( GetLocalViewPlayer(), announcement )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_kr.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_kr.gnut
new file mode 100644
index 00000000..269057c7
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_kr.gnut
@@ -0,0 +1,241 @@
+global function ClGamemodeKR_Init
+
+global function ShowTimeGainOnKill
+global function ServerCallback_FlagSpawnIncoming
+global function ServerCallback_NewKillRacer
+global function ServerCallback_EndKillrace
+
+struct {
+ var currentTimeRui
+ var currentTimeAdditionRui
+ var flagRui
+ var flagIncomingRui
+ var killRacerRui
+
+ bool isCurrentlyInRace = false
+ float currentTimeAmount
+ float currentTimeLastAdditionTime
+ float currentTimeAdditionCombined
+} file
+
+void function ClGamemodeKR_Init()
+{
+ // add ffa gamestate asset
+ ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ffa.rpak" )
+
+ // add music for mode, this is copied directly from the ffa/fra music registered in cl_music.gnut
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_freeagents_intro", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_freeagents_outro_win", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_freeagents_outro_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_THREE_MINUTE, "music_mp_freeagents_almostdone", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_freeagents_lastminute", TEAM_MILITIA )
+
+ AddCallback_OnClientScriptInit( CreateKRUI )
+ AddCreateCallback( "item_flag", OnFlagCreated )
+ AddDestroyCallback( "item_flag", OnFlagDestroyed )
+}
+
+void function CreateKRUI( entity player )
+{
+ file.currentTimeRui = CreateCockpitRui( $"ui/titan_protocol_text_center.rpak", 200 )
+ RuiSetInt( file.currentTimeRui, "lineNum", 1 )
+ RuiSetGameTime( file.currentTimeRui, "startTime", 0.0 )
+ UpdateCurrentTimeAmount()
+
+ file.flagRui = CreateCockpitRui( $"ui/speedball_flag_marker.rpak", 200 )
+ RuiSetBool( file.flagRui, "playerIsCarrying", false )
+ RuiSetBool( file.flagRui, "isCarried", false )
+
+ file.killRacerRui = CreateCockpitRui( $"ui/mfd_target_marker.rpak", 200 )
+ RuiSetBool( file.killRacerRui, "isVisible", false )
+ RuiSetImage( file.killRacerRui, "markedIcon", $"rui/hud/gametype_icons/mfd/mfd_enemy" )
+ RuiSetBool( file.killRacerRui, "isMarked", true )
+}
+
+void function OnFlagCreated( entity flag )
+{
+ if ( IsValid( file.flagIncomingRui ) )
+ RuiDestroy( file.flagIncomingRui )
+
+ RuiSetBool( file.flagRui, "isVisible", true )
+ RuiTrackFloat3( file.flagRui, "pos", flag, RUI_TRACK_OVERHEAD_FOLLOW )
+}
+
+void function OnFlagDestroyed( entity flag )
+{
+ RuiSetBool( file.flagRui, "isVisible", false )
+}
+
+void function ShowTimeGainOnKill( entity player, float oldVal, float newVal, bool actuallyChanged )
+{
+ if ( file.isCurrentlyInRace || player != GetLocalViewPlayer() || !actuallyChanged )
+ return
+
+ if ( newVal > oldVal ) // time increase: likely given on kill
+ {
+ float amount = newVal - oldVal
+
+ // show a combined number on the addition rui if last addition was recent enough
+ float additionShowAmount = amount
+ file.currentTimeAdditionCombined += amount
+ if ( Time() - file.currentTimeLastAdditionTime < 1.25 )
+ amount = file.currentTimeAdditionCombined
+ else
+ {
+ file.currentTimeAdditionRui = CreateCockpitRui( $"ui/titan_protocol_text_center.rpak", 200 )
+ RuiSetInt( file.currentTimeAdditionRui, "lineNum", 2 )
+ file.currentTimeAdditionCombined = amount
+ }
+
+ RuiSetString( file.currentTimeAdditionRui, "displayString", "+" + amount + "s 00ms " ) // formatted so that it lines up with other rui
+ RuiSetGameTime( file.currentTimeAdditionRui, "startTime", Time() )
+ RuiSetGameTime( file.currentTimeAdditionRui, "endTime", Time() + 1.5 )
+
+ file.currentTimeLastAdditionTime = Time()
+
+ thread UpdateFullTimeAmountAfterAdditionDone( file.currentTimeLastAdditionTime )
+ }
+ else // time decrease either a reset or
+ UpdateCurrentTimeAmount()
+}
+
+void function UpdateFullTimeAmountAfterAdditionDone( float previousAdditionTime )
+{
+ wait 1.25
+
+ if ( previousAdditionTime == file.currentTimeLastAdditionTime ) // if not, there's been another addition since this was last updated and we'll wait for that instead
+ UpdateCurrentTimeAmount()
+}
+
+void function UpdateCurrentTimeAmount( float overrideTime = -1 )
+{
+ if ( overrideTime == -1 )
+ file.currentTimeAmount = GetLocalViewPlayer().GetPlayerNetTime( "killRaceTime" )
+ else
+ file.currentTimeAmount = overrideTime
+
+ string currentTimeString
+ int seconds = file.currentTimeAmount.tointeger()
+
+ string secondsString = seconds.tostring()
+ if ( secondsString.len() < 2 ) // pad to 2 chars
+ secondsString = "0" + secondsString
+ currentTimeString += secondsString + "s "
+
+ string msString = ( ( file.currentTimeAmount - file.currentTimeAmount.tointeger() ) * 100 ).tostring()
+ if ( msString.len() < 2 ) // pad to 2 chars
+ msString = "0" + msString
+ currentTimeString += msString.slice( 0, 2 ) + "ms "
+
+ RuiSetString( file.currentTimeRui, "displayString", currentTimeString )
+ RuiSetGameTime( file.currentTimeRui, "endTime", Time() + 99999.0 ) // arbitrarily large number so this doesn't disappear
+}
+
+void function ServerCallback_FlagSpawnIncoming( float x, float y, float z , float spawnTime )
+{
+ print( "flagspawn: < " + x + ", " + y + ", " + z + " > in " + ( spawnTime - Time() ) + " seconds" )
+
+ AnnouncementData announcement = Announcement_Create( Localize( "#KR_FLAG_INCOMING", spawnTime.tostring() ) )
+ Announcement_SetSubText( announcement, "#KR_COLLECT_FLAG" )
+ Announcement_SetTitleColor( announcement, < 0, 0, 1 > )
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 )
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( GetLocalViewPlayer(), announcement )
+
+ RuiSetFloat3( file.flagRui, "pos", < x, y, z > )
+ RuiSetBool( file.flagRui, "isVisible", true )
+
+ file.flagIncomingRui = RuiCreate( $"ui/titanfall_timer.rpak", clGlobal.topoFullScreen, RUI_DRAW_HUD, 0 )
+ RuiTrackFloat3( file.flagIncomingRui, "playerPos", GetLocalViewPlayer(), RUI_TRACK_ABSORIGIN_FOLLOW )
+ RuiSetFloat3( file.flagIncomingRui, "pos", < x, y, z > + < 0, 0, 48 > )
+ RuiSetGameTime( file.flagIncomingRui, "impactTime", spawnTime )
+}
+
+void function ServerCallback_NewKillRacer( int playerHandle, float endTime )
+{
+ entity player = GetEntityFromEncodedEHandle( playerHandle )
+
+ string announcementMessage = Localize( "#KR_NEW_RACER", player.GetPlayerName() )
+ string announcementSubMessage
+ if ( player == GetLocalViewPlayer() )
+ {
+ file.isCurrentlyInRace = true
+ thread LerpTimeDuringRace( endTime )
+
+ StartParticleEffectOnEntity( player.GetCockpit(), GetParticleSystemIndex( $"P_MFD" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 )
+ EmitSoundOnEntity( player, "UI_InGame_MarkedForDeath_PlayerMarked" )
+ HideEventNotification()
+
+ announcementMessage = "#KR_YOU_ARE_NEW_RACER"
+ announcementSubMessage = "#KR_YOU_SET_NEW_RECORD"
+ }
+ else
+ {
+ // mark the player
+ RuiTrackFloat3( file.killRacerRui, "pos", player, RUI_TRACK_OVERHEAD_FOLLOW )
+ RuiSetBool( file.killRacerRui, "isVisible", true )
+ }
+
+ AnnouncementData announcement = Announcement_Create( announcementMessage )
+ Announcement_SetSubText( announcement, announcementSubMessage )
+ Announcement_SetTitleColor( announcement, < 1, 0, 0 > )
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 )
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( GetLocalViewPlayer(), announcement )
+}
+
+void function LerpTimeDuringRace( float endTime )
+{
+ while ( Time() < endTime )
+ {
+ // manually update this here so we can get more frequent updates than what we'd get with networked vars
+ UpdateCurrentTimeAmount( endTime - Time() )
+ WaitFrame()
+ }
+
+ UpdateCurrentTimeAmount( 0.0 )
+ file.isCurrentlyInRace = false
+}
+
+void function ServerCallback_EndKillrace( int playerHandle, int score )
+{
+ entity player = GetEntityFromEncodedEHandle( playerHandle )
+
+ vector colour = < 0, 0, 1 >
+ string announcementMessage = Localize( "#KR_ENEMY_KILLRACE_OVER", player.GetPlayerName() )
+ string announcementSubMessage
+ if ( player == GetLocalViewPlayer() )
+ {
+ StartParticleEffectOnEntity( GetLocalViewPlayer().GetCockpit(), GetParticleSystemIndex( $"P_MFD_unmark" ), FX_PATTACH_ABSORIGIN_FOLLOW, -1 )
+ colour = < 1, 0, 0 >
+ announcementMessage = "#KR_YOUR_KILLRACE_OVER"
+ announcementSubMessage = Localize( "#KR_YOUR_KILLRACE_SCORE", score )
+ }
+
+ AnnouncementData announcement = Announcement_Create( announcementMessage )
+ Announcement_SetSubText( announcement, announcementSubMessage )
+ Announcement_SetTitleColor( announcement, colour )
+ Announcement_SetPurge( announcement, true )
+ Announcement_SetPriority( announcement, 200 )
+ Announcement_SetSoundAlias( announcement, SFX_HUD_ANNOUNCE_QUICK )
+ Announcement_SetStyle( announcement, ANNOUNCEMENT_STYLE_QUICK )
+ AnnouncementFromClass( GetLocalViewPlayer(), announcement )
+
+ RuiSetBool( file.killRacerRui, "isVisible", false )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_tt.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_tt.gnut
new file mode 100644
index 00000000..b73ed958
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/cl_gamemode_tt.gnut
@@ -0,0 +1,26 @@
+global function ClGamemodeTT_Init
+
+void function ClGamemodeTT_Init()
+{
+ // register gamestate asset, this is default so not necessary but doing it anyway
+ ClGameState_RegisterGameStateAsset( $"ui/gamestate_info_ps.rpak" )
+
+ // add music for mode, this is copied directly from the attrition/tdm music registered in cl_music.gnut
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_pilothunt_intro_flyin", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_pilothunt_intro_flyin", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_pilothunt_epilogue_win", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_pilothunt_epilogue_win", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_pilothunt_epilogue_win", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_pilothunt_epilogue_win", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_pilothunt_epilogue_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_pilothunt_epilogue_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "music_mp_pilothunt_almostdone", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "music_mp_pilothunt_almostdone", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_pilothunt_lastminute", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_pilothunt_lastminute", TEAM_MILITIA )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_arena_loadouts.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_arena_loadouts.gnut
new file mode 100644
index 00000000..ae0fa7d6
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_arena_loadouts.gnut
@@ -0,0 +1,98 @@
+// the purpose of this script is basically just to provide methods that are used in modified burnmeter and boost_store scripts to get custom items to work
+
+global function InitialiseArenaLoadouts
+global function PopulateArenaLoadouts
+global function GetArenaLoadoutForRoundCount
+global function GetArenaLoadoutItemAsBurnReward
+global function GetCashBoostForRoundCount
+
+#if SERVER
+ global function GivePlayerArenaLoadoutItem
+#endif
+
+const int ABILITY_ROUND = 2
+const int CRATE_ROUND = 4
+const int TITAN_ROUND = 7
+
+struct {
+ array<BoostStoreData> tier0Weapons
+ array<BoostStoreData> tier1Weapons
+ array<BoostStoreData> antiTitanWeapons
+
+ BoostStoreData droppodRespawn
+ BoostStoreData titanBattery
+ BoostStoreData titanfall
+} file
+
+void function InitialiseArenaLoadouts()
+{
+ BoostStoreData g2Data = { itemRef="mp_weapon_g2", modesAllowed="arena", cost=75, ... }
+ file.tier0Weapons.append( g2Data )
+
+ BoostStoreData flatlineData = { itemRef="mp_weapon_vinson", modesAllowed="arena", cost=125, ... }
+ file.tier0Weapons.append( flatlineData )
+
+ BoostStoreData wingmanData = { itemRef="mp_weapon_wingman", modesAllowed="arena", cost=50, ... }
+ file.tier0Weapons.append( wingmanData )
+
+ BoostStoreData respawnData = { itemRef="droppodRespawn", modesAllowed="arena", cost=50, ... }
+ file.tier0Weapons.append( respawnData )
+
+ #if SERVER
+ SetBoostPurchaseCallback( GivePlayerArenaLoadoutItem )
+ #endif
+}
+
+array<BoostStoreData> function PopulateArenaLoadouts()
+{
+ return GetArenaLoadoutForRoundCount( 0 )
+}
+
+array<BoostStoreData> function GetArenaLoadoutForRoundCount( int round )
+{
+ return file.tier0Weapons
+}
+
+BurnReward function GetArenaLoadoutItemAsBurnReward( string itemRef )
+{
+ BurnReward reward
+ reward.ref = itemRef
+ reward.localizedName = itemRef
+
+ return reward
+}
+
+bool function ArenaLoadoutItemIsWeapon( string item )
+{
+ if ( item.find( "mp_weapon" ) == 0 )
+ return true
+
+ return false
+}
+
+int function GetCashBoostForRoundCount( int round )
+{
+ if ( round == 0 )
+ return 150
+
+ if ( round < 4 )
+ return 250
+
+ return 350
+}
+
+#if SERVER
+ void function GivePlayerArenaLoadoutItem( entity player, BoostStoreData item )
+ {
+ if ( ArenaLoadoutItemIsWeapon( item.itemRef ) )
+ {
+ array<string> mods
+ // apply mods
+ //if ( item.itemRef = "mp_weapon_wingman" )
+
+ player.GiveWeapon( item.itemRef, mods )
+ player.SetActiveWeaponByName( item.itemRef )
+ return
+ }
+ }
+#endif \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_arena.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_arena.gnut
new file mode 100644
index 00000000..b634f1d3
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_arena.gnut
@@ -0,0 +1,58 @@
+global function Sh_GamemodeArena_Init
+
+global const string GAMEMODE_ARENA = "arena"
+
+void function Sh_GamemodeArena_Init()
+{
+ // create custom gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeArena )
+ AddCallback_OnRegisteringCustomNetworkVars( ArenaRegisterNetworkVars )
+}
+
+void function CreateGamemodeArena()
+{
+ GameMode_Create( GAMEMODE_ARENA )
+ GameMode_SetName( GAMEMODE_ARENA, "#GAMEMODE_arena" )
+ GameMode_SetDesc( GAMEMODE_ARENA, "#PL_arena_desc" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_ARENA, "gnrc_modeDesc" )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_ARENA, 5, 0.0 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_ARENA, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_SetColor( GAMEMODE_ARENA, [147, 204, 57, 255] )
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_ARENA, GameModeArena_Init )
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_ARENA, RateSpawnpoints_Generic )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_ARENA, RateSpawnpoints_Generic )
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_ARENA, ClGameModeArena_Init )
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_ARENA, CompareAssaultScore )
+ #endif
+}
+
+void function ArenaRegisterNetworkVars()
+{
+ if ( GAMETYPE != GAMEMODE_ARENA )
+ return
+
+ // boost store stuff
+ Remote_RegisterFunction( "ServerCallback_OpenBoostStore" )
+ Remote_RegisterFunction( "ServerCallback_UpdateMoney" )
+ Remote_RegisterFunction( "ServerCallback_UpdateTeamReserve" )
+ Remote_RegisterFunction( "ServerCallback_UpdatePlayerHasBattery" )
+ Remote_RegisterFunction( "ServerCallback_UpdateAmpedWeaponState" )
+ Remote_RegisterFunction( "ServerCallback_BoostStoreTitanHint" )
+ Remote_RegisterFunction( "ServerCallback_UpdateTurretCount" )
+
+ RegisterNetworkedVariable( "boostStoreOpen", SNDC_GLOBAL, SNVT_BOOL, false )
+ RegisterNetworkedVariable( "FD_money", SNDC_PLAYER_GLOBAL, SNVT_UNSIGNED_INT, 0 )
+ RegisterNetworkedVariable( "FD_money256", SNDC_PLAYER_GLOBAL, SNVT_UNSIGNED_INT, 0 )
+
+ // these are required to prevent crashes in fd code that's called from menu_boost_store
+ RegisterNetworkedVariable( "numSuperRodeoGrenades", SNDC_PLAYER_GLOBAL, SNVT_INT, 0 )
+ RegisterNetworkedVariable( "FD_waveActive", SNDC_GLOBAL, SNVT_BOOL, false )
+
+ // arena-exclusive stuff
+ Remote_RegisterFunction( "ServerCallback_CreateMoneyParticles" )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_bomb.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_bomb.gnut
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_bomb.gnut
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut
new file mode 100644
index 00000000..1a1ce645
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_ctf_comp.gnut
@@ -0,0 +1,114 @@
+global function ShGamemodeCTFComp_Init
+
+global const string GAMEMODE_CTF_COMP = "ctf_comp"
+
+void function ShGamemodeCTFComp_Init()
+{
+ // create custom gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeCTFComp )
+ AddCallback_OnRegisteringCustomNetworkVars( CTFCompRegisterNetworkVars )
+}
+
+void function CreateGamemodeCTFComp()
+{
+ GameMode_Create( GAMEMODE_CTF_COMP )
+ GameMode_SetName( GAMEMODE_CTF_COMP, "#GAMEMODE_ctf_comp" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_CTF_COMP, "ctf_modeDesc" )
+ GameMode_SetDesc( GAMEMODE_CTF_COMP, "#PL_capture_the_flag_hint" )
+ GameMode_SetIcon( GAMEMODE_CTF_COMP, $"ui/menu/playlist/ctf" )
+ GameMode_SetSuddenDeath( GAMEMODE_CTF_COMP, true )
+ GameMode_SetDefaultScoreLimits( GAMEMODE_CTF_COMP, 0, 5 )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_CTF_COMP, 0, 3.0 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_CTF_COMP, "#SCOREBOARD_CAPTURES", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_CTF_COMP, "#SCOREBOARD_RETURNS", PGS_DEFENSE_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_CTF_COMP, "#SCOREBOARD_KILLS", PGS_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_CTF_COMP, "#SCOREBOARD_TITAN_DAMAGE", PGS_DISTANCE_SCORE, 6 ) // gotta use a weird pgs here since we're running out of them lol
+ GameMode_SetColor( GAMEMODE_CTF_COMP, [61, 117, 193, 255] )
+
+ AddPrivateMatchMode( GAMEMODE_CTF_COMP ) // add to private lobby modes
+
+ // this gamemode is literally just normal ctf + a few extra settings
+ // as such we do all the inits in this file, not enough logic to be worth splitting it up
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_CTF_COMP, InitCTFCompSpecificSettings )
+ GameMode_AddServerInit( GAMEMODE_CTF_COMP, CaptureTheFlag_Init )
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_CTF_COMP, RateSpawnpoints_CTF )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_CTF_COMP, RateSpawnpoints_CTF )
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_CTF_COMP, InitCTFCompSpecificSettings )
+ GameMode_AddClientInit( GAMEMODE_CTF_COMP, ClCaptureTheFlag_Init )
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_CTF_COMP, CompareCTF )
+ GameMode_AddSharedInit( GAMEMODE_CTF_COMP, GamemodeCtfDialogue_Init )
+ GameMode_AddSharedInit( GAMEMODE_CTF_COMP, CaptureTheFlagShared_Init )
+ #endif
+}
+
+void function CTFCompRegisterNetworkVars()
+{
+ if ( GAMETYPE != GAMEMODE_CTF_COMP )
+ return
+
+ // copied from the vanilla ctf remote functions
+ RegisterNetworkedVariable( "imcFlag", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "milFlag", SNDC_GLOBAL, SNVT_ENTITY )
+
+ RegisterNetworkedVariable( "imcFlagHome", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "milFlagHome", SNDC_GLOBAL, SNVT_ENTITY )
+
+ RegisterNetworkedVariable( "imcFlagState", SNDC_GLOBAL, SNVT_INT, 0 )
+ RegisterNetworkedVariable( "milFlagState", SNDC_GLOBAL, SNVT_INT, 0 )
+
+ RegisterNetworkedVariable( "flagReturnProgress", SNDC_GLOBAL, SNVT_FLOAT_RANGE_OVER_TIME, 0.0, 0.0, 1.0 )
+ RegisterNetworkedVariable( "returningFlag", SNDC_PLAYER_EXCLUSIVE, SNVT_BOOL, false )
+
+ Remote_RegisterFunction( "ServerCallback_CTF_PlayMatchNearEndMusic" )
+ Remote_RegisterFunction( "ServerCallback_CTF_StartReturnFlagProgressBar" )
+ Remote_RegisterFunction( "ServerCallback_CTF_StopReturnFlagProgressBar" )
+
+ #if CLIENT
+ CLCaptureTheFlag_RegisterNetworkFunctions()
+ #endif
+}
+
+void function InitCTFCompSpecificSettings()
+{
+ #if SERVER
+ SetShouldUsePickLoadoutScreen( true )
+ TrackTitanDamageInPlayerGameStat( PGS_DISTANCE_SCORE )
+ SetSpawnpointGamemodeOverride( CAPTURE_THE_FLAG )
+ TeamTitanSelectMenu_Init()
+ #elseif CLIENT
+ ClTeamTitanSelectMenu_Init()
+
+ // gotta register the music here because this is done hardcoded to ctf in cl_music
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_ctf_intro_flyin", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_INTRO, "music_mp_ctf_intro_flyin", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_ctf_epilogue_win", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_WIN, "music_mp_ctf_epilogue_win", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_ctf_halftime_losing", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_DRAW, "music_mp_ctf_halftime_losing", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_SUDDEN_DEATH, "music_mp_ctf_draw", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_SUDDEN_DEATH, "music_mp_ctf_draw", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_ctf_epilogue_lose", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LOSS, "music_mp_ctf_epilogue_lose", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.ROUND_BASED_GAME_WON, "music_mp_ctf_halftime_winning", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.ROUND_BASED_GAME_WON, "music_mp_ctf_halftime_winning", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.ROUND_BASED_GAME_LOST, "music_mp_ctf_halftime_losing", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.ROUND_BASED_GAME_LOST, "music_mp_ctf_halftime_losing", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "music_mp_ctf_flag_4", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.GAMEMODE_1, "music_mp_ctf_flag_4", TEAM_MILITIA )
+
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_ctf_lastminute", TEAM_IMC )
+ RegisterLevelMusicForTeam( eMusicPieceID.LEVEL_LAST_MINUTE, "music_mp_ctf_lastminute", TEAM_MILITIA )
+ #endif
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut
new file mode 100644
index 00000000..2462d537
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fastball.gnut
@@ -0,0 +1,47 @@
+global function Sh_GamemodeFastball_Init
+
+global const string GAMEMODE_FASTBALL = "fastball"
+
+void function Sh_GamemodeFastball_Init()
+{
+ // create custom gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeFastball )
+ AddCallback_OnRegisteringCustomNetworkVars( FastballRegisterNetworkVars )
+}
+
+void function CreateGamemodeFastball()
+{
+ GameMode_Create( GAMEMODE_FASTBALL )
+ GameMode_SetName( GAMEMODE_FASTBALL, "#GAMEMODE_FASTBALL" )
+ GameMode_SetDesc( GAMEMODE_FASTBALL, "#PL_fastball_desc" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_FASTBALL, "grnc_modeDesc" )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_FASTBALL, 1, 0 )
+ GameMode_SetDefaultScoreLimits( GAMEMODE_FASTBALL, 5, 0 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_FASTBALL, "#SCOREBOARD_FASTBALL_HACKS", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_FASTBALL, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_FASTBALL, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 )
+ GameMode_SetColor( GAMEMODE_FASTBALL, [147, 204, 57, 255] )
+
+ AddPrivateMatchMode( GAMEMODE_FASTBALL ) // add to private lobby modes
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_FASTBALL, GamemodeFastball_Init )
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_FASTBALL, RateSpawnpoints_Generic )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_FASTBALL, RateSpawnpoints_Generic )
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_FASTBALL, ClGamemodeFastball_Init )
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_FASTBALL, CompareAssaultScore )
+ #endif
+}
+
+void function FastballRegisterNetworkVars()
+{
+ if ( GAMETYPE != GAMEMODE_FASTBALL )
+ return
+
+ Remote_RegisterFunction( "ServerCallback_FastballUpdatePanelRui" )
+ Remote_RegisterFunction( "ServerCallback_FastballPanelHacked" )
+ Remote_RegisterFunction( "ServerCallback_FastballRespawnPlayer" )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut
new file mode 100644
index 00000000..8c6e3f63
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_fw_custom.nut
@@ -0,0 +1,73 @@
+// this script only exists to create the fw gamemode
+// all client/shared gamelogic is still done in the gamemode's respective client and shared scripts
+// these scripts are shipped with the game's official build so no need to recreate these
+// their paths are gamemodes/cl_gamemode_fw.nut and gamemodes/sh_gamemode_fw.nut, respectively
+
+global function SHCreateGamemodeFW_Init
+
+void function SHCreateGamemodeFW_Init()
+{
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeFW )
+ AddCallback_OnRegisteringCustomNetworkVars( FWOnRegisteringNetworkVars )
+}
+
+void function CreateGamemodeFW()
+{
+ //entity e = CreateEntity("npc_turret_mega"); SetAISettingsWrapper( e, "npc_turret_mega_fortwar" ); e.SetOrigin(GetPlayerArray()[0].GetOrigin()); SetTeam(e,3); DispatchSpawn(e)
+
+ // we have to manually add the client/shared scripts to scripts.rson atm so we need to prevent compile errors when they aren't included
+ // best way to do this is to just ignore this whole block for now and wait until we don't have to add them manually
+
+ GameMode_Create( FORT_WAR )
+ GameMode_SetName( FORT_WAR, "#GAMEMODE_fw" )
+ GameMode_SetDesc( FORT_WAR, "#PL_fw_desc" )
+ GameMode_SetGameModeAnnouncement( FORT_WAR, "ffa_modeDesc" ) // fw lines are unfortunately not registered to faction dialogue
+
+ #if SERVER
+ //GameMode_AddServerInit( FORT_WAR, GamemodeFW_Init ) // doesn't exist yet lol
+ #elseif CLIENT
+ GameMode_AddClientInit( FORT_WAR, CLGamemodeFW_Init )
+ #endif
+ #if !UI
+ GameMode_AddSharedInit( FORT_WAR, SHGamemodeFW_Init )
+ #endif
+}
+
+void function FWOnRegisteringNetworkVars()
+{
+ if ( GAMETYPE != FORT_WAR )
+ return
+
+ RegisterNetworkedVariable( "turretSite1", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite2", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite3", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite4", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite5", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite6", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite7", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite8", SNDC_GLOBAL, SNVT_ENTITY )
+ RegisterNetworkedVariable( "turretSite9", SNDC_GLOBAL, SNVT_ENTITY )
+
+ RegisterNetworkedVariable( "turretStateFlags1", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags2", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags3", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags4", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags5", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags6", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags7", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags8", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "turretStateFlags9", SNDC_GLOBAL, SNVT_INT )
+
+ RegisterNetworkedVariable( "imcTowerThreatLevel", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "milTowerThreatLevel", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "fwCampAlertA", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "fwCampStressA", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "fwCampAlertB", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "fwCampStressB", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "fwCampAlertC", SNDC_GLOBAL, SNVT_INT )
+ RegisterNetworkedVariable( "fwCampStressC", SNDC_GLOBAL, SNVT_INT )
+
+ #if CLIENT
+ CLFortWar_RegisterNetworkFunctions()
+ #endif
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut
new file mode 100644
index 00000000..c4021a3c
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_gg.gnut
@@ -0,0 +1,165 @@
+global function Sh_GamemodeGG_Init
+global function GetGunGameWeapons
+
+global const string GAMEMODE_GG = "gg"
+
+global struct GunGameWeapon
+{
+ string weapon
+ array<string> mods
+ int offhandSlot = -1
+}
+
+struct {
+ array<GunGameWeapon> weapons
+} file
+
+void function Sh_GamemodeGG_Init()
+{
+ // create custom gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeGG )
+}
+
+void function CreateGamemodeGG()
+{
+ GameMode_Create( GAMEMODE_GG )
+ GameMode_SetName( GAMEMODE_GG, "#GAMEMODE_GG" )
+ GameMode_SetDesc( GAMEMODE_GG, "#PL_gg_desc" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_GG, "ffa_modeDesc" )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_GG, 10, 0.0 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_GG, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_GG, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_SetColor( GAMEMODE_GG, [147, 204, 57, 255] )
+
+ AddPrivateMatchMode( GAMEMODE_GG ) // add to private lobby modes
+
+ // setup guns
+
+ // smgs
+ // car
+ GunGameWeapon ggCar = { weapon = "mp_weapon_car", mods = [ "pas_run_and_gun" ], ... }
+ file.weapons.append( ggCar )
+
+ // alternator
+ GunGameWeapon ggAlternator = { weapon = "mp_weapon_alternator_smg", mods = [ "pas_run_and_gun" ], ... }
+ file.weapons.append( ggAlternator )
+
+ // volt
+ GunGameWeapon ggVolt = { weapon = "mp_weapon_hemlok_smg", ... }
+ file.weapons.append( ggVolt )
+
+
+ // rifles
+ // hemlok
+ GunGameWeapon ggHemlok = { weapon = "mp_weapon_hemlok", mods = [ ], ... }
+ file.weapons.append( ggHemlok )
+
+ // flatline
+ GunGameWeapon ggFlatline = { weapon = "mp_weapon_vinson", mods = [ "hcog" ], ... }
+ file.weapons.append( ggFlatline )
+
+ // r201
+ GunGameWeapon ggR101 = { weapon = "mp_weapon_rspn101", ... }
+ file.weapons.append( ggR101 )
+
+
+ // lmgs
+ // devotion
+ GunGameWeapon ggDevotion = { weapon = "mp_weapon_esaw", ... }
+ file.weapons.append( ggDevotion )
+
+ // l-star
+ GunGameWeapon ggLstar = { weapon = "mp_weapon_lstar", mods = [ "pas_run_and_gun" ], ... }
+ if ( RandomInt( 100 ) <= 5 )
+ ggLstar.mods.append( "rcee" ) // easter egg mod that changes the screen of the lstar
+
+ file.weapons.append( ggLstar )
+
+
+ // shotguns
+ // eva-8
+ GunGameWeapon ggEva = { weapon = "mp_weapon_shotgun", ... }
+ file.weapons.append( ggEva )
+
+ // mastiff
+ GunGameWeapon ggMastiff = { weapon = "mp_weapon_mastiff", ... }
+ file.weapons.append( ggMastiff )
+
+
+ // grenadiers
+ // softball
+ GunGameWeapon ggSoftball = { weapon = "mp_weapon_softball", ... }
+ file.weapons.append( ggSoftball )
+
+ // epg
+ GunGameWeapon ggEpg = { weapon = "mp_weapon_epg", mods = [ "jump_kit" ], ... }
+ file.weapons.append( ggEpg )
+
+
+ // primary pistols
+ // mozambique
+ GunGameWeapon ggMozam = { weapon = "mp_weapon_shotgun_pistol", mods = [ "pas_run_and_gun" ], ... }
+ file.weapons.append( ggMozam )
+
+ // wingman elite
+ GunGameWeapon ggWme = { weapon = "mp_weapon_wingman_n", mods = [ "pas_run_and_gun", "ricochet" ], ... }
+ file.weapons.append( ggWme )
+
+
+ // snipers
+ // double take
+ GunGameWeapon ggTaketake = { weapon = "mp_weapon_doubletake", ... }
+ file.weapons.append( ggTaketake )
+
+ // kraber
+ GunGameWeapon ggKraber = { weapon = "mp_weapon_sniper", mods = [ "pas_fast_ads", "ricochet" ], ... }
+ file.weapons.append( ggKraber )
+
+
+ // secondary pistols
+ // re-45
+ GunGameWeapon ggRe45 = { weapon = "mp_weapon_autopistol", mods = [ "pas_run_and_gun", "temp_sight" ], ... }
+ file.weapons.append( ggRe45 )
+
+ // p2016
+ GunGameWeapon ggP2016 = { weapon = "mp_weapon_semipistol", mods = [ "pas_run_and_gun" ], ... }
+ file.weapons.append( ggP2016 )
+
+ // wingman
+ GunGameWeapon ggWingman = { weapon = "mp_weapon_wingman", mods = [ "pas_run_and_gun" ], ... }
+ file.weapons.append( ggWingman )
+
+
+ // final/special weapons
+ // charge rifle
+ GunGameWeapon ggChargeRifle = { weapon = "mp_weapon_defender", ... }
+ file.weapons.append( ggChargeRifle )
+
+ // pulse blade
+ GunGameWeapon ggPulseBlade = { weapon = "mp_weapon_grenade_sonar", mods = [ "pas_power_cell", "amped_tacticals" ], offhandSlot = 0 }
+ file.weapons.append( ggPulseBlade )
+
+
+ // set this to the number of guns
+ GameMode_SetDefaultScoreLimits( GAMEMODE_GG, file.weapons.len(), 0 )
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_GG, GamemodeGG_Init )
+ GameMode_AddServerInit( GAMEMODE_GG, GamemodeFFAShared_Init )
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_GG, RateSpawnpoints_Generic )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_GG, RateSpawnpoints_Generic )
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_GG, ClGamemodeGG_Init )
+ GameMode_AddClientInit( GAMEMODE_GG, GamemodeFFAShared_Init )
+ GameMode_AddClientInit( GAMEMODE_GG, ClGamemodeFFA_Init )
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_GG, CompareAssaultScore )
+ GameMode_AddSharedInit( GAMEMODE_GG, GamemodeFFA_Dialogue_Init )
+ #endif
+}
+
+array<GunGameWeapon> function GetGunGameWeapons()
+{
+ return file.weapons
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut
new file mode 100644
index 00000000..bcd86378
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_inf.gnut
@@ -0,0 +1,58 @@
+global function Sh_GamemodeInfection_Init
+
+global const string GAMEMODE_INFECTION = "inf"
+global const int INFECTION_TEAM_SURVIVOR = TEAM_MILITIA
+global const int INFECTION_TEAM_INFECTED = TEAM_IMC
+
+void function Sh_GamemodeInfection_Init()
+{
+ // create custom gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeInfection )
+ AddCallback_OnRegisteringCustomNetworkVars( InfectionRegisterNetworkVars )
+}
+
+void function CreateGamemodeInfection()
+{
+ GameMode_Create( GAMEMODE_INFECTION )
+ GameMode_SetName( GAMEMODE_INFECTION, "#GAMEMODE_inf" )
+ GameMode_SetDesc( GAMEMODE_INFECTION, "#PL_inf_desc" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_INFECTION, "ffa_modeDesc" )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_INFECTION, 5, 0.0 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_INFECTION, "#SCOREBOARD_KILLS", PGS_ASSAULT_SCORE, 2 )
+ GameMode_SetColor( GAMEMODE_INFECTION, [147, 204, 57, 255] )
+
+ AddPrivateMatchMode( GAMEMODE_INFECTION ) // add to private lobby modes
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_INFECTION, GamemodeInfection_Init )
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_INFECTION, RateSpawnpoints_Generic )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_INFECTION, RateSpawnpoints_Generic )
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_INFECTION, ClGamemodeInfection_Init )
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_INFECTION, CompareAssaultScoreAndInfection )
+ #endif
+}
+
+void function InfectionRegisterNetworkVars()
+{
+ if ( GAMETYPE != GAMEMODE_INFECTION )
+ return
+
+ Remote_RegisterFunction( "ServerCallback_YouAreInfected" )
+ Remote_RegisterFunction( "ServerCallback_AnnounceFirstInfected" )
+ Remote_RegisterFunction( "ServerCallback_AnnounceLastSurvivor" )
+}
+
+int function CompareAssaultScoreAndInfection( entity a, entity b )
+{
+ // survivors should be on top, then sort by assault score
+
+ if ( a.GetTeam() == INFECTION_TEAM_INFECTED && b.GetTeam() == INFECTION_TEAM_SURVIVOR )
+ return 1
+ else if ( a.GetTeam() == INFECTION_TEAM_SURVIVOR && b.GetTeam() == INFECTION_TEAM_INFECTED )
+ return -1
+
+ return CompareAssaultScore( a, b )
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut
new file mode 100644
index 00000000..7cd91de9
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_kr.gnut
@@ -0,0 +1,55 @@
+global function Sh_GamemodeKR_Init
+
+global const string GAMEMODE_KR = "kr"
+
+void function Sh_GamemodeKR_Init()
+{
+ // create custom gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeKR )
+ AddCallback_OnRegisteringCustomNetworkVars( KRRegisterNetworkVars )
+}
+
+void function CreateGamemodeKR()
+{
+ GameMode_Create( GAMEMODE_KR )
+ GameMode_SetName( GAMEMODE_KR, "#GAMEMODE_kr" )
+ GameMode_SetDesc( GAMEMODE_KR, "#PL_kr_desc" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_KR, "ffa_modeDesc" )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_KR, 10, 0.0 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_KR, "#SCOREBOARD_KR_RECORD", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_KR, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_SetColor( GAMEMODE_KR, [147, 204, 57, 255] )
+
+ AddPrivateMatchMode( GAMEMODE_KR ) // add to private lobby modes
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_KR, GamemodeKR_Init )
+ GameMode_AddServerInit( GAMEMODE_KR, GamemodeFFAShared_Init )
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_KR, RateSpawnpoints_Generic )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_KR, RateSpawnpoints_Generic )
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_KR, ClGamemodeKR_Init )
+ GameMode_AddClientInit( GAMEMODE_KR, GamemodeFFAShared_Init )
+ GameMode_AddClientInit( GAMEMODE_KR, ClGamemodeFFA_Init )
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_KR, CompareAssaultScore )
+ GameMode_AddSharedInit( GAMEMODE_KR, GamemodeFFA_Dialogue_Init )
+ #endif
+}
+
+void function KRRegisterNetworkVars()
+{
+ if ( GAMETYPE != GAMEMODE_KR )
+ return
+
+ Remote_RegisterFunction( "ServerCallback_FlagSpawnIncoming" )
+ Remote_RegisterFunction( "ServerCallback_NewKillRacer" )
+ Remote_RegisterFunction( "ServerCallback_EndKillrace" )
+
+ RegisterNetworkedVariable( "killRaceTime", SNDC_PLAYER_EXCLUSIVE, SNVT_TIME, 0.0 )
+
+ #if CLIENT
+ RegisterNetworkedVariableChangeCallback_time( "killRaceTime", ShowTimeGainOnKill )
+ #endif
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_sbox.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_sbox.gnut
new file mode 100644
index 00000000..893d9410
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_sbox.gnut
@@ -0,0 +1,20 @@
+global function Sh_GamemodeSbox_Init
+
+global const string GAMEMODE_SBOX = "sbox"
+
+void function Sh_GamemodeSbox_Init()
+{
+ // create custom gametype
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeSbox )
+}
+
+void function CreateGamemodeSbox()
+{
+ GameMode_Create( GAMEMODE_SBOX )
+ GameMode_SetName( GAMEMODE_SBOX, "#PL_sbox" )
+ GameMode_SetDesc( GAMEMODE_SBOX, "#PL_sbox_desc" )
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_SBOX, GamemodeSbox_Init )
+ #endif
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut
new file mode 100644
index 00000000..f3fbff28
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_gamemode_tt.gnut
@@ -0,0 +1,36 @@
+global function Sh_GamemodeTT_Init
+
+global const string GAMEMODE_TT = "tt"
+
+void function Sh_GamemodeTT_Init()
+{
+ // create custom gamemode
+ AddCallback_OnCustomGamemodesInit( CreateGamemodeTT )
+}
+
+void function CreateGamemodeTT()
+{
+ GameMode_Create( GAMEMODE_TT )
+ GameMode_SetName( GAMEMODE_TT, "#GAMEMODE_TT" )
+ GameMode_SetDesc( GAMEMODE_TT, "#PL_tt_desc" )
+ GameMode_SetGameModeAnnouncement( GAMEMODE_TT, "gnrc_modeDesc" )
+ GameMode_SetDefaultScoreLimits( GAMEMODE_TT, 20, 0 )
+ GameMode_SetDefaultTimeLimits( GAMEMODE_TT, 15, 0.0 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_TT, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_TT, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 1 )
+ GameMode_AddScoreboardColumnData( GAMEMODE_TT, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 )
+ GameMode_SetColor( GAMEMODE_TT, [200, 40, 40, 255] )
+
+ AddPrivateMatchMode( GAMEMODE_TT ) // add to private lobby modes
+
+ #if SERVER
+ GameMode_AddServerInit( GAMEMODE_TT, GamemodeTT_Init )
+ GameMode_SetPilotSpawnpointsRatingFunc( GAMEMODE_TT, RateSpawnpoints_Generic )
+ GameMode_SetTitanSpawnpointsRatingFunc( GAMEMODE_TT, RateSpawnpoints_Generic )
+ #elseif CLIENT
+ GameMode_AddClientInit( GAMEMODE_TT, ClGamemodeTT_Init )
+ #endif
+ #if !UI
+ GameMode_SetScoreCompareFunc( GAMEMODE_TT, CompareAssaultScore )
+ #endif
+} \ No newline at end of file
diff --git a/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_riff_floor_is_lava.nut b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_riff_floor_is_lava.nut
new file mode 100644
index 00000000..3a8ac8e7
--- /dev/null
+++ b/Northstar.Custom/mod/scripts/vscripts/gamemodes/sh_riff_floor_is_lava.nut
@@ -0,0 +1,262 @@
+untyped
+
+global function RiffFloorIsLavaShared_Init
+
+global function GetFogHeight
+global function GetLethalFogTopTitan
+global function GetLethalFogTop
+global function GetLethalFogBottom
+global function GetVisibleFogTop
+global function GetVisibleFogBottom
+global function GetMaxTitanSpawnFogHeight
+
+global function IsEntInSafeVolume
+global function IsEntInLethalVolume
+
+struct
+{
+ float fogDepth = 64.0
+ float maxTitanSpawnFogDepth = 170.0
+ array lethalTitanVolumes
+ array lethalPilotVolumes
+ array safePilotVolumes
+ bool volumesDebug = false
+ table< string, float > lethalFogHeights
+} file
+
+function RiffFloorIsLavaShared_Init()
+{
+ switch ( GetMapName() )
+ {
+ case "mp_lagoon":
+ AddLethalTitanVolume( Vector( -45.656845, 3555.449463, 40.422455 ), Vector( 1209.944092, 5599.152832, 234.813217 ) )
+ AddLethalTitanVolume( Vector( -5232.395020, 205.406250, 0.031250 ), Vector( -777.285400, 4075.119385, 300.634771 ) )
+ AddLethalTitanVolume( Vector( -4686.448730, 4190.655273, 20.642021 ), Vector( -41.171387, 9072.019043, 200.697632 ) )
+ AddLethalTitanVolume( Vector( -7586.861328, 4072.843994, 0.031254 ), Vector( -7012.854004, 4614.723145, 302.714966 ) )
+ break
+
+ case "mp_nexus":
+ AddLethalTitanVolume( Vector( 1567.173523, -27.374023, 209.422455 ), Vector( 2516.944092, 2585.152832, 500.813217 ) )
+ AddLethalTitanVolume( Vector( -2825.766113, 5056.203125, 243.706253 ), Vector( -2255.893555, 5688.334961, 400.251160 ) )
+ AddLethalTitanVolume( Vector( -5717.068359, -349.599976, 189.669785 ), Vector( -4960.125000, 758.196350, 400.268097 ) )
+ AddLethalTitanVolume( Vector( -3292.942139, 1713.916626, 233.749817 ), Vector( -2322.137695, 3091.497070, 477.462799 ) )
+ AddLethalTitanVolume( Vector( -878.712769, -5878.528809, 71.145332 ), Vector( 338.741943, -5014.183594, 443.146179 ) )
+ AddLethalTitanVolume( Vector( -6930.957031, -1277.388550, 107.619537 ), Vector( -6574.779297, -779.338013, 685.485901 ) )
+ break
+
+ case "mp_outpost_207":
+ AddSafePilotVolume( Vector( 2359.524658, -631.065918, -256.714142 ), Vector( 2623.051270, -182.453323, -220.125641 ) )
+
+ AddLethalTitanVolume( Vector( -100.349350, 2218.763916, -330.968750 ), Vector( 2561.511230, 4030.028320, -133.065369 ) )
+ AddLethalTitanVolume( Vector( -452.031647, 282.244629, -255.968750 ), Vector( 2241.971069, 1594.146851, -100.212967 ) )
+ break
+
+ case "mp_training_ground":
+ AddSafePilotVolume( Vector( -2618.053223, -3435.505615, 40.215054 ), Vector( -2309.167236, -3321.788330, 146.218491 ) )
+ AddSafePilotVolume( Vector( -3187.767090, -2886.333496, 45.746925 ), Vector( -2865.753174, -2681.679443, 109.089279 ) )
+ AddSafePilotVolume( Vector( -3717.815674, -2350.831543, 47.694588 ), Vector( -3431.980957, -2145.194092, 120.640717 ) )
+
+ AddLethalTitanVolume( Vector( -3439.702179, -2227.359741, -8.036909 ), Vector( 2185.765076, 2384.459412, 225.199013 ) )
+ AddLethalTitanVolume( Vector( -3200.747681, -4456.148926, 0.0 ), Vector( -1261.621826, -3000.667480, 160.689011 ) )
+ AddLethalTitanVolume( Vector( 1261.621826, 3000.667480, 0.0 ), Vector( 2700.747681, 4456.148926, 160.689011 ) )
+ AddLethalTitanVolume( Vector( -3291.510986, 3483.724609, 4.031250 ), Vector( -2018.871826, 4463.995850, 122.675621 ) )
+ AddLethalTitanVolume( Vector( 2018.871826, -3638.995850, 4.031250 ), Vector( 2241.510986, -3483.724609, 122.675621 ) )
+ AddLethalTitanVolume( Vector( -2798.816528, -2302.519897, -30.285933 ), Vector( -1561.589355, -791.616699, 300.917297 ) )
+ AddLethalTitanVolume( Vector( 3809.276123, 1639.001587, 11.272846 ), Vector( 4056.847412, 1862.587036, 100.205643 ) )
+ AddLethalTitanVolume( Vector( -4189.979492, -3298.505127, -5.597572 ), Vector( -3398.622803, -560.027344, 147.054291 ) )
+ break
+
+ case "mp_runoff":
+ AddLethalPilotVolume( Vector( -621.502319, -5743.472656, 299.838928 ), Vector( -397.317047, -5578.512207, 425.437927 ) )
+ break
+ }
+}
+
+float function GetFogHeight()
+{
+ string mapName = GetMapName()
+
+ file.lethalFogHeights = {}
+ file.lethalFogHeights[ "mp_angel_city" ] <- 216.0 // cp ctf mfd
+ file.lethalFogHeights[ "mp_lagoon" ] <- 98.0 // cp mfd
+ file.lethalFogHeights[ "mp_nexus" ] <- 310.0 // mfd
+ file.lethalFogHeights[ "mp_o2" ] <- 40.0 // mfd
+ file.lethalFogHeights[ "mp_outpost_207" ] <- -225.0 // mfd
+ file.lethalFogHeights[ "mp_training_ground" ] <- 80.0 // cp mfd
+ file.lethalFogHeights[ "mp_harmony_mines" ] <- 260.0 // cp ctf mfd
+ file.lethalFogHeights[ "mp_haven" ] <- 128.0 // mfd
+
+ // good map, needs spawns, etc...
+ file.lethalFogHeights[ "mp_rise" ] <- 420.0 // mfd
+ file.lethalFogHeights[ "mp_runoff" ] <- 340.0 // mfd
+ file.lethalFogHeights[ "mp_zone_18" ] <- 460.0 // mfd
+ file.lethalFogHeights[ "mp_sandtrap" ] <- 64.0
+
+ // these don't work as well
+ file.lethalFogHeights[ "mp_swampland" ] <- 350.0 // mfd
+ file.lethalFogHeights[ "mp_backwater" ] <- 320.0 // mfd
+ file.lethalFogHeights[ "mp_airbase" ] <- 450.0
+ file.lethalFogHeights[ "mp_boneyard" ] <- 64.0
+ file.lethalFogHeights[ "mp_colony" ] <- 270.0
+ file.lethalFogHeights[ "mp_corporate" ] <- -765.0
+ file.lethalFogHeights[ "mp_fracture" ] <- 270.0
+ file.lethalFogHeights[ "mp_overlook" ] <- 16.0
+ file.lethalFogHeights[ "mp_relic" ] <- 475.0
+ file.lethalFogHeights[ "mp_smugglers_cove" ] <- 400.0
+ file.lethalFogHeights[ "mp_wargames" ] <- 64.0
+ file.lethalFogHeights[ "mp_switchback" ] <- 840.0
+
+ file.lethalFogHeights[ "mp_chin_rodeo_express" ] <- 1580.0
+
+ // custom: titanfall 2 maps
+ // TODO: really need a modular system here
+ file.lethalFogHeights[ "mp_colony02" ] <- 270.0 // map changed name from tf1 => tf2
+ file.lethalFogHeights[ "mp_glitch" ] <- 200.0
+ file.lethalFogHeights[ "mp_grave" ] <- 2350.0
+ file.lethalFogHeights[ "mp_homestead" ] <- 64.0
+ file.lethalFogHeights[ "mp_forwardbase_kodai" ] <- 930.0
+ file.lethalFogHeights[ "mp_thaw" ] <- 32.0
+ file.lethalFogHeights[ "mp_black_water_canal" ] <- 32.0
+ file.lethalFogHeights[ "mp_eden" ] <- 175.0
+ file.lethalFogHeights[ "mp_drydock" ] <- 300.0
+ file.lethalFogHeights[ "mp_crashsite3" ] <- 800.0 // crashsite is just as awful for this as it is for anything else
+ file.lethalFogHeights[ "mp_complex3" ] <- 630.0
+ file.lethalFogHeights[ "mp_relic02" ] <- 250.0 // not great, tf1's would honestly be worse though imo
+
+ // lf maps: overall a bit hit or miss, many likely have spawn issues
+ file.lethalFogHeights[ "mp_lf_stacks" ] <- -9999.0 // entirely nonworking, breaks spawns no matter what from what i can tell, could potentially use safe zones for this?
+ file.lethalFogHeights[ "mp_lf_deck" ] <- -9999.0 // nonworking fogcontroller so fog is invisible
+ file.lethalFogHeights[ "mp_lf_uma" ] <- 64.0
+ file.lethalFogHeights[ "mp_lf_meadow" ] <- 64.0
+ file.lethalFogHeights[ "mp_lf_traffic" ] <- 50.0
+ file.lethalFogHeights[ "mp_lf_township" ] <- 64.0
+
+ if ( mapName in file.lethalFogHeights )
+ return file.lethalFogHeights[ mapName ]
+
+ return 64.0
+}
+
+float function GetLethalFogTopTitan()
+{
+ float fogTop = GetLethalFogTop()
+
+ switch ( GetMapName() )
+ {
+ case "mp_lagoon":
+ case "mp_nexus":
+ case "mp_outpost_207":
+ case "mp_training_ground":
+ case "mp_chin_rodeo_express":
+ return fogTop
+ }
+
+ return fogTop + 256.0
+}
+
+float function GetLethalFogTop()
+{
+ return GetFogHeight() - file.fogDepth * 0.2
+}
+
+float function GetLethalFogBottom()
+{
+ return GetFogHeight() - file.fogDepth * 0.7
+}
+
+float function GetVisibleFogTop()
+{
+ return GetFogHeight() + file.fogDepth * 0.5
+}
+
+float function GetVisibleFogBottom()
+{
+ return GetFogHeight() - file.fogDepth * 0.5
+}
+
+float function GetMaxTitanSpawnFogHeight()
+{
+ return GetFogHeight() - file.maxTitanSpawnFogDepth
+}
+
+function AddLethalTitanVolume( vector volumeMins, vector volumeMaxs )
+{
+ Assert( volumeMins.x < volumeMaxs.x )
+ Assert( volumeMins.y < volumeMaxs.y )
+ Assert( volumeMins.z < volumeMaxs.z )
+
+ file.lethalTitanVolumes.append( { mins = volumeMins, maxs = volumeMaxs } )
+}
+
+function AddLethalPilotVolume( vector volumeMins, vector volumeMaxs )
+{
+ Assert( volumeMins.x < volumeMaxs.x )
+ Assert( volumeMins.y < volumeMaxs.y )
+ Assert( volumeMins.z < volumeMaxs.z )
+
+ file.lethalPilotVolumes.append( { mins = volumeMins, maxs = volumeMaxs } )
+}
+
+function AddSafePilotVolume( vector volumeMins, vector volumeMaxs )
+{
+ Assert( volumeMins.x < volumeMaxs.x )
+ Assert( volumeMins.y < volumeMaxs.y )
+ Assert( volumeMins.z < volumeMaxs.z )
+
+ file.safePilotVolumes.append( { mins = volumeMins, maxs = volumeMaxs } )
+}
+
+function IsEntInSafeVolume( entity ent )
+{
+ if ( ent.IsPlayer() )
+ {
+ foreach ( volume in file.safePilotVolumes )
+ {
+ vector entOrg = ent.GetOrigin()
+
+ #if SERVER
+ if ( file.volumesDebug )
+ DebugDrawBox( Vector( 0.0, 0.0, 0.0 ), volume.mins, volume.maxs, 0, 0, 255, 1, 0.1 )
+ #endif
+
+ if ( PointIsWithinBounds( entOrg, expect vector( volume.mins ), expect vector( volume.maxs ) ) )
+ return true
+ }
+ }
+}
+
+function IsEntInLethalVolume( entity ent )
+{
+ if ( ent.IsTitan() )
+ {
+ foreach ( volume in file.lethalTitanVolumes )
+ {
+ vector entOrg = ent.GetOrigin()
+
+ #if SERVER
+ if ( file.volumesDebug )
+ DebugDrawBox( Vector( 0.0, 0.0, 0.0 ), volume.mins, volume.maxs, 255, 255, 0, 1, 0.1 )
+ #endif
+
+ if ( PointIsWithinBounds( entOrg, expect vector( volume.mins ), expect vector( volume.maxs ) ) )
+ return true
+ }
+ }
+ else if ( ent.IsPlayer() )
+ {
+ foreach ( volume in file.lethalPilotVolumes )
+ {
+ vector entOrg = ent.GetOrigin()
+
+ #if SERVER
+ if ( file.volumesDebug )
+ DebugDrawBox( Vector( 0.0, 0.0, 0.0 ), volume.mins, volume.maxs, 255, 255, 0, 1, 0.1 )
+ #endif
+
+ if ( PointIsWithinBounds( entOrg, expect vector( volume.mins ), expect vector( volume.maxs ) ) )
+ return true
+ }
+ }
+
+ return false
+}