/* -------------------------------------------------------------- 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, 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 AClass, bool bOnlyClip ) { local Inventory I; local float Price; local Ammunition AM; local KFWeapon KW; local int c; local float UsedMagCapacity; local class 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(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(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 { }