From 31c8a052e8f3cdccedb7f6f8d2bd11678189001a Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Sun, 27 Jun 2021 20:55:35 +0100 Subject: added ttdm, more scoreevents and ctf comp --- .../scripts/vscripts/mp/_base_gametype_mp.gnut | 61 ++++++++---- .../scripts/vscripts/mp/_classic_mp.nut | 2 +- .../vscripts/mp/_classic_mp_dropship_intro.gnut | 2 +- .../scripts/vscripts/mp/_classic_mp_no_intro.gnut | 56 ++++++++--- .../scripts/vscripts/mp/_gamestate_mp.nut | 46 +++------ .../scripts/vscripts/mp/_score.nut | 103 +++++++++++++++++++-- .../vscripts/mp/levels/_lf_maps_shared.gnut | 2 +- .../scripts/vscripts/mp/spawn.nut | 11 +++ 8 files changed, 212 insertions(+), 71 deletions(-) (limited to 'Northstar.CustomServers/scripts/vscripts/mp') diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut b/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut index 69fcfb50..5a684f20 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_base_gametype_mp.gnut @@ -13,6 +13,7 @@ global function TryGameModeAnnouncement global function SetKillcamsEnabled global function KillcamsEnabled global function SetPlayerDeathsHidden +global function TrackTitanDamageInPlayerGameStat global function ShouldEntTakeDamage_SPMP global function GetTitanBuildTime @@ -21,6 +22,7 @@ global function TitanPlayerHotDropsIntoLevel struct { bool killcamsEnabled = true bool playerDeathsHidden = false + int titanDamageGameStat = -1 entity intermissionCamera array specCams @@ -35,6 +37,9 @@ void function BaseGametype_Init_MPSP() AddClientCommandCallback( "spec_next", ClientCommandCallback_spec_next ) AddClientCommandCallback( "spec_prev", ClientCommandCallback_spec_prev ) AddClientCommandCallback( "spec_mode", ClientCommandCallback_spec_mode ) + + AddDamageCallback( "player", AddToTitanDamageStat ) + AddDamageCallback( "npc_titan", AddToTitanDamageStat ) } void function SetIntermissionCamera( entity camera ) @@ -222,10 +227,6 @@ void function CodeCallback_OnPlayerRespawned( entity player ) void function CodeCallback_OnPlayerKilled( entity player, var damageInfo ) { PlayerOrNPCKilled( player, damageInfo ) - - if ( player.IsTitan() ) - SoulDies( player.GetTitanSoul(), damageInfo ) // cleanup some titan stuff, no idea where else to put this - thread PostDeathThread_MP( player, damageInfo ) } @@ -253,6 +254,11 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga player.SetNoTargetSmartAmmo( false ) player.ClearExtraWeaponMods() + player.AddToPlayerGameStat( PGS_DEATHS, 1 ) + + if ( player.IsTitan() ) + SoulDies( player.GetTitanSoul(), damageInfo ) // cleanup some titan stuff, no idea where else to put this + ClearRespawnAvailable( player ) OnThreadEnd( function() : ( player ) @@ -285,24 +291,24 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga thread TrackDestroyTimeForReplay( attacker, replayTracker ) int damageSource = DamageInfo_GetDamageSourceIdentifier( damageInfo ) - if ( damageSource == eDamageSourceId.fall ) - { - // this is straight up just incorrect lol, based off tf1 stuff - - player.SetObserverModeStaticPosition( player.GetOrigin() ) - player.SetObserverModeStaticAngles( player.GetVelocity() * -1 ) - - player.StartObserverMode( OBS_MODE_STATIC_LOCKED ) - player.SetObserverTarget( null ) - } - else - { + //if ( damageSource == eDamageSourceId.fall ) + //{ + // // this is straight up just incorrect lol, based off tf1 stuff + // + // player.SetObserverModeStaticPosition( player.GetOrigin() ) + // player.SetObserverModeStaticAngles( player.GetVelocity() * -1 ) + // + // player.StartObserverMode( OBS_MODE_STATIC_LOCKED ) + // player.SetObserverTarget( null ) + //} + //else + //{ player.StartObserverMode( OBS_MODE_DEATHCAM ) if ( ShouldSetObserverTarget( attacker ) ) player.SetObserverTarget( attacker ) else player.SetObserverTarget( null ) - } + //} if ( !file.playerDeathsHidden ) Remote_CallFunction_NonReplay( player, "ServerCallback_YouDied", attacker.GetEncodedEHandle(), GetHealthFrac( attacker ), methodOfDeath ) @@ -356,7 +362,7 @@ void function PostDeathThread_MP( entity player, var damageInfo ) // based on ga player.WaitSignal( "RespawnMe" ) // set in base_gametype: ClientCommand_RespawnPlayer ClearRespawnAvailable( player ) // need so the respawn icon doesn't show for like a frame on next death - if ( ( expect bool( player.GetPersistentVar( "spawnAsTitan" ) ) && IsTitanAvailable( player ) ) || Riff_SpawnAsTitan() == 1 ) // spawn as titan + if ( ( expect bool( player.GetPersistentVar( "spawnAsTitan" ) ) && IsTitanAvailable( player ) ) || ( Riff_SpawnAsTitan() > 0 && Riff_ShouldSpawnAsTitan( player ) ) ) // spawn as titan thread RespawnAsTitan( player ) else // spawn as pilot RespawnAsPilot( player ) @@ -562,6 +568,25 @@ void function SetPlayerDeathsHidden( bool hidden ) file.playerDeathsHidden = hidden } +void function TrackTitanDamageInPlayerGameStat( int playerGameStat ) +{ + file.titanDamageGameStat = playerGameStat +} + +// this should be generic, not restricted to a specific gamemode +void function AddToTitanDamageStat( entity victim, var damageInfo ) +{ + if ( !victim.IsTitan() || file.titanDamageGameStat == -1 ) + return + + // todo: this needs to not count selfdamage + entity attacker = DamageInfo_GetAttacker( damageInfo ) + float amount = DamageInfo_GetDamage( damageInfo ) + + if ( attacker.IsPlayer() && attacker != victim ) + attacker.AddToPlayerGameStat( PGS_ASSAULT_SCORE, amount ) // titan damage on +} + // stuff to change later diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp.nut b/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp.nut index d6ac8f55..ac8a397f 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp.nut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp.nut @@ -22,7 +22,7 @@ void function ClassicMP_TryDefaultIntroSetup() if ( file.introSetupFunc == null ) { if ( IsFFAGame() ) - ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, NOINTRO_INTRO_LENGTH ) + ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() ) else ClassicMP_SetCustomIntro( ClassicMP_DefaultDropshipIntro_Setup, DROPSHIP_INTRO_LENGTH ) } diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut b/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut index 20455c69..d4712cf1 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_dropship_intro.gnut @@ -104,7 +104,7 @@ void function OnPrematchStart() array dropshipSpawns = GetEntArrayByClass_Expensive( "info_spawnpoint_dropship_start" ) foreach ( entity dropshipSpawn in dropshipSpawns ) { - if ( GameModeRemove( dropshipSpawn ) ) + if ( GameModeRemove( dropshipSpawn ) || ( GetSpawnpointGamemodeOverride() != GAMETYPE && dropshipSpawn.kv[ "gamemode_" + GetSpawnpointGamemodeOverride() ] == "0" ) ) continue // todo: possibly make this only spawn dropships if we've got enough players to need them diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_no_intro.gnut b/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_no_intro.gnut index 50e9e9a0..106f867b 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_no_intro.gnut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_classic_mp_no_intro.gnut @@ -1,7 +1,11 @@ untyped global function ClassicMP_DefaultNoIntro_Setup -global const float NOINTRO_INTRO_LENGTH = 10.0 +global function ClassicMP_DefaultNoIntro_GetLength + +global const float NOINTRO_INTRO_PILOT_LENGTH = 10.0 +global const float TITAN_DROP_SPAWN_INTRO_LENGTH = 0.0 // this intro shouldn't have a countdown visually, so we have to set the length of this intro to 0 +global const float TITAN_DROP_SPAWN_INTRO_REAL_LENGTH = 2.0 // we wait roughly this long during the intro, even when it's technically over void function ClassicMP_DefaultNoIntro_Setup() { @@ -9,20 +13,35 @@ void function ClassicMP_DefaultNoIntro_Setup() AddCallback_GameStateEnter( eGameState.Prematch, ClassicMP_DefaultNoIntro_Start ) } +float function ClassicMP_DefaultNoIntro_GetLength() +{ + if ( ShouldIntroSpawnAsTitan() ) + return TITAN_DROP_SPAWN_INTRO_LENGTH + else + return NOINTRO_INTRO_PILOT_LENGTH + + unreachable +} + void function ClassicMP_DefaultNoIntro_Start() { ClassicMP_OnIntroStarted() foreach ( entity player in GetPlayerArray() ) - thread ClassicMP_DefaultNoIntro_SpawnPlayer( player ) + ClassicMP_DefaultNoIntro_SpawnPlayer( player ) - wait NOINTRO_INTRO_LENGTH - - foreach ( entity player in GetPlayerArray() ) + if ( ShouldIntroSpawnAsTitan() ) + wait TITAN_DROP_SPAWN_INTRO_REAL_LENGTH + else { - player.UnfreezeControlsOnServer() - RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) - TryGameModeAnnouncement( player ) + wait NOINTRO_INTRO_PILOT_LENGTH + + foreach ( entity player in GetPlayerArray() ) + { + player.UnfreezeControlsOnServer() + RemoveCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) + TryGameModeAnnouncement( player ) + } } ClassicMP_OnIntroFinished() @@ -34,13 +53,28 @@ void function ClassicMP_DefaultNoIntro_SpawnPlayer( entity player ) return if ( IsAlive( player ) ) - { player.Die() - WaitFrame() - } + if ( ShouldIntroSpawnAsTitan() ) + thread ClassicMP_DefaultNoIntro_TitanSpawnPlayer( player ) + else + thread ClassicMP_DefaultNoIntro_PilotSpawnPlayer( player ) +} + + +// spawn as pilot for intro +void function ClassicMP_DefaultNoIntro_PilotSpawnPlayer( entity player ) +{ RespawnAsPilot( player ) player.FreezeControlsOnServer() AddCinematicFlag( player, CE_FLAG_CLASSIC_MP_SPAWNING ) ScreenFadeFromBlack( player, 0.5, 0.5 ) +} + +// spawn as titan for intro +void function ClassicMP_DefaultNoIntro_TitanSpawnPlayer( entity player ) +{ + // blocking call + RespawnAsTitan( player, false ) + TryGameModeAnnouncement( player ) } \ No newline at end of file diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut b/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut index 97e06377..bbeb72fb 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_gamestate_mp.nut @@ -417,6 +417,8 @@ void function PlayerWatchesSwitchingSidesKillReplay( entity player, bool doRepla { player.SetPredictionEnabled( false ) // prediction fucks with replays + // delay seems weird for switchingsides? ends literally the frame the flag is collected + entity attacker = file.roundWinningKillReplayAttacker player.SetKillReplayDelay( Time() - replayLength, THIRD_PERSON_KILL_REPLAY_ALWAYS ) player.SetKillReplayInflictorEHandle( attacker.GetEncodedEHandle() ) @@ -452,34 +454,6 @@ void function GameStateEnter_SuddenDeath() SetRespawnsEnabled( false ) } -void function GameStateEnter_SuddenDeath_Threaded() -{ - while ( GetGameState() == eGameState.SuddenDeath ) - { - // todo this really ought to work for ffa in the future - int imcPlayers - int militiaPlayers - - foreach ( entity player in GetPlayerArray() ) - { - if ( IsAlive( player ) ) - { - if ( player.GetTeam() == TEAM_IMC ) - imcPlayers++ - else - militiaPlayers++ - } - } - - if ( imcPlayers == 0 ) - SetWinner( TEAM_MILITIA ) - else if ( militiaPlayers == 0 ) - SetWinner( TEAM_IMC ) - - WaitFrame() - } -} - // eGameState.Postmatch void function GameStateEnter_Postmatch() @@ -502,6 +476,9 @@ void function GameStateEnter_Postmatch_Threaded() void function ForceFadeToBlack( entity player ) { + // todo: check if this is still necessary + player.EndSignal( "OnDestroy" ) + // hack until i figure out what deathcam stuff is causing fadetoblacks to be cleared while ( true ) { @@ -518,8 +495,9 @@ void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) if ( !GamePlayingOrSuddenDeath() ) return - // set round winning killreplay info here if no custom replaydelay - if ( file.roundWinningKillReplayTrackPilotKills && victim != attacker ) + // set round winning killreplay info here if we're tracking pilot kills + // todo: make this not count environmental deaths like falls, unsure how to prevent this + if ( file.roundWinningKillReplayTrackPilotKills && victim != attacker && attacker != svGlobal.worldspawn && IsValid( attacker ) ) { file.roundWinningKillReplayTime = Time() file.roundWinningKillReplayVictim = victim @@ -563,9 +541,10 @@ void function OnTitanKilled( entity victim, var damageInfo ) if ( !GamePlayingOrSuddenDeath() ) return - // set round winning killreplay info here if no custom replaydelay + // set round winning killreplay info here if we're tracking titan kills + // todo: make this not count environmental deaths like falls, unsure how to prevent this entity attacker = DamageInfo_GetAttacker( damageInfo ) - if ( file.roundWinningKillReplayTrackTitanKills && victim != attacker ) + if ( file.roundWinningKillReplayTrackTitanKills && victim != attacker && attacker != svGlobal.worldspawn && IsValid( attacker ) ) { file.roundWinningKillReplayTime = Time() file.roundWinningKillReplayVictim = victim @@ -625,7 +604,8 @@ void function CleanUpEntitiesForRoundEnd() } foreach ( entity npc in GetNPCArray() ) - npc.Die() + if ( IsAlive( npc ) ) + npc.Die() // need this because getnpcarray includes the pettitans we just killed at this point // allow other scripts to clean stuff up too svGlobal.levelEnt.Signal( "CleanUpEntitiesForRoundEnd" ) diff --git a/Northstar.CustomServers/scripts/vscripts/mp/_score.nut b/Northstar.CustomServers/scripts/vscripts/mp/_score.nut index b8ea6074..9f1e5978 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/_score.nut +++ b/Northstar.CustomServers/scripts/vscripts/mp/_score.nut @@ -17,7 +17,14 @@ struct { void function Score_Init() { + AddCallback_OnClientConnected( InitPlayerForScoreEvents ) +} +void function InitPlayerForScoreEvents( entity player ) +{ + player.s.currentKillstreak <- 0 + player.s.lastKillTime <- 0.0 + player.s.currentTimedKillstreak <- 0 } void function AddPlayerScore( entity targetPlayer, string scoreEventName, entity associatedEnt = null, string noideawhatthisis = "", int pointValueOverride = -1 ) @@ -33,13 +40,29 @@ void function AddPlayerScore( entity targetPlayer, string scoreEventName, entity if ( pointValueOverride != -1 ) event.pointValue = pointValueOverride - - float scale = targetPlayer.IsTitan() ? event.coreMeterScalar : 1.0 + + float scale = targetPlayer.IsTitan() ? event.coreMeterScalar : 1.0 + float earnValue = event.earnMeterEarnValue * scale float ownValue = event.earnMeterOwnValue * scale PlayerEarnMeter_AddEarnedAndOwned( targetPlayer, earnValue * scale, ownValue * scale ) + // PlayerEarnMeter_AddEarnedAndOwned handles this scaling by itself, we just need to do this for the visual stuff + float pilotScaleVar = ( expect string ( GetCurrentPlaylistVarOrUseValue( "earn_meter_pilot_multiplier", "1" ) ) ).tofloat() + float titanScaleVar = ( expect string ( GetCurrentPlaylistVarOrUseValue( "earn_meter_titan_multiplier", "1" ) ) ).tofloat() + + if ( targetPlayer.IsTitan() ) + { + earnValue *= titanScaleVar + ownValue *= titanScaleVar + } + else + { + earnValue *= pilotScaleVar + ownValue *= pilotScaleVar + } + Remote_CallFunction_NonReplay( targetPlayer, "ServerCallback_ScoreEvent", event.eventId, event.pointValue, event.displayType, associatedHandle, ownValue, earnValue ) if ( event.displayType & eEventDisplayType.CALLINGCARD ) // callingcardevents are shown to all players @@ -54,24 +77,90 @@ void function AddPlayerScore( entity targetPlayer, string scoreEventName, entity } if ( ScoreEvent_HasConversation( event ) ) - thread Delayed_PlayConversationToPlayer( event.conversation, targetPlayer, event.conversationDelay ) + PlayFactionDialogueToPlayer( event.conversation, targetPlayer ) } void function ScoreEvent_PlayerKilled( entity victim, entity attacker, var damageInfo ) { + // reset killstreaks and stuff + victim.s.currentKillstreak = 0 + victim.s.lastKillTime = 0.0 + victim.s.currentTimedKillstreak = 0 + + victim.p.numberOfDeathsSinceLastKill++ // this is reset on kill + + // have to do this early before we reset victim's player killstreaks + // nemesis when you kill a player that is dominating you + if ( attacker.IsPlayer() && attacker in victim.p.playerKillStreaks && victim.p.playerKillStreaks[ attacker ] == NEMESIS_KILL_REQUIREMENT ) + AddPlayerScore( attacker, "Nemesis" ) + + // reset killstreaks on specific players + foreach ( entity killstreakPlayer, int numKills in victim.p.playerKillStreaks ) + delete victim.p.playerKillStreaks[ killstreakPlayer ] + + if ( victim.IsTitan() ) + ScoreEvent_TitanKilled( victim, attacker, damageInfo ) + + if ( !attacker.IsPlayer() ) + return + + + // pilot kill AddPlayerScore( attacker, "KillPilot", victim ) + // headshot if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_HEADSHOT ) AddPlayerScore( attacker, "Headshot", victim ) - + + // first strike if ( !file.firstStrikeDone ) { file.firstStrikeDone = true AddPlayerScore( attacker, "FirstStrike", attacker ) } - if ( victim.IsTitan() ) - ScoreEvent_TitanKilled( victim, attacker, damageInfo ) + // comeback + if ( attacker.p.numberOfDeathsSinceLastKill >= COMEBACK_DEATHS_REQUIREMENT ) + { + AddPlayerScore( attacker, "Comeback" ) + attacker.p.numberOfDeathsSinceLastKill = 0 + } + + + // untimed killstreaks + attacker.s.currentKillstreak++ + if ( attacker.s.currentKillstreak == 3 ) + AddPlayerScore( attacker, "KillingSpree" ) + else if ( attacker.s.currentKillstreak == 5 ) + AddPlayerScore( attacker, "Rampage" ) + + // increment untimed killstreaks against specific players + if ( !( victim in attacker.p.playerKillStreaks ) ) + attacker.p.playerKillStreaks[ victim ] <- 1 + else + attacker.p.playerKillStreaks[ victim ]++ + + // dominating + if ( attacker.p.playerKillStreaks[ victim ] == DOMINATING_KILL_REQUIREMENT ) + AddPlayerScore( attacker, "Dominating" ) + + + // timed killstreaks + if ( Time() - attacker.s.lastKillTime <= CASCADINGKILL_REQUIREMENT_TIME ) + { + attacker.s.currentTimedKillstreak++ + + if ( attacker.s.currentTimedKillstreak == DOUBLEKILL_REQUIREMENT_KILLS ) + AddPlayerScore( attacker, "DoubleKill" ) + else if ( attacker.s.currentTimedKillstreak == TRIPLEKILL_REQUIREMENT_KILLS ) + AddPlayerScore( attacker, "TripleKill" ) + else if ( attacker.s.currentTimedKillstreak == MEGAKILL_REQUIREMENT_KILLS ) + AddPlayerScore( attacker, "MegaKill" ) + } + else + attacker.s.currentTimedKillstreak = 0 // reset if a kill took too long + + attacker.s.lastKillTime = Time() } void function ScoreEvent_TitanDoomed( entity titan, entity attacker, var damageInfo ) @@ -87,6 +176,8 @@ void function ScoreEvent_TitanDoomed( entity titan, entity attacker, var damageI void function ScoreEvent_TitanKilled( entity victim, entity attacker, var damageInfo ) { // will this handle npc titans with no owners well? i have literally no idea + if ( !attacker.IsPlayer() ) + return if ( attacker.IsTitan() ) AddPlayerScore( attacker, "TitanKillTitan", victim.GetTitanSoul().GetOwner() ) diff --git a/Northstar.CustomServers/scripts/vscripts/mp/levels/_lf_maps_shared.gnut b/Northstar.CustomServers/scripts/vscripts/mp/levels/_lf_maps_shared.gnut index 69ec56fb..d61d6baa 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/levels/_lf_maps_shared.gnut +++ b/Northstar.CustomServers/scripts/vscripts/mp/levels/_lf_maps_shared.gnut @@ -4,5 +4,5 @@ global function SetupLiveFireMaps void function SetupLiveFireMaps() { Riff_ForceTitanAvailability( eTitanAvailability.Never ) - ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, NOINTRO_INTRO_LENGTH ) + ClassicMP_SetCustomIntro( ClassicMP_DefaultNoIntro_Setup, ClassicMP_DefaultNoIntro_GetLength() ) } \ No newline at end of file diff --git a/Northstar.CustomServers/scripts/vscripts/mp/spawn.nut b/Northstar.CustomServers/scripts/vscripts/mp/spawn.nut index b7a50453..26e4c713 100644 --- a/Northstar.CustomServers/scripts/vscripts/mp/spawn.nut +++ b/Northstar.CustomServers/scripts/vscripts/mp/spawn.nut @@ -7,6 +7,7 @@ global function SetSpawnsUseFrontline global function SetRespawnsEnabled global function RespawnsEnabled global function SetSpawnpointGamemodeOverride +global function GetSpawnpointGamemodeOverride global function CreateNoSpawnArea global function DeleteNoSpawnArea @@ -121,6 +122,16 @@ void function SetSpawnpointGamemodeOverride( string gamemode ) file.spawnpointGamemodeOverride = gamemode } +string function GetSpawnpointGamemodeOverride() +{ + if ( file.spawnpointGamemodeOverride != "" ) + return file.spawnpointGamemodeOverride + else + return GAMETYPE + + unreachable +} + void function SetSpawnsUseFrontline( bool useFrontline ) { file.frontlineBased = useFrontline -- cgit v1.2.3