diff --git a/sources/GameModes/BaseGameMode.uc b/sources/GameModes/BaseGameMode.uc index cb7d8fe..f3accb2 100644 --- a/sources/GameModes/BaseGameMode.uc +++ b/sources/GameModes/BaseGameMode.uc @@ -33,6 +33,8 @@ class BaseGameMode extends AcediaConfig // Name of the game mode players will see in voting (formatted string) var protected config string title; +// Preferable game length (plain string) +var protected config string length; // Preferable difficulty level (plain string) var protected config string difficulty; // `Mutator`s to add with this game mode @@ -63,6 +65,7 @@ protected function HashTable ToData() result = _.collections.EmptyHashTable(); result.SetFormattedString(P("title"), title); + result.SetString(P("length"), length); result.SetString(P("difficulty"), difficulty); nextArray = _.collections.EmptyArrayList(); for (i = 0; i < includeFeature.length; i += 1) { @@ -105,8 +108,9 @@ protected function FromData(HashTable source) if (source == none) { return; } - title = source.GetFormattedString(P("title")); - title = source.GetString(P("title")); + title = source.GetFormattedString(P("title")); + length = source.GetString(P("length")); + difficulty = source.GetString(P("difficulty")); nextArray = source.GetArrayList(P("includeFeature")); includeFeature = DynamicIntoStringArray(nextArray); _.memory.Free(nextArray); @@ -195,6 +199,15 @@ public function Text GetTitle() return _.text.FromFormattedString(title); } +/** + * @return Specified game length for the game mode. + * Interpretation of this value can depend on each particular game mode. + */ +public function Text GetLength() +{ + return _.text.FromString(length); +} + /** * @return Specified difficulty for the game mode. * Interpretation of this value can depend on each particular game mode. diff --git a/sources/GameModes/GameMode.uc b/sources/GameModes/GameMode.uc index e314ec3..44f79eb 100644 --- a/sources/GameModes/GameMode.uc +++ b/sources/GameModes/GameMode.uc @@ -39,11 +39,21 @@ var protected config string acronym; // this game mode (plain string) var protected config string mapPrefix; -var private LoggerAPI.Definition warnBadOption; +// Aliases are an unnecessary overkill for difficulty names, so just define +// them in special `string` arrays. +// We accept not just these exact words, but any of their prefixes. +var private const array beginnerSynonyms; +var private const array normalSynonyms; +var private const array hardSynonyms; +var private const array suicidalSynonyms; +var private const array hoeSynonyms; + +var private LoggerAPI.Definition warnBadOption, warnDifficultyOption; protected function DefaultIt() { title = "Acedia game mode"; + length = "long"; difficulty = "Hell On Earth"; gameTypeClass = "KFMod.KFGameType"; acronym = ""; @@ -176,20 +186,95 @@ public function HashTable GetOptions() { local int i; local HashTable result; + local Text nextKey, nextValue; result = _.collections.EmptyHashTable(); for (i = 0; i < option.length; i += 1) { if (!ValidateServerURLName(option[i].key)) continue; if (!ValidateServerURLName(option[i].value)) continue; - result.SetItem( _.text.FromString(option[i].key), - _.text.FromString(option[i].value)); + if (option[i].key ~= "difficulty") + { + _.logger.Auto(warnDifficultyOption); + continue; + } + nextKey = _.text.FromString(option[i].key); + nextValue = _.text.FromString(option[i].value); + result.SetItem(nextKey, nextValue); + nextKey.FreeSelf(); + nextValue.FreeSelf(); } + // Add difficulty option + nextValue = _.text.FromInt(GetNumericDifficulty()); + result.SetItem(P("difficulty"), nextValue); + nextValue.FreeSelf(); return result; } +// Convert `GameMode`'s difficulty's textual representation into +// KF's numeric one. +private final function int GetNumericDifficulty() +{ + local int i; + local string lowerCaseDifficulty; + + lowerCaseDifficulty = Locs(_.text.IntoString(GetDifficulty())); + for (i = 0; i < default.beginnerSynonyms.length; i += 1) + { + if (IsPrefixOf(lowerCaseDifficulty, default.beginnerSynonyms[i])) { + return 1; + } + } + for (i = 0; i < default.normalSynonyms.length; i += 1) + { + if (IsPrefixOf(lowerCaseDifficulty, default.normalSynonyms[i])) { + return 2; + } + } + for (i = 0; i < default.hardSynonyms.length; i += 1) + { + if (IsPrefixOf(lowerCaseDifficulty, default.hardSynonyms[i])) { + return 4; + } + } + for (i = 0; i < default.suicidalSynonyms.length; i += 1) + { + if (IsPrefixOf(lowerCaseDifficulty, default.suicidalSynonyms[i])) { + return 5; + } + } + for (i = 0; i < default.hoeSynonyms.length; i += 1) + { + if (IsPrefixOf(lowerCaseDifficulty, default.hoeSynonyms[i])) { + return 7; + } + } + return int(lowerCaseDifficulty); +} + +protected final static function bool IsPrefixOf(string prefix, string value) +{ + return (InStr(value, prefix) == 0); +} + defaultproperties { configName = "AcediaGameModes" - warnBadOption = (l=LOG_Warning,m="Option with key \"%1\" and value \"%2\" specified for game mode \"%3\" contains invalid characters and will be ignored. This is a configuration error, you should fix it.") + beginnerSynonyms(0) = "easy" + beginnerSynonyms(1) = "beginer" + beginnerSynonyms(2) = "beginner" + beginnerSynonyms(3) = "begginer" + beginnerSynonyms(4) = "begginner" + normalSynonyms(0) = "regular" + normalSynonyms(1) = "default" + normalSynonyms(2) = "normal" + hardSynonyms(0) = "harder" // "hard" is prefix of this, so it will count + hardSynonyms(1) = "difficult" + suicidalSynonyms(0) = "suicidal" + hoeSynonyms(0) = "hellonearth" + hoeSynonyms(1) = "hellon earth" + hoeSynonyms(2) = "hell onearth" + hoeSynonyms(3) = "hoe" + warnBadOption = (l=LOG_Warning,m="Option with key \"%1\" and value \"%2\" specified for game mode \"%3\" contains invalid characters and will be ignored. This is a configuration error, you should fix it.") + warnDifficultyOption = (l=LOG_Warning,m="Option with key \"Difficulty\" is specified. This key reserved and will be ignored. Difficulty value should be set through the game mode's \"Difficulty\" setting in \"AcediaGameModes.ini\" config. This is a configuration error, you should fix it.") } \ No newline at end of file diff --git a/sources/VotingHandlerAdapter.uc b/sources/VotingHandlerAdapter.uc index 8d9631f..72db29e 100644 --- a/sources/VotingHandlerAdapter.uc +++ b/sources/VotingHandlerAdapter.uc @@ -50,15 +50,6 @@ class VotingHandlerAdapter extends AcediaObject * to read (and forget) from internal state. */ -// Aliases are an unnecessary overkill for difficulty names, so just define -// them in special `string` arrays. -// We accept detect not just these exact words, but any of their prefixes. -var private const array beginnerSynonyms; -var private const array normalSynonyms; -var private const array hardSynonyms; -var private const array suicidalSynonyms; -var private const array hoeSynonyms; - // All available game modes for Acedia, loaded during initialization. // This array is directly produces replacement for `XVotingHandler`'s // `gameConfig` array and records of `availableGameModes` relate to those of @@ -86,7 +77,15 @@ var private config string targetGameMode; // difficulty by overwriting default value of its `gameDifficulty` variable. // But to not affect game's configs we must restore old value after new map is // loaded. Store it in config variable for that. -var private config float storedGameDifficulty; +var private config int storedGameLength; + +// Aliases are an unnecessary overkill for difficulty names, so just define +// them in special `string` arrays. +// We accept not just these exact words, but any of their prefixes. +var private const array shortSynonyms; +var private const array normalSynonyms; +var private const array longSynonyms; +var private const array customSynonyms; var private LoggerAPI.Definition fatNoXVotingHandler, fatBadGameConfigIndexVH; var private LoggerAPI.Definition fatBadGameConfigIndexAdapter; @@ -201,6 +200,7 @@ public final function PrepareForServerTravel() local GameMode nextGameMode; local string nextGameClassName; local class nextGameClass; + local class nextKFGameType; local XVotingHandler votingHandler; if (votingHandlerReference == none) return; @@ -235,9 +235,15 @@ public final function PrepareForServerTravel() } isServerTraveling = true; targetGameMode = availableGameModes[pickedVHConfig].ToString(); - nextGameMode = GetConfigFromString(default.targetGameMode); - storedGameDifficulty = nextGameClass.default.gameDifficulty; - nextGameClass.default.gameDifficulty = GetNumericDifficulty(nextGameMode); + nextGameMode = GetConfigFromString(targetGameMode); + nextKFGameType = class(nextGameClass); + if (nextKFGameType != none) + { + storedGameLength = nextKFGameType.default.kfGameLength; + nextKFGameType.default.kfGameLength = + GetNumericGameLength(nextGameMode); + } + nextGameClass.static.StaticSaveConfig(); SaveConfig(); } @@ -250,11 +256,17 @@ public final function PrepareForServerTravel() */ public final function GameMode SetupGameModeAfterTravel() { + local KFGameType kfGameType; + if (!isServerTraveling) { return none; } - _server.unreal.GetGameType().default.gameDifficulty = storedGameDifficulty; + kfGameType = _server.unreal.GetKFGameType(); + if (kfGameType != none) { + kfGameType.default.kfGameLength = storedGameLength; + } isServerTraveling = false; + _server.unreal.GetGameType().StaticSaveConfig(); SaveConfig(); return GetConfigFromString(targetGameMode); } @@ -289,43 +301,37 @@ private function GameMode GetConfigFromString(string configName) // Convert `GameMode`'s difficulty's textual representation into // KF's numeric one. -private final function int GetNumericDifficulty(GameMode gameMode) +private final function int GetNumericGameLength(BaseGameMode gameMode) { local int i; - local string difficulty; + local string length; - difficulty = Locs(_.text.IntoString(gameMode.GetDifficulty())); - for (i = 0; i < default.beginnerSynonyms.length; i += 1) + length = Locs(_.text.IntoString(gameMode.GetLength())); + for (i = 0; i < default.shortSynonyms.length; i += 1) { - if (IsPrefixOf(difficulty, default.beginnerSynonyms[i])) { - return 1; + if (IsPrefixOf(length, default.shortSynonyms[i])) { + return 0; } } for (i = 0; i < default.normalSynonyms.length; i += 1) { - if (IsPrefixOf(difficulty, default.normalSynonyms[i])) { - return 2; - } - } - for (i = 0; i < default.hardSynonyms.length; i += 1) - { - if (IsPrefixOf(difficulty, default.hardSynonyms[i])) { - return 4; + if (IsPrefixOf(length, default.normalSynonyms[i])) { + return 1; } } - for (i = 0; i < default.suicidalSynonyms.length; i += 1) + for (i = 0; i < default.longSynonyms.length; i += 1) { - if (IsPrefixOf(difficulty, default.suicidalSynonyms[i])) { - return 5; + if (IsPrefixOf(length, default.longSynonyms[i])) { + return 2; } } - for (i = 0; i < default.hoeSynonyms.length; i += 1) + for (i = 0; i < default.customSynonyms.length; i += 1) { - if (IsPrefixOf(difficulty, default.hoeSynonyms[i])) { - return 7; + if (IsPrefixOf(length, default.customSynonyms[i])) { + return 3; } } - return int(difficulty); + return 3; } protected final static function bool IsPrefixOf(string prefix, string value) @@ -335,21 +341,12 @@ protected final static function bool IsPrefixOf(string prefix, string value) defaultproperties { - beginnerSynonyms(0) = "easy" - beginnerSynonyms(1) = "beginer" - beginnerSynonyms(2) = "beginner" - beginnerSynonyms(3) = "begginer" - beginnerSynonyms(4) = "begginner" - normalSynonyms(0) = "regular" - normalSynonyms(1) = "default" - normalSynonyms(2) = "normal" - hardSynonyms(0) = "harder" // "hard" is prefix of this, so it will count - hardSynonyms(1) = "difficult" - suicidalSynonyms(0) = "suicidal" - hoeSynonyms(0) = "hellonearth" - hoeSynonyms(1) = "hellon earth" - hoeSynonyms(2) = "hell onearth" - hoeSynonyms(3) = "hoe" + shortSynonyms(0) = "short" + normalSynonyms(0) = "normal" + normalSynonyms(1) = "medium" + normalSynonyms(2) = "regular" + longSynonyms(0) = "long" + customSynonyms(0) = "custom" fatNoXVotingHandler = (l=LOG_Fatal,m="`XVotingHandler` class is missing. Make sure your server setup supports Acedia's game modes (by used voting handler derived from `XVotingHandler`).") fatBadGameConfigIndexVH = (l=LOG_Fatal,m="`XVotingHandler`'s `currentGameConfig` variable value of %1 is out-of-bounds for `XVotingHandler.gameConfig` of length %2. Report this issue.") fatBadGameConfigIndexAdapter = (l=LOG_Fatal,m="`XVotingHandler`'s `currentGameConfig` variable value of %1 is out-of-bounds for `VHAdapter` of length %2. Report this issue.")