Compare commits

..

No commits in common. "142ed7d4c5da5c2ebdf60c49f2b4f382439da88e" and "0ed5d6ae0c0e0e6b1d01d9bdd96c2dc11b201591" have entirely different histories.

71 changed files with 16 additions and 631 deletions

View File

@ -2,11 +2,9 @@
const color = Self.GetTag("color");
var playerSpeed = color === 'blue' ? 2 : 4,
swimSpeed = playerSpeed * 0.4,
aggroX = 250, // X/Y distance sensitivity from player
aggroY = color === 'blue' ? 100 : 200,
jumpSpeed = color === 'blue' ? 14 : 18,
swimJumpSpeed = jumpSpeed * 0.4,
animating = false,
direction = "right",
lastDirection = "right";
@ -16,9 +14,7 @@ if (color === 'white') {
aggroX = 1000;
aggroY = 400;
playerSpeed = 8;
swimSpeed = playerSpeed * 0.4;
jumpSpeed = 20;
swimJumpSpeed = jumpSpeed * 0.4;
}
function setupAnimations(color) {
@ -34,9 +30,6 @@ function setupAnimations(color) {
function main() {
playerSpeed = color === 'blue' ? 2 : 4;
let swimJumpCooldownTick = 0, // minimum Game Tick before we can jump while swimming
swimJumpCooldown = 10; // CONFIG: delta of ticks between jumps while swimming
Self.SetMobile(true);
Self.SetGravity(true);
Self.SetInventory(true);
@ -102,25 +95,8 @@ function main() {
sampleTick++;
}
// Handle being underwater.
let canJump = Self.Grounded();
if (Self.IsWet()) {
let tick = GetTick();
if (tick > swimJumpCooldownTick) {
canJump = true;
swimJumpCooldownTick = tick + swimJumpCooldown;
}
}
// How speedy would our movement and jump be?
let xspeed = playerSpeed, yspeed = jumpSpeed;
if (Self.IsWet()) {
xspeed = swimSpeed;
yspeed = swimJumpSpeed;
}
let Vx = parseFloat(xspeed * (direction === "left" ? -1 : 1)),
Vy = jump && canJump ? parseFloat(-yspeed) : Self.GetVelocity().Y;
let Vx = parseFloat(playerSpeed * (direction === "left" ? -1 : 1)),
Vy = jump && Self.Grounded() ? parseFloat(-jumpSpeed) : Self.GetVelocity().Y;
Self.SetVelocity(Vector(Vx, Vy));
// If we changed directions, stop animating now so we can

View File

@ -19,14 +19,7 @@ function main() {
let overlap = e.Overlap;
if (overlap.Y === 0 && !(overlap.X === 0 && overlap.W < 5) && !(overlap.X === size)) {
// 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 - 2,
))
e.Actor.SetGrounded(true);
// Standing on top, ignore.
return false;
} else if (overlap.Y === size) {
// From the bottom, boop it up.

View File

@ -5,8 +5,6 @@ build:
doodad convert -t "Boy" stand-right.png stand-left.png \
walk-right-1.png walk-right-2.png walk-right-3.png \
walk-left-1.png walk-left-2.png walk-left-3.png \
idle-right-1.png idle-right-2.png idle-right-3.png \
idle-left-1.png idle-left-2.png idle-left-3.png \
boy.doodad
doodad install-script boy.js boy.doodad

View File

@ -1,9 +1,8 @@
const playerSpeed = 12;
let Vx = Vy = 0,
walking = false,
direction = "right",
lastDirection = direction;
animating = false,
animStart = animEnd = 0;
function main() {
Self.SetMobile(true);
@ -12,8 +11,6 @@ function main() {
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-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"]);
// If the player suddenly changes direction, reset the animation state to quickly switch over.
let lastVelocity = Vector(0, 0);
@ -28,35 +25,20 @@ function main() {
Self.StopAnimation();
}
lastVelocity = curVelocity;
lastDirection = direction;
let wasWalking = walking;
if (ev.Right) {
direction = "right";
Vx = playerSpeed;
walking = true;
} else if (ev.Left) {
direction = "left";
Vx = -playerSpeed;
walking = true;
} else {
// Has stopped walking!
walking = false;
stoppedWalking = true;
}
// Should we stop animating? (changed state)
if (direction !== lastDirection || wasWalking !== walking) {
Self.StopAnimation();
}
// And play what animation?
if (!Self.IsAnimating()) {
if (walking) {
Self.PlayAnimation("walk-"+direction, null);
} else {
Self.PlayAnimation("idle-"+direction, null);
if (!Self.IsAnimating()) {
Self.PlayAnimation("walk-right", null);
}
Vx = playerSpeed;
} else if (ev.Left) {
if (!Self.IsAnimating()) {
Self.PlayAnimation("walk-left", null);
}
Vx = -playerSpeed;
} else {
Self.StopAnimation();
animating = false;
}
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -34,10 +34,6 @@ doors() {
cd doors/
make
cd ..
cd gems/
make
cd ..
}
trapdoors() {
@ -88,16 +84,6 @@ warpdoor() {
cd ..
}
creatures() {
cd snake/
make
cd ..
cd crusher/
make
cd ..
}
boy
buttons
switches
@ -108,7 +94,6 @@ 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

View File

@ -1,14 +0,0 @@
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/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,207 +0,0 @@
// 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");
}
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,58 +0,0 @@
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/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 B

View File

@ -1,24 +0,0 @@
// 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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 805 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,100 +0,0 @@
// 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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 879 B

View File

@ -1,15 +0,0 @@
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/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,131 +0,0 @@
// 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) {
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) {
// 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()) {
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);
}
});
}