/* $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 <stdio.h>
#include <ctype.h>
#include <string.h>  /* in some systems you may have to change this to strings.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.");
    }
}