466 lines
14 KiB
Ucode
466 lines
14 KiB
Ucode
|
//=============================================================================
|
||
|
// KFMG_BloatDunk
|
||
|
//=============================================================================
|
||
|
// Extension of target game with specifics tied to bloat dunk. Uses a more
|
||
|
// complicated two rig setup for the wheels, each manually rotated, with
|
||
|
// independent attachment points on each. Every x seconds, picks a random
|
||
|
// light from each rig to enable to be shot.
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2016 Tripwire Interactive LLC
|
||
|
// - Dan Weiss
|
||
|
//=============================================================================
|
||
|
|
||
|
class KFMG_BloatDunk extends KFMG_TargetGame;
|
||
|
|
||
|
/** Archetype of target actor that will be spawned per-bone */
|
||
|
var() Actor TargetArchetype;
|
||
|
|
||
|
/** Two rigs used for alternately rotating wheels */
|
||
|
var() KFMGA_TargetGame OuterWheel;
|
||
|
var() KFMGA_TargetGame InnerWheel;
|
||
|
|
||
|
/** Dunk tank rig for handling curtain/dunk board */
|
||
|
var() SkeletalMeshActor DunkTankRig;
|
||
|
|
||
|
/** Bloat being dunked */
|
||
|
var() SkeletalMeshActor BloatRig;
|
||
|
|
||
|
/** How often to activate a new set of targets */
|
||
|
var() float TargetActivationDelay;
|
||
|
|
||
|
/** How fast to begin rotation */
|
||
|
var() float StartingRotationRate;
|
||
|
|
||
|
/** How much to increase rotation per-target hit */
|
||
|
var() float PerHitRotationIncrease;
|
||
|
|
||
|
/** How many targets to activate per attempt */
|
||
|
var() int NumActiveTargets;
|
||
|
|
||
|
/** Current rotation rate */
|
||
|
var float CurrentRotationRate;
|
||
|
|
||
|
/** How many targets have not been hit */
|
||
|
var int TargetsRemaining;
|
||
|
|
||
|
enum eBloatGameState
|
||
|
{
|
||
|
BGS_Off,
|
||
|
BGS_On,
|
||
|
BGS_Victory,
|
||
|
BGS_Defeat,
|
||
|
};
|
||
|
|
||
|
/** What state the game is currently in */
|
||
|
var repnotify eBloatGameState BloatGameState;
|
||
|
|
||
|
//-------------------
|
||
|
// Animation
|
||
|
|
||
|
/** Standard idle while game is off */
|
||
|
var() name IdleClosedAnim;
|
||
|
|
||
|
/** Standard idle while game is being played */
|
||
|
var() name IdleOpenAnim;
|
||
|
|
||
|
/** Animation played on game start */
|
||
|
var() name OpenAnim;
|
||
|
|
||
|
/** Played on victory to dunk the bloat */
|
||
|
var() name DunkVictoryAnim;
|
||
|
|
||
|
/** Victory close animation */
|
||
|
var() name VictoryCloseAnim;
|
||
|
|
||
|
/** Defeat close animation */
|
||
|
var() name DefeatCloseAnim;
|
||
|
|
||
|
replication
|
||
|
{
|
||
|
if (bNetInitial)
|
||
|
OuterWheel, InnerWheel, DunkTankRig, BloatRig;
|
||
|
|
||
|
if (bNetDirty)
|
||
|
CurrentRotationRate, BloatGameState;
|
||
|
}
|
||
|
|
||
|
simulated event ReplicatedEvent(name VarName)
|
||
|
{
|
||
|
super.ReplicatedEvent(VarName);
|
||
|
|
||
|
if (VarName == 'bGameRunning')
|
||
|
{
|
||
|
if (bGameRunning)
|
||
|
{
|
||
|
HandleDelayedStartup();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (VarName == 'bGameRunning' || VarName == 'BloatGameState')
|
||
|
{
|
||
|
if (!bGameRunning && BloatGameState > BGS_On)
|
||
|
{
|
||
|
FinalizeGame();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated event Tick(float DeltaTime)
|
||
|
{
|
||
|
local Rotator NewRotation;
|
||
|
super.Tick(DeltaTime);
|
||
|
|
||
|
if (bGameRunning)
|
||
|
{
|
||
|
NewRotation = OuterWheel.Rotation;
|
||
|
NewRotation.Pitch = (NewRotation.Pitch + (CurrentRotationRate * DeltaTime)) % 65536;
|
||
|
OuterWheel.SetRotation(NewRotation);
|
||
|
|
||
|
NewRotation = InnerWheel.Rotation;
|
||
|
NewRotation.Pitch = (NewRotation.Pitch - (CurrentRotationRate * DeltaTime)) % 65536;
|
||
|
InnerWheel.SetRotation(NewRotation);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** 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;
|
||
|
super.PostBeginPlay();
|
||
|
|
||
|
if (InnerWheel != none && OuterWheel != none && TargetArchetype != none)
|
||
|
{
|
||
|
foreach InnerWheel.TargetBones(BoneName)
|
||
|
{
|
||
|
SpawnTarget(InnerWheel, BoneName);
|
||
|
}
|
||
|
|
||
|
foreach OuterWheel.TargetBones(BoneName)
|
||
|
{
|
||
|
SpawnTarget(OuterWheel, BoneName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
function SpawnTarget(KFMGA_TargetGame Wheel, name BoneName)
|
||
|
{
|
||
|
local Actor NewTarget;
|
||
|
local KFMGA_Target RiggedTarget;
|
||
|
|
||
|
NewTarget = spawn(TargetArchetype.class, self, , , , TargetArchetype);
|
||
|
if (NewTarget != none)
|
||
|
{
|
||
|
MinigameTargets.AddItem(NewTarget);
|
||
|
NewTarget.SetBase(Wheel, , Wheel.SkeletalMeshComponent, BoneName);
|
||
|
|
||
|
RiggedTarget = KFMGA_Target(NewTarget);
|
||
|
if (RiggedTarget != none)
|
||
|
{
|
||
|
RiggedTarget.SetInactive();
|
||
|
RiggedTarget.SpawnerOwner = self;
|
||
|
RiggedTarget.SpawnerOwnerIndex = MinigameTargets.Length - 1;
|
||
|
UpdateBase(RiggedTarget);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Activate target game. All authority stuff should be done here. Generic stuff should be done in StartupGame
|
||
|
function Activated(KFTrigger_MinigameButton ActivationSource)
|
||
|
{
|
||
|
if (MinigameTargets.Length == (OuterWheel.TargetBones.Length + InnerWheel.TargetBones.Length))
|
||
|
{
|
||
|
super.Activated(ActivationSource);
|
||
|
|
||
|
BloatGameState = BGS_On;
|
||
|
TargetsRemaining = MinigameTargets.Length;
|
||
|
HandleDelayedStartup();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function Reset()
|
||
|
{
|
||
|
super.Reset();
|
||
|
CurrentRotationRate = 0;
|
||
|
BloatRig.SkeletalMeshComponent.StopAnim();
|
||
|
}
|
||
|
|
||
|
simulated function HandleDelayedStartup()
|
||
|
{
|
||
|
local float AnimLength;
|
||
|
local int i;
|
||
|
local Vector NewLocation;
|
||
|
local Rotator NewRotation;
|
||
|
|
||
|
//Disable server optimization for the minigame rig
|
||
|
InnerWheel.SetTickIsDisabled(false);
|
||
|
|
||
|
//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 < InnerWheel.TargetBones.Length; ++i)
|
||
|
{
|
||
|
MinigameTargets[i].Reset();
|
||
|
MinigameTargets[i].SetBase(InnerWheel, , InnerWheel.SkeletalMeshComponent, InnerWheel.TargetBones[i]);
|
||
|
}
|
||
|
|
||
|
//Disable server optimization for the minigame rig
|
||
|
OuterWheel.SetTickIsDisabled(false);
|
||
|
|
||
|
//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 < OuterWheel.TargetBones.Length; ++i)
|
||
|
{
|
||
|
MinigameTargets[InnerWheel.TargetBones.Length + i].Reset();
|
||
|
MinigameTargets[InnerWheel.TargetBones.Length + i].SetBase(OuterWheel, , OuterWheel.SkeletalMeshComponent, OuterWheel.TargetBones[i]);
|
||
|
}
|
||
|
|
||
|
if (WorldInfo.NetMode != NM_DedicatedServer && BloatRig != none)
|
||
|
{
|
||
|
if (DunkTankRig.SkeletalMeshComponent.GetSocketWorldLocationAndRotation('BloatAttach', NewLocation, NewRotation))
|
||
|
{
|
||
|
BloatRig.SetLocation(NewLocation);
|
||
|
BloatRig.SetRotation(NewRotation);
|
||
|
}
|
||
|
BloatRig.SetPhysics(PHYS_None);
|
||
|
BloatRig.SetBase(DunkTankRig, , DunkTankRig.SkeletalMeshComponent, 'Root');
|
||
|
BloatRig.SkeletalMeshComponent.PlayAnim('BloatDunk_Idle', , true);
|
||
|
}
|
||
|
|
||
|
//If we have a rig and animation, delay activation of the game startup until after the open animation has been kicked
|
||
|
// Otherwise, simply skip to start
|
||
|
if (DunkTankRig != none)
|
||
|
{
|
||
|
AnimLength = DunkTankRig.SkeletalMeshComponent.GetAnimLength(OpenAnim);
|
||
|
if (AnimLength > 0.f)
|
||
|
{
|
||
|
SetTimer(AnimLength, false, 'StartupGame');
|
||
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
||
|
{
|
||
|
DunkTankRig.SkeletalMeshComponent.PlayAnim(OpenAnim);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
StartupGame();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
StartupGame();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function StartupGame()
|
||
|
{
|
||
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
||
|
{
|
||
|
DunkTankRig.SkeletalMeshComponent.PlayAnim(IdleOpenAnim, , true);
|
||
|
}
|
||
|
|
||
|
if (Role == ROLE_Authority)
|
||
|
{
|
||
|
CurrentRotationRate = StartingRotationRate;
|
||
|
ActivateTargets();
|
||
|
SetTimer(TargetActivationDelay, true, 'ActivateTargets');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Find a random target per wheel and activate it. Deactivate */
|
||
|
function ActivateTargets()
|
||
|
{
|
||
|
local int i, Activated;
|
||
|
local array<Actor> RandLookupList;
|
||
|
|
||
|
//Deactivate old targets
|
||
|
for (i = 0; i < MinigameTargets.Length; ++i)
|
||
|
{
|
||
|
if (KFMGA_Target(MinigameTargets[i]) != none)
|
||
|
{
|
||
|
KFMGA_Target(MinigameTargets[i]).SetInactive();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Get two random targets for now
|
||
|
RandLookupList = MinigameTargets;
|
||
|
while (RandLookupList.Length > 0 && Activated < NumActiveTargets)
|
||
|
{
|
||
|
i = Rand(RandLookupList.Length);
|
||
|
if (KFMGA_Target(RandLookupList[i]).IsAlive())
|
||
|
{
|
||
|
Activated++;
|
||
|
KFMGA_Target(RandLookupList[i]).SetActive();
|
||
|
}
|
||
|
RandLookupList.Remove(i, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** 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--;
|
||
|
CurrentRotationRate += PerHitRotationIncrease;
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
if (Target.SpawnerOwnerIndex < InnerWheel.TargetBones.Length)
|
||
|
{
|
||
|
Target.SetBase(InnerWheel, , InnerWheel.SkeletalMeshComponent, InnerWheel.TargetBones[Target.SpawnerOwnerIndex]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Target.SetBase(OuterWheel, , OuterWheel.SkeletalMeshComponent, OuterWheel.TargetBones[Target.SpawnerOwnerIndex - InnerWheel.TargetBones.Length]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function MinigameComplete(bool bVictory)
|
||
|
{
|
||
|
BloatGameState = bVictory ? BGS_Victory : BGS_Defeat;
|
||
|
super.MinigameComplete(bVictory);
|
||
|
}
|
||
|
|
||
|
/** Do any cleanup that happens for either victory or defeat */
|
||
|
simulated function FinalizeGame()
|
||
|
{
|
||
|
local Actor Target;
|
||
|
|
||
|
super.FinalizeGame();
|
||
|
|
||
|
ClearTimer('ActivateTargets');
|
||
|
|
||
|
foreach MinigameTargets(Target)
|
||
|
{
|
||
|
if (KFMGA_Target(Target) != none)
|
||
|
{
|
||
|
KFMGA_Target(Target).SetInactive();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Enable server optimization for the minigame rig while it's not running
|
||
|
InnerWheel.SetTickIsDisabled(false);
|
||
|
OuterWheel.SetTickIsDisabled(false);
|
||
|
|
||
|
HandleDelayedShutdown();
|
||
|
}
|
||
|
|
||
|
/** Animation handling for delayed shutdown */
|
||
|
simulated function HandleDelayedShutdown()
|
||
|
{
|
||
|
local float DelayTime;
|
||
|
local name DelayedShutdownAnim;
|
||
|
|
||
|
switch (BloatGameState)
|
||
|
{
|
||
|
case BGS_Victory:
|
||
|
DelayTime = DunkTankRig.SkeletalMeshComponent.GetAnimLength(DunkVictoryAnim);
|
||
|
DelayedShutdownAnim = DunkVictoryAnim;
|
||
|
SetTimer(DelayTime * 2.f, false, 'FinalizeVictory');
|
||
|
break;
|
||
|
case BGS_Defeat:
|
||
|
DelayTime = DunkTankRig.SkeletalMeshComponent.GetAnimLength(DefeatCloseAnim);
|
||
|
DelayedShutdownAnim = DefeatCloseAnim;
|
||
|
SetTimer(DelayTime, false, 'Reset');
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
||
|
{
|
||
|
DunkTankRig.SkeletalMeshComponent.PlayAnim(DelayedShutdownAnim);
|
||
|
if (BloatGameState == BGS_Victory)
|
||
|
{
|
||
|
SetBloatRagdoll();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function SetBloatRagdoll()
|
||
|
{
|
||
|
if (BloatRig == none)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BloatRig.SkeletalMeshComponent.StopAnim();
|
||
|
|
||
|
//PrepareRagdoll
|
||
|
BloatRig.SkeletalMeshComponent.bUpdateSkelWhenNotRendered = true;
|
||
|
if (BloatRig.SkeletalMeshComponent.bNotUpdatingKinematicDueToDistance)
|
||
|
{
|
||
|
BloatRig.SkeletalMeshComponent.ForceSkelUpdate();
|
||
|
BloatRig.SkeletalMeshComponent.UpdateRBBonesFromSpaceBases(TRUE, TRUE);
|
||
|
BloatRig.SkeletalMeshComponent.SetBlockRigidBody(TRUE);
|
||
|
}
|
||
|
|
||
|
//InitRagdoll
|
||
|
BloatRig.SkeletalMeshComponent.PhysicsWeight = 1.0f;
|
||
|
BloatRig.SkeletalMeshComponent.SetHasPhysicsAssetInstance(true);
|
||
|
BloatRig.SetPhysics(PHYS_RigidBody);
|
||
|
BloatRig.SkeletalMeshComponent.PhysicsAssetInstance.SetAllBodiesFixed(false);
|
||
|
BloatRig.SkeletalMeshComponent.WakeRigidBody();
|
||
|
|
||
|
//PlayRagdollDeath
|
||
|
BloatRig.SkeletalMeshComponent.SetActorCollision(TRUE, FALSE);
|
||
|
BloatRig.SkeletalMeshComponent.bUpdateJointsFromAnimation = FALSE;
|
||
|
BloatRig.SkeletalMeshComponent.SetRBChannel(RBCC_KnockedDownPawn);
|
||
|
BloatRig.SkeletalMeshComponent.SetRBCollidesWithChannel(RBCC_KnockedDownPawn, TRUE);
|
||
|
BloatRig.SkeletalMeshComponent.SetRBCollidesWithChannel(RBCC_Pawn, FALSE);
|
||
|
BloatRig.SkeletalMeshComponent.SetRBCollidesWithChannel(RBCC_DeadPawn, FALSE);
|
||
|
}
|
||
|
|
||
|
simulated function FinalizeVictory()
|
||
|
{
|
||
|
local float ResetTime;
|
||
|
|
||
|
ResetTime = DunkTankRig.SkeletalMeshComponent.GetAnimLength(VictoryCloseAnim);
|
||
|
if (ResetTime > 0.f)
|
||
|
{
|
||
|
SetTimer(ResetTime, false, 'Reset');
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Reset();
|
||
|
}
|
||
|
|
||
|
if (WorldInfo.NetMode != NM_DedicatedServer)
|
||
|
{
|
||
|
DunkTankRig.SkeletalMeshComponent.PlayAnim(VictoryCloseAnim);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
TargetActivationDelay = 2.f
|
||
|
|
||
|
StartingRotationRate = 500
|
||
|
PerHitRotationIncrease = 100
|
||
|
NumActiveTargets = 2
|
||
|
|
||
|
BloatGameState = BGS_Off
|
||
|
|
||
|
//-------------------
|
||
|
// Animation
|
||
|
IdleClosedAnim = Idle_Close
|
||
|
IdleOpenAnim = Idle_Open
|
||
|
OpenAnim = Open
|
||
|
DunkVictoryAnim = Dunk
|
||
|
VictoryCloseAnim = Close_Win
|
||
|
DefeatCloseAnim = Close_lost
|
||
|
}
|