Page Menu
Home
Wildfire Games
Search
Configure Global Search
Log In
Files
F4162269
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
53 KB
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-c++
Expires
Sat, May 4, 5:29 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2829076
Attached To
rP 0 A.D. Public Repository
Event Timeline
Log In to Comment