Index: ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js +++ ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js @@ -216,12 +216,23 @@ function getGameDescription(extended = false) { let titles = []; + if (!g_GameAttributes.settings.VictoryConditions.length) + titles.push({ + "label": translateWithContext("victory condition", "Endless Game"), + "value": translate("No winner will be determined, even if everyone is defeated.") + }); + + let victoryConditions = g_GameAttributes.settings.VictoryConditions.map(victoryConditionName => + g_VictoryConditions.find(victoryCondition => victoryCondition.Name == victoryConditionName) || { + "Name": victoryConditionName, + "Description": "" + }); - let victoryIdx = g_VictoryConditions.Name.indexOf(g_GameAttributes.settings.GameType || g_VictoryConditions.Default); - if (victoryIdx != -1) + for (let victoryCondition of victoryConditions.sort((a, b) => + a.GUIOrder - b.GUIOrder || (a.Title > b.Title ? 1 : a.Title > b.Title ? -1 : 0))) { - let title = g_VictoryConditions.Title[victoryIdx]; - if (g_VictoryConditions.Name[victoryIdx] == "wonder") + let title = translateVictoryCondition(victoryCondition.Name); + if (victoryCondition.Name == "wonder") title = sprintf( translatePluralWithContext( "victory condition", @@ -232,7 +243,7 @@ { "min": g_GameAttributes.settings.WonderDuration } ); - let isCaptureTheRelic = g_VictoryConditions.Name[victoryIdx] == "capture_the_relic"; + let isCaptureTheRelic = victoryCondition.Name == "capture_the_relic"; if (isCaptureTheRelic) title = sprintf( translatePluralWithContext( @@ -246,7 +257,7 @@ titles.push({ "label": title, - "value": g_VictoryConditions.Description[victoryIdx] + "value": victoryCondition.Description }); if (isCaptureTheRelic) @@ -255,7 +266,7 @@ "value": g_GameAttributes.settings.RelicCount }); - if (g_VictoryConditions.Name[victoryIdx] == "regicide") + if (victoryCondition.Name == "regicide") if (g_GameAttributes.settings.RegicideGarrison) titles.push({ "label": translate("Hero Garrison"), @@ -264,7 +275,7 @@ else titles.push({ "label": translate("Exposed Heroes"), - "value": translate("Heroes cannot be garrisoned, and they are vulnerable to raids.") + "value": translate("Heroes cannot be garrisoned and they are vulnerable to raids.") }); } Index: ps/trunk/binaries/data/mods/public/gui/common/settings.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/settings.js +++ ps/trunk/binaries/data/mods/public/gui/common/settings.js @@ -247,7 +247,7 @@ } /** - * Loads available gametypes. + * Loads available victoryCondtions from json files. * * @returns {Array|undefined} */ @@ -256,24 +256,15 @@ let subdir = "victory_conditions/"; let victoryConditions = listFiles(g_SettingsDirectory + subdir, ".json", false).map(victoryScriptName => { - let vc = loadSettingValuesFile(subdir + victoryScriptName + ".json"); - if (vc) - vc.Name = victoryScriptName; - return vc; + let victoryCondition = loadSettingValuesFile(subdir + victoryScriptName + ".json"); + if (victoryCondition) + victoryCondition.Name = victoryScriptName; + return victoryCondition; }); - if (victoryConditions.some(vc => vc == undefined)) + if (victoryConditions.some(victoryCondition => victoryCondition == undefined)) return undefined; - // TODO: We might support enabling victory conditions separately sometime. - // Until then, we supplement the endless gametype here. - victoryConditions.push({ - "Name": "endless", - "Title": translateWithContext("victory condition", "None"), - "Description": translate("Endless game."), - "Scripts": [] - }); - return victoryConditions; } @@ -428,11 +419,11 @@ /** * Returns title or placeholder. * - * @param {string} gameType - for example "conquest" + * @param {string} victoryConditionName - For example "conquest". * @returns {string} */ -function translateVictoryCondition(gameType) +function translateVictoryCondition(victoryConditionName) { - let victoryCondition = g_Settings.VictoryConditions.find(vc => vc.Name == gameType); - return victoryCondition ? victoryCondition.Title : translateWithContext("victory condition", "Unknown"); + let victoryCondition = g_Settings.VictoryConditions.find(victoryCondition => victoryCondition.Name == victoryConditionName); + return victoryCondition ? victoryCondition.Title : translate("Unknown Victory Condition"); } Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -7,8 +7,9 @@ const g_TriggerDifficulties = prepareForDropdown(g_Settings && g_Settings.TriggerDifficulties); const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities); const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources); -const g_VictoryConditions = prepareForDropdown(g_Settings && g_Settings.VictoryConditions); const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations); +const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions; + var g_GameSpeeds = getGameSpeedChoices(false); /** @@ -433,7 +434,7 @@ { "label": translateWithContext("Match settings tab name", "Game Type"), "settings": [ - "victoryCondition", + ...g_VictoryConditions.map(victoryCondition => victoryCondition.Name), "relicCount", "relicDuration", "wonderDuration", @@ -640,22 +641,6 @@ "enabled": () => g_GameAttributes.mapType != "scenario", "initOrder": 1000 }, - "victoryCondition": { - "title": () => translate("Victory Condition"), - "tooltip": (hoverIdx) => g_VictoryConditions.Description[hoverIdx] || translate("Select victory condition."), - "labels": () => g_VictoryConditions.Title, - "ids": () => g_VictoryConditions.Name, - "default": () => g_VictoryConditions.Default, - "defined": () => g_GameAttributes.settings.GameType !== undefined, - "get": () => g_GameAttributes.settings.GameType, - "select": (itemIdx) => { - g_GameAttributes.settings.GameType = g_VictoryConditions.Name[itemIdx]; - g_GameAttributes.settings.VictoryScripts = g_VictoryConditions.Scripts[itemIdx]; - }, - "enabled": () => g_GameAttributes.mapType != "scenario", - "autocomplete": 0, - "initOrder": 1000 - }, "relicCount": { "title": () => translate("Relic Count"), "tooltip": (hoverIdx) => translate("Total number of relics spawned on the map. Relic victory is most realistic with only one or two relics. With greater numbers, the relics are important to capture to receive aura bonuses."), @@ -667,7 +652,7 @@ "select": (itemIdx) => { g_GameAttributes.settings.RelicCount = g_RelicCountList[itemIdx]; }, - "hidden": () => g_GameAttributes.settings.GameType != "capture_the_relic", + "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("capture_the_relic") == -1, "enabled": () => g_GameAttributes.mapType != "scenario", "initOrder": 1000 }, @@ -682,7 +667,7 @@ "select": (itemIdx) => { g_GameAttributes.settings.RelicDuration = g_VictoryDurations.Duration[itemIdx]; }, - "hidden": () => g_GameAttributes.settings.GameType != "capture_the_relic", + "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("capture_the_relic") == -1, "enabled": () => g_GameAttributes.mapType != "scenario", "initOrder": 1000 }, @@ -697,7 +682,7 @@ "select": (itemIdx) => { g_GameAttributes.settings.WonderDuration = g_VictoryDurations.Duration[itemIdx]; }, - "hidden": () => g_GameAttributes.settings.GameType != "wonder", + "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("wonder") == -1, "enabled": () => g_GameAttributes.mapType != "scenario", "initOrder": 1000 }, @@ -822,7 +807,34 @@ /** * Contains the logic of all boolean gamesettings. */ -var g_Checkboxes = { +var g_Checkboxes = Object.assign( + {}, + g_VictoryConditions.reduce((obj, victoryCondition) => { + obj[victoryCondition.Name] = { + "title": () => victoryCondition.Title, + "tooltip": () => victoryCondition.Description, + // Defaults are set in supplementDefault directly from g_VictoryConditions since we use an array + "defined": () => true, + "get": () => g_GameAttributes.settings.VictoryConditions.indexOf(victoryCondition.Name) != -1, + "set": checked => { + if (checked) + { + g_GameAttributes.settings.VictoryConditions.push(victoryCondition.Name); + if (victoryCondition.Set) + for (let setting in victoryCondition.ChangeWhenChecked) + g_Checkboxes[setting].set(victoryCondition.ChangeOnChecked[setting]); + } + else + g_GameAttributes.settings.VictoryConditions = g_GameAttributes.settings.VictoryConditions.filter(victoryConditionName => victoryConditionName != victoryCondition.Name); + }, + "enabled": () => + g_GameAttributes.mapType != "scenario" && + (!victoryCondition.DisabledWhenChecked || + victoryCondition.DisabledWhenChecked.every(victoryConditionName => g_GameAttributes.settings.VictoryConditions.indexOf(victoryConditionName) == -1)) + }; + return obj; + }, {}), + { "regicideGarrison": { "title": () => translate("Hero Garrison"), "tooltip": () => translate("Toggle whether heroes can be garrisoned."), @@ -832,7 +844,7 @@ "set": checked => { g_GameAttributes.settings.RegicideGarrison = checked; }, - "hidden": () => g_GameAttributes.settings.GameType != "regicide", + "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("regicide") == -1, "enabled": () => g_GameAttributes.mapType != "scenario", "initOrder": 1000 }, @@ -968,7 +980,8 @@ }, "initOrder": 1000 }, -}; + } +); /** * For setting up arbitrary GUI objects. @@ -1149,6 +1162,9 @@ */ function supplementDefaults() { + g_GameAttributes.settings.VictoryConditions = g_GameAttributes.settings.VictoryConditions || + g_VictoryConditions.filter(victoryCondition => !!victoryCondition.Default).map(victoryCondition => victoryCondition.Name); + for (let dropdown in g_Dropdowns) if (!g_Dropdowns[dropdown].defined()) g_Dropdowns[dropdown].select(g_Dropdowns[dropdown].default()); @@ -1987,14 +2003,8 @@ let mapSettings = mapData && mapData.settings ? clone(mapData.settings) : {}; if (g_GameAttributes.mapType != "random") - { delete g_GameAttributes.settings.Nomad; - let victoryIdx = g_VictoryConditions.Name.indexOf(mapSettings.GameType || "") != -1 ? g_VictoryConditions.Name.indexOf(mapSettings.GameType) : g_VictoryConditions.Default; - g_GameAttributes.settings.GameType = g_VictoryConditions.Name[victoryIdx]; - g_GameAttributes.settings.VictoryScripts = g_VictoryConditions.Scripts[victoryIdx]; - } - if (g_GameAttributes.mapType == "scenario") { delete g_GameAttributes.settings.RelicDuration; @@ -2134,13 +2144,7 @@ // Select random map if (g_GameAttributes.map == "random") - { - let victoryScriptsSelected = g_GameAttributes.settings.VictoryScripts; - let gameTypeSelected = g_GameAttributes.settings.GameType; selectMap(pickRandom(g_Dropdowns.mapSelection.ids().slice(1))); - g_GameAttributes.settings.VictoryScripts = victoryScriptsSelected; - g_GameAttributes.settings.GameType = gameTypeSelected; - } if (g_GameAttributes.settings.Biome == "random") g_GameAttributes.settings.Biome = pickRandom( @@ -2148,6 +2152,11 @@ g_BiomeList.Id.slice(1).filter(biomeID => biomeID.startsWith(g_GameAttributes.settings.SupportedBiomes)) : g_GameAttributes.settings.SupportedBiomes); + g_GameAttributes.settings.VictoryScripts = g_GameAttributes.settings.VictoryConditions.reduce( + (scripts, victoryConditionName) => scripts.concat(g_VictoryConditions[g_VictoryConditions.map(data => + data.Name).indexOf(victoryConditionName)].Scripts.filter(script => scripts.indexOf(script) == -1)), + []); + g_GameAttributes.settings.TriggerScripts = g_GameAttributes.settings.VictoryScripts.concat(g_GameAttributes.settings.TriggerScripts || []); // Prevent reseting the readystate @@ -2640,7 +2649,7 @@ "niceMapName": getMapDisplayName(g_GameAttributes.map), "mapSize": g_GameAttributes.mapType == "random" ? g_GameAttributes.settings.Size : "Default", "mapType": g_GameAttributes.mapType, - "victoryCondition": g_GameAttributes.settings.GameType, + "victoryConditions": g_GameAttributes.settings.VictoryConditions.join(","), "nbp": clients.connectedPlayers, "maxnbp": g_GameAttributes.settings.PlayerData.length, "players": clients.list, Index: ps/trunk/binaries/data/mods/public/gui/loadgame/load.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/loadgame/load.js +++ ps/trunk/binaries/data/mods/public/gui/loadgame/load.js @@ -113,7 +113,7 @@ Engine.GetGUIObjectByName("savedPlayedTime").caption = timeToString(metadata.gui.timeElapsed ? metadata.gui.timeElapsed : 0); Engine.GetGUIObjectByName("savedMapType").caption = translateMapType(metadata.initAttributes.mapType); Engine.GetGUIObjectByName("savedMapSize").caption = translateMapSize(metadata.initAttributes.settings.Size); - Engine.GetGUIObjectByName("savedVictory").caption = translateVictoryCondition(metadata.initAttributes.settings.GameType); + Engine.GetGUIObjectByName("savedVictory").caption = metadata.initAttributes.settings.VictoryConditions.map(victoryConditionName => translateVictoryCondition(victoryConditionName)).join(translate(", ")); let caption = sprintf(translate("Mods: %(mods)s"), { "mods": modsToString(metadata.mods) }); if (!hasSameMods(metadata.mods, Engine.GetEngineInfo().mods)) Index: ps/trunk/binaries/data/mods/public/gui/loadgame/load.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/loadgame/load.xml +++ ps/trunk/binaries/data/mods/public/gui/loadgame/load.xml @@ -97,17 +97,17 @@ Map Size: - + Victory: - + - + - + - - + + Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_filters.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_filters.js +++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_filters.js @@ -152,8 +152,8 @@ function initVictoryConditionFilter(filters) { let victoryConditionFilter = Engine.GetGUIObjectByName("victoryConditionFilter"); - victoryConditionFilter.list = [translateWithContext("victory condition", "Any gametype")].concat(g_VictoryConditions.map(vc => translateVictoryCondition(vc))); - victoryConditionFilter.list_data = [""].concat(g_VictoryConditions); + victoryConditionFilter.list = [translate("Any Victory Condition")].concat(g_VictoryConditions.map(victoryCondition => translateVictoryCondition(victoryCondition.Name))); + victoryConditionFilter.list_data = [""].concat(g_VictoryConditions.map(victoryCondition => victoryCondition.Name)); if (filters && filters.victoryCondition) victoryConditionFilter.selected = victoryConditionFilter.list_data.indexOf(filters.victoryCondition); @@ -243,7 +243,8 @@ // Filter by victory condition let victoryConditionFilter = Engine.GetGUIObjectByName("victoryConditionFilter"); - if (victoryConditionFilter.selected > 0 && replay.attribs.settings.GameType != victoryConditionFilter.list_data[victoryConditionFilter.selected]) + if (victoryConditionFilter.selected > 0 && + replay.attribs.settings.VictoryConditions.indexOf(victoryConditionFilter.list_data[victoryConditionFilter.selected]) == -1) return false; // Filter by rating Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js +++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js @@ -36,7 +36,7 @@ /** * Sorted list of the victory conditions occuring in the replays */ -var g_VictoryConditions = []; +var g_VictoryConditions = g_Settings && g_Settings.VictoryConditions; /** * Directory name of the currently selected replay. Used to restore the selection after changing filters. @@ -108,10 +108,6 @@ if (g_MapNames.indexOf(replay.attribs.settings.Name) == -1 && replay.attribs.settings.Name != "") g_MapNames.push(replay.attribs.settings.Name); - // Extract victory conditions - if (replay.attribs.settings.GameType && g_VictoryConditions.indexOf(replay.attribs.settings.GameType) == -1) - g_VictoryConditions.push(replay.attribs.settings.GameType); - // Extract playernames for (let playerData of replay.attribs.settings.PlayerData) { @@ -138,7 +134,6 @@ } g_MapNames.sort(); - g_VictoryConditions.sort(); // Reload filters (since they depend on g_Replays and its derivatives) initFilters(replaySelectionData && replaySelectionData.filters); @@ -182,9 +177,6 @@ if (!attribs.settings.mapType) attribs.settings.mapType = "skirmish"; - if (!attribs.settings.GameType) - attribs.settings.GameType = "conquest"; - // Remove gaia if (attribs.settings.PlayerData.length && attribs.settings.PlayerData[0] == null) attribs.settings.PlayerData.shift(); @@ -276,7 +268,8 @@ Engine.GetGUIObjectByName("sgMapName").caption = translate(replay.attribs.settings.Name); Engine.GetGUIObjectByName("sgMapSize").caption = translateMapSize(replay.attribs.settings.Size); Engine.GetGUIObjectByName("sgMapType").caption = translateMapType(replay.attribs.settings.mapType); - Engine.GetGUIObjectByName("sgVictory").caption = translateVictoryCondition(replay.attribs.settings.GameType); + Engine.GetGUIObjectByName("sgVictory").caption = replay.attribs.settings.VictoryConditions.map(victoryConditionName => + translateVictoryCondition(victoryConditionName)).join(translate(", ")); Engine.GetGUIObjectByName("sgNbPlayers").caption = sprintf(translate("Players: %(numberOfPlayers)s"), { "numberOfPlayers": replay.attribs.settings.PlayerData.length }); Engine.GetGUIObjectByName("replayFilename").caption = Engine.GetReplayDirectoryName(replay.directory); Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.xml +++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.xml @@ -175,22 +175,22 @@ - + Victory: - - + + - + - + Index: ps/trunk/binaries/data/mods/public/gui/session/session.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.js +++ ps/trunk/binaries/data/mods/public/gui/session/session.js @@ -7,8 +7,9 @@ const g_MapTypes = prepareForDropdown(g_Settings && g_Settings.MapTypes); const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities); const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources); -const g_VictoryConditions = prepareForDropdown(g_Settings && g_Settings.VictoryConditions); const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations); +const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions; + var g_GameSpeeds; /** Index: ps/trunk/binaries/data/mods/public/maps/random/wall_demo.json =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/wall_demo.json +++ ps/trunk/binaries/data/mods/public/maps/random/wall_demo.json @@ -1,13 +1,13 @@ { "settings" : { "Name" : "Wall Demo", - "GameType" : "endless", "Script" : "wall_demo.js", "Description" : "A demonstration of wall placement methods/code in random maps. Giant map size is recommended!", "Keywords": ["demo"], "CircularMap" : false, "TriggerScripts" : [ "random/wall_demo_triggers.js" - ] + ], + "VictoryConditions": [] } } Index: ps/trunk/binaries/data/mods/public/maps/scripts/CaptureTheRelic.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scripts/CaptureTheRelic.js +++ ps/trunk/binaries/data/mods/public/maps/scripts/CaptureTheRelic.js @@ -12,7 +12,7 @@ } let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); - let numSpawnedRelics = cmpEndGameManager.GetGameTypeSettings().relicCount; + let numSpawnedRelics = cmpEndGameManager.GetGameSettings().relicCount; this.playerRelicsCount = new Array(TriggerHelper.GetNumberOfPlayers()).fill(0, 1); this.playerRelicsCount[0] = numSpawnedRelics; @@ -132,7 +132,7 @@ let cmpPlayer = QueryOwnerInterface(this.relics[0], IID_Player); let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); - let captureTheRelicDuration = cmpEndGameManager.GetGameTypeSettings().relicDuration; + let captureTheRelicDuration = cmpEndGameManager.GetGameSettings().relicDuration; let isTeam = winningPlayers.length > 1; this.ownRelicsVictoryMessage = cmpGuiInterface.AddTimeNotification({ Index: ps/trunk/binaries/data/mods/public/maps/scripts/Conquest.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scripts/Conquest.js +++ ps/trunk/binaries/data/mods/public/maps/scripts/Conquest.js @@ -1,5 +1,7 @@ { let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); - cmpTrigger.conquestClassFilter = "ConquestCritical"; - cmpTrigger.conquestDefeatReason = markForTranslation("%(player)s has been defeated (lost all critical units and structures)."); + cmpTrigger.ConquestAddVictoryCondition({ + "classFilter": "ConquestCritical", + "defeatReason": markForTranslation("%(player)s has been defeated (lost all critical units and structures).") + }); } Index: ps/trunk/binaries/data/mods/public/maps/scripts/ConquestCommon.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scripts/ConquestCommon.js +++ ps/trunk/binaries/data/mods/public/maps/scripts/ConquestCommon.js @@ -1,17 +1,20 @@ Trigger.prototype.ConquestOwnershipChanged = function(msg) { - if (!this.conquestDataInit || !this.conquestClassFilter) + if (!this.conquestDataInit) return; - if (!TriggerHelper.EntityMatchesClassList(msg.entity, this.conquestClassFilter)) - return; + for (let query of this.conquestQueries) + { + if (!TriggerHelper.EntityMatchesClassList(msg.entity, query.classFilter)) + continue; - if (msg.to > 0) - this.conquestEntitiesByPlayer[msg.to].push(msg.entity); + if (msg.to > 0) + query.entitiesByPlayer[msg.to].push(msg.entity); - if (msg.from > 0) - { - let entities = this.conquestEntitiesByPlayer[msg.from]; + if (msg.from <= 0) + continue; + + let entities = query.entitiesByPlayer[msg.from]; let index = entities.indexOf(msg.entity); if (index != -1) entities.splice(index, 1); @@ -20,35 +23,40 @@ { let cmpPlayer = QueryPlayerIDInterface(msg.from); if (cmpPlayer) - cmpPlayer.SetState("defeated", this.conquestDefeatReason); + cmpPlayer.SetState("defeated", query.defeatReason); } } }; Trigger.prototype.ConquestStartGameCount = function() { - if (!this.conquestClassFilter) + if (!this.conquestQueries.length) { - warn("ConquestStartGameCount: conquestClassFilter undefined"); + warn("ConquestStartGameCount: no conquestQueries set"); return; } let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + let entitiesByPlayer = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers().map(playerID => + cmpRangeManager.GetEntitiesByPlayer(playerID)); - let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); - for (let i = 1; i < numPlayers; ++i) - this.conquestEntitiesByPlayer[i] = - cmpRangeManager.GetEntitiesByPlayer(i).filter(ent => - TriggerHelper.EntityMatchesClassList(ent, this.conquestClassFilter)); + for (let query of this.conquestQueries) + query.entitiesByPlayer = entitiesByPlayer.map( + ents => ents.filter( + ent => TriggerHelper.EntityMatchesClassList(ent, query.classFilter))); this.conquestDataInit = true; }; +Trigger.prototype.ConquestAddVictoryCondition = function(data) +{ + this.conquestQueries.push(data); +}; + { let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); - cmpTrigger.conquestEntitiesByPlayer = {}; cmpTrigger.conquestDataInit = false; - cmpTrigger.conquestClassFilter = ""; + cmpTrigger.conquestQueries = []; cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestOwnershipChanged", { "enabled": true }); cmpTrigger.DoAfterDelay(0, "ConquestStartGameCount", null); } Index: ps/trunk/binaries/data/mods/public/maps/scripts/ConquestStructures.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scripts/ConquestStructures.js +++ ps/trunk/binaries/data/mods/public/maps/scripts/ConquestStructures.js @@ -1,5 +1,7 @@ { let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); - cmpTrigger.conquestClassFilter = "Structure"; - cmpTrigger.conquestDefeatReason = markForTranslation("%(player)s has been defeated (lost all structures)."); + cmpTrigger.ConquestAddVictoryCondition({ + "classFilter": "Structure", + "defeatReason": markForTranslation("%(player)s has been defeated (lost all structures).") + }); } Index: ps/trunk/binaries/data/mods/public/maps/scripts/ConquestUnits.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scripts/ConquestUnits.js +++ ps/trunk/binaries/data/mods/public/maps/scripts/ConquestUnits.js @@ -1,5 +1,7 @@ { let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); - cmpTrigger.conquestClassFilter = "Unit+!Animal"; - cmpTrigger.conquestDefeatReason = markForTranslation("%(player)s has been defeated (lost all units)."); + cmpTrigger.ConquestAddVictoryCondition({ + "classFilter": "Unit+!Animal", + "defeatReason": markForTranslation("%(player)s has been defeated (lost all units).") + }); } Index: ps/trunk/binaries/data/mods/public/maps/scripts/Regicide.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scripts/Regicide.js +++ ps/trunk/binaries/data/mods/public/maps/scripts/Regicide.js @@ -9,7 +9,7 @@ Trigger.prototype.InitRegicideGame = function(msg) { let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); - let regicideGarrison = cmpEndGameManager.GetGameTypeSettings().regicideGarrison; + let regicideGarrison = cmpEndGameManager.GetGameSettings().regicideGarrison; let playersCivs = []; for (let playerID = 1; playerID < TriggerHelper.GetNumberOfPlayers(); ++playerID) Index: ps/trunk/binaries/data/mods/public/maps/scripts/WonderVictory.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/scripts/WonderVictory.js +++ ps/trunk/binaries/data/mods/public/maps/scripts/WonderVictory.js @@ -54,7 +54,7 @@ let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); - let wonderDuration = cmpEndGameManager.GetGameTypeSettings().wonderDuration; + let wonderDuration = cmpEndGameManager.GetGameSettings().wonderDuration; this.wonderVictoryMessages[ent] = { "playerID": player, "allies": new Set(allies), Index: ps/trunk/binaries/data/mods/public/maps/tutorials/Introductory_Tutorial.xml =================================================================== --- ps/trunk/binaries/data/mods/public/maps/tutorials/Introductory_Tutorial.xml +++ ps/trunk/binaries/data/mods/public/maps/tutorials/Introductory_Tutorial.xml @@ -40,7 +40,6 @@ { "CircularMap": true, "Description": "This is a basic tutorial to get you started playing 0 A.D.", - "GameType": "endless", "Keywords": ["demo"], "LockTeams": false, "Name": "Introductory Tutorial", @@ -86,7 +85,8 @@ "scripts/TriggerHelper.js", "scripts/Tutorial.js", "tutorials/Introductory_Tutorial.js" - ] + ], + "VictoryConditions": [] } ]]> Index: ps/trunk/binaries/data/mods/public/maps/tutorials/starting_economy_walkthrough.xml =================================================================== --- ps/trunk/binaries/data/mods/public/maps/tutorials/starting_economy_walkthrough.xml +++ ps/trunk/binaries/data/mods/public/maps/tutorials/starting_economy_walkthrough.xml @@ -48,7 +48,6 @@ ], "CircularMap": true, "Description": "This map will give a rough guide for starting the game effectively. Early in the game the most important thing is to gather resources as fast as possible so you are able to build enough troops later.", - "GameType": "endless", "Keywords": ["trigger"], "LockTeams": false, "Name": "Starting Economy Walkthrough", @@ -71,7 +70,8 @@ "scripts/Tutorial.js", "tutorials/starting_economy_walkthrough.js" ], - "Size": 256 + "Size": 256, + "VictoryConditions": [] } ]]> Index: ps/trunk/binaries/data/mods/public/simulation/ai/common-api/shared.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/common-api/shared.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/common-api/shared.js @@ -74,7 +74,7 @@ this.timeElapsed = state.timeElapsed; this.circularMap = state.circularMap; this.mapSize = state.mapSize; - this.victoryConditions = new Set([state.gameType]); + this.victoryConditions = new Set(state.victoryConditions); this.alliedVictory = state.alliedVictory; this.ceasefireActive = state.ceasefireActive; this.ceasefireTimeRemaining = state.ceasefireTimeRemaining / 1000; Index: ps/trunk/binaries/data/mods/public/simulation/components/EndGameManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/EndGameManager.js +++ ps/trunk/binaries/data/mods/public/simulation/components/EndGameManager.js @@ -1,5 +1,5 @@ /** - * System component to store the gametype, gametype settings and + * System component to store the victory conditions and their settings and * check for allied victory / last-man-standing. */ function EndGameManager() {} @@ -9,11 +9,9 @@ EndGameManager.prototype.Init = function() { - this.gameType = "conquest"; - // Contains settings specific to the victory condition, // for example wonder victory duration. - this.gameTypeSettings = {}; + this.gameSettings = {}; // Allied victory means allied players can win if victory conditions are met for each of them // False for a "last man standing" game @@ -28,24 +26,23 @@ this.endlessGame = false; }; -EndGameManager.prototype.GetGameType = function() +EndGameManager.prototype.GetGameSettings = function() { - return this.gameType; + return this.gameSettings; }; -EndGameManager.prototype.GetGameTypeSettings = function() +EndGameManager.prototype.GetVictoryConditions = function() { - return this.gameTypeSettings; + return this.gameSettings.victoryConditions; }; -EndGameManager.prototype.SetGameType = function(newGameType, newSettings = {}) +EndGameManager.prototype.SetGameSettings = function(newSettings = {}) { - this.gameType = newGameType; - this.gameTypeSettings = newSettings; + this.gameSettings = newSettings; this.skipAlliedVictoryCheck = false; - this.endlessGame = newGameType == "endless"; + this.endlessGame = !this.gameSettings.victoryConditions.length; - Engine.BroadcastMessage(MT_GameTypeChanged, {}); + Engine.BroadcastMessage(MT_VictoryConditionsChanged, {}); }; /** Index: ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js +++ ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js @@ -154,7 +154,7 @@ // Add the game type and allied victory let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); - ret.gameType = cmpEndGameManager.GetGameType(); + ret.victoryConditions = cmpEndGameManager.GetVictoryConditions(); ret.alliedVictory = cmpEndGameManager.GetAlliedVictory(); // Add basic statistics to each player Index: ps/trunk/binaries/data/mods/public/simulation/components/interfaces/EndGameManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/interfaces/EndGameManager.js +++ ps/trunk/binaries/data/mods/public/simulation/components/interfaces/EndGameManager.js @@ -4,4 +4,4 @@ * Message of the form {} * sent from EndGameManager component. */ -Engine.RegisterMessageType("GameTypeChanged"); +Engine.RegisterMessageType("VictoryConditionsChanged"); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_EndGameManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_EndGameManager.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_EndGameManager.js @@ -23,7 +23,10 @@ TS_ASSERT_EQUALS(cmpEndGameManager.skipAlliedVictoryCheck, true); cmpEndGameManager.SetAlliedVictory(true); TS_ASSERT_EQUALS(cmpEndGameManager.GetAlliedVictory(), true); -cmpEndGameManager.SetGameType("wonder", { "wonderDuration": wonderDuration }); +cmpEndGameManager.SetGameSettings({ + "victoryConditions": ["wonder"], + "wonderDuration": wonderDuration +}); TS_ASSERT_EQUALS(cmpEndGameManager.skipAlliedVictoryCheck, false); -TS_ASSERT(cmpEndGameManager.GetGameType() == "wonder"); -TS_ASSERT_EQUALS(cmpEndGameManager.GetGameTypeSettings().wonderDuration, wonderDuration); +TS_ASSERT_UNEVAL_EQUALS(cmpEndGameManager.GetVictoryConditions(), ["wonder"]); +TS_ASSERT_EQUALS(cmpEndGameManager.GetGameSettings().wonderDuration, wonderDuration); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -65,7 +65,7 @@ }); AddMock(SYSTEM_ENTITY, IID_EndGameManager, { - GetGameType: function() { return "conquest"; }, + GetVictoryConditions: () => ["conquest", "wonder"], GetAlliedVictory: function() { return false; } }); @@ -365,7 +365,7 @@ ], circularMap: false, timeElapsed: 0, - gameType: "conquest", + "victoryConditions": ["conquest", "wonder"], alliedVictory: false }); @@ -518,7 +518,7 @@ ], "circularMap": false, "timeElapsed": 0, - "gameType": "conquest", + "victoryConditions": ["conquest", "wonder"], "alliedVictory": false }); Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/capture_the_relic.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/capture_the_relic.json +++ ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/capture_the_relic.json @@ -7,9 +7,8 @@ "Scripts": [ "scripts/TriggerHelper.js", - "scripts/ConquestCommon.js", - "scripts/Conquest.js", "scripts/CaptureTheRelic.js" - ] + ], + "GUIOrder": 10 } } Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest.json +++ ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest.json @@ -3,13 +3,18 @@ "Data": { "Title": "Conquest", - "Description": "Defeat all opponents to win.", + "Description": "Defeat opponents by killing all their units and destroying all their structures.", "Scripts": [ "scripts/TriggerHelper.js", "scripts/ConquestCommon.js", "scripts/Conquest.js" ], - "Default": true + "Default": true, + "ChangeOnChecked": { + "conquest_units": false, + "conquest_structures": false + }, + "GUIOrder": 0 } } Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest_structures.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest_structures.json +++ ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest_structures.json @@ -3,12 +3,14 @@ "Data": { "Title": "Conquest Structures", - "Description": "Destroy all enemy structures to win.", + "Description": "Defeat opponents by destroying all their structures.", "Scripts": [ "scripts/TriggerHelper.js", "scripts/ConquestCommon.js", "scripts/ConquestStructures.js" - ] + ], + "DisabledWhenChecked": ["conquest"], + "GUIOrder": 1 } } Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest_units.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest_units.json +++ ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest_units.json @@ -3,12 +3,14 @@ "Data": { "Title": "Conquest Units", - "Description": "Kill all enemy units to win.", + "Description": "Defeat opponents by killing all their units.", "Scripts": [ "scripts/TriggerHelper.js", "scripts/ConquestCommon.js", "scripts/ConquestUnits.js" - ] + ], + "DisabledWhenChecked": ["conquest"], + "GUIOrder": 1 } } Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/regicide.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/regicide.json +++ ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/regicide.json @@ -7,9 +7,8 @@ "Scripts": [ "scripts/TriggerHelper.js", - "scripts/ConquestCommon.js", - "scripts/Conquest.js", "scripts/Regicide.js" - ] + ], + "GUIOrder": 10 } } Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/wonder.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/wonder.json +++ ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/wonder.json @@ -7,9 +7,8 @@ "Scripts": [ "scripts/TriggerHelper.js", - "scripts/ConquestCommon.js", - "scripts/Conquest.js", "scripts/WonderVictory.js" - ] + ], + "GUIOrder": 10 } } Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js +++ ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js @@ -53,17 +53,17 @@ } let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager); - let gameTypeSettings = {}; - if (settings.GameType && settings.GameType == "capture_the_relic") - gameTypeSettings.relicCount = settings.RelicCount; - if (settings.GameType && settings.GameType == "capture_the_relic") - gameTypeSettings.relicDuration = settings.RelicDuration * 60 * 1000; - if (settings.GameType && settings.GameType == "wonder") - gameTypeSettings.wonderDuration = settings.WonderDuration * 60 * 1000; - if (settings.GameType && settings.GameType == "regicide") - gameTypeSettings.regicideGarrison = settings.RegicideGarrison; - if (settings.GameType) - cmpEndGameManager.SetGameType(settings.GameType, gameTypeSettings); + let gameSettings = { "victoryConditions": settings.VictoryConditions }; + if (gameSettings.victoryConditions.indexOf("capture_the_relic") != -1) + { + gameSettings.relicCount = settings.RelicCount; + gameSettings.relicDuration = settings.RelicDuration * 60 * 1000; + } + if (gameSettings.victoryConditions.indexOf("wonder") != -1) + gameSettings.wonderDuration = settings.WonderDuration * 60 * 1000; + if (gameSettings.victoryConditions.indexOf("regicide") != -1) + gameSettings.regicideGarrison = settings.RegicideGarrison; + cmpEndGameManager.SetGameSettings(gameSettings); cmpEndGameManager.SetAlliedVictory(settings.LockTeams || !settings.LastManStanding); if (settings.LockTeams && settings.LastManStanding) Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/GameSetup.cpp +++ ps/trunk/source/ps/GameSetup/GameSetup.cpp @@ -1464,24 +1464,37 @@ triggerScriptsVector.push_back(nonVisualScript.FromUTF8()); } - CStr victory = "conquest"; + std::vector victoryConditions(1, "conquest"); if (args.Has("autostart-victory")) - victory = args.Get("autostart-victory"); + victoryConditions = args.GetMultiple("autostart-victory"); - scriptInterface.SetProperty(settings, "GameType", std::string(victory)); + if (victoryConditions.size() == 1 && victoryConditions[0] == "endless") + victoryConditions.clear(); - CStrW scriptPath = L"simulation/data/settings/victory_conditions/" + victory.FromUTF8() + L".json"; - JS::RootedValue scriptData(cx); - JS::RootedValue data(cx); - JS::RootedValue victoryScripts(cx); - scriptInterface.ReadJSONFile(scriptPath, &scriptData); - if (!scriptData.isUndefined() && scriptInterface.GetProperty(scriptData, "Data", &data) && !data.isUndefined() - && scriptInterface.GetProperty(data, "Scripts", &victoryScripts) && !victoryScripts.isUndefined()) - { - std::vector victoryScriptsVector; - FromJSVal_vector(cx, victoryScripts, victoryScriptsVector); - triggerScriptsVector.insert(triggerScriptsVector.end(), victoryScriptsVector.begin(), victoryScriptsVector.end()); + scriptInterface.SetProperty(settings, "VictoryConditions", victoryConditions); + + for (const CStr& victory : victoryConditions) + { + JS::RootedValue scriptData(cx); + JS::RootedValue data(cx); + JS::RootedValue victoryScripts(cx); + + CStrW scriptPath = L"simulation/data/settings/victory_conditions/" + victory.FromUTF8() + L".json"; + scriptInterface.ReadJSONFile(scriptPath, &scriptData); + if (!scriptData.isUndefined() && scriptInterface.GetProperty(scriptData, "Data", &data) && !data.isUndefined() + && scriptInterface.GetProperty(data, "Scripts", &victoryScripts) && !victoryScripts.isUndefined()) + { + std::vector victoryScriptsVector; + FromJSVal_vector(cx, victoryScripts, victoryScriptsVector); + triggerScriptsVector.insert(triggerScriptsVector.end(), victoryScriptsVector.begin(), victoryScriptsVector.end()); + } + else + { + LOGERROR("Autostart: Error reading victory script '%s'", utf8_from_wstring(scriptPath)); + throw PSERROR_Game_World_MapLoadFailed("Error reading victory script.\nCheck application log for details."); + } } + ToJSVal_vector(cx, &triggerScripts, triggerScriptsVector); scriptInterface.SetProperty(settings, "TriggerScripts", triggerScripts); Index: ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp =================================================================== --- ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp +++ ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -41,6 +41,12 @@ ID_MapTeams, ID_MapKW_Demo, ID_MapKW_Naval, + ID_VC_Conquest, + ID_VC_ConquestUnits, + ID_VC_ConquestStructures, + ID_VC_CaptureTheRelic, + ID_VC_Wonder, + ID_VC_Regicide, ID_RandomScript, ID_RandomSize, ID_RandomNomad, @@ -97,13 +103,17 @@ AtObj UpdateSettingsObject(); private: void SendToEngine(); + void OnConquestChanged(); - void OnEdit(wxCommandEvent& WXUNUSED(evt)) + void OnEdit(wxCommandEvent& evt) { SendToEngine(); + if (evt.GetId() == ID_VC_Conquest) + OnConquestChanged(); } std::set m_MapSettingsKeywords; + std::set m_MapSettingsVictoryConditions; std::vector m_PlayerCivChoices; Observable& m_MapSettings; @@ -146,31 +156,34 @@ sizer->AddSpacer(5); - // TODO: replace by filenames in binaries/data/mods/public/simulation/data/settings/victory_conditions/ - wxArrayString gameTypes; - gameTypes.Add(_T("conquest")); - gameTypes.Add(_T("conquest_structures")); - gameTypes.Add(_T("conquest_units")); - gameTypes.Add(_T("wonder")); - gameTypes.Add(_T("endless")); - gameTypes.Add(_T("regicide")); - gameTypes.Add(_T("capture_the_relic")); - wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5); gridSizer->AddGrowableCol(1); + // TODO: have preview selector tool? gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Preview")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(new wxTextCtrl(this, ID_MapPreview, wxEmptyString), _("Texture used for map preview")), wxSizerFlags().Expand()); CREATE_CHECKBOX(this, gridSizer, "Reveal map", "If checked, players won't need to explore", ID_MapReveal); - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Game type")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); - gridSizer->Add(Tooltipped(new wxChoice(this, ID_MapType, wxDefaultPosition, wxDefaultSize, gameTypes), - _("Select the game type (or victory condition)")), wxSizerFlags().Expand()); CREATE_CHECKBOX(this, gridSizer, "Lock teams", "If checked, teams will be locked", ID_MapTeams); sizer->Add(gridSizer, wxSizerFlags().Expand()); sizer->AddSpacer(5); + // TODO: replace by names in binaries/data/mods/public/simulation/data/settings/victory_conditions/ + wxStaticBoxSizer* victoryConditionSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Victory Conditions")); + wxFlexGridSizer* vcGridSizer = new wxFlexGridSizer(2, 0, 5); + vcGridSizer->AddGrowableCol(1); + CREATE_CHECKBOX(this, vcGridSizer, "Conquest", "Select Conquest victory condition", ID_VC_Conquest); + CREATE_CHECKBOX(this, vcGridSizer, "Conquest Units", "Select Conquest Units victory condition", ID_VC_ConquestUnits); + CREATE_CHECKBOX(this, vcGridSizer, "Conquest Structures", "Select Conquest Structures victory condition", ID_VC_ConquestStructures); + CREATE_CHECKBOX(this, vcGridSizer, "Capture the Relic", "Select Capture the Relic victory condition", ID_VC_CaptureTheRelic); + CREATE_CHECKBOX(this, vcGridSizer, "Wonder", "Select Wonder victory condition", ID_VC_Wonder); + CREATE_CHECKBOX(this, vcGridSizer, "Regicide", "Select Regicide victory condition", ID_VC_Regicide); + victoryConditionSizer->Add(vcGridSizer); + sizer->Add(victoryConditionSizer, wxSizerFlags().Expand()); + + sizer->AddSpacer(5); + wxStaticBoxSizer* keywordsSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Keywords")); wxFlexGridSizer* kwGridSizer = new wxFlexGridSizer(4, 5, 5); CREATE_CHECKBOX(this, kwGridSizer, "Demo", "If checked, map will only be visible using filters in game setup", ID_MapKW_Demo); @@ -201,11 +214,24 @@ // reveal map wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->SetValue(wxString(m_MapSettings["RevealMap"]) == L"true"); - // game type / victory conditions - if (m_MapSettings["GameType"].defined()) - wxDynamicCast(FindWindow(ID_MapType), wxChoice)->SetStringSelection(wxString(m_MapSettings["GameType"])); - else - wxDynamicCast(FindWindow(ID_MapType), wxChoice)->SetSelection(0); + // victory conditions + m_MapSettingsVictoryConditions.clear(); + for (AtIter victoryCondition = m_MapSettings["VictoryConditions"]["item"]; victoryCondition.defined(); ++victoryCondition) + m_MapSettingsVictoryConditions.insert(std::wstring(victoryCondition)); + + wxWindow* window; +#define INIT_CHECKBOX(ID, mapSettings, value) \ + window = FindWindow(ID); \ + if (window != nullptr) \ + wxDynamicCast(window, wxCheckBox)->SetValue(mapSettings.count(value) != 0); + + INIT_CHECKBOX(ID_VC_Conquest, m_MapSettingsVictoryConditions, L"conquest"); + INIT_CHECKBOX(ID_VC_ConquestUnits, m_MapSettingsVictoryConditions, L"conquest_units"); + INIT_CHECKBOX(ID_VC_ConquestStructures, m_MapSettingsVictoryConditions, L"conquest_structures"); + INIT_CHECKBOX(ID_VC_CaptureTheRelic, m_MapSettingsVictoryConditions, L"capture_the_relic"); + INIT_CHECKBOX(ID_VC_Wonder, m_MapSettingsVictoryConditions, L"wonder"); + INIT_CHECKBOX(ID_VC_Regicide, m_MapSettingsVictoryConditions, L"regicide"); + OnConquestChanged(); // lock teams wxDynamicCast(FindWindow(ID_MapTeams), wxCheckBox)->SetValue(wxString(m_MapSettings["LockTeams"]) == L"true"); @@ -216,9 +242,11 @@ for (AtIter keyword = m_MapSettings["Keywords"]["item"]; keyword.defined(); ++keyword) m_MapSettingsKeywords.insert(std::wstring(keyword)); - wxDynamicCast(FindWindow(ID_MapKW_Demo), wxCheckBox)->SetValue(m_MapSettingsKeywords.count(L"demo") != 0); - wxDynamicCast(FindWindow(ID_MapKW_Naval), wxCheckBox)->SetValue(m_MapSettingsKeywords.count(L"naval") != 0); + INIT_CHECKBOX(ID_MapKW_Demo, m_MapSettingsKeywords, L"demo"); + INIT_CHECKBOX(ID_MapKW_Naval, m_MapSettingsKeywords, L"naval"); } + +#undef INIT_CHECKBOX } void MapSettingsControl::SetMapSettings(const AtObj& obj) @@ -229,6 +257,22 @@ SendToEngine(); } +// TODO Use the json data for this +void MapSettingsControl::OnConquestChanged() +{ + bool conqestEnabled = wxDynamicCast(FindWindow(ID_VC_Conquest), wxCheckBox)->GetValue(); + + wxCheckBox* conquestUnitsCheckbox = wxDynamicCast(FindWindow(ID_VC_ConquestUnits), wxCheckBox); + conquestUnitsCheckbox->Enable(!conqestEnabled); + wxCheckBox* conquestStructuresCheckbox = wxDynamicCast(FindWindow(ID_VC_ConquestStructures), wxCheckBox); + conquestStructuresCheckbox->Enable(!conqestEnabled); + if (conqestEnabled) + { + conquestUnitsCheckbox->SetValue(false); + conquestStructuresCheckbox->SetValue(false); + } +} + AtObj MapSettingsControl::UpdateSettingsObject() { // map name @@ -243,8 +287,27 @@ // reveal map m_MapSettings.setBool("RevealMap", wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->GetValue()); - // game type / victory conditions - m_MapSettings.set("GameType", wxDynamicCast(FindWindow(ID_MapType), wxChoice)->GetStringSelection()); + // victory conditions +#define INSERT_VICTORY_CONDITION_CHECKBOX(name, ID) \ + if (wxDynamicCast(FindWindow(ID), wxCheckBox)->GetValue()) \ + m_MapSettingsVictoryConditions.insert(name); \ + else \ + m_MapSettingsVictoryConditions.erase(name); + + INSERT_VICTORY_CONDITION_CHECKBOX(L"conquest", ID_VC_Conquest); + INSERT_VICTORY_CONDITION_CHECKBOX(L"conquest_units", ID_VC_ConquestUnits); + INSERT_VICTORY_CONDITION_CHECKBOX(L"conquest_structures", ID_VC_ConquestStructures); + INSERT_VICTORY_CONDITION_CHECKBOX(L"capture_the_relic", ID_VC_CaptureTheRelic); + INSERT_VICTORY_CONDITION_CHECKBOX(L"wonder", ID_VC_Wonder); + INSERT_VICTORY_CONDITION_CHECKBOX(L"regicide", ID_VC_Regicide); + +#undef INSERT_VICTORY_CONDITION_CHECKBOX + + AtObj victoryConditions; + victoryConditions.set("@array", L""); + for (std::set::iterator it = m_MapSettingsVictoryConditions.begin(); it != m_MapSettingsVictoryConditions.end(); ++it) + victoryConditions.add("item", it->c_str()); + m_MapSettings.set("VictoryConditions", victoryConditions); // keywords {