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

610 lines
15 KiB
Ucode

/*
--------------------------------------------------------------
KFHumanPawn_Story
--------------------------------------------------------------
Custom Pawn class for use in Killing Floor 'Story' maps.
Author : Alex Quick
--------------------------------------------------------------
*/
class KFHumanPawn_Story extends KFHumanPawn ;
/* true if this pawn has had its currently held gear stored at a checkpoint */
var bool bSavedLoadout;
/* Pawn is currently holding a KF_StoryInventoryItem actor */
var bool bHasStoryItem;
replication
{
reliable if ( bNetDirty && (Role == Role_Authority) )
bHasStoryItem;
}
simulated function Fire( optional float F )
{
local Controller C;
for ( C = Level.ControllerList; C != None; C = C.NextController )
{
C.ReceiveWarning(self,0,vector(Rotation));
}
Super.Fire(F);
}
function bool DoJump( bool bUpdating )
{
local float JumpModifier;
JumpModifier = GetJumpZModifier();
/* Also ramp up the allowed fallspeed so larger jumps dont instantly kill us */
MaxFallSpeed = default.MaxFallSpeed * JumpModifier;
JumpZ = default.JumpZ * JumpModifier ;
return Super.DoJump(bUpdating);
}
simulated function float GetJumpZModifier()
{
local inventory I;
local float Modifier;
local KF_StoryInventoryItem Storyinv;
Modifier = 1.f;
for ( I = Inventory; I != none; I = I.Inventory )
{
StoryInv = KF_StoryInventoryItem(I);
if(StoryInv != none)
{
Modifier *= StoryInv.JumpZModifier;
}
}
return Modifier;
}
function SetHasStoryItem( bool bHasItem )
{
bHasStoryItem = bHasItem;
}
simulated event ModifyVelocity(float DeltaTime, vector OldVelocity)
{
local inventory I;
local KF_StoryInventoryItem Storyinv;
Super.ModifyVelocity(DeltaTime,OldVelocity);
for ( I = Inventory; I != none; I = I.Inventory )
{
StoryInv = KF_StoryInventoryItem(I);
if(StoryInv != none && StoryInv.bUseForcedGroundSpeed)
{
GroundSpeed = StoryInv.ForcedGroundSpeed ;
}
}
}
simulated function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
super.TakeDamage( Damage, InstigatedBY, Hitlocation, Momentum, damageType, HitIndex );
// HandleStoryAchievements is only used for Steamland. In fact, it screws up KFO-Transit!
if( class'KFGameType'.static.GetCurrentMapName(Level) ~= "KFO-Steamland" )
{
HandleStoryAchievements();
}
}
function HandleStoryAchievements()
{
local KFGameReplicationInfo KFGRI;
if ( bHasStoryItem )
{
KFGRI = KFGameReplicationInfo( Level.GRI );
if( KFGRI != none )
{
KFGRI.bObjectiveAchievementFailed = true;
}
}
}
function bool AddInventory( inventory NewItem )
{
if( !super.AddInventory(NewItem) )
return false;
if( KF_StoryInventoryItem(NewItem) != none )
{
CurrentWeight += KF_StoryInventoryItem(NewItem).Weight;
}
return true;
}
// Remove Item from this pawn's inventory, if it exists.
function DeleteInventory( inventory Item )
{
local Inventory I;
local bool bFoundItem;
if ( Role != ROLE_Authority )
{
return;
}
for ( I = Inventory; I != none; I = I.Inventory )
{
if ( I == Item )
{
bFoundItem = true;
}
}
if ( bFoundItem )
{
if ( KF_StoryInventoryItem(Item) != none )
{
CurrentWeight -= KF_StoryInventoryItem(Item).Weight;
}
}
super.DeleteInventory(Item);
}
/* Returns true if this pawn is able to hold a weapon of the supplied type */
simulated function bool AllowHoldWeapon(Weapon InWeapon)
{
local bool Result;
local Inventory Inv;
local KF_StoryInventoryItem StoryInv;
Result = true;
/* Query our inventory items to see if they restrict any weaponry we're holding */
for ( Inv=Inventory; Inv!=None; Inv=Inv.Inventory )
{
StoryInv = KF_StoryInventoryItem(Inv);
if(StoryInv != none)
{
if(!StoryInv.AllowHoldWeapon(InWeapon))
{
Result = false;
break;
}
}
}
// log("*********************************************");
// log("Allow Hold Weapon of Type : "@InWeapon@" - "@Result);
// log("*********************************************");
if(!Result)
{
PlayerController(Controller).ReceiveLocalizedMessage(Class'KFMainMessages',5,,,InWeapon);
}
Return Result;
}
simulated function Weapon FindUseableWeaponFor(KF_StoryInventoryItem I)
{
local Inventory Inv;
local Weapon UseableWeap;
local Dummy_JoggingWeapon DummyWeap;
for( Inv=Inventory; Inv!=None; Inv=Inv.Inventory )
{
if(Weapon(inv) != none && I.AllowHoldWeapon(Weapon(Inv),true))
{
UseableWeap = Weapon(Inv);
}
}
if(UseableWeap == none )
{
DummyWeap = Dummy_JoggingWeapon(FindInventoryType(class 'Dummy_JoggingWeapon'));
if(DummyWeap != none)
{
UseableWeap = DummyWeap ;
}
}
log("Found useable weapon : "@UseableWeap);
return UseableWeap;
}
/* Cache a list of equipment the pawn was carrying at the time of his death */
simulated function SaveLoadOut()
{
local Inventory Inv;
local int Count;
local KFPlayerController_Story PC;
PC = KFPlayerController_Story(Controller);
if(PC == none )
{
return;
}
/* Save equipment that we're holding */
for( Inv=Inventory; Inv!=None ;Inv=Inv.Inventory )
{
if(KFWeapon(Inv) != none)
{
PC.SavedLoadOut[Count] = string(Inv.class) ;
PC.SavedAmmo[Count] = Weapon(Inv).AmmoAmount(0);
PC.SavedMagAmmo[Count] = KFWeapon(Inv).MagAmmoRemaining;
Count ++ ;
}
}
PC.SavedHealth = Health;
PC.SavedArmor = ShieldStrength ;
}
function bool RetrieveSavedLoadOut(Controller C)
{
local int i, Index;
local KFPlayerController_Story PC;
PC = KFPLayerController_Story(C);
if(PC != none && PC.CurrentCheckPoint != none)
{
/* Add Saved equipment from our controller .. */
for(i = 0 ; i < Min(PC.SavedLoadOut.length,ArrayCount(RequiredEquipment)) ; i ++)
{
if(RequiredEquipment[i] == "" && PC.SavedLoadOut[i] != "")
{
RequiredEquipment[i] = PC.SavedLoadOut[i] ;
Index ++ ;
}
}
return Index > 0;
}
return false;
}
function AddDefaultInventory()
{
local KFLevelRules_Story StoryRules;
local int i,InvCount;
local Inventory Inv;
local KFPlayerController_Story SPC;
local bool bUseCheckPointGear;
local float MaxAmmo,CurrentAmmo;
/* Clear pawn equipment defaults */
for(i = 0 ; i < ArrayCount(RequiredEquipment) ; i ++)
{
RequiredEquipment[i] = "" ;
}
/* Base gear should fill from the level rules */
Level.Game.AddGameSpecificInventory(self);
/* next we Add saved gear - stuff we were carrying when we died */
bUseCheckPointGear = RetrieveSavedLoadOut(Controller);
Super(UnrealPawn).AddDefaultInventory();
/* next add perk specific gear - on request */
if(KFStoryGameInfo(Level.Game) != none)
{
StoryRules = KFStoryGameInfo(Level.Game).StoryRules ;
if(StoryRules != none && StoryRules.bAllowPerkStartingWeaps)
{
if ( KFPlayerReplicationInfo(PlayerReplicationInfo) != none && KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
{
KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.AddDefaultInventory(KFPlayerReplicationInfo(PlayerReplicationInfo), self);
}
}
}
if(bUseCheckPointGear)
{
/* restore saved ammo values from our last checkpoint .. */
SPC = KFPlayerController_Story(Controller);
if(SPC != none)
{
for( Inv=Inventory; Inv!=None ;Inv=Inv.Inventory )
{
if(Weapon(Inv) != none)
{
Weapon(Inv).GetAmmoCount(MaxAmmo,CurrentAmmo);
Weapon(Inv).ConsumeAmmo(0,MaxAmmo,true);
Weapon(Inv).AddAmmo(SPC.SavedAmmo[InvCount],0);
KFWeapon(Inv).MagAmmoRemaining = SPC.SavedMagAmmo[InvCount];
InvCount ++;
}
}
}
}
}
simulated function Tick(float DeltaTime)
{
local KF_StoryPRI PRI;
Super.Tick(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 // simulated proxy.
{
if(bDeleteMe || bPendingDelete)
{
PRI.SetOwnerPawn(none);
PRI.NetUpdateTime = Level.TimeSeconds - 1;
}
}
}
}
/* position to render hovering icons at */
simulated function vector GetHoverIconPosition()
{
return Location + Vect(0,0,1)*CollisionHeight ;
}
/* Fixing a problem where Perk-Less players dont get charged for ammo */
function bool ServerBuyAmmo( Class<Ammunition> AClass, bool bOnlyClip )
{
local Inventory I;
local float Price;
local Ammunition AM;
local KFWeapon KW;
local int c;
local float UsedMagCapacity;
local class<KFVeterancyTypes> PlayerVeterancy;
if ( !CanBuyNow() || AClass == None )
{
SetTraderUpdate();
return false;
}
// Grab Players Veterancy for quick reference
if ( KFPlayerReplicationInfo(PlayerReplicationInfo) != none &&
KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none )
{
PlayerVeterancy = KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill;
}
else
{
PlayerVeterancy = class'KFVeterancyTypes';
}
for ( I=Inventory; I != none; I=I.Inventory )
{
if ( I.Class == AClass )
{
AM = Ammunition(I);
}
else if ( KW == None && KFWeapon(I) != None && (Weapon(I).AmmoClass[0] == AClass || Weapon(I).AmmoClass[1] == AClass) )
{
KW = KFWeapon(I);
}
}
if ( KW == none || AM == none )
{
SetTraderUpdate();
return false;
}
AM.MaxAmmo = AM.default.MaxAmmo;
if ( KFPlayerReplicationInfo(PlayerReplicationInfo) != none && PlayerVeterancy != none )
{
AM.MaxAmmo = int(float(AM.MaxAmmo) * PlayerVeterancy.static.AddExtraAmmoFor(KFPlayerReplicationInfo(PlayerReplicationInfo), AClass));
}
if ( AM.AmmoAmount >= AM.MaxAmmo )
{
SetTraderUpdate();
return false;
}
Price = class<KFWeaponPickup>(KW.PickupClass).default.AmmoCost * PlayerVeterancy.static.GetAmmoCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), KW.PickupClass); // Clip price.
if ( KW.bHasSecondaryAmmo && AClass == KW.FireModeClass[1].default.AmmoClass )
{
UsedMagCapacity = 1; // Secondary Mags always have a Mag Capacity of 1? KW.default.SecondaryMagCapacity;
}
else
{
UsedMagCapacity = KW.default.MagCapacity;
}
if( KW.PickupClass == class'HuskGunPickup' )
{
UsedMagCapacity = class<HuskGunPickup>(KW.PickupClass).default.BuyClipSize;
}
if ( bOnlyClip )
{
if ( KFPlayerReplicationInfo(PlayerReplicationInfo) != none && PlayerVeterancy != none )
{
if( KW.PickupClass == class'HuskGunPickup' )
{
c = UsedMagCapacity * PlayerVeterancy.static.AddExtraAmmoFor(KFPlayerReplicationInfo(PlayerReplicationInfo), AM.Class);
}
else
{
c = UsedMagCapacity * PlayerVeterancy.static.GetMagCapacityMod(KFPlayerReplicationInfo(PlayerReplicationInfo), KW);
}
}
else
{
c = UsedMagCapacity;
}
}
else
{
c = (AM.MaxAmmo-AM.AmmoAmount);
}
Price = int(float(c) / UsedMagCapacity * Price);
if ( PlayerReplicationInfo.Score < Price ) // Not enough CASH (so buy the amount you CAN buy).
{
c *= (PlayerReplicationInfo.Score/Price);
if ( c == 0 )
{
SetTraderUpdate();
return false; // Couldn't even afford 1 bullet.
}
AM.AddAmmo(c);
PlayerReplicationInfo.Score = Max(PlayerReplicationInfo.Score - (float(c) / UsedMagCapacity * Price), 0);
SetTraderUpdate();
return false;
}
PlayerReplicationInfo.Score = int(PlayerReplicationInfo.Score-Price);
AM.AddAmmo(c);
SetTraderUpdate();
return true;
}
// Drops All Story Items
simulated function InternalTossCarriedItems()
{
local Inventory Inv, NextInv;
local Vector X,Y,Z;
local Vector TossDir;
local float TossSpeed;
local vector DropLoc;
GetAxes(Rotation,X,Y,Z);
TossSpeed = 250.f;
DropLoc = Location + 0.8 * CollisionRadius * X - 0.5 * CollisionRadius * Y;
// Throws all your story items
for( Inv=Inventory; Inv!=None; Inv=NextInv )
{
NextInv = Inv.Inventory;
TossDir = VRand();
TossStoryItem( Inv, TossDir, TossSpeed, DropLoc );
}
// Throws your weapon
super.InternalTossCarriedItems();
}
// Drops a single story item
simulated function TossSingleCarriedItem()
{
local Inventory Inv;
local Vector X,Y,Z;
local Vector TossDir;
local float TossSpeed;
local vector DropLoc;
GetAxes(Rotation,X,Y,Z);
TossDir = vector(Rotation);
TossSpeed = 250.f;
DropLoc = Location + 0.8 * CollisionRadius * X - 0.5 * CollisionRadius * Y;
for( Inv=Inventory; Inv!=None; Inv=Inv.Inventory)
{
TossStoryItem( Inv, TossDir, TossSpeed, DropLoc );
}
}
simulated function TossStoryItem( Inventory Inv, Vector TossDir, float TossSpeed, vector DropLoc )
{
local KF_StoryinventoryItem StoryInv;
if(Inv.IsThrowable())
{
StoryInv = KF_StoryInventoryItem(Inv);
if(StoryInv != none)
{
if(StoryInv.bDropFromCameraLoc)
{
TossDir = Vector(GetViewRotation());
if(PlayerController(Controller) != none)
{
DropLoc = PlayerController(Controller).CalcViewLocation;
}
}
TossSpeed = StoryInv.Pickup_TossVelocity;
}
Inv.Velocity = TossDir * TossSpeed;
Inv.DropFrom(DropLoc);
}
}
/* ============ AI - Monster threat assessment functionality ==============================
Clamped from -1 to 100, where 100 is the most threatening ==================================
===========================================================================================*/
function float AssessThreatTo(KFMonsterController Monster, optional bool CheckDistance)
{
local float ThreatRating;
local Inventory CurInv;
local KF_StoryInventoryItem StoryInv;
ThreatRating = Super.AssessThreatTo(Monster,CheckDistance);
/* 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;
}
defaultproperties
{
}