/* create.c */

/* Commands that create new objects */
#include "os.h"
#include "config.h"
#include "db.h"
#include "attrib.h"
#include "interface.h"
#include "externs.h"
#include "match.h"

/* utility for open and link */
static dbref parse_linkable_room (dbref player, const char *room_name)
{
  dbref room;
  /* skip leading NUMBER_TOKEN if any */
  if (*room_name == NUMBER_TOKEN)
    room_name++;
  /* parse room */
  if (!string_compare (room_name, "here")) {
    room = db[player].location;
  } else if (!string_compare (room_name, "home")) {
    return HOME;                /* HOME is always linkable */
  } else {
    room = parse_dbref (room_name);
  }

  /* check room */
  if (room < 0 || room >= db_top) {
    notify (player, "That is not a valid object.");
    return NOTHING;
  } else if (!can_link_to (player, room)) {
    notify (player, "You can't link to that.");
    return NOTHING;
  } else {
    return room;
  }
}

/* use this to create an exit */
static int do_real_open (dbref player, const char *direction, const char *linkto, dbref pseudo /* a phony location for a player if a back
                                                                                         * exit is needed */
  )
{
  dbref loc = (pseudo != NOTHING) ? pseudo : db[player].location;
  dbref exit;
  if ((loc == NOTHING) || (Typeof (loc) != TYPE_ROOM)) {
    notify (player, "Sorry you can only make exits out of rooms.");
    return 0;
  }
#ifdef RESTRICTED_BUILDING
  if (!Builder (player)) {
    notify (player, "That command is restricted to authorized builders.");
    return 0;
  }
#endif /* RESTRICTED_BUILDING */
#ifdef GUEST_RESTRICT
  if (Guest (player)) {
    notify (player, "Guests are not allowed to build.");
    return 0;
  }
#endif

  if (!*direction) {
    notify (player, "Open where?");
    return 0;
  } else if (!ok_name (direction)) {
    notify (player, "That's a strange name for an exit!");
    return 0;
  }
  if (!controls (player, loc)) {
    notify (player, "Permission denied.");
  } else if (can_pay_fees (player, EXIT_COST)) {
    /* create the exit */
    exit = new_object ();

    /* initialize everything */
    SET (db[exit].name, direction);
    db[exit].owner = db[player].owner;
    db[exit].zone = db[player].zone;
    db[exit].flags = TYPE_EXIT;
    db[exit].exits = loc;

    /* link it in */
    PUSH (exit, db[loc].exits);

    /* and we're done */
    notify (player, "Opened.");

    /* check second arg to see if we should do a link */
    if (linkto && *linkto != '\0') {
      notify (player, "Trying to link...");
      if ((loc = parse_linkable_room (player, linkto)) != NOTHING) {
        if (!payfor (player, LINK_COST)) {
          notify (player, tprintf ("You don't have enough %s to link.",
              MONIES));
        } else {
          /* it's ok, link it */
          db[exit].location = loc;
          notify (player, "Linked.");
        }
      }
    }
    return exit;
  }
  return 0;
}

void do_open (dbref player, const char *direction, char *links[])
{
  do_real_open (player, direction, links[1], NOTHING);
  if (links[2]) {
    do_real_open (player, links[2], tprintf ("%d", db[player].location),
      parse_linkable_room (player, links[1]));
  }
}

/* use this to link to a room that you own */
/* it seizes ownership of the exit */
/* 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 */
void do_link (dbref player, const char *name, const char *room_name)
{
  dbref thing;
  dbref room;
  if (Typeof (db[player].location) == TYPE_EXIT) {
    notify (player, "You somehow wound up in a exit. No biscuit.");
    return;
  }
  init_match (player, name, TYPE_EXIT);
  match_exit ();
  match_neighbor ();
  match_possession ();
  match_me ();
  match_here ();
  match_absolute ();
  match_player ();

  if ((thing = noisy_match_result ()) != NOTHING) {
    switch (Typeof (thing)) {
    case TYPE_EXIT:
      if ((room = parse_linkable_room (player, room_name)) == NOTHING)
        return;
      if ((room != HOME) &&
        !controls (player, room) && !(db[room].flags & LINK_OK)) {
        notify (player, "Permission denied.");
        break;
      }
      /* we're ok, check the usual stuff */
      if (db[thing].location != NOTHING) {
        if (controls (player, thing)) {
          if (Typeof (db[thing].location) == TYPE_PLAYER) {
            notify (player, "That exit is being carried.");
          } else {
            notify (player, "That exit is already linked.");
          }
        } else {
          notify (player, "Permission denied.");
        }
      } else {
        /* handle costs */
        if (db[thing].owner == db[player].owner) {
          if (!payfor (player, LINK_COST)) {
            notify (player, tprintf ("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 (player, tprintf ("It costs %d %s to link this exit.",
                a, ((a == 1) ? MONEY : MONIES)));
            return;
#ifdef RESTRICTED_BUILDING
          } else if (!Builder (player)) {
            notify (player, "Only authorized builders may seize exits.");
#endif /* RESTRICTED_BUILDING */
#ifdef GUEST_RESTRICT
          } else if (Guest (player)) {
            notify (player, "Guests are not allowed to build.");
#endif
          } else {
            /* pay the owner for his loss */
            giveto (db[thing].owner, EXIT_COST);
          }
        }

        /* link has been validated and paid for; do it */
        db[thing].owner = db[player].owner;
        db[thing].zone = db[player].zone;
        db[thing].location = room;

        /* notify the player */
        notify (player, "Linked.");
      }
      break;
    case TYPE_PLAYER:
    case TYPE_THING:
      init_match (player, room_name, NOTYPE);
      match_exit ();
      match_neighbor ();
      match_possession ();
      match_me ();
      match_here ();
      match_absolute ();
      match_player ();
      if ((room = noisy_match_result ()) < 0) {
        notify (player, "No match.");
        return;
      }
      if (Typeof (room) == TYPE_EXIT) {
        notify (player, "That is an exit.");
        return;
      }
      /* abode */
      if (!controls (player, room) && !IS (room, TYPE_ROOM, ROOM_ABODE)) {
        notify (player, "Permission denied.");
        break;
      }
      if (!controls (player, thing)) {
        notify (player, "Permission denied.");
      } else if (room == HOME) {
        notify (player, "Can't set home to home.");
      } else {
        /* do the link */
        db[thing].exits = room; /* home */
        notify (player, "Home set.");
      }
      break;
    case TYPE_ROOM:
      if ((room = parse_linkable_room (player, room_name)) == NOTHING)
        return;

      if (Typeof (room) != TYPE_ROOM) {
        notify (player, "That is not a room!");
        return;
      }
      if (!controls (player, thing)) {
        notify (player, "Permission denied.");
      } else {
        /* do the link, in location */
        db[thing].location = room;      /* dropto */
        notify (player, "Dropto set.");
      }
      break;
    default:
      notify (player, "Internal error: weird object type.");
      fprintf (stderr, "PANIC weird object: Typeof(%d) = %d\n",
        thing, Typeof (thing));
      break;
    }
  }
}

/* use this to create a room */
void do_dig (dbref player, const char *name, char *argv[])
{
  dbref room;

#ifdef RESTRICTED_BUILDING
  if (!Builder (player)) {
    notify (player, "That command is restricted to authorized builders.");
    return;
  }
#endif /* RESTRICTED_BUILDING */
#ifdef GUEST_RESTRICT
  if (Guest (player)) {
    notify (player, "Guests are not allowed to build.");
    return;
  }
#endif

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

    /* Initialize everything */
    SET (db[room].name, name);
    db[room].owner = db[player].owner;
    db[room].zone = db[player].zone;
    db[room].flags = TYPE_ROOM;
    notify (player, tprintf ("%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]) {
      char nbuff[MAX_COMMAND_LEN];
      sprintf (nbuff, "%d", db[player].location);
      do_real_open (player, argv[2], nbuff, room);
    }
  }
}

/* use this to create an object */
void do_create (dbref player, char *name, int cost)
{
  dbref loc;
  dbref thing;

#ifndef FREE_OBJECTS
#ifdef RESTRICTED_BUILDING
  if (!Builder (db[player].owner)) {
    notify (player, "That command is restricted to authorized builders.");
    return;
  }
#endif /* RESTRICTED_BUILDING */
#endif /* FREE_OBJECTS */

#ifdef GUEST_RESTRICT
  if (Guest (player)) {
    notify (player, "Guests are not allowed to build.");
    return;
  }
#endif

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

    /* initialize everything */
    SET (db[thing].name, name);
    db[thing].location = player;
    db[thing].owner = db[player].owner;
    db[thing].zone = db[player].zone;
    s_Pennies (thing, OBJECT_ENDOWMENT (cost));
    db[thing].flags = TYPE_THING;

    /* endow the object */
    if (Wizard (player)) {
      if (Pennies (thing) > MAX_WIZ_OBJECT_ENDOWMENT)
        s_Pennies (thing, MAX_WIZ_OBJECT_ENDOWMENT);
    } else if (Pennies (thing) > MAX_OBJECT_ENDOWMENT)
      s_Pennies (thing, MAX_OBJECT_ENDOWMENT);
    /* home is here (if we can link to it) or player's home */
    if ((loc = db[player].location) != NOTHING &&
      (controls (player, loc) || IS (loc, TYPE_ROOM, ROOM_ABODE))) {
      db[thing].exits = loc;    /* home */
    } else {
      db[thing].exits = db[player].exits;       /* home */
    }

    /* link it in */
    PUSH (thing, db[player].contents);

    /* and we're done */
    notify (player, tprintf ("Created: Object #%d.", thing));
  }
}


void do_clone (dbref player, char *name)
{
  dbref clone, thing;

#ifdef GUEST_RESTRICT
  if (Guest (player)) {
    notify (player, "Guests are not allowed to build.");
    return;
  }
#endif

  init_match (player, name, NOTYPE);
  match_everything ();

  thing = noisy_match_result ();
  if ((thing == NOTHING))
    return;

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

  /* don't allow cloning of destructed things */
  if (db[thing].flags & GOING) {
    notify (player, "There's nothing left of it to clone!");
    return;
  }

  /* make sure owner can afford it */
  if (can_pay_fees (player, OBJECT_COST)) {
    switch (Typeof (thing)) {
    case TYPE_THING:
#ifdef RESTRICTED_BUILDING
#ifndef FREE_OBJECTS
      if (!Builder (player)) {
        notify (player, "Command is for builders only.");
        return;
      }
#endif
#endif
      clone = new_object ();
      memcpy (&db[clone], &db[thing], sizeof (struct object));
      db[clone].name = NULL;
      SET (db[clone].name, db[thing].name);
      s_Pennies (clone, 1);
      atr_cpy (clone, thing);
      db[clone].key = dup_bool (db[thing].key);
      db[clone].contents = db[clone].location = db[clone].next = NOTHING;
      db[clone].flags &= ~WIZARD;
#ifdef ROYALTY_FLAG
      db[clone].flags &= ~ROYALTY;
#endif
      notify (player, tprintf ("Cloned: Object #%d.", clone));
      moveto (clone, db[player].location);
      did_it (player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING);
      break;
    case TYPE_EXIT:
#ifdef RESTRICTED_BUILDING
      if (!Builder (player)) {
        notify (player, "Command is for builders only.");
        return;
      }
#endif
      clone = do_real_open (player, db[thing].name, tprintf ("%d",
          db[thing].location), NOTHING);
      atr_cpy (clone, thing);
      db[clone].key = dup_bool (db[thing].key);
      db[clone].flags &= ~WIZARD;
      notify (player, "Exit cloned.");
      break;
    default:
      notify (player, "You can only clone things and exits.");
    }
  }
}