1
0
KF2-Dev-Scripts/KFGameContent/Classes/KFMapObjective_CollectActors.uc
2020-12-13 18:01:13 +03:00

1147 lines
33 KiB
Ucode

//=============================================================================
// KFMapObjective_CollectActors
//=============================================================================
// Objective class for keeping track of collectibles found by players
//=============================================================================
// Killing Floor 2
// Copyright (C) 2018 Tripwire Interactive LLC
//=============================================================================
class KFMapObjective_CollectActors extends KFMapObjective_VolumeBase;
/** A delay from the the start of an objective, so the player can't automatically activate the first actor */
var() float ActivationDelay;
/** How long until the next actor can be activated. */
var() float TimeUntilNextActivation;
/** A sound to play when the objective is fully complete */
var() AkEvent SuccessSoundEvent;
/** How many actors needs to be activated for the objective to be completed. */
var() array<int> NumRequired;
/** How many actors have been activated. */
var repnotify int NumAccomplished;
/** An actor that visually represents the delivery volume*/
var() SkeletalMeshActor DeliveryMeshActor;
/** An animation to play on the DeliveryMeshActor when this objective activates*/
var() name DeliveryOpenAnimName;
/** An animation to play on the DeliveryMeshActor when this objective deactivates*/
var() name DeliveryCloseAnimName;
/** How long to delay the playing of the close anim after the objective is complete (to give Kismet time to finish things) */
var() float DeliveryCloseAnimDelay;
/** Amount of timer since last collectible was reset to consider "recent" in terms of UI messaging to the player*/
var float CollectibleResetTimerLength;
/** Whether a collectible has been reset recently*/
var repnotify bool bCollectibleReset;
struct CollectibleActorInfo
{
// Collectible actor
var() KFObjectiveCollectActor Collectible;
// Trigger for collectible actor
var() KFUsableTrigger CollectibleTrigger;
};
// All collectible actors associated with this objective
var() array<CollectibleActorInfo> CollectibleInfo;
// Whether the sequence of collectibles should be randomized
var() bool bRandomSequence;
// The number of collectible actors that should be active at the same time
var() array<int> NumDesiredActiveCollectibles;
var transient array<int> AvailableCollectibleIndices;
var transient class DeliveryClass;
var float CollectActorIconSize;
var const color CollectibleIconAvailableColor;
var const color CollectibleIconCarriedColor;
var float UpdateTrailActorInterval;
enum ESoundSelectionType
{
ESoundSelectionType_Random,
ESoundSelectionType_Location,
ESoundSelectionType_Ordered,
ESoundSelectionType_Progress
};
struct KFSoundEventGroup
{
var() ESoundSelectionType SoundType;
var() array<AKEvent> SoundEvents;
var int CurrentEvent;
};
// AUDIO EVENTS
/** Sounds to play when one of the collectibles is dropped on the ground */
var() KFSoundEventGroup CollectibleDroppedSoundEvents;
/** Sounds to play when one of the collectibles is successfully delivered */
var() KFSoundEventGroup CollectibleDeliveredSoundEvents;
/** Sounds to play when one of the collect actors is activated */
var() KFSoundEventGroup CollectibleActivateSoundEvents;
/** Sounds to play when one of the players picks up an object from its original spawn */
var() KFSoundEventGroup CollectibleCollectSoundEvents;
/** A sound to play when the players have not collected in object in some time */
var() AkEvent CollectActorReminderSoundEvent;
/** How frequently to play audio when a collectible is dropped on the ground */
var() float CollectibleDroppedInterval;
/** How frequently to play audio when the player picks up an object from the collect actor */
var() float CollectibleCollectInterval;
/** How frequently to remind players that they need to pick up a collectible */
var() float CollectActorReminderInterval;
replication
{
if (bNetDirty)
NumAccomplished, bCollectibleReset;
}
simulated function ReplicatedEvent(name VarName)
{
if (VarName == nameof(NumAccomplished))
{
if (NumAccomplished != 0)
{
TriggerObjectiveProgressEvent(, float(NumAccomplished)/float(GetNumRequired()));
}
}
else
{
super.ReplicatedEvent(VarName);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
// KFInterface_MapObjective
simulated function ActivateObjective()
{
local int i;
super.ActivateObjective();
// every few seconds check whether a new trail actor has been set
SetTimer(UpdateTrailActorInterval, true, nameof(UpdateTrailActor));
if (DeliveryMeshActor != none)
{
DeliveryMeshActor.SkeletalMeshComponent.PlayAnim(DeliveryOpenAnimName);
}
if (Role == ROLE_Authority)
{
// initialize the array of collectibles to choose from
for (i = 0; i < CollectibleInfo.Length; ++i)
{
CollectibleInfo[i].Collectible.ObjectiveOwner = self;
AvailableCollectibleIndices.AddItem(i);
}
NumAccomplished = 0;
bActive = true;
bForceNetUpdate = true;
if (ActivationDelay > 0.f)
{
SetTimer(ActivationDelay, false, nameof(ChooseNextCollectible));
}
else
{
ChooseNextCollectible();
}
}
}
simulated function PlayDeliveryMeshCloseAnim()
{
if (DeliveryMeshActor != none)
{
DeliveryMeshActor.SkeletalMeshComponent.PlayAnim(DeliveryCloseAnimName);
}
}
simulated function DeactivateObjective()
{
local CollectibleActorInfo Info;
local KFPawn_Human KFPH;
local KFDroppedPickup_Carryable DroppedCarryable;
local KFCarryableObject_Collectible CarryableObject;
local Inventory InventoryItem;
super.DeactivateObjective();
if (DeliveryCloseAnimDelay > 0.f)
{
SetTimer(DeliveryCloseAnimDelay, false, nameof(PlayDeliveryMeshCloseAnim));
}
else
{
PlayDeliveryMeshCloseAnim();
}
if (Role == ROLE_Authority)
{
bActive = false;
bForceNetUpdate = true;
if (GetProgress() >= 1.f)
{
PlaySoundBase(SuccessSoundEvent, false, WorldInfo.NetMode == NM_DedicatedServer);
}
else
{
PlaySoundBase(FailureSoundEvent, false, WorldInfo.NetMode == NM_DedicatedServer);
}
ClearTimer(nameof(ChooseNextCollectible));
// also disable all of the actor collectors
foreach CollectibleInfo(Info)
{
Info.CollectibleTrigger.bActive = false;
Info.Collectible.SetEnabled(false);
Info.Collectible.SetActive(false);
Info.Collectible.bForceNetUpdate = true;
}
// grant the players' rewards
// remove the remaining carryables in the world from players' hands
foreach WorldInfo.AllPawns(class'KFPawn_Human', KFPH)
{
GrantReward(KFPlayerReplicationInfo(KFPH.PlayerReplicationInfo), KFPlayerController(KFPH.Controller));
if (KFPH != none && KFPH.InvManager != none)
{
InventoryItem = KFPH.InvManager.FindInventoryType(class<Inventory>(DeliveryClass), true);
if (InventoryItem != none)
if (InventoryItem != none)
{
KFPH.InvManager.RemoveFromInventory(InventoryItem);
CarryableObject = KFCarryableObject_Collectible(InventoryItem);
if (CarryableObject != none)
{
CarryableObject.UpdateReplicationInfo(false);
}
}
}
}
// destroy all pickups sitting on the ground
foreach AllActors(class'KFDroppedPickup_Carryable', DroppedCarryable)
{
if (DroppedCarryable != none)
{
DroppedCarryable.Destroy();
}
}
}
ClearTimer(nameof(Timer_CollectibleCollectCooldown));
ClearTimer(nameof(Timer_CollectibleDroppedCooldown));
ClearTimer(nameof(CollectActorReminder));
ClearTimer(nameof(UpdateTrailActor));
if (WorldInfo.NetMode != NM_DedicatedServer)
{
if (TrailActor != none)
{
TrailActor.Destroy();
TrailActor = none;
}
}
}
simulated function bool IsActive()
{
return bActive;
}
simulated function bool UsesProgress()
{
return true;
}
simulated function bool IsBonus();
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 float GetProgress()
{
return float(NumAccomplished) / float(GetNumRequired());
}
simulated function bool IsComplete()
{
return GetProgress() >= 1.f;
}
simulated function float GetActivationPctChance()
{
return 1.f;
}
simulated function string GetLocalizedRequirements()
{
return Localize("Objectives", default.RequirementsLocKey, "KFGame") @ GetNumRequired();
}
simulated function GetLocalizedStatus(out string statusMessage, out int bWarning, out int bNotification)
{
local int i;
local bool bDropped, bCarried, bReadyForPickup;
statusMessage = "";
if (GetProgress() >= 1.f)
{
statusMessage = Localize("Objectives", "KillRemainingZeds", LocalizationPackageName);
bWarning = 0;
bNotification = 0;
return;
}
for (i = 0; i < CollectibleInfo.Length; ++i)
{
if (CollectibleInfo[i].Collectible != none)
{
switch (CollectibleInfo[i].Collectible.CollectibleState)
{
case ECollectibleState_None:
if (CollectibleInfo[i].Collectible.bActive)
{
bReadyForPickup = true;
}
break;
case ECollectibleState_Carried:
bCarried = true;
break;
case ECollectibleState_Dropped:
bDropped = true;
break;
}
}
}
if (bDropped)
{
statusMessage = Localize("Objectives", "Dropped", LocalizationPackageName);
bWarning = 1;
return;
}
if (bCarried)
{
statusMessage = Localize("Objectives", "PickedUp", LocalizationPackageName);
bNotification = 1;
return;
}
if (bCollectibleReset)
{
statusMessage = Localize("Objectives", "Reset", LocalizationPackageName);
bWarning = 1;
return;
}
if (bReadyForPickup)
{
statusMessage = Localize("Objectives", "PickupTransportItem", LocalizationPackageName);
bNotification = 1;
return;
}
}
simulated function bool ShouldDrawIcon()
{
return true;
}
simulated function Vector GetIconLocation()
{
return Location;
}
simulated function DrawHUDActiveCollectibles(KFHUDBase hud, Canvas drawCanvas, bool bDropShadow)
{
local KFDroppedPickup_Carryable droppedCarryable;
local int i;
local float DropShadowModifier;
DropShadowModifier = bDropShadow ? 1.0f : 0.0f;
for (i = 0; i < CollectibleInfo.Length; i++)
{
if (CollectibleInfo[i].CollectibleTrigger.bActive && CollectibleInfo[i].Collectible != none)
{
DrawIconAtLocation(hud, drawCanvas, GetCollectibleIcon(), CollectibleInfo[i].Collectible.Location, -16.f + DropShadowModifier, -50.f + DropShadowModifier);
}
}
// at the location of all of the dropped collectible objects
foreach AllActors(class'KFDroppedPickup_Carryable', droppedCarryable)
{
if (droppedCarryable != none)
{
DrawIconAtLocation(hud, drawCanvas, GetCollectibleIcon(), droppedCarryable.Location, -16.f + DropShadowModifier, -50.f + DropShadowModifier);
}
}
}
simulated function DrawHUDCarriedCollectibles(KFHUDBase hud, Canvas drawCanvas, bool bDropShadow)
{
local int i;
local KFPawn_Human KFPH;
local float BarLength, FontScale, ResModifier, DropShadowModifier;
local PlayerController LocalPC;
local KFPlayerReplicationInfo KFPRI;
local PlayerReplicationInfo PRI;
local array<Controller> PawnPlayerControllers;
LocalPC = GetALocalPlayerController();
ResModifier = WorldInfo.static.GetResolutionBasedHUDScale() * hud.FriendlyHudScale;
BarLength = FMin(hud.PlayerStatusBarLengthMax * (drawCanvas.ClipX / 1024.f), hud.PlayerStatusBarLengthMax) * ResModifier;
FontScale = class'KFGameEngine'.Static.GetKFFontScale() * hud.FriendlyHudScale;
DropShadowModifier = bDropShadow ? 1.0f : 0.0f;
foreach WorldInfo.AllPawns(class'KFPawn_Human', KFPH)
{
// don't show the icon above the local player's head
if (LocalPC == none || LocalPC != KFPH.Controller)
{
if (KFPH.Mesh != none && KFPH.CylinderComponent != none && KFPH.Mesh.SkeletalMesh != none && KFPH.Mesh.bAnimTreeInitialised && `TimeSince(KFPH.Mesh.LastRenderTime) < 0.2f)
{
PawnPlayerControllers.AddItem(KFPH.Controller);
if (ClassIsChildOf(KFPH.WeaponClassForAttachmentTemplate, DeliveryClass))
{
DrawIconAtLocation(hud, drawCanvas, GetCollectibleIcon(),
KFPH.Mesh.GetPosition() + (KFPH.CylinderComponent.CollisionHeight * vect(0, 0, 2.5f)),
(BarLength * -0.5f) - (hud.PlayerStatusIconSize * ResModifier) - (CollectActorIconSize * ResModifier) + DropShadowModifier, // to the left of the perk icon
CollectActorIconSize * FontScale * ResModifier * 0.5f + DropShadowModifier); // centered vertically
}
}
}
}
for (i = 0; i < WorldInfo.GRI.PRIArray.Length; i++)
{
PRI = WorldInfo.GRI.PRIArray[i];
KFPRI = KFPlayerReplicationInfo(PRI);
if (KFPRI != none && KFPRI.bCarryingCollectible)
{
if (PawnPlayerControllers.Find(KFPRI.Owner) == INDEX_NONE && PRI != LocalPC.PlayerReplicationInfo)
{
DrawIconAtLocation(hud, drawCanvas, GetCollectibleIcon(),
KFPRI.GetSmoothedPawnIconLocation(hud.HumanPlayerIconInterpMult) + class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 2),
-1 * (hud.PlayerStatusIconSize * ResModifier) - (CollectActorIconSize * ResModifier) + DropShadowModifier, // to the left of the perk icon
-1 * CollectActorIconSize * ResModifier + DropShadowModifier); // centered vertically
}
}
}
}
simulated function DrawHUD(KFHUDBase hud, Canvas drawCanvas)
{
// at the location of all the active locations out in the world
if (!IsLocalPlayerCarryingACollectible())
{
if (drawCanvas != none)
{
drawCanvas.SetDrawColorStruct(hud.PlayerBarShadowColor);
DrawHUDActiveCollectibles(hud, drawCanvas, true);
drawCanvas.SetDrawColorStruct(CollectibleIconAvailableColor);
DrawHUDActiveCollectibles(hud, drawCanvas, false);
}
}
// at the location of all the collectible objects being carried by players
if (drawCanvas != none)
{
drawCanvas.SetDrawColorStruct(hud.PlayerBarShadowColor);
DrawHUDCarriedCollectibles(hud, drawCanvas, true);
drawCanvas.SetDrawColorStruct(CollectibleIconCarriedColor);
DrawHUDCarriedCollectibles(hud, drawCanvas, false);
}
}
// don't show the objective HUD unless the local player is carrying a collectible
simulated function bool ShouldShowObjectiveHUD()
{
return IsLocalPlayerCarryingACollectible();
}
// always show the objective progress regardless of draw distance
simulated function bool HasObjectiveDrawDistance()
{
return false;
}
simulated function Texture2D GetCollectibleIcon()
{
return ObjectiveIcon;
}
simulated function string GetProgressText()
{
if (!bActive)
{
return "";
}
return NumAccomplished $ "/" $ GetNumRequired();
}
simulated function bool GetIsMissionCritical()
{
return bIsMissionCriticalObjective;
}
// end KFInterface_MapObjective
//////////////////////////////////////////////////////////////////////////////////////////////////////
simulated function PlaySoundEvent(out KFSoundEventGroup SoundGroup, int nSpecificIndex)
{
local int nIndex;
switch (SoundGroup.SoundType)
{
case ESoundSelectionType_Random:
nIndex = Rand(SoundGroup.SoundEvents.Length);
break;
case ESoundSelectionType_Location:
nIndex = nSpecificIndex;
break;
case ESoundSelectionType_Ordered:
nIndex = SoundGroup.CurrentEvent;
SoundGroup.CurrentEvent++;
//loop back to the beginning
if (SoundGroup.CurrentEvent >= SoundGroup.SoundEvents.length)
{
SoundGroup.CurrentEvent = 0;
}
break;
case ESoundSelectionType_Progress:
nIndex = NumAccomplished;
break;
}
if (nIndex >= 0 && nIndex < SoundGroup.SoundEvents.Length && SoundGroup.SoundEvents[nIndex] != none)
{
PlaySoundBase(SoundGroup.SoundEvents[nIndex], false, WorldInfo.NetMode == NM_DedicatedServer);
}
}
simulated function ResetOrderedSoundEventsForRespawn()
{
if (CollectibleActivateSoundEvents.SoundType == ESoundSelectionType_Ordered)
{
CollectibleActivateSoundEvents.CurrentEvent--;
}
if (CollectibleCollectSoundEvents.SoundType == ESoundSelectionType_Ordered)
{
CollectibleCollectSoundEvents.CurrentEvent--;
}
if (CollectibleDroppedSoundEvents.SoundType == ESoundSelectionType_Ordered)
{
CollectibleDroppedSoundEvents.CurrentEvent--;
}
}
simulated function OnCollectActor(KFObjectiveCollectActor Collectible)
{
if (!bActive)
{
return;
}
if (!IsTimerActive(nameof(Timer_CollectibleCollectCooldown)))
{
//play the matching "collected" sound for that collect actor
PlaySoundEvent(CollectibleCollectSoundEvents, GetCollectibleIndexFromCollectActor(Collectible));
SetTimer(CollectibleCollectInterval, false, nameof(Timer_CollectibleCollectCooldown));
}
}
simulated function ProgressObjective()
{
local KFGameReplicationInfo KFGRI;
if (Role == ROLE_Authority)
{
NumAccomplished++;
bForceNetUpdate = true;
if (GetProgress() >= 1.f)
{
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
if (KFGRI != none)
{
KFGRI.DeactivateObjective();
}
}
else
{
if (TimeUntilNextActivation > 0.f)
{
SetTimer(TimeUntilNextActivation, false, nameof(ChooseNextCollectible));
}
else
{
ChooseNextCollectible();
}
}
TriggerObjectiveProgressEvent(, float(NumAccomplished) / float(GetNumRequired()));
}
}
simulated function ChooseNextCollectible()
{
local int i, j, RandAvailableIdx, RandCollectibleIdx, NumActiveCollectibles;
local bool NeedsAvailableInit;
if (Role == ROLE_Authority)
{
if (CollectibleInfo.Length == 0)
{
return;
}
// if there are more required deliveries than there are spawn locations,
// then enter a mode where whenever a collectible is delivered it gets respawned back where it came from
NeedsAvailableInit = AvailableCollectibleIndices.Length == 0 && CollectibleInfo.Length < GetNumRequired();
for (i = 0; i < CollectibleInfo.Length; ++i)
{
// check number of active (non-completed) collectibles
if (CollectibleInfo[i].CollectibleTrigger.bActive)
{
NumActiveCollectibles++;
}
// initialize the array of collectibles to choose from
else if (NeedsAvailableInit)
{
AvailableCollectibleIndices.AddItem(i);
}
}
// if less than the desired amount
for(j = 0; j < GetNumDesiredActive() - NumActiveCollectibles; j++)
{
if (AvailableCollectibleIndices.Length <= 0) break;
// choose another (random or ordered) and spawn it
RandAvailableIdx = bRandomSequence ? Rand(AvailableCollectibleIndices.Length) : 0;
RandCollectibleIdx = AvailableCollectibleIndices[RandAvailableIdx];
AvailableCollectibleIndices.Remove(RandAvailableIdx, 1);
// unhide the chosen collectible and set the trigger and collect actor as active
CollectibleInfo[RandCollectibleIdx].Collectible.SetEnabled(true);
CollectibleInfo[RandCollectibleIdx].Collectible.SetActive(true);
CollectibleInfo[RandCollectibleIdx].Collectible.bForceNetUpdate = true;
CollectibleInfo[RandCollectibleIdx].CollectibleTrigger.bActive = true;
// play audio event for activating object
PlaySoundEvent(CollectibleActivateSoundEvents, RandCollectibleIdx);
}
}
}
simulated function Actor FindClosestActiveCollector()
{
local CollectibleActorInfo Info;
local float shortestDistance, currentDistance;
local Actor closestActor;
local PlayerController LocalPC;
LocalPC = GetALocalPlayerController();
closestActor = none;
shortestDistance = -1.f;
if (LocalPC != none && LocalPC.Pawn != none)
{
foreach CollectibleInfo(Info)
{
if (Info.Collectible != none && Info.Collectible.bActive)
{
// measure the distance from the local player to the object and choose the closest one
currentDistance = VSize(Info.Collectible.Location - LocalPC.Pawn.Location);
if (shortestDistance < 0 || currentDistance < shortestDistance)
{
shortestDistance = currentDistance;
closestActor = Info.CollectibleTrigger;
}
}
}
}
return closestActor;
}
simulated function bool GetClosestPlayerCarryingACollectible(out vector CarrierLocation)
{
local KFPawn_Human KFPH;
local PlayerController LocalPC;
local array<Controller> PawnPlayerControllers;
local int i;
local PlayerReplicationInfo PRI;
local KFPlayerReplicationInfo KFPRI;
local bool bFoundCarrier;
local float CurrentDistance;
LocalPC = GetALocalPlayerController();
bFoundCarrier = false;
// carried by players within replication range
foreach WorldInfo.AllPawns(class'KFPawn_Human', KFPH)
{
if (LocalPC == none || LocalPC != KFPH.Controller)
{
if (KFPH.Mesh != none && KFPH.CylinderComponent != none && KFPH.Mesh.SkeletalMesh != none && KFPH.Mesh.bAnimTreeInitialised && `TimeSince(KFPH.Mesh.LastRenderTime) < 0.2f)
{
PawnPlayerControllers.AddItem(KFPH.Controller);
// carrying the current objective type
if (ClassIsChildOf(KFPH.WeaponClassForAttachmentTemplate, DeliveryClass))
{
if (LocalPC.Pawn != none)
{
if (!bFoundCarrier || VSize(KFPH.Mesh.GetPosition() - LocalPC.Pawn.Location) < CurrentDistance)
{
CarrierLocation = KFPH.Mesh.GetPosition();
CurrentDistance = VSize(CarrierLocation - LocalPC.Pawn.Location);
bFoundCarrier = true;
}
}
}
}
}
}
// using the replication info to get the smoothed position of players outside of range
for (i = 0; i < WorldInfo.GRI.PRIArray.Length; i++)
{
PRI = WorldInfo.GRI.PRIArray[i];
KFPRI = KFPlayerReplicationInfo(PRI);
if (KFPRI != none && KFPRI.bCarryingCollectible)
{
if (PawnPlayerControllers.Find(KFPRI.Owner) == INDEX_NONE && PRI != LocalPC.PlayerReplicationInfo)
{
if (!bFoundCarrier || VSize(KFPRI.GetSmoothedPawnIconLocation(0.0f) - LocalPC.Pawn.Location) < CurrentDistance)
{
CarrierLocation = KFPRI.GetSmoothedPawnIconLocation(0.0f);
CurrentDistance = VSize(CarrierLocation - LocalPC.Pawn.Location);
bFoundCarrier = true;
}
}
}
}
return bFoundCarrier;
}
simulated function bool GetClosestDroppedCollectible(out Actor DroppedCarryable)
{
local KFDroppedPickup_Carryable CurrentCarryable;
local float CurrentDistance, ShortestDistance;
local PlayerController LocalPC;
LocalPC = GetALocalPlayerController();
if (LocalPC != none && LocalPC.Pawn != none)
{
foreach AllActors(class'KFDroppedPickup_Carryable', CurrentCarryable)
{
if (CurrentCarryable != none)
{
CurrentDistance = VSize(CurrentCarryable.Location - LocalPC.Pawn.Location);
if (DroppedCarryable == none || CurrentDistance < ShortestDistance)
{
ShortestDistance = CurrentDistance;
DroppedCarryable = CurrentCarryable;
}
}
}
}
return DroppedCarryable != none;
}
simulated function UpdateTrailActor()
{
local Actor pathTarget;
local vector CarrierLocation;
local PlayerController LocalPC;
local float AnchorDist;
// only set a trail actor if this map objective is active
if (bActive && WorldInfo.NetMode != NM_DedicatedServer)
{
if (TrailActor == none)
{
TrailActor = class'WorldInfo'.static.GetWorldInfo().Spawn(class'KFReplicatedShowPathActor', none);
}
if (IsLocalPlayerCarryingACollectible())
{
//TrailActor.SetEmitterTemplate(ParticleSystem'FX_Objective_Temp.FX_Objective_Temp_Transport_Trail');
TrailActor.SetEmitterTemplate(ParticleSystem'FX_Gameplay_EMIT.FX_Objective_White_Trail');
}
else
{
//TrailActor.SetEmitterTemplate(ParticleSystem'FX_Objective_Temp.FX_Objective_Temp_Collect_Trail');
TrailActor.SetEmitterTemplate(ParticleSystem'FX_Gameplay_EMIT.FX_Objective_White_Trail');
}
if (IsLocalPlayerCarryingACollectible()) // local player carrying an object
{
pathTarget = self;
}
else
{
if (GetClosestPlayerCarryingACollectible(CarrierLocation)) // any player carrying an object
{
// getting the anchor from the carrier location so the same type of pathfinding can be used
LocalPC = GetALocalPlayerController();
if (LocalPC.Pawn != none)
{
pathTarget = LocalPC.Pawn.GetBestAnchor(none, CarrierLocation, false, false, AnchorDist);
}
}
else if (!GetClosestDroppedCollectible(pathTarget)) // any dropped collectible
{
// any active collectible
pathTarget = FindClosestActiveCollector();
}
}
if (pathTarget != TrailActor.Target)
{
TrailActor.SetPathTarget(pathTarget);
}
}
}
simulated function ResetCollectReminder()
{
SetTimer(CollectActorReminderInterval, true, nameof(CollectActorReminder));
}
simulated function ClearCollectReminder()
{
ClearTimer(nameof(CollectActorReminder));
}
simulated function CollectActorReminder()
{
if (CollectActorReminderSoundEvent != none)
{
PlaySoundBase(CollectActorReminderSoundEvent, false, WorldInfo.NetMode == NM_DedicatedServer);
}
}
simulated function int GetCollectibleIndexFromCollectActor(KFObjectiveCollectActor collectActor)
{
local int i;
for (i = 0; i < CollectibleInfo.Length; ++i)
{
if (CollectibleInfo[i].Collectible == collectActor)
{
return i;
}
}
return INDEX_NONE;
}
// This event is received from a volume that has this actor as its AssociatedActor
event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal)
{
local KFDroppedPickup Pickup;
local KFPawn_Human KFPH;
local bool bShouldProgress;
local KFCarryableObject_Collectible carryableObject;
local Inventory InventoryItem;
local KFInventoryManager KFInvManager;
super.Touch(Other, OtherComp, HitLocation, HitNormal);
if (bActive)
{
bShouldProgress = false;
Pickup = KFDroppedPickup(Other);
if (Pickup != none && Pickup.Inventory != none)
{
if (ClassIsChildOf(Pickup.Inventory.Class, DeliveryClass))
{
// find which collectible this belongs to; needs to happen before the object gets destroyed below
carryableObject = KFCarryableObject_Collectible(Pickup.Inventory);
if (carryableObject != none && carryableObject.ParentCollectActor != none)
{
//play the matching "dropped off" sound for that collect actor
PlaySoundEvent(CollectibleDeliveredSoundEvents, GetCollectibleIndexFromCollectActor(carryableObject.ParentCollectActor));
}
Pickup.Destroy();
bShouldProgress = true;
}
}
KFPH = KFPawn_Human(Other);
if (KFPH != none)
{
InventoryItem = KFPH.InvManager.FindInventoryType(class<Inventory>(DeliveryClass), true);
if (InventoryItem != none)
{
// find which collectible this belongs to; needs to happen before the object gets destroyed below
carryableObject = KFCarryableObject_Collectible(InventoryItem);
if (carryableObject != none && carryableObject.ParentCollectActor != none)
{
//play the matching "dropped off" sound for that collect actor
PlaySoundEvent(CollectibleDeliveredSoundEvents, GetCollectibleIndexFromCollectActor(carryableObject.ParentCollectActor));
carryableObject.UpdateReplicationInfo(false);
}
KFInvManager = KFInventoryManager(KFPH.InvManager);
if (KFInvManager != none && KFInvManager.ItemIsInInventory(InventoryItem))
{
KFPH.InvManager.RemoveFromInventory(InventoryItem);
InventoryItem.Destroy();
bShouldProgress = true;
}
}
}
if (bShouldProgress)
{
if (carryableObject != none && carryableObject.ParentCollectActor != none)
{
carryableObject.ParentCollectActor.SetCollectibleState(ECollectibleState_Delivered);
}
ProgressObjective();
}
}
}
simulated function Timer_CollectibleReset()
{
bCollectibleReset = false;
}
simulated function RespawnCollectible(KFObjectiveCollectActor collectActor)
{
local int i;
bCollectibleReset = true;
SetTimer(CollectibleResetTimerLength, false, nameof(Timer_CollectibleReset));
ResetOrderedSoundEventsForRespawn();
for (i = 0; i < CollectibleInfo.Length; i++)
{
if (CollectibleInfo[i].Collectible == collectActor)
{
AvailableCollectibleIndices.InsertItem(0, i);
CollectibleInfo[i].Collectible.SetEnabled(false);
CollectibleInfo[i].Collectible.SetActive(false);
CollectibleInfo[i].CollectibleTrigger.bActive = false;
ChooseNextCollectible();
break;
}
}
}
simulated function OnCarryableDropped(KFObjectiveCollectActor collectActor)
{
if (!IsTimerActive(nameof(Timer_CollectibleDroppedCooldown)))
{
PlaySoundEvent(CollectibleDroppedSoundEvents, GetCollectibleIndexFromCollectActor(collectActor));
SetTimer(CollectibleDroppedInterval, false, nameof(Timer_CollectibleDroppedCooldown));
}
}
simulated function DrawIconAtLocation(KFHUDBase hud, Canvas drawCanvas, Texture2D icon, vector iconLocation, float xOffset, float yOffset)
{
local vector ScreenPos;
local float ResModifier;
local float ViewDot;
local vector ViewLocation, ViewVector;
local rotator ViewRotation;
local PlayerController LocalPC;
// determine whether that object is within the player's view
LocalPC = GetALocalPlayerController();
if (LocalPC != none)
{
LocalPC.GetPlayerViewPoint(ViewLocation, ViewRotation);
ViewVector = vector(ViewRotation);
}
ViewDot = Normal((iconLocation + (class'KFPawn_Human'.default.CylinderComponent.CollisionHeight * vect(0, 0, 1))) - ViewLocation) dot ViewVector;
if (icon != none && ViewDot > 0)
{
ResModifier = WorldInfo.static.GetResolutionBasedHUDScale() * hud.FriendlyHudScale;
ScreenPos = drawCanvas.Project(iconLocation);
ScreenPos.X += xOffset;
ScreenPos.Y += yOffset;
if (ScreenPos.X < 0 || ScreenPos.X > drawCanvas.ClipX || ScreenPos.Y < 0 || ScreenPos.Y > drawCanvas.ClipY)
{
//if it is off screen, do not render
return;
}
//draw icon
drawCanvas.SetPos(ScreenPos.X, ScreenPos.Y + 12); //offset to better align with the other icons in the player's name tag
drawCanvas.DrawTile(icon, CollectActorIconSize * ResModifier, CollectActorIconSize * ResModifier, 0, 0, 256, 256);
}
}
simulated function bool IsLocalPlayerCarryingACollectible()
{
local PlayerController LocalPC;
local KFPawn_Human KFPH;
local Inventory InventoryItem;
LocalPC = GetALocalPlayerController();
if (LocalPC != none && LocalPC.Pawn != none)
{
KFPH = KFPawn_Human(LocalPC.Pawn);
InventoryItem = KFPH.InvManager.FindInventoryType(class<Inventory>(DeliveryClass), true);
return (KFPH != none && InventoryItem != none);
}
return false;
}
simulated function int GetNumRequired()
{
local int PlayerCount;
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
PlayerCount = Clamp(KFGRI.GetNumPlayers(), 1, 6) - 1;
if (PlayerCount < NumRequired.Length)
{
return NumRequired[PlayerCount];
}
else if (NumRequired.Length > 0)
{
// if there weren't enough entries, use the last one
return NumRequired[NumRequired.Length - 1];
}
return 1;
}
simulated function int GetNumDesiredActive()
{
local int PlayerCount;
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
PlayerCount = Clamp(KFGRI.GetNumPlayers(), 1, 6) - 1;
if (PlayerCount < NumDesiredActiveCollectibles.Length)
{
return NumDesiredActiveCollectibles[PlayerCount];
}
else if (NumDesiredActiveCollectibles.Length > 0)
{
// if there weren't enough entries, use the last one
return NumDesiredActiveCollectibles[NumDesiredActiveCollectibles.Length - 1];
}
return 1;
}
function Timer_CollectibleCollectCooldown() {}
function Timer_CollectibleDroppedCooldown() {}
defaultproperties
{
LocalizationPackageName = "KFGame"
DescriptionLocKey="TransportWaveDescription"
DescriptionShortLocKey = "TransportWaveDescriptionShort"
RequirementsLocKey="RequiredCollectActor"
LocalizationKey="TransportWaveObjective"
NameShortLocKey="TransportWaveObjective"
Physics=PHYS_None
bStatic=false
bNoDelete=true
bTickIsDisabled=false
RemoteRole=ROLE_SimulatedProxy
bAlwaysRelevant=true
bOnlyDirtyReplication=true
bSkipActorPropertyReplication=true
bIgnoreNetRelevancyCollision=true
NetUpdateFrequency=0.1
bReplicateRigidBodyLocation=false
bBlockActors=false
bWorldGeometry=false
bCollideWorld=false
bNoEncroachCheck=false
bProjTarget=false
bCanStepUpOn=false
bMovable=false
bRandomSequence=true
NumDesiredActiveCollectibles=(1)
CollectActorIconSize=32.f;
CollectibleIconAvailableColor=(R=0, G=255, B=0, A=255);
CollectibleIconCarriedColor=(R=185, G=70, B=255, A=255);
DeliveryClass = class'KFCarryableObject_Collectible'
UpdateTrailActorInterval = 1.f;
CollectActorReminderInterval = 40.f;
CollectibleDroppedInterval = 20.f;
CollectibleCollectInterval = 20.f;
SupportedEvents.Add(class'KFSeqEvent_ObjectiveProgress')
NumRequired=(1)
PerPlayerSpawnRateMod=(1.f, 1.f, 1.f, 1.f, 1.f, 1.f)
ObjectiveIcon=Texture2D'Objectives_UI.UI_Objectives_ObjectiveMode'
CollectibleResetTimerLength=60.0f;
DeliveryCloseAnimDelay=7.f
}