Browse Source

Implement marine unit visibility and movement logic; add map loading from JSON

master
Matteo Benedetto 7 months ago
parent
commit
20bb62ee97
  1. 42
      Entities/Units/marine.py
  2. 15
      Entities/entity.py
  3. 10
      assets/maps/map.json
  4. 6
      conf/keybinding_paused.json
  5. 6
      conf/keybinding_start_menu.json
  6. 7
      conf/keymap_game.json
  7. 58
      engine_demo.py
  8. 6
      enne2engine/controls.py
  9. 46
      enne2engine/isogeometry.py
  10. 43
      enne2engine/sdl2_wrapper.py
  11. 132
      sdl2_wrapper.py

42
Entities/Units/marine.py

@ -3,23 +3,45 @@ from Entities.entity import Entity
class Marine(Entity): class Marine(Entity):
next_cell = (1,1) target_cell = (8,7)
movement = 0 movement = 0
view = 3
def update(self): def update(self):
self.move() self.move()
super().update() 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): def select_unit(self):
self.selected = True self.selected = True
# Play a random voice response when selected # 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}") print(f"Playing sound: {sound_file}")
self.graphics.play_sound(sound_file) self.graphics.play_sound(sound_file)
def move(self): 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 # Set walking animation and direction
self.action = "walk" self.action = "walk"
self.direction = self.graphics.get_direction((self.x, self.y), self.next_cell) 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]) target_x, target_y = self.graphics.iso_transform(self.next_cell[0], self.next_cell[1])
# Increment movement counter # Increment movement counter
self.movement += 0.01 self.movement += 0.1
# Calculate how far we've moved (0.0 to 1.0) # Calculate how far we've moved (0.0 to 1.0)
move_progress = min(self.movement, 1.0) move_progress = min(self.movement, 1.0)
# Calculate new position based on progress between cells # Calculate new position based on progress between cells
self.iso_x = self.iso_x + (target_x - self.iso_x) * move_progress 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 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: if self.movement >= 1.0:
# Reset movement and set to idle # Reset movement and set to idle
self.movement = 0 self.movement = 0
self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y) 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")

15
Entities/entity.py

@ -5,6 +5,7 @@ class Entity:
self.graphics = engine.graphics self.graphics = engine.graphics
self.x = x self.x = x
self.y = y self.y = y
self.next_cell = (x, y)
self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y) self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y)
self.action = action self.action = action
self.direction = direction self.direction = direction
@ -15,15 +16,19 @@ class Entity:
self.movement = 0 self.movement = 0
def update(self): 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 occlusion = self.graphics.get_distance((self.x, self.y), self.engine.cursor_pos) / 4
# Set color based on selection status # Set color based on selection status
color = (255, 255, 0, 255) if self.selected else (0, 255, 0, 255) color = (0, 255, 0, 255)
self.graphics.draw_square(self.x, self.y, color=color, margin=4) 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: if occlusion >= 0.8:
return return
self.frame = self.graphics.render_sprite(f"{self.asset}_{self.action}_dir{self.direction}", self.iso_x, self.iso_y, self.frame, occlusion) self.frame = self.graphics.render_sprite(f"{self.asset}_{self.action}_dir{self.direction}", self.iso_x, self.iso_y, self.frame, occlusion)

10
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" }]
]

6
conf/keybinding_paused.json

@ -0,0 +1,6 @@
{
"reset_game": ["Return", 13],
"pause": ["P", 16],
"quit": ["Q", 12]
}

6
conf/keybinding_start_menu.json

@ -0,0 +1,6 @@
{
"start_game": ["Return", 13],
"toggle_full_screen": ["F"],
"quit": ["Q", 12]
}

7
conf/keymap_game.json

@ -0,0 +1,7 @@
{
"keydown:up": "scroll_up",
"keydown:down": "scroll_down",
"keydown:left": "scroll_left",
"keydown:right": "scroll_right"
}

58
engine_demo.py

@ -3,26 +3,24 @@ from enne2engine.pyglet_wrapper import PygletWrapper
from enne2engine.controls import UserControls from enne2engine.controls import UserControls
import sys import sys
import os import os
import json
from Entities.Units.marine import Marine from Entities.Units.marine import Marine
class GameEngine(UserControls): class GameEngine(UserControls):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
if "--pyglet" in sys.argv: self.graphics = SDL2Wrapper(self)
self.graphics = PygletWrapper()
else: # Load map from JSON file
self.graphics = SDL2Wrapper(self) try:
self.map = [ with open("assets/maps/map.json", "r") as map_file:
[{ '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" }], self.map = json.load(map_file)
[{ '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" }], except (FileNotFoundError, json.JSONDecodeError) as e:
[{ '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" }], print(f"Error loading map file: {e}")
[{ '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" }], print("Exiting program.")
[{ '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" }], sys.exit(0)
[{ '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.frame_time = 0 self.frame_time = 0
self.cursor_pos = (0, 0) self.cursor_pos = (0, 0)
@ -33,32 +31,48 @@ class GameEngine(UserControls):
def run(self): def run(self):
running = True running = True
# Set a custom scale if needed # 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.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: while running:
# Start the frame timer
perf_counter = self.graphics.get_perf_counter() 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: for entity in self.entities:
self.entities_positions[entity.next_cell] = entity
self.entities_positions[(entity.x, entity.y)] = 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() event = self.graphics.handle_events()
if event: if event:
#print(f"Event detected: {event}") #print(f"Event detected: {event}")
if event.startswith("MOUSEDOWN"): if event.startswith("MOUSEDOWN"):
pass 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"): elif event.startswith("SELECTION"):
# Handle multiple unit selection # Handle multiple unit selection
self.select_units_in_area(event) self.select_units_in_area(event)
elif event.startswith("MOUSEUP"): elif event.startswith("MOUSEUP"):
self.select_entity_at_cursor() if event[-1] == "1":
self.select_entity_at_cursor()
self.handle_events("keymap_game", event) self.handle_events("keymap_game", event)
running = False if event == "QUIT" else True running = False if event == "QUIT" else True
self.graphics.clear_screen() self.graphics.clear_screen()
self.graphics.render_background() 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() self.cursor_pos = self.graphics.draw_cursor()
# Draw the selection rectangle if selecting # Draw the selection rectangle if selecting
if self.graphics.is_mouse_button_pressed(1): if self.graphics.is_mouse_button_pressed(1):
self.graphics.draw_selection_rectangle() self.graphics.draw_selection_rectangle()

6
enne2engine/controls.py

@ -23,16 +23,12 @@ class UserControls:
def scroll_up(self): def scroll_up(self):
self.graphics.view_offset_y += 10 self.graphics.view_offset_y += 10
self.graphics.create_background(self.map, "tiles")
def scroll_down(self): def scroll_down(self):
self.graphics.view_offset_y -= 10 self.graphics.view_offset_y -= 10
self.graphics.create_background(self.map, "tiles")
def scroll_left(self): def scroll_left(self):
self.graphics.view_offset_x += 10 self.graphics.view_offset_x += 10
self.graphics.create_background(self.map, "tiles")
def scroll_right(self): def scroll_right(self):
self.graphics.view_offset_x -= 10 self.graphics.view_offset_x -= 10
self.graphics.create_background(self.map, "tiles")

46
enne2engine/isogeometry.py

@ -50,13 +50,44 @@ class IsometricGeometry:
for delta_x in [-1, 0, 1]: for delta_x in [-1, 0, 1]:
for delta_y in [-1, 0, 1]: for delta_y in [-1, 0, 1]:
new_x, new_y = x_coord + delta_x, y_coord + delta_y 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 # Check all eight possible directions
if delta_x == 0 and delta_y == 0: for delta_x in [-1, 0, 1]:
continue 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 # Check if the new coordinates are within the battlefield
if 0 <= new_x < self.width and 0 <= new_y < self.height: 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)) neighbors.append((new_x, new_y))
return neighbors return neighbors
@ -131,4 +162,11 @@ class IsometricGeometry:
visited.add((nx, ny)) visited.add((nx, ny))
steps += 1 steps += 1
return -1 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

43
enne2engine/sdl2_wrapper.py

@ -25,11 +25,11 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
self.base_cell_size = 132 # Original/base cell size self.base_cell_size = 132 # Original/base cell size
self.scaling_factor = 0.5 # Default scaling factor (can be changed) 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.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.view_offset_y = 0
self.surface_width = 0 self.surface_width = 0
self.surface_height = 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.window.show() # Mostra la finestra
self.renderer = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED) self.renderer = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED)
self.tile_managers = {} self.tile_managers = {}
@ -74,16 +74,16 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
elif event.type == sdl2.SDL_MOUSEBUTTONDOWN: elif event.type == sdl2.SDL_MOUSEBUTTONDOWN:
x, y = event.button.x, event.button.y x, y = event.button.x, event.button.y
# Left mouse button (button 1) # Left mouse button (button 1)
self.selection_start = (x, y)
self.selection_current = (x, y)
if event.button.button == 1: if event.button.button == 1:
self.selection_start = (x, y)
self.selection_current = (x, y)
self.selecting = True self.selecting = True
return f"MOUSEDOWN:{x}:{y}" return f"MOUSEDOWN:{x}:{y}:{event.button.button}"
elif event.type == sdl2.SDL_MOUSEBUTTONUP: elif event.type == sdl2.SDL_MOUSEBUTTONUP:
x, y = event.button.x, event.button.y x, y = event.button.x, event.button.y
# Left mouse button (button 1) # Left mouse button (button 1)
if event.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 # 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: 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}" 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) sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)
return (frame + 1) % total_frames 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 # Set page as render target and initialize it
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.tiles_texture) sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.tiles_texture)
sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, 0, 0, 0, 255) # Green background sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, 0, 0, 0, 255) # Green background
sdl2.SDL_RenderClear(self.renderer.sdlrenderer) 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() tilesheet_texture = self.tile_managers[spritesheet_name].get_tilesheet_texture()
def get_shadow(x, y): def get_shadow(x, y):
if (x, y) == self.engine.cursor_pos: return min(self.map_shadow[y][x], 1)
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
def blit_tile(tile, x, y): def blit_tile(tile, x, y):
tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile) 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)) 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 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) 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 # Apply margin to reduce the size of the square
adjusted_size = self.cell_size - margin*2 adjusted_size = self.cell_size - margin*2
@ -304,9 +293,6 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
""" """
self.scaling_factor = factor self.scaling_factor = factor
self.cell_size = int(self.base_cell_size * self.scaling_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): def play_sound(self, sound_file):
""" """
@ -388,4 +374,7 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
# Check if the requested button is pressed # Check if the requested button is pressed
if button in button_masks: if button in button_masks:
return bool(button_state & button_masks[button]) return bool(button_state & button_masks[button])
return False return False
def set_opacity(self, x, y, opacity):
self.engine.map_shadow[y][x] = max(self.engine.map_shadow[y][x], opacity)

132
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()
Loading…
Cancel
Save