-
-
Save thoroc/86b3ad505d69d9824e817d7aa5da0d90 to your computer and use it in GitHub Desktop.
[WoT] Colorize in battle chat with XVM colors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
// Add the following line to @xvm.rc to register that file: | |
// , "chat": ${"chat.xc":"chat"} | |
// Color values for substitutions. | |
// Значения цветов для подстановок. | |
"def": { | |
// Dynamic color by various statistical parameters. | |
// Динамический цвет по различным статистическим показателям. | |
"colorRating": { | |
"very_bad": "0xFE0E00", // very bad / очень плохо | |
"bad": "0xFE7903", // bad / плохо | |
"normal": "0xF8F400", // normal / средне | |
"good": "0x459300", // good / хорошо | |
"very_good": "0x02C9B3", // very good / очень хорошо | |
"unique": "0xD042F3" // unique / уникально | |
} | |
}, | |
"chat": { | |
// WARNING: this does not use the XVM macros but Python string formating! The list of keys are: | |
// status, hip, cap, vehicleID, alive, ready, flag, wn6, squadnum, spo, xwgr, e, wn8, r, xwn8, | |
// lang, xeff, b, frg, nm, dmg, xwn6, xr, name, winrate, w, wgr, lvl, team, def, cr (color), xte | |
// Colorize the name of message author if true | |
"colorizeAuthor": true, | |
// Colorize the name of targets in messages if true | |
"colorizeTarget": true, | |
// Use color scale from "colors" if set (use the ones from colors.xc otherwise) | |
"customColors": false, | |
// Dynamic color for XVM Scale | |
// Динамический цвет по шкале XVM | |
// http://www.koreanrandom.com/forum/topic/2625-/ | |
"colors": [ | |
{ "value": 16.5, "color": ${"def.colorRating.very_bad" } }, // 00 - 16.5 - very bad (20% of players) | |
{ "value": 33.5, "color": ${"def.colorRating.bad" } }, // 16.5 - 33.5 - bad (better than 20% of players) | |
{ "value": 52.5, "color": ${"def.colorRating.normal" } }, // 33.5 - 52.5 - normal (better than 60% of players) | |
{ "value": 75.5, "color": ${"def.colorRating.good" } }, // 52.5 - 75.5 - good (better than 90% of players) | |
{ "value": 92.5, "color": ${"def.colorRating.very_good"} }, // 75.5 - 92.5 - very good (better than 99% of players) | |
{ "value": 999, "color": ${"def.colorRating.unique" } } // 92.5 - XX - unique (better than 99.9% of players) | |
], | |
// Prefix for the message author | |
"authorPrefix": "<img src='xvm://res/icons/flags/{flag}.png' width='16' height='13'>", | |
// Prefix for target players | |
"prefix": "", | |
// Suffix for the message author | |
"authorSuffix": "", | |
// Suffix for target players | |
"suffix": "", | |
// Log level: -1=NONE, 0=ERROR, 1=WARN, 2=INFO, 3=DEBUG | |
"logLevel": 0 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import string | |
from debug_utils import _doLog | |
from messenger.gui.Scaleform.channels.bw_chat2.battle_controllers import TeamChannelController | |
from messenger.formatters.chat_message import TeamMessageBuilder | |
from helpers import dependency | |
from skeletons.gui.battle_session import IBattleSessionProvider | |
from messenger_common_chat2 import MESSENGER_ACTION_IDS | |
from xvm_main.python.stats import _stat | |
import xvm_main.python.utils as xvm_utils | |
import xvm_main.python.config as config | |
def dump(obj): | |
values = [] | |
for name in dir(obj): | |
attr = getattr(obj, name) | |
if callable(attr): | |
if name.startswith('get') or name.startswith('is'): | |
values.append('%s=%s' % (name, attr())) | |
return '[%s]' % (', '.join(values)) | |
def LOG_ERROR(msg, *kargs, **kwargs): | |
if config.get('chat/logLevel', 0) >= 0: | |
_doLog('ERROR', msg, kargs, kwargs) | |
def LOG_WARN(msg, *kargs, **kwargs): | |
if config.get('chat/logLevel', 0) >= 1: | |
_doLog('WARNING', msg, kargs, kwargs) | |
def LOG_INFO(msg, *kargs, **kwargs): | |
if config.get('chat/logLevel', 0) >= 2: | |
_doLog('INFO', msg, kargs, kwargs) | |
def LOG_DEBUG(msg, *kargs, **kwargs): | |
if config.get('chat/logLevel', 0) >= 3: | |
_doLog('DEBUG', msg, kargs, kwargs) | |
LOG_DEBUG('LOADING') | |
# http://stackoverflow.com/a/19800610/5099839 | |
class StatsFormatter(string.Formatter): | |
def __init__(self, default=0): | |
self.default = default | |
def get_value(self, key, args, kwargs): | |
if isinstance(key, str): | |
v = kwargs.get(key, self.default) | |
return 0 if v is None else v | |
else: | |
Formatter.get_value(key, args, kwargs) | |
class ChatColor(object): | |
sessionProvider = dependency.descriptor(IBattleSessionProvider) | |
# PlayerName_re matches "Name (Vehicle)", "Name[CLAN] (Vehicle)", and "Name[CLAN] (Vehicle (x))" | |
PlayerName_re = re.compile(r'([^\s><\[]+)(\[[^\]]*\])?( \((?:[^\(\)]|\([^\)]*\))+\))') | |
def __init__(self): | |
LOG_DEBUG('ChatColor.__init__()') | |
ChatColor._TeamChannelController_formatCommand = TeamChannelController._formatCommand | |
TeamChannelController._formatCommand = ChatColor.TeamChannelController_formatCommand | |
ChatColor._TeamChannelController_formatMessage = TeamChannelController._formatMessage | |
TeamChannelController._formatMessage = ChatColor.TeamChannelController_formatMessage | |
# Hooking TeamMessageBuilder.setColors() is not enough to set the color of enemy target | |
#ChatColor._TeamMessageBuilder_setColors = TeamMessageBuilder.setColors | |
#TeamMessageBuilder.setColors = ChatColor.TeamMessageBuilder_setColors | |
@staticmethod | |
def getVehIDByPlayerName(playerName): | |
# FIXME: build a map during battle startup and stop iterating every time | |
for vo in ChatColor.sessionProvider.getArenaDP().getVehiclesInfoIterator(): | |
if vo.player.name == playerName: | |
return vo.vehicleID | |
return None | |
@staticmethod | |
def getPlayerStats(vehicleID): | |
# Retrieve player rating and associated color | |
# FIXME: is there any API to retrieve the cached stats from Python!? | |
if vehicleID in _stat.players: | |
pl = _stat.players.get(vehicleID) | |
cacheKey = "%d=%d" % (pl.accountDBID, pl.vehCD) | |
if cacheKey in _stat.cacheBattle: | |
return ChatColor.fixStats(_stat.cacheBattle[cacheKey]) | |
return None | |
@staticmethod | |
def fixStats(stats): | |
if stats is None: | |
return None | |
stats = stats.copy() | |
rating = config.networkServicesSettings.rating | |
if not stats.has_key('xte') and stats.has_key('v') and stats['v'].has_key('xte'): | |
stats['xte'] = stats['v']['xte'] | |
stats['r'] = stats.get(rating, 0) | |
stats['xr'] = stats['r'] if rating.startswith('x') else stats.get('x' + rating, 0) | |
stats['cr'] = ChatColor.getRatingColor(stats) | |
# Stats for new players are incomplete, so use default values (StatsFormatter does most of the job) | |
stats['flag'] = stats.get('flag', 'default') | |
return stats | |
@staticmethod | |
def getRatingColor(stats): | |
if stats is None: | |
return None | |
rating = config.networkServicesSettings.rating | |
v = stats.get(rating, 0) | |
if config.get('chat/customColors', False): | |
# Use custom colors from chat.xc | |
v = stats.get('x' + rating, v) # normalized rating only | |
colors = config.get('chat/colors', None) | |
if colors is None: | |
color = '' | |
else: | |
color = next((int(x['color'], 0) for x in colors if v <= float(x['value'])), 0xFFFFFF) | |
color = "#{0:06x}".format(color) | |
else: | |
# Use colors from colors.xc | |
color = xvm_utils.getDynamicColorValue('x' if rating.startswith('x') else rating, v if v is not None else 0) | |
LOG_DEBUG('%s/%s => %s' % (rating, v, color)) | |
return color | |
@staticmethod | |
def buildExtra(stats, name): | |
if stats is None: | |
return '' | |
extra = config.get('chat/%s' % name, '') | |
return xvm_utils.fixImgTag(StatsFormatter().format(extra, **stats)) | |
@staticmethod | |
def colorize(msg, cmd=None): | |
if cmd is not None: | |
# TODO: permit to add prefix/suffix to command message, and change color of message | |
pass | |
res = u'' | |
pos = 0 | |
is_author = True | |
for m in ChatColor.PlayerName_re.finditer(msg): | |
res += msg[pos:m.start()] | |
pos = m.end() | |
name = m.group(1) | |
clan = m.group(2) | |
vname = m.group(3) | |
repl = m.group(0) | |
vid = ChatColor.getVehIDByPlayerName(name) | |
if vid is None: | |
LOG_WARN('Cannot find VID for player with name "%s"' % name) | |
else: | |
stats = ChatColor.getPlayerStats(vid) | |
if stats is None: | |
LOG_ERROR('Cannot find stats for player with name "%s"' % name) | |
else: | |
color = stats['cr'] | |
prefix = ChatColor.buildExtra(stats, 'authorPrefix' if is_author else 'prefix') | |
suffix = ChatColor.buildExtra(stats, 'authorSuffix' if is_author else 'suffix') | |
colorize = config.get('chat/%s' % ('colorizeAuthor' if is_author else 'colorizeTarget'), True) | |
if colorize: | |
repl = "%s<font color='%s'>%s%s%s</font>%s" % (prefix, color, name, clan if clan is not None else '', vname, suffix) | |
else: | |
repl = "%s%s%s%s%s" % (prefix, name, clan if clan is not None else '', vname, suffix) | |
res += repl | |
is_author = False | |
res += msg[pos:] | |
LOG_DEBUG("'%s' => '%s'" % (msg, res)) | |
return res | |
@staticmethod | |
def TeamChannelController_formatCommand(self, command): | |
fmt = ChatColor._TeamChannelController_formatCommand(self, command) | |
# LOG_DEBUG('TeamChannelController_formatCommand(%s) using %s => %s' % (command, self._mBuilder, fmt)) | |
cmd = MESSENGER_ACTION_IDS.battleChatCommandFromActionID(command.getID()) | |
if cmd is not None: | |
cmd = dict(cmd_name=cmd.msgText, cmd_id=command.getID()) | |
# LOG_DEBUG(cmd) | |
return (fmt[0], ChatColor.colorize(fmt[1], cmd)) | |
@staticmethod | |
def TeamChannelController_formatMessage(self, message, doFormatting=True): | |
fmt = ChatColor._TeamChannelController_formatMessage(self, message, doFormatting) | |
# LOG_DEBUG('TeamChannelController_formatMessage(%s) using %s => %s' % (message, self._mBuilder, fmt)) | |
return (fmt[0], ChatColor.colorize(fmt[1])) | |
@staticmethod | |
def TeamMessageBuilder_setColors(self, dbID): | |
ChatColor._TeamMessageBuilder_setColors(self, dbID) | |
vehicleID = ChatColor.sessionProvider.getArenaDP().getVehIDByAccDBID(dbID) | |
color = ChatColor.getPlayerRatingColor(vehicleID) | |
if color is not None: | |
self._ctx['playerColor'] = color[1:] # remove '#' prefix | |
return self | |
ChatColor() | |
LOG_DEBUG('LOADED') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment