147 lines
5.3 KiB
Python
147 lines
5.3 KiB
Python
"""Map renderer for the Village Simulation."""
|
|
|
|
import pygame
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from frontend.client import SimulationState
|
|
|
|
|
|
# Color palette
|
|
class Colors:
|
|
# Background colors
|
|
DAY_BG = (180, 200, 160) # Soft green for day
|
|
NIGHT_BG = (40, 45, 60) # Dark blue for night
|
|
GRID_LINE = (120, 140, 110) # Subtle grid lines
|
|
GRID_LINE_NIGHT = (60, 65, 80)
|
|
|
|
# Terrain features (for visual variety)
|
|
GRASS_LIGHT = (160, 190, 140)
|
|
GRASS_DARK = (140, 170, 120)
|
|
WATER_SPOT = (100, 140, 180)
|
|
|
|
|
|
class MapRenderer:
|
|
"""Renders the map/terrain background."""
|
|
|
|
def __init__(
|
|
self,
|
|
screen: pygame.Surface,
|
|
map_rect: pygame.Rect,
|
|
world_width: int = 20,
|
|
world_height: int = 20,
|
|
):
|
|
self.screen = screen
|
|
self.map_rect = map_rect
|
|
self.world_width = world_width
|
|
self.world_height = world_height
|
|
self._cell_width = map_rect.width / world_width
|
|
self._cell_height = map_rect.height / world_height
|
|
|
|
# Pre-generate some terrain variation
|
|
self._terrain_cache = self._generate_terrain()
|
|
|
|
def _generate_terrain(self) -> list[list[int]]:
|
|
"""Generate simple terrain variation (0 = light, 1 = dark, 2 = water)."""
|
|
import random
|
|
terrain = []
|
|
for y in range(self.world_height):
|
|
row = []
|
|
for x in range(self.world_width):
|
|
# Simple pattern: mostly grass with occasional water spots
|
|
if random.random() < 0.05:
|
|
row.append(2) # Water spot
|
|
elif (x + y) % 3 == 0:
|
|
row.append(1) # Dark grass
|
|
else:
|
|
row.append(0) # Light grass
|
|
terrain.append(row)
|
|
return terrain
|
|
|
|
def update_dimensions(self, world_width: int, world_height: int) -> None:
|
|
"""Update world dimensions and recalculate cell sizes."""
|
|
if world_width != self.world_width or world_height != self.world_height:
|
|
self.world_width = world_width
|
|
self.world_height = world_height
|
|
self._cell_width = self.map_rect.width / world_width
|
|
self._cell_height = self.map_rect.height / world_height
|
|
self._terrain_cache = self._generate_terrain()
|
|
|
|
def grid_to_screen(self, grid_x: int, grid_y: int) -> tuple[int, int]:
|
|
"""Convert grid coordinates to screen coordinates (center of cell)."""
|
|
screen_x = self.map_rect.left + (grid_x + 0.5) * self._cell_width
|
|
screen_y = self.map_rect.top + (grid_y + 0.5) * self._cell_height
|
|
return int(screen_x), int(screen_y)
|
|
|
|
def get_cell_size(self) -> tuple[int, int]:
|
|
"""Get the size of a single cell."""
|
|
return int(self._cell_width), int(self._cell_height)
|
|
|
|
def draw(self, state: "SimulationState") -> None:
|
|
"""Draw the map background."""
|
|
is_night = state.time_of_day == "night"
|
|
|
|
# Fill background
|
|
bg_color = Colors.NIGHT_BG if is_night else Colors.DAY_BG
|
|
pygame.draw.rect(self.screen, bg_color, self.map_rect)
|
|
|
|
# Draw terrain cells
|
|
for y in range(self.world_height):
|
|
for x in range(self.world_width):
|
|
cell_rect = pygame.Rect(
|
|
self.map_rect.left + x * self._cell_width,
|
|
self.map_rect.top + y * self._cell_height,
|
|
self._cell_width + 1, # +1 to avoid gaps
|
|
self._cell_height + 1,
|
|
)
|
|
|
|
terrain_type = self._terrain_cache[y][x]
|
|
|
|
if is_night:
|
|
# Darker colors at night
|
|
if terrain_type == 2:
|
|
color = (60, 80, 110)
|
|
elif terrain_type == 1:
|
|
color = (35, 40, 55)
|
|
else:
|
|
color = (45, 50, 65)
|
|
else:
|
|
if terrain_type == 2:
|
|
color = Colors.WATER_SPOT
|
|
elif terrain_type == 1:
|
|
color = Colors.GRASS_DARK
|
|
else:
|
|
color = Colors.GRASS_LIGHT
|
|
|
|
pygame.draw.rect(self.screen, color, cell_rect)
|
|
|
|
# Draw grid lines
|
|
grid_color = Colors.GRID_LINE_NIGHT if is_night else Colors.GRID_LINE
|
|
|
|
# Vertical lines
|
|
for x in range(self.world_width + 1):
|
|
start_x = self.map_rect.left + x * self._cell_width
|
|
pygame.draw.line(
|
|
self.screen,
|
|
grid_color,
|
|
(start_x, self.map_rect.top),
|
|
(start_x, self.map_rect.bottom),
|
|
1,
|
|
)
|
|
|
|
# Horizontal lines
|
|
for y in range(self.world_height + 1):
|
|
start_y = self.map_rect.top + y * self._cell_height
|
|
pygame.draw.line(
|
|
self.screen,
|
|
grid_color,
|
|
(self.map_rect.left, start_y),
|
|
(self.map_rect.right, start_y),
|
|
1,
|
|
)
|
|
|
|
# Draw border
|
|
border_color = (80, 90, 70) if not is_night else (80, 85, 100)
|
|
pygame.draw.rect(self.screen, border_color, self.map_rect, 2)
|
|
|