1917 lines
53 KiB
Ucode
1917 lines
53 KiB
Ucode
//=============================================================================
|
|
// KFGameInfo_Survival
|
|
//=============================================================================
|
|
// Classic Killing Floor wave based game type
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
// - Christian "schneidzekk" Schneider
|
|
//=============================================================================
|
|
class KFGameInfo_Survival extends KFGameInfo;
|
|
|
|
`include(KFGame\KFGameAnalytics.uci);
|
|
`include(KFGame\KFMatchStats.uci);
|
|
|
|
enum EWaveEndCondition
|
|
{
|
|
WEC_WaveWon,
|
|
WEC_TeamWipedOut,
|
|
WEC_GameWon
|
|
};
|
|
|
|
/************************************************************************************
|
|
* Game Config
|
|
***********************************************************************************/
|
|
/** Trader */
|
|
var int TimeBetweenWaves;
|
|
|
|
var const float EndCinematicDelay; // time between the game ending, and the final camera change activating
|
|
|
|
/************************************************************************************
|
|
* AAR
|
|
***********************************************************************************/
|
|
var const float AARDisplayDelay;
|
|
|
|
var Array<AARAWard> TeamAwardList;
|
|
|
|
/************************************************************************************
|
|
* Waves
|
|
***********************************************************************************/
|
|
var byte WaveMax; // The "end" wave
|
|
var int WaveNum; // The wave we are currently in
|
|
var bool bHumanDeathsLastWave; //Track this separate from player count in case someone dies and leaves
|
|
var int ObjectiveSpawnDelay; // How long should the first wave be delayed if there is an active objective.
|
|
// The boss waves spams the WaveEnd functions, adding this to prevent it (was affecting seasonal events).
|
|
var protected transient bool bWaveStarted;
|
|
|
|
/** Whether this game mode should play music from the get-go (lobby) */
|
|
static function bool ShouldPlayMusicAtStart()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/** Whether an action or ambient track should be played */
|
|
static function bool ShouldPlayActionMusicTrack(KFGameReplicationInfo GRI)
|
|
{
|
|
return GRI.bMatchHasBegun && !GRI.bTraderIsOpen;
|
|
}
|
|
|
|
event PreBeginPlay()
|
|
{
|
|
super.PreBeginPlay();
|
|
|
|
InitSpawnManager();
|
|
UpdateGameSettings();
|
|
}
|
|
|
|
event PostBeginPlay()
|
|
{
|
|
super.PostBeginPlay();
|
|
|
|
TimeBetweenWaves = GetTraderTime();
|
|
}
|
|
|
|
/** Set up the spawning */
|
|
function InitSpawnManager()
|
|
{
|
|
SpawnManager = new(self) SpawnManagerClasses[GameLength];
|
|
SpawnManager.Initialize();
|
|
WaveMax = SpawnManager.WaveSettings.Waves.Length;
|
|
MyKFGRI.WaveMax = WaveMax;
|
|
}
|
|
|
|
/* StartMatch()
|
|
Start the game - inform all actors that the match is starting, and spawn player pawns
|
|
*/
|
|
function StartMatch()
|
|
{
|
|
local KFPlayerController KFPC;
|
|
WaveNum = 0;
|
|
|
|
super.StartMatch();
|
|
|
|
if( class'KFGameEngine'.static.CheckNoAutoStart() || class'KFGameEngine'.static.IsEditor() )
|
|
{
|
|
GotoState('DebugSuspendWave');
|
|
}
|
|
else
|
|
{
|
|
GotoState('PlayingWave');
|
|
}
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
KFPC.ClientMatchStarted();
|
|
}
|
|
}
|
|
|
|
function PlayWaveStartDialog()
|
|
{
|
|
`DialogManager.PlayWaveStartDialog(MyKFGRI.IsBossWave());
|
|
if (WaveNum == 1)
|
|
{
|
|
class'KFTraderDialogManager'.static.PlayFirstWaveStartDialog(WorldInfo);
|
|
}
|
|
}
|
|
|
|
/** Custom logic to determine what the game's current intensity is */
|
|
function byte GetGameIntensityForMusic()
|
|
{
|
|
switch( GameLength )
|
|
{
|
|
case GL_Short:
|
|
if( WaveNum <= 0 )
|
|
return 1;
|
|
else if( WaveNum <= 1 )
|
|
return 4;
|
|
else if( WaveNum <= 2 )
|
|
return 7;
|
|
else
|
|
return 10;
|
|
case GL_Normal:
|
|
if( WaveNum <= 1 )
|
|
return 1;
|
|
else if( WaveNum <= 3 )
|
|
return 4;
|
|
else if( WaveNum <= 5 )
|
|
return 7;
|
|
else
|
|
return 10;
|
|
case GL_Long:
|
|
if( WaveNum <= 2 )
|
|
return 1;
|
|
else if( WaveNum <= 5 )
|
|
return 4;
|
|
else if( WaveNum <= 8 )
|
|
return 7;
|
|
else
|
|
return 10;
|
|
};
|
|
|
|
return 255;
|
|
}
|
|
|
|
function bool IsPlayerReady( KFPlayerReplicationInfo PRI )
|
|
{
|
|
local KFPlayerController KFPC;
|
|
|
|
// Spawn our player even if we don't have a perk while using the editor or skip lobby
|
|
if (class'KFGameEngine'.static.CheckSkipLobby() || class'Engine'.static.IsEditor())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( super.IsPlayerReady(PRI) )
|
|
{
|
|
KFPC = KFPlayerController(PRI.Owner);
|
|
if ( WorldInfo.NetMode == NM_StandAlone && KFPC != None && (KFPC.CurrentPerk == None || !KFPC.CurrentPerk.bInitialized) )
|
|
{
|
|
// HSL - BWJ - 3-16-16 - console doesn't read stats yet, so no perk support. Adding this hack in for now so we can spawn in
|
|
if( WorldInfo.IsConsoleDedicatedServer() || WorldInfo.IsConsoleBuild() )
|
|
{
|
|
`warn("TODO: Need perk support for console");
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
`log("ERROR: Failed to load perk for:"@KFPC@KFPC.CurrentPerk);
|
|
//ForceKickPlayer(KFPC, "Failed to find perk");
|
|
return false; // critical error
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool PlayerCanRestart( PlayerController aPlayer )
|
|
{
|
|
return (!IsWaveActive() && MyKFGRI.bMatchHasBegun);
|
|
}
|
|
|
|
function RestartPlayer(Controller NewPlayer)
|
|
{
|
|
local KFPlayerController KFPC;
|
|
local KFPlayerReplicationInfo KFPRI;
|
|
local bool bWasWaitingForClientPerkData;
|
|
|
|
KFPC = KFPlayerController(NewPlayer);
|
|
KFPRI = KFPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo);
|
|
|
|
if( KFPC != None && KFPRI != None )
|
|
{
|
|
if( IsPlayerReady( KFPRI ) )
|
|
{
|
|
bWasWaitingForClientPerkData = KFPC.bWaitingForClientPerkData;
|
|
|
|
/** If we have rejoined the match more than once, delay our respawn by some amount of time */
|
|
if( MyKFGRI.bMatchHasBegun && KFPRI.NumTimesReconnected > 1 && `TimeSince(KFPRI.LastQuitTime) < ReconnectRespawnTime )
|
|
{
|
|
KFPC.StartSpectate();
|
|
KFPC.SetTimer(ReconnectRespawnTime - `TimeSince(KFPRI.LastQuitTime), false, nameof(KFPC.SpawnReconnectedPlayer));
|
|
}
|
|
//If a wave is active, we spectate until the end of the wave
|
|
else if( IsWaveActive() && !bWasWaitingForClientPerkData )
|
|
{
|
|
KFPC.StartSpectate();
|
|
}
|
|
else
|
|
{
|
|
Super.RestartPlayer(NewPlayer);
|
|
|
|
// Already gone through one RestartPlayer() cycle, don't process again
|
|
if( bWasWaitingForClientPerkData )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( KFPRI.Deaths == 0 )
|
|
{
|
|
if( WaveNum < 1 )
|
|
{
|
|
KFPRI.Score = DifficultyInfo.GetAdjustedStartingCash();
|
|
}
|
|
else
|
|
{
|
|
KFPRI.Score = GetAdjustedDeathPenalty( KFPRI, true );
|
|
}
|
|
`log("SCORING: Player" @ KFPRI.PlayerName @ "received" @ KFPRI.Score @ "starting cash", bLogScoring);
|
|
}
|
|
}
|
|
|
|
`BalanceLog(GBE_Respawn, KFPRI, "$"$KFPRI.Score);
|
|
|
|
`AnalyticsLog(("player_respawn",
|
|
KFPRI,
|
|
"#"$MyKFGRI.WaveNum,
|
|
"#"$KFPRI.Score ));
|
|
}
|
|
}
|
|
}
|
|
|
|
function Killed(Controller Killer, Controller KilledPlayer, Pawn KilledPawn, class<DamageType> damageType)
|
|
{
|
|
local Sequence GameSeq;
|
|
local array<SequenceObject> AllWaveProgressEvents;
|
|
local KFSeqEvent_WaveProgress WaveProgressEvt;
|
|
local int i;
|
|
local KFInterface_MapObjective MapObj;
|
|
|
|
super.Killed(Killer, KilledPlayer, KilledPawn, damageType);
|
|
|
|
// tell objectives (ie dosh hold and exterminate) when something dies
|
|
if (KilledPawn.IsA('KFPawn_Monster'))
|
|
{
|
|
MapObj = KFInterface_MapObjective(MyKFGRI.CurrentObjective);
|
|
if (MapObj != none)
|
|
{
|
|
MapObj.NotifyZedKilled(Killer, KilledPawn, KFInterface_MonsterBoss(KilledPawn) != none);
|
|
}
|
|
}
|
|
|
|
// if not boss wave or endless wave, play progress update trader dialog
|
|
if( !MyKFGRI.IsBossWave() && !MyKFGRI.IsEndlessWave() && KilledPawn.IsA('KFPawn_Monster') )
|
|
{
|
|
// no KFTraderDialogManager object on dedicated server, so use static function
|
|
class'KFTraderDialogManager'.static.PlayGlobalWaveProgressDialog( MyKFGRI.AIRemaining, MyKFGRI.WaveTotalAICount, WorldInfo );
|
|
|
|
// Get the gameplay sequence.
|
|
GameSeq = WorldInfo.GetGameSequence();
|
|
|
|
if (GameSeq != none)
|
|
{
|
|
GameSeq.FindSeqObjectsByClass(class'KFSeqEvent_WaveProgress', TRUE, AllWaveProgressEvents);
|
|
|
|
for (i = 0; i < AllWaveProgressEvents.Length; i++)
|
|
{
|
|
WaveProgressEvt = KFSeqEvent_WaveProgress(AllWaveProgressEvents[i]);
|
|
|
|
if (WaveProgressEvt != None)
|
|
{
|
|
WaveProgressEvt.SetWaveProgress(MyKFGRI.AIRemaining, MyKFGRI.WaveTotalAICount, self);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//If a human died to a non-suicide
|
|
if (KilledPawn.IsA('KFPawn_Human') && DamageType != class'DmgType_Suicided')
|
|
{
|
|
bHumanDeathsLastWave = true;
|
|
}
|
|
|
|
// BossDied will handle the end of wave.
|
|
if(!(KFPawn_Monster(KilledPawn) != none && KFPawn_Monster(KilledPawn).IsABoss()))
|
|
{
|
|
CheckWaveEnd();
|
|
}
|
|
}
|
|
|
|
/* Use reduce damage for friendly fire, etc. */
|
|
function ReduceDamage(out int Damage, Pawn Injured, Controller InstigatedBy, vector HitLocation, out vector Momentum, class<DamageType> DamageType, Actor DamageCauser, TraceHitInfo HitInfo)
|
|
{
|
|
if( Injured.Controller != none && Injured.Controller.bIsPlayer && !MyKFGRI.bMatchHasBegun )
|
|
{
|
|
Damage = 0;
|
|
Momentum = vect(0,0,0);
|
|
}
|
|
|
|
Super.ReduceDamage(Damage, Injured, InstigatedBy, HitLocation, Momentum, DamageType, DamageCauser, HitInfo);
|
|
}
|
|
|
|
function BossDied(Controller Killer, optional bool bCheckWaveEnded = true)
|
|
{
|
|
local KFPawn_Monster AIP;
|
|
local KFGameReplicationInfo KFGRI;
|
|
local KFPlayerController KFPC;
|
|
|
|
super.BossDied(Killer, bCheckWaveEnded);
|
|
|
|
KFPC = KFPlayerController(Killer);
|
|
|
|
`RecordBossMurderer(KFPC);
|
|
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
if( KFGRI != none && !KFGRI.IsBossWave() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Extended zed time for an extra dramatic event
|
|
DramaticEvent( 1, 6.f );
|
|
|
|
// Kill all zeds active zeds when the game ends
|
|
foreach WorldInfo.AllPawns(class'KFPawn_Monster', AIP)
|
|
{
|
|
if( AIP.Health > 0 )
|
|
{
|
|
AIP.Died(none , none, AIP.Location);
|
|
}
|
|
}
|
|
if(bCheckWaveEnded)
|
|
{
|
|
CheckWaveEnd( true );
|
|
}
|
|
}
|
|
|
|
function UpdateGameSettings()
|
|
{
|
|
local name SessionName;
|
|
local KFOnlineGameSettings KFGameSettings;
|
|
local int NumHumanPlayers, i;
|
|
local KFGameEngine KFEngine;
|
|
|
|
if (WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.NetMode == NM_ListenServer)
|
|
{
|
|
//`REMOVEMESOON_ServerTakeoverLog("KFGameInfo_Survival.UpdateGameSettings 1 - GameInterface: "$GameInterface);
|
|
if (GameInterface != None)
|
|
{
|
|
KFEngine = KFGameEngine(class'Engine'.static.GetEngine());
|
|
|
|
SessionName = PlayerReplicationInfoClass.default.SessionName;
|
|
|
|
if( PlayfabInter != none && PlayfabInter.GetGameSettings() != none )
|
|
{
|
|
KFGameSettings = KFOnlineGameSettings(PlayfabInter.GetGameSettings());
|
|
KFGameSettings.bAvailableForTakeover = KFEngine.bAvailableForTakeover;
|
|
}
|
|
else
|
|
{
|
|
KFGameSettings = KFOnlineGameSettings(GameInterface.GetGameSettings(SessionName));
|
|
}
|
|
//Ensure bug-for-bug compatibility with KF1
|
|
|
|
//`REMOVEMESOON_ServerTakeoverLog("KFGameInfo_Survival.UpdateGameSettings 2 - KFGameSettings: "$KFGameSettings);
|
|
|
|
if (KFGameSettings != None)
|
|
{
|
|
//`REMOVEMESOON_ServerTakeoverLog("KFGameInfo_Survival.UpdateGameSettings 3 - KFGameSettings.bAvailableForTakeover: "$KFGameSettings.bAvailableForTakeover);
|
|
|
|
KFGameSettings.Mode = GetGameModeNum();
|
|
KFGameSettings.Difficulty = GameDifficulty;
|
|
//Ensure bug-for-bug compatibility with KF1
|
|
if (WaveNum == 0)
|
|
{
|
|
KFGameSettings.bInProgress = false;
|
|
KFGameSettings.CurrentWave = 1;
|
|
}
|
|
else
|
|
{
|
|
KFGameSettings.bInProgress = true;
|
|
KFGameSettings.CurrentWave = WaveNum;
|
|
}
|
|
//Also from KF1
|
|
if(MyKFGRI != none)
|
|
{
|
|
KFGameSettings.NumWaves = MyKFGRI.GetFinalWaveNum();
|
|
}
|
|
else
|
|
{
|
|
KFGameSettings.NumWaves = WaveMax - 1;
|
|
}
|
|
KFGameSettings.OwningPlayerName = class'GameReplicationInfo'.default.ServerName;
|
|
|
|
KFGameSettings.NumPublicConnections = MaxPlayersAllowed;
|
|
KFGameSettings.bRequiresPassword = RequiresPassword();
|
|
KFGameSettings.bCustom = bIsCustomGame;
|
|
KFGameSettings.bUsesStats = !IsUnrankedGame();
|
|
KFGameSettings.NumSpectators = NumSpectators;
|
|
if(MyKFGRI != none)
|
|
{
|
|
MyKFGRI.bCustom = bIsCustomGame;
|
|
}
|
|
|
|
// Set the map name
|
|
//@SABER_EGS IsEOSDedicatedServer() case added
|
|
if( WorldInfo.IsConsoleDedicatedServer() || WorldInfo.IsEOSDedicatedServer() )
|
|
{
|
|
KFGameSettings.MapName = WorldInfo.GetMapName(true);
|
|
if( GameReplicationInfo != none )
|
|
{
|
|
for( i = 0; i < GameReplicationInfo.PRIArray.Length; i++ )
|
|
{
|
|
if( !GameReplicationInfo.PRIArray[i].bBot )
|
|
{
|
|
NumHumanPlayers++;
|
|
}
|
|
}
|
|
}
|
|
|
|
KFGameSettings.NumOpenPublicConnections = KFGameSettings.NumPublicConnections - NumHumanPlayers;
|
|
}
|
|
|
|
//`REMOVEMESOON_ServerTakeoverLog("KFGameInfo_Survival.UpdateGameSettings 4 - PlayfabInter: "$PlayfabInter);
|
|
if (PlayfabInter != none)
|
|
{
|
|
//`REMOVEMESOON_ServerTakeoverLog("KFGameInfo_Survival.UpdateGameSettings 4.1 - IsRegisteredWithPlayfab: "$PlayfabInter.IsRegisteredWithPlayfab());
|
|
}
|
|
|
|
if( PlayfabInter != none && PlayfabInter.IsRegisteredWithPlayfab() )
|
|
{
|
|
PlayfabInter.ServerUpdateOnlineGame();
|
|
//@SABER_EGS_BEGIN Crossplay support
|
|
if (WorldInfo.IsEOSDedicatedServer()) {
|
|
GameInterface.UpdateOnlineGame(SessionName, KFGameSettings, true);
|
|
}
|
|
//@SABER_EGS_END
|
|
}
|
|
else
|
|
{
|
|
//Trigger re-broadcast of game settings
|
|
GameInterface.UpdateOnlineGame(SessionName, KFGameSettings, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function OnServerTitleDataRead()
|
|
{
|
|
super.OnServerTitleDataRead();
|
|
class'KFGameEngine'.static.RefreshEventContent();
|
|
//set boss index again here - this fixes the case of seasonal events like christmas setting krampus the only boss
|
|
//to spawn on krampuses lair
|
|
SetBossIndex();
|
|
}
|
|
|
|
/**
|
|
* Return whether Viewer is allowed to spectate from ViewTarget's PoV.
|
|
* Used to prevent players from spectating zeds when the DummyPRI is active.
|
|
*
|
|
*/
|
|
function bool CanSpectate( PlayerController Viewer, PlayerReplicationInfo ViewTarget )
|
|
{
|
|
// Normal PRI's should be replicatable, DummyPRI is not, indicating a zed
|
|
return ( super.CanSpectate(Viewer, ViewTarget)
|
|
&& (Viewer.PlayerReplicationInfo.bOnlySpectator || Viewer.GetTeamNum() == ViewTarget.GetTeamNum() || MyKFGRI.bTraderIsOpen) );
|
|
}
|
|
|
|
|
|
/************************************************************************************
|
|
* Timers
|
|
***********************************************************************************/
|
|
|
|
/** Default timer, called from native */
|
|
event Timer()
|
|
{
|
|
super.Timer();
|
|
|
|
if( SpawnManager != none )
|
|
{
|
|
SpawnManager.Update();
|
|
}
|
|
|
|
if( GameConductor != none )
|
|
{
|
|
GameConductor.TimerUpdate();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Checks if we are playing coop online with other people
|
|
*
|
|
* @return true if hosting and more than 1 player
|
|
*/
|
|
function byte IsMultiplayerGame()
|
|
{
|
|
return (WorldInfo.NetMode != NM_Standalone && GetNumPlayers() > 1) ? 1 : 0;
|
|
}
|
|
|
|
function UpdateWaveEndDialogInfo()
|
|
{
|
|
local int PlayersAlive, PlayersTotal, MostZedsKilled, MostDoshEarned, BestTeammateScore;
|
|
local KFPlayerController KFPC, KilledMostZeds, EarnedMostDosh, BestTeammate;
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if( KFPC.bDemoOwner )
|
|
{
|
|
// don't count demorecspectator
|
|
continue;
|
|
}
|
|
|
|
PlayersTotal++;
|
|
|
|
if( KFPC.Pawn != none && KFPC.Pawn.IsAliveAndWell() )
|
|
{
|
|
PlayersAlive++;
|
|
}
|
|
|
|
if( KFPC.MatchStats.ZedsKilledLastWave > MostZedsKilled )
|
|
{
|
|
KilledMostZeds = KFPC;
|
|
MostZedsKilled = KFPC.MatchStats.ZedsKilledLastWave;
|
|
}
|
|
|
|
if( KFPC.MatchStats.GetDoshEarnedInWave() > MostDoshEarned )
|
|
{
|
|
EarnedMostDosh = KFPC;
|
|
MostDoshEarned = KFPC.MatchStats.GetDoshEarnedInWave();
|
|
}
|
|
|
|
if( KFPC.MatchStats.ZedsKilledLastWave + KFPC.MatchStats.GetDoshEarnedInWave() > BestTeammateScore )
|
|
{
|
|
BestTeammate = KFPC;
|
|
BestTeammateScore = KFPC.MatchStats.ZedsKilledLastWave + KFPC.MatchStats.GetDoshEarnedInWave();
|
|
}
|
|
}
|
|
|
|
if( PlayersTotal > 1 )
|
|
{
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if( KFPC.Pawn != none && KFPC.Pawn.IsAliveAndWell() )
|
|
{
|
|
if( !bHumanDeathsLastWave )
|
|
{
|
|
// no teammates died
|
|
KFPC.PWRI.bAllSurvivedLastWave = true;
|
|
}
|
|
else if( PlayersAlive == 1 )
|
|
{
|
|
// only survivor
|
|
KFPC.PWRI.bOneSurvivedLastWave = true;
|
|
}
|
|
else
|
|
{
|
|
// more than one teammate died
|
|
KFPC.PWRI.bSomeSurvivedLastWave = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( KilledMostZeds != none )
|
|
{
|
|
KilledMostZeds.PWRI.bKilledMostZeds = true;
|
|
}
|
|
|
|
if( EarnedMostDosh != none )
|
|
{
|
|
EarnedMostDosh.PWRI.bEarnedMostDosh = true;
|
|
}
|
|
|
|
if( BestTeammate != none )
|
|
{
|
|
BestTeammate.PWRI.bBestTeammate = true;
|
|
}
|
|
|
|
bHumanDeathsLastWave = false;
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Do$h
|
|
***********************************************************************************/
|
|
|
|
/* Add up the team's earned money and give it to the surviving players */
|
|
function RewardSurvivingPlayers()
|
|
{
|
|
local int PlayerCut;
|
|
local int PlayerCount;
|
|
local KFPlayerController KFPC;
|
|
Local KFTeamInfo_Human T;
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if( KFPC.Pawn != none && KFPC.Pawn.IsAliveAndWell() )
|
|
{
|
|
PlayerCount++;
|
|
|
|
// Find the player's team
|
|
if( T == none && KFPC.PlayerReplicationInfo != none && KFPC.PlayerReplicationInfo.Team != none )
|
|
{
|
|
T = KFTeamInfo_Human(KFPC.PlayerReplicationInfo.Team);
|
|
}
|
|
}
|
|
}
|
|
|
|
// No dosh to distribute if there is no team or score
|
|
if( T == none || T.Score <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
PlayerCut = Round(T.Score / PlayerCount);
|
|
|
|
`log("SCORING: Team dosh earned this round:" @ T.Score, bLogScoring);
|
|
`log("SCORING: Number of surviving players:" @ PlayerCount, bLogScoring);
|
|
`log("SCORING: Dosh/survivng player:" @ PlayerCut, bLogScoring);
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if( KFPC.Pawn != none && KFPC.Pawn.IsAliveAndWell() )
|
|
{
|
|
KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo).AddDosh(PlayerCut, true);
|
|
T.AddScore( -PlayerCut );
|
|
|
|
`log("Player" @ KFPC.PlayerReplicationInfo.PlayerName @ "got" @ PlayerCut @ "for surviving the wave", bLogScoring);
|
|
}
|
|
}
|
|
|
|
// Reset team score afte the wave ends
|
|
T.AddScore( 0, true );
|
|
}
|
|
|
|
function int CalculateMinimumRespawnDosh(float UsedMaxRespawnDosh)
|
|
{
|
|
return Round(UsedMaxRespawnDosh * (float(WaveNum) / float(MyKFGRI.GetFinalWaveNum())));
|
|
}
|
|
|
|
/**
|
|
* @brief Calculates the dosh penalty or minimum dosh spawning amount
|
|
*
|
|
* @param KilledPlayerPRI The killed player's PRI
|
|
* @param bLateJoiner If true, give late joiners money without penalty
|
|
* @return Rounded amount of dosh
|
|
*/
|
|
function int GetAdjustedDeathPenalty( KFPlayerReplicationInfo KilledPlayerPRI, optional bool bLateJoiner=false )
|
|
{
|
|
local float MinimumRespawnDosh, PlayerRespawnDosh;
|
|
local float UsedMaxRespawnDosh;
|
|
|
|
//pointless to give respawn money in the last wave
|
|
if( WaveNum >= WaveMax )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if( GameDifficulty < MaxRespawnDosh.Length )
|
|
{
|
|
UsedMaxRespawnDosh = MaxRespawnDosh[GameDifficulty];
|
|
}
|
|
else
|
|
{
|
|
UsedMaxRespawnDosh = MaxRespawnDosh[MaxRespawnDosh.Length - 1];
|
|
}
|
|
|
|
MinimumRespawnDosh = CalculateMinimumRespawnDosh(UsedMaxRespawnDosh);
|
|
|
|
if( bLateJoiner )
|
|
{
|
|
return CalculateLateJoinerStartingDosh(MinimumRespawnDosh);
|
|
}
|
|
|
|
`log( "SCORING: Player" @ KilledPlayerPRI.PlayerName @ "predicted minimum respawn dosh:" @ MinimumRespawnDosh, bLogScoring );
|
|
PlayerRespawnDosh = KilledPlayerPRI.Score - (KilledPlayerPRI.Score * DeathPenaltyModifiers[GameDifficulty]);
|
|
`log( "SCORING: Player" @ KilledPlayerPRI.PlayerName @ "current respawn dosh:" @ PlayerRespawnDosh, bLogScoring );
|
|
|
|
if( MinimumRespawnDosh > PlayerRespawnDosh )
|
|
{
|
|
`log( "SCORING: Player" @ KilledPlayerPRI.PlayerName @ "MinimumRespawnDosh > PlayerRespawnDosh, returning MinimumRespawnDosh - KilledPlayerPRI.Score =" @ MinimumRespawnDosh - KilledPlayerPRI.Score, bLogScoring );
|
|
return MinimumRespawnDosh - KilledPlayerPRI.Score;
|
|
}
|
|
|
|
`log( "SCORING: Player" @ KilledPlayerPRI.PlayerName @ "PlayerRespawnDosh > MinimumRespawnDosh, returning KilledPlayerPRI.Score * DeathPenaltyModifiers[GameDifficulty] =" @ -Round( KilledPlayerPRI.Score * DeathPenaltyModifiers[GameDifficulty] ), bLogScoring );
|
|
return -Round( KilledPlayerPRI.Score * DeathPenaltyModifiers[GameDifficulty] );
|
|
}
|
|
|
|
function int CalculateLateJoinerStartingDosh(int MinimumRespawnDosh)
|
|
{
|
|
if (default.LateArrivalStarts.Length > 0 && GameLength >= 0 && GameLength < default.LateArrivalStarts.Length)
|
|
{
|
|
if (default.LateArrivalStarts[GameLength].StartingDosh.Length > 0 && WaveNum - 1 >= 0 && WaveNum - 1 < default.LateArrivalStarts[GameLength].StartingDosh.Length)
|
|
{
|
|
`log("SCORING: Late joiner received" @ LateArrivalStarts[GameLength].StartingDosh[WaveNum - 1]);
|
|
return default.LateArrivalStarts[GameLength].StartingDosh[WaveNum - 1];
|
|
}
|
|
}
|
|
|
|
`log("SCORING: Late joiner - invalid parameters to properly award late joiner dosh. Will instead receive Minimum Respawn Dosh of" @ MinimumRespawnDosh, bLogScoring);
|
|
return MinimumRespawnDosh;
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Wave Mode Cheats
|
|
***********************************************************************************/
|
|
function bool AllowWaveCheats()
|
|
{
|
|
`if(`notdefined(ShippingPC))
|
|
return true;
|
|
`else
|
|
return class'KFGameEngine'.static.IsEditor();
|
|
`endif
|
|
}
|
|
|
|
function FindCollectibles()
|
|
{
|
|
`if(`notdefined(ShippingPC))
|
|
local KFPlayerController KFPC;
|
|
local KFCollectibleActor Collectible;
|
|
local Vector EmptyVector;
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if (KFPC.IsLocalPlayerController())
|
|
{
|
|
foreach WorldInfo.AllActors(class'KFCollectibleActor', Collectible)
|
|
{
|
|
Collectible.TakeDamage(100, KFPC, EmptyVector, EmptyVector, class'KFDT_Fire_Mac10');
|
|
}
|
|
}
|
|
}
|
|
`endif
|
|
}
|
|
|
|
exec function ToggleSpawning( optional string ZedTypeString )
|
|
{
|
|
if( AllowWaveCheats() && GameReplicationInfo.bMatchHasBegun )
|
|
{
|
|
if( ZedTypeString != "" )
|
|
{
|
|
//OverrideZedSpawnList( ZedTypeString );
|
|
}
|
|
|
|
if( GetStateName() == 'DebugSuspendWave' )
|
|
{
|
|
GotoState('PlayingWave');
|
|
}
|
|
else
|
|
{
|
|
GotoState('DebugSuspendWave');
|
|
}
|
|
}
|
|
}
|
|
|
|
exec function EndCurrentWave ( )
|
|
{
|
|
if( AllowWaveCheats() )
|
|
{
|
|
DebugKillZeds();
|
|
WaveEnded(WEC_WaveWon);
|
|
}
|
|
}
|
|
|
|
exec function SetWave( byte NewWaveNum )
|
|
{
|
|
if( AllowWaveCheats() )
|
|
{
|
|
if( NewWaveNum <= WaveMax )
|
|
{
|
|
WaveNum = NewWaveNum - 1;
|
|
|
|
// stop, then restart
|
|
GotoState('DebugSuspendWave');
|
|
GotoState('PlayingWave');
|
|
|
|
ResetAllPickups();
|
|
}
|
|
else
|
|
{
|
|
`log("SetWave: new wave num must be <= "$WaveMax);
|
|
}
|
|
}
|
|
}
|
|
|
|
exec function WinMatch()
|
|
{
|
|
if( AllowWaveCheats() )
|
|
{
|
|
WaveNum = SpawnManager.WaveSettings.Waves.Length;
|
|
WaveEnded(WEC_WaveWon);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* state PlayingWave
|
|
*********************************************************************************************/
|
|
|
|
/** Returns true if wave gameplay is active */
|
|
function bool IsWaveActive();
|
|
|
|
State PlayingWave
|
|
{
|
|
function BeginState( Name PreviousStateName )
|
|
{
|
|
MyKFGRI.SetWaveActive(TRUE, GetGameIntensityForMusic());
|
|
MyKFGRI.VoteCollector.ResetSkipTraderVote();
|
|
|
|
StartWave();
|
|
if ( AllowBalanceLogging() )
|
|
{
|
|
LogPlayersDosh(GBE_WaveStart);
|
|
}
|
|
}
|
|
|
|
function bool IsWaveActive()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/** Starts a new Wave */
|
|
function StartWave()
|
|
{
|
|
local int WaveBuffer;
|
|
local KFPlayerController KFPC;
|
|
|
|
//closes trader on server
|
|
if (MyKFGRI.OpenedTrader != none)
|
|
{
|
|
MyKFGRI.CloseTrader();
|
|
NotifyTraderClosed();
|
|
}
|
|
|
|
WaveBuffer = 0;
|
|
WaveNum++;
|
|
MyKFGRI.WaveNum = WaveNum;
|
|
|
|
if (IsMapObjectiveEnabled())
|
|
{
|
|
MyKFGRI.ClearPreviousObjective();
|
|
if (MyKFGRI.StartNextObjective())
|
|
{
|
|
WaveBuffer = ObjectiveSpawnDelay;
|
|
}
|
|
}
|
|
|
|
SetupNextWave(WaveBuffer);
|
|
|
|
NumAIFinishedSpawning = 0;
|
|
NumAISpawnsQueued = 0;
|
|
AIAliveCount = 0;
|
|
MyKFGRI.bForceNextObjective = false;
|
|
|
|
if( WorldInfo.NetMode != NM_DedicatedServer && Role == ROLE_Authority )
|
|
{
|
|
MyKFGRI.UpdateHUDWaveCount();
|
|
}
|
|
|
|
WaveStarted();
|
|
MyKFGRI.NotifyWaveStart();
|
|
MyKFGRI.AIRemaining = SpawnManager.WaveTotalAI;
|
|
MyKFGRI.WaveTotalAICount = SpawnManager.WaveTotalAI;
|
|
|
|
BroadcastLocalizedMessage(class'KFLocalMessage_Priority', GetWaveStartMessage());
|
|
|
|
SetupNextTrader();
|
|
|
|
ResetAllPickups();
|
|
|
|
`DialogManager.SetTraderTime( false );
|
|
|
|
// first spawn and music are delayed 5 seconds (KFAISpawnManager.TimeUntilNextSpawn == 5 initially), so line up dialog with them;
|
|
// fixes problem of clients not being ready to receive dialog at the instant the match starts;
|
|
SetTimer( 5.f, false, nameof(PlayWaveStartDialog) );
|
|
|
|
|
|
//Reset Supplier perks here
|
|
ForEach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if (KFPC.GetPerk() != none)
|
|
{
|
|
KFPC.GetPerk().OnWaveStart();
|
|
}
|
|
}
|
|
}
|
|
|
|
function SetupNextWave(int WaveBuffer)
|
|
{
|
|
SpawnManager.SetupNextWave(WaveNum-1, WaveBuffer);
|
|
}
|
|
|
|
function byte GetWaveStartMessage()
|
|
{
|
|
if (MyKFGRI.IsBossWave())
|
|
{
|
|
return GMT_WaveSBoss;
|
|
}
|
|
|
|
return GMT_WaveStart;
|
|
}
|
|
|
|
/** Called to reset all the types of pickups */
|
|
function ResetAllPickups()
|
|
{
|
|
// for the boss wave request all ammo pickups
|
|
if (MyKFGRI.IsBossWave())
|
|
{
|
|
// -1, so that we always have a different pickup to activate
|
|
NumAmmoPickups = Max(AmmoPickups.Length - 1, 0);
|
|
}
|
|
|
|
Super.ResetAllPickups();
|
|
}
|
|
|
|
/** Overridden to scale the number of active pickups by wave */
|
|
function ResetPickups( array<KFPickupFactory> PickupList, int NumPickups )
|
|
{
|
|
if(NumPickups != 0)
|
|
{
|
|
NumPickups *= (float(WaveNum) / float(WaveMax));
|
|
|
|
// make sure to have at least 1 ammo pickup in the level, and if it's wave 2 or later make sure there's
|
|
// at least one weapon pickup
|
|
if( NumPickups == 0 && PickupList.Length > 0 && (WaveNum > 1 || KFPickupFactory_Ammo(PickupList[0]) != none) )
|
|
{
|
|
NumPickups = 1;
|
|
}
|
|
}
|
|
super.ResetPickups( PickupList, NumPickups );
|
|
}
|
|
|
|
/** Sets NextTrader and re-inits trader list if necessary */
|
|
function SetupNextTrader()
|
|
{
|
|
local byte NextTraderIndex;
|
|
|
|
// Try to set the scripted trader first
|
|
if( ScriptedTrader != none )
|
|
{
|
|
MyKFGRI.NextTrader = ScriptedTrader;
|
|
return;
|
|
}
|
|
|
|
if( TraderList.Length > 0 )
|
|
{
|
|
NextTraderIndex = DetermineNextTraderIndex();
|
|
if( NextTraderIndex >= 0 && NextTraderIndex < TraderList.Length )
|
|
{
|
|
MyKFGRI.NextTrader = TraderList[ NextTraderIndex ];
|
|
TraderList.Remove( NextTraderIndex, 1 );
|
|
}
|
|
}
|
|
|
|
if( TraderList.Length <= 0 )
|
|
{
|
|
InitTraderList();
|
|
}
|
|
}
|
|
|
|
/** Picks next trader index and allows for mutator hook */
|
|
function byte DetermineNextTraderIndex()
|
|
{
|
|
local byte NextTraderIndex;
|
|
|
|
NextTraderIndex = Rand( TraderList.Length );
|
|
|
|
`if(`__TW_SDK_)
|
|
if( BaseMutator != none )
|
|
{
|
|
BaseMutator.ModifyNextTraderIndex( NextTraderIndex );
|
|
}
|
|
`endif
|
|
|
|
return NextTraderIndex;
|
|
}
|
|
|
|
function WaveStarted()
|
|
{
|
|
local array<SequenceObject> AllWaveStartEvents, AllWaveProgressEvents;
|
|
local array<int> OutputLinksToActivate;
|
|
local KFSeqEvent_WaveStart WaveStartEvt;
|
|
local KFSeqEvent_WaveProgress WaveProgressEvt;
|
|
local Sequence GameSeq;
|
|
local int i;
|
|
local KFPlayerController KFPC;
|
|
|
|
`AnalyticsLog(("wave_start",
|
|
None,
|
|
"#"$WaveNum,
|
|
"#"$GetLivingPlayerCount()));
|
|
|
|
GameConductor.ResetWaveStats();
|
|
|
|
ForEach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if( !KFPC.bDemoOwner )
|
|
{
|
|
`AnalyticsLog(("pc_wave_start",
|
|
KFPC.PlayerReplicationInfo,
|
|
"#"$WaveNum,
|
|
KFPC.GetPerk().Class.Name,
|
|
KFPC.GetPerk().GetLevel(),
|
|
"#"$KFPC.PlayerReplicationInfo.Score,
|
|
KFPC.Pawn != none ? KFInventoryManager(KFPC.Pawn.InvManager).DumpInventory() : "",
|
|
KFPC.GetPerk().DumpPerkLoadout(),
|
|
KFPC.PlayerReplicationInfo.GetTeamNum()
|
|
));
|
|
}
|
|
|
|
`QALog("Player Name:" @ KFPC.PlayerReplicationInfo.PlayerName @ "Dosh" @ KFPC.PlayerReplicationInfo.Score, bLogScoring);
|
|
}
|
|
|
|
|
|
// Get the gameplay sequence.
|
|
GameSeq = WorldInfo.GetGameSequence();
|
|
|
|
if( GameSeq != none )
|
|
{
|
|
GameSeq.FindSeqObjectsByClass(class'KFSeqEvent_WaveStart', TRUE, AllWaveStartEvents);
|
|
|
|
for( i = 0; i < AllWaveStartEvents.Length; i++ )
|
|
{
|
|
WaveStartEvt = KFSeqEvent_WaveStart(AllWaveStartEvents[i]);
|
|
|
|
if( WaveStartEvt != None )
|
|
{
|
|
WaveStartEvt.Reset();
|
|
WaveStartEvt.SetWaveNum( WaveNum, WaveMax );
|
|
if( MyKFGRI.IsBossWave() && WaveStartEvt.OutputLinks.Length > 1 )
|
|
{
|
|
OutputLinksToActivate.AddItem( 1 );
|
|
}
|
|
else
|
|
{
|
|
OutputLinksToActivate.AddItem( 0 );
|
|
}
|
|
WaveStartEvt.CheckActivate( self, self,, OutputLinksToActivate );
|
|
}
|
|
}
|
|
|
|
GameSeq.FindSeqObjectsByClass(class'KFSeqEvent_WaveProgress', TRUE, AllWaveProgressEvents);
|
|
|
|
for( i = 0; i < AllWaveProgressEvents.Length; i++ )
|
|
{
|
|
WaveProgressEvt = KFSeqEvent_WaveProgress(AllWaveProgressEvents[i]);
|
|
|
|
if( WaveProgressEvt != None )
|
|
{
|
|
WaveProgressEvt.Reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
//So the server browser can have our new wave information
|
|
UpdateGameSettings();
|
|
|
|
bWaveStarted = true;
|
|
}
|
|
|
|
/** Do something when there are no AIs left */
|
|
function CheckWaveEnd( optional bool bForceWaveEnd = false )
|
|
{
|
|
if (WorldInfo.NetMode == NM_DedicatedServer)
|
|
{
|
|
//`REMOVEMESOON_ZombieServerLog("KFGameInfo_Survival.CheckWaveEnd - bForceWaveEnd: "$bForceWaveEnd$"; bMatchHasBegun: "$MyKFGRI.bMatchHasBegun$"; GetLivingPlayerCount(): "$GetLivingPlayerCount()$"; AIAliveCount: "$AIAliveCount$"; IsWaveActive(): "$IsWaveActive()$"; IsFinishedSpawning(): "$SpawnManager.IsFinishedSpawning());
|
|
}
|
|
|
|
if( !MyKFGRI.bMatchHasBegun )
|
|
{
|
|
`log("KFGameInfo - CheckWaveEnd - Cannot check if wave has ended since match has not begun. ");
|
|
return;
|
|
}
|
|
|
|
`log("KFGameInfo.CheckWaveEnd() AIAliveCount:" @ AIAliveCount, SpawnManager.bLogAISpawning);
|
|
|
|
if( GetLivingPlayerCount() <= 0 )
|
|
{
|
|
// `log("KFGameInfo.CheckWaveEnd() - Call Wave Ended - WEC_TeamWipedOut");
|
|
WaveEnded(WEC_TeamWipedOut);
|
|
}
|
|
else if( (AIAliveCount <= 0 && IsWaveActive() && SpawnManager.IsFinishedSpawning()) || bForceWaveEnd )
|
|
{
|
|
//`log("KFGameInfo.CheckWaveEnd() - Call Wave Ended - WEC_WaveWon");
|
|
WaveEnded(WEC_WaveWon);
|
|
}
|
|
}
|
|
|
|
/** The wave ended */
|
|
function WaveEnded(EWaveEndCondition WinCondition)
|
|
{
|
|
local array<SequenceObject> AllWaveEndEvents;
|
|
local array<int> OutputLinksToActivate;
|
|
local KFSeqEvent_WaveEnd WaveEndEvt;
|
|
local Sequence GameSeq;
|
|
local int i;
|
|
local KFPlayerController KFPC;
|
|
|
|
if(!bWaveStarted && !MyKFGRI.bTraderIsOpen)
|
|
return;
|
|
|
|
if (WorldInfo.NetMode == NM_DedicatedServer)
|
|
{
|
|
scripttrace();
|
|
//`REMOVEMESOON_ZombieServerLog("KFGameInfo_Survival.WaveEnded - WinCondition: "$WinCondition$"; WaveNum: "$WaveNum$"; WaveMax: "$WaveMax);
|
|
}
|
|
|
|
// Get the gameplay sequence.
|
|
GameSeq = WorldInfo.GetGameSequence();
|
|
|
|
if( GameSeq != none )
|
|
{
|
|
GameSeq.FindSeqObjectsByClass( class'KFSeqEvent_WaveEnd', TRUE, AllWaveEndEvents );
|
|
for( i = 0; i < AllWaveEndEvents.Length; ++i )
|
|
{
|
|
WaveEndEvt = KFSeqEvent_WaveEnd( AllWaveEndEvents[i] );
|
|
|
|
if( WaveEndEvt != None )
|
|
{
|
|
WaveEndEvt.Reset();
|
|
WaveEndEvt.SetWaveNum( WaveNum, WaveMax );
|
|
if( MyKFGRI.IsBossWave() && WaveEndEvt.OutputLinks.Length > 1 )
|
|
{
|
|
OutputLinksToActivate.AddItem( 1 );
|
|
}
|
|
else
|
|
{
|
|
OutputLinksToActivate.AddItem( 0 );
|
|
}
|
|
WaveEndEvt.CheckActivate( self, self,, OutputLinksToActivate );
|
|
}
|
|
}
|
|
}
|
|
BroadcastLocalizedMessage(class'KFLocalMessage_Priority', GMT_WaveEnd);
|
|
MyKFGRI.DeactivateObjective();
|
|
MyKFGRI.NotifyWaveEnded();
|
|
`DialogManager.SetTraderTime( !MyKFGRI.IsBossWave() );
|
|
|
|
`AnalyticsLog(("wave_end", None, "#"$WaveNum, GetEnum(enum'EWaveEndCondition',WinCondition), "#"$GameConductor.CurrentWaveZedVisibleAverageLifeSpan));
|
|
|
|
// IsPlayInEditor check was added to fix a scaleform crash that would call an actionscript function
|
|
// as scaleform was being destroyed. This issue only occurs when playing in the editor
|
|
if( WinCondition == WEC_TeamWipedOut && !class'WorldInfo'.static.IsPlayInEditor())
|
|
{
|
|
EndOfMatch(false);
|
|
}
|
|
else if( WinCondition == WEC_WaveWon )
|
|
{
|
|
RewardSurvivingPlayers();
|
|
UpdateWaveEndDialogInfo();
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if (KFPC != none)
|
|
{
|
|
KFPC.OnWaveComplete(WaveNum);
|
|
}
|
|
}
|
|
|
|
if( WaveNum < WaveMax )
|
|
{
|
|
GotoState( 'TraderOpen', 'Begin' );
|
|
}
|
|
else
|
|
{
|
|
EndOfMatch(true);
|
|
}
|
|
}
|
|
|
|
// To allow any statistics that are recorded on the very last zed killed at the end of the wave,
|
|
// wait a single frame to allow them to finalize.
|
|
SetTimer( WorldInfo.DeltaSeconds, false, nameOf(Timer_FinalizeEndOfWaveStats) );
|
|
bWaveStarted=false;
|
|
}
|
|
|
|
/** All stats should be finalized here */
|
|
function Timer_FinalizeEndOfWaveStats()
|
|
{
|
|
local bool bOpeningTrader;
|
|
local KFPlayerController KFPC;
|
|
|
|
bOpeningTrader = MyKFGRI.bTraderIsOpen && !IsInState( 'MatchEnded' ) && !IsInState( 'RoundEnded ');
|
|
foreach WorldInfo.AllControllers( class'KFPlayerController', KFPC )
|
|
{
|
|
// submit online player analytics
|
|
LogWaveEndAnalyticsFor( KFPC );
|
|
|
|
// submit aar/dialog stats
|
|
KFPC.SubmitPostWaveStats( bOpeningTrader );
|
|
|
|
`QALog( "Player Name:" @ KFPC.PlayerReplicationInfo.PlayerName @ "Dosh" @ KFPC.PlayerReplicationInfo.Score, bLogScoring );
|
|
}
|
|
}
|
|
|
|
/** Game Analytics */
|
|
function LogWaveEndAnalyticsFor(KFPlayerController KFPC)
|
|
{
|
|
local int i;
|
|
local array<WeaponDamage> Weapons;
|
|
|
|
if( KFPC.bDemoOwner )
|
|
{
|
|
return;
|
|
}
|
|
|
|
`AnalyticsLog(("pc_wave_stats",
|
|
KFPC.PlayerReplicationInfo,
|
|
"#"$WaveNum,
|
|
"#"$KFPC.MatchStats.GetHealGivenInWave(),
|
|
"#"$KFPC.MatchStats.GetHeadShotsInWave(),
|
|
"#"$KFPC.MatchStats.GetDoshEarnedInWave(),
|
|
"#"$KFPC.MatchStats.GetDamageTakenInWave(),
|
|
"#"$KFPC.MatchStats.GetDamageDealtInWave(),
|
|
"#"$KFPC.ShotsFired,
|
|
"#"$KFPC.ShotsHit,
|
|
"#"$KFPC.ShotsHitHeadshot ));
|
|
|
|
`AnalyticsLog(("pc_wave_end",
|
|
KFPC.PlayerReplicationInfo,
|
|
"#"$WaveNum,
|
|
KFPC.GetPerk().Class.Name,
|
|
"#"$KFPC.GetPerk().GetLevel(),
|
|
"#"$KFPC.PlayerReplicationInfo.Score,
|
|
"#"$KFPC.PlayerReplicationInfo.Kills,
|
|
(KFPC.Pawn != none && KFPC.Pawn.InvManager != none) ? KFInventoryManager(KFPC.Pawn.InvManager).DumpInventory() : ""));
|
|
|
|
KFPC.MatchStats.GetTopWeapons( 3, Weapons );
|
|
|
|
for ( i = 0; i < Weapons.Length; ++i )
|
|
{
|
|
`AnalyticsLog(("pc_weapon_stats",
|
|
KFPC.PlayerReplicationInfo,
|
|
"#"$WaveNum,
|
|
Weapons[i].WeaponDef.Name,
|
|
"#"$Weapons[i].DamageAmount,
|
|
"#"$Weapons[i].HeadShots, // really head kills
|
|
"#"$Weapons[i].LargeZedKills ));
|
|
}
|
|
}
|
|
|
|
/** Allow specific map to override the spawn class. Default implementation returns from the AI class list. */
|
|
function class<KFPawn_Monster> GetAISpawnType(EAIType AIType)
|
|
{
|
|
local KFMapInfo KFMI;
|
|
local KFGameReplicationInfo KFGRI;
|
|
local array<SpawnReplacement> SpawnReplacements;
|
|
local int i;
|
|
|
|
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
|
|
if (KFMI != none)
|
|
{
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
if (KFMI.bUsePresetObjectives &&
|
|
((!KFMI.WaveHasPresetObjectives(WaveNum, GameLength)) ||
|
|
(KFGRI != none && KFGRI.ObjectiveInterface != none && KFGRI.ObjectiveInterface.CanActivateObjective())))
|
|
{
|
|
switch (GameLength)
|
|
{
|
|
case GL_Short:
|
|
SpawnReplacements = KFMI.PresetWaveObjectives.ShortObjectives[WaveNum-1].SpawnReplacements;
|
|
break;
|
|
|
|
case GL_Normal:
|
|
SpawnReplacements = KFMI.PresetWaveObjectives.MediumObjectives[WaveNum-1].SpawnReplacements;
|
|
break;
|
|
|
|
case GL_Long:
|
|
SpawnReplacements = KFMI.PresetWaveObjectives.LongObjectives[WaveNum-1].SpawnReplacements;
|
|
break;
|
|
};
|
|
|
|
if (SpawnReplacements.Length > 0)
|
|
{
|
|
for (i = 0; i < SpawnReplacements.Length; ++i)
|
|
{
|
|
if (SpawnReplacements[i].SpawnEntry == AIType && FRand() < SpawnReplacements[i].PercentChance)
|
|
{
|
|
if (SpawnReplacements[i].NewClass.Length > 0)
|
|
{
|
|
return SpawnReplacements[i].NewClass[Rand(SpawnReplacements[i].NewClass.Length)];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return AIClassList[AIType];
|
|
}
|
|
|
|
/** Scale to use against WaveTotalAI to determine full wave size */
|
|
function float GetTotalWaveCountScale()
|
|
{
|
|
local float WaveScale;
|
|
local KFMapInfo KFMI;
|
|
local KFGameReplicationInfo KFGRI;
|
|
|
|
WaveScale = super.GetTotalWaveCountScale();
|
|
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
|
|
if (KFMI != none)
|
|
{
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
if (KFMI.bUsePresetObjectives &&
|
|
((!KFMI.WaveHasPresetObjectives(WaveNum, GameLength)) ||
|
|
(KFGRI != none && KFGRI.ObjectiveInterface != none && KFGRI.ObjectiveInterface.CanActivateObjective())))
|
|
{
|
|
switch (GameLength)
|
|
{
|
|
case GL_Short:
|
|
WaveScale *= KFMI.PresetWaveObjectives.ShortObjectives[WaveNum-1].WaveScale;
|
|
break;
|
|
|
|
case GL_Normal:
|
|
WaveScale *= KFMI.PresetWaveObjectives.MediumObjectives[WaveNum-1].WaveScale;
|
|
break;
|
|
|
|
case GL_Long:
|
|
WaveScale *= KFMI.PresetWaveObjectives.LongObjectives[WaveNum-1].WaveScale;
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
return WaveScale;
|
|
}
|
|
|
|
/** Allow specific game types to modify the spawn rate at a global level */
|
|
function float GetGameInfoSpawnRateMod()
|
|
{
|
|
local float SpawnRateMod;
|
|
local KFGameReplicationInfo KFGRI;
|
|
local KFMapInfo KFMI;
|
|
local int NumPlayersAlive;
|
|
|
|
SpawnRateMod = super.GetGameInfoSpawnRateMod();
|
|
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
if (KFGRI == none)
|
|
{
|
|
return SpawnRateMod;
|
|
}
|
|
|
|
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
|
|
if (KFMI == none)
|
|
{
|
|
return SpawnRateMod;
|
|
}
|
|
|
|
if (KFMI.bUsePresetObjectives &&
|
|
((!KFMI.WaveHasPresetObjectives(WaveNum, GameLength)) ||
|
|
(KFGRI != none && KFGRI.ObjectiveInterface != none && KFGRI.ObjectiveInterface.CanActivateObjective())))
|
|
{
|
|
switch (GameLength)
|
|
{
|
|
case GL_Short:
|
|
NumPlayersAlive = Clamp(KFGRI.GetNumPlayersAlive(), 1, KFMI.PresetWaveObjectives.ShortObjectives[WaveNum-1].PerPlayerSpawnRateMod.Length) - 1;
|
|
SpawnRateMod *= KFMI.PresetWaveObjectives.ShortObjectives[WaveNum-1].PerPlayerSpawnRateMod[NumPlayersAlive];
|
|
break;
|
|
|
|
case GL_Normal:
|
|
NumPlayersAlive = Clamp(KFGRI.GetNumPlayersAlive(), 1, KFMI.PresetWaveObjectives.MediumObjectives[WaveNum-1].PerPlayerSpawnRateMod.Length) - 1;
|
|
SpawnRateMod *= KFMI.PresetWaveObjectives.MediumObjectives[WaveNum-1].PerPlayerSpawnRateMod[NumPlayersAlive];
|
|
break;
|
|
|
|
case GL_Long:
|
|
NumPlayersAlive = Clamp(KFGRI.GetNumPlayersAlive(), 1, KFMI.PresetWaveObjectives.LongObjectives[WaveNum-1].PerPlayerSpawnRateMod.Length) - 1;
|
|
SpawnRateMod *= KFMI.PresetWaveObjectives.LongObjectives[WaveNum-1].PerPlayerSpawnRateMod[NumPlayersAlive];
|
|
break;
|
|
};
|
|
}
|
|
|
|
return SpawnRateMod;
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* state TraderOpen
|
|
*********************************************************************************************/
|
|
|
|
/** Called when TimeBetweenWaves expires */
|
|
function CloseTraderTimer();
|
|
|
|
function SkipTrader(int TimeAfterSkipTrader)
|
|
{
|
|
SetTimer(TimeAfterSkipTrader, false, nameof(CloseTraderTimer));
|
|
}
|
|
|
|
/** Cleans up anything from the previous wave that needs to be removed for trader time */
|
|
function DoTraderTimeCleanup();
|
|
|
|
/** Handle functionality for opening trader */
|
|
function OpenTrader()
|
|
{
|
|
MyKFGRI.OpenTrader(TimeBetweenWaves);
|
|
NotifyTraderOpened();
|
|
}
|
|
|
|
State TraderOpen
|
|
{
|
|
function BeginState( Name PreviousStateName )
|
|
{
|
|
local KFPlayerController KFPC;
|
|
|
|
MyKFGRI.SetWaveActive(FALSE, GetGameIntensityForMusic());
|
|
|
|
ForEach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
if( KFPC.GetPerk() != none )
|
|
{
|
|
KFPC.GetPerk().OnWaveEnded();
|
|
}
|
|
KFPC.ApplyPendingPerks();
|
|
}
|
|
|
|
// Restart players
|
|
StartHumans();
|
|
|
|
OpenTrader();
|
|
|
|
if ( AllowBalanceLogging() )
|
|
{
|
|
LogPlayersDosh(GBE_TraderOpen);
|
|
}
|
|
|
|
SetTimer(TimeBetweenWaves, false, nameof(CloseTraderTimer));
|
|
}
|
|
|
|
/** Cleans up anything from the previous wave that needs to be removed for trader time */
|
|
function DoTraderTimeCleanup()
|
|
{
|
|
local KFProj_BloatPukeMine PukeMine;
|
|
|
|
// Destroy all lingering explosions
|
|
MyKFGRI.FadeOutLingeringExplosions();
|
|
|
|
// Destroy all puke mine projectiles
|
|
foreach DynamicActors( class'KFProj_BloatPukeMine', PukeMine )
|
|
{
|
|
PukeMine.FadeOut();
|
|
}
|
|
}
|
|
|
|
/** Called when TimeBetweenWaves expires */
|
|
function CloseTraderTimer()
|
|
{
|
|
GotoState('PlayingWave');
|
|
}
|
|
|
|
function EndState( Name NextStateName )
|
|
{
|
|
if ( AllowBalanceLogging() )
|
|
{
|
|
LogPlayersInventory();
|
|
}
|
|
}
|
|
|
|
/** Special handling for survival/wave mode. Reduces health to 1 */
|
|
function bool PreventDeath(Pawn KilledPawn, Controller Killer, class<DamageType> DamageType, vector HitLocation)
|
|
{
|
|
// (player-only) Prevent enemy team kills during trader time to fix players
|
|
// missing the respawn and then dying from certain attacks that can do damage
|
|
// just after the last zed dies (e.g. explosives/husk suicide, damage over time)
|
|
if ( KilledPawn.Controller != None && KilledPawn.Controller.bIsPlayer
|
|
&& (Killer == none || (KilledPawn.GetTeamNum() != Killer.GetTeamNum()))
|
|
// @hack: Somehow we can get a suicide where Killer!=Victim?
|
|
&& DamageType != class'DmgType_Suicided' )
|
|
{
|
|
// sanity check - The killer pawn should be dead or are detached by now
|
|
if ( Killer == none || Killer.Pawn == None || !Killer.Pawn.IsAliveAndWell() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return Global.PreventDeath(KilledPawn, Killer, DamageType, HitLocation);
|
|
}
|
|
|
|
Begin:
|
|
Sleep(0.1f);
|
|
DoTraderTimeCleanup();
|
|
}
|
|
|
|
/** Tell Kismet a trader opened */
|
|
function NotifyTraderOpened()
|
|
{
|
|
local array<SequenceObject> AllTraderOpenedEvents;
|
|
local array<int> OutputLinksToActivate;
|
|
local KFSeqEvent_TraderOpened TraderOpenedEvt;
|
|
local Sequence GameSeq;
|
|
local int i;
|
|
|
|
// Get the gameplay sequence.
|
|
GameSeq = WorldInfo.GetGameSequence();
|
|
|
|
if( GameSeq != none )
|
|
{
|
|
GameSeq.FindSeqObjectsByClass(class'KFSeqEvent_TraderOpened', TRUE, AllTraderOpenedEvents);
|
|
|
|
for( i = 0; i < AllTraderOpenedEvents.Length; i++ )
|
|
{
|
|
TraderOpenedEvt = KFSeqEvent_TraderOpened(AllTraderOpenedEvents[i]);
|
|
|
|
if( TraderOpenedEvt != None )
|
|
{
|
|
TraderOpenedEvt.Reset();
|
|
TraderOpenedEvt.SetWaveNum( WaveNum, WaveMax );
|
|
if( MyKFGRI.IsFinalWave() && TraderOpenedEvt.OutputLinks.Length > 1 )
|
|
{
|
|
OutputLinksToActivate.AddItem( 1 );
|
|
}
|
|
else
|
|
{
|
|
OutputLinksToActivate.AddItem( 0 );
|
|
}
|
|
TraderOpenedEvt.CheckActivate( self, self,, OutputLinksToActivate );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Tell Kismet a Trader closed */
|
|
function NotifyTraderClosed()
|
|
{
|
|
local KFSeqEvent_TraderClosed TraderClosedEvt;
|
|
local array<SequenceObject> AllTraderClosedEvents;
|
|
local Sequence GameSeq;
|
|
local int i;
|
|
|
|
GameSeq = WorldInfo.GetGameSequence();
|
|
if (GameSeq != none)
|
|
{
|
|
GameSeq.FindSeqObjectsByClass(class'KFSeqEvent_TraderClosed', true, AllTraderClosedEvents);
|
|
for (i = 0; i < AllTraderClosedEvents.Length; ++i)
|
|
{
|
|
TraderClosedEvt = KFSeqEvent_TraderClosed(AllTraderClosedEvents[i]);
|
|
if (TraderClosedEvt != none)
|
|
{
|
|
TraderClosedEvt.Reset();
|
|
TraderClosedEvt.SetWaveNum(WaveNum, WaveMax);
|
|
TraderClosedEvt.CheckActivate(self, self);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
final function ForceChangeLevel()
|
|
{
|
|
bGameRestarted = false;
|
|
bChangeLevels = true;
|
|
RestartGame();
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* state MatchEnded
|
|
*********************************************************************************************/
|
|
|
|
State MatchEnded
|
|
{
|
|
function BeginState( Name PreviousStateName )
|
|
{
|
|
if (WorldInfo.NetMode == NM_DedicatedServer)
|
|
{
|
|
//`REMOVEMESOON_ZombieServerLog("KFGameInfo_Survival:MatchEnded.BeginState - PreviousStateName: "$PreviousStateName);
|
|
}
|
|
|
|
`log("KFGameInfo_Survival - MatchEnded.BeginState - AARDisplayDelay:" @ AARDisplayDelay);
|
|
|
|
MyKFGRI.EndGame();
|
|
MyKFGRI.bWaitingForAAR = true; //@HSL - JRO - 6/15/2016 - Make sure we're still at full speed before the end of game menu shows up
|
|
|
|
if ( AllowBalanceLogging() )
|
|
{
|
|
LogPlayersKillCount();
|
|
}
|
|
|
|
SetTimer(90.f, false, 'ForceChangeLevel');
|
|
SetTimer(1.f, false, nameof(ProcessAwards));
|
|
SetTimer(AARDisplayDelay, false, nameof(ShowPostGameMenu));
|
|
}
|
|
|
|
event Timer()
|
|
{
|
|
if (WorldInfo.NetMode == NM_DedicatedServer)
|
|
{
|
|
//`REMOVEMESOON_ZombieServerLog("KFGameInfo_Survival:MatchEnded.Timer - NumPlayers: "$NumPlayers);
|
|
}
|
|
|
|
global.Timer();
|
|
if (NumPlayers == 0)
|
|
{
|
|
RestartGame();
|
|
}
|
|
}
|
|
}
|
|
|
|
function EndOfMatch(bool bVictory)
|
|
{
|
|
local KFPlayerController KFPC;
|
|
|
|
if (WorldInfo.NetMode == NM_DedicatedServer)
|
|
{
|
|
//`REMOVEMESOON_ZombieServerLog("KFGameInfo_Survival.EndOfMatch - bVictory: "$bVictory);
|
|
}
|
|
|
|
`AnalyticsLog(("match_end", None, "#"$WaveNum, "#"$(bVictory ? "1" : "0"), "#"$GameConductor.ZedVisibleAverageLifespan));
|
|
|
|
if(bVictory)
|
|
{
|
|
SetTimer(EndCinematicDelay, false, nameof(SetWonGameCamera));
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
KFPC.ClientWonGame( WorldInfo.GetMapName( true ), GameDifficulty, GameLength, IsMultiplayerGame() );
|
|
}
|
|
|
|
BroadcastLocalizedMessage(class'KFLocalMessage_Priority', GMT_MatchWon);
|
|
}
|
|
else
|
|
{
|
|
BroadcastLocalizedMessage(class'KFLocalMessage_Priority', GMT_MatchLost);
|
|
SetZedsToVictoryState();
|
|
}
|
|
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
KFPC.ClientGameOver( WorldInfo.GetMapName(true), GameDifficulty, GameLength, IsMultiplayerGame(), WaveNum );
|
|
}
|
|
|
|
GotoState('MatchEnded');
|
|
}
|
|
|
|
//Get Top voted map
|
|
function string GetNextMap()
|
|
{
|
|
local KFGameReplicationInfo KFGRI;
|
|
local int NextMapIndex;
|
|
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
if( KFGRI != none )
|
|
{
|
|
NextMapIndex = KFGRI.VoteCollector.GetNextMap();
|
|
}
|
|
|
|
if( NextMapIndex != INDEX_NONE )
|
|
{
|
|
if(WorldInfo.NetMode == NM_Standalone)
|
|
{
|
|
return KFGRI.VoteCollector.Maplist[NextMapIndex];
|
|
}
|
|
else
|
|
{
|
|
return GameMapCycles[ActiveMapCycle].Maps[NextMapIndex];
|
|
}
|
|
|
|
}
|
|
|
|
return super.GetNextMap();
|
|
}
|
|
|
|
function SetWonGameCamera()
|
|
{
|
|
local KFPlayerController KFPC;
|
|
|
|
foreach WorldInfo.AllControllers( class'KFPlayerController', KFPC )
|
|
{
|
|
KFPC.ServerCamera( 'ThirdPerson' );
|
|
}
|
|
}
|
|
|
|
function SetZedsToVictoryState()
|
|
{
|
|
local KFAIController KFAIC;
|
|
|
|
foreach WorldInfo.AllControllers( class'KFAIController', KFAIC )
|
|
{
|
|
// Have the zeds enter their victory state
|
|
KFAIC.EnterZedVictoryState();
|
|
}
|
|
}
|
|
|
|
function ShowPostGameMenu()
|
|
{
|
|
local KFGameReplicationInfo KFGRI;
|
|
|
|
`log("KFGameInfo_Survival - ShowPostGameMenu");
|
|
|
|
MyKFGRI.bWaitingForAAR = false; //@HSL - JRO - 6/15/2016 - Make sure we're still at full speed before the end of game menu shows up
|
|
|
|
bEnableDeadToVOIP=true; //Being dead at this point is irrelevant. Allow players to talk about AAR -ZG
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
|
|
if(KFGRI != none)
|
|
{
|
|
KFGRI.OnOpenAfterActionReport( GetEndOfMatchTime() );
|
|
}
|
|
|
|
class'EphemeralMatchStats'.Static.SendMapOptionsAndOpenAARMenu();
|
|
|
|
UpdateCurrentMapVoteTime( GetEndOfMatchTime(), true);
|
|
|
|
WorldInfo.TWPushLogs();
|
|
}
|
|
|
|
function float GetEndOfMatchTime()
|
|
{
|
|
return MapVoteDuration;
|
|
}
|
|
|
|
function ProcessAwards()
|
|
{
|
|
class'EphemeralMatchStats'.Static.ProcessPostGameStats();
|
|
}
|
|
|
|
function UpdateCurrentMapVoteTime(byte NewTime, optional bool bStartTime)
|
|
{
|
|
if(WorldInfo.GRI.RemainingTime > NewTime || bStartTime)
|
|
{
|
|
ClearTimer(nameof(RestartGame));
|
|
SetTimer(NewTime, false, nameof(TryRestartGame));
|
|
WorldInfo.GRI.RemainingMinute = NewTime;
|
|
WorldInfo.GRI.RemainingTime = NewTime;
|
|
}
|
|
|
|
//in the case that the server has a 0 for the time we still want to be able to trigger a server travel.
|
|
if(NewTime <= 0 || WorldInfo.GRI.RemainingTime <= 0)
|
|
{
|
|
TryRestartGame();
|
|
}
|
|
}
|
|
|
|
function TryRestartGame()
|
|
{
|
|
RestartGame();
|
|
}
|
|
|
|
/************************************************************************************
|
|
* @name Objectives
|
|
***********************************************************************************/
|
|
|
|
function bool IsMapObjectiveEnabled()
|
|
{
|
|
return bEnableMapObjectives;
|
|
}
|
|
|
|
function ObjectiveFailed()
|
|
{
|
|
MyKFGRI.DeactivateObjective();
|
|
}
|
|
|
|
function OnEndlessSpawningObjectiveDeactivated()
|
|
{
|
|
CheckWaveEnd();
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* state DebugSuspendWave
|
|
*********************************************************************************************/
|
|
|
|
state DebugSuspendWave
|
|
{
|
|
ignores CheckWaveEnd;
|
|
|
|
function BeginState( Name PreviousStateName )
|
|
{
|
|
local PlayerController PC;
|
|
|
|
DebugKillZeds();
|
|
|
|
foreach WorldInfo.AllControllers(class'PlayerController', PC)
|
|
{
|
|
PC.ClientMessage("Survival: Spawn suspended");
|
|
}
|
|
}
|
|
|
|
function EndState( Name NextStateName )
|
|
{
|
|
local PlayerController PC;
|
|
|
|
foreach WorldInfo.AllControllers(class'PlayerController', PC)
|
|
{
|
|
PC.ClientMessage("Survival: Spawn resumed");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invoke the CheatManager function. For some reason calling
|
|
* ConsoleCommand directly on the GI does not follow through, so
|
|
* as a workaround just find a PC and assume they have cheats on
|
|
*/
|
|
function DebugKillZeds()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
if( AllowWaveCheats() )
|
|
{
|
|
foreach WorldInfo.AllControllers(class'PlayerController', PC)
|
|
{
|
|
if (KFDemoRecSpectator(PC) == none)
|
|
{
|
|
PC.ConsoleCommand("KillZeds");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DefaultProperties
|
|
{
|
|
TimeBetweenWaves=60 //This is going to be a difficulty setting later
|
|
EndCinematicDelay=4
|
|
AARDisplayDelay=15
|
|
bCanPerkAlwaysChange=false
|
|
MaxGameDifficulty=3
|
|
bWaveStarted=false
|
|
|
|
ObjectiveSpawnDelay=5
|
|
|
|
SpawnManagerClasses(0)=class'KFGame.KFAISpawnManager_Short'
|
|
SpawnManagerClasses(1)=class'KFGame.KFAISpawnManager_Normal'
|
|
SpawnManagerClasses(2)=class'KFGame.KFAISpawnManager_Long'
|
|
|
|
MaxRespawnDosh(0)=1750.f // Normal
|
|
MaxRespawnDosh(1)=1550.f // Hard
|
|
MaxRespawnDosh(2)=1700.f // Suicidal //1550
|
|
MaxRespawnDosh(3)=1550.f // Hell On Earth //1000.0
|
|
|
|
GameplayEventsWriterClass=class'KFGame.KFGameplayEventsWriter'
|
|
TraderVoiceGroupClass=class'KFGameContent.KFTraderVoiceGroup_Default'
|
|
DifficultyInfoClass=class'KFGameDifficulty_Survival'
|
|
DifficultyInfoConsoleClass=class'KFGameDifficulty_Survival_Console'
|
|
|
|
// Preload content classes (by reference) to prevent load time hitches during gameplay
|
|
// and keeps the GC happy. This will also load client content -- via GRI.GameClass
|
|
AIClassList(AT_Clot)=class'KFGameContent.KFPawn_ZedClot_Cyst'
|
|
AIClassList(AT_AlphaClot)=class'KFGameContent.KFPawn_ZedClot_Alpha'
|
|
AIClassList(AT_SlasherClot)=class'KFGameContent.KFPawn_ZedClot_Slasher'
|
|
AIClassList(AT_Crawler)=class'KFGameContent.KFPawn_ZedCrawler'
|
|
AIClassList(AT_GoreFast)=class'KFGameContent.KFPawn_ZedGorefast'
|
|
AIClassList(AT_Stalker)=class'KFGameContent.KFPawn_ZedStalker'
|
|
AIClassList(AT_Scrake)=class'KFGameContent.KFPawn_ZedScrake'
|
|
AIClassList(AT_FleshPound)=class'KFGameContent.KFPawn_ZedFleshpound'
|
|
AIClassList(AT_FleshpoundMini)=class'KFGameContent.KFPawn_ZedFleshpoundMini'
|
|
AIClassList(AT_Bloat)=class'KFGameContent.KFPawn_ZedBloat'
|
|
AIClassList(AT_Siren)=class'KFGameContent.KFPawn_ZedSiren'
|
|
AIClassList(AT_Husk)=class'KFGameContent.KFPawn_ZedHusk'
|
|
|
|
AIClassList(AT_EliteClot)=class'KFGameContent.KFPawn_ZedClot_AlphaKing'
|
|
AIClassList(AT_EliteCrawler)=class'KFGameContent.KFPawn_ZedCrawlerKing'
|
|
AIClassList(AT_EliteGoreFast)=class'KFGameContent.KFPawn_ZedGorefastDualBlade'
|
|
AIClassList(AT_EDAR_EMP)=class'KFGameContent.KFPawn_ZedDAR_Emp'
|
|
AIClassList(AT_EDAR_Laser)=class'KFGameContent.KFPawn_ZedDAR_Laser'
|
|
AIClassList(AT_EDAR_Rocket)=class'KFGameContent.KFPawn_ZedDAR_Rocket'
|
|
|
|
NonSpawnAIClassList(0)=class'KFGameContent.KFPawn_ZedBloatKingSubspawn'
|
|
|
|
AIBossClassList(BAT_Hans)=class'KFGameContent.KFPawn_ZedHans'
|
|
AIBossClassList(BAT_Patriarch)=class'KFGameContent.KFPawn_ZedPatriarch'
|
|
AIBossClassList(BAT_KingFleshpound)=class'KFGameContent.KFPawn_ZedFleshpoundKing'
|
|
AIBossClassList(BAT_KingBloat)=class'KFGameContent.KFPawn_ZedBloatKing'
|
|
AIBossClassList(BAT_Matriarch)=class'KFGameContent.KFPawn_ZedMatriarch'
|
|
|
|
// Short Wave
|
|
LateArrivalStarts(0)={(
|
|
StartingDosh[0]=700, //550
|
|
StartingDosh[1]=850, //650
|
|
StartingDosh[2]=1650, //1200
|
|
StartingDosh[3]=2200 //1500
|
|
)}
|
|
|
|
// Normal Wave
|
|
LateArrivalStarts(1)={(
|
|
StartingDosh[0]=600, //450
|
|
StartingDosh[1]=800, //600
|
|
StartingDosh[2]=1000, //750
|
|
StartingDosh[3]=1100, //800
|
|
StartingDosh[4]=1500, //1100
|
|
StartingDosh[5]=2000, //1400
|
|
StartingDosh[6]=2200, //1500
|
|
StartingDosh[7]=2400 //1600
|
|
)}
|
|
|
|
// Long Wave
|
|
LateArrivalStarts(2)={(
|
|
StartingDosh[0]=600, //450
|
|
StartingDosh[1]=700, //550
|
|
StartingDosh[2]=1000, //750
|
|
StartingDosh[3]=1300, //1000
|
|
StartingDosh[4]=1650, //1200
|
|
StartingDosh[5]=1800, //1300
|
|
StartingDosh[6]=2000, //1400
|
|
StartingDosh[7]=2200, //1500
|
|
StartingDosh[8]=2400, //1600
|
|
StartingDosh[9]=2400 //1600
|
|
)}
|
|
}
|