77
append_i18n.py
Normal file
77
append_i18n.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import os
|
||||
|
||||
zh_content = """
|
||||
|
||||
# Action: Play Extended
|
||||
msgid "action_reading"
|
||||
msgstr "读书"
|
||||
|
||||
msgid "action_tea_tasting"
|
||||
msgstr "品茶"
|
||||
|
||||
msgid "action_traveling"
|
||||
msgstr "游历"
|
||||
|
||||
msgid "action_zither_playing"
|
||||
msgstr "抚琴"
|
||||
|
||||
msgid "action_tea_party"
|
||||
msgstr "茶会"
|
||||
|
||||
msgid "action_chess"
|
||||
msgstr "下棋"
|
||||
|
||||
msgid "{avatar} starts {action}"
|
||||
msgstr "{avatar} 开始{action}"
|
||||
|
||||
msgid "{avatar} finished {action}."
|
||||
msgstr "{avatar} 完成了{action}。"
|
||||
|
||||
msgid "gained {val} cultivation"
|
||||
msgstr "若有所悟,修为增加 {val}"
|
||||
|
||||
msgid "breakthrough probability increased by {val:.1%}"
|
||||
msgstr "心境提升,突破概率增加 {val:.1%}"
|
||||
"""
|
||||
|
||||
en_content = """
|
||||
|
||||
# Action: Play Extended
|
||||
msgid "action_reading"
|
||||
msgstr "Reading"
|
||||
|
||||
msgid "action_tea_tasting"
|
||||
msgstr "Tea Tasting"
|
||||
|
||||
msgid "action_traveling"
|
||||
msgstr "Traveling"
|
||||
|
||||
msgid "action_zither_playing"
|
||||
msgstr "Playing Zither"
|
||||
|
||||
msgid "action_tea_party"
|
||||
msgstr "Tea Party"
|
||||
|
||||
msgid "action_chess"
|
||||
msgstr "Chess"
|
||||
|
||||
msgid "{avatar} starts {action}"
|
||||
msgstr "{avatar} starts {action}"
|
||||
|
||||
msgid "{avatar} finished {action}."
|
||||
msgstr "{avatar} finished {action}."
|
||||
|
||||
msgid "gained {val} cultivation"
|
||||
msgstr "gained {val} cultivation"
|
||||
|
||||
msgid "breakthrough probability increased by {val:.1%}"
|
||||
msgstr "breakthrough probability increased by {val:.1%}"
|
||||
"""
|
||||
|
||||
with open('src/i18n/locales/zh_CN/LC_MESSAGES/messages.po', 'a', encoding='utf-8') as f:
|
||||
f.write(zh_content)
|
||||
|
||||
with open('src/i18n/locales/en_US/LC_MESSAGES/messages.po', 'a', encoding='utf-8') as f:
|
||||
f.write(en_content)
|
||||
|
||||
print("Appended translations successfully.")
|
||||
@@ -22,7 +22,7 @@ from .move_away_from_region import MoveAwayFromRegion
|
||||
from .escape import Escape
|
||||
from .cultivate import Cultivate
|
||||
from .breakthrough import Breakthrough
|
||||
from .play import Play
|
||||
from .play import Reading, TeaTasting, Traveling, ZitherPlaying
|
||||
from .hunt import Hunt
|
||||
from .harvest import Harvest
|
||||
from .sell import Sell
|
||||
@@ -58,7 +58,10 @@ register_action(actual=True)(MoveAwayFromRegion)
|
||||
register_action(actual=False)(Escape)
|
||||
register_action(actual=True)(Cultivate)
|
||||
register_action(actual=True)(Breakthrough)
|
||||
register_action(actual=True)(Play)
|
||||
register_action(actual=True)(Reading)
|
||||
register_action(actual=True)(TeaTasting)
|
||||
register_action(actual=True)(Traveling)
|
||||
register_action(actual=True)(ZitherPlaying)
|
||||
register_action(actual=True)(Hunt)
|
||||
register_action(actual=True)(Harvest)
|
||||
register_action(actual=True)(Sell)
|
||||
@@ -97,7 +100,10 @@ __all__ = [
|
||||
"Escape",
|
||||
"Cultivate",
|
||||
"Breakthrough",
|
||||
"Play",
|
||||
"Reading",
|
||||
"TeaTasting",
|
||||
"Traveling",
|
||||
"ZitherPlaying",
|
||||
"Hunt",
|
||||
"Harvest",
|
||||
"Sell",
|
||||
@@ -116,5 +122,3 @@ __all__ = [
|
||||
"Mine",
|
||||
"Retreat",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -1,44 +1,68 @@
|
||||
from __future__ import annotations
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src.i18n import t
|
||||
from src.classes.action import TimedAction
|
||||
from src.classes.action.action import TimedAction
|
||||
from src.classes.action_runtime import ActionStatus
|
||||
from src.classes.event import Event
|
||||
from src.utils.config import CONFIG
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.avatar import Avatar
|
||||
from src.classes.world import World
|
||||
|
||||
class Play(TimedAction):
|
||||
"""
|
||||
消遣动作,持续半年时间
|
||||
"""
|
||||
|
||||
# 多语言 ID
|
||||
ACTION_NAME_ID = "play_action_name"
|
||||
DESC_ID = "play_description"
|
||||
class BasePlayAction(TimedAction):
|
||||
"""消遣动作基类"""
|
||||
duration_months = 1
|
||||
REQUIREMENTS_ID = "play_requirements"
|
||||
|
||||
# 不需要翻译的常量
|
||||
EMOJI = "🪁"
|
||||
PARAMS = {}
|
||||
|
||||
duration_months = 6
|
||||
def __init__(self, avatar: Avatar, world: World):
|
||||
super().__init__(avatar, world)
|
||||
|
||||
def _try_trigger_benefit(self) -> str:
|
||||
"""尝试触发额外收益 (突破概率)"""
|
||||
prob = CONFIG.play.base_benefit_probability if hasattr(CONFIG, 'play') else 0.05
|
||||
|
||||
if random.random() < prob:
|
||||
rate = 0.2
|
||||
self.avatar.add_breakthrough_rate(rate)
|
||||
return t("breakthrough probability increased by {val:.1%}", val=rate)
|
||||
return ""
|
||||
|
||||
def _execute(self) -> None:
|
||||
"""
|
||||
进行消遣活动
|
||||
"""
|
||||
# 消遣的具体逻辑可以在这里实现
|
||||
# 比如增加心情值、减少压力等
|
||||
pass
|
||||
|
||||
def can_start(self) -> tuple[bool, str]:
|
||||
return True, ""
|
||||
pass # 具体逻辑由子类实现或只需记录事件
|
||||
|
||||
def start(self) -> Event:
|
||||
content = t("{avatar} begins leisure activities", avatar=self.avatar.name)
|
||||
return Event(self.world.month_stamp, content, related_avatars=[self.avatar.id])
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
# 通用开始事件
|
||||
return Event(self.world.month_stamp, t("{avatar} starts {action}", avatar=self.avatar.name, action=self.get_action_name()), related_avatars=[self.avatar.id])
|
||||
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
# 结算时尝试触发收益
|
||||
benefit_msg = self._try_trigger_benefit()
|
||||
content = t("{avatar} finished {action}.", avatar=self.avatar.name, action=self.get_action_name())
|
||||
if benefit_msg:
|
||||
content += f" {benefit_msg}"
|
||||
|
||||
return [Event(self.world.month_stamp, content, related_avatars=[self.avatar.id])]
|
||||
|
||||
# 具体动作实现
|
||||
class Reading(BasePlayAction):
|
||||
ACTION_NAME_ID = "action_reading"
|
||||
DESC_ID = "action_reading_desc"
|
||||
EMOJI = "📖"
|
||||
|
||||
class TeaTasting(BasePlayAction):
|
||||
ACTION_NAME_ID = "action_tea_tasting"
|
||||
DESC_ID = "action_tea_tasting_desc"
|
||||
EMOJI = "🍵"
|
||||
|
||||
class Traveling(BasePlayAction):
|
||||
ACTION_NAME_ID = "action_traveling"
|
||||
DESC_ID = "action_traveling_desc"
|
||||
EMOJI = "🧳"
|
||||
|
||||
class ZitherPlaying(BasePlayAction):
|
||||
ACTION_NAME_ID = "action_zither_playing"
|
||||
DESC_ID = "action_zither_playing_desc"
|
||||
EMOJI = "🎵"
|
||||
|
||||
@@ -103,9 +103,9 @@ class LLMAI(AI):
|
||||
|
||||
# 更新情绪
|
||||
from src.classes.emotions import EmotionType
|
||||
raw_emotion = r.get("current_emotion", "平静")
|
||||
raw_emotion = r.get("current_emotion", "emotion_calm")
|
||||
try:
|
||||
# 尝试通过 value (中文) 获取枚举
|
||||
# 尝试通过 value 获取枚举
|
||||
avatar.emotion = EmotionType(raw_emotion)
|
||||
except ValueError:
|
||||
avatar.emotion = EmotionType.CALM
|
||||
|
||||
@@ -127,6 +127,18 @@ class Avatar(
|
||||
# 关系交互计数器: key=target_id, value={"count": 0, "checked_times": 0}
|
||||
relation_interaction_states: dict[str, dict[str, int]] = field(default_factory=lambda: defaultdict(lambda: {"count": 0, "checked_times": 0}))
|
||||
|
||||
def add_breakthrough_rate(self, rate: float, duration: int = 1) -> None:
|
||||
"""
|
||||
增加突破成功率(临时效果)
|
||||
"""
|
||||
self.temporary_effects.append({
|
||||
"source": "play_benefit",
|
||||
"effects": {"extra_breakthrough_success_rate": rate},
|
||||
"start_month": int(self.world.month_stamp),
|
||||
"duration": duration
|
||||
})
|
||||
self.recalc_effects()
|
||||
|
||||
# ========== 宗门相关 ==========
|
||||
|
||||
def consume_elixir(self, elixir: Elixir) -> bool:
|
||||
@@ -333,7 +345,8 @@ class Avatar(
|
||||
action = self.current_action.action
|
||||
# 使用 get_action_name() 获取翻译后的动作名称
|
||||
return action.get_action_name()
|
||||
return "思考"
|
||||
from src.i18n import t
|
||||
return t("action_thinking")
|
||||
|
||||
def __post_init__(self):
|
||||
"""在Avatar创建后自动初始化tile和HP"""
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from enum import Enum
|
||||
|
||||
class EmotionType(Enum):
|
||||
CALM = "平静"
|
||||
HAPPY = "开心"
|
||||
ANGRY = "愤怒"
|
||||
SAD = "悲伤"
|
||||
FEARFUL = "恐惧"
|
||||
SURPRISED = "惊讶"
|
||||
ANTICIPATING = "期待"
|
||||
DISGUSTED = "厌恶"
|
||||
CONFUSED = "疑惑"
|
||||
TIRED = "疲惫"
|
||||
CALM = "emotion_calm"
|
||||
HAPPY = "emotion_happy"
|
||||
ANGRY = "emotion_angry"
|
||||
SAD = "emotion_sad"
|
||||
FEARFUL = "emotion_fearful"
|
||||
SURPRISED = "emotion_surprised"
|
||||
ANTICIPATING = "emotion_anticipating"
|
||||
DISGUSTED = "emotion_disgusted"
|
||||
CONFUSED = "emotion_confused"
|
||||
TIRED = "emotion_tired"
|
||||
|
||||
# 情绪对应的 Emoji 配置
|
||||
EMOTION_EMOJIS = {
|
||||
|
||||
@@ -10,6 +10,7 @@ from .impart import Impart
|
||||
from .gift import Gift
|
||||
from .spar import Spar
|
||||
from .occupy import Occupy
|
||||
from .play import TeaParty, Chess
|
||||
from src.classes.action.registry import register_action
|
||||
|
||||
__all__ = [
|
||||
@@ -23,6 +24,8 @@ __all__ = [
|
||||
"Gift",
|
||||
"Spar",
|
||||
"Occupy",
|
||||
"TeaParty",
|
||||
"Chess",
|
||||
]
|
||||
|
||||
# 注册 mutual actions(均为实际动作)
|
||||
@@ -35,5 +38,5 @@ register_action(actual=True)(Impart)
|
||||
register_action(actual=True)(Gift)
|
||||
register_action(actual=True)(Spar)
|
||||
register_action(actual=True)(Occupy)
|
||||
|
||||
|
||||
register_action(actual=True)(TeaParty)
|
||||
register_action(actual=True)(Chess)
|
||||
|
||||
53
src/classes/mutual_action/play.py
Normal file
53
src/classes/mutual_action/play.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
import random
|
||||
|
||||
from src.classes.mutual_action.mutual_action import MutualAction
|
||||
from src.i18n import t
|
||||
from src.utils.config import CONFIG
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.avatar import Avatar
|
||||
from src.classes.world import World
|
||||
|
||||
def try_trigger_play_benefit(avatar: Avatar) -> str:
|
||||
"""
|
||||
尝试触发消遣收益 (复用单人消遣的逻辑)
|
||||
"""
|
||||
prob = CONFIG.play.base_benefit_probability if hasattr(CONFIG, 'play') else 0.05
|
||||
|
||||
if random.random() < prob:
|
||||
rate = 0.2
|
||||
avatar.add_breakthrough_rate(rate)
|
||||
return t("breakthrough probability increased by {val:.1%}", val=rate)
|
||||
return ""
|
||||
|
||||
class TeaParty(MutualAction):
|
||||
"""茶会:双人互动"""
|
||||
ACTION_NAME_ID = "action_tea_party"
|
||||
DESC_ID = "action_tea_party_desc"
|
||||
STORY_PROMPT_ID = "action_tea_party_story_prompt"
|
||||
REQUIREMENTS_ID = "play_requirements"
|
||||
EMOJI = "🍵"
|
||||
FEEDBACK_ACTIONS = ["Accept", "Reject"]
|
||||
|
||||
def _settle_feedback(self, target_avatar: Avatar, feedback_name: str) -> None:
|
||||
if feedback_name == "Accept":
|
||||
# 尝试给双方触发收益
|
||||
try_trigger_play_benefit(self.avatar)
|
||||
try_trigger_play_benefit(target_avatar)
|
||||
|
||||
class Chess(MutualAction):
|
||||
"""下棋:双人互动"""
|
||||
ACTION_NAME_ID = "action_chess"
|
||||
DESC_ID = "action_chess_desc"
|
||||
STORY_PROMPT_ID = "action_chess_story_prompt"
|
||||
REQUIREMENTS_ID = "play_requirements"
|
||||
EMOJI = "♟️"
|
||||
FEEDBACK_ACTIONS = ["Accept", "Reject"]
|
||||
|
||||
def _settle_feedback(self, target_avatar: Avatar, feedback_name: str) -> None:
|
||||
if feedback_name == "Accept":
|
||||
# 尝试给双方触发收益
|
||||
try_trigger_play_benefit(self.avatar)
|
||||
try_trigger_play_benefit(target_avatar)
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -726,7 +726,7 @@ msgid "play_description"
|
||||
msgstr "消遣,放松身心"
|
||||
|
||||
msgid "play_requirements"
|
||||
msgstr "无限制"
|
||||
msgstr "目标在交互范围内"
|
||||
|
||||
msgid "{avatar} begins leisure activities"
|
||||
msgstr "{avatar} 开始消遣"
|
||||
@@ -1853,34 +1853,34 @@ msgstr "{names}({price}灵石)"
|
||||
msgid "Sell: "
|
||||
msgstr "出售:"
|
||||
|
||||
msgid "平静"
|
||||
msgid "emotion_calm"
|
||||
msgstr "平静"
|
||||
|
||||
msgid "开心"
|
||||
msgid "emotion_happy"
|
||||
msgstr "开心"
|
||||
|
||||
msgid "愤怒"
|
||||
msgid "emotion_angry"
|
||||
msgstr "愤怒"
|
||||
|
||||
msgid "悲伤"
|
||||
msgid "emotion_sad"
|
||||
msgstr "悲伤"
|
||||
|
||||
msgid "恐惧"
|
||||
msgid "emotion_fearful"
|
||||
msgstr "恐惧"
|
||||
|
||||
msgid "惊讶"
|
||||
msgid "emotion_surprised"
|
||||
msgstr "惊讶"
|
||||
|
||||
msgid "期待"
|
||||
msgid "emotion_anticipating"
|
||||
msgstr "期待"
|
||||
|
||||
msgid "厌恶"
|
||||
msgid "emotion_disgusted"
|
||||
msgstr "厌恶"
|
||||
|
||||
msgid "疑惑"
|
||||
msgid "emotion_confused"
|
||||
msgstr "疑惑"
|
||||
|
||||
msgid "疲惫"
|
||||
msgid "emotion_tired"
|
||||
msgstr "疲惫"
|
||||
|
||||
# ============================================================================
|
||||
@@ -2041,91 +2041,13 @@ msgstr "{root_name}({elements})"
|
||||
msgid "{sect} {rank}"
|
||||
msgstr "{sect}{rank}"
|
||||
|
||||
# 灵根
|
||||
msgid "金灵根"
|
||||
msgstr "金灵根"
|
||||
|
||||
msgid "木灵根"
|
||||
msgstr "木灵根"
|
||||
|
||||
msgid "水灵根"
|
||||
msgstr "水灵根"
|
||||
|
||||
msgid "火灵根"
|
||||
msgstr "火灵根"
|
||||
|
||||
msgid "土灵根"
|
||||
msgstr "土灵根"
|
||||
|
||||
msgid "雷灵根"
|
||||
msgstr "雷灵根"
|
||||
|
||||
msgid "冰灵根"
|
||||
msgstr "冰灵根"
|
||||
|
||||
msgid "风灵根"
|
||||
msgstr "风灵根"
|
||||
|
||||
msgid "暗灵根"
|
||||
msgstr "暗灵根"
|
||||
|
||||
msgid "天灵根"
|
||||
msgstr "天灵根"
|
||||
|
||||
# 宗门职位
|
||||
msgid "掌门"
|
||||
msgstr "掌门"
|
||||
|
||||
msgid "长老"
|
||||
msgstr "长老"
|
||||
|
||||
msgid "内门弟子"
|
||||
msgstr "内门弟子"
|
||||
|
||||
msgid "外门弟子"
|
||||
msgstr "外门弟子"
|
||||
|
||||
# 外貌
|
||||
msgid "奇丑"
|
||||
msgstr "奇丑"
|
||||
|
||||
msgid "丑陋"
|
||||
msgstr "丑陋"
|
||||
|
||||
msgid "粗陋"
|
||||
msgstr "粗陋"
|
||||
|
||||
msgid "寒素"
|
||||
msgstr "寒素"
|
||||
|
||||
msgid "清秀"
|
||||
msgstr "清秀"
|
||||
|
||||
msgid "秀致"
|
||||
msgstr "秀致"
|
||||
|
||||
msgid "俊美"
|
||||
msgstr "俊美"
|
||||
|
||||
msgid "倾城"
|
||||
msgstr "倾城"
|
||||
|
||||
msgid "绝色"
|
||||
msgstr "绝色"
|
||||
|
||||
msgid "惊艳"
|
||||
msgstr "惊艳"
|
||||
|
||||
msgid "你长得很俊,回头率很高。"
|
||||
msgstr "你长得很俊,回头率很高。"
|
||||
|
||||
msgid "你长得很美,很抢眼。"
|
||||
msgstr "你长得很美,很抢眼。"
|
||||
|
||||
# ============================================================================
|
||||
# Additional (New)
|
||||
# ============================================================================
|
||||
|
||||
msgid "action_thinking"
|
||||
msgstr "思考"
|
||||
|
||||
msgid "{alignment}: {description}"
|
||||
msgstr "{alignment}:{description}"
|
||||
|
||||
@@ -2245,3 +2167,58 @@ msgstr "当阵营为{align}时"
|
||||
|
||||
msgid "When using {weapon_type}"
|
||||
msgstr "当使用{weapon_type}时"
|
||||
|
||||
# Action: Play Extended
|
||||
msgid "action_reading"
|
||||
msgstr "读书"
|
||||
|
||||
msgid "action_tea_tasting"
|
||||
msgstr "品茶"
|
||||
|
||||
msgid "action_traveling"
|
||||
msgstr "游历"
|
||||
|
||||
msgid "action_zither_playing"
|
||||
msgstr "抚琴"
|
||||
|
||||
msgid "action_tea_party"
|
||||
msgstr "茶会"
|
||||
|
||||
msgid "action_chess"
|
||||
msgstr "下棋"
|
||||
|
||||
msgid "{avatar} starts {action}"
|
||||
msgstr "{avatar} 开始{action}"
|
||||
|
||||
msgid "{avatar} finished {action}."
|
||||
msgstr "{avatar} 完成了{action}。"
|
||||
|
||||
msgid "gained {val} cultivation"
|
||||
msgstr "若有所悟,修为增加 {val}"
|
||||
|
||||
msgid "breakthrough probability increased by {val:.1%}"
|
||||
msgstr "心境提升,突破概率增加 {val:.1%}"
|
||||
|
||||
msgid "action_reading_desc"
|
||||
msgstr "研读古籍,增长见闻。"
|
||||
|
||||
msgid "action_tea_tasting_desc"
|
||||
msgstr "品味清茶,静心养性。"
|
||||
|
||||
msgid "action_traveling_desc"
|
||||
msgstr "游历四方,感悟天地。"
|
||||
|
||||
msgid "action_zither_playing_desc"
|
||||
msgstr "抚琴奏乐,陶冶情操。"
|
||||
|
||||
msgid "action_tea_party_desc"
|
||||
msgstr "与友共品清茶。"
|
||||
|
||||
msgid "action_chess_desc"
|
||||
msgstr "对弈博弈,磨砺心智。"
|
||||
|
||||
msgid "action_tea_party_story_prompt"
|
||||
msgstr "两人正在举行一场雅致的茶会。"
|
||||
|
||||
msgid "action_chess_story_prompt"
|
||||
msgstr "两人正在对弈下棋。"
|
||||
|
||||
@@ -158,7 +158,7 @@ class AvatarLoadMixin:
|
||||
|
||||
# 恢复情绪
|
||||
from src.classes.emotions import EmotionType
|
||||
emotion_str = data.get("emotion", "平静")
|
||||
emotion_str = data.get("emotion", "emotion_calm")
|
||||
try:
|
||||
avatar.emotion = EmotionType(emotion_str)
|
||||
except ValueError:
|
||||
|
||||
@@ -59,3 +59,6 @@ frontend:
|
||||
cloud_freq: low
|
||||
system:
|
||||
language: zh-CN
|
||||
|
||||
play:
|
||||
base_benefit_probability: 0.05
|
||||
|
||||
@@ -71,3 +71,9 @@ id,key,name,exclusion_keys,desc,rarity,condition,effects
|
||||
69,DEVOTED,Devoted,LUSTFUL;ROMANTIC;HEARTLESS,"You are devoted in love, once committed, you will not change.",N,,
|
||||
70,ROMANTIC,Romantic,DESIRELESS;HEARTLESS;DEVOTED,"You are easily moved by emotions, full of longing for beautiful feelings.",N,,
|
||||
71,HEARTLESS,Heartless,DEVOTED;ROMANTIC;FRIENDLY,"You are indifferent to emotions, not easily moved.",N,,
|
||||
1001,PLAYFUL,Playful,RATIONAL;CULTIVATION_OBSESSED,"Indulging in fun, not thinking about progress.",N,,
|
||||
1002,BOOKWORM,Bookworm,RASH,"Addicted to books.",R,,
|
||||
1003,TEA_LOVER,Tea Lover,,"Can't go a day without tea.",R,,
|
||||
1004,MUSICIAN,Musician,,"Good at playing the zither.",R,,
|
||||
1005,TRAVELER,Traveler,HOMEBODY,"Reading ten thousand books is not as good as traveling ten thousand miles.",R,,
|
||||
1006,CHESS_PLAYER,Chess Player,,"Good chess players live long.",R,,
|
||||
|
@@ -21,5 +21,5 @@ Requirements:
|
||||
- The goal should fit the character's status and the Xianxia worldview.
|
||||
- Do not fabricate information that has not appeared.
|
||||
- It can be grand or specific.
|
||||
- Primarily refer to character traits and personality, while considering sect, alignment, interpersonal relationships, historical events, etc.
|
||||
- Primarily refer to character traits and personality, while considering sect, alignment, interpersonal relationships, historical events, etc. It can be related to cultivation, interpersonal relationships, personality, pastimes, self-cultivation, sects, or production activities.
|
||||
- "thinking" should provide a detailed analysis; "long_term_objective" should only return the goal content itself, but don't mention how long it will take to complete.
|
||||
|
||||
@@ -71,3 +71,9 @@ id,key,name,exclusion_keys,desc,rarity,condition,effects
|
||||
69,DEVOTED,专情,LUSTFUL;ROMANTIC;HEARTLESS,你对爱情专一,一旦认定便不会改变。,N,,
|
||||
70,ROMANTIC,多情,DESIRELESS;HEARTLESS;DEVOTED,你容易动情,对美好的感情充满向往。,N,,
|
||||
71,HEARTLESS,薄情,DEVOTED;ROMANTIC;FRIENDLY,你对感情淡漠,不会轻易动心。,N,,
|
||||
1001,PLAYFUL,贪玩,RATIONAL;CULTIVATION_OBSESSED,沉迷玩乐,不思进取。,N,,
|
||||
1002,BOOKWORM,书痴,RASH,嗜书如命。,R,,
|
||||
1003,TEA_LOVER,茶痴,,宁可三日无食,不可一日无茶。,R,,
|
||||
1004,MUSICIAN,琴师,,善抚琴。,R,,
|
||||
1005,TRAVELER,游历者,HOMEBODY,读万卷书不如行万里路。,R,,
|
||||
1006,CHESS_PLAYER,棋痴,,善弈者长生。,R,,
|
||||
|
@@ -21,5 +21,5 @@
|
||||
- 目标要符合角色身份和修仙世界观
|
||||
- 不要虚构未出现的信息
|
||||
- 可以是宏大的也可以是具体的
|
||||
- 主要参考角色特质和性格,兼顾宗门、阵营、人际关系、历史事件等
|
||||
- 主要参考角色特质和性格,兼顾宗门、阵营、人际关系、历史事件等。可以和修炼相关、也可以和人际关系、性格、消遣、修养、宗门、生产活动相关。
|
||||
- thinking要详细分析,long_term_objective只返回目标内容本身,但别提多久完成
|
||||
93
tests/test_action_play.py
Normal file
93
tests/test_action_play.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, patch
|
||||
import asyncio
|
||||
|
||||
from src.classes.action.play import Reading, TeaTasting, Traveling, ZitherPlaying
|
||||
from src.classes.mutual_action.play import TeaParty, Chess
|
||||
from src.classes.event import Event
|
||||
from src.utils.config import CONFIG
|
||||
|
||||
class TestActionPlay:
|
||||
|
||||
@pytest.fixture
|
||||
def play_avatar(self, dummy_avatar):
|
||||
"""配置一个适合消遣的角色"""
|
||||
# 确保没有初始临时效果
|
||||
dummy_avatar.temporary_effects = []
|
||||
return dummy_avatar
|
||||
|
||||
def test_single_play_action_instantiation(self, play_avatar):
|
||||
"""测试单人消遣动作实例化"""
|
||||
world = play_avatar.world
|
||||
actions = [Reading, TeaTasting, Traveling, ZitherPlaying]
|
||||
|
||||
for action_cls in actions:
|
||||
action = action_cls(play_avatar, world)
|
||||
assert action.duration_months == 1
|
||||
assert action.can_start()[0] is True
|
||||
|
||||
@patch('src.classes.action.play.random.random')
|
||||
def test_single_play_benefit_trigger(self, mock_random, play_avatar):
|
||||
"""测试单人消遣触发收益"""
|
||||
# mock random < 0.05 to trigger benefit
|
||||
# CONFIG.play.base_benefit_probability is 0.05
|
||||
mock_random.return_value = 0.01
|
||||
|
||||
action = Reading(play_avatar, play_avatar.world)
|
||||
|
||||
# Execute finish (async)
|
||||
events = asyncio.run(action.finish())
|
||||
|
||||
# Check event content
|
||||
assert len(events) == 1
|
||||
# 检查是否包含突破概率提升的描述 (英文或中文,取决于环境,这里假设 conftest 强制了中文)
|
||||
# conftest.py force_chinese_language fixture sets language to zh-CN
|
||||
# "breakthrough probability increased by 20.0%" -> zh-CN: "心境提升,突破概率增加 20.0%"
|
||||
# Let's check for "20.0%" to be safe across languages if translation fails,
|
||||
# or check the translation key if possible. But here we get the formatted string.
|
||||
assert "20.0%" in events[0].content
|
||||
|
||||
# Check effect applied
|
||||
# Need to check if temporary_effects has the entry
|
||||
assert len(play_avatar.temporary_effects) == 1
|
||||
effect = play_avatar.temporary_effects[0]
|
||||
assert effect["source"] == "play_benefit"
|
||||
assert effect["effects"]["extra_breakthrough_success_rate"] == 0.2
|
||||
assert effect["duration"] == 1
|
||||
|
||||
@patch('src.classes.action.play.random.random')
|
||||
def test_single_play_no_benefit(self, mock_random, play_avatar):
|
||||
"""测试单人消遣未触发收益"""
|
||||
# mock random >= 0.05
|
||||
mock_random.return_value = 0.1
|
||||
|
||||
action = Reading(play_avatar, play_avatar.world)
|
||||
|
||||
events = asyncio.run(action.finish())
|
||||
|
||||
assert len(events) == 1
|
||||
assert "20.0%" not in events[0].content
|
||||
assert len(play_avatar.temporary_effects) == 0
|
||||
|
||||
@patch('src.classes.mutual_action.play.random.random')
|
||||
def test_mutual_play_benefit(self, mock_random, play_avatar):
|
||||
"""测试双人消遣触发收益"""
|
||||
# Setup target avatar
|
||||
target_avatar = MagicMock()
|
||||
target_avatar.name = "Friend"
|
||||
# MagicMock doesn't have temporary_effects list by default, but add_breakthrough_rate is called on it
|
||||
|
||||
# mock random < 0.05
|
||||
mock_random.return_value = 0.01
|
||||
|
||||
action = TeaParty(play_avatar, play_avatar.world)
|
||||
|
||||
# Simulate feedback "Accept"
|
||||
action._settle_feedback(target_avatar, "Accept")
|
||||
|
||||
# Check initiator benefit
|
||||
assert len(play_avatar.temporary_effects) == 1
|
||||
assert play_avatar.temporary_effects[0]["effects"]["extra_breakthrough_success_rate"] == 0.2
|
||||
|
||||
# Check target benefit
|
||||
target_avatar.add_breakthrough_rate.assert_called_with(0.2)
|
||||
@@ -21,7 +21,7 @@ Testing Strategy:
|
||||
"AvatarName": {
|
||||
"action_name_params_pairs": [["cultivate", {"duration": 10}]],
|
||||
"avatar_thinking": "...",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
results = await ai._decide(world, [avatar])
|
||||
@@ -67,7 +67,7 @@ class TestLLMAIDecide:
|
||||
],
|
||||
"avatar_thinking": "I should cultivate to get stronger.",
|
||||
"short_term_objective": "Reach Foundation Establishment",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class TestLLMAIDecide:
|
||||
],
|
||||
"avatar_thinking": "Time to rest after cultivation.",
|
||||
"short_term_objective": "Recover energy",
|
||||
"current_emotion": "疲惫"
|
||||
"current_emotion": "emotion_tired"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class TestLLMAIDecide:
|
||||
],
|
||||
"avatar_thinking": "Just cultivating.",
|
||||
"short_term_objective": "Get stronger",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ class TestLLMAIDecide:
|
||||
],
|
||||
"avatar_thinking": "Mixed formats.",
|
||||
"short_term_objective": "Test edge cases",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ class TestLLMAIDecide:
|
||||
"action_name_params_pairs": [["cultivate", {}]],
|
||||
"avatar_thinking": "Should not be used.",
|
||||
"short_term_objective": "Not for test avatar",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ class TestLLMAIDecide:
|
||||
],
|
||||
"avatar_thinking": "All invalid.",
|
||||
"short_term_objective": "Test",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,15 +258,15 @@ class TestLLMAIEmotionUpdate:
|
||||
async def test_decide_updates_emotion_with_valid_value(self, mock_world, test_avatar):
|
||||
"""Test that valid emotion string updates avatar.emotion correctly."""
|
||||
emotions_to_test = [
|
||||
("开心", EmotionType.HAPPY),
|
||||
("愤怒", EmotionType.ANGRY),
|
||||
("悲伤", EmotionType.SAD),
|
||||
("恐惧", EmotionType.FEARFUL),
|
||||
("惊讶", EmotionType.SURPRISED),
|
||||
("期待", EmotionType.ANTICIPATING),
|
||||
("厌恶", EmotionType.DISGUSTED),
|
||||
("疑惑", EmotionType.CONFUSED),
|
||||
("疲惫", EmotionType.TIRED),
|
||||
("emotion_happy", EmotionType.HAPPY),
|
||||
("emotion_angry", EmotionType.ANGRY),
|
||||
("emotion_sad", EmotionType.SAD),
|
||||
("emotion_fearful", EmotionType.FEARFUL),
|
||||
("emotion_surprised", EmotionType.SURPRISED),
|
||||
("emotion_anticipating", EmotionType.ANTICIPATING),
|
||||
("emotion_disgusted", EmotionType.DISGUSTED),
|
||||
("emotion_confused", EmotionType.CONFUSED),
|
||||
("emotion_tired", EmotionType.TIRED),
|
||||
]
|
||||
|
||||
ai = LLMAI()
|
||||
@@ -329,7 +329,7 @@ class TestLLMAIEmotionUpdate:
|
||||
mock_llm.return_value = mock_response
|
||||
await ai._decide(mock_world, [test_avatar])
|
||||
|
||||
# Default is "平静" which maps to CALM.
|
||||
# Default is "emotion_calm" which maps to CALM.
|
||||
assert test_avatar.emotion == EmotionType.CALM
|
||||
|
||||
|
||||
@@ -419,7 +419,7 @@ class TestLLMAIBatchProcessing:
|
||||
"action_name_params_pairs": [["cultivate", {"duration": 10}]],
|
||||
"avatar_thinking": "A is cultivating.",
|
||||
"short_term_objective": "A's goal",
|
||||
"current_emotion": "开心"
|
||||
"current_emotion": "emotion_happy"
|
||||
}
|
||||
}
|
||||
else:
|
||||
@@ -428,7 +428,7 @@ class TestLLMAIBatchProcessing:
|
||||
"action_name_params_pairs": [["move", {"target_x": 2, "target_y": 2}]],
|
||||
"avatar_thinking": "B is moving.",
|
||||
"short_term_objective": "B's goal",
|
||||
"current_emotion": "愤怒"
|
||||
"current_emotion": "emotion_angry"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ class TestAIDecideWrapper:
|
||||
"action_name_params_pairs": [["cultivate", {}]],
|
||||
"avatar_thinking": "Testing.",
|
||||
"short_term_objective": "Test",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,7 +539,7 @@ class TestLLMAIThinkingFieldVariants:
|
||||
"action_name_params_pairs": [["cultivate", {}]],
|
||||
"thinking": "Using thinking field.", # Not avatar_thinking
|
||||
"short_term_objective": "Test",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +561,7 @@ class TestLLMAIThinkingFieldVariants:
|
||||
"avatar_thinking": "Preferred field.",
|
||||
"thinking": "Fallback field.",
|
||||
"short_term_objective": "Test",
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,7 +581,7 @@ class TestLLMAIThinkingFieldVariants:
|
||||
test_avatar.name: {
|
||||
"action_name_params_pairs": [["cultivate", {}]],
|
||||
# No avatar_thinking, thinking, or short_term_objective
|
||||
"current_emotion": "平静"
|
||||
"current_emotion": "emotion_calm"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user