from common import os, json, uuid, deepcopy, dataclass, field from common import pygame, pygame_gui import eb_objects import eb_terrain_objects import eb_creature_objects #from pympler import muppy, summary import gc, psutil, os cell_classes = {"grass_small": eb_terrain_objects.Ground, "sword_default": eb_objects.Item, "elf_watching": eb_creature_objects.Unit, "rock_small": eb_terrain_objects.Rock} main_dir = os.path.dirname(os.path.abspath(__file__)) sprites_dir = os.path.join(main_dir, "res", "sprites") #class Render #class MapManager #class Event #class EventManager @dataclass class Cell: terrain_obj: None = None item_obj: None = None creature_obj: None = None is_target: bool = False @dataclass class Map: name: str sprites: dict sprites_refresh: int = 60 cells: dict = field(default_factory = dict) color: str = "gray57" target_color: str = "gold" size: int = 150 bord: int = 3 scale: float = 1 cam_x: int = 0 cam_y: int = 0 cell_dist: int = 1 #action_time_multiplier def __post_init__(self): self.cells = {} with open(self.name, 'r') as file: buff = json.load(file) for line in range(len(buff)): self.cells[line] = [] for cell in buff[str(line)]: final_cell = Cell(cell_classes[cell["terrain_obj"]["sprite_name"]](**cell["terrain_obj"])) if cell["item_obj"]: final_cell.item_obj = cell_classes[cell["item_obj"]["sprite_name"]](**cell["item_obj"]) if cell["creature_obj"]: final_cell.creature_obj = cell_classes[cell["creature_obj"]["sprite_name"]](**cell["creature_obj"]) self.cells[line].append(final_cell) def move_obj(self, type, s_x, s_y, d_x, d_y): if d_y >= len(self.cells) or d_x >= len(self.cells[s_y]) or s_y >= len(self.cells) or s_x >= len(self.cells[s_y]): return False source_cell = self.cells[s_y][s_x] dest_cell = self.cells[d_y][d_x] obj = getattr(source_cell, type) if obj is None: return False setattr(source_cell, type, None) setattr(dest_cell, type, obj) return True def get_way(self, s_x, s_y, d_x, d_y): pass def draw_map(self, screen, current_frame, grid = True): for j in range(len(self.cells)): for i, cell in enumerate(self.cells[j]): dd = {"x": int((i * self.size + self.cam_x) * self.scale), "y": int((j * self.size + self.cam_y) * self.scale), "w": int(self.size * self.scale - self.cell_dist), "h": int(self.size * self.scale - self.cell_dist), "spr_up": current_frame % self.sprites_refresh, "sprites": self.sprites, "scale": self.scale, "screen": screen} cell.terrain_obj.draw(dd) if cell.item_obj: cell.item_obj.draw(dd) if cell.creature_obj: cell.creature_obj.draw(dd) if grid: pygame.draw.rect(screen, self.color, pygame.Rect(dd["x"], dd["y"], dd["w"], dd["h"]), self.bord) @dataclass class Engine: sprites: dict = field(default_factory = dict) cached_sprites: dict = field(default_factory = dict) screen: pygame.Surface = ((1, 1)) width: int = 1600 height: int = 800 camera_step: int = 10 scale_step: float = 0.01 spr_scale: float = 1 def __post_init__(self): self.sprites = {} pygame.init() pygame.display.set_caption('Elvenbane') self.screen = pygame.display.set_mode((self.width, self.height), pygame.HWSURFACE | pygame.DOUBLEBUF) print("The engine has started. Sprites were successfully loaded.\n") self.load_sprites() print("Sprites were successfully loaded.\n") self.cached_sprites = deepcopy(self.sprites) print("Sprites were successfully cached.\n") def load_sprites(self, folder_path = sprites_dir): self.sprites = {} files = [f for f in os.listdir(folder_path) if f.lower().endswith('.png')] #TOTAL SLOR - REWRITE THIS FUNC PLS groups = {} for f in files: name = os.path.splitext(f)[0] if '_' in name and name.rsplit('_', 1)[0].count('_') >= 1: prefix = name.rsplit('_', 1)[0] num = int(name.rsplit('_', 1)[1]) groups.setdefault(prefix, []).append((num, f)) for prefix, items in groups.items(): items.sort() self.sprites[prefix] = [ pygame.image.load(os.path.join(folder_path, f)).convert_alpha() for num, f in items ] def scale_image(self, image): orig_size = image.get_size() new_size = (int(orig_size[0] * self.spr_scale), int(orig_size[1] * self.spr_scale)) return pygame.transform.smoothscale(image, new_size) def scale_sprites(self): for sp_name, sprite_list in self.sprites.items(): for i, sprite in enumerate(sprite_list): scaled = self.scale_image(sprite) self.cached_sprites[sp_name][i] = scaled def create_console(self, manager): console_window = pygame_gui.elements.UIWindow( pygame.Rect(100, 100, 400, 300), manager=manager, window_display_title='Console', resizable=True ) input_entry = pygame_gui.elements.UITextEntryLine( relative_rect=pygame.Rect(10, 250, 380, 30), container=console_window, manager=manager ) # ★ UIScrollingContainer ★ scroll_container = pygame_gui.elements.UIScrollingContainer( relative_rect=pygame.Rect(10, 10, 380, 230), container=console_window, manager=manager, allow_scroll_x=False ) # ★ UITextBox ВНУТРИ контейнера ★ output_box = pygame_gui.elements.UITextBox( html_text=">>> hlwrld1\n", relative_rect=pygame.Rect(0, 0, 380, 1000), # ← Увеличьте высоту! container=scroll_container, manager=manager ) scroll_container.set_scrollable_area_dimensions((380, 2000)) return console_window, input_entry, scroll_container, output_box def main_loop(self): easy_map = Map("def_map.json", self.cached_sprites) #sp = eb_objects.Sprite(self.sprites, "elf_watching") #gr = pygame.image.load(file_path).convert_alpha() background = pygame.Surface((1600, 800)) background.fill("chartreuse4") manager = pygame_gui.UIManager((1600, 800)) #hello_button = pygame_gui.elements.UIButton(relative_rect=pygame.Rect((350, 275), (100, 50)), # text='Say Hello', # manager=manager) console_window, input_entry, scroll_container, output_box = self.create_console(manager) output_log = ">>> hlwrld1\n" console_active = False clock = pygame.time.Clock() running = True unlock = True current_frame = 0 max_fps = 60 global_counter = 0 global_counter_cap = 100000 # profiling process = psutil.Process(os.getpid()) gc.collect() mem_before = process.memory_info().rss / 1024 while running: time_delta = clock.tick(60)/1000.0 #pygame.event.clear() if global_counter % 1000 == 0: gc.collect() mem_after = process.memory_info().rss / 1024 print(f"Leak: {mem_after - mem_before:.1f} KB per 1000 frames") # poll for events # pygame.QUIT event means the user clicked X to close your window for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame_gui.UI_TEXT_ENTRY_FINISHED and event.ui_element == input_entry: user_text = input_entry.get_text() exec(user_text) if user_text.strip(): output_log += f">>> {user_text}\n" output_box.set_text(output_log) input_entry.set_text("") user_text = "" console_active = False console_active = bool(input_entry.get_text().strip()) if event.type == pygame.VIDEORESIZE: new_size = event.size self.screen = pygame.display.set_mode(new_size, pygame.RESIZABLE) manager.clear_and_set_new_size((new_size[0], new_size[1])) # ★ ПЕРЕСОЗДАНИЕ ★ console_window.kill() input_entry.kill() scroll_container.kill() output_box.kill() console_window, input_entry, scroll_container, output_box = self.create_console(manager) manager.process_events(event) manager.update(time_delta) # fill the screen with a color to wipe away anything from last frame #self.screen.fill("chartreuse4") self.screen.blit(background, (0, 0)) easy_map.draw_map(self.screen, current_frame + 1) manager.draw_ui(self.screen) if not console_active: keys = pygame.key.get_pressed() if keys[pygame.K_w]: easy_map.cam_y += self.camera_step if keys[pygame.K_s]: easy_map.cam_y -= self.camera_step if keys[pygame.K_a]: easy_map.cam_x += self.camera_step if keys[pygame.K_d]: easy_map.cam_x -= self.camera_step if keys[pygame.K_q]: easy_map.scale += self.scale_step self.spr_scale += self.scale_step self.scale_sprites() if keys[pygame.K_e] and easy_map.scale >= self.scale_step: easy_map.scale -= self.scale_step self.spr_scale -= self.scale_step self.scale_sprites() if keys[pygame.K_ESCAPE]: running = False current_frame = (current_frame + 1) % max_fps if global_counter < global_counter_cap: global_counter += 1 else: global_counter = 0 # flip() the display to put your work on screen pygame.display.update() if global_counter % 10 == 0: current_fps = clock.get_fps() #print(f"Current FPS: {current_fps:.2f}") pygame.quit()