375 lines
11 KiB
Ucode
375 lines
11 KiB
Ucode
|
//=============================================================================
|
||
|
// KFWeapAttach_DualBase
|
||
|
//=============================================================================
|
||
|
// Attachment with a second set of meshes/FX for dual weapons
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
||
|
//=============================================================================
|
||
|
class KFWeapAttach_DualBase extends KFWeaponAttachment;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* Left-hand weapon components
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Left hand skeletal mesh. If none is specified, the right hand skeletal mesh will be duplicated. */
|
||
|
var() const SkeletalMesh LeftHandSkelMesh;
|
||
|
/** Anim set to use for 3rd person weapon anims */
|
||
|
var() AnimSet LeftWeaponAnimSet;
|
||
|
var protected transient SkeletalMeshComponent LeftWeapMesh;
|
||
|
var protected transient KFMuzzleFlash LeftMuzzleFlash;
|
||
|
/** A reference to the left muzzle flash template (to specify left shell eject socket) */
|
||
|
var() protected const KFMuzzleFlash LeftMuzzleFlashTemplate;
|
||
|
var protected transient AnimNodeSequence LeftWeapAnimNode;
|
||
|
|
||
|
/** Toggled when switching from right to left hand weapons */
|
||
|
var transient bool bPlayFXOnSecondWeapon;
|
||
|
/** Last world time bPlayFXOnSecondWeapon was set */
|
||
|
var transient float LastChooseWeaponTime;
|
||
|
|
||
|
/** Additive shoot anims */
|
||
|
const ShootLeftAnim = 'ADD_Shoot_LW';
|
||
|
const ShootRightAnim = 'ADD_Shoot_RW';
|
||
|
const CrouchShootLeftAnim = 'ADD_CH_Shoot_LW';
|
||
|
const CrouchShootRightAnim = 'ADD_CH_Shoot_RW';
|
||
|
const IronShootLeftAnim = 'ADD_Iron_Shoot_LW';
|
||
|
const IronShootRightAnim = 'ADD_Iron_Shoot_RW';
|
||
|
|
||
|
/** Weapon shoots */
|
||
|
// @todo: these should just be "Shoot", but the animsets for each gun have both anims
|
||
|
const WeaponFireAnim_R = 'Shoot_RW';
|
||
|
const WeaponFireAnim_L = 'Shoot_LW';
|
||
|
const WeaponAltFireAnim_R = 'Shoot_RW';
|
||
|
const WeaponAltFireAnim_L = 'Shoot_LW';
|
||
|
|
||
|
const LeftWeaponSocket = 'LW_Weapon';
|
||
|
|
||
|
event PreBeginPlay()
|
||
|
{
|
||
|
// set up left hand skel mesh just like we do the right in super
|
||
|
if ( LeftWeapMesh != None && !bWeapMeshIsPawnMesh )
|
||
|
{
|
||
|
if ( LeftWeaponAnimSet != None )
|
||
|
{
|
||
|
LeftWeapMesh.AnimSets[0] = LeftWeaponAnimSet;
|
||
|
}
|
||
|
else if( WeaponAnimSet != None )
|
||
|
{
|
||
|
// default to right weapon anim set
|
||
|
LeftWeapMesh.AnimSets[0] = WeaponAnimSet;
|
||
|
}
|
||
|
|
||
|
if ( LeftHandSkelMesh != none )
|
||
|
{
|
||
|
// set the skeletal mesh from our archetype to the WeaponAttachment
|
||
|
LeftWeapMesh.SkeletalMesh = LeftHandSkelMesh;
|
||
|
}
|
||
|
|
||
|
LeftWeapAnimNode = AnimNodeSequence(LeftWeapMesh.Animations);
|
||
|
}
|
||
|
|
||
|
super.PreBeginPlay();
|
||
|
}
|
||
|
|
||
|
/** Weapon Mesh Attachment */
|
||
|
event ChangeVisibility( bool bIsVisible )
|
||
|
{
|
||
|
super.ChangeVisibility( bIsVisible );
|
||
|
|
||
|
if( LeftWeapMesh != none && !bWeapMeshIsPawnMesh )
|
||
|
{
|
||
|
LeftWeapMesh.SetHidden( !bIsVisible );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* Attach / Detach
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Added second weapon */
|
||
|
simulated function AttachTo(KFPawn P)
|
||
|
{
|
||
|
Super.AttachTo(P);
|
||
|
|
||
|
if ( bWeapMeshIsPawnMesh )
|
||
|
{
|
||
|
LeftWeapMesh = P.Mesh;
|
||
|
}
|
||
|
else if ( LeftWeapMesh != None )
|
||
|
{
|
||
|
if( LeftHandSkelMesh == none )
|
||
|
{
|
||
|
// Create a duplicate of the right-hand weapn mesh
|
||
|
LeftWeapMesh = new(self) Class'SkeletalMeshComponent'(WeapMesh);
|
||
|
}
|
||
|
|
||
|
// Attach Weapon mesh to player skel mesh
|
||
|
LeftWeapMesh.SetShadowParent(P.Mesh);
|
||
|
P.Mesh.AttachComponent(LeftWeapMesh, LeftWeaponSocket);
|
||
|
}
|
||
|
|
||
|
//Do a first chance weapon skin switch (EX: changed weapon w/o ID changing by throwing a dualie)
|
||
|
if (KFPawn_Human(P) != None && KFPawn_Human(P).WeaponSkinItemId > 0)
|
||
|
{
|
||
|
SetWeaponSkin(KFPawn_Human(P).WeaponSkinItemId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Added second weapon */
|
||
|
simulated function DetachFrom(KFPawn P)
|
||
|
{
|
||
|
Super.DetachFrom(P);
|
||
|
|
||
|
// first, detach FX
|
||
|
if ( LeftMuzzleFlash != None )
|
||
|
{
|
||
|
LeftMuzzleFlash.DetachMuzzleFlash(LeftWeapMesh);
|
||
|
}
|
||
|
|
||
|
// detach weapon mesh from player skelmesh
|
||
|
if ( bWeapMeshIsPawnMesh )
|
||
|
{
|
||
|
LeftWeapMesh = None;
|
||
|
}
|
||
|
else if ( LeftWeapMesh != None )
|
||
|
{
|
||
|
LeftWeapMesh.SetShadowParent(None);
|
||
|
P.Mesh.DetachComponent(LeftWeapMesh);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Added second weapon */
|
||
|
simulated function AttachMuzzleFlash()
|
||
|
{
|
||
|
Super.AttachMuzzleFlash();
|
||
|
|
||
|
if ( WeapMesh != none && LeftMuzzleFlash == None )
|
||
|
{
|
||
|
LeftMuzzleFlash = new(self) Class'KFMuzzleFlash'(LeftMuzzleFlashTemplate);
|
||
|
LeftMuzzleFlash.AttachMuzzleFlash(LeftWeapMesh);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Assign weapon skin to 3rd person mesh
|
||
|
*/
|
||
|
event SetWeaponSkin(int ItemId, optional bool bFinishedLoading = false)
|
||
|
{
|
||
|
local array<MaterialInterface> SkinMICs;
|
||
|
|
||
|
if ( WeapMesh != none && LeftWeapMesh != none && ItemId > 0 && WorldInfo.NetMode != NM_DedicatedServer && !bWaitingForWeaponSkinLoad)
|
||
|
{
|
||
|
if (!bFinishedLoading && StartLoadWeaponSkin(ItemId))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SkinMICs = class'KFWeaponSkinList'.static.GetWeaponSkin(ItemId, WST_ThirdPerson);
|
||
|
if ( SkinMICs.Length > 0 )
|
||
|
{
|
||
|
WeapMesh.SetMaterial(0, SkinMICs[0]);
|
||
|
LeftWeapMesh.SetMaterial(0, SkinMICs[0]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* Fire Effects
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Updates bPlayFXOnSecondWeapon each time weapon is fired */
|
||
|
function bool ChooseActiveWeapon(byte FlashCount)
|
||
|
{
|
||
|
// If this was already called in this frame do nothing. See GetProjectileStartLoc() for details.
|
||
|
if ( LastChooseWeaponTime == WorldInfo.TimeSeconds )
|
||
|
{
|
||
|
return bPlayFXOnSecondWeapon;
|
||
|
}
|
||
|
|
||
|
if ( FlashCount > 0 )
|
||
|
{
|
||
|
// If we have a flash count (e.g. projectile) use that to synchronize with 1st P
|
||
|
bPlayFXOnSecondWeapon = (FlashCount & 1) == 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Otherwise just toggle it
|
||
|
bPlayFXOnSecondWeapon = !bPlayFXOnSecondWeapon;
|
||
|
}
|
||
|
|
||
|
LastChooseWeaponTime = WorldInfo.TimeSeconds;
|
||
|
return bPlayFXOnSecondWeapon;
|
||
|
}
|
||
|
|
||
|
/** Added second weapon */
|
||
|
simulated function bool ThirdPersonFireEffects( vector HitLocation, KFPawn P, byte ThirdPersonAnimRateByte )
|
||
|
{
|
||
|
ChooseActiveWeapon(P.FlashCount);
|
||
|
return super.ThirdPersonFireEffects( HitLocation, P, ThirdPersonAnimRateByte );
|
||
|
}
|
||
|
|
||
|
/** Plays fire animation on weapon mesh.
|
||
|
* Overloaded to switch between right and left.
|
||
|
*/
|
||
|
simulated function PlayWeaponFireAnim()
|
||
|
{
|
||
|
local float Duration;
|
||
|
|
||
|
if( Instigator.FiringMode == 1 && WeaponAltFireAnim != 'None' )
|
||
|
{
|
||
|
if( bPlayFXOnSecondWeapon )
|
||
|
{
|
||
|
Duration = LeftWeapMesh.GetAnimLength( WeaponAltFireAnim_L );
|
||
|
LeftWeapMesh.PlayAnim( WeaponAltFireAnim_L, Duration / ThirdPersonAnimRate,, true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Duration = WeapMesh.GetAnimLength( WeaponAltFireAnim_R );
|
||
|
WeapMesh.PlayAnim( WeaponAltFireAnim_R, Duration / ThirdPersonAnimRate,, true );
|
||
|
}
|
||
|
}
|
||
|
else if( WeaponFireAnim != 'None' )
|
||
|
{
|
||
|
if( bPlayFXOnSecondWeapon )
|
||
|
{
|
||
|
Duration = LeftWeapMesh.GetAnimLength( WeaponFireAnim_L );
|
||
|
LeftWeapMesh.PlayAnim( WeaponFireAnim_L, Duration / ThirdPersonAnimRate,, true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Duration = WeapMesh.GetAnimLength( WeaponFireAnim_R );
|
||
|
WeapMesh.PlayAnim( WeaponFireAnim_R, Duration / ThirdPersonAnimRate,, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Plays fire animation on pawn
|
||
|
* Overloaded to switch between right and left.
|
||
|
*/
|
||
|
simulated function PlayPawnFireAnim( KFPawn P, EAnimSlotStance AnimType )
|
||
|
{
|
||
|
if ( P.bIsCrouched )
|
||
|
{
|
||
|
P.PlayBodyAnim((bPlayFXOnSecondWeapon) ? CrouchShootLeftAnim : CrouchShootRightAnim, EAS_FullBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
|
||
|
}
|
||
|
else if ( P.bIsWalking )
|
||
|
{
|
||
|
P.PlayBodyAnim((bPlayFXOnSecondWeapon) ? IronShootLeftAnim : IronShootRightAnim, EAS_FullBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
P.PlayBodyAnim((bPlayFXOnSecondWeapon) ? ShootLeftAnim : ShootRightAnim, EAS_FullBody, ThirdPersonAnimRate, ShootBlendInTime, ShootBlendOutTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Added second weapon */
|
||
|
simulated function CauseMuzzleFlash(byte FiringMode)
|
||
|
{
|
||
|
// Can use the same weapon that was used by ThirdPersonFireEffects
|
||
|
if ( bPlayFXOnSecondWeapon )
|
||
|
{
|
||
|
if (LeftMuzzleFlash == None && MuzzleFlashTemplate != None)
|
||
|
{
|
||
|
AttachMuzzleFlash();
|
||
|
}
|
||
|
|
||
|
if (LeftMuzzleFlash != None )
|
||
|
{
|
||
|
LeftMuzzleFlash.CauseMuzzleFlash(FiringMode);
|
||
|
if ( LeftMuzzleFlash.bAutoActivateShellEject )
|
||
|
{
|
||
|
LeftMuzzleFlash.CauseShellEject();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Super.CauseMuzzleFlash(FiringMode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Added second weapon */
|
||
|
simulated function vector GetMuzzleLocation(optional byte MuzzleID)
|
||
|
{
|
||
|
local vector SocketLocation;
|
||
|
|
||
|
if ( MuzzleID >= 1 || bPlayFXOnSecondWeapon )
|
||
|
{
|
||
|
if (LeftMuzzleFlash == None && MuzzleFlashTemplate != None)
|
||
|
{
|
||
|
AttachMuzzleFlash();
|
||
|
}
|
||
|
|
||
|
if( LeftMuzzleFlash != none )
|
||
|
{
|
||
|
LeftWeapMesh.GetSocketWorldLocationAndRotation(LeftMuzzleFlash.GetSocketName(), SocketLocation);
|
||
|
return SocketLocation;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Super.GetMuzzleLocation(MuzzleID);
|
||
|
}
|
||
|
|
||
|
/** Overrides super to play anim on both meshes. Used for reloads at the moment (will this mess up other possible anims?) */
|
||
|
simulated function PlayWeaponMeshAnim(name AnimName, AnimNodeSlot SyncNode, bool bLoop)
|
||
|
{
|
||
|
local float Duration;
|
||
|
|
||
|
super.PlayWeaponMeshAnim( AnimName, SyncNode, bLoop );
|
||
|
|
||
|
Duration = LeftWeapMesh.GetAnimLength( AnimName );
|
||
|
LeftWeapMesh.PlayAnim( AnimName, Duration / ThirdPersonAnimRate, bLoop );
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Rendering/Lighting
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Set the lighting channels on all the appropriate weapon attachment mesh(es) */
|
||
|
simulated function SetMeshLightingChannels(LightingChannelContainer NewLightingChannels)
|
||
|
{
|
||
|
super.SetMeshLightingChannels( NewLightingChannels );
|
||
|
|
||
|
if( !bWeapMeshIsPawnMesh )
|
||
|
{
|
||
|
LeftWeapMesh.SetLightingChannels(NewLightingChannels);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Debug */
|
||
|
simulated function bool HasIndoorLighting()
|
||
|
{
|
||
|
return super.HasIndoorLighting() && LeftWeapMesh.LightingChannels.Indoor;
|
||
|
}
|
||
|
|
||
|
/** Debug */
|
||
|
simulated function bool HasOutdoorLighting()
|
||
|
{
|
||
|
return super.HasOutdoorLighting() && LeftWeapMesh.LightingChannels.Outdoor;
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
// Left Weapon SkeletalMesh (copy of right weapon in super)
|
||
|
Begin Object Class=SkeletalMeshComponent Name=SkeletalMeshComponent1
|
||
|
bOwnerNoSee=true
|
||
|
bOnlyOwnerSee=false
|
||
|
CollideActors=false
|
||
|
AlwaysLoadOnClient=true
|
||
|
AlwaysLoadOnServer=true
|
||
|
MaxDrawDistance=4000
|
||
|
bUpdateSkelWhenNotRendered=false
|
||
|
bIgnoreControllersWhenNotRendered=true
|
||
|
bOverrideAttachmentOwnerVisibility=true
|
||
|
bAcceptsDynamicDecals=FALSE
|
||
|
Animations=MeshSequenceA
|
||
|
CastShadow=true
|
||
|
bCastDynamicShadow=true
|
||
|
bPerBoneMotionBlur=true
|
||
|
bForceRefPose=1
|
||
|
// Default to outdoor. If indoor, this will be set when TWIndoorLightingVolume::Touch() event is received at spawn.
|
||
|
LightingChannels=(Outdoor=TRUE,bInitialized=TRUE)
|
||
|
End Object
|
||
|
LeftWeapMesh=SkeletalMeshComponent1
|
||
|
}
|