diff --git a/TODO.md b/TODO.md index a4dd07e..b682471 100644 --- a/TODO.md +++ b/TODO.md @@ -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 diff --git a/lib/render/interface.go b/lib/render/interface.go index 3654a38..8c74a34 100644 --- a/lib/render/interface.go +++ b/lib/render/interface.go @@ -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) diff --git a/pkg/balance/theme.go b/pkg/balance/theme.go index 15f3c89..a2317a5 100644 --- a/pkg/balance/theme.go +++ b/pkg/balance/theme.go @@ -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, diff --git a/pkg/doodle.go b/pkg/doodle.go index 12425cd..47ffb38 100644 --- a/pkg/doodle.go +++ b/pkg/doodle.go @@ -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() diff --git a/pkg/drawtool/stroke.go b/pkg/drawtool/stroke.go index bf7ec3e..5d26bc1 100644 --- a/pkg/drawtool/stroke.go +++ b/pkg/drawtool/stroke.go @@ -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, diff --git a/pkg/editor_ui.go b/pkg/editor_ui.go index 20721e5..e544374 100644 --- a/pkg/editor_ui.go +++ b/pkg/editor_ui.go @@ -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, - }, - 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) if err != nil { diff --git a/pkg/editor_ui_doodad.go b/pkg/editor_ui_doodad.go index 29fd806..c275a31 100644 --- a/pkg/editor_ui_doodad.go +++ b/pkg/editor_ui_doodad.go @@ -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) diff --git a/pkg/shell.go b/pkg/shell.go index 87abef0..2228ac8 100644 --- a/pkg/shell.go +++ b/pkg/shell.go @@ -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 } diff --git a/pkg/shmem/globals.go b/pkg/shmem/globals.go index 2f00a30..23c0783 100644 --- a/pkg/shmem/globals.go +++ b/pkg/shmem/globals.go @@ -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 diff --git a/pkg/uix/canvas.go b/pkg/uix/canvas.go index 13ea54e..e0e90c7 100644 --- a/pkg/uix/canvas.go +++ b/pkg/uix/canvas.go @@ -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 diff --git a/pkg/uix/canvas_editable.go b/pkg/uix/canvas_editable.go index 65e49b6..62c5af5 100644 --- a/pkg/uix/canvas_editable.go +++ b/pkg/uix/canvas_editable.go @@ -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() { diff --git a/pkg/uix/canvas_strokes.go b/pkg/uix/canvas_strokes.go index 9fd647d..c49b79b 100644 --- a/pkg/uix/canvas_strokes.go +++ b/pkg/uix/canvas_strokes.go @@ -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) } }