1
0
KF2-Dev-Scripts/KFGame/Classes/KFPlayerController.uc
2021-12-09 19:33:06 +03:00

11936 lines
360 KiB
Ucode

//=============================================================================
// KFPlayerController
//=============================================================================
// Player Controller for KFGame
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
class KFPlayerController extends GamePlayerController
native(Controller)
nativereplication
dependson(EphemeralMatchStats, KFWeapon);
`include(KFOnlineStats.uci)
`include(KFProfileSettings.uci)
`include(KFGameAnalytics.uci)
`include(KFGame\KFMatchStats.uci);
struct native MenuInfo
{
var byte CurrentMenuIndex;
var bool bMenusOpen; // true if we're using menus, otherwise we're using the HUD
var bool bIsStartMenu; // true if UI_Start menu open
};
/*********************************************************************************************
* @name Perks
********************************************************************************************* */
enum ETextChatChannel
{
ETCC_ALL,
ETCC_TEAM,
};
enum EVoiceChannel
{
EVC_ALL,
EVC_TEAM,
};
struct native PlayerStats
{
var int PrimaryXP;
var int SecondaryXP;
};
/** The Perk */
struct native PerkInfo
{
var class<KFPerk> PerkClass;
var byte PerkLevel; // This perk level is specifically used for clientside UI
var byte PrestigeLevel; // This perk level is specifically used for clientside UI
var KFPerk PerkArchetype;
};
struct native PlayerSteamAvatar
{
var Texture2D Avatar;
var UniqueNetId NetID;
};
struct native PlayerAvatarPS4
{
var string AvatarURL;
var string PlayerName;
var KFHTTPImageDownloader ImageDownloader;
};
/** Spawn information for player zeds (Versus) */
struct native sPlayerZedSpawnInfo
{
var class<KFPawn_Monster> PendingZedPawnClass;
var vector PendingZedSpawnLocation;
var float LastSpawnedTime;
};
struct native sSavedViewTargetInfo
{
var Actor SavedViewTarget;
var Name SavedCameraMode;
var rotator SavedRotation;
};
var ETextChatChannel CurrentTextChatChannel;
var EVoiceChannel CurrentVoiceChannel;
var array<PlayerSteamAvatar>AvatarList;
var array<PlayerAvatarPS4> AvatarListPS4;
var array<PerkInfo> PerkList;
/* Currently selected (active) perk */
var repnotify KFPerk CurrentPerk;
/** These are updated by the client and exist only on the server. They are used for updating our perk between waves */
var class<KFPerk> ServPendingPerkClass;
var int ServPendingPerkBuild;
var int ServPendingPerkLevel;
var int ServPendingPerkPrestigeLevel;
/** When true, the server has asked the client for perk data but the client has not yet responded */
var bool bWaitingForClientPerkData;
/** Used for player controlled zeds */
var class<KFPerk> MonsterPerkClass;
var const Name MusicMessageType;
var const int EarnedDosh;
/** Has perk xp/level been loaded (local player) */
var private const bool bPerkStatsLoaded;
/** Id of previously selected perk */
var public byte SavedPerkIndex;
/** Player zed spawn params (Versus) */
var transient sPlayerZedSpawnInfo PlayerZedSpawnInfo;
var KFPawn_Human UsablePawn;
/** Current power up applied to the player */
var KFPowerUp CurrentPowerUp;
/** Current power up class applied to the player */
var class<KFPowerUp> CurrentPowerUpClass;
/*********************************************************************************************
* @name Checkerboard support (for Neo)
********************************************************************************************* */
var protected float UnmodifiedFOV;
/*********************************************************************************************
* @name Achievements
********************************************************************************************* */
var transient protected int BenefactorDosh;
var private const int BenefactorDoshReq;
/*********************************************************************************************
* @name UDK Variables
********************************************************************************************* */
/** makes playercontroller hear much better (used to magnify hit sounds caused by player) */
var bool bAcuteHearing;
/** additional post processing settings modifier that can be applied
* @note: defaultproperties for this are hardcoded to zeroes in C++
*/
var PostProcessSettings PostProcessModifier;
/** if true, rotate smoothly to desiredrotation */
var bool bUsePhysicsRotation;
struct native ObjectiveAnnouncementInfo
{
/** the default announcement sound to play (can be None) */
var() SoundNodeWave AnnouncementSound;
/** text displayed onscreen for this announcement */
var() localized string AnnouncementText;
};
/** Used to determine if a player can be considered for achievements */
var bool bIsAchievementPlayer;
/*********************************************************************************************
* @name UT Variables
********************************************************************************************* */
/** Controls how often this Player can spam admin commands */
var float NextAdminCmdTime;
var config bool bHideTraderPaths;
var float RefreshObjectiveUITime;
/*********************************************************************************************
* @name Skill Tracking
********************************************************************************************* */
/** The total "shots" taken during this match. This includes gun shots taken, melee attacks, damage intervals for flame weapons, etc */
var int ShotsFired;
/** The total "shots" that hit another pawn during this match */
var int ShotsHit;
/** The total "shots" that hit an another pawn in the head during this match */
var int ShotsHitHeadshot;
/*********************************************************************************************
* @name UI
********************************************************************************************* */
var KFGFxMoviePlayer_Manager MyGFxManager;
var KFGFxMoviePlayer_HUD MyGFxHUD;
var KFGFxMoviePlayer_PostRoundMenu MyGFxPostRoundMenu;
var class<KFGFxMoviePlayer_PostRoundMenu> PostRoundMenuClass;
/** whether trader menu is open or not (only tracked client-side) */
var transient bool bClientTraderMenuOpen;
var protected bool bPlayerUsedUpdatePerk;
var class<KFAutoPurchaseHelper> PurchaseHelperClass;
var KFAutoPurchaseHelper PurchaseHelper;
var float NextSpectatorDelay;
/** Local customization pawn spawned for mid-match gear changes */
var transient KFPawn_Customization LocalCustomizationPawn;
var bool bDownloadingContent;
var string DefaultAvatarPath;
var UniqueNetId LobbyUId;
var UniqueNetId FriendUId;
/*********************************************************************************************
* @name Mixer
********************************************************************************************* */
var name MixerRallyBoneNames[2];
var string MixerCurrentDefaultScene;
/*********************************************************************************************
* @name LED Effects
********************************************************************************************* */
var class<KFLEDEffectsManager> LEDEffectsManagerClass;
var KFLEDEffectsManager LEDEffectsManager;
/*********************************************************************************************
* @name Audio
********************************************************************************************* */
/** Zed time sounds */
var AKEvent ZedTimeEnterSound;
var AKEvent ZedTimeExitSound;
var AKEvent ZedTimePartialEnterSound;
var AKEvent ZedTimePartialExitSound;
/** Store TimeDilation for the previous Tick */
var float LastTimeDilation;
/** Event to post to pause all Wwise events */
var AkEvent PauseWwiseEvent;
/** Event to post to resume all paused Wwise events */
var AkEvent ResumeWwiseEvent;
/** "Ears ringing" play event */
var AkEvent EarsRingingPlayEvent;
/** "Ears ringing" stop event */
var AkEvent EarsRingingStopEvent;
/** User preference option */
var bool bNoEarRingingSound;
/** "Heart pounding in ears" start event (probably has a fade-in) */
var AkEvent LowHealthStartEvent;
/** "Heart pounding in ears" stop event (probably has a fade-out) */
var AkEvent LowHealthStopEvent;
/** Component on which to play stingers */
var AkComponent StingerAkComponent;
/** Resets all lowpass filters */
var AkEvent ResetFiltersEvent;
/** Weapon UI sounds */
var AkEvent FlashlightOnEvent;
var AkEvent FlashlightOffEvent;
var AkEvent NightVisionOnEvent;
var AkEvent NightVisionOffEvent;
/** Sound to play when all collectibles have been found */
var AkEvent AllMapCollectiblesFoundEvent;
/*********************************************************************************************
* @name Gameplay Effects
********************************************************************************************* */
/** The post process effect used to display gameplay effects such as pain, low health, zed time, etc. */
var MaterialEffect GameplayPostProcessEffects;
/** Name of the gameplay post process effect in the post process chain. This is used to cache the effect so that we can modify its parameters from the game code */
var name GameplayPostProcessEffectName;
/** The material used in the above post process material effect */
var MaterialInstanceConstant GameplayPostProcessEffectMIC;
/** Name of the MIC paramter used to display pain */
var name EffectPainParamName;
/** Name of the MIC parameter used to display low health */
var name EffectLowHealthParamName;
/** Name of the MIC parameter used to display zed time */
var name EffectZedTimeParamName;
/** Name of the MIC parameter used to display zed time sepia*/
var name EffectZedTimeSepiaParamName;
/** Name of the MIC parameter used to display night vision time */
var name EffectNightVisionParamName;
/** Name of the MIC parameter used to display Siren's scream attack effect */
var name EffectSirenScreamParamName;
/** Name of the MIC parameter used to display Bloat's puke attack effect */
var name EffectBloatsPukeParamName;
/** Name of the MIC parameter used to display Bloat's puke attack effect */
var name EffectHealParamName;
/** Name of the MIC parameter used to display the perk skill effect */
var name EffectPerkParamName;
/** Name of the MIC parameter used to display the flash bang effect */
var name EffectFlashBangParamName;
/** Name of the MIC parameter used to display the HellishRage powerUp */
var name EffectPowerUpHellishRageParamName;
/** Night vision active */
var bool bNightVisionActive;
/** Perk skill screen effect is active */
var bool bPerkEffectIsActive;
/** Being grabbed screen effect is active */
var bool bGrabEffectIsActive;
/** Interpolation duration for pain effect */
var transient float PainEffectDuration;
/** Pain Effect - The time remaining for the pain effect */
var transient float PainEffectTimeRemaining;
/** Interpolation duration for pain effect */
var transient float HealEffectDuration;
/** Pain Effect - The time remaining for the pain effect */
var transient float HealEffectTimeRemaining;
/** Interpolation duration for pain effect */
var transient float SonicScreamEffectDuration;
/** Siren Scream Effect - The time remaining for the Siren scream effect */
var transient float SirenScreamEffectTimeRemaining;
/** Interpolation duration for pain effect */
var transient float BloatPukeEffectDuration;
/** Bloats Puke Effect - The time remaining for the Siren scream effect */
var transient float BloatPukeEffectTimeRemaining;
/** Interpolation duration for pain effect */
var transient float FlashBangEffectDuration;
/** Bloats Puke Effect - The time remaining for the Siren scream effect */
var transient float FlashBangEffectTimeRemaining;
/** Interpolation duration for HellishRage effect */
var transient float HellishRagePowerUpEffectDuration;
/** HellishRage PowerUp Effect - The time remaining for the PowerUp effect */
var transient float HellishRagePowerUpEffectTimeRemaining;
/** Low Health Effect - Determines what health is low enough to be considered for low health effects */
var const int LowHealthThreshold;
var transient float TargetZEDTimeEffectIntensity;
var transient float CurrentZEDTimeEffectIntensity;
/** ZED Time Effect - Time remaining in the fade in/out of the effect.*/
var transient float ZEDTimeEffectInterpTimeRemaining;
/** ZED Time Effect - The total fade time if going from an intensity of 0 to 1 or vice versa */
var const float PartialZEDTimeEffectIntensity;
var transient float ExplosionEarRingDuration;
var transient float ExplosionEarRingTimeRemaining;
var transient float ExplosionEarRingEffectIntensity;
var transient float ExplosionEarRingDelay;
var transient bool bPlayingLowHealthSFX;
// partial zed time visibility
var bool bCachedSeeZedTimePawn;
var float CachedZedTimeVisibilityTime;
var bool bRecursingZedTimeVisibility;
var float ZedTimeSightCounter; // interval/tick test
var() PointLightComponent AmplificationLightTemplate;
var transient PointLightComponent AmplificationLight;
var bool bShowKillTicker;
var bool bDisableAutoUpgrade;
var bool bHideBossHealthBar;
var bool bHideRemotePlayerHeadshotEffects;
/*********************************************************************************************
* @name Input
********************************************************************************************* */
/** Represents a percentage of max pawn speed. 0 is highest (100%).
* Used for replicating left analog stick (we hijack DoubleClickMove, so this enum essentially replaces EDoubleClickDir) */
enum EAnalogMovementSpeed
{
AMOVESPEED_0, // relates to DCLICK_None
AMOVESPEED_1, // relates to DCLICK_Left
AMOVESPEED_2, // relates to DCLICK_Right
AMOVESPEED_3 // relates to DCLICK_Forward
// Max relates to DCLICK_Back
};
/** Real-time timer for IgnoreMoveInput */
var private transient float PauseMoveInputTimeLeft;
/** Interval for reducing the rotation speed limit*/
var float RotationAdjustmentInterval;
/** Amount of time over which to reduce the rotation speed limit back to normal*/
var float MaxRotationAdjustmentTime;
/** Current amount of time in the rotation speed limit reduction process*/
var float CurrentRotationAdjustmentTime;
/** Curve used for determining how much to reduce the rotation speed to*/
var InterpCurveFloat RotationAdjustmentCurve;
/** Adds a clamp to the amount of rotation that can happen*/
var float RotationSpeedLimit;
/*********************************************************************************************
* @name Night Vision
********************************************************************************************* */
var() PointLightComponent NVGLightTemplate;
var transient PointLightComponent NVGLight;
/*********************************************************************************************
* @name Depth of field properties
********************************************************************************************* */
var bool bDOFEnabled; // Is DDF turned on?
var(DOF) float DOFFocalRange;
var(DOF) float DOFFocalAperture;
var(DOF) float DOFFocusBlendRate; // How quickly for the focus distance to adjust to what the player is currently looking at. This is not directly comparable to time, but the calculation is as BlendRate*Time
var float DOFFocusDepth; // What depth the player's eyes are focused at
var(DOF) float DOFMaxFocusDepth; // What max focus depth we'll use. If this is too far, when the focus trace fails (like tracing against the skybox) we end up blurring the whole screen. So this caps the max distance so this doesn't happen
var(DOF) float DOFStaticFocusDepth; // Static depth the player's eyes are focused at(if set to 0.0, then DDFFocusDepth is calculated off what we're looking at)
/** Maximum angle (in degrees) to an enemy for calculating focal distance */
var(DOF) editconst float DOFMaxEnemyAngle;
/** Whether gameplay DOF overrides are active */
var bool bGamePlayDOFActive;
/** Whether ironsights is active and overriding the normal DOF parameters */
var bool bIronSightsDOFActive;
/** Controls the weighting between environment/IronSights DOF and gameplay DOF (e.g. night vision) */
var float DOF_GP_LerpControl;
/** Controls the weighting between environment and IronSights DOF */
var float DOF_IS_LerpControl;
/** Controls how fast NVG DOF lerps on */
var float DOF_NVG_BlendInSpeed;
/** Controls how fast NVG DOF lerps off */
var float DOF_NVG_BlendOutSpeed;
/** Film grain scale to use when bCinematicMode=TRUE */
var float CIN_ImageGrainScale;
/** Postprocess parameters when Night Vision is enabled */
var(NVG_Post) float NVG_FocusBlendRate;
var(NVG_Post) float NVG_ImageGrainScale;
/** [World] Fixed focal distance for NVG */
var(NVG_Post) float NVG_DOF_FocalDistance;
/** [World] World-unit radius around the focal point that is unblurred. */
var(NVG_Post) float NVG_DOF_SharpRadius;
/** [World] World-unit focal radius that defines how far away from the focal plane ( +/- sharp radius ) the maximum far/near blur radius is reached. */
var(NVG_Post) float NVG_DOF_FocalRadius;
/** [World] Minimum blur size. */
var(NVG_Post) float NVG_DOF_MinBlurSize;
/** [World] Maximum blur size for near-field (objects closer than focal point). */
var(NVG_Post) float NVG_DOF_MaxNearBlurSize;
/** [World] Maximum blur size for far-field (objects more distance than focal point). */
var(NVG_Post) float NVG_DOF_MaxFarBlurSize;
/** [World] Exponent that is used to transition to max blur size inside the focal radius. */
/** 1 -> linear transition */
/** > 1 slower than linear transition */
/** < 1 faster than linear transition */
var(NVG_Post) float NVG_DOF_ExpFalloff;
// Cinematic mode DOF overrides
var(Cinematic_DOF) float DOF_Cinematic_BlendInSpeed;
var(Cinematic_DOF) float DOF_Cinematic_BlendOutSpeed;
var(Cinematic_DOF) float DOF_Cinematic_FocalDistance;
var(Cinematic_DOF) float DOF_Cinematic_SharpRadius;
var(Cinematic_DOF) float DOF_Cinematic_FocalRadius;
var(Cinematic_DOF) float DOF_Cinematic_MinBlurSize;
var(Cinematic_DOF) float DOF_Cinematic_MaxNearBlurSize;
var(Cinematic_DOF) float DOF_Cinematic_MaxFarBlurSize;
var(Cinematic_DOF) float DOF_Cinematic_ExpFalloff;
// Gameplay DOF overrides
var(GP_DOF) float DOF_GP_BlendInSpeed;
var(GP_DOF) float DOF_GP_BlendOutSpeed;
var(GP_DOF) float DOF_GP_FocalDistance;
var(GP_DOF) float DOF_GP_SharpRadius;
var(GP_DOF) float DOF_GP_FocalRadius;
var(GP_DOF) float DOF_GP_MinBlurSize;
var(GP_DOF) float DOF_GP_MaxNearBlurSize;
var(GP_DOF) float DOF_GP_MaxFarBlurSize;
var(GP_DOF) float DOF_GP_ExpFalloff;
/*********************************************************************************************
* @name Misc Postprocess settings
********************************************************************************************* */
var bool bReflectionsEnabled; // Is SSR turned on?
var bool bBlurEnabled; // Is full-screen blur enabled?
var float BlurStrength; // Strength of blur
var float BlurBlendInSpeed; // Speed at which blur blends in
var float BlurBlendOutSpeed; // Speed at which blur blends out
var float BlurLerpControl;
/*********************************************************************************************
* @name Online (persistant) Stats
********************************************************************************************* */
/** Online subsystem statics */
var private KFOnlineStatsRead StatsRead; //Only used to read in the stats, everything else should be handled by the write object
var private KFOnlineStatsWrite StatsWrite;
const MapObjectiveIndex = 4; //Index of our special event objective. Use the same one for all events
//@HSL_BEGIN - JRO - PS4 Sessions
/*********************************************************************************************
* @name PS4 Sessions
********************************************************************************************* */
/** TRUE if we are processing a game invite */
var bool bProcessingGameInvite;
/** List of people we've played with this game */
var array<PlayerNameIdPair> RecentlyMetPlayers;
//@HSL_END
/** TRUE if we are logging in for online play. Requires privilege checks */
var bool bLoggingInForOnlinePlay;
/** TRUE if online privilege check is pending */
var bool bOnlinePrivilegeCheckPending;
// Callback for login complete
var delegate<LoginCompleteCallback> OnLoginComplete;
/** Pending game settings for session create. Used when needing to destroy current session first */
var OnlineGameSettings PendingGameSessionCreateGameSettings;
/*********************************************************************************************
* @name Dialog and AAR stats
********************************************************************************************* */
struct native PostWaveReplicationInfo
{
var Vector VectData1; //used for compressing data //X:HeadShots Y:Dosh Earned Z:Damage Dealt
var Vector VectData2; //used for compressing data //Damage Taken, Heals Received, Heals Given
var byte NumStomps;
var byte LargeZedKills;
//Dialog
var bool bDiedDuringWave;
var bool bBestTeammate;
var bool bKilledMostZeds;
var bool bEarnedMostDosh;
var bool bAllSurvivedLastWave;
var bool bSomeSurvivedLastWave;
var bool bOneSurvivedLastWave;
var bool bKilledFleshpoundLastWave;
var bool bKilledScrakeLastWave;
/** Work-around so we don't have to wait for GRI.OpenTrader() to determine dialog */
var bool bOpeningTrader;
var class< KFPawn_Monster > ClassKilledByLastWave;
var byte RepCount;
};
var repnotify PostWaveReplicationInfo PWRI;
var EphemeralMatchStats MatchStats;
var Class<EphemeralMatchStats> MatchStatsClass;
/*********************************************************************************************
* @name Spectating
********************************************************************************************* */
enum KFSpectateModes
{
SMODE_PawnFreeCam,
SMODE_PawnThirdPerson,
SMODE_PawnFirstPerson,
SMODE_Roaming
};
var transient KFSpectateModes CurrentSpectateMode;
/** Used to tell the server if a client spectator is active */
var transient float LastUpdateSpectatorActiveTime;
var transient float UpdateSpectatorActiveInterval;
/** For spectators */
var transient int TargetViewRotationPitch;
var transient int TargetViewRotationYaw;
var transient sSavedViewTargetInfo SavedViewTargetInfo;
/*********************************************************************************************
* @name Aim Assist
********************************************************************************************* */
var config protected bool bDebugTargetAdhesion;
var(AimAssist) protected bool bDebugAutoTarget;
/** Interp curve to scale autotarget scoring for different ranges **/
var(AimAssist) InterpCurveFloat ScoreTargetDistanceCurve;
/** Aim correction */
var(AimAssist) float MaxAimCorrectionDistance;
/** Aim correction upper limit (all weapons) */
const MAX_AIM_CORRECTION_SIZE = 35.f;
/*********************************************************************************************
* @name Look At Pawn (Network: Local Player)
********************************************************************************************* */
/** How long to force us to look at a pawn **/
var() protected float ForceLookAtPawnTime;
/** Pawn we're being forced to look at **/
var() protected KFPawn ForceLookAtPawn;
/** Don't use the countdown time, just keep looking at the ForceLookAtPawn **/
var() protected bool bLockToForceLookAtPawn;
/** User preference (e.g. clot grab) */
var() bool bSkipNonCriticalForceLookAt;
/*********************************************************************************************
* @name Debug
********************************************************************************************* */
var bool bDebugPartialZedTime;
var bool bForcePartialZedTime;
/** Position to draw the tracking map */
var(ZedMap) float TrackerXPosition, TrackerYPosition;
/** Scale to draw the tracking map at */
var(ZedMap) float TrackingMapScale;
/** If true the tracking map is a top down view, otherwise its a side view*/
var(ZedMap) bool bTrackingMapTopView;
/** Position to draw the tracking map */
var(ZedMap) float TrackerSpawnVolumeSizeX, TrackerSpawnVolumeSizeY;
/** Distance range for the tracking map*/
var(ZedMap) float TrackingMapRange;
enum ETrackingRangeMode
{
ETR_Custom,
ETR_10Meters,
ETR_25Meters,
ETR_50Meters,
ETR_100Meters,
ETR_250Meters
};
/** Current distance range for the tracking map*/
var transient ETrackingRangeMode CurrentTrackerRangeMode;
enum ETrackingMode
{
ETM_All,
ETM_AllButTargeting,
ETM_PawnsOnly,
ETM_PawnsAndTargetingOnly,
ETM_SpawnsOnly,
ETM_FailedSpawnsOnly,
ETM_HumansAndSpawnsOnly,
ETM_PickupsOnly
};
/** Current targeting mode for the tracking map*/
var transient ETrackingMode CurrentTrackingMode;
/*********************************************************************************************
* @name GameConductor debug drawing
********************************************************************************************* */
enum EGameConductorDebugMode
{
EGCDM_OverallRankAndSkill,
EGCDM_Skill,
EGCDM_OverallAccuracy,
EGCDM_LifeSpan,
EGCDM_ZedSpawning,
EGCDM_GameplayAdjustments,
EGCDM_Status
};
/** Current targeting mode for the tracking map*/
var transient EGameConductorDebugMode CurrentGameConductorDebugMode;
/*********************************************************************************************
* @name Dosh Vault Debug Values
********************************************************************************************* */
var int DebugLastSeenDoshVaultValue;
var int DebugCurrentDoshVaultValue;
var int DebugCurrentDoshVaultTier;
/*********************************************************************************************
* @name Dosh Vault Round Delta
********************************************************************************************* */
var int BeginningRoundVaultAmount;
/*********************************************************************************************
* @name NoGoZones
********************************************************************************************* */
var transient float NoGoStartTime;
var transient bool bNoGoActive;
/*********************************************************************************************
* @name Profile Stored values
********************************************************************************************* */
var transient byte StoredLocalUserNum;
cpptext
{
virtual UBOOL Tick( FLOAT DeltaSeconds, ELevelTick TickType );
virtual UBOOL WantsLedgeCheck();
virtual void SmoothTargetViewRotation(APawn* TargetPawn, FLOAT DeltaSeconds);
// partial zed time visibility
UBOOL CacheSeeZedTimePawn(UBOOL bCanSeeZedTimePawn, APawn* P=NULL);
virtual UBOOL TestZedTimeVisibility(APawn* P, UNetConnection* Connection, UBOOL bLocalPlayerTest);
UBOOL ConsiderForZedTimeVisibility(APawn* P);
virtual void TickPartialZedTime(FLOAT DeltaSeconds);
void StopPartialZedTime();
// Post process
virtual void BlendPostProcessSettings(FPostProcessSettings& PPSettings, const FPostProcessSettings& PPSettingsA, const FPostProcessSettings& PPSettingsB, const FLOAT BlendWeight) const;
virtual void AddPostProcessSettings(FPostProcessSettings& PPSettings, const FPostProcessSettings& PPSettingsA) const;
virtual void ModifyPostProcessSettings(FPostProcessSettings& PPSettings) const;
virtual void PreSave();
/** Computes the focal distance for depth of field */
FLOAT ComputeFocalDistance();
/**
* This will score both Adhesion and Friction targets. We want the same scoring function as we
* don't want the two different systems fighting over targets that are close.
**/
virtual FLOAT ScoreTargetAdhesionFrictionTarget( const APawn* P, FLOAT MaxDistance, const FVector& CamLoc, const FRotator& CamRot, const FInterpCurveFloat TargetingAngle ) const;
/** Determines whether this Pawn can be used for TargetAdhesion **/
virtual UBOOL IsValidTargetAdhesionFrictionTarget( APawn* P, FLOAT MaxDistance, UBOOL bAutoAimTarget=FALSE );
virtual UBOOL HearSound(UAkBaseSoundObject* InSoundCue, AActor* SoundPlayer, const FVector& SoundLocation, UBOOL bStopWhenOwnerDestroyed
#ifdef __TW_WWISE_
,const FRotator& SoundRotation
#endif // __TW_WWISE_
);
/** Like HearSound */
virtual UBOOL HearDialog(AActor* DialogSpeaker, UAkEvent* DialogEvent, BYTE bCanBeMinimized);
INT* GetOptimizedRepList( BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel );
}
replication
{
if ( bNetDirty )
CurrentPerk, PWRI;
if( Role == ROLE_Authority )
TargetViewRotationPitch, TargetViewRotationYaw;
}
/**
* Sets whether or not hardware physics are enabled.
*
* @param bEnabled Whether to enable the physics or not.
*/
native function SetHardwarePhysicsEnabled(bool bEnabled);
native function SyncInventoryProperties();
native function AddVStat( int Amount );
native function ResetVStat();
native function CheckPerkLevelAchievements();
/** @return Whether or not the user has a keyboard plugged-in. */
native simulated function bool IsKeyboardAvailable() const;
/** @return Whether or not the user has a mouse plugged-in. */
native simulated function bool IsMouseAvailable() const;
/**
* This will find the best AdhesionFriction target based on the params passed in.
**/
native function Pawn GetTargetAdhesionFrictionTarget( FLOAT MaxDistance, const out vector CamLoc, const out Rotator CamRot, const InterpCurveFloat TargetingAngle, optional bool bAutoAimTarget );
/*
* @param BulletWhip - whip sound to play
* @param FireLocation - where shot was fired
* @param FireDir - direction shot was fired
* @param HitLocation - impact location of shot
*/
native function CheckBulletWhip(AkEvent BulletWhip, vector FireLocation, vector FireDir, vector HitLocation, Actor ShootActor);
/** Queries the PRI and returns our current Perk */
native final simulated function KFPerk GetPerk();
native final function PerformPrestigeReset(class<KFPerk> PSGPerkClass);
native reliable server private event PushPlayerStats( PlayerStats Stats );
/** Displays loading movie when client recieves a servertravel */
native private function ShowPreClientTravelMovie(string URLString);
/*********************************************************************************************
* @name Constructors, Destructors, and Loading
********************************************************************************************* */
simulated event PostBeginPlay()
{
super.PostBeginPlay();
PostAkEvent( ResetFiltersEvent );
UpdateSeasonalState();
MatchStats = new(Self) MatchStatsClass;
ClearDownloadInfo();
if (WorldInfo.NetMode != NM_DedicatedServer)
{
InitMixerDelegates();
InitLEDManager();
InitDiscord();
}
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
if (OnlineSub != none)
{
OnlineSub.AddOnReadOnlineAvatarCompleteDelegate(OnAvatarReceived);
OnlineSub.AddOnReadOnlineAvatarByNameCompleteDelegate(OnAvatarURLPS4Received);
}
}
function UpdateVOIP()
{
local KFPlayerInput KFPI;
local KFPlayerReplicationInfo KFPRI;
KFPRI = KFPlayerReplicationInfo( PlayerReplicationInfo );
KFPI = KFPlayerInput(PlayerInput);
if(KFPI == None || KFPRI == None)
{
// Retry
SetTimer(0.1f, false, nameof(UpdateVOIP) );
}
else
{
if(KFPI.bRequiresPushToTalk)
{
ClientStopNetworkedVoice();
}
else
{
ClientStartNetworkedVoice();
}
}
}
function SpawnDefaultHUD()
{
super.SpawnDefaultHUD();
// Spawn the default HUD here
if (KFGFxHudWrapper(myHUD) != none)
{
if (MyGFxHUD == none || MyGFxHUD.Class != KFGFxHudWrapper(myHUD).GetHUDClass() && !(class'WorldInfo'.Static.IsMenuLevel()))
{
KFGFxHudWrapper(myHUD).CreateHUDMovie();
}
}
}
reliable client function ClientSetHUD(class<HUD> newHUDType)
{
super.ClientSetHUD(newHUDType);
// Spawn the default HUD here
if (KFGFxHudWrapper(myHUD) != none)
{
if ((MyGFxHUD == none || MyGFxHUD.Class != KFGFxHudWrapper(myHUD).GetHUDClass()) && !(class'WorldInfo'.Static.IsMenuLevel()))
{
KFGFxHudWrapper(myHUD).CreateHUDMovie();
}
}
}
simulated function CheckSpecialEventID()
{
//Don't cache the actual value until we have all the right set of valid data
if( class'KFGameEngine'.static.GetSeasonalEventID() >= 0 )
{
StatsWrite.UpdateSpecialEventState();
}
else
{
SetTimer(RefreshObjectiveUITime, false, 'CheckSpecialEventID');
}
}
simulated function CheckWeeklyEventID()
{
//Don't cache the actual value until we have all the right set of valid data
if (class'KFGameEngine'.static.GetWeeklyEventIndex() >= 0 )
{
StatsWrite.UpdateWeeklyEventState();
}
else
{
SetTimer(RefreshObjectiveUITime, false, 'CheckWeeklyEventID');
}
}
simulated event UpdateSeasonalState()
{
SetState('Event', GetSeasonalStateName());
}
function ClearDownloadInfo()
{
local KFGameViewportClient GVC;
//clear download
GVC = KFGameViewportClient( class'Engine'.static.GetEngine().GameViewport );
if(GVC != none)
{
GVC.ClearDownloadInfo();
}
}
reliable server event AddV( int Amount )
{
`log("adding dosh: " @Amount );
AddVStat( Amount );
}
reliable server event PushV()
{
`log("pushing dosh");
`AnalyticsLog(("pc_dosh_earned",
PlayerReplicationInfo,
"#"$EarnedDosh));
ResetVStat();
}
simulated event name GetSeasonalStateName()
{
local int EventId;
local KFMapInfo KFMI;
local bool bIsWWLWeekly; // WWL Weekly should not allow seasonal overrides
EventId = class'KFGameEngine'.static.GetSeasonalEventID();
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
if (KFMI != none)
{
KFMI.ModifySeasonalEventId(EventId);
}
bIsWWLWeekly = class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12 && KFGameReplicationInfo(WorldInfo.GRI) != none && KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode;
if (bIsWWLWeekly)
return 'No_Event';
//Remove any year information, just get 1s digit
switch (EventId % 10)
{
case SEI_Summer:
return 'Summer_Sideshow';
case SEI_Fall:
return 'Fall';
case SEI_Winter:
return 'Winter';
case SEI_Spring:
return 'Spring';
default:
return 'No_Event';
}
return 'No_Event';
}
simulated event ReplicatedEvent( name VarName )
{
super.ReplicatedEvent( VarName );
if (VarName == 'PlayerReplicationInfo')
{
class'KFHeadShotEffectList'.static.RefreshCachedHeadShotEffectId();
}
if ( VarName == nameof(Pawn) )
{
//SetAmplificationLightEnabled(Pawn != None);
ToggleHealthEffects(Pawn != None);
}
if ( VarName == nameof(PWRI) )
{
SubmitPostWaveStats();
}
if ( VarName == 'CurrentPerk' )
{
//force trader to update if it is open
RecievedNewPerkClass();
}
}
simulated event ReceivedPlayer()
{
local UIDataStore_OnlinePlayerData PlayerDataDS;
local int i;
local KFGameEngine KFEngine;
KFEngine = KFGameEngine(class'Engine'.static.GetEngine());
Super.ReceivedPlayer();
// Read profile settings for local player
if( IsLocalPlayerController() )
{
if (!(class'WorldInfo'.Static.IsMenuLevel()) && class'WorldInfo'.Static.IsConsoleBuild() && !OnlineSub.IsGameOwned() && !OnlineSub.IsFreeTrialPeriodActive())
{
`log("Trail Check: Calling Disconnect KFPlayerController ReceivedPlayer");
ConsoleCommand("Disconnect");
}
// Check to see if we already have profile settings
if( OnlineSub.PlayerInterface.GetProfileSettings( LocalPlayer(Player).ControllerId ) != None )
{
// Profile settings read complete automatically if we already have a cached profile
OnReadProfileSettingsComplete(LocalPlayer(Player).ControllerId, true);
}
// Time to queue up a read
// BWJ - XB1 has to read this at the IIS after an initial user has been established
else if( !WorldInfo.IsConsoleBuild( CONSOLE_Durango ) || KFEngine.CheckSkipLobby() )
{
PlayerDataDS = UIDataStore_OnlinePlayerData(class'UIInteraction'.static.GetDataStoreClient().FindDataStore('OnlinePlayerData', LocalPlayer(Player)));
if( PlayerDataDS != none )
{
OnlineSub.PlayerInterface.AddReadProfileSettingsCompleteDelegate(LocalPlayer(Player).ControllerId, OnReadProfileSettingsComplete);
OnlineSub.PlayerInterface.ReadProfileSettings( LocalPlayer(Player).ControllerId, OnlineProfileSettings(PlayerDataDS.ProfileProvider.Profile) );
}
}
// If we joined a server, we need to strip old options so we don't carry them around on map transition
// They are used in KFGameInfo PreLogin() to prevent matchmaking if the server recently changed
// options, but should never be used to prevent login during server travel.
if (WorldInfo.NetMode == NM_Client)
{
for (i = KFEngine.LastURL.Op.Length - 1; i >= 0; i--)
{
if (InStr(KFEngine.LastURL.Op[i], "Difficulty=", false, true) != INDEX_NONE ||
InStr(KFEngine.LastURL.Op[i], "Game=", false, true) != INDEX_NONE ||
InStr(KFEngine.LastURL.Op[i], "GameLength=", false, true) != INDEX_NONE)
{
KFEngine.LastURL.Op.Remove(i, 1);
}
}
UpdateVOIP();
}
}
// Initialize our customization character as authority since we have a PRI
// if we're not authority, wait for the PRI to be replicated to call ClientInitialize
if ( Role == ROLE_Authority && IsLocalController() )
{
PlayerReplicationInfo.ClientInitialize( self );
}
//@HSL_BEGIN - JRO - 3/21/2016 - PS4 Sessions
HandleConsoleSessions();
//@HSL_END
}
reliable server function AskForPawn()
{
local KFPawn P;
P = KFPawn(Pawn);
if( P != none )
{
//Pawn ref can be always none on client under certain circumstances
P.ForceOpenActorChannel();
}
super.AskForPawn();
}
/**
* This function will be called to notify the player controller that the world has received it's game class. In the case of a client
* we need to initialize the Input System here.
*
* @Param GameClass - The Class of the game that was replicated
*/
simulated function ReceivedGameClass(class<GameInfo> GameClass)
{
local LocalPlayer LP;
Super.ReceivedGameClass(GameClass);
// Update world PPC here on the client since RestartPlayer is not called
// - aladenberger 11/15/12
LP = LocalPlayer(Player);
if(LP != None)
{
// reset the post processing when we get a new PC
LP.RemoveAllPostProcessingChains();
LP.InsertPostProcessingChain(LP.Outer.GetWorldPostProcessChain(), INDEX_NONE, true);
// Initialize game play post process effects such as damage, low health, etc.
// This needs to be done after the post process chain has been swapped out above.
InitGameplayPostProcessFX();
}
}
/** Allow achievements after the first successful spawn */
event Possess(Pawn aPawn, bool bVehicleTransition)
{
local KFPlayerReplicationInfo KFPRI;
if( aPawn != none && aPawn.IsAliveAndWell() )
{
bIsAchievementPlayer = true;
}
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
if(KFPRI != None && KFPawn_Customization(aPawn) == none)
{
// BWJ - 10-5-16 - This is required for realtime multiplay on PS4
KFPRI.bHasSpawnedIn = true;
KFPRI.bNetDirty = true;
}
ClientMatchStarted();
super.Possess( aPawn, bVehicleTransition );
}
reliable client function ClientRestart(Pawn NewPawn)
{
Super.ClientRestart(NewPawn);
if(NewPawn == none)
{
return;
}
// Destroy our local gear customization pawn when we've received a real one from the server
if( LocalCustomizationPawn != none && NewPawn != LocalCustomizationPawn && !LocalCustomizationPawn.bPendingDelete )
{
LocalCustomizationPawn.Destroy();
LocalCustomizationPawn = none;
}
UsablePawn = KFPawn_Human(NewPawn);
// Reinitialize FOV
FixFOV();
// Reset low pass audio filter
SetRTPCValue( 'GRENADEFX', 0, true );
// Upon spawning close all menus
MyGFxManager.CloseMenus(true);
if(MyGFxHUD != none && MyGFxHUD.SpectatorInfoWidget != none)
{
MyGFxHUD.SpectatorInfoWidget.SetVisible(!PlayerReplicationInfo.bOnlySpectator);
}
// Clear out persistant dead bodies when players respawn
if( WorldInfo.MyGoreEffectManager != none )
{
KFGoreManager(WorldInfo.MyGoreEffectManager).ResetPersistantGore(true);
}
// Spawn the amplification light
// Disabled for performance
//SetAmplificationLightEnabled( true );
// Reset Depth of Field
EnableDepthOfField(false);
// Only unlock achievements for people who have possessed a pawn during this game
if( NewPawn != none && NewPawn.IsAliveAndWell() )
{
bIsAchievementPlayer = true;
}
NewPawn.MovementSpeedModifier = 1.f;
// Spawn the default HUD here, if it has changed or hasn't spawned yet.
if (KFGFxHudWrapper(myHUD) != none)
{
if (MyGFxHUD == none || MyGFxHUD.Class != KFGFxHudWrapper(myHUD).GetHUDClass())
{
KFGFxHudWrapper(myHUD).CreateHUDMovie(true);
}
}
if(MyGFxHUD != none)
{
MyGFxHUD.ReceivePawn(KFPawn(Pawn));
}
}
function ActivatePlayerDiedSequenceEvents()
{
local Sequence GameSeq;
local array<SequenceObject> AllSeqEvents;
local array<int> ActivateIndices;
local int i;
local KFGameInfo KFGI;
KFGI = KFGameInfo(WorldInfo.Game);
GameSeq = WorldInfo.GetGameSequence();
if (GameSeq != None && KFGI != none)
{
// find any "player died" events that exist
GameSeq.FindSeqObjectsByClass(class'KFSeqEvent_PlayerDied', true, AllSeqEvents);
if (KFGI.GetLivingPlayerCount() > 0)
{
ActivateIndices[0] = 0;
}
else
{
ActivateIndices[0] = 1;
}
// activate them
for (i = 0; i < AllSeqEvents.Length; i++)
{
KFSeqEvent_PlayerDied(AllSeqEvents[i]).CheckActivate(WorldInfo, None, false, ActivateIndices);
}
}
}
/** Need to handle death of our customization pawn */
function PawnDied( Pawn inPawn )
{
if (inPawn == Pawn)
{
if (KFPawn_Customization(InPawn) != none)
{
if( !Pawn.bDeleteMe && !Pawn.bPendingDelete )
{
Pawn.UnPossessed();
}
Pawn = none;
if( MyGFxManager != none )
{
MyGFxManager.CloseMenus();
}
return;
}
else
{
ActivatePlayerDiedSequenceEvents();
}
}
super.PawnDied( inPawn );
}
/**
* Lists all console events to the HUD.
*/
exec function ListConsoleEvents()
{
`if(`notdefined(ShippingPC))
if( !class'WorldInfo'.Static.IsConsoleBuild() && !class'WorldInfo'.Static.IsConsoleDedicatedServer() )
{
super.ListConsoleEvents();
}
`endif
}
function SpawnReconnectedPlayer()
{
if ( WorldInfo.NetMode == NM_Client )
return;
WorldInfo.Game.RestartPlayer( Self );
}
function bool CanRestartPlayer()
{
return PlayerReplicationInfo != None && !PlayerReplicationInfo.bOnlySpectator && IsReadyToPlay() && HasClientLoadedCurrentWorld() && PendingSwapConnection == None;
}
/** Returns TRUE if player is in a state that allows them to be spawned */
function bool IsReadyToPlay()
{
return WorldInfo.Game != none
? KFGameInfo(WorldInfo.Game).IsPlayerReady( KFPlayerReplicationInfo(PlayerReplicationInfo) )
: PlayerReplicationInfo.bReadyToPlay;
}
/** Returns TRUE if this player is in a state that allows cinematics to be viewed */
function bool CanViewCinematics()
{
return PlayerReplicationInfo.bOnlySpectator || IsReadyToPlay();
}
/** reset input to defaults */
function ResetPlayerMovementInput()
{
if ( !bCinematicMode )
{
Super.ResetPlayerMovementInput();
}
}
event InitInputSystem()
{
super.InitInputSystem();
// Console has no push to talk. Default to team for voice chat
if( WorldInfo.IsConsoleBuild() )
{
CurrentVoiceChannel = EVC_TEAM;
}
// Only register for local talker if in a networked game
if( WorldInfo.NetMode == NM_ListenServer || WorldInfo.NetMode == NM_Client )
{
// Mark us as a user of the VoIP system.
if ( OnlineSub != none )
{
OnlineSub.RegisterLocalTalker( LocalPlayer(Player).ControllerId );
}
else
{
if(VoiceInterface != none)
{
VoiceInterface.RegisterLocalTalker( LocalPlayer(Player).ControllerId );
}
}
// Tell the server that we're registered as a local talker now and can begin voice
ServerNotifyRegisteredAsLocalTalker();
}
// Periodically check to see if there are connected controllers
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) )
{
SetTimer( 2.0, false, nameof(CheckForConnectedControllers) );
}
RegisterTalkerDelegate();
}
/** Spawn and place a customization pawn in a KFCustomizationPoint, or PlayerStart if no CustomizationPoints exist */
function CreateCustomizationPawn()
{
local KFGameInfo KFGI;
local NavigationPoint BestStart;
KFGI = KFGameInfo( WorldInfo.Game );
if( KFGI == none || KFGI.bRestartLevel && WorldInfo.NetMode!=NM_DedicatedServer && WorldInfo.NetMode!=NM_ListenServer )
{
`warn("bRestartLevel && !server, abort from RestartPlayer"@WorldInfo.NetMode);
return;
}
BestStart = GetBestCustomizationStart( KFGI );
// try to create a pawn to use of the default class for this player
if (Pawn == None)
{
Pawn = KFGI.SpawnCustomizationPawn( BestStart );
}
if ( KFPawn_Customization( Pawn ) != none )
{
KFPawn_Customization( Pawn ).InitializeCustomizationPawn( self, BestStart );
}
}
/** Get an unused customization point, if none are found use a PlayerStart point */
function NavigationPoint GetBestCustomizationStart( KFGameInfo KFGI )
{
local NavigationPoint BestStartSpot;
BestStartSpot = KFGI.FindCustomizationStart( self );
// if a start spot wasn't found,
if (BestStartSpot == None)
{
BestStartSpot = KFGI.FindPlayerStart( self, 0);
`warn("No customization points have been found, using PlayerStart instead");
if ( BestStartSpot == none )
{
`warn("Player start not found, failed to restart player");
return none;
}
}
return BestStartSpot;
}
/** Spawns a local customization pawn to be used only for mid-match gear changes */
function SpawnMidGameCustomizationPawn()
{
local class<KFGameInfo> KFGameClass;
local NavigationPoint BestCP;
local KFCustomizationPoint CP;
local PlayerStart PS;
local rotator StartRotation;
local KFPawn_Customization CustomizationPawn;
KFGameClass = class<KFGameInfo>( WorldInfo.GRI.GameClass );
if( KFGameClass == none )
{
return;
}
foreach AllActors( class'KFCustomizationPoint', CP )
{
if( KFGameClass.static.CheckSpawnProximity(CP, self, GetTeamNum(), true) )
{
BestCP = CP;
break;
}
}
if( BestCP == none )
{
// Just choose any old customization point if we didn't find any valid ones
BestCP = CP;
// Fall back on playerstarts if we have no customization pawn
if( BestCP == none )
{
foreach AllActors( class'PlayerStart', PS )
{
if( KFGameClass.static.CheckSpawnProximity(CP, self, GetTeamNum(), true) )
{
BestCP = PS;
break;
}
}
}
// Final fallback
if( BestCP == none )
{
BestCP = PS;
}
}
// Don't allow pawn to be spawned with any pitch or roll
StartRotation.Yaw = BestCP.Rotation.Yaw;
CustomizationPawn = Spawn( KFGameClass.default.CustomizationPawnClass,,, BestCP.Location, StartRotation,, true );
if( CustomizationPawn != none )
{
// Save our viewtarget info
SavedViewTargetInfo.SavedViewTarget = ViewTarget;
SavedViewTargetInfo.SavedCameraMode = PlayerCamera.CameraStyle;
SavedViewTargetInfo.SavedRotation = Rotation;
// Camera fade BEFORE we init our customization pawn (which changes our camera)
ClientSetCameraFade( true, MakeColor(0,0,0,255), vect2d(1.f, 0.f), 0.5f, true );
// Local pawn!
CustomizationPawn.RemoteRole = ROLE_None;
Pawn = CustomizationPawn;
CustomizationPawn.InitializeCustomizationPawn( self, BestCP );
LocalCustomizationPawn = CustomizationPawn;
}
else
{
SavedViewTargetInfo.SavedViewTarget = none;
}
}
/** Restores original viewtarget after leaving the gear menu */
function ReturnToViewTarget()
{
local bool bNeedsNewViewTarget;
// Clean up our customization pawn
if( LocalCustomizationPawn != none )
{
if( ViewTarget == LocalCustomizationPawn )
{
bNeedsNewViewTarget = true;
}
if( Pawn == LocalCustomizationPawn )
{
UnPossess();
bNeedsNewViewTarget = true;
}
if( LocalCustomizationPawn != none && !LocalCustomizationPawn.bPendingDelete )
{
LocalCustomizationPawn.Destroy();
LocalCustomizationPawn = none;
}
}
if( bNeedsNewViewTarget && WorldInfo.GRI.bMatchHasBegun && IsSpectating() )
{
// Fade in
ClientSetCameraFade( true, MakeColor(0,0,0,255), vect2d(1.f, 0.f), 0.75f, true );
// Restore original view target settings
if( SavedViewTargetInfo.SavedViewTarget != none && !SavedViewTargetInfo.SavedViewTarget.bPendingDelete )
{
SetViewTarget( SavedViewTargetInfo.SavedViewTarget );
SetCameraMode( SavedViewTargetInfo.SavedCameraMode );
SetRotation( SavedViewTargetInfo.SavedRotation );
}
else
{
// Get a new viewtarget, ours is gone
ServerViewNextPlayer();
}
}
// Clear for next time
SavedViewTargetInfo.SavedViewTarget = none;
}
function RegisterOnlineDelegates()
{
super.RegisterOnlineDelegates();
KFGameEngine(class'GameEngine'.static.GetEngine()).RegisterOnlineDelegates();
}
function ShowBestRegionSelectedPopup(int RegionIndex)
{
local string LocRegionName;
LocRegionName = PlayfabInter.GetLocalizedRegionName(RegionIndex);
`QALog(`location@`showvar(LocRegionName)@"Selected as default region", true);
}
exec function TestRegionQuery()
{
OnlineSub.StartRegionPingAndSelectDefaultRegion(ShowBestRegionSelectedPopup);
}
function OnReadProfileSettingsComplete(byte LocalUserNum,bool bWasSuccessful)
{
local KFProfileSettings Profile;
local KFPlayerInput KFInput;
local KFGameInfo KFGI;
local KFGameEngine KFEngine;
local KFPlayerReplicationInfo KFPRI;
local string MatchmakingRegion;
local KFGoreManager GoreMgr;
local UniqueNetId LobbyId, Zero;
StoredLocalUserNum = LocalUserNum;
Profile = KFProfileSettings(OnlineSub.PlayerInterface.GetProfileSettings(LocalUserNum));
`QAlog(`location@`showvar(Profile)@`showvar(bWasSuccessful), true);
if(Profile != none)
{
SavedPerkIndex = byte(Profile.GetProfileInt(KFID_SavedPerkIndex));
bSkipNonCriticalForceLookAt = Profile.GetProfileBool(KFID_AutoTurnOff);
bShowKillTicker = Profile.GetProfileBool(KFID_ShowKillTicker);
bNoEarRingingSound = Profile.GetProfileBool(KFID_ReduceHightPitchSounds);
bHideBossHealthBar = Profile.GetProfileBool(KFID_HideBossHealthBar);
bDisableAutoUpgrade = Profile.GetProfileBool(KFID_DisableAutoUpgrade);
bHideRemotePlayerHeadshotEffects = Profile.GetProfileBool(KFID_HideRemoteHeadshotEffects);
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
if(KFPRI != none)
{
KFPRI.SelectCharacter(Profile.GetProfileInt(KFID_StoredCharIndex), true);
}
KFInput = KFPlayerInput(PlayerInput);
if(KFInput != none)
{
KFInput.bQuickWeaponSelect = Profile.GetProfileBool(KFID_QuickWeaponSelect);
KFInput.bInvertController = Profile.GetProfileBool(KFID_InvertController);
KFInput.bAutoTargetEnabled = Profile.GetProfileBool(KFID_AutoTargetEnabled);
KFInput.bForceFeedbackEnabled = Profile.GetProfileBool(KFID_ForceFeedbackEnabled);
KFInput.bTargetAdhesionEnabled = Profile.GetProfileBool(KFID_TargetAdhesionEnabled);
KFInput.bTargetFrictionEnabled = Profile.GetProfileBool(KFID_TargetFrictionEnabled);
KFInput.GamepadSensitivityScale = Profile.GetProfileFloat(KFID_GamepadSensitivityScale);
KFInput.GamepadZoomedSensitivityScale = Profile.GetProfileFloat(KFID_GamepadZoomedSensitivityScale);
KFInput.GamepadDeadzoneScale = Profile.GetProfileFloat(KFID_GamepadDeadzoneScale);
KFInput.GamepadAccelerationJumpScale = Profile.GetProfileFloat(KFID_GamepadAccelerationJumpScale);
KFInput.SetGamepadLayout(Profile.GetProfileInt(KFID_CurrentLayoutIndex));
KFInput.bToggleToRun = Profile.GetProfileBool(KFID_ToggleToRun);
KFInput.bAllowSwapTo9mm = Profile.GetProfileBool(KFID_AllowSwapTo9mm);
KFInput.ReInitializeControlsUI();
}
// Chris - Moved the HUD setting import to the actual HUD class,
// this might run before the HUD is created
KFGI = KFGameInfo(WorldInfo.Game);
if(KFGI != none)
{
KFGI.GoreLevel = Profile.GetProfileInt(KFID_GoreLevel);
}
KFEngine = KFGameEngine(Class'Engine'.static.GetEngine());
if(KFEngine != none)
{
KFEngine.VOIPVolumeMultiplier = Profile.GetProfileFloat(KFID_VOIPVolumeMultiplier);
KFEngine.VOIPMicVolumeMultiplier = Profile.GetProfileFloat(KFID_VOIPMicVolumeMultiplier);
KFEngine.MusicVolumeMultiplier = Profile.GetProfileFloat(KFID_MusicVolumeMultiplier);
KFEngine.SFxVolumeMultiplier = Profile.GetProfileFloat(KFID_SFXVolumeMultiplier);
KFEngine.DialogVolumeMultiplier = Profile.GetProfileFloat(KFID_DialogVolumeMultiplier);
KFEngine.MasterVolumeMultiplier = Profile.GetProfileFloat(KFID_MasterVolumeMultiplier);
KFEngine.bMusicVocalsEnabled = Profile.GetProfileBool(KFID_MusicVocalsEnabled);
KFEngine.bShowWelderInInv = Profile.GetProfileBool(KFID_ShowWelderInInventory);
KFEngine.bUseAltAimOnDual = Profile.GetProfileBool(KFID_UseAltAimOnDuals);
KFEngine.bAntiMotionSickness = Profile.GetProfileBool(KFID_AntiMotionSickness);
KFEngine.bMinimalChatter = Profile.GetProfileBool(KFID_MinimalChatter);
KFEngine.SafeFrameScale = Profile.GetProfileFloat(KFID_SafeFrameScale);
KFEngine.bEnableNative4k = Profile.GetProfileBool(KFID_Native4kResolution);
// Ensure this never goes bad
if( KFEngine.SafeFrameScale == 0.f )
{
KFEngine.SafeFrameScale = 1.0;
}
// Now set the UI scale
SetUIScale(KFEngine.SafeFrameScale);
if(class'WorldInfo'.static.IsConsoleBuild())
{
class'KFGameEngine'.static.SetCrosshairEnabled(Profile.GetProfileBool(KFID_ShowConsoleCrossHair));
}
else
{
class'KFGameEngine'.static.SetCrosshairEnabled(Profile.GetProfileBool(KFID_ShowCrossHair));
}
KFEngine.GammaMultiplier = Profile.GetProfileFloat(KFID_GammaMultiplier);
KFEngine.SetGamma(KFEngine.GammaMultiplier);
KFEngine.SetNative4k(KFEngine.bEnableNative4k);
KFEngine.PadVolumeMultiplier = Profile.GetProfileBool(KFID_ControllerSoundEnabled) ? 100.0f : 0.0f;
KFEngine.InitAudioOptions();
KFEngine.InitGamma();
// Set the matchmaking version
if( PlayfabInter != none )
{
MatchmakingRegion = Profile.GetProfileString(KFID_MatchmakingRegion);
// For PS4, we need to find a best fit region now by pinging
if( MatchmakingRegion == "" && WorldInfo.IsConsoleBuild( CONSOLE_Orbis ) )
{
OnlineSub.StartRegionPingAndSelectDefaultRegion(none);
MatchmakingRegion = class'PlayfabInterface'.default.CurrRegionName;
}
PlayfabInter.CurrRegionName = MatchmakingRegion;
}
}
// Set the HUD scale
if( KFHUDBase(myHUD) != none )
{
KFHUDBase(myHUD).FriendlyHudScale = Profile.GetProfileFloat(KFID_FriendlyHudScale);
KFHUDBase(myHUD).ClassicPlayerInfo = Profile.GetProfileInt(KFID_ClassicPlayerInfo) != 0;
}
if( MyGFxManager != none )
{
MyGFxManager.OnProfileSettingsRead();
}
GoreMgr = KFGoreManager(class'WorldInfo'.static.GetWorldInfo().MyGoreEffectManager);
if(GoreMgr != none)
{
GoreMgr.DesiredGoreLevel = Profile.GetProfileInt(KFID_GoreLevel);
}
}
if (OnlineSub.GetLobbyInterface().GetLobbyFromCommandline(LobbyId))
{
OnlineSub.GetLobbyInterface().LobbyInvite(LobbyId, Zero, true);
}
SavedPerkIndex = CheckCurrentPerkAllowed();
// Save the new perk selected in the the profile settings
Profile.SetProfileSettingValueInt( KFID_SavedPerkIndex, SavedPerkIndex );
// Update our cached Emote Id
class'KFEmoteList'.static.RefreshCachedEmoteId();
class'KFHeadShotEffectList'.static.RefreshCachedHeadShotEffectId();
}
simulated function byte CheckCurrentPerkAllowed()
{
local KFGameReplicationInfo KFGRI;
// If the perk is not allowed for this game mode, search for one that is available starting from the index 0
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none && !KFGRI.IsPerkAllowed(PerkList[SavedPerkIndex].PerkClass) )
{
SavedPerkIndex = 0;
for(SavedPerkIndex=0 ; SavedPerkIndex<PerkList.length ; SavedPerkIndex++)
{
if( KFGRI.IsPerkAllowed(PerkList[SavedPerkIndex].PerkClass) )
{
break;
}
}
}
return SavedPerkIndex;
}
function UpdatePerkOnInit()
{
local KFProfileSettings Profile;
SavedPerkIndex = CheckCurrentPerkAllowed();
// Save the new perk selected in the the profile settings
Profile = KFProfileSettings(OnlineSub.PlayerInterface.GetProfileSettings(StoredLocalUserNum));
if( Profile != None )
{
Profile.SetProfileSettingValueInt( KFID_SavedPerkIndex, SavedPerkIndex );
}
}
/*********************************************************************************************
* @name Console Connection Handling
********************************************************************************************* */
simulated function HandleConnectionStatusChange(EOnlineServerConnectionStatus ConnectionStatus)
{
if( WorldInfo.IsConsoleBuild() )
{
if( ConnectionStatus != OSCS_Connected )
{
HandleNetworkError( true );
}
else
{
HandleReconnected();
}
}
}
simulated function HandleLoginStatusChange( bool bLoggedIn )
{
if( WorldInfo.IsConsoleBuild() )
{
if( !bLoggedIn )
{
HandleNetworkError( false );
}
else
{
HandleReconnected();
}
}
}
simulated function HandleNetworkError( bool bConnectionLost )
{
local KFGameViewportClient GVC;
local string ErrorMessage;
GVC = KFGameViewportClient(MyGFxManager.GetGameViewportClient());
`log("KFPlayerController.uc --- HandleNetworkError --- bConnectionLost?"@bConnectionLost);
if( GVC.bSeenIIS )
{
OnlineSub.GameInterface.DestroyOnlineGame('Game');
// We can't be in an online game
if(WorldInfo.GetMapName(true) != "KFMainMenu")
{
if( WorldInfo.NetMode != NM_Standalone)
{
// Flag which kind of message we need to show
if( bConnectionLost )
{
GVC.bNeedDisconnectMessage = true;
}
else
{
GVC.bNeedSignoutMessage = true;
}
ConsoleCommand("open KFMainMenu");
}
}
// We can't be in a multiplayer menu
else
{
// Kick player out of matchmaking if they are actively in it
if(MyGFxManager.StartMenu != none && MyGFxManager.StartMenu.GetStartMenuState() == EMatchmaking)
{
if( bConnectionLost )
{
ErrorMessage = Localize("Notifications", class'WorldInfo'.static.IsConsoleBuild(CONSOLE_Durango) ? "ConnectionLostMessageLive" : "ConnectionLostMessage", "KFGameConsole");
}
else
{
ErrorMessage = Localize("Notifications", class'WorldInfo'.static.IsConsoleBuild(CONSOLE_Orbis) ? "PSNSignoutMessage" : "LoggedOutMessage", "KFGameConsole");
}
MyGFxManager.SetStartMenuState(MyGFxManager.GetStartMenuState());
MyGFxManager.StartMenu.ApproveMatchMakingLeave();
MyGFxManager.OpenMenu(UI_Start);
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_ExitToMainMenu,
Localize("Notifications", "ConnectionLostTitle", "KFGameConsole"),
ErrorMessage,
class'KFCommon_LocalizedStrings'.default.OKString);
}
// Rebuild items in the "what's new" box
if( MyGFxManager != none && MyGFxManager.StartMenu != none && MyGFxManager.StartMenu.FindGameContainer != none )
{
MyGFxManager.StartMenu.FindGameContainer.SetWhatsNewItems();
}
}
// Rebuild items in the "what's new" box
if( MyGFxManager != none )
{
MyGFxManager.UpdateMenuBar();
}
}
}
simulated function HandleReconnected()
{
if( MyGFxManager != none )
{
MyGFxManager.UpdateMenuBar();
if( MyGFxManager.StartMenu != none && MyGFxManager.StartMenu.FindGameContainer != none )
{
// Rebuild items in the "what's new" box
MyGFxManager.StartMenu.FindGameContainer.SetWhatsNewItems();
}
}
}
//@HSL_BEGIN - JRO - 3/21/2016 - PS4 Sessions
/*********************************************************************************************
* @name Console Sessions
********************************************************************************************* */
function HandleConsoleSessions()
{
local UniqueNetId NullId;
local KFGameReplicationInfo GRI;
if( WorldInfo.IsConsoleDedicatedServer() && !WorldInfo.IsE3Build() )
{
`log("SESSIONS - ReceivedPlayer");
if(KFGameEngine(Class'Engine'.static.GetEngine()).ConsoleGameSessionGuid == "") // If no session made yet
{
`log("SESSIONS - Still need a session");
GRI = KFGameReplicationInfo(WorldInfo.GRI);
if(GRI.ConsoleGameSessionHost == NullId) // Lucky this guy gets to make the session!
{
`log("SESSIONS - Make one!");
GRI.ConsoleGameSessionHost = PlayerReplicationInfo.UniqueId;
ClientCreateGameSession(PlayfabInter.GetCachedLobbyId(), GameEngine(class'Engine'.static.GetEngine()).bPrivateServer, WorldInfo.Game.MaxPlayers );
}
else // Somebody is still trying to make one
{
`log("SESSIONS - Wait for"@Class'OnlineSubsystem'.static.UniqueNetIdToString(GRI.ConsoleGameSessionHost)@"to make one");
GRI.ConsoleGameSessionPendingPlayers.AddItem(PlayerReplicationInfo.UniqueId);
}
}
}
}
reliable client function ClientCreateGameSession(string LobbyId, bool bPrivate, int MaxPlayers)
{
local OnlineGameSettings GameSettings, OldGameSettings;
local string RemoteAddressString, SessionGuid;
`log("SESSIONS - ClientCreateGameSession"@LobbyId);
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
if (OnlineSub != None && OnlineSub.GameInterface != None)
{
// Create the default settings to get the standard settings to advertise
GameSettings = new class'KFOnlineGameSettings';
OnlineSub.GameInterface.GetGameServerRemoteAddress(RemoteAddressString);
//`log(" - Remote address:"@ RemoteAddressString);
GameSettings.JoinString = RemoteAddressString;
GameSettings.LobbyId = LobbyId;
GameSettings.bRequiresPassword = bPrivate;
GameSettings.NumPublicConnections = MaxPlayers;
GameSettings.SessionTemplateName = "KF2GameSessionTemplate";
PendingGameSessionCreateGameSettings = GameSettings;
// If we are already in a session, we need to destroy the old one first
if( OnlineSub.IsInSession( 'Game' ) )
{
OldGameSettings = OnlineSub.GameInterface.GetGameSettings( 'Game' );
// We want to keep private sessions alive for XB1
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) && OldGameSettings.SessionTemplateName == GameSettings.SessionTemplateName && bPrivate )
{
// Get the active session guid and send off to the server
OnlineSub.GameInterface.ReadSessionGuidBySessionName('Game', SessionGuid);
ServerGameSessionCreated(SessionGuid);
}
// We want to keep the same sessions alive for PS4
else if (WorldInfo.IsConsoleBuild(CONSOLE_Orbis) && OldGameSettings.LobbyId == GameSettings.LobbyId)
{
// Get the active session guid and send off to the server
OnlineSub.GameInterface.ReadSessionGuidBySessionName('Game', SessionGuid);
ServerGameSessionCreated(SessionGuid);
}
// Need to leave old session so we can create a new one
else
{
OnlineSub.GameInterface.AddDestroyOnlineGameCompleteDelegate(OnOldSessionDestroyedForNewGameSessionCreate);
OnlineSub.GameInterface.DestroyOnlineGame('Game');
}
}
// not in a session, create now
else
{
TryCreateGameSessionNow();
}
}
}
private simulated function OnOldSessionDestroyedForNewGameSessionCreate( name SessionName, bool bWasSuccessful )
{
// Now that its destroyed, create now!
OnlineSub.GameInterface.ClearDestroyOnlineGameCompleteDelegate( OnOldSessionDestroyedForNewGameSessionCreate );
TryCreateGameSessionNow();
}
private simulated function TryCreateGameSessionNow()
{
// Register the delegate so we can see when it's done
OnlineSub.GameInterface.AddCreateOnlineGameCompleteDelegate(OnGameSessionCreateComplete);
// Now kick off the async create
if (!OnlineSub.GameInterface.CreateOnlineGame(LocalPlayer(Player).ControllerId, 'Game', PendingGameSessionCreateGameSettings))
{
`log("Failed to create online game");
OnlineSub.GameInterface.ClearCreateOnlineGameCompleteDelegate(OnGameSessionCreateComplete);
}
// Clear the pending game settings
PendingGameSessionCreateGameSettings = none;
}
simulated function OnGameSessionCreateComplete(name SessionName,bool bWasSuccessful)
{
local string SessionGuid;
`log("SESSIONS - OnGameSessionCreateComplete:"@bWasSuccessful);
if(!bWasSuccessful)
{
ServerGameSessionFailed();
return;
}
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
OnlineSub.GameInterface.ClearCreateOnlineGameCompleteDelegate(OnGameSessionCreateComplete);
// Update the server with the session info
OnlineSub.GameInterface.ReadSessionGuidBySessionName( 'Game', SessionGuid );
`log("SESSIONS - SessionGuid:"@SessionGuid);
ServerGameSessionCreated(SessionGuid);
if(OnlineSub.GameInterface.GetPendingMembersToInvite().length > 0)
{
OnCreateGameSessionForPlayTogetherComplete(SessionGuid, bWasSuccessful);
}
}
reliable server function ServerGameSessionCreated(string SessionGuid)
{
`log("SESSIONS - ServerGameSessionCreated:"@SessionGuid);
KFGameEngine(Class'Engine'.static.GetEngine()).ConsoleGameSessionGuid = SessionGuid;
KFGameReplicationInfo(WorldInfo.GRI).ConsoleGameSessionGuid = SessionGuid;
}
reliable server function ServerGameSessionFailed()
{
local KFPlayerController Controller;
local UniqueNetId NullId;
local KFGameReplicationInfo GRI;
`log("SESSIONS - ServerGameSessionFailed");
GRI = KFGameReplicationInfo(WorldInfo.GRI);
if (GRI.ConsoleGameSessionPendingPlayers.Length > 0)
{
// Pop the front pending player
GRI.ConsoleGameSessionHost = GRI.ConsoleGameSessionPendingPlayers[0];
GRI.ConsoleGameSessionPendingPlayers.Remove(0,1);
// Tell him to make the session
foreach WorldInfo.AllControllers(class'KFPlayerController', Controller)
{
if(Controller.PlayerReplicationInfo.UniqueId == GRI.ConsoleGameSessionHost)
{
Controller.ClientCreateGameSession(PlayfabInter.GetCachedLobbyId(), GameEngine(class'Engine'.static.GetEngine()).bPrivateServer, WorldInfo.Game.MaxPlayers );
break;
}
}
}
else
{
`log("Everyone failed to make a game session");
GRI.ConsoleGameSessionHost = NullId;
}
}
simulated function TryJoinGameSession()
{
local OnlineGameSettings GS;
`log("SESSIONS - TryJoinGameSession");
if( KFGameReplicationInfo(WorldInfo.GRI).ConsoleGameSessionGuid == "" )
{
`log(" - Bad session guid. returning");
return;
}
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
if (OnlineSub != None && OnlineSub.GameInterface != None)
{
GS = OnlineSub.GameInterface.GetGameSettings( 'Game' );
// If session doesn't exist, join now
if( GS == none )
{
JoinGameSessionNow();
}
// In an old session, cleanup first
else if( GS.SessionGuid != KFGameReplicationInfo(WorldInfo.GRI).ConsoleGameSessionGuid )
{
`log("need to clean up old session before joining new one");
OnlineSub.GameInterface.AddDestroyOnlineGameCompleteDelegate(OnOldSessionDestroyedForNewGameSessionJoin);
OnlineSub.GameInterface.DestroyOnlineGame( 'Game' );
}
}
}
private simulated function OnOldSessionDestroyedForNewGameSessionJoin(name SessionName, bool bWasSuccessful)
{
// Now that its destroyed, join new one now!
OnlineSub.GameInterface.ClearDestroyOnlineGameCompleteDelegate(OnOldSessionDestroyedForNewGameSessionJoin);
JoinGameSessionNow();
}
simulated function JoinGameSessionNow()
{
local OnlineGameSearch NewGameSearch;
local byte LocalPlayerNum;
LocalPlayerNum = LocalPlayer(Player).ControllerId;
// Create a OnlineGameSettings based off the platform session info
NewGameSearch = new class'OnlineGameSearch';
OnlineSub.GameInterface.BindSessionGuidToSearch(LocalPlayerNum, NewGameSearch, KFGameReplicationInfo(WorldInfo.GRI).ConsoleGameSessionGuid);
OnlineSub.GameInterface.AddJoinOnlineGameCompleteDelegate(OnJoinGameSessionComplete);
if ( !OnlineSub.GameInterface.JoinOnlineGame(LocalPlayerNum, 'Game', NewGameSearch.Results[0]) )
{
`log("SESSIONS - Failed to join game");
}
}
simulated function OnJoinGameSessionComplete(name SessionName,bool bWasSuccessful)
{
OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
`log("SESSIONS - OnJoinGameSessionComplete:"@bWasSuccessful);
OnlineSub.GameInterface.ClearJoinOnlineGameCompleteDelegate(OnJoinGameSessionComplete);
}
simulated function ResetMusicStateForTravel()
{
// Make sure to reset music state before map travelling (lookin' at you, Monster Ball)
PlaySoundBase(AkEvent'WW_MSTG_Global.Set_State_Music_Reset', true);
}
event PreClientTravel( string PendingURL, ETravelType TravelType, bool bIsSeamlessTravel )
{
ResetMusicStateForTravel();
super.PreClientTravel(PendingURL, TravelType, bIsSeamlessTravel);
// If this is a serveraltravel start loading movie early
if( TravelType == TRAVEL_Relative && !bIsSeamlessTravel )
{
ShowPreClientTravelMovie(PendingURL);
}
DestroyOnlineGame();
}
/*********************************************************************************************
* @name Show InGame Invite Popup
********************************************************************************************* */
function showInvitePopup(string FriendName, UniqueNetId LobbyId, UniqueNetId FriendId)
{
local string AvatarPath;
LobbyUId = LobbyId;
FriendUId = FriendId;
AvatarPath = GetSteamAvatar(FriendId);
`log(getFuncName()@"friendName="@FriendName@"avatar="@AvatarPath);
MyGFxManager.DelayedOpenPopup(EConfirmInvite, EDPPID_Misc,
Class'KFCommon_LocalizedStrings'.default.InvitePopupTitleString,
FriendName$":"$Class'KFCommon_LocalizedStrings'.default.InvitePopupTextString,
Class'KFCommon_LocalizedStrings'.default.AcceptString,
Class'KFCommon_LocalizedStrings'.default.DeclineString,
acceptInviteFromFriend, rejectInviteFromFriend, "img://"$AvatarPath);
}
function acceptInviteFromFriend()
{
`log(getFuncName());
KFOnlineLobbySteamworks(OnlineSub.GetLobbyInterface()).acceptInviteFromFriend();
}
function rejectInviteFromFriend()
{
local KFOnlineLobbySteamworks SteamworksLobby;
`log(getFuncName());
if (OnlineSub != none)
{
SteamworksLobby = KFOnlineLobbySteamworks(OnlineSub.GetLobbyInterface());
}
if (SteamworksLobby != none)
{
SteamworksLobby.RejectInvite(LobbyUId, FriendUId);
}
}
/*********************************************************************************************
* @name Console Session Invites
********************************************************************************************* */
/**
* Delegate called when the user accepts a game invite externally. This allows
* the game code a chance to clean up before joining the game via
* JoinOnlineGame() call.
*
* NOTE: There must be space for all signed in players to join the game. All
* players must also have permission to play online too.
*
* @param InviteResult the search/settings for the game we're to join
*/
function OnGameInviteAccepted(const out OnlineGameSearchResult InviteResult, OnGameInviteAcceptedResult ResultReason)
{
local KFGameViewportClient Viewport;
`log("SESSIONS - OnGameInviteAccepted");
if(!class'GameEngine'.static.IsGameFullyInstalled())
{
NotifyInviteFailed("PlayGoBusy");
return;
}
CachedInviteResult = InviteResult;
if( CachedInviteResult.GameSettings != None)
{
Viewport = KFGameViewportClient( class'Engine'.static.GetEngine().GameViewport );
if (WorldInfo.IsConsoleBuild(CONSOLE_Durango) && CachedInviteResult.GameSettings.NumOpenPublicConnections <= 0)
{
NotifyInviteFailed();
return;
}
if( WorldInfo.IsConsoleBuild(CONSOLE_Durango) && CachedInviteResult.GameSettings.OwningPlayerId != LocalPlayer(Player).GetUniqueNetId() )
{
if( WorldInfo.bIsMenuLevel )
{
// If we're not at the IIS, just logout now
if( Viewport.bSeenIIS )
{
KFGameEngine(class'Engine'.static.GetEngine()).PerformLogout();
}
// Now we manually activate the user
OnlineSub.ManuallyActivateUser(CachedInviteResult.GameSettings.OwningPlayerId);
}
// Not at main menu, we need to exit back
else
{
KFGameEngine( class'Engine'.static.GetEngine() ).GameSettingsForPendingInvite = CachedInviteResult.GameSettings;
KFGameEngine(class'Engine'.static.GetEngine()).PerformLogout();
return;
}
}
if( Viewport.bSeenIIS )
{
StartLogin( OnLoginForGameInviteComplete, true );
}
else
{
TryAutoLoginForInvite();
}
}
else
{
if(ResultReason == OGIAR_WrongAccount)
{
//HSL_TODO: JRO - Add specific UI message
NotifyInviteFailed();
}
else
{
// At the moment, this can't happen, but couldn't hurt to keep it here in case that changes...
NotifyInviteFailed();
}
}
}
function TryAutoLoginForInvite()
{
local KFGFxMenu_IIS IISMenu;
IISMenu = KFGFxMenu_IIS(MyGFxManager.CurrentMenu);
if( IISMenu != none )
{
IISMenu.AttemptAutoLogin(OnLoginForGameInviteComplete);
}
else
{
// If we're getting here from a fresh boot, the IIS menu isn't ready yet! Wait for a bit.
SetTimer(1.0, false, nameof(TryAutoLoginForInvite), self);
}
}
function OnLoginForGameInviteComplete()
{
`log("SESSIONS - OnLoginForGameInviteComplete");
if (OnlineSub != None && OnlineSub.GameInterface != None && OnlineSub.SystemInterface != None)
{
// Make sure the login succeeded
if (OnlineSub.PlayerInterface.GetLoginStatus(LocalPlayer(Player).ControllerId) == LS_LoggedIn)
{
bProcessingGameInvite = true;
// First thing we do is leave a game if we're in one
if( OnlineSub.IsInSession( 'Game' ) )
{
// Set the destroy delegate so we can know when that is complete
OnlineSub.GameInterface.AddDestroyOnlineGameCompleteDelegate(OnGameDestroyedForInviteComplete);
// Now we can destroy the game (completion delegate guaranteed to be called)
OnlineSub.GameInterface.DestroyOnlineGame( 'Game' );
}
else
{
OnGameDestroyedForInviteComplete( 'Game', true );
}
}
else
{
NotifyInviteFailed();
}
}
}
function OnGameDestroyedForInviteComplete(name SessionName,bool bWasSuccessful)
{
`log("SESSIONS - OnGameDestroyedForInviteComplete");
OnlineSub.GameInterface.ClearDestroyOnlineGameCompleteDelegate(OnGameDestroyedForInviteComplete);
// Does failure here matter?
if(!bWasSuccessful)
{
`log("SESSIONS - DestroyOnlineGame for invite failed");
}
// Set the delegate for notification of the join completing
OnlineSub.GameInterface.AddJoinOnlineGameCompleteDelegate(OnSessionJoinComplete);
// This will have us join async
if (!OnlineSub.GameInterface.JoinOnlineGame(LocalPlayer(Player).ControllerId, 'Game', CachedInviteResult))
{
OnlineSub.GameInterface.ClearJoinOnlineGameCompleteDelegate(OnSessionJoinComplete);
// Do some error handling
NotifyInviteFailed();
}
}
function OnSessionJoinComplete(name SessionName,bool bWasSuccessful)
{
local OnlineGameSettings GameSettings;
local bool bGameSession, bPartySession;
GameSettings = OnlineSub.GameInterface.GetGameSettings(SessionName);
if( !bWasSuccessful )
{
NotifyInviteFailed();
return;
}
if( class'WorldInfo'.static.IsConsoleBuild( CONSOLE_Durango ) )
{
bGameSession = GameSettings.SessionTemplateName ~= "KF2GameSessionTemplate";
bPartySession = GameSettings.SessionTemplateName ~= "KF2PartySessionTemplate";
}
else
{
bGameSession = SessionName == 'Game';
bPartySession = SessionName == 'Party';
}
if( bGameSession )
{
`log("SESSIONS - OnSessionJoinComplete"@GameSettings.LobbyId@GameSettings.JoinString);
JoinPlayfabServer(bWasSuccessful, GameSettings.JoinString);
}
else if( bPartySession )
{
if(!WorldInfo.bIsMenuLevel)
{
ConsoleCommand("open KFMainMenu");
}
}
else
{
`warn("Unknown session joined"@SessionName@"and template name"@GameSettings.SessionTemplateName);
}
}
function JoinPlayfabServer(bool bWasSuccessful, string ServerIp )
{
local string OpenCommand;
`log("SESSIONS - OnQueryAdditionalServerInfoForInviteComplete with success"@bWasSuccessful@"and server IP"@ServerIp);
if( !bWasSuccessful || ServerIp == "" )
{
NotifyInviteFailed();
}
else
{
OpenCommand = "open"@ServerIp;
OpenCommand $= "?PlayfabPlayerId="$PlayfabInter.CachedPlayfabId;
OpenCommand $= "?bJoinViaInvite";
`log("Going to connect with URL:"@OpenCommand);
ConsoleCommand( OpenCommand );
}
}
function NotifyInviteFailed(optional string LocKey = "UnableToJoinInvite")
{
bProcessingGameInvite = false;
Super.NotifyInviteFailed();
`log("NotifyInviteFailed:"@LocKey $ "Title"@LocKey $ "Message");
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_Misc,
Localize("Notifications", LocKey $ "Title", "KFGameConsole"),
Localize("Notifications", LocKey $ "Message", "KFGameConsole"),
class'KFCommon_LocalizedStrings'.default.OKString);
}
/*********************************************************************************************
* @name PS4 Play Together
********************************************************************************************* */
function OnPlayTogetherStarted()
{
local KFGameViewportClient Viewport;
`log("PLAY - OnPlayTogetherStarted");
Viewport = KFGameViewportClient( class'Engine'.static.GetEngine().GameViewport );
if( Viewport.bSeenIIS )
{
//`log("OnPlayTogetherStarted Trial Check " $ "Owned: " $ OnlineSub.IsGameOwned() $ " TrailOver: " $ OnlineSub.IsFreeTrialPeriodActive());
//free trial is over and we're already logged in trying to access playtogether feature...no!
if (class'WorldInfo'.Static.IsConsoleBuild() && !OnlineSub.IsGameOwned())
{
if (OnlineSub.CanCheckFreeTrialState() && !OnlineSub.IsFreeTrialPeriodActive())
{
class'KFGFxMoviePlayer_Manager'.static.HandleFreeTrialError(FTN_BuyGame);
return;
}
if(!OnlineSub.CanCheckFreeTrialState())
{
class'KFGFxMoviePlayer_Manager'.static.HandleFreeTrialError(FTN_NetworkCheckFailed);
return;
}
}
StartLogin( OnLoginForPlayTogetherComplete, true );
}
else
{
TryAutoLoginForPlayTogether();
}
}
function TryAutoLoginForPlayTogether()
{
local KFGFxMenu_IIS IISMenu;
IISMenu = KFGFxMenu_IIS(MyGFxManager.CurrentMenu);
if( IISMenu != none )
{
IISMenu.AttemptAutoLogin(OnLoginForPlayTogetherComplete);
}
else
{
// If we're getting here from a fresh boot, the IIS menu isn't ready yet! Wait for a bit.
SetTimer(1.0, false, nameof(TryAutoLoginForPlayTogether), self);
}
}
function OnLoginForPlayTogetherComplete()
{
`log("PLAY - OnLoginForPlayTogetherComplete");
if(!class'GameEngine'.static.IsGameFullyInstalled() )
{
NotifyPlayTogetherFailed();
return;
}
if (OnlineSub != None && OnlineSub.GameInterface != None && OnlineSub.SystemInterface != None)
{
// Make sure the login succeeded
if (OnlineSub.PlayerInterface.GetLoginStatus(LocalPlayer(Player).ControllerId) == LS_LoggedIn)
{
bProcessingGameInvite = true;
// First thing we do is leave a game if we're in one
if( OnlineSub.IsInSession( 'Game' ) )
{
// Set the destroy delegate so we can know when that is complete
OnlineSub.GameInterface.AddDestroyOnlineGameCompleteDelegate(OnGameDestroyedForPlayTogetherComplete);
// Now we can destroy the game (completion delegate guaranteed to be called)
OnlineSub.GameInterface.DestroyOnlineGame( 'Game' );
}
else
{
OnGameDestroyedForPlayTogetherComplete( 'Game', true );
}
}
else
{
NotifyPlayTogetherFailed();
}
}
}
function OnGameDestroyedForPlayTogetherComplete(name SessionName,bool bWasSuccessful)
{
local KFGameViewportClient GVC;
GVC = KFGameViewportClient(MyGFxManager.GetGameViewportClient());
`log("PLAY - OnGameDestroyedForPlayTogetherComplete");
OnlineSub.GameInterface.ClearDestroyOnlineGameCompleteDelegate(OnGameDestroyedForPlayTogetherComplete);
// Does failure here matter?
if(!bWasSuccessful)
{
`log("PLAY - DestroyOnlineGame for PlayTogether failed");
}
if(WorldInfo.IsMenuLevel())
{
`log("PLAY - Session created, sending invites!"@bWasSuccessful);
TryMainMenuPlayTogether();
}
else
{
GVC.bHandlePlayTogether = true;
ConsoleCommand("open KFMainMenu");
}
}
function TryMainMenuPlayTogether()
{
local string InviteMessage;
if(MyGFxManager != none && MyGFxManager.StartMenu != none)
{
// MyGFxManager.StartMenu.OpenMultiplayerMenu();
MyGFxManager.OnlineLobby.MakeLobby(6, LV_Private);
InviteMessage = Localize("Notifications", "InviteMessage", "KFGameConsole");
MyGFxManager.OnlineLobby.SendInviteToUsers(OnlineSub.GameInterface.GetPendingMembersToInvite(), InviteMessage);
OnlineSub.GameInterface.ResetPendingMembersToInvite();
}
else
{
// Some scenarios where the main menu isn't ready yet?!
SetTimer(0.5, false, nameof(TryMainMenuPlayTogether), self);
}
}
// Called from OnGameSessionCreateComplete above if GetPendingPlayTogetherMembers().length > 0
function OnCreateGameSessionForPlayTogetherComplete(string SessionGuid,bool bWasSuccessful)
{
local string InviteMessage;
`log("PLAY - Session created, sending invites!"@bWasSuccessful);
if(!bWasSuccessful)
{
NotifyPlayTogetherFailed();
return;
}
MyGFxManager.UnloadCurrentPopup();
InviteMessage = Localize("Notifications", "InviteMessage", "KFGameConsole");
OnlineSub.PlayerInterface.SendGameInviteToUsers(SessionGuid, 'Game', OnlineSub.GameInterface.GetPendingMembersToInvite(), InviteMessage);
OnlineSub.GameInterface.ResetPendingMembersToInvite();
}
function NotifyPlayTogetherFailed(optional string LocKey = "UnableToPlayTogether")
{
bProcessingGameInvite = false;
`log("NotifyPlayTogetherFailed:"@LocKey $ "Title"@LocKey $ "Message");
MyGFxManager.UnloadCurrentPopup();
MyGFxManager.DelayedOpenPopup(ENotification, 0,
Localize("Notifications", LocKey $ "Title", "KFGameConsole"),
Localize("Notifications", LocKey $ "Message", "KFGameConsole"),
class'KFCommon_LocalizedStrings'.default.OKString);
}
//@HSL_END
/*********************************************************************************************
* @name Dosh Vault
********************************************************************************************* */
function int GetPreStigeValueDoshRewardValue()
{
if (StatsWrite != none)
{
return StatsWrite.GetPreStigeValueDoshRewardValue();
}
return INDEX_NONE;
}
function float GetDoshVaultTierValue()
{
if(StatsWrite != none)
{
return StatsWrite.GetDoshVaultTierValue();
}
return INDEX_NONE;
}
function int GetTotalDoshCount()
{
if(DebugCurrentDoshVaultValue != INDEX_NONE)
{
`log("DEBUG GetTotalDoshCount:" @DebugCurrentDoshVaultValue);
return DebugCurrentDoshVaultValue;
}
if(StatsWrite != none)
{
return StatsWrite.GetTotalDoshCount();
}
return INDEX_NONE;
}
function int GetLastSeenDoshCount()
{
if(DebugLastSeenDoshVaultValue != INDEX_NONE)
{
return DebugLastSeenDoshVaultValue;
}
if(StatsWrite != none)
{
return StatsWrite.GetLastSeenDoshCount();
}
return INDEX_NONE;
}
function int GetUnseenDoshCount()
{
if(DebugLastSeenDoshVaultValue != INDEX_NONE)
{
return DebugCurrentDoshVaultValue - DebugLastSeenDoshVaultValue;
}
if(StatsWrite != none)
{
return StatsWrite.GetUnseenDoshCount();
}
return INDEX_NONE;
}
function CheckUnlockDoshVaultReward()
{
if (StatsWrite != none)
{
StatsWrite.CheckUnlockDoshVaultReward();
}
}
function MarkDoshVaultSeen()
{
if(StatsWrite != none)
{
StatsWrite.MarkDoshVaultSeen();
}
}
function VerifyDoshVaultCrates()
{
if (StatsWrite != none)
{
StatsWrite.VerifyDoshVaultCrates();
}
}
function CheckHasViewedDoshVault()
{
if (StatsWrite != none)
{
StatsWrite.CheckHasViewedDoshVault();
}
}
/*********************************************************************************************
* @name Skill Tracking
********************************************************************************************* */
function AddShotsFired( int AddedShots )
{
ShotsFired += AddedShots;
//`log("ShotsFired = "$ShotsFired$" accuracy % = "$(Float(ShotsHit)/Float(ShotsFired) * 100.0));
}
function AddShotsHit( int AddedHits )
{
ShotsHit += AddedHits;
//`log("ShotsHit = "$ShotsHit$" accuracy % = "$(Float(ShotsHit)/Float(ShotsFired) * 100.0));
}
function AddHeadHit( int AddedHits )
{
ShotsHitHeadshot += AddedHits;
//`log("ShotsHitHeadshot = "$ShotsHitHeadshot$" accuracy % = "$(Float(ShotsHitHeadshot)/Float(ShotsFired) * 100.0));
}
/*********************************************************************************************
* @name VOIP
********************************************************************************************* */
/** Registers the talker delegate so I can log who is talking */
function RegisterTalkerDelegate()
{
if (OnlineSub != None && OnlineSub.VoiceInterface != None)
{
OnlineSub.VoiceInterface.AddPlayerTalkingDelegate(OnPlayerTalking);
}
}
function OnPlayerTalking(UniqueNetId TalkingPlayer, bool bIsTalking)
{
local KFPlayerReplicationInfo KFPRI;
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
if ( KFPRI != none )
{
if(KFPRI.UniqueId == TalkingPlayer)
{
KFPRI.VOIPStatusChanged(KFPRI, bIsTalking);
if(bIsTalking)
{
// Make sure we're in the proper channel
if( CurrentVoiceChannel == EVC_ALL
&& (WorldInfo.IsConsoleBuild() || !class'KFPlayerInput'.default.bRequiresPushToTalk) )
{
CurrentVoiceChannel = EVC_TEAM;
}
if(CurrentVoiceChannel == EVC_ALL)
{
KFPRI.ServerNotifyStartVOIP();
}
else
{
KFPRI.ServerNotifyStartTeamVoip();
}
}
else
{
KFPRI.ServerNotifyStopVOIP();
}
}
}
//`Log("UniqueNetId for talking player is ("$TalkingPlayer.Uid[0]$","$TalkingPlayer.Uid[1]$","$TalkingPlayer.Uid[2]$","$TalkingPlayer.Uid[3]$","$TalkingPlayer.Uid[4]$","$TalkingPlayer.Uid[5]$","$TalkingPlayer.Uid[6]$","$TalkingPlayer.Uid[7]$")");
}
/** Tells this client that it should send voice data over the network */
reliable client function ClientStartNetworkedVoice()
{
local KFPLayerReplicationInfo KFPRI;
KFPRI = KFPlayerReplicationInfo( PlayerReplicationInfo );
if ( KFPRI != none )
{
// Make sure we're in the proper channel
if( CurrentVoiceChannel == EVC_ALL
&& (WorldInfo.IsConsoleBuild() || !class'KFPlayerInput'.default.bRequiresPushToTalk) )
{
CurrentVoiceChannel = EVC_TEAM;
}
// Don't do this on Console so we don't show someone as talking when they get no onPlayerTalking if the player has no mic. HSL_BB
if ( !WorldInfo.IsConsoleBuild() )
{
KFPRI.VOIPStatusChanged( PlayerReplicationInfo, true );
if(CurrentVoiceChannel == EVC_ALL)
{
KFPRI.VOIPStatus = 1;
KFPRI.ServerNotifyStartVOIP();
}
else
{
KFPRI.VOIPStatus = 2;
KFPRI.ServerNotifyStartTeamVoip();
}
}
}
super.ClientStartNetworkedVoice();
}
/** Tells this client that it should not send voice data over the network */
reliable client function ClientStopNetworkedVoice()
{
local KFPLayerReplicationInfo KFPRI;
super.ClientStopNetworkedVoice();
KFPRI = KFPlayerReplicationInfo( PlayerReplicationInfo );
if ( KFPRI != none )
{
KFPRI.VOIPStatus = 0;
KFPRI.VOIPStatusChanged( PlayerReplicationInfo, false );
KFPRI.ServerNotifyStopVOIP();
}
}
reliable server function ServerNotifyRegisteredAsLocalTalker()
{
KFPlayerReplicationInfo(PlayerReplicationInfo).bVOIPRegisteredWithOSS = true;
}
/*********************************************************************************************
* @name Input
********************************************************************************************* */
/**
* Server/SP only function for changing whether the player is in cinematic mode. Updates values of various state variables, then replicates the call to the client
* to sync the current cinematic mode.
*
* @param bInCinematicMode specify TRUE if the player is entering cinematic mode; FALSE if the player is leaving cinematic mode.
* @param bHidePlayer specify TRUE to hide the player's pawn (only relevant if bInCinematicMode is TRUE)
* @param bAffectsHUD specify TRUE if we should show/hide the HUD to match the value of bCinematicMode
* @param bAffectsMovement specify TRUE to disable movement in cinematic mode, enable it when leaving
* @param bAffectsTurning specify TRUE to disable turning in cinematic mode or enable it when leaving
* @param bAffectsButtons specify TRUE to disable button input in cinematic mode or enable it when leaving.
*/
function SetCinematicMode( bool bInCinematicMode, bool bHidePlayer, bool bAffectsHUD, bool bAffectsMovement, bool bAffectsTurning, bool bAffectsButtons, optional bool bAffectsDof = true )
{
super.SetCinematicMode( bInCinematicMode, bHidePlayer, bAffectsHUD, bAffectsMovement, bAffectsTurning, bAffectsButtons, bAffectsDof );
// have the server tell the clients whether their buttons should work
ClientSetIgnoreButtons(bAffectsButtons);
}
/** Called to have all button input eaten by the HUD movie player */
reliable client function ClientSetIgnoreButtons(bool bAffectsButtons)
{
local KFGFxHudWrapper GFxHUDWrapper;
// Close the menu if we are in one
if( bAffectsButtons && MyGFxManager != none )
{
MyGFxManager.CloseMenus();
}
// When ignoreing buttons, let the HUD eat all of our input
GFxHUDWrapper = KFGFxHudWrapper(myHUD);
if( GFxHUDWrapper != none && GFxHUDWrapper.HudMovie != none )
{
if( bAffectsButtons )
{
GFxHUDWrapper.HudMovie.HudChatBox.ClearAndCloseChat();
}
GFxHUDWrapper.HudMovie.EatMyInput(bAffectsButtons);
}
}
/**
* @return TRUE if starting a force feedback waveform is allowed; child classes should override this method to e.g. temporarily disable
* force feedback
*/
simulated function bool IsForceFeedbackAllowed()
{
local KFPlayerInput KFInput;
KFInput = KFPlayerInput(PlayerInput);
return KFInput != none && KFInput.bForceFeedbackEnabled && super.IsForceFeedbackAllowed();
}
/*********************************************************************************************
* @name Perks
********************************************************************************************* */
/** InitializePerks is called once after stats have been loaded */
native final function ClientInitializePerks();
native final function ResetPerkStatsLoaded();
native final private function LoadAllPerkLevels();
native final private function ReadStatsTimeout();
native final function byte GetLevel();
native final simulated private function SetActivePerkLevel( byte NewLevel );
native final reliable server private event ServerSetLevel(class <KFPerk> PerkClass, byte NewLevel );
native final reliable server private event ClientSetLevelCheat( byte NewLevel );
native final reliable server private event ServerSetPrestigeLevel(class <KFPerk> PerkClass, byte NewLevel);
native final simulated private function SetActivePerkPrestigeLevel(byte NewLevel);
native final reliable server private event ClientSetPrestigeLevelCheat(byte NewLevel);
native final function float GetPerkPrestigeXPMultiplier(class <KFPerk> PerkClass);
native final function float GetPerkPrestigeNextXPMultiplier(class <KFPerk> PerkClass);
/** Called by UI to change/modify our perk */
native final event RequestPerkChange(byte NewPerkIndex);
native final reliable server private event ServerSetPendingPerkUpdate( byte NewPerkIndex, int NewPerkBuild, byte NewLevel, byte NewPerkPrestigeLevel, bool bClientUpdate=false );
native final reliable server private event ServerSetSavedPerkIndex( byte NewSavedPerkIndex );
native final reliable server protected event ServerSelectPerk( byte NewPerkIndex, byte NewLevel, byte NewPrestigeLevel, optional bool bForce=false );
native final function bool CanUpdatePerkInfo();
native final event bool WasPerkUpdatedThisRound();
native final function ApplyPendingPerks();
static function string GetPerkName( int Index )
{
return default.PerkList[Index].PerkClass.default.PerkName;
}
unreliable server function ServerCallOutPawnCloaking(KFPawn_Monster CloakedPawn)
{
if( CloakedPawn != none )
{
CloakedPawn.CallOutCloaking( self );
}
}
// If we are on the perk page and a level changes, update the menu
simulated event UpdatePerkLevelMenu( class<KFPerk> PerkClass )
{
local KFGFxMenu_Perks PerkMenu;
if( MyGFxManager != none )
{
PerkMenu = KFGFxMenu_Perks(MyGFxManager.CurrentMenu);
if( PerkMenu != none )
{
PerkMenu.UpdateContainers( PerkClass );
}
}
}
/** Makes sure we always spawn in with a valid perk */
function WaitForPerkAndRespawn()
{
// Check on next frame, don't use looping timer because we don't need overlaps here
SetTimer( 0.01f, false, nameOf(Timer_CheckForValidPerk) );
bWaitingForClientPerkData = true;
}
function Timer_CheckForValidPerk()
{
local KFPerk MyPerk;
MyPerk = GetPerk();
if( MyPerk != none && MyPerk.bInitialized )
{
// Make sure that readiness state didn't change while waiting
if( CanRestartPlayer() )
{
WorldInfo.Game.RestartPlayer( self );
}
bWaitingForClientPerkData = false;
return;
}
// Check again next frame
SetTimer( 0.01f, false, nameOf(Timer_CheckForValidPerk) );
}
event SetHaveUpdatePerk( bool bUsedUpdate )
{
if( KFGameReplicationInfo(WorldInfo.GRI).bMatchHasBegun )
{
bPlayerUsedUpdatePerk = bUsedUpdate;
}
}
event NotifyPendingPerkChanges()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if(KFGRI != none && KFGRI.OpenedTrader != none)
{
ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_PendingPerkChangesSet, PlayerReplicationInfo );
}
}
event NotifyPerkUpdated()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if(KFGRI != none && KFGRI.OpenedTrader != none)
{
ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_PendingPerkChangesApplied, PlayerReplicationInfo );
}
}
/*
* Network: Local Player
*/
function NotifyXPGain( class<KFPerk> PerkClass, int Amount, int BonusXP )
{
if( PerkClass != none && PerkClass == GetPerk().static.GetPerkClass() && MyGFxHUD != none && MyGFxHUD.PlayerStatusContainer != none && IsLocalController() )
{
MyGFxHUD.PlayerStatusContainer.UpdateXP( Amount, 0, false, PerkClass );
}
KFGameReplicationInfo(WorldInfo.GRI).PrimaryXPAccumulator += Amount;
`RecordAARPerkXPGain( self, PerkClass, Amount, BonusXP );
}
/*
* Network: Local Player
*/
function NotifyLevelUp(class<KFPerk> PerkClass, byte PerkLevel, byte NewPrestigeLevel)
{
local bool bTierUnlocked;
if( PerkClass != none && IsLocalController() )
{
if( PerkLevel % 5 == 0 && PerkLevel != 0)
{
bTierUnlocked = true;
class'KFPerk'.static.SaveTierUnlockToConfig( PerkClass, 1, PerkLevel );
class'KFLocalMessage_Priority'.static.ClientReceive( self, GMT_TierUnlocked );
//@HSL_BEGIN - JRO - 5/17/2016 - PS4 Activity Feeds
OnlineSub.PlayerInterfaceEx.PostActivityFeedPerkLevelUp(string(PerkClass.Name), PerkLevel);
//@HSL_END
}
else
{
bTierUnlocked = false;
class'KFLocalMessage_Priority'.static.ClientReceive( self, GMT_LevelUp );
}
MyGFxHUD.LevelUpNotificationWidget.ShowLevelUpNotification( PerkClass, Perklevel, bTierUnlocked );
// Update cached level for UI
PerkList[GetPerkIndexFromClass( PerkClass )].PerkLevel = PerkLevel;
PerkList[GetPerkIndexFromClass(PerkClass)].PrestigeLevel = NewPrestigeLevel;
// If this is our current perk, notify it of new level
if( CurrentPerk.Class == PerkClass )
{
SetActivePerkLevel( PerkLevel );
if(bTierUnlocked)
{
PostTierUnlock( PerkClass );
}
}
UpdatePerkLevelMenu( PerkClass );
ClientWriteAndFlushStats();
}
if( Role == ROLE_Authority )
{
`DialogManager.PlayLevelUpDialog( self );
}
else
{
ServerPlayLevelUpDialog();
}
}
//apply the new perk skills/changes
function PostTierUnlock(class<KFPerk> PerkClass)
{
local int PerkBuild;
local byte SelectedSkillsHolder[`MAX_PERK_SKILLS];
PerkBuild = GetPerkBuildByPerkClass( PerkClass );
GetPerk().GetUnpackedSkillsArray( PerkClass, PerkBuild, SelectedSkillsHolder);
CurrentPerk.UpdatePerkBuild(SelectedSkillsHolder, PerkClass);
}
event int GetPerkIndexFromClass( class<KFPerk> InPerkClass )
{
return PerkList.Find( 'PerkClass', InPerkClass );
}
/**
* @brief The Gunslingers RM skill plays an extra sound for certain headshots
*
* @param RhythmMethodSound Sound to play
* @param RhytmMethodRTPCName The sounds RTPC name
* @param Level Num of sound to play
*/
function PlayRMEffect( AkEvent RhythmMethodSound, name RhytmMethodRTPCName, int Level )
{
SetRTPCValue( RhytmMethodRTPCName, Level, true );
PlayAKEvent( RhythmMethodSound );
}
function RecievedNewPerkClass()
{
//refresh the needed UI for online.
if(MyGfxManager != none && MyGfxManager.TraderMenu != none)
{
MyGfxManager.TraderMenu.UpdatePlayerInfo();
}
}
/*********************************************************************************************
* @name Power Ups
********************************************************************************************* */
function ReceivePowerUp(class<KFPowerUp> PowerUpClass)
{
CurrentPowerUpClass = PowerUpClass;
if( CurrentPowerUp == none )
{
CurrentPowerUp = Spawn(PowerUpClass, Pawn, , vect(0,0,0));
CurrentPowerUp.OwnerPC = self;
CurrentPowerUp.OwnerPawn = KFPawn_Human(Pawn);
CurrentPowerUp.ActivatePowerUp();
bForceNetUpdate = true;
}
else if( CurrentPowerUp.class == PowerUpClass )
{
CurrentPowerUp.ReactivatePowerUp();
}
else
{
CurrentPowerUp.DeactivatePowerUp();
ReceivePowerUp(PowerUpClass);
}
ClientUpdatePowerUp(CurrentPowerUpClass);
}
reliable client function ClientUpdatePowerUp(class<KFPowerUp> PowerUpClass)
{
CurrentPowerUpClass = PowerUpClass;
}
simulated function KFPowerUp GetPowerUp()
{
return CurrentPowerUp;
}
simulated function class<KFPowerUp> GetPowerUpClass()
{
return CurrentPowerUpClass;
}
/*********************************************************************************************
* @name Camera
********************************************************************************************* */
native function SetViewTarget(Actor NewViewTarget, optional ViewTargetTransitionParams TransitionParams);
/**
* Returns index of chosen camera anim, or -1 if nothing suitable could be found.
* bDoNotRandomize: start from index 0.
*/
simulated native final function int ChooseRandomCameraAnim(out const array<CameraAnim> Anims, optional FLOAT Scale = 1.f, optional bool bDoNotRandomize);
/**
* Returns true if there is enough space to play the camera anim
*/
simulated native final function bool CameraAnimHasEnoughSpace( CameraAnim Anim, optional float Scale = 1.f );
/**
* Updates depth of field, lerping between on or off states as needed.
*/
native final function UpdateDOF(float DeltaTime);
native final function UpdateDOFGamePlayLerpControl(float DeltaTime);
native final function UpdateDOFIronSightsLerpControl(float DeltaTime);
native final function UpdateFullscreenBlur(float DeltaTime);
exec function PrintOutPrestigeInfo()
{
local int i;
for (i = 0; i < PerkList.length; i++)
{
`log("Perk info:" @PerkList[i].PerkLevel @PerkList[i].PrestigeLevel);
}
}
exec function ShowTestDownloadNotification (string ItemName, Float PercentComplete)
{
if(MyGFxManager != none && MyGFxManager.PartyWidget != none)
{
MyGFxManager.PartyWidget.ShowDownLoadNotification(ItemName, PercentComplete);
}
}
exec function ToggleScreenShotMode()
{
myHUD.ToggleHUD();
if ( Pawn != None && KFWeapon(Pawn.Weapon) != none )
KFWeapon(Pawn.Weapon).bForceHidden = !myHUD.bShowHUD;
}
/**
* Handle transitioning from one FOV to another, taking into account the aspect ratio of the screen being used
*
* @param NewFOV The new fov to change to
* @param TransitionTime How long the FOV transition should take
*/
simulated function HandleTransitionFOVAspectAdjusted(float NewFOV, float TransitionTime)
{
local float AdjustedFOV;
if( PlayerCamera != none && KFPlayerCamera(PlayerCamera) != none )
{
AdjustedFOV = CalcFOVForAspectRatio(NewFOV, myHUD.SizeX, myHUD.SizeY, UnmodifiedFOV);
KFPlayerCamera(PlayerCamera).TransitionFOV(AdjustedFOV,TransitionTime);
KFPlayerCamera(PlayerCamera).SetUnmodifiedFOV(UnmodifiedFOV);
}
}
/**
* Handle transitioning from one FOV to another, ignoring the aspect ratio of the screen being used
*
* @param NewFOV The new fov to change to
* @param TransitionTime How long the FOV transition should take
*/
simulated function HandleTransitionFOV(float NewFOV, float TransitionTime)
{
if( PlayerCamera != none && KFPlayerCamera(PlayerCamera) != none )
{
KFPlayerCamera(PlayerCamera).TransitionFOV(NewFOV,TransitionTime);
}
}
/**
* Called by the hud when the resolution changes, handles setting the proper FOV
* values for the player and weapon based on the aspect ratio
* @param NewSizeX the X resolution of the screen
* @param NewSizeY the Y resolution of the screen
*/
simulated function NotifyResolutionChanged(float NewSizeX, float NewSizeY)
{
local float AspectRatio;
local float NewFOV;
local float UsedPlayerIronSightFOV;
local KFWeapon KFWeap;
AspectRatio = NewSizeX / NewSizeY;
//1.6 = 16/10 which is 16:10 ratio and 16:9 comes to 1.77
NewFOV = CalcFOVForAspectRatio(PlayerCamera.default.DefaultFOV, NewSizeX, NewSizeY, UnmodifiedFOV);
//FocusFOV = CalcFOVForAspectRatio(default.FocusFOV, NewSizeX, NewSizeY);
// 16X9
if( AspectRatio >= 1.77 )
{
//`log("Detected 16X10 or greater: "$(NewSizeX / NewSizeY));
}
else if( AspectRatio >= 1.70 )
{
//`log("Detected 16X9: "$(NewSizeX / NewSizeY));
}
else
{
//`log("Detected 4X3: "$(NewSizeX / NewSizeY));
}
//`log("Current FOV = "$DefaultFOV$" Default FOV = "$PlayerCamera.default.DefaultFOV$", NewFOV = "$NewFOV);
DefaultFOV = NewFOV;
PlayerCamera.DefaultFOV = NewFOV;
// Initialize the FOV of all the weapons the player is carrying
if( Pawn != none && KFInventoryManager(Pawn.InvManager) != none )
{
KFInventoryManager(Pawn.InvManager).InitFOV(NewSizeX, NewSizeY, DefaultFOV);
}
// Set the FOV to the correct aspect ratio adjusted FOV
if( PlayerCamera != none && KFPlayerCamera(PlayerCamera) != none )
{
if( Pawn != none )
{
KFWeap = KFWeapon(Pawn.Weapon);
}
//if( bIsFocusing && (!bNoControlledBreathingZoom || (ROWeap != none && ROWeap.OverrideAllowFocusZoom()) ) )
//{
// ROPlayerCamera(PlayerCamera).TransitionFOV(FocusFOV,0.0);
//}
/*else*/ if( KFWeap != none && KFWeap.bUsingSights )
{
UsedPlayerIronSightFOV = KFWeap.PlayerIronSightFOV;
KFPlayerCamera(PlayerCamera).TransitionFOV(UsedPlayerIronSightFOV,0.0);
}
else
{
KFPlayerCamera(PlayerCamera).TransitionFOV(DefaultFOV,0.0);
}
KFPlayerCamera(PlayerCamera).SetUnmodifiedFOV(UnmodifiedFOV);
}
}
/**
* For a given 16/9 based FOV, give the proper FOV for an aspect ratio
* @param OriginalFOV the original FOV in 4/3 ratio that we need to adjust
* @param NewSizeX the X resolution of the screen
* @param NewSizeY the Y resolution of the screen
* @return The FOV value adjusted for the current aspect ratio
*/
static function float CalcFOVForAspectRatio(float OriginalFOV, float SizeX, float SizeY, out float OutUnmodifiedFOV)
{
local float AspectRatio;
local float OriginalAspectRatio;
local float NewFOV;
// TW SRS - Added support for Neo Checkerboard resolution
if( SizeY > 0 )
{
if( class'WorldInfo'.static.IsNeoCheckerboardRendering())
{
// FOV for checkerboard rendering
AspectRatio = (0.5*SizeX) / SizeY;
OriginalAspectRatio = 16/9;
NewFOV = (ATan2((Tan((OriginalFOV*Pi)/360.0)*(AspectRatio/OriginalAspectRatio)),1)*360.0)/Pi;
// Unmodified FOV for camera effects
AspectRatio = SizeX / SizeY;
OutUnmodifiedFOV = (ATan2((Tan((OriginalFOV*Pi)/360.0)*(AspectRatio/OriginalAspectRatio)),1)*360.0)/Pi;
}
else
{
AspectRatio = SizeX / SizeY;
OriginalAspectRatio = 16/9;
NewFOV = (ATan2((Tan((OriginalFOV*Pi)/360.0)*(AspectRatio/OriginalAspectRatio)),1)*360.0)/Pi;
OutUnmodifiedFOV = NewFOV;
}
}
// TW SRS - End support for Neo Checkerboard resolution
if ( NewFOV == 0 )
{
// aladenberger 5/11/5012 - Detected FOV calc bug
// Problem: SizeX = 0 is being used to reinitialize HUD widgets
// Solution: Call this function again HUD has been initalized, or avoid SizeX = 0 and use the new bRefreshHUDWidgets flag instead
`log("*** BAD FOV CALC DETECTED ***"@GetFuncName()@"SizeX="$SizeX@"SizeY="$SizeY);
}
//`log("OriginalFOV "$OriginalFOV$" NewFOV "$NewFOV);
return NewFOV;
}
/**
* Overridden to set the proper FOV for widescreen
*/
function FixFOV()
{
if( myHud != none )
{
if( PlayerCamera != none )
{
FOVAngle = CalcFOVForAspectRatio(PlayerCamera.default.DefaultFOV, myHUD.SizeX, myHUD.SizeY, UnmodifiedFOV);
DesiredFOV = FOVAngle;
DefaultFOV = FOVAngle;
}
else
{
FOVAngle = CalcFOVForAspectRatio(default.DefaultFOV, myHUD.SizeX, myHUD.SizeY, UnmodifiedFOV);
DesiredFOV = FOVAngle;
DefaultFOV = FOVAngle;
}
}
else
{
FOVAngle = PlayerCamera.default.DefaultFOV;
DesiredFOV = PlayerCamera.default.DefaultFOV;
DefaultFOV = PlayerCamera.default.DefaultFOV;
}
if( myHud != none && KFPlayerCamera(PlayerCamera) != none )
{
KFPlayerCamera(PlayerCamera).TransitionFOV( DefaultFOV, 0.0f );
KFPlayerCamera(PlayerCamera).SetUnmodifiedFOV( UnmodifiedFOV );
}
}
function ResetFOV()
{
super.ResetFOV();
if( KFPlayerCamera(PlayerCamera) != none )
{
KFPlayerCamera(PlayerCamera).TransitionFOV( PlayerCamera.DefaultFOV, 0.0f );
}
}
exec function FOV(float F)
{
`if(`notdefined(ShippingPC))
super.FOV(F);
`endif
}
/**
* Replicated function to set camera style on client
*
* @param NewCamMode, name defining the new camera mode
*/
reliable client function ClientSetCameraMode( name NewCamMode )
{
local KFPawn KFP;
local KFInterface_MonsterBoss KFBoss;
// Debugging - Show/Hide the player model using exec Camera()
KFP = KFPawn(ViewTarget);
if( KFP != None )
{
KFP.SetMeshVisibility(NewCamMode != 'FirstPerson');
// Disable aim offset in fixed camera (for anim debugging)
KFP.bEnableAimOffset = (NewCamMode != 'Fixed');
}
if( NewCamMode == 'Boss' )
{
KFBoss = GetBoss();
// If our nightvision is active... disable it
SetNightVision(false);
// If our current view target is not a boss, make sure we find one
if( ViewTarget != KFBoss.GetMonsterPawn())
{
SetViewTarget(KFBoss.GetMonsterPawn());
}
if(MyGFxHUD != none)
{
MyGFxHUD.ShowVoiceComms(false);
}
//hide interaction widget
ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', IMT_None );
}
else
{
// if the "BossHealthBar" is actually the escort health bar, don't hide the nameplate (when doing things like emoting)
if (MyGFxHUD == none || MyGFxHUD.BossHealthBar == none || MyGFxHUD.BossHealthBar.EscortPawn == none)
{
HideBossNameplate();
}
// Apply the same camera offset that Camera.uc uses to avoid popping
if( NewCamMode == 'FreeCam' )
{
if( PlayerCamera != none && PlayerCamera.CameraStyle != NewCamMode )
{
MoveToAdjustedFreeCamPosition();
}
}
else if( NewCamMode == 'FirstPerson' && !PlayerReplicationInfo.bIsSpectator )
{
// If are not in spectating mode and have a pawn to view, view it
if( Pawn != none && ViewTarget != Pawn)
{
SetViewTarget( Pawn );
}
}
if( IsSpectating() && ViewTarget != none && (ViewTarget != Pawn || (KFPawn_Customization(Pawn) == none && !Pawn.IsAliveAndWell())) )
{
NotifyChangeSpectateViewTarget();
}
}
if(MyGFxHUD != none && MyGFxHUD.SpectatorInfoWidget != none)
{
if( NewCamMode == 'FirstPerson' && ViewTarget == self )
{
MyGFxHUD.SpectatorInfoWidget.SetSpectatedKFPRI( none );
}
}
if ( PlayerCamera != None )
{
PlayerCamera.CameraStyle = NewCamMode;
}
}
function bool IsBossCameraMode()
{
if ( PlayerCamera != None && PlayerCamera.CameraStyle == 'Boss' )
{
return true;
}
return false;
}
function bool IsEmoteCameraMode()
{
if ( PlayerCamera != None && PlayerCamera.CameraStyle == 'Emote' )
{
return true;
}
return false;
}
function KFInterface_MonsterBoss GetBoss()
{
local KFPawn_Monster KFBoss;
foreach WorldInfo.AllPawns( class'KFPawn_Monster', KFBoss )
{
if (KFInterface_MonsterBoss(KFBoss) != none)
{
return KFInterface_MonsterBoss(KFBoss);
}
}
return none;
}
/**
* Set new camera mode
*
* @param NewCamMode, new camera mode.
*/
function SetCameraMode( name NewCamMode )
{
if ( PlayerCamera != None && PlayerCamera.CameraStyle != NewCamMode )
{
// Apply the same camera offset that Camera.uc uses to avoid popping
if( NewCamMode == 'FreeCam' )
{
MoveToAdjustedFreeCamPosition();
}
if ( WorldInfo.Role == ROLE_Authority )
{
ClientSetCameraMode( NewCamMode );
}
PlayerCamera.CameraStyle = NewCamMode;
}
}
function MoveToAdjustedFreeCamPosition()
{
local vector Loc, Pos, HitLocation, HitNormal;
local rotator Rot;
Loc = Location;
Loc += PlayerCamera.FreeCamOffset >> Rotation;
Rot = PlayerCamera.CameraCache.POV.Rotation;
Rot.Roll = 0;
Pos = Loc + Vector( Rot ) * PlayerCamera.FreeCamDistance;
// Make sure we're not in geometry
Trace( HitLocation, HitNormal, Pos, Loc, false, vect(12,12,12) );
SetLocation( IsZero(HitLocation) ? Pos : HitLocation );
SetRotation( Rot );
}
/** LandingShake()
returns true if controller wants landing view shake
*/
simulated function bool LandingShake()
{
// KF2TODO: If we want to be able to disable this in config add this UDK setting back in
//return bLandingShake;
return true;
}
/**
* Processes the player's ViewRotation
* adds delta rot (player input), applies any limits and post-processing
* returns the final ViewRotation set on PlayerController
* Overriden to add free-aim, recoil and sway
*
* @param DeltaTime, time since last frame
* @param ViewRotation, current player ViewRotation
* @param DeltaRot, player input added to ViewRotation
*/
function ProcessViewRotation( float DeltaTime, out Rotator out_ViewRotation, Rotator DeltaRot )
{
if( Pawn != none && KFWeapon(Pawn.Weapon) != none )
{
KFWeapon(Pawn.Weapon).WeaponProcessViewRotation(self, DeltaTime, DeltaRot);
}
super.ProcessViewRotation( DeltaTime, out_ViewRotation, DeltaRot );
}
simulated function SetBossCamera( KFInterface_MonsterBoss Boss )
{
// If our view target has been obliterated, the camera will default to view the player controller.
// So, put the player controller where the view target was.
if( Boss != none && Boss.GetMonsterPawn().HitFxInfo.bObliterated )
{
SetLocation( Boss.GetMonsterPawn().Location );
}
SetViewTarget( Boss.GetMonsterPawn() );
if( Role == ROLE_Authority && !IsLocalPlayerController() )
{
PlayerCamera.CameraStyle = 'Boss';
}
else
{
ClientSetCameraMode( 'Boss' );
}
}
// overridden because to view a dying boss as a client
event ResetCameraMode()
{
if( PlayerCamera != none && PlayerCamera.CameraStyle != 'Boss')
{
super.ResetCameraMode();
}
}
/**
* Simulated function to enable depth of field on client
*
* @param bEnableDOD, boolean to enable/disable DOF
* @param StaticDOFDistance, optional parameter specifying static focal distance
*/
simulated function EnableDepthOfField(bool bEnableDOF, optional float StaticDOFDistance, optional float Aperture)
{
DOFStaticFocusDepth = StaticDOFDistance;
DOFFocalAperture = Aperture;
bDOFEnabled = bEnableDOF;
}
/** Functions to set depth field settings on the client */
unreliable client function ClientEnableDepthOfField( bool bEnableDOF, optional float StaticDOFDistance, optional float Aperture, optional float FocusBlendRate )
{
EnableDepthOfField( bEnableDOF, StaticDOFDistance, Aperture );
DOFFocusBlendRate = FocusBlendRate;
}
unreliable client function ClientCustomDepthOfField( bool bEnableDOF,
optional float FocalDistance=1200.f,
optional float FocalRadius=1200.f,
optional float SharpRadius=1000.f,
optional float MinBlurSize=0.0f,
optional float MaxNearBlurSize=4.0f,
optional float MaxFarBlurSize=3.0f,
optional float ExpFalloff=1.0f,
optional float BlendInSpeed=1.0f,
optional float BlendOutSpeed=1.0f )
{
bGamePlayDOFActive = bEnableDOF;
DOF_GP_FocalDistance = FocalDistance;
DOF_GP_FocalRadius = FocalRadius;
DOF_GP_SharpRadius = SharpRadius;
DOF_GP_MinBlurSize = MinBlurSize;
DOF_GP_MaxNearBlurSize = MaxNearBlurSize;
DOF_GP_MaxFarBlurSize = MaxFarBlurSize;
DOF_GP_ExpFalloff = ExpFalloff;
DOF_GP_BlendInSpeed = BlendInSpeed;
DOF_GP_BlendOutSpeed = BlendOutSpeed;
}
/**
* Simulated function to enable or disable full-screen blur on client
*/
simulated function EnableBlur(bool bEnableBlur, float BlurAmount, float InSpeed, float OutSpeed)
{
bBlurEnabled = bEnableBlur;
if(bBlurEnabled)
{
BlurStrength = BlurAmount;
}
BlurBlendInSpeed = InSpeed;
BlurBlendOutSpeed = OutSpeed;
}
/**
* Simulated function to enable/disable IronSights (for DOF)
*/
simulated function EnableIronSights(bool bEnableIronSights)
{
bIronSightsDOFActive = bEnableIronSights;
}
/**
* Toggle screen space reflections
*/
simulated function EnableReflections(bool bEnabled)
{
bReflectionsEnabled = bEnabled;
}
/**
* Change Camera mode
*
* @param New camera mode to set
*/
exec function Camera( name NewMode )
{
`if(`notdefined(ShippingPC))
super.Camera( NewMode );
`else
if(StatsWrite != none && StatsWrite.HasCheated())
{
super.Camera( NewMode );
}
`endif
}
exec function ResetCustomizationCamera ()
{
if ( KFPlayerCamera( PlayerCamera ) != none )
{
KFPlayerCamera( PlayerCamera ).CustomizationCam.bInitialize = false;
KFPlayerCamera( PlayerCamera ).CustomizationCam.SetBodyView( 0 );
}
}
/** Play Camera Shake */
unreliable client function ClientPlayCameraShake( CameraShake Shake, optional float Scale=1.f, optional bool bTryForceFeedback, optional ECameraAnimPlaySpace PlaySpace=CAPS_CameraLocal, optional rotator UserPlaySpaceRot )
{
if (Pawn == none || !Pawn.IsAliveAndWell())
{
return;
}
if (bTryForceFeedback)
{
DoForceFeedbackForScreenShake(Shake, Scale);
}
if (PlayerCamera != None)
{
PlayerCamera.PlayCameraShake(Shake, Scale, PlaySpace, UserPlaySpaceRot);
}
}
/** Plays force feedback associated with shake, if available. Otherwise, plays default force feedback. */
simulated protected function DoForceFeedbackForScreenShake( CameraShake ShakeData, float Scale )
{
local KFCameraShake KFCS;
local int ShakeIdx, ShakeLevel;
local float RotMag, LocMag, FOVMag;
// Don't rumble at all if scale is zero
if( Scale == 0.f )
{
return;
}
// Respect the bSingleInstance setting in camera shakes when applying their force feedback
if( ShakeData.bSingleInstance && PlayerCamera != none && PlayerCamera.CameraShakeCamMod != none )
{
ShakeIdx = PlayerCamera.CameraShakeCamMod.ActiveShakes.Find( 'SourceShakeName', ShakeData.Name );
if( ShakeIdx != INDEX_NONE
&& !PlayerCamera.CameraShakeCamMod.ActiveShakes[ShakeIdx].bBlendingOut
&& PlayerCamera.CameraShakeCamMod.ActiveShakes[ShakeIdx].OscillatorTimeRemaining > 0.f )
{
return;
}
}
KFCS = KFCameraShake(ShakeData);
if( KFCS == none || KFCS.FFWaveform == none )
{
if( ShakeData != none )
{
// figure out if we're "big", "medium", or nothing
RotMag = ShakeData.GetRotOscillationMagnitude() * Scale;
if( RotMag > 40.f )
{
ShakeLevel = 2;
}
else if( RotMag > 20.f )
{
ShakeLevel = 1;
}
if( ShakeLevel < 2 )
{
LocMag = ShakeData.GetLocOscillationMagnitude() * Scale;
if( LocMag > 10.f )
{
ShakeLevel = 2;
}
else if( LocMag > 5.f )
{
ShakeLevel = 1;
}
FOVMag = ShakeData.FOVOscillation.Amplitude * Scale;
if( ShakeLevel < 2 )
{
if( FOVMag > 5.f )
{
ShakeLevel = 2;
}
else if( FOVMag > 2.f )
{
ShakeLevel = 1;
}
}
}
//`log("level="@ShakeLevel@"rotmag"@VSize(ShakeData.RotAmplitude)@"locmag"@VSize(ShakeData.LocAmplitude)@"fovmag"@ShakeData.FOVAmplitude);
if( ShakeLevel == 2 )
{
if( ShakeData.OscillationDuration <= 0.5f )
{
ClientPlayForceFeedbackWaveform(class'KFGameWaveForms'.default.CameraShakeBigVeryShort);
}
else if( ShakeData.OscillationDuration <= 1 )
{
ClientPlayForceFeedbackWaveform(class'KFGameWaveForms'.default.CameraShakeBigShort);
}
else
{
ClientPlayForceFeedbackWaveform(class'KFGameWaveForms'.default.CameraShakeBigLong);
}
}
else if( ShakeLevel == 1 )
{
if( ShakeData.OscillationDuration <= 0.5f )
{
ClientPlayForceFeedbackWaveform(class'KFGameWaveForms'.default.CameraShakeMediumVeryShort);
}
else if( ShakeData.OscillationDuration <= 1 )
{
ClientPlayForceFeedbackWaveform(class'KFGameWaveForms'.default.CameraShakeMediumShort);
}
else
{
ClientPlayForceFeedbackWaveform(class'KFGameWaveForms'.default.CameraShakeMediumLong);
}
}
}
}
else
{
ClientPlayForceFeedbackWaveform( KFCameraShake(ShakeData).FFWaveform );
}
}
/*********************************************************************************************
* @name Admin controls (Based on UTPlayerController)
********************************************************************************************/
function bool AdminCmdOk()
{
//If we are the server then commands are ok
if (WorldInfo.NetMode == NM_ListenServer && LocalPlayer(Player) != None)
{
return true;
}
if (WorldInfo.TimeSeconds < NextAdminCmdTime)
{
return false;
}
NextAdminCmdTime = WorldInfo.TimeSeconds + 5.0;
return true;
}
exec function AdminLogin(string Password)
{
if (Password != "" && AdminCmdOk() )
{
ServerAdminLogin(Password);
}
}
reliable server private function ServerAdminLogin(string Password)
{
if ( (WorldInfo.Game.AccessControl != none) && AdminCmdOk() )
{
if ( WorldInfo.Game.AccessControl.AdminLogin(self, Password) )
{
WorldInfo.Game.AccessControl.AdminEntered(Self);
}
}
}
exec function AdminLogOut()
{
if ( AdminCmdOk() )
{
ServerAdminLogOut();
}
}
reliable server private function ServerAdminLogOut()
{
if ( WorldInfo.Game.AccessControl != none )
{
if ( WorldInfo.Game.AccessControl.AdminLogOut(self) )
{
WorldInfo.Game.AccessControl.AdminExited(Self);
}
}
}
// Execute an administrative console command on the server.
exec function Admin( string CommandLine )
{
if (PlayerReplicationInfo.bAdmin)
{
ServerAdmin(CommandLine);
}
}
exec function Kick(string S)
{
if (!PlayerReplicationInfo.bAdmin)
{
`log("Must be admin to kick player");
return;
}
ServerKick(S);
}
exec function KickBan(string S)
{
if (!PlayerReplicationInfo.bAdmin)
{
`log("Must be admin to KickBan player");
return;
}
ServerKickBan(S);
}
reliable server function ServerKickBan(string S)
{
if (!PlayerReplicationInfo.bAdmin)
{
`log(PlayerReplicationInfo.PlayerName$" attempted to kick-ban without being admin (probably cheating)");
return;
}
WorldInfo.Game.KickBan(S);
}
reliable server function ServerKick(string S)
{
if (!PlayerReplicationInfo.bAdmin)
{
`log(PlayerReplicationInfo.PlayerName$" attempted to kick without being admin (probably cheating)");
return;
}
WorldInfo.Game.Kick(S);
}
reliable server private function ServerAdmin( string CommandLine )
{
local string Result;
if ( PlayerReplicationInfo.bAdmin )
{
Result = ConsoleCommand( CommandLine );
if( Result!="" )
ClientMessage( Result );
}
}
reliable client event ClientWasKicked()
{
if (IsPrimaryPlayer())
{
ClientSetProgressMessage(PMT_ConnectionFailure, class'KFLocalMessage'.default.KickedFromServerString, Localize("Errors", "ConnectionFailed_Title", "Engine"));
ConsoleCommand("DISCONNECT TWFORCED");
}
}
/*********************************************************************************************
* @name Weapon Handling
********************************************************************************************* */
/** Almost exactly like Controller::PickTarget(), but callable clientside */
private native function Pawn PickAimAtTarget(out float bestAim, out float bestDist, vector FireDir, vector projStart, float MaxRange, optional bool bTargetTeammates = False);
/** Accessor for PickAimAtTarget */
simulated final function Pawn GetPickedAimAtTarget(out float bestAim, out float bestDist, vector FireDir, vector projStart, float MaxRange, optional bool bTargetTeammates = False)
{
return PickAimAtTarget(bestAim, bestDist, FireDir, projStart, MaxRange, bTargetTeammates);
}
exec function SwitchToBestWeapon(optional bool bForceNewWeapon)
{
// Don't let players use an exec to bring up a weapon if the weapon prevents it
if( Pawn != none && Pawn.Weapon != none && KFWeapon(Pawn.Weapon) != none )
{
if( !KFWeapon(Pawn.Weapon).CanSwitchWeapons() )
{
return;
}
}
super.SwitchToBestWeapon(bForceNewWeapon);
}
/**
* Adjusts weapon aiming direction.
* Gives controller a chance to modify the aiming of the pawn. For example aim error, auto aiming, adhesion, AI help...
* Requested by weapon prior to firing.
*
* Completely overrides super to support free-aim hip shots going where the muzzle is pointed (modified block is marked)
*
* @param W, weapon about to fire
* @param StartFireLoc, world location of weapon fire start trace, or projectile spawn loc.
* @param BaseAimRot, original aiming rotation without any modifications.
*/
function Rotator GetAdjustedAimFor( Weapon W, vector StartFireLoc )
{
local vector FireDir, HitLocation, HitNormal;
local actor BestTarget, HitActor;
local float bestAim, bestDist;
local bool bNoAimCorrection, bInstantHit;
local rotator BaseAimRot;
bInstantHit = ( W == None || W.bInstantHit );
BaseAimRot = (Pawn != None) ? Pawn.GetBaseAimRotation() : Rotation;
if( W != none )
{
// Add in the weapon buffer rotation for recoil/sway
BaseAimRot += WeaponBufferRotation;
}
// Clients are done after WeaponBufferRotation unless aim correction is on
if( Role < ROLE_Authority && !AimingHelp(bInstantHit) )
{
return BaseAimRot;
}
FireDir = vector(BaseAimRot);
HitActor = Trace(HitLocation, HitNormal, StartFireLoc + W.GetTraceRange() * FireDir, StartFireLoc, true);
if ( (HitActor != None) && HitActor.bProjTarget )
{
BestTarget = HitActor;
bNoAimCorrection = true;
BestDist = VSize(BestTarget.Location - Pawn.Location);
}
else
{
// adjust aim based on FOV
bestAim = 0.90;
if ( AimingHelp(bInstantHit) )
{
bestAim = AimHelpDot(bInstantHit);
}
else if ( bInstantHit )
bestAim = 1.0;
BestTarget = PickAimAtTarget(bestAim, bestDist, FireDir, StartFireLoc, W.WeaponRange);
if ( BestTarget == None )
{
return BaseAimRot;
}
}
ShotTarget = Pawn(BestTarget);
if ( !AimingHelp(bInstantHit) )
{
return BaseAimRot;
}
if ( !bNoAimCorrection && W != None )
{
ProcessAimCorrection(ShotTarget, KFWeapon(W), StartFireLoc, BaseAimRot);
}
return BaseAimRot;
}
/** Aim correction for near headshot misses */
function ProcessAimCorrection(Pawn Target, KFWeapon W, vector StartLoc, out rotator AimRot)
{
local vector AimLoc, TargetLoc, HeadLoc;
local vector2d Offset;
local float Distance, AimCorrection;
local KFPawn KFP;
if ( W == None || W.AimCorrectionSize <= 0 )
{
return;
}
HeadLoc = Target.Mesh.GetBoneLocation('head');
Distance = VSize(HeadLoc - StartLoc);
if ( IsZero(HeadLoc) || Distance > MaxAimCorrectionDistance )
{
return;
}
AimLoc = StartLoc + vector(AimRot) * Distance;
// Calculate the aim friction multiplier
// Horizontal component
TargetLoc = HeadLoc;
TargetLoc.Z = AimLoc.Z;
Offset.X = PointDistToLine( AimLoc, (TargetLoc - StartLoc), StartLoc );
// Vertical component
TargetLoc = HeadLoc;
TargetLoc.X = AimLoc.X;
TargetLoc.Y = AimLoc.Y;
Offset.Y = PointDistToLine( AimLoc, (TargetLoc - StartLoc), StartLoc );
// calculate size of aim correction offset
AimCorrection = FMin(W.AimCorrectionSize, MAX_AIM_CORRECTION_SIZE);
if ( `IsInZedTime(self) )
{
AimCorrection *= 0.5f;
}
if ( Offset.X <= AimCorrection && Offset.Y <= AimCorrection )
{
// Skip headless. We could try a new target behind this one, but it would affect
// the ShotTarget and in actual gameplay this turns out to be not very useful.
KFP = KFPawn(Target);
if ( KFP != None && KFP.IsHeadless() )
{
//`log("Aim correction targeted a headless zed");
return;
}
//`log("Aim correction triggered - Slomo="$`IsInZedTime(self)@"["$Offset.X$", "$Offset.Y$"]");
AimRot = rotator(HeadLoc - StartLoc);
}
}
/** Like ServerThrowWeapon, but can specify the weapon */
reliable server function ServerThrowOtherWeapon(Weapon W)
{
if ( W != None && W.Instigator == Pawn && W.CanThrow() )
{
Pawn.TossInventory(W);
}
}
event TriggerWeaponContentLoad(class<KFWeapon> WeaponClass)
{
ClientTriggerWeaponContentLoad(WeaponClass);
}
reliable client function ClientTriggerWeaponContentLoad(class<KFWeapon> WeaponClass)
{
if (WeaponClass != none)
{
WeaponClass.static.TriggerAsyncContentLoad(WeaponClass);
}
}
simulated event OnWeaponAsyncContentLoaded(class<KFWeapon> WeaponClass)
{
// This event is called when content is done loading via TriggerAsyncContentLoad.
local KFPawn_Human KFPH;
local KFDroppedPickup KFDP;
// Attempt to set the weapon attachment for any player than might need theirs set. This is a backup
// for when content isn't quite ready when WeaponClassForAttachmentTemplate is replicated.
foreach WorldInfo.Allpawns(class'KFPawn_Human', KFPH)
{
if (WeaponClass == KFPH.WeaponClassForAttachmentTemplate)
{
KFPH.SetWeaponAttachmentFromWeaponClass(WeaponClass);
}
}
// The pickup's mesh is set on the client when it receives its InventoryClass via replication. If
// that happens before the content is done loading, we need to make sure to set the pickup's mesh
// after loading completes. This is probably a very unusual edge case.
foreach WorldInfo.AllActors(class'KFDroppedPickup', KFDP)
{
if (WeaponClass == KFDP.InventoryClass && KFDP.MyMeshComp == none)
{
KFDP.SetPickupMesh(WeaponClass.default.DroppedPickupMesh);
}
}
}
/*********************************************************************************************
* @name Player Movement
********************************************************************************************* */
// Player movement.
// Player Standing, walking, running, falling.
state PlayerWalking
{
ignores SeePlayer, HearNoise, Bump;
/** Overridden to set Pawn.MovementSpeedModifier based on controller input */
function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
{
local float MinMoveScale, MaxMoveScale;
local InterpCurveFloat MoveSensitivityCurve;
super.ProcessMove( DeltaTime, NewAccel, DoubleClickMove, DeltaRot );
// DoubleClickMove now stores an approximation of movement scale, so lerp between min and max speed scalars
MoveSensitivityCurve = class'KFPlayerInput'.default.MoveSensitivityScaleCurve;
MinMoveScale = MoveSensitivityCurve.Points[0].OutVal;
MaxMoveScale = MoveSensitivityCurve.Points[ MoveSensitivityCurve.Points.Length - 1 ].OutVal;
Pawn.MovementSpeedModifier = Lerp( MaxMoveScale, MinMoveScale, float(DoubleClickMove) / float(EAnalogMovementSpeed.AMOVESPEED_Max) );
}
/** Overridden to reset Pawn.MovementSpeedModifier */
function EndState( name NextStateName )
{
super.EndState( NextStateName );
if ( Pawn != None )
{
Pawn.MovementSpeedModifier = 1.f;
}
}
}
/* HandleWalking:
Called by PlayerController and PlayerInput to set bIsWalking flag, affecting Pawn's velocity */
function HandleWalking()
{
local bool bShouldSprint;
local KFPlayerInput KFInput;
if (Pawn != None)
{
// In KF2, the weapon determines our walking state
if (Pawn.Weapon != None)
{
Pawn.SetWalking(Pawn.Weapon.ShouldOwnerWalk());
}
else
{
Pawn.SetWalking(false);
}
bShouldSprint = (bRun != 0 && !IsZero(Pawn.Acceleration));
if ( bShouldSprint )
{
bDuck = 0; // sprint cancels crouch
}
if(bRun != 0 && IsZero(Pawn.Velocity))
{
// when a player stops moving with toggle to sprint on
// turn off sprinting so that they have to press the toggle again
KFInput = KFPlayerInput(PlayerInput);
if (KFInput != none && KFInput.bToggleToRun)
{
bRun = 0;
}
}
KFPawn(Pawn).SetSprinting(bShouldSprint);
}
}
/* CheckJumpOrDuck()
Called by ProcessMove()
handle jump and duck buttons which are pressed
*/
function CheckJumpOrDuck()
{
if ( Pawn == None )
{
return;
}
if ( bPressedJump )
{
Pawn.DoJump( bUpdating );
}
if ( Pawn.Physics != PHYS_Falling && Pawn.bCanCrouch )
{
// player is set to crouch and isn't running or is not moving
Pawn.ShouldCrouch(bDuck != 0 && (bRun == 0 || VSize(Velocity) == 0));
}
}
/** Disable movement on the local player for short time. */
function PauseMoveInput(float PauseTime=0.50)
{
if ( IsLocalPlayerController() && PauseTime > 0.f && PauseMoveInputTimeLeft == 0.f )
{
IgnoreMoveInput(TRUE);
PauseMoveInputTimeLeft = PauseTime;
}
}
/** Make sure our movement is re-enabled shortly after (in realtime seconds) */
function TickPauseMoveInput(float DeltaTime)
{
local float RealDeltaTime;
if ( PauseMoveInputTimeLeft > 0.f )
{
RealDeltaTime = (DeltaTime / WorldInfo.TimeDilation);
PauseMoveInputTimeLeft -= RealDeltaTime;
if ( PauseMoveInputTimeLeft <= 0 )
{
IgnoreMoveInput(FALSE);
PauseMoveInputTimeLeft = 0.f;
}
}
}
/* ServerMove()
- replicated function sent by client to server - contains client movement and firing info.
overriden to support replicating free aim rotation - Ramm
*/
unreliable server function ServerMove
(
float TimeStamp,
vector InAccel,
vector ClientLoc,
byte MoveFlags,
byte ClientRoll,
int View,
optional int FreeAimRot
)
{
local rotator NewFreeAimRot;
local int FreeAimPitch, FreeAimYaw;
// Unpack the free-aim rotation vars and set the rotation on the pawns weapon
if( Pawn != none && Pawn.Weapon != none )
{
// Free-aim vars
FreeAimPitch = (FreeAimRot & 65535);
FreeAimYaw = (FreeAimRot >> 16);
NewFreeAimRot.Pitch = FreeAimPitch;
NewFreeAimRot.Yaw = FreeAimYaw;
WeaponBufferRotation = NewFreeAimRot;
}
Super.ServerMove(TimeStamp, InAccel, ClientLoc, MoveFlags, ClientRoll, View, FreeAimRot);
}
/* DualServerMove()
- replicated function sent by client to server - contains client movement and firing info for two moves
overriden to support replicating free aim rotation - Ramm
*/
unreliable server function DualServerMove
(
float TimeStamp0,
vector InAccel0,
byte PendingFlags,
int View0,
float TimeStamp,
vector InAccel,
vector ClientLoc,
byte NewFlags,
byte ClientRoll,
int View,
optional int FreeAimRot0,
optional int FreeAimRot
)
{
ServerMove(TimeStamp0,InAccel0,vect(1,2,3),PendingFlags,ClientRoll,View0,FreeAimRot0);
ServerMove(TimeStamp,InAccel,ClientLoc,NewFlags,ClientRoll,View,FreeAimRot);
}
/* CallServerMove()
Call the appropriate replicated servermove() function to send a client player move to the server
overriden to support replicating free aim rotation - Ramm
*/
function CallServerMove
(
SavedMove NewMove,
vector ClientLoc,
byte ClientRoll,
int View,
SavedMove OldMove
)
{
local vector BuildAccel;
local byte OldAccelX, OldAccelY, OldAccelZ;
local int FreeAimRot;
// Pack free aim rotation before sending to server
FreeAimRot = ((WeaponBufferRotation.Yaw & 65535) << 16) + (WeaponBufferRotation.Pitch & 65535);
// compress old move if it exists
if ( OldMove != None )
{
// old move important to replicate redundantly
BuildAccel = 0.05 * OldMove.Acceleration + vect(0.5, 0.5, 0.5);
OldAccelX = CompressAccel(BuildAccel.X);
OldAccelY = CompressAccel(BuildAccel.Y);
OldAccelZ = CompressAccel(BuildAccel.Z);
OldServerMove(OldMove.TimeStamp,OldAccelX, OldAccelY, OldAccelZ, OldMove.CompressedFlags());
}
if ( PendingMove != None )
{
// send two moves simultaneously
DualServerMove
(
PendingMove.TimeStamp,
PendingMove.Acceleration * 10,
PendingMove.CompressedFlags(),
((PendingMove.Rotation.Yaw & 65535) << 16) + (PendingMove.Rotation.Pitch & 65535),
NewMove.TimeStamp,
NewMove.Acceleration * 10,
ClientLoc,
NewMove.CompressedFlags(),
ClientRoll,
View,
((PendingMove.WeaponBufferRotation.Yaw & 65535) << 16) + (PendingMove.WeaponBufferRotation.Pitch & 65535),
FreeAimRot
);
}
else
{
ServerMove
(
NewMove.TimeStamp,
NewMove.Acceleration * 10,
ClientLoc,
NewMove.CompressedFlags(),
ClientRoll,
View,
FreeAimRot
);
}
if (PlayerCamera != None && PlayerCamera.bUseClientSideCameraUpdates)
{
PlayerCamera.bShouldSendClientSideCameraUpdate = TRUE;
}
}
/** Overridden to reset MaxResponseTime every call so we don't use an invalid MaxResponseTime.
On the server, MaxResponseTime used to be set only in PostBeginPlay, taking time dilation into account but not being updated when time dilation changes.
*/
function float GetServerMoveDeltaTime(float TimeStamp)
{
MaxResponseTime = Default.MaxResponseTime * WorldInfo.TimeDilation;
return Super.GetServerMoveDeltaTime( TimeStamp );
}
/*********************************************************************************************
* @name Aim Assist
********************************************************************************************* */
/**
* Toggle ForceLookAt (e.g. while grappled)
* Network: Local Player
*/
function SetForceLookAtPawn(KFPawn P)
{
if ( IsLocalController() )
{
if ( P == None )
{
ForceLookAtPawn = None;
bLockToForceLookAtPawn = false;
}
else
{
ForceLookAtPawn = P;
bLockToForceLookAtPawn = true;
}
}
}
simulated function StartRotationAdjustment(InterpCurveFloat RotationCurve, float MaxTime)
{
CurrentRotationAdjustmentTime = 0.0f;
RotationAdjustmentCurve = RotationCurve;
MaxRotationAdjustmentTime = MaxTime;
SetTimer(RotationAdjustmentInterval, true, nameof(Timer_RotationAdjustment));
}
simulated function Timer_RotationAdjustment()
{
CurrentRotationAdjustmentTime += RotationAdjustmentInterval;
if (CurrentRotationAdjustmentTime / MaxRotationAdjustmentTime >= 1.0f)
{
// set all the way to unlimited
RotationSpeedLimit = -1.f;
CurrentRotationAdjustmentTime = 0.0f;
ClearTimer(nameof(Timer_RotationAdjustment));
}
else
{
RotationSpeedLimit = EvalInterpCurveFloat(RotationAdjustmentCurve, CurrentRotationAdjustmentTime / MaxRotationAdjustmentTime);
}
}
/** Called from PlayerController.UpdateRotation; Takes ForceLookAtPawn, bAutoTargetEnabled, and bTargetAdhesionEnabled into account */
function ModifyUpdateRotation( float DeltaTime, out Rotator DeltaRot )
{
local KFPlayerInput KFInput;
local float LimitModifier;
// some weapons can limit how quickly the player can rotate while in certain states
KFInput = KFPlayerInput(PlayerInput);
if (RotationSpeedLimit > 0)
{
LimitModifier = Abs(DeltaRot.Yaw) > Abs(DeltaRot.Pitch) ? Abs(DeltaRot.Yaw) / RotationSpeedLimit : Abs(DeltaRot.Pitch) / RotationSpeedLimit;
if(LimitModifier > 1)
{
DeltaRot.Yaw /= LimitModifier;
DeltaRot.Pitch /= LimitModifier;
}
}
if( Pawn != none && ForceLookAtPawn != none && (ForceLookAtPawnTime >= 0 || bLockToForceLookAtPawn) )
{
if( !bLockToForceLookAtPawn )
{
ForceLookAtPawnTime -= DeltaTime;
}
KFInput.ApplyForceLookAtPawn( DeltaTime, DeltaRot.Yaw, DeltaRot.Pitch );
// The CurrentAutoTarget when we're done autotargeting it
if( !bLockToForceLookAtPawn && ForceLookAtPawnTime <= 0 )
{
ForceLookAtPawn = none;
}
}
else if( Pawn != none && KFInput.CurrentAutoTarget != none && KFInput.AutoTargetTimeLeft >= 0 )
{
KFInput.ApplyAutoTarget( DeltaTime, KFWeapon(Pawn.Weapon), DeltaRot.Yaw, DeltaRot.Pitch );
}
// NOTE: we probably only want to ApplyTargetAdhesion when we are moving as it hides the Adhesion a lot better
else if( KFInput.IsAimAssistAdhesionEnabled() && Pawn != none && (PlayerInput.aForward != 0 || PlayerInput.aStrafe != 0) )
{
KFInput.ApplyTargetAdhesion( DeltaTime, KFWeapon(Pawn.Weapon), DeltaRot.Yaw, DeltaRot.Pitch );
}
}
/**
* Start an instance of autotargeting when going to ironsights or during other actions
*/
simulated function StartAutoTargeting()
{
local KFPlayerInput KFInput;
if ( !IsLocalController() )
{
return; // LocalPlayer Only
}
if( !PlayerInput.bUsingGamepad )
{
return;
}
KFInput = KFPlayerInput(PlayerInput);
KFInput.InitAutoTarget();
}
/** Overloaded to make sure player is actually using gamepad */
function bool AimingHelp(bool bInstantHit)
{
if( PlayerInput != none
&& PlayerInput.bUsingGamepad
&& MaxAimCorrectionDistance > 0.f
&& IsLocalController() )
{
return true;
}
return false;
}
/*********************************************************************************************
* @name Use / Interact
********************************************************************************************* */
static simulated function KFInterface_Usable GetCurrentUsableActor( Pawn P, optional bool bUseOnFind=false )
{
local KFInterface_Usable UsableActor;
local Actor A, BestActor;
local KFInterface_Usable BestUsableActor;
local int InteractionIndex, BestInteractionIndex;
local KFGameReplicationInfo KFGRI;
BestInteractionIndex = -1;
if ( P != None )
{
/* On endless mode, if game is paused players can move but not interact with objects. */
KFGRI = KFGameReplicationInfo(P.WorldInfo.GRI);
if(KFGRI != none && KFGRI.bIsEndlessPaused)
{
return none;
}
/* */
// Check touching -- Useful when UsedBy() is implemented by subclass instead of kismet
ForEach P.TouchingActors(class'Actor', A)
{
UsableActor = KFInterface_Usable( A );
if ( UsableActor != none && UsableActor.GetIsUsable( P ) )
{
// find the best usable by priority
// use the usable's interaction index as priority, since the UI already sort of does that
InteractionIndex = UsableActor.GetInteractionIndex( P );
if( InteractionIndex > BestInteractionIndex )
{
BestInteractionIndex = InteractionIndex;
BestUsableActor = UsableActor;
BestActor = A;
}
}
}
if( BestUsableActor != none )
{
if( bUseOnFind )
{
BestActor.UsedBy( P );
}
return BestUsableActor;
}
}
return none;
}
/** Have to override the engine function, because it only works for kismet trigger events */
function bool TriggerInteracted()
{
// first let the base engine do it's kismet thing
if ( Super.TriggerInteracted() )
return true;
if( Pawn != none )
{
GetCurrentUsableActor( Pawn, true );
}
return false;
}
/**
* Looks at all nearby triggers, looking for any that can be
* interacted with. Duplicated from super with a few small additions
*/
function GetTriggerUseList(float interactDistanceToCheck, float crosshairDist, float minDot, bool bUsuableOnly, out array<Trigger> out_useList)
{
local int Idx;
local vector cameraLoc, cameraDir;
local rotator cameraRot;
local Trigger checkTrigger;
local SeqEvent_Used UseSeq;
local float aimEpsilon;
local KFGameReplicationInfo KFGRI;
if (Pawn == None)
{
return;
}
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if(KFGRI != none && KFGRI.bIsEndlessPaused)
{
return;
}
// search of nearby actors that have use events
foreach Pawn.CollidingActors(class'Trigger',checkTrigger,interactDistanceToCheck)
{
for (Idx = 0; Idx < checkTrigger.GeneratedEvents.Length; Idx++)
{
UseSeq = SeqEvent_Used(checkTrigger.GeneratedEvents[Idx]);
if ( UseSeq == None )
{
continue;
}
// one-time init of camera vars
if ( IsZero(cameraDir) )
{
// grab camera location/rotation for checking crosshairDist
GetPlayerViewPoint(cameraLoc, cameraRot);
cameraDir = vector(cameraRot);
}
aimEpsilon = (UseSeq.bAimToInteract) ? 0.98f : minDot;
// if bUsuableOnly is true then we must get true back from CheckActivate (which tests various validity checks on the player and on the trigger's trigger count and retrigger conditions etc)
if( ( !bUsuableOnly || (checkTrigger.GeneratedEvents[Idx].CheckActivate(checkTrigger,Pawn,true)) )
// check to see if we are within range of the trigger
&& ( VSizeSq(Pawn.Location - checkTrigger.Location) <= Square(UseSeq.InteractDistance) )
// check to see if we are looking at the object
&& ( Normal(checkTrigger.Location-cameraLoc) dot cameraDir >= aimEpsilon )
// check bUseLineCheck
&& ( !UseSeq.bUseLineCheck || FastTrace(checkTrigger.Location, cameraLoc) )
)
{
out_useList[out_useList.Length] = checkTrigger;
// don't bother searching for more events
Idx = checkTrigger.GeneratedEvents.Length;
}
}
}
}
reliable client event ReceiveLocalizedMessage( class<LocalMessage> Message, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject )
{
local string TempMessage;
// Wait for player to be up to date with replication when joining a server, before stacking up messages
if ( WorldInfo.NetMode == NM_DedicatedServer || WorldInfo.GRI == None )
return;
if( Message == class'KFLocalMessage_Game' && MyGFxHUD != none )
{
TempMessage = Class'KFLocalMessage_Game'.static.GetString(switch, true, RelatedPRI_1, RelatedPRI_2, OptionalObject);
if(TempMessage != "")
{
MyGFxHUD.ShowNonCriticalMessage( TempMessage );
if(Switch == KMT_Killed || Switch == KMT_Suicide)
{
//zed suicide
if(Switch == KMT_Suicide )
{
if(RelatedPRI_2.GetTeamNum() == 255 && RelatedPRI_2.UniqueID == PlayerReplicationInfo.UniqueID)
{
class'KFMusicStingerHelper'.static.PlayZedPlayerSuicideStinger( Self );
}
if(RelatedPRI_2.GetTeamNum() == class'KFTeamInfo_Human'.default.TeamIndex && RelatedPRI_2.GetTeamNum() == PlayerReplicationInfo.GetTeamNum())
{
if(RelatedPRI_2.UniqueID == PlayerReplicationInfo.UniqueID)
{
ReceiveLocalizedMessage( class'KFLocalMessage_Priority', GMT_Died, RelatedPRI_1, RelatedPRI_2, OptionalObject );
}
else
{
class'KFMusicStingerHelper'.static.PlayTeammateDeathStinger( Self );
}
}
}
else if(Switch == KMT_Killed)
{
//killed by AI
if(RelatedPRI_2.GetTeamNum() == class'KFTeamInfo_Human'.default.TeamIndex && RelatedPRI_2.GetTeamNum() == PlayerReplicationInfo.GetTeamNum())
{
if(RelatedPRI_2.UniqueID == PlayerReplicationInfo.UniqueID)
{
ReceiveLocalizedMessage( class'KFLocalMessage_Priority', GMT_Died, RelatedPRI_1, RelatedPRI_2, OptionalObject );
}
else
{
class'KFMusicStingerHelper'.static.PlayTeammateDeathStinger( Self );
}
}
}
MyGFxHUD.ShowKillMessage( RelatedPRI_1, RelatedPRI_2, true, OptionalObject );
}
}
if( Switch == GMT_ReceivedAmmoFrom ||
Switch == GMT_ReceivedGrenadesFrom )
{
PlayAKEvent( class'KFPerk_Support'.static.GetReceivedAmmoSound() );
}
else if( Switch == GMT_ReceivedArmorFrom )
{
PlayAKEvent( class'KFPerk_Support'.static.GetReceivedArmorSound() );
}
else if( Switch == GMT_ReceivedAmmoAndArmorFrom )
{
PlayAKEvent( class'KFPerk_Support'.static.GetReceivedAmmoAndArmorSound() );
}
}
else if(Message == class'KFLocalMessage_PlayerKills' && MyGFxHUD != none)
{
if(switch == KMT_PlayerKillPlayer || ( bShowKillTicker && switch == KMT_PLayerKillZed ) )
{
if(switch == KMT_PlayerKillPlayer)
{
//human killed
if(RelatedPRI_2.GetTeamNum() == class'KFTeamInfo_Human'.default.TeamIndex)
{
//zed killed human
if(RelatedPRI_1.GetTeamNum() == 255)
{
class'KFMusicStingerHelper'.static.PlayZedKillHumanStinger( Self );
}
else if(RelatedPRI_2.GetTeamNum() == PlayerReplicationInfo.GetTeamNum()) //in the case of friendly fire
{
if(RelatedPRI_2.UniqueID == PlayerReplicationInfo.UniqueID)
{
class'KFMusicStingerHelper'.static.PlayPlayerDiedStinger( Self );
}
else
{
class'KFMusicStingerHelper'.static.PlayTeammateDeathStinger( Self );
}
}
}
else
{
if(RelatedPRI_2.UniqueID == PlayerReplicationInfo.UniqueID)
{
//zed death
class'KFMusicStingerHelper'.static.PlayZedPlayerKilledStinger( Self );
}
}
}
MyGFxHUD.ShowKillMessage( RelatedPRI_1, RelatedPRI_2, false, OptionalObject );
}
}
else
{
super.ReceiveLocalizedMessage( Message, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject );
}
}
/** Pending interaction messages exist for cases when two interacting triggers touch eachother ;)
Only one of the touching actors lists of "TouchingActors" is properly updated. Wait a tick for both to have
correct lists */
function SetPendingInteractionMessage()
{
SetTimer( 0.01f, false, nameof(UpdatePendingInteractionMessage) );
}
function UpdatePendingInteractionMessage()
{
if( Pawn != none )
{
UpdateInteractionMessages( Pawn );
}
}
static function UpdateInteractionMessages( Actor InteractingActor )
{
local KFInterface_Usable UsableActor;
local Pawn P;
local PlayerController PC;
P = Pawn(InteractingActor);
if( P != none )
{
PC = PlayerController( P.Controller );
if( PC != none && PC.Role == ROLE_AUTHORITY )
{
UsableActor = GetCurrentUsableActor( P );
if( UsableActor != none )
{
PC.SetTimer( 1.f, true, nameof(CheckCurrentUsableActor), PC );
PC.ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', UsableActor.GetInteractionIndex( P ), none, none, UsableActor );
}
else
{
PC.ClearTimer( nameof(CheckCurrentUsableActor), PC );
PC.ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', IMT_None );
}
}
}
}
/** Checks to see if the player still needs an interaction message to be displayed.
* When used with a timer, lower priority "usable" messages can be resumed after being interrupted by higher priority messages. */
function CheckCurrentUsableActor()
{
local KFInterface_Usable UsableActor;
UsableActor = GetCurrentUsableActor( Pawn );
if( UsableActor != none )
{
ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', UsableActor.GetInteractionIndex( Pawn ) );
}
else
{
ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', IMT_None );
ClearTimer( nameof(CheckCurrentUsableActor) );
}
}
/*********************************************************************************************
* @name Gameplay Effects
********************************************************************************************* */
/**
* Called from KFPawn to let the client know it has screen effects
*/
simulated function PlayScreenHitFX(class<KFDamageType> KFDT, bool bShowPainEffect)
{
PlayScreenEffects( KFDT );
// Always performt his after we've put on screen effects
PlayScreenMaterialEffects( KFDT, bShowPainEffect );
}
/** Adds a particle effect on the lens if we have one followed by the screen material*/
simulated function PlayScreenEffects(class<KFDamageType> KFDT)
{
local class<EmitterCameraLensEffectBase> LensEffectTemplate;
local KFPerk MyKFPerk;
// First apply the lens effect
MyKFPerk = GetPerk();
if( Pawn != none && Pawn.IsFirstPerson() && MyKFPerk != none )
{
// Only turn the camera lens effect on if it is not already running to avoid popping from damage over time
if( GetEffectTimeRemaining( KFDT ) <= 0 )
{
LensEffectTemplate = MyKFPerk.GetPerkLensEffect( KFDT );
if( LensEffectTemplate != none )
{
ClientSpawnCameraLensEffect( LensEffectTemplate );
}
}
}
}
/** Plays any pain or damagetype specific screen effects */
simulated function PlayScreenMaterialEffects(class<KFDamageType> KFDT, bool bShowPainEffect)
{
// Always play a pain effect
if( bShowPainEffect )
{
PainEffectTimeRemaining = PainEffectDuration;
}
// Play any damage specific screen effects
if( KFDT.default.ScreenMaterialName != '' )
{
switch (KFDT.default.ScreenMaterialName)
{
case EffectSirenScreamParamName:
if(LEDEffectsManager != none && SirenScreamEffectTimeRemaining == 0)
{
LEDEffectsManager.PlayEffectSiren(default.SonicScreamEffectDuration);
}
SirenScreamEffectTimeRemaining = default.SonicScreamEffectDuration;
CheckForReducedSirenScreamEffect();
break;
case EffectBloatsPukeParamName:
if(LEDEffectsManager != none && BloatPukeEffectTimeRemaining == 0)
{
LEDEffectsManager.PlayEffectPuke(default.BloatPukeEffectDuration);
}
BloatPukeEffectTimeRemaining = default.BloatPukeEffectDuration;
break;
case EffectHealParamName:
if(LEDEffectsManager != none && HealEffectTimeRemaining == 0)
{
LEDEffectsManager.PlayEffectHeal(default.HealEffectDuration);
}
HealEffectTimeRemaining = default.HealEffectDuration;
break;
case EffectFlashBangParamName:
if(LEDEffectsManager != none && FlashBangEffectDuration == 0)
{
LEDEffectsManager.PlayEffectFlashbang(default.FlashBangEffectDuration);
}
FlashBangEffectTimeRemaining = default.FlashBangEffectDuration;
break;
}
}
}
simulated function PlayPowerUpEffect( class<KFPowerUp> KFPowerUp )
{
PlayScreenPowerUpEffects( KFPowerUp );
PlayScreenMaterialPowerUpEffect( KFPowerUp );
}
simulated function PlayScreenPowerUpEffects( class<KFPowerUp> KFPowerUp )
{
local class<EmitterCameraLensEffectBase> LensEffectTemplate;
if( Pawn != none && Pawn.IsFirstPerson() )
{
if( GetEffectPowerUpTimeRemaining( KFPowerUp ) <= 0 )
{
LensEffectTemplate = KFPowerUp.default.CameraLensEffectTemplate;
if( LensEffectTemplate != none )
{
ClientSpawnCameraLensEffect( LensEffectTemplate );
}
}
}
}
simulated function PlayScreenMaterialPowerUpEffect( class<KFPowerUp> KFPowerUp )
{
switch ( KFPowerUp.default.ScreenMaterialName )
{
case EffectPowerUpHellishRageParamName:
HellishRagePowerUpEffectDuration = KFPowerUp.default.PowerUpDuration;
HellishRagePowerUpEffectTimeRemaining = HellishRagePowerUpEffectDuration;
break;
}
}
simulated function StopScreenPowerUpEffects( class<KFPowerUp> KFPowerUp )
{
local class<EmitterCameraLensEffectBase> LensEffectTemplate;
if( Pawn != none && Pawn.IsFirstPerson() )
{
LensEffectTemplate = KFPowerUp.default.CameraLensEffectTemplate;
if( LensEffectTemplate != none )
{
ClientRemoveCameraLensEffect( LensEffectTemplate );
}
}
}
simulated function StopPowerUpEffect( class<KFPowerUp> KFPowerUp )
{
StopScreenPowerUpEffects( KFPowerUp );
StopScreenMaterialPowerUpEffect( KFPowerUp );
}
simulated function StopScreenMaterialPowerUpEffect( class<KFPowerUp> KFPowerUp )
{
switch ( KFPowerUp.default.ScreenMaterialName )
{
case EffectPowerUpHellishRageParamName:
HellishRagePowerUpEffectDuration = KFPowerUp.default.PowerUpDuration;
HellishRagePowerUpEffectTimeRemaining = 0.2f;
break;
}
}
simulated function CheckForReducedSirenScreamEffect()
{
local KFPerk MyPerk;
// First apply the lens effect
MyPerk = GetPerk();
if( MyPerk != none && GameplayPostProcessEffectMIC != none )
{
GameplayPostProcessEffectMIC.SetScalarParameterValue( EffectSirenScreamParamName, MyPerk.GetSirenScreamStrength() );
}
}
/** Return if this effect is currently running */
simulated function float GetEffectTimeRemaining(class<KFDamageType> KFDT)
{
switch (KFDT.default.ScreenMaterialName)
{
case EffectSirenScreamParamName:
return SirenScreamEffectTimeRemaining;
break;
case EffectBloatsPukeParamName:
return BloatPukeEffectTimeRemaining;
break;
case EffectFlashBangParamName:
return FlashBangEffectTimeRemaining;
break;
}
return 0;
}
simulated function float GetEffectPowerUpTimeRemaining( class<KFPowerUp> KFPowerUp )
{
switch (KFPowerUp.default.ScreenMaterialName)
{
case EffectSirenScreamParamName:
return SirenScreamEffectTimeRemaining;
break;
case EffectBloatsPukeParamName:
return BloatPukeEffectTimeRemaining;
break;
case EffectFlashBangParamName:
return FlashBangEffectTimeRemaining;
break;
}
return 0;
}
/** Only called on locally controlled controllers. Must have an input object to run.
See parent for better desc. */
event PlayerTick( float DeltaTime )
{
super.PlayerTick(DeltaTime);
if( WorldInfo.NetMode != NM_DedicatedServer )
{
// Update gameplay effects
UpdateLowHealthEffect(DeltaTime);
UpdateZEDTimeEffects(DeltaTime);
UpdateDOF(DeltaTime);
UpdateFullscreenBlur(DeltaTime);
// Since this function is in tick, perform if checks before calling the function as an optimization
if( ExplosionEarRingTimeRemaining > 0 )
{
UpdateEarRingEffect(DeltaTime);
}
// Update damage screen effects
if( PainEffectTimeRemaining > 0 )
{
UpdateScreenEffect(DeltaTime, EffectPainParamName, PainEffectTimeRemaining, default.PainEffectDuration);
}
if( HealEffectTimeRemaining > 0 )
{
UpdateScreenEffect(DeltaTime, EffectHealParamName, HealEffectTimeRemaining, default.HealEffectDuration);
}
if( SirenScreamEffectTimeRemaining > 0 )
{
UpdateScreenEffect(DeltaTime, EffectSirenScreamParamName, SirenScreamEffectTimeRemaining, default.SonicScreamEffectDuration);
}
if( BloatPukeEffectTimeRemaining > 0 )
{
UpdateScreenEffect(DeltaTime, EffectBloatsPukeParamName, BloatPukeEffectTimeRemaining, default.BloatPukeEffectDuration);
}
if( FlashBangEffectTimeRemaining > 0 )
{
UpdateScreenEffect(DeltaTime, EffectFlashBangParamName, FlashBangEffectTimeRemaining, default.FlashBangEffectDuration);
}
if( HellishRagePowerUpEffectTimeRemaining > 0 )
{
UpdatePowerUpScreenEffect(DeltaTime, EffectPowerUpHellishRageParamName, HellishRagePowerUpEffectTimeRemaining, HellishRagePowerUpEffectDuration);
}
// Optimization : Gameplay effects are turned off if no effect is currently active
if( GameplayPostProcessEffects != none )
{
GameplayPostProcessEffects.bShowInGame = ShouldDisplayGameplayPostProcessFX();
}
}
if ( PauseMoveInputTimeLeft > 0.f )
{
TickPauseMoveInput(DeltaTime);
}
}
/** Set the post processing effect of a zed grabbing a player to on or off */
function SetGrabEffect( bool bValue, optional bool bPlayerZed, optional bool bSkipMessage )
{
bGrabEffectIsActive = bValue;
if(!bSkipMessage && bGrabEffectIsActive)
{
if(bPlayerZed)
{
ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', IMT_PlayerClotGrabWarning);
}
else
{
ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', IMT_ClotGrabWarning);
}
}
else
{
ReceiveLocalizedMessage( class'KFLocalMessage_Interaction', IMT_None);
}
if( GameplayPostProcessEffectMIC != none )
{
if(bGrabEffectIsActive)
{
// Update damage screen effects
GameplayPostProcessEffectMIC.SetScalarParameterValue('Effect_Grabbed', 1);
}
else
{
GameplayPostProcessEffectMIC.SetScalarParameterValue('Effect_Grabbed', 0);
}
}
}
/** Set the post processing effect of a zed grabbing a player to on or off */
function SetGrabEffectEMP(bool bActive, optional bool bPlayerZed, optional bool bSkipMessage)
{
local class<EmitterCameraLensEffectBase> LensEffectTemplate;
if (!bSkipMessage && bActive)
{
LensEffectTemplate = class'KFCameraLensEmit_EMP';
if (LensEffectTemplate != none)
{
ClientSpawnCameraLensEffect(LensEffectTemplate);
}
ReceiveLocalizedMessage(class'KFLocalMessage_Interaction', IMT_EMPGrabWarning);
}
else
{
ReceiveLocalizedMessage(class'KFLocalMessage_Interaction', IMT_None);
}
}
/** Set the post processing effect of an active perk skill to on or off */
function SetPerkEffect( bool bValue )
{
bPerkEffectIsActive = bValue;
if( GameplayPostProcessEffectMIC != none )
{
if( bPerkEffectIsActive )
{
// Update damage screen effects
GameplayPostProcessEffectMIC.SetScalarParameterValue( EffectPerkParamName, 1 );
}
else
{
GameplayPostProcessEffectMIC.SetScalarParameterValue( EffectPerkParamName, 0 );
}
}
}
/** Caches and intializes gameplay post process effects such as pain, low health and ZED time */
function InitGameplayPostProcessFX()
{
local MaterialInstanceConstant GameplayPPMIC;
if ( LocalPlayer(Player) != none )
{
GameplayPostProcessEffects = MaterialEffect(LocalPlayer(Player).PlayerPostProcess.FindPostProcessEffect(GameplayPostProcessEffectName));
if( GameplayPostProcessEffects == none )
{
`warn("[GameplayFX] Could not cache gameplay post process effect. Gameplay post-processing will be disabled");
}
GameplayPPMIC = MaterialInstanceConstant(GameplayPostProcessEffects.Material);
if( GameplayPPMIC != none )
{
GameplayPostProcessEffectMIC = new class'MaterialInstanceConstant';
GameplayPostProcessEffectMIC.SetParent(GameplayPPMIC);
GameplayPostProcessEffects.Material = GameplayPostProcessEffectMIC;
}
else
{
`warn("[GameplayFX] Could not find MIC in gameplay post process effect. Gameplay post-processing will be disabled");
}
// Flush to clear
ResetGameplayPostProcessFX();
}
}
/** Resets all gameplay FX to initial state.
Append to this list if additional effects are added. */
function ResetGameplayPostProcessFX()
{
if( GameplayPostProcessEffectMIC != none )
{
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectPainParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectLowHealthParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectZedTimeParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectZedTimeSepiaParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectNightVisionParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectSirenScreamParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectBloatsPukeParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectHealParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectPerkParamName, 0.f);
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectPowerUpHellishRageParamName, 0.f);
}
if( GameplayPostProcessEffects != none )
{
GameplayPostProcessEffects.bShowInGame = false;
}
bNightVisionActive = false;
bGamePlayDOFActive = false;
bPerkEffectIsActive = false;
}
/** @return TRUE if any of the gameplay post process effects have a strength greater than 0.
Append to this list if additional effects are added. */
function bool ShouldDisplayGameplayPostProcessFX()
{
return bPerkEffectIsActive ||
bGrabEffectIsActive ||
/* Pain effect is active */
PainEffectTimeRemaining > 0.f ||
/* Pawn is in low health */
(Pawn != none && Pawn.Health <= default.LowHealthThreshold) ||
/* Getting healed */
HealEffectTimeRemaining > 0.f ||
/* ZED time effect is active */
CurrentZEDTimeEffectIntensity > 0.f ||
/* sepia effect */
(class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12) ||
/* Night vision active */
bNightVisionActive ||
SirenScreamEffectTimeRemaining > 0.f ||
/** When the player is puked on */
BloatPukeEffectTimeRemaining > 0.f ||
/** When the player caught a flash bang */
FlashBangEffectTimeRemaining > 0.f ||
/** When the player has HellishRage powerUp */
HellishRagePowerUpEffectTimeRemaining > 0.f;
}
/** Update any screen effects
Only called on locally controlled controllers. */
function UpdateScreenEffect( float DeltaTime, name EffectName, out float TimeRemaining, float Duration )
{
local float Intensity;
if( TimeRemaining > 0.f )
{
if( TimeRemaining > DeltaTime )
{
TimeRemaining -= DeltaTime;
Intensity = FClamp(TimeRemaining/Duration, 0.f, 1.f);
}
else
{
TimeRemaining = 0.f;
Intensity = 0.f;
}
// Update the post process effect
if( GameplayPostProcessEffectMIC != none )
{
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectName, Intensity);
}
}
}
/** Update powerup screen effects
Only called on locally controlled controllers. */
function UpdatePowerUpScreenEffect( float DeltaTime, name EffectName, out float TimeRemaining, float Duration )
{
local float Intensity;
if( TimeRemaining > 0.f )
{
if( TimeRemaining > DeltaTime )
{
TimeRemaining -= DeltaTime;
if( TimeRemaining >= 1 )
{
Intensity = 1;
}
else
{
Intensity = TimeRemaining;
}
}
else
{
TimeRemaining = 0.f;
Intensity = 0.f;
}
// Update the post process effect
if( GameplayPostProcessEffectMIC != none )
{
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectName, Intensity);
}
}
}
/** Fade in ZED Time effect */
function SetZedTimeEffectIntensity(float TargetWeight, optional float BlendTime=0.5)
{
TargetZEDTimeEffectIntensity = TargetWeight;
ZEDTimeEffectInterpTimeRemaining = BlendTime;
}
/** Update the ZED time effect.
Only called on locally controlled controllers. */
function UpdateZEDTimeEffects(float DeltaTime)
{
local KFPawn KFP;
local float BlendDelta;
local float ZedTimeAudioModifier;
local float RealDeltaTime;
local float OldZEDTimeEffectIntensity;
local MaterialInstanceConstant WorldMIC;
// If we are playing partial zed time FX, check to see if we changed to full zed time
if ( TargetZEDTimeEffectIntensity == PartialZEDTimeEffectIntensity )
{
KFP = KFPawn(Pawn);
if ( KFP != None && !KFP.bUnaffectedByZedTime )
{
ClientEnterZedTime(false); // restart effects
}
}
// Audio filter for pitch shifting. Take global and local dilation into account.
if( WorldInfo.TimeDilation != LastTimeDilation )
{
ZedTimeAudioModifier = Max((1.0 - WorldInfo.TimeDilation) * 100, 0);
SetRTPCValue( 'ZEDTime_Modifier', ZedTimeAudioModifier, true );
LastTimeDilation = WorldInfo.TimeDilation;
}
OldZEDTimeEffectIntensity = CurrentZEDTimeEffectIntensity;
// Post Effect Blending
if( ZEDTimeEffectInterpTimeRemaining > 0 )
{
// While blending use the real world DeltaTime, since that's the GameInfo uses (see TickZedTime)
RealDeltaTime = (DeltaTime / WorldInfo.TimeDilation);
BlendDelta = (TargetZEDTimeEffectIntensity - CurrentZEDTimeEffectIntensity);
CurrentZEDTimeEffectIntensity += (BlendDelta / ZEDTimeEffectInterpTimeRemaining) * RealDeltaTime;
ZEDTimeEffectInterpTimeRemaining -= RealDeltaTime;
}
else
{
ZEDTimeEffectInterpTimeRemaining = 0.f;
CurrentZEDTimeEffectIntensity = TargetZEDTimeEffectIntensity;
}
if ( OldZEDTimeEffectIntensity != CurrentZEDTimeEffectIntensity )
{
if( GameplayPostProcessEffectMIC != none )
{
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectZedTimeParamName, CurrentZEDTimeEffectIntensity);
}
foreach WorldInfo.ZedTimeMICs( WorldMIC )
{
WorldMIC.SetScalarParameterValue(EffectZedTimeParamName, CurrentZEDTimeEffectIntensity);
}
}
}
simulated function PlayEarRingEffect(float Intensity)
{
ExplosionEarRingDuration = 10.f;
if( ExplosionEarRingTimeRemaining <= 1.f )
{
ExplosionEarRingTimeRemaining = ExplosionEarRingDuration * Intensity;
ExplosionEarRingDelay = 0.5;
if ( !bNoEarRingingSound && EarsRingingPlayEvent != none )
{
PlaySoundBase(EarsRingingPlayEvent, true);
}
}
}
simulated function UpdateEarRingEffect(float DeltaTime)
{
// Ramp up to the full ears ringing effect during the delays
if( ExplosionEarRingDelay > 0.f )
{
ExplosionEarRingEffectIntensity = (1.0 - (ExplosionEarRingDelay/0.5)) * 100.f;
ExplosionEarRingDelay -= DeltaTime;
SetRTPCValue( 'GRENADEFX', ExplosionEarRingEffectIntensity, true );
return;
}
if( ExplosionEarRingTimeRemaining > 0 )
{
ExplosionEarRingEffectIntensity = (ExplosionEarRingTimeRemaining/ExplosionEarRingDuration) * 100.f;
ExplosionEarRingTimeRemaining -= DeltaTime;
if( ExplosionEarRingTimeRemaining <= 0 || Pawn == none || Pawn.Health <= 0 )
{
ExplosionEarRingTimeRemaining = 0.f;
ExplosionEarRingEffectIntensity = 0.f;
if( EarsRingingStopEvent != none )
{
PlaySoundBase(EarsRingingStopEvent, true);
}
}
SetRTPCValue( 'GRENADEFX', ExplosionEarRingEffectIntensity, true );
}
}
/** Update the effect that plays when the player has low health.
Only called on locally controlled controllers. */
function UpdateLowHealthEffect(float DeltaTime)
{
local bool bLowHealth;
if( Pawn != none )
{
// If alive and health is less than given threshold
bLowHealth = Pawn.Health > 0 && Pawn.Health <= default.LowHealthThreshold;
// Play low health post process effect (flashing red)
if (GameplayPostProcessEffectMIC != none)
{
if (PlayerCamera.CameraStyle == 'Boss')
{
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectLowHealthParamName, 0.f);
}
else
{
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectLowHealthParamName, bLowHealth ? 1.f : 0.f);
}
}
if( bLowHealth )
{
if( !bPlayingLowHealthSFX )
{
PostAkEvent( LowHealthStartEvent );
bPlayingLowHealthSFX = true;
//Flash low health effect on keyboard
if(LEDEffectsManager != none)
{
LEDEffectsManager.PlayEffectLowHealth();
}
}
}
else
{
if( bPlayingLowHealthSFX )
{
PostAkEvent( LowHealthStopEvent );
bPlayingLowHealthSFX = false;
if(LEDEffectsManager != none)
{
LEDEffectsManager.LedRestoreLighting();
}
}
}
SetRTPCValue( 'Health', Pawn.Health, true );
}
}
/**
* Can be used to force health FX on or off
* Network: Standalone + Local Client
*/
function ToggleHealthEffects(bool bEnableFX)
{
if( !bEnableFX )
{
if( bPlayingLowHealthSFX )
{
PostAkEvent( LowHealthStopEvent );
bPlayingLowHealthSFX = false;
}
SetRTPCValue( 'Health', 100, true );
}
}
simulated function SetAmplificationLightEnabled(bool bEnabled)
{
// Spawn the amplification light
if(bEnabled)
{
// Destroy the old light
if(AmplificationLight != none)
{
AmplificationLight.DetachFromAny();
AmplificationLight = none;
}
// Recreate the light
if(AmplificationLightTemplate != none)
{
AmplificationLight = new(self) Class'PointLightComponent' (AmplificationLightTemplate);
}
// Attach the light to the pawn
if(AmplificationLight != none)
{
Pawn.AttachComponent(AmplificationLight);
}
}
else
{
// Detach and destroy the light
if(AmplificationLight != none)
{
AmplificationLight.DetachFromAny();
AmplificationLight = none;
}
}
}
/** Turns night vision on/off (called if perk has night vision enabled) */
simulated function SetNightVision(bool bEnabled)
{
if( GameplayPostProcessEffectMIC != none )
{
bNightVisionActive = bEnabled;
GameplayPostProcessEffectMIC.SetScalarParameterValue(EffectNightVisionParamName, (bNightVisionActive) ? 1.f : 0.f);
if(bEnabled)
{
bGamePlayDOFActive = true;
DOF_GP_BlendInSpeed = DOF_NVG_BlendInSpeed;
DOF_GP_BlendOutSpeed = DOF_NVG_BlendOutSpeed;
DOF_GP_FocalDistance = NVG_DOF_FocalDistance;
DOF_GP_SharpRadius = NVG_DOF_SharpRadius;
DOF_GP_FocalRadius = NVG_DOF_FocalRadius;
DOF_GP_MinBlurSize = NVG_DOF_MinBlurSize;
DOF_GP_MaxNearBlurSize = NVG_DOF_MaxNearBlurSize;
DOF_GP_MaxFarBlurSize = NVG_DOF_MaxFarBlurSize;
DOF_GP_ExpFalloff = NVG_DOF_ExpFalloff;
}
else
{
bGamePlayDOFActive = false;
}
}
if( Pawn != none )
{
if( NVGLight == none && NVGLightTemplate != none )
{
NVGLight = new(self) Class'PointLightComponent' (NVGLightTemplate);
}
if( bEnabled )
{
Pawn.AttachComponent(NVGLight);
NVGLight.SetEnabled(bEnabled);
}
else
{
Pawn.DetachComponent(NVGLight);
NVGLight.SetEnabled(bEnabled);
}
//SetAmplificationLightEnabled( !bEnabled );
}
}
/*********************************************************************************************
* @name Cinematic mode with DOF
********************************************************************************************* */
/** called by the server to synchronize cinematic transitions with the client */
reliable client function ClientSetCinematicMode(bool bInCinematicMode, bool bAffectsMovement, bool bAffectsTurning, bool bAffectsHUD, bool bAffectsDof)
{
bCinematicMode = bInCinematicMode;
if(bCinematicMode && bAffectsDof)
{
bGamePlayDOFActive = true;
DOF_GP_BlendInSpeed = DOF_Cinematic_BlendInSpeed;
DOF_GP_BlendOutSpeed = DOF_Cinematic_BlendOutSpeed;
DOF_GP_FocalDistance = DOF_Cinematic_FocalDistance;
DOF_GP_SharpRadius = DOF_Cinematic_SharpRadius;
DOF_GP_FocalRadius = DOF_Cinematic_FocalRadius;
DOF_GP_MinBlurSize = DOF_Cinematic_MinBlurSize;
DOF_GP_MaxNearBlurSize = DOF_Cinematic_MaxNearBlurSize;
DOF_GP_MaxFarBlurSize = DOF_Cinematic_MaxFarBlurSize;
DOF_GP_ExpFalloff = DOF_Cinematic_ExpFalloff;
}
else
{
bGamePlayDOFActive = false;
}
// if there's a hud, set whether it should be shown or not
if ( (myHUD != None) && bAffectsHUD )
{
myHUD.bShowHUD = !bCinematicMode;
}
if (bAffectsMovement)
{
IgnoreMoveInput(bCinematicMode);
}
if (bAffectsTurning)
{
IgnoreLookInput(bCinematicMode);
}
}
/*********************************************************************************************
* @name Zed Time
********************************************************************************************* */
/** Called when initiating zed time to perform recursive trace */
native function bool CanSeeZedTimePawn(bool bZedTimeStart, optional int RecursionCount);
/** Initialize partial zed time visibility interval */
native function StartPartialZedTimeSightCounter();
/** Notification when partial zed time sight counter spots a pawn */
event NotifyPartialZedTimeExited();
/** Returns false if partial (aka Zed Time Lite) should be used */
function bool IsAffectedByZedTime()
{
if ( bForcePartialZedTime )
{
return false;
}
return CanSeeZedTimePawn(true);
}
/** Entering Zed time on the server - give the perk a chance to do something */
function EnterZedTime()
{
local KFPawn KFP;
local KFPerk MyPerk;
local bool bPartialZedTime;
MyPerk = GetPerk();
if ( MyPerk != none )
{
MyPerk.NotifyZedTimeStarted();
}
KFP = KFPawn(Pawn);
if ( KFP != None )
{
KFP.bUnaffectedByZedTime = !IsAffectedByZedTime();
bPartialZedTime = KFP.bUnaffectedByZedTime;
if ( bPartialZedTime )
{
StartPartialZedTimeSightCounter();
}
}
// Call the client
ClientEnterZedTime(bPartialZedTime);
}
/** Exiting Zed time on the server - give the perk a chance to do something */
function FadeOutZedTime()
{
local KFPerk MyPerk;
MyPerk = GetPerk();
if ( MyPerk != none )
{
MyPerk.NotifyZedTimeEnded();
}
// Call the client
ClientFadeOutZedTime();
}
function CompleteZedTime()
{
ClientCompleteZedTime();
}
//Force completion of zed time to kill any effects remaining (in case of large > 0.5s spike)
reliable client function ClientCompleteZedTime()
{
SetZedTimeEffectIntensity(0.0, 0.0);
}
/** Called by the server when the game enters zed time. Used to play the effects */
reliable client function ClientEnterZedTime(bool bPartialOnly)
{
// TODO: Play a message lettting the player know the first time they enter zed time?
if ( bPartialOnly )
{
PlaySoundBase(ZedTimePartialEnterSound, True);
SetZedTimeEffectIntensity(PartialZEDTimeEffectIntensity);
}
else
{
PlaySoundBase(ZedTimeEnterSound, True);
SetZedTimeEffectIntensity(1.f);
}
if(LEDEffectsManager != none)
{
LEDEffectsManager.PlayEffectZedTime();
}
}
/** Called by the server when the game exits zed time. Used to play the effects */
reliable client function ClientFadeOutZedTime()
{
local KFPawn KFP;
local bool bIsPartialZedTime;
KFP = KFPawn(Pawn);
if ( KFP != None && KFP.bUnaffectedByZedTime )
{
bIsPartialZedTime = true;
}
PlaySoundBase((bIsPartialZedTime) ? ZedTimePartialExitSound : ZedTimeExitSound, True);
SetZedTimeEffectIntensity(0.f);
if(LEDEffectsManager != none)
{
LEDEffectsManager.ClearEffectZedTime();
}
}
/*********************************************************************************************
* @name User Interface
********************************************************************************************* */
function ServerNotifyTeamChanged();
function ClientRecieveNewTeam();
/** AWESOMEHUD(TM) */
// Initialize our GFx HUD
function SetGFxHUD( KFGFxMoviePlayer_HUD NewGFxHud )
{
MyGFxHUD = NewGFxHud;
// Set UI scale so the hud movie scales properly
if (WorldInfo.IsConsoleBuild(CONSOLE_Durango))
{
// Now set the UI scale
SetUIScale(KFGameEngine(class'Engine'.static.GetEngine()).SafeFrameScale);
}
}
function ShowBossNameplate( KFInterface_MonsterBoss KFBoss, optional string PlayerName)
{
if(MyGFxHUD != none)
{
MyGFxHUD.ShowBossNameplate(KFBoss.GetMonsterPawn().static.GetLocalizedName() $ PlayerName, KFBoss.GetRandomBossCaption());
}
}
function HideBossNameplate()
{
if(MyGFxHUD != none)
{
MyGFxHUD.HideBossNameplate();
}
}
reliable client function ClientOnBossDied()
{
if(MyGFxHUD != none)
{
MyGFxHUD.bossHealthBar.BossPawn = none;
MyGFxHUD.bossHealthBar.RemoveArmorUI();
}
HideBossNameplate();
if (StatsWrite != none)
{
StatsWrite.SeasonalEventStats_OnBossDied();
}
}
/* SpawnInGameFrontEnd()
Spawn the Front End for the menus that will be used in game
*/
reliable client function ClientSetFrontEnd( class< KFGFxMoviePlayer_Manager > FrontEndClass, optional bool bSkipMenus )
{
local LocalPlayer LP;
if ( MyGFxManager == none )
{
MyGFxManager = new(self) FrontEndClass;
LP = LocalPlayer( Player );
if ( LP != none )
{
MyGFxManager.Init( LP );
MyGFxManager.LaunchMenus( bSkipMenus );
if (OnlineSub.PlayerInterface.GetLoginStatus(LP.ControllerId) > LS_NotLoggedIn && !OnlineSub.SystemInterface.IsControllerConnected(LP.ControllerId))
{
`log("Controller Disconnected");
OnControllerChanged(LP.ControllerId, false, false);
}
}
// Profile settings are read in later for dingo
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) )
{
// Now set the UI scale
SetUIScale( KFGameEngine(class'Engine'.static.GetEngine()).SafeFrameScale );
}
else
{
MyGFxManager.OnProfileSettingsRead();
}
}
}
/** If -skiplobby is added to the command line, skip past the lobby menu and spawn as a random perk */
reliable server function SkipLobby()
{
local KFGameInfo KFGI;
KFGI = KFGameInfo(WorldInfo.Game);
if (KFGI != none)
{
KFGI.LobbyCountdownComplete();
}
}
/** Notifies playercontroller when a lobby was unable to be found during Find Match */
function NotifyUnsuccessfulSearch()
{
if( MyGFxManager != none )
{
MyGFxManager.NotifyUnsuccessfulSearch();
}
}
/** Notifies playercontroller when user has created, joined, or left a lobby */
function OnLobbyStatusChanged(bool bInLobby)
{
if( MyGFxManager != none )
{
MyGFxManager.OnLobbyStatusChanged(bInLobby);
}
}
function string GetSteamAvatar( UniqueNetId NetID )
{
local string AvatarPath;
local int i;
local bool bFoundAvatar;
local PlayerSteamAvatar CurrentAvatar;
AvatarPath = "";
for (i = 0; i < AvatarList.length; i++)
{
if(AvatarList[i].NetID == NetID)
{
bFoundAvatar = true;
if(AvatarList[i].Avatar != none)
{
AvatarPath = AvatarList[i].Avatar.GetPackageName() $ "." $ AvatarList[i].Avatar.Name;
}
}
}
if( !bFoundAvatar )
{
CurrentAvatar.NetID = NetID;
AvatarList.AddItem(CurrentAvatar);
if(OnlineSub != none)
{
OnlineSub.ReadOnlineAvatar(NetID, 64);
}
}
return AvatarPath;
}
function OnAvatarReceived(const UniqueNetId NetId, Texture2D Avatar)
{
local byte i;
for (i = 0; i < AvatarList.length; i++)
{
if (AvatarList[i].NetID == NetId)
{
AvatarList[i].Avatar = Avatar;
return;
}
}
}
function string GetPS4Avatar( const string InPlayerName )
{
local string AvatarPath;
local int i;
local bool bFoundAvatar;
local PlayerAvatarPS4 CurrentAvatar;
AvatarPath = "";
if( InPlayerName == "" )
{
`warn("Attempted to get PS4 avatar for player with no name");
}
for (i = 0; i < AvatarListPS4.length; i++)
{
if(AvatarListPS4[i].PlayerName == InPlayerName)
{
bFoundAvatar = true;
if( AvatarListPS4[i].ImageDownloader != none &&
AvatarListPS4[i].ImageDownloader.TheTexture != none )
{
AvatarPath = AvatarListPS4[i].ImageDownloader.TheTexture.GetPackageName() $ "." $ AvatarListPS4[i].ImageDownloader.TheTexture.Name;
}
}
}
if( !bFoundAvatar && !WorldInfo.IsE3Build() )
{
CurrentAvatar.PlayerName = InPlayerName;
AvatarListPS4.AddItem(CurrentAvatar);
if(OnlineSub != none)
{
OnlineSub.ReadOnlineAvatarByName(InPlayerName, 64);
}
}
return AvatarPath;
}
function OnAvatarURLPS4Received(const string ForPlayerName, const string AvatarURL)
{
local byte i;
for (i = 0; i < AvatarListPS4.length; i++)
{
if( AvatarListPS4[i].PlayerName == ForPlayerName )
{
if( AvatarURL != "" )
{
AvatarListPS4[i].AvatarURL = AvatarURL;
AvatarListPS4[i].ImageDownloader = new class'KFHTTPImageDownloader';
AvatarListPS4[i].ImageDownloader.DownloadImageFromURL(AvatarURL, OnPS4AvatarDownloadComplete);
}
else
{
// AvatarListPS4.RemoveItem( AvatarListPS4[i] );
}
return;
}
}
}
function OnPS4AvatarDownloadComplete(bool bWasSuccessful)
{
local int i;
for( i = AvatarListPS4.length - 1; i >= 0; i-- )
{
if( AvatarListPS4[i].ImageDownloader != None &&
AvatarListPS4[i].AvatarURL == AvatarListPS4[i].ImageDownloader.ImageURL )
{
if( bWasSuccessful )
{
if( MyGFxHUD != none &&
MyGFxHUD.GfxScoreBoardPlayer != none &&
MyGFxHUD.GfxScoreBoardPlayer.ScoreboardWidget != none )
{
MyGFxHUD.GfxScoreBoardPlayer.ScoreboardWidget.ForceUpdateNextFrame();
}
if( MyGFxManager != none )
{
MyGFxManager.ForceUpdateNextFrame();
}
}
else
{
`log("avatar download fail");
// If it fails, have it re-download again
AvatarListPS4[i].ImageDownloader = new class'KFHTTPImageDownloader';
AvatarListPS4[i].ImageDownloader.DownloadImageFromURL(AvatarListPS4[i].AvatarURL, OnPS4AvatarDownloadComplete);
// AvatarListPS4.RemoveItem(AvatarListPS4[i]);
}
}
}
}
/*********************************************************************************************
* @name Chat
********************************************************************************************* */
unreliable server function ServerSay( string Msg )
{
local KFGameInfo KFGI;
KFGI = KFGameInfo(WorldInfo.Game);
if(!KFGI.bDisablePublicTextChat || PlayerReplicationInfo.bAdmin)
{
super.ServerSay(Msg);
}
}
unreliable server function ServerTeamSay( string Msg )
{
local KFGameInfo KFGI;
KFGI = KFGameInfo(WorldInfo.Game);
if(WorldInfo.GRI.bMatchIsOver && !KFGI.bDisablePublicTextChat)
{
super.ServerSay(Msg);
}
else
{
super.ServerTeamSay(Msg);
}
}
/** Completely overrides super */
reliable client event TeamMessage( PlayerReplicationInfo PRI, coerce string S, name Type, optional float MsgLifeTime )
{
local string ChatMessage;
if(PRI == none)
{
return;
}
if( PRI.Team != none && Type == 'TeamSay' && PRI.Team.TeamIndex != PlayerReplicationInfo.Team.TeamIndex )
{
return;
}
ChatMessage = GetChatChannel(Type, PRI)@PRI.PlayerName$": " $S;
if (MyGFxManager != none && Type != MusicMessageType)
{
if (class'WorldInfo'.static.IsMenuLevel())
{
ChatMessage = S;
}
if(MyGFxManager.PartyWidget != none && !MyGFxManager.PartyWidget.ReceiveMessage(ChatMessage)) //Fails if message is for updating perks in a steam lobby
{
return;
}
if(MyGFxManager.PostGameMenu != none)
{
MyGFxManager.PostGameMenu.ReceiveMessage(ChatMessage);
}
}
if( MyGFxHUD != none )
{
if(Type == MusicMessageType)
{
MyGFxHUD.MusicNotification.ShowSongInfo(S);
}
else
{
if(Type != 'Event' && Type != 'None' )
{
RecieveChatMessage(PRI, ChatMessage, Type, MsgLifeTime);
}
else if( MyGFxHUD.HudChatBox != none && len(s) > 0 )
{
if (InStr(S, class'KFGFxWidget_BaseParty'.default.PerkPrefix) == INDEX_NONE && InStr(S, class'KFGFxWidget_BaseParty'.default.SearchingPrefix) == INDEX_NONE) //make sure it does not contain perk info
{
MyGFxHUD.HudChatBox.AddChatMessage(class'KFLocalMessage'.default.SystemString@S, class 'KFLocalMessage'.default.EventColor);
}
}
}
}
}
function string GetChatChannel(name Type, PlayerReplicationInfo PRI)
{
if(!PRI.bOnlySpectator)
{
if(Type == 'TeamSay')
{
return "<"$class'KFCommon_LocalizedStrings'.default.TeamString$">";
}
else
{
return "<"$class'KFCommon_LocalizedStrings'.default.AllString$">";
}
}
else
{
return "<"$class'KFCommon_LocalizedStrings'.default.SpectatorString$">";
}
}
function RecieveChatMessage(PlayerReplicationInfo PRI, string ChatMessage, name Type, optional float MsgLifeTime)
{
if( MyGFxHUD.HudChatBox != none )
{
if(PRI.bOnlySpectator)
{
ChatMessage = class'KFCommon_LocalizedStrings'.default.SpectatorString@ChatMessage;
}
if(PRI.bAdmin)
{
ChatMessage = class'KFLocalMessage'.default.AdminString$ChatMessage;
MyGFxHUD.HudChatBox.AddChatMessage(ChatMessage, class 'KFLocalMessage'.default.PriorityColor);
}
else
{
MyGFxHUD.HudChatBox.AddChatMessage(ChatMessage, class 'KFLocalMessage'.default.SayColor);
}
}
}
function OpenChatBox()
{
local KFGFxHudWrapper GFxHUDWrapper;
GFxHUDWrapper = KFGFxHudWrapper(myHUD);
if(GFxHUDWrapper != none && GFxHUDWrapper.HudMovie != none)
{
GFxHUDWrapper.HudMovie.OpenChatBox();
}
}
exec function Talk()
{
CurrentTextChatChannel = ETCC_ALL;
OpenChatBox();
}
exec function TeamTalk()
{
CurrentTextChatChannel = ETCC_TEAM;
OpenChatBox();
}
/*********************************************************************************************
* @name Lobby / Party
********************************************************************************************* */
reliable client function ClientSetCountdown(bool bFinalCountdown, byte CountdownTime, optional NavigationPoint PredictedSpawn)
{
if ( bFinalCountdown && PredictedSpawn != None )
{
ClientAddTextureStreamingLoc(PredictedSpawn.Location, 0.f, false);
}
if (MyGFxManager != none && MyGFxManager.PartyWidget != none)
{
MyGFxManager.PartyWidget.StartCountdown(CountdownTime, bFinalCountdown);
}
else
{
KFGameReplicationInfo(WorldInfo.GRI).RemainingTime = CountdownTime;
}
}
/**
* @brief Tells the UI to stop the local countdown
*/
reliable client function ClientResetCountdown()
{
if (MyGFxManager != none && MyGFxManager.PartyWidget != none)
{
MyGFxManager.PartyWidget.StopCountdown();
}
}
//@HSL_BEGIN - JRO - 3/14/2016 - Adding presence for Orbis
reliable client function ClientSetOnlineStatus()
{
if( OnlineSub != none )
{
OnlineSub.PlayerInterface.SetOnlineStatus(LocalPlayer(Player).ControllerId, WorldInfo.GetMapName(true), true);
}
}
//@HSL_END
/*********************************************************************************************
* @name Inventory
********************************************************************************************* */
// The player wants to fire.
exec function StartFire( optional byte FireModeNum )
{
local KFInventoryManager KFIM;
// Ignore fire input in cinematics
if( bCinematicMode )
{
return;
}
if (!KFPlayerInput(PlayerInput).bQuickWeaponSelect)
{
if (MyGFxHUD != none && MyGFxHUD.WeaponSelectWidget != none &&MyGFxHUD.WeaponSelectWidget.bChangingWeapons)
{
KFIM = KFInventoryManager( Pawn.InvManager );
KFIM.SetCurrentWeapon( KFIM.PendingWeapon );
MyGFxHUD.WeaponSelectWidget.FadeOut();
return;
}
}
if (MyGFxHUD != none && MyGFxHUD.VoiceCommsWidget != none && MyGFxHUD.VoiceCommsWidget.bActive)
{
return;
}
if (KFPlayerInput(PlayerInput).bGamepadWeaponSelectOpen && MyGFxHUD.WeaponSelectWidget != none)
{
MyGFxHUD.WeaponSelectWidget.SetWeaponSwitchStayOpen(false);
KFPlayerInput(PlayerInput).bGamepadWeaponSelectOpen = false;
}
super.StartFire( FireModeNum );
}
// The player wants to alternate-fire.
exec function StartAltFire( optional Byte FireModeNum )
{
if (!KFPlayerInput(PlayerInput).bQuickWeaponSelect)
{
if (KFPlayerInput(PlayerInput).bGamepadWeaponSelectOpen)
{
return;
}
}
super.StartAltFire(FireModeNum);
}
/*********************************************************************************************
* @name Trader
********************************************************************************************* */
simulated function KFAutoPurchaseHelper GetPurchaseHelper(optional bool bInitialize = false)
{
if(PurchaseHelper == none)
{
PurchaseHelper = new(self) PurchaseHelperClass;
bInitialize = true;
}
if(bInitialize)
{
PurchaseHelper.Initialize();
}
return PurchaseHelper;
}
function DoAutoPurchase()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if(KFGRI != none && KFGRI.GameClass.Name == 'KFGameInfo_Tutorial' || bDisableAutoUpgrade)
{
OpenTraderMenu();
return;
}
ServerSetEnablePurchases(true);
GetPurchaseHelper().DoAutoPurchase();
ServerSetEnablePurchases(false);
}
reliable server function ServerSetEnablePurchases(bool bEnalbe)
{
local KFInventoryManager KFIM;
if( Role == ROLE_Authority && Pawn != none )
{
KFIM = KFInventoryManager(Pawn.InvManager);
KFIM.bServerTraderMenuOpen = bEnalbe;
}
bClientTraderMenuOpen = bEnalbe;
}
/** Server instigated open trader menu */
function OpenTraderMenu( optional bool bForce=false )
{
local KFInventoryManager KFIM;
SyncInventoryProperties();
if( Role == ROLE_Authority && Pawn != none )
{
KFIM = KFInventoryManager(Pawn.InvManager);
if( KFIM != none && !KFIM.bServerTraderMenuOpen )
{
KFIM.bServerTraderMenuOpen = true;
ClientOpenTraderMenu(bForce);
}
}
}
reliable client function ClientOpenTraderMenu( optional bool bForce=false )
{
if( Role < ROLE_Authority && !KFGameReplicationInfo(WorldInfo.GRI).bTraderIsOpen && !bForce )
{
return; // too late
}
SyncInventoryProperties();
if( MyGFxManager != none )
{
MyGFxManager.OpenMenu( UI_Trader, false );
`TraderDialogManager.PlayOpenTraderMenuDialog( self );
}
}
function CloseTraderMenu()
{if ( MyGFxManager != none )
{
MyGFxManager.CloseTraderMenu();
}if ( MyGFxManager != none )
{
MyGFxManager.CloseTraderMenu();
}
}
/** Our score has either been replicated or updated while the trader menu is open */
simulated function NotifyTraderDoshChanged()
{
local KFGFxMenu_Trader TraderMenu;
if( MyGFxManager != none )
{
TraderMenu = KFGFxMenu_Trader(MyGFxManager.CurrentMenu);
if( TraderMenu != none )
{
GetPurchaseHelper().NotifyDoshChanged();
}
}
}
/*********************************************************************************************
* Post Round Menu
*********************************************************************************************/
reliable client function ClientOpenRoundSummary()
{
if( !PlayerReplicationInfo.bReadyToPlay )
{
return;
}
if (MyGFxManager != none)
{
MyGFxManager.CloseMenus();
MyGFxManager.SetHUDVisiblity(false);
}
if(PostRoundMenuClass == none || MyGFxPostRoundMenu != none)
{
return;
}
//Create and show the post game menu
MyGFxPostRoundMenu = new PostRoundMenuClass;
MyGFxPostRoundMenu.Init(class'Engine'.static.GetEngine().GamePlayers[MyGFxPostRoundMenu.LocalPlayerOwnerIndex]);
}
function ClosePostRoundSummary()
{
SyncInventoryProperties();
if ( MyGFxPostRoundMenu != none )
{
MyGFxPostRoundMenu.Close(true);
MyGFxPostRoundMenu = None;
PlayerInput.ResetInput();
if (MyGFxManager != none)
{
MyGFxManager.CloseMenus(true);
}
}
}
/*********************************************************************************************
* Post Game Menu
*********************************************************************************************/
reliable client function ClientShowPostGameMenu( optional bool bShowMenu=true )
{
ClosePostRoundSummary();
if( !bShowMenu )
{
ClosePostGameMenu();
}
else if (MyGFxManager != none)
{
MyGFxManager.bPostGameState = true;
MyGFxManager.bCanCloseMenu = false;
MyGFxManager.OpenMenu( UI_PostGame, false );
MyHUD.bShowHUD = false;
//`TraderDialogManager.PlayOpenTraderMenuDialog( self );
}
}
function ClosePostGameMenu()
{
if ( MyGFxManager != none )
{
MyGFxManager.bPostGameState = false;
MyGFxManager.bCanCloseMenu = true;
MyGFxManager.ClosePostGameMenu();
MyHUD.bShowHUD = true;
}
}
/********************************************************************************************
* RhythmCounter
********************************************************************************************/
function UpdateRhythmCounterWidget( int Count, int Max )
{
if( MyGFxHUD != none )
{
MyGFxHUD.UpdateRhythmCounterWidget( Count, Max );
}
}
function UpdateGoompaCounterWidget(int Count, int Max)
{
if( MyGFxHUD != none )
{
MyGFxHUD.UpdateGoompaCounterWidget(Count, Max);
}
}
/*********************************************************************************************
* Objective
* Tell the objective that a player has accepted/denied the objective
*********************************************************************************************/
simulated function SetObjectiveUIActive(bool bActive)
{
if(MyGFxHUD != none && MyGFxHUD.WaveInfoWidget != none
&& MyGFxHUD.WaveInfoWidget.ObjectiveContainer != none)
{
MyGFxHUD.WaveInfoWidget.ObjectiveContainer.SetActive(bActive);
}
}
function SetObjeciveUIIcon(string IconPath)
{
if(MyGFxHUD != none && MyGFxHUD.WaveInfoWidget != none
&& MyGFxHUD.WaveInfoWidget.ObjectiveContainer != none)
{
MyGFxHUD.WaveInfoWidget.ObjectiveContainer.SetCurrentIcon(IconPath);
}
}
/*********************************************************************************************
* @name Audio
*********************************************************************************************/
/** Gets an AkComponent from the AkAudioDevice's pool of components to use for locational sounds */
native function AkComponent GetPooledAkComponent( optional Actor SourceActor );
unreliable client event WwiseClientHearSound(AkEvent ASound, Actor SourceActor, vector SourceLocation, bool bStopWhenOwnerDestroyed, optional bool bIsOccluded )
{
local int i;
local name EnvironmentName;
local bool bFollowSourceActor;
bFollowSourceActor = SourceActor != none && IsZero( SourceLocation );
if( bFollowSourceActor && ASound.bForceHearSoundLocational )
{
bFollowSourceActor = false;
SourceLocation = SourceActor.Location;
}
// hardcoded common switch
if( ASound.bUseEnvironmentReverbSwitchGroup )
{
if( bFollowSourceActor && WorldInfo != none )
{
EnvironmentName = WorldInfo.GetAkEnvironmentName(SourceActor.Location);
}
else if( WorldInfo != none )
{
EnvironmentName = WorldInfo.GetAkEnvironmentName(SourceLocation);
}
for( i = 0; i < ASound.CustomSwitches.Length; ++i )
{
if( ASound.CustomSwitches[0].SwitchGroupName == 'Environment_Reverb' )
{
ASound.CustomSwitches[i].SwitchName = EnvironmentName;
break;
}
}
if( i >= ASound.CustomSwitches.Length )
{
ASound.CustomSwitches.Insert(0, 1);
ASound.CustomSwitches[0].SwitchGroupName = 'Environment_Reverb';
ASound.CustomSwitches[0].SwitchName = EnvironmentName;
}
}
if( bFollowSourceActor )
{
SourceActor.PostAkEvent( ASound, bIsOccluded, !(SourceActor == self || SourceActor == Pawn), bStopWhenOwnerDestroyed );
}
else
{
PostAkEventAtLocation( ASound, SourceLocation, bIsOccluded );
}
}
/* Here is an example of how to use custom switches and rtpcs */
/*
unreliable client event WwiseClientHearSound(AkEvent ASound, Actor SourceActor, vector SourceLocation, bool bStopWhenOwnerDestroyed, optional bool bIsOccluded )
{
for( i = 0; i < ASound.CustomTags.Length; ++i )
{
switch( ASound.CustomTags[i] )
{
case 'YourCustomTagHere':
// logic to determine game sync value
// logic logic logic logic
// Assign value to RTPC
ASound.CustomRTPC[i].RTPCName = 'YourCustomRTPCNameHere';
ASound.CustomRTPC[i].RTPCValue = YourCustomRTPCValue.f;
// OR to switch
ASound.CustomSwitches[i].SwitchGroupName = 'YourCustomSwitchGroupNameHere';
ASound.CustomSwitches[i].SwitchName = 'YourCustomSwitchNameHere';
};
}
super.WwiseClientHearSound(ASound, SourceActor, SourceLocation, bStopWhenOwnerDestroyed, bIsOccluded );
}
*/
/* ClientHearSoundAdvanced()
Replicated function from server for replicating audible sounds played on server that have advanced functionality and require replicating rotation
*/
unreliable client event ClientHearSoundAdvanced(AkEvent ASound, Actor SourceActor, vector SourceLocation, byte CompressedSourcePitch, byte CompressedSourceYaw, byte RapidFireEnabled, bool bStopWhenOwnerDestroyed, optional bool bIsOccluded )
{
local rotator SoundRotation;
SoundRotation.Pitch = 256 * CompressedSourcePitch;
SoundRotation.Yaw = 256 * CompressedSourceYaw;
ProcessAdvancedHearSound(ASound, SourceActor, SourceLocation, SoundRotation, RapidFireEnabled, bStopWhenOwnerDestroyed, bIsOccluded);
}
/* ClientHearSoundAdvanced()
Replicated function from server for replicating audible sounds played on server that have advanced functionality and will get the rotation from a relevant actor
*/
unreliable client event ClientHearSoundAdvancedRelevant(AkEvent ASound, Actor SourceActor, vector SourceLocation, byte RapidFireEnabled, bool bStopWhenOwnerDestroyed, optional bool bIsOccluded )
{
local rotator SoundRotation;
SoundRotation = SourceActor.GetAKRotation();
ProcessAdvancedHearSound(ASound, SourceActor, SourceLocation, SoundRotation, RapidFireEnabled, bStopWhenOwnerDestroyed, bIsOccluded);
}
/* ProcessAdvancedHearSound()
* Process a replicated advanced sound, playing the echo sounds and the original sounds
*
* @note: this only gets used for locational shoot sounds at the moment, and it only really supports those kinds of sounds (e.g. always plays echoes, applies RTPC that will mess up sounds that follow)
*/
simulated function ProcessAdvancedHearSound(AkEvent ASound, Actor SourceActor, vector SourceLocation, rotator SourceRotation, byte RapidFireEnabled, bool bStopWhenOwnerDestroyed, optional bool bIsOccluded )
{
local int i;
// if the shooter has the Commando Rapid Fire perk skill enabled, set the rapid fire rtpc (which changes pitch).
// only apply to locational (pooled) sounds, because pooled component correctly have their custom RTPCs reset.
if( RapidFireEnabled != 0 && !IsZero(SourceLocation) )
{
for( i = 0; i < ASound.CustomRTPCs.Length; ++i )
{
if( ASound.CustomRTPCs[0].RTPCName == 'ZEDTime_CommandoPerk' )
{
ASound.CustomRTPCs[i].RTPCValue = 100.f;
break;
}
}
if( i >= ASound.CustomRTPCs.Length )
{
ASound.CustomRTPCs.Insert(0, 1);
ASound.CustomRTPCs[0].RTPCName = 'ZEDTime_CommandoPerk';
ASound.CustomRTPCs[0].RTPCValue = 100.f;
}
}
// Play the original base sound
WwiseClientHearSound(ASound, SourceActor, SourceLocation, bStopWhenOwnerDestroyed, bIsOccluded);
// Play the sound from the SourceActor's location when the SourceLocation is zero.
if( SourceActor != none && IsZero(SourceLocation) )
{
SourceLocation = SourceActor.Location;
}
// Play echoes
HearEchoes( ASound, SourceLocation, SourceRotation );
}
simulated function HearEchoes( AkEvent ASound, vector SourceLocation, rotator SourceRotation )
{
local float EchoDistance;
local ReverbVolume EchoVolume;
local vector ViewLocation;
local rotator ViewRotation;
local float ViewDist;
class'KFReverbVolume'.static.CalculateEchoVolumeAndDistance( WorldInfo, SourceLocation, EchoVolume, EchoDistance );
GetPlayerViewPoint( ViewLocation, ViewRotation );
ViewDist = VSizeSq(ViewLocation-SourceLocation);
// Play all 4 positional echoes if the listener is within the echo distance
if( EchoDistance == 0 || (ViewDist < EchoDistance * EchoDistance) )
{
// For playing the echo sounds we don't care about pitch, we want the
// The sound to be arrayed around the sound location disregarding pitch.
SourceRotation.Pitch = 0;
PlayDirectionalEcho( ASound.EchoFront, EchoVolume, SourceLocation, EchoDistance, vect(1,0,0) >> SourceRotation );
PlayDirectionalEcho( ASound.EchoLeft, EchoVolume, SourceLocation, EchoDistance, vect(0,-1,0) >> SourceRotation );
PlayDirectionalEcho( ASound.EchoRight, EchoVolume, SourceLocation, EchoDistance, vect(0,1,0) >> SourceRotation );
PlayDirectionalEcho( ASound.EchoRear, EchoVolume, SourceLocation, EchoDistance, vect(-1,0,0) >> SourceRotation );
}
// Play a mono echo if the listener is outside the echo distance
else
{
//`log("Echo outside EchoDistance = "$(EchoDistance/100.0)$" Meters doing mono sound");
PlayEcho( ASound.EchoMono, SourceLocation, FMin((EchoDistance/34029.0) * 2.0,5.0) );
}
}
function PlayDirectionalEcho( AkEvent EchoSound, ReverbVolume EchoVolume, vector SourceLocation, float EchoDistance, vector EchoDirection )
{
local vector EchoLocation;
local float EchoDelay;
class'KFReverbVolume'.static.CalculateEchoLocationAndDelay( EchoVolume, SourceLocation, EchoDirection, EchoDistance, EchoLocation, EchoDelay );
PlayEcho( EchoSound, EchoLocation, EchoDelay );
}
function PlayEcho( AkEvent EchoSound, vector EchoLocation, float EchoDelay )
{
if( EchoSound != none )
{
// @todo: could randomize EchoDelay here
EchoSound.SetCustomRTPC( 'EchoDistance', EchoDelay );
// @todo: go straight to wwiseclienthearsound?
PlaySoundBase( EchoSound, true, true, false, EchoLocation, true );
}
}
/**
* Pause Wwise sounds for all players
*
* @param bPause indicates whether Wwise sounds should be paused or resumed
*/
function PauseWwiseForAllPlayers( bool bPause )
{
local KFPlayerController PC;
foreach WorldInfo.AllControllers(class'KFPlayerController', PC)
{
PC.ClientPauseWwise( bPause );
}
}
/**
* Pause Wwise sounds for this player
*
* @param bPause indicates indicates whether Wwise sounds should be paused or resumed
*/
reliable client function ClientPauseWwise(bool bPause)
{
if( bPause )
{
PostAkEvent( PauseWwiseEvent );
}
else
{
PostAkEvent( ResumeWwiseEvent );
}
}
/*********************************************************************************************
* @name Standalone (Pause)
*********************************************************************************************/
/** Try to pause game; returns success indicator. */
function bool SetPause( bool bPause, optional delegate<CanUnpause> CanUnpauseDelegate=CanUnpause)
{
local bool bWasPaused, bIsPaused;
// Don't pause if we're at the IIS
if( bPause && !KFGameViewportClient( class'Engine'.static.GetEngine().GameViewport ).bSeenIIS && WorldInfo.IsConsoleBuild() )
{
return false;
}
bWasPaused = IsPaused();
bIsPaused = super.SetPause( bPause, CanUnpauseDelegate );
if( bWasPaused != bIsPaused )
{
PauseWwiseForAllPlayers( !bWasPaused && bIsPaused );
}
return bIsPaused;
}
/**
* Attempts to pause/unpause the game when the UI opens/closes. Note: pausing
* only happens in standalone mode
*
* OVERRIDES SUPER
*
* @param bIsOpening whether the UI is opening or closing
*/
function OnExternalUIChanged(bool bIsOpening)
{
bIsExternalUIOpen = bIsOpening;
// don't pause or unpause after toggling external UI if we have menus up (menu toggling handles pausing on standalone)
// (see KFHUDBase::OnLostFocusPause)
if( WorldInfo.NetMode == NM_Standalone && MyGFxManager.bMenusOpen )
{
return;
}
SetPause(bIsOpening, CanUnpauseExternalUI);
}
reliable server function ServerPause()
{
if( WorldInfo.Game.AllowPausing( self ) )
{
// Pause if not already
if( !IsPaused() )
{
SetPause( true );
}
else
{
SetPause( false );
}
}
}
function bool PerformedUseAction()
{
local KFGameReplicationInfo KFGRI;
// Intentionally do not trigger Super Class so that we do not close the menu
if(WorldInfo.NetMode != NM_StandAlone)
{
return Super.PerformedUseAction();
}
// if the level is paused,
if ( Pawn == None )
{
return true;
}
// below is only on server
if( Role < Role_Authority )
{
return false;
}
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if(KFGRI != none && KFGRI.bIsEndlessPaused)
{
return false;
}
// leave vehicle if currently in one
if( Vehicle(Pawn) != None )
{
return Vehicle(Pawn).DriverLeave(false);
}
// try to find a vehicle to drive
if( FindVehicleToDrive() )
{
return true;
}
// try to interact with triggers
return TriggerInteracted();
}
exec function changeSafeFrame(float frameScale)
{
local Vector2D ViewportSize;
local GameViewportClient GVC;
GVC = MyGFxManager != None ? MyGFxManager.GetGameViewportClient() : MyGFxHUD != None ? MyGFxHUD.GetGameViewportClient() : None;
if ( GVC == None )
{
return;
}
GVC.GetViewportSize(ViewportSize);
if ( MyGFxManager != None )
{
MyGFxManager.UpdateViewportSize((ViewportSize.X-(ViewportSize.X*frameScale))/2,(ViewportSize.Y-(ViewportSize.Y*frameScale))/2,(ViewportSize.X*frameScale),(ViewportSize.Y*frameScale));
}
if ( MyGFxHUD != None )
{
MyGFxHUD.UpdateRatio(frameScale);
}
}
function SetUIScale(float fScale)
{
local LocalPlayer LocalPlayer;
LocalPlayer = LocalPlayer(Player);
if (LocalPlayer != None && KFGameViewportClient(LocalPlayer.ViewportClient) != None)
{
changeSafeFrame(fScale);
}
}
/*********************************************************************************************
* @name Stats Daily, Weekly and Special Event
*********************************************************************************************/
function GetSeasonalEventStatInfo(int StatIdx, out int CurrentValue, out int MaxValue)
{
CurrentValue = StatsWrite.GetSeasonalEventStatValue(StatIdx);
MaxValue = StatsWrite.GetSeasonalEventStatMaxValue(StatIdx);
}
simulated event CompletedDaily(int Index)
{
//notify the hud
if( WorldInfo.NetMode != NM_DedicatedServer && IsLocalPlayerController() && !StatsWrite.IsDailyObjectiveComplete(Index) )
{
if( MyGFxHUD != none && MyGFxHUD.LevelUpNotificationWidget != none )
{
MyGFxHUD.LevelUpNotificationWidget.FinishedDailyEvent(Index);
}
//refresh the hud
if(MyGFxManager != none && MyGFxManager.StartMenu != none
&& MyGFxManager.StartMenu.MissionObjectiveContainer != none)
{
MyGFxManager.StartMenu.MissionObjectiveContainer.Refresh();
}
}
}
function DailyEventInformation GetDailyObjective(int Index)
{
local DailyEventInformation DefaultObject;
if (StatsWrite != none)
{
return StatsWrite.GetDailyEventStruct(Index);
}
return DefaultObject;
}
function int GetCurrentDailyValue(int Index)
{
if (StatsWrite != none)
{
return StatsWrite.GetCurrentDailyValue(Index);
}
return INDEX_NONE;
}
function int GetMaxDailyValue(int Index)
{
if (StatsWrite != none)
{
return StatsWrite.GetMaxDailyValue(Index);
}
return INDEX_NONE;
}
function bool IsDailyObjectiveComplete(int Index)
{
if (StatsWrite != none)
{
return StatsWrite.IsDailyObjectiveComplete(Index);
}
`log("Stats write is none. Cannont read daily data");
return false;
}
function bool IsWeeklyEventComplete()
{
if (StatsWrite != none)
{
return StatsWrite.IsWeeklyEventComplete();
}
`log("Stats write is none. Cannont read weekly data");
return false;
}
function bool IsEventObjectiveComplete(int Index)
{
if (StatsWrite != none)
{
return StatsWrite.IsEventObjectiveComplete(Index);
}
`log("Stats write is none. Cannont read event data");
return false;
}
function int GetSpecialEventRewardValue()
{
if (StatsWrite != none)
{
return StatsWrite.GetSpecialEventRewardValue();
}
return 0;
}
/*********************************************************************************************
* @name Stats XP Achievements
*********************************************************************************************/
/** Accessor functions for UI */
native function int GetPerkXP(class<KFPerk> PerkClass);
native function int GetPerkBuildByPerkClass(class<KFPerk> PerkClass);
/** Debugging */
exec native function GiveXP(int XP);
exec native function LogPerkBuilds();
/** Network: Local Player only */
simulated event InitializeStats()
{
local class<KFOnlineStatsRead> StatsReadClass;
local class<KFOnlineStatsWrite> StatsWriteClass;
if ( StatsRead == none && WorldInfo.NetMode != NM_DedicatedServer )
{
// BWJ - 1-4-17 - Different stats read for dingo
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) )
{
StatsReadClass = class'KFOnlineStatsReadDingo';
StatsWriteClass = class'KFOnlineStatsWriteDingo';
}
else
{
StatsReadClass = class'KFOnlineStatsRead';
StatsWriteClass = class'KFOnlineStatsWrite';
}
StatsWrite = new( self ) StatsWriteClass;
StatsRead = new( self ) StatsReadClass;
StatsWrite.MyKFPC = self;
StatsRead.OwningUniqueID = WorldInfo.IsConsoleBuild() ? LocalPlayer(Player).GetUniqueNetId() : PlayerReplicationInfo.UniqueId;
StatsRead.LinkedWriteObject = StatsWrite;
// We delay this for XB1 if we're still at the IIS since we haven't established the active user yet
if( !WorldInfo.IsConsoleBuild(CONSOLE_Durango) || KFGameViewportClient(class'Engine'.static.GetEngine().GameViewport).bSeenIIS ||
class'KFGameEngine'.static.CheckSkipLobby() )
{
ReadStats();
}
}
}
simulated function LogStatValue(int StatId)
{
if (StatsWrite != none)
{
StatsWrite.LogStatValue(StatId);
}
}
simulated function SetStatsReadOwningPlayerId( UniqueNetId InUniqueId )
{
StatsRead.OwningUniqueID = InUniqueId;
}
/** Reads stats from the online persistent storage */
simulated function ReadStats()
{
local array<UniqueNetId> Players;
`log("ReadStats called! OnlineSub=" $ OnlineSub @ "StatsRead.UserStatsReceivedState=" $ StatsRead.UserStatsReceivedState @ "StatsRead.OwningUniqueID=" $ Class'OnlineSubsystem'.static.UniqueNetIdToString(StatsRead.OwningUniqueID), StatsRead.bLogStatsRead );
// Initialize stats for people using local profile (xbox one only)
if( KFGameEngine(class'Engine'.static.GetEngine()).LocalLoginStatus == LS_UsingLocalProfile )
{
OnStatsInitialized(false);
}
else if ( OnlineSub != None && StatsRead.UserStatsReceivedState != OERS_Done )
{
OnlineSub.StatsInterface.AddReadOnlineStatsCompleteDelegate( OnStatsInitialized );
Players[0] = StatsRead.OwningUniqueID;
OnlineSub.StatsInterface.ReadOnlineStats( LocalPlayer(Player).ControllerId, Players, StatsRead );
}
else if (OnlineSub == None)
{
`warn("KFPlayerController.ReadStats: No online subsystem present.");
OnStatsInitialized(false);
}
}
simulated function OnStatsInitialized( bool bWasSuccessful )
{
local int i;
local KFGameEngine KFEngine;
if(OnlineSub != none)
{
OnlineSub.StatsInterface.ClearReadOnlineStatsCompleteDelegate( OnStatsInitialized );
}
KFEngine = KFGameEngine(class'Engine'.static.GetEngine());
// Attempt stats re-read if it fails for XB1. May be a network problem
if( !bWasSuccessful &&
WorldInfo.IsConsoleBuild(CONSOLE_Durango) &&
KFGameViewportClient(class'Engine'.static.GetEngine().GameViewport).bSeenIIS &&
KFEngine.CachedStatRows.Length > 0 )
{
bWasSuccessful = true;
// Copy previous data cached from the engine
StatsRead.Rows = KFEngine.CachedStatRows;
}
else if( bWasSuccessful && WorldInfo.IsConsoleBuild(CONSOLE_Durango) )
{
// Copy cached stat rows to engine in case we need them later in case of a network interruption
KFEngine.CachedStatRows = StatsRead.Rows;
}
//If we failed to read, don't allow writing
else if ( !bWasSuccessful )
{
if (StatsWrite == none)
{
`log(GetFuncName() $ ": Something has gone wrong, we have no stats write object.");
}
else
{
StatsWrite.NotifyReadFailure();
}
}
//On all platforms, notify of successful read
if (bWasSuccessful && StatsWrite != none)
{
StatsWrite.NotifyReadSucceeded();
}
StatsRead.OnStatsInitialized( bWasSuccessful );
StatsWrite.OnStatsInitialized( bWasSuccessful );
//Init timers to wait on stats read to kick weekly/special event UI information
CheckSpecialEventID();
CheckWeeklyEventID();
if( MyGFxManager != none )
{
MyGFxManager.StatsInitialized();
}
// Load perk levels after stats are read in
LoadAllPerkLevels();
ClientInitializePerks();
// Update the GFX menu if we need to
if( MyGFxManager != none && MyGFxManager.PerksMenu != none )
{
MyGFxManager.PerksMenu.UpdateContainers( PerkList[SavedPerkIndex].PerkClass );
}
//Get starting values for tracking xp changes on AAR -ZG
for (i = 0; i < PerkList.length; i++)
{
`RecordAARPerkXPGain(self, PerkList[i].PerkClass, 0, 0);
}
}
`if(`notdefined(ShippingPC))
exec function DumpStatsRead()
{
StatsRead.DumpStats(LocalPlayer(Player).GetUniqueNetId());
}
`endif
/** Called from the server at the end of a wave to write stats */
reliable client event ClientWriteAndFlushStats()
{
if( WorldInfo.NetMode == NM_DedicatedServer ||
KFGameEngine(class'Engine'.static.GetEngine()).LocalLoginStatus != LS_LoggedIn )
{
return;
}
//Only write if our stats writer is in a fully valid state. It will have needed to:
// - Not be cheating
// - Not have had a read failure (Steam down, etc)
// - Have successfully completed a stat read
//
//Since the stats write can be in any combination of these states, check against all 3.
if( OnlineSub != none && OnlineSub.StatsInterface != none && StatsWrite != none && !StatsWrite.HasCheated() && !StatsWrite.HasReadFailure() && StatsWrite.HasReadStats() )
{
`log(GetFuncName() @ "Writing and flushing stats to steam!", StatsWrite.bLogStatsWrite);
OnlineSub.StatsInterface.WriteOnlineStats('Game', PlayerReplicationInfo.UniqueId, StatsWrite);
OnlineSub.StatsInterface.FlushOnlineStats('Game');
}
else if( StatsWrite == none )
{
`log(GetFuncName() @ "Not writing and flushing stats to steam because StatsWrite is null.", StatsWrite.bLogStatsWrite);
}
}
function bool HasReadStats()
{
return StatsWrite != none && StatsWrite.HasReadStats();
}
/**
* @brief The player won a game while still alve. Client and listen only
*
* @param MapName The name of the map played
* @param Difficulty The game's difficulty
* @param GameLength short/medium/long
*/
reliable client function ClientWonGame( string MapName, byte Difficulty, byte GameLength, byte bCoop )
{
if( WorldInfo.NetMode != NM_DedicatedServer && IsLocalPlayerController() )
{
StatsWrite.OnGameWon( MapName, Difficulty, GameLength, bCoop, GetPerk().class );
}
}
reliable client function ClientRoundEnded( byte WinningTeam )
{
if( WorldInfo.NetMode != NM_DedicatedServer && IsLocalPlayerController() )
{
StatsWrite.OnRoundEnd( WinningTeam );
}
}
reliable client function ClientGameOver(string MapName, byte Difficulty, byte GameLength, byte bCoop, byte FinalWaveNum)
{
if(WorldInfo.NetMode != NM_DedicatedServer && IsLocalPlayerController())
{
StatsWrite.OnGameEnd(MapName, Difficulty, GameLength, FinalWaveNum, bCoop, GetPerk().class);
}
}
simulated function bool SeasonalEventIsValid()
{
return StatsWrite != none && StatsWrite.SeasonalEventIsValid();
}
native function bool IsValidWeeklySurvivalMatch();
final function CompletedWeeklySurvival()
{
if (IsValidWeeklySurvivalMatch())
{
ClientCompletedWeeklySurvival();
}
}
/** Triggered per-controller by KFGameInfo_WeeklySurvival when the map is completed. */
reliable final client function ClientCompletedWeeklySurvival()
{
if( WorldInfo.NetMode != NM_DedicatedServer && IsLocalPlayerController() )
{
StatsWrite.WeeklyEventComplete();
}
}
reliable final client function ClientMapObjectiveCompleted(float XPValue)
{
StatsWrite.MapObjectiveCompleted();
ClientAddPlayerXP(XPValue, GetPerk().class, true);
OnPlayerXPAdded(XPValue, GetPerk().class);
}
final simulated function SeasonalEventStats_OnMapObjectiveDeactivated(Actor ObjectiveInterfaceActor)
{
StatsWrite.SeasonalEventStats_OnMapObjectiveDeactivated(ObjectiveInterfaceActor);
}
reliable client function ClientOnTriggerUsed(class<Trigger_PawnsOnly> TriggerClass)
{
if (StatsWrite != none)
{
StatsWrite.SeasonalEventStats_OnTriggerUsed(TriggerClass);
}
}
reliable client function ClientOnTryCompleteObjective(int ObjectiveIndex, int EventIndex)
{
if (StatsWrite != none)
{
StatsWrite.SeasonalEventStats_OnTryCompleteObjective(ObjectiveIndex, EventIndex);
}
}
/**
* @brief Unlock an achievement on the client
*
* @param AchievementIndex the achievement's index
*/
reliable client event ClientUnlockAchievement( int AchievementIndex, optional bool bAlwaysUnlock=false )
{
if ( WorldInfo.NetMode != NM_DedicatedServer && IsLocalPlayerController() && (bIsAchievementPlayer || bAlwaysUnlock) &&
!PlayerReplicationInfo.bOnlySpectator && !StatsWrite.HasCheated() && !StatsWrite.IsAchievementUnlocked(AchievementIndex) )
{
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) )
{
`log("Client unlock achievement: " @AchievementIndex);
// Just toggle the stat relevent to this to on now, so the next stats write will trigger the achievement unlock.
StatsWrite.UnlockDingoAchievement(AchievementIndex);
OnlineSub.StatsInterface.WriteOnlineStats('Game', PlayerReplicationInfo.UniqueId, StatsWrite);
}
if (OnlineSub.PlayerInterface.UnlockAchievement(LocalPlayer(Player).ControllerId, AchievementIndex)) //@HSL_BEGIN - JRO - 4/28/2016 - Using the current controller ID instead of hardcoding 0, as 0 can break on console
{
StatsWrite.OnUnlockAchievement(AchievementIndex);
}
}
}
event int GetXPDeltaForPerkClass(class<KFPerk> PerkClass)
{
local int i;
if (MatchStats == none)
{
return 0;
}
for (i = 0; i < MatchStats.PerkXPList.length; i++)
{
if (PerkClass == MatchStats.PerkXPList[i].PerkClass)
{
return MatchStats.PerkXPList[i].XPDelta;
}
}
return 0;
}
function float GetPerkLevelProgressPercentage(Class<KFPerk> PerkClass, optional out int CurrentLevelEXP, optional out int NextLevelEXP)
{
local int NextEXP, CurrentEXP;
local float EXPPercent;
local byte PerkLevel;
PerkLevel = GetPerkLevelFromPerkList(PerkClass);
CurrentEXP = GetPerkXP(PerkClass);
if( PerkLevel < `MAX_PERK_LEVEL )
{
NextEXP = class'KFOnlineStatsWrite'.static.GetXPNeededAt(PerkLevel);
EXPPercent = float(CurrentEXP) / float(NextEXP);
}
else
{
EXPPercent = 1.0f;
}
CurrentLevelEXP = CurrentEXP;
NextLevelEXP = NextEXP;
return EXPPercent * 100;
}
event byte GetPerkPrestigeLevelFromPerkList(Class<KFPerk> PerkClass)
{
local int i;
for (i = 0; i < PerkList.Length; i++)
{
if (PerkList[i].PerkClass == PerkClass)
{
return PerkList[i].PrestigeLevel;
}
}
return 0;
}
event byte GetPerkLevelFromPerkList(Class<KFPerk> PerkClass)
{
local int i;
for (i = 0; i < PerkList.Length; i++)
{
if(PerkList[i].PerkClass == PerkClass)
{
return PerkList[i].PerkLevel;
}
}
return 0;
}
/** hit stat */
function NotifyHitTaken()
{
ClientNotifyHitTaken();
}
native reliable client private function ClientNotifyHitTaken();
function NotifyHitGiven(class<DamageType> DT)
{
ClientNotifyHitGiven(DT);
}
native reliable client private function ClientNotifyHitGiven(class<DamageType> DT);
/** Kill stat */
function AddZedKill( class<KFPawn_Monster> MonsterClass, byte Difficulty, class<DamageType> DT, bool bKiller )
{
ClientAddZedKill( MonsterClass, Difficulty, DT, bKiller );
}
native reliable client private function ClientAddZedKill( class<KFPawn_Monster> MonsterClass, byte Difficulty, class<DamageType> DT, bool bKiller );
function AddZedHeadshotKill( class<KFPawn_Monster> MonsterClass, byte Difficulty, class<DamageType> DT )
{
ClientAddZedHeadshotKill( MonsterClass, Difficulty, DT );
}
native reliable client private function ClientAddZedHeadshotKill( class<KFPawn_Monster> MonsterClass, byte Difficulty, class<DamageType> DT );
function AddNonZedKill(class<Pawn> KilledClass, byte Difficulty)
{
ClientAddNonZedKill(KilledClass, Difficulty);
}
native reliable client private function ClientAddNonZedKill(class<Pawn> KilledClass, byte Difficulty);
function AddWeaponPurchased( class<KFWeaponDefinition> WeaponDef, int Price )
{
ClientAddWeaponPurchased( WeaponDef, Price );
}
native reliable client private function ClientAddWeaponPurchased( class<KFWeaponDefinition> WeaponDef, int Price );
function AddZedAssist(class<KFPawn_Monster> MonsterClass)
{
ClientAddZedAssist(MonsterClass);
}
native reliable client private function ClientAddZedAssist(class<KFPawn_Monster> MonsterClass);
/** Headshot stat */
function AddZedHeadshot( byte Difficulty, class<DamageType> DT )
{
ClientAddZedHeadshot( Difficulty, DT );
}
native reliable client private function ClientAddZedHeadshot( byte Difficulty, class<DamageType> DT );
/** Perk xp stat */
native reliable client private event ClientAddPlayerXP( int XP, class<KFPerk> PerkClass, bool bApplyPrestigeBonus = false );
/** XP notification for mods (Network: Server) */
event OnPlayerXPAdded(INT XP, class<KFPerk> PerkClass);
/** Radius kill stat */
function AddSmallRadiusKill( byte Difficulty, class<KFPerk> PerkClass )
{
ClientAddSmallRadiusKill( Difficulty, PerkClass );
}
native reliable client private function ClientAddSmallRadiusKill(byte Difficulty, class<KFPerk> PerkClass);
/** Welding stat */
function AddWeldPoints( int PointsWelded )
{
ClientAddWeldPoints( PointsWelded );
}
native reliable client private function ClientAddWeldPoints(int PointsWelded);
/** Door repair secondary XP */
function DoorRepaired()
{
ClientDoorRepaired();
}
native reliable client private function ClientDoorRepaired();
/** Healing stat */
function AddHealPoints( int PointsHealed )
{
ClientAddHealPoints( PointsHealed );
}
native reliable client private function ClientAddHealPoints(int PointsHealed);
/** Damage tracked stats. Base implementation is for player vs. zed. Check weekly PC for additional variant. */
function AddTrackedDamage(int Amount, class<DamageType> DamageType, class<Pawn> DamagerClass, class<Pawn> VictimClass)
{
ClientAddTrackedDamage(Amount, DamageType);
}
native reliable client private function ClientAddTrackedDamage(int Amount, class<DamageType> DamageType);
/** Vs Survival tracked stats */
function AddTrackedVsDamage(int Amount, class<Pawn> DamagerClass)
{
ClientAddTrackedVsDamage(Amount, DamagerClass);
}
native reliable client private function ClientAddTrackedVsDamage(int Amount, class<Pawn> DamagerClass);
/** Console command to reset this users stats on steam */
exec function ResetStats(string ConfirmSteamNickInQuotes, bool bResetAchievements=false)
{
if ( IsLocalPlayerController() )
{
if ( ConfirmSteamNickInQuotes != PlayerReplicationInfo.PlayerName )
{
LocalPlayer(Player).ViewportClient.ViewportConsole.OutputText("Failed - Confirm using your Steam nickname (Case sensitive)");
return;
}
if ( OnlineSub.ResetStats(bResetAchievements) )
{
LocalPlayer(Player).ViewportClient.ViewportConsole.OutputText("Reset was successful");
}
else
{
`warn("Reset Stats Failed!");
}
}
}
protected native function int GetBenefactorDoshReq();
final exec function LogStats()
{
`if(`isdefined(ShippingPC))
if( StatsWrite != none )
{
StatsWrite.LogStats();
}
`endif
}
/*
* Enables cheats and disables stats. Used in shipping. Cheats are allowed in dev (see KFGameInfo.AllowCheats)
*/
exec function EnableCheats()
{
if( !PlayerReplicationInfo.bAdmin && WorldInfo.NetMode != NM_Standalone )
{
ReceiveLocalizedMessage(Class'KFLocalMessage', LMT_MustLoginToCheat, PlayerReplicationInfo);
return;
}
ServerEnableCheats();
}
reliable server private function ServerEnableCheats()
{
local KFGameInfo KFGI;
// set game to unranked so that players can't earn achievements or anything
KFGI = KFGameInfo(WorldInfo.Game);
if( KFGI != none )
{
AddCheats( true );
ClientNotifyCheats();
BroadcastLocalizedMessage(class'KFLocalMessage', LMT_CheatsEnabled, PlayerReplicationInfo);
KFGI.SetGameUnranked( true );
KFGI.UpdateGameSettings();
}
}
reliable client private function ClientNotifyCheats()
{
if( StatsWrite != none )
{
StatsWrite.NotifyCheats();
}
}
/** Called from native (AKFCollectibleActor::OnCollect) when a collectible (except for the final one) is found
* @param FinderPRI: the player who found the collectible
* @param CollectibleID: which collectible this is (same as number of collectibles found so far)
*/
reliable client event OnMapCollectibleFound( PlayerReplicationInfo FinderPRI, int CollectibleID )
{
local string CollectibleFoundMsg;
local KFMapInfo KFMI;
KFMI = KFMapInfo( WorldInfo.GetMapInfo() );
CollectibleFoundMsg = Localize( "KFMapInfo", "FoundCollectibleString", "KFGame" );
CollectibleFoundMsg = repl( CollectibleFoundMsg, "%x%", FinderPRI.PlayerName );
CollectibleFoundMsg = repl( CollectibleFoundMsg, "%y%", KFMI.CollectiblesToFind - CollectibleID );
MyGFxHUD.ShowNonCriticalMessage( CollectibleFoundMsg );
if (StatsWrite != none)
{
StatsWrite.SeasonalEventStats_OnMapCollectibleFound(FinderPRI, CollectibleID);
}
}
/** Called from native (AKFCollectibleActor::OnCollect) when the final collectible is found */
reliable client event OnAllMapCollectiblesFound(string MapName)
{
MyGFxHUD.ShowNonCriticalMessage( Localize("KFMapInfo", "FoundAllCollectiblesString", "KFGame") );
PostAkEvent( AllMapCollectiblesFoundEvent );
if (StatsWrite != none)
{
StatsWrite.CheckCollectibleAchievement(MapName);
}
}
/** Called from the Server */
reliable final client event OnWaveComplete(int CurrentWave)
{
if (StatsWrite != none)
{
StatsWrite.CheckEndWaveObjective(CurrentWave);
}
}
native final function UpdateBenefactor( int DoshAmount );
native final function UnlockHoldOut();
/*********************************************************************************************
* @name Debug
*********************************************************************************************/
/** Overridden to make it accessible to cheat manager and potentially dump more debug info */
native function LogOutBugItAIGoToLogFile( const String InScreenShotDesc, const string InGoString, const string InLocString );
// Displays the number of loaded objects for a particular class and its size
simulated function DisplayDebug(HUD HUD, out float out_YL, out float out_YPos)
{
local KFGameReplicationInfo KFGRI;
local KFAIController KFAIC;
super.DisplayDebug(HUD, out_YL, out_YPos);
if (HUD.ShouldDisplayDebug('Memory'))
{
DrawDebugMemory( HUD.Canvas, out_YL, out_YPos );
}
if (HUD.ShouldDisplayDebug('Difficulty'))
{
DrawDebugDifficulty( HUD.Canvas, out_YL, out_YPos );
}
if (HUD.ShouldDisplayDebug('SpawnWaves'))
{
DrawDebugSpawning( HUD.Canvas );
}
if( HUD.ShouldDisplayDebug('Map') )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI.bTrackingMapEnabled )
{
DrawDebugMap( HUD.Canvas );
}
}
if (HUD.ShouldDisplayDebug('DoorGraph'))
{
//DrawDebugDoorGraph( HUD.Canvas );
}
if (HUD.ShouldDisplayDebug('gri'))
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
KFGRI.DisplayDebug(HUD, out_YL, out_YPos);
}
}
if (HUD.ShouldDisplayDebug('time'))
{
HUD.Canvas.SetDrawColor(0,255,0);
HUD.Canvas.DrawText("TIME: WorldInfo.TimeDilation: "$WorldInfo.TimeDilation$" CustomTimeDilation: "$CustomTimeDilation);
out_YPos += out_YL;
HUD.Canvas.SetPos(4, out_YPos);
}
if (HUD.ShouldDisplayDebug('FireAttacks'))
{
foreach WorldInfo.AllControllers(class'KFAIController', KFAIC )
{
KFAIC.DrawFireAttackDebug();
}
}
if( KFPlayerInput(PlayerInput) != none )
{
KFPlayerInput(PlayerInput).DisplayDebug(HUD, out_YL, out_YPos);
}
if( HUD.ShouldDisplayDebug('Conductor') )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI.bGameConductorGraphingEnabled )
{
DrawDebugConductor( HUD.Canvas );
}
}
}
function DrawDebugMemory( Canvas Canvas, out float out_YL, out float out_YPos)
{
local int i, ClassCount;
local float ClassSize, ResourceSize;
local array< class<Object> > MemoryArray;
// Add any class to MemoryArray for its information to be displayed
MemoryArray.AddItem(class'KFWeapon');
MemoryArray.AddItem(class'KFWeaponAttachment');
MemoryArray.AddItem(class'KFPawn_Human');
MemoryArray.AddItem(class'KFPawn_Monster');
MemoryArray.AddItem(class'Projectile');
Canvas.SetDrawColor(0,255,0);
Canvas.SetPos(4,out_YPos);
Canvas.DrawText("---------- KFPlayerController: Memory ----------");
out_YPos += out_YL;
for ( i = 0; i < MemoryArray.Length; i++ )
{
ClassCount = 0;
ClassSize = 0;
ResourceSize = 0;
( i % 2 == 0 ) ? Canvas.SetDrawColor(0,255,0) : Canvas.SetDrawColor(180,255,0);
class'KFGameEngine'.static.GetClassCountAndSize( ClassCount, ClassSize, ResourceSize, MemoryArray[i] );
Canvas.SetPos(4,out_YPos);
Canvas.DrawText(MemoryArray[i].Name $":");
Canvas.SetPos(154,out_YPos);
Canvas.DrawText("# Objects = "$ClassCount);
Canvas.SetPos(254,out_YPos);
Canvas.DrawText("Class Size = "$ClassSize $"KB");
Canvas.SetPos(404,out_YPos);
Canvas.DrawText("True Resource Size = "$ResourceSize $"MB");
out_YPos += out_YL;
}
}
/** Draw out the global difficulty modifiers */
function DrawDebugDifficulty( Canvas Canvas, out float out_YL, out float out_YPos)
{
local KFGameInfo KFGI;
local byte NumLivingPlayers, NumPlayers;
KFGI = KFGameInfo( WorldInfo.Game );
if ( KFGI != none && KFGI.DifficultyInfo != none )
{
NumLivingPlayers = KFGI.GetLivingPlayerCount();
NumPlayers = KFGI.GetNumPlayers();
Canvas.SetDrawColor(0,255,255);
Canvas.SetPos(4,out_YPos);
Canvas.DrawText( "---------- KFPlayerController: Difficulty ----------" );
DrawNextDebugLine( Canvas, out_YL, out_YPos, false, "Current Difficulty: (" $KFGI.GameDifficulty@")" );
DrawNextDebugLine( Canvas, out_YL, out_YPos, true, "Global Health Mod: " @KFGI.DifficultyInfo.GetGlobalHealthMod() );
DrawNextDebugLine( Canvas, out_YL, out_YPos, true, "Ground Speed Mod: " @KFGI.DifficultyInfo.GetAIGroundSpeedMod() );
DrawNextDebugLine( Canvas, out_YL, out_YPos, false, "Difficulty Wave Count Mod: " @KFGI.DifficultyInfo.GetDifficultyMaxAIModifier() );
DrawNextDebugLine( Canvas, out_YL, out_YPos, true, "Dosh Per Kill Mod: " @KFGI.DifficultyInfo.GetKillCashModifier() );
DrawNextDebugLine( Canvas, out_YL, out_YPos, false, "Starting Dosh: " @KFGI.DifficultyInfo.GetAdjustedStartingCash() );
DrawNextDebugLine( Canvas, out_YL, out_YPos, true, "Respawn Dosh: " @KFGI.DifficultyInfo.GetAdjustedRespawnCash() );
DrawNextDebugLine( Canvas, out_YL, out_YPos, false, "Active Ammo Pickups Mod: " @KFGI.DifficultyInfo.GetAmmoPickupModifier() );
DrawNextDebugLine( Canvas, out_YL, out_YPos, true, "Active Weapon Pickups Mod: " @KFGI.DifficultyInfo.GetItemPickupModifier() );
Canvas.SetDrawColor(0,255,255);
Canvas.DrawText( "---------- NumPlayer Modifiers ----------" );
DrawNextDebugLine( Canvas, out_YL, out_YPos, false,"Num Living Players: " @NumLivingPlayers );
DrawNextDebugLine( Canvas, out_YL, out_YPos, true, "Hidden AI Movement Speed Mod: " @KFGI.DifficultyInfo.GetAIHiddenSpeedModifier( NumLivingPlayers) );
DrawNextDebugLine( Canvas, out_YL, out_YPos, false,"Num Players Wave Count Mod: " @KFGI.DifficultyInfo.GetPlayerNumMaxAIModifier( NumPlayers ) );
DrawNextDebugLine( Canvas, out_YL, out_YPos, true, "Ammo Pickups Respawn Time: " @KFGI.DifficultyInfo.GetAmmoPickupInterval( NumLivingPlayers ) );
DrawNextDebugLine( Canvas, out_YL, out_YPos, false,"Weapon Pickups Respawn Time: " @KFGI.DifficultyInfo.GetWeaponPickupInterval( NumLivingPlayers ) );
}
}
function DrawNextDebugLine( out Canvas Canvas, out float out_YL, out float out_YPos, bool bAlternate, string DebugString, optional float XPos )
{
( !bAlternate ) ? Canvas.SetDrawColor(0,255,0) : Canvas.SetDrawColor(180,255,0);
out_YPos += out_YL;
if ( XPos != 0 )
{
Canvas.SetPos( XPos, out_YPos );
}
Canvas.DrawText( DebugString );
}
function DrawDebugSpawning( out Canvas Canvas )
{
local float Buffer, XPos, YPos, BackgroundScale, MarkerScale;
local float BackgroundWidth, BackgroundHeight, HalfMarkerWidth, HalfMarkerHeight;
local float CompleteCycle;
local byte NumCycles;
local KFGameReplicationInfo KFGRI;
local Texture2D SineTex;
local Texture2D MarkerTex;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI == none )
{
return;
}
SineTex = Texture2D'GP_Debug.AbsSine_TEX';
MarkerTex = Texture2D'GP_Debug.SineWaveMarker_TEX';
BackgroundScale = 0.5f;
MarkerScale = 0.75f * BackgroundScale;
BackgroundWidth = SineTex.SizeX * BackgroundScale;
BackgroundHeight = SineTex.SizeY * BackgroundScale;
HalfMarkerWidth = MarkerTex.SizeX * MarkerScale / 2;
HalfMarkerHeight = MarkerTex.SizeY * MarkerScale / 2;
Buffer = 8;
XPos = Canvas.ClipX - ( SineTex.SizeX * BackgroundScale ) - Buffer;
YPos = Buffer;
NumCycles = 1;
CompleteCycle = 6.28f / KFGRI.CurrentSineWavFreq; // ( 2Pi / The Frequency ) gives us the value for a complete cycle
// Draw Spawn Time Info
DrawNextSpawnTimeInfo( Canvas, XPos, BackgroundHeight, Buffer * 2 );
// Draw background
Canvas.SetPos(Xpos, YPos);
Canvas.SetDrawColor(255,255,255);
Canvas.DrawTexture( SineTex, BackgroundScale );
// Draw Marker
XPos += ( ( ( KFGRI.CurrentTotalWavesActiveTime / CompleteCycle ) % NumCycles ) * BackgroundWidth ) - HalfMarkerWidth;
YPos += ( ( KFGRI.CurrentSineMod ) * BackgroundHeight ) - HalfMarkerHeight;
Canvas.SetPos( Xpos, YPos );
Canvas.DrawTexture( MarkerTex, MarkerScale );
}
/** Draw the tracking map */
function DrawDebugMap( out Canvas Canvas )
{
`if(`notdefined(ShippingPC))
local Texture2D OverheadMap;
local float XPos, YPos, MarkerScale;
local float HalfMarkerWidth, HalfMarkerHeight;
local KFGameReplicationInfo KFGRI;
local float AdjustedSize;
local float ScreenScale;
local vector2d CenterLocation, UpperLeftLocation;
local Texture2D MarkerTex;
local int i;
local class<KFPawn> ZedClass;
local vector ZedLocation;
local bool bZedUsingSuperSpeed;
local vector MyLocation;
local color ElementColor;
local vector EnemyLocation;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( !KFGRI.bTrackingMapEnabled )
{
return;
}
OverheadMap = Texture2D'GP_Debug.MapTest';
if( Pawn != none )
{
MyLocation = Pawn.Location;
}
else
{
MyLocation = Location;
}
ScreenScale = Canvas.ClipY/900.0;
AdjustedSize = OverheadMap.GetSurfaceHeight() * ScreenScale * TrackingMapScale;
XPos = TrackerXPosition * Canvas.ClipX;
YPos = TrackerYPosition * Canvas.ClipY;
UpperLeftLocation.X = XPos;
UpperLeftLocation.Y = YPos;
// Draw background
Canvas.SetPos(Xpos, YPos);
Canvas.SetDrawColor(255,255,255,128);
Canvas.DrawTexture( OverheadMap, ScreenScale * TrackingMapScale);
CenterLocation.X = XPos + (AdjustedSize * 0.5);
CenterLocation.Y = YPos + (AdjustedSize * 0.5);
// Draw Icon where player is
MarkerTex = class'KFPawn_Human'.default.DebugRadarTexture;
MarkerScale = ScreenScale * TrackingMapScale * 0.25;
HalfMarkerWidth = MarkerTex.SizeX * MarkerScale / 2;
HalfMarkerHeight = MarkerTex.SizeY * MarkerScale / 2;
XPos = CenterLocation.X - HalfMarkerWidth;
YPos = CenterLocation.Y - HalfMarkerHeight;
// Center
Canvas.SetDrawColor(0,255,0,255);
Canvas.SetPos( Xpos, YPos );
Canvas.DrawTexture( MarkerTex, MarkerScale );
Canvas.SetDrawColor(255,255,255);
if ( KFGRI != none )
{
// Draw Zeds
if( CurrentTrackingMode != ETM_SpawnsOnly && CurrentTrackingMode != ETM_FailedSpawnsOnly
&& CurrentTrackingMode != ETM_PickupsOnly )
{
if( CurrentTrackingMode != ETM_HumansAndSpawnsOnly )
{
for (i = 0; i < ArrayCount(KFGRI.ZedInfos); i++)
{
if( KFGRI.ZedInfos[i].Zed != none )
{
ZedLocation = KFGRI.ZedInfos[i].Zed.Location;
ZedClass = KFGRI.ZedInfos[i].Zed.class;
bZedUsingSuperSpeed = KFGRI.ZedInfos[i].bUsingSuperSpeed;
}
else if( !IsZero(KFGRI.ZedInfos[i].ZedLocation) )
{
ZedLocation = KFGRI.ZedInfos[i].ZedLocation;
ZedClass = KFGRI.ZedInfos[i].ZedClass;
bZedUsingSuperSpeed = KFGRI.ZedInfos[i].bUsingSuperSpeed;
}
else
{
continue;
}
// Don't draw targets if we want pawns only
if( CurrentTrackingMode != ETM_PawnsOnly && CurrentTrackingMode != ETM_AllButTargeting )
{
if( KFGRI.ZedInfos[i].Enemy != none )
{
EnemyLocation = KFGRI.ZedInfos[i].Enemy.Location;
}
else if( !IsZero(KFGRI.ZedInfos[i].EnemyLocation) )
{
EnemyLocation = KFGRI.ZedInfos[i].EnemyLocation;
}
}
if( !IsZero(KFGRI.ZedInfos[i].LastTeleportLocation) )
{
// Orange, teleported
ElementColor.R=255;
ElementColor.G=128;
ElementColor.B=0;
// Don't render as superspeed if we just teleported since we'll be orange instead
bZedUsingSuperSpeed=false;
}
else
{
// Standard Zed Color, Yellow
ElementColor.R=255;
ElementColor.G=255;
ElementColor.B=0;
}
DrawMapElement(Canvas, ScreenScale, AdjustedSize, CenterLocation, MyLocation, ZedLocation, ZedClass, ElementColor, bZedUsingSuperSpeed, true,,EnemyLocation);
if( !IsZero(KFGRI.ZedInfos[i].LastTeleportLocation) )
{
// Blue, teleported from
ElementColor.R=0;
ElementColor.G=255;
ElementColor.B=255;
DrawMapElement(Canvas, ScreenScale, AdjustedSize, CenterLocation, MyLocation, KFGRI.ZedInfos[i].LastTeleportLocation, ZedClass, ElementColor, false, true,,ZedLocation);
}
}
}
// Draw Players
for (i = 0; i < ArrayCount(KFGRI.HumanInfos); i++)
{
if( KFGRI.HumanInfos[i].Human != none )
{
if( KFGRI.HumanInfos[i].Human == Pawn )
{
continue;
}
ZedLocation = KFGRI.HumanInfos[i].Human.Location;
ZedClass = KFGRI.HumanInfos[i].Human.class;
}
else if( !IsZero(KFGRI.HumanInfos[i].HumanLocation) )
{
ZedLocation = KFGRI.HumanInfos[i].HumanLocation;
ZedClass = KFGRI.HumanInfos[i].HumanClass;
}
else
{
continue;
}
// Standard Human color, Light blue for other players
ElementColor.R=0;
ElementColor.G=255;
ElementColor.B=255;
DrawMapElement(Canvas, ScreenScale, AdjustedSize, CenterLocation, MyLocation, ZedLocation, ZedClass, ElementColor, false, true);
}
}
if( CurrentTrackingMode != ETM_PawnsOnly && CurrentTrackingMode != ETM_PawnsAndTargetingOnly
&& CurrentTrackingMode != ETM_PickupsOnly )
{
if( CurrentTrackingMode != ETM_FailedSpawnsOnly )
{
// Draw Spawn Volumes
for (i = 0; i < ArrayCount(KFGRI.SpawnVolumeInfos); i++)
{
if( IsZero(KFGRI.SpawnVolumeInfos[i].VolumeLocation) )
{
continue;
}
if( KFGRI.SpawnVolumeInfos[i].bPortalSpawn )
{
// Portal spawns are orange
ElementColor.R=255;
ElementColor.G=128;
ElementColor.B=0;
ElementColor.A = 64 + (191 * float(KFGRI.SpawnVolumeInfos[i].VolumeAge)/255);
}
else
{
// Spawn volumes are white
ElementColor.R=255;
ElementColor.G=255;
ElementColor.B=255;
ElementColor.A = 255 * float(KFGRI.SpawnVolumeInfos[i].VolumeAge)/255;//64 + (191 * float(KFGRI.SpawnVolumeInfos[i].VolumeAge)/255);
}
DrawMapElement(Canvas, ScreenScale, AdjustedSize, CenterLocation, MyLocation, KFGRI.SpawnVolumeInfos[i].VolumeLocation, none, ElementColor, false, true, true);
}
}
// Draw Failed spawns
for (i = 0; i < ArrayCount(KFGRI.FailedSpawnInfos); i++)
{
if( IsZero(KFGRI.FailedSpawnInfos[i].VolumeLocation) )
{
continue;
}
if( KFGRI.FailedSpawnInfos[i].bPortalSpawn )
{
// Portal spawns are orange
ElementColor.R=255;
ElementColor.G=128;
ElementColor.B=0;
}
else
{
// Spawn volumes are white
ElementColor.R=255;
ElementColor.G=255;
ElementColor.B=255;
}
DrawMapElement(Canvas, ScreenScale, AdjustedSize, CenterLocation, MyLocation, KFGRI.FailedSpawnInfos[i].VolumeLocation, none, ElementColor, false, true, false,, true);
}
}
// Draw weapon pickup info
if( CurrentTrackingMode == ETM_PickupsOnly || CurrentTrackingMode == ETM_All
|| CurrentTrackingMode == ETM_AllButTargeting )
{
for (i = 0; i < ArrayCount(KFGRI.PickupInfos); i++)
{
if( IsZero(KFGRI.PickupInfos[i].PickupLocation) )
{
continue;
}
if( KFGRI.PickupInfos[i].PickupType == 0 )
{
// Ammo pickups are green
ElementColor.R=0;
ElementColor.G=255;
ElementColor.B=0;
ElementColor.A = 255;
}
else if( KFGRI.PickupInfos[i].PickupType == 1)
{
// Weapon pickups are red
ElementColor.R=255;
ElementColor.G=0;
ElementColor.B=0;
ElementColor.A = 255;
}
else if( KFGRI.PickupInfos[i].PickupType == 2 )
{
// Armor pickups are blue
ElementColor.R=0;
ElementColor.G=0;
ElementColor.B=255;
ElementColor.A = 255;
}
else
{
Continue;
}
DrawMapElement(Canvas, ScreenScale, AdjustedSize, CenterLocation, MyLocation, KFGRI.PickupInfos[i].PickupLocation, none, ElementColor, false, true, true);
}
}
}
// Draw the map key info
Canvas.SetDrawColor(0,255,0);
Canvas.SetPos(UpperLeftLocation.X + (AdjustedSize * 0.01), UpperLeftLocation.Y + (AdjustedSize * 0.97));
Canvas.DrawText( "Tracking Range: "$int(TrackingMapRange/100)$" Meters" );
Canvas.SetPos(UpperLeftLocation.X + (AdjustedSize * 0.01), UpperLeftLocation.Y + (AdjustedSize * 0.01));
Canvas.DrawText( "Tracking Mode: "$CurrentTrackingMode );
Canvas.SetPos(UpperLeftLocation.X + (AdjustedSize * 0.75), UpperLeftLocation.Y + (AdjustedSize * 0.01));
if( bTrackingMapTopView )
{
Canvas.DrawText( "Tracking View: Top Down" );
}
else
{
Canvas.DrawText( "Tracking View: Side View" );
}
`endif
}
/** Draw elements of the tracking map */
simulated function DrawMapElement(Canvas Canvas,
float ScreenScale,
float AdjustedMapSize,
vector2d CenterLocation,
vector MapHolderLocation,
vector ElementLocation,
class<KFPawn> ElementClass,
Color IconColor,
bool bUsingSuperSpeed,
bool bDrawHeightArrows,
optional bool bBox,
optional vector EnemyLocation,
optional bool bFailed)
{
`if(`notdefined(ShippingPC))
local float MarkerScale;
local float HalfMarkerWidth, HalfMarkerHeight;
local vector VectToMonster;
local vector2d MonsterLocation;
local rotator ViewRotation;
local float ZedDist, ZedDistScale;
local Texture2D MarkerTex;
local Texture2D ArrowUp, ArrowDown;
local float HalfArrowHeight;
local float MarkerHeight;
local float HeightDiff;
local byte MarkerAlpha;
local vector VectToEnemyLoc;
local float EnemyDist, EnemyDistScale;
ArrowUp=Texture2D'UI_ZEDRadar_TEX.MapIcon_Up';
ArrowDown=Texture2D'UI_ZEDRadar_TEX.MapIcon_Down';
VectToMonster = ElementLocation - MapHolderLocation;
HeightDiff = VectToMonster.Z;
if( bTrackingMapTopView )
{
// Only concerned with X and Y for the overhead map, top down view
VectToMonster.Z = 0;
}
else
{
// Only concerned with Z and Y for the side view map
VectToMonster.Y = 0;
}
ZedDist = VSize(VectToMonster);
if( ZedDist > TrackingMapRange )
{
ZedDistScale = 1.0;
if( IconColor.A > 0 )
{
MarkerAlpha = Min(128,IconColor.A);
}
else
{
MarkerAlpha = 128;
}
}
else
{
ZedDistScale = ZedDist/TrackingMapRange;
MarkerAlpha = 255;
if( IconColor.A > 0 )
{
MarkerAlpha = Min(255,IconColor.A);
}
else
{
MarkerAlpha = 255;
}
}
ViewRotation = Rotation;
ViewRotation.Yaw *= -1;
ViewRotation.Pitch = 0;
VectToMonster = Normal(VectToMonster >> ViewRotation) * AdjustedMapSize * 0.5 * ZedDistScale;
if( bTrackingMapTopView )
{
// Set up the coordinates for top down view map
MonsterLocation.X = VectToMonster.Y;
MonsterLocation.Y = VectToMonster.X * -1;
}
else
{
// Set up the coordinates for the side view map
MonsterLocation.X = VectToMonster.X;
MonsterLocation.Y = VectToMonster.Z * -1;
}
MonsterLocation *= ScreenScale;
MonsterLocation = CenterLocation + MonsterLocation;
if( !IsZero(EnemyLocation ) )
{
VectToEnemyLoc = EnemyLocation - MapHolderLocation;
if( bTrackingMapTopView )
{
// Only concerned with X and Y for the overhead map, top down view
VectToEnemyLoc.Z = 0;
}
else
{
// Only concerned with Z and Y for the side view map
VectToEnemyLoc.Y = 0;
}
EnemyDist = VSize(VectToEnemyLoc);
if( EnemyDist > TrackingMapRange )
{
EnemyDistScale = 1.0;
}
else
{
EnemyDistScale = EnemyDist/TrackingMapRange;
}
VectToEnemyLoc = Normal(VectToEnemyLoc >> ViewRotation) * AdjustedMapSize * 0.5 * EnemyDistScale;
if( bTrackingMapTopView )
{
// Set up the coordinates for top down view map
EnemyLocation.X = VectToEnemyLoc.Y;
EnemyLocation.Y = VectToEnemyLoc.X * -1;
}
else
{
// Set up the coordinates for the side view map
EnemyLocation.X = VectToEnemyLoc.X;
EnemyLocation.Y = VectToEnemyLoc.Z * -1;
}
EnemyLocation *= ScreenScale;
EnemyLocation.X = CenterLocation.X + EnemyLocation.X;
EnemyLocation.Y = CenterLocation.Y + EnemyLocation.Y;
// Blue icon with "EnemyLocation" is teleport, so draw a different color line
if( IconColor.R == 0 && IconColor.G == 255 && IconColor.B == 255 )
{
Canvas.Draw2DLine(EnemyLocation.X, EnemyLocation.Y, MonsterLocation.X, MonsterLocation.Y, MakeColor(0,255,255,128));
}
else
{
// red line for enemy
Canvas.Draw2DLine(EnemyLocation.X, EnemyLocation.Y, MonsterLocation.X, MonsterLocation.Y, MakeColor(255,0,0,128));
}
}
// Draw Element Marker
if( bFailed )
{
MarkerTex = Texture2D'UI_ZEDRadar_TEX.MapIcon_FailedSpawn';
}
else if( ElementClass != none )
{
MarkerTex = ElementClass.default.DebugRadarTexture;
}
MarkerScale = ScreenScale * TrackingMapScale * 0.25;
if( MarkerTex != none )
{
HalfMarkerWidth = MarkerTex.SizeX * MarkerScale / 2;
HalfMarkerHeight = MarkerTex.SizeY * MarkerScale / 2;
MarkerHeight = MarkerTex.SizeY * MarkerScale;
}
else if( bBox )
{
HalfMarkerWidth = TrackerSpawnVolumeSizeX * MarkerScale / 2;
HalfMarkerHeight = TrackerSpawnVolumeSizeY * MarkerScale / 2;
MarkerHeight = TrackerSpawnVolumeSizeY * MarkerScale;
}
if( bUsingSuperSpeed )
{
// red if using super speed
Canvas.SetDrawColor(255,0,0,MarkerAlpha);
}
else
{
// yellow if not using super speed
Canvas.SetDrawColor(IconColor.R,IconColor.G,IconColor.B,MarkerAlpha);
}
// Draw a box if it's a box
if( bBox )
{
Canvas.SetPos( MonsterLocation.X - (TrackerSpawnVolumeSizeX * 0.5), MonsterLocation.Y - (TrackerSpawnVolumeSizeY * 0.5));
Canvas.DrawBox(TrackerSpawnVolumeSizeX * ScreenScale, TrackerSpawnVolumeSizeY * ScreenScale);
}
// Draw an icon if it's a pawn
else
{
Canvas.SetPos( MonsterLocation.X - HalfMarkerWidth, MonsterLocation.Y - HalfMarkerHeight );
Canvas.DrawTexture( MarkerTex, MarkerScale );
}
if( bDrawHeightArrows )
{
HalfArrowHeight = ArrowUp.SizeY * MarkerScale / 2;
if( HeightDiff > 300 ) // 3 meters
{
Canvas.SetDrawColor(255,255,255,MarkerAlpha);
Canvas.SetPos( MonsterLocation.X - HalfMarkerWidth, MonsterLocation.Y );
Canvas.DrawTexture( ArrowUp, MarkerScale );
}
else if (HeightDiff < -300 )
{
Canvas.SetDrawColor(255,255,255,MarkerAlpha);
Canvas.SetPos( MonsterLocation.X - HalfMarkerWidth, (MonsterLocation.Y - MarkerHeight) + HalfArrowHeight);
Canvas.DrawTexture( ArrowDown, MarkerScale );
}
}
`endif
}
/** Draw the tracking map */
function DrawDebugConductor( out Canvas Canvas )
{
`if(`notdefined(ShippingPC))
local KFGameReplicationInfo KFGRI;
local array<vector2d> Points;
local int i;
local Float ScaleX;
local float UL_X, UL_Y, W, H;
local float MaxLifeSpan;
local float BaseLinePlayerSkill, HighSkill, LowSkill;
local float MaxGlobalSkill;
if( CurrentGameConductorDebugMode == EGCDM_Skill )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
for( i = 0; i < ArrayCount(KFGRI.PlayerAccuracyTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.PlayerAccuracyTracker)),(100.0 - KFGRI.PlayerAccuracyTracker[i]));
}
}
// Scale the graph to the screen size
ScaleX = Canvas.ClipX/1920.0;
// Set the location and size of the graph
UL_X = 1350 * ScaleX;
UL_Y = 100 * ScaleX;
W = 500 * ScaleX;
H = 250 * ScaleX;
Canvas.DrawDebugGraphBackground("Weapon Accuracy", UL_X, UL_Y, W, H);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(255, 255, 0, 255), "Hit %", 0,FFloor(KFGRI.PlayerAccuracyTracker[i-1])$"%");
if( KFGRI != none )
{
Points.Length = 0;
for( i = 0; i < ArrayCount(KFGRI.PlayerHeadshotAccuracyTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.PlayerHeadshotAccuracyTracker)),(100.0 - KFGRI.PlayerHeadshotAccuracyTracker[i]));
}
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(255, 0, 0, 255), "HeadShot %", 50 * ScaleX,FFloor(KFGRI.PlayerHeadshotAccuracyTracker[i-1])$"%");
if( KFGRI != none )
{
Points.Length = 0;
for( i = 0; i < ArrayCount(KFGRI.AggregatePlayerSkillTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.AggregatePlayerSkillTracker)),(100.0 - KFGRI.AggregatePlayerSkillTracker[i]));
}
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(0, 255, 255, 255), "Skill Rank %", 150 * ScaleX,FFloor(KFGRI.AggregatePlayerSkillTracker[i-1])$"%");
Points.Length = 0;
Points[Points.Length] = vect2d(0,100.0 - class'KFGameConductor'.default.ParShotAccuracy);
Points[Points.Length] = vect2d(100,100.0 - class'KFGameConductor'.default.ParShotAccuracy);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(255, 255, 255, 128), "Par Shot%", 250 * ScaleX,FFloor(class'KFGameConductor'.default.ParShotAccuracy)$"%");
Points.Length = 0;
Points[Points.Length] = vect2d(0,100.0 - class'KFGameConductor'.default.ParHeadshotAccuracy);
Points[Points.Length] = vect2d(100,100.0 - class'KFGameConductor'.default.ParHeadshotAccuracy);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(128, 128, 128, 128), "Par HeadShot%", 350 * ScaleX,FFloor(class'KFGameConductor'.default.ParHeadshotAccuracy)$"%");
}
else if( CurrentGameConductorDebugMode == EGCDM_OverallAccuracy )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
BaseLinePlayerSkill = class'KFGameConductor'.default.ParShotAccuracy * class'KFGameConductor'.default.ShootingAccuracySkillWeight + class'KFGameConductor'.default.ParHeadshotAccuracy * class'KFGameConductor'.default.HeadShootingAccuracySkillWeight;
HighSkill = BaseLinePlayerSkill * class'KFGameConductor'.default.HighlySkilledAccuracyMod;
LowSkill = BaseLinePlayerSkill * class'KFGameConductor'.default.LessSkilledAccuracyMod;
if( MaxGlobalSkill < BaseLinePlayerSkill )
{
MaxGlobalSkill = BaseLinePlayerSkill;
}
if( MaxGlobalSkill < HighSkill )
{
MaxGlobalSkill = HighSkill;
}
if( KFGRI != none )
{
for( i = 0; i < ArrayCount(KFGRI.AggregatePlayerSkillTracker); i++ )
{
if( MaxGlobalSkill < KFGRI.AggregatePlayerSkillTracker[i] )
{
MaxGlobalSkill = KFGRI.AggregatePlayerSkillTracker[i];
}
}
for( i = 0; i < ArrayCount(KFGRI.AggregatePlayerSkillTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.AggregatePlayerSkillTracker)),(MaxGlobalSkill - KFGRI.AggregatePlayerSkillTracker[i]));
}
}
// Scale the graph to the screen size
ScaleX = Canvas.ClipX/1920.0;
// Set the location and size of the graph
UL_X = 1350 * ScaleX;
UL_Y = 100 * ScaleX;
W = 500 * ScaleX;
H = 250 * ScaleX;
Canvas.DrawDebugGraphBackground("Overall Accuracy", UL_X, UL_Y, W, H);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxGlobalSkill), MakeColor(128, 255, 0, 255), "Avg Skill", 0,FFloor(KFGRI.AggregatePlayerSkillTracker[i-1]));
Points.Length = 0;
Points[Points.Length] = vect2d(0,MaxGlobalSkill - BaseLinePlayerSkill);
Points[Points.Length] = vect2d(100,MaxGlobalSkill - BaseLinePlayerSkill);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxGlobalSkill), MakeColor(255, 255, 255, 255), "Base Skill", 75 * ScaleX,FFloor(BaseLinePlayerSkill));
Points.Length = 0;
Points[Points.Length] = vect2d(0,MaxGlobalSkill - HighSkill);
Points[Points.Length] = vect2d(100,MaxGlobalSkill - HighSkill);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxGlobalSkill), MakeColor(255, 0, 0, 255), "High Skill", 175 * ScaleX,FFloor(HighSkill));
Points.Length = 0;
Points[Points.Length] = vect2d(0,MaxGlobalSkill - LowSkill);
Points[Points.Length] = vect2d(100,MaxGlobalSkill - LowSkill);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxGlobalSkill), MakeColor(255, 255, 0, 255), "Low Skill", 250 * ScaleX,FFloor(LowSkill));
}
else if( CurrentGameConductorDebugMode == EGCDM_LifeSpan )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
for( i = 0; i < ArrayCount(KFGRI.TotalZedLifeSpanAverageTracker); i++ )
{
if( MaxLifeSpan < KFGRI.TotalZedLifeSpanAverageTracker[i] )
{
MaxLifeSpan = KFGRI.TotalZedLifeSpanAverageTracker[i];
}
}
for( i = 0; i < ArrayCount(KFGRI.CurrentWaveZedLifeSpanAverageTracker); i++ )
{
if( MaxLifeSpan < KFGRI.CurrentWaveZedLifeSpanAverageTracker[i] )
{
MaxLifeSpan = KFGRI.CurrentWaveZedLifeSpanAverageTracker[i];
}
}
for( i = 0; i < ArrayCount(KFGRI.RecentZedLifeSpanAverageTracker); i++ )
{
if( MaxLifeSpan < KFGRI.RecentZedLifeSpanAverageTracker[i] )
{
MaxLifeSpan = KFGRI.RecentZedLifeSpanAverageTracker[i];
}
}
if( MaxLifeSpan < KFGRI.CurrentParZedLifeSpan )
{
MaxLifeSpan = KFGRI.CurrentParZedLifeSpan;
}
if( MaxLifeSpan < (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanHighlySkilledThreshold) )
{
MaxLifeSpan = (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanHighlySkilledThreshold);
}
if( MaxLifeSpan < (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanLessSkilledThreshold) )
{
MaxLifeSpan = (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanLessSkilledThreshold);
}
for( i = 0; i < ArrayCount(KFGRI.TotalZedLifeSpanAverageTracker); i++ )
{
if( MaxLifeSpan < KFGRI.TotalZedLifeSpanAverageTracker[i] )
{
MaxLifeSpan = KFGRI.TotalZedLifeSpanAverageTracker[i];
}
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.TotalZedLifeSpanAverageTracker)),(MaxLifeSpan - KFGRI.TotalZedLifeSpanAverageTracker[i]));
}
}
// Scale the graph to the screen size
ScaleX = Canvas.ClipX/1920.0;
// Set the location and size of the graph
UL_X = 1350 * ScaleX;
UL_Y = 100 * ScaleX;
W = 500 * ScaleX;
H = 250 * ScaleX;
Canvas.DrawDebugGraphBackground("Zed Lifespan Average", UL_X, UL_Y, W, H);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(0, 255, 0, 255), "Total", 0,KFGRI.TotalZedLifeSpanAverageTracker[i-1]$" AVG");
if( KFGRI != none )
{
Points.Length = 0;
for( i = 0; i < ArrayCount(KFGRI.CurrentWaveZedLifeSpanAverageTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.CurrentWaveZedLifeSpanAverageTracker)),(MaxLifeSpan - KFGRI.CurrentWaveZedLifeSpanAverageTracker[i]));
}
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(255, 128, 0, 255), "Current Wave", 50 * ScaleX,KFGRI.CurrentWaveZedLifeSpanAverageTracker[i-1]$" AVG");
if( KFGRI != none )
{
Points.Length = 0;
for( i = 0; i < ArrayCount(KFGRI.RecentZedLifeSpanAverageTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.RecentZedLifeSpanAverageTracker)),(MaxLifeSpan - KFGRI.RecentZedLifeSpanAverageTracker[i]));
}
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(0, 255, 255, 255), "Rolling AVG", 135 * ScaleX,KFGRI.RecentZedLifeSpanAverageTracker[i-1]$" AVG");
Points.Length = 0;
Points[Points.Length] = vect2d(0,MaxLifeSpan - KFGRI.CurrentParZedLifeSpan);
Points[Points.Length] = vect2d(100,MaxLifeSpan - KFGRI.CurrentParZedLifeSpan);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(255, 255, 255, 255), "Base LifeSpan", 225 * ScaleX,FFloor(KFGRI.CurrentParZedLifeSpan)$"S");
Points.Length = 0;
Points[Points.Length] = vect2d(0,MaxLifeSpan - (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanHighlySkilledThreshold));
Points[Points.Length] = vect2d(100,MaxLifeSpan - (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanHighlySkilledThreshold));
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(255, 0, 0, 255), "High Skill", 325 * ScaleX,FFloor((KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanHighlySkilledThreshold))$"S");
Points.Length = 0;
Points[Points.Length] = vect2d(0,MaxLifeSpan - (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanLessSkilledThreshold));
Points[Points.Length] = vect2d(100,MaxLifeSpan - (KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanLessSkilledThreshold));
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(255, 255, 0, 255), "Low Skill", 425 * ScaleX,FFloor((KFGRI.CurrentParZedLifeSpan * class'KFGameConductor'.default.ZedLifeSpanLessSkilledThreshold))$"S");
}
else if( CurrentGameConductorDebugMode == EGCDM_Status )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
for( i = 0; i < ArrayCount(KFGRI.PlayersHealthStatusTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.PlayersHealthStatusTracker)),(100.0 - KFGRI.PlayersHealthStatusTracker[i] * 100.0));
}
}
// Scale the graph to the screen size
ScaleX = Canvas.ClipX/1920.0;
// Set the location and size of the graph
UL_X = 1350 * ScaleX;
UL_Y = 100 * ScaleX;
W = 500 * ScaleX;
H = 250 * ScaleX;
Canvas.DrawDebugGraphBackground("Players Status", UL_X, UL_Y, W, H);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(255, 0, 0, 255), "Health Status", 0,FFloor(KFGRI.PlayersHealthStatusTracker[i-1] * 100.0)$" Health");
if( KFGRI != none )
{
Points.Length = 0;
for( i = 0; i < ArrayCount(KFGRI.PlayersAmmoStatusTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.PlayersAmmoStatusTracker)),(100.0 - KFGRI.PlayersAmmoStatusTracker[i] * 100));
}
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(255, 255, 0, 255), "Ammo Status", 120 * ScaleX,FFloor(KFGRI.PlayersAmmoStatusTracker[i-1] * 100.0)$" Ammo");
if( KFGRI != none )
{
Points.Length = 0;
for( i = 0; i < ArrayCount(KFGRI.AggregatePlayersStatusTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.AggregatePlayersStatusTracker)),(100.0 - KFGRI.AggregatePlayersStatusTracker[i] * 100));
}
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(0, 255, 255, 255), "Overall Status", 240 * ScaleX,FFloor(KFGRI.AggregatePlayersStatusTracker[i-1] * 100.0)$" Overall");
}
else if( CurrentGameConductorDebugMode == EGCDM_GameplayAdjustments )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
for( i = 0; i < ArrayCount(KFGRI.ZedMovementSpeedModifierTracker); i++ )
{
if( MaxLifeSpan < KFGRI.ZedMovementSpeedModifierTracker[i] )
{
MaxLifeSpan = KFGRI.ZedMovementSpeedModifierTracker[i];
}
}
for( i = 0; i < ArrayCount(KFGRI.ZedSpawnRateModifierTracker); i++ )
{
if( MaxLifeSpan < KFGRI.ZedSpawnRateModifierTracker[i] )
{
MaxLifeSpan = KFGRI.ZedSpawnRateModifierTracker[i];
}
}
for( i = 0; i < ArrayCount(KFGRI.ZedMovementSpeedModifierTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.ZedMovementSpeedModifierTracker)),(MaxLifeSpan - KFGRI.ZedMovementSpeedModifierTracker[i]));
}
if( KFGameInfo(WorldInfo.Game) != none )
{
if( MaxLifeSpan < KFGameInfo(WorldInfo.Game).DifficultyInfo.GetAIGroundSpeedMod() )
{
MaxLifeSpan = KFGameInfo(WorldInfo.Game).DifficultyInfo.GetAIGroundSpeedMod();
}
}
}
// Scale the graph to the screen size
ScaleX = Canvas.ClipX/1920.0;
// Set the location and size of the graph
UL_X = 1350 * ScaleX;
UL_Y = 100 * ScaleX;
W = 500 * ScaleX;
H = 250 * ScaleX;
if( KFGRI.bVersusGame )
{
Canvas.DrawDebugGraphBackground("Gameplay Modifications VersusZedHealthMod: "$KFGRI.VersusZedHealthMod$" VersusZedDamageMod: "$KFGRI.VersusZedDamageMod, UL_X, UL_Y, W, H);
}
else
{
Canvas.DrawDebugGraphBackground("Gameplay Modifications", UL_X, UL_Y, W, H);
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(0, 255, 0, 255), "Zed Move Speed", 0,KFGRI.ZedMovementSpeedModifierTracker[i-1]$"X");
if( KFGRI != none )
{
Points.Length = 0;
for( i = 0; i < ArrayCount(KFGRI.ZedSpawnRateModifierTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.ZedSpawnRateModifierTracker)),(MaxLifeSpan - KFGRI.ZedSpawnRateModifierTracker[i]));
}
}
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(255, 128, 0, 255), "Spawn Rate Mod", 150 * ScaleX,KFGRI.ZedSpawnRateModifierTracker[i-1]$"X");
if( KFGameInfo(WorldInfo.Game) != none )
{
Points.Length = 0;
Points[Points.Length] = vect2d(0,MaxLifeSpan - KFGameInfo(WorldInfo.Game).DifficultyInfo.GetAIGroundSpeedMod());
Points[Points.Length] = vect2d(100,MaxLifeSpan - KFGameInfo(WorldInfo.Game).DifficultyInfo.GetAIGroundSpeedMod());
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(0, 255, 255, 255), "Base Speed Mod", 250 * ScaleX,KFGameInfo(WorldInfo.Game).DifficultyInfo.GetAIGroundSpeedMod()$"X");
}
}
else if( CurrentGameConductorDebugMode == EGCDM_ZedSpawning )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
for( i = 0; i < ArrayCount(KFGRI.ZedSpawnRateTracker); i++ )
{
if( MaxLifeSpan < KFGRI.ZedSpawnRateTracker[i] )
{
MaxLifeSpan = KFGRI.ZedSpawnRateTracker[i];
}
}
for( i = 0; i < ArrayCount(KFGRI.ZedSpawnRateTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.ZedSpawnRateTracker)),(MaxLifeSpan - KFGRI.ZedSpawnRateTracker[i]));
}
}
// Scale the graph to the screen size
ScaleX = Canvas.ClipX/1920.0;
// Set the location and size of the graph
UL_X = 1350 * ScaleX;
UL_Y = 100 * ScaleX;
W = 500 * ScaleX;
H = 250 * ScaleX;
Canvas.DrawDebugGraphBackground("Zed Spawning", UL_X, UL_Y, W, H);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,MaxLifeSpan), MakeColor(0, 255, 0, 255), "Zed Spawn Delay", 0,KFGRI.ZedSpawnRateTracker[i-1]$" S");
}
else if( CurrentGameConductorDebugMode == EGCDM_OverallRankAndSkill )
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
for( i = 0; i < ArrayCount(KFGRI.OverallRankAndSkillModifierTracker); i++ )
{
Points[Points.Length] = vect2d(i * Float(ArrayCount(KFGRI.OverallRankAndSkillModifierTracker)),(100 - KFGRI.OverallRankAndSkillModifierTracker[i] * 100));
}
}
// Scale the graph to the screen size
ScaleX = Canvas.ClipX/1920.0;
// Set the location and size of the graph
UL_X = 1350 * ScaleX;
UL_Y = 100 * ScaleX;
W = 500 * ScaleX;
H = 250 * ScaleX;
Canvas.DrawDebugGraphBackground("Overall Rank And Skill Modifier. GameConductor Status: "$GetEnum(enum'EGameConductorStatus', KFGRI.CurrentGameConductorStatus), UL_X, UL_Y, W, H);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(128, 255, 255, 255), "Overall Rank and Skill", 0,KFGRI.OverallRankAndSkillModifierTracker[i-1]);
Points.Length = 0;
Points[Points.Length] = vect2d(0,50);
Points[Points.Length] = vect2d(100,50);
Canvas.DrawDebugGraphElement(Points, UL_X, UL_Y, W, H, vect2d(0,100), vect2d(0,100), MakeColor(255, 255, 255, 255), "Baseline", 225 * ScaleX,"0.5");
}
`endif
}
`if(`notdefined(ShippingPC))
/** Current debugging mode for the game conductor display*/
simulated exec function NextGraphMode()
{
switch ( CurrentGameConductorDebugMode )
{
case EGCDM_OverallRankAndSkill:
CurrentGameConductorDebugMode = EGCDM_GameplayAdjustments;
break;
case EGCDM_GameplayAdjustments:
CurrentGameConductorDebugMode = EGCDM_Skill;
break;
case EGCDM_Skill:
CurrentGameConductorDebugMode = EGCDM_OverallAccuracy;
break;
case EGCDM_OverallAccuracy:
CurrentGameConductorDebugMode = EGCDM_LifeSpan;
break;
case EGCDM_LifeSpan:
CurrentGameConductorDebugMode = EGCDM_Status;
break;
case EGCDM_Status:
CurrentGameConductorDebugMode = EGCDM_ZedSpawning;
break;
case EGCDM_ZedSpawning:
CurrentGameConductorDebugMode = EGCDM_OverallRankAndSkill;
break;
default:
CurrentGameConductorDebugMode = EGCDM_OverallRankAndSkill;
break;
}
}
/** Toggle the game conductor graph */
simulated exec function ToggleGraph()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if ( KFGRI != none )
{
KFGRI.bGameConductorGraphingEnabled = !KFGRI.bGameConductorGraphingEnabled;
ServerEnableConductorGraph(KFGRI.bGameConductorGraphingEnabled);
if( MyHud != none )
{
MyHud.bShowDebugInfo = KFGRI.bGameConductorGraphingEnabled;
if( KFGRI.bGameConductorGraphingEnabled && !MyHud.ShouldDisplayDebug('Conductor') )
{
MyHud.DebugDisplay[MyHud.DebugDisplay.Length] = 'Conductor';
}
if( !KFGRI.bGameConductorGraphingEnabled && MyHud.ShouldDisplayDebug('Conductor') )
{
MyHud.DebugDisplay.RemoveItem('Conductor');
}
MyHud.SaveConfig();
}
}
}
/** Enable the tracking map on the server */
reliable server function ServerEnableConductorGraph( bool bEnabled )
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if ( KFGRI != none )
{
KFGRI.bGameConductorGraphingEnabled = bEnabled;
}
}
/** Toggle the tracking map */
simulated exec function ToggleTracker()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if ( KFGRI != none )
{
KFGRI.bTrackingMapEnabled = !KFGRI.bTrackingMapEnabled;
ServerEnableTrackingMap(KFGRI.bTrackingMapEnabled);
if( MyHud != none )
{
MyHud.bShowDebugInfo = KFGRI.bTrackingMapEnabled;
if( !MyHud.ShouldDisplayDebug('Map') )
{
MyHud.DebugDisplay[MyHud.DebugDisplay.Length] = 'Map';
}
MyHud.SaveConfig();
}
}
}
/** Enable the tracking map on the server */
reliable server function ServerEnableTrackingMap( bool bEnabled )
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if ( KFGRI != none )
{
KFGRI.bTrackingMapEnabled = bEnabled;
}
}
/** Increase tracking range preset */
simulated exec function NextTrackingMode()
{
switch ( CurrentTrackingMode )
{
case ETM_All:
CurrentTrackingMode = ETM_AllButTargeting;
break;
case ETM_AllButTargeting:
CurrentTrackingMode = ETM_PawnsOnly;
break;
case ETM_PawnsOnly:
CurrentTrackingMode = ETM_PawnsAndTargetingOnly;
break;
case ETM_PawnsAndTargetingOnly:
CurrentTrackingMode = ETM_SpawnsOnly;
break;
case ETM_SpawnsOnly:
CurrentTrackingMode = ETM_FailedSpawnsOnly;
break;
case ETM_FailedSpawnsOnly:
CurrentTrackingMode = ETM_HumansAndSpawnsOnly;
break;
case ETM_HumansAndSpawnsOnly:
CurrentTrackingMode = ETM_PickupsOnly;
break;
case ETM_PickupsOnly:
CurrentTrackingMode = ETM_All;
break;
default:
CurrentTrackingMode = ETM_All;
break;
}
}
/** Toggle top down or side view */
simulated exec function ToggleMapView()
{
bTrackingMapTopView = !bTrackingMapTopView;
}
/** Set the tracking range */
simulated exec function SetTrackingRange(float NewRange)
{
CurrentTrackerRangeMode=ETR_Custom;
TrackingMapRange = NewRange;
}
/** Increase tracking range preset */
simulated exec function NextTrackingRange()
{
if( CurrentTrackerRangeMode == ETR_Custom )
{
if( TrackingMapRange < 1000 )
{
// Do nothing
}
else if( TrackingMapRange >= 1000 && TrackingMapRange < 2500 )
{
CurrentTrackerRangeMode = ETR_10Meters;
}
else if( TrackingMapRange < 5000 )
{
CurrentTrackerRangeMode = ETR_25Meters;
}
else if( TrackingMapRange < 10000 )
{
CurrentTrackerRangeMode = ETR_50Meters;
}
else
{
CurrentTrackerRangeMode = ETR_100Meters;
}
}
switch ( CurrentTrackerRangeMode )
{
case ETR_10Meters:
CurrentTrackerRangeMode = ETR_25Meters;
TrackingMapRange = 2500;
break;
case ETR_25Meters:
TrackingMapRange = 5000;
CurrentTrackerRangeMode = ETR_50Meters;
break;
case ETR_50Meters:
TrackingMapRange = 10000;
CurrentTrackerRangeMode = ETR_100Meters;
break;
case ETR_100Meters:
TrackingMapRange = 25000;
CurrentTrackerRangeMode = ETR_250Meters;
break;
case ETR_250Meters:
TrackingMapRange = 25000;
break;
default:
TrackingMapRange = 1000;
CurrentTrackerRangeMode = ETR_10Meters;
break;
}
}
/** Decrease tracking range preset */
simulated exec function PreviousTrackingRange()
{
if( CurrentTrackerRangeMode == ETR_Custom )
{
if( TrackingMapRange < 2500 )
{
CurrentTrackerRangeMode = ETR_10Meters;
}
else if( TrackingMapRange < 5000 )
{
CurrentTrackerRangeMode = ETR_25Meters;
}
else if( TrackingMapRange < 10000 )
{
CurrentTrackerRangeMode = ETR_50Meters;
}
else if( TrackingMapRange < 25000 )
{
CurrentTrackerRangeMode = ETR_100Meters;
}
}
switch ( CurrentTrackerRangeMode )
{
case ETR_250Meters:
TrackingMapRange = 10000;
CurrentTrackerRangeMode = ETR_100Meters;
break;
case ETR_100Meters:
TrackingMapRange = 5000;
CurrentTrackerRangeMode = ETR_50Meters;
break;
case ETR_50Meters:
TrackingMapRange = 2500;
CurrentTrackerRangeMode = ETR_25Meters;
break;
case ETR_25Meters:
TrackingMapRange = 1000;
CurrentTrackerRangeMode = ETR_10Meters;
break;
case ETR_10Meters:
TrackingMapRange = 1000;
break;
default:
TrackingMapRange = 25000;
CurrentTrackerRangeMode = ETR_250Meters;
break;
}
}
/** Toggle the wave debug display */
simulated exec function ToggleWaveDebug()
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if ( KFGRI != none )
{
KFGRI.bDebugSpawnManager = !KFGRI.bDebugSpawnManager;
ServerEnableWaveDebug(KFGRI.bDebugSpawnManager);
if( MyHud != none )
{
MyHud.bShowDebugInfo = KFGRI.bDebugSpawnManager;
// remove debugtype if already in array
if (INDEX_NONE != MyHud.DebugDisplay.RemoveItem('SpawnWaves'))
{
MyHud.SaveConfig();
}
if( KFGRI.bDebugSpawnManager && !MyHud.ShouldDisplayDebug('SpawnWaves') )
{
MyHud.DebugDisplay[MyHud.DebugDisplay.Length] = 'SpawnWaves';
}
MyHud.SaveConfig();
}
}
}
/** Enable the wave debug on the server */
reliable server function ServerEnableWaveDebug( bool bEnabled )
{
local KFGameReplicationInfo KFGRI;
local KFAISpawnManager KFSM;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if ( KFGRI != none )
{
KFGRI.bDebugSpawnManager = bEnabled;
KFSM = KFGameInfo( WorldInfo.Game ).SpawnManager;
if( KFSM != none )
{
KFGRI.CurrentSineWavFreq = KFSM.GetSineWaveFreq();
KFGRI.CurrentSineMod = KFSM.GetSineMod();
KFGRI.CurrentNextSpawnTimeMod = KFSM.GetNextSpawnTimeMod();
KFGRI.CurrentMaxMonsters = KFSM.GetMaxMonsters();
}
}
}
`endif
function DrawNextSpawnTimeInfo( out Canvas Canvas, /*out KFAISpawnManager KFSM,*/ float XPos, float YPos, float Buffer )
{
local KFMapInfo KFMI;
local float MapSpawnTime;
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if( KFGRI != none )
{
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
MapSpawnTime = KFMI != none ? KFMI.WaveSpawnPeriod : class'KFMapInfo'.default.WaveSpawnPeriod;
DrawNextDebugLine( Canvas, Buffer, YPos, false, "(Y Axis) Zed Spawning Intensity " @ 1 - KFGRI.CurrentSineMod, Xpos );
DrawNextDebugLine( Canvas, Buffer, YPos, true, "Base Map Spawn Time: " @MapSpawnTime, Xpos );
DrawNextDebugLine( Canvas, Buffer, YPos, false, "Length and Num Players Modifier: " @KFGRI.CurrentNextSpawnTimeMod, Xpos );
if ( KFGRI.CurrentMaxMonsters <= KFGRI.CurrentAIAliveCount )
{
DrawNextDebugLine( Canvas, Buffer, YPos, false, "Max Monsters Reached", Xpos );
}
else if ( KFGRI.bCurrentSMFinishedSpawning )
{
DrawNextDebugLine( Canvas, Buffer, YPos, false, "All zeds are in game", Xpos );
}
else
{
DrawNextDebugLine( Canvas, Buffer, YPos, false, "Time until next spawn: " @KFGRI.CurrentTimeTilNextSpawn, Xpos );
DrawNextDebugLine( Canvas, Buffer, YPos, false, "Current spawn delay: " @KFGRI.CurrentNextSpawnTime, Xpos );
}
DrawNextDebugLine( Canvas, Buffer, YPos, true, KFGRI.CurrentAIAliveCount @"/" @ KFGRI.CurrentMaxMonsters @"Zeds In Game", Xpos );
}
}
`if(`notdefined(ShippingPC))
reliable client function ClientDrawDebugLine(vector LineStart, vector LineEnd, bool bPersistent, bool bClear, Color LineColor)
{
if( bClear )
{
FlushPersistentDebugLines();
}
DrawDebugLine(LineStart,LineEnd, LineColor.R,LineColor.G,LineColor.B,bPersistent);// green
}
reliable client function ClientDrawDebugSphere(vector SphereLocation, float SphereRadius, bool bPersistent, bool bClear)
{
if( bClear )
{
FlushPersistentDebugLines();
}
DrawDebugSphere(SphereLocation,SphereRadius,8,0,255,0,bPersistent);// green
}
reliable client function ClientDrawDebugCylinder(vector CylinderLocation, float CylinderRadius, float CylinderLength, rotator CylinderRotation, bool bPersistent, bool bClear)
{
local vector X,Y,Z;
if( bClear )
{
FlushPersistentDebugLines();
}
GetAxes(CylinderRotation,X,Y,Z);
DrawDebugCylinder(CylinderLocation - Z * CylinderLength, CylinderLocation + Z * CylinderLength, CylinderRadius, 8, 0, 255, 0, bPersistent); // green
}
`endif
/*********************************************************************************************
* @name Object
*********************************************************************************************/
event Destroyed()
{
local KFProjectile KFProj;
// Stop currently playing stingers when the map is being switched
if( StingerAkComponent != none )
{
StingerAkComponent.StopEvents();
}
SetRTPCValue( 'Health', 100, true );
PostAkEvent( LowHealthStopEvent );
bPlayingLowHealthSFX = false;
// Update projectiles in the world
foreach DynamicActors( class'KFProjectile', KFProj )
{
if( KFProj.InstigatorController == self )
{
KFProj.OnInstigatorControllerLeft();
}
}
if( LocalCustomizationPawn != none && !LocalCustomizationPawn.bPendingDelete )
{
LocalCustomizationPawn.Destroy();
}
if (OnlineSub != none)
{
OnlineSub.ClearAllReadOnlineAvatarByNameCompleteDelegates();
OnlineSub.ClearAllReadOnlineAvatarCompleteDelegates();
}
if (WorldInfo.NetMode != NM_DedicatedServer)
{
ClearMixerDelegates();
ClearDiscord();
}
ClientMatchEnded();
Super.Destroyed();
}
event Exit()
{
if (LEDEffectsManager != none && WorldInfo.NetMode != NM_DedicatedServer)
{
LEDEffectsManager.LedStopEffects();
}
}
/*********************************************************************************************
* State Dead
* Pawn is dead, return to spectating after a short while
*********************************************************************************************/
state Dead
{
ignores ClientPlayForceFeedbackWaveform;
event BeginState(Name PreviousStateName)
{
local KFPawn KFP;
local KFPlayerInput KFPI;
Super.BeginState( PreviousStateName );
// Allow suicide cam to linger a tiny bit longer
if( PlayerCamera.CameraStyle == 'ZedSuicide' )
{
SetTimer( 6.f, false, nameOf(StartSpectate) );
}
else
{
SetTimer( 5.f, false, nameOf(StartSpectate) );
}
// Deactivate any post process effects when we die
ResetGameplayPostProcessFX();
// setup for first-person death
KFP = KFPawn(ViewTarget);
if( KFP != none && UsingFirstPersonCamera() )
{
KFP.Mesh.SetOwnerNoSee( true );
KFP.Mesh.CastShadow = false;
KFP.SetThirdPersonAttachmentVisibility( false );
KFP.SetFirstPersonVisibility( false );
}
if(CurrentPerk != none)
{
CurrentPerk.PlayerDied();
}
if(CurrentPowerUp != none)
{
CurrentPowerUp.PlayerDied();
}
KFPI = KFPlayerInput(PlayerInput);
if(KFPI != none)
{
KFPI.HideVoiceComms();
}
if( MyGFxManager != none )
{
MyGFxManager.CloseMenus();
}
if(MyGFxHUD != none )
{
MyGFxHUD.PawnDied();
}
}
exec function StartFire( optional byte FireModeNum )
{
StartSpectate();
}
function SetViewTarget( Actor NewViewTarget, optional ViewTargetTransitionParams TransitionParams )
{
if( PlayerCamera.CameraStyle == 'Boss' && KFPawn_Monster(NewViewTarget) != none && KFPawn_Monster(NewViewTarget).static.IsABoss())
{
super.SetViewTarget( NewViewTarget, TransitionParams );
}
}
event EndState( Name NextStateName )
{
local KFPawn KFP;
Super.EndState( NextStateName );
ClearTimer( nameof(StartSpectate) );
// undo setup for first-person death
KFP = KFPawn(ViewTarget);
if( KFP != none )
{
KFP.Mesh.CastShadow = KFP.Mesh.default.CastShadow;
}
// Show the spectating hud
NotifyChangeSpectateViewTarget();
}
/** ResetCameraMode is called from PlayerController:Dead::EndState and set the cam mode for both clients and server. We only want the server to do that. */
event ResetCameraMode()
{
if( Role == ROLE_Authority && Pawn != none && !Pawn.bPlayedDeath )
{
Global.ResetCameraMode();
}
}
}
/**
* Should be to instigate a transition to spectating, automatically handles notifying server or notifying the owning client as necessary.
*/
function StartSpectate( optional Name SpectateType )
{
if ( Role == ROLE_Authority )
{
if( IsLocalPlayerController() )
{
ClientGotoState( 'Spectating' );
}
else
{
GotoState( 'Spectating' );
ClientGotoState( 'Spectating' );
}
}
}
/*********************************************************************************************
* State BaseSpectating
* Base spectating state. Ignore rumble and camera shake in any non-play controller state.
*********************************************************************************************/
state BaseSpectating
{
ignores ClientPlayForceFeedbackWaveform, ClientPlayCameraShake;
}
/*********************************************************************************************
* State PlayerWaiting
* Base spectating state. Ignore rumble and camera shake in any non-play controller state.
*********************************************************************************************/
auto state PlayerWaiting
{
ignores SeePlayer, HearNoise, NotifyBump, TakeDamage, PhysicsVolumeChange, NextWeapon, PrevWeapon, SwitchToBestWeapon, ClientPlayForceFeedbackWaveform, ClientPlayCameraShake;
}
/*********************************************************************************************
* State WaitingForPawn
* Base spectating state. Ignore rumble and camera shake in any non-play controller state.
*********************************************************************************************/
state WaitingForPawn
{
ignores SeePlayer, HearNoise, KilledBy, ClientPlayForceFeedbackWaveform, ClientPlayCameraShake;
}
/*********************************************************************************************
* State Spectating
* View other players on your team
*********************************************************************************************/
simulated function NextSpectateMode();
unreliable server function ServerSetSpectatorActive();
function MoveToValidSpectatorLocation()
{
local PlayerStart PS;
local vector CameraLocation;
// Make sure that our freecam isn't trapped in the lobby
foreach AllActors( class'PlayerStart', PS )
{
CameraLocation = PS.Location + ( vect(0,0,1) * (PS.CylinderComponent.CollisionHeight * 2.f) );
SetLocation( CameraLocation );
ServerSetSpectatorLocation( CameraLocation );
SetRotation( rot(-1024,0,0) );
break;
}
}
state Spectating
{
ignores ClientPlayForceFeedbackWaveform;
event BeginState(Name PreviousStateName)
{
local KFGFxHudWrapper GFxHUDWrapper;
local KFPlayerReplicationInfo KFPRI;
SetTimer( NextSpectatorDelay, false, nameOf(SpectateNextPlayer) );
GFxHUDWrapper = KFGFxHudWrapper(myHUD);
if( GFxHUDWrapper != none)
{
GFxHUDWrapper.CreateHUDMovie();
}
// Make sure we nuke our customization pawn!
if( Pawn != none && KFPawn_Customization(Pawn) != none )
{
if( WorldInfo.NetMode != NM_Client )
{
Pawn.Destroy();
}
}
Super.BeginState(PreviousStateName);
// Teleport controller to a valid in-world location
if( IsLocalPlayerController() && !bIsAchievementPlayer )
{
MoveToValidSpectatorLocation();
}
// Try to get a player to spectate right away
if( WorldInfo.NetMode != NM_Client )
{
if( ViewTarget == none || ViewTarget == self || ViewTarget.bDeleteMe || (Pawn(ViewTarget) != none && !Pawn(ViewTarget).IsAliveAndWell()) )
{
ServerViewNextPlayer();
}
// Put us in roaming if our viewtarget is ourself
if( ViewTarget == self )
{
if( CurrentSpectateMode != SMODE_Roaming )
{
CurrentSpectateMode = SMODE_PawnFreeCam;
SetCameraMode( 'FreeCam' );
}
}
else
{
SpectatePlayer( SMODE_PawnFreeCam );
}
}
if( MyGFxHUD != none )
{
MyGFxHUD.SetHUDSpectating(true);
}
NotifyChangeSpectateViewTarget();
// If we end up spectating in standalone, toggle health FX off
if( WorldInfo.NetMode == NM_StandAlone )
{
ToggleHealthEffects(false);
}
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
if(KFPRI != none)
{
KFPRI.PlayerHealth = 0;
KFPRI.PlayerHealthPercent = 0;
KFPRI.bNetDirty = true;
}
if( MyGFxManager != none )
{
MyGFxManager.NotifySpectateStateChanged( true );
}
}
exec function StartFire(optional BYTE FireModeNum)
{
}
exec function SpectateNextPlayer()
{
ClearTimer( nameOf(SpectateNextPlayer) );
ServerViewNextPlayer();
NotifyChangeSpectateViewTarget();
}
exec function SpectatePreviousPlayer()
{
ClearTimer( nameOf(SpectateNextPlayer) );
ServerViewPrevPlayer();
NotifyChangeSpectateViewTarget();
}
exec function SpectateChangeCamMode()
{
ServerNextSpectateMode();
NotifyChangeSpectateViewTarget();
}
event EndState(Name NextStateName)
{
Super.EndState(NextStateName);
if( MyGFxHUD != none )
{
MyGFxHUD.SetHUDSpectating( false );
}
if( MyGFxManager != none )
{
MyGFxManager.NotifySpectateStateChanged( false );
}
}
// overridden (copy-pasted) to pass rotation delta to ReplicateMove
function PlayerMove(float DeltaTime)
{
local vector X,Y,Z;
// BEGIN MOD
local rotator OldRotation;
OldRotation = Rotation;
// END MOD
GetAxes(Rotation,X,Y,Z);
Acceleration = PlayerInput.aForward*X + PlayerInput.aStrafe*Y + PlayerInput.aUp*vect(0,0,1);
UpdateRotation(DeltaTime);
if (Role < ROLE_Authority) // then save this move and replicate it
{
ReplicateMove(DeltaTime, Acceleration, DCLICK_None, rot(0,0,0));
// BEGIN MOD
// only done for clients, as LastActiveTime only affects idle kicking
if( (!IsZero(Acceleration) || OldRotation != Rotation) && `TimeSince(LastUpdateSpectatorActiveTime) > UpdateSpectatorActiveInterval )
{
LastUpdateSpectatorActiveTime = WorldInfo.TimeSeconds;
ServerSetSpectatorActive();
}
// END MOD
}
else
{
ProcessMove(DeltaTime, Acceleration, DCLICK_None, rot(0,0,0));
}
}
unreliable server function ServerSetSpectatorActive()
{
LastActiveTime = WorldInfo.TimeSeconds;
}
function SetViewTarget( Actor NewViewTarget, optional ViewTargetTransitionParams TransitionParams )
{
if( NewViewTarget != ViewTarget )
{
NotifyChangeSpectateViewTarget();
}
global.SetViewTarget( NewViewTarget, TransitionParams );
}
}
/** Called when view target is changed while in spectating state */
function NotifyChangeSpectateViewTarget()
{
local KFPlayerReplicationInfo KFPRI;
local KFPawn KFP;
// Avoids an issue where spectate hud is briefly visible before spawning for the first time
if( WorldInfo.GRI == none || WorldInfo.GRI.ElapsedTime < 2.f )
{
return;
}
// Don't do any spectator hud updates if we're viewing our customization pawn
if( ViewTarget == LocalCustomizationPawn )
{
return;
}
// Kill the local customization pawn if it's no longer our viewtarget
if( LocalCustomizationPawn != none && !LocalCustomizationPawn.bPendingDelete )
{
if( MyGFxManager != none && MyGFxManager.CurrentMenu != none && MyGFxManager.CurrentMenu == MyGFxManager.GearMenu )
{
MyGFxManager.CloseMenus();
}
LocalCustomizationPawn.Destroy();
}
if( MyGFxHUD != none && MyGFxHUD.SpectatorInfoWidget != none )
{
KFP = KFPawn( ViewTarget );
if( KFP != none )
{
if( KFP == Pawn && Pawn.IsAliveAndWell() )
{
return;
}
// Try to get a valid PRI, default to our own if we can't
if( KFP.PlayerReplicationInfo != none )
{
KFPRI = KFPlayerReplicationInfo(KFP.PlayerReplicationInfo);
}
else
{
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
}
}
else if( ViewTarget == self )
{
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
}
if( KFPRI != none)
{
MyGFxHUD.SpectatorInfoWidget.SetSpectatedKFPRI(KFPRI);
}
}
}
reliable client event ClientSetViewTarget( Actor A, optional ViewTargetTransitionParams TransitionParams )
{
local vector ViewLocation;
local rotator ViewRotation;
// Set the camera location to where we are viewing when we switch viewtargets
// while spectating. This fixes issues where when we switch back to viewing
// ourself, the camera is off in some strange area of the map, or worse
// the customization room
if( IsSpectating() )
{
GetPlayerViewPoint( ViewLocation, ViewRotation );
SetLocation(ViewLocation);
ViewRotation.Roll=0;
SetRotation(ViewRotation);
ServerSetSpectatorLocation(Location);
}
super.ClientSetViewTarget( A, TransitionParams);
if( IsSpectating() && ViewTarget != none )
{
NotifyChangeSpectateViewTarget();
}
}
/** Switches between spectating modes (first-person, third-person, etc.) */
unreliable server function ServerNextSpectateMode()
{
local KFPawn HumanViewTarget;
if( !IsSpectating() )
{
return;
}
// switch to roaming if human viewtarget is dead
if( CurrentSpectateMode != SMODE_Roaming )
{
HumanViewTarget = KFPawn(ViewTarget);
if( HumanViewTarget == none || !HumanViewTarget.IsAliveAndWell() )
{
SpectateRoaming();
return;
}
}
switch ( CurrentSpectateMode )
{
case SMODE_PawnFreeCam:
SpectatePlayer( SMODE_PawnThirdPerson );
break;
case SMODE_PawnThirdPerson:
SpectatePlayer( SMODE_PawnFirstPerson );
break;
case SMODE_PawnFirstPerson:
SpectateRoaming();
break;
case SMODE_Roaming:
SpectatePlayer( SMODE_PawnFreeCam );
break;
}
}
/** Switches to a spectating mode that targets a player
* (reliable because this seems to get missed pretty frequently when joining a server as a spectator,
* leading to unexpected/confusing spectating views)
*/
function SpectatePlayer( KFSpectateModes Mode )
{
if( PlayerCamera != none && PlayerCamera.CameraStyle == 'Boss' )
{
return;
}
CurrentSpectateMode = Mode;
if( KFPawn_Human(ViewTarget) == none )
{
TryViewNextPlayer();
if ( KFPawn(ViewTarget) == None )
{
// if we couldn't find a valid player to target, switch to roaming cam
if( ViewTarget != self )
{
SpectateRoaming();
}
return;
}
}
// need to have a mode before setting view target (spectating)
switch( Mode )
{
case SMODE_PawnFreeCam:
SetCameraMode('FreeCam');
break;
case SMODE_PawnThirdPerson:
SetCameraMode('ThirdPerson');
break;
case SMODE_PawnFirstPerson:
SetCameraMode('FirstPerson');
break;
};
}
/** Switches to the "roaming" spectating mode, which doesn't target a specific player */
reliable server function SpectateRoaming()
{
CurrentSpectateMode = SMODE_Roaming;
ServerViewSelf();
if( ViewTarget == self )
{
SetCameraMode( 'FirstPerson' );
}
}
/** Copied from PlayerController. Function call order wasn't correct, we need to call ResetCameraMode() after the viewtarget changes */
unreliable server function ServerViewSelf(optional ViewTargetTransitionParams TransitionParams)
{
if (IsSpectating())
{
SetViewTarget( Self, TransitionParams );
ClientSetViewTarget( Self, TransitionParams );
if( PlayerCamera != none && PlayerCamera.CameraStyle != 'FirstPerson' )
{
ResetCameraMode();
}
}
}
/** Tries to find us a new view target if we don't have one or have an invalid one */
reliable server function TryViewNextPlayer()
{
local KFPawn KFPTarget;
KFPTarget = KFPawn( ViewTarget );
if( KFPTarget == None || KFPTarget.IsAliveAndWell() )
{
ServerViewNextPlayer();
}
}
/** Switches to the next player view target (only in spectating modes that target players) */
unreliable server function ServerViewNextPlayer()
{
if( IsSpectating() )
{
switch ( CurrentSpectateMode )
{
case SMODE_PawnFreeCam:
case SMODE_PawnThirdPerson:
case SMODE_PawnFirstPerson:
case SMODE_Roaming:
if( ViewTarget == self && CurrentSpectateMode != SMODE_PawnFreeCam )
{
CurrentSpectateMode = SMODE_PawnFreeCam;
SetCameraMode( 'FreeCam' );
}
ViewAPlayer( +1 );
break;
}
}
}
/** Finds previous spectate view target */
unreliable server function ServerViewPrevPlayer()
{
if( IsSpectating() )
{
switch ( CurrentSpectateMode )
{
case SMODE_PawnFreeCam:
case SMODE_PawnThirdPerson:
case SMODE_PawnFirstPerson:
case SMODE_Roaming:
if( ViewTarget == self )
{
CurrentSpectateMode = SMODE_PawnFreeCam;
SetCameraMode( 'FreeCam' );
}
ViewAPlayer( -1 );
break;
}
}
}
/*********************************************************************************************
* @name Dialog and AAR
*********************************************************************************************/
/** Submit/replicate post wave game stats for AAR and dialog */
function SubmitPostWaveStats(optional bool bOpeningTrader)
{
MatchStats.RecordWaveInfo();
if( Role == ROLE_Authority )
{
PWRI.bOpeningTrader = bOpeningTrader;
PWRI.RepCount++;
}
// Record stats only after EphemeralMatchStats has propagated
if( IsLocalPlayerController() )
{
ClientWriteAndFlushStats();
// Trigger trader open dialog after RecordWaveInfo, but before ResetLastWaveInfo!!!
// @todo: This should get moved to GRI.OpenTrader(), but for now we're
// heavily dependant on accurate match stats.
if ( PWRI.bOpeningTrader )
{
`TraderDialogManager.PlayBeginTraderTimeDialog( self );
}
}
}
function SavePersonalBest(EPersonalBests PersonalBestID, int Value)
{
StatsWrite.SavePersonalBest(PersonalBestID, Value);
}
function int GetPersonalBest(EPersonalBests PersonalBestID)
{
return StatsWrite.GetPersonalBest(PersonalBestID);
}
/**
* Recieve award information from server at end of match
* @note: Could be optimized by sending data via variable rep (PRI/GRI)
*/
reliable client function ClientReceiveAwardInfo(byte AwardID, PlayerReplicationInfo PRI, int Value)
{
MatchStats.ReceiveAwardInfo(AwardID, PRI, Value);
//@HSL_BEGIN - JRO - 5/17/2016 - PS4 Activity Feeds
if(PRI != None && PRI == PlayerReplicationInfo)
{
OnlineSub.PlayerInterfaceEx.PostActivityFeedTeamAward(MatchStats.TeamAwardList[AwardID].TitleIdentifier);
}
//@HSL_END
}
/** Network: Local Player only */
reliable client function ReceiveTopWeapons(TopWeaponReplicationInfo TopWeapons)
{
MatchStats.UnpackTopWeapons(TopWeapons);
}
unreliable client event ClientHearDialog( Actor DialogSpeaker, AkEvent DialogEvent, byte bCanBeMinimized )
{
local KFPawn KFP;
local KFGameEngine KFEngine;
KFEngine = KFGameEngine( Class'KFGameEngine'.static.GetEngine() );
// if player wants minimal chatter and the dialog event can be minimized, then don't play it
if( bCanBeMinimized == 1 && KFEngine.bMinimalChatter )
{
return;
}
KFP = KFPawn( DialogSpeaker );
if( KFP != none )
{
KFP.PlayDialogEvent( DialogEvent );
}
}
function NotifyKilled( Controller Killer, Controller Killed, pawn KilledPawn, class<DamageType> damageType )
{
local KFPawn_Monster MonsterPawn;
super.NotifyKilled( Killer, Killed, KilledPawn, damageType );
MonsterPawn = KFPawn_Monster(KilledPawn);
if( self == Killer && self != Killed && MonsterPawn != none )
{
if( !PWRI.bKilledFleshpoundLastWave && MonsterPawn.IsA('KFPawn_ZedFleshpound') )
{
PWRI.bKilledFleshpoundLastWave = true;
}
else if( !PWRI.bKilledScrakeLastWave && MonsterPawn.IsA('KFPawn_ZedScrake') )
{
PWRI.bKilledScrakeLastWave = true;
}
MatchStats.ZedsKilledLastWave++;
CheckForZedOnDeathAchievements( MonsterPawn );
}
// Own death. Like PawnDied(), but with more input parameters
else if ( self == Killed )
{
// update death streak
PWRI.bDiedDuringWave = true;
if( Killer != none && Killer.Pawn != None )
{
PWRI.ClassKilledByLastWave = class< KFPawn_Monster >( Killer.Pawn.Class );
}
//clear any interaction messages
ReceiveLocalizedMessage(class'KFLocalMessage_Interaction', IMT_None);
// Set camera to location of body with a bit of a Z offset
// NOTE: Prevents camera from snapping back to lobby for a brief duration
SetLocation( KilledPawn.Location + vect(0,0,1)*(KilledPawn.GetCollisionRadius()*2.f) );
}
else if (MonsterPawn.IsABoss())
{
CheckForZedOnDeathAchievements(MonsterPawn);
}
}
native protected function CheckForZedOnDeathAchievements( KFPawn_Monster MonsterPawn );
function PlayTraderDialog(int DialogEventID, bool bInterrupt = false)
{
`TraderDialogManager.PlayDialog(DialogEventID, self, bInterrupt);
}
reliable client function ClientPlayTraderDialog(int DialogEventID, bool bInterrupt = false)
{
`TraderDialogManager.PlayDialog(DialogEventID, self, bInterrupt);
}
simulated function PlayTraderSelectItemDialog( bool bTooExpensive, bool bTooHeavy )
{
`TraderDialogManager.PlaySelectItemDialog( self, bTooExpensive, bTooHeavy );
}
unreliable server function ServerPlayLevelUpDialog()
{
`DialogManager.PlayLevelUpDialog( self );
}
unreliable server function ServerPlayVoiceCommsDialog( int CommsIndex )
{
BroadcastLocalizedMessage( class'KFLocalMessage_VoiceComms', CommsIndex, PlayerReplicationInfo );
`DialogManager.PlayVoiceCommandDialog( KFPawn(Pawn), CommsIndex );
}
exec function PlayVoiceCommsDialog( int CommsIndex )
{
ServerPlayVoiceCommsDialog( CommsIndex );
}
function PlayTraderEndlessWaveStartDialog(int SpecialWaveEventId, int NormalWaveEventId)
{
local int DialogEventID;
local KFTraderDialogManager TDM;
DialogEventID = NormalWaveEventId;
// Only play the special wave dialog if the trader voice group has that event.
// Otherwise, just play the normal wave dialog.
if (SpecialWaveEventId != INDEX_NONE)
{
TDM = KFGameReplicationInfo(WorldInfo.GRI).TraderDialogManager;
if (TDM != none)
{
if (TDM.TraderVoiceGroupClass.default.DialogEvents[SpecialWaveEventId].AudioCue != none)
{
DialogEventID = TDM.TraderVoiceGroupClass.default.DialogEvents[SpecialWaveEventId].EventID;
}
}
}
`TraderDialogManager.PlayDialog(DialogEventID, self);
}
reliable client function ClientPlayTraderEndlessWaveStartDialog(int SpecialWaveEventId, int NormalWaveEventId)
{
PlayTraderEndlessWaveStartDialog(SpecialWaveEventId, NormalWaveEventId);
}
/**
* @brief Called from the UI or key bind to start the emote action
*/
exec function DoEmote()
{
local KFPawn MyPawn;
local byte SMFlags;
MyPawn = KFPawn(Pawn);
if( MyPawn != none
&& !bCinematicMode
&& !MyPawn.IsDoingSpecialMove()
&& class'KFEmoteList'.static.GetEquippedEmoteId() != -1
&& MyPawn.CanDoSpecialMove(SM_Emote) )
{
SMFlags = MyPawn.SpecialMoveHandler.SpecialMoveClasses[SM_Emote].static.PackFlagsBase( MyPawn );
MyPawn.DoSpecialMove( SM_Emote, true,, SMFlags );
if( Role < ROLE_Authority && MyPawn.IsDoingSpecialMove(SM_Emote) )
{
MyPawn.ServerDoSpecialMove( SM_Emote, true, , SMFlags );
}
}
if (IsLocalController() && LEDEffectsManager != none)
{
LEDEffectsManager.PlayEmoteEffect();
}
}
exec function RequestSkipTrader()
{
local KFGameReplicationInfo KFGRI;
local KFPlayerReplicationInfo KFPRI;
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
KFGRI = KFGameReplicationInfo(KFPRI.WorldInfo.GRI);
if (KFPRI != none)
{
if (KFGRI.bMatchHasBegun)
{
if (KFGRI.bTraderIsOpen && KFPRI.bHasSpawnedIn)
{
KFPRI.RequestSkiptTrader(KFPRI);
if (MyGFxManager != none)
{
MyGFxManager.CloseMenus();
if (MyGFxManager.PartyWidget != none)
{
MyGFxManager.PartyWidget.SetReadyButtonVisibility(false);
}
}
}
}
}
}
exec function RequestPauseGame()
{
local KFGameReplicationInfo KFGRI;
local KFPlayerReplicationInfo KFPRI;
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
KFGRI = KFGameReplicationInfo(KFPRI.WorldInfo.GRI);
if (KFPRI != none)
{
if (KFGRI.bMatchHasBegun)
{
if (KFGRI.bEndlessMode && KFPRI.bHasSpawnedIn)
{
KFPRI.RequestPauseGame(KFPRI);
if (MyGFxManager != none)
{
MyGFxManager.CloseMenus();
if (MyGFxManager.PartyWidget != none)
{
MyGFxManager.PartyWidget.SetReadyButtonVisibility(false);
}
}
}
}
}
}
/*********************************************************************************************
* Networking
*********************************************************************************************/
exec function ItemExchangeTimeOut ()
{
if(MyGFxManager != none && MyGFxManager.InventoryMenu != none)
{
MyGFxManager.InventoryMenu.OnItemExhangeTimeOut();
}
}
`if(`notdefined(ShippingPC))
exec function TestConnectionPopup(string Title, optional string Description)
{
ShowConnectionProgressPopup(PMT_DownloadProgress, Title,Description);
}
`endif
function ForceDisconnect()
{
if(CanDisconnect())
{
ClearDownloadInfo();
ConsoleCommand("DISCONNECT");
}
else
{
`log("Could not disconnect");
}
}
function bool CanDisconnect()
{
local String TargetMapName;
local string CurrentMovieString;
GetCurrentMovie(CurrentMovieString);
TargetMapName = KFGameEngine(Class'Engine'.static.GetEngine()).TransitionDescription;
if(TargetMapName == "KFMainMenu" || TargetMapName == "")
{
`log("Returning false - Attempting to go to main menu when on main menu");
return false;
}
else
{
if(WorldInfo.bIsMenuLevel && !bDownloadingContent)
{
`log("returning false, in menu level and not downloading content");
return false;
}
else if(CurrentMovieString == "")
{
`log("returning false, no movie playing. This means you are loaded in.");
return false;
}
}
return true;
}
reliable client event bool ShowConnectionProgressPopup( EProgressMessageType ProgressType, string ProgressTitle, string ProgressDescription, bool SuppressPasswordRetry = false)
{
local KFGameEngine KFGEngine;
local KFGameViewportClient KFGVPC;
local string CachedTitle, CachedMessage;
if( MyGFxManager == none)
{
return false;
}
KFGVPC = KFGameViewportClient(LocalPlayer(Player).ViewportClient);
if( KFGVPC != none && KFGVPC.ErrorTitle != "" )
{
KFGVPC.GetErrorMessage(CachedTitle, CachedMessage);
}
else
{
CachedTitle = ProgressTitle;
CachedMessage = ProgressDescription;
}
switch(ProgressType)
{
case PMT_ConnectionFailure :
case PMT_PeerConnectionFailure :
DestroyOnlineGame();
KFGEngine = KFGameEngine( Class'KFGameEngine'.static.GetEngine() );
if( KFGEngine != none )
{
switch (KFGEngine.LastConnectionError)
{
case CE_WrongPassword:
case CE_NeedPassword:
if ( SuppressPasswordRetry )
{
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_Misc, CachedTitle, CachedMessage, class'KFCommon_LocalizedStrings'.default.OKString);
KFGEngine.LastConnectionError = CE_None;
}
else
{
MyGFxManager.DelayedOpenPopup(EInputPrompt, EDPPID_Misc, CachedMessage, "", class'KFCommon_LocalizedStrings'.default.ConfirmString, class'KFCommon_LocalizedStrings'.default.CancelString, OnAttemptPassword);
KFGEngine.LastConnectionError = CE_None;
}
return true;
default:
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_Misc, CachedTitle, CachedMessage, class'KFCommon_LocalizedStrings'.default.OKString);
KFGEngine.LastConnectionError = CE_None;
return true;
}
}
break;
case PMT_AdminMessage :
MyGFxManager.DelayedOpenPopup(EConfirmation, EDPPID_Misc, CachedTitle, CachedMessage, "", "", None, None, class'KFCommon_LocalizedStrings'.default.CancelString);
return true;
case PMT_DownloadProgress :
bDownloadingContent = true;
if(MyGFxManager != none && MyGFxManager.PartyWidget != none)
{
MyGFxManager.PartyWidget.ShowDownLoadNotification(CachedTitle, float(CachedMessage));
}
//MyGFxManager.DelayedOpenPopup(EConfirmation, EDPPID_Misc, CachedTitle, CachedMessage, "", "", None, None, class'KFCommon_LocalizedStrings'.default.CancelString, CancelDownload);
return true;
break;
}
return false;
}
/**
* Triggered when the 'disconnect' console command is called, to allow cleanup before disconnecting (e.g. for the online subsystem)
* NOTE: If you block disconnect, store the 'Command' parameter, and trigger ConsoleCommand(Command) when done; be careful to avoid recursion
*/
event bool NotifyDisconnect(string Command)
{
ResetMusicStateForTravel();
// See also UnregisterPlayer()
ClientWriteAndFlushStats();
DestroyOnlineGame();
return super.NotifyDisconnect(Command);
}
/** If we have an online game but can't join it or are leaving it, destroy it. */
event DestroyOnlineGame()
{
//@HSL_BEGIN - JRO - 4/28/2016 - Record the people recently met
OnlineSub.PlayerInterfaceEx.RecordPlayersRecentlyMet(LocalPlayer(Player).ControllerId, RecentlyMetPlayers, string(PlayerReplicationInfo.SessionName));
RecentlyMetPlayers.length = 0;
//@HSL_END
OnlineSub.GameInterface.DestroyOnlineGame(PlayerReplicationInfo.SessionName);
}
//This is the function that the password prompt points to for onConfirm (confirmButton click or enter key)
function OnAttemptPassword()
{
local string URL, Password;
local KFGameViewportClient Viewport;
Password = KFGFxPopup_InputPrompt(MyGFxManager.CurrentPopup).PlayerInputString;
Viewport = KFGameViewportClient(LocalPlayer(Player).ViewportClient);
URL = "Open" @Viewport.LastConnectionAttemptAddress $"?Password=" $Password;
//@SABER_EGS_BEGIN
if (class'WorldInfo'.static.IsEosBuild()) {
URL $= "?PlayfabPlayerId="$class'GameEngine'.static.GetPlayfabInterface().CachedPlayfabId;
}
//@SABER_EGS_END
ConsoleCommand(URL);
}
function ClearOnlineDelegates()
{
if( OnlineSub != none )
{
OnlineSub.GameInterface.ClearOnlineDelegates();
if(OnlineSub.PlayerInterface != None)
{
OnlineSub.PlayerInterface.ClearReadProfileSettingsCompleteDelegate(LocalPlayer(Player).ControllerId, OnReadProfileSettingsComplete);
}
OnlineSub.StatsInterface.ClearReadOnlineStatsCompleteDelegate( OnStatsInitialized );
}
super.ClearOnlineDelegates();
}
exec function RequestSwitchTeam();
exec function SwitchTeam(); //disabled
/**
* @brief Activates/Deactivates the timer that could eventually start to hurt the player (Client)
*
* @param bNewActive on/off
* @param Delay Time we waitr before we start to hurt campers
*/
protected native function SetNoGoActive( bool bNewActive, float Delay );
/**
* @brief Activates/Deactivates the timer that could eventually start to hurt the player (Server)
*
* @param bNewActive on/off
* @param Delay Time we waitr before we start to hurt campers
*/
reliable protected server event ServerSetNoGoActive( bool bNewActive, float Delay )
{
bNoGoActive = bNewActive;
if( bNoGoActive )
{
SetTimer( Delay, false, nameOf(MotivatePlayerToMove) );
}
else
{
ClearTimer( nameOf(MotivatePlayerToMove) );
}
}
/**
* @brief Deals some damage to motivate the player to move out if a NoGoVolume
*/
protected function MotivatePlayerToMove()
{
if( Pawn != none && bNoGoActive )
{
Pawn.TakeDamage( (Pawn.HealthMax / 10), self, Pawn.Location, vect(0,0,0), class'KFVersusNoGoVolume'.static.GetNoGoDTClass() );
}
else
{
bNoGoActive = false;
ClearTimer( nameOf(MotivatePlayerToMove) );
}
SetTimer( class'KFVersusNoGoVolume'.static.GetNoGoHurtInterval(), true, nameOf(MotivatePlayerToMove) );
}
exec function GCF()
{
if ( MyGFxManager != None )
{
MyGFxManager.currentFocus();
}
}
function OnControllerChanged(int ControllerId,bool bIsConnected,bool bPauseGame)
{
local LocalPlayer LP;
// Don't worry about remote players
LP = LocalPlayer(Player);
// Ignore any controller changed notifications when we don't have an active one
if( !class'Engine'.static.GetEngine().GameViewport.bNeedsNewGamepadPairingForControllerDisconnect &&
!class'Engine'.static.GetEngine().GameViewport.bNeedsNewGamepadPairingForNewProfile &&
WorldInfo.IsConsoleBuild() &&
LP != None &&
LP.ControllerId == ControllerId &&
!bIsConnected &&
HasActiveUserEstablished() )
{
// Toggle menus if not actively open
if( !MyGFxManager.bMenusOpen )
{
MyGFxManager.ToggleMenus();
}
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) )
{
// We need to set a new pairing on input from any controller to resume
class'Engine'.static.GetEngine().GameViewport.bNeedsNewGamepadPairingForControllerDisconnect = true;
}
ShowControllerDisconnectedDialog();
// Notify tutorial that controller is disconnected
if( WorldInfo.Game != none )
{
WorldInfo.Game.NotifyControllerDisconnected();
}
// Notify screen size movie
if( MyGFxManager.ScreenSizeMovie != None )
{
MyGFxManager.ScreenSizeMovie.NotifyControllerDisconnected();
}
}
Super.OnControllerChanged(ControllerId, bIsConnected, bPauseGame);
}
simulated function ShowControllerDisconnectedDialog()
{
local string DisconnectMessage;
if (WorldInfo.IsConsoleBuild(CONSOLE_Durango))
{
DisconnectMessage = "ControllerDisconnectedXB1";
}
else
{
DisconnectMessage = "ControllerDisconnectedPS4Message";
}
// Only show this past the IIS
if( HasActiveUserEstablished() )
{
MyGFxManager.DelayedOpenPopup(EConfirmation, EDPPID_ControllerDisconnect, Localize("Notifications", "ControllerDisconnectedTitle", "KFGameConsole"), Localize("Notifications", DisconnectMessage, "KFGameConsole"), class'KFCommon_LocalizedStrings'.default.OKString, "", OnControllerDisconnectDialogDismissed);
}
}
simulated function OnControllerDisconnectDialogDismissed()
{
// Notify tutorial that controller is now reconnected
if( WorldInfo.Game != none )
{
WorldInfo.Game.NotifyControllerReconnected();
}
// Notify screen size movie
if (MyGFxManager.ScreenSizeMovie != None)
{
MyGFxManager.ScreenSizeMovie.NotifyControllerReconnected();
}
}
simulated function CheckForConnectedControllers()
{
local bool bHasAnyControllersConnected;
local int i;
// If we are looking for input from any controller when establishing a new (previously idle) profile, check to see if any controllers are connected. If none are, we display the controller disconnect message
if (class'Engine'.static.GetEngine().GameViewport.bNeedsNewGamepadPairingForNewProfile)
{
for (i = 0; i < `MAX_NUM_PLAYERS; i++)
{
bHasAnyControllersConnected = bHasAnyControllersConnected || OnlineSub.SystemInterface.IsControllerConnected(i);
}
if (!bHasAnyControllersConnected)
{
ShowControllerDisconnectedDialog();
}
}
// Check to see if the controller is connected, if not, throw up a dialog
else if (!OnlineSub.SystemInterface.IsControllerConnected(LocalPlayer(Player).ControllerId))
{
ShowControllerDisconnectedDialog();
}
}
simulated function bool HasActiveUserEstablished()
{
return KFGameViewportClient(class'Engine'.static.GetEngine().GameViewport).bSeenIIS ||
( MyGFxManager != none && MyGFxManager.IISMenu != none && MyGFxManager.IISMenu.bLoggingIn );
}
function OnLoginCompleted( bool bSuccess )
{
// Fire off only on success
if( LoginCompleteCallback != none && bSuccess )
{
LoginCompleteCallback();
}
// Special case for IIS, notify it failed so it can reset flags properly
else if( KFGFxMenu_IIS(MyGFxManager.CurrentMenu) != None )
{
KFGFxMenu_IIS(MyGFxManager.CurrentMenu).NotifyLoginFailed();
}
if(bSuccess)
{
ClientSetOnlineStatus();
}
else
{
if(MyGFxManager != none)
{
MyGFxManager.OnLoginFailed();
}
}
// Clear the callback
LoginCompleteCallback = none;
bLoggingInForOnlinePlay = false;
}
delegate LoginCompleteCallback();
function StartLogin( delegate<LoginCompleteCallback> InDel, optional bool bInLoggingInForOnlinePlay )
{
if( LoginCompleteCallback != none )
{
`log("Skipping login procsss since there's already a delegate set. This means it should already be in progress");
return;
}
LoginCompleteCallback = InDel;
bLoggingInForOnlinePlay = bInLoggingInForOnlinePlay;
// XB1 does not need to login. This step should be taken care of already...but sometimes we wont be connected...
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) )
{
//Adam Massingale | KFII-39028 Changed OnOSSLoginComplete call to pass in the current connection status in vs OSCS_Connected...because we might not always be connected
OnlineSub.PlayerInterface.Login(LocalPlayer(Player).ControllerId, "", "");
//OnOSSLoginComplete( LocalPlayer(Player).ControllerId, true, OSCS_Connected );
OnOSSLoginComplete( LocalPlayer(Player).ControllerId, true, OnlineSub.SystemInterface.GetCurrentConnectionStatus());
}
// If we aren't logged in, try to log in now
else if( OnlineSub.PlayerInterface.GetLoginStatus( LocalPlayer(Player).ControllerId ) == LS_NotLoggedIn )
{
OnlineSub.PlayerInterface.AddLoginCompleteDelegate( LocalPlayer(Player).ControllerId, OnOSSLoginComplete );
OnlineSub.PlayerInterface.Login(LocalPlayer(Player).ControllerId, "", "");
}
else if( bLoggingInForOnlinePlay )
{
CheckPrivilegesForMultiplayer();
}
}
function OnOSSLoginComplete( byte LocalUserNum, bool bWasSuccessful, EOnlineServerConnectionStatus ErrorCode )
{
OnlineSub.PlayerInterface.ClearLoginCompleteDelegate( LocalUserNum, OnOSSLoginComplete );
PlayerReplicationInfo.PlayerName = LocalPlayer(Player).GetNickname();
PlayerReplicationInfo.UniqueId = LocalPlayer(Player).GetUniqueNetId();
`log("KFPlayerController.uc --- OnOSSLoginComplete --- ErrorCode == OSCS_Connected"@ErrorCode);
if( ErrorCode == OSCS_Connected )
{
if( WorldInfo.IsConsoleBuild( CONSOLE_Orbis ) )
{
GetPS4Avatar( PlayerReplicationInfo.PlayerName );
}
else
{
GetSteamAvatar( PlayerReplicationInfo.UniqueId );
}
// Do playfab login if this has not been done yet
if( PlayfabInter != none && PlayfabInter.CachedPlayfabId == "" )
{
// Kick off tss read
if( OnlineSub.Patcher != none)
{
OnlineSub.Patcher.DownloadFiles();
}
PlayfabInter.AddOnLoginCompleteDelegate(OnPlayfabLoginComplete);
PlayfabInter.Login(PlayerReplicationInfo.PlayerName);
}
else
{
if( bLoggingInForOnlinePlay )
{
CheckPrivilegesForMultiplayer();
}
else
{
OnLoginCompleted(true);
}
}
// Rebuild items in the "what's new" box
if( MyGFxManager != none && MyGFxManager.StartMenu != none && MyGFxManager.StartMenu.FindGameContainer != none )
{
MyGFxManager.StartMenu.FindGameContainer.SetWhatsNewItems();
}
}
else if( ErrorCode == OSCS_PSNUnavailable || ErrorCode == OSCS_XBLiveUnavailable )
{
// Show login UI if this is for online play
if( bLoggingInForOnlinePlay )
{
OnLoginCompleted(false);
OnlineSub.PlayerInterface.ShowLoginUI(LocalPlayer(Player).ControllerId);
}
// Inform user that they must sign into PSN to use online features
else
{
OnLoginCompleted(true);
MyGFxManager.DelayedOpenPopup(EConfirmation, EDPPID_Misc, Localize( "KFGFxMenu_IIS", "NoOnlinePlay", "KFGameConsole" ), Localize( "KFGFxMenu_IIS", ErrorCode == OSCS_PSNUnavailable ? "NotLoggedInOnlinePSN" : "LiveRequiredMessage", "KFGameConsole" ), class'KFCommon_LocalizedStrings'.default.OKString );
}
}
else if( ErrorCode == OSCS_NoNetworkConnection )
{
OnLoginCompleted( !bLoggingInForOnlinePlay );
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_Misc,
Localize("Notifications", "NotConnectedTitle", "KFGameConsole"),
Localize("Notifications", bLoggingInForOnlinePlay ? "NotConnectedMessage" : "NotConnectedForOnlinePlay", "KFGameConsole"),
class'KFCommon_LocalizedStrings'.default.OKString);
}
else if( ErrorCode == OSCS_TooYoung )
{
OnLoginCompleted(!bLoggingInForOnlinePlay);
// Toggle menus if not actively open
if( !MyGFxManager.bMenusOpen )
{
MyGFxManager.ToggleMenus();
}
MyGFxManager.DelayedOpenPopup(EConfirmation, EDPPID_Misc, Localize( "KFGFxMenu_IIS", "NoOnlinePlay", "KFGameConsole" ), Localize( "KFGFxMenu_IIS", "TooYoung", "KFGameConsole" ), class'KFCommon_LocalizedStrings'.default.OKString );
}
// Patch required
else if( ErrorCode == OSCS_UpdateRequired )
{
OnLoginCompleted(!bLoggingInForOnlinePlay);
// Toggle menus if not actively open
if( !MyGFxManager.bMenusOpen )
{
MyGFxManager.ToggleMenus();
}
MyGFxManager.DelayedOpenPopup(EConfirmation, EDPPID_Misc, Localize( "KFGFxMenu_IIS", "NoOnlinePlay", "KFGameConsole" ), Localize( "KFGFxMenu_IIS", "PatchAvailable", "KFGameConsole" ), class'KFCommon_LocalizedStrings'.default.OKString );
}
else if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) && ErrorCode != OSCS_Connected )
{
OnLoginCompleted( !bLoggingInForOnlinePlay );
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_Misc,
Localize("Notifications", "NotConnectedTitle", "KFGameConsole"),
Localize("Notifications", bLoggingInForOnlinePlay ? "NotConnectedMessage" : "NotConnectedForOnlinePlay", "KFGameConsole"),
class'KFCommon_LocalizedStrings'.default.OKString);
}
// Unhandled error. Shouldn't ever hit this, but its there to warn us in case if does happen
else if( ErrorCode != OSCS_Connected )
{
OnLoginCompleted( false );
}
}
singular function CheckPrivilegesForMultiplayer()
{
local EFeaturePrivilegeLevel HintPrivLevel;
if ( class'KFGameEngine'.static.IsFreeConsolePlayOver() )
{
MyGFxManager.DelayedOpenPopup(EConfirmation, EDPPID_Misc, "",
class'KFCommon_LocalizedStrings'.default.FreeConsolePlayOverString,
class'KFCommon_LocalizedStrings'.default.BuyGameString,
class'KFCommon_LocalizedStrings'.default.OKString, OnBuyGamePressed);
OnLoginCompleted(false);
return;
}
// For XB1, we need to ensure we have an internet connection
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) && bLoggingInForOnlinePlay && OnlineSub.SystemInterface.GetCurrentConnectionStatus() != OSCS_Connected )
{
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_JoinFailure,
Localize("Notifications", "MultiplayerNotAllowed_Title", "KFGameConsole"),
Localize("Notifications", "NotConnectedForOnlinePlayXB1", "KFGameConsole"),
class'KFCommon_LocalizedStrings'.default.OKString);
OnLoginCompleted(false);
return;
}
bOnlinePrivilegeCheckPending = true;
OnlineSub.PlayerInterface.AddPrivilegeLevelCheckedDelegate(OnCanPlayOnlineCheckForMatchmakingComplete);
OnlineSub.PlayerInterface.CanPlayOnline(LocalPlayer(Player).ControllerId, HintPrivLevel, true);
}
/** Called when player selects buy game from the end of demo popup */
function OnBuyGamePressed()
{
if( class'WorldInfo'.static.IsConsoleBuild( CONSOLE_Orbis ) )
{
OnlineSub = Class'GameEngine'.static.GetOnlineSubsystem();
OnlineSub.OpenGameStorePage();
}
}
function OnCanPlayOnlineCheckForMatchmakingComplete(byte LocalUserNum, EFeaturePrivilege Privilege, EFeaturePrivilegeLevel PrivilegeLevel, bool bDiffersFromHint)
{
// If it's not FP_OnlinePlay, we caught another async check that we don't care about...
if(Privilege == FP_OnlinePlay)
{
bOnlinePrivilegeCheckPending = false;
OnlineSub.PlayerInterface.ClearPrivilegeLevelCheckedDelegate(OnCanPlayOnlineCheckForMatchmakingComplete);
if(PrivilegeLevel == FPL_Enabled)
{
OnLoginCompleted(true);
}
else
{
if (class'WorldInfo'.static.IsConsoleBuild(CONSOLE_Durango))
{
MyGFxManager.DelayedOpenPopup(ENotification, EDPPID_JoinFailure,
Localize("Notifications", "MultiplayerNotAllowed_Title", "KFGameConsole"),
Localize("Notifications", "MultiplayerNotAllowed_MessageLive", "KFGameConsole"),
class'KFCommon_LocalizedStrings'.default.OKString);
}
else
{
OnlineSub.PlayerInterfaceEx.UpsellPremiumOnlineService();
}
OnLoginCompleted(false);
}
}
}
function OnPlayfabLoginComplete(bool bWasSuccessful, string SessionTicket, string PlayfabId)
{
PlayfabInter.ClearOnLoginCompleteDelegate(OnPlayfabLoginComplete);
// Cache this on the PRI
PlayerReplicationInfo.PlayfabPlayerId = PlayfabId;
// Kick off store read
KFGameEngine(class'Engine'.static.GetEngine()).ReadPFStoreData();
// Kick off title data read
PlayfabInter.AddTitleDataReadCompleteDelegate(OnClientTitleDataRead);
PlayfabInter.ReadTitleData();
// Xbox players get exclusive rewards. Try and claim them now if they haven't been already claimed
if( WorldInfo.IsConsoleBuild( CONSOLE_Durango ) )
{
PlayfabInter.ExecuteCloudScript( "ClaimXboxExclusives", none );
}
}
function OnClientTitleDataRead()
{
PlayfabInter.ClearTitleDataReadCompleteDelegate(OnClientTitleDataRead);
// TODO: Take action on title data?
if (bLoggingInForOnlinePlay)
{
CheckPrivilegesForMultiplayer();
}
else
{
OnLoginCompleted(true);
}
}
simulated function PerformLogout()
{
ClientWriteAndFlushStats();
// Clear the stats read/write objects. They need to be recreated
StatsRead = none;
StatsWrite = none;
// Reinitialize stats
InitializeStats();
ClearDownloadInfo();
if (WorldInfo.bIsMenuLevel)
{
// Quit matchmaking
if( MyGFxManager.StartMenu != none && MyGFxManager.StartMenu.GetStartMenuState() == EMatchmaking )
{
MyGFxManager.StartMenu.ApproveMatchMakingLeave();
}
// Re-initialize the frontend menu so all settings can be reset properly
MyGFxManager.Close();
MyGFxManager = none;
// Clear login delegate
LoginCompleteCallback = none;
// Ensure this gets reset properly
KFGameViewportClient( class'Engine'.static.GetEngine().GameViewport ).bSeenIIS = false;
KFGameViewportClient( class'Engine'.static.GetEngine().GameViewport ).bAllowInputFromMultipleControllers = true;
ClientSetFrontEnd( KFGameInfo(WorldInfo.Game).KFGFxManagerClass );
}
else
{
ConsoleCommand("open KFMainMenu");
}
}
`if(`notdefined(ShippingPC))
/** Clientside cheats for dev build only */
exec function EnableEmoteDebugging()
{
ConsoleCommand("SETNOPEC KFEmoteList bDebugEmotes true");
}
exec function DisableEmoteDebugging()
{
ConsoleCommand("SETNOPEC KFEmoteList bDebugEmotes false");
}
exec function SetEquippedEmote( int ItemId )
{
EnableEmoteDebugging();
ConsoleCommand("SETNOPEC KFEmoteList EquippedEmoteId"@ItemId);
}
`endif
//-----------------------------------------------------------------------------
// Mixer Integration
//-----------------------------------------------------------------------------
reliable client function ClientMatchStarted()
{
local bool bTriggerGroups;
bTriggerGroups = MixerCurrentDefaultScene != "SelectSide" ? true : false;
MixerCurrentDefaultScene = "SelectSide";
if (bTriggerGroups)
{
MixerMoveUsersToDefaultGroup();
}
BeginningRoundVaultAmount = GetTotalDoshCount();
}
reliable client function ClientMatchEnded()
{
MixerCurrentDefaultScene = "default";
MixerMoveUsersToDefaultGroup();
}
//Triggered when mixer has finished its internal initialization routines
// Triggers a timer to move all users to the startup default group after a short
// delay intended to avoid any final spinup of scenes and groups in the background.
simulated protected function MixerStartupComplete()
{
local KFPlayerReplicationInfo KFPRI;
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
MixerCurrentDefaultScene = (KFPRI != none && KFPRI.bHasSpawnedIn) ? "SelectSide" : "default";
SetTimer(1.f, false, nameof(MixerMoveUsersToDefaultGroup));
}
simulated final function GetCurrentDefaultMixerScene(out string DefaultSceneName)
{
DefaultSceneName = MixerCurrentDefaultScene;
}
simulated final protected function MixerMoveUsersToDefaultGroup()
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
Mixer.MoveAllUsersToGroup(MixerCurrentDefaultScene);
}
}
simulated final function InitializeMixer()
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
Mixer.StartInteractiveSession(LocalPlayer(Player).ControllerId);
}
}
simulated final function ShutdownMixer()
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
if (class'MixerIntegration'.static.IsMixerInteractionEnabled())
{
Mixer.StopInteractiveSession();
}
}
}
simulated final function InitMixerDelegates()
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
Mixer.MixerStartupComplete = MixerStartupComplete;
Mixer.HandleMixerButtonEvent = HandleMixerButtonEvent;
Mixer.GetGroupBuildList = GetGroupList;
Mixer.GetIntendedDefaultScene = GetCurrentDefaultMixerScene;
}
}
simulated final function ClearMixerDelegates()
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
Mixer.MixerStartupComplete = None;
Mixer.HandleMixerButtonEvent = none;
Mixer.GetGroupBuildList = none;
Mixer.GetIntendedDefaultScene = None;
}
}
simulated final event UpdateMixer()
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
Mixer.TickMixer();
}
}
simulated final function GetGroupList(out array<string> GroupsToBuild)
{
GroupsToBuild.AddItem("SelectSide");
GroupsToBuild.AddItem("Helpers");
GroupsToBuild.AddItem("Hurters");
GroupsToBuild.AddItem("VersusZed");
}
simulated final function TestMixerCall(string Button, out array<string> MetaKeys, out array<string> MetaProps)
{
`if(`notdefined(FINAL_RELEASE))
HandleMixerButtonEvent(Button, "", "UserTest", MetaKeys, MetaProps);
`endif
}
//Delegate params:
// ControlID - Button that triggered the event
// TransactionID - Unique ID for the event
// Username - Participant that triggered the event if it's useful info
simulated private final function HandleMixerButtonEvent(string ControlId, string TransactionId, string Username, out array<string> MetaKeys, out array<string> MetaProps)
{
local int AmountValue, CooldownValue, Idx;
local string StringMetadata;
//Allow high level group flow regardless of user state
if (ControlId == "HelpButton" || ControlId == "HurtButton" || ControlId == "BackButton" || ControlId == "BackButtonHurt")
{
MixerMoveUserToGroup(ControlId, Username);
return;
}
//Throw out other inputs if we're paused
if (IsPaused())
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
return;
}
//Situation should never occur, but verify to be safe
if (MetaKeys.Length != MetaProps.Length)
{
`log("*** Key/Prop pairs mismatch in Mixer button event. This should never occur.");
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
return;
}
//Get vals from metadata
for (Idx = 0; Idx < MetaKeys.Length; ++Idx)
{
if (MetaKeys[Idx] ~= "Amount")
{
AmountValue = int(MetaProps[Idx]);
}
else if (MetaKeys[Idx] ~= "Type")
{
StringMetadata = MetaProps[Idx];
}
else if (MetaKeys[Idx] ~= "Cooldown")
{
CooldownValue = int(MetaProps[Idx]) * 1000; //to MS
}
}
switch (ControlId)
{
case "AmmoButton":
MixerGiveAmmo(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
case "ArmorButton":
MixerGiveArmor(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
case "DoshButton":
MixerGiveDosh(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
case "GrenadeButton":
MixerGiveGrenades(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
case "HealButton":
case "HealButtonVs":
MixerHealUser(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
case "ZedTimeButton":
MixerCauseZedTime(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
case "EnrageButton":
case "EnrageButtonVs":
MixerEnrageZeds(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
case "PukeButton":
MixerPukeUser(ControlId, TransactionId, AmountValue, CooldownValue, Username);
break;
//@TODO: Refactor this into a generic Zed___ string that we can parse and add to the Mixer config at any time
case "FleshpoundButton":
case "FleshpoundButtonVs":
case "ScrakeButton":
case "ScrakeButtonVs":
case "MiniFPButton":
case "MiniFPButtonVs":
MixerSpawnZed(ControlId, TransactionId, StringMetadata, AmountValue, CooldownValue, Username);
break;
//Couple examples for future use
//Client authority events should be called immediately. Within the function, SendMixerEventResult() should be called if valid
case "ClientTest":
MixerClientTest(ControlId, TransactionId);
break;
//Server authority events need to do two steps:
// 1. Client will directly call the server and pass the control and transaction IDs along with the call
// 2. Server will call the client with the result. If the result is invalid, calling the client is optional as unhandled transactions refund after 5 minutes
// Once the client receives a callback, it should call SendMixerEventResult() with a cooldown and result from the server
case "ServerTest":
MixerServerTest(ControlId, TransactionId);
break;
}
}
simulated private final function MixerClientTest(string ControlId, string TransactionId)
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
`log("*** Mixer: This is a totally valid action on the client!");
Mixer.SendMixerEventResult(ControlId, TransactionId, 1, 5000);
}
}
reliable private final server function MixerServerTest(string ControlId, string TransactionId)
{
local int Random;
Random = Rand(2);
if (Random == 0)
{
`log("*** Mixer: Server decided the action failed!");
MixerServerCallback(ControlId, TransactionId, 0, 5000);
}
else if (Random == 1)
{
`log("*** Mixer: Server decided the action was valid!");
MixerServerCallback(ControlId, TransactionId, 1, 5000);
}
}
reliable private final client function MixerServerCallback(string ControlId, string TransactionId, int Result, int Cooldown, optional string Username)
{
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
Mixer.SendMixerEventResult(ControlId, TransactionId, Result, Cooldown);
if(Result == 1 && MyGFxHUD != none)
{
switch (ControlId)
{
case "AmmoButton":
PlayAKEvent(class'KFPerk_Support'.static.GetReceivedAmmoSound());
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerGaveAmmoString);
break;
case "ArmorButton":
PlayAKEvent(class'KFPerk_Support'.static.GetReceivedArmorSound());
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerGaveArmorString);
break;
case "DoshButton":
Pawn.PlaySoundBase(AkEvent'WW_UI_PlayerCharacter.Play_UI_Pickup_Dosh');
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerGaveDoshString);
break;
case "GrenadeButton":
PlayAKEvent(class'KFPerk_Support'.static.GetReceivedAmmoSound());
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerGaveGrenadeString);
break;
case "HealButton":
case "HealButtonVs":
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerGaveHealthString);
break;
case "ZedTimeButton":
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerZedTimeString);
break;
case "EnrageButton":
case "EnrageButtonVs":
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerRageZedsString);
break;
case "FleshpoundButton":
case "FleshpoundButtonVs":
MyGFxHUD.ShowNonCriticalMessage( Username$class'KFCommon_LocalizedStrings'.default.MixerSpawnedFPString);
break;
case "ScrakeButton":
case "ScrakeButtonVs":
MyGFxHUD.ShowNonCriticalMessage(Username$class'KFCommon_LocalizedStrings'.default.MixerSpawnedScrakeString);
break;
case "MiniFPButton":
case "MiniFPButtonVs":
MyGFxHUD.ShowNonCriticalMessage(Username$class'KFCommon_LocalizedStrings'.default.MixerSpawnedMiniFPString);
break;
}
}
}
}
reliable private final server function MixerGiveAmmo(string ControlId, string TransactionId, int Amount, int Cooldown, optional string Username)
{
local KFInventoryManager KFIM;
KFIM = KFInventoryManager(Pawn.InvManager);
if (KFIM != none && KFIM.GiveWeaponsAmmo(false))
{
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
}
else
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
}
reliable private final server function MixerGiveArmor(string ControlId, string TransactionId, int Amount, int Cooldown, string Username)
{
local KFInventoryManager KFIM;
KFIM = KFInventoryManager(Pawn.InvManager);
if (KFIM != none && KFIM.AddArmor(Amount))
{
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
}
else
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
}
reliable private final server function MixerGiveDosh(string ControlId, string TransactionId, int Amount, int Cooldown, string Username)
{
local KFPlayerReplicationInfo KFPRI;
local KFPawn_Human KFPH;
KFPH = KFPawn_Human(Pawn);
KFPRI = KFPlayerReplicationInfo(Pawn.PlayerReplicationInfo);
if (KFPH != none && KFPRI != none)
{
KFPRI.AddDosh(Amount);
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
}
else
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
}
reliable private final server function MixerGiveGrenades(string ControlId, string TransactionId, int Amount, int Cooldown, string Username)
{
local KFInventoryManager KFIM;
local KFPawn_Human KFPH;
KFPH = KFPawn_Human(Pawn);
KFIM = KFInventoryManager(Pawn.InvManager);
if (KFPH != none && KFIM != none && KFIM.AddGrenades(Amount))
{
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
}
else
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
}
reliable private final server function MixerHealUser(string ControlId, string TransactionId, int Amount, int Cooldown, string Username)
{
local KFPawn KFP;
KFP = KFPawn(Pawn);
if (KFP != none && KFP.Health < KFP.HealthMax)
{
KFP.HealDamage(Amount, self, class'KFDT_Healing', false, false);
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
}
else
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
}
reliable private final server function MixerCauseZedTime(string ControlId, string TransactionId, int Amount, int Cooldown, string Username)
{
local KFGameInfo KFGI;
local KFGameReplicationInfo KFGRI;
KFGI = KFGameInfo(WorldInfo.Game);
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
if (KFGI != none && KFGRI != None && !KFGRI.bTraderIsOpen)
{
bForcePartialZedTime = true;
KFGI.DramaticEvent(100, Amount);
bForcePartialZedTime = default.bForcePartialZedTime;
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
}
else
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
}
reliable private final server function MixerEnrageZeds(string ControlId, string TransactionId, int Radius, int Cooldown, string Username)
{
local int RalliedCount;
local KFPawn_Monster KFPM;
local KFPawn KFPOwner;
RalliedCount = 0;
KFPOwner = KFPawn(Pawn);
if (KFPOwner != None)
{
// Rally nearby zeds
foreach KFPOwner.WorldInfo.GRI.VisibleCollidingActors(class'KFPawn_Monster', KFPM, Radius, Pawn.Location)
{
if (KFPM.Rally(KFPOwner, ParticleSystem'ZED_Clot_EMIT.FX_ClotA_Rage_01', 'Root', vect(0, 0, 2), ParticleSystem'ZED_Clot_EMIT.FX_Player_Zed_Buff_01', MixerRallyBoneNames, vect(0, 0, 0)))
{
++RalliedCount;
}
}
}
if (RalliedCount > 0)
{
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
}
else
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
}
simulated private final function MixerPukeUser(string ControlId, string TransactionId, float PukeLength, int Cooldown, string UserName)
{
local MixerIntegration Mixer;
local bool bRestartPuke;
bRestartPuke = BloatPukeEffectTimeRemaining <= 0.f;
BloatPukeEffectTimeRemaining = Max(BloatPukeEffectTimeRemaining, PukeLength);
if (bRestartPuke)
{
//TBD: Allow perk to use the light effect, or always use default as a mixer feature?
ClientSpawnCameraLensEffect(class'KFCameraLensEmit_Puke');
}
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
PlayAkEvent(AkEvent'WW_ZED_Gore.Impact_Puke_Damage');
MyGFxHUD.ShowNonCriticalMessage(Username$class'KFCommon_LocalizedStrings'.default.MixerPukeString);
Mixer.SendMixerEventResult(ControlId, TransactionId, 1, Cooldown);
}
}
reliable private final server function MixerSpawnZed(string ControlId, string TransactionId, string ZedClass, int Amount, int Cooldown, string UserName)
{
local class<KFPawn_Monster> SpawnClass;
local array<class<KFPawn_Monster> > AIToSpawn;
local KFGameInfo KFGI;
local int SpawnCount, i;
local KFPawn_Monster BossCheck;
local bool bFoundBoss;
KFGI = KFGameInfo(WorldInfo.Game);
if (KFGI != None)
{
if (KFGI.MyKFGRI.IsBossWave())
{
bFoundBoss = false;
foreach WorldInfo.AllPawns(class'KFPawn_Monster', BossCheck)
{
if (BossCheck.IsABoss())
{
bFoundBoss = true;
//Don't allow spawn during boss camera
if (KFInterface_MonsterBoss(BossCheck) != none && KFInterface_MonsterBoss(BossCheck).UseAnimatedBossCamera())
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
return;
}
}
}
//Boss hasn't spawned, don't allow spawning of fleshpounds yet
if (!bFoundBoss)
{
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
return;
}
}
SpawnClass = class<KFPawn_Monster>(DynamicLoadObject("KFGameContent." $ ZedClass, class'Class'));
if (SpawnClass != None)
{
for (i = 0; i < Amount; ++i)
{
AIToSpawn.AddItem(SpawnClass);
}
SpawnCount = KFGI.SpawnManager.SpawnSquad(AIToSpawn);
if (SpawnCount > 0)
{
KFGameReplicationInfo(KFGI.GameReplicationInfo).AIRemaining += SpawnCount;
MixerServerCallback(ControlId, TransactionId, 1, Cooldown, Username);
return;
}
}
}
MixerServerCallback(ControlId, TransactionId, 0, 0, Username);
}
simulated private final function MixerMoveUserToGroup(string ControlId, string UserName)
{
local string GroupName;
local MixerIntegration Mixer;
Mixer = class'PlatformInterfaceBase'.static.GetMixerIntegration();
if (Mixer != none)
{
GroupName = "";
switch (ControlId)
{
case "HelpButton":
GroupName = "Helpers";
break;
case "HurtButton":
GroupName = "Hurters";
break;
case "BackButton":
case "BackButtonHurt":
GroupName = "SelectSide";
break;
}
if (GroupName != "")
{
Mixer.MoveUserToGroup(Username, GroupName);
}
}
}
//-----------------------------------------------------------------------------
// LED Effects Integrations
//-----------------------------------------------------------------------------
simulated function InitLEDManager()
{
if (!WorldInfo.IsConsoleBuild() && LEDEffectsManager == none)
{
if (Role == ROLE_Authority && WorldInfo.NetMode != NM_DedicatedServer)//solo
{
LEDEffectsManager = new(self) LEDEffectsManagerClass;
LEDEffectsManager.InitLEDEffects();
}
ClientInitLEDManager();
}
}
reliable client function ClientInitLEDManager()
{
if (Role != ROLE_Authority)
{
LEDEffectsManager = new(self) LEDEffectsManagerClass;
LEDEffectsManager.InitLEDEffects();
}
}
simulated final event UpdateAlienFX()
{
local AlienFXLEDInterface AlienFXLED;
AlienFXLED = class'PlatformInterfaceBase'.static.GetAlienFXIntegration();
if (AlienFXLED != none)
{
AlienFXLED.UpdateAlienFX();
}
}
//LED overrides
//int RedPercent, int GreenPercent, int BluePercent, int MilliSecondsDuration, int MilliSecondsInterval
unreliable client event ClientSpawnCameraLensEffect(class<EmitterCameraLensEffectBase> LensEffectEmitterClass)
{
super.ClientSpawnCameraLensEffect(LensEffectEmitterClass);
if (IsLocalController() && LEDEffectsManager != none)
{
switch (LensEffectEmitterClass)
{
case class'KFCameraLensEmit_Puke_Light' :
LEDEffectsManager.PlayEffectGas();
break;
case class'KFCameraLensEmit_Fire' :
LEDEffectsManager.PlayEffectFire();
break;
}
}
}
//-----------------------------------------------------------------------------
// Discord Integration
//-----------------------------------------------------------------------------
simulated function InitDiscord()
{
local DiscordRPCIntegration Discord;
Discord = class'PlatformInterfaceBase'.static.GetDiscordRPCIntegration();
if (Discord != none)
{
Discord.JoinLobby = JoinDiscordLobby;
UpdateDiscordRichPresence();
SetTimer(5.f, true, nameof(UpdateDiscordRichPresence));
}
}
simulated function ClearDiscord()
{
local DiscordRPCIntegration Discord;
Discord = class'PlatformInterfaceBase'.static.GetDiscordRPCIntegration();
if (Discord != none)
{
Discord.JoinLobby = none;
}
}
simulated function JoinDiscordLobby(qword qLobbyId)
{
local KFOnlineLobbySteamworks SteamworksLobby;
local UniqueNetId LobbyId;
if (OnlineSub != none)
{
SteamworksLobby = KFOnlineLobbySteamworks(OnlineSub.GetLobbyInterface());
if (SteamworksLobby != none)
{
LobbyId.Uid = qLobbyId;
SteamworksLobby.OnLobbyInvite(LobbyId, LobbyId, true);
}
}
}
simulated event UpdateDiscord()
{
local DiscordRPCIntegration Discord;
Discord = class'PlatformInterfaceBase'.static.GetDiscordRPCIntegration();
if (Discord != none)
{
Discord.TickDiscord();
}
}
simulated function UpdateDiscordRichPresence()
{
if (WorldInfo.bIsMenuLevel)
{
CreateDiscordMenuPresence();
}
else
{
CreateDiscordGamePresence();
}
}
simulated function CreateDiscordMenuPresence()
{
local string PresenceString;
local qword LobbyId;
local int CurrentPlayers, MaxPlayers;
local KFOnlineLobbySteamworks SteamworksLobby;
local DiscordRPCIntegration Discord;
if (class'WorldInfo'.Static.IsConsoleBuild())
{
return; //Should not be attempting to make a discord call on Console
}
Discord = class'PlatformInterfaceBase'.static.GetDiscordRPCIntegration();
PresenceString = class'KFCommon_LocalizedStrings'.default.DiscordMenuPresenceString;
if (Discord != none && Discord.bDiscordReady)
{
if (OnlineSub != none)
{
//If we're actively forming a party, send down the lobby UID to Discord so the user can invite other players through the Discord interface
// We allow this for all lobby types, as the user has to manually invite or allow users into the lobby
SteamworksLobby = KFOnlineLobbySteamworks(OnlineSub.GetLobbyInterface());
if (SteamworksLobby != none && SteamworksLobby.ActiveLobbies.Length >= 1 && SteamworksLobby.CurrentLobbyId != SteamworksLobby.ZeroUniqueId)
{
LobbyId = SteamworksLobby.CurrentLobbyId.Uid;
MaxPlayers = 6;
CurrentPlayers = SteamworksLobby.ActiveLobbies[0].Members.Length;
PresenceString = class'KFCommon_LocalizedStrings'.default.DiscordPartyPresenceString;
}
}
Discord.CreateMenuPresence(PresenceString, LobbyId, CurrentPlayers, MaxPlayers);
}
}
simulated function CreateDiscordGamePresence()
{
local string PresenceString, DetailsString;
local KFGameReplicationInfo GRI;
local DiscordRPCIntegration Discord;
Discord = class'PlatformInterfaceBase'.static.GetDiscordRPCIntegration();
if (Discord != none && Discord.bDiscordReady)
{
GRI = KFGameReplicationInfo(WorldInfo.GRI);
if (GRI != None)
{
DetailsString = GRI.GameClass != None ? GRI.GameClass.default.GameName : "";
PresenceString = WorldInfo.NetMode != NM_Standalone ? class'KFCommon_LocalizedStrings'.default.DiscordNetworkMatchString : class'KFCommon_LocalizedStrings'.default.DiscordSoloMatchString;
if (GRI.WaveNum == 0)
{
DetailsString = DetailsString $ class'KFCommon_LocalizedStrings'.default.DiscordMatchLobbyString;
}
else if (GRI.bTraderIsOpen)
{
DetailsString = DetailsString $ class'KFCommon_LocalizedStrings'.default.DiscordTraderTimeString;
}
else
{
if (GRI.IsBossWave())
{
DetailsString = DetailsString $ class'KFCommon_LocalizedStrings'.default.DiscordBossWaveString;
}
else
{
DetailsString = DetailsString $ class'KFCommon_LocalizedStrings'.default.DiscordWaveString $ GRI.WaveNum $ "/" $ GRI.GetFinalWaveNum();
}
}
Discord.CreateGamePresence(PresenceString, DetailsString, WorldInfo.GetMapName(), GRI.GetNumPlayers(), WorldInfo.NetMode != NM_Standalone ? GRI.MaxHumanCount : 1);
}
}
}
event MenuInfo GetMenuInfo()
{
local MenuInfo Info;
Info.CurrentMenuIndex = MyGFxManager.CurrentMenuIndex;
Info.bMenusOpen = MyGFxManager.bMenusOpen;
Info.bIsStartMenu = MyGFxManager.CurrentMenuIndex == UI_Start;
return Info;
}
event ShowInviteMessage(string InviteMessageName)
{
`log("KFPlayerController: ShowInviteMessage");
MyGFxHUD.ShowInviteMessage(InviteMessageName);
}
function OnDrawCountText(KFSeqAct_DrawCountText inAction)
{
if(inAction.InputLinks[0].bHasImpulse)
{
if( WorldInfo.NetMode == NM_DedicatedServer )
{
DrawCountTextOnHud(inAction.DrawTextInfo.MessageText, inAction.DisplayTimeSeconds);
}
else if( MyGFxHUD != none )
{
MyGFxHUD.DisplayMapCounterText(inAction.DrawTextInfo.MessageText, inAction.DisplayTimeSeconds);
}
}
}
reliable client function DrawCountTextOnHud(string Message, float DisplayTime)
{
if( MyGFxHUD != none )
{
MyGFxHUD.DisplayMapCounterText(Message, DisplayTime);
}
}
function OnDrawLocalizedText(KFSeqAct_DrawLocalizedText inAction)
{
if(inAction.InputLinks[0].bHasImpulse)
{
if( WorldInfo.NetMode == NM_DedicatedServer )
{
DrawLocalizedTextOnHud(inAction.DrawTextInfo.MessageText, inAction.DisplayTimeSeconds);
}
else if( MyGFxHUD != none )
{
MyGFxHUD.DisplayMapText(inAction.DrawTextInfo.MessageText, inAction.DisplayTimeSeconds, true);
}
}
}
function OnDrawRandomLocalizedText(KFSeqAct_DrawRandomLocalizedText inAction)
{
if(inAction.InputLinks[0].bHasImpulse)
{
if( WorldInfo.NetMode == NM_DedicatedServer )
{
DrawLocalizedTextOnHud(inAction.DrawTextInfo.MessageText, inAction.DisplayTimeSeconds);
}
else if( MyGFxHUD != none )
{
MyGFxHUD.DisplayMapText(inAction.DrawTextInfo.MessageText, inAction.DisplayTimeSeconds, true);
}
}
}
reliable client function DrawLocalizedTextOnHud(string Message, float DisplayTime)
{
if( MyGFxHUD != none )
{
MyGFxHUD.DisplayMapText(Message, DisplayTime, true);
}
}
function bool CanUseHealObject()
{
local class<KFPowerUp> KFPowerUpClass;
local KFGameInfo GameInfo;
local bool CanHealPowerUp;
local bool CanHealGameMode;
GameInfo = KFGameInfo(WorldInfo.Game);
KFPowerUpClass = GetPowerUpClass();
CanHealPowerUp = KFPowerUpClass == none || KFPowerUpClass.default.CanBeHealedWhilePowerUpIsActive;
CanHealGameMode = GameInfo == none || GameInfo.OutbreakEvent == none || !GameInfo.OutbreakEvent.ActiveEvent.bCannotBeHealed;
return CanHealPowerUp && CanHealGameMode;
}
event OnLoginOnOtherPlatformDoneAndFriendsReady()
{
if (MyGFxManager != none)
{
MyGFxManager.OnLoginOnOtherPlatformDoneAndFriendsReady();
}
}
event OnFriendsChange()
{
if (MyGFxManager != none)
{
MyGFxManager.OnFriendsChange();
}
}
event bool FriendListPopUpIsShown()
{
local bool Result;
Result = false;
if (MyGFxManager != none)
{
Result = MyGFxManager.FriendListPopUpIsShown();
}
return Result;
}
event ShowSpeakingIcon(PlayerReplicationInfo TalkerId, bool isShowIcon)
{
local KFGFxHudWrapper MyGFxHUDW;
MyGFxHUDW = KFGFxHudWrapper(myHUD);
if(MyGFxHUDW != none && MyGFxHUDW.HudMovie != none)
{
MyGFxHUDW.HudMovie.VOIPWidget.VOIPEventTriggered(TalkerId, isShowIcon);
}
if (MyGFxManager != none)
{
MyGFxManager.UpdateVOIP(TalkerId, isShowIcon);
}
}
event OnServerTakeoverResponseRecieved()
{
`log(GetFuncName());
if (MyGFxManager != none)
{
MyGFxManager.OnServerTakeoverResponseRecieved();
}
}
defaultproperties
{
EarnedDosh=0
//Allow PC-based things to be properly ticked, but skip the rest of the tick in native
bAlwaysTick=true
MatchStatsClass=Class'EphemeralMatchStats'
InputClass=class'KFGame.KFPlayerInput'
CheatClass=class'KFGame.KFCheatManager'
CameraClass=class'KFPlayerCamera'
PurchaseHelperClass=class'KFAutoPurchaseHelper'
ServPendingPerkBuild=-1
ServPendingPerkLevel=-1
// FOV is overriden by PlayerCamera in 'FixFOV', but setting these anyway
DesiredFOV=90.0
DefaultFOV=90.0
// Set this flag so we check for sound occlusion when sounds play
bCheckSoundOcclusion=true
ZedTimeEnterSound=AkEvent'WW_GLO_Runtime.Set_ZEDTime_On'
ZedTimeExitSound=AkEvent'WW_GLO_Runtime.Set_ZEDTime_Off'
ZedTimePartialEnterSound=AkEvent'WW_GLO_Runtime.Set_ZEDTime_Partial_On'
ZedTimePartialExitSound=AkEvent'WW_GLO_Runtime.Set_ZEDTime_Partial_Off'
PauseWwiseEvent=AkEvent'WW_GLO_Runtime.Pause_All'
ResumeWwiseEvent=AkEvent'WW_GLO_Runtime.Resume_All'
//EarsRingingPlayEvent=AkEvent'WW_UI_PlayerCharacter.Play_PC_Concussion_Ear_Ring_Loop'
//EarsRingingStopEvent=AkEvent'WW_UI_PlayerCharacter.Stop_PC_Concussion_Ear_Ring_Loop'
LowHealthStartEvent=AkEvent'WW_UI_PlayerCharacter.Play_UI_Low_Health_LP'
LowHealthStopEvent=AkEvent'WW_UI_PlayerCharacter.Stop_UI_Low_Health_LP'
ResetFiltersEvent=AkEvent'WW_UI_PlayerCharacter.Reset_LowPass_Filters'
FlashlightOnEvent=AkEvent'WW_UI_PlayerCharacter.Play_WEP_Flashlight_TurnOn'
FlashlightOffEvent=AkEvent'WW_UI_PlayerCharacter.Play_WEP_Flashlight_TurnOff'
NightVisionOnEvent=AkEvent'WW_UI_PlayerCharacter.Play_WEP_Nightvision_TurnOn'
NightVisionOffEvent=AkEvent'WW_UI_PlayerCharacter.Play_WEP_Nightvision_TurnOff'
AllMapCollectiblesFoundEvent=AkEvent'WW_UI_PlayerCharacter.Play_UI_Collectible_CollectAll'
Begin Object class=AkComponent name=AkComponent_0
BoneName=Root // needs bone name so Wwise doesn't highjack it
bStopWhenOwnerDestroyed=true
End Object
StingerAkComponent=AkComponent_0
Components.Add(AkComponent_0)
// Gameplay Postprocess Effects
GameplayPostProcessEffectName=GameplayEffect
EffectPainParamName=Effect_Pain
PainEffectDuration=0.5f
EffectLowHealthParamName=Effect_LowHealth
EffectZedTimeParamName=Effect_ZEDTIME
EffectZedTimeSepiaParamName=Effect_ZEDSEPIA
EffectNightVisionParamName=Effect_NightVision
EffectSirenScreamParamName=Effect_Siren
SonicScreamEffectDuration=6.f
EffectBloatsPukeParamName=Effect_Puke
BloatPukeEffectDuration=2.f
EffectFlashBangParamName=Effect_FlashBang
FlashBangEffectDuration=3.f
EffectHealParamName=Effect_Heal
EffectPerkParamName=Effect_PerkSkill
HealEffectDuration=1.f
CurrentZEDTimeEffectIntensity=-1.f
EffectPowerUpHellishRageParamName=Effect_PowerUp_HellishRage
HellishRagePowerUpEffectDuration=1.f
LowHealthThreshold=50
PartialZEDTimeEffectIntensity=0.35f
Begin Object Class=PointLightComponent Name=NVGLightTemplate_0
Brightness=0.05
Radius=800
CastShadows=FALSE
CastStaticShadows=FALSE
bAIIgnoreLuminosity=TRUE
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
bEnabled=FALSE
bDisableSpecular=TRUE
End Object
NVGLightTemplate=NVGLightTemplate_0
Begin Object Class=PointLightComponent Name=AmplificationLightTemplate_0
Brightness=0.02
Radius=200
FalloffExponent=2.f
CastShadows=FALSE
CastStaticShadows=FALSE
bAIIgnoreLuminosity=TRUE
LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
bEnabled=TRUE
bDisableSpecular=TRUE
End Object
AmplificationLightTemplate=AmplificationLightTemplate_0
// Depth of field settings
bDOFEnabled=false
DOFFocusBlendRate=2.0f
DOFMaxFocusDepth=5000.0f // 50 meters max focal distance
DOFMaxEnemyAngle=15.0f // 15 degrees
DOFFocalRange=0.25f
DOFFocalAperture=0.2f
bGamePlayDOFActive=false
bIronSightsDOFActive=false
DOF_GP_LerpControl=0.0f
DOF_IS_LerpControl=0.0f
DOF_NVG_BlendInSpeed=4.0f
DOF_NVG_BlendOutSpeed=10.0f
NVG_FocusBlendRate=3.0f
NVG_ImageGrainScale=6.0f
CIN_ImageGrainScale=5.0f
NVG_DOF_FocalDistance=1200.0
NVG_DOF_SharpRadius=1000.0
NVG_DOF_FocalRadius=1200.0
NVG_DOF_MinBlurSize=0.0
NVG_DOF_MaxNearBlurSize=4.0
NVG_DOF_MaxFarBlurSize=3.0
NVG_DOF_ExpFalloff=1.0
DOF_Cinematic_BlendInSpeed=50.0
DOF_Cinematic_BlendOutSpeed=10.0
DOF_Cinematic_FocalDistance=300
DOF_Cinematic_SharpRadius=200.0
DOF_Cinematic_FocalRadius=300.0
DOF_Cinematic_MinBlurSize=0.0
DOF_Cinematic_MaxNearBlurSize=0.0
DOF_Cinematic_MaxFarBlurSize=4.0
DOF_Cinematic_ExpFalloff=1.0
// Reflections
bReflectionsEnabled=true
bBlurEnabled=false;
BlurStrength=0.0;
BlurBlendInSpeed=1.0;
BlurBlendOutSpeed=1.0;
BlurLerpControl=0.0;
//NavigationHandleClass=class'KFNavigationHandle'
PerkList.Add((PerkClass=class'KFPerk_Berserker'))
PerkList.Add((PerkClass=class'KFPerk_Commando')
PerkList.Add((PerkClass=class'KFPerk_Support')
PerkList.Add((PerkClass=class'KFPerk_FieldMedic'))
PerkList.Add((PerkClass=class'KFPerk_Demolitionist'))
PerkList.Add((PerkClass=class'KFPerk_Firebug'))
PerkList.Add((PerkClass=class'KFPerk_Gunslinger'))
PerkList.Add((PerkClass=class'KFPerk_Sharpshooter'))
PerkList.Add((PerkClass=class'KFPerk_SWAT'))
PerkList.Add((PerkClass=class'KFPerk_Survivalist'))
TrackerXPosition=0.67
TrackerYPosition=0.025
TrackingMapScale=1.0
TrackingMapRange=5000
CurrentTrackerRangeMode=ETR_Custom
CurrentTrackingMode=ETM_All
TrackerSpawnVolumeSizeX=15
TrackerSpawnVolumeSizeY=13
bTrackingMapTopView=True
ScoreTargetDistanceCurve=(Points=((InVal=0.0,OutVal=0.3),(InVal=200.0,OutVal=0.02),(InVal=5000.0,OutVal=0.0)))
MaxAimCorrectionDistance=10000.f
bSkipExtraLOSChecks=true
MusicMessageType=Music
UpdateSpectatorActiveInterval=1.0
BenefactorDoshReq=1000
NextSpectatorDelay=2.0
RefreshObjectiveUITime=1.0f
DefaultAvatarPath = "UI_World_TEX.KF2Icon_Default"
MixerCurrentDefaultScene="default"
MixerRallyBoneNames[0]=necksocket
MixerRallyBoneNames[1]=necksocket
LEDEffectsManagerClass=class'KFLEDEffectsManager';
DebugLastSeenDoshVaultValue=INDEX_NONE
DebugCurrentDoshVaultValue=INDEX_NONE
DebugCurrentDoshVaultTier=INDEX_NONE
BeginningRoundVaultAmount=INDEX_NONE
RotationSpeedLimit=-1.f;
RotationAdjustmentInterval = 0.1f;
CurrentRotationAdjustmentTime = 0.0f;
}