801f328093
ci / verify (push) Successful in 46s
The ship looked like it played both animations at once because it effectively did. is_moving() read the tail of `pending`, which _reconcile drains on every snapshot -- so it returned false about 20 times a second and the sprite alternated between the run and idle strips. Now reads the last sampled input, which survives the queue being emptied. Bullets were still wrong for a reason my earlier ASCII check could not see: I had dumped alpha only, and this pack animates as a colour shimmer over a fixed silhouette, so identical-looking frames told me nothing. The actual defect was that several sprites overflow their 16px cell and bleed into the neighbour below -- the first "heavy" I picked dragged in a solid slice of the sprite above it. Cells are re-picked to ones with empty borders in every frame, and build_local_assets.py now asserts that rather than trusting the choice, so a future pick that bleeds fails at build time instead of looking like a rendering bug. Added a hitbox overlay on F1 (src/view/debug_draw.gd). It draws what the simulation actually collides against over what is drawn: player hitbox against sprite radius, the muzzle point, enemy and boss radii, aggro rings, every live bullet's radius, and terrain outlined by which of the three flags each tile sets -- so a pit that stops feet but not bullets looks different from a wall. It deliberately ignores fog, since hiding half the evidence would defeat the point, and it reads every number from SimConfig/MapGrid/Content rather than keeping its own copies, or it would just confirm its own mistakes. Nearly every "that looked wrong" report in this project has been art and simulation disagreeing, and each took a round trip to diagnose. This makes that class of bug visible directly. 163 tests. check.sh, test.sh and smoke.sh pass.
61 lines
2.6 KiB
GDScript
61 lines
2.6 KiB
GDScript
extends GutTest
|
|
## The overlay's job is to be trustworthy: it must report the same numbers the
|
|
## simulation collides against, not its own idea of them. A debug view that
|
|
## quietly disagrees with the server is worse than none, because it makes wrong
|
|
## art look correct.
|
|
|
|
|
|
func test_the_overlay_reads_the_same_player_radii_the_sim_uses() -> void:
|
|
# Not a tautology worth much on its own, but it fails loudly if someone
|
|
# gives the overlay its own copy of these numbers.
|
|
assert_lt(SimConfig.PLAYER_RADIUS, SimConfig.PLAYER_VISUAL_RADIUS,
|
|
"the overlay exists to show this gap; it has to be a real gap")
|
|
assert_gt(SimConfig.PLAYER_MUZZLE_OFFSET, SimConfig.PLAYER_VISUAL_RADIUS,
|
|
"bullets must be born clear of the drawn ship")
|
|
|
|
|
|
func test_the_overlay_colours_are_distinguishable() -> void:
|
|
# Hitbox versus visual is the comparison the overlay is for; if they were
|
|
# drawn the same colour it could not be made.
|
|
assert_ne(DebugDraw.COL_HITBOX, DebugDraw.COL_VISUAL)
|
|
assert_ne(DebugDraw.COL_HITBOX, DebugDraw.COL_BULLET)
|
|
|
|
|
|
## Terrain is drawn from MapGrid's own flag tables, so a tile that blocks
|
|
## bullets but not sight renders differently from one that blocks both. This
|
|
## pins the distinction the overlay relies on.
|
|
func test_terrain_flags_remain_distinguishable() -> void:
|
|
var move_only := []
|
|
var all_three := []
|
|
for kind in [MapGrid.Kind.WALL, MapGrid.Kind.PILLAR, MapGrid.Kind.PIT,
|
|
MapGrid.Kind.BARRICADE]:
|
|
var m: bool = MapGrid.BLOCKS_MOVE[kind]
|
|
var b: bool = MapGrid.BLOCKS_BULLET[kind]
|
|
var s: bool = MapGrid.BLOCKS_SIGHT[kind]
|
|
if m and not b and not s:
|
|
move_only.append(kind)
|
|
if m and b and s:
|
|
all_three.append(kind)
|
|
assert_true(move_only.has(MapGrid.Kind.PIT),
|
|
"a pit blocks feet only -- the overlay draws that case specially")
|
|
assert_true(all_three.has(MapGrid.Kind.WALL))
|
|
|
|
|
|
## Every animation the ship can be in must exist in the atlas, or switching
|
|
## between idle and run mid-stride draws off the sheet.
|
|
func test_idle_and_run_strips_are_both_complete() -> void:
|
|
for strip in [Art.PLAYER_IDLE, Art.PLAYER_RUN]:
|
|
for n in Art.ACTOR_FRAMES:
|
|
var f := Art.frame(strip, n)
|
|
assert_true(f.end.x <= float(Art.TILESET.get_width())
|
|
and f.end.y <= float(Art.TILESET.get_height()),
|
|
"frame %d of %s runs off the tileset" % [n, strip])
|
|
|
|
|
|
## The two strips must not overlap, or "moving" and "idle" would show the same
|
|
## pixels and the run animation would be invisible.
|
|
func test_the_idle_and_run_strips_do_not_overlap() -> void:
|
|
var idle_end := Art.PLAYER_IDLE.position.x + Art.PLAYER_IDLE.size.x * Art.ACTOR_FRAMES
|
|
assert_lte(idle_end, Art.PLAYER_RUN.position.x,
|
|
"idle frames run into the run strip; the two animations would alias")
|