diff --git a/Entities/Units/marine.py b/Entities/Units/marine.py index 2e77c2b..1beb523 100644 --- a/Entities/Units/marine.py +++ b/Entities/Units/marine.py @@ -3,23 +3,45 @@ from Entities.entity import Entity class Marine(Entity): - next_cell = (1,1) + target_cell = (8,7) movement = 0 + view = 3 def update(self): self.move() super().update() + def set_visibility(self): + # Set visibility based on the distance to the cursor + distance = self.graphics.get_distance((self.x, self.y), self.engine.cursor_pos) + if distance < self.view: + self.graphics.set_opacity(self.frame, 1.0) + else: + self.graphics.set_opacity(self.frame, 0.5) + def select_unit(self): self.selected = True # Play a random voice response when selected - sound_file = f"marine/tmawht0{random.randint(0, 4)}.wav" + sound_file = f"marine/tmawht0{random.randint(0, 3)}.wav" print(f"Playing sound: {sound_file}") self.graphics.play_sound(sound_file) def move(self): - if (self.x, self.y) != self.next_cell: + self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y) + self.position = (self.x, self.y) + + if self.target_cell != (self.x, self.y) and self.selected: + target_iso_x, target_iso_y = self.graphics.iso_transform(self.target_cell[0], self.target_cell[1]) + self.graphics.draw_square(target_iso_x, target_iso_y, color=(255, 0, 0, 255), margin=6) + if self.position != self.target_cell: + if self.position == self.next_cell: + self.next_cell = self.graphics.get_next_cell(self.next_cell, self.target_cell) + self.engine.entities_positions[self.next_cell] = self + if self.engine.entities_positions.get(self.target_cell) is not None: + if self.engine.entities_positions.get(self.target_cell) != self: + self.target_cell = self.graphics.find_neighbors(self.target_cell)[0] or self.position + return # Set walking animation and direction self.action = "walk" self.direction = self.graphics.get_direction((self.x, self.y), self.next_cell) @@ -29,15 +51,21 @@ class Marine(Entity): target_x, target_y = self.graphics.iso_transform(self.next_cell[0], self.next_cell[1]) # Increment movement counter - self.movement += 0.01 - + self.movement += 0.1 # Calculate how far we've moved (0.0 to 1.0) move_progress = min(self.movement, 1.0) + # Calculate new position based on progress between cells self.iso_x = self.iso_x + (target_x - self.iso_x) * move_progress self.iso_y = self.iso_y + (target_y - self.iso_y) * move_progress - print(f"Moving to {self.iso_x}, {self.iso_y} with progress {move_progress}") if self.movement >= 1.0: # Reset movement and set to idle self.movement = 0 - self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y) \ No newline at end of file + self.x, self.y = self.next_cell + self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y) + else: + self.action = "idle" + + def set_target_cell(self, target_cell): + self.target_cell = target_cell + self.graphics.play_sound(f"marine/tmayes0{random.randint(0, 3)}.wav") \ No newline at end of file diff --git a/Entities/entity.py b/Entities/entity.py index 5f1f27f..f7526eb 100644 --- a/Entities/entity.py +++ b/Entities/entity.py @@ -5,6 +5,7 @@ class Entity: self.graphics = engine.graphics self.x = x self.y = y + self.next_cell = (x, y) self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y) self.action = action self.direction = direction @@ -15,15 +16,19 @@ class Entity: self.movement = 0 def update(self): - x, y = self.graphics.iso_transform(self.x, self.y) occlusion = self.graphics.get_distance((self.x, self.y), self.engine.cursor_pos) / 4 # Set color based on selection status - color = (255, 255, 0, 255) if self.selected else (0, 255, 0, 255) - self.graphics.draw_square(self.x, self.y, color=color, margin=4) - + color = (0, 255, 0, 255) + if self.selected: + self.graphics.draw_square(self.iso_x, self.iso_y, color=color, margin=4) + + # Draw target indicator if target is set + + + occlusion = self.graphics.map_shadow[self.y][self.x] if occlusion >= 0.8: return self.frame = self.graphics.render_sprite(f"{self.asset}_{self.action}_dir{self.direction}", self.iso_x, self.iso_y, self.frame, occlusion) - \ No newline at end of file + \ No newline at end of file diff --git a/assets/maps/map.json b/assets/maps/map.json new file mode 100644 index 0000000..0416785 --- /dev/null +++ b/assets/maps/map.json @@ -0,0 +1,10 @@ +[ + [{ "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }], + [{ "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }], + [{ "wall": true, "tile": "landscapeTiles_066" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": true, "tile": "landscapeTiles_066" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }], + [{ "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": true, "tile": "landscapeTiles_066" }], + [{ "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }], + [{ "wall": false, "tile": "landscapeTiles_001" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }], + [{ "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }], + [{ "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": false, "tile": "landscapeTiles_067" }, { "wall": true, "tile": "landscapeTiles_066" }] +] diff --git a/conf/keybinding_paused.json b/conf/keybinding_paused.json new file mode 100644 index 0000000..6ee1b3e --- /dev/null +++ b/conf/keybinding_paused.json @@ -0,0 +1,6 @@ +{ + + "reset_game": ["Return", 13], + "pause": ["P", 16], + "quit": ["Q", 12] +} \ No newline at end of file diff --git a/conf/keybinding_start_menu.json b/conf/keybinding_start_menu.json new file mode 100644 index 0000000..e392d3f --- /dev/null +++ b/conf/keybinding_start_menu.json @@ -0,0 +1,6 @@ +{ + + "start_game": ["Return", 13], + "toggle_full_screen": ["F"], + "quit": ["Q", 12] +} \ No newline at end of file diff --git a/conf/keymap_game.json b/conf/keymap_game.json new file mode 100644 index 0000000..ec12464 --- /dev/null +++ b/conf/keymap_game.json @@ -0,0 +1,7 @@ +{ + + "keydown:up": "scroll_up", + "keydown:down": "scroll_down", + "keydown:left": "scroll_left", + "keydown:right": "scroll_right" +} \ No newline at end of file diff --git a/engine_demo.py b/engine_demo.py index 088e70b..0bcbd81 100644 --- a/engine_demo.py +++ b/engine_demo.py @@ -3,26 +3,24 @@ from enne2engine.pyglet_wrapper import PygletWrapper from enne2engine.controls import UserControls import sys import os +import json from Entities.Units.marine import Marine class GameEngine(UserControls): def __init__(self): super().__init__() - if "--pyglet" in sys.argv: - self.graphics = PygletWrapper() - else: - self.graphics = SDL2Wrapper(self) - self.map = [ - [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], - [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], - [{ 'wall': True, 'tile': "landscapeTiles_066" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': True, 'tile': "landscapeTiles_066" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], - [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': True, 'tile': "landscapeTiles_066" }], - [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], - [{ 'wall': False, 'tile': "landscapeTiles_001" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], - [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], - [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': True, 'tile': "landscapeTiles_066" }] - ] + self.graphics = SDL2Wrapper(self) + + # Load map from JSON file + try: + with open("assets/maps/map.json", "r") as map_file: + self.map = json.load(map_file) + except (FileNotFoundError, json.JSONDecodeError) as e: + print(f"Error loading map file: {e}") + print("Exiting program.") + sys.exit(0) + self.frame_time = 0 self.cursor_pos = (0, 0) @@ -33,32 +31,48 @@ class GameEngine(UserControls): def run(self): running = True # Set a custom scale if needed - self.graphics.set_scaling_factor(0.5) # 50% scale + self.graphics.set_scaling_factor(0.50) # 50% scale self.entities.append(Marine("knight", 0, 0, "idle", 1, 1, self)) - self.graphics.create_background(self.map, "tiles") + self.entities.append(Marine("knight", 5, 0, "idle", 1, 1, self)) while running: + # Start the frame timer perf_counter = self.graphics.get_perf_counter() + # Initialize the map shadow and entities positions + self.map_shadow = [ [0 for _ in range(len(self.map[0]))] for _ in range(len(self.map)) ] + self.entities_positions.clear() for entity in self.entities: + self.entities_positions[entity.next_cell] = entity self.entities_positions[(entity.x, entity.y)] = entity - self.graphics.create_background(self.map, "tiles") + # Create the map background texture with tiles + self.graphics.create_background(self.map, "tiles", self.map_shadow) + + # Handle events event = self.graphics.handle_events() if event: #print(f"Event detected: {event}") - if event.startswith("MOUSEDOWN"): - pass + if event.startswith("MOUSEDOWN"): + print(f"Mouse down event: {event}") + print(f"Button pressed: {event[-1]}") + if event[-1] == "3": + print("Right mouse button pressed") + for entity in self.entities: + if entity.selected: + entity.set_target_cell(self.cursor_pos) elif event.startswith("SELECTION"): # Handle multiple unit selection self.select_units_in_area(event) elif event.startswith("MOUSEUP"): - self.select_entity_at_cursor() + if event[-1] == "1": + self.select_entity_at_cursor() self.handle_events("keymap_game", event) + running = False if event == "QUIT" else True + self.graphics.clear_screen() self.graphics.render_background() - #self.graphics.render_tile(spritesheet_name="tiles", tile="landscapeTiles_064", x=0, y=0) - #self.graphics.draw_square(0,0) self.cursor_pos = self.graphics.draw_cursor() + # Draw the selection rectangle if selecting if self.graphics.is_mouse_button_pressed(1): self.graphics.draw_selection_rectangle() diff --git a/enne2engine/controls.py b/enne2engine/controls.py index 5cebbbd..a51522c 100644 --- a/enne2engine/controls.py +++ b/enne2engine/controls.py @@ -23,16 +23,12 @@ class UserControls: def scroll_up(self): self.graphics.view_offset_y += 10 - self.graphics.create_background(self.map, "tiles") def scroll_down(self): self.graphics.view_offset_y -= 10 - self.graphics.create_background(self.map, "tiles") def scroll_left(self): self.graphics.view_offset_x += 10 - self.graphics.create_background(self.map, "tiles") def scroll_right(self): - self.graphics.view_offset_x -= 10 - self.graphics.create_background(self.map, "tiles") \ No newline at end of file + self.graphics.view_offset_x -= 10 \ No newline at end of file diff --git a/enne2engine/isogeometry.py b/enne2engine/isogeometry.py index f94d781..7c345b2 100644 --- a/enne2engine/isogeometry.py +++ b/enne2engine/isogeometry.py @@ -50,13 +50,44 @@ class IsometricGeometry: for delta_x in [-1, 0, 1]: for delta_y in [-1, 0, 1]: new_x, new_y = x_coord + delta_x, y_coord + delta_y + # Check if the new coordinates are within the battlefield + if 0 <= new_x < self.width and 0 <= new_y < self.height: + # Skip the cell itself + if delta_x == 0 and delta_y == 0: + continue + # Skip walls + if self.engine.map[new_y][new_x]["wall"]: + continue + if self.engine.entities_positions.get((new_x, new_y)) is not None: + continue + neighbors.append((new_x, new_y)) + + + return neighbors + + def find_neighbors_at_distance(self, coordinates, distance): + """ + Find and return the cells at a specific distance from the cell at position (coordinates). + """ + neighbors = [] + x_coord, y_coord = coordinates - # Skip the cell itself - if delta_x == 0 and delta_y == 0: - continue + # Check all eight possible directions + for delta_x in [-1, 0, 1]: + for delta_y in [-1, 0, 1]: + new_x, new_y = x_coord + delta_x * distance, y_coord + delta_y * distance # Check if the new coordinates are within the battlefield if 0 <= new_x < self.width and 0 <= new_y < self.height: + # Skip the cell itself + if delta_x == 0 and delta_y == 0: + continue + # Skip walls + if self.engine.map[new_y][new_x]["wall"]: + continue + if self.engine.entities_positions.get((new_x, new_y)) is not None: + continue neighbors.append((new_x, new_y)) + return neighbors @@ -131,4 +162,11 @@ class IsometricGeometry: visited.add((nx, ny)) steps += 1 - return -1 \ No newline at end of file + return -1 + + def get_next_cell(self, start, target): + # Get the neighbors of the start cell + neighbors = self.find_neighbors(start) + # Get the closest neighbor to the target cell + closest_neighbor = self.get_closest_neighbor(neighbors, target) + return closest_neighbor \ No newline at end of file diff --git a/enne2engine/sdl2_wrapper.py b/enne2engine/sdl2_wrapper.py index f532b11..4645c06 100644 --- a/enne2engine/sdl2_wrapper.py +++ b/enne2engine/sdl2_wrapper.py @@ -25,11 +25,11 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): self.base_cell_size = 132 # Original/base cell size self.scaling_factor = 0.5 # Default scaling factor (can be changed) self.cell_size = int(self.base_cell_size * self.scaling_factor) # Effective cell size - self.view_offset_x = 0 + self.view_offset_x = 400 self.view_offset_y = 0 self.surface_width = 0 self.surface_height = 0 - self.window = sdl2.ext.Window("My Game", size=self.view_size) + self.window = sdl2.ext.Window("Enne2EngineDEMO", size=self.view_size) self.window.show() # Mostra la finestra self.renderer = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED) self.tile_managers = {} @@ -74,16 +74,16 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): elif event.type == sdl2.SDL_MOUSEBUTTONDOWN: x, y = event.button.x, event.button.y # Left mouse button (button 1) + self.selection_start = (x, y) + self.selection_current = (x, y) if event.button.button == 1: - self.selection_start = (x, y) - self.selection_current = (x, y) self.selecting = True - return f"MOUSEDOWN:{x}:{y}" + return f"MOUSEDOWN:{x}:{y}:{event.button.button}" elif event.type == sdl2.SDL_MOUSEBUTTONUP: x, y = event.button.x, event.button.y # Left mouse button (button 1) if event.button.button == 1: - result = f"MOUSEUP:{x}:{y}" + result = f"MOUSEUP:{x}:{y}:{event.button.button}" # Return the selection area if it's large enough and we were selecting if self.selecting and self.selection_start and self.get_selection_size() > 5: result = f"SELECTION:{self.selection_start[0]}:{self.selection_start[1]}:{x}:{y}" @@ -146,29 +146,19 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None) return (frame + 1) % total_frames - def create_background(self, map, spritesheet_name): + def create_background(self, map, spritesheet_name, map_shadow): # Set page as render target and initialize it sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.tiles_texture) sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, 0, 0, 0, 255) # Green background sdl2.SDL_RenderClear(self.renderer.sdlrenderer) + self.height = len(map) + self.width = len(map[0]) + self.map_shadow = map_shadow tilesheet_texture = self.tile_managers[spritesheet_name].get_tilesheet_texture() def get_shadow(x, y): - if (x, y) == self.engine.cursor_pos: - return 0 - distance = self.get_distance((x, y), self.engine.cursor_pos) - if distance <2: - self.engine.map[y][x]['visited'] = True - return 0.2 - elif distance < 3: - self.engine.map[y][x]['visited'] = True - return 0.5 - else: - if self.engine.map[y][x].get('visited', False): - return 0.8 - else: - return 1 + return min(self.map_shadow[y][x], 1) def blit_tile(tile, x, y): tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile) @@ -236,9 +226,8 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): self.renderer.draw_line(points=[(iso_x - self.cell_size//2, iso_y + self.cell_size//4), (iso_x, iso_y)], color=(255, 0, 0, 255)) return c_x, c_y - def draw_square(self, x, y, color=(0, 255, 0, 255), margin=6): + def draw_square(self, iso_x, iso_y, color=(0, 255, 0, 255), margin=6): sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.sprite_texture) - iso_x, iso_y = self.iso_transform(x, y) # Apply margin to reduce the size of the square adjusted_size = self.cell_size - margin*2 @@ -304,9 +293,6 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): """ self.scaling_factor = factor self.cell_size = int(self.base_cell_size * self.scaling_factor) - # Regenerate background if needed - if hasattr(self.engine, 'map'): - self.create_background(self.engine.map, "tiles") def play_sound(self, sound_file): """ @@ -388,4 +374,7 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui): # Check if the requested button is pressed if button in button_masks: return bool(button_state & button_masks[button]) - return False \ No newline at end of file + return False + + def set_opacity(self, x, y, opacity): + self.engine.map_shadow[y][x] = max(self.engine.map_shadow[y][x], opacity) \ No newline at end of file diff --git a/sdl2_wrapper.py b/sdl2_wrapper.py new file mode 100644 index 0000000..1e2bb40 --- /dev/null +++ b/sdl2_wrapper.py @@ -0,0 +1,132 @@ +from enne2engine.sdl2_wrapper import SDL2Wrapper +from enne2engine.pyglet_wrapper import PygletWrapper +from enne2engine.controls import UserControls +import sys +import os +import json +from Entities.Units.marine import Marine + + +class GameEngine(UserControls): + def __init__(self): + super().__init__() + self.graphics = SDL2Wrapper(self) + + # Load map from JSON file + try: + with open("assets/maps/map.json", "r") as map_file: + self.map = json.load(map_file) + except (FileNotFoundError, json.JSONDecodeError) as e: + print(f"Error loading map file: {e}") + print("Exiting program.") + sys.exit(0) + + self.frame_time = 0 + self.cursor_pos = (0, 0) + + self.load_assets() + self.entities = [] + self.entities_positions = {} + + def run(self): + running = True + # Set a custom scale if needed + self.graphics.set_scaling_factor(0.75) # 50% scale + + self.entities.append(Marine("knight", 0, 0, "idle", 1, 1, self)) + while running: + # Start the frame timer + perf_counter = self.graphics.get_perf_counter() + # Initialize the map shadow and entities positions + self.map_shadow = [ [0 for _ in range(len(self.map[0]))] for _ in range(len(self.map)) ] + self.entities_positions.clear() + for entity in self.entities: + self.entities_positions[(entity.x, entity.y)] = entity + # Create the map background texture with tiles + self.graphics.create_background(self.map, "tiles", self.map_shadow) + + # Handle events + event = self.graphics.handle_events() + if event: + #print(f"Event detected: {event}") + if event.startswith("MOUSEDOWN"): + pass + elif event.startswith("SELECTION"): + # Handle multiple unit selection + self.select_units_in_area(event) + elif event.startswith("MOUSEUP"): + self.select_entity_at_cursor() + self.handle_events("keymap_game", event) + + running = False if event == "QUIT" else True + + self.graphics.clear_screen() + self.graphics.render_background() + self.cursor_pos = self.graphics.draw_cursor() + + # Draw the selection rectangle if selecting + if self.graphics.is_mouse_button_pressed(1): + self.graphics.draw_selection_rectangle() + for entity in self.entities: + entity.update() + self.graphics.render_sprites() + self.graphics.update_status(f"Frame time: {round(self.frame_time)}ms - FPS: {round(1000/self.frame_time if self.frame_time != 0 else 1)}") + self.graphics.present_renderer() + self.frame_time = self.graphics.get_frame_time(perf_counter) + self.graphics.delay_frame(self.frame_time,50) + self.graphics.quit() + + def set_cursor(self, x, y): + self.graphics.cursor = (x, y) + + def load_assets(self): + self.graphics.load_tilesheet("tiles", "assets/tiles/landscapeTiles_sheet.png") + for dir in os.listdir("assets/KnightBasic"): + for file in os.listdir(f"assets/KnightBasic/{dir}"): + if file.endswith(".json"): + self.graphics.load_spritesheet(file[:-5].lower(), f"assets/KnightBasic/{dir}/{file}") + + def select_entity_at_cursor(self): + cursor_x, cursor_y = self.cursor_pos + print(f"Cursor position: {cursor_x}, {cursor_y}") + # First deselect all entities + for entity in self.entities: + entity.selected = False + + # Then select the entity at cursor position, if any + entity = self.entities_positions.get((cursor_x, cursor_y)) + if entity: + entity.select_unit() + print(f"Selected entity at cursor: {entity.asset} at position {entity.x}, {entity.y}") + else: + print("No entity selected at cursor position.") + + def select_units_in_area(self, selection_event): + """Select all units within the specified selection area.""" + # Parse selection coordinates + _, start_x, start_y, end_x, end_y = selection_event.split(":") + start_x, start_y, end_x, end_y = int(start_x), int(start_y), int(end_x), int(end_y) + + # Calculate selection rectangle in screen coordinates + min_x = min(start_x, end_x) + max_x = max(start_x, end_x) + min_y = min(start_y, end_y) + max_y = max(start_y, end_y) + + # First deselect all entities + for entity in self.entities: + entity.selected = False + + # Select entities within the rectangle + for entity in self.entities: + # Convert entity position to screen coordinates + screen_x, screen_y = self.graphics.iso_transform(entity.x, entity.y) + + # Check if entity is within selection rectangle + if min_x <= screen_x <= max_x and min_y <= screen_y <= max_y: + entity.select_unit() + print(f"Selected entity in area: {entity.asset} at position {entity.x}, {entity.y}") + +if __name__ == "__main__": + engine = GameEngine() + engine.run() \ No newline at end of file