/* destroy.c */
#include "os.h"
#include "copyright.h"
#include "config.h"
#ifdef DESTROY
#include "db.h"
#include "externs.h"
#include "globals.h"
#include "interface.h"
#include "match.h"
dbref first_free = NOTHING;
static void dbmark (dbref loc);
static void dbmark1 (void);
static void dbunmark (void);
static void dbunmark1 (void);
static void do_empty (dbref thing);
#ifdef DESTROY
void do_destroy (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 (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 (void)
{
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 int 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 (void)
{
dbref thing;
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, thing);
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 */
static void dbmark (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);
}
static void dbunmark (void)
{
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 */
static void dbmark1 (void)
{
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);
}
}
static void dbunmark1 (void)
{
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 (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 */
static void do_empty (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 */