diff --git a/assets/wallpapers/blue-parchment.png b/assets/wallpapers/blue-parchment.png new file mode 100644 index 0000000..5e951ea Binary files /dev/null and b/assets/wallpapers/blue-parchment.png differ diff --git a/assets/wallpapers/green-parchment.png b/assets/wallpapers/green-parchment.png new file mode 100644 index 0000000..d0b742d Binary files /dev/null and b/assets/wallpapers/green-parchment.png differ diff --git a/assets/wallpapers/red-parchment.png b/assets/wallpapers/red-parchment.png new file mode 100644 index 0000000..5cb2ffb Binary files /dev/null and b/assets/wallpapers/red-parchment.png differ diff --git a/assets/wallpapers/white-parchment.png b/assets/wallpapers/white-parchment.png new file mode 100644 index 0000000..9e1fcce Binary files /dev/null and b/assets/wallpapers/white-parchment.png differ diff --git a/assets/wallpapers/yellow-parchment.png b/assets/wallpapers/yellow-parchment.png new file mode 100644 index 0000000..76d14d9 Binary files /dev/null and b/assets/wallpapers/yellow-parchment.png differ diff --git a/dev-assets/doodads/box/box.js b/dev-assets/doodads/box/box.js index cd96a6e..b78c83d 100644 --- a/dev-assets/doodads/box/box.js +++ b/dev-assets/doodads/box/box.js @@ -19,7 +19,15 @@ function main() { let overlap = e.Overlap; if (overlap.Y === 0 && !(overlap.X === 0 && overlap.W < 5) && !(overlap.X === size)) { - // Standing on top, ignore. + // Be sure to position them snug on top. + // TODO: this might be a nice general solution in the + // collision detector... + console.log("new box code"); + e.Actor.MoveTo(Point( + e.Actor.Position().X, + Self.Position().Y - e.Actor.Hitbox().Y - e.Actor.Hitbox().H - 2, + )) + e.Actor.SetGrounded(true); return false; } else if (overlap.Y === size) { // From the bottom, boop it up. diff --git a/dev-assets/doodads/build.sh b/dev-assets/doodads/build.sh index 6bc41eb..a668a3e 100755 --- a/dev-assets/doodads/build.sh +++ b/dev-assets/doodads/build.sh @@ -34,6 +34,10 @@ doors() { cd doors/ make cd .. + + cd gems/ + make + cd .. } trapdoors() { @@ -84,6 +88,16 @@ warpdoor() { cd .. } +creatures() { + cd snake/ + make + cd .. + + cd crusher/ + make + cd .. +} + boy buttons switches @@ -94,6 +108,7 @@ mobs objects onoff warpdoor +creatures doodad edit-doodad -quiet -lock -author "Noah" ../../assets/doodads/*.doodad doodad edit-doodad ../../assets/doodads/azu-blu.doodad doodad edit-doodad -hide ../../assets/doodads/boy.doodad diff --git a/dev-assets/doodads/crusher/Makefile b/dev-assets/doodads/crusher/Makefile new file mode 100644 index 0000000..abbac86 --- /dev/null +++ b/dev-assets/doodads/crusher/Makefile @@ -0,0 +1,14 @@ +ALL: build + +.PHONY: build +build: + doodad convert -t "Crusher" sleep.png peek-left.png peek-right.png \ + angry.png ouch.png crusher.doodad + doodad install-script crusher.js crusher.doodad + + # Tag the category for these doodads + for i in *.doodad; do\ + doodad edit-doodad --tag "category=creatures" $${i};\ + done + + cp *.doodad ../../../assets/doodads/ \ No newline at end of file diff --git a/dev-assets/doodads/crusher/angry.png b/dev-assets/doodads/crusher/angry.png new file mode 100644 index 0000000..6fdc159 Binary files /dev/null and b/dev-assets/doodads/crusher/angry.png differ diff --git a/dev-assets/doodads/crusher/crusher.js b/dev-assets/doodads/crusher/crusher.js new file mode 100644 index 0000000..63bcfcd --- /dev/null +++ b/dev-assets/doodads/crusher/crusher.js @@ -0,0 +1,200 @@ +// Crusher + +/* +A.I. Behaviors: + +- Sleeps and hangs in the air in a high place. +- When a player gets nearby, it begins "peeking" in their direction. +- When the player is below, tries to drop and crush them or any + other mobile doodad. +- The top edge is safe to walk on and ride back up like an elevator. +*/ + +let direction = "left", + dropSpeed = 12, + riseSpeed = 4, + watchRadius = 300, // player nearby distance to start peeking + fallRadius = 120, // player distance before it drops + helmetThickness = 48, // safe solid hitbox height + fireThickness = 12, // dangerous bottom thickness + targetAltitude = Self.Position() + lastAltitude = targetAltitude.Y + size = Self.Size(); + +const states = { + idle: 0, + peeking: 1, + falling: 2, + hit: 3, + rising: 4, +}; +let state = states.idle; + +function main() { + Self.SetMobile(true); + Self.SetGravity(false); + Self.SetInvulnerable(true); + Self.SetHitbox(5, 2, 90, 73); + Self.AddAnimation("hit", 50, + ["angry", "ouch", "angry", "angry", "angry", "angry", + "sleep", "sleep", "sleep", "sleep", "sleep", "sleep", + "sleep", "sleep", "sleep", "sleep", "sleep", "sleep", + "sleep", "sleep", "sleep", "sleep", "sleep", "sleep"], + ) + + // Player Character controls? + if (Self.IsPlayer()) { + return player(); + } + + let hitbox = Self.Hitbox(); + + Events.OnCollide((e) => { + // The bottom is deadly if falling. + if (state === states.falling || state === states.hit && e.Settled) { + if (e.Actor.IsMobile() && e.InHitbox && !e.Actor.Invulnerable()) { + if (e.Overlap.H > 72) { + if (e.Actor.IsPlayer()) { + FailLevel("Don't get crushed!"); + return; + } else { + e.Actor.Destroy(); + } + } + } + } + + // Our top edge is always solid. + if (e.Actor.IsPlayer() && e.InHitbox) { + if (e.Overlap.Y < helmetThickness) { + // Be sure to position them snug on top. + // TODO: this might be a nice general solution in the + // collision detector... + e.Actor.MoveTo(Point( + e.Actor.Position().X, + Self.Position().Y - e.Actor.Hitbox().Y - e.Actor.Hitbox().H, + )) + e.Actor.SetGrounded(true); + } + } + + // The whole hitbox is ordinarily solid. + if (state !== state.falling) { + if (e.Actor.IsMobile() && e.InHitbox) { + return false; + } + } + }); + + setInterval(() => { + // Find the player. + let player = Actors.FindPlayer(), + playerPoint = player.Position(), + point = Self.Position(), + delta = 0, + nearby = false, + below = false; + + // Face the player. + if (playerPoint.X < point.X + (size.W / 2)) { + direction = "left"; + delta = Math.abs(playerPoint.X - (point.X + (size.W/2))); + } + else if (playerPoint.X > point.X + (size.W / 2)) { + direction = "right"; + delta = Math.abs(playerPoint.X - (point.X + (size.W/2))); + } + + if (delta < watchRadius) { + nearby = true; + } + if (delta < fallRadius) { + // Check if the player is below us. + if (playerPoint.Y > point.Y + size.H) { + below = true; + } + } + + switch (state) { + case states.idle: + if (nearby) { + Self.ShowLayerNamed("peek-"+direction); + } else { + Self.ShowLayerNamed("sleep"); + } + + if (below) { + state = states.falling; + } else if (nearby) { + state = states.peeking; + } + + break; + case states.peeking: + if (nearby) { + Self.ShowLayerNamed("peek-"+direction); + } else { + state = states.idle; + break; + } + + if (below) { + state = states.falling; + } + + break; + case states.falling: + Self.ShowLayerNamed("angry"); + Self.SetVelocity(Vector(0.0, dropSpeed)); + + // Landed? + if (point.Y === lastAltitude) { + Sound.Play("crumbly-break.wav") + state = states.hit; + Self.PlayAnimation("hit", () => { + state = states.rising; + }); + } + break; + case states.hit: + // A transitory state while the hit animation + // plays out. + break; + case states.rising: + Self.ShowLayerNamed("sleep"); + Self.SetVelocity(Vector(0, -riseSpeed)); + + point = Self.Position(); + if (point.Y <= targetAltitude.Y+4 || point.Y === lastAltitude.Y) { + Self.MoveTo(targetAltitude); + Self.SetVelocity(Vector(0, 0)) + state = states.idle; + } + } + + lastAltitude = point.Y; + }, 100); +} + +// If under control of the player character. +function player() { + Events.OnKeypress((ev) => { + if (ev.Right) { + direction = "right"; + } else if (ev.Left) { + direction = "left"; + } + + // Jump! + if (ev.Down) { + Self.ShowLayerNamed("angry"); + return; + } else if (ev.Right && ev.Left) { + Self.ShowLayerNamed("ouch"); + } else if (ev.Right || ev.Left) { + Self.ShowLayerNamed("peek-"+direction); + } else { + Self.ShowLayerNamed("sleep"); + } + }); +} diff --git a/dev-assets/doodads/crusher/ouch.png b/dev-assets/doodads/crusher/ouch.png new file mode 100644 index 0000000..c982e6e Binary files /dev/null and b/dev-assets/doodads/crusher/ouch.png differ diff --git a/dev-assets/doodads/crusher/peek-left.png b/dev-assets/doodads/crusher/peek-left.png new file mode 100644 index 0000000..b2327ba Binary files /dev/null and b/dev-assets/doodads/crusher/peek-left.png differ diff --git a/dev-assets/doodads/crusher/peek-right.png b/dev-assets/doodads/crusher/peek-right.png new file mode 100644 index 0000000..b6c7115 Binary files /dev/null and b/dev-assets/doodads/crusher/peek-right.png differ diff --git a/dev-assets/doodads/crusher/sleep.png b/dev-assets/doodads/crusher/sleep.png new file mode 100644 index 0000000..796f656 Binary files /dev/null and b/dev-assets/doodads/crusher/sleep.png differ diff --git a/dev-assets/doodads/gems/Makefile b/dev-assets/doodads/gems/Makefile new file mode 100644 index 0000000..f9e5d87 --- /dev/null +++ b/dev-assets/doodads/gems/Makefile @@ -0,0 +1,58 @@ +ALL: build + +.PHONY: build +build: + ### + # Gemstones + ### + + doodad convert -t "Gemstone (Green)" green-1.png green-2.png green-3.png green-4.png \ + gem-green.doodad + doodad install-script gemstone.js gem-green.doodad + doodad edit-doodad --tag "color=green" gem-green.doodad + + doodad convert -t "Gemstone (Red)" red-1.png red-2.png red-3.png red-4.png \ + gem-red.doodad + doodad install-script gemstone.js gem-red.doodad + doodad edit-doodad --tag "color=red" gem-red.doodad + + doodad convert -t "Gemstone (Blue)" blue-1.png blue-2.png blue-3.png blue-4.png \ + gem-blue.doodad + doodad install-script gemstone.js gem-blue.doodad + doodad edit-doodad --tag "color=blue" gem-blue.doodad + + doodad convert -t "Gemstone (Yellow)" yellow-1.png yellow-2.png yellow-3.png yellow-4.png \ + gem-yellow.doodad + doodad install-script gemstone.js gem-yellow.doodad + doodad edit-doodad --tag "color=yellow" gem-yellow.doodad + + ### + # Totems + ### + + doodad convert -t "Gemstone Totem (Green)" totem-green-1.png totem-green-2.png totem-green-3.png \ + totem-green-4.png totem-green-0.png gem-totem-green.doodad + doodad install-script totem.js gem-totem-green.doodad + doodad edit-doodad --tag "color=green" gem-totem-green.doodad + + doodad convert -t "Gemstone Totem (Yellow)" totem-yellow-1.png totem-yellow-2.png totem-yellow-3.png \ + totem-yellow-4.png totem-yellow-0.png gem-totem-yellow.doodad + doodad install-script totem.js gem-totem-yellow.doodad + doodad edit-doodad --tag "color=yellow" gem-totem-yellow.doodad + + doodad convert -t "Gemstone Totem (Blue)" totem-blue-1.png totem-blue-2.png totem-blue-3.png \ + totem-blue-4.png totem-blue-0.png gem-totem-blue.doodad + doodad install-script totem.js gem-totem-blue.doodad + doodad edit-doodad --tag "color=blue" gem-totem-blue.doodad + + doodad convert -t "Gemstone Totem (Red)" totem-red-1.png totem-red-2.png totem-red-3.png \ + totem-red-4.png totem-red-0.png gem-totem-red.doodad + doodad install-script totem.js gem-totem-red.doodad + doodad edit-doodad --tag "color=red" gem-totem-red.doodad + + # Tag the category for these doodads + for i in *.doodad; do\ + doodad edit-doodad --tag "category=doors" $${i};\ + done + + cp *.doodad ../../../assets/doodads/ \ No newline at end of file diff --git a/dev-assets/doodads/gems/blue-1.png b/dev-assets/doodads/gems/blue-1.png new file mode 100644 index 0000000..c74d062 Binary files /dev/null and b/dev-assets/doodads/gems/blue-1.png differ diff --git a/dev-assets/doodads/gems/blue-2.png b/dev-assets/doodads/gems/blue-2.png new file mode 100644 index 0000000..2760b45 Binary files /dev/null and b/dev-assets/doodads/gems/blue-2.png differ diff --git a/dev-assets/doodads/gems/blue-3.png b/dev-assets/doodads/gems/blue-3.png new file mode 100644 index 0000000..b40177c Binary files /dev/null and b/dev-assets/doodads/gems/blue-3.png differ diff --git a/dev-assets/doodads/gems/blue-4.png b/dev-assets/doodads/gems/blue-4.png new file mode 100644 index 0000000..63f8480 Binary files /dev/null and b/dev-assets/doodads/gems/blue-4.png differ diff --git a/dev-assets/doodads/gems/gemstone.js b/dev-assets/doodads/gems/gemstone.js new file mode 100644 index 0000000..efb9361 --- /dev/null +++ b/dev-assets/doodads/gems/gemstone.js @@ -0,0 +1,24 @@ +// Gem stone collectibles/keys. + +const color = Self.GetTag("color"), + shimmerFreq = 1000; + +function main() { + Self.SetMobile(true); + Self.SetGravity(true); + + Self.AddAnimation("shimmer", 100, [0, 1, 2, 3, 0]); + Events.OnCollide((e) => { + if (e.Settled) { + if (e.Actor.HasInventory()) { + Sound.Play("item-get.wav") + e.Actor.AddItem(Self.Filename, 1); + Self.Destroy(); + } + } + }); + + setInterval(() => { + Self.PlayAnimation("shimmer", null); + }, shimmerFreq); +} \ No newline at end of file diff --git a/dev-assets/doodads/gems/green-1.png b/dev-assets/doodads/gems/green-1.png new file mode 100644 index 0000000..ff76d09 Binary files /dev/null and b/dev-assets/doodads/gems/green-1.png differ diff --git a/dev-assets/doodads/gems/green-2.png b/dev-assets/doodads/gems/green-2.png new file mode 100644 index 0000000..996d565 Binary files /dev/null and b/dev-assets/doodads/gems/green-2.png differ diff --git a/dev-assets/doodads/gems/green-3.png b/dev-assets/doodads/gems/green-3.png new file mode 100644 index 0000000..8449974 Binary files /dev/null and b/dev-assets/doodads/gems/green-3.png differ diff --git a/dev-assets/doodads/gems/green-4.png b/dev-assets/doodads/gems/green-4.png new file mode 100644 index 0000000..a6e7064 Binary files /dev/null and b/dev-assets/doodads/gems/green-4.png differ diff --git a/dev-assets/doodads/gems/red-1.png b/dev-assets/doodads/gems/red-1.png new file mode 100644 index 0000000..9a14ea0 Binary files /dev/null and b/dev-assets/doodads/gems/red-1.png differ diff --git a/dev-assets/doodads/gems/red-2.png b/dev-assets/doodads/gems/red-2.png new file mode 100644 index 0000000..fdcf412 Binary files /dev/null and b/dev-assets/doodads/gems/red-2.png differ diff --git a/dev-assets/doodads/gems/red-3.png b/dev-assets/doodads/gems/red-3.png new file mode 100644 index 0000000..10ecf3c Binary files /dev/null and b/dev-assets/doodads/gems/red-3.png differ diff --git a/dev-assets/doodads/gems/red-4.png b/dev-assets/doodads/gems/red-4.png new file mode 100644 index 0000000..982ec5a Binary files /dev/null and b/dev-assets/doodads/gems/red-4.png differ diff --git a/dev-assets/doodads/gems/totem-blue-0.png b/dev-assets/doodads/gems/totem-blue-0.png new file mode 100644 index 0000000..0775a22 Binary files /dev/null and b/dev-assets/doodads/gems/totem-blue-0.png differ diff --git a/dev-assets/doodads/gems/totem-blue-1.png b/dev-assets/doodads/gems/totem-blue-1.png new file mode 100644 index 0000000..0f561a3 Binary files /dev/null and b/dev-assets/doodads/gems/totem-blue-1.png differ diff --git a/dev-assets/doodads/gems/totem-blue-2.png b/dev-assets/doodads/gems/totem-blue-2.png new file mode 100644 index 0000000..dbc0447 Binary files /dev/null and b/dev-assets/doodads/gems/totem-blue-2.png differ diff --git a/dev-assets/doodads/gems/totem-blue-3.png b/dev-assets/doodads/gems/totem-blue-3.png new file mode 100644 index 0000000..8373a3c Binary files /dev/null and b/dev-assets/doodads/gems/totem-blue-3.png differ diff --git a/dev-assets/doodads/gems/totem-blue-4.png b/dev-assets/doodads/gems/totem-blue-4.png new file mode 100644 index 0000000..8bb6a80 Binary files /dev/null and b/dev-assets/doodads/gems/totem-blue-4.png differ diff --git a/dev-assets/doodads/gems/totem-green-0.png b/dev-assets/doodads/gems/totem-green-0.png new file mode 100644 index 0000000..ff0f54d Binary files /dev/null and b/dev-assets/doodads/gems/totem-green-0.png differ diff --git a/dev-assets/doodads/gems/totem-green-1.png b/dev-assets/doodads/gems/totem-green-1.png new file mode 100644 index 0000000..4a2e33f Binary files /dev/null and b/dev-assets/doodads/gems/totem-green-1.png differ diff --git a/dev-assets/doodads/gems/totem-green-2.png b/dev-assets/doodads/gems/totem-green-2.png new file mode 100644 index 0000000..ae892e6 Binary files /dev/null and b/dev-assets/doodads/gems/totem-green-2.png differ diff --git a/dev-assets/doodads/gems/totem-green-3.png b/dev-assets/doodads/gems/totem-green-3.png new file mode 100644 index 0000000..b850991 Binary files /dev/null and b/dev-assets/doodads/gems/totem-green-3.png differ diff --git a/dev-assets/doodads/gems/totem-green-4.png b/dev-assets/doodads/gems/totem-green-4.png new file mode 100644 index 0000000..4aa4c7d Binary files /dev/null and b/dev-assets/doodads/gems/totem-green-4.png differ diff --git a/dev-assets/doodads/gems/totem-red-0.png b/dev-assets/doodads/gems/totem-red-0.png new file mode 100644 index 0000000..404245c Binary files /dev/null and b/dev-assets/doodads/gems/totem-red-0.png differ diff --git a/dev-assets/doodads/gems/totem-red-1.png b/dev-assets/doodads/gems/totem-red-1.png new file mode 100644 index 0000000..e7c1a35 Binary files /dev/null and b/dev-assets/doodads/gems/totem-red-1.png differ diff --git a/dev-assets/doodads/gems/totem-red-2.png b/dev-assets/doodads/gems/totem-red-2.png new file mode 100644 index 0000000..d08ce21 Binary files /dev/null and b/dev-assets/doodads/gems/totem-red-2.png differ diff --git a/dev-assets/doodads/gems/totem-red-3.png b/dev-assets/doodads/gems/totem-red-3.png new file mode 100644 index 0000000..bd56142 Binary files /dev/null and b/dev-assets/doodads/gems/totem-red-3.png differ diff --git a/dev-assets/doodads/gems/totem-red-4.png b/dev-assets/doodads/gems/totem-red-4.png new file mode 100644 index 0000000..98ddcc9 Binary files /dev/null and b/dev-assets/doodads/gems/totem-red-4.png differ diff --git a/dev-assets/doodads/gems/totem-yellow-0.png b/dev-assets/doodads/gems/totem-yellow-0.png new file mode 100644 index 0000000..5865701 Binary files /dev/null and b/dev-assets/doodads/gems/totem-yellow-0.png differ diff --git a/dev-assets/doodads/gems/totem-yellow-1.png b/dev-assets/doodads/gems/totem-yellow-1.png new file mode 100644 index 0000000..66c163b Binary files /dev/null and b/dev-assets/doodads/gems/totem-yellow-1.png differ diff --git a/dev-assets/doodads/gems/totem-yellow-2.png b/dev-assets/doodads/gems/totem-yellow-2.png new file mode 100644 index 0000000..9cb7081 Binary files /dev/null and b/dev-assets/doodads/gems/totem-yellow-2.png differ diff --git a/dev-assets/doodads/gems/totem-yellow-3.png b/dev-assets/doodads/gems/totem-yellow-3.png new file mode 100644 index 0000000..da774b7 Binary files /dev/null and b/dev-assets/doodads/gems/totem-yellow-3.png differ diff --git a/dev-assets/doodads/gems/totem-yellow-4.png b/dev-assets/doodads/gems/totem-yellow-4.png new file mode 100644 index 0000000..e227b7c Binary files /dev/null and b/dev-assets/doodads/gems/totem-yellow-4.png differ diff --git a/dev-assets/doodads/gems/totem.js b/dev-assets/doodads/gems/totem.js new file mode 100644 index 0000000..b2e494f --- /dev/null +++ b/dev-assets/doodads/gems/totem.js @@ -0,0 +1,108 @@ +// Gem stone totem socket. + +/* +The Totem is a type of key-door that holds onto its corresponding +Gemstone. When a doodad holding the right Gemstone touches the +totem, the totem takes the gemstone and activates. + +If the Totem is not linked to any other Totems, it immediately +sends a power(true) signal upon activation. + +If the Totem is linked to other Totems, it waits until all totems +have been activated before it will emit a power signal. Only one +such totem needs to be linked to e.g. an Electric Door - no matter +which totem is solved last, they'll all emit a power signal when +all of their linked totems are activated. +*/ + +let color = Self.GetTag("color"), + keyname = "gem-"+color+".doodad", + activated = false, + linkedReceiver = false, // is linked to a non-totem which might want power + totems = {}, // linked totems + shimmerFreq = 1000; + +function main() { + // Show the hollow socket on level load (last layer) + Self.ShowLayer(4); + + // Find any linked totems. + for (let link of Self.GetLinks()) { + if (link.Filename.indexOf("gem-totem") > -1) { + totems[link.ID()] = false; + } else { + linkedReceiver = true; + } + } + + console.log("Totem %s is linked to %d neighbors", Self.ID(), Object.keys(totems).length); + + // Shimmer animation is just like the gemstones: first 4 frames + // are the filled socket sprites. + Self.AddAnimation("shimmer", 100, [0, 1, 2, 3, 0]); + + Events.OnCollide((e) => { + if (activated) return; + + if (e.Actor.IsMobile() && e.Settled) { + // Do they have our gemstone? + let hasKey = e.Actor.HasItem(keyname) >= 0; + if (!hasKey) { + return; + } + + // Take the gemstone. + e.Actor.RemoveItem(keyname, 1); + Self.ShowLayer(0); + + // Emit to our linked totem neighbors. + activated = true; + Message.Publish("gem-totem:activated", Self.ID()); + tryPower(); + } + }); + + Message.Subscribe("gem-totem:activated", (totemId) => { + totems[totemId] = true; + tryPower(); + }) + + setInterval(() => { + if (activated) { + Self.PlayAnimation("shimmer", null); + } + }, shimmerFreq); +} + +// Try to send a power signal for an activated totem. +function tryPower() { + // Only emit power if we are linked to something other than a totem. + if (!linkedReceiver) { + return; + } + + console.log("Totem %s (%s) tries power", Self.ID(), Self.Filename); + + // Can't if any of our linked totems aren't activated. + try { + for (let totemId of Object.keys(totems)) { + console.log("Totem %s (%s) sees linked totem %s", Self.ID(), Self.Filename, totemId); + if (totems[totemId] === false) { + console.log("Can't, a linked totem not active!"); + return; + } + } + } catch(e) { + console.error("Caught: %s", e); + } + + // Can't if we aren't powered. + if (activated === false) { + console.log("Can't, we are not active!"); + return; + } + + // Emit power! + console.log("POWER!"); + Message.Publish("power", true); +} \ No newline at end of file diff --git a/dev-assets/doodads/gems/yellow-1.png b/dev-assets/doodads/gems/yellow-1.png new file mode 100644 index 0000000..016d42a Binary files /dev/null and b/dev-assets/doodads/gems/yellow-1.png differ diff --git a/dev-assets/doodads/gems/yellow-2.png b/dev-assets/doodads/gems/yellow-2.png new file mode 100644 index 0000000..f602927 Binary files /dev/null and b/dev-assets/doodads/gems/yellow-2.png differ diff --git a/dev-assets/doodads/gems/yellow-3.png b/dev-assets/doodads/gems/yellow-3.png new file mode 100644 index 0000000..fb9ac0f Binary files /dev/null and b/dev-assets/doodads/gems/yellow-3.png differ diff --git a/dev-assets/doodads/gems/yellow-4.png b/dev-assets/doodads/gems/yellow-4.png new file mode 100644 index 0000000..904c7c9 Binary files /dev/null and b/dev-assets/doodads/gems/yellow-4.png differ diff --git a/dev-assets/doodads/snake/Makefile b/dev-assets/doodads/snake/Makefile new file mode 100644 index 0000000..6fa8016 --- /dev/null +++ b/dev-assets/doodads/snake/Makefile @@ -0,0 +1,15 @@ +ALL: build + +.PHONY: build +build: + doodad convert -t "Snake" left-1.png left-2.png left-3.png right-1.png right-2.png right-3.png \ + attack-left-1.png attack-left-2.png attack-left-3.png attack-right-1.png attack-right-2.png \ + attack-right-3.png snake.doodad + doodad install-script snake.js snake.doodad + + # Tag the category for these doodads + for i in *.doodad; do\ + doodad edit-doodad --tag "category=creatures" $${i};\ + done + + cp *.doodad ../../../assets/doodads/ \ No newline at end of file diff --git a/dev-assets/doodads/snake/attack-left-1.png b/dev-assets/doodads/snake/attack-left-1.png new file mode 100644 index 0000000..d7e3cdf Binary files /dev/null and b/dev-assets/doodads/snake/attack-left-1.png differ diff --git a/dev-assets/doodads/snake/attack-left-2.png b/dev-assets/doodads/snake/attack-left-2.png new file mode 100644 index 0000000..8fec20b Binary files /dev/null and b/dev-assets/doodads/snake/attack-left-2.png differ diff --git a/dev-assets/doodads/snake/attack-left-3.png b/dev-assets/doodads/snake/attack-left-3.png new file mode 100644 index 0000000..00948cd Binary files /dev/null and b/dev-assets/doodads/snake/attack-left-3.png differ diff --git a/dev-assets/doodads/snake/attack-right-1.png b/dev-assets/doodads/snake/attack-right-1.png new file mode 100644 index 0000000..06f935e Binary files /dev/null and b/dev-assets/doodads/snake/attack-right-1.png differ diff --git a/dev-assets/doodads/snake/attack-right-2.png b/dev-assets/doodads/snake/attack-right-2.png new file mode 100644 index 0000000..6bbef9c Binary files /dev/null and b/dev-assets/doodads/snake/attack-right-2.png differ diff --git a/dev-assets/doodads/snake/attack-right-3.png b/dev-assets/doodads/snake/attack-right-3.png new file mode 100644 index 0000000..e71d3cb Binary files /dev/null and b/dev-assets/doodads/snake/attack-right-3.png differ diff --git a/dev-assets/doodads/snake/left-1.png b/dev-assets/doodads/snake/left-1.png new file mode 100644 index 0000000..9c76386 Binary files /dev/null and b/dev-assets/doodads/snake/left-1.png differ diff --git a/dev-assets/doodads/snake/left-2.png b/dev-assets/doodads/snake/left-2.png new file mode 100644 index 0000000..f86887a Binary files /dev/null and b/dev-assets/doodads/snake/left-2.png differ diff --git a/dev-assets/doodads/snake/left-3.png b/dev-assets/doodads/snake/left-3.png new file mode 100644 index 0000000..9c8eedb Binary files /dev/null and b/dev-assets/doodads/snake/left-3.png differ diff --git a/dev-assets/doodads/snake/right-1.png b/dev-assets/doodads/snake/right-1.png new file mode 100644 index 0000000..571b070 Binary files /dev/null and b/dev-assets/doodads/snake/right-1.png differ diff --git a/dev-assets/doodads/snake/right-2.png b/dev-assets/doodads/snake/right-2.png new file mode 100644 index 0000000..37828ce Binary files /dev/null and b/dev-assets/doodads/snake/right-2.png differ diff --git a/dev-assets/doodads/snake/right-3.png b/dev-assets/doodads/snake/right-3.png new file mode 100644 index 0000000..6b7b90c Binary files /dev/null and b/dev-assets/doodads/snake/right-3.png differ diff --git a/dev-assets/doodads/snake/snake.js b/dev-assets/doodads/snake/snake.js new file mode 100644 index 0000000..e8b2aaf --- /dev/null +++ b/dev-assets/doodads/snake/snake.js @@ -0,0 +1,135 @@ +// Snake + +/* +A.I. Behaviors: + +- Always turns to face the nearest player character +- Jumps up when the player tries to jump over them, + aiming to attack the player. +*/ + +let direction = "left", + jumpSpeed = 12, + watchRadius = 300, // player nearby distance for snake to jump + jumpCooldownStart = time.Now(), + size = Self.Size(); + +const states = { + idle: 0, + attacking: 1, +}; +let state = states.idle; + +function main() { + Self.SetMobile(true); + Self.SetGravity(true); + Self.SetHitbox(20, 0, 28, 58); + Self.AddAnimation("idle-left", 100, ["left-1", "left-2", "left-3", "left-2"]); + Self.AddAnimation("idle-right", 100, ["right-1", "right-2", "right-3", "right-2"]); + Self.AddAnimation("attack-left", 100, ["attack-left-1", "attack-left-2", "attack-left-3"]) + Self.AddAnimation("attack-right", 100, ["attack-right-1", "attack-right-2", "attack-right-3"]) + + // Player Character controls? + if (Self.IsPlayer()) { + return player(); + } + + Events.OnCollide((e) => { + // The snake is deadly to the touch. + if (e.Settled && e.Actor.IsPlayer() && e.InHitbox) { + // Friendly to fellow snakes. + if (e.Actor.Doodad().Filename.indexOf("snake") > -1) { + return; + } + + FailLevel("Watch out for snakes!"); + return; + } + }); + + setInterval(() => { + // Find the player. + let player = Actors.FindPlayer(), + playerPoint = player.Position(), + point = Self.Position(), + delta = 0, + nearby = false; + + // Face the player. + if (playerPoint.X < point.X + (size.W / 2)) { + direction = "left"; + delta = Math.abs(playerPoint.X - (point.X + (size.W/2))); + } + else if (playerPoint.X > point.X + (size.W / 2)) { + direction = "right"; + delta = Math.abs(playerPoint.X - (point.X + (size.W/2))); + } + + if (delta < watchRadius) { + console.log("Player is nearby snake! %d", delta); + nearby = true; + } + + // If we are idle and the player is jumping nearby... + if (state == states.idle && nearby && Self.Grounded()) { + if (playerPoint.Y - point.Y+(size.H/2) < 20) { + console.warn("Player is jumping near us!") + + // Enter attack state. + if (time.Since(jumpCooldownStart) > 500 * time.Millisecond) { + state = states.attacking; + Self.SetVelocity(Vector(0, -jumpSpeed)); + Self.StopAnimation(); + Self.PlayAnimation("attack-"+direction, null); + return; + } + } + } + + // If we are attacking and gravity has claimed us back. + if (state === states.attacking && Self.Grounded()) { + console.log("Landed again after jump!"); + state = states.idle; + jumpCooldownStart = time.Now(); + Self.StopAnimation(); + } + + // Ensure that the animations are always rolling. + if (state === states.idle && !Self.IsAnimating()) { + Self.PlayAnimation("idle-"+direction, null); + } + }, 100); +} + +// If under control of the player character. +function player() { + let jumping = false; + + Events.OnKeypress((ev) => { + Vx = 0; + Vy = 0; + + if (ev.Right) { + direction = "right"; + } else if (ev.Left) { + direction = "left"; + } + + // Jump! + if (ev.Up && !jumping) { + Self.StopAnimation(); + Self.PlayAnimation("attack-"+direction, null); + jumping = true; + return; + } + + if (jumping && Self.Grounded()) { + Self.StopAnimation(); + jumping = false; + } + + if (!jumping && !Self.IsAnimating()) { + Self.PlayAnimation("idle-"+direction, null); + } + }); +} diff --git a/pkg/balance/cheats.go b/pkg/balance/cheats.go index bdfa660..59f9d2d 100644 --- a/pkg/balance/cheats.go +++ b/pkg/balance/cheats.go @@ -24,6 +24,7 @@ var ( CheatNoclip = "ghost mode" CheatShowAllActors = "show all actors" CheatGiveKeys = "give all keys" + CheatGiveGems = "give all gems" CheatDropItems = "drop all items" CheatPlayAsBird = "fly like a bird" CheatGodMode = "god mode" @@ -39,7 +40,7 @@ var ( // Actor replacement cheats var CheatActors = map[string]string{ "pinocchio": "boy", - "the cell": "azu-blue", + "the cell": "azu-blu", "super azulian": "azu-red", "hyper azulian": "azu-white", "fly like a bird": "bird-red", diff --git a/pkg/balance/theme.go b/pkg/balance/theme.go index c8f4384..3c9e280 100644 --- a/pkg/balance/theme.go +++ b/pkg/balance/theme.go @@ -1,6 +1,7 @@ package balance import ( + magicform "git.kirsle.net/apps/doodle/pkg/uix/magic-form" "git.kirsle.net/go/render" "git.kirsle.net/go/ui" "git.kirsle.net/go/ui/style" @@ -214,6 +215,61 @@ var ( ButtonLightRed = style.DefaultButton DefaultCrosshairColor = render.RGBA(0, 153, 255, 255) + + // Default built-in wallpapers. + Wallpapers = []magicform.Option{ + { + Label: "Notebook", + Value: "notebook.png", + }, + { + Label: "Legal Pad", + Value: "legal.png", + }, + { + Label: "Graph paper", + Value: "graph.png", + }, + { + Label: "Dotted paper", + Value: "dots.png", + }, + { + Label: "Blueprint", + Value: "blueprint.png", + }, + { + Label: "Red parchment", + Value: "red-parchment.png", + }, + { + Label: "Green parchment", + Value: "green-parchment.png", + }, + { + Label: "Blue parchment", + Value: "blue-parchment.png", + }, + { + Label: "Yellow parchment", + Value: "yellow-parchment.png", + }, + { + Label: "White parchment", + Value: "white-parchment.png", + }, + { + Label: "Pure white", + Value: "white.png", + }, + { + Separator: true, + }, + { + Label: "Custom wallpaper...", + Value: CustomWallpaperFilename, + }, + } ) // Customize the various button styles. diff --git a/pkg/cheats.go b/pkg/cheats.go index 6578ee8..b133107 100644 --- a/pkg/cheats.go +++ b/pkg/cheats.go @@ -121,6 +121,18 @@ func (c Command) cheatCommand(d *Doodle) bool { d.FlashError("Use this cheat in Play Mode to get all colored keys.") } + case balance.CheatGiveGems: + if isPlay { + playScene.SetCheated() + playScene.Player.AddItem("gem-red.doodad", 1) + playScene.Player.AddItem("gem-green.doodad", 1) + playScene.Player.AddItem("gem-blue.doodad", 1) + playScene.Player.AddItem("gem-yellow.doodad", 1) + d.Flash("Given all gemstones to the player character.") + } else { + d.FlashError("Use this cheat in Play Mode to get all gemstones.") + } + case balance.CheatDropItems: if isPlay { playScene.SetCheated() diff --git a/pkg/level/chunker_zipfile.go b/pkg/level/chunker_zipfile.go index 47764d3..7eff613 100644 --- a/pkg/level/chunker_zipfile.go +++ b/pkg/level/chunker_zipfile.go @@ -76,6 +76,12 @@ func (c *Chunker) MigrateZipfile(zf *zip.Writer) error { continue } + // Verify that this chunk file in the old ZIP was not empty. + if chunk, err := ChunkFromZipfile(c.Zipfile, c.Layer, point); err == nil && chunk.Len() == 0 { + log.Debug("Skip chunk %s (old zipfile chunk was empty)", coord) + continue + } + log.Info("Copy existing chunk %s", file.Name) if err := zf.Copy(file); err != nil { return err @@ -94,6 +100,10 @@ func (c *Chunker) MigrateZipfile(zf *zip.Writer) error { // Flush in-memory chunks out to zipfile. for coord, chunk := range c.Chunks { + if _, ok := erasedChunks[coord]; ok { + continue + } + filename := fmt.Sprintf("chunks/%d/%s.json", c.Layer, coord.String()) log.Info("Flush in-memory chunks to %s", filename) chunk.ToZipfile(zf, filename) diff --git a/pkg/scripting/pubsub.go b/pkg/scripting/pubsub.go index e0fa76f..7a9974c 100644 --- a/pkg/scripting/pubsub.go +++ b/pkg/scripting/pubsub.go @@ -1,6 +1,7 @@ package scripting import ( + "git.kirsle.net/apps/doodle/lib/debugging" "git.kirsle.net/apps/doodle/pkg/log" "github.com/dop251/goja" ) @@ -28,6 +29,7 @@ func RegisterPublishHooks(s *Supervisor, vm *VM) { if err := recover(); err != nil { // TODO EXCEPTIONS log.Error("RegisterPublishHooks(%s): %s", vm.Name, err) + debugging.PrintCallers() } }() @@ -72,6 +74,7 @@ func RegisterPublishHooks(s *Supervisor, vm *VM) { }, "Publish": func(name string, v ...goja.Value) { + vm.muPublish.Lock() for _, channel := range vm.Outbound { channel <- Message{ Name: name, @@ -79,6 +82,7 @@ func RegisterPublishHooks(s *Supervisor, vm *VM) { Args: v, } } + vm.muPublish.Unlock() }, "Broadcast": func(name string, v ...goja.Value) { diff --git a/pkg/scripting/vm.go b/pkg/scripting/vm.go index cec6df0..f902c5c 100644 --- a/pkg/scripting/vm.go +++ b/pkg/scripting/vm.go @@ -28,6 +28,7 @@ type VM struct { stop chan bool subscribe map[string][]goja.Value // Subscribed message handlers by name. muSubscribe sync.RWMutex + muPublish sync.Mutex // serialize PubSub publishes vm *goja.Runtime @@ -44,7 +45,7 @@ func NewVM(name string) *VM { timers: map[int]*Timer{}, // Pub/sub structs. - Inbound: make(chan Message), + Inbound: make(chan Message, 100), Outbound: []chan Message{}, stop: make(chan bool, 1), subscribe: map[string][]goja.Value{}, diff --git a/pkg/windows/add_edit_level.go b/pkg/windows/add_edit_level.go index 2f34954..1650013 100644 --- a/pkg/windows/add_edit_level.go +++ b/pkg/windows/add_edit_level.go @@ -169,39 +169,7 @@ func (config AddEditLevel) setupLevelFrame(tf *ui.TabFrame) { Label: "Wallpaper:", Font: balance.UIFont, SelectValue: selectedWallpaper, - Options: []magicform.Option{ - { - Label: "Notebook", - Value: "notebook.png", - }, - { - Label: "Legal Pad", - Value: "legal.png", - }, - { - Label: "Graph paper", - Value: "graph.png", - }, - { - Label: "Dotted paper", - Value: "dots.png", - }, - { - Label: "Blueprint", - Value: "blueprint.png", - }, - { - Label: "Pure white", - Value: "white.png", - }, - { - Separator: true, - }, - { - Label: "Custom wallpaper...", - Value: balance.CustomWallpaperFilename, - }, - }, + Options: balance.Wallpapers, OnSelect: func(v interface{}) { if filename, ok := v.(string); ok { // Picking the Custom option?