rott/kf_sources/Engine/Classes/Projectile.uc
2026-07-14 20:27:09 +07:00

311 lines
10 KiB
Ucode

//=============================================================================
// Projectile.
//
// A delayed-hit projectile that moves around for some time after it is created.
//=============================================================================
class Projectile extends Actor
abstract
native;
//-----------------------------------------------------------------------------
// Projectile variables.
// Motion information.
var() float Speed; // Initial speed of projectile.
var float MaxSpeed; // Limit on speed of projectile (0 means no limit)
var float TossZ;
var Actor ZeroCollider;
var bool bSwitchToZeroCollision; // if collisionextent nonzero, and hit actor with bBlockNonZeroExtents=0, switch to zero extent collision
var bool bNoFX; // used to prevent effects when projectiles are destroyed (see LimitationVolume)
var bool bReadyToSplash;
var bool bSpecialCalcView; // Use the projectile's SpecialCalcView function instead of letting the playercontroller handle the camera
// Damage attributes.
var float Damage;
var float DamageRadius;
var float MomentumTransfer; // Momentum magnitude imparted by impacting projectile.
var class<DamageType> MyDamageType;
// Projectile sound effects
var sound SpawnSound; // Sound made when projectile is spawned.
var sound ImpactSound; // Sound made when projectile hits something.
// explosion effects
var class<Projector> ExplosionDecal;
var class<Projector> ExplosionDecalSnow;
var float ExploWallOut; // distance to move explosions out from wall
var Controller InstigatorController;
var Actor LastTouched;
var Actor HurtWall;
var float MaxEffectDistance;
var bool bScriptPostRender; // if true, PostRender2D() gets called
// if _RO_
// for tank cannon aiming. Returns the proper pitch adjustment to hit a target at a particular range
simulated static function int GetPitchForRange(int Range){return 0;}
// for tank cannon aiming. Returns the proper Y adjustment of the scope to hit a target at a particular range
simulated static function float GetYAdjustForRange(int Range){return 0;}
simulated function PostBeginPlay()
{
local PlayerController PC;
if ( Role == ROLE_Authority && Instigator != None && Instigator.Controller != None )
{
if ( Instigator.Controller.ShotTarget != None && Instigator.Controller.ShotTarget.Controller != None )
Instigator.Controller.ShotTarget.Controller.ReceiveProjectileWarning( Self );
InstigatorController = Instigator.Controller;
}
if ( bDynamicLight && Level.NetMode != NM_DedicatedServer )
{
PC = Level.GetLocalPlayerController();
if ( (PC == None) || (PC.ViewTarget == None) || (VSize(PC.ViewTarget.Location - Location) > 4000) )
{
LightType = LT_None;
bDynamicLight = false;
}
}
bReadyToSplash = true;
}
function bool SpecialCalcView(out Actor ViewActor, out vector CameraLocation, out rotator CameraRotation, bool bBehindView);
simulated function bool CanSplash()
{
return bReadyToSplash;
}
function Reset()
{
Destroy();
}
simulated function bool CheckMaxEffectDistance(PlayerController P, vector SpawnLocation)
{
return !P.BeyondViewDistance(SpawnLocation,MaxEffectDistance);
}
/* 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;
if ( bHurtEntry )
return;
bHurtEntry = true;
foreach VisibleCollidingActors( 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') )
{
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 );
if ( Victims == LastTouched )
LastTouched = None;
Victims.TakeDamage
(
damageScale * DamageAmount,
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir,
(damageScale * Momentum * dir),
DamageType
);
if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
}
}
if ( (LastTouched != None) && (LastTouched != self) && (LastTouched.Role == ROLE_Authority) && !LastTouched.IsA('FluidSurfaceInfo') )
{
Victims = LastTouched;
LastTouched = None;
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = FMax(Victims.CollisionRadius/(Victims.CollisionRadius + Victims.CollisionHeight),1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius));
if ( Instigator == None || Instigator.Controller == None )
Victims.SetDelayedDamageInstigatorController(InstigatorController);
Victims.TakeDamage
(
damageScale * DamageAmount,
Instigator,
Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir,
(damageScale * Momentum * dir),
DamageType
);
if (Vehicle(Victims) != None && Vehicle(Victims).Health > 0)
Vehicle(Victims).DriverRadiusDamage(DamageAmount, DamageRadius, InstigatorController, DamageType, Momentum, HitLocation);
}
bHurtEntry = false;
}
/* HurtRadius()
Hurt locally authoritative actors within the radius.
*/
simulated function DelayedHurtRadius( float DamageAmount, float DamageRadius, class<DamageType> DamageType, float Momentum, vector HitLocation )
{
HurtRadius(DamageAmount, DamageRadius, DamageType, Momentum, HitLocation);
}
//==============
// Encroachment
function bool EncroachingOn( actor Other )
{
if ( (Other.Brush != None) || (Brush(Other) != None) )
return true;
return false;
}
//==============
// Touching
simulated singular function Touch(Actor Other)
{
local vector HitLocation, HitNormal;
if ( Other == None ) // Other just got destroyed in its touch?
return;
if ( Other.bProjTarget || Other.bBlockActors )
{
LastTouched = Other;
if ( Velocity == vect(0,0,0) || Other.IsA('Mover') )
{
ProcessTouch(Other,Location);
LastTouched = None;
return;
}
if ( Other.TraceThisActor(HitLocation, HitNormal, Location, Location - 2*Velocity, GetCollisionExtent()) )
HitLocation = Location;
ProcessTouch(Other, HitLocation);
LastTouched = None;
if ( (Role < ROLE_Authority) && (Other.Role == ROLE_Authority) && (Pawn(Other) != None) )
ClientSideTouch(Other, HitLocation);
}
}
simulated function ClientSideTouch(Actor Other, Vector HitLocation)
{
Other.TakeDamage(Damage, instigator, Location, MomentumTransfer * Normal(Velocity), MyDamageType);
}
simulated function ProcessTouch(Actor Other, Vector HitLocation)
{
if ( Other != Instigator )
Explode(HitLocation,Normal(HitLocation-Other.Location));
}
simulated singular function HitWall(vector HitNormal, actor Wall)
{
local PlayerController PC;
if ( Role == ROLE_Authority )
{
if ( !Wall.bStatic && !Wall.bWorldGeometry )
{
if ( Instigator == None || Instigator.Controller == None )
Wall.SetDelayedDamageInstigatorController( InstigatorController );
Wall.TakeDamage( Damage, instigator, Location, MomentumTransfer * Normal(Velocity), MyDamageType);
if (DamageRadius > 0 && Vehicle(Wall) != None && Vehicle(Wall).Health > 0)
Vehicle(Wall).DriverRadiusDamage(Damage, DamageRadius, InstigatorController, MyDamageType, MomentumTransfer, Location);
HurtWall = Wall;
}
MakeNoise(1.0);
}
Explode(Location + ExploWallOut * HitNormal, HitNormal);
if ( (ExplosionDecal != None) && (Level.NetMode != NM_DedicatedServer) )
{
if ( ExplosionDecal.Default.CullDistance != 0 )
{
PC = Level.GetLocalPlayerController();
if ( !PC.BeyondViewDistance(Location, ExplosionDecal.Default.CullDistance) )
Spawn(ExplosionDecal,self,,Location, rotator(-HitNormal));
else if ( (Instigator != None) && (PC == Instigator.Controller) && !PC.BeyondViewDistance(Location, 2*ExplosionDecal.Default.CullDistance) )
Spawn(ExplosionDecal,self,,Location, rotator(-HitNormal));
}
else
Spawn(ExplosionDecal,self,,Location, rotator(-HitNormal));
}
HurtWall = None;
}
simulated function BlowUp(vector HitLocation)
{
HurtRadius(Damage,DamageRadius, MyDamageType, MomentumTransfer, HitLocation );
if ( Role == ROLE_Authority )
MakeNoise(1.0);
}
simulated function Explode(vector HitLocation, vector HitNormal)
{
Destroy();
}
simulated final function RandSpin(float spinRate)
{
DesiredRotation = RotRand();
RotationRate.Yaw = spinRate * 2 *FRand() - spinRate;
RotationRate.Pitch = spinRate * 2 *FRand() - spinRate;
RotationRate.Roll = spinRate * 2 *FRand() - spinRate;
}
simulated static function float GetRange()
{
if (default.LifeSpan == 0.0)
return 15000;
else
return (default.MaxSpeed * default.LifeSpan);
}
function bool IsStationary()
{
return false;
}
// called if bScriptPostRender is true
simulated event PostRender2D(Canvas C, float ScreenLocX, float ScreenLocY);
defaultproperties
{
bCanBeDamaged=true
bAcceptsProjectors=false
bUseCylinderCollision=true
DamageRadius=+220.0
MaxSpeed=+02000.000000
DrawType=DT_Mesh
Texture=S_Camera
SoundVolume=0
CollisionRadius=+00000.000000
CollisionHeight=+00000.000000
bCollideActors=True
bCollideWorld=True
bNetTemporary=true
bGameRelevant=true
bReplicateInstigator=true
Physics=PHYS_Projectile
LifeSpan=+0014.000000
NetPriority=+00002.500000
MyDamageType=class'DamageType'
RemoteRole=ROLE_SimulatedProxy
bUnlit=true
TossZ=+100.0
bNetInitialRotation=true
bDisturbFluidSurface=true
}