From 0b5197f92337c1ea48f2e53913f15da0ca010cd2 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 19:05:32 +0300 Subject: [PATCH 01/14] make it server-side --- StartWave/Classes/StartWave.upkg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 StartWave/Classes/StartWave.upkg diff --git a/StartWave/Classes/StartWave.upkg b/StartWave/Classes/StartWave.upkg new file mode 100644 index 0000000..ae6c83c --- /dev/null +++ b/StartWave/Classes/StartWave.upkg @@ -0,0 +1,4 @@ +[Flags] +AllowDownload=False +ClientOptional=False +ServerSideOnly=True From 98102cf58a0b04350d762d350668fdf634ad3989 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 19:48:47 +0300 Subject: [PATCH 02/14] replace logger --- StartWave/Classes/StartWave.uc | 61 ++++++++++++++++------------------ StartWave/Classes/_Logger.uc | 20 +++++++++++ StartWave/Globals.uci | 2 ++ StartWave/Logger.uci | 15 +++++++++ 4 files changed, 65 insertions(+), 33 deletions(-) create mode 100644 StartWave/Classes/_Logger.uc create mode 100644 StartWave/Globals.uci create mode 100644 StartWave/Logger.uci diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index 6f70c4b..e7aec45 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -15,8 +15,9 @@ var config int TraderTime; var config int Dosh; /** Whether an 'initial' trader time should occur before the first wave. */ var config bool bStartWithTrader; -/** Whether mod-specific events should be logged. */ -var config bool bUseDebug; +/** Log level. */ +var config E_LogLevel LogLevel; + /** * The boss override index. For the default boss list, 0-Hans, 1-Patty, 2-King FP, 3-Abomination. Negative * values can be used to keep the boss spawn random. @@ -50,15 +51,15 @@ function InitMutator(string Options, out string ErrorMessage) Dosh = Max(class'GameInfo'.static.GetIntOption(Options, "Dosh", Dosh), 0); Boss = class'GameInfo'.static.GetIntOption(Options, "Boss", Boss); bStartWithTrader = GetBoolOption(Options, "bStartWithTrader", bStartWithTrader); - bUseDebug = GetBoolOption(Options, "bUseDebug", bUseDebug); + LogLevel = E_LogLevel(class'GameInfo'.static.GetIntOption(Options, "LogLevel", LogLevel)); //DEBUG - `log("StartWave: "$StartWave, bUseDebug, 'StartWave'); - `log("InitialTraderTime: "$InitialTraderTime, bUseDebug, 'StartWave'); - `log("TraderTime: "$TraderTime, bUseDebug, 'StartWave'); - `log("Dosh: "$Dosh, bUseDebug, 'StartWave'); - `log("Boss: "$Boss, bUseDebug, 'StartWave'); - `log("bStartWithTrader: "$bStartWithTrader, bUseDebug, 'StartWave'); + `Log_Debug("StartWave: " $ StartWave); + `Log_Debug("InitialTraderTime: " $ InitialTraderTime); + `Log_Debug("TraderTime: " $ TraderTime); + `Log_Debug("Dosh: " $ Dosh); + `Log_Debug("Boss: " $ Boss); + `Log_Debug("bStartWithTrader: " $ bStartWithTrader); bOverridenDifficultySettings = false; bOverridenTraderDuration = false; @@ -79,16 +80,14 @@ function InitMutator(string Options, out string ErrorMessage) //If we want to start with the trader active or alter the starting wave number. if(bStartWithTrader || StartWave > 1) { - `log("Calling StartWaveTimer() to alter the start wave or activate the trader initially.", - bUseDebug, 'StartWave'); + `Log_Debug("Calling StartWaveTimer() to alter the start wave or activate the trader initially."); SetTimer(0.2, false, nameof(StartWaveTimer)); } //If we will need to alter TimeBetweenWaves for later activations of the trader. if(bStartWithTrader && InitialTraderTime != TraderTime) { - `log("Calling UpdateTraderDurationTimer() to alter the trader duration later.", bUseDebug, - 'StartWave'); + `Log_Debug("Calling UpdateTraderDurationTimer() to alter the trader duration later."); SetTimer(1, true, nameof(UpdateTraderDurationTimer)); } } @@ -131,7 +130,7 @@ function SetWave(int NewWaveNum, PlayerController PC, optional bool bSkipTraderT { if(NewWaveNum < 1) { - `log("SetWave: new wave num must be > 0.", true, 'StartWave'); + `Log_Error("SetWave: new wave num must be > 0."); return; } @@ -283,11 +282,11 @@ function OverrideBoss() if(MyKFGI.MyKFGRI.BossIndex == Boss) { - `log("Successfully overrode boss index to "$Boss$" after "$i$" attempts.", bUseDebug, 'StartWave'); + `Log_Debug("Successfully overrode boss index to" @ Boss @ "after" @ i @ "attempts."); } else { - `log("Failed to override boss index after "$i$" attempts.", bUseDebug, 'StartWave'); + `Log_Debug("Failed to override boss index after" @ i @ "attempts."); } } @@ -302,13 +301,13 @@ function OverrideTimer() //If we've overriden what we need to, don't call this timer again. if(bOverridenDifficultySettings && bOverridenTraderDuration) { - `log("All settings have been overriden.", bUseDebug, 'StartWave'); + `Log_Debug("All settings have been overriden."); return; } if(!bOverridenDifficultySettings && MyKFGI.DifficultyInfo != None) { - `log("Overriding difficulty settings...", bUseDebug, 'StartWave'); + `Log_Debug("Overriding difficulty settings..."); bOverridenDifficultySettings = true; @@ -331,7 +330,7 @@ function OverrideTimer() } } - `log("Starting dosh has been set to: "$Dosh$" dosh.", bUseDebug, 'StartWave'); + `Log_Debug("Starting dosh has been set to:" @ Dosh @ "dosh."); //We need to set the difficulty settings again - normally done in KFGameInfo.InitGame - to apply //these changes, since this happens after InitGame is executed. @@ -349,7 +348,7 @@ function OverrideTimer() //executed, which sets WaveMax. if(MyKFGI.SpawnManager != None) { - `log("Overriding trader duration...", bUseDebug, 'StartWave'); + `Log_Debug("Overriding trader duration..."); bOverridenTraderDuration = true; @@ -358,13 +357,11 @@ function OverrideTimer() //Now we can override TimeBetweenWaves. KFGI_Surv.TimeBetweenWaves = bInitialTrader ? InitialTraderTime : TraderTime; - `log("Trader duration has been set to: "$KFGI_Surv.TimeBetweenWaves$" seconds.", bUseDebug, - 'StartWave'); + `Log_Debug("Trader duration has been set to:" @ KFGI_Surv.TimeBetweenWaves @ "seconds."); } else { - `log("MyKFGI.SpawnManager hasn't been set yet. Calling StartWaveTimer again.", bUseDebug, - 'StartWave'); + `Log_Debug("MyKFGI.SpawnManager hasn't been set yet. Calling StartWaveTimer again."); //We don't know WaveMax yet, so we need to wait longer. SetTimer(0.1, false, nameof(StartWaveTimer)); @@ -373,8 +370,7 @@ function OverrideTimer() } else { - `warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not" - $"compatible with non-wave-based game modes.", true, 'StartWave'); + `Log_Warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not compatible with non-wave-based game modes."); } } @@ -402,7 +398,7 @@ function StartWaveTimer() return; } - `log("Clearing the current wave.", bUseDebug, 'StartWave'); + `Log_Debug("Clearing the current wave."); //Clear the current wave. foreach WorldInfo.AllControllers(class'PlayerController', PC) @@ -421,11 +417,11 @@ function StartWaveTimer() //incremented by 1. KFGI_Surv.WaveNum = StartWave - 1; - `log("WaveNum set to: "$KFGI_Surv.WaveNum, bUseDebug, 'StartWave'); + `Log_Debug("WaveNum set to:" @ KFGI_Surv.WaveNum); if(bStartWithTrader) { - `log("Switching to state: TraderOpen.", bUseDebug, 'StartWave'); + `Log_Debug("Switching to state: TraderOpen."); //We need to update GRI's WaveNum and update the HUD element that shows the last wave. MyKFGI.MyKFGRI.WaveNum = KFGI_Surv.WaveNum; @@ -436,7 +432,7 @@ function StartWaveTimer() } else { - `log("Switching to state: PlayingWave.", bUseDebug, 'StartWave'); + `Log_Debug("Switching to state: PlayingWave."); //Start with a wave as usual - but our StartWave number will be used. MyKFGI.GotoState('PlayingWave'); @@ -457,14 +453,13 @@ function UpdateTraderDurationTimer() { if(KFGameInfo_Survival(MyKFGI) != None) { - `log("Updating trader duration to "$TraderTime$" seconds.", bUseDebug, 'StartWave'); + `Log_Debug("Updating trader duration to" @ TraderTime @ "seconds."); //We can update TimeBetweenWaves to be the TraderTime we specified in the launch command. KFGameInfo_Survival(MyKFGI).TimeBetweenWaves = TraderTime; } else { - `warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not" - $"compatible with non-wave-based game modes.", true, 'StartWave'); + `Log_Warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not compatible with non-wave-based game modes."); } //We don't need to call this timer again. diff --git a/StartWave/Classes/_Logger.uc b/StartWave/Classes/_Logger.uc new file mode 100644 index 0000000..bf77f4e --- /dev/null +++ b/StartWave/Classes/_Logger.uc @@ -0,0 +1,20 @@ +class _Logger extends Object + abstract; + +enum E_LogLevel +{ + LL_WrongLevel, + LL_None, + LL_Fatal, + LL_Error, + LL_Warning, + LL_Info, + LL_Debug, + LL_Trace, + LL_All +}; + +defaultproperties +{ + +} \ No newline at end of file diff --git a/StartWave/Globals.uci b/StartWave/Globals.uci new file mode 100644 index 0000000..a71bff2 --- /dev/null +++ b/StartWave/Globals.uci @@ -0,0 +1,2 @@ +// Imports +`include(Logger.uci) diff --git a/StartWave/Logger.uci b/StartWave/Logger.uci new file mode 100644 index 0000000..7f2e321 --- /dev/null +++ b/StartWave/Logger.uci @@ -0,0 +1,15 @@ +// Logger +`define Log_Tag 'StartWave' + +`define LocationStatic "`{ClassName}::" $ GetFuncName() + +`define Log_Base(msg, cond) `log(`msg `if(`cond), `cond`{endif}, `Log_Tag) + +`define Log_Fatal(msg) `log("FATAL:" @ `msg, (LogLevel >= LL_Fatal), `Log_Tag) +`define Log_Error(msg) `log("ERROR:" @ `msg, (LogLevel >= LL_Error), `Log_Tag) +`define Log_Warn(msg) `log("WARN:" @ `msg, (LogLevel >= LL_Warning), `Log_Tag) +`define Log_Info(msg) `log("INFO:" @ `msg, (LogLevel >= LL_Info), `Log_Tag) +`define Log_Debug(msg) `log("DEBUG:" @ `msg, (LogLevel >= LL_Debug), `Log_Tag) + +`define Log_Trace(msg) `log("TRACE:" @ `Location `if(`msg) @ `msg`{endif}, (LogLevel >= LL_Trace), `Log_Tag) +`define Log_TraceStatic(msg) `log("TRACE:" @ `LocationStatic `if(`msg) @ `msg`{endif}, (LogLevel >= LL_Trace), `Log_Tag) From 7b5669febfc3b6b04f3a0af9d827064ac45eea7b Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 19:51:12 +0300 Subject: [PATCH 03/14] make .editorconfig checker happy --- StartWave/Classes/StartWave.uc | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index e7aec45..0a960c9 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -19,9 +19,9 @@ var config bool bStartWithTrader; var config E_LogLevel LogLevel; /** - * The boss override index. For the default boss list, 0-Hans, 1-Patty, 2-King FP, 3-Abomination. Negative - * values can be used to keep the boss spawn random. - */ +*** The boss override index. For the default boss list, 0-Hans, 1-Patty, 2-King FP, 3-Abomination. Negative +*** values can be used to keep the boss spawn random. +***/ var config int Boss; /********************************************************************************************************* @@ -181,9 +181,9 @@ function SetWave(int NewWaveNum, PlayerController PC, optional bool bSkipTraderT } /** - * Since the difficulty in Endless scales with the wave number, we need to update the difficulty when - * jumping between wave numbers to match the expected difficulty. - */ +*** Since the difficulty in Endless scales with the wave number, we need to update the difficulty when +*** jumping between wave numbers to match the expected difficulty. +***/ function UpdateEndlessDifficulty() { local KFGameInfo_Endless Endless; @@ -468,13 +468,13 @@ function UpdateTraderDurationTimer() } /** - * @brief Gets a bool from the launch command if available. - * - * @param Options - options passed in via the launch command - * @param ParseString - the variable we are looking for - * @param CurrentValue - the current value of the variable - * @return bool value of the option we are looking for - */ +*** @brief Gets a bool from the launch command if available. +*** +*** @param Options - options passed in via the launch command +*** @param ParseString - the variable we are looking for +*** @param CurrentValue - the current value of the variable +*** @return bool value of the option we are looking for +***/ static function bool GetBoolOption(string Options, string ParseString, bool CurrentValue) { local string InOpt; @@ -492,13 +492,13 @@ static function bool GetBoolOption(string Options, string ParseString, bool Curr } /** - * @brief Gets a string from the launch command if available. - * - * @param Options - options passed in via the launch command - * @param ParseString - the variable we are looking for - * @param CurrentValue - the current value of the variable - * @return string value of the option we are looking for - */ +*** @brief Gets a string from the launch command if available. +*** +*** @param Options - options passed in via the launch command +*** @param ParseString - the variable we are looking for +*** @param CurrentValue - the current value of the variable +*** @return string value of the option we are looking for +***/ static function string GetStringOption(string Options, string ParseString, string CurrentValue) { local string InOpt; From 5f4b2abfe56a88d006712806b21348fa38dcf936 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 21:10:14 +0300 Subject: [PATCH 04/14] remake mod structure into my template --- StartWave/Classes/OptionsParser.uc | 77 +++++++ StartWave/Classes/StartWave.uc | 343 ++++++++++++++++------------- StartWave/Classes/StartWaveMut.uc | 53 +++++ 3 files changed, 320 insertions(+), 153 deletions(-) create mode 100644 StartWave/Classes/OptionsParser.uc create mode 100644 StartWave/Classes/StartWaveMut.uc diff --git a/StartWave/Classes/OptionsParser.uc b/StartWave/Classes/OptionsParser.uc new file mode 100644 index 0000000..404cbf7 --- /dev/null +++ b/StartWave/Classes/OptionsParser.uc @@ -0,0 +1,77 @@ +class OptionsParser extends Object; + +const GameInfo = class'GameInfo'; + +/** +*** @brief Gets a int from the launch command if available. +*** +*** @param Options - options passed in via the launch command +*** @param ParseString - the variable we are looking for +*** @param CurrentValue - the current value of the variable +*** @return int value of the option we are looking for +***/ +static function int GetIntOption( string Options, string ParseString, int CurrentValue) +{ + return GameInfo.static.GetIntOption(Options, ParseString, CurrentValue); +} + +/** +*** @brief Gets a bool from the launch command if available. +*** +*** @param Options - options passed in via the launch command +*** @param ParseString - the variable we are looking for +*** @param CurrentValue - the current value of the variable +*** @return bool value of the option we are looking for +***/ +static public function bool GetBoolOption(string Options, string ParseString, bool CurrentValue) +{ + local string InOpt; + + //Find the value associated with this variable in the launch command. + InOpt = GameInfo.static.ParseOption(Options, ParseString); + + if(InOpt != "") + { + return bool(InOpt); + } + + //If a value for this variable was not specified in the launch command, return the original value. + return CurrentValue; +} + +/** +*** @brief Gets a string from the launch command if available. +*** +*** @param Options - options passed in via the launch command +*** @param ParseString - the variable we are looking for +*** @param CurrentValue - the current value of the variable +*** @return string value of the option we are looking for +***/ +static public function string GetStringOption(string Options, string ParseString, string CurrentValue) +{ + local string InOpt; + + //Find the value associated with this variable in the launch command. + InOpt = GameInfo.static.ParseOption(Options, ParseString); + + if(InOpt != "") + { + return InOpt; + } + + //If a value for this variable was not specified in the launch command, return the original value. + return CurrentValue; +} + +/** +*** @brief Gets a LogLevel from the launch command if available. +*** +*** @param Options - options passed in via the launch command +*** @param ParseString - the variable we are looking for +*** @param CurrentValue - the current value of the variable +*** @return E_LogLevel value of the option we are looking for +***/ +static public function E_LogLevel GetLogLevelOption(string Options, string ParseString, E_LogLevel CurrentValue) +{ + return CurrentValue; // TODO: impl +} \ No newline at end of file diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index 0a960c9..4162061 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -1,6 +1,14 @@ -class StartWave extends KFMutator +class StartWave extends Info config(StartWave); +const OptionsParser = class'OptionsParser'; + +var private KFGameInfo KFGI; +var private KFGameInfo_Survival KFGIS; +var private KFGameInfo_Endless KFGIE; + +var private KFGameReplicationInfo KFGRI; + /********************************************************************************************************* * Config properties *********************************************************************************************************/ @@ -37,29 +45,71 @@ var bool bOverridenDifficultySettings; /** Whether the trader duration has been overriden. */ var bool bOverridenTraderDuration; -function InitMutator(string Options, out string ErrorMessage) +public simulated function bool SafeDestroy() { - //This needs to be called first since KFMutator.InitMutator sets the MyKFGI reference. - Super.InitMutator(Options, ErrorMessage); + `Log_Trace(); + + return (bPendingDelete || bDeleteMe || Destroy()); +} + +public event PreBeginPlay() +{ + `Log_Trace(); + + if (WorldInfo.NetMode == NM_Client) + { + `Log_Fatal("Wrong NetMode:" @ WorldInfo.NetMode); + SafeDestroy(); + return; + } + + Super.PreBeginPlay(); + + PreInit(); +} + +public event PostBeginPlay() +{ + `Log_Trace(); + + if (bPendingDelete || bDeleteMe) return; + + Super.PostBeginPlay(); + + PostInit(); +} + +private function PreInit() +{ + local String Options; + + `Log_Trace(); + + Options = WorldInfo.GetLocalURL(); //Parse options entered via the launch command. //We further restrict StartWave later when we know the maximum wave number for the selected game length. - StartWave = Max(class'GameInfo'.static.GetIntOption(Options, "StartWave", StartWave), 1); - InitialTraderTime = Max(class'GameInfo'.static.GetIntOption(Options, "InitialTraderTime", - InitialTraderTime), 1); - TraderTime = Max(class'GameInfo'.static.GetIntOption(Options, "TraderTime", TraderTime), 1); - Dosh = Max(class'GameInfo'.static.GetIntOption(Options, "Dosh", Dosh), 0); - Boss = class'GameInfo'.static.GetIntOption(Options, "Boss", Boss); - bStartWithTrader = GetBoolOption(Options, "bStartWithTrader", bStartWithTrader); - LogLevel = E_LogLevel(class'GameInfo'.static.GetIntOption(Options, "LogLevel", LogLevel)); + StartWave = OptionsParser.static.GetIntOption (Options, "StartWave", StartWave); + InitialTraderTime = OptionsParser.static.GetIntOption (Options, "InitialTraderTime", InitialTraderTime); + TraderTime = OptionsParser.static.GetIntOption (Options, "TraderTime", TraderTime); + Dosh = OptionsParser.static.GetIntOption (Options, "Dosh", Dosh); + Boss = OptionsParser.static.GetIntOption (Options, "Boss", Boss); + bStartWithTrader = OptionsParser.static.GetBoolOption (Options, "bStartWithTrader", bStartWithTrader); + LogLevel = OptionsParser.static.GetLogLevelOption(Options, "LogLevel", LogLevel); + + // Adjust values if needed + StartWave = Max(StartWave, 1); + InitialTraderTime = Max(InitialTraderTime, 1); + TraderTime = Max(TraderTime, 1); + Dosh = Max(Dosh, 0); //DEBUG - `Log_Debug("StartWave: " $ StartWave); - `Log_Debug("InitialTraderTime: " $ InitialTraderTime); - `Log_Debug("TraderTime: " $ TraderTime); - `Log_Debug("Dosh: " $ Dosh); - `Log_Debug("Boss: " $ Boss); - `Log_Debug("bStartWithTrader: " $ bStartWithTrader); + `Log_Debug("StartWave:" @ StartWave); + `Log_Debug("InitialTraderTime:" @ InitialTraderTime); + `Log_Debug("TraderTime:" @ TraderTime); + `Log_Debug("Dosh:" @ Dosh); + `Log_Debug("Boss:" @ Boss); + `Log_Debug("bStartWithTrader:" @ bStartWithTrader); bOverridenDifficultySettings = false; bOverridenTraderDuration = false; @@ -67,7 +117,7 @@ function InitMutator(string Options, out string ErrorMessage) SetTimer(0.1, false, nameof(OverrideTimer)); //Override the boss with the boss corresponding to the specified boss index. -1 signifies random. - if(Boss != -1) + if (Boss != -1) { SetTimer(0.1, false, nameof(OverrideBoss)); } @@ -78,26 +128,50 @@ function InitMutator(string Options, out string ErrorMessage) bInitialTrader = bStartWithTrader; //If we want to start with the trader active or alter the starting wave number. - if(bStartWithTrader || StartWave > 1) + if (bStartWithTrader || StartWave > 1) { `Log_Debug("Calling StartWaveTimer() to alter the start wave or activate the trader initially."); SetTimer(0.2, false, nameof(StartWaveTimer)); } //If we will need to alter TimeBetweenWaves for later activations of the trader. - if(bStartWithTrader && InitialTraderTime != TraderTime) + if (bStartWithTrader && InitialTraderTime != TraderTime) { `Log_Debug("Calling UpdateTraderDurationTimer() to alter the trader duration later."); SetTimer(1, true, nameof(UpdateTraderDurationTimer)); } } +private function PostInit() +{ + `Log_Trace(); + + KFGI = KFGameInfo(WorldInfo.Game); + if (KFGIS == None) + { + `Log_Fatal("Incompatible gamemode:" @ WorldInfo.Game $ ". Destroy..."); + SafeDestroy(); + return; + } + + KFGIS = KFGameInfo_Survival(KFGI); + if (KFGIS == None) + { + `Log_Warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not compatible with non-wave-based game modes."); + } + + KFGIE = KFGameInfo_Endless(KFGIS); + KFGRI = KFGI.MyKFGRI; +} + /** Allows for handling player input in the console by the mutator. */ -function Mutate(string MutateString, PlayerController Sender) +public function Mutate(string MutateString, PlayerController Sender) { local array CommandBreakdown; - if(MutateString == "") + `Log_Trace(); + + if (MutateString == "") { return; } @@ -106,13 +180,15 @@ function Mutate(string MutateString, PlayerController Sender) ParseStringIntoArray(MutateString, CommandBreakdown, " ", true); //The CheatManager check is equivalent to checking if cheats are enabled for that player. - if(CommandBreakdown.Length > 1 && CommandBreakdown[0] == "setwave" && Sender.CheatManager != None && - MyKFGI.GetLivingPlayerCount() > 0) + if (CommandBreakdown.Length > 1&& + CommandBreakdown[0] == "setwave" && + Sender.CheatManager != None && + KFGI.GetLivingPlayerCount() > 0) { //The setwave command should be: mutate setwave WaveNum bSkipTraderTime //where WaveNum is an integer (or byte) and bSkipTraderTime is a bool. - if(CommandBreakdown.Length == 2) + if (CommandBreakdown.Length == 2) { SetWave(int(CommandBreakdown[1]), Sender); } @@ -121,23 +197,23 @@ function Mutate(string MutateString, PlayerController Sender) SetWave(int(CommandBreakdown[1]), Sender, bool(CommandBreakdown[2])); } } - - Super.Mutate(MutateString, Sender); } /** Jumps to the specified wave, NewWaveNum, with trader time iff bSkipTraderTime is false. */ -function SetWave(int NewWaveNum, PlayerController PC, optional bool bSkipTraderTime) +private function SetWave(int NewWaveNum, PlayerController PC, optional bool bSkipTraderTime) { - if(NewWaveNum < 1) + `Log_Trace(); + + if (NewWaveNum < 1) { `Log_Error("SetWave: new wave num must be > 0."); return; } - if(KFGameInfo_Endless(MyKFGI) != None) + if (KFGIE != None) { //Jump straight to the final wave if the specified wave number is higher than wave max. - if(NewWaveNum > 254) + if (NewWaveNum > 254) { NewWaveNum = 254; } @@ -145,83 +221,87 @@ function SetWave(int NewWaveNum, PlayerController PC, optional bool bSkipTraderT else { //Jump straight to the boss wave if the specified wave number is higher than wave max. - if(NewWaveNum > KFGameInfo_Survival(MyKFGI).WaveMax) + if (NewWaveNum > KFGameInfo_Survival(KFGI).WaveMax) { - NewWaveNum = KFGameInfo_Survival(MyKFGI).WaveMax+1; + NewWaveNum = KFGameInfo_Survival(KFGI).WaveMax+1; } } - KFGameInfo_Survival(MyKFGI).WaveNum = NewWaveNum - 1; + if (KFGIS != None) + { + KFGIS.WaveNum = NewWaveNum - 1; + } //Kill all zeds currently alive. PC.ConsoleCommand("KillZeds"); //Clear any current objectives. - MyKFGI.MyKFGRI.DeactivateObjective(); + KFGRI.DeactivateObjective(); - if(bSkipTraderTime) + if (bSkipTraderTime) { //Go to some unused state so that PlayingWave.BeginState is called when we go to PlayingWave. - MyKFGI.GotoState('TravelTheWorld'); + KFGI.GotoState('TravelTheWorld'); UpdateEndlessDifficulty(); //Go to PlayingWave to start the new wave. - MyKFGI.GotoState('PlayingWave'); + KFGI.GotoState('PlayingWave'); } else { //Go to trader time before starting the new wave. - MyKFGI.GotoState('TraderOpen'); + KFGI.GotoState('TraderOpen'); UpdateEndlessDifficulty(); } - MyKFGI.ResetAllPickups(); + KFGI.ResetAllPickups(); } /** *** Since the difficulty in Endless scales with the wave number, we need to update the difficulty when *** jumping between wave numbers to match the expected difficulty. ***/ -function UpdateEndlessDifficulty() +private function UpdateEndlessDifficulty() { - local KFGameInfo_Endless Endless; local int i; - Endless = KFGameInfo_Endless(MyKFGI); + `Log_Trace(); - if(Endless == None) + if (KFGIE == None) { return; } //Reflects the difficulty update in KFGameInfo_Endless.SetWave. - Endless.bIsInHoePlus = false; - Endless.ResetDifficulty(); - Endless.SpawnManager.GetWaveSettings(Endless.SpawnManager.WaveSettings); - Endless.UpdateGameSettings(); + KFGIE.bIsInHoePlus = false; + KFGIE.ResetDifficulty(); + KFGIE.SpawnManager.GetWaveSettings(KFGIE.SpawnManager.WaveSettings); + KFGIE.UpdateGameSettings(); //Don't bother iterating for i=0-4, no difficulty increment can occur. - for(i = 5; i < Endless.WaveNum; ++i) + for (i = 5; i < KFGIE.WaveNum; ++i) { //Simulate the death of a boss. The difficulty is incremented after each boss round. - if(i % 5 == 0) + if (i % 5 == 0) { - Endless.IncrementDifficulty(); + KFGIE.IncrementDifficulty(); } //This should happen at the end of each wave (if we're in HoE+). The check is handled internally. //We do this after the simulation of a boss death so that bIsInHoePlus can be set first. - Endless.HellOnEarthPlusRoundIncrement(); + KFGIE.HellOnEarthPlusRoundIncrement(); } } /** Checks whether we should force the initial trader, regardless of the config/command value. */ -function CheckForceInitialTrader() +private function CheckForceInitialTrader() { + `Log_Trace(); + //Force the initial trader for compatibility with holdout maps. Otherwise, zeds spawn in the wrong room. - if(!bStartWithTrader && StartWave > 1) + if (!bStartWithTrader && StartWave > 1) { bStartWithTrader = true; InitialTraderTime = 1.0; @@ -229,13 +309,15 @@ function CheckForceInitialTrader() } /** Overrides the boss to spawn if a valid boss index has been specified. */ -function OverrideBoss() +private function OverrideBoss() { local bool bHalt; local byte MaxIters, i, MaxSameIters, PrevIndex, SameIters; + `Log_Trace(); + //We need a valid KFGRI reference as we use its public BossIndex field. - if(MyKFGI.MyKFGRI == None) + if (KFGRI == None) { SetTimer(0.2, false, nameof(OverrideBoss)); return; @@ -254,33 +336,33 @@ function OverrideBoss() //while we assume the index is forced, in which case we can't do anything about it. SameIters = 0; MaxSameIters = 10; - PrevIndex = MyKFGI.MyKFGRI.BossIndex; + PrevIndex = KFGRI.BossIndex; - bHalt = Boss < 0 || MyKFGI.MyKFGRI.BossIndex == Boss; + bHalt = Boss < 0 || KFGRI.BossIndex == Boss; - while(!bHalt) + while (!bHalt) { ++i; //Randomly select a new boss. - MyKFGI.SetBossIndex(); + KFGI.SetBossIndex(); //Track whether the boss index is changing. - if(MyKFGI.MyKFGRI.BossIndex == PrevIndex) + if (KFGRI.BossIndex == PrevIndex) { ++SameIters; } else { SameIters = 0; - PrevIndex = MyKFGI.MyKFGRI.BossIndex; + PrevIndex = KFGRI.BossIndex; } //Halt if we have the desired index or we have tried enough times. - bHalt = MyKFGI.MyKFGRI.BossIndex == Boss || SameIters >= MaxSameIters || i >= MaxIters; + bHalt = KFGRI.BossIndex == Boss || SameIters >= MaxSameIters || i >= MaxIters; } - if(MyKFGI.MyKFGRI.BossIndex == Boss) + if (KFGRI.BossIndex == Boss) { `Log_Debug("Successfully overrode boss index to" @ Boss @ "after" @ i @ "attempts."); } @@ -291,41 +373,40 @@ function OverrideBoss() } /** Overrides difficulty settings and trader duration when possible. */ -function OverrideTimer() +private function OverrideTimer() { - local KFGameInfo_Survival KFGI_Surv; - local KFGameInfo_Endless KFGI_Endl; - local KFGameDifficulty_Endless KFGD_Endl; + local KFGameDifficulty_Endless KFGDE; local int i; + `Log_Trace(); + //If we've overriden what we need to, don't call this timer again. - if(bOverridenDifficultySettings && bOverridenTraderDuration) + if (bOverridenDifficultySettings && bOverridenTraderDuration) { `Log_Debug("All settings have been overriden."); return; } - if(!bOverridenDifficultySettings && MyKFGI.DifficultyInfo != None) + if (!bOverridenDifficultySettings && KFGI.DifficultyInfo != None) { `Log_Debug("Overriding difficulty settings..."); bOverridenDifficultySettings = true; //Override starting dosh. - MyKFGI.DifficultyInfo.Normal.StartingDosh = Dosh; - MyKFGI.DifficultyInfo.Hard.StartingDosh = Dosh; - MyKFGI.DifficultyInfo.Suicidal.StartingDosh = Dosh; - MyKFGI.DifficultyInfo.HellOnEarth.StartingDosh = Dosh; + KFGI.DifficultyInfo.Normal.StartingDosh = Dosh; + KFGI.DifficultyInfo.Hard.StartingDosh = Dosh; + KFGI.DifficultyInfo.Suicidal.StartingDosh = Dosh; + KFGI.DifficultyInfo.HellOnEarth.StartingDosh = Dosh; - KFGI_Endl = KFGameInfo_Endless(MyKFGI); - if (KFGI_Endl != None) + if (KFGIE != None) { - KFGD_Endl = KFGameDifficulty_Endless(KFGI_Endl.DifficultyInfo); - if (KFGD_Endl != None) + KFGDE = KFGameDifficulty_Endless(KFGIE.DifficultyInfo); + if (KFGDE != None) { - for (i = 0; i < KFGD_Endl.CurrentDifficultyScaling.Difficulties.length; ++i) + for (i = 0; i < KFGDE.CurrentDifficultyScaling.Difficulties.length; ++i) { - KFGD_Endl.CurrentDifficultyScaling.Difficulties[i].StartingDosh = Dosh; + KFGDE.CurrentDifficultyScaling.Difficulties[i].StartingDosh = Dosh; } } } @@ -334,19 +415,17 @@ function OverrideTimer() //We need to set the difficulty settings again - normally done in KFGameInfo.InitGame - to apply //these changes, since this happens after InitGame is executed. - MyKFGI.DifficultyInfo.SetDifficultySettings(MyKFGI.GameDifficulty); + KFGI.DifficultyInfo.SetDifficultySettings(KFGI.GameDifficulty); } //Set the starting wave number. - if(!bOverridenTraderDuration) + if (!bOverridenTraderDuration) { - KFGI_Surv = KFGameInfo_Survival(MyKFGI); - - if(KFGI_Surv != None) + if (KFGIS != None) { //We require the SpawnManager to be set, because this signifies that InitSpawnManager has been //executed, which sets WaveMax. - if(MyKFGI.SpawnManager != None) + if (KFGI.SpawnManager != None) { `Log_Debug("Overriding trader duration..."); @@ -355,13 +434,13 @@ function OverrideTimer() //Since InitSpawnManager has been executed, then PreBeginPlay must have been executed. This //means that PostBeginPlay will have been executed as well since it happens straight after. //Now we can override TimeBetweenWaves. - KFGI_Surv.TimeBetweenWaves = bInitialTrader ? InitialTraderTime : TraderTime; + KFGIS.TimeBetweenWaves = bInitialTrader ? InitialTraderTime : TraderTime; - `Log_Debug("Trader duration has been set to:" @ KFGI_Surv.TimeBetweenWaves @ "seconds."); + `Log_Debug("Trader duration has been set to:" @ KFGIS.TimeBetweenWaves @ "seconds."); } else { - `Log_Debug("MyKFGI.SpawnManager hasn't been set yet. Calling StartWaveTimer again."); + `Log_Debug("KFGI.SpawnManager hasn't been set yet. Calling StartWaveTimer again."); //We don't know WaveMax yet, so we need to wait longer. SetTimer(0.1, false, nameof(StartWaveTimer)); @@ -378,22 +457,21 @@ function OverrideTimer() SetTimer(0.1, false, nameof(OverrideTimer)); } -function StartWaveTimer() +private function StartWaveTimer() { - local KFGameInfo_Survival KFGI_Surv; local PlayerController PC; + `Log_Trace(); + //We need to wait for the wave to be active, as this will signify that StartMatch has been executed. - if(!MyKFGI.IsWaveActive()) + if (!KFGI.IsWaveActive()) { //If the wave isn't active yet (probably still in lobby), wait. SetTimer(0.1, false, nameof(StartWaveTimer)); return; } - KFGI_Surv = KFGameInfo_Survival(MyKFGI); - - if(KFGI_Surv == None) + if (KFGIS == None) { return; } @@ -412,50 +490,52 @@ function StartWaveTimer() //Set the starting wave number. //Keep the assignments separated so that we can used the restricted StartWave later if we want. - StartWave = Min(StartWave, KFGI_Surv.WaveMax); + StartWave = Min(StartWave, KFGIS.WaveMax); //We need to subtract 1 because when the state is eventually reset to PlayingWave, this will be //incremented by 1. - KFGI_Surv.WaveNum = StartWave - 1; + KFGIS.WaveNum = StartWave - 1; - `Log_Debug("WaveNum set to:" @ KFGI_Surv.WaveNum); + `Log_Debug("WaveNum set to:" @ KFGIS.WaveNum); - if(bStartWithTrader) + if (bStartWithTrader) { `Log_Debug("Switching to state: TraderOpen."); //We need to update GRI's WaveNum and update the HUD element that shows the last wave. - MyKFGI.MyKFGRI.WaveNum = KFGI_Surv.WaveNum; - MyKFGI.MyKFGRI.UpdateHUDWaveCount(); + KFGRI.WaveNum = KFGIS.WaveNum; + KFGRI.UpdateHUDWaveCount(); //Start with the trader active. - MyKFGI.GotoState('TraderOpen', 'Begin'); + KFGI.GotoState('TraderOpen', 'Begin'); } else { `Log_Debug("Switching to state: PlayingWave."); //Start with a wave as usual - but our StartWave number will be used. - MyKFGI.GotoState('PlayingWave'); + KFGI.GotoState('PlayingWave'); } //Since we've updated the wave number, we need to update the game settings (which includes the //current wave number). - MyKFGI.UpdateGameSettings(); + KFGI.UpdateGameSettings(); bInitialTrader = false; } /** Updates the trader duration. Waits until the initial trader has closed. */ -function UpdateTraderDurationTimer() +private function UpdateTraderDurationTimer() { + `Log_Trace(); + //If the initial trader has already been opened, and the wave is now active. - if(!bInitialTrader && MyKFGI.IsWaveActive()) + if (!bInitialTrader && KFGI.IsWaveActive()) { - if(KFGameInfo_Survival(MyKFGI) != None) + if (KFGameInfo_Survival(KFGI) != None) { `Log_Debug("Updating trader duration to" @ TraderTime @ "seconds."); //We can update TimeBetweenWaves to be the TraderTime we specified in the launch command. - KFGameInfo_Survival(MyKFGI).TimeBetweenWaves = TraderTime; + KFGameInfo_Survival(KFGI).TimeBetweenWaves = TraderTime; } else { @@ -467,50 +547,7 @@ function UpdateTraderDurationTimer() } } -/** -*** @brief Gets a bool from the launch command if available. -*** -*** @param Options - options passed in via the launch command -*** @param ParseString - the variable we are looking for -*** @param CurrentValue - the current value of the variable -*** @return bool value of the option we are looking for -***/ -static function bool GetBoolOption(string Options, string ParseString, bool CurrentValue) +defaultproperties { - local string InOpt; - //Find the value associated with this variable in the launch command. - InOpt = class'GameInfo'.static.ParseOption(Options, ParseString); - - if(InOpt != "") - { - return bool(InOpt); - } - - //If a value for this variable was not specified in the launch command, return the original value. - return CurrentValue; -} - -/** -*** @brief Gets a string from the launch command if available. -*** -*** @param Options - options passed in via the launch command -*** @param ParseString - the variable we are looking for -*** @param CurrentValue - the current value of the variable -*** @return string value of the option we are looking for -***/ -static function string GetStringOption(string Options, string ParseString, string CurrentValue) -{ - local string InOpt; - - //Find the value associated with this variable in the launch command. - InOpt = class'GameInfo'.static.ParseOption(Options, ParseString); - - if(InOpt != "") - { - return InOpt; - } - - //If a value for this variable was not specified in the launch command, return the original value. - return CurrentValue; } \ No newline at end of file diff --git a/StartWave/Classes/StartWaveMut.uc b/StartWave/Classes/StartWaveMut.uc new file mode 100644 index 0000000..24710e6 --- /dev/null +++ b/StartWave/Classes/StartWaveMut.uc @@ -0,0 +1,53 @@ +class StartWaveMut extends KFMutator; + +var private StartWave StartWave; + +public simulated function bool SafeDestroy() +{ + return (bPendingDelete || bDeleteMe || Destroy()); +} + +public event PreBeginPlay() +{ + Super.PreBeginPlay(); + + if (WorldInfo.NetMode == NM_Client) return; + + foreach WorldInfo.DynamicActors(class'StartWave', StartWave) + { + break; + } + + if (StartWave == None) + { + StartWave = WorldInfo.Spawn(class'StartWave'); + } + + if (StartWave == None) + { + `Log_Base("FATAL: Can't Spawn 'StartWave'"); + SafeDestroy(); + } +} + +public function AddMutator(Mutator Mut) +{ + if (Mut == Self || bPendingDelete || bDeleteMe) return; + + if (Mut.Class == Class) + StartWaveMut(Mut).SafeDestroy(); + else + Super.AddMutator(Mut); +} + +public function Mutate(String MutateString, PlayerController Sender) +{ + StartWave.Mutate(MutateString, Sender); + + Super.Mutate(MutateString, Sender); +} + +defaultproperties +{ + +} \ No newline at end of file From 363e003dffe76f6aaa085231a1b57c6e11936775 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 21:27:00 +0300 Subject: [PATCH 05/14] add LogLevel parsing --- StartWave/Classes/OptionsParser.uc | 32 ++++++++++++------ StartWave/Classes/_Logger.uc | 54 ++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/StartWave/Classes/OptionsParser.uc b/StartWave/Classes/OptionsParser.uc index 404cbf7..9569850 100644 --- a/StartWave/Classes/OptionsParser.uc +++ b/StartWave/Classes/OptionsParser.uc @@ -10,7 +10,7 @@ const GameInfo = class'GameInfo'; *** @param CurrentValue - the current value of the variable *** @return int value of the option we are looking for ***/ -static function int GetIntOption( string Options, string ParseString, int CurrentValue) +static function int GetIntOption( String Options, String ParseString, int CurrentValue) { return GameInfo.static.GetIntOption(Options, ParseString, CurrentValue); } @@ -23,14 +23,14 @@ static function int GetIntOption( string Options, string ParseString, int Curren *** @param CurrentValue - the current value of the variable *** @return bool value of the option we are looking for ***/ -static public function bool GetBoolOption(string Options, string ParseString, bool CurrentValue) +static public function bool GetBoolOption(String Options, String ParseString, bool CurrentValue) { - local string InOpt; + local String InOpt; //Find the value associated with this variable in the launch command. InOpt = GameInfo.static.ParseOption(Options, ParseString); - if(InOpt != "") + if (InOpt != "") { return bool(InOpt); } @@ -40,21 +40,21 @@ static public function bool GetBoolOption(string Options, string ParseString, bo } /** -*** @brief Gets a string from the launch command if available. +*** @brief Gets a String from the launch command if available. *** *** @param Options - options passed in via the launch command *** @param ParseString - the variable we are looking for *** @param CurrentValue - the current value of the variable -*** @return string value of the option we are looking for +*** @return String value of the option we are looking for ***/ -static public function string GetStringOption(string Options, string ParseString, string CurrentValue) +static public function String GetStringOption(String Options, String ParseString, String CurrentValue) { - local string InOpt; + local String InOpt; //Find the value associated with this variable in the launch command. InOpt = GameInfo.static.ParseOption(Options, ParseString); - if(InOpt != "") + if (InOpt != "") { return InOpt; } @@ -71,7 +71,17 @@ static public function string GetStringOption(string Options, string ParseString *** @param CurrentValue - the current value of the variable *** @return E_LogLevel value of the option we are looking for ***/ -static public function E_LogLevel GetLogLevelOption(string Options, string ParseString, E_LogLevel CurrentValue) +static public function E_LogLevel GetLogLevelOption(String Options, String ParseString, E_LogLevel CurrentValue) { - return CurrentValue; // TODO: impl + local String InOpt; + + //Find the value associated with this variable in the launch command. + InOpt = GameInfo.static.ParseOption(Options, ParseString); + + if (InOpt != "") + { + return class'_Logger'.static.LogLevelFromString(InOpt, CurrentValue); + } + + return CurrentValue; } \ No newline at end of file diff --git a/StartWave/Classes/_Logger.uc b/StartWave/Classes/_Logger.uc index bf77f4e..065505f 100644 --- a/StartWave/Classes/_Logger.uc +++ b/StartWave/Classes/_Logger.uc @@ -14,6 +14,60 @@ enum E_LogLevel LL_All }; +public static function E_LogLevel LogLevelFromString(String LogLevel, optional E_LogLevel DefaultLogLevel) +{ + switch (LogLevel) + { + case "0": + case "WrongLevel": + case "LL_WrongLevel": + return LL_WrongLevel; + + case "1": + case "None": + case "LL_None": + return LL_None; + + case "2": + case "Fatal": + case "LL_Fatal": + return LL_Fatal; + + case "3": + case "Error": + case "LL_Error": + return LL_Error; + + case "4": + case "Warning": + case "LL_Warning": + return LL_Warning; + + case "5": + case "Info": + case "LL_Info": + return LL_Info; + + case "6": + case "Debug": + case "LL_Debug": + return LL_Debug; + + case "7": + case "Trace": + case "LL_Trace": + return LL_Trace; + + case "8": + case "All": + case "LL_All": + return LL_All; + + default: + return DefaultLogLevel; + } +} + defaultproperties { From c367e00f63fc61ce507fbf82aa5d18e0764bd8f6 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 22:11:37 +0300 Subject: [PATCH 06/14] optimize boss override --- StartWave/Classes/KFGI_Access.uc | 22 +++++++++ StartWave/Classes/StartWave.uc | 81 ++++++++++++++++++-------------- 2 files changed, 69 insertions(+), 34 deletions(-) create mode 100644 StartWave/Classes/KFGI_Access.uc diff --git a/StartWave/Classes/KFGI_Access.uc b/StartWave/Classes/KFGI_Access.uc new file mode 100644 index 0000000..bba0e2f --- /dev/null +++ b/StartWave/Classes/KFGI_Access.uc @@ -0,0 +1,22 @@ +class KFGI_Access extends Object + within KFGameInfo; + +public function OverrideBossIndex(int Index, optional bool Force = false) +{ + if (Index < 0 || Index >= default.AIBossClassList.Length) + { + return; + } + + if (!UseSpecificBossIndex(BossIndex) || Force) + { + BossIndex = Index; + } + + MyKFGRI.CacheSelectedBoss(BossIndex); +} + +defaultproperties +{ + +} diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index 4162061..a20fbf6 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -7,6 +7,8 @@ var private KFGameInfo KFGI; var private KFGameInfo_Survival KFGIS; var private KFGameInfo_Endless KFGIE; +var private KFGI_Access KFGIA; + var private KFGameReplicationInfo KFGRI; /********************************************************************************************************* @@ -112,15 +114,55 @@ private function PreInit() `Log_Debug("bStartWithTrader:" @ bStartWithTrader); bOverridenDifficultySettings = false; - bOverridenTraderDuration = false; + bOverridenTraderDuration = false; +} + +private function PostInit() +{ + `Log_Trace(); + + if (WorldInfo.Game == None || WorldInfo.GRI == None) + { + SetTimer(0.2, false, nameof(PostInit)); + return; + } + + KFGI = KFGameInfo(WorldInfo.Game); + if (KFGI == None) + { + `Log_Fatal("Incompatible gamemode:" @ WorldInfo.Game $ ". Destroy..."); + SafeDestroy(); + return; + } + + KFGIA = new(KFGI) class'KFGI_Access'; + if (KFGIA == None) + { + `Log_Fatal("Can't create KFGI_Access object"); + SafeDestroy(); + return; + } + + KFGIS = KFGameInfo_Survival(KFGI); + if (KFGIS == None) + { + `Log_Warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not compatible with non-wave-based game modes."); + } + + KFGIE = KFGameInfo_Endless(KFGIS); + + KFGRI = KFGameReplicationInfo(WorldInfo.GRI); + if (KFGRI == None) + { + `Log_Fatal("Incompatible game replication info:" @ WorldInfo.GRI $ ". Destroy..."); + SafeDestroy(); + return; + } SetTimer(0.1, false, nameof(OverrideTimer)); //Override the boss with the boss corresponding to the specified boss index. -1 signifies random. - if (Boss != -1) - { - SetTimer(0.1, false, nameof(OverrideBoss)); - } + KFGIA.OverrideBossIndex(Boss); CheckForceInitialTrader(); @@ -142,28 +184,6 @@ private function PreInit() } } -private function PostInit() -{ - `Log_Trace(); - - KFGI = KFGameInfo(WorldInfo.Game); - if (KFGIS == None) - { - `Log_Fatal("Incompatible gamemode:" @ WorldInfo.Game $ ". Destroy..."); - SafeDestroy(); - return; - } - - KFGIS = KFGameInfo_Survival(KFGI); - if (KFGIS == None) - { - `Log_Warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not compatible with non-wave-based game modes."); - } - - KFGIE = KFGameInfo_Endless(KFGIS); - KFGRI = KFGI.MyKFGRI; -} - /** Allows for handling player input in the console by the mutator. */ public function Mutate(string MutateString, PlayerController Sender) { @@ -316,13 +336,6 @@ private function OverrideBoss() `Log_Trace(); - //We need a valid KFGRI reference as we use its public BossIndex field. - if (KFGRI == None) - { - SetTimer(0.2, false, nameof(OverrideBoss)); - return; - } - //Unfortunately, we cannot directly set the boss index since KFGameInfo.BossIndex is protected. The only //way we can affect BossIndex is through KFGameInfo.SetBossIndex which randomly chooses a value in the //valid range. So we have to continue calling SetBossIndex until the desired index has been chosen. We From 81beb8052d4dbff566b23c92bceba9c1638bd880 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 22:20:03 +0300 Subject: [PATCH 07/14] ref --- StartWave/Classes/StartWave.uc | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index a20fbf6..f049fa2 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -182,6 +182,8 @@ private function PostInit() `Log_Debug("Calling UpdateTraderDurationTimer() to alter the trader duration later."); SetTimer(1, true, nameof(UpdateTraderDurationTimer)); } + + `Log_Info("Initialized."); } /** Allows for handling player input in the console by the mutator. */ @@ -241,9 +243,9 @@ private function SetWave(int NewWaveNum, PlayerController PC, optional bool bSki else { //Jump straight to the boss wave if the specified wave number is higher than wave max. - if (NewWaveNum > KFGameInfo_Survival(KFGI).WaveMax) + if (NewWaveNum > KFGIS.WaveMax) { - NewWaveNum = KFGameInfo_Survival(KFGI).WaveMax+1; + NewWaveNum = KFGIS.WaveMax+1; } } @@ -460,10 +462,6 @@ private function OverrideTimer() return; } } - else - { - `Log_Warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not compatible with non-wave-based game modes."); - } } //If the difficulty info isn't set yet, wait. @@ -544,15 +542,11 @@ private function UpdateTraderDurationTimer() //If the initial trader has already been opened, and the wave is now active. if (!bInitialTrader && KFGI.IsWaveActive()) { - if (KFGameInfo_Survival(KFGI) != None) + if (KFGIS != None) { `Log_Debug("Updating trader duration to" @ TraderTime @ "seconds."); //We can update TimeBetweenWaves to be the TraderTime we specified in the launch command. - KFGameInfo_Survival(KFGI).TimeBetweenWaves = TraderTime; - } - else - { - `Log_Warn("The game mode does not extend KFGameInfo_Survival. Most features of this mutator are not compatible with non-wave-based game modes."); + KFGIS.TimeBetweenWaves = TraderTime; } //We don't need to call this timer again. From c471a854e038712d607a3b8f30c24fb90231e3e3 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 23:10:40 +0300 Subject: [PATCH 08/14] fix parsing options --- StartWave/Classes/StartWave.uc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index f049fa2..11db43d 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -83,11 +83,13 @@ public event PostBeginPlay() private function PreInit() { + local String URL; local String Options; `Log_Trace(); - Options = WorldInfo.GetLocalURL(); + URL = WorldInfo.GetLocalURL(); + Options = Mid(URL, InStr(URL, "?")); //Parse options entered via the launch command. //We further restrict StartWave later when we know the maximum wave number for the selected game length. @@ -106,6 +108,7 @@ private function PreInit() Dosh = Max(Dosh, 0); //DEBUG + `Log_Debug("LogLevel:" @ LogLevel); `Log_Debug("StartWave:" @ StartWave); `Log_Debug("InitialTraderTime:" @ InitialTraderTime); `Log_Debug("TraderTime:" @ TraderTime); From 6857f68fb4da8c8fa13b311785f5024de26e0d19 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sat, 20 May 2023 23:13:21 +0300 Subject: [PATCH 09/14] remove redundant code --- StartWave/Classes/StartWave.uc | 71 ++++------------------------------ 1 file changed, 7 insertions(+), 64 deletions(-) diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index 11db43d..e0ceb85 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -108,13 +108,13 @@ private function PreInit() Dosh = Max(Dosh, 0); //DEBUG - `Log_Debug("LogLevel:" @ LogLevel); - `Log_Debug("StartWave:" @ StartWave); - `Log_Debug("InitialTraderTime:" @ InitialTraderTime); - `Log_Debug("TraderTime:" @ TraderTime); - `Log_Debug("Dosh:" @ Dosh); - `Log_Debug("Boss:" @ Boss); - `Log_Debug("bStartWithTrader:" @ bStartWithTrader); + `Log_Info("LogLevel:" @ LogLevel); + `Log_Info("StartWave:" @ StartWave); + `Log_Info("InitialTraderTime:" @ InitialTraderTime); + `Log_Info("TraderTime:" @ TraderTime); + `Log_Info("Dosh:" @ Dosh); + `Log_Info("Boss:" @ Boss); + `Log_Info("bStartWithTrader:" @ bStartWithTrader); bOverridenDifficultySettings = false; bOverridenTraderDuration = false; @@ -333,63 +333,6 @@ private function CheckForceInitialTrader() } } -/** Overrides the boss to spawn if a valid boss index has been specified. */ -private function OverrideBoss() -{ - local bool bHalt; - local byte MaxIters, i, MaxSameIters, PrevIndex, SameIters; - - `Log_Trace(); - - //Unfortunately, we cannot directly set the boss index since KFGameInfo.BossIndex is protected. The only - //way we can affect BossIndex is through KFGameInfo.SetBossIndex which randomly chooses a value in the - //valid range. So we have to continue calling SetBossIndex until the desired index has been chosen. We - //can verify this by checking KFGameReplicationInfo.BossIndex because that is public. - - i = 0; - MaxIters = 100; - - //Since some events/maps could force a specific boss to be spawned (see KFGameInfo.SetBossIndex), we - //should check whether the index hasn't changed after several iterations. If it stays the same for a - //while we assume the index is forced, in which case we can't do anything about it. - SameIters = 0; - MaxSameIters = 10; - PrevIndex = KFGRI.BossIndex; - - bHalt = Boss < 0 || KFGRI.BossIndex == Boss; - - while (!bHalt) - { - ++i; - - //Randomly select a new boss. - KFGI.SetBossIndex(); - - //Track whether the boss index is changing. - if (KFGRI.BossIndex == PrevIndex) - { - ++SameIters; - } - else - { - SameIters = 0; - PrevIndex = KFGRI.BossIndex; - } - - //Halt if we have the desired index or we have tried enough times. - bHalt = KFGRI.BossIndex == Boss || SameIters >= MaxSameIters || i >= MaxIters; - } - - if (KFGRI.BossIndex == Boss) - { - `Log_Debug("Successfully overrode boss index to" @ Boss @ "after" @ i @ "attempts."); - } - else - { - `Log_Debug("Failed to override boss index after" @ i @ "attempts."); - } -} - /** Overrides difficulty settings and trader duration when possible. */ private function OverrideTimer() { From 5608de55f0b8d393921b0a6c66c327c194d5d6c4 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sun, 21 May 2023 00:46:42 +0300 Subject: [PATCH 10/14] fix updating endless difficulty --- StartWave/Classes/StartWave.uc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index e0ceb85..8efe920 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -299,6 +299,10 @@ private function UpdateEndlessDifficulty() return; } + // KFGIE.ResetDifficulty() does not reset ModifiedDifficulty, so do it manually + KFGRI.SetModifiedGameDifficulty(0); + KFGI.GameDifficultyModifier = 0; + //Reflects the difficulty update in KFGameInfo_Endless.SetWave. KFGIE.bIsInHoePlus = false; KFGIE.ResetDifficulty(); @@ -306,7 +310,7 @@ private function UpdateEndlessDifficulty() KFGIE.UpdateGameSettings(); //Don't bother iterating for i=0-4, no difficulty increment can occur. - for (i = 5; i < KFGIE.WaveNum; ++i) + for (i = 5; i <= KFGIE.WaveNum; ++i) { //Simulate the death of a boss. The difficulty is incremented after each boss round. if (i % 5 == 0) @@ -318,6 +322,8 @@ private function UpdateEndlessDifficulty() //We do this after the simulation of a boss death so that bIsInHoePlus can be set first. KFGIE.HellOnEarthPlusRoundIncrement(); } + + `Log_Debug("Updated difficulty (Game + Modifier):" @ String(KFGRI.GameDifficulty) @ "+" @ String(KFGRI.GameDifficultyModifier)); } /** Checks whether we should force the initial trader, regardless of the config/command value. */ @@ -452,7 +458,9 @@ private function StartWaveTimer() //incremented by 1. KFGIS.WaveNum = StartWave - 1; - `Log_Debug("WaveNum set to:" @ KFGIS.WaveNum); + UpdateEndlessDifficulty(); + + `Log_Info("WaveNum set to:" @ KFGIS.WaveNum); if (bStartWithTrader) { From 1179c7c5e54d486bdd0adfe3f5e4a0001c1603c7 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sun, 21 May 2023 00:47:28 +0300 Subject: [PATCH 11/14] update test settings --- builder.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder.cfg b/builder.cfg index c85447d..8c1e1dc 100644 --- a/builder.cfg +++ b/builder.cfg @@ -55,7 +55,7 @@ Difficulty="0" GameLength="0" # Mutators -Mutators="StartWave.StartWave" +Mutators="StartWave.StartWaveMut" # Additional parameters -Args="Dosh=999?StartWave=7?bUseDebug=True" +Args="Dosh=10000?StartWave=16?LogLevel=LL_Debug?TraderTime=60?InitialTraderTime=60?bStartWithTrader=true?Boss=3" From f1debb3ec943c9032bcf4ab50abd103708714cc8 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sun, 21 May 2023 01:01:26 +0300 Subject: [PATCH 12/14] upd comment --- StartWave/Classes/StartWave.uc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index 8efe920..dbeb7f5 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -29,8 +29,8 @@ var config bool bStartWithTrader; var config E_LogLevel LogLevel; /** -*** The boss override index. For the default boss list, 0-Hans, 1-Patty, 2-King FP, 3-Abomination. Negative -*** values can be used to keep the boss spawn random. +*** The boss override index. For the default boss list, 0-Hans, 1-Patty, 2-King FP, 3-Abomination. +*** Negative values can be used to keep the boss spawn random. ***/ var config int Boss; From 8e213d4f785bf2a83ad6e944319ba4e72a4c428a Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sun, 21 May 2023 01:15:37 +0300 Subject: [PATCH 13/14] update descriptions --- PublicationContent/description.txt | 15 ++++++++++----- README.md | 5 ++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/PublicationContent/description.txt b/PublicationContent/description.txt index 1921b91..aa06284 100644 --- a/PublicationContent/description.txt +++ b/PublicationContent/description.txt @@ -2,7 +2,12 @@ [h1]Description:[/h1] This is the same as [url=https://steamcommunity.com/sharedfiles/filedetails/?id=1417081496]Pharrahnox's SetWave[/url], but with some fixes: -- fixed starting Dosh for Endless mode. +[olist] +[*] fixed starting Dosh for Endless mode; +[*] fixed difficulty setting when changing wave; +[*] optimized boss replacement: now it always works successfully and quickly; +[*] players no longer need to download StartWave when connecting to a server. +[/olist] A utility mod that allows users to specify the starting wave and the boss that will spawn. Additionally, users can jump between waves during the match with a console command (mutate setwave, see below). The purpose of this mod is to allow mappers to more efficiently test their maps for later waves or for the boss. It could also be used to skip early waves if you find them boring, or to test strategies against a specific boss. @@ -16,7 +21,7 @@ No. This mod is not whitelisted and will unrank your server. Any XP gained will [*]Subscribe to this mutator; [*]Start KF2; [*]Open console (`) and input: -[b]open KF-BioticsLab?Game=KFGameContent.KFGameInfo_Endless?Difficulty=0?GameLength=0?Mutator=StartWave.StartWave?[/b] +[b]open KF-BioticsLab?Game=KFGameContent.KFGameInfo_Endless?Difficulty=0?GameLength=0?Mutator=StartWave.StartWaveMut?[/b] (replace the map and add the parameters you need) [*]. [/olist] @@ -31,7 +36,7 @@ No. This mod is not whitelisted and will unrank your server. Any XP gained will [*]Add the following string to the [b][OnlineSubsystemSteamworks.KFWorkshopSteamworks][/b] section (create one if it doesn't exist): [b]ServerSubscribedWorkshopItems=2521731447[/b] [*]Start the server and wait while the mutator is downloading; -[*]Add mutator to server start parameters: [b]?Mutator=StartWave.StartWave[/b] and restart the server. +[*]Add mutator to server start parameters: [b]?Mutator=StartWave.StartWaveMut[/b] and restart the server. [/olist] [h1]Mutator setup[/h1] @@ -43,8 +48,8 @@ Create a file KFGame/Config/KFStartWave.ini and copy this text into it: // If you use the same settings often, it is recommended to store those here // and only change what you need in the launch command. -// Whether mod-specific events should be logged. -bUseDebug=true +// LogLevel (Set this to "LL_Debug" if you need more information in the log) +LogLevel=LL_Info // Whether an 'initial' trader time should occur before the first wave. bStartWithTrader=false diff --git a/README.md b/README.md index a7878f3..4e95c9f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,10 @@ The purpose of this mod is to allow mappers to more efficiently test their maps It is designed to be compatible with every mutator and wave-based gamemode, and to require little to no maintenance after game updates. **This is the same as [Pharrahnox's StartWave](https://steamcommunity.com/sharedfiles/filedetails/?id=1417081496), but with some fixes:** -- fixed starting Dosh for Endless mode. +- fixed starting Dosh for Endless mode; +- fixed difficulty setting when changing wave; +- optimized boss replacement: now it always works successfully and quickly; +- players no longer need to download StartWave when connecting to a server. ## Usage [See steam workshop page](https://steamcommunity.com/sharedfiles/filedetails/?id=2521731447) From 23424ad094ba125ffd625da10019f0863fdb8750 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Sun, 21 May 2023 01:38:52 +0300 Subject: [PATCH 14/14] optimize KillZeds --- StartWave/Classes/StartWave.uc | 19 +++++++++++++++++-- StartWave/Classes/StartWaveMut.uc | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/StartWave/Classes/StartWave.uc b/StartWave/Classes/StartWave.uc index dbeb7f5..83d215d 100644 --- a/StartWave/Classes/StartWave.uc +++ b/StartWave/Classes/StartWave.uc @@ -258,7 +258,7 @@ private function SetWave(int NewWaveNum, PlayerController PC, optional bool bSki } //Kill all zeds currently alive. - PC.ConsoleCommand("KillZeds"); + KillZeds(); //Clear any current objectives. KFGRI.DeactivateObjective(); @@ -446,7 +446,7 @@ private function StartWaveTimer() { if (KFDemoRecSpectator(PC) == none) { - PC.ConsoleCommand("KillZeds"); + KillZeds(); break; } } @@ -508,6 +508,21 @@ private function UpdateTraderDurationTimer() } } +private function KillZeds() +{ + local KFPawn_Monster KFPM; + + foreach WorldInfo.AllPawns(class'KFPawn_Monster', KFPM) + { + if (!KFPM.IsAliveAndWell()) continue; + + if (KFPM.Health > 0 && PlayerController(KFPM.Controller) == None) + { + KFPM.Died(None, None, KFPM.Location); + } + } +} + defaultproperties { diff --git a/StartWave/Classes/StartWaveMut.uc b/StartWave/Classes/StartWaveMut.uc index 24710e6..420b5d2 100644 --- a/StartWave/Classes/StartWaveMut.uc +++ b/StartWave/Classes/StartWaveMut.uc @@ -32,7 +32,7 @@ public event PreBeginPlay() public function AddMutator(Mutator Mut) { - if (Mut == Self || bPendingDelete || bDeleteMe) return; + if (Mut == Self) return; if (Mut.Class == Class) StartWaveMut(Mut).SafeDestroy();