Merge pull request #72 from rtw1x1/main

ML: Fixed HotReload and RTL/LTR integration for UI Arabic
This commit is contained in:
rtw1x1
2026-02-18 20:02:01 +00:00
committed by GitHub
16 changed files with 670 additions and 657 deletions

View File

@@ -55,9 +55,11 @@ class Console(object):
def ReloadLocale(self): def ReloadLocale(self):
"Reload Locale" "Reload Locale"
reload(localeInfo) import app
reload(uiScriptLocale) if app.ReloadLocale():
self.Print("RELOAD LOCALE") self.Print("RELOAD LOCALE OK")
else:
self.Print("RELOAD LOCALE FAILED")
def ReloadDevel(self): def ReloadDevel(self):
"ReloadDevel" "ReloadDevel"

View File

@@ -28,29 +28,7 @@ if EMOTION_VERSION == 2:
EMOTION_FRENCH_KISS = 52 EMOTION_FRENCH_KISS = 52
EMOTION_SLAP = 53 EMOTION_SLAP = 53
EMOTION_DICT = { EMOTION_DICT = {}
EMOTION_CLAP : {"name": localeInfo.EMOTION_CLAP, "command":"/clap"},
EMOTION_DANCE_1 : {"name": localeInfo.EMOTION_DANCE_1, "command":"/dance1"},
EMOTION_DANCE_2 : {"name": localeInfo.EMOTION_DANCE_2, "command":"/dance2"},
EMOTION_DANCE_3 : {"name": localeInfo.EMOTION_DANCE_3, "command":"/dance3"},
EMOTION_DANCE_4 : {"name": localeInfo.EMOTION_DANCE_4, "command":"/dance4"},
EMOTION_DANCE_5 : {"name": localeInfo.EMOTION_DANCE_5, "command":"/dance5"},
EMOTION_DANCE_6 : {"name": localeInfo.EMOTION_DANCE_6, "command":"/dance6"},
EMOTION_CONGRATULATION : {"name": localeInfo.EMOTION_CONGRATULATION, "command":"/congratulation"},
EMOTION_FORGIVE : {"name": localeInfo.EMOTION_FORGIVE, "command":"/forgive"},
EMOTION_ANGRY : {"name": localeInfo.EMOTION_ANGRY, "command":"/angry"},
EMOTION_ATTRACTIVE : {"name": localeInfo.EMOTION_ATTRACTIVE, "command":"/attractive"},
EMOTION_SAD : {"name": localeInfo.EMOTION_SAD, "command":"/sad"},
EMOTION_SHY : {"name": localeInfo.EMOTION_SHY, "command":"/shy"},
EMOTION_CHEERUP : {"name": localeInfo.EMOTION_CHEERUP, "command":"/cheerup"},
EMOTION_BANTER : {"name": localeInfo.EMOTION_BANTER, "command":"/banter"},
EMOTION_JOY : {"name": localeInfo.EMOTION_JOY, "command":"/joy"},
EMOTION_CHEERS_1 : {"name": localeInfo.EMOTION_CHEERS_1, "command":"/cheer1"},
EMOTION_CHEERS_2 : {"name": localeInfo.EMOTION_CHEERS_2, "command":"/cheer2"},
EMOTION_KISS : {"name": localeInfo.EMOTION_CLAP_KISS, "command":"/kiss"},
EMOTION_FRENCH_KISS : {"name": localeInfo.EMOTION_FRENCH_KISS, "command":"/french_kiss"},
EMOTION_SLAP : {"name": localeInfo.EMOTION_SLAP, "command":"/slap"},
}
ICON_DICT = { ICON_DICT = {
EMOTION_CLAP : "d:/ymir work/ui/game/windows/emotion_clap.sub", EMOTION_CLAP : "d:/ymir work/ui/game/windows/emotion_clap.sub",
@@ -128,16 +106,7 @@ elif EMOTION_VERSION == 1:
EMOTION_FRENCH_KISS = 52 EMOTION_FRENCH_KISS = 52
EMOTION_SLAP = 53 EMOTION_SLAP = 53
EMOTION_DICT = { EMOTION_DICT = {}
EMOTION_CLAP : {"name": localeInfo.EMOTION_CLAP, "command":"/clap"},
EMOTION_CHEERS_1 : {"name": localeInfo.EMOTION_CHEERS_1, "command":"/cheer1"},
EMOTION_CHEERS_2 : {"name": localeInfo.EMOTION_CHEERS_2, "command":"/cheer2"},
EMOTION_DANCE_1 : {"name": localeInfo.EMOTION_DANCE_1, "command":"/dance1"},
EMOTION_DANCE_2 : {"name": localeInfo.EMOTION_DANCE_2, "command":"/dance2"},
EMOTION_KISS : {"name": localeInfo.EMOTION_CLAP_KISS, "command":"/kiss"},
EMOTION_FRENCH_KISS : {"name": localeInfo.EMOTION_FRENCH_KISS, "command":"/french_kiss"},
EMOTION_SLAP : {"name": localeInfo.EMOTION_SLAP, "command":"/slap"},
}
ICON_DICT = { ICON_DICT = {
EMOTION_CLAP : "d:/ymir work/ui/game/windows/emotion_clap.sub", EMOTION_CLAP : "d:/ymir work/ui/game/windows/emotion_clap.sub",
@@ -183,14 +152,7 @@ else:
EMOTION_FRENCH_KISS = 52 EMOTION_FRENCH_KISS = 52
EMOTION_SLAP = 53 EMOTION_SLAP = 53
EMOTION_DICT = { EMOTION_DICT = {}
EMOTION_CLAP : {"name": localeInfo.EMOTION_CLAP, "command":"/clap"},
EMOTION_CHEERS_1 : {"name": localeInfo.EMOTION_CHEERS_1, "command":"/cheer1"},
EMOTION_CHEERS_2 : {"name": localeInfo.EMOTION_CHEERS_2, "command":"/cheer2"},
EMOTION_KISS : {"name": localeInfo.EMOTION_CLAP_KISS, "command":"/kiss"},
EMOTION_FRENCH_KISS : {"name": localeInfo.EMOTION_FRENCH_KISS, "command":"/french_kiss"},
EMOTION_SLAP : {"name": localeInfo.EMOTION_SLAP, "command":"/slap"},
}
ICON_DICT = { ICON_DICT = {
EMOTION_CLAP : "d:/ymir work/ui/game/windows/emotion_clap.sub", EMOTION_CLAP : "d:/ymir work/ui/game/windows/emotion_clap.sub",
@@ -225,6 +187,56 @@ else:
} }
def _RebuildLocaleStrings():
global EMOTION_DICT
if EMOTION_VERSION == 2:
EMOTION_DICT = {
EMOTION_CLAP : {"name": localeInfo.EMOTION_CLAP, "command":"/clap"},
EMOTION_DANCE_1 : {"name": localeInfo.EMOTION_DANCE_1, "command":"/dance1"},
EMOTION_DANCE_2 : {"name": localeInfo.EMOTION_DANCE_2, "command":"/dance2"},
EMOTION_DANCE_3 : {"name": localeInfo.EMOTION_DANCE_3, "command":"/dance3"},
EMOTION_DANCE_4 : {"name": localeInfo.EMOTION_DANCE_4, "command":"/dance4"},
EMOTION_DANCE_5 : {"name": localeInfo.EMOTION_DANCE_5, "command":"/dance5"},
EMOTION_DANCE_6 : {"name": localeInfo.EMOTION_DANCE_6, "command":"/dance6"},
EMOTION_CONGRATULATION : {"name": localeInfo.EMOTION_CONGRATULATION, "command":"/congratulation"},
EMOTION_FORGIVE : {"name": localeInfo.EMOTION_FORGIVE, "command":"/forgive"},
EMOTION_ANGRY : {"name": localeInfo.EMOTION_ANGRY, "command":"/angry"},
EMOTION_ATTRACTIVE : {"name": localeInfo.EMOTION_ATTRACTIVE, "command":"/attractive"},
EMOTION_SAD : {"name": localeInfo.EMOTION_SAD, "command":"/sad"},
EMOTION_SHY : {"name": localeInfo.EMOTION_SHY, "command":"/shy"},
EMOTION_CHEERUP : {"name": localeInfo.EMOTION_CHEERUP, "command":"/cheerup"},
EMOTION_BANTER : {"name": localeInfo.EMOTION_BANTER, "command":"/banter"},
EMOTION_JOY : {"name": localeInfo.EMOTION_JOY, "command":"/joy"},
EMOTION_CHEERS_1 : {"name": localeInfo.EMOTION_CHEERS_1, "command":"/cheer1"},
EMOTION_CHEERS_2 : {"name": localeInfo.EMOTION_CHEERS_2, "command":"/cheer2"},
EMOTION_KISS : {"name": localeInfo.EMOTION_CLAP_KISS, "command":"/kiss"},
EMOTION_FRENCH_KISS : {"name": localeInfo.EMOTION_FRENCH_KISS, "command":"/french_kiss"},
EMOTION_SLAP : {"name": localeInfo.EMOTION_SLAP, "command":"/slap"},
}
elif EMOTION_VERSION == 1:
EMOTION_DICT = {
EMOTION_CLAP : {"name": localeInfo.EMOTION_CLAP, "command":"/clap"},
EMOTION_CHEERS_1 : {"name": localeInfo.EMOTION_CHEERS_1, "command":"/cheer1"},
EMOTION_CHEERS_2 : {"name": localeInfo.EMOTION_CHEERS_2, "command":"/cheer2"},
EMOTION_DANCE_1 : {"name": localeInfo.EMOTION_DANCE_1, "command":"/dance1"},
EMOTION_DANCE_2 : {"name": localeInfo.EMOTION_DANCE_2, "command":"/dance2"},
EMOTION_KISS : {"name": localeInfo.EMOTION_CLAP_KISS, "command":"/kiss"},
EMOTION_FRENCH_KISS : {"name": localeInfo.EMOTION_FRENCH_KISS, "command":"/french_kiss"},
EMOTION_SLAP : {"name": localeInfo.EMOTION_SLAP, "command":"/slap"},
}
else:
EMOTION_DICT = {
EMOTION_CLAP : {"name": localeInfo.EMOTION_CLAP, "command":"/clap"},
EMOTION_CHEERS_1 : {"name": localeInfo.EMOTION_CHEERS_1, "command":"/cheer1"},
EMOTION_CHEERS_2 : {"name": localeInfo.EMOTION_CHEERS_2, "command":"/cheer2"},
EMOTION_KISS : {"name": localeInfo.EMOTION_CLAP_KISS, "command":"/kiss"},
EMOTION_FRENCH_KISS : {"name": localeInfo.EMOTION_FRENCH_KISS, "command":"/french_kiss"},
EMOTION_SLAP : {"name": localeInfo.EMOTION_SLAP, "command":"/slap"},
}
_RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(_RebuildLocaleStrings)
def __RegisterSharedEmotionAnis(mode, path): def __RegisterSharedEmotionAnis(mode, path):
chrmgr.SetPathName(path) chrmgr.SetPathName(path)
chrmgr.RegisterMotionMode(mode) chrmgr.RegisterMotionMode(mode)

View File

@@ -590,6 +590,9 @@ class LoginWindow(ui.ScriptWindow):
"""Handle locale change - save config, reload, and refresh UI""" """Handle locale change - save config, reload, and refresh UI"""
import dbg import dbg
# Remember old RTL state to detect direction change
wasRTL = app.IsRTL()
# 1) Save locale code to config/locale.cfg # 1) Save locale code to config/locale.cfg
try: try:
import os import os
@@ -609,7 +612,37 @@ class LoginWindow(ui.ScriptWindow):
dbg.TraceError("Locale changed successfully, refreshing UI...") dbg.TraceError("Locale changed successfully, refreshing UI...")
# 3) Refresh all UI text elements with new locale # 3) If RTL/LTR direction changed, reload UI script in-place
# (different UI scripts, element positioning, text alignment)
if wasRTL != app.IsRTL():
dbg.TraceError("RTL/LTR direction changed, reloading UI script...")
# Save current state before Close() destroys references
wasOnLoginBoard = self.connectBoard and self.connectBoard.IsShow()
savedId = self.idEditLine.GetText() if self.idEditLine else ""
savedPwd = self.pwdEditLine.GetText() if self.pwdEditLine else ""
# Rebuild popup dialog for new direction
self.stream.popupWindow.Destroy()
self.stream.CreatePopupDialog()
# Close and reopen with correct RTL/LTR script
self.Close()
self.Open()
# Restore the board the user was on
if wasOnLoginBoard:
self.__OpenLoginBoard()
if savedId:
self.idEditLine.SetText(savedId)
if savedPwd:
self.pwdEditLine.SetText(savedPwd)
else:
self.__RefreshServerList()
self.__OpenServerBoard()
return
# 4) Same direction - just refresh text elements
self.__RefreshLocaleUI() self.__RefreshLocaleUI()
def __RefreshLocaleUI(self): def __RefreshLocaleUI(self):
@@ -673,10 +706,8 @@ class LoginWindow(ui.ScriptWindow):
self.localeSelector.Show() self.localeSelector.Show()
self.localeSelector.SetTop() self.localeSelector.SetTop()
except: except Exception as e:
# import dbg dbg.TraceError("LoginWindow.__RefreshLocaleUI failed: %s" % str(e))
# dbg.TraceError("LoginWindow.__RefreshLocaleUI failed")
pass
def __SetServerInfo(self, name): def __SetServerInfo(self, name):
net.SetServerInfo(name.strip()) net.SetServerInfo(name.strip())

View File

@@ -20,7 +20,7 @@ FN_GM_MARK = '{:s}/effect/gm.mse'.format(APP_GET_LOCALE_PATH_COMMON)
MAP_TREE2 = 'MAP_TREE2' MAP_TREE2 = 'MAP_TREE2'
ERROR_MARK_UPLOAD_NEED_RECONNECT = 'UploadMark: Reconnect to game' ERROR_MARK_UPLOAD_NEED_RECONNECT = 'UploadMark: Reconnect to game'
ERROR_MARK_CHECK_NEED_RECONNECT = 'CheckMark: Reconnect to game' ERROR_MARK_CHECK_NEED_RECONNECT = 'CheckMark: Reconnect to game'
VIRTUAL_KEY_ALPHABET_LOWERS = r"[1234567890]/qwertyuiop\=asdfghjkl;`'zxcvbnm.," VIRTUAL_KEY_ALPHABET_LOWERS = r"[1234567890]/qwertyuiop\=asdfghjkl;`'zxcvbnm.,"
VIRTUAL_KEY_ALPHABET_UPPERS = r"{1234567890}?QWERTYUIOP|+ASDFGHJKL:~'ZXCVBNM<>" VIRTUAL_KEY_ALPHABET_UPPERS = r"{1234567890}?QWERTYUIOP|+ASDFGHJKL:~'ZXCVBNM<>"
@@ -28,30 +28,60 @@ VIRTUAL_KEY_SYMBOLS = "!@#$%^&*()_+|{}:'<>?~"
VIRTUAL_KEY_NUMBERS = "1234567890-=\\[];',./`" VIRTUAL_KEY_NUMBERS = "1234567890-=\\[];',./`"
VIRTUAL_KEY_SYMBOLS_BR = "!@#$%^&*()_+|{}:'<>?~aaaaeeeiioooouuc" VIRTUAL_KEY_SYMBOLS_BR = "!@#$%^&*()_+|{}:'<>?~aaaaeeeiioooouuc"
# Hot-reload callback registry
_reloadCallbacks = []
def RegisterReloadCallback(callback):
"""Register a function to be called after locale strings are reloaded."""
if callback not in _reloadCallbacks:
_reloadCallbacks.append(callback)
# Multi-language hot-reload support # Multi-language hot-reload support
def LoadLocaleData(): def LoadLocaleData():
""" """
Reload all game locale text strings from locale_game.txt Reload all game locale text strings from locale_game.txt
Called by app.ReloadLocale() when the user changes language. Called by app.ReloadLocale() when the user changes language.
Reloads locale_game.txt and updates all module-level locale strings. Reloads locale_game.txt and rebuilds all derived data structures.
NOTE: Does NOT fire reload callbacks. The C++ reload chain calls
localeInfo.LoadLocaleData() first, then uiScriptLocale.LoadLocaleData()
second. Callbacks are fired by uiScriptLocale.LoadLocaleData() after
BOTH modules are fully reloaded, so callbacks can safely read from
either module.
Returns: Returns:
True on success, False on failure True on success, False on failure
""" """
try: try:
localePath = app.GetLocalePath() global APP_GET_LOCALE_PATH, APP_GET_LOCALE_PATH_COMMON
localeFilePath = "{:s}/locale_game.txt".format(localePath) APP_GET_LOCALE_PATH = app.GetLocalePath()
APP_GET_LOCALE_PATH_COMMON = app.GetLocalePathCommon()
# Reload locale_game.txt - this updates all global variables in this module localeFilePath = "{:s}/locale_game.txt".format(APP_GET_LOCALE_PATH)
LoadLocaleFile(localeFilePath, globals()) LoadLocaleFile(localeFilePath, globals())
_RebuildDerivedData()
return True return True
except: except Exception as e:
# import dbg dbg.TraceError("localeInfo.LoadLocaleData failed: %s" % str(e))
# dbg.TraceError("localeInfo.LoadLocaleData failed")
return False return False
def FireReloadCallbacks():
"""
Fire all registered reload callbacks.
Called by uiScriptLocale.LoadLocaleData() after both locale modules
are fully reloaded, ensuring callbacks see fresh data from both
localeInfo and uiScriptLocale.
"""
for cb in _reloadCallbacks:
try:
cb()
except Exception as e:
dbg.TraceError("localeInfo reload callback failed: %s" % str(e))
# Load locale_game.txt # Load locale_game.txt
def LoadLocaleFile(srcFileName, localeDict): def LoadLocaleFile(srcFileName, localeDict):
def SNA(text): def SNA(text):
@@ -108,7 +138,7 @@ def LoadLocaleFile(srcFileName, localeDict):
dbg.LogBox("%s: line(%d): %s" % (srcFileName, lineIndex, line), "Error") dbg.LogBox("%s: line(%d): %s" % (srcFileName, lineIndex, line), "Error")
raise raise
LoadLocaleFile("{:s}/locale_game.txt".format(APP_GET_LOCALE_PATH), locals()) LoadLocaleFile("{:s}/locale_game.txt".format(APP_GET_LOCALE_PATH), globals())
try: try:
currentLocalePath = app.GetLocalePath() currentLocalePath = app.GetLocalePath()
@@ -117,182 +147,198 @@ try:
except Exception as e: except Exception as e:
dbg.TraceError("localeInfo: Error loading C++ locale data: %s" % str(e)) dbg.TraceError("localeInfo: Error loading C++ locale data: %s" % str(e))
# Option pvp messages def _RebuildDerivedData():
OPTION_PVPMODE_MESSAGE_DICT = { """Rebuild all derived dicts/tuples/lists from current locale strings."""
0: PVP_MODE_NORMAL, global GUILD_BUILDING_LIST_TXT, FN_GM_MARK
1: PVP_MODE_REVENGE, global OPTION_PVPMODE_MESSAGE_DICT, WHISPER_ERROR, error
2: PVP_MODE_KILL, global JOBINFO_TITLE
3: PVP_MODE_PROTECT, global GUILDWAR_NORMAL_DESCLIST, GUILDWAR_WARP_DESCLIST, GUILDWAR_CTF_DESCLIST
4: PVP_MODE_GUILD, global MODE_NAME_LIST, TITLE_NAME_LIST, LEVEL_LIST, HEALTH_LIST
} global USE_SKILL_ERROR_TAIL_DICT, NOTIFY_MESSAGE, ATTACK_ERROR_TAIL_DICT
global SHOT_ERROR_TAIL_DICT, USE_SKILL_ERROR_CHAT_DICT
global SHOP_ERROR_DICT, STAT_MINUS_DESCRIPTION, MINIMAP_ZONE_NAME_DICT
# Whisper messages GUILD_BUILDING_LIST_TXT = '{:s}/GuildBuildingList.txt'.format(APP_GET_LOCALE_PATH)
WHISPER_ERROR = { FN_GM_MARK = '{:s}/effect/gm.mse'.format(APP_GET_LOCALE_PATH_COMMON)
1: CANNOT_WHISPER_NOT_LOGON,
2: CANNOT_WHISPER_DEST_REFUSE,
3: CANNOT_WHISPER_SELF_REFUSE,
}
# Exception of graphic device. # Option pvp messages
error = dict( OPTION_PVPMODE_MESSAGE_DICT = {
CREATE_WINDOW = GAME_INIT_ERROR_MAIN_WINDOW, 0: PVP_MODE_NORMAL,
CREATE_CURSOR = GAME_INIT_ERROR_CURSOR, 1: PVP_MODE_REVENGE,
CREATE_NETWORK = GAME_INIT_ERROR_NETWORK, 2: PVP_MODE_KILL,
CREATE_ITEM_PROTO = GAME_INIT_ERROR_ITEM_PROTO, 3: PVP_MODE_PROTECT,
CREATE_MOB_PROTO = GAME_INIT_ERROR_MOB_PROTO, 4: PVP_MODE_GUILD,
CREATE_NO_DIRECTX = GAME_INIT_ERROR_DIRECTX, }
CREATE_DEVICE = GAME_INIT_ERROR_GRAPHICS_NOT_EXIST,
CREATE_NO_APPROPRIATE_DEVICE = GAME_INIT_ERROR_GRAPHICS_BAD_PERFORMANCE,
CREATE_FORMAT = GAME_INIT_ERROR_GRAPHICS_NOT_SUPPORT_32BIT,
NO_ERROR = str()
)
# Job information (none, skill_group1, skill_group2) # Whisper messages
JOBINFO_TITLE = [ WHISPER_ERROR = {
[JOB_WARRIOR0, JOB_WARRIOR1, JOB_WARRIOR2,], 1: CANNOT_WHISPER_NOT_LOGON,
[JOB_ASSASSIN0, JOB_ASSASSIN1, JOB_ASSASSIN2,], 2: CANNOT_WHISPER_DEST_REFUSE,
[JOB_SURA0, JOB_SURA1, JOB_SURA2,], 3: CANNOT_WHISPER_SELF_REFUSE,
[JOB_SHAMAN0, JOB_SHAMAN1, JOB_SHAMAN2,], }
]
#if app.ENABLE_WOLFMAN_CHARACTER: # Exception of graphic device.
#JOBINFO_TITLE += [[JOB_WOLFMAN0,JOB_WOLFMAN1,JOB_WOLFMAN2,],] error = dict(
CREATE_WINDOW = GAME_INIT_ERROR_MAIN_WINDOW,
CREATE_CURSOR = GAME_INIT_ERROR_CURSOR,
CREATE_NETWORK = GAME_INIT_ERROR_NETWORK,
CREATE_ITEM_PROTO = GAME_INIT_ERROR_ITEM_PROTO,
CREATE_MOB_PROTO = GAME_INIT_ERROR_MOB_PROTO,
CREATE_NO_DIRECTX = GAME_INIT_ERROR_DIRECTX,
CREATE_DEVICE = GAME_INIT_ERROR_GRAPHICS_NOT_EXIST,
CREATE_NO_APPROPRIATE_DEVICE = GAME_INIT_ERROR_GRAPHICS_BAD_PERFORMANCE,
CREATE_FORMAT = GAME_INIT_ERROR_GRAPHICS_NOT_SUPPORT_32BIT,
NO_ERROR = str()
)
# Guild war description # Job information (none, skill_group1, skill_group2)
GUILDWAR_NORMAL_DESCLIST = (GUILD_WAR_USE_NORMAL_MAP, GUILD_WAR_LIMIT_30MIN, GUILD_WAR_WIN_CHECK_SCORE) JOBINFO_TITLE = [
# Guild war warp description [JOB_WARRIOR0, JOB_WARRIOR1, JOB_WARRIOR2,],
GUILDWAR_WARP_DESCLIST = (GUILD_WAR_USE_BATTLE_MAP, GUILD_WAR_WIN_WIPE_OUT_GUILD, GUILD_WAR_REWARD_POTION) [JOB_ASSASSIN0, JOB_ASSASSIN1, JOB_ASSASSIN2,],
# Guild war flag description [JOB_SURA0, JOB_SURA1, JOB_SURA2,],
GUILDWAR_CTF_DESCLIST = (GUILD_WAR_USE_BATTLE_MAP, GUILD_WAR_WIN_TAKE_AWAY_FLAG1, GUILD_WAR_WIN_TAKE_AWAY_FLAG2, GUILD_WAR_REWARD_POTION) [JOB_SHAMAN0, JOB_SHAMAN1, JOB_SHAMAN2,],
]
# Mode of pvp options #if app.ENABLE_WOLFMAN_CHARACTER:
MODE_NAME_LIST = (PVP_OPTION_NORMAL, PVP_OPTION_REVENGE, PVP_OPTION_KILL, PVP_OPTION_PROTECT,) #JOBINFO_TITLE += [[JOB_WOLFMAN0,JOB_WOLFMAN1,JOB_WOLFMAN2,],]
# Title name of alignment
TITLE_NAME_LIST = (PVP_LEVEL0, PVP_LEVEL1, PVP_LEVEL2, PVP_LEVEL3, PVP_LEVEL4, PVP_LEVEL5, PVP_LEVEL6, PVP_LEVEL7, PVP_LEVEL8,)
# Horse levels # Guild war description
LEVEL_LIST = (str(), HORSE_LEVEL1, HORSE_LEVEL2, HORSE_LEVEL3) GUILDWAR_NORMAL_DESCLIST = (GUILD_WAR_USE_NORMAL_MAP, GUILD_WAR_LIMIT_30MIN, GUILD_WAR_WIN_CHECK_SCORE)
# Horse health # Guild war warp description
HEALTH_LIST = (HORSE_HEALTH0, HORSE_HEALTH1, HORSE_HEALTH2, HORSE_HEALTH3) GUILDWAR_WARP_DESCLIST = (GUILD_WAR_USE_BATTLE_MAP, GUILD_WAR_WIN_WIPE_OUT_GUILD, GUILD_WAR_REWARD_POTION)
# Guild war flag description
GUILDWAR_CTF_DESCLIST = (GUILD_WAR_USE_BATTLE_MAP, GUILD_WAR_WIN_TAKE_AWAY_FLAG1, GUILD_WAR_WIN_TAKE_AWAY_FLAG2, GUILD_WAR_REWARD_POTION)
# Use-skill messages # Mode of pvp options
USE_SKILL_ERROR_TAIL_DICT = { MODE_NAME_LIST = (PVP_OPTION_NORMAL, PVP_OPTION_REVENGE, PVP_OPTION_KILL, PVP_OPTION_PROTECT,)
'IN_SAFE': CANNOT_SKILL_SELF_IN_SAFE, # Title name of alignment
'NEED_TARGET': CANNOT_SKILL_NEED_TARGET, TITLE_NAME_LIST = (PVP_LEVEL0, PVP_LEVEL1, PVP_LEVEL2, PVP_LEVEL3, PVP_LEVEL4, PVP_LEVEL5, PVP_LEVEL6, PVP_LEVEL7, PVP_LEVEL8,)
'NEED_EMPTY_BOTTLE': CANNOT_SKILL_NEED_EMPTY_BOTTLE,
'NEED_POISON_BOTTLE': CANNOT_SKILL_NEED_POISON_BOTTLE,
'REMOVE_FISHING_ROD': CANNOT_SKILL_REMOVE_FISHING_ROD,
'NOT_YET_LEARN': CANNOT_SKILL_NOT_YET_LEARN,
'NOT_MATCHABLE_WEAPON': CANNOT_SKILL_NOT_MATCHABLE_WEAPON,
'WAIT_COOLTIME': CANNOT_SKILL_WAIT_COOLTIME,
'NOT_ENOUGH_HP': CANNOT_SKILL_NOT_ENOUGH_HP,
'NOT_ENOUGH_SP': CANNOT_SKILL_NOT_ENOUGH_SP,
'CANNOT_USE_SELF': CANNOT_SKILL_USE_SELF,
'ONLY_FOR_ALLIANCE': CANNOT_SKILL_ONLY_FOR_ALLIANCE,
'CANNOT_ATTACK_ENEMY_IN_SAFE_AREA': CANNOT_SKILL_DEST_IN_SAFE,
'CANNOT_APPROACH': CANNOT_SKILL_APPROACH,
'CANNOT_ATTACK': CANNOT_SKILL_ATTACK,
'ONLY_FOR_CORPSE': CANNOT_SKILL_ONLY_FOR_CORPSE,
'EQUIP_FISHING_ROD': CANNOT_SKILL_EQUIP_FISHING_ROD,
'NOT_HORSE_SKILL': CANNOT_SKILL_NOT_HORSE_SKILL,
'HAVE_TO_RIDE': CANNOT_SKILL_HAVE_TO_RIDE,
}
# Notify messages # Horse levels
NOTIFY_MESSAGE = { LEVEL_LIST = (str(), HORSE_LEVEL1, HORSE_LEVEL2, HORSE_LEVEL3)
'CANNOT_EQUIP_SHOP': CANNOT_EQUIP_IN_SHOP, # Horse health
'CANNOT_EQUIP_EXCHANGE': CANNOT_EQUIP_IN_EXCHANGE, HEALTH_LIST = (HORSE_HEALTH0, HORSE_HEALTH1, HORSE_HEALTH2, HORSE_HEALTH3)
}
# Attack messages # Use-skill messages
ATTACK_ERROR_TAIL_DICT = { USE_SKILL_ERROR_TAIL_DICT = {
'IN_SAFE': CANNOT_ATTACK_SELF_IN_SAFE, 'IN_SAFE': CANNOT_SKILL_SELF_IN_SAFE,
'DEST_IN_SAFE': CANNOT_ATTACK_DEST_IN_SAFE, 'NEED_TARGET': CANNOT_SKILL_NEED_TARGET,
} 'NEED_EMPTY_BOTTLE': CANNOT_SKILL_NEED_EMPTY_BOTTLE,
'NEED_POISON_BOTTLE': CANNOT_SKILL_NEED_POISON_BOTTLE,
'REMOVE_FISHING_ROD': CANNOT_SKILL_REMOVE_FISHING_ROD,
'NOT_YET_LEARN': CANNOT_SKILL_NOT_YET_LEARN,
'NOT_MATCHABLE_WEAPON': CANNOT_SKILL_NOT_MATCHABLE_WEAPON,
'WAIT_COOLTIME': CANNOT_SKILL_WAIT_COOLTIME,
'NOT_ENOUGH_HP': CANNOT_SKILL_NOT_ENOUGH_HP,
'NOT_ENOUGH_SP': CANNOT_SKILL_NOT_ENOUGH_SP,
'CANNOT_USE_SELF': CANNOT_SKILL_USE_SELF,
'ONLY_FOR_ALLIANCE': CANNOT_SKILL_ONLY_FOR_ALLIANCE,
'CANNOT_ATTACK_ENEMY_IN_SAFE_AREA': CANNOT_SKILL_DEST_IN_SAFE,
'CANNOT_APPROACH': CANNOT_SKILL_APPROACH,
'CANNOT_ATTACK': CANNOT_SKILL_ATTACK,
'ONLY_FOR_CORPSE': CANNOT_SKILL_ONLY_FOR_CORPSE,
'EQUIP_FISHING_ROD': CANNOT_SKILL_EQUIP_FISHING_ROD,
'NOT_HORSE_SKILL': CANNOT_SKILL_NOT_HORSE_SKILL,
'HAVE_TO_RIDE': CANNOT_SKILL_HAVE_TO_RIDE,
}
# Shot messages # Notify messages
SHOT_ERROR_TAIL_DICT = { NOTIFY_MESSAGE = {
'EMPTY_ARROW': CANNOT_SHOOT_EMPTY_ARROW, 'CANNOT_EQUIP_SHOP': CANNOT_EQUIP_IN_SHOP,
'IN_SAFE': CANNOT_SHOOT_SELF_IN_SAFE, 'CANNOT_EQUIP_EXCHANGE': CANNOT_EQUIP_IN_EXCHANGE,
'DEST_IN_SAFE': CANNOT_SHOOT_DEST_IN_SAFE, }
}
# Skill messages # Attack messages
USE_SKILL_ERROR_CHAT_DICT = { ATTACK_ERROR_TAIL_DICT = {
'NEED_EMPTY_BOTTLE': SKILL_NEED_EMPTY_BOTTLE, 'IN_SAFE': CANNOT_ATTACK_SELF_IN_SAFE,
'NEED_POISON_BOTTLE': SKILL_NEED_POISON_BOTTLE, 'DEST_IN_SAFE': CANNOT_ATTACK_DEST_IN_SAFE,
'ONLY_FOR_GUILD_WAR': SKILL_ONLY_FOR_GUILD_WAR, }
}
# Shop/private-shop messages # Shot messages
SHOP_ERROR_DICT = { SHOT_ERROR_TAIL_DICT = {
'NOT_ENOUGH_MONEY': SHOP_NOT_ENOUGH_MONEY, 'EMPTY_ARROW': CANNOT_SHOOT_EMPTY_ARROW,
'SOLDOUT': SHOP_SOLDOUT, 'IN_SAFE': CANNOT_SHOOT_SELF_IN_SAFE,
'INVENTORY_FULL': SHOP_INVENTORY_FULL, 'DEST_IN_SAFE': CANNOT_SHOOT_DEST_IN_SAFE,
'INVALID_POS': SHOP_INVALID_POS, }
'NOT_ENOUGH_MONEY_EX': SHOP_NOT_ENOUGH_MONEY_EX,
}
# Character status description # Skill messages
STAT_MINUS_DESCRIPTION = { USE_SKILL_ERROR_CHAT_DICT = {
'HTH-': STAT_MINUS_CON, 'NEED_EMPTY_BOTTLE': SKILL_NEED_EMPTY_BOTTLE,
'INT-': STAT_MINUS_INT, 'NEED_POISON_BOTTLE': SKILL_NEED_POISON_BOTTLE,
'STR-': STAT_MINUS_STR, 'ONLY_FOR_GUILD_WAR': SKILL_ONLY_FOR_GUILD_WAR,
'DEX-': STAT_MINUS_DEX, }
}
# MR-11: Complete map name list # Shop/private-shop messages
# Map names SHOP_ERROR_DICT = {
MINIMAP_ZONE_NAME_DICT = { 'NOT_ENOUGH_MONEY': SHOP_NOT_ENOUGH_MONEY,
'metin2_map_a1': MAP_A1, 'SOLDOUT': SHOP_SOLDOUT,
'map_a2': MAP_A2, 'INVENTORY_FULL': SHOP_INVENTORY_FULL,
'metin2_map_a3': MAP_A3, 'INVALID_POS': SHOP_INVALID_POS,
'metin2_map_b1': MAP_B1, 'NOT_ENOUGH_MONEY_EX': SHOP_NOT_ENOUGH_MONEY_EX,
'map_b2': MAP_B2, }
'metin2_map_b3': MAP_B3,
'metin2_map_c1': MAP_C1, # Character status description
'map_c2': MAP_C2, STAT_MINUS_DESCRIPTION = {
'metin2_map_c3': MAP_C3, 'HTH-': STAT_MINUS_CON,
'map_n_snowm_01': MAP_SNOW, 'INT-': STAT_MINUS_INT,
'metin2_map_n_flame_01': MAP_FLAME, 'STR-': STAT_MINUS_STR,
'metin2_map_n_desert_01': MAP_DESERT, 'DEX-': STAT_MINUS_DEX,
'metin2_map_milgyo': MAP_TEMPLE, }
'metin2_map_monkeydungeon': MAP_MONKEY_DUNGEON,
'metin2_map_monkeydungeon_02': MAP_MONKEY_DUNGEON2, # MR-11: Complete map name list
'metin2_map_monkeydungeon_03': MAP_MONKEY_DUNGEON3, # Map names
'metin2_map_spiderdungeon': MAP_SPIDER, MINIMAP_ZONE_NAME_DICT = {
'metin2_map_spiderdungeon_02': MAP_SPIDERDUNGEON_02, 'metin2_map_a1': MAP_A1,
'metin2_map_spiderdungeon_03': MAP_SPIDERDUNGEON_03, 'map_a2': MAP_A2,
'metin2_map_deviltower1': MAP_DEVILTOWER1, 'metin2_map_a3': MAP_A3,
'metin2_map_devilsCatacomb': MAP_DEVILCATACOMB, 'metin2_map_b1': MAP_B1,
'metin2_map_guild_01': MAP_GUILD_01, 'map_b2': MAP_B2,
'metin2_map_guild_02': MAP_GUILD_02, 'metin2_map_b3': MAP_B3,
'metin2_map_guild_03': MAP_GUILD_03, 'metin2_map_c1': MAP_C1,
'metin2_guild_village_01': GUILD_VILLAGE_01, 'map_c2': MAP_C2,
'metin2_guild_village_02': GUILD_VILLAGE_02, 'metin2_map_c3': MAP_C3,
'metin2_guild_village_03': GUILD_VILLAGE_03, 'map_n_snowm_01': MAP_SNOW,
'metin2_map_trent': MAP_TREE, 'metin2_map_n_flame_01': MAP_FLAME,
'metin2_map_trent02': MAP_TREE2, 'metin2_map_n_desert_01': MAP_DESERT,
'season1/metin2_map_WL_01': MAP_WL, 'metin2_map_milgyo': MAP_TEMPLE,
'season1/metin2_map_nusluck01': MAP_NUSLUCK, 'metin2_map_monkeydungeon': MAP_MONKEY_DUNGEON,
'season1/metin2_map_oxevent': MAP_OXEVENT, 'metin2_map_monkeydungeon_02': MAP_MONKEY_DUNGEON2,
'metin2_map_wedding_01': MAP_WEDDING_01, 'metin2_map_monkeydungeon_03': MAP_MONKEY_DUNGEON3,
'metin2_map_bf': MAP_BATTLE_FIELD, 'metin2_map_spiderdungeon': MAP_SPIDER,
'metin2_map_bf_02': MAP_BATTLE_FIELD, 'metin2_map_spiderdungeon_02': MAP_SPIDERDUNGEON_02,
'metin2_map_bf_03': MAP_BATTLE_FIELD, 'metin2_map_spiderdungeon_03': MAP_SPIDERDUNGEON_03,
'Metin2_map_CapeDragonHead': MAP_CAPE, 'metin2_map_deviltower1': MAP_DEVILTOWER1,
'metin2_map_Mt_Thunder': MAP_THUNDER, 'metin2_map_devilsCatacomb': MAP_DEVILCATACOMB,
'metin2_map_dawnmistwood': MAP_DAWN, 'metin2_map_guild_01': MAP_GUILD_01,
'metin2_map_BayBlackSand': MAP_BAY, 'metin2_map_guild_02': MAP_GUILD_02,
'metin2_map_n_flame_dungeon_01': MAP_N_FLAME_DUNGEON_01, 'metin2_map_guild_03': MAP_GUILD_03,
'metin2_map_n_snow_dungeon_01': MAP_N_SNOW_DUNGEON_01, 'metin2_guild_village_01': GUILD_VILLAGE_01,
'metin2_map_duel': MAP_DUEL, 'metin2_guild_village_02': GUILD_VILLAGE_02,
'season2/metin2_map_skipia_dungeon_01': MAP_SKIPIA_DUNGEON_01, 'metin2_guild_village_03': GUILD_VILLAGE_03,
'metin2_map_skipia_dungeon_02': MAP_SKIPIA_DUNGEON_02, 'metin2_map_trent': MAP_TREE,
'metin2_map_skipia_dungeon_boss': MAP_SKIPIA_DUNGEON_BOSS, 'metin2_map_trent02': MAP_TREE2,
'metin2_map_skipia_dungeon_boss2': MAP_SKIPIA_DUNGEON_BOSS_2, 'season1/metin2_map_WL_01': MAP_WL,
} 'season1/metin2_map_nusluck01': MAP_NUSLUCK,
# MR-11: -- END OF -- Complete map name list 'season1/metin2_map_oxevent': MAP_OXEVENT,
'metin2_map_wedding_01': MAP_WEDDING_01,
'metin2_map_bf': MAP_BATTLE_FIELD,
'metin2_map_bf_02': MAP_BATTLE_FIELD,
'metin2_map_bf_03': MAP_BATTLE_FIELD,
'Metin2_map_CapeDragonHead': MAP_CAPE,
'metin2_map_Mt_Thunder': MAP_THUNDER,
'metin2_map_dawnmistwood': MAP_DAWN,
'metin2_map_BayBlackSand': MAP_BAY,
'metin2_map_n_flame_dungeon_01': MAP_N_FLAME_DUNGEON_01,
'metin2_map_n_snow_dungeon_01': MAP_N_SNOW_DUNGEON_01,
'metin2_map_duel': MAP_DUEL,
'season2/metin2_map_skipia_dungeon_01': MAP_SKIPIA_DUNGEON_01,
'metin2_map_skipia_dungeon_02': MAP_SKIPIA_DUNGEON_02,
'metin2_map_skipia_dungeon_boss': MAP_SKIPIA_DUNGEON_BOSS,
'metin2_map_skipia_dungeon_boss2': MAP_SKIPIA_DUNGEON_BOSS_2,
}
# MR-11: -- END OF -- Complete map name list
_RebuildDerivedData()
# Path of quest icon file # Path of quest icon file
def GetLetterImageName(): def GetLetterImageName():

View File

@@ -39,7 +39,9 @@ class PopupDialog(ui.ScriptWindow):
PythonScriptLoader = ui.PythonScriptLoader() PythonScriptLoader = ui.PythonScriptLoader()
PythonScriptLoader.LoadScriptFile(self, "UIScript/PopupDialog.py") PythonScriptLoader.LoadScriptFile(self, "UIScript/PopupDialog.py")
def Open(self, Message, event = 0, ButtonName = localeInfo.UI_CANCEL): def Open(self, Message, event = 0, ButtonName = None):
if ButtonName is None:
ButtonName = localeInfo.UI_CANCEL
if True == self.IsShow(): if True == self.IsShow():
self.Close() self.Close()

View File

@@ -42,22 +42,25 @@ class CharacterWindow(ui.ScriptWindow):
PAGE_HORSE = 3 PAGE_HORSE = 3
SKILL_GROUP_NAME_DICT = { SKILL_GROUP_NAME_DICT = {}
playerSettingModule.JOB_WARRIOR : { 1 : localeInfo.SKILL_GROUP_WARRIOR_1, 2 : localeInfo.SKILL_GROUP_WARRIOR_2, }, STAT_DESCRIPTION = {}
playerSettingModule.JOB_ASSASSIN : { 1 : localeInfo.SKILL_GROUP_ASSASSIN_1, 2 : localeInfo.SKILL_GROUP_ASSASSIN_2, }, STAT_MINUS_DESCRIPTION = ""
playerSettingModule.JOB_SURA : { 1 : localeInfo.SKILL_GROUP_SURA_1, 2 : localeInfo.SKILL_GROUP_SURA_2, },
playerSettingModule.JOB_SHAMAN : { 1 : localeInfo.SKILL_GROUP_SHAMAN_1, 2 : localeInfo.SKILL_GROUP_SHAMAN_2, },
}
STAT_DESCRIPTION = { @staticmethod
"HTH" : localeInfo.STAT_TOOLTIP_CON, def _RebuildLocaleStrings():
"INT" : localeInfo.STAT_TOOLTIP_INT, CharacterWindow.SKILL_GROUP_NAME_DICT = {
"STR" : localeInfo.STAT_TOOLTIP_STR, playerSettingModule.JOB_WARRIOR : { 1 : localeInfo.SKILL_GROUP_WARRIOR_1, 2 : localeInfo.SKILL_GROUP_WARRIOR_2, },
"DEX" : localeInfo.STAT_TOOLTIP_DEX, playerSettingModule.JOB_ASSASSIN : { 1 : localeInfo.SKILL_GROUP_ASSASSIN_1, 2 : localeInfo.SKILL_GROUP_ASSASSIN_2, },
} playerSettingModule.JOB_SURA : { 1 : localeInfo.SKILL_GROUP_SURA_1, 2 : localeInfo.SKILL_GROUP_SURA_2, },
playerSettingModule.JOB_SHAMAN : { 1 : localeInfo.SKILL_GROUP_SHAMAN_1, 2 : localeInfo.SKILL_GROUP_SHAMAN_2, },
}
STAT_MINUS_DESCRIPTION = localeInfo.STAT_MINUS_DESCRIPTION CharacterWindow.STAT_DESCRIPTION = {
"HTH" : localeInfo.STAT_TOOLTIP_CON,
"INT" : localeInfo.STAT_TOOLTIP_INT,
"STR" : localeInfo.STAT_TOOLTIP_STR,
"DEX" : localeInfo.STAT_TOOLTIP_DEX,
}
CharacterWindow.STAT_MINUS_DESCRIPTION = localeInfo.STAT_MINUS_DESCRIPTION
def __init__(self): def __init__(self):
ui.ScriptWindow.__init__(self) ui.ScriptWindow.__init__(self)
@@ -1341,3 +1344,6 @@ class CharacterWindow(ui.ScriptWindow):
if startIndex != self.questShowingStartIndex: if startIndex != self.questShowingStartIndex:
self.questShowingStartIndex = startIndex self.questShowingStartIndex = startIndex
self.RefreshQuest() self.RefreshQuest()
CharacterWindow._RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(CharacterWindow._RebuildLocaleStrings)

View File

@@ -102,10 +102,16 @@ class ChatModeButton(ui.Window):
## ChatLine ## ChatLine
class ChatLine(ui.EditLine): class ChatLine(ui.EditLine):
CHAT_MODE_NAME = { chat.CHAT_TYPE_TALKING : localeInfo.CHAT_NORMAL, CHAT_MODE_NAME = {}
chat.CHAT_TYPE_PARTY : localeInfo.CHAT_PARTY,
chat.CHAT_TYPE_GUILD : localeInfo.CHAT_GUILD, @staticmethod
chat.CHAT_TYPE_SHOUT : localeInfo.CHAT_SHOUT, } def _RebuildLocaleStrings():
ChatLine.CHAT_MODE_NAME = {
chat.CHAT_TYPE_TALKING : localeInfo.CHAT_NORMAL,
chat.CHAT_TYPE_PARTY : localeInfo.CHAT_PARTY,
chat.CHAT_TYPE_GUILD : localeInfo.CHAT_GUILD,
chat.CHAT_TYPE_SHOUT : localeInfo.CHAT_SHOUT,
}
def __init__(self): def __init__(self):
ui.EditLine.__init__(self) ui.EditLine.__init__(self)
@@ -1163,3 +1169,5 @@ class ChatLogWindow(ui.Window):
else: else:
self.interface.MakeHyperlinkTooltip(hyperlink) self.interface.MakeHyperlinkTooltip(hyperlink)
ChatLine._RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(ChatLine._RebuildLocaleStrings)

View File

@@ -683,10 +683,16 @@ class CommentSlot(ui.Window):
class GuildWindow(ui.ScriptWindow): class GuildWindow(ui.ScriptWindow):
JOB_NAME = { 0 : localeInfo.JOB_WARRIOR, JOB_NAME = {}
@staticmethod
def _RebuildLocaleStrings():
GuildWindow.JOB_NAME = {
0 : localeInfo.JOB_WARRIOR,
1 : localeInfo.JOB_ASSASSIN, 1 : localeInfo.JOB_ASSASSIN,
2 : localeInfo.JOB_SURA, 2 : localeInfo.JOB_SURA,
3 : localeInfo.JOB_SHAMAN, } 3 : localeInfo.JOB_SHAMAN,
}
GUILD_SKILL_PASSIVE_SLOT = 0 GUILD_SKILL_PASSIVE_SLOT = 0
GUILD_SKILL_ACTIVE_SLOT = 1 GUILD_SKILL_ACTIVE_SLOT = 1
@@ -2876,3 +2882,5 @@ Destroy building:
net.SendChatPacket("/build d vid") net.SendChatPacket("/build d vid")
""" """
GuildWindow._RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(GuildWindow._RebuildLocaleStrings)

View File

@@ -1,242 +1,64 @@
""" """
Generic UI Locale Refresh System Generic UI Locale Refresh System
This module provides automatic locale refresh for UI windows without hardcoding element names. Provides RefreshByMapping and RebuildDictionary for hot-reload support.
""" """
import dbg import dbg
import ui
class LocaleRefreshHelper:
"""
Helper class to automatically refresh UI text elements when locale changes.
Works by re-reading the original UI script and applying new locale strings.
"""
def __init__(self):
self.scriptCache = {} # Cache loaded UI scripts
def RefreshWindow(self, window, scriptPath):
"""
Automatically refresh all text elements in a window by re-reading the UI script.
Args:
window: The ui.ScriptWindow instance to refresh
scriptPath: Path to the UI script file (e.g., "UIScript/LoginWindow.py")
Returns:
Number of elements successfully refreshed
"""
import uiScriptLocale
import localeInfo
dbg.TraceError("LocaleRefreshHelper: Refreshing window from %s" % scriptPath)
# Load the UI script to get the original text definitions
try:
scriptData = self._LoadUIScript(scriptPath)
except Exception as e:
dbg.TraceError("LocaleRefreshHelper: Failed to load script %s: %s" % (scriptPath, str(e)))
return 0
# Recursively refresh all elements
refreshCount = self._RefreshElement(window, scriptData.get("window", {}), window)
dbg.TraceError("LocaleRefreshHelper: Refreshed %d elements" % refreshCount)
return refreshCount
def RefreshElementsByMapping(self, elementMap):
"""
Refresh UI elements using a manual mapping dictionary.
Useful for elements that can't be auto-detected.
Args:
elementMap: Dict of {element_instance: locale_string_name}
Example:
mapping = {
self.loginButton: "LOGIN_CONNECT",
self.exitButton: "LOGIN_EXIT"
}
helper.RefreshElementsByMapping(mapping)
"""
import uiScriptLocale
import localeInfo
refreshCount = 0
for element, localeKey in list(elementMap.items()):
try:
# Try uiScriptLocale first, then localeInfo
if hasattr(uiScriptLocale, localeKey):
text = getattr(uiScriptLocale, localeKey)
elif hasattr(localeInfo, localeKey):
text = getattr(localeInfo, localeKey)
else:
dbg.TraceError("LocaleRefreshHelper: Locale key not found: %s" % localeKey)
continue
# Set the text
if hasattr(element, 'SetText'):
element.SetText(text)
refreshCount += 1
except Exception as e:
dbg.TraceError("LocaleRefreshHelper: Failed to refresh element with key %s: %s" % (localeKey, str(e)))
return refreshCount
def RefreshDictionaries(self, targetDict, localeModule="localeInfo"):
"""
Rebuild a dictionary with fresh locale strings.
Useful for error message dictionaries, etc.
Args:
targetDict: Dictionary to rebuild with format {key: "LOCALE_CONSTANT_NAME"}
localeModule: Name of the locale module ("localeInfo" or "uiScriptLocale")
Returns:
New dictionary with fresh locale values
Example:
template = {
"WRONGPWD": "LOGIN_FAILURE_WRONG_PASSWORD",
"FULL": "LOGIN_FAILURE_TOO_MANY_USER"
}
newDict = helper.RefreshDictionaries(template)
"""
import localeInfo
import uiScriptLocale
module = localeInfo if localeModule == "localeInfo" else uiScriptLocale
newDict = {}
for key, localeKey in list(targetDict.items()):
if hasattr(module, localeKey):
newDict[key] = getattr(module, localeKey)
else:
dbg.TraceError("LocaleRefreshHelper: Locale key not found: %s" % localeKey)
return newDict
def _LoadUIScript(self, scriptPath):
"""Load and cache a UI script file."""
if scriptPath in self.scriptCache:
return self.scriptCache[scriptPath]
# Execute the UI script to get its data
scriptData = {}
try:
exec(compile(open(scriptPath, "rb").read(), scriptPath, 'exec'), scriptData)
self.scriptCache[scriptPath] = scriptData
except Exception as e:
dbg.TraceError("LocaleRefreshHelper: Failed to execute script %s: %s" % (scriptPath, str(e)))
raise
return scriptData
def _RefreshElement(self, windowInstance, elementDef, currentElement):
"""
Recursively refresh an element and its children.
Args:
windowInstance: The root window instance
elementDef: Element definition from UI script
currentElement: Current UI element instance
Returns:
Number of elements refreshed
"""
import uiScriptLocale
import localeInfo
refreshCount = 0
# If this element has text defined in the script, refresh it
if isinstance(elementDef, dict) and "text" in elementDef:
textDef = elementDef["text"]
# Check if it's a locale reference (starts with uiScriptLocale or localeInfo)
if isinstance(textDef, str):
text = self._ResolveLocaleString(textDef)
if text and hasattr(currentElement, 'SetText'):
try:
currentElement.SetText(text)
refreshCount += 1
except:
pass
# Recursively process children
if isinstance(elementDef, dict) and "children" in elementDef:
children = elementDef.get("children", [])
for childDef in children:
if isinstance(childDef, dict) and "name" in childDef:
childName = childDef["name"]
try:
childElement = windowInstance.GetChild(childName)
refreshCount += self._RefreshElement(windowInstance, childDef, childElement)
except:
pass
return refreshCount
def _ResolveLocaleString(self, textDef):
"""
Resolve a locale string reference to its current value.
Args:
textDef: String like "uiScriptLocale.LOGIN_CONNECT" or direct text
Returns:
The resolved locale string or None
"""
import uiScriptLocale
import localeInfo
# Check if it's a locale reference
if "uiScriptLocale." in str(textDef):
# Extract the attribute name
parts = str(textDef).split(".")
if len(parts) >= 2:
attrName = parts[-1]
if hasattr(uiScriptLocale, attrName):
return getattr(uiScriptLocale, attrName)
elif "localeInfo." in str(textDef):
parts = str(textDef).split(".")
if len(parts) >= 2:
attrName = parts[-1]
if hasattr(localeInfo, attrName):
return getattr(localeInfo, attrName)
return None
# Global helper instance for easy access
_globalHelper = LocaleRefreshHelper()
def RefreshWindowByScript(window, scriptPath):
"""
Convenience function to refresh a window using its UI script.
Args:
window: The ui.ScriptWindow instance
scriptPath: Path to UI script (e.g., "UIScript/LoginWindow.py")
"""
return _globalHelper.RefreshWindow(window, scriptPath)
def RefreshByMapping(elementMap): def RefreshByMapping(elementMap):
""" """
Convenience function to refresh elements by mapping. Refresh UI elements using a mapping dictionary.
Args: Args:
elementMap: Dict of {element: "LOCALE_KEY"} elementMap: Dict of {element_instance: "LOCALE_KEY_NAME"}
Returns:
Number of elements successfully refreshed
""" """
return _globalHelper.RefreshElementsByMapping(elementMap) import uiScriptLocale
import localeInfo
refreshCount = 0
for element, localeKey in list(elementMap.items()):
try:
# Try uiScriptLocale first, then localeInfo
if hasattr(uiScriptLocale, localeKey):
text = getattr(uiScriptLocale, localeKey)
elif hasattr(localeInfo, localeKey):
text = getattr(localeInfo, localeKey)
else:
dbg.TraceError("LocaleRefresh: key not found: %s" % localeKey)
continue
if hasattr(element, 'SetText'):
element.SetText(text)
refreshCount += 1
except Exception as e:
dbg.TraceError("LocaleRefresh: failed for key %s: %s" % (localeKey, str(e)))
return refreshCount
def RebuildDictionary(template, localeModule="localeInfo"): def RebuildDictionary(template, localeModule="localeInfo"):
""" """
Convenience function to rebuild a dictionary with fresh locale strings. Rebuild a dictionary with fresh locale strings.
Args: Args:
template: Dict of {key: "LOCALE_KEY"} template: Dict of {key: "LOCALE_CONSTANT_NAME"}
localeModule: "localeInfo" or "uiScriptLocale" localeModule: "localeInfo" or "uiScriptLocale"
Returns:
New dictionary with resolved locale values
""" """
return _globalHelper.RefreshDictionaries(template, localeModule) import localeInfo
import uiScriptLocale
module = localeInfo if localeModule == "localeInfo" else uiScriptLocale
newDict = {}
for key, localeKey in list(template.items()):
if hasattr(module, localeKey):
newDict[key] = getattr(module, localeKey)
else:
dbg.TraceError("LocaleRefresh: key not found: %s" % localeKey)
return newDict

View File

@@ -83,8 +83,8 @@ class LocaleSelector(ui.Window):
self.confirmDialog = None self.confirmDialog = None
for btn in self.flagButtons: for btn in self.flagButtons:
btn.SetEvent(None)
btn.Hide() btn.Hide()
btn = None
self.flagButtons = [] self.flagButtons = []
if self.background: if self.background:

View File

@@ -5,9 +5,59 @@ import localeInfo
LOCALE_PATH = uiScriptLocale.MAPNAME_PATH LOCALE_PATH = uiScriptLocale.MAPNAME_PATH
class MapNameShower(ui.ExpandedImageBox): # Static map name -> image suffix mapping (locale-independent)
_MAP_NAME_SUFFIXES = {
"metin2_map_a1": "a1.tga",
"map_a2": "a2.tga",
"metin2_map_a3": "a3.tga",
"metin2_map_b1": "b1.tga",
"map_b2": "b2.tga",
"metin2_map_b3": "b3.tga",
"metin2_map_c1": "c1.tga",
"map_c2": "c2.tga",
"metin2_map_c3": "c3.tga",
"map_n_snowm_01": "snow1.tga",
"metin2_map_deviltower1": "devil1_title.tga",
"metin2_map_n_flame_01": "frame1.tga",
"metin2_map_n_desert_01": "desert1.tga",
"metin2_map_milgyo": "milgyo.tga",
"metin2_map_monkeydungeon": "monkey1.tga",
"metin2_map_monkeydungeon_02": "monkey2.tga",
"metin2_map_monkeydungeon_03": "monkey3.tga",
"metin2_map_guild_01": "guild1.tga",
"metin2_map_guild_02": "guild2.tga",
"metin2_map_guild_03": "guild3.tga",
"metin2_map_trent": "trent.tga",
"metin2_map_trent02": "trent02.tga",
"season2/map_n_snowm_02": "snow2.tga",
"season2/metin2_map_a2_1": "a2_2.tga",
"season2/metin2_map_n_desert_02": "desert2.tga",
"season2/metin2_map_n_flame_02": "frame2.tga",
"season2/metin2_map_milgyo_a": "milgyo2.TGA",
"season2/metin2_map_trent_a": "trent_a.tga",
"season2/metin2_map_trent02_a": "trent02_a.tga",
"season2/metin2_map_skipia_dungeon_01": "skipia.tga",
"season2/metin2_map_skipia_dungeon_02": "skipia.tga",
"metin2_map_devilsCatacomb": "devil_basement.tga",
"metin2_guild_village_01": "a4.tga",
"metin2_guild_village_02": "b4.tga",
"metin2_guild_village_03": "c4.tga",
"metin2_map_BayBlackSand": "bay.tga",
"metin2_map_Mt_Thunder": "thunder.tga",
"metin2_map_dawnmistwood": "dawn.tga",
"Metin2_map_CapeDragonHead": "cape.tga",
"metin2_map_spiderdungeon": "spider1.tga",
"metin2_map_spiderdungeon_02": "spider1.tga",
"metin2_map_spiderdungeon_03": "spider1.tga",
}
MAP_NAME_IMAGE = {} def _RebuildLocaleStrings():
global LOCALE_PATH
LOCALE_PATH = uiScriptLocale.MAPNAME_PATH
localeInfo.RegisterReloadCallback(_RebuildLocaleStrings)
class MapNameShower(ui.ExpandedImageBox):
STATE_HIDE = 0 STATE_HIDE = 0
STATE_FADE_IN = 1 STATE_FADE_IN = 1
@@ -15,51 +65,6 @@ class MapNameShower(ui.ExpandedImageBox):
STATE_FADE_OUT = 3 STATE_FADE_OUT = 3
def __init__(self): def __init__(self):
self.MAP_NAME_IMAGE = {
"metin2_map_a1" : LOCALE_PATH+"a1.tga",
"map_a2" : LOCALE_PATH+"a2.tga",
"metin2_map_a3" : LOCALE_PATH+"a3.tga",
"metin2_map_b1" : LOCALE_PATH+"b1.tga",
"map_b2" : LOCALE_PATH+"b2.tga",
"metin2_map_b3" : LOCALE_PATH+"b3.tga",
"metin2_map_c1" : LOCALE_PATH+"c1.tga",
"map_c2" : LOCALE_PATH+"c2.tga",
"metin2_map_c3" : LOCALE_PATH+"c3.tga",
"map_n_snowm_01" : LOCALE_PATH+"snow1.tga",
"metin2_map_deviltower1" : LOCALE_PATH+"devil1_title.tga",
"metin2_map_n_flame_01" : LOCALE_PATH+"frame1.tga",
"metin2_map_n_desert_01" : LOCALE_PATH+"desert1.tga",
"metin2_map_milgyo" : LOCALE_PATH+"milgyo.tga",
"metin2_map_monkeydungeon" : LOCALE_PATH+"monkey1.tga",
"metin2_map_monkeydungeon_02" : LOCALE_PATH+"monkey2.tga",
"metin2_map_monkeydungeon_03" : LOCALE_PATH+"monkey3.tga",
"metin2_map_guild_01" : LOCALE_PATH+"guild1.tga",
"metin2_map_guild_02" : LOCALE_PATH+"guild2.tga",
"metin2_map_guild_03" : LOCALE_PATH+"guild3.tga",
"metin2_map_trent" : LOCALE_PATH+"trent.tga",
"metin2_map_trent02" : LOCALE_PATH+"trent02.tga",
"season2/map_n_snowm_02": LOCALE_PATH+"snow2.tga",
"season2/metin2_map_a2_1": LOCALE_PATH+"a2_2.tga",
"season2/metin2_map_n_desert_02": LOCALE_PATH+"desert2.tga",
"season2/metin2_map_n_flame_02": LOCALE_PATH+"frame2.tga",
"season2/metin2_map_milgyo_a": LOCALE_PATH+"milgyo2.TGA",
"season2/metin2_map_trent_a": LOCALE_PATH+"trent_a.tga",
"season2/metin2_map_trent02_a": LOCALE_PATH+"trent02_a.tga",
"season2/metin2_map_skipia_dungeon_01": LOCALE_PATH+"skipia.tga",
"season2/metin2_map_skipia_dungeon_02": LOCALE_PATH+"skipia.tga",
"metin2_map_devilsCatacomb" : LOCALE_PATH+"devil_basement.tga",
"metin2_guild_village_01" : LOCALE_PATH+"a4.tga",
"metin2_guild_village_02" : LOCALE_PATH+"b4.tga",
"metin2_guild_village_03" : LOCALE_PATH+"c4.tga",
"metin2_map_BayBlackSand" : LOCALE_PATH+"bay.tga",
"metin2_map_Mt_Thunder" : LOCALE_PATH+"thunder.tga",
"metin2_map_dawnmistwood" : LOCALE_PATH+"dawn.tga",
"Metin2_map_CapeDragonHead" : LOCALE_PATH+"cape.tga",
"metin2_map_spiderdungeon" : LOCALE_PATH+"spider1.tga",
"metin2_map_spiderdungeon_02" : LOCALE_PATH+"spider1.tga",
"metin2_map_spiderdungeon_03" : LOCALE_PATH+"spider1.tga",
}
ui.ExpandedImageBox.__init__(self, "TOP_MOST") ui.ExpandedImageBox.__init__(self, "TOP_MOST")
self.AddFlag("not_pick") self.AddFlag("not_pick")
self.__Initialize() self.__Initialize()
@@ -96,8 +101,9 @@ class MapNameShower(ui.ExpandedImageBox):
elif x > 56000 and y > 38000 and x < 68000 and y < 49000: elif x > 56000 and y > 38000 and x < 68000 and y < 49000:
return 8 return 8
elif x > 56000 and y > 13000 and x < 68000 and y < 23000: elif x > 56000 and y > 13000 and x < 68000 and y < 23000:
return 9 return 9
return 0 return 0
def __GetDevilBase(self, x, y): def __GetDevilBase(self, x, y):
if x > 3000 and y > 4500 and x < 45000 and y < 45000: if x > 3000 and y > 4500 and x < 45000 and y < 45000:
return 1 return 1
@@ -113,14 +119,15 @@ class MapNameShower(ui.ExpandedImageBox):
return 6 return 6
elif x > 5000 and y > 104900 and x < 15000 and y < 122000: elif x > 5000 and y > 104900 and x < 15000 and y < 122000:
return 7 return 7
return 0 return 0
def ShowMapName(self, mapName, x, y): def ShowMapName(self, mapName, x, y):
if mapName not in self.MAP_NAME_IMAGE: if mapName not in _MAP_NAME_SUFFIXES:
print((" [ERROR] - There is no map name image", mapName)) print((" [ERROR] - There is no map name image", mapName))
return return
try: try:
self.LoadImage(self.MAP_NAME_IMAGE[mapName]) self.LoadImage(LOCALE_PATH + _MAP_NAME_SUFFIXES[mapName])
except RuntimeError: except RuntimeError:
return return
@@ -197,8 +204,8 @@ class MapNameShower(ui.ExpandedImageBox):
if self.floorImage: if self.floorImage:
self.floorImage.Hide() self.floorImage.Hide()
self.floorImage = None self.floorImage = None
if self.objectiveImage: if self.objectiveImage:
self.objectiveImage.Hide() self.objectiveImage.Hide()
self.objectiveImage = None self.objectiveImage = None
return return

View File

@@ -28,15 +28,7 @@ class PartyMemberInfoBoard(ui.ScriptWindow):
#PARTY_AFFECT_REGEN_BONUS = 6 #PARTY_AFFECT_REGEN_BONUS = 6
PARTY_AFFECT_INCREASE_AREA_150 = 7 PARTY_AFFECT_INCREASE_AREA_150 = 7
PARTY_AFFECT_INCREASE_AREA_200 = 8 PARTY_AFFECT_INCREASE_AREA_200 = 8
AFFECT_STRING_DICT = { PARTY_AFFECT_EXPERIENCE : localeInfo.PARTY_BONUS_EXP, AFFECT_STRING_DICT = {}
PARTY_AFFECT_ATTACKER : localeInfo.PARTY_BONUS_ATTACKER,
PARTY_AFFECT_TANKER : localeInfo.PARTY_BONUS_TANKER,
PARTY_AFFECT_BUFFER : localeInfo.PARTY_BONUS_BUFFER,
PARTY_AFFECT_SKILL_MASTER : localeInfo.PARTY_BONUS_SKILL_MASTER,
PARTY_AFFECT_BERSERKER : localeInfo.PARTY_BONUS_BERSERKER,
PARTY_AFFECT_DEFENDER : localeInfo.PARTY_BONUS_DEFENDER,
PARTY_AFFECT_INCREASE_AREA_150 : localeInfo.PARTY_INCREASE_AREA_150,
PARTY_AFFECT_INCREASE_AREA_200 : localeInfo.PARTY_INCREASE_AREA_200, }
PARTY_SKILL_HEAL = 1 PARTY_SKILL_HEAL = 1
PARTY_SKILL_WARP = 2 PARTY_SKILL_WARP = 2
@@ -55,12 +47,29 @@ class PartyMemberInfoBoard(ui.ScriptWindow):
MEMBER_BUTTON_WARP : "party_skill_warp", MEMBER_BUTTON_WARP : "party_skill_warp",
MEMBER_BUTTON_EXPEL : "party_expel", } MEMBER_BUTTON_EXPEL : "party_expel", }
STATE_NAME_DICT = { player.PARTY_STATE_ATTACKER : localeInfo.PARTY_SET_ATTACKER, STATE_NAME_DICT = {}
player.PARTY_STATE_BERSERKER : localeInfo.PARTY_SET_BERSERKER,
player.PARTY_STATE_TANKER : localeInfo.PARTY_SET_TANKER, @staticmethod
player.PARTY_STATE_DEFENDER : localeInfo.PARTY_SET_DEFENDER, def _RebuildLocaleStrings():
player.PARTY_STATE_BUFFER : localeInfo.PARTY_SET_BUFFER, PartyMemberInfoBoard.AFFECT_STRING_DICT = {
player.PARTY_STATE_SKILL_MASTER : localeInfo.PARTY_SET_SKILL_MASTER, } PartyMemberInfoBoard.PARTY_AFFECT_EXPERIENCE : localeInfo.PARTY_BONUS_EXP,
PartyMemberInfoBoard.PARTY_AFFECT_ATTACKER : localeInfo.PARTY_BONUS_ATTACKER,
PartyMemberInfoBoard.PARTY_AFFECT_TANKER : localeInfo.PARTY_BONUS_TANKER,
PartyMemberInfoBoard.PARTY_AFFECT_BUFFER : localeInfo.PARTY_BONUS_BUFFER,
PartyMemberInfoBoard.PARTY_AFFECT_SKILL_MASTER : localeInfo.PARTY_BONUS_SKILL_MASTER,
PartyMemberInfoBoard.PARTY_AFFECT_BERSERKER : localeInfo.PARTY_BONUS_BERSERKER,
PartyMemberInfoBoard.PARTY_AFFECT_DEFENDER : localeInfo.PARTY_BONUS_DEFENDER,
PartyMemberInfoBoard.PARTY_AFFECT_INCREASE_AREA_150 : localeInfo.PARTY_INCREASE_AREA_150,
PartyMemberInfoBoard.PARTY_AFFECT_INCREASE_AREA_200 : localeInfo.PARTY_INCREASE_AREA_200,
}
PartyMemberInfoBoard.STATE_NAME_DICT = {
player.PARTY_STATE_ATTACKER : localeInfo.PARTY_SET_ATTACKER,
player.PARTY_STATE_BERSERKER : localeInfo.PARTY_SET_BERSERKER,
player.PARTY_STATE_TANKER : localeInfo.PARTY_SET_TANKER,
player.PARTY_STATE_DEFENDER : localeInfo.PARTY_SET_DEFENDER,
player.PARTY_STATE_BUFFER : localeInfo.PARTY_SET_BUFFER,
player.PARTY_STATE_SKILL_MASTER : localeInfo.PARTY_SET_SKILL_MASTER,
}
def __init__(self): def __init__(self):
ui.ScriptWindow.__init__(self) ui.ScriptWindow.__init__(self)
@@ -117,6 +126,9 @@ class PartyMemberInfoBoard(ui.ScriptWindow):
self.nameTextLine = None self.nameTextLine = None
self.gauge = None self.gauge = None
self.stateButton = None self.stateButton = None
for img in self.partyAffectImageList:
img.OnMouseOverIn = None
img.OnMouseOverOut = None
self.partyAffectImageList = [] self.partyAffectImageList = []
self.stateButtonDict = {} self.stateButtonDict = {}
@@ -388,7 +400,10 @@ class PartyMemberInfoBoard(ui.ScriptWindow):
class PartyMenu(ui.ThinBoard): class PartyMenu(ui.ThinBoard):
BUTTON_NAME = ( localeInfo.PARTY_HEAL_ALL_MEMBER, localeInfo.PARTY_BREAK_UP, localeInfo.PARTY_LEAVE ) # Locale-independent button identifiers (never use translated strings as dict keys)
BTN_HEAL_ALL = 0
BTN_BREAK_UP = 1
BTN_LEAVE = 2
def __init__(self): def __init__(self):
ui.ThinBoard.__init__(self) ui.ThinBoard.__init__(self)
@@ -441,28 +456,28 @@ class PartyMenu(ui.ThinBoard):
self.ChangePartyParameter(self.distributionMode) self.ChangePartyParameter(self.distributionMode)
def __CreateButtons(self): def __CreateButtons(self):
buttonConfig = (
(self.BTN_HEAL_ALL, "PARTY_HEAL_ALL_MEMBER",
"d:/ymir work/ui/game/windows/Party_Skill_Heal",
ui.__mem_func__(self.OnPartyUseSkill)),
(self.BTN_BREAK_UP, "PARTY_BREAK_UP",
"d:/ymir work/ui/game/windows/Party_Disband",
net.SendPartyExitPacket),
(self.BTN_LEAVE, "PARTY_LEAVE",
"d:/ymir work/ui/game/windows/Party_Exit",
net.SendPartyExitPacket),
)
for name in self.BUTTON_NAME: for btnId, localeAttr, imgBase, event in buttonConfig:
button = ui.Button() button = ui.Button()
button.SetParent(self) button.SetParent(self)
button.SetWindowHorizontalAlignCenter() button.SetWindowHorizontalAlignCenter()
button.SetToolTipText(name) button.SetToolTipText(getattr(localeInfo, localeAttr))
self.buttonDict[name] = button button.SetUpVisual(imgBase + "_01.sub")
button.SetOverVisual(imgBase + "_02.sub")
self.buttonDict[localeInfo.PARTY_HEAL_ALL_MEMBER].SetEvent(ui.__mem_func__(self.OnPartyUseSkill)) button.SetDownVisual(imgBase + "_03.sub")
self.buttonDict[localeInfo.PARTY_HEAL_ALL_MEMBER].SetUpVisual("d:/ymir work/ui/game/windows/Party_Skill_Heal_01.sub") button.SetEvent(event)
self.buttonDict[localeInfo.PARTY_HEAL_ALL_MEMBER].SetOverVisual("d:/ymir work/ui/game/windows/Party_Skill_Heal_02.sub") self.buttonDict[btnId] = button
self.buttonDict[localeInfo.PARTY_HEAL_ALL_MEMBER].SetDownVisual("d:/ymir work/ui/game/windows/Party_Skill_Heal_03.sub")
self.buttonDict[localeInfo.PARTY_BREAK_UP].SetEvent(net.SendPartyExitPacket)
self.buttonDict[localeInfo.PARTY_BREAK_UP].SetUpVisual("d:/ymir work/ui/game/windows/Party_Disband_01.sub")
self.buttonDict[localeInfo.PARTY_BREAK_UP].SetOverVisual("d:/ymir work/ui/game/windows/Party_Disband_02.sub")
self.buttonDict[localeInfo.PARTY_BREAK_UP].SetDownVisual("d:/ymir work/ui/game/windows/Party_Disband_03.sub")
self.buttonDict[localeInfo.PARTY_LEAVE].SetEvent(net.SendPartyExitPacket)
self.buttonDict[localeInfo.PARTY_LEAVE].SetUpVisual("d:/ymir work/ui/game/windows/Party_Exit_01.sub")
self.buttonDict[localeInfo.PARTY_LEAVE].SetOverVisual("d:/ymir work/ui/game/windows/Party_Exit_02.sub")
self.buttonDict[localeInfo.PARTY_LEAVE].SetDownVisual("d:/ymir work/ui/game/windows/Party_Exit_03.sub")
def __ClearShowingButtons(self): def __ClearShowingButtons(self):
self.showingButtonList = [] self.showingButtonList = []
@@ -507,19 +522,19 @@ class PartyMenu(ui.ThinBoard):
def ShowLeaderButton(self): def ShowLeaderButton(self):
self.isLeader = True self.isLeader = True
self.__ClearShowingButtons() self.__ClearShowingButtons()
self.__ShowButton(localeInfo.PARTY_BREAK_UP) self.__ShowButton(self.BTN_BREAK_UP)
def ShowMemberButton(self): def ShowMemberButton(self):
self.isLeader = False self.isLeader = False
self.__ClearShowingButtons() self.__ClearShowingButtons()
self.__ShowButton(localeInfo.PARTY_LEAVE) self.__ShowButton(self.BTN_LEAVE)
def OnPartyUseSkill(self): def OnPartyUseSkill(self):
net.SendPartyUseSkillPacket(PartyMemberInfoBoard.PARTY_SKILL_HEAL, 0) net.SendPartyUseSkillPacket(PartyMemberInfoBoard.PARTY_SKILL_HEAL, 0)
self.__HideButton(localeInfo.PARTY_HEAL_ALL_MEMBER) self.__HideButton(self.BTN_HEAL_ALL)
def PartyHealReady(self): def PartyHealReady(self):
self.__ShowButton(localeInfo.PARTY_HEAL_ALL_MEMBER) self.__ShowButton(self.BTN_HEAL_ALL)
def __UpAllModeButtons(self): def __UpAllModeButtons(self):
for button in list(self.modeButtonList.values()): for button in list(self.modeButtonList.values()):
@@ -735,3 +750,6 @@ class PartyWindow(ui.Window):
self.partyMenuButton.SetOverVisual("d:/ymir work/ui/game/windows/Party_Menu_Close_02.sub") self.partyMenuButton.SetOverVisual("d:/ymir work/ui/game/windows/Party_Menu_Close_02.sub")
self.partyMenuButton.SetDownVisual("d:/ymir work/ui/game/windows/Party_Menu_Close_03.sub") self.partyMenuButton.SetDownVisual("d:/ymir work/ui/game/windows/Party_Menu_Close_03.sub")
self.partyMenu.Show() self.partyMenu.Show()
PartyMemberInfoBoard._RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(PartyMemberInfoBoard._RebuildLocaleStrings)

View File

@@ -47,7 +47,7 @@ EMPIREDESC_B = "%s/empiredesc_b.txt" % (name)
EMPIREDESC_C = "%s/empiredesc_c.txt" % (name) EMPIREDESC_C = "%s/empiredesc_c.txt" % (name)
LOCALE_INTERFACE_FILE_NAME = "%s/locale_interface.txt" % (name) LOCALE_INTERFACE_FILE_NAME = "%s/locale_interface.txt" % (name)
LoadLocaleFile(LOCALE_INTERFACE_FILE_NAME, locals()) LoadLocaleFile(LOCALE_INTERFACE_FILE_NAME, globals())
def LoadLocaleData(): def LoadLocaleData():
""" """
@@ -56,6 +56,10 @@ def LoadLocaleData():
Called by app.ReloadLocale() when the user changes language. Called by app.ReloadLocale() when the user changes language.
Updates all locale-dependent paths and reloads locale_interface.txt. Updates all locale-dependent paths and reloads locale_interface.txt.
This is the LAST Python module reloaded by the C++ reload chain,
so it fires localeInfo.FireReloadCallbacks() at the end to notify
all modules that both localeInfo and uiScriptLocale are fully loaded.
Returns: Returns:
True on success, False on failure True on success, False on failure
""" """
@@ -88,8 +92,12 @@ def LoadLocaleData():
# Reload locale_interface.txt - this updates all UI strings # Reload locale_interface.txt - this updates all UI strings
LoadLocaleFile(LOCALE_INTERFACE_FILE_NAME, globals()) LoadLocaleFile(LOCALE_INTERFACE_FILE_NAME, globals())
# Fire reload callbacks now that BOTH modules are fully loaded
import localeInfo
localeInfo.FireReloadCallbacks()
return True return True
except: except Exception as e:
# import dbg import dbg
# dbg.TraceError("uiScriptLocale.LoadLocaleData failed") dbg.TraceError("uiScriptLocale.LoadLocaleData failed: %s" % str(e))
return False return False

View File

@@ -3,8 +3,15 @@ import ui
import localeInfo import localeInfo
import uiScriptLocale import uiScriptLocale
FILE_NAME_LEN = 20 FILE_NAME_LEN = 20
DEFAULT_THEMA = localeInfo.MUSIC_METIN2_DEFAULT_THEMA DEFAULT_THEMA = ""
def _RebuildLocaleStrings():
global DEFAULT_THEMA
DEFAULT_THEMA = localeInfo.MUSIC_METIN2_DEFAULT_THEMA
_RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(_RebuildLocaleStrings)
class Item(ui.ListBoxEx.Item): class Item(ui.ListBoxEx.Item):
def __init__(self, fileName): def __init__(self, fileName):

View File

@@ -13,37 +13,62 @@ import uiCommon
class TargetBoard(ui.ThinBoard): class TargetBoard(ui.ThinBoard):
BUTTON_NAME_LIST = ( # Locale-independent button identifiers (never use translated strings as dict keys)
localeInfo.TARGET_BUTTON_WHISPER, BTN_WHISPER = 0
localeInfo.TARGET_BUTTON_EXCHANGE, BTN_EXCHANGE = 1
localeInfo.TARGET_BUTTON_FIGHT, BTN_FIGHT = 2
localeInfo.TARGET_BUTTON_ACCEPT_FIGHT, BTN_ACCEPT_FIGHT = 3
localeInfo.TARGET_BUTTON_AVENGE, BTN_AVENGE = 4
localeInfo.TARGET_BUTTON_FRIEND, BTN_FRIEND = 5
localeInfo.TARGET_BUTTON_INVITE_PARTY, BTN_INVITE_PARTY = 6
localeInfo.TARGET_BUTTON_LEAVE_PARTY, BTN_LEAVE_PARTY = 7
localeInfo.TARGET_BUTTON_EXCLUDE, BTN_EXCLUDE = 8
localeInfo.TARGET_BUTTON_INVITE_GUILD, BTN_INVITE_GUILD = 9
localeInfo.TARGET_BUTTON_REMOVE_GUILD, BTN_REMOVE_GUILD = 10
localeInfo.TARGET_BUTTON_DISMOUNT, BTN_DISMOUNT = 11
localeInfo.TARGET_BUTTON_EXIT_OBSERVER, BTN_EXIT_OBSERVER = 12
localeInfo.TARGET_BUTTON_VIEW_EQUIPMENT, BTN_VIEW_EQUIPMENT = 13
localeInfo.TARGET_BUTTON_REQUEST_ENTER_PARTY, BTN_REQUEST_ENTER_PARTY = 14
localeInfo.TARGET_BUTTON_BUILDING_DESTROY, BTN_BUILDING_DESTROY = 15
localeInfo.TARGET_BUTTON_EMOTION_ALLOW, BTN_EMOTION_ALLOW = 16
"VOTE_BLOCK_CHAT", BTN_VOTE_BLOCK_CHAT = 17
# (buttonId, localeInfo attribute name) — text resolved at __init__ time
BUTTON_CONFIG = (
(BTN_WHISPER, "TARGET_BUTTON_WHISPER"),
(BTN_EXCHANGE, "TARGET_BUTTON_EXCHANGE"),
(BTN_FIGHT, "TARGET_BUTTON_FIGHT"),
(BTN_ACCEPT_FIGHT, "TARGET_BUTTON_ACCEPT_FIGHT"),
(BTN_AVENGE, "TARGET_BUTTON_AVENGE"),
(BTN_FRIEND, "TARGET_BUTTON_FRIEND"),
(BTN_INVITE_PARTY, "TARGET_BUTTON_INVITE_PARTY"),
(BTN_LEAVE_PARTY, "TARGET_BUTTON_LEAVE_PARTY"),
(BTN_EXCLUDE, "TARGET_BUTTON_EXCLUDE"),
(BTN_INVITE_GUILD, "TARGET_BUTTON_INVITE_GUILD"),
(BTN_REMOVE_GUILD, "TARGET_BUTTON_REMOVE_GUILD"),
(BTN_DISMOUNT, "TARGET_BUTTON_DISMOUNT"),
(BTN_EXIT_OBSERVER, "TARGET_BUTTON_EXIT_OBSERVER"),
(BTN_VIEW_EQUIPMENT, "TARGET_BUTTON_VIEW_EQUIPMENT"),
(BTN_REQUEST_ENTER_PARTY, "TARGET_BUTTON_REQUEST_ENTER_PARTY"),
(BTN_BUILDING_DESTROY, "TARGET_BUTTON_BUILDING_DESTROY"),
(BTN_EMOTION_ALLOW, "TARGET_BUTTON_EMOTION_ALLOW"),
(BTN_VOTE_BLOCK_CHAT, "VOTE_BLOCK_CHAT"),
) )
GRADE_NAME = { GRADE_NAME = {}
nonplayer.PAWN : localeInfo.TARGET_LEVEL_PAWN,
nonplayer.S_PAWN : localeInfo.TARGET_LEVEL_S_PAWN,
nonplayer.KNIGHT : localeInfo.TARGET_LEVEL_KNIGHT,
nonplayer.S_KNIGHT : localeInfo.TARGET_LEVEL_S_KNIGHT,
nonplayer.BOSS : localeInfo.TARGET_LEVEL_BOSS,
nonplayer.KING : localeInfo.TARGET_LEVEL_KING,
}
EXCHANGE_LIMIT_RANGE = 3000 EXCHANGE_LIMIT_RANGE = 3000
@staticmethod
def _RebuildLocaleStrings():
TargetBoard.GRADE_NAME = {
nonplayer.PAWN : localeInfo.TARGET_LEVEL_PAWN,
nonplayer.S_PAWN : localeInfo.TARGET_LEVEL_S_PAWN,
nonplayer.KNIGHT : localeInfo.TARGET_LEVEL_KNIGHT,
nonplayer.S_KNIGHT : localeInfo.TARGET_LEVEL_S_KNIGHT,
nonplayer.BOSS : localeInfo.TARGET_LEVEL_BOSS,
nonplayer.KING : localeInfo.TARGET_LEVEL_KING,
}
def __init__(self): def __init__(self):
ui.ThinBoard.__init__(self) ui.ThinBoard.__init__(self)
@@ -80,7 +105,7 @@ class TargetBoard(ui.ThinBoard):
self.buttonDict = {} self.buttonDict = {}
self.showingButtonList = [] self.showingButtonList = []
for buttonName in self.BUTTON_NAME_LIST: for btnId, localeAttr in self.BUTTON_CONFIG:
button = ui.Button() button = ui.Button()
button.SetParent(self) button.SetParent(self)
@@ -94,32 +119,31 @@ class TargetBoard(ui.ThinBoard):
button.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub") button.SetDownVisual("d:/ymir work/ui/public/small_thin_button_03.sub")
button.SetWindowHorizontalAlignCenter() button.SetWindowHorizontalAlignCenter()
button.SetText(buttonName) button.SetText(getattr(localeInfo, localeAttr, localeAttr))
button.Hide() button.Hide()
self.buttonDict[buttonName] = button self.buttonDict[btnId] = button
self.showingButtonList.append(button) self.showingButtonList.append(button)
self.buttonDict[localeInfo.TARGET_BUTTON_WHISPER].SetEvent(ui.__mem_func__(self.OnWhisper)) self.buttonDict[self.BTN_WHISPER].SetEvent(ui.__mem_func__(self.OnWhisper))
self.buttonDict[localeInfo.TARGET_BUTTON_EXCHANGE].SetEvent(ui.__mem_func__(self.OnExchange)) self.buttonDict[self.BTN_EXCHANGE].SetEvent(ui.__mem_func__(self.OnExchange))
self.buttonDict[localeInfo.TARGET_BUTTON_FIGHT].SetEvent(ui.__mem_func__(self.OnPVP)) self.buttonDict[self.BTN_FIGHT].SetEvent(ui.__mem_func__(self.OnPVP))
self.buttonDict[localeInfo.TARGET_BUTTON_ACCEPT_FIGHT].SetEvent(ui.__mem_func__(self.OnPVP)) self.buttonDict[self.BTN_ACCEPT_FIGHT].SetEvent(ui.__mem_func__(self.OnPVP))
self.buttonDict[localeInfo.TARGET_BUTTON_AVENGE].SetEvent(ui.__mem_func__(self.OnPVP)) self.buttonDict[self.BTN_AVENGE].SetEvent(ui.__mem_func__(self.OnPVP))
self.buttonDict[localeInfo.TARGET_BUTTON_FRIEND].SetEvent(ui.__mem_func__(self.OnAppendToMessenger)) self.buttonDict[self.BTN_FRIEND].SetEvent(ui.__mem_func__(self.OnAppendToMessenger))
self.buttonDict[localeInfo.TARGET_BUTTON_FRIEND].SetEvent(ui.__mem_func__(self.OnAppendToMessenger)) self.buttonDict[self.BTN_INVITE_PARTY].SetEvent(ui.__mem_func__(self.OnPartyInvite))
self.buttonDict[localeInfo.TARGET_BUTTON_INVITE_PARTY].SetEvent(ui.__mem_func__(self.OnPartyInvite)) self.buttonDict[self.BTN_LEAVE_PARTY].SetEvent(ui.__mem_func__(self.OnPartyExit))
self.buttonDict[localeInfo.TARGET_BUTTON_LEAVE_PARTY].SetEvent(ui.__mem_func__(self.OnPartyExit)) self.buttonDict[self.BTN_EXCLUDE].SetEvent(ui.__mem_func__(self.OnPartyRemove))
self.buttonDict[localeInfo.TARGET_BUTTON_EXCLUDE].SetEvent(ui.__mem_func__(self.OnPartyRemove))
self.buttonDict[localeInfo.TARGET_BUTTON_INVITE_GUILD].SAFE_SetEvent(self.__OnGuildAddMember) self.buttonDict[self.BTN_INVITE_GUILD].SAFE_SetEvent(self.__OnGuildAddMember)
self.buttonDict[localeInfo.TARGET_BUTTON_REMOVE_GUILD].SAFE_SetEvent(self.__OnGuildRemoveMember) self.buttonDict[self.BTN_REMOVE_GUILD].SAFE_SetEvent(self.__OnGuildRemoveMember)
self.buttonDict[localeInfo.TARGET_BUTTON_DISMOUNT].SAFE_SetEvent(self.__OnDismount) self.buttonDict[self.BTN_DISMOUNT].SAFE_SetEvent(self.__OnDismount)
self.buttonDict[localeInfo.TARGET_BUTTON_EXIT_OBSERVER].SAFE_SetEvent(self.__OnExitObserver) self.buttonDict[self.BTN_EXIT_OBSERVER].SAFE_SetEvent(self.__OnExitObserver)
self.buttonDict[localeInfo.TARGET_BUTTON_VIEW_EQUIPMENT].SAFE_SetEvent(self.__OnViewEquipment) self.buttonDict[self.BTN_VIEW_EQUIPMENT].SAFE_SetEvent(self.__OnViewEquipment)
self.buttonDict[localeInfo.TARGET_BUTTON_REQUEST_ENTER_PARTY].SAFE_SetEvent(self.__OnRequestParty) self.buttonDict[self.BTN_REQUEST_ENTER_PARTY].SAFE_SetEvent(self.__OnRequestParty)
self.buttonDict[localeInfo.TARGET_BUTTON_BUILDING_DESTROY].SAFE_SetEvent(self.__OnDestroyBuilding) self.buttonDict[self.BTN_BUILDING_DESTROY].SAFE_SetEvent(self.__OnDestroyBuilding)
self.buttonDict[localeInfo.TARGET_BUTTON_EMOTION_ALLOW].SAFE_SetEvent(self.__OnEmotionAllow) self.buttonDict[self.BTN_EMOTION_ALLOW].SAFE_SetEvent(self.__OnEmotionAllow)
self.buttonDict["VOTE_BLOCK_CHAT"].SetEvent(ui.__mem_func__(self.__OnVoteBlockChat)) self.buttonDict[self.BTN_VOTE_BLOCK_CHAT].SetEvent(ui.__mem_func__(self.__OnVoteBlockChat))
self.name = name self.name = name
self.hpGauge = hpGauge self.hpGauge = hpGauge
@@ -182,8 +206,8 @@ class TargetBoard(ui.ThinBoard):
self.Show() self.Show()
else: else:
self.HideAllButton() self.HideAllButton()
self.__ShowButton(localeInfo.TARGET_BUTTON_WHISPER) self.__ShowButton(self.BTN_WHISPER)
self.__ShowButton("VOTE_BLOCK_CHAT") self.__ShowButton(self.BTN_VOTE_BLOCK_CHAT)
self.__ArrangeButtonPosition() self.__ArrangeButtonPosition()
self.SetTargetName(name) self.SetTargetName(name)
self.Show() self.Show()
@@ -207,11 +231,11 @@ class TargetBoard(ui.ThinBoard):
self.HideAllButton() self.HideAllButton()
if player.IsMountingHorse(): if player.IsMountingHorse():
self.__ShowButton(localeInfo.TARGET_BUTTON_DISMOUNT) self.__ShowButton(self.BTN_DISMOUNT)
canShow=1 canShow=1
if player.IsObserverMode(): if player.IsObserverMode():
self.__ShowButton(localeInfo.TARGET_BUTTON_EXIT_OBSERVER) self.__ShowButton(self.BTN_EXIT_OBSERVER)
canShow=1 canShow=1
if canShow: if canShow:
@@ -291,10 +315,10 @@ class TargetBoard(ui.ThinBoard):
def ShowDefaultButton(self): def ShowDefaultButton(self):
self.isShowButton = True self.isShowButton = True
self.showingButtonList.append(self.buttonDict[localeInfo.TARGET_BUTTON_WHISPER]) self.showingButtonList.append(self.buttonDict[self.BTN_WHISPER])
self.showingButtonList.append(self.buttonDict[localeInfo.TARGET_BUTTON_EXCHANGE]) self.showingButtonList.append(self.buttonDict[self.BTN_EXCHANGE])
self.showingButtonList.append(self.buttonDict[localeInfo.TARGET_BUTTON_FIGHT]) self.showingButtonList.append(self.buttonDict[self.BTN_FIGHT])
self.showingButtonList.append(self.buttonDict[localeInfo.TARGET_BUTTON_EMOTION_ALLOW]) self.showingButtonList.append(self.buttonDict[self.BTN_EMOTION_ALLOW])
for button in self.showingButtonList: for button in self.showingButtonList:
button.Show() button.Show()
@@ -399,7 +423,7 @@ class TargetBoard(ui.ThinBoard):
self.HideAllButton() self.HideAllButton()
if chr.INSTANCE_TYPE_BUILDING == chr.GetInstanceType(self.vid): if chr.INSTANCE_TYPE_BUILDING == chr.GetInstanceType(self.vid):
#self.__ShowButton(localeInfo.TARGET_BUTTON_BUILDING_DESTROY) #self.__ShowButton(self.BTN_BUILDING_DESTROY)
#self.__ArrangeButtonPosition() #self.__ArrangeButtonPosition()
return return
@@ -424,11 +448,11 @@ class TargetBoard(ui.ThinBoard):
guildAuthorityButtons = { guildAuthorityButtons = {
guild.AUTH_ADD_MEMBER: { guild.AUTH_ADD_MEMBER: {
"text": localeInfo.TARGET_BUTTON_INVITE_GUILD, "btn": self.BTN_INVITE_GUILD,
"condition": lambda: isNotGuildMember(self.nameString, self.vid), "condition": lambda: isNotGuildMember(self.nameString, self.vid),
}, },
guild.AUTH_REMOVE_MEMBER: { guild.AUTH_REMOVE_MEMBER: {
"text": localeInfo.TARGET_BUTTON_REMOVE_GUILD, "btn": self.BTN_REMOVE_GUILD,
"condition": lambda: isGuildMember(self.nameString, self.vid) and not isGuildMaster(self.nameString), "condition": lambda: isGuildMember(self.nameString, self.vid) and not isGuildMaster(self.nameString),
}, },
} }
@@ -437,47 +461,47 @@ class TargetBoard(ui.ThinBoard):
hasAuthority = guild.MainPlayerHasAuthority(guildAuthority) hasAuthority = guild.MainPlayerHasAuthority(guildAuthority)
satisfiesCondition = guildButton["condition"]() satisfiesCondition = guildButton["condition"]()
if hasAuthority and satisfiesCondition: if hasAuthority and satisfiesCondition:
self.__ShowButton(guildButton["text"]) self.__ShowButton(guildButton["btn"])
if not messenger.IsFriendByName(self.nameString): if not messenger.IsFriendByName(self.nameString):
self.__ShowButton(localeInfo.TARGET_BUTTON_FRIEND) self.__ShowButton(self.BTN_FRIEND)
if player.IsPartyMember(self.vid): if player.IsPartyMember(self.vid):
self.__HideButton(localeInfo.TARGET_BUTTON_FIGHT) self.__HideButton(self.BTN_FIGHT)
if player.IsPartyLeader(self.vid): if player.IsPartyLeader(self.vid):
self.__ShowButton(localeInfo.TARGET_BUTTON_LEAVE_PARTY) self.__ShowButton(self.BTN_LEAVE_PARTY)
elif player.IsPartyLeader(player.GetMainCharacterIndex()): elif player.IsPartyLeader(player.GetMainCharacterIndex()):
self.__ShowButton(localeInfo.TARGET_BUTTON_EXCLUDE) self.__ShowButton(self.BTN_EXCLUDE)
else: else:
if player.IsPartyMember(player.GetMainCharacterIndex()): if player.IsPartyMember(player.GetMainCharacterIndex()):
if player.IsPartyLeader(player.GetMainCharacterIndex()): if player.IsPartyLeader(player.GetMainCharacterIndex()):
self.__ShowButton(localeInfo.TARGET_BUTTON_INVITE_PARTY) self.__ShowButton(self.BTN_INVITE_PARTY)
else: else:
if chr.IsPartyMember(self.vid): if chr.IsPartyMember(self.vid):
self.__ShowButton(localeInfo.TARGET_BUTTON_REQUEST_ENTER_PARTY) self.__ShowButton(self.BTN_REQUEST_ENTER_PARTY)
else: else:
self.__ShowButton(localeInfo.TARGET_BUTTON_INVITE_PARTY) self.__ShowButton(self.BTN_INVITE_PARTY)
if player.IsRevengeInstance(self.vid): if player.IsRevengeInstance(self.vid):
self.__HideButton(localeInfo.TARGET_BUTTON_FIGHT) self.__HideButton(self.BTN_FIGHT)
self.__ShowButton(localeInfo.TARGET_BUTTON_AVENGE) self.__ShowButton(self.BTN_AVENGE)
elif player.IsChallengeInstance(self.vid): elif player.IsChallengeInstance(self.vid):
self.__HideButton(localeInfo.TARGET_BUTTON_FIGHT) self.__HideButton(self.BTN_FIGHT)
self.__ShowButton(localeInfo.TARGET_BUTTON_ACCEPT_FIGHT) self.__ShowButton(self.BTN_ACCEPT_FIGHT)
elif player.IsCantFightInstance(self.vid): elif player.IsCantFightInstance(self.vid):
self.__HideButton(localeInfo.TARGET_BUTTON_FIGHT) self.__HideButton(self.BTN_FIGHT)
if not player.IsSameEmpire(self.vid): if not player.IsSameEmpire(self.vid):
self.__HideButton(localeInfo.TARGET_BUTTON_INVITE_PARTY) self.__HideButton(self.BTN_INVITE_PARTY)
self.__HideButton(localeInfo.TARGET_BUTTON_FRIEND) self.__HideButton(self.BTN_FRIEND)
self.__HideButton(localeInfo.TARGET_BUTTON_FIGHT) self.__HideButton(self.BTN_FIGHT)
distance = player.GetCharacterDistance(self.vid) distance = player.GetCharacterDistance(self.vid)
if distance > self.EXCHANGE_LIMIT_RANGE: if distance > self.EXCHANGE_LIMIT_RANGE:
self.__HideButton(localeInfo.TARGET_BUTTON_EXCHANGE) self.__HideButton(self.BTN_EXCHANGE)
self.__ArrangeButtonPosition() self.__ArrangeButtonPosition()
self.__ArrangeButtonPosition() self.__ArrangeButtonPosition()
@@ -500,7 +524,7 @@ class TargetBoard(ui.ThinBoard):
def OnUpdate(self): def OnUpdate(self):
if self.isShowButton: if self.isShowButton:
exchangeButton = self.buttonDict[localeInfo.TARGET_BUTTON_EXCHANGE] exchangeButton = self.buttonDict[self.BTN_EXCHANGE]
distance = player.GetCharacterDistance(self.vid) distance = player.GetCharacterDistance(self.vid)
if distance < 0: if distance < 0:
@@ -513,3 +537,6 @@ class TargetBoard(ui.ThinBoard):
else: else:
if distance < self.EXCHANGE_LIMIT_RANGE: if distance < self.EXCHANGE_LIMIT_RANGE:
self.RefreshButton() self.RefreshButton()
TargetBoard._RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(TargetBoard._RebuildLocaleStrings)

View File

@@ -2215,26 +2215,32 @@ class SkillToolTip(ToolTip):
( 26, 1, ), ( 26, 1, ),
( 32, 2, ), ) ( 32, 2, ), )
SKILL_GRADE_NAME = { player.SKILL_GRADE_MASTER : localeInfo.SKILL_GRADE_NAME_MASTER, SKILL_GRADE_NAME = {}
player.SKILL_GRADE_GRAND_MASTER : localeInfo.SKILL_GRADE_NAME_GRAND_MASTER, AFFECT_NAME_DICT = {}
player.SKILL_GRADE_PERFECT_MASTER : localeInfo.SKILL_GRADE_NAME_PERFECT_MASTER, }
AFFECT_NAME_DICT = {
"HP" : localeInfo.TOOLTIP_SKILL_AFFECT_ATT_POWER,
"ATT_GRADE" : localeInfo.TOOLTIP_SKILL_AFFECT_ATT_GRADE,
"DEF_GRADE" : localeInfo.TOOLTIP_SKILL_AFFECT_DEF_GRADE,
"ATT_SPEED" : localeInfo.TOOLTIP_SKILL_AFFECT_ATT_SPEED,
"MOV_SPEED" : localeInfo.TOOLTIP_SKILL_AFFECT_MOV_SPEED,
"DODGE" : localeInfo.TOOLTIP_SKILL_AFFECT_DODGE,
"RESIST_NORMAL" : localeInfo.TOOLTIP_SKILL_AFFECT_RESIST_NORMAL,
"REFLECT_MELEE" : localeInfo.TOOLTIP_SKILL_AFFECT_REFLECT_MELEE,
}
AFFECT_APPEND_TEXT_DICT = { AFFECT_APPEND_TEXT_DICT = {
"DODGE" : "%", "DODGE" : "%",
"RESIST_NORMAL" : "%", "RESIST_NORMAL" : "%",
"REFLECT_MELEE" : "%", "REFLECT_MELEE" : "%",
} }
@staticmethod
def _RebuildLocaleStrings():
SkillToolTip.SKILL_GRADE_NAME = {
player.SKILL_GRADE_MASTER : localeInfo.SKILL_GRADE_NAME_MASTER,
player.SKILL_GRADE_GRAND_MASTER : localeInfo.SKILL_GRADE_NAME_GRAND_MASTER,
player.SKILL_GRADE_PERFECT_MASTER : localeInfo.SKILL_GRADE_NAME_PERFECT_MASTER,
}
SkillToolTip.AFFECT_NAME_DICT = {
"HP" : localeInfo.TOOLTIP_SKILL_AFFECT_ATT_POWER,
"ATT_GRADE" : localeInfo.TOOLTIP_SKILL_AFFECT_ATT_GRADE,
"DEF_GRADE" : localeInfo.TOOLTIP_SKILL_AFFECT_DEF_GRADE,
"ATT_SPEED" : localeInfo.TOOLTIP_SKILL_AFFECT_ATT_SPEED,
"MOV_SPEED" : localeInfo.TOOLTIP_SKILL_AFFECT_MOV_SPEED,
"DODGE" : localeInfo.TOOLTIP_SKILL_AFFECT_DODGE,
"RESIST_NORMAL" : localeInfo.TOOLTIP_SKILL_AFFECT_RESIST_NORMAL,
"REFLECT_MELEE" : localeInfo.TOOLTIP_SKILL_AFFECT_REFLECT_MELEE,
}
def __init__(self): def __init__(self):
ToolTip.__init__(self, self.SKILL_TOOL_TIP_WIDTH) ToolTip.__init__(self, self.SKILL_TOOL_TIP_WIDTH)
def __del__(self): def __del__(self):
@@ -2705,5 +2711,8 @@ if __name__ == "__main__":
toolTip.AddItemData_Offline(10, desc, summ, 0, 0) toolTip.AddItemData_Offline(10, desc, summ, 0, 0)
toolTip.Show() toolTip.Show()
app.Loop() app.Loop()
SkillToolTip._RebuildLocaleStrings()
localeInfo.RegisterReloadCallback(SkillToolTip._RebuildLocaleStrings)