Add JS + History to Shell, Add Main Scene
* The shell now supports an "eval" command, or "$" for short. * Runs it in an Otto JavaScript VM. * Some global variables are available, like `d` is the Doodle object itself, `log`, `RGBA()` and `Point()` * The shell supports paging through input history using the arrow keys. * Added an initial Main Scene
This commit is contained in:
parent
94c1df050b
commit
41e1838549
46
Debug Notes.md
Normal file
46
Debug Notes.md
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Debug Notes
|
||||||
|
|
||||||
|
## Entering Debug Mode
|
||||||
|
|
||||||
|
Command line argument:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
% doodle -debug
|
||||||
|
|
||||||
|
# running the dev build uses debug mode by default
|
||||||
|
% make run
|
||||||
|
```
|
||||||
|
|
||||||
|
In the developer console:
|
||||||
|
|
||||||
|
```dos
|
||||||
|
> boolProp Debug true
|
||||||
|
> boolProp D true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debug Options
|
||||||
|
|
||||||
|
The `boolProp` command can also be used to toggle on and off different
|
||||||
|
debug options while the game is running.
|
||||||
|
|
||||||
|
```
|
||||||
|
DebugOverlay
|
||||||
|
DO
|
||||||
|
Toggles the main debug text overlay with FPS counter.
|
||||||
|
|
||||||
|
DebugCollision
|
||||||
|
DC
|
||||||
|
Toggles the collision detection bounding box lines.
|
||||||
|
```
|
||||||
|
|
||||||
|
## JavaScript Shell
|
||||||
|
|
||||||
|
The developer console can parse JavaScript commands for more access to the
|
||||||
|
game's internal objects.
|
||||||
|
|
||||||
|
The following global variables are available to the shell:
|
||||||
|
|
||||||
|
* `d` is the master Doodle struct.
|
||||||
|
* `log` is the master logger object for logging messages to the terminal.
|
||||||
|
* `RGBA()` is the `render.RGBA()` function for creating a Color value.
|
||||||
|
* `Point(x, y)` to create a `render.Point`
|
51
README.md
51
README.md
|
@ -139,17 +139,54 @@ As a rough idea of the milestones needed for this game to work:
|
||||||
for the rest of this program.
|
for the rest of this program.
|
||||||
* [x] Labels
|
* [x] Labels
|
||||||
* [ ] Buttons (text only is OK)
|
* [ ] Buttons (text only is OK)
|
||||||
* [x] Buttons wrap their Label and dynamically compute their size based
|
* [x] Buttons wrap their Label and dynamically compute their size based
|
||||||
on how wide the label will render, plus padding and border.
|
on how wide the label will render, plus padding and border.
|
||||||
* [x] Border colors and widths and paddings are all configurable.
|
* [x] Border colors and widths and paddings are all configurable.
|
||||||
* [ ] Buttons should interact with the cursor and be hoverable and
|
* [ ] Buttons should interact with the cursor and be hoverable and
|
||||||
clickable.
|
clickable.
|
||||||
* [ ] UI Manager that will keep track of buttons to know when the mouse
|
* [ ] UI Manager that will keep track of buttons to know when the mouse
|
||||||
is interacting with them.
|
is interacting with them.
|
||||||
* [ ] Frames
|
* [ ] Frames
|
||||||
|
* Like Buttons, can have border (raised, sunken or solid), padding and
|
||||||
|
background color.
|
||||||
|
* [ ] Should be able to size themselves dynamically based on child widgets.
|
||||||
* [ ] Windows (fixed, non-draggable is OK)
|
* [ ] Windows (fixed, non-draggable is OK)
|
||||||
* [ ] Expand the Palette support in levels for solid vs. transparent, fire,
|
* [ ] Title bar with label
|
||||||
etc. with UI toolbar to choose palettes.
|
* [ ] Window body implements a Frame that contains child widgets.
|
||||||
|
* [ ] Window can resize itself dynamically based on the Frame.
|
||||||
|
* [ ] Create a "Main Menu" scene with buttons to enter a new Edit Mode,
|
||||||
|
play an existing map from disk, etc.
|
||||||
|
* [ ] Add user interface Frames or Windows to the Edit Mode.
|
||||||
|
* [ ] A toolbar of buttons (New, Save, Open, Play) can be drawn at the top
|
||||||
|
before the UI toolkit gains a proper MenuBar widget.
|
||||||
|
* [ ] Expand the Palette support in levels for solid vs. transparent, fire,
|
||||||
|
etc. with UI toolbar to choose palettes.
|
||||||
|
|
||||||
|
Lesser important UI features that can come at any later time:
|
||||||
|
|
||||||
|
* [ ] MenuBar widget with drop-down menu support.
|
||||||
|
* [ ] Checkbox and Radiobox widgets.
|
||||||
|
* [ ] Text Entry widgets (in the meantime use the Developer Shell to prompt for
|
||||||
|
text input questions)
|
||||||
|
|
||||||
|
## Doodad Editor
|
||||||
|
|
||||||
|
* [ ] The Edit Mode should support creating drawings for Doodads.
|
||||||
|
* [ ] It should know whether you're drawing a Map or a Doodad as some
|
||||||
|
behaviors may need to be different between the two.
|
||||||
|
* [ ] Compress the coordinates down toward `(0,0)` when saving a Doodad,
|
||||||
|
by finding the toppest, leftest point and making that `(0,0)` and adjusting
|
||||||
|
the rest accordingly. This will help trim down Doodads into the smallest
|
||||||
|
possible space for easy collision detection.
|
||||||
|
* [ ] Add a UX to edit multiple frames for a Doodad.
|
||||||
|
* [ ] Edit Mode should be able to fully save the drawings and frames, and an
|
||||||
|
external CLI tool can install the JavaScript into them.
|
||||||
|
* [ ] Possible UX to toggle Doodad options, like its collision rules and
|
||||||
|
whether the Doodad is continued to be "mobile" (i.e. doors and buttons won't
|
||||||
|
move, but items and enemies may be able to; and non-mobile Doodads don't
|
||||||
|
need to collision check against level geometry).
|
||||||
|
* [ ] Edit Mode should have a Doodad Palette (Frame or Window) to drag
|
||||||
|
Doodads into the map.
|
||||||
* [ ] ???
|
* [ ] ???
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
46
commands.go
46
commands.go
|
@ -3,6 +3,7 @@ package doodle
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command is a parsed shell command.
|
// Command is a parsed shell command.
|
||||||
|
@ -36,6 +37,13 @@ func (c Command) Run(d *Doodle) error {
|
||||||
return c.Quit()
|
return c.Quit()
|
||||||
case "help":
|
case "help":
|
||||||
return c.Help(d)
|
return c.Help(d)
|
||||||
|
case "eval":
|
||||||
|
case "$":
|
||||||
|
out, err := d.shell.js.Run(c.ArgsLiteral)
|
||||||
|
d.Flash("%+v", out)
|
||||||
|
return err
|
||||||
|
case "boolProp":
|
||||||
|
return c.BoolProp(d)
|
||||||
default:
|
default:
|
||||||
return c.Default()
|
return c.Default()
|
||||||
}
|
}
|
||||||
|
@ -91,7 +99,7 @@ func (c Command) Help(d *Doodle) error {
|
||||||
|
|
||||||
// Save the current map to disk.
|
// Save the current map to disk.
|
||||||
func (c Command) Save(d *Doodle) error {
|
func (c Command) Save(d *Doodle) error {
|
||||||
if scene, ok := d.scene.(*EditorScene); ok {
|
if scene, ok := d.Scene.(*EditorScene); ok {
|
||||||
filename := ""
|
filename := ""
|
||||||
if len(c.Args) > 0 {
|
if len(c.Args) > 0 {
|
||||||
filename = c.Args[0]
|
filename = c.Args[0]
|
||||||
|
@ -139,6 +147,42 @@ func (c Command) Quit() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BoolProp command sets available boolean variables.
|
||||||
|
func (c Command) BoolProp(d *Doodle) error {
|
||||||
|
if len(c.Args) != 2 {
|
||||||
|
return errors.New("Usage: boolProp <name> <true or false>")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
name = c.Args[0]
|
||||||
|
value = c.Args[1]
|
||||||
|
truthy = value[0] == 't' || value[0] == 'T' || value[0] == '1'
|
||||||
|
ok = true
|
||||||
|
)
|
||||||
|
|
||||||
|
switch name {
|
||||||
|
case "Debug":
|
||||||
|
case "D":
|
||||||
|
d.Debug = truthy
|
||||||
|
case "DebugOverlay":
|
||||||
|
case "DO":
|
||||||
|
DebugOverlay = truthy
|
||||||
|
case "DebugCollision":
|
||||||
|
case "DC":
|
||||||
|
DebugCollision = truthy
|
||||||
|
default:
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
d.Flash("Set boolProp %s=%s", name, strconv.FormatBool(truthy))
|
||||||
|
} else {
|
||||||
|
d.Flash("Unknown boolProp name %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Default command.
|
// Default command.
|
||||||
func (c Command) Default() error {
|
func (c Command) Default() error {
|
||||||
return fmt.Errorf("%s: command not found. Try `help` for help",
|
return fmt.Errorf("%s: command not found. Try `help` for help",
|
||||||
|
|
12
doodle.go
12
doodle.go
|
@ -32,7 +32,7 @@ type Doodle struct {
|
||||||
// Command line shell options.
|
// Command line shell options.
|
||||||
shell Shell
|
shell Shell
|
||||||
|
|
||||||
scene Scene
|
Scene Scene
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes the game object.
|
// New initializes the game object.
|
||||||
|
@ -62,8 +62,8 @@ func (d *Doodle) Run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the default scene.
|
// Set up the default scene.
|
||||||
if d.scene == nil {
|
if d.Scene == nil {
|
||||||
d.Goto(&EditorScene{})
|
d.Goto(&MainScene{})
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Enter Main Loop")
|
log.Info("Enter Main Loop")
|
||||||
|
@ -96,14 +96,14 @@ func (d *Doodle) Run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the scene's logic.
|
// Run the scene's logic.
|
||||||
err = d.scene.Loop(d, ev)
|
err = d.Scene.Loop(d, ev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the scene.
|
// Draw the scene.
|
||||||
d.scene.Draw(d)
|
d.Scene.Draw(d)
|
||||||
|
|
||||||
// Draw the shell.
|
// Draw the shell.
|
||||||
err = d.shell.Draw(d, ev)
|
err = d.shell.Draw(d, ev)
|
||||||
|
@ -114,7 +114,7 @@ func (d *Doodle) Run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the debug overlay over all scenes.
|
// Draw the debug overlay over all scenes.
|
||||||
// d.DrawDebugOverlay()
|
d.DrawDebugOverlay()
|
||||||
|
|
||||||
// Render the pixels to the screen.
|
// Render the pixels to the screen.
|
||||||
err = d.Engine.Present()
|
err = d.Engine.Present()
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/events"
|
"git.kirsle.net/apps/doodle/events"
|
||||||
"git.kirsle.net/apps/doodle/level"
|
"git.kirsle.net/apps/doodle/level"
|
||||||
"git.kirsle.net/apps/doodle/render"
|
"git.kirsle.net/apps/doodle/render"
|
||||||
"git.kirsle.net/apps/doodle/ui"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EditorScene manages the "Edit Level" game mode.
|
// EditorScene manages the "Edit Level" game mode.
|
||||||
|
@ -133,59 +132,6 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
|
||||||
func (s *EditorScene) Draw(d *Doodle) error {
|
func (s *EditorScene) Draw(d *Doodle) error {
|
||||||
s.canvas.Draw(d.Engine)
|
s.canvas.Draw(d.Engine)
|
||||||
|
|
||||||
label := ui.NewLabel(render.Text{
|
|
||||||
Text: "Hello UI toolkit!",
|
|
||||||
Size: 26,
|
|
||||||
Color: render.Pink,
|
|
||||||
Stroke: render.SkyBlue,
|
|
||||||
Shadow: render.Black,
|
|
||||||
})
|
|
||||||
label.SetPoint(render.NewPoint(128, 128))
|
|
||||||
label.Compute(d.Engine)
|
|
||||||
log.Info("Label rect: %+v", label.Size())
|
|
||||||
log.Info("Label at: %s", label.Point())
|
|
||||||
label.Present(d.Engine)
|
|
||||||
|
|
||||||
button := ui.NewButton(*ui.NewLabel(render.Text{
|
|
||||||
Text: "Hello",
|
|
||||||
Size: 14,
|
|
||||||
Color: render.Black,
|
|
||||||
}))
|
|
||||||
button.SetPoint(render.NewPoint(200, 200))
|
|
||||||
button.Present(d.Engine)
|
|
||||||
|
|
||||||
// Point and size of that button
|
|
||||||
point := button.Point()
|
|
||||||
size := button.Size()
|
|
||||||
|
|
||||||
button2 := ui.NewButton(*ui.NewLabel(render.Text{
|
|
||||||
Text: "World!",
|
|
||||||
Size: 14,
|
|
||||||
Color: render.Blue,
|
|
||||||
}))
|
|
||||||
button2.SetPoint(render.Point{
|
|
||||||
X: point.X + size.W,
|
|
||||||
Y: point.Y,
|
|
||||||
})
|
|
||||||
button2.Present(d.Engine)
|
|
||||||
|
|
||||||
button.SetText("Buttons that don't click yet")
|
|
||||||
button.SetPoint(render.NewPoint(250, 300))
|
|
||||||
button.Label.Text.Size = 24
|
|
||||||
button.Border = 8
|
|
||||||
button.Outline = 4
|
|
||||||
button.Present(d.Engine)
|
|
||||||
|
|
||||||
button2.SetText("Multiple colors, too")
|
|
||||||
button2.Label.Text.Color = render.White
|
|
||||||
button2.Background = render.RGBA(0, 153, 255, 255)
|
|
||||||
button2.HighlightColor = render.RGBA(100, 200, 255, 255)
|
|
||||||
button2.ShadowColor = render.RGBA(0, 100, 153, 255)
|
|
||||||
button2.SetPoint(render.NewPoint(10, 300))
|
|
||||||
button2.Present(d.Engine)
|
|
||||||
|
|
||||||
_ = label
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
fps.go
22
fps.go
|
@ -10,6 +10,13 @@ import (
|
||||||
// Frames to cache for FPS calculation.
|
// Frames to cache for FPS calculation.
|
||||||
const maxSamples = 100
|
const maxSamples = 100
|
||||||
|
|
||||||
|
// Debug mode options, these can be enabled in the dev console
|
||||||
|
// like: boolProp DebugOverlay true
|
||||||
|
var (
|
||||||
|
DebugOverlay = true
|
||||||
|
DebugCollision = true
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fpsCurrentTicks uint32 // current time we get sdl.GetTicks()
|
fpsCurrentTicks uint32 // current time we get sdl.GetTicks()
|
||||||
fpsLastTime uint32 // last time we printed the fpsCurrentTicks
|
fpsLastTime uint32 // last time we printed the fpsCurrentTicks
|
||||||
|
@ -21,7 +28,7 @@ var (
|
||||||
|
|
||||||
// DrawDebugOverlay draws the debug FPS text on the SDL canvas.
|
// DrawDebugOverlay draws the debug FPS text on the SDL canvas.
|
||||||
func (d *Doodle) DrawDebugOverlay() {
|
func (d *Doodle) DrawDebugOverlay() {
|
||||||
if !d.Debug {
|
if !d.Debug || !DebugOverlay {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +36,7 @@ func (d *Doodle) DrawDebugOverlay() {
|
||||||
"FPS: %d (%dms) S:%s F12=screenshot",
|
"FPS: %d (%dms) S:%s F12=screenshot",
|
||||||
fpsCurrent,
|
fpsCurrent,
|
||||||
fpsSkipped,
|
fpsSkipped,
|
||||||
d.scene.Name(),
|
d.Scene.Name(),
|
||||||
)
|
)
|
||||||
|
|
||||||
err := d.Engine.DrawText(
|
err := d.Engine.DrawText(
|
||||||
|
@ -52,6 +59,10 @@ func (d *Doodle) DrawDebugOverlay() {
|
||||||
|
|
||||||
// DrawCollisionBox draws the collision box around a Doodad.
|
// DrawCollisionBox draws the collision box around a Doodad.
|
||||||
func (d *Doodle) DrawCollisionBox(actor doodads.Doodad) {
|
func (d *Doodle) DrawCollisionBox(actor doodads.Doodad) {
|
||||||
|
if !d.Debug || !DebugCollision {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
rect = doodads.GetBoundingRect(actor)
|
rect = doodads.GetBoundingRect(actor)
|
||||||
box = doodads.GetCollisionBox(rect)
|
box = doodads.GetCollisionBox(rect)
|
||||||
|
@ -74,13 +85,6 @@ func (d *Doodle) TrackFPS(skipped uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if fpsLastTime < fpsCurrentTicks-fpsInterval {
|
if fpsLastTime < fpsCurrentTicks-fpsInterval {
|
||||||
// log.Debug("Uptime: %s FPS: %d deltaTicks: %d skipped: %dms",
|
|
||||||
// time.Now().Sub(d.startTime),
|
|
||||||
// fpsCurrent,
|
|
||||||
// fpsCurrentTicks-fpsLastTime,
|
|
||||||
// skipped,
|
|
||||||
// )
|
|
||||||
|
|
||||||
fpsLastTime = fpsCurrentTicks
|
fpsLastTime = fpsCurrentTicks
|
||||||
fpsCurrent = fpsFrames
|
fpsCurrent = fpsFrames
|
||||||
fpsFrames = 0
|
fpsFrames = 0
|
||||||
|
|
74
main_scene.go
Normal file
74
main_scene.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package doodle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.kirsle.net/apps/doodle/events"
|
||||||
|
"git.kirsle.net/apps/doodle/render"
|
||||||
|
"git.kirsle.net/apps/doodle/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MainScene implements the main menu of Doodle.
|
||||||
|
type MainScene struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name of the scene.
|
||||||
|
func (s *MainScene) Name() string {
|
||||||
|
return "Main"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the scene.
|
||||||
|
func (s *MainScene) Setup(d *Doodle) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop the editor scene.
|
||||||
|
func (s *MainScene) Loop(d *Doodle, ev *events.State) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the pixels on this frame.
|
||||||
|
func (s *MainScene) Draw(d *Doodle) error {
|
||||||
|
// Clear the canvas and fill it with white.
|
||||||
|
d.Engine.Clear(render.White)
|
||||||
|
|
||||||
|
label := ui.NewLabel(render.Text{
|
||||||
|
Text: "Doodle v" + Version,
|
||||||
|
Size: 26,
|
||||||
|
Color: render.Pink,
|
||||||
|
Stroke: render.SkyBlue,
|
||||||
|
Shadow: render.Black,
|
||||||
|
})
|
||||||
|
label.Compute(d.Engine)
|
||||||
|
label.MoveTo(render.Point{
|
||||||
|
X: (d.width / 2) - (label.Size().W / 2),
|
||||||
|
Y: 120,
|
||||||
|
})
|
||||||
|
label.Present(d.Engine)
|
||||||
|
|
||||||
|
button := ui.NewButton(*ui.NewLabel(render.Text{
|
||||||
|
Text: "New Map",
|
||||||
|
Size: 14,
|
||||||
|
Color: render.Black,
|
||||||
|
}))
|
||||||
|
button.Compute(d.Engine)
|
||||||
|
|
||||||
|
button.MoveTo(render.Point{
|
||||||
|
X: (d.width / 2) - (button.Size().W / 2),
|
||||||
|
Y: 200,
|
||||||
|
})
|
||||||
|
button.Present(d.Engine)
|
||||||
|
|
||||||
|
button.SetText("Load Map")
|
||||||
|
button.Compute(d.Engine)
|
||||||
|
button.MoveTo(render.Point{
|
||||||
|
X: (d.width / 2) - (button.Size().W / 2),
|
||||||
|
Y: 260,
|
||||||
|
})
|
||||||
|
button.Present(d.Engine)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the scene.
|
||||||
|
func (s *MainScene) Destroy() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ type PlayScene struct {
|
||||||
height int32
|
height int32
|
||||||
|
|
||||||
// Player character
|
// Player character
|
||||||
player doodads.Doodad
|
Player doodads.Doodad
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the scene.
|
// Name of the scene.
|
||||||
|
@ -42,7 +42,7 @@ func (s *PlayScene) Setup(d *Doodle) error {
|
||||||
s.Filename = ""
|
s.Filename = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
s.player = doodads.NewPlayer()
|
s.Player = doodads.NewPlayer()
|
||||||
|
|
||||||
if s.canvas == nil {
|
if s.canvas == nil {
|
||||||
log.Debug("PlayScene.Setup: no grid given, initializing empty grid")
|
log.Debug("PlayScene.Setup: no grid given, initializing empty grid")
|
||||||
|
@ -80,17 +80,17 @@ func (s *PlayScene) Draw(d *Doodle) error {
|
||||||
s.canvas.Draw(d.Engine)
|
s.canvas.Draw(d.Engine)
|
||||||
|
|
||||||
// Draw our hero.
|
// Draw our hero.
|
||||||
s.player.Draw(d.Engine)
|
s.Player.Draw(d.Engine)
|
||||||
|
|
||||||
// Draw out bounding boxes.
|
// Draw out bounding boxes.
|
||||||
d.DrawCollisionBox(s.player)
|
d.DrawCollisionBox(s.Player)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// movePlayer updates the player's X,Y coordinate based on key pressed.
|
// movePlayer updates the player's X,Y coordinate based on key pressed.
|
||||||
func (s *PlayScene) movePlayer(ev *events.State) {
|
func (s *PlayScene) movePlayer(ev *events.State) {
|
||||||
delta := s.player.Position()
|
delta := s.Player.Position()
|
||||||
var playerSpeed int32 = 8
|
var playerSpeed int32 = 8
|
||||||
var gravity int32 = 2
|
var gravity int32 = 2
|
||||||
|
|
||||||
|
@ -110,20 +110,20 @@ func (s *PlayScene) movePlayer(ev *events.State) {
|
||||||
// Apply gravity.
|
// Apply gravity.
|
||||||
// var onFloor bool
|
// var onFloor bool
|
||||||
|
|
||||||
info, ok := doodads.CollidesWithGrid(s.player, &s.canvas, delta)
|
info, ok := doodads.CollidesWithGrid(s.Player, &s.canvas, delta)
|
||||||
if ok {
|
if ok {
|
||||||
// Collision happened with world.
|
// Collision happened with world.
|
||||||
}
|
}
|
||||||
delta = info.MoveTo
|
delta = info.MoveTo
|
||||||
|
|
||||||
// Apply gravity if not grounded.
|
// Apply gravity if not grounded.
|
||||||
if !s.player.Grounded() {
|
if !s.Player.Grounded() {
|
||||||
// Gravity has to pipe through the collision checker, too, so it
|
// Gravity has to pipe through the collision checker, too, so it
|
||||||
// can't give us a cheated downward boost.
|
// can't give us a cheated downward boost.
|
||||||
delta.Y += gravity
|
delta.Y += gravity
|
||||||
}
|
}
|
||||||
|
|
||||||
s.player.MoveTo(delta)
|
s.Player.MoveTo(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadLevel loads a level from disk.
|
// LoadLevel loads a level from disk.
|
||||||
|
|
10
scene.go
10
scene.go
|
@ -21,11 +21,11 @@ type Scene interface {
|
||||||
// Goto a scene. First it unloads the current scene.
|
// Goto a scene. First it unloads the current scene.
|
||||||
func (d *Doodle) Goto(scene Scene) error {
|
func (d *Doodle) Goto(scene Scene) error {
|
||||||
// Teardown existing scene.
|
// Teardown existing scene.
|
||||||
if d.scene != nil {
|
if d.Scene != nil {
|
||||||
d.scene.Destroy()
|
d.Scene.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Goto Scene")
|
log.Info("Goto Scene: %s", scene.Name())
|
||||||
d.scene = scene
|
d.Scene = scene
|
||||||
return d.scene.Setup(d)
|
return d.Scene.Setup(d)
|
||||||
}
|
}
|
||||||
|
|
93
shell.go
93
shell.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/balance"
|
"git.kirsle.net/apps/doodle/balance"
|
||||||
"git.kirsle.net/apps/doodle/events"
|
"git.kirsle.net/apps/doodle/events"
|
||||||
"git.kirsle.net/apps/doodle/render"
|
"git.kirsle.net/apps/doodle/render"
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Flash a message to the user.
|
// Flash a message to the user.
|
||||||
|
@ -17,16 +18,26 @@ func (d *Doodle) Flash(template string, v ...interface{}) {
|
||||||
|
|
||||||
// Shell implements the developer console in-game.
|
// Shell implements the developer console in-game.
|
||||||
type Shell struct {
|
type Shell struct {
|
||||||
parent *Doodle
|
parent *Doodle
|
||||||
Open bool
|
|
||||||
Prompt string
|
Open bool
|
||||||
Text string
|
Prompt string
|
||||||
History []string
|
Text string
|
||||||
Output []string
|
History []string
|
||||||
Flashes []Flash
|
Output []string
|
||||||
Cursor string
|
Flashes []Flash
|
||||||
|
|
||||||
|
// Blinky cursor variables.
|
||||||
|
cursor byte // cursor symbol
|
||||||
cursorFlip uint64 // ticks until cursor flip
|
cursorFlip uint64 // ticks until cursor flip
|
||||||
cursorRate uint64
|
cursorRate uint64
|
||||||
|
|
||||||
|
// Paging through history variables.
|
||||||
|
historyPaging bool
|
||||||
|
historyIndex int
|
||||||
|
|
||||||
|
// JavaScript shell interpreter.
|
||||||
|
js *otto.Otto
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flash holds a message to flash on screen.
|
// Flash holds a message to flash on screen.
|
||||||
|
@ -37,15 +48,32 @@ type Flash struct {
|
||||||
|
|
||||||
// NewShell initializes the shell helper (the "Shellper").
|
// NewShell initializes the shell helper (the "Shellper").
|
||||||
func NewShell(d *Doodle) Shell {
|
func NewShell(d *Doodle) Shell {
|
||||||
return Shell{
|
s := Shell{
|
||||||
parent: d,
|
parent: d,
|
||||||
History: []string{},
|
History: []string{},
|
||||||
Output: []string{},
|
Output: []string{},
|
||||||
Flashes: []Flash{},
|
Flashes: []Flash{},
|
||||||
Prompt: ">",
|
Prompt: ">",
|
||||||
Cursor: "_",
|
cursor: '_',
|
||||||
cursorRate: balance.ShellCursorBlinkRate,
|
cursorRate: balance.ShellCursorBlinkRate,
|
||||||
|
js: otto.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make the Doodle instance available to the shell.
|
||||||
|
bindings := map[string]interface{}{
|
||||||
|
"d": d,
|
||||||
|
"log": log,
|
||||||
|
"RGBA": render.RGBA,
|
||||||
|
"Point": render.NewPoint,
|
||||||
|
}
|
||||||
|
for name, v := range bindings {
|
||||||
|
err := s.js.Set(name, v)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to make `%s` available to JS shell: %s", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the shell, resetting its internal state.
|
// Close the shell, resetting its internal state.
|
||||||
|
@ -54,11 +82,18 @@ func (s *Shell) Close() {
|
||||||
s.Open = false
|
s.Open = false
|
||||||
s.Prompt = ">"
|
s.Prompt = ">"
|
||||||
s.Text = ""
|
s.Text = ""
|
||||||
|
s.historyPaging = false
|
||||||
|
s.historyIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a command in the shell.
|
// Execute a command in the shell.
|
||||||
func (s *Shell) Execute(input string) {
|
func (s *Shell) Execute(input string) {
|
||||||
command := s.Parse(input)
|
command := s.Parse(input)
|
||||||
|
if command.Raw != "" {
|
||||||
|
s.Output = append(s.Output, s.Prompt+command.Raw)
|
||||||
|
s.History = append(s.History, command.Raw)
|
||||||
|
}
|
||||||
|
|
||||||
if command.Command == "clear" {
|
if command.Command == "clear" {
|
||||||
s.Output = []string{}
|
s.Output = []string{}
|
||||||
} else {
|
} else {
|
||||||
|
@ -68,10 +103,6 @@ func (s *Shell) Execute(input string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if command.Raw != "" {
|
|
||||||
s.History = append(s.History, command.Raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the text buffer in the shell.
|
// Reset the text buffer in the shell.
|
||||||
s.Text = ""
|
s.Text = ""
|
||||||
}
|
}
|
||||||
|
@ -149,6 +180,32 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
||||||
s.Execute(s.Text)
|
s.Execute(s.Text)
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil
|
return nil
|
||||||
|
} else if (ev.Up.Now || ev.Down.Now) && len(s.History) > 0 {
|
||||||
|
// Paging through history.
|
||||||
|
if !s.historyPaging {
|
||||||
|
s.historyPaging = true
|
||||||
|
s.historyIndex = len(s.History)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume the inputs and make convenient variables.
|
||||||
|
ev.Down.Read()
|
||||||
|
isUp := ev.Up.Read()
|
||||||
|
|
||||||
|
// Scroll through the input history.
|
||||||
|
if isUp {
|
||||||
|
s.historyIndex--
|
||||||
|
if s.historyIndex < 0 {
|
||||||
|
s.historyIndex = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.historyIndex++
|
||||||
|
if s.historyIndex >= len(s.History) {
|
||||||
|
s.historyIndex = len(s.History) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Text = s.History[s.historyIndex]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the line height we can draw.
|
// Compute the line height we can draw.
|
||||||
|
@ -159,10 +216,10 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
||||||
// Cursor flip?
|
// Cursor flip?
|
||||||
if d.ticks > s.cursorFlip {
|
if d.ticks > s.cursorFlip {
|
||||||
s.cursorFlip = d.ticks + s.cursorRate
|
s.cursorFlip = d.ticks + s.cursorRate
|
||||||
if s.Cursor == "" {
|
if s.cursor == ' ' {
|
||||||
s.Cursor = "_"
|
s.cursor = '_'
|
||||||
} else {
|
} else {
|
||||||
s.Cursor = ""
|
s.cursor = ' '
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +272,7 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
||||||
// Draw the command prompt.
|
// Draw the command prompt.
|
||||||
d.Engine.DrawText(
|
d.Engine.DrawText(
|
||||||
render.Text{
|
render.Text{
|
||||||
Text: s.Prompt + s.Text + s.Cursor,
|
Text: s.Prompt + s.Text + string(s.cursor),
|
||||||
Size: balance.ShellFontSize,
|
Size: balance.ShellFontSize,
|
||||||
Color: balance.ShellForegroundColor,
|
Color: balance.ShellForegroundColor,
|
||||||
},
|
},
|
||||||
|
|
|
@ -90,7 +90,7 @@ func (w *Button) Present(e render.Engine) {
|
||||||
e.DrawBox(w.Background, box)
|
e.DrawBox(w.Background, box)
|
||||||
|
|
||||||
// Draw the text label inside.
|
// Draw the text label inside.
|
||||||
w.Label.SetPoint(render.Point{
|
w.Label.MoveTo(render.Point{
|
||||||
X: P.X + w.Padding + w.Border + w.Outline,
|
X: P.X + w.Padding + w.Border + w.Outline,
|
||||||
Y: P.Y + w.Padding + w.Border + w.Outline,
|
Y: P.Y + w.Padding + w.Border + w.Outline,
|
||||||
})
|
})
|
||||||
|
|
13
ui/widget.go
13
ui/widget.go
|
@ -9,7 +9,8 @@ type Widget interface {
|
||||||
SetWidth(int32) // Set
|
SetWidth(int32) // Set
|
||||||
SetHeight(int32) // Set
|
SetHeight(int32) // Set
|
||||||
Point() render.Point
|
Point() render.Point
|
||||||
SetPoint(render.Point)
|
MoveTo(render.Point)
|
||||||
|
MoveBy(render.Point)
|
||||||
Size() render.Rect // Return the Width and Height of the widget.
|
Size() render.Rect // Return the Width and Height of the widget.
|
||||||
Resize(render.Rect)
|
Resize(render.Rect)
|
||||||
|
|
||||||
|
@ -35,11 +36,17 @@ func (w *BaseWidget) Point() render.Point {
|
||||||
return w.point
|
return w.point
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPoint updates the X,Y position of the widget relative to the window.
|
// MoveTo updates the X,Y position to the new point.
|
||||||
func (w *BaseWidget) SetPoint(v render.Point) {
|
func (w *BaseWidget) MoveTo(v render.Point) {
|
||||||
w.point = v
|
w.point = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MoveBy adds the X,Y values to the widget's current position.
|
||||||
|
func (w *BaseWidget) MoveBy(v render.Point) {
|
||||||
|
w.point.X += v.X
|
||||||
|
w.point.Y += v.Y
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the box with W and H attributes containing the size of the
|
// Size returns the box with W and H attributes containing the size of the
|
||||||
// widget. The X,Y attributes of the box are ignored and zero.
|
// widget. The X,Y attributes of the box are ignored and zero.
|
||||||
func (w *BaseWidget) Size() render.Rect {
|
func (w *BaseWidget) Size() render.Rect {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user