576 lines
15 KiB
Ucode
576 lines
15 KiB
Ucode
//=============================================================================
|
|
// KFMapObjective_HoldVolume
|
|
//=============================================================================
|
|
// Objective type for the dosh hold objective. While this is active, players
|
|
// have to keep zeds out of an area in order to earn bonus dosh. If no
|
|
// players or any zed are in the volume, the wave end bonus ticks down.
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2017 Tripwire Interactive LLC
|
|
// - Dan Weiss
|
|
//=============================================================================
|
|
class KFMapObjective_DoshHold extends KFMapObjective_AreaDefense;
|
|
|
|
`include(KFGame/KFGameDialog.uci)
|
|
|
|
/** Timer before penalty check starts */
|
|
var() const float PenaltyStartupTimer;
|
|
|
|
/** Timer length for checking the dosh penalty rule set */
|
|
var() const float DoshPenaltyCheckTimer;
|
|
|
|
/** Penalties for various states of the volume*/
|
|
var() const int NoHumansPenalty;
|
|
var() const int ZedsPenalty;
|
|
|
|
/** Whether the trail shows up during the previous trader time */
|
|
var() bool bUseEarlyTrail;
|
|
|
|
/* Percentage of the wave's zeds that needs to be killed to obtain the max reward */
|
|
var() const float PctOfWaveZedsKilledForMaxReward;
|
|
|
|
var transient float RewardPerZed;
|
|
|
|
struct WaveLengthPctChances
|
|
{
|
|
var() float PctChances[11];
|
|
|
|
structdefaultproperties
|
|
{
|
|
PctChances[0]=1.f
|
|
PctChances[1]=1.f
|
|
PctChances[2]=1.f
|
|
PctChances[3]=1.f
|
|
PctChances[4]=1.f
|
|
PctChances[5]=1.f
|
|
PctChances[6]=1.f
|
|
PctChances[7]=1.f
|
|
PctChances[8]=1.f
|
|
PctChances[9]=1.f
|
|
PctChances[10]=1.f
|
|
}
|
|
};
|
|
|
|
var() WaveLengthPctChances ActivatePctChances[3];
|
|
|
|
|
|
/** Sound event to play when objective is activated (overrides default trader dialog for this event) */
|
|
var() AkEvent ActivationSoundEventOverride;
|
|
|
|
/** A sound to play when the objective is fully complete (overrides default trader dialog for this event) */
|
|
var() AkEvent SuccessSoundEvent100pctOverride;
|
|
|
|
/** A sound to play when the objective is mostly complete (overrides default trader dialog for this event) */
|
|
var() AkEvent SuccessSoundEvent85pctOverride;
|
|
|
|
/** A sound to play when the objective is adequately complete (overrides default trader dialog for this event) */
|
|
var() AkEvent SuccessSoundEvent50pctOverride;
|
|
|
|
/** A sound to play when the objective is barely complete (overrides default trader dialog for this event) */
|
|
var() AkEvent SuccessSoundEvent25pctOverride;
|
|
|
|
/** A sound to play when the objective is failed (overrides default trader dialog for this event) */
|
|
var() AkEvent FailureSoundEventOverride;
|
|
|
|
|
|
/** Sound event to play when wave is 25% complete */
|
|
var() AkEvent WaveProgressSoundEvent25pct;
|
|
|
|
/** Sound event to play when wave is 50% complete */
|
|
var() AkEvent WaveProgressSoundEvent50pct;
|
|
|
|
/** Sound event to play when wave is 75% complete */
|
|
var() AkEvent WaveProgressSoundEvent75pct;
|
|
|
|
/** Sound event to play when wave is 90% complete */
|
|
var() AkEvent WaveProgressSoundEvent90pct;
|
|
|
|
/** Sound event to play to remind players about the objective */
|
|
var() AkEvent RemindPlayersSoundEvent;
|
|
|
|
/** How often to remind players about the objective if they aren't engaged in completing it */
|
|
var() float RemindPlayersTime;
|
|
|
|
var transient float PrevWaveProgress;
|
|
var transient bool bRemindPlayers;
|
|
|
|
|
|
simulated event ReplicatedEvent(name VarName)
|
|
{
|
|
if (VarName == nameof(bActive))
|
|
{
|
|
if (!bActive)
|
|
{
|
|
DeactivateObjective();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Volume
|
|
//-----------------------------------------------------------------------------
|
|
event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal)
|
|
{
|
|
local int PlayerCount;
|
|
|
|
super.Touch(Other, OtherComp, HitLocation, HitNormal);
|
|
|
|
if (KFPawn_Human(Other) != none && TouchingHumans.Find(Other) == INDEX_NONE)
|
|
{
|
|
PlayerCount = Clamp(KFGameInfo(WorldInfo.Game).GetLivingPlayerCount(), 1, 6) - 1;
|
|
if (TouchingHumans.Length >= PlayerThresholds[PlayerCount] && IsTimerActive('StartPenaltyCheck'))
|
|
{
|
|
StartPenaltyCheck();
|
|
}
|
|
}
|
|
}
|
|
|
|
function NotifyZedKilled(Controller Killer, Pawn KilledPawn, bool bIsBoss)
|
|
{
|
|
local int i;
|
|
local KFGameInfo KFGI;
|
|
local KFGameReplicationInfo KFGRI;
|
|
|
|
if (ROLE == Role_Authority)
|
|
{
|
|
if (bActive)
|
|
{
|
|
KFGI = KFGameInfo(WorldInfo.Game);
|
|
if (KFGI != none)
|
|
{
|
|
for (i = 0; i < TouchingHumans.Length; i++)
|
|
{
|
|
if (Killer == TouchingHumans[i].Controller)
|
|
{
|
|
if (RewardPerZed == 0)
|
|
{
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
|
|
RewardPerZed = GetMaxDoshReward() / (PctOfWaveZedsKilledForMaxReward * KFGRI.WaveTotalAICount);
|
|
}
|
|
CurrentRewardAmount = FMin(CurrentRewardAmount + RewardPerZed, float(GetMaxDoshReward()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GetProgress() >= 1.0f)
|
|
{
|
|
DeactivateObjective();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//// Designers wanted this, but now they don't want it, but they might want it again,
|
|
//// so the code is staying here.
|
|
//simulated function NotifyObjectiveSelected()
|
|
//{
|
|
// if (WorldInfo.NetMode != NM_DedicatedServer)
|
|
// {
|
|
// ActivateBoundarySplines();
|
|
// UpdateMeshArrayState();
|
|
//
|
|
// if (bUseTrailToVolume && bUseEarlyTrail)
|
|
// {
|
|
// if (TrailActor == none)
|
|
// {
|
|
// TrailActor = class'WorldInfo'.static.GetWorldInfo().Spawn(class'KFReplicatedShowPathActor', none);
|
|
// }
|
|
// if (TrailActor != none)
|
|
// {
|
|
// TrailActor.SetEmitterTemplate(ParticleSystem'FX_Gameplay_EMIT.FX_Objective_White_Trail');
|
|
// TrailActor.SetPathTarget(self, self, VCT_NotInVolume);
|
|
// }
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
function CheckBonusState()
|
|
{
|
|
local int i;
|
|
local int PlayerCount;
|
|
|
|
bDangerState = false;
|
|
PlayerCount = Clamp(KFGameInfo(WorldInfo.Game).GetLivingPlayerCount(), 1, 6) - 1;
|
|
if (TouchingHumans.Length < PlayerThresholds[PlayerCount])
|
|
{
|
|
bDangerState = true;
|
|
|
|
if(bRemindPlayers)
|
|
{
|
|
if (RemindPlayersSoundEvent != none)
|
|
{
|
|
PlaySoundBase(RemindPlayersSoundEvent,, WorldInfo.NetMode == NM_DedicatedServer);
|
|
SetTimer(RemindPlayersTime, false, 'Timer_AllowRemindPlayers');
|
|
}
|
|
bRemindPlayers = false;
|
|
}
|
|
}
|
|
else if (TouchingZeds.Length > 0)
|
|
{
|
|
//Remove dead zeds
|
|
for (i = TouchingZeds.Length - 1; i >= 0; --i)
|
|
{
|
|
if (!IsValidZed(TouchingZeds[i]))
|
|
{
|
|
TouchingZeds.Remove(i, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateMeshArrayState();
|
|
}
|
|
|
|
simulated function Timer_AllowRemindPlayers()
|
|
{
|
|
bRemindPlayers = true;
|
|
}
|
|
|
|
function StartPenaltyCheck()
|
|
{
|
|
ClearTimer('StartPenaltyCheck');
|
|
if(bActive)
|
|
{
|
|
SetTimer(DoshPenaltyCheckTimer, true, 'CheckBonusState');
|
|
}
|
|
}
|
|
|
|
function ActivationVO()
|
|
{
|
|
if (ActivationSoundEventOverride != none)
|
|
{
|
|
PlaySoundBase(ActivationSoundEventOverride, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else
|
|
{
|
|
class'KFTraderDialogManager'.static.PlayGlobalDialog(`TRAD_ObjDefendA, WorldInfo, true);
|
|
}
|
|
}
|
|
|
|
simulated function ActivateObjective()
|
|
{
|
|
local int PlayerCount;
|
|
|
|
super.ActivateObjective();
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
// dosh hold reward starts at zero and counts up when a zed is killed
|
|
CurrentRewardAmount = 0;
|
|
|
|
// RewardPerZed will be set upon killing the first zed because this function (ActivateObjective)
|
|
// is called before KFGRI.WaveTotalAICount is set for the current wave
|
|
RewardPerZed = 0;
|
|
|
|
PlayerCount = Clamp(KFGameInfo(WorldInfo.Game).GetLivingPlayerCount(), 1, 6) - 1;
|
|
if (TouchingHumans.Length >= PlayerThresholds[PlayerCount])
|
|
{
|
|
StartPenaltyCheck();
|
|
}
|
|
else
|
|
{
|
|
SetTimer(PenaltyStartupTimer, false, 'StartPenaltyCheck');
|
|
}
|
|
|
|
//Because we're tired of dealing with bugs in the VO system in smart ways, delay by a frame
|
|
// to avoid first tick replication not having a controller.
|
|
SetTimer(0.01f, false, 'ActivationVO');
|
|
|
|
SetTimer(1.f, true, 'Timer_CheckPawnCount');
|
|
SetTimer(1.f, true, 'Timer_CheckWaveProgress');
|
|
PrevWaveProgress = 0;
|
|
bRemindPlayers = true;
|
|
}
|
|
}
|
|
|
|
simulated function Timer_CheckWaveProgress()
|
|
{
|
|
local KFGameReplicationInfo KFGRI;
|
|
local float WaveProgress;
|
|
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.Game.GameReplicationInfo);
|
|
if (KFGRI != none)
|
|
{
|
|
WaveProgress = 1.f - (float(KFGRI.AIRemaining) / float(KFGRI.WaveTotalAICount));
|
|
|
|
if (PrevWaveProgress < 0.25 && WaveProgress >= 0.25)
|
|
{
|
|
PlaySoundBase(WaveProgressSoundEvent25pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else if (PrevWaveProgress < 0.5 && WaveProgress >= 0.5)
|
|
{
|
|
PlaySoundBase(WaveProgressSoundEvent50pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else if (PrevWaveProgress < 0.75 && WaveProgress >= 0.75)
|
|
{
|
|
PlaySoundBase(WaveProgressSoundEvent75pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else if (PrevWaveProgress < 0.9 && WaveProgress >= 0.9)
|
|
{
|
|
PlaySoundBase(WaveProgressSoundEvent90pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
|
|
PrevWaveProgress = WaveProgress;
|
|
}
|
|
}
|
|
|
|
simulated function Timer_CheckPawnCount()
|
|
{
|
|
local int PlayerCount;
|
|
|
|
PlayerCount = Clamp(KFGameInfo(WorldInfo.Game).GetLivingPlayerCount(), 1, 6) - 1;
|
|
bTooFewPlayers = TouchingHumans.Length < PlayerThresholds[PlayerCount];
|
|
bTooManyZeds = TouchingZeds.Length > ZedThresholds[PlayerCount];
|
|
}
|
|
|
|
simulated function DeactivateObjective()
|
|
{
|
|
local KFPawn_Human KFPH;
|
|
local KFPlayerController KFPC;
|
|
local bool bOneHumanAlive;
|
|
|
|
super.DeactivateObjective();
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
ClearTimer('CheckBonusState');
|
|
ClearTimer('StartPenaltyCheck');
|
|
ClearTimer('Timer_AllowRemindPlayers');
|
|
ClearTimer('Timer_CheckWaveProgress');
|
|
ClearTimer('Timer_CheckPawnCount');
|
|
|
|
bOneHumanAlive = false;
|
|
|
|
foreach WorldInfo.AllPawns(class'KFPawn_Human', KFPH)
|
|
{
|
|
if (KFPH.IsAliveAndWell())
|
|
{
|
|
bOneHumanAlive = true;
|
|
}
|
|
}
|
|
|
|
// give the dosh hold reward to a player even if they died
|
|
if (CurrentRewardAmount > 0 && bOneHumanAlive)
|
|
{
|
|
foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
|
|
{
|
|
GrantReward(KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo), KFPC);
|
|
}
|
|
}
|
|
|
|
if (bOneHumanAlive)
|
|
{
|
|
PlayDeactivationDialog();
|
|
}
|
|
}
|
|
|
|
KFPC = KFPlayerController(GetALocalPlayerController());
|
|
if (KFPC != none && KFPC.MyGFxHUD != none)
|
|
{
|
|
KFPC.MyGFxHUD.WaveInfoWidget.ObjectiveContainer.SetFailState(CurrentRewardAmount <= 0);
|
|
}
|
|
}
|
|
|
|
function PlayDeactivationDialog()
|
|
{
|
|
if (CurrentRewardAmount <= 0)
|
|
{
|
|
if (FailureSoundEventOverride != none)
|
|
{
|
|
PlaySoundBase(FailureSoundEventOverride, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else
|
|
{
|
|
class'KFTraderDialogManager'.static.PlayGlobalDialog(`TRAD_ObjDefendAFailed, WorldInfo, true);
|
|
}
|
|
BroadcastLocalizedMessage(class'KFLocalMessage_Priority', GMT_ObjectiveLost);
|
|
}
|
|
else
|
|
{
|
|
if (GetProgress() <= JustWinThreshold)
|
|
{
|
|
if (SuccessSoundEvent25pctOverride != none)
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent25pctOverride, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else
|
|
{
|
|
class'KFTraderDialogManager'.static.PlayGlobalDialog(`TRAD_ObjDefendAWonJust, WorldInfo, true);
|
|
}
|
|
}
|
|
else if (GetProgress() <= StandardWinThreshold)
|
|
{
|
|
if (SuccessSoundEvent50pctOverride != none)
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent50pctOverride, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else
|
|
{
|
|
class'KFTraderDialogManager'.static.PlayGlobalDialog(`TRAD_ObjDefendAWon, WorldInfo, true);
|
|
}
|
|
}
|
|
else if (GetProgress() <= GoodWinThreshold)
|
|
{
|
|
if (SuccessSoundEvent85pctOverride != none)
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent85pctOverride, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else
|
|
{
|
|
class'KFTraderDialogManager'.static.PlayGlobalDialog(`TRAD_ObjDefendAWonGood, WorldInfo, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SuccessSoundEvent100pctOverride != none)
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent100pctOverride, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else
|
|
{
|
|
class'KFTraderDialogManager'.static.PlayGlobalDialog(`TRAD_ObjDefendAWonPerf, WorldInfo, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function int GetDoshReward()
|
|
{
|
|
return CurrentRewardAmount;
|
|
}
|
|
|
|
simulated function float GetProgress()
|
|
{
|
|
local int MaxDoshReward;
|
|
|
|
MaxDoshReward = GetMaxDoshReward();
|
|
if (MaxDoshReward == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return CurrentRewardAmount / float(MaxDoshReward);
|
|
}
|
|
|
|
simulated function bool IsComplete()
|
|
{
|
|
return GetProgress() > 0.f && !bActive;
|
|
}
|
|
|
|
simulated function float GetActivationPctChance()
|
|
{
|
|
local KFGameReplicationInfo KFGRI;
|
|
local int ArrayEnd;
|
|
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
if (KFGRI != none)
|
|
{
|
|
// Since we're using a static array for rewards, we need to know the true end of the array.
|
|
ArrayEnd = Clamp(KFGRI.WaveMax - 2, 0, ArrayCount(ActivatePctChances[KFGRI.GameLength].PctChances) - 1);
|
|
return ActivatePctChances[KFGRI.GameLength].PctChances[Clamp(KFGRI.WaveNum - 1, 0, ArrayEnd)];
|
|
}
|
|
|
|
return 1.f;
|
|
}
|
|
|
|
simulated function string GetLocalizedRequirements()
|
|
{
|
|
local int PlayerCount;
|
|
|
|
PlayerCount = Clamp(KFGameReplicationInfo(WorldInfo.GRI).GetNumPlayers(), 1, 6) - 1;
|
|
|
|
return Localize("Objectives", default.RequirementsLocKey, "KFGame") @ PlayerThresholds[PlayerCount];
|
|
}
|
|
|
|
simulated function GetLocalizedStatus(out string statusMessage, out int bWarning, out int bNotification);
|
|
|
|
simulated function string GetProgressText()
|
|
{
|
|
return string(GetMaxDoshReward());
|
|
}
|
|
|
|
simulated function bool GetProgressTextIsDosh()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
simulated function bool ShouldShowObjectiveHUD()
|
|
{
|
|
return !IsComplete();
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
DescriptionLocKey="DescriptionDoshHold"
|
|
DescriptionShortLocKey="DescriptionDoshHoldShort"
|
|
RequirementsLocKey = "RequiredDoshHold"
|
|
LocalizationKey="DoshHold"
|
|
NameShortLocKey="DoshHold"
|
|
|
|
PenaltyStartupTimer=20.f
|
|
DoshPenaltyCheckTimer=0.25f
|
|
NoHumansPenalty=5
|
|
ZedsPenalty=1
|
|
|
|
// Short Match
|
|
ActivatePctChances[0]={(
|
|
PctChances[0]=.0,
|
|
PctChances[1]=.35,
|
|
PctChances[2]=.35,
|
|
PctChances[3]=.35
|
|
)}
|
|
|
|
// Normal Match
|
|
ActivatePctChances[1]={(
|
|
PctChances[0]=.0,
|
|
PctChances[1]=.35,
|
|
PctChances[2]=.35,
|
|
PctChances[3]=.35,
|
|
PctChances[4]=.35,
|
|
PctChances[5]=.35,
|
|
PctChances[6]=.35
|
|
)}
|
|
|
|
// Long Match
|
|
ActivatePctChances[2]={(
|
|
PctChances[0]=.0,
|
|
PctChances[1]=.35,
|
|
PctChances[2]=.35,
|
|
PctChances[3]=.35,
|
|
PctChances[4]=.35,
|
|
PctChances[5]=.35,
|
|
PctChances[6]=.35, //6
|
|
PctChances[7]=.35, //7
|
|
PctChances[8]=.35, //8
|
|
PctChances[9]=.35, //9
|
|
PctChances[10]=.35 //1.0
|
|
)}
|
|
|
|
PlayerThresholds[0]=1
|
|
PlayerThresholds[1]=1
|
|
PlayerThresholds[2]=1
|
|
PlayerThresholds[3]=1
|
|
PlayerThresholds[4]=1
|
|
PlayerThresholds[5]=1
|
|
|
|
ZedThresholds[0]=0
|
|
ZedThresholds[1]=0
|
|
ZedThresholds[2]=0
|
|
ZedThresholds[3]=0
|
|
ZedThresholds[4]=0
|
|
ZedThresholds[5]=0
|
|
|
|
ObjectiveIcon=Texture2D'Objectives_UI.UI_Objectives_ObjectiveMode'
|
|
|
|
RemindPlayersTime=30.f
|
|
bUseEarlyTrail=false
|
|
|
|
DoshRewards = (150,150,200,250,300,350,400,400,400,400,400)
|
|
XPRewards = (50,50,100,150,200,250,300,350,350,350,350)
|
|
|
|
XPDifficultyScalars=(1.0f,1.1f,1.2f,1.3f)
|
|
DoshDifficultyScalars=(1.0f,1.1f,1.2f,1.3f)
|
|
|
|
PctOfWaveZedsKilledForMaxReward=0.7f
|
|
}
|