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); + } + }); +}