// climb.js
(function () {
/*global adventurejs A*/
"use strict";
/**
* @augments {adventurejs.Verb}
* @class climb
* @ajsnode game.dictionary.verbs.climb
* @ajsconstruct MyGame.createVerb({ "name": "climb", [...] });
* @ajsconstructedby adventurejs.Dictionary#createVerb
* @hideconstructor
* @ajsinstanceof Verb
* @ajsnavheading LocomotionVerbs
* @summary Verb meaning climb, as in "climb tree"; or, travel in specified direction.
* @ajssynonyms climb onto, climb on, climb up
* @tutorial Scripting_VerbSubscriptions
* @tutorial Verbs_VerbAnatomy
* @tutorial Verbs_VerbProcess
* @tutorial Verbs_ModifyVerbs
* @tutorial Verbs_WriteVerbs
* @classdesc
* <pre class="display border outline">
* <span class="input">> climb tree</span>
* You climb a good way into the tree before your climbing
* is interrupted by an angry squirrel. Apparently you've
* put a foot into the knot in which it stores its nuts.
* </pre>
* <p>
* <strong>Climb</strong> can operate with or without
* a noun, inferring from context whether the action can succeed.
* </p>
* @ajsverbphases doBeforeTry, doAfterTry, doBeforeSuccess, doAfterSuccess
*/
A.Preverbs.climb = {
name: "climb",
prettyname: "climb",
past_tense: "climbed",
synonyms: ["climb"],
type: { locomotion: true, travel: true },
verb_prep_noun: [
// parse climb up / climb down as climb
// to prevent up/down being treated as nouns
//"climb down",
//"climb up",
],
/**
* @ajsverbstructures
* @memberof climb
*/
accepts_structures: [
"verb",
"verb noun",
"verb preposition",
"verb preposition noun",
"verb noun preposition noun",
"verb preposition noun preposition noun",
// "verb preposition noun preposition noun preposition noun",
],
player_must_be: {
not_on_floor: true,
not_constrained: true,
//not_under: true,
//not_behind: true,
},
/**
* @memberof climb
* @ajsverbphrase
* phrase1:
* {
* accepts_noun: true,
* accepts_preposition: true,
* accepts_direction: true,
* noun_must_be:
* {
* not_global: true,
* tangible: true,
* known: true,
* present: true,
* visible: true,
* reachable: true,
* },
* },
*/
phrase1: {
accepts_noun: true,
accepts_preposition: true,
accepts_preposition_without_noun: true,
accepts_direction: true,
noun_must_be: {
tangible: true,
known: true,
present: true,
visible: true,
reachable: true,
},
},
/**
* @memberof climb
* @ajsverbphrase
* phrase2:
* {
* accepts_noun: true,
* accepts_preposition: true,
* noun_must_be:
* {
* tangible: true,
* known: true,
* present: true,
* visible: true,
* reachable: true,
* },
* },
*/
phrase2: {
accepts_noun: true,
noun_must_be: {
tangible: true,
known: true,
present: true,
visible: true,
reachable: true,
},
accepts_preposition: true,
accepts_these_prepositions: ["with", "to"],
},
/**
* @memberof climb
* @ajsverbparams
* with_params: {},
*/
with_params: {},
doTry: function () {
var input = this.game.getInput();
var direct_object = input.getAsset(1);
var direct_preposition = input.getPreposition(1);
var indirect_object = input.getAsset(2);
var indirect_preposition = input.getPreposition(2);
var player = this.game.getPlayer();
var nest_preposition = player.getNestPreposition();
var nest_asset = player.getNestAsset();
var msg = "";
var try_go, up, down, fromto;
if (direct_object?.is.global) {
// @TODO floor handling
this.game.debug(
`F1724 | ${this.name}.js | ${direct_object.id}.is.global `,
);
msg += `$(We) can't climb ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// doVerb go
// @TODO what if you're half way up a wall and try to climb east on the wall?
try_go = direct_object?.direction;
if (
direct_preposition &&
-1 !== ["in", "out", "under", "behind"].indexOf(direct_preposition)
) {
// ie climb in, climb under, climb behind
try_go = true;
}
if (try_go) {
this.game.debug(`F1190 | ${this.name}.js | infer 'go', doVerb go`);
this.game.dictionary.doVerb("go");
//this.game.tryTravel(direct_preposition);
return null;
}
// sentence structure: verb ----------
// ex: climb
if (input.hasStructure("verb")) {
// if climb was entered without any preposition or object,
// assume up and try to get an object
if (nest_asset) {
// up or down?
let h = nest_asset.dimensions.height;
let y = nest_asset.position.y;
let py = player.position.y;
if (py >= y + h) input.setPreposition(1, "down");
else input.setPreposition(1, "up");
// update input
input.setAsset(1, nest_asset);
input.setStructure("verb preposition noun");
direct_object = input.getAsset(1);
direct_preposition = input.getPreposition(1);
} else {
// prompt for a direct object
input.setSoftPrompt({ noun1: true });
this.game.debug(
`F1177 | ${this.name}.js | no noun provided or inferrable, soft prompt noun1`,
);
msg += `What would $(we) like to climb? `;
this.handleFailure(msg);
return null;
}
} // verb
down =
(direct_object && direct_object.direction === "down") ||
direct_preposition === "off" ||
direct_preposition === "down" ||
(direct_preposition === "from" && !indirect_preposition);
up =
(direct_object && direct_object.direction === "up") ||
(direct_object && !direct_preposition) ||
direct_preposition === "up" ||
direct_preposition === "on" ||
direct_preposition === "over";
// sentence structure: verb preposition ----------
// climb down, climb up
// try to get a noun
if (input.hasStructure("verb preposition")) {
// was nest asset the implied noun?
if (nest_asset) {
input.setAsset(1, nest_asset);
input.setStructure(input.getStructure() + " noun");
direct_object = input.getAsset(1);
if (down && !nest_asset.is.climbable) {
input.setPreposition(1, "off");
direct_preposition = "off";
this.game.debug(
`F1206 | ${this.name}.js | infer 'climb down ${nest_asset.id} `,
);
}
}
if (!nest_asset) {
if (down) {
// if player is not nested then "climb down" means "down"
this.game.debug(
`F1186 | ${this.name}.js | infer 'go down', tryTravel down`,
);
this.game.tryTravel("down");
return null;
}
if (up) {
this.game.debug(
`F1193 | ${this.name}.js | infer 'go up', tryTravel up`,
);
this.game.tryTravel("up");
return null;
}
}
} // verb preposition
// sentence structure: verb noun ----------
// ex: climb tree, climb east
// try to get a preposition
if (input.hasStructure("verb noun")) {
// is player already on noun?
// set preposition to nest_preposition
if (nest_asset && nest_asset.id === direct_object.id) {
direct_preposition = nest_preposition;
input.setPreposition(1, direct_preposition);
input.setStructure("verb preposition noun");
}
// is player not on noun?
if (nest_asset && nest_asset.id !== direct_object.id) {
input.swapPhrases(1, 2);
input.setPreposition(1, "from");
input.setPreposition(2, "to");
input.setAsset(1, nest_asset);
input.setStructure("verb preposition noun preposition noun");
direct_object = input.getAsset(1);
indirect_object = input.getAsset(2);
direct_preposition = "from";
indirect_preposition = "to";
}
} // verb noun
// sentence structure: verb preposition noun ----------
// ex: climb on tree
// verify preposition and noun
if (input.hasStructure("verb preposition noun")) {
if (direct_preposition === "to") {
// "climb to" is used for climbing from one
// nest to another. If player isn't nested,
// it's just regular "climb".
if (!nest_asset) {
input.setInPhrase(1, "preposition", "on");
if (direct_object.position.y >= player.position.y) up = true;
else down = true;
}
if (nest_asset && nest_asset.id === direct_object.id) {
this.game.debug(
`F1192 | ${this.name}.js | player is nested on ${direct_object.id} `,
);
msg += `$(We're) already on ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
if (nest_asset) {
input.swapPhrases(1, 2);
input.setPreposition(1, "from");
input.setAsset(1, nest_asset);
direct_object = input.getAsset(1);
direct_preposition = input.getPreposition(1);
indirect_object = input.getAsset(2);
indirect_preposition = input.getPreposition(2);
fromto = true;
input.setStructure("verb preposition noun preposition noun");
}
} // to
if (down && !nest_asset) {
// @TODO this is a quick patch - need more handling here
// could be my favorite example of a ladder in a hole
if (direct_object.position.y >= player.position.y) {
this.game.debug(
`F1718 | ${this.name}.js | player is not on ${direct_object.id} `,
);
msg += `$(We're) not on ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// @TODO should this return true? do we still have more checking to do?
}
if (down && nest_asset) {
if (
direct_object.id === nest_asset.id &&
direct_object.is.unleavable
) {
this.game.debug(
`F1184 | ${this.name}.js | ${direct_object.id}.is.unleavable is true `,
);
msg += `$(We) can't leave ${direct_object.articlename} that way. `;
this.handleFailure(msg);
return null;
}
// player is not on direct_object
if (direct_object.id !== nest_asset.id) {
this.game.debug(
`F1185 | ${this.name}.js | player is not on ${direct_object.id} `,
);
msg += `$(We're) not on ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
} // down
if (up && nest_asset && nest_asset.id !== direct_object.id) {
// it's fromto
input.swapPhrases(1, 2);
input.setPreposition(1, "from");
input.setPreposition(2, "to");
input.setAsset(1, nest_asset);
input.setStructure("verb preposition noun preposition noun");
direct_object = input.getAsset(1);
indirect_object = input.getAsset(2);
direct_preposition = "from";
indirect_preposition = "to";
}
if (up && nest_asset && nest_asset.id === direct_object.id) {
if (
"on" === nest_preposition &&
player.position.y >= nest_asset.getYTop()
) {
// player is already on target
// and can't climb any higher
this.game.debug(
`F1178 | ${this.name}.js | player.position.y >= ${direct_object.id}.dimensions.height `,
);
msg += `$(We) can't climb any higher on ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
}
// player is nested some other way with target
// and needs to unnest first
if (up && nest_asset && "on" !== nest_preposition) {
this.game.debug(
`F1179 | ${this.name}.js | player is ${nest_preposition} ${nest_asset.id} `,
);
msg += `$(We) can't do that from $(our) position ${player.getPostureGerund()} ${nest_preposition} ${
nest_asset.articlename
}. `;
this.handleFailure(msg);
return null;
}
// player can't nest in/on object at all
if (
(up && !direct_object.isDOV(this.name)) ||
!direct_object.canPlayerNest("on")
) {
this.game.debug(
`F1196 | ${this.name}.js | !${direct_object.id}.dov.${this.name} or !${direct_object.id}.aspects.on.player.can.enter `,
);
msg += `$(We) can't climb ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
} // up
} // verb preposition noun
// sentence structure: verb noun preposition noun ----------
if (input.hasStructure("verb noun preposition noun")) {
// example: 'climb cliff with pick'
if ("with" !== indirect_preposition && "to" !== indirect_preposition) {
// already accounted for this in accepts_these_prepositions
this.game.debug(`F1856 | ${this.name}.js | irregular phrase `);
msg += this.game.parser.getUnparsedMessage(input.input);
this.handleFailure(msg);
return null;
}
// to ----------
if ("to" === indirect_preposition) {
// we got something like "climb tree to window"
// we're going to treat it like "climb from tree to window"
input.setPreposition(1, "from");
direct_preposition = "from";
input.setStructure("verb preposition noun preposition noun");
}
// with ----------
if ("with" === indirect_preposition) {
input.verb_params.with = true;
// works with any indirect object?
if (direct_object.DOVallowWithAnything(this.name)) {
return true;
}
// indirect object not required?
if (direct_object.DOVallowWithNothing(this.name)) {
this.game.debug(
`F1857 | ${this.name}.js | ${direct_object.id} can't be ${this.state} with ${indirect_object.id} `,
);
msg += `${indirect_object.Articlename} won't help $(us) ${this.name} ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// indirect object usable with direct object?
if (!direct_object.DOVallowWithAsset(this.name, indirect_object)) {
this.game.debug(
`F1858 | ${this.name}.js | ${direct_object.id}.dov.${this.name}.with_assets does not include ${indirect_object.id} `,
);
msg += `$(We) can't ${this.name} ${direct_object.articlename} ${indirect_preposition} ${indirect_object.articlename}. `;
this.handleFailure(msg);
return null;
}
// single use indirect object?
if (
indirect_object.IOVallowOnce(this.name) &&
indirect_object.IOVdidDo(this.name)
) {
this.game.debug(
`F1859 | ${this.name}.js | ${indirect_object.id}.iov.${
this.name
}.once and ${indirect_object.id}.iov.${this.name}.do_count is ${
indirect_object.iov[this.name].do_count
} `,
);
msg += `${indirect_object.Articlename} has already been used to ${this.name} something. `;
this.handleFailure(msg);
return null;
}
} // with
} // verb noun preposition noun
// sentence structure: verb preposition noun preposition noun
// ex: climb from tree to roof
if (input.hasStructure("verb preposition noun preposition noun")) {
// climb from a to b is the only phrase we accept in this form
if (direct_preposition === "to" && indirect_preposition === "from") {
// reverse them
var phrase2 = Object.assign({}, input.getPhrase(2));
var phrase1 = Object.assign({}, input.getPhrase(1));
input.setPhrase(1, phrase2);
input.setPhrase(2, phrase1);
direct_object = input.getAsset(1);
direct_preposition = input.getPreposition(1);
indirect_object = input.getAsset(2);
indirect_preposition = input.getPreposition(2);
}
if (!fromto)
fromto =
direct_preposition === "from" && indirect_preposition === "to";
if (!fromto) {
// "climb from to" is the only variant that can handle 2 nouns
this.game.debug(
`F1180 | ${this.name}.js | this phrase and sentence structure not supported `,
);
msg += this.game.parser.getUnparsedMessage(input.input);
this.handleFailure(msg);
return null;
}
if (fromto && (!nest_asset || nest_asset.id !== direct_object.id)) {
// from noun must be the nest
this.game.debug(
`F1187 | ${this.name}.js | ${nest_asset.id} !== ${direct_object.id}`,
);
msg += `$(We're) not on ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
} // fromto
// @TODO can player reach iobj from nest?
if (fromto /*&& direct_object.id !== indirect_object.id*/) {
this.game.debug(`F1853 | ${this.name}.js | change to fromto `);
msg += `$(We) can't climb to ${indirect_object.articlename} from ${nest_preposition} ${direct_object.articlename}. `;
this.handleFailure(msg);
return null;
}
} // verb preposition noun preposition noun
// save our findings for doSuccess so we don't have to repeat this logic
if (up) input.verb_params.up = true;
else if (down) input.verb_params.down = true;
else if (fromto) input.verb_params.fromto = true;
return true;
},
doSuccess: function () {
var input = this.game.getInput();
var direct_object = input.getAsset(1);
var indirect_object = input.getAsset(2);
var player = this.game.getPlayer();
var nest_asset = player.getNestAsset();
var top = direct_object.getYTop();
var bottom = direct_object.getYBottom();
var direct_preposition = input.getPreposition(1);
var indirect_preposition = input.getPreposition(2);
var preposition = "on";
var posture = direct_object.aspects[preposition].player.posture;
var newY = 0;
var newPoint = { y: 0 };
var grounded;
var msg = "";
var results;
this.game.debug(`F1181 | ${this.name}.js | doSuccess `);
this.game.debug(
`F1340 | ${this.name}.js | start y is ${player.position.y} `,
);
// up ----------
if (input.verb_params.up) {
this.game.debug(`F1374 | ${this.name}.js | climb up `);
if (
direct_object.dimensions.height <= this.game.settings.reach_height
) {
// default posture for "climb on [short thing like furniture]" is stand
posture = direct_object.aspects[preposition].player.can.stand
? "stand"
: direct_object.aspects[preposition].player.posture;
msg += `$(We) ${
input.verb_params.with
? "use " + indirect_object.articlename + " to"
: ""
} climb up ${
direct_object.articlename
} and ${posture} ${preposition} it. `;
} else {
newY =
top - player.position.y >= this.game.settings.reach_height
? player.position.y + this.game.settings.reach_height
: top;
msg += `$(We) ${
input.verb_params.with
? "use " + indirect_object.articlename + " to"
: ""
} climb `;
let percent = newY / top;
if (percent === 1) msg += "to the top of ";
else if (percent > 0.5) msg += "further up ";
else msg += "part way up ";
msg += `${direct_object.articlename}. `;
}
// if it isn't already nested, nest it
if (nest_asset.id !== direct_object.id) {
results = player.onNestThisToThat(direct_object, preposition);
if ("undefined" !== typeof results) return results;
player.posture = posture;
}
player.position.y = newY;
} // up
// down ----------
if (input.verb_params.down) {
this.game.debug(`F1123 | ${this.name}.js | climb down `);
// climbing down a thing with its bottom above the ground, like a stalactite
if (bottom > 0 && player.position.y > bottom) {
newY =
player.position.y - bottom >= this.game.settings.reach_height
? player.position.y - this.game.settings.reach_height
: bottom;
msg += `$(We) ${
input.verb_params.with
? "use " + indirect_object.articlename + " to"
: ""
} climb downward on ${direct_object.articlename}. `;
}
// player is already at bottom of thing that is above the ground
else if (bottom > 0 && player.position.y === bottom) {
msg += `$(We) fall to the ground. `;
newY = 0;
grounded = true;
// TODO gravity
}
// climbing down a thing rooted to the ground
else if (bottom === 0) {
newY =
player.position.y - bottom >= this.game.settings.reach_height
? player.position.y - this.game.settings.reach_height
: bottom;
msg += `$(We) ${
input.verb_params.with
? "use " + indirect_object.articlename + " to"
: ""
} climb `;
if (newY > 0) {
msg += `downward on `;
} else {
msg += `down off `;
grounded = true;
}
msg += `${direct_object.articlename}. `;
}
// climbing down a thing below ground level
// @todo what happens at the bottom?
// at the moment we're left, stuck at the bottom of the thing
else if (0 > bottom) {
newY =
player.position.y - bottom >= this.game.settings.reach_height
? player.position.y - this.game.settings.reach_height
: bottom;
msg += `$(We) ${
input.verb_params.with
? "use " + indirect_object.articlename + " to"
: ""
} climb ${newY > bottom ? "further down " : "to the bottom of "}`;
if (newY > bottom) {
msg += `further down `;
} else {
msg += `to the bottom of `;
}
msg += `${direct_object.articlename}. `;
}
player.position.y = newY;
if (grounded) {
results = player.onUnnestThisFromThat(nest_asset);
if ("undefined" !== typeof results) return results;
player.posture = "stand";
}
} // down
// from/to ----------
if (input.verb_params.fromto) {
this.game.debug(`F1373 | ${this.name}.js | climb from to `);
posture = indirect_object.aspects.on.player.posture;
// msg += `$(We) ${
// input.verb_params.with
// ? "use " + indirect_object.articlename + " to"
// : ""
// } climb from ${direct_object.articlename} to ${
// indirect_object.articlename
// } and ${posture} ${preposition} it. `;
msg += `$(We) climb from ${direct_object.articlename} to ${indirect_object.articlename} and ${posture} ${preposition} it. `;
// if player is nested, unnest
if (nest_asset.id !== direct_object.id) {
results = player.onUnnestThisFromThat(direct_object);
if ("undefined" !== typeof results) return results;
}
// if it isn't already nested, nest it
if (nest_asset.id !== indirect_object.id) {
results = player.onNestThisToThat(indirect_object, preposition);
if ("undefined" !== typeof results) return results;
player.posture = posture;
}
let range = indirect_object.getYRange();
if (newY < range.min) newY = range.min;
else if (newY > range.max) newY = range.max;
player.setY(newY);
} // fromto
this.game.debug(`F1694 | climb.js | end y is ${player.position.y} `);
// print output
this.handleSuccess(msg, direct_object);
return true;
},
}; // END p.preverbs.push
})();