/**
* \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;
}