/* 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 */