From 7901ed51d421acd8022b712af0f8e36ba28b071c Mon Sep 17 00:00:00 2001 From: historic_bruno Date: Fri, 4 Oct 2013 02:29:16 +0000 Subject: [PATCH] Implements skirmish maps, based on patch by sanderd17, fixes #1198. Skirmish maps are like scenarios, except the player can choose their civ during match setup. To create a skirmish map: place some skirmish entities for each player in Atlas (see templates/skirmish/* for examples), uncheck the player's civ in Atlas' player panel if desired, and save in the maps/skirmishes directory. The map will appear in match setup under the "Skirmish" match type. Implements custom, VFS-based map load/save dialogs for Atlas (replaces broken native file dialogs), fixes #631, #889. Fixes map loading/saving to handle arbitrary subdirectories for better organization. Adds default settings to Atlas player panel, fixes #1872. Each setting now has a checkbox to choose whether it should be saved with the map (avoids writing lots of useless default data for each map). Adds map preview setting to Atlas, refs #1745. Cleans up and simplifies some duplicate code. Fixes optional serialization performance test. This was SVN commit r13938. --- .../mods/public/gui/gamesetup/gamesetup.js | 104 +++++++-- .../data/mods/public/gui/loading/loading.js | 1 + .../simulation/components/SkirmishReplacer.js | 63 +++++ .../components/interfaces/SkirmishReplacer.js | 1 + .../structures/default_civil_centre.xml | 12 + .../skirmish/structures/default_wall_gate.xml | 27 +++ .../skirmish/structures/default_wall_long.xml | 23 ++ .../structures/default_wall_medium.xml | 23 ++ .../structures/default_wall_short.xml | 23 ++ .../structures/default_wall_tower.xml | 23 ++ .../skirmish/structures/iber_wall_gate.xml | 26 +++ .../skirmish/structures/iber_wall_long.xml | 22 ++ .../skirmish/structures/iber_wall_medium.xml | 22 ++ .../skirmish/structures/iber_wall_short.xml | 22 ++ .../skirmish/structures/iber_wall_tower.xml | 22 ++ .../skirmish/units/default_cavalry.xml | 19 ++ .../units/default_infantry_melee_b.xml | 14 ++ .../units/default_infantry_ranged_b.xml | 18 ++ .../units/default_support_female_citizen.xml | 12 + build/premake/premake4.lua | 1 + source/graphics/MapReader.cpp | 12 +- source/graphics/MapReader.h | 4 +- source/gui/scripting/ScriptFunctions.cpp | 2 +- source/ps/Game.cpp | 20 +- source/ps/GameSetup/GameSetup.cpp | 9 +- source/ps/World.cpp | 10 +- source/ps/World.h | 4 +- source/simulation2/Simulation2.cpp | 20 +- .../components/tests/test_Pathfinder.h | 2 +- source/simulation2/tests/test_Serializer.h | 11 +- .../CustomControls/MapDialog/MapDialog.cpp | 214 +++++++++++++++++ .../CustomControls/MapDialog/MapDialog.h | 55 +++++ .../AtlasUI/ScenarioEditor/ScenarioEditor.cpp | 105 +++------ .../ScenarioEditor/Sections/Map/Map.cpp | 28 ++- .../ScenarioEditor/Sections/Player/Player.cpp | 221 ++++++++++++------ .../GameInterface/Handlers/MapHandlers.cpp | 22 +- source/tools/atlas/GameInterface/Messages.h | 6 + 37 files changed, 997 insertions(+), 226 deletions(-) create mode 100644 binaries/data/mods/public/simulation/components/SkirmishReplacer.js create mode 100644 binaries/data/mods/public/simulation/components/interfaces/SkirmishReplacer.js create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml create mode 100644 binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml create mode 100644 source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp create mode 100644 source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.h diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js index 0d2c86358a..6ce3102757 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -107,8 +107,8 @@ function initMain() // Init map types var mapTypes = getGUIObjectByName("mapTypeSelection"); - mapTypes.list = ["Scenario","Random"]; - mapTypes.list_data = ["scenario","random"]; + mapTypes.list = ["Scenario","Skirmish","Random"]; + mapTypes.list_data = ["scenario","skirmish","random"]; // Setup map filters - will appear in order they are added addFilter("Default", function(settings) { return settings && !keywordTestOR(settings.Keywords, ["naval", "demo", "hidden"]); }); @@ -471,6 +471,7 @@ function initMapNameList() switch (g_GameAttributes.mapType) { case "scenario": + case "skirmish": mapFiles = getXMLFileList(g_GameAttributes.mapPath); break; @@ -487,7 +488,7 @@ function initMapNameList() var mapList = []; for (var i = 0; i < mapFiles.length; ++i) { - var file = mapFiles[i]; + var file = g_GameAttributes.mapPath + mapFiles[i]; var mapData = loadMapData(file); if (g_GameAttributes.mapFilter && mapData && testFilter(g_GameAttributes.mapFilter, mapData.settings)) @@ -521,22 +522,20 @@ function loadMapData(name) if (!name) return undefined; - if (name == "random") - { - g_MapData[name] = {settings : {"Name" : "Random", "Description" : "Randomly selects a map from the list"}}; - return g_MapData[name]; - } - if (!g_MapData[name]) { switch (g_GameAttributes.mapType) { case "scenario": - g_MapData[name] = Engine.LoadMapSettings(g_GameAttributes.mapPath+name); + case "skirmish": + g_MapData[name] = Engine.LoadMapSettings(name); break; case "random": - g_MapData[name] = parseJSONData(g_GameAttributes.mapPath+name+".json"); + if (name == "random") + g_MapData[name] = {settings : {"Name" : "Random", "Description" : "Randomly selects a map from the list"}}; + else + g_MapData[name] = parseJSONData(name+".json"); break; default: @@ -652,8 +651,17 @@ function selectMapType(type) case "scenario": // Set a default map // TODO: This should be remembered from the last session - g_GameAttributes.map = (g_IsNetworked ? DEFAULT_NETWORKED_MAP : DEFAULT_OFFLINE_MAP); g_GameAttributes.mapPath = "maps/scenarios/"; + g_GameAttributes.map = g_GameAttributes.mapPath + (g_IsNetworked ? DEFAULT_NETWORKED_MAP : DEFAULT_OFFLINE_MAP); + break; + + case "skirmish": + g_GameAttributes.mapPath = "maps/skirmishes/"; + g_GameAttributes.settings = { + PlayerData: g_DefaultPlayerData.slice(0, 4), + Seed: Math.floor(Math.random() * 65536), + CheatsEnabled: g_GameAttributes.settings.CheatsEnabled + }; break; case "random": @@ -799,8 +807,8 @@ function launchGame() var chosenName = civAINames[i]; else var chosenName = civAINames[Math.floor(Math.random() * civAINames.length)]; - for (var j = 0; j < numPlayers; ++j) - if (g_GameAttributes.settings.PlayerData[j].Name.indexOf(chosenName) !== -1) + for (var j = 0; j < numPlayers; ++j) + if (g_GameAttributes.settings.PlayerData[j].Name && g_GameAttributes.settings.PlayerData[j].Name.indexOf(chosenName) !== -1) usedName++; // Assign civ specific names to AI players @@ -899,12 +907,16 @@ function onGameAttributesChange() startingResources.selected = (STARTING_RESOURCES_DATA.indexOf(mapSettings.StartingResources) != -1 ? STARTING_RESOURCES_DATA.indexOf(mapSettings.StartingResources) : STARTING_RESOURCES_DEFAULTIDX); startingResourcesText.caption = STARTING_RESOURCES[startingResources.selected]; + // Update map preview + getGUIObjectByName("mapPreview").sprite = "cropped:(0.78125,0.5859375)session/icons/mappreview/" + getMapPreview(mapName); + // Handle map type specific logic switch (g_GameAttributes.mapType) { case "random": if (g_IsController) - { //Host + { + //Host numPlayersSelection.selected = numPlayers - 1; numPlayersSelection.hidden = false; mapSize.hidden = false; @@ -922,9 +934,6 @@ function onGameAttributesChange() populationCapText.hidden = true; startingResourcesText.hidden = true; - // Update map preview - getGUIObjectByName("mapPreview").sprite = "cropped:(0.78125,0.5859375)session/icons/mappreview/" + getMapPreview(mapName); - mapSizeText.caption = "Map size:"; mapSize.selected = sizeIdx; revealMapText.caption = "Reveal map:"; @@ -947,8 +956,6 @@ function onGameAttributesChange() populationCapText.hidden = false; startingResources.hidden = true; startingResourcesText.hidden = false; - // Update map preview - getGUIObjectByName("mapPreview").sprite = "cropped:(0.78125,0.5859375)session/icons/mappreview/" + getMapPreview(mapName); numPlayersText.caption = numPlayers; mapSizeText.caption = g_MapSizes.names[sizeIdx]; @@ -959,6 +966,57 @@ function onGameAttributesChange() break; + case "skirmish": + mapSizeText.caption = "Default"; + numPlayersText.caption = numPlayers; + numPlayersSelection.hidden = true; + mapSize.hidden = true; + if (g_IsController) + { + //Host + revealMap.hidden = false; + victoryCondition.hidden = false; + lockTeams.hidden = false; + populationCap.hidden = false; + startingResources.hidden = false; + + numPlayersText.hidden = false; + mapSizeText.hidden = false; + revealMapText.hidden = true; + victoryConditionText.hidden = true; + lockTeamsText.hidden = true; + populationCapText.hidden = true; + startingResourcesText.hidden = true; + + revealMapText.caption = "Reveal map:"; + revealMap.checked = (mapSettings.RevealMap ? true : false); + + victoryConditionText.caption = "Victory condition:"; + victoryCondition.selected = victoryIdx; + lockTeamsText.caption = "Teams locked:"; + lockTeams.checked = (mapSettings.LockTeams ? true : false); + } + else + { + // Client + numPlayersText.hidden = false; + mapSizeText.hidden = false; + revealMapText.hidden = false; + victoryConditionText.hidden = false; + lockTeamsText.hidden = false; + populationCap.hidden = true; + populationCapText.hidden = false; + startingResources.hidden = true; + startingResourcesText.hidden = false; + + revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No"); + victoryConditionText.caption = VICTORY_TEXT[victoryIdx]; + lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No"); + } + + break; + + case "scenario": // For scenario just reflect settings for the current map numPlayersSelection.hidden = true; @@ -975,9 +1033,7 @@ function onGameAttributesChange() populationCapText.hidden = false; startingResources.hidden = true; startingResourcesText.hidden = false; - - // Update map preview - getGUIObjectByName("mapPreview").sprite = "cropped:(0.78125,0.5859375)session/icons/mappreview/" + getMapPreview(mapName); + numPlayersText.caption = numPlayers; mapSizeText.caption = "Default"; revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No"); @@ -1046,7 +1102,7 @@ function onGameAttributesChange() pCivText.caption = g_CivData[civ].Name; pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-"; } - else if (g_GameAttributes.mapType == "random") + else if (g_GameAttributes.mapType != "scenario") { pCivText.hidden = true; pCiv.hidden = false; diff --git a/binaries/data/mods/public/gui/loading/loading.js b/binaries/data/mods/public/gui/loading/loading.js index 8ca31919af..182d86cacf 100644 --- a/binaries/data/mods/public/gui/loading/loading.js +++ b/binaries/data/mods/public/gui/loading/loading.js @@ -46,6 +46,7 @@ function init(data) var mapName = data.attribs.settings.Name; switch (data.attribs.mapType) { + case "skirmish": case "scenario": loadingMapName.caption = "Loading \"" + mapName + "\""; break; diff --git a/binaries/data/mods/public/simulation/components/SkirmishReplacer.js b/binaries/data/mods/public/simulation/components/SkirmishReplacer.js new file mode 100644 index 0000000000..8ea0732e8d --- /dev/null +++ b/binaries/data/mods/public/simulation/components/SkirmishReplacer.js @@ -0,0 +1,63 @@ +const civList = ["general", "athen", "brit", "cart", "celt", "gaul", "hele", "iber", "mace", "maur", "pers", "rome", "spart"]; + +function SkirmishReplacer() {} + + +SkirmishReplacer.prototype.Schema = ""; +for each (var civ in civList) + SkirmishReplacer.prototype.Schema += + "" + + "" + + "" + + "" + + ""; + + + +SkirmishReplacer.prototype.Init = function() +{ +}; + +SkirmishReplacer.prototype.OnOwnershipChanged = function(msg) +{ + if (msg.to == 0) + warn("Skirmish map elements can only be owned by regular players. Please delete entity "+this.entity+" or change the ownership to a non-gaia player."); +}; + +/** + * Replace this entity with a civ-specific entity on the first turn + */ +SkirmishReplacer.prototype.OnUpdate = function(msg) +{ + var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); + var civ = cmpPlayer.GetCiv(); + + var templateName = ""; + if (civ in this.template) + templateName = this.template[civ]; + else if ("general" in this.template) + templateName = this.template.general; + + if (!templateName || civ == "gaia") + { + Engine.DestroyEntity(this.entity); + return; + } + + templateName = templateName.replace(/\{civ\}/g, civ); + + var cmpCurPosition = Engine.QueryInterface(this.entity, IID_Position); + var replacement = Engine.AddEntity(templateName); + var cmpReplacementPosition = Engine.QueryInterface(replacement, IID_Position) + var pos = cmpCurPosition.GetPosition2D(); + cmpReplacementPosition.JumpTo(pos.x, pos.y); + var rot = cmpCurPosition.GetRotation(); + cmpReplacementPosition.SetYRotation(rot.y); + var cmpCurOwnership = Engine.QueryInterface(this.entity, IID_Ownership); + var cmpReplacementOwnership = Engine.QueryInterface(replacement, IID_Ownership); + cmpReplacementOwnership.SetOwner(cmpCurOwnership.GetOwner()); + + Engine.DestroyEntity(this.entity); +}; + +Engine.RegisterComponentType(IID_SkirmishReplacer, "SkirmishReplacer", SkirmishReplacer); diff --git a/binaries/data/mods/public/simulation/components/interfaces/SkirmishReplacer.js b/binaries/data/mods/public/simulation/components/interfaces/SkirmishReplacer.js new file mode 100644 index 0000000000..7a972823fe --- /dev/null +++ b/binaries/data/mods/public/simulation/components/interfaces/SkirmishReplacer.js @@ -0,0 +1 @@ +Engine.RegisterInterface("SkirmishReplacer"); diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml new file mode 100644 index 0000000000..513fcea81c --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml @@ -0,0 +1,12 @@ + + + + skirm + + + structures/{civ}_civil_centre + + + structures/athenians/civic_centre_new.xml + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml new file mode 100644 index 0000000000..03b31ed9bf --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml @@ -0,0 +1,27 @@ + + + + + 9.0 + + + skirm + + + + + + + + + + structures/{civ}_wall_gate + + + + structures/hellenes/wall_gate.xml + + + 38.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml new file mode 100644 index 0000000000..3010a88435 --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml @@ -0,0 +1,23 @@ + + + + + 9.0 + + + skirm + + + + + + structures/{civ}_wall_long + + + + structures/hellenes/wall_long.xml + + + 36.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml new file mode 100644 index 0000000000..260ab0d5e6 --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml @@ -0,0 +1,23 @@ + + + + + 9.0 + + + skirm + + + + + + structures/{civ}_wall_medium + + + + structures/hellenes/wall_medium.xml + + + 24.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml new file mode 100644 index 0000000000..828120b38c --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml @@ -0,0 +1,23 @@ + + + + + 9.0 + + + skirm + + + + + + structures/{civ}_wall_short + + + + structures/hellenes/wall_short.xml + + + 12.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml new file mode 100644 index 0000000000..caf4049bff --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml @@ -0,0 +1,23 @@ + + + + + 15.0 + + + skirm + + + + + + structures/{civ}_wall_tower + + + + structures/hellenes/wall_tower.xml + + + 10 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml new file mode 100644 index 0000000000..6465999b52 --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml @@ -0,0 +1,26 @@ + + + + + 9.0 + + + skirm + + + + + + + + + + structures/iber_wall_gate + + + structures/iberians/wall_gate.xml + + + 36.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml new file mode 100644 index 0000000000..97427264a2 --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml @@ -0,0 +1,22 @@ + + + + + 9.0 + + + skirm + + + + + + structures/iber_wall_long + + + structures/iberians/wall_long.xml + + + 36.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml new file mode 100644 index 0000000000..633b2353fc --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml @@ -0,0 +1,22 @@ + + + + + 9.0 + + + skirm + + + + + + structures/iber_wall_medium + + + structures/iberians/wall_medium.xml + + + 24.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml new file mode 100644 index 0000000000..f35cb8e31a --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml @@ -0,0 +1,22 @@ + + + + + 9.0 + + + skirm + + + + + + structures/iber_wall_short + + + structures/iberians/wall_short.xml + + + 12.0 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml new file mode 100644 index 0000000000..092fbc0f5b --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml @@ -0,0 +1,22 @@ + + + + + 15.0 + + + skirm + + + + + + structures/iber_wall_tower + + + structures/iberians/wall_tower.xml + + + 10 + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml b/binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml new file mode 100644 index 0000000000..db628bc6b7 --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml @@ -0,0 +1,19 @@ + + + + skirm + + + + units/{civ}_cavalry_javelinist_b + units/celt_cavalry_swordsman_b + units/gaul_cavalry_swordsman_b + units/hele_cavalry_swordsman_b + units/iber_cavalry_spearman_b + units/maur_support_elephant + units/rome_cavalry_spearman_b + + + units/athenians/cavalry_javelinist_b.xml + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml b/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml new file mode 100644 index 0000000000..a001d15b7d --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml @@ -0,0 +1,14 @@ + + + + skirm + + + + units/{civ}_infantry_spearman_b + units/rome_infantry_swordsman_b + + + units/athenians/infantry_spearman_b.xml + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml b/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml new file mode 100644 index 0000000000..eea0a03ea3 --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml @@ -0,0 +1,18 @@ + + + + skirm + + + + units/{civ}_infantry_javelinist_b + units/athen_infantry_slinger_b + units/brit_infantry_slinger_b + units/cart_infantry_archer_b + units/maur_infantry_archer_b + units/pers_infantry_archer_b + + + units/athenians/infantry_slinger_b.xml + + diff --git a/binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml b/binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml new file mode 100644 index 0000000000..060b0d23a1 --- /dev/null +++ b/binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml @@ -0,0 +1,12 @@ + + + + skirm + + + units/{civ}_support_female_citizen + + + units/athenians/female_citizen.xml + + diff --git a/build/premake/premake4.lua b/build/premake/premake4.lua index 0397111c2a..bba9714c99 100644 --- a/build/premake/premake4.lua +++ b/build/premake/premake4.lua @@ -951,6 +951,7 @@ function setup_atlas_projects() "CustomControls/EditableListCtrl", "CustomControls/FileHistory", "CustomControls/HighResTimer", + "CustomControls/MapDialog", "CustomControls/SnapSplitterWindow", "CustomControls/VirtualDirTreeCtrl", "CustomControls/Windows", diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 5230855faf..575e27b60d 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -64,7 +64,7 @@ CMapReader::CMapReader() } // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful -void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_, +void CMapReader::LoadMap(const VfsPath& pathname, const CScriptValRooted& settings, CTerrain *pTerrain_, WaterManager* pWaterMan_, SkyManager* pSkyMan_, CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_, CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities) @@ -83,6 +83,7 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_, m_PlayerID = playerID_; m_SkipEntities = skipEntities; m_StartingCameraTarget = INVALID_ENTITY; + m_ScriptSettings = settings; filename_xml = pathname.ChangeExtension(L".xml"); @@ -116,8 +117,11 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_, if (pPostproc) pPostproc->SetPostEffect(L"default"); - // load map settings script - RegMemFun(this, &CMapReader::LoadScriptSettings, L"CMapReader::LoadScriptSettings", 50); + // load map or script settings script + if (settings.undefined()) + RegMemFun(this, &CMapReader::LoadScriptSettings, L"CMapReader::LoadScriptSettings", 50); + else + RegMemFun(this, &CMapReader::LoadRMSettings, L"CMapReader::LoadRMSettings", 50); // load player settings script (must be done before reading map) RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50); @@ -138,7 +142,6 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_, RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5); } - // LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain *pTerrain_, WaterManager* pWaterMan_, SkyManager* pSkyMan_, @@ -1196,6 +1199,7 @@ int CMapReader::DelayLoadFinished() int CMapReader::LoadRMSettings() { // copy random map settings over to sim + ENSURE(pSimulation2); pSimulation2->SetMapSettings(m_ScriptSettings); return 0; diff --git a/source/graphics/MapReader.h b/source/graphics/MapReader.h index 169e08c0b2..3d61fc042f 100644 --- a/source/graphics/MapReader.h +++ b/source/graphics/MapReader.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2013 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -53,7 +53,7 @@ public: ~CMapReader(); // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful - void LoadMap(const VfsPath& pathname, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, + void LoadMap(const VfsPath& pathname, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc, CSimulation2*, const CSimContext*, int playerID, bool skipEntities); diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp index f98c9d22e2..6913c2b2ed 100644 --- a/source/gui/scripting/ScriptFunctions.cpp +++ b/source/gui/scripting/ScriptFunctions.cpp @@ -421,7 +421,7 @@ CScriptVal LoadMapSettings(void* cbdata, VfsPath pathname) CMapSummaryReader reader; - if (reader.LoadMap(pathname.ChangeExtension(L".xml")) != PSRETURN_OK) + if (reader.LoadMap(pathname) != PSRETURN_OK) return CScriptVal(); return reader.GetMapSettings(guiManager->GetScriptInterface()).get(); diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index 761d65ee0f..2bf2d6c5e9 100644 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -142,15 +142,7 @@ void CGame::RegisterInit(const CScriptValRooted& attribs, const std::string& sav if (m_GameView) m_GameView->RegisterInit(); - if (mapType == "scenario") - { - // Load scenario attributes - std::wstring mapFile; - m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "map", mapFile); - - m_World->RegisterInit(mapFile, m_PlayerID); - } - else if (mapType == "random") + if (mapType == "random") { // Load random map attributes std::wstring scriptFile; @@ -161,6 +153,16 @@ void CGame::RegisterInit(const CScriptValRooted& attribs, const std::string& sav m_World->RegisterInitRMS(scriptFile, settings, m_PlayerID); } + else + { + std::wstring mapFile; + m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "map", mapFile); + CScriptValRooted settings; + if (mapType == "skirmish") + m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "settings", settings); + + m_World->RegisterInit(mapFile, settings, m_PlayerID); + } if (m_IsSavedGame) RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000); diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index aaff10dfaa..b675343747 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -1081,6 +1081,7 @@ bool Autostart(const CmdLineArgs& args) * -autostart-ip=127.0.0.1 -- multiplayer connect to 127.0.0.1 * -autostart-random=104 -- random map, optional seed value = 104 (default is 0, random is -1) * -autostart-size=192 -- random map size in tiles = 192 (default is 192) + * -autostart-civ=1:hele -- set player #1 civ to "hele" * * Examples: * -autostart=Acropolis -autostart-host -autostart-players=2 -- Host game on Acropolis map, 2 players @@ -1131,8 +1132,7 @@ bool Autostart(const CmdLineArgs& args) } // Random map definition will be loaded from JSON file, so we need to parse it - std::wstring mapPath = L"maps/random/"; - std::wstring scriptPath = mapPath + autoStartName.FromUTF8() + L".json"; + std::wstring scriptPath = L"maps/random/" + autoStartName.FromUTF8() + L".json"; CScriptValRooted scriptData = scriptInterface.ReadJSONFile(scriptPath); if (!scriptData.undefined() && scriptInterface.GetProperty(scriptData.get(), "settings", settings)) { @@ -1157,7 +1157,6 @@ bool Autostart(const CmdLineArgs& args) } scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName)); - scriptInterface.SetProperty(attrs.get(), "mapPath", mapPath); scriptInterface.SetProperty(attrs.get(), "mapType", std::string("random")); scriptInterface.SetProperty(settings.get(), "Seed", seed); // Random seed scriptInterface.SetProperty(settings.get(), "Size", mapSize); // Random map size (in patches) @@ -1184,7 +1183,9 @@ bool Autostart(const CmdLineArgs& args) } else { - scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName)); + // TODO: support akirmish maps + std::string mapFile = "maps/scenarios/" + autoStartName; + scriptInterface.SetProperty(attrs.get(), "map", mapFile); scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario")); } diff --git a/source/ps/World.cpp b/source/ps/World.cpp index 0a9ee5f3b6..879825ddfa 100644 --- a/source/ps/World.cpp +++ b/source/ps/World.cpp @@ -65,19 +65,19 @@ CWorld::CWorld(CGame *pGame): /** * Initializes the game world with the attributes provided. **/ -void CWorld::RegisterInit(const CStrW& mapFile, int playerID) +void CWorld::RegisterInit(const CStrW& mapFile, const CScriptValRooted& settings, int playerID) { // Load the map, if one was specified if (mapFile.length()) { - VfsPath mapfilename(VfsPath("maps/scenarios") / (mapFile + L".pmp")); + VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp"); CMapReader* reader = 0; try { reader = new CMapReader; CTriggerManager* pTriggerManager = NULL; - reader->LoadMap(mapfilename, m_Terrain, + reader->LoadMap(mapfilename, settings, m_Terrain, CRenderer::IsInitialised() ? g_Renderer.GetWaterManager() : NULL, CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL, &g_LightEnv, m_pGame->GetView(), @@ -89,8 +89,8 @@ void CWorld::RegisterInit(const CStrW& mapFile, int playerID) catch (PSERROR_File& err) { delete reader; - LOGERROR(L"Failed to load scenario %ls: %hs", mapfilename.string().c_str(), err.what()); - throw PSERROR_Game_World_MapLoadFailed("Failed to load scenario.\nCheck application log for details."); + LOGERROR(L"Failed to load map %ls: %hs", mapfilename.string().c_str(), err.what()); + throw PSERROR_Game_World_MapLoadFailed("Failed to load map.\nCheck application log for details."); } } } diff --git a/source/ps/World.h b/source/ps/World.h index cfc129c20f..c37c16e317 100644 --- a/source/ps/World.h +++ b/source/ps/World.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2013 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -73,7 +73,7 @@ public: /* Initialize the World - load the map and all objects */ - void RegisterInit(const CStrW& mapFile, int playerID); + void RegisterInit(const CStrW& mapFile, const CScriptValRooted& settings, int playerID); /* Initialize the World - generate and load the random map diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp index ffc16f1f3b..bd93c08a69 100644 --- a/source/simulation2/Simulation2.cpp +++ b/source/simulation2/Simulation2.cpp @@ -387,22 +387,20 @@ void CSimulation2Impl::Update(int turnLength, const std::vectorLoadMap(mapfilename, &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure - } - else + if (mapType == "random") { // TODO: support random map scripts debug_warn(L"Serialization test mode only supports scenarios"); } + else + { + std::wstring mapFile; + m_ComponentManager.GetScriptInterface().GetProperty(m_InitAttributes.get(), "map", mapFile); + VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp"); + mapReader->LoadMap(mapfilename, CScriptValRooted(), &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure + } LDR_EndRegistering(); ENSURE(LDR_NonprogressiveLoad() == INFO::OK); diff --git a/source/simulation2/components/tests/test_Pathfinder.h b/source/simulation2/components/tests/test_Pathfinder.h index 74b33e627c..51a1b920ae 100644 --- a/source/simulation2/components/tests/test_Pathfinder.h +++ b/source/simulation2/components/tests/test_Pathfinder.h @@ -68,7 +68,7 @@ public: CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself LDR_BeginRegistering(); - mapReader->LoadMap(L"maps/scenarios/Median Oasis 01.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + mapReader->LoadMap(L"maps/scenarios/Median Oasis 01.pmp", CScriptValRooted(), &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, &sim2.GetSimContext(), -1, false); LDR_EndRegistering(); TS_ASSERT_OK(LDR_NonprogressiveLoad()); diff --git a/source/simulation2/tests/test_Serializer.h b/source/simulation2/tests/test_Serializer.h index 3784395947..942d5da733 100644 --- a/source/simulation2/tests/test_Serializer.h +++ b/source/simulation2/tests/test_Serializer.h @@ -25,6 +25,7 @@ #include "graphics/MapReader.h" #include "graphics/Terrain.h" +#include "graphics/TerrainTextureManager.h" #include "lib/timer.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" @@ -635,6 +636,12 @@ public: TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"public", VFS_MOUNT_MUST_EXIST)); TS_ASSERT_OK(g_VFS->Mount(L"cache/", DataDir()/"cache")); + // Need some stuff for terrain movement costs: + // (TODO: this ought to be independent of any graphics code) + tex_codec_register_all(); + new CTerrainTextureManager; + g_TexMan.LoadTerrainTextures(); + CTerrain terrain; CSimulation2 sim2(NULL, &terrain); @@ -644,7 +651,7 @@ public: CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself LDR_BeginRegistering(); - mapReader->LoadMap(L"maps/scenarios/Latium.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + mapReader->LoadMap(L"maps/scenarios/Acropolis 01.pmp", CScriptValRooted(), &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &sim2, &sim2.GetSimContext(), -1, false); LDR_EndRegistering(); TS_ASSERT_OK(LDR_NonprogressiveLoad()); @@ -677,6 +684,8 @@ public: debug_printf(L"# time = %f (%f/%d)\n", t/reps, t, (int)reps); // Shut down the world + delete &g_TexMan; + tex_codec_unregister_all(); g_VFS.reset(); CXeromyces::Terminate(); } diff --git a/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp b/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp new file mode 100644 index 0000000000..59f618c665 --- /dev/null +++ b/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp @@ -0,0 +1,214 @@ +/* Copyright (C) 2013 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "MapDialog.h" + +#include "GameInterface/MessagePasser.h" +#include "GameInterface/Messages.h" + +enum { + ID_MapDialogFilename = 1, + ID_MapDialogNotebook, + ID_ScenarioPage, + ID_SkirmishPage +}; + +static const wxString scenarioPath(L"maps/scenarios/"); +static const wxString skirmishPath(L"maps/skirmishes/"); + +MapDialog::MapDialog(wxWindow* parent, MapDialogType type) + : wxDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(600,400), wxCAPTION|wxRESIZE_BORDER|wxCLOSE_BOX|wxSYSTEM_MENU), m_Type(type) +{ + Freeze(); + + SetIcon(wxIcon(_T("ICON_ScenarioEditor"))); // load from atlas.rc + + if (m_Type == MAPDIALOG_OPEN) + SetTitle(_("Choose map to open")); + else // MAPDIALOG_SAVE + SetTitle(_("Choose map to save")); + + AtlasMessage::qGetMapList qry; + qry.Post(); + + wxSizer* sizer = new wxBoxSizer(wxVERTICAL); + + wxNotebook* notebook = new wxNotebook(this, ID_MapDialogNotebook); + { + wxPanel* page = new wxPanel(notebook, ID_ScenarioPage); + wxSizer* pageSizer = new wxBoxSizer(wxVERTICAL); + // TODO: Should display something nicer than raw VFS paths + wxListBox* listBox = new wxListBox(page, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE|wxLB_HSCROLL); + const std::vector& scenarioFilenames = *qry.scenarioFilenames; + for (size_t i = 0; i < scenarioFilenames.size(); ++i) + { + wxString name = scenarioFilenames[i].substr(scenarioPath.Length()); + listBox->Append(name, new wxStringClientData(scenarioFilenames[i])); + } + + pageSizer->Add(listBox, wxSizerFlags().Proportion(1).Expand().Align(wxBOTTOM)); + page->SetSizer(pageSizer); + notebook->AddPage(page, _("Scenarios")); + } + { + wxPanel* page = new wxPanel(notebook, ID_SkirmishPage); + wxSizer* pageSizer = new wxBoxSizer(wxVERTICAL); + // TODO: Should display something nicer than raw VFS paths + wxListBox* listBox = new wxListBox(page, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE|wxLB_HSCROLL); + const std::vector& skirmishFilenames = *qry.skirmishFilenames; + for (size_t i = 0; i < skirmishFilenames.size(); ++i) + { + wxString name = skirmishFilenames[i].substr(skirmishPath.Length()); + listBox->Append(name, new wxStringClientData(skirmishFilenames[i])); + } + + pageSizer->Add(listBox, wxSizerFlags().Proportion(1).Expand()); + page->SetSizer(pageSizer); + notebook->AddPage(page, _("Skirmishes")); + } + + notebook->SetSelection(0); + + sizer->Add(notebook, wxSizerFlags().Proportion(1).Expand()); + + sizer->AddSpacer(5); + + wxSizer* filenameSizer = new wxBoxSizer(wxHORIZONTAL); + filenameSizer->AddSpacer(10); + filenameSizer->Add(new wxStaticText(this, wxID_ANY, _("Map name: ")), wxSizerFlags().Align(wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL)); + wxTextCtrl* filename = new wxTextCtrl(this, ID_MapDialogFilename, wxEmptyString); + if (m_Type == MAPDIALOG_OPEN) + filename->Disable(); + filenameSizer->Add(filename, wxSizerFlags().Proportion(1).Expand()); + sizer->Add(filenameSizer, wxSizerFlags().Expand()); + + sizer->AddSpacer(20); + + wxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL); + if (m_Type == MAPDIALOG_OPEN) + buttonSizer->Add(new wxButton(this, wxID_OPEN, _("Open"))); + else // MAPDIALOG_SAVE + buttonSizer->Add(new wxButton(this, wxID_SAVE, _("Save"))); + buttonSizer->AddSpacer(5); + buttonSizer->Add(new wxButton(this, wxID_CANCEL, _("Cancel"))); + + sizer->Add(buttonSizer, wxSizerFlags().Align(wxALIGN_RIGHT).Border(wxRIGHT|wxBOTTOM, 10)); + + SetSizer(sizer); + + Layout(); + Thaw(); +} + +wxString MapDialog::GetFilename() const +{ + wxFileName filename(m_Filename, wxPATH_UNIX); + filename.SetExt(L"xml"); + if (m_SelectedPage == 0) + return scenarioPath + filename.GetFullPath(wxPATH_UNIX); + else if (m_SelectedPage == 1) + return skirmishPath + filename.GetFullPath(wxPATH_UNIX); + else + return wxEmptyString; +} + +void MapDialog::OnListBox(wxCommandEvent& evt) +{ + if (evt.GetInt() < 0) + m_Filename = wxEmptyString; + else + m_Filename = evt.GetString(); + + wxDynamicCast(FindWindow(ID_MapDialogFilename), wxTextCtrl)->ChangeValue(m_Filename); + + if (evt.GetEventType() == wxEVT_COMMAND_LISTBOX_DOUBLECLICKED) + { + if (m_Type == MAPDIALOG_OPEN) + OpenFile(); + else + SaveFile(); + } +} + +void MapDialog::OnCancel(wxCommandEvent& WXUNUSED(evt)) +{ + EndModal(wxID_CANCEL); +} + +void MapDialog::OnOpen(wxCommandEvent& WXUNUSED(evt)) +{ + OpenFile(); +} + +void MapDialog::OnSave(wxCommandEvent& WXUNUSED(evt)) +{ + SaveFile(); +} + +void MapDialog::OnFilename(wxCommandEvent& evt) +{ + m_Filename = evt.GetString(); +} + +void MapDialog::OnNotebookChanged(wxNotebookEvent& evt) +{ + m_SelectedPage = evt.GetSelection(); +} + +void MapDialog::OpenFile() +{ + wxString filename = GetFilename(); + if (filename.empty()) + return; + + AtlasMessage::qVFSFileExists qry(filename.wc_str()); + qry.Post(); + if (!qry.exists) + return; + + EndModal(wxID_OK); +} + +void MapDialog::SaveFile() +{ + wxString filename = GetFilename(); + if (filename.empty()) + return; + + // TODO: this test would work better outside the VFS + AtlasMessage::qVFSFileExists qry(filename.wc_str()); + qry.Post(); + if (qry.exists) + { + if (wxMessageBox(_("WARNING: '") + filename + _("' already exists, it may be overwritten. Continue?"), _("Overwrite map confirmation"), wxICON_EXCLAMATION | wxYES_NO) != wxYES) + return; + } + + EndModal(wxID_OK); +} + +BEGIN_EVENT_TABLE(MapDialog, wxDialog) + EVT_BUTTON (wxID_CANCEL, MapDialog::OnCancel) + EVT_BUTTON (wxID_OPEN, MapDialog::OnOpen) + EVT_BUTTON (wxID_SAVE, MapDialog::OnSave) + EVT_LISTBOX (wxID_ANY, MapDialog::OnListBox) + EVT_LISTBOX_DCLICK (wxID_ANY, MapDialog::OnListBox) + EVT_TEXT (ID_MapDialogFilename, MapDialog::OnFilename) + EVT_NOTEBOOK_PAGE_CHANGED (ID_MapDialogNotebook, MapDialog::OnNotebookChanged) +END_EVENT_TABLE() diff --git a/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.h b/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.h new file mode 100644 index 0000000000..8c9b293be9 --- /dev/null +++ b/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2013 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#ifndef INCLUDED_MAPDIALOG +#define INCLUDED_MAPDIALOG + +#include + +enum MapDialogType { MAPDIALOG_OPEN, MAPDIALOG_SAVE }; + +class MapDialog : public wxDialog +{ +public: + MapDialog(wxWindow* parent, MapDialogType type); + + /** + * Returns VFS path of selected map with .xml extension, else empty string + */ + wxString GetFilename() const; + +private: + + void OnCancel(wxCommandEvent& evt); + void OnOpen(wxCommandEvent& evt); + void OnSave(wxCommandEvent& evt); + void OnListBox(wxCommandEvent& evt); + void OnFilename(wxCommandEvent& evt); + void OnNotebookChanged(wxNotebookEvent& evt); + + void OpenFile(); + void SaveFile(); + + wxArrayString m_MapFilenames; + wxString m_Filename; + MapDialogType m_Type; + int m_SelectedPage; + + DECLARE_EVENT_TABLE(); +}; + +#endif // INCLUDED_MAPDIALOG diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index 5cdca8fc8e..57b4734e20 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -35,6 +35,7 @@ #include "CustomControls/Buttons/ToolButton.h" #include "CustomControls/Canvas/Canvas.h" #include "CustomControls/HighResTimer/HighResTimer.h" +#include "CustomControls/MapDialog/MapDialog.h" #include "GameInterface/MessagePasser.h" #include "GameInterface/Messages.h" @@ -521,7 +522,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac // Start with a blank map (so that the editor can assume there's always // a valid map loaded) - POST_MESSAGE(LoadMap, (_T("_default"))); + POST_MESSAGE(LoadMap, (_T("maps/scenarios/_default.xml"))); POST_MESSAGE(SimPlay, (0.f, false)); // Select the initial sidebar (after the map has loaded) @@ -626,56 +627,28 @@ void ScenarioEditor::OnRedo(wxCommandEvent&) void ScenarioEditor::OnNew(wxCommandEvent& WXUNUSED(event)) { if (wxMessageBox(_("Discard current map and start blank new map?"), _("New map"), wxOK|wxCANCEL|wxICON_QUESTION, this) == wxOK) - OpenFile(_T("_default"), _T("")); + OpenFile(_T(""), _T("maps/scenarios/_default.xml")); } bool ScenarioEditor::OpenFile(const wxString& name, const wxString& filename) { wxBusyInfo busy(_("Loading ") + name); wxBusyCursor busyc; - - wxString mapPath(_T("maps/scenarios/")); - wxFileName fullFilename(filename); - // For compatibility it's still possible to load xml maps but in that case, - // we want a blank filename so that we're forced to Save As - if (filename.IsEmpty() || fullFilename.GetExt().IsSameAs(_T("xml"), false)) - { - fullFilename = _T(""); - } - else - { // force extension to pmp - fullFilename.SetExt(_T("pmp")); - - // check if pmp exists - qVFSFileExists qry(std::wstring((mapPath + fullFilename.GetFullName()).wc_str())); - qry.Post(); - if (!qry.exists) - { - return false; - } - } - - // Every map requires an xml file, so check that it exists - wxFileName xmlName(name); - xmlName.SetExt(_T("xml")); - qVFSFileExists qry(std::wstring((mapPath + xmlName.GetFullName()).wc_str())); + AtlasMessage::qVFSFileExists qry(filename.wc_str()); qry.Post(); if (!qry.exists) - { return false; - } - + // Deactivate tools, so they don't carry forwards into the new CWorld // and crash. m_ToolManager.SetCurrentTool(_T("")); // TODO: clear the undo buffer, etc - // TODO: Work when the map is not in .../maps/scenarios/ - std::wstring map(name.wc_str()); + std::wstring map(filename.wc_str()); POST_MESSAGE(LoadMap, (map)); - SetOpenFilename(fullFilename.GetFullName()); + SetOpenFilename(name); { // Wait for it to load, while the wxBusyInfo is telling the user that we're doing that qPing qry; @@ -692,27 +665,12 @@ bool ScenarioEditor::OpenFile(const wxString& name, const wxString& filename) void ScenarioEditor::OnOpen(wxCommandEvent& WXUNUSED(event)) { - wxFileDialog dlg (NULL, wxFileSelectorPromptStr, - Datafile::GetDataDirectory() + _T("/mods/public/maps/scenarios"), m_OpenFilename, - _T("PMP files (*.pmp)|*.pmp|All files (*.*)|*.*"), - wxFD_OPEN); - // Set default filter - dlg.SetFilterIndex(0); - - wxString cwd = wxFileName::GetCwd(); - + MapDialog dlg (NULL, MAPDIALOG_OPEN); if (dlg.ShowModal() == wxID_OK) { - // TODO: Handle maps in subdirectories of maps/scenarios - wxFileName filename(dlg.GetFilename()); - if (!OpenFile(filename.GetName(), filename.GetFullName())) - { - wxLogError(_("Map '%ls' does not exist"), filename.GetName().c_str()); - } - - // paranoia - MSDN says OFN_NOCHANGEDIR (used when we don't give wxCHANGE_DIR) - // "is ineffective for GetOpenFileName", but it seems to work anyway - wxCHECK_RET(cwd == wxFileName::GetCwd(), _T("cwd changed")); + wxString filename = dlg.GetFilename(); + if (!OpenFile(filename, filename)) + wxLogError(_("Map '%ls' does not exist"), filename.c_str()); } // TODO: Make this a non-undoable command @@ -727,12 +685,10 @@ void ScenarioEditor::OnImportHeightmap(wxCommandEvent& WXUNUSED(event)) // Set default filter dlg.SetFilterIndex(0); - wxString cwd = wxFileName::GetCwd(); - if (dlg.ShowModal() != wxID_OK) return; - OpenFile(_T("_default"), _T("")); + OpenFile(_T(""), _T("maps/scenarios/_default.xml")); std::wstring image(dlg.GetPath().wc_str()); POST_MESSAGE(ImportHeightmap, (image)); @@ -744,8 +700,16 @@ void ScenarioEditor::OnMRUFile(wxCommandEvent& event) { wxString filename(m_FileHistory.GetHistoryFile(event.GetId() - wxID_FILE1)); + // Handle old MRU filenames + if (filename.Mid(0, 5) != _T("maps/")) + { + filename = L"maps/scenarios/" + filename; + m_FileHistory.RemoveFileFromHistory(event.GetId() - wxID_FILE1); + } + if (!OpenFile(filename, filename)) - { // Missing or invalid - warn and remove from MRU + { + // Missing or invalid - warn and remove from MRU wxLogError(_("Map '%ls' does not exist"), filename.c_str()); m_FileHistory.RemoveFileFromHistory(event.GetId() - wxID_FILE1); } @@ -767,7 +731,6 @@ void ScenarioEditor::OnSave(wxCommandEvent& event) // the preview units.) m_ToolManager.SetCurrentTool(_T("")); - // TODO: Handle maps in subdirectories of maps/scenarios std::wstring map(m_OpenFilename.wc_str()); POST_MESSAGE(SaveMap, (map)); @@ -779,35 +742,19 @@ void ScenarioEditor::OnSave(wxCommandEvent& event) void ScenarioEditor::OnSaveAs(wxCommandEvent& WXUNUSED(event)) { - wxFileDialog dlg (NULL, wxFileSelectorPromptStr, - Datafile::GetDataDirectory() + _T("/mods/public/maps/scenarios"), m_OpenFilename, - _T("PMP files (*.pmp)|*.pmp|All files (*.*)|*.*"), - wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - // Set default filter - dlg.SetFilterIndex(0); - + MapDialog dlg(NULL, MAPDIALOG_SAVE); if (dlg.ShowModal() == wxID_OK) { - // On wxMSW the extension is automatically set to pmp if that filter is selected - // but not on wxGTK or wxOSX. Set it explicitly since it's the only possible format. - wxFileName filename(dlg.GetFilename()); - filename.SetExt(_T("pmp")); - if (!filename.IsOk()) - { // Shouldn't happen - wxLogError(_("Invalid filename '%ls'"), filename.GetFullName().c_str()); - return; - } - - wxBusyInfo busy(_("Saving ") + filename.GetFullName()); + wxString filename(dlg.GetFilename()); + wxBusyInfo busy(_("Saving ") + filename); wxBusyCursor busyc; m_ToolManager.SetCurrentTool(_T("")); - // TODO: Handle maps in subdirectories of maps/scenarios - std::wstring map(filename.GetFullName().wc_str()); + std::wstring map(filename.wc_str()); POST_MESSAGE(SaveMap, (map)); - SetOpenFilename(filename.GetFullName()); + SetOpenFilename(filename); // Wait for it to finish saving qPing qry; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp index 6e79fa6f42..5e08cbf0e1 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -34,9 +34,10 @@ enum ID_MapDescription, ID_MapReveal, ID_MapType, + ID_MapPreview, ID_MapTeams, ID_MapKW_Demo, - ID_MapKW_Hidden, + ID_MapKW_Naval, ID_RandomScript, ID_RandomSize, ID_RandomSeed, @@ -109,6 +110,7 @@ private: BEGIN_EVENT_TABLE(MapSettingsControl, wxPanel) EVT_TEXT(ID_MapName, MapSettingsControl::OnEdit) EVT_TEXT(ID_MapDescription, MapSettingsControl::OnEdit) + EVT_TEXT(ID_MapPreview, MapSettingsControl::OnEdit) EVT_CHECKBOX(wxID_ANY, MapSettingsControl::OnEdit) EVT_CHOICE(wxID_ANY, MapSettingsControl::OnEdit) END_EVENT_TABLE(); @@ -147,6 +149,10 @@ void MapSettingsControl::CreateWidgets() 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()); gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Reveal map")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(new wxCheckBox(this, ID_MapReveal, wxEmptyString), _("If checked, players won't need to explore"))); @@ -161,12 +167,12 @@ void MapSettingsControl::CreateWidgets() sizer->AddSpacer(5); wxStaticBoxSizer* keywordsSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Keywords")); - wxFlexGridSizer* kwGridSizer = new wxFlexGridSizer(2, 5, 5); + wxFlexGridSizer* kwGridSizer = new wxFlexGridSizer(4, 5, 5); kwGridSizer->Add(new wxStaticText(this, wxID_ANY, _("Demo")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); kwGridSizer->Add(Tooltipped(new wxCheckBox(this, ID_MapKW_Demo, wxEmptyString), _("If checked, map will only be visible using filters in game setup"))); - kwGridSizer->Add(new wxStaticText(this, wxID_ANY, _("Hidden")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); - kwGridSizer->Add(Tooltipped(new wxCheckBox(this, ID_MapKW_Hidden, wxEmptyString), + kwGridSizer->Add(new wxStaticText(this, wxID_ANY, _("Naval")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + kwGridSizer->Add(Tooltipped(new wxCheckBox(this, ID_MapKW_Naval, wxEmptyString), _("If checked, map will only be visible using filters in game setup"))); keywordsSizer->Add(kwGridSizer); sizer->Add(keywordsSizer, wxSizerFlags().Expand()); @@ -188,6 +194,9 @@ void MapSettingsControl::ReadFromEngine() // map description wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->ChangeValue(wxString(m_MapSettings["Description"])); + // map preview + wxDynamicCast(FindWindow(ID_MapPreview), wxTextCtrl)->ChangeValue(wxString(m_MapSettings["Preview"])); + // reveal map wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->SetValue(wxString(m_MapSettings["RevealMap"]) == L"true"); @@ -207,7 +216,7 @@ void MapSettingsControl::ReadFromEngine() m_MapSettingsKeywords.insert(std::wstring(keyword)); wxDynamicCast(FindWindow(ID_MapKW_Demo), wxCheckBox)->SetValue(m_MapSettingsKeywords.count(L"demo") != 0); - wxDynamicCast(FindWindow(ID_MapKW_Hidden), wxCheckBox)->SetValue(m_MapSettingsKeywords.count(L"hidden") != 0); + wxDynamicCast(FindWindow(ID_MapKW_Naval), wxCheckBox)->SetValue(m_MapSettingsKeywords.count(L"naval") != 0); } } @@ -227,6 +236,9 @@ AtObj MapSettingsControl::UpdateSettingsObject() // map description m_MapSettings.set("Description", wxDynamicCast(FindWindow(ID_MapDescription), wxTextCtrl)->GetValue()); + // map preview + m_MapSettings.set("Preview", wxDynamicCast(FindWindow(ID_MapPreview), wxTextCtrl)->GetValue()); + // reveal map m_MapSettings.setBool("RevealMap", wxDynamicCast(FindWindow(ID_MapReveal), wxCheckBox)->GetValue()); @@ -240,10 +252,10 @@ AtObj MapSettingsControl::UpdateSettingsObject() else m_MapSettingsKeywords.erase(L"demo"); - if (wxDynamicCast(FindWindow(ID_MapKW_Hidden), wxCheckBox)->GetValue()) - m_MapSettingsKeywords.insert(L"hidden"); + if (wxDynamicCast(FindWindow(ID_MapKW_Naval), wxCheckBox)->GetValue()) + m_MapSettingsKeywords.insert(L"naval"); else - m_MapSettingsKeywords.erase(L"hidden"); + m_MapSettingsKeywords.erase(L"naval"); AtObj keywords; keywords.set("@array", L""); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp index c9e643b10b..1d771f1398 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Player/Player.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2013 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -35,8 +35,18 @@ enum ID_PlayerStone, ID_PlayerPop, ID_PlayerColour, - ID_PlayerHuman, - ID_PlayerAI, + + ID_DefaultName, + ID_DefaultCiv, + ID_DefaultColour, + ID_DefaultAI, + ID_DefaultFood, + ID_DefaultWood, + ID_DefaultMetal, + ID_DefaultStone, + ID_DefaultPop, + ID_DefaultTeam, + ID_CameraSet, ID_CameraView, ID_CameraClear @@ -54,6 +64,39 @@ static wxWindow* Tooltipped(wxWindow* window, const wxString& tip) ////////////////////////////////////////////////////////////////////////// +class DefaultCheckbox : public wxCheckBox +{ +public: + DefaultCheckbox(wxWindow* parent, wxWindowID id, wxWindow* control, bool initialValue = false) + : wxCheckBox(parent, id, wxEmptyString), m_Control(control) + { + SetValue(initialValue); + } + + virtual void SetValue(bool value) + { + m_Control->Enable(value); + wxCheckBox::SetValue(value); + } + + void OnChecked(wxCommandEvent& evt) + { + m_Control->Enable(evt.IsChecked()); + + evt.Skip(); + } + +private: + wxWindow* m_Control; + + DECLARE_EVENT_TABLE(); +}; + +BEGIN_EVENT_TABLE(DefaultCheckbox, wxCheckBox) + EVT_CHECKBOX(wxID_ANY, DefaultCheckbox::OnChecked) +END_EVENT_TABLE(); + + class PlayerNotebookPage : public wxPanel { @@ -73,25 +116,33 @@ public: ///////////////////////////////////////////////////////////////////////// // Player Info wxStaticBoxSizer* playerInfoSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Player info")); - wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5); - gridSizer->AddGrowableCol(1); - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxFlexGridSizer* gridSizer = new wxFlexGridSizer(3, 5, 5); + gridSizer->AddGrowableCol(2); + wxTextCtrl* nameCtrl = new wxTextCtrl(this, wxID_ANY); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultName, nameCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Name")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(nameCtrl, wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT)); m_Controls.name = nameCtrl; - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Civilisation")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxChoice* civChoice = new wxChoice(this, wxID_ANY); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultCiv, civChoice), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Civilisation")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(civChoice, wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT)); m_Controls.civ = civChoice; - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Colour")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxButton* colourButton = new wxButton(this, ID_PlayerColour); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultColour, colourButton), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Colour")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(colourButton, _("Set player colour")), wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT)); m_Controls.colour = colourButton; - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Default AI")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxChoice* aiChoice = new wxChoice(this, wxID_ANY); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultAI, aiChoice), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("AI")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(aiChoice, - _("Select default AI")), wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT)); + _("Select AI")), wxSizerFlags(1).Expand().Align(wxALIGN_RIGHT)); m_Controls.ai = aiChoice; playerInfoSizer->Add(gridSizer, wxSizerFlags(1).Expand()); @@ -102,30 +153,40 @@ public: ///////////////////////////////////////////////////////////////////////// // Resources wxStaticBoxSizer* resourceSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Resources")); - wxFlexGridSizer* gridSizer = new wxFlexGridSizer(2, 5, 5); - gridSizer->AddGrowableCol(1); - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Food")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxFlexGridSizer* gridSizer = new wxFlexGridSizer(3, 5, 5); + gridSizer->AddGrowableCol(2); + wxSpinCtrl* foodCtrl = new wxSpinCtrl(this, ID_PlayerFood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultFood, foodCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Food")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(foodCtrl, _("Initial value of food resource")), wxSizerFlags().Expand()); m_Controls.food = foodCtrl; - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Wood")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxSpinCtrl* woodCtrl = new wxSpinCtrl(this, ID_PlayerWood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultWood, woodCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Wood")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(woodCtrl, _("Initial value of wood resource")), wxSizerFlags().Expand()); m_Controls.wood = woodCtrl; - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Metal")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxSpinCtrl* metalCtrl = new wxSpinCtrl(this, ID_PlayerMetal, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultMetal, metalCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Metal")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(metalCtrl, _("Initial value of metal resource")), wxSizerFlags().Expand()); m_Controls.metal = metalCtrl; - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Stone")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxSpinCtrl* stoneCtrl = new wxSpinCtrl(this, ID_PlayerStone, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultStone, stoneCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Stone")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(stoneCtrl, _("Initial value of stone resource")), wxSizerFlags().Expand()); m_Controls.stone = stoneCtrl; - gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Pop limit")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + wxSpinCtrl* popCtrl = new wxSpinCtrl(this, ID_PlayerPop, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX); + gridSizer->Add(new DefaultCheckbox(this, ID_DefaultPop, popCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + gridSizer->Add(new wxStaticText(this, wxID_ANY, _("Pop limit")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); gridSizer->Add(Tooltipped(popCtrl, _("Population limit for this player")), wxSizerFlags().Expand()); m_Controls.pop = popCtrl; @@ -138,8 +199,11 @@ public: // Diplomacy wxStaticBoxSizer* diplomacySizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Diplomacy")); wxBoxSizer* boxSizer = new wxBoxSizer(wxHORIZONTAL); - boxSizer->Add(new wxStaticText(this, wxID_ANY, _("Team")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); wxChoice* teamCtrl = new wxChoice(this, wxID_ANY); + boxSizer->Add(new DefaultCheckbox(this, ID_DefaultTeam, teamCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL)); + boxSizer->AddSpacer(5); + boxSizer->Add(new wxStaticText(this, wxID_ANY, _("Team")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT)); + boxSizer->AddSpacer(5); teamCtrl->Append(_("None")); teamCtrl->Append(_T("1")); teamCtrl->Append(_T("2")); @@ -479,6 +543,7 @@ BEGIN_EVENT_TABLE(PlayerSettingsControl, wxPanel) EVT_BUTTON(ID_PlayerColour, PlayerSettingsControl::OnPlayerColour) EVT_BUTTON(ID_CameraSet, PlayerSettingsControl::OnEdit) EVT_BUTTON(ID_CameraClear, PlayerSettingsControl::OnEdit) + EVT_CHECKBOX(wxID_ANY, PlayerSettingsControl::OnEdit) EVT_CHOICE(wxID_ANY, PlayerSettingsControl::OnEdit) EVT_TEXT(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersText) EVT_TEXT(wxID_ANY, PlayerSettingsControl::OnEdit) @@ -596,7 +661,8 @@ void PlayerSettingsControl::ReadFromEngine() m_MapSettings = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qry.settings); } else - { // Use blank object, it will be created next + { + // Use blank object, it will be created next m_MapSettings = AtObj(); } @@ -607,9 +673,7 @@ void PlayerSettingsControl::ReadFromEngine() m_NumPlayers = MAX_NUM_PLAYERS; } else - { m_NumPlayers = player.count(); - } wxASSERT(m_NumPlayers <= MAX_NUM_PLAYERS && m_NumPlayers != 0); @@ -628,21 +692,24 @@ void PlayerSettingsControl::ReadFromEngine() for (size_t i = 0; i < MAX_NUM_PLAYERS; ++i) { - PlayerPageControls controls = m_PlayerControls[i]; + const PlayerPageControls& controls = m_PlayerControls[i]; // name wxString name(_("Unknown")); - if (player["Name"].defined()) + bool defined = player["Name"].defined(); + if (defined) name = wxString(player["Name"]); else if (playerDefs["Name"].defined()) name = wxString(playerDefs["Name"]); controls.name->SetValue(name); + wxDynamicCast(FindWindowById(ID_DefaultName, controls.page), DefaultCheckbox)->SetValue(defined); // civ wxChoice* choice = controls.civ; wxString civCode; - if (player["Civ"].defined()) + defined = player["Civ"].defined(); + if (defined) civCode = wxString(player["Civ"]); else civCode = wxString(playerDefs["Civ"]); @@ -656,31 +723,30 @@ void PlayerSettingsControl::ReadFromEngine() break; } } + wxDynamicCast(FindWindowById(ID_DefaultCiv, controls.page), DefaultCheckbox)->SetValue(defined); // colour wxColour colour; AtObj clrObj = *player["Colour"]; - if (clrObj.defined()) - { - colour = wxColor((*clrObj["r"]).getInt(), (*clrObj["g"]).getInt(), (*clrObj["b"]).getInt()); - } - else - { + defined = clrObj.defined(); + if (!defined) clrObj = *playerDefs["Colour"]; - colour = wxColor((*clrObj["r"]).getInt(), (*clrObj["g"]).getInt(), (*clrObj["b"]).getInt()); - } + colour = wxColor((*clrObj["r"]).getInt(), (*clrObj["g"]).getInt(), (*clrObj["b"]).getInt()); controls.colour->SetBackgroundColour(colour); + wxDynamicCast(FindWindowById(ID_DefaultColour, controls.page), DefaultCheckbox)->SetValue(defined); // player type wxString aiID; - if (player["AI"].defined()) + defined = player["AI"].defined(); + if (defined) aiID = wxString(player["AI"]); else aiID = wxString(playerDefs["AI"]); choice = controls.ai; if (!aiID.empty()) - { // AI + { + // AI for (size_t j = 0; j < choice->GetCount(); ++j) { wxStringClientData* str = dynamic_cast(choice->GetClientObject(j)); @@ -691,44 +757,55 @@ void PlayerSettingsControl::ReadFromEngine() } } } - else - { // Human + else // Human choice->SetSelection(0); - } + wxDynamicCast(FindWindowById(ID_DefaultAI, controls.page), DefaultCheckbox)->SetValue(defined); // resources AtObj resObj = *player["Resources"]; - if (resObj.defined() && resObj["food"].defined()) + defined = resObj.defined() && resObj["food"].defined(); + if (defined) controls.food->SetValue(wxString(resObj["food"])); else controls.food->SetValue(0); + wxDynamicCast(FindWindowById(ID_DefaultFood, controls.page), DefaultCheckbox)->SetValue(defined); - if (resObj.defined() && resObj["wood"].defined()) + defined = resObj.defined() && resObj["wood"].defined(); + if (defined) controls.wood->SetValue(wxString(resObj["wood"])); else controls.wood->SetValue(0); + wxDynamicCast(FindWindowById(ID_DefaultWood, controls.page), DefaultCheckbox)->SetValue(defined); - if (resObj.defined() && resObj["metal"].defined()) + defined = resObj.defined() && resObj["metal"].defined(); + if (defined) controls.metal->SetValue(wxString(resObj["metal"])); else controls.metal->SetValue(0); + wxDynamicCast(FindWindowById(ID_DefaultMetal, controls.page), DefaultCheckbox)->SetValue(defined); - if (resObj.defined() && resObj["stone"].defined()) + defined = resObj.defined() && resObj["stone"].defined(); + if (defined) controls.stone->SetValue(wxString(resObj["stone"])); else controls.stone->SetValue(0); + wxDynamicCast(FindWindowById(ID_DefaultStone, controls.page), DefaultCheckbox)->SetValue(defined); // population limit - if (player["PopulationLimit"].defined()) + defined = player["PopulationLimit"].defined(); + if (defined) controls.pop->SetValue(wxString(player["PopulationLimit"])); else controls.pop->SetValue(0); + wxDynamicCast(FindWindowById(ID_DefaultPop, controls.page), DefaultCheckbox)->SetValue(defined); // team - if (player["Team"].defined()) + defined = player["Team"].defined(); + if (defined) controls.team->SetSelection((*player["Team"]).getInt() + 1); else controls.team->SetSelection(0); + wxDynamicCast(FindWindowById(ID_DefaultTeam, controls.page), DefaultCheckbox)->SetValue(defined); // camera if (player["StartingCamera"].defined()) @@ -767,7 +844,6 @@ void PlayerSettingsControl::ReadFromEngine() AtObj PlayerSettingsControl::UpdateSettingsObject() { // Update player data in the map settings - AtIter oldPlayer = m_MapSettings["PlayerData"]["item"]; AtObj players; players.set("@array", L""); @@ -777,66 +853,67 @@ AtObj PlayerSettingsControl::UpdateSettingsObject() { PlayerPageControls controls = m_PlayerControls[i]; - AtObj player = *oldPlayer; + AtObj player; // name wxTextCtrl* text = controls.name; - if (!text->GetValue().empty()) - { + if (text->IsEnabled()) player.set("Name", text->GetValue()); - } // civ wxChoice* choice = controls.civ; - if (choice->GetSelection() >= 0) + if (choice->IsEnabled() && choice->GetSelection() >= 0) { wxStringClientData* str = dynamic_cast(choice->GetClientObject(choice->GetSelection())); player.set("Civ", str->GetData()); } // colour - wxColour colour = controls.colour->GetBackgroundColour(); - AtObj clrObj; - clrObj.setInt("r", (int)colour.Red()); - clrObj.setInt("g", (int)colour.Green()); - clrObj.setInt("b", (int)colour.Blue()); - player.set("Colour", clrObj); + if (controls.colour->IsEnabled()) + { + wxColour colour = controls.colour->GetBackgroundColour(); + AtObj clrObj; + clrObj.setInt("r", (int)colour.Red()); + clrObj.setInt("g", (int)colour.Green()); + clrObj.setInt("b", (int)colour.Blue()); + player.set("Colour", clrObj); + } // player type choice = controls.ai; - if (choice->GetSelection() > 0) - { // ai - get id - wxStringClientData* str = dynamic_cast(choice->GetClientObject(choice->GetSelection())); - player.set("AI", str->GetData()); - } - else - { // human - player.set("AI", _T("")); + if (choice->IsEnabled()) + { + if (choice->GetSelection() > 0) + { + // ai - get id + wxStringClientData* str = dynamic_cast(choice->GetClientObject(choice->GetSelection())); + player.set("AI", str->GetData()); + } + else // human + player.set("AI", _T("")); } // resources AtObj resObj; - if (controls.food->GetValue() > 0) + if (controls.food->IsEnabled()) resObj.setInt("food", controls.food->GetValue()); - if (controls.wood->GetValue() > 0) + if (controls.wood->IsEnabled()) resObj.setInt("wood", controls.wood->GetValue()); - if (controls.metal->GetValue() > 0) + if (controls.metal->IsEnabled()) resObj.setInt("metal", controls.metal->GetValue()); - if (controls.stone->GetValue() > 0) + if (controls.stone->IsEnabled()) resObj.setInt("stone", controls.stone->GetValue()); if (resObj.defined()) player.set("Resources", resObj); // population limit - if (controls.pop->GetValue() > 0) + if (controls.pop->IsEnabled()) player.setInt("PopulationLimit", controls.pop->GetValue()); // team choice = controls.team; - if (choice->GetSelection() >= 0) - { // valid selection + if (choice->IsEnabled() && choice->GetSelection() >= 0) player.setInt("Team", choice->GetSelection() - 1); - } // camera AtObj camObj; @@ -858,8 +935,6 @@ AtObj PlayerSettingsControl::UpdateSettingsObject() player.set("StartingCamera", camObj); players.add("item", player); - if (oldPlayer.defined()) - ++oldPlayer; } m_MapSettings.set("PlayerData", players); diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp index 71272d4bfa..26111335bc 100644 --- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -116,7 +116,7 @@ QUERYHANDLER(GenerateMap) CScriptValRooted atts; si.Eval("({})", atts); si.SetProperty(atts.get(), "mapType", std::string("scenario")); - si.SetProperty(atts.get(), "map", std::wstring(L"_default")); + si.SetProperty(atts.get(), "map", std::wstring(L"maps/scenarios/_default")); StartGame(atts); msg->status = -1; @@ -226,7 +226,7 @@ MESSAGEHANDLER(ImportHeightmap) MESSAGEHANDLER(SaveMap) { CMapWriter writer; - const VfsPath pathname = VfsPath("maps/scenarios") / *msg->filename; + VfsPath pathname = VfsPath(*msg->filename).ChangeExtension(L".pmp"); writer.SaveMap(pathname, g_Game->GetWorld()->GetTerrain(), g_Renderer.GetWaterManager(), g_Renderer.GetSkyManager(), @@ -352,4 +352,22 @@ QUERYHANDLER(VFSFileExists) msg->exists = VfsFileExists(*msg->path); } +static Status AddToFilenames(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData) +{ + std::vector& filenames = *(std::vector*)cbData; + filenames.push_back(pathname.string().c_str()); + return INFO::OK; +} + +QUERYHANDLER(GetMapList) +{ + std::vector scenarioFilenames; + vfs::ForEachFile(g_VFS, L"maps/scenarios/", AddToFilenames, (uintptr_t)&scenarioFilenames, L"*.xml", vfs::DIR_RECURSIVE); + msg->scenarioFilenames = scenarioFilenames; + + std::vector skirmishFilenames; + vfs::ForEachFile(g_VFS, L"maps/skirmishes/", AddToFilenames, (uintptr_t)&skirmishFilenames, L"*.xml", vfs::DIR_RECURSIVE); + msg->skirmishFilenames = skirmishFilenames; +} + } diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index cf3bb382d3..48ce2809ae 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -159,6 +159,12 @@ MESSAGE(SaveMap, ((std::wstring, filename)) ); +QUERY(GetMapList, + , + ((std::vector, scenarioFilenames)) + ((std::vector, skirmishFilenames)) + ); + QUERY(GetMapSettings, , ((std::string, settings))