571 lines
17 KiB
Ucode
571 lines
17 KiB
Ucode
//=============================================================================
|
|
// PipeBombProjectile
|
|
//=============================================================================
|
|
class PipeBombProjectile extends Projectile;
|
|
|
|
//var float ExplodeTimer;
|
|
var bool bCanHitOwner, bHitWater;
|
|
var() float DampenFactor, DampenFactorParallel;
|
|
var class<xEmitter> 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<Projectile> 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<Sound> 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<string> 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> 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<Pawn> 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> 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<KFWeaponDamageType>(damageType) != none &&
|
|
(Class<KFWeaponDamageType>(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
|
|
}
|