491 lines
15 KiB
Ucode
491 lines
15 KiB
Ucode
//=============================================================================
|
|
// KFGameInfo_Tutorial
|
|
//=============================================================================
|
|
// The game mode class for a single player tutorial
|
|
//=============================================================================
|
|
// Killing Floor 2
|
|
// Copyright (C) 2016 Tripwire Interactive LLC
|
|
//=============================================================================
|
|
|
|
class KFGameInfo_Tutorial extends KFGameInfo_Survival
|
|
dependson(KFTutorialSectionInfo)
|
|
hidedropdown;
|
|
|
|
var class<KFGFxMoviePlayer_Tutorial> MoviePlayerTutorialClass;
|
|
var KFGFxMoviePlayer_Tutorial MyGFXTutorial;
|
|
|
|
var KFTutorialSectionInfo LobbyStartInfo;
|
|
var KFTutorialSectionInfo StartMatchInfo;
|
|
var KFTutorialSectionInfo Wave1EndInfo;
|
|
var KFTutorialSectionInfo TraderCloseInfo;
|
|
var KFTutorialSectionInfo VictoryInfo;
|
|
var KFTutorialSectionInfo HealSelfInfo;
|
|
var KFTutorialSectionInfo ZedTimeInfo;
|
|
var KFTutorialSectionInfo MeleeTutorial;
|
|
|
|
var KFTutorialSectionInfo TraderMenuTutorial;
|
|
var KFTutorialSectionInfo PerkMenuTutorial;
|
|
var KFTutorialSectionInfo GearMenuTutorial;
|
|
var KFTutorialSectionInfo InventoryMenuTutorial;
|
|
var KFTutorialSectionInfo StoreMenuTutorial;
|
|
|
|
var bool bShowedHealSelf;
|
|
var bool bShowedZedTime;
|
|
var bool bShowedTraderTutorial;
|
|
var bool bShowedMeleeTutorial;
|
|
var bool bShowedPerkMenuTutorial;
|
|
var bool bShowedGearMenuTutorial;
|
|
var bool bShowedInventoryMenuTutorial;
|
|
var bool bShowedStoreMenuTutorial;
|
|
var bool bShowedTraderCloseInfo;
|
|
|
|
/** Timer countdown (seconds) after player is done trading */
|
|
var int TimeAfterTrading;
|
|
|
|
/** Amount of dosh the player spawns with in the tutorial */
|
|
var int StartingDosh;
|
|
|
|
/** Cached local player controller ref */
|
|
var KFPlayerController MyPC;
|
|
/** Cached weapon for melee tutorial */
|
|
var Weapon CurrentWeapon;
|
|
|
|
event InitGame( string Options, out string ErrorMessage )
|
|
{
|
|
// Force standalone for this game mode. SetGameType() would be
|
|
// ideal, but NetMode has no been set yet.
|
|
if ( WorldInfo.NetMode != NM_Standalone )
|
|
{
|
|
ConsoleCommand("open" @ WorldInfo.GetMapName(true));
|
|
return;
|
|
}
|
|
|
|
Super.InitGame(Options, ErrorMessage);
|
|
}
|
|
|
|
event PostBeginPlay()
|
|
{
|
|
super.PostBeginPlay();
|
|
|
|
// use defaultprops instead of difficulty sysstem
|
|
TimeBetweenWaves = default.TimeBetweenWaves;
|
|
`TraderDialogManager.bEnabled = false;
|
|
}
|
|
|
|
/** We're going to use our local PC often, so may as well cache it */
|
|
event PostLogin( PlayerController NewPlayer )
|
|
{
|
|
Super.PostLogin(NewPlayer);
|
|
|
|
// We're going to use our local PC often, so may as well cache it
|
|
if ( NewPlayer.IsLocalController() )
|
|
{
|
|
MyPC = KFPlayerController(NewPlayer);
|
|
CreateTutorialHUD(LobbyStartInfo);
|
|
}
|
|
}
|
|
|
|
/** Override starting dosh */
|
|
function RestartPlayer(Controller NewPlayer)
|
|
{
|
|
Super.RestartPlayer(NewPlayer);
|
|
|
|
if( NewPlayer.PlayerReplicationInfo != None )
|
|
{
|
|
NewPlayer.PlayerReplicationInfo.Score = StartingDosh;
|
|
CreateTutorialHUD(StartMatchInfo);
|
|
}
|
|
}
|
|
|
|
/** 50% damage recieved */
|
|
function ReduceDamage(out int Damage, Pawn Injured, Controller InstigatedBy, vector HitLocation, out vector Momentum, class<DamageType> DamageType, Actor DamageCauser, TraceHitInfo HitInfo)
|
|
{
|
|
if ( Injured.IsHumanControlled() )
|
|
{
|
|
//Damage = Max(Round(Damage / 2.f), 1);
|
|
if(!bShowedHealSelf)
|
|
{
|
|
if(Injured.Health <= Injured.HealthMax * 0.90f)
|
|
{
|
|
CreateTutorialHUD(HealSelfInfo);
|
|
bShowedHealSelf = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// checks neutral zone and god mode and calls mutator hook
|
|
Super.ReduceDamage(Damage, Injured, InstigatedBy, HitLocation, Momentum, DamageType, DamageCauser, HitInfo);
|
|
}
|
|
|
|
function bool PreventDeath(Pawn KilledPawn, Controller Killer, class<DamageType> DamageType, vector HitLocation)
|
|
{
|
|
if ( KilledPawn.IsHumanControlled() )
|
|
{
|
|
// give some bonus health so we're not always at 1 health
|
|
KilledPawn.Health = KilledPawn.HealthMax * 0.25f;
|
|
return true;
|
|
}
|
|
|
|
return Super.PreventDeath(KilledPawn, Killer, DamageType, HitLocation);
|
|
}
|
|
|
|
/** */
|
|
function WaveEnded(EWaveEndCondition WinCondition)
|
|
{
|
|
// Skip the final "dummy" wave and go to match ended
|
|
if ( WaveNum == (WaveMax - 1) )
|
|
{
|
|
WaveNum = WaveMax;
|
|
}
|
|
|
|
Super.WaveEnded(WinCondition);
|
|
}
|
|
|
|
/** */
|
|
function EndOfMatch(bool bVictory)
|
|
{
|
|
Super.EndOfMatch(bVictory);
|
|
|
|
if ( bVictory )
|
|
{
|
|
// Console builds try to claim tutorial rewards now
|
|
//@SABER_EGS IsEosBuild() case added
|
|
if( WorldInfo.IsConsoleBuild() || WorldInfo.IsEosBuild())
|
|
{
|
|
PlayfabInter.AddOnCloudScriptExecutionCompleteDelegate( OnTutorialRewardsComplete );
|
|
PlayfabInter.ExecuteCloudScript( "ClaimTutorialRewards", none );
|
|
}
|
|
CreateTutorialHUD(VictoryInfo);
|
|
}
|
|
}
|
|
|
|
|
|
function OnTutorialRewardsComplete( bool bWasSuccessful, string FunctionName, JsonObject FunctionResult )
|
|
{
|
|
PlayfabInter.ClearOnCloudScriptExecutionCompleteDelegate( OnTutorialRewardsComplete );
|
|
// On success, re-read inventory
|
|
if( bWasSuccessful && FunctionName == "ClaimTutorialRewards" )
|
|
{
|
|
class'GameEngine'.static.GetPlayfabInterface().ReadInventory();
|
|
}
|
|
}
|
|
|
|
|
|
/** Skip the AAR and return to main menu */
|
|
function ShowPostGameMenu()
|
|
{
|
|
ConsoleCommand("disconnect");
|
|
}
|
|
|
|
/** Picks next trader index and allows for mutator hook */
|
|
function byte DetermineNextTraderIndex()
|
|
{
|
|
local int index;
|
|
local KFTraderTrigger Trader;
|
|
|
|
// In evac there is a specific trader we want near some doors to weld
|
|
ForEach TraderList(Trader, index)
|
|
{
|
|
if ( Trader.Name == 'KFTraderTrigger_17' )
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return super.DetermineNextTraderIndex();
|
|
}
|
|
|
|
/** No pickups introduced in the first round */
|
|
function ResetPickups( array<KFPickupFactory> PickupList, int NumPickups )
|
|
{
|
|
if( WaveNum == 1 )
|
|
{
|
|
Super(KFGameInfo).ResetPickups(PickupList, 0);
|
|
}
|
|
|
|
super.ResetPickups( PickupList, NumPickups );
|
|
}
|
|
|
|
function SetZedTimeDilation(float NewTimeDilation)
|
|
{
|
|
super.SetZedTimeDilation(NewTimeDilation);
|
|
if(NewTimeDilation == ZedTimeSlomoScale)
|
|
{
|
|
if(!bShowedZedTime)
|
|
{
|
|
CreateTutorialHUD(ZedTimeInfo);
|
|
bShowedZedTime = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** check main menu tutorial(s) */
|
|
function NotifyMenuOpened(byte MenuIndex)
|
|
{
|
|
switch(MenuIndex)
|
|
{
|
|
case UI_Perks:
|
|
if ( !bShowedPerkMenuTutorial )
|
|
{
|
|
CreateTutorialHUD(PerkMenuTutorial);
|
|
bShowedPerkMenuTutorial = true;
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case UI_Gear:
|
|
if ( !bShowedGearMenuTutorial )
|
|
{
|
|
CreateTutorialHUD(GearMenuTutorial);
|
|
bShowedGearMenuTutorial = true;
|
|
return;
|
|
}
|
|
break;
|
|
case UI_Inventory:
|
|
if ( !bShowedInventoryMenuTutorial )
|
|
{
|
|
CreateTutorialHUD(InventoryMenuTutorial);
|
|
bShowedInventoryMenuTutorial = true;
|
|
return;
|
|
}
|
|
break;
|
|
case UI_Store:
|
|
if ( !bShowedStoreMenuTutorial )
|
|
{
|
|
CreateTutorialHUD(StoreMenuTutorial);
|
|
bShowedStoreMenuTutorial = true;
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* KFGFxMoviePlayer_Tutorial Interface
|
|
*********************************************************************************************/
|
|
|
|
function bool AllowUnpause()
|
|
{
|
|
if(MyGFXTutorial != none)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Create/Open a tutorial popup */
|
|
function CreateTutorialHUD(KFTutorialSectionInfo TutorialSectionInfo, optional bool bViaTimer)
|
|
{
|
|
local PlayerController PC;
|
|
|
|
// If we asked for a tutorial that requires a delay do that now. Track
|
|
// on the tutorial object so we can have multiple simultaneous timers
|
|
if ( !bViaTimer && TutorialSectionInfo.OpenDelay > 0 )
|
|
{
|
|
SetTimer(TutorialSectionInfo.OpenDelay, false, nameof(TutorialSectionInfo.OpenDelayTimer), TutorialSectionInfo);
|
|
return;
|
|
}
|
|
|
|
PC = GetALocalPlayerController();
|
|
|
|
PC.PlayerInput.ResetInput();
|
|
|
|
// @todo: object creation should only be done once?
|
|
if(MyGFXTutorial == none && MoviePlayerTutorialClass != None )
|
|
{
|
|
MyGFXTutorial = new MoviePlayerTutorialClass;
|
|
MyGFXTutorial.SetTimingMode(TM_Real);
|
|
MyGFXTutorial.TutorialSectionInfo = TutorialSectionInfo;
|
|
MyGFXTutorial.TargetTutorialArray = TutorialSectionInfo.GetSlides(PC);
|
|
}
|
|
|
|
if(PC != none && MyGFXTutorial != none)
|
|
{
|
|
MyGFXTutorial.Init(class'Engine'.static.GetEngine().GamePlayers[MyGFXTutorial.LocalPlayerOwnerIndex]);
|
|
PC.SetPause(true, AllowUnpause);
|
|
if(MyGFXTutorial.TutorialSectionInfo.VoiceOverOpenEvent != none)
|
|
{
|
|
PC.PlaySoundBase( MyGFXTutorial.TutorialSectionInfo.VoiceOverOpenEvent, true, false, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Close, unpause, and delete the tutorial UI object **/
|
|
function RemoveTutorialHud()
|
|
{
|
|
local PlayerController PC;
|
|
|
|
if ( MyGFXTutorial != None )
|
|
{
|
|
PC = GetALocalPlayerController();
|
|
if(PC != none)
|
|
{
|
|
if(MyGFXTutorial.TutorialSectionInfo.VoiceOverOpenEvent != none)
|
|
{
|
|
PC.PlaySoundBase( MyGFXTutorial.TutorialSectionInfo.VoiceOverCloseEvent, true, false, false );
|
|
}
|
|
MyGFXTutorial.Close(true);
|
|
MyGFXTutorial = None;
|
|
PC.SetPause(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function NotifyControllerDisconnected()
|
|
{
|
|
if( MyGFXTutorial != none )
|
|
{
|
|
MyGFXTutorial.SetVisibility( false );
|
|
// Make tutorial hide if we need to show controller disconnect dialog
|
|
MyGFXTutorial.SetPriority(1);
|
|
KFPlayerController(GetALocalPlayerController()).MyGFxManager.SetPriority(2);
|
|
}
|
|
}
|
|
|
|
|
|
function NotifyControllerReconnected()
|
|
{
|
|
if (MyGFXTutorial != none)
|
|
{
|
|
MyGFXTutorial.SetVisibility( true );
|
|
// Put tutorial back on top
|
|
MyGFXTutorial.SetPriority(2);
|
|
KFPlayerController(GetALocalPlayerController()).MyGFxManager.SetPriority(1);
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************
|
|
* Update
|
|
*********************************************************************************************/
|
|
|
|
function Tick( float DeltaTime )
|
|
{
|
|
Super.Tick(DeltaTime);
|
|
|
|
// Normally we wouldn't use a script tick for something like this, but in this
|
|
// case it's a little cleaner using the brute force approach and this is not
|
|
// a performance critical class.
|
|
CheckPlayerAction();
|
|
}
|
|
|
|
/** Brute force update for what a player has done recently so we can display tutorials */
|
|
function CheckPlayerAction()
|
|
{
|
|
// check melee tutorial
|
|
if ( !bShowedMeleeTutorial && MyPC.Pawn != None && CurrentWeapon != MyPC.Pawn.Weapon )
|
|
{
|
|
if ( MyPC.Pawn.Weapon.IsA('KFWeap_MeleeBase') )
|
|
{
|
|
CreateTutorialHUD(MeleeTutorial);
|
|
bShowedMeleeTutorial = true;
|
|
}
|
|
CurrentWeapon = MyPC.Pawn.Weapon;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************
|
|
* state TraderOpen
|
|
*********************************************************************************************/
|
|
|
|
State TraderOpen
|
|
{
|
|
function BeginState( Name PreviousStateName )
|
|
{
|
|
super.BeginState(PreviousStateName);
|
|
CreateTutorialHUD(Wave1EndInfo);
|
|
}
|
|
|
|
// Normally we would use Timer(), but perf is not an issue here
|
|
function CheckPlayerAction()
|
|
{
|
|
// Notify when menu is closed. Menu wil be 'None' before it's been opened for the first time
|
|
if ( MyKFGRI.RemainingTime > TimeAfterTrading )
|
|
{
|
|
if ( MyPC != None && MyPC.MyGFxManager.TraderMenu != None )
|
|
{
|
|
if ( !MyPC.bClientTraderMenuOpen )
|
|
{
|
|
OnTraderMenuClosed();
|
|
return;
|
|
}
|
|
else if ( !bShowedTraderTutorial )
|
|
{
|
|
CreateTutorialHUD(TraderMenuTutorial);
|
|
bShowedTraderTutorial = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
MyKFGRI.RemainingTime = TimeBetweenWaves;
|
|
MyKFGRI.RemainingMinute = TimeBetweenWaves;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( MyPC.bClientTraderMenuOpen )
|
|
{
|
|
MyKFGRI.RemainingTime = TimeBetweenWaves;
|
|
MyKFGRI.RemainingMinute = TimeBetweenWaves;
|
|
ClearTimer(nameof(CloseTraderTimer));
|
|
}
|
|
|
|
Global.CheckPlayerAction();
|
|
}
|
|
}
|
|
|
|
function OnTraderMenuClosed()
|
|
{
|
|
// shorten time remaining before next wave
|
|
if ( MyKFGRI.RemainingTime > TimeAfterTrading )
|
|
{
|
|
MyKFGRI.RemainingTime = TimeAfterTrading;
|
|
MyKFGRI.RemainingMinute = TimeAfterTrading;
|
|
|
|
if( !bShowedTraderCloseInfo )
|
|
{
|
|
CreateTutorialHUD(TraderCloseInfo);
|
|
bShowedTraderCloseInfo = true;
|
|
}
|
|
|
|
// refresh timer
|
|
SetTimer(TimeAfterTrading, false, nameof(CloseTraderTimer));
|
|
}
|
|
}
|
|
|
|
function bool IsMapObjectiveEnabled()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
DefaultProperties
|
|
{
|
|
StartMatchInfo=KFTutorialSectionInfo'GP_Tutorial_ARCH.StartMatchArch'
|
|
Wave1EndInfo=KFTutorialSectionInfo'GP_Tutorial_ARCH.Wave1EndArch'
|
|
TraderCloseInfo=KFTutorialSectionInfo'GP_Tutorial_ARCH.TraderCloseArch'
|
|
VictoryInfo=KFTutorialSectionInfo'GP_Tutorial_ARCH.VictoryArch'
|
|
HealSelfInfo=KFTutorialSectionInfo'GP_Tutorial_ARCH.HealSelfArch'
|
|
ZedTimeInfo=KFTutorialSectionInfo'GP_Tutorial_ARCH.ZedTimeArch'
|
|
LobbyStartInfo=KFTutorialSectionInfo'GP_Tutorial_ARCH.LobbyStartArch'
|
|
MeleeTutorial=KFTutorialSectionInfo'GP_Tutorial_ARCH.MeleeArch'
|
|
|
|
PerkMenuTutorial=KFTutorialSectionInfo'GP_Tutorial_ARCH.PerkMenuArch'
|
|
GearMenuTutorial=KFTutorialSectionInfo'GP_Tutorial_ARCH.GearMenuArch'
|
|
InventoryMenuTutorial=KFTutorialSectionInfo'GP_Tutorial_ARCH.InventoryMenuArch'
|
|
StoreMenuTutorial=KFTutorialSectionInfo'GP_Tutorial_ARCH.StoreMenuArch'
|
|
TraderMenuTutorial=KFTutorialSectionInfo'GP_Tutorial_ARCH.TraderOpenArch'
|
|
|
|
// tutorial subclass to catch player menu navigation
|
|
KFGFxManagerClass=class'KFGFxMoviePlayer_Manager_Tutorial'
|
|
|
|
MoviePlayerTutorialClass =Class'KFGFxMoviePlayer_Tutorial'
|
|
MaxGameDifficulty=0
|
|
SpawnManagerClasses.Empty
|
|
SpawnManagerClasses(0)=class'KFAISpawnManager_Tutorial'
|
|
|
|
TimeBetweenWaves=600
|
|
TimeAfterTrading=30
|
|
StartingDosh=2000
|
|
}
|