/* move.c */
#include "copyrite.h"
#include "config.h"
#include <ctype.h>
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#include "conf.h"
#include "mushdb.h"
#include "intrface.h"
#include "match.h"
#include "externs.h"
#include "globals.h"
#include "parse.h"
#include "confmagic.h"
extern void do_destroy _((dbref player, char *name, int confirm));
void moveto _((dbref what, dbref where));
void moveit _((dbref what, dbref where));
static void send_contents _((dbref loc, dbref dest));
static void maybe_dropto _((dbref loc, dbref dropto));
void enter_room _((dbref player, dbref loc));
void safe_tel _((dbref player, dbref dest));
int can_move _((dbref player, const char *direction));
static dbref find_var_dest _((dbref player, dbref exit_obj));
void do_move _((dbref player, const char *direction, int type));
void do_firstexit _((dbref player, const char *what));
void do_get _((dbref player, const char *what));
void do_drop _((dbref player, const char *name));
void do_enter _((dbref player, const char *what, int is_alias));
void do_leave _((dbref player));
dbref global_exit _((dbref player, const char *direction));
dbref remote_exit _((dbref player, const char *direction));
void move_wrapper _((dbref player, const char *command));
void
moveto(what, where)
dbref what;
dbref where;
{
enter_room(what, where);
}
void
moveit(what, where)
dbref what;
dbref where;
{
dbref loc, old;
/* remove what from old loc */
if ((loc = old = Location(what)) != NOTHING) {
Contents(loc) = remove_first(Contents(loc), what);
}
/* test for special cases */
switch (where) {
case NOTHING:
Location(what) = NOTHING;
return; /* NOTHING doesn't have contents */
case HOME:
where = Exits(what); /* home */
safe_tel(what, where);
return;
/*NOTREACHED */
break;
}
/* now put what in where */
PUSH(what, Contents(where));
Location(what) = where;
if (!WIZ_NOAENTER || !(Wizard(what) && Dark(what)))
if ((where != NOTHING) && (old != where)) {
did_it(what, what, NULL, NULL, "OXMOVE", NULL, NULL, old);
if (Hearer(what)) {
did_it(what, old, "LEAVE", NULL, "OLEAVE", "has left.",
"ALEAVE", old);
if (Typeof(where) != TYPE_ROOM)
did_it(what, where, NULL, NULL, "OXENTER", NULL, NULL, old);
if (Typeof(old) != TYPE_ROOM)
did_it(what, old, NULL, NULL, "OXLEAVE", NULL, NULL, where);
did_it(what, where, "ENTER", NULL, "OENTER", "has arrived.",
"AENTER", where);
} else {
/* non-listeners only trigger the actions not the messages */
did_it(what, old, NULL, NULL, NULL, NULL, "ALEAVE", old);
did_it(what, where, NULL, NULL, NULL, NULL, "AENTER", where);
}
}
did_it(what, what, "MOVE", NULL, "OMOVE", NULL, "AMOVE", where);
}
#define Dropper(thing) (Hearer(thing) && Connected(Owner(thing)))
static void
send_contents(loc, dest)
dbref loc;
dbref dest;
{
dbref first;
dbref rest;
first = Contents(loc);
Contents(loc) = NOTHING;
/* blast locations of everything in list */
DOLIST(rest, first) {
Location(rest) = NOTHING;
}
while (first != NOTHING) {
rest = Next(first);
if (Dropper(first)) {
Location(first) = loc;
PUSH(first, Contents(loc));
} else
enter_room(first, Sticky(first) ? HOME : dest);
first = rest;
}
Contents(loc) = reverse(Contents(loc));
}
static void
maybe_dropto(loc, dropto)
dbref loc;
dbref dropto;
{
dbref thing;
if (loc == dropto)
return; /* bizarre special case */
if (Typeof(loc) != TYPE_ROOM)
return;
/* check for players */
DOLIST(thing, Contents(loc)) {
if (Dropper(thing))
return;
}
/* no players, send everything to the dropto */
send_contents(loc, dropto);
}
void
enter_room(player, loc)
dbref player;
dbref loc;
{
dbref old;
dbref dropto;
static int deep = 0;
if (deep++ > 15) {
deep--;
return;
}
if (!GoodObject(player)) {
deep--;
return;
}
/* check for room == HOME */
if (loc == HOME)
loc = Home(player);
if ((Typeof(player) != TYPE_PLAYER) && (Typeof(player) != TYPE_THING)) {
fprintf(stderr, "ERROR: Non object moved!! %d\n", player);
fflush(stderr);
deep--;
return;
}
if (Typeof(loc) == TYPE_EXIT) {
fprintf(stderr, "ERROR: Attempt to move %d to exit %d\n", player, loc);
deep--;
return;
}
if (loc == player) {
fprintf(stderr, "ERROR: Attempt to move player %d into itself\n", player);
deep--;
return;
}
/* get old location */
old = Location(player);
/* go there */
moveit(player, loc);
/* if old location has STICKY dropto, send stuff through it */
if ((loc != old) && Dropper(player) &&
(old != NOTHING) && (Typeof(old) == TYPE_ROOM) &&
((dropto = Location(old)) != NOTHING) && Sticky(old))
maybe_dropto(old, dropto);
/* autolook */
look_room(player, loc, 2);
deep--;
}
/* teleports player to location while removing items they shouldnt take */
void
safe_tel(player, dest)
dbref player;
dbref dest;
{
dbref first;
dbref rest;
if (dest == HOME)
dest = Home(player);
if (Owner(Location(player)) == Owner(dest)) {
enter_room(player, dest);
return;
}
first = Contents(player);
Contents(player) = NOTHING;
/* blast locations of everything in list */
DOLIST(rest, first) {
db[rest].location = NOTHING;
}
while (first != NOTHING) {
rest = db[first].next;
/* if thing is ok to take then move to player else send home.
* thing is not okay to move if it's STICKY and its home is not
* the player.
*/
if (!controls(player, first) && (Sticky(first) && (Home(first) != player)))
enter_room(first, HOME);
else {
PUSH(first, Contents(player));
db[first].location = player;
}
first = rest;
}
Contents(player) = reverse(Contents(player));
enter_room(player, dest);
}
int
can_move(player, direction)
dbref player;
const char *direction;
{
#ifdef FIXED_FLAG
if (!strcasecmp(direction, "home") && !Fixed(Owner(player)))
#else
if (!strcasecmp(direction, "home"))
#endif
return 1;
/* otherwise match on exits */
return (last_match_result(player, direction, TYPE_EXIT, MAT_EXIT | MAT_ABSOLUTE) != NOTHING);
}
static dbref
find_var_dest(player, exit_obj)
dbref player;
dbref exit_obj;
{
/* This is used to evaluate the u-function DESTINATION on an exit with
* a VARIABLE (ambiguous) link.
*/
char const *abuf, *ap;
char buff[BUFFER_LEN], *bp;
ATTR *a;
dbref dest_room;
a = atr_get(exit_obj, "DESTINATION");
if (!a)
return NOTHING;
abuf = safe_uncompress(a->value);
if (!abuf)
return NOTHING;
if (!*abuf) {
free((Malloc_t) abuf);
return NOTHING;
}
ap = abuf;
bp = buff;
process_expression(buff, &bp, &ap, exit_obj, player, player,
PE_DEFAULT, PT_DEFAULT, NULL);
*bp = '\0';
dest_room = parse_dbref(buff);
free((Malloc_t) abuf);
return (dest_room);
}
void
do_move(player, direction, type)
dbref player;
const char *direction;
int type;
/* type 0 is normal, type 1 is global */
{
dbref exit_m, loc, var_dest;
if (!strcasecmp(direction, "home")) {
/* send him home */
/* but steal all his possessions */
if ((loc = Location(player)) != NOTHING && !Dark(player) && !Dark(loc)) {
/* tell everybody else */
notify_except(Contents(loc), player,
tprintf("%s goes home.", db[player].name));
}
/* give the player the messages */
notify(player, "There's no place like home...");
notify(player, "There's no place like home...");
notify(player, "There's no place like home...");
safe_tel(player, HOME);
} else {
/* find the exit */
if (DO_GLOBALS && (type == 1))
exit_m = match_result(player, direction, TYPE_EXIT, MAT_EXIT | MAT_GLOBAL | MAT_CHECK_KEYS | MAT_ABSOLUTE);
else if (DO_GLOBALS && (type == 2))
exit_m = match_result(player, direction, TYPE_EXIT, MAT_EXIT | MAT_REMOTES | MAT_CHECK_KEYS | MAT_ABSOLUTE);
else
exit_m = match_result(player, direction, TYPE_EXIT, MAT_EXIT | MAT_CHECK_KEYS | MAT_ABSOLUTE);
if (!Hasprivs(player) && GoodObject(exit_m) && is_dbref(direction) && (Source(exit_m) != Location(player)))
exit_m = NOTHING;
switch (exit_m) {
case NOTHING:
/* try to force the object */
notify(player, "You can't go that way.");
break;
case AMBIGUOUS:
notify(player, "I don't know which way you mean!");
break;
default:
/* we got one */
/* check to see if we got through */
if (could_doit(player, exit_m)) {
switch (Location(exit_m)) {
case HOME:
var_dest = Exits(player);
break;
case AMBIGUOUS:
var_dest = find_var_dest(player, exit_m);
break;
default:
var_dest = Location(exit_m);
}
did_it(player, exit_m, "SUCCESS", NULL, "OSUCCESS", NULL,
"ASUCCESS", NOTHING);
did_it(player, exit_m, "DROP", NULL, "ODROP", NULL, "ADROP",
var_dest);
/* This comes from Talek's patch. Looks to me like it interferes with
* normal operation of unlinked exits, though? -- Amby.
*/
/* It doesn't, because unlinked exits were caught in could_doit(),
* 25 lines back. Reinstating to prevent core dumps. - Talek
*/
if (!GoodObject(var_dest)) {
fprintf(stderr,
"Exit #%d destination became %d during move.\n",
exit_m, var_dest);
notify(player, "Exit destination is invalid.");
return;
}
switch (Typeof(var_dest)) {
case TYPE_ROOM:
enter_room(player, var_dest);
break;
case TYPE_PLAYER:
case TYPE_THING:
if (Destroyed(var_dest)) {
notify(player, "You can't go that way.");
return;
}
if (Location(var_dest) == NOTHING)
return;
safe_tel(player, var_dest);
break;
case TYPE_EXIT:
notify(player, "This feature coming soon.");
break;
}
} else
did_it(player, exit_m, "FAILURE", "You can't go that way.",
"OFAILURE", NULL, "AFAILURE", NOTHING);
break;
}
}
}
void
do_firstexit(player, what)
dbref player;
const char *what;
{
dbref thing;
dbref loc;
if ((thing = noisy_match_result(player, what, TYPE_EXIT, MAT_EXIT)) == NOTHING)
return;
loc = Home(thing);
if (!controls(player, loc)) {
notify(player, "You cannot modify exits in that room.");
return;
}
Exits(loc) = remove_first(Exits(loc), thing);
Home(thing) = loc;
PUSH(thing, Exits(loc));
notify(player, tprintf("%s is now the first exit.", Name(thing)));
}
void
do_get(player, what)
dbref player;
const char *what;
{
dbref loc = Location(player);
dbref thing;
char tbuf1[BUFFER_LEN];
long match_flags = MAT_NEIGHBOR | MAT_ABSOLUTE | MAT_CHECK_KEYS;
if ((Typeof(loc) != TYPE_ROOM) && !(db[loc].flags & ENTER_OK) &&
!controls(player, loc)) {
notify(player, "Permission denied.");
return;
}
if (!Long_Fingers(player))
match_flags |= MAT_CONTROL;
if (match_result(player, what, TYPE_THING, match_flags) == NOTHING) {
if (POSSESSIVE_GET) {
/* take care of possessive get (stealing) */
thing = parse_match_possessive(player, what);
if (!GoodObject(thing)) {
notify(player, "I don't see that here.");
return;
}
/* to steal something, you have to be able to get it, and the
* object must be ENTER_OK and not locked against you.
*/
if (could_doit(player, thing) &&
(POSSGET_ON_DISCONNECTED ||
((Typeof(Location(thing)) != TYPE_PLAYER) ||
Connected(Location(thing)))) &&
(controls(player, thing) ||
((Flags(Location(thing)) & ENTER_OK) &&
eval_lock(player, Location(thing), Enter_Lock)
))) {
notify(Location(thing), tprintf("%s was taken from you.", Name(thing)));
moveto(thing, player);
notify(thing, "Taken.");
sprintf(tbuf1, "takes %s.", db[thing].name);
did_it(player, thing, "SUCCESS", "Taken.", "OSUCCESS", tbuf1, "ASUCCESS",
NOTHING);
} else
did_it(player, thing, "FAILURE", "You can't take that from there.",
"OFAILURE", NULL, "AFAILURE", NOTHING);
} else {
notify(player, "I don't see that here.");
}
return;
} else {
if ((thing = noisy_match_result(player, what, TYPE_THING, match_flags)) != NOTHING) {
if (db[thing].location == player) {
notify(player, "You already have that!");
return;
}
switch (Typeof(thing)) {
case TYPE_PLAYER:
case TYPE_THING:
if (thing == player) {
notify(player, "You cannot get yourself!");
return;
}
if (could_doit(player, thing)) {
moveto(thing, player);
notify(thing, "Taken.");
sprintf(tbuf1, "takes %s.", db[thing].name);
did_it(player, thing, "SUCCESS", "Taken.", "OSUCCESS", tbuf1,
"ASUCCESS", NOTHING);
} else
did_it(player, thing, "FAILURE", "You can't pick that up.",
"OFAILURE", NULL, "AFAILURE", NOTHING);
break;
case TYPE_EXIT:
notify(player, "You can't pick up exits.");
return;
default:
notify(player, "You can't take that!");
break;
}
}
}
}
void
do_drop(player, name)
dbref player;
const char *name;
{
dbref loc;
dbref thing;
char tbuf1[BUFFER_LEN];
if ((loc = Location(player)) == NOTHING)
return;
switch (thing = match_result(player, name, TYPE_THING, MAT_POSSESSION | MAT_ABSOLUTE | MAT_CONTROL)) {
case NOTHING:
notify(player, "You don't have that!");
return;
case AMBIGUOUS:
notify(player, "I don't know which you mean!");
return;
default:
if (db[thing].location != player) {
/* Shouldn't ever happen. */
notify(player, "You can't drop that.");
return;
} else if (Typeof(thing) == TYPE_EXIT) {
notify(player, "Sorry you can't drop exits.");
return;
#ifdef DROP_LOCK
} else if (!eval_lock(player, thing, Drop_Lock)) {
notify(player, "You can't seem to get rid of that.");
return;
#endif
#ifdef DROP_LOCK
} else if ((Typeof(loc) == TYPE_ROOM) &&
!eval_lock(player, loc, Drop_Lock)) {
notify(player, "You can't seem to drop things here.");
return;
#endif
#ifdef DROP_LOCK
} else if ((Typeof(loc) == TYPE_ROOM) &&
!eval_lock(player, loc, Drop_Lock)) {
notify(player, "You can't seem to drop things here.");
return;
#endif
} else if (Sticky(thing)
#ifdef FIXED_FLAG
&& !Fixed(Owner(thing))
#endif
) {
notify(thing, "Dropped.");
safe_tel(thing, HOME);
} else if ((Location(loc) != NOTHING) && (Typeof(loc) == TYPE_ROOM) &&
!Sticky(loc)) {
/* location has immediate dropto */
notify(thing, "Dropped.");
moveto(thing, db[loc].location);
} else {
notify(thing, "Dropped.");
moveto(thing, loc);
}
break;
}
sprintf(tbuf1, "drops %s.", db[thing].name);
did_it(player, thing, "DROP", "Dropped.", "ODROP", tbuf1, "ADROP", NOTHING);
}
void
do_enter(player, what, is_alias)
dbref player;
const char *what;
int is_alias;
/* 1 if we got here via enter alias */
{
dbref thing;
long match_flags = MAT_CHECK_KEYS | MAT_NEIGHBOR | MAT_EXIT;
if (is_alias || Hasprivs(player))
match_flags |= MAT_ABSOLUTE; /* necessary for enter aliases to work */
if ((thing = noisy_match_result(player, what, TYPE_THING, match_flags)) == NOTHING) {
/* notify(player,"I don't see that here."); */
return;
}
switch (Typeof(thing)) {
case TYPE_ROOM:
case TYPE_EXIT:
notify(player, "Permission denied.");
break;
default:
/* the object must pass the lock. Also, the thing being entered */
/* has to be controlled, or must be enter_ok */
if (!(((Flags(thing) & ENTER_OK) || controls(player, thing)) &&
(eval_lock(player, thing, Enter_Lock))
)) {
did_it(player, thing, "EFAIL", "Permission denied.", "OEFAIL",
NULL, "AEFAIL", NOTHING);
return;
}
if (thing == player) {
notify(player, "Sorry, you must remain beside yourself!");
return;
}
safe_tel(player, thing);
break;
}
}
void
do_leave(player)
dbref player;
{
if ((Typeof(Location(player)) == TYPE_ROOM) ||
(IS(Location(player), TYPE_THING, THING_NOLEAVE)
#ifdef LEAVE_LOCK
|| !eval_lock(player, Location(player), Leave_Lock)
#endif
)) {
did_it(player, Location(player), "LFAIL", "You can't leave.", "OLFAIL",
NULL, "ALFAIL", NOTHING);
return;
}
enter_room(player, db[Location(player)].location);
}
dbref
global_exit(player, direction)
dbref player;
const char *direction;
{
return (last_match_result(player, direction, TYPE_EXIT, MAT_GLOBAL | MAT_EXIT) != NOTHING);
}
dbref
remote_exit(player, direction)
dbref player;
const char *direction;
{
return (last_match_result(player, direction, TYPE_EXIT, MAT_REMOTES | MAT_EXIT) != NOTHING);
}
void
move_wrapper(player, command)
dbref player;
const char *command;
{
/* check local exit, then zone exit, then global. If nothing is
* matched, treat it as local so player will get an error message.
*/
if (!Mobile(player))
return;
#ifdef FIXED_FLAG
if (!strcasecmp(command, "home") && Fixed(Owner(player))) {
notify(player, "You can't do that IC!");
return;
}
#endif
if (DO_GLOBALS) {
if (can_move(player, command))
do_move(player, command, 0);
else if ((Zone(Location(player)) != NOTHING) &&
remote_exit(player, command))
do_move(player, command, 2);
else if ((Location(player) != MASTER_ROOM) &&
global_exit(player, command))
do_move(player, command, 1);
else
do_move(player, command, 0);
} else {
do_move(player, command, 0);
}
}