/* $Header: set.c,v 2.0 90/05/05 12:45:42 lachesis Exp $
 * $Log:    set.c,v $
 * Revision 2.0  90/05/05  12:45:42  lachesis
 * Incorporated ABODE and HAVEN flags (remembering to convert FireFoot's
 * usage of those flags to ours).
 * Added Examine of objects that don't belong to you, added GOD_PRIV.
 *
 * Revision 1.3  90/04/21  17:20:48  lachesis
 * Added property lists.
 *
 * Revision 1.2  90/04/20  14:06:51  lachesis
 * Added @odrop && @drop.
 *
 * Revision 1.1  90/04/14  14:56:51  lachesis
 * Initial revision
 *
 */
#include "copyright.h"

/* commands which set parameters */
#include "os.h"

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

#ifdef COMPRESS
#define alloc_compressed(x) alloc_string(compress(x))
#else /* COMPRESS */
#define alloc_compressed(x) alloc_string(x)
#endif /* COMPRESS */

static dbref match_controlled (dbref player, const char *name)
{
  dbref match;

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

  match = noisy_match_result ();
  if (match != NOTHING && !controls (player, match)) {
    notify (player, "Permission denied.");
    return NOTHING;
  } else {
    return match;
  }
}

void do_name (dbref player, const char *name, char *newname)
{
  dbref thing;
  char *password;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    /* check for bad name */
    if (*newname == '\0') {
      notify (player, "Give it what new name?");
      return;
    }

    /* check for renaming a player */
    if (Typeof (thing) == TYPE_PLAYER) {
      if (!db[thing].sp.player.password) {
        notify (player, "Sorry");
        return;
      }
      /* split off password */
      for (password = newname; *password && !isspace (*password); password++)
        /*EMPTY*/;
      /* eat whitespace */
      if (*password) {
        *password++ = '\0';     /* terminate name */
        while (*password && isspace (*password))
          password++;
      }

      /* check for null password */
      if (!*password) {
        notify (player,
          "You must specify a password to change a player name.");
        notify (player, "E.g.: name player = newname password");
        return;
      } else if (strcmp (password, db[thing].sp.player.password)) {
        notify (player, "Incorrect password.");
        return;
      } else if (string_compare (newname, db[thing].name)
        && !ok_player_name (newname)) {
        notify (player, "You can't give a player that name.");
        return;
      }
      /* everything ok, notify */
      fprintf (stderr, "NAME CHANGE: %s(#%d) to %s\n",
        db[thing].name, thing, newname);
      delete_player (player);
      if (db[thing].name) {
        free ((void *) db[thing].name);
      }
      db[thing].name = alloc_string (newname);
      add_player (player);
      notify (player, "Name set.");
      return;
    } else {
      if (!ok_name (newname)) {
        notify (player, "That is not a reasonable name.");
        return;
      }
    }

    /* everything ok, change the name */
    if (db[thing].name) {
      free ((void *) db[thing].name);
    }
    db[thing].name = alloc_string (newname);
    notify (player, "Name set.");
  }
}

void do_describe (dbref player, const char *name, const char *description)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].description) {
      free ((void *) db[thing].description);
    }
    db[thing].description = alloc_compressed (description);
    notify (player, "Description set.");
  }
}

void do_fail (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].fail_message) {
      free ((void *) db[thing].fail_message);
    }
    db[thing].fail_message = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_success (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].succ_message) {
      free ((void *) db[thing].succ_message);
    }
    db[thing].succ_message = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

/* sets the drop message for player */
void do_drop_message (dbref player, const char *name, const char *message)
{
  dbref thing;
  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].drop_message) {
      free ((void *) db[thing].drop_message);
    }
    db[thing].drop_message = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_osuccess (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].osuccess) {
      free ((void *) db[thing].osuccess);
    }
    db[thing].osuccess = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_ofail (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].ofail) {
      free ((void *) db[thing].ofail);
    }
    db[thing].ofail = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_odrop (dbref player, const char *name, const char *message)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    if (db[thing].odrop) {
      free ((void *) db[thing].odrop);
    }
    db[thing].odrop = alloc_compressed (message);
    notify (player, "Message set.");
  }
}

void do_lock (dbref player, const char *name, const char *keyname)
{
  dbref thing;
  struct boolexp *key;

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

  switch (thing = match_result ()) {
  case NOTHING:
    notify (player, "I don't see what you want to lock!");
    return;
  case AMBIGUOUS:
    notify (player, "I don't know which one you want to lock!");
    return;
  default:
    if (!controls (player, thing)) {
      notify (player, "You can't lock that!");
      return;
    }
    break;
  }

  key = parse_boolexp (player, keyname);
  if (key == TRUE_BOOLEXP) {
    notify (player, "I don't understand that key.");
  } else {
    /* everything ok, do it */
    free_boolexp (db[thing].key);
    db[thing].key = key;
    notify (player, "Locked.");
  }
}

void do_unlock (dbref player, const char *name)
{
  dbref thing;

  if ((thing = match_controlled (player, name)) != NOTHING) {
    free_boolexp (db[thing].key);
    db[thing].key = TRUE_BOOLEXP;
    notify (player, "Unlocked.");
  }
}

void do_unlink (dbref player, const char *name)
{
  dbref exit;

  init_match (player, name, TYPE_EXIT);
  match_all_exits ();
  match_here ();
  if (Wizard (player)) {
    match_absolute ();
  }

  switch (exit = match_result ()) {
  case NOTHING:
    notify (player, "Unlink what?");
    break;
  case AMBIGUOUS:
    notify (player, "I don't know which one you mean!");
    break;
  default:
    if (!controls (player, exit)) {
      notify (player, "Permission denied.");
    } else {
      switch (Typeof (exit)) {
      case TYPE_EXIT:
        db[exit].sp.exit.ndest = 0;
        if (db[exit].sp.exit.dest)
          free (db[exit].sp.exit.dest);
        db[exit].sp.exit.dest = 0;
        notify (player, "Unlinked.");
        break;
      case TYPE_ROOM:
        db[exit].sp.room.dropto = NOTHING;
        notify (player, "Dropto removed.");
        break;
      default:
        notify (player, "You can't unlink that!");
        break;
      }
    }
  }
}

#ifdef PLAYER_CHOWN
void do_chown (dbref player, const char *name, const char *newowner)
{
  dbref thing;
  dbref owner;

  if (!*name) {
    notify (player, "You must specify what you want to take ownership of.");
    return;
  }
#if 0
  if (!Wizard (player) && *newowner) {
    notify (player, "Only wizards can transfer ownership to others.");
    return;
  }
#endif

  init_match (player, name, NOTYPE);
  match_everything ();
  if ((thing = noisy_match_result ()) == NOTHING)
    return;
  if (*newowner && string_compare (newowner, "me")) {
    if ((owner = lookup_player (newowner)) == NOTHING) {
      notify (player, "I couldn't find that player.");
      return;
    }
  } else {
    owner = player;
  }

  if (!Wizard (player) && (!(db[thing].flags & CHOWN_OK) ||
      !could_doit (player, thing))) {
    notify (player, "You can't take possession of that.");
    return;
  }

  switch (Typeof (thing)) {
  case TYPE_ROOM:
    if (!Wizard (player) && (db[player].location != thing)) {
      notify (player, "You cannot chown another room.");
      return;
    }
    db[thing].sp.room.owner = owner;
    break;
  case TYPE_THING:
    db[thing].sp.thing.owner = owner;
    break;
  case TYPE_EXIT:
    db[thing].sp.exit.owner = owner;
    break;
  case TYPE_PLAYER:
    notify (player, "Players always own themselves.");
    return;
#ifdef RECYCLE
  case TYPE_GARBAGE:
    notify (player, "No one wants to own garbage.");
    return;
#endif
  }
  if (owner == player)
    notify (player, "Owner changed to you.");
  else
    notify (player, "Owner changed.");
}

#else /* without PLAYER_CHOWN */
void do_chown (dbref player, const char *name, const char *newowner)
{
  dbref thing;
  dbref owner;

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

  init_match (player, name, NOTYPE);
  match_everything ();
  if ((thing = noisy_match_result ()) == NOTHING)
    return;
  if ((owner = lookup_player (newowner)) == NOTHING) {
    notify (player, "I couldn't find that player.");
    return;
  }
  switch (Typeof (thing)) {
  case TYPE_ROOM:
    db[thing].sp.room.owner = owner;
    break;
  case TYPE_THING:
    db[thing].sp.thing.owner = owner;
    break;
  case TYPE_EXIT:
    db[thing].sp.exit.owner = owner;
    break;
  case TYPE_PLAYER:
    notify (player, "Players always own themselves.");
    return;
#ifdef RECYCLE
  case TYPE_GARBAGE:
    notify (player, "No one wants to own garbage.");
    return;
#endif
  }
  notify (player, "Owner changed.");
}

#endif /* PLAYER_CHOWN */

/* Note: Gender code taken out.  All gender references are now to be handled
   by property lists...
   Setting of flags and property code done here.  Note that the PROP_DELIMITER
   identifies when you're setting a property.
   A @set <thing>= :
   will clear all properties.
   A @set <thing>= type:
   will remove that property.
   A @set <thing>= type: class
   will add that property or replace it.                                    */

void do_set (dbref player, const char *name, const char *flag)
{
  dbref thing;
  const char *p;
  object_flag_type f;

  /* find thing */
  if ((thing = match_controlled (player, name)) == NOTHING)
    return;

  /* move p past NOT_TOKEN if present */
  for (p = flag; *p && (*p == NOT_TOKEN || isspace (*p)); p++)
    /*EMPTY*/;

  /* Now we check to see if it's a property reference */
  /* if this gets changed, please also modify boolexp.c */
  if (index (flag, PROP_DELIMITER)) {
    /* copy the string so we can muck with it */
    char *type = get_string (flag);     /* type */
    char *class = index (type, PROP_DELIMITER); /* class */
    char *x = type;             /* to preserve string location so we can free it */
    char *temp;
    struct plist *prop, *last;

    while (isspace (*type) && (*type != PROP_DELIMITER))
      type++;
    if (*type == PROP_DELIMITER) {
      /* clear all properties */
      for (last = prop = db[thing].properties; prop; prop = last) {
        last = prop->next;
        free_prop (prop);
      }
      notify (player, "All properties removed.");
      free ((void *) x);
      db[thing].properties = 0;
      return;
    } else {
      /* get rid of trailing spaces */
      for (temp = class - 1; isspace (*temp); temp--)
        /*EMPTY*/;
      temp++;
      *temp = '\0';
    }
    class++;                    /* move to next character */
    while (isspace (*class) && *class)
      class++;
    if (!(*class)) {
      remove_property (thing, type);
      notify (player, "Property removed.");
    } else {
      /* remove trailing spaces */
      for (temp = class + strlen (class);
        isspace (*temp) && temp > class; temp--)
        /*EMPTY*/;
      *temp = '\0';
      add_property (thing, type, class, 0);
      notify (player, "Property set.");
    }
    free ((void *) x);
    return;
  }

  /* identify flag */
  if (*p == '\0') {
    notify (player, "You must specify a flag to set.");
    return;
  } else if (string_prefix ("LINK_OK", p)) {
    f = LINK_OK;
  } else if (string_prefix ("DARK", p)) {
    f = DARK;
  } else if (string_prefix ("STICKY", p)) {
    f = STICKY;
  } else if (string_prefix ("WIZARD", p)) {
    f = WIZARD;
  } else if (string_prefix ("TEMPLE", p)) {
    f = TEMPLE;
#ifdef RESTRICTED_BUILDING
  } else if (string_prefix ("BUILDER", p)) {
    f = BUILDER;
#endif /* RESTRICTED_BUILDING */
#ifdef PLAYER_CHOWN
  } else if (string_prefix ("CHOWN_OK", p)) {
    f = CHOWN_OK;
#endif /* PLAYER_CHOWN */
  } else if (string_prefix ("JUMP_OK", p)) {
    f = JUMP_OK;
#ifdef HAVEN
  } else if (string_prefix ("HAVEN", p)) {
    f = HAVEN;
#endif /* HAVEN */
#ifdef ABODE
  } else if (string_prefix ("ABODE", p)) {
    f = ABODE;
#endif /* ABODE */
  } else {
    notify (player, "I don't recognize that flag.");
    return;
  }

  /* check for restricted flag */
  if (!Wizard (player)
    && (f == WIZARD
#ifdef RESTRICTED_BUILDING
      || f == BUILDER
#endif /* RESTRICTED_BUILDING */
      || f == TEMPLE || f == DARK && Typeof (thing) != TYPE_ROOM)) {
    notify (player, "Permission denied.");
    return;
  }

  /* check for stupid wizard */
  if (f == WIZARD && *flag == NOT_TOKEN && thing == player) {
    notify (player, "You cannot make yourself mortal.");
    return;
  }
#ifdef GOD_PRIV
  if (f == WIZARD && *flag == NOT_TOKEN && God (thing)) {
    notify (player, "Even wizards can't make God mortal.");
    return;
  }
#endif /* GOD_PRIV */

  /* else everything is ok, do the set */
  if (*flag == NOT_TOKEN) {
    /* reset the flag */
    db[thing].flags &= ~f;
    notify (player, "Flag reset.");
  } else {
    /* set the flag */
    db[thing].flags |= f;
    notify (player, "Flag set.");
  }
}