From 65a811db0d8b709e76104a6919f45f662be396cc Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Tue, 16 Jul 2019 22:10:18 -0700 Subject: [PATCH] Auto-prune Empty Chunks in Level Files * Discovered a bug where if you hit the Undo key to erase pixels and an entire chunk became empty by it, the chunk would have rendering errors and show as a solid black square instead of the level wallpaper showing through. * Chunks that have no pixels in them are culled from the chunker immediately when you call a Delete() operation. * The level file saver also calls a maintenance function to prune all empty chunks upon saving the file. So existing levels with broken chunks need only be re-saved to fix them. --- dev-assets/doodads/objects/exit-flag.js | 4 ++++ pkg/doodle.go | 6 ------ pkg/level/chunker.go | 14 ++++++++++++++ pkg/level/fmt_maintenance.go | 13 +++++++++++++ pkg/level/fmt_readwrite.go | 1 + 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/dev-assets/doodads/objects/exit-flag.js b/dev-assets/doodads/objects/exit-flag.js index 056890c..df4aec0 100644 --- a/dev-assets/doodads/objects/exit-flag.js +++ b/dev-assets/doodads/objects/exit-flag.js @@ -4,6 +4,10 @@ function main() { Self.SetHitbox(22+16, 16, 75-16, 86); Events.OnCollide(function(e) { + if (!e.Settled) { + return; + } + if (e.InHitbox) { EndLevel(); } diff --git a/pkg/doodle.go b/pkg/doodle.go index 61782b2..12cd953 100644 --- a/pkg/doodle.go +++ b/pkg/doodle.go @@ -114,12 +114,6 @@ func (d *Doodle) Run() error { // Poll for events. ev, err := d.Engine.Poll() shmem.Cursor = render.NewPoint(ev.CursorX.Now, ev.CursorY.Now) - if ev.EnterKey.Now { - log.Info("MainLoop sees enter key now") - } - if ev.KeyName.Now != "" { - log.Info("MainLoop sees key %s", ev.KeyName.Now) - } if err != nil { log.Error("event poll error: %s", err) d.running = false diff --git a/pkg/level/chunker.go b/pkg/level/chunker.go index 23a1e7d..903e0cd 100644 --- a/pkg/level/chunker.go +++ b/pkg/level/chunker.go @@ -6,6 +6,7 @@ import ( "math" "git.kirsle.net/apps/doodle/lib/render" + "git.kirsle.net/apps/doodle/pkg/log" "github.com/vmihailenco/msgpack" ) @@ -224,6 +225,8 @@ func (c *Chunker) SetRect(r render.Rect, sw *Swatch) error { // Delete a pixel at the given coordinate. func (c *Chunker) Delete(p render.Point) error { coord := c.ChunkCoordinate(p) + defer c.pruneChunk(coord) + if chunk, ok := c.Chunks[coord]; ok { return chunk.Delete(p) } @@ -249,6 +252,17 @@ func (c *Chunker) DeleteRect(r render.Rect) error { return nil } +// pruneChunk will remove an empty chunk from the chunk map, called after +// delete operations. +func (c *Chunker) pruneChunk(coord render.Point) { + if chunk, ok := c.Chunks[coord]; ok { + if chunk.Len() == 0 { + log.Info("Chunker.pruneChunk: %s has become empty", coord) + delete(c.Chunks, coord) + } + } +} + // ChunkCoordinate computes a chunk coordinate from an absolute coordinate. func (c *Chunker) ChunkCoordinate(abs render.Point) render.Point { if c.Size == 0 { diff --git a/pkg/level/fmt_maintenance.go b/pkg/level/fmt_maintenance.go index de51671..076a8d5 100644 --- a/pkg/level/fmt_maintenance.go +++ b/pkg/level/fmt_maintenance.go @@ -4,6 +4,19 @@ import "git.kirsle.net/apps/doodle/pkg/log" // Maintenance functions for the file format on disk. +// PruneChunks cleans up any level chunks that have no pixel data. +func (m *Level) PruneChunks() int { + var count int + for coord, chunk := range m.Chunker.Chunks { + if chunk.Len() == 0 { + log.Info("PruneChunks: %d has no pixels", coord) + delete(m.Chunker.Chunks, coord) + count++ + } + } + return count +} + // PruneLinks cleans up any Actor Links that can not be resolved in the // level data. For example, if actors were linked in Edit Mode and one // actor is deleted leaving a broken link. diff --git a/pkg/level/fmt_readwrite.go b/pkg/level/fmt_readwrite.go index ef74afa..e2bdbc6 100644 --- a/pkg/level/fmt_readwrite.go +++ b/pkg/level/fmt_readwrite.go @@ -105,6 +105,7 @@ func (m *Level) WriteFile(filename string) error { m.GameVersion = branding.Version // Maintenance functions, clean up cruft before save. + m.PruneChunks() m.PruneLinks() bin, err := m.ToJSON()