aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-06-22 14:30:49 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-06-22 14:30:49 +0100
commit207facbc402f5639cbcd31f079214351ef605cf2 (patch)
tree4710b2a88dd64f3dfea1609d31a5de9141640951 /Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut
parentc2d438568df6d98cf731807e30eaa7da31e5ea52 (diff)
downloadNorthstarMods-207facbc402f5639cbcd31f079214351ef605cf2.tar.gz
NorthstarMods-207facbc402f5639cbcd31f079214351ef605cf2.zip
initial commit after moving to new repo
Diffstat (limited to 'Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut')
-rw-r--r--Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut512
1 files changed, 512 insertions, 0 deletions
diff --git a/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut
new file mode 100644
index 000000000..e710a9118
--- /dev/null
+++ b/Northstar.CustomServers/scripts/vscripts/gamemodes/_gamemode_ctf.nut
@@ -0,0 +1,512 @@
+untyped
+// this needs a refactor lol
+
+global function CaptureTheFlag_Init
+global function RateSpawnpoints_CTF
+
+const array<string> SWAP_FLAG_MAPS = [
+ "mp_forwardbase_kodai",
+ "mp_lf_meadow"
+]
+
+struct {
+ entity imcFlagSpawn
+ entity imcFlag
+ entity imcFlagReturnTrigger
+
+ entity militiaFlagSpawn
+ entity militiaFlag
+ entity militiaFlagReturnTrigger
+
+ array<entity> imcCaptureAssistList
+ array<entity> militiaCaptureAssistList
+} file
+
+void function CaptureTheFlag_Init()
+{
+ PrecacheModel( CTF_FLAG_MODEL )
+ PrecacheModel( CTF_FLAG_BASE_MODEL )
+
+ CaptureTheFlagShared_Init()
+ SetSwitchSidesBased( true )
+ SetSuddenDeathBased( true )
+ //SetSpawnsUseFrontline( true )
+
+ AddCallback_OnClientConnected( CTFInitPlayer )
+
+ AddCallback_GameStateEnter( eGameState.Prematch, CreateFlags )
+ AddCallback_OnTouchHealthKit( "item_flag", OnFlagCollected )
+ AddCallback_OnPlayerKilled( OnPlayerKilled )
+ AddCallback_OnPilotBecomesTitan( DropFlagForBecomingTitan )
+
+ RegisterSignal( "FlagReturnEnded" )
+ RegisterSignal( "ResetDropTimeout" )
+
+ // setup stuff for the functions in sh_gamemode_ctf
+ // don't really like using level for stuff but just how it be
+ level.teamFlags <- {}
+
+ // setup score event earnmeter values
+ ScoreEvent_SetEarnMeterValues( "KillPilot", 0.05, 0.20 )
+ ScoreEvent_SetEarnMeterValues( "Headshot", 0.0, 0.02 )
+ ScoreEvent_SetEarnMeterValues( "FirstStrike", 0.0, 0.05 )
+ ScoreEvent_SetEarnMeterValues( "KillTitan", 0.0, 0.25 )
+ ScoreEvent_SetEarnMeterValues( "PilotBatteryStolen", 0.0, 0.35 )
+
+ ScoreEvent_SetEarnMeterValues( "FlagCarrierKill", 0.0, 0.20 )
+ ScoreEvent_SetEarnMeterValues( "FlagTaken", 0.0, 0.10 )
+ ScoreEvent_SetEarnMeterValues( "FlagCapture", 0.0, 0.30 )
+ ScoreEvent_SetEarnMeterValues( "FlagCaptureAssist", 0.0, 0.20 )
+ ScoreEvent_SetEarnMeterValues( "FlagReturn", 0.0, 0.20 )
+}
+
+void function RateSpawnpoints_CTF( int checkClass, array<entity> spawnpoints, int team, entity player )
+{
+ // ok this is the 3rd time rewriting this due to not understanding ctf spawns properly
+ // legit just
+ // if there are no enemies in base, spawn them in base
+ // if there are, spawn them outside of it ( but ideally still close )
+ // max distance away should be like, angel city markets
+
+ array<entity> startSpawns = SpawnPoints_GetPilotStart( team )
+ array<entity> enemyPlayers = GetPlayerArrayOfTeam_Alive( GetOtherTeam( team ) )
+
+ vector startSpawnAverage
+ bool enemyInBase = false
+ foreach ( entity startSpawn in startSpawns )
+ {
+ startSpawnAverage += startSpawn.GetOrigin()
+
+ foreach ( entity enemy in enemyPlayers )
+ {
+ if ( Distance( startSpawn.GetOrigin(), enemy.GetOrigin() ) <= 1000.0 )
+ {
+ enemyInBase = true
+ break
+ }
+ }
+ }
+
+ startSpawnAverage /= startSpawns.len()
+
+ print( "spawn for " + player + " is there an enemy in base?" + enemyInBase )
+
+ foreach ( entity spawn in spawnpoints )
+ {
+ float rating = 0.0
+
+ bool isStart = false
+ foreach ( entity startSpawn in startSpawns )
+ {
+ if ( Distance2D( spawn.GetOrigin(), startSpawn.GetOrigin() ) < 1500.0 ) // this was for some reason the only distance i could get to work
+ {
+ isStart = true
+ break
+ }
+ }
+
+ if ( isStart )
+ {
+ if ( !enemyInBase )
+ rating = 1000 + RandomFloat( 100.0 )
+ else
+ rating = -1000.0
+ }
+ else if ( !isStart && enemyInBase )
+ {
+ entity friendlyFlag
+ entity enemyFlag
+ if ( team == TEAM_IMC )
+ {
+ friendlyFlag = file.imcFlagSpawn
+ enemyFlag = file.militiaFlagSpawn
+ }
+ else
+ {
+ friendlyFlag = file.militiaFlagSpawn
+ enemyFlag = file.imcFlagSpawn
+ }
+
+ float dist = Distance2D( spawn.GetOrigin(), enemyFlag.GetOrigin() )
+ float flagDist = Distance2D( startSpawnAverage, enemyFlag.GetOrigin() )
+
+ if ( dist < ( flagDist / 2 ) ) // spawns shouldn't be closer to enemies than they are to us
+ rating = -1000.0
+ if ( dist > flagDist * 1.1 ) // spawn is behind startspawns
+ rating = -1000.0
+ else
+ {
+ rating = dist // closer spawns are better
+
+ foreach( entity enemy in enemyPlayers ) // reduce rating if enemies are near by
+ if ( Distance( enemy.GetOrigin(), spawn.GetOrigin() ) < 500.0 )
+ rating /= 2
+ }
+ }
+
+ spawn.CalculateRating( checkClass, team, rating, rating )
+ }
+}
+
+void function CTFInitPlayer( entity player )
+{
+ if ( !IsValid( file.imcFlagSpawn ) )
+ return
+
+ vector imcSpawn = file.imcFlagSpawn.GetOrigin()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_IMC, imcSpawn.x, imcSpawn.y, imcSpawn.z )
+
+ vector militiaSpawn = file.militiaFlagSpawn.GetOrigin()
+ Remote_CallFunction_NonReplay( player, "ServerCallback_SetFlagHomeOrigin", TEAM_MILITIA, militiaSpawn.x, militiaSpawn.y, militiaSpawn.z )
+}
+
+void function OnPlayerKilled( entity victim, entity attacker, var damageInfo )
+{
+ if ( GetFlagForTeam( GetOtherTeam( victim.GetTeam() ) ).GetParent() == victim )
+ {
+ if ( victim != attacker && attacker.IsPlayer() )
+ AddPlayerScore( attacker, "FlagCarrierKill", victim )
+
+ DropFlag( victim )
+ }
+}
+
+void function CreateFlags()
+{
+ if ( IsValid( file.imcFlagSpawn ) )
+ {
+ file.imcFlagSpawn.Destroy()
+ file.imcFlag.Destroy()
+ file.imcFlagReturnTrigger.Destroy()
+
+ file.militiaFlagSpawn.Destroy()
+ file.militiaFlag.Destroy()
+ file.militiaFlagReturnTrigger.Destroy()
+ }
+
+ foreach ( entity spawn in GetEntArrayByClass_Expensive( "info_spawnpoint_flag" ) )
+ {
+ // on some maps flags are on the opposite side from what they should be
+ // likely this is because respawn uses distance checks from spawns to check this in official
+ // but i don't like doing that so just using a list of maps to swap them on lol
+ bool switchedSides = HasSwitchedSides() == 1
+ bool shouldSwap = SWAP_FLAG_MAPS.contains( GetMapName() ) ? !switchedSides : switchedSides
+
+ int flagTeam = spawn.GetTeam()
+ if ( shouldSwap )
+ {
+ flagTeam = GetOtherTeam( flagTeam )
+ SetTeam( spawn, flagTeam )
+ }
+
+ // create flag base
+ entity base = CreatePropDynamic( CTF_FLAG_BASE_MODEL, spawn.GetOrigin(), spawn.GetAngles(), 0 )
+ SetTeam( base, spawn.GetTeam() )
+ svGlobal.flagSpawnPoints[ flagTeam ] = base
+
+ // create flag
+ entity flag = CreateEntity( "item_flag" )
+ flag.SetValueForModelKey( CTF_FLAG_MODEL )
+ SetTeam( flag, flagTeam )
+ flag.MarkAsNonMovingAttachment()
+ DispatchSpawn( flag )
+ flag.SetModel( CTF_FLAG_MODEL )
+ flag.SetOrigin( spawn.GetOrigin() + < 0, 0, base.GetBoundingMaxs().z * 2 > ) // ensure flag doesn't spawn clipped into geometry
+ flag.SetVelocity( < 0, 0, 1 > )
+
+ flag.s.canTake <- true
+ flag.s.playersReturning <- []
+
+ level.teamFlags[ flag.GetTeam() ] <- flag
+
+ entity returnTrigger = CreateEntity( "trigger_cylinder" )
+ SetTeam( returnTrigger, flagTeam )
+ returnTrigger.SetRadius( CTF_GetFlagReturnRadius() )
+ returnTrigger.SetAboveHeight( CTF_GetFlagReturnRadius() )
+ returnTrigger.SetBelowHeight( CTF_GetFlagReturnRadius() )
+
+ returnTrigger.SetEnterCallback( OnPlayerEntersFlagReturnTrigger )
+ returnTrigger.SetLeaveCallback( OnPlayerExitsFlagReturnTrigger )
+
+ DispatchSpawn( returnTrigger )
+
+ thread TrackFlagReturnTrigger( flag, returnTrigger )
+
+ if ( flagTeam == TEAM_IMC )
+ {
+ file.imcFlagSpawn = base
+ file.imcFlag = flag
+ file.imcFlagReturnTrigger = returnTrigger
+
+ SetGlobalNetEnt( "imcFlag", file.imcFlag )
+ SetGlobalNetEnt( "imcFlagHome", file.imcFlagSpawn )
+ }
+ else
+ {
+ file.militiaFlagSpawn = base
+ file.militiaFlag = flag
+ file.militiaFlagReturnTrigger = returnTrigger
+
+ SetGlobalNetEnt( "milFlag", file.militiaFlag )
+ SetGlobalNetEnt( "milFlagHome", file.militiaFlagSpawn )
+ }
+ }
+
+ foreach ( entity player in GetPlayerArray() )
+ CTFInitPlayer( player )
+}
+
+void function TrackFlagReturnTrigger( entity flag, entity returnTrigger )
+{
+ // this is a bit of a hack, it seems parenting the return trigger to the flag actually sets the pickup radius of the flag to be the same as the trigger
+ // this isn't wanted since only pickups should use that additional radius
+ flag.EndSignal( "OnDestroy" )
+
+ while ( true )
+ {
+ returnTrigger.SetOrigin( flag.GetOrigin() )
+ WaitFrame()
+ }
+}
+
+void function SetFlagStateForTeam( int team, int state )
+{
+ if ( state == eFlagState.Away ) // we tell the client the flag is the player carrying it if they're carrying it
+ SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", ( team == TEAM_IMC ? file.imcFlag : file.militiaFlag ).GetParent() )
+ else
+ SetGlobalNetEnt( team == TEAM_IMC ? "imcFlag" : "milFlag", team == TEAM_IMC ? file.imcFlag : file.militiaFlag )
+
+ SetGlobalNetInt( team == TEAM_IMC ? "imcFlagState" : "milFlagState", state )
+}
+
+bool function OnFlagCollected( entity player, entity flag )
+{
+ if ( !IsAlive( player ) || flag.GetParent() != null || player.IsTitan() || player.IsPhaseShifted() )
+ return false
+
+ if ( player.GetTeam() != flag.GetTeam() && flag.s.canTake )
+ GiveFlag( player, flag ) // pickup enemy flag
+ else if ( player.GetTeam() == flag.GetTeam() && IsFlagHome( flag ) && PlayerHasEnemyFlag( player ) )
+ CaptureFlag( player, GetFlagForTeam( GetOtherTeam( flag.GetTeam() ) ) ) // cap the flag
+
+ return false // don't wanna delete the flag entity
+}
+
+void function GiveFlag( entity player, entity flag )
+{
+ print( player + " picked up the flag!" )
+ flag.Signal( "ResetDropTimeout" )
+
+ flag.SetParent( player, "FLAG" )
+ thread DropFlagIfPhased( player, flag )
+
+ // do notifications
+ MessageToPlayer( player, eEventNotifications.YouHaveTheEnemyFlag )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_GrabFlag" )
+ AddPlayerScore( player, "FlagTaken", player )
+ PlayFactionDialogueToPlayer( "ctf_flagPickupYou", player )
+
+ MessageToTeam( player.GetTeam(), eEventNotifications.PlayerHasEnemyFlag, player, player )
+ EmitSoundOnEntityToTeamExceptPlayer( flag, "UI_CTF_3P_TeamGrabFlag", player.GetTeam(), player )
+ PlayFactionDialogueToTeamExceptPlayer( "ctf_flagPickupFriendly", player.GetTeam(), player )
+
+ MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerHasFriendlyFlag, player, player )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_EnemyGrabFlag", flag.GetTeam() )
+
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.Away ) // used for held
+}
+
+void function DropFlagIfPhased( entity player, entity flag )
+{
+ player.EndSignal( "StartPhaseShift" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ DropFlag( player, true )
+ })
+
+ while( flag.GetParent() == player )
+ WaitFrame()
+}
+
+void function DropFlagForBecomingTitan( entity pilot, entity titan )
+{
+ DropFlag( pilot, true )
+}
+
+void function DropFlag( entity player, bool realDrop = true )
+{
+ entity flag = GetFlagForTeam( GetOtherTeam( player.GetTeam() ) )
+
+ if ( flag.GetParent() != player )
+ return
+
+ print( player + " dropped the flag!" )
+
+ flag.ClearParent()
+ flag.SetAngles( < 0, 0, 0 > )
+ flag.SetVelocity( < 0, 0, 0 > )
+
+ if ( realDrop )
+ {
+ // start drop timeout countdown
+ thread TrackFlagDropTimeout( flag )
+
+ // add to capture assists
+ if ( player.GetTeam() == TEAM_IMC )
+ file.imcCaptureAssistList.append( player )
+ else
+ file.militiaCaptureAssistList.append( player )
+
+ // do notifications
+ MessageToPlayer( player, eEventNotifications.YouDroppedTheEnemyFlag )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagDrop" )
+
+ MessageToTeam( player.GetTeam(), eEventNotifications.PlayerDroppedEnemyFlag, player, player )
+ // todo need a sound here maybe
+
+ MessageToTeam( GetOtherTeam( player.GetTeam() ), eEventNotifications.PlayerDroppedFriendlyFlag, player, player )
+ // todo need a sound here maybe
+ }
+
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.Home ) // used for return prompt
+}
+
+void function TrackFlagDropTimeout( entity flag )
+{
+ flag.EndSignal( "ResetDropTimeout" )
+
+ wait CTF_GetDropTimeout()
+
+ ResetFlag( flag )
+}
+
+void function ResetFlag( entity flag )
+{
+ // ensure we can't pickup the flag after it's been dropped but before it's been reset
+ flag.s.canTake = false
+
+ if ( flag.GetParent() != null )
+ DropFlag( flag.GetParent(), false )
+
+ entity spawn
+ if ( flag.GetTeam() == TEAM_IMC )
+ spawn = file.imcFlagSpawn
+ else
+ spawn = file.militiaFlagSpawn
+
+ flag.SetOrigin( spawn.GetOrigin() + < 0, 0, spawn.GetBoundingMaxs().z + 1 > )
+
+ // we can take it again now
+ flag.s.canTake = true
+
+ SetFlagStateForTeam( flag.GetTeam(), eFlagState.None ) // used for home
+
+ flag.Signal( "ResetDropTimeout" )
+}
+
+void function CaptureFlag( entity player, entity flag )
+{
+ // reset flag
+ ResetFlag( flag )
+
+ print( player + " captured the flag!" )
+
+ // score
+ int team = player.GetTeam()
+ AddTeamScore( team, 1 )
+ AddPlayerScore( player, "FlagCapture", player )
+ player.AddToPlayerGameStat( PGS_ASSAULT_SCORE, 1 ) // add 1 to captures on scoreboard
+
+ array<entity> assistList
+ if ( player.GetTeam() == TEAM_IMC )
+ assistList = file.imcCaptureAssistList
+ else
+ assistList = file.militiaCaptureAssistList
+
+ foreach( entity assistPlayer in assistList )
+ if ( player != assistPlayer )
+ AddPlayerScore( assistPlayer, "FlagCaptureAssist", player )
+
+ assistList.clear()
+
+ // notifs
+ MessageToPlayer( player, eEventNotifications.YouCapturedTheEnemyFlag )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_PlayerScore" )
+
+ MessageToTeam( team, eEventNotifications.PlayerCapturedEnemyFlag, player, player )
+ EmitSoundOnEntityToTeamExceptPlayer( flag, "UI_CTF_3P_TeamScore", player.GetTeam(), player )
+
+ MessageToTeam( GetOtherTeam( team ), eEventNotifications.PlayerCapturedFriendlyFlag, player, player )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyScore", flag.GetTeam() )
+
+ if ( GameRules_GetTeamScore( team ) == GameMode_GetRoundScoreLimit( GAMETYPE ) - 1 )
+ {
+ PlayFactionDialogueToTeam( "ctf_notifyWin1more", team )
+ PlayFactionDialogueToTeam( "ctf_notifyLose1more", GetOtherTeam( team ) )
+ }
+}
+
+void function OnPlayerEntersFlagReturnTrigger( entity trigger, entity player )
+{
+ entity flag
+ if ( trigger.GetTeam() == TEAM_IMC )
+ flag = file.imcFlag
+ else
+ flag = file.militiaFlag
+
+ if ( !player.IsPlayer() || !player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
+ return
+
+ thread TryReturnFlag( player, flag )
+}
+
+void function OnPlayerExitsFlagReturnTrigger( entity trigger, entity player )
+{
+ entity flag
+ if ( trigger.GetTeam() == TEAM_IMC )
+ flag = file.imcFlag
+ else
+ flag = file.militiaFlag
+
+ if ( !player.IsPlayer() || !player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null )
+ return
+
+ player.Signal( "FlagReturnEnded" )
+}
+
+void function TryReturnFlag( entity player, entity flag )
+{
+ // start return progress bar
+ Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StartReturnFlagProgressBar", Time() + CTF_GetFlagReturnTime() )
+ EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagReturnMeter" )
+
+ OnThreadEnd( function() : ( player )
+ {
+ // cleanup
+ Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StopReturnFlagProgressBar" )
+ StopSoundOnEntity( player, "UI_CTF_1P_FlagReturnMeter" )
+ })
+
+ player.EndSignal( "FlagReturnEnded" )
+ player.EndSignal( "OnDeath" )
+
+ wait CTF_GetFlagReturnTime()
+
+ // flag return succeeded
+ // return flag
+ ResetFlag( flag )
+
+ // do notifications for return
+ MessageToPlayer( player, eEventNotifications.YouReturnedFriendlyFlag )
+ AddPlayerScore( player, "FlagReturn", player )
+ player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, 1 )
+
+ MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerReturnedFriendlyFlag, null, player )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_TeamReturnsFlag", flag.GetTeam() )
+ PlayFactionDialogueToTeam( "ctf_flagReturnedFriendly", flag.GetTeam() )
+
+ MessageToTeam( GetOtherTeam( flag.GetTeam() ), eEventNotifications.PlayerReturnedEnemyFlag, null, player )
+ EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyReturnsFlag", GetOtherTeam( flag.GetTeam() ) )
+ PlayFactionDialogueToTeam( "ctf_flagReturnedEnemy", GetOtherTeam( flag.GetTeam() ) )
+} \ No newline at end of file