// Aperture.js
(function () {
/*global adventurejs A*/
"use strict";
/**
* @augments adventurejs.Thing
* @class adventurejs.Aperture
* @ajsconstruct MyGame.createAsset({ "class":"Aperture", "name":"foo", [...] })
* @ajsconstructedby adventurejs.Game#createAsset
* @ajsnavheading DoorExitClasses
* @param {String} game_name The name of the top level game object.
* @param {String} name A name for the object, to be serialized and used as ID.
* @summary Base class for doors, windows, and other passageways.
* @tutorial CreateExit
* @classdesc
* <p>
* <strong>Aperture</strong> is the base class for all
* {@link adventurejs.Door|Doors},
* {@link adventurejs.Window|Windows},
* and other types of passageway. An Aperture is
* always associated with an {@link adventurejs.Exit|Exit}.
* Because Exits have no physical properties of their own,
* Apertures provide a way to add those missing
* manipulatable physical properties.
* Each Aperture only exists in one {@link adventurejs.Room|Room}
* and is one-way. To make a two-way passage, create
* two Apertures, one in each Room, and set each Aperture's
* <a href="#linked_asset">linked_asset</a>
* property to its mate. This allows them to share state,
* i.e., unlocking one side also unlocks the other.
* This example shows two Rooms with a two-way passage between them.
* </p>
* <h3 class="example">Example:</h3>
* <pre class="display"><code class="language-javascript">MyGame.createAsset({
* "class":"Exit",
* "direction":"north",
* "place":{in:"South Room"},
* "destination":"North Room",
* "aperture":"icy door"
* });
* MyGame.createAsset({
* "class":"Door",
* "name":"icy door",
* "place":{in:"South Room"},
* "direction":"north",
* "linked_asset":"warm door"
* });
* MyGame.createAsset({
* "class":"Exit",
* "direction":"south",
* "place":{in:"North Room"},
* "destination":"South Room",
* "aperture":"warm door"
* });
* MyGame.createAsset({
* "class":"Door",
* "name":"warm door",
* "place":{in:"North Room"},
* "direction":"south",
* "linked_asset":"icy door"
* });
* </code></pre>
*
**/
class Aperture extends adventurejs.Thing {
constructor(name, game_name) {
super(name, game_name);
this.class = "Aperture";
this.is = new adventurejs.Aperture_Is("is", this.game_name, this.id).set({
parent_id: this.id,
});
this.unsetDOV("take");
this.setDOVs([
"open",
"close" /*,'lock','unlock','seal','unseal','pick'*/,
]);
/**
* Set a direction for this asset. Chiefly used with Aperture class to set
* directions apertures. Takes a direction string, ie "north", "northeast", "down".
* @var {String} adventurejs.Tangible#direction
* @default ""
* @todo Use lookup table for this? In a GUI this would be a pull-down menu.
*/
this.direction = ""; // for use with apertures
/**
* Set an ID representing an Exit. Used chiefly for Aperture class to set an
* exit corresponding to an aperture.
* @var {String} adventurejs.Tangible#exit
* @default ""
*/
this.exit = ""; // for use with apertures
this.location_required = true;
// ex: this.direction = "north"; // an aperture will be associated with an exit direction
this.can.auto_unlock = true;
this.can.auto_unseal = true;
this.can.auto_open = true;
/**
* ID of an Asset that is the other side of this. Used
* for connecting doors.
* @var {String} adventurejs.Aperture#linked_asset
* @default ""
*/
//this.linked_asset = "";
}
initialize(game) {
super.initialize(game);
var words = [];
words.push(this.direction);
var directionConstructor =
this.direction + " " + this.constructor.name.toLowerCase();
words.push(directionConstructor);
words.push(A.serialize(directionConstructor));
var directionName = this.direction + " " + this.name.toLowerCase();
words.push(directionName);
words.push(A.serialize(directionName));
while (words.length > 0) {
if (!this.game.world_lookup[words[0]]) {
this.game.world_lookup[words[0]] = {};
this.game.world_lookup[words[0]].IDs = [];
this.game.world_lookup[words[0]].type = "direction";
}
if (-1 === this.game.world_lookup[words[0]].IDs.indexOf(this.id)) {
this.game.world_lookup[words[0]].IDs.push(this.id);
}
words.shift();
}
return this;
}
validate(game) {
super.validate(game);
var msg = "";
if (this.linked_asset) {
var linked_asset = this.game.getAsset(this.linked_asset);
if (!(linked_asset instanceof adventurejs.Aperture)) {
msg = `${this.constructor.name} ${this.name}'s linked_asset, ${this.linked_asset}, is not an Aperture. `;
this.game.log("error", "critical", msg, "Tangible");
return false;
}
// we reset linked_asset, known, and seen because these
// also set the properties of linked assets, and
// though this would have been called during construction,
// the linked asset might not yet have been constructed
// to receive the linkage
//this.linked_asset = this.linked_asset;
// this.is.known = this.is.known;
// this.is.seen = this.is.seen;
// this.is.closed = this.is.closed;
// this.is.locked = this.is.locked;
// this.is.sealed = this.is.sealed;
}
// this check was already performed in tangible.validate
// but it's optional in tangible whereas aperture must have a place
if (!this.hasPlace()) {
msg = `${this.constructor.name} ${this.name}'s place isn't set. `;
this.game.log("error", "critical", msg, "Tangible");
return false;
}
// check the aperture's direction
// aperture must have a direction
if (!this.direction) {
msg = `${this.constructor.name} ${this.name} has no direction. `;
this.game.log("error", "critical", msg, "Tangible");
return false;
}
// this.place will have been validated in tangible.validate
var exitObject = this.game.getAsset(
this.place[Object.keys(this.place)[0]] + "_" + this.direction
);
//if( "undefined" === typeof exitObject )
if (Object(exitObject) !== exitObject) {
msg = `${this.constructor.name} ${this.name}'s place + direction do not match an exit. `;
this.game.log("error", "critical", msg, "Tangible");
return false;
}
if (!(exitObject instanceof adventurejs.Exit)) {
msg = `${this.constructor.name} ${this.name}'s place + direction matches ${exitObject.name} which is a ${exitObject.constructor.name} rather than an Exit. `;
this.game.log("error", "critical", msg, "Tangible");
return false;
}
// BOOM!
// looks like we have a valid exit
this.exit = exitObject.id;
// make sure our exit refers back to this
// depending on order validated, the exit might already have it
//this.exit.aperture = this.id;
//this.game.world[this.exit].aperture = this.id;
exitObject.aperture = this.id;
}
/*doc@this*/
get linked_asset() {
return this._linked_asset;
}
set linked_asset(value) {
var oldasset, newasset;
value = A.serialize(value);
if (this._linked_asset && this._linked_asset !== value) {
// we have to inform the old asset
oldasset = this.game.getAsset(this._linked_asset);
oldasset._linked_asset = "";
}
newasset = this.game.getAsset(value);
if (newasset) {
// inform the new asset
newasset._linked_asset = this.id;
}
this._linked_asset = value;
}
}
adventurejs.Aperture = Aperture;
})();