196 lines
7.6 KiB
Python
196 lines
7.6 KiB
Python
from typing import Any
|
||
import re
|
||
|
||
|
||
def get_effect_desc(effect_key: str) -> str:
|
||
"""获取 effect 的描述名称(支持国际化)"""
|
||
from src.i18n import t
|
||
|
||
# 映射 effect_key -> msgid
|
||
msgid_map = {
|
||
"extra_hp_recovery_rate": "effect_extra_hp_recovery_rate",
|
||
"extra_max_hp": "effect_extra_max_hp",
|
||
"extra_max_lifespan": "effect_extra_max_lifespan",
|
||
"extra_weapon_proficiency_gain": "effect_extra_weapon_proficiency_gain",
|
||
"extra_dual_cultivation_exp": "effect_extra_dual_cultivation_exp",
|
||
"extra_breakthrough_success_rate": "effect_extra_breakthrough_success_rate",
|
||
"extra_retreat_success_rate": "effect_extra_retreat_success_rate",
|
||
"extra_fortune_probability": "effect_extra_fortune_probability",
|
||
"extra_misfortune_probability": "effect_extra_misfortune_probability",
|
||
"extra_harvest_materials": "effect_extra_harvest_materials",
|
||
"extra_hunt_materials": "effect_extra_hunt_materials",
|
||
"extra_mine_materials": "effect_extra_mine_materials",
|
||
"extra_item_sell_price_multiplier": "effect_extra_item_sell_price_multiplier",
|
||
"shop_buy_price_reduction": "effect_shop_buy_price_reduction",
|
||
"extra_weapon_upgrade_chance": "effect_extra_weapon_upgrade_chance",
|
||
"extra_plunder_multiplier": "effect_extra_plunder_multiplier",
|
||
"extra_catch_success_rate": "effect_extra_catch_success_rate",
|
||
"extra_cultivate_exp": "effect_extra_cultivate_exp",
|
||
"extra_battle_strength_points": "effect_extra_battle_strength_points",
|
||
"extra_escape_success_rate": "effect_extra_escape_success_rate",
|
||
"extra_assassinate_success_rate": "effect_extra_assassinate_success_rate",
|
||
"extra_observation_radius": "effect_extra_observation_radius",
|
||
"extra_move_step": "effect_extra_move_step",
|
||
"legal_actions": "effect_legal_actions",
|
||
"damage_reduction": "effect_damage_reduction",
|
||
"realm_suppression_bonus": "effect_realm_suppression_bonus",
|
||
"cultivate_duration_reduction": "effect_cultivate_duration_reduction",
|
||
"extra_cast_success_rate": "effect_extra_cast_success_rate",
|
||
"extra_refine_success_rate": "effect_extra_refine_success_rate",
|
||
}
|
||
|
||
msgid = msgid_map.get(effect_key, effect_key)
|
||
return t(msgid)
|
||
|
||
|
||
def get_action_short_name(action_name: str) -> str:
|
||
"""获取 action 的简短名称(复用 Action 系统翻译)"""
|
||
from src.i18n import t
|
||
|
||
# 使用统一的命名规则
|
||
msgid = f"action_{action_name.lower()}_short_name"
|
||
return t(msgid)
|
||
|
||
def format_value(key: str, value: Any) -> str:
|
||
"""
|
||
格式化效果数值
|
||
"""
|
||
if key == "legal_actions" and isinstance(value, list):
|
||
from src.i18n import t
|
||
actions = [get_action_short_name(str(a)) for a in value]
|
||
sep = t("action_list_separator") # "、" 或 ", "
|
||
return sep.join(actions)
|
||
|
||
if isinstance(value, (int, float)):
|
||
# 处理百分比类型的字段
|
||
if "rate" in key or "probability" in key or "chance" in key or "multiplier" in key or "gain" in key or "reduction" in key or "bonus" in key:
|
||
# 如果是小数,转为百分比。通常 0.1 表示 +10%
|
||
# 但有些可能是直接的倍率?代码里 1.0 + value,所以 value 是增量
|
||
if isinstance(value, float):
|
||
percent = value * 100
|
||
sign = "+" if percent > 0 else ""
|
||
return f"{sign}{percent:.1f}%"
|
||
|
||
# 处理数值类型的字段
|
||
sign = "+" if value > 0 else ""
|
||
return f"{sign}{value}"
|
||
|
||
return str(value)
|
||
|
||
def translate_condition(condition: str) -> str:
|
||
"""
|
||
将代码形式的条件表达式转换为易读描述
|
||
"""
|
||
from src.i18n import t
|
||
import re
|
||
|
||
if not condition:
|
||
return t("Conditional effect")
|
||
|
||
# 1. 处理 Persona 判断 (特质)
|
||
# 模式: any(p.key == "CHILD_OF_FORTUNE" for p in avatar.personas)
|
||
if "avatar.personas" in condition:
|
||
# 优先匹配 key
|
||
m_key = re.search(r'p\.key\s*==\s*["\'](.*?)["\']', condition)
|
||
if m_key:
|
||
key = m_key.group(1)
|
||
# 尝试从全局数据中查找对应的 Persona Name
|
||
from src.classes.persona import personas_by_id
|
||
trait_name = key # 默认显示key,如果找到则显示name
|
||
for p in personas_by_id.values():
|
||
if p.key == key:
|
||
trait_name = p.name
|
||
break
|
||
return t("Has [{trait}] trait", trait=trait_name)
|
||
|
||
# 兼容旧的 name 匹配
|
||
m_name = re.search(r'p\.name\s*==\s*["\'](.*?)["\']', condition)
|
||
if m_name:
|
||
return t("Has [{trait}] trait", trait=m_name.group(1))
|
||
|
||
# 2. 处理 Alignment 判断 (阵营)
|
||
# 模式: avatar.alignment == Alignment.RIGHTEOUS
|
||
if "avatar.alignment" in condition:
|
||
m_align = re.search(r'Alignment\.([A-Z_]+)', condition)
|
||
if m_align:
|
||
align_key = m_align.group(1)
|
||
from src.classes.alignment import Alignment
|
||
try:
|
||
# 获取枚举并调用 str() 进行翻译
|
||
align_enum = Alignment[align_key]
|
||
return t("When alignment is {align}", align=str(align_enum))
|
||
except KeyError:
|
||
pass
|
||
|
||
# 3. 处理 WeaponType 判断 (兵器类型)
|
||
# 模式: avatar.weapon.type == WeaponType.SWORD
|
||
if "avatar.weapon.type" in condition:
|
||
m_weapon = re.search(r'WeaponType\.([A-Z_]+)', condition)
|
||
if m_weapon:
|
||
w_key = m_weapon.group(1)
|
||
from src.classes.weapon_type import WeaponType
|
||
try:
|
||
w_enum = WeaponType[w_key]
|
||
return t("When using {weapon_type}", weapon_type=str(w_enum))
|
||
except KeyError:
|
||
pass
|
||
|
||
# 4. 兜底简化
|
||
# 移除代码前缀和符号,使未识别的条件稍微可读一些
|
||
simple_cond = condition
|
||
simple_cond = simple_cond.replace("avatar.", "")
|
||
simple_cond = simple_cond.replace("Alignment.", "")
|
||
simple_cond = simple_cond.replace("WeaponType.", "")
|
||
simple_cond = simple_cond.replace("==", ":")
|
||
|
||
return t("When {condition}", condition=simple_cond)
|
||
|
||
def format_effects_to_text(effects: dict[str, Any] | list[dict[str, Any]]) -> str:
|
||
"""
|
||
将 effects 字典转换为易读的文本描述。
|
||
例如:{"extra_max_hp": 100} -> "最大生命值 +100" / "Max HP +100"
|
||
"""
|
||
from src.i18n import t
|
||
|
||
if not effects:
|
||
return ""
|
||
|
||
if isinstance(effects, list):
|
||
parts = []
|
||
for eff in effects:
|
||
text = format_effects_to_text(eff)
|
||
if text:
|
||
parts.append(text)
|
||
return "\n".join(parts)
|
||
|
||
desc_list = []
|
||
for k, v in effects.items():
|
||
if k in ["when", "duration_month"]:
|
||
continue
|
||
|
||
# 使用翻译函数获取名称
|
||
name = get_effect_desc(k)
|
||
|
||
# 如果是 eval 表达式(字符串形式)或者看起来像代码
|
||
if isinstance(v, str):
|
||
if v.startswith("eval(") or "avatar." in v or "//" in v:
|
||
val_str = t("Special effect (dynamic)")
|
||
else:
|
||
val_str = format_value(k, v)
|
||
else:
|
||
val_str = format_value(k, v)
|
||
|
||
desc_list.append(f"{name} {val_str}")
|
||
|
||
# 使用翻译的分隔符
|
||
sep = t("effect_separator")
|
||
text = sep.join(desc_list)
|
||
|
||
# 如果有条件,添加条件描述
|
||
if effects.get("when"):
|
||
cond = translate_condition(str(effects["when"]))
|
||
return t("[{condition}] {effects}", condition=cond, effects=text)
|
||
|
||
return text
|
||
|