add dual cultivate

This commit is contained in:
bridge
2025-10-12 22:53:52 +08:00
parent 1e7f365391
commit c03d239272
4 changed files with 126 additions and 3 deletions

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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