/* $Header: move.c,v 2.0 90/05/05 12:45:38 lachesis Exp $
* $Log: move.c,v $
* Revision 2.0 90/05/05 12:45:38 lachesis
* Incorporated ABODE and HAVEN flags (remembering to convert FireFoot's
* usage of those flags to ours).
* Added Examine of objects that don't belong to you, added GOD_PRIV.
*
* Revision 1.3 90/04/24 14:46:32 lachesis
* Fixed the @odrop on rooms to links.
*
* Revision 1.2 90/04/20 14:06:47 lachesis
* Added @odrop && @drop.
*
* Revision 1.1 90/04/14 14:56:47 lachesis
* Initial revision
*
*/
#include "copyright.h"
#include "os.h"
#include "db.h"
#include "config.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
void moveto (dbref what, dbref where)
{
dbref loc;
/* remove what from old loc */
if ((loc = db[what].location) != NOTHING) {
db[loc].contents = remove_first (db[loc].contents, what);
}
/* test for special cases */
switch (where) {
case NOTHING:
db[what].location = NOTHING;
return; /* NOTHING doesn't have contents */
case HOME:
switch (Typeof (what)) {
case TYPE_PLAYER:
where = db[what].sp.player.home;
break;
case TYPE_THING:
where = db[what].sp.thing.home;
break;
}
}
/* now put what in where */
PUSH (what, db[where].contents);
db[what].location = where;
}
void send_contents (dbref loc, dbref dest)
{
dbref first;
dbref rest;
first = db[loc].contents;
db[loc].contents = NOTHING;
/* blast locations of everything in list */
DOLIST (rest, first) {
db[rest].location = NOTHING;
}
while (first != NOTHING) {
rest = db[first].next;
if (Typeof (first) != TYPE_THING) {
moveto (first, loc);
} else {
moveto (first, db[first].flags & STICKY ? HOME : dest);
}
first = rest;
}
db[loc].contents = reverse (db[loc].contents);
}
static void maybe_dropto (dbref loc, dbref dropto)
{
dbref thing;
if (loc == dropto)
return; /* bizarre special case */
/* check for players */
DOLIST (thing, db[loc].contents) {
if (Typeof (thing) == TYPE_PLAYER)
return;
}
/* no players, send everything to the dropto */
send_contents (loc, dropto);
}
void enter_room (dbref player, dbref loc)
{
dbref old;
dbref dropto;
char buf[BUFFER_LEN];
/* check for room == HOME */
if (loc == HOME)
loc = db[player].sp.player.home; /* home */
/* get old location */
old = db[player].location;
/* check for self-loop */
/* self-loops don't do move or other player notification */
/* but you still get autolook and penny check */
if (loc != old) {
if (old != NOTHING) {
/* notify others unless DARK */
if (!Dark (old) && !Dark (player)) {
sprintf (buf, "%s has left.", db[player].name);
notify_except (db[old].contents, player, buf);
}
}
/* go there */
moveto (player, loc);
/* if old location has STICKY dropto, send stuff through it */
if (old != NOTHING
&& (dropto = db[old].sp.room.dropto) != NOTHING
&& (db[old].flags & STICKY)) {
maybe_dropto (old, dropto);
}
/* tell other folks in new location if not DARK */
if (!Dark (loc) && !Dark (player)) {
sprintf (buf, "%s has arrived.", db[player].name);
notify_except (db[loc].contents, player, buf);
}
}
/* autolook */
look_room (player, loc);
/* check for pennies */
if (!controls (player, loc)
&& db[player].sp.player.pennies <= MAX_PENNIES
&& OS_RAND () % PENNY_RATE == 0) {
notify (player, "You found a cookie!");
db[player].sp.player.pennies++;
}
}
void send_home (dbref thing)
{
switch (Typeof (thing)) {
case TYPE_PLAYER:
/* send his possessions home first! */
/* that way he sees them when he arrives */
send_contents (thing, HOME);
enter_room (thing, db[thing].sp.player.home); /* home */
break;
case TYPE_THING:
moveto (thing, db[thing].sp.thing.home); /* home */
break;
default:
/* no effect */
break;
}
}
int can_move (dbref player, const char *direction)
{
if (!string_compare (direction, "home"))
return 1;
/* otherwise match on exits */
init_match (player, direction, TYPE_EXIT);
match_all_exits ();
return (last_match_result () != NOTHING);
}
/*
* trigger()
*
* This procudure triggers a series of actions, or meta-actions
* which are contained in the 'dest' field of the exit.
* Locks other than the first one are over-ridden.
*
* `player' is the player who triggered the exit
* `exit' is the exit triggered
* `pflag' is a flag which indicates whether player and room exits
* are to be used (non-zero) or ignored (zero). Note that
* player/room destinations triggered via a meta-link are
* ignored.
*
*/
static void trigger (dbref player, dbref exit, int pflag)
{
int i;
dbref dest;
int sobjact; /* sticky object action flag, sends home source obj */
int succ;
char buf[BUFFER_LEN];
sobjact = 0;
succ = 0;
for (i = 0; i < db[exit].sp.exit.ndest; i++) {
dest = (db[exit].sp.exit.dest)[i];
if (dest == HOME)
dest = db[player].sp.player.home;
switch (Typeof (dest)) {
case TYPE_ROOM:
if (pflag) {
if (db[exit].drop_message)
notify (player, db[exit].drop_message);
if (db[exit].odrop) {
#ifdef GENDER
pronoun_substitute (buf, player, db[exit].odrop, 1);
#else /* GENDER */
sprintf (buf, "%s %s", db[player].name, db[exit].odrop);
#endif /* GENDER */
notify_except (db[dest].contents, player, buf);
}
enter_room (player, dest);
succ = 1;
}
break;
case TYPE_THING:
if (Typeof (db[exit].location) == TYPE_THING) {
moveto (dest, db[db[exit].location].location);
if (!(db[exit].flags & STICKY)) {
/* send home source object */
sobjact = 1;
}
} else {
moveto (dest, db[exit].location);
}
if (db[exit].succ_message)
succ = 1;
break;
case TYPE_EXIT: /* It's a meta-link(tm)! */
trigger (player, (db[exit].sp.exit.dest)[i], 0);
if (db[exit].succ_message)
succ = 1;
break;
case TYPE_PLAYER:
if (pflag && db[dest].location != NOTHING) {
succ = 1;
if (db[dest].flags & JUMP_OK) {
if (db[exit].drop_message)
notify (player, db[exit].drop_message);
if (db[exit].odrop) {
#ifdef GENDER
char buf[BUFFER_LEN];
pronoun_substitute (buf, player, db[exit].odrop, 1);
notify_except (db[db[player].location].contents, player, buf);
#else /* GENDER */
notify_except (db[db[player].location].contents, player,
db[exit].odrop);
#endif
}
enter_room (player, db[dest].location);
} else {
notify (player, "That player does not wish to be disturbed.");
}
}
break;
}
}
if (sobjact)
send_home (db[exit].location);
if (!succ && pflag)
notify (player, "Done.");
}
void do_move (dbref player, const char *direction)
{
dbref exit;
dbref loc;
char buf[BUFFER_LEN];
if (!string_compare (direction, "home")) {
/* send him home */
/* but steal all his possessions */
if ((loc = db[player].location) != NOTHING) {
/* tell everybody else */
sprintf (buf, "%s goes home.", db[player].name);
notify_except (db[loc].contents, player, buf);
}
/* 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...");
notify (player, "You wake up back home, without your possessions.");
send_home (player);
} else {
/* find the exit */
init_match_check_keys (player, direction, TYPE_EXIT);
match_all_exits ();
switch (exit = match_result ()) {
case NOTHING:
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 */
loc = db[player].location;
if (!(db[loc].flags & JUMP_OK)
&& (db[exit].location != NOTHING)
&& (Typeof (db[exit].location) == TYPE_PLAYER ||
Typeof (db[exit].location) == TYPE_THING)
&& db[exit].sp.exit.owner != db[loc].sp.room.owner
&& db[exit].location != loc) {
notify (player, "You can't do that here.");
} else if (can_doit (player, exit, "You can't go that way.")) {
trigger (player, exit, 1);
}
break;
}
}
}
void do_get (dbref player, const char *what)
{
dbref thing;
init_match_check_keys (player, what, TYPE_THING);
match_all_exits ();
match_neighbor ();
if (Wizard (player))
match_absolute (); /* the wizard has long fingers */
if ((thing = noisy_match_result ()) != NOTHING) {
if (db[thing].location == player) {
notify (player, "You already have that!");
return;
}
switch (Typeof (thing)) {
case TYPE_THING:
if (can_doit (player, thing, "You can't pick that up.")) {
moveto (thing, player);
notify (player, "Taken.");
}
break;
case TYPE_EXIT:
if (controls (player, thing)) {
notify (player, "Use @attach exit=me to move an exit.");
} else {
notify (player, "I don't see that here.");
}
break;
default:
notify (player, "You can't take that!");
break;
}
}
}
void do_drop (dbref player, const char *name)
{
dbref loc;
dbref thing;
char buf[BUFFER_LEN];
int reward;
if ((loc = getloc (player)) == NOTHING)
return;
init_match (player, name, TYPE_THING);
match_player_actions ();
match_possession ();
switch (thing = match_result ()) {
case NOTHING:
notify (player, "You don't have that!");
break;
case AMBIGUOUS:
notify (player, "I don't know which you mean!");
break;
default:
if (db[thing].location != player) {
/* Shouldn't ever happen. */
notify (player, "You can't drop that.");
} else if (Typeof (thing) == TYPE_EXIT) {
notify (player, "Use @attach exit=here to drop an exit.");
break;
} else if (db[loc].flags & TEMPLE) {
/* sacrifice time */
send_home (thing);
sprintf (buf,
"A kindly nurse takes %s from you and puts it out of your view.",
db[thing].name);
notify (player, buf);
sprintf (buf, "%s hands %s to the nurse.",
db[player].name, db[thing].name);
notify_except (db[loc].contents, player, buf);
/* check for reward */
if (!controls (player, thing)) {
reward = db[thing].sp.thing.value;
if (reward < 1 || db[player].sp.player.pennies > MAX_PENNIES) {
reward = 1;
} else if (reward > MAX_OBJECT_ENDOWMENT) {
reward = MAX_OBJECT_ENDOWMENT;
}
db[player].sp.player.pennies += reward;
sprintf (buf,
"The nurse gives you %d %s for returning this item.",
reward, reward == 1 ? "cookie" : "cookies");
notify (player, buf);
}
} else if (db[thing].flags & STICKY) {
send_home (thing);
if (db[thing].drop_message)
notify (player, db[thing].drop_message);
else
notify (player, "Dropped.");
if (db[loc].drop_message)
notify (player, db[loc].drop_message);
} else if (db[loc].sp.room.dropto != NOTHING && !(db[loc].flags & STICKY)) {
/* location has immediate dropto */
moveto (thing, db[loc].sp.room.dropto);
if (db[thing].drop_message)
notify (player, db[thing].drop_message);
else
notify (player, "Dropped.");
if (db[loc].drop_message)
notify (player, db[loc].drop_message);
} else {
moveto (thing, loc);
if (db[thing].drop_message)
notify (player, db[thing].drop_message);
else
notify (player, "Dropped.");
if (db[loc].drop_message)
notify (player, db[loc].drop_message);
if (db[thing].odrop) {
#ifdef GENDER
pronoun_substitute (buf, player, db[thing].odrop, 1);
#else /* GENDER */
sprintf (buf, "%s %s", db[player].name, db[thing].odrop);
#endif /* GENDER */
} else
sprintf (buf, "%s dropped %s.", db[player].name, db[thing].name);
notify_except (db[loc].contents, player, buf);
if (db[loc].odrop) {
#ifdef GENDER
pronoun_substitute (buf, thing, db[loc].odrop, 1);
#else /* !GENDER */
sprintf (buf, "%s %s", db[thing].name, db[loc].odrop);
#endif /* GENDER */
notify_except (db[loc].contents, player, buf);
}
}
break;
}
}
#ifdef RECYCLE
void recycle (dbref player, dbref thing)
{
extern dbref recyclable;
struct object *o = db + thing;
dbref first;
dbref rest;
switch (Typeof (thing)) {
case TYPE_ROOM:
for (first = db[thing].sp.room.exits; first != NOTHING; first = rest) {
rest = db[first].next;
if (db[first].location == NOTHING || db[first].location == thing)
recycle (player, first);
}
notify_except (db[thing].contents, NOTHING,
"You feel a wrenching sensation...");
break;
case TYPE_THING:
for (first = db[thing].sp.thing.actions; first != NOTHING; first = rest) {
rest = db[first].next;
if (db[first].location == NOTHING || db[first].location == thing)
recycle (player, first);
}
break;
}
for (rest = 0; rest < db_top; rest++) {
switch (Typeof (rest)) {
case TYPE_ROOM:
if (db[rest].sp.room.dropto == thing)
db[rest].sp.room.dropto = NOTHING;
if (db[rest].sp.room.exits == thing)
db[rest].sp.room.exits = db[thing].next;
if (db[rest].sp.room.owner == thing)
db[rest].sp.room.owner = GOD;
break;
case TYPE_THING:
if (db[rest].sp.thing.home == thing) {
if (db[db[rest].sp.thing.owner].sp.player.home == thing)
db[db[rest].sp.thing.owner].sp.player.home = PLAYER_START;
db[rest].sp.thing.home = db[db[rest].sp.thing.owner].sp.player.home;
}
if (db[rest].sp.thing.actions == thing)
db[rest].sp.thing.actions = db[thing].next;
if (db[rest].sp.thing.owner == thing)
db[rest].sp.thing.owner = GOD;
break;
case TYPE_EXIT:
{
int i, j;
for (i = j = 0; i < db[rest].sp.exit.ndest; i++) {
if ((db[rest].sp.exit.dest)[i] != thing)
(db[rest].sp.exit.dest)[j++] = (db[rest].sp.exit.dest)[i];
}
db[rest].sp.exit.ndest = j;
}
if (db[rest].sp.exit.owner == thing)
db[rest].sp.exit.owner = GOD;
break;
case TYPE_PLAYER:
if (db[rest].sp.player.home == thing)
db[rest].sp.player.home = PLAYER_START;
if (db[rest].sp.player.actions == thing)
db[rest].sp.player.actions = db[thing].next;
break;
}
if (db[rest].location == thing)
db[rest].location = NOTHING;
if (db[rest].contents == thing)
db[rest].contents = db[thing].next;
if (db[rest].next == thing)
db[rest].next = db[thing].next;
}
for (first = db[thing].contents; first != NOTHING; first = rest) {
rest = db[first].next;
if (Typeof (first) == TYPE_PLAYER)
enter_room (first, HOME);
else
moveto (first, HOME);
}
moveto (thing, NOTHING);
db_free_object (thing);
db_clear_object (thing);
o->name = "<garbage>";
o->description = "<recyclable>";
o->key = TRUE_BOOLEXP;
o->flags = TYPE_GARBAGE;
o->next = recyclable;
recyclable = thing;
}
void do_recycle (dbref player, const char *name)
{
dbref thing;
init_match (player, name, TYPE_THING);
match_all_exits ();
match_neighbor ();
match_possession ();
match_here ();
if (Wizard (player)) {
match_absolute ();
}
if ((thing = noisy_match_result ()) != NOTHING) {
if (!controls (player, thing)) {
notify (player, "Permission denied.");
} else {
switch (Typeof (thing)) {
case TYPE_ROOM:
if (db[thing].sp.room.owner != player) {
notify (player, "Permission denied.");
return;
}
if (thing == PLAYER_START) {
notify (player, "This room may not be recycled.");
return;
}
break;
case TYPE_THING:
if (db[thing].sp.thing.owner != player) {
notify (player, "Permission denied.");
return;
}
break;
case TYPE_EXIT:
if (db[thing].sp.exit.owner != player) {
notify (player, "Permission denied.");
return;
}
if (!unset_source (player, db[player].location, thing,
"You can't recycle an exit in another room."))
return;
break;
case TYPE_PLAYER:
notify (player, "You can't recycle a player!");
return;
case TYPE_GARBAGE:
notify (player, "That's already garbage!");
return;
}
recycle (player, thing);
notify (player, "Thank you for recycling.");
}
}
}
#endif /* RECYCLE */