Files
cultivation-world-simulator/src/run/load_map.py
2025-12-06 17:22:17 +08:00

152 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
import csv
from src.classes.map import Map
from src.classes.tile import TileType
from src.classes.region import Region, NormalRegion, CultivateRegion, CityRegion
from src.classes.sect_region import SectRegion
from src.utils.df import game_configs, get_str, get_int
from src.classes.essence import EssenceType
from src.classes.sect import sects_by_id # 直接导入已加载的宗门数据
# 静态配置路径
CONFIG_DIR = os.path.join(os.path.dirname(__file__), "../../static/game_configs")
def load_cultivation_world_map() -> Map:
"""
从静态 CSV 文件加载修仙世界地图。
读取: tile_map.csv, region_map.csv
以及: normal/city/cultivate/sect_region.csv
"""
tile_csv = os.path.join(CONFIG_DIR, "tile_map.csv")
region_csv = os.path.join(CONFIG_DIR, "region_map.csv")
if not os.path.exists(tile_csv) or not os.path.exists(region_csv):
raise FileNotFoundError(f"Map data files not found in {CONFIG_DIR}")
# 1. 读取 Tile Map 以确定尺寸
with open(tile_csv, 'r', encoding='utf-8') as f:
tile_rows = list(csv.reader(f))
height = len(tile_rows)
width = len(tile_rows[0]) if height > 0 else 0
game_map = Map(width=width, height=height)
# 2. 填充 Tile Type
for y, row in enumerate(tile_rows):
for x, tile_name in enumerate(row):
if x < width:
try:
t_type = TileType[tile_name.upper()]
except KeyError:
# 如果不是标准地形,则是宗门驻地名称
# 这些名称直接对应 SECT 类型
t_type = TileType.SECT
game_map.create_tile(x, y, t_type)
# 3. 读取 Region Map 并聚合坐标
# region_coords: { region_id: [(x, y), ...] }
region_coords = {}
with open(region_csv, 'r', encoding='utf-8') as f:
region_rows = list(csv.reader(f))
for y, row in enumerate(region_rows):
if y >= height: break
for x, val in enumerate(row):
if x >= width: break
try:
rid = int(val)
if rid != -1:
if rid not in region_coords:
region_coords[rid] = []
region_coords[rid].append((x, y))
except ValueError:
continue
# 4. 加载 Region 元数据并创建对象
_load_and_assign_regions(game_map, region_coords)
# 5. 更新缓存
game_map.update_sect_regions()
return game_map
def _load_and_assign_regions(game_map: Map, region_coords: dict[int, list[tuple[int, int]]]):
"""
读取各 region.csv创建 Region 对象,并分配给 Map 和 Tile
"""
# 辅助函数:处理 Region 数据
def process_region_config(df, cls, type_tag):
for row in df:
rid = get_int(row, "id")
if rid not in region_coords:
continue
cors = region_coords[rid]
# 构建参数
params = {
"id": rid,
"name": get_str(row, "name"),
"desc": get_str(row, "desc"),
"cors": cors,
}
# 特有字段处理
if type_tag == "normal":
params["animal_ids"] = _parse_list(get_str(row, "animal_ids"))
params["plant_ids"] = _parse_list(get_str(row, "plant_ids"))
elif type_tag == "cultivate":
params["essence_type"] = EssenceType.from_str(get_str(row, "root_type"))
params["essence_density"] = get_int(row, "root_density")
elif type_tag == "sect":
sect_id = get_int(row, "sect_id")
params["sect_id"] = sect_id
# 直接从已加载的 sects_by_id 中获取宗门对象
# 如果找不到对应的 sect_id默认使用驻地名称作为兜底防止崩溃但正常情况下应该能找到
sect_obj = sects_by_id.get(sect_id)
if sect_obj:
params["sect_name"] = sect_obj.name
else:
params["sect_name"] = get_str(row, "name")
# 实例化
try:
region_obj = cls(**params)
game_map.regions[rid] = region_obj
game_map.region_names[region_obj.name] = region_obj
# 写入 Map 缓存 (region_cors)
game_map.region_cors[rid] = cors
# 绑定到 Tiles
for rx, ry in cors:
if game_map.is_in_bounds(rx, ry):
game_map.tiles[(rx, ry)].region = region_obj
except Exception as e:
print(f"Error creating region {rid}: {e}")
# 执行加载
process_region_config(game_configs["normal_region"], NormalRegion, "normal")
process_region_config(game_configs["city_region"], CityRegion, "city")
process_region_config(game_configs["cultivate_region"], CultivateRegion, "cultivate")
process_region_config(game_configs["sect_region"], SectRegion, "sect")
def _parse_list(s: str) -> list[int]:
if not s: return []
res = []
for x in s.split(","):
x = x.strip()
if x:
try:
res.append(int(float(x)))
except (ValueError, TypeError):
pass
return res