/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/wiz.c,v 1.16 90/09/28 12:25:37 rearl Exp $ */

/*
 * $Log:	wiz.c,v $
 * Revision 1.16  90/09/28  12:25:37  rearl
 * Fixed missing newline bug in @newpassword logging.
 * 
 * Revision 1.15  90/09/18  08:02:56  rearl
 * Fixed @tel for rooms -- a bug in permissions checking.
 * 
 * Revision 1.14  90/09/16  04:43:20  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.13  90/09/15  22:28:34  rearl
 * Send inventory of the toad home, not the wizard's!
 * 
 * Revision 1.12  90/09/13  06:30:20  rearl
 * @toad modified to chown the victim's items to a recipient player.
 * 
 * Revision 1.11  90/09/10  02:19:06  rearl
 * Changed NL line termination to CR/LF pairs.
 * 
 * Revision 1.10  90/09/05  02:32:31  rearl
 * Added match_here() for room parent setting.
 * 
 * Revision 1.9  90/09/01  06:00:02  rearl
 * Fixed code in @teleport.
 * 
 * Revision 1.8  90/08/27  03:35:41  rearl
 * Changed teleport checks...
 * 
 * Revision 1.7  90/08/11  04:12:47  rearl
 * *** empty log message ***
 * 
 * Revision 1.6  90/08/06  03:49:14  rearl
 * Added logging of @force, @boot, and @toad.
 * 
 * Revision 1.5  90/08/05  03:20:16  rearl
 * Redid matching routines.
 * 
 * Revision 1.4  90/08/02  22:07:04  rearl
 * Changed one call to a log function, that's it.
 * 
 * Revision 1.3  90/07/29  17:46:28  rearl
 * Made @stat command a little cleaner, toaded victims are now 
 * toaded first, then all their connections are booted from the game.
 * 
 * Revision 1.2  90/07/23  14:48:37  casie
 * *** empty log message ***
 * 
 * Revision 1.1  90/07/19  23:04:20  casie
 * Initial revision
 * 
 *
 */

#include "copyright.h"
#include "config.h"

/* Wizard-only commands */

#include "db.h"
#include "params.h"
#include "interface.h"
#include "match.h"
#include "externs.h"

void do_teleport(dbref player, const char *arg1, const char *arg2)
{
  dbref victim;
  dbref destination;
  const char *to;
  struct match_data md;
  
  /* get victim, destination */
  if(*arg2 == '\0') {
    victim = player;
    to = arg1;
  } else {
    init_match(player, arg1, NOTYPE, &md);
    match_neighbor(&md);
    match_possession(&md);
    match_me(&md);
    match_here(&md);
    match_absolute(&md);
    match_player(&md);
    
    if((victim = noisy_match_result(&md)) == NOTHING) {
      return;
    }
    to = arg2;
  }
  
  /* get destination */
  init_match(player, to, TYPE_PLAYER, &md);
  match_here(&md);
  match_home(&md);
  match_absolute(&md);
  match_neighbor(&md);
  match_me(&md);
  match_player(&md);
  
  switch(destination = match_result(&md)) {
  case NOTHING:
    notify(player, "Send it where?");
    break;
  case AMBIGUOUS:
    notify(player, "I don't know which destination you mean!");
    break;
  case HOME:
    switch (Typeof(victim)) {
    case TYPE_PLAYER:
      destination = DBFETCH(victim)->link;
      break;
    case TYPE_THING:
      destination = DBFETCH(victim)->link;
      break;
    case TYPE_ROOM:
      destination = GLOBAL_ENVIRONMENT;
      break;
    case TYPE_PROGRAM:
      destination = OWNER(victim);
      break;
    default:
      destination = PLAYER_START;  /* caught in the next switch anyway */
      break;
    }
  default:
    switch (Typeof(victim)) {
    case TYPE_PLAYER:
      if (!Arch(player) && (victim != player)) {
	notify(player, "Permission denied.");
	break;
      }
      if (Typeof(destination) != TYPE_ROOM
	  && Typeof(destination) != TYPE_PLAYER
	  && Typeof(destination) != TYPE_THING) {
	notify(player, "Bad destination.");
	break;
      }
      if(!Arch(player) &&
	 !(controls(player,destination)) &&
	 !((FLAGS(destination)&JUMP_OK)
#ifdef ABODE
	   || (FLAGS(destination)&ABODE)
#else
	   || (FLAGS(destination)&LINK_OK)
#endif /* ABODE */
	   )) {
	notify(player,"Permission denied!");
	break;
      }
      if(parent_loop_check(victim,destination)) {
	notify(player,"Error: would create a loop");
	break;
      }
      enter_room(victim, destination, DBFETCH(victim)->location);
      notify(player, "Teleported.");
      break;
    case TYPE_THING:
    case TYPE_PROGRAM:
      if (Typeof(destination) != TYPE_ROOM &&
	  Typeof(destination) != TYPE_PLAYER &&
	  Typeof(destination) != TYPE_THING) {
	notify(player, "Bad destination.");
	break;
      }
      if (!Arch(player)
	  && !((controls(player, destination)
#ifdef ABODE
		|| (FLAGS(destination)&ABODE)
#else
		|| (FLAGS(destination)&LINK_OK)
#endif /* ABODE */
		|| (FLAGS(destination)&JUMP_OK))
	       && (controls(player, victim)
		   || controls(player, DBFETCH(victim)->location)))) {
	notify(player, "Permission denied.");
	break;
      }
      /* check for non-sticky dropto */
      if (Typeof(destination) == TYPE_ROOM
	  && DBFETCH(destination)->link != NOTHING
	  && !(FLAGS(destination) & STICKY))
	destination = DBFETCH(destination)->link;
      if(parent_loop_check(victim,destination)) {
	notify(player,"Would create a loop. sorry.");
	break;
      }
      moveto(victim, destination);
      notify(player, "Teleported.");
      break;
    case TYPE_ROOM:
      if (Typeof(destination) != TYPE_ROOM) {
	notify(player, "Bad destination.");
	break;
      }
      if (!controls(player, victim)
	  || !can_link_to(player, NOTYPE, destination)
	  || victim == GLOBAL_ENVIRONMENT) {
	notify(player, "Permission denied.");
	break;
      }
      if (parent_loop_check(victim, destination)) {
	notify(player, "Parent would create a loop.");
	break;
      }
      moveto(victim, destination);
      notify(player, "Parent set.");
      break;
#ifdef RECYCLE
    case TYPE_GARBAGE:
      notify(player, "That object is in a place where magic cannot reach it.");
      break;
#endif
    default:
      notify(player, "You can't teleport that.");
      break;
    }
    break;
  }
  return;
}


void do_force(dbref player, const char *what, char *command)
{
  dbref victim;
  struct match_data md;
  init_match(player,what,TYPE_PLAYER,&md);
  match_neighbor(&md);
  match_possession(&md);
  match_me(&md);
  match_absolute(&md);
  if(Wizard(player))
    match_player(&md);
  victim=noisy_match_result(&md);
  if(victim==NOTHING) {
    return;
  }
  if(player != OWNER(victim) && !Wizard(player)) {
    notify(player,"Permission denied.");
    return;
  }
#ifdef GOD_PRIV
  if (God(victim)) {
    notify(player, "You cannot force god to do anything.");
    return;
  }
#endif /* GOD_PRIV */
  if(Typeof(victim)!=TYPE_PLAYER &&
     Typeof(victim)!=TYPE_THING) {
    notify(player, "Illegal object type.");
    return;
  }
  if(Typeof(victim)==TYPE_PLAYER)
    log_status("FORCED: %s(%d) by %s(%d): %s\n", NAME(victim),
	       victim, NAME(player), player, command);
  /* force victim to do command */
  process_command(victim, command, player,0);
}

void do_stats(dbref player, const char *name)
{
  int breakdown[10];
  int total = 0;
  dbref i;
  dbref owner = 0;
  struct match_data md;
  char buf[BUFFER_LEN];
  
  if(!payfor(player, LOOKUP_COST)) {
    notify(player,"You don't have enough pennies.");
    return;
  }

  if (name != NULL && *name != '\0') {
    owner = lookup_player(name);
    if (owner == NOTHING) {
      init_match(player, name, NOTYPE, &md);
      match_me(&md);
      match_absolute(&md);
      match_player(&md);
      
      if((owner = match_result(&md)) == NOTHING) {
        notify(player, "I can't find that player.");
        return;
      }
    }
    if(!controls(player,owner)) {
      notify(player, "Permission denied.");
      return;
    }
  }

  for(i = 0; i < 10; i++)
    breakdown[i] = 0;

  if(name != NULL || *name != '\0') {
    for (i = 0; i < db_top; i++)
      if ((*name == '\0') || (OWNER(i) == owner)) {
	total++;
	breakdown[Typeof(i)]++;
      }
  } else {
    for (i = 0; i < db_top; i++) {
      total++;
      breakdown[Typeof(i)]++;
    }
  }
  
#ifdef RECYCLE
  sprintf(buf,
	  "%d object%s  =   %d room%s, %d exit%s, %d thing%s, %d program%s, %d player%s, %d garbage.",
	  total,                   (total == 1)                   ? "" : "s",
	  breakdown[TYPE_ROOM],    (breakdown[TYPE_ROOM] == 1)    ? "" : "s",
	  breakdown[TYPE_EXIT],    (breakdown[TYPE_EXIT] == 1)    ? "" : "s",
	  breakdown[TYPE_THING],   (breakdown[TYPE_THING] == 1)   ? "" : "s",
	  breakdown[TYPE_PROGRAM], (breakdown[TYPE_PROGRAM] == 1) ? "" : "s",
	  breakdown[TYPE_PLAYER],  (breakdown[TYPE_PLAYER] == 1)  ? "" : "s",
	  breakdown[TYPE_GARBAGE]);
#else /* RECYCLE */
  sprintf(buf,
	  "%d object%s  =   %d room%s, %d exit%s, %d thing%s, %d program%s, %d player%s.",
	  total,                   (total == 1)                   ? "" : "s",
	  breakdown[TYPE_ROOM],    (breakdown[TYPE_ROOM] == 1)    ? "" : "s",
	  breakdown[TYPE_EXIT],    (breakdown[TYPE_EXIT] == 1)    ? "" : "s",
	  breakdown[TYPE_THING],   (breakdown[TYPE_THING] == 1)   ? "" : "s",
	  breakdown[TYPE_PROGRAM], (breakdown[TYPE_PROGRAM] == 1) ? "" : "s",
	  breakdown[TYPE_PLAYER],  (breakdown[TYPE_PLAYER] == 1)  ? "" : "s")
#endif /* RECYCLE */
    notify(player, buf);
}

void do_boot(dbref player, const char *name)
{
  dbref victim;
  char buf[BUFFER_LEN];
  
  if((victim = lookup_player(name)) == NOTHING) {
    notify(player, "That player does not exist.");
    return;
  }
  
  if(Typeof(victim) != TYPE_PLAYER) {
    notify(player, "You can only boot players!");
  } else if (!Wizard(player) && (OWNER(victim) != player)) {
    notify(player, "Permission denied.");
  }
#ifdef GOD_PRIV
  else if(God(victim)) {
    notify(player, "Permission denied.");
  }
#endif /* GOD_PRIV */
  else {
    notify(victim, "You have been booted off the game.");
    if (boot_off(victim)) {
      log_status("BOOTED: %s(%d) by %s(%d)\n", NAME(victim),
		 victim, NAME(player), player);
      sprintf(buf, "You booted %s off!", NAME(victim));
      notify(player, buf);
    } else {
      sprintf(buf, "%s is not connected.", NAME(victim));
      notify(player, buf);
    }
  }
}

void do_toad(dbref player, const char *name, const char *recip)
{
  dbref victim;
  dbref recipient;
  dbref stuff;

  if(Typeof(player) != TYPE_PLAYER) {
    notify(player, "Permission denied.");
    return;
  }

  if(!Wizard(player)) {
    notify(player, "Only a wizard can destroy a player.");
    return;
  }
  
  if((victim = lookup_player(name)) == NOTHING) {
    notify(player, "That player does not exist.");
    return;
  }
  
  if (!*recip) {
    recipient = GOD;
  } else {
    if ((recipient = lookup_player(recip)) == NOTHING
	|| recipient == victim) {
      notify(player, "That recipient does not exist.");
      return;
    }
  }
  
  if(Typeof(victim) != TYPE_PLAYER) {
    notify(player, "You can only destroy the living!");
  } else if(Wizard(victim) || FLAGS(victim)&WIZARD) { /* don't toad Quell W */
    notify(player, "You can't destroy a wizard.");
  } else {
    /* chown things to recipient, checking for a sane home location */
    /* for object. XXX -- if HOME/inventory handling changes, */
    /* please check this code.*/

    for (stuff = 0; stuff < db_top; stuff++) {
      if ((Typeof(stuff) == TYPE_THING)
        && (DBFETCH(stuff)->link == victim)) {
      DBSTORE(stuff, link, PLAYER_START);
      }
      if (OWNER(stuff) == victim) {
      OWNER(stuff) = recipient;
      DBDIRTY(stuff);
      }
    }
    /* Take them home; things should no longer be homed here, programs */
    /* should not be owned by player */

    send_contents(victim, HOME);
    if(DBFETCH(victim)->sp.player.password) {
      free((void *) DBFETCH(victim)->sp.player.password);
      DBFETCH(victim)->sp.player.password = NULL;
    }
    FLAGS(victim) = TYPE_THING;
    OWNER(victim) = player; /* you get it */
    
    /* notify people */
    notify(victim, "You have been destroyed.");
    sprintf(buf, "You destroyed %s!", NAME(victim));
    notify(player, buf);
    log_status("DESTROYED: %s(%d) by %s(%d)\n", NAME(victim),
	       victim, NAME(player), player);
    
    delete_player(victim);

    DBDIRTY(victim);
    while (boot_off(victim))
      ;
  }
}

void do_newpassword(dbref player, const char *name, const char *password)
{
  dbref victim;
  char buf[BUFFER_LEN];
  
  if(!Wizard(player)) {
    notify(player, "Permission denied.");
    return;
  } else if((victim = lookup_player(name)) == NOTHING) {
    notify(player, "No such player.");
  } else if(*password != '\0' && !ok_password(password)) {
    /* Wiz can set null passwords, but not bad passwords */
    notify(player, "Bad password");
#ifdef GOD_PRIV
  } else if (God(victim) && !God(player))
    {
      notify(player, "Permission denied.");
      return;
#endif /* GOD_PRIV */
    } else {
      /* it's ok, do it */
      if(DBFETCH(victim)->sp.player.password)
	free((void *) DBFETCH(victim)->sp.player.password);
      DBSTORE(victim, sp.player.password, alloc_string(password));
      sprintf(buf, "%s's password changed to %s.", NAME(victim),password); 
      notify(player, buf);
      sprintf(buf, "Your password has been changed by %s.", NAME(player));
      notify(victim, buf);
      log_status("NEWPASS'ED: %s(%d) by %s(%d)\n", NAME(victim), (int) victim,
		 NAME(player), (int) player);
    }
}

void do_pcreate(dbref player, const char *user, const char *password)
{
  dbref newguy;
  char buf[BUFSIZ];

  if (!Wizard(player)) {
    notify(player, "Permission denied.");
    return;
  }
  newguy = create_player (user, password);
  if (newguy == NOTHING) {
    notify(player, "Create failed.");
  } else {
    log_status("PCREATED %s(%d) by %s(%d)\n",
	       NAME(newguy), (int) newguy, NAME(player), (int) player);
    sprintf(buf, "%s created.", unparse_object(player, newguy));
    notify(player,buf);
  }
}

void swap(dbref r1,dbref r2)
{
  dbref lo;
  struct object tmpobj;

#define CSWAP(th1) { if((th1) == r1) (th1) = r2; \
                     else if((th1) == r2) (th1) = r1; }

  bcopy(DBFETCH(r1), &tmpobj, sizeof(tmpobj));
  bcopy(DBFETCH(r2), DBFETCH(r1), sizeof(tmpobj));
  bcopy(&tmpobj, DBFETCH(r2), sizeof(tmpobj));
  for(lo = 0; lo < db_top; lo++) {
    if(Typeof(lo) == TYPE_ROOM || Typeof(lo) == TYPE_THING
       || Typeof(lo) == TYPE_PLAYER)
      CSWAP(DBFETCH(lo)->exits);
    if(Typeof(lo) != TYPE_EXIT)
      CSWAP(DBFETCH(lo)->link);
    CSWAP(DBFETCH(lo)->location);
    CSWAP(DBFETCH(lo)->owner);
    CSWAP(DBFETCH(lo)->contents);
    CSWAP(DBFETCH(lo)->next);
    if(Typeof(lo) == TYPE_EXIT && DBFETCH(lo)->sp.exit.ndest) {
      int i;
      for(i = 0; i < (DBFETCH(lo)->sp.exit.ndest); i++) {
	CSWAP(DBFETCH(lo)->sp.exit.dest[i]);
      }
    }
    if(Typeof(lo) == TYPE_PLAYER) {
      CSWAP(DBFETCH(lo)->curr_prog);
    }
  }
}

void do_swap(dbref player, char *t1, char *t2)
{
  struct match_data md;
  dbref r1, r2;

  if(!Wizard(player)) {
    notify(player, "Permission denied.");
    return;
  }

  init_match(player,t1,TYPE_THING,&md);
  match_all_exits(&md);
  match_neighbor(&md);
  match_possession(&md);
  match_here(&md);
  if(Arch(player))
    match_absolute(&md);
  r1 = noisy_match_result(&md);
  if(r1 == NOTHING) {
    notify(player,"Bad first argument.");
    return;
  }
  init_match(player, t2, TYPE_THING, &md);
  match_all_exits(&md);
  match_neighbor(&md);
  match_possession(&md);
  match_here(&md);
  if(Arch(player))
    match_absolute(&md);
  r2 = noisy_match_result(&md);
  if(r2 == NOTHING) {
    notify(player,"Bad second argument.");
    return;
  }
  if(r1 < 0 || r2 < 0 || r1 >= db_top || r2 >= db_top) {
    notify(player,"Bad arguments.");
    return;
  }
  if(!controls(player, r1) && Typeof(r1) != TYPE_GARBAGE) {
    notify(player,"Permission denied.");
    return;
  }
  if(!controls(player, r2) && Typeof(r2) != TYPE_GARBAGE) {
    notify(player,"Permission denied.");
    return;
  }

#ifdef GOD_PRIV
  if((Typeof(r1) == TYPE_PROGRAM || Typeof(r2) == TYPE_PROGRAM ||
      Typeof(r1) == TYPE_PLAYER || Typeof(r2) == TYPE_PLAYER)
     && !God(player)) {		/* very destructive for program calls, */
    ;				/* and players want to keep their numbers */
    ;				/* and regular wizards can't do this. */
    notify(player,"Permission denied.");
    return;
  }

  if((God(r1) || God(r2)) && !God(player))
    notify(player, "Permission denied.");
#endif /* GOD_PRIV */

  notify(r1, "You sense that your identity has changed.");
  notify(r2, "You sense that your identity has changed.");
  swap(r1, r2);
  notify(player,"Swapped...");
}