565 lines
14 KiB
Ucode
565 lines
14 KiB
Ucode
//=============================================================================
|
|
// KFMapObjective_RepairActors
|
|
//=============================================================================
|
|
// Objective type for the repair panels. Player must weld n number of actors in
|
|
// order to repair them. After one actor is repaired, another will break.
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2018 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
class KFMapObjective_RepairActors extends KFMapObjective_ActorBase;
|
|
|
|
/** How many actors needs to be repaired for the objective to be completed, per number of players */
|
|
var() const int ActivationsRequiredForPlayerCount[6];
|
|
|
|
/** A delay from the the start of an objective, so the player can't automatically repair the first actor */
|
|
var() float ActivationDelay;
|
|
|
|
/** A sound to play when each repair actor is activated */
|
|
var() array<AkEvent> ActorActivationSoundEvents;
|
|
|
|
/** A sound to play when each repair actor is repaired */
|
|
var() array<AkEvent> ActorRepairedSoundEvents;
|
|
|
|
/** How many actors needs to be repaired for the objective to be completed. */
|
|
var int ActivationsRequired;
|
|
|
|
/** How many actors have been repaired. */
|
|
var repnotify int ActorsRepaired;
|
|
|
|
/** List of all the actors that must be repaired by the user. */
|
|
var() array<KFRepairableActor> RepairableActors;
|
|
|
|
/** List of Repairable Actors we haven't repaired yet. This will ensure we cycle through all actors before repeating. */
|
|
var array<KFRepairableActor> UnusedRepairableActors;
|
|
|
|
/** Reference to last actor repaired so we don't fix the same actor one after the other. */
|
|
var KFRepairableActor LastRepairedActor;
|
|
|
|
/** Current Actor that needs to be repaired. */
|
|
var repnotify KFRepairableActor CurrentActorToRepair;
|
|
|
|
/** How long until the next repairable actor can be activated. */
|
|
var() float TimeUntilNextActivation;
|
|
|
|
/** A sound to play when the objective is fully complete */
|
|
var() AkEvent SuccessSoundEvent100pct;
|
|
|
|
/** A sound to play when the objective is mostly complete */
|
|
var() AkEvent SuccessSoundEvent85pct;
|
|
|
|
/** A sound to play when the objective is adequately complete */
|
|
var() AkEvent SuccessSoundEvent50pct;
|
|
|
|
/** A sound to play when the objective is barely complete */
|
|
var() AkEvent SuccessSoundEvent25pct;
|
|
|
|
/** Win thresholds - Named to match the VO tracks*/
|
|
var float JustWinThreshold;
|
|
var float StandardWinThreshold;
|
|
var float GoodWinThreshold;
|
|
|
|
/** Whether the sequence of repairables should be randomized */
|
|
var() bool bRandomSequence;
|
|
|
|
replication
|
|
{
|
|
if(bNetDirty)
|
|
CurrentActorToRepair, ActorsRepaired, ActivationsRequired;
|
|
}
|
|
|
|
simulated event ReplicatedEvent(name VarName)
|
|
{
|
|
if (VarName == nameof(CurrentActorToRepair))
|
|
{
|
|
UpdateTrailActor();
|
|
}
|
|
else if (VarName == nameof(ActorsRepaired))
|
|
{
|
|
if (ActorsRepaired != 0)
|
|
{
|
|
TriggerObjectiveProgressEvent(, float(ActorsRepaired)/float(ActivationsRequired));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
super.ReplicatedEvent(VarName);
|
|
}
|
|
}
|
|
|
|
simulated function ActivateObjective()
|
|
{
|
|
local int PlayerCount;
|
|
local KFRepairableActor CurrActor;
|
|
|
|
super.ActivateObjective();
|
|
|
|
foreach RepairableActors(CurrActor)
|
|
{
|
|
CurrActor.OnRepairCompelete = OnActorRepaired;
|
|
}
|
|
|
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
|
{
|
|
if (bUseTrailToObjective)
|
|
{
|
|
TrailActor = class'WorldInfo'.static.GetWorldInfo().Spawn(class'KFReplicatedShowPathActor', none);
|
|
TrailActor.SetEmitterTemplate(ParticleSystem'FX_Gameplay_EMIT.FX_Objective_White_Trail');
|
|
}
|
|
}
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
PlayerCount = Clamp(KFGameInfo(WorldInfo.Game).GetLivingPlayerCount(), 1, 6) - 1;
|
|
ActivationsRequired = ActivationsRequiredForPlayerCount[PlayerCount];
|
|
|
|
ActorsRepaired = 0;
|
|
bIsActive = true;
|
|
|
|
if (ActivationDelay > 0.f)
|
|
{
|
|
SetTimer(ActivationDelay, false, 'ActivateNextRepairableActor');
|
|
}
|
|
else
|
|
{
|
|
ActivateNextRepairableActor();
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function DeactivateObjective()
|
|
{
|
|
local KFPlayerController KFPC;
|
|
local KFPawn_Human KFPH;
|
|
local KFRepairableActor CurrActor;
|
|
|
|
super.DeactivateObjective();
|
|
|
|
if(Role == ROLE_Authority)
|
|
{
|
|
bIsActive = false;
|
|
|
|
if (!HasFailedObjective())
|
|
{
|
|
foreach WorldInfo.AllPawns(class'KFPawn_Human', KFPH)
|
|
{
|
|
GrantReward(KFPlayerReplicationInfo(KFPH.PlayerReplicationInfo), KFPlayerController(KFPH.Controller));
|
|
|
|
if (KFPlayerController(KFPH.Controller) != none)
|
|
{
|
|
if (GetTotalProgress() >= 1.0f)
|
|
{
|
|
// @todo: hook up seasonal event here if/when desired
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
`log("objective failed");
|
|
}
|
|
|
|
PlayDeactivationDialog();
|
|
|
|
ClearTimer(nameof(ActivateNextRepairableActor));
|
|
}
|
|
|
|
foreach RepairableActors(CurrActor)
|
|
{
|
|
CurrActor.Reset();
|
|
}
|
|
|
|
if(WorldInfo.NetMode != NM_DedicatedServer)
|
|
{
|
|
if (TrailActor != none)
|
|
{
|
|
TrailActor.Destroy();
|
|
TrailActor = none;
|
|
}
|
|
}
|
|
|
|
KFPC = KFPlayerController(GetALocalPlayerController());
|
|
if (KFPC != none && KFPC.MyGFxHUD != none)
|
|
{
|
|
KFPC.MyGFxHUD.WaveInfoWidget.ObjectiveContainer.SetFailState(HasFailedObjective());
|
|
}
|
|
}
|
|
|
|
function PlayDeactivationDialog()
|
|
{
|
|
if (GetTotalProgress() <= 0.0f)
|
|
{
|
|
PlaySoundBase(FailureSoundEvent, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
BroadcastLocalizedMessage(class'KFLocalMessage_Priority', GMT_ObjectiveLost);
|
|
}
|
|
else
|
|
{
|
|
if (GetTotalProgress() <= JustWinThreshold)
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent25pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else if (GetTotalProgress() <= StandardWinThreshold)
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent50pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else if (GetTotalProgress() <= GoodWinThreshold)
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent85pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
else
|
|
{
|
|
PlaySoundBase(SuccessSoundEvent100pct, false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
}
|
|
}
|
|
|
|
function ActivateNextRepairableActor()
|
|
{
|
|
if(Role == ROLE_Authority)
|
|
{
|
|
LastRepairedActor = CurrentActorToRepair;
|
|
CurrentActorToRepair = ChooseNextActorToRepair();
|
|
if (CurrentActorToRepair != none)
|
|
{
|
|
CurrentActorToRepair.PlayDestroyed();
|
|
}
|
|
|
|
if (ActorsRepaired < ActorActivationSoundEvents.Length)
|
|
{
|
|
PlaySoundBase(ActorActivationSoundEvents[ActorsRepaired], false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
}
|
|
|
|
UpdateTrailActor();
|
|
}
|
|
|
|
simulated function UpdateTrailActor()
|
|
{
|
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
|
{
|
|
if (TrailActor != none)
|
|
{
|
|
TrailActor.SetPathTarget(CurrentActorToRepair.RepairTrigger);
|
|
}
|
|
}
|
|
}
|
|
|
|
function KFRepairableActor ChooseNextActorToRepair()
|
|
{
|
|
local int ChosenActorIndex;
|
|
local KFRepairableActor ChosenActor;
|
|
local array<KFRepairableActor> ValidActors;
|
|
|
|
if (RepairableActors.length == 0)
|
|
{
|
|
return none;
|
|
}
|
|
|
|
if (UnusedRepairableActors.length == 0)
|
|
{
|
|
UnusedRepairableActors = RepairableActors;
|
|
}
|
|
|
|
ValidActors = UnusedRepairableActors;
|
|
if(ValidActors.length > 1)
|
|
{
|
|
ValidActors.RemoveItem(LastRepairedActor);
|
|
}
|
|
|
|
if (bRandomSequence)
|
|
{
|
|
ChosenActorIndex = RandRange(0, ValidActors.length - 1);
|
|
}
|
|
else
|
|
{
|
|
// if not random choose the next actor in the sequence
|
|
ChosenActorIndex = 0;
|
|
}
|
|
|
|
ChosenActor = ValidActors[ChosenActorIndex];
|
|
UnusedRepairableActors.Remove(ChosenActorIndex, 1);
|
|
|
|
return ChosenActor;
|
|
}
|
|
|
|
function OnActorRepaired(KFRepairableActor RepairedActor)
|
|
{
|
|
local KFGameReplicationInfo KFGRI;
|
|
|
|
if (!bIsActive)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
if (ActorsRepaired < ActorRepairedSoundEvents.Length)
|
|
{
|
|
PlaySoundBase(ActorRepairedSoundEvents[ActorsRepaired], false, WorldInfo.NetMode == NM_DedicatedServer);
|
|
}
|
|
}
|
|
|
|
ActorsRepaired++;
|
|
TriggerObjectiveProgressEvent(, float(ActorsRepaired) / float(ActivationsRequired));
|
|
|
|
if (GetTotalProgress() >= 1.f)
|
|
{
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
if (KFGRI != none)
|
|
{
|
|
KFGRI.DeactivateObjective();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TimeUntilNextActivation > 0.f)
|
|
{
|
|
SetTimer(TimeUntilNextActivation, false, nameof(ActivateNextRepairableActor));
|
|
CurrentActorToRepair = none;
|
|
UpdateTrailActor();
|
|
}
|
|
else
|
|
{
|
|
ActivateNextRepairableActor();
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function bool IsActive()
|
|
{
|
|
return bIsActive;
|
|
}
|
|
|
|
simulated function bool IsBonus()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
function bool CanActivateObjective()
|
|
{
|
|
return !IsCurrentGameModeBlacklisted();
|
|
}
|
|
|
|
function bool IsCurrentGameModeBlacklisted()
|
|
{
|
|
local class<KFGameInfo> CurrGameClass;
|
|
|
|
foreach GameModeBlacklist(CurrGameClass)
|
|
{
|
|
if (CurrGameClass == WorldInfo.GRI.GameClass)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
simulated function bool UsesProgress()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
simulated function bool ShouldShowObjectiveHUD()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
simulated function float GetProgress()
|
|
{
|
|
if (!HasFailedObjective())
|
|
{
|
|
return float(ActorsRepaired) / float(ActivationsRequired);
|
|
}
|
|
|
|
return 0.f;
|
|
}
|
|
|
|
simulated function bool IsComplete()
|
|
{
|
|
return GetProgress() >= 1.f;
|
|
}
|
|
|
|
simulated function float GetTotalProgress()
|
|
{
|
|
if (ActivationsRequired == 0)
|
|
{
|
|
return 0.f;
|
|
}
|
|
|
|
return float(ActorsRepaired) / float(ActivationsRequired);
|
|
}
|
|
|
|
simulated function float GetActivationPctChance()
|
|
{
|
|
return 1.f;
|
|
}
|
|
|
|
simulated function string GetLocalizedRequirements()
|
|
{
|
|
return Localize("Objectives", default.RequirementsLocKey, "KFGame") @ ActivationsRequired;
|
|
}
|
|
|
|
simulated function bool ShouldDrawIcon()
|
|
{
|
|
return CurrentActorToRepair != none;
|
|
}
|
|
|
|
simulated function Vector GetIconLocation()
|
|
{
|
|
if (CurrentActorToRepair != none)
|
|
{
|
|
return CurrentActorToRepair.Location + CurrentActorToRepair.IconLocationOffset;
|
|
}
|
|
|
|
return Location;
|
|
}
|
|
|
|
simulated function int GetVoshReward()
|
|
{
|
|
local int MaxDosh;
|
|
|
|
MaxDosh = GetMaxVoshReward();
|
|
if (MaxDosh == 0)
|
|
{
|
|
return MaxDosh;
|
|
}
|
|
|
|
return int(MaxDosh * GetTotalProgress());
|
|
}
|
|
|
|
simulated function int GetXPReward()
|
|
{
|
|
local int MaxXP;
|
|
|
|
MaxXP = GetMaxXPReward();
|
|
if (MaxXP == 0)
|
|
{
|
|
return MaxXP;
|
|
}
|
|
|
|
return int(MaxXP * GetTotalProgress());
|
|
}
|
|
|
|
simulated function string GetProgressText()
|
|
{
|
|
if (!bIsActive)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
return ActorsRepaired $ "/" $ ActivationsRequired;
|
|
}
|
|
|
|
simulated function bool GetIsMissionCritical()
|
|
{
|
|
return bIsMissionCriticalObjective;
|
|
}
|
|
|
|
// this is currently a copy of DrawObjectiveHUD,
|
|
// except it uses the individual repairable weld completion instead of the overall objective completion
|
|
simulated function DrawHUD(KFHUDBase hud, Canvas drawCanvas)
|
|
{
|
|
local float Percentage;
|
|
local float BarHeight, BarLength;
|
|
local vector ScreenPos, TargetLocation;
|
|
local float ResModifier;
|
|
local float ThisDot;
|
|
local KFGameReplicationInfo KFGRI;
|
|
local vector ViewLocation, ViewVector;
|
|
local rotator ViewRotation;
|
|
local KFPlayerController KFPC;
|
|
|
|
ResModifier = WorldInfo.static.GetResolutionBasedHUDScale() * hud.FriendlyHudScale;
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
KFPC = KFPlayerController(GetALocalPlayerController());
|
|
|
|
if (!ShouldDrawIcon() || KFGRI.bHidePawnIcons)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (KFPC != none)
|
|
{
|
|
KFPC.GetPlayerViewPoint(ViewLocation, ViewRotation);
|
|
}
|
|
|
|
ViewVector = vector(ViewRotation);
|
|
ThisDot = Normal(GetIconLocation() - ViewLocation) dot ViewVector;
|
|
|
|
if (ThisDot <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
BarLength = FMin(hud.PlayerStatusBarLengthMax * (drawCanvas.ClipX / 1024.f), hud.PlayerStatusBarLengthMax) * ResModifier;
|
|
BarHeight = FMin(8.f * (drawCanvas.ClipX / 1024.f), 8.f) * ResModifier;
|
|
TargetLocation = GetIconLocation();
|
|
ScreenPos = drawCanvas.Project(TargetLocation);
|
|
|
|
// Make sure that the entire health bar is on screen
|
|
ScreenPos.X = FClamp(ScreenPos.X, BarLength * 0.5f + hud.PlayerStatusIconSize, drawCanvas.ClipX - BarLength * 0.5f);
|
|
ScreenPos.Y = FClamp(ScreenPos.Y, hud.PlayerStatusIconSize * 0.5f, drawCanvas.ClipY - hud.PlayerStatusIconSize * 0.5f);
|
|
|
|
//Draw progress bar
|
|
Percentage = FMin(FClamp(float(CurrentActorToRepair.WeldIntegrity) / float(CurrentActorToRepair.MaxWeldIntegrity), 0.f, 1.f), 1);
|
|
hud.DrawKFBar(Percentage, BarLength, BarHeight, ScreenPos.X - (BarLength * 0.5f), ScreenPos.Y, hud.NonPlayerHealth);
|
|
|
|
//draw objective icon
|
|
if (GetIcon() != none)
|
|
{
|
|
drawCanvas.SetDrawColorStruct(hud.PlayerBarShadowColor);
|
|
drawCanvas.SetPos((ScreenPos.X - (BarLength * 0.75)) + 1, (ScreenPos.Y - BarHeight * 2.0) + 1);
|
|
drawCanvas.DrawTile(GetIcon(), hud.PlayerStatusIconSize * ResModifier, hud.PlayerStatusIconSize * ResModifier, 0, 0, 256, 256);
|
|
|
|
drawCanvas.SetDrawColorStruct(GetIconColor());
|
|
drawCanvas.SetPos(ScreenPos.X - (BarLength * 0.75), ScreenPos.Y - BarHeight * 2.0);
|
|
drawCanvas.DrawTile(GetIcon(), hud.PlayerStatusIconSize * ResModifier, hud.PlayerStatusIconSize * ResModifier, 0, 0, 256, 256);
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
LocalizationKey="RepairObjective"
|
|
DescriptionLocKey="UseWelderToRepair"
|
|
NameShortLocKey="RepairObjective"
|
|
DescriptionShortLocKey="UseWelderToRepairShort"
|
|
RequirementsLocKey="RepairObjectiveRequired"
|
|
bAlwaysRelevant=true
|
|
RemoteRole=ROLE_SimulatedProxy
|
|
|
|
TimeUntilNextActivation=5.f
|
|
|
|
SupportedEvents.Add(class'KFSeqEvent_ObjectiveProgress')
|
|
|
|
GameModeBlacklist.Add(class'KFGameInfo_Endless')
|
|
GameModeBlacklist.Add(class'KFGameInfo_WeeklySurvival')
|
|
|
|
Begin Object Class=SpriteComponent Name=Sprite
|
|
Sprite=Texture2D'EditorResources.S_Actor'
|
|
HiddenGame=True
|
|
AlwaysLoadOnClient=False
|
|
AlwaysLoadOnServer=False
|
|
End Object
|
|
Components.Add(Sprite)
|
|
|
|
ActivationsRequired=6
|
|
bUseTrailToObjective=true
|
|
|
|
PerPlayerSpawnRateMod=(1.f, 1.f, 1.f, 1.f, 1.f, 1.f)
|
|
|
|
ObjectiveIcon=Texture2D'Objectives_UI.UI_Objectives_ObjectiveMode'
|
|
|
|
//These are basically range caps. For example:
|
|
// Just win would be 0% - 25%
|
|
// Standard win would be 25% - 50%
|
|
// Good win would be 50% - 85%
|
|
// Perfect win then assumes everything above good win
|
|
JustWinThreshold=0.25
|
|
StandardWinThreshold=0.5
|
|
GoodWinThreshold=0.85
|
|
LocalizationPackageName="KFGame"
|
|
|
|
bRandomSequence=true
|
|
}
|