/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/predicates.c,v 1.8 90/09/18 08:01:39 rearl Exp $ */

/*
 * $Log:	predicates.c,v $
 * Revision 1.8  90/09/18  08:01:39  rearl
 * Fixed a few broken things.
 * 
 * Revision 1.7  90/09/16  04:42:42  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.6  90/09/10  02:22:30  rearl
 * Fixed some calls to pronoun_substitute.
 * 
 * Revision 1.5  90/08/27  03:32:49  rearl
 * Major changes on several predicates, usage...
 * 
 * Revision 1.4  90/08/15  03:07:34  rearl
 * Macroified some things.  Took out #ifdef GENDER.
 * 
 * Revision 1.3  90/08/06  02:46:30  rearl
 * Put can_link() back in.  Added restricted() for flags.
 * With #define GOD_PRIV, sub-wizards can no longer set other
 * players to Wizard.
 * 
 * Revision 1.2  90/08/02  18:54:04  rearl
 * Fixed controls() to return TRUE if exit is unlinked.  Everyone
 * controls an unlinked exit.  Got rid of can_link().
 * 
 * Revision 1.1  90/07/19  23:03:58  casie
 * Initial revision
 * 
 *
 */

#include "copyright.h"
#include "config.h"

/* Predicates for testing various conditions */

#include <ctype.h>

#include "db.h"
#include "interface.h"
#include "params.h"
#include "externs.h"

int can_hear(dbref who)
{
  return (notify_internal(who, NULL)
	  || ((FLAGS(who)&PUPPET) && Typeof(who) != TYPE_PLAYER)
	  || get_attr(who,"listen"));
}

int can_link_to(dbref who, object_flag_type what_type, dbref where)
{
  if (where == HOME) return 1;
  switch (what_type) {
  case TYPE_GARBAGE:
    return 0;
    break;
  case TYPE_EXIT:
    return (controls(who, where) || (FLAGS(where) & LINK_OK));
    /*NOTREACHED*/
    break;
  case TYPE_PLAYER:
    return (Typeof(where) == TYPE_ROOM && (controls(who, where)
					   || Linkable(where)));
    /*NOTREACHED*/
    break;
  case TYPE_ROOM:
  case TYPE_THING:
    return ((Typeof(where) == TYPE_ROOM || Typeof(where) == TYPE_PLAYER)
	    && (controls(who, where) || Linkable(where)));
    /*NOTREACHED*/
    break;
  case NOTYPE:
#ifdef ABODE
    return (controls(who, where) || ((!(Typeof(where) == TYPE_PLAYER)) && (FLAGS(where) & ABODE)) || (FLAGS(where) & LINK_OK));
#else /* ABODE */
    return (controls(who, where) || (FLAGS(where) & LINK_OK));
#endif /* ABODE */
    /*NOTREACHED*/
    break;
  }
  return 0;
}

int can_link(dbref who, dbref what)
{
  return (controls(who, what) || ((Typeof(what) == TYPE_EXIT)
				  && DBFETCH(what)->sp.exit.ndest == 0));
}

int could_doit(dbref player, dbref thing)
{
  if(Typeof(thing) == TYPE_EXIT && DBFETCH(thing)->sp.exit.ndest == 0) {
    return 0;
  }
  return(eval_boolexp (player, DBFETCH(thing)->key, thing));
}

int can_doit(dbref player, dbref thing, const char *default_fail_msg)
{
  dbref loc;
  char buf[BUFFER_LEN];
  
  if((loc = getloc(player)) == NOTHING) return 0;
  
  if(!could_doit(player, thing)) {
    /* can't do it */
    if(get_attr(thing, "Fail")) {
      exec_or_notify(player, thing, get_attr(thing, "Fail"));
    } else if(default_fail_msg) {
      notify(player, default_fail_msg);
    }
    
    if(get_attr(thing, "Ofail") && !Dark(player)) {
      sprintf(buf, "%s %s", NAME(player),
	      pronoun_substitute(player, get_attr(thing, "Ofail"), thing));
      notify_except(DBFETCH(loc)->contents, player, buf);
    }
    if(get_attr(thing, "Afail")) {
      trigobj(thing,get_attr(thing, "Afail"),NOTHING);
    }
    return 0;
  } else {
    /* can do it */
    if(get_attr(thing, "Succ")) {
      exec_or_notify(player, thing, get_attr(thing, "Succ"));
    }
    
    if(get_attr(thing, "Osucc") && !Dark(player)) {
      sprintf(buf, "%s %s", NAME(player),
	      pronoun_substitute(player, get_attr(thing, "Osucc"), thing));
      notify_except(DBFETCH(loc)->contents, player, buf);
    }
    if(get_attr(thing, "Asucc")) {
      trigobj(thing, get_attr(thing, "Asucc"), NOTHING);
    }
    return 1;
  }
}

int can_see(dbref player, dbref thing, int can_see_loc)
{
  if (player == thing || Typeof(thing) == TYPE_EXIT
      || Typeof(thing) == TYPE_ROOM) return 0;

  if (can_see_loc) {
    switch (Typeof(thing)) {
    case TYPE_PROGRAM:
      return((FLAGS(thing) & LINK_OK) || controls(player, thing) ||
	     controls(player, DBFETCH(thing)->location));
    default:
      return (!Dark(thing) || controls(player, thing));
    }
  } else {
    /* can't see loc */
    return player == OWNER(thing);
  }
}

int controls(dbref who, dbref what)
{
  /* Wizard players control everything */
  /* Wizard objects control everything except wizard players */
  /* Architects control everything except wizard objects */
  /* owners control their stuff */
  /* objects (not players) with the same owner control each other */
  /* No one controls garbage */

  if (Typeof(what) == TYPE_GARBAGE) return 0;
  if(FLAGS(who)&QUELL) return 0;

  if(who == OWNER(what)) return 1;

  switch(Typeof(who)) {
  case TYPE_PLAYER:
#ifdef GOD_PRIV
    if(is_god(who)) return 1;
    if(Wizard(who))
      return (!is_god(what));
#else
    if(Wizard(who)) return 1;
#endif /* GOD_PRIV */
    if(Arch(who))
      return (!(FLAGS(what)&WIZARD));
    break;
  case TYPE_THING:
    if(Typeof(what) == TYPE_PLAYER) {
      if(Arch(who))
	return (!(FLAGS(what)&WIZARD));
    }
    else {
      if(what == OWNER(who)) return 0;
      if((OWNER(who) == OWNER(what)) && !(FLAGS(what)&WIZARD))
	return 1;
      if(Wizard(who))
	return 1;
      if(Arch(who))
	return(!(FLAGS(what)&WIZARD));
    }
    break;
  default:
    return 0;
  }
  return 0;
}

int restricted(dbref player, dbref thing, object_flag_type flag)
{
  switch (flag) {
  case ARCHITECT:
    return (!Wizard(player) && (Typeof(thing) != TYPE_ROOM));
    break;
  case DARK:
    return (!(Arch(player))
	    && (Typeof(thing) == TYPE_PLAYER)); /* no dark players. */
    /*NOTREACHED*/
    break;
  case MUCKER:
    return (!Wizard(player) && ((Typeof(thing) == TYPE_PROGRAM)
				|| (Typeof(thing) == TYPE_PLAYER)));
  case BUILDER:
    return (!Wizard(player));
    /*NOTREACHED*/
    break;
  case WIZARD:
    if (Wizard(player) || God(player)) {
      if(player != global_cause ||
	 player != global_trigger) return 1; /* @force, or something similar */
#ifdef GOD_PRIV
      return ((Typeof(thing) == TYPE_PLAYER) && !God(player));
#else /* !GOD_PRIV */
      return !Wizard(player);
#endif /* GOD_PRIV */
    } else return 1;
    /*NOTREACHED*/
    break;
  case STICKY:
    return ((Typeof(thing)==TYPE_PROGRAM) && !(Arch(player) && Mucker(player)));
    break;
  default:
    return 0;
    /*NOTREACHED*/
    break;
  }
}

int payfor(dbref who, int cost)
{
  if(Arch(who)) {
    return 1;
  } else if(DBFETCH(who)->sp.player.pennies >= cost) {
    DBFETCH(who)->sp.player.pennies -= cost;
    DBDIRTY(who);
    return 1;
  } else {
    return 0;
  }
}

int word_start (const char *str, const char let)
{
  int chk;
  
  for (chk = 1; *str; str++) {
    if (chk && *str == let) return 1;
    chk = *str == ' ';
  }
  return 0;
}

int ok_name(const char *name)
{
  return (name
	  && *name
	  && *name != LOOKUP_TOKEN
	  && *name != NUMBER_TOKEN
	  && !index(name, ARG_DELIMITER)
	  && !index(name, AND_TOKEN)
	  && !index(name, OR_TOKEN)
	  && !word_start(name, NOT_TOKEN)
	  && string_compare(name, "me")
	  && string_compare(name, "home")
	  && string_compare(name, "here"));
}

int ok_player_name(const char *name)
{
  const char *scan;
  
  if(!ok_name(name) || strlen(name) > PLAYER_NAME_LIMIT) return 0;
  
  for(scan = name; *scan; scan++) {
    if(!(isprint(*scan) && !isspace(*scan))) { /* was isgraph(*scan) */
      return 0;
    }
  }
  
  /* lookup name to avoid conflicts */
  return (lookup_player(name) == NOTHING);
}

int ok_password(const char *password)
{
  const char *scan;
  
  if(*password == '\0') return 0;
  
  for(scan = password; *scan; scan++) {
    if(!(isprint(*scan) && !isspace(*scan))) {
      return 0;
    }
  }
  
  return 1;
}

int is_ok(dbref what)
{
  if(what < 0) {
    return 0;
  } else {
    return(Typeof(what) != TYPE_GARBAGE);
  }
}

int Builder(dbref x)
{
  return(Arch(x) || FLAGS(x)&BUILDER);
}

int Arch(dbref x)
{
  return(Wizard(x) ||
	 ((Typeof(x) != TYPE_ROOM) &&
	  (FLAGS(x)&ARCHITECT && !(FLAGS(x)&QUELL))));
}

int God(dbref x)
{ return is_god(x); }

int Wizard(dbref x)
{ return (FLAGS(x)&WIZARD && !(FLAGS(x)&QUELL)); }

int Mucker(dbref x)
{
  return((Typeof(x) == TYPE_PLAYER) && (Wizard(x) || FLAGS(x)&MUCKER));
}

int Murky(dbref x)
{
  return(((Typeof(x) == TYPE_ROOM) || (Typeof(x) == TYPE_THING)
	  || (Typeof(x) == TYPE_EXIT)) &&
	 FLAGS(x)&MUCKER);
}

char *check_func(const char *x) { return alloc_string (x); }

#ifdef HAVEN
int Haven(dbref x) {return FLAGS(x)&HAVEN;}
#endif /* HAVEN */

int Halted(dbref x)
{
  return FLAGS(x)&HALTED;
}

int Dark(dbref x)
{
  if(Typeof(x) == TYPE_PLAYER && !notify_internal(x,NULL))
    return 1;
  if((Typeof(x) != TYPE_ROOM)
     && (FLAGS(DBFETCH(x)->location)&DARK)) return 1;
  return (FLAGS(x)&DARK);
}

int Silent(dbref x)
{
  return((Typeof(x) == TYPE_PLAYER) && (FLAGS(x)&STICKY));
}

int Paranoid(dbref x)
{
  return((Typeof(x) == TYPE_PLAYER) && (FLAGS(x)&PUPPET));
}

#define MAX_FUN_ARGS 50
void do_switch(dbref player, char *arg1, char *arg2, dbref cause)
{
  char *arg[MAX_FUN_ARGS], *ptrsrv[10];
  char buff[BUFFER_LEN];
  char *p;
  int x = 0, a = 0, any = 0;
  const char *delimit = ",";

  exec(&arg1, buff, player, cause, 0);
  arg[0] = alloc_ec_string(buff);

  for(x = 1; x < MAX_FUN_ARGS; x++)
    {
      arg[x] = NULL;
      p = (char *) parse_up(&arg2, delimit);
      if(p)
	arg[x] = alloc_ec_string(p);
    }

  if(!arg[1])
    return;

  for(x = 0; x < 10; x++)
    ptrsrv[x] = wptr[x];

  for(x = 1; x < (MAX_FUN_ARGS - 1) && arg[x] && arg[x + 1]; x += 2)
    if(wild_match(arg[x], arg[0])) {
      any = 1;
      for(a = 0; a < 10; a++)
	wptr[a] = ptrsrv[a];
      trigobj(player, trash_braces(arg[x + 1]), cause);
    }

  if(x < MAX_FUN_ARGS && !any && arg[x])
    {
      for(a = 0; a < 10; a++)
	wptr[a] = ptrsrv[a];
      trigobj(player, trash_braces(arg[x]), cause);
    }
}