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:
Noah 2019-07-05 16:04:36 -07:00
parent dc2695cfc9
commit a504658055
12 changed files with 107 additions and 32 deletions

11
TODO.md
View File

@ -3,8 +3,7 @@
## Alpha Launch Minimum Checklist
- [ ] 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:**
@ -16,7 +15,7 @@
- Doodads Palette:
- [ ] 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:**
@ -27,8 +26,8 @@
- [ ] Single-player "campaign mode" of built-in levels.
- campaign.json file format configuring the level order
- [ ] Level Editor Improvements
- [ ] Undo/Redo Function
- [ ] Lines and Boxes
- [x] Undo/Redo Function
- [x] Lines and Boxes
- [ ] Eraser Tool
- [ ] Brush size and/or shape
- [ ] Doodad CLI Tool Features
@ -52,7 +51,7 @@
- [ ] Doors
- [x] Locked Doors and Keys
- [x] Electric Doors
- [ ] Trapdoors (1 of 4)
- [x] Trapdoors (all 4 directions)
## Doodad Ideas

View File

@ -189,6 +189,7 @@ var (
Cyan = RGBA(0, 255, 255, 255)
DarkCyan = RGBA(0, 153, 153, 255)
Yellow = RGBA(255, 255, 0, 255)
Orange = RGBA(255, 153, 0, 255)
DarkYellow = RGBA(153, 153, 0, 255)
Magenta = RGBA(255, 0, 255, 255)
Purple = RGBA(153, 0, 153, 255)

View File

@ -55,6 +55,11 @@ var (
// Color for draggable doodad.
DragColor = render.MustHexColor("#0099FF")
// Link lines drawn between connected doodads.
LinkLineColor = render.Magenta
LinkLighten = 128
LinkAnimSpeed uint64 = 30 // ticks
PlayButtonFont = render.Text{
FontFilename: "DejaVuSans-Bold.ttf",
Size: 16,

View File

@ -36,7 +36,6 @@ type Doodle struct {
startTime time.Time
running bool
ticks uint64
width int
height int
@ -110,7 +109,7 @@ func (d *Doodle) Run() error {
d.Engine.Clear(render.White)
start := time.Now() // Record how long this frame took.
d.ticks++
shmem.Tick++
// Poll for events.
ev, err := d.Engine.Poll()

View File

@ -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.
//
// For a Line, returns all of the points between PointA and PointB. For freehand,

View File

@ -10,7 +10,6 @@ import (
"git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance"
"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/enum"
"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
// mode when you click an existing Doodad and it "pops" out of the canvas
// and onto the cursor to be repositioned.
drawing.OnDragStart = func(filename string) {
doodad, err := doodads.LoadFile(filename)
if err != nil {
log.Error("drawing.OnDragStart: %s", err.Error())
}
u.startDragActor(doodad)
drawing.OnDragStart = func(actor *level.Actor) {
u.startDragActor(nil, actor)
}
// A link event to connect two actors together.
@ -350,7 +345,6 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
b.Actor.AddLink(idA)
// Reset the Link tool.
drawing.Tool = drawtool.ActorTool
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
}
size := actor.canvas.Size()
u.Scene.Level.Actors.Add(&level.Actor{
var (
// 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,
Y: (u.cursor.Y - drawing.Scroll.Y - (size.H / 2)) - P.Y,
},
}
)
// 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)
if err != nil {

View File

@ -11,6 +11,7 @@ import (
"git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance"
"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/uix"
)
@ -18,13 +19,29 @@ import (
// DraggableActor is a Doodad being dragged from the Doodad palette.
type DraggableActor struct {
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.
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()
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.
drawing := uix.NewCanvas(doodad.Layers[0].Chunker.Size, false)
drawing.LoadDoodad(doodad)
@ -34,6 +51,7 @@ func (u *EditorUI) startDragActor(doodad *doodads.Doodad) {
u.DraggableActor = &DraggableActor{
canvas: drawing,
doodad: doodad,
actor: actor,
}
}
@ -153,7 +171,7 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
// editor_ui.go#SetupCanvas()
btn.Handle(ui.MouseDown, func(e render.Point) {
log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title)
u.startDragActor(doodad)
u.startDragActor(doodad, nil)
})
u.Supervisor.Add(btn)

View File

@ -10,6 +10,7 @@ import (
"git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/shmem"
"github.com/robertkrimen/otto"
)
@ -146,7 +147,7 @@ func (s *Shell) Write(line string) {
s.Output = append(s.Output, line)
s.Flashes = append(s.Flashes, Flash{
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?
if d.ticks > s.cursorFlip {
s.cursorFlip = d.ticks + s.cursorRate
if shmem.Tick > s.cursorFlip {
s.cursorFlip = shmem.Tick + s.cursorRate
if s.cursor == ' ' {
s.cursor = '_'
} else {
@ -329,7 +330,7 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
outputY := int32(d.height - (lineHeight * 2) - 16)
for i := len(s.Flashes); i > 0; i-- {
flash := s.Flashes[i-1]
if d.ticks >= flash.Expires {
if shmem.Tick >= flash.Expires {
continue
}

View File

@ -9,6 +9,9 @@ import (
// Shared globals for easy access throughout the app.
// Not an ideal place to keep things but *shrug*
var (
// Tick is incremented by the main game loop each frame.
Tick uint64
// Current render engine (i.e. SDL2 or HTML5 Canvas)
// The level.Chunk.ToBitmap() uses this to cache a texture image.
CurrentRenderEngine render.Engine

View File

@ -67,7 +67,7 @@ type Canvas struct {
// that controls the actors. Upstream should delete them and then reinstall
// the actor list from scratch.
OnDeleteActors func([]*level.Actor)
OnDragStart func(filename string)
OnDragStart func(*level.Actor)
// -- WHEN Canvas.Tool is "Link" --
// When the Canvas wants to link two actors together. Arguments are the IDs

View File

@ -183,7 +183,7 @@ func (w *Canvas) loopEditable(ev *events.State) error {
// Pop this canvas out for the drag/drop.
if w.OnDragStart != nil {
deleteActors = append(deleteActors, actor.Actor)
w.OnDragStart(actor.Actor.Filename)
w.OnDragStart(actor.Actor)
}
break
} else if ev.Button2.Read() {

View File

@ -7,6 +7,7 @@ import (
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/level"
"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.
@ -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.
for _, actor := range actorMap {
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.
stroke := drawtool.NewStroke(drawtool.Line, render.Magenta)
stroke := drawtool.NewStroke(drawtool.Line, color)
stroke.PointA = render.Point{
X: aP.X + (aS.W / 2),
Y: aP.Y + (aS.H / 2),
@ -141,6 +163,16 @@ func (w *Canvas) presentActorLinks(e render.Engine) {
}
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)
}
}