12381 lines
374 KiB
Ucode
12381 lines
374 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;
|
|
/** Index of the weapon chosen for the survival perk */
|
|
var public byte SurvivalPerkWeapIndex;
|
|
/** Index of the grenade chosen for the survival perk */
|
|
var public byte SurvivalPerkGrenIndex;
|
|
|
|
/** 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;
|
|
|
|
var array<KFPawn_Monster> KilledZeds;
|
|
var array<float> KilledZedsLastZPosition;
|
|
var KFSeaTrigger SeaTrigger;
|
|
|
|
/*********************************************************************************************
|
|
* @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;
|
|
|
|
/*********************************************************************************************
|
|
* @name KFWeap_Autoturret
|
|
|
|
With the trader you can buy and sell the weapon without exploding the turrets. Add them to
|
|
the controller to keep track of them rather than the weapon.
|
|
**********************************************************************************************/
|
|
var transient array<Actor> DeployedTurrets;
|
|
|
|
/*********************************************************************************************
|
|
* @name HV Storm Cannon
|
|
|
|
Keep track of the electricity VFX Chain (Client Only)
|
|
********************************************************************************************* */
|
|
struct native KFStormCannonBeamData
|
|
{
|
|
var KFPawn_Monster SourcePawn;
|
|
var ParticleSystemComponent ParticleSystemPawn;
|
|
var ParticleSystemComponent ParticleSystemBeam;
|
|
var int ID;
|
|
|
|
structdefaultproperties
|
|
{
|
|
SourcePawn = none
|
|
ParticleSystemPawn = none
|
|
ParticleSystemBeam = none
|
|
ID = 255
|
|
}
|
|
};
|
|
|
|
var array<KFStormCannonBeamData> StormCannonBeamData;
|
|
|
|
var byte StormCannonIDCounter;
|
|
|
|
var transient bool bShotgunJumping;
|
|
|
|
var bool bAllowSeasonalSkins;
|
|
|
|
|
|
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()
|
|
{
|
|
local KFSeaTrigger actor_search;
|
|
|
|
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);
|
|
}
|
|
|
|
foreach AllActors(class'KFSeaTrigger', actor_search)
|
|
{
|
|
SeaTrigger = actor_search;
|
|
|
|
if (WorldInfo.NetMode == NM_Client || WorldInfo.NetMode == NM_Standalone)
|
|
{
|
|
SetTimer(1.f, true, nameOf(ZedKillsSeaDetection));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 function bool GetAllowSeasonalSkins()
|
|
{
|
|
local KFGameReplicationInfo KFGRI;
|
|
local bool bIsWWLWeekly, bIsAllowSeasonalSkins; // Situations that shouldn't allow seasonal overrides
|
|
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
|
|
bIsWWLWeekly = KFGRI != none && KFGRI.bIsWeeklyMode && KFGRI.CurrentWeeklyIndex == 12;
|
|
bIsAllowSeasonalSkins = KFGRI != none && KFGRI.bAllowSeasonalSkins;
|
|
//`Log("GetAllowSeasonalSkins: AllowSeasonalSkins: "$bIsAllowSeasonalSkins$" WWLWeekly "$bIsWWLWeekly);
|
|
if(bIsWWLWeekly || bIsAllowSeasonalSkins == false)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
simulated event name GetSeasonalStateName()
|
|
{
|
|
local int EventId, MapModifiedEventId;
|
|
local KFMapInfo KFMI;
|
|
|
|
EventId = class'KFGameEngine'.static.GetSeasonalEventID();
|
|
|
|
MapModifiedEventId = SEI_None;
|
|
|
|
KFMI = KFMapInfo(WorldInfo.GetMapInfo());
|
|
if (KFMI != none)
|
|
{
|
|
KFMI.ModifySeasonalEventId(MapModifiedEventId);
|
|
}
|
|
|
|
bAllowSeasonalSkins = GetAllowSeasonalSkins();
|
|
|
|
`Log("GetSeasonalStateName: "$EventId$" AllowSeasonalSkins: "$bAllowSeasonalSkins);
|
|
|
|
if (MapModifiedEventId == SEI_None)
|
|
{
|
|
if (bAllowSeasonalSkins == false)
|
|
{
|
|
EventId = SEI_None;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
`Log("GetSeasonalStateName: ModifySeasonalEventId: "$MapModifiedEventId);
|
|
EventId = MapModifiedEventId;
|
|
}
|
|
|
|
//Remove any year information, just get 1s digit
|
|
switch (EventId % 10)
|
|
{
|
|
case SEI_Summer:
|
|
`Log("GetSeasonalStateName: Summer");
|
|
return 'Summer_Sideshow';
|
|
case SEI_Fall:
|
|
`Log("GetSeasonalStateName: Fall");
|
|
return 'Fall';
|
|
case SEI_Winter:
|
|
`Log("GetSeasonalStateName: Winter");
|
|
return 'Winter';
|
|
case SEI_Spring:
|
|
`Log("GetSeasonalStateName: Spring");
|
|
return 'Spring';
|
|
default:
|
|
`Log("GetSeasonalStateName: No Event");
|
|
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);
|
|
SurvivalPerkWeapIndex = byte(Profile.GetProfileInt(KFID_SurvivalStartingWeapIdx));
|
|
SurvivalPerkGrenIndex = byte(Profile.GetProfileInt(KFID_SurvivalStartingGrenIdx));
|
|
|
|
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);
|
|
|
|
// Console?? PC??
|
|
//KFInput.MouseLookUpScale = Profile.GetProfileFloat(KFID_MouseLookUpScale);
|
|
//KFInput.MouseLookRightScale = Profile.GetProfileFloat(KFID_MouseLookRightScale);
|
|
KFInput.bViewSmoothingEnabled = Profile.GetProfileBool(KFID_ViewSmoothingEnabled);
|
|
KFInput.bViewAccelerationEnabled = Profile.GetProfileBool(KFID_ViewAccelerationEnabled);
|
|
|
|
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
|
|
********************************************************************************************* */
|
|
|
|
public function bool CanUseDosh()
|
|
{
|
|
/** If this is run in Server or Standalone, GameInfo exists so can access to the OutbreakEvent */
|
|
if (Role == Role_Authority)
|
|
{
|
|
return KFGameInfo(WorldInfo.Game).OutbreakEvent == none
|
|
|| !KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bDisableAddDosh;
|
|
}
|
|
/** But in client, GameInfo doesn't exist, so needs to be checked in a different way. */
|
|
else
|
|
{
|
|
/**
|
|
In client there's a kfgame replication info that contains if the mode is a weekly, and the index.
|
|
This way would also work in server, but will need to be in code rather than using the weekly variables.
|
|
*/
|
|
/** Another option is to use instead a variable replicated just with that value */
|
|
return KFGameReplicationInfo(WorldInfo.GRI) == none
|
|
|| !KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode
|
|
|| KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex != 16;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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 Gun Game
|
|
********************************************************************************************* */
|
|
|
|
public function bool CanUseGunGame()
|
|
{
|
|
/** If this is run in Server or Standalone, GameInfo exists so can access to the OutbreakEvent */
|
|
if (Role == Role_Authority)
|
|
{
|
|
return KFGameInfo(WorldInfo.Game).OutbreakEvent != none
|
|
&& KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bGunGameMode;
|
|
}
|
|
/** But in client, GameInfo doesn't exist, so needs to be checked in a different way. */
|
|
else
|
|
{
|
|
/**
|
|
In client there's a kfgame replication info that contains if the mode is a weekly, and the index.
|
|
This way would also work in server, but will need to be in code rather than using the weekly variables.
|
|
*/
|
|
/** Another option is to use instead a variable replicated just with that value */
|
|
return KFGameReplicationInfo(WorldInfo.GRI) != none
|
|
&& KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode
|
|
&& KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 16;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function bool CanUseVIP()
|
|
{
|
|
/** If this is run in Server or Standalone, GameInfo exists so can access to the OutbreakEvent */
|
|
if (Role == Role_Authority)
|
|
{
|
|
return KFGameInfo(WorldInfo.Game).OutbreakEvent != none
|
|
&& KFGameInfo(WorldInfo.Game).OutbreakEvent.ActiveEvent.bVIPGameMode;
|
|
}
|
|
/** But in client, GameInfo doesn't exist, so needs to be checked in a different way. */
|
|
else
|
|
{
|
|
/**
|
|
In client there's a kfgame replication info that contains if the mode is a weekly, and the index.
|
|
This way would also work in server, but will need to be in code rather than using the weekly variables.
|
|
*/
|
|
/** Another option is to use instead a variable replicated just with that value */
|
|
return KFGameReplicationInfo(WorldInfo.GRI) != none
|
|
&& KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode
|
|
&& KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 17;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @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 )
|
|
{
|
|
if (MyGfxManager.TraderMenu != none)
|
|
{
|
|
MyGfxManager.TraderMenu.UpdatePlayerInfo();
|
|
}
|
|
|
|
if (MyGfxManager.PerksMenu != none)
|
|
{
|
|
MyGfxManager.PerksMenu.UpdateContainers(CurrentPerk.Class);
|
|
}
|
|
}
|
|
|
|
InitPerkLoadout();
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @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, optional bool check_9mm_logic = false)
|
|
{
|
|
// 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, check_9mm_logic);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
local KFPawn KFP;
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
foreach WorldInfo.AllPawns(class'KFPawn', KFP)
|
|
{
|
|
if (KFP.bIsTurret && WeaponClass == KFP.WeaponClassForAttachmentTemplate)
|
|
{
|
|
KFP.SetTurretWeaponAttachment(WeaponClass);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* @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(bAllowSeasonalSkins != GetAllowSeasonalSkins())
|
|
{
|
|
UpdateSeasonalState();
|
|
}
|
|
|
|
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 */
|
|
(KFGameReplicationInfo(WorldInfo.GRI) != none &&
|
|
KFGameReplicationInfo(WorldInfo.GRI).bIsWeeklyMode &&
|
|
KFGameReplicationInfo(WorldInfo.GRI).CurrentWeeklyIndex == 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)
|
|
{
|
|
if (StatsWrite != none)
|
|
{
|
|
CurrentValue = StatsWrite.GetSeasonalEventStatValue(StatIdx);
|
|
MaxValue = StatsWrite.GetSeasonalEventStatMaxValue(StatIdx);
|
|
}
|
|
else
|
|
{
|
|
`warn( "GetSeasonalEventStatInfo() No StatsWrite ??");
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
InitPerkLoadout();
|
|
|
|
// 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);
|
|
}
|
|
|
|
StatsWrite.SeasonalEventStats_OnStatsInitialized();
|
|
}
|
|
|
|
|
|
`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 AddAfflictionCaused(EAfflictionType Type)
|
|
{
|
|
ClientAddAfflictionCaused(Type);
|
|
}
|
|
native reliable client private function ClientAddAfflictionCaused(EAfflictionType Type);
|
|
|
|
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;
|
|
local int i;
|
|
|
|
// Stop currently playing stingers when the map is being switched
|
|
if( StingerAkComponent != none )
|
|
{
|
|
StingerAkComponent.StopEvents();
|
|
}
|
|
|
|
// Destroy deployed turrets
|
|
// Destroyed event on Turret calls the KFPlayer to modify the same list, that's why we use a while loop...
|
|
for (i = 0; i < DeployedTurrets.Length; i++)
|
|
{
|
|
DeployedTurrets[i].Destroy();
|
|
}
|
|
DeployedTurrets.Remove(0, DeployedTurrets.Length);
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function ZedKillsSeaDetection()
|
|
{
|
|
local int i;
|
|
local KFPawn_Monster Monster;
|
|
local float LastMonsterZPosition;
|
|
|
|
for (i = KilledZeds.Length - 1; i >= 0; i--)
|
|
{
|
|
Monster = KilledZeds[i];
|
|
|
|
// If respawned...
|
|
if (Monster == None || Monster.Health > 0)
|
|
{
|
|
KilledZeds.Remove(i, 1);
|
|
KilledZedsLastZPosition.Remove(i, 1);
|
|
continue;
|
|
}
|
|
|
|
LastMonsterZPosition = KilledZedsLastZPosition[i];
|
|
|
|
KilledZedsLastZPosition[i] = Monster.Location.Z;
|
|
|
|
// If we are falling...
|
|
if (LastMonsterZPosition > Monster.Location.Z)
|
|
{
|
|
// If we passed the point of the sea trigger detection
|
|
if (Monster.Location.Z < SeaTrigger.Location.Z)
|
|
{
|
|
ClientOnTriggerUsed(class'KFSeaTrigger');
|
|
|
|
KilledZeds.Remove(i, 1);
|
|
KilledZedsLastZPosition.Remove(i, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 );
|
|
}
|
|
}
|
|
|
|
reliable client function NotifyKilledForTracking(KFPawn_Monster KilledMonster)
|
|
{
|
|
if (KilledMonster != None && KilledZeds.Find(KilledMonster) == INDEX_NONE)
|
|
{
|
|
KilledZeds.AddItem(KilledMonster);
|
|
KilledZedsLastZPosition.AddItem(KilledMonster.Location.Z);
|
|
}
|
|
}
|
|
|
|
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 );
|
|
|
|
// If we need to track killed zeds, make the function to be called on client...
|
|
if (SeaTrigger != none)
|
|
{
|
|
NotifyKilledForTracking(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 || KFGRI.bForceSkipTraderUI) && 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) );
|
|
}
|
|
|
|
function AdjustDamage(out int InDamage, Controller InstigatedBy, class<DamageType> DamageType, Actor DamageCauser, Actor DamageReceiver)
|
|
{
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
reliable client function ForceMonsterHeadExplode(KFPawn_Monster Victim)
|
|
{
|
|
if (Victim != none)
|
|
{
|
|
Victim.bIsHeadless=true;
|
|
Victim.PlayHeadAsplode();
|
|
}
|
|
}
|
|
|
|
simulated function InitPerkLoadout()
|
|
{
|
|
if (CurrentPerk.IsA('KFPerk_Survivalist'))
|
|
{
|
|
CurrentPerk.SetWeaponSelectedIndex(SurvivalPerkWeapIndex);
|
|
CurrentPerk.SetGrenadeSelectedIndex(SurvivalPerkGrenIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* HV Storm Cannon RPCs
|
|
*/
|
|
|
|
client reliable function AddStormCannonVFX(KFPawn_Monster Target, int ID)
|
|
{
|
|
local int i, DataIndex;
|
|
local KFStormCannonBeamData BeamData;
|
|
local ParticleSystemComponent PawnParticleSystem, BeamParticleSystem;
|
|
local KFPawn_Monster Origin;
|
|
|
|
if (Target == none)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DataIndex = INDEX_NONE;
|
|
BeamParticleSystem = none;
|
|
|
|
for (i = 0; i < StormCannonBeamData.Length; ++i)
|
|
{
|
|
if (StormCannonBeamData[i].ID == ID)
|
|
{
|
|
DataIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DataIndex == INDEX_NONE)
|
|
{
|
|
BeamData.SourcePawn = Target;
|
|
BeamData.ID = ID;
|
|
|
|
// Simulated EMP affliction Effects
|
|
Target.PlaySoundBase(class'KFAffliction_EMP'.default.OnEMPSound, true, true, true);
|
|
BeamData.ParticleSystemPawn = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(ParticleSystem'FX_Gameplay_EMIT.FX_Char_Emp_clot'
|
|
, Target.Mesh
|
|
, class'KFAffliction_EMP'.default.EffectSocketName
|
|
, false);
|
|
|
|
StormCannonBeamData.AddItem(BeamData);
|
|
}
|
|
else if (StormCannonBeamData[DataIndex].SourcePawn != none && StormCannonBeamData[DataIndex].SourcePawn != Target)
|
|
{
|
|
Origin = StormCannonBeamData[DataIndex].SourcePawn;
|
|
StormCannonBeamData[DataIndex].SourcePawn = Target;
|
|
|
|
// Deactivate old VFX on SourcePawn
|
|
if (StormCannonBeamData[DataIndex].ParticleSystemPawn != none)
|
|
{
|
|
Origin.PlaySoundBase(class'KFAffliction_EMP'.default.OnEMPEndSound, true, true);
|
|
|
|
PawnParticleSystem = StormCannonBeamData[DataIndex].ParticleSystemPawn; // Need to do this as UE doesn't allow passing an index into an array as out parameter
|
|
Origin.DetachEmitter(PawnParticleSystem);
|
|
}
|
|
|
|
// Activate new VFX on Target Pawn
|
|
Target.PlaySoundBase(class'KFAffliction_EMP'.default.OnEMPSound, true, true, true);
|
|
StormCannonBeamData[DataIndex].ParticleSystemPawn = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(ParticleSystem'FX_Gameplay_EMIT.FX_Char_Emp_clot'
|
|
, Target.Mesh
|
|
, class'KFAffliction_EMP'.default.EffectSocketName
|
|
, false);
|
|
|
|
if (StormCannonBeamData[DataIndex].ParticleSystemBeam == none)
|
|
{
|
|
StormCannonBeamData[DataIndex].ParticleSystemBeam = WorldInfo.MyEmitterPool.SpawnEmitter(ParticleSystem'WEP_HVStormCannon_EMIT.FX_HVStormCannon_Beam'
|
|
, Origin.Mesh.GetBoneLocation('head'));
|
|
}
|
|
|
|
// Reuse Particle System
|
|
BeamParticleSystem = StormCannonBeamData[DataIndex].ParticleSystemBeam;
|
|
}
|
|
|
|
if (BeamParticleSystem == none || Origin == none)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//`Log("SpawnBeamStormCannon with ID: " $ID $" from : " $Origin $" To: " $self);
|
|
|
|
BeamParticleSystem.SetBeamSourcePoint(0, Origin.Mesh.GetBoneLocation('head'), 0);
|
|
BeamParticleSystem.SetBeamTargetPoint(0, Target.Mesh.GetBoneLocation('head'), 0);
|
|
BeamParticleSystem.SetAbsolute(false, false, false);
|
|
}
|
|
|
|
client reliable function RemoveStormCannonVFX(int ID)
|
|
{
|
|
local int i;
|
|
local ParticleSystemComponent PawnParticleSystem;
|
|
|
|
for (i = 0; i < StormCannonBeamData.Length; ++i)
|
|
{
|
|
if (StormCannonBeamData[i].ID == ID)
|
|
{
|
|
if (StormCannonBeamData[i].ParticleSystemPawn != none)
|
|
{
|
|
PawnParticleSystem = StormCannonBeamData[i].ParticleSystemPawn; // Need to do this as UE doesn't allow passing an index into an array as out parameter
|
|
StormCannonBeamData[i].SourcePawn.DetachEmitter(PawnParticleSystem);
|
|
|
|
StormCannonBeamData[i].ParticleSystemPawn.SetActive(false);
|
|
StormCannonBeamData[i].ParticleSystemPawn = none;
|
|
}
|
|
|
|
if (StormCannonBeamData[i].ParticleSystemBeam != none)
|
|
{
|
|
StormCannonBeamData[i].ParticleSystemBeam.DeactivateSystem();
|
|
StormCannonBeamData[i].ParticleSystemBeam.KillParticlesForced();
|
|
StormCannonBeamData[i].ParticleSystemBeam = none;
|
|
}
|
|
|
|
StormCannonBeamData.Remove(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function SetShotgunJump(bool bJumping)
|
|
{
|
|
bShotgunJumping = bJumping;
|
|
}
|
|
|
|
simulated function ResetShotgunJump()
|
|
{
|
|
SetTimer(0.1, false, nameof(ClearShotgunJumpFlag));
|
|
}
|
|
|
|
simulated function ClearShotgunJumpFlag()
|
|
{
|
|
bShotgunJumping = false;
|
|
}
|
|
|
|
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
|
|
|
|
StormCannonIDCounter = 0
|
|
bShotgunJumping = false
|
|
bAllowSeasonalSkins = false
|
|
}
|