refactor regions

This commit is contained in:
bridge
2025-09-10 22:55:31 +08:00
parent 6425e80ffe
commit 12fdccfee5
24 changed files with 664 additions and 438 deletions

1
.gitignore vendored
View File

@@ -136,6 +136,7 @@ Thumbs.db
# Logs and caches
*.log
*.cache
logs/
# Temporary files
~$*

View File

@@ -27,6 +27,7 @@
### 🗺️ 地块系统
- ✅ 基础tile地块系统
- ✅ 基础区域、修行区域、城市区域
- [ ] 同tile内NPC相互交互
- [ ] 城市/宗派据点的机制
- [ ] 灵气分布与产出设计

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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

307
src/classes/region.py Normal file
View File

@@ -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")

View File

@@ -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,

View File

@@ -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。
"""

View File

@@ -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)

View File

@@ -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__":
# 创建地图

198
src/run/log.py Normal file
View File

@@ -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

View File

@@ -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))

View File

@@ -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,

View File

@@ -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:
"""

View File

@@ -1,3 +1,4 @@
id,name,desc,grade
,,,
1,灵兔,天性机敏的灵性兔子,毛色雪白,蕴含微弱灵力,性情温和易驯服,1
2,魔狼,凶猛的魔性狼族,体型巨大,拥有强大的魔力和锋利的爪牙,2
1 id name desc grade
2
3 1 灵兔 天性机敏的灵性兔子,毛色雪白,蕴含微弱灵力,性情温和易驯服 1
4 2 魔狼 凶猛的魔性狼族,体型巨大,拥有强大的魔力和锋利的爪牙 2

View File

@@ -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"
1 id name desc shape north-west-cor south-east-cor
2 ID必须以3开头
3 301 青云城 繁华都市,人烟稠密,商贾云集。此地是交易天材地宝、寻找机缘的重要场所。 square 34,21 35,22
4 302 沙月城 沙漠绿洲中的贸易重镇,各路商队在此集结,是修士补给和交流的重要据点。 square 14,19 15,20
5 303 翠林城 森林深处的修仙重镇,众多修士在此栖居,是修炼和炼宝的理想之地。 square 54,14 55,15

View File

@@ -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
1 id name desc shape north-west-cor south-east-cor root_type root_density
2 ID必须以2开头
3 201 太白金府 青峰山脉深处的金行洞府,金精气凝,刀剑鸣音不绝,乃金系修士的最高圣地。 square 26,12 27,13 10
4 202 青木洞天 青云林海中的木行洞府,生机盎然,灵药遍地,乃木系修士的最高圣地。 square 48,18 49,19 10
5 203 玄水秘境 无边碧海深处的水行洞府,碧波万里,水精凝神,乃水系修士的最高圣地。 square 67,25 68,26 10
6 204 离火洞府 炎狱火山旁的火行洞府,烈焰冲天,真火精纯,乃火系修士的最高圣地。 square 50,33 51,34 10
7 205 厚土玄宫 青峰山脉的土行洞府,厚德载物,山岳共鸣,乃土系修士的最高圣地。 square 30,16 31,17 10
8 206 古越遗迹 雨林深处的上古遗迹,古藤缠绕,木行灵气与金行灵气交融。蕴藏古老功法与灵药配方。 square 25,40 26,41 8
9 207 沧海遗迹 沉没在海中的远古文明遗迹,水行灵气浓郁,潮汐间偶有宝物现世。 square 66,47 67,48 9

View File

@@ -1,4 +1,5 @@
id,name,desc,grade
,,,
1,灵兔皮毛,灵性充沛的兔子皮毛,质地柔软,常用于制作防护装备,1
2,魔狼皮毛,蕴含魔力的狼族皮毛,坚韧耐用,适合制作高级护甲,2
3,奇草,生长在灵气充沛之地的奇异草药,具有神奇的治愈效果,1
1 id name desc grade
2
3 1 灵兔皮毛 灵性充沛的兔子皮毛,质地柔软,常用于制作防护装备 1
4 2 魔狼皮毛 蕴含魔力的狼族皮毛,坚韧耐用,适合制作高级护甲 2
5 3 奇草 生长在灵气充沛之地的奇异草药,具有神奇的治愈效果 1

View File

@@ -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"
1 id name desc shape north-west-cor south-east-cor
2 ID必须以1开头
3 101 平原地带 地势平坦,灵气平和。是初学修炼者打基础和建立宗门的理想之地。 rectangle 19,9 64,34
4 102 西域流沙 茫茫大漠,黄沙漫天。此地气候干燥,日夜温差极大,是沙漠商队的必经之路。 rectangle 0,0 18,49
5 103 南疆蛮荒 古木参天,藤蔓缠绕。此地森林茂密,野兽众多,是采集药材和狩猎的危险之地。 rectangle 19,35 64,45
6 104 极北冰原 千里冰封,万年不化。此地严寒刺骨,风雪交加,只有最坚韧的冒险者才能在此生存。 rectangle 19,0 64,8
7 105 无边碧海 浩瀚无垠,波涛汹涌。此地风浪险恶,暗礁密布,是海商和渔民的挑战之海。 rectangle 65,0 69,49
8 106 天河奔流 一江春水向东流,奔腾不息入东海。此河贯穿东西,水流湍急,是重要的交通要道。 meandering 18,25 67,47
9 107 青峰山脉 连绵起伏,直插云霄。此地山势险峻,多有奇石异洞,是探险者寻宝的热门之地。 rectangle 28,5 32,20
10 108 万丈雪峰 雪峰皑皑,寒风刺骨。此地终年积雪,山路崎岖,是登山者的终极挑战。 rectangle 25,2 34,7
11 109 碧野千里 芳草萋萋,一望无际。此地水草丰美,牛羊成群,是游牧民族的天然牧场。 rectangle 20,12 39,24
12 110 青云林海 古树参天,绿意盎然。此地森林广袤,物产丰富,是伐木工和猎人的主要活动区域。 rectangle 40,10 59,29
13 111 炎狱火山 烈焰冲天,岩浆奔流。此地火山活跃,地热丰富,是铁匠锻造的理想之地,但也极其危险。 square 52,32 54,34
14 112 沃土良田 土地肥沃,五谷丰登。此地土壤深厚,雨水充沛,是农民耕种的黄金宝地。 rectangle 33,25 37,29
15 113 迷雾沼泽 雾气缭绕,泥泞不堪。此地地形复杂,瘴气丛生,是盗贼和亡命之徒的藏身之所。 rectangle 42,30 45,33

View File

@@ -1,4 +1,5 @@
id,name,prompt
,,
1,理性,你是一个理性的人,你总是会用逻辑来思考问题,做事会谋定而后动。
2,无常,你是一个无常的人,目标飘忽不定,不会长期坚持一个目标。
3,怠惰,你是一个怠惰的人,你总是会拖延,不想努力,更热衷于享受人生。
1 id name prompt
2
3 1 理性 你是一个理性的人,你总是会用逻辑来思考问题,做事会谋定而后动。
4 2 无常 你是一个无常的人,目标飘忽不定,不会长期坚持一个目标。
5 3 怠惰 你是一个怠惰的人,你总是会拖延,不想努力,更热衷于享受人生。

View File

@@ -1,3 +1,4 @@
id,name,desc,grade
,,,
1,奇草,生长在灵气充沛之地的奇异草药,叶片呈淡蓝色,具有神奇的治愈效果,1
2,灵木,千年古树吸收天地灵气而成,木质坚硬且蕴含浓郁灵力,2
1 id name desc grade
2
3 1 奇草 生长在灵气充沛之地的奇异草药,叶片呈淡蓝色,具有神奇的治愈效果 1
4 2 灵木 千年古树吸收天地灵气而成,木质坚硬且蕴含浓郁灵力 2

View File

@@ -1,5 +1,4 @@
你是一个决策者这是一个修仙的仙侠世界你负责来决定一些NPC的下一步行为。
世界地图上存在的区域为:
{global_info}
你需要进行决策的NPC的dict[AvatarId, info]为
{avatar_infos}