Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml (revision 14098)
@@ -1,118 +1,121 @@
Index: ps/trunk/binaries/data/mods/public/gui/page_options.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/page_options.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/page_options.xml (revision 14098)
@@ -1,15 +1,12 @@
common/setup.xmlcommon/styles.xmlcommon/sprite1.xmlcommon/icon_sprites.xmlcommon/init.xml
-
common/global.xml
-
-common/common_sprites.xml
-common/common_styles.xml
-
+ common/common_sprites.xml
+ common/common_styles.xmloptions/options.xml
Index: ps/trunk/binaries/data/mods/public/gui/session/messages.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/messages.js (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/session/messages.js (revision 14098)
@@ -1,532 +1,538 @@
// Chat data
const CHAT_TIMEOUT = 30000;
const MAX_NUM_CHAT_LINES = 20;
var chatMessages = [];
var chatTimers = [];
// Notification Data
const NOTIFICATION_TIMEOUT = 10000;
const MAX_NUM_NOTIFICATION_LINES = 3;
var notifications = [];
var notificationsTimers = [];
var cheats = getCheatsData();
function getCheatsData()
{
var cheats = {};
var cheatFileList = getJSONFileList("simulation/data/cheats/");
for each (var fileName in cheatFileList)
{
var currentCheat = parseJSONData("simulation/data/cheats/"+fileName+".json");
if (Object.keys(cheats).indexOf(currentCheat.Name) !== -1)
warn("Cheat name '"+currentCheat.Name+"' is already present");
else
cheats[currentCheat.Name] = currentCheat.Data;
}
return cheats;
}
// Notifications
function handleNotifications()
{
var notification = Engine.GuiInterfaceCall("GetNextNotification");
if (!notification)
return;
// Handle chat notifications specially
if (notification.type == "chat")
{
var guid = findGuidForPlayerID(g_PlayerAssignments, notification.player);
if (guid == undefined)
{
addChatMessage({
"type": "message",
"guid": -1,
"player": notification.player,
"text": notification.message
});
} else {
addChatMessage({
"type": "message",
"guid": findGuidForPlayerID(g_PlayerAssignments, notification.player),
"text": notification.message
});
}
}
else if (notification.type == "defeat")
{
addChatMessage({
"type": "defeat",
"guid": findGuidForPlayerID(g_PlayerAssignments, notification.player),
"player": notification.player
});
// If the diplomacy panel is open refresh it.
if (isDiplomacyOpen)
openDiplomacy();
}
else if (notification.type == "diplomacy")
{
addChatMessage({
"type": "diplomacy",
"player": notification.player,
"player1": notification.player1,
"status": notification.status
});
// If the diplomacy panel is open refresh it.
if (isDiplomacyOpen)
openDiplomacy();
}
else if (notification.type == "quit")
{
// Used for AI testing
exit();
}
else if (notification.type == "tribute")
{
addChatMessage({
"type": "tribute",
"player": notification.player,
"player1": notification.player1,
"amounts": notification.amounts
});
}
else if (notification.type == "attack")
{
if (notification.player == Engine.GetPlayerID())
{
if (Engine.ConfigDB_GetValue("user", "gui.session.attacknotificationmessage") === "true")
{
addChatMessage({
"type": "attack",
"player": notification.player,
"attacker": notification.attacker
});
}
}
}
else
{
// Only display notifications directed to this player
if (notification.player == Engine.GetPlayerID())
{
notifications.push(notification);
notificationsTimers.push(setTimeout(removeOldNotifications, NOTIFICATION_TIMEOUT));
if (notifications.length > MAX_NUM_NOTIFICATION_LINES)
removeOldNotifications();
else
displayNotifications();
}
}
}
function removeOldNotifications()
{
clearTimeout(notificationsTimers[0]); // The timer only needs to be cleared when new notifications bump old notifications off
notificationsTimers.shift();
notifications.shift();
displayNotifications();
}
function displayNotifications()
{
var messages = [];
for each (var n in notifications)
messages.push(n.message);
getGUIObjectByName("notificationText").caption = messages.join("\n");
}
// Returns [username, playercolor] for the given player
function getUsernameAndColor(player)
{
// This case is hit for AIs, whose names don't exist in playerAssignments.
var color = g_Players[player].color;
return [
escapeText(g_Players[player].name),
color.r + " " + color.g + " " + color.b,
];
}
// Messages
function handleNetMessage(message)
{
log("Net message: " + uneval(message));
switch (message.type)
{
case "netstatus":
// If we lost connection, further netstatus messages are useless
if (g_Disconnected)
return;
var obj = getGUIObjectByName("netStatus");
switch (message.status)
{
case "waiting_for_players":
obj.caption = "Waiting for other players to connect...";
obj.hidden = false;
break;
case "join_syncing":
obj.caption = "Synchronising gameplay with other players...";
obj.hidden = false;
break;
case "active":
obj.caption = "";
obj.hidden = true;
break;
case "connected":
obj.caption = "Connected to the server.";
obj.hidden = false;
break;
case "authenticated":
obj.caption = "Connection to the server has been authenticated.";
obj.hidden = false;
break;
case "disconnected":
g_Disconnected = true;
obj.caption = "Connection to the server has been lost.\n\nThe game has ended.";
obj.hidden = false;
break;
default:
error("Unrecognised netstatus type "+message.status);
break;
}
break;
case "players":
// Find and report all leavings
for (var host in g_PlayerAssignments)
{
if (! message.hosts[host])
{
// Tell the user about the disconnection
addChatMessage({ "type": "disconnect", "guid": host });
// Update the cached player data, so we can display the disconnection status
updatePlayerDataRemove(g_Players, host);
}
}
// Find and report all joinings
for (var host in message.hosts)
{
if (! g_PlayerAssignments[host])
{
// Update the cached player data, so we can display the correct name
updatePlayerDataAdd(g_Players, host, message.hosts[host]);
// Tell the user about the connection
addChatMessage({ "type": "connect", "guid": host }, message.hosts);
}
}
g_PlayerAssignments = message.hosts;
+
+ if (g_IsController)
+ {
+ var players = [ assignment.name for each (assignment in g_PlayerAssignments) ]
+ Engine.SendChangeStateGame(Object.keys(g_PlayerAssignments).length, players.join(", "));
+ }
break;
case "chat":
addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
break;
// To prevent errors, ignore these message types that occur during autostart
case "gamesetup":
case "start":
break;
default:
error("Unrecognised net message type "+message.type);
}
}
function submitChatDirectly(text)
{
if (text.length)
{
if (g_IsNetworked)
Engine.SendNetworkChat(text);
else
addChatMessage({ "type": "message", "guid": "local", "text": text });
}
}
function submitChatInput()
{
var input = getGUIObjectByName("chatInput");
var text = input.caption;
var isCheat = false;
if (text.length)
{
if (g_Players[Engine.GetPlayerID()].cheatsEnabled)
{
for each (var cheat in Object.keys(cheats))
{
// Line must start with the cheat.
if (text.indexOf(cheat) !== 0)
continue;
var number;
if (cheats[cheat].DefaultNumber !== undefined)
{
// Match the first word in the substring.
var match = text.substr(cheat.length).match(/\S+/);
if (match && match[0])
number = Math.floor(match[0]);
if (number <= 0 || isNaN(number))
number = cheats[cheat].DefaultNumber;
}
Engine.PostNetworkCommand({
"type": "cheat",
"action": cheats[cheat].Action,
"number": number,
"text": cheats[cheat].Type,
"selected": g_Selection.toList(),
"templates": cheats[cheat].Templates,
"player": Engine.GetPlayerID()});
isCheat = true;
break;
}
}
if (!isCheat)
{
if (getGUIObjectByName("toggleTeamChat").checked)
text = "/team " + text;
if (g_IsNetworked)
Engine.SendNetworkChat(text);
else
addChatMessage({ "type": "message", "guid": "local", "text": text });
}
input.caption = ""; // Clear chat input
}
input.blur(); // Remove focus
toggleChatWindow();
}
function addChatMessage(msg, playerAssignments)
{
// Default to global assignments, but allow overriding for when reporting
// new players joining
if (!playerAssignments)
playerAssignments = g_PlayerAssignments;
var playerColor, username;
// No prefix by default. May be set by parseChatCommands().
msg.prefix = "";
if (playerAssignments[msg.guid])
{
var n = playerAssignments[msg.guid].player;
playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b;
username = escapeText(playerAssignments[msg.guid].name);
// Parse in-line commands in regular messages.
if (msg.type == "message")
parseChatCommands(msg, playerAssignments);
}
else if (msg.type == "defeat" && msg.player)
{
[username, playerColor] = getUsernameAndColor(msg.player);
}
else if (msg.type == "message")
{
[username, playerColor] = getUsernameAndColor(msg.player);
parseChatCommands(msg, playerAssignments);
}
else
{
playerColor = "255 255 255";
username = "Unknown player";
}
var message = escapeText(msg.text);
var formatted;
switch (msg.type)
{
case "connect":
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has joined the game.";
break;
case "disconnect":
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has left the game.";
break;
case "defeat":
// In singleplayer, the local player is "You". "You has" is incorrect.
var verb = (!g_IsNetworked && msg.player == Engine.GetPlayerID()) ? "have" : "has";
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] " + verb + " been defeated.";
break;
case "diplomacy":
var status = (msg.status == "ally" ? "allied" : (msg.status == "enemy" ? "at war" : "neutral"));
if (msg.player == Engine.GetPlayerID())
{
[username, playerColor] = getUsernameAndColor(msg.player1);
formatted = "You are now "+status+" with [color=\"" + playerColor + "\"]"+username + "[/color].";
}
else if (msg.player1 == Engine.GetPlayerID())
{
[username, playerColor] = getUsernameAndColor(msg.player);
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] is now " + status + " with you."
}
else // No need for other players to know of this.
return;
break;
case "tribute":
if (msg.player != Engine.GetPlayerID())
return;
[username, playerColor] = getUsernameAndColor(msg.player1);
// Format the amounts to proper English: 200 food, 100 wood, and 300 metal; 100 food; 400 wood and 200 stone
var amounts = Object.keys(msg.amounts)
.filter(function (type) { return msg.amounts[type] > 0; })
.map(function (type) { return msg.amounts[type] + " " + type; });
if (amounts.length > 1)
{
var lastAmount = amounts.pop();
amounts = amounts.join(", ") + " and " + lastAmount;
}
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has sent you " + amounts + ".";
break;
case "attack":
if (msg.player != Engine.GetPlayerID())
return;
[username, playerColor] = getUsernameAndColor(msg.attacker);
formatted = "You have been attacked by [color=\"" + playerColor + "\"]" + username + "[/color]!";
break;
case "message":
// May have been hidden by the 'team' command.
if (msg.hide)
return;
if (msg.action)
{
Engine.Console_Write(msg.prefix + "* " + username + " " + message);
formatted = msg.prefix + "* [color=\"" + playerColor + "\"]" + username + "[/color] " + message;
}
else
{
Engine.Console_Write(msg.prefix + "<" + username + "> " + message);
formatted = msg.prefix + "<[color=\"" + playerColor + "\"]" + username + "[/color]> " + message;
}
break;
default:
error("Invalid chat message '" + uneval(msg) + "'");
return;
}
chatMessages.push(formatted);
chatTimers.push(setTimeout(removeOldChatMessages, CHAT_TIMEOUT));
if (chatMessages.length > MAX_NUM_CHAT_LINES)
removeOldChatMessages();
else
getGUIObjectByName("chatText").caption = chatMessages.join("\n");
}
function removeOldChatMessages()
{
clearTimeout(chatTimers[0]); // The timer only needs to be cleared when new messages bump old messages off
chatTimers.shift();
chatMessages.shift();
getGUIObjectByName("chatText").caption = chatMessages.join("\n");
}
// Parses chat messages for commands.
function parseChatCommands(msg, playerAssignments)
{
// Only interested in messages that start with '/'.
if (!msg.text || msg.text[0] != '/')
return;
var sender;
if (playerAssignments[msg.guid])
sender = playerAssignments[msg.guid].player;
else
sender = msg.player;
var recurse = false;
var split = msg.text.split(/\s/);
// Parse commands embedded in the message.
switch (split[0])
{
case "/all":
// Resets values that 'team' or 'enemy' may have set.
msg.prefix = "";
msg.hide = false;
recurse = true;
break;
case "/team":
// Check if we are in a team.
if (g_Players[Engine.GetPlayerID()] && g_Players[Engine.GetPlayerID()].team != -1)
{
if (g_Players[Engine.GetPlayerID()].team != g_Players[sender].team)
msg.hide = true;
else
msg.prefix = "(Team) ";
}
else
msg.hide = true;
recurse = true;
break;
case "/enemy":
// Check if we are in a team.
if (g_Players[Engine.GetPlayerID()] && g_Players[Engine.GetPlayerID()].team != -1)
{
if (g_Players[Engine.GetPlayerID()].team == g_Players[sender].team && sender != Engine.GetPlayerID())
msg.hide = true;
else
msg.prefix = "(Enemy) ";
}
recurse = true;
break;
case "/me":
msg.action = true;
break;
case "/msg":
var trimmed = msg.text.substr(split[0].length + 1);
var matched = "";
// Reject names which don't match or are a superset of the intended name.
for each (var player in playerAssignments)
if (trimmed.indexOf(player.name + " ") == 0 && player.name.length > matched.length)
matched = player.name;
// If the local player's name was the longest one matched, show the message.
var playerName = g_Players[Engine.GetPlayerID()].name;
if (matched.length && (matched == playerName || sender == Engine.GetPlayerID()))
{
msg.prefix = "(Private) ";
msg.text = trimmed.substr(matched.length + 1);
msg.hide = false; // Might override team message hiding.
return;
}
else
msg.hide = true;
break;
default:
return;
}
msg.text = msg.text.substr(split[0].length + 1);
// Hide the message if parsing commands left it empty.
if (!msg.text.length)
msg.hide = true;
// Attempt to parse more commands if the current command allows it.
if (recurse)
parseChatCommands(msg, playerAssignments);
}
Index: ps/trunk/binaries/data/mods/public/gui/summary/summary.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/summary/summary.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/summary/summary.xml (revision 14098)
@@ -1,295 +1,304 @@
onTick();
Summary
selectPanel(0);ScoreselectPanel(1);Units/buildingsselectPanel(2);ConquestselectPanel(3);ResourcesselectPanel(4);Market
Player name
Economy
score
Military
score
Exploration
score
Total
score
Player name
Units
trained
Units
lost
Enemy units
killed
Buildings
constructed
Buildings
lost
Enemy
buildings
destroyed
Player name
Civ centres
built
Enemy
civ centres
destroyed
Map
exploration
Player name
Resource Statistics (Gathered / Used)
Food
Wood
Stone
Metal
Vegetarian
ratio
Treasures
collected
Tributes
(Sent / Received)
Player name
Food
exchanged
Wood
exchanged
Stone
exchanged
Metal
exchanged
Barter
efficiency
Trade
income
Continue
-
- Engine.SwitchGuiPage("page_pregame.xml");
+
Index: ps/trunk/build/premake/premake4.lua
===================================================================
--- ps/trunk/build/premake/premake4.lua (revision 14097)
+++ ps/trunk/build/premake/premake4.lua (revision 14098)
@@ -1,1384 +1,1411 @@
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 = "without-lobby", description = "Disable the use of gloox and the multiplayer lobby" }
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 = "with-c++11", description = "Enable C++11 on GCC" }
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 = "build-shared-glooxwrapper", description = "Rebuild glooxwrapper DLL for Windows. Requires the same compiler version that gloox was built with" }
newoption { trigger = "use-shared-glooxwrapper", description = "Use prebuilt glooxwrapper DLL for Windows" }
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
+ if _OPTIONS["without-lobby"] then
+ defines { "CONFIG2_LOBBY=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" }
flags { "EnableSSE2" } -- Enable SSE2 code generation for VS
-- 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 _OPTIONS["with-c++11"] then
buildoptions {
-- Enable C++11 standard. VS2010 and higher automatically support C++11
-- but we have to enable it manually on GNU C++ and Intel C++
"-std=c++0x"
}
end
if arch == "arm" then
-- disable warnings about va_list ABI change and use
-- compile-time flags for futher configuration.
buildoptions { "-Wno-psabi" }
if _OPTIONS["android"] then
-- Android uses softfp, so we should too.
buildoptions { "-mfloat-abi=softfp" }
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 = {}
+static_lib_names_debug = {}
+static_lib_names_release = {}
-- 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
function setup_shared_lib_project (project_name, rel_source_dirs, extern_libs, extra_params)
local target_type = "SharedLib"
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" }
links { "delayimp" }
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, {})
+<<<<<<< HEAD
+ if not _OPTIONS["without-lobby"] then
+ source_dirs = {
+ "lobby",
+ }
+
+ extern_libs = {
+ "spidermonkey",
+ "boost",
+ "gloox",
+ }
+ setup_static_lib_project("lobby", source_dirs, extern_libs, {})
+ end
+
+=======
+>>>>>>> 1a7a32ad84f645a7a907472ab2d3fd9be00d50c2
if _OPTIONS["use-shared-glooxwrapper"] and not _OPTIONS["build-shared-glooxwrapper"] then
table.insert(static_lib_names_debug, "glooxwrapper_dbg")
table.insert(static_lib_names_release, "glooxwrapper")
else
source_dirs = {
"lobby/glooxwrapper",
}
extern_libs = {
"boost",
"gloox",
}
if _OPTIONS["build-shared-glooxwrapper"] then
setup_shared_lib_project("glooxwrapper", source_dirs, extern_libs, {})
else
setup_static_lib_project("glooxwrapper", source_dirs, extern_libs, {})
end
end
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"
+ "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"
+ "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" or _ACTION == "vs2012" 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
+if not _OPTIONS["without-lobby"] then
+ table.insert(used_extern_libs, "gloox")
+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" }
links { "log" }
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" and _ACTION ~= "vs2012" 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" and _ACTION ~= "vs2012" 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 }
configuration "Debug"
links { static_lib_names_debug }
configuration "Release"
links { static_lib_names_release }
configuration { }
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" }
-- Enables console for the TEST project on Windows
linkoptions { "/SUBSYSTEM:CONSOLE" }
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 }
configuration "Debug"
links { static_lib_names_debug }
configuration "Release"
links { static_lib_names_release }
configuration { }
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/binaries/data/config/default.cfg
===================================================================
--- ps/trunk/binaries/data/config/default.cfg (revision 14097)
+++ ps/trunk/binaries/data/config/default.cfg (revision 14098)
@@ -1,349 +1,355 @@
; Global Configuration Settings
;
; **************************************************************
; * DO NOT EDIT THIS FILE if you want personal customisations: *
; * create a text file called "local.cfg" instead, and copy *
; * the lines from this file that you want to change. *
; * *
; * On Linux, create: *
; * $XDG_CONFIG_HOME/0ad/config/local.cfg *
; * (Note: $XDG_CONFIG_HOME defaults to ~/.config) *
; * *
; * On OS X, create: *
; * ~/Library/Application\ Support/0ad/config/local.cfg *
; * *
; * On Windows, create: *
; * %appdata%\0ad\config\local.cfg *
; * *
; **************************************************************
; Enable/disable windowed mode by default. (Use Alt+Enter to toggle in the game.)
windowed = false
; Enable/disable the splashscreen
splashscreenenable = true
; Pause the game on window focus loss (Only applicable to single player mode)
pauseonfocusloss = true
; Default player name to use in multiplayer
; playername = "anonymous"
; Default server name or IP to use in multiplayer
multiplayerserver = "127.0.0.1"
; Force a particular resolution. (If these are 0, the default is
; to keep the current desktop resolution in fullscreen mode or to
; use 1024x768 in windowed mode.)
xres = 0
yres = 0
; Force a non-standard bit depth (if 0 then use the current desktop bit depth)
bpp = 0
; System settings:
waternormals = true
waterrealdepth = true
waterfoam = false
watercoastalwaves = false
waterrefraction = true
waterreflection = true
watershadows = false
shadows = true
shadowpcf = true
vsync = false
particles = true
silhouettes = true
showsky = false
nos3tc = false
noautomipmap = true
novbo = false
noframebufferobject = false
; Disable hardware cursors
nohwcursor = false
; Linux only: Set the driconf force_s3tc_enable option at startup,
; for compressed texture support
force_s3tc_enable = true
; Specify the render path. This can be one of:
; default Automatically select one of the below, depending on system capabilities
; fixed Only use OpenGL fixed function pipeline
; shader Use vertex/fragment shaders for transform and lighting where possible
; Using 'fixed' instead of 'default' may work around some graphics-related problems,
; but will reduce performance and features when a modern graphics card is available.
renderpath = default
;;;;; EXPERIMENTAL ;;;;;
; Prefer GLSL shaders over ARB shaders (not recommended). REQUIRES gentangents=true.
preferglsl = false
; Generate tangents for normal and parallax mapping. REQUIRES preferglsl=true.
gentangents = false
; Experimental probably-non-working GPU skinning support; requires preferglsl; use at own risk
gpuskinning = false
; Use smooth LOS interpolation; REQUIRES preferglsl=true.
smoothlos = false
; Use screen-space postprocessing filters (HDR, bloom, DOF, etc). Incompatible with fixed renderpath.
postproc = false
; Quality level of shader effects (set to 10 to display effects)
materialmgr.quality = 0.0
; Maximum distance to display parallax effect. Set to 0 to disable parallax.
materialmgr.PARALLAX_DIST.max = 150
; Maximum distance to display high quality parallax effect.
materialmgr.PARALLAX_HQ_DIST.max = 75
; Maximum distance to display very high quality parallax effect. Set to 30 to enable.
materialmgr.PARALLAX_VHQ_DIST.max = 0
;;;;;;;;;;;;;;;;;;;;;;;;
; Replace alpha-blending with alpha-testing, for performance experiments
forcealphatest = false
; Opt-in online user reporting system
userreport.url = "http://feedback.wildfiregames.com/report/upload/v1/"
; Colour of the sky (in "r g b" format)
skycolor = "0 0 0"
; GENERAL PREFERENCES:
sound.mastergain = 0.9
sound.musicgain = 0.2
sound.ambientgain = 0.6
sound.actiongain = 0.7
sound.uigain = 0.7
; Camera control settings
view.scroll.speed = 120.0
view.scroll.speed.modifier = 1.05 ; Multiplier for changing scroll speed
view.rotate.x.speed = 1.2
view.rotate.x.min = 28.0
view.rotate.x.max = 60.0
view.rotate.x.default = 35.0
view.rotate.y.speed = 2.0
view.rotate.y.speed.wheel = 0.45
view.rotate.y.default = 0.0
view.rotate.speed.modifier = 1.05 ; Multiplier for changing rotation speed
view.drag.speed = 0.5
view.zoom.speed = 256.0
view.zoom.speed.wheel = 32.0
view.zoom.min = 50.0
view.zoom.max = 200.0
view.zoom.default = 120.0
view.zoom.speed.modifier = 1.05 ; Multiplier for changing zoom speed
view.pos.smoothness = 0.1
view.zoom.smoothness = 0.4
view.rotate.x.smoothness = 0.5
view.rotate.y.smoothness = 0.3
view.near = 2.0 ; Near plane distance
view.far = 4096.0 ; Far plane distance
view.fov = 45.0 ; Field of view (degrees), lower is narrow, higher is wide
view.height.smoothness = 0.5
view.height.min = 16
; How close do we have to be to the actual location in order to jump back to the previous one?
camerajump.threshold = 40
; HOTKEY MAPPINGS:
; Each one of the specified keys will trigger the action on the left
; for multiple-key combinations, separate keys with '+' and enclose the entire thing
; in doublequotes.
; See keys.txt for the list of key names.
; > SYSTEM SETTINGS
hotkey.exit = "Alt+F4", "Ctrl+Break", "Super+Q" ; Exit to desktop
hotkey.leave = Escape ; End current game or Exit
hotkey.confirm = Return ; Confirm the current command.
hotkey.pause = Pause ; Pause/unpause game
hotkey.screenshot = F2 ; Take PNG screenshot
hotkey.bigscreenshot = "Shift+F2" ; Take large BMP screenshot
hotkey.togglefullscreen = "Alt+Return" ; Toggle fullscreen/windowed mode
hotkey.screenshot.watermark = "Alt+K" ; Toggle product/company watermark for official screenshots
hotkey.wireframe = "Alt+W" ; Toggle wireframe mode
hotkey.silhouettes = "Alt+S" ; Toggle unit silhouettes
hotkey.showsky = "Alt+Z" ; Toggle sky
; > CAMERA SETTINGS
hotkey.camera.reset = "R" ; Reset camera rotation to default.
hotkey.camera.follow = "F" ; Follow the first unit in the selection
hotkey.camera.zoom.in = Plus, Equals, NumPlus ; Zoom camera in (continuous control)
hotkey.camera.zoom.out = Minus, NumMinus ; Zoom camera out (continuous control)
hotkey.camera.zoom.wheel.in = WheelUp ; Zoom camera in (stepped control)
hotkey.camera.zoom.wheel.out = WheelDown ; Zoom camera out (stepped control)
hotkey.camera.rotate.up = "Ctrl+UpArrow", "Ctrl+W" ; Rotate camera to look upwards
hotkey.camera.rotate.down = "Ctrl+DownArrow", "Ctrl+S" ; Rotate camera to look downwards
hotkey.camera.rotate.cw = "Ctrl+LeftArrow", "Ctrl+A", Q ; Rotate camera clockwise around terrain
hotkey.camera.rotate.ccw = "Ctrl+RightArrow", "Ctrl+D", E ; Rotate camera anticlockwise around terrain
hotkey.camera.rotate.wheel.cw = "Shift+WheelUp", MouseX1 ; Rotate camera clockwise around terrain (stepped control)
hotkey.camera.rotate.wheel.ccw = "Shift+WheelDown", MouseX2 ; Rotate camera anticlockwise around terrain (stepped control)
hotkey.camera.pan = MouseMiddle ; Enable scrolling by moving mouse
hotkey.camera.left = A, LeftArrow ; Scroll or rotate left
hotkey.camera.right = D, RightArrow ; Scroll or rotate right
hotkey.camera.up = W, UpArrow ; Scroll or rotate up/forwards
hotkey.camera.down = S, DownArrow ; Scroll or rotate down/backwards
hotkey.camera.scroll.speed.increase = "Ctrl+Shift+S" ; Increase scroll speed
hotkey.camera.scroll.speed.decrease = "Ctrl+Alt+S" ; Decrease scroll speed
hotkey.camera.rotate.speed.increase = "Ctrl+Shift+R" ; Increase rotation speed
hotkey.camera.rotate.speed.decrease = "Ctrl+Alt+R" ; Decrease rotation speed
hotkey.camera.zoom.speed.increase = "Ctrl+Shift+Z" ; Increase zoom speed
hotkey.camera.zoom.speed.decrease = "Ctrl+Alt+Z" ; Decrease zoom speed
hotkey.camera.jump.1 = F5 ; Jump to position N
hotkey.camera.jump.2 = F6
hotkey.camera.jump.3 = F7
hotkey.camera.jump.4 = F8
;hotkey.camera.jump.5 =
;hotkey.camera.jump.6 =
;hotkey.camera.jump.7 =
;hotkey.camera.jump.8 =
;hotkey.camera.jump.9 =
;hotkey.camera.jump.10 =
hotkey.camera.jump.set.1 = "Ctrl+F5" ; Set jump position N
hotkey.camera.jump.set.2 = "Ctrl+F6"
hotkey.camera.jump.set.3 = "Ctrl+F7"
hotkey.camera.jump.set.4 = "Ctrl+F8"
;hotkey.camera.jump.set.5 =
;hotkey.camera.jump.set.6 =
;hotkey.camera.jump.set.7 =
;hotkey.camera.jump.set.8 =
;hotkey.camera.jump.set.9 =
;hotkey.camera.jump.set.10 =
; > CONSOLE SETTINGS
hotkey.console.toggle = BackQuote, F9 ; Open/close console
; > CLIPBOARD CONTROLS
hotkey.copy = "Ctrl+C" ; Copy to clipboard
hotkey.paste = "Ctrl+V" ; Paste from clipboard
hotkey.cut = "Ctrl+X" ; Cut selected text and copy to the clipboard
; > ENTITY SELECTION
hotkey.selection.add = Shift ; Add units to selection
hotkey.selection.milonly = Alt ; Add only military units to selection
hotkey.selection.remove = Ctrl ; Remove units from selection
hotkey.selection.cancel = Esc ; Un-select all units and cancel building placement
hotkey.selection.idleworker = Period ; Select next idle worker
hotkey.selection.idlewarrior = ForwardSlash ; Select next idle warrior
hotkey.selection.offscreen = Alt ; Include offscreen units in selection
hotkey.selection.group.select.0 = 0
hotkey.selection.group.save.0 = "Ctrl+0"
hotkey.selection.group.add.0 = "Shift+0"
hotkey.selection.group.select.1 = 1
hotkey.selection.group.save.1 = "Ctrl+1"
hotkey.selection.group.add.1 = "Shift+1"
hotkey.selection.group.select.2 = 2
hotkey.selection.group.save.2 = "Ctrl+2"
hotkey.selection.group.add.2 = "Shift+2"
hotkey.selection.group.select.3 = 3
hotkey.selection.group.save.3 = "Ctrl+3"
hotkey.selection.group.add.3 = "Shift+3"
hotkey.selection.group.select.4 = 4
hotkey.selection.group.save.4 = "Ctrl+4"
hotkey.selection.group.add.4 = "Shift+4"
hotkey.selection.group.select.5 = 5
hotkey.selection.group.save.5 = "Ctrl+5"
hotkey.selection.group.add.5 = "Shift+5"
hotkey.selection.group.select.6 = 6
hotkey.selection.group.save.6 = "Ctrl+6"
hotkey.selection.group.add.6 = "Shift+6"
hotkey.selection.group.select.7 = 7
hotkey.selection.group.save.7 = "Ctrl+7"
hotkey.selection.group.add.7 = "Shift+7"
hotkey.selection.group.select.8 = 8
hotkey.selection.group.save.8 = "Ctrl+8"
hotkey.selection.group.add.8 = "Shift+8"
hotkey.selection.group.select.9 = 9
hotkey.selection.group.save.9 = "Ctrl+9"
hotkey.selection.group.add.9 = "Shift+9"
; > SESSION CONTROLS
hotkey.session.kill = Delete ; Destroy selected units
hotkey.session.stop = "H" ; Stop the current action
hotkey.session.attack = "Ctrl+Alt" ; Modifier to force attack instead of another action
hotkey.session.garrison = Ctrl ; Modifier to garrison when clicking on building
hotkey.session.attackmove = Ctrl ; Modifier to attackmove when clicking on a point
hotkey.session.queue = Shift ; Modifier to queue unit orders instead of replacing
hotkey.session.batchtrain = Shift ; Modifier to train units in batches
hotkey.session.massbarter = Shift ; Modifier to barter bunch of resources
hotkey.session.masstribute = Shift ; Modifier to tribute bunch of resources
hotkey.session.unloadtype = Shift ; Modifier to unload all units of type
hotkey.session.deselectgroup = Ctrl ; Modifier to deselect units when clicking group icon, instead of selecting
hotkey.session.rotate.cw = RightBracket ; Rotate building placement preview clockwise
hotkey.session.rotate.ccw = LeftBracket ; Rotate building placement preview anticlockwise
hotkey.timewarp.fastforward = Space ; If timewarp mode enabled, speed up the game
hotkey.timewarp.rewind = Backspace ; If timewarp mode enabled, go back to earlier point in the game
; > UNIT TRAINING
hotkey.session.queueunit.1 = "Z" ; add first unit type to queue
hotkey.session.queueunit.2 = "X" ; add second unit type to queue
hotkey.session.queueunit.3 = "C" ; add third unit type to queue
hotkey.session.queueunit.4 = "V" ; add fourth unit type to queue
hotkey.session.queueunit.5 = "B" ; add fivth unit type to queue
hotkey.session.queueunit.6 = "N" ; add sixth unit type to queue
hotkey.session.queueunit.7 = "M" ; add seventh unit type to queue
hotkey.session.queueunit.8 = Comma ; add eighth unit type to queue
; > OVERLAY KEYS
hotkey.fps.toggle = "Alt+F" ; Toggle frame counter
hotkey.session.devcommands.toggle = "Alt+D" ; Toggle developer commands panel
hotkey.session.gui.toggle = "Alt+G" ; Toggle visibility of session GUI
hotkey.menu.toggle = "F10" ; Toggle in-game menu
hotkey.timeelapsedcounter.toggle = "F12" ; Toggle time elapsed counter
hotkey.session.showstatusbars = Tab ; Toggle display of status bars
; > HOTKEYS ONLY
hotkey.chat = Return ; Toggle chat window
hotkey.teamchat = "T" ; Toggle chat window in team chat mode
+hotkey.complete.playername = "Alt+C" ; Complete a player's name
; > GUI TEXTBOX HOTKEYS
hotkey.text.delete.left = "Ctrl+Backspace" ; Delete word to the left of cursor
hotkey.text.delete.right = "Ctrl+Del" ; Delete word to the right of cursor
hotkey.text.move.left = "Ctrl+LeftArrow" ; Move cursor to start of word to the left of cursor
hotkey.text.move.right = "Ctrl+RightArrow" ; Move cursor to start of word to the right of cursor
; > PROFILER
hotkey.profile.toggle = "F11" ; Enable/disable real-time profiler
hotkey.profile.save = "Shift+F11" ; Save current profiler data to logs/profile.txt
hotkey.profile2.enable = "F11" ; Enable HTTP/GPU modes for new profiler
profiler2.http.autoenable = false ; Enable HTTP server output at startup (default off for security/performance)
profiler2.script.enable = false ; Enable Javascript profiling. Needs to be set before startup and can't be changed later. (default off for performance)
profiler2.gpu.autoenable = false ; Enable GPU timing at startup (default off for performance/compatibility)
profiler2.gpu.arb.enable = true ; Allow GL_ARB_timer_query timing mode when available
profiler2.gpu.ext.enable = true ; Allow GL_EXT_timer_query timing mode when available
profiler2.gpu.intel.enable = true ; Allow GL_INTEL_performance_queries timing mode when available
; > JS DEBUGGER
jsdebugger.enable = false ; Enable Javascript debugging (default off for security/performance)
; > QUICKSAVE
hotkey.quicksave = "Shift+F5"
hotkey.quickload = "Shift+F8"
; EXPERIMENTAL: joystick/gamepad settings
joystick.enable = false
joystick.deadzone = 8192
joystick.camera.pan.x = 0
joystick.camera.pan.y = 1
joystick.camera.rotate.x = 3
joystick.camera.rotate.y = 2
joystick.camera.zoom.in = 5
joystick.camera.zoom.out = 4
; SESSION GUI SETTINGS
gui.session.timeelapsedcounter = false ; Show the game duration in the top right corner
gui.session.attacknotificationmessage = true ; Show attack notification messages
gui.session.minimap.pingduration = 50.0 ; The duration for which an entity will be pinged after an attack notification
gui.session.minimap.blinkduration = 1.7 ; The blink duration while pinging
; GENERAL GUI SETTINGS
gui.cursorblinkrate = 0.5 ; Cursor blink rate in seconds (0.0 to disable blinking)
+
+; Multiplayer lobby preferences
+lobby.server = "lobby.wildfiregames.com" ; Address of lobby server
+lobby.xpartamupp = "xpartamupp" ; Name of the server-side xmpp client that manage games
+lobby.chattimestamp = false ; Show time chat message was posted.
Index: ps/trunk/binaries/data/mods/public/gui/lobby/lobby.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/lobby.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/lobby.js (revision 14098)
@@ -0,0 +1,797 @@
+var g_ChatMessages = [];
+var g_Name = "unknown";
+var g_GameList = {};
+var g_specialKey = Math.random();
+var g_spamMonitor = {};
+var g_spammers = {};
+var g_timestamp = Engine.ConfigDB_GetValue("user", "lobby.chattimestamp") == "true";
+var g_mapSizes = {};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+function init(attribs)
+{
+ // Play menu music
+ global.music.setState(global.music.states.MENU);
+
+ g_Name = Engine.LobbyGetNick();
+
+ g_mapSizes = initMapSizes();
+ g_mapSizes.shortNames.push("Any");
+ g_mapSizes.tiles.push("");
+
+ var mapSizeFilter = getGUIObjectByName("mapSizeFilter");
+ mapSizeFilter.list = g_mapSizes.shortNames;
+ mapSizeFilter.list_data = g_mapSizes.tiles;
+
+ var playersNumberFilter = getGUIObjectByName("playersNumberFilter");
+ playersNumberFilter.list = [2,3,4,5,6,7,8,"Any"];
+ playersNumberFilter.list_data = [2,3,4,5,6,7,8,""];
+
+ var mapTypeFilter = getGUIObjectByName("mapTypeFilter");
+ mapTypeFilter.list = ["Random", "Scenario", "Any"];
+ mapTypeFilter.list_data = ["conquest", "scenario", ""];
+
+ Engine.LobbySetPlayerPresence("available");
+ Engine.SendGetGameList();
+ Engine.SendGetBoardList();
+ updatePlayerList();
+
+ resetFilters();
+ var spamMonitorTimer = setTimeout(clearSpamMonitor, 5000);
+ var spammerTimer = setTimeout(clearSpammers, 30000);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+// Xmpp client connection management
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+function lobbyStop()
+{
+ Engine.StopXmppClient();
+}
+
+function lobbyConnect()
+{
+ Engine.ConnectXmppClient();
+}
+
+function lobbyDisconnect()
+{
+ Engine.DisconnectXmppClient();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+// Server requests functions
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+function lobbyRefreshGameList()
+{
+ Engine.SendGetGameList();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+// Update functions
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+function resetFilters()
+{
+ // Reset states of gui objects
+ getGUIObjectByName("mapSizeFilter").selected = getGUIObjectByName("mapSizeFilter").list.length - 1;
+ getGUIObjectByName("playersNumberFilter").selected = getGUIObjectByName("playersNumberFilter").list.length - 1;
+ getGUIObjectByName("mapTypeFilter").selected = getGUIObjectByName("mapTypeFilter").list.length - 1;
+ getGUIObjectByName("showFullFilter").checked = false;
+
+ // Update the list of games
+ updateGameList();
+
+ // Update info box about the game currently selected
+ selectGame(getGUIObjectByName("gamesBox").selected);
+}
+
+function applyFilters()
+{
+ // Update the list of games
+ updateGameList();
+
+ // Update info box about the game currently selected
+ selectGame(getGUIObjectByName("gamesBox").selected);
+}
+
+function displayGame(g, mapSizeFilter, playersNumberFilter, mapTypeFilter, showFullFilter)
+{
+ if(mapSizeFilter != "" && g.mapSize != mapSizeFilter) return false;
+ if(playersNumberFilter != "" && g.tnbp != playersNumberFilter) return false;
+ if(mapTypeFilter != "" && g.mapType != mapTypeFilter) return false;
+ if(!showFullFilter && g.tnbp == g.nbp) return false;
+
+ return true;
+}
+
+// Do a full update of the player listing **Only call on init**
+function updatePlayerList()
+{
+ var playersBox = getGUIObjectByName("playersBox");
+ [playerList, presenceList, nickList] = [[],[],[]];
+ for each (var p in Engine.GetPlayerList())
+ {
+ var [name, status] = formatPlayerListEntry(p.name, p.presence);
+ playerList.push(name);
+ presenceList.push(status);
+ nickList.push(p.name);
+ }
+ playersBox.list_name = playerList;
+ playersBox.list_status = presenceList;
+ playersBox.list = nickList;
+ if (playersBox.selected >= playersBox.list.length)
+ playersBox.selected = -1;
+ return [playerList, presenceList, nickList];
+}
+
+// Update leaderboard listing
+function updateBoardList()
+{
+ // Get list from C++
+ var boardList = Engine.GetBoardList();
+ // Get GUI leaderboard object
+ var leaderboard = getGUIObjectByName("leaderboardBox");
+ // Sort list in acending order by rating
+ boardList.sort(function(a, b) b.rating - a.rating);
+
+ var list = [];
+ var list_name = [];
+ var list_rank = [];
+ var list_rating = [];
+
+ // Push changes
+ for (var i = 0; i < boardList.length; i++)
+ {
+ list_name.push(boardList[i].name);
+ list_rating.push(boardList[i].rating);
+ list_rank.push(i+1);
+ list.push(boardList[i].name);
+ }
+
+ leaderboard.list_name = list_name;
+ leaderboard.list_rating = list_rating;
+ leaderboard.list_rank = list_rank;
+ leaderboard.list = list;
+
+ if (leaderboard.selected >= leaderboard.list.length)
+ leaderboard.selected = -1;
+}
+
+// Update game listing
+function updateGameList()
+{
+ var gamesBox = getGUIObjectByName("gamesBox");
+ var gameList = Engine.GetGameList();
+ // Store the game whole game list data so that we can access it later
+ // to update the game info panel.
+ g_GameList = gameList;
+
+ // Sort the list of games to that games 'waiting' are displayed at the top
+ g_GameList.sort(function (a,b) {
+ return a.state == 'waiting' ? -1 : b.state == 'waiting' ? +1 : 0;
+ });
+
+ var list_name = [];
+ var list_ip = [];
+ var list_mapName = [];
+ var list_mapSize = [];
+ var list_mapType = [];
+ var list_nPlayers = [];
+ var list = [];
+ var list_data = [];
+
+ var mapSizeFilterDD = getGUIObjectByName("mapSizeFilter");
+ var playersNumberFilterDD = getGUIObjectByName("playersNumberFilter");
+ var mapTypeFilterDD = getGUIObjectByName("mapTypeFilter");
+ var showFullFilterCB = getGUIObjectByName("showFullFilter");
+
+ // Get filter values
+ var mapSizeFilter = mapSizeFilterDD.selected >= 0 ? mapSizeFilterDD.list_data[mapSizeFilterDD.selected] : "";
+ var playersNumberFilter = playersNumberFilterDD.selected >=0 ? playersNumberFilterDD.list_data[playersNumberFilterDD.selected] : "";
+ var mapTypeFilter = mapTypeFilterDD.selected >= 0 ? mapTypeFilterDD.list_data[mapTypeFilterDD.selected] : "";
+ var showFullFilter = showFullFilterCB.checked;
+
+ var c = 0;
+ for each (g in gameList)
+ {
+ if(displayGame(g, mapSizeFilter, playersNumberFilter, mapTypeFilter, showFullFilter))
+ {
+ // Highlight games 'waiting' for this player, otherwise display as green
+ var name = (g.state != 'waiting') ? '[color="0 125 0"]' + g.name + '[/color]' : '[color="orange"]' + g.name + '[/color]';
+ list_name.push(name);
+ list_ip.push(g.ip);
+ list_mapName.push(g.niceMapName);
+ list_mapSize.push(g.mapSize.split("(")[0]);
+ list_mapType.push(toTitleCase(g.mapType));
+ list_nPlayers.push(g.nbp + "/" +g.tnbp);
+ list.push(g.name);
+ list_data.push(c);
+ }
+ c++;
+ }
+
+ gamesBox.list_name = list_name;
+ // gamesBox.list_ip = list_ip;
+ gamesBox.list_mapName = list_mapName;
+ gamesBox.list_mapSize = list_mapSize;
+ gamesBox.list_mapType = list_mapType;
+ gamesBox.list_nPlayers = list_nPlayers;
+ gamesBox.list = list;
+ gamesBox.list_data = list_data;
+
+ if (gamesBox.selected >= gamesBox.list_name.length)
+ gamesBox.selected = -1;
+
+ // If game selected, update info box about the game.
+ if(getGUIObjectByName("gamesBox").selected != -1)
+ selectGame(getGUIObjectByName("gamesBox").selected)
+}
+
+// The following function colorizes and formats the entries in the player list.
+function formatPlayerListEntry(nickname, presence)
+{
+ // Set colors based on player status
+ var color_close = '[/color]';
+ switch (presence)
+ {
+ case "playing":
+ var color = '[color="125 0 0"]';
+ var status = color + "Busy" + color_close;
+ break;
+ case "away":
+ var color = '[color="0 0 125"]';
+ var status = color + "Away" + color_close;
+ break;
+ case "available":
+ var color = '[color="0 125 0"]';
+ var status = color + "Online" + color_close;
+ break;
+ case "offline":
+ var color = '[color="0 0 0"]';
+ var status = color + "Offline" + color_close;
+ break;
+ default:
+ warn("Unknown presence '"+presence+"'");
+ break;
+ }
+
+ var name = colorPlayerName(nickname);
+
+ // Push this player's name and status onto the list
+ return [name, status];
+}
+
+function selectGame(selected)
+{
+ if (selected == -1)
+ {
+ // Hide the game info panel if a game is not selected
+ getGUIObjectByName("gameInfo").hidden = true;
+ getGUIObjectByName("gameInfoEmpty").hidden = false;
+ getGUIObjectByName("joinGameButton").hidden = true;
+ return;
+ }
+
+ var mapData;
+ var g = getGUIObjectByName("gamesBox").list_data[selected];
+
+ // Load map data
+ if (g_GameList[g].mapType == "random" && fileExists(g_GameList[g].mapName + ".json"))
+ mapData = parseJSONData(g_GameList[g].mapName + ".json");
+ else if (fileExists(g_GameList[g].mapName + ".xml"))
+ mapData = Engine.LoadMapSettings(g_GameList[g].mapName + ".xml");
+ else
+ // Warn the player if we can't find the map.
+ warn("Map '"+ g_GameList[g].mapName +"' not found");
+
+ // Show the game info paneland join button.
+ getGUIObjectByName("gameInfo").hidden = false;
+ getGUIObjectByName("gameInfoEmpty").hidden = true;
+ getGUIObjectByName("joinGameButton").hidden = false;
+
+ // Display the map name, number of players, the names of the players, the map size and the map type.
+ getGUIObjectByName("sgMapName").caption = g_GameList[g].niceMapName;
+ getGUIObjectByName("sgNbPlayers").caption = g_GameList[g].nbp + "/" + g_GameList[g].tnbp;
+ getGUIObjectByName("sgPlayersNames").caption = g_GameList[g].players;
+ getGUIObjectByName("sgMapSize").caption = g_GameList[g].mapSize.split("(")[0];
+ getGUIObjectByName("sgMapType").caption = toTitleCase(g_GameList[g].mapType);
+
+ // Display map description if it exists, otherwise display a placeholder.
+ if (mapData && mapData.settings.Description)
+ var mapDescription = mapData.settings.Description;
+ else
+ var mapDescription = "Sorry, no description available.";
+
+ // Display map preview if it exists, otherwise display a placeholder.
+ if (mapData && mapData.settings.Preview)
+ var mapPreview = mapData.settings.Preview;
+ else
+ var mapPreview = "nopreview.png";
+
+ getGUIObjectByName("sgMapDescription").caption = mapDescription;
+ getGUIObjectByName("sgMapPreview").sprite = "cropped:(0.7812,0.5859)session/icons/mappreview/" + mapPreview;
+}
+
+function joinSelectedGame()
+{
+ var gamesBox = getGUIObjectByName("gamesBox");
+ if (gamesBox.selected >= 0)
+ {
+ var g = gamesBox.list_data[gamesBox.selected];
+ var sname = g_Name;
+ var sip = g_GameList[g].ip;
+
+ // TODO: What about valid host names?
+ // Check if it looks like an ip address
+ if (sip.split('.').length != 4)
+ {
+ addChatMessage({ "from": "system", "text": "This game does not have a valid address" });
+ return;
+ }
+
+ // Open Multiplayer connection window with join option.
+ Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "join", name: sname, ip: sip });
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+// Utils
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+function twoDigits(n)
+{
+ return n < 10 ? "0" + n : n;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+// GUI event handlers
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+function onTick()
+{
+ // Wake up XmppClient
+ Engine.RecvXmppClient();
+
+ updateTimers();
+
+ // Receive messages
+ while (true)
+ {
+ var message = Engine.LobbyGuiPollMessage();
+ // Clean Message
+ if (!message)
+ break;
+ message.from = escapeText(message.from);
+ message.text = escapeText(message.text);
+ switch (message.type)
+ {
+ case "mucmessage": // For room messages
+ addChatMessage({ "from": message.from, "text": message.text });
+ break;
+ case "message": // For private messages
+ addChatMessage({ "from": message.from, "text": message.text });
+ break;
+ case "muc":
+ var nick = message.text;
+ var presence = Engine.LobbyGetPlayerPresence(nick);
+ var playersBox = getGUIObjectByName("playersBox");
+ var playerList = playersBox.list_name;
+ var presenceList = playersBox.list_status;
+ var nickList = playersBox.list;
+ var nickIndex = nickList.indexOf(nick);
+ switch(message.level)
+ {
+ case "join":
+ if (nick == g_Name)
+ {
+ // We just joined, we need to get the full player list
+ [playerList, presenceList, nickList] = updatePlayerList();
+ break;
+ }
+ var [name, status] = formatPlayerListEntry(nick, presence);
+ playerList.push(name);
+ presenceList.push(status);
+ nickList.push(nick);
+ addChatMessage({ "text": "/special " + nick + " has joined.", "key": g_specialKey });
+ break;
+ case "leave":
+ if (nickIndex == -1) // Left, but not present (TODO: warn about this?)
+ break;
+ playerList.splice(nickIndex, 1);
+ presenceList.splice(nickIndex, 1);
+ nickList.splice(nickIndex, 1);
+ addChatMessage({ "text": "/special " + nick + " has left.", "key": g_specialKey });
+ break;
+ case "nick":
+ if (nickIndex == -1) // This shouldn't ever happen
+ break;
+ var [name, status] = formatPlayerListEntry(message.data, presence); // TODO: actually we don't want to change the presence here, so use what was used before
+ playerList[nickIndex] = name;
+ // presence stays the same
+ nickList[nickIndex] = message.data;
+ addChatMessage({ "text": "/special " + nick + " is now known as " + message.data + ".", "key": g_specialKey });
+ break;
+ case "presence":
+ if (nickIndex == -1) // This shouldn't ever happen
+ break;
+ var [name, status] = formatPlayerListEntry(nick, presence);
+ presenceList[nickIndex] = status;
+ playerList[nickIndex] = name;
+ break;
+ default:
+ warn("Unknown message.level '" + message.level + "'");
+ break;
+ }
+ // Push new data to GUI
+ playersBox.list_name = playerList;
+ playersBox.list_status = presenceList;
+ playersBox.list = nickList;
+ if (playersBox.selected >= playersBox.list.length)
+ playersBox.selected = -1;
+ break;
+ case "system":
+ switch (message.level)
+ {
+ case "standard":
+ addChatMessage({ "from": "system", "text": message.text, "color": "0 150 0" });
+ if (message.text == "disconnected")
+ {
+ // Clear the list of games and the list of players
+ updateGameList();
+ updateBoardList();
+ updatePlayerList();
+ // Disable the 'host' button
+ getGUIObjectByName("hostButton").enabled = false;
+ }
+ else if (message.text == "connected")
+ {
+ getGUIObjectByName("hostButton").enabled = true;
+ }
+ break;
+ case "error":
+ addChatMessage({ "from": "system", "text": message.text, "color": "150 0 0" });
+ break;
+ case "internal":
+ switch (message.text)
+ {
+ case "gamelist updated":
+ updateGameList();
+ break;
+ case "boardlist updated":
+ updateBoardList();
+ break;
+ }
+ break
+ }
+ break;
+ default:
+ error("Unrecognised message type "+message.type);
+ }
+ }
+}
+
+/* Messages */
+function submitChatInput()
+{
+ var input = getGUIObjectByName("chatInput");
+ var text = escapeText(input.caption);
+ if (text.length)
+ {
+ if (!handleSpecialCommand(text))
+ Engine.LobbySendMessage(text);
+ input.caption = "";
+ }
+}
+
+function completeNick()
+{
+ var input = getGUIObjectByName("chatInput");
+ var text = escapeText(input.caption);
+ if (text.length)
+ {
+ var matched = false;
+ for each (var playerObj in Engine.GetPlayerList()) {
+ var player = playerObj.name;
+ var breaks = text.match(/(\s+)/g) || [];
+ text.split(/\s+/g).reduceRight(function (wordsSoFar, word, index) {
+ if (matched)
+ return null;
+ var matchCandidate = word + (breaks[index - 1] || "") + wordsSoFar;
+ if (player.toUpperCase().indexOf(matchCandidate.toUpperCase().trim()) == 0) {
+ input.caption = text.replace(matchCandidate.trim(), player);
+ matched = true;
+ }
+ return matchCandidate;
+ }, "");
+ if (matched)
+ break;
+ }
+ }
+}
+
+function handleSpecialCommand(text)
+{
+ if (text[0] != '/')
+ return false;
+
+ var [cmd, nick] = ircSplit(text);
+
+ switch (cmd)
+ {
+ case "away":
+ // TODO: Should we handle away messages?
+ Engine.LobbySetPlayerPresence("away");
+ break;
+ case "back":
+ Engine.LobbySetPlayerPresence("available");
+ break;
+ case "nick":
+ if (g_spammers[g_Name] != undefined)
+ break;
+ // Strip invalid characters.
+ nick = sanitizePlayerName(nick, true, true);
+ Engine.LobbySetNick(nick);
+ g_Name = nick;
+ break;
+ case "kick": // TODO: Split reason from nick and pass it too, for now just support "/kick nick"
+ // also allow quoting nicks (and/or prevent users from changing it here, but that doesn't help if the spammer uses a different client)
+ Engine.LobbyKick(nick, "");
+ break;
+ case "ban": // TODO: Split reason from nick and pass it too, for now just support "/ban nick"
+ Engine.LobbyBan(nick, "");
+ break;
+ case "quit":
+ lobbyStop();
+ Engine.SwitchGuiPage("page_pregame.xml");
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+function addChatMessage(msg)
+{
+ // Set sender color
+ if (msg.color)
+ msg.from = '[color="' + msg.color + '"]' + msg.from + '[/color]';
+ else if (msg.from)
+ msg.from = colorPlayerName(msg.from);
+
+ // Highlight local user's nick
+ if (msg.text.indexOf(g_Name) != -1 && g_Name != msg.from)
+ msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Name + '\\b', "g"), colorPlayerName(g_Name));
+
+ // Run spam test
+ if (updateSpamandDetect(msg.text, msg.from))
+ return;
+
+ // Format Text
+ var formatted = ircFormat(msg.text, msg.from, msg.key);
+
+ // If there is text, add it to the chat box.
+ if (formatted)
+ {
+ g_ChatMessages.push(formatted);
+ getGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
+ }
+}
+
+function ircSplit(string)
+{
+ var idx = string.indexOf(' ');
+ if (idx != -1)
+ return [string.substr(1,idx-1), string.substr(idx+1)];
+ return [string.substr(1), ""];
+}
+
+// The following formats text in an IRC-like way
+function ircFormat(text, from, key)
+{
+ time = new Date(Date.now());
+ function warnUnsupportedCommand(command, from) // Function to warn only local player
+ {
+ if (from === g_Name)
+ addChatMessage({ "from": "system", "text": "We're sorry, the '" + command + "' command is not supported." });
+ return;
+ }
+
+ // Build time header if enabled
+ if (g_timestamp)
+ formatted = '[font="serif-bold-13"]\x5B' + twoDigits(time.getHours() % 12) + ":" + twoDigits(time.getMinutes()) + '\x5D[/font] '
+ else
+ formatted = "";
+
+ // Handle commands
+ if (text[0] == '/')
+ {
+ var [command, message] = ircSplit(text);
+ switch (command)
+ {
+ case "me":
+ return formatted + '[font="serif-bold-13"]* ' + from + '[/font] ' + message;
+ case "say":
+ return formatted + '[font="serif-bold-13"]<' + from + '>[/font] ' + message;
+ case "special":
+ if (key === g_specialKey)
+ return formatted + '[font="serif-bold-13"] == ' + message + '[/font]';
+ break;
+ default:
+ return warnUnsupportedCommand(command, from)
+ }
+ }
+ return formatted + '[font="serif-bold-13"]<' + from + '>[/font] ' + text;
+}
+
+// The following function tracks message stats and returns true if the input text is spam.
+function updateSpamandDetect(text, from)
+{
+ // Check for blank lines.
+ if (text == " ")
+ return true;
+ // Update the spam monitor.
+ if (g_spamMonitor[from])
+ g_spamMonitor[from]++;
+ else
+ g_spamMonitor[from] = 1;
+ if (g_spamMonitor[from] > 5)
+ g_spammers[from] = true
+ // Block spammers and notify the player if they are blocked.
+ if(from in g_spammers)
+ {
+ if (from == g_Name)
+ {
+ addChatMessage({ "from": "system", "text": "Please do not spam. You have been blocked for thirty seconds." });
+ }
+ return true;
+ }
+ // Return false if everything is clear.
+ return false;
+}
+
+// Timers to clear spammers after some time.
+function clearSpamMonitor()
+{
+ g_spamMonitor = {};
+ spamTimer = setTimeout(clearSpamMonitor, 5000);
+}
+
+function clearSpammers()
+{
+ g_spammers = {};
+ spammerTimer = setTimeout(clearSpammers, 30000);
+}
+
+/* Utilities */
+// Generate a (mostly) unique color for this player based on their name.
+// See http://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-jquery-javascript
+function getPlayerColor(playername)
+{
+ // Generate a probably-unique hash for the player name and use that to create a color.
+ var hash = 0;
+ for (var i = 0; i < playername.length; i++)
+ hash = playername.charCodeAt(i) + ((hash << 5) - hash);
+
+ // First create the color in RGB then HSL, clamp the lightness so it's not too dark to read, and then convert back to RGB to display.
+ // The reason for this roundabout method is this algorithm can generate values from 0 to 255 for RGB but only 0 to 100 for HSL; this gives
+ // us much more variety if we generate in RGB. Unfortunately, enforcing that RGB values are a certain lightness is very difficult, so
+ // we convert to HSL to do the computation. Since our GUI code only displays RGB colors, we have to convert back.
+ var [h, s, l] = rgbToHsl(hash >> 24 & 0xFF, hash >> 16 & 0xFF, hash >> 8 & 0xFF);
+ return hslToRgb(h, s, Math.max(0.4, l)).join(" ");
+}
+
+function repeatString(times, string) {
+ return Array(times + 1).join(string);
+}
+
+// Some names are special and should always appear in certain colors.
+var fixedColors = { "system": repeatString(7, "255.0.0."), "WFGbot": repeatString(6, "255.24.24."),
+ "pyrogenesis": repeatString(2, "97.0.0.") + repeatString(2, "124.0.0.") + "138.0.0." +
+ repeatString(2, "174.0.0.") + repeatString(2, "229.40.0.") + repeatString(2, "243.125.15.") };
+function colorPlayerName(playername)
+{
+ var color = fixedColors[playername];
+ if (color) {
+ color = color.split(".");
+ return ('[color="' + playername.split("").map(function (c, i) color.slice(i * 3, i * 3 + 3).join(" ") + '"]' + c + '[/color][color="')
+ .join("") + '"]').slice(0, -10);
+ }
+ return '[color="' + getPlayerColor(playername) + '"]' + playername + '[/color]';
+}
+
+// Ensure `value` is between 0 and 1.
+function clampColorValue(value)
+{
+ return Math.abs(1 - Math.abs(value - 1));
+}
+
+// See http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
+function rgbToHsl(r, g, b)
+{
+ r /= 255;
+ g /= 255;
+ b /= 255;
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2;
+
+ if (max == min)
+ h = s = 0; // achromatic
+ else
+ {
+ var d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch (max)
+ {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+
+ return [h, s, l];
+}
+
+function hslToRgb(h, s, l)
+{
+ [h, s, l] = [h, s, l].map(clampColorValue);
+ var r, g, b;
+
+ if (s == 0)
+ r = g = b = l; // achromatic
+ else {
+ function hue2rgb(p, q, t)
+ {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1/6) return p + (q - p) * 6 * t;
+ if (t < 1/2) return q;
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ }
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1/3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1/3);
+ }
+
+ return [r, g, b].map(function (n) Math.round(n * 255));
+}
+
+(function () {
+function hexToRgb(hex) {
+ return parseInt(hex.slice(0, 2), 16) + "." + parseInt(hex.slice(2, 4), 16) + "." + parseInt(hex.slice(4, 6), 16) + ".";
+}
+function r(times, hex) {
+ return repeatString(times, hexToRgb(hex));
+}
+
+fixedColors["Twilight Sparkle"] = r(2, "d19fe3") + r(2, "b689c8") + r(2, "a76bc2") +
+ r(4, "263773") + r(2, "131f46") + r(2, "662d8a") + r(2, "ed438a");
+fixedColors["Applejack"] = r(3, "ffc261") + r(3, "efb05d") + r(3, "f26f31");
+fixedColors["Rarity"] = r(1, "ebeff1") + r(1, "dee3e4") + r(1, "bec2c3") +
+ r(1, "83509f") + r(1, "4b2568") + r(1, "4917d6");
+fixedColors["Rainbow Dash"] = r(2, "ee4144") + r(1, "f37033") + r(1, "fdf6af") +
+ r(1, "62bc4d") + r(1, "1e98d3") + r(2, "672f89") + r(1, "9edbf9") +
+ r(1, "88c4eb") + r(1, "77b0e0") + r(1, "1e98d3");
+fixedColors["Pinkie Pie"] = r(2, "f3b6cf") + r(2, "ec9dc4") + r(4, "eb81b4") +
+ r(1, "ed458b") + r(1, "be1d77");
+fixedColors["Fluttershy"] = r(2, "fdf6af") + r(2, "fee78f") + r(2, "ead463") +
+ r(2, "f3b6cf") + r(2, "eb81b4");
+fixedColors["Sweetie Belle"] = r(2, "efedee") + r(3, "e2dee3") + r(3, "cfc8d1") +
+ r(2, "b28dc0") + r(2, "f6b8d2") + r(1, "795b8a");
+fixedColors["Apple Bloom"] = r(2, "f4f49b") + r(2, "e7e793") + r(2, "dac582") +
+ r(2, "f46091") + r(2, "f8415f") + r(1, "c52451");
+fixedColors["Scootaloo"] = r(2, "fbba64") + r(2, "f2ab56") + r(2, "f37003") +
+ r(2, "bf5d95") + r(1, "bf1f79");
+fixedColors["Luna"] = r(1, "7ca7fa") + r(1, "5d6fc1") + r(1, "656cb9") + r(1, "393993");
+fixedColors["Celestia"] = r(1, "fdfafc") + r(1, "f7eaf2") + r(1, "d99ec5") +
+ r(1, "00aec5") + r(1, "f7c6dc") + r(1, "98d9ef") + r(1, "ced7ed") + r(1, "fed17b");
+})();
Index: ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.js (revision 14098)
@@ -0,0 +1,148 @@
+var g_LobbyIsConnecting = false;
+var g_EncrytedPassword = "";
+
+function init()
+{
+ g_EncrytedPassword = Engine.ConfigDB_GetValue("user", "lobby.password");
+}
+
+function lobbyStop()
+{
+ getGUIObjectByName("connectFeedback").caption = "";
+ getGUIObjectByName("registerFeedback").caption = "";
+
+ if (g_LobbyIsConnecting == false)
+ return;
+
+ g_LobbyIsConnecting = false;
+ Engine.StopXmppClient();
+}
+
+function lobbyStart()
+{
+ if (g_LobbyIsConnecting != false)
+ return;
+
+ var username = getGUIObjectByName("connectUsername").caption;
+ var password = getGUIObjectByName("connectPassword").caption;
+ var feedback = getGUIObjectByName("connectFeedback");
+ // Use username as nick unless overridden.
+ if (getGUIObjectByName("nickPanel").hidden == true)
+ var nick = sanitizePlayerName(username, true, true);
+ else
+ var nick = sanitizePlayerName(getGUIObjectByName("joinPlayerName").caption, true, true);
+ if (!username || !password)
+ {
+ feedback.caption = "Username or password empty";
+ return;
+ }
+
+ feedback.caption = "Connecting..";
+ // If they enter a different password, re-encrypt.
+ if (password != g_EncrytedPassword)
+ g_EncrytedPassword = Engine.EncryptPassword(password, username);
+ Engine.StartXmppClient(username, g_EncrytedPassword, "arena", nick);
+ g_LobbyIsConnecting = true;
+ Engine.ConnectXmppClient();
+}
+
+function lobbyStartRegister()
+{
+ if (g_LobbyIsConnecting != false)
+ return;
+ var account = getGUIObjectByName("connectUsername").caption;
+ var password = getGUIObjectByName("connectPassword").caption;
+ var passwordAgain = getGUIObjectByName("registerPasswordAgain").caption;
+ var feedback = getGUIObjectByName("registerFeedback");
+
+ if (!account || !password || !passwordAgain)
+ {
+ feedback.caption = "Login or password empty";
+ return;
+ }
+ if (password != passwordAgain)
+ {
+ feedback.caption = "Password mismatch";
+ getGUIObjectByName("connectPassword").caption = "";
+ getGUIObjectByName("registerPasswordAgain").caption = "";
+ return;
+ }
+ // Check they are using a valid account name.
+ sanitizedName = sanitizePlayerName(account, true, true)
+ if (sanitizedName != account)
+ {
+ feedback.caption = "Sorry, you can't use [, ], unicode, whitespace, or commas.";
+ return;
+ }
+
+ feedback.caption = "Registering...";
+ if (password != g_EncrytedPassword)
+ g_EncrytedPassword = Engine.EncryptPassword(password, username);
+ Engine.StartRegisterXmppClient(account, g_EncrytedPassword);
+ g_LobbyIsConnecting = true;
+ Engine.ConnectXmppClient();
+}
+
+function onTick()
+{
+ if (!g_LobbyIsConnecting)
+ // The Xmpp Client has not been created
+ return;
+
+ // The XmppClient has been created, we are waiting
+ // to be connected or to receive an error.
+
+ //Wake up XmppClient
+ Engine.RecvXmppClient();
+
+ //Receive messages
+ while (true)
+ {
+ var message = Engine.LobbyGuiPollMessage();
+ if (!message)
+ break;
+
+ if (message.type == "muc" && message.level == "join")
+ {
+ // We are connected, switch to the lobby page
+ Engine.PopGuiPage();
+ var username = getGUIObjectByName("connectUsername").caption;
+ var password = getGUIObjectByName("connectPassword").caption;
+ // Use username as nick unless overridden.
+ if (getGUIObjectByName("nickPanel").hidden == true)
+ var nick = sanitizePlayerName(username, true, true);
+ else
+ var nick = sanitizePlayerName(getGUIObjectByName("joinPlayerName").caption, true, true);
+
+ // Switch to lobby
+ Engine.SwitchGuiPage("page_lobby.xml");
+ // Store nick, login, and password
+ Engine.ConfigDB_CreateValue("user", "playername", nick);
+ Engine.ConfigDB_CreateValue("user", "lobby.login", username);
+ // We only store the encrypted password, so make sure to re-encrypt it if changed before saving.
+ if (password != g_EncrytedPassword)
+ g_EncrytedPassword = Engine.EncryptPassword(password, username);
+ Engine.ConfigDB_CreateValue("user", "lobby.password", g_EncrytedPassword);
+ Engine.ConfigDB_WriteFile("user", "config/user.cfg");
+
+ return;
+ }
+ else if (message.type == "system" && message.text == "registered")
+ {
+ // Great, we are registered. Switch to the connection window.
+ getGUIObjectByName("registerFeedback").caption = toTitleCase(message.text);
+ getGUIObjectByName("connectFeedback").caption = toTitleCase(message.text);
+ Engine.StopXmppClient();
+ g_LobbyIsConnecting = false;
+ getGUIObjectByName("pageRegister").hidden = true;
+ getGUIObjectByName("pageConnect").hidden = false;
+ }
+ else if(message.type == "system" && (message.level == "error" || message.text == "disconnected"))
+ {
+ getGUIObjectByName("connectFeedback").caption = toTitleCase(message.text);
+ getGUIObjectByName("registerFeedback").caption = toTitleCase(message.text);
+ Engine.StopXmppClient();
+ g_LobbyIsConnecting = false;
+ }
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/lobby/styles.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/styles.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/styles.xml (revision 14098)
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/page_lobby.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/page_lobby.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/page_lobby.xml (revision 14098)
@@ -0,0 +1,17 @@
+
+
+ common/modern/styles.xml
+ common/modern/sprites.xml
+ common/modern/setup.xml
+
+ common/setup.xml
+ common/styles.xml
+ common/sprite1.xml
+ common/global.xml
+
+ common/common_sprites.xml
+ common/common_styles.xml
+
+ lobby/styles.xml
+ lobby/lobby.xml
+
Index: ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.xml (revision 14098)
@@ -1,550 +1,572 @@
onTick();
tooltipText
[font="serif-bold-16"]Help improve 0 A.D.![/font]
You can automatically send us anonymous feedback that will help us fix bugs, and improve performance and compatibility.
Enable feedback
EnableUserReport(true);
Technical details
Engine.PushGuiPage("page_manual.xml", { "page": "userreport" });[font="serif-bold-16"]Thank you for helping improve 0 A.D.![/font]
Anonymous feedback is currently enabled.
Status: $status.
Disable feedback
EnableUserReport(false);
Technical details
Engine.PushGuiPage("page_manual.xml", { "page": "userreport" });
closeMenu();
Matches
Engine.SwitchGuiPage("page_gamesetup.xml", { type: "offline" });
Campaigns
closeMenu();
Load Game
closeMenu();
Engine.PushGuiPage("page_loadgame.xml", { type: "offline" });
Join Game
closeMenu();
// Open Multiplayer connection window with join option.
- Engine.PushGuiPage("page_gamesetup_mp.xml", "join");
+ Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "join" });
Host Game
closeMenu();
// Open Multiplayer connection window with host option.
- Engine.PushGuiPage("page_gamesetup_mp.xml", "host");
+ Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "host" });
-
+
+
+ Game Lobby
+
+ closeMenu();
+ // Open Multiplayer game lobby.
+ Engine.PushGuiPage("page_prelobby.xml", []);
+
+
+ if (!Engine.StartXmppClient)
+ {
+ this.enabled = false;
+ this.tooltip = "Launch the multiplayer lobby. [DISABLED BY BUILD]";
+ }
+
+
+
Options
closeMenu();
Scenario Editor
closeMenu();
Learn To Play
closeMenu();
Single Player
closeMenu();
openMenu("submenuSinglePlayer", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 3);
Multiplayer
closeMenu();
- openMenu("submenuMultiplayer", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 2);
+ openMenu("submenuMultiplayer", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 3);
Tools Options
closeMenu();
openMenu("submenuToolsAndOptions", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 2);
History
closeMenu();
Exit
closeMenu();
[font="serif-bold-16"]Alpha XIV: Naukratis[/font]
WARNING: This is an early development version of the game. Many features have not been added yet.
Get involved at: play0ad.com
Fundraiser
Website
Chat
Report a Bug
WILDFIRE GAMES
Index: ps/trunk/binaries/data/mods/public/gui/session/session.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/session.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/session/session.xml (revision 14098)
@@ -1,1259 +1,1260 @@
onTick();
onSimulationUpdate();
this.hidden = !this.hidden;
closeOpenDialogs();toggleChatWindow();toggleChatWindow(true);openMenu();
var newSetting = !Engine.Renderer_GetSilhouettesEnabled();
Engine.Renderer_SetSilhouettesEnabled(newSetting);
getGUIObjectByName("silhouettesCheckbox").checked = newSetting;
var newSetting = !Engine.Renderer_GetShowSkyEnabled();
Engine.Renderer_SetShowSkyEnabled(newSetting);
togglePause();Engine.QuickSave();Engine.QuickLoad();performCommand(g_Selection.toList()[0], "delete");setCameraFollow(g_Selection.toList()[0]);jumpCamera(1);jumpCamera(2);jumpCamera(3);jumpCamera(4);jumpCamera(5);jumpCamera(6);jumpCamera(7);jumpCamera(8);jumpCamera(9);jumpCamera(10);setJumpCamera(1);setJumpCamera(2);setJumpCamera(3);setJumpCamera(4);setJumpCamera(5);setJumpCamera(6);setJumpCamera(7);setJumpCamera(8);setJumpCamera(9);setJumpCamera(10);stopUnits(g_Selection.toList());addTrainingByPosition(0);addTrainingByPosition(1);addTrainingByPosition(2);addTrainingByPosition(3);addTrainingByPosition(4);addTrainingByPosition(5);addTrainingByPosition(6);findIdleUnit(["Hero", "Champion", "CitizenSoldier", "Siege", "Warship"]);clearSelection()
-
- toggleDeveloperOverlay();
-
+
+ if (!Engine.IsRankedGame()) toggleDeveloperOverlay();
+ Control all units
g_DevSettings.controlAll = this.checked;
Engine.PostNetworkCommand( {"type": "control-all", "flag": this.checked} );
Change perspectivegetGUIObjectByName("viewPlayer").hidden = !this.checked;Display selection statePathfinder overlayEngine.GuiInterfaceCall("SetPathfinderDebugOverlay", this.checked);Obstruction overlayEngine.GuiInterfaceCall("SetObstructionDebugOverlay", this.checked);Unit motion overlayg_Selection.SetMotionDebugOverlay(this.checked);Range overlayEngine.GuiInterfaceCall("SetRangeDebugOverlay", this.checked);Bounding box overlayEngine.SetBoundingBoxDebugOverlay(this.checked);Restrict cameraEngine.GameView_SetConstrainCameraEnabled(this.checked);Reveal mapEngine.PostNetworkCommand({"type": "reveal-map", "enable": this.checked});Enable time warp
if (this.checked)
messageBox(500, 250, "Note: time warp mode is a developer option, and not intended\nfor use over long periods of time. Using it incorrectly may\ncause the game to run out of memory or crash.", "Time warp mode", 2);
Engine.EnableTimeWarpRecording(this.checked ? 10 : 0);Promote selected unitsEngine.PostNetworkCommand({"type": "promote", "entities": g_Selection.toList()});this.hidden = !this.hidden;Game PausedClick to Resume GametogglePause();submitChatInput();
Send
submitChatInput();
Cancel
closeChat();
Team Only
Diplomacy
Close
closeDiplomacy();SettingsEnable Shadowsthis.checked = Engine.Renderer_GetShadowsEnabled();Engine.Renderer_SetShadowsEnabled(this.checked);Enable Shadow Filteringthis.checked = Engine.Renderer_GetShadowPCFEnabled();Engine.Renderer_SetShadowPCFEnabled(this.checked);Water - HQ Wavinessthis.checked = Engine.Renderer_GetWaterNormalEnabled();Engine.Renderer_SetWaterNormalEnabled(this.checked);Water - Use Actual Depththis.checked = Engine.Renderer_GetWaterRealDepthEnabled();Engine.Renderer_SetWaterRealDepthEnabled(this.checked);Water - Enable Reflectionsthis.checked = Engine.Renderer_GetWaterReflectionEnabled();Engine.Renderer_SetWaterReflectionEnabled(this.checked);Water - Enable Refractionthis.checked = Engine.Renderer_GetWaterRefractionEnabled();Engine.Renderer_SetWaterRefractionEnabled(this.checked);Water - Enable Shore Foamthis.checked = Engine.Renderer_GetWaterFoamEnabled();Engine.Renderer_SetWaterFoamEnabled(this.checked);Water - Enable Shore Wavesthis.checked = Engine.Renderer_GetWaterCoastalWavesEnabled();Engine.Renderer_SetWaterCoastalWavesEnabled(this.checked);Water - Use Surface Shadowsif (Engine.Renderer_GetWaterShadowEnabled()) this.checked = true; else this.checked = false;Engine.Renderer_SetWaterShadowEnabled(this.checked);Enable Particlesthis.checked = Engine.Renderer_GetParticlesEnabled();Engine.Renderer_SetParticlesEnabled(this.checked);Enable Unit Silhouettesthis.checked = Engine.Renderer_GetSilhouettesEnabled();Engine.Renderer_SetSilhouettesEnabled(this.checked);Enable Musicif (this.checked) global.music.start(); else global.music.stop();Developer Overlay
+ if (Engine.IsRankedGame()) this.enabled = false;toggleDeveloperOverlay();
Close
closeSettings(true);selectViewPlayer(this.selected);
ALPHA XIV : Naukratis
this.caption = buildTime(0) + " (" + buildTime(2) + ")"
toggleGameSpeed();
toggleDiplomacy();
MENU
toggleMenu();
Settings
settingsMenuButton();
Save
openSave();
Chat
chatMenuButton();
Resign
resignMenuButton();
Exit
exitMenuButton();PausetogglePause();
Manual
openManual();handleMinimapEvent(arguments[0]);findIdleUnit(["Female", "Trade", "FishingBoat", "CitizenSoldier", "Healer"]);Exchange resources:Health:Stamina:
- Return to Main Menu
+ ExitleaveGame()
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/background.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/background.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/background.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/background.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/background.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/border.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/border.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/border.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/border.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/border.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-bottom.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-bottom.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-bottom.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-bottom.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-bottom.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-top.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-top.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-top.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-top.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dialog-deco-top.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dropdown-arrow.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dropdown-arrow.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dropdown-arrow.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dropdown-arrow.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/dropdown-arrow.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/gold-separator.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/gold-separator.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/gold-separator.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/gold-separator.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/gold-separator.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-left.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-left.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-left.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-left.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-left.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-right.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-right.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-right.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-right.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/item-shading-right.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollback.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollback.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollback.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollback.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollback.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollbar.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollbar.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollbar.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollbar.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/scrollbar.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/source/gui/CInput.cpp
===================================================================
--- ps/trunk/source/gui/CInput.cpp (revision 14097)
+++ ps/trunk/source/gui/CInput.cpp (revision 14098)
@@ -1,2008 +1,2045 @@
/* 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 .
*/
/*
CInput
*/
#include "precompiled.h"
#include "GUI.h"
#include "CInput.h"
#include "CGUIScrollBarVertical.h"
#include "graphics/FontMetrics.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextRenderer.h"
#include "lib/ogl.h"
#include "lib/sysdep/clipboard.h"
#include "lib/timer.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "renderer/Renderer.h"
#include
extern int g_yres;
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
CInput::CInput()
: m_iBufferPos(-1), m_iBufferPos_Tail(-1), m_SelectingText(false), m_HorizontalScroll(0.f),
m_PrevTime(0.0), m_CursorVisState(true), m_CursorBlinkRate(0.5)
{
AddSetting(GUIST_float, "buffer_zone");
AddSetting(GUIST_CStrW, "caption");
AddSetting(GUIST_int, "cell_id");
AddSetting(GUIST_CStrW, "font");
+ AddSetting(GUIST_CStrW, "mask_char");
+ AddSetting(GUIST_bool, "mask");
AddSetting(GUIST_int, "max_length");
AddSetting(GUIST_bool, "multiline");
AddSetting(GUIST_bool, "scrollbar");
AddSetting(GUIST_CStr, "scrollbar_style");
AddSetting(GUIST_CGUISpriteInstance, "sprite");
AddSetting(GUIST_CGUISpriteInstance, "sprite_selectarea");
AddSetting(GUIST_CColor, "textcolor");
AddSetting(GUIST_CColor, "textcolor_selected");
AddSetting(GUIST_CStrW, "tooltip");
AddSetting(GUIST_CStr, "tooltip_style");
CFG_GET_VAL("gui.cursorblinkrate", Double, m_CursorBlinkRate);
// Add scroll-bar
CGUIScrollBarVertical * bar = new CGUIScrollBarVertical();
bar->SetRightAligned(true);
- bar->SetUseEdgeButtons(true);
AddScrollBar(bar);
}
CInput::~CInput()
{
}
InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
{
ENSURE(m_iBufferPos != -1);
if (ev->ev.type == SDL_HOTKEYDOWN)
{
return(ManuallyHandleHotkeyEvent(ev));
}
else if (ev->ev.type == SDL_KEYDOWN)
{
// Since the GUI framework doesn't handle to set settings
// in Unicode (CStrW), we'll simply retrieve the actual
// pointer and edit that.
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
int szChar = ev->ev.key.keysym.sym;
wchar_t cooked = (wchar_t)ev->ev.key.keysym.unicode;
switch (szChar)
{
case SDLK_TAB: // '\t'
/* Auto Complete */
// TODO Gee: (2004-09-07) What to do with tab?
break;
case SDLK_BACKSPACE: // '\b'
m_WantedX=0.0f;
if (SelectingText())
DeleteCurSelection();
else
{
m_iBufferPos_Tail = -1;
if (pCaption->empty() || m_iBufferPos == 0)
{
break;
}
else
{
if (m_iBufferPos == (int)pCaption->length())
*pCaption = pCaption->Left( (long) pCaption->length()-1);
else
*pCaption = pCaption->Left( m_iBufferPos-1 ) +
pCaption->Right( (long) pCaption->length()-m_iBufferPos );
--m_iBufferPos;
UpdateText(m_iBufferPos, m_iBufferPos+1, m_iBufferPos);
}
}
UpdateAutoScroll();
break;
case SDLK_DELETE:
m_WantedX=0.0f;
// If selection:
if (SelectingText())
{
DeleteCurSelection();
}
else
{
if (pCaption->empty() || m_iBufferPos == (int)pCaption->length())
{
break;
}
else
{
*pCaption = pCaption->Left( m_iBufferPos ) +
pCaption->Right( (long) pCaption->length()-(m_iBufferPos+1) );
UpdateText(m_iBufferPos, m_iBufferPos+1, m_iBufferPos);
}
}
UpdateAutoScroll();
break;
case SDLK_HOME:
// If there's not a selection, we should create one now
if (!shiftKeyPressed)
{
// Make sure a selection isn't created.
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
// Place tail at the current point:
m_iBufferPos_Tail = m_iBufferPos;
}
m_iBufferPos = 0;
m_WantedX=0.0f;
UpdateAutoScroll();
break;
case SDLK_END:
// If there's not a selection, we should create one now
if (!shiftKeyPressed)
{
// Make sure a selection isn't created.
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
// Place tail at the current point:
m_iBufferPos_Tail = m_iBufferPos;
}
m_iBufferPos = (long) pCaption->length();
m_WantedX=0.0f;
UpdateAutoScroll();
break;
/**
Conventions for Left/Right when text is selected:
References:
Visual Studio
Visual Studio has the 'newer' approach, used by newer versions of
things, and in newer applications. A left press will always place
the pointer on the left edge of the selection, and then of course
remove the selection. Right will do the exakt same thing.
If you have the pointer on the right edge and press right, it will
in other words just remove the selection.
Windows (eg. Notepad)
A left press always takes the pointer a step to the left and
removes the selection as if it were never there in the first place.
Right of course does the same thing but to the right.
I chose the Visual Studio convention. Used also in Word, gtk 2.0, MSN
Messenger.
**/
case SDLK_LEFT:
m_WantedX=0.f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
{
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
m_iBufferPos_Tail = m_iBufferPos;
}
if (m_iBufferPos > 0)
--m_iBufferPos;
}
else
{
if (m_iBufferPos_Tail < m_iBufferPos)
m_iBufferPos = m_iBufferPos_Tail;
m_iBufferPos_Tail = -1;
}
UpdateAutoScroll();
break;
case SDLK_RIGHT:
m_WantedX=0.0f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
{
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
m_iBufferPos_Tail = m_iBufferPos;
}
if (m_iBufferPos < (int)pCaption->length())
++m_iBufferPos;
}
else
{
if (m_iBufferPos_Tail > m_iBufferPos)
m_iBufferPos = m_iBufferPos_Tail;
m_iBufferPos_Tail = -1;
}
UpdateAutoScroll();
break;
/**
Conventions for Up/Down when text is selected:
References:
Visual Studio
Visual Studio has a very strange approach, down takes you below the
selection to the next row, and up to the one prior to the whole
selection. The weird part is that it is always aligned as the
'pointer'. I decided this is to much work for something that is
a bit arbitrary
Windows (eg. Notepad)
Just like with left/right, the selection is destroyed and it moves
just as if there never were a selection.
I chose the Notepad convention even though I use the VS convention with
left/right.
**/
case SDLK_UP:
{
if (!shiftKeyPressed)
{
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
m_iBufferPos_Tail = m_iBufferPos;
}
std::list::iterator current = m_CharacterPositions.begin();
while (current != m_CharacterPositions.end())
{
if (m_iBufferPos >= current->m_ListStart &&
m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size())
break;
++current;
}
float pos_x;
if (m_iBufferPos-current->m_ListStart == 0)
pos_x = 0.f;
else
pos_x = current->m_ListOfX[m_iBufferPos-current->m_ListStart-1];
if (m_WantedX > pos_x)
pos_x = m_WantedX;
// Now change row:
if (current != m_CharacterPositions.begin())
{
--current;
// Find X-position:
m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
}
// else we can't move up
UpdateAutoScroll();
}
break;
case SDLK_DOWN:
{
if (!shiftKeyPressed)
{
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
m_iBufferPos_Tail = m_iBufferPos;
}
std::list::iterator current = m_CharacterPositions.begin();
while (current != m_CharacterPositions.end())
{
if (m_iBufferPos >= current->m_ListStart &&
m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size())
break;
++current;
}
float pos_x;
if (m_iBufferPos-current->m_ListStart == 0)
pos_x = 0.f;
else
pos_x = current->m_ListOfX[m_iBufferPos-current->m_ListStart-1];
if (m_WantedX > pos_x)
pos_x = m_WantedX;
// Now change row:
// Add first, so we can check if it's .end()
++current;
if (current != m_CharacterPositions.end())
{
// Find X-position:
m_iBufferPos = current->m_ListStart + GetXTextPosition(current, pos_x, m_WantedX);
}
// else we can't move up
UpdateAutoScroll();
}
break;
case SDLK_PAGEUP:
GetScrollBar(0).ScrollMinusPlenty();
break;
case SDLK_PAGEDOWN:
GetScrollBar(0).ScrollPlusPlenty();
break;
/* END: Message History Lookup */
case '\r':
// 'Return' should do a Press event for single liners (e.g. submitting forms)
// otherwise a '\n' character will be added.
{
bool multiline;
GUI::GetSetting(this, "multiline", multiline);
if (!multiline)
{
SendEvent(GUIM_PRESSED, "press");
break;
}
cooked = '\n'; // Change to '\n' and do default:
// NOTE: Fall-through
}
default: //Insert a character
{
if (cooked == 0)
return IN_PASS; // Important, because we didn't use any key
// check max length
int max_length;
GUI::GetSetting(this, "max_length", max_length);
if (max_length != 0 && (int)pCaption->length() >= max_length)
break;
m_WantedX=0.0f;
if (SelectingText())
DeleteCurSelection();
m_iBufferPos_Tail = -1;
if (m_iBufferPos == (int)pCaption->length())
*pCaption += cooked;
else
*pCaption = pCaption->Left(m_iBufferPos) + cooked +
pCaption->Right((long) pCaption->length()-m_iBufferPos);
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
++m_iBufferPos;
UpdateAutoScroll();
}
break;
}
return IN_HANDLED;
}
return IN_PASS;
}
InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
{
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
bool shiftKeyPressed = g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT];
std::string hotkey = static_cast(ev->ev.user.data1);
if (hotkey == "paste")
{
m_WantedX=0.0f;
wchar_t* text = sys_clipboard_get();
if (text)
{
if (SelectingText())
{
DeleteCurSelection();
}
if (m_iBufferPos == (int)pCaption->length())
*pCaption += text;
else
*pCaption = pCaption->Left(m_iBufferPos) + text +
pCaption->Right((long) pCaption->length()-m_iBufferPos);
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
m_iBufferPos += (int)wcslen(text);
sys_clipboard_free(text);
}
return IN_HANDLED;
}
else if (hotkey == "copy" || hotkey == "cut")
{
m_WantedX=0.0f;
if (SelectingText())
{
int virtualFrom;
int virtualTo;
if (m_iBufferPos_Tail >= m_iBufferPos)
{
virtualFrom = m_iBufferPos;
virtualTo = m_iBufferPos_Tail;
}
else
{
virtualFrom = m_iBufferPos_Tail;
virtualTo = m_iBufferPos;
}
CStrW text = (pCaption->Left(virtualTo)).Right(virtualTo - virtualFrom);
sys_clipboard_set(&text[0]);
if (hotkey == "cut")
{
DeleteCurSelection();
}
}
return IN_HANDLED;
}
else if (hotkey == "text.delete.left")
{
m_WantedX=0.0f;
if (SelectingText())
{
DeleteCurSelection();
}
if (!pCaption->empty() && !m_iBufferPos == 0)
{
m_iBufferPos_Tail = m_iBufferPos;
CStrW searchString = pCaption->Left( m_iBufferPos );
// If we are starting in whitespace, adjust position until we get a non whitespace
while (m_iBufferPos > 0)
{
if (!iswspace(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
// If we end up on a puctuation char we just delete it (treat punct like a word)
if (iswpunct(searchString[m_iBufferPos - 1]))
m_iBufferPos--;
else
{
// Now we are on a non white space character, adjust position to char after next whitespace char is found
while (m_iBufferPos > 0)
{
if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
}
DeleteCurSelection();
}
return IN_HANDLED;
}
else if (hotkey == "text.delete.right")
{
m_WantedX=0.0f;
if (SelectingText())
{
DeleteCurSelection();
}
if (!pCaption->empty() && m_iBufferPos < (int)pCaption->length())
{
// Delete the word to the right of the cursor
m_iBufferPos_Tail = m_iBufferPos;
// Delete chars to the right unit we hit whitespace
while (++m_iBufferPos < (int)pCaption->length())
{
if (iswspace((*pCaption)[m_iBufferPos]) || iswpunct((*pCaption)[m_iBufferPos]))
break;
}
// Eliminate any whitespace behind the word we just deleted
while (m_iBufferPos < (int)pCaption->length())
{
if (!iswspace((*pCaption)[m_iBufferPos]))
break;
m_iBufferPos++;
}
DeleteCurSelection();
}
return IN_HANDLED;
}
else if (hotkey == "text.move.left")
{
m_WantedX=0.0f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
{
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
m_iBufferPos_Tail = m_iBufferPos;
}
if (!pCaption->empty() && !m_iBufferPos == 0)
{
CStrW searchString = pCaption->Left( m_iBufferPos );
// If we are starting in whitespace, adjust position until we get a non whitespace
while (m_iBufferPos > 0)
{
if (!iswspace(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
// If we end up on a puctuation char we just select it (treat punct like a word)
if (iswpunct(searchString[m_iBufferPos - 1]))
m_iBufferPos--;
else
{
// Now we are on a non white space character, adjust position to char after next whitespace char is found
while (m_iBufferPos > 0)
{
if (iswspace(searchString[m_iBufferPos - 1]) || iswpunct(searchString[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
}
}
}
else
{
if (m_iBufferPos_Tail < m_iBufferPos)
m_iBufferPos = m_iBufferPos_Tail;
m_iBufferPos_Tail = -1;
}
UpdateAutoScroll();
return IN_HANDLED;
}
else if (hotkey == "text.move.right")
{
m_WantedX=0.0f;
if (shiftKeyPressed || !SelectingText())
{
if (!shiftKeyPressed)
{
m_iBufferPos_Tail = -1;
}
else if (!SelectingText())
{
m_iBufferPos_Tail = m_iBufferPos;
}
if (!pCaption->empty() && m_iBufferPos < (int)pCaption->length())
{
CStrW searchString = *pCaption;
// Select chars to the right until we hit whitespace
while (++m_iBufferPos < (int)pCaption->length())
{
if (iswspace((*pCaption)[m_iBufferPos]) || iswpunct((*pCaption)[m_iBufferPos]))
break;
}
// Also select any whitespace following the word we just selected
while (m_iBufferPos < (int)pCaption->length())
{
if (!iswspace((*pCaption)[m_iBufferPos]))
break;
m_iBufferPos++;
}
}
}
else
{
if (m_iBufferPos_Tail > m_iBufferPos)
m_iBufferPos = m_iBufferPos_Tail;
m_iBufferPos_Tail = -1;
}
UpdateAutoScroll();
return IN_HANDLED;
}
else
{
return IN_PASS;
}
}
void CInput::HandleMessage(SGUIMessage &Message)
{
// TODO Gee:
IGUIScrollBarOwner::HandleMessage(Message);
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
{
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
// Update scroll-bar
// TODO Gee: (2004-09-01) Is this really updated each time it should?
if (scrollbar &&
(Message.value == CStr("size") ||
Message.value == CStr("z") ||
Message.value == CStr("absolute")))
{
GetScrollBar(0).SetX(m_CachedActualSize.right);
GetScrollBar(0).SetY(m_CachedActualSize.top);
GetScrollBar(0).SetZ(GetBufferedZ());
GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
}
// Update scrollbar
if (Message.value == CStr("scrollbar_style"))
{
CStr scrollbar_style;
GUI::GetSetting(this, Message.value, scrollbar_style);
GetScrollBar(0).SetScrollBarStyle(scrollbar_style);
}
if (Message.value == CStr("size") ||
Message.value == CStr("z") ||
Message.value == CStr("font") ||
Message.value == CStr("absolute") ||
Message.value == CStr("caption") ||
Message.value == CStr("scrollbar") ||
Message.value == CStr("scrollbar_style"))
{
UpdateText();
}
if (Message.value == CStr("multiline"))
{
bool multiline;
GUI::GetSetting(this, "multiline", multiline);
if (multiline == false)
{
GetScrollBar(0).SetLength(0.f);
}
else
{
GetScrollBar(0).SetLength( m_CachedActualSize.bottom - m_CachedActualSize.top );
}
UpdateText();
}
}break;
case GUIM_MOUSE_PRESS_LEFT:
// Check if we're selecting the scrollbar:
{
bool scrollbar, multiline;
GUI::GetSetting(this, "scrollbar", scrollbar);
GUI::GetSetting(this, "multiline", multiline);
if (GetScrollBar(0).GetStyle() && multiline)
{
if (GetMousePos().x > m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Width)
break;
}
// Okay, this section is about pressing the mouse and
// choosing where the point should be placed. For
// instance, if we press between a and b, the point
// should of course be placed accordingly. Other
// special cases are handled like the input box norms.
if (g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT])
{
m_iBufferPos = GetMouseHoveringTextPosition();
}
else
{
m_iBufferPos = m_iBufferPos_Tail = GetMouseHoveringTextPosition();
}
m_SelectingText = true;
UpdateAutoScroll();
// If we immediately release the button it will just be seen as a click
// for the user though.
}break;
case GUIM_MOUSE_DBLCLICK_LEFT:
{
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
if (pCaption->length() == 0)
break;
m_iBufferPos = m_iBufferPos_Tail = GetMouseHoveringTextPosition();
if (m_iBufferPos >= (int)pCaption->length())
m_iBufferPos = m_iBufferPos_Tail = pCaption->length() - 1;
// See if we are clicking over whitespace
if (iswspace((*pCaption)[m_iBufferPos]))
{
// see if we are in a section of whitespace greater than one character
if ((m_iBufferPos + 1 < (int) pCaption->length() && iswspace((*pCaption)[m_iBufferPos + 1])) ||
(m_iBufferPos - 1 > 0 && iswspace((*pCaption)[m_iBufferPos - 1])))
{
//
// We are clicking in an area with more than one whitespace character
// so we select both the word to the left and then the word to the right
//
// [1] First the left
// skip the whitespace
while (m_iBufferPos > 0)
{
if (!iswspace((*pCaption)[m_iBufferPos - 1]))
break;
m_iBufferPos--;
}
// now go until we hit white space or punctuation
while (m_iBufferPos > 0)
{
if (iswspace((*pCaption)[m_iBufferPos - 1]))
break;
m_iBufferPos--;
if (iswpunct((*pCaption)[m_iBufferPos]))
break;
}
// [2] Then the right
// go right until we are not in whitespace
while (++m_iBufferPos_Tail < (int)pCaption->length())
{
if (!iswspace((*pCaption)[m_iBufferPos_Tail]))
break;
}
if (m_iBufferPos_Tail == (int)pCaption->length())
break;
// now go to the right until we hit whitespace or punctuation
while (++m_iBufferPos_Tail < (int)pCaption->length())
{
if (iswspace((*pCaption)[m_iBufferPos_Tail]) || iswpunct((*pCaption)[m_iBufferPos_Tail]))
break;
}
}
else
{
// single whitespace so select word to the right
while (++m_iBufferPos_Tail < (int)pCaption->length())
{
if (!iswspace((*pCaption)[m_iBufferPos_Tail]))
break;
}
if (m_iBufferPos_Tail == (int)pCaption->length())
break;
// Don't include the leading whitespace
m_iBufferPos = m_iBufferPos_Tail;
// now go to the right until we hit whitespace or punctuation
while (++m_iBufferPos_Tail < (int)pCaption->length())
{
if (iswspace((*pCaption)[m_iBufferPos_Tail]) || iswpunct((*pCaption)[m_iBufferPos_Tail]))
break;
}
}
}
else
{
// clicked on non-whitespace so select current word
// go until we hit white space or punctuation
while (m_iBufferPos > 0)
{
if (iswspace((*pCaption)[m_iBufferPos - 1]))
break;
m_iBufferPos--;
if (iswpunct((*pCaption)[m_iBufferPos]))
break;
}
// go to the right until we hit whitespace or punctuation
while (++m_iBufferPos_Tail < (int)pCaption->length())
{
if (iswspace((*pCaption)[m_iBufferPos_Tail]) || iswpunct((*pCaption)[m_iBufferPos_Tail]))
break;
}
}
}
break;
case GUIM_MOUSE_RELEASE_LEFT:
if (m_SelectingText)
{
m_SelectingText = false;
}
break;
case GUIM_MOUSE_MOTION:
// If we just pressed down and started to move before releasing
// this is one way of selecting larger portions of text.
if (m_SelectingText)
{
// Actually, first we need to re-check that the mouse button is
// really pressed (it can be released while outside the control.
if (!g_mouse_buttons[SDL_BUTTON_LEFT])
m_SelectingText = false;
else
m_iBufferPos = GetMouseHoveringTextPosition();
UpdateAutoScroll();
}
break;
case GUIM_MOUSE_WHEEL_DOWN:
{
GetScrollBar(0).ScrollPlus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
break;
}
case GUIM_MOUSE_WHEEL_UP:
{
GetScrollBar(0).ScrollMinus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
break;
}
case GUIM_LOAD:
{
GetScrollBar(0).SetX( m_CachedActualSize.right );
GetScrollBar(0).SetY( m_CachedActualSize.top );
GetScrollBar(0).SetZ( GetBufferedZ() );
GetScrollBar(0).SetLength( m_CachedActualSize.bottom - m_CachedActualSize.top );
CStr scrollbar_style;
GUI::GetSetting(this, "scrollbar_style", scrollbar_style);
GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
UpdateText();
}
break;
case GUIM_GOT_FOCUS:
m_iBufferPos = 0;
m_PrevTime = 0.0;
m_CursorVisState = false;
break;
case GUIM_LOST_FOCUS:
m_iBufferPos = -1;
m_iBufferPos_Tail = -1;
break;
default:
break;
}
}
void CInput::UpdateCachedSize()
{
// If an ancestor's size changed, this will let us intercept the change and
// update our scrollbar positions
IGUIObject::UpdateCachedSize();
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
if (scrollbar)
{
GetScrollBar(0).SetX(m_CachedActualSize.right);
GetScrollBar(0).SetY(m_CachedActualSize.top);
GetScrollBar(0).SetZ(GetBufferedZ());
GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
}
}
void CInput::Draw()
{
float bz = GetBufferedZ();
if (m_CursorBlinkRate > 0.0)
{
// check if the cursor visibility state needs to be changed
double currTime = timer_Time();
if ((currTime - m_PrevTime) >= m_CursorBlinkRate)
{
m_CursorVisState = !m_CursorVisState;
m_PrevTime = currTime;
}
}
else
{
// should always be visible
m_CursorVisState = true;
}
// First call draw on ScrollBarOwner
bool scrollbar;
float buffer_zone;
bool multiline;
+ bool mask;
GUI::GetSetting(this, "scrollbar", scrollbar);
GUI::GetSetting(this, "buffer_zone", buffer_zone);
GUI::GetSetting(this, "multiline", multiline);
+ GUI::GetSetting(this, "mask", mask);
if (scrollbar && multiline)
{
// Draw scrollbar
IGUIScrollBarOwner::Draw();
}
if (GetGUI())
{
CStrW font_name_w;
CColor color, color_selected;
//CStrW caption;
GUI::GetSetting(this, "font", font_name_w);
GUI::GetSetting(this, "textcolor", color);
GUI::GetSetting(this, "textcolor_selected", color_selected);
CStrIntern font_name(font_name_w.ToUTF8());
// Get pointer of caption, it might be very large, and we don't
// want to copy it continuously.
- CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
+ CStrW *pCaption = NULL;
+ wchar_t mask_char = L'*';
+ if (mask)
+ {
+ CStrW maskStr;
+ GUI::GetSetting(this, "mask_char", maskStr);
+ if (maskStr.length() > 0)
+ mask_char = maskStr[0];
+ }
+ else
+ pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
CGUISpriteInstance *sprite=NULL, *sprite_selectarea=NULL;
int cell_id;
GUI::GetSettingPointer(this, "sprite", sprite);
GUI::GetSettingPointer(this, "sprite_selectarea", sprite_selectarea);
GUI::GetSetting(this, "cell_id", cell_id);
GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
float scroll=0.f;
if (scrollbar && multiline)
{
scroll = GetScrollBar(0).GetPos();
}
CFontMetrics font(font_name);
// We'll have to setup clipping manually, since we're doing the rendering manually.
CRect cliparea(m_CachedActualSize);
// First we'll figure out the clipping area, which is the cached actual size
// substracted by an optional scrollbar
if (scrollbar)
{
scroll = GetScrollBar(0).GetPos();
// substract scrollbar from cliparea
if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
cliparea.right <= GetScrollBar(0).GetOuterRect().right)
cliparea.right = GetScrollBar(0).GetOuterRect().left;
if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
cliparea.left < GetScrollBar(0).GetOuterRect().right)
cliparea.left = GetScrollBar(0).GetOuterRect().right;
}
if (cliparea != CRect())
{
glEnable(GL_SCISSOR_TEST);
glScissor(cliparea.left, g_yres - cliparea.bottom, cliparea.GetWidth(), cliparea.GetHeight());
}
// These are useful later.
int VirtualFrom, VirtualTo;
if (m_iBufferPos_Tail >= m_iBufferPos)
{
VirtualFrom = m_iBufferPos;
VirtualTo = m_iBufferPos_Tail;
}
else
{
VirtualFrom = m_iBufferPos_Tail;
VirtualTo = m_iBufferPos;
}
// Get the height of this font.
float h = (float)font.GetHeight();
float ls = (float)font.GetLineSpacing();
CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text);
CTextRenderer textRenderer(tech->GetShader());
textRenderer.Font(font_name);
// Set the Z to somewhat more, so we can draw a selected area between the
// the control and the text.
textRenderer.Translate(
(float)(int)(m_CachedActualSize.left) + buffer_zone,
(float)(int)(m_CachedActualSize.top+h) + buffer_zone,
bz+0.1f);
// U+FE33: PRESENTATION FORM FOR VERTICAL LOW LINE
// (sort of like a | which is aligned to the left of most characters)
float buffered_y = -scroll+buffer_zone;
// When selecting larger areas, we need to draw a rectangle box
// around it, and this is to keep track of where the box
// started, because we need to follow the iteration until we
// reach the end, before we can actually draw it.
bool drawing_box = false;
float box_x=0.f;
float x_pointer=0.f;
// If we have a selecting box (i.e. when you have selected letters, not just when
// the pointer is between two letters) we need to process all letters once
// before we do it the second time and render all the text. We can't do it
// in the same loop because text will have been drawn, so it will disappear when
// drawn behind the text that has already been drawn. Confusing, well it's necessary
// (I think).
if (SelectingText())
{
// Now m_iBufferPos_Tail can be of both sides of m_iBufferPos,
// just like you can select from right to left, as you can
// left to right. Is there a difference? Yes, the pointer
// be placed accordingly, so that if you select shift and
// expand this selection, it will expand on appropriate side.
// Anyway, since the drawing procedure needs "To" to be
// greater than from, we need virtual values that might switch
// place.
int VirtualFrom, VirtualTo;
if (m_iBufferPos_Tail >= m_iBufferPos)
{
VirtualFrom = m_iBufferPos;
VirtualTo = m_iBufferPos_Tail;
}
else
{
VirtualFrom = m_iBufferPos_Tail;
VirtualTo = m_iBufferPos;
}
bool done = false;
for (std::list::const_iterator it = m_CharacterPositions.begin();
it != m_CharacterPositions.end();
++it, buffered_y += ls, x_pointer = 0.f)
{
if (multiline)
{
if (buffered_y > m_CachedActualSize.GetHeight())
break;
}
// We might as well use 'i' here to iterate, because we need it
// (often compared against ints, so don't make it size_t)
for (int i=0; i < (int)it->m_ListOfX.size()+2; ++i)
{
if (it->m_ListStart + i == VirtualFrom)
{
// we won't actually draw it now, because we don't
// know the width of each glyph to that position.
// we need to go along with the iteration, and
// make a mark where the box started:
drawing_box = true; // will turn false when finally rendered.
// Get current x position
box_x = x_pointer;
}
// no else!
const bool at_end = (i == (int)it->m_ListOfX.size()+1);
if (drawing_box == true &&
(it->m_ListStart + i == VirtualTo || at_end))
{
// Depending on if it's just a row change, or if it's
// the end of the select box, do slightly different things.
if (at_end)
{
if (it->m_ListStart + i != VirtualFrom)
{
// and actually add a white space! yes, this is done in any common input
x_pointer += (float)font.GetCharacterWidth(L' ');
}
}
else
{
drawing_box = false;
done = true;
}
CRect rect;
// Set 'rect' depending on if it's a multiline control, or a one-line control
if (multiline)
{
rect = CRect(m_CachedActualSize.left+box_x+buffer_zone,
m_CachedActualSize.top+buffered_y+(h-ls)/2,
m_CachedActualSize.left+x_pointer+buffer_zone,
m_CachedActualSize.top+buffered_y+(h+ls)/2);
if (rect.bottom < m_CachedActualSize.top)
continue;
if (rect.top < m_CachedActualSize.top)
rect.top = m_CachedActualSize.top;
if (rect.bottom > m_CachedActualSize.bottom)
rect.bottom = m_CachedActualSize.bottom;
}
else // if one-line
{
rect = CRect(m_CachedActualSize.left+box_x+buffer_zone-m_HorizontalScroll,
m_CachedActualSize.top+buffered_y+(h-ls)/2,
m_CachedActualSize.left+x_pointer+buffer_zone-m_HorizontalScroll,
m_CachedActualSize.top+buffered_y+(h+ls)/2);
if (rect.left < m_CachedActualSize.left)
rect.left = m_CachedActualSize.left;
if (rect.right > m_CachedActualSize.right)
rect.right = m_CachedActualSize.right;
}
if (sprite_selectarea)
GetGUI()->DrawSprite(*sprite_selectarea, cell_id, bz+0.05f, rect);
}
if (i < (int)it->m_ListOfX.size())
- x_pointer += (float)font.GetCharacterWidth((*pCaption)[it->m_ListStart + i]);
+ {
+ if (!mask)
+ x_pointer += (float)font.GetCharacterWidth((*pCaption)[it->m_ListStart + i]);
+ else
+ x_pointer += (float)font.GetCharacterWidth(mask_char);
+ }
}
if (done)
break;
// If we're about to draw a box, and all of a sudden changes
// line, we need to draw that line's box, and then reset
// the box drawing to the beginning of the new line.
if (drawing_box)
{
box_x = 0.f;
}
}
}
// Reset some from previous run
buffered_y = -scroll;
// Setup initial color (then it might change and change back, when drawing selected area)
textRenderer.Color(color);
tech->BeginPass();
bool using_selected_color = false;
for (std::list::const_iterator it = m_CharacterPositions.begin();
it != m_CharacterPositions.end();
++it, buffered_y += ls)
{
if (buffered_y + buffer_zone >= -ls || !multiline)
{
if (multiline)
{
if (buffered_y + buffer_zone > m_CachedActualSize.GetHeight())
break;
}
CMatrix3D savedTransform = textRenderer.GetTransform();
// Text must always be drawn in integer values. So we have to convert scroll
if (multiline)
textRenderer.Translate(0.f, -(float)(int)scroll, 0.f);
else
textRenderer.Translate(-(float)(int)m_HorizontalScroll, 0.f, 0.f);
// We might as well use 'i' here, because we need it
// (often compared against ints, so don't make it size_t)
for (int i=0; i < (int)it->m_ListOfX.size()+1; ++i)
{
if (!multiline && i < (int)it->m_ListOfX.size())
{
if (it->m_ListOfX[i] - m_HorizontalScroll < -buffer_zone)
{
// We still need to translate the OpenGL matrix
if (i == 0)
textRenderer.Translate(it->m_ListOfX[i], 0.f, 0.f);
else
textRenderer.Translate(it->m_ListOfX[i] - it->m_ListOfX[i-1], 0.f, 0.f);
continue;
}
}
// End of selected area, change back color
if (SelectingText() &&
it->m_ListStart + i == VirtualTo)
{
using_selected_color = false;
textRenderer.Color(color);
}
if (i != (int)it->m_ListOfX.size() &&
it->m_ListStart + i == m_iBufferPos)
{
// selecting only one, then we need only to draw a cursor.
if (m_CursorVisState)
textRenderer.Put(0.0f, 0.0f, L"_");
}
// Drawing selected area
if (SelectingText() &&
it->m_ListStart + i >= VirtualFrom &&
it->m_ListStart + i < VirtualTo &&
using_selected_color == false)
{
using_selected_color = true;
textRenderer.Color(color_selected);
}
if (i != (int)it->m_ListOfX.size())
- textRenderer.PrintfAdvance(L"%lc", (*pCaption)[it->m_ListStart + i]);
+ {
+ if (!mask)
+ textRenderer.PrintfAdvance(L"%lc", (*pCaption)[it->m_ListStart + i]);
+ else
+ textRenderer.PrintfAdvance(L"%lc", mask_char);
+ }
// check it's now outside a one-liner, then we'll break
if (!multiline && i < (int)it->m_ListOfX.size())
{
if (it->m_ListOfX[i] - m_HorizontalScroll > m_CachedActualSize.GetWidth()-buffer_zone)
break;
}
}
if (it->m_ListStart + (int)it->m_ListOfX.size() == m_iBufferPos)
{
textRenderer.Color(color);
if (m_CursorVisState)
textRenderer.PutAdvance(L"_");
if (using_selected_color)
{
textRenderer.Color(color_selected);
}
}
textRenderer.SetTransform(savedTransform);
}
textRenderer.Translate(0.f, ls, 0.f);
}
textRenderer.Render();
if (cliparea != CRect())
glDisable(GL_SCISSOR_TEST);
tech->EndPass();
}
}
void CInput::UpdateText(int from, int to_before, int to_after)
{
CStrW caption;
CStrW font_name_w;
float buffer_zone;
bool multiline;
+ bool mask;
GUI::GetSetting(this, "font", font_name_w);
GUI::GetSetting(this, "caption", caption);
GUI::GetSetting(this, "buffer_zone", buffer_zone);
GUI::GetSetting(this, "multiline", multiline);
+ GUI::GetSetting(this, "mask", mask);
CStrIntern font_name(font_name_w.ToUTF8());
+ wchar_t mask_char = L'*';
+ if (mask)
+ {
+ CStrW maskStr;
+ GUI::GetSetting(this, "mask_char", maskStr);
+ if (maskStr.length() > 0)
+ mask_char = maskStr[0];
+ }
+
// Ensure positions are valid after caption changes
m_iBufferPos = std::min(m_iBufferPos, (int)caption.size());
m_iBufferPos_Tail = std::min(m_iBufferPos_Tail, (int)caption.size());
if (font_name.empty())
{
// Destroy everything stored, there's no font, so there can be
// no data.
m_CharacterPositions.clear();
return;
}
SRow row;
row.m_ListStart = 0;
int to = 0; // make sure it's initialized
if (to_before == -1)
to = (int)caption.length();
CFontMetrics font(font_name);
std::list::iterator current_line;
// Used to ... TODO
int check_point_row_start = -1;
int check_point_row_end = -1;
// Reset
if (from == 0 && to_before == -1)
{
m_CharacterPositions.clear();
current_line = m_CharacterPositions.begin();
}
else
{
ENSURE(to_before != -1);
std::list::iterator destroy_row_from, destroy_row_to;
// Used to check if the above has been set to anything,
// previously a comparison like:
// destroy_row_from == std::list::iterator()
// ... was used, but it didn't work with GCC.
bool destroy_row_from_used=false, destroy_row_to_used=false;
// Iterate, and remove everything between 'from' and 'to_before'
// actually remove the entire lines they are on, it'll all have
// to be redone. And when going along, we'll delete a row at a time
// when continuing to see how much more after 'to' we need to remake.
int i=0;
for (std::list::iterator it = m_CharacterPositions.begin();
it != m_CharacterPositions.end(); ++it, ++i)
{
if (destroy_row_from_used == false &&
it->m_ListStart > from)
{
// Destroy the previous line, and all to 'to_before'
destroy_row_from = it;
--destroy_row_from;
destroy_row_from_used = true;
// For the rare case that we might remove characters to a word
// so that it suddenly fits on the previous row,
// we need to by standards re-do the whole previous line too
// (if one exists)
if (destroy_row_from != m_CharacterPositions.begin())
--destroy_row_from;
}
if (destroy_row_to_used == false &&
it->m_ListStart > to_before)
{
destroy_row_to = it;
destroy_row_to_used = true;
// If it isn't the last row, we'll add another row to delete,
// just so we can see if the last restorted line is
// identical to what it was before. If it isn't, then we'll
// have to continue.
// 'check_point_row_start' is where we store how the that
// line looked.
if (destroy_row_to != m_CharacterPositions.end())
{
check_point_row_start = destroy_row_to->m_ListStart;
check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
if (destroy_row_to->m_ListOfX.empty())
++check_point_row_end;
}
++destroy_row_to;
break;
}
}
if (destroy_row_from_used == false)
{
destroy_row_from = m_CharacterPositions.end();
--destroy_row_from;
// As usual, let's destroy another row back
if (destroy_row_from != m_CharacterPositions.begin())
--destroy_row_from;
destroy_row_from_used = true;
current_line = destroy_row_from;
}
if (destroy_row_to_used == false)
{
destroy_row_to = m_CharacterPositions.end();
check_point_row_start = -1;
destroy_row_from_used = true;
}
// set 'from' to the row we'll destroy from
// and 'to' to the row we'll destroy to
from = destroy_row_from->m_ListStart;
if (destroy_row_to != m_CharacterPositions.end())
to = destroy_row_to->m_ListStart; // notice it will iterate [from, to), so it will never reach to.
else
to = (int)caption.length();
// Setup the first row
row.m_ListStart = destroy_row_from->m_ListStart;
std::list::iterator temp_it = destroy_row_to;
--temp_it;
current_line = m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
// If there has been a change in number of characters
// we need to change all m_ListStart that comes after
// the interval we just destroyed. We'll change all
// values with the delta change of the string length.
int delta = to_after - to_before;
if (delta != 0)
{
for (std::list::iterator it = current_line;
it != m_CharacterPositions.end();
++it)
{
it->m_ListStart += delta;
}
// Update our check point too!
check_point_row_start += delta;
check_point_row_end += delta;
if (to != (int)caption.length())
to += delta;
}
}
int last_word_started=from;
//int last_list_start=-1; // unused
float x_pos = 0.f;
//if (to_before != -1)
// return;
for (int i=from; i= GetTextAreaWidth() && multiline)
{
// The following decides whether it will word-wrap a word,
// or if it's only one word on the line, where it has to
// break the word apart.
if (last_word_started == row.m_ListStart)
{
last_word_started = i;
row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started));
//row.m_ListOfX.push_back( x_pos );
//continue;
}
else
{
// regular word-wrap
row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started+1));
}
// Now, create a new line:
// notice: when we enter a newline, you can stand with the cursor
// both before and after that character, being on different
// rows. With automatic word-wrapping, that is not possible. Which
// is intuitively correct.
current_line = m_CharacterPositions.insert( current_line, row );
++current_line;
// Setup the next row:
row.m_ListOfX.clear();
row.m_ListStart = last_word_started;
i=last_word_started-1;
x_pos = 0.f;
}
else
// Get width of this character:
row.m_ListOfX.push_back( x_pos );
}
// Check if it's the last iteration, and we're not revising the whole string
// because in that case, more word-wrapping might be needed.
// also check if the current line isn't the end
if (to_before != -1 && i == to-1 && current_line != m_CharacterPositions.end())
{
// check all rows and see if any existing
if (row.m_ListStart != check_point_row_start)
{
std::list::iterator destroy_row_from, destroy_row_to;
// Are used to check if the above has been set to anything,
// previously a comparison like:
// destroy_row_from == std::list::iterator()
// was used, but it didn't work with GCC.
bool destroy_row_from_used=false, destroy_row_to_used=false;
// Iterate, and remove everything between 'from' and 'to_before'
// actually remove the entire lines they are on, it'll all have
// to be redone. And when going along, we'll delete a row at a time
// when continuing to see how much more after 'to' we need to remake.
int i=0;
for (std::list::iterator it=m_CharacterPositions.begin();
it!=m_CharacterPositions.end(); ++it, ++i)
{
if (destroy_row_from_used == false &&
it->m_ListStart > check_point_row_start)
{
// Destroy the previous line, and all to 'to_before'
//if (i >= 2)
// destroy_row_from = it-2;
//else
// destroy_row_from = it-1;
destroy_row_from = it;
destroy_row_from_used = true;
//--destroy_row_from;
}
if (destroy_row_to_used == false &&
it->m_ListStart > check_point_row_end)
{
destroy_row_to = it;
destroy_row_to_used = true;
// If it isn't the last row, we'll add another row to delete,
// just so we can see if the last restorted line is
// identical to what it was before. If it isn't, then we'll
// have to continue.
// 'check_point_row_start' is where we store how the that
// line looked.
// if (destroy_row_to !=
if (destroy_row_to != m_CharacterPositions.end())
{
check_point_row_start = destroy_row_to->m_ListStart;
check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
if (destroy_row_to->m_ListOfX.empty())
++check_point_row_end;
}
else
check_point_row_start = check_point_row_end = -1;
++destroy_row_to;
break;
}
}
if (destroy_row_from_used == false)
{
destroy_row_from = m_CharacterPositions.end();
--destroy_row_from;
destroy_row_from_used = true;
current_line = destroy_row_from;
}
if (destroy_row_to_used == false)
{
destroy_row_to = m_CharacterPositions.end();
check_point_row_start = check_point_row_end = -1;
destroy_row_to_used = true;
}
// set 'from' to the from row we'll destroy
// and 'to' to 'to_after'
from = destroy_row_from->m_ListStart;
if (destroy_row_to != m_CharacterPositions.end())
to = destroy_row_to->m_ListStart; // notice it will iterate [from, to[, so it will never reach to.
else
to = (int)caption.length();
// Set current line, new rows will be added before current_line, so
// we'll choose the destroy_row_to, because it won't be deleted
// in the coming erase.
current_line = destroy_row_to;
std::list::iterator temp = destroy_row_to;
--temp;
m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
}
// else, the for loop will end naturally.
}
}
// This is kind of special, when we renew a some lines, then the last
// one will sometimes end with a space (' '), that really should
// be omitted when word-wrapping. So we'll check if the last row
// we'll add has got the same value as the next row.
if (current_line != m_CharacterPositions.end())
{
if (row.m_ListStart + (int)row.m_ListOfX.size() == current_line->m_ListStart)
row.m_ListOfX.resize( row.m_ListOfX.size()-1 );
}
// add the final row (even if empty)
m_CharacterPositions.insert(current_line, row);
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
// Update scollbar
if (scrollbar)
{
GetScrollBar(0).SetScrollRange(m_CharacterPositions.size() * font.GetLineSpacing() + buffer_zone*2.f);
GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight());
}
}
int CInput::GetMouseHoveringTextPosition()
{
if (m_CharacterPositions.empty())
return 0;
// Return position
int retPosition;
float buffer_zone;
bool multiline;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
GUI::GetSetting(this, "multiline", multiline);
std::list::iterator current = m_CharacterPositions.begin();
CPos mouse = GetMousePos();
if (multiline)
{
CStrW font_name_w;
bool scrollbar;
GUI::GetSetting(this, "font", font_name_w);
GUI::GetSetting(this, "scrollbar", scrollbar);
CStrIntern font_name(font_name_w.ToUTF8());
float scroll=0.f;
if (scrollbar)
{
scroll = GetScrollBar(0).GetPos();
}
// Pointer to caption, will come in handy
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
UNUSED2(pCaption);
// Now get the height of the font.
// TODO: Get the real font
CFontMetrics font(font_name);
float spacing = (float)font.GetLineSpacing();
//float height = (float)font.GetHeight(); // unused
// Change mouse position relative to text.
mouse -= m_CachedActualSize.TopLeft();
mouse.x -= buffer_zone;
mouse.y += scroll - buffer_zone;
//if ((m_CharacterPositions.size()-1) * spacing + height < mouse.y)
// m_iBufferPos = pCaption->Length();
int row = (int)((mouse.y) / spacing);//m_CharachterPositions.size()
if (row < 0)
row = 0;
if (row > (int)m_CharacterPositions.size()-1)
row = (int)m_CharacterPositions.size()-1;
// TODO Gee (2004-11-21): Okay, I need a 'std::list' for some reasons, but I would really like to
// be able to get the specific element here. This is hopefully a temporary hack.
for (int i=0; im_ListStart;
// Okay, now loop through the glyphs to find the appropriate X position
float dummy;
retPosition += GetXTextPosition(current, mouse.x, dummy);
return retPosition;
}
// Does not process horizontal scrolling, 'x' must be modified before inputted.
int CInput::GetXTextPosition(const std::list::iterator ¤t, const float &x, float &wanted)
{
int ret=0;
float previous=0.f;
int i=0;
for (std::vector::iterator it=current->m_ListOfX.begin();
it!=current->m_ListOfX.end();
++it, ++i)
{
if (*it >= x)
{
if (x - previous >= *it - x)
ret += i+1;
else
ret += i;
break;
}
previous = *it;
}
// If a position wasn't found, we will assume the last
// character of that line.
if (i == (int)current->m_ListOfX.size())
{
ret += i;
wanted = x;
}
else wanted = 0.f;
return ret;
}
void CInput::DeleteCurSelection()
{
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
int virtualFrom;
int virtualTo;
if (m_iBufferPos_Tail >= m_iBufferPos)
{
virtualFrom = m_iBufferPos;
virtualTo = m_iBufferPos_Tail;
}
else
{
virtualFrom = m_iBufferPos_Tail;
virtualTo = m_iBufferPos;
}
*pCaption = pCaption->Left( virtualFrom ) +
pCaption->Right( (long) pCaption->length() - (virtualTo) );
UpdateText(virtualFrom, virtualTo, virtualFrom);
// Remove selection
m_iBufferPos_Tail = -1;
m_iBufferPos = virtualFrom;
}
bool CInput::SelectingText() const
{
return m_iBufferPos_Tail != -1 &&
m_iBufferPos_Tail != m_iBufferPos;
}
float CInput::GetTextAreaWidth()
{
bool scrollbar;
float buffer_zone;
GUI::GetSetting(this, "scrollbar", scrollbar);
GUI::GetSetting(this, "buffer_zone", buffer_zone);
if (scrollbar && GetScrollBar(0).GetStyle())
return m_CachedActualSize.GetWidth() - buffer_zone*2.f - GetScrollBar(0).GetStyle()->m_Width;
else
return m_CachedActualSize.GetWidth() - buffer_zone*2.f;
}
void CInput::UpdateAutoScroll()
{
float buffer_zone;
bool multiline;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
GUI::GetSetting(this, "multiline", multiline);
// Autoscrolling up and down
if (multiline)
{
CStrW font_name_w;
bool scrollbar;
GUI::GetSetting(this, "font", font_name_w);
GUI::GetSetting(this, "scrollbar", scrollbar);
CStrIntern font_name(font_name_w.ToUTF8());
float scroll=0.f;
if (!scrollbar)
return;
scroll = GetScrollBar(0).GetPos();
// Now get the height of the font.
// TODO: Get the real font
CFontMetrics font(font_name);
float spacing = (float)font.GetLineSpacing();
//float height = font.GetHeight();
// TODO Gee (2004-11-21): Okay, I need a 'std::list' for some reasons, but I would really like to
// be able to get the specific element here. This is hopefully a temporary hack.
std::list::iterator current = m_CharacterPositions.begin();
int row=0;
while (current != m_CharacterPositions.end())
{
if (m_iBufferPos >= current->m_ListStart &&
m_iBufferPos <= current->m_ListStart+(int)current->m_ListOfX.size())
break;
++current;
++row;
}
// If scrolling down
if (-scroll + (float)(row+1) * spacing + buffer_zone*2.f > m_CachedActualSize.GetHeight())
{
// Scroll so the selected row is shown completely, also with buffer_zone length to the edge.
GetScrollBar(0).SetPos((float)(row+1) * spacing - m_CachedActualSize.GetHeight() + buffer_zone*2.f);
}
else
// If scrolling up
if (-scroll + (float)row * spacing < 0.f)
{
// Scroll so the selected row is shown completely, also with buffer_zone length to the edge.
GetScrollBar(0).SetPos((float)row * spacing);
}
}
else // autoscrolling left and right
{
// Get X position of position:
if (m_CharacterPositions.empty())
return;
float x_position = 0.f;
float x_total = 0.f;
if (!m_CharacterPositions.begin()->m_ListOfX.empty())
{
// Get position of m_iBufferPos
if ((int)m_CharacterPositions.begin()->m_ListOfX.size() >= m_iBufferPos &&
m_iBufferPos != 0)
x_position = m_CharacterPositions.begin()->m_ListOfX[m_iBufferPos-1];
// Get complete length:
x_total = m_CharacterPositions.begin()->m_ListOfX[ m_CharacterPositions.begin()->m_ListOfX.size()-1 ];
}
// Check if outside to the right
if (x_position - m_HorizontalScroll + buffer_zone*2.f > m_CachedActualSize.GetWidth())
m_HorizontalScroll = x_position - m_CachedActualSize.GetWidth() + buffer_zone*2.f;
// Check if outside to the left
if (x_position - m_HorizontalScroll < 0.f)
m_HorizontalScroll = x_position;
// Check if the text doesn't even fill up to the right edge even though scrolling is done.
if (m_HorizontalScroll != 0.f &&
x_total - m_HorizontalScroll + buffer_zone*2.f < m_CachedActualSize.GetWidth())
m_HorizontalScroll = x_total - m_CachedActualSize.GetWidth() + buffer_zone*2.f;
// Now this is the fail-safe, if x_total isn't even the length of the control,
// remove all scrolling
if (x_total + buffer_zone*2.f < m_CachedActualSize.GetWidth())
m_HorizontalScroll = 0.f;
}
}
Index: ps/trunk/source/gui/COList.h
===================================================================
--- ps/trunk/source/gui/COList.h (nonexistent)
+++ ps/trunk/source/gui/COList.h (revision 14098)
@@ -0,0 +1,64 @@
+#ifndef INCLUDED_COLIST
+#define INCLUDED_COLIST
+
+//--------------------------------------------------------
+// Includes / Compiler directives
+//--------------------------------------------------------
+#include "GUI.h"
+#include "CList.h"
+
+//--------------------------------------------------------
+// Macros
+//--------------------------------------------------------
+
+//--------------------------------------------------------
+// Types
+//--------------------------------------------------------
+
+//--------------------------------------------------------
+// Declarations
+//--------------------------------------------------------
+
+struct ObjectDef
+{
+ CColor m_TextColor;
+ CStr m_Id;
+ float m_Width;
+ CStrW m_Heading;
+
+};
+
+/**
+ * Todo : add description
+ *
+ */
+class COList : public CList
+{
+ GUI_OBJECT(COList)
+
+public:
+ COList();
+
+protected:
+ void SetupText();
+ void HandleMessage(SGUIMessage &Message);
+
+ /**
+ * Handle the \ tag.
+ */
+ virtual bool HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile);
+
+ void DrawList(const int &selected, const CStr& _sprite,
+ const CStr& _sprite_selected, const CStr& _textcolor);
+
+ virtual CRect GetListRect() const;
+
+ std::vector m_ObjectsDefs;
+
+private:
+ float m_HeadingHeight;
+ // Width of space avalible for columns
+ float m_TotalAvalibleColumnWidth;
+};
+
+#endif // INCLUDED_COLIST
Index: ps/trunk/binaries/system/gloox-1.0d.dll
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/system/gloox-1.0d.dll
===================================================================
--- ps/trunk/binaries/system/gloox-1.0d.dll (nonexistent)
+++ ps/trunk/binaries/system/gloox-1.0d.dll (revision 14098)
Property changes on: ps/trunk/binaries/system/gloox-1.0d.dll
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/source/gui/CGUIScrollBarVertical.cpp
===================================================================
--- ps/trunk/source/gui/CGUIScrollBarVertical.cpp (revision 14097)
+++ ps/trunk/source/gui/CGUIScrollBarVertical.cpp (revision 14098)
@@ -1,192 +1,194 @@
/* Copyright (C) 2009 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 .
*/
/*
IGUIScrollBar
*/
#include "precompiled.h"
#include "GUI.h"
#include "CGUIScrollBarVertical.h"
#include "ps/CLogger.h"
CGUIScrollBarVertical::CGUIScrollBarVertical()
{
}
CGUIScrollBarVertical::~CGUIScrollBarVertical()
{
}
void CGUIScrollBarVertical::SetPosFromMousePos(const CPos &mouse)
{
if (!GetStyle())
return;
/**
* Calculate the position for the top of the item being scrolled
*/
- m_Pos = m_PosWhenPressed + GetMaxPos() * (mouse.y - m_BarPressedAtPos.y) / (m_Length - GetStyle()->m_Width * 2 - m_BarSize);
+ float emptyBackground = m_Length - m_BarSize;
+ if (GetStyle()->m_UseEdgeButtons)
+ emptyBackground -= GetStyle()->m_Width * 2;
+ m_Pos = m_PosWhenPressed + GetMaxPos() * (mouse.y - m_BarPressedAtPos.y) / emptyBackground;
}
void CGUIScrollBarVertical::Draw()
{
if (!GetStyle())
{
LOGWARNING(L"Attempt to draw scrollbar without a style.");
return;
}
- // Only draw the scrollbar if the GUI exists and there is something to scroll.
if (GetGUI() && GetMaxPos() != 1)
{
CRect outline = GetOuterRect();
// Draw background
GetGUI()->DrawSprite(GetStyle()->m_SpriteBackVertical,
0,
m_Z+0.1f,
CRect(outline.left,
- outline.top+(m_UseEdgeButtons?GetStyle()->m_Width:0),
+ outline.top+(GetStyle()->m_UseEdgeButtons?GetStyle()->m_Width:0),
outline.right,
- outline.bottom-(m_UseEdgeButtons?GetStyle()->m_Width:0))
+ outline.bottom-(GetStyle()->m_UseEdgeButtons?GetStyle()->m_Width:0))
);
- if (m_UseEdgeButtons)
+ if (GetStyle()->m_UseEdgeButtons)
{
// Get Appropriate sprites
const CGUISpriteInstance *button_top, *button_bottom;
// figure out what sprite to use for top button
if (m_ButtonMinusHovered)
{
if (m_ButtonMinusPressed)
button_top = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopPressed, GetStyle()->m_SpriteButtonTop);
else
button_top = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonTopOver, GetStyle()->m_SpriteButtonTop);
}
else button_top = &GetStyle()->m_SpriteButtonTop;
// figure out what sprite to use for bottom button
if (m_ButtonPlusHovered)
{
if (m_ButtonPlusPressed)
button_bottom = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomPressed, GetStyle()->m_SpriteButtonBottom);
else
button_bottom = &GUI<>::FallBackSprite(GetStyle()->m_SpriteButtonBottomOver, GetStyle()->m_SpriteButtonBottom);
}
else button_bottom = &GetStyle()->m_SpriteButtonBottom;
// Draw top button
GetGUI()->DrawSprite(*button_top,
0,
m_Z+0.2f,
CRect(outline.left,
outline.top,
outline.right,
outline.top+GetStyle()->m_Width)
);
// Draw bottom button
GetGUI()->DrawSprite(*button_bottom,
0,
m_Z+0.2f,
CRect(outline.left,
outline.bottom-GetStyle()->m_Width,
outline.right,
outline.bottom)
);
}
// Draw bar
GetGUI()->DrawSprite(GetStyle()->m_SpriteBarVertical,
0,
m_Z + 0.2f,
GetBarRect());
}
}
void CGUIScrollBarVertical::HandleMessage(SGUIMessage &Message)
{
IGUIScrollBar::HandleMessage(Message);
}
CRect CGUIScrollBarVertical::GetBarRect() const
{
CRect ret;
if (!GetStyle())
return ret;
// Get from where the scroll area begins to where it ends
float from = m_Y;
float to = m_Y + m_Length - m_BarSize;
- if (m_UseEdgeButtons)
+ if (GetStyle()->m_UseEdgeButtons)
{
from += GetStyle()->m_Width;
to -= GetStyle()->m_Width;
}
// Setup rectangle
ret.top = from + (to - from) * (m_Pos / GetMaxPos());
ret.bottom = ret.top + m_BarSize;
ret.right = m_X + ((m_RightAligned)?(0.f):(GetStyle()->m_Width));
ret.left = ret.right - GetStyle()->m_Width;
return ret;
}
CRect CGUIScrollBarVertical::GetOuterRect() const
{
CRect ret;
if (!GetStyle())
return ret;
ret.top = m_Y;
ret.bottom = m_Y+m_Length;
ret.right = m_X + ((m_RightAligned)?(0):(GetStyle()->m_Width));
ret.left = ret.right - GetStyle()->m_Width;
return ret;
}
bool CGUIScrollBarVertical::HoveringButtonMinus(const CPos &mouse)
{
if (!GetStyle())
return false;
float StartX = (m_RightAligned)?(m_X-GetStyle()->m_Width):(m_X);
return (mouse.x >= StartX &&
mouse.x <= StartX + GetStyle()->m_Width &&
mouse.y >= m_Y &&
mouse.y <= m_Y + GetStyle()->m_Width);
}
bool CGUIScrollBarVertical::HoveringButtonPlus(const CPos &mouse)
{
if (!GetStyle())
return false;
float StartX = (m_RightAligned)?(m_X-GetStyle()->m_Width):(m_X);
return (mouse.x > StartX &&
mouse.x < StartX + GetStyle()->m_Width &&
mouse.y > m_Y + m_Length - GetStyle()->m_Width &&
mouse.y < m_Y + m_Length);
}
Index: ps/trunk/source/gui/COList.cpp
===================================================================
--- ps/trunk/source/gui/COList.cpp (nonexistent)
+++ ps/trunk/source/gui/COList.cpp (revision 14098)
@@ -0,0 +1,318 @@
+#include "precompiled.h"
+#include "COList.h"
+
+#include "ps/CLogger.h"
+
+COList::COList() : CList(),m_HeadingHeight(30.f)
+{
+ AddSetting(GUIST_CGUISpriteInstance, "sprite_heading");
+}
+
+void COList::SetupText()
+{
+ if (!GetGUI())
+ return;
+
+ CGUIList *pList;
+ GUI::GetSettingPointer(this, "list_name", pList);
+
+ //ENSURE(m_GeneratedTexts.size()>=1);
+
+ m_ItemsYPositions.resize( pList->m_Items.size()+1 );
+
+ // Delete all generated texts. Some could probably be saved,
+ // but this is easier, and this function will never be called
+ // continuously, or even often, so it'll probably be okay.
+ std::vector::iterator it;
+ for (it=m_GeneratedTexts.begin(); it!=m_GeneratedTexts.end(); ++it)
+ {
+ if (*it)
+ delete *it;
+ }
+ m_GeneratedTexts.clear();
+
+ CStrW font;
+ if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty())
+ // Use the default if none is specified
+ // TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style.
+ font = L"default";
+
+ //CGUIString caption;
+ bool scrollbar;
+ //GUI::GetSetting(this, "caption", caption);
+ GUI::GetSetting(this, "scrollbar", scrollbar);
+
+ float width = GetListRect().GetWidth();
+ // remove scrollbar if applicable
+ if (scrollbar && GetScrollBar(0).GetStyle())
+ width -= GetScrollBar(0).GetStyle()->m_Width;
+
+ // Cache width for other use
+ m_TotalAvalibleColumnWidth = width;
+
+ float buffer_zone=0.f;
+ GUI::GetSetting(this, "buffer_zone", buffer_zone);
+
+
+ for (unsigned int c=0; cGenerateText(gui_string, font, width, buffer_zone, this);
+ AddText(text);
+ }
+
+
+ // Generate texts
+ float buffered_y = 0.f;
+
+ for (int i=0; i<(int)pList->m_Items.size(); ++i)
+ {
+ m_ItemsYPositions[i] = buffered_y;
+ for (unsigned int c=0; c::GetSettingPointer(this, m_ObjectsDefs[c].m_Id, pList_c);
+ SGUIText *text = new SGUIText();
+ *text = GetGUI()->GenerateText(pList_c->m_Items[i], font, width, buffer_zone, this);
+ if (c==0)
+ buffered_y += text->m_Size.cy;
+ AddText(text);
+ }
+ }
+
+ m_ItemsYPositions[pList->m_Items.size()] = buffered_y;
+
+ //if (! scrollbar)
+ // CalculateTextPosition(m_CachedActualSize, m_TextPos, *m_GeneratedTexts[0]);
+
+ // Setup scrollbar
+ if (scrollbar)
+ {
+ GetScrollBar(0).SetScrollRange( m_ItemsYPositions.back() );
+ GetScrollBar(0).SetScrollSpace( GetListRect().GetHeight() );
+
+ CRect rect = GetListRect();
+ GetScrollBar(0).SetX( rect.right );
+ GetScrollBar(0).SetY( rect.top );
+ GetScrollBar(0).SetZ( GetBufferedZ() );
+ GetScrollBar(0).SetLength( rect.bottom - rect.top );
+ }
+}
+
+CRect COList::GetListRect() const
+{
+ return m_CachedActualSize + CRect(0, m_HeadingHeight, 0, 0);
+}
+
+void COList::HandleMessage(SGUIMessage &Message)
+{
+ CList::HandleMessage(Message);
+// switch (Message.type)
+// {
+// case GUIM_SETTINGS_UPDATED:
+// if (Message.value.Find("list_") != -1)
+// {
+// SetupText();
+// }
+// }
+}
+
+bool COList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
+{
+ int elmt_item = pFile->GetElementID("item");
+ int elmt_heading = pFile->GetElementID("heading");
+ int elmt_def = pFile->GetElementID("def");
+
+ if (child.GetNodeName() == elmt_item)
+ {
+ AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8());
+ return true;
+ }
+ else if (child.GetNodeName() == elmt_heading)
+ {
+ CStrW text (child.GetText().FromUTF8());
+
+ return true;
+ }
+ else if (child.GetNodeName() == elmt_def)
+ {
+ ObjectDef oDef;
+
+ XMBAttributeList attributes = child.GetAttributes();
+ for (int i=0; iGetAttributeString(attr.Name));
+ CStr attr_value (attr.Value);
+
+ if (attr_name == "color")
+ {
+ CColor color;
+ if (!GUI::ParseString(attr_value.FromUTF8(), color))
+ LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str());
+ else oDef.m_TextColor = color;
+ }
+ else if (attr_name == "id")
+ {
+ oDef.m_Id = "list_"+attr_value;
+ }
+ else if (attr_name == "width")
+ {
+ float width;
+ if (!GUI::ParseString(attr_value.FromUTF8(), width))
+ LOGERROR(L"GUI: Error parsing '%hs' (\"%ls\")", attr_name.c_str(), attr_value.c_str());
+ else
+ {
+ // Check if it's a relative value, and save as decimal if so.
+ if (attr_value.find("%") != std::string::npos)
+ {
+ width = width / 100.f;
+ }
+ oDef.m_Width = width;
+ }
+ }
+ else if (attr_name == "heading")
+ {
+ oDef.m_Heading = attr_value.FromUTF8();
+ }
+
+ }
+
+ m_ObjectsDefs.push_back(oDef);
+
+ AddSetting(GUIST_CGUIList, oDef.m_Id);
+ SetupText();
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void COList::DrawList(const int &selected,
+ const CStr& _sprite,
+ const CStr& _sprite_selected,
+ const CStr& _textcolor)
+{
+ float bz = GetBufferedZ();
+
+ // First call draw on ScrollBarOwner
+ bool scrollbar;
+ GUI::GetSetting(this, "scrollbar", scrollbar);
+
+ if (scrollbar)
+ {
+ // Draw scrollbar
+ IGUIScrollBarOwner::Draw();
+ }
+
+ if (GetGUI())
+ {
+ CRect rect = GetListRect();
+
+ CGUISpriteInstance *sprite=NULL, *sprite_selectarea=NULL;
+ int cell_id;
+ GUI::GetSettingPointer(this, _sprite, sprite);
+ GUI::GetSettingPointer(this, _sprite_selected, sprite_selectarea);
+ GUI::GetSetting(this, "cell_id", cell_id);
+
+ CGUIList *pList;
+ GUI::GetSettingPointer(this, "list_name", pList);
+
+ GetGUI()->DrawSprite(*sprite, cell_id, bz, rect);
+
+ float scroll=0.f;
+ if (scrollbar)
+ {
+ scroll = GetScrollBar(0).GetPos();
+ }
+
+ if (selected != -1)
+ {
+ ENSURE(selected >= 0 && selected+1 < (int)m_ItemsYPositions.size());
+
+ // Get rectangle of selection:
+ CRect rect_sel(rect.left, rect.top + m_ItemsYPositions[selected] - scroll,
+ rect.right, rect.top + m_ItemsYPositions[selected+1] - scroll);
+
+ if (rect_sel.top <= rect.bottom &&
+ rect_sel.bottom >= rect.top)
+ {
+ if (rect_sel.bottom > rect.bottom)
+ rect_sel.bottom = rect.bottom;
+ if (rect_sel.top < rect.top)
+ rect_sel.top = rect.top;
+
+ if (scrollbar)
+ {
+ // Remove any overlapping area of the scrollbar.
+ if (rect_sel.right > GetScrollBar(0).GetOuterRect().left &&
+ rect_sel.right <= GetScrollBar(0).GetOuterRect().right)
+ rect_sel.right = GetScrollBar(0).GetOuterRect().left;
+
+ if (rect_sel.left >= GetScrollBar(0).GetOuterRect().left &&
+ rect_sel.left < GetScrollBar(0).GetOuterRect().right)
+ rect_sel.left = GetScrollBar(0).GetOuterRect().right;
+ }
+
+ GetGUI()->DrawSprite(*sprite_selectarea, cell_id, bz+0.05f, rect_sel);
+ }
+ }
+
+ CColor color;
+ GUI::GetSetting(this, _textcolor, color);
+
+ CGUISpriteInstance *sprite_heading=NULL;
+ GUI::GetSettingPointer(this, "sprite_heading", sprite_heading);
+ CRect rect_head(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.right,
+ m_CachedActualSize.top + m_HeadingHeight);
+ GetGUI()->DrawSprite(*sprite_heading, cell_id, bz, rect_head);
+
+ float xpos = 0;
+ for (unsigned int def=0; def< m_ObjectsDefs.size(); ++def)
+ {
+ DrawText(def, color, m_CachedActualSize.TopLeft() + CPos(xpos, 4), bz+0.1f, rect_head);
+ // Check if it's a decimal value, and if so, assume relative positioning.
+ if (m_ObjectsDefs[def].m_Width < 1 && m_ObjectsDefs[def].m_Width > 0)
+ xpos += m_ObjectsDefs[def].m_Width * m_TotalAvalibleColumnWidth;
+ else
+ xpos += m_ObjectsDefs[def].m_Width;
+ }
+
+ for (int i=0; i<(int)pList->m_Items.size(); ++i)
+ {
+ if (m_ItemsYPositions[i+1] - scroll < 0 ||
+ m_ItemsYPositions[i] - scroll > rect.GetHeight())
+ continue;
+
+ // Clipping area (we'll have to substract the scrollbar)
+ CRect cliparea = GetListRect();
+
+ if (scrollbar)
+ {
+ if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
+ cliparea.right <= GetScrollBar(0).GetOuterRect().right)
+ cliparea.right = GetScrollBar(0).GetOuterRect().left;
+
+ if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
+ cliparea.left < GetScrollBar(0).GetOuterRect().right)
+ cliparea.left = GetScrollBar(0).GetOuterRect().right;
+ }
+
+ xpos = 0;
+ for (unsigned int def=0; def< m_ObjectsDefs.size(); ++def)
+ {
+ DrawText(m_ObjectsDefs.size() * (i+/*Heading*/1) + def, m_ObjectsDefs[def].m_TextColor, rect.TopLeft() + CPos(xpos, -scroll + m_ItemsYPositions[i]), bz+0.1f, cliparea);
+ // Check if it's a decimal value, and if so, assume relative positioning.
+ if (m_ObjectsDefs[def].m_Width < 1 && m_ObjectsDefs[def].m_Width > 0)
+ xpos += m_ObjectsDefs[def].m_Width * m_TotalAvalibleColumnWidth;
+ else
+ xpos += m_ObjectsDefs[def].m_Width;
+ }
+ }
+ }
+}
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-high.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-high.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-high.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-high.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-high.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/source/gui/CList.cpp
===================================================================
--- ps/trunk/source/gui/CList.cpp (revision 14097)
+++ ps/trunk/source/gui/CList.cpp (revision 14098)
@@ -1,548 +1,547 @@
/* 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 .
*/
/*
CList
*/
#include "precompiled.h"
#include "CList.h"
#include "CGUIScrollBarVertical.h"
#include "lib/external_libraries/libsdl.h"
#include "ps/CLogger.h"
#include "soundmanager/ISoundManager.h"
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
CList::CList() :
m_Modified(false)
{
// Add sprite_disabled! TODO
AddSetting(GUIST_float, "buffer_zone");
//AddSetting(GUIST_CGUIString, "caption"); will it break removing this? If I know my system, then no, but test just in case TODO (Gee).
AddSetting(GUIST_CStrW, "font");
AddSetting(GUIST_bool, "scrollbar");
AddSetting(GUIST_CStr, "scrollbar_style");
AddSetting(GUIST_CStrW, "sound_disabled");
AddSetting(GUIST_CStrW, "sound_selected");
AddSetting(GUIST_CGUISpriteInstance, "sprite");
AddSetting(GUIST_CGUISpriteInstance, "sprite_selectarea");
AddSetting(GUIST_int, "cell_id");
AddSetting(GUIST_EAlign, "text_align");
AddSetting(GUIST_CColor, "textcolor");
AddSetting(GUIST_CColor, "textcolor_selected");
AddSetting(GUIST_int, "selected"); // Index selected. -1 is none.
AddSetting(GUIST_CStrW, "tooltip");
AddSetting(GUIST_CStr, "tooltip_style");
// Each list item has both a name (in 'list') and an associated data string (in 'list_data')
AddSetting(GUIST_CGUIList, "list");
AddSetting(GUIST_CGUIList, "list_data"); // TODO: this should be a list of raw strings, not of CGUIStrings
GUI::SetSetting(this, "scrollbar", false);
// Nothing is selected as default.
GUI::SetSetting(this, "selected", -1);
// Add scroll-bar
CGUIScrollBarVertical * bar = new CGUIScrollBarVertical();
bar->SetRightAligned(true);
- bar->SetUseEdgeButtons(true);
AddScrollBar(bar);
}
CList::~CList()
{
}
void CList::SetupText()
{
if (!GetGUI())
return;
m_Modified = true;
CGUIList *pList;
GUI::GetSettingPointer(this, "list", pList);
//ENSURE(m_GeneratedTexts.size()>=1);
m_ItemsYPositions.resize( pList->m_Items.size()+1 );
// Delete all generated texts. Some could probably be saved,
// but this is easier, and this function will never be called
// continuously, or even often, so it'll probably be okay.
std::vector::iterator it;
for (it=m_GeneratedTexts.begin(); it!=m_GeneratedTexts.end(); ++it)
{
if (*it)
delete *it;
}
m_GeneratedTexts.clear();
CStrW font;
if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty())
// Use the default if none is specified
// TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style.
font = L"default";
//CGUIString caption;
bool scrollbar;
//GUI::GetSetting(this, "caption", caption);
GUI::GetSetting(this, "scrollbar", scrollbar);
float width = GetListRect().GetWidth();
// remove scrollbar if applicable
if (scrollbar && GetScrollBar(0).GetStyle())
width -= GetScrollBar(0).GetStyle()->m_Width;
float buffer_zone=0.f;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
// Generate texts
float buffered_y = 0.f;
for (int i=0; i<(int)pList->m_Items.size(); ++i)
{
// Create a new SGUIText. Later on, input it using AddText()
SGUIText *text = new SGUIText();
*text = GetGUI()->GenerateText(pList->m_Items[i], font, width, buffer_zone, this);
m_ItemsYPositions[i] = buffered_y;
buffered_y += text->m_Size.cy;
AddText(text);
}
m_ItemsYPositions[pList->m_Items.size()] = buffered_y;
//if (! scrollbar)
// CalculateTextPosition(m_CachedActualSize, m_TextPos, *m_GeneratedTexts[0]);
// Setup scrollbar
if (scrollbar)
{
GetScrollBar(0).SetScrollRange( m_ItemsYPositions.back() );
GetScrollBar(0).SetScrollSpace( GetListRect().GetHeight() );
CRect rect = GetListRect();
GetScrollBar(0).SetX( rect.right );
GetScrollBar(0).SetY( rect.top );
GetScrollBar(0).SetZ( GetBufferedZ() );
GetScrollBar(0).SetLength( rect.bottom - rect.top );
}
}
void CList::HandleMessage(SGUIMessage &Message)
{
IGUIScrollBarOwner::HandleMessage(Message);
//IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead!
m_Modified = false;
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
if (Message.value == "list")
{
SetupText();
}
// If selected is changed, call "SelectionChange"
if (Message.value == "selected")
{
// TODO: Check range
// TODO only works if lower-case, shouldn't it be made case sensitive instead?
ScriptEvent("selectionchange");
}
if (Message.value == "scrollbar")
{
SetupText();
}
// Update scrollbar
if (Message.value == "scrollbar_style")
{
CStr scrollbar_style;
GUI::GetSetting(this, Message.value, scrollbar_style);
GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
SetupText();
}
break;
case GUIM_MOUSE_PRESS_LEFT:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
{
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_disabled", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
break;
}
bool scrollbar;
CGUIList *pList;
GUI::GetSetting(this, "scrollbar", scrollbar);
GUI::GetSettingPointer(this, "list", pList);
float scroll=0.f;
if (scrollbar)
{
scroll = GetScrollBar(0).GetPos();
}
CRect rect = GetListRect();
CPos mouse = GetMousePos();
mouse.y += scroll;
int set=-1;
for (int i=0; i<(int)pList->m_Items.size(); ++i)
{
if (mouse.y >= rect.top + m_ItemsYPositions[i] &&
mouse.y < rect.top + m_ItemsYPositions[i+1] &&
// mouse is not over scroll-bar
!(mouse.x >= GetScrollBar(0).GetOuterRect().left &&
mouse.x <= GetScrollBar(0).GetOuterRect().right))
{
set = i;
}
}
if (set != -1)
{
GUI::SetSetting(this, "selected", set);
UpdateAutoScroll();
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
} break;
case GUIM_MOUSE_WHEEL_DOWN:
{
GetScrollBar(0).ScrollPlus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
break;
}
case GUIM_MOUSE_WHEEL_UP:
{
GetScrollBar(0).ScrollMinus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
break;
}
case GUIM_LOAD:
{
CStr scrollbar_style;
GUI::GetSetting(this, "scrollbar_style", scrollbar_style);
GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
}
break;
default:
break;
}
IGUITextOwner::HandleMessage(Message);
}
InReaction CList::ManuallyHandleEvent(const SDL_Event_* ev)
{
int szChar = ev->ev.key.keysym.sym;
switch (szChar)
{
case SDLK_HOME:
SelectFirstElement();
UpdateAutoScroll();
break;
case SDLK_END:
SelectLastElement();
UpdateAutoScroll();
break;
case SDLK_UP:
SelectPrevElement();
UpdateAutoScroll();
break;
case SDLK_DOWN:
SelectNextElement();
UpdateAutoScroll();
break;
case SDLK_PAGEUP:
GetScrollBar(0).ScrollMinusPlenty();
break;
case SDLK_PAGEDOWN:
GetScrollBar(0).ScrollPlusPlenty();
break;
default: // Do nothing
return IN_PASS;
}
return IN_HANDLED;
}
void CList::Draw()
{
int selected;
GUI::GetSetting(this, "selected", selected);
DrawList(selected, "sprite", "sprite_selectarea", "textcolor");
}
void CList::DrawList(const int &selected,
const CStr& _sprite,
const CStr& _sprite_selected,
const CStr& _textcolor)
{
float bz = GetBufferedZ();
// First call draw on ScrollBarOwner
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
if (scrollbar)
{
// Draw scrollbar
IGUIScrollBarOwner::Draw();
}
if (GetGUI())
{
CRect rect = GetListRect();
CGUISpriteInstance *sprite=NULL, *sprite_selectarea=NULL;
int cell_id;
GUI::GetSettingPointer(this, _sprite, sprite);
GUI::GetSettingPointer(this, _sprite_selected, sprite_selectarea);
GUI::GetSetting(this, "cell_id", cell_id);
CGUIList *pList;
GUI::GetSettingPointer(this, "list", pList);
GetGUI()->DrawSprite(*sprite, cell_id, bz, rect);
float scroll=0.f;
if (scrollbar)
{
scroll = GetScrollBar(0).GetPos();
}
if (selected != -1)
{
ENSURE(selected >= 0 && selected+1 < (int)m_ItemsYPositions.size());
// Get rectangle of selection:
CRect rect_sel(rect.left, rect.top + m_ItemsYPositions[selected] - scroll,
rect.right, rect.top + m_ItemsYPositions[selected+1] - scroll);
if (rect_sel.top <= rect.bottom &&
rect_sel.bottom >= rect.top)
{
if (rect_sel.bottom > rect.bottom)
rect_sel.bottom = rect.bottom;
if (rect_sel.top < rect.top)
rect_sel.top = rect.top;
if (scrollbar)
{
// Remove any overlapping area of the scrollbar.
if (rect_sel.right > GetScrollBar(0).GetOuterRect().left &&
rect_sel.right <= GetScrollBar(0).GetOuterRect().right)
rect_sel.right = GetScrollBar(0).GetOuterRect().left;
if (rect_sel.left >= GetScrollBar(0).GetOuterRect().left &&
rect_sel.left < GetScrollBar(0).GetOuterRect().right)
rect_sel.left = GetScrollBar(0).GetOuterRect().right;
}
GetGUI()->DrawSprite(*sprite_selectarea, cell_id, bz+0.05f, rect_sel);
}
}
CColor color;
GUI::GetSetting(this, _textcolor, color);
for (int i=0; i<(int)pList->m_Items.size(); ++i)
{
if (m_ItemsYPositions[i+1] - scroll < 0 ||
m_ItemsYPositions[i] - scroll > rect.GetHeight())
continue;
// Clipping area (we'll have to substract the scrollbar)
CRect cliparea = GetListRect();
if (scrollbar)
{
if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
cliparea.right <= GetScrollBar(0).GetOuterRect().right)
cliparea.right = GetScrollBar(0).GetOuterRect().left;
if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
cliparea.left < GetScrollBar(0).GetOuterRect().right)
cliparea.left = GetScrollBar(0).GetOuterRect().right;
}
DrawText(i, color, rect.TopLeft() - CPos(0.f, scroll - m_ItemsYPositions[i]), bz+0.1f, cliparea);
}
}
}
void CList::AddItem(const CStrW& str, const CStrW& data)
{
CGUIList *pList, *pListData;
GUI::GetSettingPointer(this, "list", pList);
GUI::GetSettingPointer(this, "list_data", pListData);
CGUIString gui_string;
gui_string.SetValue(str);
pList->m_Items.push_back( gui_string );
CGUIString data_string;
data_string.SetValue(data);
pListData->m_Items.push_back( data_string );
// TODO Temp
SetupText();
}
bool CList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
{
int elmt_item = pFile->GetElementID("item");
if (child.GetNodeName() == elmt_item)
{
AddItem(child.GetText().FromUTF8(), child.GetText().FromUTF8());
return true;
}
else
{
return false;
}
}
void CList::SelectNextElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
CGUIList *pList;
GUI::GetSettingPointer(this, "list", pList);
if (selected != (int)pList->m_Items.size()-1)
{
++selected;
GUI::SetSetting(this, "selected", selected);
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
}
void CList::SelectPrevElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
if (selected != 0)
{
--selected;
GUI::SetSetting(this, "selected", selected);
CStrW soundPath;
if (g_SoundManager && GUI::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
g_SoundManager->PlayAsUI(soundPath.c_str(), false);
}
}
void CList::SelectFirstElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
if (selected != 0)
{
GUI::SetSetting(this, "selected", 0);
}
}
void CList::SelectLastElement()
{
int selected;
GUI::GetSetting(this, "selected", selected);
CGUIList *pList;
GUI::GetSettingPointer(this, "list", pList);
if (selected != (int)pList->m_Items.size()-1)
{
GUI::SetSetting(this, "selected", (int)pList->m_Items.size()-1);
}
}
void CList::UpdateAutoScroll()
{
int selected;
bool scrollbar;
float scroll;
GUI::GetSetting(this, "selected", selected);
GUI::GetSetting(this, "scrollbar", scrollbar);
CRect rect = GetListRect();
// No scrollbar, no scrolling (at least it's not made to work properly).
if (!scrollbar)
return;
scroll = GetScrollBar(0).GetPos();
// Check upper boundary
if (m_ItemsYPositions[selected] < scroll)
{
GetScrollBar(0).SetPos(m_ItemsYPositions[selected]);
return; // this means, if it wants to align both up and down at the same time
// this will have precedence.
}
// Check lower boundary
if (m_ItemsYPositions[selected+1]-rect.GetHeight() > scroll)
{
GetScrollBar(0).SetPos(m_ItemsYPositions[selected+1]-rect.GetHeight());
}
}
Index: ps/trunk/source/gui/IGUIScrollBar.h
===================================================================
--- ps/trunk/source/gui/IGUIScrollBar.h (revision 14097)
+++ ps/trunk/source/gui/IGUIScrollBar.h (revision 14098)
@@ -1,460 +1,454 @@
/* Copyright (C) 2009 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 .
*/
/*
A GUI ScrollBar
--Overview--
A GUI Scrollbar, this class doesn't present all functionality
to the scrollbar, it just controls the drawing and a wrapper
for interaction with it.
--Usage--
Used in everywhere scrollbars are needed, like in a combobox for instance.
--More info--
Check GUI.h
*/
#ifndef INCLUDED_IGUISCROLLBAR
#define INCLUDED_IGUISCROLLBAR
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "GUI.h"
//--------------------------------------------------------
// Declarations
//--------------------------------------------------------
/**
* The GUI Scroll-bar style. Tells us how scroll-bars look and feel.
*
* A scroll-bar style can choose whether to support horizontal, vertical
* or both.
*
* @see IGUIScrollBar
*/
struct SGUIScrollBarStyle
{
//--------------------------------------------------------
/** @name General Settings */
//--------------------------------------------------------
//@{
/**
* Width of bar, also both sides of the edge buttons.
*/
float m_Width;
/**
* Scrollable with the wheel.
*/
bool m_ScrollWheel;
/**
* How much (in percent, 0.1f = 10%) to scroll each time
* the wheel is admitted, or the buttons are pressed.
*/
float m_ScrollSpeed;
/**
* Whether or not the edge buttons should appear or not. Sometimes
* you actually don't want them, like perhaps in a combo box.
*/
bool m_ScrollButtons;
/**
* Sometimes there is *a lot* to scroll, but to prevent the scroll "bar"
* from being almost invisible (or ugly), you can set a minimum in pixel
* size.
*/
float m_MinimumBarSize;
/**
* Sometimes you would like your scroll bar to have a fixed maximum size
* so that the texture does not get too stretched, you can set a maximum
* in pixels.
*/
float m_MaximumBarSize;
+
+ /**
+ * True if you want edge buttons, i.e. buttons that can be pressed in order
+ * to scroll.
+ */
+ bool m_UseEdgeButtons;
//@}
//--------------------------------------------------------
/** @name Horizontal Sprites */
//--------------------------------------------------------
//@{
CGUISpriteInstance m_SpriteButtonTop;
CGUISpriteInstance m_SpriteButtonTopPressed;
CGUISpriteInstance m_SpriteButtonTopDisabled;
CGUISpriteInstance m_SpriteButtonTopOver;
CGUISpriteInstance m_SpriteButtonBottom;
CGUISpriteInstance m_SpriteButtonBottomPressed;
CGUISpriteInstance m_SpriteButtonBottomDisabled;
CGUISpriteInstance m_SpriteButtonBottomOver;
CGUISpriteInstance m_SpriteBarVertical;
CGUISpriteInstance m_SpriteBarVerticalOver;
CGUISpriteInstance m_SpriteBarVerticalPressed;
CGUISpriteInstance m_SpriteBackVertical;
//@}
//--------------------------------------------------------
/** @name Vertical Sprites */
//--------------------------------------------------------
//@{
CGUISpriteInstance m_SpriteButtonLeft;
CGUISpriteInstance m_SpriteButtonLeftPressed;
CGUISpriteInstance m_SpriteButtonLeftDisabled;
CGUISpriteInstance m_SpriteButtonRight;
CGUISpriteInstance m_SpriteButtonRightPressed;
CGUISpriteInstance m_SpriteButtonRightDisabled;
CGUISpriteInstance m_SpriteBackHorizontal;
CGUISpriteInstance m_SpriteBarHorizontal;
//@}
};
/**
* The GUI Scroll-bar, used everywhere there is a scroll-bar in the game.
*
* To include a scroll-bar to an object, inherent the object from
* IGUIScrollBarOwner and call AddScrollBar() to add the scroll-bars.
*
* It's also important that the scrollbar is located within the parent
* object's mouse over area. Otherwise the input won't be sent to the
* scroll-bar.
*
* The class does not provide all functionality to the scroll-bar, many
* things the parent of the scroll-bar, must provide. Like a combo-box.
*/
class IGUIScrollBar
{
public:
IGUIScrollBar();
virtual ~IGUIScrollBar();
public:
/**
* Draw the scroll-bar
*/
virtual void Draw()=0;
/**
* If an object that contains a scrollbar has got messages, send
* them to the scroll-bar and it will see if the message regarded
* itself.
*
* @see IGUIObject#HandleMessage()
*/
virtual void HandleMessage(SGUIMessage &Message)=0;
/**
* Set m_Pos with g_mouse_x/y input, i.e. when draggin.
*/
virtual void SetPosFromMousePos(const CPos &mouse)=0;
/**
* Hovering the scroll minus button
*
* @param mouse current mouse position
* @return True if mouse positions are hovering the button
*/
virtual bool HoveringButtonMinus(const CPos& UNUSED(mouse)) { return false; }
/**
* Hovering the scroll plus button
*
* @param mouse current mouse position
* @return True if mouse positions are hovering the button
*/
virtual bool HoveringButtonPlus(const CPos& UNUSED(mouse)) { return false; }
/**
* Get scroll-position
*/
float GetPos() const { return m_Pos; }
/**
* Set scroll-position by hand
*/
virtual void SetPos(float f) { m_Pos = f; UpdatePosBoundaries(); }
/**
* Get the value of m_Pos that corresponds to the bottom of the scrollable region
*/
float GetMaxPos() const { return std::max(1.f, m_ScrollRange - m_ScrollSpace); }
/**
* Increase scroll one step
*/
virtual void ScrollPlus() { m_Pos += 30.f; UpdatePosBoundaries(); }
/**
* Decrease scroll one step
*/
virtual void ScrollMinus() { m_Pos -= 30.f; UpdatePosBoundaries(); }
/**
* Increase scroll three steps
*/
virtual void ScrollPlusPlenty() { m_Pos += 90.f; UpdatePosBoundaries(); }
/**
* Decrease scroll three steps
*/
virtual void ScrollMinusPlenty() { m_Pos -= 90.f; UpdatePosBoundaries(); }
/**
* Set host object, must be done almost at creation of scroll bar.
* @param pOwner Pointer to host object.
*/
void SetHostObject(IGUIScrollBarOwner * pOwner) { m_pHostObject = pOwner; }
/**
* Get GUI pointer
* @return CGUI pointer
*/
CGUI *GetGUI() const;
/**
* Set GUI pointer
* @param pGUI pointer to CGUI object.
*/
void SetGUI(CGUI *pGUI) { m_pGUI = pGUI; }
/**
* Set Width
* @param width Width
*/
void SetWidth(float width) { m_Width = width; }
/**
* Set X Position
* @param x Position in this axis
*/
void SetX(float x) { m_X = x; }
/**
* Set Y Position
* @param y Position in this axis
*/
void SetY(float y) { m_Y = y; }
/**
* Set Z Position
* @param z Position in this axis
*/
void SetZ(float z) { m_Z = z; }
/**
* Set Length of scroll bar
* @param length Length
*/
void SetLength(float length) { m_Length = length; }
/**
* Set content length
* @param range Maximum scrollable range
*/
void SetScrollRange(float range) { m_ScrollRange = std::max(range, 1.f); SetupBarSize(); UpdatePosBoundaries(); }
/**
* Set space that is visible in the scrollable control.
* @param space Visible area in the scrollable control.
*/
void SetScrollSpace(float space) { m_ScrollSpace = space; SetupBarSize(); UpdatePosBoundaries(); }
/**
* Set bar pressed
* @param b True if bar is pressed
*/
void SetBarPressed(bool b) { m_BarPressed = b; }
/**
- * Set use edge buttons
- * @param b True if edge buttons should be used
- */
- void SetUseEdgeButtons(bool b) { m_UseEdgeButtons = b; }
-
- /**
* Set Scroll bar style string
* @param style String with scroll bar style reference name
*/
void SetScrollBarStyle(const CStr& style) { m_ScrollBarStyle = style; }
/**
* Get style used by the scrollbar
* @return Scroll bar style struct.
*/
const SGUIScrollBarStyle * GetStyle() const;
/**
* Get the rectangle of the actual BAR. not the whole scroll-bar.
* @return Rectangle, CRect
*/
virtual CRect GetBarRect() const = 0;
/**
* Get the rectangle of the outline of the scrollbar, every component of the
* scroll-bar should be inside this area.
* @return Rectangle, CRect
*/
virtual CRect GetOuterRect() const = 0;
protected:
/**
* Sets up bar size
*/
void SetupBarSize();
/**
* Call every time m_Pos has been updated.
*/
void UpdatePosBoundaries();
protected:
//@}
//--------------------------------------------------------
/** @name Settings */
//--------------------------------------------------------
//@{
/**
- * True if you want edge buttons, i.e. buttons that can be pressed in order
- * to scroll.
- */
- bool m_UseEdgeButtons;
-
- /**
* Width of the scroll bar
*/
float m_Width;
/**
* Absolute X Position
*/
float m_X;
/**
* Absolute Y Position
*/
float m_Y;
/**
* Absolute Z Position
*/
float m_Z;
/**
* Total length of scrollbar, including edge buttons.
*/
float m_Length;
/**
* Content that can be scrolled, in pixels
*/
float m_ScrollRange;
/**
* Content that can be viewed at a time, in pixels
*/
float m_ScrollSpace;
/**
* Use input from the scroll-wheel? True or false.
*/
float m_BarSize;
/**
* Scroll bar style reference name
*/
CStr m_ScrollBarStyle;
/**
* Pointer to scroll bar style used.
*/
SGUIScrollBarStyle *m_pStyle;
/**
* Host object, prerequisite!
*/
IGUIScrollBarOwner *m_pHostObject;
/**
* Reference to CGUI object, these cannot work stand-alone
*/
CGUI *m_pGUI;
/**
* Mouse position when bar was pressed
*/
CPos m_BarPressedAtPos;
//@}
//--------------------------------------------------------
/** @name States */
//--------------------------------------------------------
//@{
/**
* If the bar is currently being pressed and dragged.
*/
bool m_BarPressed;
/**
* Bar being hovered or not
*/
bool m_BarHovered;
/**
* Scroll buttons hovered
*/
bool m_ButtonMinusHovered, m_ButtonPlusHovered;
/**
* Scroll buttons pressed
*/
bool m_ButtonMinusPressed, m_ButtonPlusPressed;
/**
* Position of scroll bar, 0 means scrolled all the way to one side.
* It is measured in pixels, it is up to the host to make it actually
* apply in pixels.
*/
float m_Pos;
/**
* Position from 0.f to 1.f it had when the bar was pressed.
*/
float m_PosWhenPressed;
//@}
};
#endif
Index: ps/trunk/source/lobby/StanzaExtensions.cpp
===================================================================
--- ps/trunk/source/lobby/StanzaExtensions.cpp (nonexistent)
+++ ps/trunk/source/lobby/StanzaExtensions.cpp (revision 14098)
@@ -0,0 +1,175 @@
+/* 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 "StanzaExtensions.h"
+
+/******************************************************
+ * GameReport, fairly generic custom stanza extension used
+ * to report game statistics.
+ */
+GameReport::GameReport( const glooxwrapper::Tag* tag ):StanzaExtension( ExtGameReport )
+{
+ if( !tag || tag->name() != "report" || tag->xmlns() != XMLNS_GAMEREPORT )
+ return;
+ // TODO if we want to handle receiving this stanza extension.
+};
+
+/**
+ * Required by gloox, used to serialize the GameReport into XML for sending.
+ */
+glooxwrapper::Tag* GameReport::tag() const
+{
+ glooxwrapper::Tag* t = glooxwrapper::Tag::allocate( "report" );
+ t->setXmlns( XMLNS_GAMEREPORT );
+
+ std::vector::const_iterator it = m_GameReport.begin();
+ for( ; it != m_GameReport.end(); ++it )
+ t->addChild( (*it)->clone() );
+
+ return t;
+}
+
+/**
+ * Required by gloox, used to find the GameReport element in a recived IQ.
+ */
+const glooxwrapper::string& GameReport::filterString() const
+{
+ static const glooxwrapper::string filter = "/iq/report[@xmlns='" XMLNS_GAMEREPORT "']";
+ return filter;
+}
+
+glooxwrapper::StanzaExtension* GameReport::clone() const
+{
+ GameReport* q = new GameReport();
+ return q;
+}
+
+/******************************************************
+ * BoardListQuery, custom IQ Stanza, used solely to
+ * request and receive leaderboard data from server.
+ */
+BoardListQuery::BoardListQuery( const glooxwrapper::Tag* tag ):StanzaExtension( ExtBoardListQuery )
+{
+ if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_BOARDLIST )
+ return;
+
+ const glooxwrapper::ConstTagList boardTags = tag->findTagList_clone( "query/board" );
+ glooxwrapper::ConstTagList::const_iterator it = boardTags.begin();
+ for ( ; it != boardTags.end(); ++it )
+ m_BoardList.push_back( *it );
+}
+
+/**
+ * Required by gloox, used to find the BoardList element in a recived IQ.
+ */
+const glooxwrapper::string& BoardListQuery::filterString() const
+{
+ static const glooxwrapper::string filter = "/iq/query[@xmlns='" XMLNS_BOARDLIST "']";
+ return filter;
+}
+
+/**
+ * Required by gloox, used to serialize the BoardList request into XML for sending.
+ */
+glooxwrapper::Tag* BoardListQuery::tag() const
+{
+ glooxwrapper::Tag* t = glooxwrapper::Tag::allocate( "query" );
+ t->setXmlns( XMLNS_BOARDLIST );
+
+ std::vector::const_iterator it = m_BoardList.begin();
+ for( ; it != m_BoardList.end(); ++it )
+ t->addChild( (*it)->clone() );
+
+ return t;
+}
+
+glooxwrapper::StanzaExtension* BoardListQuery::clone() const
+{
+ BoardListQuery* q = new BoardListQuery();
+ return q;
+}
+
+BoardListQuery::~BoardListQuery()
+{
+ std::vector::const_iterator it = m_BoardList.begin();
+ for( ; it != m_BoardList.end(); ++it )
+ glooxwrapper::Tag::free(*it);
+ m_BoardList.clear();
+}
+
+/******************************************************
+ * GameListQuery, custom IQ Stanza, used to receive
+ * the listing of games from the server, and register/
+ * unregister/changestate games on the server.
+ */
+GameListQuery::GameListQuery( const glooxwrapper::Tag* tag ):StanzaExtension( ExtGameListQuery )
+{
+ if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_GAMELIST )
+ return;
+
+ const glooxwrapper::Tag* c = tag->findTag_clone( "query/game" );
+ if (c)
+ m_Command = c->cdata();
+ glooxwrapper::Tag::free(c);
+
+ const glooxwrapper::ConstTagList games = tag->findTagList_clone( "query/game" );
+ glooxwrapper::ConstTagList::const_iterator it = games.begin();
+ for ( ; it != games.end(); ++it )
+ m_GameList.push_back( *it );
+}
+
+/**
+ * Required by gloox, used to find the GameList element in a recived IQ.
+ */
+const glooxwrapper::string& GameListQuery::filterString() const
+{
+ static const glooxwrapper::string filter = "/iq/query[@xmlns='" XMLNS_GAMELIST "']";
+ return filter;
+}
+
+/**
+ * Required by gloox, used to serialize the game object into XML for sending.
+ */
+glooxwrapper::Tag* GameListQuery::tag() const
+{
+ glooxwrapper::Tag* t = glooxwrapper::Tag::allocate( "query" );
+ t->setXmlns( XMLNS_GAMELIST );
+
+ // Check for register / unregister command
+ if(!m_Command.empty())
+ t->addChild(glooxwrapper::Tag::allocate("command", m_Command));
+
+ std::vector::const_iterator it = m_GameList.begin();
+ for( ; it != m_GameList.end(); ++it )
+ t->addChild( (*it)->clone() );
+
+ return t;
+}
+
+glooxwrapper::StanzaExtension* GameListQuery::clone() const
+{
+ GameListQuery* q = new GameListQuery();
+ return q;
+}
+
+GameListQuery::~GameListQuery()
+{
+ std::vector::const_iterator it = m_GameList.begin();
+ for( ; it != m_GameList.end(); ++it )
+ glooxwrapper::Tag::free(*it);
+ m_GameList.clear();
+}
Index: ps/trunk/source/lobby/sha.cpp
===================================================================
--- ps/trunk/source/lobby/sha.cpp (nonexistent)
+++ ps/trunk/source/lobby/sha.cpp (revision 14098)
@@ -0,0 +1,347 @@
+/**
+ * FIPS-180-2 compliant SHA-256 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "precompiled.h"
+#include "sha.h"
+#include
+#include
+
+
+#define GET_UINT32(n,b,i) \
+{ \
+ (n) = ( (uint) (b)[(i) ] << 24 ) \
+ | ( (uint) (b)[(i) + 1] << 16 ) \
+ | ( (uint) (b)[(i) + 2] << 8 ) \
+ | ( (uint) (b)[(i) + 3] ); \
+}
+
+#define PUT_UINT32(n,b,i) \
+{ \
+ (b)[(i) ] = (byte) ( ((n) >> 24) & 0xFF ); \
+ (b)[(i) + 1] = (byte) ( ((n) >> 16) & 0xFF ); \
+ (b)[(i) + 2] = (byte) ( ((n) >> 8) & 0xFF ); \
+ (b)[(i) + 3] = (byte) ( ((n) ) & 0xFF ); \
+}
+
+SHA256::SHA256()
+{
+ init();
+}
+void SHA256::init()
+{
+ total[0] = 0;
+ total[1] = 0;
+
+ state[0] = 0x6A09E667;
+ state[1] = 0xBB67AE85;
+ state[2] = 0x3C6EF372;
+ state[3] = 0xA54FF53A;
+ state[4] = 0x510E527F;
+ state[5] = 0x9B05688C;
+ state[6] = 0x1F83D9AB;
+ state[7] = 0x5BE0CD19;
+}
+
+void SHA256::transform(byte (&data)[64])
+{
+ uint temp1, temp2, W[64];
+ uint A, B, C, D, E, F, G, H;
+
+ GET_UINT32( W[0], data, 0 );
+ GET_UINT32( W[1], data, 4 );
+ GET_UINT32( W[2], data, 8 );
+ GET_UINT32( W[3], data, 12 );
+ GET_UINT32( W[4], data, 16 );
+ GET_UINT32( W[5], data, 20 );
+ GET_UINT32( W[6], data, 24 );
+ GET_UINT32( W[7], data, 28 );
+ GET_UINT32( W[8], data, 32 );
+ GET_UINT32( W[9], data, 36 );
+ GET_UINT32( W[10], data, 40 );
+ GET_UINT32( W[11], data, 44 );
+ GET_UINT32( W[12], data, 48 );
+ GET_UINT32( W[13], data, 52 );
+ GET_UINT32( W[14], data, 56 );
+ GET_UINT32( W[15], data, 60 );
+
+#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)
+#define ROTR(x,n) (SHR(x,n) | (x << (32 - n)))
+
+#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3))
+#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10))
+
+#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22))
+#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25))
+
+#define F0(x,y,z) ((x & y) | (z & (x | y)))
+#define F1(x,y,z) (z ^ (x & (y ^ z)))
+
+#define R(t) \
+( \
+ W[t] = S1(W[t - 2]) + W[t - 7] + \
+ S0(W[t - 15]) + W[t - 16] \
+)
+
+#define P(a,b,c,d,e,f,g,h,x,K) \
+{ \
+ temp1 = h + S3(e) + F1(e,f,g) + K + x; \
+ temp2 = S2(a) + F0(a,b,c); \
+ d += temp1; h = temp1 + temp2; \
+}
+
+ A = state[0];
+ B = state[1];
+ C = state[2];
+ D = state[3];
+ E = state[4];
+ F = state[5];
+ G = state[6];
+ H = state[7];
+
+ P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 );
+ P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 );
+ P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF );
+ P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 );
+ P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B );
+ P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 );
+ P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 );
+ P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 );
+ P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 );
+ P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 );
+ P( G, H, A, B, C, D, E, F, W[10], 0x243185BE );
+ P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 );
+ P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 );
+ P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE );
+ P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 );
+ P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 );
+ P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 );
+ P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 );
+ P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 );
+ P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC );
+ P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F );
+ P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA );
+ P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC );
+ P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA );
+ P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 );
+ P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D );
+ P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 );
+ P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 );
+ P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 );
+ P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 );
+ P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 );
+ P( B, C, D, E, F, G, H, A, R(31), 0x14292967 );
+ P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 );
+ P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 );
+ P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC );
+ P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 );
+ P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 );
+ P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB );
+ P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E );
+ P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 );
+ P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 );
+ P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B );
+ P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 );
+ P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 );
+ P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 );
+ P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 );
+ P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 );
+ P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 );
+ P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 );
+ P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 );
+ P( G, H, A, B, C, D, E, F, R(50), 0x2748774C );
+ P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 );
+ P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 );
+ P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A );
+ P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F );
+ P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 );
+ P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE );
+ P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F );
+ P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 );
+ P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 );
+ P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA );
+ P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB );
+ P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 );
+ P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 );
+
+ state[0] += A;
+ state[1] += B;
+ state[2] += C;
+ state[3] += D;
+ state[4] += E;
+ state[5] += F;
+ state[6] += G;
+ state[7] += H;
+}
+
+void SHA256::update(const void* input, uint length )
+{
+ uint left, fill;
+
+ if( ! length ) return;
+
+ left = total[0] & 0x3F;
+ fill = 64 - left;
+
+ total[0] += length;
+ total[0] &= 0xFFFFFFFF;
+
+ if( total[0] < length )
+ total[1]++;
+
+ if( left && length >= fill )
+ {
+ memcpy( (void *) (buffer + left),
+ (void *) input, fill );
+ transform(buffer);
+ length -= fill;
+ input = (byte*)input + fill;
+ left = 0;
+ }
+
+ while( length >= 64 )
+ {
+ transform((byte(&)[64])input);
+ length -= 64;
+ input = (byte*)input + 64;
+ }
+
+ if( length )
+ {
+ memcpy( (void *) (buffer + left),
+ (void *) input, length );
+ }
+}
+
+static byte sha256_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void SHA256::finish(byte (&digest)[32] )
+{
+ uint last, padn;
+ uint high, low;
+ byte msglen[8];
+
+ high = ( total[0] >> 29 )
+ | ( total[1] << 3 );
+ low = ( total[0] << 3 );
+
+ PUT_UINT32( high, msglen, 0 );
+ PUT_UINT32( low, msglen, 4 );
+
+ last = total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ update(sha256_padding, padn);
+ update(msglen, 8);
+
+ PUT_UINT32( state[0], digest, 0 );
+ PUT_UINT32( state[1], digest, 4 );
+ PUT_UINT32( state[2], digest, 8 );
+ PUT_UINT32( state[3], digest, 12 );
+ PUT_UINT32( state[4], digest, 16 );
+ PUT_UINT32( state[5], digest, 20 );
+ PUT_UINT32( state[6], digest, 24 );
+ PUT_UINT32( state[7], digest, 28 );
+}
+
+
+/**
+ * From BSD's PBKDF implementation:
+ */
+static void hmac_sha256(byte (&digest)[SHA_DIGEST_SIZE],
+ const byte* text, size_t text_len, const byte* key, size_t key_len)
+{
+ SHA256 hash;
+ byte tk[SHA_DIGEST_SIZE]; // temporary key incase we need to pad the key with zero bytes
+ if (key_len > SHA_DIGEST_SIZE)
+ {
+ hash.update(key, key_len);
+ hash.finish(tk);
+ key = tk;
+ key_len = SHA_DIGEST_SIZE;
+ }
+
+ byte k_pad[SHA_DIGEST_SIZE];
+
+ memset(k_pad, 0, sizeof k_pad);
+ memcpy(k_pad, key, key_len);
+ for (int i = 0; i < SHA_DIGEST_SIZE; ++i)
+ k_pad[i] ^= 0x36;
+ hash.init();
+ hash.update(k_pad, SHA_DIGEST_SIZE);
+ hash.update(text, text_len);
+ hash.finish(digest);
+
+
+ memset(k_pad, 0, sizeof k_pad);
+ memcpy(k_pad, key, key_len);
+ for (int i = 0; i < SHA_DIGEST_SIZE; ++i)
+ k_pad[i] ^= 0x5c;
+
+ hash.init();
+ hash.update(k_pad, SHA_DIGEST_SIZE);
+ hash.update(digest, SHA_DIGEST_SIZE);
+ hash.finish(digest);
+}
+
+
+int pbkdf2(byte (&output)[SHA_DIGEST_SIZE],
+ const byte* key, size_t key_len,
+ const byte* salt, size_t salt_len,
+ unsigned rounds)
+{
+ byte asalt[SHA_DIGEST_SIZE + 4], obuf[SHA_DIGEST_SIZE], d1[SHA_DIGEST_SIZE], d2[SHA_DIGEST_SIZE];
+
+ if (rounds < 1 || key_len == 0 || salt_len == 0)
+ return -1;
+
+ if (salt_len > SHA_DIGEST_SIZE) salt_len = SHA_DIGEST_SIZE; // length cap for the salt
+ memset(asalt, 0, salt_len);
+ memcpy(asalt, salt, salt_len);
+
+ for (unsigned count = 1; ; ++count)
+ {
+ asalt[salt_len + 0] = (count >> 24) & 0xff;
+ asalt[salt_len + 1] = (count >> 16) & 0xff;
+ asalt[salt_len + 2] = (count >> 8) & 0xff;
+ asalt[salt_len + 3] = count & 0xff;
+ hmac_sha256(d1, asalt, salt_len + 4, key, key_len);
+ memcpy(obuf, d1, SHA_DIGEST_SIZE);
+
+ for (unsigned i = 1; i < rounds; i++)
+ {
+ hmac_sha256(d2, d1, SHA_DIGEST_SIZE, key, key_len);
+ memcpy(d1, d2, SHA_DIGEST_SIZE);
+ for (unsigned j = 0; j < SHA_DIGEST_SIZE; j++)
+ obuf[j] ^= d1[j];
+ }
+
+ memcpy(output, obuf, SHA_DIGEST_SIZE);
+ key += SHA_DIGEST_SIZE;
+ if (key_len < SHA_DIGEST_SIZE)
+ break;
+ key_len -= SHA_DIGEST_SIZE;
+ };
+ return 0;
+}
Index: ps/trunk/source/gui/IGUIScrollBar.cpp
===================================================================
--- ps/trunk/source/gui/IGUIScrollBar.cpp (revision 14097)
+++ ps/trunk/source/gui/IGUIScrollBar.cpp (revision 14098)
@@ -1,184 +1,184 @@
/* Copyright (C) 2009 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 .
*/
/*
IGUIScrollBar
*/
#include "precompiled.h"
#include "GUI.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
//-------------------------------------------------------------------
// IGUIScrollBar
//-------------------------------------------------------------------
IGUIScrollBar::IGUIScrollBar() : m_pStyle(NULL), m_pGUI(NULL),
m_X(300.f), m_Y(300.f),
m_ScrollRange(1.f), m_ScrollSpace(0.f), // MaxPos: not 0, due to division.
m_Length(200.f), m_Width(20.f),
m_BarSize(0.f), m_Pos(0.f),
- m_UseEdgeButtons(true),
m_ButtonPlusPressed(false),
m_ButtonMinusPressed(false),
m_ButtonPlusHovered(false),
m_ButtonMinusHovered(false),
m_BarHovered(false),
m_BarPressed(false)
{
}
IGUIScrollBar::~IGUIScrollBar()
{
}
void IGUIScrollBar::SetupBarSize()
{
+ if (!GetStyle())
+ {
+ return;
+ }
float min = GetStyle()->m_MinimumBarSize;
float max = GetStyle()->m_MaximumBarSize;
float length = m_Length;
// Check for edge buttons
- if (m_UseEdgeButtons)
+ if (GetStyle()->m_UseEdgeButtons)
length -= GetStyle()->m_Width * 2.f;
// Check min and max are valid
if (min > length)
- {
- LOGWARNING(L"Minimum scrollbar size of %g is larger than the total scrollbar size of %g", min, length);
min = 0.f;
- }
if (max < min)
max = length;
// Clamp size to not exceed a minimum or maximum.
m_BarSize = clamp(length * std::min((float)m_ScrollSpace / (float)m_ScrollRange, 1.f), min, max);
}
const SGUIScrollBarStyle *IGUIScrollBar::GetStyle() const
{
if (!m_pHostObject)
return NULL;
return m_pHostObject->GetScrollBarStyle(m_ScrollBarStyle);
}
CGUI *IGUIScrollBar::GetGUI() const
{
if (!m_pHostObject)
return NULL;
return m_pHostObject->GetGUI();
}
void IGUIScrollBar::UpdatePosBoundaries()
{
if (m_Pos < 0.f ||
m_ScrollRange < m_ScrollSpace) // <= scrolling not applicable
m_Pos = 0.f;
else
if (m_Pos > GetMaxPos())
m_Pos = GetMaxPos();
}
void IGUIScrollBar::HandleMessage(SGUIMessage &Message)
{
switch (Message.type)
{
case GUIM_MOUSE_MOTION:
{
// TODO Gee: Optimizations needed!
CPos mouse = m_pHostObject->GetMousePos();
// If bar is being dragged
if (m_BarPressed)
{
SetPosFromMousePos(mouse);
UpdatePosBoundaries();
}
// check if components are being hovered
m_BarHovered = GetBarRect().PointInside(mouse);
m_ButtonMinusHovered = HoveringButtonMinus(mouse);
m_ButtonPlusHovered = HoveringButtonPlus(mouse);
if (!m_ButtonMinusHovered)
m_ButtonMinusPressed = false;
if (!m_ButtonPlusHovered)
m_ButtonPlusPressed = false;
} break;
case GUIM_MOUSE_PRESS_LEFT:
{
if (!m_pHostObject)
break;
CPos mouse = m_pHostObject->GetMousePos();
// if bar is pressed
if (GetBarRect().PointInside(mouse))
{
m_BarPressed = true;
m_BarPressedAtPos = mouse;
m_PosWhenPressed = m_Pos;
}
else
// if button-minus is pressed
if (m_ButtonMinusHovered)
{
m_ButtonMinusPressed = true;
ScrollMinus();
}
else
// if button-plus is pressed
if (m_ButtonPlusHovered)
{
m_ButtonPlusPressed = true;
ScrollPlus();
}
else
// Pressing the background of the bar, to scroll
// notice the if-sentence alone does not admit that,
// it must be after the above if/elses
{
if (GetOuterRect().PointInside(mouse))
{
// Scroll plus or minus a lot, this might change, it doesn't
// have to be fancy though.
if (mouse.y < GetBarRect().top)
ScrollMinusPlenty();
else
ScrollPlusPlenty();
// Simulate mouse movement to see if bar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
}
}
} break;
case GUIM_MOUSE_RELEASE_LEFT:
m_ButtonMinusPressed = false;
m_ButtonPlusPressed = false;
break;
default:
break;
}
}
Index: ps/trunk/source/lobby/IXmppClient.h
===================================================================
--- ps/trunk/source/lobby/IXmppClient.h (nonexistent)
+++ ps/trunk/source/lobby/IXmppClient.h (revision 14098)
@@ -0,0 +1,60 @@
+/* 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 IXMPPCLIENT_H
+#define IXMPPCLIENT_H
+
+class ScriptInterface;
+class CScriptVal;
+class CScriptValRooted;
+
+class IXmppClient
+{
+public:
+ static IXmppClient* create(ScriptInterface& scriptInterface, const std::string& sUsername, const std::string& sPassword, const std::string& sRoom, const std::string& sNick, bool regOpt = false);
+ virtual ~IXmppClient() {}
+
+ virtual void connect() = 0;
+ virtual void disconnect() = 0;
+ virtual void recv() = 0;
+ virtual void SendIqGetGameList() = 0;
+ virtual void SendIqGetBoardList() = 0;
+ virtual void SendIqGameReport(CScriptVal data) = 0;
+ virtual void SendIqRegisterGame(CScriptVal data) = 0;
+ virtual void SendIqUnregisterGame() = 0;
+ virtual void SendIqChangeStateGame(const std::string& nbp, const std::string& players) = 0;
+ virtual void SetNick(const std::string& nick) = 0;
+ virtual void GetNick(std::string& nick) = 0;
+ virtual void kick(const std::string& nick, const std::string& reason) = 0;
+ virtual void ban(const std::string& nick, const std::string& reason) = 0;
+ virtual void SetPresence(const std::string& presence) = 0;
+ virtual void GetPresence(const std::string& nickname, std::string& presence) = 0;
+
+ virtual CScriptValRooted GUIGetPlayerList() = 0;
+ virtual CScriptValRooted GUIGetGameList() = 0;
+ virtual CScriptValRooted GUIGetBoardList() = 0;
+
+ virtual ScriptInterface& GetScriptInterface() = 0;
+
+ virtual CScriptValRooted GuiPollMessage() = 0;
+ virtual void SendMUCMessage(const std::string& message) = 0;
+};
+
+extern IXmppClient *g_XmppClient;
+extern bool g_rankedGame;
+
+#endif // XMPPCLIENT_H
Index: ps/trunk/source/lobby/XmppClient.h
===================================================================
--- ps/trunk/source/lobby/XmppClient.h (nonexistent)
+++ ps/trunk/source/lobby/XmppClient.h (revision 14098)
@@ -0,0 +1,140 @@
+/* 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 XXXMPPCLIENT_H
+#define XXXMPPCLIENT_H
+
+#include "IXmppClient.h"
+
+#include "glooxwrapper/glooxwrapper.h"
+
+//game - script
+#include
+#include "scriptinterface/ScriptVal.h"
+
+//Game - script
+class ScriptInterface;
+
+namespace glooxwrapper
+{
+ class Client;
+ struct CertInfo;
+}
+
+class XmppClient : public IXmppClient, public glooxwrapper::ConnectionListener, public glooxwrapper::MUCRoomHandler, public glooxwrapper::IqHandler, public glooxwrapper::RegistrationHandler, public glooxwrapper::MessageHandler
+{
+ NONCOPYABLE(XmppClient);
+private:
+ //Game - script
+ ScriptInterface& m_ScriptInterface;
+ //Components
+ glooxwrapper::Client* m_client;
+ glooxwrapper::MUCRoom* m_mucRoom;
+ glooxwrapper::Registration* m_registration;
+ //Account infos
+ std::string m_username;
+ std::string m_password;
+ std::string m_nick;
+ std::string m_xpartamuppId;
+
+public:
+ //Basic
+ XmppClient(ScriptInterface& scriptInterface, const std::string& sUsername, const std::string& sPassword, const std::string& sRoom, const std::string& sNick, bool regOpt = false);
+ virtual ~XmppClient();
+
+ //Network
+ void connect();
+ void disconnect();
+ void recv();
+ void SendIqGetGameList();
+ void SendIqGetBoardList();
+ void SendIqGameReport(CScriptVal data);
+ void SendIqRegisterGame(CScriptVal data);
+ void SendIqUnregisterGame();
+ void SendIqChangeStateGame(const std::string& nbp, const std::string& players);
+ void SetNick(const std::string& nick);
+ void GetNick(std::string& nick);
+ void kick(const std::string& nick, const std::string& reason);
+ void ban(const std::string& nick, const std::string& reason);
+ void SetPresence(const std::string& presence);
+ void GetPresence(const std::string& nickname, std::string& presence);
+
+ CScriptValRooted GUIGetPlayerList();
+ CScriptValRooted GUIGetGameList();
+ CScriptValRooted GUIGetBoardList();
+
+ //Script
+ ScriptInterface& GetScriptInterface();
+
+protected:
+ /* Xmpp handlers */
+ /* MUC handlers */
+ virtual void handleMUCParticipantPresence(glooxwrapper::MUCRoom*, const glooxwrapper::MUCRoomParticipant, const glooxwrapper::Presence&);
+ virtual void handleMUCError(glooxwrapper::MUCRoom*, gloox::StanzaError);
+ virtual void handleMUCMessage(glooxwrapper::MUCRoom* room, const glooxwrapper::Message& msg, bool priv);
+ /* MUC handlers not supported by glooxwrapper */
+ // virtual bool handleMUCRoomCreation(glooxwrapper::MUCRoom*) {return false;}
+ // virtual void handleMUCSubject(glooxwrapper::MUCRoom*, const std::string&, const std::string&) {}
+ // virtual void handleMUCInviteDecline(glooxwrapper::MUCRoom*, const glooxwrapper::JID&, const std::string&) {}
+ // virtual void handleMUCInfo(glooxwrapper::MUCRoom*, int, const std::string&, const glooxwrapper::DataForm*) {}
+ // virtual void handleMUCItems(glooxwrapper::MUCRoom*, const std::list >&) {}
+
+ /* Log handler */
+ virtual void handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message);
+
+ /* ConnectionListener handlers*/
+ virtual void onConnect();
+ virtual void onDisconnect(gloox::ConnectionError e);
+ virtual bool onTLSConnect(const glooxwrapper::CertInfo& info);
+
+ /* Iq Handlers */
+ virtual bool handleIq(const glooxwrapper::IQ& iq);
+ virtual void handleIqID(const glooxwrapper::IQ&, int) {}
+
+ /* Registration Handlers */
+ virtual void handleRegistrationFields(const glooxwrapper::JID& /*from*/, int fields, glooxwrapper::string instructions );
+ virtual void handleRegistrationResult(const glooxwrapper::JID& /*from*/, gloox::RegistrationResult result);
+ virtual void handleAlreadyRegistered(const glooxwrapper::JID& /*from*/);
+ virtual void handleDataForm(const glooxwrapper::JID& /*from*/, const glooxwrapper::DataForm& /*form*/);
+ virtual void handleOOB(const glooxwrapper::JID& /*from*/, const glooxwrapper::OOB& oob);
+
+ /* Message Handler */
+ virtual void handleMessage(const glooxwrapper::Message& msg, glooxwrapper::MessageSession * session);
+
+ // Helpers
+ void GetPresenceString(const gloox::Presence::PresenceType p, std::string& presence) const;
+ std::string StanzaErrorToString(gloox::StanzaError err);
+public:
+ /* Messages */
+ CScriptValRooted GuiPollMessage();
+ void SendMUCMessage(const std::string& message);
+ protected:
+ void PushGuiMessage(const CScriptValRooted& message);
+ void CreateSimpleMessage(const std::string& type, const std::string& text, const std::string& level = "standard", const std::string& data = "");
+
+private:
+ /// Map of players
+ std::map m_PlayerMap;
+ /// List of games
+ std::vector m_GameList;
+ /// List of rankings
+ std::vector m_BoardList;
+ /// Queue of messages
+ std::deque m_GuiMessageQueue;
+};
+
+#endif // XMPPCLIENT_H
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-low.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-low.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-low.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-low.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/shadow-low.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-left.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-left.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-left.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-left.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-left.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-middle.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-middle.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-middle.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-middle.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/titlebar-middle.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/white-separator.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/white-separator.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/white-separator.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/white-separator.png (revision 14098)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/global/modern/white-separator.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/common/common_styles.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/common_styles.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/common/common_styles.xml (revision 14098)
@@ -1,298 +1,307 @@
+
+
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js (revision 14098)
@@ -1,292 +1,313 @@
/*
DESCRIPTION : Generic utility functions.
- NOTES :
+ NOTES :
*/
// ====================================================================
function getRandom(randomMin, randomMax)
{
// Returns a random whole number in a min..max range.
// NOTE: There should probably be an engine function for this,
// since we'd need to keep track of random seeds for replays.
- var randomNum = randomMin + (randomMax-randomMin)*Math.random(); // num is random, from A to B
+ var randomNum = randomMin + (randomMax-randomMin)*Math.random(); // num is random, from A to B
return Math.round(randomNum);
}
// ====================================================================
// Get list of XML files in pathname with recursion, excepting those starting with _
function getXMLFileList(pathname)
{
var files = buildDirEntList(pathname, "*.xml", true);
var result = [];
-
+
// Get only subpath from filename and discard extension
for (var i = 0; i < files.length; ++i)
{
var file = files[i];
file = file.substring(pathname.length, file.length-4);
// Split path into directories so we can check for beginning _ character
var tokens = file.split("/");
-
+
if (tokens[tokens.length-1][0] != "_")
result.push(file);
}
return result;
}
// ====================================================================
// Get list of JSON files in pathname
function getJSONFileList(pathname)
{
var files = buildDirEntList(pathname, "*.json", false);
- // Remove the path and extension from each name, since we just want the filename
+ // Remove the path and extension from each name, since we just want the filename
files = [ n.substring(pathname.length, n.length-5) for each (n in files) ];
return files;
}
// ====================================================================
// Parse JSON data
function parseJSONData(pathname)
{
var data = {};
-
+
var rawData = readFile(pathname);
if (!rawData)
{
error("Failed to read file: "+pathname);
}
else
{
try
{ // Catch nasty errors from JSON parsing
// TODO: Need more info from the parser on why it failed: line number, position, etc!
data = JSON.parse(rawData);
if (!data)
error("Failed to parse JSON data in: "+pathname+" (check for valid JSON data)");
-
-
+
+
}
catch(err)
{
error(err.toString()+": parsing JSON data in "+pathname);
}
}
-
+
return data;
}
// ====================================================================
// A sorting function for arrays of objects with 'name' properties, ignoring case
function sortNameIgnoreCase(x, y)
{
var lowerX = x.name.toLowerCase();
var lowerY = y.name.toLowerCase();
-
+
if (lowerX < lowerY)
return -1;
else if (lowerX > lowerY)
return 1;
else
return 0;
}
// ====================================================================
// Escape text tags and whitespace, so users can't use special formatting in their chats
// Limit string length to 256 characters
function escapeText(text)
{
if (!text)
return text;
-
+
var out = text.replace(/[\[\]]+/g,"");
out = out.replace(/\s+/g, " ");
-
+
return out.substr(0, 255);
-
+
}
// ====================================================================
function toTitleCase (string)
{
if (!string)
return string;
// Returns the title-case version of a given string.
string = string.toString();
string = string[0].toUpperCase() + string.substring(1).toLowerCase();
-
+
return string;
}
// ====================================================================
// Parse and return JSON data from file in simulation/data/*
// returns valid object or undefined on error
function parseJSONFromDataFile(filename)
{
var path = "simulation/data/"+filename;
var rawData = readFile(path);
if (!rawData)
error("Failed to read file: "+path);
try
{
// Catch nasty errors from JSON parsing
// TODO: Need more info from the parser on why it failed: line number, position, etc!
var data = JSON.parse(rawData);
return data;
}
catch(err)
{
error(err.toString()+": parsing JSON data in "+path);
}
return undefined;
}
// ====================================================================
// Load default player data, for when it's not otherwise specified
function initPlayerDefaults()
{
var defaults = [];
var data = parseJSONFromDataFile("player_defaults.json");
if (!data || !data.PlayerData)
error("Failed to parse player defaults in player_defaults.json (check for valid JSON data)");
else
defaults = data.PlayerData;
return defaults;
}
// ====================================================================
// Load map size data
function initMapSizes()
{
var sizes = {
+ "shortNames":[],
"names":[],
"tiles": [],
"default": 0
};
var data = parseJSONFromDataFile("map_sizes.json");
if (!data || !data.Sizes)
error("Failed to parse map sizes in map_sizes.json (check for valid JSON data)");
else
{
for (var i = 0; i < data.Sizes.length; ++i)
{
+ sizes.shortNames.push(data.Sizes[i].Name);
sizes.names.push(data.Sizes[i].LongName);
sizes.tiles.push(data.Sizes[i].Tiles);
if (data.Sizes[i].Default)
sizes["default"] = i;
}
}
return sizes;
}
// ====================================================================
// Load game speed data
function initGameSpeeds()
{
var gameSpeeds = {
"names": [],
"speeds": [],
"default": 0
};
var data = parseJSONFromDataFile("game_speeds.json");
if (!data || !data.Speeds)
error("Failed to parse game speeds in game_speeds.json (check for valid JSON data)");
else
{
for (var i = 0; i < data.Speeds.length; ++i)
{
gameSpeeds.names.push(data.Speeds[i].Name);
gameSpeeds.speeds.push(data.Speeds[i].Speed);
if (data.Speeds[i].Default)
gameSpeeds["default"] = i;
}
}
return gameSpeeds;
}
// ====================================================================
// Convert integer color values to string (for use in GUI objects)
function iColorToString(color)
{
var string = "0 0 0";
if (color && ("r" in color) && ("g" in color) && ("b" in color))
string = color.r + " " + color.g + " " + color.b;
-
+
return string;
}
// ====================================================================
/**
* Convert time in milliseconds to hh:mm:ss string representation.
* @param time Time period in milliseconds (integer)
* @return String representing time period
*/
function timeToString(time)
{
var hours = Math.floor(time / 1000 / 60 / 60);
var minutes = Math.floor(time / 1000 / 60) % 60;
var seconds = Math.floor(time / 1000) % 60;
return hours + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
}
// ====================================================================
function removeDupes(array)
{
for (var i = 0; i < array.length; i++)
{
if (array.indexOf(array[i]) < i)
{
array.splice(i, 1);
i--;
}
}
}
// ====================================================================
// "Inside-out" implementation of Fisher-Yates shuffle
function shuffleArray(source)
{
if (!source.length)
return [];
var result = [source[0]];
for (var i = 1; i < source.length; ++i)
{
var j = Math.floor(Math.random() * i);
result[i] = result[j];
result[j] = source[i];
}
return result;
-}
\ No newline at end of file
+}
+
+// ====================================================================
+// Filter out conflicting characters and limit the length of a given name.
+// @param name Name to be filtered.
+// @param stripUnicode Whether or not to remove unicode characters.
+// @param stripSpaces Whether or not to remove whitespace.
+function sanitizePlayerName(name, stripUnicode, stripSpaces)
+{
+ // We delete the '[', ']' characters (GUI tags) and delete the ',' characters (player name separators) by default.
+ var sanitizedName = name.replace(/[\[\],]/g, "");
+ // Optionally strip unicode
+ if (stripUnicode)
+ sanitizedName = sanitizedName.replace(/[^\x20-\x7f]/g, "");
+ // Optionally strip whitespace
+ if (stripSpaces)
+ sanitizedName = sanitizedName.replace(/\s/g, "");
+ // Limit the length to 20 characters
+ return sanitizedName.substr(0,20);
+}
Index: ps/trunk/binaries/data/mods/public/gui/common/modern/setup.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/modern/setup.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/common/modern/setup.xml (revision 14098)
@@ -0,0 +1,16 @@
+
+
+
+
+ ==========================================
+ - SCROLLBARS -
+ ==========================================
+
+
Index: ps/trunk/binaries/data/mods/public/gui/common/modern/sprites.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/modern/sprites.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/common/modern/sprites.xml (revision 14098)
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/common/modern/styles.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/modern/styles.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/common/modern/styles.xml (revision 14098)
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/common/setup.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/setup.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/common/setup.xml (revision 14098)
@@ -1,242 +1,242 @@
0 0 0255 255 255255 0 00 0 255255 255 0237 227 167243 242 240143 142 14043 42 400 200 0191 191 2159 98 24255 165 080 0 030 20 100 0 0 0
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 14098)
@@ -1,1479 +1,1557 @@
////////////////////////////////////////////////////////////////////////////////////////////////
// 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;
+//Server name, if user is a server, connected to the multiplayer lobby
+var g_ServerName;
+
// 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);
}
+
+ if (attribs.serverName)
+ g_ServerName = attribs.serverName;
+
+ // Init the Cancel Button caption and tooltip
+ var cancelButton = getGUIObjectByName("cancelGame");
+ if(!Engine.HasXmppClient())
+ {
+ cancelButton.caption = "Main menu";
+ cancelButton.tooltip = "Return to the main menu."
+ }
+ else
+ {
+ cancelButton.caption = "Quit";
+ cancelButton.tooltip = "Return to the lobby."
+ }
+
}
// 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 = ["Skirmish","Random","Scenario"];
mapTypes.list_data = ["skirmish","random","scenario"];
// 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;
+ // Create a unique ID for this match, to be used for identifying the same game reports
+ // for the lobby.
+ g_GameAttributes.matchID = Engine.GetMatchID();
+
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();
};
+ mapSize.selected = 0;
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();
+ cancelSetup();
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();
+
+ if (g_IsController)
+ sendRegisterGameStanza();
break;
case "start":
+ if (g_IsController)
+ {
+ var players = [ assignment.name for each (assignment in g_PlayerAssignments) ]
+ Engine.SendChangeStateGame(Object.keys(g_PlayerAssignments).length, players.join(", "));
+ }
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": g_GameAttributes,
"isNetworked" : g_IsNetworked,
- "playerAssignments": g_PlayerAssignments
+ "playerAssignments": g_PlayerAssignments,
+ "isController": g_IsController
});
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)
- ];
+ 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");
+ // 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 = 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 (!g_MapData[name])
{
switch (g_GameAttributes.mapType)
{
case "scenario":
case "skirmish":
g_MapData[name] = Engine.LoadMapSettings(name);
break;
case "random":
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();
+
+ // Set player presence
+ Engine.LobbySetPlayerPresence("available");
+
+ if(g_IsController)
+ {
+ // Unregister the game
+ Engine.SendUnregisterGame();
+ }
}
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.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 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)
- {
+ for (var i = 0; i < numPlayers; ++i)
+ {
civs = allcivs[Math.floor(Math.random()*allcivs.length)];
-
- if (!g_GameAttributes.settings.PlayerData[i].Civ || g_GameAttributes.settings.PlayerData[i].Civ == "random")
- g_GameAttributes.settings.PlayerData[i].Civ = civs[Math.floor(Math.random()*civs.length)];
+
+ if (!g_GameAttributes.settings.PlayerData[i].Civ || 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
+ else
var chosenName = civAINames[Math.floor(Math.random() * civAINames.length)];
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
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;
-
+
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;
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;
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 != "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);
+ if (g_IsController && g_LoadingState >= 2)
+ sendRegisterGameStanza();
+ }
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 (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
+ else if (g_IsController && g_GameAttributes.settings.PlayerData[playerSlot].AI && g_GameAttributes.settings.PlayerData[playerSlot].AI != "")
{
- // 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);
- }
+ 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;
}
+
+function sendRegisterGameStanza()
+{
+ var selectedMapSize = getGUIObjectByName("mapSize").selected;
+ var selectedVictoryCondition = getGUIObjectByName("victoryCondition").selected;
+
+ // Map sizes only apply to random maps.
+ if (g_GameAttributes.mapType == "random")
+ var mapSize = getGUIObjectByName("mapSize").list[selectedMapSize];
+ else
+ var mapSize = "Default";
+
+ var victoryCondition = getGUIObjectByName("victoryCondition").list[selectedVictoryCondition];
+ var numberOfPlayers = Object.keys(g_PlayerAssignments).length;
+ var players = [ assignment.name for each (assignment in g_PlayerAssignments) ].join(", ");
+
+ var nbp = numberOfPlayers ? numberOfPlayers : 1;
+ var tnbp = g_GameAttributes.settings.PlayerData.length;
+
+ gameData = {
+ "name":g_ServerName,
+ "mapName":g_GameAttributes.map,
+ "niceMapName":getMapDisplayName(g_GameAttributes.map),
+ "mapSize":mapSize,
+ "mapType":g_GameAttributes.mapType,
+ "victoryCondition":victoryCondition,
+ "nbp":nbp,
+ "tnbp":tnbp,
+ "players":players
+ };
+ Engine.SendRegisterGame(gameData);
+}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 14098)
@@ -1,320 +1,316 @@
Match Setup
Loading
Loading map data. Please wait...
onTick();
Number of players:selectNumPlayers(this.list_data[this.selected]);Player NamePlayer PlacementCivilizationTeamSettingsMatch Type:Map Filter:Select Map:Map Size:selectMapType(this.list_data[this.selected]);selectMapFilter(this.list[this.selected]);selectMap(this.list_data[this.selected]);
-
-
-
-
+
+
+
-
+
-
+
More Options
toggleMoreOptions();
-
+
More Options
-
+
Game Speed:Victory Condition:
-
+
Population Cap:
-
+
Starting Resources:
-
+
Reveal Map:Teams Locked:Cheats:
-
+
OK
toggleMoreOptions();
-
submitChatInput();
Send
submitChatInput();[Tooltip text]
-
+
Start game!
launchGame();
- Main menu
-
-
-
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js (revision 14098)
@@ -1,187 +1,226 @@
var g_IsConnecting = false;
var g_GameType; // "server" or "client"
+var g_ServerName = "";
var g_IsRejoining = false;
var g_GameAttributes; // used when rejoining
var g_PlayerAssignments; // used when rejoining
-function init(multiplayerGameType)
+function init(attribs)
{
- switch (multiplayerGameType)
+ switch (attribs.multiplayerGameType)
{
case "join":
- getGUIObjectByName("pageJoin").hidden = false;
- getGUIObjectByName("pageHost").hidden = true;
+ if(Engine.HasXmppClient())
+ {
+ if (startJoin(attribs.name, attribs.ip))
+ switchSetupPage("pageJoin", "pageConnecting");
+ }
+ else
+ {
+ getGUIObjectByName("pageJoin").hidden = false;
+ getGUIObjectByName("pageHost").hidden = true;
+ }
break;
case "host":
getGUIObjectByName("pageJoin").hidden = true;
getGUIObjectByName("pageHost").hidden = false;
+ if(Engine.HasXmppClient())
+ {
+ getGUIObjectByName("hostServerNameWrapper").hidden = false;
+ getGUIObjectByName("hostPlayerName").caption = attribs.name;
+ getGUIObjectByName("hostServerName").caption = attribs.name + "'s game";
+ }
+ else
+ getGUIObjectByName("hostPlayerNameWrapper").hidden = false;
break;
default:
- error("Unrecognised multiplayer game type : " + multiplayerGameType);
+ error("Unrecognised multiplayer game type : " + attribs.multiplayerGameType);
break;
}
}
function cancelSetup()
{
if (g_IsConnecting)
Engine.DisconnectNetworkGame();
- Engine.PopGuiPage();
+ // Set player lobby presence
+ Engine.LobbySetPlayerPresence("available");
+ Engine.PopGuiPage();
}
function startConnectionStatus(type)
{
g_GameType = type;
g_IsConnecting = true;
g_IsRejoining = false;
getGUIObjectByName("connectionStatus").caption = "Connecting to server...";
}
function onTick()
{
if (!g_IsConnecting)
return;
+ pollAndHandleNetworkClient();
+}
+
+function pollAndHandleNetworkClient()
+{
while (true)
{
var message = Engine.PollNetworkClient();
if (!message)
break;
log("Net message: "+uneval(message));
// If we're rejoining an active game, we don't want to actually display
// the game setup screen, so perform similar processing to gamesetup.js
// in this screen
if (g_IsRejoining)
{
switch (message.type)
{
case "netstatus":
switch (message.status)
{
case "disconnected":
cancelSetup();
reportDisconnect(message.reason);
return;
default:
error("Unrecognised netstatus type "+message.status);
break;
}
break;
case "gamesetup":
g_GameAttributes = message.data;
break;
case "players":
g_PlayerAssignments = message.hosts;
break;
case "start":
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": g_GameAttributes,
"isNetworked" : true,
"playerAssignments": g_PlayerAssignments
});
break;
case "chat":
// Ignore, since we have nowhere to display chat messages
break;
default:
error("Unrecognised net message type "+message.type);
}
}
else
{
// Not rejoining - just trying to connect to server
switch (message.type)
{
case "netstatus":
switch (message.status)
{
case "connected":
getGUIObjectByName("connectionStatus").caption = "Registering with server...";
break;
case "authenticated":
if (message.rejoining)
{
getGUIObjectByName("connectionStatus").caption = "Game has already started - rejoining...";
g_IsRejoining = true;
return; // we'll process the game setup messages in the next tick
}
else
{
- Engine.SwitchGuiPage("page_gamesetup.xml", { "type": g_GameType });
+ Engine.SwitchGuiPage("page_gamesetup.xml", { "type": g_GameType, "serverName": g_ServerName });
return; // don't process any more messages - leave them for the game GUI loop
}
case "disconnected":
cancelSetup();
reportDisconnect(message.reason);
return;
default:
error("Unrecognised netstatus type "+message.status);
break;
}
break;
default:
error("Unrecognised net message type "+message.type);
break;
}
}
}
}
function switchSetupPage(oldpage, newpage)
{
getGUIObjectByName(oldpage).hidden = true;
getGUIObjectByName(newpage).hidden = false;
}
function startHost(playername, servername)
{
+ // Disallow identically named games in the multiplayer lobby
+ if (Engine.HasXmppClient())
+ {
+ for each (g in Engine.GetGameList())
+ {
+ if (g.name === servername)
+ {
+ getGUIObjectByName("hostFeedback").caption = "Game name already in use.";
+ return false;
+ }
+ }
+ }
try
{
Engine.StartNetworkHost(playername);
}
catch (e)
{
cancelSetup();
messageBox(400, 200,
"Cannot host game: " + e.message + ".",
"Error", 2);
return false;
}
startConnectionStatus("server");
- // TODO: ought to do something(?) with servername
-
+ g_ServerName = servername;
+ // Set player lobby presence
+ Engine.LobbySetPlayerPresence("playing");
return true;
}
function startJoin(playername, ip)
{
try
{
Engine.StartNetworkJoin(playername, ip);
}
catch (e)
{
cancelSetup();
messageBox(400, 200,
"Cannot join game: " + e.message + ".",
"Error", 2);
return false;
}
startConnectionStatus("client");
+ // Set player lobby presence
+ Engine.LobbySetPlayerPresence("playing");
return true;
}
Index: ps/trunk/binaries/data/mods/public/gui/gui.dtd
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gui.dtd (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/gui.dtd (revision 14098)
@@ -1,204 +1,205 @@
Index: ps/trunk/binaries/data/mods/public/gui/lobby/lobby.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/lobby.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/lobby.xml (revision 14098)
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+ completeNick();
+
+
+
+
+
+
+ Multiplayer Lobby
+
+
+
+ onTick();
+
+
+
+
+
+
+
+
+
+
+
+
+ Leaderboard
+ getGUIObjectByName("leaderboard").hidden = false;getGUIObjectByName("leaderboardFade").hidden = false;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Map Type:
+
+
+
+
+
+
+
+
+
+ Map Size:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Players:
+
+
+
+
+
+
+
+ Join Game
+
+ joinSelectedGame();
+
+
+
+ Host Game
+
+ // Open Multiplayer connection window with host option.
+ Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "host", name: g_Name });
+
+
+
+
+ Main Menu
+
+ lobbyStop();
+ Engine.SwitchGuiPage("page_pregame.xml");
+
+
+
+
+
+
+
+ selectGame(this.selected);
+
+
+
+
+
+
+
+
+
+
+ applyFilters();
+
+
+
+ applyFilters();
+
+
+
+ applyFilters();
+
+
+ Show full servers
+
+ applyFilters();
+
+
+
+
+
+
+ submitChatInput();
+
+
+
+
+
+
+
+
+ Leaderboard
+
+
+
+
+
+
+ Back
+ getGUIObjectByName("leaderboard").hidden = true;getGUIObjectByName("leaderboardFade").hidden = true;
+
+
+ Update
+ Engine.SendGetBoardList();
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.xml (revision 14098)
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+ onTick();
+
+
+
+
+ [Connection status]
+
+
+
+
+ Multiplayer Lobby
+
+
+
+
+ Connect to the game lobby.
+
+
+ Login:
+
+
+
+ this.caption = Engine.ConfigDB_GetValue("user", "lobby.login");
+
+
+
+ Password:
+
+
+
+ this.caption = Engine.ConfigDB_GetValue("user", "lobby.password");
+
+
+ lobbyStart();
+
+
+
+
+ Nickname:
+
+
+
+ this.caption = Engine.ConfigDB_GetValue("user", "playername");
+
+
+
+
+
+
+
+
+
+ Cancel
+
+ lobbyStop();
+ Engine.PopGuiPage();
+
+
+
+ Register
+
+ lobbyStop();
+ getGUIObjectByName("pageConnect").hidden = true;
+ getGUIObjectByName("pageRegister").hidden = false;
+
+
+
+ Connect
+
+ lobbyStart();
+
+
+
+
+
+
+ Registration.
+
+
+ Password again:
+
+
+
+ lobbyStartRegister()
+
+
+
+
+ Back
+
+ lobbyStop();
+ getGUIObjectByName("pageRegister").hidden = true;
+ getGUIObjectByName("pageConnect").hidden = false;
+
+
+
+ Register
+
+ lobbyStartRegister()
+
+
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/options/options.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/options/options.xml (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/options/options.xml (revision 14098)
@@ -1,68 +1,105 @@
-
-
-
+
-
-
-
-
- Game Options
-
-
-
-
-
- Enable Shadows
-
- this.checked = Engine.Renderer_GetShadowsEnabled();
- Engine.Renderer_SetShadowsEnabled(this.checked);
-
-
-
- Enable Shadow Filtering
-
- this.checked = Engine.Renderer_GetShadowPCFEnabled();
- Engine.Renderer_SetShadowPCFEnabled(this.checked);
-
-
-
-
-
-
- Enable Music
-
- if (this.checked) startMusic(); else stopMusic();
-
-
-
-
-
-
-
- Cancel
-
-
-
+
+
+ Game Options
+
+ System Settings
+ Windowed Mode
+
+ Engine.ConfigDB_GetValue("user", "windowed") === "true" ? this.checked = true : this.checked = false;
+ Engine.ConfigDB_CreateValue("user", "windowed", String(this.checked));
+
+ Background Pause
+
+ Engine.ConfigDB_GetValue("user", "pauseonfocusloss") === "true" ? this.checked = true : this.checked = false;
+ Engine.ConfigDB_CreateValue("user", "pauseonfocusloss", String(this.checked));
+
+
+
+ Graphics Settings
+ Enable Shadows
+
+ this.checked = Engine.Renderer_GetShadowsEnabled();
+ Engine.Renderer_SetShadowsEnabled(this.checked);
+
+ Enable Shadow Filtering
+
+ this.checked = Engine.Renderer_GetShadowPCFEnabled();
+ Engine.Renderer_SetShadowPCFEnabled(this.checked);
+
+
+
+ Sound Settings
+ Master Gain
+
+ this.caption = Engine.ConfigDB_GetValue("user", "sound.mastergain");
+
+ Save
+ Engine.ConfigDB_CreateValue("user", "sound.mastergain", String(getGUIObjectByName("SMasterCFG").caption));
+
+ Music Gain
+
+ this.caption = Engine.ConfigDB_GetValue("user", "sound.musicgain");
+
+ Save
+ Engine.ConfigDB_CreateValue("user", "sound.musicgain", String(getGUIObjectByName("SMusicCFG").caption));
+
+
+
+
+ Cancel
+ Engine.PopGuiPage();
+
+
+ Save
+ Engine.ConfigDB_WriteFile("user", "config/user.cfg");Engine.PopGuiPage();
+
Index: ps/trunk/binaries/data/mods/public/gui/page_prelobby.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/page_prelobby.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/page_prelobby.xml (revision 14098)
@@ -0,0 +1,14 @@
+
+
+ common/setup.xml
+ common/styles.xml
+ common/sprite1.xml
+
+common/common_sprites.xml
+common/common_styles.xml
+
+ gamesetup/styles.xml
+ summary/sprites.xml
+ lobby/prelobby.xml
+
+
Index: ps/trunk/binaries/data/mods/public/gui/session/session.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 14097)
+++ ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 14098)
@@ -1,690 +1,814 @@
// Network Mode
var g_IsNetworked = false;
+// Is this user in control of game settings (i.e. is a network server, or offline player)
+var g_IsController;
+
// Cache the basic player data (name, civ, color)
var g_Players = [];
// Cache the useful civ data
var g_CivData = {};
var g_GameSpeeds = {};
var g_CurrentSpeed;
var g_PlayerAssignments = { "local": { "name": "You", "player": 1 } };
// Cache dev-mode settings that are frequently or widely used
var g_DevSettings = {
controlAll: false
};
// Whether status bars should be shown for all of the player's units.
var g_ShowAllStatusBars = false;
// Indicate when one of the current player's training queues is blocked
// (this is used to support population counter blinking)
var g_IsTrainingBlocked = false;
// Cache simulation state (updated on every simulation update)
var g_SimState;
// Cache EntityStates
var g_EntityStates = {}; // {id:entState}
// Whether the player has lost/won and reached the end of their game
var g_GameEnded = false;
var g_Disconnected = false; // Lost connection to server
+// Holds player states from the last tick
+var g_CachedLastStates = "";
+
// Colors to flash when pop limit reached
const DEFAULT_POPULATION_COLOR = "white";
const POPULATION_ALERT_COLOR = "orange";
function GetSimState()
{
if (!g_SimState)
{
g_SimState = Engine.GuiInterfaceCall("GetSimulationState");
}
return g_SimState;
}
function GetEntityState(entId)
{
if (!(entId in g_EntityStates))
{
var entState = Engine.GuiInterfaceCall("GetEntityState", entId);
g_EntityStates[entId] = entState;
}
return g_EntityStates[entId];
}
// Cache TemplateData
var g_TemplateData = {}; // {id:template}
function GetTemplateData(templateName)
{
if (!(templateName in g_TemplateData))
{
var template = Engine.GuiInterfaceCall("GetTemplateData", templateName);
g_TemplateData[templateName] = template;
}
return g_TemplateData[templateName];
}
// Cache TechnologyData
var g_TechnologyData = {}; // {id:template}
function GetTechnologyData(technologyName)
{
if (!(technologyName in g_TechnologyData))
{
var template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName);
g_TechnologyData[technologyName] = template;
}
return g_TechnologyData[technologyName];
}
// Init
function init(initData, hotloadData)
{
if (initData)
{
g_IsNetworked = initData.isNetworked; // Set network mode
+ g_IsController = initData.isController; // Set controller mode
g_PlayerAssignments = initData.playerAssignments;
+ g_MatchID = initData.attribs.matchID;
// Cache the player data
// (This may be updated at runtime by handleNetMessage)
g_Players = getPlayerData(g_PlayerAssignments);
if (initData.savedGUIData)
restoreSavedGameData(initData.savedGUIData);
getGUIObjectByName("gameSpeedButton").hidden = g_IsNetworked;
}
else // Needed for autostart loading option
{
g_Players = getPlayerData(null);
}
// Cache civ data
g_CivData = loadCivData();
g_CivData["gaia"] = { "Code": "gaia", "Name": "Gaia" };
g_GameSpeeds = initGameSpeeds();
g_CurrentSpeed = Engine.GetSimRate();
var gameSpeed = getGUIObjectByName("gameSpeed");
gameSpeed.list = g_GameSpeeds.names;
gameSpeed.list_data = g_GameSpeeds.speeds;
var idx = g_GameSpeeds.speeds.indexOf(g_CurrentSpeed);
gameSpeed.selected = idx != -1 ? idx : g_GameSpeeds["default"];
gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); }
getGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem;
getGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name;
initMenuPosition(); // set initial position
// Populate player selection dropdown
var playerNames = [];
var playerIDs = [];
for (var player in g_Players)
{
playerNames.push(g_Players[player].name);
playerIDs.push(player);
}
var viewPlayerDropdown = getGUIObjectByName("viewPlayer");
viewPlayerDropdown.list = playerNames;
viewPlayerDropdown.list_data = playerIDs;
viewPlayerDropdown.selected = Engine.GetPlayerID();
// If in Atlas editor, disable the exit button
if (Engine.IsAtlasRunning())
getGUIObjectByName("menuExitButton").enabled = false;
if (hotloadData)
{
g_Selection.selected = hotloadData.selection;
}
else
{
// Starting for the first time:
var civMusic = g_CivData[g_Players[Engine.GetPlayerID()].civ].Music;
initMusic();
global.music.storeTracks(civMusic);
global.music.setState(global.music.states.PEACE);
playRandomAmbient("temperate");
}
if (Engine.ConfigDB_GetValue("user", "gui.session.timeelapsedcounter") === "true")
getGUIObjectByName("timeElapsedCounter").hidden = false;
onSimulationUpdate();
// Report the performance after 5 seconds (when we're still near
// the initial camera view) and a minute (when the profiler will
// have settled down if framerates as very low), to give some
// extremely rough indications of performance
setTimeout(function() { reportPerformance(5); }, 5000);
setTimeout(function() { reportPerformance(60); }, 60000);
}
function selectViewPlayer(playerID)
{
Engine.SetPlayerID(playerID);
if (playerID != 0) {
getGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[playerID].civ].Emblem;
getGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[playerID].civ].Name;
}
}
function reportPerformance(time)
{
var settings = Engine.GetMapSettings();
var data = {
time: time,
map: settings.Name,
seed: settings.Seed, // only defined for random maps
size: settings.Size, // only defined for random maps
profiler: Engine.GetProfilerState()
};
Engine.SubmitUserReport("profile", 3, JSON.stringify(data));
}
function resignGame()
{
var simState = GetSimState();
// Players can't resign if they've already won or lost.
if (simState.players[Engine.GetPlayerID()].state != "active" || g_Disconnected)
return;
// Tell other players that we have given up and been defeated
Engine.PostNetworkCommand({
"type": "defeat-player",
"playerId": Engine.GetPlayerID()
});
global.music.setState(global.music.states.DEFEAT);
resumeGame();
}
function leaveGame()
{
var extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState");
var playerState = extendedSimState.players[Engine.GetPlayerID()];
-
+ var mapSettings = Engine.GetMapSettings();
var gameResult;
+
if (g_Disconnected)
{
gameResult = "You have been disconnected."
}
else if (playerState.state == "won")
{
gameResult = "You have won the battle!";
}
else if (playerState.state == "defeated")
{
gameResult = "You have been defeated...";
}
else // "active"
{
gameResult = "You have abandoned the game.";
// Tell other players that we have given up and been defeated
Engine.PostNetworkCommand({
"type": "defeat-player",
"playerId": Engine.GetPlayerID()
});
global.music.setState(global.music.states.DEFEAT);
}
- var mapSettings = Engine.GetMapSettings();
-
stopAmbient();
endGame();
+ if (g_IsController)
+ {
+ Engine.SendUnregisterGame();
+ }
Engine.SwitchGuiPage("page_summary.xml", {
"gameResult" : gameResult,
"timeElapsed" : extendedSimState.timeElapsed,
"playerStates": extendedSimState.players,
"players": g_Players,
"mapSettings": mapSettings
});
}
// Return some data that we'll use when hotloading this file after changes
function getHotloadData()
{
return { selection: g_Selection.selected };
}
// Return some data that will be stored in saved game files
function getSavedGameData()
{
var data = {};
data.playerAssignments = g_PlayerAssignments;
data.groups = g_Groups.groups;
// TODO: any other gui state?
return data;
}
function restoreSavedGameData(data)
{
// Clear selection when loading a game
g_Selection.reset();
// Restore control groups
for (var groupNumber in data.groups)
{
g_Groups.groups[groupNumber].groups = data.groups[groupNumber].groups;
g_Groups.groups[groupNumber].ents = data.groups[groupNumber].ents;
}
updateGroups();
}
var lastTickTime = new Date;
/**
* Called every frame.
*/
function onTick()
{
var now = new Date;
var tickLength = new Date - lastTickTime;
lastTickTime = now;
checkPlayerState();
while (true)
{
var message = Engine.PollNetworkClient();
if (!message)
break;
handleNetMessage(message);
}
updateCursorAndTooltip();
// If the selection changed, we need to regenerate the sim display (the display depends on both the
// simulation state and the current selection).
if (g_Selection.dirty)
{
onSimulationUpdate();
// Display rally points for selected buildings
Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() });
}
// Run timers
updateTimers();
// Animate menu
updateMenuPosition(tickLength);
// When training is blocked, flash population (alternates colour every 500msec)
if (g_IsTrainingBlocked && (Date.now() % 1000) < 500)
getGUIObjectByName("resourcePop").textcolor = POPULATION_ALERT_COLOR;
else
getGUIObjectByName("resourcePop").textcolor = DEFAULT_POPULATION_COLOR;
// Clear renamed entities list
Engine.GuiInterfaceCall("ClearRenamedEntities");
}
function checkPlayerState()
{
- var simState = GetSimState();
- var playerState = simState.players[Engine.GetPlayerID()];
+ // Once the game ends, we're done here.
+ if (g_GameEnded)
+ return;
+
+ // Send a game report for each player in this game.
+ var m_simState = GetSimState();
+ var playerState = m_simState.players[Engine.GetPlayerID()];
+ var tempStates = "";
+ for each (var player in m_simState.players) {tempStates += player.state + ",";}
- if (!g_GameEnded)
+ if (g_CachedLastStates != tempStates)
{
- // If the game is about to end, disable the ability to resign.
- if (playerState.state != "active")
- getGUIObjectByName("menuResignButton").enabled = false;
- else
- return;
+ g_CachedLastStates = tempStates;
+ reportGame(Engine.GuiInterfaceCall("GetExtendedSimulationState"));
+ }
- if (playerState.state == "defeated")
- {
- g_GameEnded = true;
- // TODO: DEFEAT_CUE is missing?
- global.music.setState(global.music.states.DEFEAT);
-
- closeMenu();
- closeOpenDialogs();
-
- if (Engine.IsAtlasRunning())
- {
- // If we're in Atlas, we can't leave the game
- var btCaptions = ["OK"];
- var btCode = [null];
- var message = "Press OK to continue";
- }
- else
- {
- var btCaptions = ["Yes", "No"];
- var btCode = [leaveGame, null];
- var message = "Do you want to quit?";
- }
- messageBox(400, 200, message, "DEFEATED!", 0, btCaptions, btCode);
- }
- else if (playerState.state == "won")
- {
- g_GameEnded = true;
- global.music.setState(global.music.states.VICTORY);
+ // If the local player hasn't finished playing, we return here to avoid the victory/defeat messages.
+ if (playerState.state == "active")
+ return;
- closeMenu();
- closeOpenDialogs();
+ // We can't resign once the game is over.
+ getGUIObjectByName("menuResignButton").enabled = false;
- if (!getGUIObjectByName("devCommandsRevealMap").checked)
- getGUIObjectByName("devCommandsRevealMap").checked = true;
+ // Make sure nothing is open to avoid stacking.
+ closeMenu();
+ closeOpenDialogs();
- if (Engine.IsAtlasRunning())
- {
- // If we're in Atlas, we can't leave the game
- var btCaptions = ["OK"];
- var btCode = [null];
- var message = "Press OK to continue";
- }
- else
- {
- var btCaptions = ["Yes", "No"];
- var btCode = [leaveGame, null];
- var message = "Do you want to quit?";
- }
- messageBox(400, 200, message, "VICTORIOUS!", 0, btCaptions, btCode);
- }
+ // Make sure this doesn't run again.
+ g_GameEnded = true;
+
+ if (Engine.IsAtlasRunning())
+ {
+ // If we're in Atlas, we can't leave the game
+ var btCaptions = ["OK"];
+ var btCode = [null];
+ var message = "Press OK to continue";
+ }
+ else
+ {
+ var btCaptions = ["Yes", "No"];
+ var btCode = [leaveGame, null];
+ var message = "Do you want to quit?";
+ }
+
+ if (playerState.state == "defeated")
+ {
+ global.music.setState(global.music.states.DEFEAT);
+ messageBox(400, 200, message, "DEFEATED!", 0, btCaptions, btCode);
+ }
+ else if (playerState.state == "won")
+ {
+ global.music.setState(global.music.states.VICTORY);
+ // TODO: Reveal map directly instead of this silly proxy.
+ if (!getGUIObjectByName("devCommandsRevealMap").checked)
+ getGUIObjectByName("devCommandsRevealMap").checked = true;
+ messageBox(400, 200, message, "VICTORIOUS!", 0, btCaptions, btCode);
}
+
}
function changeGameSpeed(speed)
{
// For non-networked games only
if (!g_IsNetworked)
{
Engine.SetSimRate(speed);
g_CurrentSpeed = speed;
}
}
/**
* Recomputes GUI state that depends on simulation state or selection state. Called directly every simulation
* update (see session.xml), or from onTick when the selection has changed.
*/
function onSimulationUpdate()
{
g_Selection.dirty = false;
g_EntityStates = {};
g_TemplateData = {};
g_TechnologyData = {};
g_SimState = Engine.GuiInterfaceCall("GetSimulationState");
// If we're called during init when the game is first loading, there will be no simulation yet, so do nothing
if (!g_SimState)
return;
handleNotifications();
if (g_ShowAllStatusBars)
recalculateStatusBarDisplay();
updateHero();
updateGroups();
updateDebug();
updatePlayerDisplay();
updateSelectionDetails();
updateResearchDisplay();
updateBuildingPlacementPreview();
updateTimeElapsedCounter();
// Update music state on basis of battle state.
var battleState = Engine.GuiInterfaceCall("GetBattleState", Engine.GetPlayerID());
if (battleState)
global.music.setState(global.music.states[battleState]);
}
function updateHero()
{
var simState = GetSimState();
var playerState = simState.players[Engine.GetPlayerID()];
var heroButton = getGUIObjectByName("unitHeroButton");
if (!playerState || playerState.heroes.length <= 0)
{
heroButton.hidden = true;
return;
}
var heroImage = getGUIObjectByName("unitHeroImage");
var heroState = GetEntityState(playerState.heroes[0]);
var template = GetTemplateData(heroState.template);
heroImage.sprite = "stretched:session/portraits/" + template.icon;
var hero = playerState.heroes[0];
heroButton.onpress = function()
{
if (!Engine.HotkeyIsPressed("selection.add"))
g_Selection.reset();
g_Selection.addList([hero]);
};
heroButton.ondoublepress = function() { selectAndMoveTo(getEntityOrHolder(hero)); };
heroButton.hidden = false;
// Setup tooltip
var tooltip = "[font=\"serif-bold-16\"]" + template.name.specific + "[/font]";
tooltip += "\n[font=\"serif-bold-13\"]Health:[/font] " + heroState.hitpoints + "/" + heroState.maxHitpoints;
tooltip += "\n[font=\"serif-bold-13\"]" + (heroState.attack ? heroState.attack.type + " " : "")
+ "Attack:[/font] " + damageTypeDetails(heroState.attack);
// Show max attack range if ranged attack, also convert to tiles (4m per tile)
if (heroState.attack && heroState.attack.type == "Ranged")
tooltip += ", [font=\"serif-bold-13\"]Range:[/font] " + Math.round(heroState.attack.maxRange/4);
tooltip += "\n[font=\"serif-bold-13\"]Armor:[/font] " + damageTypeDetails(heroState.armour);
tooltip += "\n" + template.tooltip;
heroButton.tooltip = tooltip;
};
function updateGroups()
{
var guiName = "Group";
g_Groups.update();
for (var i = 0; i < 10; i++)
{
var button = getGUIObjectByName("unit"+guiName+"Button["+i+"]");
var label = getGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i;
if (g_Groups.groups[i].getTotalCount() == 0)
button.hidden = true;
else
button.hidden = false;
button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); } })(i);
button.ondoublepress = (function(i) { return function() { performGroup("snap", i); } })(i);
}
var numButtons = i;
var rowLength = 1;
var numRows = Math.ceil(numButtons / rowLength);
var buttonSideLength = getGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
var buttonSpacer = buttonSideLength+1;
for (var i = 0; i < numRows; i++)
layoutButtonRow(i, guiName, buttonSideLength, buttonSpacer, rowLength*i, rowLength*(i+1) );
}
function updateDebug()
{
var simState = GetSimState();
var debug = getGUIObjectByName("debug");
if (getGUIObjectByName("devDisplayState").checked)
{
debug.hidden = false;
}
else
{
debug.hidden = true;
return;
}
var conciseSimState = deepcopy(simState);
conciseSimState.players = "<<>>";
var text = "simulation: " + uneval(conciseSimState);
var selection = g_Selection.toList();
if (selection.length)
{
var entState = GetEntityState(selection[0]);
if (entState)
{
var template = GetTemplateData(entState.template);
text += "\n\nentity: {\n";
for (var k in entState)
text += " "+k+":"+uneval(entState[k])+"\n";
text += "}\n\ntemplate: " + uneval(template);
}
}
debug.caption = text;
}
function updatePlayerDisplay()
{
var simState = GetSimState();
var playerState = simState.players[Engine.GetPlayerID()];
if (!playerState)
return;
getGUIObjectByName("resourceFood").caption = playerState.resourceCounts.food;
getGUIObjectByName("resourceWood").caption = playerState.resourceCounts.wood;
getGUIObjectByName("resourceStone").caption = playerState.resourceCounts.stone;
getGUIObjectByName("resourceMetal").caption = playerState.resourceCounts.metal;
getGUIObjectByName("resourcePop").caption = playerState.popCount + "/" + playerState.popLimit;
g_IsTrainingBlocked = playerState.trainingBlocked;
}
function selectAndMoveTo(ent)
{
var entState = GetEntityState(ent);
if (!entState || !entState.position)
return;
g_Selection.reset();
g_Selection.addList([ent]);
var position = entState.position;
Engine.CameraMoveTo(position.x, position.z);
}
function updateResearchDisplay()
{
var researchStarted = Engine.GuiInterfaceCall("GetStartedResearch", Engine.GetPlayerID());
if (!researchStarted)
return;
// Set up initial positioning.
var buttonSideLength = getGUIObjectByName("researchStartedButton[0]").size.right;
for (var i = 0; i < 10; ++i)
{
var button = getGUIObjectByName("researchStartedButton[" + i + "]");
var size = button.size;
size.top = (4 + buttonSideLength) * i;
size.bottom = size.top + buttonSideLength;
button.size = size;
}
var numButtons = 0;
for (var tech in researchStarted)
{
// Show at most 10 in-progress techs.
if (numButtons >= 10)
break;
var template = GetTechnologyData(tech);
var button = getGUIObjectByName("researchStartedButton[" + numButtons + "]");
button.hidden = false;
button.tooltip = getEntityNames(template);
button.onpress = (function(e) { return function() { selectAndMoveTo(e) } })(researchStarted[tech].researcher);
var icon = "stretched:session/portraits/" + template.icon;
getGUIObjectByName("researchStartedIcon[" + numButtons + "]").sprite = icon;
// Scale the progress indicator.
var size = getGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size;
// Buttons are assumed to be square, so left/right offsets can be used for top/bottom.
size.top = size.left + Math.round(researchStarted[tech].progress * (size.right - size.left));
getGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size = size;
++numButtons;
}
// Hide unused buttons.
for (var i = numButtons; i < 10; ++i)
getGUIObjectByName("researchStartedButton[" + i + "]").hidden = true;
}
function updateTimeElapsedCounter()
{
var simState = GetSimState();
var speed = g_CurrentSpeed != 1.0 ? " (" + g_CurrentSpeed + "x)" : "";
var timeElapsedCounter = getGUIObjectByName("timeElapsedCounter");
timeElapsedCounter.caption = timeToString(simState.timeElapsed) + speed;
}
// Toggles the display of status bars for all of the player's entities.
function recalculateStatusBarDisplay()
{
if (g_ShowAllStatusBars)
var entities = Engine.PickFriendlyEntitiesOnScreen(Engine.GetPlayerID());
else
{
var selected = g_Selection.toList();
for each (var ent in g_Selection.highlighted)
selected.push(ent);
// Remove selected entities from the 'all entities' array, to avoid disabling their status bars.
var entities = Engine.GuiInterfaceCall("GetPlayerEntities").filter(
function(idx) { return (selected.indexOf(idx) == -1); }
);
}
Engine.GuiInterfaceCall("SetStatusBars", { "entities": entities, "enabled": g_ShowAllStatusBars });
}
// Temporarily adding this here
const AMBIENT_TEMPERATE = "temperate";
var currentAmbient;
function playRandomAmbient(type)
{
switch (type)
{
case AMBIENT_TEMPERATE:
// Seem to need the underscore at the end of "temperate" to avoid crash
// (Might be caused by trying to randomly load day_temperate.xml)
// currentAmbient = newRandomSound("ambient", "temperate_", "dayscape");
const AMBIENT = "audio/ambient/dayscape/day_temperate_gen_03.ogg";
Engine.PlayAmbientSound( AMBIENT, true );
break;
default:
Engine.Console_Write("Unrecognized ambient type: " + type);
break;
}
}
// Temporarily adding this here
function stopAmbient()
{
if (currentAmbient)
{
currentAmbient.free();
currentAmbient = null;
}
}
+// Send a report on the game status to the lobby
+function reportGame(extendedSimState)
+{
+ // Resources gathered and used
+ var playerFoodGatheredString = "";
+ var playerWoodGatheredString = "";
+ var playerStoneGatheredString = "";
+ var playerMetalGatheredString = "";
+ var playerFoodUsedString = "";
+ var playerWoodUsedString = "";
+ var playerStoneUsedString = "";
+ var playerMetalUsedString = "";
+ // Resources exchanged
+ var playerFoodBoughtString = "";
+ var playerWoodBoughtString = "";
+ var playerStoneBoughtString = "";
+ var playerMetalBoughtString = "";
+ var playerFoodSoldString = "";
+ var playerWoodSoldString = "";
+ var playerStoneSoldString = "";
+ var playerMetalSoldString = "";
+ var playerTradeIncomeString = "";
+ // Unit Stats
+ var playerUnitsLostString = "";
+ var playerUnitsTrainedString = "";
+ var playerEnemyUnitsKilledString = "";
+ // Building stats
+ var playerBuildingsConstructedString = "";
+ var playerBuildingsLostString = "";
+ var playerEnemyBuildingsDestroyedString = "";
+ var playerCivCentersBuiltString = "";
+ var playerEnemyCivCentersDestroyedString = "";
+ // Tribute
+ var playerTributeSentString = "";
+ var playerTributeReceivedString = "";
+ // Various
+ var mapName = Engine.GetMapSettings().Name;
+ var playerStatesString = "";
+ var playerCivsString = "";
+ var playerPercentMapExploredString = "";
+ var playerTreasuresCollectedString = "";
+
+ // Serialize the statistics for each player into a comma-separated list.
+ for each (var player in extendedSimState.players)
+ {
+ playerStatesString += player.state + ",";
+ playerCivsString += player.civ + ",";
+ playerFoodGatheredString += player.statistics.resourcesGathered.food + ",";
+ playerWoodGatheredString += player.statistics.resourcesGathered.wood + ",";
+ playerStoneGatheredString += player.statistics.resourcesGathered.stone + ",";
+ playerMetalGatheredString += player.statistics.resourcesGathered.metal + ",";
+ playerFoodUsedString += player.statistics.resourcesUsed.food + ",";
+ playerWoodUsedString += player.statistics.resourcesUsed.wood + ",";
+ playerStoneUsedString += player.statistics.resourcesUsed.stone + ",";
+ playerMetalUsedString += player.statistics.resourcesUsed.metal + ",";
+ playerUnitsLostString += player.statistics.unitsLost + ",";
+ playerUnitsTrainedString += player.statistics.unitsTrained + ",";
+ playerEnemyUnitsKilledString += player.statistics.enemyUnitsKilled + ",";
+ playerBuildingsConstructedString += player.statistics.buildingsConstructed + ",";
+ playerBuildingsLostString += player.statistics.buildingsLost + ",";
+ playerEnemyBuildingsDestroyedString += player.statistics.enemyBuildingsDestroyed + ",";
+ playerFoodBoughtString += player.statistics.resourcesBought.food + ",";
+ playerWoodBoughtString += player.statistics.resourcesBought.wood + ",";
+ playerStoneBoughtString += player.statistics.resourcesBought.stone + ",";
+ playerMetalBoughtString += player.statistics.resourcesBought.metal + ",";
+ playerFoodSoldString += player.statistics.resourcesSold.food + ",";
+ playerWoodSoldString += player.statistics.resourcesSold.wood + ",";
+ playerStoneSoldString += player.statistics.resourcesSold.stone + ",";
+ playerMetalSoldString += player.statistics.resourcesSold.metal + ",";
+ playerTributeSentString += player.statistics.tributesSent + ",";
+ playerTributeReceivedString += player.statistics.tributesReceived + ",";
+ playerPercentMapExploredString += player.statistics.precentMapExplored = ",";
+ playerCivCentersBuiltString += player.statistics.civCentresBuilt + ",";
+ playerEnemyCivCentersDestroyedString += player.statistics.enemyCivCentresDestroyed + ",";
+ playerTreasuresCollectedString += player.statistics.treasuresCollected + ",";
+ playerTradeIncomeString += player.statistics.tradeIncome + ",";
+ }
+
+ // Send the report with serialized data
+ Engine.SendGameReport({
+ "timeElapsed" : extendedSimState.timeElapsed,
+ "playerStates" : playerStatesString,
+ "playerID": Engine.GetPlayerID(),
+ "matchID": g_MatchID,
+ "civs" : playerCivsString,
+ "mapName" : mapName,
+ "foodGathered": playerFoodGatheredString,
+ "woodGathered": playerWoodGatheredString,
+ "stoneGathered": playerStoneGatheredString,
+ "metalGathered": playerMetalGatheredString,
+ "foodUsed": playerFoodUsedString,
+ "woodUsed": playerWoodUsedString,
+ "stoneUsed": playerStoneUsedString,
+ "metalUsed": playerMetalUsedString,
+ "unitsLost": playerUnitsLostString,
+ "unitsTrained": playerUnitsTrainedString,
+ "enemyUnitsKilled": playerEnemyUnitsKilledString,
+ "buildingsLost": playerBuildingsLostString,
+ "buildingsConstructed": playerBuildingsConstructedString,
+ "enemyBuildingsDestroyed": playerEnemyBuildingsDestroyedString,
+ "foodBought": playerFoodBoughtString,
+ "woodBought": playerWoodBoughtString,
+ "stoneBought": playerStoneBoughtString,
+ "metalBought": playerMetalBoughtString,
+ "foodSold": playerFoodSoldString,
+ "woodSold": playerWoodSoldString,
+ "stoneSold": playerStoneSoldString,
+ "metalSold": playerMetalSoldString,
+ "tributeSent": playerTributeSentString,
+ "tributeReceived": playerTributeReceivedString,
+ "precentMapExplored": playerPercentMapExploredString,
+ "civCentersBuilt": playerCivCentersBuiltString,
+ "enemyCivCentersDestroyed": playerEnemyCivCentersDestroyedString,
+ "treasuresCollected": playerTreasuresCollectedString,
+ "tradeIncome": playerTradeIncomeString
+ });
+}
Index: ps/trunk/source/gui/CText.cpp
===================================================================
--- ps/trunk/source/gui/CText.cpp (revision 14097)
+++ ps/trunk/source/gui/CText.cpp (revision 14098)
@@ -1,283 +1,282 @@
/* Copyright (C) 2010 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 .
*/
/*
CText
*/
#include "precompiled.h"
#include "GUI.h"
#include "CText.h"
#include "CGUIScrollBarVertical.h"
#include "lib/ogl.h"
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
CText::CText()
{
AddSetting(GUIST_float, "buffer_zone");
AddSetting(GUIST_CGUIString, "caption");
AddSetting(GUIST_int, "cell_id");
AddSetting(GUIST_bool, "clip");
AddSetting(GUIST_CStrW, "font");
AddSetting(GUIST_bool, "scrollbar");
AddSetting(GUIST_CStr, "scrollbar_style");
AddSetting(GUIST_bool, "scroll_bottom");
AddSetting(GUIST_CGUISpriteInstance, "sprite");
AddSetting(GUIST_EAlign, "text_align");
AddSetting(GUIST_EVAlign, "text_valign");
AddSetting(GUIST_CColor, "textcolor");
AddSetting(GUIST_CStrW, "tooltip");
AddSetting(GUIST_CStr, "tooltip_style");
// Private settings
AddSetting(GUIST_CStrW, "_icon_tooltip");
AddSetting(GUIST_CStr, "_icon_tooltip_style");
//GUI::SetSetting(this, "ghost", true);
GUI::SetSetting(this, "scrollbar", false);
GUI::SetSetting(this, "clip", true);
// Add scroll-bar
CGUIScrollBarVertical * bar = new CGUIScrollBarVertical();
bar->SetRightAligned(true);
- bar->SetUseEdgeButtons(true);
AddScrollBar(bar);
// Add text
AddText(new SGUIText());
}
CText::~CText()
{
}
void CText::SetupText()
{
if (!GetGUI())
return;
ENSURE(m_GeneratedTexts.size()>=1);
CStrW font;
if (GUI::GetSetting(this, "font", font) != PSRETURN_OK || font.empty())
// Use the default if none is specified
// TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style.
font = L"default";
CGUIString caption;
bool scrollbar;
GUI::GetSetting(this, "caption", caption);
GUI::GetSetting(this, "scrollbar", scrollbar);
float width = m_CachedActualSize.GetWidth();
// remove scrollbar if applicable
if (scrollbar && GetScrollBar(0).GetStyle())
width -= GetScrollBar(0).GetStyle()->m_Width;
float buffer_zone=0.f;
GUI::GetSetting(this, "buffer_zone", buffer_zone);
*m_GeneratedTexts[0] = GetGUI()->GenerateText(caption, font, width, buffer_zone, this);
if (! scrollbar)
CalculateTextPosition(m_CachedActualSize, m_TextPos, *m_GeneratedTexts[0]);
// Setup scrollbar
if (scrollbar)
{
bool scrollbottom = false;
GUI::GetSetting(this, "scroll_bottom", scrollbottom);
// If we are currently scrolled to the bottom of the text,
// then add more lines of text, update the scrollbar so we
// stick to the bottom.
// (Use 1.5px delta so this triggers the first time caption is set)
bool bottom = false;
if (scrollbottom && GetScrollBar(0).GetPos() > GetScrollBar(0).GetMaxPos() - 1.5f)
bottom = true;
GetScrollBar(0).SetScrollRange(m_GeneratedTexts[0]->m_Size.cy);
GetScrollBar(0).SetScrollSpace(m_CachedActualSize.GetHeight());
GetScrollBar(0).SetX(m_CachedActualSize.right);
GetScrollBar(0).SetY(m_CachedActualSize.top);
GetScrollBar(0).SetZ(GetBufferedZ());
GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
if (bottom)
GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos());
}
}
void CText::HandleMessage(SGUIMessage &Message)
{
IGUIScrollBarOwner::HandleMessage(Message);
//IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead!
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
if (Message.value == "scrollbar")
{
SetupText();
}
// Update scrollbar
if (Message.value == "scrollbar_style")
{
CStr scrollbar_style;
GUI::GetSetting(this, Message.value, scrollbar_style);
GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
SetupText();
}
break;
case GUIM_MOUSE_WHEEL_DOWN:
{
GetScrollBar(0).ScrollPlus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
break;
}
case GUIM_MOUSE_WHEEL_UP:
{
GetScrollBar(0).ScrollMinus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
SGUIMessage msg(GUIM_MOUSE_MOTION);
HandleMessage(msg);
break;
}
case GUIM_LOAD:
{
GetScrollBar(0).SetX( m_CachedActualSize.right );
GetScrollBar(0).SetY( m_CachedActualSize.top );
GetScrollBar(0).SetZ( GetBufferedZ() );
GetScrollBar(0).SetLength( m_CachedActualSize.bottom - m_CachedActualSize.top );
CStr scrollbar_style;
GUI::GetSetting(this, "scrollbar_style", scrollbar_style);
GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
}
break;
default:
break;
}
IGUITextOwner::HandleMessage(Message);
}
void CText::Draw()
{
float bz = GetBufferedZ();
// First call draw on ScrollBarOwner
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
if (scrollbar)
{
// Draw scrollbar
IGUIScrollBarOwner::Draw();
}
if (GetGUI())
{
CGUISpriteInstance *sprite;
int cell_id;
bool clip;
GUI::GetSettingPointer(this, "sprite", sprite);
GUI::GetSetting(this, "cell_id", cell_id);
GUI::GetSetting(this, "clip", clip);
GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
float scroll=0.f;
if (scrollbar)
{
scroll = GetScrollBar(0).GetPos();
}
// Clipping area (we'll have to subtract the scrollbar)
CRect cliparea;
if (clip)
{
cliparea = m_CachedActualSize;
if (scrollbar)
{
// subtract scrollbar from cliparea
if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
cliparea.right <= GetScrollBar(0).GetOuterRect().right)
cliparea.right = GetScrollBar(0).GetOuterRect().left;
if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
cliparea.left < GetScrollBar(0).GetOuterRect().right)
cliparea.left = GetScrollBar(0).GetOuterRect().right;
}
}
CColor color;
GUI::GetSetting(this, "textcolor", color);
if (scrollbar)
DrawText(0, color, m_CachedActualSize.TopLeft() - CPos(0.f, scroll), bz+0.1f, cliparea);
else
DrawText(0, color, m_TextPos, bz+0.1f, cliparea);
}
}
bool CText::MouseOverIcon()
{
std::vector::iterator text_it;
std::list::iterator sprite_it;
for (text_it=m_GeneratedTexts.begin(); text_it!=m_GeneratedTexts.end(); ++text_it)
{
SGUIText* guitext = *text_it;
for (sprite_it=guitext->m_SpriteCalls.begin(); sprite_it!=guitext->m_SpriteCalls.end(); ++sprite_it)
{
//Check mouse over sprite
SGUIText::SSpriteCall spritecall = *sprite_it;
if (spritecall.m_Area.PointInside(GetMousePos() - m_CachedActualSize.TopLeft()))
{
//If tooltip exists, set the property
if(!spritecall.m_Tooltip.empty())
{
SetSetting("_icon_tooltip_style", spritecall.m_TooltipStyle);
SetSetting("_icon_tooltip", spritecall.m_Tooltip);
}
return true;
}
}
}
return false;
}
Index: ps/trunk/source/gui/scripting/ScriptFunctions.cpp
===================================================================
--- ps/trunk/source/gui/scripting/ScriptFunctions.cpp (revision 14097)
+++ ps/trunk/source/gui/scripting/ScriptFunctions.cpp (revision 14098)
@@ -1,746 +1,997 @@
/* 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/GUID.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 "lobby/IXmppClient.h"
+#include "lobby/sha.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);
}
+std::wstring GetMatchID(void* UNUSED(cbdata))
+{
+ return ps_generate_guid().FromUTF8();
+}
+
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) != 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();
}
+/* Begin lobby related functions */
+
+void StartXmppClient(void* cbdata, std::string sUsername, std::string sPassword, std::string sRoom, std::string sNick)
+{
+ CGUIManager* guiManager = static_cast (cbdata);
+
+ ENSURE(!g_XmppClient);
+
+ g_XmppClient = IXmppClient::create(guiManager->GetScriptInterface(), sUsername, sPassword, sRoom, sNick);
+ g_rankedGame = true;
+}
+
+void StartRegisterXmppClient(void* cbdata, std::string sUsername, std::string sPassword)
+{
+ CGUIManager* guiManager = static_cast (cbdata);
+
+ ENSURE(!g_XmppClient);
+
+ g_XmppClient = IXmppClient::create(guiManager->GetScriptInterface(), sUsername, sPassword, "", "", true);
+}
+
+bool HasXmppClient(void* UNUSED(cbdata))
+{
+ return (g_XmppClient ? true : false);
+}
+
+void StopXmppClient(void* UNUSED(cbdata))
+{
+ ENSURE(g_XmppClient);
+ SAFE_DELETE(g_XmppClient);
+ g_rankedGame = false;
+}
+
+void ConnectXmppClient(void* UNUSED(cbdata))
+{
+ ENSURE(g_XmppClient);
+ g_XmppClient->connect();
+}
+
+void DisconnectXmppClient(void* UNUSED(cbdata))
+{
+ ENSURE(g_XmppClient);
+ g_XmppClient->disconnect();
+}
+
+void RecvXmppClient(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return;
+ g_XmppClient->recv();
+}
+
+void SendGetGameList(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return;
+ g_XmppClient->SendIqGetGameList();
+}
+
+void SendGetBoardList(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return;
+ g_XmppClient->SendIqGetBoardList();
+}
+
+void SendGameReport(void* UNUSED(cbdata), CScriptVal data)
+{
+ if (!g_XmppClient)
+ return;
+ g_XmppClient->SendIqGameReport(data);
+}
+
+void SendRegisterGame(void* UNUSED(cbdata), CScriptVal data)
+{
+ if (!g_XmppClient)
+ return;
+ g_XmppClient->SendIqRegisterGame(data);
+}
+
+void SendUnregisterGame(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return;
+ g_XmppClient->SendIqUnregisterGame();
+}
+
+void SendChangeStateGame(void* UNUSED(cbdata), std::string nbp, std::string players)
+{
+ if (!g_XmppClient)
+ return;
+ g_XmppClient->SendIqChangeStateGame(nbp, players);
+}
+
+CScriptVal GetPlayerList(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return CScriptVal();
+
+ CScriptValRooted playerList = g_XmppClient->GUIGetPlayerList();
+
+ return playerList.get();
+}
+
+CScriptVal GetGameList(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return CScriptVal();
+
+ CScriptValRooted gameList = g_XmppClient->GUIGetGameList();
+
+ return gameList.get();
+}
+
+CScriptVal GetBoardList(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return CScriptVal();
+
+ CScriptValRooted boardList = g_XmppClient->GUIGetBoardList();
+
+ return boardList.get();
+}
+
+CScriptVal LobbyGuiPollMessage(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return CScriptVal();
+
+ CScriptValRooted poll = g_XmppClient->GuiPollMessage();
+
+ return poll.get();
+}
+
+void LobbySendMessage(void* UNUSED(cbdata), std::string message)
+{
+ if (!g_XmppClient)
+ return;
+
+ g_XmppClient->SendMUCMessage(message);
+}
+
+void LobbySetPlayerPresence(void* UNUSED(cbdata), std::string presence)
+{
+ if (!g_XmppClient)
+ return;
+
+ g_XmppClient->SetPresence(presence);
+}
+
+void LobbySetNick(void* UNUSED(cbdata), std::string nick)
+{
+ if (!g_XmppClient)
+ return;
+
+ g_XmppClient->SetNick(nick);
+}
+
+std::string LobbyGetNick(void* UNUSED(cbdata))
+{
+ if (!g_XmppClient)
+ return "";
+
+ std::string nick;
+ g_XmppClient->GetNick(nick);
+ return nick;
+}
+
+void LobbyKick(void* UNUSED(cbdata), std::string nick, std::string reason)
+{
+ if (!g_XmppClient)
+ return;
+
+ g_XmppClient->kick(nick, reason);
+}
+
+void LobbyBan(void* UNUSED(cbdata), std::string nick, std::string reason)
+{
+ if (!g_XmppClient)
+ return;
+
+ g_XmppClient->ban(nick, reason);
+}
+
+std::string LobbyGetPlayerPresence(void* UNUSED(cbdata), std::string nickname)
+{
+ if (!g_XmppClient)
+ return "";
+
+ std::string presence;
+ g_XmppClient->GetPresence(nickname, presence);
+ return presence;
+}
+
+/* End lobby related functions */
+
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;
}
+// Non-public secure PBKDF2 hash function with salting and 1,337 iterations
+void EncryptPassword(const std::string& username, std::string& password)
+{
+ const int DIGESTSIZE = SHA_DIGEST_SIZE;
+ const int ITERATIONS = 1337;
+
+ static const byte salt_base[DIGESTSIZE] = {
+ 244, 243, 249, 244, 32, 33, 34, 35, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 32, 33, 244, 224, 127, 129, 130, 140, 153, 133, 123, 234, 123 };
+
+ // initialize the salt buffer
+ byte salt_buffer[DIGESTSIZE] = {0};
+ SHA256 hash;
+ hash.update(salt_base, username.length());
+ hash.update(username.c_str(), username.length());
+ hash.finish(salt_buffer);
+
+ // PBKDF2 to create the buffer
+ byte encrypted[DIGESTSIZE];
+ pbkdf2(encrypted, (byte*)password.c_str(), password.length(), salt_buffer, DIGESTSIZE, ITERATIONS);
+
+ static const char base16[] = "0123456789ABCDEF";
+ char hex[2 * DIGESTSIZE];
+ for(int i = 0; i < DIGESTSIZE; ++i)
+ {
+ hex[i*2] = base16[encrypted[i] >> 4]; // 4 high bits
+ hex[i*2 + 1] = base16[encrypted[i] & 0x0F];// 4 low bits
+ }
+ password.assign(hex, sizeof(hex));
+}
+
+// Public hash interface.
+std::string EncryptPassword(void* UNUSED(cbdata), std::string user, std::string pass)
+{
+ EncryptPassword(user, pass);
+ return pass;
+}
+
+bool IsRankedGame(void* UNUSED(cbdata))
+{
+ return g_rankedGame;
+}
+
+void SetRankedGame(void* UNUSED(cbdata), bool isRanked)
+{
+ g_rankedGame = isRanked;
+}
+
} // 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("GetMatchID");
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");
+
+#if CONFIG2_LOBBY // Allow the lobby to be disabled
+ // Lobby functions
+ scriptInterface.RegisterFunction("StartXmppClient");
+ scriptInterface.RegisterFunction("StartRegisterXmppClient");
+ scriptInterface.RegisterFunction("HasXmppClient");
+ scriptInterface.RegisterFunction("StopXmppClient");
+ scriptInterface.RegisterFunction("ConnectXmppClient");
+ scriptInterface.RegisterFunction("DisconnectXmppClient");
+ scriptInterface.RegisterFunction("RecvXmppClient");
+ scriptInterface.RegisterFunction("SendGetGameList");
+ scriptInterface.RegisterFunction("SendGetBoardList");
+ scriptInterface.RegisterFunction