/
sususer
/
ColonyGEN
Обзор
Документация
Войти
/
sususer
/
ColonyGEN
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
client/render/map_renderer.py
434 строки
15 KB
Chekr
f11
03 июн 2026, 13:53
03 июн 2026, 13:53
d260831
Код
Авторство
О чём код?
import pygame import os pygame.font.init() class MapRenderer: def __init__(self, tile_size=16): self.tile_size = tile_size self.camera_x = 0 self.camera_y = 0 self.min_tile_size = 6 self.max_tile_size = 64 self.font = pygame.font.SysFont(None, 14) self.debug = True asset_root = os.path.abspath( os.path.join(os.path.dirname(__file__), "..", "..", "assets") ) base_path = os.path.join(asset_root, "tiles") print("[TILES PATH]", base_path) self.textures = { "plain": pygame.image.load(os.path.join(base_path, "grass.png")).convert_alpha(), "forest": pygame.image.load(os.path.join(base_path, "forest.png")).convert_alpha(), "mountain": pygame.image.load(os.path.join(base_path, "mountain.png")).convert_alpha(), "water": pygame.image.load(os.path.join(base_path, "water.png")).convert_alpha(), "beach": pygame.image.load(os.path.join(base_path, "sand.png")).convert_alpha(), "sand": pygame.image.load(os.path.join(base_path, "sand.png")).convert_alpha(), } self.scaled_textures = {} building_path = os.path.join(asset_root, "buildings") self.building_textures = { "generator": pygame.image.load(os.path.join(building_path, "generator.png")).convert_alpha(), "mine": pygame.image.load(os.path.join(building_path, "mine.png")).convert_alpha(), "researchstation": pygame.image.load(os.path.join(building_path, "station.png")).convert_alpha(), "wire": pygame.image.load(os.path.join(building_path, "wire.png")).convert_alpha(), "bridge": pygame.image.load(os.path.join(building_path, "bridge.png")).convert_alpha(), "bridge_corner": pygame.image.load(os.path.join(building_path, "bridge_corner.png")).convert_alpha(), "bridge_cross": pygame.image.load(os.path.join(building_path, "bridge_cross.png")).convert_alpha(), "bridge_straight": pygame.image.load(os.path.join(building_path, "bridge_straight.png")).convert_alpha(), "bridge_tm": pygame.image.load(os.path.join(building_path, "bridge_tm.png")).convert_alpha(), "road": pygame.image.load(os.path.join(building_path, "road.png")).convert_alpha(), "road_corner": pygame.image.load(os.path.join(building_path, "road_corner.png")).convert_alpha(), "road_cross": pygame.image.load(os.path.join(building_path, "road_cross.png")).convert_alpha(), "road_straight": pygame.image.load(os.path.join(building_path, "road_straight.png")).convert_alpha(), "road_tm": pygame.image.load(os.path.join(building_path, "road_tm.png")).convert_alpha(), "base": pygame.image.load(os.path.join(building_path, "base.png")).convert_alpha(), } self.scaled_buildings = {} self.scaled_tints = {} self.biome_tints = { "dense_forest": (0, 45, 0, 80), "young_forest": (70, 150, 45, 55), "wetland": (20, 95, 85, 65), "grassland": (95, 150, 55, 28), "desert": (220, 175, 70, 70), "coast": (230, 205, 135, 55), "lake": (20, 75, 160, 55), "river": (35, 105, 205, 70), "highland": (95, 95, 95, 50), "rocky_highland": (85, 70, 55, 65), "mountain_range": (45, 45, 45, 75), "colony_core": (255, 255, 255, 25) } def screen_to_world(self, mx, my): x = mx // self.tile_size + self.camera_x y = my // self.tile_size + self.camera_y return x, y def handle_click(self, mx, my, simulation, game_map): if simulation is None: return x, y = self.screen_to_world(mx, my) if not (0 <= y < game_map.height and 0 <= x < game_map.width): return tile = game_map.grid[y][x] if tile.type != "forest": return if not tile.explored: return if simulation.ap < 3: return if tile.resource <= 0: return tile.resource -= 1 simulation.resources += 1 simulation.ap -= 3 def zoom(self, direction): if direction > 0: self.tile_size = min(self.max_tile_size, self.tile_size + 2) else: self.tile_size = max(self.min_tile_size, self.tile_size - 2) self.scaled_textures.clear() self.scaled_buildings.clear() self.scaled_tints.clear() def _get_texture(self, tile_type): key = (tile_type, self.tile_size) if key not in self.scaled_textures: img = self.textures.get(tile_type, self.textures["plain"]) self.scaled_textures[key] = pygame.transform.scale( img, (self.tile_size, self.tile_size) ) return self.scaled_textures[key] def _get_building_texture(self, building_type): key = (building_type, self.tile_size) if key not in self.scaled_buildings: img = self.building_textures.get(building_type) if img is None: return None self.scaled_buildings[key] = pygame.transform.scale( img, (self.tile_size, self.tile_size) ) return self.scaled_buildings[key] def _get_biome_tint(self, biome): color = self.biome_tints.get(biome) if color is None: return None key = (biome, self.tile_size) if key not in self.scaled_tints: surface = pygame.Surface((self.tile_size, self.tile_size), pygame.SRCALPHA) surface.fill(color) self.scaled_tints[key] = surface return self.scaled_tints[key] def _is_path_neighbor(self, game_map, x, y): if not (0 <= y < game_map.height and 0 <= x < game_map.width): return False tile = game_map.grid[y][x] if tile.type == "base": return True building = tile.building if building is None: return False return building.__class__.__name__.lower() in ( "road", "bridge", "mine", "researchstation", "generator" ) def _path_connections(self, game_map, x, y): return { "up": self._is_path_neighbor(game_map, x, y - 1), "right": self._is_path_neighbor(game_map, x + 1, y), "down": self._is_path_neighbor(game_map, x, y + 1), "left": self._is_path_neighbor(game_map, x - 1, y) } def _path_variant(self, connections, prefix): connected = [direction for direction, active in connections.items() if active] if len(connected) >= 4: return f"{prefix}_cross", 0 if len(connected) == 3: if not connections["up"]: return f"{prefix}_tm", 0 if not connections["right"]: return f"{prefix}_tm", 270 if not connections["down"]: return f"{prefix}_tm", 180 return f"{prefix}_tm", 90 if len(connected) == 2: if connections["left"] and connections["right"]: return f"{prefix}_straight", 0 if connections["up"] and connections["down"]: return f"{prefix}_straight", 90 if connections["right"] and connections["down"]: return f"{prefix}_corner", 0 if connections["right"] and connections["up"]: return f"{prefix}_corner", 90 if connections["left"] and connections["up"]: return f"{prefix}_corner", 180 return f"{prefix}_corner", 270 if len(connected) == 1: if connections["up"] or connections["down"]: return f"{prefix}_straight", 90 return f"{prefix}_straight", 0 return prefix, 0 def _get_path_texture(self, building_type, x, y, game_map): if game_map is None: return self._get_building_texture(building_type) texture_name, angle = self._path_variant( self._path_connections(game_map, x, y), building_type ) key = (texture_name, angle, self.tile_size) if key not in self.scaled_buildings: img = self.building_textures.get(texture_name) if img is None: img = self.building_textures.get(building_type) if img is None: return None scaled = pygame.transform.scale(img, (self.tile_size, self.tile_size)) if angle: scaled = pygame.transform.rotate(scaled, angle) self.scaled_buildings[key] = scaled return self.scaled_buildings[key] def _draw_building(self, screen, building, rect, game_map=None, simulation=None): btype = building.__class__.__name__.lower() if btype in ("road", "bridge"): texture = self._get_path_texture(btype, building.x, building.y, game_map) else: texture = self._get_building_texture(btype) if texture: screen.blit(texture, rect) else: pygame.draw.rect(screen, (200, 0, 200), rect) if self.debug: text = self.font.render(btype[:3].upper(), True, (255, 255, 255)) screen.blit(text, (rect.x + 2, rect.y + 2)) if hasattr(building, "energy_debug"): value = building.energy_debug() if value != 0: color = (0, 255, 0) if value > 0 else (255, 80, 80) e_text = self.font.render(f"{value:+}", True, color) screen.blit( e_text, ( rect.x + (rect.width - e_text.get_width()) // 2, rect.y - 20 ) ) if simulation and hasattr(simulation, "energy"): if btype in ("mine", "researchstation"): powered = simulation.energy.is_powered(building) color = (0, 200, 0) if powered else (200, 0, 0) pygame.draw.circle( screen, color, (rect.x + rect.width // 2, rect.y - 6), 2 ) if self.debug and simulation and hasattr(simulation, "is_mine_connected"): if btype == "mine": connected = simulation.is_mine_connected(building) color = (0, 255, 0) if connected else (255, 200, 0) pygame.draw.circle( screen, color, (rect.x + rect.width // 2 + 6, rect.y - 6), 2 ) t = self.font.render("C" if connected else "X", True, color) screen.blit(t, (rect.x + rect.width - 10, rect.y - 10)) def _world_to_screen(self, x, y): sx = (x - self.camera_x) * self.tile_size + self.tile_size // 2 sy = (y - self.camera_y) * self.tile_size + self.tile_size // 2 return sx, sy def _draw_energy_networks(self, screen, simulation): if not hasattr(simulation, "energy"): return for net in simulation.energy.networks: wires = net["wires"] buildings = net["buildings"] balance = net["balance"] for w1 in wires: x1, y1 = self._world_to_screen(w1.x, w1.y) nearest = None best_dist = float("inf") for w2 in wires: if w1 == w2: continue dx = w1.x - w2.x dy = w1.y - w2.y dist = dx * dx + dy * dy if dist < best_dist: best_dist = dist nearest = w2 if nearest: x2, y2 = self._world_to_screen(nearest.x, nearest.y) pygame.draw.line(screen, (80, 180, 255), (x1, y1), (x2, y2), 2) for b in buildings: bx, by = self._world_to_screen(b.x, b.y) nearest = None best_dist = float("inf") for w in wires: dx = b.x - w.x dy = b.y - w.y dist = dx * dx + dy * dy if dist < best_dist: best_dist = dist nearest = w if nearest: wx, wy = self._world_to_screen(nearest.x, nearest.y) pygame.draw.line(screen, (120, 220, 255), (bx, by), (wx, wy), 1) if not wires: continue anchor = wires[0] sx, sy = self._world_to_screen(anchor.x, anchor.y) color = (0, 255, 0) if balance >= 0 else (255, 80, 80) text = self.font.render(f"{balance:+}", True, color) screen.blit(text, (sx - text.get_width() // 2, sy - self.tile_size)) def render(self, screen, game_map, simulation=None): screen_width, screen_height = screen.get_size() tiles_x = screen_width // self.tile_size + 1 tiles_y = screen_height // self.tile_size + 1 buildings_by_pos = {} if simulation: for b in simulation.buildings: buildings_by_pos[(b.y, b.x)] = b for y in range(self.camera_y, self.camera_y + tiles_y): for x in range(self.camera_x, self.camera_x + tiles_x): if 0 <= y < game_map.height and 0 <= x < game_map.width: tile = game_map.grid[y][x] rect = pygame.Rect( (x - self.camera_x) * self.tile_size, (y - self.camera_y) * self.tile_size, self.tile_size, self.tile_size ) screen.blit(self._get_texture(tile.type), rect) tint = self._get_biome_tint(getattr(tile, "biome", "unknown")) if tint: screen.blit(tint, rect) if tile.type == "base": screen.blit(self._get_building_texture("base"), rect) if self.debug: pygame.draw.rect(screen, (255, 255, 255), rect, 2) if (y, x) in buildings_by_pos: self._draw_building( screen, buildings_by_pos[(y, x)], rect, game_map, simulation ) if tile.resource > 0 and (self.debug or tile.explored): text = self.font.render(str(tile.resource), True, (255, 255, 0)) screen.blit(text, (rect.x + 2, rect.y + 2)) if simulation: self._draw_energy_networks(screen, simulation)