You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
18 KiB

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 .sdl2_utils.isogeometry import IsometricGeometry
from .sdl2_utils.gui import SDL2Gui
class SDL2Renderer(IsometricGeometry, SDL2Gui):
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.height = len(engine.map)
self.width = len(engine.map[0])
self.view_size = (800, 600)
self.target_size = (800, 600)
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.ground_level_offset = self.base_cell_size // 4
self.view_offset_x = 400
self.view_offset_y = 0
self.surface_width = 0
self.surface_height = 0
self.window = sdl2.ext.Window("Enne2EngineDEMO", size=self.view_size)
self.window.show() # Mostra la finestra
self.renderer = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED)
self.tile_managers = {}
self.sprite_managers = {}
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())
def handle_events(self):
for event in sdl2.ext.get_events():
if event.type == sdl2.SDL_QUIT:
return "QUIT"
elif event.type == sdl2.SDL_KEYUP:
key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8')
return f"KEYUP:{key}".lower()
elif event.type == sdl2.SDL_KEYDOWN:
key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf-8')
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)
self.selection_start = (x, y)
self.selection_current = (x, y)
if event.button.button == 1:
self.selecting = True
return f"MOUSEDOWN:{x}:{y}:{event.button.button}"
elif event.type == sdl2.SDL_MOUSEBUTTONUP:
x, y = event.button.x, event.button.y
# Left mouse button (button 1)
if event.button.button == 1:
result = f"MOUSEUP:{x}:{y}:{event.button.button}"
# Return the selection area if it's large enough and we were selecting
if self.selecting and self.selection_start and self.get_selection_size() > 5:
result = f"SELECTION:{self.selection_start[0]}:{self.selection_start[1]}:{x}:{y}"
# 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)
def present_renderer(self):
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.base_cell_size)
def load_tilesheet(self, name, path):
surface = sdl2.ext.load_image(path)
texture = sdl2.ext.Texture(self.renderer, surface)
self.tile_managers[name] = TileManager(path.replace('.png', '.xml'), surface, texture, self.cell_size)
def load_sprite(self, spritesheet, name):
return self.tile_managers[spritesheet].get_tile_rect(name)
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 create_background(self, map, spritesheet_name, map_shadow):
# Set page as render target and initialize it
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.tiles_texture)
sdl2.SDL_SetRenderDrawColor(self.renderer.sdlrenderer, 0, 0, 0, 255) # Green background
sdl2.SDL_RenderClear(self.renderer.sdlrenderer)
self.map_shadow = map_shadow
tilesheet_texture = self.tile_managers[spritesheet_name].get_tilesheet_texture()
def get_shadow(x, y):
shadow = min(self.map_shadow[y][x], 1)
if shadow == 1 and self.engine.map[y][x].get('visited'):
shadow = 0.8
return shadow
def blit_tile(tile, x, y):
tile_rect = self.tile_managers[spritesheet_name].get_tile_rect(tile)
if tile_rect is not None:
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] * self.scaling_factor - self.ground_level_offset * self.scaling_factor)
# Adjusted horizontal offset calculation for half-sized cells
horizontal_offset = (self.cell_size - tile_rect[2] * self.scaling_factor )
iso_x, iso_y = self.iso_transform(x, y)
iso_y += vertical_offset
iso_x += (horizontal_offset - tile_rect[2] * self.scaling_factor // 2)
# Scale the actual tile rectangle based on the current scaling factor
dst_rect = (int(iso_x), int(iso_y),
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)
def render_background(self):
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
def delay_frame(self, frame_time, target):
self.performance = (frame_time)
if self.performance < target:
delay = target - round(self.performance)
else:
delay = 0
sdl2.SDL_Delay(delay)
def draw_cursor(self):
x, y = self.cursor
c_x, c_y = self.inv_iso_transform(x, y)
iso_x, iso_y = self.iso_transform(c_x, c_y)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.sprite_texture)
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))
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)
return c_x, c_y
def draw_square(self, iso_x, iso_y, color=(0, 255, 0, 255), margin=6):
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.sprite_texture)
# 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))
color_mod_b = int(255 * (1 - occlusion))
original_color_mods_r_g_b_a = [0] * 3
r_ptr = ctypes.pointer(ctypes.c_ubyte())
g_ptr = ctypes.pointer(ctypes.c_ubyte())
b_ptr = ctypes.pointer(ctypes.c_ubyte())
sdl_texture = texture.texture if hasattr(texture, 'texture') else texture.tx
sdl2.SDL_GetTextureColorMod(sdl_texture,
r_ptr,
g_ptr,
b_ptr)
original_color_mods_r_g_b_a[0] = r_ptr.contents.value
original_color_mods_r_g_b_a[1] = g_ptr.contents.value
original_color_mods_r_g_b_a[2] = b_ptr.contents.value
sdl2.SDL_SetTextureColorMod(sdl_texture,
color_mod_r,
color_mod_g,
color_mod_b)
return original_color_mods_r_g_b_a
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.)
"""
if factor > 0:
print(f"Scaling factor set to {factor}")
self.scaling_factor = factor
self.cell_size = int(self.base_cell_size * self.scaling_factor)
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
def set_opacity(self, x, y, opacity):
self.engine.map_shadow[y][x] = max(self.engine.map_shadow[y][x], opacity)
def draw_line(self, points, color=(255, 0, 0, 255)):
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, self.sprite_texture)
self.renderer.draw_line(points=points, color=color)
sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None)