add long time memory

This commit is contained in:
bridge
2025-11-18 00:39:06 +08:00
parent 5ae2538d86
commit 307ad405ac
13 changed files with 167 additions and 61 deletions

View File

@@ -126,7 +126,34 @@ class ActualActionMixin():
不一定是多个step也有可能就一个step。
新接口:子类必须实现 can_start/start/step/finish。
类变量:
- IS_MAJOR: 是否为大事长期记忆默认False小事/短期记忆)
"""
# 默认为小事(短期记忆)
IS_MAJOR: bool = False
def create_event(self, content: str, related_avatars=None) -> Event:
"""
创建事件的辅助方法自动带上is_major属性
Args:
content: 事件内容
related_avatars: 相关角色ID列表
Returns:
Event对象is_major根据当前Action的IS_MAJOR类变量设置
"""
from src.classes.action.action import Action
# 获取当前类的IS_MAJOR属性
is_major = self.__class__.IS_MAJOR if hasattr(self.__class__, 'IS_MAJOR') else False
return Event(
month_stamp=self.world.month_stamp,
content=content,
related_avatars=related_avatars,
is_major=is_major
)
@abstractmethod
def can_start(self, **params) -> tuple[bool, str]:
@@ -164,7 +191,7 @@ class TimedAction(DefineAction, ActualActionMixin):
duration_months: int = 1
def step(self, **params) -> ActionResult:
if getattr(self, 'start_monthstamp', None) is None:
if not hasattr(self, 'start_monthstamp') or self.start_monthstamp is None:
self.start_monthstamp = self.world.month_stamp
self._execute(**params)
done = (self.world.month_stamp - self.start_monthstamp) >= (self.duration_months - 1)

View File

@@ -15,6 +15,8 @@ class Battle(InstantAction):
STORY_PROMPT: str | None = (
"不要出现具体血量、伤害点数或任何数值表达。"
)
# 战斗是大事(长期记忆)
IS_MAJOR: bool = True
def _get_target(self, avatar_name: str):
"""
@@ -62,7 +64,7 @@ class Battle(InstantAction):
rel_ids.append(target.id)
except Exception:
pass
event = Event(self.world.month_stamp, f"{self.avatar.name}{target_name} 发起战斗(战斗力:{self.avatar.name} {int(s_att)} vs {target_name} {int(s_def)}", related_avatars=rel_ids)
event = Event(self.world.month_stamp, f"{self.avatar.name}{target_name} 发起战斗(战斗力:{self.avatar.name} {int(s_att)} vs {target_name} {int(s_def)}", related_avatars=rel_ids, is_major=True)
# 记录开始事件内容,供故事生成使用
self._start_event_content = event.content
return event
@@ -83,13 +85,13 @@ class Battle(InstantAction):
rel_ids.append(t.id)
except Exception:
pass
result_event = Event(self.world.month_stamp, result_text, related_avatars=rel_ids)
result_event = Event(self.world.month_stamp, result_text, related_avatars=rel_ids, is_major=True)
# 生成战斗小故事(同步调用,与其他动作保持一致)
target = self._get_target(avatar_name)
start_text = getattr(self, "_start_event_content", "") or result_event.content
start_text = self._start_event_content if hasattr(self, '_start_event_content') else result_event.content
story = StoryTeller.tell_from_actors(start_text, result_event.content, self.avatar, target, prompt=self.STORY_PROMPT)
story_event = Event(self.world.month_stamp, story, related_avatars=rel_ids)
story_event = Event(self.world.month_stamp, story, related_avatars=rel_ids, is_story=True)
return [result_event, story_event]

View File

@@ -30,6 +30,8 @@ class Breakthrough(TimedAction):
PARAMS = {}
# 冷却突破应当有CD避免连刷
ACTION_CD_MONTHS: int = 3
# 突破是大事(长期记忆)
IS_MAJOR: bool = True
# 保留类级常量声明,实际读取模块级配置
def calc_success_rate(self) -> float:
@@ -65,8 +67,8 @@ class Breakthrough(TimedAction):
# 记录结果用于 finish 事件
self._last_result = (
"success",
getattr(old_realm, "value", str(old_realm)),
getattr(new_realm, "value", str(new_realm)),
old_realm.value,
new_realm.value,
)
else:
# 突破失败:减少最大寿元上限
@@ -100,12 +102,11 @@ class Breakthrough(TimedAction):
return (ok, "" if ok else "当前不处于瓶颈,无法突破")
def start(self) -> Event:
# 清理状态
# 初始化状态
self._last_result = None
self._success_rate_cached = None
# 预判是否生成故事与选择劫难
old_realm = self.avatar.cultivation_progress.realm
# 仅基于出发境界判断是否生成故事
self._gen_story = old_realm in ALLOW_STORY_FROM_REALMS
if self._gen_story:
self._calamity = TribulationSelector.choose_tribulation(self.avatar)
@@ -113,36 +114,33 @@ class Breakthrough(TimedAction):
else:
self._calamity = None
self._calamity_other = None
return Event(self.world.month_stamp, f"{self.avatar.name} 开始尝试突破境界", related_avatars=[self.avatar.id])
return Event(self.world.month_stamp, f"{self.avatar.name} 开始尝试突破境界", related_avatars=[self.avatar.id], is_major=True)
# TimedAction 已统一 step 逻辑
def finish(self) -> list[Event]:
res = getattr(self, "_last_result", None)
if not (isinstance(res, tuple) and res):
if not self._last_result:
return []
result_ok = res[0] == "success"
if not getattr(self, "_gen_story", False):
result_ok = self._last_result[0] == "success"
if not self._gen_story:
# 不生成故事:不出现劫难,仅简单结果
core_text = f"{self.avatar.name} 突破{'成功' if result_ok else '失败'}"
return [Event(self.world.month_stamp, core_text, related_avatars=[self.avatar.id])]
return [Event(self.world.month_stamp, core_text, related_avatars=[self.avatar.id], is_major=True)]
calamity = getattr(self, "_calamity", "劫难")
calamity = self._calamity
core_text = f"{self.avatar.name} 遭遇了{calamity}劫难,突破{'成功' if result_ok else '失败'}"
rel_ids = [self.avatar.id]
other = getattr(self, "_calamity_other", None)
if other is not None:
if self._calamity_other is not None:
try:
rel_ids.append(other.id)
rel_ids.append(self._calamity_other.id)
except Exception:
pass
events: list[Event] = [Event(self.world.month_stamp, core_text, related_avatars=rel_ids)]
events: list[Event] = [Event(self.world.month_stamp, core_text, related_avatars=rel_ids, is_major=True)]
if True:
# 故事参与者:本体 +(可选)相关角色
prompt = TribulationSelector.get_story_prompt(str(calamity))
story = StoryTeller.tell_from_actors(core_text, ("突破成功" if result_ok else "突破失败"), self.avatar, getattr(self, "_calamity_other", None), prompt=prompt)
events.append(Event(self.world.month_stamp, story, related_avatars=rel_ids))
# 故事参与者:本体 +(可选)相关角色
prompt = TribulationSelector.get_story_prompt(str(calamity))
story = StoryTeller.tell_from_actors(core_text, ("突破成功" if result_ok else "突破失败"), self.avatar, self._calamity_other, prompt=prompt)
events.append(Event(self.world.month_stamp, story, related_avatars=rel_ids, is_story=True))
return events