diff --git a/dev-assets/doodads/bird/bird.js b/dev-assets/doodads/bird/bird.js new file mode 100644 index 0000000..ca8db3e --- /dev/null +++ b/dev-assets/doodads/bird/bird.js @@ -0,0 +1,54 @@ +// Red bird mob. +function main() { + var speed = 4; + var Vx = Vy = 0; + var altitude = Self.Position().Y; // original height in the level + + console.log("Bird altitude is %d", altitude); + + var direction = "left"; + var states = { + flying: 0, + diving: 1, + }; + var state = states.flying; + + Self.SetMobile(true); + Self.SetGravity(false); + Self.SetHitbox(0, 10, 46, 32); + Self.AddAnimation("fly-left", 100, ["left-1", "left-2"]); + Self.AddAnimation("fly-right", 100, ["right-1", "right-2"]); + + Events.OnCollide(function(e) { + if (e.Actor.IsMobile() && e.InHitbox) { + return false; + } + }); + + // Sample our X position every few frames and detect if we've hit a solid wall. + var sampleTick = 0; + var sampleRate = 2; + var lastSampledX = 0; + var lastSampledY = 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++; + + // TODO: Vector() requires floats, pain in the butt for JS, + // the JS API should be friendlier and custom... + var Vx = parseFloat(speed * (direction === "left" ? -1 : 1)); + Self.SetVelocity(Vector(Vx, 0.0)); + + if (!Self.IsAnimating()) { + Self.PlayAnimation("fly-"+direction, null); + } + }, 100); +} diff --git a/dev-assets/doodads/bird/dive-left.png b/dev-assets/doodads/bird/dive-left.png new file mode 100644 index 0000000..4ec5794 Binary files /dev/null and b/dev-assets/doodads/bird/dive-left.png differ diff --git a/dev-assets/doodads/bird/dive-right.png b/dev-assets/doodads/bird/dive-right.png new file mode 100644 index 0000000..8516ae3 Binary files /dev/null and b/dev-assets/doodads/bird/dive-right.png differ diff --git a/dev-assets/doodads/bird/left-1.png b/dev-assets/doodads/bird/left-1.png new file mode 100644 index 0000000..e3c6e50 Binary files /dev/null and b/dev-assets/doodads/bird/left-1.png differ diff --git a/dev-assets/doodads/bird/left-2.png b/dev-assets/doodads/bird/left-2.png new file mode 100644 index 0000000..e093f1f Binary files /dev/null and b/dev-assets/doodads/bird/left-2.png differ diff --git a/dev-assets/doodads/bird/right-1.png b/dev-assets/doodads/bird/right-1.png new file mode 100644 index 0000000..91930e2 Binary files /dev/null and b/dev-assets/doodads/bird/right-1.png differ diff --git a/dev-assets/doodads/bird/right-2.png b/dev-assets/doodads/bird/right-2.png new file mode 100644 index 0000000..380b3b3 Binary files /dev/null and b/dev-assets/doodads/bird/right-2.png differ diff --git a/dev-assets/doodads/build.sh b/dev-assets/doodads/build.sh index 784a47c..d9d7f8a 100755 --- a/dev-assets/doodads/build.sh +++ b/dev-assets/doodads/build.sh @@ -98,6 +98,17 @@ azulians() { cd .. } +mobs() { + cd bird/ + + doodad convert -t "Bird (red)" left-1.png left-2.png right-1.png right-2.png \ + dive-left.png dive-right.png bird-red.doodad + doodad install-script bird.js bird-red.doodad + + cp *.doodad ../../../assets/doodads/ + cd .. +} + objects() { cd objects/ @@ -136,14 +147,38 @@ onoff() { cd .. } +warpdoor() { + cd warp-door/ + + doodad convert -t "Warp Door" door-1.png door-2.png door-3.png door-4.png warp-door.doodad + doodad edit-doodad -q --tag color=none warp-door.doodad + doodad install-script warp-door.js warp-door.doodad + + doodad convert -t "Warp Door (Blue)" blue-1.png blue-2.png blue-3.png blue-4.png blue-off.png \ + warp-door-blue.doodad + doodad edit-doodad -q --tag color=blue warp-door-blue.doodad + doodad install-script warp-door.js warp-door-blue.doodad + + doodad convert -t "Warp Door (Orange)" orange-off.png orange-1.png orange-2.png orange-3.png orange-4.png \ + warp-door-orange.doodad + doodad edit-doodad -q --tag color=orange warp-door-orange.doodad + doodad install-script warp-door.js warp-door-orange.doodad + + cp *.doodad ../../../assets/doodads/ + + cd .. +} + boy buttons switches doors trapdoors azulians +mobs 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 -hide ../../assets/doodads/boy.doodad diff --git a/dev-assets/doodads/on-off/blue-button.png b/dev-assets/doodads/on-off/blue-button.png index 184627a..9df37be 100644 Binary files a/dev-assets/doodads/on-off/blue-button.png and b/dev-assets/doodads/on-off/blue-button.png differ diff --git a/dev-assets/doodads/on-off/blue-off.png b/dev-assets/doodads/on-off/blue-off.png index 6275e2b..1375fe6 100644 Binary files a/dev-assets/doodads/on-off/blue-off.png and b/dev-assets/doodads/on-off/blue-off.png differ diff --git a/dev-assets/doodads/on-off/blue-on.png b/dev-assets/doodads/on-off/blue-on.png index e200231..20cc9ba 100644 Binary files a/dev-assets/doodads/on-off/blue-on.png and b/dev-assets/doodads/on-off/blue-on.png differ diff --git a/dev-assets/doodads/on-off/orange-button.png b/dev-assets/doodads/on-off/orange-button.png index ec5ef16..fbf2cde 100644 Binary files a/dev-assets/doodads/on-off/orange-button.png and b/dev-assets/doodads/on-off/orange-button.png differ diff --git a/dev-assets/doodads/on-off/orange-off.png b/dev-assets/doodads/on-off/orange-off.png index 1ce8f1e..e2fa95e 100644 Binary files a/dev-assets/doodads/on-off/orange-off.png and b/dev-assets/doodads/on-off/orange-off.png differ diff --git a/dev-assets/doodads/on-off/orange-on.png b/dev-assets/doodads/on-off/orange-on.png index c56e09e..0aaae44 100644 Binary files a/dev-assets/doodads/on-off/orange-on.png and b/dev-assets/doodads/on-off/orange-on.png differ diff --git a/dev-assets/doodads/on-off/state-block-blue.js b/dev-assets/doodads/on-off/state-block-blue.js index f09f5cc..b754ea7 100644 --- a/dev-assets/doodads/on-off/state-block-blue.js +++ b/dev-assets/doodads/on-off/state-block-blue.js @@ -1,6 +1,6 @@ // Blue State Block function main() { - Self.SetHitbox(0, 0, 33, 33); + Self.SetHitbox(0, 0, 42, 42); // Blue block is ON by default. var state = true; diff --git a/dev-assets/doodads/on-off/state-block-orange.js b/dev-assets/doodads/on-off/state-block-orange.js index dfea8b7..8989f73 100644 --- a/dev-assets/doodads/on-off/state-block-orange.js +++ b/dev-assets/doodads/on-off/state-block-orange.js @@ -1,6 +1,6 @@ // Orange State Block function main() { - Self.SetHitbox(0, 0, 33, 33); + Self.SetHitbox(0, 0, 42, 42); // Orange block is OFF by default. var state = false; diff --git a/dev-assets/doodads/on-off/state-button.js b/dev-assets/doodads/on-off/state-button.js index 85084d8..c5e7bd7 100644 --- a/dev-assets/doodads/on-off/state-button.js +++ b/dev-assets/doodads/on-off/state-button.js @@ -5,7 +5,7 @@ var state = false; function main() { console.log("%s ID '%s' initialized!", Self.Title, Self.ID()); - Self.SetHitbox(0, 0, 33, 33); + Self.SetHitbox(0, 0, 42, 42); // When the button is activated, don't keep toggling state until we're not // being touched again. diff --git a/dev-assets/doodads/warp-door/blue-1.png b/dev-assets/doodads/warp-door/blue-1.png new file mode 100644 index 0000000..4921faf Binary files /dev/null and b/dev-assets/doodads/warp-door/blue-1.png differ diff --git a/dev-assets/doodads/warp-door/blue-2.png b/dev-assets/doodads/warp-door/blue-2.png new file mode 100644 index 0000000..ca96abf Binary files /dev/null and b/dev-assets/doodads/warp-door/blue-2.png differ diff --git a/dev-assets/doodads/warp-door/blue-3.png b/dev-assets/doodads/warp-door/blue-3.png new file mode 100644 index 0000000..1a1e75c Binary files /dev/null and b/dev-assets/doodads/warp-door/blue-3.png differ diff --git a/dev-assets/doodads/warp-door/blue-4.png b/dev-assets/doodads/warp-door/blue-4.png new file mode 100644 index 0000000..f676890 Binary files /dev/null and b/dev-assets/doodads/warp-door/blue-4.png differ diff --git a/dev-assets/doodads/warp-door/blue-off.png b/dev-assets/doodads/warp-door/blue-off.png new file mode 100644 index 0000000..a42b51c Binary files /dev/null and b/dev-assets/doodads/warp-door/blue-off.png differ diff --git a/dev-assets/doodads/warp-door/door-1.png b/dev-assets/doodads/warp-door/door-1.png new file mode 100644 index 0000000..e057d24 Binary files /dev/null and b/dev-assets/doodads/warp-door/door-1.png differ diff --git a/dev-assets/doodads/warp-door/door-2.png b/dev-assets/doodads/warp-door/door-2.png new file mode 100644 index 0000000..71d0b61 Binary files /dev/null and b/dev-assets/doodads/warp-door/door-2.png differ diff --git a/dev-assets/doodads/warp-door/door-3.png b/dev-assets/doodads/warp-door/door-3.png new file mode 100644 index 0000000..d225a8d Binary files /dev/null and b/dev-assets/doodads/warp-door/door-3.png differ diff --git a/dev-assets/doodads/warp-door/door-4.png b/dev-assets/doodads/warp-door/door-4.png new file mode 100644 index 0000000..65f3e03 Binary files /dev/null and b/dev-assets/doodads/warp-door/door-4.png differ diff --git a/dev-assets/doodads/warp-door/orange-1.png b/dev-assets/doodads/warp-door/orange-1.png new file mode 100644 index 0000000..01a6a61 Binary files /dev/null and b/dev-assets/doodads/warp-door/orange-1.png differ diff --git a/dev-assets/doodads/warp-door/orange-2.png b/dev-assets/doodads/warp-door/orange-2.png new file mode 100644 index 0000000..2bb01fb Binary files /dev/null and b/dev-assets/doodads/warp-door/orange-2.png differ diff --git a/dev-assets/doodads/warp-door/orange-3.png b/dev-assets/doodads/warp-door/orange-3.png new file mode 100644 index 0000000..1983e86 Binary files /dev/null and b/dev-assets/doodads/warp-door/orange-3.png differ diff --git a/dev-assets/doodads/warp-door/orange-4.png b/dev-assets/doodads/warp-door/orange-4.png new file mode 100644 index 0000000..f8c08b2 Binary files /dev/null and b/dev-assets/doodads/warp-door/orange-4.png differ diff --git a/dev-assets/doodads/warp-door/orange-off.png b/dev-assets/doodads/warp-door/orange-off.png new file mode 100644 index 0000000..476703a Binary files /dev/null and b/dev-assets/doodads/warp-door/orange-off.png differ diff --git a/dev-assets/doodads/warp-door/warp-door.js b/dev-assets/doodads/warp-door/warp-door.js new file mode 100644 index 0000000..2dc73ab --- /dev/null +++ b/dev-assets/doodads/warp-door/warp-door.js @@ -0,0 +1,81 @@ +// Warp Doors +function main() { + console.log("Warp Door %s Initialized", Self.Title); + + Self.SetHitbox(0, 0, 34, 76); + + // Are we a blue or orange door? Regular warp door will be 'none' + var color = Self.GetTag("color"); + var isStateDoor = color === 'blue' || color === 'orange'; + var state = color === 'blue'; // Blue door is ON by default. + + var animating = false; + var collide = false; + + // Declare animations and sprite names. + var animSpeed = 100; + var spriteDefault, spriteDisabled; // the latter for state doors. + if (color === 'blue') { + Self.AddAnimation("open", animSpeed, ["blue-2", "blue-3", "blue-4"]); + Self.AddAnimation("close", animSpeed, ["blue-4", "blue-3", "blue-2", "blue-1"]); + spriteDefault = "blue-1"; + spriteDisabled = "blue-off"; + } else if (color === 'orange') { + Self.AddAnimation("open", animSpeed, ["orange-2", "orange-3", "orange-4"]); + Self.AddAnimation("close", animSpeed, ["orange-4", "orange-3", "orange-2", "orange-1"]); + spriteDefault = "orange-1"; + spriteDisabled = "orange-off"; + } else { + Self.AddAnimation("open", animSpeed, ["door-2", "door-3", "door-4"]); + Self.AddAnimation("close", animSpeed, ["door-4", "door-3", "door-2", "door-1"]); + spriteDefault = "door-1"; + } + + console.log("Warp %s: default=%s disabled=%+v color=%s isState=%+v state=%+v", Self.Title, spriteDefault, spriteDisabled, color, isStateDoor, state); + + // Subscribe to the global state-change if we are a state door. + if (isStateDoor) { + Message.Subscribe("broadcast:state-change", function(newState) { + console.log("Warp %s: received state to %+v", Self.Title, newState); + state = color === 'blue' ? !newState : newState; + + // Activate or deactivate the door. + Self.ShowLayerNamed(state ? spriteDefault : spriteDisabled); + }); + } + + // TODO: respond to a "Use" button instead of a Collide to open the door. + Events.OnCollide(function(e) { + if (!e.Settled) { + return; + } + + if (animating || collide) { + return; + } + + // Only players can use doors for now. + if (e.Actor.IsPlayer() && e.InHitbox) { + if (isStateDoor && !state) { + // The state door is inactive (dotted outline). + return; + } + + // Play the open and close animation. + animating = true; + collide = true; + Self.PlayAnimation("open", function() { + e.Actor.Hide() + Self.PlayAnimation("close", function() { + Self.ShowLayerNamed(isStateDoor && !state ? spriteDisabled : spriteDefault); + e.Actor.Show() + animating = false; + }); + }); + } + }); + + Events.OnLeave(function(e) { + collide = false; + }); +} diff --git a/pkg/uix/actor.go b/pkg/uix/actor.go index cc16527..72ef702 100644 --- a/pkg/uix/actor.go +++ b/pkg/uix/actor.go @@ -37,6 +37,7 @@ type Actor struct { hasGravity bool isMobile bool // Mobile character, such as the player or an enemy noclip bool // Disable collision detection + hidden bool // invisible, via Hide() and Show() hitbox render.Rect inventory map[string]int // item inventory. doodad name -> quantity, 0 for key item. data map[string]string // arbitrary key/value store. DEPRECATED ?? @@ -117,6 +118,12 @@ func (a *Actor) IsMobile() bool { return a.isMobile } +// IsPlayer returns whether the actor is the player character. +// It's true when the Actor ID is "PLAYER" +func (a *Actor) IsPlayer() bool { + return a.Canvas.Name == "PLAYER" +} + // Size returns the size of the actor, from the underlying doodads.Drawing. func (a *Actor) Size() render.Rect { return a.Drawing.Size() @@ -157,6 +164,16 @@ func (a *Actor) SetGrounded(v bool) { a.grounded = v } +// Hide makes the actor invisible. +func (a *Actor) Hide() { + a.hidden = true +} + +// Show a hidden actor. +func (a *Actor) Show() { + a.hidden = false +} + // SetNoclip sets the noclip setting for an actor. If true, the actor can // clip through level geometry. func (a *Actor) SetNoclip(v bool) { diff --git a/pkg/uix/canvas_actors.go b/pkg/uix/canvas_actors.go index ce8f6ad..5bb02e3 100644 --- a/pkg/uix/canvas_actors.go +++ b/pkg/uix/canvas_actors.go @@ -113,6 +113,12 @@ func (w *Canvas) drawActors(e render.Engine, p render.Point) { log.Error("Canvas.drawActors: null actor at index %d (of %d actors)", i, len(w.actors)) continue } + + // Skip hidden actors. + if a.hidden { + continue + } + var ( can = a.Canvas // Canvas widget that draws the actor actorPoint = a.Position()