diff --git a/src/classes/avatar.py b/src/classes/avatar.py index e233a6a..29121e3 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -138,13 +138,14 @@ class Avatar: personas_info = ", ".join([p.get_detailed_info() for p in self.personas]) if self.personas else "无" items_info = ",".join([f"{item.get_detailed_info()}x{quantity}" for item, quantity in self.items.items()]) if self.items else "无" else: - sect_info = self.sect.get_info() if self.sect is not None else "散修" + # personas和sect一致返回detailed,因为这俩太重要了 + sect_info = self.sect.get_detailed_info() if self.sect is not None else "散修" region_info = region.get_info() if region is not None else "无" alignment_info = self.alignment.get_info() if self.alignment is not None else "未知" root_info = self.root.get_info() technique_info = self.technique.get_info() if self.technique is not None else "无" cultivation_info = self.cultivation_progress.get_info() - personas_info = ", ".join([p.get_info() for p in self.personas]) if self.personas else "无" + personas_info = ", ".join([p.get_detailed_info() for p in self.personas]) if self.personas else "无" items_info = ",".join([f"{item.get_info()}x{quantity}" for item, quantity in self.items.items()]) if self.items else "无" return { diff --git a/src/classes/mutual_action/__init__.py b/src/classes/mutual_action/__init__.py index 404e0bc..3174c43 100644 --- a/src/classes/mutual_action/__init__.py +++ b/src/classes/mutual_action/__init__.py @@ -4,6 +4,7 @@ from .mutual_action import MutualAction from .drive_away import DriveAway from .attack import Attack from .conversation import Conversation +from .dual_cultivation import DualCultivation from src.classes.action.registry import register_action __all__ = [ @@ -11,11 +12,13 @@ __all__ = [ "DriveAway", "Attack", "Conversation", + "DualCultivation", ] # 注册 mutual actions(均为实际动作) register_action(actual=True)(DriveAway) register_action(actual=True)(Attack) register_action(actual=True)(Conversation) +register_action(actual=True)(DualCultivation) diff --git a/src/classes/mutual_action/dual_cultivation.py b/src/classes/mutual_action/dual_cultivation.py new file mode 100644 index 0000000..9b827f1 --- /dev/null +++ b/src/classes/mutual_action/dual_cultivation.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +import random +from pathlib import Path +from typing import TYPE_CHECKING + +from .mutual_action import MutualAction +from src.classes.event import Event +from src.classes.story_teller import StoryTeller +from src.utils.config import CONFIG + +if TYPE_CHECKING: + from src.classes.avatar import Avatar + + +class DualCultivation(MutualAction): + """双修:合欢宗弟子可与感知范围内的异性修士尝试双修。 + + - 仅限发起方为合欢宗成员 + - 仅当目标在感知范围内且为异性 + - 目标可以选择 接受 或 拒绝 + - 若接受:发起者获得大量修为(约为修炼的 3~5 倍,随对方等级浮动),目标不获得修为 + - 成功进入后生成一段“恋爱/双修”的小故事 + """ + + ACTION_NAME = "双修" + COMMENT = "以情入道的双修之术,仅合欢宗弟子可发起,对象可接受或拒绝" + DOABLES_REQUIREMENTS = "发起者为合欢宗;目标在感知范围内且为异性" + PARAMS = {"target_avatar": "AvatarName"} + FEEDBACK_ACTIONS = ["Accept", "Reject"] + + def _get_template_path(self) -> Path: + # 复用 mutual_action 模板,仅需返回 Accept/Reject + return CONFIG.paths.templates / "mutual_action.txt" + + def can_start(self, target_avatar: "Avatar|str|None" = None) -> bool: + if target_avatar is None: + return False + # 必须为合欢宗 + sect = self.avatar.sect + if sect is None or sect.name != "合欢宗": + return False + target = self._get_target_avatar(target_avatar) + if target is None: + return False + # 必须为异性 + if target.gender == self.avatar.gender: + return False + # 必须在感知范围内 + from src.classes.observe import is_within_observation + return is_within_observation(self.avatar, target) + + def start(self, target_avatar: "Avatar|str") -> Event: + target = self._get_target_avatar(target_avatar) + target_name = target.name if target is not None else str(target_avatar) + event = Event(self.world.month_stamp, f"{self.avatar.name} 邀请 {target_name} 进行双修") + # 仅写入历史 + self.avatar.add_event(event, to_sidebar=False) + if target is not None: + target.add_event(event, to_sidebar=False) + # 记录开始文本用于故事生成 + self._start_event_content = event.content + # 初始化内部标记,避免后续 getattr + self._dual_cultivation_success = False + self._dual_exp_gain = 0 + return event + + def _settle_feedback(self, target_avatar: "Avatar", feedback_name: str) -> None: + fb = str(feedback_name).strip() + if fb == "Accept": + # 接受则当场结算修为收益(发起者获得,对象不获得),并记录标记供 finish 生成故事 + self._apply_dual_cultivation_gain(self.avatar, target_avatar) + self._dual_cultivation_success = True + else: + # 拒绝 + self._dual_cultivation_success = False + + def _apply_dual_cultivation_gain(self, initiator: "Avatar", target: "Avatar") -> None: + # 以“修炼”的基础经验为参照:base=100 * essence_density + # 由于此处不依赖具体修炼区域灵气,取一个稳定的基准值:视为 essence_density=1 的基础; + # 然后按对方等级决定 3~5 倍之间的倍数。 + base = 100 + # 对方等级越高,倍数越高(3.0 ~ 5.0),做一个线性映射并夹紧 + other_level = target.cultivation_progress.level + factor = 3.0 + min(2.0, max(0.0, other_level / 60.0 * 2.0)) # level 0-120 -> +0~4,但上限5 + # 添加少量抖动,避免过度稳定 + jitter = random.uniform(-0.2, 0.2) + factor = max(3.0, min(5.0, factor + jitter)) + exp_gain = int(base * factor) + initiator.cultivation_progress.add_exp(exp_gain) + self._dual_exp_gain = exp_gain + + def finish(self, target_avatar: "Avatar|str") -> list[Event]: + target = self._get_target_avatar(target_avatar) + events: list[Event] = [] + success = self._dual_cultivation_success + if target is None: + return events + + if success: + gain = int(self._dual_exp_gain) + result_text = f"{self.avatar.name} 与 {target.name} 成功双修,{self.avatar.name} 获得修为经验 +{gain} 点" + result_event = Event(self.world.month_stamp, result_text) + events.append(result_event) + + # 生成恋爱/双修小故事:使用通用故事模板 + avatar_infos = StoryTeller.build_avatar_infos(self.avatar, target) + start_text = self._start_event_content or result_event.content + story = StoryTeller.tell_story(avatar_infos, start_text, result_event.content) + story_event = Event(self.world.month_stamp, story) + events.append(story_event) + else: + result_text = f"{target.name} 拒绝了与 {self.avatar.name} 的双修" + result_event = Event(self.world.month_stamp, result_text) + events.append(result_event) + + return events + + diff --git a/static/game_configs/sect.csv b/static/game_configs/sect.csv index e5ea4f7..dd1aaca 100644 --- a/static/game_configs/sect.csv +++ b/static/game_configs/sect.csv @@ -5,7 +5,7 @@ id,name,desc,member_act_style,alignment,sect_surnames,male_sect_given_names,fema 3,水镜宗,正道十宗之一,实则严守中立。拥有仙界异宝"彻天水镜"可预知未来。,你处事冷静圆融,喜以柔克刚,擅借力与反制。,中,水;镜;颜;玉;寒;霜;冰;清;沐;阮,岚;照心;寒江,水月;映月;寒影;秋水;轻漪;雪落;如镜;青荷;凝霜,1 4,冥王宗,行走幽冥之道,术法阴冷狠厉。,你言辞冷厉少情,敬畏因果而不惧杀伐,偏向效率与结果。,邪,宋;元;冥;王;玄;幽;夜;白;江;冷,元敕;元难;元烁;冥焰;噬魂;夜阙;幽垣;玄骨;寒魄;冥狱;影灭;夜行,冥霜;幽绫;夜珑;寒鸢;阎铃;魇瑶;玄魄;幽棂,1 5,朱勾宗,邪宗大派。以炼器、机关、暗杀闻名于世,素来阴毒冷僻。,你直面欲望与代价,不惧黑暗,以攻伐见长。,邪,朱;血;狱;百;,血手;勾魂;朱砂;赤狱;凝血;摄魄;夺心;吞灵;渊渟;夜烬,朱绫;夜鸢;绯刃;寒簪;明玥;凝芒;血莹;玉珥,1 -6,合欢宗,以情入道,双修与魅术并重,善驭人心,长于权变。,你辞令婉转,善于拿捏人欲与局势,以柔制刚。,中,合;欢;苏;陆;柳;花;月;楚;顾;白,流烟,婉心;轻柔;疏影;如梦;绮念;月华;惜香;慕雪;倾城,1 +6,合欢宗,以情入道,靠双修增进修为,善驭人心,长于权变。,你辞令婉转,善于拿捏人欲与局势,以柔制刚。,中,合;欢;苏;陆;柳;花;月;楚;顾;白,流烟,婉心;轻柔;疏影;如梦;绮念;月华;惜香;慕雪;倾城,1000 7,镇魂宗,铁血风格,擅安魂、封邪、渡厄,兼有刚烈镇压之术。,你肃穆沉稳,重安魂镇邪,少言而果决。,正,厉;卢;镇;魂;钟;青;凌;白;楚;顾,斗量;阳;镇灵;定魄;钟离;安魂;肃霜;白岭;清钟;涤秽;正鸣;宁川,清宁;素铃;靖霜;澄心;灵钟;镇月;安祈;涤魂,1 8,幽魂噬影宗,镇宗典籍《幽冥录》。幽明气为根基。,你行事隐秘果断,重结果轻虚名,擅潜行与出其不意。,邪,冥;阎;鬼;百;幽;归;应;阴;碧;夜,璃;无藏;馑;噬影;隐魄;夜藏;无相;玄影;摄魂;遁形;寒星;绝响;空痕,采儿;幽姝;冥绮;寒灯;影绫;夜绡;魇歌;暗萝,1 9,千帆城,炼器大宗,巧匠云集。著名法宝有灵灭丝、定魂蓝星、天罗网、万里极光壁、飞翼等。商旅云集,自成体系。,你务实精明,重交易与信誉,崇尚规则与秩序。,中,商;楚;顾;白;苏;林;叶;秦;赵;魏,商行;持衡;清评;问价;立契;通衡;问道;理市;衡准;守约,素蓝;明衡;巧心;青帆;绫舟;观星;衡绫;星槎,1