diff options
Diffstat (limited to 'Northstar.CustomServers/mod/scripts/vscripts/gamemodes')
24 files changed, 2818 insertions, 0 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_frontline.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_frontline.gnut new file mode 100644 index 00000000..37b89169 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_frontline.gnut @@ -0,0 +1 @@ +//fuck
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut new file mode 100644 index 00000000..cf7f7e15 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_ai_gamemodes.gnut @@ -0,0 +1,6 @@ +global function AiGameModes_Init + +void function AiGameModes_Init() +{ + +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_capture_point.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_capture_point.gnut new file mode 100644 index 00000000..e02157d1 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_capture_point.gnut @@ -0,0 +1 @@ +// not using this, everything is just in _hardpoints instead lol
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_featured_mode_settings.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_featured_mode_settings.gnut new file mode 100644 index 00000000..090814cb --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_featured_mode_settings.gnut @@ -0,0 +1,125 @@ +untyped +global function FeaturedModeSettings_Init + +void function FeaturedModeSettings_Init() +{ + // if it's not super obvious at a glance this script is used for playlist vars with the prefix "featured_mode_" + // these often set loadouts and shit so they need a script + // note: for turbo_titans, the core multiplier is set in playlist + + AddCallback_OnPlayerRespawned( FeaturedModeSettingsSetupPilotLoadouts ) + AddCallback_OnPilotBecomesTitan( FeaturedModeSettingsSetupTitanLoadouts ) +} + +bool function IsFeaturedMode( string modeName ) +{ + return GetCurrentPlaylistVar( "featured_mode_" + modeName ) == "1" +} + +void function FeaturedModeSettingsSetupPilotLoadouts( entity player ) +{ + bool shouldChangeLoadout = false + + // create loadout struct + PilotLoadoutDef modifiedLoadout = clone GetActivePilotLoadout( player ) + + if ( IsFeaturedMode( "all_holopilot" ) ) + { + shouldChangeLoadout = true + + modifiedLoadout.special = "mp_ability_holopilot" + } + + if ( IsFeaturedMode( "all_grapple" ) ) + { + shouldChangeLoadout = true + + modifiedLoadout.special = "mp_ability_grapple" + modifiedLoadout.specialMods = [ "all_grapple" ] + } + + if ( IsFeaturedMode( "all_phase" ) ) + { + shouldChangeLoadout = true + + modifiedLoadout.special = "mp_ability_shifter" + modifiedLoadout.specialMods = [ "all_phase" ] + } + + if ( IsFeaturedMode( "all_ticks" ) ) + { + shouldChangeLoadout = true + + modifiedLoadout.ordnance = "mp_weapon_frag_drone" + modifiedLoadout.ordnanceMods = [ "all_ticks" ] + } + + if ( IsFeaturedMode( "rocket_arena" ) ) + { + // this crashes sometimes for some reason + + shouldChangeLoadout = true + + modifiedLoadout.primary = "mp_weapon_epg" + modifiedLoadout.primaryMods = [ "rocket_arena" ] + + // set secondary to whatever one is pistol + if ( GetWeaponInfoFileKeyField_Global( player.GetMainWeapons()[ 1 ].GetWeaponClassName(), "menu_category" ) == "at" ) + { + modifiedLoadout.weapon3 = "mp_weapon_autopistol" + modifiedLoadout.weapon3Mods = [ "rocket_arena" ] + } + else + { + modifiedLoadout.secondary = "mp_weapon_autopistol" + modifiedLoadout.secondaryMods = [ "rocket_arena" ] + } + + player.GiveExtraWeaponMod( "rocket_arena" ) + } + + if ( IsFeaturedMode( "shotguns_snipers" ) ) + { + + shouldChangeLoadout = true + + // this one was never released, assuming it just gives you a mastiff and a kraber with quick swap + modifiedLoadout.primary = "mp_weapon_sniper" + modifiedLoadout.primaryMods = [ "pas_fast_swap", "pas_fast_ads" ] + + // set secondary to whatever one is pistol + if ( GetWeaponInfoFileKeyField_Global( player.GetMainWeapons()[ 1 ].GetWeaponClassName(), "menu_category" ) == "at" ) + { + modifiedLoadout.weapon3 = "mp_weapon_mastiff" + modifiedLoadout.weapon3Mods = [ "pas_fast_swap", "pas_run_and_gun" ] + } + else + { + modifiedLoadout.secondary = "mp_weapon_mastiff" + modifiedLoadout.secondaryMods = [ "pas_fast_swap", "pas_run_and_gun" ] + } + } + + // dont wanna give a new loadout if it's not necessary, could break other callbacks + if ( shouldChangeLoadout ) + GivePilotLoadout( player, modifiedLoadout ) + + if ( IsFeaturedMode( "tactikill" ) ) + player.GiveExtraWeaponMod( "tactical_cdr_on_kill" ) + + if ( IsFeaturedMode( "amped_tacticals" ) ) + player.GiveExtraWeaponMod( "amped_tacticals" ) +} + +void function FeaturedModeSettingsSetupTitanLoadouts( entity player, entity titan ) +{ + // this doesn't work atm, figure out how it should work and fix at some point + entity soul = player.GetTitanSoul() + if ( IsFeaturedMode( "turbo_titans" ) ) + { + if ( GetSoulTitanSubClass( soul ) == "stryder" || GetSoulTitanSubClass( soul ) == "atlas" ) + GivePassive( player, ePassives.PAS_MOBILITY_DASH_CAPACITY ) + else + GivePassive( player, ePassives.PAS_DASH_RECHARGE ) + } +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_frontline.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_frontline.gnut new file mode 100644 index 00000000..7ece7dc1 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_frontline.gnut @@ -0,0 +1,159 @@ +untyped + + +global function GetFrontline +global function SetFrontline +global function AddCalculateFrontlineCallback + +const DEBUG_FRONTLINE = false + +global struct Frontline +{ + vector origin = Vector( 0.0, 0.0, 0.0 ) + vector combatDir = Vector( 0.0, 0.0, 0.0 ) + vector line = Vector( 0.0, 0.0, 0.0 ) + vector friendlyCenter = Vector( 0.0, 0.0, 0.0 ) + vector enemyCenter = Vector( 0.0, 0.0, 0.0 ) + float lastCalcTime = -1.0 +} + +struct +{ + Frontline frontline + array<void functionref()> calculateFrontlineCallbacks +} file + +Frontline function GetFrontline( team ) +{ + if ( file.frontline.lastCalcTime < Time() ) + { + CalculateFrontline() + file.frontline.lastCalcTime = Time() + } + + Frontline fl + fl = clone file.frontline + + if ( team == TEAM_MILITIA ) + { + fl.combatDir *= -1.0 + vector temp = fl.friendlyCenter + fl.friendlyCenter = fl.enemyCenter + fl.enemyCenter = temp + } + + return fl +} + +void function AddCalculateFrontlineCallback( void functionref() callbackFunc ) +{ + // Check if this function has already been added + #if DEV + foreach ( func in file.calculateFrontlineCallbacks ) + { + Assert( func != callbackFunc ) + } + #endif + + file.calculateFrontlineCallbacks.append( callbackFunc ) +} + +void function CalculateFrontline() +{ + #if DEV + float debugTime = 0.2 + #endif + + if ( file.calculateFrontlineCallbacks.len() > 0 ) + { + foreach ( callbackFunc in file.calculateFrontlineCallbacks ) + { + callbackFunc() + } + } + else + { + vector militiaCenter = CalculateWeightedTeamCenter( TEAM_MILITIA ) + vector imcCenter = CalculateWeightedTeamCenter( TEAM_IMC ) + + file.frontline.friendlyCenter = imcCenter // friendlyCenter is for TEAM_IMC by default + file.frontline.enemyCenter = militiaCenter + + file.frontline.origin = ( militiaCenter + imcCenter ) * 0.5 + file.frontline.combatDir = Normalize( militiaCenter - imcCenter ) // combatDir is for TEAM_IMC by default + file.frontline.line = CrossProduct( file.frontline.combatDir, Vector( 0.0, 0.0, 1.0 ) ) + + #if DEV + if ( DEBUG_FRONTLINE ) + { + DrawBox( militiaCenter, Vector( -8.0, -8.0, -8.0 ), Vector( 8.0, 8.0, 8.0 ), 255, 102, 0, true, debugTime ) + DrawBox( imcCenter, Vector( -8.0, -8.0, -8.0 ), Vector( 8.0, 8.0, 8.0 ), 0, 0, 255, true, debugTime ) + DebugDrawLine( militiaCenter, imcCenter, 0, 255, 0, true, debugTime ) + } + #endif + } + + #if DEV + if ( DEBUG_FRONTLINE ) + { + DrawBox( file.frontline.origin, Vector( -32.0, -32.0, -32.0 ), Vector( 32.0, 32.0, 32.0 ), 255, 0, 0, true, debugTime ) + DebugDrawLine( file.frontline.origin - file.frontline.line * 500.0, file.frontline.origin + file.frontline.line * 500.0, 255, 0, 0, true, debugTime ) + } + #endif +} + +void function SetFrontline( vector origin, vector combatDir ) +{ + file.frontline.origin = origin + file.frontline.combatDir = combatDir + file.frontline.line = CrossProduct( file.frontline.combatDir, Vector( 0.0, 0.0, 1.0 ) ) +} + +vector function CalculateWeightedTeamCenter( int team ) +{ + array<entity> teamPlayers = GetPlayerArrayOfTeam_Alive( team ) + int teamPlayersCount = teamPlayers.len() + + if ( teamPlayersCount == 0 ) + return Vector( 0.0, 0.0, 0.0 ) + + // find minimum distances between teammates + array<float> minTeammateDistances// = arrayofsize( teamPlayersCount, 99999.0 ) + minTeammateDistances.resize( teamPlayersCount, 99999.0 ) + + for ( int i = 0; i < teamPlayersCount; i++ ) + { + entity playerI = teamPlayers[ i ] + + for ( int j = i + 1; j < teamPlayersCount; j++ ) + { + entity playerJ = teamPlayers[ j ] + float distanceBetweenPlayers = Distance( playerI.GetOrigin(), playerJ.GetOrigin() ) + + if ( distanceBetweenPlayers < minTeammateDistances[ i ] ) + minTeammateDistances[ i ] = distanceBetweenPlayers + + if ( distanceBetweenPlayers < minTeammateDistances[ j ] ) + minTeammateDistances[ j ] = distanceBetweenPlayers + } + } + + vector weightedOrgSum = Vector( 0.0, 0.0, 0.0 ) + float weightSum = 0.0 + float weight = 0.0 + float halfPi = 1.57 // passing a fraction of this value into sin which gives us the first part of a sin wave from 0 - 1 + float maxPossibleDistance = MAX_WORLD_RANGE + float magicNumber = 14.0 // magic number gives the desired falloff + + // calculate a weighted origin based on how close players are to teammates + foreach ( index, player in teamPlayers ) + { + float radians = halfPi * ( minTeammateDistances[ index ] / maxPossibleDistance ) // radians will be a value between 0 - halfPi + weight = pow( ( 1.0 - sin( radians ) ), magicNumber ) // pow squashes the result so the curve has the falloff that's desired + + weightedOrgSum += player.GetOrigin() * weight + weightSum += weight + } + + return weightedOrgSum / weightSum +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut new file mode 100644 index 00000000..a30944cf --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_aitdm.nut @@ -0,0 +1,12 @@ +global function GamemodeAITdm_Init +global function RateSpawnpoints_Frontline + +void function GamemodeAITdm_Init() +{ + +} + +void function RateSpawnpoints_Frontline(int _0, array<entity> _1, int _2, entity _3) +{ + +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut new file mode 100644 index 00000000..b75ed51b --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_at.nut @@ -0,0 +1,18 @@ +global function GamemodeAt_Init +global function RateSpawnpoints_AT +global function RateSpawnpoints_SpawnZones + +void function GamemodeAt_Init() +{ + +} + +void function RateSpawnpoints_AT( int checkclass, array<entity> spawnpoints, int team, entity player ) +{ + RateSpawnpoints_Generic( checkclass, spawnpoints, team, player ) // temp +} + +void function RateSpawnpoints_SpawnZones( int checkclass, array<entity> spawnpoints, int team, entity player ) +{ + RateSpawnpoints_Generic( checkclass, spawnpoints, team, player ) // temp +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut new file mode 100644 index 00000000..b1de4d4f --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_coliseum.nut @@ -0,0 +1,98 @@ +untyped + +global function GamemodeColiseum_Init +global function GamemodeColiseum_CustomIntro + +bool hasShownIntroScreen = false + +void function GamemodeColiseum_Init() +{ + // gamemode settings + SetRoundBased( true ) + SetRespawnsEnabled( false ) + SetShouldUseRoundWinningKillReplay( true ) + Riff_ForceTitanAvailability( eTitanAvailability.Never ) + Riff_ForceBoostAvailability( eBoostAvailability.Disabled ) + Riff_ForceSetEliminationMode( eEliminationMode.Pilots ) + SetLoadoutGracePeriodEnabled( false ) // prevent modifying loadouts with grace period + SetWeaponDropsEnabled( false ) + + ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() ) + AddCallback_GameStateEnter( eGameState.Prematch, ShowColiseumIntroScreen ) + AddCallback_OnPlayerRespawned( GivePlayerColiseumLoadout ) +} + +// stub function referenced in sh_gamemodes_mp +void function GamemodeColiseum_CustomIntro( entity player ) +{} + +void function ShowColiseumIntroScreen() +{ + if ( !hasShownIntroScreen ) + thread ShowColiseumIntroScreenThreaded() + + hasShownIntroScreen = true +} + +void function ShowColiseumIntroScreenThreaded() +{ + wait 5 + + foreach ( entity player in GetPlayerArray() ) + Remote_CallFunction_NonReplay( player, "ServerCallback_ColiseumIntro", 1, 1, 1 ) // stub numbers atm because lazy +} + +void function GivePlayerColiseumLoadout( entity player ) +{ + if ( GetCurrentPlaylistVarInt( "coliseum_loadouts_enabled", 1 ) == 0 ) + return + + // create loadout struct + PilotLoadoutDef coliseumLoadout = clone GetActivePilotLoadout( player ) + + /* from playlists.txt + coliseum_primary "mp_weapon_lstar" + coliseum_primary_attachment "" + coliseum_primary_mod1 "" + coliseum_primary_mod2 "" + coliseum_primary_mod3 "" + coliseum_secondary "mp_weapon_softball" + coliseum_secondary_mod1 "" + coliseum_secondary_mod2 "" + coliseum_secondary_mod3 "" + coliseum_weapon3 "" + coliseum_weapon3_mod1 "" + coliseum_weapon3_mod2 "" + coliseum_weapon3_mod3 "" + coliseum_melee "melee_pilot_emptyhanded" + coliseum_special "mp_ability_heal" + coliseum_ordnance "mp_weapon_frag_drone" + coliseum_passive1 "pas_fast_health_regen" + coliseum_passive2 "pas_wallhang"*/ + + coliseumLoadout.primary = GetColiseumItem( "primary" ) + coliseumLoadout.primaryMods = [ GetColiseumItem( "primary_attachment" ), GetColiseumItem( "primary_mod1" ), GetColiseumItem( "primary_mod2" ), GetColiseumItem( "primary_mod3" ) ] + + coliseumLoadout.secondary = GetColiseumItem( "secondary" ) + coliseumLoadout.secondaryMods = [ GetColiseumItem( "secondary_mod1" ), GetColiseumItem( "secondary_mod2" ), GetColiseumItem( "secondary_mod3" ) ] + + coliseumLoadout.weapon3 = GetColiseumItem( "weapon3" ) + coliseumLoadout.weapon3Mods = [ GetColiseumItem( "weapon3_mod1" ), GetColiseumItem( "weapon3_mod2" ), GetColiseumItem( "weapon3_mod3" ) ] + + coliseumLoadout.melee = GetColiseumItem( "melee" ) + coliseumLoadout.special = GetColiseumItem( "special" ) + coliseumLoadout.ordnance = GetColiseumItem( "ordnance" ) + coliseumLoadout.passive1 = GetColiseumItem( "passive1" ) + coliseumLoadout.passive2 = GetColiseumItem( "passive2" ) + + coliseumLoadout.setFile = GetSuitAndGenderBasedSetFile( "coliseum", coliseumLoadout.race == RACE_HUMAN_FEMALE ? "female" : "male" ) + + GivePilotLoadout( player, coliseumLoadout ) +} + +string function GetColiseumItem( string name ) +{ + return expect string ( GetCurrentPlaylistVar( "coliseum_" + name ) ) +} + +// todo this needs the outro: unsure what anims it uses
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut new file mode 100644 index 00000000..ddfe6ee6 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_cp.nut @@ -0,0 +1,282 @@ +untyped + +global function GamemodeCP_Init +global function RateSpawnpoints_CP + +// needed for sh_gamemode_cp_dialogue +global array<entity> HARDPOINTS + +struct HardpointStruct +{ + entity hardpoint + entity trigger + entity prop + + array<entity> imcCappers + array<entity> militiaCappers +} + +struct { + bool ampingEnabled = true + + array<HardpointStruct> hardpoints +} file + +void function GamemodeCP_Init() +{ + file.ampingEnabled = GetCurrentPlaylistVar( "amped_capture_points" ) == "1" + + RegisterSignal( "HardpointCaptureStart" ) + + AddCallback_EntitiesDidLoad( SpawnHardpoints ) + AddCallback_GameStateEnter( eGameState.Playing, StartHardpointThink ) +} + +void function RateSpawnpoints_CP( int checkClass, array<entity> spawnpoints, int team, entity player ) +{ + +} + +void function SpawnHardpoints() +{ + foreach ( entity spawnpoint in GetEntArrayByClass_Expensive( "info_hardpoint" ) ) + { + if ( GameModeRemove( spawnpoint ) ) + continue + + // spawnpoints are CHardPoint entities + // init the hardpoint ent + int hardpointID = 0 + if ( spawnpoint.kv.hardpointGroup == "B" ) + hardpointID = 1 + else if ( spawnpoint.kv.hardpointGroup == "C" ) + hardpointID = 2 + + spawnpoint.SetHardpointID( hardpointID ) + + HardpointStruct hardpointStruct + hardpointStruct.hardpoint = spawnpoint + hardpointStruct.prop = CreatePropDynamic( spawnpoint.GetModelName(), spawnpoint.GetOrigin(), spawnpoint.GetAngles(), 6 ) + + entity trigger = GetEnt( expect string( spawnpoint.kv.triggerTarget ) ) + hardpointStruct.trigger = trigger + + file.hardpoints.append( hardpointStruct ) + HARDPOINTS.append( spawnpoint ) // for vo script + spawnpoint.s.trigger <- trigger // also for vo script + + SetGlobalNetEnt( "objective" + spawnpoint.kv.hardpointGroup + "Ent", spawnpoint ) + + // set up trigger functions + trigger.SetEnterCallback( OnHardpointEntered ) + trigger.SetLeaveCallback( OnHardpointLeft ) + } +} + +// functions for handling hardpoint netvars +void function SetHardpointState( HardpointStruct hardpoint, int state ) +{ + SetGlobalNetInt( "objective" + hardpoint.hardpoint.kv.hardpointGroup + "State", state ) + hardpoint.hardpoint.SetHardpointState( state ) +} + +int function GetHardpointState( HardpointStruct hardpoint ) +{ + return GetGlobalNetInt( "objective" + hardpoint.hardpoint.kv.hardpointGroup + "State" ) +} + +void function SetHardpointCappingTeam( HardpointStruct hardpoint, int team ) +{ + SetGlobalNetInt( "objective" + hardpoint.hardpoint.kv.hardpointGroup + "CappingTeam", team ) +} + +int function GetHardpointCappingTeam( HardpointStruct hardpoint ) +{ + return GetGlobalNetInt( "objective" + hardpoint.hardpoint.kv.hardpointGroup + "CappingTeam" ) +} + +void function SetHardpointCaptureProgress( HardpointStruct hardpoint, float progress ) +{ + SetGlobalNetFloat( "objective" + hardpoint.hardpoint.kv.hardpointGroup + "Progress", progress ) +} + +float function GetHardpointCaptureProgress( HardpointStruct hardpoint ) +{ + return GetGlobalNetFloat( "objective" + hardpoint.hardpoint.kv.hardpointGroup + "Progress" ) +} + + +void function StartHardpointThink() +{ + thread TrackChevronStates() + + foreach ( HardpointStruct hardpoint in file.hardpoints ) + thread HardpointThink( hardpoint ) +} + +void function HardpointThink( HardpointStruct hardpoint ) +{ + entity hardpointEnt = hardpoint.hardpoint + + float lastTime = Time() + float lastScoreTime = Time() + + WaitFrame() // wait a frame so deltaTime is never zero + while ( GamePlayingOrSuddenDeath() ) + { + int imcCappers = hardpoint.imcCappers.len() + int militiaCappers = hardpoint.militiaCappers.len() + + float deltaTime = Time() - lastTime + + int cappingTeam + if ( imcCappers > militiaCappers ) + cappingTeam = TEAM_IMC + else if ( militiaCappers > imcCappers ) + cappingTeam = TEAM_MILITIA + + if ( cappingTeam != TEAM_UNASSIGNED ) + { + // hardpoint is owned by controlling team + if ( hardpointEnt.GetTeam() == cappingTeam ) + { + // hardpoint is being neutralised, reverse the neutralisation + if ( GetHardpointCappingTeam( hardpoint ) != cappingTeam || GetHardpointCaptureProgress( hardpoint ) < 1.0 ) + { + SetHardpointCappingTeam( hardpoint, cappingTeam ) + SetHardpointCaptureProgress( hardpoint, min( 1.0, GetHardpointCaptureProgress( hardpoint ) + ( deltaTime / CAPTURE_DURATION_CAPTURE ) ) ) + } + // hardpoint is fully captured, start amping if amping is enabled + else if ( file.ampingEnabled && GetHardpointState( hardpoint ) < CAPTURE_POINT_STATE_AMPING ) + SetHardpointState( hardpoint, CAPTURE_POINT_STATE_AMPING ) + + // amp the hardpoint + if ( GetHardpointState( hardpoint ) == CAPTURE_POINT_STATE_AMPING ) + { + SetHardpointCaptureProgress( hardpoint, min( 2.0, GetHardpointCaptureProgress( hardpoint ) + ( deltaTime / HARDPOINT_AMPED_DELAY ) ) ) + if ( GetHardpointCaptureProgress( hardpoint ) == 2.0 ) + { + SetHardpointState( hardpoint, CAPTURE_POINT_STATE_AMPED ) + + // can't use the dialogue functions here because for some reason GamemodeCP_VO_Amped isn't global? + PlayFactionDialogueToTeam( "amphp_youAmped" + hardpointEnt.kv.hardpointGroup, cappingTeam ) + PlayFactionDialogueToTeam( "amphp_enemyAmped" + hardpointEnt.kv.hardpointGroup, GetOtherTeam( cappingTeam ) ) + } + } + } + else // we don't own this hardpoint, cap it + { + SetHardpointCappingTeam( hardpoint, cappingTeam ) + GamemodeCP_VO_StartCapping( hardpointEnt ) // this doesn't consistently trigger for some reason + + SetHardpointCaptureProgress( hardpoint, min( 1.0, GetHardpointCaptureProgress( hardpoint ) + ( deltaTime / CAPTURE_DURATION_CAPTURE ) ) ) + + if ( GetHardpointCaptureProgress( hardpoint ) >= 1.0 ) + { + SetTeam( hardpointEnt, cappingTeam ) + SetTeam( hardpoint.prop, cappingTeam ) + SetHardpointState( hardpoint, CAPTURE_POINT_STATE_CAPTURED ) + + EmitSoundOnEntityToTeamExceptPlayer( hardpointEnt, "hardpoint_console_captured", cappingTeam, null ) + GamemodeCP_VO_Captured( hardpointEnt ) + } + } + } + // capture halting + else if ( imcCappers > 0 && imcCappers == militiaCappers ) + SetHardpointState( hardpoint, CAPTURE_POINT_STATE_HALTED ) + // amped decay + else if ( imcCappers == 0 && militiaCappers == 0 && GetHardpointState( hardpoint ) >= CAPTURE_POINT_STATE_AMPING ) + { + // it seems like network vars won't change if they're too similar? often we get situations here where it's tryna change from 1.00098 to 1 which doesn't work + // so we need to check the "real" progress manually + // have only gotten this issue here so far, but in theory i think this could be an issue in a good few places, worth looking out for + // tho, idk might not be, we don't work with numbers at this small of a scale too often + float realProgress = max( 1.0, GetHardpointCaptureProgress( hardpoint ) - ( deltaTime / HARDPOINT_AMPED_DELAY ) ) + SetHardpointCaptureProgress( hardpoint, realProgress ) + + if ( realProgress == 1 ) + SetHardpointState( hardpoint, CAPTURE_POINT_STATE_CAPTURED ) + // dont use unamping atm + //else + // SetHardpointState( hardpoint, CAPTURE_POINT_STATE_SELF_UNAMPING ) + } + + // scoring + if ( hardpointEnt.GetTeam() != TEAM_UNASSIGNED && GetHardpointState( hardpoint ) >= CAPTURE_POINT_STATE_CAPTURED && Time() - lastScoreTime >= TEAM_OWNED_SCORE_FREQ ) + { + lastScoreTime = Time() + + // 2x score if amped + if ( GetHardpointState( hardpoint ) == CAPTURE_POINT_STATE_AMPED ) + AddTeamScore( hardpointEnt.GetTeam(), 2 ) + else + AddTeamScore( hardpointEnt.GetTeam(), 1 ) + } + + lastTime = Time() + WaitFrame() + } +} + +// doing this in HardpointThink is effort since it's for individual hardpoints +// so we do it here instead +void function TrackChevronStates() +{ + // you get 1 amped arrow for chevron / 4, 1 unamped arrow for every 1 the amped chevrons + + while ( true ) + { + int imcChevron + int militiaChevron + + foreach ( HardpointStruct hardpoint in file.hardpoints ) + { + if ( hardpoint.hardpoint.GetTeam() == TEAM_IMC ) + { + if ( hardpoint.hardpoint.GetHardpointState() == CAPTURE_POINT_STATE_AMPED ) + imcChevron += 4 + else if ( hardpoint.hardpoint.GetHardpointState() >= CAPTURE_POINT_STATE_CAPTURED ) + imcChevron++ + } + else if ( hardpoint.hardpoint.GetTeam() == TEAM_MILITIA ) + { + if ( hardpoint.hardpoint.GetHardpointState() == CAPTURE_POINT_STATE_AMPED ) + militiaChevron += 4 + else if ( hardpoint.hardpoint.GetHardpointState() >= CAPTURE_POINT_STATE_CAPTURED ) + militiaChevron++ + } + } + + SetGlobalNetInt( "imcChevronState", imcChevron ) + SetGlobalNetInt( "milChevronState", militiaChevron ) + + WaitFrame() + } +} + +void function OnHardpointEntered( entity trigger, entity player ) +{ + HardpointStruct hardpoint + foreach ( HardpointStruct hardpointStruct in file.hardpoints ) + if ( hardpointStruct.trigger == trigger ) + hardpoint = hardpointStruct + + if ( player.GetTeam() == TEAM_IMC ) + hardpoint.imcCappers.append( player ) + else + hardpoint.militiaCappers.append( player ) +} + +void function OnHardpointLeft( entity trigger, entity player ) +{ + HardpointStruct hardpoint + foreach ( HardpointStruct hardpointStruct in file.hardpoints ) + if ( hardpointStruct.trigger == trigger ) + hardpoint = hardpointStruct + + if ( player.GetTeam() == TEAM_IMC ) + hardpoint.imcCappers.remove( hardpoint.imcCappers.find( player ) ) + else + hardpoint.militiaCappers.remove( hardpoint.militiaCappers.find( player ) ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut new file mode 100644 index 00000000..704f55d3 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ctf.nut @@ -0,0 +1,518 @@ +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 ) + SetShouldUseRoundWinningKillReplay( true ) + SetRoundWinningKillReplayKillClasses( false, false ) // make these fully manual + + 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 + + int spawnTeam = team + if ( HasSwitchedSides() ) + spawnTeam = GetOtherTeam( team ) + + array<entity> startSpawns = SpawnPoints_GetPilotStart( spawnTeam ) + array<entity> enemyPlayers = GetPlayerArrayOfTeam_Alive( GetOtherTeam( spawnTeam ) ) + + 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 + SetRoundWinningKillReplayAttacker( player ) // set attacker for last cap replay + + array<entity> assistList + if ( player.GetTeam() == TEAM_IMC ) + assistList = file.imcCaptureAssistList + else + assistList = file.militiaCaptureAssistList + + foreach( entity assistPlayer in assistList ) + if ( player != assistPlayer ) + AddPlayerScore( assistPlayer, "FlagCaptureAssist", player ) + + assistList.clear() + + // notifs + MessageToPlayer( player, eEventNotifications.YouCapturedTheEnemyFlag ) + EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_PlayerScore" ) + + MessageToTeam( team, eEventNotifications.PlayerCapturedEnemyFlag, player, player ) + EmitSoundOnEntityToTeamExceptPlayer( flag, "UI_CTF_3P_TeamScore", player.GetTeam(), player ) + + MessageToTeam( GetOtherTeam( team ), eEventNotifications.PlayerCapturedFriendlyFlag, player, player ) + EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyScore", flag.GetTeam() ) + + if ( GameRules_GetTeamScore( team ) == GameMode_GetRoundScoreLimit( GAMETYPE ) - 1 ) + { + PlayFactionDialogueToTeam( "ctf_notifyWin1more", team ) + PlayFactionDialogueToTeam( "ctf_notifyLose1more", GetOtherTeam( team ) ) + } +} + +void function OnPlayerEntersFlagReturnTrigger( entity trigger, entity player ) +{ + entity flag + if ( trigger.GetTeam() == TEAM_IMC ) + flag = file.imcFlag + else + flag = file.militiaFlag + + if ( !player.IsPlayer() || player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null ) + return + + thread TryReturnFlag( player, flag ) +} + +void function OnPlayerExitsFlagReturnTrigger( entity trigger, entity player ) +{ + entity flag + if ( trigger.GetTeam() == TEAM_IMC ) + flag = file.imcFlag + else + flag = file.militiaFlag + + if ( !player.IsPlayer() || player.IsTitan() || player.GetTeam() != flag.GetTeam() || IsFlagHome( flag ) || flag.GetParent() != null ) + return + + player.Signal( "FlagReturnEnded" ) +} + +void function TryReturnFlag( entity player, entity flag ) +{ + // start return progress bar + Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StartReturnFlagProgressBar", Time() + CTF_GetFlagReturnTime() ) + EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_FlagReturnMeter" ) + + OnThreadEnd( function() : ( player ) + { + // cleanup + Remote_CallFunction_NonReplay( player, "ServerCallback_CTF_StopReturnFlagProgressBar" ) + StopSoundOnEntity( player, "UI_CTF_1P_FlagReturnMeter" ) + }) + + player.EndSignal( "FlagReturnEnded" ) + player.EndSignal( "OnDeath" ) + + wait CTF_GetFlagReturnTime() + + // flag return succeeded + // return flag + ResetFlag( flag ) + + // do notifications for return + MessageToPlayer( player, eEventNotifications.YouReturnedFriendlyFlag ) + AddPlayerScore( player, "FlagReturn", player ) + player.AddToPlayerGameStat( PGS_DEFENSE_SCORE, 1 ) + + MessageToTeam( flag.GetTeam(), eEventNotifications.PlayerReturnedFriendlyFlag, null, player ) + EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_TeamReturnsFlag", flag.GetTeam() ) + PlayFactionDialogueToTeam( "ctf_flagReturnedFriendly", flag.GetTeam() ) + + MessageToTeam( GetOtherTeam( flag.GetTeam() ), eEventNotifications.PlayerReturnedEnemyFlag, null, player ) + EmitSoundOnEntityToTeam( flag, "UI_CTF_3P_EnemyReturnsFlag", GetOtherTeam( flag.GetTeam() ) ) + PlayFactionDialogueToTeam( "ctf_flagReturnedEnemy", GetOtherTeam( flag.GetTeam() ) ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fd.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fd.nut new file mode 100644 index 00000000..b5f700e5 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fd.nut @@ -0,0 +1,12 @@ +global function GamemodeFD_Init +global function RateSpawnpoints_FD + +void function GamemodeFD_Init() +{ + +} + +void function RateSpawnpoints_FD(int _0, array<entity> _1, int _2, entity _3) +{ + +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut new file mode 100644 index 00000000..932f14b7 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ffa.nut @@ -0,0 +1,17 @@ +global function FFA_Init + +void function FFA_Init() +{ + Evac_SetEnabled( false ) + + AddCallback_OnPlayerKilled( OnPlayerKilled ) +} + +void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() && GetGameState() == eGameState.Playing ) + { + AddTeamScore( attacker.GetTeam(), 1 ) + attacker.AddToPlayerGameStat( PGS_SCORE, 1 ) + } +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut new file mode 100644 index 00000000..9d8f84b5 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_fra.nut @@ -0,0 +1,27 @@ +global function GamemodeFRA_AddAdditionalInitCallback + +// fra doesn't register a gamemode init by default, adding one just so we can set stuff up for it +void function GamemodeFRA_AddAdditionalInitCallback() +{ + AddCallback_OnCustomGamemodesInit( GamemodeFRA_AddAdditionalInit ) +} + +void function GamemodeFRA_AddAdditionalInit() +{ + GameMode_AddServerInit( FREE_AGENCY, GamemodeFRA_Init ) +} + +void function GamemodeFRA_Init() +{ + // need a way to disable passive earnmeter gain + ScoreEvent_SetEarnMeterValues( "PilotBatteryPickup", 0.0, 0.34 ) + EarnMeterMP_SetPassiveMeterGainEnabled( false ) + PilotBattery_SetMaxCount( 3 ) + + AddCallback_OnPlayerKilled( FRARemoveEarnMeter ) +} + +void function FRARemoveEarnMeter( entity victim, entity attacker, var damageInfo ) +{ + PlayerEarnMeter_Reset( victim ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut new file mode 100644 index 00000000..89f9c991 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_lts.nut @@ -0,0 +1,103 @@ +untyped +global function GamemodeLts_Init + +struct { + entity lastDamageInfoVictim + entity lastDamageInfoAttacker + int lastDamageInfoMethodOfDeath + float lastDamageInfoTime + + bool shouldDoHighlights +} file + +void function GamemodeLts_Init() +{ + // gamemode settings + SetShouldUsePickLoadoutScreen( true ) + SetSwitchSidesBased( true ) + SetRoundBased( true ) + SetRespawnsEnabled( false ) + Riff_ForceSetEliminationMode( eEliminationMode.PilotsTitans ) + Riff_ForceSetSpawnAsTitan( eSpawnAsTitan.Always ) + SetShouldUseRoundWinningKillReplay( true ) + SetRoundWinningKillReplayKillClasses( true, true ) // both titan and pilot kills are tracked + FlagSet( "ForceStartSpawn" ) + + AddCallback_OnPilotBecomesTitan( RefreshThirtySecondWallhackHighlight ) + AddCallback_OnTitanBecomesPilot( RefreshThirtySecondWallhackHighlight ) + + SetTimeoutWinnerDecisionFunc( CheckTitanHealthForDraw ) + TrackTitanDamageInPlayerGameStat( PGS_ASSAULT_SCORE ) + + ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() ) + AddCallback_GameStateEnter( eGameState.Playing, WaitForThirtySecondsLeft ) +} + +void function WaitForThirtySecondsLeft() +{ + thread WaitForThirtySecondsLeftThreaded() +} + +void function WaitForThirtySecondsLeftThreaded() +{ + svGlobal.levelEnt.EndSignal( "RoundEnd" ) // end this on round end + + float endTime = expect float ( GetServerVar( "roundEndTime" ) ) + + // wait until 30sec left + wait ( endTime - 30 ) - Time() + foreach ( entity player in GetPlayerArray() ) + { + // warn there's 30 seconds left + Remote_CallFunction_NonReplay( player, "ServerCallback_LTSThirtySecondWarning" ) + + // do initial highlight + RefreshThirtySecondWallhackHighlight( player, null ) + } +} + +void function RefreshThirtySecondWallhackHighlight( entity player, entity titan ) +{ + if ( TimeSpentInCurrentState() < expect float ( GetServerVar( "roundEndTime" ) ) - 30.0 ) + return + + Highlight_SetEnemyHighlight( player, "enemy_sonar" ) // i think this needs a different effect, this works for now tho + + if ( player.GetPetTitan() != null ) + Highlight_SetEnemyHighlight( player.GetPetTitan(), "enemy_sonar" ) +} + +int function CheckTitanHealthForDraw() +{ + int militiaTitans + int imcTitans + + float militiaHealth + float imcHealth + + foreach ( entity titan in GetTitanArray() ) + { + if ( titan.GetTeam() == TEAM_MILITIA ) + { + // doomed is counted as 0 health + militiaHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan ) + militiaTitans++ + } + else + { + // doomed is counted as 0 health in this + imcHealth += titan.GetTitanSoul().IsDoomed() ? 0.0 : GetHealthFrac( titan ) + imcTitans++ + } + } + + // note: due to how stuff is set up rn, there's actually no way to do win/loss reasons outside of a SetWinner call, i.e. not in timeout winner decision + // as soon as there is, strings in question are "#GAMEMODE_TITAN_TITAN_ADVANTAGE" and "#GAMEMODE_TITAN_TITAN_DISADVANTAGE" + + if ( militiaTitans != imcTitans ) + return militiaTitans > imcTitans ? TEAM_MILITIA : TEAM_IMC + else if ( militiaHealth != imcHealth ) + return militiaHealth > imcHealth ? TEAM_MILITIA : TEAM_IMC + + return TEAM_UNASSIGNED +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut new file mode 100644 index 00000000..8d0545cb --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_mfd.nut @@ -0,0 +1,232 @@ +untyped +global function GamemodeMfd_Init + +struct { + entity imcLastMark + entity militiaLastMark +} file + +void function GamemodeMfd_Init() +{ + GamemodeMfdShared_Init() + + RegisterSignal( "MarkKilled" ) + + AddCallback_OnPlayerKilled( UpdateMarksForKill ) + AddCallback_GameStateEnter( eGameState.Playing, CreateInitialMarks ) +} + +void function CreateInitialMarks() +{ + entity imcMark = CreateEntity( MARKER_ENT_CLASSNAME ) + imcMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT + SetTeam( imcMark, TEAM_IMC ) + SetTargetName( imcMark, MARKET_ENT_MARKED_NAME ) // why is it market_ent lol + DispatchSpawn( imcMark ) + FillMFDMarkers( imcMark ) + + entity imcPendingMark = CreateEntity( MARKER_ENT_CLASSNAME ) + imcPendingMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT + SetTeam( imcPendingMark, TEAM_IMC ) + SetTargetName( imcPendingMark, MARKET_ENT_PENDING_MARKED_NAME ) + DispatchSpawn( imcPendingMark ) + FillMFDMarkers( imcPendingMark ) + + entity militiaMark = CreateEntity( MARKER_ENT_CLASSNAME ) + militiaMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT + SetTeam( militiaMark, TEAM_MILITIA ) + SetTargetName( militiaMark, MARKET_ENT_MARKED_NAME ) + DispatchSpawn( militiaMark ) + FillMFDMarkers( militiaMark ) + + entity militiaPendingMark = CreateEntity( MARKER_ENT_CLASSNAME ) + militiaPendingMark.kv.spawnflags = SF_INFOTARGET_ALWAYS_TRANSMIT_TO_CLIENT + SetTeam( militiaPendingMark, TEAM_MILITIA ) + SetTargetName( militiaPendingMark, MARKET_ENT_PENDING_MARKED_NAME ) + DispatchSpawn( militiaPendingMark ) + FillMFDMarkers( militiaPendingMark ) + + thread MFDThink() +} + +void function MFDThink() +{ + svGlobal.levelEnt.EndSignal( "GameStateChanged" ) + + entity imcMark + entity militiaMark + + while ( true ) + { + if ( !TargetsMarkedImmediately() ) + wait MFD_BETWEEN_MARKS_TIME + + // wait for enough players to spawn + array<entity> imcPlayers + array<entity> militiaPlayers + while ( imcPlayers.len() == 0 || militiaPlayers.len() == 0 ) + { + imcPlayers = GetPlayerArrayOfTeam( TEAM_IMC ) + militiaPlayers = GetPlayerArrayOfTeam( TEAM_MILITIA ) + + WaitFrame() + } + + // get marks, wanna increment the mark each mark, reset on player change + int imcIndex = imcPlayers.find( imcMark ) + if ( imcIndex == -1 ) // last mark + imcIndex = 0 + else + imcIndex = ( imcIndex + 1 ) % imcPlayers.len() + + imcMark = imcPlayers[ imcIndex ] + + int militiaIndex = militiaPlayers.find( imcMark ) + if ( militiaIndex == -1 ) // last mark + militiaIndex = 0 + else + militiaIndex = ( militiaIndex + 1 ) % militiaPlayers.len() + + militiaMark = militiaPlayers[ militiaIndex ] + + level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark ) + level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark ) + + foreach ( entity player in GetPlayerArray() ) + { + Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" ) + Remote_CallFunction_NonReplay( player, "ServerCallback_MFD_StartNewMarkCountdown", Time() + MFD_COUNTDOWN_TIME ) + } + + // reset if mark leaves + bool shouldReset + float endTime = Time() + MFD_COUNTDOWN_TIME + while ( endTime > Time() || ( !IsAlive( imcMark ) || !IsAlive( militiaMark ) ) ) + { + if ( !IsValid( imcMark ) || !IsValid( militiaMark ) ) + { + shouldReset = true + break + } + + WaitFrame() + } + + if ( shouldReset ) + continue + + waitthread MarkPlayers( imcMark, militiaMark ) + } +} + +void function MarkPlayers( entity imcMark, entity militiaMark ) +{ + imcMark.EndSignal( "OnDestroy" ) + imcMark.EndSignal( "Disconnected" ) + + militiaMark.EndSignal( "OnDestroy" ) + militiaMark.EndSignal( "Disconnected" ) + + OnThreadEnd( function() : ( imcMark, militiaMark ) + { + // clear marks + level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null ) + level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null ) + + foreach ( entity player in GetPlayerArray() ) + Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" ) + }) + + // clear pending marks + level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null ) + level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null ) + + // set marks + level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark ) + level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark ) + + foreach ( entity player in GetPlayerArray() ) + Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" ) + + // wait until mark dies + entity deadMark = expect entity( svGlobal.levelEnt.WaitSignal( "MarkKilled" ).mark ) + + // award points + entity livingMark = GetMarked( GetOtherTeam( deadMark.GetTeam() ) ) + livingMark.SetPlayerGameStat( PGS_DEFENSE_SCORE, livingMark.GetPlayerGameStat( PGS_DEFENSE_SCORE ) + 1 ) + + // thread this so we don't kill our own thread + thread AddTeamScore( livingMark.GetTeam(), 1 ) +} + +void function UpdateMarksForKill( entity victim, entity attacker, var damageInfo ) +{ + if ( victim == GetMarked( victim.GetTeam() ) ) + { + svGlobal.levelEnt.Signal( "MarkKilled", { mark = victim } ) + + if ( attacker.IsPlayer() ) + attacker.SetPlayerGameStat( PGS_ASSAULT_SCORE, attacker.GetPlayerGameStat( PGS_ASSAULT_SCORE ) + 1 ) + } +} + +/* +void function MarkPlayers() +{ + // todo: need to handle disconnecting marks + if ( !TargetsMarkedImmediately() ) + wait MFD_BETWEEN_MARKS_TIME + + + // wait until we actually have 2 valid players + array<entity> imcPlayers + array<entity> militiaPlayers + while ( imcPlayers.len() == 0 || militiaPlayers.len() == 0 ) + { + imcPlayers = GetPlayerArrayOfTeam( TEAM_IMC ) + militiaPlayers = GetPlayerArrayOfTeam( TEAM_MILITIA ) + + WaitFrame() + } + + // decide marks + entity imcMark = imcPlayers[ RandomInt( imcPlayers.len() ) ] + level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark ) + + entity militiaMark = militiaPlayers[ RandomInt( militiaPlayers.len() ) ] + level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark ) + + foreach ( entity player in GetPlayerArray() ) + { + Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" ) + Remote_CallFunction_NonReplay( player, "ServerCallback_MFD_StartNewMarkCountdown", Time() + MFD_COUNTDOWN_TIME ) + } + + wait MFD_COUNTDOWN_TIME + + while ( !IsAlive( imcMark ) || !IsAlive( militiaMark ) ) + WaitFrame() + + // clear pending marks + level.mfdPendingMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null ) + level.mfdPendingMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null ) + + // set marks + level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( imcMark ) + level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( militiaMark ) + + foreach ( entity player in GetPlayerArray() ) + Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" ) + + while ( IsAlive( imcMark ) && IsAlive( militiaMark ) ) + WaitFrame() + + // clear marks + level.mfdActiveMarkedPlayerEnt[ TEAM_IMC ].SetOwner( null ) + level.mfdActiveMarkedPlayerEnt[ TEAM_MILITIA ].SetOwner( null ) + + foreach ( entity player in GetPlayerArray() ) + Remote_CallFunction_NonReplay( player, "SCB_MarkedChanged" ) + + thread MarkPlayers() +}*/
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut new file mode 100644 index 00000000..3a852f91 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ps.nut @@ -0,0 +1,12 @@ +global function GamemodePs_Init + +void function GamemodePs_Init() +{ + AddCallback_OnPlayerKilled( GiveScoreForPlayerKill ) +} + +void function GiveScoreForPlayerKill( entity victim, entity attacker, var damageInfo ) +{ + if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() ) + AddTeamScore( attacker.GetTeam(), 1 ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut new file mode 100644 index 00000000..4532fb97 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_speedball.nut @@ -0,0 +1,127 @@ +global function GamemodeSpeedball_Init + +struct { + entity flagBase + entity flag + entity flagCarrier +} file + +void function GamemodeSpeedball_Init() +{ + PrecacheModel( CTF_FLAG_MODEL ) + PrecacheModel( CTF_FLAG_BASE_MODEL ) + + // gamemode settings + SetRoundBased( true ) + SetRespawnsEnabled( false ) + SetShouldUseRoundWinningKillReplay( true ) + Riff_ForceTitanAvailability( eTitanAvailability.Never ) + Riff_ForceSetEliminationMode( eEliminationMode.Pilots ) + + AddSpawnCallbackEditorClass( "script_ref", "info_speedball_flag", CreateFlag ) + + AddCallback_GameStateEnter( eGameState.Playing, ResetFlag ) + AddCallback_OnTouchHealthKit( "item_flag", OnFlagCollected ) + AddCallback_OnPlayerKilled( OnPlayerKilled ) + SetTimeoutWinnerDecisionFunc( TimeoutCheckFlagHolder ) + + ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() ) +} + +void function CreateFlag( entity flagSpawn ) +{ + entity flagBase = CreatePropDynamic( CTF_FLAG_BASE_MODEL, flagSpawn.GetOrigin(), flagSpawn.GetAngles() ) + + entity flag = CreateEntity( "item_flag" ) + flag.SetValueForModelKey( CTF_FLAG_MODEL ) + flag.MarkAsNonMovingAttachment() + DispatchSpawn( flag ) + flag.SetModel( CTF_FLAG_MODEL ) + flag.SetOrigin( flagBase.GetOrigin() + < 0, 0, flagBase.GetBoundingMaxs().z + 1 > ) + flag.SetVelocity( < 0, 0, 1 > ) + + file.flag = flag + file.flagBase = flagBase +} + +bool function OnFlagCollected( entity player, entity flag ) +{ + if ( !IsAlive( player ) || flag.GetParent() != null || player.IsTitan() || player.IsPhaseShifted() ) + return false + + GiveFlag( player ) + return false // so flag ent doesn't despawn +} + +void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( file.flagCarrier == victim ) + DropFlag() + + if ( victim.IsPlayer() && GetGameState() == eGameState.Playing ) + if ( GetPlayerArrayOfTeam_Alive( victim.GetTeam() ).len() == 1 ) + foreach ( entity player in GetPlayerArray() ) + Remote_CallFunction_NonReplay( player, "ServerCallback_SPEEDBALL_LastPlayer", player.GetTeam() != victim.GetTeam() ) +} + +void function GiveFlag( entity player ) +{ + file.flag.SetParent( player, "FLAG" ) + file.flagCarrier = player + SetGlobalNetEnt( "flagCarrier", player ) + thread DropFlagIfPhased( player ) + + EmitSoundOnEntityOnlyToPlayer( player, player, "UI_CTF_1P_GrabFlag" ) + foreach ( entity otherPlayer in GetPlayerArray() ) + { + MessageToPlayer( otherPlayer, eEventNotifications.SPEEDBALL_FlagPickedUp, player ) + + if ( otherPlayer.GetTeam() == player.GetTeam() ) + EmitSoundOnEntityToTeamExceptPlayer( file.flag, "UI_CTF_3P_TeamGrabFlag", player.GetTeam(), player ) + } +} + +void function DropFlagIfPhased( entity player ) +{ + player.EndSignal( "StartPhaseShift" ) + + OnThreadEnd( function() : ( player ) + { + if ( file.flag.GetParent() == player ) + DropFlag() + }) + + while( file.flag.GetParent() == player ) + WaitFrame() +} + +void function DropFlag() +{ + file.flag.ClearParent() + file.flag.SetAngles( < 0, 0, 0 > ) + SetGlobalNetEnt( "flagCarrier", file.flag ) + EmitSoundOnEntityOnlyToPlayer( file.flagCarrier, file.flagCarrier, "UI_CTF_1P_FlagDrop" ) + + foreach ( entity player in GetPlayerArray() ) + MessageToPlayer( player, eEventNotifications.SPEEDBALL_FlagDropped, file.flagCarrier ) + + file.flagCarrier = null +} + +void function ResetFlag() +{ + file.flag.ClearParent() + file.flag.SetAngles( < 0, 0, 0 > ) + file.flag.SetVelocity( < 0, 0, 1 > ) // hack: for some reason flag won't have gravity if i don't do this + file.flag.SetOrigin( file.flagBase.GetOrigin() + < 0, 0, file.flagBase.GetBoundingMaxs().z * 2 > ) + file.flagCarrier = null + SetGlobalNetEnt( "flagCarrier", file.flag ) +} + +int function TimeoutCheckFlagHolder() +{ + if ( file.flagCarrier == null ) + return TEAM_UNASSIGNED + + return file.flagCarrier.GetTeam() +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut new file mode 100644 index 00000000..9e80b863 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_tdm.nut @@ -0,0 +1,19 @@ +global function GamemodeTdm_Init +global function RateSpawnpoints_Directional + +void function GamemodeTdm_Init() +{ + AddCallback_OnPlayerKilled( GiveScoreForPlayerKill ) +} + +void function GiveScoreForPlayerKill( entity victim, entity attacker, var damageInfo ) +{ + if ( victim != attacker && victim.IsPlayer() && attacker.IsPlayer() ) + AddTeamScore( attacker.GetTeam(), 1 ) +} + +void function RateSpawnpoints_Directional( int checkclass, array<entity> spawnpoints, int team, entity player ) +{ + // temp + RateSpawnpoints_Generic( checkclass, spawnpoints, team, player ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut new file mode 100644 index 00000000..faf3e5ca --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_gamemode_ttdm.nut @@ -0,0 +1,73 @@ +global function GamemodeTTDM_Init + +const float TTDMIntroLength = 15.0 + +void function GamemodeTTDM_Init() +{ + Riff_ForceSetSpawnAsTitan( eSpawnAsTitan.Always ) + Riff_ForceTitanExitEnabled( eTitanExitEnabled.Never ) + TrackTitanDamageInPlayerGameStat( PGS_ASSAULT_SCORE ) + + ClassicMP_SetCustomIntro( TTDMIntroSetup, TTDMIntroLength ) + + AddCallback_OnPlayerKilled( AddTeamScoreForPlayerKilled ) // dont have to track autotitan kills since you cant leave your titan in this mode + + // probably needs scoreevent earnmeter values +} + +void function TTDMIntroSetup() +{ + // this should show intermission cam for 15 sec in prematch, before spawning players as titans + AddCallback_GameStateEnter( eGameState.Prematch, TTDMIntroStart ) + AddCallback_OnClientConnected( TTDMIntroShowIntermissionCam ) +} + +void function TTDMIntroStart() +{ + thread TTDMIntroStartThreaded() +} + +void function TTDMIntroStartThreaded() +{ + ClassicMP_OnIntroStarted() + + foreach ( entity player in GetPlayerArray() ) + TTDMIntroShowIntermissionCam( player ) + + wait TTDMIntroLength + + ClassicMP_OnIntroFinished() +} + +void function TTDMIntroShowIntermissionCam( entity player ) +{ + if ( GetGameState() != eGameState.Prematch ) + return + + thread PlayerWatchesTTDMIntroIntermissionCam( player ) +} + +void function PlayerWatchesTTDMIntroIntermissionCam( entity player ) +{ + ScreenFadeFromBlack( player ) + + entity intermissionCam = GetEntArrayByClass_Expensive( "info_intermission" )[ 0 ] + + // the angle set here seems sorta inconsistent as to whether it actually works or just stays at 0 for some reason + player.SetObserverModeStaticPosition( intermissionCam.GetOrigin() ) + player.SetObserverModeStaticAngles( intermissionCam.GetAngles() ) + player.StartObserverMode( OBS_MODE_STATIC_LOCKED ) + + wait TTDMIntroLength + + RespawnAsTitan( player, false ) + TryGameModeAnnouncement( player ) +} + +void function AddTeamScoreForPlayerKilled( entity victim, entity attacker, var damageInfo ) +{ + if ( victim == attacker || !victim.IsPlayer() || !attacker.IsPlayer() ) + return + + AddTeamScore( GetOtherTeam( victim.GetTeam() ), 1 ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_hardpoints.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_hardpoints.gnut new file mode 100644 index 00000000..0a32f133 --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_hardpoints.gnut @@ -0,0 +1,35 @@ +// atm this is just a stub script since hardpoints are only really used in hardpoint +// respawn probably tried to share this code across multiple modes but atm we just dont need to do that + +global function Hardpoints_Init + +global function CapturePoint_GetStartProgress +global function CapturePoint_GetCappingTeam +global function CapturePoint_GetOwningTeam +global function CapturePoint_GetGoalProgress + + +void function Hardpoints_Init() +{ + +} + +float function CapturePoint_GetStartProgress( entity hardpoint ) +{ + return GetGlobalNetFloat( "objective" + hardpoint.kv.hardpointGroup + "Progress" ) +} + +int function CapturePoint_GetCappingTeam( entity hardpoint ) +{ + return GetGlobalNetInt( "objective" + hardpoint.kv.hardpointGroup + "CappingTeam" ) +} + +int function CapturePoint_GetOwningTeam( entity hardpoint ) +{ + return hardpoint.GetTeam() +} + +float function CapturePoint_GetGoalProgress( entity hardpoint ) +{ + return GetGlobalNetFloat( "objective" + hardpoint.kv.hardpointGroup + "Progress" ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_riff_floor_is_lava.nut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_riff_floor_is_lava.nut new file mode 100644 index 00000000..b660e89f --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_riff_floor_is_lava.nut @@ -0,0 +1,102 @@ +global function RiffFloorIsLava_Init + +void function RiffFloorIsLava_Init() +{ + AddCallback_OnPlayerRespawned( FloorIsLava_PlayerRespawned ) + + AddSpawnCallback( "env_fog_controller", InitLavaFogController ) + AddCallback_EntitiesDidLoad( CreateCustomSpawns ) +} + +void function InitLavaFogController( entity fogController ) +{ + fogController.kv.fogztop = GetVisibleFogTop() + fogController.kv.fogzbottom = GetVisibleFogBottom() + fogController.kv.foghalfdisttop = "60000" + fogController.kv.foghalfdistbottom = "200" + fogController.kv.fogdistoffset = "0" + fogController.kv.fogdensity = ".85" + + fogController.kv.forceontosky = true + //fogController.kv.foghalfdisttop = "10000" +} + +void function CreateCustomSpawns() +{ + thread CreateCustomSpawns_Threaded() +} + +void function CreateCustomSpawns_Threaded() +{ + WaitEndFrame() // wait for spawns to clear + + float raycastTop = GetLethalFogTop() + 2500.0 + array< vector > raycastPositions + foreach ( entity hardpoint in GetEntArrayByClass_Expensive( "info_hardpoint" ) ) + { + if ( !hardpoint.HasKey( "hardpointGroup" ) ) + continue + + //if ( hardpoint.kv.hardpointGroup != "A" && hardpoint.kv.hardpointGroup != "B" && hardpoint.kv.hardpointGroup != "C" ) + if ( hardpoint.kv.hardpointGroup != "B" ) // roughly map center + continue + + vector pos = hardpoint.GetOrigin() + for ( int x = -2000; x < 2000; x += 200 ) + for ( int y = -2000; y < 2000; y += 200 ) + raycastPositions.append( < x, y, raycastTop > ) + } + + int validSpawnsCreated = 0 + foreach ( vector raycastPos in raycastPositions ) + { + //vector hardpoint = validHardpoints[ RandomInt( validHardpoints.len() ) ].GetOrigin() + //float a = RandomFloat( 1 ) * 2 * PI + //float r = 1000.0 * sqrt( RandomFloat( 1 ) ) + // + //vector castStart = < hardpoint.x + r * cos( a ), hardpoint.y + r * sin( a ), > + //vector castEnd = < hardpoint.x + r * cos( a ), hardpoint.y + r * sin( a ), GetLethalFogBottom() > + + TraceResults trace = TraceLine( raycastPos, < raycastPos.x, raycastPos.y, GetLethalFogBottom() >, [], TRACE_MASK_SOLID, TRACE_COLLISION_GROUP_NONE ) // should only hit world + print( "raycast: " + trace.endPos ) + if ( trace.endPos.z >= GetLethalFogTop() ) + { + print( "creating floor is lava spawn at " + trace.endPos ) + validSpawnsCreated++ + + // valid spot, create a spawn + entity spawnpoint = CreateEntity( "info_spawnpoint_human" ) + spawnpoint.SetOrigin( trace.endPos ) + spawnpoint.kv.ignoreGamemode = 1 + DispatchSpawn( spawnpoint ) + } + } +} + +void function FloorIsLava_PlayerRespawned( entity player ) +{ + thread FloorIsLava_ThinkForPlayer( player ) +} + +void function FloorIsLava_ThinkForPlayer( entity player ) +{ + player.EndSignal( "OnDestroy" ) + player.EndSignal( "OnDeath" ) + + while ( true ) + { + WaitFrame() + + if ( player.GetOrigin().z < GetLethalFogTop() ) + { + // do damage + float damageMultiplier = 0.08 + if ( player.IsTitan() ) + damageMultiplier *= 0.05 + + player.TakeDamage( player.GetMaxHealth() * damageMultiplier, null, null, { damageSourceId = eDamageSourceId.floor_is_lava } ) + + wait 0.1 + } + } +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_spawnpoints.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_spawnpoints.gnut new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/_spawnpoints.gnut diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/sh_gamemodes.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/sh_gamemodes.gnut new file mode 100644 index 00000000..9114fcad --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/sh_gamemodes.gnut @@ -0,0 +1,819 @@ + +global function GameModes_Init + +global function GameMode_Create +global function GameMode_SetName +global function GameMode_SetGameModeAttackAnnouncement +global function GameMode_SetGameModeDefendAnnouncement +global function GameMode_SetAttackDesc +global function GameMode_SetDefendDesc +global function GameMode_SetIcon +global function GameMode_SetDefaultScoreLimits +global function GameMode_AddScoreboardColumnData +global function GameMode_SetGameModeAnnouncement +global function GameMode_SetDefaultTimeLimits +global function GameMode_SetDesc +global function GameMode_SetColor +global function GameMode_SetSuddenDeath + +global function GameMode_GetScoreLimit +global function GameMode_GetRoundScoreLimit +global function GameMode_GetTimeLimit +global function GameMode_GetRoundTimeLimit +global function GameMode_GetGameModeAnnouncement +global function GameMode_GetGameModeAttackAnnouncement +global function GameMode_GetGameModeDefendAnnouncement +global function GameMode_GetDesc +global function GameMode_GetName +global function GameMode_GetIcon +global function GameMode_GetColor +global function GameMode_GetAttackDesc +global function GameMode_GetDefendDesc +global function GameMode_GetPilotSpawnpointsRatingFunc +global function GameMode_GetTitanSpawnpointsRatingFunc +global function GameMode_GetScoreCompareFunc +global function GameMode_GetSuddenDeathEnabled +global function GameMode_GetEvacEnabled +global function GameMode_GetGameEndingWarning +global function GameMode_GetGameEndingConversation +global function GameMode_GetScoreboardColumnTitles +global function GameMode_GetScoreboardColumnScoreTypes +global function GameMode_GetScoreboardColumnNumDigits +global function GameMode_GetCustomIntroAnnouncement +global function GameMode_RunServerInits +global function GameMode_RunClientInits +global function GameMode_RunSharedInits +global function GameMode_IsDefined + +global function GameMode_AddServerInit +global function GameMode_AddClientInit +global function GameMode_AddSharedInit +global function GameMode_SetScoreCompareFunc +global function GameMode_SetPilotSpawnpointsRatingFunc +global function GameMode_SetTitanSpawnpointsRatingFunc +global function GameMode_SetCustomIntroAnnouncement + +global function GameMode_GetGameModeId + +global function GameMode_SetEvacEnabled + +global function GameMode_GetLoadoutSelectTime + +global struct GamemodeSettings +{ + string name = "" + string name_localized = "Undefined Game Mode" + string desc_localized = "Undefined Game Mode Description" + string desc_attack = "" + string desc_defend = "" + string gameModeAnnoucement = "" + string gameModeAttackAnnoucement = "" + string gameModeDefendAnnoucement = "" + asset icon = $"ui/menu/playlist/classic" + array<int> color = [127, 127, 127, 255] + array< void functionref() > serverInits + array< void functionref() > clientInits + array< void functionref() > sharedInits + void functionref( int, array<entity>, int, entity ) pilotSpawnpointRatingFunc + void functionref( int, array<entity>, int, entity ) titanSpawnpointRatingFunc + IntFromEntityCompare scoreCompareFunc + int defaultScoreLimit = 100 + int defaultTimeLimit = 10 + int defaultRoundScoreLimit = 5 + float defaultRoundTimeLimit = 5.0 + bool evacEnabled = true + string gameModeEndingWarning = "#GAMEMODE_END_IN_N_SECONDS" + string gameModeEndingConversation = "" + bool suddenDeathEnabled = false + array<string> scoreboardColumnTitles + array<int> scoreboardColumnScoreTypes + array<int> scoreboardColumnNumDigits + void functionref(entity) customIntroAnnouncementFunc +} + + + +// Don't remove items from this list once the game is in production +// Durango online analytics needs the numbers for each mode to stay the same +// DO NOT CHANGE THESE VALUES AFTER THEY HAVE GONE LIVE +global enum eGameModes +{ + invalid = -1, + TEAM_DEATHMATCH_ID = 0, + CAPTURE_POINT_ID = 1, + ATTRITION_ID = 2, + CAPTURE_THE_FLAG_ID = 3, + MARKED_FOR_DEATH_ID = 4, + LAST_TITAN_STANDING_ID = 5, + WINGMAN_LAST_TITAN_STANDING_ID = 6, + PILOT_SKIRMISH_ID = 7, + MARKED_FOR_DEATH_PRO_ID = 8, + COOPERATIVE_ID = 9, + GAMEMODE_SP_ID = 10, + TITAN_BRAWL_ID = 11, + FFA_ID = 12, + PROTOTYPE2 = 13, + WINGMAN_PILOT_SKIRMISH_ID = 14, + PROTOTYPE3 = 15, + PROTOTYPE4 = 16, + FREE_AGENCY_ID = 17, + PROTOTYPE6 = 18, + COLISEUM_ID = 19, + PROTOTYPE7 = 20, + AI_TDM_ID = 21, + PROTOTYPE8 = 22, + PROTOTYPE9 = 23, + SPEEDBALL_ID = 24, + PROTOTYPE10 = 25, + PROTOTYPE11 = 26, + PROTOTYPE12 = 27, + FD_ID = 28, + PROTOTYPE14 = 29, +} + +const table<string, int> gameModesStringToIdMap = { + [ TEAM_DEATHMATCH ] = eGameModes.TEAM_DEATHMATCH_ID, + [ PILOT_SKIRMISH ] = eGameModes.PILOT_SKIRMISH_ID, + [ CAPTURE_POINT ] = eGameModes.CAPTURE_POINT_ID, + [ ATTRITION ] = eGameModes.ATTRITION_ID, + [ CAPTURE_THE_FLAG ] = eGameModes.CAPTURE_THE_FLAG_ID, + [ LAST_TITAN_STANDING ] = eGameModes.LAST_TITAN_STANDING_ID, + [ GAMEMODE_SP ] = eGameModes.GAMEMODE_SP_ID, + [ FFA ] = eGameModes.FFA_ID, + [ COLISEUM ] = eGameModes.COLISEUM_ID, + [ AI_TDM ] = eGameModes.AI_TDM_ID, + [ SPEEDBALL ] = eGameModes.SPEEDBALL_ID, + [ MARKED_FOR_DEATH ] = eGameModes.MARKED_FOR_DEATH_ID, + [ TITAN_BRAWL ] = eGameModes.TITAN_BRAWL_ID, + [ FREE_AGENCY ] = eGameModes.FREE_AGENCY_ID, + [ FD ] = eGameModes.FD_ID, + [ FD_EASY ] = eGameModes.FD_ID, + [ FD_NORMAL ] = eGameModes.FD_ID, + [ FD_HARD ] = eGameModes.FD_ID, + [ FD_MASTER ] = eGameModes.FD_ID, + [ FD_INSANE ] = eGameModes.FD_ID, +} + +struct +{ + table< string, GamemodeSettings > gameModeDefs +} file + +void function GameModes_Init() +{ + string gameMode + + gameMode = GAMEMODE_SP + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#GAMEMODE_SOLO" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/coop" ) //HACK TODO: get a sp icon + GameMode_SetDesc( gameMode, "#GAMEMODE_SOLO_HINT" ) + GameMode_SetDefaultScoreLimits( gameMode, 0, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 0, 0.0 ) + + gameMode = CAPTURE_POINT + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_hardpoint" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "hp_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_CP" ) + #endif + GameMode_SetDesc( gameMode, "#PL_hardpoint_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/cp" ) + GameMode_SetDefaultScoreLimits( gameMode, 500, 500 ) + GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_ASSAULT", PGS_ASSAULT_SCORE, 4 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEFENSE", PGS_DEFENSE_SCORE, 4 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 ) + GameMode_SetColor( gameMode, [46, 188, 180, 255] ) + + gameMode = LAST_TITAN_STANDING + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_last_titan_standing" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "lts_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_LTS" ) + #endif + GameMode_SetDesc( gameMode, "#PL_last_titan_standing_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/lts" ) + GameMode_SetDefaultScoreLimits( gameMode, 0, 4 ) + GameMode_SetDefaultTimeLimits( gameMode, 5, 4.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_DAMAGE", PGS_ASSAULT_SCORE, 6 ) + GameMode_SetColor( gameMode, [223, 94, 0, 255] ) + + gameMode = ATTRITION + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_attrition" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "bh_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_AT" ) + #endif + GameMode_SetDesc( gameMode, "#PL_attrition_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/at" ) + GameMode_SetDefaultScoreLimits( gameMode, 5000, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 4 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_BONUS", PGS_SCORE, 4 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 ) + GameMode_SetColor( gameMode, [88, 172, 67, 255] ) + + gameMode = TEAM_DEATHMATCH + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_pilot_hunter" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "phunt_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_TDM" ) + #endif + GameMode_SetDesc( gameMode, "#PL_pilot_hunter_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" ) + GameMode_SetDefaultScoreLimits( gameMode, 50, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_ASSISTS", PGS_ASSISTS, 2 ) + GameMode_SetColor( gameMode, [212, 83, 152, 255] ) + + gameMode = AI_TDM + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_aitdm" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "gnrc_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_TDM" ) + #endif + GameMode_SetDesc( gameMode, "#PL_aitdm_hint" ) + GameMode_SetIcon( gameMode, FFA_MODE_ICON ) + GameMode_SetDefaultScoreLimits( gameMode, 1, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 3 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 1 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_GRUNT_KILLS", PGS_NPC_KILLS, 2 ) + // GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 ) + GameMode_SetColor( gameMode, [200, 40, 40, 255] ) + + gameMode = COLISEUM + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_coliseum" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "gnrc_modeDesc" ) //TODO: This is just the mode name as opposed to instructions... + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_PS" ) + #endif + GameMode_SetDesc( gameMode, "#PL_coliseum_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" ) + GameMode_SetDefaultScoreLimits( gameMode, 15, 2 ) + GameMode_SetDefaultTimeLimits( gameMode, 0, 4.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 ) + GameMode_SetColor( gameMode, [151, 71, 175, 255] ) + + gameMode = PILOT_SKIRMISH + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_pilot_skirmish" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "pvp_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_PS" ) + #endif + GameMode_SetDesc( gameMode, "#PL_pilot_skirmish_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" ) + GameMode_SetDefaultScoreLimits( gameMode, 100, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_ASSISTS", PGS_ASSISTS, 2 ) + GameMode_SetColor( gameMode, [207, 191, 59, 255] ) + + gameMode = CAPTURE_THE_FLAG + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_capture_the_flag" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "ctf_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_CTF" ) + #endif + GameMode_SetDesc( gameMode, "#PL_capture_the_flag_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/ctf" ) + GameMode_SetSuddenDeath( gameMode, true ) + GameMode_SetDefaultScoreLimits( gameMode, 0, 5 ) + GameMode_SetDefaultTimeLimits( gameMode, 0, 3.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_CAPTURES", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_RETURNS", PGS_DEFENSE_SCORE, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 ) + GameMode_SetColor( gameMode, [61, 117, 193, 255] ) + + gameMode = FFA + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_ffa" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "ffa_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_FFA" ) + #endif + GameMode_SetDesc( gameMode, "#PL_ffa_hint" ) + GameMode_SetIcon( gameMode, FFA_MODE_ICON ) + GameMode_SetDefaultScoreLimits( gameMode, 10, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 10, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 2 ) + GameMode_SetColor( gameMode, [147, 204, 57, 255] ) + + gameMode = FREE_AGENCY + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_free_agents" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "freea_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_FREE_AGENCY" ) + #endif + GameMode_SetDesc( gameMode, "#PL_free_agents_hint" ) + GameMode_SetIcon( gameMode, FFA_MODE_ICON ) + GameMode_SetDefaultScoreLimits( gameMode, 10, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SCORE", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_PILOT_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_KILLS", PGS_TITAN_KILLS, 2 ) + GameMode_SetColor( gameMode, [127, 127, 127, 255] ) + + gameMode = SPEEDBALL + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_speedball" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "gnrc_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_CTF" ) + #endif + GameMode_SetDesc( gameMode, "#PL_speedball_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/ctf" ) + GameMode_SetDefaultScoreLimits( gameMode, 0, 5 ) + GameMode_SetDefaultTimeLimits( gameMode, 0, 1.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_FLAGS_SECURED", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 ) + GameMode_SetColor( gameMode, [225, 141, 8, 255] ) + + gameMode = MARKED_FOR_DEATH + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_marked_for_death" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "mfd_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_MFD" ) + #endif + GameMode_SetDesc( gameMode, "#PL_marked_for_death_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/mfd" ) + GameMode_SetDefaultScoreLimits( gameMode, 10, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 10, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_MFD_SCORE", PGS_ASSAULT_SCORE, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_MFD_MARKS_OUTLASTED", PGS_DEFENSE_SCORE, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_KILLS, 2 ) + GameMode_SetColor( gameMode, [127, 127, 127, 255] ) + + gameMode = TITAN_BRAWL + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_titan_brawl" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "lts_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_TTDM" ) + #endif + GameMode_SetDesc( gameMode, "#PL_titan_brawl_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/lts" ) + GameMode_SetDefaultScoreLimits( gameMode, 30, 0 ) + GameMode_SetDefaultTimeLimits( gameMode, 15, 0.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_KILLS", PGS_PILOT_KILLS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_DEATHS", PGS_DEATHS, 2 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TITAN_DAMAGE", PGS_ASSAULT_SCORE, 6 ) + GameMode_SetColor( gameMode, [83, 212, 152, 255] ) + + gameMode = FD + GameMode_Create( gameMode ) + GameMode_SetName( gameMode, "#PL_fd" ) + #if FACTION_DIALOGUE_ENABLED + GameMode_SetGameModeAnnouncement( gameMode, "fd_modeDesc" ) + #else + GameMode_SetGameModeAnnouncement( gameMode, "GameModeAnnounce_PS" ) + #endif + GameMode_SetDesc( gameMode, "#PL_fd_hint" ) + GameMode_SetIcon( gameMode, $"ui/menu/playlist/tdm" ) + GameMode_SetSuddenDeath( gameMode, true ) + GameMode_SetDefaultScoreLimits( gameMode, 0, 5 ) + GameMode_SetDefaultTimeLimits( gameMode, 0, 5.0 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_TOTAL_SCORE", PGS_DETONATION_SCORE, 4 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_COMBAT_SCORE", PGS_ASSAULT_SCORE, 4 ) + //GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_HEALING_SCORE", PGS_DISTANCE_SCORE, 3 ) + GameMode_AddScoreboardColumnData( gameMode, "#SCOREBOARD_SUPPORT_SCORE", PGS_DEFENSE_SCORE, 4 ) + + #if DEVSCRIPTS + DevGameModes_Init() + #endif + + #if SERVER || CLIENT + // add modes/maps/playlists to private lobby that aren't there by default + PrivateMatchModesInit() + + InitCustomGamemodes() // do custom gamemode callbacks + GameModes_Init_SV_CL() + #endif + + //// + GameMode_VerifyModes() +} + +// TODO: scoreboards + +/************************************************************* + Setters +*************************************************************/ + +GamemodeSettings function GameMode_Create( string gameModeName ) +{ + Assert( !(gameModeName in file.gameModeDefs), "Gametype already defined!" ) + + GamemodeSettings settings + file.gameModeDefs[gameModeName] <- settings + + return file.gameModeDefs[gameModeName] +} + +void function GameMode_SetName( string gameModeName, string nameText ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut (" + gameModeName + ")" ) + file.gameModeDefs[gameModeName].name_localized = nameText +} + +void function GameMode_SetGameModeAnnouncement( string gameModeName, string gameModeAnnoucement ) //Note: Still need to register the conversation +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].gameModeAnnoucement = gameModeAnnoucement +} + +void function GameMode_SetGameModeAttackAnnouncement( string gameModeName, string gameModeAttackAnnoucement ) //Note: Still need to register the conversation +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].gameModeAttackAnnoucement = gameModeAttackAnnoucement +} + +void function GameMode_SetGameModeDefendAnnouncement( string gameModeName, string gameModeDefendAnnoucement ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) //Note: Still need to register the conversation + file.gameModeDefs[gameModeName].gameModeDefendAnnoucement = gameModeDefendAnnoucement +} + +void function GameMode_SetDesc( string gameModeName, string descText ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].desc_localized = descText +} + +void function GameMode_SetAttackDesc( string gameModeName, string descText ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].desc_attack = descText +} + +void function GameMode_SetDefendDesc( string gameModeName, string descText ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].desc_defend = descText +} + +void function GameMode_SetIcon( string gameModeName, asset icon ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].icon = icon +} + +void function GameMode_SetColor( string gameModeName, array<int> color ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].color = color +} + +void function GameMode_SetSuddenDeath( string gameModeName, bool state ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].suddenDeathEnabled = state +} + +void function GameMode_AddServerInit( string gameModeName, void functionref() func ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].serverInits.append( func ) +} + +void function GameMode_AddClientInit( string gameModeName, void functionref() func ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].clientInits.append( func ) +} + +void function GameMode_AddSharedInit( string gameModeName, void functionref() func ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].sharedInits.append( func ) +} + +void function GameMode_SetPilotSpawnpointsRatingFunc( string gameModeName, void functionref( int, array<entity>, int, entity ) func ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].pilotSpawnpointRatingFunc = func +} + +void function GameMode_SetTitanSpawnpointsRatingFunc( string gameModeName, void functionref( int, array<entity>, int, entity ) func ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].titanSpawnpointRatingFunc = func +} + +void function GameMode_SetScoreCompareFunc( string gameModeName, int functionref( entity, entity ) func ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].scoreCompareFunc = func +} + +void function GameMode_SetDefaultScoreLimits( string gameModeName, int scoreLimit, int roundScoreLimit ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].defaultScoreLimit = scoreLimit + file.gameModeDefs[gameModeName].defaultRoundScoreLimit = roundScoreLimit +} + +void function GameMode_SetDefaultTimeLimits( string gameModeName, int timeLimit, float roundTimeLimit ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].defaultTimeLimit = timeLimit + file.gameModeDefs[gameModeName].defaultRoundTimeLimit = roundTimeLimit +} + +void function GameMode_AddScoreboardColumnData( string gameModeName, string title, int scoreType, int numDigits ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].scoreboardColumnTitles.append( title ) + file.gameModeDefs[gameModeName].scoreboardColumnScoreTypes.append( scoreType ) + file.gameModeDefs[gameModeName].scoreboardColumnNumDigits.append( numDigits ) +} + +void function GameMode_SetEvacEnabled( string gameModeName, bool value ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].evacEnabled = value +} + +void function GameMode_SetGameEndingWarning( string gameModeName, string warning ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].gameModeEndingWarning = warning +} + +void function GameMode_SetGameEndingConversation( string gameModeName, string conversation ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].gameModeEndingConversation = conversation +} + +void function GameMode_SetCustomIntroAnnouncement( string gameModeName, void functionref(entity) func ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + file.gameModeDefs[gameModeName].customIntroAnnouncementFunc = func +} + +/************************************************************* + Getters +*************************************************************/ + +int function GameMode_GetScoreLimit( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return GetCurrentPlaylistVarInt( "scorelimit", file.gameModeDefs[gameModeName].defaultScoreLimit ) +} + +int function GameMode_GetRoundScoreLimit( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return GetCurrentPlaylistVarInt( "roundscorelimit", file.gameModeDefs[gameModeName].defaultRoundScoreLimit ) +} + +int function GameMode_GetTimeLimit( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return GetCurrentPlaylistVarInt( "timelimit", file.gameModeDefs[gameModeName].defaultTimeLimit ) +} + +float function GameMode_GetRoundTimeLimit( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return GetCurrentPlaylistVarFloat( "roundtimelimit", file.gameModeDefs[gameModeName].defaultRoundTimeLimit ) +} + +string function GameMode_GetGameModeAnnouncement( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].gameModeAnnoucement +} + +string function GameMode_GetGameModeAttackAnnouncement( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].gameModeAttackAnnoucement +} + +string function GameMode_GetGameModeDefendAnnouncement( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].gameModeDefendAnnoucement +} + +string function GameMode_GetDesc( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].desc_localized +} + +string function GameMode_GetName( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].name_localized +} + +asset function GameMode_GetIcon( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].icon +} + +array<int> function GameMode_GetColor( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].color +} + +string function GameMode_GetAttackDesc( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].desc_attack +} + +string function GameMode_GetDefendDesc( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].desc_defend +} + +void functionref( int, array<entity>, int, entity ) function GameMode_GetPilotSpawnpointsRatingFunc( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + Assert( file.gameModeDefs[gameModeName].pilotSpawnpointRatingFunc != null, "No respawn func set for " + gameModeName ) + return file.gameModeDefs[gameModeName].pilotSpawnpointRatingFunc +} + +void functionref( int, array<entity>, int, entity ) function GameMode_GetTitanSpawnpointsRatingFunc( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + Assert( file.gameModeDefs[gameModeName].titanSpawnpointRatingFunc != null, "No respawn func set for " + gameModeName ) + return file.gameModeDefs[gameModeName].titanSpawnpointRatingFunc +} + +IntFromEntityCompare function GameMode_GetScoreCompareFunc( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].scoreCompareFunc +} + +bool function GameMode_GetSuddenDeathEnabled( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].suddenDeathEnabled +} + +bool function GameMode_GetEvacEnabled( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].evacEnabled +} + +string function GameMode_GetGameEndingWarning( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].gameModeEndingWarning +} + +string function GameMode_GetGameEndingConversation( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].gameModeEndingConversation +} + +array<string> function GameMode_GetScoreboardColumnTitles( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].scoreboardColumnTitles +} + +array<int> function GameMode_GetScoreboardColumnScoreTypes( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].scoreboardColumnScoreTypes +} + +array<int> function GameMode_GetScoreboardColumnNumDigits( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].scoreboardColumnNumDigits +} + +void functionref(entity) function GameMode_GetCustomIntroAnnouncement( string gameModeName ) +{ + Assert( gameModeName in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + return file.gameModeDefs[gameModeName].customIntroAnnouncementFunc +} + +/************************************************************* + +*************************************************************/ +void function GameMode_RunServerInits() +{ + Assert( GAMETYPE in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + + foreach ( initFunc in file.gameModeDefs[GAMETYPE].serverInits ) + { + initFunc() + } +} + +void function GameMode_RunClientInits() +{ + Assert( GAMETYPE in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + + foreach ( initFunc in file.gameModeDefs[GAMETYPE].clientInits ) + { + initFunc() + } +} + +void function GameMode_RunSharedInits() +{ + Assert( GAMETYPE in file.gameModeDefs, "No MP Gametype specified in _settings.nut" ) + + foreach ( initFunc in file.gameModeDefs[GAMETYPE].sharedInits ) + { + initFunc() + } +} + +void function GameMode_VerifyModes() +{ + foreach ( gameModeName, gameModeData in file.gameModeDefs ) + { + int gameModeId = GameMode_GetGameModeId( gameModeName ) + bool foundGameModeIdString = false + foreach ( idString, gameModeEnumId in eGameModes ) + { + if ( gameModeEnumId != gameModeId ) + continue + + foundGameModeIdString = true + break + } + Assert( foundGameModeIdString, "GAMEMODE not defined properly in eGameModes!" ) + + GAMETYPE_TEXT[gameModeName] <- gameModeData.name_localized + GAMETYPE_DESC[gameModeName] <- gameModeData.desc_localized + GAMETYPE_ICON[gameModeName] <- gameModeData.icon + GAMETYPE_COLOR[gameModeName] <- gameModeData.color + #if CLIENT + PrecacheHUDMaterial( GAMETYPE_ICON[gameModeName] ) + #endif + } +} + +int function GameMode_GetGameModeId( string gameModeName ) +{ + if ( gameModeName in gameModesStringToIdMap ) + return gameModesStringToIdMap[gameModeName] + + #if DEVSCRIPTS + if ( gameModeName in devGameModesStringToIdMap ) + return devGameModesStringToIdMap[gameModeName] + #endif + + Assert( false, "GAMEMODE " + gameModeName + " not defined in gameModesStringToIdMap" ) + + return 0 +} + +bool function GameMode_IsDefined( string gameModeName ) +{ + return (gameModeName in file.gameModeDefs) +} + +float function GameMode_GetLoadoutSelectTime() +{ + return GetCurrentPlaylistVarFloat( "pick_loadout_time", 5.0 ) +}
\ No newline at end of file diff --git a/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/sh_gamemodes_custom.gnut b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/sh_gamemodes_custom.gnut new file mode 100644 index 00000000..51f8bf9e --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/gamemodes/sh_gamemodes_custom.gnut @@ -0,0 +1,20 @@ +untyped +global function InitCustomGamemodes +global function AddCallback_OnCustomGamemodesInit + +struct { + array<void functionref()> onCustomGamemodesInitCallbacks +} file + +void function InitCustomGamemodes() +{ + print( "InitCustomGamemodes" ) + + foreach ( void functionref() callback in file.onCustomGamemodesInitCallbacks ) + callback() +} + +void function AddCallback_OnCustomGamemodesInit( void functionref() callback ) +{ + file.onCustomGamemodesInitCallbacks.append( callback ) +}
\ No newline at end of file |