/** * Copyright 2023 Anton Tarasenko *------------------------------------------------------------------------------ * This file is part of Acedia. * * Acedia is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License, or * (at your option) any later version. * * Acedia is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Acedia. If not, see . */ class MapTool extends AcediaObject; // Finding voting handler is not cheap, so only do it once and then store it. var private NativeActorRef votingHandlerReference; // Maps map pseudonims we've used in voting handler to real map names var private HashTable pseudonimToMap; var private array pseudonimMapList; var private array realMapList; var private int gameModesSeen; var private string backupMessageMapWon; var private string backupMessageAdminMapChange; var private const string ACEDIA_MAP_FORCED_COMMAND; var private const string ACEDIA_MAP_WON_COMMAND; protected function Constructor() { pseudonimToMap = _.collections.EmptyHashTable(); } protected function Finalizer() { _server.unreal.broadcasts.OnHandleText(self).Disconnect(); _.memory.Free(votingHandlerReference); _.memory.Free(pseudonimToMap); votingHandlerReference = none; pseudonimToMap = none; pseudonimMapList.length = 0; realMapList.length = 0; gameModesSeen = 0; } public function bool Initialize(NativeActorRef initVotingHandlerReference) { if (initVotingHandlerReference == none) return false; if (XVotingHandler(initVotingHandlerReference.Get()) == none) return false; initVotingHandlerReference.NewRef(); votingHandlerReference = initVotingHandlerReference; return true; } public function string LoadGameModeMaps(GameMode gameMode) { local int i; local XVotingHandler votingHandler; local ArrayList gameModeMaps; local Text mapNameReal, mapNamePseudonim; local VotingHandler.MapHistoryInfo nextMapInfo; local VotingHandler.MapVoteMapList nextRecord; local array newMapsPseudonim, newMapReal; local string gameModePrefix; votingHandler = GetVotingHandler(); if (votingHandler == none) { return "!!!"; } gameModePrefix = ("KF" $ gameModesSeen); nextRecord.bEnabled = true; gameModeMaps = GetAllGameModeMaps(gameMode); for (i = 0; i < gameModeMaps.GetLength(); i += 1) { // Make a pseudonim to map connection mapNameReal = gameModeMaps.GetText(i); mapNamePseudonim = MakeMapPseudonim(mapNameReal, gameModePrefix); pseudonimToMap.SetItem(mapNamePseudonim, mapNameReal); // Setup `VotingHandler.MapVoteMapList` struct for next map if (votingHandler.history != none) { nextMapInfo = votingHandler.history.GetMapHistory(mapNameReal.ToString()); nextRecord.playCount = nextMapInfo.p; nextRecord.sequence = nextMapInfo.s; } else { nextRecord.playCount = 0; nextRecord.sequence = 0; } nextRecord.mapName = mapNamePseudonim.ToString(); newMapsPseudonim[newMapsPseudonim.length] = nextRecord; nextRecord.mapName = mapNameReal.ToString(); newMapReal[newMapReal.length] = nextRecord; } AppendMaps(newMapsPseudonim, newMapReal); gameModesSeen += 1; return gameModePrefix; } private function Text MakeMapPseudonim(Text realName, string gameModePrefix) { local Parser parser; local MutableText prefix, nameBody; local MutableText result; result = _.text.FromStringM(gameModePrefix); result.Append(P("-")); parser = realName.Parse(); parser.MUntil(prefix, _.text.GetCharacter("-")); parser.Match(P("-")); if (parser.Ok()) { nameBody = parser.GetRemainderM(); result.Append(nameBody); } else { result.Append(realName); } _.memory.Free(nameBody); _.memory.Free(prefix); _.memory.Free(parser); return result.IntoText(); } private function ArrayList GetAllGameModeMaps(GameMode gameMode) { local int i, j; local HashTable uniqueMapSet; local ArrayList result; local array usedMapLists; local MapList nextMapConfig; local array nextMapArray; local Text nextMapName, lowerMapName; uniqueMapSet = _.collections.EmptyHashTable(); // to quickly make sure we add each map only once result = _.collections.EmptyArrayList(); usedMapLists = gameMode.GetIncludedMapLists(); for (i = 0; i < usedMapLists.length; i += 1) { // Get maps from `MapList` config nextMapConfig = MapList(class'MapList'.static.GetConfigInstance(usedMapLists[i])); nextMapArray.length = 0; if (nextMapConfig != none) { nextMapArray = nextMapConfig.map; } else { //_.logger.Auto(warnMissingMapList).Arg(usedMapLists[i].Copy()); } _.memory.Free(nextMapConfig); // Add maps we haven't yet added from other lists for (j = 0; j < nextMapArray.length; j += 1) { nextMapName = _.text.FromString(nextMapArray[j]); lowerMapName = nextMapName.LowerCopy(); if (!uniqueMapSet.HasKey(lowerMapName)) { uniqueMapSet.SetItem(lowerMapName, none); result.AddItem(nextMapName); } _.memory.Free(lowerMapName); _.memory.Free(nextMapName); } } _.memory.Free(uniqueMapSet); _.memory.FreeMany(usedMapLists); return result; } private function AppendMaps( array newMapsPseudonim, array newMapsReal) { local int i; local XVotingHandler votingHandler; votingHandler = GetVotingHandler(); if (votingHandler == none) { warn("votingHandler is none!"); return; } for (i = 0; i < newMapsPseudonim.length; i += 1) { pseudonimMapList[pseudonimMapList.length] = newMapsPseudonim[i]; } for (i = 0; i < newMapsReal.length; i += 1) { realMapList[realMapList.length] = newMapsReal[i]; } } public final function InjectMaps() { local XVotingHandler votingHandler; votingHandler = GetVotingHandler(); if (votingHandler != none) { votingHandler.mapList = pseudonimMapList; votingHandler.mapCount = pseudonimMapList.length; backupMessageMapWon = votingHandler.lmsgMapWon; backupMessageAdminMapChange = votingHandler.lmsgAdminMapChange; votingHandler.lmsgMapWon = ACEDIA_MAP_WON_COMMAND $ "::%mapname%"; votingHandler.lmsgAdminMapChange = ACEDIA_MAP_FORCED_COMMAND $ "::%mapname%"; _server.unreal.broadcasts.OnHandleText(self).connect = HandleMapChange; } } private function bool HandleMapChange( Actor sender, out string message, name type, bool teamMessage ) { local Parser parser; local XVotingHandler votingHandler; votingHandler = GetVotingHandler(); if (sender == none) return true; if (votingHandler != sender) return true; parser = _.text.ParseString(message); parser.Match(P(ACEDIA_MAP_WON_COMMAND)); parser.Match(P("::")); if (parser.Ok()) { message = Repl(backupMessageMapWon, "%mapname%", parser.GetRemainderS()); } else { parser.Match(P(ACEDIA_MAP_FORCED_COMMAND)); parser.Match(P("::")); if (parser.Ok()) { message = Repl(backupMessageAdminMapChange, "%mapname%", parser.GetRemainderS()); } } if (parser.Ok()) { votingHandler.mapList = realMapList; votingHandler.mapCount = realMapList.length; } _.memory.Free(parser); return true; } private function XVotingHandler GetVotingHandler() { if (votingHandlerReference != none) { return XVotingHandler(votingHandlerReference.Get()); } return none; } defaultproperties { ACEDIA_MAP_FORCED_COMMAND = "ACEDIA_LAUNCHER:MAP_FORCED:DEADBEEF" ACEDIA_MAP_WON_COMMAND = "ACEDIA_LAUNCHER:MAP_WON:DEADBEEF" }