@ -1,43 +0,0 @@ |
|||||
// Azulian (Red)
|
|
||||
// DEPRECATED: they both share azulian.js now.
|
|
||||
|
|
||||
function main() { |
|
||||
var playerSpeed = 4; |
|
||||
var gravity = 4; |
|
||||
var Vx = Vy = 0; |
|
||||
|
|
||||
var direction = "right"; |
|
||||
|
|
||||
Self.SetHitbox(0, 0, 32, 32) |
|
||||
Self.SetMobile(true); |
|
||||
Self.SetInventory(true); |
|
||||
Self.SetGravity(true); |
|
||||
Self.AddAnimation("walk-left", 100, ["red-wl1", "red-wl2", "red-wl3", "red-wl4"]); |
|
||||
Self.AddAnimation("walk-right", 100, ["red-wr1", "red-wr2", "red-wr3", "red-wr4"]); |
|
||||
|
|
||||
// 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++; |
|
||||
|
|
||||
// TODO: Vector() requires floats, pain in the butt for JS,
|
|
||||
// the JS API should be friendlier and custom...
|
|
||||
var Vx = parseFloat(playerSpeed * (direction === "left" ? -1 : 1)); |
|
||||
Self.SetVelocity(Vector(Vx, 0.0)); |
|
||||
|
|
||||
if (!Self.IsAnimating()) { |
|
||||
Self.PlayAnimation("walk-" + direction, null); |
|
||||
} |
|
||||
}, 100); |
|
||||
} |
|
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1004 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1002 B |
After Width: | Height: | Size: 994 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 985 B |
After Width: | Height: | Size: 999 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 959 B |
Before Width: | Height: | Size: 989 B After Width: | Height: | Size: 989 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1022 B After Width: | Height: | Size: 1022 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,38 +1,62 @@ |
|||||
function main() { |
|
||||
var playerSpeed = 12; |
|
||||
var gravity = 4; |
|
||||
var Vx = Vy = 0; |
|
||||
|
const playerSpeed = 12; |
||||
|
|
||||
var animating = false; |
|
||||
var animStart = animEnd = 0; |
|
||||
var animFrame = animStart; |
|
||||
|
let Vx = Vy = 0, |
||||
|
walking = false, |
||||
|
direction = "right", |
||||
|
lastDirection = direction; |
||||
|
|
||||
|
function main() { |
||||
Self.SetMobile(true); |
Self.SetMobile(true); |
||||
Self.SetInventory(true); |
Self.SetInventory(true); |
||||
Self.SetGravity(true); |
Self.SetGravity(true); |
||||
Self.SetHitbox(0, 0, 32, 52); |
Self.SetHitbox(0, 0, 32, 52); |
||||
Self.AddAnimation("walk-left", 200, ["stand-left", "walk-left-1", "walk-left-2", "walk-left-3", "walk-left-2", "walk-left-1"]); |
Self.AddAnimation("walk-left", 200, ["stand-left", "walk-left-1", "walk-left-2", "walk-left-3", "walk-left-2", "walk-left-1"]); |
||||
Self.AddAnimation("walk-right", 200, ["stand-right", "walk-right-1", "walk-right-2", "walk-right-3", "walk-right-2", "walk-right-1"]); |
Self.AddAnimation("walk-right", 200, ["stand-right", "walk-right-1", "walk-right-2", "walk-right-3", "walk-right-2", "walk-right-1"]); |
||||
|
Self.AddAnimation("idle-left", 200, ["idle-left-1", "idle-left-2", "idle-left-3", "idle-left-2"]); |
||||
|
Self.AddAnimation("idle-right", 200, ["idle-right-1", "idle-right-2", "idle-right-3", "idle-right-2"]); |
||||
|
|
||||
Events.OnKeypress(function (ev) { |
|
||||
|
// If the player suddenly changes direction, reset the animation state to quickly switch over.
|
||||
|
let lastVelocity = Vector(0, 0); |
||||
|
|
||||
|
Events.OnKeypress((ev) => { |
||||
Vx = 0; |
Vx = 0; |
||||
Vy = 0; |
Vy = 0; |
||||
|
|
||||
if (ev.Right) { |
|
||||
if (!Self.IsAnimating()) { |
|
||||
Self.PlayAnimation("walk-right", null); |
|
||||
|
let curVelocity = Self.GetVelocity(); |
||||
|
if ((lastVelocity.X < 0 && curVelocity.X > 0) || |
||||
|
(lastVelocity.X > 0 && curVelocity.X < 0)) { |
||||
|
Self.StopAnimation(); |
||||
} |
} |
||||
|
lastVelocity = curVelocity; |
||||
|
lastDirection = direction; |
||||
|
|
||||
|
let wasWalking = walking; |
||||
|
if (ev.Right) { |
||||
|
direction = "right"; |
||||
Vx = playerSpeed; |
Vx = playerSpeed; |
||||
|
walking = true; |
||||
} else if (ev.Left) { |
} else if (ev.Left) { |
||||
if (!Self.IsAnimating()) { |
|
||||
Self.PlayAnimation("walk-left", null); |
|
||||
} |
|
||||
|
direction = "left"; |
||||
Vx = -playerSpeed; |
Vx = -playerSpeed; |
||||
|
walking = true; |
||||
} else { |
} else { |
||||
|
// Has stopped walking!
|
||||
|
walking = false; |
||||
|
stoppedWalking = true; |
||||
|
} |
||||
|
|
||||
|
// Should we stop animating? (changed state)
|
||||
|
if (direction !== lastDirection || wasWalking !== walking) { |
||||
Self.StopAnimation(); |
Self.StopAnimation(); |
||||
animating = false; |
|
||||
} |
} |
||||
|
|
||||
// Self.SetVelocity(Point(Vx, Vy));
|
|
||||
|
// And play what animation?
|
||||
|
if (!Self.IsAnimating()) { |
||||
|
if (walking) { |
||||
|
Self.PlayAnimation("walk-"+direction, null); |
||||
|
} else { |
||||
|
Self.PlayAnimation("idle-"+direction, null); |
||||
|
} |
||||
|
} |
||||
}) |
}) |
||||
} |
} |
||||
|
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
@ -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/ |
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,207 @@ |
|||||
|
// 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, |
||||
|
drop: 2, |
||||
|
falling: 3, |
||||
|
hit: 4, |
||||
|
rising: 5, |
||||
|
}; |
||||
|
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.drop; |
||||
|
} 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.drop; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
case states.drop: |
||||
|
// Begin the fall.
|
||||
|
Self.ShowLayerNamed("angry"); |
||||
|
Self.SetVelocity(Vector(0.0, dropSpeed)); |
||||
|
lastAltitude = -point.Y; |
||||
|
state = states.falling; |
||||
|
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"); |
||||
|
} |
||||
|
}); |
||||
|
} |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
@ -1,27 +0,0 @@ |
|||||
// DEPRECATED: old locked door script. Superceded by colored-door.js.
|
|
||||
function main() { |
|
||||
Self.AddAnimation("open", 0, [1]); |
|
||||
var unlocked = false; |
|
||||
var color = Self.GetTag("color"); |
|
||||
|
|
||||
Self.SetHitbox(16, 0, 32, 64); |
|
||||
|
|
||||
Events.OnCollide(function(e) { |
|
||||
if (unlocked) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (e.InHitbox) { |
|
||||
var data = e.Actor.GetData("key:" + color); |
|
||||
if (data === "") { |
|
||||
// Door is locked.
|
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (e.Settled) { |
|
||||
unlocked = true; |
|
||||
Self.PlayAnimation("open", null); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
@ -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/ |
After Width: | Height: | Size: 891 B |
After Width: | Height: | Size: 927 B |
After Width: | Height: | Size: 903 B |
After Width: | Height: | Size: 835 B |
@ -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); |
||||
|
} |
After Width: | Height: | Size: 785 B |
After Width: | Height: | Size: 807 B |
After Width: | Height: | Size: 864 B |
After Width: | Height: | Size: 835 B |
After Width: | Height: | Size: 711 B |
After Width: | Height: | Size: 702 B |
After Width: | Height: | Size: 712 B |
After Width: | Height: | Size: 724 B |
After Width: | Height: | Size: 798 B |
After Width: | Height: | Size: 958 B |
After Width: | Height: | Size: 1005 B |
After Width: | Height: | Size: 987 B |
After Width: | Height: | Size: 921 B |
After Width: | Height: | Size: 711 B |
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 938 B |
After Width: | Height: | Size: 978 B |
After Width: | Height: | Size: 956 B |
After Width: | Height: | Size: 752 B |
After Width: | Height: | Size: 811 B |
After Width: | Height: | Size: 805 B |
After Width: | Height: | Size: 790 B |
After Width: | Height: | Size: 817 B |
After Width: | Height: | Size: 837 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,100 @@ |
|||||
|
// 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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 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; |
||||
|
} |
||||
|
|
||||
|
// Can't if any of our linked totems aren't activated.
|
||||
|
try { |
||||
|
for (let totemId of Object.keys(totems)) { |
||||
|
if (totems[totemId] === false) { |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} catch(e) { |
||||
|
console.error("Caught: %s", e); |
||||
|
} |
||||
|
|
||||
|
// Can't if we aren't powered.
|
||||
|
if (activated === false) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Emit power!
|
||||
|
Message.Publish("power", true); |
||||
|
} |
After Width: | Height: | Size: 971 B |
After Width: | Height: | Size: 921 B |
After Width: | Height: | Size: 898 B |
After Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 957 B After Width: | Height: | Size: 956 B |