/* create.c */

/* Commands that create new objects */
#include <string.h>
#include "config.h"
#include "db.h"
#include "attrib.h"
#include "interface.h"
#include "externs.h"

/* utility for open and link */
static dbref parse_linkable_room(player, room_name)
    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 */
int do_real_open(player, direction, linkto, pseudo)
    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(player,direction,links)
    dbref player;
    const char *direction;
    const 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(player, name, room_name)
    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;
	      notify(player, tprintf("It costs %d %s to link this exit.",
				     (a = LINK_COST+EXIT_COST),
				     ((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(player, name, argv)
    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(player, name, cost)
    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(player, name)
    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.");
    }
  }
}