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