Browse Source

Refactor code structure for improved readability and maintainability

master
Matteo Benedetto 7 months ago
parent
commit
882a4aac62
  1. 38
      .gitignore
  2. 0
      Entities/Units/__init__.py
  3. 43
      Entities/Units/marine.py
  4. 0
      Entities/__init__.py
  5. 13
      Entities/entity.py
  6. BIN
      assets/Sounds/marine/tmadth00.wav
  7. BIN
      assets/Sounds/marine/tmadth01.wav
  8. BIN
      assets/Sounds/marine/tmapss01.wav
  9. BIN
      assets/Sounds/marine/tmapss02.wav
  10. BIN
      assets/Sounds/marine/tmapss03.wav
  11. BIN
      assets/Sounds/marine/tmapss04.wav
  12. BIN
      assets/Sounds/marine/tmapss05.wav
  13. BIN
      assets/Sounds/marine/tmapss06.wav
  14. BIN
      assets/Sounds/marine/tmasti00.wav
  15. BIN
      assets/Sounds/marine/tmasti01.wav
  16. BIN
      assets/Sounds/marine/tmawht00.wav
  17. BIN
      assets/Sounds/marine/tmawht01.wav
  18. BIN
      assets/Sounds/marine/tmawht02.wav
  19. BIN
      assets/Sounds/marine/tmawht03.wav
  20. BIN
      assets/Sounds/marine/tmayes00.wav
  21. BIN
      assets/Sounds/marine/tmayes01.wav
  22. BIN
      assets/Sounds/marine/tmayes02.wav
  23. BIN
      assets/Sounds/marine/tmayes03.wav
  24. 92
      engine_demo.py
  25. 87
      enne2engine/engine_demo.py
  26. 11
      enne2engine/isogeometry.py
  27. 322
      enne2engine/sdl2_wrapper.py

38
.gitignore vendored

@ -1,2 +1,36 @@
.venv
__pycache__
# Virtual Environment
.venv/
venv/
ENV/
# Python cache files
__pycache__/
*.py[cod]
*$py.class
.pytest_cache/
# Distribution / packaging
dist/
build/
*.egg-info/
*.egg
# IDE specific files
.idea/
.vscode/
*.swp
*.swo
# OS specific files
.DS_Store
Thumbs.db
# Logs
*.log
logs/
# Local configuration
.env
.env.local
*.local.py
engine_demo

0
enne2engine/Entities/__init__.py → Entities/Units/__init__.py

43
Entities/Units/marine.py

@ -0,0 +1,43 @@
import random
from Entities.entity import Entity
class Marine(Entity):
next_cell = (1,1)
movement = 0
def update(self):
self.move()
super().update()
def select_unit(self):
self.selected = True
# Play a random voice response when selected
sound_file = f"marine/tmawht0{random.randint(0, 4)}.wav"
print(f"Playing sound: {sound_file}")
self.graphics.play_sound(sound_file)
def move(self):
if (self.x, self.y) != self.next_cell:
# Set walking animation and direction
self.action = "walk"
self.direction = self.graphics.get_direction((self.x, self.y), self.next_cell)
self.moving = True
# Calculate target coordinates
target_x, target_y = self.graphics.iso_transform(self.next_cell[0], self.next_cell[1])
# Increment movement counter
self.movement += 0.01
# 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)

0
enne2engine/Entities/unit.py → Entities/__init__.py

13
enne2engine/Entities/entity.py → Entities/entity.py

@ -2,21 +2,28 @@ class Entity:
def __init__(self, asset, x, y, action, direction, speed, engine):
self.asset = asset
self.graphics = engine.graphics
self.x = x
self.y = y
self.iso_x, self.iso_y = self.graphics.iso_transform(self.x, self.y)
self.action = action
self.direction = direction
self.speed = speed
self.frame = 0
self.graphics = engine.graphics
self.engine = engine
self.selected = True
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)
if occlusion >= 0.8:
return
self.frame = self.graphics.render_sprite(f"{self.asset}_{self.action}_dir{self.direction}", x,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)

BIN
assets/Sounds/marine/tmadth00.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmadth01.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmapss01.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmapss02.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmapss03.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmapss04.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmapss05.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmapss06.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmasti00.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmasti01.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmawht00.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmawht01.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmawht02.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmawht03.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmayes00.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmayes01.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmayes02.wav

Binary file not shown.

BIN
assets/Sounds/marine/tmayes03.wav

Binary file not shown.

92
engine_demo.py

@ -3,8 +3,8 @@ from enne2engine.pyglet_wrapper import PygletWrapper
from enne2engine.controls import UserControls
import sys
import os
from Entities.Units.marine import Marine
from enne2engine.Entities.entity import Entity
class GameEngine(UserControls):
def __init__(self):
@ -12,14 +12,14 @@ class GameEngine(UserControls):
if "--pyglet" in sys.argv:
self.graphics = PygletWrapper()
else:
self.graphics = SDL2Wrapper()
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_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" }]
]
@ -28,51 +28,52 @@ class GameEngine(UserControls):
self.load_assets()
self.entities = []
self.entities_positions = {}
def run(self):
running = True
self.entities.append(Entity("knight", 0, 0, "idle", 1, 1, self))
self.graphics.create_background(self.map, "tiles")
# Set a custom scale if needed
self.graphics.set_scaling_factor(0.5) # 50% scale
self.entities.append(Marine("knight", 0, 0, "idle", 1, 1, self))
self.graphics.create_background(self.map, "tiles")
while running:
perf_counter = self.graphics.get_perf_counter()
for entity in self.entities:
self.entities_positions[(entity.x, entity.y)] = entity
self.graphics.create_background(self.map, "tiles")
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.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()
# 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 render_background(self):
for y, row in enumerate(self.map):
for x, cell in enumerate(row):
cell["occlusion"] = self.get_occlusion(x, y)
if cell['occlusion']==1:
continue
self.graphics.render_tile("tiles", cell, x, y)
def get_occlusion(self, x, y):
if (x, y) == self.cursor_pos:
return 0
distance = self.graphics.get_distance((x, y), self.cursor_pos)
if distance <2:
return 0.2
elif distance < 3:
return 0.5
else:
return 0.8
def load_assets(self):
self.graphics.load_tilesheet("tiles", "assets/tiles/landscapeTiles_sheet.png")
for dir in os.listdir("assets/KnightBasic"):
@ -80,7 +81,46 @@ class GameEngine(UserControls):
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()

87
enne2engine/engine_demo.py

@ -1,87 +0,0 @@
from sdl2_wrapper import SDL2Wrapper
from pyglet_wrapper import PygletWrapper
from controls import UserControls
import sys
import os
from Entities.entity import Entity
class GameEngine(UserControls):
def __init__(self):
super().__init__()
if "--pyglet" in sys.argv:
self.graphics = PygletWrapper()
else:
self.graphics = SDL2Wrapper()
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_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': False, 'tile': "landscapeTiles_067" }, { 'wall': True, 'tile': "landscapeTiles_066" }]
]
self.frame_time = 0
self.cursor_pos = (0, 0)
self.load_assets()
self.entities = []
def run(self):
running = True
self.graphics.create_background(self.map, "tiles")
self.entities.append(Entity("knight", 0, 0, "idle", 1, 1, self))
while running:
perf_counter = self.graphics.get_perf_counter()
event = self.graphics.handle_events()
if event:
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()
for entity in self.entities:
entity.update()
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 render_background(self):
for y, row in enumerate(self.map):
for x, cell in enumerate(row):
cell["occlusion"] = self.get_occlusion(x, y)
if cell['occlusion']==1:
continue
self.graphics.render_tile("tiles", cell, x, y)
def get_occlusion(self, x, y):
if (x, y) == self.cursor_pos:
return 0
distance = self.graphics.get_distance((x, y), self.cursor_pos)
if distance <2:
return 0.2
elif distance < 3:
return 0.5
else:
return 0.8
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}")
if __name__ == "__main__":
engine = GameEngine()
engine.run()

11
enne2engine/isogeometry.py

@ -83,8 +83,15 @@ class IsometricGeometry:
return screen_x, screen_y
def inv_iso_transform(self, screen_x, screen_y):
x = (2 * screen_y + screen_x - 2 * self.view_offset_x) // self.cell_size - 1
y = (2 * screen_y - screen_x + 2 * self.view_offset_x) // self.cell_size
# Adjust screen coordinates by removing view offsets
adj_x = screen_x - self.view_offset_x
adj_y = screen_y - self.view_offset_y
# Inverse isometric transformation formulas:
# Convert adjusted screen coordinates back to grid/world coordinates
x = (adj_x + 2 * adj_y) // self.cell_size
y = (2 * adj_y - adj_x) // self.cell_size
#print(f"adj_x: {adj_x}, adj_y: {adj_y}, x: {x}, y: {y}")
return x, y
def get_distance(self, position1, position2):

322
enne2engine/sdl2_wrapper.py

@ -1,22 +1,32 @@
import random
import math
import os
from math import sqrt
import ctypes
import sdl2
import sdl2.ext
import sdl2.sdlmixer as sdlmixer
from .tilemanager import TileManager
from .spritemanager import SpriteManager
from .isogeometry import IsometricGeometry
from .sdl2_utils.gui import SDL2Gui
class SDL2Wrapper(IsometricGeometry, SDL2Gui):
def __init__(self):
def __init__(self, engine):
self.engine = engine
sdl2.ext.init()
# Initialize SDL2 mixer
sdlmixer.Mix_Init(sdlmixer.MIX_INIT_OGG | sdlmixer.MIX_INIT_MP3)
sdlmixer.Mix_OpenAudio(44100, sdlmixer.MIX_DEFAULT_FORMAT, 2, 1024)
sdlmixer.Mix_AllocateChannels(16) # Allocate channels for multiple sounds
self.view_size = (800, 600)
self.target_size = (800, 600)
self.cell_size = 132
self.view_offset_x = 400
self.view_offset_y = 200
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_y = 0
self.surface_width = 0
self.surface_height = 0
self.window = sdl2.ext.Window("My Game", size=self.view_size)
@ -27,7 +37,21 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
self.factory = sdl2.ext.SpriteFactory(renderer=self.renderer)
self.fonts = self.generate_fonts("assets/decterm.ttf")
self.cursor = (0, 0)
# Add selection rectangle tracking variables
self.selection_start = None
self.selection_current = None
self.selecting = False
self.tiles_texture = sdl2.SDL_CreateTexture(self.renderer.renderer,
sdl2.SDL_PIXELFORMAT_RGBA8888,
sdl2.SDL_TEXTUREACCESS_TARGET,
self.view_size[0],
self.view_size[1])
self.sprite_texture = sdl2.SDL_CreateTexture(self.renderer.renderer,
sdl2.SDL_PIXELFORMAT_RGBA8888,
sdl2.SDL_TEXTUREACCESS_TARGET,
self.view_size[0],
self.view_size[1])
sdl2.SDL_SetTextureBlendMode(self.sprite_texture, sdl2.SDL_BLENDMODE_BLEND)
def get_perf_counter(self):
return int(sdl2.SDL_GetPerformanceCounter())
@ -43,10 +67,43 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
return f"KEYDOWN:{key}".lower()
elif event.type == sdl2.SDL_MOUSEMOTION:
x, y = event.motion.x, event.motion.y
# Update current selection point if dragging
if self.selecting:
self.selection_current = (x, y)
return f"MOUSEMOTION:{x}:{y}"
elif event.type == sdl2.SDL_MOUSEBUTTONDOWN:
x, y = event.button.x, event.button.y
# Left mouse button (button 1)
if event.button.button == 1:
self.selection_start = (x, y)
self.selection_current = (x, y)
self.selecting = True
return f"MOUSEDOWN:{x}:{y}"
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}"
# 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}"
# Always reset selection state on mouse up, regardless of selecting flag
self.selecting = False
self.selection_start = None
self.selection_current = None
return result
return f"MOUSEUP:{x}:{y}"
return False
def clear_screen(self, color=(0, 0, 0, 255)): # Aggiunto valore alfa di default
sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, *color)
# Clear sprite texture with transparent color
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.sprite_texture)
sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, 0, 0, 0, 0) # Transparent color (alpha=0)
sdl2.SDL_RenderClear(self.renderer.sdlrenderer)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)
# Return to main renderer with the specified color
sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, *color)
sdl2.SDL_RenderClear(self.renderer.sdlrenderer)
@ -54,12 +111,15 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
sdl2.SDL_RenderPresent(self.renderer.sdlrenderer)
def quit(self):
# Clean up mixer resources
sdlmixer.Mix_CloseAudio()
sdlmixer.Mix_Quit()
sdl2.SDL_Quit()
def load_spritesheet(self, name, path):
surface = sdl2.ext.load_image(path.replace('.json', '.png'))
texture = sdl2.ext.Texture(self.renderer, surface)
self.sprite_managers[name] = SpriteManager(path, surface, texture, self.cell_size)
self.sprite_managers[name] = SpriteManager(path, surface, texture, self.base_cell_size)
def load_tilesheet(self, name, path):
surface = sdl2.ext.load_image(path)
@ -72,80 +132,86 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
def render_sprite(self, name, x, y, frame, occlusion=0):
srcrect, total_frames = self.sprite_managers[name].get_frame_rect(frame)
x -= self.cell_size // 2
y -= self.cell_size // 4
if occlusion:
original_color_mods_r_g_b_a = self.apply_texture_color_mdod(self.sprite_managers[name].spritesheet_texture, occlusion)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.sprite_texture)
self.renderer.copy(self.sprite_managers[name].spritesheet_texture,
dstrect=(x, y, self.cell_size, self.cell_size),
srcrect=srcrect)
if occlusion:
sdl2.SDL_SetTextureColorMod(self.sprite_managers[name].spritesheet_texture.tx,
*original_color_mods_r_g_b_a)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)
return (frame + 1) % total_frames
def render_tile(self, spritesheet_name, cell, x, y):
tile_name = cell.get('tile')
occlusion = cell.get('occlusion', 0)
texture = self.tile_managers[spritesheet_name].get_tilesheet_texture()
tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile_name)
if tile_rect is not None:
iso_x, iso_y = self.iso_transform(x, y)
y_offset = self.cell_size - tile_rect[3]
dst_rect = (iso_x, iso_y + y_offset,
tile_rect[2], tile_rect[3])
# Salva i valori originali dei moduli di colore
original_color_mods_r_g_b_a = self.apply_texture_color_mdod(texture, occlusion)
# Copia la texture con il nuovo modulo di colore
self.renderer.copy(self.tile_managers[spritesheet_name].get_tilesheet_texture(),
sdl2.SDL_Rect(*tile_rect),
sdl2.SDL_Rect(*dst_rect))
sdl2.SDL_SetTextureColorMod(texture.tx, *original_color_mods_r_g_b_a)
def create_background(self, map, spritesheet_name):
# 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)
def render_tile_shadow(self, spritesheet_name, cell, x, y):
tile_name = cell.get('tile')
occlusion = cell.get('occlusion', 0)
tilesheet_texture = self.tile_managers[spritesheet_name].get_tilesheet_texture()
def create_background(self, map, spritesheet_name):
self.surface_width = round(self.cell_size * len(map[0])*sqrt(2))
self.surface_height = round(self.cell_size//2 * len(map)*sqrt(2))
tilesheet_surface = self.tile_managers[spritesheet_name].get_tilesheet_surface()
bg_surface = sdl2.SDL_CreateRGBSurface(0, self.surface_width, self.surface_height, 32, 0, 0, 0, 0)
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
def blit_tile(tile, x, y):
tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile)
if tile_rect is not None:
vertical_offset = self.cell_size - tile_rect[3]
horizontal_offset = (self.cell_size - tile_rect[2])
shadow = get_shadow(x, y)
if shadow==1:
return
self.apply_texture_color_mdod(tilesheet_texture, shadow)
# Adjusted vertical offset calculation for half-sized cells
vertical_offset = self.cell_size - tile_rect[3] // 2 - self.cell_size//4
if vertical_offset < 0:
vertical_offset = 0
# Adjusted horizontal offset calculation for half-sized cells
horizontal_offset = (self.cell_size - tile_rect[2] // 2)
iso_x, iso_y = self.iso_transform(x, y)
iso_y += vertical_offset
iso_x += horizontal_offset
iso_x += horizontal_offset - tile_rect[2] // 4 # Divided by 4 instead of 2
# Scale the actual tile rectangle based on the current scaling factor
dst_rect = (iso_x, iso_y,
tile_rect[2], tile_rect[3])
sdl2.SDL_BlitSurface(tilesheet_surface,
sdl2.SDL_Rect(*tile_rect),
bg_surface,
sdl2.SDL_Rect(*dst_rect))
#sdl2.SDL_FillRect(bg_surface, sdl2.SDL_Rect(*dst_rect), sdl2.SDL_MapRGB(bg_surface.contents.format, 0, 0, 255))
int(tile_rect[2] * self.scaling_factor),
int(tile_rect[3] * self.scaling_factor))
sdl2.SDL_RenderCopy(self.renderer.sdlrenderer, tilesheet_texture.tx,
sdl2.SDL_Rect(*tile_rect),
sdl2.SDL_Rect(*dst_rect))
for y, row in enumerate(map):
for x, cell in enumerate(row):
tile_name = cell.get('tile')
blit_tile(tile_name, x, y)
# Reset render target to the default (window)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)
self.background_texture = self.factory.from_surface(bg_surface)
self.apply_texture_color_mdod(self.background_texture, 0.8)
def render_texture_to_texture(self, src_texture, dst_texture, srcrect, dstrect):
self.renderer.copy(src_texture, srcrect, dstrect)
def render_background(self):
self.renderer.copy(self.background_texture,
dstrect=(0, 0, self.surface_width, self.surface_height),
srcrect=(0,0, self.surface_width, self.surface_height))
sdl2.SDL_RenderCopy(self.renderer.sdlrenderer, self.tiles_texture, None, None)
def render_sprites(self):
sdl2.SDL_RenderCopy(self.renderer.sdlrenderer, self.sprite_texture, None, None)
def get_frame_time(self, perf_counter):
return (sdl2.SDL_GetPerformanceCounter() - perf_counter) / sdl2.SDL_GetPerformanceFrequency() *1000
@ -160,17 +226,39 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
def draw_cursor(self):
x, y = self.cursor
x += self.view_offset_x
y -= self.view_offset_y
c_x, c_y = self.inv_iso_transform(x, y)
#print(f"Cursor: {c_x}, {c_y}")
iso_x, iso_y = self.iso_transform(c_x, c_y)
iso_y += self.cell_size // 2
self.renderer.draw_line(points=[(iso_x, iso_y), (iso_x + self.cell_size//2, iso_y + self.cell_size//4)], color=(255, 0, 0, 255))
self.renderer.draw_line(points=[(iso_x + self.cell_size//2, iso_y + self.cell_size//4), (iso_x + self.cell_size, iso_y)], color=(255, 0, 0, 255))
self.renderer.draw_line(points=[(iso_x + self.cell_size, iso_y), (iso_x + self.cell_size//2, iso_y - self.cell_size//4)], 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))
self.renderer.draw_line(points=[(iso_x, iso_y), (iso_x + self.cell_size//2, iso_y + self.cell_size//4)], 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 + self.cell_size//2)], color=(255, 0, 0, 255))
self.renderer.draw_line(points=[(iso_x, iso_y + self.cell_size//2), (iso_x - self.cell_size//2, iso_y + self.cell_size//4)], 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
def draw_square(self, x, 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
# Calculate half-sizes with margin applied
half_width = adjusted_size // 2
half_height = adjusted_size // 4
# Draw an isometric square with margin
self.renderer.draw_line(points=[(iso_x, iso_y + margin),
(iso_x + half_width-margin, iso_y + half_height +margin//2)], color=color)
self.renderer.draw_line(points=[(iso_x + half_width-margin, iso_y + half_height +margin//2),
(iso_x, iso_y + half_height*2+margin//4)], color=color)
self.renderer.draw_line(points=[(iso_x, iso_y + half_height*2+margin//4),
(iso_x - half_width+margin, iso_y + half_height +margin//2)], color=color)
self.renderer.draw_line(points=[(iso_x - half_width+margin, iso_y + half_height +margin//2),
(iso_x, iso_y + margin)], color=color)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)
def apply_texture_color_mdod(self, texture, occlusion):
color_mod_r = int(255 * (1 - occlusion))
color_mod_g = int(255 * (1 - occlusion))
@ -193,9 +281,111 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
color_mod_b)
return original_color_mods_r_g_b_a
def render_tile_shadow(self, x, y, darkness=0):
iso_x, iso_y = self.iso_transform(x, y)
iso_y += self.cell_size // 2
self.renderer.copy(self.shadow_texture,
dstrect=(iso_x, iso_y, self.cell_size, self.cell_size),
srcrect=(0, 0, self.cell_size, self.cell_size))
def render_tile(self, spritesheet_name, x, y, tile):
tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile)
if tile_rect is not None:
vertical_offset = self.cell_size - tile_rect[3]
horizontal_offset = (self.cell_size - tile_rect[2])
iso_x, iso_y = self.iso_transform(x, y)
iso_y += vertical_offset - tile_rect[3]//4
iso_x += horizontal_offset - tile_rect[2] // 2
dst_rect = (iso_x, iso_y,
tile_rect[2], tile_rect[3])
sdl2.SDL_RenderCopy(self.renderer.sdlrenderer, self.tile_managers[spritesheet_name].get_tilesheet_texture().tx,
sdl2.SDL_Rect(*tile_rect),
sdl2.SDL_Rect(*dst_rect))
def set_scaling_factor(self, factor):
"""
Set a new scaling factor and recalculate cell_size.
Args:
factor (float): The scaling factor (0.5 = half size, 1.0 = original size, etc.)
"""
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):
"""
Play a sound file using SDL2 mixer.
Args:
sound_file (str): Name of the sound file to play
"""
sound_path = os.path.join("assets", "Sounds", sound_file)
if os.path.exists(sound_path):
sound = sdlmixer.Mix_LoadWAV(sound_path.encode('utf-8'))
if sound:
channel = sdlmixer.Mix_PlayChannel(-1, sound, 0)
if channel == -1:
print(f"Error playing sound: {sdlmixer.Mix_GetError().decode('utf-8')}")
# Free the sound when finished (after a delay or in a callback)
# For simplicity, we're not handling this here, but in a real app you might want to
else:
print(f"Sound file not found: {sound_path}")
def draw_selection_rectangle(self):
"""Draw the selection rectangle if currently selecting."""
# Only draw if we're in a valid selection state
if not self.selecting or not self.selection_start or not self.selection_current:
return
# Get coordinates for the rectangle
start_x, start_y = self.selection_start
current_x, current_y = self.selection_current
# Calculate the rectangle dimensions
x = min(start_x, current_x)
y = min(start_y, current_y)
width = abs(current_x - start_x)
height = abs(current_y - start_y)
# Only draw if we have a meaningful selection area
if width > 1 and height > 1:
# Draw an empty rectangle in green
self.draw_rectangle(x, y, width, height, "selection", outline=(0, 255, 0), filling=None)
def get_selection_size(self):
"""Get the size (diagonal length) of the selection rectangle."""
if not self.selection_start or not self.selection_current:
return 0
start_x, start_y = self.selection_start
current_x, current_y = self.selection_current
# Calculate diagonal length using Pythagorean theorem
dx = current_x - start_x
dy = current_y - start_y
return int(math.sqrt(dx*dx + dy*dy))
def is_mouse_button_pressed(self, button=1):
"""
Check if a specific mouse button is currently pressed.
Args:
button (int): Button number (1=left, 2=middle, 3=right)
Returns:
bool: True if the button is pressed, False otherwise
"""
# Create ctypes variables to store the mouse position
x = ctypes.c_int(0)
y = ctypes.c_int(0)
# Get the current mouse state (returns a bitmask of buttons)
button_state = sdl2.SDL_GetMouseState(ctypes.byref(x), ctypes.byref(y))
# Map button numbers to SDL constants
button_masks = {
1: sdl2.SDL_BUTTON_LMASK, # Left button
2: sdl2.SDL_BUTTON_MMASK, # Middle button
3: sdl2.SDL_BUTTON_RMASK, # Right button
}
# Check if the requested button is pressed
if button in button_masks:
return bool(button_state & button_masks[button])
return False
Loading…
Cancel
Save