update map
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 49 KiB |
@@ -7,7 +7,7 @@ from src.classes.avatar import Avatar, Gender
|
||||
|
||||
from .theme import COLORS
|
||||
from .fonts import create_font, get_region_font as _get_region_font_cached
|
||||
from .assets import load_tile_images, load_avatar_images, load_sect_images
|
||||
from .assets import load_tile_images, load_avatar_images, load_sect_images, load_region_images
|
||||
from .rendering import (
|
||||
draw_map,
|
||||
draw_region_labels,
|
||||
@@ -65,6 +65,7 @@ class Front:
|
||||
|
||||
self.tile_images = load_tile_images(self.pygame, self.tile_size)
|
||||
self.sect_images = load_sect_images(self.pygame, self.tile_size)
|
||||
self.region_images = load_region_images(self.pygame, self.tile_size)
|
||||
self.male_avatars, self.female_avatars = load_avatar_images(self.pygame, self.tile_size)
|
||||
self.avatar_images: Dict[str, object] = {}
|
||||
self._assign_avatar_images()
|
||||
@@ -132,8 +133,9 @@ class Front:
|
||||
self.margin,
|
||||
STATUS_BAR_HEIGHT,
|
||||
)
|
||||
# 底图后叠加宗门总部图层(2x2)
|
||||
from .rendering import draw_sect_headquarters
|
||||
# 底图后叠加小区域整图(2x2/3x3),再绘制宗门总部,避免被覆盖
|
||||
from .rendering import draw_sect_headquarters, draw_small_regions
|
||||
draw_small_regions(pygame, self.screen, self.world, self.region_images, self.tile_images, self.tile_size, self.margin, STATUS_BAR_HEIGHT)
|
||||
draw_sect_headquarters(pygame, self.screen, self.world, self.sect_images, self.tile_size, self.margin, STATUS_BAR_HEIGHT)
|
||||
hovered_region = draw_region_labels(
|
||||
pygame,
|
||||
|
||||
@@ -70,6 +70,34 @@ def load_sect_images(pygame_mod, tile_size: int):
|
||||
return images
|
||||
|
||||
|
||||
__all__ = ["load_tile_images", "load_avatar_images", "load_sect_images"]
|
||||
def load_region_images(pygame_mod, tile_size: int) -> Dict[str, Dict[int, object]]:
|
||||
"""
|
||||
加载小区域整图:按名称加载 assets/regions/<name>.png。
|
||||
为兼容 2x2 和 3x3,分别生成两种缩放版本:
|
||||
- key 2 -> (tile_size*2, tile_size*2)
|
||||
- key 3 -> (tile_size*3, tile_size*3)
|
||||
返回结构: { name: {2: surf2x2, 3: surf3x3} }
|
||||
"""
|
||||
results: Dict[str, Dict[int, object]] = {}
|
||||
base_dir = Path("assets/regions")
|
||||
if base_dir.exists():
|
||||
for filename in base_dir.iterdir():
|
||||
if filename.suffix.lower() != ".png" or filename.name == "original.png":
|
||||
continue
|
||||
try:
|
||||
image = pygame_mod.image.load(str(filename))
|
||||
except pygame_mod.error:
|
||||
continue
|
||||
name_key = filename.stem
|
||||
variants: Dict[int, object] = {}
|
||||
for n in (2, 3):
|
||||
w = tile_size * n
|
||||
h = tile_size * n
|
||||
variants[n] = pygame_mod.transform.scale(image, (w, h))
|
||||
results[name_key] = variants
|
||||
return results
|
||||
|
||||
|
||||
__all__ = ["load_tile_images", "load_avatar_images", "load_sect_images", "load_region_images"]
|
||||
|
||||
|
||||
|
||||
@@ -66,10 +66,72 @@ def draw_sect_headquarters(pygame_mod, screen, world, sect_images: dict, ts: int
|
||||
screen.blit(image, (x_px, y_px))
|
||||
|
||||
|
||||
def _is_small_square_region(region) -> int:
|
||||
"""
|
||||
若为 2x2 或 3x3 的矩形/正方形区域,返回边长(2或3);否则返回0。
|
||||
"""
|
||||
try:
|
||||
nw = tuple(map(int, str(getattr(region, "north_west_cor", "0,0")).split(",")))
|
||||
se = tuple(map(int, str(getattr(region, "south_east_cor", "0,0")).split(",")))
|
||||
except Exception:
|
||||
return 0
|
||||
if getattr(region, "shape", None) is None:
|
||||
return 0
|
||||
shape_name = getattr(region.shape, "name", "")
|
||||
if shape_name not in ("RECTANGLE", "SQUARE"):
|
||||
return 0
|
||||
width = se[0] - nw[0] + 1
|
||||
height = se[1] - nw[1] + 1
|
||||
if width == height and width in (2, 3):
|
||||
return width
|
||||
return 0
|
||||
|
||||
|
||||
def draw_small_regions(pygame_mod, screen, world, region_images: dict, tile_images: dict, ts: int, m: int, top_offset: int = 0):
|
||||
"""
|
||||
使用整图绘制 2x2 / 3x3 的小区域:
|
||||
- 优先按名称从 region_images 中取 n×n 的整图(n 为 2 或 3)
|
||||
- 若没有整图,则将现有 tile 图裁切/合成为一张,避免重复边框
|
||||
"""
|
||||
for region in world.map.regions.values():
|
||||
n = _is_small_square_region(region)
|
||||
if n == 0:
|
||||
continue
|
||||
# 仅对 2x2 生效;3x3 不覆盖(保持每格一张图)
|
||||
if n != 2:
|
||||
continue
|
||||
try:
|
||||
nw = tuple(map(int, str(getattr(region, "north_west_cor", "0,0")).split(",")))
|
||||
except Exception:
|
||||
continue
|
||||
x_px = m + nw[0] * ts
|
||||
y_px = m + top_offset + nw[1] * ts
|
||||
name_key = str(getattr(region, "name", ""))
|
||||
variants = region_images.get(name_key)
|
||||
if variants and variants.get(n):
|
||||
screen.blit(variants[n], (x_px, y_px))
|
||||
continue
|
||||
# 回退:直接将该区域左上角 tile 的贴图放大为 n×n 覆盖(只用一张图,而不是四/九张)
|
||||
try:
|
||||
tile = world.map.get_tile(nw[0], nw[1])
|
||||
base_image = tile_images.get(tile.type)
|
||||
except Exception:
|
||||
base_image = None
|
||||
if base_image is not None:
|
||||
scaled = pygame_mod.transform.scale(base_image, (ts * n, ts * n))
|
||||
screen.blit(scaled, (x_px, y_px))
|
||||
else:
|
||||
# 最后兜底:淡色块
|
||||
tmp = pygame_mod.Surface((ts * n, ts * n), pygame_mod.SRCALPHA)
|
||||
tmp.fill((255, 255, 255, 24))
|
||||
screen.blit(tmp, (x_px, y_px))
|
||||
|
||||
|
||||
def calculate_font_size_by_area(tile_size: int, area: int) -> int:
|
||||
base = int(tile_size * 1.1)
|
||||
growth = int(max(0, min(24, (area ** 0.5))))
|
||||
return max(16, min(40, base + growth))
|
||||
size = base + growth - 7 # 再降低2个字号
|
||||
return max(16, min(40, size))
|
||||
|
||||
|
||||
def draw_region_labels(pygame_mod, screen, colors, world, get_region_font, tile_size: int, margin: int, top_offset: int = 0, outline_px: int = 2):
|
||||
@@ -101,8 +163,9 @@ def draw_region_labels(pygame_mod, screen, colors, world, get_region_font, tile_
|
||||
name = getattr(region, "name", None)
|
||||
if not name:
|
||||
continue
|
||||
# 以“区域最下缘的中点”为锚点(优先放在区域下方)
|
||||
if getattr(region, "cors", None):
|
||||
# 小区域(面积<=9,例如2x2/3x3)标签放在底部;大区域放在中心
|
||||
use_bottom = getattr(region, "area", 0) <= 9
|
||||
if use_bottom and getattr(region, "cors", None):
|
||||
bottom_y = max(y for _, y in region.cors)
|
||||
xs_on_bottom = [x for x, y in region.cors if y == bottom_y]
|
||||
if xs_on_bottom:
|
||||
@@ -111,14 +174,12 @@ def draw_region_labels(pygame_mod, screen, colors, world, get_region_font, tile_
|
||||
anchor_cx_tile = (left_x + right_x) / 2.0
|
||||
else:
|
||||
anchor_cx_tile = float(region.center_loc[0])
|
||||
screen_cx = int(m + anchor_cx_tile * ts + ts // 2)
|
||||
screen_cy = int(m + top_offset + (bottom_y + 1) * ts + 2)
|
||||
else:
|
||||
# 兜底使用中心点
|
||||
anchor_cx_tile = float(region.center_loc[0])
|
||||
bottom_y = int(region.center_loc[1])
|
||||
|
||||
screen_cx = int(m + anchor_cx_tile * ts + ts // 2)
|
||||
# 锚点Y取区域底边像素的下一行,再加少量间距
|
||||
screen_cy = int(m + top_offset + (bottom_y + 1) * ts + 2)
|
||||
# 居中放置
|
||||
screen_cx = int(m + float(region.center_loc[0]) * ts + ts // 2)
|
||||
screen_cy = int(m + top_offset + float(region.center_loc[1]) * ts)
|
||||
font_size = calculate_font_size_by_area(tile_size, region.area)
|
||||
region_font = get_region_font(font_size)
|
||||
text_surface = region_font.render(str(name), True, colors["text"])
|
||||
|
||||
@@ -82,10 +82,25 @@ def add_sect_headquarters(game_map: Map, enabled_sects: list[Sect]):
|
||||
base_w, base_h = BASE_W, BASE_H
|
||||
size_w = se[0] - nw[0]
|
||||
size_h = se[1] - nw[1]
|
||||
nw_x = max(0, min(game_map.width - 1, _scale_x(nw[0], game_map.width)))
|
||||
nw_y = max(0, min(game_map.height - 1, _scale_y(nw[1], game_map.height)))
|
||||
se_x = max(nw_x, min(game_map.width - 1, nw_x + size_w))
|
||||
se_y = max(nw_y, min(game_map.height - 1, nw_y + size_h))
|
||||
# 初步缩放坐标
|
||||
nw_x = _scale_x(nw[0], game_map.width)
|
||||
nw_y = _scale_y(nw[1], game_map.height)
|
||||
se_x = nw_x + size_w
|
||||
se_y = nw_y + size_h
|
||||
# 边界修正:确保 2x2 或 1x2 等固定尺寸完整在图内
|
||||
if se_x >= game_map.width:
|
||||
shift = se_x - (game_map.width - 1)
|
||||
nw_x -= shift
|
||||
se_x -= shift
|
||||
if se_y >= game_map.height:
|
||||
shift = se_y - (game_map.height - 1)
|
||||
nw_y -= shift
|
||||
se_y -= shift
|
||||
# 最终夹紧
|
||||
nw_x = max(0, min(game_map.width - 1, nw_x))
|
||||
nw_y = max(0, min(game_map.height - 1, nw_y))
|
||||
se_x = max(nw_x, min(game_map.width - 1, se_x))
|
||||
se_y = max(nw_y, min(game_map.height - 1, se_y))
|
||||
region = SectRegion(
|
||||
id=400 + sect.id,
|
||||
name=hq_name,
|
||||
@@ -213,11 +228,11 @@ def _create_2x2_wuxing_caves(game_map: Map):
|
||||
"""创建2*2的五行洞府区域"""
|
||||
# 五行洞府配置:金木水火土
|
||||
wuxing_caves = [
|
||||
{"name": "太白金府", "base_x": _scale_x(26, game_map.width), "base_y": _scale_y(12, game_map.height), "element": EssenceType.GOLD, "description": "青峰山脉深处的金行洞府"},
|
||||
{"name": "太白金府", "base_x": _scale_x(24, game_map.width), "base_y": _scale_y(12, game_map.height), "element": EssenceType.GOLD, "description": "青峰山脉深处的金行洞府"},
|
||||
{"name": "青木洞天", "base_x": _scale_x(48, game_map.width), "base_y": _scale_y(18, game_map.height), "element": EssenceType.WOOD, "description": "青云林海中的木行洞府"},
|
||||
{"name": "玄水秘境", "base_x": _scale_x(67, game_map.width), "base_y": _scale_y(25, game_map.height), "element": EssenceType.WATER, "description": "无边碧海深处的水行洞府"},
|
||||
{"name": "离火洞府", "base_x": _scale_x(48, game_map.width), "base_y": _scale_y(33, game_map.height), "element": EssenceType.FIRE, "description": "炎狱火山旁的火行洞府"},
|
||||
{"name": "厚土玄宫", "base_x": _scale_x(30, game_map.width), "base_y": _scale_y(16, game_map.height), "element": EssenceType.EARTH, "description": "青峰山脉的土行洞府"}
|
||||
{"name": "厚土玄宫", "base_x": _scale_x(32, game_map.width), "base_y": _scale_y(16, game_map.height), "element": EssenceType.EARTH, "description": "青峰山脉的土行洞府"}
|
||||
]
|
||||
|
||||
for cave in wuxing_caves:
|
||||
@@ -281,6 +296,12 @@ def _scale_loaded_regions(game_map: Map) -> None:
|
||||
new_nw_y = max(0, min(height - 1, _scale_y(nw_y, height)))
|
||||
new_se_x = max(new_nw_x, min(width - 1, _scale_x(se_x, width)))
|
||||
new_se_y = max(new_nw_y, min(height - 1, _scale_y(se_y, height)))
|
||||
# 夹紧到地图范围
|
||||
new_nw_x = max(0, min(width - 1, new_nw_x))
|
||||
new_se_x = max(new_nw_x, min(width - 1, new_se_x))
|
||||
new_nw_y = max(0, min(height - 1, new_nw_y))
|
||||
new_se_y = max(new_nw_y, min(height - 1, new_se_y))
|
||||
|
||||
params = {
|
||||
"id": region.id,
|
||||
"name": region.name,
|
||||
|
||||
@@ -150,14 +150,9 @@ def parse_llm_response(res: str) -> dict:
|
||||
pass
|
||||
|
||||
# 3) 整体 json5 兜底
|
||||
try:
|
||||
obj = json5.loads(res)
|
||||
if isinstance(obj, dict):
|
||||
return obj
|
||||
except Exception:
|
||||
pass
|
||||
obj = json5.loads(res)
|
||||
return obj
|
||||
|
||||
raise ValueError("无法从LLM响应中解析出有效的JSON字典对象")
|
||||
|
||||
def get_prompt_and_call_llm(template_path: Path, infos: dict, mode="normal") -> str:
|
||||
"""
|
||||
|
||||
@@ -1,9 +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
|
||||
201,太白金府,青峰山脉深处的金行洞府,金精气凝,刀剑鸣音不绝,乃金系修士的最高圣地。,square,"24,12","25,13",金,10
|
||||
202,青木洞天,青云林海中的木行洞府,生机盎然,灵药遍地,乃木系修士的最高圣地。,square,"48,18","49,19",木,10
|
||||
203,玄水秘境,无边碧海深处的水行洞府,碧波万里,水精凝神,乃水系修士的最高圣地。,square,"67,25","68,26",水,10
|
||||
204,离火洞府,炎狱火山旁的火行洞府,烈焰冲天,真火精纯,乃火系修士的最高圣地。,square,"48,33","49,34",火,10
|
||||
205,厚土玄宫,青峰山脉的土行洞府,厚德载物,山岳共鸣,乃土系修士的最高圣地。,square,"30,16","31,17",土,10
|
||||
205,厚土玄宫,青峰山脉的土行洞府,厚德载物,山岳共鸣,乃土系修士的最高圣地。,square,"32,16","33,17",土,10
|
||||
206,古越遗迹,雨林深处的上古遗迹,古藤缠绕,木行灵气与金行灵气交融。蕴藏古老功法与灵药配方。,square,"25,40","26,41",木,8
|
||||
207,沧海遗迹,沉没在海中的远古文明遗迹,水行灵气浓郁,潮汐间偶有宝物现世。,square,"66,47","67,48",水,9
|
||||
|
||||
|
Reference in New Issue
Block a user