Prepare v0.8.0 for release

This commit is contained in:
Noah 2021-09-03 21:35:12 -07:00
parent 7866f618da
commit f446ed9130
8 changed files with 104 additions and 35 deletions

View File

@ -1,15 +1,31 @@
# Changes # Changes
## v0.8.0 (TBD) ## v0.8.0 (September 3, 2021)
To Do:
* Thief needs animations
* New levels
This release brings some new features, new doodads, and new levels. This release brings some new features, new doodads, and new levels.
New features: New features:
* **Checkpoints** for gameplay will ease the pain of dying to fire
pixels or Anvils by teleporting you back to the checkpoint instead
of resetting the whole level.
* The **Doodad Properties** window while editing a doodad grants access
to many features which were previously only available via the
`doodad` tool, such as:
* Edit metadata like the Title and Author of your doodad
* Set the default hitbox of your doodad.
* Attach, open, and delete the JavaScript for your doodad
* Manage tags (key/value store) on your doodads: how you can
communicate settings to the JavaScript which can receive the
tags via `Self.GetTag("name")`
* Some **Generic Doodad Scripts** are built in. Using only the in-game
tools, it is possible to create custom doodads which have some basic
in-game logic and you don't need to write any code. The generic
scripts include:
* Generic Solid: the hitbox is solid
* Generic Fire: its hitbox harms the player
* Generic Anvil: harmless, deadly when falling
* Generic Collectible Item: it goes in your inventory
* **All Characters are Playable!** Use the Link Tool to connect your * **All Characters are Playable!** Use the Link Tool to connect your
Start Flag with another doodad on your level, and you will play Start Flag with another doodad on your level, and you will play
**as** that doodad when the level starts. The Creature doodads are **as** that doodad when the level starts. The Creature doodads are
@ -31,6 +47,18 @@ New doodads have been added:
* The **Blue Azulian** is now selectable from the Doodads menu. It * The **Blue Azulian** is now selectable from the Doodads menu. It
behaves like the Red Azulian but moves at half the speed. The behaves like the Red Azulian but moves at half the speed. The
Azulians can pick up items and open doors. Azulians can pick up items and open doors.
* The **Checkpoint Flag** will remember the player's spot in the level.
Dying to fire pixels or Anvils no longer forces a restart of the
level - you can resume from your last checkpoint, or the Start Flag
by default.
New levels have been added:
* **Castle.level:** introduces the new Thief character. Castle-themed
level showing off various new doodads.
* **Thief 1.level:** a level where you play as the Thief! You need to
steal Small Keys from dozens of Azulians and even steal items back
from another Thief who has already stolen some of the keys.
Some doodads have changed behavior: Some doodads have changed behavior:
@ -52,12 +80,11 @@ The user interface has been improved:
5. All: a classic view paging over all doodads (and doodads 5. All: a classic view paging over all doodads (and doodads
not fitting any of the above categories). not fitting any of the above categories).
doodad edit-doodad --tag "categories=doors,gizmos" filename.doodad
New functions are available in the JavaScript API for custom doodads: New functions are available in the JavaScript API for custom doodads:
* FailLevel(message string): global function that kills the player * FailLevel(message string): global function that kills the player
with a custom death message. with a custom death message.
* SetCheckpoint(Point): set the player respawn location
* Self.MoveTo(Point(x, y int)) * Self.MoveTo(Point(x, y int))
* Self.IsPlayer() bool * Self.IsPlayer() bool
* Self.SetInventory(bool): turn on or off inventory. Keys and other * Self.SetInventory(bool): turn on or off inventory. Keys and other
@ -69,6 +96,7 @@ New functions are available in the JavaScript API for custom doodads:
* Self.RemoveItem(filename string, quantity int) * Self.RemoveItem(filename string, quantity int)
* Self.HasItem(filename string) * Self.HasItem(filename string)
* Self.Inventory() map[string]int * Self.Inventory() map[string]int
* Self.Hitbox() - also see Self.Hitbox.IsEmpty()
The Events.OnLeave() callback now receives a CollideEvent argument, The Events.OnLeave() callback now receives a CollideEvent argument,
like OnCollide, instead of the useless actor ID string. Notable like OnCollide, instead of the useless actor ID string. Notable
@ -87,6 +115,12 @@ Other miscellaneous changes:
* A **death barrier** will prevent Boy from falling forever on unbounded * A **death barrier** will prevent Boy from falling forever on unbounded
maps should he somehow fall off the level. The death barrier is a maps should he somehow fall off the level. The death barrier is a
Y value 1,000 pixels below the lowest pixel on your map. Y value 1,000 pixels below the lowest pixel on your map.
* Mobile doodads no longer "moonwalk" when they change directions.
* A new color is added to all default palettes: "hint" (pink) for
writing hint notes.
* A maximum scroll speed on the "follow the player character" logic
makes for cooler animations when the character teleports around.
* Levels and Doodads are now sorted on the Open menu.
## v0.7.2 (July 19 2021) ## v0.7.2 (July 19 2021)

View File

@ -48,7 +48,7 @@ func init() {
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 2 { if c.NArg() < 2 {
return cli.NewExitError( return cli.Exit(
"Usage: doodad convert <input.png...> <output.doodad>\n"+ "Usage: doodad convert <input.png...> <output.doodad>\n"+
" Image file types: png, bmp\n"+ " Image file types: png, bmp\n"+
" Drawing file types: level, doodad", " Drawing file types: level, doodad",
@ -59,7 +59,7 @@ func init() {
// Parse the chroma key. // Parse the chroma key.
chroma, err := render.HexColor(c.String("key")) chroma, err := render.HexColor(c.String("key"))
if err != nil { if err != nil {
return cli.NewExitError( return cli.Exit(
"Chrome key not a valid color: "+err.Error(), "Chrome key not a valid color: "+err.Error(),
1, 1,
) )
@ -76,22 +76,22 @@ func init() {
if inputType == extPNG || inputType == extBMP { if inputType == extPNG || inputType == extBMP {
if outputType == extLevel || outputType == extDoodad { if outputType == extLevel || outputType == extDoodad {
if err := imageToDrawing(c, chroma, inputFiles, outputFile); err != nil { if err := imageToDrawing(c, chroma, inputFiles, outputFile); err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
return nil return nil
} }
return cli.NewExitError("Image inputs can only output to Doodle drawings", 1) return cli.Exit("Image inputs can only output to Doodle drawings", 1)
} else if inputType == extLevel || inputType == extDoodad { } else if inputType == extLevel || inputType == extDoodad {
if outputType == extPNG || outputType == extBMP { if outputType == extPNG || outputType == extBMP {
if err := drawingToImage(c, chroma, inputFiles, outputFile); err != nil { if err := drawingToImage(c, chroma, inputFiles, outputFile); err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
return nil return nil
} }
return cli.NewExitError("Doodle drawing inputs can only output to image files", 1) return cli.Exit("Doodle drawing inputs can only output to image files", 1)
} }
return cli.NewExitError("File types must be: png, bmp, level, doodad", 1) return cli.Exit("File types must be: png, bmp, level, doodad", 1)
}, },
} }
} }
@ -107,13 +107,13 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
for i, filename := range inputFiles { for i, filename := range inputFiles {
reader, err := os.Open(filename) reader, err := os.Open(filename)
if err != nil { if err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
img, format, err := image.Decode(reader) img, format, err := image.Decode(reader)
log.Info("Parsed image %d of %d. Format: %s", i+1, len(inputFiles), format) log.Info("Parsed image %d of %d. Format: %s", i+1, len(inputFiles), format)
if err != nil { if err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
// Get the bounding box information of the source image. // Get the bounding box information of the source image.
@ -131,7 +131,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
chunkSize = imageSize.Y chunkSize = imageSize.Y
} }
} else if imageSize != imageBounds { } else if imageSize != imageBounds {
return cli.NewExitError("your source images are not all the same dimensions", 1) return cli.Exit("your source images are not all the same dimensions", 1)
} }
images = append(images, img) images = append(images, img)
@ -177,7 +177,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
err := doodad.WriteJSON(outputFile) err := doodad.WriteJSON(outputFile)
if err != nil { if err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
case extLevel: case extLevel:
log.Info("Output is a Level file: %s", outputFile) log.Info("Output is a Level file: %s", outputFile)
@ -198,10 +198,10 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
err := lvl.WriteJSON(outputFile) err := lvl.WriteJSON(outputFile)
if err != nil { if err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
default: default:
return cli.NewExitError("invalid output file: not a Doodle drawing", 1) return cli.Exit("invalid output file: not a Doodle drawing", 1)
} }
return nil return nil

View File

@ -3,10 +3,12 @@ package commands
import ( import (
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/go/render"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -32,6 +34,10 @@ func init() {
Name: "author", Name: "author",
Usage: "set the doodad author", Usage: "set the doodad author",
}, },
&cli.StringFlag{
Name: "hitbox",
Usage: "set the doodad hitbox (X,Y,W,H or W,H format)",
},
&cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: "tag", Name: "tag",
Aliases: []string{"t"}, Aliases: []string{"t"},
@ -56,7 +62,7 @@ func init() {
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 1 { if c.NArg() < 1 {
return cli.NewExitError( return cli.Exit(
"Usage: doodad edit-doodad <filename.doodad>", "Usage: doodad edit-doodad <filename.doodad>",
1, 1,
) )
@ -100,6 +106,34 @@ func editDoodad(c *cli.Context, filename string) error {
modified = true modified = true
} }
if c.String("hitbox") != "" {
// Setting a hitbox, parse it out.
parts := strings.Split(c.String("hitbox"), ",")
var ints []int
for _, part := range parts {
a, err := strconv.Atoi(strings.TrimSpace(part))
if err != nil {
return err
}
ints = append(ints, a)
}
if len(ints) == 2 {
dd.Hitbox = render.NewRect(ints[0], ints[1])
modified = true
} else if len(ints) == 4 {
dd.Hitbox = render.Rect{
X: ints[0],
Y: ints[1],
W: ints[2],
H: ints[3],
}
modified = true
} else {
return cli.Exit("Hitbox should be in X,Y,W,H or just W,H format, 2 or 4 numbers.", 1)
}
}
// Tags. // Tags.
tags := c.StringSlice("tag") tags := c.StringSlice("tag")
if len(tags) > 0 { if len(tags) > 0 {
@ -152,7 +186,7 @@ func editDoodad(c *cli.Context, filename string) error {
if modified { if modified {
if err := dd.WriteJSON(filename); err != nil { if err := dd.WriteJSON(filename); err != nil {
return cli.NewExitError(fmt.Sprintf("Write error: %s", err), 1) return cli.Exit(fmt.Sprintf("Write error: %s", err), 1)
} }
} else { } else {
log.Warn("Note: No changes made to level") log.Warn("Note: No changes made to level")

View File

@ -58,7 +58,7 @@ func init() {
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 1 { if c.NArg() < 1 {
return cli.NewExitError( return cli.Exit(
"Usage: doodad edit-level <filename.level>", "Usage: doodad edit-level <filename.level>",
1, 1,
) )
@ -151,7 +151,7 @@ func editLevel(c *cli.Context, filename string) error {
if modified { if modified {
if err := lvl.WriteFile(filename); err != nil { if err := lvl.WriteFile(filename); err != nil {
return cli.NewExitError(fmt.Sprintf("Write error: %s", err), 1) return cli.Exit(fmt.Sprintf("Write error: %s", err), 1)
} }
} else { } else {
log.Warn("Note: No changes made to level") log.Warn("Note: No changes made to level")

View File

@ -26,7 +26,7 @@ func init() {
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() != 2 { if c.NArg() != 2 {
return cli.NewExitError( return cli.Exit(
"Usage: doodad install-script <script.js> <filename.doodad>", "Usage: doodad install-script <script.js> <filename.doodad>",
1, 1,
) )
@ -41,12 +41,12 @@ func init() {
// Read the JavaScript source. // Read the JavaScript source.
javascript, err := ioutil.ReadFile(scriptFile) javascript, err := ioutil.ReadFile(scriptFile)
if err != nil { if err != nil {
return cli.NewExitError(err.Error(), 1) return cli.Exit(err.Error(), 1)
} }
doodad, err := doodads.LoadJSON(doodadFile) doodad, err := doodads.LoadJSON(doodadFile)
if err != nil { if err != nil {
return cli.NewExitError( return cli.Exit(
fmt.Sprintf("Failed to read doodad file: %s", err), fmt.Sprintf("Failed to read doodad file: %s", err),
1, 1,
) )

View File

@ -46,7 +46,7 @@ func init() {
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 1 { if c.NArg() < 1 {
return cli.NewExitError( return cli.Exit(
"Usage: doodad show <.level .doodad ...>", "Usage: doodad show <.level .doodad ...>",
1, 1,
) )
@ -58,12 +58,12 @@ func init() {
case enum.LevelExt: case enum.LevelExt:
if err := showLevel(c, filename); err != nil { if err := showLevel(c, filename); err != nil {
log.Error(err.Error()) log.Error(err.Error())
return cli.NewExitError("Error", 1) return cli.Exit("Error", 1)
} }
case enum.DoodadExt: case enum.DoodadExt:
if err := showDoodad(c, filename); err != nil { if err := showDoodad(c, filename); err != nil {
log.Error(err.Error()) log.Error(err.Error())
return cli.NewExitError("Error", 1) return cli.Exit("Error", 1)
} }
default: default:
log.Error("File %s: not a level or doodad", filename) log.Error("File %s: not a level or doodad", filename)
@ -172,6 +172,7 @@ func showDoodad(c *cli.Context, filename string) error {
fmt.Printf(" Game version: %s\n", dd.GameVersion) fmt.Printf(" Game version: %s\n", dd.GameVersion)
fmt.Printf(" Doodad title: %s\n", dd.Title) fmt.Printf(" Doodad title: %s\n", dd.Title)
fmt.Printf(" Author: %s\n", dd.Author) fmt.Printf(" Author: %s\n", dd.Author)
fmt.Printf(" Hitbox: %s\n", dd.Hitbox)
fmt.Printf(" Locked: %+v\n", dd.Locked) fmt.Printf(" Locked: %+v\n", dd.Locked)
fmt.Printf(" Hidden: %+v\n", dd.Hidden) fmt.Printf(" Hidden: %+v\n", dd.Hidden)
fmt.Printf(" Script size: %d bytes\n", len(dd.Script)) fmt.Printf(" Script size: %d bytes\n", len(dd.Script))

View File

@ -4,7 +4,7 @@ package branding
const ( const (
AppName = "Sketchy Maze" AppName = "Sketchy Maze"
Summary = "A drawing-based maze game" Summary = "A drawing-based maze game"
Version = "0.7.2" Version = "0.8.0"
Website = "https://www.sketchymaze.com" Website = "https://www.sketchymaze.com"
Copyright = "2021 Noah Petherbridge" Copyright = "2021 Noah Petherbridge"
Byline = "a game by Noah Petherbridge." Byline = "a game by Noah Petherbridge."

View File

@ -385,8 +385,8 @@ func (s *MainScene) Draw(d *Doodle) error {
// Version label // Version label
s.labelVersion.MoveTo(render.Point{ s.labelVersion.MoveTo(render.Point{
X: (d.width / 2) - (s.labelVersion.Size().W / 2), X: (d.width) - (s.labelVersion.Size().W) - 20,
Y: s.labelSubtitle.Point().Y + s.labelSubtitle.Size().H + 8, Y: 20,
}) })
s.labelVersion.Present(d.Engine, s.labelVersion.Point()) s.labelVersion.Present(d.Engine, s.labelVersion.Point())