// Settings.js
(function () {
/*global adventurejs A*/
"use strict";
/**
*
* @class adventurejs.Settings
* @ajsinternal
* @param {Game} game A reference to the game instance.
* @ajsnavheading FrameworkReference
* @summary Manages settings for a {@link adventurejs.Game|Game} instance.
* @classdesc
* <p>
* <strong>Settings</strong> is a repository for global Game
* options. Settings is created automatically
* by {@link adventurejs.Game|Game}. This is an internal class
* that authors should not need to construct. However,
* authors can set options from their game file as
* shown below, or change them during runtime
* with calls to <a href="#set">MyGame.settings.set()</a>.
* </p>
* <h3 class="examples">Example:</h3>
* <pre class="display"><code class="language-javascript">var MyGame = new adventurejs.Game( "MyGame", "GameDisplay" );
* MyGame.settings.set({
* max_undos: 20,
* show_room_names_in_exit_descriptions: false,
* can_auto_open_apertures: false
* });
* </code></pre>
*/
class Settings {
constructor(game) {
this.game = game;
/**
* Each level of undo stores a snapshot of the entire
* game state in memory. Raising a game's undo count
* will increase its memory requirements.
* @var {Number} adventurejs.Settings#max_undos
* @default 10
*/
this.max_undos = 10;
/**
* Console logging: control the types of statements that print to console.
* Useful for debugging.
* 0 = critical ( warnings & errors only )
* 1 = high
* 2 = medium
* 3 = low
* @var {Number} adventurejs.Settings#log_level
* @default 1
*/
this.log_level = 1;
/**
* Trace log statements: control which log levels call console.trace()
* Useful for debugging.
* @var {Number} adventurejs.Settings#log_trace_level
* @default 0
*/
this.log_trace_level = 0;
/**
* Console logging: control what keywords print to console.
* Warnings and Errors will print regardless of settings.
* See the available list of keywords below. Comment any
* to remove those from console output.
* @var {Number} adventurejs.Settings#log_level
* @default 1
*/
this.log_keywords = {
tutorial: true,
exit: true,
character: true,
tangible: true,
behavior: true /* onX functions */,
copyoperations: true /* clone, merge, diff, restore, save */,
dictionary: true,
display: true,
game: true,
parser: true,
print: true,
room: true,
saverestore: true,
vessel: true,
substanceemitter: true,
substancemixer: true,
travel: true,
utility: true,
verb: true,
verbs: true,
};
/**
* If set true, when player tries to go in a direction that hasn't
* got an {@link adventurejs.Exit|Exit},
* print the current room's Exits as a reminder
* of what Exits are available. Though this is a global setting,
* {@link adventurejs.Room|Rooms}
* can have their own setting. Room setting overrides global setting.
* @var {Boolean} adventurejs.Settings#when_travel_fails_list_exits
* @default true
*/
this.when_travel_fails_list_exits = true;
/**
* If set true,
* {@link adventurejs.Exit|Exit} descriptions can include
* the name of the {@link adventurejs.Room|Room} that the
* Exit leads to. (The logic for this may also consider other
* conditions such as whether the player knows about the
* other Room.) Though this is a global setting,
* Exits can have their own setting. Exit setting overrides
* global setting.
* @var {Boolean} adventurejs.Settings#show_room_names_in_exit_descriptions
* @default true
*/
this.show_room_names_in_exit_descriptions = true;
/**
* If set true,
* {@link adventurejs.Exit|Exit} descriptions can include
* the name of the {@link adventurejs.Room|Room} that the
* Exit leads to once player knows about the destination Room.
* Generally player must visit a room to know about it, but
* there can be exceptions.
* Though this is a global setting,
* Exits can have their own setting. Exit setting overrides
* global setting.
* @var {Boolean} adventurejs.Settings#show_room_names_in_exit_descriptions_only_when_room_is_known
* @default false
*/
this.show_room_names_in_exit_descriptions_only_when_room_is_known = false;
/**
* If set true,
* {@link adventurejs.Exit|Exit} descriptions can include
* the name of the {@link adventurejs.Room|Room} that the
* Exit leads to once player has used the Exit.
* Though this is a global setting,
* Exits can have their own setting. Exit setting overrides
* global setting.
* @var {Boolean} adventurejs.Settings#show_room_names_in_exit_descriptions_only_after_exit_has_been_used
* @default true
*/
this.show_room_names_in_exit_descriptions_only_after_exit_has_been_used = true;
/**
* If true, disambiguation prompts will present an ordered
* list rather than plain text.
* <br><br>
* 'Disambiguation' means the player has asked for an object
* that could refer to multiple things, and the
* {@link adventurejs.Parser|Parser}
* hasn't been able to narrow it down to one object.
* Let's say there are three cats of different colors in the room.
* Normally the game would ask the player:
* <pre class="display">
* which cat did you mean, the orange cat, the black cat, or the piebald cat?
* </pre>
* If this is true, prompts will look like this instead:
* <pre class="display">
* Which cat did you mean?
* 1) the orange cat
* 2) the black cat
* 3) the piebald cat
* </pre>
* When this is done, the player can enter 1, 2, or 3 and the parser
* will understand their choice.
* <br><br>
* For more information about Disambiguation, see
* <a href="/doc/NextSteps_Disambiguation.html">How to Disambiguate</a>
* @var {Boolean} adventurejs.Settings#show_disambiguation_as_ordered_list
* @default false
*/
this.show_disambiguation_as_ordered_list = false;
/**
* If true, disambiguation prompts will present a plain text
* paragraph with numbered options in it.
* <br><br>
* 'Disambiguation' means the player has asked for an object
* that could refer to multiple things, and the
* {@link adventurejs.Parser|Parser}
* hasn't been able to narrow it down to one object.
* Let's say there are three kinds of pizza in the room.
* Normally the game would ask the player:
* <pre class="display">
* Which pizza did you mean, the pepperoni pizza,
* the pineapple pizza, or the vegetable pizza?
* </pre>
* If this is true, numbers will be inserted into the text,
* like this:
* <pre class="display">
* Which pizza did you mean, 1) the pepperoni pizza,
* 2) the pineapple pizza, or 3) the vegetable pizza?
* </pre>
* When this is done, the player can enter 1, 2, or 3 and the parser
* will understand their choice.
* <br><br>
* For more information about Disambiguation, see
* <a href="/doc/NextSteps_Disambiguation.html">How to Disambiguate</a>
* @var {Boolean} adventurejs.Settings#
* @default true
*/
this.show_disambiguation_as_numbered_paragraph = true;
/**
* If true, when player enters a blank line,
* treat it as if player typed "look".
* Otherwise play dumb.
* @var {Boolean} adventurejs.Settings#if_input_is_empty_print_room_description
* @default false
*/
this.if_input_is_empty_print_room_description = false;
/**
* if_input_is_empty_print_this can return string or array or function.
* @var {String|Array|Function} adventurejs.Dictionary#if_input_is_empty_print_this
* @default "I didn't see any input."
*/
this.if_input_is_empty_print_this = `I didn't see any input. `;
/**
* When the parser can't understand a player's input, it prints
* a generic statement using this string. The statement is
* also used in some other arbitrary places, which is to say,
* players may see it a lot.
* If you would like to customize this output
* you can do so by setting this property to your preferred
* output and including $(input) as a token to be replaced
* with the player's input. For example:
* <pre class="display"><code class="language-javascript">MyGame.settings.set({
* if_parser_has_no_response_print_this: "I didn't understand <em class='unparsed'>$(input)</em>. ",
* });
* </code></pre>
* This example wraps the player's input in an <em> element so that
* it can be styled differently from the rest of the output. This
* isn't required and you can omit the <em> if you prefer.
* And in fact you don't even need to include the original input
* if you prefer not to. Just omit <code>$(input)</code> from your
* string and the string will be printed as you set it.
* <br><br>
* <code>if_parser_has_no_response_print_this</code> supports the use of
* <code>{@link adventurejs.Game#getStringOrArrayOrFunction|getStringOrArrayOrFunction}</code>
* meaning that you can vary it up if you like by setting it to
* a rotating array of strings or a contextually aware function.
* @var {Boolean} adventurejs.Settings#if_parser_has_no_response_print_this
* @default true
*/
this.if_parser_has_no_response_print_this = `I don't understand what you mean by <em class='unparsed'>$(input)</em>. `;
/**
* If true, when player enters just a noun,
* treat it as if player typed "examine x".
* Otherwise play dumb.
* @var {Boolean} adventurejs.Settings#if_input_is_an_asset_name_examine_it
* @default true
*/
this.if_input_is_an_asset_name_examine_it = true;
/**
* We're all about strings here. Your job as an author is
* to write lots and lots and lots of them. Sometimes it
* gets boring seeing the same string over and over again,
* and you just want to show some alternates.
* Some properties let you do that through the use of
* {@link adventurejs.Game#getStringOrArrayOrFunction|getStringOrArrayOrFunction}.
* You can feed getStringOrArrayOrFunction a string
* or an array or a function (that returns a string).
* If you provide an array, this setting determines
* whether strings in the array are presented in sequence,
* or randomly. If this setting is true, arrays
* will be randomized.
* <br><br>
* For more information, see
* <a href="/doc/Scripting_StringArrayFunction.html">How to Use String|Array|Function</a>
* @var {Boolean} adventurejs.Settings#randomize_arrays_in_getStringOrArrayOrFunction
* @default true
* @todo List properties that this applies to.
*/
this.randomize_arrays_in_getStringOrArrayOrFunction = true;
/**
* When players apply a verb to a group of objects,
* such as "take all",
* if any of those objects have a verb hook,
* that may create awkward output. For example, let's say
* there are four medallions and one is cursed, using
* verb_hooks.take,
* and the player inputs "take all medallions":
* <pre class="display">
* "You take the gold medallion. You take the brass medallion.
* Oh no, taking the onyx medallion transported you to the
* nether realms! You take the silver medallion."
* </pre>
* This option tries to provide a little bit of control over that
* by saying: if player picks up these four objects, take the cursed
* one last. It doesn't solve all problems. For instance, if
* two of our medallions each took the player to a different location,
* that might break something. Some situations can't be solved
* programmatically and just come down to good game design.
* <br><br>
* Alternately, you may want to handle assets with verb_hooks
* first. Let's say you have an explosive item, and if player inputs
* "take all" you want the explosive to be picked up first and then
* end the turn in order to prevent other items from handled in the
* same turn.
* <br><br>
* For more information on verb_hooks, see
* <a href="/doc/Scripting_VerbPhases.html">Verb Phases</a>
* <br><br>
* Valid values are: -1 (move to beginning of queue), 0 (leave in place),
* 1 (move to end of queue).
* Warning: may cause undesired side effects.
* @var {Boolean} adventurejs.Settings#move_verb_hooks_to_end_of_queue
* @default true
*/
// @TODO this block no longer works since moving verb_hooks into verb subscriptions
this.enqueue_assets_with_verb_hooks = { order: 1 }; // valid values: -1, 0, 1
/**
* See the explanation for
* <a href="#move_verb_hooks_to_end_of_queue">move_verb_hooks_to_end_of_queue</a>
* above. This reverses that. When using a verb on multiple items,
* you can choose to move items with custom functions to the beginning
* of the queue. If this option is set true, it will override
* move_verb_hooks_to_end_of_queue. Included for
* completionism. Warning: this may cause undesired side effects.
* @var {Boolean} adventurejs.Settings#move_verb_hooks_to_start_of_queue
* @default false
*/
// @TODO this block no longer works since moving verb_hooks into verb subscriptions
this.move_verb_hooks_to_start_of_queue = false;
/**
* If true, {@link goTo} {@link adventurejs.Verb|Verb} will
* {@link open} closed {@link adventurejs.Aperture|Apertures} that
* lie between the start {@link adventurejs.Room|Room}
* and the destination Room, but only if player has already
* opened those Apertures before.
* @var {Boolean} adventurejs.Settings#can_auto_open_apertures
* @default true
*/
this.can_auto_open_apertures = true;
/**
* If true, {@link goTo} {@link adventurejs.Verb|Verb} will
* {@link unlock} locked {@link adventurejs.Aperture|Apertures} that
* lie between the start {@link adventurejs.Room|Room}
* and the destination Room, but only if player has already
* unlocked those Apertures before
* and is carrying any necessary {@link adventurejs.Key|Keys}.
* @var {Boolean} adventurejs.Settings#can_auto_unlock_apertures
* @default true
*/
this.can_auto_unlock_apertures = true;
/**
* If true, {@link goTo} {@link adventurejs.Verb|Verb} will
* {@link unseal} sealed {@link adventurejs.Aperture|Apertures} that
* lie between the start {@link adventurejs.Room|Room}
* and the destination Room, but only if player has already
* unsealed those Apertures and is carrying any necessary {@link adventurejs.Key|Keys}.
* @var {Boolean} adventurejs.Settings#can_auto_unseal_apertures
* @default true
*/
this.can_auto_unseal_apertures = true;
/**
* If true, when player {@link tie_noun1_to_noun2|ties} a
* {@link adventurejs.Rope|Rope} to another asset,
* {@link adventurejs.Game|Game} will move the asset into
* player's inventory, using
* {@link adventurejs.Tangible#onMoveThatToThis|player.onMoveThatToThis()}.
* <br><br>
* If the asset in that situation is coded with a verb event hook,
* it has the potential to cause undesirable side effects.
* It's an unlikely edge case, but if you find yourself in
* that situation, try setting this to false to avoid side effects.
* @var {Boolean} adventurejs.Settings#on_tie_rope_to_takeable_object_take_object
* @default true
*/
this.on_tie_rope_to_takeable_object_take_object = true;
/**
* If true, when player ties a rope to its maximum number
* of tied points, will remove rope from player's inventory.
* <br><br>
* If the rope in that situation has a verb event hook for
* {@link drop},
* it has the potential to cause undesirable side effects.
* It's an unlikely edge case, but if you find yourself in
* that situation, try setting this to false to avoid side effects.
* @var {Boolean} adventurejs.Settings#on_tie_rope_to_max_objects_drop_rope
* @default true
*/
this.on_tie_rope_to_max_objects_drop_rope = true;
/**
* If true, and player unties a rope they are not holding, which
* is tied to an object that is not in the player's inventory,
* if the rope is takeable, player will take it.
* <br><br>
* If the rope in that situation has a verb event hook for
* {@link drop},
* it has the potential to cause undesirable side effects.
* It's an unlikely edge case, but if you find yourself in
* that situation, try setting this to false to avoid side effects.
* @var {Boolean} adventurejs.Settings#on_untie_rope_from_object_outside_inventory_take_rope
* @default true
*/
this.on_untie_rope_from_object_outside_inventory_take_rope = true;
/**
* Standard value for drinking, in ml. If player
* {@link drink|drinks} from a
* {@link adventurejs.Vessel|Vessel},
* this is the quantity of liquid that will be removed from it
* (unless the Vessel has custom drink logic).
* @var {Number} adventurejs.Settings#mouthful
* @default 100
*/
this.mouthful = 100;
/**
* Standard value for holding in hands, in ml. Generally
* if player tries to interact with substances without a container
* they're given some kind of "slips through your fingers" message
* but in the case of something like "throw sand" when player is
* on a beach, we'll let them throw a handful of sand,
* and if the containing object is not infinite, subtract this
* amount from it.
* @var {Number} adventurejs.Settings#handful
* @default 100
*/
this.handful = 100;
/**
* Room temperature in celsius.
* @var {Number} adventurejs.Settings#room_temperature
* @default 20
* @todo celsius/farenheit conversion
*/
this.room_temperature = 20;
/**
* States lookup for substances.
* @var {Object} adventurejs.Settings#states
*/
this.states = {
SOLID: "SOLID",
LIQUID: "LIQUID",
GAS: "GAS",
};
/**
* The <code>pronouns</code> setting is a lookup table
* for use with <code>person</code>.
* @var {Object} adventurejs.Settings#pronouns
*/
this.pronouns = {
FIRST: "first",
SECOND: "second",
PLURAL: "plural",
MALE: "male",
FEMALE: "female",
NONBINARY: "nonbinary",
NONHUMAN: "nonhuman",
};
/**
* The <code>person</code> setting allows you to change
* the pronouns in all of the default response strings
* built-in to Adventurejs. For instance, if take
* has a response that says <code>You pick up the axe.</code>
* you can change the word You to I in this and all other
* responses by setting person from seconds to first.
* @var {String} adventurejs.Settings#person
*/
this.person = this.pronouns.SECOND;
// settings so far unused
this.ambient_gravity = 1;
this.ambient_light = 1;
this.light_sources_are_cumulative = false;
this.ambient_sound = 0;
this.things_float_on_water = true;
this.things_sink_in_water = true;
this.things_disappear_in_water = true;
this.things_rise_in_low_gravity = true;
this.things_fall_in_high_gravity = true;
this.things_disappear_in_space = true;
this.verbosity = 0;
this.print_verbose_room_descriptions_on_first_visit = true;
this.min_verbosity = -2;
this.max_verbosity = 1;
// only -2 to 1 are supported
// -2 = briefer, -1 = brief, 0 = description, 1 = verbose
/**
* If <code>print_debug_messages</code> is true, some failure responses may print
* additional information to the game display.
* Intended as a debugging aid.
* @var {String} adventurejs.Settings#print_debug_messages
*/
this.print_debug_messages = false;
/**
* If true, room/zone events will print. See
* <a href="/doc/NextSteps_SceneEvents.html">Scene Events</a>
* for more information.
* @var {Boolean} adventurejs.Settings#enable_events
* @default true
*/
this.enable_events = true;
/**
* If player inputs something like
* "throw rock at water", the word "water" may be
* ambiguous because multiple water containers are
* present: a glass of water, a bottle of water, a lake.
* If any of those containers is a body of water like
* a lake or ocean, we can probably assume that the
* player meant "throw rock in lake". If
* <code>throw_at_substance_prefers_bodies</code>
* is true, the verb <code>throw</code>'s logic will
* make that assumption, rather than prompting player
* to specify an indirect object.
* @var {Boolean} adventurejs.Settings#throw_at_substance_prefers_bodies
* @default true
*/
this.throw_at_substance_prefers_bodies = true;
/**
* If true, some verbs, on receiving input of sentence
* structure "verb noun" may infer an indirect object
* if one is required and player is carrying it.
* This pattern is intended
* chiefly for locks and keys, with the idea being that
* if player inputs "unlock door" while carrying a door
* key, the game can infer the door key instead of
* prompting the player for a key, which may seem
* obnoxious in some circumstances.
* It works with lock / unlock, seal / unseal, open / close,
* and some others.
* @var {Boolean} adventurejs.Settings#try_to_infer_indirect_objects
* @default true
*/
this.try_to_infer_indirect_objects = true;
/**
* If true, <code>try_to_infer_indirect_objects</code> will
* only be applied once a player has already interacted
* with the direct object. This pattern is intended to
* prevent giving away puzzle solutions. For example,
* perhaps there are many keys but only one that opens
* a certain lock, and determining which key opens the
* lock is part of a puzzle. If this is set to true,
* it would prevent players from being able to unlock
* the door without trying all the keys to find the
* correct one.
* @var {Boolean} adventurejs.Settings#infer_indirect_objects_only_after_interaction
* @default true
*/
this.infer_indirect_objects_only_after_interaction = true;
/**
* If true, when <code>try_to_infer_indirect_objects</code>
* results in multiple assets, prompt player to select one.
* @var {Boolean} adventurejs.Settings#if_inferred_multiple_indirect_objects_then_prompt
* @default true
*/
this.if_inferred_multiple_indirect_objects_then_prompt = false;
/**
* Set the visibility of the compass rose in the status bar.
* @var {Boolean} adventurejs.Settings#show_compass_rose_in_status
* @default true
*/
this._show_compass_rose_in_status = true;
/* *
* Set the status bar compass rose to show a magnified
* version on hover.
* @var {Boolean} adventurejs.Settings#magnify_compass_rose_on_hover
* @default true
*/
// Object.defineProperty(this, 'magnify_compass_rose_on_hover', {
// configurable: true,
// get() { return this.__magnify_compass_rose_on_hover; },
// set(value) {
// this.__magnify_compass_rose_on_hover = value;
// if(this.game.display) this.game.display.exitsContainerEl.classList[value?'add':'remove']('magnify');
// },
// });
// this.magnify_compass_rose_on_hover = false;
/* *
* When we initialize assets, By default we add
* the name, each word of the name and each pair
* of words in the name. In some situations this
* may be excessive and lead to the parser
* misinterpreting phrases.
* @var {Boolean} adventurejs.Settings#split_asset_names_for_world_lookup
* @default true
*/
//this.split_asset_names_for_world_lookup = true;
/**
* We can track the user's y position. This option sets the maximum distance that the
* player can reach vertically, ie when climbing an object like a tree. 1 is considered
* to be about the height of a person.
* @var {Boolean} adventurejs.Settings#reach_height
* @default 1
*/
this.reach_height = 1;
/**
* We can track the user's x/z position. This option sets the maximum distance that the
* player can reach horizontally within a room. 0.33 is considered to be about arm's length.
* @var {Boolean} adventurejs.Settings#reach_length
* @default .33
*/
this.reach_length = 0.33;
/**
* We can track the user's y position. This option sets the default distance that the
* player travels vertically, ie when climbing an object like a tree. 1 is considered
* to be about the height of a person.
* @var {Boolean} adventurejs.Settings#climb_height
* @default 1
*/
this.climb_height = 1;
/**
* We can track the user's x/z position. This option sets the default distance that the
* player travels horizontally when moving within a room. 1 is considered to be about
* the height of a person.
* @var {Boolean} adventurejs.Settings#stride_length
* @default .33
*/
this.stride_length = 0.33;
/**
* We can track the user's x/z position. This option sets the maximum distance that the
* player travels horizontally when moving within a room. 1 is considered to be about
* the height of a person.
* @var {Boolean} adventurejs.Settings#jump_length
* @default 1
*/
this.jump_length = 1;
/**
* We can track the user's x/z position. This option sets whether
* x/z are considered in determining reachability. By default,
* all tangible assets are positioned at x:0,y:0,z:0, and reachability
* checks don't consider x/z (though y is always considered). Now imagine
* you have a dorm room with a bed and a desk, where the bed is at
* x:3 and the desk is across the room at x:-3. If this property is true,
* then their relative distance apart will be taken into account
* when determining reachability.
* @var {Boolean} adventurejs.Settings#xz_determines_reachability
* @default false
*/
this.xz_determines_reachability = true;
}
/**
* Provides a chainable shortcut method for setting a number of properties on the instance.
* @method adventurejs.Settings#set
* @param {Object} props A generic object containing properties to copy to the instance.
* @returns {adventurejs.Settings} Returns the instance the method is called on (useful for chaining calls.)
* @chainable
*/
set(props) {
return A.deepSet.call(this.game, props, this);
}
get show_compass_rose_in_status() {
return this._show_compass_rose_in_status;
}
set show_compass_rose_in_status(value) {
this._show_compass_rose_in_status = value;
if (this.game.display)
this.game.display.exitsContainerEl.classList[value ? "remove" : "add"](
"hidden"
);
}
}
adventurejs.Settings = Settings;
})();