//============================================================================= // KFProj_Dosh //============================================================================= // Projectile for the doshinegun //============================================================================= // Killing Floor 2 // Copyright (C) 2021 Tripwire Interactive LLC //============================================================================= class KFProj_Dosh extends KFProj_RicochetBullet hidedropdown; `define KFPROJ_DOSH_CACHED_LOCATION 20 var PrimitiveComponent DroppedPickupMesh; /** Dampen amount for every bounce */ var() float DampenFactor; /** Dampen amount for parallel angle to velocity */ var() float DampenFactorParallel; var transient Vector PreviousLocations [`KFPROJ_DOSH_CACHED_LOCATION]; var transient rotator PreviousRotations [`KFPROJ_DOSH_CACHED_LOCATION]; var transient Vector Ceiling; // Make sure that last location always exists. simulated event PostBeginPlay() { Super.PostBeginPlay(); } event Tick( float DeltaTime ) { local int i; for (i = `KFPROJ_DOSH_CACHED_LOCATION - 1; i > 0; --i) { PreviousLocations[i] = PreviousLocations[i-1]; PreviousRotations[i] = PreviousRotations[i-1]; } if (`KFPROJ_DOSH_CACHED_LOCATION > 0) { PreviousLocations[0] = Location; PreviousRotations[0] = Rotation; } } /** * Give a little bounce */ simulated event HitWall(vector HitNormal, Actor Wall, PrimitiveComponent WallComp) { local TraceHitInfo HitInfo; // check to make sure we didn't hit a pawn if( Pawn(Wall) == none ) { if (!Wall.bStatic && Wall.bCanBeDamaged && (DamageRadius == 0 || bDamageDestructiblesOnTouch) && !CheckRepeatingTouch(Wall) ) { HitInfo.HitComponent = WallComp; HitInfo.Item = INDEX_None; Wall.TakeDamage( Damage, InstigatorController, Location, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, self); } } Bounce( HitNormal, Wall ); } /** Adjusts movement/physics of projectile. * Returns true if projectile actually bounced / was allowed to bounce */ simulated function bool Bounce( vector HitNormal, Actor BouncedOff) { Velocity = DampingFactor * (Velocity - 2.0*HitNormal*(Velocity dot HitNormal)); if( WorldInfo.NetMode != NM_DedicatedServer ) { // do the impact effects `ImpactEffectManager.PlayImpactEffects(Location, Instigator, HitNormal, ImpactEffects ); } // also done from ProcessDestructibleTouchOnBounce. update LastBounced to solve problem with bouncing rapidly between world/non-world geometry LastBounced.Actor = BouncedOff; LastBounced.Time = WorldInfo.TimeSeconds; SpawnDosh(BouncedOff); Destroy(); return true; } function SpawnDosh(Actor BouncedOff) { local KFDroppedPickup_Cash P; local int i; local Vector Pos; local rotator Rot; if ( WorldInfo.NetMode == NM_Client ) return; if (Pawn(BouncedOff) == none) { Pos = Location; Rot = Rotation; P = Spawn(class'KFDroppedPickup_Cash',,, Pos, Rot,, false); if (P == none) { for (i = 0; i < `KFPROJ_DOSH_CACHED_LOCATION; ++i) { Pos = PreviousLocations[i]; Rot = PreviousRotations[i]; P = Spawn(class'KFDroppedPickup_Cash',,, Pos, Rot,, false); if (P != none) { break; } } } // DrawDebugSphere( Pos, 20, 8, 0, 255, 255, true ); if (P != none && RelocateFromCeiling(Pos)) { P.Destroy(); if(Ceiling.Z > -10000) { // DrawDebugSphere( Ceiling, 22, 8, 255, 255, 0, true ); P = Spawn(class'KFDroppedPickup_Cash',,, Ceiling, Rot,, false); Velocity = vect(0,0,0); } } } else { P = Spawn(class'KFDroppedPickup_Cash',,, Location, Rotation,, true); } if( P == None ) { Destroy(); } else { P.SetPhysics(PHYS_Falling); P.InventoryClass = class'KFInventory_Money'; P.Inventory = Instigator.CreateInventory(P.InventoryClass); P.Velocity = Velocity; P.Instigator = Instigator; P.SetPickupMesh(DroppedPickupMesh); P.SetPickupParticles(none); P.CashAmount = class'KFWeap_AssaultRifle_Doshinegun'.default.DoshCost; } } //============== // Touching // Overridden to get bouncing off of destructible meshes simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal) { local KFPawn KFP; local KFPawn_Human KFPH; local KFPlayerReplicationInfo KFPRI; local TraceHitInfo HitInfo; KFPH = KFPawn_Human(Other); if ( KFPH != none && KFPH != Instigator) { KFPRI = KFPlayerReplicationInfo(KFPH.PlayerReplicationInfo); if (KFPRI != none) { KFPRI.AddDosh(class'KFWeap_AssaultRifle_Doshinegun'.default.DoshCost); Destroy(); return; } } else if ( Other != Instigator && Other.bCanBeDamaged ) { KFP = KFPawn( Other ); if ( KFP != None ) { // check/ignore repeat touch events if( CheckRepeatingTouch(Other) ) { return; } ProcessBulletTouch(Other, HitLocation, HitNormal); Bounce(HitNormal, Other); return; } else { HitInfo.HitComponent = LastTouchComponent; HitInfo.Item = INDEX_None; // force TraceComponent on fractured meshes Other.TakeDamage(Damage, InstigatorController, HitLocation, MomentumTransfer * Normal(Velocity), MyDamageType, HitInfo, self);// special cases for types of destructibles, bShouldBreakSolidGlassOnBounce return; } } super.ProcessTouch(Other, HitLocation, HitNormal); } simulated function SyncOriginalLocation() { local Actor HitActor; local vector HitLocation, HitNormal; local TraceHitInfo HitInfo; if (Role < ROLE_Authority && Instigator != none && Instigator.IsLocallyControlled()) { HitActor = Trace(HitLocation, HitNormal, OriginalLocation, Location,,, HitInfo, TRACEFLAG_Bullet); if (HitActor != none) { Bounce(HitNormal, HitActor); } } Super.SyncOriginalLocation(); } simulated function bool RelocateFromCeiling(Vector Pos) { local Actor HitActorVolume, HitActorWorld, HitActorVolumeBack; local Vector HitLocationVolume, HitLocationWorld; local Vector HitNormalUnused; HitActorWorld = Owner.Trace(HitLocationWorld, HitNormalUnused, Pos - vect(0,0,10000), Pos, false,,,TRACEFLAG_Bullet); if(HitActorWorld != none) { HitActorVolume = KFWeapon(Owner).DoTraceNative(HitLocationVolume, Pos, Pos - vect(0,0,10000)); if (HitActorVolume == none || (HitLocationWorld.Z + 80) > HitLocationVolume.Z || IsZero(HitLocationVolume-Pos)) { return false; } // DrawDebugSphere( HitLocationWorld , 32, 8, 0, 255, 0, true ); // DrawDebugSphere( HitLocationVolume, 30, 8, 255, 0, 0, true ); HitLocationWorld += vect(0,0,1); HitActorVolumeBack = KFWeapon(Owner).DoTraceNative(HitLocationVolume, HitLocationWorld, HitLocationWorld + vect(0,0,10000)); if (HitActorVolumeBack != none && !IsZero(HitLocationVolume-HitLocationWorld)) { // DrawDebugSphere( HitLocationVolume, 28, 8, 255, 255, 0, true ); Ceiling = HitLocationVolume - vect(0,0,10); return true; } } Ceiling = Vect(0,0,-10000); return true; } defaultproperties { MaxSpeed=7500.0 //5000.0 Speed=7500.0 //5000.0 GravityScale=0.57 //0.4 Physics=PHYS_Falling TossZ=100 //150 MomentumTransfer=50000.0 DamageRadius=0 bWarnAIWhenFired=true BouncesLeft=1 DampingFactor=0.1 DampenFactor=0.250000 DampenFactorParallel=0.400000 LifeSpan=120 //8 bNetTemporary=False NetPriority=5 NetUpdateFrequency=200 ProjFlightTemplate=ParticleSystem'WEP_Doshinegun_EMIT.PS_Trail' ProjFlightTemplateZedTime=ParticleSystem'WEP_Doshinegun_EMIT.PS_Trail' ImpactEffects=KFImpactEffectInfo'WEP_Doshinegun_ARCH.Dosh_Impact' Begin Object Class=SkeletalMeshComponent Name=PickupMesh0 SkeletalMesh=SkeletalMesh'GP_Mesh.dosh_SM' PhysicsAsset=PhysicsAsset'GP_Mesh.dosh_SM_Physics' BlockNonZeroExtent=false CastShadow=false End Object DroppedPickupMesh=PickupMesh0 }