From 9949265748867f992158ee2f861e3755f3d3deb5 Mon Sep 17 00:00:00 2001 From: bridge Date: Tue, 11 Nov 2025 23:07:36 +0800 Subject: [PATCH] merge persona and traits & add rarity --- src/classes/action/harvest.py | 6 +- src/classes/action/hunt.py | 6 +- src/classes/avatar.py | 26 ++++---- src/classes/persona.py | 27 ++++++-- src/classes/rarity.py | 96 +++++++++++++++++++++++++++ src/classes/trait.py | 106 ------------------------------ src/front/rendering.py | 76 ++++++++++++++++++++- src/sim/load/avatar_load_mixin.py | 6 -- src/sim/new_avatar.py | 23 ------- src/sim/save/avatar_save_mixin.py | 1 - static/game_configs/persona.csv | 65 +++++++++--------- static/game_configs/trait.csv | 5 -- static/templates/ai.txt | 2 +- static/templates/conversation.txt | 2 +- 14 files changed, 247 insertions(+), 200 deletions(-) create mode 100644 src/classes/rarity.py delete mode 100644 src/classes/trait.py delete mode 100644 static/game_configs/trait.csv diff --git a/src/classes/action/harvest.py b/src/classes/action/harvest.py index a8b7a0f..a062d61 100644 --- a/src/classes/action/harvest.py +++ b/src/classes/action/harvest.py @@ -38,7 +38,11 @@ class Harvest(TimedAction): target_plant = random.choice(available_plants) # 随机选择该植物的一种物品 item = random.choice(target_plant.items) - self.avatar.add_item(item, 1) + # 基础获得1个,额外物品来自effects + base_quantity = 1 + extra_items = int(self.avatar.effects.get("extra_harvest_items", 0) or 0) + total_quantity = base_quantity + extra_items + self.avatar.add_item(item, total_quantity) def can_start(self) -> tuple[bool, str]: region = self.avatar.tile.region diff --git a/src/classes/action/hunt.py b/src/classes/action/hunt.py index c17fb07..ae01780 100644 --- a/src/classes/action/hunt.py +++ b/src/classes/action/hunt.py @@ -38,7 +38,11 @@ class Hunt(TimedAction): target_animal = random.choice(available_animals) # 随机选择该动物的一种物品 item = random.choice(target_animal.items) - self.avatar.add_item(item, 1) + # 基础获得1个,额外物品来自effects + base_quantity = 1 + extra_items = int(self.avatar.effects.get("extra_hunt_items", 0) or 0) + total_quantity = base_quantity + extra_items + self.avatar.add_item(item, total_quantity) def can_start(self) -> tuple[bool, str]: region = self.avatar.tile.region diff --git a/src/classes/avatar.py b/src/classes/avatar.py index 2412da2..00ae710 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -29,7 +29,6 @@ from src.classes.alignment import Alignment from src.classes.persona import Persona, personas_by_id, get_random_compatible_personas from src.classes.item import Item from src.classes.treasure import Treasure -from src.classes.trait import Trait, get_random_trait from src.classes.magic_stone import MagicStone from src.classes.hp_and_mp import HP, MP, HP_MAX_BY_REALM, MP_MAX_BY_REALM from src.utils.id_generator import get_avatar_id @@ -102,8 +101,6 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): treasure: Optional[Treasure] = None # 灵兽:最多一个;若再次捕捉则覆盖 spirit_animal: Optional[SpiritAnimal] = None - # 特质:每个角色有且仅有一个 - trait: Trait = None # 将在__post_init__中初始化 # 当月/当步新设动作标记:在 commit_next_plan 设为 True,首次 tick_action 后清为 False _new_action_set_this_step: bool = False # 动作冷却:记录动作类名 -> 上次完成月戳 @@ -124,14 +121,10 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): # 最大寿元已在 Age 构造时基于境界初始化 - # 如果personas列表为空,则随机分配两个符合条件且不互斥的persona + # 如果personas列表为空,则随机分配符合条件且不互斥的persona if not self.personas: self.personas = get_random_compatible_personas(persona_num, avatar=self) - # 如果trait为空,则随机分配一个 - if self.trait is None: - self.trait = get_random_trait(self) - # 出生即按宗门分配功法: # - 散修:仅从无宗门功法抽样 # - 有宗门:从“无宗门 + 本宗门”集合抽样 @@ -158,8 +151,9 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): merged = _merge_effects(merged, self.technique.effects) # 来自灵根 merged = _merge_effects(merged, self.root.effects) - # 来自特质 - merged = _merge_effects(merged, self.trait.effects) + # 来自特质(persona) + for persona in self.personas: + merged = _merge_effects(merged, persona.effects) # 来自法宝 if self.treasure is not None: merged = _merge_effects(merged, self.treasure.effects) @@ -235,8 +229,7 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): "灵根": root_info, "功法": technique_info, "境界": cultivation_info, - "个性": personas_info, - "特质": self.trait.get_detailed_info() if detailed else self.trait.get_info(), + "特质": personas_info, "物品": items_info, "外貌": appearance_info, "法宝": treasure_info, @@ -570,9 +563,12 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin): add_kv(lines, "功法", tech_str) if self.personas: - add_kv(lines, "个性", ", ".join([p.name for p in self.personas])) - - add_kv(lines, "特质", self.trait.get_info()) + # 使用颜色标记格式:text + persona_parts = [] + for p in self.personas: + r, g, b = p.rarity.color_rgb + persona_parts.append(f"{p.name}") + add_kv(lines, "特质", ", ".join(persona_parts)) add_kv(lines, "位置", f"({self.pos_x}, {self.pos_y})") add_kv(lines, "灵石", str(self.magic_stone)) diff --git a/src/classes/persona.py b/src/classes/persona.py index 45dd269..d0ce689 100644 --- a/src/classes/persona.py +++ b/src/classes/persona.py @@ -4,6 +4,8 @@ from typing import List, Optional, TYPE_CHECKING from src.utils.df import game_configs from src.utils.config import CONFIG +from src.classes.effect import load_effect_from_str +from src.classes.rarity import Rarity, get_rarity_from_str ids_separator = CONFIG.df.ids_separator @@ -14,14 +16,21 @@ if TYPE_CHECKING: @dataclass class Persona: """ - 角色个性 + 角色特质 + 包含个性、天赋等角色特征 """ id: int name: str desc: str exclusion_ids: List[int] - weight: float + rarity: Rarity condition: str + effects: dict[str, object] + + @property + def weight(self) -> float: + """根据稀有度获取采样权重""" + return self.rarity.weight def get_info(self) -> str: return self.name @@ -41,21 +50,25 @@ def _load_personas() -> tuple[dict[int, Persona], dict[str, Persona]]: exclusion_ids = [] if exclusion_ids_str: exclusion_ids = [int(x.strip()) for x in exclusion_ids_str.split(ids_separator) if x.strip()] - # 解析权重(缺失或为 NaN 时默认为 1.0),避免不必要的异常 - weight_val = row.get("weight", 1) - weight_str = str(weight_val).strip() - weight = float(weight_str) if weight_str and weight_str.lower() != "nan" else 1.0 + # 解析稀有度(缺失或为 NaN 时默认为 N) + rarity_val = row.get("rarity", "N") + rarity_str = str(rarity_val).strip().upper() + rarity = get_rarity_from_str(rarity_str) if rarity_str and rarity_str != "NAN" else get_rarity_from_str("N") # 条件:可为空 condition_val = row.get("condition", "") condition = "" if str(condition_val) == "nan" else str(condition_val).strip() + # 解析effects + raw_effects_val = row.get("effects", "") + effects = load_effect_from_str(raw_effects_val) persona = Persona( id=int(row["id"]), name=str(row["name"]), desc=str(row["desc"]), exclusion_ids=exclusion_ids, - weight=weight, + rarity=rarity, condition=condition, + effects=effects, ) personas_by_id[persona.id] = persona personas_by_name[persona.name] = persona diff --git a/src/classes/rarity.py b/src/classes/rarity.py new file mode 100644 index 0000000..504b707 --- /dev/null +++ b/src/classes/rarity.py @@ -0,0 +1,96 @@ +""" +稀有度系统 +定义角色特质、物品等的稀有度等级 +""" +from enum import Enum +from dataclasses import dataclass + + +class RarityLevel(Enum): + """稀有度等级""" + N = "N" # Normal - 普通 + R = "R" # Rare - 稀有 + SR = "SR" # Super Rare - 超稀有 + SSR = "SSR" # Super Super Rare - 传说 + + +@dataclass +class Rarity: + """ + 稀有度配置 + 包含等级、权重、颜色等信息 + """ + level: RarityLevel + weight: float + color_rgb: tuple[int, int, int] # RGB颜色值 + color_hex: str # 十六进制颜色值 + chinese_name: str + + def __str__(self) -> str: + return self.chinese_name + + +# 稀有度配置表 +RARITY_CONFIGS = { + RarityLevel.N: Rarity( + level=RarityLevel.N, + weight=10.0, + color_rgb=(255, 255, 255), # 白色 + color_hex="#FFFFFF", + chinese_name="普通" + ), + RarityLevel.R: Rarity( + level=RarityLevel.R, + weight=5.0, + color_rgb=(74, 144, 226), # 蓝色 + color_hex="#4A90E2", + chinese_name="稀有" + ), + RarityLevel.SR: Rarity( + level=RarityLevel.SR, + weight=3.0, + color_rgb=(147, 112, 219), # 紫色 + color_hex="#9370DB", + chinese_name="超稀有" + ), + RarityLevel.SSR: Rarity( + level=RarityLevel.SSR, + weight=1.0, + color_rgb=(255, 215, 0), # 金色 + color_hex="#FFD700", + chinese_name="传说" + ), +} + + +def get_rarity_from_str(rarity_str: str) -> Rarity: + """ + 从字符串获取稀有度配置 + + Args: + rarity_str: 稀有度字符串,如 "N", "R", "SR", "SSR" + + Returns: + Rarity: 稀有度配置对象,若无法识别则返回N + """ + rarity_str = str(rarity_str).strip().upper() + try: + level = RarityLevel(rarity_str) + return RARITY_CONFIGS[level] + except (ValueError, KeyError): + # 默认返回普通稀有度 + return RARITY_CONFIGS[RarityLevel.N] + + +def get_weight_from_rarity(rarity_str: str) -> float: + """ + 根据稀有度字符串获取权重 + + Args: + rarity_str: 稀有度字符串 + + Returns: + float: 对应的权重值 + """ + rarity = get_rarity_from_str(rarity_str) + return rarity.weight diff --git a/src/classes/trait.py b/src/classes/trait.py deleted file mode 100644 index 9aefd87..0000000 --- a/src/classes/trait.py +++ /dev/null @@ -1,106 +0,0 @@ -import random -from dataclasses import dataclass -from typing import Optional, TYPE_CHECKING - -from src.utils.df import game_configs -from src.classes.effect import load_effect_from_str - -if TYPE_CHECKING: - from src.classes.avatar import Avatar - - -@dataclass -class Trait: - """ - 角色特质 - 每个角色有且仅有一个特质(可能是占位特质"无") - """ - id: int - name: str - desc: str - weight: float - condition: str - effects: dict[str, object] - - def get_info(self) -> str: - return self.name - - def get_detailed_info(self) -> str: - return f"{self.name}({self.desc})" - - -def _load_traits() -> tuple[dict[int, Trait], dict[str, Trait]]: - """从配表加载trait数据""" - traits_by_id: dict[int, Trait] = {} - traits_by_name: dict[str, Trait] = {} - - trait_df = game_configs["trait"] - for _, row in trait_df.iterrows(): - # 解析权重(缺失或为 NaN 时默认为 1.0) - weight_val = row.get("weight", 1) - weight_str = str(weight_val).strip() - weight = float(weight_str) if weight_str and weight_str.lower() != "nan" else 1.0 - - # 条件:可为空 - condition_val = row.get("condition", "") - condition = "" if str(condition_val) == "nan" else str(condition_val).strip() - - # 解析effects - raw_effects_val = row.get("effects", "") - effects = load_effect_from_str(raw_effects_val) - - trait = Trait( - id=int(row["id"]), - name=str(row["name"]), - desc=str(row["desc"]), - weight=weight, - condition=condition, - effects=effects, - ) - traits_by_id[trait.id] = trait - traits_by_name[trait.name] = trait - - return traits_by_id, traits_by_name - - -# 从配表加载trait数据 -traits_by_id, traits_by_name = _load_traits() - - -def _is_trait_allowed(trait_id: int, avatar: Optional["Avatar"]) -> bool: - """ - 判断特质是否允许被选择(条件判定) - """ - trait = traits_by_id[trait_id] - # 条件判定 - if avatar is not None and trait.condition: - allowed = bool(eval(trait.condition, {"__builtins__": {}}, {"avatar": avatar})) - if not allowed: - return False - return True - - -def get_random_trait(avatar: Optional["Avatar"] = None) -> Trait: - """ - 根据权重随机选择一个特质 - - Args: - avatar: 可选,若提供则按 trait.condition 过滤 - - Returns: - Trait: 选中的特质 - """ - # 初始候选:若提供 avatar,则先按条件过滤;否则全量 - available_ids = list(traits_by_id.keys()) - if avatar is not None: - available_ids = [tid for tid in available_ids if _is_trait_allowed(tid, avatar)] - - if not available_ids: - # 如果没有可用的特质,返回第一个(通常是"无") - return list(traits_by_id.values())[0] - - candidates = [traits_by_id[tid] for tid in available_ids] - weights = [max(0.0, c.weight) for c in candidates] - - return random.choices(candidates, weights=weights, k=1)[0] - diff --git a/src/front/rendering.py b/src/front/rendering.py index 9aeb38c..d26fc1e 100644 --- a/src/front/rendering.py +++ b/src/front/rendering.py @@ -334,9 +334,23 @@ def draw_avatars_and_pick_hover( def draw_tooltip(pygame_mod, screen, colors, lines: List[str], mouse_x: int, mouse_y: int, font, min_width: int = 260, top_limit: int = 0): + """ + 绘制tooltip,支持颜色标记格式:text + """ + import re padding = 6 spacing = 2 - surf_lines = [font.render(t, True, colors["text"]) for t in lines] + + # 解析每行文本,生成渲染表面 + surf_lines = [] + for line in lines: + # 检查是否包含颜色标记 + if " object: + """ + 渲染带颜色标记的文本,格式:text + 返回一个合成的Surface + """ + import re + + # 解析颜色标记 + pattern = r'(.*?)' + parts = [] + last_end = 0 + + for match in re.finditer(pattern, text): + # 添加标记前的普通文本 + if match.start() > last_end: + plain_text = text[last_end:match.start()] + parts.append((plain_text, default_color)) + + # 添加带颜色的文本 + r, g, b = int(match.group(1)), int(match.group(2)), int(match.group(3)) + colored_text = match.group(4) + parts.append((colored_text, (r, g, b))) + + last_end = match.end() + + # 添加剩余的普通文本 + if last_end < len(text): + parts.append((text[last_end:], default_color)) + + # 如果没有颜色标记,直接返回普通渲染 + if len(parts) == 1 and parts[0][1] == default_color: + return font.render(text, True, default_color) + + # 渲染每个部分并合成 + rendered_parts = [] + total_width = 0 + max_height = 0 + + for txt, color in parts: + if txt: + surf = font.render(txt, True, color) + rendered_parts.append(surf) + total_width += surf.get_width() + max_height = max(max_height, surf.get_height()) + + # 创建合成Surface + if not rendered_parts: + return font.render("", True, default_color) + + combined = pygame_mod.Surface((total_width, max_height), pygame_mod.SRCALPHA) + combined.fill((0, 0, 0, 0)) # 透明背景 + + x_offset = 0 + for surf in rendered_parts: + combined.blit(surf, (x_offset, 0)) + x_offset += surf.get_width() + + return combined + + def draw_tooltip_for_avatar(pygame_mod, screen, colors, font, avatar: Avatar, tooltip_min_width: int = 260, status_bar_height: int = 32): # 改为从 Avatar.get_hover_info 获取信息行,避免前端重复拼接 lines = avatar.get_hover_info() diff --git a/src/sim/load/avatar_load_mixin.py b/src/sim/load/avatar_load_mixin.py index 6ec0eb6..9b25c7d 100644 --- a/src/sim/load/avatar_load_mixin.py +++ b/src/sim/load/avatar_load_mixin.py @@ -47,7 +47,6 @@ class AvatarLoadMixin: from src.classes.root import Root from src.classes.alignment import Alignment from src.classes.persona import personas_by_id - from src.classes.trait import traits_by_id from src.classes.appearance import get_appearance_by_level from src.classes.magic_stone import MagicStone from src.classes.action_runtime import ActionPlan @@ -133,11 +132,6 @@ class AvatarLoadMixin: persona_ids = data.get("persona_ids", []) avatar.personas = [personas_by_id[pid] for pid in persona_ids if pid in personas_by_id] - # 重建trait - trait_id = data.get("trait_id") - if trait_id is not None and trait_id in traits_by_id: - avatar.trait = traits_by_id[trait_id] - # 设置外貌(通过level获取完整的Appearance对象) avatar.appearance = get_appearance_by_level(data.get("appearance", 5)) diff --git a/src/sim/new_avatar.py b/src/sim/new_avatar.py index 27dc26c..6be6088 100644 --- a/src/sim/new_avatar.py +++ b/src/sim/new_avatar.py @@ -18,7 +18,6 @@ from src.classes.relation import Relation from src.classes.technique import get_technique_by_sect, attribute_to_root, Technique, techniques_by_id, techniques_by_name from src.classes.treasure import treasures_by_sect_id, Treasure, treasures_by_id, treasures_by_name from src.classes.persona import Persona, personas_by_id, personas_by_name -from src.classes.trait import Trait, traits_by_id, traits_by_name # —— 参数常量(便于调参)—— @@ -572,22 +571,6 @@ def _parse_treasure(value: Union[str, int, Treasure, None]) -> Optional[Treasure return treasures_by_name.get(s) -def _parse_trait(value: Union[str, int, Trait, None]) -> Optional[Trait]: - """解析trait参数""" - if value is None: - return None - if isinstance(value, Trait): - return value - if isinstance(value, int): - return traits_by_id.get(value) - s = str(value).strip() - if not s: - return None - if s.isdigit(): - return traits_by_id.get(int(s)) - return traits_by_name.get(s) - - def _parse_personas(value: Union[str, int, Persona, List[Union[str, int, Persona]], None]) -> Optional[List[Persona]]: if value is None: return None @@ -662,7 +645,6 @@ def get_new_avatar_with_config( technique: Union[str, int, Technique, None] = None, treasure: Union[str, int, Treasure, None] = None, personas: Union[str, int, Persona, List[Union[str, int, Persona]], None] = None, - trait: Union[str, int, Trait, None] = None, appearance: Optional[int] = None, ) -> Avatar: """ @@ -738,11 +720,6 @@ def get_new_avatar_with_config( if pers_list is not None and len(pers_list) > 0: avatar.personas = pers_list - # 覆盖:特质 - trait_obj = _parse_trait(trait) - if trait_obj is not None: - avatar.trait = trait_obj - # 覆盖:外貌/颜值 if isinstance(appearance, int): avatar.appearance = get_appearance_by_level(appearance) diff --git a/src/sim/save/avatar_save_mixin.py b/src/sim/save/avatar_save_mixin.py index d3beed9..4db929e 100644 --- a/src/sim/save/avatar_save_mixin.py +++ b/src/sim/save/avatar_save_mixin.py @@ -86,7 +86,6 @@ class AvatarSaveMixin: "sect_rank": self.sect_rank.value if self.sect_rank else None, "alignment": self.alignment.name if self.alignment else None, "persona_ids": [p.id for p in self.personas] if self.personas else [], - "trait_id": self.trait.id if self.trait else None, "appearance": self.appearance.level, # 行动与AI diff --git a/static/game_configs/persona.csv b/static/game_configs/persona.csv index ad1103e..11f997f 100644 --- a/static/game_configs/persona.csv +++ b/static/game_configs/persona.csv @@ -1,32 +1,33 @@ -id,name,exclusion_ids,desc,weight,condition -,,和本persona互斥的persona的id,描述/用于LLM输入的文本,采样时不同权重被采样到的概率,选取条件(可用avatar字段/Alignment中文或英文等) -1,理性,2;5,你总是会用逻辑来思考问题,做事会谋定而后动。,1, -2,无常,1;24;25;9,目标飘忽不定,不会长期坚持一个目标。,1, -3,怠惰,4;24;25,你总是会拖延,不想努力,更热衷于享受人生。,1, -4,冒险,3;10,你总是会冒险,喜欢刺激,总想放手一搏。,1, -5,随性,1;24;25,你总是会随机应变,性子到哪里了就是哪里,没有一定之规。,1, -6,贪财,,你对灵石和财富有着强烈的渴望。,1, -7,采药,,喜欢在山林中寻找各种奇花异草和灵药,对植物有着敏锐的直觉和深厚的兴趣。你认为大自然的恩赐需要用心去发现和珍惜。,1, -8,猎者,,享受在野外追踪猎物的刺激感,对各种动物的习性了如指掌,喜欢捕猎野兽。情况允许也会御兽。,1, -9,沉思,2,你总是会深思熟虑,思考问题比较有哲理。,1, -10,惜命,4;24;25,你总是会珍惜自己的生命,不会轻易冒险。,1, -11,友爱,13;14;15;12;24;25,你重视同伴与和谐,乐于助人,倾向通过协作与沟通化解矛盾。,1, -12,复仇,11;14,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。你必须立刻进行复仇行动。,1, -13,孤僻,11,你喜欢独处,避免与人深交,更信赖自己的判断与行动。,1, -14,淡漠,11;12;15;24;25,你情感克制,对外界冷静疏离,不轻易被他人或事件影响。,1, -15,好斗,11;14;10;17,你直面冲突,偏好以力量与对抗解决问题,并格外热衷增加己身战力。,1, -16,鲁莽,1;9;10,你行事冲动、少考虑后果,常凭直觉立刻行动。,1, -17,胆小,4;15;12;24;25,你谨小慎微,容易畏惧风险,倾向回避正面冲突。,1, -18,霸道,11;17,你行事强势,不讲道理,习惯以自己的利益为先,倾向多吃多占、压人一步,对他人的反对不以为意。,1, -19,修行痴迷,2;3;5,你将绝大多数时间用于修炼,厌恶与修行无关的社交与享乐。,1, -20,内向,21,你更享受独处与自我思考,倾向回避不必要的社交与长谈。,1, -21,外向,13;14;20,你乐于与人交流,主动结识伙伴,倾向接受对话和合作。,1, -22,刻薄,11;23,你在对话中倾向以讽刺、嘲弄、挖苦的方式表达自己,更容易引发冲突与对立;你通常不愿迅速进入友好关系,更可能在不顺利的交流后滋生敌意或结仇。,1, -23,热情,13;14;22,你待人友好、积极回应,倾向用鼓励与赞美拉近距离;你更愿意主动展开善意的交流,乐于合作与分享,1, -24,极端正义,20;25,你对邪恶深恶痛绝,对正义的理想抱有近乎偏执的追求。,1,avatar.alignment == "正" -25,极端邪恶,20;24,你推崇权力与恐惧,为达目的不择手段,对善良嗤之以鼻。,1,avatar.alignment == "邪" -26,开放,27,你对待和他人结为道侣或者双修比较随意,1, -27,腼腆,26,你对待和他人结为道侣或者双修比较谨慎,1, -28,舔狗,13;14;22;27,你对异性中外貌出众者格外友善,倾向主动接近、帮助与合作。,1, -29,嫉妒,11;23,你对在修为、外貌或财富等方面远超于你的人容易产生敌意,更倾向对其冷淡、挑衅或打压。,1, -30,穿越者,,你来自现代社会,怀念现代社会的一切,你的思考(thinking)必须是现代化的思考,1, \ No newline at end of file +id,name,exclusion_ids,desc,rarity,condition,effects +,,和本persona互斥的persona的id,描述/用于LLM输入的文本,稀有度(N/R/SR/SSR),选取条件(可用avatar字段/Alignment中文或英文等),"JSON形式" +1,理性,2;5,你总是会用逻辑来思考问题,做事会谋定而后动。,N, +2,无常,1;24;25;9,目标飘忽不定,不会长期坚持一个目标。,N, +3,怠惰,4;24;25,你总是会拖延,不想努力,更热衷于享受人生。,N, +4,冒险,3;10,你总是会冒险,喜欢刺激,总想放手一搏。,N, +5,随性,1;24;25,你总是会随机应变,性子到哪里了就是哪里,没有一定之规。,N, +6,贪财,,你对灵石和财富有着强烈的渴望。,N, +7,药师,,喜欢在山林中寻找各种奇花异草和灵药,对植物有着敏锐的直觉和深厚的兴趣。你认为大自然的恩赐需要用心去发现和珍惜。,R,,"{""extra_harvest_items"": 1}" +8,猎者,,享受在野外追踪猎物的刺激感,对各种动物的习性了如指掌,喜欢捕猎野兽。情况允许也会御兽。,R,,"{""extra_hunt_items"": 1}" +9,沉思,2,你总是会深思熟虑,思考问题比较有哲理。,N, +10,惜命,4;24;25,你总是会珍惜自己的生命,不会轻易冒险。,N, +11,友爱,13;14;15;12;24;25,你重视同伴与和谐,乐于助人,倾向通过协作与沟通化解矛盾。,N, +12,复仇,11;14,"你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。你必须立刻进行复仇行动。",N,, +13,孤僻,11,你喜欢独处,避免与人深交,更信赖自己的判断与行动。,N, +14,淡漠,11;12;15;24;25,你情感克制,对外界冷静疏离,不轻易被他人或事件影响。,N, +15,好斗,11;14;10;17,你直面冲突,偏好以力量与对抗解决问题,并格外热衷增加己身战力。,R,,"{""extra_battle_strength_points"": 1}" +16,鲁莽,1;9;10,你行事冲动、少考虑后果,常凭直觉立刻行动。,N, +17,胆小,4;15;12;24;25,你谨小慎微,容易畏惧风险,倾向回避正面冲突。,N, +18,霸道,11;17,你行事强势,不讲道理,习惯以自己的利益为先,倾向多吃多占、压人一步,对他人的反对不以为意。,N, +19,修行痴迷,2;3;5,你将绝大多数时间用于修炼,厌恶与修行无关的社交与享乐。,R,,"{""extra_cultivate_exp"": 30}" +20,内向,21,你更享受独处与自我思考,倾向回避不必要的社交与长谈。,N, +21,外向,13;14;20,你乐于与人交流,主动结识伙伴,倾向接受对话和合作。,N, +22,刻薄,11;23,你在对话中倾向以讽刺、嘲弄、挖苦的方式表达自己,更容易引发冲突与对立;你通常不愿迅速进入友好关系,更可能在不顺利的交流后滋生敌意或结仇。,N, +23,热情,13;14;22,你待人友好、积极回应,倾向用鼓励与赞美拉近距离;你更愿意主动展开善意的交流,乐于合作与分享,N, +24,极端正义,20;25,你对邪恶深恶痛绝,对正义的理想抱有近乎偏执的追求。,SR,avatar.alignment == "正" +25,极端邪恶,20;24,你推崇权力与恐惧,为达目的不择手段,对善良嗤之以鼻。,SR,avatar.alignment == "邪" +26,开放,27,你对待和他人结为道侣或者双修比较随意,R,,"{""extra_dual_cultivation_exp"": 30}" +27,腼腆,26,你对待和他人结为道侣或者双修比较谨慎,N, +28,舔狗,13;14;22;27,你对异性中外貌出众者格外友善,倾向主动接近、帮助与合作。,N, +29,嫉妒,11;23,你对在修为、外貌或财富等方面远超于你的人容易产生敌意,更倾向对其冷淡、挑衅或打压。,N, +30,穿越者,,你来自现代社会,怀念现代社会的一切,你的思考(thinking)必须是现代化的思考,SR, +31,气运之子,,天生气运加身,更易遇到奇遇,战斗力也略有提升,SSR,,"{""extra_fortune_probability"": 0.05, ""extra_battle_strength_points"": 2}" \ No newline at end of file diff --git a/static/game_configs/trait.csv b/static/game_configs/trait.csv deleted file mode 100644 index df9973d..0000000 --- a/static/game_configs/trait.csv +++ /dev/null @@ -1,5 +0,0 @@ -id,name,desc,weight,condition,effects -,名称,描述/用于LLM输入的文本,采样时不同权重被采样到的概率,选取条件(可用avatar字段/Alignment中文或英文等),"JSON形式" -1,无,无特殊天赋,19,, -2,气运之子,天生气运加身,更易遇到奇遇,战斗力也略有提升,1,,"{""extra_fortune_probability"": 0.05, ""extra_battle_strength_points"": 2}" - diff --git a/static/templates/ai.txt b/static/templates/ai.txt index c1882fe..4fb7653 100644 --- a/static/templates/ai.txt +++ b/static/templates/ai.txt @@ -19,7 +19,7 @@ }} 要求与约束: -- thought从侧面体现出角色个性、宗门信息等角色特质 +- thought从侧面体现出角色特质、宗门信息等 - 执行动作只能从给定的全部动作中选,且需满足对应条件,见动作的requirements文本 - 一些动作需要先移动满足某些条件才可执行,可以适当规划。 - 和另一个角色交互的动作,必须在对应角色附近。执行前可以先MoveToAvatar \ No newline at end of file diff --git a/static/templates/conversation.txt b/static/templates/conversation.txt index f632982..08759e3 100644 --- a/static/templates/conversation.txt +++ b/static/templates/conversation.txt @@ -2,7 +2,7 @@ 你需要进行决策的NPC的dict[AvatarName, info]为 {avatar_infos} -正在进行的动作为:{avatar_name_1}和{avatar_name_2}正在对话。这个对话可能是善意的,也可能是恶意的,也可能是闲聊。内容和性质取决于NPC性格、正邪、关系等因素。 +正在进行的动作为:{avatar_name_1}和{avatar_name_2}正在对话。这个对话可能是善意的,也可能是恶意的,也可能是闲聊。内容和性质取决于NPC特质(性格、天赋等)、正邪、关系等因素。 两者可能进入的关系:{possible_new_relations} 两者可能取消的关系:{possible_cancal_relations}