350 lines
10 KiB
Ucode
350 lines
10 KiB
Ucode
//=============================================================================
|
|
// KFPawn_Customization
|
|
//=============================================================================
|
|
// Customization pawn, allows previewing characters before play
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
|
|
class KFPawn_Customization extends KFPawn_Human
|
|
native(Pawn);
|
|
|
|
struct native sReplicatedMovementData
|
|
{
|
|
var vector NewLocation;
|
|
var rotator NewRotation;
|
|
};
|
|
|
|
var bool bPlayingEmote;
|
|
|
|
/** Post-spawn replicated location (we skip actor property replication, this allows us to update after spawn) */
|
|
var repnotify sReplicatedMovementData ReplicatedMovementData;
|
|
|
|
/** Post-spawn replicated hidden status, controlled by the server */
|
|
var repnotify bool bServerHidden;
|
|
|
|
/** Local hidden variable, OR'd with bServerHidden */
|
|
var bool bLocalHidden;
|
|
|
|
var AnimSet MaleCustomizationAnimSet;
|
|
var AnimSet FemaleCustomizationAnimSet;
|
|
|
|
var bool bUsingCustomizationPoint; // This is true if we have been placed on a KFCustomizationPoint
|
|
|
|
cpptext
|
|
{
|
|
virtual UBOOL IsAliveAndWell() const;
|
|
virtual void PostNetReceiveLocation();
|
|
UBOOL SetDesiredRotation( FRotator TargetDesiredRotation,UBOOL InLockDesiredRotation=FALSE,UBOOL InUnlockWhenReached=FALSE,FLOAT InterpolationTime=-1.000000,UBOOL bResetRotationRate=TRUE );
|
|
}
|
|
|
|
replication
|
|
{
|
|
if( bNetInitial || bNetDirty )
|
|
ReplicatedMovementData, bServerHidden;
|
|
}
|
|
|
|
/** Updates visiblity of surrounding customization pawns so that none overlap */
|
|
native function UpdateCustomizationPawnVisibility();
|
|
|
|
/** Updates location and rotation on client */
|
|
simulated event ReplicatedEvent( name VarName )
|
|
{
|
|
if( VarName == nameOf(ReplicatedMovementData) )
|
|
{
|
|
OnMovementDataUpdated();
|
|
}
|
|
else if( VarName == nameOf(bServerHidden) )
|
|
{
|
|
SetHidden( bServerHidden || bLocalHidden );
|
|
UpdateCustomizationPawnVisibility();
|
|
}
|
|
else
|
|
{
|
|
super.ReplicatedEvent( VarName );
|
|
}
|
|
}
|
|
|
|
/** Sets our post-spawn location and rotation */
|
|
function SetUpdatedMovementData( vector NewLoc, rotator NewRot )
|
|
{
|
|
ReplicatedMovementData.NewLocation = NewLoc;
|
|
ReplicatedMovementData.NewRotation = NewRot;
|
|
|
|
// Set directly on listen server
|
|
OnMovementDataUpdated();
|
|
|
|
// Update on network immediately
|
|
bForceNetUpdate = true;
|
|
}
|
|
|
|
/** Sets our updated movement data on client/listen server */
|
|
simulated function OnMovementDataUpdated()
|
|
{
|
|
SetLocation( ReplicatedMovementData.NewLocation );
|
|
SetRotation( ReplicatedMovementData.NewRotation );
|
|
|
|
// Update visibility on listen server/clients
|
|
if( WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
UpdateCustomizationPawnVisibility();
|
|
}
|
|
}
|
|
|
|
/** Sets the server-controlled hidden status (we only care about bServerHidden=true) */
|
|
function SetServerHidden( bool bNewHidden )
|
|
{
|
|
bServerHidden = bNewHidden;
|
|
|
|
// Set directly on listen server
|
|
if( WorldInfo.NetMode != NM_DedicatedServer )
|
|
{
|
|
// Only hide if the server tells us to
|
|
SetHidden( bServerHidden || bLocalHidden );
|
|
UpdateCustomizationPawnVisibility();
|
|
}
|
|
|
|
// Update on network immediately
|
|
bForceNetUpdate = true;
|
|
}
|
|
|
|
/** Overridden, we don't want anything to stomp on SetUpdatedMovementData() */
|
|
function ClientSetRotation( rotator NewRotation ) {}
|
|
simulated function FaceRotation(rotator NewRotation, float DeltaTime) {}
|
|
|
|
/*********************************************************************************************
|
|
Initialization
|
|
********************************************************************************************* */
|
|
/** Disable foot IK */
|
|
simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp)
|
|
{
|
|
Super.PostInitAnimTree(SkelComp);
|
|
|
|
// Disable foot IK so we have have straight legs for this character. When
|
|
// bAllowStretching is set the knees must be bent a little.
|
|
if ( IKFootLeft != None )
|
|
{
|
|
IKFootLeft.SetSkelControlActive(false);
|
|
}
|
|
if ( IKFootRight != None )
|
|
{
|
|
IKFootRight.SetSkelControlActive(false);
|
|
}
|
|
|
|
SetHeadScale(IntendedHeadScale, CurrentHeadScale);
|
|
}
|
|
|
|
/** Always use the customization anim set */
|
|
simulated function SetCharacterAnimationInfo()
|
|
{
|
|
local KFCharacterInfo_Human CharInfoHuman;
|
|
|
|
super.SetCharacterAnimationInfo();
|
|
|
|
// Character Animation
|
|
CharInfoHuman = KFCharacterInfo_Human( GetCharacterInfo() );
|
|
if( CharInfoHuman != none && CharInfoHuman.bIsFemale )
|
|
{
|
|
Mesh.AnimSets.AddItem(FemaleCustomizationAnimSet);
|
|
}
|
|
else
|
|
{
|
|
Mesh.AnimSets.AddItem(MaleCustomizationAnimSet);
|
|
}
|
|
|
|
Mesh.UpdateAnimations();
|
|
PlayRandomIdleAnimation(true);
|
|
}
|
|
|
|
function AttachWeaponByItemDefinition( int ItemDefinition )
|
|
{
|
|
local class<KFWeaponDefinition> WeaponDef;
|
|
local int ItemINdex;
|
|
local KFWeaponAttachment WeaponPreview;
|
|
|
|
//find weapon def
|
|
ItemIndex = class'KFWeaponSkinList'.default.Skins.Find('Id', ItemDefinition);
|
|
|
|
if(ItemIndex == INDEX_NONE)
|
|
{
|
|
`log("Could not find item" @ItemDefinition);
|
|
return;
|
|
}
|
|
|
|
WeaponDef = class'KFWeaponSkinList'.default.Skins[ItemIndex].WeaponDef;
|
|
|
|
if(WeaponDef == none)
|
|
{
|
|
`log("Weapon def NONE for : " @ItemDefinition);
|
|
return;
|
|
}
|
|
|
|
//load in and add object .
|
|
WeaponPreview = KFWeaponAttachment ( DynamicLoadObject( WeaponDef.default.AttachmentArchtypePath, class'KFWeaponAttachment' ) );
|
|
|
|
//attatch it to player
|
|
WeaponAttachmentTemplate = WeaponPreview;
|
|
|
|
WeaponAttachmentChanged();
|
|
|
|
//setweapon skin
|
|
WeaponAttachment.SetWeaponSkin(ItemDefinition);
|
|
|
|
}
|
|
|
|
simulated function PlayRandomIdleAnimation(optional bool bNewCharacter)
|
|
{
|
|
local byte AnimIndex;
|
|
local name AnimName;
|
|
local AnimSet AnimSet;
|
|
local float BlendInTime;
|
|
|
|
AnimSet = Mesh.AnimSets[Mesh.AnimSets.Length - 1];
|
|
AnimIndex = Rand(AnimSet.Sequences.Length);
|
|
AnimName = AnimSet.Sequences[AnimIndex].SequenceName;
|
|
|
|
BlendInTime = (bNewCharacter) ? 0.f : 0.4;
|
|
|
|
// Briefly turn off notify so that PlayCustomAnim won't call OnAnimEnd (e.g. character swap)
|
|
BodyStanceNodes[EAS_FullBody].SetActorAnimEndNotification( FALSE );
|
|
|
|
BodyStanceNodes[EAS_FullBody].PlayCustomAnim(AnimName, 1.f, BlendInTime, 0.4, false, true);
|
|
BodyStanceNodes[EAS_FullBody].SetActorAnimEndNotification( TRUE );
|
|
|
|
// Since the emote is being cancelled clear this flag too; lets the player queue up another emote again if they want
|
|
bPlayingEmote = false;
|
|
}
|
|
|
|
simulated function PlayEmoteAnimation(optional bool bNewCharacter)
|
|
{
|
|
local name AnimName;
|
|
local float BlendInTime;
|
|
|
|
if (bPlayingEmote)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bPlayingEmote = true;
|
|
|
|
AnimName = class'KFEmoteList'.static.GetUnlockedEmote( class'KFEmoteList'.static.GetEquippedEmoteId() );
|
|
|
|
BlendInTime = (bNewCharacter) ? 0.f : 0.4;
|
|
|
|
// Briefly turn off notify so that PlayCustomAnim won't call OnAnimEnd (e.g. character swap)
|
|
BodyStanceNodes[EAS_FullBody].SetActorAnimEndNotification( FALSE );
|
|
|
|
BodyStanceNodes[EAS_FullBody].PlayCustomAnim(AnimName, 1.f, BlendInTime, 0.4, false, true);
|
|
BodyStanceNodes[EAS_FullBody].SetActorAnimEndNotification( TRUE );
|
|
}
|
|
|
|
/** Called from SkeletalMeshComponent::PlayParticleEffect() */
|
|
simulated function OnAnimNotifyParticleSystemSpawned( const AnimNotify_PlayParticleEffect AnimNotifyData, ParticleSystemComponent PSC )
|
|
{
|
|
if (bPlayingEmote)
|
|
{
|
|
PSC.bUseAsOccluder = true;
|
|
}
|
|
}
|
|
|
|
simulated event OnAnimEnd(AnimNodeSequence SeqNode, float PlayedTime, float ExcessTime)
|
|
{
|
|
bPlayingEmote = false;
|
|
PlayRandomIdleAnimation();
|
|
}
|
|
|
|
simulated function NotifyTeamChanged()
|
|
{
|
|
// Applies Character Info for < ROLE_Authority
|
|
if( PlayerReplicationInfo != none && CharacterArch == none )
|
|
{
|
|
SetCharacterArch(GetCharacterInfo());
|
|
}
|
|
}
|
|
|
|
/** If the customization point is created after the pawn, relocate our customization pawn to a customization point */
|
|
function bool MoveToCustomizationPoint()
|
|
{
|
|
local NavigationPoint BestStartSpot;
|
|
local KFGameInfo KFGI;
|
|
local PlayerController PC;
|
|
|
|
KFGI = KFGameInfo( WorldInfo.Game );
|
|
|
|
PC = PlayerController( Controller );
|
|
|
|
if( PC != none )
|
|
{
|
|
BestStartSpot = KFGI.FindCustomizationStart( PC );
|
|
|
|
// If a customization point wasn't found or if SetLocation fails, exit.
|
|
if( BestStartSpot == none )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Update our location/rotation data
|
|
SetUpdatedMovementData( BestStartSpot.Location, BestStartSpot.Rotation );
|
|
|
|
// initialize and start it up
|
|
KFPlayerCamera(PC.PlayerCamera).CustomizationCam.bInitialize = false;
|
|
|
|
bUsingCustomizationPoint = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Customization Pawns cannot take damage */
|
|
event TakeDamage(int Damage, Controller InstigatedBy, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
Customizing
|
|
********************************************************************************************* */
|
|
|
|
/** Set basic variables for the newly created Customization Pawn, derived from RestartPlayer */
|
|
function InitializeCustomizationPawn( PlayerController NewController, NavigationPoint BestStartSpot )
|
|
{
|
|
if (Controller != None)
|
|
{
|
|
Controller.UnPossess();
|
|
}
|
|
|
|
PossessedBy( NewController, false );
|
|
NewController.SetViewTarget( self );
|
|
NewController.SetCameraMode( 'Customization' );
|
|
|
|
// initialize and start it up
|
|
bUsingCustomizationPoint = ( KFCustomizationPoint( BestStartSpot ) != none );
|
|
|
|
// Need to update our client-side movement data
|
|
SetUpdatedMovementData( BestStartSpot.Location, BestStartSpot.Rotation );
|
|
}
|
|
|
|
function Reset();
|
|
|
|
defaultproperties
|
|
{
|
|
MaleCustomizationAnimSet=AnimSet'CHR_BaseMale_ANIM.CS_Male'
|
|
FemaleCustomizationAnimSet=AnimSet'CHR_BaseFemale_ANIM.CS_Female'
|
|
bDisableTurnInPlace=true
|
|
bEnableAimOffset=false
|
|
bSkipActorPropertyReplication=true
|
|
bReplicateMovement=false
|
|
bDisableMeshRotationSmoothing=true
|
|
|
|
// Default the customization pawn to walking so that IK is enabled
|
|
Physics=PHYS_Walking
|
|
|
|
// artificial nudge -3uu to align to floor w/o IK
|
|
Begin Object Name=KFPawnSkeletalMeshComponent
|
|
Translation=(Z=-89)
|
|
End Object
|
|
}
|