Add new PNG images for clean and preview outputs

- Added `image_clean.png` to the output directory for the clean image representation.
- Added `image_clean_preview.png` for the preview of the clean image.
- Introduced `image_svg_clean.png` for the SVG clean image representation.
This commit is contained in:
John Doe
2026-03-27 23:40:27 +01:00
parent e7c5ebb119
commit 02202e4d3d
403 changed files with 40415 additions and 411 deletions
+245 -49
View File
@@ -1,48 +1,123 @@
import os
import random
from engine import maze
from runtime_paths import bundle_path
class Graphics():
def load_assets(self):
print("Loading graphics assets...")
self.tunnel = self.render_engine.load_image("Rat/BMP_TUNNEL.png", surface=True)
self.grasses = [self.render_engine.load_image(f"Rat/BMP_1_GRASS_{i+1}.png", surface=True) for i in range(4)]
self.rat_assets = {}
self.rat_assets_textures = {}
self.rat_image_sizes = {} # Pre-cache image sizes
self.bomb_assets = {}
for sex in ["MALE", "FEMALE", "BABY"]:
self.rat_assets[sex] = {}
for direction in ["UP", "DOWN", "LEFT", "RIGHT"]:
self.rat_assets[sex][direction] = self.render_engine.load_image(f"Rat/BMP_{sex}_{direction}.png", transparent_color=(128, 128, 128))
# Load textures and pre-cache sizes
for sex in ["MALE", "FEMALE", "BABY"]:
self.rat_assets_textures[sex] = {}
self.rat_image_sizes[sex] = {}
for direction in ["UP", "DOWN", "LEFT", "RIGHT"]:
texture = self.render_engine.load_image(f"Rat/BMP_{sex}_{direction}.png", transparent_color=(128, 128, 128), surface=False)
self.rat_assets_textures[sex][direction] = texture
# Cache size to avoid get_image_size() calls in draw loop
self.rat_image_sizes[sex][direction] = texture.size
for n in range(5):
self.bomb_assets[n] = self.render_engine.load_image(f"Rat/BMP_BOMB{n}.png", transparent_color=(128, 128, 128))
self.assets = {}
for file in os.listdir("assets/Rat"):
if file.endswith(".png"):
self.assets[file[:-4]] = self.render_engine.load_image(f"Rat/{file}", transparent_color=(128, 128, 128))
# Pre-generate blood stain textures pool (optimization)
print("Pre-generating blood stain pool...")
self.blood_stain_textures = []
for _ in range(10):
blood_surface = self.render_engine.generate_blood_surface()
blood_texture = self.render_engine.draw_blood_surface(blood_surface, (0, 0))
if blood_texture:
self.blood_stain_textures.append(blood_texture)
# Blood layer sprites (instead of regenerating background)
self.blood_layer_sprites = []
theme_index = self.get_theme_index()
print(f"[gfx] load_assets requested: level={self.current_level + 1} theme={theme_index}")
if not hasattr(self, "theme_assets_cache"):
self.theme_assets_cache = {}
if not hasattr(self, "blood_layer_sprites"):
self.blood_layer_sprites = []
if not hasattr(self, "cave_foreground_tiles"):
self.cave_foreground_tiles = []
if not getattr(self, "common_assets_loaded", False):
print("Loading graphics assets...")
self.rat_assets = {}
self.rat_assets_textures = {}
self.rat_image_sizes = {}
self.bomb_assets = {}
self.assets = {}
for sex in ["MALE", "FEMALE", "BABY"]:
self.rat_assets[sex] = {}
self.rat_assets_textures[sex] = {}
self.rat_image_sizes[sex] = {}
for direction in ["UP", "DOWN", "LEFT", "RIGHT"]:
self.rat_assets[sex][direction] = self.render_engine.load_image(
f"Rat/BMP_{sex}_{direction}.png",
transparent_color=((125, 125, 125), (128, 128, 128)),
)
texture = self.render_engine.load_image(
f"Rat/BMP_{sex}_{direction}.png",
transparent_color=((125, 125, 125), (128, 128, 128)),
surface=False,
)
self.rat_assets_textures[sex][direction] = texture
self.rat_image_sizes[sex][direction] = texture.size
for n in range(5):
self.bomb_assets[n] = self.render_engine.load_image(
f"Rat/BMP_BOMB{n}.png",
transparent_color=((125, 125, 125), (128, 128, 128)),
)
rat_asset_dir = bundle_path("assets", "Rat")
for file in os.listdir(rat_asset_dir):
if file.endswith(".png"):
self.assets[file[:-4]] = self.render_engine.load_image(
f"Rat/{file}",
transparent_color=((125, 125, 125), (128, 128, 128)),
)
print("Pre-generating blood stain pool...")
self.blood_stain_textures = []
for _ in range(10):
blood_surface = self.render_engine.generate_blood_surface()
blood_texture = self.render_engine.draw_blood_surface(blood_surface, (0, 0))
if blood_texture:
self.blood_stain_textures.append(blood_texture)
self.common_assets_loaded = True
print("[gfx] common assets loaded")
else:
print("[gfx] common assets cache hit")
if theme_index not in self.theme_assets_cache:
print(f"Loading theme assets {theme_index}...")
self.theme_assets_cache[theme_index] = {
"floor_tile": self.render_engine.create_color_surface((128, 128, 128)),
"tunnel": self.render_engine.load_image("Rat/BMP_TUNNEL.png", surface=True),
"grasses": [
self.render_engine.load_image(f"Rat/BMP_{theme_index}_GRASS_{i+1}.png", surface=True)
for i in range(4)
],
"flowers": [
self.render_engine.load_image(f"Rat/BMP_{theme_index}_FLOWER_{i+1}.png", surface=True)
for i in range(4)
],
"caves": {
direction: self.render_engine.load_image(
f"Rat/BMP_{theme_index}_CAVE_{direction}.png",
transparent_color=((125, 125, 125), (128, 128, 128)),
surface=False,
)
for direction in ["UP", "DOWN", "LEFT", "RIGHT"]
},
"edges": {
direction: self.render_engine.load_image(f"Rat/BMP_{theme_index}_{direction}.png", surface=True)
for direction in ["N", "S", "E", "W"]
},
"corners": {
direction: self.render_engine.load_image(f"Rat/BMP_{theme_index}_{direction}.png", surface=True)
for direction in ["NE", "NW", "SE", "SW"]
},
"inner_corners": {
direction: self.render_engine.load_image(f"Rat/BMP_{theme_index}_{direction}.png", surface=True)
for direction in ["EN", "ES", "WN", "WS"]
},
}
print(f"[gfx] theme cache miss -> loaded theme {theme_index}")
else:
print(f"[gfx] theme cache hit -> reusing theme {theme_index}")
self.loaded_theme_index = theme_index
theme_assets = self.theme_assets_cache[theme_index]
self.floor_tile = theme_assets["floor_tile"]
self.tunnel = theme_assets["tunnel"]
self.grasses = theme_assets["grasses"]
self.flowers = theme_assets["flowers"]
self.caves = theme_assets["caves"]
self.edges = theme_assets["edges"]
self.corners = theme_assets["corners"]
self.inner_corners = theme_assets["inner_corners"]
def get_theme_index(self):
return self.current_level % 32 // 8 + 1
@@ -50,12 +125,16 @@ class Graphics():
def draw_maze(self):
if self.background_texture is None:
print("Generating background texture")
print(f"[gfx] generating background texture for level={self.current_level + 1} theme={self.loaded_theme_index}")
self.regenerate_background()
self.render_engine.draw_background(self.background_texture)
# Draw blood layer as sprites (optimized - no background regeneration)
self.draw_blood_layer()
def draw_cave_foreground(self):
for surface, x, y in self.cave_foreground_tiles:
self.render_engine.draw_image(x, y, surface, anchor="nw", tag="cave")
def draw_blood_layer(self):
"""Draw all blood stains as sprites overlay (optimized)"""
@@ -65,19 +144,136 @@ class Graphics():
def regenerate_background(self):
"""Generate or regenerate the background texture (static - no blood stains)"""
texture_tiles = []
for y, row in enumerate(self.map.matrix):
self.cave_foreground_tiles = []
half_cell = self.cell_size // 2
def draw(surface, x, y):
texture_tiles.append((surface, x, y))
def draw_cave(surface, x, y):
self.cave_foreground_tiles.append((surface, x, y))
def occupied(x, y):
return self.map.in_bounds(x, y) and self.map.get_cell(x, y) != maze.MAP_EMPTY
def is_tunnel(x, y):
return self.map.in_bounds(x, y) and self.map.get_cell(x, y) == maze.MAP_TUNNEL
def random_wall():
return random.choice(self.grasses)
def random_flower():
return random.choice(self.flowers)
for y, row in enumerate(self.map.tiles):
for x, cell in enumerate(row):
variant = x*y % 4
tile = self.grasses[variant] if cell else self.tunnel
texture_tiles.append((tile, x*self.cell_size, y*self.cell_size))
px = x * self.cell_size
py = y * self.cell_size
if cell == maze.MAP_EMPTY:
continue
if cell == maze.MAP_WALL:
if x == 0 or y == 0 or x == self.map.width - 1 or y == self.map.height - 1:
draw(random_wall(), px, py)
if x > 0 and y > 0 and (not occupied(x - 1, y - 1) or not occupied(x, y - 1) or not occupied(x - 1, y)):
north = occupied(x, y - 1)
west = occupied(x - 1, y)
if north or west:
if north and west:
draw(self.inner_corners["WN"], px, py)
elif north and not west:
draw(self.edges["W"], px, py)
else:
draw(self.edges["N"], px, py)
else:
draw(self.corners["NW"], px, py)
if y < self.map.height - 1 and x < self.map.width - 1:
south = occupied(x, y + 1)
east = occupied(x + 1, y)
southeast = occupied(x + 1, y + 1)
if southeast and south and east:
if (
random.randrange(10) != 0
or x == 0
or y == 0
or x == self.map.width - 2
or y == self.map.height - 2
or is_tunnel(x + 1, y)
or is_tunnel(x, y + 1)
or is_tunnel(x + 1, y + 1)
):
draw(random_wall(), px + half_cell, py + half_cell)
else:
draw(random_flower(), px + half_cell, py + half_cell)
elif south or east:
if south and east:
draw(self.inner_corners["ES"], px + half_cell, py + half_cell)
elif south and not east:
draw(self.edges["E"], px + half_cell, py + half_cell)
else:
draw(self.edges["S"], px + half_cell, py + half_cell)
else:
draw(self.corners["SE"], px + half_cell, py + half_cell)
if y > 0 and x < self.map.width - 1 and (not occupied(x + 1, y - 1) or not occupied(x, y - 1) or not occupied(x + 1, y)):
north = occupied(x, y - 1)
east = occupied(x + 1, y)
if north or east:
if north and east:
draw(self.inner_corners["EN"], px + half_cell, py)
elif north and not east:
draw(self.edges["E"], px + half_cell, py)
else:
draw(self.edges["N"], px + half_cell, py)
else:
draw(self.corners["NE"], px + half_cell, py)
if y < self.map.height - 1 and x > 0 and (not occupied(x - 1, y + 1) or not occupied(x, y + 1) or not occupied(x - 1, y)):
south = occupied(x, y + 1)
west = occupied(x - 1, y)
if south or west:
if south and west:
draw(self.inner_corners["WS"], px, py + half_cell)
elif south and not west:
draw(self.edges["W"], px, py + half_cell)
else:
draw(self.edges["S"], px, py + half_cell)
else:
draw(self.corners["SW"], px, py + half_cell)
elif cell == maze.MAP_TUNNEL:
above = occupied(x, y - 1)
below = occupied(x, y + 1)
left = occupied(x - 1, y)
right = occupied(x + 1, y)
if above:
if below:
if left:
if right:
if random.randrange(10) != 0:
draw(random_wall(), px + half_cell, py + half_cell)
else:
draw(random_flower(), px + half_cell, py + half_cell)
else:
draw_cave(self.caves["RIGHT"], px, py)
else:
draw(self.grasses[0], px + half_cell, py + half_cell)
draw_cave(self.caves["LEFT"], px, py)
else:
draw_cave(self.caves["DOWN"], px, py)
else:
draw(self.grasses[0], px + half_cell, py + half_cell)
draw_cave(self.caves["UP"], px, py)
# Blood stains now handled separately as overlay layer
self.background_texture = self.render_engine.create_texture(texture_tiles)
self.background_texture = self.render_engine.create_texture(texture_tiles, fill_color=(128, 128, 128))
def add_blood_stain(self, position):
"""Add a blood stain as sprite overlay (optimized - no background regeneration)"""
import random
# Pick random blood texture from pre-generated pool
if not self.blood_stain_textures:
return