// Aspect.js
(function () {
/* global adventurejs A */
/**
* @ajspath adventurejs.Atom.Aspect
* @augments adventurejs.Atom
* @class adventurejs.Aspect
* @ajsnavheading BaseClasses
* @param {String} game_name Name of top level game instance that is scoped to window.
* @param {String} name Instance name.
* @summary Class that allows putting things in/on/under/behind/attached.
* @tutorial Tangibles_Aspects
* @classdesc
* <p>
* <strong>Aspect</strong> is a special class
* that creates spaces within any
* {@link adventurejs.Tangible|Tangible}
* {@link adventurejs.Asset|Asset},
* which can contain other Tangibles and/or
* {@link adventurejs.Substance|Substances},
* with the addition of a
* {@link adventurejs.Vessel|Vessel}.
* The five most commonly used aspects are behind,
* in, on, under, and attached, and a lot of default logic is
* predicated on using one of these. However it is possible to
* create aspects at any preposition. Just note that it might
* lead to unexpected results and require custom code.
* </p>
* <pre class="display"><code class="language-javascript">this.aspects.behind = {};
* </code></pre>
* <p>
* Here is an example of how to set the properties of a
* Aspect of an existing class using createAsset.
* If you use a preposition that hasn't been defined for the class
* you're using, a new Aspect will be constructed
* automatically during construction.
* </p>
* <pre class="display"><code class="language-javascript">MyGame.createAsset({
* class: "Desk",
* name: "desk",
* place: { in: "Office" },
* behind: {
* list_contents_in_room: false,
* list_contents_in_examine: true,
* contents_limits: {
* height: 1,
* width: 6,
* depth: 4,
* },
* }
* });
* </code></pre>
* <p>
* To define a new class with an Aspect, use the Aspect
* constructor within the class constructor. Here is a very simple
* example of a new class with a behind Aspect.
* </p>
* <pre class="display"><code class="language-javascript">class NewClass {
* constructor( name, game_name ) {
* super( name, game_name );
* this.aspects.newaspect = new adventurejs.Aspect( "behind", this.game_name, this.id )
* .set({
* // optional params
* });
* }
* }
* adventurejs.NewClass = NewClass;
* };
* </code></pre>
**/
class Aspect extends adventurejs.Atom {
constructor(name, game_name, context_id) {
super(name, game_name, context_id);
this.class = "Aspect";
this.preposition = name;
this.id = `${context_id}|${name}|aspect`;
this.TYPES = {
in: "container",
on: "surface",
under: "relative",
behind: "relative",
over: "relative",
attached: "connection",
};
this.type = this.TYPES[this.id];
/**
* When the player enters a room, items in the room are
* listed in the room's description, and the contents of
* those items may or may not be listed as well, depending
* on this setting. For example, consider a bed with pillows
* on it: "There is a bed here. On the bed you see some pillows."
* Now consider the monster under the bed: "There is a bed here.
* Under the bed you see a monster." To prevent the monster from
* being revealed in the room description, simply set
* bed.aspects.under.list_contents_in_room to false.
* @var {boolean} adventurejs.Aspect#list_contents_in_room
* @default true
*/
this.list_contents_in_room = true;
/**
* When an asset is examined, authors may control whether
* aspect contents are listed in the description. Consider
* a pocket protector: examining it might list the pens and
* pencils in it. On the other hand, consider a desk with
* drawers: if the drawers are part of the desk's general
* description, the author might not want them listed.
* @var {boolean} adventurejs.Aspect#list_contents_in_examine
* @default true
*/
this.list_contents_in_examine = true;
/**
* This property allows authors some control over when asset
* contents become known. Generally speaking, assets are unknown
* until they have been "seen", which usually occurs when the
* player enters a room containing the asset.
* When an asset becomes known, it may or may not be desirable
* for its contents to become known. For example, items on a
* desk should probably be known, whereas items under a bed
* might better remain unknown until the player tries "look under bed".
* Or consider a knapsack: if it's closed, the player might see
* the knapsack, but shouldn't know what's inside of it until they open it.
* @var {boolean} adventurejs.Aspect#know_contents_with_parent
* @default true
*/
this.know_contents_with_parent = true;
/**
* This property allows authors some control over when asset
* contents become seen. Generally speaking, assets are unseen
* until they have been "seen", which usually occurs when the
* player enters a room containing the asset.
* When an asset becomes seen, it may or may not be desirable
* for its contents to become seen. For example, items on a
* desk should probably be seen, whereas items under a bed
* might better remain unseen until the player tried "look under bed".
* Or consider a knapsack: if it's closed, the player might see
* the knapsack, but shouldn't see what's inside of it until they open it.
* Known and seen are distinct properties because it's possible
* to know about an asset without seeing it.
* @var {boolean} adventurejs.Aspect#see_contents_with_parent
* @default true
*/
this.see_contents_with_parent = true;
/**
* A list of asset ids that are contained in this aspect.
* @var {Array} adventurejs.Aspect#contents
* @default []
*/
this.contents = []; // defined
/**
* Set maximums for contents of dimensions, count, and volume.
* -1 means infinite.
* @var {Array} adventurejs.Aspect#contents_limits
* @default { height: -1, width: -1, depth: -1, count: -1, weight: -1, }
*/
this.contents_limits = {
height: -1,
width: -1,
depth: -1,
count: -1,
weight: -1,
};
/**
* Objects that are taller than player height may be scaled
* (ie climbed, though the action is not exclusive to climb verb).
* This var determines how far the player climbs per turn. For example,
* a tree with height of 5 and scale_increment of 1
* will take the player 5 turns of climbing to reach the top, whereas
* a scale_increment of 5 will let players climb the tree in 1 turn.
* The default scale_increment of -1 will let players climb an object
* in 1 turn regardless of height.
* @var {Array} adventurejs.Aspect#scale_increment
* @default -1
*/
this.scale_increment = -1;
/**
* A list of asset ids that are allowed to be contained in this aspect.
* Use this to limit what things can be put in other things. For
* example, consider a scabbard which only allows one particular sword
* to be put in it.
* @var {Array} adventurejs.Aspect#with_assets
* @default []
*/
this.with_assets = []; // defined
/**
* A list of classes that are allowed to be contained in this aspect.
* Use this to limit what things can be put in other things. For
* example, consider a pocket protector, which only allows pens and
* pencils to be put in it.
* @var {Array} adventurejs.Aspect#with_classes
* @default []
*/
this.with_classes = []; // defined
/**
* Determine whether the player can add assets to this aspect's contents.
* @var {boolean} adventurejs.Aspect#player_can_add_assets_to_contents
* @default true
*/
this.player_can_add_assets_to_contents = true;
/**
* Determine whether the player can remove assets from this aspect's contents.
* @var {boolean} adventurejs.Aspect#player_can_remove_assets_from_contents
* @default true
*/
this.player_can_remove_assets_from_contents = true;
/**
* Vessels allow aspects to contain substances.
* <a href="adventurejs.Vessel.html">Vessel</a> is a distinct class
* with its own methods and properties for managing substances.
* In theory, any aspect can contain substances: but in practice,
* only in aspects are used. For example, consider a pile of dust
* under a bed: rather than adding the dust to bed.aspects.under.vessel,
* you would create a dustpile asset and add dust to dustpile.aspects.in.vessel.
* @var {Object} adventurejs.Aspect#vessel
* @default {}
*/
this.vessel = {};
/**
* Nests allow aspects to contain characters.
* <a href="adventurejs.Vessel.html">Vessel</a> is a distinct class
* with its own methods and properties for managing substances.
* A collection of properties that defines whether a player may
* enter this aspect, what actions they are allowed to perform
* in it, and the default posture they will take upon entering.
* Aspects and Rooms both share these properties. To initialize a nest:
* <pre class="display"><code class="language-javascript">this.nest = new adventurejs.Nest(
* "nest",
* this.game_name,
* this.context_id
* ).set({
* preposition: this.name,
* });
* </code></pre>
* @var {Object} adventurejs.Aspect#nest
* @default {}
*/
this.nest = {};
/**
* <strong>orientation</strong> determines whether an Aspect
* is horizontal or vertical. Chiefly meant to distinguish
* between player being on a flat surface such as a table,
* vs player being on a vertical face such as a tree.
* @var {String} adventurejs.Aspect#orientation
* @default "horizontal"
*/
this.orientation = "horizontal";
return this;
}
get contents() {
return this._contents;
}
set contents(arr) {
if (!Array.isArray(this._contents)) {
this._contents = [];
}
this._contents = A.validateAssetList(arr);
}
get with_assets() {
return this._with_assets;
}
set with_assets(arr) {
if (!Array.isArray(this._with_assets)) {
this._with_assets = [];
}
this._with_assets = A.validateAssetList(arr);
}
get with_classes() {
return this._with_classes;
}
set with_classes(arr) {
if (!Array.isArray(this._with_classes)) {
this._with_classes = [];
}
this._with_classes = A.validateClassList(arr);
}
get number_of_listable_things() {
var num = this.contents.length;
for (var i = num - 1; i > -1; i--) {
if (!this.game.getAsset(this.contents[i]).is.listed) {
num--;
}
}
return num;
}
/**
* For the most part the answer is yes. It might not be for <code>in</code>.
* @todo reachability
*/
canPlayerReachThisContents() {
//
}
/**
* For the most part the answer is yes. It might not be for <code>in</code>.
* @todo visibility
*/
canPlayerSeeThisContents() {
//
}
/**
* canCharacter(property) returns aspect.nest.can[property] or false.
* @param {*} property A property to test.
* @returns {Boolean}
*/
canCharacter(property) {
if (!this.nest || !this.nest.can) return false;
return this.nest.can[property] || false;
}
canThisHoldThat(asset) {
let response = { fail: false, msg: "", status: "", return: undefined };
let msg = "";
let can = true;
if ("string" === typeof asset) {
asset = this.game.getAsset(asset);
}
if (!asset || !(asset instanceof adventurejs.Tangible)) {
this.game.debug(
` | Verb.js via ${this.name}.js | received bad request `
);
msg += this.game.settings.getUnparsedMessage(
this.game.getInput().input
);
response = {
fail: true,
msg: msg,
status: "bad_request",
return: false,
};
return response;
}
}
}
adventurejs.Aspect = Aspect;
})();