Centralized Tick Counter, Fix Actor Dragging Bug
* The game's tick counter was moved from Doodle.ticks to shmem.Tick where it is more easily available from every corner of the code. * Fix a bug in the Level Editor where dragging an already-existing actor from one part of your map to another, would cause it to lose all its data (especially its UUID), breaking links to other doodads. Now the existing Actor catches a ride on the drag object to be reinserted later. * Animate the Link Line visualizers between actors. They now animate a blinking color between magenta and grey-ish.
This commit is contained in:
parent
dc2695cfc9
commit
a504658055
11
TODO.md
11
TODO.md
|
@ -3,8 +3,7 @@
|
||||||
## Alpha Launch Minimum Checklist
|
## Alpha Launch Minimum Checklist
|
||||||
|
|
||||||
- [ ] Open Source Licenses
|
- [ ] Open Source Licenses
|
||||||
- [ ] Doodad Scripts: an "end level" function for a level goalpost.
|
- [x] Doodad Scripts: an "end level" function for a level goalpost.
|
||||||
|
|
||||||
|
|
||||||
**Blocker Bugs:**
|
**Blocker Bugs:**
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@
|
||||||
|
|
||||||
- Doodads Palette:
|
- Doodads Palette:
|
||||||
- [ ] Hide some doodads like the player character.
|
- [ ] Hide some doodads like the player character.
|
||||||
- [ ] Pagination or scrolling UI for long lists of doodads.
|
- [x] Pagination or scrolling UI for long lists of doodads.
|
||||||
|
|
||||||
**Nice to haves:**
|
**Nice to haves:**
|
||||||
|
|
||||||
|
@ -27,8 +26,8 @@
|
||||||
- [ ] Single-player "campaign mode" of built-in levels.
|
- [ ] Single-player "campaign mode" of built-in levels.
|
||||||
- campaign.json file format configuring the level order
|
- campaign.json file format configuring the level order
|
||||||
- [ ] Level Editor Improvements
|
- [ ] Level Editor Improvements
|
||||||
- [ ] Undo/Redo Function
|
- [x] Undo/Redo Function
|
||||||
- [ ] Lines and Boxes
|
- [x] Lines and Boxes
|
||||||
- [ ] Eraser Tool
|
- [ ] Eraser Tool
|
||||||
- [ ] Brush size and/or shape
|
- [ ] Brush size and/or shape
|
||||||
- [ ] Doodad CLI Tool Features
|
- [ ] Doodad CLI Tool Features
|
||||||
|
@ -52,7 +51,7 @@
|
||||||
- [ ] Doors
|
- [ ] Doors
|
||||||
- [x] Locked Doors and Keys
|
- [x] Locked Doors and Keys
|
||||||
- [x] Electric Doors
|
- [x] Electric Doors
|
||||||
- [ ] Trapdoors (1 of 4)
|
- [x] Trapdoors (all 4 directions)
|
||||||
|
|
||||||
## Doodad Ideas
|
## Doodad Ideas
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,7 @@ var (
|
||||||
Cyan = RGBA(0, 255, 255, 255)
|
Cyan = RGBA(0, 255, 255, 255)
|
||||||
DarkCyan = RGBA(0, 153, 153, 255)
|
DarkCyan = RGBA(0, 153, 153, 255)
|
||||||
Yellow = RGBA(255, 255, 0, 255)
|
Yellow = RGBA(255, 255, 0, 255)
|
||||||
|
Orange = RGBA(255, 153, 0, 255)
|
||||||
DarkYellow = RGBA(153, 153, 0, 255)
|
DarkYellow = RGBA(153, 153, 0, 255)
|
||||||
Magenta = RGBA(255, 0, 255, 255)
|
Magenta = RGBA(255, 0, 255, 255)
|
||||||
Purple = RGBA(153, 0, 153, 255)
|
Purple = RGBA(153, 0, 153, 255)
|
||||||
|
|
|
@ -55,6 +55,11 @@ var (
|
||||||
// Color for draggable doodad.
|
// Color for draggable doodad.
|
||||||
DragColor = render.MustHexColor("#0099FF")
|
DragColor = render.MustHexColor("#0099FF")
|
||||||
|
|
||||||
|
// Link lines drawn between connected doodads.
|
||||||
|
LinkLineColor = render.Magenta
|
||||||
|
LinkLighten = 128
|
||||||
|
LinkAnimSpeed uint64 = 30 // ticks
|
||||||
|
|
||||||
PlayButtonFont = render.Text{
|
PlayButtonFont = render.Text{
|
||||||
FontFilename: "DejaVuSans-Bold.ttf",
|
FontFilename: "DejaVuSans-Bold.ttf",
|
||||||
Size: 16,
|
Size: 16,
|
||||||
|
|
|
@ -36,7 +36,6 @@ type Doodle struct {
|
||||||
|
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
running bool
|
running bool
|
||||||
ticks uint64
|
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
|
||||||
|
@ -110,7 +109,7 @@ func (d *Doodle) Run() error {
|
||||||
d.Engine.Clear(render.White)
|
d.Engine.Clear(render.White)
|
||||||
|
|
||||||
start := time.Now() // Record how long this frame took.
|
start := time.Now() // Record how long this frame took.
|
||||||
d.ticks++
|
shmem.Tick++
|
||||||
|
|
||||||
// Poll for events.
|
// Poll for events.
|
||||||
ev, err := d.Engine.Poll()
|
ev, err := d.Engine.Poll()
|
||||||
|
|
|
@ -45,6 +45,19 @@ func NewStroke(shape Shape, color render.Color) *Stroke {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy returns a duplicate of the Stroke reference.
|
||||||
|
func (s *Stroke) Copy() *Stroke {
|
||||||
|
nextStrokeID++
|
||||||
|
return &Stroke{
|
||||||
|
ID: nextStrokeID,
|
||||||
|
Shape: s.Shape,
|
||||||
|
Color: s.Color,
|
||||||
|
|
||||||
|
Points: []render.Point{},
|
||||||
|
uniqPoint: map[render.Point]interface{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IterPoints returns an iterator of points represented by the stroke.
|
// IterPoints returns an iterator of points represented by the stroke.
|
||||||
//
|
//
|
||||||
// For a Line, returns all of the points between PointA and PointB. For freehand,
|
// For a Line, returns all of the points between PointA and PointB. For freehand,
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/lib/ui"
|
"git.kirsle.net/apps/doodle/lib/ui"
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/branding"
|
"git.kirsle.net/apps/doodle/pkg/branding"
|
||||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||||
"git.kirsle.net/apps/doodle/pkg/level"
|
"git.kirsle.net/apps/doodle/pkg/level"
|
||||||
|
@ -333,12 +332,8 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
// A drag event initiated inside the Canvas. This happens in the ActorTool
|
// A drag event initiated inside the Canvas. This happens in the ActorTool
|
||||||
// mode when you click an existing Doodad and it "pops" out of the canvas
|
// mode when you click an existing Doodad and it "pops" out of the canvas
|
||||||
// and onto the cursor to be repositioned.
|
// and onto the cursor to be repositioned.
|
||||||
drawing.OnDragStart = func(filename string) {
|
drawing.OnDragStart = func(actor *level.Actor) {
|
||||||
doodad, err := doodads.LoadFile(filename)
|
u.startDragActor(nil, actor)
|
||||||
if err != nil {
|
|
||||||
log.Error("drawing.OnDragStart: %s", err.Error())
|
|
||||||
}
|
|
||||||
u.startDragActor(doodad)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A link event to connect two actors together.
|
// A link event to connect two actors together.
|
||||||
|
@ -350,7 +345,6 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
b.Actor.AddLink(idA)
|
b.Actor.AddLink(idA)
|
||||||
|
|
||||||
// Reset the Link tool.
|
// Reset the Link tool.
|
||||||
drawing.Tool = drawtool.ActorTool
|
|
||||||
d.Flash("Linked '%s' and '%s' together", a.Doodad.Title, b.Doodad.Title)
|
d.Flash("Linked '%s' and '%s' together", a.Doodad.Title, b.Doodad.Title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,15 +363,25 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
size := actor.canvas.Size()
|
var (
|
||||||
u.Scene.Level.Actors.Add(&level.Actor{
|
|
||||||
// Uncenter the drawing from the cursor.
|
// Uncenter the drawing from the cursor.
|
||||||
Point: render.Point{
|
size = actor.canvas.Size()
|
||||||
|
position = render.Point{
|
||||||
X: (u.cursor.X - drawing.Scroll.X - (size.W / 2)) - P.X,
|
X: (u.cursor.X - drawing.Scroll.X - (size.W / 2)) - P.X,
|
||||||
Y: (u.cursor.Y - drawing.Scroll.Y - (size.H / 2)) - P.Y,
|
Y: (u.cursor.Y - drawing.Scroll.Y - (size.H / 2)) - P.Y,
|
||||||
},
|
}
|
||||||
Filename: actor.doodad.Filename,
|
)
|
||||||
})
|
|
||||||
|
// Was it an already existing actor to re-add to the map?
|
||||||
|
if actor.actor != nil {
|
||||||
|
actor.actor.Point = position
|
||||||
|
u.Scene.Level.Actors.Add(actor.actor)
|
||||||
|
} else {
|
||||||
|
u.Scene.Level.Actors.Add(&level.Actor{
|
||||||
|
Point: position,
|
||||||
|
Filename: actor.doodad.Filename,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
err := drawing.InstallActors(u.Scene.Level.Actors)
|
err := drawing.InstallActors(u.Scene.Level.Actors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/lib/ui"
|
"git.kirsle.net/apps/doodle/lib/ui"
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/level"
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
"git.kirsle.net/apps/doodle/pkg/uix"
|
"git.kirsle.net/apps/doodle/pkg/uix"
|
||||||
)
|
)
|
||||||
|
@ -18,13 +19,29 @@ import (
|
||||||
// DraggableActor is a Doodad being dragged from the Doodad palette.
|
// DraggableActor is a Doodad being dragged from the Doodad palette.
|
||||||
type DraggableActor struct {
|
type DraggableActor struct {
|
||||||
canvas *uix.Canvas
|
canvas *uix.Canvas
|
||||||
doodad *doodads.Doodad
|
doodad *doodads.Doodad // if a new one from the palette
|
||||||
|
actor *level.Actor // if a level actor
|
||||||
}
|
}
|
||||||
|
|
||||||
// startDragActor begins the drag event for a Doodad onto a level.
|
// startDragActor begins the drag event for a Doodad onto a level.
|
||||||
func (u *EditorUI) startDragActor(doodad *doodads.Doodad) {
|
// actor may be nil (if you drag a new doodad from the palette) or otherwise
|
||||||
|
// is an existing actor from the level.
|
||||||
|
func (u *EditorUI) startDragActor(doodad *doodads.Doodad, actor *level.Actor) {
|
||||||
u.Supervisor.DragStart()
|
u.Supervisor.DragStart()
|
||||||
|
|
||||||
|
if doodad == nil {
|
||||||
|
if actor != nil {
|
||||||
|
obj, err := doodads.LoadFile(actor.Filename)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("startDragExistingActor: actor doodad name %s not found: %s", actor.Filename, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doodad = obj
|
||||||
|
} else {
|
||||||
|
panic("EditorUI.startDragActor: doodad AND/OR actor is required, but neither were given")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create the canvas to render on the mouse cursor.
|
// Create the canvas to render on the mouse cursor.
|
||||||
drawing := uix.NewCanvas(doodad.Layers[0].Chunker.Size, false)
|
drawing := uix.NewCanvas(doodad.Layers[0].Chunker.Size, false)
|
||||||
drawing.LoadDoodad(doodad)
|
drawing.LoadDoodad(doodad)
|
||||||
|
@ -34,6 +51,7 @@ func (u *EditorUI) startDragActor(doodad *doodads.Doodad) {
|
||||||
u.DraggableActor = &DraggableActor{
|
u.DraggableActor = &DraggableActor{
|
||||||
canvas: drawing,
|
canvas: drawing,
|
||||||
doodad: doodad,
|
doodad: doodad,
|
||||||
|
actor: actor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +171,7 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
|
||||||
// editor_ui.go#SetupCanvas()
|
// editor_ui.go#SetupCanvas()
|
||||||
btn.Handle(ui.MouseDown, func(e render.Point) {
|
btn.Handle(ui.MouseDown, func(e render.Point) {
|
||||||
log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title)
|
log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title)
|
||||||
u.startDragActor(doodad)
|
u.startDragActor(doodad, nil)
|
||||||
})
|
})
|
||||||
u.Supervisor.Add(btn)
|
u.Supervisor.Add(btn)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/lib/ui"
|
"git.kirsle.net/apps/doodle/lib/ui"
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -146,7 +147,7 @@ func (s *Shell) Write(line string) {
|
||||||
s.Output = append(s.Output, line)
|
s.Output = append(s.Output, line)
|
||||||
s.Flashes = append(s.Flashes, Flash{
|
s.Flashes = append(s.Flashes, Flash{
|
||||||
Text: line,
|
Text: line,
|
||||||
Expires: s.parent.ticks + balance.FlashTTL,
|
Expires: shmem.Tick + balance.FlashTTL,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,8 +254,8 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cursor flip?
|
// Cursor flip?
|
||||||
if d.ticks > s.cursorFlip {
|
if shmem.Tick > s.cursorFlip {
|
||||||
s.cursorFlip = d.ticks + s.cursorRate
|
s.cursorFlip = shmem.Tick + s.cursorRate
|
||||||
if s.cursor == ' ' {
|
if s.cursor == ' ' {
|
||||||
s.cursor = '_'
|
s.cursor = '_'
|
||||||
} else {
|
} else {
|
||||||
|
@ -329,7 +330,7 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
||||||
outputY := int32(d.height - (lineHeight * 2) - 16)
|
outputY := int32(d.height - (lineHeight * 2) - 16)
|
||||||
for i := len(s.Flashes); i > 0; i-- {
|
for i := len(s.Flashes); i > 0; i-- {
|
||||||
flash := s.Flashes[i-1]
|
flash := s.Flashes[i-1]
|
||||||
if d.ticks >= flash.Expires {
|
if shmem.Tick >= flash.Expires {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,9 @@ import (
|
||||||
// Shared globals for easy access throughout the app.
|
// Shared globals for easy access throughout the app.
|
||||||
// Not an ideal place to keep things but *shrug*
|
// Not an ideal place to keep things but *shrug*
|
||||||
var (
|
var (
|
||||||
|
// Tick is incremented by the main game loop each frame.
|
||||||
|
Tick uint64
|
||||||
|
|
||||||
// Current render engine (i.e. SDL2 or HTML5 Canvas)
|
// Current render engine (i.e. SDL2 or HTML5 Canvas)
|
||||||
// The level.Chunk.ToBitmap() uses this to cache a texture image.
|
// The level.Chunk.ToBitmap() uses this to cache a texture image.
|
||||||
CurrentRenderEngine render.Engine
|
CurrentRenderEngine render.Engine
|
||||||
|
|
|
@ -67,7 +67,7 @@ type Canvas struct {
|
||||||
// that controls the actors. Upstream should delete them and then reinstall
|
// that controls the actors. Upstream should delete them and then reinstall
|
||||||
// the actor list from scratch.
|
// the actor list from scratch.
|
||||||
OnDeleteActors func([]*level.Actor)
|
OnDeleteActors func([]*level.Actor)
|
||||||
OnDragStart func(filename string)
|
OnDragStart func(*level.Actor)
|
||||||
|
|
||||||
// -- WHEN Canvas.Tool is "Link" --
|
// -- WHEN Canvas.Tool is "Link" --
|
||||||
// When the Canvas wants to link two actors together. Arguments are the IDs
|
// When the Canvas wants to link two actors together. Arguments are the IDs
|
||||||
|
|
|
@ -183,7 +183,7 @@ func (w *Canvas) loopEditable(ev *events.State) error {
|
||||||
// Pop this canvas out for the drag/drop.
|
// Pop this canvas out for the drag/drop.
|
||||||
if w.OnDragStart != nil {
|
if w.OnDragStart != nil {
|
||||||
deleteActors = append(deleteActors, actor.Actor)
|
deleteActors = append(deleteActors, actor.Actor)
|
||||||
w.OnDragStart(actor.Actor.Filename)
|
w.OnDragStart(actor.Actor)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
} else if ev.Button2.Read() {
|
} else if ev.Button2.Read() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
"git.kirsle.net/apps/doodle/pkg/level"
|
"git.kirsle.net/apps/doodle/pkg/level"
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// canvas_strokes.go: functions related to drawtool.Stroke and the Canvas.
|
// canvas_strokes.go: functions related to drawtool.Stroke and the Canvas.
|
||||||
|
@ -115,6 +116,27 @@ func (w *Canvas) presentActorLinks(e render.Engine) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no links, stop.
|
||||||
|
if len(actorMap) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The glow colored line. Huge hacky block of code but makes for some
|
||||||
|
// basic visualization for now.
|
||||||
|
var color = balance.LinkLineColor
|
||||||
|
var lightenStep = float64(balance.LinkLighten) / 16
|
||||||
|
var step = shmem.Tick % balance.LinkAnimSpeed
|
||||||
|
if step < 32 {
|
||||||
|
for i := uint64(0); i < step; i++ {
|
||||||
|
color = color.Lighten(int(lightenStep))
|
||||||
|
}
|
||||||
|
if step > 16 {
|
||||||
|
for i := uint64(0); i < step-16; i++ {
|
||||||
|
color = color.Darken(int(lightenStep))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Loop over the linked actors and draw stroke lines.
|
// Loop over the linked actors and draw stroke lines.
|
||||||
for _, actor := range actorMap {
|
for _, actor := range actorMap {
|
||||||
for _, linkID := range actor.Actor.Links {
|
for _, linkID := range actor.Actor.Links {
|
||||||
|
@ -130,7 +152,7 @@ func (w *Canvas) presentActorLinks(e render.Engine) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Draw a line connecting the centers of each actor together.
|
// Draw a line connecting the centers of each actor together.
|
||||||
stroke := drawtool.NewStroke(drawtool.Line, render.Magenta)
|
stroke := drawtool.NewStroke(drawtool.Line, color)
|
||||||
stroke.PointA = render.Point{
|
stroke.PointA = render.Point{
|
||||||
X: aP.X + (aS.W / 2),
|
X: aP.X + (aS.W / 2),
|
||||||
Y: aP.Y + (aS.H / 2),
|
Y: aP.Y + (aS.H / 2),
|
||||||
|
@ -141,6 +163,16 @@ func (w *Canvas) presentActorLinks(e render.Engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
strokes = append(strokes, stroke)
|
strokes = append(strokes, stroke)
|
||||||
|
|
||||||
|
// Make it double thick.
|
||||||
|
double := stroke.Copy()
|
||||||
|
double.PointA = render.NewPoint(stroke.PointA.X, stroke.PointA.Y+1)
|
||||||
|
double.PointB = render.NewPoint(stroke.PointB.X, stroke.PointB.Y+1)
|
||||||
|
strokes = append(strokes, double)
|
||||||
|
double = stroke.Copy()
|
||||||
|
double.PointA = render.NewPoint(stroke.PointA.X+1, stroke.PointA.Y)
|
||||||
|
double.PointB = render.NewPoint(stroke.PointB.X+1, stroke.PointB.Y)
|
||||||
|
strokes = append(strokes, double)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user