2020-12-13 15:01:13 +00:00
|
|
|
//=============================================================================
|
|
|
|
// KFDroppedPickup
|
|
|
|
//=============================================================================
|
|
|
|
// Implements dropped weapon/item pickups
|
|
|
|
//=============================================================================
|
|
|
|
// Killing Floor 2
|
|
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
|
|
// - Christian "schneidzekk" Schneider
|
|
|
|
//=============================================================================
|
|
|
|
// Based on GearDroppedPickup
|
|
|
|
// Copyright 1998-2011 Epic Games, Inc. All Rights Reserved.
|
|
|
|
//=============================================================================
|
|
|
|
class KFDroppedPickup extends DroppedPickup
|
|
|
|
native
|
|
|
|
nativereplication;
|
|
|
|
|
|
|
|
var const RigidBodyState RBState;
|
|
|
|
var const float AngErrorAccumulator;
|
|
|
|
|
|
|
|
/** True if a collision should trigger audio, false otherwise. */
|
|
|
|
var transient bool bCollisionSoundsEnabled;
|
|
|
|
|
|
|
|
var protectedwrite MeshComponent MyMeshComp;
|
|
|
|
|
|
|
|
/** Cache a reference to the cylinder component */
|
|
|
|
var protectedwrite CylinderComponent MyCylinderComp;
|
|
|
|
|
|
|
|
/** (TW) Setup rigid body physics for static meshes components */
|
|
|
|
var const bool bEnableStaticMeshRBPhys;
|
|
|
|
/** (TW) If set this pickups passes (and can be picked) through blocking volumes) */
|
|
|
|
var const bool bIgnoreBlockingVolumes;
|
|
|
|
|
|
|
|
/** When TRUE, there is a delay before the owner is allowed to re-pickup if they are low on health */
|
|
|
|
var protected const bool bUseLowHealthDelay;
|
|
|
|
|
|
|
|
/** Skin assigned from dropped weapon */
|
|
|
|
var int SkinItemId;
|
|
|
|
|
|
|
|
/** Whether the weapon skin has finished loading */
|
|
|
|
var bool bWaitingForWeaponSkinLoad;
|
|
|
|
|
|
|
|
/** Whether or not to use the authority's rigid body update */
|
|
|
|
var protected bool bUseAuthorityRBUpdate;
|
|
|
|
|
|
|
|
/** Life span after changing authority update status */
|
|
|
|
var protected float PostAuthorityChangeLifeSpan;
|
|
|
|
|
|
|
|
/** Amount of delay before anything can pick this up */
|
|
|
|
var protected float PickupDelay;
|
|
|
|
|
|
|
|
// Visuals
|
|
|
|
var bool bEmptyPickup; // pickup has an inventory with no ammo
|
|
|
|
var LinearColor EmptyPickupColor;
|
|
|
|
|
|
|
|
|
|
|
|
var bool bUpgradedPickup; //if the item has an upgrade level of > 0
|
|
|
|
cpptext
|
|
|
|
{
|
|
|
|
// AActor interface
|
|
|
|
virtual void physRigidBody(FLOAT DeltaTime);
|
|
|
|
virtual INT* GetOptimizedRepList(BYTE* InDefault, FPropertyRetirement* Retire, INT* Ptr, UPackageMap* Map, UActorChannel* Channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
replication
|
|
|
|
{
|
|
|
|
if ( Role == ROLE_Authority )
|
|
|
|
RBState, bUseAuthorityRBUpdate;
|
|
|
|
|
|
|
|
if ( bNetInitial )
|
|
|
|
SkinItemId, bEmptyPickup, bUpgradedPickup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
Constructors, Destructors, and Loading
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
simulated native function StaticMesh GetPickupMesh(class<KFWeapon> ItemClass);
|
|
|
|
/**
|
|
|
|
* sets the pickups mesh and makes it the collision component so we can run rigid body physics on it
|
|
|
|
*/
|
|
|
|
simulated function SetPickupMesh(PrimitiveComponent NewPickupMesh)
|
|
|
|
{
|
|
|
|
local ActorComponent Comp;
|
|
|
|
local SkeletalMeshComponent SkelMC;
|
|
|
|
local StaticMeshComponent StaticMC;
|
2021-06-02 20:06:18 +00:00
|
|
|
local KFGameInfo KFGI;
|
2020-12-13 15:01:13 +00:00
|
|
|
|
|
|
|
if (Role == ROLE_Authority )
|
|
|
|
{
|
|
|
|
if ( Inventory != None && Inventory.IsA('KFWeapon') )
|
|
|
|
{
|
|
|
|
SkinItemId = KFWeapon(Inventory).SkinItemId;
|
|
|
|
bEmptyPickup = !KFWeapon(Inventory).HasAnyAmmo();
|
|
|
|
bUpgradedPickup = KFWeapon(Inventory).CurrentWeaponUpgradeIndex > 0;
|
|
|
|
|
|
|
|
}
|
2021-06-02 20:06:18 +00:00
|
|
|
|
|
|
|
KFGI = KFGameInfo(WorldInfo.Game);
|
|
|
|
if (KFGI != none && KFGI.OutbreakEvent != none && KFGI.OutbreakEvent.ActiveEvent.DroppedItemLifespan >= 0.0f)
|
|
|
|
{
|
|
|
|
LifeSpan = KFGI.OutbreakEvent.ActiveEvent.DroppedItemLifespan;
|
|
|
|
}
|
|
|
|
|
2020-12-13 15:01:13 +00:00
|
|
|
SetTimer(LifeSpan, false, nameof(TryFadeOut));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NewPickupMesh != None)
|
|
|
|
{
|
|
|
|
if (CollisionComponent == None || CollisionComponent.Class != NewPickupMesh.Class)
|
|
|
|
{
|
|
|
|
Comp = new(self) NewPickupMesh.Class(NewPickupMesh);
|
|
|
|
MyMeshComp = MeshComponent(Comp);
|
|
|
|
AttachComponent(Comp);
|
|
|
|
MyCylinderComp = CylinderComponent(CollisionComponent);
|
|
|
|
AlignMeshToCylinder();
|
|
|
|
|
|
|
|
CollisionComponent = PrimitiveComponent(Comp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Comp = CollisionComponent;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Weapons packages are async loaded from string. At this point the packages will be loaded into root,
|
|
|
|
// but the asset still has to be grabbed and set.
|
|
|
|
if (class<KFWeapon>(InventoryClass) != none)
|
|
|
|
{
|
|
|
|
StaticMeshComponent(MyMeshComp).SetStaticMesh(GetPickupMesh(class<KFWeapon>(InventoryClass)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up physics on the cloned component
|
|
|
|
CollisionComponent.SetScale3D(NewPickupMesh.Scale3D);
|
|
|
|
|
|
|
|
CollisionComponent.SetBlockRigidBody(TRUE);
|
|
|
|
CollisionComponent.SetActorCollision(TRUE,FALSE);
|
|
|
|
CollisionComponent.SetRBChannel( RBCC_Pickup );
|
|
|
|
CollisionComponent.SetRBCollidesWithChannel( RBCC_Default, TRUE );
|
|
|
|
if ( !bIgnoreBlockingVolumes )
|
|
|
|
{
|
|
|
|
CollisionComponent.SetRBCollidesWithChannel( RBCC_BlockingVolume, TRUE );
|
|
|
|
}
|
|
|
|
|
|
|
|
CollisionComponent.SetNotifyRigidBodyCollision( TRUE );
|
|
|
|
CollisionComponent.ScriptRigidBodyCollisionThreshold = 100;
|
|
|
|
CollisionComponent.WakeRigidBody();
|
|
|
|
|
|
|
|
SkelMC = SkeletalMeshComponent(CollisionComponent);
|
|
|
|
if (SkelMC != None)
|
|
|
|
{
|
|
|
|
SkelMC.bUpdateSkelWhenNotRendered=FALSE;
|
|
|
|
SkelMC.bComponentUseFixedSkelBounds=FALSE; // using physics when dropped, can't use fixed bounds
|
|
|
|
|
|
|
|
SkelMC.PhysicsWeight = 1.f;
|
|
|
|
SkelMC.SetHasPhysicsAssetInstance(TRUE);
|
|
|
|
if (Role == ROLE_Authority)
|
|
|
|
{
|
|
|
|
SetPhysics(PHYS_RigidBody);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkelMC.PhysicsAssetInstance.SetAllBodiesFixed(false);
|
|
|
|
|
|
|
|
bCallRigidBodyWakeEvents = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StaticMC = StaticMeshComponent(CollisionComponent);
|
|
|
|
if (StaticMC != none && bEnableStaticMeshRBPhys)
|
|
|
|
{
|
|
|
|
CollisionComponent.InitRBPhys();
|
|
|
|
if (Role == ROLE_Authority && CollisionComponent.BodyInstance != None)
|
|
|
|
{
|
|
|
|
SetPhysics(PHYS_RigidBody);
|
|
|
|
}
|
|
|
|
|
|
|
|
bCallRigidBodyWakeEvents = true;
|
|
|
|
|
|
|
|
// PhysX3 fix - Inherit RB velocity from actor (StaticMesh only)
|
|
|
|
MyMeshComp.SetRBLinearVelocity(Velocity);
|
|
|
|
Velocity = vect(0,0,0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetPickupSkin(SkinItemId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
native function bool StartLoadPickupSkin(int SkinId);
|
|
|
|
|
|
|
|
simulated event SetPickupSkin(int ItemId, bool bFinishedLoading = false)
|
|
|
|
{
|
|
|
|
local array<MaterialInterface> SkinMICs;
|
|
|
|
|
|
|
|
if (ItemId > 0 && WorldInfo.NetMode != NM_DedicatedServer && !bWaitingForWeaponSkinLoad)
|
|
|
|
{
|
|
|
|
if (bFinishedLoading || !StartLoadPickupSkin(ItemId))
|
|
|
|
{
|
|
|
|
SkinMICs = class'KFWeaponSkinList'.static.GetWeaponSkin(ItemId, WST_Pickup);
|
|
|
|
if ( SkinMICs.Length > 0 )
|
|
|
|
{
|
|
|
|
MyMeshComp.SetMaterial(0, SkinMICs[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bUpgradedPickup)
|
|
|
|
{
|
|
|
|
SetUpgradedMaterial();
|
|
|
|
}
|
|
|
|
if (bEmptyPickup)
|
|
|
|
{
|
|
|
|
SetEmptyMaterial();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function SetUpgradedMaterial()
|
|
|
|
{
|
|
|
|
local MaterialInstanceConstant MeshMIC;
|
|
|
|
|
|
|
|
if (MyMeshComp != None)
|
|
|
|
{
|
|
|
|
MeshMIC = MyMeshComp.CreateAndSetMaterialInstanceConstant(0);
|
|
|
|
if (MeshMIC != None)
|
|
|
|
{
|
|
|
|
MeshMIC.SetScalarParameterValue('Upgrade', 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated function SetEmptyMaterial()
|
|
|
|
{
|
|
|
|
local MaterialInstanceConstant MeshMIC;
|
|
|
|
|
|
|
|
if (MyMeshComp != None)
|
|
|
|
{
|
|
|
|
MeshMIC = MyMeshComp.CreateAndSetMaterialInstanceConstant(0);
|
|
|
|
if (MeshMIC != None)
|
|
|
|
{
|
|
|
|
MeshMIC.SetVectorParameterValue('GlowColor', EmptyPickupColor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* returns the display name of the item to be picked up
|
|
|
|
*/
|
|
|
|
simulated function String GetDisplayName()
|
|
|
|
{
|
|
|
|
if ( InventoryClass != None )
|
|
|
|
{
|
|
|
|
return InventoryClass.Default.ItemName;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Moves the mesh to be centered and sized correctly since most of our weapon meshes are way off center */
|
|
|
|
simulated function AlignMeshToCylinder()
|
|
|
|
{
|
|
|
|
local vector MeshCenterLocal;
|
|
|
|
local SkeletalMeshComponent SkelComp;
|
|
|
|
|
|
|
|
// translate mesh so that the bounds origin is at the actor position
|
|
|
|
SkelComp = SkeletalMeshComponent(MyMeshComp);
|
|
|
|
if (SkelComp != None)
|
|
|
|
{
|
|
|
|
MeshCenterLocal = (SkelComp.Bounds.Origin - Location) << Rotation;
|
|
|
|
SkelComp.SetTranslation(-MeshCenterLocal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
PHYS_RigidBody
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/** RigidBody went to sleep after being awake - only valid if bCallRigidBodyWakeEvents==TRUE */
|
|
|
|
event OnSleepRBPhysics()
|
|
|
|
{
|
|
|
|
local SkeletalMeshComponent SkelComp;
|
|
|
|
|
|
|
|
//`log( "turning off RB collision" );
|
|
|
|
CollisionComponent.SetNotifyRigidBodyCollision(false);
|
|
|
|
ClearTimer( 'CheckForRigidBodySleepState' );
|
|
|
|
CollisionComponent.SetBlockRigidBody(false);
|
|
|
|
CollisionComponent.SetActorCollision(false, false);
|
|
|
|
|
|
|
|
// Fix in place so nothing can move it.
|
|
|
|
SkelComp = SkeletalMeshComponent(CollisionComponent);
|
|
|
|
if (SkelComp != None && SkelComp.PhysicsAssetInstance != None)
|
|
|
|
{
|
|
|
|
SkelComp.PhysicsAssetInstance.SetAllBodiesFixed(true);
|
|
|
|
SkelComp.bUpdateKinematicBonesFromAnimation = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SkelComp != None)
|
|
|
|
{
|
|
|
|
SkelComp.bNoSkeletonUpdate = true;
|
|
|
|
SkelComp.bUpdateSkelWhenNotRendered = false;
|
|
|
|
SkelComp.SetAnimTreeTemplate(None);
|
|
|
|
}
|
|
|
|
else if ( bEnableStaticMeshRBPhys )
|
|
|
|
{
|
|
|
|
CollisionComponent.GetRootBodyInstance().SetFixed(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// reduce to dirty replication like DroppedPickup
|
|
|
|
bOnlyDirtyReplication = true;
|
|
|
|
bNetDirty = true;
|
|
|
|
|
|
|
|
// pretend we were rendered this frame to make sure everything (bounds, physics, etc) gets updated with our final pose
|
|
|
|
CollisionComponent.LastRenderTime = WorldInfo.TimeSeconds;
|
|
|
|
|
|
|
|
AlignCollisionCylinder();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** moves the cylinder to be centered and sized correctly since most of our weapon meshes are way off center */
|
|
|
|
simulated final function AlignCollisionCylinder()
|
|
|
|
{
|
|
|
|
local vector MeshCenter;
|
|
|
|
local vector MeshTranslationLocal;
|
|
|
|
|
|
|
|
MeshCenter = (MyMeshComp.Bounds.Origin - Location);
|
|
|
|
// Added cylinder CollisionHeight so that cylinder rests on the floor
|
|
|
|
MeshTranslationLocal = (MyCylinderComp.default.CollisionHeight * vect(0,0,1) + MeshCenter) << Rotation;
|
|
|
|
|
|
|
|
MyCylinderComp.SetTranslation(MeshTranslationLocal);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @see GearWeapon.DropFrom for the ScriptRigidBodyCollisionThreshold value we use **/
|
|
|
|
event RigidBodyCollision(PrimitiveComponent HitComponent, PrimitiveComponent OtherComponent,
|
|
|
|
const out CollisionImpactData RigidCollisionData, int ContactIndex)
|
|
|
|
{
|
|
|
|
PlayCollisionSound();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
Sound Effects
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Overridden to prevent adding to navigation network.
|
|
|
|
*/
|
|
|
|
event Landed(vector HitNormal, actor FloorActor)
|
|
|
|
{
|
|
|
|
bForceNetUpdate = TRUE;
|
|
|
|
NetUpdateFrequency = 3;
|
|
|
|
|
|
|
|
PlayCollisionSound();
|
|
|
|
}
|
|
|
|
|
|
|
|
function PlayCollisionSound()
|
|
|
|
{
|
|
|
|
//local SoundCue Cue;
|
|
|
|
//local float Duration;
|
|
|
|
|
|
|
|
if (bCollisionSoundsEnabled)
|
|
|
|
{
|
|
|
|
//Cue = class<KFWeapon>(InventoryClass).default.WeaponDropSound;
|
|
|
|
|
|
|
|
//if (Cue != None)
|
|
|
|
//{
|
|
|
|
// Duration = Cue.GetCueDuration();
|
|
|
|
// SetTimer( Duration, FALSE, nameof(ReenableCollisionSounds) );
|
|
|
|
// PlaySound(Cue);
|
|
|
|
// bCollisionSoundsEnabled = FALSE;
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function ReenableCollisionSounds()
|
|
|
|
{
|
|
|
|
bCollisionSoundsEnabled = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
Inventory
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Give pickup to player
|
|
|
|
*/
|
|
|
|
function GiveTo(Pawn P)
|
|
|
|
{
|
|
|
|
local KFWeapon KFW;
|
|
|
|
local class<KFWeapon> KFWInvClass;
|
|
|
|
local Inventory NewInventory;
|
|
|
|
local KFInventoryManager KFIM;
|
2021-12-09 16:33:06 +00:00
|
|
|
local KFGameReplicationInfo KFGRI;
|
|
|
|
|
|
|
|
KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
|
|
|
|
if (KFGRI != none && KFGRI.bIsEndlessPaused)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2020-12-13 15:01:13 +00:00
|
|
|
|
|
|
|
KFIM = KFInventoryManager(P.InvManager);
|
|
|
|
if (KFIM != None)
|
|
|
|
{
|
|
|
|
KFWInvClass = class<KFWeapon>(InventoryClass);
|
|
|
|
foreach KFIM.InventoryActors(class'KFWeapon', KFW)
|
|
|
|
{
|
|
|
|
if (KFW.Class == InventoryClass)
|
|
|
|
{
|
|
|
|
// if this isn't a dual-wield class, then we can't carry another
|
|
|
|
if (KFW.DualClass == none)
|
|
|
|
{
|
|
|
|
PlayerController(P.Owner).ReceiveLocalizedMessage(class'KFLocalMessage_Game', GMT_AlreadyCarryingWeapon);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// if we already have the dual version of this single, then we can't carry another
|
|
|
|
else if (KFWInvClass != none && KFW.Class == KFWInvClass.default.DualClass)
|
|
|
|
{
|
|
|
|
PlayerController(P.Owner).ReceiveLocalizedMessage(class'KFLocalMessage_Game', GMT_AlreadyCarryingWeapon);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (KFWInvClass != none && KFWeapon(Inventory) != none && !KFIM.CanCarryWeapon(KFWInvClass, KFWeapon(Inventory).CurrentWeaponUpgradeIndex))
|
|
|
|
{
|
|
|
|
PlayerController(P.Owner).ReceiveLocalizedMessage(class'KFLocalMessage_Game', GMT_TooMuchWeight);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NewInventory = KFIM.CreateInventory(InventoryClass, true);
|
|
|
|
if (NewInventory != none)
|
|
|
|
{
|
|
|
|
// Added extra check in case we want to pick up a non-weapon based pickup
|
|
|
|
KFW = KFWeapon(NewInventory);
|
|
|
|
if (KFW != none)
|
|
|
|
{
|
|
|
|
KFW.SetOriginalValuesFromPickup(KFWeapon(Inventory));
|
|
|
|
KFW = KFIM.CombineWeaponsOnPickup(KFW);
|
|
|
|
KFW.NotifyPickedUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
Destroy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Role == ROLE_Authority)
|
|
|
|
{
|
|
|
|
//refresh weapon hud here
|
|
|
|
NotifyHUDofWeapon(P);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
event Destroyed()
|
|
|
|
{
|
|
|
|
super.Destroyed();
|
|
|
|
|
|
|
|
Inventory.Destroy();
|
|
|
|
Inventory = none;
|
|
|
|
}
|
|
|
|
|
|
|
|
function NotifyHUDofWeapon(Pawn P)
|
|
|
|
{
|
|
|
|
local KFPlayerController KFPC;
|
|
|
|
|
|
|
|
KFPC = KFPlayerController(P.Owner);
|
|
|
|
|
|
|
|
if (KFPC != none && KFPC.MyGFxHUD != none)
|
|
|
|
{
|
|
|
|
KFPC.MyGFxHUD.NotifyHUDofWeapon();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* State Pickup
|
|
|
|
* Pickup is active
|
|
|
|
*********************************************************************************************/
|
|
|
|
|
|
|
|
function bool ValidTouch(Pawn Other);
|
|
|
|
|
|
|
|
auto state Pickup
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Validate touch (if valid return true to let other pick me up and trigger event).
|
|
|
|
* Overridden to remove call to WorldInfo.Game.PickupQuery(Other, InventoryType, self) since we need this to be client side
|
|
|
|
*/
|
|
|
|
function bool ValidTouch(Pawn Other)
|
|
|
|
{
|
|
|
|
local Actor HitA;
|
|
|
|
local vector HitLocation, HitNormal;
|
|
|
|
local TraceHitInfo HitInfo;
|
|
|
|
local bool bHitWall;
|
|
|
|
|
|
|
|
// make sure its a live player
|
|
|
|
if (Other == None || !Other.bCanPickupInventory || !Other.IsAliveAndWell() || (Other.DrivenVehicle == None && Other.Controller == None))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (`TimeSince(CreationTime) < PickupDelay)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// prevent picking up as soon as it's spawned
|
|
|
|
if ( Other == Instigator )
|
|
|
|
{
|
|
|
|
// If low on health, wait a little longer to allow pickup again
|
|
|
|
if( (bUseLowHealthDelay && (Instigator.Health / Instigator.HealthMax) <= 0.2f && `TimeSince(CreationTime) < 1.f)
|
|
|
|
|| `TimeSince(CreationTime) < 0.1f )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure not touching through wall
|
|
|
|
foreach Other.TraceActors( class'Actor', HitA, HitLocation, HitNormal, MyCylinderComp.GetPosition() + vect(0,0,10), Other.Location, vect(1,1,1), HitInfo )
|
|
|
|
{
|
|
|
|
if ( IsTouchBlockedBy(HitA, HitInfo.HitComponent) )
|
|
|
|
{
|
|
|
|
if (MyMeshComp == None)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bHitWall = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bHitWall)
|
|
|
|
{
|
|
|
|
// fail only if wall between both cylinder and mesh center
|
|
|
|
foreach Other.TraceActors( class'Actor', HitA, HitLocation, HitNormal, MyMeshComp.Bounds.Origin + vect(0,0,10), Other.Location, vect(1,1,1), HitInfo )
|
|
|
|
{
|
|
|
|
if ( IsTouchBlockedBy(HitA, HitInfo.HitComponent) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure game will let player pick me up
|
|
|
|
if (WorldInfo.Game.PickupQuery(Other, Inventory.class, self))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Overridden to prevent adding to navigation network.
|
|
|
|
*/
|
|
|
|
function BeginState(Name PreviousStateName)
|
|
|
|
{
|
|
|
|
SetTimer(LifeSpan - 1, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** helper for ValidTouch */
|
|
|
|
simulated function bool IsTouchBlockedBy( Actor A, PrimitiveComponent HitComponent )
|
|
|
|
{
|
|
|
|
if( A != self && A.bWorldGeometry && HitComponent != none && HitComponent.BlockActors )
|
|
|
|
{
|
|
|
|
if ( BlockingVolume(A) != None )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Level was reset without reloading */
|
|
|
|
function Reset()
|
|
|
|
{
|
|
|
|
Destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Handle any behavior associated with disabling the authority override for RB simulation. Also
|
|
|
|
* flip the replicated flag to let the clients take over.
|
|
|
|
*/
|
|
|
|
function DisableAuthorityRBSim()
|
|
|
|
{
|
|
|
|
bUseAuthorityRBUpdate = false;
|
|
|
|
|
|
|
|
//Since we're now doing client simulation, keep it around long enough to
|
|
|
|
// be useful, but no longer. Reset fadeout timer.
|
|
|
|
ClearTimer(nameof(TryFadeOut));
|
|
|
|
SetTimer(PostAuthorityChangeLifeSpan, false, nameof(TryFadeOut));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* state FadeOut extends Pickup
|
|
|
|
* Pickup is about to disappear
|
|
|
|
*********************************************************************************************/
|
|
|
|
|
|
|
|
State FadeOut
|
|
|
|
{
|
|
|
|
function Tick(float DeltaTime)
|
|
|
|
{
|
|
|
|
SetDrawScale(FMax(0.01, DrawScale - Default.DrawScale * DeltaTime));
|
|
|
|
Global.Tick(DeltaTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated event BeginState(Name PreviousStateName)
|
|
|
|
{
|
|
|
|
bFadeOut = true;
|
|
|
|
RotationRate.Yaw=60000;
|
|
|
|
SetPhysics(PHYS_Rotating);
|
|
|
|
LifeSpan = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** disable normal touching. we require input from the player to pick it up */
|
|
|
|
event Touch( Actor Other, PrimitiveComponent OtherComp, vector HitLocation, vector HitNormal )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Called on the server, try to find out when item is not close to player */
|
|
|
|
function TryFadeOut()
|
|
|
|
{
|
|
|
|
local Pawn P;
|
|
|
|
|
|
|
|
//Only do this if we're letting server run the RB updates
|
|
|
|
if (bUseAuthorityRBUpdate)
|
|
|
|
{
|
|
|
|
// don't fade out yet if a player is nearby
|
|
|
|
foreach WorldInfo.AllPawns(class'Pawn', P, Location, 1024.0)
|
|
|
|
{
|
|
|
|
if (P.IsPlayerOwned())
|
|
|
|
{
|
|
|
|
SetTimer(5.0, false, nameof(TryFadeOut));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GotoState('FadeOut');
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultproperties
|
|
|
|
{
|
|
|
|
//TickGroup=TG_DuringAsyncWork // can't turn this on here as we get a TickGroup error when trying to update bones
|
|
|
|
|
|
|
|
bOnlyDirtyReplication=FALSE
|
|
|
|
bOnlyRelevantToOwner=FALSE
|
|
|
|
bAlwaysRelevant=TRUE
|
|
|
|
bKillDuringLevelTransition=TRUE
|
|
|
|
bCanStepUpOn=false
|
|
|
|
Lifespan=300.f
|
|
|
|
PostAuthorityChangeLifeSpan=5.f
|
|
|
|
|
|
|
|
Begin Object NAME=CollisionCylinder
|
|
|
|
CollisionRadius=+00075.000000
|
|
|
|
CollisionHeight=+00040.000000
|
|
|
|
CollideActors=true
|
|
|
|
Translation=(Z=40) // offset by CollisionHeight so that cylinder is on floor
|
|
|
|
End Object
|
|
|
|
|
|
|
|
bUseLowHealthDelay=TRUE
|
|
|
|
|
|
|
|
bCollisionSoundsEnabled=TRUE
|
|
|
|
|
|
|
|
// custom collision settings
|
|
|
|
bEnableStaticMeshRBPhys=TRUE
|
|
|
|
|
|
|
|
bUseAuthorityRBUpdate=TRUE
|
|
|
|
|
|
|
|
EmptyPickupColor=(R=0.75)
|
|
|
|
bWaitingForWeaponSkinLoad=false
|
|
|
|
}
|