doodads/warp-door/warp-door.js

170 lines
4.9 KiB
JavaScript

/*
Warp Doors
Link it to another Warp Door and the player can travel between them.
Doors without links will behave as 'locked' doors and cannot be opened.
Options: "locked (exit only)" bool will make the door behave as locked
even if linked - it can be an exit-only door that the player can come
out of but can not open.
*/
const color = Self.GetTag("color"),
isStateDoor = color === 'blue' || color === 'orange',
isLocked = Self.GetOption("locked (exit only)") === true;
// State in case we're a blue warp door.
let state = color === 'blue',
animating = false,
collide = false;
function main() {
// Declare animations and sprite names.
let animSpeed = 100;
let spriteDefault, spriteDisabled; // the latter for state doors.
if (color === 'blue') {
Self.AddAnimation("open", animSpeed, ["blue-2", "blue-3", "blue-4"]);
Self.AddAnimation("close", animSpeed, ["blue-4", "blue-3", "blue-2", "blue-1"]);
spriteDefault = "blue-1";
spriteDisabled = "blue-off";
} else if (color === 'orange') {
Self.AddAnimation("open", animSpeed, ["orange-2", "orange-3", "orange-4"]);
Self.AddAnimation("close", animSpeed, ["orange-4", "orange-3", "orange-2", "orange-1"]);
spriteDefault = "orange-1";
spriteDisabled = "orange-off";
} else if (color === 'invisible') {
// Invisible Warp Door region.
Self.Hide();
Self.AddAnimation("open", animSpeed, [0, 0]);
Self.AddAnimation("close", animSpeed, [0, 0]);
} else {
Self.AddAnimation("open", animSpeed, ["door-2", "door-3", "door-4"]);
Self.AddAnimation("close", animSpeed, ["door-4", "door-3", "door-2", "door-1"]);
spriteDefault = "door-1";
}
// Find our linked Warp Door.
let linkedDoor = null;
for (let link of Self.GetLinks()) {
if (link.Title.indexOf("Warp Door") > -1) {
linkedDoor = link;
break;
}
}
// Subscribe to the global state-change if we are a state door.
if (isStateDoor) {
Message.Subscribe("broadcast:state-change", (newState) => {
state = color === 'blue' ? !newState : newState;
// Activate or deactivate the door.
Self.ShowLayerNamed(state ? spriteDefault : spriteDisabled);
});
}
// For player groundedness work-around
let playerLastY = []; // last sampling of Y values
// The player Uses the door.
let flashedCooldown = false; // "Locked Door" flashed message.
Events.OnUse((e) => {
if (animating) {
return;
}
// Doors without linked exits are not usable.
if (linkedDoor === null || isLocked) {
if (!flashedCooldown) {
Flash("This door is locked.");
flashedCooldown = true;
setTimeout(function() {
flashedCooldown = false;
}, 1000);
}
return;
}
// Only players can use doors for now.
if (e.Actor.IsPlayer()) {
if (isStateDoor && !state) {
// The state door is inactive (dotted outline).
return;
}
// The player must be grounded or have no gravity to open the door.
if (!e.Actor.Grounded() && e.Actor.HasGravity()) {
// Work-around: if two Boxes are stacked atop each other the player can
// get stuck if he jumps on top. He may not be Grounded but isn't changing
// effective Y position and a warp door may work as a good way out.
let yValue = e.Actor.Position().Y;
// Collect a sampling of last few Y values. If the player Y position
// is constant the last handful of frames, treat them as if they're
// grounded (or else they can't activate the warp door).
playerLastY.unshift(yValue);
if (playerLastY.length < 6) {
return;
}
// We have enough history.
playerLastY.pop();
// Hasn't moved?
let isGrounded = true;
for (let i = 0; i < playerLastY.length; i++) {
if (yValue !== playerLastY[i]) {
isGrounded = false;
break;
}
}
if (!isGrounded) {
return;
}
// Player was effectively grounded! No change in Y position.
}
// Freeze the player.
e.Actor.Freeze()
// Play the open and close animation.
animating = true;
Sound.Play("door-opening.mp3");
Self.PlayAnimation("open", () => {
e.Actor.Hide()
Self.PlayAnimation("close", () => {
Self.ShowLayerNamed(isStateDoor && !state ? spriteDisabled : spriteDefault);
animating = false;
// Teleport the player to the linked door. Inform the target
// door of the arrival of the player so it doesn't trigger
// to send the player back here again on a loop.
if (linkedDoor !== null) {
Message.Publish("warp-door:incoming", e.Actor);
e.Actor.MoveTo(linkedDoor.Position());
}
});
});
}
});
// Respond to incoming warp events.
Message.Subscribe("warp-door:incoming", (player) => {
animating = true;
player.Unfreeze();
Self.PlayAnimation("open", () => {
player.Show();
Self.PlayAnimation("close", () => {
animating = false;
// If the receiving door was a State Door, fix its state.
if (isStateDoor) {
Self.ShowLayerNamed(state ? spriteDefault : spriteDisabled);
}
});
});
});
}