2018-06-21 01:43:14 +00:00
|
|
|
package doodle
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"image"
|
|
|
|
"image/png"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
2018-07-22 00:12:22 +00:00
|
|
|
"git.kirsle.net/apps/doodle/events"
|
2018-06-21 01:43:14 +00:00
|
|
|
"git.kirsle.net/apps/doodle/level"
|
2018-07-22 00:12:22 +00:00
|
|
|
"git.kirsle.net/apps/doodle/render"
|
2018-06-21 01:43:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// EditorScene manages the "Edit Level" game mode.
|
|
|
|
type EditorScene struct {
|
2018-07-24 03:10:53 +00:00
|
|
|
// Configuration for the scene initializer.
|
|
|
|
OpenFile bool
|
|
|
|
Filename string
|
2018-08-11 00:19:47 +00:00
|
|
|
Canvas level.Grid
|
2018-07-24 03:10:53 +00:00
|
|
|
|
2018-08-05 19:54:57 +00:00
|
|
|
UI *EditorUI
|
|
|
|
|
2018-08-11 00:19:47 +00:00
|
|
|
Palette *level.Palette // Full palette of swatches for this level
|
|
|
|
Swatch *level.Swatch // actively selected painting swatch
|
|
|
|
|
2018-06-21 01:43:14 +00:00
|
|
|
// History of all the pixels placed by the user.
|
2018-08-11 00:19:47 +00:00
|
|
|
pixelHistory []*level.Pixel
|
2018-07-25 00:44:32 +00:00
|
|
|
lastPixel *level.Pixel // last pixel placed while mouse down and dragging
|
2018-08-11 00:19:47 +00:00
|
|
|
canvas level.Grid
|
2018-07-22 03:43:01 +00:00
|
|
|
filename string // Last saved filename.
|
2018-06-21 01:43:14 +00:00
|
|
|
|
|
|
|
// Canvas size
|
|
|
|
width int32
|
|
|
|
height int32
|
|
|
|
}
|
|
|
|
|
2018-06-21 02:00:46 +00:00
|
|
|
// Name of the scene.
|
|
|
|
func (s *EditorScene) Name() string {
|
|
|
|
return "Edit"
|
|
|
|
}
|
|
|
|
|
2018-06-21 01:43:14 +00:00
|
|
|
// Setup the editor scene.
|
2018-07-21 22:11:00 +00:00
|
|
|
func (s *EditorScene) Setup(d *Doodle) error {
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
s.Palette = level.DefaultPalette()
|
|
|
|
|
2018-07-24 03:10:53 +00:00
|
|
|
// 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 {
|
2018-08-11 00:19:47 +00:00
|
|
|
d.Flash("LoadLevel error: %s", err)
|
2018-07-24 03:10:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if s.Canvas != nil {
|
|
|
|
log.Debug("EditorScene: Received Canvas from caller")
|
|
|
|
s.canvas = s.Canvas
|
|
|
|
s.Canvas = nil
|
|
|
|
}
|
|
|
|
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
// Select the first swatch in the palette.
|
2018-08-11 00:19:47 +00:00
|
|
|
if len(s.Palette.Swatches) > 0 {
|
|
|
|
s.Swatch = s.Palette.Swatches[0]
|
|
|
|
s.Palette.ActiveSwatch = s.Swatch.Name
|
|
|
|
}
|
2018-08-05 19:54:57 +00:00
|
|
|
|
2018-08-11 00:19:47 +00:00
|
|
|
// Initialize the user interface. It references the palette and such so it
|
|
|
|
// must be initialized after those things.
|
|
|
|
s.UI = NewEditorUI(d, s)
|
2018-07-24 03:10:53 +00:00
|
|
|
d.Flash("Editor Mode. Press 'P' to play this map.")
|
|
|
|
|
2018-07-21 22:11:00 +00:00
|
|
|
if s.pixelHistory == nil {
|
2018-08-11 00:19:47 +00:00
|
|
|
s.pixelHistory = []*level.Pixel{}
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
2018-07-21 22:11:00 +00:00
|
|
|
if s.canvas == nil {
|
2018-07-24 03:10:53 +00:00
|
|
|
log.Debug("EditorScene: Setting default canvas to an empty grid")
|
2018-08-11 00:19:47 +00:00
|
|
|
s.canvas = level.Grid{}
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
2018-07-21 22:11:00 +00:00
|
|
|
s.width = d.width // TODO: canvas width = copy the window size
|
|
|
|
s.height = d.height
|
2018-06-21 01:43:14 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop the editor scene.
|
2018-07-22 00:12:22 +00:00
|
|
|
func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
|
2018-08-05 19:54:57 +00:00
|
|
|
s.UI.Loop(ev)
|
|
|
|
|
2018-06-21 01:43:14 +00:00
|
|
|
// Taking a screenshot?
|
|
|
|
if ev.ScreenshotKey.Pressed() {
|
|
|
|
log.Info("Taking a screenshot")
|
2018-07-21 22:11:00 +00:00
|
|
|
s.Screenshot()
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
|
|
|
|
2018-07-24 03:10:53 +00:00
|
|
|
// Switching to Play Mode?
|
|
|
|
if ev.KeyName.Read() == "p" {
|
|
|
|
log.Info("Play Mode, Go!")
|
|
|
|
d.Goto(&PlayScene{
|
|
|
|
Canvas: s.canvas,
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-21 01:43:14 +00:00
|
|
|
// Clicking? Log all the pixels while doing so.
|
|
|
|
if ev.Button1.Now {
|
2018-07-25 00:44:32 +00:00
|
|
|
// log.Warn("Button1: %+v", ev.Button1)
|
|
|
|
lastPixel := s.lastPixel
|
2018-08-11 00:19:47 +00:00
|
|
|
pixel := &level.Pixel{
|
|
|
|
X: ev.CursorX.Now,
|
|
|
|
Y: ev.CursorY.Now,
|
|
|
|
Palette: s.Palette,
|
|
|
|
Swatch: s.Swatch,
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Append unique new pixels.
|
2018-07-21 22:11:00 +00:00
|
|
|
if len(s.pixelHistory) == 0 || s.pixelHistory[len(s.pixelHistory)-1] != pixel {
|
2018-07-25 00:44:32 +00:00
|
|
|
if lastPixel != nil {
|
|
|
|
// Draw the pixels in between.
|
2018-08-11 00:19:47 +00:00
|
|
|
if lastPixel != pixel {
|
2018-07-25 00:44:32 +00:00
|
|
|
for point := range render.IterLine(lastPixel.X, lastPixel.Y, pixel.X, pixel.Y) {
|
2018-08-11 00:19:47 +00:00
|
|
|
dot := &level.Pixel{
|
2018-07-25 00:44:32 +00:00
|
|
|
X: point.X,
|
|
|
|
Y: point.Y,
|
|
|
|
Palette: lastPixel.Palette,
|
2018-08-11 00:19:47 +00:00
|
|
|
Swatch: lastPixel.Swatch,
|
2018-07-25 00:44:32 +00:00
|
|
|
}
|
|
|
|
s.canvas[dot] = nil
|
|
|
|
}
|
|
|
|
}
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
|
|
|
|
2018-08-11 00:19:47 +00:00
|
|
|
s.lastPixel = pixel
|
2018-07-21 22:11:00 +00:00
|
|
|
s.pixelHistory = append(s.pixelHistory, pixel)
|
2018-06-21 01:43:14 +00:00
|
|
|
|
|
|
|
// Save in the pixel canvas map.
|
2018-07-21 22:11:00 +00:00
|
|
|
s.canvas[pixel] = nil
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
2018-07-25 00:44:32 +00:00
|
|
|
} else {
|
|
|
|
s.lastPixel = nil
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
|
|
|
|
2018-07-22 03:43:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the current frame.
|
|
|
|
func (s *EditorScene) Draw(d *Doodle) error {
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
// Clear the canvas and fill it with white.
|
|
|
|
d.Engine.Clear(render.White)
|
|
|
|
|
2018-07-24 03:10:53 +00:00
|
|
|
s.canvas.Draw(d.Engine)
|
2018-08-05 19:54:57 +00:00
|
|
|
s.UI.Present(d.Engine)
|
2018-06-21 01:43:14 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadLevel loads a level from disk.
|
2018-07-21 22:11:00 +00:00
|
|
|
func (s *EditorScene) LoadLevel(filename string) error {
|
2018-07-22 03:43:01 +00:00
|
|
|
s.filename = filename
|
2018-08-11 00:19:47 +00:00
|
|
|
s.pixelHistory = []*level.Pixel{}
|
|
|
|
s.canvas = level.Grid{}
|
2018-06-21 01:43:14 +00:00
|
|
|
|
|
|
|
m, err := level.LoadJSON(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-11 00:19:47 +00:00
|
|
|
s.Palette = m.Palette
|
|
|
|
if len(s.Palette.Swatches) > 0 {
|
|
|
|
s.Swatch = m.Palette.Swatches[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, pixel := range m.Pixels {
|
2018-07-21 22:11:00 +00:00
|
|
|
s.pixelHistory = append(s.pixelHistory, pixel)
|
|
|
|
s.canvas[pixel] = nil
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SaveLevel saves the level to disk.
|
2018-07-22 03:43:01 +00:00
|
|
|
func (s *EditorScene) SaveLevel(filename string) {
|
|
|
|
s.filename = filename
|
2018-08-11 00:19:47 +00:00
|
|
|
|
|
|
|
m := level.New()
|
|
|
|
m.Title = "Alpha"
|
|
|
|
m.Author = os.Getenv("USER")
|
|
|
|
m.Width = s.width
|
|
|
|
m.Height = s.height
|
|
|
|
m.Palette = s.Palette
|
2018-06-21 01:43:14 +00:00
|
|
|
|
2018-07-21 22:11:00 +00:00
|
|
|
for pixel := range s.canvas {
|
2018-08-11 00:19:47 +00:00
|
|
|
m.Pixels = append(m.Pixels, &level.Pixel{
|
|
|
|
X: pixel.X,
|
|
|
|
Y: pixel.Y,
|
|
|
|
PaletteIndex: int32(pixel.Swatch.Index()),
|
2018-07-25 00:44:32 +00:00
|
|
|
})
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2018-07-21 22:11:00 +00:00
|
|
|
func (s *EditorScene) Screenshot() {
|
|
|
|
screenshot := image.NewRGBA(image.Rect(0, 0, int(s.width), int(s.height)))
|
2018-06-21 01:43:14 +00:00
|
|
|
|
|
|
|
// White-out the image.
|
2018-07-21 22:11:00 +00:00
|
|
|
for x := 0; x < int(s.width); x++ {
|
|
|
|
for y := 0; y < int(s.height); y++ {
|
2018-06-21 01:43:14 +00:00
|
|
|
screenshot.Set(x, y, image.White)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill in the dots we drew.
|
2018-07-21 22:11:00 +00:00
|
|
|
for pixel := range s.canvas {
|
2018-07-25 00:44:32 +00:00
|
|
|
screenshot.Set(int(pixel.X), int(pixel.Y), image.Black)
|
2018-06-21 01:43:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
2018-07-24 03:10:53 +00:00
|
|
|
|
|
|
|
// Destroy the scene.
|
|
|
|
func (s *EditorScene) Destroy() error {
|
|
|
|
return nil
|
|
|
|
}
|