Fix bullet atlas, animation speed, sprite flip, fire rate, silent shots
ci / verify (push) Successful in 46s

Bullet sprites were wrong because I read the pack's layout backwards. Each of
its 8 PNGs is one ANIMATION FRAME, and a column within a file is a COLOUR
variant -- so walking columns cycled the palette while the shape sat still, and
the rows I picked were in a region of larger multi-cell sprites, which is the
scattered debris that showed on screen. tools/build_local_assets.py now composes
a proper atlas: 8 frames across, one row per SimConfig.KIND_*.

Animations ran at whatever the machine's framerate was, because they counted
_process calls. At 240fps that is four times too fast on its own, before the
requested halving. Both actor and bullet animation are now driven by elapsed
seconds, so they look the same on any machine.

The knight shifted sideways instead of mirroring: a Rect2 with negative width
does not flip, the draw call normalises it, so the sprite kept its orientation
and jumped right by its own width. Mirrored through the canvas transform about
the sprite's centre instead.

Shots were silent when a bullet spawned inside an enemy. The sound rode on
BULLET_SPAWN, which is deliberately suppressed for a bullet resolved and removed
within the same tick -- so no event, no sound, and this got more likely the
closer you stood. Firing is now its own event (PLAYER_FIRED): the shot happened
whether or not a bullet survived to be replicated. Protocol 3 -> 4, because
inserting mid-enum shifts the wire value of every event after it.

Fire cooldown 7 -> 14 ticks (4.3 shots/sec).

Licence question 2, confirmed verbatim: the paid tier DOES grant commercial use
("You may use these assets in personal, commercial or non-commercial
projects"), but its next sentence still forbids redistribution "no matter how
much you modify it". Paying moves these from local-only-and-non-commercial to
local-only-and-commercial; it never makes them committable. Only replacing them
with permissively licensed art does that.

Question 3: CREDITS.md now credits every pack, including the two whose licences
do not require it and the unused ones in considering_dont_use_yet/.

158 tests. check.sh, test.sh and smoke.sh pass.
This commit is contained in:
claude committed 2026-09-04 00:03:04 +02:00
1 parent 3586555aea
commit afe76c22ce
14 files changed
+234 -85

No files matched your search

+10 -13
View File
@@ -21,7 +21,7 @@ const _FALLBACK_COLOURS: Array[Color] = [
var _layers: Array[MultiMeshInstance2D] = []
## Advances the bullets' own animation, independent of the simulation.
var _anim_tick: int = 0
var _anim_time: float = 0.0
## True when the licence-restricted bullet sheet is absent and we are drawing
## generated dots instead. See Art.bullets_texture().
var _fallback: bool = false
@@ -31,11 +31,12 @@ func _ready() -> void:
var sheet := Art.bullets_texture()
_fallback = sheet == null
var art: Texture2D = sheet if sheet != null else _make_dot_texture()
var kinds := SimConfig.KIND_HEAVY + 1
if _fallback:
GameLog.info("view", "bullet sheet absent, drawing generated dots")
var quad := QuadMesh.new()
quad.size = Vector2.ONE
for kind in SimConfig.KIND_HEAVY + 1:
for kind in kinds:
var layer := MultiMeshInstance2D.new()
layer.texture = art
var mm := MultiMesh.new()
@@ -49,8 +50,8 @@ func _ready() -> void:
_layers.append(layer)
func _process(_delta: float) -> void:
_anim_tick += 1
func _process(delta: float) -> void:
_anim_time += delta
if not _fallback:
_apply_frame()
@@ -71,22 +72,18 @@ func _make_dot_texture() -> ImageTexture:
return ImageTexture.create_from_image(img)
## Point each layer's texture at this frame's cell in the sheet. Every bullet in
## a layer shares the frame, which is why this is one region assignment rather
## than per-instance work.
## Point each layer's texture at this frame's row/column in the atlas. Every
## bullet in a layer shares the frame, so this is one region assignment per kind
## rather than any per-instance work.
func _apply_frame() -> void:
var frame := (_anim_tick / 6) % Art.BULLET_FRAMES
var frame_index := int(_anim_time / Art.BULLET_FRAME_SECONDS)
for kind in _layers.size():
var cell: Vector2i = Art.BULLET_CELLS[kind]
var region := Rect2(
Vector2(float(cell.x + frame), float(cell.y)) * Art.BULLET_CELL,
Vector2(Art.BULLET_CELL, Art.BULLET_CELL))
var atlas := _layers[kind].texture as AtlasTexture
if atlas == null:
atlas = AtlasTexture.new()
atlas.atlas = Art.bullets_texture()
_layers[kind].texture = atlas
atlas.region = region
atlas.region = Art.bullet_region(kind, frame_index)
func render_pool(pool: BulletPool) -> void: