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();
//
if (element_name == el_template)
{
TemplateName = setting.GetText().FromUTF8();
}
//
else if (element_name == el_player)
{
PlayerID = setting.GetText().ToInt();
}
//
else if (element_name == el_position)
{
XMBAttributeList attrs = setting.GetAttributes();
Position = CFixedVector3D(
fixed::FromString(attrs.GetNamedItem(at_x)),
fixed::FromString(attrs.GetNamedItem(at_y)),
fixed::FromString(attrs.GetNamedItem(at_z)));
}
//
else if (element_name == el_orientation)
{
XMBAttributeList attrs = setting.GetAttributes();
Orientation = CFixedVector3D(
fixed::FromString(attrs.GetNamedItem(at_x)),
fixed::FromString(attrs.GetNamedItem(at_y)),
fixed::FromString(attrs.GetNamedItem(at_z)));
// TODO: what happens if some attributes are missing?
}
//
else if (element_name == el_obstruction)
{
XMBAttributeList attrs = setting.GetAttributes();
ControlGroup = attrs.GetNamedItem(at_group).ToInt();
ControlGroup2 = attrs.GetNamedItem(at_group2).ToInt();
}
//
else if (element_name == el_actor)
{
XMBAttributeList attrs = setting.GetAttributes();
CStr seedStr = attrs.GetNamedItem(at_seed);
if (!seedStr.empty())
{
Seed = seedStr.ToLong();
ENSURE(Seed >= 0);
}
}
else
debug_warn(L"Invalid map XML data");
}
entity_id_t ent = sim.AddEntity(TemplateName, EntityUid);
entity_id_t player = cmpPlayerManager->GetPlayerByID(PlayerID);
if (ent == INVALID_ENTITY || player == INVALID_ENTITY)
{ // Don't add entities with invalid player IDs
LOGERROR(L"Failed to load entity template '%ls'", TemplateName.c_str());
}
else
{
CmpPtr cmpPosition(sim, ent);
if (cmpPosition)
{
cmpPosition->JumpTo(Position.X, Position.Z);
cmpPosition->SetYRotation(Orientation.Y);
// TODO: other parts of the position
}
CmpPtr cmpOwnership(sim, ent);
if (cmpOwnership)
cmpOwnership->SetOwner(PlayerID);
CmpPtr cmpObstruction(sim, ent);
if (cmpObstruction)
{
if (ControlGroup != INVALID_ENTITY)
cmpObstruction->SetControlGroup(ControlGroup);
if (ControlGroup2 != INVALID_ENTITY)
cmpObstruction->SetControlGroup2(ControlGroup2);
cmpObstruction->ResolveFoundationCollisions();
}
CmpPtr cmpVisual(sim, ent);
if (cmpVisual)
{
if (Seed != -1)
cmpVisual->SetActorSeed((u32)Seed);
// TODO: variation/selection strings
}
if (PlayerID == m_MapReader.m_PlayerID && (boost::algorithm::ends_with(TemplateName, L"civil_centre") || m_MapReader.m_StartingCameraTarget == INVALID_ENTITY))
{
// Focus on civil centre or first entity owned by player
m_MapReader.m_StartingCameraTarget = ent;
}
}
completed_jobs++;
LDR_CHECK_TIMEOUT(completed_jobs, total_jobs);
}
return 0;
}
int CXMLReader::ProgressiveRead()
{
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = timer_Time() + 200e-3;
int ret;
while (node_idx < nodes.Count)
{
XMBElement node = nodes.Item(node_idx);
CStr name = xmb_file.GetElementString(node.GetNodeName());
if (name == "Terrain")
{
ReadTerrain(node);
}
else if (name == "Environment")
{
ReadEnvironment(node);
}
else if (name == "Camera")
{
ReadCamera(node);
}
else if (name == "ScriptSettings")
{
//Already loaded - this is to prevent an assertion
}
else if (name == "Entities")
{
if (!m_MapReader.m_SkipEntities)
{
ret = ReadEntities(node, end_time);
if (ret != 0) // error or timed out
return ret;
}
}
else if (name == "Paths")
{
ReadCinema(node);
}
else if (name == "Triggers")
{
ReadTriggers(node);
}
else if (name == "Script")
{
if (m_MapReader.pSimulation2)
m_MapReader.pSimulation2->SetStartupScript(node.GetText());
}
else
{
debug_printf(L"Invalid XML element in map file: %hs\n", name.c_str());
debug_warn(L"Invalid map XML data");
}
node_idx++;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// load script settings from map
int CMapReader::LoadScriptSettings()
{
if (!xml_reader)
xml_reader = new CXMLReader(filename_xml, *this);
// parse the script settings
if (pSimulation2)
pSimulation2->SetMapSettings(xml_reader->ReadScriptSettings());
return 0;
}
// load player settings script
int CMapReader::LoadPlayerSettings()
{
if (pSimulation2)
pSimulation2->LoadPlayerSettings(true);
return 0;
}
// load map settings script
int CMapReader::LoadMapSettings()
{
if (pSimulation2)
pSimulation2->LoadMapSettings();
return 0;
}
// progressive
int CMapReader::ReadXML()
{
if (!xml_reader)
xml_reader = new CXMLReader(filename_xml, *this);
int ret = xml_reader->ProgressiveRead();
// finished or failed
if (ret <= 0)
{
SAFE_DELETE(xml_reader);
}
return ret;
}
int CMapReader::DelayLoadFinished()
{
// we were dynamically allocated by CWorld::Initialize
delete this;
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int CMapReader::LoadRMSettings()
{
// copy random map settings over to sim
+ ENSURE(pSimulation2);
pSimulation2->SetMapSettings(m_ScriptSettings);
return 0;
}
int CMapReader::GenerateMap()
{
if (!m_MapGen)
{
// Initialize map generator
m_MapGen = new CMapGenerator();
VfsPath scriptPath;
if (m_ScriptFile.length())
scriptPath = L"maps/random/"+m_ScriptFile;
// Stringify settings to pass across threads
std::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(m_ScriptSettings.get());
// Try to generate map
m_MapGen->GenerateMap(scriptPath, scriptSettings);
}
// Check status
int progress = m_MapGen->GetProgress();
if (progress < 0)
{
// RMS failed - return to main menu
throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
}
else if (progress == 0)
{
// Finished, get results as StructuredClone object, which must be read to obtain the JS val
shared_ptr results = m_MapGen->GetResults();
// Parse data into simulation context
CScriptValRooted data(pSimulation2->GetScriptInterface().GetContext(), pSimulation2->GetScriptInterface().ReadStructuredClone(results));
if (data.undefined())
{
// RMS failed - return to main menu
throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
}
else
{
m_MapData = data;
}
}
else
{
// Still working
// Sleep for a while, slowing down the rendering thread
// to allow more CPU for the map generator thread
SDL_Delay(100);
}
// return progress
return progress;
};
int CMapReader::ParseTerrain()
{
TIMER(L"ParseTerrain");
// parse terrain from map data
// an error here should stop the loading process
#define GET_TERRAIN_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
{ LOGERROR(L"CMapReader::ParseTerrain() failed to get '%hs' property", #prop);\
throw PSERROR_Game_World_MapLoadFailed("Error parsing terrain data.\nCheck application log for details"); }
u32 size;
GET_TERRAIN_PROPERTY(m_MapData.get(), size, size)
m_PatchesPerSide = size / PATCH_SIZE;
// flat heightmap of u16 data
GET_TERRAIN_PROPERTY(m_MapData.get(), height, m_Heightmap)
// load textures
std::vector textureNames;
GET_TERRAIN_PROPERTY(m_MapData.get(), textureNames, textureNames)
num_terrain_tex = textureNames.size();
while (cur_terrain_tex < num_terrain_tex)
{
ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
CTerrainTextureEntry* texentry = g_TexMan.FindTexture(textureNames[cur_terrain_tex]);
m_TerrainTextures.push_back(texentry);
cur_terrain_tex++;
}
// build tile data
m_Tiles.resize(SQR(size));
CScriptValRooted tileData;
GET_TERRAIN_PROPERTY(m_MapData.get(), tileData, tileData)
// parse tile data object into flat arrays
std::vector tileIndex;
std::vector tilePriority;
GET_TERRAIN_PROPERTY(tileData.get(), index, tileIndex);
GET_TERRAIN_PROPERTY(tileData.get(), priority, tilePriority);
ENSURE(SQR(size) == tileIndex.size() && SQR(size) == tilePriority.size());
// reorder by patches and store
for (size_t x = 0; x < size; ++x)
{
size_t patchX = x / PATCH_SIZE;
size_t offX = x % PATCH_SIZE;
for (size_t y = 0; y < size; ++y)
{
size_t patchY = y / PATCH_SIZE;
size_t offY = y % PATCH_SIZE;
STileDesc tile;
tile.m_Tex1Index = tileIndex[y*size + x];
tile.m_Tex2Index = 0xFFFF;
tile.m_Priority = tilePriority[y*size + x];
m_Tiles[(patchY * m_PatchesPerSide + patchX) * SQR(PATCH_SIZE) + (offY * PATCH_SIZE + offX)] = tile;
}
}
// reset generator state
cur_terrain_tex = 0;
#undef GET_TERRAIN_PROPERTY
return 0;
}
int CMapReader::ParseEntities()
{
TIMER(L"ParseEntities");
// parse entities from map data
std::vector entities;
if (!pSimulation2->GetScriptInterface().GetProperty(m_MapData.get(), "entities", entities))
LOGWARNING(L"CMapReader::ParseEntities() failed to get 'entities' property");
CSimulation2& sim = *pSimulation2;
CmpPtr cmpPlayerManager(sim, SYSTEM_ENTITY);
size_t entity_idx = 0;
size_t num_entities = entities.size();
Entity currEnt;
while (entity_idx < num_entities)
{
// Get current entity struct
currEnt = entities[entity_idx];
entity_id_t ent = pSimulation2->AddEntity(currEnt.templateName, currEnt.entityID);
entity_id_t player = cmpPlayerManager->GetPlayerByID(currEnt.playerID);
if (ent == INVALID_ENTITY || player == INVALID_ENTITY)
{ // Don't add entities with invalid player IDs
LOGERROR(L"Failed to load entity template '%ls'", currEnt.templateName.c_str());
}
else
{
CmpPtr cmpPosition(sim, ent);
if (cmpPosition)
{
cmpPosition->JumpTo(currEnt.position.X, currEnt.position.Z);
cmpPosition->SetYRotation(currEnt.rotation.Y);
// TODO: other parts of the position
}
CmpPtr cmpOwnership(sim, ent);
if (cmpOwnership)
cmpOwnership->SetOwner(currEnt.playerID);
// Detect and fix collisions between foundation-blocking entities.
// This presently serves to copy wall tower control groups to wall
// segments, allowing players to expand RMS-generated walls.
CmpPtr cmpObstruction(sim, ent);
if (cmpObstruction)
cmpObstruction->ResolveFoundationCollisions();
if (currEnt.playerID == m_PlayerID && (boost::algorithm::ends_with(currEnt.templateName, L"civil_centre") || m_StartingCameraTarget == INVALID_ENTITY))
{
// Focus on civil centre or first entity owned by player
m_StartingCameraTarget = currEnt.entityID;
}
}
entity_idx++;
}
return 0;
}
int CMapReader::ParseEnvironment()
{
// parse environment settings from map data
#define GET_ENVIRONMENT_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING(L"CMapReader::ParseEnvironment() failed to get '%hs' property", #prop);
CScriptValRooted envObj;
GET_ENVIRONMENT_PROPERTY(m_MapData.get(), Environment, envObj)
if (envObj.undefined())
{
LOGWARNING(L"CMapReader::ParseEnvironment(): Environment settings not found");
return 0;
}
//m_LightEnv.SetLightingModel("standard");
if (pPostproc)
pPostproc->SetPostEffect(L"default");
std::wstring skySet;
GET_ENVIRONMENT_PROPERTY(envObj.get(), SkySet, skySet)
if (pSkyMan)
pSkyMan->SetSkySet(skySet);
CColor sunColor;
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunColour, sunColor)
m_LightEnv.m_SunColor = RGBColor(sunColor.r, sunColor.g, sunColor.b);
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunElevation, m_LightEnv.m_Elevation)
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunRotation, m_LightEnv.m_Rotation)
CColor terrainAmbientColor;
GET_ENVIRONMENT_PROPERTY(envObj.get(), TerrainAmbientColour, terrainAmbientColor)
m_LightEnv.m_TerrainAmbientColor = RGBColor(terrainAmbientColor.r, terrainAmbientColor.g, terrainAmbientColor.b);
CColor unitsAmbientColor;
GET_ENVIRONMENT_PROPERTY(envObj.get(), UnitsAmbientColour, unitsAmbientColor)
m_LightEnv.m_UnitsAmbientColor = RGBColor(unitsAmbientColor.r, unitsAmbientColor.g, unitsAmbientColor.b);
// Water properties
CScriptValRooted waterObj;
GET_ENVIRONMENT_PROPERTY(envObj.get(), Water, waterObj)
CScriptValRooted waterBodyObj;
GET_ENVIRONMENT_PROPERTY(waterObj.get(), WaterBody, waterBodyObj)
// Water level - necessary
float waterHeight;
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Height, waterHeight)
CmpPtr cmpWaterManager(*pSimulation2, SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
cmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(waterHeight));
// If we have graphics, get rest of settings
if (pWaterMan)
{
// TODO: Water type not implemented
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Colour, pWaterMan->m_WaterColor)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Shininess, pWaterMan->m_Shininess)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Waviness, pWaterMan->m_Waviness)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Murkiness, pWaterMan->m_Murkiness)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Tint, pWaterMan->m_WaterTint)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), ReflectionTint, pWaterMan->m_ReflectionTint)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), ReflectionTintStrength, pWaterMan->m_ReflectionTintStrength)
}
m_LightEnv.CalculateSunDirection();
#undef GET_ENVIRONMENT_PROPERTY
return 0;
}
int CMapReader::ParseCamera()
{
// parse camera settings from map data
// defaults if we don't find player starting camera
float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);
CVector3D translation = CVector3D(100, 150, -100);
#define GET_CAMERA_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING(L"CMapReader::ParseCamera() failed to get '%hs' property", #prop);
CScriptValRooted cameraObj;
GET_CAMERA_PROPERTY(m_MapData.get(), Camera, cameraObj)
if (!cameraObj.undefined())
{ // If camera property exists, read values
CFixedVector3D pos;
GET_CAMERA_PROPERTY(cameraObj.get(), Position, pos)
translation = pos;
GET_CAMERA_PROPERTY(cameraObj.get(), Rotation, rotation)
GET_CAMERA_PROPERTY(cameraObj.get(), Declination, declination)
}
#undef GET_CAMERA_PROPERTY
if (pGameView)
{
pGameView->GetCamera()->m_Orientation.SetXRotation(declination);
pGameView->GetCamera()->m_Orientation.RotateY(rotation);
pGameView->GetCamera()->m_Orientation.Translate(translation);
pGameView->GetCamera()->UpdateFrustum();
}
return 0;
}
CMapReader::~CMapReader()
{
// Cleaup objects
delete xml_reader;
delete m_MapGen;
}
Index: ps/trunk/source/graphics/MapReader.h
===================================================================
--- ps/trunk/source/graphics/MapReader.h (revision 13937)
+++ ps/trunk/source/graphics/MapReader.h (revision 13938)
@@ -1,182 +1,182 @@
-/* Copyright (C) 2011 Wildfire Games.
+/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_MAPREADER
#define INCLUDED_MAPREADER
#include "MapIO.h"
#include "lib/res/handle.h"
#include "ps/CStr.h"
#include "LightEnv.h"
#include "ps/FileIo.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/system/Entity.h"
class CObjectEntry;
class CTerrain;
class WaterManager;
class SkyManager;
class CLightEnv;
class CCinemaManager;
class CPostprocManager;
class CTriggerManager;
class CSimulation2;
class CSimContext;
class CTerrainTextureEntry;
class CScriptValRooted;
class ScriptInterface;
class CGameView;
class CXMLReader;
class CMapGenerator;
class CMapReader : public CMapIO
{
friend class CXMLReader;
public:
// constructor
CMapReader();
~CMapReader();
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
- void LoadMap(const VfsPath& pathname, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
+ void LoadMap(const VfsPath& pathname, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc, CSimulation2*, const CSimContext*,
int playerID, bool skipEntities);
void LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID);
private:
// Load script settings for use by scripts
int LoadScriptSettings();
// load player settings only
int LoadPlayerSettings();
// load map settings only
int LoadMapSettings();
// UnpackTerrain: unpack the terrain from the input stream
int UnpackTerrain();
//UnpackCinema: unpack the cinematic tracks from the input stream
int UnpackCinema();
// UnpackMap: unpack the given data from the raw data stream into local variables
int UnpackMap();
// ApplyData: take all the input data, and rebuild the scene from it
int ApplyData();
// ReadXML: read some other data (entities, etc) in XML format
int ReadXML();
// clean up everything used during delayed load
int DelayLoadFinished();
// Copy random map settings over to sim
int LoadRMSettings();
// Generate random map
int GenerateMap();
// Parse script data into terrain
int ParseTerrain();
// Parse script data into entities
int ParseEntities();
// Parse script data into environment
int ParseEnvironment();
// Parse script data into camera
int ParseCamera();
// size of map
ssize_t m_PatchesPerSide;
// heightmap for map
std::vector m_Heightmap;
// list of terrain textures used by map
std::vector m_TerrainTextures;
// tile descriptions for each tile
std::vector m_Tiles;
// lightenv stored in file
CLightEnv m_LightEnv;
// startup script
CStrW m_Script;
// random map data
CStrW m_ScriptFile;
CScriptValRooted m_ScriptSettings;
CScriptValRooted m_MapData;
CMapGenerator* m_MapGen;
// state latched by LoadMap and held until DelayedLoadFinished
CFileUnpacker unpacker;
CTerrain* pTerrain;
WaterManager* pWaterMan;
SkyManager* pSkyMan;
CPostprocManager* pPostproc;
CLightEnv* pLightEnv;
CGameView* pGameView;
CCinemaManager* pCinema;
CTriggerManager* pTrigMan;
CSimulation2* pSimulation2;
const CSimContext* pSimContext;
int m_PlayerID;
bool m_SkipEntities;
VfsPath filename_xml;
bool only_xml;
u32 file_format_version;
entity_id_t m_StartingCameraTarget;
CVector3D m_StartingCamera;
// UnpackTerrain generator state
size_t cur_terrain_tex;
size_t num_terrain_tex;
CXMLReader* xml_reader;
};
/**
* A restricted map reader that returns various summary information
* for use by scripts (particularly the GUI).
*/
class CMapSummaryReader
{
public:
/**
* Try to load a map file.
* @param pathname Path to .pmp or .xml file
*/
PSRETURN LoadMap(const VfsPath& pathname);
/**
* Returns a value of the form:
* @code
* {
* "settings": { ... contents of the map's ... }
* }
* @endcode
*/
CScriptValRooted GetMapSettings(ScriptInterface& scriptInterface);
private:
CStr m_ScriptSettings;
};
#endif
Index: ps/trunk/source/gui/scripting/ScriptFunctions.cpp
===================================================================
--- ps/trunk/source/gui/scripting/ScriptFunctions.cpp (revision 13937)
+++ ps/trunk/source/gui/scripting/ScriptFunctions.cpp (revision 13938)
@@ -1,746 +1,746 @@
/* 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 "scriptinterface/ScriptInterface.h"
#include "graphics/Camera.h"
#include "graphics/GameView.h"
#include "graphics/MapReader.h"
#include "gui/GUIManager.h"
#include "graphics/scripting/JSInterface_GameView.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "lib/sysdep/sysdep.h"
#include "maths/FixedVector3D.h"
#include "network/NetClient.h"
#include "network/NetServer.h"
#include "network/NetTurnManager.h"
#include "ps/CLogger.h"
#include "ps/CConsole.h"
#include "ps/Errors.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "ps/Hotkey.h"
#include "ps/Overlay.h"
#include "ps/ProfileViewer.h"
#include "ps/Pyrogenesis.h"
#include "ps/SavedGame.h"
#include "ps/scripting/JSInterface_ConfigDB.h"
#include "ps/scripting/JSInterface_Console.h"
#include "ps/UserReport.h"
#include "ps/GameSetup/Atlas.h"
#include "ps/GameSetup/Config.h"
#include "ps/ConfigDB.h"
#include "renderer/scripting/JSInterface_Renderer.h"
#include "tools/atlas/GameInterface/GameLoop.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpAIManager.h"
#include "simulation2/components/ICmpCommandQueue.h"
#include "simulation2/components/ICmpGuiInterface.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "simulation2/components/ICmpSelectable.h"
#include "simulation2/helpers/Selection.h"
#include "js/jsapi.h"
/*
* This file defines a set of functions that are available to GUI scripts, to allow
* interaction with the rest of the engine.
* Functions are exposed to scripts within the global object 'Engine', so
* scripts should call "Engine.FunctionName(...)" etc.
*/
extern void restart_mainloop_in_atlas(); // from main.cpp
namespace {
CScriptVal GetActiveGui(void* UNUSED(cbdata))
{
return OBJECT_TO_JSVAL(g_GUI->GetScriptObject());
}
void PushGuiPage(void* UNUSED(cbdata), std::wstring name, CScriptVal initData)
{
g_GUI->PushPage(name, initData);
}
void SwitchGuiPage(void* UNUSED(cbdata), std::wstring name, CScriptVal initData)
{
g_GUI->SwitchPage(name, initData);
}
void PopGuiPage(void* UNUSED(cbdata))
{
g_GUI->PopPage();
}
CScriptVal GuiInterfaceCall(void* cbdata, std::wstring name, CScriptVal data)
{
CGUIManager* guiManager = static_cast (cbdata);
if (!g_Game)
return JSVAL_VOID;
CSimulation2* sim = g_Game->GetSimulation2();
ENSURE(sim);
CmpPtr cmpGuiInterface(*sim, SYSTEM_ENTITY);
if (!cmpGuiInterface)
return JSVAL_VOID;
int player = -1;
if (g_Game)
player = g_Game->GetPlayerID();
CScriptValRooted arg (sim->GetScriptInterface().GetContext(), sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), data.get()));
CScriptVal ret (cmpGuiInterface->ScriptCall(player, name, arg.get()));
return guiManager->GetScriptInterface().CloneValueFromOtherContext(sim->GetScriptInterface(), ret.get());
}
void PostNetworkCommand(void* cbdata, CScriptVal cmd)
{
CGUIManager* guiManager = static_cast (cbdata);
if (!g_Game)
return;
CSimulation2* sim = g_Game->GetSimulation2();
ENSURE(sim);
CmpPtr cmpCommandQueue(*sim, SYSTEM_ENTITY);
if (!cmpCommandQueue)
return;
jsval cmd2 = sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), cmd.get());
cmpCommandQueue->PostNetworkCommand(cmd2);
}
std::vector PickEntitiesAtPoint(void* UNUSED(cbdata), int x, int y)
{
return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID(), false);
}
std::vector PickFriendlyEntitiesInRect(void* UNUSED(cbdata), int x0, int y0, int x1, int y1, int player)
{
return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player, false);
}
std::vector PickFriendlyEntitiesOnScreen(void* cbdata, int player)
{
return PickFriendlyEntitiesInRect(cbdata, 0, 0, g_xres, g_yres, player);
}
std::vector PickSimilarFriendlyEntities(void* UNUSED(cbdata), std::string templateName, bool includeOffScreen, bool matchRank, bool allowFoundations)
{
return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetPlayerID(), includeOffScreen, matchRank, false, allowFoundations);
}
CFixedVector3D GetTerrainAtScreenPoint(void* UNUSED(cbdata), int x, int y)
{
CVector3D pos = g_Game->GetView()->GetCamera()->GetWorldCoordinates(x, y, true);
return CFixedVector3D(fixed::FromFloat(pos.X), fixed::FromFloat(pos.Y), fixed::FromFloat(pos.Z));
}
std::wstring SetCursor(void* UNUSED(cbdata), std::wstring name)
{
std::wstring old = g_CursorName;
g_CursorName = name;
return old;
}
int GetPlayerID(void* UNUSED(cbdata))
{
if (g_Game)
return g_Game->GetPlayerID();
return -1;
}
void SetPlayerID(void* UNUSED(cbdata), int id)
{
if (g_Game)
g_Game->SetPlayerID(id);
}
std::wstring GetDefaultPlayerName(void* UNUSED(cbdata))
{
CStr playername;
CFG_GET_VAL("playername", String, playername);
std::wstring name = playername.FromUTF8();
if (!name.empty())
return name;
name = sys_get_user_name();
if (!name.empty())
return name;
return L"anonymous";
}
std::wstring GetDefaultMPServer(void* UNUSED(cbdata))
{
CStr server;
CFG_GET_VAL("multiplayerserver", String, server);
return server.FromUTF8();
}
void SaveMPConfig(void* UNUSED(cbdata), std::wstring playerName, std::wstring server)
{
g_ConfigDB.CreateValue(CFG_USER, "playername")->m_String = CStrW(playerName).ToUTF8();
g_ConfigDB.CreateValue(CFG_USER, "multiplayerserver")->m_String = CStrW(server).ToUTF8();
g_ConfigDB.WriteFile(CFG_USER);
}
void StartNetworkGame(void* UNUSED(cbdata))
{
ENSURE(g_NetServer);
g_NetServer->StartGame();
}
void StartGame(void* cbdata, CScriptVal attribs, int playerID)
{
CGUIManager* guiManager = static_cast (cbdata);
ENSURE(!g_NetServer);
ENSURE(!g_NetClient);
ENSURE(!g_Game);
g_Game = new CGame();
// Convert from GUI script context to sim script context
CSimulation2* sim = g_Game->GetSimulation2();
CScriptValRooted gameAttribs (sim->GetScriptInterface().GetContext(),
sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), attribs.get()));
g_Game->SetPlayerID(playerID);
g_Game->StartGame(gameAttribs, "");
}
CScriptVal StartSavedGame(void* cbdata, std::wstring name)
{
CGUIManager* guiManager = static_cast (cbdata);
ENSURE(!g_NetServer);
ENSURE(!g_NetClient);
ENSURE(!g_Game);
// Load the saved game data from disk
CScriptValRooted metadata;
std::string savedState;
Status err = SavedGames::Load(name, guiManager->GetScriptInterface(), metadata, savedState);
if (err < 0)
return CScriptVal();
g_Game = new CGame();
// Convert from GUI script context to sim script context
CSimulation2* sim = g_Game->GetSimulation2();
CScriptValRooted gameMetadata (sim->GetScriptInterface().GetContext(),
sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), metadata.get()));
CScriptValRooted gameInitAttributes;
sim->GetScriptInterface().GetProperty(gameMetadata.get(), "initAttributes", gameInitAttributes);
int playerID;
sim->GetScriptInterface().GetProperty(gameMetadata.get(), "player", playerID);
// Start the game
g_Game->SetPlayerID(playerID);
g_Game->StartGame(gameInitAttributes, savedState);
return metadata.get();
}
void SaveGame(void* cbdata, std::wstring filename, std::wstring description)
{
CGUIManager* guiManager = static_cast (cbdata);
if (SavedGames::Save(filename, description, *g_Game->GetSimulation2(), guiManager, g_Game->GetPlayerID()) < 0)
LOGERROR(L"Failed to save game");
}
void SaveGamePrefix(void* cbdata, std::wstring prefix, std::wstring description)
{
CGUIManager* guiManager = static_cast (cbdata);
if (SavedGames::SavePrefix(prefix, description, *g_Game->GetSimulation2(), guiManager, g_Game->GetPlayerID()) < 0)
LOGERROR(L"Failed to save game");
}
void SetNetworkGameAttributes(void* cbdata, CScriptVal attribs)
{
CGUIManager* guiManager = static_cast (cbdata);
ENSURE(g_NetServer);
g_NetServer->UpdateGameAttributes(attribs, guiManager->GetScriptInterface());
}
void StartNetworkHost(void* cbdata, std::wstring playerName)
{
CGUIManager* guiManager = static_cast (cbdata);
ENSURE(!g_NetClient);
ENSURE(!g_NetServer);
ENSURE(!g_Game);
g_NetServer = new CNetServer();
if (!g_NetServer->SetupConnection())
{
guiManager->GetScriptInterface().ReportError("Failed to start server");
SAFE_DELETE(g_NetServer);
return;
}
g_Game = new CGame();
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
if (!g_NetClient->SetupConnection("127.0.0.1"))
{
guiManager->GetScriptInterface().ReportError("Failed to connect to server");
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
}
}
void StartNetworkJoin(void* cbdata, std::wstring playerName, std::string serverAddress)
{
CGUIManager* guiManager = static_cast (cbdata);
ENSURE(!g_NetClient);
ENSURE(!g_NetServer);
ENSURE(!g_Game);
g_Game = new CGame();
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
if (!g_NetClient->SetupConnection(serverAddress))
{
guiManager->GetScriptInterface().ReportError("Failed to connect to server");
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
}
}
void DisconnectNetworkGame(void* UNUSED(cbdata))
{
// TODO: we ought to do async reliable disconnections
SAFE_DELETE(g_NetServer);
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
}
CScriptVal PollNetworkClient(void* cbdata)
{
CGUIManager* guiManager = static_cast (cbdata);
if (!g_NetClient)
return CScriptVal();
CScriptValRooted poll = g_NetClient->GuiPoll();
// Convert from net client context to GUI script context
return guiManager->GetScriptInterface().CloneValueFromOtherContext(g_NetClient->GetScriptInterface(), poll.get());
}
void AssignNetworkPlayer(void* UNUSED(cbdata), int playerID, std::string guid)
{
ENSURE(g_NetServer);
g_NetServer->AssignPlayer(playerID, guid);
}
void SendNetworkChat(void* UNUSED(cbdata), std::wstring message)
{
ENSURE(g_NetClient);
g_NetClient->SendChatMessage(message);
}
std::vector GetAIs(void* cbdata)
{
CGUIManager* guiManager = static_cast (cbdata);
return ICmpAIManager::GetAIs(guiManager->GetScriptInterface());
}
std::vector GetSavedGames(void* cbdata)
{
CGUIManager* guiManager = static_cast (cbdata);
return SavedGames::GetSavedGames(guiManager->GetScriptInterface());
}
bool DeleteSavedGame(void* UNUSED(cbdata), std::wstring name)
{
return SavedGames::DeleteSavedGame(name);
}
void OpenURL(void* UNUSED(cbdata), std::string url)
{
sys_open_url(url);
}
void RestartInAtlas(void* UNUSED(cbdata))
{
restart_mainloop_in_atlas();
}
bool AtlasIsAvailable(void* UNUSED(cbdata))
{
return ATLAS_IsAvailable();
}
bool IsAtlasRunning(void* UNUSED(cbdata))
{
return (g_AtlasGameLoop && g_AtlasGameLoop->running);
}
CScriptVal LoadMapSettings(void* cbdata, VfsPath pathname)
{
CGUIManager* guiManager = static_cast (cbdata);
CMapSummaryReader reader;
- if (reader.LoadMap(pathname.ChangeExtension(L".xml")) != PSRETURN_OK)
+ if (reader.LoadMap(pathname) != PSRETURN_OK)
return CScriptVal();
return reader.GetMapSettings(guiManager->GetScriptInterface()).get();
}
CScriptVal GetMapSettings(void* cbdata)
{
CGUIManager* guiManager = static_cast (cbdata);
if (!g_Game)
return CScriptVal();
return guiManager->GetScriptInterface().CloneValueFromOtherContext(
g_Game->GetSimulation2()->GetScriptInterface(),
g_Game->GetSimulation2()->GetMapSettings().get());
}
/**
* Get the current X coordinate of the camera.
*/
float CameraGetX(void* UNUSED(cbdata))
{
if (g_Game && g_Game->GetView())
return g_Game->GetView()->GetCameraX();
return -1;
}
/**
* Get the current Z coordinate of the camera.
*/
float CameraGetZ(void* UNUSED(cbdata))
{
if (g_Game && g_Game->GetView())
return g_Game->GetView()->GetCameraZ();
return -1;
}
/**
* Start / stop camera following mode
* @param entityid unit id to follow. If zero, stop following mode
*/
void CameraFollow(void* UNUSED(cbdata), entity_id_t entityid)
{
if (g_Game && g_Game->GetView())
g_Game->GetView()->CameraFollow(entityid, false);
}
/**
* Start / stop first-person camera following mode
* @param entityid unit id to follow. If zero, stop following mode
*/
void CameraFollowFPS(void* UNUSED(cbdata), entity_id_t entityid)
{
if (g_Game && g_Game->GetView())
g_Game->GetView()->CameraFollow(entityid, true);
}
/// Move camera to a 2D location
void CameraMoveTo(void* UNUSED(cbdata), entity_pos_t x, entity_pos_t z)
{
// called from JS; must not fail
if(!(g_Game && g_Game->GetWorld() && g_Game->GetView() && g_Game->GetWorld()->GetTerrain()))
return;
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
CVector3D target;
target.X = x.ToFloat();
target.Z = z.ToFloat();
target.Y = terrain->GetExactGroundLevel(target.X, target.Z);
g_Game->GetView()->MoveCameraTarget(target);
}
entity_id_t GetFollowedEntity(void* UNUSED(cbdata))
{
if (g_Game && g_Game->GetView())
return g_Game->GetView()->GetFollowedEntity();
return INVALID_ENTITY;
}
bool HotkeyIsPressed_(void* UNUSED(cbdata), std::string hotkeyName)
{
return HotkeyIsPressed(hotkeyName);
}
void DisplayErrorDialog(void* UNUSED(cbdata), std::wstring msg)
{
debug_DisplayError(msg.c_str(), DE_NO_DEBUG_INFO, NULL, NULL, NULL, 0, NULL, NULL);
}
CScriptVal GetProfilerState(void* cbdata)
{
CGUIManager* guiManager = static_cast (cbdata);
return g_ProfileViewer.SaveToJS(guiManager->GetScriptInterface());
}
bool IsUserReportEnabled(void* UNUSED(cbdata))
{
return g_UserReporter.IsReportingEnabled();
}
bool IsSplashScreenEnabled(void* UNUSED(cbdata))
{
bool splashScreenEnable = true;
CFG_GET_VAL("splashscreenenable", Bool, splashScreenEnable);
return splashScreenEnable;
}
void SetSplashScreenEnabled(void* UNUSED(cbdata), bool enabled)
{
CStr val = (enabled ? "true" : "false");
g_ConfigDB.CreateValue(CFG_USER, "splashscreenenable")->m_String = val;
g_ConfigDB.WriteFile(CFG_USER);
}
void SetUserReportEnabled(void* UNUSED(cbdata), bool enabled)
{
g_UserReporter.SetReportingEnabled(enabled);
}
std::string GetUserReportStatus(void* UNUSED(cbdata))
{
return g_UserReporter.GetStatus();
}
void SubmitUserReport(void* UNUSED(cbdata), std::string type, int version, std::wstring data)
{
g_UserReporter.SubmitReport(type.c_str(), version, utf8_from_wstring(data));
}
void SetSimRate(void* UNUSED(cbdata), float rate)
{
g_Game->SetSimRate(rate);
}
float GetSimRate(void* UNUSED(cbdata))
{
return g_Game->GetSimRate();
}
void SetTurnLength(void* UNUSED(cbdata), int length)
{
if (g_NetServer)
g_NetServer->SetTurnLength(length);
else
LOGERROR(L"Only network host can change turn length");
}
// Focus the game camera on a given position.
void SetCameraTarget(void* UNUSED(cbdata), float x, float y, float z)
{
g_Game->GetView()->ResetCameraTarget(CVector3D(x, y, z));
}
// Deliberately cause the game to crash.
// Currently implemented via access violation (read of address 0).
// Useful for testing the crashlog/stack trace code.
int Crash(void* UNUSED(cbdata))
{
debug_printf(L"Crashing at user's request.\n");
return *(volatile int*)0;
}
void DebugWarn(void* UNUSED(cbdata))
{
debug_warn(L"Warning at user's request.");
}
// Force a JS garbage collection cycle to take place immediately.
// Writes an indication of how long this took to the console.
void ForceGC(void* cbdata)
{
CGUIManager* guiManager = static_cast (cbdata);
double time = timer_Time();
JS_GC(guiManager->GetScriptInterface().GetContext());
time = timer_Time() - time;
g_Console->InsertMessage(L"Garbage collection completed in: %f", time);
}
void DumpSimState(void* UNUSED(cbdata))
{
OsPath path = psLogDir()/"sim_dump.txt";
std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
g_Game->GetSimulation2()->DumpDebugState(file);
}
void DumpTerrainMipmap(void* UNUSED(cbdata))
{
VfsPath filename(L"screenshots/terrainmipmap.png");
g_Game->GetWorld()->GetTerrain()->GetHeightMipmap().DumpToDisk(filename);
OsPath realPath;
g_VFS->GetRealPath(filename, realPath);
LOGMESSAGERENDER(L"Terrain mipmap written to '%ls'", realPath.string().c_str());
}
void EnableTimeWarpRecording(void* UNUSED(cbdata), unsigned int numTurns)
{
g_Game->GetTurnManager()->EnableTimeWarpRecording(numTurns);
}
void RewindTimeWarp(void* UNUSED(cbdata))
{
g_Game->GetTurnManager()->RewindTimeWarp();
}
void QuickSave(void* UNUSED(cbdata))
{
g_Game->GetTurnManager()->QuickSave();
}
void QuickLoad(void* UNUSED(cbdata))
{
g_Game->GetTurnManager()->QuickLoad();
}
void SetBoundingBoxDebugOverlay(void* UNUSED(cbdata), bool enabled)
{
ICmpSelectable::ms_EnableDebugOverlays = enabled;
}
} // namespace
void GuiScriptingInit(ScriptInterface& scriptInterface)
{
JSI_GameView::RegisterScriptFunctions(scriptInterface);
JSI_Renderer::RegisterScriptFunctions(scriptInterface);
JSI_Console::RegisterScriptFunctions(scriptInterface);
JSI_ConfigDB::RegisterScriptFunctions(scriptInterface);
// GUI manager functions:
scriptInterface.RegisterFunction("GetActiveGui");
scriptInterface.RegisterFunction("PushGuiPage");
scriptInterface.RegisterFunction("SwitchGuiPage");
scriptInterface.RegisterFunction("PopGuiPage");
// Simulation<->GUI interface functions:
scriptInterface.RegisterFunction("GuiInterfaceCall");
scriptInterface.RegisterFunction("PostNetworkCommand");
// Entity picking
scriptInterface.RegisterFunction, int, int, &PickEntitiesAtPoint>("PickEntitiesAtPoint");
scriptInterface.RegisterFunction, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect");
scriptInterface.RegisterFunction, int, &PickFriendlyEntitiesOnScreen>("PickFriendlyEntitiesOnScreen");
scriptInterface.RegisterFunction, std::string, bool, bool, bool, &PickSimilarFriendlyEntities>("PickSimilarFriendlyEntities");
scriptInterface.RegisterFunction("GetTerrainAtScreenPoint");
// Network / game setup functions
scriptInterface.RegisterFunction("StartNetworkGame");
scriptInterface.RegisterFunction("StartGame");
scriptInterface.RegisterFunction("StartNetworkHost");
scriptInterface.RegisterFunction("StartNetworkJoin");
scriptInterface.RegisterFunction("DisconnectNetworkGame");
scriptInterface.RegisterFunction("PollNetworkClient");
scriptInterface.RegisterFunction("SetNetworkGameAttributes");
scriptInterface.RegisterFunction("AssignNetworkPlayer");
scriptInterface.RegisterFunction("SendNetworkChat");
scriptInterface.RegisterFunction, &GetAIs>("GetAIs");
// Saved games
scriptInterface.RegisterFunction("StartSavedGame");
scriptInterface.RegisterFunction, &GetSavedGames>("GetSavedGames");
scriptInterface.RegisterFunction("DeleteSavedGame");
scriptInterface.RegisterFunction("SaveGame");
scriptInterface.RegisterFunction("SaveGamePrefix");
scriptInterface.RegisterFunction("QuickSave");
scriptInterface.RegisterFunction("QuickLoad");
// Misc functions
scriptInterface.RegisterFunction("SetCursor");
scriptInterface.RegisterFunction("GetPlayerID");
scriptInterface.RegisterFunction("SetPlayerID");
scriptInterface.RegisterFunction("GetDefaultPlayerName");
scriptInterface.RegisterFunction("GetDefaultMPServer");
scriptInterface.RegisterFunction("SaveMPConfig");
scriptInterface.RegisterFunction("OpenURL");
scriptInterface.RegisterFunction("RestartInAtlas");
scriptInterface.RegisterFunction("AtlasIsAvailable");
scriptInterface.RegisterFunction("IsAtlasRunning");
scriptInterface.RegisterFunction("LoadMapSettings");
scriptInterface.RegisterFunction("GetMapSettings");
scriptInterface.RegisterFunction("CameraGetX");
scriptInterface.RegisterFunction("CameraGetZ");
scriptInterface.RegisterFunction("CameraFollow");
scriptInterface.RegisterFunction("CameraFollowFPS");
scriptInterface.RegisterFunction("CameraMoveTo");
scriptInterface.RegisterFunction("GetFollowedEntity");
scriptInterface.RegisterFunction("HotkeyIsPressed");
scriptInterface.RegisterFunction("DisplayErrorDialog");
scriptInterface.RegisterFunction("GetProfilerState");
// User report functions
scriptInterface.RegisterFunction("IsUserReportEnabled");
scriptInterface.RegisterFunction("SetUserReportEnabled");
scriptInterface.RegisterFunction("GetUserReportStatus");
scriptInterface.RegisterFunction("SubmitUserReport");
// Splash screen functions
scriptInterface.RegisterFunction("IsSplashScreenEnabled");
scriptInterface.RegisterFunction("SetSplashScreenEnabled");
// Development/debugging functions
scriptInterface.RegisterFunction("SetSimRate");
scriptInterface.RegisterFunction("GetSimRate");
scriptInterface.RegisterFunction("SetTurnLength");
scriptInterface.RegisterFunction("SetCameraTarget");
scriptInterface.RegisterFunction("Crash");
scriptInterface.RegisterFunction("DebugWarn");
scriptInterface.RegisterFunction("ForceGC");
scriptInterface.RegisterFunction("DumpSimState");
scriptInterface.RegisterFunction("DumpTerrainMipmap");
scriptInterface.RegisterFunction("EnableTimeWarpRecording");
scriptInterface.RegisterFunction("RewindTimeWarp");
scriptInterface.RegisterFunction("SetBoundingBoxDebugOverlay");
}
Index: ps/trunk/source/ps/Game.cpp
===================================================================
--- ps/trunk/source/ps/Game.cpp (revision 13937)
+++ ps/trunk/source/ps/Game.cpp (revision 13938)
@@ -1,358 +1,360 @@
/* 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 "Game.h"
#include "graphics/GameView.h"
#include "graphics/LOSTexture.h"
#include "graphics/ParticleManager.h"
#include "graphics/UnitManager.h"
#include "gui/GUIManager.h"
#include "lib/config2.h"
#include "lib/timer.h"
#include "network/NetClient.h"
#include "network/NetServer.h"
#include "network/NetTurnManager.h"
#include "ps/CConsole.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "ps/Loader.h"
#include "ps/LoaderThunks.h"
#include "ps/Overlay.h"
#include "ps/Profile.h"
#include "ps/Replay.h"
#include "ps/World.h"
#include "ps/GameSetup/GameSetup.h"
#include "renderer/Renderer.h"
#include "renderer/TimeManager.h"
#include "scripting/ScriptingHost.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPlayer.h"
#include "simulation2/components/ICmpPlayerManager.h"
#include "soundmanager/ISoundManager.h"
extern bool g_GameRestarted;
/**
* Globally accessible pointer to the CGame object.
**/
CGame *g_Game=NULL;
/**
* Constructor
*
**/
CGame::CGame(bool disableGraphics):
m_World(new CWorld(this)),
m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), m_World->GetTerrain())),
m_GameView(disableGraphics ? NULL : new CGameView(this)),
m_GameStarted(false),
m_Paused(false),
m_SimRate(1.0f),
m_PlayerID(-1),
m_IsSavedGame(false)
{
m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());
// TODO: should use CDummyReplayLogger unless activated by cmd-line arg, perhaps?
// Need to set the CObjectManager references after various objects have
// been initialised, so do it here rather than via the initialisers above.
if (m_GameView)
m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager());
m_TurnManager = new CNetLocalTurnManager(*m_Simulation2, GetReplayLogger()); // this will get replaced if we're a net server/client
m_Simulation2->LoadDefaultScripts();
}
/**
* Destructor
*
**/
CGame::~CGame()
{
// Again, the in-game call tree is going to be different to the main menu one.
if (CProfileManager::IsInitialised())
g_Profiler.StructuralReset();
delete m_TurnManager;
delete m_GameView;
delete m_Simulation2;
delete m_World;
delete m_ReplayLogger;
}
void CGame::SetTurnManager(CNetTurnManager* turnManager)
{
if (m_TurnManager)
delete m_TurnManager;
m_TurnManager = turnManager;
if (m_TurnManager)
m_TurnManager->SetPlayerID(m_PlayerID);
}
/**
* Initializes the game with the set of attributes provided.
* Makes calls to initialize the game view, world, and simulation objects.
* Calls are made to facilitate progress reporting of the initialization.
**/
void CGame::RegisterInit(const CScriptValRooted& attribs, const std::string& savedState)
{
m_InitialSavedState = savedState;
m_IsSavedGame = !savedState.empty();
m_Simulation2->SetInitAttributes(attribs);
std::string mapType;
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "mapType", mapType);
float speed;
if (m_Simulation2->GetScriptInterface().HasProperty(attribs.get(), "gameSpeed") && m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "gameSpeed", speed))
SetSimRate(speed);
LDR_BeginRegistering();
RegMemFun(m_Simulation2, &CSimulation2::ProgressiveLoad, L"Simulation init", 1000);
// RC, 040804 - GameView needs to be initialized before World, otherwise GameView initialization
// overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
// values. At the minute, it's just lighting settings, but could be extended to store camera position.
// Storing lighting settings in the game view seems a little odd, but it's no big deal; maybe move it at
// some point to be stored in the world object?
if (m_GameView)
m_GameView->RegisterInit();
- if (mapType == "scenario")
- {
- // Load scenario attributes
- std::wstring mapFile;
- m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "map", mapFile);
-
- m_World->RegisterInit(mapFile, m_PlayerID);
- }
- else if (mapType == "random")
+ if (mapType == "random")
{
// Load random map attributes
std::wstring scriptFile;
CScriptValRooted settings;
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "script", scriptFile);
m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "settings", settings);
m_World->RegisterInitRMS(scriptFile, settings, m_PlayerID);
}
+ else
+ {
+ std::wstring mapFile;
+ m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "map", mapFile);
+ CScriptValRooted settings;
+ if (mapType == "skirmish")
+ m_Simulation2->GetScriptInterface().GetProperty(attribs.get(), "settings", settings);
+
+ m_World->RegisterInit(mapFile, settings, m_PlayerID);
+ }
if (m_IsSavedGame)
RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000);
LDR_EndRegistering();
}
int CGame::LoadInitialState()
{
ENSURE(m_IsSavedGame);
ENSURE(!m_InitialSavedState.empty());
std::string state;
m_InitialSavedState.swap(state); // deletes the original to save a bit of memory
std::stringstream stream(state);
bool ok = m_Simulation2->DeserializeState(stream);
if (!ok)
{
CancelLoad(L"Failed to load saved game state. It might have been\nsaved with an incompatible version of the game.");
return 0;
}
return 0;
}
/**
* Game initialization has been completed. Set game started flag and start the session.
*
* @return PSRETURN 0
**/
PSRETURN CGame::ReallyStartGame()
{
// Call the script function InitGame only for new games, and InitSavedGame for saved games
if (!m_IsSavedGame)
{
CScriptVal settings;
m_Simulation2->GetScriptInterface().GetProperty(m_Simulation2->GetInitAttributes().get(), "settings", settings);
m_Simulation2->InitGame(settings);
}
else
{
m_Simulation2->InitSavedGame();
}
// We need to do an initial Interpolate call to set up all the models etc,
// because Update might never interpolate (e.g. if the game starts paused)
// and we could end up rendering before having set up any models (so they'd
// all be invisible)
Interpolate(0, 0);
m_GameStarted=true;
// Render a frame to begin loading assets
if (CRenderer::IsInitialised())
Render();
// Call the reallyStartGame GUI function, but only if it exists
if (g_GUI && g_GUI->HasPages())
{
jsval fval, rval;
JSBool ok = JS_GetProperty(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), "reallyStartGame", &fval);
ENSURE(ok);
if (ok && !JSVAL_IS_VOID(fval))
JS_CallFunctionValue(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), fval, 0, NULL, &rval);
}
if (g_NetClient)
g_NetClient->LoadFinished();
debug_printf(L"GAME STARTED, ALL INIT COMPLETE\n");
// The call tree we've built for pregame probably isn't useful in-game.
if (CProfileManager::IsInitialised())
g_Profiler.StructuralReset();
// Mark terrain as modified so the minimap can repaint (is there a cleaner way of handling this?)
g_GameRestarted = true;
return 0;
}
int CGame::GetPlayerID()
{
return m_PlayerID;
}
void CGame::SetPlayerID(int playerID)
{
m_PlayerID = playerID;
if (m_TurnManager)
m_TurnManager->SetPlayerID(m_PlayerID);
}
void CGame::StartGame(const CScriptValRooted& attribs, const std::string& savedState)
{
m_ReplayLogger->StartGame(attribs);
RegisterInit(attribs, savedState);
}
// TODO: doInterpolate is optional because Atlas interpolates explicitly,
// so that it has more control over the update rate. The game might want to
// do the same, and then doInterpolate should be redundant and removed.
bool CGame::Update(const double deltaRealTime, bool doInterpolate)
{
if (m_Paused)
return true;
if (!m_TurnManager)
return true;
const double deltaSimTime = deltaRealTime * m_SimRate;
bool ok = true;
if (deltaSimTime)
{
// To avoid confusing the profiler, we need to trigger the new turn
// while we're not nested inside any PROFILE blocks
if (m_TurnManager->WillUpdate(deltaSimTime))
g_Profiler.Turn();
// At the normal sim rate, we currently want to render at least one
// frame per simulation turn, so let maxTurns be 1. But for fast-forward
// sim rates we want to allow more, so it's not bounded by framerate,
// so just use the sim rate itself as the number of turns per frame.
size_t maxTurns = (size_t)m_SimRate;
if (m_TurnManager->Update(deltaSimTime, maxTurns))
{
{
PROFILE3("gui sim update");
g_GUI->SendEventToAll("SimulationUpdate");
}
GetView()->GetLOSTexture().MakeDirty();
}
if (CRenderer::IsInitialised())
g_Renderer.GetTimeManager().Update(deltaSimTime);
}
if (doInterpolate)
{
m_TurnManager->Interpolate(deltaSimTime, deltaRealTime);
if ( g_SoundManager )
g_SoundManager->IdleTask();
}
return ok;
}
void CGame::Interpolate(float simFrameLength, float realFrameLength)
{
if (!m_TurnManager)
return;
m_TurnManager->Interpolate(simFrameLength, realFrameLength);
}
static CColor BrokenColor(0.3f, 0.3f, 0.3f, 1.0f);
void CGame::CachePlayerColours()
{
m_PlayerColours.clear();
CmpPtr cmpPlayerManager(*m_Simulation2, SYSTEM_ENTITY);
if (!cmpPlayerManager)
return;
int numPlayers = cmpPlayerManager->GetNumPlayers();
m_PlayerColours.resize(numPlayers);
for (int i = 0; i < numPlayers; ++i)
{
CmpPtr cmpPlayer(*m_Simulation2, cmpPlayerManager->GetPlayerByID(i));
if (!cmpPlayer)
m_PlayerColours[i] = BrokenColor;
else
m_PlayerColours[i] = cmpPlayer->GetColour();
}
}
CColor CGame::GetPlayerColour(int player) const
{
if (player < 0 || player >= (int)m_PlayerColours.size())
return BrokenColor;
return m_PlayerColours[player];
}
Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp
===================================================================
--- ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 13937)
+++ ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 13938)
@@ -1,1344 +1,1345 @@
/* 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 "lib/app_hooks.h"
#include "lib/config2.h"
#include "lib/input.h"
#include "lib/ogl.h"
#include "lib/timer.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/file/common/file_stats.h"
#include "lib/res/h_mgr.h"
#include "lib/res/graphics/cursor.h"
#include "lib/sysdep/cursor.h"
#include "lib/sysdep/cpu.h"
#include "lib/sysdep/gfx.h"
#include "lib/sysdep/os_cpu.h"
#include "lib/tex/tex.h"
#if OS_WIN
#include "lib/sysdep/os/win/wversion.h"
#endif
#include "graphics/CinemaTrack.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/MapReader.h"
#include "graphics/MaterialManager.h"
#include "graphics/TerrainTextureManager.h"
#include "gui/GUI.h"
#include "gui/GUIManager.h"
#include "gui/scripting/JSInterface_IGUIObject.h"
#include "gui/scripting/JSInterface_GUITypes.h"
#include "gui/scripting/ScriptFunctions.h"
#include "maths/MathUtil.h"
#include "maths/scripting/JSInterface_Vector3D.h"
#include "network/NetServer.h"
#include "network/NetClient.h"
#include "ps/CConsole.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Filesystem.h"
#include "ps/Font.h"
#include "ps/Game.h"
#include "ps/GameSetup/Atlas.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/GameSetup/Paths.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/CmdLineArgs.h"
#include "ps/GameSetup/HWDetect.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "ps/Joystick.h"
#include "ps/Loader.h"
#include "ps/Overlay.h"
#include "ps/Profile.h"
#include "ps/ProfileViewer.h"
#include "ps/Profiler2.h"
#include "ps/Pyrogenesis.h" // psSetLogDir
#include "ps/scripting/JSInterface_Console.h"
#include "ps/TouchInput.h"
#include "ps/UserReport.h"
#include "ps/Util.h"
#include "ps/VideoMode.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/VertexBufferManager.h"
#include "renderer/ModelRenderer.h"
#include "scripting/ScriptingHost.h"
#include "scripting/ScriptGlue.h"
#include "scriptinterface/DebuggingServer.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptStats.h"
#include "simulation2/Simulation2.h"
#include "soundmanager/scripting/JSInterface_Sound.h"
#include "soundmanager/ISoundManager.h"
#include "tools/atlas/GameInterface/GameLoop.h"
#include "tools/atlas/GameInterface/View.h"
#if !(OS_WIN || OS_MACOSX || OS_ANDROID) // assume all other platforms use X11 for wxWidgets
#define MUST_INIT_X11 1
#include
#else
#define MUST_INIT_X11 0
#endif
#if OS_WIN
extern void wmi_Shutdown();
#endif
#include
ERROR_GROUP(System);
ERROR_TYPE(System, SDLInitFailed);
ERROR_TYPE(System, VmodeFailed);
ERROR_TYPE(System, RequiredExtensionsMissing);
bool g_DoRenderGui = true;
bool g_DoRenderLogger = true;
bool g_DoRenderCursor = true;
static const int SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code
static void SetTextureQuality(int quality)
{
int q_flags;
GLint filter;
retry:
// keep this in sync with SANE_TEX_QUALITY_DEFAULT
switch(quality)
{
// worst quality
case 0:
q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP;
filter = GL_NEAREST;
break;
// [perf] add bilinear filtering
case 1:
q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP;
filter = GL_LINEAR;
break;
// [vmem] no longer reduce resolution
case 2:
q_flags = OGL_TEX_HALF_BPP;
filter = GL_LINEAR;
break;
// [vmem] add mipmaps
case 3:
q_flags = OGL_TEX_HALF_BPP;
filter = GL_NEAREST_MIPMAP_LINEAR;
break;
// [perf] better filtering
case 4:
q_flags = OGL_TEX_HALF_BPP;
filter = GL_LINEAR_MIPMAP_LINEAR;
break;
// [vmem] no longer reduce bpp
case SANE_TEX_QUALITY_DEFAULT:
q_flags = OGL_TEX_FULL_QUALITY;
filter = GL_LINEAR_MIPMAP_LINEAR;
break;
// [perf] add anisotropy
case 6:
// TODO: add anisotropic filtering
q_flags = OGL_TEX_FULL_QUALITY;
filter = GL_LINEAR_MIPMAP_LINEAR;
break;
// invalid
default:
debug_warn(L"SetTextureQuality: invalid quality");
quality = SANE_TEX_QUALITY_DEFAULT;
// careful: recursion doesn't work and we don't want to duplicate
// the "sane" default values.
goto retry;
}
ogl_tex_set_defaults(q_flags, filter);
}
//----------------------------------------------------------------------------
// GUI integration
//----------------------------------------------------------------------------
// display progress / description in loading screen
void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task)
{
g_ScriptingHost.GetScriptInterface().SetGlobal("g_Progress", percent, true);
g_ScriptingHost.GetScriptInterface().SetGlobal("g_LoadDescription", pending_task, true);
g_GUI->SendEventToAll("progress");
}
void Render()
{
PROFILE3("render");
if (g_SoundManager)
g_SoundManager->IdleTask();
ogl_WarnIfError();
g_Profiler2.RecordGPUFrameStart();
ogl_WarnIfError();
// prepare before starting the renderer frame
if (g_Game && g_Game->IsGameStarted())
g_Game->GetView()->BeginFrame();
if (g_Game)
g_Renderer.SetSimulation(g_Game->GetSimulation2());
// start new frame
g_Renderer.BeginFrame();
ogl_WarnIfError();
if (g_Game && g_Game->IsGameStarted())
g_Game->GetView()->Render();
ogl_WarnIfError();
g_Renderer.RenderTextOverlays();
if (g_DoRenderGui)
g_GUI->Draw();
ogl_WarnIfError();
// If we're in Atlas game view, render special overlays (e.g. editor bandbox)
if (g_AtlasGameLoop && g_AtlasGameLoop->view)
{
g_AtlasGameLoop->view->DrawOverlays();
ogl_WarnIfError();
}
// Text:
glDisable(GL_DEPTH_TEST);
g_Console->Render();
ogl_WarnIfError();
if (g_DoRenderLogger)
g_Logger->Render();
ogl_WarnIfError();
// Profile information
g_ProfileViewer.RenderProfile();
ogl_WarnIfError();
// Draw the cursor (or set the Windows cursor, on Windows)
if (g_DoRenderCursor)
{
PROFILE3_GPU("cursor");
CStrW cursorName = g_CursorName;
if (cursorName.empty())
{
cursor_draw(g_VFS, NULL, g_mouse_x, g_yres-g_mouse_y, false);
}
else
{
bool forceGL = false;
CFG_GET_VAL("nohwcursor", Bool, forceGL);
#if CONFIG2_GLES
#warning TODO: implement cursors for GLES
#else
// set up transform for GL cursor
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
CMatrix3D transform;
transform.SetOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
glLoadMatrixf(&transform._11);
#endif
if (cursor_draw(g_VFS, cursorName.c_str(), g_mouse_x, g_yres-g_mouse_y, forceGL) < 0)
LOGWARNING(L"Failed to draw cursor '%ls'", cursorName.c_str());
#if CONFIG2_GLES
#warning TODO: implement cursors for GLES
#else
// restore transform
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
#endif
}
}
glEnable(GL_DEPTH_TEST);
g_Renderer.EndFrame();
PROFILE2_ATTR("draw calls: %d", (int)g_Renderer.GetStats().m_DrawCalls);
PROFILE2_ATTR("terrain tris: %d", (int)g_Renderer.GetStats().m_TerrainTris);
PROFILE2_ATTR("water tris: %d", (int)g_Renderer.GetStats().m_WaterTris);
PROFILE2_ATTR("model tris: %d", (int)g_Renderer.GetStats().m_ModelTris);
PROFILE2_ATTR("overlay tris: %d", (int)g_Renderer.GetStats().m_OverlayTris);
PROFILE2_ATTR("blend splats: %d", (int)g_Renderer.GetStats().m_BlendSplats);
PROFILE2_ATTR("particles: %d", (int)g_Renderer.GetStats().m_Particles);
ogl_WarnIfError();
g_Profiler2.RecordGPUFrameEnd();
ogl_WarnIfError();
}
static void RegisterJavascriptInterfaces()
{
// maths
JSI_Vector3D::init();
// GUI
CGUI::ScriptingInit();
GuiScriptingInit(g_ScriptingHost.GetScriptInterface());
JSI_Sound::RegisterScriptFunctions(g_ScriptingHost.GetScriptInterface());
}
static void InitScripting()
{
TIMER(L"InitScripting");
// Create the scripting host. This needs to be done before the GUI is created.
// [7ms]
new ScriptingHost;
RegisterJavascriptInterfaces();
}
static size_t OperatingSystemFootprint()
{
#if OS_WIN
switch(wversion_Number())
{
case WVERSION_2K:
case WVERSION_XP:
return 150;
case WVERSION_XP64:
return 200;
default: // newer Windows version: assume the worst, and don't warn
case WVERSION_VISTA:
return 300;
case WVERSION_7:
return 250;
}
#else
return 200;
#endif
}
static size_t ChooseCacheSize()
{
// (all sizes in MiB and signed to allow temporarily negative computations)
const ssize_t total = (ssize_t)os_cpu_MemorySize();
// (NB: os_cpu_MemoryAvailable is useless on Linux because free memory
// is marked as "in use" by OS caches.)
const ssize_t os = (ssize_t)OperatingSystemFootprint();
const ssize_t game = 300; // estimated working set
ssize_t cache = 500; // upper bound: total size of our data
// the cache reserves contiguous address space, which is a precious
// resource on 32-bit systems, so don't use too much:
if(ARCH_IA32 || sizeof(void*) == 4)
cache = std::min(cache, (ssize_t)200);
// try to leave over enough memory for the OS and game
cache = std::min(cache, total-os-game);
// always provide at least this much to ensure correct operation
cache = std::max(cache, (ssize_t)64);
debug_printf(L"Cache: %d (total: %d) MiB\n", (int)cache, (int)total);
return size_t(cache)*MiB;
}
ErrorReactionInternal psDisplayError(const wchar_t* UNUSED(text), size_t UNUSED(flags))
{
// If we're fullscreen, then sometimes (at least on some particular drivers on Linux)
// displaying the error dialog hangs the desktop since the dialog box is behind the
// fullscreen window. So we just force the game to windowed mode before displaying the dialog.
// (But only if we're in the main thread, and not if we're being reentrant.)
if (ThreadUtil::IsMainThread())
{
static bool reentering = false;
if (!reentering)
{
reentering = true;
g_VideoMode.SetFullscreen(false);
reentering = false;
}
}
// We don't actually implement the error display here, so return appropriately
return ERI_NOT_IMPLEMENTED;
}
static std::vector GetMods(const CmdLineArgs& args, bool dev)
{
std::vector mods = args.GetMultiple("mod");
// TODO: It would be nice to remove this hard-coding
mods.insert(mods.begin(), "public");
// Add the user mod if not explicitly disabled or we have a dev copy so
// that saved files end up in version control and not in the user mod.
if (!dev && !args.Has("noUserMod"))
mods.push_back("user");
return mods;
}
static void InitVfs(const CmdLineArgs& args, int flags)
{
TIMER(L"InitVfs");
const bool setup_error = (flags & INIT_HAVE_DISPLAY_ERROR) == 0;
const Paths paths(args);
OsPath logs(paths.Logs());
CreateDirectories(logs, 0700);
psSetLogDir(logs);
// desired location for crashlog is now known. update AppHooks ASAP
// (particularly before the following error-prone operations):
AppHooks hooks = {0};
hooks.bundle_logs = psBundleLogs;
hooks.get_log_dir = psLogDir;
if (setup_error)
hooks.display_error = psDisplayError;
app_hooks_update(&hooks);
const size_t cacheSize = ChooseCacheSize();
g_VFS = CreateVfs(cacheSize);
// Work out whether we are a dev version to make sure saved files
// (maps, etc) end up in version control.
const OsPath readonlyConfig = paths.RData()/"config"/"";
g_VFS->Mount(L"config/", readonlyConfig);
bool dev = (g_VFS->GetFileInfo(L"config/dev.cfg", NULL) == INFO::OK);
const std::vector mods = GetMods(args, dev);
OsPath modPath = paths.RData()/"mods";
OsPath modUserPath = paths.UserData()/"mods";
for (size_t i = 0; i < mods.size(); ++i)
{
size_t priority = (i+1)*2; // mods are higher priority than regular mountings, which default to priority 0
size_t userFlags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE|VFS_MOUNT_REPLACEABLE;
size_t baseFlags = userFlags|VFS_MOUNT_MUST_EXIST;
OsPath modName(mods[i]);
if (dev)
{
// We are running a dev copy, so only mount mods in the user mod path
// if the mod does not exist in the data path.
if (DirectoryExists(modPath / modName/""))
g_VFS->Mount(L"", modPath / modName/"", baseFlags, priority);
else
g_VFS->Mount(L"", modUserPath / modName/"", userFlags, priority);
}
else
{
g_VFS->Mount(L"", modPath / modName/"", baseFlags, priority);
// Ensure that user modified files are loaded, if they are present
g_VFS->Mount(L"", modUserPath / modName/"", userFlags, priority+1);
}
}
// We mount these dirs last as otherwise writing could result in files being placed in a mod's dir.
g_VFS->Mount(L"screenshots/", paths.UserData()/"screenshots"/"");
g_VFS->Mount(L"saves/", paths.UserData()/"saves"/"", VFS_MOUNT_WATCH);
// Mounting with highest priority, so that a mod supplied user.cfg is harmless
g_VFS->Mount(L"config/", readonlyConfig, 0, (size_t)-1);
if(readonlyConfig != paths.Config())
g_VFS->Mount(L"config/", paths.Config(), 0, (size_t)-1);
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); // (adding XMBs to archive speeds up subsequent reads)
// note: don't bother with g_VFS->TextRepresentation - directories
// haven't yet been populated and are empty.
}
static void InitPs(bool setup_gui, const CStrW& gui_page, CScriptVal initData)
{
{
// console
TIMER(L"ps_console");
g_Console->UpdateScreenSize(g_xres, g_yres);
// Calculate and store the line spacing
CFont font(CONSOLE_FONT);
g_Console->m_iFontHeight = font.GetLineSpacing();
g_Console->m_iFontWidth = font.GetCharacterWidth(L'C');
g_Console->m_charsPerPage = (size_t)(g_xres / g_Console->m_iFontWidth);
// Offset by an arbitrary amount, to make it fit more nicely
g_Console->m_iFontOffset = 7;
double blinkRate = 0.5;
CFG_GET_VAL("gui.cursorblinkrate", Double, blinkRate);
g_Console->SetCursorBlinkRate(blinkRate);
}
// hotkeys
{
TIMER(L"ps_lang_hotkeys");
LoadHotkeys();
}
if (!setup_gui)
{
// We do actually need *some* kind of GUI loaded, so use the
// (currently empty) Atlas one
g_GUI->SwitchPage(L"page_atlas.xml", initData);
return;
}
// GUI uses VFS, so this must come after VFS init.
g_GUI->SwitchPage(gui_page, initData);
}
static void InitInput()
{
#if !SDL_VERSION_ATLEAST(2, 0, 0)
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
#endif
g_Joystick.Initialise();
// register input handlers
// This stack is constructed so the first added, will be the last
// one called. This is important, because each of the handlers
// has the potential to block events to go further down
// in the chain. I.e. the last one in the list added, is the
// only handler that can block all messages before they are
// processed.
in_add_handler(game_view_handler);
in_add_handler(CProfileViewer::InputThunk);
in_add_handler(conInputHandler);
in_add_handler(HotkeyInputHandler);
// gui_handler needs to be registered after (i.e. called before!) the
// hotkey handler so that input boxes can be typed in without
// setting off hotkeys.
in_add_handler(gui_handler);
in_add_handler(touch_input_handler);
// must be registered after (called before) the GUI which relies on these globals
in_add_handler(GlobalsInputHandler);
}
static void ShutdownPs()
{
SAFE_DELETE(g_GUI);
SAFE_DELETE(g_Console);
// disable the special Windows cursor, or free textures for OGL cursors
cursor_draw(g_VFS, 0, g_mouse_x, g_yres-g_mouse_y, false);
}
static void InitRenderer()
{
TIMER(L"InitRenderer");
if(g_NoGLS3TC)
ogl_tex_override(OGL_TEX_S3TC, OGL_TEX_DISABLE);
if(g_NoGLAutoMipmap)
ogl_tex_override(OGL_TEX_AUTO_MIPMAP_GEN, OGL_TEX_DISABLE);
// create renderer
new CRenderer;
// set renderer options from command line options - NOVBO must be set before opening the renderer
g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO, g_NoGLVBO);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, g_Shadows);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERNORMAL, g_WaterNormal);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERREALDEPTH, g_WaterRealDepth);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERFOAM, g_WaterFoam);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERCOASTALWAVES, g_WaterCoastalWaves);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERREFLECTION, g_WaterReflection);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERREFRACTION, g_WaterRefraction);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERSHADOW, g_WaterShadows);
g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCF, g_ShadowPCF);
g_Renderer.SetOptionBool(CRenderer::OPT_PARTICLES, g_Particles);
g_Renderer.SetOptionBool(CRenderer::OPT_SILHOUETTES, g_Silhouettes);
g_Renderer.SetOptionBool(CRenderer::OPT_SHOWSKY, g_ShowSky);
// create terrain related stuff
new CTerrainTextureManager;
g_Renderer.Open(g_xres, g_yres);
// Setup lighting environment. Since the Renderer accesses the
// lighting environment through a pointer, this has to be done before
// the first Frame.
g_Renderer.SetLightEnv(&g_LightEnv);
// I haven't seen the camera affecting GUI rendering and such, but the
// viewport has to be updated according to the video mode
SViewPort vp;
vp.m_X = 0;
vp.m_Y = 0;
vp.m_Width = g_xres;
vp.m_Height = g_yres;
g_Renderer.SetViewport(vp);
ColorActivateFastImpl();
ModelRenderer::Init();
}
static void InitSDL()
{
#if OS_LINUX
// In fullscreen mode when SDL is compiled with DGA support, the mouse
// sensitivity often appears to be unusably wrong (typically too low).
// (This seems to be reported almost exclusively on Ubuntu, but can be
// reproduced on Gentoo after explicitly enabling DGA.)
// Disabling the DGA mouse appears to fix that problem, and doesn't
// have any obvious negative effects.
setenv("SDL_VIDEO_X11_DGAMOUSE", "0", 0);
#endif
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0)
{
LOGERROR(L"SDL library initialization failed: %hs", SDL_GetError());
throw PSERROR_System_SDLInitFailed();
}
atexit(SDL_Quit);
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_StartTextInput();
#else
SDL_EnableUNICODE(1);
#endif
}
static void ShutdownSDL()
{
SDL_Quit();
sys_cursor_reset();
}
void EndGame()
{
SAFE_DELETE(g_NetServer);
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
ISoundManager::CloseGame();
}
void Shutdown(int UNUSED(flags))
{
EndGame();
ShutdownPs(); // Must delete g_GUI before g_ScriptingHost
in_reset_handlers();
TIMER_BEGIN(L"shutdown TexMan");
delete &g_TexMan;
TIMER_END(L"shutdown TexMan");
// destroy renderer
TIMER_BEGIN(L"shutdown Renderer");
delete &g_Renderer;
g_VBMan.Shutdown();
TIMER_END(L"shutdown Renderer");
tex_codec_unregister_all();
g_Profiler2.ShutdownGPU();
// Free cursors before shutting down SDL, as they may depend on SDL.
cursor_shutdown();
TIMER_BEGIN(L"shutdown SDL");
ShutdownSDL();
TIMER_END(L"shutdown SDL");
g_VideoMode.Shutdown();
TIMER_BEGIN(L"shutdown UserReporter");
g_UserReporter.Deinitialize();
TIMER_END(L"shutdown UserReporter");
TIMER_BEGIN(L"shutdown ScriptingHost");
delete &g_ScriptingHost;
delete g_DebuggingServer;
TIMER_END(L"shutdown ScriptingHost");
TIMER_BEGIN(L"shutdown ConfigDB");
delete &g_ConfigDB;
TIMER_END(L"shutdown ConfigDB");
// resource
// first shut down all resource owners, and then the handle manager.
TIMER_BEGIN(L"resource modules");
ISoundManager::SetEnabled(false);
g_VFS.reset();
// this forcibly frees all open handles (thus preventing real leaks),
// and makes further access to h_mgr impossible.
h_mgr_shutdown();
file_stats_dump();
TIMER_END(L"resource modules");
TIMER_BEGIN(L"shutdown misc");
timer_DisplayClientTotals();
CNetHost::Deinitialize();
SAFE_DELETE(g_ScriptStatsTable);
// should be last, since the above use them
SAFE_DELETE(g_Logger);
delete &g_Profiler;
delete &g_ProfileViewer;
TIMER_END(L"shutdown misc");
#if OS_WIN
TIMER_BEGIN(L"shutdown wmi");
wmi_Shutdown();
TIMER_END(L"shutdown wmi");
#endif
}
#if OS_UNIX
static void FixLocales()
{
#if OS_MACOSX || OS_BSD
// OS X requires a UTF-8 locale in LC_CTYPE so that *wprintf can handle
// wide characters. Peculiarly the string "UTF-8" seems to be acceptable
// despite not being a real locale, and it's conveniently language-agnostic,
// so use that.
setlocale(LC_CTYPE, "UTF-8");
#endif
// On misconfigured systems with incorrect locale settings, we'll die
// with a C++ exception when some code (e.g. Boost) tries to use locales.
// To avoid death, we'll detect the problem here and warn the user and
// reset to the default C locale.
// For informing the user of the problem, use the list of env vars that
// glibc setlocale looks at. (LC_ALL is checked first, and LANG last.)
const char* const LocaleEnvVars[] = {
"LC_ALL",
"LC_COLLATE",
"LC_CTYPE",
"LC_MONETARY",
"LC_NUMERIC",
"LC_TIME",
"LC_MESSAGES",
"LANG"
};
try
{
// this constructor is similar to setlocale(LC_ALL, ""),
// but instead of returning NULL, it throws runtime_error
// when the first locale env variable found contains an invalid value
std::locale("");
}
catch (std::runtime_error&)
{
LOGWARNING(L"Invalid locale settings");
for (size_t i = 0; i < ARRAY_SIZE(LocaleEnvVars); i++)
{
if (char* envval = getenv(LocaleEnvVars[i]))
LOGWARNING(L" %hs=\"%hs\"", LocaleEnvVars[i], envval);
else
LOGWARNING(L" %hs=\"(unset)\"", LocaleEnvVars[i]);
}
// We should set LC_ALL since it overrides LANG
if (setenv("LC_ALL", std::locale::classic().name().c_str(), 1))
debug_warn(L"Invalid locale settings, and unable to set LC_ALL env variable.");
else
LOGWARNING(L"Setting LC_ALL env variable to: %hs", getenv("LC_ALL"));
}
}
#else
static void FixLocales()
{
// Do nothing on Windows
}
#endif
void EarlyInit()
{
// If you ever want to catch a particular allocation:
//_CrtSetBreakAlloc(232647);
ThreadUtil::SetMainThread();
debug_SetThreadName("main");
// add all debug_printf "tags" that we are interested in:
debug_filter_add(L"TIMER");
timer_LatchStartTime();
// initialise profiler early so it can profile startup,
// but only after LatchStartTime
g_Profiler2.Initialise();
FixLocales();
// Because we do GL calls from a secondary thread, Xlib needs to
// be told to support multiple threads safely.
// This is needed for Atlas, but we have to call it before any other
// Xlib functions (e.g. the ones used when drawing the main menu
// before launching Atlas)
#if MUST_INIT_X11
int status = XInitThreads();
if (status == 0)
debug_printf(L"Error enabling thread-safety via XInitThreads\n");
#endif
// Initialise the low-quality rand function
srand(time(NULL)); // NOTE: this rand should *not* be used for simulation!
}
bool Autostart(const CmdLineArgs& args);
void Init(const CmdLineArgs& args, int flags)
{
h_mgr_init();
// Do this as soon as possible, because it chdirs
// and will mess up the error reporting if anything
// crashes before the working directory is set.
InitVfs(args, flags);
// This must come after VFS init, which sets the current directory
// (required for finding our output log files).
g_Logger = new CLogger;
// Special command-line mode to dump the entity schemas instead of running the game.
// (This must be done after loading VFS etc, but should be done before wasting time
// on anything else.)
if (args.Has("dumpSchema"))
{
CSimulation2 sim(NULL, NULL);
sim.LoadDefaultScripts();
std::ofstream f("entity.rng", std::ios_base::out | std::ios_base::trunc);
f << sim.GenerateSchema();
std::cout << "Generated entity.rng\n";
exit(0);
}
// override ah_translate with our i18n code.
AppHooks hooks = {0};
hooks.translate = psTranslate;
hooks.translate_free = psTranslateFree;
app_hooks_update(&hooks);
// Set up the console early, so that debugging
// messages can be logged to it. (The console's size
// and fonts are set later in InitPs())
g_Console = new CConsole();
CNetHost::Initialize();
new CProfileViewer;
new CProfileManager; // before any script code
g_ScriptStatsTable = new CScriptStatsTable;
g_ProfileViewer.AddRootTable(g_ScriptStatsTable);
#if CONFIG2_AUDIO
ISoundManager::CreateSoundManager();
#endif
// g_ConfigDB, command line args, globals
CONFIG_Init(args);
// before scripting
if (g_JSDebuggerEnabled)
g_DebuggingServer = new CDebuggingServer();
InitScripting(); // before GUI
// Optionally start profiler HTTP output automatically
// (By default it's only enabled by a hotkey, for security/performance)
bool profilerHTTPEnable = false;
CFG_GET_VAL("profiler2.http.autoenable", Bool, profilerHTTPEnable);
if (profilerHTTPEnable)
g_Profiler2.EnableHTTP();
if (!g_Quickstart)
g_UserReporter.Initialize(); // after config
PROFILE2_EVENT("Init finished");
}
void InitGraphics(const CmdLineArgs& args, int flags)
{
const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0;
if(setup_vmode)
{
InitSDL();
if (!g_VideoMode.InitSDL())
throw PSERROR_System_VmodeFailed(); // abort startup
#if !SDL_VERSION_ATLEAST(2, 0, 0)
SDL_WM_SetCaption("0 A.D.", "0 A.D.");
#endif
}
RunHardwareDetection();
tex_codec_register_all();
const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file
SetTextureQuality(quality);
ogl_WarnIfError();
// Optionally start profiler GPU timings automatically
// (By default it's only enabled by a hotkey, for performance/compatibility)
bool profilerGPUEnable = false;
CFG_GET_VAL("profiler2.gpu.autoenable", Bool, profilerGPUEnable);
if (profilerGPUEnable)
g_Profiler2.EnableGPU();
if(!g_Quickstart)
{
WriteSystemInfo();
// note: no longer vfs_display here. it's dog-slow due to unbuffered
// file output and very rarely needed.
}
if(g_DisableAudio)
ISoundManager::SetEnabled(false);
g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface());
// (must come after SetVideoMode, since it calls ogl_Init)
if (ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL) != 0 // ARB
&& ogl_HaveExtensions(0, "GL_ARB_vertex_shader", "GL_ARB_fragment_shader", NULL) != 0) // GLSL
{
DEBUG_DISPLAY_ERROR(
L"Your graphics card doesn't appear to be fully compatible with OpenGL shaders."
L" In the future, the game will not support pre-shader graphics cards."
L" You are advised to try installing newer drivers and/or upgrade your graphics card."
L" For more information, please see http://www.wildfiregames.com/forum/index.php?showtopic=16734"
);
// TODO: actually quit once fixed function support is dropped
}
const char* missing = ogl_HaveExtensions(0,
"GL_ARB_multitexture",
"GL_EXT_draw_range_elements",
"GL_ARB_texture_env_combine",
"GL_ARB_texture_env_dot3",
NULL);
if(missing)
{
wchar_t buf[500];
swprintf_s(buf, ARRAY_SIZE(buf),
L"The %hs extension doesn't appear to be available on your computer."
L" The game may still work, though - you are welcome to try at your own risk."
L" If not or it doesn't look right, upgrade your graphics card.",
missing
);
DEBUG_DISPLAY_ERROR(buf);
// TODO: i18n
}
if (!ogl_HaveExtension("GL_ARB_texture_env_crossbar"))
{
DEBUG_DISPLAY_ERROR(
L"The GL_ARB_texture_env_crossbar extension doesn't appear to be available on your computer."
L" Shadows are not available and overall graphics quality might suffer."
L" You are advised to try installing newer drivers and/or upgrade your graphics card.");
g_Shadows = false;
}
ogl_WarnIfError();
InitRenderer();
InitInput();
ogl_WarnIfError();
try
{
if (!Autostart(args))
{
const bool setup_gui = ((flags & INIT_NO_GUI) == 0);
// We only want to display the splash screen at startup
CScriptValRooted data;
if (g_GUI)
{
ScriptInterface& scriptInterface = g_GUI->GetScriptInterface();
scriptInterface.Eval("({})", data);
scriptInterface.SetProperty(data.get(), "isStartup", true);
}
InitPs(setup_gui, L"page_pregame.xml", data.get());
}
}
catch (PSERROR_Game_World_MapLoadFailed& e)
{
// Map Loading failed
// Start the engine so we have a GUI
InitPs(true, L"page_pregame.xml", JSVAL_VOID);
// Call script function to do the actual work
// (delete game data, switch GUI page, show error, etc.)
CancelLoad(CStr(e.what()).FromUTF8());
}
}
void RenderGui(bool RenderingState)
{
g_DoRenderGui = RenderingState;
}
void RenderLogger(bool RenderingState)
{
g_DoRenderLogger = RenderingState;
}
void RenderCursor(bool RenderingState)
{
g_DoRenderCursor = RenderingState;
}
bool Autostart(const CmdLineArgs& args)
{
/*
* Handle various command-line options, for quick testing of various features:
* -autostart=name -- map name for scenario, or rms name for random map
* -autostart-ai=1:dummybot -- adds the dummybot AI to player 1
* -autostart-playername=name -- multiplayer player name
* -autostart-host -- multiplayer host mode
* -autostart-players=2 -- number of players
* -autostart-client -- multiplayer client mode
* -autostart-ip=127.0.0.1 -- multiplayer connect to 127.0.0.1
* -autostart-random=104 -- random map, optional seed value = 104 (default is 0, random is -1)
* -autostart-size=192 -- random map size in tiles = 192 (default is 192)
+ * -autostart-civ=1:hele -- set player #1 civ to "hele"
*
* Examples:
* -autostart=Acropolis -autostart-host -autostart-players=2 -- Host game on Acropolis map, 2 players
* -autostart=latium -autostart-random=-1 -- Start single player game on latium random map, random rng seed
*/
CStr autoStartName = args.Get("autostart");
#if OS_ANDROID
// HACK: currently the most convenient way to test maps on Android;
// should find a better solution
autoStartName = "Oasis";
#endif
if (autoStartName.empty())
{
return false;
}
g_Game = new CGame();
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
CScriptValRooted attrs;
scriptInterface.Eval("({})", attrs);
CScriptVal settings;
scriptInterface.Eval("({})", settings);
CScriptVal playerData;
scriptInterface.Eval("([])", playerData);
// Set different attributes for random or scenario game
if (args.Has("autostart-random"))
{
CStr seedArg = args.Get("autostart-random");
// Default seed is 0
uint32 seed = 0;
if (!seedArg.empty())
{
if (seedArg.compare("-1") == 0)
{ // Random seed value
seed = rand();
}
else
{
seed = seedArg.ToULong();
}
}
// Random map definition will be loaded from JSON file, so we need to parse it
- std::wstring mapPath = L"maps/random/";
- std::wstring scriptPath = mapPath + autoStartName.FromUTF8() + L".json";
+ std::wstring scriptPath = L"maps/random/" + autoStartName.FromUTF8() + L".json";
CScriptValRooted scriptData = scriptInterface.ReadJSONFile(scriptPath);
if (!scriptData.undefined() && scriptInterface.GetProperty(scriptData.get(), "settings", settings))
{
// JSON loaded ok - copy script name over to game attributes
std::wstring scriptFile;
scriptInterface.GetProperty(settings.get(), "Script", scriptFile);
scriptInterface.SetProperty(attrs.get(), "script", scriptFile); // RMS filename
}
else
{
// Problem with JSON file
LOGERROR(L"Error reading random map script '%ls'", scriptPath.c_str());
throw PSERROR_Game_World_MapLoadFailed("Error reading random map script.\nCheck application log for details.");
}
// Get optional map size argument (default 192)
uint mapSize = 192;
if (args.Has("autostart-size"))
{
CStr size = args.Get("autostart-size");
mapSize = size.ToUInt();
}
scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName));
- scriptInterface.SetProperty(attrs.get(), "mapPath", mapPath);
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("random"));
scriptInterface.SetProperty(settings.get(), "Seed", seed); // Random seed
scriptInterface.SetProperty(settings.get(), "Size", mapSize); // Random map size (in patches)
// Get optional number of players (default 2)
size_t numPlayers = 2;
if (args.Has("autostart-players"))
{
CStr num = args.Get("autostart-players");
numPlayers = num.ToUInt();
}
// Set up player data
for (size_t i = 0; i < numPlayers; ++i)
{
CScriptVal player;
scriptInterface.Eval("({})", player);
// We could load player_defaults.json here, but that would complicate the logic
// even more and autostart is only intended for developers anyway
scriptInterface.SetProperty(player.get(), "Civ", std::string("athen"));
scriptInterface.SetPropertyInt(playerData.get(), i, player);
}
}
else
{
- scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName));
+ // TODO: support akirmish maps
+ std::string mapFile = "maps/scenarios/" + autoStartName;
+ scriptInterface.SetProperty(attrs.get(), "map", mapFile);
scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario"));
}
// Set player data for AIs
// attrs.settings = { PlayerData: [ { AI: ... }, ... ] }:
if (args.Has("autostart-ai"))
{
std::vector aiArgs = args.GetMultiple("autostart-ai");
for (size_t i = 0; i < aiArgs.size(); ++i)
{
// Instead of overwriting existing player data, modify the array
CScriptVal player;
if (!scriptInterface.GetPropertyInt(playerData.get(), i, player) || player.undefined())
{
scriptInterface.Eval("({})", player);
}
int playerID = aiArgs[i].BeforeFirst(":").ToInt();
CStr name = aiArgs[i].AfterFirst(":");
scriptInterface.SetProperty(player.get(), "AI", std::string(name));
scriptInterface.SetProperty(player.get(), "AIDiff", 2);
scriptInterface.SetPropertyInt(playerData.get(), playerID-1, player);
}
}
// Set AI difficulty
if (args.Has("autostart-aidiff"))
{
std::vector civArgs = args.GetMultiple("autostart-aidiff");
for (size_t i = 0; i < civArgs.size(); ++i)
{
// Instead of overwriting existing player data, modify the array
CScriptVal player;
if (!scriptInterface.GetPropertyInt(playerData.get(), i, player) || player.undefined())
{
scriptInterface.Eval("({})", player);
}
int playerID = civArgs[i].BeforeFirst(":").ToInt();
int difficulty = civArgs[i].AfterFirst(":").ToInt();
scriptInterface.SetProperty(player.get(), "AIDiff", difficulty);
scriptInterface.SetPropertyInt(playerData.get(), playerID-1, player);
}
}
// Set player data for Civs
if (args.Has("autostart-civ"))
{
std::vector civArgs = args.GetMultiple("autostart-civ");
for (size_t i = 0; i < civArgs.size(); ++i)
{
// Instead of overwriting existing player data, modify the array
CScriptVal player;
if (!scriptInterface.GetPropertyInt(playerData.get(), i, player) || player.undefined())
{
scriptInterface.Eval("({})", player);
}
int playerID = civArgs[i].BeforeFirst(":").ToInt();
CStr name = civArgs[i].AfterFirst(":");
scriptInterface.SetProperty(player.get(), "Civ", std::string(name));
scriptInterface.SetPropertyInt(playerData.get(), playerID-1, player);
}
}
// Add player data to map settings
scriptInterface.SetProperty(settings.get(), "PlayerData", playerData);
// Add map settings to game attributes
scriptInterface.SetProperty(attrs.get(), "settings", settings);
CScriptVal mpInitData;
g_GUI->GetScriptInterface().Eval("({isNetworked:true, playerAssignments:{}})", mpInitData);
g_GUI->GetScriptInterface().SetProperty(mpInitData.get(), "attribs",
CScriptVal(g_GUI->GetScriptInterface().CloneValueFromOtherContext(scriptInterface, attrs.get())));
// Get optional playername
CStrW userName = L"anonymous";
if (args.Has("autostart-playername"))
{
userName = args.Get("autostart-playername").FromUTF8();
}
if (args.Has("autostart-host"))
{
InitPs(true, L"page_loading.xml", mpInitData.get());
size_t maxPlayers = 2;
if (args.Has("autostart-players"))
{
maxPlayers = args.Get("autostart-players").ToUInt();
}
g_NetServer = new CNetServer(maxPlayers);
g_NetServer->UpdateGameAttributes(attrs.get(), scriptInterface);
bool ok = g_NetServer->SetupConnection();
ENSURE(ok);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(userName);
g_NetClient->SetupConnection("127.0.0.1");
}
else if (args.Has("autostart-client"))
{
InitPs(true, L"page_loading.xml", mpInitData.get());
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(userName);
CStr ip = "127.0.0.1";
if (args.Has("autostart-ip"))
{
ip = args.Get("autostart-ip");
}
bool ok = g_NetClient->SetupConnection(ip);
ENSURE(ok);
}
else
{
g_Game->SetPlayerID(1);
g_Game->StartGame(attrs, "");
LDR_NonprogressiveLoad();
PSRETURN ret = g_Game->ReallyStartGame();
ENSURE(ret == PSRETURN_OK);
InitPs(true, L"page_session.xml", JSVAL_VOID);
}
return true;
}
void CancelLoad(const CStrW& message)
{
// Cancel loader
LDR_Cancel();
// Call the cancelOnError GUI function, defined in ..gui/common/functions_utility_error.js
// So all GUI pages that load games should include this script
if (g_GUI && g_GUI->HasPages())
{
JSContext* cx = g_ScriptingHost.getContext();
jsval fval, rval;
JSBool ok = JS_GetProperty(cx, g_GUI->GetScriptObject(), "cancelOnError", &fval);
ENSURE(ok);
jsval msgval = ScriptInterface::ToJSVal(cx, message);
if (ok && !JSVAL_IS_VOID(fval))
JS_CallFunctionValue(cx, g_GUI->GetScriptObject(), fval, 1, &msgval, &rval);
}
}
Index: ps/trunk/source/ps/World.h
===================================================================
--- ps/trunk/source/ps/World.h (revision 13937)
+++ ps/trunk/source/ps/World.h (revision 13938)
@@ -1,111 +1,111 @@
-/* Copyright (C) 2011 Wildfire Games.
+/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* 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 .
*/
/**
* File : World.h
* Project : engine
* Description : Contains the CWorld Class which contains all the entities and represents them at a specific moment in time.
*
**/
#ifndef INCLUDED_WORLD
#define INCLUDED_WORLD
#include "ps/Errors.h"
#include "scriptinterface/ScriptInterface.h"
#ifndef ERROR_GROUP_GAME_DEFINED
#define ERROR_GROUP_GAME_DEFINED
ERROR_GROUP(Game);
#endif
ERROR_SUBGROUP(Game, World);
ERROR_TYPE(Game_World, MapLoadFailed);
class CGame;
class CUnitManager;
class CTerritoryManager;
class CTerrain;
class CStrW;
/**
* CWorld is a general data class containing whatever is needed to accurately represent the world.
* This includes the map, entities, influence maps, tiles, heightmap, etc.
**/
class CWorld
{
NONCOPYABLE(CWorld);
/**
* pointer to the CGame object representing the game.
**/
CGame *m_pGame;
/**
* pointer to the CTerrain object representing the height map.
**/
CTerrain *m_Terrain;
/**
* pointer to the CUnitManager that holds all the units in the world.
**/
CUnitManager *m_UnitManager;
/**
* pointer to the CTerritoryManager that holds territory matrix for the world.
**/
CTerritoryManager *m_TerritoryManager;
public:
CWorld(CGame *pGame);
~CWorld();
/*
Initialize the World - load the map and all objects
*/
- void RegisterInit(const CStrW& mapFile, int playerID);
+ void RegisterInit(const CStrW& mapFile, const CScriptValRooted& settings, int playerID);
/*
Initialize the World - generate and load the random map
*/
void RegisterInitRMS(const CStrW& scriptFile, const CScriptValRooted& settings, int playerID);
/**
* Get the pointer to the terrain object.
*
* @return CTerrain * the value of m_Terrain.
**/
inline CTerrain *GetTerrain()
{ return m_Terrain; }
/**
* Get a reference to the unit manager object.
*
* @return CUnitManager & dereferenced m_UnitManager.
**/
inline CUnitManager &GetUnitManager()
{ return *m_UnitManager; }
/**
* Get the pointer to the territory manager object.
*
* @return CTerritoryManager * the value of m_TerritoryManager.
**/
inline CTerritoryManager *GetTerritoryManager()
{ return m_TerritoryManager; }
};
// rationale: see definition.
class CLightEnv;
extern CLightEnv g_LightEnv;
#endif
Index: ps/trunk/source/simulation2/components/tests/test_Pathfinder.h
===================================================================
--- ps/trunk/source/simulation2/components/tests/test_Pathfinder.h (revision 13937)
+++ ps/trunk/source/simulation2/components/tests/test_Pathfinder.h (revision 13938)
@@ -1,142 +1,142 @@
/* 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 "simulation2/system/ComponentTest.h"
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/components/ICmpPathfinder.h"
#include "graphics/MapReader.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureManager.h"
#include "lib/timer.h"
#include "lib/tex/tex.h"
#include "ps/Loader.h"
#include "simulation2/Simulation2.h"
class TestCmpPathfinder : public CxxTest::TestSuite
{
public:
void setUp()
{
CXeromyces::Startup();
g_VFS = CreateVfs(20 * MiB);
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"public", VFS_MOUNT_MUST_EXIST));
TS_ASSERT_OK(g_VFS->Mount(L"cache/", DataDir()/"cache"));
// Need some stuff for terrain movement costs:
// (TODO: this ought to be independent of any graphics code)
tex_codec_register_all();
new CTerrainTextureManager;
g_TexMan.LoadTerrainTextures();
}
void tearDown()
{
delete &g_TexMan;
tex_codec_unregister_all();
g_VFS.reset();
CXeromyces::Terminate();
}
// disabled by default; run tests with the "-test TestCmpPathfinder" flag to enable
void test_performance_DISABLED()
{
CTerrain terrain;
CSimulation2 sim2(NULL, &terrain);
sim2.LoadDefaultScripts();
sim2.ResetState();
CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself
LDR_BeginRegistering();
- mapReader->LoadMap(L"maps/scenarios/Median Oasis 01.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ mapReader->LoadMap(L"maps/scenarios/Median Oasis 01.pmp", CScriptValRooted(), &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&sim2, &sim2.GetSimContext(), -1, false);
LDR_EndRegistering();
TS_ASSERT_OK(LDR_NonprogressiveLoad());
sim2.Update(0);
CmpPtr cmp(sim2, SYSTEM_ENTITY);
#if 0
entity_pos_t x0 = entity_pos_t::FromInt(10);
entity_pos_t z0 = entity_pos_t::FromInt(495);
entity_pos_t x1 = entity_pos_t::FromInt(500);
entity_pos_t z1 = entity_pos_t::FromInt(495);
ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, x1, z1 };
ICmpPathfinder::Path path;
cmp->ComputePath(x0, z0, goal, cmp->GetPassabilityClass("default"), cmp->GetCostClass("default"), path);
for (size_t i = 0; i < path.m_Waypoints.size(); ++i)
printf("%d: %f %f\n", (int)i, path.m_Waypoints[i].x.ToDouble(), path.m_Waypoints[i].z.ToDouble());
#endif
double t = timer_Time();
srand(1234);
for (size_t j = 0; j < 1024*2; ++j)
{
entity_pos_t x0 = entity_pos_t::FromInt(rand() % 512);
entity_pos_t z0 = entity_pos_t::FromInt(rand() % 512);
entity_pos_t x1 = x0 + entity_pos_t::FromInt(rand() % 64);
entity_pos_t z1 = z0 + entity_pos_t::FromInt(rand() % 64);
ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, x1, z1 };
ICmpPathfinder::Path path;
cmp->ComputePath(x0, z0, goal, cmp->GetPassabilityClass("default"), cmp->GetCostClass("default"), path);
}
t = timer_Time() - t;
printf("[%f]", t);
}
void test_performance_short_DISABLED()
{
CTerrain terrain;
terrain.Initialize(5, NULL);
CSimulation2 sim2(NULL, &terrain);
sim2.LoadDefaultScripts();
sim2.ResetState();
const entity_pos_t range = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*12);
CmpPtr cmpObstructionMan(sim2, SYSTEM_ENTITY);
CmpPtr cmpPathfinder(sim2, SYSTEM_ENTITY);
srand(0);
for (size_t i = 0; i < 200; ++i)
{
fixed x = fixed::FromFloat(1.5f*range.ToFloat() * rand()/(float)RAND_MAX);
fixed z = fixed::FromFloat(1.5f*range.ToFloat() * rand()/(float)RAND_MAX);
// printf("# %f %f\n", x.ToFloat(), z.ToFloat());
cmpObstructionMan->AddUnitShape(INVALID_ENTITY, x, z, fixed::FromInt(2), 0, INVALID_ENTITY);
}
NullObstructionFilter filter;
ICmpPathfinder::Goal goal = { ICmpPathfinder::Goal::POINT, range, range };
ICmpPathfinder::Path path;
cmpPathfinder->ComputeShortPath(filter, range/3, range/3, fixed::FromInt(2), range, goal, 0, path);
for (size_t i = 0; i < path.m_Waypoints.size(); ++i)
printf("# %d: %f %f\n", (int)i, path.m_Waypoints[i].x.ToFloat(), path.m_Waypoints[i].z.ToFloat());
}
};
Index: ps/trunk/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp (nonexistent)
+++ ps/trunk/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp (revision 13938)
@@ -0,0 +1,214 @@
+/* Copyright (C) 2013 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "MapDialog.h"
+
+#include "GameInterface/MessagePasser.h"
+#include "GameInterface/Messages.h"
+
+enum {
+ ID_MapDialogFilename = 1,
+ ID_MapDialogNotebook,
+ ID_ScenarioPage,
+ ID_SkirmishPage
+};
+
+static const wxString scenarioPath(L"maps/scenarios/");
+static const wxString skirmishPath(L"maps/skirmishes/");
+
+MapDialog::MapDialog(wxWindow* parent, MapDialogType type)
+ : wxDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(600,400), wxCAPTION|wxRESIZE_BORDER|wxCLOSE_BOX|wxSYSTEM_MENU), m_Type(type)
+{
+ Freeze();
+
+ SetIcon(wxIcon(_T("ICON_ScenarioEditor"))); // load from atlas.rc
+
+ if (m_Type == MAPDIALOG_OPEN)
+ SetTitle(_("Choose map to open"));
+ else // MAPDIALOG_SAVE
+ SetTitle(_("Choose map to save"));
+
+ AtlasMessage::qGetMapList qry;
+ qry.Post();
+
+ wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
+
+ wxNotebook* notebook = new wxNotebook(this, ID_MapDialogNotebook);
+ {
+ wxPanel* page = new wxPanel(notebook, ID_ScenarioPage);
+ wxSizer* pageSizer = new wxBoxSizer(wxVERTICAL);
+ // TODO: Should display something nicer than raw VFS paths
+ wxListBox* listBox = new wxListBox(page, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE|wxLB_HSCROLL);
+ const std::vector& scenarioFilenames = *qry.scenarioFilenames;
+ for (size_t i = 0; i < scenarioFilenames.size(); ++i)
+ {
+ wxString name = scenarioFilenames[i].substr(scenarioPath.Length());
+ listBox->Append(name, new wxStringClientData(scenarioFilenames[i]));
+ }
+
+ pageSizer->Add(listBox, wxSizerFlags().Proportion(1).Expand().Align(wxBOTTOM));
+ page->SetSizer(pageSizer);
+ notebook->AddPage(page, _("Scenarios"));
+ }
+ {
+ wxPanel* page = new wxPanel(notebook, ID_SkirmishPage);
+ wxSizer* pageSizer = new wxBoxSizer(wxVERTICAL);
+ // TODO: Should display something nicer than raw VFS paths
+ wxListBox* listBox = new wxListBox(page, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE|wxLB_HSCROLL);
+ const std::vector& skirmishFilenames = *qry.skirmishFilenames;
+ for (size_t i = 0; i < skirmishFilenames.size(); ++i)
+ {
+ wxString name = skirmishFilenames[i].substr(skirmishPath.Length());
+ listBox->Append(name, new wxStringClientData(skirmishFilenames[i]));
+ }
+
+ pageSizer->Add(listBox, wxSizerFlags().Proportion(1).Expand());
+ page->SetSizer(pageSizer);
+ notebook->AddPage(page, _("Skirmishes"));
+ }
+
+ notebook->SetSelection(0);
+
+ sizer->Add(notebook, wxSizerFlags().Proportion(1).Expand());
+
+ sizer->AddSpacer(5);
+
+ wxSizer* filenameSizer = new wxBoxSizer(wxHORIZONTAL);
+ filenameSizer->AddSpacer(10);
+ filenameSizer->Add(new wxStaticText(this, wxID_ANY, _("Map name: ")), wxSizerFlags().Align(wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL));
+ wxTextCtrl* filename = new wxTextCtrl(this, ID_MapDialogFilename, wxEmptyString);
+ if (m_Type == MAPDIALOG_OPEN)
+ filename->Disable();
+ filenameSizer->Add(filename, wxSizerFlags().Proportion(1).Expand());
+ sizer->Add(filenameSizer, wxSizerFlags().Expand());
+
+ sizer->AddSpacer(20);
+
+ wxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
+ if (m_Type == MAPDIALOG_OPEN)
+ buttonSizer->Add(new wxButton(this, wxID_OPEN, _("Open")));
+ else // MAPDIALOG_SAVE
+ buttonSizer->Add(new wxButton(this, wxID_SAVE, _("Save")));
+ buttonSizer->AddSpacer(5);
+ buttonSizer->Add(new wxButton(this, wxID_CANCEL, _("Cancel")));
+
+ sizer->Add(buttonSizer, wxSizerFlags().Align(wxALIGN_RIGHT).Border(wxRIGHT|wxBOTTOM, 10));
+
+ SetSizer(sizer);
+
+ Layout();
+ Thaw();
+}
+
+wxString MapDialog::GetFilename() const
+{
+ wxFileName filename(m_Filename, wxPATH_UNIX);
+ filename.SetExt(L"xml");
+ if (m_SelectedPage == 0)
+ return scenarioPath + filename.GetFullPath(wxPATH_UNIX);
+ else if (m_SelectedPage == 1)
+ return skirmishPath + filename.GetFullPath(wxPATH_UNIX);
+ else
+ return wxEmptyString;
+}
+
+void MapDialog::OnListBox(wxCommandEvent& evt)
+{
+ if (evt.GetInt() < 0)
+ m_Filename = wxEmptyString;
+ else
+ m_Filename = evt.GetString();
+
+ wxDynamicCast(FindWindow(ID_MapDialogFilename), wxTextCtrl)->ChangeValue(m_Filename);
+
+ if (evt.GetEventType() == wxEVT_COMMAND_LISTBOX_DOUBLECLICKED)
+ {
+ if (m_Type == MAPDIALOG_OPEN)
+ OpenFile();
+ else
+ SaveFile();
+ }
+}
+
+void MapDialog::OnCancel(wxCommandEvent& WXUNUSED(evt))
+{
+ EndModal(wxID_CANCEL);
+}
+
+void MapDialog::OnOpen(wxCommandEvent& WXUNUSED(evt))
+{
+ OpenFile();
+}
+
+void MapDialog::OnSave(wxCommandEvent& WXUNUSED(evt))
+{
+ SaveFile();
+}
+
+void MapDialog::OnFilename(wxCommandEvent& evt)
+{
+ m_Filename = evt.GetString();
+}
+
+void MapDialog::OnNotebookChanged(wxNotebookEvent& evt)
+{
+ m_SelectedPage = evt.GetSelection();
+}
+
+void MapDialog::OpenFile()
+{
+ wxString filename = GetFilename();
+ if (filename.empty())
+ return;
+
+ AtlasMessage::qVFSFileExists qry(filename.wc_str());
+ qry.Post();
+ if (!qry.exists)
+ return;
+
+ EndModal(wxID_OK);
+}
+
+void MapDialog::SaveFile()
+{
+ wxString filename = GetFilename();
+ if (filename.empty())
+ return;
+
+ // TODO: this test would work better outside the VFS
+ AtlasMessage::qVFSFileExists qry(filename.wc_str());
+ qry.Post();
+ if (qry.exists)
+ {
+ if (wxMessageBox(_("WARNING: '") + filename + _("' already exists, it may be overwritten. Continue?"), _("Overwrite map confirmation"), wxICON_EXCLAMATION | wxYES_NO) != wxYES)
+ return;
+ }
+
+ EndModal(wxID_OK);
+}
+
+BEGIN_EVENT_TABLE(MapDialog, wxDialog)
+ EVT_BUTTON (wxID_CANCEL, MapDialog::OnCancel)
+ EVT_BUTTON (wxID_OPEN, MapDialog::OnOpen)
+ EVT_BUTTON (wxID_SAVE, MapDialog::OnSave)
+ EVT_LISTBOX (wxID_ANY, MapDialog::OnListBox)
+ EVT_LISTBOX_DCLICK (wxID_ANY, MapDialog::OnListBox)
+ EVT_TEXT (ID_MapDialogFilename, MapDialog::OnFilename)
+ EVT_NOTEBOOK_PAGE_CHANGED (ID_MapDialogNotebook, MapDialog::OnNotebookChanged)
+END_EVENT_TABLE()
Property changes on: ps/trunk/source/tools/atlas/AtlasUI/CustomControls/MapDialog/MapDialog.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp (revision 13937)
+++ ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp (revision 13938)
@@ -1,1024 +1,971 @@
/* 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 "ScenarioEditor.h"
#include "wx/busyinfo.h"
#include "wx/config.h"
#include "wx/dir.h"
#include "wx/evtloop.h"
#include "wx/ffile.h"
#include "wx/filename.h"
#include "wx/image.h"
#include "wx/sysopt.h"
#include "wx/tooltip.h"
#include "General/AtlasEventLoop.h"
#include "General/Datafile.h"
#include "CustomControls/Buttons/ToolButton.h"
#include "CustomControls/Canvas/Canvas.h"
#include "CustomControls/HighResTimer/HighResTimer.h"
+#include "CustomControls/MapDialog/MapDialog.h"
#include "GameInterface/MessagePasser.h"
#include "GameInterface/Messages.h"
#include "AtlasScript/ScriptInterface.h"
#include "Misc/KeyMap.h"
#include "Tools/Common/Tools.h"
#include "Tools/Common/Brushes.h"
#include "Tools/Common/MiscState.h"
static HighResTimer g_Timer;
using namespace AtlasMessage;
//////////////////////////////////////////////////////////////////////////
// GL functions exported from DLL, and called by game (in a separate
// thread to the standard wx one)
ATLASDLLIMPEXP void Atlas_GLSetCurrent(void* canvas)
{
static_cast