Add semi-transparent tunnel cover overlay for internal passages
- Add sdl2.create_overlay_texture() and draw_overlay_texture(alpha) for transparent full-map overlays built from sub-tile surface blits. - Add Graphics.regenerate_tunnel_cover() which builds an overlay of 4 random grass sub-tiles (20x20) for every internal tunnel cell, defined as a tunnel cell surrounded by occupied cells (wall or tunnel) on all four sides. Cells with at least one open side are handled by cave_foreground and skipped here. - Draw the tunnel cover in the game loop after top-layer units/effects but before cave_foreground and points, at 95% opacity (alpha=242) so the unit and effect passing underneath is just barely visible.
This commit is contained in:
@@ -26,6 +26,8 @@ class Graphics:
|
|||||||
self.blood_layer_sprites = []
|
self.blood_layer_sprites = []
|
||||||
if not hasattr(self, "cave_foreground_tiles"):
|
if not hasattr(self, "cave_foreground_tiles"):
|
||||||
self.cave_foreground_tiles = []
|
self.cave_foreground_tiles = []
|
||||||
|
if not hasattr(self, "tunnel_cover_texture"):
|
||||||
|
self.tunnel_cover_texture = None
|
||||||
|
|
||||||
if not getattr(self, "common_assets_loaded", False):
|
if not getattr(self, "common_assets_loaded", False):
|
||||||
print("Loading graphics assets...")
|
print("Loading graphics assets...")
|
||||||
@@ -194,6 +196,45 @@ class Graphics:
|
|||||||
# Draw blood layer as sprites (optimized - no background regeneration)
|
# Draw blood layer as sprites (optimized - no background regeneration)
|
||||||
self.draw_blood_layer()
|
self.draw_blood_layer()
|
||||||
|
|
||||||
|
def regenerate_tunnel_cover(self):
|
||||||
|
"""Generate an overlay texture that covers internal tunnel passages with grass sub-tiles.
|
||||||
|
|
||||||
|
A tunnel cell is considered an internal passage only when it is surrounded
|
||||||
|
by occupied cells (walls or tunnels) on all four sides. In that case there is
|
||||||
|
no cave entrance drawn by cave_foreground, so we cover it here.
|
||||||
|
"""
|
||||||
|
tunnel_tiles = []
|
||||||
|
half_cell = self.game.cell_size // 2
|
||||||
|
|
||||||
|
def occupied(x, y):
|
||||||
|
return self.game.map.in_bounds(x, y) and self.game.map.get_cell(x, y) != maze.MAP_EMPTY
|
||||||
|
|
||||||
|
for y, row in enumerate(self.game.map.tiles):
|
||||||
|
for x, cell in enumerate(row):
|
||||||
|
if cell != maze.MAP_TUNNEL:
|
||||||
|
continue
|
||||||
|
|
||||||
|
above = occupied(x, y - 1)
|
||||||
|
below = occupied(x, y + 1)
|
||||||
|
left = occupied(x - 1, y)
|
||||||
|
right = occupied(x + 1, y)
|
||||||
|
|
||||||
|
if not (above and below and left and right):
|
||||||
|
continue
|
||||||
|
|
||||||
|
px = x * self.game.cell_size
|
||||||
|
py = y * self.game.cell_size
|
||||||
|
for qx, qy, sx, sy in [
|
||||||
|
(0, 0, 0, 0),
|
||||||
|
(half_cell, 0, half_cell, 0),
|
||||||
|
(0, half_cell, 0, half_cell),
|
||||||
|
(half_cell, half_cell, half_cell, half_cell),
|
||||||
|
]:
|
||||||
|
grass = random.choice(self.grasses)
|
||||||
|
tunnel_tiles.append((grass, sx, sy, half_cell, half_cell, px + qx, py + qy))
|
||||||
|
|
||||||
|
self.tunnel_cover_texture = self.game.render_engine.create_overlay_texture(tunnel_tiles)
|
||||||
|
|
||||||
def draw_cave_foreground(self):
|
def draw_cave_foreground(self):
|
||||||
active_cave_explosions = {}
|
active_cave_explosions = {}
|
||||||
for unit in self.game.units.values():
|
for unit in self.game.units.values():
|
||||||
@@ -218,6 +259,7 @@ class Graphics:
|
|||||||
"""Generate or regenerate the background texture (static - no blood stains)"""
|
"""Generate or regenerate the background texture (static - no blood stains)"""
|
||||||
texture_tiles = []
|
texture_tiles = []
|
||||||
self.cave_foreground_tiles = []
|
self.cave_foreground_tiles = []
|
||||||
|
self.regenerate_tunnel_cover()
|
||||||
half_cell = self.game.cell_size // 2
|
half_cell = self.game.cell_size // 2
|
||||||
|
|
||||||
def draw(surface, x, y):
|
def draw(surface, x, y):
|
||||||
|
|||||||
@@ -236,6 +236,61 @@ class GameWindow:
|
|||||||
sdl2.SDL_FreeSurface(bg_surface)
|
sdl2.SDL_FreeSurface(bg_surface)
|
||||||
return bg_texture
|
return bg_texture
|
||||||
|
|
||||||
|
def create_overlay_texture(self, tiles: list):
|
||||||
|
"""Create a transparent RGBA texture from sub-tile surface blits.
|
||||||
|
|
||||||
|
Each tile is a tuple (surface, src_x, src_y, width, height, dst_x, dst_y).
|
||||||
|
Source surfaces are converted to RGBA so the resulting texture has an
|
||||||
|
alpha channel and supports global alpha modulation.
|
||||||
|
"""
|
||||||
|
bg_surface = sdl2.SDL_CreateRGBSurface(
|
||||||
|
0, self.width, self.height, 32,
|
||||||
|
0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000
|
||||||
|
)
|
||||||
|
sdl2.SDL_FillRect(bg_surface, None, 0)
|
||||||
|
sdl2.SDL_SetSurfaceBlendMode(bg_surface, sdl2.SDL_BLENDMODE_BLEND)
|
||||||
|
rgba_format = sdl2.SDL_PIXELFORMAT_RGBA8888
|
||||||
|
for surface, src_x, src_y, width, height, dst_x, dst_y in tiles:
|
||||||
|
src_fmt = sdl2.SDL_PIXELFORMAT_UNKNOWN
|
||||||
|
try:
|
||||||
|
src_fmt = surface.format.format
|
||||||
|
except AttributeError:
|
||||||
|
src_fmt = sdl2.SDL_PIXELFORMAT_UNKNOWN
|
||||||
|
srcrect = sdl2.SDL_Rect(int(src_x), int(src_y), int(width), int(height))
|
||||||
|
dstrect = sdl2.SDL_Rect(int(dst_x), int(dst_y), int(width), int(height))
|
||||||
|
if src_fmt == rgba_format:
|
||||||
|
sdl2.SDL_BlitSurface(surface, srcrect, bg_surface, dstrect)
|
||||||
|
else:
|
||||||
|
converted = sdl2.SDL_ConvertSurfaceFormat(surface, rgba_format, 0)
|
||||||
|
if converted:
|
||||||
|
sdl2.SDL_BlitSurface(converted, srcrect, bg_surface, dstrect)
|
||||||
|
sdl2.SDL_FreeSurface(converted)
|
||||||
|
else:
|
||||||
|
sdl2.SDL_BlitSurface(surface, srcrect, bg_surface, dstrect)
|
||||||
|
bg_texture = self.factory.from_surface(bg_surface)
|
||||||
|
try:
|
||||||
|
sdl2.SDL_SetTextureBlendMode(bg_texture.texture, sdl2.SDL_BLENDMODE_BLEND)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
sdl2.SDL_FreeSurface(bg_surface)
|
||||||
|
return bg_texture
|
||||||
|
|
||||||
|
def draw_overlay_texture(self, texture, alpha=255):
|
||||||
|
"""Draw a full-map transparent overlay texture with current view offset."""
|
||||||
|
if texture is None:
|
||||||
|
return
|
||||||
|
raw_texture = getattr(texture, "texture", texture)
|
||||||
|
if alpha < 255:
|
||||||
|
try:
|
||||||
|
sdl2.SDL_SetTextureAlphaMod(raw_texture, int(alpha))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
sdl2.SDL_SetTextureBlendMode(raw_texture, sdl2.SDL_BLENDMODE_BLEND)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self.renderer.copy(texture, dstrect=sdl2.SDL_Rect(self.w_offset, self.h_offset, self.width, self.height))
|
||||||
|
|
||||||
def create_color_surface(self, color, width=None, height=None):
|
def create_color_surface(self, color, width=None, height=None):
|
||||||
"""Create a solid color surface matching the current cell size by default."""
|
"""Create a solid color surface matching the current cell size by default."""
|
||||||
width = width or self.cell_size
|
width = width or self.cell_size
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class MiceMaze:
|
|||||||
self.scrolling = False
|
self.scrolling = False
|
||||||
self.sounds = {}
|
self.sounds = {}
|
||||||
self.background_texture = None
|
self.background_texture = None
|
||||||
|
self.tunnel_cover_texture = None
|
||||||
self.combined_scores = None
|
self.combined_scores = None
|
||||||
|
|
||||||
def _setup_engine(self):
|
def _setup_engine(self):
|
||||||
@@ -527,12 +528,17 @@ class MiceMaze:
|
|||||||
if getattr(unit, "draw_on_top", False) and not getattr(unit, "draw_last", False):
|
if getattr(unit, "draw_on_top", False) and not getattr(unit, "draw_last", False):
|
||||||
unit.draw()
|
unit.draw()
|
||||||
|
|
||||||
|
# Draw tunnel cover overlay above units/effects but below cave foreground and points
|
||||||
|
self.render_engine.draw_overlay_texture(self.graphics.tunnel_cover_texture, alpha=191)
|
||||||
|
|
||||||
|
# Draw cave foreground (tunnel entrances) above the tunnel cover
|
||||||
|
self.graphics.draw_cave_foreground()
|
||||||
|
|
||||||
# Draw foreground/last-layer units (points) on top of everything
|
# Draw foreground/last-layer units (points) on top of everything
|
||||||
for unit in sorted_units:
|
for unit in sorted_units:
|
||||||
if getattr(unit, "draw_last", False):
|
if getattr(unit, "draw_last", False):
|
||||||
unit.draw()
|
unit.draw()
|
||||||
|
|
||||||
self.graphics.draw_cave_foreground()
|
|
||||||
self.render_engine.draw_pointer(self.pointer[0] * self.cell_size, self.pointer[1] * self.cell_size)
|
self.render_engine.draw_pointer(self.pointer[0] * self.cell_size, self.pointer[1] * self.cell_size)
|
||||||
|
|
||||||
self.render_engine.update_status(f"Mice: {self.unit_manager.count_rats()} - Points: {self.points}")
|
self.render_engine.update_status(f"Mice: {self.unit_manager.count_rats()} - Points: {self.points}")
|
||||||
|
|||||||
Reference in New Issue
Block a user