//============================================================================= // KFTrigger_MinigameGenerator //============================================================================= // Trigger used to activate all minigames in a level //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC // - Dan Weiss //============================================================================= class KFTrigger_MinigameGenerator extends Trigger_PawnsOnly config(Game) implements(KFInterface_Usable); /** Amount of time (in seconds) that the generator should remain running */ var() float ActiveTime; /** Amount of time until the generator can be reactivated */ var() float ReactivationTime; var bool bActiveGenerator; var bool bCanBeActivated; var array LevelButtons; /** Objective index for the event this is tied to */ var() int ObjectiveIndex; /** Index of the event this is tied to */ var() int EventIndex; /** Amount of time remaining until the generator shuts off */ var repnotify byte GeneratorTimeRemaining; var float PreviousTimeRemaining; /** Actor reference showing status indicator */ var() array GeneratorStatusIndicators; /** Material slot to use for the status modification */ var() int GlowIndex; struct GeneratorStatus { var() float MinTimeRemaining; var() LinearColor StatusColor; }; var() array StatusIndicator; var int CurrentStatusIndex; struct EmissiveActor { var() Actor EmissiveActor; var float GeneratorOnValue; var float GeneratorOffValue; structdefaultproperties { GeneratorOnValue=0.f GeneratorOffValue=0.f } }; struct EmissiveUpdateActorList { var() float ActivationDelay; var() array ActorList; var array CachedActorList; }; var() array MapEmissiveActorList; var int CurrentEmissiveActorListIndex; replication { if (bNetDirty) GeneratorTimeRemaining; } simulated event ReplicatedEvent(name VarName) { if (VarName == 'GeneratorTimeRemaining') { UpdateGeneratorStatus(); } else { super.ReplicatedEvent(VarName); } } function Tick(float DeltaTime) { super.Tick(DeltaTime); if (bActiveGenerator) { GeneratorTimeRemaining = FloatToByte(GetRemainingTimeForTimer('DeactivateMinigames') / ActiveTime); UpdateGeneratorStatus(); } } simulated function UpdateGeneratorStatus() { local float CurrentTimeRemaining; local LinearColor ParamColor; local bool bStatusChange; local int CurIndex; local int i; if (WorldInfo.NetMode != NM_DedicatedServer && StatusIndicator.Length > 0) { CurIndex = CurrentStatusIndex; CurrentTimeRemaining = ByteToFloat(GeneratorTimeRemaining); //Reset or first startup if (PreviousTimeRemaining < CurrentTimeRemaining) { CurrentStatusIndex = 0; if (CurrentTimeRemaining > 0) { EnableEmissiveActors(); } } //Update if we've gone past the time required for current color if (StatusIndicator[CurrentStatusIndex].MinTimeRemaining > CurrentTimeRemaining) { for (CurrentStatusIndex = 0; CurrentStatusIndex < StatusIndicator.Length; ++CurrentStatusIndex) { if (StatusIndicator[CurrentStatusIndex].MinTimeRemaining < CurrentTimeRemaining) { break; } } } CurrentStatusIndex = Min(CurrentStatusIndex, StatusIndicator.Length - 1); //Check for change and update color if necessary bStatusChange = CurrentStatusIndex != CurIndex; if (bStatusChange && GeneratorStatusIndicators.Length > 0 ) { ParamColor = StatusIndicator[CurrentStatusIndex].StatusColor; for (i = 0; i < GeneratorStatusIndicators.Length; ++i) { if (MaterialInstance(GeneratorStatusIndicators[i].StaticMeshComponent.GetMaterial(GlowIndex)) != none) { MaterialInstance(GeneratorStatusIndicators[i].StaticMeshComponent.GetMaterial(GlowIndex)).SetVectorParameterValue('Scalar_Glow_color', ParamColor); } } } PreviousTimeRemaining = CurrentTimeRemaining; if (CurrentTimeRemaining <= 0) { DisableEmissiveActors(); } } } simulated function EnableEmissiveActors() { CurrentEmissiveActorListIndex = 0; if (MapEmissiveActorList.Length > 0) { if (MapEmissiveActorList[0].ActivationDelay > 0.f) { SetTimer(MapEmissiveActorList[0].ActivationDelay, false, nameof(EnableNextEmissiveList)); } else { EnableNextEmissiveList(); } } } simulated function EnableNextEmissiveList() { local EmissiveActor CurrentActor; local int i; local StaticMeshActor Smesh; local SkeletalMeshActor Skmesh; local MaterialInstance MatInst; if (MapEmissiveActorList[CurrentEmissiveActorListIndex].ActorList.Length > 0) { foreach MapEmissiveActorList[CurrentEmissiveActorListIndex].CachedActorList(CurrentActor) { Smesh = StaticMeshActor(CurrentActor.EmissiveActor); Skmesh = SkeletalMeshActor(CurrentActor.EmissiveActor); if (Smesh != none) { for (i = 0; i < Smesh.StaticMeshComponent.Materials.Length; ++i) { MatInst = MaterialInstance(Smesh.StaticMeshComponent.GetMaterial(i)); if (MatInst != None) { MatInst.SetScalarParameterValue('Scalar_Emissive_Intensity', CurrentActor.GeneratorOnValue); } } } else if (Skmesh != none) { for (i = 0; i < Skmesh.SkeletalMeshComponent.Materials.Length; ++i) { MatInst = MaterialInstance(Skmesh.SkeletalMeshComponent.GetMaterial(i)); if (MatInst != None) { MatInst.SetScalarParameterValue('Scalar_Emissive_Intensity', CurrentActor.GeneratorOnValue); } } } } } CurrentEmissiveActorListIndex++; if (CurrentEmissiveActorListIndex < MapEmissiveActorList.Length) { if (MapEmissiveActorList[CurrentEmissiveActorListIndex].ActivationDelay > 0.f) { SetTimer(MapEmissiveActorList[CurrentEmissiveActorListIndex].ActivationDelay - MapEmissiveActorList[CurrentEmissiveActorListIndex - 1].ActivationDelay, false, nameof(EnableNextEmissiveList)); } else { EnableNextEmissiveList(); } } } simulated function DisableEmissiveActors() { local EmissiveUpdateActorList CurrentList; local EmissiveActor CurrentActor; local int i; local StaticMeshActor Smesh; local SkeletalMeshActor Skmesh; local MaterialInstance MatInst; foreach MapEmissiveActorList(CurrentList) { foreach CurrentList.CachedActorList(CurrentActor) { Smesh = StaticMeshActor(CurrentActor.EmissiveActor); Skmesh = SkeletalMeshActor(CurrentActor.EmissiveActor); if (Smesh != none) { for (i = 0; i < Smesh.StaticMeshComponent.Materials.Length; ++i) { MatInst = MaterialInstance(Smesh.StaticMeshComponent.GetMaterial(i)); if (MatInst != None) { MatInst.SetScalarParameterValue('Scalar_Emissive_Intensity', CurrentActor.GeneratorOffValue); } } } else if (Skmesh != none) { for (i = 0; i < Skmesh.SkeletalMeshComponent.Materials.Length; ++i) { MatInst = MaterialInstance(Skmesh.SkeletalMeshComponent.GetMaterial(i)); if (MatInst != None) { MatInst.SetScalarParameterValue('Scalar_Emissive_Intensity', CurrentActor.GeneratorOffValue); } } } } } } simulated function InitEmissiveActors() { local EmissiveActor NewActor; local int i, j, k; local StaticMeshActor Smesh; local SkeletalMeshActor Skmesh; local float CurValue; for (i = 0; i < MapEmissiveActorList.Length; ++i) { for (j = 0; j < MapEmissiveActorList[i].ActorList.Length; ++j) { Smesh = StaticMeshActor(MapEmissiveActorList[i].ActorList[j]); Skmesh = SkeletalMeshActor(MapEmissiveActorList[i].ActorList[j]); NewActor.EmissiveActor = MapEmissiveActorList[i].ActorList[j]; k = 0; if (Smesh != none) { while (Smesh.StaticMeshComponent.GetMaterial(k) != none) { Smesh.StaticMeshComponent.CreateAndSetMaterialInstanceConstant(k); MaterialInstance(Smesh.StaticMeshComponent.GetMaterial(k)).Parent.GetScalarParameterValue('Scalar_Emissive_Intensity', CurValue); ++k; NewActor.GeneratorOnValue = Max(CurValue, NewActor.GeneratorOnValue); } } else if (Skmesh != none) { while (Skmesh.SkeletalMeshComponent.GetMaterial(k) != none) { Skmesh.SkeletalMeshComponent.CreateAndSetMaterialInstanceConstant(k); MaterialInstance(Skmesh.SkeletalMeshComponent.GetMaterial(k)).Parent.GetScalarParameterValue('Scalar_Emissive_Intensity', NewActor.GeneratorOnValue); ++k; NewActor.GeneratorOnValue = Max(CurValue, NewActor.GeneratorOnValue); } } MapEmissiveActorList[i].CachedActorList.AddItem(NewActor); } } } /** KFInterface_Usable */ simulated function bool GetIsUsable(Pawn User) { return true; } /** KFInterface_Usable */ simulated function int GetInteractionIndex(Pawn User) { return IMT_UseMinigameGenerator; } /** HUD */ event Touch(Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal) { super.Touch(Other, OtherComp, HitLocation, HitNormal); class'KFPlayerController'.static.UpdateInteractionMessages(Other); } /** HUD */ event UnTouch(Actor Other) { super.UnTouch(Other); class'KFPlayerController'.static.UpdateInteractionMessages(Other); } /** Cache all minigame triggers at startup so we don't have to do all actors every activation */ simulated function PostBeginPlay() { local KFTrigger_MinigameButton MinigameTrigger; local int i; super.PostBeginPlay(); if (Role == ROLE_Authority) { foreach WorldInfo.AllActors(class'KFTrigger_MinigameButton', MinigameTrigger) { LevelButtons.AddItem(MinigameTrigger); } } if (WorldInfo.NetMode != NM_DedicatedServer) { if (GeneratorStatusIndicators.Length > 0 && GlowIndex >= 0) { for (i = 0; i < GeneratorStatusIndicators.Length; ++i) { if (GlowIndex < GeneratorStatusIndicators[i].StaticMeshComponent.Materials.Length) { GeneratorStatusIndicators[i].StaticMeshComponent.CreateAndSetMaterialInstanceConstant(GlowIndex); } } } InitEmissiveActors(); UpdateGeneratorStatus(); } } /** Handles trigger interaction */ function bool UsedBy(Pawn User) { if (bCanBeActivated) { //Clear any timer that was already started, treat this as a full reactivation of minigames ClearTimer('DeactivateMinigames'); SetTimer(ActiveTime, false, 'DeactivateMinigames'); //Set reactivation timer to prevent button spam bCanBeActivated = false; SetTimer(ReactivationTime, false, 'AllowActivation'); ActivateMinigames(); //Activate objective completion for turning on generator TriggerObjectiveCompletion(); } return true; } function TriggerObjectiveCompletion() { local KFPlayercontroller KFPC; local bool bIsObjectiveDataValid; bIsObjectiveDataValid = ObjectiveIndex >= 0 && ObjectiveIndex < 5 && EventIndex > SEI_None && EventIndex < SEI_MAX; foreach WorldInfo.AllControllers(class'KFPlayerController', KFPC) { KFPC.ClientOnTriggerUsed(class'KFTrigger_MinigameGenerator'); if(bIsObjectiveDataValid) { KFPC.ClientOnTryCompleteObjective(ObjectiveIndex, EventIndex); } } } function AllowActivation() { local int i; bCanBeActivated = true; for (i = 0; i < Touching.Length; ++i) { class'KFPlayerController'.static.UpdateInteractionMessages(Touching[i]); } } /** Handle activation of the generator and minigames in the level */ function ActivateMinigames() { local KFTrigger_MinigameButton MinigameButton; //Don't activate if we're simply updating the timer if (!bActiveGenerator) { foreach LevelButtons(MinigameButton) { MinigameButton.SetMinigameActive(); } bActiveGenerator = true; UpdateGeneratorState(); } else { GeneratorReactivated(); } } /** Handle deactivation of the generator and minigames in the level */ function DeactivateMinigames() { local KFTrigger_MinigameButton MinigameButton; //Don't deactivate if we're already inactive if (bActiveGenerator) { foreach LevelButtons(MinigameButton) { MinigameButton.SetMinigameInactive(); } bActiveGenerator = false; UpdateGeneratorState(); GeneratorTimeRemaining = 0; UpdateGeneratorStatus(); } } function GeneratorReactivated() { local KFSeqEvent_MinigameGeneratorStateChanged StateChangeEvent; local int i; //Notify Kismet that minigame state has been changed for (i = 0; i < GeneratedEvents.Length; i++) { StateChangeEvent = KFSeqEvent_MinigameGeneratorStateChanged(GeneratedEvents[i]); if (StateChangeEvent != none) { StateChangeEvent.StateChanged(self, self, bActiveGenerator, true); } } } function UpdateGeneratorState() { local KFSeqEvent_MinigameGeneratorStateChanged StateChangeEvent; local int i; //Notify Kismet that minigame state has been changed for (i = 0; i < GeneratedEvents.Length; i++) { StateChangeEvent = KFSeqEvent_MinigameGeneratorStateChanged(GeneratedEvents[i]); if (StateChangeEvent != none) { StateChangeEvent.StateChanged(self, self, bActiveGenerator, false); } } } defaultproperties { SupportedEvents.Empty SupportedEvents.Add(class'KFSeqEvent_MinigameGeneratorStateChanged') RemoteRole=ROLE_SimulatedProxy NetPriority=+00002.500000 bAlwaysRelevant=true ActiveTime=300.0f ReactivationTime=10.0f bCanBeActivated=true CurrentStatusIndex=-1 GlowIndex=1 PreviousTimeRemaining=-1.f CurrentEmissiveActorListIndex=-1 EventIndex=-1 ObjectiveIndex=-1 }