diff --git a/dev-assets/doodads/azulian/Makefile b/dev-assets/doodads/azulian/Makefile index 20e7aa5..05dae89 100644 --- a/dev-assets/doodads/azulian/Makefile +++ b/dev-assets/doodads/azulian/Makefile @@ -4,11 +4,13 @@ ALL: build build: doodad convert -t "Blue Azulian" blu-front.png blu-back.png \ blu-wr{1,2,3,4}.png blu-wl{1,2,3,4}.png azu-blu.doodad + doodad edit-doodad --tag "color=blue" azu-blu.doodad doodad install-script azulian.js azu-blu.doodad doodad convert -t "Red Azulian" red-front.png red-back.png \ red-wr{1,2,3,4}.png red-wl{1,2,3,4}.png azu-red.doodad - doodad install-script azulian-red.js azu-red.doodad + doodad edit-doodad --tag "color=red" azu-red.doodad + doodad install-script azulian.js azu-red.doodad # Tag the category for these doodads for i in *.doodad; do\ diff --git a/dev-assets/doodads/azulian/azulian-red.js b/dev-assets/doodads/azulian/azulian-red.js index f42d92d..aeb5f91 100644 --- a/dev-assets/doodads/azulian/azulian-red.js +++ b/dev-assets/doodads/azulian/azulian-red.js @@ -1,3 +1,6 @@ +// Azulian (Red) +// DEPRECATED: they both share azulian.js now. + function main() { var playerSpeed = 4; var gravity = 4; diff --git a/dev-assets/doodads/azulian/azulian.js b/dev-assets/doodads/azulian/azulian.js index 3aa7637..54db87e 100644 --- a/dev-assets/doodads/azulian/azulian.js +++ b/dev-assets/doodads/azulian/azulian.js @@ -1,38 +1,74 @@ -function main() { - var playerSpeed = 12; - var gravity = 4; - var Vx = Vy = 0; +// Azulian (Red and Blue) +var playerSpeed = 12, + animating = false, + direction = "right"; - var animating = false; - var animStart = animEnd = 0; - var animFrame = animStart; +function setupAnimations(color) { + var left = color === 'blue' ? 'blu-wl' : 'red-wl', + right = color === 'blue' ? 'blu-wr' : 'red-wr', + leftFrames = [left + '1', left + '2', left + '3', left + '4'], + rightFrames = [right + '1', right + '2', right + '3', right + '4']; + + Self.AddAnimation("walk-left", 100, leftFrames); + Self.AddAnimation("walk-right", 100, rightFrames); +} + +function main() { + var color = Self.GetTag("color"); + playerSpeed = color === 'blue' ? 2 : 4; Self.SetMobile(true); Self.SetGravity(true); Self.SetInventory(true); - Self.SetHitbox(7, 4, 17, 28); - Self.AddAnimation("walk-left", 100, ["blu-wl1", "blu-wl2", "blu-wl3", "blu-wl4"]); - Self.AddAnimation("walk-right", 100, ["blu-wr1", "blu-wr2", "blu-wr3", "blu-wr4"]); + Self.SetHitbox(0, 0, 24, 32); + setupAnimations(color); + if (Self.IsPlayer()) { + return playerControls(); + } + + // A.I. pattern: walks back and forth, turning around + // when it meets resistance. + + // Sample our X position every few frames and detect if we've hit a solid wall. + var sampleTick = 0; + var sampleRate = 5; + var lastSampledX = 0; + + setInterval(function () { + if (sampleTick % sampleRate === 0) { + var curX = Self.Position().X; + var delta = Math.abs(curX - lastSampledX); + if (delta < 5) { + direction = direction === "right" ? "left" : "right"; + } + lastSampledX = curX; + } + sampleTick++; + + var Vx = parseFloat(playerSpeed * (direction === "left" ? -1 : 1)); + Self.SetVelocity(Vector(Vx, 0.0)); + + if (!Self.IsAnimating()) { + Self.PlayAnimation("walk-" + direction, null); + } + }, 100); +} + +function playerControls() { + // Note: player speed is controlled by the engine. Events.OnKeypress(function (ev) { - Vx = 0; - Vy = 0; - if (ev.Right) { if (!Self.IsAnimating()) { Self.PlayAnimation("walk-right", null); } - Vx = playerSpeed; } else if (ev.Left) { if (!Self.IsAnimating()) { Self.PlayAnimation("walk-left", null); } - Vx = -playerSpeed; } else { Self.StopAnimation(); animating = false; } - - // Self.SetVelocity(Point(Vx, Vy)); }) } diff --git a/dev-assets/doodads/build.sh b/dev-assets/doodads/build.sh index 9c99564..d115872 100755 --- a/dev-assets/doodads/build.sh +++ b/dev-assets/doodads/build.sh @@ -91,5 +91,5 @@ objects onoff warpdoor doodad edit-doodad -quiet -lock -author "Noah" ../../assets/doodads/*.doodad -doodad edit-doodad -hide ../../assets/doodads/azu-blu.doodad +doodad edit-doodad ../../assets/doodads/azu-blu.doodad doodad edit-doodad -hide ../../assets/doodads/boy.doodad diff --git a/dev-assets/doodads/objects/Makefile b/dev-assets/doodads/objects/Makefile index c56b8ed..86e33ce 100644 --- a/dev-assets/doodads/objects/Makefile +++ b/dev-assets/doodads/objects/Makefile @@ -4,6 +4,7 @@ ALL: build build: # Start Flag doodad convert -t "Start Flag" start-flag.png start-flag.doodad + doodad install-script start-flag.js start-flag.doodad # Exit Flag doodad convert -t "Exit Flag" exit-flag.png exit-flag.doodad diff --git a/dev-assets/doodads/objects/start-flag.js b/dev-assets/doodads/objects/start-flag.js new file mode 100644 index 0000000..dc84a77 --- /dev/null +++ b/dev-assets/doodads/objects/start-flag.js @@ -0,0 +1,11 @@ +// Start Flag. +function main() { + Self.SetHitbox(22 + 16, 16, 75 - 16, 86); + + // Linking a doodad to the Start Flag sets the + // player character. Destroy the original doodads. + var links = Self.GetLinks(); + for (var i = 0; i < links.length; i++) { + links[i].Destroy(); + } +} diff --git a/pkg/balance/numbers.go b/pkg/balance/numbers.go index d269a78..154ad5a 100644 --- a/pkg/balance/numbers.go +++ b/pkg/balance/numbers.go @@ -13,7 +13,7 @@ var ( // Window scrolling behavior in Play Mode. ScrollboxOffset = render.Point{ // from center of screen - X: 40, + X: 60, Y: 100, } diff --git a/pkg/doodads/drawing.go b/pkg/doodads/drawing.go index 2ccc1b5..f57a282 100644 --- a/pkg/doodads/drawing.go +++ b/pkg/doodads/drawing.go @@ -22,7 +22,7 @@ type Drawing struct { // an empty ID string, it will make a random UUIDv4 ID. func NewDrawing(id string, doodad *Doodad) *Drawing { if id == "" { - id = uuid.Must(uuid.NewRandom()).String() + id = uuid.Must(uuid.NewUUID()).String() } return &Drawing{ id: id, diff --git a/pkg/editor_ui.go b/pkg/editor_ui.go index 1609089..91d5509 100644 --- a/pkg/editor_ui.go +++ b/pkg/editor_ui.go @@ -378,6 +378,7 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas { for _, actor := range actors { u.Scene.Level.Actors.Remove(actor) } + u.Scene.Level.PruneLinks() drawing.InstallActors(u.Scene.Level.Actors) } } @@ -395,8 +396,15 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas { // The actors are a uix.Actor which houses a level.Actor which we // want to update to map each other's IDs. idA, idB := a.Actor.ID(), b.Actor.ID() - a.Actor.AddLink(idB) - b.Actor.AddLink(idA) + + // Are they already linked? + if a.Actor.IsLinked(idB) || b.Actor.IsLinked(idA) { + a.Actor.Unlink(idB) + b.Actor.Unlink(idA) + } else { + a.Actor.AddLink(idB) + b.Actor.AddLink(idA) + } // Reset the Link tool. d.Flash("Linked '%s' and '%s' together", a.Doodad().Title, b.Doodad().Title) diff --git a/pkg/level/actors.go b/pkg/level/actors.go index 3d1ba1d..6fd7567 100644 --- a/pkg/level/actors.go +++ b/pkg/level/actors.go @@ -19,7 +19,7 @@ func (m ActorMap) Inflate() { // given a random UUIDv4 ID. func (m ActorMap) Add(a *Actor) { if a.id == "" { - a.id = uuid.Must(uuid.NewRandom()).String() + a.id = uuid.Must(uuid.NewUUID()).String() } m[a.id] = a } @@ -57,3 +57,25 @@ func (a *Actor) AddLink(id string) { } a.Links = append(a.Links, id) } + +// Unlink removes the linked actor's ID. +func (a *Actor) Unlink(id string) { + var newLinks []string + for _, exist := range a.Links { + if exist == id { + continue + } + newLinks = append(newLinks, exist) + } + a.Links = newLinks +} + +// IsLinked checks if the actor is linked to the other actor's ID. +func (a *Actor) IsLinked(id string) bool { + for _, exist := range a.Links { + if exist == id { + return true + } + } + return false +} diff --git a/pkg/level/chunk.go b/pkg/level/chunk.go index f348d7e..e506b85 100644 --- a/pkg/level/chunk.go +++ b/pkg/level/chunk.go @@ -134,7 +134,7 @@ func (c *Chunk) generateTexture(mask render.Color) (render.Texturer, error) { // Generate a unique name for this chunk cache. var name string if c.uuid == uuid.Nil { - c.uuid = uuid.Must(uuid.NewRandom()) + c.uuid = uuid.Must(uuid.NewUUID()) } name = c.uuid.String() diff --git a/pkg/play_scene.go b/pkg/play_scene.go index 9df0f04..977b6dd 100644 --- a/pkg/play_scene.go +++ b/pkg/play_scene.go @@ -251,40 +251,51 @@ func (s *PlayScene) setupAsync(d *Doodle) error { // setupPlayer creates and configures the Player Character in the level. func (s *PlayScene) setupPlayer() { - // Load in the player character. - player, err := doodads.LoadFile(balance.PlayerCharacterDoodad) - if err != nil { - log.Error("PlayScene.Setup: failed to load player doodad: %s", err) - player = doodads.NewDummy(32) - } - // Find the spawn point of the player. Search the level for the // "start-flag.doodad" var ( + playerCharacterFilename = balance.PlayerCharacterDoodad + spawn render.Point + flag *level.Actor + flagSize = render.NewRect(86, 86) // TODO: start-flag.doodad is 86x86 px flagCount int ) for actorID, actor := range s.Level.Actors { if actor.Filename == "start-flag.doodad" { - if flagCount > 1 { + // Support alternative player characters: if the Start Flag is linked + // to another actor, that actor becomes the player character. + for _, linkID := range actor.Links { + if linkedActor, ok := s.Level.Actors[linkID]; ok { + playerCharacterFilename = linkedActor.Filename + log.Info("Playing as: %s", playerCharacterFilename) + } break } // TODO: start-flag.doodad is 86x86 pixels but we can't tell that // from right here. - size := render.NewRect(86, 86) log.Info("Found start-flag.doodad at %s (ID %s)", actor.Point, actorID) - spawn = render.NewPoint( - // X: centered inside the flag. - actor.Point.X+(size.W/2)-(player.Layers[0].Chunker.Size/2), - - // Y: the bottom of the flag, 4 pixels from the floor. - actor.Point.Y+size.H-4-(player.Layers[0].Chunker.Size), - ) - flagCount++ + flag = actor + break } } + // Load in the player character. + player, err := doodads.LoadFile(playerCharacterFilename) + if err != nil { + log.Error("PlayScene.Setup: failed to load player doodad: %s", err) + player = doodads.NewDummy(32) + } + + spawn = render.NewPoint( + // X: centered inside the flag. + flag.Point.X+(flagSize.W/2)-(player.Layers[0].Chunker.Size/2), + + // Y: the bottom of the flag, 4 pixels from the floor. + flag.Point.Y+flagSize.H-4-(player.Layers[0].Chunker.Size), + ) + // Surface warnings around the spawn flag. if flagCount == 0 { s.d.Flash("Warning: this level contained no Start Flag.") diff --git a/pkg/uix/actor.go b/pkg/uix/actor.go index 970080a..2b0643a 100644 --- a/pkg/uix/actor.go +++ b/pkg/uix/actor.go @@ -63,7 +63,7 @@ type Actor struct { // If the id is blank, a new UUIDv4 is generated. func NewActor(id string, levelActor *level.Actor, doodad *doodads.Doodad) *Actor { if id == "" { - id = uuid.Must(uuid.NewRandom()).String() + id = uuid.Must(uuid.NewUUID()).String() } size := doodad.Layers[0].Chunker.Size