This commit is contained in:
bridge
2025-08-23 21:45:05 +08:00
parent f72bccf0d3
commit a94ea2bd8b
10 changed files with 353 additions and 56 deletions

View File

@@ -6,6 +6,7 @@ from src.sim.simulator import Simulator
from src.classes.world import World
from src.classes.tile import TileType
from src.classes.avatar import Avatar, Gender
from src.sim.event import Event
class Front:
@@ -33,6 +34,7 @@ class Front:
step_interval_ms: int = 400,
window_title: str = "Cultivation World Simulator",
font_path: Optional[str] = None,
sidebar_width: int = 300, # 新增:侧边栏宽度
):
self.world = world
self.simulator = simulator
@@ -41,10 +43,12 @@ class Front:
self.step_interval_ms = step_interval_ms
self.window_title = window_title
self.font_path = font_path
self.sidebar_width = sidebar_width # 新增:侧边栏宽度
# 运行时状态
self._auto_step = True
self._last_step_ms = 0
self.events: List[Event] = [] # 新增:存储事件历史
# 初始化pygame
import pygame
@@ -52,8 +56,8 @@ class Front:
pygame.init()
pygame.font.init()
# 计算窗口大小
width_px = world.map.width * tile_size + margin * 2
# 计算窗口大小(包含侧边栏)
width_px = world.map.width * tile_size + margin * 2 + sidebar_width
height_px = world.map.height * tile_size + margin * 2
self.screen = pygame.display.set_mode((width_px, height_px))
pygame.display.set_caption(window_title)
@@ -61,6 +65,8 @@ class Front:
# 字体和缓存
self.font = self._create_font(16)
self.tooltip_font = self._create_font(14)
self.sidebar_font = self._create_font(12) # 新增:侧边栏字体
self.status_font = self._create_font(18) # 新增:状态栏字体(更大更清晰)
self._region_font_cache: Dict[int, object] = {}
# 配色方案
@@ -71,6 +77,12 @@ class Front:
"tooltip_bg": (32, 32, 32),
"tooltip_bd": (90, 90, 90),
"avatar": (240, 220, 90),
"sidebar_bg": (25, 25, 25), # 新增:侧边栏背景色
"sidebar_border": (60, 60, 60), # 新增:侧边栏边框色
"event_text": (200, 200, 200), # 新增:事件文字色
"status_bg": (15, 15, 15), # 新增:状态栏背景色(深色)
"status_border": (50, 50, 50), # 新增:状态栏边框色
"status_text": (220, 220, 220), # 新增:状态栏文字色(亮色)
}
# 加载tile图像
@@ -79,6 +91,13 @@ class Front:
self.clock = pygame.time.Clock()
def add_events(self, new_events: List[Event]):
"""新增:添加新事件到事件历史"""
self.events.extend(new_events)
# 保持最多1000个事件避免内存占用过大
if len(self.events) > 1000:
self.events = self.events[-1000:]
def run(self):
"""主循环"""
pygame = self.pygame
@@ -110,7 +129,9 @@ class Front:
def _step_once(self):
"""执行一步模拟"""
self.simulator.step()
events = self.simulator.step() # 获取返回的事件
if events: # 新增:将事件添加到事件历史
self.add_events(events)
self._last_step_ms = 0
def _render(self):
@@ -134,27 +155,74 @@ class Front:
# 状态信息
self._draw_status_bar()
self._draw_date_info()
# 新增:绘制侧边栏
self._draw_sidebar()
pygame.display.flip()
def _draw_status_bar(self):
"""绘制状态栏"""
hint = f"A:自动步进({'' if self._auto_step else ''}) SPACE:单步 ESC:退出"
text_surf = self.font.render(hint, True, self.colors["text"])
self.screen.blit(text_surf, (self.margin, 4))
def _draw_date_info(self):
"""绘制日期信息"""
"""绘制状态栏 - 包含操作指南和年月信息"""
pygame = self.pygame
# 状态栏配置
status_y = 8
status_height = 32
padding = 8
# 绘制状态栏背景
status_rect = pygame.Rect(0, 0, self.screen.get_width(), status_height)
pygame.draw.rect(self.screen, self.colors["status_bg"], status_rect)
pygame.draw.line(self.screen, self.colors["status_border"],
(0, status_height), (self.screen.get_width(), status_height), 2)
# 1. 绘制操作指南
self._draw_operation_guide(status_y, padding)
# 2. 绘制年月信息
self._draw_year_month_info(status_y, padding)
def _draw_operation_guide(self, y_pos: int, padding: int):
"""绘制操作指南"""
# 构建操作指南文本
auto_status = "" if self._auto_step else ""
guide_text = f"A:自动步进({auto_status}) SPACE:单步 ESC:退出"
# 渲染文本
guide_surf = self.status_font.render(guide_text, True, self.colors["status_text"])
# 绘制文本
x_pos = self.margin + padding
self.screen.blit(guide_surf, (x_pos, y_pos))
# 保存操作指南的宽度,供年月信息定位使用
self._guide_width = guide_surf.get_width()
def _draw_year_month_info(self, y_pos: int, padding: int):
"""绘制年月信息"""
# 获取年月数据
year = int(self.simulator.year)
month_num = self._get_month_number()
# 构建年月文本
ym_text = f"{year}{month_num:02d}"
# 渲染文本
ym_surf = self.status_font.render(ym_text, True, self.colors["status_text"])
# 计算位置:放在操作指南右边,留适当间距
x_pos = self.margin + self._guide_width + padding * 3
self.screen.blit(ym_surf, (x_pos, y_pos))
def _get_month_number(self) -> int:
"""获取月份数字"""
try:
month_num = list(type(self.simulator.month)).index(self.simulator.month) + 1
return month_num
except Exception:
month_num = 1
ym_text = f"{int(self.simulator.year)}{month_num:02d}"
ym_surf = self.font.render(ym_text, True, self.colors["text"])
screen_w, _ = self.screen.get_size()
self.screen.blit(ym_surf, (screen_w - self.margin - ym_surf.get_width(), 4))
return 1
def _draw_map(self):
"""绘制地图"""
@@ -285,7 +353,7 @@ class Front:
hovered = None
min_dist = float("inf")
for avatar in self.simulator.avatars:
for avatar_id, avatar in self.simulator.avatars.items():
cx, cy = self._avatar_center_pixel(avatar)
radius = max(8, self.tile_size // 3)
@@ -394,17 +462,9 @@ class Front:
image_path = f"assets/tiles/{tile_type.value}.png"
if os.path.exists(image_path):
try:
image = pygame.image.load(image_path)
scaled_image = pygame.transform.scale(image, (self.tile_size, self.tile_size))
self.tile_images[tile_type] = scaled_image
print(f"已加载tile图像: {image_path}")
except Exception as e:
print(f"加载tile图像失败 {image_path}: {e}")
self._create_fallback_surface(tile_type)
else:
print(f"tile图像文件不存在: {image_path}")
self._create_fallback_surface(tile_type)
image = pygame.image.load(image_path)
scaled_image = pygame.transform.scale(image, (self.tile_size, self.tile_size))
self.tile_images[tile_type] = scaled_image
def _create_fallback_surface(self, tile_type):
"""创建默认的fallback surface"""
@@ -445,6 +505,61 @@ class Front:
# 退回默认字体
return pygame.font.SysFont(None, size)
def _draw_sidebar(self):
"""新增:绘制侧边栏"""
pygame = self.pygame
# 计算侧边栏位置
sidebar_x = self.world.map.width * self.tile_size + self.margin * 2
sidebar_y = self.margin
# 绘制侧边栏背景
sidebar_rect = pygame.Rect(sidebar_x, sidebar_y, self.sidebar_width,
self.screen.get_height() - self.margin * 2)
pygame.draw.rect(self.screen, self.colors["sidebar_bg"], sidebar_rect)
pygame.draw.rect(self.screen, self.colors["sidebar_border"], sidebar_rect, 2)
# 绘制标题
title_text = "事件历史"
title_surf = self.sidebar_font.render(title_text, True, self.colors["text"])
title_x = sidebar_x + 10
title_y = sidebar_y + 10
self.screen.blit(title_surf, (title_x, title_y))
# 绘制分隔线
line_y = title_y + title_surf.get_height() + 10
pygame.draw.line(self.screen, self.colors["sidebar_border"],
(sidebar_x + 10, line_y),
(sidebar_x + self.sidebar_width - 10, line_y), 1)
# 绘制事件列表
event_y = line_y + 15
max_events = (self.screen.get_height() - event_y - self.margin) // 20 # 每行20像素
# 显示最近的事件(从最新开始)
recent_events = self.events[-max_events:] if len(self.events) > max_events else self.events
for event in reversed(recent_events): # 最新的在顶部
event_text = str(event)
# 如果文本太长,截断它
if len(event_text) > 35: # 大约35个字符
event_text = event_text[:32] + "..."
event_surf = self.sidebar_font.render(event_text, True, self.colors["event_text"])
self.screen.blit(event_surf, (title_x, event_y))
event_y += 20
# 如果超出显示区域,停止绘制
if event_y > self.screen.get_height() - self.margin:
break
# 如果没有事件,显示提示信息
if not self.events:
no_event_text = "暂无事件"
no_event_surf = self.sidebar_font.render(no_event_text, True, self.colors["event_text"])
self.screen.blit(no_event_surf, (title_x, event_y))
__all__ = ["Front"]