/**
* 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"
}