doodle/editor_scene.go
Noah Petherbridge e141203c4b Basic Collision Detection, Toggle Between Play/Edit
Known bugs:
* The Pixel format in the Grid has DX and DY attributes and
  it wreaks havoc on collision detection in Play Mode when you
  come straight from the editor. Reloading the map from disk to
  play is OK cuz it lacks these attrs.
2018-07-23 20:10:53 -07:00

281 lines
6.1 KiB
Go

package doodle
import (
"fmt"
"image"
"image/png"
"io/ioutil"
"os"
"time"
"git.kirsle.net/apps/doodle/draw"
"git.kirsle.net/apps/doodle/events"
"git.kirsle.net/apps/doodle/level"
"git.kirsle.net/apps/doodle/render"
)
// EditorScene manages the "Edit Level" game mode.
type EditorScene struct {
// Configuration for the scene initializer.
OpenFile bool
Filename string
Canvas render.Grid
// History of all the pixels placed by the user.
pixelHistory []render.Pixel
canvas render.Grid
filename string // Last saved filename.
// Canvas size
width int32
height int32
}
// Name of the scene.
func (s *EditorScene) Name() string {
return "Edit"
}
// Setup the editor scene.
func (s *EditorScene) Setup(d *Doodle) error {
// Were we given configuration data?
if s.Filename != "" {
log.Debug("EditorScene: 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 {
return err
}
}
}
if s.Canvas != nil {
log.Debug("EditorScene: Received Canvas from caller")
s.canvas = s.Canvas
s.Canvas = nil
}
d.Flash("Editor Mode. Press 'P' to play this map.")
if s.pixelHistory == nil {
s.pixelHistory = []render.Pixel{}
}
if s.canvas == nil {
log.Debug("EditorScene: Setting default canvas to an empty grid")
s.canvas = render.Grid{}
}
s.width = d.width // TODO: canvas width = copy the window size
s.height = d.height
return nil
}
// Loop the editor scene.
func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
// Taking a screenshot?
if ev.ScreenshotKey.Pressed() {
log.Info("Taking a screenshot")
s.Screenshot()
}
// Switching to Play Mode?
if ev.KeyName.Read() == "p" {
log.Info("Play Mode, Go!")
d.Goto(&PlayScene{
Canvas: s.canvas,
})
return nil
}
// Clear the canvas and fill it with white.
d.Engine.Clear(render.White)
// Clicking? Log all the pixels while doing so.
if ev.Button1.Now {
log.Warn("Button1: %+v", ev.Button1)
pixel := render.Pixel{
Start: ev.Button1.Pressed(),
X: ev.CursorX.Now,
Y: ev.CursorY.Now,
DX: ev.CursorX.Last,
DY: ev.CursorY.Last,
}
if pixel.Start {
log.Warn("START PIXEL %+v", pixel)
}
// Append unique new pixels.
if len(s.pixelHistory) == 0 || s.pixelHistory[len(s.pixelHistory)-1] != pixel {
// If not a start pixel, make the delta coord the previous one.
if !pixel.Start && len(s.pixelHistory) > 0 {
prev := s.pixelHistory[len(s.pixelHistory)-1]
pixel.DY = prev.Y
pixel.DX = prev.X
}
s.pixelHistory = append(s.pixelHistory, pixel)
// Save in the pixel canvas map.
fmt.Printf("%+v", pixel)
s.canvas[pixel] = nil
}
}
return nil
}
// Draw the current frame.
func (s *EditorScene) Draw(d *Doodle) error {
// for i, pixel := range s.pixelHistory {
// if !pixel.Start && i > 0 {
// prev := s.pixelHistory[i-1]
// if prev.X == pixel.X && prev.Y == pixel.Y {
// d.Engine.DrawPoint(
// render.Black,
// render.Point{pixel.X, pixel.Y},
// )
// } else {
// d.Engine.DrawLine(
// render.Black,
// render.Point{pixel.X, pixel.Y},
// render.Point{prev.X, prev.Y},
// )
// }
// }
// d.Engine.DrawPoint(render.Black, render.Point{pixel.X, pixel.Y})
// }
s.canvas.Draw(d.Engine)
return nil
}
// LoadLevel loads a level from disk.
func (s *EditorScene) LoadLevel(filename string) error {
s.filename = filename
s.pixelHistory = []render.Pixel{}
s.canvas = render.Grid{}
m, err := level.LoadJSON(filename)
if err != nil {
return err
}
for _, point := range m.Pixels {
pixel := render.Pixel{
Start: true,
X: point.X,
Y: point.Y,
DX: point.X,
DY: point.Y,
}
s.pixelHistory = append(s.pixelHistory, pixel)
s.canvas[pixel] = nil
}
return nil
}
// SaveLevel saves the level to disk.
func (s *EditorScene) SaveLevel(filename string) {
s.filename = filename
m := level.Level{
Version: 1,
Title: "Alpha",
Author: os.Getenv("USER"),
Width: s.width,
Height: s.height,
Palette: []level.Palette{
level.Palette{
Color: "#000000",
Solid: true,
},
},
Pixels: []level.Pixel{},
}
for pixel := range s.canvas {
if pixel.DX == 0 && pixel.DY == 0 {
m.Pixels = append(m.Pixels, level.Pixel{
X: pixel.X,
Y: pixel.Y,
Palette: 0,
})
} else {
for point := range render.IterLine(pixel.X, pixel.Y, pixel.DX, pixel.DY) {
m.Pixels = append(m.Pixels, level.Pixel{
X: point.X,
Y: point.Y,
Palette: 0,
})
}
}
}
json, err := m.ToJSON()
if err != nil {
log.Error("SaveLevel error: %s", err)
return
}
err = ioutil.WriteFile(filename, json, 0644)
if err != nil {
log.Error("Create map file error: %s", err)
return
}
}
// Screenshot saves the level canvas to disk as a PNG image.
func (s *EditorScene) Screenshot() {
screenshot := image.NewRGBA(image.Rect(0, 0, int(s.width), int(s.height)))
// White-out the image.
for x := 0; x < int(s.width); x++ {
for y := 0; y < int(s.height); y++ {
screenshot.Set(x, y, image.White)
}
}
// Fill in the dots we drew.
for pixel := range s.canvas {
// A line or a dot?
if pixel.DX == 0 && pixel.DY == 0 {
screenshot.Set(int(pixel.X), int(pixel.Y), image.Black)
} else {
for point := range draw.Line(pixel.X, pixel.Y, pixel.DX, pixel.DY) {
screenshot.Set(int(point.X), int(point.Y), image.Black)
}
}
}
// Create the screenshot directory.
if _, err := os.Stat("./screenshots"); os.IsNotExist(err) {
log.Info("Creating directory: ./screenshots")
err = os.Mkdir("./screenshots", 0755)
if err != nil {
log.Error("Can't create ./screenshots: %s", err)
return
}
}
filename := fmt.Sprintf("./screenshots/screenshot-%s.png",
time.Now().Format("2006-01-02T15-04-05"),
)
fh, err := os.Create(filename)
if err != nil {
log.Error(err.Error())
return
}
defer fh.Close()
if err := png.Encode(fh, screenshot); err != nil {
log.Error(err.Error())
return
}
}
// Destroy the scene.
func (s *EditorScene) Destroy() error {
return nil
}