diff --git a/doodads/doodads.go b/doodads/doodads.go index 6667b68..c906034 100644 --- a/doodads/doodads.go +++ b/doodads/doodads.go @@ -30,12 +30,16 @@ type Doodad interface { type Collide struct { Top bool TopPoint render.Point + TopPixel *level.Swatch Left bool LeftPoint render.Point + LeftPixel *level.Swatch Right bool RightPoint render.Point + RightPixel *level.Swatch Bottom bool BottomPoint render.Point + BottomPixel *level.Swatch MoveTo render.Point } @@ -68,7 +72,7 @@ const ( ) // CollidesWithGrid checks if a Doodad collides with level geometry. -func CollidesWithGrid(d Doodad, grid *level.Grid, target render.Point) (*Collide, bool) { +func CollidesWithGrid(d Doodad, grid *level.Chunker, target render.Point) (*Collide, bool) { var ( P = d.Position() S = d.Size() @@ -280,7 +284,7 @@ func GetCollisionBox(box render.Rect) CollisionBox { // ScanBoundingBox scans all of the pixels in a bounding box on the grid and // returns if any of them intersect with level geometry. -func (c *Collide) ScanBoundingBox(box render.Rect, grid *level.Grid) bool { +func (c *Collide) ScanBoundingBox(box render.Rect, grid *level.Chunker) bool { col := GetCollisionBox(box) c.ScanGridLine(col.Top[0], col.Top[1], grid, Top) @@ -293,12 +297,9 @@ func (c *Collide) ScanBoundingBox(box render.Rect, grid *level.Grid) bool { // ScanGridLine scans all of the pixels between p1 and p2 on the grid and tests // for any pixels to be set, implying a collision between level geometry and the // bounding boxes of the doodad. -func (c *Collide) ScanGridLine(p1, p2 render.Point, grid *level.Grid, side Side) { +func (c *Collide) ScanGridLine(p1, p2 render.Point, grid *level.Chunker, side Side) { for point := range render.IterLine2(p1, p2) { - if grid.Exists(&level.Pixel{ - X: point.X, - Y: point.Y, - }) { + if _, err := grid.Get(point); err == nil { // A hit! switch side { case Top: diff --git a/editor_scene.go b/editor_scene.go index 5997a63..8d70f02 100644 --- a/editor_scene.go +++ b/editor_scene.go @@ -1,10 +1,12 @@ package doodle import ( + "fmt" "io/ioutil" "os" "git.kirsle.net/apps/doodle/balance" + "git.kirsle.net/apps/doodle/enum" "git.kirsle.net/apps/doodle/events" "git.kirsle.net/apps/doodle/level" "git.kirsle.net/apps/doodle/render" @@ -15,20 +17,19 @@ type EditorScene struct { // Configuration for the scene initializer. OpenFile bool Filename string - Canvas *level.Chunker UI *EditorUI + // The current level being edited. + DrawingType enum.DrawingType + Level *level.Level + // The canvas widget that contains the map we're working on. // XXX: in dev builds this is available at $ d.Scene.GetDrawing() drawing *level.Canvas - // History of all the pixels placed by the user. - filename string // Last saved filename. - - // Canvas size - width int32 - height int32 + // Last saved filename by the user. + filename string } // Name of the scene. @@ -39,27 +40,37 @@ func (s *EditorScene) Name() string { // Setup the editor scene. func (s *EditorScene) Setup(d *Doodle) error { s.drawing = level.NewCanvas(balance.ChunkSize, true) - s.drawing.Palette = level.DefaultPalette() if len(s.drawing.Palette.Swatches) > 0 { s.drawing.SetSwatch(s.drawing.Palette.Swatches[0]) } - // Were we given configuration data? + // TODO: move inside the UI. Just an approximate position for now. + s.drawing.MoveTo(render.NewPoint(0, 19)) + s.drawing.Resize(render.NewRect(d.width-150, d.height-44)) + s.drawing.Compute(d.Engine) + + // // Were we given configuration data? if s.Filename != "" { - log.Debug("EditorScene: Set filename to %s", s.Filename) + log.Debug("EditorScene.Setup: Set filename to %s", s.Filename) s.filename = s.Filename s.Filename = "" - if s.OpenFile { - log.Debug("EditorScene: Loading map from filename at %s", s.filename) - if err := s.LoadLevel(s.filename); err != nil { - d.Flash("LoadLevel error: %s", err) - } + } + if s.Level != nil { + log.Debug("EditorScene.Setup: received level from scene caller") + s.drawing.LoadLevel(s.Level) + } else if s.filename != "" && s.OpenFile { + log.Debug("EditorScene.Setup: Loading map from filename at %s", s.filename) + if err := s.LoadLevel(s.filename); err != nil { + d.Flash("LoadLevel error: %s", err) } } - if s.Canvas != nil { - log.Debug("EditorScene: Received Canvas from caller") - s.drawing.Load(s.drawing.Palette, s.Canvas) - s.Canvas = nil + + // No level? + if s.Level == nil { + log.Debug("EditorScene.Setup: initializing a new Level") + s.Level = level.New() + s.Level.Palette = level.DefaultPalette() + s.drawing.LoadLevel(s.Level) } // Initialize the user interface. It references the palette and such so it @@ -67,8 +78,6 @@ func (s *EditorScene) Setup(d *Doodle) error { s.UI = NewEditorUI(d, s) d.Flash("Editor Mode. Press 'P' to play this map.") - s.width = d.width // TODO: canvas width = copy the window size - s.height = d.height return nil } @@ -81,7 +90,8 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error { if ev.KeyName.Read() == "p" { log.Info("Play Mode, Go!") d.Goto(&PlayScene{ - // Canvas: s.drawing.Grid(), XXX + Filename: s.filename, + Level: s.Level, }) return nil } @@ -95,11 +105,6 @@ func (s *EditorScene) Draw(d *Doodle) error { d.Engine.Clear(render.Magenta) s.UI.Present(d.Engine) - - // TODO: move inside the UI. Just an approximate position for now. - s.drawing.MoveTo(render.NewPoint(0, 19)) - s.drawing.Resize(render.NewRect(d.width-150, d.height-44)) - s.drawing.Compute(d.Engine) s.drawing.Present(d.Engine, s.drawing.Point()) return nil @@ -108,8 +113,16 @@ func (s *EditorScene) Draw(d *Doodle) error { // LoadLevel loads a level from disk. func (s *EditorScene) LoadLevel(filename string) error { s.filename = filename - return s.drawing.LoadFilename(filename) + level, err := level.LoadJSON(filename) + if err != nil { + return fmt.Errorf("EditorScene.LoadLevel(%s): %s", filename, err) + } + + s.DrawingType = enum.LevelDrawing + s.Level = level + s.drawing.LoadLevel(s.Level) + return nil } // SaveLevel saves the level to disk. @@ -117,11 +130,14 @@ func (s *EditorScene) LoadLevel(filename string) error { func (s *EditorScene) SaveLevel(filename string) { s.filename = filename - m := level.New() - m.Title = "Alpha" - m.Author = os.Getenv("USER") - m.Width = s.width - m.Height = s.height + m := s.Level + if m.Title == "" { + m.Title = "Alpha" + } + if m.Author == "" { + m.Author = os.Getenv("USER") + } + m.Palette = s.drawing.Palette m.Chunker = s.drawing.Chunker() diff --git a/enum/enum.go b/enum/enum.go new file mode 100644 index 0000000..e71074c --- /dev/null +++ b/enum/enum.go @@ -0,0 +1,12 @@ +// Package enum defines all the little enum types used throughout Doodle. +package enum + +// DrawingType tells the EditorScene whether the currently open drawing is +// a Level or a Doodad. +type DrawingType int + +// EditorType values. +const ( + LevelDrawing DrawingType = iota + DoodadDrawing +) diff --git a/level/canvas.go b/level/canvas.go index 3bc592d..7b936ae 100644 --- a/level/canvas.go +++ b/level/canvas.go @@ -38,23 +38,15 @@ func NewCanvas(size int, editable bool) *Canvas { func (w *Canvas) Load(p *Palette, g *Chunker) { w.Palette = p w.chunks = g -} - -// LoadFilename initializes the Canvas using a file on disk. -func (w *Canvas) LoadFilename(filename string) error { - m, err := LoadJSON(filename) - if err != nil { - return err - } - - w.Palette = m.Palette - w.chunks = m.Chunker if len(w.Palette.Swatches) > 0 { w.SetSwatch(w.Palette.Swatches[0]) } +} - return nil +// LoadLevel initializes a Canvas from a Level object. +func (w *Canvas) LoadLevel(level *Level) { + w.Load(level.Palette, level.Chunker) } // SetSwatch changes the currently selected swatch for editing. @@ -117,10 +109,9 @@ func (w *Canvas) Loop(ev *events.State) error { Y: ev.CursorY.Now - P.Y + w.Scroll.Y, } pixel := &Pixel{ - X: cursor.X, - Y: cursor.Y, - Palette: w.Palette, - Swatch: w.Palette.ActiveSwatch, + X: cursor.X, + Y: cursor.Y, + Swatch: w.Palette.ActiveSwatch, } // Append unique new pixels. diff --git a/level/chunker.go b/level/chunker.go index c2b0afb..e4312b3 100644 --- a/level/chunker.go +++ b/level/chunker.go @@ -29,7 +29,7 @@ func NewChunker(size int) *Chunker { // on disk) to connect references to the swatches in the palette. func (c *Chunker) Inflate(pal *Palette) error { for coord, chunk := range c.Chunks { - log.Debug("Chunker.Inflate: expanding chunk %s %+v", coord, chunk) + log.Debug("Chunker.Inflate: expanding chunk %s", coord) chunk.Inflate(pal) } return nil @@ -52,7 +52,11 @@ func (c *Chunker) IterViewport(viewport render.Rect) <-chan Pixel { for cy := topLeft.Y; cy <= bottomRight.Y; cy++ { if chunk, ok := c.GetChunk(render.NewPoint(cx, cy)); ok { for px := range chunk.Iter() { - pipe <- px + + // Verify this pixel is also in range. + if px.Point().Inside(viewport) { + pipe <- px + } } } } diff --git a/level/grid.go b/level/grid.go deleted file mode 100644 index f774476..0000000 --- a/level/grid.go +++ /dev/null @@ -1,27 +0,0 @@ -package level - -import ( - "git.kirsle.net/apps/doodle/render" -) - -// Grid is a 2D grid of pixels in X,Y notation. -type Grid map[*Pixel]interface{} - -// Exists returns true if the point exists on the grid. -func (g *Grid) Exists(p *Pixel) bool { - if _, ok := (*g)[p]; ok { - return true - } - return false -} - -// Draw the grid efficiently. -func (g *Grid) Draw(e render.Engine) { - for pixel := range *g { - color := pixel.Swatch.Color - e.DrawPoint(color, render.Point{ - X: pixel.X, - Y: pixel.Y, - }) - } -} diff --git a/level/json.go b/level/json.go index 324bbf1..3ea821b 100644 --- a/level/json.go +++ b/level/json.go @@ -57,7 +57,6 @@ func LoadJSON(filename string) (*Level, error) { px, px.PaletteIndex, len(m.Palette.Swatches), ) } - px.Palette = m.Palette px.Swatch = m.Palette.Swatches[px.PaletteIndex] } return m, err diff --git a/level/types.go b/level/types.go index 40e8f96..794abb0 100644 --- a/level/types.go +++ b/level/types.go @@ -50,8 +50,7 @@ type Pixel struct { PaletteIndex int32 `json:"p"` // Private runtime values. - Palette *Palette `json:"-"` // pointer to its palette, TODO: needed? - Swatch *Swatch `json:"-"` // pointer to its swatch, for when rendered. + Swatch *Swatch `json:"-"` // pointer to its swatch, for when rendered. } func (p Pixel) String() string { diff --git a/play_scene.go b/play_scene.go index c4082d3..b5b8bee 100644 --- a/play_scene.go +++ b/play_scene.go @@ -1,6 +1,9 @@ package doodle import ( + "fmt" + + "git.kirsle.net/apps/doodle/balance" "git.kirsle.net/apps/doodle/doodads" "git.kirsle.net/apps/doodle/events" "git.kirsle.net/apps/doodle/level" @@ -11,14 +14,10 @@ import ( type PlayScene struct { // Configuration attributes. Filename string - Canvas *level.Grid + Level *level.Level // Private variables. - canvas *level.Grid - - // Canvas size - width int32 - height int32 + drawing *level.Canvas // Player character Player doodads.Doodad @@ -31,27 +30,28 @@ func (s *PlayScene) Name() string { // Setup the play scene. func (s *PlayScene) Setup(d *Doodle) error { - // Given a filename or map data to play? - if s.Canvas != nil { - log.Debug("PlayScene.Setup: loading map from given canvas") - s.canvas = s.Canvas + s.drawing = level.NewCanvas(balance.ChunkSize, false) + s.drawing.MoveTo(render.Origin) + s.drawing.Resize(render.NewRect(d.width, d.height)) + s.drawing.Compute(d.Engine) + // Given a filename or map data to play? + if s.Level != nil { + log.Debug("PlayScene.Setup: received level from scene caller") + s.drawing.LoadLevel(s.Level) } else if s.Filename != "" { log.Debug("PlayScene.Setup: loading map from file %s", s.Filename) s.LoadLevel(s.Filename) - s.Filename = "" } s.Player = doodads.NewPlayer() - if s.canvas == nil { + if s.Level == nil { log.Debug("PlayScene.Setup: no grid given, initializing empty grid") - s.canvas = &level.Grid{} + s.Level = level.New() + s.drawing.LoadLevel(s.Level) } - s.width = d.width // TODO: canvas width = copy the window size - s.height = d.height - d.Flash("Entered Play Mode. Press 'E' to edit this map.") return nil @@ -63,11 +63,13 @@ func (s *PlayScene) Loop(d *Doodle, ev *events.State) error { if ev.KeyName.Read() == "e" { log.Info("Edit Mode, Go!") d.Goto(&EditorScene{ - // Canvas: s.canvas, + Filename: s.Filename, + Level: s.Level, }) return nil } + // s.drawing.Loop(ev) s.movePlayer(ev) return nil } @@ -77,7 +79,8 @@ func (s *PlayScene) Draw(d *Doodle) error { // Clear the canvas and fill it with white. d.Engine.Clear(render.White) - s.canvas.Draw(d.Engine) + // Draw the level. + s.drawing.Present(d.Engine, s.drawing.Point()) // Draw our hero. s.Player.Draw(d.Engine) @@ -110,7 +113,7 @@ func (s *PlayScene) movePlayer(ev *events.State) { // Apply gravity. // var onFloor bool - info, ok := doodads.CollidesWithGrid(s.Player, s.canvas, delta) + info, ok := doodads.CollidesWithGrid(s.Player, s.Level.Chunker, delta) if ok { // Collision happened with world. } @@ -128,16 +131,15 @@ func (s *PlayScene) movePlayer(ev *events.State) { // LoadLevel loads a level from disk. func (s *PlayScene) LoadLevel(filename string) error { - s.canvas = &level.Grid{} + s.Filename = filename - // m, err := level.LoadJSON(filename) - // if err != nil { - // return err - // } + level, err := level.LoadJSON(filename) + if err != nil { + return fmt.Errorf("PlayScene.LoadLevel(%s): %s", filename, err) + } - // for _, pixel := range m.Pixels { - // // *s.canvas[pixel] = nil - // } + s.Level = level + s.drawing.LoadLevel(s.Level) return nil } diff --git a/render/point.go b/render/point.go index dcb16cf..ac10d12 100644 --- a/render/point.go +++ b/render/point.go @@ -12,6 +12,11 @@ type Point struct { Y int32 } +// Common points. +var ( + Origin Point +) + // NewPoint makes a new Point at an X,Y coordinate. func NewPoint(x, y int32) Point { return Point{