/**
* 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 {
"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")
}
};