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

343 lines
10 KiB
Ucode

//=============================================================================
// © 2003 Matt 'SquirrelZero' Farber
//=============================================================================
// "UT2k3 Ultimate Flashlight"
// I wrote this using _both_ a dynamic projector and a dynamic light, with the
// dynamic projector as the focal point and lightsource providing general
// ambient illumination. Either one by itself is pretty dull, but combine the
// two and you've got yourself a really nice effect if done correctly.
//=============================================================================
class Effect_TacLightProjector extends DynamicProjector;
// add a light too, there seems to be a problem with the projector darkening terrain sometimes
var Effect_TacLightGlow TacLightGlow;
var Weapon ValidWeapon;
var Pawn LightPawn;
var bool bHasLight;
var WeaponAttachment AssignedAttach;
var byte LightRot[2];
var KFPlayerController AssignedPC;
var bool bIsAssigned;
var vector WallHitLocation;
var() float ProjectorPullbackDist;
replication
{
// relevant variables needed by the client
reliable if (Role == ROLE_Authority)
LightPawn,bHasLight;
unreliable if( Role == ROLE_Authority && !bNetOwner && bHasLight )
LightRot;
}
// setup the pawn and controller variables, spawn the dynamic light
simulated function PostBeginPlay()
{
SetCollision(True, False, False);
if (Owner != None)
{
LightPawn = Pawn(Owner);
ValidWeapon = LightPawn.Weapon;
}
if( Level.NetMode==NM_DedicatedServer )
Return;
if( TacLightGlow==None )
TacLightGlow = spawn(class'Effect_TacLightGlow');
if( Class'KFPawn'.Default.bDetailedShadows )
{
AssignedPC = KFPlayerController(Level.GetLocalPlayerController());
if( AssignedPC!=None )
AddProjecting();
LightRadius = 1;
}
}
simulated function AddProjecting()
{
bIsAssigned = True;
AssignedPC.LightSources[AssignedPC.LightSources.Length] = Self;
}
simulated function RemoveProjecting()
{
local int i,l;
bIsAssigned = False;
l = AssignedPC.LightSources.Length;
for( i=0; i<l; i++ )
if( AssignedPC.LightSources[i]==Self )
{
AssignedPC.LightSources.Remove(i,1);
return;
}
}
simulated function Destroyed()
{
Super.Destroyed();
if( TacLightGlow!=None )
TacLightGlow.Destroy();
if( KFWeaponAttachment(AssignedAttach)!=None )
{
KFWeaponAttachment(AssignedAttach).TacBeamGone();
AssignedAttach = None;
}
if( bIsAssigned )
{
RemoveProjecting();
AssignedPC = None;
}
}
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
local vector StartTrace;
local coords LightBonePosition;
LightBonePosition = LightPawn.Weapon.GetBoneCoords('LightBone');
StartTrace = LightBonePosition.Origin - LightPawn.Weapon.Location;
StartTrace = StartTrace * 0.2;
StartTrace = StartTrace + LightBonePosition.XAxis * KFWeapon(LightPawn.Weapon).FirstPersonFlashlightOffset.X + LightBonePosition.YAxis * KFWeapon(LightPawn.Weapon).FirstPersonFlashlightOffset.Y + LightBonePosition.ZAxis * KFWeapon(LightPawn.Weapon).FirstPersonFlashlightOffset.Z;
StartTrace = LightPawn.Weapon.Location + StartTrace;
LightPawn.DrawDebugSphere(StartTrace, 10.0, 10, 255, 0, 0);
LightPawn.DrawDebugSphere(WallHitLocation, TacLightGlow.LightRadius, 10, 255, 255, 0);
Canvas.DrawText("LIGHT" @ Location @ "Hit="$WallHitLocation);
YPos += YL;
Canvas.DrawText("LightRadius: "$TacLightGlow.LightRadius$" LightBrightness: "$TacLightGlow.LightBrightness$" DrawScale = "$DrawScale$" FOV = "$FOV);
YPos += YL;
}
// updates the taclight projector and dynamic light positions
simulated function Tick(float DeltaTime)
{
local vector StartTrace,EndTrace,X,HitLocation,HitNormal,AdjustedLocation;
local float BeamLength;
local rotator R;
local coords LightBonePosition;
local KFWeapon KFWeap;
local Actor HitActor;
if( Level.NetMode==NM_DedicatedServer )
{
if( LightPawn==none || LightPawn.Weapon==None || LightPawn.Weapon!=ValidWeapon )
{
Destroy();
return;
}
SetLocation(LightPawn.Location);
if( !bHasLight || LightPawn.Controller==None )
Return;
LightRot[0] = LightPawn.Controller.Rotation.Yaw/256;
LightRot[1] = LightPawn.Controller.Rotation.Pitch/256;
Return;
}
if (TacLightGlow == None)
return;
if( Level.NetMode!=NM_Client && (LightPawn == none || LightPawn.Weapon==None || LightPawn.Weapon!=ValidWeapon) )
{
DetachProjector();
Destroy();
return;
}
// we're changing its location and rotation, so detach it
DetachProjector();
// fallback
if( LightPawn==None || !bHasLight )
{
if (TacLightGlow != None)
TacLightGlow.bDynamicLight = false;
if( AssignedAttach!=None )
{
if( KFWeaponAttachment(AssignedAttach)!=None )
KFWeaponAttachment(AssignedAttach).TacBeamGone();
AssignedAttach = None;
}
if( bIsAssigned )
RemoveProjecting();
return;
}
if( Level.NetMode!=NM_Client || PlayerController(LightPawn.Controller)!=None )
{
if( PlayerController(LightPawn.Controller)==None || PlayerController(LightPawn.Controller).bBehindView || LightPawn.Weapon==None )
{
if( XPawn(LightPawn)!=None && XPawn(LightPawn).WeaponAttachment!=None )
StartTrace = XPawn(LightPawn).WeaponAttachment.Location;
else StartTrace = LightPawn.Location+LightPawn.EyePosition();
}
else
{
KFWeap = KFWeapon(LightPawn.Weapon);
LightBonePosition = KFWeap.GetBoneCoords('LightBone');
StartTrace = LightBonePosition.Origin - KFWeap.Location;
StartTrace = StartTrace * 0.2;
StartTrace = StartTrace + LightBonePosition.XAxis * KFWeap.FirstPersonFlashlightOffset.X
+ LightBonePosition.YAxis * KFWeap.FirstPersonFlashlightOffset.Y
+ LightBonePosition.ZAxis * KFWeap.FirstPersonFlashlightOffset.Z;
StartTrace = KFWeap.Location + StartTrace;
}
if ( LightPawn.IsLocallyControlled() && PlayerController(LightPawn.Controller) != none && !PlayerController(LightPawn.Controller).bBehindView )
{
X = LightPawn.Weapon.GetBoneCoords('LightBone').XAxis;
R = rotator(X);
}
else if ( XPawn(LightPawn) != none && XPawn(LightPawn).WeaponAttachment != none )
{
if ( DualiesAttachment(XPawn(LightPawn).WeaponAttachment) != none )
{
if ( DualiesAttachment(XPawn(LightPawn).WeaponAttachment).Mesh == DualiesAttachment(XPawn(LightPawn).WeaponAttachment).BrotherMesh )
{
X = DualiesAttachment(XPawn(LightPawn).WeaponAttachment).Brother.GetBoneCoords('FlashLight').XAxis;
R = rotator(X);
}
else
{
X = XPawn(LightPawn).WeaponAttachment.GetBoneCoords('FlashLight').XAxis;
R = rotator(X);
}
}
else
{
X = XPawn(LightPawn).WeaponAttachment.GetBoneCoords('FlashLight').XAxis;
R = rotator(X);
}
}
else
{
R = LightPawn.Controller.Rotation;
X = vector(R);
}
if( Level.NetMode!=NM_Client )
{
LightRot[0] = R.Yaw/256;
LightRot[1] = R.Pitch/256;
}
}
else
{
if( XPawn(LightPawn)!=None && XPawn(LightPawn).WeaponAttachment!=None && (Level.TimeSeconds-LightPawn.LastRenderTime)<1 )
StartTrace = XPawn(LightPawn).WeaponAttachment.Location;
else StartTrace = LightPawn.Location+LightPawn.EyePosition();
R.Yaw = LightRot[0]*256;
R.Pitch = LightRot[1]*256;
X = vector(R);
}
// not too far out, we don't want a flashlight that can shine across the map
EndTrace = StartTrace + 1800*X;
HitActor = Trace(HitLocation,HitNormal,EndTrace,StartTrace,true);
if( HitActor == none )
HitLocation = EndTrace;
WallHitLocation = HitLocation;
// find out how far the first hit was
BeamLength = VSize(StartTrace-HitLocation);
// this makes a neat focus effect when you get close to a wall
if (BeamLength <= 90)
SetDrawScale(FMax(0.02,(BeamLength/90))*Default.DrawScale);
else SetDrawScale(Default.DrawScale);
FOV = Lerp((BeamLength / 1800.0),(default.FOV * 0.6),default.FOV);
// Don't let the projector penetrate the wall
if (BeamLength <= ProjectorPullbackDist)
{
SetLocation(StartTrace - X * (ProjectorPullbackDist - BeamLength));
}
else
{
SetLocation(StartTrace);
}
SetRotation(R);
// reattach it
AttachProjector();
// turns the dynamic light on if it's off
if (!TacLightGlow.bDynamicLight)
TacLightGlow.bDynamicLight = True;
// again, neat focus effect up close, starts earlier than the dynamic projector
if (BeamLength <= 100)
{
TacLightGlow.LightBrightness = TacLightGlow.Default.LightBrightness;
TacLightGlow.LightRadius = Lerp((BeamLength / 100.0),0.0,(TacLightGlow.Default.LightRadius * 1.25));
} // else we scale its radius and brightness depending on distance from the material
else
{
// fades the lightsource out as it moves farther away
TacLightGlow.LightBrightness = TacLightGlow.Default.LightBrightness * (1.0 - (BeamLength / 1800.0));
// this makes the light act more like a spotlight, resizing depending on distance
TacLightGlow.LightRadius = FMin((TacLightGlow.Default.LightRadius * 4),Lerp((BeamLength / 900.0),TacLightGlow.Default.LightRadius,TacLightGlow.Default.LightRadius * 4));
}
AdjustedLocation = HitLocation;
// Pull back a bit so the light doesn't go through the terrain
if( HitActor != none && HitActor.IsA('TerrainInfo') )
{
TacLightGlow.SetLocation(AdjustedLocation - 50 * X );
}
else
{
TacLightGlow.SetLocation(AdjustedLocation);
}
if( XPawn(LightPawn)!=None && XPawn(LightPawn).WeaponAttachment!=AssignedAttach )
{
if( KFWeaponAttachment(AssignedAttach)!=None )
KFWeaponAttachment(AssignedAttach).TacBeamGone();
AssignedAttach = XPawn(LightPawn).WeaponAttachment;
}
if( KFWeaponAttachment(AssignedAttach)!=None )
KFWeaponAttachment(AssignedAttach).UpdateTacBeam(BeamLength);
if( !bIsAssigned && AssignedPC!=None )
AddProjecting();
}
defaultproperties
{
ProjTexture=Texture'KillingFloorWeapons.Dualies.LightCircle'
MaterialBlendingOp=PB_Modulate
FrameBufferBlendingOp=PB_Add
FOV=50 // 5
MaxTraceDistance=1600
bClipBSP=true//True
bProjectOnUnlit=True
bGradient=True
bProjectBSP=True
bProjectOnAlpha=True
bProjectOnBackfaces = false
bProjectOnParallelBSP=True
bLightChanged=True
RemoteRole=ROLE_SimulatedProxy
DrawScale=0.1
bNotOnDedServer=False
bProjectActor=True
bNoProjectOnOwner=true
bHidden=False
DrawType=DT_None
bSkipActorPropertyReplication=True
ProjectorPullbackDist=25
}