pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file create.c
 *
 * \brief Functions for creating objects of all types.
 *
 *
 */

#include "copyrite.h"
#include "config.h"
#include <string.h>
#include "conf.h"
#include "externs.h"
#include "mushdb.h"
#include "attrib.h"
#include "match.h"
#include "extchat.h"
#include "log.h"
#include "flags.h"
#include "dbdefs.h"
#include "lock.h"
#include "parse.h"
#include "game.h"
#include "command.h"
#include "confmagic.h"

static dbref parse_linkable_room(dbref player, const char *room_name);
static dbref check_var_link(const char *dest_name);
static dbref clone_object(dbref player, dbref thing, const char *newname,
			  int preserve);

struct db_stat_info current_state; /**< Current stats for database */

/* utility for open and link */
static dbref
parse_linkable_room(dbref player, const char *room_name)
{
  dbref room;

  /* parse room */
  if (!strcasecmp(room_name, "here")) {
    room = IsExit(player) ? Source(player) : Location(player);
  } else if (!strcasecmp(room_name, "home")) {
    return HOME;		/* HOME is always linkable */
  } else {
    room = parse_dbref(room_name);
  }

  /* check room */
  if (!GoodObject(room)) {
    notify(player, T("That is not a valid object."));
    return NOTHING;
  } else if (Going(room)) {
    notify(player, T("That room is being destroyed. Sorry."));
    return NOTHING;
  } else if (!can_link_to(player, room)) {
    notify(player, T("You can't link to that."));
    return NOTHING;
  } else {
    return room;
  }
}

static dbref
check_var_link(const char *dest_name)
{
  /* This allows an exit to be linked to a 'variable' destination.
   * Such exits can be linked by anyone but the owner's ability
   * to link to the destination is checked when it's computed.
   */

  if (!strcasecmp("VARIABLE", dest_name))
    return AMBIGUOUS;
  else
    return NOTHING;
}

/** Create an exit.
 * This function opens an exit and optionally links it.
 * \param player the enactor.
 * \param direction the name of the exit.
 * \param linkto the room to link to, as a string.
 * \param pseudo a phony location for player if a back exit is needed. This is bpass by do_open() as the source room of the back exit.
 * \return dbref of the new exit, or NOTHING.
 */
dbref
do_real_open(dbref player, const char *direction, const char *linkto,
	     dbref pseudo)
{
  dbref loc =
    (pseudo !=
     NOTHING) ? pseudo : (IsExit(player) ? Source(player) : Location(player));
  dbref new_exit;
  if (!command_check_byname(player, "@dig")) {
    notify(player, "Permission denied.");
    return NOTHING;
  }
  if ((loc == NOTHING) || (!IsRoom(loc))) {
    notify(player, T("Sorry you can only make exits out of rooms."));
    return NOTHING;
  }
  if (Going(loc)) {
    notify(player, T("You can't make an exit in a place that's crumbling."));
    return NOTHING;
  }
  if (!*direction) {
    notify(player, T("Open where?"));
    return NOTHING;
  } else if (!ok_name(direction)) {
    notify(player, T("That's a strange name for an exit!"));
    return NOTHING;
  }
  if (!Open_Anywhere(player) && !controls(player, loc)) {
    notify(player, T("Permission denied."));
  } else if (can_pay_fees(player, EXIT_COST)) {
    /* create the exit */
    new_exit = new_object();

    /* initialize everything */
    set_name(new_exit, direction);
    Owner(new_exit) = Owner(player);
    Zone(new_exit) = Zone(player);
    Source(new_exit) = loc;
    Type(new_exit) = TYPE_EXIT;
    Flags(new_exit) = string_to_bits("FLAG", options.exit_flags);

    /* link it in */
    PUSH(new_exit, Exits(loc));

    /* and we're done */
    notify_format(player, T("Opened exit %s"), unparse_dbref(new_exit));

    /* check second arg to see if we should do a link */
    if (linkto && *linkto != '\0') {
      notify(player, T("Trying to link..."));
      if ((loc = check_var_link(linkto)) == NOTHING)
	loc = parse_linkable_room(player, linkto);
      if (loc != NOTHING) {
	if (!payfor(player, LINK_COST)) {
	  notify_format(player, T("You don't have enough %s to link."), MONIES);
	} else {
	  /* it's ok, link it */
	  Location(new_exit) = loc;
	  notify_format(player, T("Linked exit #%d to #%d"), new_exit, loc);
	}
      }
    }
    current_state.exits++;
    local_data_create(new_exit);
    return new_exit;
  }
  return NOTHING;
}

/** Open a new exit.
 * \verbatim
 * This is the top-level function for @open. It calls do_real_open()
 * to do the real work of opening both the exit forward and the exit back.
 * \endverbatim
 * \param player the enactor.
 * \param direction name of the exit forward.
 * \param links 1-based array containing name of destination and optionally name of exit back.
 */
void
do_open(dbref player, const char *direction, char **links)
{
  dbref forward;
  forward = do_real_open(player, direction, links[1], NOTHING);
  if (links[2] && GoodObject(forward) && GoodObject(Location(forward))) {
    do_real_open(player, links[2], "here", Location(forward));
  }
}

/** Unlink an exit or room.
 * \verbatim
 * This is the top-level function for @unlink, which can unlink an exit
 * or remove a drop-to from a room.
 * \endverbatim
 * \param player the enactor.
 * \param name name of the object to unlink.
 */
void
do_unlink(dbref player, const char *name)
{
  dbref exit_l, old_loc;
  long match_flags = MAT_EXIT | MAT_HERE | MAT_ABSOLUTE;

  if (!Wizard(player)) {
    match_flags |= MAT_CONTROL;
  }
  switch (exit_l = match_result(player, name, TYPE_EXIT, match_flags)) {
  case NOTHING:
    notify(player, T("Unlink what?"));
    break;
  case AMBIGUOUS:
    notify(player, T("I don't know which one you mean!"));
    break;
  default:
    if (!controls(player, exit_l)) {
      notify(player, T("Permission denied."));
    } else {
      switch (Typeof(exit_l)) {
      case TYPE_EXIT:
	old_loc = Location(exit_l);
	Location(exit_l) = NOTHING;
	notify_format(player, T("Unlinked exit #%d (Used to lead to %s)."),
		      exit_l, unparse_object(player, old_loc));
	break;
      case TYPE_ROOM:
	Location(exit_l) = NOTHING;
	notify(player, T("Dropto removed."));
	break;
      default:
	notify(player, T("You can't unlink that!"));
	break;
      }
    }
  }
}

/** Link an exit, room, player, or thing.
 * \verbatim
 * This is the top-level function for @link, which is used to link an
 * exit to a destination, set a player or thing's home, or set a 
 * drop-to on a room.
 *
 * Linking an exit usually seizes ownership of the exit and costs 1 penny.
 * 1 penny is also transferred to the former owner.
 * \endverbatim
 * \param player the enactor.
 * \param name the name of the object to link.
 * \param room_name the name of the link destination.
 * \param preserve if 1, preserve ownership and zone data on exit relink.
 */
void
do_link(dbref player, const char *name, const char *room_name, int preserve)
{
  /* Use this to link to a room that you own. 
   * It usually seizes ownership of the exit and costs 1 penny,
   * plus a penny transferred to the exit owner if they aren't you.
   * You must own the linked-to room AND specify it by room number.
   */

  dbref thing;
  dbref room;

  if (!room_name || !*room_name) {
    do_unlink(player, name);
    return;
  }
  if (!IsRoom(player) && GoodObject(Location(player)) &&
      IsExit(Location(player))) {
    notify(player, T("You somehow wound up in a exit. No biscuit."));
    return;
  }
  if ((thing = noisy_match_result(player, name, TYPE_EXIT, MAT_EVERYTHING))
      != NOTHING) {
    switch (Typeof(thing)) {
    case TYPE_EXIT:
      if ((room = check_var_link(room_name)) == NOTHING)
	room = parse_linkable_room(player, room_name);
      if (room == NOTHING)
	return;
      if (GoodObject(room) && !can_link_to(player, room)) {
	notify(player, T("Permission denied."));
	break;
      }
      /* We may link an exit if it's unlinked and we pass the link-lock
       * or if we control it.
       */
      if (!(controls(player, thing)
	    || ((Location(thing) == NOTHING)
		&& eval_lock(player, thing, Link_Lock)))) {
	notify(player, T("Permission denied."));
	return;
      }
      if (preserve && !Wizard(player)) {
	notify(player, T("Permission denied."));
	return;
      }
      /* handle costs */
      if (Owner(thing) == Owner(player)) {
	if (!payfor(player, LINK_COST)) {
	  notify_format(player, T("It costs %d %s to link this exit."),
			LINK_COST, ((LINK_COST == 1) ? MONEY : MONIES));
	  return;
	}
      } else {
	if (!payfor(player, LINK_COST + EXIT_COST)) {
	  int a = LINK_COST + EXIT_COST;
	  notify_format(player, T("It costs %d %s to link this exit."), a,
			((a == 1) ? MONEY : MONIES));
	  return;
	} else if (!preserve) {
	  /* pay the owner for his loss */
	  giveto(Owner(thing), EXIT_COST);
	  chown_object(player, thing, player, 0);
	}
      }

      /* link has been validated and paid for; do it */
      if (!preserve) {
	Owner(thing) = Owner(player);
	Zone(thing) = Zone(player);
      }
      Location(thing) = room;

      /* notify the player */
      notify_format(player, T("Linked exit #%d to %s"), thing,
		    unparse_object(player, room));
      break;
    case TYPE_PLAYER:
    case TYPE_THING:
      if ((room =
	   noisy_match_result(player, room_name, NOTYPE, MAT_EVERYTHING)) < 0) {
	notify(player, T("No match."));
	return;
      }
      if (IsExit(room)) {
	notify(player, T("That is an exit."));
	return;
      }
      if (thing == room) {
	notify(player, T("You may not link something to itself."));
	return;
      }
      /* abode */
      if (!controls(player, room) && !Abode(room)) {
	notify(player, T("Permission denied."));
	break;
      }
      if (!controls(player, thing)) {
	notify(player, T("Permission denied."));
      } else if (room == HOME) {
	notify(player, T("Can't set home to home."));
      } else {
	/* do the link */
	Home(thing) = room;	/* home */
	if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player)))
	  notify(player, T("Home set."));
      }
      break;
    case TYPE_ROOM:
      if ((room = parse_linkable_room(player, room_name)) == NOTHING)
	return;
      if ((room != HOME) && (!IsRoom(room))) {
	notify(player, T("That is not a room!"));
	return;
      }
      if (!controls(player, thing)) {
	notify(player, T("Permission denied."));
      } else {
	/* do the link, in location */
	Location(thing) = room;	/* dropto */
	notify(player, T("Dropto set."));
      }
      break;
    default:
      notify(player, "Internal error: weird object type.");
      do_log(LT_ERR, NOTHING, NOTHING,
	     T("Weird object! Type of #%d is %d"), thing, Typeof(thing));
      break;
    }
  }
}

/** Create a room.
 * \verbatim
 * This is the top-level interface for @dig.
 * \endverbatim
 * \param player the enactor.
 * \param name the name of the room to create.
 * \param argv array of additional arguments to command (exit forward,exit back)
 * \param tport if 1, teleport the player to the new room.
 * \return dbref of new room, or NOTHING.
 */
dbref
do_dig(dbref player, const char *name, char **argv, int tport)
{
  dbref room;

  /* we don't need to know player's location!  hooray! */
  if (*name == '\0') {
    notify(player, T("Dig what?"));
  } else if (!ok_name(name)) {
    notify(player, T("That's a silly name for a room!"));
  } else if (can_pay_fees(player, ROOM_COST)) {
    room = new_object();

    /* Initialize everything */
    set_name(room, name);
    Owner(room) = Owner(player);
    Zone(room) = Zone(player);
    Type(room) = TYPE_ROOM;
    Flags(room) = string_to_bits("FLAG", options.room_flags);

    notify_format(player, T("%s created with room number %d."), name, room);
    if (argv[1] && *argv[1]) {
      char nbuff[MAX_COMMAND_LEN];
      sprintf(nbuff, "#%d", room);
      do_real_open(player, argv[1], nbuff, NOTHING);
    }
    if (argv[2] && *argv[2]) {
      do_real_open(player, argv[2], "here", room);
    }
    current_state.rooms++;
    local_data_create(room);
    if (tport) {
      /* We need to use the full command, because we need NO_TEL
       * and Z_TEL checking */
      char roomstr[MAX_COMMAND_LEN];
      sprintf(roomstr, "#%d", room);
      do_teleport(player, "me", roomstr, 0, 0);	/* if flag, move the player */
    }
    return room;
  }
  return NOTHING;
}

/** Create a thing.
 * \verbatim
 * This is the top-level function for @create.
 * \endverbatim
 * \param player the enactor.
 * \param name name of thing to create.
 * \param cost pennies spent in creation.
 * \return dbref of new thing, or NOTHING.
 */
dbref
do_create(dbref player, char *name, int cost)
{
  dbref loc;
  dbref thing;

  if (*name == '\0') {
    notify(player, T("Create what?"));
    return NOTHING;
  } else if (!ok_name(name)) {
    notify(player, T("That's a silly name for a thing!"));
    return NOTHING;
  } else if (cost < OBJECT_COST) {
    cost = OBJECT_COST;
  }
  if (can_pay_fees(player, cost)) {
    /* create the object */
    thing = new_object();

    /* initialize everything */
    set_name(thing, name);
    if (!IsExit(player))	/* Exits shouldn't have contents! */
      Location(thing) = player;
    else
      Location(thing) = Source(player);
    Owner(thing) = Owner(player);
    Zone(thing) = Zone(player);
    s_Pennies(thing, cost);
    Type(thing) = TYPE_THING;
    Flags(thing) = string_to_bits("FLAG", options.thing_flags);

    /* home is here (if we can link to it) or player's home */
    if ((loc = Location(player)) != NOTHING &&
	(controls(player, loc) || Abode(loc))) {
      Home(thing) = loc;	/* home */
    } else {
      Home(thing) = Home(player);	/* home */
    }

    /* link it in */
    if (!IsExit(player))
      PUSH(thing, Contents(player));
    else
      PUSH(thing, Contents(Source(player)));

    /* and we're done */
    notify_format(player, "Created: Object %s.", unparse_dbref(thing));
    current_state.things++;
    local_data_create(thing);
    return thing;
  }
  return NOTHING;
}

/* Clone an object. The new object is owned by the cloning player */
static dbref
clone_object(dbref player, dbref thing, const char *newname, int preserve)
{
  dbref clone;

  clone = new_object();

  memcpy(REFDB(clone), REFDB(thing), sizeof(struct object));
  Owner(clone) = Owner(player);
  Name(clone) = NULL;
  if (newname && *newname)
    set_name(clone, newname);
  else
    set_name(clone, Name(thing));
  s_Pennies(clone, Pennies(thing));
  atr_cpy(clone, thing);
  Locks(clone) = NULL;
  clone_locks(player, thing, clone);
  Zone(clone) = Zone(thing);
  Parent(clone) = Parent(thing);
  Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing));
  if (!preserve) {
    clear_flag_internal(clone, "WIZARD");
    clear_flag_internal(clone, "ROYALTY");
    Warnings(clone) = 0;	/* zap warnings */
    Powers(clone) = new_flag_bitmask("POWER");	/* zap powers */
  } else {
    Powers(clone) = clone_flag_bitmask("POWER", Powers(thing));
    if (Wizard(clone) || Royalty(clone) || Warnings(clone) ||
	!null_flagmask("POWER", Powers(clone)))
      notify(player,
	     T
	     ("Warning: @CLONE/PRESERVE on an object with WIZ, ROY, @powers, or @warnings."));
  }
  /* We give the clone the same modification time that its
   * other clone has, but update the creation time */
  CreTime(clone) = mudtime;

  Contents(clone) = Location(clone) = Next(clone) = NOTHING;

  return clone;

}

/** Clone an object.
 * \verbatim
 * This is the top-level function for @clone, which creates a duplicate
 * of a (non-player) object.
 * \endverbatim
 * \param player the enactor.
 * \param name the name of the object to clone.
 * \param newname the name to give the duplicate.
 * \param preserve if 1, preserve ownership and privileges on duplicate.
 * \return dbref of the duplicate, or NOTHING.
 */
dbref
do_clone(dbref player, char *name, char *newname, int preserve)
{
  dbref clone, thing;
  char dbnum[BUFFER_LEN];

  if (newname && *newname && !ok_name(newname)) {
    notify(player, T("That is not a reasonable name."));
    return NOTHING;
  }
  thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING);
  if ((thing == NOTHING))
    return NOTHING;

  if (!controls(player, thing) || IsPlayer(thing) ||
      (IsRoom(thing) && !command_check_byname(player, "@dig")) ||
      (IsExit(thing) && !command_check_byname(player, "@open")) ||
      (IsThing(thing) && !command_check_byname(player, "@create"))) {
    notify(player, T("Permission denied."));
    return NOTHING;
  }
  /* don't allow cloning of destructed things */
  if (IsGarbage(thing)) {
    notify(player, T("There's nothing left of it to clone!"));
    return NOTHING;
  }
  if (preserve && !Wizard(player)) {
    notify(player,
	   T("You cannot @CLONE/PRESERVE.  Use normal @CLONE instead."));
    return NOTHING;
  }
  /* make sure owner can afford it */
  switch (Typeof(thing)) {
  case TYPE_THING:
    if (can_pay_fees(player, Pennies(thing))) {
      clone = clone_object(player, thing, newname, preserve);
      notify_format(player, T("Cloned: Object %s."), unparse_dbref(clone));
      if (IsRoom(player))
	moveto(clone, player);
      else
	moveto(clone, Location(player));
      current_state.things++;
      local_data_clone(clone, thing);
      real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING,
		  global_eval_context.wenv, 0);
      return clone;
    }
    return NOTHING;
    break;
  case TYPE_ROOM:
    if (can_pay_fees(player, ROOM_COST)) {
      clone = clone_object(player, thing, newname, preserve);
      Exits(clone) = NOTHING;
      notify_format(player, T("Cloned: Room #%d."), clone);
      current_state.rooms++;
      local_data_clone(clone, thing);
      real_did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING,
		  global_eval_context.wenv, 0);
      return clone;
    }
    return NOTHING;
    break;
  case TYPE_EXIT:
    /* For exits, we don't want people to be able to link it to
       a location they can't with @open. So, all this stuff. 
     */
    switch (Location(thing)) {
    case NOTHING:
      strcpy(dbnum, "#-1");
      break;
    case HOME:
      strcpy(dbnum, "home");
      break;
    case AMBIGUOUS:
      strcpy(dbnum, "variable");
      break;
    default:
      strcpy(dbnum, unparse_dbref(Location(thing)));
    }
    if (newname && *newname)
      clone = do_real_open(player, newname, dbnum, NOTHING);
    else
      clone = do_real_open(player, Name(thing), dbnum, NOTHING);
    if (!GoodObject(clone))
      return NOTHING;
    else {
      atr_cpy(clone, thing);
      clone_locks(player, thing, clone);
      Zone(clone) = Zone(thing);
      Parent(clone) = Parent(thing);
      Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing));
      if (!preserve) {
	clear_flag_internal(clone, "WIZARD");
	clear_flag_internal(clone, "ROYALTY");
	Warnings(clone) = 0;	/* zap warnings */
	Powers(clone) = new_flag_bitmask("POWER");	/* zap powers */
      } else {
	Warnings(clone) = Warnings(thing);
	Powers(clone) = clone_flag_bitmask("POWER", Powers(thing));
      }
      if (Wizard(clone) || Royalty(clone) || Warnings(clone) ||
	  !null_flagmask("POWER", Powers(clone)))
	notify(player,
	       T
	       ("Warning: @CLONE/PRESERVE on an exit with WIZ, ROY, @powers, or Warnings."));
      notify_format(player, T("Cloned: Exit #%d."), clone);
      local_data_clone(clone, thing);
      return clone;
    }
  }
  return NOTHING;

}