-
-
Save Tey/7aff5cb5d7caaa7f49ee820887675eb5 to your computer and use it in GitHub Desktop.
{ | |
// 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 | |
} | |
} |
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') |
Added 'cr' key for color rating (equivalent to "{{c:r}}" XVM macro).
Fixed a bug where colorizeTarget
was not taken into account (Python code expected colorize
instead)
Fix for xTE rating.
Added a way to use specific colors instead of the ones from colors.xc
.
Added a way to set mod verbosity.
There is a bug that is causing the flag to be displayed twice: https://iv.pl/images/218b8259684026908ef014e57b55517d.jpg
Please fix :)
@Aslain Sorry, I haven't played this game for years and it's not even installed anymore on my computers, so I will not be able to provide updates/fixes. However, a quick look at the code makes me believe there might be another mod adding the first flag, because if mod_chat_color
would duplicate the flag, then it would also duplicate the player name which does not seem to be the case in your screenshot.
@Aslain Sorry, I haven't played this game for years and it's not even installed anymore on my computers, so I will not be able to provide updates/fixes. However, a quick look at the code makes me believe there might be another mod adding the first flag, because if
mod_chat_color
would duplicate the flag, then it would also duplicate the player name which does not seem to be the case in your screenshot.
Ok, thanks for your response, the mod was checked with default XVM + mod_chat_color and it was double flags at this point. So no other mod was involed.
I have disabled the flags in config, nobody is using them anyway.
Fixed a bug with players with no stats (new players)