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 # Virtual Environment
__pycache__ .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): def __init__(self, asset, x, y, action, direction, speed, engine):
self.asset = asset self.asset = asset
self.graphics = engine.graphics
self.x = x self.x = x
self.y = y self.y = 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
self.speed = speed self.speed = speed
self.frame = 0 self.frame = 0
self.graphics = engine.graphics
self.engine = engine self.engine = engine
self.selected = True
self.movement = 0
def update(self): def update(self):
x, y = self.graphics.iso_transform(self.x, self.y) 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
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: if occlusion >= 0.8:
return 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 from enne2engine.controls import UserControls
import sys import sys
import os import os
from Entities.Units.marine import Marine
from enne2engine.Entities.entity import Entity
class GameEngine(UserControls): class GameEngine(UserControls):
def __init__(self): def __init__(self):
@ -12,14 +12,14 @@ class GameEngine(UserControls):
if "--pyglet" in sys.argv: if "--pyglet" in sys.argv:
self.graphics = PygletWrapper() self.graphics = PygletWrapper()
else: else:
self.graphics = SDL2Wrapper() self.graphics = SDL2Wrapper(self)
self.map = [ 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': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }], [{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, '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': 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': 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_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': False, 'tile': "landscapeTiles_067" }],
[{ 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, 'tile': "landscapeTiles_067" }, { 'wall': False, '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': True, 'tile': "landscapeTiles_066" }]
] ]
@ -28,51 +28,52 @@ class GameEngine(UserControls):
self.load_assets() self.load_assets()
self.entities = [] self.entities = []
self.entities_positions = {}
def run(self): def run(self):
running = True running = True
self.entities.append(Entity("knight", 0, 0, "idle", 1, 1, self)) # Set a custom scale if needed
self.graphics.create_background(self.map, "tiles") 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: while running:
perf_counter = self.graphics.get_perf_counter() 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() event = self.graphics.handle_events()
if event: 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) 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.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
if self.graphics.is_mouse_button_pressed(1):
self.graphics.draw_selection_rectangle()
for entity in self.entities: for entity in self.entities:
entity.update() 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.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.graphics.present_renderer()
self.frame_time = self.graphics.get_frame_time(perf_counter) self.frame_time = self.graphics.get_frame_time(perf_counter)
self.graphics.delay_frame(self.frame_time,50) self.graphics.delay_frame(self.frame_time,50)
self.graphics.quit() self.graphics.quit()
def set_cursor(self, x, y): def set_cursor(self, x, y):
self.graphics.cursor = (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): def load_assets(self):
self.graphics.load_tilesheet("tiles", "assets/tiles/landscapeTiles_sheet.png") self.graphics.load_tilesheet("tiles", "assets/tiles/landscapeTiles_sheet.png")
for dir in os.listdir("assets/KnightBasic"): for dir in os.listdir("assets/KnightBasic"):
@ -80,7 +81,46 @@ class GameEngine(UserControls):
if file.endswith(".json"): if file.endswith(".json"):
self.graphics.load_spritesheet(file[:-5].lower(), f"assets/KnightBasic/{dir}/{file}") 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__": if __name__ == "__main__":
engine = GameEngine() 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 return screen_x, screen_y
def inv_iso_transform(self, 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 # Adjust screen coordinates by removing view offsets
y = (2 * screen_y - screen_x + 2 * self.view_offset_x) // self.cell_size 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 return x, y
def get_distance(self, position1, position2): def get_distance(self, position1, position2):

322
enne2engine/sdl2_wrapper.py

@ -1,22 +1,32 @@
import random import random
import math import math
import os
from math import sqrt from math import sqrt
import ctypes import ctypes
import sdl2 import sdl2
import sdl2.ext import sdl2.ext
import sdl2.sdlmixer as sdlmixer
from .tilemanager import TileManager from .tilemanager import TileManager
from .spritemanager import SpriteManager from .spritemanager import SpriteManager
from .isogeometry import IsometricGeometry from .isogeometry import IsometricGeometry
from .sdl2_utils.gui import SDL2Gui from .sdl2_utils.gui import SDL2Gui
class SDL2Wrapper(IsometricGeometry, SDL2Gui): class SDL2Wrapper(IsometricGeometry, SDL2Gui):
def __init__(self): def __init__(self, engine):
self.engine = engine
sdl2.ext.init() 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.view_size = (800, 600)
self.target_size = (800, 600) self.target_size = (800, 600)
self.cell_size = 132 self.base_cell_size = 132 # Original/base cell size
self.view_offset_x = 400 self.scaling_factor = 0.5 # Default scaling factor (can be changed)
self.view_offset_y = 200 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_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("My Game", size=self.view_size)
@ -27,7 +37,21 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
self.factory = sdl2.ext.SpriteFactory(renderer=self.renderer) self.factory = sdl2.ext.SpriteFactory(renderer=self.renderer)
self.fonts = self.generate_fonts("assets/decterm.ttf") self.fonts = self.generate_fonts("assets/decterm.ttf")
self.cursor = (0, 0) 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): def get_perf_counter(self):
return int(sdl2.SDL_GetPerformanceCounter()) return int(sdl2.SDL_GetPerformanceCounter())
@ -43,10 +67,43 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
return f"KEYDOWN:{key}".lower() return f"KEYDOWN:{key}".lower()
elif event.type == sdl2.SDL_MOUSEMOTION: elif event.type == sdl2.SDL_MOUSEMOTION:
x, y = event.motion.x, event.motion.y 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}" 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 return False
def clear_screen(self, color=(0, 0, 0, 255)): # Aggiunto valore alfa di default 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_SetRenderDrawColor(self.renderer.sdlrenderer, *color)
sdl2.SDL_RenderClear(self.renderer.sdlrenderer) sdl2.SDL_RenderClear(self.renderer.sdlrenderer)
@ -54,12 +111,15 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
sdl2.SDL_RenderPresent(self.renderer.sdlrenderer) sdl2.SDL_RenderPresent(self.renderer.sdlrenderer)
def quit(self): def quit(self):
# Clean up mixer resources
sdlmixer.Mix_CloseAudio()
sdlmixer.Mix_Quit()
sdl2.SDL_Quit() sdl2.SDL_Quit()
def load_spritesheet(self, name, path): def load_spritesheet(self, name, path):
surface = sdl2.ext.load_image(path.replace('.json', '.png')) surface = sdl2.ext.load_image(path.replace('.json', '.png'))
texture = sdl2.ext.Texture(self.renderer, surface) 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): def load_tilesheet(self, name, path):
surface = sdl2.ext.load_image(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): def render_sprite(self, name, x, y, frame, occlusion=0):
srcrect, total_frames = self.sprite_managers[name].get_frame_rect(frame) srcrect, total_frames = self.sprite_managers[name].get_frame_rect(frame)
x -= self.cell_size // 2
y -= self.cell_size // 4
if occlusion: if occlusion:
original_color_mods_r_g_b_a = self.apply_texture_color_mdod(self.sprite_managers[name].spritesheet_texture, 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, self.renderer.copy(self.sprite_managers[name].spritesheet_texture,
dstrect=(x, y, self.cell_size, self.cell_size), dstrect=(x, y, self.cell_size, self.cell_size),
srcrect=srcrect) srcrect=srcrect)
if occlusion: if occlusion:
sdl2.SDL_SetTextureColorMod(self.sprite_managers[name].spritesheet_texture.tx, sdl2.SDL_SetTextureColorMod(self.sprite_managers[name].spritesheet_texture.tx,
*original_color_mods_r_g_b_a) *original_color_mods_r_g_b_a)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)
return (frame + 1) % total_frames return (frame + 1) % total_frames
def render_tile(self, spritesheet_name, cell, x, y): def create_background(self, map, spritesheet_name):
tile_name = cell.get('tile') # Set page as render target and initialize it
occlusion = cell.get('occlusion', 0) sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.tiles_texture)
texture = self.tile_managers[spritesheet_name].get_tilesheet_texture() sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, 0, 0, 0, 255) # Green background
tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile_name) sdl2.SDL_RenderClear(self.renderer.sdlrenderer)
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 render_tile_shadow(self, spritesheet_name, cell, x, y): tilesheet_texture = self.tile_managers[spritesheet_name].get_tilesheet_texture()
tile_name = cell.get('tile')
occlusion = cell.get('occlusion', 0)
def create_background(self, map, spritesheet_name): def get_shadow(x, y):
self.surface_width = round(self.cell_size * len(map[0])*sqrt(2)) if (x, y) == self.engine.cursor_pos:
self.surface_height = round(self.cell_size//2 * len(map)*sqrt(2)) return 0
tilesheet_surface = self.tile_managers[spritesheet_name].get_tilesheet_surface() distance = self.get_distance((x, y), self.engine.cursor_pos)
bg_surface = sdl2.SDL_CreateRGBSurface(0, self.surface_width, self.surface_height, 32, 0, 0, 0, 0) 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)
if tile_rect is not None: if tile_rect is not None:
vertical_offset = self.cell_size - tile_rect[3] shadow = get_shadow(x, y)
horizontal_offset = (self.cell_size - tile_rect[2]) 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_x, iso_y = self.iso_transform(x, y)
iso_y += vertical_offset 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, dst_rect = (iso_x, iso_y,
tile_rect[2], tile_rect[3]) int(tile_rect[2] * self.scaling_factor),
sdl2.SDL_BlitSurface(tilesheet_surface, int(tile_rect[3] * self.scaling_factor))
sdl2.SDL_Rect(*tile_rect),
bg_surface, sdl2.SDL_RenderCopy(self.renderer.sdlrenderer, tilesheet_texture.tx,
sdl2.SDL_Rect(*dst_rect)) sdl2.SDL_Rect(*tile_rect),
#sdl2.SDL_FillRect(bg_surface, sdl2.SDL_Rect(*dst_rect), sdl2.SDL_MapRGB(bg_surface.contents.format, 0, 0, 255)) sdl2.SDL_Rect(*dst_rect))
for y, row in enumerate(map): for y, row in enumerate(map):
for x, cell in enumerate(row): for x, cell in enumerate(row):
tile_name = cell.get('tile') tile_name = cell.get('tile')
blit_tile(tile_name, x, y) 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): def render_background(self):
self.renderer.copy(self.background_texture, sdl2.SDL_RenderCopy(self.renderer.sdlrenderer, self.tiles_texture, None, None)
dstrect=(0, 0, self.surface_width, self.surface_height),
srcrect=(0,0, self.surface_width, self.surface_height)) def render_sprites(self):
sdl2.SDL_RenderCopy(self.renderer.sdlrenderer, self.sprite_texture, None, None)
def get_frame_time(self, perf_counter): def get_frame_time(self, perf_counter):
return (sdl2.SDL_GetPerformanceCounter() - perf_counter) / sdl2.SDL_GetPerformanceFrequency() *1000 return (sdl2.SDL_GetPerformanceCounter() - perf_counter) / sdl2.SDL_GetPerformanceFrequency() *1000
@ -160,17 +226,39 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
def draw_cursor(self): def draw_cursor(self):
x, y = self.cursor x, y = self.cursor
x += self.view_offset_x
y -= self.view_offset_y
c_x, c_y = self.inv_iso_transform(x, 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_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
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)) , 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//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 + 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, 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)) 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):
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): def apply_texture_color_mdod(self, texture, occlusion):
color_mod_r = int(255 * (1 - occlusion)) color_mod_r = int(255 * (1 - occlusion))
color_mod_g = int(255 * (1 - occlusion)) color_mod_g = int(255 * (1 - occlusion))
@ -193,9 +281,111 @@ class SDL2Wrapper(IsometricGeometry, SDL2Gui):
color_mod_b) color_mod_b)
return original_color_mods_r_g_b_a return original_color_mods_r_g_b_a
def render_tile_shadow(self, x, y, darkness=0): def render_tile(self, spritesheet_name, x, y, tile):
iso_x, iso_y = self.iso_transform(x, y) tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile)
iso_y += self.cell_size // 2 if tile_rect is not None:
self.renderer.copy(self.shadow_texture, vertical_offset = self.cell_size - tile_rect[3]
dstrect=(iso_x, iso_y, self.cell_size, self.cell_size), horizontal_offset = (self.cell_size - tile_rect[2])
srcrect=(0, 0, self.cell_size, self.cell_size)) 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