/* destroy.c */
#include "copyright.h"
#include "config.h"
#ifdef DESTROY
#include <ctype.h>
#include "db.h"
#include "externs.h"
#include "globals.h"
dbref first_free = NOTHING;
#ifdef DESTROY
void do_destroy(player, name, confirm)
dbref player;
char *name;
int confirm;
{
dbref thing;
dbref loc;
int a;
init_match(player, name, NOTYPE);
match_everything();
thing = last_match_result();
if ((thing != NOTHING) &&
!controls(player, thing) &&
!((Typeof(thing) == TYPE_EXIT) &&
(controls(player,db[thing].location) ||
controls(player,db[thing].exits))) &&
!((db[thing].flags & THING_DEST_OK) &&
(Typeof(thing) == TYPE_THING))) {
notify(player, "Permission denied.");
return;
}
if (thing == PLAYER_START || thing == MASTER_ROOM) {
notify(player, "Permission denied.");
return;
}
if (thing == NOTHING)
thing = match_controlled(player, name);
if (thing <= 0) /* I hope no wizard is this stupid but just
* in case */
return;
/* what kind of thing we are destroying? */
switch (Typeof(thing)) {
case TYPE_PLAYER:
if (Typeof(player) != TYPE_PLAYER) {
notify(player, "Programs don't kill people; people kill people!");
return;
}
if (!Wizard(player) && (db[thing].owner == thing)) {
notify(player, "Sorry, no suicide allowed.");
return;
}
if (Wizard(thing) && !God(player)) {
notify(player, "Even you can't do that!");
return;
}
if(God(player) && player == thing) {
notify(player, "Sorry, God cannot destroy himself.");
return;
}
if (!confirm) {
notify(player, "You must use @nuke to destroy a player.");
return;
}
/* bye bye */
do_halt(thing, "");
fprintf(stderr, "*** PLAYER NUKED*** %s(#%d) destroyed %s(#%d)\n",
db[player].name, player, db[thing].name, thing);
notify(player, tprintf("You get your 0 %s deposit back.", MONEY));
boot_off(thing);
do_chownall(player, name, "");
#ifdef USE_MAILER
do_mail(thing, "clear", "");
#endif
delete_player(thing);
loc = db[thing].location;
if(loc != NOTHING) {
db[loc].contents = remove_first(db[loc].contents, thing);
free_object(thing);
}
break;
case TYPE_THING:
/* check to make sure there's no accidental destruction */
if (!confirm && Wizard(player) && !(db[thing].flags & THING_DEST_OK)
&& (db[thing].owner != player)) {
notify(player,
"That object does not belong to you. Use @nuke to destroy it.");
return;
}
if (!confirm && (db[thing].flags & THING_SAFE)) {
notify(player, "That object is marked SAFE. Use @nuke to destroy it.");
return;
}
if (!confirm && (db[thing].flags & WIZARD)) {
notify(player,
"That object is set WIZARD. You must use @nuke to destroy it.");
return;
}
do_halt(thing, "");
/* give player money back */
giveto(db[thing].owner, (a = OBJECT_DEPOSIT(Pennies(thing))));
#ifdef QUOTA
add_quota(db[thing].owner, 1);
#endif /* QUOTA */
if (db[thing].flags & THING_PUPPET) db[thing].flags &= ~THING_PUPPET;
if(!Quiet(thing) && !Quiet(db[thing].owner))
notify(db[thing].owner,
tprintf("You get your %d %s deposit back for %s.",
a, ((a == 1) ? MONEY : MONIES), db[thing].name));
notify(player, "Destroyed.");
loc = db[thing].location;
if(loc != NOTHING) {
db[loc].contents = remove_first(db[loc].contents, thing);
free_object(thing);
}
break;
case TYPE_ROOM:
if (db[thing].flags & GOING) {
notify(player, "No use beating a dead room.");
return;
}
/* don't let room be deleted if >2 exits */
if (((loc = db[thing].exits) != NOTHING) &&
((loc = db[loc].next) != NOTHING) &&
((loc = db[loc].next) != NOTHING)) {
notify(player,
"The room must have less than 3 exits before it can be destroyed.");
return;
}
do_halt(thing, "");
notify_except(db[thing].contents, 0,
"The room shakes and begins to crumble.");
if (player == db[thing].owner)
notify(db[thing].owner,
tprintf("You will be rewarded shortly for %s(#%d).",
db[thing].name, thing));
else
notify(player, tprintf("The wrecking ball is on its way for %s(#%d).",
db[thing].name, thing));
db[thing].flags |= GOING; /* mark for deletion but don't empty yet */
return;
case TYPE_EXIT:
/* Patched 12/1/90 by Michael Stanley */
if(db[thing].exits == NOTHING)
loc = find_entrance(thing);
else
loc = db[thing].exits;
db[loc].exits = remove_first(db[loc].exits, thing);
giveto(db[thing].owner, EXIT_COST);
#ifdef QUOTA
add_quota(db[thing].owner, 1);
#endif /* QUOTA */
do_halt(thing, "");
notify(db[thing].owner,
tprintf("You get your %d %s deposit back for %s.", EXIT_COST,
((EXIT_COST == 1) ? MONEY : MONIES), db[thing].name));
notify(player, "Destroyed.");
break;
}
do_empty(thing);
}
#endif /* DESTROY */
/* object must be empty and reference free before being freed */
void free_object(obj)
dbref obj;
{
db[obj].next = first_free;
first_free = obj;
}
/*
* unlink all dead rooms+build new free list. Must be called whenever
* database is read from disk. May also be called at other times to
* straighten out the free list.
*/
#define CHECK_REF(a) if ((((a)>-1) && (db[a].flags & GOING)) || (a>=db_top)\
|| (a<-3))
/* check for free list corruption */
#define NOT_OK(thing)\
((db[thing].location!=NOTHING) || (db[thing].owner!=GOD) ||\
((db[thing].flags & ~ACCESSED)!=(TYPE_THING | GOING)))
/* return a cleaned up object off the free list or NOTHING */
dbref free_get()
{
dbref newobj;
if (first_free == NOTHING)
return (NOTHING);
newobj = first_free;
first_free = db[first_free].next;
/* Make sure this object really should be in free list */
/* NOTE: A little problem here, a wiz can @teleport an object */
/* out of the free list, when that object comes up for recycling */
/* it will be caught here and the free list will be rebuilt. */
if (NOT_OK(newobj)) {
static nrecur = 0;
if (nrecur++ == 20) {
first_free = NOTHING;
report();
fprintf(stderr, "ERROR: Removed free list and continued\n");
return (NOTHING);
}
report();
fprintf(stderr, "ERROR: Object #%d should not free\n", newobj);
fprintf(stderr, "ERORR: Corrupt free list, fixing\n");
FIX;
nrecur--;
return (free_get());
}
/* free object name */
SET(db[newobj].name, NULL);
return (newobj);
}
void fix_free_list()
{
dbref thing;
void dbmark();
void dbunmark();
first_free = NOTHING;
/* destroy all rooms+make sure everything else is really dead */
for (thing = 0; thing < db_top; thing++)
if (db[thing].flags & GOING) {
if (Typeof(thing) == TYPE_ROOM)
do_empty(thing);
else
/*
* if something other than room, make sure it is located in
* NOTHING. Otherwise undelete it. Needed in case @tel is
* used on an object.
*/
if (NOT_OK(thing))
db[thing].flags &= ~GOING;
}
first_free = NOTHING;
/* check for references to destroyed objects */
for (thing = 0; thing < db_top; thing++)
/* if object is alive make sure it doesn't refer to any dead objects */
if (!(db[thing].flags & GOING)) {
/* test object home */
CHECK_REF(db[thing].exits)
switch (Typeof(thing)) {
case TYPE_PLAYER:
case TYPE_THING:
db[thing].exits = PLAYER_START; /* set home to limbo */
break;
case TYPE_ROOM: /* yuck probably corrupted set to nothing */
{
fprintf(stderr, "ERROR: Dead exit in exit list for room #%d\n",
thing);
report();
db[thing].exits = NOTHING;
}
}
CHECK_REF(db[thing].location)
switch (Typeof(thing)) {
case TYPE_PLAYER: /*
* this case shouldn't happen but just
* in case...
*/
case TYPE_THING:
moveit(thing,PLAYER_START);
break;
case TYPE_EXIT: /* Make exits destination limbo */
db[thing].location = 0;
SET(db[thing].name, "limbo");
db[thing].flags |= GOING;
break;
case TYPE_ROOM: /* Remove drop to if it goes to dead object */
db[thing].location = NOTHING;
}
if (((db[thing].next < 0) || (db[thing].next >= db_top)) &&
(db[thing].next != NOTHING)) {
fprintf(stderr, "ERROR: Invalid next pointer from object %s(%d)\n",
db[thing].name, thing);
report();
db[thing].next = NOTHING;
}
if ((db[thing].owner < 0) || (db[thing].owner >= db_top)) {
fprintf(stderr, "ERROR: Invalid object owner %s(%d)\n", db[thing].name,
db);
report();
db[thing].owner = GOD;
}
} else
/* if object is dead stick in free list */
free_object(thing);
/* mark all rooms that can be reached from limbo */
dbmark(0);
/* look through list and inform any player with an unconnected room */
dbunmark();
}
/* Check data base for disconnected rooms */
void dbmark(loc)
dbref loc;
{
dbref thing;
if ((loc < 0) || (loc >= db_top) ||
(db[loc].flags & MARKED) ||
(Typeof(loc) != TYPE_ROOM))
return;
db[loc].flags |= MARKED;
/* destroy any exits needing destruction */
do {
for (thing = db[loc].exits;
(thing != NOTHING) && !(db[thing].flags & GOING);
thing = db[thing].next) ;
if (thing != NOTHING) {
db[loc].exits = remove_first(db[loc].exits, thing);
do_empty(thing);
}
}
while (thing != NOTHING);
/* recursively trace */
for (thing = db[loc].exits; thing != NOTHING; thing = db[thing].next)
dbmark(db[thing].location);
}
void dbunmark()
{
dbref loc;
for (loc = 0; loc < db_top; loc++)
if (db[loc].flags & MARKED)
db[loc].flags &= ~MARKED;
else if (Typeof(loc) == TYPE_ROOM) {
dest_info(NOTHING, loc);
}
}
/* Check data base for disconnected objects */
void dbmark1()
{
dbref thing;
dbref loc;
for (loc = 0; loc < db_top; loc++)
if (Typeof(loc) != TYPE_EXIT) {
for (thing = db[loc].contents;thing != NOTHING;thing = db[thing].next) {
if ((db[thing].location != loc) || (Typeof(thing) == TYPE_EXIT)) {
fprintf(stderr,
"ERROR: Contents of object %d corrupt at object %d cleared\n",
loc, thing);
db[loc].contents = NOTHING;
break;
}
db[thing].flags |= MARKED;
}
if(Typeof(db[loc].owner) != TYPE_PLAYER) {
fprintf(stderr,"ERROR: Bad owner on object %d changed to %d\n", loc,
GOD);
db[loc].owner = GOD;
}
} else { /* lets convert old style exits to new ones. */
/* but only if it needs it */
if(db[loc].exits == NOTHING)
db[loc].exits = find_entrance(loc);
}
}
void dbunmark1()
{
dbref loc;
for (loc = 0; loc < db_top; loc++)
if (db[loc].flags & MARKED)
db[loc].flags &= ~MARKED;
else if (((Typeof(loc) == TYPE_PLAYER) || (Typeof(loc) == TYPE_THING))
&& !(db[loc].flags & GOING)) {
fprintf(stderr, "ERROR DBCK: Moved object %d\n", loc);
moveto(loc, 0);
}
}
void do_dbck(player)
dbref player;
{
if (!Wizard(player)) {
notify(player, "Silly mortal chicks are for kids!");
return;
}
FIX;
dbmark1();
dbunmark1();
}
/* send contents of destroyed object home+destroy exits */
/* all objects must be moved to nothing or otherwise unlinked first */
void do_empty(thing)
dbref thing;
{
static int nrecur = 0;
if (nrecur++ > 20) { /* if run away recursion return */
report();
fprintf(stderr, "ERROR: Runaway recursion in do_empty\n");
nrecur--;
return;
}
switch (Typeof(thing)) {
case TYPE_ROOM: /* if room destroy all exits out of it */
{
dbref first;
dbref rest;
/* before we kill it tell people what is happening */
dest_info(thing, NOTHING);
/* return owners deposit */
if (db[thing].owner > 0) {
giveto(db[thing].owner, ROOM_COST);
#ifdef QUOTA
add_quota(db[thing].owner, 1);
#endif /* QUOTA */
}
first = db[thing].exits;
db[thing].exits = NOTHING;
/* set destination of all exits to nothing */
DOLIST(rest, first) {
db[rest].location = NOTHING;
}
/* Clear all exits out of exit list */
while (first != NOTHING) {
rest = db[first].next;
if (Typeof(first) == TYPE_EXIT) {
/* compensate owner for loss then destroy */
if (db[first].owner > 0) {
giveto(first, EXIT_COST);
#ifdef QUOTA
add_quota(db[first].owner, 1);
#endif /* QUOTA */
}
do_empty(first);
}
first = rest;
}
}
case TYPE_THING:
case TYPE_PLAYER: /* if room or player send contents home */
{
dbref first;
dbref rest;
first = db[thing].contents;
db[thing].contents = NOTHING;
/* send all objects to nowhere */
DOLIST(rest, first) {
db[rest].location = NOTHING;
}
/* now send them home */
while (first != NOTHING) {
rest = db[first].next;
/* if home is in thing set it to limbo */
if (db[first].exits == thing)
db[first].exits = 0;
switch (Typeof(first)) {
case TYPE_EXIT: /* if player holding exits, destroy it */
do_empty(first);
break;
case TYPE_THING: /* move to home */
case TYPE_PLAYER:
if (db[first].exits != NOTHING) {
PUSH(first, db[db[first].exits].contents);
db[first].location = db[first].exits;
/* notify players they have been moved */
if (Typeof(first) == TYPE_PLAYER)
dest_info(first,NOTHING);
}
break;
}
first = rest;
}
}
break;
}
/* chomp chomp */
atr_free(thing);
db[thing].list = NULL;
/* don't eat name otherwise examine will crash */
free_boolexp(db[thing].key);
db[thing].key = TRUE_BOOLEXP;
s_Pennies(thing, 0);
db[thing].owner = GOD;
db[thing].flags = GOING | TYPE_THING; /* toad it */
db[thing].location = NOTHING;
SET(db[thing].name, "Garbage");
db[thing].exits = 0;
free_object(thing);
nrecur--;
}
#endif /* DESTROY */