2020-12-13 15:01:13 +00:00
//=============================================================================
// 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 \K FGameAnalytics.uci);
` include(KFGame \K FMatchStats.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.
2022-05-11 15:13:25 +00:00
2021-03-02 11:56:51 +00:00
// The boss waves spams the WaveEnd functions, adding this to prevent it (was affecting seasonal events).
var protected transient bool bWaveStarted ;
2020-12-13 15:01:13 +00:00
2022-05-11 15:13:25 +00:00
// When this is true next wave will be last
var protected bool bGunGamePlayerOnLastGun ;
2022-09-01 15:58:51 +00:00
var transient array < KFBarmwichBonfireVolume > BonfireVolumes ;
2020-12-13 15:01:13 +00:00
/** 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 ( ) ;
2022-05-11 15:13:25 +00:00
bGunGamePlayerOnLastGun = false ;
2022-09-01 15:58:51 +00:00
UpdateBonfires ( ) ;
2020-12-13 15:01:13 +00:00
}
/** Set up the spawning */
function InitSpawnManager ( )
{
SpawnManager = new ( self ) SpawnManagerClasses [ GameLength ] ;
SpawnManager . Initialize ( ) ;
WaveMax = SpawnManager . WaveSettings . Waves . Length ;
MyKFGRI . WaveMax = WaveMax ;
}
/ * S t a r t M a t c h ( )
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 ( )
{
2021-11-16 17:03:42 +00:00
if ( OutbreakEvent != none && OutbreakEvent . ActiveEvent . bBossRushMode )
return ;
2020-12-13 15:01:13 +00:00
` 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 ) ) ;
}
}
}
2022-05-11 15:13:25 +00:00
function ResetGunGame ( KFPlayerController _WeeklySurvival KFPC _WS )
{
super . ResetGunGame ( KFPC _WS ) ;
}
function RestartGunGamePlayerWeapon ( KFPlayerController _WeeklySurvival KFPC _WS , byte WaveToUse )
{
super . RestartGunGamePlayerWeapon ( KFPC _WS , WaveToUse ) ;
}
2020-12-13 15:01:13 +00:00
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 ++ )
{
2021-11-16 17:03:42 +00:00
if ( ! GameReplicationInfo . PRIArray [ i ] . bBot && ! GameReplicationInfo . PRIArray [ i ] . bOnlySpectator && PlayerController ( GameReplicationInfo . PRIArray [ i ] . Owner ) != none )
2020-12-13 15:01:13 +00:00
{
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 )
{
2021-06-02 20:06:18 +00:00
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. Also, we need to ensure if OverrideItemPickupModifier is set to 0 we really
// don't want any item pickups.
if ( NumPickups == 0 && PickupList . Length > 0
&& ( WaveNum > 1
|| KFPickupFactory _Ammo ( PickupList [ 0 ] ) != none
|| ( KFPickupFactory _Item ( PickupList [ 0 ] ) != none && ( OutbreakEvent == none || OutbreakEvent . ActiveEvent . OverrideItemPickupModifier != 0 ) )
)
)
2020-12-13 15:01:13 +00:00
{
2021-06-02 20:06:18 +00:00
NumPickups = 1 ;
2020-12-13 15:01:13 +00:00
}
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 ( ) ;
2021-03-02 11:56:51 +00:00
bWaveStarted = true ;
2020-12-13 15:01:13 +00:00
}
/** 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 ;
2021-06-02 20:06:18 +00:00
if ( ! bWaveStarted && ! MyKFGRI . bTraderIsOpen && WinCondition != WEC _TeamWipedOut )
{
2021-03-02 11:56:51 +00:00
return ;
2021-06-02 20:06:18 +00:00
}
2021-03-02 11:56:51 +00:00
2022-09-01 15:58:51 +00:00
ClearAllActorsFromBonfire ( ) ;
2020-12-13 15:01:13 +00:00
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 ) ;
}
}
2022-09-01 15:58:51 +00:00
if ( OutbreakEvent != none )
2022-05-11 15:13:25 +00:00
{
2022-09-01 15:58:51 +00:00
OnOutbreakWaveWon ( ) ;
2022-05-11 15:13:25 +00:00
}
if ( WaveNum < WaveMax )
2020-12-13 15:01:13 +00:00
{
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 ) ) ;
2021-03-02 11:56:51 +00:00
bWaveStarted = false ;
2020-12-13 15:01:13 +00:00
}
/** 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.1 f ) ;
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 ;
}
}
}
}
2022-09-01 15:58:51 +00:00
function OnOutbreakWaveWon ( ) { }
function UpdateBonfires ( )
{
local KFBarmwichBonfireVolume BonfireVolume ;
foreach AllActors ( class 'KFBarmwichBonfireVolume' , BonfireVolume )
{
BonfireVolumes . AddItem ( BonfireVolume ) ;
}
}
function ClearAllActorsFromBonfire ( )
{
local KFBarmwichBonfireVolume BonfireVolume ;
foreach BonfireVolumes ( BonfireVolume )
{
BonfireVolume . ClearAllActors ( ) ;
}
}
function ClearActorFromBonfire ( Actor Other )
{
local KFBarmwichBonfireVolume BonfireVolume ;
foreach BonfireVolumes ( BonfireVolume )
{
BonfireVolume . ClearActor ( Other ) ;
}
}
2020-12-13 15:01:13 +00:00
DefaultProperties
{
TimeBetweenWaves = 60 //This is going to be a difficulty setting later
EndCinematicDelay = 4
AARDisplayDelay = 15
bCanPerkAlwaysChange = false
MaxGameDifficulty = 3
2021-03-02 11:56:51 +00:00
bWaveStarted = false
2022-05-11 15:13:25 +00:00
bGunGamePlayerOnLastGun = false
2021-03-02 11:56:51 +00:00
2020-12-13 15:01:13 +00:00
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 ) = { (
2021-03-02 11:56:51 +00:00
StartingDosh [ 0 ] = 700 , //550
StartingDosh [ 1 ] = 850 , //650
StartingDosh [ 2 ] = 1650 , //1200
StartingDosh [ 3 ] = 2200 //1500
2020-12-13 15:01:13 +00:00
) }
// Normal Wave
LateArrivalStarts ( 1 ) = { (
2021-03-02 11:56:51 +00:00
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
2020-12-13 15:01:13 +00:00
) }
// Long Wave
LateArrivalStarts ( 2 ) = { (
2021-03-02 11:56:51 +00:00
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
2020-12-13 15:01:13 +00:00
) }
}