610 lines
15 KiB
Ucode
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
|
|
{
|
|
}
|
|
|
|
|