Prepare fixtures

This commit is contained in:
dkanus 2026-07-14 20:27:09 +07:00
commit 9c94356263
6021 changed files with 722805 additions and 22 deletions

View file

@ -0,0 +1,39 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class ACTION_CashReward extends ScriptedAction
editinlinenew;
var () int BaseCashAmount;
function bool InitActionFor(ScriptedController C)
{
local Controller Controller;
if(Abs(BaseCashAmount) > 0)
{
for ( Controller = C.Level.ControllerList; Controller != none; Controller = Controller.NextController )
{
if(Controller.PlayerReplicationInfo != none &&
Controller.Pawn != none &&
Controller.Pawn.Health > 0)
{
Controller.PlayerReplicationInfo.Score += BaseCashAmount ;
if(PlayerController(Controller) != none)
{
PlayerController(Controller).ClientPlaySound(class 'CashPickup'.default.PickupSound);
PlayerController(Controller).ReceiveLocalizedMessage(class 'Msg_CashReward',BaseCashAmount);
}
}
}
}
return false;
}
DefaultProperties
{
}

View file

@ -0,0 +1,24 @@
class ACTION_ChangeConditionLoc extends ScriptedAction;
var () KF_ObjectiveCondition AssociatedCondition;
var () actor NewLocation;
function bool InitActionFor(ScriptedController C)
{
if(AssociatedCondition != none)
{
AssociatedCondition.HUD_World.World_Location = NewLocation;
}
return false;
}
function string GetActionString()
{
return ActionString;
}
defaultproperties
{
ActionString="Update Condition Loc"
}

View file

@ -0,0 +1,81 @@
/*
--------------------------------------------------------------
ACTION_ChangeViewTarget
--------------------------------------------------------------
Scripted Action used to switch players viewtarget to a specific actor.
Used in story mode for the Patriarch 'Grand Entrance' sequence.
Author : Alex Quick
--------------------------------------------------------------
*/
class ACTION_ChangeViewTarget extends ScriptedAction;
var() name ViewActorTag;
var bool bViewingActor;
/* Initialises this action ** NOTE : returning TRUE out of this function will pause
all subsequent actions. you must return false if you want the scripted actions to proceed*/
function bool InitActionFor(ScriptedController C)
{
local Controller CC;
local PlayerController PC;
local Actor TestActor, ViewActor;
if(ViewActorTag != '')
{
foreach AllObjects(class 'Actor', TestActor)
{
if(TestActor.Tag == ViewActorTag)
{
ViewActor = TestActor;
break;
}
}
}
bViewingActor = ViewActor != none ;
for ( CC = C.Level.ControllerList; CC != None; CC = CC.NextController )
{
PC = PlayerController(CC);
if( PC !=None )
{
if(ViewActorTag != '')
{
log("! Change View Target to : "@ViewActor,'Story_Debug');
ViewActor.bAlwaysRelevant = True;
PC.SetViewTarget(ViewActor);
PC.ClientSetViewTarget(ViewActor);
PC.bBehindView = True;
PC.ClientSetBehindView(True);
if(KFMonster(ViewActor) != none)
{
KFMonster(ViewActor).MakeGrandEntry();
}
}
else
{
log("! Change View Target to : "@PC.Pawn,'Story_Debug');
if( PC.Pawn!=None )
{
PC.SetViewTarget(PC.Pawn);
PC.ClientSetViewTarget(PC.Pawn);
}
else
{
PC.SetViewTarget(PC);
PC.ClientSetViewTarget(PC);
}
PC.bBehindView = False;
PC.ClientSetBehindView(False);
}
}
}
return false;
}

View file

@ -0,0 +1,118 @@
class ACTION_DropInventory extends ScriptedAction;
var() class<Inventory> InventoryType;
var() name InvTag;
var() bool bSpawnPickup;
var() bool bAlwaysDropFromInstigator; // Should we always drop the pickup from the guy who instigates this action, or do we not care?
/* C.GetInstigator() is unreliable in this case, because a different player than the one who's inventory Item we
need to remove could be instigating the trigger*/
function Pawn GetInvHolder( ScriptedController OwningController, out Inventory ItemToRemove)
{
local Controller C;
if(bAlwaysDropFromInstigator && OwningController.GetInstigator() != none)
{
ItemToRemove = OwningController.GetInstigator().FindInventoryType(InventoryType);
if(ItemToRemove != none &&
(InvTag == '' || ItemToRemove.Tag == InvTag))
{
return OwningController.GetInstigator();
}
}
else
{
for ( C=OwningController.Level.ControllerList; C!=None; C=C.NextController )
{
if(C.Pawn != none && C.Pawn.bCanPickupInventory)
{
ItemToRemove = C.Pawn.FindInventoryType(InventoryType) ;
if(ItemToRemove != none &&
(InvTag == '' || ItemToRemove.Tag == InvTag))
{
return C.Pawn;
}
}
}
}
return none;
}
function bool InitActionFor(ScriptedController C)
{
local Inventory DelInv;
local Pawn P;
local PlayerController PC, OPC;
local class<Pickup> PickupClass;
local bool bSwitchWeapons;
local Controller CLC;
P = GetInvHolder(C,DelInv);
if( P == none || DelInv == none)
{
return false;
}
bSwitchWeapons = (DelInv == P.Weapon);
if( bSpawnPickup )
{
DelInv.Velocity = Vector( P.Rotation ) * 250.f ;
DelInv.DropFrom( P.Location );
}
else
{
if ( KFHumanPawn_Story( P ) != none)
{
KFHumanPawn_Story( P ).SetHasStoryItem( false );
}
if(KF_StoryInventoryItem(DelInv) != none)
{
KF_StoryInventoryItem(DelInv).UpdateHeldMaterial(P,none);
}
DelInv.DetachFromPawn(P);
P.DeleteInventory( DelInv );
DelInv.Destroy();
}
// JDR: TODO - re-add DeleteAmmo to Weapon and KFWeapon
//if( Weapon(DelInv) != none )
//{
// Weapon( DelInv ).DeleteAmmo();
//}
OPC = PlayerController(P.Controller);
if( OPC != none && bSwitchWeapons )
{
OPC.ClientSwitchToBestWeapon();
}
PickupClass = InventoryType.default.PickupClass;
if( PickupClass != none )
{
for( CLC = P.Level.ControllerList; CLC != None; CLC = CLC.NextController )
{
PC = PlayerController(CLC);
if(PC != none)
{
// JDR: there is no unified way to retrieve a "dropped" message currently,
// so this only displays the correct message for the gold bar for now,
// which is all we're using this for, for now...
PC.ReceiveLocalizedMessage(PickupClass.default.MessageClass,3,OPC.PlayerReplicationInfo);
}
}
}
return false;
}
defaultproperties
{
ActionString="Delete Inventory"
}

View file

@ -0,0 +1,62 @@
class ACTION_GiveWaveEndCash extends ScriptedAction
editinlinenew;
function bool InitActionFor(ScriptedController C)
{
local Controller PC;
local int moneyPerPlayer,div;
local TeamInfo T;
for ( PC = C.Level.ControllerList; PC != none; PC = PC.NextController )
{
if ( PC.Pawn != none && PC.PlayerReplicationInfo != none && PC.PlayerReplicationInfo.Team != none )
{
T = PC.PlayerReplicationInfo.Team;
div++;
}
}
if ( T == none || T.Score <= 0 )
{
return false;
}
moneyPerPlayer = int(T.Score / float(div));
for ( PC = C.Level.ControllerList; PC != none; PC = PC.NextController )
{
if ( PC.Pawn != none && PC.PlayerReplicationInfo != none && PC.PlayerReplicationInfo.Team != none )
{
if ( div == 1 )
{
PC.PlayerReplicationInfo.Score += T.Score;
T.Score = 0;
}
else
{
PC.PlayerReplicationInfo.Score += moneyPerPlayer;
T.Score-=moneyPerPlayer;
div--;
}
if(PlayerController(PC) != none)
{
PlayerController(PC).ClientPlaySound(class 'CashPickup'.default.PickupSound);
PlayerController(PC).ReceiveLocalizedMessage(class 'Msg_CashReward',MoneyPerPlayer);
}
PC.PlayerReplicationInfo.NetUpdateTime = C.Level.TimeSeconds - 1;
if( T.Score <= 0 )
{
T.Score = 0;
Break;
}
}
}
return false;
}

View file

@ -0,0 +1,107 @@
class ACTION_OpenRandomTrader extends ScriptedAction;
var () bool bCloseOtherShops;
var () bool bOpenDoor;
var () bool bUseExistingShop;
var ShopVolume CurrentShop;
var array<ShopVolume> Shops;
function CacheShops()
{
local ShopVolume Shop;
Shops.length = 0 ;
foreach AllObjects(class 'ShopVolume', Shop)
{
Shops[Shops.length] = Shop ;
}
}
function bool InitActionFor(ScriptedController C)
{
CacheShops();
CurrentShop = GetCurrentShop(C);
if(!bUseExistingShop || CurrentShop == none)
{
FindNewShop(C);
}
HandleShops();
return true;
}
function FindNewShop(ScriptedController C)
{
local ShopVolume NewShop;
local KFGameReplicationInfo KFGRI;
NewShop = Shops[Rand(Shops.length)] ;
KFGRI = KFGameReplicationInfo(C.Level.game.GameReplicationInfo) ;
if(KFGRI != none)
{
KFGRI.CurrentShop = NewShop;
}
CurrentShop = KFGRi.CurrentShop ;
HandleShops();
}
function HandleShops()
{
if(CurrentShop != none)
{
if(bOpenDoor && !CurrentShop.bCurrentlyOpen)
{
OpenSelectedShop();
}
CloseOtherShops();
}
}
function CloseOtherShops()
{
local int i;
if(bCloseOtherShops)
{
for(i = 0 ; i < Shops.length ; i ++)
{
if(Shops[i].bCurrentlyOpen)
{
Shops[i].BootPlayers();
Shops[i].CloseShop();
}
}
}
}
function ShopVolume GetCurrentShop(ScriptedController C)
{
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(C.Level.game.GameReplicationInfo) ;
if(KFGRI != none)
{
return KFGRi.CurrentShop ;
}
}
function OpenSelectedShop()
{
CurrentShop.InitTeleports();
CurrentShop.OpenShop();
}
function string GetActionString()
{
return ActionString;
}
defaultproperties
{
bCloseOtherShops = true
ActionString="Open Random Trader Shop"
}

View file

@ -0,0 +1,30 @@
/*
--------------------------------------------------------------
ACTION_ResetPlayerPerkSelection
--------------------------------------------------------------
Force allows players to be able to change their perks.
Author : Alex Quick
--------------------------------------------------------------
*/
class ACTION_ResetPlayerPerkSelection extends ScriptedAction;
function bool InitActionFor(ScriptedController C)
{
local Controller Controller;
local KFPC KFController;
for ( Controller = C.Level.ControllerList; Controller != none; Controller = Controller.NextController )
{
KFController = KFPC(Controller);
if(KFController != none)
{
KFController.bChangedVeterancyThisWave = false;
}
}
return false;
}

View file

@ -0,0 +1,36 @@
class ACTION_SetCollision extends ScriptedAction;
var(Action) bool bShouldCollideActors,bShouldBlockActors;
var(Action) name CollisionActorTag;
var Array<Actor> Target;
event PostBeginPlay( ScriptedSequence SS )
{
local Actor A;
if ( CollisionActorTag != '' )
{
ForEach SS.AllActors(class'Actor', A, CollisionActorTag)
Target[Target.Length] = A;
}
}
function bool InitActionFor(ScriptedController C)
{
local int i;
if ( Target.Length > 0 )
{
For (i=0; i<Target.Length; i++)
{
Target[i].SetCollision(bShouldCollideActors,bShouldBlockActors);
}
}
else
{
C.GetInstigator().SetCollision(bShouldCollideActors,bShouldBlockActors);
}
return false;
}

View file

@ -0,0 +1,33 @@
/*
--------------------------------------------------------------
Turns all KF_StoryNPCs in the map off or on.
Author : Alex Quick
--------------------------------------------------------------
*/
class ACTION_ToggleStoryNPCs extends ScriptedAction;
enum ENPCState
{
Off,
On
};
var () ENPCState DesiredState;
function bool InitActionFor(ScriptedController C)
{
local KF_StoryNPC A;
foreach C.AllActors(class 'KF_StoryNPC', A)
{
A.SetActive(bool(DesiredState));
}
return false;
}

View file

@ -0,0 +1,65 @@
/*
--------------------------------------------------------------
ACTION_TriggerRandomEvents
--------------------------------------------------------------
Functions like 'ACTION_TriggerEvent' except that it supports
multiple different events being fired off at once. It will pick
up to 'NumToTrigger' events from the 'PossibleEvents' array
in order to do this.
Author : Alex Quick
--------------------------------------------------------------
*/
class ACTION_TriggerRandomEvents extends ScriptedAction;
var(Action) array<name> PossibleEvents;
var(Action) int NumToTrigger;
function bool InitActionFor(ScriptedController C)
{
local array<name> EventList,PendingEvents;
local int RandIdx;
local int i;
EventList = PossibleEvents;
/* Always check that we're not trying to trigger more events than are in the array */
NumToTrigger = Min(NumToTrigger,EventList.length);
for(i = 0 ; i < NumToTrigger; i ++)
{
RandIdx = Rand(EventList.length);
PendingEvents.length = PendingEvents.length + 1;
PendingEvents[PendingEvents.length - 1] = EventList[RandIdx];
/* remove it from the list so it can't be used twice */
EventList.Remove(RandIdx,1);
}
for(i = 0 ; i < PendingEvents.length ; i ++)
{
C.TriggerEvent(PendingEvents[i],C.SequenceScript,C.GetInstigator());
}
return false;
}
function GetActionEvents(out array<name> TriggeredEvents, out array<name> ReceivedEvents)
{
local int i;
for( i = 0 ; i < PossibleEvents.length ; i ++)
{
if(PossibleEvents[i] != '')
{
TriggeredEvents[TriggeredEvents.length] = PossibleEvents[i];
}
}
}

View file

@ -0,0 +1,64 @@
class AnimatedRachel extends Decoration; // Decoration
#exec OBJ LOAD FILE=KF_RachelC_anim.ukx
var bool bTriggered;
var bool bTriggeredAnimation;
simulated function PostBeginPlay()
{
If ( Level.NetMode != NM_DedicatedServer)
{
LoopAnim('Idle_Fidget');
}
}
simulated function Trigger( actor Other, pawn EventInstigator )
{
if ( Level.NetMode != NM_DedicatedServer )
{
if (!bTriggeredAnimation)
{
bTriggeredAnimation = TRUE;
LoopAnim('Idle_Gesticulate');
}
ELSE
{
bTriggeredAnimation = FALSE;
LoopAnim('Idle_Fidget');
}
}
bClientTrigger = !bClientTrigger;
}
simulated event ClientTrigger()
{
if ( Level.NetMode != NM_DedicatedServer )
{
if (!bTriggeredAnimation)
{
bTriggeredAnimation = TRUE;
LoopAnim('Idle_Gesticulate');
}
ELSE
{
bTriggeredAnimation = FALSE;
LoopAnim('Idle_Fidget');
}
}
}
defaultproperties
{
bStatic=False
bStasis=False
Mesh=SkeletalMesh'KF_RachelC_anim.RachelC_mesh''
DrawScale=1
RemoteRole=ROLE_SimulatedProxy
bNoDelete=True
NetUpdateFrequency=0.5
bSkipActorPropertyReplication=True
bAlwaysRelevant=True
}

View file

@ -0,0 +1,21 @@
// A special type of volume which functions like a blocking volume, but blocks ONLY humans from entering
class BlockingVolume_Toggleable extends BlockingVolume;
function Reset()
{
SetCollision(default.bCollideActors);
}
simulated function Trigger( actor Other, pawn EventInstigator )
{
SetCollision(!bCollideActors);
}
defaultproperties
{
bClassBlocker=True
BlockedClasses(0)=Class'KFMod.KFHumanPawn'
bStatic = false
}

View file

@ -0,0 +1,21 @@
class BreakerGibGroup extends xPawnGibGroup;
defaultproperties
{
Gibs(0)=none
Gibs(1)=none
Gibs(2)=none
Gibs(3)=none
Gibs(4)=none
Gibs(5)=none
Gibs(6)=none
Gibs(7)=none
BloodHitClass=Class'Emitter_BreakerExplosion'
LowGoreBloodHitClass=Class'Emitter_BreakerExplosion'
BloodGibClass=Class'Emitter_BreakerExplosion'
LowGoreBloodGibClass=Class'Emitter_BreakerExplosion'
LowGoreBloodEmitClass=Class'Emitter_BreakerExplosion'
BloodEmitClass=Class'Emitter_BreakerExplosion'
NoBloodEmitClass=Class'Emitter_BreakerExplosion'
NoBloodHitClass=Class'Emitter_BreakerExplosion'
}

View file

@ -0,0 +1,15 @@
class BreakerSoundGroup extends xPawnSoundGroup;
defaultproperties
{
DeathSounds(0)=Sound'PatchSounds.MetalCrash'
DeathSounds(1)=Sound'PatchSounds.MetalCrash'
DeathSounds(2)=Sound'PatchSounds.MetalCrash'
DeathSounds(3)=Sound'PatchSounds.MetalCrash'
DeathSounds(4)=Sound'PatchSounds.MetalCrash'
PainSounds(0)=Sound'KF_EnemyGlobalSnd.Zomb_HitDoor_Metal'
PainSounds(1)=Sound'KF_EnemyGlobalSnd.Zomb_HitDoor_Metal'
PainSounds(2)=Sound'KF_EnemyGlobalSnd.Zomb_HitDoor_Metal'
PainSounds(3)=Sound'KF_EnemyGlobalSnd.Zomb_HitDoor_Metal'
PainSounds(4)=Sound'KF_EnemyGlobalSnd.Zomb_HitDoor_Metal'
}

View file

@ -0,0 +1,36 @@
class CashPickup_Story extends CashPickup ;
function GiveCashTo( Pawn Other )
{
// You all love the mental-mad typecasting XD
if( !bDroppedCash )
{
/* the parent class was randomizing the cash amount by default values ... so whatever the LD set as 'cash amount' was being ignored .
@fixme - This also appears to be *increasing* the amount of cash you gain from pickups the higher the difficulty level ... that doesn't sound right. */
CashAmount = ((rand(0.5 * CashAmount) + CashAmount) * (KFGameReplicationInfo(Level.GRI).GameDiff * 0.5)) * Max(KFGameType(Level.Game).GetNumPlayers(),1) ;
}
else if ( Other.PlayerReplicationInfo != none && DroppedBy.PlayerReplicationInfo != none &&
((DroppedBy.PlayerReplicationInfo.Score + float(CashAmount)) / Other.PlayerReplicationInfo.Score) >= 0.50 &&
PlayerController(DroppedBy) != none && KFSteamStatsAndAchievements(PlayerController(DroppedBy).SteamStatsAndAchievements) != none )
{
if ( Other.PlayerReplicationInfo != DroppedBy.PlayerReplicationInfo )
{
KFSteamStatsAndAchievements(PlayerController(DroppedBy).SteamStatsAndAchievements).AddDonatedCash(CashAmount);
}
}
if( Other.Controller!=None && Other.Controller.PlayerReplicationInfo!=none )
{
Other.Controller.PlayerReplicationInfo.Score += CashAmount;
}
AnnouncePickup(Other);
SetRespawn();
}
defaultproperties
{
RespawnTime = 0
}

View file

@ -0,0 +1,65 @@
/*
--------------------------------------------------------------
Dialogue_EventListener
--------------------------------------------------------------
Simple event listener actor spawned by KF_DialogueSpots.
Notifies the dialogue spot that it can continue the dialogue chain
Author : Alex Quick
--------------------------------------------------------------
*/
class Dialogue_EventListener extends Actor;
var KF_DialogueSpot DlgOwner;
var int AssociatedIndex;
function PostBeginPlay()
{
DlgOwner = KF_DialogueSpot(Owner);
}
function Trigger( actor Other, pawn EventInstigator )
{
if(DlgOwner != none )
{
if(DlgOwner.bDebugDialogue)
{
log("----- DIALOGUE DEBUG ------ DialogueListener was triggered for Index "@AssociatedIndex@"Received event : "@DlgOwner.Dialogues[AssociatedIndex].Events.RequiredEvent@",triggered by : "@Other@" .. Proceeding ", 'Story_Debug');
}
if(!DlgOwner.Dialogues[AssociatedIndex].bWasTriggered )
{
if(DlgOwner.bFinished)
{
DlgOwner.bFinished = false;
}
DlgOwner.Dialogues[AssociatedIndex].bWasTriggered = true ;
DlgOwner.CurrentMsgIdx = AssociatedIndex;
DlgOwner.TraverseDialogue();
}
}
}
function Timer()
{
if(DlgOwner != none)
{
DlgOwner.OnDialogueDisplayComplete(AssociatedIndex);
}
}
defaultproperties
{
bHidden=True
CollisionRadius=1
CollisionHeight=1
bBlockZeroExtentTraces= False
bBlockNonZeroExtentTraces= False
RemoteRole = Role_None
}

View file

@ -0,0 +1,49 @@
class Dummy_JoggingAttachment extends KFMeleeAttachment;
defaultproperties
{
Mesh=none
MovementAnims(0)=JogF_Pipe
MovementAnims(1)=JogB_Pipe
MovementAnims(2)=JogL_Pipe
MovementAnims(3)=JogR_Pipe
CrouchAnims(0)=CHwalkF_Pipe
CrouchAnims(1)=CHwalkB_Pipe
CrouchAnims(2)=CHwalkL_Pipe
CrouchAnims(3)=CHwalkR_Pipe
AirStillAnim=JumpF_Mid
AirAnims(0)=JumpF_Mid
AirAnims(1)=JumpF_Mid
AirAnims(2)=JumpL_Mid
AirAnims(3)=JumpR_Mid
TakeoffStillAnim=JumpF_Takeoff
TakeoffAnims(0)=JumpF_Takeoff
TakeoffAnims(1)=JumpF_Takeoff
TakeoffAnims(2)=JumpL_Takeoff
TakeoffAnims(3)=JumpR_Takeoff
LandAnims(0)=JumpF_Land
LandAnims(1)=JumpF_Land
LandAnims(2)=JumpL_Land
LandAnims(3)=JumpR_Land
TurnRightAnim=TurnR_Pipe
TurnLeftAnim=TurnL_Pipe
CrouchTurnRightAnim=CH_TurnR_Pipe
CrouchTurnLeftAnim=CH_TurnL_Pipe
IdleRestAnim=Idle_Pipe//Idle_Rest
IdleCrouchAnim=CHIdle_Pipe
IdleSwimAnim=Swim_Tread
IdleWeaponAnim=Idle_Pipe//Idle_Rifle
IdleHeavyAnim=Idle_Pipe//Idle_Biggun
IdleRifleAnim=Idle_Pipe//Idle_Rifle
IdleChatAnim=Idle_Pipe
FireAnims(0)=Attack1_Pipe
FireAnims(1)=Attack2_Pipe
FireAnims(2)=Attack3_Pipe
FireAnims(3)=Attack4_Pipe
FireAltAnims(0)=Attack1_Pipe
FireAltAnims(1)=Attack2_Pipe
FireAltAnims(2)=Attack3_Pipe
FireAltAnims(3)=Attack4_Pipe
}

View file

@ -0,0 +1,39 @@
/*
Dummy_JoggingWeapon
--------------------------------------------------------------
This is basically just a dummy inventory class that KF pawns
can hold when they are supposed to not be holding any weapon.
It's necessary since there isn't any real support in the KF Pawn
Animation / Weapon attachment code for that.
While carrying this weapon your dude looks like he is Jogging with
his arms at his sides.
Author : Alex Quick
--------------------------------------------------------------
*/
class Dummy_JoggingWeapon extends KFWeapon
HideDropdown;
simulated function String GetHumanReadableName()
{
return "";
}
defaultproperties
{
Weight=0
FireModeClass(0)=Class'KFMod.NoFire'
FireModeClass(1)=Class'KFMod.NoFire'
InventoryGroup = 0
bKFNeverThrow=True
AttachmentClass = class 'Dummy_JoggingAttachment'
}

View file

@ -0,0 +1,41 @@
class Emitter_BreakerExplosion extends MetalHitEmitter;
defaultproperties
{
Begin Object Class=SpriteEmitter Name=SpriteEmitter33
UseDirectionAs=PTDU_UpAndNormal
UseCollision=True
UseColorScale=True
FadeOut=True
FadeIn=True
RespawnDeadParticles=False
UseSizeScale=True
UseRegularSizeScale=False
UniformSize=True
ScaleSizeXByVelocity=True
AutomaticInitialSpawning=False
Acceleration=(Z=-210.000000)
DampingFactorRange=(X=(Min=0.500000,Max=0.500000),Y=(Min=0.500000,Max=0.500000),Z=(Min=0.500000,Max=0.500000))
ColorScale(0)=(Color=(B=255,G=255,R=187))
ColorScale(1)=(RelativeTime=0.214286,Color=(G=103,R=206,A=255))
ColorScale(2)=(RelativeTime=0.439286,Color=(B=100,G=177,R=255,A=255))
ColorScale(3)=(RelativeTime=1.000000,Color=(G=103,R=206,A=255))
ColorScale(4)=(RelativeTime=1.000000,Color=(G=103,R=206,A=255))
ColorScale(5)=(RelativeTime=1.000000,Color=(R=128,A=255))
ColorScale(6)=(RelativeTime=1.000000)
ColorScale(7)=(RelativeTime=1.000000)
FadeOutStartTime=0.336000
FadeInEndTime=0.064000
MaxParticles=100
Name="SpriteEmitter33"
SizeScale(0)=(RelativeSize=1.000000)
SizeScale(1)=(RelativeTime=1.000000,RelativeSize=0.250000)
StartSizeRange=(X=(Min=3.000000,Max=10.000000),Y=(Min=5000.000000,Max=5000.000000),Z=(Min=5000.000000,Max=5000.000000))
ScaleSizeByVelocityMultiplier=(X=0.010000,Y=0.010000)
InitialParticlesPerSecond=5000.000000
Texture=Texture'KFX.KFSparkHead'
LifetimeRange=(Min=1.500000,Max=1.500000)
StartVelocityRange=(X=(Min=-200.000000,Max=200.000000),Y=(Min=-200.000000,Max=200.000000),Z=(Min=-200.000000,Max=200.000000))
End Object
Emitters(0)=SpriteEmitter'SpriteEmitter33'
}

View file

@ -0,0 +1,39 @@
class Emitter_CoinExplosion extends Emitter;
defaultproperties
{
Begin Object Class=MeshEmitter Name=MeshEmitter0
StaticMesh=StaticMesh'KF_Swansong_SM.Lootbag.Coin'
UseParticleColor=True
UseCollision=True
UseMaxCollisions=True
UseColorScale=True
RespawnDeadParticles=False
SpinParticles=True
DampRotation=True
AutomaticInitialSpawning=False
Acceleration=(Z=-215.000000)
DampingFactorRange=(X=(Min=0.500000,Max=0.500000),Y=(Min=0.500000,Max=0.500000),Z=(Min=0.500000,Max=0.500000))
MaxCollisions=(Min=7.000000,Max=7.000000)
ColorScale(0)=(Color=(G=255,R=255))
ColorScale(1)=(RelativeTime=1.000000,Color=(G=255,R=255))
FadeOutStartTime=1.000000
MaxParticles=30
StartLocationRange=(X=(Min=-3.000000,Max=3.000000),Y=(Min=-3.000000,Max=3.000000),Z=(Max=10.000000))
UseRotationFrom=PTRS_Normal
SpinsPerSecondRange=(X=(Min=0.200000,Max=0.800000),Y=(Min=0.200000,Max=0.800000),Z=(Min=0.200000,Max=0.800000))
StartSpinRange=(X=(Max=16000.000000),Y=(Max=16000.000000),Z=(Max=16000.000000))
RotationDampingFactorRange=(X=(Min=0.250000,Max=0.500000),Y=(Min=0.250000,Max=0.500000),Z=(Min=0.250000,Max=0.500000))
StartSizeRange=(X=(Min=0.600000,Max=0.600000),Y=(Min=0.600000,Max=0.600000))
InitialParticlesPerSecond=10000.000000
StartVelocityRange=(X=(Min=-30.000000,Max=30.000000),Y=(Min=-30.000000,Max=30.000000),Z=(Min=50.000000,Max=150.000000))
End Object
Emitters(0)=MeshEmitter'MeshEmitter0'
// Default non particle properties
bNoDelete=FALSE
DrawScale=0.500000
AmbientGlow=48
bUnlit=False
}

View file

@ -0,0 +1,60 @@
class Emitter_VialBreak extends Emitter;
defaultproperties
{
// Glass
Begin Object Class=SpriteEmitter Name=SpriteEmitter75
FadeOut=True
RespawnDeadParticles=False
UseRevolution=True
SpinParticles=True
UniformSize=True
AutomaticInitialSpawning=False
UseRandomSubdivision=True
Acceleration=(Z=-150.000000)
FadeOutStartTime=1.000000
MaxParticles=6
StartLocationRange=(X=(Min=-5.000000,Max=5.000000),Y=(Min=-5.000000,Max=5.000000),Z=(Min=5.000000,Max=10.000000))
SpinsPerSecondRange=(X=(Min=0.100000,Max=0.500000),Y=(Min=0.100000,Max=0.500000))
StartSpinRange=(X=(Max=16000.000000),Y=(Max=16000.000000),Z=(Max=16000.000000))
StartSizeRange=(X=(Min=5.000000,Max=10.000000),Y=(Min=5.000000,Max=10.000000))
InitialParticlesPerSecond=1000.000000
Texture=Texture'Effects_Tex.BulletHits.brokenglass_chunks_01'
TextureUSubdivisions=2
TextureVSubdivisions=2
LifetimeRange=(Min=1.500000,Max=1.500000)
StartVelocityRange=(X=(Min=-30.000000,Max=30.000000),Y=(Min=-30.000000,Max=30.000000),Z=(Min=45.000000,Max=55.000000))
End Object
Emitters(0)=SpriteEmitter'SpriteEmitter75'
// DNA Goo
Begin Object Class=SpriteEmitter Name=SpriteEmitter76
FadeOut=True
RespawnDeadParticles=False
SpinParticles=True
UseSizeScale=True
UseRegularSizeScale=False
UniformSize=True
AutomaticInitialSpawning=False
UseRandomSubdivision=True
Acceleration=(Z=-100.000000)
FadeOutStartTime=1.000000
MaxParticles=5
StartLocationRange=(X=(Min=-5.000000,Max=5.000000),Y=(Min=-5.000000,Max=5.000000),Z=(Min=-10.000000,Max=0.000000))
UseRotationFrom=PTRS_Normal
StartSpinRange=(X=(Max=16000.000000),Y=(Max=16000.000000))
SizeScale(0)=(RelativeSize=1.000000)
SizeScale(1)=(RelativeTime=1.000000,RelativeSize=10.000000)
StartSizeRange=(X=(Min=5.000000,Max=5.000000),Y=(Min=5.000000,Max=5.000000))
InitialParticlesPerSecond=10000.000000
DrawStyle=PTDS_Modulated
Texture=Texture'kf_fx_trip_t.Misc.Vomit_Splat_E'
TextureUSubdivisions=1
TextureVSubdivisions=1
LifetimeRange=(Min=1.500000,Max=1.500000)
StartVelocityRange=(X=(Min=-20.000000,Max=20.000000),Y=(Min=-20.000000,Max=20.000000),Z=(Min=15.000000,Max=25.000000))
End Object
Emitters(1)=SpriteEmitter'SpriteEmitter76'
bNoDelete=False
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
/*
--------------------------------------------------------------
Inv_MaintenanceKeyCard
--------------------------------------------------------------
*/
class Inv_MaintenanceKeyCard extends KF_StoryInventoryItem;
defaultproperties
{
bDropFromCameraLoc=true
AttachmentClass = none;
PickupClass = class 'Pickup_MaintenanceKeyCard'
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Keycard_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
}

View file

@ -0,0 +1,20 @@
/*
--------------------------------------------------------------
Inv_Nitroglycerin
--------------------------------------------------------------
*/
class Inv_Nitroglycerin extends KF_StoryInventoryItem;
defaultproperties
{
bDropFromCameraLoc=true
AttachmentClass = none;
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Nitroglycerin_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
bUseForcedGroundSpeed = true
ForcedGroundSpeed =120.0
PickupClass = class 'Pickup_Nitroglycerin'
}

View file

@ -0,0 +1,16 @@
/*
--------------------------------------------------------------
Inv_PatriarchEyeBall
--------------------------------------------------------------
*/
class Inv_PatriarchEyeball extends KF_StoryInventoryItem;
defaultproperties
{
bDropFromCameraLoc=true
AttachmentClass = none;
PickupClass = class 'Pickup_PatriarchEyeBall'
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Eyeball_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
}

View file

@ -0,0 +1,16 @@
/*
--------------------------------------------------------------
Inv_Thermite
--------------------------------------------------------------
*/
class Inv_Thermite extends KF_StoryInventoryItem;
defaultproperties
{
bDropFromCameraLoc=true
AttachmentClass = none;
PickupClass = class 'Pickup_Thermite'
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Thermite_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
}

View file

@ -0,0 +1,23 @@
/*
--------------------------------------------------------------
KFAmmoPickup_Story
--------------------------------------------------------------
AmmoPickup actors tailored to Story missions. Refills specific
types of weapons.
Author : Alex Quick
--------------------------------------------------------------
*/
class KFAmmoPickup_Story extends KFAmmoPickup;
state Pickup
{
// When touched by an actor.
function Touch(Actor Other)
{
Super(Ammo).Touch(Other);
}
}

View file

@ -0,0 +1,6 @@
class KFHintmanager_Story extends KFHintManager;
defaultproperties
{
Hints(42)=(type=61,priority=20,delay=1,title="Optional Missions",hint="You have just received an optional mission. Completing it will give give you a cash bonus. To start the mission proceed to the Mission area on your HUD")
}

View file

@ -0,0 +1,609 @@
/*
--------------------------------------------------------------
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
{
}

View file

@ -0,0 +1,152 @@
/*
--------------------------------------------------------------
KFLevelRules_Story
--------------------------------------------------------------
Extended LevelRules info for use in 'Story' style missions .
Can modify player starting equipment, health, cash , etc.
Also stores a list of Objectives the player must complete to
be victorious.
Author : Alex Quick
--------------------------------------------------------------
*/
class KFLevelRules_Story extends KFLevelRules
hidecategories(Sound,Events)
dependson(KFStoryGameInfo) ;
#exec OBJ LOAD FILE=KFStoryGame_Tex.utx
/* should the first objective in the StoryObjectives array activate as soon as the match begins ? */
var(Rules_Objectives) bool bAutoStartObjectives;
/* Amount of dosh to start players out with in story mode */
var(Rules_Cash) int StartingCashSum;
/* A modifier for the amount of cash players receive from killing zombies. ie. at 0.f they will not receive anything */
var(Rules_Cash) float CashReward_ZEDKills_Modifier;
/* A modifier for the amount of cash players lose when they die */
var(Rules_Cash) float CashPenalty_Death_Modifier;
/* absolute maximum number of zombies we can have in this story map at one time */
var(Rules_Monsters) int MaxEnemiesAtOnce;
/* should the game kill off ZEDs which haven't been seen by players for a while? */
var(Rules_Monsters) bool bAutoKillStragglers;
/* Auto Kill threshold if bAutoKilLStragglers is true */
var(Rules_Monsters) int MaxStragglers;
/* Struct for controlling attributes which affect enemy spawning base on game difficulty and player count */
struct SMonsterSpawnScaling
{
var () KFStoryGameInfo.SDifficultyWrapper EnemySpawnRate;
var () KFStoryGameInfo.SDifficultyWrapper NumberOfEnemies;
};
var(Rules_Monsters) SMonsterSpawnScaling Spawn_Difficulty_Scaling;
/* should bots be allowed to spawn ? */
var(Rules_Bots) bool bAllowBots;
/* Textures to display on the HUD when the match is over */
var(Rules_HUD) Material VictoryMaterial,DefeatMaterial;
/* If a team is restarted after dying during a story mission these are the actor types we need to reset*/
var(Rules_CheckPoints) array<class> CheckpointResetClasses ;
/* If true the weapon / item pickups in the map will be randomly spawned based on difficulty - like in a normal KF match */
var(Rules_Equipment) bool bRandomizeWeaponPickups;
// Amount of starting HP for all players
var(Rules_Equipment) int PlayerStartHealth;
// Amount of starting armor for all players.
var(Rules_Equipment) int PlayerStartArmor;
// Defines starting equipment for players
var(Rules_Equipment) array< Class<Inventory> > RequiredPlayerEquipment;
/* if true, allow high level players to spawn with their 'default' gear */
var(Rules_Equipment) bool bAllowPerkStartingWeaps;
// Enable / Disable Cash display on HUD .
var(Rules_HUD) bool bShowCash;
var(Rules_HUD) name HUDStyle;
function ModifyPlayer( Pawn Other )
{
if( PlayerStartHealth>0 )
Other.Health = PlayerStartHealth;
if( PlayerStartArmor>0 )
Other.ShieldStrength = PlayerStartArmor;
}
function AddGameInv( Pawn Other )
{
local int i;
local Inventory Inv;
For( i=0; i<RequiredPlayerEquipment.Length; i++ )
{
if( RequiredPlayerEquipment[i]==None )
Continue;
if( Other.FindInventoryType(RequiredPlayerEquipment[i])==None )
{
Inv = Spawn(RequiredPlayerEquipment[i]);
if( Inv != None )
{
Inv.GiveTo(Other);
if ( Inv != None )
Inv.PickupFunction(Other);
}
}
}
}
defaultproperties
{
CashReward_ZEDKills_Modifier=1.000000
CashPenalty_Death_Modifier=1.000000
bAllowPerkStartingWeaps=True
MaxEnemiesAtOnce=32
MaxStragglers=5
bAutoKillStragglers = true
bShowCash = true
StartingCashSum = 250
VictoryMaterial=Combiner'KFMapEndTextures.VictoryCombiner'
DefeatMaterial=Combiner'KFMapEndTextures.DefeatCombiner'
CheckpointResetClasses(0)=Class'ZombieVolume'
CheckpointResetClasses(1)=Class'Gameplay.ScriptedSequence'
CheckpointResetClasses(2)=Class'Engine.Pickup'
CheckpointResetClasses(3)=Class'Engine.Pawn'
CheckpointResetClasses(4)=Class'Engine.Triggers'
CheckpointResetClasses(5)=Class'Gameplay.TriggerLight'
CheckpointResetClasses(6)=Class'Engine.BlockingVolume'
CheckPointResetClasses(7)=Class'Engine.Decoration'
CheckPointResetClasses(8)=Class'KF_StoryWaveDesigner'
CheckPointResetClasses(9)=Class'StaticMeshActor_Hideable'
CheckPointResetClasses(10)=Class'KF_StoryWaveDesigner'
RequiredPlayerEquipment(0)=Class'KFMod.Single'
RequiredPlayerEquipment(1)=Class'KFMod.Syringe'
RequiredPlayerEquipment(2)=Class'KFMod.Welder'
RequiredPlayerEquipment(3)=Class'KFMod.Frag'
RequiredPlayerEquipment(4)=Class'KFMod.knife'
Texture=Texture'KFStoryGame_Tex.Editor.KFRules_Ico'
bAutoStartObjectives = true
bRandomizeWeaponPickups = true
/* Difficulty Magic Numbers go here! */
Spawn_Difficulty_Scaling=(EnemySpawnRate=(Scale_GameDifficulty=(Scale_Beginner=1.0,Scale_Hard=1.17,Scale_Suicidal=1.17,Scale_HellOnEarth=1.17),Scale_PlayerCount=(Scale_1P=1,Scale_2P=1,Scale_3P=1.0,Scale_4P=1.17,Scale_5P=1.53,Scale_6P=3.3)),NumberOfEnemies=(Scale_GameDifficulty=(Scale_Beginner=0.7,Scale_Hard=1.3,Scale_Suicidal=1.5,Scale_HellOnEarth=1.7),Scale_PlayerCount=(Scale_1P=1,Scale_2P=2,Scale_3P=2.75,Scale_4P=3.5,Scale_5P=4,Scale_6P=4.5)))
}

View file

@ -0,0 +1,8 @@
class KFOMapList extends MapList
config;
defaultproperties
{
Maps(0)="KFO-Steamland"
MapNum=1
}

View file

@ -0,0 +1,603 @@
/*
--------------------------------------------------------------
KFPlayerController_Story
--------------------------------------------------------------
Custom PlayerController class for use in Killing Floor 'Story' maps.
Author : Alex Quick
--------------------------------------------------------------
*/
class KFPlayerController_Story extends KFPlayerController ;
/* number of times this player has been respawned by the current CheckpointVolume */
var int NumCheckPointRespawns;
/* reference to the last Trader Shop we used */
var KFShopVolume_Story CurrentShopVolume;
var KF_StoryCheckPointVolume CurrentCheckPoint;
var bool bShowObjectiveDebug;
/* == Checkpoint Data ===================================================================
Stuff we want to keep track of when this player is 'checkpointed' during a story mission.
=========================================================================================
*/
/* array of equipment the pawn was holding when he last activated a checkpoint */
var array<String> SavedLoadOut;
/* the ammo counts for weapons this player had at the time they last activated a checkpoint */
var array<Int> SavedAmmo;
/* the magazine ammo counts for weapons this player had at the time they last activated a checkpoint */
var array<Int> SavedMagAmmo;
/* the cash this player had at the time he last activated a checkpoint */
var float SavedCash;
/* the amount of health this player had at the time he last activated a checkpoint */
var int SavedHealth;
/* the amount of body armor this player had at the time he last activated a checkpoint */
var int SavedArmor;
/* If we are doing a trader time objective this is the number of seconds left before it completes */
var float RemainingTraderTime;
replication
{
unreliable if(Role == Role_Authority)
SetClientWhispClr,ClientSetUV2Tex,UnReliableConditionUpdate;
reliable if(Role == Role_Authority)
CurrentShopVolume,ReliableConditionUpdate,ClientShowStoryDialogue, ClientPlayStorySound;
unreliable if( Role<ROLE_Authority )
ServerStopUsing,ServerCompleteObj,ServerFFObj;
reliable if (Role <ROLE_Authority)
ServerReadyLateJoiner;
}
simulated function ClientSetUV2Tex(Actor TargetActor , Material NewUV2Tex)
{
TargetActor.UV2Texture = NewUV2Tex;
}
simulated function ClientShowStoryDialogue(name DialogueActor, int DlgIndex, float DisplayDuration)
{
if(HUD_StoryMode(myHUD) != none)
{
HUD_StoryMode(myHUD).AddDialogue(DialogueActor,DlgIndex,DisplayDuration);
}
}
// Play a story mode sound on the client
// If bAdjustPitch = true then the sound will be adjusted to the game pitch
// based on the game speed (i.e. lower pitch if zed time is enabled). If it
// is false it will not be adjusted with the game speed
simulated function ClientPlayStorySound(sound ASound, float Volume, bool bAdjustPitch )
{
local float UsedPitch;
UsedPitch = 1.0;
if( bAdjustPitch )
{
UsedPitch = 1.0;
}
else
{
UsedPitch = 1.1 / Level.TimeDilation;
}
if ( ViewTarget != None )
ViewTarget.PlayOwnedSound(ASound,Slot_Interface,Volume,true,,UsedPitch,false);
else
PlayOwnedSound(ASound,Slot_Interface,Volume,true,, 1.1 / Level.TimeDilation, false);
}
exec simulated function ToggleConditionStack()
{
if(HUD_StoryMode(myHUD) != none)
{
HUD_StoryMode(myHUD).bCollapseConditions = !HUD_StoryMode(myHUD).bCollapseConditions;
}
}
/* Called on the client - Updates condition data unreliably */
simulated function UnreliableConditionUpdate(
KF_ObjectiveCondition UpdatedCondition,
KF_StoryObjective ObjOwner,
float NewProgressPct,
Actor NewLocActor,
string NewDataString,
bool NewComplete)
{
DoConditionUpdate(UpdatedCondition,ObjOwner.Name,NewProgressPct,NewLocActor,UpdatedCondition.GetHUDHint()@NewDataString,NewComplete, '');
}
/* Called on the client - Updates condition data reliably */
simulated function ReliableConditionUpdate(
KF_ObjectiveCondition UpdatedCondition,
KF_StoryObjective ObjOwner,
float NewProgressPct,
Actor NewLocActor,
string NewDataString,
bool NewComplete)
{
local name PendingLocActorTag;
/*
log(" *** RELIABLE CONDITION UPDATE ******************************************************");
log(" >>> "@UpdatedCondition);
log(" *** Progress Percent :"@NewProgressPct);
log(" *** Data String :"@NewDataString);
log(" *** Complete? :"@NewComplete);
*/
// If we have an actors tag, but no actor Queue him with PendingLocActorTag to grab his reference later.
// This is pretty much a hack to get around cases where we are trying to grab a reference to an actor which
// does not yet exist on the client .
if (NewLocActor == none )
{
if( UpdatedCondition.IsA('ObjCondition_ActorHealth') )
{
PendingLocActorTag = ObjCondition_ActorHealth(UpdatedCondition).TargetPawnTag;
}
else if( UpdatedCondition.IsA('ObjCondition_Use') )
{
PendingLocActorTag = ObjCondition_Use(UpdatedCondition).UsePawn_Tag;
}
}
DoConditionUpdate(UpdatedCondition,ObjOwner.Name,NewProgressPct,NewLocActor,UpdatedCondition.GetHUDHint()@NewDataString,NewComplete,PendingLocActorTag);
}
simulated function DoConditionUpdate(
KF_ObjectiveCondition UpdatedCondition,
name ObjOwner,
float NewProgressPct,
Actor NewLocActor,
string NewDataString,
bool NewComplete,
name PendingLocActorTag)
{
local ObjCondition_Timed TimeCondition;
if(HUD_StoryMode(myHUD) == none)
{
log(" WARNING - NO HUD AVAILABLE FOR : "@self@" CLIENT CONDITION UPDATE WILL FAIL. ",'Story_Debug');
}
if(Level.NetMode == NM_DedicatedServer ||
HUD_StoryMode(myHUD) == none ||
UpdatedCondition == none ||
ObjOwner == '')
{
return;
}
TimeCondition = ObjCondition_Timed(UpdatedCondition);
if(TimeCondition != none &&
TimeCondition.bTraderTime)
{
RemainingTraderTime = TimeCondition.Duration - (TimeCondition.Duration * NewProgressPct);
}
HUD_StoryMode(myHUD).UpdateConditionHint(
UpdatedCondition.name,
ObjOwner,
NewProgressPct,
NewLocActor,
NewDataString,
NewComplete,
PendingLocActorTag);
}
simulated function SetClientWhispClr(color NewWhispClr)
{
Class'Objective_Whisp'.default.mColorRange[0] = NewWhispClr;
Class'Objective_Whisp'.default.mColorRange[1] = NewWhispClr;
}
/* This player's progress was saved at a checkpoint */
function SaveLoadOut()
{
if(PlayerReplicationInfo != none)
{
SavedCash = PlayerReplicationInfo.Score;
}
if(KFHumanPawn_Story(Pawn) != none)
{
KFHumanPawn_Story(Pawn).SaveLoadOut() ;
}
}
/* Overriden to ensure it opens up the story-friendly version of the Trader Buy Menu */
function ShowBuyMenu(string wlTag,float maxweight)
{
StopForceFeedback(); // jdf - no way to pause feedback
// Open menu
ClientOpenMenu("KFStoryUI.GUIBuyMenu_Story",,wlTag,string(maxweight));
}
// Send a voice message of a certain type to a certain player.
/* Added a hack to filter out trader voices */
function ServerSpeech( name Type, int Index, string Callsign )
{
if(Type != 'TRADER')
{
Super.ServerSpeech(Type,Index,Callsign);
}
}
function SetPawnClass(string inClass, string inCharacter)
{
Super.SetPawnClass(inClass,inCharacter);
/* @todo - why doesn't GetDefaultPlayerClass() work ? */
// PawnClass = Level.Game.GetDefaultPlayerClass(self); // PawnClass = Class'KFHumanPawn';
PawnClass = class 'KFStoryGame.KFHumanPawn_Story';
}
/*
Helper function for quickly determining whether a player is looking at a point in space.
original author : Ron Prestenback.
* @param TargetLocation the location to check
* @param MinDotResult a value between 0 and 1 representing the minimum dot result value that should be
* considered acceptable for testing whether the location is within our field of
* view. (Default value is corresponds to a maximum angle of around 47 degrees)
*
* @return TRUE if the location is within the angle specified from the player's line of sight.
*/
simulated function bool IsLookingAtLocation( vector TargetLocation, optional float MinDotResult)
{
local vector DirectionNormal, EyesLoc;
local rotator EyesRot;
local float ViewAngleCosine;
// get the location and rotation of the camera
EyesLoc = CalcViewLocation;;
EyesRot = CalcViewRotation;
// get the normalized distance between the two locations
DirectionNormal = Normal(TargetLocation - EyesLoc);
// get the dot result of the player's view location and the target location
ViewAngleCosine = DirectionNormal dot vector(EyesRot);
// determine if the angle between our camera's rotation and the target location is within range.
// log("IS LOOKING AT LOCATION - "@ViewAngleCosine@" Required : "@MinDotResult);
return ViewAngleCosine >= MinDotResult;
}
simulated function bool HasLineOfSightTo( vector TargetLocation)
{
return IsLookingAtLocation(TargetLocation,0.5f) && FastTrace(CalcViewLocation, TargetLocation) && FastTrace(TargetLocation,CalcViewLocation); //EyePostion() is returning a funny value sometimes. Not sure why. C.Pawn./*EyePosition()*/Location
}
/* Extended 'Use' functionality - Support for OnRelease events */
// The player wants to stop using something in the level.
exec function StopUsing()
{
ServerStopUsing();
// log(self@"stopped pressing Use");
}
/* === Server Debug Commands ==================
===============================================*/
/* Fast Forward*/
exec function FFObj(name ObjName)
{
if( !class'ROEngine.ROLevelInfo'.static.RODebugMode() )
{
return;
}
ServerFFObj(ObjName);
}
/* Helpful function for level Designers testing objectives at different stages in their map
Changes the active objective to the one provided. If bSimulateCompletion, it will also fast forward
through the Objective chain in an attempt to simulate actual player progression. */
function ServerFFObj(name ObjName)
{
local KF_StoryObjective NewObj;
local KFStoryGameInfo StoryGI;
StoryGI = KFStoryGameInfo(Level.Game);
if(StoryGI == none)
{
return;
}
NewObj = StoryGI.FindObjectiveNamed(ObjName);
if(NewObj != none && !StoryGI.bGameEnded )
{
if(!NewObj.IsValidForActivation())
{
ClientMessage("WARNING : ["@NewObj.ObjectiveName@"] has already been completed and is a non-recurring objective. It cannot be activated");
return ;
}
if(NewObj != StoryGI.CurrentObjective)
{
StoryGI.ForcedTargetObj = NewObj;
StoryGI.bSkipDialogue = true;
if(KF_StoryGRI(StoryGI.GameReplicationInfo) != none)
{
KF_StoryGRI(StoryGI.GameReplicationInfo).SetDebugTargetObj(NewObj);
}
StoryGI.GoToForcedObj();
ClientMessage("Fast tracking to ... "@NewObj.ObjectiveName,'CriticalEventPlus');
}
}
else
{
ClientMessage("WARNING : cannot find objective : "@ObjName);
}
}
exec function CompleteObj()
{
if( !class'ROEngine.ROLevelInfo'.static.RODebugMode() )
{
return;
}
ServerCompleteObj();
}
function ServerCompleteObj()
{
local KF_StoryObjective CurrentObjective;
if(KFStoryGameInfo(Level.Game) != none )
{
CurrentObjective = KFStoryGameInfo(Level.Game).CurrentObjective ;
if(CurrentObjective != none)
{
if(CurrentObjective.bCompleted)
{
Level.GetLocalPlayerController().ClientMessage("WARNING : ["@CurrentObjective.ObjectiveName@"] is already COMPLETE. ",'Story_Debug');
return ;
}
CurrentObjective.ForceCompleteObjective();
}
}
}
exec function StartUsing()
{
Super.ServerUse();
}
function ServerUse()
{
Super.ServerUse();
/* Hackey hack time!*/
if(KFStoryGameInfo(Level.Game) != none &&
KFStoryGameInfo(Level.Game).CurrentObjective != none)
{
KFStoryGameInfo(Level.Game).CurrentObjective.UsedBy(pawn);
}
}
function ServerStopUsing()
{
if ( Role < ROLE_Authority )
return;
if ( Level.Pauser == PlayerReplicationInfo )
{
SetPause(false);
return;
}
if (Pawn == None || !Pawn.bCanUse)
return;
/* Hackey hack time!*/
if(KFStoryGameInfo(Level.Game) != none &&
KFStoryGameInfo(Level.Game).CurrentObjective != none)
{
KFStoryGameInfo(Level.Game).CurrentObjective.StoppedUsing(pawn);
}
}
exec function SoakAI()
{
local AIController Bot;
log("Start Soaking");
UnrealMPGameInfo(Level.Game).bSoaking = true;
ForEach DynamicActors(class'AIController',Bot)
Bot.bSoaking = true;
}
exec function ObjDebug()
{
bShowObjectiveDebug = !bShowObjectiveDebug;
}
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
local KFStoryGameInfo StoryGI;
local int i;
StoryGI = KFStoryGameInfo(Level.Game);
if (StoryGI != none && bShowObjectiveDebug)
{
Canvas.SetDrawColor(50, 255, 50);
Canvas.DrawText("========== OBJECTIVE LIST ======================");
for(i = 0 ; i < StoryGI.AllObjectives.length ; i ++)
{
Canvas.DrawText("["$i+1$"]"@StoryGI.AllObjectives[i].ObjectiveName);
}
YPos += YL/2;
Canvas.SetPos(4, YPos);
}
else
{
Super.DisplayDebug(Canvas, YL, YPos);
}
}
simulated function UpdateHintManagement(bool bUseHints)
{
if (Level.GetLocalPlayerController() == self)
{
if (bUseHints && HintManager == none)
{
HintManager = spawn(class'KFHintManager_Story', self);
if (HintManager == none)
warn("Unable to spawn hint manager");
}
else if (!bUseHints && HintManager != none)
{
HintManager.Destroy();
HintManager = none;
}
if (!bUseHints)
if (HUDKillingFloor(myHUD) != none)
HUDKillingFloor(myHUD).bDrawHint = false;
}
}
function ServerThrowWeapon()
{
// If we have any story items, throw them one at a time
if( KFHumanPawn_Story(Pawn) != none &&
KFHumanPawn_Story(Pawn).IsCarryingThrowableInventory())
{
KFHumanPawn_Story(Pawn).TossSingleCarriedItem();
}
else
{
super.ServerThrowWeapon();
}
}
/* A late joining client wants to tell the server he's ready to play .. */
function ServerReadyLateJoiner()
{
if ( !Level.Game.bWaitingToStartMatch )
PlayerReplicationInfo.bReadyToPlay = true;
}
function SelectVeterancy(class<KFVeterancyTypes> VetSkill, optional bool bForceChange)
{
if ( VetSkill == none || KFPlayerReplicationInfo(PlayerReplicationInfo) == none )
{
return;
}
if ( KFSteamStatsAndAchievements(SteamStatsAndAchievements) != none )
{
SetSelectedVeterancy( VetSkill );
if ( KFStoryGameInfo(Level.Game) != none && // another place where we gotta use IsTraderObj() instead of bWaveInProgress ...
!KFStoryGameInfo(Level.Game).IsTraderTime() &&
VetSkill != KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill )
{
bChangedVeterancyThisWave = false;
ClientMessage(Repl(YouWillBecomePerkString, "%Perk%", VetSkill.Default.VeterancyName));
}
else if ( !bChangedVeterancyThisWave || bForceChange )
{
if ( VetSkill != KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill )
{
ClientMessage(Repl(YouAreNowPerkString, "%Perk%", VetSkill.Default.VeterancyName));
}
if ( GameReplicationInfo.bMatchHasBegun )
{
bChangedVeterancyThisWave = true;
}
KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill = VetSkill;
KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkillLevel = KFSteamStatsAndAchievements(SteamStatsAndAchievements).PerkHighestLevelAvailable(VetSkill.default.PerkIndex);
if( KFHumanPawn(Pawn) != none )
{
KFHumanPawn(Pawn).VeterancyChanged();
}
}
else
{
ClientMessage(PerkChangeOncePerWaveString);
}
}
}
state Dead
{ /*
function Timer()
{
There was some forced respawn code in KFGameType's implementation ...
I'm trying to cut out anything that could interfere with
the Story game's checkpoint respawn system
super(PlayerController).Timer();
} */
function Timer()
{
/* I REALLY hate to do this, but it's the only way to get it to work like it does in KFGameType*/
if ( KFGameType(Level.Game) != none && Level.Game.GameReplicationInfo.bMatchHasBegun &&
Role == ROLE_Authority && KFStoryGameInfo(Level.Game).IsTraderTime() )
{
PlayerReplicationInfo.Score = Max(KFGameType(Level.Game).MinRespawnCash, int(PlayerReplicationInfo.Score));
SetViewTarget(self);
ClientSetBehindView(false);
bBehindView = False;
ClientSetViewTarget(Pawn);
PlayerReplicationInfo.bOutOfLives = false;
Pawn = none;
ServerReStartPlayer();
}
super.Timer();
}
}
defaultproperties
{
PlayerReplicationInfoClass=class'KFStoryGame.KF_StoryPRI'
// bWantsTraderPath = true
LobbyMenuClassString="KFStoryUI.LobbyMenu_Story"
}

View file

@ -0,0 +1,82 @@
/*
--------------------------------------------------------------
KFScoreBoard_Story
--------------------------------------------------------------
Author : Alex Quick
--------------------------------------------------------------
*/
class KFScoreBoard_Story extends KFScoreboardNew;
/* Override to replace the Wavestring with the Title of the current objective */
function DrawTitle(Canvas Canvas, float HeaderOffsetY, float PlayerAreaY, float PlayerBoxSizeY)
{
local string TitleString, ScoreInfoString, RestartString;
local float TitleXL, ScoreInfoXL, YL, TitleY, TitleYL;
local KF_StoryObjective CurrentObj;
local string ObjString;
if(KF_StoryGRI(GRI) != none)
{
CurrentObj = KF_StoryGRI(GRI).GetCurrentObjective() ;
if(CurrentObj != none)
{
ObjString = CurrentObj.HUD_Header.Header_Text ;
}
}
TitleString = SkillLevel[Clamp(InvasionGameReplicationInfo(GRI).BaseDifficulty, 0, 7)] @ "|" @ ObjString @ "|" @ Level.Title;
Canvas.Font = class'ROHud'.static.GetSmallMenuFont(Canvas);
Canvas.StrLen(TitleString, TitleXL, TitleYL);
if ( GRI.TimeLimit != 0 )
{
ScoreInfoString = TimeLimit $ FormatTime(GRI.RemainingTime);
}
else
{
ScoreInfoString = FooterText @ FormatTime(GRI.ElapsedTime);
}
Canvas.DrawColor = HUDClass.default.RedColor;
if ( UnrealPlayer(Owner).bDisplayLoser )
{
ScoreInfoString = class'HUDBase'.default.YouveLostTheMatch;
}
else if ( UnrealPlayer(Owner).bDisplayWinner )
{
ScoreInfoString = class'HUDBase'.default.YouveWonTheMatch;
}
else if ( PlayerController(Owner).IsDead() )
{
RestartString = Restart;
if ( PlayerController(Owner).PlayerReplicationInfo.bOutOfLives )
{
RestartString = OutFireText;
}
ScoreInfoString = RestartString;
}
TitleY = Canvas.ClipY * 0.13;
Canvas.SetPos(0.5 * (Canvas.ClipX - TitleXL), TitleY);
Canvas.DrawText(TitleString);
Canvas.StrLen(ScoreInfoString, ScoreInfoXL, YL);
Canvas.SetPos(0.5 * (Canvas.ClipX - ScoreInfoXL), TitleY + TitleYL);
Canvas.DrawText(ScoreInfoString);
}
defaultproperties
{
}

View file

@ -0,0 +1,76 @@
/*
--------------------------------------------------------------
KeyPickup_Story
--------------------------------------------------------------
Custom ShopVolume for use in Story missions.
Implements its own version of the LevelRules actor's 'ItemForSale'
functionality, so that different story shops can be configured with
their own unique item lists.
Author : Alex Quick
--------------------------------------------------------------
*/
class KFShopVolume_Story extends ShopVolume;
/* stuff this shop sells */
var () array<class<Pickup> > SaleItems;
/* Text to display to players when they open the shop menu */
var () string WelcomeText;
/* Title of shop - displayed in the UI's header */
var() string ShopName;
/* Toggles display of the 'perks' header in the trader UI */
var() bool bShowPerkHeader;
/* fix for accessed nones */
function Touch( Actor Other )
{
if(MyTrader != none)
{
Super.Touch(Other);
}
}
/* fix for accessed nones */
function UnTouch( Actor Other )
{
if(MyTrader != none)
{
Super.UnTouch(Other);
}
}
function Trigger( actor Other, pawn EventInstigator )
{
SetCollision(!bCollideActors);
}
function UsedBy( Pawn user )
{
local KFPlayerController_Story StoryUser;
/* Grab a reference to this shop volume - we'll use it in the Trader UI Pages */
StoryUser = KFPlayerController_Story(user.Controller);
if(StoryUser != none)
{
StoryUser.CurrentShopVolume = self;
}
Super.UsedBy(User);
}
defaultproperties
{
bShowPerkHeader = true
bStatic = false
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
class KFStoryRoster extends xTeamRoster;
function bool AddToTeam( Controller Other )
{
if(Other.PlayerReplicationinfo == none)
{
return false;
}
return Super.AddToTeam(Other);
}

View file

@ -0,0 +1,19 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class KFTeamAI_Story extends KFTeamAI;
/* Hack for story NPCs - check their team Index number instead of the TeamInfo object ..
(They dont actually get put on teams with human players) */
function bool FriendlyToward(Pawn Other)
{
if(KF_StoryNPC(Other) != none)
{
return KF_StoryNPC(Other).TeamIndex == Team.TeamIndex ;
}
return Super.OnThisTeam(Other);
}

View file

@ -0,0 +1,105 @@
/*
--------------------------------------------------------------
KFUseTrigger_Story
--------------------------------------------------------------
Custom Use Trigger that can trigger additional events.
Author : Alex Quick
--------------------------------------------------------------
*/
class KFUseTrigger_Story extends KFUseTrigger;
/* event to fire off when the doors associated with this trigger have been fully welded shut */
var(Events) name FullWeldEvent;
/* if true, this trigger starts life dormant. it can only be 'used' once it has been triggered */
var() bool bTriggerEnabled;
var() bool bEnabled;
var() bool bAllowZEDInteraction;
var() bool bShouldBeEnabledInWaveMode;
function PostBeginPlay()
{
Super.PostBeginPlay();
if(KFStoryGameinfo(Level.Game) == none)
{
bEnabled = bShouldBeEnabledInWaveMode;
}
}
function bool TriggerIsUseable()
{
return bEnabled && bCollideActors;
}
function AddWeld( float ExtraWeld, bool bZombieAttacking, Pawn WelderInst )
{
if(!TriggerIsUseable() && WelderInst != none)
{
return;
}
Super.AddWeld(ExtraWeld,bZombieAttacking,WelderInst);
if(WeldStrength >= MaxWeldStrength)
{
TriggerEvent(FullWeldEvent,self,none);
}
}
function UnWeld(float DeWeldage,bool bZombieAttacking, Pawn WelderInst)
{
if(!TriggerIsUseable() && WelderInst != none)
{
return;
}
Super.UnWeld(DeWeldage,bZombieAttacking,WelderInst);
}
function UsedBy(Pawn user)
{
if(!TriggerIsUseable())
{
return;
}
Super.UsedBy(user);
}
function Touch( Actor Other )
{
if(bEnabled && (!Other.IsA('KFMonster') || bAllowZEDInteraction))
{
Super.Touch(Other);
}
}
function Reset()
{
Super.Reset();
bEnabled = !bTriggerEnabled ;
}
function Trigger( actor Other, pawn EventInstigator )
{
if(bTriggerEnabled)
{
bEnabled = !bEnabled;
}
Super.Trigger(other,EventInstigator);
}
defaultproperties
{
bShouldBeEnabledInWaveMode = true
bEnabled = true
}

View file

@ -0,0 +1,253 @@
/*
--------------------------------------------------------------
KF_StoryNPC_Static
--------------------------------------------------------------
Repairable breaker box NPC for use in Objective missions.
Has support for different visual damage states.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_BreakerBoxNPC extends KF_StoryNPC_Static;
var float LastTriggerEventTime;
var Material BrokenMat,FixedMat;
var () bool bWeldable;
var(Events) name Event_FullHealth,Event_NoHealth;
var bool bRepaired,SavedbFullhealth,bDestroyed,SavedbNoHealth;
var(Display) StaticMesh HealthyMesh,DestroyedMesh;
var class<Emitter> HitEmitter;
replication
{
reliable if(Role == Role_Authority && bNetDirty)
bRepaired;
}
function Reset()
{
Super.Reset();
if(bCheckPointed)
{
bRepaired = SavedbFullHealth;
bDestroyed = SavedbNoHealth;
}
CheckHealthCondition(self);
}
simulated function PostBeginPlay()
{
Super.PostbeginPlay();
CheckHealthCondition(self);
bHidden = true; // breaker boxes start out hidden and non-interactable.
SetCollision(false,false);
}
function SetActive(bool On)
{
Super.SetActive(On);
if(bActive)
{
bHidden = false;
SetCollision(true,true);
}
}
simulated function Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if(!bDestroyed && Level.TimeSeconds -LastTriggerEventTime > 1.0)
{
LastTriggerEventTime = Level.TimeSeconds;
TriggerEvent('IncrementPower',self,self);
}
}
function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
local bool bHealed;
if ( bWeldable && damageType == class 'DamTypeWelder' )
{
Health = Min(Health + Damage, NPCHealth);
bHealed = true;
}
CheckHealthCondition(InstigatedBy);
if(bHealed)
{
return; // can skip the rest of the takedamage stuff.
}
Super.TakeDamage(Damage,InstigatedBy,HitLocation,Momentum,damageType,HitIndex);
CheckhealthCondition(InstigatedBy);
}
simulated function CheckHealthCondition(Pawn InstigatedBy)
{
if(!bRepaired && Health >= NPCHealth)
{
bRepaired = true;
bDestroyed = false;
TriggerEvent(Event_FullHealth,self,InstigatedBy);
if(HealthyMesh != none)
{
SetStaticMesh(HealthyMesh);
}
}
else
{
if(!bDestroyed && Health <= 0 )
{
bDestroyed = true;
bRepaired = false;
TriggerEvent(Event_NoHealth,self,InstigatedBy);
if(DestroyedMesh != none)
{
SetStaticMesh(DestroyedMesh);
}
}
}
}
/* Draw additional icons for Breaker boxes */
simulated event PostRender2D(Canvas C, float ScreenLocX, float ScreenLocY) // called if bScriptPostRender is true, overrides native team beacon drawing code
{
local PlayerController PC;
local float Opacity;
// local float Dist;
local HUDKillingFloor KFHUD;
local float IconSize;
local float XCentre,YCentre;
local vector ScreenPos;
local float HealthPct;
local Material RenderMat;
local vector CameraLocation,CamDir;
local rotator CameraRotation;
if(!bShowHealthBar ||
!bActive ||
GetStateName() == 'Dying' )
{
return;
}
IconSize = 64.f * (C.SizeX/1920.f);
PC = Level.GetLocalPlayerController();
if ( PC != None )
{
KFHUD = HUDKillingFloor(PC.myHUD);
if(KFHUD != none)
{
C.GetCameraLocation(CameraLocation, CameraRotation);
CamDir = vector(CameraRotation);
/* Dont render stuff behind the camera */
if ( (Normal(Location - CameraLocation) dot vector(CameraRotation)) < 0 )
{
return;
}
if(bRepaired)
{
RenderMat = FixedMat ;
}
else
{
RenderMat = BrokenMat;
}
ScreenPos = C.WorldToScreen(Location);
XCentre = ScreenPos.X;
YCentre = ScreenPos.Y;
Opacity = FClamp(1.f - (VSize(PC.CalcViewLocation - Location) / 3000.f),0.4f,1.f) ;
HealthPct = FClamp(float(Health) / NPCHealth,0.f,1.f);
C.DrawColor.R = Lerp(HealthPct,255,0);
C.DrawColor.G = Lerp(HealthPct,0,255);
C.DrawColor.B = 50;
C.DrawColor.A = Opacity * 255;
C.SetPos(XCentre - (0.5 * IconSize) + 1.0, YCentre - (0.5 * IconSize) + 1.0);
C.DrawTileScaled(RenderMat, IconSize/ RenderMat.MaterialVSize() ,IconSize/ RenderMat.MaterialVSize() );
}
}
}
// Breaker boxes should never spawn gibs even if it looks cool
simulated function SpawnGibs(Rotator HitRotation, float ChunkPerterbation)
{}
// Replace the damage type hit emitter with a custom one
function SpawnHitEmitter( float Damage, Pawn InstigatedBy, vector HitLocation, class<DamageType> damageType, vector Momentum )
{
local Vector HitNormal;
// Play any set effect
if ( EffectIsRelevant(Location,true) )
{
if (HitEmitter != None)
{
if( InstigatedBy != none )
HitNormal = Normal((InstigatedBy.Location+(vect(0,0,1)*InstigatedBy.EyeHeight))-HitLocation);
Spawn(HitEmitter,,,HitLocation+HitNormal + (-HitNormal * CollisionRadius), Rotator(HitNormal));
}
}
}
defaultproperties
{
NotThreateningTo(0)= class 'ZombieFleshPound'
NotThreateningTo(1)= class 'ZombieScrake'
DrawScale = 0.5
Skins(0) = none
FixedMat = Texture 'KFStoryGame_Tex.HUD.Electricity_Icon_64'
BrokenMat = Texture 'KFStoryGame_Tex.HUD.Repair_Icon_64_BW'
StaticMesh = StaticMesh'Props_ObjectiveMode.Breaker_Box'
DestroyedMesh = StaticMesh 'Props_ObjectiveMode.Breaker_Box_DMG'
HealthyMesh = StaticMesh'Props_ObjectiveMode.Breaker_Box'
BaseAIThreatRating = 0.010000
FriendlyFireDamageScale = 0
NPCHealth = 200.000000
NPCName = "Breaker Box"
StartingHealthPct = 0
bStartActive = false
bShowHealthBar = true
bIndestructible = true
bWeldable = true
HitEmitter=class 'kfmod.Breaker_Damaged_OneOff'
ProjectileBloodSplatClass=none
CollisionRadius=30
PrePivot=(X=0,Y=0,Z=30)
}

View file

@ -0,0 +1,464 @@
/*
--------------------------------------------------------------
KF_DialogueSpot
--------------------------------------------------------------
Displays dialogue on the screen with a header and image.
Optionally plays voicover audio.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_DialogueSpot extends Info
dependson(KFStoryGameInfo)
placeable;
var int CurrentMsgIdx;
var Controller DialogueInstigator;
var() array<KFStoryGameInfo.SDialogueEntry> Dialogues;
/* true if this dialogue spot should display its text when a player touches the dialogue actor's collision cylinder */
var bool bTouchTriggered;
/* true if this dialogue can be 'skipped' by moving out of range of the speaker*/
var bool bCanSkipDlg;
/* the range at which dialogue will be skipped if bCanSkipDlg*/
var float SkipDlgRange;
/* if true, the dialogue will play randomly each time it is triggered rather than in order */
var() bool bRandomize;
// if true, this dialogue can be played multiple times. If not, only allow one play of each dialogue
var() bool bAllowRepeatDialogue;
var bool bDebugDialogue;
var bool bTraversing;
var bool bFinished;
var bool bSingleTouchOnly;
var bool bTouched;
function Reset()
{
local int i, SavedIdx;
local array<byte> SavedTriggerStates;
Super.Reset();
/* Partial reset using saved positions*/
bTraversing = false;
if(!bFinished)
{
SavedIdx = KFStoryGameInfo(Level.Game).CurrentCheckPoint.GetSavedDialogueIndexFor(self,SavedTriggerStates) ;
CurrentMsgIdx = SavedIdx;
if(bDebugDialogue)
{
log("----- DIALOGUE DEBUG ------ Resetting "@self@". Dialogue will play back from Index : "@SavedIdx, 'Story_Debug');
}
for(i = 0 ; i < Dialogues.length ; i ++)
{
Dialogues[i].bWasTriggered = bool(SavedTriggerStates[i]);
}
}
else /* Full reset from the start. */
{
for(i = 0 ; i < Dialogues.length ; i ++)
{
Dialogues[i].bWasTriggered = false;
}
CurrentMsgIdx = 0;
bFinished = false;
}
}
function Destroyed()
{
local int i;
/* Perform cleanup on any event listeners */
for(i = 0 ; i < Dialogues.length ; i ++)
{
if(Dialogues[i].EventListener != none)
{
Dialogues[i].EventListener.Destroy();
}
}
}
/* Dialogue callback - timing is handled in the event listener */
function OnDialogueDisplayComplete(int Index)
{
// log("Dialogue - Triggering Displayed Event : "@Dialogues[Index].Events.DisplayedEvent);
if(Dialogues[Index].Events.DisplayedEvent != '')
{
TriggerEvent(Dialogues[Index].Events.DisplayedEvent,self,DialogueInstigator.Pawn);
}
Dialogues[Index].bWasTriggered = false;
}
function PostBeginPlay()
{
local int i;
local Dialogue_EventListener NewListener;
/* No KFO Gametype, no Initialization */
if(KFStoryGameinfo(Level.Game) == none)
{
return;
}
// Spawn Event listeners for Dialogue entries which require them.
for(i = 0 ; i < Dialogues.length ; i ++)
{
NewListener = Spawn(class 'Dialogue_EventListener',self,Dialogues[i].Events.RequiredEvent);
NewListener.AssociatedIndex = i ;
Dialogues[i].EventListener = NewListener;
}
}
function Trigger( actor Other, pawn EventInstigator )
{
if(EventInstigator != none &&
EventInstigator.Controller != none)
{
DialogueInstigator = EventInstigator.Controller;
}
TraverseDialogue();
}
function bool SelfTriggered()
{
return bTouchTriggered;
}
function Touch( Actor Other )
{
if ( Pawn(Other) != None &&
PlayerController(Pawn(Other).Controller) != none &&
SelfTriggered() &&
(!bSingleTouchOnly || !bTouched))
{
bTouched = true;
Trigger(self,Pawn(Other));
}
}
function int GetIndexFor(KFstoryGameInfo.SDialogueEntry TestDlg)
{
local int i;
for(i = 0 ; i < Dialogues.length ; i ++)
{
if(Dialogues[i] == TestDlg)
{
return i;
}
}
return - 1 ;
}
/* Retrieves the index of the next dialogue entry in a DialogueSpot actor with a 'required event'.
Necessary in cases where the game is restarting from an objective in a non-linear fashion and
certain intermediate dialogue needs to be skipped
*/
function int GetNextDlgRequiredEventIdx(int TestIndex)
{
local int i;
for( i = TestIndex + 1 ; i < Dialogues.length ; i ++)
{
if(Dialogues[i].Events.RequiredEvent != '')
{
return i ;
}
}
return -1;
}
function TraverseDialogue()
{
local bool bWaitingForEvent ;
/* Hit the end. early out */
if( Dialogues.length <= CurrentMsgIdx || bTraversing || (bFinished && !bAllowRepeatDialogue))
{
return;
}
if(bRandomize)
{
CurrentMsgIdx = RandRange(0,Dialogues.length) ;
}
/* If the next index after this one is event-triggered, don't set the timer */
if(Dialogues[CurrentMsgIdx].Events.RequiredEvent != '' &&
!Dialogues[CurrentMsgIdx].bLooping)
{
bWaitingForEvent = !Dialogues[CurrentMsgIdx].bWasTriggered;
}
if(!bWaitingForEvent)
{
if(bDebugDialogue)
{
log("----- DIALOGUE DEBUG ------ Showing Dialogue at index .. "@CurrentMsgIdx, 'Story_Debug');
}
ShowDialogue(CurrentMsgIdx);
if(CurrentMsgIdx + 1 < Dialogues.length )
{
bTraversing = true;
if(GetCurrentDisplayDur() > 0)
{
SetTimer(GetCurrentDisplayDur(),false);
}
else
{
Timer();
}
}
else
{
/* we're finished. Reset the actor */
OnDialogueCompleted();
}
}
else
{
if(bDebugDialogue)
{
log("----- DIALOGUE DEBUG ------ Freezing dialogue at index :"@CurrentMsgIdx@"for event : "@Dialogues[CurrentMsgIdx].Events.RequiredEvent, 'Story_Debug');
}
}
}
function OnDialogueCompleted()
{
bFinished = true;
}
function Timer()
{
if(bTraversing)
{
bTraversing = false;
if(CurrentMsgIdx + 1 < Dialogues.length && !bRandomize)
{
if(!Dialogues[CurrentMsgIdx].bLooping)
{
CurrentMsgIdx ++ ;
if(bDebugDialogue)
{
log("----- DIALOGUE DEBUG ------ Incrementing Dialogue Index .. ", 'Story_Debug');
}
TraverseDialogue();
}
}
}
}
/* Helper function - calculates the amount of time a string should
be displayed for based on its word length */
function float CalcDisplayTime(string InString)
{
local float TimePerWord;
local array<string> Words;
TimePerWord = 0.5f;
Split(InString," ",Words);
return FMax(Words.length * TimePerWord,3.f);
}
function ShowDialogue(int DlgIndex)
{
local Controller C;
local KFPlayerController_Story StoryPC;
local string SpeakerName;
local actor VoiceOverSource;
local Material Portrait;
local Pawn MyInstigator;
/* nothing to show, nothing to play ... early out */
if(Dialogues[DlgIndex].Display.Dialogue_Text == "" &&
Dialogues[DlgIndex].VoiceOver.VoiceOverSound == none)
{
return;
}
for (C = Level.ControllerList; C != None; C = C.NextController)
{
StoryPC = KFPlayerController_Story(C);
if(StoryPC != none &&
(Dialogues[DlgIndex].BroadcastScope != InstigatorOnly ||
(DialogueInstigator != none && DialogueInstigator == C) ))
{
SpeakerName = Dialogues[DlgIndex].Display.Dialogue_Header ;
Portrait = Dialogues[DlgIndex].Display.Portrait_Material ;
if(SpeakerName == "self")
{
SpeakerName = StoryPC.PlayerReplicationinfo.PlayerName ;
Portrait = xPlayerReplicationInfo(StoryPC.PlayerReplicationInfo).Rec.Portrait;
}
if(GetCurrentDisplayDur() > 0)
{
Dialogues[DlgIndex].EventListener.SetTimer(GetCurrentDisplayDur(),false);
}
else
{
OnDialogueDisplayComplete(DlgIndex);
}
if(!SkipDialogue())
{
StoryPC.ClientShowStoryDialogue(name ,DlgIndex,GetCurrentDisplayDur());
}
if ( !SkipDialogue() && Dialogues[DlgIndex].VoiceOver.VoiceOverSound != None )
{
/* attempt to play a spatialised sound from the source actor (if there is one) */
VoiceOverSource = Dialogues[DlgIndex].VoiceOver.SourceActor ;
if(VoiceOverSource != none)
{
VoiceOverSource.PlaySound(Dialogues[DlgIndex].VoiceOver.VoiceOverSound,SLOT_Talk,VoiceOverSource.SoundVolume,,VoiceOverSource.SoundRadius,VoiceOverSource.SoundPitch,VoiceOverSource.bFullVolume);
}
else /*otherwise just play a ClientSound */
{
StoryPC.ClientPlayStorySound(Dialogues[DlgIndex].VoiceOver.VoiceOverSound, 2.0, false );
}
}
if(Dialogues[DlgIndex].Events.DisplayingEvent != '')
{
MyInstigator = DialogueInstigator.Pawn;
TriggerEvent(Dialogues[DlgIndex].Events.DisplayingEvent,self,MyInstigator);
}
}
}
}
function bool SkipDialogue()
{
return KFStoryGameInfo(Level.Game) != none && KFStoryGameInfo(Level.Game).bSkipDialogue;
}
function float GetCurrentDisplayDur()
{
local float Duration,VODuration;
if( SkipDialogue())
{
return 0.f;
}
Duration = CalcDisplayTime(Dialogues[CurrentMsgIdx].Display.Dialogue_Text);
/* Always make sure the text sticks around for at least as long as the VO */
if(Dialogues[CurrentMsgIdx].VoiceOver.VoiceOverSound != none)
{
VODuration = Dialogues[CurrentMsgIdx].VoiceOver.VoiceOverSound.Duration;
Duration = VODuration * 1.15; // Gamespeed is 1.1 so you always have to multiplay sound durations by 1.1. Adding a bit more buffer just in case - Ramm
}
return Duration;
}
function GetRequiredEvents( out array<Name> AllRequiredEvents)
{
local int i;
for(i = 0 ; i < Dialogues.length ; i ++)
{
if(Dialogues[i].Events.RequiredEvent != '')
{
AllRequiredEvents[AllRequiredEvents.length] = Dialogues[i].Events.RequiredEvent;
}
}
}
function GetDisplayEvents( out array<Name> AllDisplayEvents)
{
local int i;
for(i = 0 ; i < Dialogues.length ; i ++)
{
if(Dialogues[i].Events.DisplayedEvent != '')
{
AllDisplayEvents[AllDisplayEvents.length] = Dialogues[i].Events.DisplayedEvent;
}
if(Dialogues[i].Events.DisplayingEvent != '')
{
AllDisplayEvents[AllDisplayEvents.length] = Dialogues[i].Events.DisplayingEvent;
}
}
}
function GetEvents(out array<name> TriggeredEvents, out array<name> ReceivedEvents)
{
local int i;
Super.GetEvents(TriggeredEvents,ReceivedEvents);
for(i = 0 ; i < Dialogues.length ; i ++)
{
/* Things I trigger */
if(Dialogues[i].Events.DisplayedEvent != '')
{
TriggeredEvents[TriggeredEvents.length] = Dialogues[i].Events.DisplayedEvent;
}
if(Dialogues[i].Events.DisplayingEvent != '')
{
TriggeredEvents[TriggeredEvents.length] = Dialogues[i].Events.DisplayingEvent;
}
/* Things that trigger me*/
if(Dialogues[i].Events.RequiredEvent != '')
{
ReceivedEvents[ReceivedEvents.length] = Dialogues[i].Events.RequiredEvent;
}
}
}
defaultproperties
{
DrawScale = 0.5
bDebugDialogue = true
bNoDelete = true
bCollideActors = true
SkipDlgRange = 250
Texture = Texture 'KFStoryGame_Tex.Editor.KF_Dlgspot_Ico'
}

View file

@ -0,0 +1,65 @@
/*
--------------------------------------------------------------
KF_HUDStyleManager
--------------------------------------------------------------
Placeable actor able to store multiple skin presets for the
players HUD.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_HUDStyleManager extends Info
dependson(KFStoryGameInfo)
placeable;
struct SConditionStyle
{
var() KFStoryGameInfo.SConditionHintInfoWorld Style_ObjCondition_World;
var() KFStoryGameInfo.SConditionHintInfoHUD Style_ObjCondition_Screen;
var() Color Condition_Clr;
};
struct SObjectiveStyle
{
var() KFStoryGameInfo.SObjectiveHeaderInfo Header;
var() KFStoryGameInfo.SObjectiveBackgroundInfo Background;
var() KFStoryGameInfo.SVect2D Position;
var() SConditionStyle Conditions;
var() bool bOverride;
};
struct SMainHUDStyle
{
var() Material Ammo_Background;
var() bool bOverride;
};
struct SDialogueStyle
{
var() KFStoryGameInfo.SDialogueDisplayInfo Dialogue_Box;
var() bool bOverride;
};
struct SStylePreset
{
var() name StyleName;
var() SDialogueStyle Dialogue;
var() SObjectiveStyle Objectives;
var() SMainHUDStyle MainHUD;
};
var() SStylePreset StylePreset;
defaultproperties
{
bNoDelete = true
}

View file

@ -0,0 +1,51 @@
/*
--------------------------------------------------------------
KF_ObjectiveAction
--------------------------------------------------------------
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_ObjectiveAction extends BaseObjectiveAction
hidecategories(Object);
/* Reference to the Objective that this action belongs to */
var private KF_StoryObjective ObjOwner;
/* Is this action currently being processed ? */
var bool bActive;
function bool IsValidActionFor(KF_StoryObjective Obj)
{
return true;
}
function KF_StoryObjective GetObjOwner()
{
return ObjOwner;
}
function SetObjOwner(Actor NewOwner)
{
ObjOwner = KF_StoryObjective(NewOwner);
}
function ActionActivated(pawn ActivatingPlayer)
{
bActive = true;
}
function ActionDeActivated()
{
Reset();
}
function Reset()
{
bActive = false;
}

View file

@ -0,0 +1,849 @@
/*
--------------------------------------------------------------
KF_ObjectiveCondition
--------------------------------------------------------------
Object used to store gameplay conditions for KF_StoryObjectives
Configured by level designers.
Author : Alex Quick
--------------------------------------------------------------
*/
#exec OBJ LOAD FILE=KFStoryGame_Tex.utx
class KF_ObjectiveCondition extends Object
abstract
dependson(KFStoryGameInfo)
hidecategories(Object);
const HintCharLimit = 35;
/* Pawn responsible for Activating and / or completing this condition. */
var const name InstigatorName;
var bool bComplete,SavedbComplete;
var float LastActivatedTime;
var float LastRepTime;
/* Minimum time between client updates of condition data */
var (Network) float ConditionRepInterval;
/* HUD properties ======================================================================
=======================================================================================*/
/* HUD Properties that pertain to the world (Wisp trail, projected icons, etc.) */
var (HUD) KFStoryGameInfo.SConditionHintInfoWorld HUD_World;
/* HUD Properties that pertain to the player's On-screen display */
var (HUD) KFStoryGameInfo.SConditionHintInfoHUD HUD_Screen;
var float OldCompletionPct,NewCompletionPct;
var bool OldComplete,NewComplete;
var string OldDataString,NewDataString;
var vector OldWorldLoc,NewLocation;
/* =====================================================================================
=======================================================================================*/
var bool bActive,SavedbActive;
var bool bWasTriggered,SavedbWasTriggered;
var byte ConditionType; // 0 = failure, 1 = success , 2 = optional
/* An array that stores Condition dependencies.
ie. We only want this condition to be considered for completion if the
conditions in this list are already completed and share the same instigator as us.
an example of this would be a Key locked door */
var() array <KF_ObjectiveCondition> DependentConditions;
/* If true this objective cannot be 'uncompleted' once it is complete, without being reset */
var() bool bCompleteOnce;
var bool bLockCompletion;
var bool bForceReliableUpdate;
/* Difficulty Modifiers ============================================================*/
enum EKFGameDifficulty
{
All,
Beginner,
Normal,
Hard,
Suicidal,
HellOnEarth,
};
/* Only use this condition above MinDifficulty and below MaxDifficulty */
var(Difficulty) EKFGameDifficulty Difficulty_Min,Difficulty_Max;
var(Difficulty) int PlayerCount_Min,PlayerCount_Max;
/* Scales the Condition's requirements based on the server's difficulty setting */
var(Difficulty) KFStoryGameInfo.SConditionDifficultyScale Scale_GameDifficulty;
/* Scales the Condition's requirements based on the number of players on the server */
var(Difficulty) float Scale_PlayerCount;
var private transient KF_StoryObjective ObjOwner;
/* =====================================================================================
=======================================================================================*/
var(Events) array<KFStoryGameInfo.SObjectiveProgressEvent> ProgressEvents;
/* =====================================================================================
=======================================================================================*/
var private KF_Objective_EventListener Eventlistener;
var(Events) name Tag;
var() KFStoryGameInfo.EConditionInitialState InitialState;
var() KFStoryGameInfo.EConditionActivationMethod ActivationMethod;
var() KFStoryGameInfo.EProgressImportance ProgressImportance;
var const name InitialWorldLocActorName, WorldLocActorName;
/*========== AUDIO =====================================================================*/
var(Audio) sound Sound_Completion;
function StoppedUsing(pawn User){}
function StartedUsing(pawn User){}
function PostBeginPlay(KF_StoryObjective MyOwner)
{
SpawnEventListener(MyOwner);
// cache and clear.
if(HUD_World.World_Location != none)
{
SetTargetActor(InitialWorldLocActorName,HUD_World.World_Location);
SetTargetActor(WorldLocActorName,HUD_World.World_Location);
HUD_World.World_Location = none;
}
}
function SaveState()
{
SavedbComplete = bComplete;
SavedbActive = bActive;
SavedbWasTriggered = bWasTriggered;
}
function Actor GetTargetActor(name TargetName)
{
local Actor TargetActor;
EventListener.FindAssociatedActor(TargetName,TargetActor);
return TargetActor;
}
function SetTargetActor(name NewTargetName, Actor NewValue)
{
Eventlistener.AddAssociatedActor(NewTargetName,NewValue);
}
function ReleaseTargetActor(name ReleaseTargetName)
{
EventListener.RemoveAssociatedActor(ReleaseTargetname);
}
function Reset()
{
local int i;
if(GetObjOwner().ActiveCheckPoint == none)
{
bWasTriggered = false;
bActive = false;
bComplete = false;
}
else
{
bWasTriggered = SavedbWasTriggered;
bActive = SavedbActive;
bComplete = SavedbComplete;
}
for(i = 0 ; i < ProgressEvents.Length ; i ++)
{
ProgressEvents[i].bWasTriggered = false;
}
OldDataString = "";
OldCompletionPct = 0.f;
OldWorldLoc = vect(0,0,0);
NewLocation = vect(0,0,0);
bLockCompletion = false;
}
function Trigger(actor Other, pawn EventInstigator)
{
if(ConditionIsValid())
{
SetTargetActor(InstigatorName,EventInstigator);
switch(ActivationMethod)
{
case TriggerToggled :
if(!bActive)
{
// log("**********************************************");
// log("Activating Condition By Trigger - :"@name);
GetObjOwner().ActivateCondition(self);
return;
}
else
{
// log("**********************************************");
// log("Deactivating Condition By Trigger - :"@name);
GetObjOwner().DeActivateCondition(self);
return;
}
break;
case TriggerActivates :
if(!bActive)
{
GetObjOwner().ActivateCondition(self);
return;
}
break;
case TriggerDeActivates :
if(bActive)
{
GetObjOwner().DeActivateCondition(self);
return;
}
break;
}
}
if(bActive)
{
bWasTriggered = true;
}
}
function bool ConditionIsRelevant()
{
return true;
}
function bool ConditionIsActive()
{
// log(self@"Owner : "@GetObjOwner().ObjectiveName@" Complete ? : "@bComplete@" Active ? : "@bActive,'Story_Debug');
return GetObjOwner() != none && bActive && (!bCompleteOnce || !bComplete);
}
/* Returns true if this condition should initialize automatically when its owning objective becomes active
In most cases this will be true ... except for complex conditions which the LD intentionally wants to activate
manually (after certain events in the mission , etc.)
*/
function bool ShouldInitOnActivation()
{
local bool Result;
if(InitialState == Active ||
(ActivationMethod == RandomlyActivate &&
FRand() > 0.5)) // condition is valid for activation
{
Result = true;
}
return Result && ConditionIsValid();
}
/* Only returns true if this condition is valid for activation - based on various factors like Game Difficulty, Player Count, etc.
*/
function bool ConditionIsValid()
{
local int NumPlayers;
local float CurrentDifficulty,MaxDifficulty,MinDifficulty;
local bool Result;
Result = true;
/* =============== Check Difficulty & Player Count validitiy ======*/
/* Make sure the values are in range first .. */
PlayerCount_Min = Min(PlayerCount_Min,PlayerCount_Max);
PlayerCount_Max = Max(PlayerCount_Max,PlayerCount_Min);
if(GetObjOwner() != none)
{
NumPlayers = KFStoryGameInfo(GetObjOwner().Level.Game).GetTotalActivePlayers();
CurrentDifficulty = GetObjOwner().Level.Game.GameDifficulty;
}
if((PlayerCount_Max > 0 && NumPlayers > PlayerCount_Max) ||
(PlayerCount_Min > 0 && NumPlayers < PlayerCount_Min))
{
return false;
}
if(Difficulty_Max > 0)
{
switch(Difficulty_Max)
{
case Beginner : MaxDifficulty = 1.f ; break;
case Normal : MaxDifficulty = 2.f ; break;
case Hard : MaxDifficulty = 4.f ; break;
case Suicidal : MaxDifficulty = 5.f ; break;
case HellOnEarth : MaxDifficulty = 7.f ; break;
}
Result = CurrentDifficulty <= MaxDifficulty ;
}
if(Difficulty_Min > 0)
{
switch(Difficulty_Min)
{
case Beginner : MinDifficulty = 1.f ; break;
case Normal : MinDifficulty = 2.f ; break;
case Hard : MinDifficulty = 4.f ; break;
case Suicidal : MinDifficulty = 5.f ; break;
case HellOnEarth : MinDifficulty = 7.f ; break;
}
return Result && CurrentDifficulty >= MinDifficulty ;
}
return Result;
}
/* Returns a value that scales this condition's requirements based on the Game Difficulty setting of the server */
function float GetGameDifficultyModifier()
{
local float CurrentDifficulty,DiffModifier;
CurrentDifficulty = GetObjOwner().Level.Game.GameDifficulty;
DiffModifier = 1.f;
switch(CurrentDifficulty)
{
case 1 : DiffModifier = Scale_GameDifficulty.Scale_Beginner; break; // Beginner.
case 3 : DiffModifier = Scale_GameDifficulty.Scale_Hard; break; // Hard.
case 5 : DiffModifier = Scale_GameDifficulty.Scale_Suicidal; break; // Suicidal.
case 7 : DiffModifier = Scale_GameDifficulty.Scale_HellOnEarth; break; // Hell On Earth.
}
return DiffModifier;
}
/* Returns a value that scales this condition's requirements based on the number of players on the server */
function float GetPlayerCountModifier()
{
local int NumPlayers;
NumPlayers = KFStoryGameInfo(GetObjOwner().Level.Game).GetTotalActivePlayers();
if(NumPlayers <= 1)
{
return 1.f;
}
return FMax(NumPlayers * Scale_PlayerCount, 1.f);
}
function float GetTotalDifficultyModifier()
{
return GetGameDifficultyModifier() * GetPlayerCountModifier();
}
function ConditionActivated(pawn ActivatingPlayer)
{
SetTargetActor(InstigatorName,ActivatingPlayer);
LastActivatedTime = GetObjOwner().Level.TimeSeconds;
bActive = true;
if(GetTargetActor(InitialWorldLocActorName) != none)
{
SetTargetActor(WorldLocActorName,GetTargetActor(InitialWorldLocActorName));
}
ReliableConditionUpdate();
}
function ConditionDeActivated()
{
Reset();
}
function bool IsOptionalCondition()
{
return ConditionType == 2;
}
function SpawnEventListener(KF_StoryObjective MyOwner)
{
if(Eventlistener == none )
{
EventListener = MyOwner.Spawn(class 'KF_Objective_EventListener');
EventListener.SetConditionOwner(self);
}
}
/* Allow Completion of this Condition
always true unless there is an outstanding dependency */
function bool AllowCompletion()
{
local int i;
local bool Result;
Result = true;
for(i = 0 ; i < DependentConditions.length ; i ++)
{
if(!DependentConditions[i].bComplete ||
!DependentConditions[i].FindInstigator(GetInstigatorList()))
{
Result = false;
break;
}
}
return Result;
}
/* Some conditions may have multiple instigators */
function bool FindInstigator(array<Pawn> TestInstigators)
{
local int i,idx;
local array<Pawn> MyInstigatorList;
MyInstigatorList = GetInstigatorList();
for(i = 0 ; i < TestInstigators.length ; i ++)
{
for(idx = 0 ; idx < MyInstigatorList.length ; idx ++)
{
if(TestInstigators[i] == MyInstigatorList[idx])
{
return true;
}
}
}
return false;
}
function array<Pawn> GetInstigatorList()
{
local array<Pawn> Instigators;
Instigators[Instigators.length] = Pawn(GetTargetActor(InstigatorName));
return Instigators;
}
function ConditionTick(float DeltaTime)
{
local float PctComplete;
PctComplete = GetCompletionPct();
TriggerProgressEvents(PctComplete);
if(!bComplete)
{
if(PctComplete >= 1.f &&
AllowCompletion())
{
ConditionCompleted();
}
}
else
{
if(!bLockCompletion &&
!bCompleteOnce &&
PctComplete < 1.f)
{
bComplete = false;
}
}
/* Timed HUD Update - replicated , so do it only when the values actually change.*/
ConditionTickHUDUpdate();
}
/* Progress Event updates - Fired off at different stages in the condition's completion */
function TriggerProgressEvents(float PctComplete)
{
local int EventIdx;
for(EventIdx = 0 ; EventIdx < ProgressEvents.length ; EventIdx ++)
{
if(PctComplete >= ProgressEvents[EventIdx].ProgressPct &&
(!ProgressEvents[EventIdx].bWasTriggered ||
(ProgressEvents[EventIdx].bReTriggerable &&
PctComplete != ProgressEvents[EventIdx].LastTriggeredPct ) ) )
{
ProgressEvents[EventIdx].bWasTriggered = true;
GetObjOwner().TriggerEvent( ProgressEvents[EventIdx].EventName,GetObjOwner(),Pawn(GetTargetActor(InstigatorName)));
}
ProgressEvents[EventIdx].LastTriggeredPct = PctComplete;
}
}
function bool AllowConditionRepUpdate()
{
if( GetObjOwner().Level.TimeSeconds - LastRepTime < ConditionRepInterval ||
(HUD_World.bHide && HUD_Screen.Screen_ProgressStyle == 0 ))
{
return false;
}
return true;
}
/* Reliable HUD Update - Sends condition information to clients. Only used when a player
absolutely MUST get accurate data from this condition.
*/
function ReliableConditionUpdate(optional KFPlayerController_Story TargetPlayer)
{
local KFPlayerController_Story StoryPC;
local Controller C;
local float CompletionPct;
local string HUDHint;
local Actor NewLocActor;
GetLocation(NewLocActor);
CompletionPct = GetCompletionPct();
if(HUD_Screen.Screen_CountStyle != Hide_Counter)
{
HUDHint = GetDataString();
}
/* We want to update a specific Client's HUD */
if(TargetPlayer != none)
{
TargetPlayer.ReliableConditionUpdate(self,
GetObjOwner(),
CompletionPct,
NewLocActor,
HUDHint,
bComplete);
}
else // update everyone at once
{
For( C=GetObjOwner().Level.ControllerList; C!=None; C=C.NextController )
{
StoryPC = KFPlayerController_Story(C);
if(StoryPC != none)
{
StoryPC.ReliableConditionUpdate(self,
GetObjOwner(),
CompletionPct,
NewLocActor,
HUDHint,
bComplete);
}
}
}
}
/* Replicates relevant info for this condition to clients
VERY expensive - for this reason it is throttled and only
set to replicate dirty values
@Todo - is there a more efficient way to do this ?
*/
function ConditionTickHUDUpdate()
{
local KFPlayerController_Story StoryPC;
local Controller C;
local bool bUpdateHUD;
local Actor NewLocActor;
if((GetObjOwner().AllowConditionRepUpdate() && AllowConditionRepUpdate()) )
{
GetLocation(NewLocActor);
OldComplete = NewComplete;
NewComplete = bComplete || !bActive;
OldDataString = NewDataString;
if(HUD_Screen.Screen_CountStyle != Hide_Counter)
{
NewDataString = GetDataString();
}
OldCompletionPct = NewCompletionPct;
NewCompletionPct = GetCompletionPct();
bUpdateHUD = NewCompletionPct != OldCompletionPct ||
NewComplete != OldComplete ||
NewDataString != OldDataString;
if(bUpdateHUD)
{
LastRepTime = GetObjOwner().Level.TimeSeconds;
GetObjOwner().CurrentRepUpdates ++ ;
For( C=GetObjOwner().Level.ControllerList; C!=None; C=C.NextController )
{
StoryPC = KFPlayerController_Story(C);
if(StoryPC != none)
{
if( bForceReliableUpdate )
{
StoryPC.ReliableConditionUpdate(self,
GetObjOwner(),
NewCompletionPct,
NewLocActor,
NewDataString,
bComplete);
}
else
{
StoryPC.UnreliableConditionUpdate(self,
GetObjOwner(),
NewCompletionPct,
NewLocActor,
NewDataString,
NewComplete);
}
}
}
}
}
}
/* Set the 'bComplete' bool to notify the parent Objective that this Condition is complete */
function ConditionCompleted()
{
local actor SourceActor;
local Controller C;
/* NOTE - any completion event stuff should be done *Before* bComplete is marked true*/
if(Sound_Completion != none)
{
GetLocation(SourceActor);
if(SourceActor != none &&
SourceActor != GetObjOwner())
{
SourceActor.PlaySound(Sound_Completion,,SourceActor.SoundVolume,,SourceActor.SoundRadius,SourceActor.SoundPitch,true);
}
else
{
for (C = GetObjOwner().Level.ControllerList; C != None; C = C.NextController)
{
if(PlayerController(C) != none)
{
PlayerController(C).ClientPlaySound(Sound_Completion,true,2.f,SLOT_Talk);
}
}
// log("Warning - Attempted to play Completion sound for"@name@" with no Source Actor!",'Story_Debug');
}
}
bComplete = true;
log("==============================================================================================", 'Story_Debug');
log("============= "@self@" a condition of "@GetObjOwner().ObjectiveName@" was just marked complete. ", 'Story_Debug');
ReliableConditionUpdate();
}
/* Accessor functions ========================== */
function SetObjOwner(KF_StoryObjective NewOwner)
{
ObjOwner = NewOwner;
}
function KF_StoryObjective GetObjOwner()
{
return ObjOwner;
}
/* Returns a reference to the pawn which instigated this condition last */
function Pawn GetInstigator()
{
return Pawn(GetTargetActor(InstigatorName));
}
function int GetOwnerArrayIndex()
{
return GetObjOwner().FindIndexForCondition(self);
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
return float(bComplete);
}
function string GetDataString()
{
return "" ;
}
/* Hint to display over the world icon */
function string GetWorldHint()
{
return HUD_World.World_Hint ;
}
function string GetHUDHint()
{
return ClampHUDHint(HUD_Screen.Screen_Hint);
}
function string ClampHUDHint(string Hint)
{
if( Len(Hint) > HintCharLimit )
{
Hint = left(Hint,HintCharLimit);
Hint $= "..";
}
return Hint;
}
function vector GetLocation(optional out Actor LocActor)
{
if(ConditionIsActive())
{
if(GetWorldLocActor(LocActor))
{
return LocActor.Location ;
}
LocActor = GetObjOwner();
return GetObjOwner().Location;
}
}
function bool GetWorldLocActor(out Actor LocActor)
{
local Actor HUDWorldActor;
HUDWorldActor = GetTargetActor(WorldLocActorName);
if(ConditionIsActive() &&
HUDWorldActor != none &&
!HUDWorldActor.bPendingDelete &&
!HUDWorldActor.bDeleteMe)
{
LocActor = HUDWorldActor;
return true; ;
}
return false;
}
function vector GetWhispLocation(optional out Actor LocActor)
{
return GetNearestPathNodeTo(GetLocation(LocActor)).Location;
}
function bool ShouldShowWhispTrailFor(PlayerController C)
{
return HUD_World.bShowWhispTrail;
}
function NavigationPoint GetNearestPathNodeTo( vector DesiredLocation)
{
local navigationpoint N,Closest;
local float ClosestDistSq,DistSq;
for ( N=GetObjOwner().Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
{
DistSq = VSizeSquared(N.Location - DesiredLocation) ;
if(Closest == none || DistSq < ClosestDistSq)
{
Closest = N ;
ClosestDistSq = DistSq;
}
}
return Closest;
}
/* TimeFormatting string function Copied from Scoreboard.uc */
function String FormatTime( int Seconds )
{
local int Minutes, Hours;
local String Time;
if( Seconds > 3600 )
{
Hours = Seconds / 3600;
Seconds -= Hours * 3600;
Time = Hours$":";
}
Minutes = Seconds / 60;
Seconds -= Minutes * 60;
if( Minutes >= 10 )
Time = Time $ Minutes $ ":";
else
Time = Time $ "0" $ Minutes $ ":";
if( Seconds >= 10 )
Time = Time $ Seconds;
else
Time = Time $ "0" $ Seconds;
return Time;
}
defaultproperties
{
WorldLocActorName = "HUDWorldActor"
InitialWorldLocActorName = "InitialHUDWorldActor"
InstigatorName = "Instigator"
ConditionRepInterval = 0.5
InitialState = Active
Scale_GameDifficulty=(Scale_Beginner=1,Scale_Hard=1,Scale_Suicidal=1,Scale_HellOnEarth=1)
Scale_PlayerCount=1.f
HUD_World=(World_Clr=(R=255,G=50,B=50,A=255),Whisp_Clr=(R=255,G=50,B=50,A=255),World_Texture=none/*TexOscillator'KFStoryGame_Tex.HUD.ObjArrow_osc'*/,bHide=false,World_Texture_Scale=1.f)
HUD_Screen=(Screen_Clr=(R=255,G=50,B=50,A=255),Screen_ProgressStyle=HDS_Combination,bShowStrikethrough=true,FontScale=Font_Medium,Screen_ProgressBarBG=Texture'KFStoryGame_Tex.HUD.HUd_Rectangel_W_Stroke_Neutral',Screen_ProgressBarFill=Texture 'KFStoryGame_Tex.HUD.Hud_Rectangle_W_Stroke_Fill')
}

View file

@ -0,0 +1,106 @@
/*
--------------------------------------------------------------
KF_Objective_EventListener
--------------------------------------------------------------
Bzsic Proxy actor which receives trigger events for Objective conditions.
As of 10/17/13 we are also using this to store actor references for conditions.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_Objective_EventListener extends Actor;
// Condition which this actor stores actor References for.
var private KF_ObjectiveCondition ConditionOwner;
// A struct which contains a reference to an actor.
// Objects cannot have references to actors which are being garbage collected
// or the game will crash. So we cache them here instead.
struct SConditionActorReference
{
var Actor TargetActor;
var name ActorName;
};
// This array contains a list of all the Actors which are relevant to its ConditionOwner.
var protected array<SConditionActorReference> AssociatedActors;
// This function is called to cache an Actor that is relevant to ConditionOwner.
function AddAssociatedActor(name NewActorName,Actor NewActor )
{
local int Index;
if(!FindAssociatedActor(NewActorName,,Index))
{
AssociatedActors.Length = AssociatedActors.length + 1;
AssociatedActors[AssociatedActors.length - 1].TargetActor = NewActor;
AssociatedActors[AssociatedActors.length - 1].ActorName = NewActorName;
}
else
{
AssociatedActors[Index].TargetActor = NewActor;
}
}
// This function is called to remove an Actor associated with ConditionOwner from the AssociatedActors cache.
function RemoveAssociatedActor(name ActorToRemove)
{
local int Index;
if(FindAssociatedActor(ActorToRemove,,Index))
{
AssociatedActors.Remove(Index,1);
}
}
// Search the AssociatedActors cache by name.
function bool FindAssociatedActor( name TargetActorName, optional out Actor TargetActor , optional out int Index)
{
local int i;
for(i = 0 ; i < AssociatedActors.length ; i ++)
{
if(AssociatedActors[i].ActorName != '' &&
AssociatedActors[i].ActorName == TargetActorName)
{
TargetActor = AssociatedActors[i].TargetActor;
Index = i;
return true;
}
}
return false;
}
function SetConditionOwner(KF_ObjectiveCondition NewOwner)
{
ConditionOwner = NewOwner;
Tag = ConditionOwner.Tag;
}
function KF_ObjectiveCondition GetConditionOwner()
{
return ConditionOwner;
}
function Trigger( actor Other, pawn EventInstigator )
{
if(ConditionOwner != none)
{
ConditionOwner.Trigger(Other,EventInstigator);
}
}
defaultproperties
{
bstatic = false
bHidden = true
bNoDelete = false
RemoteRole = Role_None
}

View file

@ -0,0 +1,29 @@
/* a welder that can be used on human pawns to heal them */
class KF_PawnWelder extends Welder;
#exec OBJ LOAD FILE=KF_Weapons_Trip_T.utx
simulated function Tick(float dt)
{
local KF_BreakerBoxNPC Breaker;
Super.Tick(dt);
if(WeldFire(FireMode[FireModeArray]).LastHitActor != none)
{
Breaker = KF_BreakerBoxNPC(WeldFire(FireMode[FireModeArray]).LastHitActor);
if(Breaker != none)
{
ScreenWeldPercent = (Breaker.Health / Breaker.HealthMax) * 100;
}
}
}
defaultproperties
{
// AmmoRegenRate=20.000000
FireModeClass(0)=Class'PawnWeldFire'
}

View file

@ -0,0 +1,248 @@
/*
--------------------------------------------------------------
KF_StoryInvPickupSpot
--------------------------------------------------------------
When placing Inventory Pickups in Story maps this actor should be used
in place of KF_StoryInventoryPickups.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_PlaceableStoryPickup extends xPickupBase
hidecategories(PickupBase);
/* Icon to render for this Pickup on the holding player's HUD */
var(Pickup_HUD) Material HUDMaterial;
/* Icon to render over top of this Pickup while it's sitting on the ground */
var(Pickup_HUD) Material GroundMaterial;
/* If true, don't perform line traces to determine if we should render the GroundMaterial (setting this true is an optimization) */
var(Pickup_HUD) bool bRenderIconThroughWalls;
/* Modifier to apply to the holding player's groundspeed */
var(Pickup_PawnModifiers) float MovementSpeedModifier;
var(Pickup_Feedback) localized string Message_Dropped; // Human readable description when dropped.
var(Pickup_Feedback) localized string Message_PickedUp;
var(Pickup_Feedback) localized string Message_Use;
var(Pickup_Audio) Sound Sound_Dropped,Sound_PickedUp;
var(Pickup_3P) vector Attachment_Offset;
var(Pickup_3P) name Attachment_Bone;
var(Pickup_3P) rotator Attachment_Rotation;
var(Pickup_1P) bool bRender1PMesh;
/* When holding this item, display it with an 'X-Ray' shader in first person */
var(Pickup_1P) bool bUseFirstPersonXRayEffect;
var(Pickup_1P) rotator ViewRotationOffset;
var(Pickup_1P) vector ViewLocationOffset;
/* Multiplies the height of the player's jumpZ by this amount */
var(Pickup_PawnModifiers) float JumpZModifier;
/* List of weapons which cannot be used when this Item is carrieed */
var(Pickup_Restrictions) array< Class<Weapon> > Weapons_Restricted;
/* List of weapons which can *only* be used when this item is carried */
var(Pickup_Restrictions) array < Class<Weapon> > Weapons_Allowed;
/* Changes the amount of interest ZEDs will show in the player holding this item */
var(Pickup_PawnModifiers) float AIThreatModifier;
/* Number of items of this class which can be held by a pawn at once */
var(Pickup_Restrictions) int MaxHeldCopies;
/* Number of Inventory blocks this item takes up of the holder's weight allowance */
var(Pickup_Restrictions) int InventoryWeight;
/* Determines when the pickup actor should be spawned for this pickupspot */
enum EPickupSpawnMethod
{
Spawn_OnMapLoad,
Spawn_OnMatchBegin,
Spawn_OnTrigger,
};
var(Pickup_Spawning) EPickupSpawnMethod SpawnMethod;
var KF_StoryInventoryPickup MyStoryPickup;
struct SCarriedEvent
{
var() name EventName;
var() float TriggerInterval;
var() int NumRepeats;
var int NumTimesTriggered;
var float LastTriggerTime;
};
var(Events) array<SCarriedEvent> CarriedEvents;
var(Events) name DroppedEvent;
/* UUs per second this pickup should travel at when tossed by a player */
var(Pickup_Tossing) float Pickup_TossVelocity;
/* If true, orient this pickup's toss direction from the players camera instead of his pawn rotation */
var(Pickup_Tossing) bool bDropFromCameraLoc;
/* Type of damage this pickup does when it smacks into something :- ) */
var(Pickup_Tossing) class<DamageType> ImpactDamType;
var(Pickup_Tossing) int ImpactDamage;
simulated event PostBeginPlay()
{
if(SpawnMethod == Spawn_OnMapLoad)
{
Super.PostBeginPlay();
}
}
function MatchStarting()
{
Super.MatchStarting();
if(SpawnMethod == Spawn_OnMatchBegin)
{
SpawnPickup();
}
}
event Trigger( Actor Other, Pawn EventInstigator )
{
Super.Trigger(Other,EventInstigator);
if(SpawnMethod == Spawn_OnTrigger)
{
SpawnPickup();
}
}
simulated function CopyPropertiesTo(KF_StoryInventoryPickup NewPickup)
{
// log("*******************************************************");
// log("Client Copy properties from : "@self@" to - :"@NewPickup);
NewPickup.StoryPickupBase = self;
NewPickup.Event = event;
NewPickup.tag = tag;
NewPickup.MaxHeldCopies = MaxHeldCopies;
NewPickup.SetCollisionSize(CollisionRadius,CollisionHeight);
NewPickup.PrePivot = PrePivot;
NewPickup.PlacedRotation = Rotation;
NewPickup.SetDrawType(DrawType);
NewPickup.SetStaticMesh(StaticMesh);
NewPickup.LinkMesh(Mesh);
NewPickup.SetDrawScale(DrawScale);
NewPickup.SetDrawScale3D(DrawScale3D);
NewPickup.bRenderIconThroughWalls = bRenderIconThroughWalls;
NewPickup.bUseFirstPersonXRayEffect = bUseFirstPersonXRayEffect;
NewPickup.MovementSpeedModifier = MovementSpeedModifier;
NewPickup.AIThreatModifier = AIThreatModifier;
NewPickup.Weight = InventoryWeight;
NewPickup.default.DroppedMessage = Message_Dropped;
NewPickup.default.UseMeMessage = Message_Use;
NewPickup.default.PickupMessage = Message_PickedUp;
NewPickup.CarriedMaterial = HUDMaterial ;
Newpickup.GroundMaterial = GroundMaterial;
NewPickup.PickupSound = Sound_PickedUp;
NewPickup.DroppedSound = Sound_Dropped;
NewPickup.UV2Texture = UV2Texture;
NewPickup.bRender1PMesh = bRender1PMesh;
NewPickup.ViewLocationOffset = ViewLocationOffset;
NewPickup.ViewRotationOffset = ViewRotationOffset;
NewPickup.Pickup_TossVelocity = Pickup_TossVelocity;
NewPickup.bDropFromCameraLoc = bDropFromCameraLoc;
NewPickup.ImpactDamType = ImpactDamType;
NewPickup.ImpactDamage = ImpactDamage;
// Lighting
NewPickup.LightType = LightType;
NewPickup.LightCone = LightCone;
NewPickup.LightBrightness = LightBrightness;
NewPickup.LightRadius = LightRadius;
NewPickup.bUseDynamicLights = bUseDynamicLights;
NewPickup.LightSaturation = LightSaturation;
NewPickup.bDynamicLight = bDynamicLight;
NewPickup.AmbientGlow = AmbientGlow;
NewPickup.LightHue = LightHue;
NewPickup.bLightChanged = true;
}
function SpawnPickup()
{
if( myPickUp != none || PowerUp == None || Level.NetMode == NM_Client )
return;
myPickUp = Spawn(PowerUp,,,Location,Rotation);
if(myPickup != none)
{
myPickUp.PickUpBase = self;
MyStoryPickup = KF_StoryInventoryPickup(myPickup);
if(MyStoryPickup != none)
{
CopyPropertiesTo(MyStoryPickup);
}
}
if (myMarker != None)
{
myMarker.markedItem = myPickUp;
myMarker.ExtraCost = ExtraPathCost;
if (myPickUp != None)
myPickup.MyMarker = MyMarker;
}
else log("No marker for "$self);
}
defaultproperties
{
ImpactDamType = class 'Engine.Crushed'
ImpactDamage = 0
Pickup_TossVelocity = 250
bRender1PMesh = true
bStatic=false // to receive events
bRenderIconThroughWalls = true
bHidden = true
bUseDynamicLights = true
bNetInitialRotation = true
bNoDelete = true
DrawType=DT_StaticMesh
PrePivot=(X=0,Y=0,Z=10)
DrawScale = 1
StaticMesh = StaticMesh 'DetailSM.Crates.WoodBox_B'
CollisionHeight = 10
CollisionRadius = 30
Message_Use = "Press USE key to Pick up"
MovementSpeedModifier = 1.f
JumpZModifier = 1.f
AIThreatModifier = 1.f
PowerUp = class 'KF_StoryInventoryPickup'
Sound_Dropped = Sound'Inf_Player.RagdollImpacts.BodyImpact'
Sound_PickedUp = Sound 'KF_AxeSnd.Axe_Select'
}

View file

@ -0,0 +1,792 @@
/*
--------------------------------------------------------------
KF_StoryCheckPointVolume
--------------------------------------------------------------
Volume used to control spawning of players in story maps.
When active, only those player starts inside the bounds of the volume are considered
valid spawn points. Can also be configured to force the respawn of dead (out of lives) players.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryCheckPointVolume extends PhysicsVolume
hidecategories(PhysicsVolume,Collision,Brush,Advanced,Force,Karma,Lighting,LightColor,Movement,Sound,Volume,Display,VolumeFog) ;
/* prints debug text */
var bool bDebugCheckpoint;
/* if true this checkpoint will begin play active */
var(StoryCheckPoint) bool bStartEnabled;
/* If true, touching (or triggering) this checkpoint will restart all dead players at one of the playerstarts inside the volume's bounds */
var(StoryCheckPoint) bool bRespawnPlayers;
/* If true, this scripted action will respawn bot players as well as humans . Only used if bRespawnPlayers is true*/
var(StoryCheckPoint) bool bIncludeBots;
/* true if CheckPointActivated() was called and this volume is the current spawn-checkpoint for players*/
var bool bIsActive;
/* if true, CheckPointActivated() will only be called when the entire team is inside the Volume */
var(StoryCheckPoint) bool bRequiresWholeTeam;
/* Only relevant if bRespawn players is true. In addition to respawning dead players when Activated this volume will also auto-respawn everyone when their whole team wipes out */
var(StoryCheckPoint) bool bRespawnOnWipe;
/* Event that triggers when a team that was wiped out is given a 'second chance' by this Volume. Only relevant if bRespawnOnWipe is true */
var(Events) array<name> SecondChanceEvents;
/* array of additional events this checkpoint can trigger for use in maps with complex trigger setups */
var(Events) array<name> ActivationEvents;
/* Delay before dead players are actually restarted after the Checkpoint activates */
var(StoryCheckPoint) float RespawnDelay;
/* %of Max Health a player spawns with when this checkpoint brings him back to life. */
var(StoryCheckPoint) float RespawnHealthModifier;
/* if true , 'RespawnHealthModifier' becomes cumulative each time a player respawns at this checkpoint. ie. if a player dies twice with a value of 0.75, his starting health would be 56*/
var(StoryCheckPoint) bool bCumulativeHealthModifier;
/* same as above, but used if the LD wants to reset only specific actors in the map */
var(Team_Restart) array<actor> CheckPointResetActors;
/* for cases where the LD wants to reset all actors in the map of a particular class *Except* some specific actors ..*/
var(Team_Restart) array<actor> ResetExcludeActors;
var name RestartFromObjective;
/* players who wipe out will respawn at this specific checkpoint, if assigned */
var(Team_Restart) string RestartFromCheckPoint;
/* Object reference to the CheckPoint who's name we specified above */
var KF_StoryCheckPointVolume ForcedRestartCheckPoint;
/* Removes all dropped / thrown weapon pickups from play when a team is restarted at this checkpoint */
var(Team_Restart) bool bRemoveDroppedWeapons;
/* true while the Respawn Timer is running but RespawnPlayers() has not yet been called */
var bool bPendingRespawn;
/* true while the team is wiped out completely and is waiting to be restarted from this checkpoint */
var bool bPendingFullRestart;
/* Spawned to control timing of Player respawns. Necessary because volumes are Static & can't run their own timers */
var RespawnTimer RespawnDelayTimer;
/* Cached reference to the player who last activated this Checkpoint */
var Controller ActivatingPlayer;
/* Cached reference to the player who was last respawned by this Checkpoint */
var Controller LastRespawnedPlayer;
/* If true, teleport stragglers to a playerstart inside this volume */
var(Stragglers) bool bTeleportStragglers;
/* Max distance a player can be from this volume before being consider a straggler */
var(Stragglers) float TeleportStragglerDist;
// if bTeleportStragglers, any living players not inside this volume will be teleported to a playerstart in the checkpoitn volume.
var(Stragglers) const Volume TeleportExclusionVolume;
/* If true this checkpoint can only be set as the active checkpoint in the map a single time
Should probably be set true by default as most story maps will probably have a linear progression.
*/
var(StoryCheckPoint) bool bShowActivationMsg;
var(StoryCheckPoint) bool bSingleActivationOnly;
/* FriendlyName */
var(StoryCheckPoint) string CheckPointName;
/* Cached reference to Story gameinfo to cut down on typecasting */
var KFStoryGameInfo StoryGI;
/* used to save the current state of Dialogue at the time a checkpoint is activated so that it can be returned to later if a team restarts */
struct SDialogueState
{
var KF_DialogueSpot DialogueActor;
var int SavedIndex;
var array<byte> DialogueTriggerStates;
};
var array<SDialogueState> SavedDialogue;
enum ECheckPointTriggerType
{
CTT_Touch,
CTT_Trigger,
};
/* do we want this checkpoint to become active when remotely triggered, or on player touch ? */
var (StoryCheckPoint) ECheckPointTriggerType CheckPointTriggerType;
var array<PlayerStart> PSList;
function Reset()
{
LastRespawnedPlayer = none;
ActivatingPlayer = none;
bPendingRespawn = false;
bIsActive = false;
}
simulated function PostBeginPlay()
{
local NavigationPoint N;
local PlayerStart PS;
local KF_DialogueSpot DlgSpot;
if( Level.NetMode==NM_Client )
return;
/* No KFO Gametype, no Initialization */
StoryGI = KFStoryGameInfo(Level.Game);
if(StoryGI == none)
{
return;
}
For( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
{
PS = PlayerStart(N);
if( PS!=None )
{
if(Encompasses(PS) )
{
PSList[PSList.Length] = PS;
}
}
}
if(RestartFromCheckPoint != "" &&
RestartFromCheckPoint != CheckPointName &&
StoryGI != none )
{
ForcedRestartCheckPoint = StoryGI.FindCheckPointNamed(RestartFromCheckPoint);
}
// cache all the DialogueSpots in the map - we'll need this to store the states for each one
foreach DynamicActors(class 'KF_DialogueSpot', DlgSpot)
{
SavedDialogue.length = SavedDialogue.length + 1;
SavedDialogue[SavedDialogue.length-1].DialogueActor = DlgSpot;
SavedDialogue[SavedDialogue.length-1].DialogueTriggerStates.length = DlgSpot.Dialogues.length ;
}
}
/* Wrapper for checking if this volume can grant second chances to teams that wipe out during story gameplay */
function bool CanGrantSecondChances()
{
local bool Result;
if(ForcedRestartCheckPoint != none)
{
Result = true;//ForcedRestartCheckPoint.IsActiveRespawnPoint();
}
else
{
Result = IsActiveRespawnPoint();
}
if(!Result)
{
if(ForcedRestartCheckPoint != none)
{
log("Forced Restart CheckPoint : "@ForcedRestartCheckPoint.CheckPointName@" for - "@CheckPointName@" is unable to Respawn players at the moment. It is probably inactive . ");
}
else
{
log("CheckPoint : "@CheckPointName@"is unable to Respawn players at the moment. It is probably inactive . ");
}
}
return Result ;
}
function bool IsActiveRespawnPoint()
{
return bIsActive && bRespawnPlayers && bRespawnOnWipe /*&& !bPendingRespawn*/;
}
/* Respawns all dead players in the game when they wipe out - Returns true if successful */
function bool GrantASecondChance(out KF_StoryCheckPointVolume RespawnPoint)
{
if(CanGrantSecondChances())
{
RespawnPoint = self;
if(ForcedRestartCheckPoint != none)
{
RespawnPoint = ForcedRestartCheckPoint ;
}
bPendingFullRestart = true;
RespawnPoint.Reset() ;
RespawnPoint.Instigator = Instigator;
return true;
}
return false;
}
function UpdateSpawnAvailability()
{
local NavigationPoint N;
local PlayerStart PS;
local bool bEnableMe;
if( PSList.Length>0 )
{
For( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
{
PS = PlayerStart(N);
if(PS != none)
{
// log("Updating spawn availability for "@self@" - "@PS@".bEnabled = "@bEnableMe);
bEnableMe = bIsActive && PStartBelongsToThisVolume(PS);
PS.bEnabled = bEnableMe ;
}
}
}
else
{
log("Warning - No Playerstarts associated with "@self@": Respawns will fail.");
}
}
function bool PStartBelongsToThisVolume( PlayerStart TestSpot)
{
local int i;
for( i = 0 ; i < PSList.length; i ++)
{
if(PSList[i] == TestSpot)
{
return true;
}
}
return Encompasses(TestSpot);
}
/* CTT_Touch*/
simulated event PawnEnteredVolume(Pawn Other)
{
if(CheckPointTriggerType == CTT_Touch &&
(Other.Controller != none && Other.Controller.bIsPlayer) /*&& Other.IsPlayerPawn()*/) // <- IsPlayerPawn() returns true in Monster.uc ...derp
{
CheckPointActivated(Other,false,bShowActivationMsg);
}
}
/* CTT_Trigger*/
function Trigger( actor Other, pawn EventInstigator )
{
if(CheckPointTriggerType == CTT_Trigger/* && EventInstigator != none &&
EventInstigator.IsPlayerPawn()*/)
{
CheckPointActivated(EventInstigator,false,bShowActivationMsg);
}
}
function ResetPlayerCheckPointStats()
{
local Controller C;
local KFPlayerController_Story PC;
For ( C= Level.ControllerList; C!=None; C=C.NextController )
{
PC = KFPlayerController_Story(C);
if(PC != none)
{
PC.NumCheckPointRespawns = 0 ;
}
}
}
function int GetNumPlayersInVolume()
{
local int Num;
local Controller C;
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
if(C.bIsPlayer && C.Pawn != none)
{
if(Encompasses(C.Pawn))
{
Num ++ ;
}
}
}
// log("NUM PLAYERS IN VOLUME : "@Num);
return Num;
}
/* Tracks the current state of game Objectives & Dialogue at the time this checkpoint was activated */
function SaveStoryState()
{
local int i,idx;
local KF_DialogueSpot Dlg;
local bool bJumpingForward;
local int NextEventIdx;
local name NextSortedObj;
local Controller C;
local KFPlayerController_Story SPC;
if(bPendingFullRestart)
{
return;
}
/* If the LD hasn't provided a forced Objective to restart from, use whatever was current at the time of activation */
if(RestartFromObjective == '' )
{
if(StoryGI.CurrentObjective != none && StoryGI.CurrentObjective.bCheckPointable)
{
RestartFromObjective = StoryGI.CurrentObjective.ObjectiveName;
}
else
{
/* if there's no current objective - the next objective is probably bManualActivate , so we're at a gap between obj's.
In this case we can just use the LastObjective and jump one forward. */
NextSortedObj = StoryGI.SortedObjectives[ Min( StoryGI.CurrentObjectiveIdx + 1 ,StoryGI.SortedObjectives.length - 1 ) ].ObjectiveName ;
log(" No current Objective .. Checkpoint will restart from Next sorted objective : "@NextSortedObj,'Story_Debug');
RestartFromObjective = NextSortedObj;
bJumpingForward = true;
}
for(i = 0 ; i < StoryGI.AllObjectives.length ; i ++)
{
if(StoryGI.AllObjectives[i].ObjectiveName == RestartFromObjective)
{
StoryGI.AllObjectives[i].SetCheckPoint(self);
}
else
{
StoryGI.AllObjectives[i].ClearCheckPoint();
}
}
}
for( i = 0 ; i < SavedDialogue.length ; i ++)
{
Dlg = SavedDialogue[i].DialogueActor ;
if(Dlg != none)
{
NextEventIdx = Dlg.GetNextDlgRequiredEventIdx(Dlg.CurrentMsgIdx) ;
if(bJumpingForward && NextEventIdx > 0)
{
SavedDialogue[i].SavedIndex = NextEventIdx;
}
else
{
SavedDialogue[i].SavedIndex = Dlg.CurrentMsgIdx ;
}
if(Dlg.bDebugDialogue)
{
log("----- DIALOGUE DEBUG ------ Saving Dialogue Index of : "@Dlg@" At : "@SavedDialogue[i].SavedIndex, 'Story_Debug' );
}
for(idx = 0 ; idx < SavedDialogue[i].DialogueTriggerStates.length ; idx ++ )
{
SavedDialogue[i].DialogueTriggerStates[idx] = byte(Dlg.Dialogues[idx].bWasTriggered) ;
}
}
}
// store players current equipment and cash amounts so we can restore it all when they spawn from this checkpoint
for (C = Level.ControllerList; C != None; C = C.NextController)
{
SPC = KFPlayerController_Story(C);
if(SPC != none)
{
SPC.SaveLoadOut();
}
if(KF_StoryNPC(C.Pawn) != none)
{
KF_StoryNPC(C.Pawn).SaveHealthState();
}
}
}
function ModifyPlayer( pawn aPlayer)
{
local float CheckPointHealthModifier;
local KFPlayerController_Story SPC;
SPC = KFPlayerController_Story(aPlayer.Controller);
if(SPC == none)
{
return;
}
if(bRespawnOnWipe)
{
aPlayer.Health = SPC.SavedHealth;
aPlayer.ShieldStrength = SPC.SavedArmor;
SPC.PlayerReplicationInfo.Score = SPC.SavedCash;
}
if (RespawnHealthModifier != 1.f )
{
CheckPointHealthModifier = RespawnHealthModifier ;
if(bCumulativeHealthModifier)
{
CheckpointHealthModifier = CheckPointHealthModifier ** SPC.NumCheckPointRespawns ;
}
aPlayer.Health = Max(aPlayer.Health * CheckPointHealthModifier,1) ;
}
}
function int GetSavedDialogueIndexFor(KF_DialogueSpot DialogueSpot , out array<byte> WasTriggeredArray)
{
local int i;
if(DialogueSpot == none)
{
return -1;
}
for( i = 0 ; i < SavedDialogue.length ; i ++)
{
if(SavedDialogue[i].DialogueActor != none &&
SavedDialogue[i].DialogueActor == DialogueSpot)
{
WasTriggeredArray = SavedDialogue[i].DialogueTriggerStates ;
return SavedDialogue[i].SavedIndex ;
}
}
}
/* Called when this volume has been set as the new active checkpoint zone in the map
Enables encompassed playerstarts & disables all others.
*/
function CheckPointActivated( Pawn CheckPointInstigator, bool bForceActivate, optional bool bShowMessage)
{
local KF_StoryCheckPointVolume OldCheckpoint;
local Controller C;
/* Check for a human controlled pawn */
if(CheckPointInstigator.Controller != none)
{
ActivatingPlayer = CheckPointInstigator.Controller;
if(ActivatingPlayer != none && PlayerController(ActivatingPlayer) != none &&
ActivatingPlayer.Pawn != none )
{
Instigator = ActivatingPlayer.Pawn ;
}
}
if(!bForceActivate &&
bRequiresWholeTeam &&
GetNumPlayersInVolume() < StoryGI.GetTotalActivePlayers())
{
return;
}
if(!bIsActive || bForceActivate || !bSingleActivationOnly)
{
log("===============================================",'Story_Debug');
log("CheckPointActivated! - "@CheckPointName,'Story_Debug');
bIsActive = true;
if(!bPendingFullRestart)
{
TriggerActivationEvents();
if(bRespawnOnWipe)
{
SaveStoryState();
}
}
if(StoryGI != none)
{
OldCheckPoint = StoryGI.CurrentCheckPoint ;
StoryGI.CurrentCheckPoint = self ;
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
if(KFPlayerController_Story(C) != none)
{
KFPlayerController_Story(C).CurrentCheckPoint = self;
}
}
if(bShowMessage)
{
BroadcastLocalizedMessage( StoryGI.default.CheckPointMessageClass , 0, ActivatingPlayer.PlayerReplicationinfo, None, self );
}
}
UpdateSpawnAvailability();
ResetPlayerCheckpointStats();
if( bRespawnPlayers )
{
DelayedRespawnDeadPlayers();
}
if( bTeleportstragglers)
{
TeleportLivingPlayers();
}
if(Instigator == none)
{
log("Warning - No human instigator found when Activating "@self@". Some actors require a human instigator to trigger successfully . (Movers) ");
}
if(bDebugCheckPoint)
{
PrintDebugText(ActivatingPlayer.PlayerReplicationInfo.PlayerName@"activated"@CheckPointName);
}
}
}
/* Fire off a set of events when this checkpoint is activated */
function TriggerActivationEvents()
{
local int i;
TriggerEvent(Event,self, Instigator);
for(i = 0 ; i < ActivationEvents.length ; i ++)
{
TriggerEvent(ActivationEvents[i],self,Instigator);
}
}
function DelayedRespawnDeadPlayers()
{
if(!bPendingRespawn)
{
bPendingRespawn = true;
if ( RespawnDelayTimer == None )
{
RespawnDelayTimer = spawn(class'RespawnTimer', self);
if(RespawnDelayTimer != none)
{
RespawnDelayTimer.TimerFrequency = RespawnDelay ;
}
}
else
{
RespawnDelayTimer.Reset();
}
}
}
/* Called from ReSpawnTimer.Timer() - notification that the timer expired
and it is now time to perform the actual respawn.*/
function RespawnTimerPop()
{
RespawnDeadPlayers();
/* if this is a full-team-restart, we need to reset certain actors in the map
as well - Notify the gameinfo that this would be a good time to do that*/
if(StoryGI != none && StoryGI.bPendingTeamRespawn)
{
StoryGI.bPendingTeamRespawn = false ;
TriggerActivationEvents();
}
}
/* Brings bOutLives players back from the dead. They will spawn at one of the player starts inside this volume's bounds */
function RespawnDeadPlayers()
{
local Controller C;
local KFPlayerController KFPC;
local PlayerController PC;
local KFPlayerReplicationInfo KFPRI;
bPendingRespawn = false;
For ( C= Level.ControllerList; C!=None; C=C.NextController )
{
if ( C.PlayerReplicationInfo != none )
{
/* I'm guessing we are still using Perks in Story mode ? ... will leave this in for now */
KFPC = KFPlayerController(C);
if ( KFPC != none )
{
KFPRI = KFPlayerReplicationInfo(C.PlayerReplicationinfo);
if ( KFPRI != none )
{
KFPC.bChangedVeterancyThisWave = false;
if ( KFPRI.ClientVeteranSkill != KFPC.SelectedVeterancy )
{
KFPC.SendSelectedVeterancyToServer();
}
}
}
if(C.PlayerReplicationInfo.bOnlySpectator ||
!C.PlayerReplicationInfo.bOutOfLives ||
(C.PlayerReplicationInfo.bBot && !bIncludeBots))
{
continue;
}
C.PlayerReplicationInfo.bOutOfLives = false;
C.PlayerReplicationInfo.NumLives = 0;
PC = PlayerController(C);
if( PC != none )
{
PC.GotoState('PlayerWaiting');
PC.SetViewTarget(C);
PC.ClientSetBehindView(false);
PC.bBehindView = False;
// PC.ClientSetViewTarget(C.Pawn); <- that's all well and good but .. At this point 'C' has no pawn ... Gonna move this down after RestartPlayer()
}
if(KFPlayerController_Story(C) != none)
{
KFPLayerController_Story(C).NumCheckPointRespawns ++ ;
}
LastRespawnedPlayer = C;
// Make sure the dude we're respawning hast at least some cash.
C.PlayerReplicationInfo.Score = Max(KFGameType(Level.Game).MinRespawnCash, int(C.PlayerReplicationInfo.Score));
C.ServerReStartPlayer();
if(PC != none && PC.Pawn != none)
{
PC.ClientSetViewTarget(PC.Pawn);
}
}
}
LastRespawnedPlayer = none;
/* Full Checkpoint restart (everyone was wiped out) */
if(bPendingFullRestart && StoryGI != none)
{
bPendingFullRestart = false;
StoryGI.NotifyTeamRestarted();
}
}
/* Teleports all living players in the map (outside of the volume) to a playerstart in this volume. */
function TeleportLivingPlayers()
{
local NavigationPoint TeleportSpot;
local Controller C;
local bool bTeleportMe;
For ( C= Level.ControllerList; C!=None; C=C.NextController )
{
bTeleportMe = false;
if ( C.PlayerReplicationInfo != none )
{
if(C.bIsPlayer &&
!C.PlayerReplicationInfo.bOutOfLives &&
!C.PlayerReplicationInfo.bOnlySpectator &&
C.Pawn != none && C.Pawn.Health > 0 &&
C.Pawn.PhysicsVolume != self )
{
if(TeleportExclusionVolume != none)
{
bTeleportMe = !TeleportExclusionVolume.Encompasses(C.Pawn);
}
else
{
bTeleportMe = VSize(C.Pawn.Location - Location) >= TeleportStragglerDist;
}
if(bTeleportMe)
{
TeleportSpot = Level.Game.FindPlayerStart(C,C.GetTeamNum());
if(TeleportSpot != none)
{
C.Pawn.SetLocation(TeleportSpot.Location);
}
}
}
}
}
}
function PrintDebugText( string Message)
{
local Controller C;
local PlayerController P;
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
P = PlayerController(C);
if( P != None )
{
P.TeamMessage(C.PlayerReplicationInfo, Message, 'CriticalEvent');
}
}
}
defaultproperties
{
CheckPointTriggerType = CTT_Touch ;
bRespawnPlayers = true
bRespawnOnWipe = true
bIncludeBots = true
bDebugCheckPoint = false
RespawnDelay = 2.f
RespawnHealthModifier = 1.0
bCumulativeHealthModifier = true
bSingleActivationOnly = true
CheckPointName = "a checkpoint"
TeleportStragglerDist = 2000
bTeleportStragglers = false
bRemoveDroppedWeapons = true
bShowActivationMsg = true
bStatic = false // needs to be non-static or it won't receive trigger events
}

View file

@ -0,0 +1,58 @@
class KF_StoryElevator extends Mover;
var array<KF_StoryElevator_Door> Doors;
// Elevator stopped. Detach the doors */
function FinishNotify()
{
Super.FinishNotify();
DetachDoors();
}
function NotifyDoorsClosed()
{
if(!bInterpolating)
{
AttachDoors();
Trigger(self,none);
}
}
function DetachDoors()
{
local int i;
for(i = 0 ; i < Doors.length ; i ++)
{
Doors[i].DetachFromElevator();
Doors[i].Trigger(self,none);
}
}
function AttachDoors()
{
local int i;
for(i = 0 ; i < Doors.length ; i ++)
{
Doors[i].AttachToElevator();
}
}
state() TriggerToggle
{
function Reset()
{
super.Reset();
// Reset instantly
SetResetStatus( true );
GotoState( 'TriggerToggle', 'Close' );
}
}
defaultproperties
{
MoverEncroachType=ME_CrushWhenEncroach
InitialState="TriggerToggle"
}

View file

@ -0,0 +1,101 @@
class KF_StoryElevator_Door extends Mover;
var KF_StoryElevator MyElevator;
var() name ElevatorTag;
var vector InitialClosedLoc,InitialBasePos;
simulated function PostBeginPlay()
{
Super.PostBeginPlay();
foreach DynamicActors(class 'KF_StoryElevator', MyElevator, ElevatorTag)
{
break;
}
InitialBasePos = BasePos;
if(MyElevator != none)
{
MyElevator.Doors[MyElevator.Doors.length] = self;
}
}
// when the doors close, Base on and then move the elevator */
function FinishedClosing()
{
Super.FinishedClosing();
if(MyElevator != none)
{
InitialClosedLoc = Location;
MyElevator.NotifyDoorsClosed();
}
}
// Toggle when triggered.
state() TriggerToggle
{
event Trigger( Actor Other, Pawn EventInstigator )
{
/* elevator is in motion. would be a bad time to open the doors. */
if(MyElevator != none && MyElevator.bInterpolating)
{
return;
}
Super.Trigger(Other,EventInstigator);
}
function Reset()
{
super.Reset();
DetachFromElevator(true);
// Reset instantly
SetResetStatus( true );
GotoState( 'TriggerToggle', 'Close' );
}
}
function AttachToElevator()
{
SetPhysics(PHYS_None);
bHardAttach = true;
SetBase(MyElevator);
}
function DetachFromElevator(optional bool bReset)
{
if(bReset)
{
ResetKeyPositions();
}
else
{
UpdateKeyPositions();
}
SetPhysics(default.Physics);
SetBase(none);
bHardAttach = false;
}
function UpdateKeyPositions()
{
BasePos.Z = Location.Z ;
}
function ResetKeyPositions()
{
BasePos = InitialBasePos;
}
defaultproperties
{
MoverEncroachType=ME_IgnoreWhenEncroach
InitialState="TriggerToggle"
}

View file

@ -0,0 +1,84 @@
/*
--------------------------------------------------------------
KF_StoryGRI
--------------------------------------------------------------
Custom GamereplicationInfo class for use in Story mode
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryGRI extends KFGameReplicationinfo;
var private KF_StoryObjective CurrentObjective;
var private Material VictorySplashMaterial;
var private Material DefeatSplashMaterial;
var private KF_HUDStyleManager HUDStyleManager;
var private KF_StoryObjective DebugTargetObj;
replication
{
reliable if(Role == ROLE_Authority && bNetDirty)
CurrentObjective,DebugTargetObj;
reliable if( Role == ROLE_Authority && bNetInitial)
HUDStyleManager,VictorySplashMaterial,DefeatSplashMaterial;
}
/* accessor for retrieving the current Objective set in the gameinfo */
simulated function KF_StoryObjective GetCurrentObjective()
{
return CurrentObjective;
}
simulated function KF_StoryObjective GetDebugTargetObjective()
{
return DebugTargetObj;
}
simulated function KF_HUDStyleManager GetHUDStyleManager()
{
return HUDStyleManager;
}
simulated function Material GetVictorySplashMaterial()
{
return VictorySplashMaterial;
}
simulated function Material GetDefeatSplashMaterial()
{
return DefeatSplashMaterial;
}
function SetHUDStyleManager(KF_HUDStyleManager NewManager)
{
HUDStyleManager = NewManager;
}
function SetDefeatMaterial(Material NewMat)
{
DefeatSplashMaterial = NewMat;
}
function SetVictoryMaterial( Material NewMat)
{
VictorySplashMaterial = NewMat;
}
function SetCurrentObjective(KF_StoryObjective NewObjective)
{
CurrentObjective = NewObjective;
}
function SetDebugTargetObj(KF_StoryObjective NewDebugTarget)
{
DebugTargetObj = NewDebugTarget;
}

View file

@ -0,0 +1,470 @@
/*
--------------------------------------------------------------
KF_StoryInventoryItem
--------------------------------------------------------------
Base class for Objective-driven inventory items which players can
hold on their pawns.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryInventoryItem extends Inventory
dependson(KF_PlaceableStoryPickup);
/* Number of items of this class which can be held by a pawn at once */
var int MaxHeldCopies;
var Material CarriedMaterial;
/* Icon to render over top of this Pickup while it's sitting on the ground */
var Material GroundMaterial;
var float MovementSpeedModifier;
var class<DamageType> ImpactDamType;
var int ImpactDamage;
var rotator ViewRotationOffset;
var vector ViewLocationOffset;
/* If we want the pawn to go a specific speed when carrying this item (ignoring all other modifiers) , here's where we do it */
var float ForcedGroundSpeed;
var bool bUseForcedGroundSpeed;
var float Pickup_TossVelocity;
var bool bDropFromCameraLoc;
var bool bRender1PMesh;
var float JumpZModifier;
var StaticMesh PickupSM;
var KF_PlaceableStoryPickup StoryPickupBase;
var name DroppedEvent;
var int Weight;
/* Changes the amount of interest ZEDs will show in the player holding this item */
var float AIThreatModifier;
var name InvAttachmentBone;
/* When holding this item, display it with an 'X-Ray' shader in first person */
var bool bUseFirstPersonXRayEffect;
/* List of weapons which cannot be used when this Item is carrieed */
var array< Class<Weapon> > AllowedWeapons;
/* List of weapons which can *only* be used when this item is carried */
var array < Class<Weapon> > RestrictedWeapons;
var array<KF_PlaceableStoryPickup.SCarriedEvent> CarriedEvents;
/* Disable the bCollideActors property of pickups spawned by this Item when they are created */
var bool bDisableCollisionOnDrop;
replication
{
reliable if( Role==ROLE_Authority)
MovementSpeedModifier,CarriedMaterial,ClientGiveTo,AllowHoldWeapon,StoryPickupBase,bRender1PMesh,bUseFirstPersonXRayEffect;
}
function GiveTo( pawn Other, optional Pickup Pickup )
{
Super.GiveTo(Other,Pickup);
AttachToPawn(Instigator);
ClientGiveTo(Other,Pickup);
UpdateHeldMaterial(Other,CarriedMaterial);
if ( KFHumanPawn_Story( Other ) != none)
{
KFHumanPawn_Story( Other ).SetHasStoryItem( true );
}
}
/* Updates the Material which floats over the pawns head to this icon' Mat */
function UpdateHeldMaterial(Pawn Holder, Material NewMat)
{
local KF_StoryPRI PRI;
PRI = KF_StoryPRI(Holder.PlayerReplicationInfo);
if(PRI != none)
{
PRI.SetFloatingIconMat(NewMat);
PRI.NetupdateTime = Level.TimeSeconds - 1;
}
}
function Tick(float DeltaTime)
{
TriggerHeldEvents();
}
function TriggerHeldEvents()
{
local int i;
if(Instigator != none )
{
for(i = 0 ; i < CarriedEvents.length ; i ++)
{
if((CarriedEvents[i].NumRepeats == 0 ||
CarriedEvents[i].NumTimesTriggered < CarriedEvents[i].NumRepeats) &&
Level.TimeSeconds - CarriedEvents[i].LastTriggerTime >= CarriedEvents[i].TriggerInterval)
{
CarriedEvents[i].NumTimesTriggered ++ ;
CarriedEvents[i].LastTriggerTime = Level.TimeSeconds;
TriggerEvent(CarriedEvents[i].EventName,self,Instigator);
}
}
}
}
simulated function ClientGiveTo(pawn Other,Pickup OwningPickup)
{
Instigator = Other;
if(KFHumanPawn_Story(Other) != none &&
Other.Weapon != none &&
!AllowHoldWeapon(Other.Weapon))
{
Other.PendingWeapon = KFHumanPawn_Story(Other).FindUseableWeaponFor(self);
if(Other.PendingWeapon != none)
{
Other.PendingWeapon.ClientWeaponSet(true);
}
}
}
/* Render first person Model for Inventory item */
simulated event RenderOverlays( canvas Canvas)
{
local int i;
local int NumSkins;
local vector ViewLoc, Locoffset;
local Rotator ViewRot,RotOffset,PawnRot;
/* Orient the model to the holder's camera and draw it */
if(bRender1PMesh &&
Instigator != none && PlayerController(Instigator.Controller) != none &&
!PlayerController(Instigator.Controller).bBehindView)
{
/* X-Ray vision effect */
NumSkins = 3;
Skins.length = Max(Skins.length,NumSkins);
for(i = 0 ; i < Skins.length ; i ++)
{
if(bUseFirstPersonXRayEffect)
{
Skins[i] = Shader 'KFStoryGame_Tex.Shaders.SeethruPickup_shdr' ;
}
}
if(StoryPickupBase != none)
{
LocOffset = StoryPickupBase.ViewLocationOffset;
RotOffset = StoryPickupbase.ViewRotationOffset;
}
PawnRot = Instigator.Rotation;
ViewRot = Instigator.GetViewRotation() ;
ViewLoc = (Instigator.Location + PlayerController(Instigator.Controller).CalcViewLocation) /2 ;
SetLocation(ViewLoc + (vector(ViewRot) * (CollisionRadius + Instigator.CollisionRadius)));
SetRotation(ViewRot);
SetRelativeLocation(Location + LocOffset);
SetRelativeRotation(ViewRot + RotOffset);
Canvas.DrawActor(None, false, true); // clear Z Buffer.
bDrawingFirstPerson = true;
Canvas.DrawActor(self, false, false, 90.f);
bDrawingFirstPerson = false;
}
}
function AttachToPawn(Pawn P)
{
local name BoneName;
local vector LocOffset;
local rotator RotOffset;
/* NO attachment for this item, early out */
if(AttachmentClass == none)
{
return;
}
/* If attaching to a bone on the mesh, make sure to use relative values */
if(StoryPickupBase != none )
{
LocOffset = StoryPickupBase.Attachment_Offset;
RotOffset = StoryPickupBase.Attachment_Rotation;
}
Instigator = P;
if ( ThirdPersonActor == None )
{
ThirdPersonActor = Spawn(AttachmentClass,Owner);
if(ThirdPersonActor == none)
{
return;
}
InventoryAttachment(ThirdPersonActor).InitFor(self);
}
else
ThirdPersonActor.NetUpdateTime = Level.TimeSeconds - 1;
BoneName = InvAttachmentBone;
if ( BoneName == '' )
{
// no attachment bone. Dont render in third person.
ThirdPersonActor.SetBase(P);
ThirdPersonActor.bHidden = true;
}
else
{
P.AttachToBone(ThirdPersonActor,BoneName);
ThirdPersonActor.SetRelativeLocation(LocOffset);
ThirdPersonActor.SetRelativeRotation(RotOffset);
}
}
simulated function CopyPropertiesFrom(KF_StoryInventoryPickup OwningPickup)
{
StoryPickupBase = OwningPickup.StoryPickupBase;
MaxHeldCopies = OwningPickup.MaxHeldCopies;
MovementSpeedModifier = OwningPickup.MovementSpeedModifier;
AIThreatModifier = OwningPickup.AIThreatModifier;
PickupSM = OwningPickup.StaticMesh;
Weight = OwningPickup.Weight;
CarriedMaterial = OwningPickup.CarriedMaterial ;
GroundMaterial = OwningPickup.GroundMaterial;
PrePivot = OwningPickup.PrePivot;
AmbientGlow = OwningPickup.AmbientGlow;
UV2Texture = OwningPickup.UV2Texture;
bUseFirstPersonXRayEffect = OwningPickup.bUseFirstPersonXRayEffect;
bRender1PMesh = OwningPickup.bRender1PMesh;
ViewLocationOffset = OwningPickup.ViewLocationOffset;
ViewRotationOffset = OwningPickup.ViewRotationOffset;
Pickup_TossVelocity = OwningPickup.Pickup_TossVelocity;
bDropFromCameraLoc = OwningPickup.bDropFromCameraLoc;
ImpactDamType = OwningPickup.ImpactDamType;
ImpactDamage = OwningPickup.ImpactDamage;
SetDrawScale(OwningPickup.DrawScale);
SetStaticMesh(OwningPickup.StaticMesh);
LinkMesh(OwningPickup.Mesh);
SetDrawType(OwningPickup.DrawType);
SetDrawScale3D(OwningPickup.DrawScale3D);
SetRotation(OwningPickup.Rotation);
SetCollisionSize(OwningPickup.CollisionRadius,OwningPickup.CollisionHeight);
if(StoryPickupBase != none)
{
JumpZModifier = StoryPickupBase.JumpZModifier;
DroppedEvent = StoryPickupBase.DroppedEvent;
CarriedEvents = StoryPickupBase.CarriedEvents;
InvAttachmentBone = StoryPickupBase.Attachment_Bone;
}
log("=================================================");
log("Copy properties from : "@OwningPickup@" to - "@self);
log("=================================================");
}
simulated function ClientItemThrown()
{
AmbientSound = None;
Instigator.DeleteInventory(self);
if(Instigator.Weapon.IsA('Dummy_JoggingWeapon'))
{
Instigator.Controller.SwitchToBestWeapon();
}
}
function DropFrom(vector StartLocation)
{
local Pickup P;
local Rotator VRot;
if( StoryPickupBase != none )
{
VRot = StoryPickupBase.Rotation;
}
P = spawn(PickupClass,,,StartLocation,VRot);
if( P == none && Instigator != none )
{
// couldn't spawn using StartLocation, try Instigator's location and just drop it
P = spawn(PickupClass,,,Instigator.Location,VRot);
Velocity = vect(0,0,0);
}
if ( P == None )
{
if( Instigator != none && Instigator.Health > 0 )
{
// couldn't spawn using Instigator's location, just hold on to it (do nothing)
return;
}
// Instigator can't hold on to it because he's dead, so freak out
warn(self$" couldn't be dropped by instigator "$Instigator);
destroy();
return;
}
ClientItemThrown();
if ( Instigator != None )
{
if ( KFHumanPawn_Story( Instigator ) != none)
{
KFHumanPawn_Story( Instigator ).SetHasStoryItem( false );
}
DetachFromPawn(Instigator);
Instigator.DeleteInventory(self);
}
UpdateHeldMaterial(Instigator,none);
SetDefaultDisplayProperties();
StopAnimating();
GotoState('');
if(StoryPickupBase != none && KF_StoryInventoryPickup(P) != none)
{
StoryPickupBase.CopyPropertiesTo(KF_StoryInventoryPickup(P));
}
else
{
if(KF_StoryInventoryPickup(P) != none)
{
KF_StoryInventoryPickup(P).MovementSpeedModifier = MovementSpeedModifier;
}
P.Tag = tag; // make sure we copy the tag over to the new pickup .
}
P.InitDroppedPickupFor(self);
P.Velocity = Velocity;
Velocity = vect(0,0,0);
Instigator = None;
}
simulated function float GetMovementModifierFor(Pawn InPawn)
{
return MovementSpeedmodifier;
}
simulated function bool AllowHoldWeapon(Weapon InWeapon, optional bool SkipDummyWeap)
{
local int i;
local bool Result;
local array< class<Weapon> > AllowedWeaps,RestrictedWeaps;
if(InWeapon == none)
{
return false;
}
/* Hackity hack!*/
if(!SkipDummyWeap &&
InWeapon.IsA('Dummy_JoggingWeapon'))
{
return true;
}
if(StoryPickupBase == none)
{
AllowedWeaps = AllowedWeapons;
RestrictedWeaps = RestrictedWeapons;
}
else
{
AllowedWeaps = StoryPickupBase.Weapons_Allowed;
RestrictedWeaps = StoryPickupBase.Weapons_Restricted;
}
if(AllowedWeaps.length == 0)
{
Result = true;
}
else
{
for(i = 0 ; i < AllowedWeaps.length ; i ++)
{
if(ClassisChildOf(InWeapon.class,AllowedWeaps[i]))
{
Result = true;
break;
}
}
}
for(i = 0 ; i < RestrictedWeaps.length ; i ++)
{
if(ClassisChildOf(InWeapon.class,RestrictedWeaps[i]))
{
Result = false;
break;
}
}
// log("does"@self@" allow the use of :"@InWeapon@" ? :"@Result);
return Result;
}
simulated function bool IsThrowable()
{
return true;
}
defaultproperties
{
ImpactDamType = class 'Engine.Crushed'
ImpactDamage = 0
Pickup_TossVelocity = 250
BobDamping=5.000000
PlayerViewOffset = (X=25,Y=0,Z=-20)
JumpZModifier = 1.f
AIThreatModifier = 1.f
MovementSpeedModifier = 1.f
PickupClass = class 'KF_StoryInventoryPickup'
AttachmentClass = class 'StoryInventoryAttachment'
InvAttachmentBone = "CHR_LArmForeArm"
}

View file

@ -0,0 +1,394 @@
/*
--------------------------------------------------------------
KF_StoryInventoryPickup
--------------------------------------------------------------
Base class for Objective-driven inventory items which players can
hold on their pawns.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryInventoryPickup extends Pickup
notplaceable;
/* Number of items of this class which can be held by a pawn at once */
var int MaxHeldCopies;
var Material CarriedMaterial;
var Material GroundMaterial;
var float MovementSpeedModifier;
var float Pickup_TossVelocity;
var class<DamageType> ImpactDamType;
var int ImpactDamage;
var bool bDropFromCameraLoc;
var string DroppedMessage; // Human readable description when dropped.
var string UseMeMessage;
var bool bRender1PMesh;
var Rotator PlacedRotation;
var float LastUseMeMsgTime;
var bool bRenderIconThroughWalls;
var rotator ViewRotationOffset;
var vector ViewLocationOffset;
/* When holding this item, display it with an 'X-Ray' shader in first person */
var bool bUseFirstPersonXRayEffect;
/* Sound this pickup makes when it falls to the floor */
var Sound DroppedSound;
/* Number of inventory blocks this item takes up when carried */
var int Weight;
/* Changes the amount of interest ZEDs will show in the player holding this item */
var float AIThreatModifier;
var KF_PlaceableStoryPickup StoryPickupBase;
replication
{
reliable if (Role== Role_Authority)
GroundMaterial,bRenderIconThroughWalls;
}
static function string GetLocalString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2
)
{
if(Switch == 1)
{
return default.DroppedMessage;
}
else
{
return default.PickupMessage;
}
}
state FallingPickup
{
// Story Inventory pickups can't be grabbed by walking over them.
function Touch( actor Other )
{
if(ImpactDamage > 0 && Other != Instigator)
{
Other.TakeDamage(ImpactDamage,Instigator,Other.Location,Velocity,ImpactDamType);
}
}
event Landed( vector HitNormal )
{
Global.Landed(HitNormal);
if(DroppedSound != none)
{
PlaySound(DroppedSound,,2.f,false,SoundRadius,SoundPitch,true);
}
}
}
auto state Pickup
{
function Timer()
{
}
// Story Inventory pickups can't be grabbed by walking over them.
function Touch( actor Other )
{
if(!ValidTouch(Other))
{
return;
}
if(Pawn(Other) != none && PlayerController(Pawn(Other).Controller) != none && Level.TimeSeconds - LastUseMeMsgTime > 4.f)
{
LastUseMeMsgTime = Level.TimeSeconds;
PlayerController(Pawn(Other).Controller).ClientMessage(UseMeMessage, 'KFCriticalEvent');
}
/* Have KFBots auto pickup stuff for testing purposes*/
if(Pawn(Other) != none && KFInvasionBot(Pawn(Other).Controller) != none)
{
UsedBy(Pawn(Other));
}
}
/* ValidTouch()
Validate touch (if valid return true to let other pick me up and trigger event).
*/
function bool ValidTouch( actor Other )
{
// make sure its a live player
if ( (Pawn(Other) == None) || !Pawn(Other).bCanPickupInventory || (Pawn(Other).DrivenVehicle == None && Pawn(Other).Controller == None) )
return false;
/* Too much weight .. ? */
if (KFHumanPawn(Other) != none && !KFHumanPawn(Other).CanCarry(Weight))
{
PlayerController(Pawn(Other).Controller).ReceiveLocalizedMessage(Class'KFMainMessages', 4);
return false;
}
/* Too many held copies ? */
if(IsHoldingTooManyCopies(Pawn(Other)))
{
return false;
}
// make sure not touching through wall
if ( !FastTrace(Other.Location, Location) )
return false;
return true;
}
event UsedBy( Pawn user)
{
local Inventory Copy;
local KF_StoryInventoryPickup P,Closest;
local float Dist,ClosestDist;
/* Only grab one inventory pickup at a time */
foreach User.TouchingActors(class 'KF_StoryInventoryPickup', P)
{
Dist = VSizeSquared(P.Location - Location);
if(ClosestDist == 0 || Dist < ClosestDist)
{
ClosestDist = Dist;
Closest = P;
}
}
// If used by a player pawn, let him pick this up.
if( Closest == self && ValidTouch(user) )
{
Copy = SpawnCopy(user);
AnnouncePickup(user);
SetRespawn();
if ( Copy != None )
Copy.PickupFunction(user);
TriggerEvent(Event, self, user);
}
}
}
function AnnouncePickup( Pawn Receiver )
{
BroadCastPickupEvent(Receiver,1);
PlaySound( PickupSound,SLOT_Interact );
}
function AnnounceDropped( Pawn Dropper)
{
BroadCastPickupEvent(Dropper,2);
}
function BroadCastPickupEvent( Pawn Receiver , int Switch)
{
local Controller C;
local PlayerController PC;
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
PC = PlayerController(C);
if(PC != none)
{
PC.ReceiveLocalizedMessage(MessageClass,Switch,Receiver.PlayerReplicationInfo);
}
}
}
simulated function bool IsHoldingTooManyCopies(pawn InPawn)
{
local Inventory Inv;
local int NumHeld;
if(MaxHeldCopies <= 0)
{
return false;
}
for( inv=InPawn.Inventory; inv!=None; inv=inv.Inventory )
{
if(Inv.class == InventoryType)
{
NumHeld ++;
}
}
return (NumHeld + 1) > MaxHeldCopies;
}
/* Draw floating icons overtop of pickups, on request */
simulated event RenderOverlays( canvas Canvas )
{
local float Opacity;
local float IconSize;
local float XCentre,YCentre;
local vector ScreenPos;
local Material RenderMat;
local vector CameraLocation;
local rotator CameraRotation;
local float Dist;
Super.RenderOverlays(Canvas);
if(GroundMaterial == none)
{
return;
}
Canvas.GetCameraLocation(CameraLocation, CameraRotation);
/* fading jazz from PlayerBeacon code */
Dist = vsize(CameraLocation-Location);
Dist -= class 'HUDKillingFloor'.default.HealthBarFullVisDist;
Dist = FClamp(Dist, 0, class 'HUDKillingFloor'.default.HealthBarCutoffDist-class 'HUDKillingFloor'.default.HealthBarFullVisDist);
Dist = Dist / (class 'HUDKillingFloor'.default.HealthBarCutoffDist- class 'HUDKillingFloor'.default.HealthBarFullVisDist);
Opacity = Max(byte((1.f - Dist) * 255.f),100.f);
if(!bRenderIconThroughWalls && !FastTrace(Location,CameraLocation))
{
return;
}
RenderMat = GroundMaterial ;
IconSize = GroundMaterial.MaterialUSize();
ScreenPos = Canvas.WorldToScreen(Location + CollisionHeight * Vect(0,0,1));
XCentre = ScreenPos.X;
YCentre = ScreenPos.Y;
/* Dont render stuff behind the camera */
if ( (Normal(Location - CameraLocation) dot vector(CameraRotation)) < 0 )
{
return;
}
Canvas.DrawColor.R = 255;
Canvas.DrawColor.G = 255;
Canvas.DrawColor.B = 255;
Canvas.DrawColor.A = Opacity;
Canvas.SetPos(XCentre - (0.5 * IconSize) + 1.0, YCentre - (0.5 * IconSize) + 1.0);
Canvas.DrawTileScaled(RenderMat, IconSize/ RenderMat.MaterialVSize() ,IconSize/ RenderMat.MaterialVSize() );
}
function InitDroppedPickupFor(Inventory Inv)
{
SetPhysics(PHYS_Falling);
GotoState('FallingPickup');
Inventory = Inv;
bAlwaysRelevant = false;
bOnlyReplicateHidden = false;
bUpdateSimulatedPosition = true;
bDropped = true;
bIgnoreEncroachers = false; // handles case of dropping stuff on lifts etc
NetUpdateFrequency = 8;
// Inv.Instigator.HandlePickup(self);
Instigator = Inv.Instigator;
BroadCastPickupEvent(Inv.Instigator,2);
}
/* Create an Inventory Item for this Pickup */
function inventory SpawnCopy( pawn Other )
{
local Inventory Copy;
local KF_StoryInventoryItem StoryInv;
if ( Inventory != None )
{
Copy = Inventory;
Inventory = None;
}
else
{
Copy = spawn(InventoryType,Other,,,rot(0,0,0));
}
Copy.Tag = tag;
StoryInv = KF_StoryInventoryItem(Copy);
if(StoryInv != none)
{
StoryInv.CopyPropertiesFrom(self);
}
Copy.GiveTo( Other, self );
return Copy;
}
defaultproperties
{
ImpactDamType = class 'Engine.Crushed'
ImpactDamage = 0
Pickup_TossVelocity = 250
bRenderIconThroughWalls = true
bRender1PMesh = true
bOrientOnSlope=false
bCollideActors = true
bIgnoreEncroachers = false
MovementSpeedModifier = 1.f
AIThreatModifier = 1.f
DrawType = DT_StaticMesh
Physics = PHYS_Falling
StaticMesh = StaticMesh 'DetailSM.Crates.WoodBox_B'
PrePivot=(X=0,Y=0,Z=10)
DrawScale = 1
CollisionHeight = 10
CollisionRadius = 30
InventoryType = class 'KF_StoryInventoryItem'
UseMeMessage = "Press USE key to Pick up"
MessageClass=class'PickupMessagePlus'
DroppedMessage = "You dropped the gold bars."
bOnlyReplicateHidden = false
bNetinitialRotation =true
bFixedRotationDir = false
NetUpdateFrequency = 8
DroppedSound = Sound'Inf_Player.RagdollImpacts.BodyImpact'
PickupSound = Sound 'KF_AxeSnd.Axe_Select'
}

View file

@ -0,0 +1,564 @@
/*
--------------------------------------------------------------
LD Placeable NPC actor. Can be controlled by AI Scripts
(for non-combat behaviour). Or with the KF_StoryNPC_AI controller
for simple combat AI.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryNPC extends KFHumanPawn
placeable
hidecategories(DeRes,Bob,Blur,UDamage);
var () localized array<string> NPCDialogue;
var () localized string NPCName;
var KF_DialogueSpot DialogueTrigger;
var () bool bRunning;
var name RunningAnims[8];
var () float BaseGroundSpeed;
var () float RespawnTime;
var () bool bStartActive;
var () bool bIndestructible;
var (AI) int TeamIndex;
var (AI) bool bFireAtWill;
var (AI) float BaseAIThreatRating;
var (AI) array< class <Pawn> > OnlyThreateningTo,NotThreateningTo;
var (AI) bool bNoThreatToZEDs;
var bool bShotAnim;
var () bool bHasInfiniteAmmo;
var bool bInitialFireAtWill;
var () bool bDropInventoryOnDeath;
/* If true, this pawn is marked Hidden while inactive. */
var (Display) bool bOnlyVisibleWhenActive;
/* Initial location this NPC was placed at by the L.D */
var vector PlacedLoc;
var rotator PlacedRot;
/* Modifier to apply to incoming damage from friendly pawns */
var() float FriendlyFireDamageScale;
var() bool bShowHealthBar;
var(Movement) bool bUseDefaultPhysics;
var() float NPCHealth;
var() float StartingHealthPct;
var() bool bDamageable;
var bool bActive;
var bool bInitialActive;
/* Event to fire off when this NPC becomes 'Active' */
var(Events) name ActivationEvent,DeActivationEvent,HealedEvent;
var(Pawn) bool bUseHitPoints;
enum ENPCTriggerAction
{
TA_ToggleHoldFire,
TA_CommitSuicide,
TA_ToggleActive,
};
var () ENPCTriggerAction TriggerAction;
replication
{
reliable if(Role == Role_Authority)
bActive;
}
/* Stub. The objective just changed. Called from KFStoryGameinfo.SetActiveObjective() */
function OnObjectiveChanged(name OldObjectiveName, name NewObjectiveName){}
function SetMovemetPhysics()
{
if(!bUseDefaultPhysics)
{
Super.SetMovementPhysics();
}
}
function bool HealDamage(int Amount, Controller Healer, class<DamageType> DamageType)
{
Health = Min(Health + Amount, HealthMax);
return true;
}
function ProcessLocationalDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, array<int> PointsHit )
{
if(!bUseHitPoints)
{
TakeDamage(Damage,instigatedBy,HitLocation,Momentum,damageType);
}
else
{
Super.ProcessLocationalDamage(Damage,InstigatedBy,HitLocation,Momentum,DamageType,PointsHit);
}
}
function SaveHealthState(){}
function Reset()
{
bFireAtWill = bInitialFireAtWill ;
SetActive(bInitialActive);
ResurrectNPC();
}
simulated function PostBeginPlay()
{
local AIScript A;
Super.PostbeginPlay();
SpawnDialogueTrigger();
bInitialFireAtWill = bFireAtWill;
if(bStartActive)
{
SetActive(true);
}
if(bOnlyVisibleWhenActive)
{
UpdateVisibility(bActive);
}
bInitialActive = bActive;
PlacedLoc = Location;
PlacedRot = Rotation;
GroundSpeed = BaseGroundSpeed;
MenuName = NPCName;
Health = NPCHealth * StartingHealthPct;
HealthMax = NPCHealth;
// automatically add controller to pawns which were placed in level
// NOTE: pawns spawned during gameplay are not automatically possessed by a controller
if ( (Health > 0) && !bDontPossess )
{
// check if I have an AI Script
if ( AIScriptTag != '' )
{
ForEach AllActors(class'AIScript',A,AIScriptTag)
break;
// let the AIScript spawn and init my controller
if ( A != None )
{
A.SpawnControllerFor(self);
if ( Controller != None )
return;
}
}
if ( (ControllerClass != None) && (Controller == None) )
Controller = spawn(ControllerClass);
if ( Controller != None )
{
Controller.Possess(self);
AIController(Controller).Skill += SkillModifier;
}
}
}
function ResurrectNPC()
{
if(Health < NPCHealth)
{
Health = NPCHealth * StartingHealthPct;
HealthMax = NPCHealth;
SetCollision(true,true);
bHidden = false;
RepositionNPC();
}
}
function RepositionNPC()
{
if(bMovable)
{
SetRotation(PlacedRot);
SetLocation(PlacedLoc);
}
}
function SpawnDialogueTrigger()
{
local int i;
if(DialogueTrigger == none && NPCDialogue.length > 0)
{
DialogueTrigger = Spawn(class 'KF_DialogueSpot');
DialogueTrigger.bTouchTriggered = true;
DialogueTrigger.bRandomize = true;
DialogueTrigger.bHardAttach = true;
DialogueTrigger.SetBase(self);
DialogueTrigger.SetCollisionSize(CollisionRadius * 1.5,CollisionHeight) ;
for(i = 0 ; i < NPCDialogue.length ; i ++)
{
DialogueTrigger.Dialogues.length = i + 1;
DialogueTrigger.Dialogues[i].Display.Dialogue_text = NPCDialogue[i];
DialogueTrigger.Dialogues[i].Display.Dialogue_header = NPCName;
DialogueTrigger.Dialogues[i].BroadcastScope = InstigatorOnly;
DialogueTrigger.Dialogues[i].VoiceOver.SourceActor = self;
}
}
}
/* Doing this here instead of in Gameinfo.ReduceDamage() because of how hilariously hacked up KFGameType's implementation
of that function is ... */
simulated function TakeDamage(int Damage,pawn instigatedBy, Vector HitLocation, vector Momentum, class<DamageType> damageType, optional int HitIndex)
{
local int ReducedDamage;
/* log("=============================================================",'Story_Debug');
log(self@"Took Damage : "@Damage@" from : "@instigatedBy@" Am I damageable ? : "@bDamageable@" Current Health : "@Health,'Story_Debug');
*/
if(!bDamageable || Health <= 0)
{
return;
}
ReducedDamage = Damage;
// friendly fire ... scale down the damage
if(/*instigatedBy.Controller.GetTeamNum() == Controller.GetTeamNum()*/
PlayerController(instigatedBy.Controller) != none) // Yeah looks like this guy's never actually getting assigned a correct team.
{
ReducedDamage *= FriendlyFireDamageScale;
}
Super.TakeDamage(ReducedDamage,InstigatedBy,HitLocation,Momentum,damageType,HitIndex);
}
function bool GiveHealth(int HealAmount, int HealMax)
{
local bool bHealed;
bHealed = Super.GiveHealth(HealAmount,HealMax);
if(HealedEvent != '' && HealAmount > 0 && bHealed)
{
TriggerEvent(HealedEvent,self,Instigator);
}
return bHealed;
}
/*
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
if(!bPlayedDeath && !bIndestructible)
{
SpawnDummyCorpse(Killer,damageType,HitLocation);
}
}
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
Super(xPawn).PlayDying(DamageType,HitLoc);
}*/
simulated function SpawnDummyCorpse(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
local KFPawn DummyCorpse;
local int i, EquipLength;
SetCollision(false,false);
bHidden = true;
DummyCorpse = Spawn(class 'KFHumanPawn',,,Location,Rotation);
if(DummyCorpse != none)
{
DummyCorpse.LinkMesh(Mesh);
DummyCorpse.tag = tag;
DummyCorpse.Event = Event;
if(bDropInventoryOnDeath)
{
EquipLength = arraycount(RequiredEquipment);
for(i = 0 ; i < EquipLength ; i ++)
{
DummyCorpse.RequiredEquipment[i] = RequiredEquipment[i] ;
}
DummyCorpse.AddDefaultInventory();
}
DummyCorpse.Died(Killer,damageType,HitLocation);
}
}
function Trigger( Actor Other, Pawn EventInstigator )
{
switch(TriggerAction)
{
case TA_CommitSuicide : Died(Controller,class 'Suicided', Location) ; break;
case TA_ToggleHoldFire : bFireAtWill = !bFireAtWill; break;
case TA_ToggleActive : SetActive(!bActive); break;
}
}
function UpdateVisibility(bool On)
{
if(bOnlyVisibleWhenActive)
{
bHidden = !On;
SetCollision(!bHidden,!bHidden);
}
}
/* turns this NPC on / off
ie. Makes it damageable and decides whether AI ignores it
Network : Server */
function SetActive(bool On)
{
if(Role < Role_Authority)
{
return;
}
UpdateVisibility(On);
bActive = On;
bDamageable = bActive;
log(self@" is now bActive : "@On,'Story_Debug');
if(On && ActivationEvent != '')
{
TriggerEvent(ActivationEvent,self,self);
}
else
if(!On && DeActivationEvent != '')
{
TriggerEvent(DeActivationEvent,self,self);
}
}
simulated function int GetTeamNum()
{
return TeamIndex;
}
/* === AI functions =====================================*/
function float AssessThreatTo(KFMonsterController Monster, optional bool CheckDistance)
{
if(bNoThreatToZEDs ||
TeamIndex == 255 ||
Health <= 0 ||
!bActive ||
!bDamageable ||
(!IsThreateningTo(Monster.Pawn)) )
{
return -1.f;
}
return Super.AssessThreatTo(Monster,CheckDistance) + BaseAIThreatRating ;
}
/* Returns true if the supplied Monster considers this NPC a threat (and is prepared to attack it) */
function bool IsThreateningTo( Pawn Monster)
{
local int i;
local bool Result;
if(Monster == none)
{
return false;
}
Result = true;
for(i = 0 ; i < NotThreateningTo.length ; i ++)
{
if(ClassisChildOf(Monster.class,NotThreateningTo[i]))
{
Result = false;
break;
}
}
for(i = 0 ; i < OnlyThreateningTo.length ; i ++)
{
Result = false;
if(ClassisChildOf(Monster.class,OnlyThreateningTo[i]))
{
Result = true;
break;
}
}
return Result;
}
function bool IsPacifist()
{
local Inventory Inv;
local int Count;
local Weapon Weap;
local bool bUsingLethalGear;
for( Inv=Inventory; Inv!=None && Count < 1000; Inv=Inv.Inventory )
{
Weap = Weapon(Inv);
bUsingLethalGear = Weap != none && (Weap.bMeleeWeapon || Weap.HasAmmo()) ;
if(bUsingLethalGear && bFireAtWill)
{
return false;
}
Count++;
}
return true;
}
simulated event PostRender2D(Canvas C, float ScreenLocX, float ScreenLocY) // called if bScriptPostRender is true, overrides native team beacon drawing code
{
local PlayerController PC;
local float Opacity;
// local float Dist;
local HUDKillingFloor KFHUD;
local vector CameraLocation,CamDir;
local rotator CameraRotation;
if(!bShowHealthBar ||
!bActive ||
GetStateName() == 'Dying')
{
return;
}
PC = Level.GetLocalPlayerController();
if ( PC != None )
{
KFHUD = HUDKillingFloor(PC.myHUD);
if(KFHUD != none)
{
C.GetCameraLocation(CameraLocation, CameraRotation);
CamDir = vector(CameraRotation);
/* Rendering behind us... */
if ( (Normal(Location - CameraLocation) dot CamDir) < 0 )
{
return;
}
Opacity = FClamp(1.f - (VSize(PC.CalcViewLocation - Location) / 3000.f),0.25f,1.f) ;
KFHUD.DrawKFBar(C,ScreenLocX,ScreenLocY,Health/HealthMax,Byte(Opacity * 255.f),false);
}
}
}
function RosterEntry GetPlacedRoster()
{
return None;
}
function string GetPlayerName()
{
return NPCName;
}
defaultproperties
{
/* AccelRate=1000.0
AirControl=+0.15
GroundSpeed=200.0
WaterSpeed=200.000000
AirSpeed=230.000000
JumpZ=325.000000
MaxFallSpeed=600.000000
BaseEyeHeight=44.0
EyeHeight=44.0
CrouchHeight=34.000000
CollisionHeight=50.000000
Mesh=SkeletalMesh'KF_Soldier_Trip.Punk_Harold'
MovementAnims(0) ="WalkF_HuskGun"
MovementAnims(1) ="WalkB_HuskGun"
MovementAnims(2) ="WalkL_HuskGun"
MovementAnims(3) ="WalkR_HuskGun"
RunningAnims(0) = "JogF_Bullpup"
RunningAnims(1) = "JogB_Bullpup"
RunningAnims(2) = "JogL_Bullpup"
RunningAnims(3) = "JogR_Bullpup"
IdleWeaponAnim="Idle_Knife"
IdleRestAnim="Idle_Knife"
TurnLeftAnim="TurnL"
TurnRightAnim="TurnR"
*/
NPCName ="An NPC"
bNoDelete = true
bFireAtWill = true
ControllerClass = class 'KF_StoryNPC_AI'
TeamIndex = 0
NPCHealth = 100
FriendlyFireDamageScale = 1.f
bDropInventoryOnDeath = true
bStartActive = true
bDamageable = true
StartingHealthPct = 1.f
bAlwaysRelevant = true
bUseHitPoints = true
TriggerAction = TA_ToggleActive
}

View file

@ -0,0 +1,48 @@
class KF_StoryNPC_AI extends ScriptedController;
var KF_StoryNPC StoryPawn;
simulated function int GetTeamNum()
{
if(StoryPawn == none)
{
return 255;
}
else
{
return StoryPawn.TeamIndex;
}
}
function bool SameTeamAs(Controller C)
{
if(KFMonsterController(C) != none)
{
return false;
}
return true;
}
function Possess(Pawn aPawn)
{
Super.Possess(aPawn);
StoryPawn = KF_StoryNPC(Pawn);
if(StoryPawn != none)
{
bIsPlayer = true;
}
}
defaultproperties
{
FovAngle=+00085.000000
bCanOpenDoors=true
bCanDoSpecial=true
bIsPlayer=false
bStasis=false
RotationRate=(Pitch=3072,Yaw=30000,Roll=2048)
RemoteRole=ROLE_None
}

View file

@ -0,0 +1,50 @@
/*
--------------------------------------------------------------
a runtime-spawnable version of the KF_StoryNPC
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryNPC_Spawnable extends KF_StoryNPC
notplaceable;
function Reset()
{
bFireAtWill = bInitialFireAtWill ;
SetActive(bInitialActive);
}
simulated function PostBeginPlay()
{
/* need to manually spawn controllers for non editor-placed pawns */
Super.PostBeginPlay();
if(Controller == none)
{
Controller = Spawn(ControllerClass);
Controller.Possess(self);
}
Skins.length = 0;
SetMovementPhysics();
}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
if(Controller != none)
{
// so that the controller will get garbage collected */
Controller.bIsPlayer = false;
}
/* skip the phoney corpse stuff in the superclass and just do a normal death */
Super(KFPawn).Died(Killer,damageType,HitLocation);
}
defaultproperties
{
bNoDelete = false
}

View file

@ -0,0 +1,107 @@
/*
--------------------------------------------------------------
KF_StoryNPC_Static
--------------------------------------------------------------
StaticMesh NPC that can take damage.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryNPC_Static extends KF_StoryNPC;
var bool bCheckPointed;
var float SavedHealth;
simulated function PostBeginPlay()
{
Super.PostbeginPlay();
if(!bUseHitPoints)
{
Hitpoints.length = 0;
}
}
function ResurrectNPC(){}
function SaveHealthState()
{
bCheckPointed = true;
SavedHealth = Health;
}
function Reset()
{
Super.Reset();
if(bCheckPointed)
{
Health = SavedHealth;
}
}
event EncroachedBy( actor Other ){}
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
{
local Controller PC;
SpawnGibs(rotation, 1);
/* Necessary because this pawn never dies .. we dont want AI Controllers getting stuck in infinite loops trying to attack it */
for ( PC=Level.ControllerList; PC!=None; PC=PC.NextController )
{
if(PC.Enemy == self)
{
PC.Enemy = none;
}
}
if(bIndestructible)
{
return;
}
BaseAIThreatRating = -1.f;
}
// Don't spawn any inventory for a static NPC
function AddDefaultInventory(){}
DefaultProperties
{
RespawnTime 1.f
bUseHitPoints = false
bMovable = false
bStatic = false // <--- I really wanna set this to true but if I do ... this actor can't receive any events.
bUseDefaultPhysics = true
bStaticLighting = true
bShadowCast=True
bPlayerShadows=false
bActorShadows = false
bDramaticLighting=false
bPathColliding = true
bUseCylinderCollision = false
bCanPickupInventory = false
NetUpdateFrequency = 5.f
bIndestructible = true
bCanJump = false
bCanBeHealed = false
Physics = PHYS_NONE
DrawType = DT_StaticMesh
StaticMesh = StaticMesh'Waterworks_SM.pipe01_03'
ControllerClass = class 'AIController'
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,101 @@
/*
--------------------------------------------------------------
KF_StoryPRI
--------------------------------------------------------------
Custom Player ReplicationInfo class for Objective Mode
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryPRI extends KFPlayerReplicationInfo;
/* Struct that contains information for rendering a hovering icon
overtop of a player which is not relevant to clients */
struct SPlayerIconData
{
var Material IconMat; // Icon to render over top of the pawn's head.
var Vector CurrentPawnLoc; // current location of the player pawn.
var Vector LastPawnLoc;
};
var private vector InterpolatedPawnLoc;
var private SPlayerIconData FloatingIconData;
/* Reference to the pawn this PRI belongs to */
var private KFHumanPawn_Story OwnerPawn;
var float LastIconUpdateTime;
replication
{
unreliable if(Role == Role_Authority && bNetDirty)
FloatingIconData,OwnerPawn;
}
function SetReplicatedPawnLoc(vector NewLoc)
{
if(Level.TimeSeconds - LastIconUpdateTime > NetUpdateFrequency)
{
LastIconUpdateTime = Level.TimeSeconds;
FloatingIconData.LastPawnLoc = FloatingIconData.CurrentPawnLoc;
}
if(NewLoc != FloatingIconData.CurrentPawnLoc)
{
FloatingIconData.CurrentPawnLoc = NewLoc;
}
}
function SetOwnerPawn(KFHumanPawn_Story NewOwnerPawn)
{
OwnerPawn = NewOwnerPawn;
}
function SetFloatingIconMat(material NewMat)
{
FloatingIconData.IconMat = NewMat;
}
/* Returns the current position of the pawn this PRI belongs to (or as close as we can get) */
simulated function Vector GetCurrentPawnLoc()
{
return FloatingIconData.CurrentPawnLoc;
}
simulated function Vector GetlastPawnLoc()
{
return FloatingIconData.LastPawnLoc;
}
simulated function Vector GetInterpolatedPawnLoc()
{
return InterpolatedPawnLoc;
}
/* Set on the client only */
simulated function SetInterpolatedPawnLoc(vector NewValue)
{
InterpolatedPawnLoc = NewValue;
}
/* Returns the Icon to render overtop of our pawn's head (if one is set) */
simulated function Material GetFloatingIconMat()
{
return FloatingIconData.IconMat;
}
simulated function KFHumanPawn_Story GetOwnerPawn()
{
return OwnerPawn;
}

View file

@ -0,0 +1,128 @@
/*
--------------------------------------------------------------
KF_StorySquadDesigner
--------------------------------------------------------------
This actor provides level designers with a method of settings up
pre-defined squads of Monsters for story missions. Squads can be
referenced in either KF_StoryZombeVolumes or KF_StoryWaveDesigners.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StorySquadDesigner extends Info
dependson(KFStoryGameInfo)
hidecategories(Collision,Force,Karma,Lighting,LightColor,Sound,Events,Movement)
placeable;
var() array<KFStoryGameInfo.SZEDSquadType> Squads;
var bool bUseDefaultSquads;
/* In case we want to just use whatever is in the KF Gametype instead of having to set up custom monster squads */
function FillSquadsFromGameType()
{
local int i,idx;
local KFGameType KFGI;
local string SquadString;
local array<string> ZEDTypes;
local string NewZEDType;
local int SplitSize;
local string LeftHalf;
local int Squadidx;
local bool bExistsAlready;
KFGI = KFGameType(Level.Game);
if(KFGI == none)
{
return;
}
SplitSize = 2;
for(i = 0 ; i < KFGI.MonsterSquad.length ; i ++)
{
Squads.length = Squads.length + 1;
Squads[Squads.length-1].Squad_Name = KFGI.MonsterSquad[i] ;
SquadString = Squads[Squads.length-1].Squad_Name ;
while(Len(SquadString) >= SplitSize)
{
NewZEDType = Left(SquadString,SplitSize);
ZEDTypes[ZEDTypes.length] = NewZEDType;
Divide(SquadString,NewZEDType,LeftHalf,SquadString);
}
for(idx = 0 ; idx < ZEDTypes.length ; idx ++)
{
bExistsAlready = false;
for(Squadidx = 0 ; SquadIdx < Squads[Squads.length-1].Squad_ZEDS.length ; SquadIdx ++)
{
if(string(Squads[Squads.length-1].Squad_ZEDs[SquadIdx].ZEDClass) == ZEDTypes[idx])
{
bExistsAlready = true;
break;
}
}
if(bExistsAlready)
{
continue;
}
else
{
Squads[Squads.length-1].Squad_ZEDs.length = Squads[Squads.length-1].Squad_ZEDs.length + 1;
Squads[Squads.length-1].Squad_ZEDs[Squads[Squads.length-1].Squad_ZEDs.length -1].ZEDClass = GetAssociatedZEDClass(Right(ZEDTypes[idx],1)) ;
Squads[Squads.length-1].Squad_ZEDs[Squads[Squads.length-1].Squad_ZEDs.length -1].NumToSpawn = int(Left(ZEDTypes[idx],1)) ;
}
}
ZEDTypes.length = 0;
}
}
/* Returns the class of the ZED associated with the supplied letter ...
a result of the delightfully overblown Monster Squad system from UT2k4s invasion gametype
where a Monster Class is a letter! and a Squad is a bunch of letters! And a Wave is a bunch of
bits that represent a bunch of binary which represents a bunch of letters which represent a bunch of Monsters! HERPdeDERPaHURRRRDURRRR !
*/
function class<KFMonster> GetAssociatedZEDClass( string Letter)
{
local KFGameType KFGI;
local int i;
local class<KFMonster> ZEDClassType;
local string MonsterID;
KFGI = KFGameType(Level.Game);
if(KFGI == none)
{
return none;
}
for(i = 0 ; i < KFGI.MonsterCollection.default.StandardMonsterClasses.length ; i ++)
{
MonsterID = KFGI.MonsterCollection.default.StandardMonsterClasses[i].MID ;
if(MonsterID == Letter)
{
ZEDClassType = class<KFMonster>(DynamicLoadObject(KFGI.MonsterCollection.default.StandardMonsterClasses[i].MClassName, class'Class')) ;
// log(" The Monster class associated with the letter : "@Letter@"is : "@ZEDClassType.default.menuname,'Story_Debug');
return ZEDClassType;
}
}
}
defaultproperties
{
DrawScale = 0.5
Texture = Texture'KFStoryGame_Tex.Editor.KF_StorySquads_Ico'
}

View file

@ -0,0 +1,196 @@
/*
--------------------------------------------------------------
KF_StoryTraderController
--------------------------------------------------------------
This Actor is used to control the opening / closing of trader shops
in story mode missions .
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryTraderController extends info
placeable;
enum EShopAction
{
Action_OpenCurrentShop,
Action_SelectNewShop,
};
var () EShopAction ShopAction;
var ShopVolume CurrentShop;
var KFGameReplicationInfo KFGRI;
/* List of shops we want to active / de-activate */
var () array<ShopVolume> Shops;
/* Should we disable player collision when the trader is activated ? */
var () bool bDisablePlayerCollision;
/* Should we remove pickups from the ground when the trader shop closes ? */
var () bool bDestroyPickupsOnShopClose;
event PostBeginPlay()
{
Super.PostBeginPlay();
KFGRI = KFGameReplicationInfo(Level.game.GameReplicationInfo) ;
}
function Trigger(Actor Other, Pawn EventInstigator)
{
switch(ShopAction)
{
case Action_SelectNewShop :
CloseCurrentShop();
FindNewShop();
break;
case Action_OpenCurrentShop :
OpenCurrentShop();
break;
}
}
function OpenCurrentShop()
{
local Controller C;
if(GetCurrentShop() == none)
{
FindNewShop();
}
log("=========================================",'Story_Debug');
log("OPEN trader shop : "@GetCurrentShop(),'Story_Debug');
GetCurrentShop().OpenShop();
if(KFGameType(Level.Game) != none)
{
KFGameType(Level.Game).bTradingDoorsOpen = true;
}
/* Maybe disable player collision when the shop is opened ? */
if(bDisablePlayerCollision)
{
for ( C = Level.ControllerList; C != none; C = C.NextController )
{
if(C.Pawn != none && C.Pawn.Health > 0 )
{
C.Pawn.bBlockActors = false;
}
}
}
}
function bool CloseCurrentShop()
{
local bool bSuccessfulBoot;
local Controller C;
local WeaponPickup DroppedWeapon;
if(GetCurrentShop() == none ||
!GetCurrentShop().bCurrentlyOpen)
{
return false;
}
log("=========================================",'Story_Debug');
log("CLOSE trader shop : "@GetCurrentShop(),'Story_Debug');
bSuccessfulBoot = GetCurrentShop().BootPlayers();
GetCurrentShop().CloseShop();
// wait for doors to close before BootPlayers (see KFGameInfo.Timer)
SetTimer(1.f, false);
if( KFGameType(Level.Game) != none)
{
KFGameType(Level.Game).bTradingDoorsOpen = false;
}
/* Post Buy-Menu garbage collection & Player Collision Handling */
for ( C = Level.ControllerList; C != none; C = C.NextController )
{
/* Maybe turn player collision back on ? */
if ( bDisablePlayerCollision &&
C.Pawn != none && C.Pawn.Health > 0 )
{
C.Pawn.bBlockActors = C.Pawn.default.bBlockActors;
}
if(KFPlayerController(C) != none)
{
KFPlayerController(C).ClientForceCollectGarbage();
}
}
/* Maybe remove dropped weapons from the ground ? */
if(bDestroyPickupsOnShopClose)
{
foreach AllActors(class'WeaponPickup', DroppedWeapon)
{
if ( DroppedWeapon.bDropped )
{
DroppedWeapon.Destroy();
}
}
}
if(!bSuccessfulBoot)
{
log("WARNING !! - Couldn't boot all players out of Shop : "@GetCurrentShop(),'Story_Debug');
}
return bSuccessfulBoot;
}
function Timer()
{
if(GetCurrentShop() != none && !GetCurrentShop().bCurrentlyOpen)
{
GetCurrentShop().BootPlayers();
}
}
function FindNewShop()
{
local ShopVolume NewShop;
if(Shops.length > 0)
{
NewShop = Shops[Rand(Shops.length)] ;
if(NewShop != none)
{
NewShop.InitTeleports();
}
}
if(KFGRI != none)
{
KFGRI.CurrentShop = NewShop;
}
log("=========================================",'Story_Debug');
log("FIND Trader Shop : "@GetCurrentShop(),'Story_Debug');
}
function ShopVolume GetCurrentShop()
{
if(KFGRI != none)
{
return KFGRI.CurrentShop ;
}
}
defaultproperties
{
bDisablePlayerCollision = true
bDestroyPickupsOnShopClose = true
Texture = Texture'KFStoryGame_Tex.Editor.Trader_ico'
}

View file

@ -0,0 +1,262 @@
/*
--------------------------------------------------------------
KF_StoryWaveDesigner
--------------------------------------------------------------
This actor provides level designers with a method of settings up
waves in story missions. It is used in conjunction with either
KFZombieVolumes or KFStoryZombieVolumes.
note : This class just serves as a front end for level designers
and a place to declare structs.
All of the heavy lifting is done in KF_Wave_Controller
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryWaveDesigner extends Info
dependson(KFStoryGameInfo)
hidecategories(sound)
placeable;
enum ESpawnProgressionType
{
Proceed_On_AllZEDSDead,
Proceed_On_AllZEDsSpawned,
};
struct SWaveSpawn
{
var() array<string> SquadList;
var array<int> SpawnedSquads;
var KFStoryGameInfo.SZEDSquadType NextSpawnSquad;
var array<KFStoryGameInfo.SZEDSquadType> AllSquads;
var() name ZombieDeathEvent;
var() name ZombieSpawnTag;
var() name ZombieSpawnEvent;
var() float SpawnInterval;
var() int MaxZEDs;
var() bool bNoDifficultyScaling;
var() bool bRandomSquadsWithoutRepeats; // Squads will be used in a random order, but will use every squad in the list before starting to go through the list again
};
enum ESineWavePatternUsage
{
SWP_StartAtMinRate,
SWP_StartAtMaxRate,
SWP_AlwaysFullSpeed,
};
struct SZEDWave
{
var bool bKillStragglers;
var() array<name> Wave_VolumeTags;
var() name Wave_ActivationTag;
var() array<SWaveSpawn> Wave_Spawns;
var KF_Wave_Controller WaveController;
var() ESineWavePatternUsage SineWavePatternUsage;
};
/*==============================================================================*/
var () array<SZEDWave> Waves;
var int CurrentWaveIdx;
enum EWaveProgressType
{
Procceed_On_AllZEDsSpawned,
Proceed_On_AllZEDsKilled,
};
var(Debug) bool bPrintDebugLogs;
/*==============================================================================*/
function Reset()
{
local int i;
for(i = 0 ; i < Waves.length ; i ++)
{
Waves[i].WaveController.Reset();
}
}
function PostBeginPlay()
{
if(KFStoryGameInfo(Level.Game) != none)
{
InitWaveControllers();
}
}
function InitWaveControllers()
{
local int i;
for(i = 0 ; i < Waves.length ; i ++)
{
SpawnControllerForWave(i);
}
}
function SpawnControllerForWave(int Index)
{
Waves[Index].WaveController = Spawn(class 'KF_Wave_Controller',self);
if(Waves[Index].WaveController != none)
{
Waves[Index].WaveController.SetOwningmanager() ;
Waves[Index].WaveController.WaveIndex = Index ;
Waves[Index].WaveController.Initialize();
}
else
{
log(" === WARNING === could not spawn a WaveController for : "@self@" at Wave Index : "@Index@"!",'Story_Debug');
}
}
/*
==================================================================================
Handy functions ... mostly gonna query the Wave Controller here */
function KF_Wave_Controller GetCurrentWaveController()
{
return Waves[CurrentWaveIdx].WaveController ;
}
/* Returns the amount of time between ZED Spawns */
function float GetAdjustedSpawninterval()
{
return GetCurrentWaveController().GetSpawnInterval();
}
/* Grab the total number of ZEDs in the current wave */
function int GetWaveMaxMonsters()
{
return GetCurrentWaveController().GetMaxMonsters();
}
/* Grab the number of ZEDs left alive in the current wave */
function int GetWaveRemainingMonsters()
{
return GetCurrentWaveController().GetRemainingMonsters();
}
function bool IsDirectingSpawnsFor( ZombieVolume TestVol)
{
return GetCurrentWaveController().IsDirectingSpawnsFor(TestVol);
}
/*
function array<class <KFMonster> > GetCurrentZEDList()
{
return GetCurrentWaveController().GetCurrentZEDList();
} */
function array<class <KFMonster> > GetNextSpawnSquad()
{
return GetCurrentWaveController().NextSpawnSquad;
}
function int GetWaveByName(name WaveTag)
{
local int i;
for(i = 0 ; i < Waves.length ; i ++)
{
if(Waves[i].Wave_ActivationTag == WaveTag)
{
return i;
}
}
return -1;
}
/*=====================================================================================
======================================================================================*/
// Events ==============================================================================
function Trigger( actor Other, pawn EventInstigator )
{
GoToNextWave();
}
function GoToNextWave()
{
if(CurrentWaveidx < Waves.length - 1)
{
GoToWave(Waves[CurrentWaveIdx + 1].Wave_ActivationTag);
}
}
function GoToWave(name WaveTag)
{
local int i;
CurrentWaveIdx = GetWaveByName(WaveTag) ;
/* Abort any other currently active waves in this designer*/
for(i = 0 ; i < Waves.length ; i ++)
{
if(i != CurrentWaveIdx &&
Waves[i].WaveController.bActive)
{
Waves[i].WaveController.AbortWave();
}
}
}
/*====================================================================================
======================================================================================*/
function GetEvents(out array<name> TriggeredEvents, out array<name> ReceivedEvents)
{
local int i,idx;
Super.GetEvents(TriggeredEvents,ReceivedEvents);
for(i = 0 ; i < Waves.length ; i ++)
{
if(Waves[i].Wave_ActivationTag != '')
{
ReceivedEvents[ReceivedEvents.length] = Waves[i].Wave_ActivationTag ;
}
for(idx = 0 ; idx < Waves[i].Wave_Spawns.length ; idx ++)
{
if(Waves[i].Wave_Spawns[idx].ZombieSpawnEvent != '')
{
TriggeredEvents[TriggeredEvents.length] = Waves[i].Wave_Spawns[idx].ZombieSpawnEvent;
}
if(Waves[i].Wave_Spawns[idx].ZombieDeathEvent != '')
{
TriggeredEvents[TriggeredEvents.length] = Waves[i].Wave_Spawns[idx].ZombieDeathEvent;
}
}
}
}
defaultproperties
{
bStatic = false
DrawScale = 0.5
Texture = Texture'KFStoryGame_Tex.Editor.KF_StoryWaves_Ico'
}

View file

@ -0,0 +1,84 @@
/*
--------------------------------------------------------------
KF_UseableMover
--------------------------------------------------------------
A Type of mover that is triggered by 'Use Objectives' in KF Story missions
It interpolates while the player is holding the Use key and resets to
its original position if the player lets go of the key. (unless the Objective
condition is set to keep use progress, in which case it will just freeze
where it was at).
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_UseableMover extends Mover;
/* reference to the Use condition which controls this mover's interpolation */
var ObjCondition_Use ControllingCondition;
var float InitialMoveTime;
/* Interaction stubs - Called by the Controlling Condition */
function Startedusing();
function StoppedUsing();
/* Called when a use condition 'possesses' this Mover */
function Notify_Controlled(ObjCondition_Use NewController)
{
if(NewController != none)
{
ControllingCondition = NewController;
InitialMoveTime = ControllingCondition.GetRemainingUseTime() ;
MoveTime = InitialMoveTime;
InitialState = 'UseControlled' ;
Backup_InitialState = InitialState;
GoToState('UseControlled');
}
}
/* state the mover enters when it is being controlled by a player */
state UseControlled
{
function StartedUsing()
{
InitialMoveTime = ControllingCondition.GetRemainingUseTime() ;
MoveTime = InitialMoveTime;
GoToState('UseControlled','Open');
}
function StoppedUsing()
{
if(ControllingCondition.bKeepUseProgress)
{
GoToState('UseControlled','Freeze');
}
else
{
InitialMoveTime = ControllingCondition.GetRemainingUseTime() * 0.1 ;
MoveTime = InitialMoveTime;
GoToState('UseControlled','Close');
}
}
Open:
bClosed = false;
DoOpen();
FinishInterpolation();
FinishedOpening();
Stop;
Close:
DoClose();
FinishInterpolation();
FinishedClosing();
SetResetStatus( false );
Freeze:
bInterpolating = false;
FinishInterpolation();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
/*
--------------------------------------------------------------
KeyPickup_Story
--------------------------------------------------------------
Author : Alex Quick
--------------------------------------------------------------
*/
class KeyPickup_Story extends WeaponPickup;
/*
function inventory SpawnCopy( pawn Other )
{
local inventory KeyInv;
KeyInv = Super.SpawnCopy(Other);
KeyInv.Tag = Tag;
if( KFKeyInventory(KeyInv)!=None )
{
KFKeyInventory(KeyInv).MyPickup = self;
}
return Copy;
}*/
defaultproperties
{
InventoryType=Class'KFMod.KFKeyInventory'
PickupMessage="You found a key"
PickupSound=Sound'PatchSounds.slide1-3'
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'KillingFloorLabStatics.KeyCard1'
Physics=PHYS_Falling
DrawScale=0.100000
AmbientGlow=40
UV2Texture=FadeColor'PatchTex.Common.PickupOverlay'
CollisionRadius=20.000000
CollisionHeight=5.000000
MessageClass=Class'UnrealGame.PickupMessagePlus'
}

View file

@ -0,0 +1,34 @@
class Msg_CashReward extends LocalMessage;
static function string GetString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2,
optional Object OptionalObject
)
{
if(Switch > 0)
{
return "+ £"@Switch ;
}
else
{
return "- £"@Switch ;
}
}
defaultproperties
{
bIsConsoleMessage=false
DrawColor=(B=100,G=255,R=100,A=255)
Lifetime=5
FontSize=2
bBeep=False
bFadeMessage=True
bIsUnique=False
StackMode=SM_Up
PosX = 0.92
PosY = 0.82
}

View file

@ -0,0 +1,52 @@
/*
--------------------------------------------------------------
Msg_CheckPoint
--------------------------------------------------------------
Notification that a checkpoint was reached
Author : Alex Quick
--------------------------------------------------------------
*/
class Msg_CheckPoint extends LocalMessage;
var string CheckPointStrings[2];
static function string GetString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2,
optional Object OptionalObject
)
{
local string FinalString;
local KF_StoryCheckPointVolume CheckPoint;
CheckPoint = KF_StoryCheckPointVolume(OptionalObject);
switch(Switch)
{
case 0 : FinalString = RelatedPRI_1.PlayerName@default.CheckPointStrings[0]@CheckPoint.CheckPointName ; break;
case 1 : FinalString = default.CheckPointStrings[1]@CheckPoint.CheckPointName ; break;
}
return FinalString;
}
defaultproperties
{
CheckPointStrings(0)="reached"
CheckPointStrings(1)="Your team will respawn at .."
bIsConsoleMessage=False
bFadeMessage=True
Lifetime=5
DrawColor=(R=25,B=255,G=150)
PosY=0.5
FontSize=2
bIsUnique = false
}

View file

@ -0,0 +1,67 @@
/*
--------------------------------------------------------------
Msg_EyeBallNotification
--------------------------------------------------------------
Local Message class for Patriarch's Eyeball
--------------------------------------------------------------
*/
class Msg_EyeBallNotification extends WaitingMessage;
var localized string EyeWasPickedUpString;
var localized string EyeWasDroppedString;
var localized string EyeWasScannedString;
static function string GetString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2,
optional Object OptionalObject
)
{
switch(Switch)
{
case 1 : // Someone picked up the eye
return RelatedPRI_1.PlayerName@default.EyeWasPickedUpString ;
case 2 : // Someone dropped the eye
return RelatedPRI_1.PlayerName@default.EyeWasDroppedString ;
case 3 : // Someone used the eye on the retinal scanner.
return RelatedPRI_1.PlayerName@default.EyeWasScannedString;
}
}
static function GetPos(int Switch, out EDrawPivot OutDrawPivot, out EStackMode OutStackMode, out float OutPosX, out float OutPosY)
{
OutDrawPivot = default.DrawPivot;
OutStackMode = default.StackMode;
OutPosX = default.PosX;
OutPosY = 0.7;
}
static function float GetLifeTime(int Switch)
{
return default.LifeTime;
}
static function int GetFontSize(int Switch, PlayerReplicationInfo RelatedPRI1, PlayerReplicationInfo RelatedPRI2, PlayerReplicationInfo LocalPlayer)
{
return 2;
}
defaultproperties
{
Lifetime=4
DrawColor=(R=255,G=25,B=25)
EyeWasPickedUpString = "picked up the Patriarch's eyeball"
EyeWasDroppedString = "dropped the Patriarch's eyeball"
EyeWasScannedString = "used the Patriarch's eyeball on the retinal scanner."
}

View file

@ -0,0 +1,61 @@
/*
--------------------------------------------------------------
Msg_MaintenanceKeyCardNotification
--------------------------------------------------------------
Local Message class for the Subway Maintenance keycard
--------------------------------------------------------------
*/
class Msg_MaintenanceKeyCardNotification extends WaitingMessage;
var localized string KeyCardPickedUpString;
var localized string KeyCardDroppedString;
static function string GetString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2,
optional Object OptionalObject
)
{
switch(Switch)
{
case 1 : // Someone picked up the keycard.
return RelatedPRI_1.PlayerName@default.KeyCardPickedUpString ;
case 2 : // Someone dropped the keycard.
return RelatedPRI_1.PlayerName@default.KeyCardDroppedString ;
}
}
static function GetPos(int Switch, out EDrawPivot OutDrawPivot, out EStackMode OutStackMode, out float OutPosX, out float OutPosY)
{
OutDrawPivot = default.DrawPivot;
OutStackMode = default.StackMode;
OutPosX = default.PosX;
OutPosY = 0.7;
}
static function float GetLifeTime(int Switch)
{
return default.LifeTime;
}
static function int GetFontSize(int Switch, PlayerReplicationInfo RelatedPRI1, PlayerReplicationInfo RelatedPRI2, PlayerReplicationInfo LocalPlayer)
{
return 2;
}
defaultproperties
{
Lifetime=4
DrawColor=(R=255,G=25,B=25)
KeyCardPickedUpString = "picked up the Maintenance Keycard"
KeyCardDroppedString = "dropped the Maintenance KeyCard"
}

View file

@ -0,0 +1,61 @@
/*
--------------------------------------------------------------
Msg_NitroglycerinNotification
--------------------------------------------------------------
Local Message class for the Nitroglycerin
--------------------------------------------------------------
*/
class Msg_NitroglycerinNotification extends WaitingMessage;
var localized string NitroglycerinPickedUpString;
var localized string NitroglycerinDroppedString;
static function string GetString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2,
optional Object OptionalObject
)
{
switch(Switch)
{
case 1 : // Someone picked up the Nitroglycerin
return RelatedPRI_1.PlayerName@default.NitroglycerinPickedUpString ;
case 2 : // Someone dropped the Nitroglycerin
return RelatedPRI_1.PlayerName@default.NitroglycerinDroppedString ;
}
}
static function GetPos(int Switch, out EDrawPivot OutDrawPivot, out EStackMode OutStackMode, out float OutPosX, out float OutPosY)
{
OutDrawPivot = default.DrawPivot;
OutStackMode = default.StackMode;
OutPosX = default.PosX;
OutPosY = 0.7;
}
static function float GetLifeTime(int Switch)
{
return default.LifeTime;
}
static function int GetFontSize(int Switch, PlayerReplicationInfo RelatedPRI1, PlayerReplicationInfo RelatedPRI2, PlayerReplicationInfo LocalPlayer)
{
return 2;
}
defaultproperties
{
Lifetime=4
DrawColor=(R=255,G=25,B=25)
NitroglycerinPickedUpString = "picked up the Nitroglycerin"
NitroglycerinDroppedString = "dropped the Nitroglycerin"
}

View file

@ -0,0 +1,61 @@
/*
--------------------------------------------------------------
Msg_ThermiteNotification
--------------------------------------------------------------
Local Message class for the Thermite pickup
--------------------------------------------------------------
*/
class Msg_ThermiteNotification extends WaitingMessage;
var localized string ThermitePickedUpString;
var localized string ThermiteDroppedString;
static function string GetString(
optional int Switch,
optional PlayerReplicationInfo RelatedPRI_1,
optional PlayerReplicationInfo RelatedPRI_2,
optional Object OptionalObject
)
{
switch(Switch)
{
case 1 : // Someone picked up the Thermite
return RelatedPRI_1.PlayerName@default.ThermitePickedUpString ;
case 2 : // Someone dropped the Thermite
return RelatedPRI_1.PlayerName@default.ThermiteDroppedString ;
}
}
static function GetPos(int Switch, out EDrawPivot OutDrawPivot, out EStackMode OutStackMode, out float OutPosX, out float OutPosY)
{
OutDrawPivot = default.DrawPivot;
OutStackMode = default.StackMode;
OutPosX = default.PosX;
OutPosY = 0.7;
}
static function float GetLifeTime(int Switch)
{
return default.LifeTime;
}
static function int GetFontSize(int Switch, PlayerReplicationInfo RelatedPRI1, PlayerReplicationInfo RelatedPRI2, PlayerReplicationInfo LocalPlayer)
{
return 2;
}
defaultproperties
{
Lifetime=4
DrawColor=(R=255,G=25,B=25)
ThermitePickedUpString = "picked up the Thermite"
ThermiteDroppedString = "dropped the Thermite"
}

View file

@ -0,0 +1,22 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class ObjAction_GoToLastObjective extends ObjAction_GoToObjective
hideCategories(KF_ObjectiveAction)
editinlinenew;
function name GetTargetObj()
{
/* Last objective only returns anything meaningful at runtime */
if(GetObjOwner() != none && GetObjOwner().StoryGI != none)
{
return GetObjOwner().StoryGI.LastObjective.ObjectiveName ;
}
return '';
}
DefaultProperties
{
}

View file

@ -0,0 +1,112 @@
/*
--------------------------------------------------------------
Action_GoToNextObjective
--------------------------------------------------------------
This Action changes the current objective to the next one
in the sorted Objectives list defined in the Level Rules actor.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjAction_GoToNextObjective extends ObjAction_GoToObjective
hideCategories(KF_ObjectiveAction)
editinlinenew;
/* Offset to apply to ObjectiveIndex. ie. We want to skip forward to the next,NEXT objective. (offset=1) */
var () int Offset;
/* if not null, Go to the next objective that has this tag */
var () name NextObjTag;
/* At Runtime query the gameinfo to figure out what the next objective is. Otherwise use the list
in the levelinfo */
function name GetTargetObj()
{
local array<StoryObjectiveBase> ObjectiveList;
local int i,ObjIdx;
if(GetObjOwner() != none && GetObjOwner().StoryGI != none) // runtime.
{
ObjectiveList = GetObjOwner().StoryGI.SortedObjectives ;
ObjIdx = GetObjOwner().StoryGI.CurrentObjectiveIdx;
}
if(NextObjTag == '')
{
return ObjectiveList[ ObjIdx + (1 + Offset) ].ObjectiveName ;
}
else
{
for(i = (ObjIdx + (1 + Offset)) ; i < ObjectiveList.length ; i ++)
{
if(ObjectiveList[i].tag == NextObjTag)
{
return ObjectiveList[i].ObjectiveName;
}
}
}
log("Warning - Could not find any objectives with tag : "@NextObjTag@" in the Sorted Objectives array."@self@" will fail. ");
return '';
}
function StoryObjectiveBase GetNextEditorObj(StoryObjectiveBase Sender, array<StoryObjectiveBase> ObjList)
{
local int i,idx,ObjIdx;
local LevelInfo LI;
local array<StoryObjectiveBase> SortedObjList;
if(Sender == none)
{
return none;
}
LI = Sender.Level ;
/* Build a list of the linear objectives in the map */
SortedObjList.length = LI.StoryObjectives.length;
for(i = 0 ; i < ObjList.length ; i ++)
{
for(idx = 0 ; idx < LI.StoryObjectives.length ; idx++)
{
if(ObjList[i].ObjectiveName == LI.StoryObjectives[idx])
{
SortedObjList[idx] = ObjList[i] ;
}
if(SortedObjList[idx] == Sender)
{
ObjIdx = idx;
}
}
}
ObjList = SortedObjList;
if(NextObjTag == '')
{
return ObjList[ ObjIdx + (1 + Offset) ];
}
else
{
for(i = (ObjIdx + (1 + Offset)) ; i < ObjList.length ; i ++)
{
if(ObjList[i].tag == NextObjTag)
{
return ObjList[i];
}
}
}
return none;
}
DefaultProperties
{
}

View file

@ -0,0 +1,65 @@
/*
--------------------------------------------------------------
Action_GoToObjective
--------------------------------------------------------------
This Action changes the current objective to the one specified
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjAction_GoToObjective extends KF_ObjectiveAction
editinlinenew;
function ExecuteAction(Controller ActionInstigator)
{
Super.ExecuteAction(ActionInstigator);
GetObjOwner().StoryGI.SetActiveObjective(GetObjOwner().StoryGI.FindObjectiveNamed(GetTargetObj()),ActionInstigator.Pawn) ;
}
function bool IsValidActionFor(KF_StoryObjective Obj)
{
local KF_StoryObjective TargetObj;
TargetObj = Obj.StoryGI.FindObjectiveNamed(GetTargetObj());
if(TargetObj != none)
{
return TargetObj.IsValidForActivation();
}
return false;
}
function name GetTargetObj()
{
return ObjectiveName;
}
function StoryObjectiveBase GetNextEditorObj(StoryObjectiveBase Sender, array<StoryObjectiveBase> ObjList)
{
local int i;
if(ObjectiveName != '')
{
for(i = 0 ; i < ObjList.length ; i ++)
{
if(ObjList[i].ObjectiveName == ObjectiveName)
{
return ObjList[i];
}
}
}
return none;
}
DefaultProperties
{
}

View file

@ -0,0 +1,26 @@
/*
--------------------------------------------------------------
Action_LoseGame
--------------------------------------------------------------
This Action ends the match in defeat, when Activated.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjAction_LoseGame extends KF_ObjectiveAction
editinlinenew
HideCategories(KF_ObjectiveAction);
function ExecuteAction(Controller ActionInstigator)
{
Super.ExecuteAction(ActionInstigator);
GetObjOwner().StoryGI.EndGame(ActionInstigator.PlayerReplicationinfo, "LoseAction") ;
}
DefaultProperties
{
}

View file

@ -0,0 +1,98 @@
/*
--------------------------------------------------------------
Action_Random
--------------------------------------------------------------
This Action serves as a container for any number of other Actions.
when initalized, it picks one at random and activates it.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjAction_Random extends KF_ObjectiveAction
editinlinenew
HideCategories(KF_ObjectiveAction);
struct SRandomAction
{
var () editinlineuse KF_ObjectiveAction Action;
var () int Priority;
};
var (Actions) float PriorityBias;
var (Actions) editinlineuse array<SRandomAction> RandomActions;
var int RandIdx;
function ExecuteAction(Controller ActionInstigator)
{
Super.ExecuteAction(ActionInstigator);
AssignRandomAction(ActionInstigator);
}
function Reset()
{
super.Reset();
SwapAction(self);
}
function AssignRandomAction(Controller ActionInstigator)
{
local float HighestRating;
local float PriorityVal;
local int BestIdx;
local array<float> Ratings;
local int i,idx;
HighestRating = -1;
BestIdx = -1;
for(i = 0 ; i < RandomActions.length ; i ++)
{
PriorityVal = RandomActions[i].Priority ;
Ratings[i] = RandRange( FMin(Ratings[idx] * PriorityBias,Ratings[idx]) , PriorityVal ) ;
}
for(idx = 0 ; idx < Ratings.length ; idx ++)
{
if(Ratings[idx] > highestRating &&
RandomActions[idx].Action.IsValidActionFor(GetObjOwner()) )
{
BestIdx = idx;
HighestRating = Ratings[idx] ;
}
}
if(BestIdx < 0 || BestIdx >= RandomActions.length)
{
log("Warning - Cannot find a valid action for : "@self@" Random action will fail. ",'Story_Debug');
}
RandIdx = BestIdx;
RandomActions[RandIdx].Action.ActionType = ActionType;
RandomActions[RandIdx].Action.SetObjOwner(GetObjOwner());
SwapAction(RandomActions[RandIdx].Action) ;
RandomActions[RandIdx].Action.ExecuteAction(ActionInstigator);
}
function SwapAction(KF_ObjectiveAction SwapAction)
{
switch(ActionType)
{
case 0 : GetObjOwner().FailureAction = SwapAction; break;
case 1 : GetObjOwner().SuccessAction = SwapAction; break;
}
}
defaultproperties
{
PriorityBias = 0.5
}

View file

@ -0,0 +1,31 @@
/*
--------------------------------------------------------------
Action_ResetCurrentObjective
--------------------------------------------------------------
This Action resets the currently active objective to the state it was in when it was first activated.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjAction_ResetCurrentObjective extends KF_ObjectiveAction
editinlinenew
HideCategories(KF_ObjectiveAction);
function ExecuteAction(Controller ActionInstigator)
{
Super.ExecuteAction(ActionInstigator);
if(GetObjOwner() != none)
{
GetObjOwner().StoryGI.CurrentObjective.Reset();
GetObjOwner().StoryGI.CurrentObjective.Notify_ConditionsActivated(ActionInstigator.Pawn);
}
}
DefaultProperties
{
}

View file

@ -0,0 +1,26 @@
/*
--------------------------------------------------------------
Action_RestartFromCheckPoint
--------------------------------------------------------------
This Action forces players to respawn from the last activated checkpoint
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjAction_RestartFromCheckPoint extends KF_ObjectiveAction
editinlinenew
HideCategories(KF_ObjectiveAction);
function ExecuteAction(Controller ActionInstigator)
{
Super.ExecuteAction(ActionInstigator);
GetObjOwner().StoryGI.RestartEveryone() ;
}
DefaultProperties
{
}

View file

@ -0,0 +1,26 @@
/*
--------------------------------------------------------------
Action_WinGame
--------------------------------------------------------------
This Action ends the match in victory, when Activated.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjAction_WinGame extends KF_ObjectiveAction
editinlinenew
HideCategories(KF_ObjectiveAction);
function ExecuteAction(Controller ActionInstigator)
{
Super.ExecuteAction(ActionInstigator);
GetObjOwner().StoryGI.EndGame(ActionInstigator.PlayerReplicationInfo,"WinAction");
}
DefaultProperties
{
}

View file

@ -0,0 +1,234 @@
/*
--------------------------------------------------------------
Condition_ActorHealth
--------------------------------------------------------------
A Condition which tracks the health state of specified Actor(s)
and is marked complete when it drops below a specified threshold.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_ActorHealth extends KF_ObjectiveCondition
editinlinenew;
enum EHealthMethod
{
Health_Empty,
Health_Full,
};
var () edfindable private Actor TargetActor;
var bool bAcquiredPawn;
/* if we are defending a pawn, this is the minimum health that pawn can drop to before we fail */
var () float MinHealthPct;
/* tag for actors that are spawned at runtime which we must also defend */
var () Name TargetPawnTag;
var () EHealthMethod HealthCondition;
var float CurrentHealth,HealthMax;
var const name InitialHealthActorName,HealthActorName;
function PostBeginPlay(KF_StoryObjective MyOwner)
{
Super.PostBeginPlay(MyOwner);
// Ditch any reference to this actor ASAP.
if(TargetActor != none)
{
SetTargetActor(HealthActorName,TargetActor);
SetTargetActor(InitialHealthActorName,TargetActor);
TargetActor = none;
}
}
function Reset()
{
Super.Reset();
CurrentHealth = 0;
HealthMax = 0;
bAcquiredPawn = false;
}
function ConditionActivated(pawn ActivatingPlayer)
{
local Actor InitialActor;
Super.ConditionActivated(ActivatingPlayer);
InitialActor = GetTargetActor(InitialHealthActorName) ;
if(InitialActor != none &&
!InitialActor.bPendingDelete)
{
SetTargetActor(HealthActorName,InitialActor);
}
}
function bool ConditionIsRelevant()
{
local Actor MyTargetActor;
MyTargetActor = GetTargetActor(HealthActorName);
if(MyTargetActor != none &&
KF_StoryNPC(MyTargetActor) != none)
{
return KF_StoryNPC(MyTargetActor).bActive ;
}
return true;
}
function ConditionTick(float DeltaTime)
{
local KFDoorMover DoorActor;
local Pawn PawnActor;
local Actor MyTargetActor;
UpdatePawnList();
MyTargetActor = GetTargetActor(HealthActorName);
if(MyTargetActor != none &&
!MyTargetActor.bDeleteMe &&
!MyTargetActor.bPendingDelete)
{
PawnActor = Pawn(MyTargetActor);
DoorActor = KFDoorMover(MyTargetActor);
if(DoorActor != none && DoorActor.MyTrigger != none )
{
HealthMax = DoorActor.MyTrigger.MaxWeldStrength ;
CurrentHealth = DoorActor.MyTrigger.WeldStrength ;
}
else
if(PawnActor != none)
{
HealthMax = PawnActor.HealthMax;
CurrentHealth = PawnActor.Health;
}
else
{
HealthMax = 100.f;
CurrentHealth = float(MyTargetActor != none && !MyTargetActor.bHidden && !MyTargetActor.bPendingDelete) * 100.f ;
}
}
else
{
CurrentHealth = 0;
}
Super.ConditionTick(DeltaTime);
}
function UpdatePawnList()
{
local Controller C;
local Actor MyTargetActor;
MyTargetActor = GetTargetActor(HealthActorName);
if(bAcquiredPawn ||
TargetpawnTag == '' ||
(MyTargetActor != none &&
MyTargetActor.Tag == TargetPawnTag))
{
return;
}
for ( C= GetObjOwner().Level.ControllerList; C!=None; C=C.NextController )
{
if(C.Pawn != none &&
!C.Pawn.bDeleteMe &&
!C.Pawn.bPendingDelete &&
C.Pawn.Health > 0 &&
C.Pawn.Tag == TargetpawnTag)
{
SetTargetActor(HealthActorName,C.Pawn);
bAcquiredPawn = true;
break;
}
}
}
function vector GetLocation(optional out Actor LocActor)
{
local Actor MyTargetActor;
local Actor HUDWorldLocActor;
if(ConditionIsActive())
{
MyTargetActor = GetTargetActor(HealthActorName);
if(MyTargetActor != none &&
!MyTargetActor.bPendingDelete)
{
LocActor = MyTargetActor;
return MyTargetActor.Location ;
}
HUDWorldLocActor = GetTargetActor(WorldLocActorName);
if( HUDWorldLocActor != none)
{
LocActor = HUDWorldLocActor ;
return HUDWorldLocActor.Location ;
}
}
return vect(0,0,0); // force not to display.
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
local float Pct;
if(HealthMax == 0)
{
return 0.f;
}
if(HealthCondition == Health_Empty)
{
Pct = 1.f - FClamp(CurrentHealth / HealthMax,0.f,1.f) ;
}
else
{
Pct = FClamp(CurrentHealth / HealthMax,0.f,1.f) ;
}
return Pct;
}
function string GetDataString()
{
if(HUD_Screen.Screen_CountStyle == 1)
{
return Int(Round((1.f-GetCompletionPct())*100))$"%" ;
}
else
{
return Int(Round((GetCompletionPct())*100))$"%" ;
}
}
DefaultProperties
{
InitialHealthActorName = "InitialHealthActor"
HealthActorName = "HealthActor"
}

View file

@ -0,0 +1,327 @@
/*
--------------------------------------------------------------
Condition_Area
--------------------------------------------------------------
A Condition which is marked complete when a player either
(A) leaves a volume or (B) enters a volume
Can also be configured to check ZoneInfo regions.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Area extends KF_ObjectiveCondition
editinlinenew;
var () private edfindable Volume AreaVolume;
var const Name AreaVolumeName,InitialAreaVolumeName;
var () string AreaZoneName;
var private Volume InitialVolume;
var () float Duration;
var () bool bRequiresWholeTeam;
var private ZoneInfo AssociatedZone; // Zones are NoDelete so having a reference in here should be O.K
enum EAreaConditionType
{
Method_LeaveArea,
Method_EnterArea,
};
var ()class<Actor> ProximityTriggerType;
var () name ProximityTag;
var ()EAreaConditionType CompletionMethod;
var () bool bKeepProgress;
var float LastInAreaTime;
var float TimeInArea;
var float TimeOutOfArea;
var float LastOutOfAreaTime;
var bool bTimingOut;
var int NumInVolume;
var private array<name> PawnInstigatorNames;
function PostBeginPlay(KF_StoryObjective MyOwner)
{
local ZoneInfo Zone;
Super.PostBeginPlay(MyOwner);
if(AreaVolume != none)
{
SetTargetActor(InitialAreaVolumeName,AreaVolume);
SetTargetActor(AreaVolumeName,AreaVolume);
AreaVolume = none;
}
else if(AreaZoneName != "")
{
foreach AllObjects(class 'ZoneInfo', Zone)
{
if(Zone.LocationName == AreaZoneName)
{
AssociatedZone = Zone;
break;
}
}
}
}
function array<Pawn> GetInstigatorList()
{
local array<Pawn> PawnInstigators;
local int i;
for(i = 0 ; i < PawnInstigatorNames.length ; i ++)
{
PawnInstigators[PawnInstigators.length] = Pawn(GetTargetActor(PawnInstigatorNames[i])) ;
}
return PawnInstigators;
}
function AdjustToDifficulty(float Difficulty)
{
Duration /= FMax(Difficulty-2,1);
}
function Reset()
{
Super.Reset();
bTimingOut = false;
TimeInArea = 0.f;
TimeOutOfArea = 0.f;
}
function ConditionActivated(pawn ActivatingPlayer)
{
Super.ConditionActivated(ActivatingPlayer);
Duration = FMax(Duration,0.1f);
if(GetTargetActor(InitialAreaVolumeName) != none)
{
SetTargetActor(AreaVolumeName,GetTargetActor(InitialAreaVolumeName));
}
}
function ConditionTick(Float DeltaTime)
{
local controller AController;
local Actor ProximityActor;
local Volume MyAreaVolume;
local int i;
NumInVolume = 0;
for(i = 0 ; i < PawnInstigatorNames.length ; i ++)
{
ReleaseTargetActor(PawnInstigatorNames[i]);
}
PawnInstigatorNames.length = 0;
MyAreaVolume = Volume(GetTargetActor(AreaVolumeName));
if(MyAreaVolume != none || AreaZoneName != "" )
{
if(MyAreaVolume != none)
{
if(ClassIsChildOf(ProximityTriggerType,class 'Controller'))
{
for ( AController=GetObjOwner().Level.ControllerList; AController!=None; AController=AController.NextController )
{
if(AController.Pawn != none &&
AController.Pawn.Health > 0 &&
MyAreaVolume.Encompasses(AController.Pawn))
{
if(ClassIsChildOf(AController.class,ProximityTriggerType))
{
PawnInstigatorNames[PawnInstigatorNames.length] = AController.Pawn.name;
SetTargetActor(AController.Pawn.name,AController.Pawn);
NumInVolume ++ ;
}
}
}
}
else
{
foreach MyAreaVolume.TouchingActors(class 'Actor', ProximityActor)
{
if(ProximityActor.IsA(ProximityTriggerType.name) &&
!ProximityActor.bPendingDelete && (ProximityTag == '' ||
ProximityActor.Tag == ProximityTag))
{
if(ProximityActor.Instigator != none)
{
PawnInstigatorNames[PawnInstigatorNames.length] = ProximityActor.Instigator.name;
SetTargetActor(ProximityActor.Instigator.name,ProximityActor.Instigator);
}
NumInVolume ++ ;
}
}
}
}
else if(AreaZoneName != "")
{
for ( AController=GetObjOwner().Level.ControllerList; AController!=None; AController=AController.NextController )
{
if(AController.Pawn != none &&
AController.Pawn.Health > 0 &&
(ClassIsChildOf(AController.Pawn.Class,ProximityTriggerType) ||
ClassIsChildOf(AController.Class,ProximityTriggerType)) &&
(ProximityTag == '' || AController.Pawn.Tag == ProximityTag))
{
if(AController.Pawn.Region.Zone.LocationName == AreaZoneName)
{
PawnInstigatorNames[PawnInstigatorNames.length] = AController.Pawn.name;
SetTargetActor(AController.Pawn.name,AController.Pawn);
NumInVolume ++ ;
}
}
}
}
if(NumInVolume == 0 ||
(bRequiresWholeTeam && NumInVolume < GetObjOwner().StoryGI.GetTotalActivePlayers()))
{
bTimingOut = true;
if(bKeepProgress)
{
TimeInArea = FMax( TimeInArea - DeltaTime, 0 );
}
else
{
TimeInArea = 0.f;
}
TimeOutOfArea = FMin( TimeOutOfArea + DeltaTime, Duration );
}
else
{
bTimingOut = false;
TimeInArea = FMin( TimeInArea + DeltaTime, Duration );
if(bKeepProgress)
{
TimeOutOfArea = FMax( TimeOutOfArea - DeltaTime, 0 );
}
else
{
TimeOutOfArea = 0.f;
}
}
}
Super.ConditionTick(DeltaTime);
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
if(CompletionMethod == Method_LeaveArea)
{
return FClamp(TimeOutOfArea / Duration,0.f,1.f);
}
else
{
return FClamp(TimeInArea / Duration,0.f,1.f);
}
}
function string GetDataString()
{
local string DataString;
local int NumActivePlayers;
if(bRequiresWholeTeam )
{
NumActivePlayers = GetObjOwner().StoryGI.GetTotalActivePlayers() ;
if(NumActivePlayers > 1)
{
DataString@="["$NumInVolume$"/"$NumActivePlayers$"]" ;
}
}
return DataString ;
}
function string GetHUDHint()
{
local string HintString;
return Super.GetHUDHint();
if( (CompletionMethod == Method_LeaveArea && bTimingOut) ||
(CompletionMethod == Method_EnterArea && !bTimingOut) )
{
HintString = Super.GetHUDHint();
}
return HintString ;
}
function vector GetLocation(optional out Actor LocActor)
{
local Actor WorldLocActor;
local Vector WorldLocation;
local Volume MyAreaVolume;
if(ConditionIsActive())
{
MyAreaVolume = Volume(GetTargetActor(AreaVolumeName));
WorldLocation = Super.GetLocation(WorldLocActor);
if(WorldLocActor != none)
{
LocActor = WorldLocActor;
return WorldLocation;
}
if(MyAreaVolume != none)
{
LocActor = MyAreaVolume;
return LocActor.Location;
}
else
if(AreaZoneName != "" && AssociatedZone != none)
{
LocActor = AssociatedZone;
return LocActor.Location;
}
}
}
DefaultProperties
{
AreaVolumeName = "AreaVolume"
InitialAreaVolumeName = "InitialAreaVolume"
CompletionMethod = Method_EnterArea
ProximityTriggerType = class 'PlayerController'
HUD_World=(bIgnoreWorldLocHidden=true)
}

View file

@ -0,0 +1,123 @@
/*
--------------------------------------------------------------
Condition_Counter
--------------------------------------------------------------
A Condition which increments each time its owning Objective is
triggered.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Counter extends KF_ObjectiveCondition
editinlinenew;
enum ECounterType
{
CT_Default, // default setting - NumToCount is user defined and NumCounted represents the number of times this objective has been triggered .
CT_Cash, // NumCounted becomes the total cash sum accrued by all players. NumToCount is whatever sum of cash you expect them to accumulate.
CT_PlayerCount, // NumCounted becomes the number of unique players who triggered this Objective. NumToCount is the total number of active players in the match.
};
var () ECounterType CountType;
var () int NumToCount;
var int NumCounted,SavedNumCounted;
/* if SuccessCondition is set to OBJ_Counter and CountType is CT_PlayerCount, this array will store unique player IDs for each
played who has been 'counted' so far. */
var array<int> CountedPlayerIDs;
function SaveState()
{
Super.SaveState();
SavedNumCounted = NumCounted;
}
function Reset()
{
Super.Reset();
NumCounted = SavedNumCounted;
CountedPlayerIDs.length = 0;
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
local float Numerator;
local float Denominator;
switch(CountType)
{
case CT_Default : break;
case CT_Cash : NumCounted = GetObjOwner().StoryGI.GetTotalCashSum(); break;
case CT_PlayerCount : NumToCount = Max(GetObjOwner().StoryGI.GetTotalActivePlayers(),1); break;
}
Numerator = float(NumCounted);
// Take the floor of the modified amount to ensure accuracy. Also, there's no floor function available.
Denominator = float(int(float(NumToCount) * GetTotalDifficultyModifier()));
return FClamp(Numerator/Denominator,0.f,1.f) ;
}
function Trigger( actor Other, pawn EventInstigator)
{
Super.Trigger(Other,EventInstigator);
/* each trigger increments the 'counter' for objectives of that type */
if(ConditionIsActive() && ValidForCounting(EventInstigator))
{
SetTargetActor(InstigatorName,EventInstigator);
NumCounted = Min(NumCounted + 1,Round(NumToCount * GetTotalDifficultyModifier())) ;
}
}
/* Relevant if this Objective is configured to count players -
returns true if this is the first time the supplied pawn has triggered us
*/
function bool ValidForCounting(Pawn CountedPawn)
{
local int i;
if(CountType == CT_PlayerCount &&
CountedPawn.PlayerReplicationInfo != none)
{
for( i = 0 ; i < CountedPlayerIDs.length ; i ++)
{
/* this guy has already been counted. ignore him */
if(CountedPlayerIDs[i] == CountedPawn.PlayerReplicationInfo.PlayerID)
{
return false;
}
}
/* If we got this far CountedPawn is unique - add his ID to the array */
CountedPlayerIDs[CountedPlayerIDs.length] = CountedPawn.PlayerReplicationInfo.PlayerID ;
}
return true;
}
function string GetDataString()
{
if(HUD_Screen.Screen_CountStyle < 1)
{
return NumCounted$"/"$int(NumToCount * GetTotalDifficultyModifier()) ;
}
return string(Max((int(NumToCount * GetTotalDifficultyModifier()))-NumCounted,0)) ;
}
DefaultProperties
{
NumToCount = 1
}

View file

@ -0,0 +1,68 @@
/*
--------------------------------------------------------------
Condition_Inventory
--------------------------------------------------------------
A Condition which is marked complete when a player is carrying
an inventory item of the specified class.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Inventory extends KF_ObjectiveCondition
editinlinenew;
/* The type of inventory item to search for */
var() class<Inventory> DesiredItemClass;
var bool bHeld;
var() name DesiredItemTag;
function Reset()
{
Super.Reset();
bHeld = false;
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
return float(bHeld && AllowCompletion());
}
function ConditionTick(float DeltaTime)
{
local Controller C;
local Inventory Inv;
Super.ConditionTick(DeltaTime);
bHeld = false;
for ( C=GetObjOwner().Level.ControllerList; C!=None; C=C.NextController )
{
if(PlayerController(C) != none && C.Pawn != none)
{
for( Inv = C.Pawn.Inventory; Inv != None ; Inv = Inv.Inventory )
{
if(ClassIsChildOf(Inv.class,DesiredItemClass) && (DesiredItemTag == '' ||
Inv.tag == DesiredItemTag))
{
bHeld = true;
SetTargetActor(InstigatorName,C.Pawn);
break;
}
}
}
}
}
defaultproperties
{
HUD_Screen=(Screen_ProgressStyle=HDS_TextOnly)
}

View file

@ -0,0 +1,68 @@
/*
--------------------------------------------------------------
Condition_LineOfSight
--------------------------------------------------------------
A Condition which is marked complete when a living human player
looks at / has an unobstructed line of sight to it.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_LineOfSight extends KF_ObjectiveCondition
editinlinenew;
var () float MinDotProduct;
var () float MinDistance;
var () bool bPerformLineCheck;
var bool bHasLOS;
function Reset()
{
Super.Reset();
bHasLOS = false;
}
function ConditionTick(float DeltaTime)
{
local Controller C;
local float Dist;
for ( C=GetObjOwner().Level.ControllerList; C!=None; C=C.NextController )
{
if(KFPlayerController_Story(C) != none && C.Pawn != none)
{
if(KFPlayerController_Story(C).IsLookingAtLocation(GetLocation(),MinDotProduct) && C.Pawn != none)
{
Dist = VSize(C.Pawn.Location - GetLocation());
if( Dist <= MinDistance)
{
bHasLOS = (!bPerformLineCheck || GetObjOwner().FastTrace(PlayerController(C).CalcViewLocation, GetLocation())); //EyePostion() is returning a funny value sometimes. Not sure why. C.Pawn./*EyePosition()*/Location
if(bHasLOS)
{
SetTargetActor(InstigatorName,C.Pawn);
break;
}
}
}
}
}
Super.ConditionTick(DeltaTime);
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
return float(bHasLOS) ;
}
DefaultProperties
{
bPerformLineCheck=true
MinDistance = 1000
}

View file

@ -0,0 +1,85 @@
/*
--------------------------------------------------------------
Condition_Multi
--------------------------------------------------------------
Multi Conditions are marked complete only when all 'child'
conditions are also Complete
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Multi extends KF_ObjectiveCondition
editinlinenew;
var () array<KF_ObjectiveCondition> ChildConditions;
var array<byte> CompleteConditions;
var int NumCompleted,NumConditions;
function ConditionTick(float DeltaTime)
{
local int i;
local array<KF_ObjectiveCondition> ValidConditions;
NumConditions = 0;
for(i = 0 ; i < ChildConditions.length ; i ++)
{
if(ChildConditions[i].ConditionIsRelevant())
{
ValidConditions[ValidConditions.length] = ChildConditions[i];
}
}
NumConditions = ValidConditions.length;
CompleteConditions.length = ValidConditions.length;
for(i = 0 ; i < ValidConditions.length ; i ++)
{
if(ValidConditions[i].bComplete)
{
CompleteConditions[i] = 1 ;
}
else
{
CompleteConditions[i] = 0;
}
}
NumCompleted = 0;
for(i = 0 ; i < NumConditions ; i ++)
{
if(CompleteConditions[i] == 1)
{
NumCompleted ++ ;
}
}
Super.ConditionTick(DeltaTime);
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
return float(NumCompleted) / float(NumConditions) ;
}
function string GetDataString()
{
if(HUD_Screen.Screen_CountStyle < 1)
{
return NumCompleted$"/"$NumConditions ;
}
return string(Max(NumConditions-NumCompleted,0)) ;
}
DefaultProperties
{
bForceReliableUpdate=true
}

View file

@ -0,0 +1,109 @@
/*
--------------------------------------------------------------
Condition_Random
--------------------------------------------------------------
This Condition serves as a container for any number of other Conditions.
when initalized, it picks one at random and activates it.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Random extends KF_ObjectiveCondition
hidecategories(Difficulty,HUD,Events,KF_ObjectiveCondition)
editinlinenew;
var int RandIdx;
struct SRandomCondition
{
var () editinlineuse KF_ObjectiveCondition Condition;
var () int Priority;
};
var int ConditionIndex;
var (Conditions) float PriorityBias;
var (Conditions) editinlineuse array<SRandomCondition> RandomConditions;
function ConditionActivated(pawn ActivatingPlayer)
{
Super.ConditionActivated(ActivatingPlayer);
ConditionIndex = GetObjOwner().FindIndexForCondition(self);
AssignRandomCondition();
}
function Reset()
{
super.Reset();
SwapConditionAtIndex(ConditionType,self);
}
function AssignRandomCondition()
{
local float HighestRating;
local float PriorityVal;
local int BestIdx;
local array<float> Ratings;
local int i,idx;
HighestRating = -1;
for(i = 0 ; i < RandomConditions.length ; i ++)
{
if(!RandomConditions[i].Condition.bComplete)
{
PriorityVal = RandomConditions[i].Priority ;
Ratings[i] = RandRange( FMin(Ratings[idx] * PriorityBias,Ratings[idx]) , PriorityVal ) ;
}
}
for(idx = 0 ; idx < Ratings.length ; idx ++)
{
if(Ratings[idx] > highestRating )
{
BestIdx = idx;
HighestRating = Ratings[idx] ;
}
}
if(BestIdx < 0 || BestIdx >= RandomConditions.length)
{
log("Warning - Cannot find a valid Condition for : "@self@" Random Condition will fail. ",'Story_Debug');
}
RandIdx = BestIdx;
RandomConditions[RandIdx].Condition.ConditionType = ConditionType;
RandomConditions[RandIdx].Condition.SetObjOwner(GetObjOwner());
if(RandomConditions[RandIdx].Condition.ShouldInitOnActivation())
{
GetObjOwner().ActivateCondition(RandomConditions[RandIdx].Condition);
}
SwapConditionAtIndex(ConditionIndex,RandomConditions[RandIdx].Condition) ;
}
function SwapConditionAtIndex(int Index, KF_ObjectiveCondition SwapCondition)
{
switch(ConditionType)
{
case 0 : GetObjOwner().FailureConditions[Index] = SwapCondition; break;
case 1 : GetObjOwner().SuccessConditions[Index] = SwapCondition; break;
case 2 : GetObjOwner().OptionalConditions[Index] = SwapCondition; break;
}
}
defaultproperties
{
PriorityBias = 0.5
HUD_Screen=(Screen_ProgressStyle=HDS_TextOnly)
}

View file

@ -0,0 +1,80 @@
/*
--------------------------------------------------------------
Condition_Timed
--------------------------------------------------------------
A Condition which is marked complete after a specified amount of
time expires.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Timed extends KF_ObjectiveCondition
editinlinenew;
var bool bTraderTime;
var () float Duration;
var float RemainingSeconds;
var float StartTime;
function AdjustToDifficulty(float Difficulty)
{
Duration /= FMax(Difficulty-2,1);
}
function Reset()
{
Super.Reset();
StartTime = 0.f;
RemainingSeconds = Duration;
}
function ConditionActivated(pawn ActivatingPlayer)
{
Super.ConditionActivated(ActivatingPlayer);
// log("======================"@self@"was just activated by : "@ActivatingPlayer,'Story_Debug');
RemainingSeconds = Duration ;
StartTime = GetObjOwner().Level.TimeSeconds ;
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
return FClamp(1.f- (RemainingSeconds / Duration),0.f,1.f);
}
function ConditionTick(float DeltaTime)
{
local float NewTimeRemaining;
if(RemainingSeconds > 0)
{
NewTimeRemaining = FMax(Duration - int(GetObjOwner().Level.TimeSeconds - StartTime),0) ;
RemainingSeconds = NewTimeRemaining;
}
Super.ConditionTick(DeltaTime);
}
function string GetDataString()
{
if(HUD_Screen.Screen_CountStyle == Count_Down)
{
return FormatTime(RemainingSeconds) ;
}
else
{
return FormatTime(GetObjOwner().Level.TimeSeconds - StartTime) ;
}
}
DefaultProperties
{
Duration = 60.f
HUD_Screen=(Screen_Hint="Time Left :",Screen_ProgressStyle=HDS_TextOnly,Screen_CountStyle=Count_Down)
}

View file

@ -0,0 +1,29 @@
/*
--------------------------------------------------------------
Condition_Touch
--------------------------------------------------------------
This Condition is marked complete when a player encroaches the
collision cylinder of its owning Objective actor.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Touch extends KF_ObjectiveCondition
hidecategories(Difficulty)
editinlinenew;
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
return float(GetObjOwner().bWasTouched) ;
}
DefaultProperties
{
}

View file

@ -0,0 +1,114 @@
/*
--------------------------------------------------------------
Condition_TraderShop
--------------------------------------------------------------
A Type of timed condition which displays HUD info for Trader shops.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_TraderTime extends ObjCondition_Timed
editinlinenew;
var bool OldShopOpen;
/* Point the whisp at the pathnode closest to the Entrance to the trader shop*/
function vector GetWhispLocation(optional out Actor LocActor)
{
LocActor = GetNearestPathNodeTo(GetAssociatedDoor().Location);
return LocActor.Location;
}
function bool ShouldShowWhispTrailFor(PlayerController C)
{
return Super.ShouldShowWhispTrailFor(C) && (KFPlayerController(C) == none || KFPlayerController(C).bWantsTraderPath) ;
}
/* Retrieves the Trader Door associated with the Currently Active Shop */
function KFTraderDoor GetAssociatedDoor()
{
local int i;
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(GetObjOwner().Level.game.GameReplicationInfo) ;
if(GetObjOwner().StoryGI == none || KFGRI == none || KFGRI.CurrentShop == none)
{
return none;
}
for(i = 0 ; i < GetObjOwner().StoryGI.AllTraderDoors.length ; i ++)
{
if(GetObjOwner().StoryGI.AllTraderDoors[i].Tag == KFGRI.CurrentShop.Event)
{
return GetObjOwner().StoryGI.AllTraderDoors[i];
}
}
return none;
}
/*Center the icon on the ShopVolume */
function vector GetLocation(optional out Actor LocActor)
{
local KFGameReplicationInfo KFGRI;
if(ConditionIsActive())
{
KFGRI = KFGameReplicationInfo(GetObjOwner().Level.game.GameReplicationInfo) ;
if(KFGRI != none && KFGRI.CurrentShop != none)
{
LocActor = KFGRI.CurrentShop;
return KFGRI.CurrentShop.Location ;
}
}
return vect(0,0,0);
}
function ConditionTick(float DeltaTime)
{
Super.ConditionTick(DeltaTime);
UpdateWhispVisibility();
}
function ConditionDeActivated()
{
Super.ConditionDeActivated();
UpdateWhispVisibility();
}
function UpdateWhispVisibility()
{
local bool NewShopOpen;
local KFGameReplicationInfo KFGRI;
KFGRI = KFGameReplicationInfo(GetObjOwner().Level.Game.GameReplicationInfo);
if(KFGRI == none)
{
return;
}
if(KFGRI.CurrentShop != none)
{
NewShopOpen = KFGRI.CurrentShop.bCurrentlyOpen;
// only show a whisp trail when the shop is open for business
HUD_World.bShowWhispTrail = NewShopOpen ;
OldShopOpen = NewShopOpen;
}
}
DefaultProperties
{
bTraderTime = true
HUD_World=(bHide=false,bShowWhispTrail=true,World_Hint="Trader",World_Texture=Texture 'KFStoryGame_Tex.HUD.Trader_Icon_64',bIgnoreWorldLocHidden=true)
}

View file

@ -0,0 +1,28 @@
/*
--------------------------------------------------------------
Condition_Triggered
--------------------------------------------------------------
This Condition is marked complete when its owning objective is
the recipient of a trigger event
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Triggered extends KF_ObjectiveCondition
hidecategories(Difficulty)
editinlinenew;
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
return float(bWasTriggered);
}
defaultproperties
{
HUD_Screen=(Screen_ProgressStyle=HDS_TextOnly)
}

View file

@ -0,0 +1,326 @@
/*
--------------------------------------------------------------
Condition_Use
--------------------------------------------------------------
This Condition is marked complete when a player presses the 'Use'
key while in range of its owning actor.
Author : Alex Quick
--------------------------------------------------------------
*/
class ObjCondition_Use extends KF_ObjectiveCondition
editinlinenew;
/* requires that line of sight be maintained with the objective during the use process. Only really relevant if HoldUseSeconds is something > 0 */
var () bool bMaintainUseLOS;
/* Min View angle cosine required to perform a 'use' action */
var () float MinUseViewAngle;
/* Number of seconds a player must hold the use key for before the objective completes. Releasing the key will abort any progress. */
var () float HoldUseSeconds;
/* if true, any progress made 'Using' this objective will be kept if the player stops holding the use key. only relevant if HoldUseSeconds is > 0 */
var () bool bKeepUseProgress;
var () name UsePawn_Tag;
var float FinishedUseSeconds;
var float LastUseTime;
var bool bAcquiredPawn;
var bool bWasUsed;
var const name UseActorName,InitialUseActorName,CurrentUserName,ControlledMoverName;
var () edfindable private Actor UseActor; // this exists only for level designers.
function Reset()
{
Super.Reset();
FinishedUseSeconds = 0.f;
bWasUsed = false;
}
function PostBeginPlay(KF_StoryObjective MyOwner)
{
Super.PostBeginPlay(MyOwner);
// Ditch any reference to this actor ASAP.
if(UseActor != none)
{
SetTargetActor(InitialUseActorName,UseActor);
SetTargetActor(UseActorName,UseActor);
UseActor = none;
}
}
function SetObjOwner(KF_StoryObjective NewOwner)
{
local Actor MyUseActor;
MyUseActor = GetTargetActor(UseActorName);
if(MyUseActor == none || (NewOwner != MyUseActor && GetObjOwner() == MyUseActor))
{
SetTargetActor(UseActorName,NewOwner);
}
Super.SetObjOwner(NewOwner);
}
function ConditionActivated(pawn ActivatingPlayer)
{
local Actor InitialUseActor,MyUseActor;
local KF_UseableMover ControlledMover;
Super.ConditionActivated(ActivatingPlayer);
InitialUseActor = GetTargetActor(InitialUseActorName);
MyUseActor = GetTargetActor(UseActorName);
if(InitialuseActor != none &&
!InitialuseActor.bPendingDelete)
{
SetTargetActor('UseActorName',InitialUseActor);
if(KF_UseableMover(MyUseActor) != none)
{
SetTargetActor(ControlledMoverName,KF_UseableMover(MyUseActor));
ControlledMover = KF_UseableMover(GetTargetActor(ControlledMoverName));
if(ControlledMover != none)
{
ControlledMover.Notify_Controlled(self);
}
}
}
}
function ConditionTick(float DeltaTime)
{
local float RemainingHoldUseTime;
local Actor MyUseActor;
local Pawn CurrentUser;
UpdateUseablePawnList();
MyUseActor = GetTargetActor(UseActorName);
CurrentUser = Pawn(GetTargetActor(CurrentUserName));
/* Important we call this before the Range checks or it won't register completion */
Super.ConditionTick(DeltaTime);
if(CurrentUser != none && CurrentUser.Controller != none)
{
RemainingHoldUseTime = GetRemainingUseTime() ;
if(!InRangeAndView() || RemainingHoldUseTime <= 0)
{
StopUsingObj(CurrentUser);
}
}
}
function UpdateUseablePawnList()
{
local Controller C;
local Actor MyUseActor;
MyUseActor = GetTargetActor(UseActorName);
if(UsePawn_Tag == '' ||
(MyUseActor != none &&
MyUseActor.Tag == UsePawn_Tag))
{
return;
}
for ( C= GetObjOwner().Level.ControllerList; C!=None; C=C.NextController )
{
if(C.Pawn != none &&
!C.Pawn.bDeleteMe &&
!C.Pawn.bPendingDelete &&
C.Pawn.Health > 0 &&
C.Pawn.Tag == UsePawn_Tag)
{
SetTargetActor(UseActorName,C.Pawn);
break;
}
}
}
function bool InRangeAndView()
{
local bool bhasLOS;
local bool bInRange;
local Pawn CurrentUser;
CurrentUser = Pawn(GetTargetActor(CurrentUserName));
if(CurrentUser == none)
{
return false;
}
bHasLOS = (!bMaintainUseLOS ||
KFPlayerController_Story(CurrentUser.Controller) != none &&
KFPlayerController_Story(CurrentUser.Controller).IsLookingAtLocation(GetLocation(),MinUseViewAngle));
bInRange = IsTouchingUseActor(CurrentUser) ;
return bInRange && bHasLOS;
}
function bool IsTouchingUseActor(pawn Toucher)
{
local Actor A;
local float DistSq;
local Actor MyUseActor;
MyUseActor = GetTargetActor(UseActorName);
if(Toucher != none && MyUseActor != none)
{
foreach Toucher.TouchingActors(class 'Actor', A)
{
if(A == MyUseActor)
{
return true;
}
}
if(MyUseActor.bBlockActors)
{
DistSq = VsizeSquared(MyUseActor.Location - Toucher.Location) ;
if(DistSq <= Square( (MyUseActor.CollisionRadius + (Toucher.CollisionRadius)) * 1.25 ) )
{
return true;
}
}
}
return false;
}
function Startedusing(pawn User)
{
local Pawn CurrentUser;
local KF_UseableMover ControlledMover;
ControlledMover = KF_UseableMover(GetTargetActor(ControlledMoverName));
CurrentUser = Pawn(GetTargetActor(CurrentUserName));
if(CurrentUser == none && IsTouchingUseActor(User))
{
SetTargetActor(InstigatorName,User);
SetTargetActor(CurrentUserName,User);
LastUseTime = User.Level.TimeSeconds;
if(AllowCompletion())
{
bWasUsed = true;
}
if(ControlledMover != none)
{
ControlledMover.StartedUsing();
}
}
}
function StoppedUsing(pawn User)
{
local KF_UseableMover ControlledMover;
ControlledMover = KF_USeableMover(GetTargetActor(ControlledMoverName));
StopusingObj(User);
if(ControlledMover != none)
{
ControlledMover.StoppedUsing();
}
}
function StopUsingObj(pawn User)
{
local Pawn CurrentUser;
CurrentUser = Pawn(GetTargetActor(CurrentUserName));
if(CurrentUser != none &&
User == CurrentUser)
{
/* Cache the amount of time we have been holding use for */
if( bKeepUseProgress)
{
FinishedUseSeconds += (User.Level.TimeSeconds - LastUseTime) ;
}
SetTargetActor(CurrentUserName,none);
}
}
function vector GetLocation(optional out actor LocActor)
{
local Actor MyUseActor;
if(ConditionIsActive())
{
MyUseActor = GetTargetActor(UseActorName);
if(MyUseActor != none &&
!MyUseActor.bPendingDelete)
{
LocActor = MyUseActor;
return MyUseActor.Location;
}
return Super.GetLocation(LocActor);
}
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
if(HoldUseSeconds > 0)
{
return 1.f - (GetRemainingUseTime() / HoldUseSeconds);
}
else
{
return float(bWasUsed);
}
}
/* Wrapper for finding the amount of time a player needs to hold down the USE key to complete this condition */
function float GetRemainingUseTime()
{
local Pawn CurrentUser;
CurrentUser = Pawn(GetTargetActor(CurrentUserName));
if(CurrentUser != none)
{
return FMax(HoldUseSeconds - ((GetObjOwner().Level.TimeSeconds - LastUseTime) + FinishedUseSeconds), 0.f) ;
}
return (HoldUseSeconds - FinishedUseSeconds);
}
function string GetDataString()
{
if(HoldUseSeconds > 0)
{
return Round((1.f-(GetRemainingUseTime() / HoldUseSeconds))*100.f)$"%" ;
}
return "" ;
}
DefaultProperties
{
UseActorName = "UseActor"
InitialUseActorName = "InitialUseActor"
CurrentUserName = "CurrentUser"
ControlledMoverName = "ControlledMover"
}

View file

@ -0,0 +1,145 @@
//-----------------------------------------------------------
//
//-----------------------------------------------------------
class ObjCondition_WaveCounter extends ObjCondition_Counter
hidecategories(ObjCondition_Counter);
var const name WaveDesignerName;
var int LastWaveIdx;
/* Tag of the WaveDesigner tied to this wave counter */
var() name DesignerTag;
var() bool bUseCurrentWave;
var() int AssociatedWaveIndex;
var() int AssociatedCycleIndex;
var() bool bSumOfAllCycles;
var int NumStragglers;
function ConditionActivated(pawn ActivatingPlayer)
{
local KF_StoryWaveDesigner WaveDesigner;
NumCounted = 0;
Super.ConditionActivated(ActivatingPlayer);
foreach GetObjOwner().AllActors(class 'KF_StoryWaveDesigner' , WaveDesigner,DesignerTag)
{
SetTargetActor(WaveDesignerName,WaveDesigner);
break;
}
}
function ConditionDeActivated()
{
super.ConditionDeActivated();
// let's actually let the objective control this stuff manually ...
// WaveDesigner.Waves[GetAssociatedWaveIndex()].WaveController.AbortWave();
}
/* returns the percentage of completion for this condition */
function float GetCompletionPct()
{
local KF_StoryWaveDesigner WaveDesigner;
WaveDesigner = KF_StoryWaveDesigner(GetTargetActor(WaveDesignername));
if(WaveDesigner == none)
{
// log("WARNING - no Wave Designer associated with : "@name,'Story_Debug');
return 0.f;
}
if(WaveDesigner.Waves[GetAssociatedWaveIndex()].WaveController.bActive)
{
if(bSumOfAllCycles)
{
NumToCount = WaveDesigner.Waves[GetAssociatedWaveIndex()].WaveController.GetMaxMonsters();
}
else
{
NumToCount = WaveDesigner.Waves[GetAssociatedWaveIndex()].WaveController.GetCycleMaxZEDs(AssociatedCycleIndex) ;
}
}
else
{
NumToCount = WaveDesigner.Waves[GetAssociatedWaveIndex()].WaveController.NumStragglers;
NumCounted = NumToCount - GetObjOwner().StoryGI.NumMonsters;
}
/* NO ZEDS . Complete automatically */
if(NumToCount <= 0)
{
return 1.f;
}
return Super.GetCompletionPct();
}
function int GetAssociatedWaveIndex()
{
local int WaveIdx;
if(bUseCurrentWave)
{
WaveIdx = KF_StoryWaveDesigner(GetTargetActor(WaveDesignername)).CurrentWaveIdx ;
}
else
{
WaveIdx = AssociatedWaveIndex;
}
return WaveIdx;
}
/* We need to ensure that the counter is reset between waves */
function ConditionTick(float DeltaTime)
{
local int NewWaveIdx;
local KF_StoryWaveDesigner WaveDesigner;
Super.ConditionTick(DeltaTime);
WaveDesigner = KF_StoryWaveDesigner(GetTargetActor(WaveDesignername));
if(WaveDesigner != none)
{
NewWaveIdx = GetAssociatedWaveIndex();
if(NewWaveIdx != LastWaveIdx)
{
OnWaveChange();
}
LastWaveIdx = NewWaveIdx;
}
}
function OnWaveChange()
{
NumCounted = 0;
}
function Trigger( actor Other, pawn EventInstigator)
{
Super.Trigger(Other,EventInstigator);
// log("You have killed : "@NumCounted@"ZEDS out of : "@NumToCount,'Story_Debug');
}
/* Difficulty scaling for enemies is handled in the Wave Controller, so don't do it twice. */
function float GetTotalDifficultyModifier()
{
return 1.f;
}
DefaultProperties
{
WaveDesignerName = "WaveDesigner"
CountType = CT_Default
bUseCurrentWave = true
bSumOfAllCycles = true
}

View file

@ -0,0 +1,53 @@
class Objective_Whisp extends RedWhisp;
var transient vector DestLoc;
function PostBeginPlay()
{
super(xEmitter).PostBeginPlay();
InitWhisp();
}
function InitWhisp()
{
local int i,start;
local PlayerController C;
local Actor HitActor;
local Vector HitLocation,HitNormal;
C = PlayerController(Owner);
if ( C.Pawn == None )
return;
SetLocation(C.Pawn.Location);
WayPoints[0] = C.Pawn.Location + 200 * vector(C.Rotation);
HitActor = Trace(HitLocation, HitNormal,WayPoints[0], C.Pawn.Location,false);
if ( HitActor != None )
WayPoints[0] = HitLocation;
NumPoints++;
if ( (C.RouteCache[i] != None) && C.RouteCache[1] != none && C.ActorReachable(C.RouteCache[1]) )
start = 1;
for ( i=start; i<start+10; i++ )
{
if ( C.RouteCache[i] == None )
break;
else
{
WayPoints[NumPoints] = C.RouteCache[i].Location;
NumPoints++;
}
}
if( NumPoints < start+10 )
{
WayPoints[NumPoints] = DestLoc;
NumPoints++;
}
Velocity = 500 * Normal(WayPoints[0] - Location) + C.Pawn.Velocity;
}

View file

@ -0,0 +1,131 @@
class PawnWeldFire extends WeldFire;
var array <class<Actor> > ValidWeldTypes;
simulated Function Timer()
{
local Actor HitActor;
local vector StartTrace, EndTrace, HitLocation, HitNormal,AdjustedLocation;
local rotator PointRot;
local int MyDamage;
If( !KFWeapon(Weapon).bNoHit )
{
MyDamage = MeleeDamage + Rand(MaxAdditionalDamage);
if ( KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill != none )
{
MyDamage = float(MyDamage) * KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo).ClientVeteranSkill.Static.GetWeldSpeedModifier(KFPlayerReplicationInfo(Instigator.PlayerReplicationInfo));
}
PointRot = Instigator.GetViewRotation();
StartTrace = Instigator.Location + Instigator.EyePosition();
if( AIController(Instigator.Controller)!=None && Instigator.Controller.Target!=None )
{
EndTrace = StartTrace + vector(PointRot)*weaponRange;
Weapon.bBlockHitPointTraces = false;
HitActor = Trace( HitLocation, HitNormal, EndTrace, StartTrace, true);
Weapon.bBlockHitPointTraces = Weapon.default.bBlockHitPointTraces;
if( HitActor==None )
{
EndTrace = Instigator.Controller.Target.Location;
Weapon.bBlockHitPointTraces = false;
HitActor = Trace( HitLocation, HitNormal, EndTrace, StartTrace, true);
Weapon.bBlockHitPointTraces = Weapon.default.bBlockHitPointTraces;
}
if( HitActor==None )
HitLocation = Instigator.Controller.Target.Location;
HitActor = Instigator.Controller.Target;
}
else
{
EndTrace = StartTrace + vector(PointRot)*weaponRange;
Weapon.bBlockHitPointTraces = false;
HitActor = Trace( HitLocation, HitNormal, EndTrace, StartTrace, true);
Weapon.bBlockHitPointTraces = Weapon.default.bBlockHitPointTraces;
}
LastHitActor = HitActor;
if( HitActor != none && IsValidWeldTarget(HitActor) && Level.NetMode!=NM_Client )
{
AdjustedLocation = Hitlocation;
AdjustedLocation.Z = (Hitlocation.Z - 0.15 * Instigator.collisionheight);
HitActor.TakeDamage(MyDamage, Instigator, HitLocation , vector(PointRot),hitDamageClass);
Spawn(class'KFWelderHitEffect',,, AdjustedLocation, rotator(HitLocation - StartTrace));
}
}
}
function bool IsValidWeldTarget(actor HitActor)
{
local int idx;
/* No Welding of in-active breaker boxes */
if(HitActor.IsA('KF_BreakerBoxNPC') && !KF_BreakerBoxNPC(HitActor).bActive)
{
return false;
}
for(idx = 0 ; idx < ValidWeldTypes.length ; idx ++)
{
if(ClassIsChildOf(HitActor.Class,ValidWeldTypes[idx]))
{
return true;
}
}
return false;
}
function Actor GetWeldTarget()
{
local Actor A;
local vector Dummy,End,Start;
if( AIController(Instigator.Controller)!=None )
Return Pawn(Instigator.Controller.Target);
Start = Instigator.Location+Instigator.EyePosition();
End = Start+vector(Instigator.GetViewRotation())*weaponRange;
Instigator.bBlockHitPointTraces = false;
A = Instigator.Trace(Dummy,Dummy,End,Start,True);
Instigator.bBlockHitPointTraces = Instigator.default.bBlockHitPointTraces;
return A;
}
function bool AllowFire()
{
local Actor WeldTarget;
WeldTarget = GetWeldTarget();
// Can't use welder, if no door.
if ( WeldTarget == none || !IsValidWeldTarget(WeldTarget) )
{
if ( KFPlayerController(Instigator.Controller) != none )
{
KFPlayerController(Instigator.Controller).CheckForHint(54);
if ( FailTime + 0.5 < Level.TimeSeconds )
{
PlayerController(Instigator.Controller).ClientMessage(NoWeldTargetMessage, 'CriticalEvent');
FailTime = Level.TimeSeconds;
}
}
return false;
}
return Weapon.AmmoAmount(ThisModeNum) >= AmmoPerFire ;
}
defaultproperties
{
ValidWeldTypes(0) = class 'KFDoorMover'
ValidWeldTypes(1) = class 'KF_StoryNPC_Static'
}

View file

@ -0,0 +1,34 @@
/*
--------------------------------------------------------------
Pickup_MaintenanceKeyCard
--------------------------------------------------------------
*/
class Pickup_MaintenanceKeyCard extends KF_StoryInventoryPickup
placeable;
defaultproperties
{
DrawScale=1.0
bRenderIconThroughWalls = false
bRender1PMesh = false
CollisionRadius = 25
CollisionHeight = 25
bOrientOnSlope=true
MaxHeldCopies = 1
UV2Texture = FadeColor'PatchTex.Common.PickupOverlay'
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Keycard_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
InventoryType = class 'Inv_MaintenanceKeyCard'
MessageClass = class 'Msg_MaintenanceKeyCardNotification'
StaticMesh=StaticMesh'KF_Swansong_SM.Metro.SM_Keycard'
PrePivot=(X=0.f,Y=0.f,Z=25.f)
}

View file

@ -0,0 +1,31 @@
/*
--------------------------------------------------------------
Pickup_Nitroglycerin
--------------------------------------------------------------
*/
class Pickup_Nitroglycerin extends KF_StoryInventoryPickup;
defaultproperties
{
Drawscale=0.25
StaticMesh = StaticMesh'KF_Swansong_SM.LAB.SM_Nitroglycerin_Bottle'
bRenderIconThroughWalls = false
bRender1PMesh = false
CollisionRadius = 25
CollisionHeight = 25
bOrientOnSlope=false
MaxHeldCopies = 1
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Nitroglycerin_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
InventoryType = class 'Inv_Nitroglycerin'
MessageClass = class 'Msg_NitroglycerinNotification'
PrePivot=(X=0.f,Y=0.f,Z=100)
}

View file

@ -0,0 +1,35 @@
/*
--------------------------------------------------------------
Pickup_PatriarchEyeBall
--------------------------------------------------------------
*/
class Pickup_PatriarchEyeBall extends KF_StoryInventoryPickup;
defaultproperties
{
DrawScale=2.f
bRenderIconThroughWalls = false
bRender1PMesh = false
CollisionRadius = 25
CollisionHeight = 25
bOrientOnSlope=true
MaxHeldCopies = 1
UV2Texture = FadeColor'PatchTex.Common.PickupOverlay'
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Eyeball_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
InventoryType = class 'Inv_PatriarchEyeBall'
MessageClass = class 'Msg_EyeBallNotification'
StaticMesh=StaticMesh 'kf_gore_trip_sm.gibbs.eyeball'
Skins(0)=Texture 'kf_fx_trip_t.Gore.eyeball_diff'
PrePivot=(X=0,Y=0,Z=11.5)
DroppedSound=Sound'KFPawnDamageSound.MeleeDamageSounds.bathitflesh2'
}

View file

@ -0,0 +1,31 @@
/*
--------------------------------------------------------------
Pickup_Thermite
--------------------------------------------------------------
*/
class Pickup_Thermite extends KF_StoryInventoryPickup;
defaultproperties
{
DrawScale = 0.6
StaticMesh = StaticMesh'KF_Swansong_SM.Metro.SM_Thermite'
bRenderIconThroughWalls = false
bRender1PMesh = false
CollisionRadius = 40
CollisionHeight = 10
bOrientOnSlope=false
MaxHeldCopies = 1
PrePivot=(X=0,Y=0,Z=18)
UV2Texture = FadeColor'PatchTex.Common.PickupOverlay'
CarriedMaterial = Texture 'KF_Swansong_Tex.Icons.Thermite_Icon_64'
GroundMaterial = Colormodifier 'FrightYard_T.RemotePickupGroundIco_cm'
InventoryType = class 'Inv_Thermite'
MessageClass = class 'Msg_ThermiteNotification'
}

View file

@ -0,0 +1,48 @@
/*
--------------------------------------------------------------
RespawnTimer
--------------------------------------------------------------
Modified volumeTimer for use with KF_StoryCheckPointVolume actors.
Author : Alex Quick
--------------------------------------------------------------
*/
class RespawnTimer extends info;
var KF_StoryCheckPointVolume CheckPoint;
var float TimerFrequency;
var bool bInitialised;
function PostBeginPlay()
{
super.PostBeginPlay();
CheckPoint = KF_StoryCheckPointVolume(Owner);
SetTimer(1.0, false);
}
function Timer()
{
if(!bInitialised)
{
bInitialised = true;
SetTimer(TimerFrequency, false);
}
else
{
CheckPoint.RespawnTimerPop();
}
}
function Reset()
{
bInitialised = false;
Timer();
}
defaultproperties
{
TimerFrequency=2.000000
}

View file

@ -0,0 +1,61 @@
/*
--------------------------------------------------------------
StaticMeshActor_Hideable
--------------------------------------------------------------
StaticMeshActor which can be toggled on / off
Author : Alex Quick
--------------------------------------------------------------
*/
class StaticMeshActor_Hideable extends StaticMeshActor;
var bool bInitialHidden;
var () bool bNoCollisionWhileHidden;
simulated function PostBeginPlay()
{
bInitialhidden = bHidden;
}
simulated function Trigger( actor Other, pawn EventInstigator )
{
bHidden = !bHidden;
if(bNoCollisionWhileHidden)
{
if(bHidden)
{
SetCollision(false,false);
bBlockZeroExtentTraces = false;
bBlockNonZeroExtentTraces = false;
}
else
{
SetCollision(default.bCollideActors,default.bBlockActors);
bBlockZeroExtentTraces = default.bBlockZeroExtentTraces;
bBlockNonZeroExtentTraces = default.bBlockNonZeroExtentTraces;
}
}
}
simulated function Reset()
{
bHidden = bInitialhidden;
}
defaultproperties
{
bNoCollisionWhileHidden = true
bStatic = false
bAlwaysRelevant = true
bNoDelete = true
RemoteRole=ROLE_SimulatedProxy
}

View file

@ -0,0 +1,58 @@
/*
--------------------------------------------------------------
StoryInventoryAttachment
--------------------------------------------------------------
Third person actor which represents a KF_StoryInventoryItem while
carried by a player.
Author : Alex Quick
--------------------------------------------------------------
*/
class StoryInventoryAttachment extends InventoryAttachment;
var KF_StoryInventoryItem StoryOwner;
function InitFor(Inventory I)
{
Instigator = I.Instigator;
StoryOwner = KF_StoryInventoryItem(I);
if(StoryOwner != none)
{
SetStaticMesh(StoryOwner.PickupSM);
LinkMesh(StoryOwner.Mesh);
AmbientGlow = StoryOwner.AmbientGlow;
SetDrawScale3D(StoryOwner.DrawScale3D);
SetDrawScale(StoryOwner.DrawScale);
SetDrawType(StoryOwner.DrawType);
// Lighting
if(StoryOwner.StoryPickupBase != none)
{
LightType = StoryOwner.StoryPickupBase.LightType;
LightBrightness = StoryOwner.StoryPickupBase.LightBrightness;
LightRadius = StoryOwner.StoryPickupBase.LightRadius;
LightHue = StoryOwner.StoryPickupBase.lighthue;
bUseDynamicLights = StoryOwner.StoryPickupBase.bUseDynamicLights;
LightSaturation = StoryOwner.StoryPickupBase.LightSaturation;
bDynamicLight = StoryOwner.StoryPickupBase.bDynamicLight;
bLightChanged = true;
}
}
}
defaultproperties
{
bActorShadows=true
DrawType = DT_StaticMesh;
bCollideActors=False
bCollideWorld=False
bBlockActors=False
}

Some files were not shown because too many files have changed in this diff Show more