From 3a9a9fd6f0cd09ec7e450ba3a3df9386d4b2d68a Mon Sep 17 00:00:00 2001 From: bridge Date: Sat, 3 Jan 2026 22:26:55 +0800 Subject: [PATCH] add choice helper --- src/classes/auxiliary.py | 12 +++++------- src/classes/fortune.py | 11 ++++++----- src/classes/kill_and_grab.py | 13 +++++++------ src/classes/single_choice.py | 22 ++++++++++++++++++++++ src/classes/technique.py | 4 +++- src/classes/weapon.py | 6 ++++-- src/utils/protagonist.py | 2 +- static/game_configs/weapon.csv | 18 +++++++++--------- tests/conftest.py | 2 +- tests/test_equipment.py | 29 ----------------------------- 10 files changed, 58 insertions(+), 61 deletions(-) diff --git a/src/classes/auxiliary.py b/src/classes/auxiliary.py index b7741bd..4d67184 100644 --- a/src/classes/auxiliary.py +++ b/src/classes/auxiliary.py @@ -25,13 +25,11 @@ class Auxiliary: # 特殊属性(用于存储实例特定数据) special_data: dict = field(default_factory=dict) - def get_info(self) -> str: - """获取简略信息""" - suffix = "" - # 万魂幡特殊显示 - if self.name == "万魂幡" and self.special_data.get("devoured_souls", 0) > 0: - suffix = f"(吞噬魂魄:{self.special_data['devoured_souls']})" - return f"{self.name}{suffix}" + def get_info(self, detailed: bool = False) -> str: + """获取信息""" + if detailed: + return self.get_detailed_info() + return f"{self.name}" def get_detailed_info(self) -> str: """获取详细信息""" diff --git a/src/classes/fortune.py b/src/classes/fortune.py index 018c906..9edb1fb 100644 --- a/src/classes/fortune.py +++ b/src/classes/fortune.py @@ -150,7 +150,7 @@ def _find_potential_master(avatar: Avatar) -> Optional[Avatar]: def _can_get_weapon(avatar: Avatar) -> bool: - """检查是否可以获得兵器奇遇:当前兵器是练气级(凡品)时可触发""" + """检查是否可以获得兵器奇遇:当前兵器是练气级(练气)时可触发""" if avatar.weapon is None: return True return avatar.weapon.realm == Realm.Qi_Refinement @@ -395,7 +395,7 @@ async def try_trigger_fortune(avatar: Avatar) -> list[Event]: # 导入单选决策模块 - from src.classes.single_choice import make_decision + from src.classes.single_choice import make_decision, format_swap_choice_desc async def _resolve_choice( new_obj: Any, @@ -414,16 +414,17 @@ async def try_trigger_fortune(avatar: Avatar) -> list[Event]: return True, f"{avatar.name} 获得{new_grade_val}{type_label}『{new_name}』" old_name = old_obj.name - old_grade_val = getattr(old_obj, "grade", getattr(old_obj, "realm", None)).value + + swap_desc = format_swap_choice_desc(new_obj, old_obj, type_label) options = [ { "key": "A", - "desc": f"保留原{type_label}『{old_name}』({old_grade_val}),放弃新{type_label}『{new_name}』({new_grade_val})。" + "desc": f"保留原{type_label}『{old_name}』,放弃新{type_label}『{new_name}』。" }, { "key": "B", - "desc": f"卖掉原{type_label}『{old_name}』换取灵石,接受新{type_label}『{new_name}』({new_grade_val})。" + "desc": f"卖掉原{type_label}『{old_name}』换取灵石,接受新{type_label}『{new_name}』。\n{swap_desc}" } ] diff --git a/src/classes/kill_and_grab.py b/src/classes/kill_and_grab.py index 7b762cd..64c7406 100644 --- a/src/classes/kill_and_grab.py +++ b/src/classes/kill_and_grab.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import TYPE_CHECKING import random -from src.classes.single_choice import make_decision +from src.classes.single_choice import make_decision, format_swap_choice_desc if TYPE_CHECKING: from src.classes.avatar import Avatar @@ -49,14 +49,15 @@ async def kill_and_grab(winner: Avatar, loser: Avatar) -> str: else: # 其他情况下都让 AI 决策 # 构建详细描述,包含效果 - item_desc = loot_item.get_detailed_info() - current_desc = winner_current.get_detailed_info() - - context = f"战斗胜利,{loser.name} 身死道消,留下了一件{loot_item.realm.value}{'兵器' if loot_type == 'weapon' else '辅助装备'}『{item_desc}』。" + item_label = '兵器' if loot_type == 'weapon' else '辅助装备' + context = f"战斗胜利,{loser.name} 身死道消,留下了一件{loot_item.realm.value}{item_label}『{loot_item.name}』。" + + swap_desc = format_swap_choice_desc(loot_item, winner_current, item_label) + options = [ { "key": "A", - "desc": f"夺取『{loot_item.name}』,卖掉身上的『{winner_current.name}』换取灵石。\n - 新装备:{item_desc}\n - 原装备:{current_desc}" + "desc": f"夺取『{loot_item.name}』,卖掉身上的『{winner_current.name}』换取灵石。\n{swap_desc}" }, { "key": "B", diff --git a/src/classes/single_choice.py b/src/classes/single_choice.py index 7411e05..372e692 100644 --- a/src/classes/single_choice.py +++ b/src/classes/single_choice.py @@ -80,3 +80,25 @@ async def make_decision( choice = options[0]["key"] return choice + + +def format_swap_choice_desc(new_item: Any, old_item: Any | None, item_type_name: str) -> str: + """ + 生成替换装备/功法时的决策描述文本。 + + Args: + new_item: 新获得的物品对象(必须实现 get_info(detailed=True)) + old_item: 当前持有的物品对象(可能为 None) + item_type_name: 物品类型名称(如"兵器"、"功法") + """ + new_info = item.get_info(detailed=True) + + if old_item: + old_info = item.get_info(detailed=True) + return ( + f"现有{item_type_name}:{old_info}\n" + f"新{item_type_name}:{new_info}\n" + f"(选择替换将卖出旧{item_type_name})" + ) + else: + return f"新{item_type_name}:{new_info}" \ No newline at end of file diff --git a/src/classes/technique.py b/src/classes/technique.py index ef30491..5a79834 100644 --- a/src/classes/technique.py +++ b/src/classes/technique.py @@ -73,7 +73,9 @@ class Technique: return True return bool(eval(self.condition, {"__builtins__": {}}, {"avatar": avatar, "Alignment": Alignment})) - def get_info(self) -> str: + def get_info(self, detailed: bool = False) -> str: + if detailed: + return self.get_detailed_info() return f"{self.name}({self.attribute}){self.grade.value}" def get_detailed_info(self) -> str: diff --git a/src/classes/weapon.py b/src/classes/weapon.py index ce8ecd2..2873b09 100644 --- a/src/classes/weapon.py +++ b/src/classes/weapon.py @@ -30,8 +30,10 @@ class Weapon: # 特殊属性(如万魂幡的吞噬魂魄计数) special_data: dict = field(default_factory=dict) - def get_info(self) -> str: - """获取简略信息""" + def get_info(self, detailed: bool = False) -> str: + """获取信息""" + if detailed: + return self.get_detailed_info() return f"{self.name}" def get_detailed_info(self) -> str: diff --git a/src/utils/protagonist.py b/src/utils/protagonist.py index 4a9b5e1..b07964d 100644 --- a/src/utils/protagonist.py +++ b/src/utils/protagonist.py @@ -155,7 +155,7 @@ protagonist_configs = [ "level": 65, "sect": 14, # 噬魔宗 "technique": 28, # 燃血大法 (自残修仙) - "weapon": 1001, # 凡品剑 (红中) + "weapon": 1001, # 练气剑 (红中) "auxiliary": 2008, # 菩提子 (清心压制) "personas": ["无常", "好斗", "忠诚"], "appearance": 20, diff --git a/static/game_configs/weapon.csv b/static/game_configs/weapon.csv index 6e068e4..957cdf4 100644 --- a/static/game_configs/weapon.csv +++ b/static/game_configs/weapon.csv @@ -1,14 +1,14 @@ id,name,weapon_type,grade,desc,effects ,名称,兵器类型,等级(练气/筑基/金丹/元婴),描述/提示词,JSON形式(支持宽松格式,见effects.py说明) -1001,凡品剑,剑,练气,市井铁匠铺打造的铁剑,勉强能用。,{extra_battle_strength_points: 1} -1002,凡品刀,刀,练气,厚背砍刀,寻常武夫的最爱。,{extra_battle_strength_points: 1} -1003,凡品枪,枪,练气,白蜡杆制成,枪头挂着红缨。,{extra_battle_strength_points: 1} -1004,凡品棍,棍,练气,普通的硬木棍,长与眉齐。,{extra_battle_strength_points: 1} -1005,凡品扇,扇,练气,文人雅士常用的折扇,附庸风雅。,{extra_battle_strength_points: 1} -1006,凡品鞭,鞭,练气,普通的皮鞭,多用于赶车或刑罚。,{extra_battle_strength_points: 1} -1007,凡品琴,琴,练气,桐木制成的古琴,音色尚可。,{extra_battle_strength_points: 1} -1008,凡品笛,笛,练气,紫竹削制,声音清脆。,{extra_battle_strength_points: 1} -1009,凡品暗器,暗器,练气,随处可见的铁制暗器。,{extra_battle_strength_points: 1} +1001,练气剑,剑,练气,市井铁匠铺打造的铁剑,勉强能用。,{extra_battle_strength_points: 1} +1002,练气刀,刀,练气,厚背砍刀,寻常武夫的最爱。,{extra_battle_strength_points: 1} +1003,练气枪,枪,练气,白蜡杆制成,枪头挂着红缨。,{extra_battle_strength_points: 1} +1004,练气棍,棍,练气,普通的硬木棍,长与眉齐。,{extra_battle_strength_points: 1} +1005,练气扇,扇,练气,文人雅士常用的折扇,附庸风雅。,{extra_battle_strength_points: 1} +1006,练气鞭,鞭,练气,普通的皮鞭,多用于赶车或刑罚。,{extra_battle_strength_points: 1} +1007,练气琴,琴,练气,桐木制成的古琴,音色尚可。,{extra_battle_strength_points: 1} +1008,练气笛,笛,练气,紫竹削制,声音清脆。,{extra_battle_strength_points: 1} +1009,练气暗器,暗器,练气,随处可见的铁制暗器。,{extra_battle_strength_points: 1} 2001,青霜剑,剑,筑基,剑气森寒。,{extra_battle_strength_points: 2} 2002,井中月,刀,筑基,刀光如井中映月,虚实难测。,{extra_battle_strength_points: 2} 2003,火尖枪,枪,筑基,枪头隐有火光,能引动凡火。,"{extra_battle_strength_points: 2, extra_max_hp: 50}" diff --git a/tests/conftest.py b/tests/conftest.py index 8e09456..2160278 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -49,7 +49,7 @@ def dummy_avatar(base_world): # 赋予一个 Mock 武器,防止 get_avatar_info 报错 av.weapon = MagicMock() - av.weapon.get_detailed_info.return_value = "测试木剑(凡品)" + av.weapon.get_detailed_info.return_value = "测试木剑(练气)" av.weapon_proficiency = 0.0 return av diff --git a/tests/test_equipment.py b/tests/test_equipment.py index bd4a2f9..09dbf3f 100644 --- a/tests/test_equipment.py +++ b/tests/test_equipment.py @@ -82,35 +82,6 @@ class TestEquipment: assert aux.name in detailed assert aux.realm.value in detailed - def test_soul_banner(self): - """测试万魂幡特殊逻辑""" - # 查找万魂幡 - soul_banner = None - for a in auxiliaries_by_id.values(): - if a.name == "万魂幡": - soul_banner = a - break - - if not soul_banner: - pytest.skip("万魂幡 not found in config") - - import copy - banner = copy.deepcopy(soul_banner) - - # 初始状态 - assert "吞噬魂魄" not in banner.get_info() - - # 增加魂魄 - banner.special_data["devoured_souls"] = 100 - - # 检查显示更新 - assert "吞噬魂魄:100" in banner.get_info() - assert "吞噬魂魄:100" in banner.get_detailed_info() - - # 检查结构化信息 - struct = banner.get_structured_info() - assert "已吞噬魂魄:100" in struct["desc"] - def test_grade_renaming_compatibility(self): """测试 realm 改名后的兼容性(如果有必要)""" # 确保 weapon 和 auxiliary 确实有 realm 属性