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

1580 lines
44 KiB
Ucode

/*
--------------------------------------------------------------
KF_StoryObjective
--------------------------------------------------------------
Placeable Objective Actor which stores a set of Conditions and
Actions.
Conditions determine how to complete the objective while actions
determine what happens once the objective is completed. Objective
progress is handled server-side and there can only be one active
objective at a time per server.
Author : Alex Quick
--------------------------------------------------------------
*/
class KF_StoryObjective extends StoryObjectiveBase
HideCategories(Sound)
dependson(KFStoryGameInfo)
placeable ;
#exec OBJ LOAD FILE=KFStoryGame_Tex.utx
const MAXCONDITIONS = 6;
var(Debug) bool bDebugConditionInstancing;
/* Cached reference to Story gameinfo to cut down on typecasting */
var KFStoryGameInfo StoryGI;
/* Assigned when a checkpoint has set this Objective as its 'RestartFromObjective'. */
var KF_StoryCheckPointVolume ActiveCheckPoint;
var array<Actor> RelevantActors;
/* ====================== HUD =======================================================================
=====================================================================================================*/
var(Objective_HUD) KFStoryGameInfo.SObjectiveHeaderInfo HUD_Header;
var(Objective_HUD) KFStoryGameInfo.SObjectiveBackGroundInfo HUD_Background;
/* should we show this objective on the HUD at all ? */
var(Objective_HUD) bool bShowOnHUD;
/* displays on the HUD - gives the player a hint about what his current objective is */
var localized string ObjectiveHint;
/* for letting the player know what kind of failure condition this objective has (if any) */
var/*(Objective_HUD)*/ string ObjectiveFailureHint;
/* local message to display when the objective is successfully completed */
var localized string SuccessText;
/* local message to display when the objective is failed */
var localized string FailureText;
var texture BackgroundTexture;
var bool bBGScaleUniform;
var(Objective_HUD) KFStoryGameInfo.SVect2D HUD_ScreenPosition;
var color Header_Clr;
/* Un-touched Hintstring ( before we append timer info )*/
var string InitialHint;
var float StartTime;
/* Replication Throttling ===========================================================================*/
/* Number of replication updates that have been pushed in the last second for conditions belonging to this objective */
var int CurrentRepUpdates;
/* Maximum number of condition replication updates per second for conditions belonging to this objective */
var int MaxRepUpdatesPerSecond;
/* ====================== Audio =======================================================================
=====================================================================================================*/
/* Sounds to play when the Objective is completed or becomes active, respectively. */
var(Objective_Audio) Sound CompletionSound,ActivationSound,FailureSound;
var(Objective_Audio) localized string ObjectiveInProgressMusic;
var(Objective_Audio) float MusicFadeInTime,MusicFadeOutTime;
var(Objective_Audio) bool bStopMusicOnCompletion;
var string CompletionSoundName,ActivationSoundName;
/* ====================== General Settings ===========================================================
=====================================================================================================*/
/* Can checkpoints save/restart from this objective ? */
var(Objective_Settings) bool bCheckPointable;
/* Can this objective be completed more than once without a checkpoint / level reset ? */
var(Objective_Settings) bool bRecurring;
/* Is this whole objective not mandatory for players to complete ? */
var(Objective_Settings) bool bOptional;
/* if true this objective & its conditions should revert to their starting state when this obj is no longer active . Defaults true*/
var(Objective_Settings) bool bResetOnDeactivation;
/* ====================== Events =====================================================================
=====================================================================================================*/
/* Events to use when the Objective becoems active / completes respectively. Reduces the need for multiple trigger actors / scripted actions */
var(Objective_Events) array<name> ActivationEvents;
/* Events to call when the Objective is de-activated (either by failing, succeeding, or simply switching to another objective via cheats */
var(Objective_Events) array<name> DeActivationEvents;
/* Events to fire off when the Objective is marked 'Complete' */
var(Objective_Events) array<name> CompletionEvents;
/* Events to fire off when the Objecvtive is marked 'Failed ' */
var(Objective_Events) array<name> FailureEvents;
/* =====================================================================================
=======================================================================================*/
/* true when ObjectiveCompleted() has been called. This Objective cannot be activated again */
var bool bCompleted;
/* true if this objective had a secondary condition that was not met */
var bool bFailed;
/* ====================== Success CONDITIONS ========================================================
Specifies a type of condition the player will succeed in completing the objective if he satisfies
=====================================================================================================*/
var(Objective_Conditions) export editinlineuse array<KF_ObjectiveCondition> SuccessConditions;
var(Objective_Conditions) export editinlineuse array<KF_ObjectiveCondition> OptionalConditions;
var bool bPendingSuccessActions;
var bool bPendingFailureActions;
/* OBJ_Touch vars ============================================================================
==============================================================================================*/
/* true if Touch() was called on this objective by a valid instigator. OBJ_Touch objectives will be considered complete the next tick */
var bool bWasTouched;
/* OBJ_Triggered vars ========================================================================
==============================================================================================*/
var bool bWasUsed;
/* ====================== FAILURE CONDITIONS ========================================================
Specifies a type of condition the player will fail the objective if he does not satisfy.
=====================================================================================================*/
var(Objective_Conditions) export editinlineuse array<KF_ObjectiveCondition> FailureConditions;
/* Conditions in this array are active & ticked */
var array<KF_ObjectiveCondition> AllConditions;
/* Condition Instancing ================================================================================ */
/* If set - this objective's own failure condition will be ignored in place of the F.C in the specified Objective. */
var(Objective_Instancing) name Copy_FailureConditions_From;
/* first objective in the Failure conditions' linked list */
var KF_StoryObjective FirstFailureParent;
/* If set - this objective's own success condition will be ignored in place of the F.C in the specified Objective. */
var(Objective_Instancing) name Copy_SuccessConditions_From;
var KF_StoryObjective FirstSuccessParent;
var KF_StoryObjective FirstOptionalParent;
/* If set - this objective's own success condition will be ignored in place of the F.C in the specified Objective. */
var(Objective_Instancing) name Copy_OptionalConditions_From;
var(Objective_Instancing) name Copy_FailureActions_From;
var(Objective_Instancing) name Copy_SuccessActions_From;
var KF_StoryObjective Action_FirstFailureParent;
var KF_StoryObjective Action_FirstSuccessParent;
var float LastWhispTime;
var float WhispInterval;
var bool bShowWhispTrails;
var float RemainingTime;
/* Called in GameInfo.PostLogin() */
function OnPlayerJoined(Controller JoiningPlayer)
{
local int i;
local KFPlayerController_Story KFSPC;
KFSPC = KFPlayerController_Story(JoiningPlayer);
if(KFSPC == none)
{
return;
}
/* Push Existing Condition data through to newly connected client */
for(i = 0 ; i < AllConditions.Length ; i ++)
{
AllConditions[i].ReliableConditionUpdate(KFSPC);
}
}
/* Event called when player leaves the game - Update Condition values*/
function OnPlayerLeft(Controller LeavingPlayer){}
/* Event called when the objective list finishes sorting / populating in the gameinfo */
function OnObjPopulationComplete(){}
simulated function PostBeginPlay()
{
local int i;
Super.PostBeginPlay();
InitialHint = ObjectiveHint;
StoryGI = KFStoryGameInfo(level.Game);
/* No KFO Gametype, no Initialization */
if(StoryGI == none)
{
return;
}
for(i = 0 ; i < SuccessConditions.length ; i ++)
{
SuccessConditions[i].PostBeginPlay(self);
}
for(i = 0 ; i < OptionalConditions.length ; i ++)
{
OptionalConditions[i].PostBeginPlay(self);
}
for(i = 0 ; i < FailureConditions.length ; i ++)
{
FailureConditions[i].PostBeginPlay(self);
}
SetTimer(1.0,true);
}
/* Throttle the amount of data that is being replicated to clients */
function bool AllowConditionRepUpdate()
{
if(CurrentRepUpdates >= MaxRepUpdatesPerSecond ||
Level.Pauser != none)
{
return false;
}
return true;
}
function Timer()
{
CurrentRepUpdates = 0;
}
/* Adds a condition to the 'AllConditions' array in this objective. This means that
the condition will then be ticked & Displayed on the HUD, etc. */
function ActivateCondition( KF_ObjectiveCondition NewCondition)
{
if(FindConditionByName(NewCondition.name) == none)
{
AllConditions[AllConditions.length] = NewCondition;
NewCondition.ConditionActivated(Instigator);
}
}
function DeActivateCondition( KF_ObjectiveCondition OldCondition)
{
local int i;
for(i = 0 ; i < AllConditions.length ; i ++)
{
if(AllConditions[i] == OldCondition)
{
AllConditions[i].ConditionDeActivated();
AllConditions.Remove(i,1);
i -- ;
}
}
}
/* 0 == Failure Condition, 1 == Success */
simulated function byte GetNamedConditionType( name ConditionName)
{
local int i;
for(i = 0 ; i < SuccessConditions.length ; i ++)
{
if(SuccessConditions[i].name == ConditionName)
{
return 1;
}
}
for(i = 0 ; i < FailureConditions.length ; i ++)
{
if(FailureConditions[i].name == ConditionName)
{
return 0;
}
}
return 255;
}
/*
Init Conditions
- Fill the AllConditions array so we can easily iterate conditions
- Set this Objective as the owner of each condition
- Spawn event listeners for each condition so they can receive trigger events
*/
function InitConditions()
{
local int i;
AllConditions.length = 0;
for( i = 0 ; i < SuccessConditions.length ; i ++)
{
SuccessConditions[i].ConditionType = 1;
SuccessConditions[i].SetObjOwner(self);
if(SuccessConditions[i].ShouldInitOnActivation())
{
ActivateCondition(SuccessConditions[i]);
}
}
i = 0 ;
for( i = 0 ; i < FailureConditions.length ; i ++)
{
FailureConditions[i].ConditionType = 0;
FailureConditions[i].SetObjOwner(self);
if(FailureConditions[i].ShouldInitOnActivation())
{
ActivateCondition(FailureConditions[i]);
}
}
i = 0 ;
for( i = 0 ; i < OptionalConditions.length ; i ++)
{
OptionalConditions[i].ConditionType = 2;
OptionalConditions[i].SetObjOwner(self);
if(OptionalConditions[i].ShouldInitOnActivation())
{
ActivateCondition(OptionalConditions[i]);
}
}
}
function float GetGameDifficulty()
{
return Level.Game.GameDifficulty ;
}
function InitActions()
{
if(SuccessAction != none)
{
SuccessAction.SetObjOwner(self);
SuccessAction.ActionType = 1;
}
if(FailureAction != none)
{
FailureAction.SetObjOwner(self);
FailureAction.ActionType = 0;
}
}
function UpdateInstancedConditions()
{
local KF_StoryObjective FP,SP,FPA,SPA,OP;
local int i,idx;
FindFirstSuccessParent();
FindFirstOptionalParent();
FindFirstFailureParent();
FindFirstActionSuccessParent();
FindFirstActionFailureParent();
SP = GetSuccessParent() ;
FP = GetFailureParent() ;
OP = GetOptionalParent();
SPA = GetActionSuccessParent();
FPA = GetActionFailureParent();
if(bDebugConditionInstancing)
{
log(" OBJ INSTANCE DEBUG ===============================================================",'Story_Debug');
log(ObjectiveName@":::: Success Condition Parent ---> "@SP.ObjectiveName,'Story_Debug');
log(ObjectiveName@":::: Failure Condition Parent ---> "@FP.ObjectiveName,'Story_Debug');
log(ObjectiveName@":::: Optional Condition Parent ---> "@OP.ObjectiveName,'Story_Debug');
log(ObjectiveName@":::: Success Action Parent ---> "@SPA.ObjectiveName,'Story_Debug');
log(ObjectiveName@":::: Failure Action Parent ---> "@FPA.ObjectiveName,'Story_Debug');
}
if(SP != none && SP != self && SP.SuccessConditions.length > 0)
{
if(bDebugConditionInstancing)
log("=========== Copying ("$SP.SuccessConditions.length$") Success Condition(s) from : "@SP.ObjectiveName@" to : "@ObjectiveName,'Story_Debug');
for(i = 0 ; i < SP.SuccessConditions.length ; i ++)
{
if(SP.SuccessConditions[i].ConditionisValid())
{
SuccessConditions[i] = SP.SuccessConditions[i];
}
}
}
if(SPA != none && SPA != self && SPA.SuccessAction != none)
{
if(bDebugConditionInstancing)
log("=========== Copying Success Action from : "@SPA.ObjectiveName@" to : "@ObjectiveName,'Story_Debug');
SuccessAction = SPA.SuccessAction;
}
if(FP != none && FP != self && FP.FailureConditions.length > 0)
{
if(bDebugConditionInstancing)
log("=========== Copying ("$FP.FailureConditions.length$") Failure Condition(s) from : "@FP.ObjectiveName@" to : "@ObjectiveName,'Story_Debug');
for(idx = 0 ; idx < FP.FailureConditions.length ; idx ++)
{
if(FP.FailureConditions[idx].ConditionisValid())
{
FailureConditions[idx] = FP.FailureConditions[idx];
}
}
}
if(FPA != none && FPA != self && FPA.FailureAction != none)
{
if(bDebugConditionInstancing)
log("=========== Copying Failure Action from : "@FPA.ObjectiveName@" to : "@ObjectiveName,'Story_Debug');
FailureAction = FPA.FailureAction;
}
if(OP != none && OP != self && OP.OptionalConditions.length > 0)
{
if(bDebugConditionInstancing)
log("=========== Copying ("$OP.OptionalConditions.length$") Optional Condition(s) from : "@OP.ObjectiveName@" to : "@ObjectiveName,'Story_Debug');
for(i = 0 ; i < OP.OptionalConditions.length ; i ++)
{
if(OP.OptionalConditions[i].ConditionIsValid())
{
OptionalConditions[i] = OP.OptionalConditions[i];
}
}
}
}
/* Returns the first SuccessCondition Parent Objective in the Linked list */
function FindFirstSuccessParent()
{
local int i;
local KF_StoryObjective Obj;
if(FirstSuccessParent != none ||
StoryGI == none ||
Copy_SuccessConditions_From == '' ||
Copy_SuccessConditions_From == ObjectiveName)
{
return;
}
for(i = 0 ; i < StoryGI.AllObjectives.length ; i ++)
{
Obj = StoryGI.AllObjectives[i];
if(Obj.ObjectiveName == Copy_SuccessConditions_From)
{
FirstSuccessParent = Obj;
break;
}
}
}
/* Returns the first FailureCondition Parent Objective in the Linked list */
function FindFirstFailureParent()
{
local int i;
local KF_StoryObjective Obj;
if(FirstFailureParent != none ||
StoryGI == none ||
Copy_FailureConditions_From == '' ||
Copy_FailureConditions_From == ObjectiveName)
{
return;
}
for(i = 0 ; i < StoryGI.AllObjectives.length ; i ++)
{
Obj = StoryGI.AllObjectives[i];
if(Obj.ObjectiveName == Copy_FailureConditions_From)
{
FirstFailureParent = Obj;
break;
}
}
}
/* Returns the first Optional Condition Parent Objective in the Linked list */
function FindFirstOptionalParent()
{
local int i;
local KF_StoryObjective Obj;
if(FirstOptionalParent != none ||
StoryGI == none ||
Copy_OptionalConditions_From == '' ||
Copy_OptionalConditions_From == ObjectiveName)
{
return;
}
for(i = 0 ; i < StoryGI.AllObjectives.length ; i ++)
{
Obj = StoryGI.AllObjectives[i];
if(Obj.ObjectiveName == Copy_OptionalConditions_From)
{
FirstOptionalParent = Obj;
break;
}
}
}
/* Returns the first FailureCondition Parent Objective in the Linked list */
function FindFirstActionSuccessParent()
{
local int i;
local KF_StoryObjective Obj;
if(Action_FirstSuccessParent != none ||
StoryGI == none ||
Copy_SuccessActions_From == '' ||
Copy_SuccessActions_From == ObjectiveName)
{
return;
}
for(i = 0 ; i < StoryGI.AllObjectives.length ; i ++)
{
Obj = StoryGI.AllObjectives[i];
if(Obj.ObjectiveName == Copy_SuccessActions_From)
{
Action_FirstSuccessParent = Obj;
break;
}
}
}
/* Returns the first FailureCondition Parent Objective in the Linked list */
function FindFirstActionFailureParent()
{
local int i;
local KF_StoryObjective Obj;
if(Action_FirstFailureParent != none ||
StoryGI == none ||
Copy_FailureActions_From == '' ||
Copy_FailureActions_From == ObjectiveName)
{
return;
}
for(i = 0 ; i < StoryGI.AllObjectives.length ; i ++)
{
Obj = StoryGI.AllObjectives[i];
if(Obj.ObjectiveName == Copy_FailureActions_From)
{
Action_FirstFailureParent = Obj;
break;
}
}
}
/* accessor for retrieving this objective's Success parent. (if using instanced conditions) */
function KF_StoryObjective GetSuccessParent()
{
if(FirstSuccessParent == none)
{
return none;
}
else
{
return FirstSuccessParent ;
}
}
/* accessor for retrieving this objective's Optional objective parent. (if using instanced conditions) */
function KF_StoryObjective GetOptionalParent()
{
if(FirstOptionalParent == none)
{
return self;
}
else
{
return FirstOptionalParent.GetOptionalParent() ;
}
}
function KF_StoryObjective GetActionSuccessParent()
{
if(Action_FirstSuccessParent == none)
{
return self;
}
else
{
return Action_FirstSuccessParent.GetActionSuccessParent() ;
}
}
/* accessor for retrieving this objective's Failure parent. (if using instanced conditions) */
function KF_StoryObjective GetFailureParent()
{
if(FirstFailureParent == none)
{
return self;
}
else
{
return FirstFailureParent.GetFailureParent() ;
}
}
function KF_StoryObjective GetActionFailureParent()
{
if(Action_FirstFailureParent == none)
{
return self;
}
else
{
return Action_FirstFailureParent.GetActionFailureParent() ;
}
}
/* reset objective to its initial state - used when restarting from a checkpoint */
function Reset()
{
StartTime = 0.f;
InitialHint = ObjectiveHint;
bCompleted = false;
bFailed = false;
bWasUsed = false;
bWasTouched = false;
ResetConditions();
ResetActions();
// UpdateInstancedConditions();
}
function ResetConditions()
{
local int i;
for(i = 0 ; i < AllConditions.length ; i ++)
{
if(AllConditions[i].GetObjOwner() == self)
{
AllConditions[i].ConditionDeActivated();
}
}
}
function ResetActions()
{
if(SuccessAction!=none)
{
SuccessAction.ActionDeActivated();
}
if(FailureAction != none)
{
FailureAction.ActionDeActivated();
}
}
/* Wrapper for checking if this objective is the player's current goal */
function bool IsCurrentObjective()
{
if(StoryGI != none)
{
return StoryGI.CurrentObjective == self ;
}
return false;
}
/* Returns true if this objective is able to be set as active (ie. Not already Completed or failed) */
function bool IsValidForActivation()
{
return !bCompleted && !bFailed ;
}
/* Notification that this objective has just been assigned as the current goal in GameInfo*/
function Notify_ObjectiveActivated(pawn ActivatingPlayer)
{
local int i;
local Controller C;
local PlayerController PC;
StartTime = Level.TimeSeconds;
NetUpdateFrequency = 10.f;
SetHumanInstigator(ActivatingPlayer);
UpdateInstancedConditions();
InitConditions();
InitActions();
for(i = 0 ; i < ActivationEvents.length ; i ++)
{
TriggerEvent(ActivationEvents[i],self,ActivatingPlayer);
// log("===========================================================",'Story_Debug');
// log(self@"is triggering Activation event : "@ActivationEvents[i],'Story_Debug');
}
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
// log("Playing sound : "@ActivationSound,'Story_Debug');
PC = PlayerController(C);
if(PC != none )
{
if(ActivationSound != none)
{
PC.ClientPlaySound(ActivationSound,,,SLOT_MISC);
}
}
}
ShowWhispTrails(true);
PlayObjectiveMusic();
Notify_ConditionsActivated(ActivatingPlayer);
}
function Notify_ObjectiveDeActivated()
{
local int i;
NetUpdateFrequency = 1.f ;
ShowWhispTrails(false);
for(i = 0 ; i < DeActivationEvents.length ; i ++)
{
TriggerEvent(DeActivationEvents[i],self,Instigator);
}
/* even if the objective isn't recurring we still need to reset the conditions (since they could be instanced
by other objectives and need to be returned to their default states */
if(bResetOnDeactivation)
{
if(!bRecurring)
{
ResetConditions();
ResetActions();
}
else
{
Reset(); // if its a recurring objective then just reset everything.
}
}
}
function Notify_ConditionsActivated(pawn ActivatingPlayer)
{
local int i;
for(i = 0 ; i < AllConditions.length ; i ++)
{
AllConditions[i].ConditionActivated(ActivatingPlayer);
}
}
function ShowWhispTrails(bool On)
{
bShowWhispTrails = On ;
}
function PlayObjectiveMusic()
{
local Controller C;
if(ObjectiveInProgressMusic != "" &&
ObjectiveInProgressMusic != StoryGI.CurrentMusicTrack)
{
for( C=Level.ControllerList;C!=None;C=C.NextController )
{
if (KFPlayerController(C)!= none)
{
KFPlayerController(C).NetPlayMusic(ObjectiveInProgressMusic, MusicFadeInTime,MusicFadeOutTime);
}
}
StoryGI.CurrentMusicTrack = ObjectiveInProgressMusic;
}
}
function ForceCompleteObjective()
{
local int i,idx;
bCompleted = false;
for(i = 0 ; i < SuccessConditions.length ; i ++)
{
for(idx = 0 ; idx < SuccessConditions[i].ProgressEvents.Length ; idx ++)
{
if(!SuccessConditions[i].ProgressEvents[idx].bWasTriggered)
{
TriggerEvent(SuccessConditions[i].ProgressEvents[idx].EventName,self,Instigator);
}
}
SuccessConditions[i].bLockCompletion = true;
SuccessConditions[i].ConditionCompleted();
}
}
/* Called when this Actor's conditions have been met - notify the gameinfo that it may proceed to the next objective */
function ObjectiveCompleted(Controller Scorer, optional bool SkipActions)
{
local int i;
local Controller C;
local PlayerController PC;
local KFSteamStatsAndAchievements KFSteamStats;
local KFGameReplicationInfo KFGRI;
if(IsValidForActivation())
{
bCompleted = true;
Log("======================================",'Story_Debug');
Log("OBJECTIVE "@ObjectiveName@" COMPLETE ",'Story_Debug');
Log("======================================",'Story_Debug');
for(i = 0 ; i < CompletionEvents.length ; i ++)
{
TriggerEvent(CompletionEvents[i],self,Instigator);
}
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
PC = PlayerController(C);
if(PC != none)
{
if(CompletionSound != none )
{
PC.ClientPlaySound(CompletionSound,,,SLOT_MISC);
}
if(SuccessText != "")
{
PC.ClientMessage( SuccessText, 'CriticalEvent');
}
KFGRI = KFGameReplicationInfo( Level.GRI );
if( KFGRI != none )
{
if( !KFGRI.bObjectiveAchievementFailed )
{
KFSteamStats = KFSteamStatsAndAchievements( PC.SteamStatsAndAchievements );
if ( KFSteamStats != none )
{
KFSteamStats.OnObjectiveCompleted( ObjectiveName );
}
}
}
}
}
KFGRI.bObjectiveAchievementFailed = false;
if(!SkipActions)
{
bPendingSuccessActions = true;
}
if(StoryGI != none)
{
StoryGI.OnObjectiveCompleted(self);
}
}
else
{
// log(" WARNING - OBJECTIVE : "@ObjectiveName@" is already complete. ",'Story_Debug');
}
}
/* Deferred execution of actions to allow Actors which are triggered by Success / Failure events to be ticked before
the objective is changed */
function ExecuteSuccessActions()
{
if(SuccessAction != none)
{
SuccessAction.ExecuteAction(Instigator.Controller);
}
}
/* Counterpart to ObjectiveCompleted() - Called when this Objective's Failure condition has not been satisfied - notify the gameinfo that it should end the game in defeat */
function ObjectiveFailed(Controller Failer, optional bool SkipActions)
{
local int i;
local Controller C;
local PlayerController PC;
if(IsValidForActivation())
{
bFailed = true;
Log("======================================",'Story_Debug');
Log("OBJECTIVE "@ObjectiveName@" FAILED ",'Story_Debug');
Log("======================================",'Story_Debug');
for(i = 0 ; i < FailureEvents.length ; i ++)
{
TriggerEvent(FailureEvents[i],self,Instigator);
}
if(FailureText != "")
{
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
PC = PlayerController(C);
if(PC != none)
{
PC.ClientMessage( FailureText, 'CriticalEvent');
}
}
}
if(!SkipActions)
{
bPendingFailureActions = true;
}
if(StoryGI != none)
{
StoryGI.OnObjectiveFailed(self);
}
}
}
/* Deferred execution of actions to allow Actors which are triggered by Success / Failure events to be ticked before
the objective is changed */
function ExecuteFailureActions()
{
if(FailureAction != none)
{
FailureAction.ExecuteAction(Instigator.Controller);
}
}
/* for triggering events - Some actors require a human pawn instigator to receive trigger events. Movers for example.*/
function SetHumanInstigator(Actor NewInstigator)
{
local Controller C;
if(Pawn(NewInstigator) != none &&
PlayerController(Pawn(NewInstigator).Controller) != none)
{
Instigator = Pawn(NewInstigator);
}
else // time to get a little more creative ...
{
for ( C=Level.ControllerList; C!=None; C=C.NextController )
{
if(PlayerController(C) != none && C.Pawn != none)
{
Instigator = C.Pawn ;
break;
}
}
}
}
function Touch( Actor Other )
{
if(IsCurrentObjective())
{
if(Pawn(Other) != none && Pawn(Other).Controller != none && Pawn(Other).Controller.bIsPlayer)
{
bWasTouched = true;
}
}
}
function Trigger( actor Other, pawn EventInstigator )
{
if(PendingGameRestart() || bCompleted || bFailed)
{
return ;
}
/* triggering an inactive objective will activate it and set it as the current objective .. */
if(!IsCurrentObjective())
{
StoryGI.SetActiveObjective(self,Instigator);
}
}
function Tick(float DeltaTime)
{
if(bCompleted || bFailed)
{
if(bPendingFailureActions)
{
bPendingFailureActions = false;
ExecuteFailureActions();
}
if(bPendingSuccessActions)
{
bPendingSuccessActions = false;
ExecuteSuccessActions();
}
/* This would only happen if we didn't have any success / failure action defined
that transitioned to another objective.*/
if(IsCurrentObjective() &&
(bCompleted || bFailed))
{
StoryGI.SetActiveObjective(none);
}
}
else
{
if(IsCurrentObjective())
{
TickConditions(DeltaTime);
RefreshWhispTrails();
if(CheckConditions())
{
ObjectiveCompleted(Instigator.Controller);
}
}
}
}
function RefreshWhispTrails()
{
local Controller C;
local PlayerController PC;
if( bShowWhispTrails && Level.TimeSeconds - LastWhispTime > WhispInterval)
{
LastWhispTime = Level.TimeSeconds;
For( C=Level.ControllerList; C!=None; C=C.NextController )
{
PC = PlayerController(C);
if( PC != none && PC.Pawn!=None && PC.Pawn.Health>0 )
{
StoryGI.ShowPathToObj(PC);
}
}
}
}
function TickConditions(float DeltaTime)
{
local int i;
local bool bUpdatedTime;
for(i = 0 ; i < AllConditions.length ; i ++)
{
if(AllConditions[i].GetObjOwner() == self &&
AllConditions[i].ConditionIsActive())
{
AllConditions[i].ConditionTick(DeltaTime);
/* Kind of a hack - we need to grab the remaining time for an
active timer condition to use on the trader menu */
if(!bUpdatedTime && ObjCondition_Timed(AllConditions[i]) != none)
{
bUpdatedTime = true;
RemainingTime = ObjCondition_Timed(AllConditions[i]).RemainingSeconds ;
}
}
}
}
/* Returns true if this Objective has a 'TraderTime' condition that is currently active */
function bool IsTraderObj()
{
local int i;
for(i = 0 ; i < SuccessConditions.length ; i ++)
{
if(SuccessConditions[i].GetObjOwner() == self &&
SuccessConditions[i].ConditionIsActive() &&
ObjCondition_TraderTime(SuccessConditions[i]) != none )
{
return true;
}
}
return false;
}
function bool PendingGameRestart()
{
return StoryGI != none && StoryGI.bPendingTeamRespawn ;
}
/* Returns true if this condition's progress should be used to determine the
completion state of the objective. Inactive or optional conditions are ignored .. */
function bool CheckConditionProgress(KF_ObjectiveCondition TestCondition)
{
return TestCondition != none &&
TestCondition.ProgressImportance != Ignore &&
TestCondition.bActive &&
TestCondition.ConditionIsValid();
}
/* Verifies when and If this objective has satisfied (or failed)
all its conditions
We failed/Succeeded if ...
A. a condition marked 'Critical' was completed.
B. all conditions with normal importance were completed *
*/
function bool CheckConditions()
{
local int i;
local bool bNewFailedObj;
local int NumGoals,NumComplete,NumFailed;
local int NumFailConditions;
local bool bNewComplete;
if(bCompleted || PendingGameRestart())
{
return false;
}
/* Check the failure conditions first - If we screwed this objective up, no point checking anything else */
for(i = 0 ; i < FailureConditions.length ; i ++)
{
if(CheckConditionProgress(FailureConditions[i]))
{
NumFailConditions ++ ;
if(FailureConditions[i].bComplete)
{
NumFailed ++ ;
}
bNewFailedObj = (FailureConditions[i].bComplete &&
FailureConditions[i].ProgressImportance == Critical) ;
if(bNewFailedObj)
{
SetHumanInstigator(FailureConditions[i].GetInstigator());
break;
}
}
}
i = 0;
bNewFailedObj = bNewFailedObj || (NumFailConditions > 0 && NumFailed >= NumFailConditions);
if(bNewFailedObj != bFailed)
{
if(bNewFailedObj)
{
ObjectiveFailed(Instigator.Controller);
}
}
if( bFailed )
{
return false;
}
/* Ok - If we got this far we haven't failed this objective so ... Check to see if we've succeeded instead */
for(i = 0 ; i < SuccessConditions.length ; i ++)
{
if(CheckConditionProgress(SuccessConditions[i]))
{
NumGoals ++ ;
if(SuccessConditions[i].bComplete)
{
NumComplete ++ ;
}
bNewComplete = (SuccessConditions[i].bComplete &&
SuccessConditions[i].ProgressImportance == Critical) ;
if(bNewComplete)
{
SetHumanInstigator(SuccessConditions[i].GetInstigator());
break;
}
}
}
bNewComplete = bNewComplete || (NumGoals > 0 && NumComplete >= NumGoals) ;
if(NumGoals == 0)
{
log("WARNING - no Success Conditions assigned to : "@ObjectiveName@". It is impossible to complete this objective.",'Story_Debug');
}
return bNewComplete;
}
// called if this Actor was touching a Pawn who pressed Use
event UsedBy( Pawn user )
{
local int i;
/* If we don't have to hold the key down, just immediately complete */
if(!bCompleted && IsCurrentObjective() && User != none)
{
bWasUsed = true;
for(i = 0 ; i < AllConditions.length ; i ++)
{
AllConditions[i].StartedUsing(user);
}
}
}
/* We walked away from this Objective - stop using it */
event UnTouch( Actor Other )
{
if(Pawn(Other) != none)
{
StoppedUsing(Pawn(Other));
}
}
/* called by KFPlayerController_Story when the player releases the Use key */
function StoppedUsing( Pawn user)
{
local int i;
for(i = 0 ; i < AllConditions.length ; i ++)
{
AllConditions[i].StoppedUsing(user);
}
}
/* Average completion %age of active failure conditions */
function float GetFailureProgressPct()
{
local float Pct;
local int i;
local float HighestPct;
if(bFailed)
{
return 1.f;
}
for( i = 0 ; i < FailureConditions.length ; i ++)
{
Pct = FailureConditions[i].GetCompletionPct();
if(Pct > HighestPct)
{
HighestPct = Pct;
}
}
return HighestPct;
}
/* Average completion %age of active success conditions */
function float GetSuccessProgressPct()
{
local float Pct;
local int i;
if(bCompleted)
{
return 1.f;
}
for( i = 0 ; i < SuccessConditions.length ; i ++)
{
Pct += SuccessConditions[i].GetCompletionPct();
}
Pct /= SuccessConditions.length ;
return Pct;
}
simulated function KF_ObjectiveCondition FindConditionByName(name ConditionName)
{
local int i;
for(i = 0 ; i < AllConditions.length ; i++)
{
if(AllConditions[i].Name == ConditionName)
{
return AllConditions[i];
}
}
}
simulated function int FindIndexForCondition(KF_ObjectiveCondition TestCondition, optional name TestConditionName, optional bool bSearchAllConditionArray)
{
local int i;
local array<KF_ObjectiveCondition> SearchArray;
if(TestCondition == none && TestConditionName != '')
{
for(i = 0 ; i < Allconditions.length ; i ++)
{
if(AllConditions[i].name == TestConditionName)
{
TestCondition = AllConditions[i] ;
if(bSearchAllConditionArray)
{
return i;
}
break;
}
}
}
i = 0;
switch(TestCondition.ConditionType)
{
case 0 : SearchArray = FailureConditions; break;
case 1 : SearchArray = SuccessConditions; break;
case 2 : SearchArray = OptionalConditions; break;
}
for(i = 0 ; i < SearchArray.length ; i ++)
{
if(SearchArray[i] == TestCondition)
{
return i;
}
}
log("Warning - Could not find index for condition : "@TestCondition@" in "@ObjectiveName,'Story_Debug');
return -1 ;
}
/* Debug helper function - Returns the names of the next Objective after this one */
function name GetNextObjectiveName()
{
return '' ;
}
function SetCheckPoint(KF_StoryCheckPointVolume Instigator)
{
local int i;
if(bCheckPointable)
{
ActiveCheckPoint = Instigator;
for(i = 0 ; i < AllConditions.length ; i ++)
{
AllConditions[i].SaveState();
}
}
}
function ClearCheckPoint()
{
ActiveCheckPoint = none;
}
/* ======= UnrealED Storymode Visualisation ====================================================================================
================================================================================================================================*/
function GetEvents(out array<name> TriggeredEvents, out array<name> ReceivedEvents)
{
local int i,idx;
Super.GetEvents(TriggeredEvents,ReceivedEvents);
/* Things this Objective Triggers */
for(i = 0 ; i < ActivationEvents.length ; i ++)
{
if(ActivationEvents[i] != '')
{
TriggeredEvents[TriggeredEvents.length] = ActivationEvents[i];
}
}
for(i = 0 ; i < DeActivationEvents.length ; i ++)
{
if(DeActivationEvents[i] != '')
{
TriggeredEvents[TriggeredEvents.length] = DeActivationEvents[i];
}
}
for(i = 0 ; i < CompletionEvents.length ; i ++)
{
if(CompletionEvents[i] != '')
{
TriggeredEvents[TriggeredEvents.length] = CompletionEvents[i];
}
}
for(i = 0 ; i < FailureEvents.length ; i ++)
{
if(FailureEvents[i] != '')
{
TriggeredEvents[TriggeredEvents.length] = FailureEvents[i];
}
}
for(i = 0 ; i < SuccessConditions.length ; i ++)
{
for(idx = 0 ; idx < SuccessConditions[i].ProgressEvents.length ;idx ++)
{
if(SuccessConditions[i].ProgressEvents[idx].EventName != '')
{
TriggeredEvents[TriggeredEvents.length] = SuccessConditions[i].ProgressEvents[idx].EventName;
}
}
if(SuccessConditions[i].Tag != '' &&
SuccessConditions[i].ActivationMethod > 0 &&
SuccessConditions[i].ActivationMethod < 4)
{
ReceivedEvents[ReceivedEvents.length] = SuccessConditions[i].Tag ;
}
}
for(i = 0 ; i < FailureConditions.length ; i ++)
{
for(idx = 0 ; idx <FailureConditions[i].ProgressEvents.length ;idx ++)
{
if(FailureConditions[i].ProgressEvents[idx].EventName != '')
{
TriggeredEvents[TriggeredEvents.length] = FailureConditions[i].ProgressEvents[idx].EventName;
}
}
if(FailureConditions[i].Tag != '' &&
FailureConditions[i].ActivationMethod > 0 &&
FailureConditions[i].ActivationMethod < 4)
{
ReceivedEvents[ReceivedEvents.length] = FailureConditions[i].Tag ;
}
}
for(i = 0 ; i < OptionalConditions.length ; i ++)
{
for(idx = 0 ; idx <OptionalConditions[i].ProgressEvents.length ;idx ++)
{
if(OptionalConditions[i].ProgressEvents[idx].EventName != '')
{
TriggeredEvents[TriggeredEvents.length] = OptionalConditions[i].ProgressEvents[idx].EventName;
}
}
if(OptionalConditions[i].Tag != '' &&
OptionalConditions[i].ActivationMethod > 0 &&
OptionalConditions[i].ActivationMethod < 4)
{
ReceivedEvents[ReceivedEvents.length] = OptionalConditions[i].Tag ;
}
}
}
/* ======= UnrealED Storymode Visualisation ====================================================================================
================================================================================================================================*/
defaultproperties
{
Begin Object Class=ObjAction_GoToNextObjective Name=DefaultSuccessAction
End Object
SuccessAction=DefaultSuccessAction
WhispInterval = 1.1
MaxRepUpdatesPerSecond = 10
bCheckPointable = true
bResetOnDeactivation=true
bStatic = false
RemoteRole = Role_None
bCollideActors = true
bHidden = true
bNoDelete=true
Texture = Texture 'KFStoryGame_Tex.Editor.KFObj_Ico'
bShowOnHUD = true
DrawScale = 0.5
// Audio
MusicFadeinTime = 1.0
MusicFadeOutTime = 2.0
bStopMusicOnCompletion = true
HUD_Header=(Header_Color=(R=255,G=255,B=255,A=255),Header_Scale=Font_Large)
HUD_Background=(Background_Texture=Texture'KFStoryGame_Tex.HUD.ObjectiveHUDBG',Background_Texture_Collapsed=Texture'KFStoryGame_Tex.HUD.ObjectiveHUDBG_Collapsed',Background_AspectRatio=Aspect_FromTexture,BackGround_Padding=64,Background_Scale=1.f)
HUD_ScreenPosition=(Horizontal=1,Vertical=0.02)
}