/* $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 "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);
}
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
&& random() % 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.
*
*/
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 */