2302 lines
67 KiB
Ucode
2302 lines
67 KiB
Ucode
//=============================================================================
|
|
// KFGameReplicationInfo
|
|
//=============================================================================
|
|
// The KF 2 game replication class
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
// - Christian "schneidzekk" Schneider 8/27/2012
|
|
//=============================================================================
|
|
|
|
class KFGameReplicationInfo extends GameReplicationInfo
|
|
native(ReplicationInfo)
|
|
nativereplication;
|
|
|
|
/*************************************
|
|
* Pre game server welcome screen
|
|
*************************************/
|
|
struct native PreGameServerAdInfo
|
|
{
|
|
var string BannerLink; // Link to the banner image
|
|
var string ServerMOTD; // The server message of the day string
|
|
var string WebsiteLink; //url to the website of the server\
|
|
var string ClanMotto;
|
|
};
|
|
|
|
var repnotify PreGameServerAdInfo ServerAdInfo;
|
|
|
|
var int PrimaryXPAccumulator;
|
|
var int SecondaryXPAccumulator;
|
|
|
|
/************************************
|
|
* Traders
|
|
************************************/
|
|
|
|
/** Trader picked by the server and used for distance on HUD */
|
|
var KFTraderTrigger NextTrader;
|
|
/** Trader that is currently open for business */
|
|
var KFTraderTrigger OpenedTrader;
|
|
|
|
/** Settings used by Kismet for scripted trader actions */
|
|
var Volume TraderVolume;
|
|
var byte TraderVolumeCheckType;
|
|
|
|
// used for debuging purposes to walk through the traders
|
|
var int DebugingNextTraderIndex;
|
|
|
|
/** The Archtype that holds all of our trader information */
|
|
var string TraderItemsPath;
|
|
var transient KFGFxObject_TraderItems TraderItems;
|
|
|
|
/** Allow grenades */
|
|
var bool bAllowGrenadePurchase;
|
|
|
|
/** Trader dialog manager */
|
|
var KFTraderDialogManager TraderDialogManager;
|
|
var class<KFTraderDialogManager> TraderDialogManagerClass;
|
|
var class<KFTraderVoiceGroupBase> TraderVoiceGroupClass;
|
|
|
|
var repnotify bool bTraderIsOpen;
|
|
var repnotify bool bWaveIsActive;
|
|
var repnotify bool bWaveStarted;
|
|
var bool bIsEndlessPaused;
|
|
var bool bForceSkipTraderUI;
|
|
|
|
/** Replicates at beginning and end of waves to change track / track type */
|
|
var repnotify byte MusicTrackRepCount;
|
|
|
|
var repnotify byte RepKickYesVotes;
|
|
var repnotify byte RepKickNoVotes;
|
|
|
|
var repnotify byte RepSkipTraderYesVotes;
|
|
var repnotify byte RepSkipTraderNoVotes;
|
|
|
|
var repnotify byte RepPauseGameYesVotes;
|
|
var repnotify byte RepPauseGameNoVotes;
|
|
|
|
/** whether the current game can use stats */
|
|
var private const bool bIsUnrankedGame;
|
|
|
|
//Stored so that we can tell this on the AAR
|
|
var bool bMatchVictory;
|
|
|
|
//Whether or not traders are enabled
|
|
var bool bTradersEnabled;
|
|
|
|
struct native PerkAvailableData
|
|
{
|
|
var bool bPerksAvailableLimited;
|
|
|
|
var bool bBerserkerAvailable;
|
|
var bool bCommandoAvailable;
|
|
var bool bSupportAvailable;
|
|
var bool bFieldMedicAvailable;
|
|
var bool bDemolitionistAvailable;
|
|
var bool bFirebugAvailable;
|
|
var bool bGunslingerAvailable;
|
|
var bool bSharpshooterAvailable;
|
|
var bool bSwatAvailable;
|
|
var bool bSurvivalistAvailable;
|
|
};
|
|
|
|
//Wheter or not some perks are not allowed
|
|
var repnotify PerkAvailableData PerksAvailableData;
|
|
|
|
/************************************
|
|
* Spawning
|
|
************************************/
|
|
var byte WaveMax; // The "end" wave
|
|
var repnotify byte WaveNum; // The wave we are currently in
|
|
var bool bWaveIsEndless;
|
|
var repnotify byte GunGameWavesCurrent;
|
|
var repnotify bool bWaveGunGameIsFinal;
|
|
var int AIRemaining;
|
|
var int WaveTotalAICount;
|
|
var bool bEndlessMode;
|
|
var bool bObjectiveMode;
|
|
var bool bIsWeeklyMode;
|
|
//@HSL_BEGIN - JRO - 3/21/2016 - PS4 Sessions
|
|
/************************************
|
|
* Console Sessions
|
|
************************************/
|
|
//console info
|
|
var repnotify string ConsoleGameSessionGuid;
|
|
var UniqueNetId ConsoleGameSessionHost;
|
|
var array<UniqueNetId> ConsoleGameSessionPendingPlayers;
|
|
//@HSL_END
|
|
|
|
/************************************
|
|
* Settings
|
|
************************************/
|
|
var repnotify byte GameLength;
|
|
var byte GameDifficulty;
|
|
var byte GameDifficultyModifier;
|
|
var byte MaxPerkLevel;
|
|
var bool bCustom;
|
|
var float GameAmmoCostScale;
|
|
|
|
// $$dweiss
|
|
//Bandaid memory fix - Don't preload all bosses, cache in a globally recognized spot that
|
|
// the gameinfo and clients both have. For now, repnotify when the boss index changes and cache
|
|
// the appropriate monster archetype to make sure the content for it stays around for the length
|
|
// of a match.
|
|
var repnotify byte BossIndex;
|
|
var KFCharacterInfo_Monster CachedBossArch;
|
|
|
|
/** Combined from the PRI unlocks, but does not subtract logged out players */
|
|
var private const int GameSharedUnlocks;
|
|
|
|
/************************************
|
|
* Wave Debugging
|
|
************************************/
|
|
var float CurrentSineMod;
|
|
var float CurrentNextSpawnTime;
|
|
var float CurrentSineWavFreq;
|
|
var float CurrentNextSpawnTimeMod;
|
|
var int CurrentAIAliveCount;
|
|
var bool bCurrentSMFinishedSpawning;
|
|
var int CurrentMaxMonsters;
|
|
var float CurrentTimeTilNextSpawn;
|
|
var float CurrentTotalWavesActiveTime;
|
|
/** Spawn Debugging Is Active. */
|
|
var bool bDebugSpawnManager;
|
|
|
|
/************************************
|
|
* Tracking Map
|
|
************************************/
|
|
/** Tracking Map is Active. */
|
|
var bool bTrackingMapEnabled;
|
|
|
|
/************************************
|
|
* @name Map/Kick/Trader vote Collector
|
|
************************************/
|
|
|
|
var KFVoteCollector VoteCollector;
|
|
var class<KFVoteCollector> VoteCollectorClass;
|
|
|
|
/** Stores information for replicating recently used spawn volumes for the tracker map. */
|
|
struct native SpawnVolumeInfo
|
|
{
|
|
var vector VolumeLocation;
|
|
var float UsedTime;
|
|
var bool bPortalSpawn;
|
|
var byte VolumeAge;
|
|
};
|
|
|
|
/** Recently used spawn volumes for the tracker map. */
|
|
var SpawnVolumeInfo SpawnVolumeInfos[16];
|
|
/** Index of the last spawn volume added to the array. */
|
|
var int LastSpawnVolumeIndex;
|
|
|
|
/** Track recently failed spawns from spawn volumes and portal spawns. */
|
|
var SpawnVolumeInfo FailedSpawnInfos[8];
|
|
/** Index of the last spawn volume added to the array. */
|
|
var int LastFailedSpawnIndex;
|
|
|
|
/** Stores information for replicating live zeds for the tracker map. */
|
|
struct native ZedInfo
|
|
{
|
|
var vector ZedLocation;
|
|
var KFPawn_Monster Zed;
|
|
var class<KFPawn_Monster> ZedClass;
|
|
var vector LastTeleportLocation;
|
|
var bool bUsingSuperSpeed;
|
|
var vector EnemyLocation;
|
|
var KFPawn Enemy;
|
|
};
|
|
|
|
/** List of live zeds for the tracker map. */
|
|
var ZedInfo ZedInfos[32];
|
|
/** How often to update replicating zeds for the tracker map. */
|
|
var float UpdateZedInfoInterval;
|
|
|
|
/** Stores information for replicating live players for the tracker map. */
|
|
struct native HumanInfo
|
|
{
|
|
var vector HumanLocation;
|
|
var KFPawn Human;
|
|
var class<KFPawn> HumanClass;
|
|
};
|
|
|
|
/** List of live players for the tracker map. */
|
|
var HumanInfo HumanInfos[6];
|
|
/** How often to update replicating humans for the tracker map. */
|
|
var float UpdateHumanInfoInterval;
|
|
|
|
/** Max player count */
|
|
var int MaxHumanCount;
|
|
|
|
/** Stores information for replicating pickups for the tracker map. */
|
|
struct native PickupInfo
|
|
{
|
|
var vector PickupLocation;
|
|
var int PickupType; // 0 = ammo, 1 = weapon, 2 = armor
|
|
};
|
|
|
|
/** List of active pickups for the tracker map. */
|
|
var PickupInfo PickupInfos[20];
|
|
/** How often to update replicating pickups for the tracker map. */
|
|
var float UpdatePickupInfoInterval;
|
|
|
|
/** When TRUE, the icons that are drawn when a pawn is not visible will be hidden */
|
|
var bool bHidePawnIcons;
|
|
|
|
/************************************
|
|
* GameConductor
|
|
************************************/
|
|
|
|
/** Tracking Map is Active. */
|
|
var bool bGameConductorGraphingEnabled;
|
|
|
|
/** Keep track of the player accuracy over the last 10 secondse */
|
|
var float PlayerAccuracyTracker[10];
|
|
|
|
/** Keep track of the player headdshot accuracy over the last 10 secondse */
|
|
var float PlayerHeadshotAccuracyTracker[10];
|
|
|
|
/** Keep track of the aggregate player player skill over the last 10 secondse */
|
|
var float AggregatePlayerSkillTracker[10];
|
|
|
|
/** Keep track of the zed total average lifespan over time */
|
|
var float TotalZedLifeSpanAverageTracker[10];
|
|
|
|
/** Keep track of the zed current wave average lifespan over time */
|
|
var float CurrentWaveZedLifeSpanAverageTracker[10];
|
|
|
|
/** Keep track of the zed average lifespan over the last 10 seconds */
|
|
var float RecentZedLifeSpanAverageTracker[10];
|
|
|
|
/** Keep track of the players health over the last 10 seconds */
|
|
var float PlayersHealthStatusTracker[10];
|
|
|
|
/** Keep track of the players ammo over the last 10 seconds */
|
|
var float PlayersAmmoStatusTracker[10];
|
|
|
|
/** Keep track of the players combined status over the last 10 seconds */
|
|
var float AggregatePlayersStatusTracker[10];
|
|
|
|
/** The current baseline for how long zeds should live */
|
|
var float CurrentParZedLifeSpan;
|
|
|
|
/** Keep track of the overall rank and skill modifier the game conductor is using over the last 10 seconds */
|
|
var float OverallRankAndSkillModifierTracker[10];
|
|
|
|
/** Keep track of the overall rank and skill modifier the game conductor is using over the last 10 seconds */
|
|
var float ZedMovementSpeedModifierTracker[10];
|
|
|
|
/** Keep track of the overall rank and skill modifier the game conductor is using over the last 10 seconds */
|
|
var float ZedSpawnRateModifierTracker[10];
|
|
|
|
/** Keep track of the overall rank and skill modifier the game conductor is using over the last 10 seconds */
|
|
var float ZedSpawnRateTracker[10];
|
|
|
|
/** Replicate the current game conductor status */
|
|
var byte CurrentGameConductorStatus;
|
|
|
|
/** The current baseline for how long zeds should live */
|
|
var float VersusZedHealthMod;
|
|
|
|
/** The current baseline for how long zeds should live */
|
|
var float VersusZedDamageMod;
|
|
|
|
/** The current game is a versus game */
|
|
var bool bVersusGame;
|
|
|
|
/** The current game has global damage*/
|
|
var bool bGlobalDamage;
|
|
|
|
|
|
/************************************
|
|
* Team Management
|
|
************************************/
|
|
var bool bAllowSwitchTeam;
|
|
|
|
/************************************
|
|
* Actor Iterators
|
|
************************************/
|
|
|
|
var array<KFDoorActor> DoorList;
|
|
|
|
/************************************
|
|
* Objectives
|
|
************************************/
|
|
var repnotify Actor CurrentObjective;
|
|
var KFInterface_MapObjective ObjectiveInterface;
|
|
var repnotify Actor PreviousObjective;
|
|
var repnotify Actor NextObjective;
|
|
var bool NextObjectiveIsEndless;
|
|
var repnotify int PreviousObjectiveResult;
|
|
var repnotify int PreviousObjectiveXPResult;
|
|
var repnotify int PreviousObjectiveVoshResult;
|
|
|
|
/** How long after selecting the objective until we activate it. */
|
|
var int ObjectiveDelay;
|
|
|
|
/** If true, then the next objective will start regardless of random chance. */
|
|
var bool bForceNextObjective;
|
|
|
|
/************************************
|
|
* Music
|
|
************************************/
|
|
/** Audio component used for playing music tracks via SeqAct_PlayMusicTrack */
|
|
var AkComponent MusicComp;
|
|
/** Currently playing music track */
|
|
var KFMusicTrackInfo CurrentMusicTrackInfo;
|
|
/** Desired music intensity, based on gameplay and replicated by host */
|
|
var byte MusicIntensity;
|
|
/** replicated music track (allows server to force play/sync specific tracks) */
|
|
var repnotify KFMusicTrackInfo ReplicatedMusicTrackInfo;
|
|
|
|
/************************************
|
|
* debug
|
|
************************************/
|
|
|
|
/************************************
|
|
* Broken Trader Utils
|
|
************************************/
|
|
var transient bool bIsBrokenTrader;
|
|
|
|
/************************************
|
|
* Weekly Events
|
|
************************************/
|
|
var int CurrentWeeklyIndex;
|
|
|
|
/** If true, force show skip time between waves ready button */
|
|
var bool bForceShowSkipTrader;
|
|
|
|
/************************************
|
|
* Steam heartbeat
|
|
************************************/
|
|
|
|
var private float SteamHeartbeatAccumulator;
|
|
native function SendSteamHeartbeat();
|
|
native function SendSteamRequestItemDrop();
|
|
|
|
function native private EndOfWave();
|
|
|
|
|
|
cpptext
|
|
{
|
|
INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel );
|
|
|
|
virtual void TickAuthoritative( FLOAT DeltaSeconds );
|
|
virtual UBOOL IsUnrankedGame();
|
|
virtual FString GetGameBalanceCol1() { return FString::Printf(TEXT(",%i,"), WaveNum); }
|
|
virtual int GetWaveNum() { return WaveNum; }
|
|
virtual int GetWaveMax() { return WaveMax; }
|
|
virtual UBOOL GetWon() { return bMatchVictory; }
|
|
virtual void TickSpecial(FLOAT DeltaSeconds);
|
|
}
|
|
|
|
|
|
/************************************
|
|
* Replication
|
|
************************************/
|
|
replication
|
|
{
|
|
if ( bNetDirty )
|
|
TraderVolume, TraderVolumeCheckType, bTraderIsOpen, NextTrader, WaveNum, bWaveIsEndless, GunGameWavesCurrent, bWaveGunGameIsFinal, AIRemaining, WaveTotalAICount, bWaveIsActive, MaxHumanCount, bGlobalDamage,
|
|
CurrentObjective, PreviousObjective, PreviousObjectiveResult, PreviousObjectiveXPResult, PreviousObjectiveVoshResult, MusicIntensity, ReplicatedMusicTrackInfo, MusicTrackRepCount,
|
|
bIsUnrankedGame, GameSharedUnlocks, bHidePawnIcons, ConsoleGameSessionGuid, GameDifficulty, GameDifficultyModifier, BossIndex, bWaveStarted, NextObjective, bIsBrokenTrader, bIsWeeklyMode,
|
|
CurrentWeeklyIndex, bIsEndlessPaused, bForceSkipTraderUI; //@HSL - JRO - 3/21/2016 - PS4 Sessions
|
|
if ( bNetInitial )
|
|
GameLength, WaveMax, bCustom, bVersusGame, TraderItems, GameAmmoCostScale, bAllowGrenadePurchase, MaxPerkLevel, bTradersEnabled, bForceShowSkipTrader;
|
|
if ( bNetInitial || bNetDirty )
|
|
PerksAvailableData;
|
|
if ( bNetInitial && Role == ROLE_Authority )
|
|
ServerAdInfo;
|
|
|
|
if( bNetDirty && VoteCollector != none && VoteCollector.bIsKickVoteInProgress)
|
|
RepKickNoVotes, RepKickYesVotes;
|
|
if( bNetDirty && VoteCollector != none && VoteCollector.bIsSkipTraderVoteInProgress)
|
|
RepSkipTraderNoVotes, RepSkipTraderYesVotes;
|
|
if( bNetDirty && VoteCollector != none && VoteCollector.bIsPauseGameVoteInProgress)
|
|
RepPauseGameNoVotes, RepPauseGameYesVotes;
|
|
|
|
// !SHIPPING_PC_GAME && !FINAL_RELEASE in C++
|
|
if ( bDebugSpawnManager && bNetDirty )
|
|
CurrentSineMod, CurrentNextSpawnTime, CurrentSineWavFreq, CurrentNextSpawnTimeMod,
|
|
CurrentAIAliveCount, bCurrentSMFinishedSpawning, CurrentMaxMonsters, CurrentTimeTilNextSpawn,
|
|
CurrentTotalWavesActiveTime;
|
|
if( bNetDirty )
|
|
bTrackingMapEnabled;
|
|
if ( bTrackingMapEnabled && bNetDirty )
|
|
SpawnVolumeInfos, ZedInfos, HumanInfos, FailedSpawnInfos, PickupInfos;
|
|
if( bNetDirty )
|
|
bGameConductorGraphingEnabled;
|
|
if ( bGameConductorGraphingEnabled && bNetDirty )
|
|
PlayerAccuracyTracker, PlayerHeadshotAccuracyTracker, AggregatePlayerSkillTracker,
|
|
TotalZedLifeSpanAverageTracker, CurrentWaveZedLifeSpanAverageTracker, RecentZedLifeSpanAverageTracker,
|
|
PlayersHealthStatusTracker, PlayersAmmoStatusTracker, AggregatePlayersStatusTracker,
|
|
CurrentParZedLifeSpan, OverallRankAndSkillModifierTracker, ZedMovementSpeedModifierTracker,
|
|
ZedSpawnRateModifierTracker, ZedSpawnRateTracker, CurrentGameConductorStatus;
|
|
if ( bGameConductorGraphingEnabled && bNetDirty && bVersusGame )
|
|
VersusZedHealthMod, VersusZedDamageMod;
|
|
// endif
|
|
}
|
|
|
|
simulated event ReplicatedEvent(name VarName)
|
|
{
|
|
if ( VarName == nameof(bTraderIsOpen) )
|
|
{
|
|
if ( bTraderIsOpen )
|
|
{
|
|
OpenTrader();
|
|
}
|
|
else
|
|
{
|
|
CloseTrader();
|
|
}
|
|
}
|
|
else if ( VarName == nameof(bWaveIsActive))
|
|
{
|
|
if(!bWaveIsActive)
|
|
{
|
|
FadeOutLingeringExplosions();
|
|
EndOfWave();
|
|
}
|
|
}
|
|
else if (VarName == nameof(bWaveStarted))
|
|
{
|
|
if (bWaveStarted)
|
|
{
|
|
NotifyWaveStart();
|
|
}
|
|
else
|
|
{
|
|
NotifyWaveEnded();
|
|
}
|
|
}
|
|
else if( VarName == nameof(ReplicatedMusicTrackInfo) )
|
|
{
|
|
ForceNewMusicTrack( ReplicatedMusicTrackInfo );
|
|
}
|
|
else if( VarName == nameof(MusicTrackRepCount) )
|
|
{
|
|
// don't start music for boss wave, boss will start it at end of monologue
|
|
if( !bWaveIsActive || !IsBossWave() )
|
|
{
|
|
PlayNewMusicTrack(true);
|
|
}
|
|
}
|
|
else if( VarName == nameOf(bIsUnrankedGame) )
|
|
{
|
|
if( bIsUnrankedGame )
|
|
{
|
|
`warn(GetFuncName() @ "Game is UNRANKED!");
|
|
}
|
|
}
|
|
else if( VarName == nameOf(RepKickYesVotes) || VarName == nameOf(RepKickNoVotes) )
|
|
{
|
|
VoteCollector.UnPackKickVotes();
|
|
}
|
|
else if( VarName == nameOf(RepSkipTraderYesVotes) || VarName == nameOf(RepSkipTraderNoVotes) )
|
|
{
|
|
VoteCollector.UnPackSkipTraderVotes();
|
|
}
|
|
else if ( VarName == nameof(RepPauseGameYesVotes) || VarName == nameof(RepPauseGameNoVotes) )
|
|
{
|
|
VoteCollector.UnPackPauseGameVotes();
|
|
}
|
|
else if( VarName == 'ServerAdInfo')
|
|
{
|
|
ShowPreGameServerWelcomeScreen();
|
|
}
|
|
else if( VarName == 'WaveNum')
|
|
{
|
|
UpdateHUDWaveCount();
|
|
TriggerClientWaveStartEvents();
|
|
}
|
|
//@HSL_BEGIN - JRO - 3/21/2016 - PS4 Sessions
|
|
else if( VarName == 'ConsoleGameSessionGuid' )
|
|
{
|
|
KFPlayerController(GetALocalPlayerController()).TryJoinGameSession();
|
|
}
|
|
//@HSL_END
|
|
else if (VarName == 'CurrentObjective')
|
|
{
|
|
if (CurrentObjective != none)
|
|
{
|
|
ObjectiveInterface = KFInterface_MapObjective(CurrentObjective);
|
|
ObjectiveInterface.ActivateObjective();
|
|
}
|
|
else
|
|
{
|
|
if (GetALocalPlayerController() != none)
|
|
{
|
|
KFPlayerController(GetALocalPlayerController()).SeasonalEventStats_OnMapObjectiveDeactivated(Actor(ObjectiveInterface));
|
|
}
|
|
|
|
ObjectiveInterface.DeactivateObjective();
|
|
ObjectiveInterface = none;
|
|
}
|
|
}
|
|
else if (VarName == 'BossIndex')
|
|
{
|
|
CacheSelectedBoss(BossIndex);
|
|
}
|
|
else if (VarName == nameof(NextObjective))
|
|
{
|
|
if (NextObjective != none)
|
|
{
|
|
KFInterface_MapObjective(NextObjective).NotifyObjectiveSelected();
|
|
}
|
|
}
|
|
else if (VarName == nameof(GameLength))
|
|
{
|
|
ReceivedGameLength();
|
|
}
|
|
else if(VarName == nameof(PerksAvailableData))
|
|
{
|
|
UpdatePerksAvailable();
|
|
}
|
|
else if (VarName == 'GunGameWavesCurrent')
|
|
{
|
|
UpdateHUDWaveCount();
|
|
}
|
|
else if (VarName == 'bWaveGunGameIsFinal')
|
|
{
|
|
UpdateHUDWaveCount();
|
|
}
|
|
else
|
|
{
|
|
super.ReplicatedEvent(VarName);
|
|
}
|
|
}
|
|
|
|
simulated event PostBeginPlay()
|
|
{
|
|
local KFDoorActor Door;
|
|
|
|
VoteCollector = new(Self) VoteCollectorClass;
|
|
|
|
Super.PostBeginPlay();
|
|
|
|
//@HSL_BEGIN - JRO - 3/21/2016 - PS4 Sessions
|
|
ConsoleGameSessionGuid = KFGameEngine(Class'Engine'.static.GetEngine()).ConsoleGameSessionGuid;
|
|
//@HSL_END
|
|
|
|
// cache list of all doors actors (useful for HUD)
|
|
ForEach DynamicActors(class'KFDoorActor', Door)
|
|
{
|
|
DoorList.AddItem(Door);
|
|
}
|
|
|
|
if( WorldInfo.NetMode != NM_DedicatedServer && TraderDialogManagerClass != none )
|
|
{
|
|
TraderDialogManager = Spawn(TraderDialogManagerClass);
|
|
}
|
|
|
|
// Override timer at a constant 1s instead of TimeDilation, so that it slows
|
|
// down during zedtime. Also, removed the SetTimer() call from Timer()
|
|
SetTimer(1.f, true);
|
|
|
|
TraderItems = KFGFxObject_TraderItems(DynamicLoadObject(TraderItemsPath, class'KFGFxObject_TraderItems'));
|
|
}
|
|
|
|
simulated function ReceivedGameLength()
|
|
{
|
|
ActivateLevelLoadedEvents();
|
|
}
|
|
|
|
simulated function ActivateLevelLoadedEvents()
|
|
{
|
|
local Sequence GameSeq;
|
|
local array<SequenceObject> AllSeqEvents;
|
|
local array<int> ActivateIndices;
|
|
local int i;
|
|
|
|
GameSeq = WorldInfo.GetGameSequence();
|
|
if (GameSeq != None)
|
|
{
|
|
// find any Level Loaded events that exist
|
|
GameSeq.FindSeqObjectsByClass(class'KFSeqEvent_LevelLoaded', true, AllSeqEvents);
|
|
ActivateIndices = GetKFSeqEventLevelLoadedIndices();
|
|
|
|
if (ActivateIndices.Length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// activate them
|
|
for (i = 0; i < AllSeqEvents.Length; i++)
|
|
{
|
|
// We need the GRI to be able to activate the correct index (based on game length / type).
|
|
// If we've been waiting for the GRI, activate.
|
|
if (KFSeqEvent_LevelLoaded(AllSeqEvents[i]).bWaitingForGRI)
|
|
{
|
|
SeqEvent_LevelLoaded(AllSeqEvents[i]).CheckActivate(WorldInfo, None, false, ActivateIndices);
|
|
KFSeqEvent_LevelLoaded(AllSeqEvents[i]).bWaitingForGRI = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function array<int> GetKFSeqEventLevelLoadedIndices()
|
|
{
|
|
local array<int> ActivateIndices;
|
|
|
|
switch (GameLength)
|
|
{
|
|
case 0: // short
|
|
ActivateIndices[0] = 3;
|
|
break;
|
|
|
|
case 1: // medium
|
|
ActivateIndices[0] = 4;
|
|
break;
|
|
|
|
case 2: // long
|
|
ActivateIndices[0] = 5;
|
|
break;
|
|
};
|
|
|
|
return ActivateIndices;
|
|
}
|
|
|
|
/** Called when the GameClass property is set (at startup for the server, after the variable has been replicated on clients) */
|
|
simulated function ReceivedGameClass()
|
|
{
|
|
local class<KFGameInfo> KFGameClass;
|
|
local KFMapInfo KFMI;
|
|
local class<KFTraderVoiceGroupBase> MapVoiceGroupClass;
|
|
|
|
KFGameClass = class<KFGameInfo>(GameClass);
|
|
if ( KFGameClass != None )
|
|
{
|
|
// Load/Cache game type specific classes (Network: All)
|
|
KFGameClass.static.PreloadGlobalContentClasses();
|
|
|
|
if( TraderDialogManager != none )
|
|
{
|
|
// Priority for trader voice groups:
|
|
// 1. special game mode (e.g. endless)
|
|
// 2. map specific (e.g. Summer Sideshow)
|
|
// 3. purchaseable (for survival game mode)
|
|
// 4. default (for survival game mode)
|
|
|
|
TraderDialogManager.TraderVoiceGroupClass = KFGameClass.default.TraderVoiceGroupClass;
|
|
|
|
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
|
|
if (KFMI != none)
|
|
{
|
|
if (bEndlessMode)
|
|
{
|
|
if (KFMI.TraderVoiceGroupClassPath_Endless != "")
|
|
{
|
|
MapVoiceGroupClass = class<KFTraderVoiceGroupBase>(DynamicLoadObject(KFMI.TraderVoiceGroupClassPath_Endless, class'Class'));
|
|
}
|
|
}
|
|
else if (bObjectiveMode)
|
|
{
|
|
if (KFMI.TraderVoiceGroupClassPath_Objective != "")
|
|
{
|
|
MapVoiceGroupClass = class<KFTraderVoiceGroupBase>(DynamicLoadObject(KFMI.TraderVoiceGroupClassPath_Objective, class'Class'));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (KFMI.TraderVoiceGroupClassPath != "")
|
|
{
|
|
MapVoiceGroupClass = class<KFTraderVoiceGroupBase>(DynamicLoadObject(KFMI.TraderVoiceGroupClassPath, class'Class'));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MapVoiceGroupClass != None)
|
|
{
|
|
TraderDialogManager.TraderVoiceGroupClass = MapVoiceGroupClass;
|
|
}
|
|
}
|
|
|
|
if( KFGameClass.static.ShouldPlayMusicAtStart() && MusicComp == None )
|
|
{
|
|
PlayNewMusicTrack(false, true);
|
|
}
|
|
}
|
|
|
|
DebugingNextTraderIndex = -1;
|
|
|
|
Super.ReceivedGameClass();
|
|
}
|
|
|
|
simulated function CacheSelectedBoss(int NewBossIndex)
|
|
{
|
|
local class<KFGameInfo> KFGameClass;
|
|
local class<KFPawn_Monster> KFMonsterClass;
|
|
|
|
BossIndex = NewBossIndex;
|
|
|
|
KFGameClass = class<KFGameInfo>(GameClass);
|
|
if (KFGameClass != None)
|
|
{
|
|
KFMonsterClass = KFGameClass.static.GetSpecificBossClass(BossIndex, KFMapInfo(WorldInfo.GetMapInfo()));
|
|
if (KFMonsterClass != none)
|
|
{
|
|
SetCachedBossArchetype(KFMonsterClass.default.MonsterArchPath);
|
|
}
|
|
}
|
|
}
|
|
native function SetCachedBossArchetype(string MonsterArchPath);
|
|
|
|
simulated function UpdateHUDWaveCount()
|
|
{
|
|
local KFPlayerController KFPC;
|
|
|
|
if( WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
KFPC = KFPlayerController(GetALocalPlayerController());
|
|
if( KFPC != none && KFPC.MyGFxHUD != none )
|
|
{
|
|
KFPC.MyGFxHUD.UpdateWaveCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Process wave end event on client */
|
|
simulated function NotifyWaveEnded()
|
|
{
|
|
local PlayerReplicationInfo PRI;
|
|
local KFPlayerReplicationInfo KFPRI;
|
|
|
|
if ( WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
if( WorldInfo.MyGoreEffectManager != none )
|
|
{
|
|
KFGoreManager(WorldInfo.MyGoreEffectManager).ResetPersistantGore(false);
|
|
}
|
|
}
|
|
|
|
bWaveStarted = false;
|
|
bForceNetUpdate = true;
|
|
|
|
// Reset all supplier perks
|
|
foreach PRIArray( PRI )
|
|
{
|
|
KFPRI = KFPlayerReplicationInfo( PRI );
|
|
if( KFPRI != none )
|
|
{
|
|
KFPRI.NotifyWaveEnded();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Process wave end event on client */
|
|
simulated function NotifyWaveStart()
|
|
{
|
|
local PlayerReplicationInfo PRI;
|
|
local KFPlayerReplicationInfo KFPRI;
|
|
|
|
bWaveStarted = true;
|
|
bForceNetUpdate = true;
|
|
|
|
// Reset all supplier perks
|
|
foreach PRIArray(PRI)
|
|
{
|
|
KFPRI = KFPlayerReplicationInfo(PRI);
|
|
if (KFPRI != none)
|
|
{
|
|
KFPRI.NotifyWaveStart();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called on the server when the match is over
|
|
*
|
|
* Network - Server and Client (Via ReplicatedEvent)
|
|
*/
|
|
|
|
simulated function EndGame()
|
|
{
|
|
bMatchHasBegun = false;
|
|
bMatchIsOver = true;
|
|
|
|
class'KFGameEngine'.static.RefreshOnlineGameData(true);
|
|
}
|
|
|
|
/* Welcome screen shenanigans */
|
|
exec reliable client function ShowPreGameServerWelcomeScreen()
|
|
{
|
|
local KFPlayerController KFPC;
|
|
|
|
if( WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFPC = KFPlayerController(GetALocalPlayerController());
|
|
if(KFPC != none && KFPC.MyGFxManager != none)
|
|
{
|
|
KFPC.MyGFxManager.ShowWelcomeScreen();
|
|
}
|
|
|
|
}
|
|
|
|
simulated function GetKFPRIArray(out array<KFPlayerReplicationInfo> KFPRIArray, optional bool bGetSpectators, optional bool bGetZedPlayers = true)
|
|
{
|
|
local int i;
|
|
local int Num;
|
|
|
|
KFPRIArray.Remove(0, KFPRIArray.Length);
|
|
for ( i = 0; i < PRIArray.Length; i++)
|
|
{
|
|
if ( PRIArray[i] != None && KFPlayerReplicationInfo(PRIArray[i]) != none &&
|
|
(bGetSpectators || !PRIArray[i].bOnlySpectator) &&
|
|
(bGetZedPlayers || PRIArray[i].GetTeamNum() != 255))
|
|
{
|
|
KFPRIArray[num++] = KFPlayerReplicationInfo(PRIArray[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Fades out any lingering explosions in the world, called from ::ReplicatedEvent() */
|
|
simulated function FadeOutLingeringExplosions()
|
|
{
|
|
local KFExplosionActorLingering LingeringExplosion;
|
|
|
|
foreach DynamicActors( class'KFExplosionActorLingering', LingeringExplosion )
|
|
{
|
|
LingeringExplosion.FadeOut();
|
|
}
|
|
}
|
|
|
|
function StartScavengeTime(int time)
|
|
{
|
|
RemainingTime = time;
|
|
RemainingMinute = time;
|
|
bStopCountDown = false;
|
|
}
|
|
|
|
simulated function OpenTrader(optional int time)
|
|
{
|
|
local KFPlayerController KFPC;
|
|
local array<int> OutputLinksToActivate;
|
|
local array<SequenceObject> AllTraderOpenedEvents;
|
|
local KFSeqEvent_TraderOpened TraderOpenedEvt;
|
|
local Sequence GameSeq;
|
|
local int i;
|
|
|
|
if( OpenedTrader != none )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( time > 0 && Role == ROLE_Authority )
|
|
{
|
|
bStopCountDown = false;
|
|
RemainingTime = time;
|
|
RemainingMinute = time;
|
|
}
|
|
|
|
OpenedTrader = NextTrader;
|
|
|
|
if( OpenedTrader != none )
|
|
{
|
|
OpenedTrader.OpenTrader();
|
|
|
|
`TraderDialogManager.PlayOpenTraderDialog( WaveNum, WaveMax, GetALocalPlayerController() );
|
|
|
|
KFPC = KFPlayerController(GetALocalPlayerController());
|
|
if( KFPC != none )
|
|
{
|
|
if( KFPC.MyGFxManager != none )
|
|
{
|
|
KFPC.MyGFxManager.OnTraderTimeStart();
|
|
}
|
|
if( KFPC.MyGFxHUD != none )
|
|
{
|
|
KFPC.MyGFxHUD.UpdateWaveCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
if( WorldInfo.NetMode == NM_Client )
|
|
{
|
|
// 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.bClientSideOnly )
|
|
{
|
|
TraderOpenedEvt.Reset();
|
|
TraderOpenedEvt.SetWaveNum( WaveNum, WaveMax );
|
|
if( IsFinalWave() && TraderOpenedEvt.OutputLinks.Length > 1 )
|
|
{
|
|
OutputLinksToActivate.AddItem( 1 );
|
|
}
|
|
else
|
|
{
|
|
OutputLinksToActivate.AddItem( 0 );
|
|
}
|
|
TraderOpenedEvt.CheckActivate( self, self,, OutputLinksToActivate );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Cheat/Debugging */
|
|
simulated function OpenTraderNext(optional int time)
|
|
{
|
|
local KFGameInfo kfGameInfo;
|
|
|
|
kfGameInfo = KFGameInfo(WorldInfo.Game);
|
|
|
|
if( kfGameInfo == none )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( time > 0 && Role == ROLE_Authority )
|
|
{
|
|
bStopCountDown = false;
|
|
RemainingTime = time;
|
|
RemainingMinute = time;
|
|
}
|
|
|
|
// See if we have a scripted trader to assign first
|
|
if( kfGameInfo.ScriptedTrader != none )
|
|
{
|
|
NextTrader = kfGameInfo.ScriptedTrader;
|
|
kfGameInfo.ScriptedTrader = none;
|
|
}
|
|
else if( kfGameInfo.TraderList.Length > 0 )
|
|
{
|
|
if( DebugingNextTraderIndex == -1 && OpenedTrader != none )
|
|
{
|
|
// lets add the current trader to the end of list so it can be used again
|
|
kfGameInfo.TraderList.AddItem(OpenedTrader);
|
|
}
|
|
|
|
if( DebugingNextTraderIndex + 1 >= kfGameInfo.TraderList.Length )
|
|
{
|
|
DebugingNextTraderIndex= -1;
|
|
}
|
|
|
|
DebugingNextTraderIndex = DebugingNextTraderIndex + 1;
|
|
NextTrader = kfGameInfo.TraderList[ DebugingNextTraderIndex ];
|
|
//kfGameInfo.TraderList.Remove( kfGameInfo.NextTraderIndex, 1 );
|
|
}
|
|
|
|
OpenedTrader = NextTrader;
|
|
OpenedTrader.OpenTrader();
|
|
|
|
`TraderDialogManager.PlayOpenTraderDialog( WaveNum, WaveMax, GetALocalPlayerController() );
|
|
}
|
|
|
|
simulated function CloseTrader()
|
|
{
|
|
local KFPlayerController KFPC;
|
|
local PlayerController LocalPC;
|
|
local KFSeqEvent_TraderClosed TraderClosedEvt;
|
|
local array<SequenceObject> AllTraderClosedEvents;
|
|
local Sequence GameSeq;
|
|
local int i;
|
|
|
|
LocalPC = GetALocalPlayerController();
|
|
|
|
if (OpenedTrader != none)
|
|
{
|
|
bStopCountDown = true;
|
|
OpenedTrader.CloseTrader();
|
|
OpenedTrader = none;
|
|
|
|
`TraderDialogManager.PlayCloseTraderDialog( LocalPC );
|
|
|
|
if (WorldInfo.NetMode == NM_Client)
|
|
{
|
|
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.bClientSideOnly)
|
|
{
|
|
TraderClosedEvt.Reset();
|
|
TraderClosedEvt.SetWaveNum(WaveNum, WaveMax);
|
|
TraderClosedEvt.CheckActivate(self, self);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//KFPC.bPlayerUsedUpdatePerk should always be set to false here
|
|
KFPC = KFPlayerController(LocalPC);
|
|
if(KFPC != none)
|
|
{
|
|
KFPC.SetHaveUpdatePerk(false);
|
|
}
|
|
}
|
|
|
|
simulated function int GetTraderTimeRemaining()
|
|
{
|
|
return max(0, RemainingTime);
|
|
}
|
|
|
|
/** Triggers all client-side wave start events */
|
|
simulated function TriggerClientWaveStartEvents()
|
|
{
|
|
local array<SequenceObject> AllWaveStartEvents;
|
|
local array<int> OutputLinksToActivate;
|
|
local KFSeqEvent_WaveStart WaveStartEvt;
|
|
local Sequence GameSeq;
|
|
local int i;
|
|
|
|
if( WorldInfo.NetMode == NM_Client )
|
|
{
|
|
// 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.bClientSideOnly )
|
|
{
|
|
WaveStartEvt.Reset();
|
|
WaveStartEvt.SetWaveNum( WaveNum, WaveMax );
|
|
if( IsBossWave() && WaveStartEvt.OutputLinks.Length > 1 )
|
|
{
|
|
OutputLinksToActivate.AddItem( 1 );
|
|
}
|
|
else
|
|
{
|
|
OutputLinksToActivate.AddItem( 0 );
|
|
}
|
|
WaveStartEvt.CheckActivate( self, self,, OutputLinksToActivate );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function float GetHeartbeatAccumulatorAmount()
|
|
{
|
|
return SteamHeartbeatAccumulator;
|
|
}
|
|
|
|
|
|
//After action report
|
|
simulated function OnOpenAfterActionReport(optional float time)
|
|
{
|
|
if( time > 0 && Role == ROLE_Authority )
|
|
{
|
|
bStopCountDown = false;
|
|
RemainingTime = time;
|
|
RemainingMinute = time;
|
|
}
|
|
}
|
|
|
|
simulated function ProcessChanceDrop()
|
|
{
|
|
`if (`__TW_NETWORKING_)
|
|
SendSteamHeartbeat(); // be sure no time is lost at the end of match
|
|
SendSteamRequestItemDrop(); // see if we've accumulated enough time
|
|
`endif
|
|
}
|
|
|
|
simulated event SendPlayfabGameTimeUpdate( optional bool bGameEnd )
|
|
{
|
|
local JsonObject Parms;
|
|
|
|
Parms = new class'JsonObject';
|
|
Parms.SetIntValue("UpdateTime", SteamHeartbeatAccumulator);
|
|
Parms.SetBoolValue("bGameEnd", bGameEnd);
|
|
class'GameEngine'.static.GetPlayfabInterface().ExecuteCloudScript("UpdatePlayRewards", Parms);
|
|
}
|
|
|
|
simulated function int GetNextMapTimeRemaining()
|
|
{
|
|
return max(0, RemainingTime);
|
|
}
|
|
|
|
/** Called from the GameInfo when the trader pod should be activated */
|
|
function SetWaveActive(bool bWaveActive, optional byte NewMusicIntensity)
|
|
{
|
|
|
|
// set up music intensity for this wave
|
|
MusicIntensity = NewMusicIntensity;
|
|
bTraderIsOpen = !bWaveActive && bMatchHasBegun && bTradersEnabled;
|
|
bForceSkipTraderUI = !bWaveActive && bMatchHasBegun && bForceShowSkipTrader;
|
|
bWaveIsActive = bWaveActive;
|
|
bForceNetUpdate = true;
|
|
|
|
// replicate track change
|
|
MusicTrackRepCount++;
|
|
|
|
if( !(IsBossWaveNext() && bWaveActive) && WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
PlayNewMusicTrack( true );
|
|
}
|
|
}
|
|
|
|
simulated function bool CanOverrideWeeklyMusic()
|
|
{
|
|
local KFGameInfo KFGI;
|
|
|
|
if (WorldInfo.NetMode == NM_Client)
|
|
{
|
|
return (!bIsWeeklyMode || class'KFGameEngine'.static.GetWeeklyEventIndexMod() != 12 || GetNumPlayersAlive() == 0);
|
|
}
|
|
else
|
|
{
|
|
KFGI = KFGameInfo(WorldInfo.Game);
|
|
return (KFGI == none || KFGI.OutbreakEvent == none || !KFGI.OutbreakEvent.ActiveEvent.bForceWWLMusic || GetNumPlayersAlive() == 0);
|
|
}
|
|
}
|
|
|
|
simulated function bool IsFinalWave()
|
|
{
|
|
return (WaveNum == WaveMax - 1);
|
|
}
|
|
|
|
simulated function bool IsBossWave()
|
|
{
|
|
if (bIsWeeklyMode && CurrentWeeklyIndex == 14)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return WaveNum == WaveMax;
|
|
}
|
|
|
|
simulated function bool IsInfiniteWave()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
simulated function bool IsBossWaveNext()
|
|
{
|
|
return WaveNum == WaveMax - 1;
|
|
}
|
|
|
|
simulated function bool IsSpecialWave(out int ModIndex)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
simulated function bool IsWeeklyWave(out int ModIndex)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
simulated function bool IsEndlessWave()
|
|
{
|
|
return bWaveIsEndless;
|
|
}
|
|
|
|
// Called once a second
|
|
simulated event Timer()
|
|
{
|
|
local KFGameInfo MyKFGameInfo;
|
|
|
|
// Override super RemainingTime handling
|
|
if( WorldInfo.Game == None || WorldInfo.Game.MatchIsInProgress() )
|
|
{
|
|
ElapsedTime++;
|
|
}
|
|
|
|
if( Role == ROLE_Authority && bDebugSpawnManager )
|
|
{
|
|
MyKFGameInfo = KFGameInfo(WorldInfo.Game);
|
|
if( MyKFGameInfo != none )
|
|
{
|
|
CurrentAIAliveCount = KFGameInfo(WorldInfo.Game).AIAliveCount;
|
|
if( MyKFGameInfo.SpawnManager != none )
|
|
{
|
|
bCurrentSMFinishedSpawning = MyKFGameInfo.SpawnManager.IsFinishedSpawning();
|
|
CurrentMaxMonsters = MyKFGameInfo.SpawnManager.GetMaxMonsters();
|
|
CurrentTimeTilNextSpawn = MyKFGameInfo.SpawnManager.TimeUntilNextSpawn;
|
|
CurrentTotalWavesActiveTime = MyKFGameInfo.SpawnManager.TotalWavesActiveTime;
|
|
CurrentSineMod = MyKFGameInfo.SpawnManager.GetSineMod();
|
|
}
|
|
else
|
|
{
|
|
bCurrentSMFinishedSpawning = true;
|
|
CurrentMaxMonsters = 0;
|
|
CurrentTimeTilNextSpawn = 0;
|
|
CurrentTotalWavesActiveTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( WorldInfo.NetMode == NM_Client )
|
|
{
|
|
if( RemainingMinute != 0 )
|
|
{
|
|
RemainingTime = RemainingMinute;
|
|
RemainingMinute = 0;
|
|
}
|
|
}
|
|
|
|
if( RemainingTime > 0 && !bStopCountDown )
|
|
{
|
|
RemainingTime--;
|
|
if( WorldInfo.NetMode != NM_Client )
|
|
{
|
|
// Sync the time every 5 seconds, like we did in RO2
|
|
// Unreal's time get's out of sync rather fast
|
|
if( RemainingTime % 5 == 0 )
|
|
{
|
|
RemainingMinute = RemainingTime;
|
|
}
|
|
}
|
|
}
|
|
// End super
|
|
|
|
if( WorldInfo.NetMode != NM_DedicatedServer && OpenedTrader != none && RemainingTime > 0 )
|
|
{
|
|
`TraderDialogManager.PlayTraderTickDialog( RemainingTime, GetALocalPlayerController(), WorldInfo );
|
|
}
|
|
|
|
// set in native AK callback (ExitCueCallback)
|
|
if( bPendingMusicTrackChange )
|
|
{
|
|
bPendingMusicTrackChange = false;
|
|
PlayNewMusicTrack();
|
|
}
|
|
// fallback in case something goes wrong and we have no music playing
|
|
else if( MusicComp != none && !MusicComp.IsPlaying() )
|
|
{
|
|
PlayNewMusictrack();
|
|
}
|
|
}
|
|
|
|
//@HSL_BEGIN - JRO - 8/24/2016 - Make sure the session guid gets reset when everyone has left.
|
|
simulated function RemovePRI(PlayerReplicationInfo PRI)
|
|
{
|
|
local UniqueNetId NullId;
|
|
|
|
Super.RemovePRI(PRI);
|
|
|
|
if(WorldInfo.IsConsoleDedicatedServer())
|
|
{
|
|
if(PRIArray.Length == 0)
|
|
{
|
|
ConsoleGameSessionGuid = "";
|
|
ConsoleGameSessionHost = NullId;
|
|
}
|
|
}
|
|
|
|
if (!bMatchHasBegun)
|
|
{
|
|
UpdateSharedUnlocks();
|
|
}
|
|
}
|
|
//@HSL_END
|
|
|
|
native private simulated function UpdateSharedUnlocks();
|
|
|
|
/** Called by the menu system to determine if perk changes are allowed */
|
|
simulated event bool CanChangePerks()
|
|
{
|
|
return bTraderIsOpen;
|
|
}
|
|
|
|
/* DisplayDebug()
|
|
list important controller attributes on canvas
|
|
*/
|
|
simulated function DisplayDebug(HUD HUD, out float YL, out float YPos)
|
|
{
|
|
local int TotalClots, NumSlashers, NumUnders, NumAlphas;
|
|
//local KFPawn_ZedClot_Slasher KFPS;
|
|
//local KFPawn_ZedClot_Alpha KFPA;
|
|
//local KFPawn_ZedClot_Cyst KFPC;
|
|
local KFPawn_Monster KFPM;
|
|
local Canvas Canvas;
|
|
|
|
Canvas = HUD.Canvas;
|
|
|
|
Super.DisplayDebug(HUD, YL, YPos);
|
|
|
|
//Canvas.SetDrawColor(0,255,255);
|
|
|
|
if (HUD.ShouldDisplayDebug('gamestate'))
|
|
{
|
|
Canvas.SetPos(4,YPos);
|
|
Canvas.DrawText("---------- KFGameInfo GameState Info ----------");
|
|
YPos += YL;
|
|
|
|
|
|
foreach DynamicActors( class'KFPawn_Monster', KFPM )
|
|
{
|
|
if( KFPM.IsAliveAndWell() )
|
|
{
|
|
if( KFPM.IsA('KFPawn_ZedClot_Slasher') )
|
|
{
|
|
TotalClots++;
|
|
NumSlashers++;
|
|
}
|
|
else if( KFPM.IsA('KFPawn_ZedClot_Alpha') )
|
|
{
|
|
TotalClots++;
|
|
NumAlphas++;
|
|
}
|
|
else if( KFPM.IsA('KFPawn_ZedClot_Cyst') )
|
|
{
|
|
TotalClots++;
|
|
NumUnders++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( TotalClots > 0 )
|
|
{
|
|
Canvas.DrawText("TotalClots:" @ TotalClots @ ", Alpha%:" @ (Float(NumAlphas)/Float(TotalClots))*100 @ ", Slasher%:" @ (Float(NumSlashers)/Float(TotalClots))*100 @ ", UnderDev%:" @ (Float(NumUnders)/Float(TotalClots))*100, FALSE);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
}
|
|
else
|
|
{
|
|
Canvas.DrawText("TotalClots: 0", FALSE);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
}
|
|
|
|
Canvas.DrawText("CurrentSineMod:" @ CurrentSineMod @ ", CurrentNextSpawnTime:" @ CurrentNextSpawnTime, FALSE);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
}
|
|
|
|
if (HUD.ShouldDisplayDebug('gamespeed'))
|
|
{
|
|
Canvas.SetPos(4,YPos);
|
|
Canvas.DrawText("---------- GameSpeed Info ----------");
|
|
YPos += YL;
|
|
|
|
Canvas.DrawText("GameSpeed:" @ WorldInfo.TimeDilation);
|
|
YPos += YL;
|
|
Canvas.SetPos(4,YPos);
|
|
}
|
|
}
|
|
|
|
simulated function int GetNumPlayers() //dead or alive
|
|
{
|
|
local array< KFPlayerReplicationInfo > PRIs;
|
|
|
|
GetKFPRIArray(PRIs);
|
|
|
|
return PRIs.length;
|
|
}
|
|
|
|
simulated function int GetNumPlayersAlive()
|
|
{
|
|
local int i, NumPlayersAlive;
|
|
local array< KFPlayerReplicationInfo > PRIs;
|
|
|
|
GetKFPRIArray(PRIs);
|
|
|
|
for ( i = 0; i < PRIs.Length; i++ )
|
|
{
|
|
if( PRIs[i].PlayerHealth > 0 )
|
|
{
|
|
NumPlayersAlive++;
|
|
}
|
|
}
|
|
|
|
return NumPlayersAlive;
|
|
}
|
|
|
|
simulated function bool AnyPlayersAlive()
|
|
{
|
|
return GetNumPlayersAlive() > 0;
|
|
}
|
|
|
|
/** Add recently used spawn volumes to the array for the tracker map */
|
|
function AddRecentSpawnVolume( vector VolumeLocation, optional bool bPortalSpawn )
|
|
{
|
|
LastSpawnVolumeIndex++;
|
|
|
|
if( LastSpawnVolumeIndex > (ArrayCount(SpawnVolumeInfos) - 1) )
|
|
{
|
|
LastSpawnVolumeIndex = 0;
|
|
}
|
|
|
|
SpawnVolumeInfos[LastSpawnVolumeIndex].VolumeLocation = VolumeLocation;
|
|
SpawnVolumeInfos[LastSpawnVolumeIndex].UsedTime = WorldInfo.TimeSeconds;
|
|
SpawnVolumeInfos[LastSpawnVolumeIndex].bPortalSpawn = bPortalSpawn;
|
|
SpawnVolumeInfos[LastSpawnVolumeIndex].VolumeAge = 255;
|
|
bNetDirty = true;
|
|
}
|
|
|
|
/** Add recently used spawn volumes to the array for the tracker map */
|
|
function AddFailedSpawn( vector SpawnLocation, optional bool bPortalSpawn )
|
|
{
|
|
LastFailedSpawnIndex++;
|
|
|
|
if( LastFailedSpawnIndex > (ArrayCount(FailedSpawnInfos) - 1) )
|
|
{
|
|
LastFailedSpawnIndex = 0;
|
|
}
|
|
|
|
FailedSpawnInfos[LastFailedSpawnIndex].VolumeLocation = SpawnLocation;
|
|
FailedSpawnInfos[LastFailedSpawnIndex].UsedTime = WorldInfo.TimeSeconds;
|
|
FailedSpawnInfos[LastFailedSpawnIndex].bPortalSpawn = bPortalSpawn;
|
|
bNetDirty = true;
|
|
}
|
|
|
|
/** Network: Server */
|
|
event Tick(float DeltaTime)
|
|
{
|
|
super.Tick( DeltaTime );
|
|
|
|
`if(`notdefined(ShippingPC))
|
|
// If the tracker map is enabled update the various elements for replication
|
|
if( bTrackingMapEnabled )
|
|
{
|
|
UpdateSpawnVolumes();
|
|
|
|
if( UpdateZedInfoInterval <= 0 )
|
|
{
|
|
UpdateZedList();
|
|
UpdateZedInfoInterval = default.UpdateZedInfoInterval;
|
|
}
|
|
else
|
|
{
|
|
UpdateZedInfoInterval -= DeltaTime;
|
|
}
|
|
|
|
if( UpdateHumanInfoInterval <= 0 )
|
|
{
|
|
UpdateHumanList();
|
|
UpdateHumanInfoInterval = default.UpdateHumanInfoInterval;
|
|
}
|
|
else
|
|
{
|
|
UpdateHumanInfoInterval -= DeltaTime;
|
|
}
|
|
|
|
if( UpdatePickupInfoInterval <= 0 )
|
|
{
|
|
UpdatePickupList();
|
|
UpdatePickupInfoInterval = default.UpdatePickupInfoInterval;
|
|
}
|
|
else
|
|
{
|
|
UpdatePickupInfoInterval -= DeltaTime;
|
|
}
|
|
}
|
|
`endif
|
|
}
|
|
|
|
/** Update the pickup array for the tracker map */
|
|
function UpdatePickupList()
|
|
{
|
|
local int i, j;
|
|
local KFGameInfo KFGameInfo;
|
|
|
|
KFGameInfo = KFGameInfo(WorldInfo.Game);
|
|
|
|
if( KFGameInfo == none )
|
|
{
|
|
return;
|
|
}
|
|
|
|
i=0;
|
|
|
|
for (j = 0; j < KFGameInfo.AllPickupFactories.Length; j++)
|
|
{
|
|
if( i < ArrayCount(PickupInfos) )
|
|
{
|
|
if( KFGameInfo.AllPickupFactories[j] != none && !KFGameInfo.AllPickupFactories[j].bPickupHidden )
|
|
{
|
|
PickupInfos[i].PickupLocation = KFGameInfo.AllPickupFactories[j].Location;
|
|
if( KFGameInfo.AllPickupFactories[j].CurrentPickupIsAmmo() )
|
|
{
|
|
PickupInfos[i].PickupType = 0;
|
|
}
|
|
else if( KFGameInfo.AllPickupFactories[j].CurrentPickupIsWeapon() )
|
|
{
|
|
PickupInfos[i].PickupType = 1;
|
|
}
|
|
else if( KFGameInfo.AllPickupFactories[j].CurrentPickupIsArmor() )
|
|
{
|
|
PickupInfos[i].PickupType = 2;
|
|
}
|
|
else
|
|
{
|
|
PickupInfos[i].PickupType = -1;
|
|
}
|
|
bNetDirty = true;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Zero out the rest
|
|
for (i=i; i < ArrayCount(PickupInfos); i++)
|
|
{
|
|
PickupInfos[i].PickupLocation = vect(0,0,0);
|
|
PickupInfos[i].PickupType = -1;
|
|
bNetDirty = true;
|
|
}
|
|
}
|
|
|
|
/** Update the spawn volumes array for the tracker map */
|
|
function UpdateSpawnVolumes()
|
|
{
|
|
local int i;
|
|
|
|
for (i = 0; i < ArrayCount(SpawnVolumeInfos); i++)
|
|
{
|
|
if( !IsZero(SpawnVolumeInfos[i].VolumeLocation) )
|
|
{
|
|
// Clear out older spawn volumes
|
|
if( SpawnVolumeInfos[i].bPortalSpawn && `TimeSince(SpawnVolumeInfos[i].UsedTime) > 5.0 )
|
|
{
|
|
SpawnVolumeInfos[i].VolumeLocation = vect(0,0,0);
|
|
bNetDirty = true;
|
|
}
|
|
else if( `TimeSince(SpawnVolumeInfos[i].UsedTime) > 30.0 )
|
|
{
|
|
SpawnVolumeInfos[i].VolumeLocation = vect(0,0,0);
|
|
bNetDirty = true;
|
|
}
|
|
else
|
|
{
|
|
if( SpawnVolumeInfos[i].bPortalSpawn )
|
|
{
|
|
SpawnVolumeInfos[i].VolumeAge = ((5 - `TimeSince(SpawnVolumeInfos[i].UsedTime))/5) * 255;
|
|
}
|
|
else
|
|
{
|
|
SpawnVolumeInfos[i].VolumeAge = ((30 - `TimeSince(SpawnVolumeInfos[i].UsedTime))/30) * 255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ArrayCount(FailedSpawnInfos); i++)
|
|
{
|
|
if( !IsZero(FailedSpawnInfos[i].VolumeLocation) )
|
|
{
|
|
// Clear out older failed spawns
|
|
if( `TimeSince(FailedSpawnInfos[i].UsedTime) > 10.0 )
|
|
{
|
|
FailedSpawnInfos[i].VolumeLocation = vect(0,0,0);
|
|
bNetDirty = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Update the zed array for the tracker map */
|
|
function UpdateZedList()
|
|
{
|
|
local KFPawn_Monster KFPM;
|
|
local int i;
|
|
local bool bFoundZed;
|
|
|
|
// Clear out dead or destroyed zeds
|
|
for (i = 0; i < ArrayCount(ZedInfos); i++)
|
|
{
|
|
if( (ZedInfos[i].Zed == none && !IsZero(ZedInfos[i].ZedLocation))
|
|
|| (ZedInfos[i].Zed != none && !ZedInfos[i].Zed.IsAliveAndWell()) )
|
|
{
|
|
ZedInfos[i].ZedLocation = vect(0,0,0);
|
|
ZedInfos[i].Zed = none;
|
|
ZedInfos[i].ZedClass = none;
|
|
ZedInfos[i].bUsingSuperSpeed = false;
|
|
ZedInfos[i].Enemy = None;
|
|
ZedInfos[i].EnemyLocation = vect(0,0,0);
|
|
ZedInfos[i].LastTeleportLocation = vect(0,0,0);
|
|
bNetDirty = true;
|
|
}
|
|
}
|
|
|
|
foreach WorldInfo.Allpawns(class'KFPawn_Monster', KFPM)
|
|
{
|
|
if ( KFPM.IsAliveAndWell() )
|
|
{
|
|
bFoundZed = false;
|
|
|
|
// See if this zed is already in the array, and update it
|
|
for (i = 0; i < ArrayCount(ZedInfos); i++)
|
|
{
|
|
if( ZedInfos[i].Zed == KFPM )
|
|
{
|
|
ZedInfos[i].ZedLocation = KFPM.Location;
|
|
ZedInfos[i].bUsingSuperSpeed = KFPM.bUseHiddenSpeed;
|
|
|
|
if( KFPM.Controller != none && KFAIController(KFPM.Controller) != none
|
|
&& `TimeSince(KFAIController(KFPM.Controller).LastTeleportTime) < 5.0 )
|
|
{
|
|
ZedInfos[i].LastTeleportLocation = KFAIController(KFPM.Controller).LastTeleportLocation;
|
|
}
|
|
else
|
|
{
|
|
ZedInfos[i].LastTeleportLocation = vect(0,0,0);
|
|
}
|
|
|
|
if( KFPM.Controller != none && KFPM.Controller.Enemy != none )
|
|
{
|
|
ZedInfos[i].Enemy = KFPawn(KFPM.Controller.Enemy);
|
|
ZedInfos[i].EnemyLocation = KFPM.Controller.Enemy.Location;
|
|
}
|
|
else
|
|
{
|
|
ZedInfos[i].Enemy = None;
|
|
ZedInfos[i].EnemyLocation = vect(0,0,0);
|
|
}
|
|
bFoundZed = true;
|
|
bNetDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bFoundZed )
|
|
{
|
|
// Add this zed to an empty slot
|
|
for (i = 0; i < ArrayCount(ZedInfos); i++)
|
|
{
|
|
if( ZedInfos[i].Zed == none )
|
|
{
|
|
ZedInfos[i].ZedLocation = KFPM.Location;
|
|
ZedInfos[i].Zed = KFPM;
|
|
ZedInfos[i].ZedClass = KFPM.class;
|
|
ZedInfos[i].bUsingSuperSpeed = KFPM.bUseHiddenSpeed;
|
|
if( KFPM.Controller != none && KFPM.Controller.Enemy != none )
|
|
{
|
|
ZedInfos[i].Enemy = KFPawn(KFPM.Controller.Enemy);
|
|
ZedInfos[i].EnemyLocation = KFPM.Controller.Enemy.Location;
|
|
}
|
|
else
|
|
{
|
|
ZedInfos[i].Enemy = None;
|
|
ZedInfos[i].EnemyLocation = vect(0,0,0);
|
|
}
|
|
if( KFPM.Controller != none && KFAIController(KFPM.Controller) != none
|
|
&& `TimeSince(KFAIController(KFPM.Controller).LastTeleportTime) < 5.0 )
|
|
{
|
|
ZedInfos[i].LastTeleportLocation = KFAIController(KFPM.Controller).LastTeleportLocation;
|
|
}
|
|
else
|
|
{
|
|
ZedInfos[i].LastTeleportLocation = vect(0,0,0);
|
|
}
|
|
bNetDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Update the human array for the tracker map */
|
|
function UpdateHumanList()
|
|
{
|
|
local KFPawn_Human KFPH;
|
|
local int i;
|
|
local bool bFoundHuman;
|
|
|
|
// Clear out dead or destroyed humans
|
|
for (i = 0; i < ArrayCount(HumanInfos); i++)
|
|
{
|
|
if( (HumanInfos[i].Human == none && !IsZero(HumanInfos[i].HumanLocation))
|
|
|| (HumanInfos[i].Human != none && !HumanInfos[i].Human.IsAliveAndWell()) )
|
|
{
|
|
HumanInfos[i].HumanLocation = vect(0,0,0);
|
|
HumanInfos[i].Human = none;
|
|
HumanInfos[i].HumanClass = none;
|
|
bNetDirty = true;
|
|
}
|
|
}
|
|
|
|
foreach WorldInfo.Allpawns(class'KFPawn_Human', KFPH)
|
|
{
|
|
if ( KFPH.IsAliveAndWell() )
|
|
{
|
|
bFoundHuman = false;
|
|
|
|
// See if this human is already in the array, and update it
|
|
for (i = 0; i < ArrayCount(HumanInfos); i++)
|
|
{
|
|
if( HumanInfos[i].Human == KFPH )
|
|
{
|
|
HumanInfos[i].HumanLocation = KFPH.Location;
|
|
bFoundHuman = true;
|
|
bNetDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bFoundHuman )
|
|
{
|
|
// Add this zed to an empty slot
|
|
for (i = 0; i < ArrayCount(HumanInfos); i++)
|
|
{
|
|
if( HumanInfos[i].Human == none )
|
|
{
|
|
HumanInfos[i].HumanLocation = KFPH.Location;
|
|
HumanInfos[i].Human = KFPH;
|
|
HumanInfos[i].HumanClass = KFPH.class;
|
|
bNetDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
native function UpdateMusicTrack( KFMusicTrackInfo NextMusicTrackInfo, bool bPlayStandardTrack );
|
|
|
|
simulated function PlayNewMusicTrack( optional bool bGameStateChanged, optional bool bForceAmbient )
|
|
{
|
|
local KFMapInfo KFMI;
|
|
local class<KFGameInfo> KFGameClass;
|
|
local KFMusicTrackInfo NextMusicTrackInfo;
|
|
local bool bLoop;
|
|
local bool bPlayActionTrack;
|
|
|
|
if ( class'KFGameEngine'.static.CheckNoMusic() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
KFGameClass = class<KFGameInfo>(GameClass);
|
|
if ( KFGameClass == None )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !CanOverrideWeeklyMusic() )
|
|
return;
|
|
|
|
// @todo: consider using music intensity (255?) for ambient music to simplify this logic
|
|
bPlayActionTrack = (!bForceAmbient && KFGameClass.static.ShouldPlayActionMusicTrack(self));
|
|
|
|
// if we've just transitioned into the "action" phase of the game,
|
|
// check if we need to delay the action music
|
|
if( bGameStateChanged )
|
|
{
|
|
if( bPlayActionTrack )
|
|
{
|
|
if( KFGameClass.default.ActionMusicDelay > 0 )
|
|
{
|
|
SetTimer( KFGameClass.default.ActionMusicDelay, false, nameof(PlayNewMusicTrack) );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if( CurrentMusicTrackInfo != none )
|
|
{
|
|
bLoop = CurrentMusicTrackInfo.bLoop;
|
|
}
|
|
|
|
// loop if we're designated to loop or this is the boss wave
|
|
if( bLoop || (!bEndlessMode && IsBossWave()))
|
|
{
|
|
NextMusicTrackInfo = CurrentMusicTrackInfo;
|
|
}
|
|
else
|
|
{
|
|
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
|
|
if ( KFMI != none )
|
|
{
|
|
NextMusicTrackInfo = KFMI.GetNextMusicTrackByGameIntensity(bPlayActionTrack, MusicIntensity);
|
|
}
|
|
else
|
|
{
|
|
// Some maps might not be setup correctly, let's just grab a random default song
|
|
NextMusicTrackInfo = class'KFMapInfo'.static.StaticGetRandomTrack(bPlayActionTrack);
|
|
}
|
|
}
|
|
|
|
UpdateMusicTrack( NextMusicTrackInfo, KFGameEngine(Class'Engine'.static.GetEngine()).bMusicVocalsEnabled );
|
|
}
|
|
|
|
simulated function ForceNewMusicTrack( KFMusicTrackInfo ForcedTrackInfo )
|
|
{
|
|
if( Role == ROLE_Authority )
|
|
{
|
|
ReplicatedMusicTrackInfo = ForcedTrackInfo;
|
|
}
|
|
|
|
UpdateMusicTrack( ForcedTrackInfo, KFGameEngine(Class'Engine'.static.GetEngine()).bMusicVocalsEnabled );
|
|
}
|
|
|
|
/***********************************************************
|
|
@name Voting
|
|
************************************************************/
|
|
|
|
function ServerStartVoteKick(PlayerReplicationInfo PRI_Kickee, PlayerReplicationInfo PRI_Kicker)
|
|
{
|
|
if(VoteCollector != none)
|
|
{
|
|
VoteCollector.ServerStartVoteKick(PRI_Kickee, PRI_Kicker);
|
|
}
|
|
}
|
|
|
|
reliable server function RecieveVoteKick(PlayerReplicationInfo PRI, bool bKick)
|
|
{
|
|
if(VoteCollector != none)
|
|
{
|
|
VoteCollector.RecieveVoteKick(PRI, bKick);
|
|
}
|
|
}
|
|
|
|
function ServerStartVoteSkipTrader(PlayerReplicationInfo PRI)
|
|
{
|
|
if(VoteCollector != none)
|
|
{
|
|
VoteCollector.ServerStartVoteSkipTrader(PRI);
|
|
}
|
|
}
|
|
|
|
reliable server function RecieveVoteSkipTrader(PlayerReplicationInfo PRI, bool bSkipTrader)
|
|
{
|
|
if(VoteCollector != none)
|
|
{
|
|
VoteCollector.RecieveVoteSkipTrader(PRI, bSkipTrader);
|
|
}
|
|
}
|
|
|
|
function ServerStartVotePauseGame(PlayerReplicationInfo PRI)
|
|
{
|
|
if(VoteCollector != none)
|
|
{
|
|
VoteCollector.ServerStartVotePauseGame(PRI);
|
|
}
|
|
}
|
|
|
|
reliable server function ReceiveVotePauseGame(PlayerReplicationInfo PRI, bool bPauseGame)
|
|
{
|
|
if(VoteCollector != none)
|
|
{
|
|
VoteCollector.ReceiveVotePauseGame(PRI, bPauseGame);
|
|
}
|
|
}
|
|
|
|
reliable server function ReceiveVoteMap(PlayerReplicationInfo PRI, int MapIndex)
|
|
{
|
|
if(VoteCollector != none)
|
|
{
|
|
VoteCollector.ReceiveVoteMap( PRI, MapIndex);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
@name Ranking
|
|
************************************************************/
|
|
|
|
final private event NotifyGameUnranked()
|
|
{
|
|
if( WorldInfo.Game != none )
|
|
{
|
|
WorldInfo.Game.UpdateGameSettings();
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
@name Post Round Info
|
|
************************************************************/
|
|
|
|
function int GetCurrentRoundNumber();
|
|
|
|
/***********************************************************
|
|
@name Team management
|
|
************************************************************/
|
|
simulated function bool AreTeamsOutOfBalanced();
|
|
|
|
|
|
//@HSL_BEGIN - AGM - 7-16-15 - Support for checking for valid stats session
|
|
/***********************************************************
|
|
@name Stats management
|
|
************************************************************/
|
|
simulated event bool IsStatsSessionValid()
|
|
{
|
|
return true;
|
|
}
|
|
//@HSL_END
|
|
|
|
//*****************************************************************************
|
|
// Objectives
|
|
//*****************************************************************************
|
|
function ChooseNextObjective(int NextWaveNum)
|
|
{
|
|
local KFMapInfo KFMI;
|
|
|
|
// reset/default to no objective chosen
|
|
NextObjective = none;
|
|
NextObjectiveIsEndless = false;
|
|
|
|
if (bIsWeeklyMode && KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bBossRushMode)
|
|
return;
|
|
|
|
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
|
|
if (KFMI != none && NextWaveNum != WaveMax)
|
|
{
|
|
if (KFMI.bUsePresetObjectives && NextWaveNum <= GetPresetObjectiveLength(KFMI))
|
|
{
|
|
ChooseNextPresetObjective(KFMI, NextWaveNum);
|
|
return;
|
|
}
|
|
|
|
if (KFMI.bUseRandomObjectives)
|
|
{
|
|
ChooseNextRandomObjective(KFMI, NextWaveNum);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function bool ChooseNextPresetObjective(KFMapInfo KFMI, int NextWaveNum)
|
|
{
|
|
local array<KFInterface_MapObjective> PossibleObjectives;
|
|
local bool bUseEndlessSpawning;
|
|
|
|
if (KFMI == none)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Grab appropriate list of possible objectives based on wave and game length
|
|
switch(GameLength)
|
|
{
|
|
case GL_Short:
|
|
if (KFMI.PresetWaveObjectives.ShortObjectives[NextWaveNum - 1].PossibleObjectives.Length > 0)
|
|
{
|
|
PossibleObjectives = KFMI.PresetWaveObjectives.ShortObjectives[NextWaveNum - 1].PossibleObjectives;
|
|
bUseEndlessSpawning = KFMI.PresetWaveObjectives.ShortObjectives[NextWaveNum - 1].bUseEndlessSpawning;
|
|
}
|
|
break;
|
|
case GL_Normal:
|
|
if (KFMI.PresetWaveObjectives.MediumObjectives[NextWaveNum - 1].PossibleObjectives.Length > 0)
|
|
{
|
|
PossibleObjectives = KFMI.PresetWaveObjectives.MediumObjectives[NextWaveNum - 1].PossibleObjectives;
|
|
bUseEndlessSpawning = KFMI.PresetWaveObjectives.MediumObjectives[NextWaveNum - 1].bUseEndlessSpawning;
|
|
}
|
|
break;
|
|
case GL_Long:
|
|
if (KFMI.PresetWaveObjectives.LongObjectives[NextWaveNum - 1].PossibleObjectives.Length > 0)
|
|
{
|
|
PossibleObjectives = KFMI.PresetWaveObjectives.LongObjectives[NextWaveNum - 1].PossibleObjectives;
|
|
bUseEndlessSpawning = KFMI.PresetWaveObjectives.LongObjectives[NextWaveNum - 1].bUseEndlessSpawning;
|
|
}
|
|
break;
|
|
default: //Disable for mods with weird counts
|
|
break;
|
|
}
|
|
|
|
return SetNextObjective(PossibleObjectives, bUseEndlessSpawning) != INDEX_NONE;
|
|
}
|
|
|
|
function int GetPresetObjectiveLength(KFMapInfo KFMI)
|
|
{
|
|
if (KFMI == none)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
switch (GameLength)
|
|
{
|
|
case GL_Short:
|
|
return ArrayCount(KFMI.PresetWaveObjectives.ShortObjectives);
|
|
case GL_Normal:
|
|
return ArrayCount(KFMI.PresetWaveObjectives.MediumObjectives);
|
|
case GL_Long:
|
|
return ArrayCount(KFMI.PresetWaveObjectives.LongObjectives);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function bool ChooseNextRandomObjective(KFMapInfo KFMI, int NextWaveNum)
|
|
{
|
|
local int Idx;
|
|
Idx = INDEX_NONE;
|
|
//Start a random objective if we have any set
|
|
if (KFMI.RandomWaveObjectives.Length > 0 && KFMI.RandomObjectiveWavesToDisable.Find(NextWaveNum) == INDEX_NONE)
|
|
{
|
|
//Attempt to reset if we've run out
|
|
if (KFMI.CurrentAvailableRandomWaveObjectives.Length == 0)
|
|
{
|
|
KFMI.CurrentAvailableRandomWaveObjectives = KFMI.RandomWaveObjectives;
|
|
}
|
|
|
|
Idx = SetNextObjective(KFMI.CurrentAvailableRandomWaveObjectives);
|
|
if (Idx >= 0)
|
|
{
|
|
KFMI.CurrentAvailableRandomWaveObjectives.Remove(Idx, 1);
|
|
}
|
|
}
|
|
|
|
return Idx != INDEX_NONE;
|
|
}
|
|
|
|
function int SetNextObjective(array<KFInterface_MapObjective> PossibleObjectives, bool bUseEndlessSpawning = false, bool bActivateImmediately = false)
|
|
{
|
|
local int RandID;
|
|
local float DieRoll, PctChanceToActivate;
|
|
|
|
DieRoll = FRand();
|
|
//Loop through list of possible ones to find a random valid one. If we never call activate, nothing was valid
|
|
while (PossibleObjectives.Length > 0)
|
|
{
|
|
RandID = Rand(PossibleObjectives.Length);
|
|
|
|
if (PossibleObjectives[RandID].CanActivateObjectiveByWeekly())
|
|
{
|
|
PctChanceToActivate = PossibleObjectives[RandID].GetActivationPctChance();
|
|
if (bForceNextObjective || (PossibleObjectives[RandID].CanActivateObjective() && PreviousObjective != PossibleObjectives[RandID] && (PctChanceToActivate >= 1.f || DieRoll <= PctChanceToActivate)))
|
|
{
|
|
if (bActivateImmediately)
|
|
{
|
|
ActivateObjective(PossibleObjectives[RandID], bUseEndlessSpawning);
|
|
}
|
|
else
|
|
{
|
|
NextObjective = Actor(PossibleObjectives[RandID]);
|
|
NextObjectiveIsEndless = bUseEndlessSpawning;
|
|
KFInterface_MapObjective(NextObjective).NotifyObjectiveSelected();
|
|
}
|
|
return RandID;
|
|
}
|
|
}
|
|
|
|
PossibleObjectives.Remove(RandID, 1);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
function bool StartNextObjective()
|
|
{
|
|
if (NextObjective != none)
|
|
{
|
|
ActivateObjective(NextObjective, NextObjectiveIsEndless);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function ActivateObjective(KFInterface_MapObjective NewObjective, bool bUseEndlessSpawning = false)
|
|
{
|
|
local KFGameInfo KFGI;
|
|
|
|
if (NewObjective != none)
|
|
{
|
|
CurrentObjective = Actor(NewObjective);
|
|
ClearPreviousObjective();
|
|
ObjectiveInterface = NewObjective;
|
|
|
|
if(ObjectiveDelay > 0)
|
|
{
|
|
SetTimer(ObjectiveDelay,, 'Timer_ActivateObjective');
|
|
}
|
|
else
|
|
{
|
|
Timer_ActivateObjective();
|
|
}
|
|
|
|
if (Role == ROLE_Authority && bUseEndlessSpawning)
|
|
{
|
|
KFGI = KFGameInfo(WorldInfo.Game);
|
|
if (KFGI != none && KFGI.SpawnManager != none)
|
|
{
|
|
KFGI.SpawnManager.bTemporarilyEndless = true;
|
|
bWaveIsEndless = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function DeactivateObjective()
|
|
{
|
|
local KFGameInfo KFGI;
|
|
local KFPawn_Monster KFPM;
|
|
|
|
if (CurrentObjective != None)
|
|
{
|
|
PreviousObjective = CurrentObjective;
|
|
PreviousObjectiveResult = ObjectiveInterface.GetDoshReward();
|
|
PreviousObjectiveVoshResult = ObjectiveInterface.GetVoshReward();
|
|
PreviousObjectiveXPResult = ObjectiveInterface.GetXPReward();
|
|
|
|
if (GetALocalPlayerController() != none)
|
|
{
|
|
KFPlayerController(GetALocalPlayerController()).SeasonalEventStats_OnMapObjectiveDeactivated(Actor(ObjectiveInterface));
|
|
}
|
|
|
|
ObjectiveInterface.DeactivateObjective();
|
|
CurrentObjective = none;
|
|
ObjectiveInterface = none;
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
KFGI = KFGameInfo(WorldInfo.Game);
|
|
if (KFGI != none && KFGI.SpawnManager != none && KFGI.SpawnManager.bTemporarilyEndless)
|
|
{
|
|
KFGI.SpawnManager.bTemporarilyEndless = false;
|
|
bWaveIsEndless = false;
|
|
|
|
// when the wave switches off endless, remove all pending spawns
|
|
// so that the user doesn't see an increasing zed count as these zeds trickle in
|
|
KFGI.SpawnManager.ActiveSpawner.PendingSpawns.Length = 0;
|
|
AIRemaining = KFGI.SpawnManager.GetAIAliveCount() + Max(0, KFGI.SpawnManager.WaveTotalAI - KFGI.NumAISpawnsQueued);
|
|
|
|
if( AIRemaining <= class'KFGameInfo'.static.GetNumAlwaysRelevantZeds() )
|
|
{
|
|
//Tell the remaining pawns to set themselves relevant.
|
|
foreach WorldInfo.AllPawns( class'KFPawn_Monster', KFPM )
|
|
{
|
|
KFPM.CheckShouldAlwaysBeRelevant();
|
|
}
|
|
}
|
|
|
|
KFGI.OnEndlessSpawningObjectiveDeactivated();
|
|
}
|
|
}
|
|
}
|
|
|
|
// find the next objective (if it exists)
|
|
// so that the area can be showed early, during trader time (if needed)
|
|
ChooseNextObjective(WaveNum + 1);
|
|
}
|
|
|
|
function ClearPreviousObjective()
|
|
{
|
|
PreviousObjective = none;
|
|
PreviousObjectiveResult = INDEX_NONE;
|
|
PreviousObjectiveVoshResult = INDEX_NONE;
|
|
PreviousObjectiveXPResult = INDEX_NONE;
|
|
}
|
|
|
|
function Timer_ActivateObjective()
|
|
{
|
|
if (ObjectiveInterface != none)
|
|
{
|
|
ObjectiveInterface.ActivateObjective();
|
|
}
|
|
}
|
|
|
|
function float GetMapObjectiveSpawnRateMod()
|
|
{
|
|
if (ObjectiveInterface != none)
|
|
{
|
|
return ObjectiveInterface.GetSpawnRateMod();
|
|
}
|
|
|
|
return 1.f;
|
|
}
|
|
|
|
simulated event byte GetModifiedGameDifficulty()
|
|
{
|
|
return GameDifficulty + GameDifficultyModifier;
|
|
}
|
|
|
|
simulated event SetModifiedGameDifficulty(byte NewDifficultyMod)
|
|
{
|
|
GameDifficultyModifier = NewDifficultyMod;
|
|
bNetDirty = true;
|
|
}
|
|
|
|
simulated function bool ShouldSetBossCamOnBossSpawn()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
simulated function bool ShouldSetBossCamOnBossDeath()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
simulated function int GetFinalWaveNum()
|
|
{
|
|
return WaveMax - 1;
|
|
}
|
|
|
|
simulated function bool IsObjectiveMode()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function SetGlobalDamage(bool bEnable)
|
|
{
|
|
bGlobalDamage = bEnable;
|
|
}
|
|
|
|
simulated function bool IsGlobalDamage()
|
|
{
|
|
return bGlobalDamage;
|
|
}
|
|
|
|
simulated function bool IsPerkAllowed(class<KFPerk> PerkClass)
|
|
{
|
|
if(PerksAvailableData.bPerksAvailableLimited)
|
|
{
|
|
if(PerkClass == class'KFPerk_Berserker') return PerksAvailableData.bBerserkerAvailable;
|
|
else if(PerkClass == class'KFPerk_Commando') return PerksAvailableData.bCommandoAvailable;
|
|
else if(PerkClass == class'KFPerk_Support') return PerksAvailableData.bSupportAvailable;
|
|
else if(PerkClass == class'KFPerk_FieldMedic') return PerksAvailableData.bFieldMedicAvailable;
|
|
else if(PerkClass == class'KFPerk_Demolitionist') return PerksAvailableData.bDemolitionistAvailable;
|
|
else if(PerkClass == class'KFPerk_Firebug') return PerksAvailableData.bFirebugAvailable;
|
|
else if(PerkClass == class'KFPerk_Gunslinger') return PerksAvailableData.bGunslingerAvailable;
|
|
else if(PerkClass == class'KFPerk_Sharpshooter') return PerksAvailableData.bSharpshooterAvailable;
|
|
else if(PerkClass == class'KFPerk_Swat') return PerksAvailableData.bSwatAvailable;
|
|
else if(PerkClass == class'KFPerk_Survivalist') return PerksAvailableData.bSurvivalistAvailable;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
simulated function UpdatePerksAvailable()
|
|
{
|
|
KFPlayerController(GetALocalPlayerController()).UpdatePerkOnInit();
|
|
}
|
|
|
|
simulated function NotifyBrokenTrader()
|
|
{
|
|
bIsBrokenTrader = true;
|
|
bNetDirty = true;
|
|
}
|
|
|
|
simulated function NotifyWeeklyEventIndex(int EventIndex)
|
|
{
|
|
CurrentWeeklyIndex = EventIndex;
|
|
bNetDirty = true;
|
|
}
|
|
|
|
|
|
simulated function bool IsGunGameMode()
|
|
{
|
|
return bIsWeeklyMode && CurrentWeeklyIndex == 16;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
TraderItemsPath="GP_Trader_ARCH.DefaultTraderItems"
|
|
TraderDialogManagerClass=class'KFGame.KFTraderDialogManager'
|
|
bTradersEnabled=true
|
|
VoteCollectorClass=class'KFGame.KFVoteCollector'
|
|
UpdateZedInfoInterval=0.5
|
|
UpdateHumanInfoInterval=0.5
|
|
UpdatePickupInfoInterval=1.0
|
|
WaveMax=255
|
|
bAllowSwitchTeam=false
|
|
GameAmmoCostScale=1.0
|
|
bAllowGrenadePurchase = true
|
|
MaxPerkLevel=4
|
|
BossIndex=255
|
|
PreviousObjectiveResult=-1
|
|
PreviousObjectiveVoshResult=-1
|
|
PreviousObjectiveXPResult=-1
|
|
bIsBrokenTrader=false
|
|
bIsWeeklyMode=false
|
|
bForceShowSkipTrader=false
|
|
bForceSkipTraderUI=false
|
|
GunGameWavesCurrent=1
|
|
bWaveGunGameIsFinal=false
|
|
}
|