diff --git a/.gitignore b/.gitignore index 350aa8d..ed20e18 100644 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,7 @@ Thumbs.db # Logs and caches *.log *.cache +logs/ # Temporary files ~$* diff --git a/README.md b/README.md index d734e45..6bf0de7 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ ### 🗺️ 地块系统 - ✅ 基础tile地块系统 +- ✅ 基础区域、修行区域、城市区域 - [ ] 同tile内NPC相互交互 - [ ] 城市/宗派据点的机制 - [ ] 灵气分布与产出设计 diff --git a/src/classes/action.py b/src/classes/action.py index 6fc8f7d..8502f62 100644 --- a/src/classes/action.py +++ b/src/classes/action.py @@ -7,7 +7,7 @@ import inspect from src.classes.essence import Essence, EssenceType from src.classes.root import Root, corres_essence_type -from src.classes.tile import Region +from src.classes.region import Region from src.classes.event import Event, NULL_EVENT if TYPE_CHECKING: @@ -172,7 +172,8 @@ class MoveToRegion(DefineAction, ActualActionMixin): 移动到某个region """ if isinstance(region, str): - region = self.world.map.region_names[region] + from src.classes.region import regions_by_name + region = regions_by_name[region] cur_loc = (self.avatar.pos_x, self.avatar.pos_y) region_center_loc = region.center_loc delta_x = region_center_loc[0] - cur_loc[0] @@ -187,7 +188,8 @@ class MoveToRegion(DefineAction, ActualActionMixin): 判断动作是否完成 """ if isinstance(region, str): - region = self.world.map.region_names[region] + from src.classes.region import regions_by_name + region = regions_by_name[region] return self.avatar.is_in_region(region) def get_event(self, region: Region|str) -> Event: @@ -196,8 +198,9 @@ class MoveToRegion(DefineAction, ActualActionMixin): """ if isinstance(region, str): region_name = region - if region in self.world.map.region_names: - region_name = self.world.map.region_names[region].name + from src.classes.region import regions_by_name + if region in regions_by_name: + region_name = regions_by_name[region].name elif hasattr(region, 'name'): region_name = region.name else: @@ -249,8 +252,9 @@ class Cultivate(DefineAction, ActualActionMixin): """ 判断修炼动作是否可以执行 """ - return self.avatar.cultivation_progress.can_cultivate() - + root = self.avatar.root + _corres_essence_type = corres_essence_type[root] + return self.avatar.cultivation_progress.can_cultivate() and self.avatar.tile.region.essence.get_density(_corres_essence_type) > 0 # 突破境界class @long_action(step_month=1) diff --git a/src/classes/ai.py b/src/classes/ai.py index e75516b..5ff20b6 100644 --- a/src/classes/ai.py +++ b/src/classes/ai.py @@ -9,7 +9,7 @@ from typing import TYPE_CHECKING import random from src.classes.world import World -from src.classes.tile import Region +from src.classes.region import Region from src.classes.root import corres_essence_type from src.classes.event import Event, NULL_EVENT from src.utils.llm import get_ai_prompt_and_call_llm_async diff --git a/src/classes/avatar.py b/src/classes/avatar.py index 964ced0..e0ea28c 100644 --- a/src/classes/avatar.py +++ b/src/classes/avatar.py @@ -7,7 +7,8 @@ import json from src.classes.calendar import MonthStamp from src.classes.action import Action, ALL_ACTUAL_ACTION_CLASSES, ALL_ACTION_CLASSES, ALL_ACTUAL_ACTION_NAMES from src.classes.world import World -from src.classes.tile import Tile, Region +from src.classes.tile import Tile +from src.classes.region import Region from src.classes.cultivation import CultivationProgress from src.classes.root import Root from src.classes.age import Age diff --git a/src/classes/essence.py b/src/classes/essence.py index aad262c..55906ae 100644 --- a/src/classes/essence.py +++ b/src/classes/essence.py @@ -1,4 +1,5 @@ from enum import Enum +from collections import defaultdict class EssenceType(Enum): @@ -14,6 +15,32 @@ class EssenceType(Enum): def __str__(self) -> str: """返回灵气类型的中文名称""" return essence_names.get(self, self.value) + + @classmethod + def from_str(cls, essence_str: str) -> 'EssenceType': + """ + 从字符串创建EssenceType实例 + + Args: + essence_str: 灵气的字符串表示,如 "金", "木", "水", "火", "土" + + Returns: + 对应的EssenceType枚举值 + + Raises: + ValueError: 如果字符串不匹配任何已知的灵气类型 + """ + # 首先尝试匹配中文名称 + for essence_type, chinese_name in essence_names.items(): + if chinese_name == essence_str: + return essence_type + + # 然后尝试匹配英文值 + for essence_type in cls: + if essence_type.value == essence_str: + return essence_type + + raise ValueError(f"Unknown essence type: {essence_str}") essence_names = { EssenceType.GOLD: "金", @@ -31,7 +58,9 @@ class Essence(): 浓度从0~10。 """ def __init__(self, density: dict[EssenceType, int]): - self.density = density + self.density = defaultdict(int) + for essence_type, density in density.items(): + self.density[essence_type] = density def get_density(self, essence_type: EssenceType) -> int: return self.density[essence_type] diff --git a/src/classes/item.py b/src/classes/item.py index 755dbbb..2871014 100644 --- a/src/classes/item.py +++ b/src/classes/item.py @@ -12,6 +12,9 @@ class Item: desc: str grade: int + def __hash__(self) -> int: + return hash(self.id) + def __str__(self) -> str: return self.name diff --git a/src/classes/region.py b/src/classes/region.py new file mode 100644 index 0000000..535fc3b --- /dev/null +++ b/src/classes/region.py @@ -0,0 +1,307 @@ +from dataclasses import dataclass, field +from typing import Union, TypeVar, Type +from enum import Enum +from abc import ABC, abstractmethod + +from src.utils.df import game_configs +from src.classes.essence import EssenceType, Essence + + +def get_tiles_from_shape(shape: 'Shape', north_west_cor: str, south_east_cor: str) -> list[tuple[int, int]]: + """ + 根据形状和两个角点坐标,计算出对应的所有坐标点 + + Args: + shape: 区域形状 + north_west_cor: 西北角坐标,格式: "x,y" + south_east_cor: 东南角坐标,格式: "x,y" + + Returns: + 所有坐标点的列表 + """ + nw_coords = tuple(map(int, north_west_cor.split(','))) + se_coords = tuple(map(int, south_east_cor.split(','))) + + min_x, min_y = nw_coords + max_x, max_y = se_coords + + coordinates = [] + + if shape == Shape.SQUARE or shape == Shape.RECTANGLE: + # 正方形和长方形:填充整个矩形区域 + for x in range(min_x, max_x + 1): + for y in range(min_y, max_y + 1): + coordinates.append((x, y)) + + elif shape == Shape.MEANDERING: + # 蜿蜒形状(如河流):创建一条从西北到东南的蜿蜒路径 + # 计算河流的宽度(根据距离动态调整) + distance_x = max_x - min_x + distance_y = max_y - min_y + total_distance = max(distance_x, distance_y) + + # 河流宽度:距离越长,河流越宽 + if total_distance < 10: + width = 1 + elif total_distance < 30: + width = 2 + else: + width = 3 + + # 生成中心路径点 + path_points = [] + if distance_x >= distance_y: + # 主要沿X轴方向流动 + for x in range(min_x, max_x + 1): + # 计算对应的y坐标,添加一些蜿蜒效果 + progress = (x - min_x) / max(distance_x, 1) + base_y = min_y + int(progress * distance_y) + + # 添加蜿蜒效果:使用简单的正弦波 + import math + wave_amplitude = min(3, distance_y // 4) if distance_y > 0 else 0 + wave_y = int(wave_amplitude * math.sin(progress * math.pi * 2)) + y = max(min_y, min(max_y, base_y + wave_y)) + + path_points.append((x, y)) + else: + # 主要沿Y轴方向流动 + for y in range(min_y, max_y + 1): + progress = (y - min_y) / max(distance_y, 1) + base_x = min_x + int(progress * distance_x) + + # 添加蜿蜒效果 + import math + wave_amplitude = min(3, distance_x // 4) if distance_x > 0 else 0 + wave_x = int(wave_amplitude * math.sin(progress * math.pi * 2)) + x = max(min_x, min(max_x, base_x + wave_x)) + + path_points.append((x, y)) + + # 为每个路径点添加宽度 + for px, py in path_points: + for dx in range(-width//2, width//2 + 1): + for dy in range(-width//2, width//2 + 1): + nx, ny = px + dx, py + dy + # 确保在边界内 + if min_x <= nx <= max_x and min_y <= ny <= max_y: + coordinates.append((nx, ny)) + + # 去重并排序 + return sorted(list(set(coordinates))) + + +@dataclass +class Region(ABC): + """ + 区域抽象基类 + 理想中,一些地块应当在一起组成一个区域。 + 比如,某山;某湖、江、海;某森林;某平原;某城市; + 一些分布,比如物产,按照Region来分布。 + 再比如,灵气,应当也是按照region分布的。 + 默认,一个region内部的属性,是共通的。 + 同时,NPC应当对Region有观测和认知。 + """ + id: int + name: str + desc: str + shape: 'Shape' + north_west_cor: str # 西北角坐标,格式: "x,y" + south_east_cor: str # 东南角坐标,格式: "x,y" + + # 这些字段将在__post_init__中设置 + cors: list[tuple[int, int]] = field(init=False) # 存储所有坐标点 + center_loc: tuple[int, int] = field(init=False) + area: int = field(init=False) + + def __post_init__(self): + """初始化计算字段""" + # 先计算所有坐标点 + self.cors = get_tiles_from_shape(self.shape, self.north_west_cor, self.south_east_cor) + + # 基于坐标点计算面积 + self.area = len(self.cors) + + # 计算中心位置(基于实际坐标点的平均值) + if self.cors: + avg_x = sum(coord[0] for coord in self.cors) // len(self.cors) + avg_y = sum(coord[1] for coord in self.cors) // len(self.cors) + self.center_loc = (avg_x, avg_y) + else: + # 如果没有坐标点,使用边界框中心作为fallback + nw_coords = tuple(map(int, self.north_west_cor.split(','))) + se_coords = tuple(map(int, self.south_east_cor.split(','))) + self.center_loc = ( + (nw_coords[0] + se_coords[0]) // 2, + (nw_coords[1] + se_coords[1]) // 2 + ) + + def __hash__(self) -> int: + return hash(self.id) + + def __eq__(self, other) -> bool: + if not isinstance(other, Region): + return False + return self.id == other.id + + @abstractmethod + def get_region_type(self) -> str: + """返回区域类型的字符串表示""" + pass + + +class Shape(Enum): + """ + 区域形状类型 + """ + SQUARE = "square" # 正方形 + RECTANGLE = "rectangle" # 长方形 + MEANDERING = "meandering" # 蜿蜒的(如河流) + + @classmethod + def from_str(cls, shape_str: str) -> 'Shape': + """ + 从字符串创建Shape实例 + + Args: + shape_str: 形状的字符串表示,如 "square", "rectangle", "meandering" + + Returns: + 对应的Shape枚举值 + + Raises: + ValueError: 如果字符串不匹配任何已知的形状类型 + """ + for shape in cls: + if shape.value == shape_str: + return shape + raise ValueError(f"Unknown shape type: {shape_str}") + + +@dataclass +class NormalRegion(Region): + """ + 普通区域 - 平原、大河之类的,没有灵气或灵气很低 + """ + + def get_region_type(self) -> str: + return "normal" + + def __str__(self) -> str: + return f"普通区域:{self.name} - {self.desc}" + + +@dataclass +class CultivateRegion(Region): + """ + 修炼区域 - 有灵气的区域,可以修炼 + """ + essence_type: EssenceType # 最高灵气类型 + essence_density: int # 最高灵气密度 + essence: Essence = field(init=False) # 灵气对象,根据 essence_type 和 essence_density 生成 + + def __post_init__(self): + # 先调用父类的 __post_init__ + super().__post_init__() + + # 创建灵气对象,主要灵气类型设置为指定密度,其他类型设置为0 + essence_density_dict = {essence_type: 0 for essence_type in EssenceType} + essence_density_dict[self.essence_type] = self.essence_density + self.essence = Essence(essence_density_dict) + + def get_region_type(self) -> str: + return "cultivate" + + def __str__(self) -> str: + return f"修炼区域:{self.name}({self.essence_type}行灵气:{self.essence_density})- {self.desc}" + + +@dataclass +class CityRegion(Region): + """ + 城市区域 - 不能修炼,但会有特殊操作 + """ + + def get_region_type(self) -> str: + return "city" + + def __str__(self) -> str: + return f"城市区域:{self.name} - {self.desc}" + + +T = TypeVar('T', NormalRegion, CultivateRegion, CityRegion) + +def _load_regions(region_type: Type[T], config_name: str) -> tuple[dict[int, T], dict[str, T]]: + """ + 通用的区域加载函数 + + Args: + region_type: 区域类型 (NormalRegion, CultivateRegion, CityRegion) + config_name: 配置文件名 ("normal_region", "cultivate_region", "city_region") + + Returns: + (按ID索引的字典, 按名称索引的字典) + """ + regions_by_id: dict[int, T] = {} + regions_by_name: dict[str, T] = {} + + region_df = game_configs[config_name] + for _, row in region_df.iterrows(): + # 构建基础参数 + base_params = { + "id": int(row["id"]), + "name": str(row["name"]), + "desc": str(row["desc"]), + "shape": Shape.from_str(str(row["shape"])), + "north_west_cor": str(row["north-west-cor"]), + "south_east_cor": str(row["south-east-cor"]) + } + + # 如果是修炼区域,添加额外参数 + if region_type == CultivateRegion: + base_params["essence_type"] = EssenceType.from_str(str(row["root_type"])) + base_params["essence_density"] = int(row["root_density"]) + + region = region_type(**base_params) + regions_by_id[region.id] = region + regions_by_name[region.name] = region + + return regions_by_id, regions_by_name + + +def load_all_regions() -> tuple[ + dict[int, Union[NormalRegion, CultivateRegion, CityRegion]], + dict[str, Union[NormalRegion, CultivateRegion, CityRegion]] +]: + """ + 统一加载所有类型的区域数据 + 返回: (按ID索引的字典, 按名称索引的字典) + """ + all_regions_by_id: dict[int, Union[NormalRegion, CultivateRegion, CityRegion]] = {} + all_regions_by_name: dict[str, Union[NormalRegion, CultivateRegion, CityRegion]] = {} + + # 加载普通区域 + normal_by_id, normal_by_name = _load_regions(NormalRegion, "normal_region") + all_regions_by_id.update(normal_by_id) + all_regions_by_name.update(normal_by_name) + + # 加载修炼区域 + cultivate_by_id, cultivate_by_name = _load_regions(CultivateRegion, "cultivate_region") + all_regions_by_id.update(cultivate_by_id) + all_regions_by_name.update(cultivate_by_name) + + # 加载城市区域 + city_by_id, city_by_name = _load_regions(CityRegion, "city_region") + all_regions_by_id.update(city_by_id) + all_regions_by_name.update(city_by_name) + + return all_regions_by_id, all_regions_by_name + + +# 从配表加载所有区域数据 +regions_by_id, regions_by_name = load_all_regions() + +# 分别加载各类型区域数据 +normal_regions_by_id, normal_regions_by_name = _load_regions(NormalRegion, "normal_region") +cultivate_regions_by_id, cultivate_regions_by_name = _load_regions(CultivateRegion, "cultivate_region") +city_regions_by_id, city_regions_by_name = _load_regions(CityRegion, "city_region") diff --git a/src/classes/root.py b/src/classes/root.py index bce261d..b77112a 100644 --- a/src/classes/root.py +++ b/src/classes/root.py @@ -18,6 +18,25 @@ class Root(Enum): WATER = "水" FIRE = "火" EARTH = "土" + + @classmethod + def from_str(cls, root_str: str) -> 'Root': + """ + 从字符串创建Root实例 + + Args: + root_str: 灵根的字符串表示,如 "金", "木", "水", "火", "土" + + Returns: + 对应的Root枚举值 + + Raises: + ValueError: 如果字符串不匹配任何已知的灵根类型 + """ + for root in cls: + if root.value == root_str: + return root + raise ValueError(f"Unknown root type: {root_str}") corres_essence_type = { Root.GOLD: EssenceType.GOLD, diff --git a/src/classes/tile.py b/src/classes/tile.py index edb6fa7..b38cf37 100644 --- a/src/classes/tile.py +++ b/src/classes/tile.py @@ -1,8 +1,9 @@ -import itertools from enum import Enum -from dataclasses import dataclass, field +from dataclasses import dataclass +from typing import TYPE_CHECKING -from src.classes.essence import Essence, EssenceType +if TYPE_CHECKING: + from src.classes.region import Region class TileType(Enum): PLAIN = "plain" # 平原 @@ -22,52 +23,6 @@ class TileType(Enum): RUINS = "ruins" # 遗迹 FARM = "farm" # 农田 -region_id_counter = itertools.count(1) - - -@dataclass -class Region(): - """ - 理想中,一些地块应当在一起组成一个区域。 - 比如,某山;某湖、江、海;某森林;某平原;某城市; - 一些分布,比如物产,按照Region来分布。 - 再比如,灵气,应当也是按照region分布的。 - 默认,一个region内部的属性,是共通的。 - 同时,NPC应当对Region有观测和认知。 - """ - name: str - description: str - essence: Essence - id: int = field(init=False) - center_loc: tuple[int, int] = field(init=False) - area: int = field(init=False) - - def __post_init__(self): - self.id = next(region_id_counter) - - def __str__(self) -> str: - return f"区域。名字:{self.name},描述:{self.description},最浓的灵气:{self.get_most_dense_essence()}, 灵气值:{self.get_most_dense_essence_value()}" - - def get_most_dense_essence(self) -> EssenceType: - return max(self.essence.density.items(), key=lambda x: x[1])[0] - - def get_most_dense_essence_value(self) -> int: - most_dense_essence = self.get_most_dense_essence() - return self.essence.density[most_dense_essence] - - def __hash__(self) -> int: - return hash(self.id) - - def __eq__(self, other) -> bool: - if not isinstance(other, Region): - return False - return self.id == other.id - # 物产 - # 灵气 - # 其他 - -default_region = Region(name="平原", description="最普通的平原,没有什么可说的", essence=Essence(density={EssenceType.GOLD: 1, EssenceType.WOOD: 1, EssenceType.WATER: 1, EssenceType.FIRE: 1, EssenceType.EARTH: 1})) -default_region.area = 1 # 默认区域面积为1 @dataclass class Tile(): @@ -75,7 +30,7 @@ class Tile(): type: TileType x: int y: int - region: Region # 可以是一个region的一部分,也可以不属于任何region + region: 'Region' = None # 可以是一个region的一部分,也可以不属于任何region class Map(): """ @@ -83,10 +38,18 @@ class Map(): """ def __init__(self, width: int, height: int): self.tiles = {} - self.regions = {} # region_id -> region - self.region_names = {} # region_name -> region self.width = width self.height = height + + # 加载所有region数据到Map中 + self._load_regions() + + def _load_regions(self): + """从配置文件加载所有区域数据到Map实例中""" + # 延迟导入避免循环导入 + from src.classes.region import load_all_regions + + self.regions, self.region_names = load_all_regions() def is_in_bounds(self, x: int, y: int) -> bool: """ @@ -95,25 +58,11 @@ class Map(): return 0 <= x < self.width and 0 <= y < self.height def create_tile(self, x: int, y: int, tile_type: TileType): - self.tiles[(x, y)] = Tile(tile_type, x, y, region=default_region) + self.tiles[(x, y)] = Tile(tile_type, x, y, region=None) def get_tile(self, x: int, y: int) -> Tile: return self.tiles[(x, y)] - def create_region(self, name: str, description: str, essence: Essence, locs: list[tuple[int, int]]): - """ - 创建一个region。 - """ - region = Region(name=name, description=description, essence=essence) - center_loc = self.get_center_locs(locs) - for loc in locs: - self.tiles[loc].region = region - region.center_loc = center_loc - region.area = len(locs) - self.regions[region.id] = region - self.region_names[name] = region - return region - def get_center_locs(self, locs: list[tuple[int, int]]) -> tuple[int, int]: """ 获取locs的中心位置。 @@ -139,7 +88,7 @@ class Map(): return min(locs, key=distance_squared) - def get_region(self, x: int, y: int) -> Region | None: + def get_region(self, x: int, y: int) -> 'Region | None': """ 获取一个region。 """ diff --git a/src/front/front.py b/src/front/front.py index 96e33cb..4217da9 100644 --- a/src/front/front.py +++ b/src/front/front.py @@ -284,14 +284,14 @@ class Front: def _draw_region_labels(self): """绘制区域标签""" pygame = self.pygame - map_obj = self.world.map ts = self.tile_size m = self.margin mouse_x, mouse_y = pygame.mouse.get_pos() # 绘制每个region的标签 + from src.classes.region import regions_by_id hovered_region = None - for region in map_obj.regions.values(): + for region in regions_by_id.values(): name = getattr(region, "name", None) if not name: continue @@ -458,25 +458,24 @@ class Front: def _draw_tooltip_for_region(self, region, mouse_x: int, mouse_y: int): """绘制Region的tooltip""" + # 如果region为None,不显示tooltip + if region is None: + return + lines = [ f"区域: {region.name}", - f"描述: {region.description}", + f"描述: {region.desc}", ] - # 添加灵气信息 - if hasattr(region, 'essence') and region.essence: - essence_items = [] - for essence_type, density in region.essence.density.items(): - if density > 0: - essence_name = str(essence_type) - essence_items.append((density, essence_name)) - - if essence_items: - essence_items.sort(reverse=True) - lines.append("灵气分布:") - for density, name in essence_items: - stars = "★" * density + "☆" * (10 - density) - lines.append(f" {name}: {stars}") + # 根据region类型添加灵气信息 + from src.classes.region import CultivateRegion + + if isinstance(region, CultivateRegion): + # 修炼区域:只显示最高灵气类型和密度 + stars = "★" * region.essence_density + "☆" * (10 - region.essence_density) + lines.append(f"主要灵气: {region.essence_type} {stars}") + + # 普通区域和城市区域不显示灵气信息 self._draw_tooltip(lines, mouse_x, mouse_y, self.tooltip_font) diff --git a/src/run/create_map.py b/src/run/create_map.py index c2def10..9239984 100644 --- a/src/run/create_map.py +++ b/src/run/create_map.py @@ -15,7 +15,7 @@ def create_cultivation_world_map() -> Map: _create_base_terrain(game_map) # 创建区域 - _create_regions(game_map) + _assign_regions_to_tiles(game_map) return game_map @@ -208,347 +208,20 @@ def _add_other_terrains(game_map: Map): if game_map.tiles[(x, y)].type == TileType.PLAIN: game_map.tiles[(x, y)].type = TileType.SWAMP -def _create_regions(game_map: Map): - """创建区域并分配灵气""" - - # 收集各地形的坐标 - terrain_coords = {} +def _assign_regions_to_tiles(game_map: Map): + """将区域分配给地图中的tiles""" + # 初始化所有tiles的region为None for x in range(game_map.width): for y in range(game_map.height): - tile_type = game_map.tiles[(x, y)].type - if tile_type not in terrain_coords: - terrain_coords[tile_type] = [] - terrain_coords[tile_type].append((x, y)) + game_map.tiles[(x, y)].region = None - # 西域流沙 (大漠) - if TileType.DESERT in terrain_coords: - essence = Essence({ - EssenceType.FIRE: 8, - EssenceType.EARTH: 7, - EssenceType.GOLD: 6, - EssenceType.WATER: 1, - EssenceType.WOOD: 2 - }) - game_map.create_region( - "西域流沙", - "茫茫大漠,黄沙漫天。此地火行灵气浓郁,土行次之,乃是修炼火系功法的绝佳之地。", - essence, - terrain_coords[TileType.DESERT] - ) - - # 南疆蛮荒 (雨林) - if TileType.RAINFOREST in terrain_coords: - essence = Essence({ - EssenceType.WOOD: 8, # 木行主属性,但低于洞府的10 - EssenceType.WATER: 6, - EssenceType.EARTH: 5, - EssenceType.FIRE: 3, - EssenceType.GOLD: 2 - }) - game_map.create_region( - "南疆蛮荒", - "古木参天,藤蔓缠绕。此地木行灵气极为浓郁,水行次之,是修炼木系功法和炼制灵药的宝地。", - essence, - terrain_coords[TileType.RAINFOREST] - ) - - # 极北冰原 (冰原) - if TileType.GLACIER in terrain_coords: - essence = Essence({ - EssenceType.WATER: 8, - EssenceType.GOLD: 6, - EssenceType.EARTH: 4, - EssenceType.FIRE: 1, - EssenceType.WOOD: 3 - }) - game_map.create_region( - "极北冰原", - "千里冰封,万年不化。此地水行灵气充沛,金行次之,寒气逼人,唯有修炼寒冰功法者方可久居。", - essence, - terrain_coords[TileType.GLACIER] - ) - - # 无边碧海 (海洋) - if TileType.SEA in terrain_coords: - essence = Essence({ - EssenceType.WATER: 8, # 水行主属性,但低于洞府的10 - EssenceType.GOLD: 4, - EssenceType.WOOD: 3, - EssenceType.EARTH: 2, - EssenceType.FIRE: 1 - }) - game_map.create_region( - "无边碧海", - "浩瀚无垠,波涛汹涌。此地水行灵气达到极致,蕴含无穷玄机,是水系修士向往的圣地。", - essence, - terrain_coords[TileType.SEA] - ) - - # 天河奔流 (大河) - if TileType.WATER in terrain_coords: - essence = Essence({ - EssenceType.WATER: 7, - EssenceType.WOOD: 5, - EssenceType.EARTH: 4, - EssenceType.GOLD: 3, - EssenceType.FIRE: 2 - }) - game_map.create_region( - "天河奔流", - "一江春水向东流,奔腾不息入东海。此河贯穿东西,水行灵气丰沛,滋养两岸万物生灵。", - essence, - terrain_coords[TileType.WATER] - ) - - # 青峰山脉 (山脉) - if TileType.MOUNTAIN in terrain_coords: - essence = Essence({ - EssenceType.EARTH: 8, - EssenceType.GOLD: 7, - EssenceType.FIRE: 5, - EssenceType.WOOD: 3, - EssenceType.WATER: 2 - }) - game_map.create_region( - "青峰山脉", - "连绵起伏,直插云霄。此地土行灵气深厚,金行次之,乃是修炼土系功法和寻找天材地宝的胜地。", - essence, - terrain_coords[TileType.MOUNTAIN] - ) - - # 万丈雪峰 (雪山) - if TileType.SNOW_MOUNTAIN in terrain_coords: - essence = Essence({ - EssenceType.WATER: 7, # 水行主属性,但低于洞府的10 - EssenceType.GOLD: 6, # 金行次要,但低于洞府的10 - EssenceType.EARTH: 6, - EssenceType.FIRE: 1, - EssenceType.WOOD: 2 - }) - game_map.create_region( - "万丈雪峰", - "雪峰皑皑,寒风刺骨。此地水行与金行灵气并重,是修炼冰系神通和淬炼法宝的绝佳之地。", - essence, - terrain_coords[TileType.SNOW_MOUNTAIN] - ) - - # 碧野千里 (草原) - if TileType.GRASSLAND in terrain_coords: - essence = Essence({ - EssenceType.WOOD: 5, # 木行属性适中 - EssenceType.EARTH: 5, # 土行属性适中 - EssenceType.WATER: 5, - EssenceType.GOLD: 3, - EssenceType.FIRE: 4 - }) - game_map.create_region( - "碧野千里", - "芳草萋萋,一望无际。此地木土并重,灵气平和,是修炼基础功法和放牧灵兽的理想之地。", - essence, - terrain_coords[TileType.GRASSLAND] - ) - - # 青云林海 (森林) - if TileType.FOREST in terrain_coords: - essence = Essence({ - EssenceType.WOOD: 7, # 木行主属性,但低于洞府的10 - EssenceType.WATER: 4, - EssenceType.EARTH: 4, - EssenceType.GOLD: 3, - EssenceType.FIRE: 3 - }) - game_map.create_region( - "青云林海", - "古树参天,绿意盎然。此地木行灵气浓郁,是修炼木系功法、采集灵草和驯服林间灵兽的宝地。", - essence, - terrain_coords[TileType.FOREST] - ) - - # 炎狱火山 (火山) - if TileType.VOLCANO in terrain_coords: - essence = Essence({ - EssenceType.FIRE: 8, # 火行主属性,但低于洞府的10 - EssenceType.EARTH: 7, - EssenceType.GOLD: 4, - EssenceType.WATER: 1, - EssenceType.WOOD: 1 - }) - game_map.create_region( - "炎狱火山", - "烈焰冲天,岩浆奔流。此地火行灵气达到极致,是修炼火系神通和锻造法宝的圣地,常人不可近。", - essence, - terrain_coords[TileType.VOLCANO] - ) - - # 为每个2*2城市、洞穴和遗迹创建独立区域 - _create_city_regions(game_map) - _create_wuxing_caves_regions(game_map) - _create_ruins_regions(game_map) - - - - - - # 沃土良田 (农田) - if TileType.FARM in terrain_coords: - essence = Essence({ - EssenceType.WOOD: 6, # 木行属性较强 - EssenceType.EARTH: 6, # 土行属性较强 - EssenceType.WATER: 6, - EssenceType.GOLD: 2, - EssenceType.FIRE: 3 - }) - game_map.create_region( - "沃土良田", - "土地肥沃,五谷丰登。此地木土并重,水行充沛,是种植灵药和培育灵植的理想之地。", - essence, - terrain_coords[TileType.FARM] - ) - - # 平原地带 (平原) - if TileType.PLAIN in terrain_coords: - essence = Essence({ - EssenceType.EARTH: 5, - EssenceType.WOOD: 4, - EssenceType.WATER: 4, - EssenceType.GOLD: 3, - EssenceType.FIRE: 3 - }) - game_map.create_region( - "平原地带", - "地势平坦,灵气平和。此地五行均衡,是初学修炼者打基础和建立宗门的理想之地。", - essence, - terrain_coords[TileType.PLAIN] - ) - - # 迷雾沼泽 (沼泽) - if TileType.SWAMP in terrain_coords: - essence = Essence({ - EssenceType.WATER: 6, # 水行属性较强 - EssenceType.WOOD: 5, # 木行属性适中 - EssenceType.EARTH: 5, - EssenceType.FIRE: 2, - EssenceType.GOLD: 3 - }) - game_map.create_region( - "迷雾沼泽", - "雾气缭绕,泥泞不堪。此地水木灵气浓郁,但瘴气丛生,是修炼毒功和寻找奇异灵草的危险之地。", - essence, - terrain_coords[TileType.SWAMP] - ) - -def _create_city_regions(game_map: Map): - """为每个2*2城市创建独立区域""" - cities = [ - {"name": "青云城", "base_x": 34, "base_y": 21, "description": "繁华都市,人烟稠密,商贾云集。此地金行灵气较为集中,是交易天材地宝、寻找机缘的重要场所。"}, - {"name": "沙月城", "base_x": 14, "base_y": 19, "description": "沙漠绿洲中的贸易重镇,各路商队在此集结。金行灵气充沛,是修士补给和交流的重要据点。"}, - {"name": "翠林城", "base_x": 54, "base_y": 14, "description": "森林深处的修仙重镇,木行灵气与金行灵气并重。众多修士在此栖居,是修炼和炼宝的理想之地。"} - ] - - for city in cities: - base_x, base_y = city["base_x"], city["base_y"] - city_coords = [] - - for dx in range(2): - for dy in range(2): - x, y = base_x + dx, base_y + dy - if game_map.is_in_bounds(x, y): - city_coords.append((x, y)) - - essence = Essence({ - EssenceType.GOLD: 6, - EssenceType.EARTH: 5, - EssenceType.WOOD: 5, - EssenceType.WATER: 4, - EssenceType.FIRE: 4 - }) - - game_map.create_region( - city["name"], - city["description"], - essence, - city_coords - ) - -def _create_wuxing_caves_regions(game_map: Map): - """为每个2*2五行洞府创建独立区域""" - wuxing_caves = [ - {"name": "太白金府", "base_x": 26, "base_y": 12, "element": EssenceType.GOLD, - "description": "青峰山脉深处的金行洞府,金精气凝,刀剑鸣音不绝,乃金系修士的最高圣地。"}, - {"name": "青木洞天", "base_x": 48, "base_y": 18, "element": EssenceType.WOOD, - "description": "青云林海中的木行洞府,生机盎然,灵药遍地,乃木系修士的最高圣地。"}, - {"name": "玄水秘境", "base_x": 67, "base_y": 25, "element": EssenceType.WATER, - "description": "无边碧海深处的水行洞府,碧波万里,水精凝神,乃水系修士的最高圣地。"}, - {"name": "离火洞府", "base_x": 50, "base_y": 33, "element": EssenceType.FIRE, - "description": "炎狱火山旁的火行洞府,烈焰冲天,真火精纯,乃火系修士的最高圣地。"}, - {"name": "厚土玄宫", "base_x": 30, "base_y": 16, "element": EssenceType.EARTH, - "description": "青峰山脉的土行洞府,厚德载物,山岳共鸣,乃土系修士的最高圣地。"} - ] - - for cave in wuxing_caves: - base_x, base_y = cave["base_x"], cave["base_y"] - cave_coords = [] - - for dx in range(2): - for dy in range(2): - x, y = base_x + dx, base_y + dy - if game_map.is_in_bounds(x, y): - cave_coords.append((x, y)) - - # 每个洞府的主属性灵气为10(最高值),其他属性较低 - essence_config = {essence_type: 2 for essence_type in EssenceType} - essence_config[cave["element"]] = 10 # 主属性达到最高值 - - essence = Essence(essence_config) - - game_map.create_region( - cave["name"], - cave["description"], - essence, - cave_coords - ) - -def _create_ruins_regions(game_map: Map): - """为每个2*2遗迹创建独立区域""" - ruins = [ - {"name": "古越遗迹", "base_x": 25, "base_y": 40, "description": "雨林深处的上古遗迹,古藤缠绕,木行灵气与金行灵气交融。蕴藏古老功法与灵药配方。"}, - {"name": "沧海遗迹", "base_x": 66, "base_y": 47, "description": "沉没在海中的远古文明遗迹,水行灵气浓郁,潮汐间偶有宝物现世。"} - ] - - for ruin in ruins: - base_x, base_y = ruin["base_x"], ruin["base_y"] - ruin_coords = [] - - for dx in range(2): - for dy in range(2): - x, y = base_x + dx, base_y + dy - if game_map.is_in_bounds(x, y): - ruin_coords.append((x, y)) - - # 根据遗迹位置调整灵气配置 - if ruin["name"] == "古越遗迹": # 雨林深处 - essence = Essence({ - EssenceType.WOOD: 8, - EssenceType.GOLD: 6, - EssenceType.WATER: 5, - EssenceType.EARTH: 4, - EssenceType.FIRE: 3 - }) - else: # 沧海遗迹,海中 - essence = Essence({ - EssenceType.WATER: 9, - EssenceType.GOLD: 6, - EssenceType.EARTH: 3, - EssenceType.WOOD: 3, - EssenceType.FIRE: 2 - }) - - game_map.create_region( - ruin["name"], - ruin["description"], - essence, - ruin_coords - ) + # 遍历所有region,为对应的坐标分配正确的region + # 现在从map实例获取region信息,而不是直接从region.py导入 + for region in game_map.regions.values(): + for coord_x, coord_y in region.cors: + # 确保坐标在地图范围内 + if game_map.is_in_bounds(coord_x, coord_y): + game_map.tiles[(coord_x, coord_y)].region = region if __name__ == "__main__": # 创建地图 diff --git a/src/run/log.py b/src/run/log.py new file mode 100644 index 0000000..f3be32f --- /dev/null +++ b/src/run/log.py @@ -0,0 +1,198 @@ +""" +通用日志模块 +功能: +1. 自动记录各种调用的输入和输出(目前主要用于LLM) +2. 启动时自动删除一周以前的日志文件 +3. 日志文件按日期分组 +""" + +import logging +import os +import json +import time +from datetime import datetime, timedelta +from pathlib import Path +from typing import Optional + +class Logger: + """通用日志记录器""" + + def __init__(self, log_dir: str = "logs"): + """ + 初始化日志记录器 + + Args: + log_dir: 日志文件存储目录 + """ + self.log_dir = Path(log_dir) + self.log_dir.mkdir(exist_ok=True) + + # 清理旧日志文件 + self._cleanup_old_logs() + + # 设置当前日志文件 + self._setup_current_logger() + + def _cleanup_old_logs(self): + """删除一周以前的日志文件""" + cutoff_date = datetime.now() - timedelta(days=7) + + for log_file in self.log_dir.glob("*.log"): + try: + # 从文件名中提取日期 + date_str = log_file.stem.split("_")[-1] # 取最后一部分作为日期 + file_date = datetime.strptime(date_str, "%Y%m%d") + + if file_date < cutoff_date: + log_file.unlink() + print(f"已删除过期日志文件: {log_file}") + except (ValueError, OSError) as e: + print(f"处理日志文件 {log_file} 时出错: {e}") + + def _setup_current_logger(self): + """设置当前日期的日志记录器""" + current_date = datetime.now().strftime("%Y%m%d") + log_filename = f"app_{current_date}.log" + self.log_file_path = self.log_dir / log_filename + + # 创建日志记录器 + self.logger = logging.getLogger(f"app_logger_{current_date}") + self.logger.setLevel(logging.INFO) + + # 清除现有的处理器(避免重复记录) + self.logger.handlers.clear() + + # 创建文件处理器 + handler = logging.FileHandler( + self.log_file_path, + encoding='utf-8', + mode='a' + ) + + # 设置日志格式 + formatter = logging.Formatter( + '%(asctime)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' + ) + handler.setFormatter(formatter) + + self.logger.addHandler(handler) + + # 不向根日志记录器传播 + self.logger.propagate = False + + def log_llm_interaction(self, + model_name: str, + prompt: str, + response: str, + duration: Optional[float] = None, + additional_info: Optional[dict] = None): + """ + 记录LLM交互 + + Args: + model_name: 使用的模型名称 + prompt: 输入的提示词 + response: LLM的响应 + duration: 调用耗时(秒) + additional_info: 额外信息 + """ + log_data = { + "timestamp": datetime.now().isoformat(), + "model_name": model_name, + "prompt": prompt, + "response": response, + "prompt_length": len(prompt), + "response_length": len(response), + "duration": duration + } + + if additional_info: + log_data.update(additional_info) + + # 记录日志 + log_message = f"LLM_INTERACTION: {json.dumps(log_data, ensure_ascii=False)}" + self.logger.info(log_message) + + def log_error(self, error_message: str, prompt: str = None): + """ + 记录错误 + + Args: + error_message: 错误信息 + prompt: 相关的提示词(可选) + """ + log_data = { + "timestamp": datetime.now().isoformat(), + "error": error_message, + "prompt": prompt if prompt else None + } + + log_message = f"LLM_ERROR: {json.dumps(log_data, ensure_ascii=False)}" + self.logger.error(log_message) + + def get_today_stats(self) -> dict: + """ + 获取今日统计信息 + + Returns: + dict: 包含今日调用次数、总耗时等信息 + """ + if not self.log_file_path.exists(): + return { + "total_calls": 0, + "total_duration": 0, + "total_prompt_length": 0, + "total_response_length": 0, + "errors": 0 + } + + stats = { + "total_calls": 0, + "total_duration": 0, + "total_prompt_length": 0, + "total_response_length": 0, + "errors": 0 + } + + with open(self.log_file_path, 'r', encoding='utf-8') as f: + for line in f: + if "LLM_INTERACTION:" in line: + try: + json_str = line.split("LLM_INTERACTION: ", 1)[1] + data = json.loads(json_str) + stats["total_calls"] += 1 + stats["total_duration"] += data.get("duration", 0) or 0 + stats["total_prompt_length"] += data.get("prompt_length", 0) + stats["total_response_length"] += data.get("response_length", 0) + except (json.JSONDecodeError, IndexError): + pass + elif "LLM_ERROR:" in line: + stats["errors"] += 1 + + return stats + + +# 全局日志记录器实例 +_logger = None + +def get_logger() -> Logger: + """获取全局日志记录器实例""" + global _logger + if _logger is None: + _logger = Logger() + return _logger + +# LLM专用的便捷函数 +def log_llm_call(model_name: str, prompt: str, response: str, duration: float = None): + """便捷函数:记录LLM调用""" + logger = get_logger() + logger.log_llm_interaction(model_name, prompt, response, duration) + +def log_llm_error(error_message: str, prompt: str = None): + """便捷函数:记录LLM错误""" + logger = get_logger() + logger.log_error(error_message, prompt) + +# 向后兼容的别名 +get_llm_logger = get_logger diff --git a/src/run/run.py b/src/run/run.py index b2c7ee3..26d868e 100644 --- a/src/run/run.py +++ b/src/run/run.py @@ -23,6 +23,7 @@ from src.run.create_map import create_cultivation_world_map from src.utils.names import get_random_name from src.utils.id_generator import get_avatar_id from src.utils.config import CONFIG +from src.run.log import get_logger def clamp(value: int, lo: int, hi: int) -> int: @@ -90,6 +91,10 @@ def make_avatars(world: World, count: int = 12, current_month_stamp: MonthStamp async def main(): # 为了每次更丰富,使用随机种子;如需复现可将 seed 固定 + + # 初始化日志系统(会自动清理旧日志) + logger = get_logger() + print(f"日志系统已初始化,日志文件:{logger.log_file_path}") game_map = create_cultivation_world_map() world = World(map=game_map, month_stamp=create_month_stamp(Year(100), Month.JANUARY)) diff --git a/src/utils/df.py b/src/utils/df.py index c240b48..cf0d77e 100644 --- a/src/utils/df.py +++ b/src/utils/df.py @@ -5,7 +5,8 @@ from src.utils.config import CONFIG def load_csv(path: Path) -> pd.DataFrame: - df = pd.read_csv(path) + # 跳过第二行说明行,只读取标题行和实际数据行 + df = pd.read_csv(path, skiprows=[1]) row_types = { "id": int, "name": str, diff --git a/src/utils/llm.py b/src/utils/llm.py index be13f2d..575e1cb 100644 --- a/src/utils/llm.py +++ b/src/utils/llm.py @@ -7,6 +7,7 @@ import json5 from src.utils.config import CONFIG from src.utils.io import read_txt +from src.run.log import log_llm_call def get_prompt(template: str, infos: dict) -> str: """ @@ -36,7 +37,9 @@ def call_llm(prompt: str) -> str: ) # 返回生成的内容 - return response.choices[0].message.content + result = response.choices[0].message.content + log_llm_call(model_name, prompt, result) # 记录日志 + return result async def call_llm_async(prompt: str) -> str: """ @@ -48,7 +51,8 @@ async def call_llm_async(prompt: str) -> str: str: LLM返回的结果 """ # 使用asyncio.to_thread包装同步调用 - return await asyncio.to_thread(call_llm, prompt) + result = await asyncio.to_thread(call_llm, prompt) + return result def parse_llm_response(res: str) -> dict: """ diff --git a/static/game_configs/animal.csv b/static/game_configs/animal.csv index 12a7fcd..79263c8 100644 --- a/static/game_configs/animal.csv +++ b/static/game_configs/animal.csv @@ -1,3 +1,4 @@ id,name,desc,grade +,,, 1,灵兔,天性机敏的灵性兔子,毛色雪白,蕴含微弱灵力,性情温和易驯服,1 2,魔狼,凶猛的魔性狼族,体型巨大,拥有强大的魔力和锋利的爪牙,2 diff --git a/static/game_configs/city_region.csv b/static/game_configs/city_region.csv new file mode 100644 index 0000000..6fdbb57 --- /dev/null +++ b/static/game_configs/city_region.csv @@ -0,0 +1,5 @@ +id,name,desc,shape,north-west-cor,south-east-cor +"ID必须以3开头",,,, +301,青云城,繁华都市,人烟稠密,商贾云集。此地是交易天材地宝、寻找机缘的重要场所。,square,"34,21","35,22" +302,沙月城,沙漠绿洲中的贸易重镇,各路商队在此集结,是修士补给和交流的重要据点。,square,"14,19","15,20" +303,翠林城,森林深处的修仙重镇,众多修士在此栖居,是修炼和炼宝的理想之地。,square,"54,14","55,15" diff --git a/static/game_configs/cultivate_region.csv b/static/game_configs/cultivate_region.csv new file mode 100644 index 0000000..8a216b8 --- /dev/null +++ b/static/game_configs/cultivate_region.csv @@ -0,0 +1,9 @@ +id,name,desc,shape,north-west-cor,south-east-cor,root_type,root_density +"ID必须以2开头",,,,,,, +201,太白金府,青峰山脉深处的金行洞府,金精气凝,刀剑鸣音不绝,乃金系修士的最高圣地。,square,"26,12","27,13",金,10 +202,青木洞天,青云林海中的木行洞府,生机盎然,灵药遍地,乃木系修士的最高圣地。,square,"48,18","49,19",木,10 +203,玄水秘境,无边碧海深处的水行洞府,碧波万里,水精凝神,乃水系修士的最高圣地。,square,"67,25","68,26",水,10 +204,离火洞府,炎狱火山旁的火行洞府,烈焰冲天,真火精纯,乃火系修士的最高圣地。,square,"50,33","51,34",火,10 +205,厚土玄宫,青峰山脉的土行洞府,厚德载物,山岳共鸣,乃土系修士的最高圣地。,square,"30,16","31,17",土,10 +206,古越遗迹,雨林深处的上古遗迹,古藤缠绕,木行灵气与金行灵气交融。蕴藏古老功法与灵药配方。,square,"25,40","26,41",木,8 +207,沧海遗迹,沉没在海中的远古文明遗迹,水行灵气浓郁,潮汐间偶有宝物现世。,square,"66,47","67,48",水,9 diff --git a/static/game_configs/item.csv b/static/game_configs/item.csv index 77fb11e..f5741a1 100644 --- a/static/game_configs/item.csv +++ b/static/game_configs/item.csv @@ -1,4 +1,5 @@ id,name,desc,grade +,,, 1,灵兔皮毛,灵性充沛的兔子皮毛,质地柔软,常用于制作防护装备,1 2,魔狼皮毛,蕴含魔力的狼族皮毛,坚韧耐用,适合制作高级护甲,2 3,奇草,生长在灵气充沛之地的奇异草药,具有神奇的治愈效果,1 diff --git a/static/game_configs/normal_region.csv b/static/game_configs/normal_region.csv new file mode 100644 index 0000000..1409fa8 --- /dev/null +++ b/static/game_configs/normal_region.csv @@ -0,0 +1,15 @@ +id,name,desc,shape,north-west-cor,south-east-cor +"ID必须以1开头",,,, +101,平原地带,地势平坦,灵气平和。是初学修炼者打基础和建立宗门的理想之地。,rectangle,"19,9","64,34" +102,西域流沙,茫茫大漠,黄沙漫天。此地气候干燥,日夜温差极大,是沙漠商队的必经之路。,rectangle,"0,0","18,49" +103,南疆蛮荒,古木参天,藤蔓缠绕。此地森林茂密,野兽众多,是采集药材和狩猎的危险之地。,rectangle,"19,35","64,45" +104,极北冰原,千里冰封,万年不化。此地严寒刺骨,风雪交加,只有最坚韧的冒险者才能在此生存。,rectangle,"19,0","64,8" +105,无边碧海,浩瀚无垠,波涛汹涌。此地风浪险恶,暗礁密布,是海商和渔民的挑战之海。,rectangle,"65,0","69,49" +106,天河奔流,一江春水向东流,奔腾不息入东海。此河贯穿东西,水流湍急,是重要的交通要道。,meandering,"18,25","67,47" +107,青峰山脉,连绵起伏,直插云霄。此地山势险峻,多有奇石异洞,是探险者寻宝的热门之地。,rectangle,"28,5","32,20" +108,万丈雪峰,雪峰皑皑,寒风刺骨。此地终年积雪,山路崎岖,是登山者的终极挑战。,rectangle,"25,2","34,7" +109,碧野千里,芳草萋萋,一望无际。此地水草丰美,牛羊成群,是游牧民族的天然牧场。,rectangle,"20,12","39,24" +110,青云林海,古树参天,绿意盎然。此地森林广袤,物产丰富,是伐木工和猎人的主要活动区域。,rectangle,"40,10","59,29" +111,炎狱火山,烈焰冲天,岩浆奔流。此地火山活跃,地热丰富,是铁匠锻造的理想之地,但也极其危险。,square,"52,32","54,34" +112,沃土良田,土地肥沃,五谷丰登。此地土壤深厚,雨水充沛,是农民耕种的黄金宝地。,rectangle,"33,25","37,29" +113,迷雾沼泽,雾气缭绕,泥泞不堪。此地地形复杂,瘴气丛生,是盗贼和亡命之徒的藏身之所。,rectangle,"42,30","45,33" diff --git a/static/game_configs/persona.csv b/static/game_configs/persona.csv index 330c643..d64d35b 100644 --- a/static/game_configs/persona.csv +++ b/static/game_configs/persona.csv @@ -1,4 +1,5 @@ id,name,prompt +,, 1,理性,你是一个理性的人,你总是会用逻辑来思考问题,做事会谋定而后动。 2,无常,你是一个无常的人,目标飘忽不定,不会长期坚持一个目标。 3,怠惰,你是一个怠惰的人,你总是会拖延,不想努力,更热衷于享受人生。 diff --git a/static/game_configs/plant.csv b/static/game_configs/plant.csv index 5f1846e..0874f1d 100644 --- a/static/game_configs/plant.csv +++ b/static/game_configs/plant.csv @@ -1,3 +1,4 @@ id,name,desc,grade +,,, 1,奇草,生长在灵气充沛之地的奇异草药,叶片呈淡蓝色,具有神奇的治愈效果,1 2,灵木,千年古树吸收天地灵气而成,木质坚硬且蕴含浓郁灵力,2 diff --git a/static/templates/ai.txt b/static/templates/ai.txt index ee774ac..02d6555 100644 --- a/static/templates/ai.txt +++ b/static/templates/ai.txt @@ -1,5 +1,4 @@ 你是一个决策者,这是一个修仙的仙侠世界,你负责来决定一些NPC的下一步行为。 -世界地图上存在的区域为: {global_info} 你需要进行决策的NPC的dict[AvatarId, info]为 {avatar_infos}