// goTo.js
(function () {
/*global adventurejs A*/
"use strict";
/**
* @augments {adventurejs.Verb}
* @class goTo
* @ajsnode game.dictionary.verbs.goTo
* @ajsconstruct MyGame.createVerb({ "name": "goTo", [...] });
* @ajsconstructedby adventurejs.Dictionary#createVerb
* @hideconstructor
* @ajsinstanceof Verb
* @ajsnavheading LocomotionVerbs
* @summary Verb meaning go to location, or location of asset.
* @tutorial Scripting_VerbSubscriptions
* @tutorial Verbs_VerbAnatomy
* @tutorial Verbs_VerbProcess
* @tutorial Verbs_ModifyVerbs
* @tutorial Verbs_WriteVerbs
* @classdesc
* <pre class="display border outline">
* <span class="input">> go to grand ballroom</span>
* You go up to the mezzanine. You go south to the Grand Ballroom.
*
* <strong>Grand Ballroom</strong>
* Balls everywhere. Literally. More of a ball pit, really.
* </pre>
* <p>
* <strong>goTo</strong> is not called directly by the parser.
* When a player enters <string>"go to"</string>, the verb
* <code>go</code> is called, and forwards to <code>goTo</code>
* if it's contextually suitable.
* <code>goTo</code> is a special verb that will attempt
* to map a path from the player's location to the specified
* {@link adventurejs.Room|Room},
* or the Room containing a specified
* {@link adventurejs.Tangible|Tangible}
* {@link adventurejs.Asset|Asset}.
* The target asset must be known by player.
* </p>
* <p>
* <code>goTo</code> will not open or unlock doors that the
* player has not already opened or unlocked. It will open
* doors the player has already opened, and it will unlock
* doors the player has already unlocked providing the player
* is carrying a key.
* </p>
* @ajsverbphases
*/
A.Preverbs.goTo = {
name: "goTo",
prettyname: "go to",
//synonyms: ["goto"],
//verb_prep_noun: ["go to"],
player_must_be: {
not_constrained: true,
not_nested_elsewhere: true,
},
/**
* @ajsverbstructures
* @memberof goTo
*/
accepts_structures: ["verb noun"],
/**
* @memberof goTo
* @ajsverbphrase
* phrase1:
* {
* not_global: true,
* accepts_noun: true,
* requires_noun: true,
* noun_must_be:
* {
* known: true,
* tangible: true,
* },
* },
*/
phrase1: {
accepts_noun: true,
requires_noun: true,
noun_must_be: {
known: true,
tangible: true,
},
},
/**
* @memberof goTo
* @ajsverbparams
* with_params: {},
*/
with_params: {},
msgNoObject: "Where did you want to go?",
doTry: function () {
return true;
},
doSuccess: function () {
var input = this.game.getInput();
var direct_object = input.getAsset(1);
var destination = input.getNoun(1);
var current_room = this.game.world._currentRoom;
var player = this.game.getPlayer();
var msg = "";
if (!(direct_object instanceof adventurejs.Room)) {
destination = direct_object.getRoomId();
}
if (destination === current_room) {
this.game.debug(
`F1296 | ${this.name}.js | destination is current room `
);
msg += `Look around. `;
this.game.print(msg);
return false;
}
/**
* We use layout to build a list of rooms and their connections.
*/
var layout = {};
for (var i = 0; i < this.game.room_lookup.length; i++) {
var room = this.game.getAsset(this.game.room_lookup[i]);
if (!room) {
continue;
}
if (!room.player_has_visited) {
// exclude rooms that player hasn't visited yet
this.game.log(
"log",
"high",
"${this.name}.js > " + room.id + ".player_has_visited is false",
"verbs"
);
continue;
}
var nodes = [];
var exits = room.exits;
var keys = Object.keys(exits);
for (var k = 0; k < keys.length; k++) {
var exit = exits[keys[k]];
exit = this.game.getAsset(exit);
if (!exit) {
continue;
}
var destination_room = this.game.getAsset(exit.destinationID);
if (!destination_room) {
continue;
}
var aperture = exit.aperture;
// exclude exits that player hasn't used
if (!exit.is.used) {
this.game.log(
"log",
"high",
`${this.name}.js > excluded ${exit.id}.is.used is false`,
"verbs"
);
continue;
}
// exclude exits that have no destination
if (!exit.destinationID) {
continue;
}
// exclude destinations that player hasn't visited
if (!destination_room.player_has_visited) {
this.game.log(
"log",
"high",
`${this.name}.js > ${destination_room.id}.player_has_visited is false`,
"verbs"
);
continue;
}
// test exits for locked doors
if (aperture) {
var key_assets;
aperture = this.game.getAsset(aperture);
//
// LOCKED
//
if (aperture.isDOV("unlock") && aperture.is.locked) {
key_assets = player.getIOVkeys("unlock", aperture);
// if door is locked and player hasn't got key, pass
if (
!aperture.DOVallowWithNothing("unlock") &&
!key_assets.length
) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.is.locked and player has no key`,
"verbs"
);
continue;
}
// if door is locked and it wasn't locked by player, pass
// 6/30/22 CHANGE: if aperture is locked and player didn't lock it
if (
!aperture.didDoVerbs({
related_verbs: ["lock", "unlock", "pick"],
})
) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.is.locked and has never been unlocked by player`,
"verbs"
);
continue;
}
// if door was locked by player,
// check this aperture's can.auto_unlock
if (!aperture.can.auto_unlock) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.can.auto_unlock is false`,
"verbs"
);
continue;
}
// if door was locked by player,
// check settings.can_auto_unlock_apertures
if (
true !== aperture.can.auto_unlock &&
!this.game.settings.can_auto_unlock_apertures
) {
this.game.log(
"log",
"high",
`${this.name}.js > ${this.game.name}.settings.can_auto_unlock_apertures is false`,
"verbs"
);
continue;
}
}
//
// SEALED
//
if (aperture.isDOV("unseal") && aperture.is.sealed) {
key_assets = player.getIOVkeys("unseal", aperture);
// has player got a key?
if (
!aperture.DOVallowWithNothing("unseal") &&
!key_assets.length
) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.is.sealed and player has no key`,
"verbs"
);
continue;
}
// never auto-unseal apertures the player hasn't already unsealed
// 6/30/22 CHANGE: if aperture is sealed and player didn't seal it
if (!aperture.didDoVerbs({ related_verbs: ["seal", "unseal"] })) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.is.sealed and has never been unsealed by player`,
"verbs"
);
continue;
}
// check aperture settings for auto unlock
if (!aperture.can.auto_unseal) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.can.auto_unseal is false`,
"verbs"
);
continue;
}
// check game settings for auto unseal
if (
!aperture.can.auto_unseal &&
!this.game.settings.can_auto_unseal_apertures
) {
this.game.log(
"log",
"high",
`${this.name}.js > ${this.game.name}.settings.can_auto_unseal_apertures is false`,
"verbs"
);
continue;
}
}
//
// CLOSED
//
if (aperture.isDOV("open") && aperture.is.closed) {
key_assets = player.getIOVkeys("open", aperture);
// has player got a key?
if (!aperture.DOVallowWithNothing("open") && !key_assets.length) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.is.closed and player has no key`,
"verbs"
);
continue;
}
// if door is closed and it wasn't closed by player, pass
// 6/30/22 CHANGE: if aperture is closed and player didn't close it
if (!aperture.didDoVerbs({ related_verbs: ["open", "close"] })) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.is.closed and was not closed by player`,
"verbs"
);
continue;
}
// check aperture settings for auto open
if (!aperture.can.auto_open) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.can.auto_open is false`,
"verbs"
);
continue;
}
// check game settings for auto open
if (
!aperture.can.auto_open &&
!this.game.settings.can_auto_open_apertures
) {
this.game.log(
"log",
"high",
`${this.name}.js > ${this.game.name}.settings.can.auto_open_apertures is false`,
"verbs"
);
continue;
}
}
if (aperture.is.hidden) {
this.game.log(
"log",
"high",
`${this.name}.js > ${aperture.id}.is.hidden`,
"verbs"
);
continue;
}
} // if aperture
// good to go!
nodes.push(exit.destinationID);
}
layout[room.id] = nodes;
}
this.game.log("log", "low", ["layout:", layout], "verbs");
/**
* We use graph to convert uni-directional to bi-directional.
* needs to look like: where: { a: { b: cost of a->b }
*/
var graph = {};
for (var id in layout) {
if (!graph[id]) {
graph[id] = {};
}
layout[id].forEach(function (aid) {
graph[id][aid] = 1;
if (!graph[aid]) {
graph[aid] = {};
}
graph[aid][id] = 1;
});
}
this.game.log("log", "low", ["graph:", graph], "verbs");
//choose start node
var start = this.game.world._currentRoom;
//get all solutions
var solutions = A.dijkstra(graph, start);
this.game.log(
"log",
"low",
["solutions[destination]:", solutions[destination]],
"verbs"
);
if (
"undefined" === typeof solutions[destination] ||
0 === solutions[destination].length
) {
this.game.debug(
`F1297 | ${this.name}.js | No path was found. Blockers may include locked/sealed/closed doors which player has not opened or does not have a key for, or destination unknown to / unvisited by player. `
);
msg += "$(We) don't know of a route between here and there";
if (!destination || !destination.is || !destination.is.known) {
msg += ", or even if there is a there there";
}
msg += ".";
this.handleFailure(msg);
return false;
} else {
var len = solutions[destination].length;
for (var i = 0; i < len; i++) {
var thisRoom, nextRoom;
if (0 === i) {
thisRoom = start;
} else {
thisRoom = solutions[destination][i - 1];
}
nextRoom = solutions[destination][i];
thisRoom = this.game.world[thisRoom];
nextRoom = this.game.world[nextRoom];
// cycle through the room's exits
for (var direction in thisRoom.exits) {
var exit = this.game.world[thisRoom.id + "_" + direction];
var dest = exit.destinationID;
// no destination? probably just a description
if ("undefined" === typeof dest) {
continue;
}
//
if (nextRoom.id === dest) {
this.game.log(
"log",
"low",
["nextRoom.id", nextRoom.id, "dest", dest],
"verbs"
);
var output = "";
var aperture = exit.aperture;
if (aperture) aperture = this.game.getAsset(aperture);
if (aperture && (aperture.is.locked || aperture.is.closed)) {
// THIS METHOD MAY SIDESTEP AUTHOR SIDE EFFECTS
// output = "You ";
// if( aperture.is.locked ) {
// output += "unlock and open ";
// aperture.setLocked(false);//is.locked = false;
// aperture.setClosed(false);//is.closed = false;
// }
// else if( aperture.is.closed ) {
// output = "open ";
// aperture.setClosed(false);//is.closed = false;
// }
// output += " "
// + aperture.articlename
// + ". ";
//this.game.print( output, "concatenate_output" );
// THIS METHOD INVOKES OPEN VERB
this.game.parser.input_queue.push({
input: "open " + aperture.id,
printInput: false,
excludeRoomDescriptions: true,
output_class: "concatenate_output",
linefeed: linefeed,
});
}
/**
* printing this output results in double output with tryTravel ie
* You go west to the Eastern Room. $(We) move west.
* You go west to the East Room. $(We) move west.
* You go west to the Standing Room. $(We) move west.
*/
output =
"$(We) go " +
direction +
" to " +
(nextRoom.use_definite_article_in_lists
? nextRoom.definite_article + " "
: "") +
nextRoom.name +
". ";
// print a linefeed after the last item only
var linefeed = i === len - 1 ? true : undefined;
this.game.parser.input_queue.push({
// print output: output,
/* output was doubling "you move etc" messages
because tryTravel prints them too
leaving this here for now as an option
for future dev */
input: direction,
printInput: false,
excludeRoomDescriptions: true,
output_class: "concatenate_output",
linefeed: linefeed,
});
continue;
}
}
}
}
//display solutions
// console.log("From '"+start+"' to");
// for(var s in solutions) {
// if(!solutions[s]) continue;
// if( s !== destination ) continue;
// console.log(" -> " + s + ": [" + solutions[s].join(", ") + "] (dist:" + solutions[s].dist + ")");
// }
return true;
},
};
})(); // goTo