The script located at scripts/behaviours/door.mjs also deines a list
o character races that are allowed to ope doors.
For characters with one of the included race, the default behavior for doorways is to let anyone
automatically open them as long as they are unlocked, or if any NPC that owns a key to the door.
You may override that behaviour in your own door scripts, by overloading the canGoThrough method. If that method returns false, the character won't be allowed to move through the door unless it's already open.
Let's see how you may customize your NPCs so they won't open doors unless explicitely allowed to:
import {Door} from "./door.mjs";
class CustomDoor extends Door {
canGoThrough(character) {
if (!game.playerParty.containsCharacter(character) && character.hasVariable("can-open-custom-door"))
return false;
return super.canGoThrough(character);
}
}
export unction create(model) {
return new CustomDoor(model);
}
This script ensures that no NPC will try to open the door, while the player and their party member will use the default behavior.
Return true or false to override the default behavior and allow all characters to open the door.
When a character needs to open the door to go through it, the onUse method will be called on the doorway script.
While locks are commonly associated with doors, you can script other objects to include a lock
behaviour, by leveraging scripts/behaviours/locked.mjs.
Let's see how to use it create a locked storage box:
import {LockedComponented} from "./locked.mjs";
class LockedStorage {
constructor(model) {
this.model = model;
// To begin with, we instantiate a LockedComponent. Available options are:
// - lockpickLevel: determines how hard the lock is to pick
// - breakable: if true, the lock will break on critical failure
// - onSuccess, onFailure and onCriticalFailure: hooks called after a success, failure or critical failure
this.lockedComponent = new LockedComponent(this, {
lockpickLevel: 2,
breakable: true,
onSuccess: function() { game.appendToConsole("Lock picked"); },
onFailure: function() { game.appendToConsole("Lock not picked"); },
onCriticalFailure: function() { game.appendToConsole("Lock broken"); },
});
// Unless our game object is a door, we must override the `toggleLocked`
// method on the LockedComponent:
this.lockedComponent.toggleLocked = this.toggleLocked.bind(this);
}
toggleLocked() {
this.model.setVariable("locked", !this.locked);
}
isLocked() {
return this.model.hasVariable("locked") ? this.model.getVariable("locked") : true;
}
// We provide the `onUse` hook, and return `true` when the storage is locked, to
// prevent the default behaviour from running.
onUse() {
if (this.isLocked()) {
game.appendToConsole(i18n.t("messages.locker-locked"));
return true;
}
return false;
}
// We do the same thing with `onSteal`, as stealing can also be used to open a
// storage box
onUseSteal() {
return this.onUse();
}
// Lastly, we provide the `onUseLockpick` hook, and forward the call to
// the LockedComponent.
onUseLockpick(user) {
return this.lockedComponent.onUseLockpick(user);
}
}
Picking a lock will also grant experience to the player, according to the lockpickLevel value.
You can also customize the reward:
this.lockedComponent.xpReward = 42;
Traps can be implemented just like locks by using scripts/behaviours/trapped.mjs.
Let's see how to use it to create a trapped storage box:
import {TrappedComponented} from "./trapped.mjs";
import {explode} from "./explosion.mjs";
class TrappedStorage {
constructor(model) {
this.model = model;
// To begin with, we instantiate a TrappedComponent. Available options are:
// - trapLevel: determines how hard the trap is to disarm
// - onTriggered: determines what happens when the trap is triggered
// - onSuccess, onFailure and onCriticalFailure: hooks called after a success, failure or critical failure
this.trappedComponent = new TrappedComponent(this, {
trapLevel: 2,
onTriggered: this.onTriggered.bind(this),
onSuccess: function() { game.appendToConsole("Trap toggled"); },
onFailure: function() { game.appendToConsole("Trap not toggled"); },
onCriticalFailure: function() { game.appendToConsole("Trap triggered"); }
});
// We set `destructible` to true. This overrides the default behaviour, and
// means the object may be destroyed, in the blast of an explosion for instance.
this.destructible = true;
}
// Our onTriggered hook uses `explode` from `explosion.mjs` to trigger
// an explosion dealing 20 damage in a 1 case radius around the storage box.
onTriggered() {
const damage = 20;
const radius = 1;
const position = {
x: this.model.position.x,
y: this.model.position.y,
z: this.model.floor
};
explode(position, radius, damage);
}
// We provide the `onUse` hook, and return `true` when the storage is locked, to
// prevent the default behaviour from running.
onUse() {
if (!this.trappedComponent.disarmed) {
this.trappedComponent.trigger();
return true;
}
return false;
}
// We do the same thing with `onSteal`, as stealing can also be used to open a
// storage box
onUseSteal() {
return this.onUse();
}
// Lastly, we provide the `onUseLockpick` hook, and forward the call to
// the LockedComponent.
onUseExplosives(user) {
return this.trappedComponent.onUseExplosives(user);
}
}
Disarming a trap grants experience, according to the trapLevel value.
You can also customize the reward:
this.trappedComponent.xpReward = 42;