297 lines
8.0 KiB
Ucode
297 lines
8.0 KiB
Ucode
|
//=============================================================================
|
||
|
// KFProjectileStickHelper_HRG_Crossboom
|
||
|
//=============================================================================
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2022 Tripwire Interactive LLC
|
||
|
//=============================================================================
|
||
|
|
||
|
class KFProjectileStickHelper_HRG_Crossboom extends KFProjectileStickHelper;
|
||
|
|
||
|
var() float MinDistanceFromPlayer;
|
||
|
var() float DampingFactor;
|
||
|
var() bool bAltFire;
|
||
|
|
||
|
simulated function TryStick(vector HitNormal, optional vector HitLocation, optional Actor HitActor)
|
||
|
{
|
||
|
local TraceHitInfo HitInfo;
|
||
|
// local KFProj_Bolt_HRG_Crossboom ProjOwner;
|
||
|
// ProjOwner = KFProj_Bolt_HRG_Crossboom(Outer);
|
||
|
|
||
|
if (Instigator == None || !Instigator.IsLocallyControlled() || (Physics == PHYS_None && StuckToActor != none))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (HitActor != none && (HitActor == StuckToActor || HitActor == PinPawn))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GetImpactInfo(Velocity, HitLocation, HitNormal, HitInfo);
|
||
|
|
||
|
if (HitInfo.HitComponent != none && GetImpactResult(HitActor, HitInfo.HitComponent))
|
||
|
{
|
||
|
|
||
|
Stick(HitActor, HitLocation, HitNormal, HitInfo);
|
||
|
|
||
|
// Bounce if distance is not met?
|
||
|
/*
|
||
|
if (VSizeSq(HitLocation - Instigator.Location) >= MinDistanceFromPlayer * MinDistanceFromPlayer)
|
||
|
{
|
||
|
Stick(HitActor, HitLocation, HitNormal, HitInfo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Bounce
|
||
|
ProjOwner.Velocity = DampingFactor * (ProjOwner.Velocity - 2.0*HitNormal*(ProjOwner.Velocity dot HitNormal));
|
||
|
ProjOwner.GravityScale = 1.0f;
|
||
|
|
||
|
if( WorldInfo.NetMode != NM_DedicatedServer )
|
||
|
{
|
||
|
// do the impact effects
|
||
|
`ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator, HitNormal, ProjOwner.ImpactEffects, true );
|
||
|
}
|
||
|
|
||
|
ProjOwner.NotifyBounce();
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
if( WorldInfo.NetMode != NM_DedicatedServer )
|
||
|
{
|
||
|
if (bAltFire)
|
||
|
{
|
||
|
`ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator,, KFProj_Bolt_HRG_CrossboomAlt(Outer).ImpactEffects);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
`ImpactEffectManager.PlayImpactEffects(HitLocation, Instigator,, KFProj_Bolt_HRG_Crossboom(Outer).ImpactEffects);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Stops movement of projectile and calculates orientation to surface */
|
||
|
simulated function Stick(Actor HitActor, vector HitLocation, vector HitNormal, const out TraceHitInfo HitInfo)
|
||
|
{
|
||
|
local int BoneIdx;
|
||
|
|
||
|
local KFPawn_Monster HitMonster;
|
||
|
local array<ImpactInfo> HitZoneImpactList;
|
||
|
local vector StartTrace, EndTrace, Direction, ClosestBoneLocation;
|
||
|
local name BoneName;
|
||
|
|
||
|
BoneName = HitInfo.BoneName;
|
||
|
|
||
|
HitMonster = KFPawn_Monster(HitActor);
|
||
|
if (HitMonster != none)
|
||
|
{
|
||
|
// get injury hit zone
|
||
|
StartTrace = HitLocation;
|
||
|
Direction = Normal(Velocity);
|
||
|
EndTrace = StartTrace + Direction * (HitMonster.CylinderComponent.CollisionRadius * 6.0);
|
||
|
TraceProjHitZones(HitMonster, EndTrace, StartTrace, HitZoneImpactList);
|
||
|
|
||
|
if (BoneName == '')
|
||
|
{
|
||
|
// get the best bone to attach to
|
||
|
ClosestBoneLocation = HitMonster.Mesh.GetClosestCollidingBoneLocation(HitLocation, true, false);
|
||
|
BoneName = HitMonster.Mesh.FindClosestBone(ClosestBoneLocation, ClosestBoneLocation);
|
||
|
}
|
||
|
|
||
|
// do impact damage
|
||
|
if (KFWeapon(Owner) != none)
|
||
|
{
|
||
|
HitZoneImpactList[0].RayDir = Normal(EndTrace - StartTrace); // add a raydir here since TraceProjHitZones doesn't fill this out (so certain afflictions apply)
|
||
|
KFWeapon(Owner).HandleProjectileImpact(WeaponFireMode, HitZoneImpactList[0], PenetrationPower);
|
||
|
}
|
||
|
}
|
||
|
else if ( HitActor.bCanBeDamaged ) // Break collectibles and damage destructibles
|
||
|
{
|
||
|
if (Role != ROLE_Authority)
|
||
|
{
|
||
|
if (KFProj_Bolt_HRG_Crossboom(Outer) != none)
|
||
|
{
|
||
|
KFProj_Bolt_HRG_Crossboom(Outer).OnCollectibleHit(HitActor);
|
||
|
}
|
||
|
else if (KFProj_Bolt_HRG_CrossboomAlt(Outer) != none)
|
||
|
{
|
||
|
KFProj_Bolt_HRG_CrossboomAlt(Outer).OnCollectibleHit(HitActor);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
HitActor.TakeDamage(Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, Owner);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!IsZero(HitLocation))
|
||
|
{
|
||
|
SetLocation(HitLocation);
|
||
|
}
|
||
|
|
||
|
SetStickOrientation(HitNormal);
|
||
|
|
||
|
BoneIdx = INDEX_NONE;
|
||
|
|
||
|
if (BoneName != '')
|
||
|
{
|
||
|
BoneIdx = GetBoneIndexFromActor(HitActor, BoneName);
|
||
|
}
|
||
|
|
||
|
StickToActor(HitActor, HitInfo.HitComponent, BoneIdx, true);
|
||
|
|
||
|
if (Role < ROLE_Authority)
|
||
|
{
|
||
|
Outer.ServerStick(HitActor, BoneIdx, StuckToLocation, StuckToRotation);
|
||
|
}
|
||
|
|
||
|
if (WorldInfo.NetMode != NM_DedicatedServer && StickAkEvent != none)
|
||
|
{
|
||
|
PlaySoundBase(StickAkEvent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Changes the base of the charge to the stick actor and sets its relative loc/rot */
|
||
|
simulated function StickToActor(Actor StickTo, PrimitiveComponent HitComp, int BoneIdx, optional bool bCalculateRelativeLocRot)
|
||
|
{
|
||
|
local SkeletalMeshComponent SkelMeshComp;
|
||
|
local Name BoneName;
|
||
|
|
||
|
local vector RelStuckToLocation;
|
||
|
local rotator RelStuckToRotation;
|
||
|
|
||
|
local KFPawn StickToPawn;
|
||
|
|
||
|
StickToPawn = KFPawn(StickTo);
|
||
|
|
||
|
if (bCanPin && (StickToPawn == none || StickToPawn.bCanBePinned))
|
||
|
{
|
||
|
// if StickTo pawn is dead, pin it and keep flying
|
||
|
if (Role == ROLE_Authority)
|
||
|
{
|
||
|
if (StickToPawn != none && !StickToPawn.IsAliveAndWell())
|
||
|
{
|
||
|
if (PinPawn == none)
|
||
|
{
|
||
|
Pin(StickTo, BoneIdx);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (WorldInfo.NetMode != NM_DedicatedServer && PinPawn != none)
|
||
|
{
|
||
|
if (StickToPawn == none)
|
||
|
{
|
||
|
// Pin pinned pawn to StickTo actor
|
||
|
//PinPawn.Mesh.RetardRBLinearVelocity(vector(Rotation), 0.75);
|
||
|
PinPawn.Mesh.SetRBPosition(Location, PinBoneName);
|
||
|
|
||
|
PinConstraint = Spawn(class'RB_ConstraintActorSpawnable',,,Location);
|
||
|
PinConstraint.InitConstraint(PinPawn, none, PinBoneName, '');
|
||
|
}
|
||
|
|
||
|
PinPawn = none;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetPhysics(PHYS_None);
|
||
|
|
||
|
PrevStuckToActor = StuckToActor;
|
||
|
StuckToActor = StickTo;
|
||
|
StuckToBoneIdx = BoneIdx;
|
||
|
|
||
|
// if we found a skel mesh, set our base to it and set relative loc/rot
|
||
|
if (BoneIdx != INDEX_NONE)
|
||
|
{
|
||
|
SkelMeshComp = SkeletalMeshComponent(HitComp);
|
||
|
|
||
|
BoneName = SkelMeshComp.GetBoneName(BoneIdx);
|
||
|
|
||
|
if (bCalculateRelativeLocRot)
|
||
|
{
|
||
|
StuckToLocation = Location;
|
||
|
StuckToRotation = Rotation;
|
||
|
}
|
||
|
|
||
|
SkelMeshComp.TransformToBoneSpace(BoneName, StuckToLocation, StuckToRotation, RelStuckToLocation, RelStuckToRotation);
|
||
|
|
||
|
SetBase(StickTo,, SkelMeshComp, BoneName);
|
||
|
SetRelativeLocation(RelStuckToLocation);
|
||
|
SetRelativeRotation(RelStuckToRotation);
|
||
|
|
||
|
}
|
||
|
// otherwise, just set our base
|
||
|
else
|
||
|
{
|
||
|
if (bCalculateRelativeLocRot)
|
||
|
{
|
||
|
// set replicated loc/rot
|
||
|
StuckToLocation = Location;
|
||
|
StuckToRotation = Rotation;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// set loc/rot to replicated loc/rot
|
||
|
SetLocation(StuckToLocation);
|
||
|
SetRotation(StuckToRotation);
|
||
|
}
|
||
|
|
||
|
SetBase(StickTo);
|
||
|
}
|
||
|
|
||
|
if (bAltFire)
|
||
|
{
|
||
|
KFProj_Bolt_HRG_CrossboomAlt(Outer).NotifyStick();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
KFProj_Bolt_HRG_Crossboom(Outer).NotifyStick();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
simulated function UnStick()
|
||
|
{
|
||
|
PrevStuckToActor = StuckToActor;
|
||
|
|
||
|
StuckToActor = none;
|
||
|
StuckToBoneIdx = INDEX_NONE;
|
||
|
|
||
|
StuckToLocation = vect(0,0,0);
|
||
|
StuckToRotation = rot(0,0,0);
|
||
|
if (!PrevStuckToActor.bTearOff)
|
||
|
{
|
||
|
SetBase(none);
|
||
|
SetPhysics(default.Physics);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Returns appropriate interaction with HitActor (stick or ignore, for now. add bounce later?) */
|
||
|
simulated function bool GetImpactResult(Actor HitActor, PrimitiveComponent HitComp)
|
||
|
{
|
||
|
local StaticMeshComponent StaticMeshComp;
|
||
|
|
||
|
StaticMeshComp = StaticMeshComponent(HitComp);
|
||
|
if (StaticMeshComp != none)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (super.GetImpactResult(HitActor, HitComp))
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check if it is water
|
||
|
return (InterpActor(HitActor) != none && InterpActor(HitActor).bDestroyProjectilesOnEncroach);
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
MinDistanceFromPlayer=1500.0f
|
||
|
DampingFactor=0.02
|
||
|
bAltFire=false
|
||
|
}
|