Debug view improvements, warp animation

* Add a visible frame one tile beyond the normal screen in debug mode.

* Fix the display of motion paths for mounted traps in debug mode.

* Add an animation for the warp trap.
This commit is contained in:
James Campbell
2026-07-22 11:56:21 -04:00
parent d84e4627c3
commit 59e4bcce1c
8 changed files with 249 additions and 41 deletions

View File

@@ -49,6 +49,12 @@ class Level:
self.width = self.cols * self.tile
self.height = self.grid_rows * self.tile
# Draw-time translation (pixels): everything visible is shifted by this
# when blitted. Physics stays in true level coordinates — this is purely
# so the debug view can reveal a margin of overscan around the level (set
# by the Game). (0, 0) in normal play, so nothing moves.
self.render_offset = (0, 0)
self.solids = [] # list[pygame.Rect] — full blocking
self.oneways = [] # list[pygame.Rect] — blocking only from above
self.spawn = (self.tile, self.tile)
@@ -123,9 +129,10 @@ class Level:
def draw(self, surface, assets):
t = self.tile
ox, oy = self.render_offset
block = assets.get("block", t, t)
for rect in self.solids:
surface.blit(block, rect)
surface.blit(block, rect.move(ox, oy))
# One-way platforms look identical to solid blocks — you only discover
# them by falling through. `debug` fades them so a level designer can
@@ -135,10 +142,10 @@ class Level:
oneway_img = block.copy()
oneway_img.fill((255, 255, 255, 110), special_flags=pygame.BLEND_RGBA_MULT)
for rect in self.oneways:
surface.blit(oneway_img, rect)
surface.blit(oneway_img, rect.move(ox, oy))
surface.blit(assets.get("goal", self.goal_rect.w, self.goal_rect.h),
self.goal_rect)
self.goal_rect.move(ox, oy))
for tr in self.traps:
tr.render(surface, assets)
@@ -149,22 +156,48 @@ class Level:
def _draw_grid(self, surface):
"""A faint tile grid with col/row labels, so `at: [col,row]` placement
is eyeballable while designing."""
is eyeballable while designing.
In the debug view the drawing surface is larger than the level (a margin
of overscan; see ``render_offset``). The grid extends across the whole
surface — labels go negative / past the map in the margin — the off-map
region is shaded, and the true play area is outlined so it's clear what's
actually on screen at runtime."""
if Level._grid_font is None:
Level._grid_font = pygame.font.SysFont("consolas,menlo,monospace", 10)
overlay = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
t = self.tile
ox, oy = self.render_offset
sw, sh = surface.get_size()
overlay = pygame.Surface((sw, sh), pygame.SRCALPHA)
# Shade the off-screen overscan so the real play area reads clearly.
view = pygame.Rect(ox, oy, self.width, self.height)
shade = (10, 12, 20, 130)
for band in (pygame.Rect(0, 0, sw, oy), # above
pygame.Rect(0, view.bottom, sw, sh - view.bottom), # below
pygame.Rect(0, oy, ox, self.height), # left
pygame.Rect(view.right, oy, sw - view.right, self.height)): # right
if band.w > 0 and band.h > 0:
overlay.fill(shade, band)
# Tile grid across the whole surface; offsets are whole tiles so lines
# stay aligned to the level grid. mx/my = margin width in tiles.
mx, my = ox // t, oy // t
line = (255, 255, 255, 26)
for c in range(self.cols + 1):
x = c * self.tile
pygame.draw.line(overlay, line, (x, 0), (x, self.height))
for r in range(self.grid_rows + 1):
y = r * self.tile
pygame.draw.line(overlay, line, (0, y), (self.width, y))
for k in range(sw // t + 1):
pygame.draw.line(overlay, line, (k * t, 0), (k * t, sh))
for k in range(sh // t + 1):
pygame.draw.line(overlay, line, (0, k * t), (sw, k * t))
label = (150, 162, 190)
for c in range(self.cols):
overlay.blit(Level._grid_font.render(str(c), True, label),
(c * self.tile + 2, 1))
for r in range(self.grid_rows):
overlay.blit(Level._grid_font.render(str(r), True, label),
(1, r * self.tile + 1))
for k in range(sw // t):
overlay.blit(Level._grid_font.render(str(k - mx), True, label),
(k * t + 2, 1))
for k in range(sh // t):
overlay.blit(Level._grid_font.render(str(k - my), True, label),
(1, k * t + 1))
# Outline the actual runtime viewport (the level's true bounds).
if (mx or my):
pygame.draw.rect(overlay, (95, 210, 235, 200), view, 2)
surface.blit(overlay, (0, 0))