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

386 lines
11 KiB
Ucode

//=============================================================================
// KFMG_RiggedTargetGame
//=============================================================================
// Extension of target game that uses a rigged animation and series of other
// subactors to handle the individual target hits.
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// - Dan Weiss
//=============================================================================
class KFMG_RiggedTargetGame extends KFMG_TargetGame;
/** Archetype of target actor that will be spawned per-bone */
var() Actor TargetArchetype;
/** SKL actor used to drive animation of the minigame */
var() repnotify KFMGA_TargetGame MinigameRig;
/** How many targets have not been hit */
var int TargetsRemaining;
/** Which animation index to use (determined by server) */
var repnotify int AnimationIndex;
/** Which rig index to use for grabbing animation data (always 0 in base game, 0...n in multilevel extension) */
var repnotify int RigIndex;
/** Local track variable for the state of the game. Primarily used for repnotify check to make sure all info has come down */
var bool bLocalStarted;
/** End game state - 0 victory, 1 defeat */
var repnotify int EndGameState;
enum eAnimationState
{
EAS_Closed, //Inactive state
EAS_Open, //Transition to active
EAS_Active, //Active state
EAS_Idle, //Pre-close transition
EAS_Close, //Transition to inactive state
};
/** Time to take to blend to new animations (if using an AnimTree) */
var() array<float> BlendToTimes;
replication
{
if (bNetDirty)
AnimationIndex, RigIndex, EndGameState;
if (bNetInitial)
MinigameRig;
}
simulated event ReplicatedEvent(name VarName)
{
super.ReplicatedEvent(VarName);
if (VarName == 'EndGameState')
{
if (EndGameState >= 0)
{
FinalizeGame();
}
}
else if (VarName == 'AnimationIndex')
{
if (!bLocalStarted && AnimationIndex >= 0 && RigIndex >= 0)
{
StartupGame();
}
}
else if (VarName == 'RigIndex')
{
if (!bLocalStarted && AnimationIndex >= 0 && RigIndex >= 0)
{
StartupGame();
}
}
else if (VarName == 'MinigameRig')
{
MinigameRig.ParentGame = self;
}
}
/** Spawn a target per-bone and hide actor. They will be reset and unhidden on
* activation of the mini-game.
*/
event PostBeginPlay()
{
local name BoneName;
local Actor NewTarget;
local KFMGA_Target RiggedTarget;
super.PostBeginPlay();
MinigameRig.ParentGame = self;
if (MinigameRig != none && TargetArchetype != none)
{
foreach MinigameRig.TargetBones(BoneName)
{
NewTarget = spawn(TargetArchetype.class, self, , , , TargetArchetype);
if (NewTarget != none)
{
MinigameTargets.AddItem(NewTarget);
NewTarget.SetBase(MinigameRig, , MinigameRig.SkeletalMeshComponent, BoneName);
RiggedTarget = KFMGA_Target(NewTarget);
if (RiggedTarget != none)
{
RiggedTarget.SetInactive();
RiggedTarget.SpawnerOwner = self;
RiggedTarget.SpawnerOwnerIndex = MinigameTargets.Length - 1;
}
}
}
}
}
simulated function Reset()
{
local int i;
super.Reset();
SwitchAnim(EAS_Closed);
if (!MinigameRig.SkeletalMeshComponent.Animations.IsA('AnimTree'))
{
MinigameRig.SkeletalMeshComponent.StopAnim();
}
//Enable server optimization for the minigame rig while it's not running
MinigameRig.SetTickIsDisabled(true);
bLocalStarted = false;
for (i = 0; i < MinigameTargets.Length; ++i)
{
MinigameTargets[i].Reset();
}
}
//Activate target game. All authority stuff should be done here. Generic stuff should be done in StartupGame
function Activated(KFTrigger_MinigameButton ActivationSource)
{
if (RigIndex >= 0 && MinigameRig != none && (MinigameRig.MinigameAnimData.Length > 0 || MinigameRig.SkeletalMeshComponent.AnimTreeTemplate != none) && MinigameTargets.Length == MinigameRig.TargetBones.Length)
{
super.Activated(ActivationSource);
TargetsRemaining = MinigameTargets.Length;
AnimationIndex = MinigameRig.MinigameAnimData[RigIndex].MinigameAnimations.Length > 0 ? Rand(MinigameRig.MinigameAnimData[RigIndex].MinigameAnimations.Length) : -1;
StartupGame();
EndGameState = -1;
}
}
/** Handles animation functionality for a rigged target game. If the base is an animtree, sets a blend list.
* If the rig uses straight animations, calls a playanim.
*/
simulated function SwitchAnim(eAnimationState SelectedState)
{
local name AnimName;
local bool bLoop;
local AnimNodeBlendList BlendList;
//Set blend node in the animtree
if (MinigameRig.SkeletalMeshComponent.Animations.IsA('AnimTree'))
{
BlendList = AnimNodeBlendList(MinigameRig.SkeletalMeshComponent.FindAnimNode('StateBlend'));
if (BlendList != none)
{
BlendList.SetActiveChild(SelectedState, BlendToTimes[SelectedState]);
}
}
//Play animations directly
else
{
bLoop = false;
switch(SelectedState)
{
case EAS_Closed:
AnimName = MinigameRig.IdleClosedAnim;
bLoop = true;
break;
case EAS_Open:
AnimName = MinigameRig.GameStartAnim;
break;
case EAS_Active:
AnimName = MinigameRig.MinigameAnimData[RigIndex].MinigameAnimations[AnimationIndex];
bLoop = true;
break;
case EAS_Idle:
AnimName = MinigameRig.IdleOpenAnim;
break;
case EAS_Close:
AnimName = MinigameRig.GameEndAnim;
break;
}
if (AnimName != 'none')
{
MinigameRig.SkeletalMeshComponent.PlayAnim(AnimName, , bLoop);
}
}
}
simulated function StartupGame()
{
local float StartupTime;
//Disable server optimization for the minigame rig
MinigameRig.SetTickIsDisabled(false);
StartupTime = MinigameRig.SkeletalMeshComponent.GetAnimLength(MinigameRig.GameStartAnim);
if (MinigameRig.SkeletalMeshComponent.AnimTreeTemplate != none && !MinigameRig.SkeletalMeshComponent.Animations.IsA('AnimTree'))
{
MinigameRig.SkeletalMeshComponent.SetAnimTreeTemplate(MinigameRig.SkeletalMeshComponent.AnimTreeTemplate);
}
if (StartupTime > 0)
{
SetTimer(StartupTime, false, 'DelayedStart');
if (WorldInfo.NetMode != NM_DedicatedServer)
{
SwitchAnim(EAS_Open);
}
}
else
{
DelayedStart();
}
EndGameState = -1;
bLocalStarted = true;
}
simulated function DelayedStart()
{
local int i;
SwitchAnim(EAS_Active);
//For each of the targets, reset and set the base to a specified bone. Gives us clean a target per-bone
// without the need to spin up a new actor per-bone every time we start the minigame.
for (i = 0; i < MinigameTargets.Length; ++i)
{
MinigameTargets[i].SetBase(MinigameRig, , MinigameRig.SkeletalMeshComponent, MinigameRig.TargetBones[i]);
MinigameTargets[i].Reset();
if (KFMGA_Target(MinigameTargets[i]) != none)
{
KFMGA_Target(MinigameTargets[i]).AttachBoneName = MinigameRig.TargetBones[i];
KFMGA_Target(MinigameTargets[i]).SetActive();
}
}
}
/** Called by target when it's been hit by a valid damage source */
function TargetHit(Actor Target, Controller HitInstigator)
{
if (bGameRunning && MinigameTargets.Find(Target) != INDEX_NONE)
{
TargetsRemaining--;
if (HitInstigator != none && KillerControllers.Find(HitInstigator) == INDEX_NONE)
{
KillerControllers.AddItem(HitInstigator);
}
if (TargetsRemaining <= 0)
{
MinigameComplete(true);
}
}
}
/** Called on clients to fixup the object's base after first replication */
simulated function UpdateBase(KFMGA_Target Target)
{
Target.SetBase(MinigameRig, , MinigameRig.SkeletalMeshComponent, MinigameRig.TargetBones[Target.SpawnerOwnerIndex]);
}
/** Called on rigged games to check whether a target at a specific bone can be played */
simulated event bool CanPlayAkEvent(Actor inOwner, name BoneName)
{
local KFMGA_Target Target;
local int i;
for (i = 0; i < MinigameTargets.Length; ++i)
{
Target = KFMGA_Target(MinigameTargets[i]);
if (Target != none && Target.AttachBoneName == BoneName)
{
return Target.IsAlive();
}
}
return false;
}
function MinigameComplete(bool bVictory)
{
EndGameState = bVictory ? 0 : 1;
super.MinigameComplete(bVictory);
}
/** Do any cleanup that happens for either victory or defeat */
simulated function FinalizeGame()
{
local Actor Target;
super.FinalizeGame();
AnimationIndex = -1;
foreach MinigameTargets(Target)
{
if (KFMGA_Target(Target) != none)
{
KFMGA_Target(Target).SetInactive();
}
}
HandleDelayedShutdown();
}
simulated function HandleDelayedShutdown()
{
local float DelayTime;
switch (EndGameState)
{
case 0: //victory
DelayTime = MinigameRig.PostVictoryIdleTime;
break;
case 1: //defeat
DelayTime = MinigameRig.PostDefeatIdleTime;
break;
}
if (DelayTime > 0)
{
SetTimer(DelayTime, false, 'Finalize');
if (WorldInfo.NetMode != NM_DedicatedServer)
{
SwitchAnim(EAS_Idle);
}
}
else
{
Finalize();
}
}
simulated function Finalize()
{
local int i;
local float ResetTime;
for (i = 0; i < MinigameTargets.Length; ++i)
{
if (KFMGA_Target(MinigameTargets[i]) != none)
{
KFMGA_Target(MinigameTargets[i]).Finalize();
}
}
ResetTime = MinigameRig.SkeletalMeshComponent.GetAnimLength(MinigameRig.GameEndAnim);
if (ResetTime > 0.f)
{
SetTimer(ResetTime, false, 'Reset');
}
else
{
Reset();
}
if (WorldInfo.NetMode != NM_DedicatedServer)
{
SwitchAnim(EAS_Close);
}
}
defaultproperties
{
AnimationIndex = -1
BlendToTimes=(0.f,0.f,0.f,0.5f,0.f)
}