From 3d60df4dbf9e7cd53f5422e8ed25f255c80c1cf0 Mon Sep 17 00:00:00 2001 From: bridge Date: Thu, 13 Nov 2025 01:54:46 +0800 Subject: [PATCH] update treasure to weapon and auxiliary --- src/classes/action/nurture_weapon.py | 25 ++++++++++ src/classes/avatar.py | 75 +++++++++++++++++++++------- src/classes/effects.py | 18 +++++++ src/classes/fortune.py | 4 +- src/classes/weapon.py | 9 ++++ src/sim/new_avatar.py | 65 +++++++++++++++++++++++- static/game_configs/weapon.csv | 9 ++++ 7 files changed, 183 insertions(+), 22 deletions(-) diff --git a/src/classes/action/nurture_weapon.py b/src/classes/action/nurture_weapon.py index 35fb92f..02e8708 100644 --- a/src/classes/action/nurture_weapon.py +++ b/src/classes/action/nurture_weapon.py @@ -17,9 +17,33 @@ class NurtureWeapon(TimedAction): duration_months = 3 def _execute(self) -> None: + from src.classes.equipment_grade import EquipmentGrade + from src.classes.weapon import get_treasure_weapon + # 温养兵器增加较多熟练度(5-10) proficiency_gain = random.uniform(5.0, 10.0) self.avatar.increase_weapon_proficiency(proficiency_gain) + + # 如果是普通兵器,有5%概率升级为宝物 + if self.avatar.weapon and self.avatar.weapon.grade == EquipmentGrade.COMMON: + if random.random() < 0.05: + treasure_weapon = get_treasure_weapon(self.avatar.weapon.weapon_type) + if treasure_weapon: + import copy + old_weapon_name = self.avatar.weapon.name + old_proficiency = self.avatar.weapon_proficiency + # 深拷贝宝物兵器并更换(会重新计算长期效果) + new_weapon = copy.deepcopy(treasure_weapon) + self.avatar.change_weapon(new_weapon) + # 恢复熟练度(change_weapon 会归零,需要手动恢复) + self.avatar.weapon_proficiency = old_proficiency + # 记录升华事件 + from src.classes.event import Event + self.avatar.world.add_event(Event( + self.avatar.world.month_stamp, + f"{self.avatar.name} 温养{old_weapon_name}时,兵器灵性大增,升华为{treasure_weapon.name}!", + related_avatars=[self.avatar.id] + )) def can_start(self) -> tuple[bool, str]: # 任何时候都可以温养兵器 @@ -36,6 +60,7 @@ class NurtureWeapon(TimedAction): def finish(self) -> list[Event]: weapon_name = self.avatar.weapon.name if self.avatar.weapon else "兵器" proficiency = self.avatar.weapon_proficiency + # 注意:升华事件已经在_execute中添加,这里只添加完成事件 return [ Event( self.world.month_stamp, diff --git a/src/classes/avatar.py b/src/classes/avatar.py index 64225a4..e657747 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -146,23 +146,10 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): from src.classes.alignment import Alignment as _Alignment self.alignment = random.choice(list(_Alignment)) - # 兵器初始化:如果无兵器,分配一个普通兵器 - # 有宗门且宗门有倾向兵器时,80%概率使用宗门兵器,否则随机 - if self.weapon is None: - if self.sect is not None and self.sect.preferred_weapon: - # 有宗门倾向兵器,80%概率使用 - if random.random() < 0.8: - # 尝试根据宗门倾向兵器类型获取 - for wt in WeaponType: - if wt.value == self.sect.preferred_weapon: - self.weapon = get_common_weapon(wt) - break - # 如果还没有兵器(无宗门、无倾向、或20%随机),随机分配 - if self.weapon is None: - weapon_type = random.choice(list(WeaponType)) - self.weapon = get_common_weapon(weapon_type) - # effects 改为实时属性,不在此初始化 + + # 初始化时计算所有长期效果(HP/MP等) + self.recalc_effects() @property def effects(self) -> dict[str, object]: @@ -388,9 +375,11 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): self.cultivation_progress.level = new_level self.cultivation_progress.realm = self.cultivation_progress.get_realm(new_level) - # 如果境界提升了,更新寿命期望 + # 如果境界提升了,更新寿命期望和长期效果 if self.cultivation_progress.realm != old_realm: self.age.update_realm(self.cultivation_progress.realm) + # 境界变化会影响 HP/MP 基础值,需要重新计算 + self.recalc_effects() # 如果有宗门,检查是否需要晋升职位 from src.classes.sect_ranks import check_and_promote_sect_rank check_and_promote_sect_rank(self, old_realm, self.cultivation_progress.realm) @@ -734,15 +723,65 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): """ return self.cultivation_progress.get_move_step() + def recalc_effects(self) -> None: + """ + 重新计算所有长期效果 + 在装备更换、突破境界等情况下调用 + + 说明: + - self.effects 是 @property,每次访问都会重新 merge 所有来源的 effects + - 包括:宗门、功法、灵根、特质、兵器、辅助装备、灵兽 + - 也会重新计算动态表达式(如 eval(...)) + + 当前包括: + - HP/MP 最大值 + - 将来可能还有其他长期 effects + """ + # 计算基础最大值(基于境界) + base_max_hp = HP_MAX_BY_REALM.get(self.cultivation_progress.realm, 100) + base_max_mp = MP_MAX_BY_REALM.get(self.cultivation_progress.realm, 100) + + # 访问 self.effects 会触发 @property,重新 merge 所有 effects + effects = self.effects + extra_max_hp = int(effects.get("extra_max_hp", 0)) + extra_max_mp = int(effects.get("extra_max_mp", 0)) + + # 计算新的最大值 + new_max_hp = base_max_hp + extra_max_hp + new_max_mp = base_max_mp + extra_max_mp + + # 更新最大值 + self.hp.max = new_max_hp + self.mp.max = new_max_mp + + # 调整当前值(不超过新的最大值) + if self.hp.cur > new_max_hp: + self.hp.cur = new_max_hp + if self.mp.cur > new_max_mp: + self.mp.cur = new_max_mp + + # 将来这里可以添加其他长期 effects 的计算 + def change_weapon(self, new_weapon: Weapon) -> None: """ - 更换兵器,熟练度归零 + 更换兵器,熟练度归零,并重新计算长期效果 Args: new_weapon: 新的兵器 """ self.weapon = new_weapon self.weapon_proficiency = 0.0 + self.recalc_effects() + + def change_auxiliary(self, new_auxiliary: Optional[Auxiliary]) -> None: + """ + 更换辅助装备,并重新计算长期效果 + + Args: + new_auxiliary: 新的辅助装备(可为 None 表示卸下) + """ + self.auxiliary = new_auxiliary + self.recalc_effects() def increase_weapon_proficiency(self, amount: float) -> None: """ diff --git a/src/classes/effects.py b/src/classes/effects.py index c15ae64..472115f 100644 --- a/src/classes/effects.py +++ b/src/classes/effects.py @@ -26,6 +26,22 @@ EXTRA_BATTLE_STRENGTH_POINTS = "extra_battle_strength_points" 说明: 直接增加角色的战斗力数值 """ +EXTRA_MAX_HP = "extra_max_hp" +""" +额外最大生命值 +类型: int +结算: src/classes/avatar.py (__post_init__) +说明: 增加角色的最大生命值上限 +""" + +EXTRA_MAX_MP = "extra_max_mp" +""" +额外最大灵力值 +类型: int +结算: src/classes/avatar.py (__post_init__) +说明: 增加角色的最大灵力值上限 +""" + EXTRA_OBSERVATION_RADIUS = "extra_observation_radius" """ 额外观察半径 @@ -155,6 +171,8 @@ Effects 通过 src/classes/effect.py 中的 _merge_effects() 函数合并。 ALL_EFFECTS = [ # 战斗相关 "extra_battle_strength_points", # int - 额外战斗力 + "extra_max_hp", # int - 额外最大生命值 + "extra_max_mp", # int - 额外最大灵力值 "extra_observation_radius", # int - 额外观察半径 # 修炼相关 diff --git a/src/classes/fortune.py b/src/classes/fortune.py index 5a1862a..03e8d42 100644 --- a/src/classes/fortune.py +++ b/src/classes/fortune.py @@ -428,7 +428,7 @@ async def try_trigger_fortune(avatar: Avatar) -> list[Event]: kind = FortuneKind.TECHNIQUE theme = _pick_theme(kind) else: - avatar.weapon = weapon + avatar.change_weapon(weapon) res_text = f"{avatar.name} 获得{weapon.grade}兵器『{weapon.name}』" if kind == FortuneKind.AUXILIARY: @@ -438,7 +438,7 @@ async def try_trigger_fortune(avatar: Avatar) -> list[Event]: kind = FortuneKind.TECHNIQUE theme = _pick_theme(kind) else: - avatar.auxiliary = auxiliary + avatar.change_auxiliary(auxiliary) res_text = f"{avatar.name} 获得{auxiliary.grade}辅助装备『{auxiliary.name}』" if kind == FortuneKind.TECHNIQUE: diff --git a/src/classes/weapon.py b/src/classes/weapon.py index 112501e..99b983c 100644 --- a/src/classes/weapon.py +++ b/src/classes/weapon.py @@ -117,3 +117,12 @@ def get_common_weapon(weapon_type: WeaponType) -> Optional[Weapon]: weapon_name = f"普通{weapon_type.value}" return weapons_by_name.get(weapon_name) + +def get_treasure_weapon(weapon_type: WeaponType) -> Optional[Weapon]: + """获取指定类型的宝物级兵器""" + from src.classes.equipment_grade import EquipmentGrade + for weapon in weapons_by_id.values(): + if weapon.weapon_type == weapon_type and weapon.grade == EquipmentGrade.TREASURE: + return weapon + return None + diff --git a/src/sim/new_avatar.py b/src/sim/new_avatar.py index aa81682..830ca01 100644 --- a/src/sim/new_avatar.py +++ b/src/sim/new_avatar.py @@ -63,6 +63,63 @@ def random_gender() -> Gender: return Gender.MALE if random.random() < 0.5 else Gender.FEMALE +def _assign_initial_weapon(avatar: Avatar) -> None: + """ + 为新角色分配初始兵器 + - 根据宗门倾向(80%概率)或随机选择兵器类型 + - 1%概率获得法宝(优先宗门相关) + - 5%概率获得宝物 + - 94%概率获得普通兵器 + """ + from src.classes.weapon import get_common_weapon, get_treasure_weapon, weapons_by_id, weapons_by_sect_id + from src.classes.weapon_type import WeaponType + from src.classes.equipment_grade import EquipmentGrade + import copy + + # 1. 确定兵器类型:宗门倾向或随机 + weapon_type = None + if avatar.sect is not None and avatar.sect.preferred_weapon: + if random.random() < 0.8: + for wt in WeaponType: + if wt.value == avatar.sect.preferred_weapon: + weapon_type = wt + break + if weapon_type is None: + weapon_type = random.choice(list(WeaponType)) + + # 2. 确定品质并分配兵器 + roll = random.random() + + if roll < 0.01: + # 尝试获得法宝(优先宗门相关) + artifact_weapon = None + if avatar.sect is not None and avatar.sect.id in weapons_by_sect_id: + candidate = weapons_by_sect_id[avatar.sect.id] + if candidate.grade == EquipmentGrade.ARTIFACT: + artifact_weapon = candidate + + # 如果没有宗门相关法宝,从所有同类型法宝中选择 + if artifact_weapon is None: + artifact_candidates = [w for w in weapons_by_id.values() + if w.grade == EquipmentGrade.ARTIFACT and w.weapon_type == weapon_type] + if artifact_candidates: + artifact_weapon = random.choice(artifact_candidates) + + if artifact_weapon is not None: + avatar.weapon = copy.deepcopy(artifact_weapon) + return + + if roll < 0.06: # 0.01 + 0.05 + # 获得宝物 + treasure_weapon = get_treasure_weapon(weapon_type) + if treasure_weapon: + avatar.weapon = copy.deepcopy(treasure_weapon) + return + + # 获得普通兵器 + avatar.weapon = get_common_weapon(weapon_type) + + def get_new_avatar_from_mortal(world: World, current_month_stamp: MonthStamp, name: str, age: Age) -> Avatar: """ 从凡人中来的新修士:先规划宗门/关系,再生成实际角色;不分配宗门法宝。 @@ -213,6 +270,9 @@ def build_mortal_from_plan(world: World, current_month_stamp: MonthStamp, *, nam # 分配宗门职位(根据境界) _assign_sect_rank(avatar, world) + # 初始兵器分配(必须在 Avatar.__post_init__ 之前) + _assign_initial_weapon(avatar) + # 写关系(父母/师徒);不发放宗门法宝 if plan.parent_avatar is not None: plan.parent_avatar.set_relation(avatar, Relation.PARENT) @@ -435,8 +495,9 @@ def build_avatars_from_plan( if sect is not None: avatar.alignment = sect.alignment avatar.technique = get_technique_by_sect(sect) - # 每个宗门只分配一个法宝级兵器给最强者(但不在这里分配,而是让奇遇系统处理) - # 宗门成员初始都是普通兵器 + + # 初始兵器分配(必须在 Avatar.__post_init__ 之前) + _assign_initial_weapon(avatar) if avatar.technique is not None: mapped = attribute_to_root(avatar.technique.attribute) diff --git a/static/game_configs/weapon.csv b/static/game_configs/weapon.csv index 54c9027..7e101fc 100644 --- a/static/game_configs/weapon.csv +++ b/static/game_configs/weapon.csv @@ -12,4 +12,13 @@ id,name,weapon_type,grade,sect_id,desc,effects 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}" +2004,降魔杵,棍,宝物,,降妖除魔,正气凛然。,"{""extra_battle_strength_points"": 2, ""extra_max_hp"": 50}" +2005,紫罗扇,扇,宝物,,扇动间紫气缭绕,洞悉先机。,"{""extra_battle_strength_points"": 2, ""extra_observation_radius"": 1}" +2006,蛟龙鞭,鞭,宝物,,鞭身如蛟龙盘旋,柔中带刚。,"{""extra_battle_strength_points"": 2}" +2007,瑶光琴,琴,宝物,,琴音悠扬,助人悟道。,"{""extra_battle_strength_points"": 2, ""cultivation_speed_rate"": 1.1}" +2008,凤鸣笛,笛,宝物,,笛声如凤鸣九天,清心宁神。,"{""extra_battle_strength_points"": 2, ""cultivation_speed_rate"": 1.1}" +2009,追魂刺,暗器,宝物,,暗器如影随形,取人性命于无声。,"{""extra_battle_strength_points"": 2}"