// getModifiedDescription.js
(function () {
/*global adventurejs A*/
var p = adventurejs.Game.prototype;
/**
* Get complex indirect descriptions,
* such as "look at this through magnifying glass with candle", where
* "through magnifying glass, with candle" is a key at
* asset.descriptions[identifier]["through magnifying glass, with candle"]
* "look" is always the default direct object description.
* @memberOf adventurejs.Game
* @method adventurejs.Game#getModifiedDescription
* @param {Object} params
* @return {String}
*/
p.getModifiedDescription = function Game_getModifiedDescription(params) {
var asset = params.asset;
var identifier = params.identifier;
var find_modifiers = params.find_modifiers;
var fallback_base = params.fallback_base;
var append_base = params.append_base;
var prepend_base = params.prepend_base;
var msg = "";
if (!identifier || identifier === "at") identifier = "look";
if ("string" === typeof asset) {
asset = this.game.getAsset(asset);
if (!asset) return "";
}
if (asset.proxy) {
let proxy = this.game.getAsset(asset.proxy);
if (proxy) {
let proxy_description = this.game.getModifiedDescription({
asset: proxy,
identifier: identifier,
find_modifiers: find_modifiers,
fallback_base: fallback_base,
});
if (proxy_description) return proxy_description;
}
}
if (identifier === "at") {
identifier = "look";
}
var input = this.game.getInput();
var base_description =
this.game.getDescription({ asset: asset, identifier: identifier }) || "";
// does object have descriptions at all?
if (!asset.descriptions) {
// @TODO if asset.description return that
return fallback_base ? base_description : "";
}
// does this description identifier exist?
if (!asset.descriptions[identifier]) {
// if identifier was look then we got nuthin
if (identifier === "look" || !asset.descriptions.look) {
return fallback_base ? base_description : "";
}
// we have look - does it exist under look?
if (!asset.descriptions.look[identifier]) {
// no such identifier
return fallback_base ? base_description : "";
}
// it exists - treat it as a modifier on look
input.pushViewModifier(identifier, null, "input");
identifier = "look";
}
// view_modifiers is expected to look like:
// view_modifiers: [ { asset: ClassedObject, prep: "string", type: "string" } ]
if (find_modifiers) {
this.game.findViewModifiers();
}
if (input.view_modifiers.length === 0) {
return fallback_base ? base_description : "";
}
// look for valid descriptions
for (let index = 0; index < input.view_modifiers.length; index++) {
let indirect_identifier = input.view_modifiers[index].identifier;
let indirect_asset = input.view_modifiers[index].asset;
let indirect_aspects;
if (!indirect_identifier || !indirect_asset) {
continue;
}
// account for equivalencies: for example,
// in the case of glasses, binoculars, etc player might say
// "look at x with binoculars" while author has coded for
// "look at x through binoculars" and we'll allow it
// though it means we have to search for all acceptable equivalencies
indirect_aspects = [indirect_identifier]; // convert to array
// "look with" may be equivalent to "look through"
if (
indirect_identifier === "with" &&
indirect_asset.hasQuirk("look_with_means_look_through")
) {
if (!indirect_aspects.includes("through"))
indirect_aspects.push("through");
}
if (
indirect_identifier === "through" &&
indirect_asset.hasQuirk("look_with_means_look_through")
) {
if (!indirect_aspects.includes("with")) indirect_aspects.push("with");
}
// "look in" may be equivalent to "look on"
if (
indirect_identifier === "in" &&
indirect_asset.hasQuirk("in_means_on")
) {
if (!indirect_aspects.includes("on")) indirect_aspects.push("on");
}
// save equivalencies
for (let i = 0; i < indirect_aspects.length; i++) {
let indirect_identifier = indirect_aspects[i];
let target = `${indirect_identifier} ${indirect_asset.name}`;
if (!input.view_modifiers[index].equivalencies) {
input.view_modifiers[index].equivalencies = [];
}
if (!input.view_modifiers[index].equivalencies.includes(target)) {
input.view_modifiers[index].equivalencies.push(target);
}
}
}
// example of a view_modifier
// {aspect: 'with', asset: Candle, type: 'auto', equivalencies: Array(2)}
// equivalencies: (2) ['with occult candle', 'through occult candle']
let provided_modifier_targets = [];
for (let index = 0; index < input.view_modifiers.length; index++) {
provided_modifier_targets.push(input.view_modifiers[index].equivalencies);
}
// we're allowing authors to set view modifiers in any order:
// for instance "in pit, from tree" or "from tree, in pit"
// so we have to look for every possible combination
let expanded_modifier_targets = A.generateCombinations(
provided_modifier_targets
);
if ("string" === typeof asset.descriptions[identifier]) {
return asset.descriptions[identifier];
}
let comparisons = [];
let description_keys = Object.keys(asset.descriptions[identifier]);
// loop through expanded_modifier_targets
// inside each loop, loop through indirect descriptions
for (let i = 0; i < expanded_modifier_targets.length; i++) {
for (let j = 0; j < description_keys.length; j++) {
// modifier can be greater than description
// but description can not be greater than modifier
// alternately
// modifier can have elements that are not in description
// description can't have any elements that are not in modifier
if (
description_keys[j].split(",").length >
expanded_modifier_targets[i].split(",").length
) {
continue;
}
let count = A.countCommonElements(
expanded_modifier_targets[i],
description_keys[j]
);
comparisons.push({
view_modifiers: expanded_modifier_targets[i],
description: description_keys[j],
count: count,
});
// console.warn(
// "expanded_modifier_targets[i]:",
// expanded_modifier_targets[i],
// "\n",
// "description_keys[j]:",
// description_keys[j]
// );
// console.warn("count", count);
}
}
// @TODO modify this so if it finds something with multiples it prints that
// but if it finds multiple singles it prints those
var highestcount = -1;
var highestindex = -1;
for (let i = 0; i < comparisons.length; i++) {
// console.warn(comparisons[i].count);
let count = comparisons[i].count;
let description_length = comparisons[i].description.split(",").length;
if (count === description_length && count > highestcount) {
highestcount = comparisons[i].count;
highestindex = i;
}
}
// console.warn(
// "getModifiedDescription > Highest count is " + highestcount,
// comparisons[highestindex]
// );
if (highestcount > -1) {
// save a record of use back to the original view_modifiers
// console.warn(
// "getModifiedDescription > comparisons[highestindex].description",
// comparisons[highestindex].description
// );
let modifiers_set = comparisons[highestindex].description;
let best_description =
asset.descriptions[identifier][comparisons[highestindex].description];
let modifiers = modifiers_set.split(",").map((item) => item.trim());
let user_modified = false;
for (let i = 0; i < modifiers.length; i++) {
let modifier = modifiers[i];
for (let j = 0; j < input.view_modifiers.length; j++) {
if (input.view_modifiers[j].string === modifier) {
input.view_modifiers[j].used = true;
if (input.view_modifiers[j].type === "input") user_modified = true;
}
}
}
// call the description
msg = A.getSAF.call(this.game, best_description);
if (prepend_base || this.game.settings.concatenate_descriptions) {
msg = base_description + msg;
}
if (append_base) {
msg = msg + base_description;
}
return msg;
}
// fallback
return fallback_base ? base_description : "";
};
})();