From b74014f9f269efea41e8edb05548fed832e6cb1b Mon Sep 17 00:00:00 2001 From: bridge Date: Sun, 4 Jan 2026 22:49:20 +0800 Subject: [PATCH] add emotion --- src/classes/ai.py | 10 +++++++ src/classes/avatar/core.py | 2 ++ src/classes/avatar/info_presenter.py | 9 +++++++ src/classes/emotions.py | 27 +++++++++++++++++++ src/sim/load/avatar_load_mixin.py | 8 ++++++ src/sim/save/avatar_save_mixin.py | 1 + static/game_configs/persona.csv | 1 + static/templates/ai.txt | 1 + .../game/panels/info/AvatarDetail.vue | 5 ++++ web/src/types/core.ts | 7 +++++ 10 files changed, 71 insertions(+) create mode 100644 src/classes/emotions.py diff --git a/src/classes/ai.py b/src/classes/ai.py index 4fa7e80..3122259 100644 --- a/src/classes/ai.py +++ b/src/classes/ai.py @@ -99,6 +99,16 @@ class LLMAI(AI): avatar_thinking = r.get("avatar_thinking", r.get("thinking", "")) short_term_objective = r.get("short_term_objective", "") + + # 更新情绪 + from src.classes.emotions import EmotionType + raw_emotion = r.get("current_emotion", "平静") + try: + # 尝试通过 value (中文) 获取枚举 + avatar.emotion = EmotionType(raw_emotion) + except ValueError: + avatar.emotion = EmotionType.CALM + results[avatar] = (pairs, avatar_thinking, short_term_objective) return results diff --git a/src/classes/avatar/core.py b/src/classes/avatar/core.py index ba56206..186c20c 100644 --- a/src/classes/avatar/core.py +++ b/src/classes/avatar/core.py @@ -37,6 +37,7 @@ from src.classes.appearance import Appearance, get_random_appearance from src.classes.spirit_animal import SpiritAnimal from src.classes.long_term_objective import LongTermObjective from src.classes.nickname_data import Nickname +from src.classes.emotions import EmotionType from src.utils.config import CONFIG # Mixin 导入 @@ -106,6 +107,7 @@ class Avatar( auxiliary: Optional[Auxiliary] = None spirit_animal: Optional[SpiritAnimal] = None nickname: Optional[Nickname] = None + emotion: EmotionType = EmotionType.CALM custom_pic_id: Optional[int] = None is_dead: bool = False diff --git a/src/classes/avatar/info_presenter.py b/src/classes/avatar/info_presenter.py index 57a1b78..ab3957e 100644 --- a/src/classes/avatar/info_presenter.py +++ b/src/classes/avatar/info_presenter.py @@ -12,6 +12,7 @@ if TYPE_CHECKING: from src.classes.battle import get_base_strength from src.classes.relation import get_relation_label +from src.classes.emotions import EMOTION_EMOJIS, EmotionType from src.utils.config import CONFIG @@ -84,6 +85,7 @@ def get_avatar_info(avatar: "Avatar", detailed: bool = False) -> dict: "外貌": appearance_info, "兵器": weapon_info, "辅助装备": auxiliary_info, + "情绪": avatar.emotion.value, } if detailed: @@ -109,6 +111,8 @@ def get_avatar_structured_info(avatar: "Avatar") -> dict: 获取结构化的角色信息,用于前端展示和交互。 """ # 基础信息 + emoji = EMOTION_EMOJIS.get(avatar.emotion, EMOTION_EMOJIS[EmotionType.CALM]) + info = { "id": avatar.id, "name": avatar.name, @@ -121,6 +125,11 @@ def get_avatar_structured_info(avatar: "Avatar") -> dict: "alignment": str(avatar.alignment) if avatar.alignment else "未知", "magic_stone": avatar.magic_stone.value, "base_battle_strength": int(get_base_strength(avatar)), + "emotion": { + "name": avatar.emotion.value, + "emoji": emoji, + "desc": avatar.emotion.value + }, "thinking": avatar.thinking, "short_term_objective": avatar.short_term_objective, "long_term_objective": avatar.long_term_objective.content if avatar.long_term_objective else "", diff --git a/src/classes/emotions.py b/src/classes/emotions.py new file mode 100644 index 0000000..3105279 --- /dev/null +++ b/src/classes/emotions.py @@ -0,0 +1,27 @@ +from enum import Enum + +class EmotionType(Enum): + CALM = "平静" + HAPPY = "开心" + ANGRY = "愤怒" + SAD = "悲伤" + FEARFUL = "恐惧" + SURPRISED = "惊讶" + ANTICIPATING = "期待" + DISGUSTED = "厌恶" + CONFUSED = "疑惑" + TIRED = "疲惫" + +# 情绪对应的 Emoji 配置 +EMOTION_EMOJIS = { + EmotionType.CALM: "😌", + EmotionType.HAPPY: "😄", + EmotionType.ANGRY: "😡", + EmotionType.SAD: "😢", + EmotionType.FEARFUL: "😨", + EmotionType.SURPRISED: "😲", + EmotionType.ANTICIPATING: "🤩", + EmotionType.DISGUSTED: "🤢", + EmotionType.CONFUSED: "😕", + EmotionType.TIRED: "😫", +} diff --git a/src/sim/load/avatar_load_mixin.py b/src/sim/load/avatar_load_mixin.py index 9fcdcb4..e1cf7b4 100644 --- a/src/sim/load/avatar_load_mixin.py +++ b/src/sim/load/avatar_load_mixin.py @@ -155,6 +155,14 @@ class AvatarLoadMixin: from src.classes.nickname_data import Nickname avatar.nickname = Nickname.from_dict(data.get("nickname")) + # 恢复情绪 + from src.classes.emotions import EmotionType + emotion_str = data.get("emotion", "平静") + try: + avatar.emotion = EmotionType(emotion_str) + except ValueError: + avatar.emotion = EmotionType.CALM + # 恢复死亡状态 avatar.is_dead = data.get("is_dead", False) avatar.death_info = data.get("death_info") diff --git a/src/sim/save/avatar_save_mixin.py b/src/sim/save/avatar_save_mixin.py index 2567b37..b233f79 100644 --- a/src/sim/save/avatar_save_mixin.py +++ b/src/sim/save/avatar_save_mixin.py @@ -91,6 +91,7 @@ class AvatarSaveMixin: "persona_ids": [p.id for p in self.personas] if self.personas else [], "appearance": self.appearance.level, "nickname": self.nickname.to_dict() if self.nickname else None, + "emotion": self.emotion.value, "is_dead": self.is_dead, "death_info": self.death_info, diff --git a/static/game_configs/persona.csv b/static/game_configs/persona.csv index 5441271..6db5dd9 100644 --- a/static/game_configs/persona.csv +++ b/static/game_configs/persona.csv @@ -61,3 +61,4 @@ id,name,exclusion_names,desc,rarity,condition,effects 59,扫把星,福缘深厚;气运之子,天生霉运缠身,喝凉水都塞牙。虽然活着不易,但也磨练了心性。,N,,"{extra_misfortune_probability: 0.005, extra_item_sell_price_multiplier: -0.1}" 60,大器晚成,,早年修行多舛,霉运连连;但若能坚持至金丹元婴,便可否极泰来,气运亨通。,SR,,"[{when: 'avatar.cultivation_progress.realm.value in [""练气"", ""筑基""]', extra_misfortune_probability: 0.005}, {when: 'avatar.cultivation_progress.realm.value in [""金丹"", ""元婴""]', extra_fortune_probability: 0.01}]" 61,炼器师,好斗,精通炼器之道,对材料敏锐,擅长铸造法宝。你认为法宝是修行的关键,战斗并非你的专长。,R,,{extra_cast_success_rate: 0.15} +62,情绪化,理性;淡漠,你的情绪波动很大,极易受外界事件影响而改变心情,做事也更随心所欲。,N,, diff --git a/static/templates/ai.txt b/static/templates/ai.txt index ab46f26..bd81141 100644 --- a/static/templates/ai.txt +++ b/static/templates/ai.txt @@ -12,6 +12,7 @@ {{ {avatar_name}: {{ "avatar_thinking": ... // 从角色角度,以第一人称视角,简单清晰的描述想法 + "current_emotion": ... // 从以下列表中选择一个最符合当前心情的词:平静、开心、愤怒、悲伤、恐惧、惊讶、期待、厌恶、疑惑、疲惫 "short_term_objective": ..., // 角色接下来一段时间的短期目标 "action_name_params_pairs": list[Tuple[action_name, action_params]] // 一次性决定未来的5~10个动作,按顺序执行 }} diff --git a/web/src/components/game/panels/info/AvatarDetail.vue b/web/src/components/game/panels/info/AvatarDetail.vue index 65f8d80..0d2b45d 100644 --- a/web/src/components/game/panels/info/AvatarDetail.vue +++ b/web/src/components/game/panels/info/AvatarDetail.vue @@ -109,6 +109,11 @@ async function handleClearObjective() { + diff --git a/web/src/types/core.ts b/web/src/types/core.ts index 995b085..c141922 100644 --- a/web/src/types/core.ts +++ b/web/src/types/core.ts @@ -64,6 +64,13 @@ export interface AvatarDetail extends EntityBase { magic_stone: number; base_battle_strength: number; + // 情绪 + emotion: { + name: string; + emoji: string; + desc: string; + }; + // 属性与资质 alignment: string; alignment_detail?: EffectEntity;