520 lines
16 KiB
Ucode
520 lines
16 KiB
Ucode
//=============================================================================
|
|
// SRHumanPawn
|
|
//=============================================================================
|
|
class SRHumanPawn extends KFHumanPawn_Story;
|
|
|
|
var ClientPerkRepLink PerkLink;
|
|
var transient float CashTossTimer,LongTossCashTimer;
|
|
var transient byte LongTossCashCount;
|
|
|
|
// Override bad coding in StoryGame
|
|
simulated function Fire( optional float F )
|
|
{
|
|
Super(Pawn).Fire(F);
|
|
}
|
|
function AddDefaultInventory()
|
|
{
|
|
if( KFStoryGameInfo(Level.Game)!=none )
|
|
Super.AddDefaultInventory();
|
|
else Super(KFHumanPawn).AddDefaultInventory();
|
|
}
|
|
|
|
function ServerSellAmmo( Class<Ammunition> AClass );
|
|
|
|
final function bool HasWeaponClass( class<Inventory> IC, optional out Inventory Res )
|
|
{
|
|
local Inventory I;
|
|
|
|
for ( I=Inventory; I!=None; I=I.Inventory )
|
|
if( I.Class==IC )
|
|
{
|
|
Res = I;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
final function ClientPerkRepLink FindStats()
|
|
{
|
|
local LinkedReplicationInfo L;
|
|
|
|
if( Controller==None || Controller.PlayerReplicationInfo==None )
|
|
return None;
|
|
for( L=Controller.PlayerReplicationInfo.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo )
|
|
if( ClientPerkRepLink(L)!=None )
|
|
return ClientPerkRepLink(L);
|
|
return None;
|
|
}
|
|
|
|
function ServerBuyWeapon( Class<Weapon> WClass, float Weight )
|
|
{
|
|
local float Price;
|
|
local int OtherPrice;
|
|
local Inventory I,OI;
|
|
local class<KFWeapon> SecType;
|
|
|
|
if( !CanBuyNow() || Class<KFWeapon>(WClass)==None || Class<KFWeaponPickup>(WClass.Default.PickupClass)==None || HasWeaponClass(WClass) )
|
|
Return;
|
|
|
|
// Validate if allowed to buy that weapon.
|
|
if( PerkLink==None )
|
|
PerkLink = FindStats();
|
|
if( PerkLink!=None && !PerkLink.CanBuyPickup(Class<KFWeaponPickup>(WClass.Default.PickupClass)) )
|
|
return;
|
|
|
|
Price = class<KFWeaponPickup>(WClass.Default.PickupClass).Default.Cost;
|
|
|
|
if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
|
|
Price *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), WClass.Default.PickupClass);
|
|
|
|
Weight = Class<KFWeapon>(WClass).Default.Weight;
|
|
|
|
if( class'DualWeaponsManager'.Static.IsDualWeapon(WClass,SecType) )
|
|
{
|
|
if( WClass!=class'Dualies' && HasWeaponClass(SecType,OI) )
|
|
{
|
|
Weight-=SecType.Default.Weight;
|
|
Price*=0.5f;
|
|
OtherPrice = KFWeapon(OI).SellValue;
|
|
if( OtherPrice==-1 )
|
|
{
|
|
OtherPrice = class<KFWeaponPickup>(SecType.Default.PickupClass).Default.Cost * 0.75;
|
|
if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
|
|
OtherPrice *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), SecType.Default.PickupClass);
|
|
}
|
|
}
|
|
}
|
|
else if( class'DualWeaponsManager'.Static.HasDualies(WClass,Inventory) )
|
|
return;
|
|
|
|
Price = int(Price); // Truncuate price.
|
|
|
|
if( Weight>0 && !CanCarry(Weight) )
|
|
{
|
|
ClientMessage("Error: "$WClass.Name$" is too heavy ("$CurrentWeight$"+"$Weight$">"$MaxCarryWeight$")");
|
|
return;
|
|
}
|
|
if ( PlayerReplicationInfo.Score<Price )
|
|
{
|
|
ClientMessage("Error: "$WClass.Name$" is too expensive ("$int(Price)$">"$int(PlayerReplicationInfo.Score)$")");
|
|
Return;
|
|
}
|
|
|
|
I = Spawn(WClass);
|
|
if ( I != none )
|
|
{
|
|
if ( KFGameType(Level.Game) != none )
|
|
KFGameType(Level.Game).WeaponSpawned(I);
|
|
|
|
KFWeapon(I).UpdateMagCapacity(PlayerReplicationInfo);
|
|
KFWeapon(I).FillToInitialAmmo();
|
|
KFWeapon(I).SellValue = Price * 0.75;
|
|
if( OtherPrice>0 )
|
|
KFWeapon(I).SellValue+=OtherPrice;
|
|
I.GiveTo(self);
|
|
PlayerReplicationInfo.Score -= Price;
|
|
ClientForceChangeWeapon(I);
|
|
}
|
|
else ClientMessage("Error: "$WClass.Name$" failed to spawn.");
|
|
|
|
SetTraderUpdate();
|
|
}
|
|
function ServerSellWeapon( Class<Weapon> WClass )
|
|
{
|
|
local Inventory I;
|
|
local KFWeapon NewWep;
|
|
local float Price;
|
|
local class<KFWeapon> SecType;
|
|
|
|
if ( !CanBuyNow() || Class<KFWeapon>(WClass) == none || Class<KFWeaponPickup>(WClass.Default.PickupClass)==none
|
|
|| Class<KFWeapon>(WClass).Default.bKFNeverThrow )
|
|
{
|
|
SetTraderUpdate();
|
|
Return;
|
|
}
|
|
|
|
for ( I = Inventory; I != none; I = I.Inventory )
|
|
{
|
|
if ( I.Class==WClass )
|
|
{
|
|
if ( KFWeapon(I) != none && KFWeapon(I).SellValue != -1 )
|
|
Price = KFWeapon(I).SellValue;
|
|
else
|
|
{
|
|
Price = (class<KFWeaponPickup>(WClass.default.PickupClass).default.Cost * 0.75);
|
|
|
|
if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
|
|
Price *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), WClass.Default.PickupClass);
|
|
}
|
|
|
|
if( class'DualWeaponsManager'.Static.IsDualWeapon(WClass,SecType) )
|
|
{
|
|
NewWep = Spawn(SecType);
|
|
if( WClass!=class'Dualies' )
|
|
{
|
|
Price *= 0.5f;
|
|
NewWep.SellValue = Price;
|
|
}
|
|
NewWep.GiveTo(self);
|
|
}
|
|
|
|
if ( I==Weapon || I==PendingWeapon )
|
|
{
|
|
ClientCurrentWeaponSold();
|
|
}
|
|
|
|
PlayerReplicationInfo.Score += int(Price);
|
|
|
|
I.Destroy();
|
|
|
|
SetTraderUpdate();
|
|
|
|
if ( KFGameType(Level.Game)!=none )
|
|
KFGameType(Level.Game).WeaponDestroyed(WClass);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function AddBlur(Float BlurDuration, float Intensity)
|
|
{
|
|
if( KFPC!=none && Viewport(KFPC.Player)!=None )
|
|
Super.AddBlur(BlurDuration,Intensity);
|
|
}
|
|
simulated function DoHitCamEffects(vector HitDirection, float JarrScale, float BlurDuration, float JarDurationScale )
|
|
{
|
|
if( KFPC!=none && Viewport(KFPC.Player)!=None )
|
|
Super.DoHitCamEffects(HitDirection,JarrScale,BlurDuration,JarDurationScale);
|
|
}
|
|
simulated function StopHitCamEffects()
|
|
{
|
|
if( KFPC!=none && Viewport(KFPC.Player)!=None )
|
|
Super.StopHitCamEffects();
|
|
}
|
|
|
|
exec function TossCash( int Amount )
|
|
{
|
|
// To fix cash tossing exploit.
|
|
if( CashTossTimer<Level.TimeSeconds && (LongTossCashTimer<Level.TimeSeconds || LongTossCashCount<20) )
|
|
{
|
|
Super.TossCash(Max(Amount,50));
|
|
CashTossTimer = Level.TimeSeconds+0.1f;
|
|
if( LongTossCashTimer<Level.TimeSeconds )
|
|
{
|
|
LongTossCashTimer = Level.TimeSeconds+5.f;
|
|
LongTossCashCount = 0;
|
|
}
|
|
else ++LongTossCashCount;
|
|
}
|
|
}
|
|
|
|
function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
|
|
{
|
|
local vector shotDir, hitLocRel, deathAngVel, shotStrength;
|
|
local float maxDim;
|
|
local string RagSkelName;
|
|
local KarmaParamsSkel skelParams;
|
|
local bool PlayersRagdoll;
|
|
local PlayerController pc;
|
|
|
|
if ( Level.NetMode != NM_DedicatedServer )
|
|
{
|
|
// Is this the local player's ragdoll?
|
|
if(OldController != None)
|
|
pc = PlayerController(OldController);
|
|
if( pc != none && pc.ViewTarget == self )
|
|
PlayersRagdoll = true;
|
|
|
|
// In low physics detail, if we were not just controlling this pawn,
|
|
// and it has not been rendered in 3 seconds, just destroy it.
|
|
if( !PlayersRagdoll && (Level.TimeSeconds-LastRenderTime)>3 )
|
|
{
|
|
GoTo'NonRagdoll';
|
|
return;
|
|
}
|
|
|
|
// Try and obtain a rag-doll setup. Use optional 'override' one out of player record first, then use the species one.
|
|
if( RagdollOverride != "")
|
|
RagSkelName = RagdollOverride;
|
|
else if(Species != None)
|
|
RagSkelName = Species.static.GetRagSkelName( GetMeshName() );
|
|
else RagSkelName = "Male1"; // Otherwise assume it is Male1 ragdoll were after here.
|
|
|
|
KMakeRagdollAvailable();
|
|
|
|
if( KIsRagdollAvailable() && RagSkelName != "" )
|
|
{
|
|
skelParams = KarmaParamsSkel(KParams);
|
|
skelParams.KSkeleton = RagSkelName;
|
|
|
|
// Stop animation playing.
|
|
StopAnimating(true);
|
|
|
|
if( DamageType != None )
|
|
{
|
|
if ( DamageType.default.bLeaveBodyEffect )
|
|
TearOffMomentum = vect(0,0,0);
|
|
|
|
if( DamageType.default.bKUseOwnDeathVel )
|
|
{
|
|
RagDeathVel = DamageType.default.KDeathVel;
|
|
RagDeathUpKick = DamageType.default.KDeathUpKick;
|
|
}
|
|
}
|
|
|
|
// Set the dude moving in direction he was shot in general
|
|
shotDir = Normal(GetTearOffMomemtum());
|
|
shotStrength = RagDeathVel * shotDir;
|
|
|
|
// Calculate angular velocity to impart, based on shot location.
|
|
hitLocRel = TakeHitLocation - Location;
|
|
|
|
// We scale the hit location out sideways a bit, to get more spin around Z.
|
|
hitLocRel.X *= RagSpinScale;
|
|
hitLocRel.Y *= RagSpinScale;
|
|
|
|
// If the tear off momentum was very small for some reason, make up some angular velocity for the pawn
|
|
if( VSize(GetTearOffMomemtum()) < 0.01 )
|
|
{
|
|
//Log("TearOffMomentum magnitude of Zero");
|
|
deathAngVel = VRand() * 18000.0;
|
|
}
|
|
else deathAngVel = RagInvInertia * (hitLocRel Cross shotStrength);
|
|
|
|
// Set initial angular and linear velocity for ragdoll.
|
|
// Scale horizontal velocity for characters - they run really fast!
|
|
if ( DamageType.Default.bRubbery )
|
|
skelParams.KStartLinVel = vect(0,0,0);
|
|
if ( Damagetype.default.bKUseTearOffMomentum )
|
|
skelParams.KStartLinVel = GetTearOffMomemtum() + Velocity;
|
|
else
|
|
{
|
|
skelParams.KStartLinVel.X = 0.6 * Velocity.X;
|
|
skelParams.KStartLinVel.Y = 0.6 * Velocity.Y;
|
|
skelParams.KStartLinVel.Z = 1.0 * Velocity.Z;
|
|
skelParams.KStartLinVel += shotStrength;
|
|
}
|
|
// If not moving downwards - give extra upward kick
|
|
if( !DamageType.default.bLeaveBodyEffect && !DamageType.Default.bRubbery && (Velocity.Z > -10) )
|
|
skelParams.KStartLinVel.Z += RagDeathUpKick;
|
|
|
|
if ( DamageType.Default.bRubbery )
|
|
{
|
|
Velocity = vect(0,0,0);
|
|
skelParams.KStartAngVel = vect(0,0,0);
|
|
}
|
|
else
|
|
{
|
|
skelParams.KStartAngVel = deathAngVel;
|
|
|
|
// Set up deferred shot-bone impulse
|
|
maxDim = Max(CollisionRadius, CollisionHeight);
|
|
|
|
skelParams.KShotStart = TakeHitLocation - (1 * shotDir);
|
|
skelParams.KShotEnd = TakeHitLocation + (2*maxDim*shotDir);
|
|
skelParams.KShotStrength = RagShootStrength;
|
|
}
|
|
|
|
// If this damage type causes convulsions, turn them on here.
|
|
if(DamageType != None && DamageType.default.bCauseConvulsions)
|
|
{
|
|
RagConvulseMaterial=DamageType.default.DamageOverlayMaterial;
|
|
skelParams.bKDoConvulsions = true;
|
|
}
|
|
|
|
// Turn on Karma collision for ragdoll.
|
|
KSetBlockKarma(true);
|
|
|
|
// Set physics mode to ragdoll.
|
|
// This doesn't actaully start it straight away, it's deferred to the first tick.
|
|
SetPhysics(PHYS_KarmaRagdoll);
|
|
|
|
// If viewing this ragdoll, set the flag to indicate that it is 'important'
|
|
if( PlayersRagdoll )
|
|
skelParams.bKImportantRagdoll = true;
|
|
|
|
skelParams.bRubbery = DamageType.Default.bRubbery;
|
|
bRubbery = DamageType.Default.bRubbery;
|
|
|
|
skelParams.KActorGravScale = RagGravScale;
|
|
|
|
return;
|
|
}
|
|
// jag
|
|
}
|
|
|
|
NonRagdoll:
|
|
// non-ragdoll death fallback
|
|
LifeSpan = 0.2f;
|
|
}
|
|
|
|
function float AssessThreatTo(KFMonsterController Monster, optional bool CheckDistance)
|
|
{
|
|
local float ThreatRating;
|
|
local Inventory CurInv;
|
|
local KF_StoryInventoryItem StoryInv;
|
|
|
|
ThreatRating = FMax(Super(KFHumanPawn).AssessThreatTo(Monster,CheckDistance),0.1f);
|
|
|
|
/* Factor in story Items which adjust your desirability to ZEDs */
|
|
for ( CurInv = Inventory; CurInv != none; CurInv = CurInv.Inventory )
|
|
{
|
|
StoryInv = KF_StoryInventoryItem(CurInv);
|
|
if(StoryInv != none)
|
|
ThreatRating *= StoryInv.AIThreatModifier;
|
|
}
|
|
|
|
return ThreatRating;
|
|
}
|
|
|
|
simulated final function int GetFloorSurface()
|
|
{
|
|
local int SurfaceTypeID;
|
|
local actor A;
|
|
local vector HL,HN,Start,End;
|
|
local material FloorMat;
|
|
|
|
if ( (Base!=None) && (!Base.IsA('LevelInfo')) && (Base.SurfaceType!=0) )
|
|
SurfaceTypeID = Base.SurfaceType;
|
|
else
|
|
{
|
|
Start = Location - Vect(0,0,1)*CollisionHeight;
|
|
End = Start - Vect(0,0,16);
|
|
A = Trace(hl,hn,End,Start,false,,FloorMat);
|
|
if (FloorMat !=None)
|
|
SurfaceTypeID = FloorMat.SurfaceType;
|
|
}
|
|
return SurfaceTypeID;
|
|
}
|
|
simulated function Sound GetSound(xPawnSoundGroup.ESoundType soundType)
|
|
{
|
|
local int SurfaceTypeID;
|
|
|
|
if( SoundGroupClass==None )
|
|
SoundGroupClass = Class'KFMaleSoundGroup';
|
|
if( soundType == EST_Land || soundType == EST_Jump )
|
|
SurfaceTypeID = GetFloorSurface();
|
|
return SoundGroupClass.static.GetSound(soundType, SurfaceTypeID);
|
|
}
|
|
|
|
// The player wants to switch to weapon group number F.
|
|
simulated function SwitchWeapon(byte F)
|
|
{
|
|
local Weapon DesiredWeap;
|
|
local bool AllowSwitch;
|
|
|
|
// Fixed script warnings.
|
|
if( Weapon!=None && Weapon.Inventory!=None )
|
|
DesiredWeap = Weapon.Inventory.WeaponChange(F, false);
|
|
if(DesiredWeap == none && Inventory!=None )
|
|
DesiredWeap = Inventory.WeaponChange(F, true);
|
|
|
|
if(DesiredWeap != none)
|
|
AllowSwitch = AllowHoldWeapon(DesiredWeap);
|
|
|
|
if( AllowSwitch )
|
|
Super(KFPawn).SwitchWeapon(F);
|
|
}
|
|
|
|
simulated function tick(float DeltaTime)
|
|
{
|
|
local KF_StoryPRI PRI;
|
|
|
|
super(KFPawn).Tick(DeltaTime);
|
|
|
|
if( IsLocallyControlled() && !bUsingHitBlur && BlurFadeOutTime > 0 )
|
|
{
|
|
BlurFadeOutTime -= DeltaTime;
|
|
DeltaTime = BlurFadeOutTime/StartingBlurFadeOutTime * CurrentBlurIntensity;
|
|
|
|
if( BlurFadeOutTime <= 0 )
|
|
{
|
|
BlurFadeOutTime = 0;
|
|
StopHitCamEffects();
|
|
}
|
|
else if( bUseBlurEffect && KFPC!=none && !KFPC.PostFX_IsReady() )
|
|
{
|
|
if( CameraEffectFound != none )
|
|
UnderWaterBlur(CameraEffectFound).BlurAlpha = Lerp( DeltaTime, 255, UnderWaterBlur(CameraEffectFound).default.BlurAlpha );
|
|
else KFPC.SetBlur(DeltaTime);
|
|
}
|
|
}
|
|
|
|
/* Replicated Location stuff - for tracking this pawn's position to display icons when its not relevant */
|
|
PRI = KF_StoryPRI(PlayerReplicationInfo);
|
|
if(PRI != none && PRI.GetFloatingIconMat() != none)
|
|
{
|
|
if(Role == Role_Authority) // server authoritative
|
|
{
|
|
if(PRI.GetOwnerPawn() != self)
|
|
{
|
|
PRI.SetOwnerPawn(self);
|
|
PRI.NetUpdateTime = Level.TimeSeconds - 1;
|
|
}
|
|
KF_StoryPRI(PlayerReplicationInfo).SetReplicatedPawnLoc(GetHoverIconPosition());
|
|
}
|
|
else if(bDeleteMe || bPendingDelete) // simulated proxy.
|
|
{
|
|
PRI.SetOwnerPawn(none);
|
|
PRI.NetUpdateTime = Level.TimeSeconds - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function SetWeaponAttachment(WeaponAttachment NewAtt)
|
|
{
|
|
local byte i;
|
|
|
|
Super.SetWeaponAttachment(NewAtt);
|
|
|
|
if( KFWeaponAttachment(WeaponAttachment)!=None )
|
|
{
|
|
// Veryify valid animations.
|
|
for( i=0; i<4; ++i )
|
|
{
|
|
DoubleJumpAnims[i] = TakeoffAnims[i];
|
|
if( !HasAnim(MovementAnims[i]) )
|
|
MovementAnims[i] = Default.MovementAnims[i];
|
|
if( !HasAnim(WalkAnims[i]) )
|
|
MovementAnims[i] = Default.WalkAnims[i];
|
|
}
|
|
if( !HasAnim(IdleWeaponAnim) )
|
|
MovementAnims[i] = Default.IdleWeaponAnim;
|
|
if( !HasAnim(IdleRestAnim) )
|
|
MovementAnims[i] = Default.IdleRestAnim;
|
|
if( !HasAnim(IdleChatAnim) )
|
|
MovementAnims[i] = Default.IdleChatAnim;
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
InvisMaterial=Shader'KF_Specimens_Trip_T.patriarch_invisible'
|
|
WallDodgeAnims(0)="JumpF_Takeoff"
|
|
WallDodgeAnims(1)="JumpL_Takeoff"
|
|
WallDodgeAnims(2)="JumpL_Takeoff"
|
|
WallDodgeAnims(3)="JumpR_Takeoff"
|
|
MovementAnims(0)="JogF_Pipe"
|
|
MovementAnims(1)="JogB_Pipe"
|
|
MovementAnims(2)="JogL_Pipe"
|
|
MovementAnims(3)="JogR_Pipe"
|
|
SwimAnims(0)="WalkF_Flamethrower"
|
|
SwimAnims(1)="WalkB_Flamethrower"
|
|
SwimAnims(2)="WalkL_Flamethrower"
|
|
SwimAnims(3)="WalkR_Flamethrower"
|
|
CrouchAnims(0)="CHwalkF_Pipe"
|
|
CrouchAnims(1)="CHwalkB_Pipe"
|
|
CrouchAnims(2)="CHwalkL_Pipe"
|
|
CrouchAnims(3)="CHwalkR_Pipe"
|
|
WalkAnims(0)="WalkF_Flamethrower"
|
|
WalkAnims(1)="WalkB_Flamethrower"
|
|
WalkAnims(2)="WalkL_Flamethrower"
|
|
WalkAnims(3)="WalkR_Flamethrower"
|
|
DoubleJumpAnims(0)="JumpF_Takeoff"
|
|
DoubleJumpAnims(1)="JumpL_Takeoff"
|
|
DoubleJumpAnims(2)="JumpL_Takeoff"
|
|
DoubleJumpAnims(3)="JumpR_Takeoff"
|
|
IdleWeaponAnim="Idle_Pipe"
|
|
IdleRestAnim="Idle_Pipe"
|
|
IdleChatAnim="Idle_Pipe"
|
|
}
|