//============================================================================= // PipeBombProjectile //============================================================================= class PipeBombProjectile extends Projectile; //var float ExplodeTimer; var bool bCanHitOwner, bHitWater; var() float DampenFactor, DampenFactorParallel; var class HitEffectClass; var float LastSparkTime; var() vector RotMag; // how far to rot view var() vector RotRate; // how fast to rot view var() float RotTime; // how much time to rot the instigator's view var() vector OffsetMag; // max view offset vertically var() vector OffsetRate; // how fast to offset view vertically var() float OffsetTime; // how much time to offset view var() class ShrapnelClass; var bool bHasExploded; var bool bDisintegrated; // This nade has been disintegrated by a siren scream. var() sound DisintegrateSound;// The sound of this projectile disintegrating var() sound BeepSound; // A beeping sound this projectile makes var bool bEnemyDetected; // We've found an enemy var int Countdown; // Countdown to explosion after enemy detected var() float DetectionRadius;// How far away to detect enemies var bool bArmed; // Landed on the ground and armed var() float ArmingCountDown;// How long before arming after it lands on the ground var() array ExplodeSounds; var int PlacedTeam; // TeamIndex of the team that placed this projectile var() float ThreatThreshhold;// How much of a threat to detect before we blow up var PipebombLight BombLight; // Flashing light var() vector BombLightOffset; // Offset for flashing light var bool bTriggered; // This thing has exploded var() array ExplodeSoundRefs; var string StaticMeshRef; replication { reliable if(Role == ROLE_Authority) bTriggered; } static function PreloadAssets() { default.ExplodeSounds[0] = sound(DynamicLoadObject(default.ExplodeSoundRefs[0], class'Sound', true)); UpdateDefaultStaticMesh(StaticMesh(DynamicLoadObject(default.StaticMeshRef, class'StaticMesh', true))); } static function bool UnloadAssets() { default.ExplodeSounds[0] = none; UpdateDefaultStaticMesh(none); return true; } simulated function Destroyed() { if( !bHasExploded && !bHidden && bTriggered) Explode(Location,vect(0,0,1)); if( bHidden && !bDisintegrated ) Disintegrate(Location,vect(0,0,1)); if( BombLight != none ) { BombLight.Destroy(); } Super.Destroyed(); } // cut-n-paste to remove grenade smoke trail simulated function PostBeginPlay() { if ( Level.NetMode != NM_DedicatedServer) { BombLight = Spawn(class'PipebombLight',self); BombLight.SetBase(self); } if ( Role == ROLE_Authority ) { Velocity = Speed * Vector(Rotation); RandSpin(25000); bCanHitOwner = false; if (Instigator.HeadVolume.bWaterVolume) { bHitWater = true; Velocity = 0.6*Velocity; } } } simulated function PostNetReceive() { if( bHidden && !bDisintegrated ) { Disintegrate(Location, vect(0,0,1)); } else if( bTriggered && !bHasExploded ) { Explode(Location,vect(0,0,1)); } } function Timer() { local Pawn CheckPawn; local float ThreatLevel; if( !bHidden && !bTriggered ) { if( ArmingCountDown >= 0 ) { ArmingCountDown -= 0.1; if( ArmingCountDown <= 0 ) { SetTimer(1.0,True); } } else { // Check for enemies if( !bEnemyDetected ) { bAlwaysRelevant=false; PlaySound(BeepSound,,0.5,,50.0); foreach VisibleCollidingActors( class 'Pawn', CheckPawn, DetectionRadius, Location ) { if( CheckPawn == Instigator || KFGameType(Level.Game).FriendlyFireScale > 0 && CheckPawn.PlayerReplicationInfo != none && CheckPawn.PlayerReplicationInfo.Team.TeamIndex == PlacedTeam ) { // Make the thing beep if someone on our team is within the detection radius // This gives them a chance to get out of the way ThreatLevel += 0.001; } else { if( (CheckPawn != Instigator) && (CheckPawn.Role == ROLE_Authority) && ((CheckPawn.PlayerReplicationInfo != none && CheckPawn.PlayerReplicationInfo.Team.TeamIndex != PlacedTeam) || CheckPawn.GetTeamNum() != PlacedTeam)) { if( KFMonster(CheckPawn) != none ) { ThreatLevel += KFMonster(CheckPawn).MotionDetectorThreat; if( ThreatLevel >= ThreatThreshhold ) { bEnemyDetected=true; SetTimer(0.15,True); } } else { bEnemyDetected=true; SetTimer(0.15,True); } } } } if( ThreatLevel >= ThreatThreshhold ) { bEnemyDetected=true; SetTimer(0.15,True); } else if( ThreatLevel > 0 ) { SetTimer(0.5,True); } else { SetTimer(1.0,True); } } // Play some fast beeps and blow up else { bAlwaysRelevant=true; Countdown--; if( CountDown > 0 ) { PlaySound(BeepSound,SLOT_Misc,2.0,,150.0); } else { Explode(Location, vector(Rotation)); } } } } else { Destroy(); } } simulated function Landed( vector HitNormal ) { SetTimer(1.0,True); HitWall( HitNormal, none ); } simulated function ProcessTouch( actor Other, vector HitLocation ) { // Don't bounce off of bullet whiz cylinders if( Other.bBlockHitPointTraces ) { return; } // more realistic interactions with karma objects. if (Other.IsA('NetKActor')) KAddImpulse(Velocity,HitLocation,); // Stop the grenade in its tracks if it hits an enemy. if ( !Other.bWorldGeometry && ((Other != Instigator && Other.Base != Instigator )|| bCanHitOwner) ) Velocity = Vect(0,0,0); } // Overridden to tweak the handling of the impact sound simulated function HitWall( vector HitNormal, actor Wall ) { local Vector VNorm; local PlayerController PC; // Reflect off Wall w/damping VNorm = (Velocity dot HitNormal) * HitNormal; Velocity = -VNorm * DampenFactor + (Velocity - VNorm) * DampenFactorParallel; RandSpin(100000); DesiredRotation.Roll = 0; RotationRate.Roll = 0; Speed = VSize(Velocity); if ( Speed < 20 ) { bBounce = False; PrePivot.Z = 3.5; SetPhysics(PHYS_None); DesiredRotation = Rotation; DesiredRotation.Roll = 0; DesiredRotation.Pitch = 0; SetRotation(DesiredRotation); SetTimer(0.1,True); } else { if ( (Level.NetMode != NM_DedicatedServer) && (Speed > 50) ) PlaySound(ImpactSound, SLOT_Misc ); else { bFixedRotationDir = false; bRotateToDesired = true; DesiredRotation.Pitch = 0; RotationRate.Pitch = 50000; } if ( !Level.bDropDetail && (Level.DetailMode != DM_Low) && (Level.TimeSeconds - LastSparkTime > 0.5) && EffectIsRelevant(Location,false) ) { PC = Level.GetLocalPlayerController(); if ( (PC.ViewTarget != None) && VSize(PC.ViewTarget.Location - Location) < 6000 ) Spawn(HitEffectClass,,, Location, Rotator(HitNormal)); LastSparkTime = Level.TimeSeconds; } } } simulated function BlowUp(vector HitLocation) { DelayedHurtRadius(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation ); if ( Role == ROLE_Authority ) MakeNoise(1.0); } simulated function Explode(vector HitLocation, vector HitNormal) { local PlayerController LocalPlayer; local Projectile P; local byte i; bHasExploded = True; BlowUp(HitLocation); bTriggered = true; if( Role == ROLE_Authority ) { SetTimer(0.1, false); NetUpdateTime = Level.TimeSeconds - 1; } PlaySound(ExplodeSounds[rand(ExplodeSounds.length)],,2.0); // Shrapnel for( i=Rand(6); i<10; i++ ) { P = Spawn(ShrapnelClass,,,,RotRand(True)); if( P!=None ) P.RemoteRole = ROLE_None; } if ( EffectIsRelevant(Location,false) ) { Spawn(Class'KFMod.KFNadeLExplosion',,, HitLocation, rotator(vect(0,0,1))); Spawn(ExplosionDecal,self,,HitLocation, rotator(-HitNormal)); } // Shake nearby players screens LocalPlayer = Level.GetLocalPlayerController(); if ( (LocalPlayer != None) && (VSize(Location - LocalPlayer.ViewTarget.Location) < (DamageRadius * 1.5)) ) LocalPlayer.ShakeView(RotMag, RotRate, RotTime, OffsetMag, OffsetRate, OffsetTime); // Clear Explosive Detonation Flag // if ( KFPlayerController(Instigator.Controller) != none && KFSteamStatsAndAchievements(KFPlayerController(Instigator.Controller).SteamStatsAndAchievements) != none ) // { // KFSteamStatsAndAchievements(KFPlayerController(Instigator.Controller).SteamStatsAndAchievements).OnGrenadeExploded(); // } if( Role < ROLE_Authority ) { Destroy(); } } // Make the projectile distintegrate, instead of explode simulated function Disintegrate(vector HitLocation, vector HitNormal) { bDisintegrated = true; bHidden = true; if( Role == ROLE_Authority ) { SetTimer(0.1, false); NetUpdateTime = Level.TimeSeconds - 1; } PlaySound(DisintegrateSound,,2.0); if ( EffectIsRelevant(Location,false) ) { Spawn(Class'KFMod.SirenNadeDeflect',,, HitLocation, rotator(vect(0,0,1))); } } /* HurtRadius() Hurt locally authoritative actors within the radius. */ simulated function HurtRadius( float DamageAmount, float DamageRadius, class DamageType, float Momentum, vector HitLocation ) { local actor Victims; local float damageScale, dist; local vector dir; local int NumKilled; local KFMonster KFMonsterVictim; local Pawn P; local KFPawn KFP; local array CheckedPawns; local int i; local bool bAlreadyChecked; if ( bHurtEntry ) return; bHurtEntry = true; foreach CollidingActors (class 'Actor', Victims, DamageRadius, HitLocation) { // don't let blast damage affect fluid - VisibleCollisingActors doesn't really work for them - jag if( (Victims != self) && (Hurtwall != Victims) && (Victims.Role == ROLE_Authority) && !Victims.IsA('FluidSurfaceInfo') && ExtendedZCollision(Victims)==None ) { if( (Instigator==None || Instigator.Health<=0) && KFPawn(Victims)!=None ) Continue; dir = Victims.Location - HitLocation; dist = FMax(1,VSize(dir)); dir = dir/dist; damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius); if ( Instigator == None || Instigator.Controller == None ) { Victims.SetDelayedDamageInstigatorController( InstigatorController ); } P = Pawn(Victims); if( P != none ) { for (i = 0; i < CheckedPawns.Length; i++) { if (CheckedPawns[i] == P) { bAlreadyChecked = true; break; } } if( bAlreadyChecked ) { bAlreadyChecked = false; P = none; continue; } KFMonsterVictim = KFMonster(Victims); if( KFMonsterVictim != none && KFMonsterVictim.Health <= 0 ) { KFMonsterVictim = none; } KFP = KFPawn(Victims); if( KFMonsterVictim != none ) { damageScale *= KFMonsterVictim.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity)); } else if( KFP != none ) { damageScale *= KFP.GetExposureTo(Location + 15 * -Normal(PhysicsVolume.Gravity)); // Reduce damage to poeple so I can make the damage radius a bit bigger for killing zeds damageScale *= 0.5; } CheckedPawns[CheckedPawns.Length] = P; if ( damageScale <= 0) { P = none; continue; } else { //Victims = P; P = none; } } Victims.TakeDamage(damageScale * DamageAmount,Instigator,Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir,(damageScale * Momentum * dir),DamageType); if( Role == ROLE_Authority && KFMonsterVictim != none && KFMonsterVictim.Health <= 0 ) { NumKilled++; } if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0) { Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation); } } } if( Role == ROLE_Authority ) { if ( NumKilled >= 10 && Instigator != none && Instigator.PlayerReplicationInfo != none && KFSteamStatsAndAchievements(Instigator.PlayerReplicationInfo.SteamStatsAndAchievements) != none ) { KFSteamStatsAndAchievements(Instigator.PlayerReplicationInfo.SteamStatsAndAchievements).Killed10ZedsWithPipebomb(); } if( NumKilled >= 4 ) { KFGameType(Level.Game).DramaticEvent(0.05); } else if( NumKilled >= 2 ) { KFGameType(Level.Game).DramaticEvent(0.03); } } bHurtEntry = false; } // Shoot nades in mid-air // Alex function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class damageType, optional int HitIndex) { if ( damageType == class'DamTypePipeBomb' || ClassIsChildOf(damageType, class'DamTypeMelee') || (Damage < 25 && damageType.IsA('SirenScreamDamage')) ) { return; } // Don't let our own explosives blow this up!!! if ( InstigatedBy == none || InstigatedBy != none && InstigatedBy.PlayerReplicationInfo != none && InstigatedBy.PlayerReplicationInfo.Team != none && InstigatedBy.PlayerReplicationInfo.Team.TeamIndex == PlacedTeam && Class(damageType) != none && (Class(damageType).default.bIsExplosive || InstigatedBy != Instigator) ) { return; } if ( damageType == class'SirenScreamDamage') { if ( Damage >= 5 ) { Disintegrate(HitLocation, vect(0,0,1)); } } else { Explode(HitLocation, vect(0,0,1)); } } defaultproperties { TossZ=+0.0 RotMag=(X=600.000000,Y=600.000000,Z=600.000000) RotRate=(X=12500.000000,Y=12500.000000,Z=12500.000000) RotTime=6.000000 OffsetMag=(X=5.000000,Y=10.000000,Z=5.000000) OffsetRate=(X=300.000000,Y=300.000000,Z=300.000000) OffsetTime=3.500000 ShrapnelClass=Class'PipeBombShrapnel' DampenFactor=0.250000 DampenFactorParallel=0.400000 HitEffectClass=None Speed=50.000000 MaxSpeed=50//350.000000 Damage=1500.000000 DamageRadius=350.000000 MomentumTransfer=100000.000000 MyDamageType=Class'KFMod.DamTypePipeBomb' ExplosionDecal=Class'KFMod.KFScorchMark' Physics=PHYS_Falling DrawType=DT_StaticMesh StaticMeshRef="KF_pickups2_Trip.Pipebomb_Pickup" DrawScale=1.000000 AmbientGlow=0 bUnlit=False TransientSoundVolume=200.000000 bNetTemporary=False bNetNotify=true bBounce=True bFixedRotationDir=True DesiredRotation=(Pitch=12000,Yaw=5666,Roll=2334) FluidSurfaceShootStrengthMod=3.f ImpactSound=sound'KF_GrenadeSnd.Nade_HitSurf' ExplodeSoundRefs(0)="Inf_Weapons.antitankmine_explode01" ExplodeSoundRefs(1)="Inf_Weapons.antitankmine_explode02" ExplodeSoundRefs(2)="Inf_Weapons.antitankmine_explode03" DisintegrateSound=Sound'Inf_Weapons.faust_explode_distant02' BeepSound=Sound'KF_FoundrySnd.Keypad_beep01' bBlockHitPointTraces=true LifeSpan=0.000000 CollisionRadius=+0008.000000 CollisionHeight=+00003.000000 bProjTarget=True Countdown=5 DetectionRadius=150 ArmingCountDown=1 ThreatThreshhold=1.0 }