Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 13937) +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 13938) @@ -1,1423 +1,1479 @@ //////////////////////////////////////////////////////////////////////////////////////////////// // Constants const DEFAULT_NETWORKED_MAP = "Acropolis 01"; const DEFAULT_OFFLINE_MAP = "Acropolis 01"; // TODO: Move these somewhere like simulation\data\game_types.json, Atlas needs them too const VICTORY_TEXT = ["Conquest", "None"]; const VICTORY_DATA = ["conquest", "endless"]; const VICTORY_DEFAULTIDX = 0; const POPULATION_CAP = ["50", "100", "150", "200", "250", "300", "Unlimited"]; const POPULATION_CAP_DATA = [50, 100, 150, 200, 250, 300, 10000]; const POPULATION_CAP_DEFAULTIDX = 5; const STARTING_RESOURCES = ["Very Low", "Low", "Medium", "High", "Very High", "Deathmatch"]; const STARTING_RESOURCES_DATA = [100, 300, 500, 1000, 3000, 50000]; const STARTING_RESOURCES_DEFAULTIDX = 1; // Max number of players for any map const MAX_PLAYERS = 8; //////////////////////////////////////////////////////////////////////////////////////////////// // Is this is a networked game, or offline var g_IsNetworked; // Is this user in control of game settings (i.e. is a network server, or offline player) var g_IsController; // Are we currently updating the GUI in response to network messages instead of user input // (and therefore shouldn't send further messages to the network) var g_IsInGuiUpdate; var g_PlayerAssignments = {}; // Default game setup attributes var g_DefaultPlayerData = []; var g_GameAttributes = { settings: {} }; var g_GameSpeeds = {}; var g_MapSizes = {}; var g_AIs = []; var g_ChatMessages = []; // Data caches var g_MapData = {}; var g_CivData = {}; var g_MapFilters = []; // Warn about the AI's nonexistent naval map support. var g_NavalWarning = "\n\n[font=\"serif-bold-12\"][color=\"orange\"]Warning:[/color][/font] \ The AI does not support naval maps and may cause severe performance issues. \ Naval maps are recommended to be played with human opponents only."; // To prevent the display locking up while we load the map metadata, // we'll start with a 'loading' message and switch to the main screen in the // tick handler var g_LoadingState = 0; // 0 = not started, 1 = loading, 2 = loaded //////////////////////////////////////////////////////////////////////////////////////////////// function init(attribs) { switch (attribs.type) { case "offline": g_IsNetworked = false; g_IsController = true; break; case "server": g_IsNetworked = true; g_IsController = true; break; case "client": g_IsNetworked = true; g_IsController = false; break; default: error("Unexpected 'type' in gamesetup init: "+attribs.type); } } // Called after the map data is loaded and cached function initMain() { // Load AI list and hide deprecated AIs g_AIs = Engine.GetAIs(); // Sort AIs by displayed name g_AIs.sort(function (a, b) { return a.data.name < b.data.name ? -1 : b.data.name < a.data.name ? +1 : 0; }); // Get default player data - remove gaia g_DefaultPlayerData = initPlayerDefaults(); g_DefaultPlayerData.shift(); for (var i = 0; i < g_DefaultPlayerData.length; i++) g_DefaultPlayerData[i].Civ = "random"; g_GameSpeeds = initGameSpeeds(); g_MapSizes = initMapSizes(); // Init civs initCivNameList(); // 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"]); }); addFilter("Naval Maps", function(settings) { return settings && keywordTestAND(settings.Keywords, ["naval"]); }); addFilter("Demo Maps", function(settings) { return settings && keywordTestAND(settings.Keywords, ["demo"]); }); addFilter("All Maps", function(settings) { return true; }); // Populate map filters dropdown var mapFilters = getGUIObjectByName("mapFilterSelection"); mapFilters.list = getFilters(); g_GameAttributes.mapFilter = "Default"; // Setup controls for host only if (g_IsController) { mapTypes.selected = 0; mapFilters.selected = 0; initMapNameList(); var numPlayersSelection = getGUIObjectByName("numPlayersSelection"); var players = []; for (var i = 1; i <= MAX_PLAYERS; ++i) players.push(i); numPlayersSelection.list = players; numPlayersSelection.list_data = players; numPlayersSelection.selected = MAX_PLAYERS - 1; var gameSpeed = getGUIObjectByName("gameSpeed"); gameSpeed.hidden = false; getGUIObjectByName("gameSpeedText").hidden = true; gameSpeed.list = g_GameSpeeds.names; gameSpeed.list_data = g_GameSpeeds.speeds; gameSpeed.onSelectionChange = function() { // Update attributes so other players can see change if (this.selected != -1) g_GameAttributes.gameSpeed = g_GameSpeeds.speeds[this.selected]; if (!g_IsInGuiUpdate) updateGameAttributes(); } gameSpeed.selected = g_GameSpeeds["default"]; var populationCaps = getGUIObjectByName("populationCap"); populationCaps.list = POPULATION_CAP; populationCaps.list_data = POPULATION_CAP_DATA; populationCaps.selected = POPULATION_CAP_DEFAULTIDX; populationCaps.onSelectionChange = function() { if (this.selected != -1) g_GameAttributes.settings.PopulationCap = POPULATION_CAP_DATA[this.selected]; if (!g_IsInGuiUpdate) updateGameAttributes(); } var startingResourcesL = getGUIObjectByName("startingResources"); startingResourcesL.list = STARTING_RESOURCES; startingResourcesL.list_data = STARTING_RESOURCES_DATA; startingResourcesL.selected = STARTING_RESOURCES_DEFAULTIDX; startingResourcesL.onSelectionChange = function() { if (this.selected != -1) g_GameAttributes.settings.StartingResources = STARTING_RESOURCES_DATA[this.selected]; if (!g_IsInGuiUpdate) updateGameAttributes(); } var victoryConditions = getGUIObjectByName("victoryCondition"); victoryConditions.list = VICTORY_TEXT; victoryConditions.list_data = VICTORY_DATA; victoryConditions.onSelectionChange = function() { // Update attributes so other players can see change if (this.selected != -1) g_GameAttributes.settings.GameType = VICTORY_DATA[this.selected]; if (!g_IsInGuiUpdate) updateGameAttributes(); }; victoryConditions.selected = VICTORY_DEFAULTIDX; var mapSize = getGUIObjectByName("mapSize"); mapSize.list = g_MapSizes.names; mapSize.list_data = g_MapSizes.tiles; mapSize.onSelectionChange = function() { // Update attributes so other players can see change if (this.selected != -1) g_GameAttributes.settings.Size = g_MapSizes.tiles[this.selected]; if (!g_IsInGuiUpdate) updateGameAttributes(); }; getGUIObjectByName("revealMap").onPress = function() { // Update attributes so other players can see change g_GameAttributes.settings.RevealMap = this.checked; if (!g_IsInGuiUpdate) updateGameAttributes(); }; getGUIObjectByName("lockTeams").onPress = function() { // Update attributes so other players can see change g_GameAttributes.settings.LockTeams = this.checked; if (!g_IsInGuiUpdate) updateGameAttributes(); }; getGUIObjectByName("enableCheats").onPress = function() { // Update attributes so other players can see change g_GameAttributes.settings.CheatsEnabled = this.checked; if (!g_IsInGuiUpdate) updateGameAttributes(); }; } else { // If we're a network client, disable all the map controls // TODO: make them look visually disabled so it's obvious why they don't work getGUIObjectByName("mapTypeSelection").hidden = true; getGUIObjectByName("mapTypeText").hidden = false; getGUIObjectByName("mapFilterSelection").hidden = true; getGUIObjectByName("mapFilterText").hidden = false; getGUIObjectByName("mapSelectionText").hidden = false; getGUIObjectByName("mapSelection").hidden = true; getGUIObjectByName("victoryConditionText").hidden = false; getGUIObjectByName("victoryCondition").hidden = true; getGUIObjectByName("gameSpeedText").hidden = false; getGUIObjectByName("gameSpeed").hidden = true; // Disable player and game options controls // TODO: Shouldn't players be able to choose their own assignment? for (var i = 0; i < MAX_PLAYERS; ++i) { getGUIObjectByName("playerAssignment["+i+"]").enabled = false; getGUIObjectByName("playerCiv["+i+"]").hidden = true; getGUIObjectByName("playerTeam["+i+"]").hidden = true; } getGUIObjectByName("numPlayersSelection").hidden = true; } // Set up multiplayer/singleplayer bits: if (!g_IsNetworked) { getGUIObjectByName("chatPanel").hidden = true; getGUIObjectByName("enableCheats").checked = true; g_GameAttributes.settings.CheatsEnabled = true; } else { getGUIObjectByName("enableCheatsDesc").hidden = false; getGUIObjectByName("enableCheats").checked = false; g_GameAttributes.settings.CheatsEnabled = false; if (g_IsController) getGUIObjectByName("enableCheats").hidden = false; else getGUIObjectByName("enableCheatsText").hidden = false; } // Settings for all possible player slots var boxSpacing = 32; for (var i = 0; i < MAX_PLAYERS; ++i) { // Space player boxes var box = getGUIObjectByName("playerBox["+i+"]"); var boxSize = box.size; var h = boxSize.bottom - boxSize.top; boxSize.top = i * boxSpacing; boxSize.bottom = i * boxSpacing + h; box.size = boxSize; // Populate team dropdowns var team = getGUIObjectByName("playerTeam["+i+"]"); team.list = ["None", "1", "2", "3", "4"]; team.list_data = [-1, 0, 1, 2, 3]; team.selected = 0; let playerSlot = i; // declare for inner function use team.onSelectionChange = function() { // Update team if (this.selected != -1) g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1; if (!g_IsInGuiUpdate) updateGameAttributes(); }; // Set events var civ = getGUIObjectByName("playerCiv["+i+"]"); civ.onSelectionChange = function() { // Update civ if ((this.selected != -1)&&(g_GameAttributes.mapType !== "scenario")) g_GameAttributes.settings.PlayerData[playerSlot].Civ = this.list_data[this.selected]; if (!g_IsInGuiUpdate) updateGameAttributes(); }; } if (g_IsNetworked) { // For multiplayer, focus the chat input box by default getGUIObjectByName("chatInput").focus(); } else { // For single-player, focus the map list by default, // to allow easy keyboard selection of maps getGUIObjectByName("mapSelection").focus(); } } function handleNetMessage(message) { log("Net message: "+uneval(message)); switch (message.type) { case "netstatus": switch (message.status) { case "disconnected": Engine.DisconnectNetworkGame(); Engine.SwitchGuiPage("page_pregame.xml"); reportDisconnect(message.reason); break; default: error("Unrecognised netstatus type "+message.status); break; } break; case "gamesetup": if (message.data) // (the host gets undefined data on first connect, so skip that) g_GameAttributes = message.data; onGameAttributesChange(); break; case "players": // Find and report all joinings/leavings for (var host in message.hosts) if (! g_PlayerAssignments[host]) addChatMessage({ "type": "connect", "username": message.hosts[host].name }); for (var host in g_PlayerAssignments) if (! message.hosts[host]) addChatMessage({ "type": "disconnect", "guid": host }); // Update the player list g_PlayerAssignments = message.hosts; updatePlayerList(); break; case "start": Engine.SwitchGuiPage("page_loading.xml", { "attribs": g_GameAttributes, "isNetworked" : g_IsNetworked, "playerAssignments": g_PlayerAssignments }); break; case "chat": addChatMessage({ "type": "message", "guid": message.guid, "text": message.text }); break; default: error("Unrecognised net message type "+message.type); } } // Get display name from map data function getMapDisplayName(map) { var mapData = loadMapData(map); if (!mapData || !mapData.settings || !mapData.settings.Name) { // Give some msg that map format is unsupported log("Map data missing in scenario '"+map+"' - likely unsupported format"); return map; } return mapData.settings.Name; } // Get display name from map data function getMapPreview(map) { var mapData = loadMapData(map); if (!mapData || !mapData.settings || !mapData.settings.Preview) { // Give some msg that map format is unsupported return "nopreview.png"; } return mapData.settings.Preview; } // Get a setting if it exists or return default function getSetting(settings, defaults, property) { if (settings && (property in settings)) return settings[property]; // Use defaults if (defaults && (property in defaults)) return defaults[property]; return undefined; } // Initialize the dropdowns containing all the available civs function initCivNameList() { // Cache civ data g_CivData = loadCivData(); // Extract name/code, and skip civs that are explicitly disabled // (intended for unusable incomplete civs) var civList = [ { "name": civ.Name, "code": civ.Code } for each (civ in g_CivData) if (civ.SelectableInGameSetup !== false) ]; // Alphabetically sort the list, ignoring case civList.sort(sortNameIgnoreCase); var civListNames = [ civ.name for each (civ in civList) ]; var civListCodes = [ civ.code for each (civ in civList) ]; // Add random civ to beginning of list civListNames.unshift("[color=\"orange\"]Random"); civListCodes.unshift("random"); // Update the dropdowns for (var i = 0; i < MAX_PLAYERS; ++i) { var civ = getGUIObjectByName("playerCiv["+i+"]"); civ.list = civListNames; civ.list_data = civListCodes; civ.selected = 0; } } // Initialise the list control containing all the available maps function initMapNameList() { // Get a list of map filenames // TODO: Should verify these are valid maps before adding to list var mapSelectionBox = getGUIObjectByName("mapSelection") var mapFiles; switch (g_GameAttributes.mapType) { case "scenario": + case "skirmish": mapFiles = getXMLFileList(g_GameAttributes.mapPath); break; case "random": mapFiles = getJSONFileList(g_GameAttributes.mapPath); break; default: error("initMapNameList: Unexpected map type '"+g_GameAttributes.mapType+"'"); return; } // Apply map filter, if any defined 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)) mapList.push({ "name": getMapDisplayName(file), "file": file }); } // Alphabetically sort the list, ignoring case mapList.sort(sortNameIgnoreCase); if (g_GameAttributes.mapType == "random") mapList.unshift({ "name": "[color=\"orange\"]Random[/color]", "file": "random" }); var mapListNames = [ map.name for each (map in mapList) ]; var mapListFiles = [ map.file for each (map in mapList) ]; // Select the default map var selected = mapListFiles.indexOf(g_GameAttributes.map); // Default to the first element if list is not empty and we can't find the one we searched for if (selected == -1 && mapList.length) { selected = 0; } // Update the list control mapSelectionBox.list = mapListNames; mapSelectionBox.list_data = mapListFiles; mapSelectionBox.selected = selected; } 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: error("loadMapData: Unexpected map type '"+g_GameAttributes.mapType+"'"); return undefined; } } return g_MapData[name]; } //////////////////////////////////////////////////////////////////////////////////////////////// // GUI event handlers function cancelSetup() { Engine.DisconnectNetworkGame(); } function onTick() { // First tick happens before first render, so don't load yet if (g_LoadingState == 0) { g_LoadingState++; } else if (g_LoadingState == 1) { getGUIObjectByName("loadingWindow").hidden = true; getGUIObjectByName("setupWindow").hidden = false; initMain(); g_LoadingState++; } else if (g_LoadingState == 2) { while (true) { var message = Engine.PollNetworkClient(); if (!message) break; handleNetMessage(message); } } } // Called when user selects number of players function selectNumPlayers(num) { // Avoid recursion if (g_IsInGuiUpdate) return; // Network clients can't change number of players if (g_IsNetworked && !g_IsController) return; // Only meaningful for random maps if (g_GameAttributes.mapType != "random") return; // Update player data var pData = g_GameAttributes.settings.PlayerData; if (pData && num < pData.length) { // Remove extra player data g_GameAttributes.settings.PlayerData = pData.slice(0, num); } else { // Add player data from defaults for (var i = pData.length; i < num; ++i) g_GameAttributes.settings.PlayerData.push(g_DefaultPlayerData[i]); } // Some players may have lost their assigned slot for (var guid in g_PlayerAssignments) { var player = g_PlayerAssignments[guid].player; if (player > num) { if (g_IsNetworked) Engine.AssignNetworkPlayer(player, ""); else g_PlayerAssignments = { "local": { "name": "You", "player": 1, "civ": "", "team": -1} }; } } updateGameAttributes(); } // Called when the user selects a map type from the list function selectMapType(type) { // Avoid recursion if (g_IsInGuiUpdate) return; // Network clients can't change map type if (g_IsNetworked && !g_IsController) return; // Reset game attributes g_GameAttributes.map = ""; g_GameAttributes.mapType = type; // Clear old map data g_MapData = {}; // Select correct path switch (g_GameAttributes.mapType) { 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": g_GameAttributes.mapPath = "maps/random/"; g_GameAttributes.settings = { PlayerData: g_DefaultPlayerData.slice(0, 4), Seed: Math.floor(Math.random() * 65536), CheatsEnabled: g_GameAttributes.settings.CheatsEnabled }; break; default: error("selectMapType: Unexpected map type '"+g_GameAttributes.mapType+"'"); return; } initMapNameList(); updateGameAttributes(); } function selectMapFilter(filterName) { // Avoid recursion if (g_IsInGuiUpdate) return; // Network clients can't change map filter if (g_IsNetworked && !g_IsController) return; g_GameAttributes.mapFilter = filterName; initMapNameList(); updateGameAttributes(); } // Called when the user selects a map from the list function selectMap(name) { // Avoid recursion if (g_IsInGuiUpdate) return; // Network clients can't change map if (g_IsNetworked && !g_IsController) return; // Return if we have no map if (!name) return; var mapData = loadMapData(name); var mapSettings = (mapData && mapData.settings ? deepcopy(mapData.settings) : {}); // Copy any new settings g_GameAttributes.map = name; g_GameAttributes.script = mapSettings.Script; if (mapData !== "Random") for (var prop in mapSettings) g_GameAttributes.settings[prop] = mapSettings[prop]; // Use default AI if the map doesn't specify any explicitly for (var i = 0; i < g_GameAttributes.settings.PlayerData.length; ++i) { if (!('AI' in g_GameAttributes.settings.PlayerData[i])) g_GameAttributes.settings.PlayerData[i].AI = g_DefaultPlayerData[i].AI; if (!('AIDiff' in g_GameAttributes.settings.PlayerData[i])) g_GameAttributes.settings.PlayerData[i].AIDiff = g_DefaultPlayerData[i].AIDiff; } // Reset player assignments on map change if (!g_IsNetworked) { // Slot 1 g_PlayerAssignments = { "local": { "name": "You", "player": 1, "civ": "", "team": -1} }; } else { var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : g_GameAttributes.settings.PlayerData.length); for (var guid in g_PlayerAssignments) { // Unassign extra players var player = g_PlayerAssignments[guid].player; if (player <= MAX_PLAYERS && player > numPlayers) Engine.AssignNetworkPlayer(player, ""); } } updateGameAttributes(); } function launchGame() { if (g_IsNetworked && !g_IsController) { error("Only host can start game"); return; } // Check that we have a map if (!g_GameAttributes.map) return; if (g_GameAttributes.map == "random") selectMap(getGUIObjectByName("mapSelection").list_data[Math.floor(Math.random() * (getGUIObjectByName("mapSelection").list.length - 1)) + 1]); g_GameAttributes.settings.mapType = g_GameAttributes.mapType; var numPlayers = g_GameAttributes.settings.PlayerData.length; // Assign random civilizations to players with that choice // (this is synchronized because we're the host) var cultures = []; for each (var civ in g_CivData) if (civ.Culture !== undefined && cultures.indexOf(civ.Culture) < 0 && (civ.SelectableInGameSetup === undefined || civ.SelectableInGameSetup)) cultures.push(civ.Culture); var allcivs = new Array(cultures.length); for (var i = 0; i < allcivs.length; ++i) allcivs[i] = []; for each (var civ in g_CivData) if (civ.Culture !== undefined && (civ.SelectableInGameSetup === undefined || civ.SelectableInGameSetup)) allcivs[cultures.indexOf(civ.Culture)].push(civ.Code); const romanNumbers = [undefined, "I", "II", "III", "IV", "V", "VI", "VII", "VIII"]; for (var i = 0; i < numPlayers; ++i) { civs = allcivs[Math.floor(Math.random()*allcivs.length)]; if (g_GameAttributes.settings.PlayerData[i].Civ == "random") g_GameAttributes.settings.PlayerData[i].Civ = civs[Math.floor(Math.random()*civs.length)]; // Setting names for AI players. Check if the player is AI and the match is not a scenario if (g_GameAttributes.mapType !== "scenario" && g_GameAttributes.settings.PlayerData[i].AI) { // Get the civ specific names if (g_CivData[g_GameAttributes.settings.PlayerData[i].Civ].AINames !== undefined) var civAINames = shuffleArray(g_CivData[g_GameAttributes.settings.PlayerData[i].Civ].AINames); else var civAINames = [g_CivData[g_GameAttributes.settings.PlayerData[i].Civ].Name]; // Choose the name var usedName = 0; if (i < civAINames.length) 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 if (usedName) g_GameAttributes.settings.PlayerData[i].Name = chosenName + " " + romanNumbers[usedName+1]; else g_GameAttributes.settings.PlayerData[i].Name = chosenName; } } if (g_IsNetworked) { Engine.SetNetworkGameAttributes(g_GameAttributes); Engine.StartNetworkGame(); } else { // Find the player ID which the user has been assigned to var numPlayers = g_GameAttributes.settings.PlayerData.length; var playerID = -1; for (var i = 0; i < numPlayers; ++i) { var assignBox = getGUIObjectByName("playerAssignment["+i+"]"); if (assignBox.list_data[assignBox.selected] == "local") playerID = i+1; } // Remove extra player data g_GameAttributes.settings.PlayerData = g_GameAttributes.settings.PlayerData.slice(0, numPlayers); Engine.StartGame(g_GameAttributes, playerID); Engine.SwitchGuiPage("page_loading.xml", { "attribs": g_GameAttributes, "isNetworked" : g_IsNetworked, "playerAssignments": g_PlayerAssignments }); } } //////////////////////////////////////////////////////////////////////////////////////////////// function onGameAttributesChange() { g_IsInGuiUpdate = true; // Don't set any attributes here, just show the changes in GUI var mapName = g_GameAttributes.map || ""; var mapSettings = g_GameAttributes.settings; var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : MAX_PLAYERS); // Update some controls for clients if (!g_IsController) { getGUIObjectByName("mapFilterText").caption = g_GameAttributes.mapFilter; var mapTypeSelection = getGUIObjectByName("mapTypeSelection"); var idx = mapTypeSelection.list_data.indexOf(g_GameAttributes.mapType); getGUIObjectByName("mapTypeText").caption = mapTypeSelection.list[idx]; var mapSelectionBox = getGUIObjectByName("mapSelection"); mapSelectionBox.selected = mapSelectionBox.list_data.indexOf(mapName); getGUIObjectByName("mapSelectionText").caption = getMapDisplayName(mapName); var populationCapBox = getGUIObjectByName("populationCap"); populationCapBox.selected = populationCapBox.list_data.indexOf(mapSettings.PopulationCap); var startingResourcesBox = getGUIObjectByName("startingResources"); startingResourcesBox.selected = startingResourcesBox.list_data.indexOf(mapSettings.StartingResources); initMapNameList(); } // Controls common to all map types var numPlayersSelection = getGUIObjectByName("numPlayersSelection"); var revealMap = getGUIObjectByName("revealMap"); var victoryCondition = getGUIObjectByName("victoryCondition"); var lockTeams = getGUIObjectByName("lockTeams"); var mapSize = getGUIObjectByName("mapSize"); var enableCheats = getGUIObjectByName("enableCheats"); var populationCap = getGUIObjectByName("populationCap"); var startingResources = getGUIObjectByName("startingResources"); var numPlayersText= getGUIObjectByName("numPlayersText"); var mapSizeText = getGUIObjectByName("mapSizeText"); var revealMapText = getGUIObjectByName("revealMapText"); var victoryConditionText = getGUIObjectByName("victoryConditionText"); var lockTeamsText = getGUIObjectByName("lockTeamsText"); var enableCheatsText = getGUIObjectByName("enableCheatsText"); var populationCapText = getGUIObjectByName("populationCapText"); var startingResourcesText = getGUIObjectByName("startingResourcesText"); var gameSpeedText = getGUIObjectByName("gameSpeedText"); var sizeIdx = (g_MapSizes.tiles.indexOf(mapSettings.Size) != -1 ? g_MapSizes.tiles.indexOf(mapSettings.Size) : g_MapSizes["default"]); var speedIdx = (g_GameAttributes.gameSpeed !== undefined && g_GameSpeeds.speeds.indexOf(g_GameAttributes.gameSpeed) != -1) ? g_GameSpeeds.speeds.indexOf(g_GameAttributes.gameSpeed) : g_GameSpeeds["default"]; var victoryIdx = (VICTORY_DATA.indexOf(mapSettings.GameType) != -1 ? VICTORY_DATA.indexOf(mapSettings.GameType) : VICTORY_DEFAULTIDX); enableCheats.checked = (g_GameAttributes.settings.CheatsEnabled === undefined || !g_GameAttributes.settings.CheatsEnabled ? false : true) enableCheatsText.caption = (enableCheats.checked ? "Yes" : "No"); gameSpeedText.caption = g_GameSpeeds.names[speedIdx]; populationCap.selected = (POPULATION_CAP_DATA.indexOf(mapSettings.PopulationCap) != -1 ? POPULATION_CAP_DATA.indexOf(mapSettings.PopulationCap) : POPULATION_CAP_DEFAULTIDX); populationCapText.caption = POPULATION_CAP[populationCap.selected]; 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; revealMap.hidden = false; victoryCondition.hidden = false; lockTeams.hidden = false; populationCap.hidden = false; startingResources.hidden = false; numPlayersText.hidden = true; mapSizeText.hidden = true; revealMapText.hidden = true; victoryConditionText.hidden = true; lockTeamsText.hidden = true; 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:"; 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; - // 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]; revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No"); victoryConditionText.caption = VICTORY_TEXT[victoryIdx]; lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No"); } 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; mapSize.hidden = true; revealMap.hidden = true; victoryCondition.hidden = true; lockTeams.hidden = true; 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; - - // 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"); victoryConditionText.caption = VICTORY_TEXT[victoryIdx]; lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No"); getGUIObjectByName("populationCap").selected = POPULATION_CAP_DEFAULTIDX; break; default: error("onGameAttributesChange: Unexpected map type '"+g_GameAttributes.mapType+"'"); return; } // Display map name getGUIObjectByName("mapInfoName").caption = getMapDisplayName(mapName); // Load the description from the map file, if there is one var description = mapSettings.Description || "Sorry, no description available."; if (g_GameAttributes.mapFilter == "Naval Maps") description += g_NavalWarning; // Describe the number of players var playerString = numPlayers + " " + (numPlayers == 1 ? "player" : "players") + ". "; for (var i = 0; i < MAX_PLAYERS; ++i) { // Show only needed player slots getGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers); // Show player data or defaults as necessary if (i >= numPlayers) continue; var pName = getGUIObjectByName("playerName["+i+"]"); var pCiv = getGUIObjectByName("playerCiv["+i+"]"); var pCivText = getGUIObjectByName("playerCivText["+i+"]"); var pTeam = getGUIObjectByName("playerTeam["+i+"]"); var pTeamText = getGUIObjectByName("playerTeamText["+i+"]"); var pColor = getGUIObjectByName("playerColour["+i+"]"); // Player data / defaults var pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {}; var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {}; // Common to all game types var color = iColorToString(getSetting(pData, pDefs, "Colour")); pColor.sprite = "colour:"+color+" 100"; pName.caption = getSetting(pData, pDefs, "Name"); var team = getSetting(pData, pDefs, "Team"); var civ = getSetting(pData, pDefs, "Civ"); // For clients or scenarios, hide some player dropdowns if (!g_IsController || g_GameAttributes.mapType == "scenario") { pCivText.hidden = false; pCiv.hidden = true; pTeamText.hidden = false; pTeam.hidden = true; // Set text values if (civ == "random") pCivText.caption = "[color=\"orange\"]Random"; else 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; pTeamText.hidden = true; pTeam.hidden = false; // Set dropdown values pCiv.selected = (civ ? pCiv.list_data.indexOf(civ) : 0); pTeam.selected = (team !== undefined && team >= 0) ? team+1 : 0; } } getGUIObjectByName("mapInfoDescription").caption = playerString + description; g_IsInGuiUpdate = false; // Game attributes include AI settings, so update the player list updatePlayerList(); } function updateGameAttributes() { if (g_IsNetworked) Engine.SetNetworkGameAttributes(g_GameAttributes); else onGameAttributesChange(); } function updatePlayerList() { g_IsInGuiUpdate = true; var hostNameList = []; var hostGuidList = []; var assignments = []; var aiAssignments = {}; var noAssignment; var assignedCount = 0; for (var guid in g_PlayerAssignments) { var name = g_PlayerAssignments[guid].name; var hostID = hostNameList.length; var player = g_PlayerAssignments[guid].player; hostNameList.push(name); hostGuidList.push(guid); assignments[player] = hostID; if (player != 255) assignedCount++; } // Only enable start button if we have enough assigned players if (g_IsController) getGUIObjectByName("startGame").enabled = (assignedCount > 0); for each (var ai in g_AIs) { if (ai.data.hidden) { // If the map uses a hidden AI then don't hide it var usedByMap = false; for (var i = 0; i < MAX_PLAYERS; ++i) if (i < g_GameAttributes.settings.PlayerData.length && g_GameAttributes.settings.PlayerData[i].AI == ai.id) { usedByMap = true; break; } if (!usedByMap) continue; } // Give AI a different color so it stands out aiAssignments[ai.id] = hostNameList.length; hostNameList.push("[color=\"70 150 70 255\"]AI: " + ai.data.name); hostGuidList.push("ai:" + ai.id); } noAssignment = hostNameList.length; hostNameList.push("[color=\"140 140 140 255\"]Unassigned"); hostGuidList.push(""); for (var i = 0; i < MAX_PLAYERS; ++i) { let playerSlot = i; let playerID = i+1; // we don't show Gaia, so first slot is ID 1 var selection = assignments[playerID]; var configButton = getGUIObjectByName("playerConfig["+i+"]"); configButton.hidden = true; // Look for valid player slots if (playerSlot >= g_GameAttributes.settings.PlayerData.length) continue; // If no human is assigned, look for an AI instead if (selection === undefined) { var aiId = g_GameAttributes.settings.PlayerData[playerSlot].AI; if (aiId) { // Check for a valid AI if (aiId in aiAssignments) selection = aiAssignments[aiId]; else warn("AI \""+aiId+"\" not present. Defaulting to unassigned."); } if (!selection) selection = noAssignment; // Since no human is assigned, show the AI config button if (g_IsController) { configButton.hidden = false; configButton.onpress = function() { Engine.PushGuiPage("page_aiconfig.xml", { ais: g_AIs, id: g_GameAttributes.settings.PlayerData[playerSlot].AI, difficulty: g_GameAttributes.settings.PlayerData[playerSlot].AIDiff, callback: function(ai) { g_GameAttributes.settings.PlayerData[playerSlot].AI = ai.id; g_GameAttributes.settings.PlayerData[playerSlot].AIDiff = ai.difficulty; if (g_IsNetworked) Engine.SetNetworkGameAttributes(g_GameAttributes); else updatePlayerList(); } }); }; } } else { // There was a human, so make sure we don't have any AI left // over in their slot, if we're in charge of the attributes if (g_IsController && g_GameAttributes.settings.PlayerData[playerSlot].AI && g_GameAttributes.settings.PlayerData[playerSlot].AI != "") { g_GameAttributes.settings.PlayerData[playerSlot].AI = ""; if (g_IsNetworked) Engine.SetNetworkGameAttributes(g_GameAttributes); } } var assignBox = getGUIObjectByName("playerAssignment["+i+"]"); assignBox.list = hostNameList; assignBox.list_data = hostGuidList; if (assignBox.selected != selection) assignBox.selected = selection; if (g_IsNetworked && g_IsController) { assignBox.onselectionchange = function () { if (!g_IsInGuiUpdate) { var guid = hostGuidList[this.selected]; if (guid == "") { // Unassign any host from this player slot Engine.AssignNetworkPlayer(playerID, ""); // Remove AI from this player slot g_GameAttributes.settings.PlayerData[playerSlot].AI = ""; } else if (guid.substr(0, 3) == "ai:") { // Unassign any host from this player slot Engine.AssignNetworkPlayer(playerID, ""); // Set the AI for this player slot g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3); } else swapPlayers(guid, playerSlot); Engine.SetNetworkGameAttributes(g_GameAttributes); } }; } else if (!g_IsNetworked) { assignBox.onselectionchange = function () { if (!g_IsInGuiUpdate) { var guid = hostGuidList[this.selected]; if (guid == "") { // Remove AI from this player slot g_GameAttributes.settings.PlayerData[playerSlot].AI = ""; } else if (guid.substr(0, 3) == "ai:") { // Set the AI for this player slot g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3); } else swapPlayers(guid, playerSlot); updatePlayerList(); } }; } } g_IsInGuiUpdate = false; } function swapPlayers(guid, newSlot) { // Player slots are indexed from 0 as Gaia is omitted. var newPlayerID = newSlot + 1; var playerID = g_PlayerAssignments[guid].player; // Attempt to swap the player or AI occupying the target slot, // if any, into the slot this player is currently in. if (playerID != 255) { for (var i in g_PlayerAssignments) { // Move the player in the destination slot into the current slot. if (g_PlayerAssignments[i].player == newPlayerID) { if (g_IsNetworked) Engine.AssignNetworkPlayer(playerID, i); else g_PlayerAssignments[i].player = playerID; break; } } // Transfer the AI from the target slot to the current slot. g_GameAttributes.settings.PlayerData[playerID - 1].AI = g_GameAttributes.settings.PlayerData[newSlot].AI; } if (g_IsNetworked) Engine.AssignNetworkPlayer(newPlayerID, guid); else g_PlayerAssignments[guid].player = newPlayerID; // Remove AI from this player slot g_GameAttributes.settings.PlayerData[newSlot].AI = ""; } function submitChatInput() { var input = getGUIObjectByName("chatInput"); var text = input.caption; if (text.length) { Engine.SendNetworkChat(text); input.caption = ""; } } function addChatMessage(msg) { var username = escapeText(msg.username || g_PlayerAssignments[msg.guid].name); var message = escapeText(msg.text); // TODO: Maybe host should have distinct font/color? var color = "white"; if (g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != 255) { // Valid player who has been assigned - get player colour var player = g_PlayerAssignments[msg.guid].player - 1; var mapName = g_GameAttributes.map; var mapData = loadMapData(mapName); var mapSettings = (mapData && mapData.settings ? mapData.settings : {}); var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {}; var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {}; color = iColorToString(getSetting(pData, pDefs, "Colour")); } var formatted; switch (msg.type) { case "connect": formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has joined[/color]'; break; case "disconnect": formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has left[/color]'; break; case "message": formatted = '[font="serif-bold-13"]<[color="'+ color +'"]' + username + '[/color]>[/font] ' + message; break; default: error("Invalid chat message '" + uneval(msg) + "'"); return; } g_ChatMessages.push(formatted); getGUIObjectByName("chatText").caption = g_ChatMessages.join("\n"); } function toggleMoreOptions() { getGUIObjectByName("moreOptions").hidden = !getGUIObjectByName("moreOptions").hidden; } //////////////////////////////////////////////////////////////////////////////////////////////// // Basic map filters API // Add a new map list filter function addFilter(name, filterFunc) { if (filterFunc instanceof Object) { // Basic validity test var newFilter = {}; newFilter.name = name; newFilter.filter = filterFunc; g_MapFilters.push(newFilter); } else { error("Invalid map filter: "+name); } } // Get array of map filter names function getFilters() { var filters = []; for (var i = 0; i < g_MapFilters.length; ++i) filters.push(g_MapFilters[i].name); return filters; } // Test map filter on given map settings object function testFilter(name, mapSettings) { for (var i = 0; i < g_MapFilters.length; ++i) if (g_MapFilters[i].name == name) return g_MapFilters[i].filter(mapSettings); error("Invalid map filter: "+name); return false; } // Test an array of keywords against a match array using AND logic function keywordTestAND(keywords, matches) { if (!keywords || !matches) return false; for (var m = 0; m < matches.length; ++m) if (keywords.indexOf(matches[m]) == -1) return false; return true; } // Test an array of keywords against a match array using OR logic function keywordTestOR(keywords, matches) { if (!keywords || !matches) return false; for (var m = 0; m < matches.length; ++m) if (keywords.indexOf(matches[m]) != -1) return true; return false; } Index: ps/trunk/binaries/data/mods/public/gui/loading/loading.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/loading/loading.js (revision 13937) +++ ps/trunk/binaries/data/mods/public/gui/loading/loading.js (revision 13938) @@ -1,115 +1,116 @@ var g_Data; const END_PIECE_WIDTH = 16; function init(data) { g_Data = data; // Set to "hourglass" cursor. setCursor("cursor-wait"); // Get tip image and corresponding tip text var tipTextLoadingArray = buildDirEntList("gui/text/tips/", "*.txt", false); if (tipTextLoadingArray.length > 0) { // Set tip text var tipTextFilePath = tipTextLoadingArray[getRandom (0, tipTextLoadingArray.length-1)]; var tipText = readFile(tipTextFilePath); if (tipText) { var index = tipText.indexOf("\n"); tipTextTitle = tipText.substring(0, index); tipTextMessage = tipText.substring(index); getGUIObjectByName("tipTitle").caption = tipTextTitle? tipTextTitle : ""; getGUIObjectByName("tipText").caption = tipTextMessage? tipTextMessage : ""; } // Set tip image var fileName = tipTextFilePath.substring(tipTextFilePath.lastIndexOf("/")+1).replace(".txt", ".png"); var tipImageFilePath = "loading/tips/" + fileName; var sprite = "stretched:" + tipImageFilePath; getGUIObjectByName("tipImage").sprite = sprite? sprite : ""; } else { error("Failed to find any matching tips for the loading screen.") } // janwas: main loop now sets progress / description, but that won't // happen until the first timeslice completes, so set initial values. var loadingMapName = getGUIObjectByName ("loadingMapName"); if (data) { var mapName = data.attribs.settings.Name; switch (data.attribs.mapType) { + case "skirmish": case "scenario": loadingMapName.caption = "Loading \"" + mapName + "\""; break; case "random": loadingMapName.caption = "Generating \"" + mapName + "\""; break; default: error("Unknown map type: " + data.attribs.mapType); } } getGUIObjectByName("progressText").caption = ""; getGUIObjectByName("progressbar").caption = 0; // Pick a random quote of the day (each line is a separate tip). var quoteArray = readFileLines("gui/text/quotes.txt"); getGUIObjectByName("quoteText").caption = quoteArray[getRandom(0, quoteArray.length-1)]; } // ==================================================================== function displayProgress() { // Make the progessbar finish a little early so that the user can actually see it finish if (g_Progress < 100) { // Show 100 when it is really 99 var progress = g_Progress + 1; getGUIObjectByName("progressbar").caption = progress; // display current progress getGUIObjectByName("progressText").caption = progress + "%"; // Displays detailed loading info rather than a percent // getGUIObjectByName("progressText").caption = g_LoadDescription; // display current progess details // Keep curved right edge of progress bar in sync with the rest of the progress bar var middle = getGUIObjectByName("progressbar"); var rightSide = getGUIObjectByName("progressbar_right"); var middleLength = (middle.size.right - middle.size.left) - (END_PIECE_WIDTH / 2); var increment = Math.round(progress * middleLength / 100); var size = rightSide.size; size.left = increment; size.right = increment + END_PIECE_WIDTH; rightSide.size = size; } } // ==================================================================== function reallyStartGame() { // Stop the music // if (global.curr_music) // global.curr_music.fade(-1, 0.0, 5.0); // fade to 0 over 5 seconds // This is a reserved function name that is executed by the engine when it is ready // to start the game (i.e. loading progress has reached 100%). // Switch GUI from loading screen to game session. Engine.SwitchGuiPage("page_session.xml", g_Data); // Restore default cursor. setCursor("arrow-default"); } Index: ps/trunk/binaries/data/mods/public/simulation/components/SkirmishReplacer.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/SkirmishReplacer.js (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/components/SkirmishReplacer.js (revision 13938) @@ -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); Index: ps/trunk/binaries/data/mods/public/simulation/components/interfaces/SkirmishReplacer.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/interfaces/SkirmishReplacer.js (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/components/interfaces/SkirmishReplacer.js (revision 13938) @@ -0,0 +1 @@ +Engine.RegisterInterface("SkirmishReplacer"); Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml (revision 13938) @@ -0,0 +1,12 @@ + + + + skirm + + + structures/{civ}_civil_centre + + + structures/athenians/civic_centre_new.xml + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_civil_centre.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml (revision 13938) @@ -0,0 +1,27 @@ + + + + + 9.0 + + + skirm + + + + + + + + + + structures/{civ}_wall_gate + + + + structures/hellenes/wall_gate.xml + + + 38.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_gate.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml (revision 13938) @@ -0,0 +1,23 @@ + + + + + 9.0 + + + skirm + + + + + + structures/{civ}_wall_long + + + + structures/hellenes/wall_long.xml + + + 36.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_long.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml (revision 13938) @@ -0,0 +1,23 @@ + + + + + 9.0 + + + skirm + + + + + + structures/{civ}_wall_medium + + + + structures/hellenes/wall_medium.xml + + + 24.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_medium.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml (revision 13938) @@ -0,0 +1,23 @@ + + + + + 9.0 + + + skirm + + + + + + structures/{civ}_wall_short + + + + structures/hellenes/wall_short.xml + + + 12.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_short.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml (revision 13938) @@ -0,0 +1,23 @@ + + + + + 15.0 + + + skirm + + + + + + structures/{civ}_wall_tower + + + + structures/hellenes/wall_tower.xml + + + 10 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/default_wall_tower.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml (revision 13938) @@ -0,0 +1,26 @@ + + + + + 9.0 + + + skirm + + + + + + + + + + structures/iber_wall_gate + + + structures/iberians/wall_gate.xml + + + 36.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_gate.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml (revision 13938) @@ -0,0 +1,22 @@ + + + + + 9.0 + + + skirm + + + + + + structures/iber_wall_long + + + structures/iberians/wall_long.xml + + + 36.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_long.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml (revision 13938) @@ -0,0 +1,22 @@ + + + + + 9.0 + + + skirm + + + + + + structures/iber_wall_medium + + + structures/iberians/wall_medium.xml + + + 24.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_medium.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml (revision 13938) @@ -0,0 +1,22 @@ + + + + + 9.0 + + + skirm + + + + + + structures/iber_wall_short + + + structures/iberians/wall_short.xml + + + 12.0 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_short.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml (revision 13938) @@ -0,0 +1,22 @@ + + + + + 15.0 + + + skirm + + + + + + structures/iber_wall_tower + + + structures/iberians/wall_tower.xml + + + 10 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/structures/iber_wall_tower.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml (revision 13938) @@ -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 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_cavalry.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml (revision 13938) @@ -0,0 +1,14 @@ + + + + skirm + + + + units/{civ}_infantry_spearman_b + units/rome_infantry_swordsman_b + + + units/athenians/infantry_spearman_b.xml + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_melee_b.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml (revision 13938) @@ -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 + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_infantry_ranged_b.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml (revision 13938) @@ -0,0 +1,12 @@ + + + + skirm + + + units/{civ}_support_female_citizen + + + units/athenians/female_citizen.xml + + Property changes on: ps/trunk/binaries/data/mods/public/simulation/templates/skirmish/units/default_support_female_citizen.xml ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: ps/trunk/build/premake/premake4.lua =================================================================== --- ps/trunk/build/premake/premake4.lua (revision 13937) +++ ps/trunk/build/premake/premake4.lua (revision 13938) @@ -1,1323 +1,1324 @@ newoption { trigger = "android", description = "Use non-working Android cross-compiling mode" } newoption { trigger = "atlas", description = "Include Atlas scenario editor projects" } newoption { trigger = "collada", description = "Include COLLADA projects (requires FCollada library)" } newoption { trigger = "coverage", description = "Enable code coverage data collection (GCC only)" } newoption { trigger = "gles", description = "Use non-working OpenGL ES 2.0 mode" } newoption { trigger = "icc", description = "Use Intel C++ Compiler (Linux only; should use either \"--cc icc\" or --without-pch too, and then set CXX=icpc before calling make)" } newoption { trigger = "outpath", description = "Location for generated project files" } newoption { trigger = "without-audio", description = "Disable use of OpenAL/Ogg/Vorbis APIs" } newoption { trigger = "minimal-flags", description = "Only set compiler/linker flags that are really needed. Has no effect on Windows builds" } newoption { trigger = "without-nvtt", description = "Disable use of NVTT" } newoption { trigger = "without-tests", description = "Disable generation of test projects" } newoption { trigger = "without-pch", description = "Disable generation and usage of precompiled headers" } newoption { trigger = "with-system-nvtt", description = "Search standard paths for nvidia-texture-tools library, instead of using bundled copy" } newoption { trigger = "with-system-enet", description = "Search standard paths for libenet, instead of using bundled copy" } newoption { trigger = "with-system-mozjs185", description = "Search standard paths for libmozjs185, instead of using bundled copy" } newoption { trigger = "sysroot", description = "Set compiler system root path, used for building against a non-system SDK. For example /usr/local becomes SYSROOT/user/local" } newoption { trigger = "macosx-version-min", description = "Set minimum required version of the OS X API, the build will possibly fail if an older SDK is used, while newer API functions will be weakly linked (i.e. resolved at runtime)" } newoption { trigger = "macosx-bundle", description = "Enable OSX bundle, the argument is the bundle identifier string (e.g. com.wildfiregames.0ad)" } newoption { trigger = "bindir", description = "Directory for executables (typically '/usr/games'); default is to be relocatable" } newoption { trigger = "datadir", description = "Directory for data files (typically '/usr/share/games/0ad'); default is ../data/ relative to executable" } newoption { trigger = "libdir", description = "Directory for libraries (typically '/usr/lib/games/0ad'); default is ./ relative to executable" } -- Root directory of project checkout relative to this .lua file rootdir = "../.." dofile("extern_libs4.lua") -- detect CPU architecture (simplistic, currently only supports x86, amd64 and ARM) arch = "x86" if _OPTIONS["android"] then arch = "arm" elseif os.is("windows") then if os.getenv("PROCESSOR_ARCHITECTURE") == "amd64" or os.getenv("PROCESSOR_ARCHITEW6432") == "amd64" then arch = "amd64" end else arch = os.getenv("HOSTTYPE") if arch == "x86_64" or arch == "amd64" then arch = "amd64" else os.execute("gcc -dumpmachine > .gccmachine.tmp") local f = io.open(".gccmachine.tmp", "r") local machine = f:read("*line") f:close() if string.find(machine, "x86_64") == 1 or string.find(machine, "amd64") == 1 then arch = "amd64" elseif string.find(machine, "i.86") == 1 then arch = "x86" elseif string.find(machine, "arm") == 1 then arch = "arm" else print("WARNING: Cannot determine architecture from GCC, assuming x86") end end end -- Set up the Solution solution "pyrogenesis" targetdir(rootdir.."/binaries/system") libdirs(rootdir.."/binaries/system") if not _OPTIONS["outpath"] then error("You must specify the 'outpath' parameter") end location(_OPTIONS["outpath"]) configurations { "Release", "Debug" } -- Get some environement specific information used later. if os.is("windows") then nasmpath(rootdir.."/build/bin/nasm.exe") lcxxtestpath = rootdir.."/build/bin/cxxtestgen.exe" has_broken_pch = false else lcxxtestpath = rootdir.."/build/bin/cxxtestgen.pl" if os.is("linux") and arch == "amd64" then nasmformat "elf64" elseif os.is("macosx") and arch == "amd64" then nasmformat "macho64" elseif os.is("macosx") then nasmformat "macho" else nasmformat "elf" end -- GCC bug (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10591) - PCH breaks anonymous namespaces -- Fixed in 4.2.0, but we have to disable PCH for earlier versions, else -- it conflicts annoyingly with wx 2.8 headers. -- It's too late to do this test by the time we start compiling the PCH file, so -- do the test in this build script instead (which is kind of ugly - please fix if -- you have a better idea) if not _OPTIONS["icc"] then os.execute("gcc -dumpversion > .gccver.tmp") local f = io.open(".gccver.tmp", "r") major, dot, minor = f:read(1, 1, 1) f:close() major = 0+major -- coerce to number minor = 0+minor has_broken_pch = (major < 4 or (major == 4 and minor < 2)) if has_broken_pch then print("WARNING: Detected GCC <4.2 -- disabling PCH for Atlas (will increase build times)") end end end source_root = rootdir.."/source/" -- default for most projects - overridden by local in others -- Rationale: projects should not have any additional include paths except for -- those required by external libraries. Instead, we should always write the -- full relative path, e.g. #include "maths/Vector3d.h". This avoids confusion -- ("which file is meant?") and avoids enormous include path lists. -- projects: engine static libs, main exe, atlas, atlas frontends, test. -------------------------------------------------------------------------------- -- project helper functions -------------------------------------------------------------------------------- function project_set_target(project_name) -- Note: On Windows, ".exe" is added on the end, on unices the name is used directly local obj_dir_prefix = _OPTIONS["outpath"].."/obj/"..project_name.."_" configuration "Debug" objdir(obj_dir_prefix.."Debug") targetsuffix("_dbg") configuration "Release" objdir(obj_dir_prefix.."Release") configuration { } end function project_set_build_flags() flags { "Symbols", "NoEditAndContinue" } if not _OPTIONS["icc"] and (os.is("windows") or not _OPTIONS["minimal-flags"]) then -- adds the -Wall compiler flag flags { "ExtraWarnings" } -- this causes far too many warnings/remarks on ICC end -- disable Windows debug heap, since it makes malloc/free hugely slower when -- running inside a debugger if os.is("windows") then flags { "NoDebugHeap" } end configuration "Debug" defines { "DEBUG" } configuration "Release" if os.is("windows") or not _OPTIONS["minimal-flags"] then flags { "OptimizeSpeed" } end defines { "NDEBUG", "CONFIG_FINAL=1" } configuration { } if _OPTIONS["gles"] then defines { "CONFIG2_GLES=1" } end if _OPTIONS["without-audio"] then defines { "CONFIG2_AUDIO=0" } end if _OPTIONS["without-nvtt"] then defines { "CONFIG2_NVTT=0" } end -- required for the lowlevel library. must be set from all projects that use it, otherwise it assumes it is -- being used as a DLL (which is currently not the case in 0ad) defines { "LIB_STATIC_LINK" } -- various platform-specific build flags if os.is("windows") then -- use native wchar_t type (not typedef to unsigned short) flags { "NativeWChar" } -- VC++ 2008 has implied FPO as the default (newer versions default to /Oy-) -- disable it explicitly since it breaks our stack walker in release build if _ACTION == "vs2008" then buildoptions { "/Oy-" } end else -- *nix if _OPTIONS["icc"] and not _OPTIONS["minimal-flags"] then buildoptions { "-w1", -- "-Wabi", -- "-Wp64", -- complains about OBJECT_TO_JSVAL which is annoying "-Wpointer-arith", "-Wreturn-type", -- "-Wshadow", "-Wuninitialized", "-Wunknown-pragmas", "-Wunused-function", "-wd1292" -- avoid lots of 'attribute "__nonnull__" ignored' } configuration "Debug" buildoptions { "-O0" } -- ICC defaults to -O2 configuration { } if os.is("macosx") then linkoptions { "-multiply_defined","suppress" } end else -- exclude most non-essential build options for minimal-flags if not _OPTIONS["minimal-flags"] then buildoptions { -- enable most of the standard warnings "-Wno-switch", -- enumeration value not handled in switch (this is sometimes useful, but results in lots of noise) "-Wno-reorder", -- order of initialization list in constructors (lots of noise) "-Wno-invalid-offsetof", -- offsetof on non-POD types (see comment in renderer/PatchRData.cpp) "-Wextra", "-Wno-missing-field-initializers", -- (this is common in external headers we can't fix) -- add some other useful warnings that need to be enabled explicitly "-Wunused-parameter", "-Wredundant-decls", -- (useful for finding some multiply-included header files) -- "-Wformat=2", -- (useful sometimes, but a bit noisy, so skip it by default) -- "-Wcast-qual", -- (useful for checking const-correctness, but a bit noisy, so skip it by default) "-Wnon-virtual-dtor", -- (sometimes noisy but finds real bugs) "-Wundef", -- (useful for finding macro name typos) -- enable security features (stack checking etc) that shouldn't have -- a significant effect on performance and can catch bugs "-fstack-protector-all", "-U_FORTIFY_SOURCE", -- (avoid redefinition warning if already defined) "-D_FORTIFY_SOURCE=2", -- always enable strict aliasing (useful in debug builds because of the warnings) "-fstrict-aliasing", -- don't omit frame pointers (for now), because performance will be impacted -- negatively by the way this breaks profilers more than it will be impacted -- positively by the optimisation "-fno-omit-frame-pointer" } if not _OPTIONS["without-pch"] then buildoptions { -- do something (?) so that ccache can handle compilation with PCH enabled -- (ccache 3.1+ also requires CCACHE_SLOPPINESS=time_macros for this to work) "-fpch-preprocess" } end if arch == "x86" or arch == "amd64" then buildoptions { -- enable SSE intrinsics "-msse" } end if os.is("linux") or os.is("bsd") then linkoptions { "-Wl,--no-undefined", "-Wl,--as-needed" } end if arch == "x86" then buildoptions { -- To support intrinsics like __sync_bool_compare_and_swap on x86 -- we need to set -march to something that supports them "-march=i686" } end end if arch == "arm" then -- disable warnings about va_list ABI change buildoptions { "-Wno-psabi" } if _OPTIONS["android"] then -- target generic arm CPUs with NEON buildoptions { "-mtune=generic-arm -mfpu=neon -mfloat-abi=softfp" } else -- target Cortex-A15 CPUs with NEON buildoptions { "-mtune=cortex-a15 -mfpu=neon-vfpv4 -mfloat-abi=hard" } end end if _OPTIONS["coverage"] then buildoptions { "-fprofile-arcs", "-ftest-coverage" } links { "gcov" } end -- We don't want to require SSE2 everywhere yet, but OS X headers do -- require it (and Intel Macs always have it) so enable it here if os.is("macosx") then buildoptions { "-msse2" } end -- Check if SDK path should be used if _OPTIONS["sysroot"] then buildoptions { "-isysroot " .. _OPTIONS["sysroot"] } linkoptions { "-Wl,-syslibroot," .. _OPTIONS["sysroot"] } end -- On OS X, sometimes we need to specify the minimum API version to use if _OPTIONS["macosx-version-min"] then buildoptions { "-mmacosx-version-min=" .. _OPTIONS["macosx-version-min"] } linkoptions { "-mmacosx-version-min=" .. _OPTIONS["macosx-version-min"] } end -- Check if we're building a bundle if _OPTIONS["macosx-bundle"] then defines { "BUNDLE_IDENTIFIER=" .. _OPTIONS["macosx-bundle"] } end end if not _OPTIONS["minimal-flags"] then buildoptions { -- Hide symbols in dynamic shared objects by default, for efficiency and for equivalence with -- Windows - they should be exported explicitly with __attribute__ ((visibility ("default"))) "-fvisibility=hidden" } end if _OPTIONS["bindir"] then defines { "INSTALLED_BINDIR=" .. _OPTIONS["bindir"] } end if _OPTIONS["datadir"] then defines { "INSTALLED_DATADIR=" .. _OPTIONS["datadir"] } end if _OPTIONS["libdir"] then defines { "INSTALLED_LIBDIR=" .. _OPTIONS["libdir"] } end if os.is("linux") or os.is("bsd") then -- To use our local shared libraries, they need to be found in the -- runtime dynamic linker path. Add their path to -rpath. if _OPTIONS["libdir"] then linkoptions {"-Wl,-rpath," .. _OPTIONS["libdir"] } else -- On FreeBSD we need to allow use of $ORIGIN if os.is("bsd") then linkoptions { "-Wl,-z,origin" } end -- Adding the executable path and taking care of correct escaping if _ACTION == "gmake" then linkoptions { "-Wl,-rpath,'$$ORIGIN'" } elseif _ACTION == "codeblocks" then linkoptions { "-Wl,-R\\\\$$ORIGIN" } end end end end end -- add X11 includes paths after all the others so they don't conflict with -- bundled libs function project_add_x11_dirs() if not os.is("windows") and not os.is("macosx") then -- X11 includes may be installed in one of a gadzillion of three places -- Famous last words: "You can't include too much! ;-)" includedirs { "/usr/X11R6/include/X11", "/usr/X11R6/include", "/usr/include/X11" } libdirs { "/usr/X11R6/lib" } end end -- create a project and set the attributes that are common to all projects. function project_create(project_name, target_type) project(project_name) language "C++" kind(target_type) project_set_target(project_name) project_set_build_flags() end -- OSX creates a .app bundle if the project type of the main application is set to "WindowedApp". -- We don't want this because this bundle would be broken (it lacks all the resources and external dependencies, Info.plist etc...) -- Windows opens a console in the background if it's set to ConsoleApp, which is not what we want. -- I didn't check if this setting matters for linux, but WindowedApp works there. function get_main_project_target_type() if _OPTIONS["android"] then return "SharedLib" elseif os.is("macosx") then return "ConsoleApp" else return "WindowedApp" end end -- source_root: rel_source_dirs and rel_include_dirs are relative to this directory -- rel_source_dirs: A table of subdirectories. All source files in these directories are added. -- rel_include_dirs: A table of subdirectories to be included. -- extra_params: table including zero or more of the following: -- * no_pch: If specified, no precompiled headers are used for this project. -- * pch_dir: If specified, this directory will be used for precompiled headers instead of the default -- /pch//. -- * extra_files: table of filenames (relative to source_root) to add to project -- * extra_links: table of library names to add to link step function project_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params) for i,v in pairs(rel_source_dirs) do local prefix = source_root..v.."/" files { prefix.."*.cpp", prefix.."*.h", prefix.."*.inl", prefix.."*.js", prefix.."*.asm", prefix.."*.mm" } end -- Put the project-specific PCH directory at the start of the -- include path, so '#include "precompiled.h"' will look in -- there first local pch_dir if not extra_params["pch_dir"] then pch_dir = source_root .. "pch/" .. project().name .. "/" else pch_dir = extra_params["pch_dir"] end includedirs { pch_dir } -- Precompiled Headers -- rationale: we need one PCH per static lib, since one global header would -- increase dependencies. To that end, we can either include them as -- "projectdir/precompiled.h", or add "source/PCH/projectdir" to the -- include path and put the PCH there. The latter is better because -- many projects contain several dirs and it's unclear where there the -- PCH should be stored. This way is also a bit easier to use in that -- source files always include "precompiled.h". -- Notes: -- * Visual Assist manages to use the project include path and can -- correctly open these files from the IDE. -- * precompiled.cpp (needed to "Create" the PCH) also goes in -- the abovementioned dir. if (not _OPTIONS["without-pch"] and not extra_params["no_pch"]) then pchheader(pch_dir.."precompiled.h") pchsource(pch_dir.."precompiled.cpp") defines { "USING_PCH" } files { pch_dir.."precompiled.h", pch_dir.."precompiled.cpp" } else flags { "NoPCH" } end -- next is source root dir, for absolute (nonrelative) includes -- (e.g. "lib/precompiled.h") includedirs { source_root } for i,v in pairs(rel_include_dirs) do includedirs { source_root .. v } end if extra_params["extra_files"] then for i,v in pairs(extra_params["extra_files"]) do -- .rc files are only needed on Windows if path.getextension(v) ~= ".rc" or os.is("windows") then files { source_root .. v } end end end if extra_params["extra_links"] then links { extra_params["extra_links"] } end end -- Add command-line options to set up the manifest dependencies for Windows -- (See lib/sysdep/os/win/manifest.cpp) function project_add_manifest() linkoptions { "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df'\"" } configuration "Debug" linkoptions { "\"/manifestdependency:type='win32' name='Microsoft.VC80.DebugCRT' version='8.0.50727.4053' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'\"" } configuration "Release" linkoptions { "\"/manifestdependency:type='win32' name='Microsoft.VC80.CRT' version='8.0.50727.4053' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'\"" } configuration { } end -------------------------------------------------------------------------------- -- engine static libraries -------------------------------------------------------------------------------- -- the engine is split up into several static libraries. this eases separate -- distribution of those components, reduces dependencies a bit, and can -- also speed up builds. -- more to the point, it is necessary to efficiently support a separate -- test executable that also includes much of the game code. -- names of all static libs created. automatically added to the -- main app project later (see explanation at end of this file) static_lib_names = {} -- set up one of the static libraries into which the main engine code is split. -- extra_params: -- no_default_link: If specified, linking won't be done by default. -- For the rest of extra_params, see project_add_contents(). -- note: rel_source_dirs and rel_include_dirs are relative to global source_root. function setup_static_lib_project (project_name, rel_source_dirs, extern_libs, extra_params) local target_type = "StaticLib" project_create(project_name, target_type) project_add_contents(source_root, rel_source_dirs, {}, extra_params) project_add_extern_libs(extern_libs, target_type) project_add_x11_dirs() if not extra_params["no_default_link"] then table.insert(static_lib_names, project_name) end if os.is("windows") then flags { "NoRTTI" } end end -- this is where the source tree is chopped up into static libs. -- can be changed very easily; just copy+paste a new setup_static_lib_project, -- or remove existing ones. static libs are automagically added to -- main_exe link step. function setup_all_libs () -- relative to global source_root. local source_dirs = {} -- names of external libraries used (see libraries_dir comment) local extern_libs = {} source_dirs = { "network", } extern_libs = { "spidermonkey", "enet", "boost", -- dragged in via server->simulation.h->random } setup_static_lib_project("network", source_dirs, extern_libs, {}) source_dirs = { "simulation2", "simulation2/components", "simulation2/helpers", "simulation2/scripting", "simulation2/serialization", "simulation2/system", "simulation2/testcomponents", } extern_libs = { "boost", "opengl", "spidermonkey", } setup_static_lib_project("simulation2", source_dirs, extern_libs, {}) source_dirs = { "scriptinterface", } extern_libs = { "boost", "spidermonkey", "valgrind", "sdl", } setup_static_lib_project("scriptinterface", source_dirs, extern_libs, {}) source_dirs = { "ps", "ps/scripting", "ps/Network", "ps/GameSetup", "ps/XML", "soundmanager", "soundmanager/data", "soundmanager/items", "soundmanager/scripting", "scripting", "maths", "maths/scripting", } extern_libs = { "spidermonkey", "sdl", -- key definitions "libxml2", "opengl", "zlib", "boost", "enet", "libcurl" } if not _OPTIONS["without-audio"] then table.insert(extern_libs, "openal") table.insert(extern_libs, "vorbis") end setup_static_lib_project("engine", source_dirs, extern_libs, {}) source_dirs = { "graphics", "graphics/scripting", "renderer", "renderer/scripting", "third_party/mikktspace" } extern_libs = { "opengl", "sdl", -- key definitions "spidermonkey", -- for graphics/scripting "boost" } if not _OPTIONS["without-nvtt"] then table.insert(extern_libs, "nvtt") end setup_static_lib_project("graphics", source_dirs, extern_libs, {}) source_dirs = { "tools/atlas/GameInterface", "tools/atlas/GameInterface/Handlers" } extern_libs = { "boost", "sdl", -- key definitions "opengl", "spidermonkey" } setup_static_lib_project("atlas", source_dirs, extern_libs, {}) source_dirs = { "gui", "gui/scripting" } extern_libs = { "spidermonkey", "sdl", -- key definitions "opengl", "boost" } setup_static_lib_project("gui", source_dirs, extern_libs, {}) source_dirs = { "lib", "lib/adts", "lib/allocators", "lib/external_libraries", "lib/file", "lib/file/archive", "lib/file/common", "lib/file/io", "lib/file/vfs", "lib/pch", "lib/posix", "lib/res", "lib/res/graphics", "lib/sysdep", "lib/tex" } extern_libs = { "boost", "sdl", "opengl", "libpng", "zlib", "libjpg", "valgrind", "cxxtest", } -- CPU architecture-specific if arch == "amd64" then table.insert(source_dirs, "lib/sysdep/arch/amd64"); table.insert(source_dirs, "lib/sysdep/arch/x86_x64"); elseif arch == "x86" then table.insert(source_dirs, "lib/sysdep/arch/ia32"); table.insert(source_dirs, "lib/sysdep/arch/x86_x64"); elseif arch == "arm" then table.insert(source_dirs, "lib/sysdep/arch/arm"); end -- OS-specific sysdep_dirs = { linux = { "lib/sysdep/os/linux", "lib/sysdep/os/unix" }, -- note: RC file must be added to main_exe project. -- note: don't add "lib/sysdep/os/win/aken.cpp" because that must be compiled with the DDK. windows = { "lib/sysdep/os/win", "lib/sysdep/os/win/wposix", "lib/sysdep/os/win/whrt" }, macosx = { "lib/sysdep/os/osx", "lib/sysdep/os/unix" }, bsd = { "lib/sysdep/os/bsd", "lib/sysdep/os/unix", "lib/sysdep/os/unix/x" }, } for i,v in pairs(sysdep_dirs[os.get()]) do table.insert(source_dirs, v); end if os.is("linux") then if _OPTIONS["android"] then table.insert(source_dirs, "lib/sysdep/os/android") else table.insert(source_dirs, "lib/sysdep/os/unix/x") end end -- runtime-library-specific if _ACTION == "vs2005" or _ACTION == "vs2008" or _ACTION == "vs2010" then table.insert(source_dirs, "lib/sysdep/rtl/msc"); else table.insert(source_dirs, "lib/sysdep/rtl/gcc"); end setup_static_lib_project("lowlevel", source_dirs, extern_libs, {}) -- Third-party libraries that are built as part of the main project, -- not built externally and then linked source_dirs = { "third_party/mongoose", } extern_libs = { } setup_static_lib_project("mongoose", source_dirs, extern_libs, { no_pch = 1 }) -- CxxTest mock function support extern_libs = { "boost", "cxxtest", } -- 'real' implementations, to be linked against the main executable -- (files are added manually and not with setup_static_lib_project -- because not all files in the directory are included) setup_static_lib_project("mocks_real", {}, extern_libs, { no_default_link = 1, no_pch = 1 }) files { "mocks/*.h", source_root.."mocks/*_real.cpp" } -- 'test' implementations, to be linked against the test executable setup_static_lib_project("mocks_test", {}, extern_libs, { no_default_link = 1, no_pch = 1 }) files { source_root.."mocks/*.h", source_root.."mocks/*_test.cpp" } end -------------------------------------------------------------------------------- -- main EXE -------------------------------------------------------------------------------- -- used for main EXE as well as test used_extern_libs = { "opengl", "sdl", "libjpg", "libpng", "zlib", "spidermonkey", "libxml2", "boost", "cxxtest", "comsuppw", "enet", "libcurl", "valgrind", } if not os.is("windows") and not _OPTIONS["android"] and not os.is("macosx") then -- X11 should only be linked on *nix table.insert(used_extern_libs, "x11") table.insert(used_extern_libs, "xcursor") end if not _OPTIONS["without-audio"] then table.insert(used_extern_libs, "openal") table.insert(used_extern_libs, "vorbis") end if not _OPTIONS["without-nvtt"] then table.insert(used_extern_libs, "nvtt") end -- Bundles static libs together with main.cpp and builds game executable. function setup_main_exe () local target_type = get_main_project_target_type() project_create("pyrogenesis", target_type) links { "mocks_real" } local extra_params = { extra_files = { "main.cpp" }, no_pch = 1 } project_add_contents(source_root, {}, {}, extra_params) project_add_extern_libs(used_extern_libs, target_type) project_add_x11_dirs() -- Platform Specifics if os.is("windows") then files { source_root.."lib/sysdep/os/win/icon.rc" } -- from "lowlevel" static lib; must be added here to be linked in files { source_root.."lib/sysdep/os/win/error_dialog.rc" } flags { "NoRTTI" } linkoptions { -- wraps main thread in a __try block(see wseh.cpp). replace with mainCRTStartup if that's undesired. "/ENTRY:wseh_EntryPoint", -- see wstartup.h "/INCLUDE:_wstartup_InitAndRegisterShutdown", -- allow manual unload of delay-loaded DLLs "/DELAY:UNLOAD", } -- see manifest.cpp project_add_manifest() elseif os.is("linux") or os.is("bsd") then if not _OPTIONS["android"] and not (os.getversion().description == "OpenBSD") then links { "rt" } end if _OPTIONS["android"] then -- NDK's STANDALONE-TOOLCHAIN.html says this is required linkoptions { "-Wl,--fix-cortex-a8" } end if os.is("linux") or os.getversion().description == "GNU/kFreeBSD" then links { -- Dynamic libraries (needed for linking for gold) "dl", } elseif os.is("bsd") then links { -- Needed for backtrace* on BSDs "execinfo", } end -- Threading support buildoptions { "-pthread" } if not _OPTIONS["android"] then linkoptions { "-pthread" } end -- For debug_resolve_symbol configuration "Debug" linkoptions { "-rdynamic" } configuration { } elseif os.is("macosx") then links { "pthread" } linkoptions { "-framework ApplicationServices", "-framework Cocoa", "-framework CoreFoundation" } end end -------------------------------------------------------------------------------- -- atlas -------------------------------------------------------------------------------- -- setup a typical Atlas component project -- extra_params, rel_source_dirs and rel_include_dirs: as in project_add_contents; function setup_atlas_project(project_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params) local source_root = rootdir.."/source/tools/atlas/" .. project_name .. "/" project_create(project_name, target_type) -- if not specified, the default for atlas pch files is in the project root. if not extra_params["pch_dir"] then extra_params["pch_dir"] = source_root end project_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params) project_add_extern_libs(extern_libs, target_type) project_add_x11_dirs() -- Platform Specifics if os.is("windows") then defines { "_UNICODE" } -- Link to required libraries links { "winmm", "comctl32", "rpcrt4", "delayimp", "ws2_32" } -- required to use WinMain() on Windows, otherwise will default to main() flags { "WinMain" } elseif os.is("linux") or os.is("bsd") then buildoptions { "-rdynamic", "-fPIC" } linkoptions { "-fPIC", "-rdynamic" } elseif os.is("macosx") then -- install_name settings aren't really supported yet by premake, but there are plans for the future. -- we currently use this hack to work around some bugs with wrong install_names. if target_type == "SharedLib" then if _OPTIONS["macosx-bundle"] then -- If we're building a bundle, it will be in ../Frameworks linkoptions { "-install_name @executable_path/../Frameworks/lib"..project_name..".dylib" } else linkoptions { "-install_name @executable_path/lib"..project_name..".dylib" } end end end end -- build all Atlas component projects function setup_atlas_projects() setup_atlas_project("AtlasObject", "StaticLib", { -- src "." },{ -- include },{ -- extern_libs "boost", "libxml2", "spidermonkey", "wxwidgets" },{ -- extra_params no_pch = 1 }) setup_atlas_project("AtlasScript", "StaticLib", { -- src "." },{ -- include ".." },{ -- extern_libs "boost", "spidermonkey", "valgrind", "wxwidgets", },{ -- extra_params no_pch = 1 }) atlas_src = { "ActorEditor", "CustomControls/Buttons", "CustomControls/Canvas", "CustomControls/ColourDialog", "CustomControls/DraggableListCtrl", "CustomControls/EditableListCtrl", "CustomControls/FileHistory", "CustomControls/HighResTimer", + "CustomControls/MapDialog", "CustomControls/SnapSplitterWindow", "CustomControls/VirtualDirTreeCtrl", "CustomControls/Windows", "ErrorReporter", "General", "General/VideoRecorder", "Misc", "ScenarioEditor", "ScenarioEditor/Sections/Common", "ScenarioEditor/Sections/Cinematic", "ScenarioEditor/Sections/Environment", "ScenarioEditor/Sections/Map", "ScenarioEditor/Sections/Object", "ScenarioEditor/Sections/Player", "ScenarioEditor/Sections/Terrain", "ScenarioEditor/Sections/Trigger", "ScenarioEditor/Tools", "ScenarioEditor/Tools/Common", } atlas_extra_links = { "AtlasObject", "AtlasScript", } atlas_extern_libs = { "boost", "boost_signals", "comsuppw", --"ffmpeg", -- disabled for now because it causes too many build difficulties "libxml2", "sdl", -- key definitions "spidermonkey", "wxwidgets", "zlib", } if not os.is("windows") and not os.is("macosx") then -- X11 should only be linked on *nix table.insert(atlas_extern_libs, "x11") end setup_atlas_project("AtlasUI", "SharedLib", atlas_src, { -- include "..", "CustomControls", "Misc" }, atlas_extern_libs, { -- extra_params pch_dir = rootdir.."/source/tools/atlas/AtlasUI/Misc/", no_pch = (has_broken_pch), extra_links = atlas_extra_links, extra_files = { "Misc/atlas.rc" } }) end -- Atlas 'frontend' tool-launching projects function setup_atlas_frontend_project (project_name) local target_type = get_main_project_target_type() project_create(project_name, target_type) project_add_extern_libs({ "spidermonkey", }, target_type) project_add_x11_dirs() local source_root = rootdir.."/source/tools/atlas/AtlasFrontends/" files { source_root..project_name..".cpp" } if os.is("windows") then files { source_root..project_name..".rc" } end includedirs { source_root .. ".." } -- Platform Specifics if os.is("windows") then defines { "_UNICODE" } -- required to use WinMain() on Windows, otherwise will default to main() flags { "WinMain" } -- see manifest.cpp project_add_manifest() else -- Non-Windows, = Unix links { "AtlasObject" } end links { "AtlasUI" } end function setup_atlas_frontends() setup_atlas_frontend_project("ActorEditor") end -------------------------------------------------------------------------------- -- collada -------------------------------------------------------------------------------- function setup_collada_project(project_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params) project_create(project_name, target_type) local source_root = source_root.."collada/" extra_params["pch_dir"] = source_root project_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params) project_add_extern_libs(extern_libs, target_type) project_add_x11_dirs() -- Platform Specifics if os.is("windows") then -- required to use WinMain() on Windows, otherwise will default to main() flags { "WinMain" } elseif os.is("linux") then defines { "LINUX" } links { "dl", } -- FCollada is not aliasing-safe, so disallow dangerous optimisations -- (TODO: It'd be nice to fix FCollada, but that looks hard) buildoptions { "-fno-strict-aliasing" } buildoptions { "-rdynamic" } linkoptions { "-rdynamic" } elseif os.is("bsd") then if os.getversion().description == "OpenBSD" then links { "c", } end if os.getversion().description == "GNU/kFreeBSD" then links { "dl", } end buildoptions { "-fno-strict-aliasing" } buildoptions { "-rdynamic" } linkoptions { "-rdynamic" } elseif os.is("macosx") then -- define MACOS-something? -- install_name settings aren't really supported yet by premake, but there are plans for the future. -- we currently use this hack to work around some bugs with wrong install_names. if target_type == "SharedLib" then if _OPTIONS["macosx-bundle"] then -- If we're building a bundle, it will be in ../Frameworks linkoptions { "-install_name @executable_path/../Frameworks/lib"..project_name..".dylib" } else linkoptions { "-install_name @executable_path/lib"..project_name..".dylib" } end end buildoptions { "-fno-strict-aliasing" } -- On OSX, fcollada uses a few utility functions from coreservices linkoptions { "-framework CoreServices" } end end -- build all Collada component projects function setup_collada_projects() setup_collada_project("Collada", "SharedLib", { -- src "." },{ -- include },{ -- extern_libs "fcollada", "libxml2" },{ -- extra_params }) end -------------------------------------------------------------------------------- -- tests -------------------------------------------------------------------------------- -- Cxxtestgen needs to create .cpp files from the .h files before they can be compiled. -- By default we are using prebuildcommands, but we are also using customizations of premake -- for makefiles. The reason is that premake currently has a bug with makefiles and parallel -- builds (e.g. -j5). It's not guaranteed that prebuildcommands always run before building. -- All the *.cpp and *.h files need to be added to files no matter if prebuildcommands -- or customizations are used. -- If no customizations are implemented for a specific action (e.g. vs2010), passing the -- parameters won't have any effects. function configure_cxxtestgen() local lcxxtestrootfile = source_root.."test_root.cpp" files { lcxxtestrootfile } -- Define the options used for cxxtestgen local lcxxtestoptions = "--have-std" local lcxxtestrootoptions = "--have-std" if os.is("windows") then lcxxtestrootoptions = lcxxtestrootoptions .. " --gui=PsTestWrapper --runner=Win32ODSPrinter" else lcxxtestrootoptions = lcxxtestrootoptions .. " --gui=PsTestWrapper --runner=ErrorPrinter" end -- Precompiled headers - the header is added to all generated .cpp files -- note that the header isn't actually precompiled here, only #included -- so that the build stage can use it as a precompiled header. local include = " --include=precompiled.h" lcxxtestrootoptions = lcxxtestrootoptions .. include lcxxtestoptions = lcxxtestoptions .. include -- Set all the parameters used in our cxxtestgen customization in premake. cxxtestrootfile(lcxxtestrootfile) cxxtestpath(lcxxtestpath) cxxtestrootoptions(lcxxtestrootoptions) cxxtestoptions(lcxxtestoptions) -- The file paths needs to be made relative to the project directory for the prebuildcommands. -- premake's paths are relative to premake4.lua by default. lcxxtestrootfile = path.rebase(lcxxtestrootfile, path.getabsolute("."), _OPTIONS["outpath"]) lcxxtestpath = path.rebase(lcxxtestpath, path.getabsolute("."), _OPTIONS["outpath"]) -- On windows we have to use backlashes in our paths. We don't have to take care -- of that for the parameters passed to our cxxtestgen customizations. if os.is("windows") then lcxxtestrootfile = path.translate(lcxxtestrootfile, "\\") lcxxtestpath = path.translate(lcxxtestpath, "\\") end if _ACTION ~= "gmake" and _ACTION ~= "vs2010" then prebuildcommands { lcxxtestpath.." --root "..lcxxtestrootoptions.." -o "..lcxxtestrootfile } end -- Find header files in 'test' subdirectories local all_files = os.matchfiles(source_root .. "**/tests/*.h") for i,v in pairs(all_files) do -- Don't include sysdep tests on the wrong sys -- Don't include Atlas tests unless Atlas is being built if not (string.find(v, "/sysdep/os/win/") and not os.is("windows")) and not (string.find(v, "/tools/atlas/") and not _OPTIONS["atlas"]) and not (string.find(v, "/sysdep/arch/x86_x64/") and ((arch ~= "amd64") or (arch ~= "x86"))) then local src_file = string.sub(v, 1, -3) .. ".cpp" cxxtestsrcfiles { src_file } files { src_file } cxxtesthdrfiles { v } if _ACTION ~= "gmake" and _ACTION ~= "vs2010" then -- see detailed comment above. src_file = path.rebase(src_file, path.getabsolute("."), _OPTIONS["outpath"]) v = path.rebase(v, path.getabsolute("."), _OPTIONS["outpath"]) if os.is("windows") then src_file = path.translate(src_file, "\\") v = path.translate(v, "\\") end prebuildcommands { lcxxtestpath.." --part "..lcxxtestoptions.." -o "..src_file.." "..v } end end end end function setup_tests() local target_type = get_main_project_target_type() project_create("test", target_type) configure_cxxtestgen() links { static_lib_names } links { "mocks_test" } if _OPTIONS["atlas"] then links { "AtlasObject" } project_add_extern_libs({"wxwidgets"}, target_type) end extra_params = { extra_files = { "test_setup.cpp" }, } project_add_contents(source_root, {}, {}, extra_params) project_add_extern_libs(used_extern_libs, target_type) project_add_x11_dirs() -- TODO: should fix the duplication between this OS-specific linking -- code, and the similar version in setup_main_exe if os.is("windows") then -- from "lowlevel" static lib; must be added here to be linked in files { source_root.."lib/sysdep/os/win/error_dialog.rc" } flags { "NoRTTI" } -- see wstartup.h linkoptions { "/INCLUDE:_wstartup_InitAndRegisterShutdown" } project_add_manifest() elseif os.is("linux") or os.is("bsd") then if not _OPTIONS["android"] and not (os.getversion().description == "OpenBSD") then links { "rt" } end if _OPTIONS["android"] then -- NDK's STANDALONE-TOOLCHAIN.html says this is required linkoptions { "-Wl,--fix-cortex-a8" } end if os.is("linux") or os.getversion().description == "GNU/kFreeBSD" then links { -- Dynamic libraries (needed for linking for gold) "dl", } elseif os.is("bsd") then links { -- Needed for backtrace* on BSDs "execinfo", } end -- Threading support buildoptions { "-pthread" } if not _OPTIONS["android"] then linkoptions { "-pthread" } end -- For debug_resolve_symbol configuration "Debug" linkoptions { "-rdynamic" } configuration { } includedirs { source_root .. "pch/test/" } end end -- must come first, so that VC sets it as the default project and therefore -- allows running via F5 without the "where is the EXE" dialog. setup_main_exe() setup_all_libs() -- add the static libs to the main EXE project. only now (after -- setup_all_libs has run) are the lib names known. cannot move -- setup_main_exe to run after setup_all_libs (see comment above). -- we also don't want to hardcode the names - that would require more -- work when changing the static lib breakdown. project("pyrogenesis") -- Set the main project active links { static_lib_names } if _OPTIONS["atlas"] then setup_atlas_projects() setup_atlas_frontends() end if _OPTIONS["collada"] then setup_collada_projects() end if not _OPTIONS["without-tests"] then setup_tests() end Index: ps/trunk/source/graphics/MapReader.cpp =================================================================== --- ps/trunk/source/graphics/MapReader.cpp (revision 13937) +++ ps/trunk/source/graphics/MapReader.cpp (revision 13938) @@ -1,1516 +1,1520 @@ /* 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 "MapReader.h" #include "graphics/Camera.h" #include "graphics/CinemaTrack.h" #include "graphics/Entity.h" #include "graphics/GameView.h" #include "graphics/MapGenerator.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "graphics/TerrainTextureEntry.h" #include "graphics/TerrainTextureManager.h" #include "lib/timer.h" #include "lib/external_libraries/libsdl.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/Loader.h" #include "ps/LoaderThunks.h" #include "ps/World.h" #include "ps/XML/Xeromyces.h" #include "renderer/PostprocManager.h" #include "renderer/SkyManager.h" #include "renderer/WaterManager.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpObstruction.h" #include "simulation2/components/ICmpOwnership.h" #include "simulation2/components/ICmpPlayer.h" #include "simulation2/components/ICmpPlayerManager.h" #include "simulation2/components/ICmpPosition.h" #include "simulation2/components/ICmpTerrain.h" #include "simulation2/components/ICmpVisual.h" #include "simulation2/components/ICmpWaterManager.h" #include CMapReader::CMapReader() : xml_reader(0), m_PatchesPerSide(0), m_MapGen(0) { cur_terrain_tex = 0; // important - resets generator state // Maps that don't override the default probably want the old lighting model //m_LightEnv.SetLightingModel("old"); //pPostproc->SetPostEffect(L"default"); } // 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) { // latch parameters (held until DelayedLoadFinished) pTerrain = pTerrain_; pLightEnv = pLightEnv_; pGameView = pGameView_; pWaterMan = pWaterMan_; pSkyMan = pSkyMan_; pCinema = pCinema_; pTrigMan = pTrigMan_; pPostproc = pPostproc_; pSimulation2 = pSimulation2_; pSimContext = pSimContext_; m_PlayerID = playerID_; m_SkipEntities = skipEntities; m_StartingCameraTarget = INVALID_ENTITY; + m_ScriptSettings = settings; filename_xml = pathname.ChangeExtension(L".xml"); // In some cases (particularly tests) we don't want to bother storing a large // mostly-empty .pmp file, so we let the XML file specify basic terrain instead. // If there's an .xml file and no .pmp, then we're probably in this XML-only mode only_xml = false; if (!VfsFileExists(pathname) && VfsFileExists(filename_xml)) { only_xml = true; } file_format_version = CMapIO::FILE_VERSION; // default if there's no .pmp if (!only_xml) { // [25ms] unpacker.Read(pathname, "PSMP"); file_format_version = unpacker.GetVersion(); } // check oldest supported version if (file_format_version < FILE_READ_VERSION) throw PSERROR_File_InvalidVersion(); // delete all existing entities if (pSimulation2) pSimulation2->ResetState(); // reset post effects 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); // unpack the data if (!only_xml) RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1200); // read the corresponding XML file RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 5800); // apply data to the world RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5); // load map settings script (must be done after reading map) RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5); 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_, CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_, CSimulation2 *pSimulation2_, int playerID_) { // latch parameters (held until DelayedLoadFinished) m_ScriptFile = scriptFile; m_ScriptSettings = settings; pTerrain = pTerrain_; pLightEnv = pLightEnv_; pGameView = pGameView_; pWaterMan = pWaterMan_; pSkyMan = pSkyMan_; pCinema = pCinema_; pTrigMan = pTrigMan_; pPostproc = pPostproc_; pSimulation2 = pSimulation2_; pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL; m_PlayerID = playerID_; m_SkipEntities = false; m_StartingCameraTarget = INVALID_ENTITY; // delete all existing entities if (pSimulation2) pSimulation2->ResetState(); only_xml = false; // copy random map settings (before entity creation) 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); // load map generator with random map script RegMemFun(this, &CMapReader::GenerateMap, L"CMapReader::GenerateMap", 5000); // parse RMS results into terrain structure RegMemFun(this, &CMapReader::ParseTerrain, L"CMapReader::ParseTerrain", 500); // parse RMS results into environment settings RegMemFun(this, &CMapReader::ParseEnvironment, L"CMapReader::ParseEnvironment", 5); // parse RMS results into camera settings RegMemFun(this, &CMapReader::ParseCamera, L"CMapReader::ParseCamera", 5); // parse RMS results into entities RegMemFun(this, &CMapReader::ParseEntities, L"CMapReader::ParseEntities", 1000); // apply data to the world RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5); // load map settings script (must be done after reading map) RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5); RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5); } // UnpackMap: unpack the given data from the raw data stream into local variables int CMapReader::UnpackMap() { // now unpack everything into local data int ret = UnpackTerrain(); if (ret != 0) // failed or timed out { return ret; } return 0; } // UnpackTerrain: unpack the terrain from the end of the input data stream // - data: map size, heightmap, list of textures used by map, texture tile assignments int CMapReader::UnpackTerrain() { // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. const double end_time = timer_Time() + 200e-3; // first call to generator (this is skipped after first call, // i.e. when the loop below was interrupted) if (cur_terrain_tex == 0) { m_PatchesPerSide = (ssize_t)unpacker.UnpackSize(); // unpack heightmap [600us] size_t verticesPerSide = m_PatchesPerSide*PATCH_SIZE+1; m_Heightmap.resize(SQR(verticesPerSide)); unpacker.UnpackRaw(&m_Heightmap[0], SQR(verticesPerSide)*sizeof(u16)); // unpack # textures num_terrain_tex = unpacker.UnpackSize(); m_TerrainTextures.reserve(num_terrain_tex); } // unpack texture names; find handle for each texture. // interruptible. while (cur_terrain_tex < num_terrain_tex) { CStr texturename; unpacker.UnpackString(texturename); ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled) CTerrainTextureEntry* texentry = g_TexMan.FindTexture(texturename); m_TerrainTextures.push_back(texentry); cur_terrain_tex++; LDR_CHECK_TIMEOUT(cur_terrain_tex, num_terrain_tex); } // unpack tile data [3ms] ssize_t tilesPerSide = m_PatchesPerSide*PATCH_SIZE; m_Tiles.resize(size_t(SQR(tilesPerSide))); unpacker.UnpackRaw(&m_Tiles[0], sizeof(STileDesc)*m_Tiles.size()); // reset generator state. cur_terrain_tex = 0; return 0; } // ApplyData: take all the input data, and rebuild the scene from it int CMapReader::ApplyData() { if (m_PatchesPerSide == 0) { // we'll probably crash when trying to use this map later throw PSERROR_Game_World_MapLoadFailed("Error loading map: no terrain data.\nCheck application log for details."); } if (!only_xml) { // initialise the terrain pTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]); // setup the textures on the minipatches STileDesc* tileptr = &m_Tiles[0]; for (ssize_t j=0; jGetPatch(i,j)->m_MiniPatches[m][k]; // can't fail mp.Tex = m_TerrainTextures[tileptr->m_Tex1Index]; mp.Priority = tileptr->m_Priority; tileptr++; } } } } } // copy over the lighting parameters if (pLightEnv) *pLightEnv = m_LightEnv; CmpPtr cmpPlayerManager(*pSimContext, SYSTEM_ENTITY); if (pGameView && cmpPlayerManager) { // Default to global camera (with constraints) pGameView->ResetCameraTarget(pGameView->GetCamera()->GetFocus()); // TODO: Starting rotation? CmpPtr cmpPlayer(*pSimContext, cmpPlayerManager->GetPlayerByID(m_PlayerID)); if (cmpPlayer && cmpPlayer->HasStartingCamera()) { // Use player starting camera CFixedVector3D pos = cmpPlayer->GetStartingCameraPos(); pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat())); } else if (m_StartingCameraTarget != INVALID_ENTITY) { // Point camera at entity CmpPtr cmpPosition(*pSimContext, m_StartingCameraTarget); if (cmpPosition) { CFixedVector3D pos = cmpPosition->GetPosition(); pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat())); } } } CmpPtr cmpTerrain(*pSimContext, SYSTEM_ENTITY); if (cmpTerrain) cmpTerrain->ReloadTerrain(); return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////// PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname) { VfsPath filename_xml = pathname.ChangeExtension(L".xml"); CXeromyces xmb_file; if (xmb_file.Load(g_VFS, filename_xml) != PSRETURN_OK) return PSRETURN_File_ReadFailed; // Define all the relevant elements used in the XML file #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(scenario); EL(scriptsettings); #undef AT #undef EL XMBElement root = xmb_file.GetRoot(); ENSURE(root.GetNodeName() == el_scenario); XERO_ITER_EL(root, child) { int child_name = child.GetNodeName(); if (child_name == el_scriptsettings) { m_ScriptSettings = child.GetText(); } } return PSRETURN_OK; } CScriptValRooted CMapSummaryReader::GetMapSettings(ScriptInterface& scriptInterface) { CScriptValRooted data; scriptInterface.Eval("({})", data); if (!m_ScriptSettings.empty()) scriptInterface.SetProperty(data.get(), "settings", scriptInterface.ParseJSON(m_ScriptSettings), false); return data; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Holds various state data while reading maps, so that loading can be // interrupted (e.g. to update the progress display) then later resumed. class CXMLReader { NONCOPYABLE(CXMLReader); public: CXMLReader(const VfsPath& xml_filename, CMapReader& mapReader) : m_MapReader(mapReader) { Init(xml_filename); } CStr ReadScriptSettings(); // return semantics: see Loader.cpp!LoadFunc. int ProgressiveRead(); private: CXeromyces xmb_file; CMapReader& m_MapReader; int el_entity; int el_tracks; int el_template, el_player; int el_position, el_orientation, el_obstruction; int el_actor; int at_x, at_y, at_z; int at_group, at_group2; int at_id; int at_angle; int at_uid; int at_seed; XMBElementList nodes; // children of root // loop counters int node_idx; int entity_idx; // # entities+nonentities processed and total (for progress calc) int completed_jobs, total_jobs; // maximum used entity ID, so we can safely allocate new ones entity_id_t max_uid; void Init(const VfsPath& xml_filename); void ReadTerrain(XMBElement parent); void ReadEnvironment(XMBElement parent); void ReadCamera(XMBElement parent); void ReadCinema(XMBElement parent); void ReadTriggers(XMBElement parent); int ReadEntities(XMBElement parent, double end_time); }; void CXMLReader::Init(const VfsPath& xml_filename) { // must only assign once, so do it here node_idx = entity_idx = 0; if (xmb_file.Load(g_VFS, xml_filename) != PSRETURN_OK) throw PSERROR_File_ReadFailed(); // define the elements and attributes that are frequently used in the XML file, // so we don't need to do lots of string construction and comparison when // reading the data. // (Needs to be synchronised with the list in CXMLReader - ugh) #define EL(x) el_##x = xmb_file.GetElementID(#x) #define AT(x) at_##x = xmb_file.GetAttributeID(#x) EL(entity); EL(tracks); EL(template); EL(player); EL(position); EL(orientation); EL(obstruction); EL(actor); AT(x); AT(y); AT(z); AT(group); AT(group2); AT(angle); AT(uid); AT(seed); #undef AT #undef EL XMBElement root = xmb_file.GetRoot(); ENSURE(xmb_file.GetElementString(root.GetNodeName()) == "Scenario"); nodes = root.GetChildNodes(); // find out total number of entities+nonentities // (used when calculating progress) completed_jobs = 0; total_jobs = 0; for (int i = 0; i < nodes.Count; i++) total_jobs += nodes.Item(i).GetChildNodes().Count; // Find the maximum entity ID, so we can safely allocate new IDs without conflicts max_uid = SYSTEM_ENTITY; XMBElement ents = nodes.GetFirstNamedItem(xmb_file.GetElementID("Entities")); XERO_ITER_EL(ents, ent) { CStr uid = ent.GetAttributes().GetNamedItem(at_uid); max_uid = std::max(max_uid, (entity_id_t)uid.ToUInt()); } } CStr CXMLReader::ReadScriptSettings() { XMBElement root = xmb_file.GetRoot(); ENSURE(xmb_file.GetElementString(root.GetNodeName()) == "Scenario"); nodes = root.GetChildNodes(); XMBElement settings = nodes.GetFirstNamedItem(xmb_file.GetElementID("ScriptSettings")); return settings.GetText(); } void CXMLReader::ReadTerrain(XMBElement parent) { #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) AT(patches); AT(texture); AT(priority); AT(height); #undef AT ssize_t patches = 9; CStr texture = "grass1_spring"; int priority = 0; u16 height = 16384; XERO_ITER_ATTR(parent, attr) { if (attr.Name == at_patches) patches = attr.Value.ToInt(); else if (attr.Name == at_texture) texture = attr.Value; else if (attr.Name == at_priority) priority = attr.Value.ToInt(); else if (attr.Name == at_height) height = (u16)attr.Value.ToInt(); } m_MapReader.m_PatchesPerSide = patches; // Load the texture ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled) CTerrainTextureEntry* texentry = g_TexMan.FindTexture(texture); m_MapReader.pTerrain->Initialize(patches, NULL); // Fill the heightmap u16* heightmap = m_MapReader.pTerrain->GetHeightMap(); ssize_t verticesPerSide = m_MapReader.pTerrain->GetVerticesPerSide(); for (ssize_t i = 0; i < SQR(verticesPerSide); ++i) heightmap[i] = height; // Fill the texture map for (ssize_t pz = 0; pz < patches; ++pz) { for (ssize_t px = 0; px < patches; ++px) { CPatch* patch = m_MapReader.pTerrain->GetPatch(px, pz); // can't fail for (ssize_t z = 0; z < PATCH_SIZE; ++z) { for (ssize_t x = 0; x < PATCH_SIZE; ++x) { patch->m_MiniPatches[z][x].Tex = texentry; patch->m_MiniPatches[z][x].Priority = priority; } } } } } void CXMLReader::ReadEnvironment(XMBElement parent) { #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(lightingmodel); EL(posteffect); EL(skyset); EL(suncolour); EL(sunelevation); EL(sunrotation); EL(terrainambientcolour); EL(unitsambientcolour); EL(water); EL(waterbody); EL(type); EL(colour); EL(height); EL(shininess); EL(waviness); EL(murkiness); EL(tint); EL(reflectiontint); EL(reflectiontintstrength); EL(fog); EL(fogcolour); EL(fogfactor); EL(fogthickness); EL(postproc); EL(brightness); EL(contrast); EL(saturation); EL(bloom); AT(r); AT(g); AT(b); #undef AT #undef EL XERO_ITER_EL(parent, element) { int element_name = element.GetNodeName(); XMBAttributeList attrs = element.GetAttributes(); if (element_name == el_lightingmodel) { // NOP - obsolete. } else if (element_name == el_skyset) { if (m_MapReader.pSkyMan) m_MapReader.pSkyMan->SetSkySet(element.GetText().FromUTF8()); } else if (element_name == el_suncolour) { m_MapReader.m_LightEnv.m_SunColor = RGBColor( attrs.GetNamedItem(at_r).ToFloat(), attrs.GetNamedItem(at_g).ToFloat(), attrs.GetNamedItem(at_b).ToFloat()); } else if (element_name == el_sunelevation) { m_MapReader.m_LightEnv.m_Elevation = attrs.GetNamedItem(at_angle).ToFloat(); } else if (element_name == el_sunrotation) { m_MapReader.m_LightEnv.m_Rotation = attrs.GetNamedItem(at_angle).ToFloat(); } else if (element_name == el_terrainambientcolour) { m_MapReader.m_LightEnv.m_TerrainAmbientColor = RGBColor( attrs.GetNamedItem(at_r).ToFloat(), attrs.GetNamedItem(at_g).ToFloat(), attrs.GetNamedItem(at_b).ToFloat()); } else if (element_name == el_unitsambientcolour) { m_MapReader.m_LightEnv.m_UnitsAmbientColor = RGBColor( attrs.GetNamedItem(at_r).ToFloat(), attrs.GetNamedItem(at_g).ToFloat(), attrs.GetNamedItem(at_b).ToFloat()); } else if (element_name == el_fog) { XERO_ITER_EL(element, fog) { int element_name = fog.GetNodeName(); if (element_name == el_fogcolour) { XMBAttributeList attrs = fog.GetAttributes(); m_MapReader.m_LightEnv.m_FogColor = RGBColor( attrs.GetNamedItem(at_r).ToFloat(), attrs.GetNamedItem(at_g).ToFloat(), attrs.GetNamedItem(at_b).ToFloat()); } else if (element_name == el_fogfactor) { m_MapReader.m_LightEnv.m_FogFactor = fog.GetText().ToFloat(); } else if (element_name == el_fogthickness) { m_MapReader.m_LightEnv.m_FogMax = fog.GetText().ToFloat(); } } } else if (element_name == el_postproc) { XERO_ITER_EL(element, postproc) { int element_name = postproc.GetNodeName(); if (element_name == el_brightness) { m_MapReader.m_LightEnv.m_Brightness = postproc.GetText().ToFloat(); } else if (element_name == el_contrast) { m_MapReader.m_LightEnv.m_Contrast = postproc.GetText().ToFloat(); } else if (element_name == el_saturation) { m_MapReader.m_LightEnv.m_Saturation = postproc.GetText().ToFloat(); } else if (element_name == el_bloom) { m_MapReader.m_LightEnv.m_Bloom = postproc.GetText().ToFloat(); } else if (element_name == el_posteffect) { if (m_MapReader.pPostproc) m_MapReader.pPostproc->SetPostEffect(postproc.GetText().FromUTF8()); } } } else if (element_name == el_water) { XERO_ITER_EL(element, waterbody) { ENSURE(waterbody.GetNodeName() == el_waterbody); XERO_ITER_EL(waterbody, waterelement) { int element_name = waterelement.GetNodeName(); if (element_name == el_height) { CmpPtr cmpWaterManager(*m_MapReader.pSimContext, SYSTEM_ENTITY); ENSURE(cmpWaterManager); cmpWaterManager->SetWaterLevel(entity_pos_t::FromString(waterelement.GetText())); continue; } // The rest are purely graphical effects, and should be ignored if // graphics are disabled if (!m_MapReader.pWaterMan) continue; if (element_name == el_type) { // TODO: implement this, when WaterManager supports it } #define READ_COLOUR(el, out) \ else if (element_name == el) \ { \ XMBAttributeList attrs = waterelement.GetAttributes(); \ out = CColor( \ attrs.GetNamedItem(at_r).ToFloat(), \ attrs.GetNamedItem(at_g).ToFloat(), \ attrs.GetNamedItem(at_b).ToFloat(), \ 1.f); \ } #define READ_FLOAT(el, out) \ else if (element_name == el) \ { \ out = waterelement.GetText().ToFloat(); \ } \ READ_COLOUR(el_colour, m_MapReader.pWaterMan->m_WaterColor) READ_FLOAT(el_shininess, m_MapReader.pWaterMan->m_Shininess) READ_FLOAT(el_waviness, m_MapReader.pWaterMan->m_Waviness) READ_FLOAT(el_murkiness, m_MapReader.pWaterMan->m_Murkiness) READ_COLOUR(el_tint, m_MapReader.pWaterMan->m_WaterTint) READ_COLOUR(el_reflectiontint, m_MapReader.pWaterMan->m_ReflectionTint) READ_FLOAT(el_reflectiontintstrength, m_MapReader.pWaterMan->m_ReflectionTintStrength) #undef READ_FLOAT #undef READ_COLOUR else debug_warn(L"Invalid map XML data"); } } } else debug_warn(L"Invalid map XML data"); } m_MapReader.m_LightEnv.CalculateSunDirection(); } void CXMLReader::ReadCamera(XMBElement parent) { // defaults if we don't find player starting camera #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(declination); EL(rotation); EL(position); AT(angle); AT(x); AT(y); AT(z); #undef AT #undef EL float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f); CVector3D translation = CVector3D(100, 150, -100); XERO_ITER_EL(parent, element) { int element_name = element.GetNodeName(); XMBAttributeList attrs = element.GetAttributes(); if (element_name == el_declination) { declination = attrs.GetNamedItem(at_angle).ToFloat(); } else if (element_name == el_rotation) { rotation = attrs.GetNamedItem(at_angle).ToFloat(); } else if (element_name == el_position) { translation = CVector3D( attrs.GetNamedItem(at_x).ToFloat(), attrs.GetNamedItem(at_y).ToFloat(), attrs.GetNamedItem(at_z).ToFloat()); } else debug_warn(L"Invalid map XML data"); } if (m_MapReader.pGameView) { m_MapReader.pGameView->GetCamera()->m_Orientation.SetXRotation(declination); m_MapReader.pGameView->GetCamera()->m_Orientation.RotateY(rotation); m_MapReader.pGameView->GetCamera()->m_Orientation.Translate(translation); m_MapReader.pGameView->GetCamera()->UpdateFrustum(); } } void CXMLReader::ReadCinema(XMBElement parent) { #define EL(x) int el_##x = xmb_file.GetElementID(#x) #define AT(x) int at_##x = xmb_file.GetAttributeID(#x) EL(path); EL(rotation); EL(distortion); EL(node); EL(position); EL(time); AT(name); AT(timescale); AT(mode); AT(style); AT(growth); AT(switch); AT(x); AT(y); AT(z); #undef EL #undef AT std::map pathList; XERO_ITER_EL(parent, element) { int elementName = element.GetNodeName(); if ( elementName == el_path ) { XMBAttributeList attrs = element.GetAttributes(); CStrW name(attrs.GetNamedItem(at_name).FromUTF8()); float timescale = attrs.GetNamedItem(at_timescale).ToFloat(); CCinemaData pathData; pathData.m_Timescale = timescale; TNSpline spline, backwardSpline; XERO_ITER_EL(element, pathChild) { elementName = pathChild.GetNodeName(); attrs = pathChild.GetAttributes(); //Load distortion attributes if ( elementName == el_distortion ) { pathData.m_Mode = attrs.GetNamedItem(at_mode).ToInt(); pathData.m_Style = attrs.GetNamedItem(at_style).ToInt(); pathData.m_Growth = attrs.GetNamedItem(at_growth).ToInt(); pathData.m_Switch = attrs.GetNamedItem(at_switch).ToInt(); } //Load node data used for spline else if ( elementName == el_node ) { SplineData data; XERO_ITER_EL(pathChild, nodeChild) { elementName = nodeChild.GetNodeName(); attrs = nodeChild.GetAttributes(); //Fix?: assumes that time is last element if ( elementName == el_position ) { data.Position.X = attrs.GetNamedItem(at_x).ToFloat(); data.Position.Y = attrs.GetNamedItem(at_y).ToFloat(); data.Position.Z = attrs.GetNamedItem(at_z).ToFloat(); continue; } else if ( elementName == el_rotation ) { data.Rotation.X = attrs.GetNamedItem(at_x).ToFloat(); data.Rotation.Y = attrs.GetNamedItem(at_y).ToFloat(); data.Rotation.Z = attrs.GetNamedItem(at_z).ToFloat(); continue; } else if ( elementName == el_time ) data.Distance = nodeChild.GetText().ToFloat(); else debug_warn(L"Invalid cinematic element for node child"); backwardSpline.AddNode(data.Position, data.Rotation, data.Distance); } } else debug_warn(L"Invalid cinematic element for path child"); } //Construct cinema path with data gathered CCinemaPath temp(pathData, backwardSpline); const std::vector& nodes = temp.GetAllNodes(); if ( nodes.empty() ) { debug_warn(L"Failure loading cinematics"); return; } for ( std::vector::const_reverse_iterator it = nodes.rbegin(); it != nodes.rend(); ++it ) { spline.AddNode(it->Position, it->Rotation, it->Distance); } CCinemaPath path(pathData, spline); pathList[name] = path; } else ENSURE("Invalid cinema child"); } if (m_MapReader.pCinema) m_MapReader.pCinema->SetAllPaths(pathList); } void CXMLReader::ReadTriggers(XMBElement UNUSED(parent)) { } int CXMLReader::ReadEntities(XMBElement parent, double end_time) { XMBElementList entities = parent.GetChildNodes(); ENSURE(m_MapReader.pSimulation2); CSimulation2& sim = *m_MapReader.pSimulation2; CmpPtr cmpPlayerManager(sim, SYSTEM_ENTITY); while (entity_idx < entities.Count) { // all new state at this scope and below doesn't need to be // wrapped, since we only yield after a complete iteration. XMBElement entity = entities.Item(entity_idx++); ENSURE(entity.GetNodeName() == el_entity); XMBAttributeList attrs = entity.GetAttributes(); CStr uid = attrs.GetNamedItem(at_uid); ENSURE(!uid.empty()); int EntityUid = uid.ToInt(); CStrW TemplateName; int PlayerID = 0; CFixedVector3D Position; CFixedVector3D Orientation; long Seed = -1; // Obstruction control groups. entity_id_t ControlGroup = INVALID_ENTITY; entity_id_t ControlGroup2 = INVALID_ENTITY; XERO_ITER_EL(entity, setting) { int element_name = setting.GetNodeName(); //