// tryTravel.js
(function () {
/*global adventurejs A*/
"use strict";
var p = adventurejs.Game.prototype;
/**
* Tries to move the player in the specified direction.
* @memberOf adventurejs.Game
* @method adventurejs.Game#tryTravel
* @param {String} direction
* @param {Object} params
*/
p.tryTravel = function Game_tryTravel(direction, params) {
this.game.log("log", "high", "tryTravel.js > " + direction, "Travel");
var input = this.game.getInput();
var currentRoom = this.getCurrentRoom();
var player = this.game.getPlayer();
var nest_parent_object = player.getNestAsset();
var nest_preposition = player.getNestPreposition();
var exitID = currentRoom.exits[direction]; // may be no exit
var msg = "";
var results;
if ("undefined" === typeof params) params = {};
// TODO: constraint message
if (player.is.constrained) {
this.game.debug(`F1103 | tryTravel.js |player.is.constrained`);
msg +=
A.getSAF.call(this.game, player.constrained_msg) ||
"$(We) can't go anywhere. ";
if (msg) this.game.print(msg, input.output_class);
return null;
}
// player is holding something like a rope or a railing
if (player.IOVgetConnectionCount("hold") > 0) {
this.game.debug(
`F1133 | tryTravel.js | ${player.id}.iov.hold.with_params.connections.length is ${player.iov.hold.with_params.connections.length}`
);
msg += `$(We'll) have to let go of ${this.game.getPrintableObjectList({
objects: player.IOVgetConnections("hold"),
})}. `;
if (msg) this.game.print(msg, input.output_class);
return null;
}
// is player nested in something?
if (player.isNested()) {
// by default, can't reach an exit while nested
// but there are exceptions
var canReach = false;
var reachableThings;
if (nest_parent_object.is.rideable) {
canReach = true;
}
// is player nested behind/in/on/under something
// that can reach this direction?
if (nest_parent_object.hasAspectAt(nest_preposition)) {
reachableThings =
nest_parent_object.aspects[nest_preposition]
.things_player_can_reach_from_this_aspect;
// @todo this no longer works - need to call canPlayerReach( direction )
if (-1 < reachableThings.indexOf(direction)) {
canReach = true;
}
}
// is player nested on top of something that can reach this direction?
if (!canReach && player.isNestedOnTop()) {
// try the convenience prop first
reachableThings =
nest_parent_object.things_player_can_reach_from_top_of_this;
if (true === A.isIdInMixedArray(direction, reachableThings))
canReach = true;
// if not, try the aspect positions
if (!canReach) {
reachableThings =
nest_parent_object.aspects[nest_preposition]
.things_player_can_reach_from_positions_of_this_aspect.top;
if (true === A.isIdInMixedArray(direction, reachableThings))
canReach = true;
}
}
// is player nested on bottom of something that can reach this direction?
if (!canReach && player.isNestedOnBottom()) {
// try the convenience prop first
reachableThings =
nest_parent_object.things_player_can_reach_from_bottom_of_this;
if (true === A.isIdInMixedArray(direction, reachableThings))
canReach = true;
// if not, try the aspect positions
if (!canReach) {
reachableThings =
nest_parent_object.aspects[nest_preposition]
.things_player_can_reach_from_positions_of_this_aspect.bottom;
if (true === A.isIdInMixedArray(direction, reachableThings))
canReach = true;
}
}
if (false === canReach) {
this.game.debug(
`F1134 | tryTravel.js | ${direction} is unreachable from ${nest_preposition} ${nest_parent_object.id}`
);
msg += `$(We'll) have to get ${player.getPrettyUnnestPreposition()} ${
nest_parent_object.articlename
} first. `;
if (msg) this.game.print(msg, input.output_class);
return null;
}
}
// does this room have an exit in the specified direction?
if ("undefined" === typeof exitID) {
this.game.debug(`F1115 | tryTravel.js |exitID is undefined`);
//var directionObject = this.game.world[ "global_" + direction ];
var direct_object = this.game.world["global_" + direction];
// console.warn( "global!" );
// console.warn( direct_object.id );
var global_description;
var currentRoom_scenery_object =
currentRoom.room_scenery[direct_object.id];
var currentRoomZone = this.game.world[currentRoom.zone];
var currentRoomZone_scenery_object;
if ("undefined" !== typeof currentRoomZone) {
currentRoomZone_scenery_object =
currentRoomZone.zone_scenery[direct_object.id];
}
// try to get current room's scenery settings
// description can be left undefined
if (
"undefined" !== typeof currentRoom_scenery_object &&
true === currentRoom_scenery_object.enabled &&
"undefined" !== typeof currentRoom_scenery_object.description
) {
// console.warn( "USE CURRENT ROOM SCENERY OBJECT DESCRIPTION" );
this.game.debug(
`F1116 | tryTravel.js |use room.room_scenery description for ${currentRoom.id}.room_scenery.${direct_object.id}`
);
global_description = currentRoom_scenery_object.description;
}
// otherwise try to get current room's zone scenery settings
else if (
"undefined" !== typeof currentRoomZone &&
"undefined" !== typeof currentRoomZone_scenery_object &&
true === currentRoomZone_scenery_object.enabled &&
"undefined" !== typeof currentRoomZone_scenery_object.description
) {
this.game.debug(
`F1117 | tryTravel.js |use room.zone.zone_scenery description for ${currentRoom.zone}.zone_scenery.${direct_object.id}`
);
// console.warn( "USE CURRENT ROOM ZONE SCENERY OBJECT DESCRIPTION" );
global_description = currentRoomZone_scenery_object.description;
}
// otherwise if room enables object,
// try to get object's native (aka global) description
else if (
"undefined" !== typeof currentRoom_scenery_object &&
true === currentRoom_scenery_object.enabled &&
"undefined" !== typeof direct_object.description
) {
this.game.debug(
`F1118 | tryTravel.js |use room.room_scenery description for ${direct_object.id}.description`
);
// console.warn( "USE GLOBAL ASSET DESCRIPTION" );
global_description = direct_object.description;
}
if ("undefined" !== typeof global_description) {
msg += A.getSAF.call(this.game, global_description);
}
if (
this.settings.when_travel_fails_list_exits ||
this.world[this.world._currentRoom].when_travel_fails_list_exits
) {
this.game.debug(
`F1120 | tryTravel.js | ${this.game_name}.settings.when_travel_fails_list_exits or ${currentRoom.id}.when_travel_fails_list_exits`
);
var exits = this.getCurrentRoomExits();
if (exits) msg += " " + exits;
}
this.print(msg, input.output_class);
return;
}
// ok we think there's an exit
var exit = this.world[exitID];
this.game.log(
"log",
"high",
"tryTravel.js > found exit: ",
exit.id,
"Travel"
);
// is there actually an exit object?
if ("undefined" === typeof exit) {
this.game.debug(`F1104 | tryTravel.js |no exit found matching ${exitID}`);
msg += `That doesn't appear to be an exit. `;
this.print(msg, input.output_class);
return;
}
if ("undefined" !== typeof exit.aperture && "" !== exit.aperture) {
var aperture = this.game.world[exit.aperture];
var key_assets;
var locked_msg = `${aperture.Articlename} is closed and locked. `;
var sealed_msg = `${aperture.Articlename} is sealed. `;
var closed_msg = `${aperture.Articlename} is closed. `;
this.game.log(
"log",
"high",
"tryTravel.js > found aperture " + aperture.id,
"Travel"
);
//
// 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.debug(
`F1105 | tryTravel.js | ${aperture.id}.is.locked and player hasn't got a key`
);
this.print(msg + locked_msg);
return;
}
// never auto-unlock apertures the player hasn't already unlocked
// 6/30/22 CHANGE: if aperture is locked and player didn't lock it
if (
!aperture.didDoVerbs({
related_verbs: ["lock", "unlock", "pick", "open", "close"],
})
) {
this.game.debug(
`F1106 | tryTravel.js | ${aperture.id}.is.locked and player did not lock it - no auto unlock on first unlock`
);
this.print(msg + locked_msg);
return;
}
// check aperture settings for auto unlock
if (!aperture.can.auto_unlock) {
this.game.debug(
`F1107 | tryTravel.js | ${aperture.id}.is.locked and .can.auto_unlock is false`
);
this.print(msg + locked_msg);
return;
}
}
//
// 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.debug(
`F1108 | tryTravel.js | ${aperture.id}.is.sealed and player hasn't got a key`
);
this.print(msg + sealed_msg);
return;
}
// 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.debug(
`F1111 | tryTravel.js | ${aperture.id}.is.sealed and player did not seal it - no auto unseal on first unseal`
);
this.print(msg + sealed_msg);
return;
}
// check aperture settings for auto unlock
if (!aperture.can.auto_unseal) {
this.game.debug(
`F1109 | tryTravel.js | ${aperture.id}.is.sealed and .can.auto_unseal is false`
);
this.print(msg + sealed_msg);
return;
}
}
//
// 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.debug(
`F1108 | tryTravel.js | ${aperture.id}.is.closed and player hasn't got a key`
);
this.print(msg + closed_msg);
return;
}
// never auto-open apertures the player hasn't already opened
// 6/30/22 CHANGE: if aperture is closed and player didn't close it
if (!aperture.didDoVerbs({ related_verbs: ["open", "close"] })) {
this.game.debug(
`F1719 | tryTravel.js | ${aperture.id}.is.closed and player did not close it - no auto unseal on first unseal`
);
this.print(msg + closed_msg);
return;
}
// check aperture settings for auto open
if (!aperture.can.auto_open) {
this.game.debug(
`F1110 | tryTravel.js | ${aperture.id}.is.closed and .can.auto_open is false`
);
this.print(closed_msg);
return;
}
}
// check if aperture is hidden
if (aperture.is.hidden) {
this.game.debug(`F1112 | tryTravel.js | ${aperture.id}.is.hidden`);
msg += `Hmm. You don't see any exit ${exit.direction}. `;
this.print(msg, input.output_class);
return;
}
} // if aperture
// direction is valid but has no destination, try to print description
if ("undefined" === typeof exit.destinationID && exit.descriptions.look) {
this.game.log(
"log",
"high",
"tryTravel.js > " +
exit.direction +
" has no destination. Printing description.",
"Travel"
);
this.game.debug(
`F1112 | tryTravel.js | ${exit.id}.destinationID is blank or undefined`
);
this.print(msg + A.getSAF.call(this, exit.descriptions.look));
return;
}
// is player holding a rope that is tied to something in the room?
// @TODO expand this to include other attachment types
// such as things that are plugged into each other
if (player.hasRopesThatBlockTravel()) {
var ropes = player.getRopesThatBlockTravel();
var rope = this.game.getAsset(ropes[0]);
var object_rope_is_tied_to;
for (var i = 0; i < rope.dov.tie?.with_params.connections.length; i++) {
if (player.id !== rope.dov.tie?.with_params.connections[i]) {
object_rope_is_tied_to = this.game.getAsset(
rope.dov.tie?.with_params.connections[i]
);
}
}
this.game.debug(`F1113 | tryTravel.js |player.hasRopesThatBlockTravel`);
msg = `$(We're) preventing from leaving by ${rope.articlename}`;
if ("undefined" !== typeof object_rope_is_tied_to) {
msg += ` tied to ${object_rope_is_tied_to.articlename}`;
}
msg += `. `;
if (msg) this.game.print(msg, input.output_class);
return null;
}
if (player.isNested() && false === nest_parent_object.is.rideable) {
results = player.onUnnestThisFromThat(nest_parent_object);
if ("undefined" !== typeof results) return results;
}
// doSuccess
// direction is valid and destination exists
if ("undefined" !== typeof exit.destinationID) {
this.game.log(
"log",
"high",
"tryTravel.js > Direction is valid and destination exists.",
"Travel"
);
this.game.debug(
`F1876 | tryTravel.js | doSuccess, travel to ${exit.destinationID}`
);
// TODO add logic for hidden exits
exit.setUsed();
var addPeriod = false;
if ("undefined" !== typeof aperture) {
if (aperture.is.locked && aperture.can.auto_unlock) {
/*&& this.game.settings.can_auto_unlock_apertures*/
msg += `$(We) unlock ${aperture.articlename}`;
addPeriod = true;
aperture.setLocked(false); //.locked = false;
aperture.incrementDoVerbCount("unlock");
}
if (aperture.is.sealed && aperture.can.auto_unseal) {
/*&& this.game.settings.can_auto_unseal_apertures*/
msg += `$(We) unseal ${aperture.articlename}`;
addPeriod = true;
aperture.setSealed(false); //is.sealed = false;
aperture.incrementDoVerbCount("unseal");
}
if (aperture.is.closed && aperture.can.auto_open) {
/*&& this.game.settings.can_auto_open_apertures*/
if (!msg) {
msg += `$(We) open ${aperture.articlename}`;
} else {
msg += ", and then open it";
}
//+ ". ";
addPeriod = true;
aperture.setClosed(false);
aperture.incrementDoVerbCount("open");
}
if (addPeriod) msg += ". ";
aperture.setUsed();
if ("undefined" !== typeof aperture.linked_asset) {
this.game.world[aperture.linked_asset].setUsed();
}
}
var newRoomID = exit.destinationID;
var newRoomObj = this.world[newRoomID];
for (var exitProp in newRoomObj.exits) {
var newRoomExitID = newRoomObj.exits[exitProp];
var newRoomExitObj = this.world[newRoomExitID];
if (newRoomExitObj.destinationID === currentRoom.id) {
newRoomExitObj.setUsed();
}
}
if (player.isNested() && nest_parent_object.is.rideable) {
// results = newRoom.onMoveThatToThis( nest_parent_object, "in" ); // string
// if( false === results ) { return false; }
// else if ( null === results ) { return null; }
msg += `$(We) ride ${nest_parent_object.articlename} `;
if (this.game.dictionary.verbs[direction].is_spatial_direction) {
//msg += "to ";
} else if (this.game.dictionary.verbs[direction].is_compass_direction) {
msg += `to the `;
}
msg += `${direction}. `;
} else if (params.with) {
msg += `$(We) push `;
msg += this.game.getPrintableObjectList({
objects: params.with,
article: "definite",
});
msg += ` `;
if (this.game.dictionary.verbs[direction].is_spatial_direction) {
//msg += "to ";
} else if (this.game.dictionary.verbs[direction].is_compass_direction) {
msg += `to the `;
}
msg += `${direction}. `;
if ("string" === typeof params.with) {
params.with = [params.with];
}
for (var i = 0; i < params.with.length; i++) {
var object = this.game.getAsset(params.with[i]);
object.moveFrom(object.getPlaceAsset());
object.moveTo("in", newRoomObj);
}
} else if (exit.descriptions && exit.descriptions.travel) {
msg += A.getSAF.call(this.game, exit.descriptions.travel);
} else {
msg +=
this.game.dictionary.verbs[input.input_verb] &&
this.game.dictionary.verbs[input.input_verb].type.travel
? `$(We) ${input.input_verb} `
: `$(We) ${player.getPostureVerb()} `;
if (this.game.dictionary.verbs[direction].is_spatial_direction) {
// relative directions include "port" and "starboard",
// "left" and "right", etc
//msg += "to ";
}
// else if(this.game.dictionary.verbs[direction].is_compass_direction)
// {
// msg += "to the ";
// }
msg +=
direction +
" to " +
(newRoomObj.use_definite_article_in_lists
? newRoomObj.definite_article + " "
: "") +
newRoomObj.name +
". ";
}
this.game.print(msg, input.output_class);
this.setPlayerRoom(this.world[newRoomID], params);
return true;
}
// final fallback
this.game.debug(`F1114 | tryTravel.js |no ${direction} exit found`);
msg += `There doesn't appear to be an exit ${direction}. `;
if (
this.settings.when_travel_fails_list_exits ||
this.world[this.world._currentRoom].when_travel_fails_list_exits
) {
this.game.debug(
`F1119 | tryTravel.js | ${this.game_name}.settings.when_travel_fails_list_exits is true`
);
var exits = this.getCurrentRoomExits();
if (exits) msg += " " + exits;
}
this.print(msg, input.output_class);
};
})();