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.
This commit is contained in:
parent
6af60f1128
commit
65a811db0d
|
@ -4,6 +4,10 @@ function main() {
|
||||||
Self.SetHitbox(22+16, 16, 75-16, 86);
|
Self.SetHitbox(22+16, 16, 75-16, 86);
|
||||||
|
|
||||||
Events.OnCollide(function(e) {
|
Events.OnCollide(function(e) {
|
||||||
|
if (!e.Settled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.InHitbox) {
|
if (e.InHitbox) {
|
||||||
EndLevel();
|
EndLevel();
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,12 +114,6 @@ func (d *Doodle) Run() error {
|
||||||
// Poll for events.
|
// Poll for events.
|
||||||
ev, err := d.Engine.Poll()
|
ev, err := d.Engine.Poll()
|
||||||
shmem.Cursor = render.NewPoint(ev.CursorX.Now, ev.CursorY.Now)
|
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 {
|
if err != nil {
|
||||||
log.Error("event poll error: %s", err)
|
log.Error("event poll error: %s", err)
|
||||||
d.running = false
|
d.running = false
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/lib/render"
|
"git.kirsle.net/apps/doodle/lib/render"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
"github.com/vmihailenco/msgpack"
|
"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.
|
// Delete a pixel at the given coordinate.
|
||||||
func (c *Chunker) Delete(p render.Point) error {
|
func (c *Chunker) Delete(p render.Point) error {
|
||||||
coord := c.ChunkCoordinate(p)
|
coord := c.ChunkCoordinate(p)
|
||||||
|
defer c.pruneChunk(coord)
|
||||||
|
|
||||||
if chunk, ok := c.Chunks[coord]; ok {
|
if chunk, ok := c.Chunks[coord]; ok {
|
||||||
return chunk.Delete(p)
|
return chunk.Delete(p)
|
||||||
}
|
}
|
||||||
|
@ -249,6 +252,17 @@ func (c *Chunker) DeleteRect(r render.Rect) error {
|
||||||
return nil
|
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.
|
// ChunkCoordinate computes a chunk coordinate from an absolute coordinate.
|
||||||
func (c *Chunker) ChunkCoordinate(abs render.Point) render.Point {
|
func (c *Chunker) ChunkCoordinate(abs render.Point) render.Point {
|
||||||
if c.Size == 0 {
|
if c.Size == 0 {
|
||||||
|
|
|
@ -4,6 +4,19 @@ import "git.kirsle.net/apps/doodle/pkg/log"
|
||||||
|
|
||||||
// Maintenance functions for the file format on disk.
|
// 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
|
// 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
|
// level data. For example, if actors were linked in Edit Mode and one
|
||||||
// actor is deleted leaving a broken link.
|
// actor is deleted leaving a broken link.
|
||||||
|
|
|
@ -105,6 +105,7 @@ func (m *Level) WriteFile(filename string) error {
|
||||||
m.GameVersion = branding.Version
|
m.GameVersion = branding.Version
|
||||||
|
|
||||||
// Maintenance functions, clean up cruft before save.
|
// Maintenance functions, clean up cruft before save.
|
||||||
|
m.PruneChunks()
|
||||||
m.PruneLinks()
|
m.PruneLinks()
|
||||||
|
|
||||||
bin, err := m.ToJSON()
|
bin, err := m.ToJSON()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user