diff --git a/src/classes/ai.py b/src/classes/ai.py index a8b7180..cccd2f8 100644 --- a/src/classes/ai.py +++ b/src/classes/ai.py @@ -122,7 +122,11 @@ class LLMAI(AI): 异步决策逻辑:通过LLM决定执行什么动作和参数 """ global_info = world.get_info() - avatar_infos = {avatar.name: avatar.get_prompt_info() for avatar in avatars_to_decide} + # 在提示中包含与该角色处于同一区域的其他角色 + avatar_infos = {} + for avatar in avatars_to_decide: + co_region = world.get_avatars_in_same_region(avatar) + avatar_infos[avatar.name] = avatar.get_prompt_info(co_region) general_action_infos = ACTION_INFOS_STR info = { "avatar_infos": avatar_infos, diff --git a/src/classes/avatar.py b/src/classes/avatar.py index 0d95ff2..dfaef93 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -349,7 +349,7 @@ class Avatar: action_space = [action.name for action in doable_actions] return action_space - def get_prompt_info(self) -> str: + def get_prompt_info(self, co_region_avatars: Optional[List["Avatar"]] = None) -> str: """ 获取角色提示词信息 """ @@ -371,11 +371,18 @@ class Avatar: else: items_info = "物品持有情况:无" + # 同区域角色(可选) + co_region_info = "" + if co_region_avatars: + entries: list[str] = [] + for other in co_region_avatars[:8]: + entries.append(f"{other.name}(境界:{other.cultivation_progress.get_simple_info()})") + co_region_info = "\n同区域角色:" + (",".join(entries) if entries else "无") + # 关系摘要 relations_summary = self._get_relations_summary_str() - personas_count = len(self.personas) - return f"{info}\n{personas_info}\n{magic_stone_info}\n{items_info}\n关系:{relations_summary}\n决策时需参考这个角色的{personas_count}个个性特点。\n该角色的目前暂时的合法动作为:{action_space}" + return f"{info}\n{personas_info}\n{magic_stone_info}\n{items_info}\n关系:{relations_summary}\n{co_region_info}\n该角色的目前暂时的合法动作为:{action_space}" def set_relation(self, other: "Avatar", relation: Relation) -> None: """ @@ -424,7 +431,7 @@ class Avatar: """ relation = self.get_relation(other_avatar) relation_str = str(relation) - return f"{other_avatar.name},境界:{str(other_avatar.cultivation_progress)},关系:{relation_str}" + return f"{other_avatar.name},境界:{other_avatar.cultivation_progress.get_simple_info()},关系:{relation_str}" @property def move_step_length(self) -> int: diff --git a/src/classes/avatar_manager.py b/src/classes/avatar_manager.py new file mode 100644 index 0000000..50f3136 --- /dev/null +++ b/src/classes/avatar_manager.py @@ -0,0 +1,28 @@ +from __future__ import annotations +from dataclasses import dataclass, field +from typing import Dict, List, TYPE_CHECKING + +if TYPE_CHECKING: + from src.classes.avatar import Avatar + + +@dataclass +class AvatarManager: + avatars: Dict[str, "Avatar"] = field(default_factory=dict) + + def get_avatars_in_same_region(self, avatar: "Avatar") -> List["Avatar"]: + """ + 返回与给定 avatar 处于同一区域的其他角色列表(不含自己)。 + """ + if avatar is None or getattr(avatar, "tile", None) is None or avatar.tile.region is None: + return [] + region = avatar.tile.region + same_region: list["Avatar"] = [] + for other in self.avatars.values(): + if other is avatar or getattr(other, "tile", None) is None: + continue + if other.tile.region == region: + same_region.append(other) + return same_region + + diff --git a/src/classes/cultivation.py b/src/classes/cultivation.py index bf71f20..dbb845a 100644 --- a/src/classes/cultivation.py +++ b/src/classes/cultivation.py @@ -89,10 +89,16 @@ class CultivationProgress: return REALM_TO_MOVE_STEP[self.realm] def __str__(self) -> str: + return self.get_info() + + def get_info(self) -> str: can_break_through = self.can_break_through() can_break_through_str = "可以突破" if can_break_through else "不可以突破" return f"{self.realm.value}{self.stage.value}({self.level}级){can_break_through_str}" + def get_simple_info(self) -> str: + return f"{self.realm.value}{self.stage.value}" + def get_exp_required(self) -> int: """ 计算升级到指定等级需要的经验值 diff --git a/src/classes/world.py b/src/classes/world.py index 489e4aa..13c8cb4 100644 --- a/src/classes/world.py +++ b/src/classes/world.py @@ -1,15 +1,25 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from typing import TYPE_CHECKING from src.classes.map import Map from src.classes.calendar import Year, Month, MonthStamp +from src.classes.avatar_manager import AvatarManager + +if TYPE_CHECKING: + from src.classes.avatar import Avatar + @dataclass class World(): map: Map month_stamp: MonthStamp + avatar_manager: AvatarManager = field(default_factory=AvatarManager) def get_info(self) -> str: map_intro = "世界上的区域为:" map_info = self.map.get_info() info = f"{map_intro}\n{map_info}" - return info \ No newline at end of file + return info + + def get_avatars_in_same_region(self, avatar: "Avatar"): + return self.avatar_manager.get_avatars_in_same_region(avatar) \ No newline at end of file diff --git a/src/front/app.py b/src/front/app.py index 8f7f75b..8cd30ca 100644 --- a/src/front/app.py +++ b/src/front/app.py @@ -159,7 +159,7 @@ class Front: def _assign_avatar_images(self): import random - for avatar_id, avatar in self.simulator.avatars.items(): + for avatar_id, avatar in self.world.avatar_manager.avatars.items(): if avatar_id not in self.avatar_images: if avatar.gender == Gender.MALE and self.male_avatars: self.avatar_images[avatar_id] = random.choice(self.male_avatars) @@ -175,11 +175,11 @@ class Front: ts = self.tile_size m = self.margin # 清理已不存在的 avatar 状态 - to_del = [aid for aid in self._avatar_display_states.keys() if aid not in self.simulator.avatars] + to_del = [aid for aid in self._avatar_display_states.keys() if aid not in self.world.avatar_manager.avatars] for aid in to_del: self._avatar_display_states.pop(aid, None) # 初始化/补全 - for avatar_id, avatar in self.simulator.avatars.items(): + for avatar_id, avatar in self.world.avatar_manager.avatars.items(): if avatar_id not in self._avatar_display_states: cx = m + avatar.pos_x * ts + ts // 2 cy = m + avatar.pos_y * ts + ts // 2 @@ -197,7 +197,7 @@ class Front: ts = self.tile_size m = self.margin self._init_avatar_display_states() - for avatar_id, avatar in self.simulator.avatars.items(): + for avatar_id, avatar in self.world.avatar_manager.avatars.items(): state = self._avatar_display_states[avatar_id] # 当前目标像素 cur_target_x = m + avatar.pos_x * ts + ts // 2 diff --git a/src/front/rendering.py b/src/front/rendering.py index 197bb67..adb3172 100644 --- a/src/front/rendering.py +++ b/src/front/rendering.py @@ -87,7 +87,7 @@ def draw_avatars_and_pick_hover( mouse_x, mouse_y = pygame_mod.mouse.get_pos() hovered = None min_dist = float("inf") - for avatar_id, avatar in simulator.avatars.items(): + for avatar_id, avatar in simulator.world.avatar_manager.avatars.items(): if get_display_center is not None: cx_f, cy_f = get_display_center(avatar, tile_size, margin) cx, cy = int(cx_f), int(cy_f) diff --git a/src/run/run.py b/src/run/run.py index 0ffef18..819de21 100644 --- a/src/run/run.py +++ b/src/run/run.py @@ -117,7 +117,7 @@ async def main(): sim = Simulator(world) # 创建角色,传入当前年份确保年龄与生日匹配,使用配置文件中的NPC数量 - sim.avatars.update(make_avatars(world, count=CONFIG.game.init_npc_num, current_month_stamp=world.month_stamp)) + world.avatar_manager.avatars.update(make_avatars(world, count=CONFIG.game.init_npc_num, current_month_stamp=world.month_stamp)) front = Front( simulator=sim, diff --git a/src/sim/simulator.py b/src/sim/simulator.py index eb60581..41600ee 100644 --- a/src/sim/simulator.py +++ b/src/sim/simulator.py @@ -11,7 +11,6 @@ from src.utils.config import CONFIG class Simulator: def __init__(self, world: World): - self.avatars = {} # dict of str -> Avatar self.world = world self.birth_rate = CONFIG.game.npc_birth_rate_per_month # 从配置文件读取NPC每月出生率 @@ -28,7 +27,7 @@ class Simulator: # 决定动作行为 avatars_to_decide = [] - for avatar in list(self.avatars.values()): + for avatar in list(self.world.avatar_manager.avatars.values()): if avatar.cur_action_pair is None: # 若有排队动作但当前不可执行:丢弃之后的所有动作 if avatar.has_next_actions(): @@ -54,7 +53,7 @@ class Simulator: events.append(event) # 结算角色行为 - for avatar_id, avatar in self.avatars.items(): + for avatar_id, avatar in self.world.avatar_manager.avatars.items(): await avatar.act() if avatar.death_by_old_age(): death_avatar_ids.append(avatar_id) @@ -64,7 +63,7 @@ class Simulator: # 删除死亡的角色 for avatar_id in death_avatar_ids: - self.avatars.pop(avatar_id) + self.world.avatar_manager.avatars.pop(avatar_id) # 新角色 if random.random() < self.birth_rate: @@ -72,7 +71,7 @@ class Simulator: gender = random.choice(list(Gender)) name = get_random_name(gender) new_avatar = get_new_avatar_from_ordinary(self.world, self.world.month_stamp, name, Age(age)) - self.avatars[new_avatar.id] = new_avatar + self.world.avatar_manager.avatars[new_avatar.id] = new_avatar event = Event(self.world.month_stamp, f"{new_avatar.name}晋升为修士了。") events.append(event) diff --git a/src/utils/names.py b/src/utils/names.py index 7bfafea..d96f5a8 100644 --- a/src/utils/names.py +++ b/src/utils/names.py @@ -36,7 +36,7 @@ FEMALE_GIVEN_NAMES = [ "如雪", "若梦", "凝香", "紫霞", "月华", "清音", "蝶舞", "花颜", "雅韵", "诗涵", "静雯", "慕蓉", "婉儿", "柔情", "倾城", "红颜", "如烟", "妙音", "冰心", "玉颜", "如玉", "清影", "梦瑶", "紫嫣", "霜华", "若水", "青莲", "雪儿", "慧心", "素手", - "如意", "诗雨", "梦蝶", "紫萱", "冰蓮", "若兰", "清雅", "雪梅", "慕雪", "天音", + "如意", "诗雨", "梦蝶", "紫萱", "冰莲", "若兰", "清雅", "雪梅", "慕雪", "天音", "如风", "诗韵", "梦云", "紫烟", "冰雪", "若霜", "清秋", "雪莲", "慕凝", "天香", # 新增的50个平凡仙侠名字 "小雨", "春花", "夏草", "秋叶", "冬梅", "东篱", "西园", "南风", "北雁", "中庭",