1338 lines
40 KiB
Ucode
1338 lines
40 KiB
Ucode
class stub_GT extends KFGameType;
|
|
|
|
|
|
var transient array<string> sCachedPlayersInfo;
|
|
var transient float fDelay;
|
|
|
|
// killzeds
|
|
var transient array<KFMonster> Monsters;
|
|
|
|
// InitGame
|
|
var string CmdLine;
|
|
|
|
// camera fix
|
|
var bool bBossView;
|
|
var float BossViewBackTime;
|
|
var transient ZombieBoss BossArray;
|
|
|
|
|
|
//=============================================================================
|
|
// GameLength / MaxPlayers Fix
|
|
//=============================================================================
|
|
|
|
// max players limit ^, gamelenght fix, save cmdline
|
|
event InitGame(string Options, out string Error)
|
|
{
|
|
local KFLevelRules KFLRit;
|
|
local ShopVolume SH;
|
|
local ZombieVolume ZZ;
|
|
local string InOpt;
|
|
|
|
super(Invasion).InitGame(Options, Error);
|
|
|
|
// remove 6 player limit, yay!
|
|
// max to 32 players
|
|
MaxPlayers = clamp(GetIntOption( Options, "MaxPlayers", MaxPlayers ), 0, 32);
|
|
default.MaxPlayers = clamp(default.MaxPlayers, 0, 32);
|
|
|
|
foreach DynamicActors(class'KFLevelRules', KFLRit)
|
|
{
|
|
if (KFLRules == none)
|
|
KFLRules = KFLRit;
|
|
else Warn("MULTIPLE KFLEVELRULES FOUND!!!!!");
|
|
}
|
|
|
|
// added all traders open option
|
|
foreach AllActors(class'ShopVolume', SH)
|
|
{
|
|
if (SH == none)
|
|
continue;
|
|
|
|
// open everything
|
|
if (class'o_Settings'.default.bAllTradersOpen)
|
|
{
|
|
SH.bAlwaysClosed = false;
|
|
SH.bAlwaysEnabled = true;
|
|
}
|
|
|
|
// now fill the array ;d
|
|
if (!SH.bObjectiveModeOnly || bUsingObjectiveMode )
|
|
{
|
|
ShopList[ShopList.Length] = SH;
|
|
}
|
|
}
|
|
|
|
if (class'o_Settings'.default.bAllTradersOpen)
|
|
log("> bAllTradersOpen = true. All traders will be open!");
|
|
|
|
foreach DynamicActors(class'ZombieVolume', ZZ)
|
|
{
|
|
if (!ZZ.bObjectiveModeOnly || bUsingObjectiveMode)
|
|
{
|
|
ZedSpawnList[ZedSpawnList.Length] = ZZ;
|
|
}
|
|
}
|
|
|
|
// provide default rules if mapper did not need custom one
|
|
if (KFLRules == none)
|
|
KFLRules = spawn(class'KFLevelRules');
|
|
|
|
log("KFLRules = "$KFLRules);
|
|
|
|
InOpt = ParseOption(Options, "UseBots");
|
|
if (InOpt != "")
|
|
{
|
|
bNoBots = bool(InOpt);
|
|
}
|
|
|
|
// allow to set gamelength from cmdline
|
|
KFGameLength = GetIntOption(Options, "GameLength", KFGameLength);
|
|
|
|
// add anti idiot protection
|
|
if (KFGameLength < 0 || KFGameLength > 3)
|
|
{
|
|
log("> GameLength must be in [0..3]: 0-short, 1-medium, 2-long, 3-custom");
|
|
KFGameLength = GL_Long;
|
|
}
|
|
log("> Game length = "$KFGameLength);
|
|
|
|
// added a log
|
|
MonsterCollection = SpecialEventMonsterCollections[ GetSpecialEventType() ];
|
|
log("> MonsterCollection = " $ MonsterCollection);
|
|
|
|
if (KFGameLength != GL_Custom)
|
|
{
|
|
// Set up the default game type settings
|
|
bUseEndGameBoss = true;
|
|
bRespawnOnBoss = true;
|
|
if (StandardMonsterClasses.Length > 0)
|
|
{
|
|
MonsterClasses = StandardMonsterClasses;
|
|
}
|
|
MonsterSquad = StandardMonsterSquads;
|
|
MaxZombiesOnce = StandardMaxZombiesOnce;
|
|
bCustomGameLength = false;
|
|
UpdateGameLength();
|
|
|
|
// Set difficulty based values
|
|
if (GameDifficulty >= 7.0) // Hell on Earth
|
|
{
|
|
TimeBetweenWaves = TimeBetweenWavesHell;
|
|
StartingCash = StartingCashHell;
|
|
MinRespawnCash = MinRespawnCashHell;
|
|
}
|
|
else if (GameDifficulty >= 5.0) // Suicidal
|
|
{
|
|
TimeBetweenWaves = TimeBetweenWavesSuicidal;
|
|
StartingCash = StartingCashSuicidal;
|
|
MinRespawnCash = MinRespawnCashSuicidal;
|
|
}
|
|
else if (GameDifficulty >= 4.0) // Hard
|
|
{
|
|
TimeBetweenWaves = TimeBetweenWavesHard;
|
|
StartingCash = StartingCashHard;
|
|
MinRespawnCash = MinRespawnCashHard;
|
|
}
|
|
else if (GameDifficulty >= 2.0) // Normal
|
|
{
|
|
TimeBetweenWaves = TimeBetweenWavesNormal;
|
|
StartingCash = StartingCashNormal;
|
|
MinRespawnCash = MinRespawnCashNormal;
|
|
}
|
|
else //if ( GameDifficulty == 1.0 ) // Beginner
|
|
{
|
|
TimeBetweenWaves = TimeBetweenWavesBeginner;
|
|
StartingCash = StartingCashBeginner;
|
|
MinRespawnCash = MinRespawnCashBeginner;
|
|
}
|
|
|
|
InitialWave = 0;
|
|
PrepareSpecialSquads();
|
|
}
|
|
else
|
|
{
|
|
bCustomGameLength = true;
|
|
UpdateGameLength();
|
|
}
|
|
|
|
LoadUpMonsterList();
|
|
|
|
// save options for debug and further use
|
|
class'stub_GT'.default.CmdLine = Options;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Player Camera Fix
|
|
//=============================================================================
|
|
|
|
// N.B. i edited whole timer, fixed some random fuckups
|
|
// like last zed killing
|
|
state MatchInProgress
|
|
{
|
|
function OpenShops()
|
|
{
|
|
local int i;
|
|
local Controller C;
|
|
|
|
bTradingDoorsOpen = true;
|
|
|
|
for (i=0; i<ShopList.Length; i++)
|
|
{
|
|
if (ShopList[i].bAlwaysClosed)
|
|
continue;
|
|
if (ShopList[i].bAlwaysEnabled)
|
|
ShopList[i].OpenShop();
|
|
}
|
|
|
|
if (KFGameReplicationInfo(GameReplicationInfo).CurrentShop == none)
|
|
{
|
|
SelectShop();
|
|
}
|
|
|
|
KFGameReplicationInfo(GameReplicationInfo).CurrentShop.OpenShop();
|
|
|
|
// Tell all players to start showing the path to the trader
|
|
for (C=Level.ControllerList; C!=none; C=C.NextController)
|
|
{
|
|
if (C.Pawn != none && C.Pawn.Health > 0)
|
|
{
|
|
// Disable pawn collision during trader time
|
|
C.Pawn.bBlockActors = false;
|
|
|
|
if (KFPlayerController(C) != none)
|
|
{
|
|
KFPlayerController(C).SetShowPathToTrader(true);
|
|
// Have Trader tell players that the Shop's Open
|
|
if (WaveNum < FinalWave)
|
|
KFPlayerController(C).ClientLocationalVoiceMessage(C.PlayerReplicationInfo, none, 'TRADER', 2);
|
|
else
|
|
KFPlayerController(C).ClientLocationalVoiceMessage(C.PlayerReplicationInfo, none, 'TRADER', 3);
|
|
|
|
// send message if eveything is open
|
|
if (class'o_Settings'.default.bAllTradersOpen)
|
|
class'o_Helper'.static.SendMessage(PlayerController(C), class'o_Settings'.default.bAllTradersMessage, false);
|
|
|
|
// Hints
|
|
KFPlayerController(C).CheckForHint(31);
|
|
HintTime_1 = Level.TimeSeconds + 11;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function CloseShops()
|
|
{
|
|
local int i;
|
|
local Controller C;
|
|
local Pickup Pickup;
|
|
|
|
bTradingDoorsOpen = False;
|
|
for( i=0; i<ShopList.Length; i++ )
|
|
{
|
|
if( ShopList[i].bCurrentlyOpen )
|
|
ShopList[i].CloseShop();
|
|
}
|
|
|
|
SelectShop();
|
|
|
|
// changed from AllActors
|
|
foreach DynamicActors(class'Pickup', Pickup)
|
|
{
|
|
// do not touch dosh
|
|
if (Pickup == none || Pickup.IsA('CashPickup'))
|
|
continue;
|
|
|
|
// trying not to destroy them imidiately
|
|
if (Pickup.bDropped)
|
|
Pickup.LifeSpan = 3.0;
|
|
}
|
|
|
|
// Tell all players to stop showing the path to the trader
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if ( C.Pawn != none && C.Pawn.Health > 0 )
|
|
{
|
|
// Restore pawn collision during trader time
|
|
C.Pawn.bBlockActors = C.Pawn.default.bBlockActors;
|
|
|
|
if ( KFPlayerController(C) != none )
|
|
{
|
|
KFPlayerController(C).SetShowPathToTrader(false);
|
|
// disable Garbage Collection!
|
|
// DO NOT FORGET TO ENABLE IT ON SERVERTRAVEL !!
|
|
// KFPlayerController(C).ClientForceCollectGarbage();
|
|
|
|
if ( WaveNum < FinalWave - 1 )
|
|
{
|
|
// Have Trader tell players that the Shop's Closed
|
|
KFPlayerController(C).ClientLocationalVoiceMessage(C.PlayerReplicationInfo, none, 'TRADER', 6);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function nTimer()
|
|
{
|
|
local Controller C;
|
|
local bool bOneMessage;
|
|
local Bot B;
|
|
|
|
Global.Timer();
|
|
|
|
if ( Level.TimeSeconds > HintTime_1 && bTradingDoorsOpen && bShowHint_2 )
|
|
{
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
// added player ctrl check
|
|
if( KFPlayerController(C) != none && C.Pawn != none && C.Pawn.Health > 0 )
|
|
{
|
|
KFPlayerController(C).CheckForHint(32);
|
|
HintTime_2 = Level.TimeSeconds + 11;
|
|
}
|
|
}
|
|
|
|
bShowHint_2 = false;
|
|
}
|
|
|
|
if ( Level.TimeSeconds > HintTime_2 && bTradingDoorsOpen && bShowHint_3 )
|
|
{
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
// added player ctrl check
|
|
if (KFPlayerController(C) != none && C.Pawn != none && C.Pawn.Health > 0 )
|
|
{
|
|
KFPlayerController(C).CheckForHint(33);
|
|
}
|
|
}
|
|
bShowHint_3 = false;
|
|
}
|
|
|
|
if ( !bFinalStartup )
|
|
{
|
|
bFinalStartup = true;
|
|
PlayStartupMessage();
|
|
}
|
|
|
|
if ( NeedPlayers() && AddBot() && (RemainingBots > 0) )
|
|
RemainingBots--;
|
|
ElapsedTime++;
|
|
GameReplicationInfo.ElapsedTime = ElapsedTime;
|
|
|
|
if( !UpdateMonsterCount() )
|
|
{
|
|
EndGame(none,"TimeLimit");
|
|
return;
|
|
}
|
|
|
|
if( bUpdateViewTargs )
|
|
UpdateViews();
|
|
|
|
if (!bNoBots && !bBotsAdded)
|
|
{
|
|
if(KFGameReplicationInfo(GameReplicationInfo) != none)
|
|
|
|
if((NumPlayers + NumBots) < MaxPlayers && KFGameReplicationInfo(GameReplicationInfo).PendingBots > 0 )
|
|
{
|
|
AddBots(1);
|
|
KFGameReplicationInfo(GameReplicationInfo).PendingBots --;
|
|
}
|
|
|
|
if (KFGameReplicationInfo(GameReplicationInfo).PendingBots == 0)
|
|
{
|
|
bBotsAdded = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Close Trader doors
|
|
// added bWaveInProgress check
|
|
if( bWaveBossInProgress || bWaveInProgress)
|
|
{
|
|
if( bTradingDoorsOpen )
|
|
{
|
|
CloseShops();
|
|
TraderProblemLevel = 0;
|
|
}
|
|
|
|
if( TraderProblemLevel<4 )
|
|
{
|
|
if( BootShopPlayers() )
|
|
TraderProblemLevel = 0;
|
|
else
|
|
TraderProblemLevel++;
|
|
}
|
|
}
|
|
|
|
// set camera for midgame boss's
|
|
// check DoBossDeath()
|
|
if (class'stub_GT'.default.bBossView && !bWaveBossInProgress && class'stub_GT'.default.BossViewBackTime < Level.TimeSeconds)
|
|
{
|
|
class'stub_GT'.default.bBossView = false;
|
|
|
|
for ( C = Level.ControllerList; C != None; C = C.NextController )
|
|
{
|
|
if( PlayerController(C)!=None )
|
|
{
|
|
if ( C.Pawn == None && !C.PlayerReplicationInfo.bOnlySpectator && bRespawnOnBoss )
|
|
C.ServerReStartPlayer();
|
|
if ( C.Pawn != None )
|
|
{
|
|
PlayerController(C).SetViewTarget(C.Pawn);
|
|
PlayerController(C).ClientSetViewTarget(C.Pawn);
|
|
}
|
|
else
|
|
{
|
|
PlayerController(C).SetViewTarget(C);
|
|
PlayerController(C).ClientSetViewTarget(C);
|
|
}
|
|
PlayerController(C).bBehindView = False;
|
|
PlayerController(C).ClientSetBehindView(False);
|
|
}
|
|
}
|
|
}
|
|
|
|
// boss wave
|
|
if (bWaveBossInProgress)
|
|
{
|
|
// set camera on boss when he spawns
|
|
if ( !bHasSetViewYet && NumMonsters > 0 )
|
|
{
|
|
bHasSetViewYet = true;
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if ( KFMonster(C.Pawn)!=none && KFMonster(C.Pawn).MakeGrandEntry() )
|
|
{
|
|
ViewingBoss = KFMonster(C.Pawn);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ViewingBoss != none )
|
|
{
|
|
class'stub_GT'.default.bBossView = true;
|
|
ViewingBoss.bAlwaysRelevant = true;
|
|
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if ( PlayerController(C) != none )
|
|
{
|
|
PlayerController(C).SetViewTarget(ViewingBoss);
|
|
PlayerController(C).ClientSetViewTarget(ViewingBoss);
|
|
PlayerController(C).bBehindView = true;
|
|
PlayerController(C).ClientSetBehindView(true);
|
|
PlayerController(C).ClientSetMusic(BossBattleSong,MTRAN_FastFade);
|
|
}
|
|
if ( C.PlayerReplicationInfo!=none && bRespawnOnBoss )
|
|
{
|
|
C.PlayerReplicationInfo.bOutOfLives = false;
|
|
C.PlayerReplicationInfo.NumLives = 0;
|
|
if ( (C.Pawn == none) && !C.PlayerReplicationInfo.bOnlySpectator && PlayerController(C)!=none )
|
|
C.GotoState('PlayerWaiting');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove camera from boss
|
|
else if ( class'stub_GT'.default.bBossView && (ViewingBoss==none || (ViewingBoss!=none && !ViewingBoss.bShotAnim) ) )
|
|
{
|
|
class'stub_GT'.default.bBossView = false;
|
|
ViewingBoss = none;
|
|
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if( PlayerController(C)!=none )
|
|
{
|
|
if ( C.Pawn == none && !C.PlayerReplicationInfo.bOnlySpectator && bRespawnOnBoss )
|
|
C.ServerReStartPlayer();
|
|
if( C.Pawn != none )
|
|
{
|
|
PlayerController(C).SetViewTarget(C.Pawn);
|
|
PlayerController(C).ClientSetViewTarget(C.Pawn);
|
|
}
|
|
else
|
|
{
|
|
PlayerController(C).SetViewTarget(C);
|
|
PlayerController(C).ClientSetViewTarget(C);
|
|
}
|
|
PlayerController(C).bBehindView = false;
|
|
PlayerController(C).ClientSetBehindView(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// all dead
|
|
if ( (TotalMaxMonsters<=0 || Level.TimeSeconds > WaveEndTime) && NumMonsters <= 0 )
|
|
DoWaveEnd();
|
|
// if we can spawn more
|
|
else if (MaxMonsters - NumMonsters > 0)
|
|
AddBoss();
|
|
}
|
|
|
|
// usual wave
|
|
else if (bWaveInProgress)
|
|
{
|
|
WaveTimeElapsed += 1.0;
|
|
// trader door part moved up
|
|
|
|
if (!MusicPlaying)
|
|
StartGameMusic(true);
|
|
|
|
if (TotalMaxMonsters <= 0)
|
|
{
|
|
// TWI's Check for STUCK monsters was bugged, killed zeds imidiately
|
|
if ( NumMonsters <= 5 && WaveTimeElapsed > 10.0)
|
|
{
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if ( KFMonsterController(C) != none && KFMonster(C.Pawn) != none && (Level.TimeSeconds - KFMonster(C.Pawn).LastSeenOrRelevantTime > 8) )
|
|
{
|
|
C.Pawn.KilledBy( C.Pawn );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// if everyone's spawned and they're all dead
|
|
if ( NumMonsters <= 0 )
|
|
DoWaveEnd();
|
|
}
|
|
|
|
// all monsters spawned
|
|
else if ( NextMonsterTime < Level.TimeSeconds && (NumMonsters+NextSpawnSquad.Length <= MaxMonsters) )
|
|
{
|
|
WaveEndTime = Level.TimeSeconds + 160;
|
|
if (!bDisableZedSpawning)
|
|
AddSquad(); // Comment this out to prevent zed spawning
|
|
|
|
if (nextSpawnSquad.length > 0)
|
|
NextMonsterTime = Level.TimeSeconds + 0.2;
|
|
else
|
|
NextMonsterTime = Level.TimeSeconds + CalcNextSquadSpawnTime();
|
|
}
|
|
}
|
|
|
|
else if ( NumMonsters <= 0 )
|
|
{
|
|
if ( WaveNum == FinalWave && !bUseEndGameBoss )
|
|
{
|
|
if( bDebugMoney )
|
|
log("$$$$$$$$$$$$$$$$ Final TotalPossibleMatchMoney = "$TotalPossibleMatchMoney,'Debug');
|
|
EndGame(none,"TimeLimit");
|
|
return;
|
|
}
|
|
|
|
else if( WaveNum == (FinalWave + 1) && bUseEndGameBoss )
|
|
{
|
|
if( bDebugMoney )
|
|
log("$$$$$$$$$$$$$$$$ Final TotalPossibleMatchMoney = "$TotalPossibleMatchMoney,'Debug');
|
|
EndGame(none,"TimeLimit");
|
|
return;
|
|
}
|
|
|
|
WaveCountDown--;
|
|
if ( !CalmMusicPlaying )
|
|
{
|
|
InitMapWaveCfg();
|
|
StartGameMusic(false);
|
|
}
|
|
|
|
// Open Trader doors
|
|
if ( !bTradingDoorsOpen && WaveNum != InitialWave )
|
|
{
|
|
bTradingDoorsOpen = true;
|
|
OpenShops();
|
|
}
|
|
|
|
// Select a shop if one isn't open
|
|
if ( KFGameReplicationInfo(GameReplicationInfo).CurrentShop == none )
|
|
SelectShop();
|
|
|
|
KFGameReplicationInfo(GameReplicationInfo).TimeToNextWave = WaveCountDown;
|
|
|
|
// Have Trader tell players that they've got 30 seconds
|
|
if ( WaveCountDown == 30 )
|
|
{
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if ( KFPlayerController(C) != none )
|
|
KFPlayerController(C).ClientLocationalVoiceMessage(C.PlayerReplicationInfo, none, 'TRADER', 4);
|
|
}
|
|
}
|
|
|
|
// Have Trader tell players that they've got 10 seconds
|
|
else if ( WaveCountDown == 10 )
|
|
{
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if ( KFPlayerController(C) != none )
|
|
KFPlayerController(C).ClientLocationalVoiceMessage(C.PlayerReplicationInfo, none, 'TRADER', 5);
|
|
}
|
|
}
|
|
|
|
else if ( WaveCountDown == 5 )
|
|
{
|
|
KFGameReplicationInfo(Level.Game.GameReplicationInfo).MaxMonstersOn = false;
|
|
InvasionGameReplicationInfo(GameReplicationInfo).WaveNumber = WaveNum;
|
|
}
|
|
|
|
else if ( (WaveCountDown > 0) && (WaveCountDown < 5) )
|
|
{
|
|
if( WaveNum == FinalWave && bUseEndGameBoss )
|
|
BroadcastLocalizedMessage(class'KFMod.WaitingMessage', 3);
|
|
else
|
|
BroadcastLocalizedMessage(class'KFMod.WaitingMessage', 1);
|
|
}
|
|
else if ( WaveCountDown <= 1 )
|
|
{
|
|
bWaveInProgress = true;
|
|
KFGameReplicationInfo(GameReplicationInfo).bWaveInProgress = true;
|
|
|
|
// Randomize the ammo pickups again
|
|
if( WaveNum > 0 )
|
|
SetupPickups();
|
|
|
|
if ( WaveNum == FinalWave && bUseEndGameBoss )
|
|
StartWaveBoss();
|
|
|
|
else
|
|
{
|
|
SetupWave();
|
|
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if ( PlayerController(C) != none )
|
|
{
|
|
PlayerController(C).LastPlaySpeech = 0;
|
|
if ( KFPlayerController(C) != none )
|
|
KFPlayerController(C).bHasHeardTraderWelcomeMessage = false;
|
|
}
|
|
|
|
if ( Bot(C) != none )
|
|
{
|
|
B = Bot(C);
|
|
InvasionBot(B).bDamagedMessage = false;
|
|
B.bInitLifeMessage = false;
|
|
|
|
if ( !bOneMessage && (FRand() < 0.65) )
|
|
{
|
|
bOneMessage = true;
|
|
if ( (B.Squad.SquadLeader != none) && B.Squad.CloseToLeader(C.Pawn) )
|
|
{
|
|
B.SendMessage(B.Squad.SquadLeader.PlayerReplicationInfo, 'OTHER', B.GetMessageIndex('INPOSITION'), 20, 'TEAM');
|
|
B.bInitLifeMessage = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// SERVER INFO
|
|
//=============================================================================
|
|
|
|
// MasterServerUplink.uc
|
|
// Called when we should refresh the game state
|
|
// event Refresh()
|
|
// {
|
|
// if ( (!bInitialStateCached) || ( Level.TimeSeconds > CacheRefreshTime ) )
|
|
// {
|
|
// Level.Game.GetServerInfo(FullCachedServerState);
|
|
// Level.Game.GetServerDetails(FullCachedServerState);
|
|
|
|
// CachedServerState = FullCachedServerState;
|
|
|
|
// Level.Game.GetServerPlayers(FullCachedServerState);
|
|
|
|
// ServerState = FullCachedServerState;
|
|
// CacheRefreshTime = Level.TimeSeconds + 60;
|
|
// bInitialStateCached = false;
|
|
// }
|
|
// else if (Level.Game.NumPlayers != CachePlayerCount)
|
|
// {
|
|
// ServerState = CachedServerState;
|
|
|
|
// Level.Game.GetServerPlayers(ServerState);
|
|
|
|
// FullCachedServerState = ServerState;
|
|
|
|
// }
|
|
// else
|
|
// ServerState = FullCachedServerState;
|
|
|
|
// CachePlayerCount = Level.Game.NumPlayers;
|
|
// }
|
|
|
|
|
|
// show server detailed info
|
|
function GetServerDetails( out ServerResponseLine ServerState )
|
|
{
|
|
local int l;
|
|
local Mutator M;
|
|
local GameRules G;
|
|
local int i, Len, NumMutators;
|
|
local string MutatorName;
|
|
local bool bFound;
|
|
|
|
// game info
|
|
AddServerDetail( ServerState, "ServerMode", Eval(Level.NetMode == NM_ListenServer, "non-dedicated", "dedicated") );
|
|
AddServerDetail( ServerState, "AdminName", GameReplicationInfo.AdminName );
|
|
AddServerDetail( ServerState, "AdminEmail", GameReplicationInfo.AdminEmail );
|
|
|
|
AddServerDetail( ServerState, "ServerVersion", Level.ROVersion );
|
|
AddServerDetail( ServerState, "IsVacSecured", Eval(IsVACSecured(), "true", "false"));
|
|
|
|
if ( AccessControl != none && AccessControl.RequiresPassword() )
|
|
AddServerDetail( ServerState, "GamePassword", "true" );
|
|
|
|
if ( AllowGameSpeedChange() && (GameSpeed != 1.0) )
|
|
AddServerDetail( ServerState, "GameSpeed", int(GameSpeed*100)/100.0 );
|
|
|
|
AddServerDetail( ServerState, "MaxSpectators", MaxSpectators );
|
|
|
|
// voting
|
|
if( VotingHandler != none )
|
|
VotingHandler.GetServerDetails(ServerState);
|
|
|
|
// Ask the mutators if they have anything to add.
|
|
for (M = BaseMutator; M != none; M = M.NextMutator)
|
|
{
|
|
M.GetServerDetails(ServerState);
|
|
NumMutators++;
|
|
}
|
|
|
|
// Ask the gamerules if they have anything to add.
|
|
for ( G=GameRulesModifiers; G!=none; G=G.NextGameRules )
|
|
G.GetServerDetails(ServerState);
|
|
|
|
// make sure all the mutators were really added
|
|
for ( i=0; i<ServerState.ServerInfo.Length; i++ )
|
|
if ( ServerState.ServerInfo[i].Key ~= "Mutator" )
|
|
NumMutators--;
|
|
|
|
if ( NumMutators > 1 )
|
|
{
|
|
// something is missing
|
|
for (M = BaseMutator.NextMutator; M != none; M = M.NextMutator)
|
|
{
|
|
MutatorName = M.GetHumanReadableName();
|
|
for ( i=0; i<ServerState.ServerInfo.Length; i++ )
|
|
{
|
|
if ( (ServerState.ServerInfo[i].Value ~= MutatorName) && (ServerState.ServerInfo[i].Key ~= "Mutator") )
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
|
|
if ( !bFound )
|
|
{
|
|
Len = ServerState.ServerInfo.Length;
|
|
ServerState.ServerInfo.Length = Len+1;
|
|
ServerState.ServerInfo[i].Key = "Mutator";
|
|
ServerState.ServerInfo[i].Value = MutatorName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// kf gametype
|
|
l = ServerState.ServerInfo.Length;
|
|
ServerState.ServerInfo.Length = l+1;
|
|
ServerState.ServerInfo[l].Key = "Max runtime zombies";
|
|
ServerState.ServerInfo[l].Value = string(MaxZombiesOnce);
|
|
l++;
|
|
ServerState.ServerInfo.Length = l+1;
|
|
ServerState.ServerInfo[l].Key = "Starting cash";
|
|
ServerState.ServerInfo[l].Value = string(StartingCash);
|
|
l++;
|
|
|
|
// invasion
|
|
AddServerDetail( ServerState, "InitialWave", InitialWave );
|
|
AddServerDetail( ServerState, "FinalWave", FinalWave );
|
|
|
|
// teamgame
|
|
AddServerDetail( ServerState, "BalanceTeams", bBalanceTeams);
|
|
AddServerDetail( ServerState, "PlayersBalanceTeams", bPlayersBalanceTeams);
|
|
AddServerDetail( ServerState, "FriendlyFireScale", int(FriendlyFireScale*100) $ "%" );
|
|
|
|
// deathmatch
|
|
AddServerDetail( ServerState, "GoalScore", GoalScore );
|
|
AddServerDetail( ServerState, "TimeLimit", TimeLimit );
|
|
AddServerDetail( ServerState, "Translocator", bAllowTrans );
|
|
AddServerDetail( ServerState, "WeaponStay", bWeaponStay );
|
|
AddServerDetail( ServerState, "ForceRespawn", bForceRespawn );
|
|
|
|
// unreal mp game
|
|
AddServerDetail( ServerState, "MinPlayers", MinPlayers );
|
|
AddServerDetail( ServerState, "EndTimeDelay", EndTimeDelay );
|
|
}
|
|
|
|
|
|
// show perk, health in player info
|
|
function GetServerPlayers( out ServerResponseLine ServerState )
|
|
{
|
|
local Mutator M;
|
|
local Controller C;
|
|
local PlayerReplicationInfo PRI;
|
|
local int i; // , TeamFlag[2];
|
|
|
|
i = ServerState.PlayerInfo.Length;
|
|
// TeamFlag[0] = 1 << 29;
|
|
// TeamFlag[1] = TeamFlag[0] << 1;
|
|
|
|
for( C=Level.ControllerList; C != none; C=C.NextController )
|
|
{
|
|
PRI = C.PlayerReplicationInfo;
|
|
if( (PRI != none) && !PRI.bBot && MessagingSpectator(C) == none )
|
|
{
|
|
ServerState.PlayerInfo.Length = i+1;
|
|
ServerState.PlayerInfo[i].PlayerNum = C.PlayerNum;
|
|
// our new functions might be a bit heavy, so limit its execution
|
|
// and use cached string
|
|
if (Level.TimeSeconds >= class'stub_GT'.default.fDelay)
|
|
{
|
|
class'stub_GT'.default.sCachedPlayersInfo[i] = class'stub_GT'.static.ParsePlayerName(PRI, C, bWaitingToStartMatch);
|
|
ServerState.PlayerInfo[i].PlayerName = class'stub_GT'.default.sCachedPlayersInfo[i]; // PRI.PlayerName;
|
|
class'stub_GT'.default.fDelay = Level.TimeSeconds + class'o_Settings'.default.fRefreshTime;
|
|
}
|
|
else
|
|
ServerState.PlayerInfo[i].PlayerName = class'stub_GT'.default.sCachedPlayersInfo[i];
|
|
ServerState.PlayerInfo[i].Score = PRI.Score;
|
|
ServerState.PlayerInfo[i].Ping = 4 * PRI.Ping;
|
|
// do we need this?
|
|
// if (bTeamGame && PRI.Team != none)
|
|
// ServerState.PlayerInfo[i].StatsID = class'stub_GT'.static.GetPerkInfo(PRI); // ServerState.PlayerInfo[i].StatsID | TeamFlag[PRI.Team.TeamIndex];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// Ask the mutators if they have anything to add.
|
|
for (M = BaseMutator.NextMutator; M != none; M = M.NextMutator)
|
|
M.GetServerPlayers(ServerState);
|
|
}
|
|
|
|
|
|
static final function string ParsePlayerName(out PlayerReplicationInfo PRI, out Controller C, bool bWaitingToStartMatch)
|
|
{
|
|
local string status, perk;
|
|
|
|
if (C == none || PRI == none || KFPlayerReplicationInfo(PRI) == none)
|
|
return "NULL PRI";
|
|
|
|
// in case we are in
|
|
if (C.IsInState('PlayerWaiting'))
|
|
{
|
|
if (bWaitingToStartMatch)
|
|
{
|
|
if(PRI.bReadyToPlay)
|
|
status = class'o_Settings'.default.sReady;
|
|
else
|
|
status = class'o_Settings'.default.sNotReady;
|
|
}
|
|
else
|
|
status = class'o_Settings'.default.sAwaiting;
|
|
}
|
|
|
|
// if we are spectator, do not check perk, kills, etc
|
|
else if (PRI.bOnlySpectator)
|
|
{
|
|
return class'o_Helper'.static.StripTags(PRI.PlayerName) @ class'o_Helper'.static.ParseTags(class'o_Settings'.default.sSpectator);
|
|
}
|
|
|
|
else if (PRI.bOutOfLives && !PRI.bOnlySpectator)
|
|
status = class'o_Settings'.default.sDead;
|
|
|
|
// else we are alive and need more info
|
|
else
|
|
status = class'o_Settings'.default.sAlive;
|
|
|
|
// parse kills, health
|
|
status = repl(status, class'o_Settings'.default.sTagHP, KFPlayerReplicationInfo(PRI).PlayerHealth);
|
|
status = repl(status, class'o_Settings'.default.sTagKills, PRI.Kills);
|
|
// status ready !
|
|
|
|
// parse perk if we want it
|
|
if (class'o_Settings'.default.bShowPerk)
|
|
{
|
|
switch (string(KFPlayerReplicationInfo(PRI).ClientVeteranSkill))
|
|
{
|
|
case "KFMod.KFVetSharpshooter":
|
|
case "ServerPerksP.SRVetSharpshooter":
|
|
case "ScrnBalanceSrv.ScrnVetSharpshooter":
|
|
perk = "Sharp";
|
|
break;
|
|
case "KFMod.KFVetFieldMedic":
|
|
case "ServerPerksP.SRVetFieldMedic":
|
|
case "ScrnBalanceSrv.ScrnVetFieldMedic":
|
|
perk = "Medic";
|
|
break;
|
|
case "KFMod.KFVetBerserker":
|
|
case "ServerPerksP.SRVetBerserker":
|
|
case "ScrnBalanceSrv.ScrnVetBerserker":
|
|
perk = "Zerk";
|
|
break;
|
|
case "KFMod.KFVetCommando":
|
|
case "ServerPerksP.SRVetCommando":
|
|
case "ScrnBalanceSrv.ScrnVetCommando":
|
|
perk = "Mando";
|
|
break;
|
|
case "KFMod.KFVetDemolitions":
|
|
case "ServerPerksP.SRVetDemolitions":
|
|
case "ScrnBalanceSrv.ScrnVetDemolitions":
|
|
perk = "Demo";
|
|
break;
|
|
case "KFMod.KFVetFirebug":
|
|
case "ServerPerksP.SRVetFirebug":
|
|
case "ScrnBalanceSrv.ScrnVetFirebug":
|
|
perk = "Pyro";
|
|
break;
|
|
case "KFMod.KFVetSupportSpec":
|
|
case "ServerPerksP.SRVetSupportSpec":
|
|
case "ScrnBalanceSrv.ScrnVetSupportSpec":
|
|
perk = "Sup";
|
|
break;
|
|
case "ScrnBalanceSrv.ScrnVetGunslinger":
|
|
perk = "Slinger";
|
|
break;
|
|
default:
|
|
perk = "NULL perk";
|
|
}
|
|
|
|
perk @= KFPlayerReplicationInfo(PRI).ClientVeteranSkillLevel;
|
|
perk = "^r[" $ perk $ "]^w";
|
|
}
|
|
|
|
return class'o_Helper'.static.ParseTags(perk @ PRI.PlayerName @ status);
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Disable slomo!
|
|
//=============================================================================
|
|
|
|
// TO-DO add a bool to controll it !
|
|
// tick will be called constantly but now it does nothing :3
|
|
event Tick(float DeltaTime)
|
|
{
|
|
local float TrueTimeFactor;
|
|
local Controller C;
|
|
|
|
// global switch
|
|
if (!class'o_Settings'.default.bAllowZedTime)
|
|
return;
|
|
|
|
if ( bZEDTimeActive )
|
|
{
|
|
TrueTimeFactor = 1.1/Level.TimeDilation;
|
|
CurrentZEDTimeDuration -= DeltaTime * TrueTimeFactor;
|
|
|
|
if( CurrentZEDTimeDuration < (ZEDTimeDuration*0.166) && CurrentZEDTimeDuration > 0 )
|
|
{
|
|
if ( !bSpeedingBackUp )
|
|
{
|
|
bSpeedingBackUp = true;
|
|
|
|
for ( C=Level.ControllerList;C!=none;C=C.NextController )
|
|
{
|
|
if (KFPlayerController(C)!= none)
|
|
{
|
|
KFPlayerController(C).ClientExitZedTime();
|
|
}
|
|
}
|
|
}
|
|
|
|
SetGameSpeed(Lerp( (CurrentZEDTimeDuration/(ZEDTimeDuration*0.166)), 1.0, 0.2 ));
|
|
}
|
|
|
|
if ( CurrentZEDTimeDuration <= 0 )
|
|
{
|
|
bZEDTimeActive = false;
|
|
bSpeedingBackUp = false;
|
|
SetGameSpeed(1.0);
|
|
ZedTimeExtensionsUsed = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function DramaticEvent(float BaseZedTimePossibility, optional float DesiredZedTimeDuration)
|
|
{
|
|
local float RandChance;
|
|
local float TimeSinceLastEvent;
|
|
local Controller C;
|
|
|
|
// global switch
|
|
if (!class'o_Settings'.default.bAllowZedTime)
|
|
return;
|
|
|
|
TimeSinceLastEvent = Level.TimeSeconds - LastZedTimeEvent;
|
|
|
|
// Don't go in slomo if we were just IN slomo
|
|
if( TimeSinceLastEvent < 10.0 && BaseZedTimePossibility != 1.0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( TimeSinceLastEvent > 60 )
|
|
{
|
|
BaseZedTimePossibility *= 4.0;
|
|
}
|
|
else if( TimeSinceLastEvent > 30 )
|
|
{
|
|
BaseZedTimePossibility *= 2.0;
|
|
}
|
|
|
|
RandChance = FRand();
|
|
|
|
//log("TimeSinceLastEvent = "$TimeSinceLastEvent$" RandChance = "$RandChance$" BaseZedTimePossibility = "$BaseZedTimePossibility);
|
|
|
|
if( RandChance <= BaseZedTimePossibility )
|
|
{
|
|
bZEDTimeActive = true;
|
|
bSpeedingBackUp = false;
|
|
LastZedTimeEvent = Level.TimeSeconds;
|
|
|
|
if ( DesiredZedTimeDuration != 0.0 )
|
|
{
|
|
CurrentZEDTimeDuration = DesiredZedTimeDuration;
|
|
}
|
|
else
|
|
{
|
|
CurrentZEDTimeDuration = ZEDTimeDuration;
|
|
}
|
|
|
|
SetGameSpeed(ZedTimeSlomoScale);
|
|
|
|
for ( C = Level.ControllerList; C != none; C = C.NextController )
|
|
{
|
|
if (KFPlayerController(C)!= none)
|
|
{
|
|
KFPlayerController(C).ClientEnterZedTime();
|
|
}
|
|
|
|
if ( C.PlayerReplicationInfo != none && KFSteamStatsAndAchievements(C.PlayerReplicationInfo.SteamStatsAndAchievements) != none )
|
|
{
|
|
KFSteamStatsAndAchievements(C.PlayerReplicationInfo.SteamStatsAndAchievements).AddZedTime(ZEDTimeDuration);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// DO NOT Force slomo for a longer period of time when the boss dies
|
|
function DoBossDeath()
|
|
{
|
|
class'stub_GT'.default.bBossView = true;
|
|
|
|
// global switch
|
|
if (class'o_Settings'.default.bAllowZedTime)
|
|
{
|
|
bZEDTimeActive = true;
|
|
bSpeedingBackUp = false;
|
|
LastZedTimeEvent = Level.TimeSeconds;
|
|
CurrentZEDTimeDuration = ZEDTimeDuration*2;
|
|
SetGameSpeed(ZedTimeSlomoScale);
|
|
|
|
class'stub_GT'.default.BossViewBackTime = Level.Timeseconds + ZEDTimeDuration*1.1;
|
|
}
|
|
|
|
// changed controller disabling to directly killing zeds
|
|
Killzeds();
|
|
}
|
|
|
|
|
|
// remove latejoiner shit, GameInfo code
|
|
event PreLogin( string Options, string Address, string PlayerID, out string Error, out string FailCode )
|
|
{
|
|
super(GameInfo).PreLogin( Options, Address, PlayerID, Error, FailCode );
|
|
}
|
|
|
|
|
|
// adding garbage collection to here since wave swith doesn't trigger it anymore
|
|
// function Logout(Controller Exiting)
|
|
// {
|
|
// local Inventory Inv;
|
|
|
|
// if (Exiting != none && MessagingSpectator(Exiting) == none )
|
|
// {
|
|
// Exiting.ConsoleCommand("obj garbage");
|
|
// log("Triggered GC for KFPC named: " $ Exiting);
|
|
// }
|
|
|
|
// if (Exiting.Pawn != none)
|
|
// {
|
|
// for (Inv = Exiting.Pawn.Inventory; Inv != none; Inv = Inv.Inventory)
|
|
// {
|
|
// if (class<Weapon>(Inv.class) != none)
|
|
// WeaponDestroyed(class<Weapon>(Inv.class));
|
|
// }
|
|
// }
|
|
|
|
// super(DeathMatch).Logout(Exiting);
|
|
// }
|
|
|
|
|
|
//=============================================================================
|
|
// LET SPECS TO FLY AFTER GAME ENDS
|
|
//=============================================================================
|
|
|
|
function bool CheckEndGame(PlayerReplicationInfo Winner, string Reason)
|
|
{
|
|
local Controller P;
|
|
local PlayerController Player;
|
|
local bool bSetAchievement;
|
|
local string MapName;
|
|
|
|
EndTime = Level.TimeSeconds + EndTimeDelay;
|
|
|
|
if ( WaveNum > FinalWave )
|
|
{
|
|
GameReplicationInfo.Winner = Teams[0];
|
|
KFGameReplicationInfo(GameReplicationInfo).EndGameType = 2;
|
|
|
|
if ( GameDifficulty >= 2.0 )
|
|
{
|
|
bSetAchievement = true;
|
|
// Get the MapName out of the URL
|
|
MapName = GetCurrentMapName(Level);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KFGameReplicationInfo(GameReplicationInfo).EndGameType = 1;
|
|
}
|
|
|
|
if ( (GameRulesModifiers != none) && !GameRulesModifiers.CheckEndGame(Winner, Reason) )
|
|
{
|
|
KFGameReplicationInfo(GameReplicationInfo).EndGameType = 0;
|
|
return false;
|
|
}
|
|
|
|
for (P = Level.ControllerList; P != none; P = P.nextController)
|
|
{
|
|
Player = PlayerController(P);
|
|
if (Player != none)
|
|
{
|
|
Player.ClientSetBehindView(true);
|
|
// disable this so players can move freely after the game ends
|
|
// Player.ClientGameEnded();
|
|
|
|
if (bSetAchievement && KFSteamStatsAndAchievements(Player.SteamStatsAndAchievements) != none)
|
|
KFSteamStatsAndAchievements(Player.SteamStatsAndAchievements).WonGame(MapName, GameDifficulty, KFGameLength == GL_Long);
|
|
|
|
if (KFGameReplicationInfo(GameReplicationInfo).EndGameType == 1)
|
|
{
|
|
foreach DynamicActors(class'ZombieBoss', class'stub_GT'.default.BossArray)
|
|
{
|
|
if (class'stub_GT'.default.BossArray == none || class'stub_GT'.default.BossArray.Health <= 0)
|
|
continue;
|
|
class'o_Helper'.static.ShowPatHP(Player, class'stub_GT'.default.BossArray);
|
|
}
|
|
}
|
|
}
|
|
|
|
// and this
|
|
// P.GameHasEnded();
|
|
}
|
|
|
|
// If we won the match
|
|
if ( KFGameReplicationInfo(GameReplicationInfo).EndGameType == 2 )
|
|
{
|
|
CheckHarchierAchievement();
|
|
}
|
|
|
|
if ( CurrentGameProfile != none )
|
|
{
|
|
CurrentGameProfile.bWonMatch = false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Killzeds fix
|
|
//=============================================================================
|
|
|
|
exec function KillZeds()
|
|
{
|
|
local KFMonster Monster;
|
|
|
|
// do not directly kill zeds in this loop
|
|
// it leads to log spam
|
|
foreach DynamicActors(class 'KFMonster', Monster)
|
|
{
|
|
// failsafe
|
|
if (Monster == none || Monster.Health <= 0 && Monster.bDeleteMe)
|
|
continue;
|
|
// fill our array
|
|
class'stub_GT'.default.Monsters[class'stub_GT'.default.Monsters.length] = Monster;
|
|
}
|
|
|
|
// and i have to do this hacky hack to avoid crashes
|
|
// pass it to our new kill function
|
|
class'stub_GT'.static.MowZeds(class'stub_GT'.default.Monsters);
|
|
}
|
|
|
|
|
|
final static function MowZeds(out array<KFMonster> Monsters)
|
|
{
|
|
local int i;
|
|
|
|
// suicide is the easiest solution
|
|
// + I don't want to add kills to executer
|
|
for (i=0; i<Monsters.length; ++i)
|
|
{
|
|
if (Monsters[i] != none)
|
|
Monsters[i].Suicide();
|
|
}
|
|
|
|
Monsters.length = 0;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Garbage Collection on Server Travel
|
|
//=============================================================================
|
|
|
|
// function ProcessServerTravel( string URL, bool bItems )
|
|
// {
|
|
// local playercontroller P, LocalPlayer;
|
|
|
|
// // Pass it along
|
|
// BaseMutator.ServerTraveling(URL,bItems);
|
|
|
|
// EndLogging("mapchange");
|
|
|
|
// // Notify clients we're switching level and give them time to receive.
|
|
// // We call PreClientTravel directly on any local PlayerPawns (ie listen server)
|
|
// log("ProcessServerTravel:"@URL);
|
|
|
|
// foreach DynamicActors(class'PlayerController', P)
|
|
// {
|
|
// if ( NetConnection( P.Player) != none )
|
|
// {
|
|
// P.ClientTravel( Eval( Instr(URL,"?") > 0, Left(URL,Instr(URL,"?")), URL), TRAVEL_Relative, bItems );
|
|
// class'stub_GT'.static.TriggerGC(p);
|
|
// }
|
|
// else
|
|
// {
|
|
// LocalPlayer = P;
|
|
// P.PreClientTravel();
|
|
// class'stub_GT'.static.TriggerGC(p);
|
|
// }
|
|
// }
|
|
|
|
// if ( (Level.NetMode == NM_ListenServer) && (LocalPlayer != None) )
|
|
// Level.NextURL = Level.NextURL
|
|
// $"?Team="$LocalPlayer.GetDefaultURL("Team")
|
|
// $"?Name="$LocalPlayer.GetDefaultURL("Name")
|
|
// $"?Class="$LocalPlayer.GetDefaultURL("Class")
|
|
// $"?Character="$LocalPlayer.GetDefaultURL("Character");
|
|
|
|
// // Switch immediately if not networking.
|
|
// if( Level.NetMode!=NM_DedicatedServer && Level.NetMode!=NM_ListenServer )
|
|
// Level.NextSwitchCountdown = 0.0;
|
|
// }
|
|
|
|
|
|
final static function TriggerGC(PlayerController p)
|
|
{
|
|
if (p == none || KFPlayerController(p) == none)
|
|
return;
|
|
|
|
KFPlayerController(p).ClientForceCollectGarbage();
|
|
log("Ayyy, ClientForceCollectGarbage triggered for " $ KFPlayerController(p).PlayerOwnerName);
|
|
}
|
|
|
|
|
|
function bool SetPause( BOOL bPause, PlayerController P )
|
|
{
|
|
if (bPauseable || (bAdminCanPause && (P.IsA('Admin') || P.PlayerReplicationInfo.bAdmin || P.PlayerReplicationInfo.bSilentAdmin)) || Level.Netmode==NM_Standalone)
|
|
{
|
|
if (bPause)
|
|
Level.Pauser=P.PlayerReplicationInfo;
|
|
else
|
|
Level.Pauser=None;
|
|
return True;
|
|
}
|
|
else return False;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// remove UpdateGameLength() / greylist
|
|
//=============================================================================
|
|
function UpdateGameLength(){}
|
|
|
|
|
|
exec function AddNamedBot(string botname)
|
|
{
|
|
super(Invasion).AddNamedBot(botname);
|
|
}
|
|
|
|
|
|
exec function AddBots(int num)
|
|
{
|
|
num = Clamp(num, 0, MaxPlayers - (NumPlayers + NumBots));
|
|
|
|
while (--num >= 0)
|
|
{
|
|
if (Level.NetMode != NM_Standalone)
|
|
MinPlayers = Max(MinPlayers + 1, NumPlayers + NumBots + 1);
|
|
AddBot();
|
|
}
|
|
}
|
|
|
|
|
|
event PostLogin( PlayerController NewPlayer )
|
|
{
|
|
local int i;
|
|
|
|
NewPlayer.SetGRI(GameReplicationInfo);
|
|
NewPlayer.PlayerReplicationInfo.PlayerID = CurrentID++;
|
|
|
|
super(Invasion).PostLogin(NewPlayer);
|
|
|
|
if (UnrealPlayer(NewPlayer) != None)
|
|
UnrealPlayer(NewPlayer).ClientReceiveLoginMenu(LoginMenuClass, bAlwaysShowLoginMenu);
|
|
if (NewPlayer.PlayerReplicationInfo.Team != None)
|
|
GameEvent("TeamChange",""$NewPlayer.PlayerReplicationInfo.Team.TeamIndex,NewPlayer.PlayerReplicationInfo);
|
|
|
|
if (NewPlayer != None && Level.NetMode == NM_ListenServer && Level.GetLocalPlayerController() == NewPlayer)
|
|
NewPlayer.InitializeVoiceChat();
|
|
|
|
if (KFPlayerController(NewPlayer) != none)
|
|
{
|
|
for (i = 0; i < InstancedWeaponClasses.Length; i++)
|
|
{
|
|
KFPlayerController(NewPlayer).ClientWeaponSpawned(InstancedWeaponClasses[i], none);
|
|
}
|
|
}
|
|
|
|
if (NewPlayer.PlayerReplicationInfo.bOnlySpectator) // must not be a spectator
|
|
{
|
|
KFPlayerController(NewPlayer).JoinedAsSpectatorOnly();
|
|
}
|
|
else
|
|
{
|
|
NewPlayer.GotoState('PlayerWaiting');
|
|
}
|
|
|
|
if (KFPlayerController(NewPlayer) != None)
|
|
StartInitGameMusic(KFPlayerController(NewPlayer));
|
|
|
|
// if (bCustomGameLength && NewPlayer.SteamStatsAndAchievements != none)
|
|
// {
|
|
// NewPlayer.SteamStatsAndAchievements.bUsedCheats = true;
|
|
// }
|
|
}
|
|
|
|
|
|
defaultproperties{} |