Prepare fixtures
This commit is contained in:
parent
797e5ea192
commit
9c94356263
6021 changed files with 722805 additions and 22 deletions
34
kf_sources/ScrnStoryGame/Classes/ACTION_AddAmmo.uc
Normal file
34
kf_sources/ScrnStoryGame/Classes/ACTION_AddAmmo.uc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// adds ammunition to current weapon
|
||||
|
||||
class ACTION_AddAmmo extends ScriptedAction;
|
||||
|
||||
var(Action) int AmmoToAdd;
|
||||
var(Action) int FireMode;
|
||||
|
||||
enum EAmmoAmount
|
||||
{
|
||||
AMMO_Constant,
|
||||
AMMO_Max,
|
||||
AMMO_SuperMax
|
||||
};
|
||||
|
||||
var(Action) EAmmoAmount AmmoAmount;
|
||||
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
if ( C.Pawn.Weapon != none ) {
|
||||
switch ( AmmoAmount ) {
|
||||
case AMMO_Constant: C.Pawn.Weapon.AddAmmo(AmmoToAdd, FireMode); break;
|
||||
case AMMO_Max: C.Pawn.Weapon.MaxOutAmmo(); break;
|
||||
case AMMO_SuperMax: C.Pawn.Weapon.SuperMaxOutAmmo(); break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="add ammo"
|
||||
}
|
||||
43
kf_sources/ScrnStoryGame/Classes/ACTION_AddShield.uc
Normal file
43
kf_sources/ScrnStoryGame/Classes/ACTION_AddShield.uc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// adds health and shields to possesed Pawn or to matching Tag
|
||||
|
||||
class ACTION_AddShield extends ScriptedAction;
|
||||
|
||||
var(Action) class<Pawn> PawnClass; // leave none to heal possed pawn
|
||||
var(Action) name PawnTag;
|
||||
|
||||
var(Action) int ShieldToAdd;
|
||||
var(Action) int HealthToAdd;
|
||||
var(Action) bool bDelayedHealing; // use GiveHealth() for KFPawns
|
||||
|
||||
|
||||
function ProceedPawn(Pawn P)
|
||||
{
|
||||
if ( HealthToAdd > 0 ) {
|
||||
if ( bDelayedHealing && KFPawn(P) != none )
|
||||
KFPawn(P).GiveHealth(HealthToAdd, P.HealthMax);
|
||||
else
|
||||
P.Health = min(P.HealthMax, P.Health + HealthToAdd);
|
||||
}
|
||||
if ( ShieldToAdd > 0 )
|
||||
P.AddShieldStrength(ShieldToAdd);
|
||||
}
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local Actor A;
|
||||
|
||||
if ( PawnClass == none ) {
|
||||
ProceedPawn(C.Pawn);
|
||||
}
|
||||
else {
|
||||
ForEach C.DynamicActors(PawnClass, A, PawnTag)
|
||||
ProceedPawn(Pawn(A));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="add health and armor"
|
||||
}
|
||||
71
kf_sources/ScrnStoryGame/Classes/ACTION_DamagePawn.uc
Normal file
71
kf_sources/ScrnStoryGame/Classes/ACTION_DamagePawn.uc
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
class ACTION_DamagePawn extends ScriptedAction;
|
||||
|
||||
/**
|
||||
* Who damaged pawn?
|
||||
* DMG_None - nobody (none)
|
||||
* DMG_Instigator - ScriptedController.Instigator.Controller
|
||||
* DMG_Myself - ScriptedController
|
||||
* DMG_Himself - Victim
|
||||
*/
|
||||
enum EDamageInstigator
|
||||
{
|
||||
DMG_None,
|
||||
DMG_Instigator,
|
||||
DMG_Myself,
|
||||
DMG_Himself
|
||||
};
|
||||
|
||||
enum EDamageCalculation
|
||||
{
|
||||
DCALC_Amount, // damage is calculated using MinDamageAmount and MaxDamageAmount
|
||||
DCALC_HealthPct, // damage is calculated using Victim's Health, MinDamagePct and MaxDamagePct
|
||||
DCALC_HealthMaxPct, // damage is calculated using Victim's HealthMax, MinDamagePct and MaxDamagePct
|
||||
};
|
||||
|
||||
var(Action) class<Pawn> VictimClass;
|
||||
var(Action) name VictimTag;
|
||||
var(Action) EDamageInstigator DamageInstigator;
|
||||
var(Action) class<DamageType> DamageType;
|
||||
var(Action) EDamageCalculation DamageCalculation;
|
||||
var(Action) int MinDamageAmount, MaxDamageAmount;
|
||||
var(Action) float MinDamagePct, MaxDamagePct; // used if MinDamageAmount==MaxDamageAmount==0
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local Actor A;
|
||||
local Pawn Victim, Instigator;
|
||||
local int Damage;
|
||||
|
||||
switch ( DamageInstigator ) {
|
||||
case DMG_Instigator: Instigator = C.Instigator; break;
|
||||
case DMG_Myself: Instigator = C.Pawn; break;
|
||||
}
|
||||
|
||||
ForEach C.DynamicActors(VictimClass, A, VictimTag) {
|
||||
Victim = Pawn(A);
|
||||
if ( DamageInstigator == DMG_Himself )
|
||||
Instigator = Victim;
|
||||
|
||||
switch ( DamageCalculation )
|
||||
{
|
||||
case DCALC_Amount: Damage = RandRange(MinDamageAmount, MaxDamageAmount); break;
|
||||
case DCALC_HealthPct: Damage = Victim.Health * ( MinDamagePct + frand()*(MaxDamagePct - MinDamagePct) ); break;
|
||||
case DCALC_HealthMaxPct: Damage = Victim.HealthMax * ( MinDamagePct + frand()*(MaxDamagePct - MinDamagePct) ); break;
|
||||
}
|
||||
|
||||
Victim.TakeDamage(Damage, Instigator, Victim.Location, vect(0,0,0), DamageType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString @ GetItemName(String(VictimClass)) @ VictimTag;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
VictimClass=Class'Old2k4.Monster'
|
||||
ActionString="Damage pawn"
|
||||
}
|
||||
27
kf_sources/ScrnStoryGame/Classes/ACTION_Flashlight.uc
Normal file
27
kf_sources/ScrnStoryGame/Classes/ACTION_Flashlight.uc
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Toggles weapon's flashligh on / off
|
||||
// Current weapon must have flashlight attached (Single, Shotgun etc.)
|
||||
|
||||
class ACTION_Flashlight extends ScriptedAction;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local KFWeapon Weapon;
|
||||
|
||||
if ( (C.Pawn == None) || (C.Pawn.Weapon == None) )
|
||||
return false;
|
||||
|
||||
Weapon = KFWeapon(C.Pawn.Weapon);
|
||||
if ( Weapon == none || !Weapon.bTorchEnabled ) {
|
||||
log(C.Pawn.Tag @ "trying to use flashlight on" @ C.Pawn.Weapon);
|
||||
return false;
|
||||
}
|
||||
|
||||
Weapon.LightFire();
|
||||
Weapon.AdjustLightGraphic();
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="toggle weapon's flashlight"
|
||||
}
|
||||
39
kf_sources/ScrnStoryGame/Classes/ACTION_IfActorCount.uc
Normal file
39
kf_sources/ScrnStoryGame/Classes/ACTION_IfActorCount.uc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Proceeds if given actor count is in defined range
|
||||
class ACTION_IfActorCount extends ScriptedAction;
|
||||
|
||||
var(Action) class<Actor> ActorClass;
|
||||
var(Action) name ActorTag;
|
||||
var(Action) int MinCount;
|
||||
var(Action) int MaxCount;
|
||||
|
||||
|
||||
function ProceedToNextAction(ScriptedController C)
|
||||
{
|
||||
local Actor Other;
|
||||
local int ActorCount;
|
||||
|
||||
C.ActionNum += 1;
|
||||
|
||||
foreach C.DynamicActors(ActorClass, Other, ActorTag)
|
||||
ActorCount++;
|
||||
|
||||
if ( ActorCount < MinCount || (MaxCount != 0 && ActorCount > MaxCount) )
|
||||
ProceedToSectionEnd(C);
|
||||
}
|
||||
|
||||
function bool StartsSection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
if ( MaxCount == 0 )
|
||||
return ActionString $ " > " $ MinCount;
|
||||
return ActionString @ "["$MinCount$".."$MaxCount$"]";
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="If actor count"
|
||||
}
|
||||
23
kf_sources/ScrnStoryGame/Classes/ACTION_IfHasEnemy.uc
Normal file
23
kf_sources/ScrnStoryGame/Classes/ACTION_IfHasEnemy.uc
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Same as ACTION_IfMonsterHasEnemy, but can be used with any Pawn (e.g. NPC)
|
||||
class ACTION_IfHasEnemy extends ScriptedAction;
|
||||
|
||||
var(Action) float MaxDistance;
|
||||
|
||||
function ProceedToNextAction( ScriptedController C )
|
||||
{
|
||||
C.ActionNum += 1;
|
||||
|
||||
if ( C.Enemy != none && (C.Enemy.bDeleteMe || C.Enemy.Health <= 0) )
|
||||
C.Enemy = none;
|
||||
if ( C.Enemy == None || (MaxDistance > 0 && vsize(C.Pawn.Location - C.Enemy.Location) > MaxDistance + C.Enemy.CollisionRadius) )
|
||||
ProceedToSectionEnd( C );
|
||||
}
|
||||
|
||||
function bool StartsSection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
20
kf_sources/ScrnStoryGame/Classes/ACTION_IfIsHurt.uc
Normal file
20
kf_sources/ScrnStoryGame/Classes/ACTION_IfIsHurt.uc
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Same as ACTION_IfMonsterIsHurt, but can be used with any Pawn (e.g. NPC)
|
||||
class ACTION_IfIsHurt extends ScriptedAction;
|
||||
|
||||
var() int HealthThreshold;
|
||||
|
||||
function ProceedToNextAction( ScriptedController C )
|
||||
{
|
||||
C.ActionNum += 1;
|
||||
if ( C.Pawn.Health > HealthThreshold )
|
||||
ProceedToSectionEnd( C );
|
||||
}
|
||||
|
||||
function bool StartsSection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
23
kf_sources/ScrnStoryGame/Classes/ACTION_IfMyPlayerClose.uc
Normal file
23
kf_sources/ScrnStoryGame/Classes/ACTION_IfMyPlayerClose.uc
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
class ACTION_IfMyPlayerClose extends ScriptedAction;
|
||||
|
||||
var(Action) float MaxDistance;
|
||||
|
||||
function ProceedToNextAction( ScriptedController C )
|
||||
{
|
||||
local Pawn MyPlayer;
|
||||
|
||||
C.ActionNum += 1;
|
||||
|
||||
MyPlayer = C.GetMyPlayer();
|
||||
if ( MyPlayer == none || vsize(MyPlayer.Location - C.Pawn.Location) > MaxDistance + C.Pawn.CollisionRadius + MyPlayer.CollisionRadius )
|
||||
ProceedToSectionEnd( C );
|
||||
}
|
||||
|
||||
function bool StartsSection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
68
kf_sources/ScrnStoryGame/Classes/ACTION_IfPlayerCount.uc
Normal file
68
kf_sources/ScrnStoryGame/Classes/ACTION_IfPlayerCount.uc
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
ACTION_IfPlayerCount
|
||||
--------------------------------------------------------------
|
||||
|
||||
Checks if player count is between MinPlayers and MaxPlayers (inclusive).
|
||||
If not - goes to section end.
|
||||
|
||||
Setting MaxPlayers=0 (default) disables MaxPlayers restriction and only checks MinPlayers.
|
||||
Set bCountOnlyAlive=True to exclude dead players
|
||||
|
||||
Author : PooSH
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class ACTION_IfPlayerCount extends ScriptedAction;
|
||||
|
||||
var(Action) int MinPlayers, MaxPlayers;
|
||||
var(Action) bool bCountOnlyAlive; // all players should be counted or only alive?
|
||||
|
||||
|
||||
//returns adjusted NumToSpawn to difficulty and number of players
|
||||
function int GetPlayerCount(LevelInfo Level)
|
||||
{
|
||||
local int LivingCount;
|
||||
local Controller C;
|
||||
|
||||
if ( !bCountOnlyAlive )
|
||||
return Level.Game.NumPlayers;
|
||||
|
||||
for ( C=Level.ControllerList; C!=None; C=C.NextController ) {
|
||||
if (C.PlayerReplicationInfo != None && C.bIsPlayer
|
||||
&& !C.PlayerReplicationInfo.bOutOfLives && !C.PlayerReplicationInfo.bOnlySpectator )
|
||||
LivingCount++;
|
||||
}
|
||||
return LivingCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ProceedToNextAction(ScriptedController C)
|
||||
{
|
||||
local int PlayerCount;
|
||||
|
||||
C.ActionNum += 1;
|
||||
|
||||
PlayerCount = GetPlayerCount(C.Level);
|
||||
if ( PlayerCount < MinPlayers || (MaxPlayers != 0 && PlayerCount > MaxPlayers) )
|
||||
ProceedToSectionEnd(C);
|
||||
}
|
||||
|
||||
function bool StartsSection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
if ( MaxPlayers == 0 )
|
||||
return ActionString $ " > " $ MinPlayers;
|
||||
return ActionString $ " ["$MinPlayers$".."$MaxPlayers$"]";
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="If player count"
|
||||
}
|
||||
28
kf_sources/ScrnStoryGame/Classes/ACTION_IfSeesEnemy.uc
Normal file
28
kf_sources/ScrnStoryGame/Classes/ACTION_IfSeesEnemy.uc
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Same as ACTION_IfMonsterSeesEnemy, but can be used with any Pawn (e.g. NPC)
|
||||
// added MaxDistance
|
||||
class ACTION_IfSeesEnemy extends ScriptedAction;
|
||||
|
||||
var(Action) float MaxDistance;
|
||||
|
||||
function ProceedToNextAction( ScriptedController C )
|
||||
{
|
||||
C.ActionNum += 1;
|
||||
|
||||
if ( C.Enemy != none && (C.Enemy.bDeleteMe || C.Enemy.Health <= 0) )
|
||||
C.Enemy = none;
|
||||
|
||||
if ( C.Enemy == none || !C.CanSee(C.Enemy)
|
||||
|| (MaxDistance > 0 && vsize(C.Pawn.Location - C.Enemy.Location) > MaxDistance + C.Enemy.CollisionRadius) )
|
||||
{
|
||||
ProceedToSectionEnd( C );
|
||||
}
|
||||
}
|
||||
|
||||
function bool StartsSection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
59
kf_sources/ScrnStoryGame/Classes/ACTION_KillPawn.uc
Normal file
59
kf_sources/ScrnStoryGame/Classes/ACTION_KillPawn.uc
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
class ACTION_KillPawn extends ScriptedAction;
|
||||
|
||||
/**
|
||||
* Who killed pawn?
|
||||
* KILLER_None - nobody (none)
|
||||
* KILLER_Instigator - ScriptedController.Instigator.Controller
|
||||
* KILLER_Myself - ScriptedController
|
||||
* KILLER_Suicide - Victim's controller
|
||||
*/
|
||||
enum EKiller
|
||||
{
|
||||
KILLER_None,
|
||||
KILLER_Instigator,
|
||||
KILLER_Myself,
|
||||
KILLER_Suicide
|
||||
};
|
||||
|
||||
var(Action) class<Pawn> VictimClass;
|
||||
var(Action) name VictimTag;
|
||||
var(Action) class<DamageType> DamageType;
|
||||
var(Action) EKiller Killer;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local Actor A;
|
||||
local Pawn Victim;
|
||||
local Controller KillerController;
|
||||
|
||||
switch ( Killer ) {
|
||||
case KILLER_Instigator:
|
||||
if ( C.Instigator != none )
|
||||
KillerController = C.Instigator.Controller;
|
||||
break;
|
||||
|
||||
case KILLER_Myself:
|
||||
KillerController = C;
|
||||
break;
|
||||
}
|
||||
|
||||
ForEach C.DynamicActors(VictimClass, A, VictimTag) {
|
||||
Victim = Pawn(A);
|
||||
if ( Killer == KILLER_Suicide )
|
||||
KillerController = Victim.Controller;
|
||||
Victim.Died(KillerController, DamageType, Victim.Location);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString @ GetItemName(String(VictimClass)) @ VictimTag;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
VictimClass=Class'Old2k4.Monster'
|
||||
ActionString="Kill pawn"
|
||||
}
|
||||
38
kf_sources/ScrnStoryGame/Classes/ACTION_MoveToEnemyTimed.uc
Normal file
38
kf_sources/ScrnStoryGame/Classes/ACTION_MoveToEnemyTimed.uc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
class ACTION_MoveToEnemyTimed extends LatentScriptedAction;
|
||||
|
||||
var(Action) float TimeLimit;
|
||||
|
||||
function bool MoveToGoal()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function Actor GetMoveTargetFor(ScriptedController C)
|
||||
{
|
||||
return C.Enemy;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@TimeLimit;
|
||||
}
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
if ( C.Enemy == none || C.Enemy.bDeleteMe || C.Enemy.Health <= 0 )
|
||||
return false;
|
||||
|
||||
C.CurrentAction = self;
|
||||
C.SetTimer(TimeLimit, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
function bool CompleteWhenTimer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="Move to enemy or wait for timer"
|
||||
}
|
||||
51
kf_sources/ScrnStoryGame/Classes/ACTION_MoveToPlayerTimed.uc
Normal file
51
kf_sources/ScrnStoryGame/Classes/ACTION_MoveToPlayerTimed.uc
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// completes when pawn reaches player or on timer
|
||||
class ACTION_MoveToPlayerTimed extends LatentScriptedAction;
|
||||
|
||||
var(Action) float TimeLimit;
|
||||
// if distance between me and my player is lower that that, action is skipped.
|
||||
// MinDistance doesn't include collision radiuses
|
||||
var(Action) float MinDistance;
|
||||
var(Action) float Cooldown; // time to wait till next action, if player is closer than MinDistance
|
||||
|
||||
var bool bNeedToMode;
|
||||
|
||||
function bool MoveToGoal()
|
||||
{
|
||||
return bNeedToMode;
|
||||
}
|
||||
|
||||
function Actor GetMoveTargetFor(ScriptedController C)
|
||||
{
|
||||
return C.GetMyPlayer();
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@TimeLimit;
|
||||
}
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local Pawn MyPlayer;
|
||||
|
||||
C.CurrentAction = self;
|
||||
|
||||
MyPlayer = C.GetMyPlayer();
|
||||
bNeedToMode = MyPlayer != none && (MinDistance <= 0 || vsize(C.Pawn.Location - MyPlayer.Location) >= MinDistance + C.Pawn.CollisionRadius + MyPlayer.CollisionRadius);
|
||||
if ( bNeedToMode )
|
||||
C.SetTimer(TimeLimit, false);
|
||||
else
|
||||
C.SetTimer(fmax(Cooldown, 0.01), false); // wait at least 1 tick
|
||||
return true;
|
||||
}
|
||||
|
||||
function bool CompleteWhenTimer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
Cooldown=0.500000
|
||||
ActionString="Move to player or wait for timer"
|
||||
}
|
||||
24
kf_sources/ScrnStoryGame/Classes/ACTION_NPC_Active.uc
Normal file
24
kf_sources/ScrnStoryGame/Classes/ACTION_NPC_Active.uc
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
class ACTION_NPC_Active extends ScriptedAction;
|
||||
|
||||
var(Action) bool bActive;
|
||||
var(Action) name NPC_Tag;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local KF_StoryNPC P;
|
||||
|
||||
foreach C.DynamicActors(Class'KF_StoryNPC',P, NPC_Tag)
|
||||
P.SetActive(bActive);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@bActive;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="NPC active"
|
||||
}
|
||||
26
kf_sources/ScrnStoryGame/Classes/ACTION_NPC_Bleed.uc
Normal file
26
kf_sources/ScrnStoryGame/Classes/ACTION_NPC_Bleed.uc
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// can be used only on KF_StoryNPC_Spawnable_SE
|
||||
|
||||
class ACTION_NPC_Bleed extends ScriptedAction;
|
||||
|
||||
var(Action) bool bBleed;
|
||||
var(Action) name NPC_Tag;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local KF_StoryNPC_Spawnable_SE P;
|
||||
|
||||
foreach C.DynamicActors(Class'KF_StoryNPC_Spawnable_SE',P, NPC_Tag)
|
||||
P.bBleed = bBleed;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@bBleed;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="NPC bleed"
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
class ACTION_PlayKFReplicatedAmbientSound extends ACTION_PlayAmbientSound;
|
||||
|
||||
var(Action) edfindable protected Actor SoundActor; // actor to set ambientsound on
|
||||
// used to find SoundActor, if it is not set. Tag must be unique!
|
||||
var(Action) edfindable name SoundActorTag;
|
||||
|
||||
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
// play appropriate sound
|
||||
if ( AmbientSound != None )
|
||||
{
|
||||
if ( SoundActor == none )
|
||||
foreach C.AllActors(class'Actor', SoundActor, SoundActorTag)
|
||||
break;
|
||||
|
||||
if ( SoundActor != none ) {
|
||||
// this probabaly isn't working,
|
||||
// because actor must have those values set in defaultproperties
|
||||
if ( SoundActor.RemoteRole < ROLE_SimulatedProxy )
|
||||
SoundActor.RemoteRole = ROLE_SimulatedProxy;
|
||||
SoundActor.bAlwaysRelevant = true; //
|
||||
|
||||
SoundActor.AmbientSound = AmbientSound;
|
||||
SoundActor.SoundVolume = SoundVolume;
|
||||
SoundActor.SoundPitch = SoundPitch;
|
||||
SoundActor.SoundRadius = SoundRadius;
|
||||
|
||||
SoundActor.NetUpdateTime = SoundActor.Level.TimeSeconds - 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@AmbientSound;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Allows to adjust KFLevelRules_Story.Rules_Monsters
|
||||
|
||||
class ACTION_STORY_MonsterRules extends ScriptedAction;
|
||||
|
||||
/* absolute maximum number of zombies we can have in this story map at one time */
|
||||
var(Action) int MaxEnemiesAtOnce;
|
||||
|
||||
/* should the game kill off ZEDs which haven't been seen by players for a while? */
|
||||
var(Action) bool bAutoKillStragglers;
|
||||
|
||||
/* Auto Kill threshold if bAutoKilLStragglers is true */
|
||||
var(Action) int MaxStragglers;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local KFLevelRules_Story KFLR;
|
||||
|
||||
foreach C.AllActors(class'KFLevelRules_Story', KFLR) {
|
||||
if ( MaxEnemiesAtOnce > 0 )
|
||||
KFLR.MaxEnemiesAtOnce = MaxEnemiesAtOnce;
|
||||
KFLR.bAutoKillStragglers = bAutoKillStragglers;
|
||||
KFLR.MaxStragglers = MaxStragglers;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bAutoKillStragglers=True
|
||||
MaxStragglers=5
|
||||
ActionString="story monster rules"
|
||||
}
|
||||
79
kf_sources/ScrnStoryGame/Classes/ACTION_SetEnemy.uc
Normal file
79
kf_sources/ScrnStoryGame/Classes/ACTION_SetEnemy.uc
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
class ACTION_SetEnemy extends ScriptedAction;
|
||||
|
||||
enum EEnemyChanging
|
||||
{
|
||||
ECH_Never,
|
||||
ECH_Always,
|
||||
ECH_IfOutOfRange,
|
||||
};
|
||||
|
||||
enum EEnemySelection
|
||||
{
|
||||
ESEL_First,
|
||||
ESEL_Closest,
|
||||
};
|
||||
|
||||
var(Action) class<Pawn> EnemyClass;
|
||||
var(Action) name EnemyTag;
|
||||
var(Action) bool bOnlyVisible;
|
||||
var(Action) float MaxDistance;
|
||||
var(Action) bool bSetViewTarget; // auto-set target on enemy
|
||||
var(Action) EEnemyChanging AllowChangingEnemies;
|
||||
var(Action) EEnemySelection EnemySelection;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local Controller EC;
|
||||
local float BestDist, CurDist;
|
||||
|
||||
if ( EnemyTag == 'Clear' ) {
|
||||
C.Enemy = none;
|
||||
if (bSetViewTarget)
|
||||
C.ScriptedFocus = none;
|
||||
}
|
||||
else {
|
||||
if ( C.Enemy != none && (C.Enemy.bDeleteMe || C.Enemy.Health <= 0) )
|
||||
C.Enemy = none;
|
||||
|
||||
if ( C.Enemy != none && (AllowChangingEnemies == ECH_Never
|
||||
|| (AllowChangingEnemies == ECH_IfOutOfRange
|
||||
&& ((bOnlyVisible && !C.CanSee(C.Enemy)) || vsize(C.Pawn.Location - C.Enemy.Location) > MaxDistance))) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BestDist = Square(MaxDistance);
|
||||
for ( EC = C.Level.ControllerList; EC != none; EC = EC.nextController ) {
|
||||
if ( EC.Pawn != none && ClassIsChildOf(EC.Pawn.class, EnemyClass)
|
||||
&& !EC.Pawn.bPendingDelete && !EC.Pawn.bDeleteMe && EC.Pawn.Health > 0
|
||||
&& (EC.Pawn.Tag == EnemyTag || EnemyTag == '') )
|
||||
{
|
||||
if ( bOnlyVisible && !C.CanSee(EC.Pawn) )
|
||||
continue;
|
||||
|
||||
CurDist = VSizeSquared(C.Pawn.Location - EC.Pawn.Location);
|
||||
if ( CurDist < BestDist ) {
|
||||
C.Enemy = EC.Pawn;
|
||||
BestDist = CurDist;
|
||||
if ( EnemySelection == ESEL_First)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( bSetViewTarget && C.Enemy != none )
|
||||
C.ScriptedFocus = C.Enemy;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
EnemyClass=Class'Old2k4.Monster'
|
||||
MaxDistance=500.000000
|
||||
ActionString="set enemy"
|
||||
bValidForTrigger=False
|
||||
}
|
||||
47
kf_sources/ScrnStoryGame/Classes/ACTION_SetPhysicsOther.uc
Normal file
47
kf_sources/ScrnStoryGame/Classes/ACTION_SetPhysicsOther.uc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// Set Physics to all actors with designed Tag
|
||||
|
||||
class ACTION_SetPhysicsOther extends ScriptedAction;
|
||||
|
||||
var(Action) class<Actor> BaseClass;
|
||||
var(Action) name OtherTag;
|
||||
var(Action) Actor.EPhysics NewPhysicsMode;
|
||||
var(Action) float NewNetUpdateFrequency; // in cases we need to force replication to clients, e.g. dropping down pickup
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local Actor Other;
|
||||
local Pickup Pickup;
|
||||
|
||||
foreach C.AllActors(BaseClass, Other, OtherTag) {
|
||||
Other.SetPhysics(NewPhysicsMode);
|
||||
|
||||
if ( Other.Level.NetMode != NM_StandAlone && NewPhysicsMode != PHYS_None ) {
|
||||
if ( Other.NetUpdateFrequency < NewNetUpdateFrequency ) {
|
||||
Other.NetUpdateFrequency = NewNetUpdateFrequency;
|
||||
Other.NetUpdateTime = Other.Level.TimeSeconds - 1;
|
||||
}
|
||||
if ( NewPhysicsMode == PHYS_Falling) {
|
||||
Pickup = Pickup(Other);
|
||||
if ( Pickup != none ) {
|
||||
// copied from Pickup.InitDroppedPickupFor()
|
||||
Pickup.bOnlyReplicateHidden = false;
|
||||
Pickup.bUpdateSimulatedPosition = true;
|
||||
Pickup.bIgnoreEncroachers = false;
|
||||
Pickup.NetUpdateFrequency = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@NewPhysicsMode;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
baseClass=Class'Engine.Actor'
|
||||
ActionString="change physics to "
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
ACTION_SpawnRandomPickups
|
||||
--------------------------------------------------------------
|
||||
|
||||
Spawns random number of pickups on the map.
|
||||
All pickups must have the same tag.
|
||||
|
||||
Author : PooSH
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class ACTION_SpawnRandomPickups extends ScriptedAction;
|
||||
|
||||
var() class<xPickUpBase> PickupType;
|
||||
var() name PickupTag;
|
||||
var() int NumToSpawn;
|
||||
var() float PlayerCountScale; // extra scaling per player above 1
|
||||
|
||||
//returns adjusted NumToSpawn to difficulty and number of players
|
||||
function int GetAdjustedNumToSpawn(LevelInfo Level)
|
||||
{
|
||||
local int LivingCount;
|
||||
local Controller C;
|
||||
|
||||
for ( C=Level.ControllerList; C!=None; C=C.NextController ) {
|
||||
if (C.PlayerReplicationInfo != None && C.bIsPlayer
|
||||
&& !C.PlayerReplicationInfo.bOutOfLives && !C.PlayerReplicationInfo.bOnlySpectator )
|
||||
LivingCount++;
|
||||
}
|
||||
|
||||
if ( LivingCount <= 1 )
|
||||
return NumToSpawn;
|
||||
|
||||
return NumToSpawn * (1.0 + PlayerCountScale*(LivingCount-1));
|
||||
}
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local Actor a;
|
||||
local xPickUpBase x;
|
||||
local array<xPickUpBase> xPickups;
|
||||
local int i, count;
|
||||
|
||||
Count = GetAdjustedNumToSpawn(C.Level);
|
||||
if ( Count <= 0 )
|
||||
return false;
|
||||
|
||||
foreach C.AllActors(PickupType, a, PickupTag) {
|
||||
x = xPickUpBase(a);
|
||||
if ( x.myPickUp==none )
|
||||
xPickups[xPickups.length] = x;
|
||||
}
|
||||
|
||||
while ( xPickups.length > Count )
|
||||
xPickups.remove(rand(xPickups.length), 1);
|
||||
|
||||
log("Spawning "$xPickups.length$" pickups of "$PickupTag$" ("$PickupType$")...");
|
||||
|
||||
for ( i=0; i<xPickups.length; ++i )
|
||||
xPickups[i].SpawnPickup();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
PickupType=Class'Engine.xPickUpBase'
|
||||
}
|
||||
121
kf_sources/ScrnStoryGame/Classes/ACTION_SpawnZED.uc
Normal file
121
kf_sources/ScrnStoryGame/Classes/ACTION_SpawnZED.uc
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
Spawns zed (kfmonster). Respects current game event (KFMonstersCollection)
|
||||
Supports custom monsters.
|
||||
|
||||
ZedKind What kind of zed should be spawned:
|
||||
ZED_Custom specified by CustomZedClass
|
||||
ZED_Boss spawn end game boss
|
||||
ZED_Clot..ZED_Husk spawn standard monsters. Monster class will be
|
||||
taken from KFMonstersCollection, so it supports events.
|
||||
|
||||
CustomZedClass Custom zed class name WITHOUT package name, e.g. "ZombieBrute".
|
||||
Code will search KFMonstersCollection.MonsterClasses for a given class.
|
||||
If class will no be found, monster will not spawn!
|
||||
|
||||
NewTag Tag to be assigned to the spawned monster
|
||||
LocationTag Tag of the spawn location
|
||||
RotateToTag Tag of the actor for spawned zed to loot at
|
||||
|
||||
bOffsetFromScriptedPawn If true, scripted pawn will be used for offset calculation.
|
||||
LocationTag has higher priority than bOffsetFromScriptedPawn.
|
||||
LocationOffset Spawn offset from the chosen location
|
||||
RotationOffset Offset from the chosen rotation
|
||||
|
||||
@author PooSH
|
||||
*/
|
||||
|
||||
class ACTION_SpawnZED extends ScriptedAction;
|
||||
|
||||
|
||||
enum EZEDKind {
|
||||
ZED_Custom,
|
||||
ZED_Boss,
|
||||
ZED_Clot,
|
||||
ZED_Crawler,
|
||||
ZED_Gorefast,
|
||||
ZED_Stalker,
|
||||
ZED_Scrake,
|
||||
ZED_Fleshpound,
|
||||
ZED_Bloat,
|
||||
ZED_Siren,
|
||||
ZED_Husk,
|
||||
};
|
||||
|
||||
|
||||
var(Action) EZEDKind ZedKind;
|
||||
var(Action) string CustomZedClass;
|
||||
var(Action) name NewTag;
|
||||
var(Action) name LocationTag;
|
||||
var(Action) name RotateToTag;
|
||||
var(Action) bool bOffsetFromScriptedPawn;
|
||||
var(Action) vector LocationOffset;
|
||||
var(Action) rotator RotationOffset;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
local vector L;
|
||||
local rotator R;
|
||||
local actor a;
|
||||
local class<KFMonstersCollection> MC;
|
||||
local class<KFMonster> MonsterClass;
|
||||
local int i;
|
||||
|
||||
if ( KFGameType(C.Level.Game) == none ) {
|
||||
log("ACTION_SpawnZED can be used only with KFGameType!");
|
||||
return false;
|
||||
}
|
||||
MC = KFGameType(C.Level.Game).MonsterCollection;
|
||||
|
||||
if ( ZedKind == ZED_Custom ) {
|
||||
for ( i=0; i < MC.default.MonsterClasses.length; ++i ) {
|
||||
if ( CustomZedClass ~= GetItemName(MC.default.MonsterClasses[i].MClassName) ) {
|
||||
MonsterClass = class<KFMonster>(DynamicLoadObject(MC.default.MonsterClasses[i].MClassName, Class'class'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( ZedKind == ZED_Boss )
|
||||
MonsterClass = class<KFMonster>(DynamicLoadObject(MC.default.EndGameBossClass, Class'class'));
|
||||
else
|
||||
MonsterClass = class<KFMonster>(DynamicLoadObject(MC.default.MonsterClasses[int(ZedKind)-2].MClassName, Class'class'));
|
||||
|
||||
if ( MonsterClass == none )
|
||||
return false;
|
||||
|
||||
if ( LocationTag != '' ) {
|
||||
foreach C.AllActors(class'Actor', a, LocationTag)
|
||||
break;
|
||||
}
|
||||
if ( a != none )
|
||||
L = a.Location + LocationOffset;
|
||||
else if ( bOffsetFromScriptedPawn && C.Pawn != none )
|
||||
L = C.Pawn.Location + LocationOffset;
|
||||
else
|
||||
L = C.SequenceScript.Location + LocationOffset;
|
||||
|
||||
a = none;
|
||||
if ( RotateToTag != '' ) {
|
||||
foreach C.AllActors(class'Actor', a, RotateToTag)
|
||||
break;
|
||||
}
|
||||
if ( a != none )
|
||||
R = rotator(a.Location - L) + RotationOffset;
|
||||
else if ( bOffsetFromScriptedPawn && C.Pawn != none )
|
||||
R = C.Pawn.Rotation + RotationOffset;
|
||||
else
|
||||
R = C.SequenceScript.Rotation + RotationOffset;
|
||||
|
||||
C.Spawn(MonsterClass,,NewTag,L,R);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@GetEnum(enum'ACTION_SpawnZED.EZEDKind', ZedKind)@CustomZedClass;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="Spawn ZED"
|
||||
}
|
||||
54
kf_sources/ScrnStoryGame/Classes/ACTION_WaitForAnyPlayer.uc
Normal file
54
kf_sources/ScrnStoryGame/Classes/ACTION_WaitForAnyPlayer.uc
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// ACTION_WaitForPlayer succeds only if MyPlayerController.Pawn is nearby
|
||||
// ACTION_WaitForAnyPlayer succeeds if any players are nearby
|
||||
|
||||
class ACTION_WaitForAnyPlayer extends ACTION_WaitForPlayer;
|
||||
|
||||
var(Action) float CheckTime; // how often to look for a player?
|
||||
|
||||
var ScriptedController MyScriptedController;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
MyScriptedController = C;
|
||||
if ( CheckIfNearPlayer(C) )
|
||||
return false;
|
||||
C.CurrentAction = self;
|
||||
C.SetTimer(fmax(CheckTime, 0.1),true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function bool WaitForPlayer()
|
||||
{
|
||||
if ( MyScriptedController != none )
|
||||
CheckIfNearPlayer(MyScriptedController); // update MyScriptedController.MyPlayerController
|
||||
return true;
|
||||
}
|
||||
|
||||
function bool CheckIfNearPlayer(ScriptedController SC)
|
||||
{
|
||||
local Controller C;
|
||||
local PlayerController PC, BACKUP_MyPlayerController;
|
||||
|
||||
if ( SC.MyPlayerController != none && SC.MyPlayerController.Pawn != none && SC.CheckIfNearPlayer(Distance) )
|
||||
return true;
|
||||
|
||||
BACKUP_MyPlayerController = SC.MyPlayerController;
|
||||
|
||||
for ( C = SC.Level.ControllerList; C != none; C = C.nextController ) {
|
||||
PC = PlayerController(C);
|
||||
if ( PC != none && PC.Pawn != none ) {
|
||||
SC.MyPlayerController = PC;
|
||||
if ( SC.CheckIfNearPlayer(Distance) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// restore original MyPlayerController, if no players are nearby - just in case
|
||||
SC.MyPlayerController = BACKUP_MyPlayerController;
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
CheckTime=1.000000
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
class ACTION_WaitForTimerRandom extends LatentScriptedAction;
|
||||
|
||||
var(Action) float MinTime;
|
||||
var(Action) float MaxTime;
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
C.CurrentAction = self;
|
||||
C.SetTimer(MinTime + frand()*(MaxTime-MinTime), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
function bool CompleteWhenTriggered()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function bool CompleteWhenTimer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function string GetActionString()
|
||||
{
|
||||
return ActionString@MinTime$".."$MaxTime;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ActionString="Wait for timer"
|
||||
}
|
||||
16
kf_sources/ScrnStoryGame/Classes/ACTION_ZEDTIME.uc
Normal file
16
kf_sources/ScrnStoryGame/Classes/ACTION_ZEDTIME.uc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// turns on zed time
|
||||
class ACTION_ZEDTIME extends ScriptedAction;
|
||||
|
||||
var(Action) float Possibility;
|
||||
var(Action) float Duration; // leave 0 for default ZED time duration
|
||||
|
||||
function bool InitActionFor(ScriptedController C)
|
||||
{
|
||||
KFGameType(C.Level.Game).DramaticEvent(Possibility, Duration);
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
Possibility=1.000000
|
||||
}
|
||||
21
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Deaths.uc
Normal file
21
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Deaths.uc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
class DLGFILTER_Deaths extends DialoguePlayerFilter;
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
local Controller C;
|
||||
local float TopValue;
|
||||
|
||||
for ( C = PRI.Level.ControllerList; C != None; C = C.NextController ) {
|
||||
if ( C.PlayerReplicationInfo != none )
|
||||
TopValue = fmax(TopValue, C.PlayerReplicationInfo.Deaths);
|
||||
}
|
||||
|
||||
if ( TopValue == 0.0 )
|
||||
return 0.0;
|
||||
|
||||
return Value * PRI.Deaths / TopValue;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
14
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Female.uc
Normal file
14
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Female.uc
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class DLGFILTER_Female extends DialoguePlayerFilter;
|
||||
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
if ( PRI.bIsFemale )
|
||||
return Value;
|
||||
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
14
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Instigator.uc
Normal file
14
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Instigator.uc
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class DLGFILTER_Instigator extends DialoguePlayerFilter;
|
||||
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
if ( Dialogue.DialogueInstigator == PRI.Owner )
|
||||
return Value;
|
||||
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
30
kf_sources/ScrnStoryGame/Classes/DLGFILTER_KillAssists.uc
Normal file
30
kf_sources/ScrnStoryGame/Classes/DLGFILTER_KillAssists.uc
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
class DLGFILTER_KillAssists extends DialoguePlayerFilter;
|
||||
|
||||
var private transient float TopValue;
|
||||
var private transient float TopValueTime; // lst time when TopValue was set
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
local Controller C;
|
||||
|
||||
if ( KFPlayerReplicationInfo(PRI) == none )
|
||||
return 0.0;
|
||||
|
||||
if ( TopValueTime != PRI.Level.TimeSeconds ) {
|
||||
TopValue = 0.0;
|
||||
TopValueTime = PRI.Level.TimeSeconds;
|
||||
for ( C = PRI.Level.ControllerList; C != None; C = C.NextController ) {
|
||||
if ( KFPlayerReplicationInfo(C.PlayerReplicationInfo) != none )
|
||||
TopValue = fmax(TopValue, KFPlayerReplicationInfo(C.PlayerReplicationInfo).KillAssists);
|
||||
}
|
||||
}
|
||||
|
||||
if ( TopValue == 0.0 )
|
||||
return 0.0;
|
||||
|
||||
return Value * KFPlayerReplicationInfo(PRI).KillAssists / TopValue;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
27
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Kills.uc
Normal file
27
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Kills.uc
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// more kills = higher value
|
||||
class DLGFILTER_Kills extends DialoguePlayerFilter;
|
||||
|
||||
var private transient float TopValue;
|
||||
var private transient float TopValueTime; // lst time when TopValue was set
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
local Controller C;
|
||||
|
||||
if ( TopValueTime != PRI.Level.TimeSeconds ) {
|
||||
TopValue = 0.0;
|
||||
TopValueTime = PRI.Level.TimeSeconds;
|
||||
for ( C = PRI.Level.ControllerList; C != None; C = C.NextController ) {
|
||||
if ( C.PlayerReplicationInfo != none )
|
||||
TopValue = fmax(TopValue, C.PlayerReplicationInfo.Kills);
|
||||
}
|
||||
}
|
||||
|
||||
if ( TopValue == 0.0 )
|
||||
return 0.0;
|
||||
return Value * PRI.Kills / TopValue;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
61
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Perk.uc
Normal file
61
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Perk.uc
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
class DLGFILTER_Perk extends DialoguePlayerFilter;
|
||||
|
||||
enum EPerk
|
||||
{
|
||||
PERK_Medic,
|
||||
PERK_Support,
|
||||
PERK_Sharpshooter,
|
||||
PERK_Commando,
|
||||
PERK_Berserker,
|
||||
PERK_Firebug,
|
||||
PERK_Demolitions,
|
||||
};
|
||||
var(Filter) EPerk Perk;
|
||||
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
local KFPlayerReplicationInfo KFPRI;
|
||||
|
||||
KFPRI = KFPlayerReplicationInfo(PRI);
|
||||
|
||||
if ( KFPRI == none || KFPRI.ClientVeteranSkill == none )
|
||||
return 0.0;
|
||||
|
||||
switch (Perk) {
|
||||
case PERK_Medic:
|
||||
if ( KFPRI.ClientVeteranSkill.static.GetSyringeChargeRate(KFPRI) > 1.01 )
|
||||
return Value;
|
||||
break;
|
||||
case PERK_Support:
|
||||
if ( KFPRI.ClientVeteranSkill.static.GetWeldSpeedModifier(KFPRI) > 1.01 )
|
||||
return Value;
|
||||
break;
|
||||
case PERK_Sharpshooter:
|
||||
if ( KFPRI.ClientVeteranSkill.static.GetHeadShotDamMulti(KFPRI, none, class'DamTypeCrossbow') > 1.01 )
|
||||
return Value;
|
||||
break;
|
||||
case PERK_Commando:
|
||||
if ( KFPRI.ClientVeteranSkill.static.ShowStalkers(KFPRI) )
|
||||
return Value;
|
||||
break;
|
||||
case PERK_Berserker:
|
||||
if ( KFPRI.ClientVeteranSkill.static.GetMeleeMovementSpeedModifier(KFPRI) > 0.01 )
|
||||
return Value;
|
||||
break;
|
||||
case PERK_Firebug:
|
||||
if ( KFPRI.ClientVeteranSkill.static.AddDamage(KFPRI, none, none, 100, class'DamTypeBurned') == 100 )
|
||||
return Value;
|
||||
break;
|
||||
case PERK_Demolitions:
|
||||
if ( KFPRI.ClientVeteranSkill.static.AddDamage(KFPRI, none, none, 100, class'DamTypeM79Grenade') == 100 )
|
||||
return Value;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
26
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Score.uc
Normal file
26
kf_sources/ScrnStoryGame/Classes/DLGFILTER_Score.uc
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
class DLGFILTER_Score extends DialoguePlayerFilter;
|
||||
|
||||
var private transient float TopValue;
|
||||
var private transient float TopValueTime; // lst time when TopValue was set
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
local Controller C;
|
||||
|
||||
if ( TopValueTime != PRI.Level.TimeSeconds ) {
|
||||
TopValue = 0.0;
|
||||
TopValueTime = PRI.Level.TimeSeconds;
|
||||
for ( C = PRI.Level.ControllerList; C != None; C = C.NextController ) {
|
||||
if ( C.PlayerReplicationInfo != none )
|
||||
TopValue = fmax(TopValue, C.PlayerReplicationInfo.Score);
|
||||
}
|
||||
}
|
||||
|
||||
if ( TopValue == 0.0 )
|
||||
return 0.0;
|
||||
return Value * PRI.Score / TopValue;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
6
kf_sources/ScrnStoryGame/Classes/DamTypeBleedFixed.uc
Normal file
6
kf_sources/ScrnStoryGame/Classes/DamTypeBleedFixed.uc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class DamTypeBleedFixed extends DamTypeBleedOut;
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bArmorStops=False
|
||||
}
|
||||
16
kf_sources/ScrnStoryGame/Classes/DialoguePlayerFilter.uc
Normal file
16
kf_sources/ScrnStoryGame/Classes/DialoguePlayerFilter.uc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
class DialoguePlayerFilter extends Object
|
||||
abstract
|
||||
hidecategories(Object)
|
||||
collapsecategories
|
||||
editinlinenew;
|
||||
|
||||
var(Filter) const float Value;
|
||||
|
||||
function float CalcPlayerValue(KF_PlayerDialogue Dialogue, int DlgIdx, PlayerReplicationInfo PRI)
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
class Dialogue_EventListener_SE extends Dialogue_EventListener
|
||||
dependson(KF_DialogueSpot_SE);
|
||||
|
||||
function Trigger( actor Other, pawn EventInstigator )
|
||||
{
|
||||
local KF_DialogueSpot_SE DlgOwnerSE;
|
||||
|
||||
DlgOwnerSE = KF_DialogueSpot_SE(DlgOwner);
|
||||
if ( DlgOwnerSE == none || DlgOwnerSE.RequiredEventType == RET_Trigger ) {
|
||||
super.Trigger(Other, EventInstigator);
|
||||
return;
|
||||
}
|
||||
|
||||
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 bLooping=True, dialogue is DISABLED
|
||||
switch (DlgOwnerSE.RequiredEventType) {
|
||||
case RET_Enable:
|
||||
DlgOwner.Dialogues[AssociatedIndex].bWasTriggered = false;
|
||||
break;
|
||||
case RET_Disable:
|
||||
DlgOwner.Dialogues[AssociatedIndex].bWasTriggered = true;
|
||||
break;
|
||||
case RET_Toggle:
|
||||
DlgOwner.Dialogues[AssociatedIndex].bWasTriggered = !DlgOwner.Dialogues[AssociatedIndex].bWasTriggered;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
43
kf_sources/ScrnStoryGame/Classes/FragPickup_SE.uc
Normal file
43
kf_sources/ScrnStoryGame/Classes/FragPickup_SE.uc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// if player already has nades, it gives +1 ammo
|
||||
|
||||
class FragPickup_SE extends FragPickup;
|
||||
|
||||
auto state pickup
|
||||
{
|
||||
function bool GiveAmmo(KFHumanPawn Receiver)
|
||||
{
|
||||
local Inventory Inv;
|
||||
local KFWeapon W;
|
||||
|
||||
if ( Receiver == none )
|
||||
return false;
|
||||
|
||||
for ( Inv=Receiver.Inventory; Inv!=none; Inv=Inv.Inventory ) {
|
||||
W = KFWeapon(Inv);
|
||||
if ( W != none && ClassIsChildOf(Inv.class, InventoryType) ) {
|
||||
if ( !W.AmmoMaxed(0) ) {
|
||||
KFWeapon(Inv).AddAmmo(AmmoAmount[0], 0);
|
||||
PlaySound( PickupSound,SLOT_Interact );
|
||||
SetRespawn();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function Touch(Actor Other)
|
||||
{
|
||||
// if player have nades (weapon) - give him weapon
|
||||
// otherwise - give weapon
|
||||
if ( KFHumanPawn(Other) != none && !GiveAmmo(KFHumanPawn(Other)) )
|
||||
super.Touch(Other);
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
cost=40
|
||||
BuyClipSize=1
|
||||
AmmoAmount(0)=1
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
class KFDestroyableStaticMesh_SE extends RODestroyableStaticMeshBase
|
||||
placeable;
|
||||
|
||||
var(Sound) sound DamagedSound; // Ambient sound for damaged mesh
|
||||
var(Sound) bool bUseDamagedSound; // use DamagedSound?
|
||||
|
||||
var protected transient int OriginalHealth;
|
||||
|
||||
|
||||
auto state Working
|
||||
{
|
||||
function BeginState()
|
||||
{
|
||||
if ( OriginalHealth == 0 )
|
||||
OriginalHealth = Health;
|
||||
super.BeginState();
|
||||
Health = OriginalHealth; // fix health resetting to default 0 and ignoring value set in editor
|
||||
}
|
||||
|
||||
function EndState()
|
||||
{
|
||||
super.EndState();
|
||||
|
||||
if ( bUseDamagedSound ) {
|
||||
AmbientSound = DamagedSound;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
TypesCanDamage(0)=Class'KFMod.DamTypeFrag'
|
||||
StaticMesh=StaticMesh'IndustrySM2.Doors.fact_metaldoor_04'
|
||||
bWorldGeometry=False
|
||||
Tag="DestroyableStaticMesh"
|
||||
}
|
||||
165
kf_sources/ScrnStoryGame/Classes/KF_BreakerBoxNPC_SE.uc
Normal file
165
kf_sources/ScrnStoryGame/Classes/KF_BreakerBoxNPC_SE.uc
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
KF_BreakerBoxNPC_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
Enhanced version of KF_BreakerBoxNPC:
|
||||
- TickEvent can be set by map author.
|
||||
- Doesn't trigger TickEvent when inactive
|
||||
- can retain visibility while inactive
|
||||
- can be started active
|
||||
- Min and max threat rating
|
||||
- Threat rating for ScrN Balance mod
|
||||
|
||||
|
||||
Author : PooSH
|
||||
Original Author: Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
class KF_BreakerBoxNPC_SE extends KF_BreakerBoxNPC;
|
||||
|
||||
var(Events) name TickEvent;
|
||||
var(AI) float MinAIThreatRating, MaxAIThreatRating; // only if parent method return value > 0
|
||||
|
||||
// used in AssessThreatTo()
|
||||
var protected transient KFMonsterController LastThreatMonster;
|
||||
var protected transient float LastThreatTime, LastThreat;
|
||||
|
||||
|
||||
// restrict healing
|
||||
function bool GiveHealth(int HealAmount, int HealMax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function bool HealDamage(int Amount, Controller Healer, class<DamageType> DamageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
Super(KF_StoryNPC_Static).PostbeginPlay();
|
||||
|
||||
CheckHealthCondition(self);
|
||||
}
|
||||
|
||||
function GetEvents(out array<name> TriggeredEvents, out array<name> ReceivedEvents)
|
||||
{
|
||||
super.GetEvents(TriggeredEvents, ReceivedEvents);
|
||||
|
||||
if( TickEvent != '' )
|
||||
TriggeredEvents[TriggeredEvents.length] = TickEvent;
|
||||
}
|
||||
|
||||
|
||||
function SetActive(bool On)
|
||||
{
|
||||
Super(KF_StoryNPC_Static).SetActive(On);
|
||||
}
|
||||
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
Super(KF_StoryNPC_Static).Tick(DeltaTime);
|
||||
|
||||
if( TickEvent != '' && bActive && !bDestroyed && Level.TimeSeconds - LastTriggerEventTime > 1.0)
|
||||
{
|
||||
LastTriggerEventTime = Level.TimeSeconds;
|
||||
TriggerEvent(TickEvent, self, self);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Monster threat assessment functionality
|
||||
|
||||
Changes by PooSH:
|
||||
1) KFGameType.bUseZEDThreatAssessment set to true, i.e. in regular game new AssessThreatTo()
|
||||
function will be used too.
|
||||
2) Distance between monster and player will always be in place.
|
||||
3) AssessThreatTo will always return value > 0. Because zeds should not ignore players.
|
||||
4) Added randomization - zeds can choose different targets in the same circumstances.
|
||||
5) Blood smell. Wounded players will attract zeds slightly more than their healthy teammates.
|
||||
|
||||
* @param Monster Monster's controller, for which we are calculating the threat level
|
||||
* @param CheckDistance Not used!
|
||||
* @return threat level between 0 and 100, where 100 is the max threat level.
|
||||
*
|
||||
* @author PooSH
|
||||
*/
|
||||
function float AssessThreatTo(KFMonsterController Monster, optional bool CheckDistance)
|
||||
{
|
||||
local float DistancePart, RandomPart, TacticalPart;
|
||||
local float DistanceSquared; // squared distance is calculated faster
|
||||
|
||||
if ( !Level.Game.IsA('ScrnStoryGameInfo') ) {
|
||||
LastThreat = super.AssessThreatTo(Monster, CheckDistance);
|
||||
// don't use MinAIThreatRating for vanilla game, cuz then zeds won't focus on players at all
|
||||
if ( LastThreat > MaxAIThreatRating )
|
||||
LastThreat = MaxAIThreatRating;
|
||||
return LastThreat;
|
||||
}
|
||||
|
||||
// the following code is for ScrnBalance only
|
||||
|
||||
if(Monster == none || KFMonster(Monster.Pawn) == none)
|
||||
{
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
if(bNoThreatToZEDs ||
|
||||
TeamIndex == 255 ||
|
||||
Health <= 0 ||
|
||||
!bActive ||
|
||||
!bDamageable ||
|
||||
(!IsThreateningTo(Monster.Pawn)) )
|
||||
{
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
if ( LastThreatMonster == Monster && LastThreatTime == Level.TimeSeconds )
|
||||
return LastThreat; // threat level for the given monster has been calculated already during the current tick
|
||||
|
||||
DistanceSquared = VSizeSquared(Monster.Pawn.Location - Location);
|
||||
|
||||
DistancePart = 65.0;
|
||||
RandomPart = MaxAIThreatRating * 0.35;
|
||||
|
||||
// TacticalPart is useless for static NPCs
|
||||
/*
|
||||
// let zeds smell blood within 25m radius - wounded players attract zeds more
|
||||
if ( DistanceSquared < 1562500.0 )
|
||||
TacticalPart += RandomPart * 0.40 * (HealthMax - Health) / HealthMax;
|
||||
// more chance to attack the same enemy multiple times
|
||||
if ( Monster.Enemy == self || Monster.Target == self )
|
||||
TacticalPart += RandomPart * 0.25;
|
||||
// more chance to focus on the player, who are attacking the monster
|
||||
if ( KFMonster(Monster.Pawn).LastDamagedBy == self)
|
||||
TacticalPart += RandomPart * 0.25;
|
||||
RandomPart = 100.0 - DistancePart - TacticalPart;
|
||||
*/
|
||||
|
||||
// If target is closer than 1 meter, max DistancePart value will be used,
|
||||
// otherwise DistancePart is lowering by 10% per meter
|
||||
// 1 meter = 50 ups (2500 squared)
|
||||
if ( DistanceSquared > 2500.0 )
|
||||
DistancePart /= 1.0 + DistanceSquared / 250000.0;
|
||||
RandomPart *= frand();
|
||||
|
||||
// save threat level for this tick
|
||||
LastThreatMonster = Monster;
|
||||
LastThreatTime = Level.TimeSeconds;
|
||||
LastThreat = DistancePart + TacticalPart + RandomPart;
|
||||
//LastThreat *= InventoryThreatModifier();
|
||||
LastThreat += BaseAIThreatRating;
|
||||
LastThreat = fclamp(LastThreat, MinAIThreatRating, MaxAIThreatRating);
|
||||
return LastThreat;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
MaxAIThreatRating=40.000000
|
||||
bWorldGeometry=True
|
||||
bBlockHitPointTraces=True
|
||||
}
|
||||
225
kf_sources/ScrnStoryGame/Classes/KF_DialogueSpot_SE.uc
Normal file
225
kf_sources/ScrnStoryGame/Classes/KF_DialogueSpot_SE.uc
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
KF_DialogueSpot_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Author : PooSH
|
||||
Original Author: Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class KF_DialogueSpot_SE extends KF_DialogueSpot
|
||||
placeable;
|
||||
|
||||
/**
|
||||
ERequiredEventType - determines what to do on RequiredEvent triggered:
|
||||
RET_Trigger Act the same as original KF_DialogueSpot - start dialogue from triggered index
|
||||
RET_Enable Just enables triggered index (doesn't show dialogue).
|
||||
RET_Disable Disables triggered index (doesn't show dialogue)
|
||||
RET_Toggle Toggles between enabled and disabled
|
||||
|
||||
In all cases except RET_Trigger, bLooping shows if dialogue is initially DISABLED
|
||||
*/
|
||||
enum ERequiredEventType
|
||||
{
|
||||
RET_Trigger,
|
||||
RET_Enable,
|
||||
RET_Disable,
|
||||
RET_Toggle
|
||||
};
|
||||
var(KF_DialogueSpot) const ERequiredEventType RequiredEventType;
|
||||
|
||||
var(KF_DialogueSpot) float DisplayTimePerWord;
|
||||
var(KF_DialogueSpot) float DisplayTimePerChar;
|
||||
var(KF_DialogueSpot) float MinDisplayTime;
|
||||
var(KF_DialogueSpot) float MaxDisplayTime;
|
||||
|
||||
|
||||
|
||||
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_SE',self,Dialogues[i].Events.RequiredEvent);
|
||||
NewListener.AssociatedIndex = i ;
|
||||
Dialogues[i].EventListener = NewListener;
|
||||
if ( RequiredEventType != RET_Trigger && Dialogues[i].Events.RequiredEvent != '' )
|
||||
Dialogues[i].bWasTriggered = Dialogues[i].bLooping;
|
||||
}
|
||||
}
|
||||
|
||||
// C&P to set bLooping as default bWasTriggered state -- PooSH
|
||||
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 ++)
|
||||
{
|
||||
if ( RequiredEventType != RET_Trigger && Dialogues[i].Events.RequiredEvent != '' )
|
||||
Dialogues[i].bWasTriggered = Dialogues[i].bLooping;
|
||||
else
|
||||
Dialogues[i].bWasTriggered = false;
|
||||
}
|
||||
|
||||
CurrentMsgIdx = 0;
|
||||
bFinished = false;
|
||||
}
|
||||
}
|
||||
|
||||
function int GetNextDlgRequiredEventIdx(int TestIndex)
|
||||
{
|
||||
if ( RequiredEventType == RET_Trigger )
|
||||
return super.GetNextDlgRequiredEventIdx(TestIndex);
|
||||
|
||||
return -1; // for other event types - event is not required, it just enables/disables that dialogue
|
||||
}
|
||||
|
||||
function int GetRandomEnabledDialogueIndex()
|
||||
{
|
||||
local array<int> AvaliableIndicies;
|
||||
local int i;
|
||||
|
||||
for ( i=0; i<Dialogues.length; ++i ) {
|
||||
if ( !Dialogues[i].bWasTriggered )
|
||||
AvaliableIndicies[AvaliableIndicies.Length] = i;
|
||||
}
|
||||
|
||||
if ( AvaliableIndicies.length == 0 )
|
||||
return -1;
|
||||
if ( AvaliableIndicies.length == 1)
|
||||
return 0;
|
||||
return AvaliableIndicies[RandRange(0,AvaliableIndicies.length)];
|
||||
}
|
||||
|
||||
function TraverseDialogue()
|
||||
{
|
||||
local float DisplayDur;
|
||||
|
||||
if ( RequiredEventType == RET_Trigger ) {
|
||||
super.TraverseDialogue();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Hit the end. early out */
|
||||
if( bTraversing || bFinished )
|
||||
return;
|
||||
|
||||
if(bRandomize) {
|
||||
CurrentMsgIdx = GetRandomEnabledDialogueIndex();
|
||||
if ( CurrentMsgIdx == -1 ) {
|
||||
OnDialogueCompleted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( CurrentMsgIdx >= Dialogues.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ( CurrentMsgIdx < Dialogues.length && Dialogues[CurrentMsgIdx].bWasTriggered )
|
||||
CurrentMsgIdx++;
|
||||
|
||||
if ( CurrentMsgIdx >= Dialogues.length ) {
|
||||
OnDialogueCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(bDebugDialogue)
|
||||
{
|
||||
log("----- DIALOGUE DEBUG ------ Showing Dialogue at index .. "@CurrentMsgIdx, 'Story_Debug');
|
||||
}
|
||||
ShowDialogue(CurrentMsgIdx);
|
||||
|
||||
if( !bRandomize ) {
|
||||
if(CurrentMsgIdx + 1 < Dialogues.length )
|
||||
{
|
||||
bTraversing = true;
|
||||
|
||||
DisplayDur = GetCurrentDisplayDur();
|
||||
if(DisplayDur > 0)
|
||||
{
|
||||
SetTimer(DisplayDur,false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Timer();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we're finished. Reset the actor */
|
||||
OnDialogueCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Timer()
|
||||
{
|
||||
if ( RequiredEventType == RET_Trigger ) {
|
||||
super.Timer();
|
||||
return;
|
||||
}
|
||||
|
||||
if(bTraversing) {
|
||||
bTraversing = false;
|
||||
CurrentMsgIdx++ ;
|
||||
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 array<string> Words;
|
||||
|
||||
if ( DisplayTimePerWord > 0 )
|
||||
Split(InString," ",Words);
|
||||
|
||||
return FClamp(Words.length * DisplayTimePerWord + Len(InString)*DisplayTimePerChar, MinDisplayTime, MaxDisplayTime);
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
DisplayTimePerWord=0.300000
|
||||
MinDisplayTime=3.000000
|
||||
MaxDisplayTime=60.000000
|
||||
}
|
||||
282
kf_sources/ScrnStoryGame/Classes/KF_PlayerDialogue.uc
Normal file
282
kf_sources/ScrnStoryGame/Classes/KF_PlayerDialogue.uc
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
KF_PlayerDialogue
|
||||
--------------------------------------------------------------
|
||||
|
||||
Displayes player's portrait and nickname as title to act as
|
||||
if it is a player to speaks it.
|
||||
|
||||
|
||||
Author : PooSH
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class KF_PlayerDialogue extends KF_DialogueSpot_SE
|
||||
placeable;
|
||||
|
||||
// Trigers when dialogue was triggered but didn't matched player conditions, so it was skipped
|
||||
// You can set SkippedEvent=Tag, to force dialogue displaying, if requirements were not matched.
|
||||
// In that case Intigator will be used as speaker, or random player, if instigated by non-player.
|
||||
var(Events) name SkippedEvent;
|
||||
|
||||
|
||||
var() byte MinPlayerCount;
|
||||
var() byte MaxPlayerCount;
|
||||
var() bool bOnlyAlivePlayers;
|
||||
|
||||
|
||||
/*
|
||||
SpeakerName Unique name which dialogue assosiates with a player matching the following criterias.
|
||||
If player is already assigned to a local name, then he will be used. Otherwice
|
||||
player lookup will be made and the result assigned to a local name.
|
||||
ExcludeNames list of local names that can not be used for the current dialogue
|
||||
bUseInstigator Use dialogue's instigator (DialogueInstigator) for the current dialogue.
|
||||
bSetInstigator Change dialogue's instigator to the chosen speaker
|
||||
bForceDisplayEvents If true, DisplayingEvent and DisplayedEvent will be triggered even if dialogue is skipped.
|
||||
Filters Array of DialoguePlayerFilter instances, each of them returns a number between 0 and defined Value
|
||||
depending of specific player stats. Player with highest total value will be chosen for speaking.
|
||||
MinFilterValue Players with filter value < MinFilterValue are skipped and can't be used for displaying the dialogue.
|
||||
MinFilterValue=0 (default) turns off min value filtering! Use MinFilterValue=0.000001 to set it next to 0.
|
||||
|
||||
If no players found with a given criteria, dialogue will not be displayed, so use it with caution!
|
||||
|
||||
WARNING! If both bUseInstigator=True and SpeakerName was set, then use priority depends from bSetInstigator:
|
||||
If bSetInstigator=True, then SpeakerName has higher priority,
|
||||
i.e. speaker will be chosen by name and set as instigator
|
||||
If bSetInstigator=False, then bUseInstigator has higher priority,
|
||||
i.e. instigator will be used as a speaker, overriding the value stored by SpeakerName
|
||||
*/
|
||||
struct SDialogueFilter
|
||||
{
|
||||
var() name SpeakerName;
|
||||
var() name ExcludeNames[5];
|
||||
var() bool bUseInstigator;
|
||||
var() bool bSetInstigator;
|
||||
var() bool bForceDisplayEvents;
|
||||
var() export editinline Array<DialoguePlayerFilter> Filters;
|
||||
var() float MinFilterValue;
|
||||
|
||||
};
|
||||
var() array<SDialogueFilter> DialogueFilters;
|
||||
|
||||
var protected NamedPRITable NamedPRIs;
|
||||
|
||||
var PlayerDialogueReplicationInfo PlayerDialogueReplicationInfo;
|
||||
|
||||
|
||||
function PostBeginPlay()
|
||||
{
|
||||
local int i;
|
||||
|
||||
super.PostBeginPlay();
|
||||
|
||||
NamedPRIs = NamedPRITable(class'NamedPRITable'.static.InitTable(Level));
|
||||
|
||||
if ( Level.NetMode != NM_Standalone ) {
|
||||
PlayerDialogueReplicationInfo = spawn(class'PlayerDialogueReplicationInfo', self);
|
||||
if ( PlayerDialogueReplicationInfo != none ) {
|
||||
PlayerDialogueReplicationInfo.DialogueActorName = self.name;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i=0; i<Dialogues.length; ++i )
|
||||
Dialogues[i].Display.Dialogue_Header = ""; // names of speaking players will be put here later
|
||||
}
|
||||
|
||||
function GetEvents(out array<name> TriggeredEvents, out array<name> ReceivedEvents)
|
||||
{
|
||||
super.GetEvents(TriggeredEvents, ReceivedEvents);
|
||||
|
||||
if ( SkippedEvent != '' )
|
||||
ReceivedEvents[ReceivedEvents.length] = SkippedEvent;
|
||||
}
|
||||
|
||||
function DialogueSkipped()
|
||||
{
|
||||
if ( SkippedEvent != '' )
|
||||
TriggerEvent(SkippedEvent, self, GetInstigator());
|
||||
}
|
||||
|
||||
function Pawn GetInstigator()
|
||||
{
|
||||
if ( DialogueInstigator == none )
|
||||
return none;
|
||||
|
||||
return DialogueInstigator.Pawn;
|
||||
}
|
||||
|
||||
function RetrieveAllPRIs(out array<PlayerReplicationInfo> PRIs)
|
||||
{
|
||||
local Controller C;
|
||||
|
||||
PRIs.length = 0;
|
||||
for ( C = Level.ControllerList; C != None; C = C.NextController ) {
|
||||
if ( C.PlayerReplicationInfo != none && PlayerController(C) != none
|
||||
&& !C.PlayerReplicationInfo.bOnlySpectator
|
||||
&& (!bOnlyAlivePlayers || (C.Pawn != none && !C.Pawn.bDeleteMe && C.Pawn.Health > 0) ) )
|
||||
PRIs[PRIs.length] = C.PlayerReplicationInfo;
|
||||
}
|
||||
}
|
||||
|
||||
function PlayerReplicationInfo GetSpeakingPRI(int DlgIdx)
|
||||
{
|
||||
local int i, j;
|
||||
local array<PlayerReplicationInfo> PRIs;
|
||||
local PlayerReplicationInfo result, NamedPRI;
|
||||
local float TopValue, FilteredValue;
|
||||
local array<float> FilteredValues;
|
||||
|
||||
if ( DlgIdx < DialogueFilters.length ) {
|
||||
// if PRI is assigned to SpeakerName already - use it
|
||||
if ( DialogueFilters[DlgIdx].SpeakerName != '' );
|
||||
NamedPRI = NamedPRIs.GetPRI(DialogueFilters[DlgIdx].SpeakerName);
|
||||
|
||||
if ( DialogueFilters[DlgIdx].bUseInstigator ) {
|
||||
if ( PlayerController(DialogueInstigator) != none && DialogueInstigator.PlayerReplicationInfo != none )
|
||||
result = DialogueInstigator.PlayerReplicationInfo;
|
||||
if ( NamedPRI != none && NamedPRI != result ) {
|
||||
// if bSetInstigator=True, then SpeakerName has higher priority than instigator
|
||||
if ( result == none || DialogueFilters[DlgIdx].bSetInstigator ) {
|
||||
result = NamedPRI;
|
||||
if ( DialogueFilters[DlgIdx].bSetInstigator )
|
||||
DialogueInstigator = PlayerController(NamedPRI.Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( NamedPRI != none )
|
||||
result = NamedPRI;
|
||||
else {
|
||||
RetrieveAllPRIs(PRIs);
|
||||
// removed excluded names
|
||||
for ( i=0; i<5; ++i) {
|
||||
if ( DialogueFilters[DlgIdx].ExcludeNames[i] != '' ) {
|
||||
NamedPRI = NamedPRIs.GetPRI(DialogueFilters[DlgIdx].ExcludeNames[i]);
|
||||
if ( NamedPRI != none ) {
|
||||
for ( j=0; j<PRIs.length; ++j )
|
||||
if ( PRIs[j] == NamedPRI )
|
||||
PRIs.remove(j--, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
NamedPRI = none; //reset value
|
||||
|
||||
// now PRIs contains only valid speakers
|
||||
if ( DialogueFilters[DlgIdx].Filters.length > 0 ) {
|
||||
TopValue = DialogueFilters[DlgIdx].MinFilterValue;
|
||||
// calculate total filtered value of each PRI
|
||||
for ( j=0; j<PRIs.length; ++j ) {
|
||||
FilteredValue = 0;
|
||||
for ( i=0; i<DialogueFilters[DlgIdx].Filters.length; ++i )
|
||||
FilteredValue += DialogueFilters[DlgIdx].Filters[i].CalcPlayerValue(self, DlgIdx, PRIs[j]);
|
||||
FilteredValues[j] = FilteredValue;
|
||||
if ( FilteredValue > TopValue || (j == 0 && TopValue == 0.f) )
|
||||
TopValue = FilteredValue;
|
||||
}
|
||||
// leave only PRIs with TopValue
|
||||
for ( j=0; j<PRIs.length; ++j ) {
|
||||
if ( TopValue - FilteredValues[j] > 0.01 ) {
|
||||
PRIs.remove(j, 1);
|
||||
FilteredValues.remove(j, 1);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = PRIs[rand(PRIs.length)];
|
||||
}
|
||||
}
|
||||
if ( DialogueFilters[DlgIdx].SpeakerName != '' && result != NamedPRI ) {
|
||||
NamedPRI = result;
|
||||
NamedPRIs.SetPRI(DialogueFilters[DlgIdx].SpeakerName, NamedPRI);
|
||||
}
|
||||
}
|
||||
else {
|
||||
RetrieveAllPRIs(PRIs);
|
||||
result = PRIs[rand(PRIs.length)];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function bool RequirementsMatched()
|
||||
{
|
||||
local int PlayerCount;
|
||||
|
||||
if ( bOnlyAlivePlayers )
|
||||
PlayerCount = KFStoryGameInfo(Level.Game).GetTotalActivePlayers();
|
||||
else
|
||||
PlayerCount = Level.Game.NumPlayers;
|
||||
|
||||
return PlayerCount >= MinPlayerCount && (PlayerCount <= MaxPlayerCount || MaxPlayerCount == 0);
|
||||
}
|
||||
|
||||
// set protrait and title from speaking player, then call TraverseDialogue
|
||||
function TraverseDialogue()
|
||||
{
|
||||
local bool bOriginalRandomize;
|
||||
|
||||
if( Dialogues.length <= CurrentMsgIdx || bTraversing || (bFinished && !bAllowRepeatDialogue) )
|
||||
return;
|
||||
|
||||
if ( !RequirementsMatched() ) {
|
||||
DialogueSkipped();
|
||||
CurrentMsgIdx = Dialogues.length;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( bRandomize ) {
|
||||
// we need to know dialogue index already
|
||||
bOriginalRandomize = true;
|
||||
bRandomize = false;
|
||||
CurrentMsgIdx = rand(Dialogues.length);
|
||||
}
|
||||
|
||||
SetPlayerDialogue(CurrentMsgIdx, GetSpeakingPRI(CurrentMsgIdx));
|
||||
|
||||
if ( Dialogues[CurrentMsgIdx].Display.Dialogue_Header == "" ) {
|
||||
if ( CurrentMsgIdx < DialogueFilters.length && DialogueFilters[CurrentMsgIdx].bForceDisplayEvents ) {
|
||||
if ( Dialogues[CurrentMsgIdx].Events.DisplayingEvent != '' )
|
||||
TriggerEvent(Dialogues[CurrentMsgIdx].Events.DisplayingEvent,self,GetInstigator());
|
||||
if ( Dialogues[CurrentMsgIdx].Events.DisplayedEvent != '' )
|
||||
TriggerEvent(Dialogues[CurrentMsgIdx].Events.DisplayedEvent,self,GetInstigator());
|
||||
}
|
||||
if ( !bOriginalRandomize ) {
|
||||
CurrentMsgIdx++;
|
||||
TraverseDialogue();
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.TraverseDialogue();
|
||||
}
|
||||
|
||||
bRandomize = bOriginalRandomize;
|
||||
}
|
||||
|
||||
function SetPlayerDialogue(int DlgIdx, PlayerReplicationInfo SpeakerPRI)
|
||||
{
|
||||
if ( DlgIdx < 0 || DlgIdx >= Dialogues.length )
|
||||
return;
|
||||
|
||||
if ( SpeakerPRI != none ) {
|
||||
Dialogues[DlgIdx].Display.Portrait_Material = SpeakerPRI.GetPortrait();
|
||||
Dialogues[DlgIdx].Display.Dialogue_Header = SpeakerPRI.PlayerName;
|
||||
//avoid skipping a dialogue in cases when player doesn't have a name (is it possible?)
|
||||
if ( Dialogues[DlgIdx].Display.Dialogue_Header == "" )
|
||||
Dialogues[DlgIdx].Display.Dialogue_Header = "Fresh Meat";
|
||||
|
||||
if ( PlayerDialogueReplicationInfo != none ) {
|
||||
PlayerDialogueReplicationInfo.DlgIndex = DlgIdx;
|
||||
PlayerDialogueReplicationInfo.SpeakerPRI = SpeakerPRI;
|
||||
PlayerDialogueReplicationInfo.NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Dialogues[DlgIdx].Display.Portrait_Material = none;
|
||||
Dialogues[DlgIdx].Display.Dialogue_Header = "";
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bOnlyAlivePlayers=True
|
||||
}
|
||||
353
kf_sources/ScrnStoryGame/Classes/KF_StoryNPC_Spawnable_SE.uc
Normal file
353
kf_sources/ScrnStoryGame/Classes/KF_StoryNPC_Spawnable_SE.uc
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
class KF_StoryNPC_Spawnable_SE extends KF_RingMasterNPC;
|
||||
|
||||
var(Bleed) bool bBleed; // should NPC bleed at all?
|
||||
var(Bleed) float MinBleedDamage, MaxBleedDamage; // minand max amount of health that NPC looses per bleed
|
||||
|
||||
var(KF_StoryNPC) bool bActivateOnHealing;
|
||||
var(KF_StoryNPC) bool bActivateOnHealingOnlyOnce;
|
||||
var(KF_StoryNPC) float HealthPctToActivate;
|
||||
|
||||
var(Events) name DeathEvent;
|
||||
var(Events) name UseEvent;
|
||||
|
||||
var(KF_StoryNPC) bool bHealthBarOnlyIfVisible; // draw HP bar only if player can see NPC
|
||||
var(KF_StoryNPC) float HealthBarMaxDistance;
|
||||
var protected float HealthBarMaxDistanceSquared;
|
||||
|
||||
var(KF_StoryNPC) float ZombieDamageReduction;
|
||||
|
||||
var bool bUseCoverAnim;
|
||||
|
||||
// display message when touching by a player
|
||||
var() localized string TouchMessage;
|
||||
var float NextTouchMessageTime;
|
||||
var() name TouchMessageType;
|
||||
|
||||
|
||||
var Touchable Touchable;
|
||||
var() bool bTouchable; // players can touch me, i.e. call Touch() event
|
||||
var() bool bUsable; // players can use me, i.e. press <use> button on me, calling UsedBy() event
|
||||
|
||||
|
||||
// used in AssessThreatTo()
|
||||
var protected transient KFMonsterController LastThreatMonster;
|
||||
var protected transient float LastThreatTime, LastThreat;
|
||||
|
||||
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
super.PostBeginPlay();
|
||||
|
||||
HealthBarMaxDistanceSquared = Square(HealthBarMaxDistance);
|
||||
|
||||
if ( bTouchable || bUsable )
|
||||
SpawnTouchable();
|
||||
}
|
||||
|
||||
function GetEvents(out array<name> TriggeredEvents, out array<name> ReceivedEvents)
|
||||
{
|
||||
super.GetEvents(TriggeredEvents, ReceivedEvents);
|
||||
|
||||
if( DeathEvent != '' )
|
||||
TriggeredEvents[TriggeredEvents.length] = DeathEvent;
|
||||
if( UseEvent != '' )
|
||||
TriggeredEvents[TriggeredEvents.length] = UseEvent;
|
||||
}
|
||||
|
||||
simulated function Tick(float DeltaTime)
|
||||
{
|
||||
if( bBleed && bActive && Level.TimeSeconds - LastBleedOutTime > BleedOutInterval) {
|
||||
TakeDamage(RandRange(MinBleedDamage, MaxBleedDamage),self,Location + Vect(0,0,1)*CollisionHeight,vect(0,0,0),class'DamTypeBleedFixed');
|
||||
LastBleedOutTime = Level.TimeSeconds;
|
||||
}
|
||||
|
||||
Super(KF_StoryNPC_Spawnable).Tick(DeltaTime);
|
||||
|
||||
// do we really need this? -- PooSH
|
||||
if ( Controller == none || Controller.Enemy == none ) {
|
||||
/* Update his idle state to reflect his injuries , etc. */
|
||||
IdleRestAnim = GetIdleAnim();
|
||||
IdleWeaponAnim = IdleRestAnim;
|
||||
}
|
||||
}
|
||||
|
||||
simulated function AddHealth()
|
||||
{
|
||||
super.AddHealth();
|
||||
|
||||
if ( bActivateOnHealing && !bActive && float(Health)/HealthMax >= HealthPctToActivate ) {
|
||||
SetActive(true);
|
||||
if ( bActivateOnHealingOnlyOnce )
|
||||
bActivateOnHealing = false;
|
||||
}
|
||||
}
|
||||
|
||||
simulated function name GetIdleAnim()
|
||||
{
|
||||
if( !bActive )
|
||||
{
|
||||
return IdleSeatedAnim;
|
||||
}
|
||||
|
||||
return IdleStandingInjuredAnim;
|
||||
}
|
||||
|
||||
function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
|
||||
{
|
||||
if( DeathEvent != '' )
|
||||
{
|
||||
TriggerEvent(DeathEvent,Killer,self);
|
||||
}
|
||||
|
||||
if ( Touchable != none )
|
||||
Touchable.Destroy();
|
||||
|
||||
super.Died(Killer, damageType, HitLocation);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
&& (HealthBarMaxDistanceSquared == 0 || VSizeSquared(PC.Location - Location) < HealthBarMaxDistanceSquared)
|
||||
&& (!bHealthBarOnlyIfVisible || PC.CanSee(self)) )
|
||||
{
|
||||
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 TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType, optional int HitIdx )
|
||||
{
|
||||
// Reduce damage for zombie attacks
|
||||
if( (class<DamTypeZombieAttack>(damageType) != none || class<DamTypeBurned>(damageType) != none
|
||||
|| class<DamTypeLawRocketImpact>(damageType) != none) && Damage * ZombieDamageReduction >= 1 )
|
||||
{
|
||||
Damage *= ZombieDamageReduction;
|
||||
}
|
||||
|
||||
// Cancel out the 5 we are going to lose when TakeDamage is called in the super
|
||||
// This is done so that healing works better for the Ringmaster, as his constant
|
||||
// bleedout is making healing not work well
|
||||
if( damageType == class'DamTypeBleedOut' && healthToGive > 0 )
|
||||
{
|
||||
healthtoGive+=5;
|
||||
}
|
||||
|
||||
//log("Taking "$Damage$" damage of type "$damageType);
|
||||
|
||||
// pushing the Ringmaster off his path can be problematic, so zero the momentum
|
||||
super(KF_StoryNPC).TakeDamage( Damage, instigatedBy, hitlocation, vect(0,0,0), damageType, HitIdx );
|
||||
}
|
||||
|
||||
simulated function Destroyed()
|
||||
{
|
||||
if ( Touchable != none )
|
||||
Touchable.Destroy();
|
||||
|
||||
super.Destroyed();
|
||||
}
|
||||
|
||||
function SpawnTouchable()
|
||||
{
|
||||
if ( Touchable != none )
|
||||
return;
|
||||
|
||||
Touchable = Spawn(class'Touchable');
|
||||
Touchable.SetMyPawn(self);
|
||||
Touchable.bTouchable = bTouchable;
|
||||
Touchable.bUsable = bUsable;
|
||||
}
|
||||
|
||||
|
||||
function Touch( Actor Other )
|
||||
{
|
||||
local Pawn P;
|
||||
|
||||
if( TouchMessage != "" && Level.TimeSeconds > NextTouchMessageTime ) {
|
||||
// Send a string message to the toucher.
|
||||
P = Pawn(Other);
|
||||
if ( P != None && P.Controller != none && P.Controller.CanSee(self) )
|
||||
{
|
||||
P.ClientMessage( TouchMessage, TouchMessageType );
|
||||
NextTouchMessageTime = Level.TimeSeconds + 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function UsedBy( Pawn user )
|
||||
{
|
||||
if ( KFHumanPawn(user) != none && PlayerController(user.Controller) != none ) {
|
||||
if ( UseEvent != '' )
|
||||
TriggerEvent(UseEvent, user, self);
|
||||
}
|
||||
}
|
||||
/**
|
||||
Monster threat assessment functionality
|
||||
|
||||
Changes by PooSH:
|
||||
1) KFGameType.bUseZEDThreatAssessment set to true, i.e. in regular game new AssessThreatTo()
|
||||
function will be used too.
|
||||
2) Distance between monster and player will always be in place.
|
||||
3) AssessThreatTo will always return value > 0. Because zeds should not ignore players.
|
||||
4) Added randomization - zeds can choose different targets in the same circumstances.
|
||||
5) Blood smell. Wounded players will attract zeds slightly more than their healthy teammates.
|
||||
|
||||
* @param Monster Monster's controller, for which we are calculating the threat level
|
||||
* @param CheckDistance Not used!
|
||||
* @return threat level between 0 and 100, where 100 is the max threat level.
|
||||
*
|
||||
* @author PooSH
|
||||
*/
|
||||
function float AssessThreatTo(KFMonsterController Monster, optional bool CheckDistance)
|
||||
{
|
||||
local float DistancePart, RandomPart, TacticalPart;
|
||||
local float DistanceSquared; // squared distance is calculated faster
|
||||
|
||||
if ( !Level.Game.IsA('ScrnStoryGameInfo') )
|
||||
return super.AssessThreatTo(Monster, CheckDistance);
|
||||
|
||||
// the following code is for ScrnBalance only
|
||||
|
||||
if(Monster == none || KFMonster(Monster.Pawn) == none)
|
||||
{
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
if(bNoThreatToZEDs ||
|
||||
TeamIndex == 255 ||
|
||||
Health <= 0 ||
|
||||
!bActive ||
|
||||
!bDamageable ||
|
||||
(!IsThreateningTo(Monster.Pawn)) )
|
||||
{
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
if ( LastThreatMonster == Monster && LastThreatTime == Level.TimeSeconds )
|
||||
return LastThreat; // threat level for the given monster has been calculated already during the current tick
|
||||
|
||||
DistanceSquared = VSizeSquared(Monster.Pawn.Location - Location);
|
||||
|
||||
DistancePart = 65.0;
|
||||
RandomPart = 100.0 - DistancePart;
|
||||
// let zeds smell blood within 25m radius - wounded players attract zeds more
|
||||
if ( DistanceSquared < 1562500.0 )
|
||||
TacticalPart += RandomPart * 0.40 * (HealthMax - Health) / HealthMax;
|
||||
// more chance to attack the same enemy multiple times
|
||||
if ( Monster.Enemy == self || Monster.Target == self )
|
||||
TacticalPart += RandomPart * 0.25;
|
||||
// more chance to focus on the player, who are attacking the monster
|
||||
if ( KFMonster(Monster.Pawn).LastDamagedBy == self)
|
||||
TacticalPart += RandomPart * 0.25;
|
||||
RandomPart = 100.0 - DistancePart - TacticalPart;
|
||||
|
||||
// If target is closer than 1 meter, max DistancePart value will be used,
|
||||
// otherwise DistancePart is lowering by 10% per meter
|
||||
// 1 meter = 50 ups (2500 squared)
|
||||
if ( DistanceSquared > 2500.0 )
|
||||
DistancePart /= 1.0 + DistanceSquared / 250000.0;
|
||||
RandomPart *= frand();
|
||||
|
||||
// save threat level for this tick
|
||||
LastThreatMonster = Monster;
|
||||
LastThreatTime = Level.TimeSeconds;
|
||||
LastThreat = DistancePart + TacticalPart + RandomPart;
|
||||
LastThreat *= InventoryThreatModifier();
|
||||
LastThreat += BaseAIThreatRating;
|
||||
return LastThreat;
|
||||
}
|
||||
|
||||
// used in story game mode to attract zeds when holding some mission items
|
||||
function float InventoryThreatModifier()
|
||||
{
|
||||
local float ThreatRating;
|
||||
local Inventory CurInv;
|
||||
local KF_StoryInventoryItem StoryInv;
|
||||
|
||||
ThreatRating = 1.0;
|
||||
|
||||
/* Factor in story Items which adjust your desirability to ZEDs */
|
||||
for ( CurInv = Inventory; CurInv != none; CurInv = CurInv.Inventory ) {
|
||||
StoryInv = KF_StoryInventoryItem(CurInv);
|
||||
if(StoryInv != none)
|
||||
ThreatRating *= StoryInv.AIThreatModifier ;
|
||||
}
|
||||
|
||||
return ThreatRating;
|
||||
}
|
||||
|
||||
simulated function PlayDirectionalHit(Vector HitLoc)
|
||||
{
|
||||
if ( bUseCoverAnim )
|
||||
super.PlayDirectionalHit(HitLoc);
|
||||
else
|
||||
super(KFHumanPawn).PlayDirectionalHit(HitLoc);
|
||||
}
|
||||
|
||||
function SetScriptedAnimData(name BaseAnim, float BlendInTime, float BlendOutTime, float AnimRate,
|
||||
byte AnimIterations, bool bLoopAnim, float StartFrame)
|
||||
{
|
||||
super.SetScriptedAnimData(BaseAnim, BlendInTime, BlendOutTime, AnimRate,
|
||||
AnimIterations, bLoopAnim, StartFrame);
|
||||
// tell server to replicate animation data immediately -- PooSH
|
||||
// but it doesn't work anyway...
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
ZombieDamageReduction=1.000000
|
||||
bUseCoverAnim=True
|
||||
CowerAnim="CHIdle_LAW"
|
||||
IdleSeatedAnim="CHIdle_Knife"
|
||||
IdleStandingAnim="Profile_Idle"
|
||||
IdleStandingInjuredAnim="Profile_Idle"
|
||||
StandUpAnim="Profile_Idle"
|
||||
BleedingSounds(0)=SoundGroup'KF_MaleVoiceOne.Automatic_Commands.Auto_Dying'
|
||||
BleedingSounds(1)=SoundGroup'KF_MaleVoiceOne.Automatic_Commands.Auto_Dying'
|
||||
BleedingSounds(2)=SoundGroup'KF_MaleVoiceOne.SUPPORT.MEDIC'
|
||||
BleedingSounds(3)=SoundGroup'KF_MaleVoiceOne.SUPPORT.MEDIC'
|
||||
BleedingSounds(4)=SoundGroup'KF_MaleVoiceOne.SUPPORT.MEDIC'
|
||||
HitAnims(0)="HitF_Knife"
|
||||
HitAnims(1)="HitB_Knife"
|
||||
HitAnims(2)="HitL_Knife"
|
||||
HitAnims(3)="HitR_Knife"
|
||||
SkeletonMesh=SkeletalMesh'KFSoldiers.Soldier'
|
||||
RagdollOverride="British_Soldier1"
|
||||
MovementAnims(0)="JogF_Knife"
|
||||
MovementAnims(1)="JogB_Knife"
|
||||
MovementAnims(2)="JogL_Knife"
|
||||
MovementAnims(3)="JogR_Knife"
|
||||
IdleChatAnim="Profile_Idle"
|
||||
Mesh=SkeletalMesh'KF_Soldier_Trip.British_Soldier1'
|
||||
}
|
||||
15
kf_sources/ScrnStoryGame/Classes/NPCEnemy_AI.uc
Normal file
15
kf_sources/ScrnStoryGame/Classes/NPCEnemy_AI.uc
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
class NPCEnemy_AI extends NPC_AI;
|
||||
|
||||
function bool SameTeamAs(Controller C)
|
||||
{
|
||||
if( MonsterController(C) != none)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
15
kf_sources/ScrnStoryGame/Classes/NPCFriendly_AI.uc
Normal file
15
kf_sources/ScrnStoryGame/Classes/NPCFriendly_AI.uc
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
class NPCFriendly_AI extends NPC_AI;
|
||||
|
||||
function bool SameTeamAs(Controller C)
|
||||
{
|
||||
if(MonsterController(C) != none)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
128
kf_sources/ScrnStoryGame/Classes/NPC_AI.uc
Normal file
128
kf_sources/ScrnStoryGame/Classes/NPC_AI.uc
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// todo: extend KFInvasionBot, which will support scripting
|
||||
|
||||
class NPC_AI extends ScriptedController;
|
||||
|
||||
var KF_StoryNPC StoryPawn;
|
||||
|
||||
var float ReloadTime; // hack to force magazine update
|
||||
var bool bPendingReload;
|
||||
|
||||
// made NPC not following dead bodies
|
||||
function Pawn GetMyPlayer()
|
||||
{
|
||||
local Controller C;
|
||||
|
||||
if ( MyPlayerController == None || MyPlayerController.Pawn == None || MyPlayerController.Pawn.Health <= 0 ) {
|
||||
for ( C = Level.ControllerList; C != none; C = C.nextController ) {
|
||||
if ( PlayerController(C) != none && C.Pawn != none && C.Pawn.Health > 0 ) {
|
||||
MyPlayerController = PlayerController(C);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( MyPlayerController == None )
|
||||
return None;
|
||||
return MyPlayerController.Pawn;
|
||||
}
|
||||
|
||||
simulated function int GetTeamNum()
|
||||
{
|
||||
if(StoryPawn == none)
|
||||
return 255;
|
||||
else
|
||||
return StoryPawn.TeamIndex;
|
||||
}
|
||||
|
||||
function Possess(Pawn aPawn)
|
||||
{
|
||||
Super.Possess(aPawn);
|
||||
|
||||
StoryPawn = KF_StoryNPC(Pawn);
|
||||
}
|
||||
|
||||
|
||||
state Scripting
|
||||
{
|
||||
// added reloading -- PooSH
|
||||
function bool WeaponFireAgain(float RefireRate, bool bFinishedFire)
|
||||
{
|
||||
local KFWeapon W;
|
||||
|
||||
W = KFWeapon(Pawn.Weapon);
|
||||
|
||||
if ( W != none ) {
|
||||
if ( bPendingReload )
|
||||
return false;
|
||||
|
||||
if ( W.bIsReloading ) {
|
||||
// make sure reload finishes
|
||||
StartWeaponReload();
|
||||
}
|
||||
|
||||
// todo: are there any weapons with MagCapacity=1, which require reload?..
|
||||
if ( W.MagCapacity > 1 && W.MagAmmoRemaining == 0 ) {
|
||||
StopFiring();
|
||||
W.ReloadMeNow();
|
||||
StartWeaponReload();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( Pawn(ScriptedFocus) != none && Pawn(ScriptedFocus).Health <= 0 )
|
||||
ScriptedFocus = none; // stop shooting dead bodies
|
||||
|
||||
return super.WeaponFireAgain(RefireRate, bFinishedFire);
|
||||
}
|
||||
|
||||
function StartWeaponReload()
|
||||
{
|
||||
local KFWeapon W;
|
||||
|
||||
W = KFWeapon(Pawn.Weapon);
|
||||
if ( W == none )
|
||||
return;
|
||||
|
||||
bPendingReload = true;
|
||||
ReloadTime = Level.TimeSeconds + W.ReloadRate + 0.1;
|
||||
Enable('Tick');
|
||||
W.ClientReload();
|
||||
W.Instigator.SetAnimAction(W.WeaponReloadAnim);
|
||||
}
|
||||
|
||||
// lame hack, but it seems like KFWeapon.AllowReload() supports only KFInvasionBot and KFFriendlyAI
|
||||
function ForceWeaponReload()
|
||||
{
|
||||
local KFWeapon W;
|
||||
|
||||
W = KFWeapon(Pawn.Weapon);
|
||||
if ( W == none )
|
||||
return;
|
||||
|
||||
W.AddReloadedAmmo();
|
||||
W.ActuallyFinishReloading();
|
||||
}
|
||||
|
||||
function Tick(float DeltaTime)
|
||||
{
|
||||
if ( bPendingReload ) {
|
||||
if ( Level.TimeSeconds >= ReloadTime ) {
|
||||
bPendingReload = false;
|
||||
ForceWeaponReload();
|
||||
}
|
||||
}
|
||||
else if ( bPendingShoot )
|
||||
{
|
||||
bPendingShoot = false;
|
||||
MayShootTarget();
|
||||
}
|
||||
if ( !bPendingShoot && !bPendingReload
|
||||
&& ((CurrentAction == None) || !CurrentAction.StillTicking(self,DeltaTime)) )
|
||||
disable('Tick');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
66
kf_sources/ScrnStoryGame/Classes/NamedObjectTable.uc
Normal file
66
kf_sources/ScrnStoryGame/Classes/NamedObjectTable.uc
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
class NamedObjectTable extends Info;
|
||||
|
||||
var private array<name> Names;
|
||||
var private array<Object> Objects;
|
||||
|
||||
// looks for the table or creates a new one, if such doesn't exist
|
||||
static function NamedObjectTable InitTable(LevelInfo Level, optional name TableTag)
|
||||
{
|
||||
local Actor Other;
|
||||
|
||||
foreach Level.AllActors(default.class, Other, TableTag)
|
||||
return NamedObjectTable(Other);
|
||||
|
||||
return Level.spawn(default.class, Level, TableTag);
|
||||
}
|
||||
|
||||
|
||||
function ClearTable()
|
||||
{
|
||||
Names.length = 0;
|
||||
Objects.length = 0;
|
||||
}
|
||||
|
||||
function int TableLength()
|
||||
{
|
||||
return Names.Length;
|
||||
}
|
||||
|
||||
// returns Names.length if not foud
|
||||
function protected int GetIndex(name ObjectName)
|
||||
{
|
||||
local int i;
|
||||
|
||||
for ( i=0; i<Names.length; ++i )
|
||||
if ( Names[i] == ObjectName )
|
||||
break;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function SetObject(name ObjectName, Object Object)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = GetIndex(ObjectName);
|
||||
Names[i] = ObjectName;
|
||||
Objects[i] = Object;
|
||||
}
|
||||
|
||||
function Object GetObject(name ObjectName)
|
||||
{
|
||||
local int i;
|
||||
|
||||
i = GetIndex(ObjectName);
|
||||
|
||||
if ( i < Objects.length )
|
||||
return Objects[i];
|
||||
|
||||
return none;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
21
kf_sources/ScrnStoryGame/Classes/NamedPRITable.uc
Normal file
21
kf_sources/ScrnStoryGame/Classes/NamedPRITable.uc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
class NamedPRITable extends NamedObjectTable;
|
||||
|
||||
// ensure that nothing but PlayerReplicationInfo can be put in table
|
||||
function SetObject(name ObjectName, Object Object)
|
||||
{
|
||||
super.SetObject(ObjectName, PlayerReplicationInfo(Object));
|
||||
}
|
||||
|
||||
function SetPRI(name PRI_Name, PlayerReplicationInfo PRI)
|
||||
{
|
||||
super.SetObject(PRI_Name, PRI);
|
||||
}
|
||||
|
||||
function PlayerReplicationInfo GetPRI(name PRI_Name)
|
||||
{
|
||||
return PlayerReplicationInfo(GetObject(PRI_Name));
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
Condition_ActorHealth_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
A Condition which tracks the health state of specified Actor(s)
|
||||
and is marked complete when it drops below a specified threshold.
|
||||
|
||||
Enhancements and fixes:
|
||||
- bSearchOnlyOnce
|
||||
|
||||
Author : PooSH
|
||||
Original Author : Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class ObjCondition_ActorHealth_SE extends ObjCondition_ActorHealth
|
||||
editinlinenew;
|
||||
|
||||
var () bool bSearchOnlyOnce;
|
||||
var transient bool bSearchCompleted;
|
||||
|
||||
|
||||
function Reset()
|
||||
{
|
||||
Super.Reset();
|
||||
HealthMax = 100;
|
||||
}
|
||||
|
||||
function ConditionActivated(pawn ActivatingPlayer)
|
||||
{
|
||||
local Actor MyHealthActor;
|
||||
|
||||
Super.ConditionActivated(ActivatingPlayer);
|
||||
|
||||
MyHealthActor = GetTargetActor(HealthActorName);
|
||||
|
||||
if(MyHealthActor != none && !MyHealthActor.bPendingDelete)
|
||||
bSearchCompleted = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ConditionTick(float DeltaTime)
|
||||
{
|
||||
if ( bSearchOnlyOnce && bSearchCompleted && GetTargetActor(HealthActorName) == none ) {
|
||||
// if bSearchOnlyOnce=true and TargetActor was searchen for and not found,
|
||||
// then count it as dead
|
||||
HealthMax = 100;
|
||||
CurrentHealth = 0;
|
||||
super(KF_ObjectiveCondition).ConditionTick(DeltaTime);
|
||||
}
|
||||
else {
|
||||
super.ConditionTick(DeltaTime);
|
||||
if ( HealthMax <= 0 )
|
||||
HealthMax = 100; // prevents 0/0 showing as 100%
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function UpdatePawnList()
|
||||
{
|
||||
if( bSearchOnlyOnce && bSearchCompleted )
|
||||
return;
|
||||
|
||||
super.UpdatePawnList();
|
||||
bSearchCompleted = true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
193
kf_sources/ScrnStoryGame/Classes/ObjCondition_Area_SE.uc
Normal file
193
kf_sources/ScrnStoryGame/Classes/ObjCondition_Area_SE.uc
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
ObjCondition_Area_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
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.
|
||||
|
||||
Enhancements and fixes:
|
||||
- Fixed GetLocation() ignoring HUD_World.World_Location
|
||||
- Aplied difficulty and player count scaling on duration
|
||||
- LastInAreaTime, LastOutOfAreaTime, bTimingOut changed to
|
||||
RequirementsMatchedTime and bRequirementsMatched
|
||||
- Added bCheckDependentsFirst
|
||||
|
||||
|
||||
Author : PooSH
|
||||
Original Author: Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class ObjCondition_Area_SE extends ObjCondition_Area
|
||||
editinlinenew;
|
||||
|
||||
/**
|
||||
EAreaInstigator - determines which pawn must be used as Instigator:
|
||||
Instigator_Triggered Instigator stays the one which triggered this area or activated this objective.
|
||||
Pawns inside this area do not affect Instigator value.
|
||||
This is default behavior of ObjCondition_Area
|
||||
Instigator_First Instigator will be set to a pawn, which entered the area first.
|
||||
If Instigator leaves the area, the next pawn in the area will be used
|
||||
as Instigator and so on.
|
||||
Instigator_Last Instigator will be set to a pawn, which entered the area first.
|
||||
*/
|
||||
|
||||
/*
|
||||
enum EAreaInstigator
|
||||
{
|
||||
Instigator_Triggered,
|
||||
Instigator_First,
|
||||
Instigator_Last
|
||||
};
|
||||
var() EAreaInstigator AreaInstigator;
|
||||
*/
|
||||
|
||||
// if true, DependentConditions will be checked before the Duration
|
||||
var(KF_ObjectiveCondition) bool bCheckDependentsFirst;
|
||||
// call DependentConditions[].FindInstigator()?
|
||||
var(KF_ObjectiveCondition) bool bCheckDependentsInstigator;
|
||||
// Time since when requiremets are matched
|
||||
// 0 - requrements are not matched at the current moment
|
||||
var float RequirementsMatchedTime;
|
||||
var bool bRequirementsMatched;
|
||||
|
||||
var protected float OriginalDuration;
|
||||
|
||||
|
||||
function PostBeginPlay(KF_StoryObjective MyOwner)
|
||||
{
|
||||
Super.PostBeginPlay(MyOwner);
|
||||
|
||||
OriginalDuration = Duration;
|
||||
}
|
||||
|
||||
|
||||
function Reset()
|
||||
{
|
||||
Super.Reset();
|
||||
bRequirementsMatched = false;
|
||||
RequirementsMatchedTime = 0;
|
||||
}
|
||||
|
||||
|
||||
function ConditionActivated(pawn ActivatingPlayer)
|
||||
{
|
||||
Super.ConditionActivated(ActivatingPlayer);
|
||||
|
||||
Duration = OriginalDuration * GetTotalDifficultyModifier();
|
||||
bRequirementsMatched = false;
|
||||
RequirementsMatchedTime = 0;
|
||||
}
|
||||
|
||||
function ConditionTick(Float DeltaTime)
|
||||
{
|
||||
super.ConditionTick(DeltaTime);
|
||||
|
||||
if ( CompletionMethod == Method_EnterArea )
|
||||
bRequirementsMatched = NumInVolume > 0
|
||||
&& (!bRequiresWholeTeam || NumInVolume >= GetObjOwner().StoryGI.GetTotalActivePlayers());
|
||||
else
|
||||
bRequirementsMatched = NumInVolume == 0
|
||||
|| (!bRequiresWholeTeam && NumInVolume < GetObjOwner().StoryGI.GetTotalActivePlayers());
|
||||
|
||||
if (bRequirementsMatched) {
|
||||
if (bCheckDependentsFirst && !AllowCompletion())
|
||||
RequirementsMatchedTime = 0.f;
|
||||
else if ( RequirementsMatchedTime == 0.f )
|
||||
RequirementsMatchedTime = GetObjOwner().Level.TimeSeconds;
|
||||
}
|
||||
else
|
||||
RequirementsMatchedTime = 0.f;
|
||||
}
|
||||
|
||||
/* returns the percentage of completion for this condition */
|
||||
function float GetCompletionPct()
|
||||
{
|
||||
if ( !bRequirementsMatched || RequirementsMatchedTime == 0.f )
|
||||
return 0.f;
|
||||
|
||||
if ( Duration == 0.f )
|
||||
return float(bRequirementsMatched);
|
||||
|
||||
return FClamp((GetObjOwner().Level.TimeSeconds - RequirementsMatchedTime) / Duration,0.f,1.f);
|
||||
}
|
||||
|
||||
|
||||
function string GetDataString()
|
||||
{
|
||||
local string DataString;
|
||||
|
||||
/* if( (CompletionMethod == Method_StayInArea && bTimingOut) ||
|
||||
(CompletionMethod == Method_EnterArea && !bTimingOut) )
|
||||
{
|
||||
DataString = FormatTime(FMax(Duration - (GetObjOwner().Level.TimeSeconds - LastInAreaTime),0.f)) ;
|
||||
}
|
||||
*/
|
||||
|
||||
if(bRequiresWholeTeam)
|
||||
{
|
||||
DataString@="["$NumInVolume$"/"$GetObjOwner().StoryGI.GetTotalActivePlayers()$"]" ;
|
||||
}
|
||||
|
||||
return DataString ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Overriden to skip inactive dependencies -- PooSH */
|
||||
function bool AllowCompletion()
|
||||
{
|
||||
local int i;
|
||||
local array<Pawn> MyInstigators;
|
||||
|
||||
if ( DependentConditions.length > 0 ) {
|
||||
MyInstigators = GetInstigatorList();
|
||||
for(i = 0 ; i < DependentConditions.length ; i ++) {
|
||||
if ( DependentConditions[i].ConditionIsActive() ) {
|
||||
if( !DependentConditions[i].bComplete
|
||||
|| (bCheckDependentsInstigator && !DependentConditions[i].FindInstigator(MyInstigators)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// fixes bug when location of KF_StoryObjective is returned instead of Volume
|
||||
// -- PooSH
|
||||
function vector GetLocation(optional out Actor LocActor)
|
||||
{
|
||||
local Volume MyAreaVolume;
|
||||
|
||||
if(ConditionIsActive())
|
||||
{
|
||||
// first look if actor is defined in HUD world settings
|
||||
if ( GetWorldLocActor(LocActor) )
|
||||
return LocActor.Location;
|
||||
|
||||
// if HUD world actor is not defined - try to use Volume, then - Zone
|
||||
MyAreaVolume = Volume(GetTargetActor(AreaVolumeName));
|
||||
if(MyAreaVolume != none) {
|
||||
LocActor = MyAreaVolume;
|
||||
return LocActor.Location;
|
||||
}
|
||||
// AssociatedZone is private and has no getter (including GetTargetActor())
|
||||
// else if( AreaZoneName != "" && AssociatedZone != none ) {
|
||||
// LocActor = AssociatedZone;
|
||||
// return LocActor.Location;
|
||||
// }
|
||||
}
|
||||
|
||||
// return zero vector instead, if nothing found (not the KF_StoryObjective.Location)
|
||||
return vect(0,0,0);
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bCheckDependentsInstigator=True
|
||||
}
|
||||
66
kf_sources/ScrnStoryGame/Classes/ObjCondition_Counter_SE.uc
Normal file
66
kf_sources/ScrnStoryGame/Classes/ObjCondition_Counter_SE.uc
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
ObjCondition_Counter_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
Enhanced version if ObjCondition_Counter.
|
||||
|
||||
Allows to disable condition reset() on deactivation.
|
||||
Usefull if condition needs to be activated/deactivated multiple
|
||||
times during the mission.
|
||||
|
||||
- Counting disabled when objective is not active - fixed in KF v1054 too
|
||||
- Completion count doesn't get lowered after player death
|
||||
- ConditionIsValid() returns false if ObjOwner is none
|
||||
|
||||
Author : PooSH
|
||||
Original Author : Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
class ObjCondition_Counter_SE extends ObjCondition_Counter
|
||||
editinlinenew;
|
||||
|
||||
var(ObjCondition_Counter) bool bResetOnDeactivation;
|
||||
|
||||
var transient float TotalDifficultyModifier;
|
||||
|
||||
function ConditionDeActivated()
|
||||
{
|
||||
if ( bResetOnDeactivation )
|
||||
Reset();
|
||||
else {
|
||||
bActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// don't let counter lower after player death
|
||||
function float GetTotalDifficultyModifier()
|
||||
{
|
||||
if ( TotalDifficultyModifier == 0 )
|
||||
TotalDifficultyModifier = super.GetTotalDifficultyModifier();
|
||||
return TotalDifficultyModifier;
|
||||
}
|
||||
|
||||
function Reset()
|
||||
{
|
||||
super.Reset();
|
||||
TotalDifficultyModifier = 0;
|
||||
}
|
||||
|
||||
// Dunno why, but sometimes it is called if condition is inactive (even if other or none objective is running)
|
||||
function bool ConditionIsValid()
|
||||
{
|
||||
if ( GetObjOwner() == none )
|
||||
return false; // wtf?
|
||||
|
||||
return super.ConditionIsValid();
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bResetOnDeactivation=True
|
||||
}
|
||||
157
kf_sources/ScrnStoryGame/Classes/ObjCondition_Inventory_SE.uc
Normal file
157
kf_sources/ScrnStoryGame/Classes/ObjCondition_Inventory_SE.uc
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
ObjCondition_InventoryInstigator
|
||||
--------------------------------------------------------------
|
||||
|
||||
A Condition which is marked complete when instigator is
|
||||
carrying an inventory item of the specified class.
|
||||
|
||||
Author : PooSH
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
class ObjCondition_Inventory_SE extends ObjCondition_Inventory
|
||||
editinlinenew;
|
||||
|
||||
/* Array of pawns which are currently holding this item in iventory */
|
||||
var protected array<Pawn> PawnInstigators;
|
||||
|
||||
/**
|
||||
EItemDestoyMode Allows to auto-destroy the item in inventory.
|
||||
Item will be destroyer only from Instigator's inventory.
|
||||
If multiple paws have the same inventory, and you want to destroy
|
||||
all of them, use ACTION_DropInventory instead
|
||||
|
||||
IDM_DontDestroy Default setting. Inventory will not be auto-destroyed.
|
||||
IDM_DestroyOnComplete Inventory will be destroyed on completion.
|
||||
Can be used when condition has DependentConditions,
|
||||
e.g. bring inventory to the area
|
||||
IDM_DestroyOnTrigger Inventory will be destroyed on trigger received.
|
||||
Can be used when this condition is in DependentConditions
|
||||
of parent condition, and parent condition triggers the event
|
||||
on completion.
|
||||
e.g. use an actor while holding the inventory
|
||||
*/
|
||||
enum EItemDestoyMode
|
||||
{
|
||||
IDM_DontDestroy, // default setting - do not destroy inventory
|
||||
IDM_DestroyOnComplete, // auto destroy inventory on condition completion.
|
||||
IDM_DestroyOnTrigger // destroy inventory on trigger, if condition complete
|
||||
};
|
||||
var(ObjCondition_Inventory) EItemDestoyMode ItemDestoyMode;
|
||||
|
||||
// call DependentConditions[].FindInstigator()?
|
||||
var(KF_ObjectiveCondition) bool bCheckDependentsInstigator;
|
||||
|
||||
|
||||
function Trigger(actor Other, pawn EventInstigator)
|
||||
{
|
||||
super.Trigger(Other, EventInstigator);
|
||||
|
||||
if ( ItemDestoyMode == IDM_DestroyOnTrigger && ConditionIsValid()
|
||||
&& DestroyInventoryItem(EventInstigator) )
|
||||
ConditionTick(0);
|
||||
}
|
||||
|
||||
function Reset()
|
||||
{
|
||||
Super.Reset();
|
||||
PawnInstigators.length = 0;
|
||||
}
|
||||
|
||||
|
||||
function bool DestroyInventoryItem(Pawn Holder)
|
||||
{
|
||||
local Inventory DelItem;
|
||||
|
||||
DelItem = FindDesiredItem(Holder);
|
||||
if (DelItem == none)
|
||||
return false;
|
||||
|
||||
if ( KFHumanPawn_Story(Holder) != none)
|
||||
KFHumanPawn_Story(Holder).SetHasStoryItem( false );
|
||||
|
||||
if(KF_StoryInventoryItem(DelItem) != none)
|
||||
KF_StoryInventoryItem(DelItem).UpdateHeldMaterial(Holder, none);
|
||||
|
||||
DelItem.DetachFromPawn(Holder);
|
||||
Holder.DeleteInventory(DelItem);
|
||||
DelItem.Destroy();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function array<Pawn> GetInstigatorList()
|
||||
{
|
||||
return PawnInstigators;
|
||||
}
|
||||
|
||||
function ConditionTick(float DeltaTime)
|
||||
{
|
||||
local Controller C;
|
||||
local Pawn MyInstigator;
|
||||
local bool bInstigatorFound;
|
||||
|
||||
PawnInstigators.length = 0;
|
||||
MyInstigator = GetInstigator();
|
||||
|
||||
for ( C=GetObjOwner().Level.ControllerList; C!=None; C=C.NextController ) {
|
||||
if(PlayerController(C) != none && C.Pawn != none && FindDesiredItem(C.Pawn) != none ) {
|
||||
PawnInstigators[PawnInstigators.length] = C.Pawn;
|
||||
bInstigatorFound = bInstigatorFound || MyInstigator == C.Pawn;
|
||||
}
|
||||
}
|
||||
bHeld = PawnInstigators.length > 0;
|
||||
if ( bHeld && !bInstigatorFound ) {
|
||||
MyInstigator = PawnInstigators[0];
|
||||
SetTargetActor(InstigatorName, MyInstigator);
|
||||
}
|
||||
|
||||
Super(KF_ObjectiveCondition).ConditionTick(DeltaTime);
|
||||
|
||||
if ( bComplete && ItemDestoyMode == IDM_DestroyOnComplete )
|
||||
DestroyInventoryItem(GetInstigator());
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Inventory FindDesiredItem(Pawn Holder)
|
||||
{
|
||||
local Inventory Inv;
|
||||
|
||||
if ( Holder == none )
|
||||
return none;
|
||||
|
||||
for( Inv = Holder.Inventory; Inv != None ; Inv = Inv.Inventory ) {
|
||||
if( ClassIsChildOf(Inv.class,DesiredItemClass)
|
||||
&& (DesiredItemTag == '' || Inv.tag == DesiredItemTag) )
|
||||
return Inv;
|
||||
}
|
||||
return none;
|
||||
}
|
||||
|
||||
/* Overriden to skip inactive dependencies -- PooSH */
|
||||
function bool AllowCompletion()
|
||||
{
|
||||
local int i;
|
||||
local array<Pawn> MyInstigators;
|
||||
|
||||
if ( DependentConditions.length > 0 ) {
|
||||
MyInstigators = GetInstigatorList();
|
||||
for(i = 0 ; i < DependentConditions.length ; i ++) {
|
||||
if ( DependentConditions[i].ConditionIsActive() ) {
|
||||
if( !DependentConditions[i].bComplete
|
||||
|| (bCheckDependentsInstigator && !DependentConditions[i].FindInstigator(MyInstigators)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bCheckDependentsInstigator=True
|
||||
}
|
||||
142
kf_sources/ScrnStoryGame/Classes/ObjCondition_Multi_SE.uc
Normal file
142
kf_sources/ScrnStoryGame/Classes/ObjCondition_Multi_SE.uc
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
Condition_Use_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
Enhanced version if ObjCondition_Multi.
|
||||
|
||||
This Condition is marked complete when a player presses the 'Use'
|
||||
key while in range of its owning actor.
|
||||
|
||||
Enhancements:
|
||||
- Automatically skips invalid conditions (e.g. which didn't matched player counts)
|
||||
- Option to skip inactive conditions (i.e.
|
||||
it can be completed even if inactive conditons are incomplete)
|
||||
- Allow to enable/disable child condition on trigger
|
||||
|
||||
|
||||
|
||||
Author : PooSH
|
||||
Original Author : Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
class ObjCondition_Multi_SE extends ObjCondition_Multi
|
||||
editinlinenew;
|
||||
|
||||
var (ObjCondition_Multi) bool bSkipInactive;
|
||||
var (ObjCondition_Multi) KFStoryGameInfo.EConditionActivationMethod ChildActivationMethod;
|
||||
|
||||
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() && ChildConditions[i].ConditionIsValid()
|
||||
&& (!bSkipInactive || ChildConditions[i].ConditionIsActive()) )
|
||||
{
|
||||
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(KF_ObjectiveCondition).ConditionTick(DeltaTime);
|
||||
}
|
||||
|
||||
function float GetCompletionPct()
|
||||
{
|
||||
if ( NumCompleted >= NumConditions )
|
||||
return 1.f;
|
||||
|
||||
return float(NumCompleted) / float(NumConditions) ;
|
||||
}
|
||||
|
||||
|
||||
function Trigger(actor Other, pawn EventInstigator)
|
||||
{
|
||||
local int i;
|
||||
|
||||
super.Trigger(Other, EventInstigator);
|
||||
|
||||
if( ConditionIsActive() && ConditionIsValid() && ChildActivationMethod != Null )
|
||||
{
|
||||
if ( ChildActivationMethod == RandomlyActivate ) {
|
||||
warn("RandomlyActivate can not be used as ChildActivationMethod");
|
||||
return;
|
||||
}
|
||||
|
||||
for(i = 0 ; i < ChildConditions.length ; i ++)
|
||||
{
|
||||
if( !ChildConditions[i].ConditionIsValid() )
|
||||
continue;
|
||||
|
||||
switch(ChildActivationMethod)
|
||||
{
|
||||
case TriggerToggled :
|
||||
ChildConditions[i].SetTargetActor(InstigatorName, EventInstigator);
|
||||
if(!ChildConditions[i].bActive)
|
||||
{
|
||||
log(name $ " Activating Child Condition By Trigger - " $ ChildConditions[i].name);
|
||||
GetObjOwner().ActivateCondition(ChildConditions[i]);
|
||||
ChildConditions[i].ConditionActivated(EventInstigator);
|
||||
}
|
||||
else
|
||||
{
|
||||
log(name $ " Deactivating Child Condition By Trigger - " $ ChildConditions[i].name);
|
||||
GetObjOwner().DeActivateCondition(ChildConditions[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case TriggerActivates :
|
||||
if(!ChildConditions[i].bActive)
|
||||
{
|
||||
log(name $ " Activating Child Condition By Trigger - " $ ChildConditions[i].name);
|
||||
ChildConditions[i].SetTargetActor(InstigatorName, EventInstigator);
|
||||
GetObjOwner().ActivateCondition(ChildConditions[i]);
|
||||
ChildConditions[i].ConditionActivated(EventInstigator);
|
||||
}
|
||||
break;
|
||||
|
||||
case TriggerDeActivates :
|
||||
if(ChildConditions[i].bActive)
|
||||
{
|
||||
log(name $ " Deactivating Child Condition By Trigger - " $ ChildConditions[i].name);
|
||||
ChildConditions[i].SetTargetActor(InstigatorName, EventInstigator);
|
||||
GetObjOwner().DeActivateCondition(ChildConditions[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
233
kf_sources/ScrnStoryGame/Classes/ObjCondition_Use_SE.uc
Normal file
233
kf_sources/ScrnStoryGame/Classes/ObjCondition_Use_SE.uc
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
class ObjCondition_Use_SE extends ObjCondition_Use
|
||||
editinlinenew;
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------
|
||||
ObjCondition_Use_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
Enhanced version of ObjCondition_Use.
|
||||
|
||||
- Fixed a bug when GetCompletionPct() could return value > 1
|
||||
- Added bCheckDependentsFirst
|
||||
- ConditionTick() stops using if bCheckDependentsFirst and DependentConditions incomplete
|
||||
- HoldUseSeconds use difficulty modifiers
|
||||
- if Screen_CountStyle == Count_Down, then seconds left are displayed on the HUD
|
||||
- If UseActor uses collision cylinder, it will be always taken
|
||||
in place, not only when it is blocking actors
|
||||
|
||||
|
||||
Author : PooSH
|
||||
Original Author: Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// if true, DependentConditions the progression
|
||||
var(KF_ObjectiveCondition) bool bCheckDependentsFirst;
|
||||
// call DependentConditions[].FindInstigator()?
|
||||
var(KF_ObjectiveCondition) bool bCheckDependentsInstigator;
|
||||
|
||||
var(Audio) sound UsingSound;
|
||||
var protected sound OriginalUsingSound;
|
||||
|
||||
var protected transient float InitialHoldUseSeconds;
|
||||
|
||||
|
||||
|
||||
function Reset()
|
||||
{
|
||||
ResetSound();
|
||||
Super.Reset();
|
||||
SetTargetActor(CurrentUserName,none);
|
||||
}
|
||||
|
||||
function ResetSound()
|
||||
{
|
||||
local Actor MyUseActor;
|
||||
|
||||
// restore original ambient sound (mostly - none)
|
||||
if ( UsingSound != OriginalUsingSound ) {
|
||||
MyUseActor = GetTargetActor(UseActorName);
|
||||
if ( MyUseActor != none && MyUseActor.AmbientSound != OriginalUsingSound )
|
||||
MyUseActor.AmbientSound = OriginalUsingSound;
|
||||
}
|
||||
}
|
||||
|
||||
function PostBeginPlay(KF_StoryObjective MyOwner)
|
||||
{
|
||||
local Actor MyUseActor;
|
||||
|
||||
Super.PostBeginPlay(MyOwner);
|
||||
|
||||
InitialHoldUseSeconds = HoldUseSeconds;
|
||||
MyUseActor = GetTargetActor(UseActorName);
|
||||
if ( MyUseActor != none )
|
||||
OriginalUsingSound = MyUseActor.AmbientSound;
|
||||
}
|
||||
|
||||
|
||||
function SetObjOwner(KF_StoryObjective NewOwner)
|
||||
{
|
||||
ResetSound(); // just to be sure we won't leave ambient sound for old actor
|
||||
super.SetObjOwner(NewOwner);
|
||||
}
|
||||
|
||||
function ConditionActivated(pawn ActivatingPlayer)
|
||||
{
|
||||
if ( InitialHoldUseSeconds > 0 )
|
||||
HoldUseSeconds = InitialHoldUseSeconds * GetTotalDifficultyModifier();
|
||||
|
||||
Super.ConditionActivated(ActivatingPlayer);
|
||||
}
|
||||
|
||||
function ConditionTick(float DeltaTime)
|
||||
{
|
||||
Super.ConditionTick(DeltaTime);
|
||||
|
||||
// stop using objective, if we don't match condition
|
||||
if ( bCheckDependentsFirst && GetTargetActor(CurrentUserName) != none && !AllowCompletion() )
|
||||
StopUsingObj(Pawn(GetTargetActor(CurrentUserName)));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// added bUseCylinderCollision in cases use actor doesn't want block actors (e.g. use blocking volume)
|
||||
// but just wants to collide with actors
|
||||
// -- PooSH
|
||||
if( MyUseActor.bBlockActors || MyUseActor.bUseCylinderCollision )
|
||||
{
|
||||
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;
|
||||
local Actor MyUseActor;
|
||||
local Pawn OriginalIntigator;
|
||||
local bool bCantUse;
|
||||
|
||||
if ( bComplete )
|
||||
return; // no point of using already completed objective
|
||||
|
||||
MyUseActor = GetTargetActor(UseActorName);
|
||||
ControlledMover = KF_UseableMover(GetTargetActor(ControlledMoverName));
|
||||
CurrentUser = Pawn(GetTargetActor(CurrentUserName));
|
||||
|
||||
if(CurrentUser == none && IsTouchingUseActor(User))
|
||||
{
|
||||
OriginalIntigator = GetInstigator();
|
||||
// Instigator must be set to check DependentConditions
|
||||
if ( OriginalIntigator != User )
|
||||
SetTargetActor(InstigatorName, User);
|
||||
bCantUse = bCheckDependentsFirst && !AllowCompletion();
|
||||
if ( bCantUse ) {
|
||||
SetTargetActor(InstigatorName, OriginalIntigator); // restore original instigator
|
||||
return;
|
||||
}
|
||||
|
||||
SetTargetActor(InstigatorName,User);
|
||||
SetTargetActor(CurrentUserName,User);
|
||||
LastUseTime = User.Level.TimeSeconds;
|
||||
|
||||
if(AllowCompletion())
|
||||
{
|
||||
bWasUsed = true;
|
||||
}
|
||||
|
||||
if(ControlledMover != none)
|
||||
{
|
||||
ControlledMover.StartedUsing();
|
||||
}
|
||||
|
||||
if ( HoldUseSeconds > 0 && UsingSound != none ) {
|
||||
MyUseActor.AmbientSound = UsingSound;
|
||||
MyUseActor.NetUpdateTime = MyUseActor.Level.TimeSeconds - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function StopUsingObj(pawn User)
|
||||
{
|
||||
ResetSound();
|
||||
super.StopUsingObj(User);
|
||||
// ensure that we are not going abouve 100%
|
||||
if ( FinishedUseSeconds > HoldUseSeconds )
|
||||
FinishedUseSeconds = HoldUseSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// fixed progress going above 100% -- PooSH
|
||||
function float GetCompletionPct()
|
||||
{
|
||||
return fclamp(super.GetCompletionPct(), 0.f, 1.f);
|
||||
}
|
||||
|
||||
|
||||
// count down style shows remaining time -- PooSH
|
||||
function string GetDataString()
|
||||
{
|
||||
if(HoldUseSeconds > 0)
|
||||
{
|
||||
if ( HUD_Screen.Screen_CountStyle == 0 ) {
|
||||
return string( (1.f-(GetRemainingUseTime() / HoldUseSeconds))*100.f) $"%" ;
|
||||
}
|
||||
else {
|
||||
return string(GetRemainingUseTime()) ;
|
||||
}
|
||||
}
|
||||
|
||||
return "" ;
|
||||
}
|
||||
|
||||
/* Overriden to skip inactive dependencies -- PooSH */
|
||||
function bool AllowCompletion()
|
||||
{
|
||||
local int i;
|
||||
local array<Pawn> MyInstigators;
|
||||
|
||||
if ( DependentConditions.length > 0 ) {
|
||||
MyInstigators = GetInstigatorList();
|
||||
for(i = 0 ; i < DependentConditions.length ; i ++) {
|
||||
if ( DependentConditions[i].ConditionIsActive() ) {
|
||||
if( !DependentConditions[i].bComplete
|
||||
|| (bCheckDependentsInstigator && !DependentConditions[i].FindInstigator(MyInstigators)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bCheckDependentsInstigator=True
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
--------------------------------------------------------------
|
||||
ObjCondition_Counter_SE
|
||||
--------------------------------------------------------------
|
||||
|
||||
Bug-fixed version if ObjCondition_Counter_SE.
|
||||
|
||||
- ConditionIsValid() returns false if ObjOwner is none
|
||||
|
||||
Author : PooSH
|
||||
Original Author : Alex Quick
|
||||
|
||||
--------------------------------------------------------------
|
||||
*/
|
||||
|
||||
class ObjCondition_WaveCounter_SE extends ObjCondition_WaveCounter;
|
||||
|
||||
// Dunno why, but sometimes it is called if condition is inactive (even if other or none objective is running)
|
||||
function bool ConditionIsValid()
|
||||
{
|
||||
if ( GetObjOwner() == none )
|
||||
return false; // wtf?
|
||||
|
||||
return super.ConditionIsValid();
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
}
|
||||
86
kf_sources/ScrnStoryGame/Classes/PickupModifier.uc
Normal file
86
kf_sources/ScrnStoryGame/Classes/PickupModifier.uc
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
class PickupModifier extends Actor
|
||||
placeable;
|
||||
|
||||
var() class<Pickup> PickupClass; // base pickup class to apply properties on
|
||||
var() name PickupTag; // pickup tag to apply properties on
|
||||
|
||||
var() bool bRemoveGlow; // removes UV2Texture and AmbientGlow
|
||||
var() bool bRemoveUnlit; // sets bUnlit=false (can't see pickups in dark places)
|
||||
var() int SellValue; // if > 0 sets weapon's sell value (only for KFWeaponPickup)
|
||||
|
||||
replication {
|
||||
reliable if ( (bNetInitial || bNetDirty) && Role == ROLE_Authority )
|
||||
bRemoveGlow, bRemoveUnlit, SellValue;
|
||||
}
|
||||
|
||||
|
||||
simulated function PostBeginPlay()
|
||||
{
|
||||
SetTimer(5, false); // give enough time for pickups to spawn
|
||||
}
|
||||
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
ApplyMod();
|
||||
}
|
||||
|
||||
simulated function Timer()
|
||||
{
|
||||
ApplyMod();
|
||||
}
|
||||
|
||||
simulated function ApplyMod()
|
||||
{
|
||||
local Actor Other;
|
||||
local int count;
|
||||
|
||||
foreach DynamicActors(PickupClass, Other, PickupTag) {
|
||||
count++;
|
||||
|
||||
if ( bRemoveGlow ) {
|
||||
Other.ScaleGlow = 0;
|
||||
Other.AmbientGlow = 0;
|
||||
Pickup(Other).bAmbientGlow = false;
|
||||
Other.UV2Texture = none;
|
||||
}
|
||||
if ( bRemoveUnlit ) {
|
||||
SetDisplayProperties(Style, Texture, false); // sets bUnlit
|
||||
Other.bStaticLighting = false;
|
||||
Other.bUseDynamicLights = true;
|
||||
}
|
||||
|
||||
if ( SellValue > 0 && KFWeaponPickup(Other) != none ) {
|
||||
KFWeaponPickup(Other).SellValue = SellValue;
|
||||
}
|
||||
}
|
||||
|
||||
Log("PickupModifier: " $ count $ " pickups were modified");
|
||||
}
|
||||
|
||||
event Trigger( Actor Other, Pawn EventInstigator )
|
||||
{
|
||||
ApplyMod();
|
||||
bClientTrigger = !bClientTrigger;
|
||||
NetUpdateTime = Level.TimeSeconds - 1;
|
||||
}
|
||||
|
||||
|
||||
simulated function ClientTrigger()
|
||||
{
|
||||
Log("PickupModifier: ClientTrigger()");
|
||||
ApplyMod();
|
||||
}
|
||||
|
||||
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
PickupClass=Class'KFMod.KFWeaponPickup'
|
||||
bHidden=True
|
||||
bAlwaysRelevant=True
|
||||
bOnlyDirtyReplication=True
|
||||
RemoteRole=ROLE_SimulatedProxy
|
||||
NetUpdateFrequency=2.000000
|
||||
Texture=Texture'Engine.S_Inventory'
|
||||
bNetNotify=True
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// This class is required to replicate player (speaker) names of KF_PlayerDialogue
|
||||
|
||||
class PlayerDialogueReplicationInfo extends ReplicationInfo;
|
||||
|
||||
var name DialogueActorName;
|
||||
var int DlgIndex;
|
||||
var PlayerReplicationInfo SpeakerPRI;
|
||||
|
||||
var private int NumTries;
|
||||
|
||||
replication {
|
||||
reliable if ( bNetInitial && Role == ROLE_Authority )
|
||||
DialogueActorName;
|
||||
|
||||
reliable if ( bNetDirty && Role == ROLE_Authority )
|
||||
DlgIndex, SpeakerPRI;
|
||||
}
|
||||
|
||||
simulated function PostNetReceive()
|
||||
{
|
||||
NumTries = 10; // try 10 times to find diealogue, then - give up
|
||||
Timer();
|
||||
}
|
||||
|
||||
simulated function Timer()
|
||||
{
|
||||
local KFPlayerController_Story MyPC;
|
||||
local HUD_StoryMode MyHUD;
|
||||
local int HUDDlgIndex;
|
||||
local KF_DialogueSpot NewDlg;
|
||||
|
||||
SetTimer(0, false);
|
||||
|
||||
MyPC = KFPlayerController_Story(Level.GetLocalPlayerController());
|
||||
if ( MyPC == none )
|
||||
return;
|
||||
|
||||
MyHUD = HUD_StoryMode(MyPC.MyHUD);
|
||||
if ( MyHUD == none )
|
||||
return;
|
||||
|
||||
NewDlg = MyHUD.FindDialogueActor(DialogueActorName);
|
||||
if(NewDlg == none)
|
||||
{
|
||||
log("Warning - could not find Dialogue Actor of name : "@DialogueActorName@" Aborting HUD Render. ", 'PlayerDialogueReplicationInfo');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!MyHUD.FindExistingDialogue(NewDlg.Dialogues[DlgIndex].Display.Dialogue_text,HUDDlgIndex)) {
|
||||
// try again later - maybe KFPlayerController_Story.ClientShowStoryDialogue() isn't replicated yet
|
||||
if ( NumTries > 0) {
|
||||
SetTimer(0.1, false);
|
||||
NumTries--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MyHUD.Dialogues[HUDDlgIndex].Speaker = SpeakerPRI.PlayerName;
|
||||
MyHUD.Dialogues[HUDDlgIndex].Portrait = SpeakerPRI.GetPortrait();
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
NetUpdateFrequency=5.000000
|
||||
bNetNotify=True
|
||||
}
|
||||
51
kf_sources/ScrnStoryGame/Classes/Touchable.uc
Normal file
51
kf_sources/ScrnStoryGame/Classes/Touchable.uc
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
class Touchable extends Actor;
|
||||
|
||||
var private Pawn MyPawn;
|
||||
|
||||
var() bool bTouchable;
|
||||
var() bool bUsable;
|
||||
|
||||
function Pawn GetMyPawn()
|
||||
{
|
||||
return MyPawn;
|
||||
}
|
||||
|
||||
function SetMyPawn(Pawn NewPawn)
|
||||
{
|
||||
if ( MyPawn == NewPawn )
|
||||
return;
|
||||
|
||||
if ( MyPawn != none ) {
|
||||
bHardAttach = false;
|
||||
SetBase(none);
|
||||
}
|
||||
|
||||
MyPawn = NewPawn;
|
||||
if ( MyPawn != none ) {
|
||||
bHardAttach = true;
|
||||
SetLocation( MyPawn.Location );
|
||||
SetBase(MyPawn);
|
||||
SetCollisionSize(MyPawn.CollisionRadius * 1.5, MyPawn.CollisionHeight) ;
|
||||
}
|
||||
}
|
||||
|
||||
function Touch( Actor Other )
|
||||
{
|
||||
if ( bTouchable && MyPawn != none && MyPawn != Other )
|
||||
MyPawn.Touch(Other);
|
||||
}
|
||||
|
||||
function UsedBy( Pawn user )
|
||||
{
|
||||
if ( bUsable && MyPawn != none && MyPawn != user )
|
||||
MyPawn.UsedBy(user);
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
bHidden=True
|
||||
RemoteRole=ROLE_None
|
||||
CollisionRadius=30.000000
|
||||
CollisionHeight=40.000000
|
||||
bCollideActors=True
|
||||
}
|
||||
43
kf_sources/ScrnStoryGame/Classes/TriggerDamageVolume.uc
Normal file
43
kf_sources/ScrnStoryGame/Classes/TriggerDamageVolume.uc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// causes touching Pawns' damage or death on trigger
|
||||
|
||||
class TriggerDamageVolume extends Volume;
|
||||
|
||||
var() class<Pawn> PawnClass;
|
||||
var() name PawnTag;
|
||||
var() int Damage;
|
||||
var() class<DamageType> DamageType;
|
||||
var() bool bInstantKill; // instant kill pawns instead of damaging them
|
||||
|
||||
|
||||
function Trigger( actor Other, pawn EventInstigator )
|
||||
{
|
||||
local Pawn P;
|
||||
local vector VoidVector;
|
||||
local Controller Killer;
|
||||
local int i;
|
||||
|
||||
// log("TriggerDamageVolume triggered: " $ name $ " by " $ EventInstigator);
|
||||
// PlayerController(EventInstigator.Controller).ClientMessage("TriggerDamageVolume"@name@"triggered");
|
||||
|
||||
if ( EventInstigator != none )
|
||||
Killer = EventInstigator.Controller;
|
||||
|
||||
// TouchingActors doesn't work correctly, because after killing the pawn,
|
||||
// it gets removed from the array, breaking the iterator
|
||||
for ( i = Touching.length-1 ; i >= 0 ; --i ) {
|
||||
P = Pawn(Touching[i]);
|
||||
if ( P != none && (P.Tag == PawnTag || PawnTag == '') ) {
|
||||
if ( bInstantKill )
|
||||
P.Died(Killer, DamageType , P.Location);
|
||||
else
|
||||
P.TakeDamage(Damage, EventInstigator, P.Location, VoidVector, DamageType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
PawnClass=Class'KFMod.KFMonster'
|
||||
DamageType=Class'Engine.Suicided'
|
||||
bInstantKill=True
|
||||
}
|
||||
62
kf_sources/ScrnStoryGame/Classes/index.html
Normal file
62
kf_sources/ScrnStoryGame/Classes/index.html
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<html>
|
||||
<head><title>Index of /kf_sources/ScrnStoryGame/Classes/</title></head>
|
||||
<body>
|
||||
<h1>Index of /kf_sources/ScrnStoryGame/Classes/</h1><hr><pre><a href="../">../</a>
|
||||
<a href="ACTION_AddAmmo.uc">ACTION_AddAmmo.uc</a> 01-Jul-2020 18:12 656
|
||||
<a href="ACTION_AddShield.uc">ACTION_AddShield.uc</a> 01-Jul-2020 18:12 953
|
||||
<a href="ACTION_DamagePawn.uc">ACTION_DamagePawn.uc</a> 01-Jul-2020 18:12 2134
|
||||
<a href="ACTION_Flashlight.uc">ACTION_Flashlight.uc</a> 01-Jul-2020 18:12 647
|
||||
<a href="ACTION_IfActorCount.uc">ACTION_IfActorCount.uc</a> 01-Jul-2020 18:12 819
|
||||
<a href="ACTION_IfHasEnemy.uc">ACTION_IfHasEnemy.uc</a> 01-Jul-2020 18:12 619
|
||||
<a href="ACTION_IfIsHurt.uc">ACTION_IfIsHurt.uc</a> 01-Jul-2020 18:12 416
|
||||
<a href="ACTION_IfMyPlayerClose.uc">ACTION_IfMyPlayerClose.uc</a> 01-Jul-2020 18:12 482
|
||||
<a href="ACTION_IfPlayerCount.uc">ACTION_IfPlayerCount.uc</a> 01-Jul-2020 18:12 1735
|
||||
<a href="ACTION_IfSeesEnemy.uc">ACTION_IfSeesEnemy.uc</a> 01-Jul-2020 18:12 648
|
||||
<a href="ACTION_KillPawn.uc">ACTION_KillPawn.uc</a> 01-Jul-2020 18:12 1306
|
||||
<a href="ACTION_MoveToEnemyTimed.uc">ACTION_MoveToEnemyTimed.uc</a> 01-Jul-2020 18:12 661
|
||||
<a href="ACTION_MoveToPlayerTimed.uc">ACTION_MoveToPlayerTimed.uc</a> 01-Jul-2020 18:12 1276
|
||||
<a href="ACTION_NPC_Active.uc">ACTION_NPC_Active.uc</a> 01-Jul-2020 18:12 424
|
||||
<a href="ACTION_NPC_Bleed.uc">ACTION_NPC_Bleed.uc</a> 01-Jul-2020 18:12 494
|
||||
<a href="ACTION_PlayKFReplicatedAmbientSound.uc">ACTION_PlayKFReplicatedAmbientSound.uc</a> 01-Jul-2020 18:12 1174
|
||||
<a href="ACTION_STORY_MonsterRules.uc">ACTION_STORY_MonsterRules.uc</a> 01-Jul-2020 18:12 992
|
||||
<a href="ACTION_SetEnemy.uc">ACTION_SetEnemy.uc</a> 01-Jul-2020 18:12 1932
|
||||
<a href="ACTION_SetPhysicsOther.uc">ACTION_SetPhysicsOther.uc</a> 01-Jul-2020 18:12 1336
|
||||
<a href="ACTION_SpawnRandomPickups.uc">ACTION_SpawnRandomPickups.uc</a> 01-Jul-2020 18:12 1796
|
||||
<a href="ACTION_SpawnZED.uc">ACTION_SpawnZED.uc</a> 01-Jul-2020 18:12 3505
|
||||
<a href="ACTION_WaitForAnyPlayer.uc">ACTION_WaitForAnyPlayer.uc</a> 01-Jul-2020 18:12 1500
|
||||
<a href="ACTION_WaitForTimerRandom.uc">ACTION_WaitForTimerRandom.uc</a> 01-Jul-2020 18:12 549
|
||||
<a href="ACTION_ZEDTIME.uc">ACTION_ZEDTIME.uc</a> 01-Jul-2020 18:12 366
|
||||
<a href="DLGFILTER_Deaths.uc">DLGFILTER_Deaths.uc</a> 01-Jul-2020 18:12 507
|
||||
<a href="DLGFILTER_Female.uc">DLGFILTER_Female.uc</a> 01-Jul-2020 18:12 248
|
||||
<a href="DLGFILTER_Instigator.uc">DLGFILTER_Instigator.uc</a> 01-Jul-2020 18:12 279
|
||||
<a href="DLGFILTER_KillAssists.uc">DLGFILTER_KillAssists.uc</a> 01-Jul-2020 18:12 875
|
||||
<a href="DLGFILTER_Kills.uc">DLGFILTER_Kills.uc</a> 01-Jul-2020 18:12 741
|
||||
<a href="DLGFILTER_Perk.uc">DLGFILTER_Perk.uc</a> 01-Jul-2020 18:12 1573
|
||||
<a href="DLGFILTER_Score.uc">DLGFILTER_Score.uc</a> 01-Jul-2020 18:12 711
|
||||
<a href="DamTypeBleedFixed.uc">DamTypeBleedFixed.uc</a> 01-Jul-2020 18:12 101
|
||||
<a href="DialoguePlayerFilter.uc">DialoguePlayerFilter.uc</a> 01-Jul-2020 18:12 297
|
||||
<a href="Dialogue_EventListener_SE.uc">Dialogue_EventListener_SE.uc</a> 01-Jul-2020 18:12 1103
|
||||
<a href="FragPickup_SE.uc">FragPickup_SE.uc</a> 01-Jul-2020 18:12 930
|
||||
<a href="KFDestroyableStaticMesh_SE.uc">KFDestroyableStaticMesh_SE.uc</a> 01-Jul-2020 18:12 856
|
||||
<a href="KF_BreakerBoxNPC_SE.uc">KF_BreakerBoxNPC_SE.uc</a> 01-Jul-2020 18:12 5245
|
||||
<a href="KF_DialogueSpot_SE.uc">KF_DialogueSpot_SE.uc</a> 01-Jul-2020 18:12 5384
|
||||
<a href="KF_PlayerDialogue.uc">KF_PlayerDialogue.uc</a> 01-Jul-2020 18:12 9616
|
||||
<a href="KF_StoryNPC_Spawnable_SE.uc">KF_StoryNPC_Spawnable_SE.uc</a> 01-Jul-2020 18:12 11388
|
||||
<a href="NPCEnemy_AI.uc">NPCEnemy_AI.uc</a> 01-Jul-2020 18:12 188
|
||||
<a href="NPCFriendly_AI.uc">NPCFriendly_AI.uc</a> 01-Jul-2020 18:12 190
|
||||
<a href="NPC_AI.uc">NPC_AI.uc</a> 01-Jul-2020 18:12 2814
|
||||
<a href="NamedObjectTable.uc">NamedObjectTable.uc</a> 01-Jul-2020 18:12 1137
|
||||
<a href="NamedPRITable.uc">NamedPRITable.uc</a> 01-Jul-2020 18:12 482
|
||||
<a href="ObjCondition_ActorHealth_SE.uc">ObjCondition_ActorHealth_SE.uc</a> 01-Jul-2020 18:12 1685
|
||||
<a href="ObjCondition_Area_SE.uc">ObjCondition_Area_SE.uc</a> 01-Jul-2020 18:12 5711
|
||||
<a href="ObjCondition_Counter_SE.uc">ObjCondition_Counter_SE.uc</a> 01-Jul-2020 18:12 1622
|
||||
<a href="ObjCondition_Inventory_SE.uc">ObjCondition_Inventory_SE.uc</a> 01-Jul-2020 18:12 4555
|
||||
<a href="ObjCondition_Multi_SE.uc">ObjCondition_Multi_SE.uc</a> 01-Jul-2020 18:12 4047
|
||||
<a href="ObjCondition_Use_SE.uc">ObjCondition_Use_SE.uc</a> 01-Jul-2020 18:12 6356
|
||||
<a href="ObjCondition_WaveCounter_SE.uc">ObjCondition_WaveCounter_SE.uc</a> 01-Jul-2020 18:12 758
|
||||
<a href="PickupModifier.uc">PickupModifier.uc</a> 01-Jul-2020 18:12 2006
|
||||
<a href="PlayerDialogueReplicationInfo.uc">PlayerDialogueReplicationInfo.uc</a> 01-Jul-2020 18:12 1709
|
||||
<a href="Touchable.uc">Touchable.uc</a> 01-Jul-2020 18:12 920
|
||||
<a href="TriggerDamageVolume.uc">TriggerDamageVolume.uc</a> 01-Jul-2020 18:12 1267
|
||||
</pre><hr></body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue