pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file rob.c
 *
 * \brief Kill and give.
 *
 * This file is called rob.c for historical reasons, and one day it'll
 * probably get folded into some other file.
 *
 *
 */

#include "config.h"
#include "copyrite.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "conf.h"
#include "externs.h"
#include "mushdb.h"
#include "attrib.h"
#include "match.h"
#include "parse.h"
#include "flags.h"
#include "log.h"
#include "lock.h"
#include "dbdefs.h"
#include "game.h"
#include "confmagic.h"
#include "case.h"

static void do_give_to(dbref player, char *arg, int silent);

/** Set an object's money value, with limit-checking.
 * \param thing dbref of object.
 * \param amount amount to set object's pennies to.
 */
void
s_Pennies(dbref thing, int amount)
{
  if (amount < 0)
    amount = 0;
  else if (amount > HUGE_INT)
    amount = HUGE_INT;
  Pennies(thing) = amount;
}


/** The kill command - send an object back home.
 * \param player the enactor.
 * \param what name of object to kill.
 * \param cost amount to pay to kill.
 * \param slay if 1, this is the wizard 'slay' command instead.
 */
void
do_kill(dbref player, const char *what, int cost, int slay)
{
  dbref victim;
  char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN], *tp;

  if (slay && !Wizard(player)) {
    notify(player, T("You do not have such power."));
    return;
  }
  victim = match_result(player, what, TYPE_PLAYER, MAT_NEAR_THINGS);

  if (player == victim) {
    notify(player, T("No suicide allowed."));
    return;
  }
  if (slay)
    do_log(LT_WIZ, player, victim, "SLAY");

  switch (victim) {
  case NOTHING:
    notify(player, T("I don't see that here."));
    break;
  case AMBIGUOUS:
    notify(player, T("I don't know what you mean!"));
    break;
  default:
    if (Suspect(player))
      flag_broadcast("WIZARD", 0,
		     T("Broadcast: Suspect %s tried to kill %s(#%d)."),
		     Name(player), Name(victim), victim);
    if (!Mobile(victim)) {
      notify(player, T("Sorry, you can only kill players and objects."));
    } else if ((Haven(Location(victim)) &&
		!Wizard(player)) ||
	       (controls(victim, Location(victim)) &&
		!controls(player, Location(victim)))) {
      notify(player, "Sorry.");
    } else if (NoKill(victim) && !Wizard(player) && (Owner(victim) != player)) {
      notify(player, T("That object cannot be killed."));
    } else {
      /* go for it */
      /* set cost */
      /* if this isn't called via slay */
      if (!slay) {
	if (cost < KILL_MIN_COST)
	  cost = KILL_MIN_COST;

	/* see if it works */
	if (!payfor(player, cost)) {
	  notify_format(player, T("You don't have enough %s."), MONIES);
	  break;
	}
      }
      if (((get_random_long(0, KILL_BASE_COST) < cost) || slay) &&
	  !Wizard(victim)) {
	/* you killed him */
	tp = tbuf1;
	safe_format(tbuf1, &tp, T("You killed %s!"), Name(victim));
	*tp = '\0';
	tp = tbuf2;
	safe_format(tbuf2, &tp, "killed %s!", Name(victim));
	*tp = '\0';
	do_halt(victim, "", victim);
	did_it(player, victim, "DEATH", tbuf1, "ODEATH", tbuf2, "ADEATH",
	       NOTHING);

	/* notify victim */
	notify_format(victim, T("%s killed you!"), Name(player));

	/* maybe pay off the bonus */
	/* if we were not called via slay */
	if (!slay) {
	  int payoff = cost * KILL_BONUS / 100;
	  if (payoff + Pennies(Owner(victim)) > Max_Pennies(Owner(victim)))
	    payoff = Max_Pennies(Owner(victim)) - Pennies(Owner(victim));
	  if (payoff > 0) {
	    notify_format(victim, T("Your insurance policy pays %d %s."),
			  payoff, ((payoff == 1) ? MONEY : MONIES));
	    giveto(Owner(victim), payoff);
	  } else {
	    notify(victim, T("Your insurance policy has been revoked."));
	  }
	}
	/* send him home */
	safe_tel(victim, HOME, 0);
	/* if victim is object also dequeue all commands */
      } else {
	/* notify player and victim only */
	notify(player, T("Your murder attempt failed."));
	notify_format(victim, T("%s tried to kill you!"), Name(player));
      }
      break;
    }
  }
}

/** the buy command
 * \param player the enactor/buyer
 * \param item the item to buy
 * \param from who to buy it from
 * \param cost the cost
 */
void
do_buy(dbref player, char *item, char *from, int price)
{
  dbref vendor;
  char *prices;
  char *plus;
  char *cost;
  char finditem[BUFFER_LEN];
  char buycost[BUFFER_LEN];
  int boughtit;
  int len;
  char buff[BUFFER_LEN], *bp;
  char obuff[BUFFER_LEN];
  char *r[BUFFER_LEN / 2];
  char *c[BUFFER_LEN / 2];
  char *buy_env[10] = { NULL };
  int affordable;
  int costcount, ci;
  int count, i;
  int low, high;		/* lower bound, upper bound of cost */
  ATTR *a;

  if (!GoodObject(Location(player)))
    return;

  vendor = Contents(Location(player));
  if (vendor == player)
    vendor = Next(player);

  if (from != NULL && *from) {
    switch (vendor =
	    match_result(player, from, TYPE_PLAYER,
			 MAT_NEAR_THINGS | MAT_ENGLISH)) {
    case NOTHING:
      notify(player, T("Buy from whom?"));
      return;
    case AMBIGUOUS:
      notify(player, T("I don't know who you mean!"));
      return;
    }
    if (vendor == player) {
      notify(player, T("You can't buy from yourself!"));
      return;
    }
  } else if (vendor == NOTHING) {
    notify(player, T("There's nobody here to buy things from."));
    return;
  } else {
    from = NULL;
  }

  if (!item || !*item || !(item = trim_space_sep(item, ' '))) {
    notify(player, T("Buy what?"));
    return;
  }

  bp = finditem;
  safe_str(item, finditem, &bp);
  safe_chr(':', finditem, &bp);
  *bp = '\0';
  for (bp = strchr(finditem, ' '); bp; bp = strchr(bp, ' '))
    *bp = '_';

  len = strlen(finditem);

  /* Scan pricelists */
  boughtit = -1;
  affordable = 1;
  do {
    a = atr_get(vendor, "PRICELIST");
    if (!a)
      continue;
    /* atr_value uses a static buffer, so we'll take advantage of that */
    prices = atr_value(a);
    upcasestr(prices);
    count = list2arr(r, BUFFER_LEN / 2, atr_value(a), ' ');
    if (!count)
      continue;
    for (i = 0; i < count; i++) {
      if (!strncasecmp(finditem, r[i], len)) {
	/* Check cost */
	cost = r[i] + len;
	if (!*cost)
	  continue;
	costcount = list2arr(c, BUFFER_LEN / 2, cost, ',');
	for (ci = 0; ci < costcount; ci++) {
	  cost = c[ci];
	  /* Formats:
	   * 10,2000+,10-100
	   */
	  if ((plus = strchr(cost, '-'))) {
	    *(plus++) = '\0';
	    if (!is_strict_integer(cost))
	      continue;
	    if (!is_strict_integer(plus))
	      continue;
	    low = parse_integer(cost);
	    high = parse_integer(plus);
	    if (price < 0) {
	      boughtit = low;
	    } else if (price >= low && price <= high) {
	      boughtit = price;
	    }
	  } else if ((plus = strchr(cost, '+'))) {
	    *(plus++) = '\0';
	    if (!is_strict_integer(cost))
	      continue;
	    low = parse_integer(cost);
	    if (price < 0) {
	      boughtit = low;
	    } else if (price > low) {
	      boughtit = price;
	    }
	  } else if (is_strict_integer(cost)) {
	    low = parse_integer(cost);
	    if (price < 0) {
	      boughtit = low;
	    } else if (low == price) {
	      boughtit = price;
	    }
	  } else {
	    continue;
	  }
	  if (boughtit >= 0) {
	    if (!payfor(player, boughtit)) {
	      affordable = 0;
	      boughtit = 0;
	      continue;
	    }
	    bp = strchr(finditem, ':');
	    if (bp)
	      *bp = '\0';
	    for (bp = finditem; *bp; bp++)
	      *bp = DOWNCASE(*bp);
	    bp = buff;
	    safe_format(buff, &bp, "You buy a %s from %s.",
			finditem, Name(vendor));
	    *bp = '\0';
	    bp = obuff;
	    safe_format(obuff, &bp, "buys a %s from %s.",
			finditem, Name(vendor));
	    buy_env[0] = finditem;
	    buy_env[1] = buycost;
	    bp = buycost;
	    safe_integer(boughtit, buycost, &bp);
	    *bp = '\0';
	    real_did_it(player, vendor, "BUY", buff, "OBUY", obuff, "ABUY",
			NOTHING, buy_env, NA_INTER_SEE);
	    return;
	  }
	}
      }
    }
  } while (!from && ((vendor = Next(vendor)) != NOTHING));

  if (price >= 0) {
    if (!from) {
      notify(player, T("I can't find that item with that price here."));
    } else {
      notify_format(player, T("%s isn't selling that item for that price"),
		    Name(vendor));
    }
  } else if (affordable) {
    if (!from) {
      notify(player, T("I can't find that item here."));
    } else {
      notify_format(player, T("%s isn't selling that item."), Name(vendor));
    }
  } else {
    notify(player, T("You can't afford that."));
  }
}

/** The give command.
 * \param player the enactor/giver.
 * \param recipient name of object to receive.
 * \param amnt name of object to be transferred, or amount of pennies.
 * \param silent if 1, hush the usual messages.
 */
void
do_give(dbref player, char *recipient, char *amnt, int silent)
{
  dbref who;
  int amount;
  char tbuf1[BUFFER_LEN];
  char *bp;

  /* If we have a recipient, but no amnt, try parsing for
   * 'give <amnt> to <recipient>' instead of 'give <recipient>=<amount>'
   */
  if (recipient && *recipient && (!amnt || !*amnt)) {
    do_give_to(player, recipient, silent);
    return;
  }

  /* check recipient */
  switch (who =
	  match_result(player, recipient, TYPE_PLAYER,
		       MAT_NEAR_THINGS | MAT_ENGLISH)) {
  case NOTHING:
    notify(player, T("Give to whom?"));
    return;
  case AMBIGUOUS:
    notify(player, T("I don't know who you mean!"));
    return;
  }

  /* Can't give to garbage... */
  if (IsGarbage(who)) {
    notify(player, T("Give to whom?"));
    return;
  }

  if (!is_strict_integer(amnt)) {
    /* We're giving an object */
    dbref thing;
    switch (thing =
	    match_result(player, amnt, TYPE_THING,
			 MAT_POSSESSION | MAT_ENGLISH)) {
    case NOTHING:
      notify(player, T("You don't have that!"));
      return;
    case AMBIGUOUS:
      notify(player, T("I don't know which you mean!"));
      return;
    default:
      /* if you can give yourself, that's like "enter". since we
       * do no lock check with give, we shouldn't be able to
       * do this.
       */
      if (thing == player) {
	notify(player, T("You can't give yourself away!"));
	return;
      }
      /* Don't give things to themselves. */
      if (thing == who) {
	notify(player, T("You can't give an object to itself!"));
	return;
      }
      if (!eval_lock(player, thing, Give_Lock)) {
	notify(player, T("You can't give that away."));
	return;
      }
      if (Mobile(thing) && (EnterOk(who) || controls(player, who))) {
	moveto(thing, who);

	/* Notify the giver with their GIVE message */
	bp = tbuf1;
	safe_format(tbuf1, &bp, T("You gave %s to %s."), Name(thing),
		    Name(who));
	*bp = '\0';
	did_it_with(player, player, "GIVE", tbuf1, "OGIVE", NULL,
		    "AGIVE", NOTHING, thing, who, NA_INTER_SEE);

	/* Notify the object that it's been given */
	notify_format(thing, T("%s gave you to %s."), Name(player), Name(who));

	/* Recipient gets success message on thing and receive on self */
	did_it(who, thing, "SUCCESS", NULL, "OSUCCESS", NULL, "ASUCCESS",
	       NOTHING);
	bp = tbuf1;
	safe_format(tbuf1, &bp, T("%s gave you %s."), Name(player),
		    Name(thing));
	*bp = '\0';
	did_it_with(who, who, "RECEIVE", tbuf1, "ORECEIVE", NULL,
		    "ARECEIVE", NOTHING, thing, player, NA_INTER_SEE);
      } else
	notify(player, T("Permission denied."));
    }
    return;
  }
  /* At this point, we're giving an amount. */
  amount = parse_integer(amnt);
  if (Pennies(who) + amount > Max_Pennies(who))
    amount = Max_Pennies(who) - Pennies(who);
  if (amount < 0 && !Can_Debit(player)) {
    notify(player, T("What is this, a holdup?"));
    return;
  } else if (amount == 0) {
    notify_format(player,
		  T("You must specify a positive number of %s."), MONIES);
    return;
  }
  if (Can_Debit(player) && (amount < 0) && (Pennies(who) + amount < 0))
    amount = -Pennies(who);
  /* try to do the give */
  if (!Moneybags(player) && !payfor(player, amount)) {
    notify_format(player, T("You don't have that many %s to give!"), MONIES);
  } else {
    char *pay_env[10] = { NULL };
    char paid[SBUF_LEN], *pb;

    pay_env[0] = pb = paid;

    /* objects work differently */
    if (IsThing(who)) {
      /* give pennies to an object */
      int cost = 0;
      ATTR *a;
      char *preserveq[NUMQ];
      char *preserves[10];
      char fbuff[BUFFER_LEN];
      char *fbp, *asave;
      char const *ap;

      a = atr_get(who, "COST");
      if (!a) {
	/* No cost attribute */
	notify_format(player, T("%s refuses your money."), Name(who));
	giveto(player, amount);
	return;
      }
      save_global_regs("give_save", preserveq);
      save_global_env("give_save", preserves);
      asave = safe_atr_value(a);
      ap = asave;
      fbp = fbuff;
      safe_integer_sbuf(amount, paid, &pb);
      *pb = '\0';
      global_eval_context.wenv[0] = paid;
      process_expression(fbuff, &fbp, &ap, who, player, player,
			 PE_DEFAULT, PT_DEFAULT, NULL);
      *fbp = '\0';
      free((Malloc_t) asave);
      restore_global_regs("give_save", preserveq);
      restore_global_env("give_save", preserves);
      if (amount < (cost = atoi(fbuff))) {
	notify(player, T("Feeling poor today?"));
	giveto(player, amount);
	return;
      }
      if (cost < 0)
	return;
      if ((amount - cost) > 0) {
	notify_format(player, T("You get %d in change."), amount - cost);
      } else {
	notify_format(player, T("You paid %d %s."), amount,
		      ((amount == 1) ? MONEY : MONIES));
      }
      giveto(player, amount - cost);
      giveto(who, cost);
      pb = paid;
      safe_integer_sbuf(cost, paid, &pb);
      *pb = '\0';
      real_did_it(player, who, "PAYMENT", NULL, "OPAYMENT", NULL, "APAYMENT",
		  NOTHING, pay_env, NA_INTER_SEE);
      return;
    } else {
      /* give pennies to a player */
      if (amount > 0) {
	notify_format(player,
		      T("You give %d %s to %s."), amount,
		      ((amount == 1) ? MONEY : MONIES), Name(who));
      } else {
	notify_format(player, T("You took %d %s from %s!"), abs(amount),
		      ((abs(amount) == 1) ? MONEY : MONIES), Name(who));
      }
      if (IsPlayer(who) && !silent) {
	if (amount > 0) {
	  notify_format(who, T("%s gives you %d %s."), Name(player),
			amount, ((amount == 1) ? MONEY : MONIES));
	} else {
	  notify_format(who, T("%s took %d %s from you!"), Name(player),
			abs(amount), ((abs(amount) == 1) ? MONEY : MONIES));
	}
      }
      giveto(who, amount);
      safe_integer_sbuf(amount, paid, &pb);
      *pb = '\0';
      real_did_it(player, who, "PAYMENT", NULL, "OPAYMENT", NULL, "APAYMENT",
		  NOTHING, pay_env, NA_INTER_SEE);
    }
  }
}



/** The other syntax of the give command.
 * \param player the enactor/giver.
 * \param arg "something to someone".
 * \param silent if 1, hush the usual messages.
 */
static void
do_give_to(dbref player, char *arg, int silent)
{
  char *s;

  /* Parse out the object and recipient */
  upcasestr(arg);
  s = (char *) string_match(arg, "TO ");
  if (!s) {
    notify(player, T("Did you want to give something *to* someone?"));
    return;
  }
  while ((s > arg) && isspace(*(s - 1))) {
    s--;
  }
  if (s == arg) {
    notify(player, T("Give what?"));
    return;
  }
  *s++ = '\0';
  s = (char *) string_match(s, "TO ");
  s += 3;
  while (*s && isspace(*s))
    s++;
  if (!*s) {
    notify(player, T("Give to whom?"));
    return;
  }
  /* At this point, 'arg' is the object, and 's' is the recipient.
   * But be double-safe to be sure we don't loop.
   */
  if (!*arg || !*s) {
    notify(player, T("I don't know what you mean."));
    return;
  }
  do_give(player, s, arg, silent);
  return;
}