/* $Header: /cvsroot/fbmuck/fbmuck/src/move.c,v 1.26 2003/09/06 22:28:58 revar Exp $ */
#include "copyright.h"
#include "config.h"
#include "db.h"
#include "props.h"
#include "params.h"
#include "tune.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
#include "fb.h"
void
moveto(dbref what, dbref where)
{
dbref loc;
/* do NOT move garbage */
if (what != NOTHING && Typeof(what) == TYPE_GARBAGE) {
return;
}
/* remove what from old loc */
if ((loc = DBFETCH(what)->location) != NOTHING) {
DBSTORE(loc, contents, remove_first(DBFETCH(loc)->contents, what));
}
/* test for special cases */
switch (where) {
case NOTHING:
DBSTORE(what, location, NOTHING);
return; /* NOTHING doesn't have contents */
case HOME:
switch (Typeof(what)) {
case TYPE_PLAYER:
where = PLAYER_HOME(what);
break;
case TYPE_THING:
where = THING_HOME(what);
if (parent_loop_check(what, where)) {
where = PLAYER_HOME(OWNER(what));
if (parent_loop_check(what, where))
where = (dbref) 0;
}
break;
case TYPE_ROOM:
where = GLOBAL_ENVIRONMENT;
break;
case TYPE_PROGRAM:
where = OWNER(what);
break;
}
break;
default:
if (parent_loop_check(what, where)) {
switch (Typeof(what)) {
case TYPE_PLAYER:
where = PLAYER_HOME(what);
break;
case TYPE_THING:
where = THING_HOME(what);
if (parent_loop_check(what, where)) {
where = PLAYER_HOME(OWNER(what));
if (parent_loop_check(what, where))
where = (dbref) 0;
}
break;
case TYPE_ROOM:
where = GLOBAL_ENVIRONMENT;
break;
case TYPE_PROGRAM:
where = OWNER(what);
break;
}
}
}
/* now put what in where */
PUSH(what, DBFETCH(where)->contents);
DBDIRTY(where);
DBSTORE(what, location, where);
}
dbref reverse(dbref);
void
send_contents(int descr, dbref loc, dbref dest)
{
dbref first;
dbref rest;
dbref where;
first = DBFETCH(loc)->contents;
DBSTORE(loc, contents, NOTHING);
/* blast locations of everything in list */
DOLIST(rest, first) {
DBSTORE(rest, location, NOTHING);
}
while (first != NOTHING) {
rest = DBFETCH(first)->next;
if ((Typeof(first) != TYPE_THING)
&& (Typeof(first) != TYPE_PROGRAM)) {
moveto(first, loc);
} else {
where = FLAGS(first) & STICKY ? HOME : dest;
if (tp_thing_movement && (Typeof(first) == TYPE_THING)) {
enter_room(descr, first,
parent_loop_check(first, where) ? loc : where,
DBFETCH(first)->location);
} else {
moveto(first, parent_loop_check(first, where) ? loc : where);
}
}
first = rest;
}
DBSTORE(loc, contents, reverse(DBFETCH(loc)->contents));
}
void
maybe_dropto(int descr, dbref loc, dbref dropto)
{
dbref thing;
if (loc == dropto)
return; /* bizarre special case */
/* check for players */
DOLIST(thing, DBFETCH(loc)->contents) {
if (Typeof(thing) == TYPE_PLAYER)
return;
}
/* no players, send everything to the dropto */
send_contents(descr, loc, dropto);
}
/* What are we doing here? Quick explanation - we want to prevent
environment loops from happening. Any item should always be able
to 'find' its way to room #0. Since the loop check is recursive,
we also put in a max iteration check, to keep people from creating
huge envchains in order to bring the server down. We have a loop
if we:
a) Try to parent to ourselves.
b) Parent to nothing (not really a loop, but won't get you to #0).
c) Parent to our own home (not a valid destination).
d) Find our source room down the environment chain.
Note: This system will only work if every step _up_ to this point has
resulted in a consistent (ie: no loops) environment.
*/
int
location_loop_check(dbref source, dbref dest)
{
unsigned int level = 0;
unsigned int place = 0;
dbref pstack[MAX_PARENT_DEPTH+2];
if (source == dest) {
return 1;
}
pstack[0] = source;
pstack[1] = dest;
while (level < MAX_PARENT_DEPTH) {
dest = getloc(dest);
if (dest == NOTHING) {
return 0;
}
if (dest == HOME) { /* We should never get this, either. */
return 1;
}
if (dest == (dbref) 0) { /* Reached the top of the chain. */
return 0;
}
/* Check to see if we've found this item before.. */
for (place = 0; place < (level+2); place++) {
if (pstack[place] == dest) {
return 1;
}
}
pstack[level+2] = dest;
level++;
}
return 1;
}
int
parent_loop_check(dbref source, dbref dest)
{
unsigned int level = 0;
unsigned int place = 0;
dbref pstack[MAX_PARENT_DEPTH+2];
if (dest == HOME) {
switch(Typeof(source)) {
case TYPE_PLAYER:
dest = PLAYER_HOME(source);
break;
case TYPE_THING:
dest = THING_HOME(source);
break;
case TYPE_ROOM:
dest = GLOBAL_ENVIRONMENT;
break;
case TYPE_PROGRAM:
dest = OWNER(source);
break;
default:
return 1;
}
}
if (location_loop_check(source, dest)) {
return 1;
}
if (source == dest) {
return 1;
}
pstack[0] = source;
pstack[1] = dest;
while (level < MAX_PARENT_DEPTH) {
/* if (Typeof(dest) == TYPE_THING) {
dest = THING_HOME(dest);
} */
dest = getparent(dest);
if (dest == NOTHING) {
return 0;
}
if (dest == HOME) { /* We should never get this, either. */
return 1;
}
if (dest == (dbref) 0) { /* Reached the top of the chain. */
return 0;
}
/* Check to see if we've found this item before.. */
for (place = 0; place < (level+2); place++) {
if (pstack[place] == dest) {
return 1;
}
}
pstack[level+2] = dest;
level++;
}
return 1;
}
static int donelook = 0;
void
enter_room(int descr, dbref player, dbref loc, dbref exit)
{
dbref old;
dbref dropto;
char buf[BUFFER_LEN];
/* check for room == HOME */
if (loc == HOME)
loc = PLAYER_HOME(player); /* home */
/* get old location */
old = DBFETCH(player)->location;
if (parent_loop_check(player, loc)) {
switch (Typeof(player)) {
case TYPE_PLAYER:
loc = PLAYER_HOME(player);
break;
case TYPE_THING:
loc = THING_HOME(player);
if (parent_loop_check(player, loc)) {
loc = PLAYER_HOME(OWNER(player));
if (parent_loop_check(player, loc))
loc = (dbref) 0;
}
break;
case TYPE_ROOM:
loc = GLOBAL_ENVIRONMENT;
break;
case TYPE_PROGRAM:
loc = OWNER(player);
break;
}
}
/* 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) {
/* go there */
moveto(player, loc);
if (old != NOTHING) {
propqueue(descr, player, old, exit, player, NOTHING, "_depart", "Depart", 1, 1);
envpropqueue(descr, player, old, exit, old, NOTHING, "_depart", "Depart", 1, 1);
propqueue(descr, player, old, exit, player, NOTHING, "_odepart", "Odepart", 1, 0);
envpropqueue(descr, player, old, exit, old, NOTHING, "_odepart", "Odepart", 1, 0);
/* notify others unless DARK */
if (!Dark(old) && !Dark(player) &&
((Typeof(player) != TYPE_THING) ||
((Typeof(player) == TYPE_THING) && (FLAGS(player) & (ZOMBIE | VEHICLE))))
&& (Typeof(exit) != TYPE_EXIT || !Dark(exit))) {
#if !defined(QUIET_MOVES)
snprintf(buf, sizeof(buf), "%s has left.", PNAME(player));
notify_except(DBFETCH(old)->contents, player, buf, player);
#endif
}
}
/* if old location has STICKY dropto, send stuff through it */
if (old != NOTHING && Typeof(old) == TYPE_ROOM
&& (dropto = DBFETCH(old)->sp.room.dropto) != NOTHING && (FLAGS(old) & STICKY)) {
maybe_dropto(descr, old, dropto);
}
/* tell other folks in new location if not DARK */
if (!Dark(loc) && !Dark(player) &&
((Typeof(player) != TYPE_THING) ||
((Typeof(player) == TYPE_THING) && (FLAGS(player) & (ZOMBIE | VEHICLE))))
&& (Typeof(exit) != TYPE_EXIT || !Dark(exit))) {
#if !defined(QUIET_MOVES)
snprintf(buf, sizeof(buf), "%s has arrived.", PNAME(player));
notify_except(DBFETCH(loc)->contents, player, buf, player);
#endif
}
}
/* autolook */
if ((Typeof(player) != TYPE_THING) ||
((Typeof(player) == TYPE_THING) && (FLAGS(player) & (ZOMBIE | VEHICLE)))) {
if (donelook < 8) {
donelook++;
if (can_move(descr, player, tp_autolook_cmd, 1)) {
do_move(descr, player, tp_autolook_cmd, 1);
} else {
do_look_around(descr, player);
}
donelook--;
} else {
notify(player, "Look aborted because of look action loop.");
}
}
if (tp_penny_rate != 0) {
/* check for pennies */
if (!controls(player, loc)
&& PLAYER_PENNIES(player) <= tp_max_pennies && RANDOM() % tp_penny_rate == 0) {
notify_fmt(player, "You found a %s!", tp_penny);
PLAYER_ADD_PENNIES(OWNER(player), 1);
DBDIRTY(OWNER(player));
}
}
if (loc != old) {
envpropqueue(descr, player, loc, exit, player, NOTHING, "_arrive", "Arrive", 1, 1);
envpropqueue(descr, player, loc, exit, player, NOTHING, "_oarrive", "Oarrive", 1, 0);
}
}
void
send_home(int descr, dbref thing, int puppethome)
{
switch (Typeof(thing)) {
case TYPE_PLAYER:
/* send his possessions home first! */
/* that way he sees them when he arrives */
send_contents(descr, thing, HOME);
enter_room(descr, thing, PLAYER_HOME(thing), DBFETCH(thing)->location);
break;
case TYPE_THING:
if (puppethome)
send_contents(descr, thing, HOME);
if (tp_thing_movement || (FLAGS(thing) & (ZOMBIE | LISTENER))) {
enter_room(descr, thing, PLAYER_HOME(thing), DBFETCH(thing)->location);
break;
}
moveto(thing, HOME); /* home */
break;
case TYPE_PROGRAM:
moveto(thing, OWNER(thing));
break;
default:
/* no effect */
break;
}
return;
}
int
can_move(int descr, dbref player, const char *direction, int lev)
{
struct match_data md;
if (tp_allow_home && !string_compare(direction, "home"))
return 1;
/* otherwise match on exits */
init_match(descr, player, direction, TYPE_EXIT, &md);
md.match_level = lev;
match_all_exits(&md);
return (last_match_result(&md) != NOTHING);
}
/*
* trigger()
*
* This procedure 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(int descr, dbref player, dbref exit, int pflag)
{
int i;
dbref dest;
int sobjact; /* sticky object action flag, sends home
* source obj */
int succ;
struct frame *tmpfr;
sobjact = 0;
succ = 0;
for (i = 0; i < DBFETCH(exit)->sp.exit.ndest; i++) {
dest = (DBFETCH(exit)->sp.exit.dest)[i];
if (dest == HOME)
dest = PLAYER_HOME(player);
switch (Typeof(dest)) {
case TYPE_ROOM:
if (pflag) {
if (parent_loop_check(player, dest)) {
notify(player, "That would cause a paradox.");
break;
}
if (!Wizard(OWNER(player)) && Typeof(player) == TYPE_THING
&& (FLAGS(dest) & ZOMBIE)) {
notify(player, "You can't go that way.");
break;
}
if ((FLAGS(player) & VEHICLE) && ((FLAGS(dest) | FLAGS(exit)) & VEHICLE)) {
notify(player, "You can't go that way.");
break;
}
if (GETDROP(exit))
exec_or_notify_prop(descr, player, exit, MESGPROP_DROP, "(@Drop)");
if (GETODROP(exit) && !Dark(player)) {
parse_oprop(descr, player, dest, exit, MESGPROP_ODROP,
PNAME(player), "(@Odrop)");
}
enter_room(descr, player, dest, exit);
succ = 1;
}
break;
case TYPE_THING:
if (dest == getloc(exit) && (FLAGS(dest) & VEHICLE)) {
if (pflag) {
if (parent_loop_check(player, dest)) {
notify(player, "That would cause a paradox.");
break;
}
if (GETDROP(exit))
exec_or_notify_prop(descr, player, exit, MESGPROP_DROP, "(@Drop)");
if (GETODROP(exit) && !Dark(player)) {
parse_oprop(descr, player, dest, exit, MESGPROP_ODROP,
PNAME(player), "(@Odrop)");
}
enter_room(descr, player, dest, exit);
succ = 1;
}
} else {
if (Typeof(DBFETCH(exit)->location) == TYPE_THING) {
if (parent_loop_check(dest, getloc(getloc(exit)))) {
notify(player, "That would cause a paradox.");
break;
}
if (tp_thing_movement) {
enter_room(descr, dest, DBFETCH(DBFETCH(exit)->location)->location,
exit);
} else {
moveto(dest, DBFETCH(DBFETCH(exit)->location)->location);
}
if (!(FLAGS(exit) & STICKY)) {
/* send home source object */
sobjact = 1;
}
} else {
if (parent_loop_check(dest, getloc(exit))) {
notify(player, "That would cause a paradox.");
break;
}
if (tp_thing_movement) {
enter_room(descr, dest, DBFETCH(exit)->location, exit);
} else {
moveto(dest, DBFETCH(exit)->location);
}
}
if (GETSUCC(exit))
succ = 1;
}
break;
case TYPE_EXIT: /* It's a meta-link(tm)! */
ts_useobject(dest);
trigger(descr, player, (DBFETCH(exit)->sp.exit.dest)[i], 0);
if (GETSUCC(exit))
succ = 1;
break;
case TYPE_PLAYER:
if (pflag && DBFETCH(dest)->location != NOTHING) {
if (parent_loop_check(player, dest)) {
notify(player, "That would cause a paradox.");
break;
}
succ = 1;
if (FLAGS(dest) & JUMP_OK) {
if (GETDROP(exit)) {
exec_or_notify_prop(descr, player, exit, MESGPROP_DROP, "(@Drop)");
}
if (GETODROP(exit) && !Dark(player)) {
parse_oprop(descr, player, getloc(dest), exit,
MESGPROP_ODROP, PNAME(player), "(@Odrop)");
}
enter_room(descr, player, DBFETCH(dest)->location, exit);
} else {
notify(player, "That player does not wish to be disturbed.");
}
}
break;
case TYPE_PROGRAM:
tmpfr = interp(descr, player, DBFETCH(player)->location, dest, exit,
FOREGROUND, STD_REGUID, 0);
if (tmpfr) {
interp_loop(player, dest, tmpfr, 0);
}
return;
}
}
if (sobjact)
send_home(descr, DBFETCH(exit)->location, 0);
if (!succ && pflag)
notify(player, "Done.");
}
void
do_move(int descr, dbref player, const char *direction, int lev)
{
dbref exit;
dbref loc;
char buf[BUFFER_LEN];
struct match_data md;
if (tp_allow_home && !string_compare(direction, "home")) {
/* send him home */
/* but steal all his possessions */
if ((loc = DBFETCH(player)->location) != NOTHING) {
/* tell everybody else */
snprintf(buf, sizeof(buf), "%s goes home.", PNAME(player));
notify_except(DBFETCH(loc)->contents, player, buf, player);
}
/* 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(descr, player, 1);
} else {
/* find the exit */
init_match_check_keys(descr, player, direction, TYPE_EXIT, &md);
md.match_level = lev;
match_all_exits(&md);
switch (exit = match_result(&md)) {
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 */
ts_useobject(exit);
loc = DBFETCH(player)->location;
if (can_doit(descr, player, exit, "You can't go that way.")) {
trigger(descr, player, exit, 1);
}
break;
}
}
}
void
do_leave(int descr, dbref player)
{
dbref loc, dest;
loc = DBFETCH(player)->location;
if (loc == NOTHING || Typeof(loc) == TYPE_ROOM) {
notify(player, "You can't go that way.");
return;
}
if (!(FLAGS(loc) & VEHICLE)) {
notify(player, "You can only exit vehicles.");
return;
}
dest = DBFETCH(loc)->location;
if (Typeof(dest) != TYPE_ROOM && Typeof(dest) != TYPE_THING) {
notify(player, "You can't exit a vehicle inside of a player.");
return;
}
/*
* if (Typeof(dest) == TYPE_ROOM && !controls(player, dest)
* && !(FLAGS(dest) | JUMP_OK)) {
* notify(player, "You can't go that way.");
* return;
* }
*/
if (parent_loop_check(player, dest)) {
notify(player, "You can't go that way.");
return;
}
notify(player, "You exit the vehicle.");
enter_room(descr, player, dest, loc);
}
void
do_get(int descr, dbref player, const char *what, const char *obj)
{
dbref thing, cont;
int cando;
struct match_data md;
init_match_check_keys(descr, player, what, TYPE_THING, &md);
match_neighbor(&md);
match_possession(&md);
if (Wizard(OWNER(player)))
match_absolute(&md); /* the wizard has long fingers */
if ((thing = noisy_match_result(&md)) != NOTHING) {
cont = thing;
if (obj && *obj) {
init_match_check_keys(descr, player, obj, TYPE_THING, &md);
match_rmatch(cont, &md);
if (Wizard(OWNER(player)))
match_absolute(&md); /* the wizard has long fingers */
if ((thing = noisy_match_result(&md)) == NOTHING) {
return;
}
if (Typeof(cont) == TYPE_PLAYER) {
notify(player, "You can't steal things from players.");
return;
}
if (!test_lock_false_default(descr, player, cont, "_/clk")) {
notify(player, "You can't open that container.");
return;
}
}
if (Typeof(player) != TYPE_PLAYER) {
if (Typeof(DBFETCH(thing)->location) != TYPE_ROOM) {
if (OWNER(player) != OWNER(thing)) {
notify(player, "Zombies aren't allowed to be thieves!");
return;
}
}
}
if (DBFETCH(thing)->location == player) {
notify(player, "You already have that!");
return;
}
if (Typeof(cont) == TYPE_PLAYER) {
notify(player, "You can't steal stuff from players.");
return;
}
if (parent_loop_check(thing, player)) {
notify(player, "You can't pick yourself up by your bootstraps!");
return;
}
switch (Typeof(thing)) {
case TYPE_THING:
ts_useobject(thing);
case TYPE_PROGRAM:
if (obj && *obj) {
cando = could_doit(descr, player, thing);
if (!cando)
notify(player, "You can't get that.");
} else {
cando = can_doit(descr, player, thing, "You can't pick that up.");
}
if (cando) {
if (tp_thing_movement && (Typeof(thing) == TYPE_THING)) {
enter_room(descr, thing, player, DBFETCH(thing)->location);
} else {
moveto(thing, player);
}
notify(player, "Taken.");
}
break;
default:
notify(player, "You can't take that!");
break;
}
}
}
void
do_drop(int descr, dbref player, const char *name, const char *obj)
{
dbref loc, cont;
dbref thing;
char buf[BUFFER_LEN];
struct match_data md;
if ((loc = getloc(player)) == NOTHING)
return;
init_match(descr, player, name, NOTYPE, &md);
match_possession(&md);
if ((thing = noisy_match_result(&md)) == NOTHING || thing == AMBIGUOUS)
return;
cont = loc;
if (obj && *obj) {
init_match(descr, player, obj, NOTYPE, &md);
match_possession(&md);
match_neighbor(&md);
if (Wizard(OWNER(player)))
match_absolute(&md); /* the wizard has long fingers */
if ((cont = noisy_match_result(&md)) == NOTHING || thing == AMBIGUOUS) {
return;
}
}
switch (Typeof(thing)) {
case TYPE_THING:
ts_useobject(thing);
case TYPE_PROGRAM:
if (DBFETCH(thing)->location != player) {
/* Shouldn't ever happen. */
notify(player, "You can't drop that.");
break;
}
if (Typeof(cont) != TYPE_ROOM && Typeof(cont) != TYPE_PLAYER &&
Typeof(cont) != TYPE_THING) {
notify(player, "You can't put anything in that.");
break;
}
if (Typeof(cont) != TYPE_ROOM &&
!test_lock_false_default(descr, player, cont, "_/clk")) {
notify(player, "You don't have permission to put something in that.");
break;
}
if (parent_loop_check(thing, cont)) {
notify(player, "You can't put something inside of itself.");
break;
}
if (Typeof(cont) == TYPE_ROOM && (FLAGS(thing) & STICKY) &&
Typeof(thing) == TYPE_THING) {
send_home(descr, thing, 0);
} else {
int immediate_dropto = (Typeof(cont) == TYPE_ROOM &&
DBFETCH(cont)->sp.room.dropto != NOTHING
&& !(FLAGS(cont) & STICKY));
if (tp_thing_movement && (Typeof(thing) == TYPE_THING)) {
enter_room(descr, thing,
immediate_dropto ? DBFETCH(cont)->sp.room.dropto : cont, player);
} else {
moveto(thing, immediate_dropto ? DBFETCH(cont)->sp.room.dropto : cont);
}
}
if (Typeof(cont) == TYPE_THING) {
notify(player, "Put away.");
return;
} else if (Typeof(cont) == TYPE_PLAYER) {
notify_fmt(cont, "%s hands you %s", PNAME(player), PNAME(thing));
notify_fmt(player, "You hand %s to %s", PNAME(thing), PNAME(cont));
return;
}
if (GETDROP(thing))
exec_or_notify_prop(descr, player, thing, MESGPROP_DROP, "(@Drop)");
else
notify(player, "Dropped.");
if (GETDROP(loc))
exec_or_notify_prop(descr, player, loc, MESGPROP_DROP, "(@Drop)");
if (GETODROP(thing)) {
parse_oprop(descr, player, loc, thing, MESGPROP_ODROP,
PNAME(player), "(@Odrop)");
} else {
snprintf(buf, sizeof(buf), "%s drops %s.", PNAME(player), PNAME(thing));
notify_except(DBFETCH(loc)->contents, player, buf, player);
}
if (GETODROP(loc)) {
parse_oprop(descr, player, loc, loc, MESGPROP_ODROP, PNAME(thing), "(@Odrop)");
}
break;
default:
notify(player, "You can't drop that.");
break;
}
}
void
do_recycle(int descr, dbref player, const char *name)
{
dbref thing;
char buf[BUFFER_LEN];
struct match_data md;
init_match(descr, player, name, TYPE_THING, &md);
match_all_exits(&md);
match_neighbor(&md);
match_possession(&md);
match_registered(&md);
match_here(&md);
match_absolute(&md);
if ((thing = noisy_match_result(&md)) != NOTHING) {
if (!controls(player, thing)) {
if(Wizard(OWNER(player)) && (Typeof(thing) == TYPE_GARBAGE))
notify(player, "That's already garbage!");
else
notify(player, "Permission denied.");
} else {
switch (Typeof(thing)) {
case TYPE_ROOM:
if (OWNER(thing) != OWNER(player)) {
notify(player, "Permission denied.");
return;
}
if (thing == tp_player_start || thing == GLOBAL_ENVIRONMENT) {
notify(player, "This room may not be recycled (is either player start or the global environment).");
return;
}
break;
case TYPE_THING:
if (OWNER(thing) != OWNER(player)) {
notify(player, "Permission denied.");
return;
}
if (thing == player) {
snprintf(buf, sizeof(buf),
"%.512s's owner commands it to kill itself. It blinks a few times in shock, and says, \"But.. but.. WHY?\" It suddenly clutches it's heart, grimacing with pain.. Staggers a few steps before falling to it's knees, then plops down on it's face. *thud* It kicks it's legs a few times, with weakening force, as it suffers a seizure. It's color slowly starts changing to purple, before it explodes with a fatal *POOF*!",
PNAME(thing));
notify_except(DBFETCH(getloc(thing))->contents, thing, buf, player);
notify(OWNER(player), buf);
notify(OWNER(player), "Now don't you feel guilty?");
}
break;
case TYPE_EXIT:
if (OWNER(thing) != OWNER(player)) {
notify(player, "Permission denied.");
return;
}
if (!unset_source(player, DBFETCH(player)->location, thing)) {
notify(player, "You can't do that to an exit in another room.");
return;
}
break;
case TYPE_PLAYER:
notify(player, "You can't recycle a player!");
return;
/* NOTREACHED */
break;
case TYPE_PROGRAM:
if (OWNER(thing) != OWNER(player)) {
notify(player, "Permission denied.");
return;
}
/* FIXME: This is a workaround for bug #201633 */
if(PROGRAM_INSTANCES(thing)) {
notify(player, "Recycle failed: Program is running.");
return;
}
break;
case TYPE_GARBAGE:
notify(player, "That's already garbage!");
return;
/* NOTREACHED */
break;
}
snprintf(buf, sizeof(buf), "Thank you for recycling %.512s (#%d).", NAME(thing), thing);
recycle(descr, player, thing);
notify(player, buf);
}
}
}
void
recycle(int descr, dbref player, dbref thing)
{
extern dbref recyclable;
static int depth = 0;
dbref first;
dbref rest;
char buf[2048];
int looplimit;
depth++;
if (force_level) {
if(thing == force_prog) {
log_status("SANITYCHECK: Was about to recycle FORCEing object #%d!", thing);
notify(player, "ERROR: Cannot recycle an object FORCEing you!");
return;
}
if((Typeof(thing) == TYPE_PROGRAM) && (PROGRAM_INSTANCES(thing) != 0)) {
log_status("SANITYCHECK: Trying to recycle a running program (#%d) from FORCE!", thing);
notify(player, "ERROR: Cannot recycle a running program from FORCE.");
return;
}
}
/* dequeue any MUF or MPI events for the given object */
dequeue_prog(thing, 0);
switch (Typeof(thing)) {
case TYPE_ROOM:
if (!Wizard(OWNER(thing)))
PLAYER_ADD_PENNIES(OWNER(thing), tp_room_cost);
DBDIRTY(OWNER(thing));
for (first = DBFETCH(thing)->exits; first != NOTHING; first = rest) {
rest = DBFETCH(first)->next;
if (DBFETCH(first)->location == NOTHING || DBFETCH(first)->location == thing)
recycle(descr, player, first);
}
notify_except(DBFETCH(thing)->contents, NOTHING,
"You feel a wrenching sensation...", player);
break;
case TYPE_THING:
if (!Wizard(OWNER(thing)))
PLAYER_ADD_PENNIES(OWNER(thing), THING_VALUE(thing));
DBDIRTY(OWNER(thing));
for (first = DBFETCH(thing)->exits; first != NOTHING; first = rest) {
rest = DBFETCH(first)->next;
if (DBFETCH(first)->location == NOTHING || DBFETCH(first)->location == thing)
recycle(descr, player, first);
}
break;
case TYPE_EXIT:
if (!Wizard(OWNER(thing)))
PLAYER_ADD_PENNIES(OWNER(thing), tp_exit_cost);
if (!Wizard(OWNER(thing)))
if (DBFETCH(thing)->sp.exit.ndest != 0)
PLAYER_ADD_PENNIES(OWNER(thing), tp_link_cost);
DBDIRTY(OWNER(thing));
break;
case TYPE_PROGRAM:
snprintf(buf, sizeof(buf), "muf/%d.m", (int) thing);
unlink(buf);
break;
}
for (rest = 0; rest < db_top; rest++) {
switch (Typeof(rest)) {
case TYPE_ROOM:
if (DBFETCH(rest)->sp.room.dropto == thing) {
DBFETCH(rest)->sp.room.dropto = NOTHING;
DBDIRTY(rest);
}
if (DBFETCH(rest)->exits == thing) {
DBFETCH(rest)->exits = DBFETCH(thing)->next;
DBDIRTY(rest);
}
if (OWNER(rest) == thing) {
OWNER(rest) = GOD;
DBDIRTY(rest);
}
break;
case TYPE_THING:
if (THING_HOME(rest) == thing) {
dbref loc;
if (PLAYER_HOME(OWNER(rest)) == thing)
PLAYER_SET_HOME(OWNER(rest), tp_player_start);
loc = PLAYER_HOME(OWNER(rest));
if (parent_loop_check(rest, loc)) {
loc = OWNER(rest);
if (parent_loop_check(rest, loc)) {
loc = (dbref) 0;
}
}
THING_SET_HOME(rest, loc);
DBDIRTY(rest);
}
if (DBFETCH(rest)->exits == thing) {
DBFETCH(rest)->exits = DBFETCH(thing)->next;
DBDIRTY(rest);
}
if (OWNER(rest) == thing) {
OWNER(rest) = GOD;
DBDIRTY(rest);
}
break;
case TYPE_EXIT:
{
int i, j;
for (i = j = 0; i < DBFETCH(rest)->sp.exit.ndest; i++) {
if ((DBFETCH(rest)->sp.exit.dest)[i] != thing)
(DBFETCH(rest)->sp.exit.dest)[j++] = (DBFETCH(rest)->sp.exit.dest)[i];
}
if (j < DBFETCH(rest)->sp.exit.ndest) {
PLAYER_ADD_PENNIES(OWNER(rest), tp_link_cost);
DBDIRTY(OWNER(rest));
DBFETCH(rest)->sp.exit.ndest = j;
DBDIRTY(rest);
}
}
if (OWNER(rest) == thing) {
OWNER(rest) = GOD;
DBDIRTY(rest);
}
break;
case TYPE_PLAYER:
if (Typeof(thing) == TYPE_PROGRAM && (FLAGS(rest) & INTERACTIVE)
&& (PLAYER_CURR_PROG(rest) == thing)) {
if (FLAGS(rest) & READMODE) {
notify(rest,
"The program you were running has been recycled. Aborting program.");
} else {
free_prog_text(PROGRAM_FIRST(thing));
PROGRAM_SET_FIRST(thing, NULL);
PLAYER_SET_INSERT_MODE(rest, 0);
FLAGS(thing) &= ~INTERNAL;
FLAGS(rest) &= ~INTERACTIVE;
PLAYER_SET_CURR_PROG(rest, NOTHING);
notify(rest,
"The program you were editing has been recycled. Exiting Editor.");
}
}
if (PLAYER_HOME(rest) == thing) {
PLAYER_SET_HOME(rest, tp_player_start);
DBDIRTY(rest);
}
if (DBFETCH(rest)->exits == thing) {
DBFETCH(rest)->exits = DBFETCH(thing)->next;
DBDIRTY(rest);
}
if (PLAYER_CURR_PROG(rest) == thing)
PLAYER_SET_CURR_PROG(rest, 0);
break;
case TYPE_PROGRAM:
if (OWNER(rest) == thing) {
OWNER(rest) = GOD;
DBDIRTY(rest);
}
}
/*
*if (DBFETCH(rest)->location == thing)
* DBSTORE(rest, location, NOTHING);
*/
if (DBFETCH(rest)->contents == thing)
DBSTORE(rest, contents, DBFETCH(thing)->next);
if (DBFETCH(rest)->next == thing)
DBSTORE(rest, next, DBFETCH(thing)->next);
}
looplimit = db_top;
while ((looplimit-- > 0) && ((first = DBFETCH(thing)->contents) != NOTHING)) {
if (Typeof(first) == TYPE_PLAYER ||
(Typeof(first) == TYPE_THING &&
(FLAGS(first) & (ZOMBIE | VEHICLE) || tp_thing_movement))
) {
enter_room(descr, first, HOME, DBFETCH(thing)->location);
/* If the room is set to drag players back, there'll be no
* reasoning with it. DRAG the player out.
*/
if (DBFETCH(first)->location == thing) {
notify_fmt(player, "Escaping teleport loop! Going home.");
moveto(first, HOME);
}
} else {
moveto(first, HOME);
}
}
moveto(thing, NOTHING);
depth--;
db_free_object(thing);
db_clear_object(thing);
NAME(thing) = (char*) string_dup("<garbage>");
SETDESC(thing, "<recyclable>");
FLAGS(thing) = TYPE_GARBAGE;
DBFETCH(thing)->next = recyclable;
recyclable = thing;
DBDIRTY(thing);
}