Page MenuHomeWildfire Games

No OneTemporary

Index: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/focus-attacked.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Index: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/focus-attacked.png
===================================================================
--- ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/focus-attacked.png (nonexistent)
+++ ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/focus-attacked.png (revision 24565)
Property changes on: ps/trunk/binaries/data/mods/public/art/textures/ui/session/icons/focus-attacked.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+image/png
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/page_session.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/page_session.xml (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/page_session.xml (revision 24565)
@@ -1,20 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<page>
<include>common/modern/setup.xml</include>
<include>common/modern/styles.xml</include>
<include>common/modern/sprites.xml</include>
<include>common/resources/</include>
<include>common/setup.xml</include>
<include>common/sprites.xml</include>
<include>common/styles.xml</include>
+ <include>session/setup.xml</include>
<include>session/sprites.xml</include>
<include>session/styles.xml</include>
<include>session/session.xml</include>
<!-- Work around a render bug:
Needs to be drawn last, as it should overlay everything -->
<include>common/global.xml</include>
</page>
Index: ps/trunk/binaries/data/mods/public/gui/session/chat/ChatHistory.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/chat/ChatHistory.js (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/chat/ChatHistory.js (revision 24565)
@@ -1,134 +1,134 @@
/**
* The objective of this class is to build a message type filter selection and
* to store and display the chat history according to that selection.
*/
class ChatHistory
{
constructor()
{
/**
* All unparsed chat messages received since connect, including timestamp.
*/
this.chatMessages = [];
this.selectionChangeHandlers = [];
this.chatHistoryFilter = Engine.GetGUIObjectByName("chatHistoryFilter");
let filters = prepareForDropdown(this.Filters.filter(chatFilter => !chatFilter.hidden));
this.chatHistoryFilter.list = filters.text.map(text => translateWithContext("chat history filter", text));
this.chatHistoryFilter.list_data = filters.key;
this.chatHistoryFilter.selected = 0;
this.chatHistoryFilter.onSelectionChange = this.onSelectionChange.bind(this);
this.chatHistoryText = Engine.GetGUIObjectByName("chatHistoryText");
}
registerSelectionChangeHandler(handler)
{
this.selectionChangeHandlers.push(handler);
}
/**
* Called each time the history filter changes.
*/
onSelectionChange()
{
this.displayChatHistory();
for (let handler of this.selectionChangeHandlers)
handler();
}
displayChatHistory()
{
let selected = this.chatHistoryFilter.list_data[this.chatHistoryFilter.selected];
this.chatHistoryText.caption =
this.chatMessages.filter(msg => msg.filter[selected]).map(msg =>
Engine.ConfigDB_GetValue("user", "chat.timestamp") == "true" ?
sprintf(translate("%(time)s %(message)s"), {
"time": msg.timePrefix,
"message": msg.txt
}) :
msg.txt).join("\n");
}
onChatMessage(msg, formatted)
{
// Save to chat history
let historical = {
- "txt": formatted,
+ "txt": formatted.text,
"timePrefix": sprintf(translate("\\[%(time)s]"), {
"time": Engine.FormatMillisecondsIntoDateStringLocal(Date.now(), translate("HH:mm"))
}),
"filter": {}
};
// Apply the filters now before diplomacies or playerstates change
let senderID = msg.guid && g_PlayerAssignments[msg.guid] ? g_PlayerAssignments[msg.guid].player : 0;
for (let filter of this.Filters)
historical.filter[filter.key] = filter.filter(msg, senderID);
this.chatMessages.push(historical);
}
}
/**
* Notice only messages will be filtered that are visible to the player in the first place.
*/
ChatHistory.prototype.Filters = [
{
"key": "all",
"text": markForTranslationWithContext("chat history filter", "Chat and notifications"),
"filter": (msg, senderID) => true
},
{
"key": "chat",
"text": markForTranslationWithContext("chat history filter", "Chat messages"),
"filter": (msg, senderID) => msg.type == "message"
},
{
"key": "player",
"text": markForTranslationWithContext("chat history filter", "Players chat"),
"filter": (msg, senderID) =>
msg.type == "message" &&
senderID > 0 && !isPlayerObserver(senderID)
},
{
"key": "ally",
"text": markForTranslationWithContext("chat history filter", "Ally chat"),
"filter": (msg, senderID) =>
msg.type == "message" &&
msg.cmd && msg.cmd == "/allies"
},
{
"key": "enemy",
"text": markForTranslationWithContext("chat history filter", "Enemy chat"),
"filter": (msg, senderID) =>
msg.type == "message" &&
msg.cmd && msg.cmd == "/enemies"
},
{
"key": "observer",
"text": markForTranslationWithContext("chat history filter", "Observer chat"),
"filter": (msg, senderID) =>
msg.type == "message" &&
msg.cmd && msg.cmd == "/observers"
},
{
"key": "private",
"text": markForTranslationWithContext("chat history filter", "Private chat"),
"filter": (msg, senderID) => !!msg.isVisiblePM
},
{
"key": "gamenotifications",
"text": markForTranslationWithContext("chat history filter", "Game notifications"),
"filter": (msg, senderID) => msg.type != "message" && msg.guid === undefined
},
{
"key": "chatnotifications",
"text": markForTranslationWithContext("chat history filter", "Network notifications"),
"filter": (msg, senderID) => msg.type != "message" && msg.guid !== undefined,
"hidden": !Engine.HasNetClient()
}
];
Index: ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatNetwork.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatNetwork.js (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatNetwork.js (revision 24565)
@@ -1,69 +1,77 @@
/**
* This class parses network events sent from the NetClient, such as players connecting or disconnecting from the game.
*/
class ChatMessageFormatNetwork
{
}
ChatMessageFormatNetwork.clientlist = class
{
parse()
{
- return getUsernameList();
+ return { "text": getUsernameList() };
}
};
ChatMessageFormatNetwork.connect = class
{
parse(msg)
{
- return sprintf(
- g_PlayerAssignments[msg.guid].player != -1 ?
- // Translation: A player that left the game joins again
- translate("%(player)s is starting to rejoin the game.") :
- // Translation: A player joins the game for the first time
- translate("%(player)s is starting to join the game."),
- { "player": colorizePlayernameByGUID(msg.guid) });
+ return {
+ "text": sprintf(
+ g_PlayerAssignments[msg.guid].player != -1 ?
+ // Translation: A player that left the game joins again
+ translate("%(player)s is starting to rejoin the game.") :
+ // Translation: A player joins the game for the first time
+ translate("%(player)s is starting to join the game."),
+ { "player": colorizePlayernameByGUID(msg.guid) })
+ };
}
};
ChatMessageFormatNetwork.disconnect = class
{
parse(msg)
{
- return sprintf(translate("%(player)s has left the game."), {
- "player": colorizePlayernameByGUID(msg.guid)
- });
+ return {
+ "text": sprintf(translate("%(player)s has left the game."), {
+ "player": colorizePlayernameByGUID(msg.guid)
+ })
+ };
}
};
ChatMessageFormatNetwork.kicked = class
{
parse(msg)
{
- return sprintf(
- msg.banned ?
- translate("%(username)s has been banned") :
- translate("%(username)s has been kicked"),
- {
- "username": colorizePlayernameHelper(
- msg.username,
- g_Players.findIndex(p => p.name == msg.username)
- )
- });
+ return {
+ "text": sprintf(
+ msg.banned ?
+ translate("%(username)s has been banned") :
+ translate("%(username)s has been kicked"),
+ {
+ "username": colorizePlayernameHelper(
+ msg.username,
+ g_Players.findIndex(p => p.name == msg.username)
+ )
+ })
+ };
}
};
ChatMessageFormatNetwork.rejoined = class
{
parse(msg)
{
- return sprintf(
- g_PlayerAssignments[msg.guid].player != -1 ?
- // Translation: A player that left the game joins again
- translate("%(player)s has rejoined the game.") :
- // Translation: A player joins the game for the first time
- translate("%(player)s has joined the game."),
- { "player": colorizePlayernameByGUID(msg.guid) });
+ return {
+ "text": sprintf(
+ g_PlayerAssignments[msg.guid].player != -1 ?
+ // Translation: A player that left the game joins again
+ translate("%(player)s has rejoined the game.") :
+ // Translation: A player joins the game for the first time
+ translate("%(player)s has joined the game."),
+ { "player": colorizePlayernameByGUID(msg.guid) })
+ };
}
};
Index: ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatPlayer.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatPlayer.js (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatPlayer.js (revision 24565)
@@ -1,166 +1,168 @@
/**
* This class interprets the given message as a chat text sent by a player to a selected addressee.
* It supports the /me command, translation and acoustic notification.
*/
class ChatMessageFormatPlayer
{
constructor()
{
this.AddresseeTypes = [];
}
registerAddresseeTypes(types)
{
this.AddresseeTypes = this.AddresseeTypes.concat(types);
}
parse(msg)
{
if (!msg.text)
return "";
let isMe = msg.text.startsWith("/me ");
if (!isMe && !this.parseMessageAddressee(msg))
return "";
isMe = msg.text.startsWith("/me ");
if (isMe)
msg.text = msg.text.substr("/me ".length);
// Translate or escape text
if (!msg.text)
return "";
if (msg.translate)
{
msg.text = translate(msg.text);
if (msg.translateParameters)
{
let parameters = msg.parameters || {};
translateObjectKeys(parameters, msg.translateParameters);
msg.text = sprintf(msg.text, parameters);
}
}
else
{
msg.text = escapeText(msg.text);
let userName = g_PlayerAssignments[Engine.GetPlayerGUID()].name;
if (userName != g_PlayerAssignments[msg.guid].name &&
msg.text.toLowerCase().indexOf(splitRatingFromNick(userName).nick.toLowerCase()) != -1)
soundNotification("nick");
}
// GUID for players, playerID for AIs
let coloredUsername = msg.guid != -1 ? colorizePlayernameByGUID(msg.guid) : colorizePlayernameByID(msg.player);
- return sprintf(translate(this.strings[isMe ? "me" : "regular"][msg.context ? "context" : "no-context"]), {
- "message": msg.text,
- "context": msg.context ? translateWithContext("chat message context", msg.context) : "",
- "user": coloredUsername,
- "userTag": sprintf(translate("<%(user)s>"), { "user": coloredUsername })
- });
+ return {
+ "text": sprintf(translate(this.strings[isMe ? "me" : "regular"][msg.context ? "context" : "no-context"]), {
+ "message": msg.text,
+ "context": msg.context ? translateWithContext("chat message context", msg.context) : "",
+ "user": coloredUsername,
+ "userTag": sprintf(translate("<%(user)s>"), { "user": coloredUsername })
+ })
+ };
}
/**
* Checks if the current user is an addressee of the chatmessage sent by another player.
* Sets the context and potentially addresseeGUID of that message.
* Returns true if the message should be displayed.
*/
parseMessageAddressee(msg)
{
if (!msg.text.startsWith('/'))
return true;
// Split addressee command and message-text
msg.cmd = msg.text.split(/\s/)[0];
msg.text = msg.text.substr(msg.cmd.length + 1);
// GUID is "local" in single-player, some string in multiplayer.
// Chat messages sent by the simulation (AI) come with the playerID.
let senderID = msg.player ? msg.player : (g_PlayerAssignments[msg.guid] || msg).player;
let isSender = msg.guid ?
msg.guid == Engine.GetPlayerGUID() :
senderID == Engine.GetPlayerID();
// Parse private message
let isPM = msg.cmd == "/msg";
let addresseeGUID;
let addresseeIndex;
if (isPM)
{
addresseeGUID = this.matchUsername(msg.text);
let addressee = g_PlayerAssignments[addresseeGUID];
if (!addressee)
{
if (isSender)
warn("Couldn't match username: " + msg.text);
return false;
}
// Prohibit PM if addressee and sender are identical
if (isSender && addresseeGUID == Engine.GetPlayerGUID())
return false;
msg.text = msg.text.substr(addressee.name.length + 1);
addresseeIndex = addressee.player;
}
// Set context string
let addresseeType = this.AddresseeTypes.find(type => type.command == msg.cmd);
if (!addresseeType)
{
if (isSender)
warn("Unknown chat command: " + msg.cmd);
return false;
}
msg.context = addresseeType.context;
// For observers only permit public- and observer-chat and PM to observers
if (isPlayerObserver(senderID) &&
(isPM && !isPlayerObserver(addresseeIndex) || !isPM && msg.cmd != "/observers"))
return false;
let visible = isSender || addresseeType.isAddressee(senderID, addresseeGUID);
msg.isVisiblePM = isPM && visible;
return visible;
}
/**
* Returns the guid of the user with the longest name that is a prefix of the given string.
*/
matchUsername(text)
{
if (!text)
return "";
let match = "";
let playerGUID = "";
for (let guid in g_PlayerAssignments)
{
let pName = g_PlayerAssignments[guid].name;
if (text.indexOf(pName + " ") == 0 && pName.length > match.length)
{
match = pName;
playerGUID = guid;
}
}
return playerGUID;
}
}
/**
* Chatmessage shown after commands like /me or /enemies.
*/
ChatMessageFormatPlayer.prototype.strings = {
"regular": {
"context": markForTranslation("(%(context)s) %(userTag)s %(message)s"),
"no-context": markForTranslation("%(userTag)s %(message)s")
},
"me": {
"context": markForTranslation("(%(context)s) * %(user)s %(message)s"),
"no-context": markForTranslation("* %(user)s %(message)s")
}
};
Index: ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageFormatSimulation.js (revision 24565)
@@ -1,157 +1,179 @@
/**
* These classes construct a chat message from simulation events initiated from the GuiInterface PushNotification method.
*/
class ChatMessageFormatSimulation
{
}
ChatMessageFormatSimulation.attack = class
{
parse(msg)
{
if (msg.player != g_ViewedPlayer)
return "";
let message = msg.targetIsDomesticAnimal ?
- translate("Your livestock has been attacked by %(attacker)s!") :
- translate("You have been attacked by %(attacker)s!");
+ translate("%(icon)sYour livestock has been attacked by %(attacker)s!") :
+ translate("%(icon)sYou have been attacked by %(attacker)s!");
- return sprintf(message, {
- "attacker": colorizePlayernameByID(msg.attacker)
- });
+ return {
+ "text": sprintf(message, {
+ "icon": '[icon="icon_focusattacked"]',
+ "attacker": colorizePlayernameByID(msg.attacker)
+ }),
+ "callback": ((entityId, position) => function() {
+ if (GetEntityState(entityId))
+ setCameraFollow(entityId);
+ else
+ Engine.SetCameraTarget(position.x, position.y, position.z);
+ })(msg.target, msg.position),
+ "tooltip": translate("Click to focus on the attacked unit.")
+ };
}
};
ChatMessageFormatSimulation.barter = class
{
parse(msg)
{
if (!g_IsObserver || Engine.ConfigDB_GetValue("user", "gui.session.notifications.barter") != "true")
return "";
let amountGiven = {};
amountGiven[msg.resourceGiven] = msg.amountGiven;
let amountGained = {};
amountGained[msg.resourceGained] = msg.amountGained;
- return sprintf(translate("%(player)s bartered %(amountGiven)s for %(amountGained)s."), {
- "player": colorizePlayernameByID(msg.player),
- "amountGiven": getLocalizedResourceAmounts(amountGiven),
- "amountGained": getLocalizedResourceAmounts(amountGained)
- });
+ return {
+ "text": sprintf(translate("%(player)s bartered %(amountGiven)s for %(amountGained)s."), {
+ "player": colorizePlayernameByID(msg.player),
+ "amountGiven": getLocalizedResourceAmounts(amountGiven),
+ "amountGained": getLocalizedResourceAmounts(amountGained)
+ })
+ };
}
};
ChatMessageFormatSimulation.diplomacy = class
{
parse(msg)
{
let messageType;
if (g_IsObserver)
messageType = "observer";
else if (Engine.GetPlayerID() == msg.sourcePlayer)
messageType = "active";
else if (Engine.GetPlayerID() == msg.targetPlayer)
messageType = "passive";
else
return "";
- return sprintf(translate(this.strings[messageType][msg.status]), {
- "player": colorizePlayernameByID(messageType == "active" ? msg.targetPlayer : msg.sourcePlayer),
- "player2": colorizePlayernameByID(messageType == "active" ? msg.sourcePlayer : msg.targetPlayer)
- });
+ return {
+ "text": sprintf(translate(this.strings[messageType][msg.status]), {
+ "player": colorizePlayernameByID(messageType == "active" ? msg.targetPlayer : msg.sourcePlayer),
+ "player2": colorizePlayernameByID(messageType == "active" ? msg.sourcePlayer : msg.targetPlayer)
+ })
+ };
}
};
ChatMessageFormatSimulation.diplomacy.prototype.strings = {
"active": {
"ally": markForTranslation("You are now allied with %(player)s."),
"enemy": markForTranslation("You are now at war with %(player)s."),
"neutral": markForTranslation("You are now neutral with %(player)s.")
},
"passive": {
"ally": markForTranslation("%(player)s is now allied with you."),
"enemy": markForTranslation("%(player)s is now at war with you."),
"neutral": markForTranslation("%(player)s is now neutral with you.")
},
"observer": {
"ally": markForTranslation("%(player)s is now allied with %(player2)s."),
"enemy": markForTranslation("%(player)s is now at war with %(player2)s."),
"neutral": markForTranslation("%(player)s is now neutral with %(player2)s.")
}
};
ChatMessageFormatSimulation.phase = class
{
parse(msg)
{
let notifyPhase = Engine.ConfigDB_GetValue("user", "gui.session.notifications.phase");
if (notifyPhase == "none" || msg.player != g_ViewedPlayer && !g_IsObserver && !g_Players[msg.player].isMutualAlly[g_ViewedPlayer])
return "";
let message = "";
if (notifyPhase == "all")
{
if (msg.phaseState == "started")
message = translate("%(player)s is advancing to the %(phaseName)s.");
else if (msg.phaseState == "aborted")
message = translate("The %(phaseName)s of %(player)s has been aborted.");
}
if (msg.phaseState == "completed")
message = translate("%(player)s has reached the %(phaseName)s.");
- return sprintf(message, {
- "player": colorizePlayernameByID(msg.player),
- "phaseName": getEntityNames(GetTechnologyData(msg.phaseName, g_Players[msg.player].civ))
- });
+ return {
+ "text": sprintf(message, {
+ "player": colorizePlayernameByID(msg.player),
+ "phaseName": getEntityNames(GetTechnologyData(msg.phaseName, g_Players[msg.player].civ))
+ })
+ };
}
};
ChatMessageFormatSimulation.playerstate = class
{
parse(msg)
{
if (!msg.message.pluralMessage)
- return sprintf(translate(msg.message), {
- "player": colorizePlayernameByID(msg.players[0])
- });
+ return {
+ "text": sprintf(translate(msg.message), {
+ "player": colorizePlayernameByID(msg.players[0])
+ })
+ };
let mPlayers = msg.players.map(playerID => colorizePlayernameByID(playerID));
let lastPlayer = mPlayers.pop();
- return sprintf(translatePlural(msg.message.message, msg.message.pluralMessage, msg.message.pluralCount), {
- // Translation: This comma is used for separating first to penultimate elements in an enumeration.
- "players": mPlayers.join(translate(", ")),
- "lastPlayer": lastPlayer
- });
+ return {
+ "text": sprintf(translatePlural(msg.message.message, msg.message.pluralMessage, msg.message.pluralCount), {
+ // Translation: This comma is used for separating first to penultimate elements in an enumeration.
+ "players": mPlayers.join(translate(", ")),
+ "lastPlayer": lastPlayer
+ })
+ };
}
};
/**
* Optionally show all tributes sent in observer mode and tributes sent between allied players.
* Otherwise, only show tributes sent directly to us, and tributes that we send.
*/
ChatMessageFormatSimulation.tribute = class
{
parse(msg)
{
let message = "";
if (msg.targetPlayer == Engine.GetPlayerID())
message = translate("%(player)s has sent you %(amounts)s.");
else if (msg.sourcePlayer == Engine.GetPlayerID())
message = translate("You have sent %(player2)s %(amounts)s.");
else if (Engine.ConfigDB_GetValue("user", "gui.session.notifications.tribute") == "true" &&
(g_IsObserver || g_GameAttributes.settings.LockTeams &&
g_Players[msg.sourcePlayer].isMutualAlly[Engine.GetPlayerID()] &&
g_Players[msg.targetPlayer].isMutualAlly[Engine.GetPlayerID()]))
message = translate("%(player)s has sent %(player2)s %(amounts)s.");
- return sprintf(message, {
- "player": colorizePlayernameByID(msg.sourcePlayer),
- "player2": colorizePlayernameByID(msg.targetPlayer),
- "amounts": getLocalizedResourceAmounts(msg.amounts)
- });
+ return {
+ "text": sprintf(message, {
+ "player": colorizePlayernameByID(msg.sourcePlayer),
+ "player2": colorizePlayernameByID(msg.targetPlayer),
+ "amounts": getLocalizedResourceAmounts(msg.amounts)
+ })
+ };
}
};
Index: ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageHandler.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageHandler.js (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/chat/ChatMessageHandler.js (revision 24565)
@@ -1,86 +1,86 @@
/**
* The purpose of this class is to run a given chat message through parsers until
* one of them succeeds and then call all callback handlers on the result.
*/
class ChatMessageHandler
{
constructor()
{
/**
* Each property is an array of messageformat class instances.
* The classes must have a parse function that receives a
* msg object and translates into a string.
*/
this.messageFormats = {};
/**
* Functions that are called each time a message was parsed.
*/
this.messageHandlers = [];
this.registerMessageFormat("system", new ChatMessageHandler.System());
}
/**
* @param type - a string denoting the messagetype used by addChatMessage calls.
* @param handler - a class instance with a parse function.
*/
registerMessageFormat(type, handler)
{
if (!this.messageFormats[type])
this.messageFormats[type] = [];
this.messageFormats[type].push(handler);
}
/**
* Receives a class where each enumerable owned property is a chat format
* class identified by the property name.
*/
registerMessageFormatClass(formatClass)
{
for (let type in formatClass)
this.registerMessageFormat(type, new formatClass[type]());
}
registerMessageHandler(handler)
{
this.messageHandlers.push(handler);
}
handleMessage(msg)
{
let formatted = this.parseMessage(msg);
if (!formatted)
return;
for (let handler of this.messageHandlers)
handler(msg, formatted);
}
parseMessage(msg)
{
if (!this.messageFormats[msg.type])
{
error("Unknown chat message type: " + uneval(msg));
return undefined;
}
for (let messageFormat of this.messageFormats[msg.type])
{
- let txt = messageFormat.parse(msg);
- if (txt)
- return txt;
+ let formatted = messageFormat.parse(msg);
+ if (formatted)
+ return formatted;
}
return undefined;
}
}
ChatMessageHandler.System = class
{
parse(msg)
{
return msg.txt;
}
};
Index: ps/trunk/binaries/data/mods/public/gui/session/chat/ChatOverlay.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/chat/ChatOverlay.js (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/chat/ChatOverlay.js (revision 24565)
@@ -1,69 +1,104 @@
/**
* This class is concerned with displaying the most recent chat messages on a screen overlay for some seconds.
*/
class ChatOverlay
{
constructor()
{
/**
* Maximum number of lines to display simultaneously.
*/
- this.chatLines = 20;
+ this.chatLinesNumber = 20;
/**
* Number of seconds after which chatmessages will disappear.
*/
this.chatTimeout = 30;
/**
* Holds the timer-IDs used for hiding the chat after chatTimeout seconds.
*/
this.chatTimers = [];
/**
* The currently displayed strings, limited by the given timeframe and limit above.
*/
this.chatMessages = [];
this.chatText = Engine.GetGUIObjectByName("chatText");
+ this.chatLines = Engine.GetGUIObjectByName("chatLines").children;
+ this.chatLinesNumber = Math.min(this.chatLinesNumber, this.chatLines.length);
+ }
+
+ displayChatMessages()
+ {
+ for (let i = 0; i < this.chatLinesNumber; ++i)
+ {
+ let chatMessage = this.chatMessages[i];
+ if (chatMessage && chatMessage.text)
+ {
+ // First scale line width to maximum size.
+ let lineSize = this.chatLines[i].size;
+ let height = lineSize.bottom - lineSize.top;
+ lineSize.top = i * height;
+ lineSize.bottom = lineSize.top + height;
+ lineSize.rright = 100;
+ this.chatLines[i].size = lineSize;
+
+ this.chatLines[i].caption = chatMessage.text;
+
+ // Now read the actual text width and scale the line width accordingly.
+ lineSize.rright = 0;
+ lineSize.right = lineSize.left + this.chatLines[i].getTextSize().width;
+ this.chatLines[i].size = lineSize;
+
+ if (chatMessage.callback)
+ this.chatLines[i].onPress = chatMessage.callback;
+
+ if (chatMessage.tooltip)
+ this.chatLines[i].tooltip = chatMessage.tooltip;
+ }
+ this.chatLines[i].hidden = !chatMessage || !chatMessage.text;
+ this.chatLines[i].ghost = !chatMessage || !chatMessage.callback;
+ }
}
/**
* Displays this message in the chat overlay and sets up the timer to remove it after a while.
*/
onChatMessage(msg, chatMessage)
{
this.chatMessages.push(chatMessage);
this.chatTimers.push(setTimeout(this.removeOldChatMessage.bind(this), this.chatTimeout * 1000));
- if (this.chatMessages.length > this.chatLines)
+ if (this.chatMessages.length > this.chatLinesNumber)
this.removeOldChatMessage();
else
- this.chatText.caption = this.chatMessages.join("\n");
+ this.displayChatMessages();
}
/**
* Empty all messages currently displayed in the chat overlay.
*/
clearChatMessages()
{
this.chatMessages = [];
- this.chatText.caption = "";
+ this.displayChatMessages();
for (let timer of this.chatTimers)
clearTimeout(timer);
this.chatTimers = [];
}
/**
* Called when the timer has run out for the oldest chatmessage or when the message limit is reached.
*/
removeOldChatMessage()
{
clearTimeout(this.chatTimers[0]);
this.chatTimers.shift();
this.chatMessages.shift();
- this.chatText.caption = this.chatMessages.join("\n");
+ this.displayChatMessages();
}
}
Index: ps/trunk/binaries/data/mods/public/gui/session/messages.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/messages.js (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/messages.js (revision 24565)
@@ -1,556 +1,559 @@
/**
* All tutorial messages received so far.
*/
var g_TutorialMessages = [];
/**
* GUI tags applied to the most recent tutorial message.
*/
var g_TutorialNewMessageTags = { "color": "yellow" };
/**
* These handlers are called everytime a client joins or disconnects.
*/
var g_PlayerAssignmentsChangeHandlers = new Set();
/**
* These handlers are called when the ceasefire time has run out.
*/
var g_CeasefireEndedHandlers = new Set();
/**
* These handlers are fired when the match is networked and
* the current client established the connection, authenticated,
* finished the loading screen, starts or finished synchronizing after a rejoin.
* The messages are constructed in NetClient.cpp.
*/
var g_NetworkStatusChangeHandlers = new Set();
/**
* These handlers are triggered whenever a client finishes the loading screen.
*/
var g_ClientsLoadingHandlers = new Set();
/**
* These handlers are fired if the server informed the players that the networked game is out of sync.
*/
var g_NetworkOutOfSyncHandlers = new Set();
/**
* Handle all netmessage types that can occur.
*/
var g_NetMessageTypes = {
"netstatus": msg => {
handleNetStatusMessage(msg);
},
"netwarn": msg => {
addNetworkWarning(msg);
},
"out-of-sync": msg => {
for (let handler of g_NetworkOutOfSyncHandlers)
handler(msg);
},
"players": msg => {
handlePlayerAssignmentsMessage(msg);
},
"paused": msg => {
g_PauseControl.setClientPauseState(msg.guid, msg.pause);
},
"clients-loading": msg => {
for (let handler of g_ClientsLoadingHandlers)
handler(msg.guids);
},
"rejoined": msg => {
addChatMessage({
"type": "rejoined",
"guid": msg.guid
});
},
"kicked": msg => {
addChatMessage({
"type": "kicked",
"username": msg.username,
"banned": msg.banned
});
},
"chat": msg => {
addChatMessage({
"type": "message",
"guid": msg.guid,
"text": msg.text
});
},
"gamesetup": msg => {}, // Needed for autostart
"start": msg => {}
};
var g_PlayerStateMessages = {
"won": translate("You have won!"),
"defeated": translate("You have been defeated!")
};
/**
* Defines how the GUI reacts to notifications that are sent by the simulation.
* Don't open new pages (message boxes) here! Otherwise further notifications
* handled in the same turn can't access the GUI objects anymore.
*/
var g_NotificationsTypes =
{
"aichat": function(notification, player)
{
let message = {
"type": "message",
"text": notification.message,
"guid": findGuidForPlayerID(player) || -1,
"player": player,
"translate": true
};
if (notification.translateParameters)
{
message.translateParameters = notification.translateParameters;
message.parameters = notification.parameters;
colorizePlayernameParameters(notification.parameters);
}
addChatMessage(message);
},
"defeat": function(notification, player)
{
playersFinished(notification.allies, notification.message, false);
},
"won": function(notification, player)
{
playersFinished(notification.allies, notification.message, true);
},
"diplomacy": function(notification, player)
{
updatePlayerData();
g_DiplomacyColors.onDiplomacyChange();
addChatMessage({
"type": "diplomacy",
"sourcePlayer": player,
"targetPlayer": notification.targetPlayer,
"status": notification.status
});
},
"ceasefire-ended": function(notification, player)
{
updatePlayerData();
for (let handler of g_CeasefireEndedHandlers)
handler();
},
"tutorial": function(notification, player)
{
updateTutorial(notification);
},
"tribute": function(notification, player)
{
addChatMessage({
"type": "tribute",
"sourcePlayer": notification.donator,
"targetPlayer": player,
"amounts": notification.amounts
});
},
"barter": function(notification, player)
{
addChatMessage({
"type": "barter",
"player": player,
"amountGiven": notification.amountGiven,
"amountGained": notification.amountGained,
"resourceGiven": notification.resourceGiven,
"resourceGained": notification.resourceGained
});
},
"spy-response": function(notification, player)
{
g_DiplomacyDialog.onSpyResponse(notification, player);
if (notification.entity && g_ViewedPlayer == player)
{
g_DiplomacyDialog.close();
setCameraFollow(notification.entity);
}
},
"attack": function(notification, player)
{
if (player != g_ViewedPlayer)
return;
// Focus camera on attacks
if (g_FollowPlayer)
{
setCameraFollow(notification.target);
g_Selection.reset();
if (notification.target)
g_Selection.addList([notification.target]);
}
if (Engine.ConfigDB_GetValue("user", "gui.session.notifications.attack") !== "true")
return;
+ let entState = GetEntityState(notification.target);
addChatMessage({
"type": "attack",
"player": player,
"attacker": notification.attacker,
+ "target": notification.target,
+ "position": entState && entState.position,
"targetIsDomesticAnimal": notification.targetIsDomesticAnimal
});
},
"phase": function(notification, player)
{
addChatMessage({
"type": "phase",
"player": player,
"phaseName": notification.phaseName,
"phaseState": notification.phaseState
});
},
"dialog": function(notification, player)
{
if (player == Engine.GetPlayerID())
openDialog(notification.dialogName, notification.data, player);
},
"playercommand": function(notification, player)
{
// For observers, focus the camera on units commanded by the selected player
if (!g_FollowPlayer || player != g_ViewedPlayer)
return;
let cmd = notification.cmd;
// Ignore rallypoint commands of trained animals
let entState = cmd.entities && cmd.entities[0] && GetEntityState(cmd.entities[0]);
if (g_ViewedPlayer != 0 &&
entState && entState.identity && entState.identity.classes &&
entState.identity.classes.indexOf("Animal") != -1)
return;
// Focus the structure to build.
if (cmd.type == "repair")
{
let targetState = GetEntityState(cmd.target);
if (targetState)
Engine.CameraMoveTo(targetState.position.x, targetState.position.z);
}
else if (cmd.type == "delete-entities" && notification.position)
Engine.CameraMoveTo(notification.position.x, notification.position.y);
// Focus commanded entities, but don't lose previous focus when training units
else if (cmd.type != "train" && cmd.type != "research" && entState)
setCameraFollow(cmd.entities[0]);
if (["walk", "attack-walk", "patrol"].indexOf(cmd.type) != -1)
DrawTargetMarker(cmd);
// Select units affected by that command
let selection = [];
if (cmd.entities)
selection = cmd.entities;
if (cmd.target)
selection.push(cmd.target);
// Allow gaia in selection when gathering
g_Selection.reset();
g_Selection.addList(selection, false, cmd.type == "gather");
},
"play-tracks": function(notification, player)
{
if (notification.lock)
{
global.music.storeTracks(notification.tracks.map(track => ({ "Type": "custom", "File": track })));
global.music.setState(global.music.states.CUSTOM);
}
global.music.setLocked(notification.lock);
}
};
function registerPlayerAssignmentsChangeHandler(handler)
{
g_PlayerAssignmentsChangeHandlers.add(handler);
}
function registerCeasefireEndedHandler(handler)
{
g_CeasefireEndedHandlers.add(handler);
}
function registerNetworkOutOfSyncHandler(handler)
{
g_NetworkOutOfSyncHandlers.add(handler);
}
function registerNetworkStatusChangeHandler(handler)
{
g_NetworkStatusChangeHandlers.add(handler);
}
function registerClientsLoadingHandler(handler)
{
g_ClientsLoadingHandlers.add(handler);
}
function findGuidForPlayerID(playerID)
{
return Object.keys(g_PlayerAssignments).find(guid => g_PlayerAssignments[guid].player == playerID);
}
/**
* Processes all pending notifications sent from the GUIInterface simulation component.
*/
function handleNotifications()
{
for (let notification of Engine.GuiInterfaceCall("GetNotifications"))
{
if (!notification.players || !notification.type || !g_NotificationsTypes[notification.type])
{
error("Invalid GUI notification: " + uneval(notification));
continue;
}
for (let player of notification.players)
g_NotificationsTypes[notification.type](notification, player);
}
}
function toggleTutorial()
{
let tutorialPanel = Engine.GetGUIObjectByName("tutorialPanel");
tutorialPanel.hidden = !tutorialPanel.hidden || !Engine.GetGUIObjectByName("tutorialText").caption;
}
/**
* Updates the tutorial panel when a new goal.
*/
function updateTutorial(notification)
{
// Show the tutorial panel if not yet done
Engine.GetGUIObjectByName("tutorialPanel").hidden = false;
if (notification.warning)
{
Engine.GetGUIObjectByName("tutorialWarning").caption = coloredText(translate(notification.warning), "orange");
return;
}
let notificationText =
notification.instructions.reduce((instructions, item) =>
instructions + (typeof item == "string" ? translate(item) : colorizeHotkey(translate(item.text), item.hotkey)),
"");
Engine.GetGUIObjectByName("tutorialText").caption = g_TutorialMessages.concat(setStringTags(notificationText, g_TutorialNewMessageTags)).join("\n");
g_TutorialMessages.push(notificationText);
if (notification.readyButton)
{
Engine.GetGUIObjectByName("tutorialReady").hidden = false;
if (notification.leave)
{
Engine.GetGUIObjectByName("tutorialWarning").caption = translate("Click to quit this tutorial.");
Engine.GetGUIObjectByName("tutorialReady").caption = translate("Quit");
Engine.GetGUIObjectByName("tutorialReady").onPress = endGame;
}
else
Engine.GetGUIObjectByName("tutorialWarning").caption = translate("Click when ready.");
}
else
{
Engine.GetGUIObjectByName("tutorialWarning").caption = translate("Follow the instructions.");
Engine.GetGUIObjectByName("tutorialReady").hidden = true;
}
}
/**
* Process every CNetMessage (see NetMessage.h, NetMessages.h) sent by the CNetServer.
* Saves the received object to mainlog.html.
*/
function handleNetMessages()
{
while (true)
{
let msg = Engine.PollNetworkClient();
if (!msg)
return;
log("Net message: " + uneval(msg));
if (g_NetMessageTypes[msg.type])
g_NetMessageTypes[msg.type](msg);
else
error("Unrecognised net message type '" + msg.type + "'");
}
}
function handleNetStatusMessage(message)
{
if (g_Disconnected)
return;
g_IsNetworkedActive = message.status == "active";
if (message.status == "disconnected")
{
g_Disconnected = true;
updateCinemaPath();
closeOpenDialogs();
}
for (let handler of g_NetworkStatusChangeHandlers)
handler(message);
}
function handlePlayerAssignmentsMessage(message)
{
for (let guid in g_PlayerAssignments)
if (!message.newAssignments[guid])
onClientLeave(guid);
let joins = Object.keys(message.newAssignments).filter(guid => !g_PlayerAssignments[guid]);
g_PlayerAssignments = message.newAssignments;
joins.forEach(guid => {
onClientJoin(guid);
});
for (let handler of g_PlayerAssignmentsChangeHandlers)
handler();
// TODO: use subscription instead
updateGUIObjects();
}
function onClientJoin(guid)
{
let playerID = g_PlayerAssignments[guid].player;
if (g_Players[playerID])
{
g_Players[playerID].guid = guid;
g_Players[playerID].name = g_PlayerAssignments[guid].name;
g_Players[playerID].offline = false;
}
addChatMessage({
"type": "connect",
"guid": guid
});
}
function onClientLeave(guid)
{
g_PauseControl.setClientPauseState(guid, false);
for (let id in g_Players)
if (g_Players[id].guid == guid)
g_Players[id].offline = true;
addChatMessage({
"type": "disconnect",
"guid": guid
});
}
function addChatMessage(msg)
{
g_Chat.ChatMessageHandler.handleMessage(msg);
}
function clearChatMessages()
{
g_Chat.ChatOverlay.clearChatMessages();
}
/**
* This function is used for AIs, whose names don't exist in g_PlayerAssignments.
*/
function colorizePlayernameByID(playerID)
{
let username = g_Players[playerID] && escapeText(g_Players[playerID].name);
return colorizePlayernameHelper(username, playerID);
}
function colorizePlayernameByGUID(guid)
{
let username = g_PlayerAssignments[guid] ? g_PlayerAssignments[guid].name : "";
let playerID = g_PlayerAssignments[guid] ? g_PlayerAssignments[guid].player : -1;
return colorizePlayernameHelper(username, playerID);
}
function colorizePlayernameHelper(username, playerID)
{
let playerColor = playerID > -1 ? g_DiplomacyColors.getPlayerColor(playerID) : "white";
return coloredText(username || translate("Unknown Player"), playerColor);
}
/**
* Insert the colorized playername to chat messages sent by the AI and time notifications.
*/
function colorizePlayernameParameters(parameters)
{
for (let param in parameters)
if (param.startsWith("_player_"))
parameters[param] = colorizePlayernameByID(parameters[param]);
}
/**
* Custom dialog response handling, usable by trigger maps.
*/
function sendDialogAnswer(guiObject, dialogName)
{
Engine.GetGUIObjectByName(dialogName + "-dialog").hidden = true;
Engine.PostNetworkCommand({
"type": "dialog-answer",
"dialog": dialogName,
"answer": guiObject.name.split("-").pop(),
});
resumeGame();
}
/**
* Custom dialog opening, usable by trigger maps.
*/
function openDialog(dialogName, data, player)
{
let dialog = Engine.GetGUIObjectByName(dialogName + "-dialog");
if (!dialog)
{
warn("messages.js: Unknow dialog with name " + dialogName);
return;
}
dialog.hidden = false;
for (let objName in data)
{
let obj = Engine.GetGUIObjectByName(dialogName + "-dialog-" + objName);
if (!obj)
{
warn("messages.js: Key '" + objName + "' not found in '" + dialogName + "' dialog.");
continue;
}
for (let key in data[objName])
{
let n = data[objName][key];
if (typeof n == "object" && n.message)
{
let message = n.message;
if (n.translateMessage)
message = translate(message);
let parameters = n.parameters || {};
if (n.translateParameters)
translateObjectKeys(parameters, n.translateParameters);
obj[key] = sprintf(message, parameters);
}
else
obj[key] = n;
}
}
g_PauseControl.implicitPause();
}
Index: ps/trunk/binaries/data/mods/public/gui/session/session.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/session.xml (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/session.xml (revision 24565)
@@ -1,113 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<objects>
<script directory="gui/common/"/>
<script directory="gui/maps/"/>
<script directory="gui/session/"/>
<script directory="gui/session/chat/"/>
<script directory="gui/session/developer_overlay/"/>
<script directory="gui/session/diplomacy/"/>
<script directory="gui/session/diplomacy/playercontrols/"/>
<script directory="gui/session/lobby/"/>
<script directory="gui/session/lobby/LobbyRatingReport/"/>
<script directory="gui/session/message_box/"/>
<script directory="gui/session/minimap/"/>
<script directory="gui/session/objectives/"/>
<script directory="gui/session/top_panel/"/>
<script directory="gui/session/top_panel/IconButtons/"/>
<script directory="gui/session/trade/"/>
<object name="session">
<action on="Tick">
onTick();
</action>
<action on="SavegameLoaded">
restoreSavedGameData(arguments[0]);
</action>
<action on="SimulationUpdate">
onSimulationUpdate();
</action>
<!-- Hotkeys won't work properly unless outside menu -->
<include directory="gui/session/hotkeys/"/>
<include file="gui/session/NetworkStatusOverlay.xml"/>
<include file="gui/session/PauseOverlay.xml"/>
<include file="gui/session/TimeNotificationOverlay.xml"/>
<!-- Chat messages -->
- <object name="chatPanel" size="0 130 100% 100%-240" type="image" ghost="true" z="0" absolute="true">
- <object name="chatText" size="3 1 100%-1 100%-1" type="text" style="chatPanelOverlay" ghost="true"/>
+ <object name="chatPanel" size="0 131 100% 100%-240" z="0" absolute="true">
+ <object name="chatLines">
+ <repeat count="20">
+ <object name="chatLine[n]" size="3 0 100% 17" type="button" style="chatPanelOverlay" tooltip_style="sessionToolTipBottomBold" ghost="true" hidden="true"/>
+ </repeat>
+ </object>
</object>
<include directory="gui/session/chat/"/>
<include directory="gui/session/developer_overlay/"/>
<include directory="gui/session/dialogs/"/>
<include directory="gui/session/diplomacy/"/>
<include directory="gui/session/objectives/"/>
<include file="gui/session/GameSpeedControl.xml"/>
<include file="gui/session/PanelEntities.xml"/>
<include file="gui/session/ResearchProgress.xml"/>
<include file="gui/session/TopPanel.xml"/>
<include file="gui/session/trade/TradeDialog.xml"/>
<include file="gui/session/tutorial_panel.xml"/>
<include file="gui/session/Menu.xml"/>
<!-- Contains miscellanious objects s.a.: the technology research -->
<!-- progress, group selection icons, and the hero selection icon -->
<include directory="gui/session/session_objects/"/>
<!-- Information tooltip -->
<!-- Follows the mouse around if 'independent' is set to 'true'. -->
<object name="informationTooltip" type="tooltip" independent="true" style="informationTooltip"/>
<!-- Structure placement info tooltip -->
<object name="placementTooltip" type="tooltip" independent="true" style="informationTooltip"/>
<!-- START of BOTTOM PANEL -->
<!-- Limit to the minimal supported width of 1024 pixels. -->
<object size="50%-512 0 50%+512 100%">
<object size="50%-512 100%-200 50%-312 100%">
<include directory="gui/session/minimap/"/>
</object>
<!-- Supplemental Details Panel (left). -->
<object name="supplementalSelectionDetails"
size="50%-316 100%-166 50%-110 100%"
sprite="supplementalDetailsPanel"
type="image"
z="20"
>
<include directory="gui/session/selection_panels_left/"/>
</object>
<!-- Selection Details Panel (middle). -->
<object name="selectionDetails"
size="50%-114 100%-200 50%+114 100%"
sprite="selectionDetailsPanel"
type="image"
>
<include directory="gui/session/selection_panels_middle/"/>
</object>
<!-- Commands Panel (right). -->
<object name="unitCommands"
size="50%+110 100%-166 50%+512 100%"
sprite="unitCommandsPanel"
type="image"
z="20"
>
<include directory="gui/session/selection_panels_right/"/>
</object>
</object><!-- END OF BOTTOM PANEL -->
</object> <!-- END OF SESSION OBJECT -->
<!-- Selection bandbox -->
<object name="bandbox" type="image" sprite="bandbox" ghost="true" hidden="true" z="200"/>
</objects>
Index: ps/trunk/binaries/data/mods/public/gui/session/setup.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/setup.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/session/setup.xml (revision 24565)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<setup>
+ <icon name="icon_focusattacked"
+ sprite="stretched:session/icons/focus-attacked.png"
+ size="14 14"
+ />
+</setup>
Index: ps/trunk/binaries/data/mods/public/gui/session/styles.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/styles.xml (revision 24564)
+++ ps/trunk/binaries/data/mods/public/gui/session/styles.xml (revision 24565)
@@ -1,265 +1,265 @@
<?xml version="1.0" encoding="utf-8"?>
<styles>
<!-- ================================ ================================ -->
<!-- Text Styles -->
<!-- ================================ ================================ -->
<style name="DiplomacyText"
font="sans-bold-stroke-14"
textcolor="white"
text_align="left"
text_valign="top"
/>
<style name="PauseText"
font="sans-bold-24"
textcolor="white"
text_align="center"
text_valign="center"
/>
<style name="ResumeMessageText"
font="sans-bold-12"
textcolor="white"
text_align="center"
text_valign="center"
/>
<style name="netStatusPlayersText"
font="sans-bold-16"
textcolor="white"
text_align="center"
text_valign="top"
/>
<style name="leftAlignedText"
textcolor="white"
text_align="left"
text_valign="center"
/>
<style name="rightAlignedText"
textcolor="white"
text_align="right"
text_valign="top"
/>
<style name="centeredText"
textcolor="black"
text_align="center"
text_valign="center"
/>
<style name="largeBoldCenteredText"
font="sans-bold-18"
textcolor="white"
text_align="center"
text_valign="center"
/>
<style name="largeCenteredOutlinedText"
font="sans-bold-stroke-14"
textcolor="white"
text_align="center"
text_valign="center"
/>
<style name="largeLeftOutlinedText"
font="sans-bold-stroke-14"
textcolor="white"
text_align="left"
text_valign="center"
/>
<style name="iconButtonCount"
textcolor="255 255 255"
font="sans-9"
text_align="right"
text_valign="top"
buffer_zone="4"
/>
<style name="iconButtonProgress"
textcolor="255 255 255"
font="sans-stroke-14"
text_align="center"
text_valign="center"
/>
<style name="groupIconsText"
font="sans-bold-stroke-14"
textcolor="white"
text_align="center"
text_valign="center"
/>
<style name="groupIconsCenteredText"
font="mono-stroke-10"
textcolor="255 255 255"
text_align="center"
text_valign="center"
/>
<style name="devCommandsText"
font="sans-10"
textcolor="255 255 255"
text_align="right"
/>
<style name="resourceText"
textcolor="white"
font="sans-bold-stroke-14"
ghost="true"
text_align="left"
text_valign="center"
/>
<style name="gathererCounts"
textcolor="white"
font="sans-stroke-12"
ghost="true"
text_align="right"
text_valign="bottom"
/>
<style name="StatsTextLeft"
font="sans-stroke-12"
textcolor="white"
text_align="left"
text_valign="center"
ghost="true"
/>
<style name="StatsTextCentered"
font="sans-stroke-12"
textcolor="white"
text_align="center"
text_valign="center"
ghost="true"
/>
<style name="StatsTextRight"
font="sans-stroke-12"
textcolor="white"
text_align="right"
text_valign="center"
ghost="true"
/>
<style name="CarryingTextRight"
font="sans-bold-stroke-13"
textcolor="white"
text_align="right"
text_valign="center"
ghost="true"
/>
<style name="SpecificNameCentered"
font="sans-bold-stroke-13"
textcolor="gold"
text_align="center"
text_valign="center"
ghost="true"
/>
<style name="GenericNameCentered"
font="sans-stroke-12"
textcolor="white"
text_align="center"
text_valign="center"
ghost="true"
/>
<style name="dialogTitleText"
font="sans-bold-18"
textcolor="white"
text_align="center"
text_valign="center"
/>
<style name="dialogText"
font="sans-16"
textcolor="white"
text_align="center"
text_valign="center"
/>
<!-- ================================ ================================ -->
<!-- Icon Styles -->
<!-- ================================ ================================ -->
<style name="CivIconOverlay"
sprite=""
sprite_over="CivIconOver"
sound_pressed="audio/interface/ui/ui_button_click.ogg"
/>
<style name="commandIcon"
sprite="command"
ghost="true"
/>
<style name="iconButton"
sprite="snIconPortrait"
sprite_over="snIconPortraitOver"
sprite_disabled="snIconPortraitDisabled"
tooltip_style="snToolTipBottom"
sound_pressed="audio/interface/ui/ui_button_click.ogg"
/>
<!-- ================================ ================================ -->
<!-- Tooltip Styles -->
<!-- ================================ ================================ -->
<style name="resourceCounter"
tooltip_style="snToolTip"
/>
<style name="resourceCounter"
tooltip_style="snToolTip"
/>
<style name="informationTooltip"
anchor="top"
buffer_zone="6"
font="sans-bold-14"
maxwidth="300"
offset="16 32"
sprite="BackgroundInformationTooltip"
textcolor="255 255 255"
/>
<!-- ================================ ================================ -->
<!-- Misc Styles -->
<!-- ================================ ================================ -->
<style name="chatPanelOverlay"
buffer_zone="5"
font="sans-bold-stroke-14"
textcolor="white"
text_align="left"
- text_valign="top"
+ text_valign="center"
/>
<style name="chatInput"
sprite="chatInput"
sprite_selectarea="chatInputHighlight"
textcolor="white"
textcolor_selected="darkgray"
/>
<style name="notificationPanel"
buffer_zone="5"
font="sans-bold-stroke-14"
textcolor="white"
text_align="center"
text_valign="bottom"
/>
<style name="netStatus"
font="sans-bold-18"
textcolor="255 255 255"
text_align="center"
text_valign="center"
sprite="sessionOverlayBackground"
/>
</styles>

File Metadata

Mime Type
text/x-c++
Expires
Sat, May 4, 5:29 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2829076

Event Timeline