tinymux/mux/modules/engine/rob.cpp

444 lines
12 KiB
C++
Raw Permalink Normal View History

/*! \file rob.cpp
* \brief Commands dealing with giving/taking/killing things or money.
*
*/
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
void do_kill
(
dbref executor,
dbref caller,
dbref enactor,
int eval,
int key,
int nargs,
UTF8 *what,
UTF8 *costchar,
const UTF8 *cargs[],
int ncargs
)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(nargs);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
init_match(executor, what, TYPE_PLAYER);
match_neighbor();
match_me();
match_here();
if (Long_Fingers(executor))
{
match_player();
match_absolute();
}
dbref victim = match_result();
switch (victim)
{
case NOTHING:
notify(executor, M_("I dont see that player here."));
break;
case AMBIGUOUS:
notify(executor, M_("I dont know who you mean!"));
break;
default:
if ( !isPlayer(victim)
&& !isThing(victim))
{
notify(executor, M_("Sorry, you can only kill players and things."));
break;
}
if ( ( Haven(Location(victim))
&& !Wizard(executor))
|| ( Controls(victim, Location(victim))
&& !Controls(executor, Location(victim)))
|| Unkillable(victim))
{
notify(executor, M_("Sorry."));
break;
}
// Go for it.
//
int cost = 0;
if (key == KILL_KILL)
{
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373) Completes the sweep the issue called for. mux_atol returns long, which is 32-bit on LLP64, so every caller silently truncated on Windows. Two of those were real defects (the truthiness family and cf_size, fixed in the preceding commits); the rest were latent, waiting for a value large enough to matter. Rather than audit 290 sites for whether each can reach 2^31 today, use the 64-bit parser everywhere and remove the class. A dbref cannot overflow now, but nothing stops a later caller passing that same site a timestamp or a byte count. Pure 1:1 substitution: 285 lines changed, and every removed line contained mux_atol while every added line contains mux_atoi64. No control flow, no types, no behaviour beyond the wider parse. This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so the generated code there is unchanged. It only widens the parse on Windows. Narrowing destinations are unaffected either way: `int x = mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both models. Left alone: mux_atol itself in mathutil, its declaration, and three comments that name it. Callers that genuinely want 32-bit semantics can still ask for them; none appear to. Verified on Windows: full solution builds clean with no new warnings, smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched -- identical to before the sweep, with the same 16 build-configuration failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST). Spot checks after the change: the boolean family returns 1 for multiples of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited, and arithmetic, string and list functions are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
cost = mux_atoi64(costchar);
if (cost < mudconf.killmin)
{
cost = mudconf.killmin;
}
if (cost > mudconf.killmax)
{
cost = mudconf.killmax;
}
// See if it works.
//
if (!payfor(executor, cost))
{
notify(executor, tprintf(M_("You dont have enough %s."), mudconf.many_coins));
return;
}
}
if ( RealWizard(victim)
|| ( 0 < mudconf.killguarantee
&& !( RandomINT32(0, mudconf.killguarantee-1) < cost
|| key == KILL_SLAY)))
{
// Failure: notify player and victim only.
//
notify(executor, M_("Your murder attempt failed."));
{
LBuf buf1 = LBuf_Src("do_kill.failed");
mux_sprintf(buf1, LBUF_SIZE, M_("%s tried to kill you!"), Moniker(executor));
notify_with_cause_ooc(victim, executor, buf1, MSG_SRC_KILL);
if (Suspect(executor))
{
mux_strncpy(buf1, Moniker(executor), LBUF_SIZE-1);
if (executor == Owner(executor))
{
raw_broadcast(WIZARD, M_("[Suspect] %s tried to kill %s(#%d)."), buf1.get(), Moniker(victim), victim);
}
else
{
LBuf buf2 = LBuf_Src("do_kill.SUSP.failed");
mux_strncpy(buf2, Moniker(Owner(executor)), LBUF_SIZE-1);
raw_broadcast(WIZARD, M_("[Suspect] %s <via %s(#%d)> tried to kill %s(#%d)."),
buf2.get(), buf1.get(), executor, Moniker(victim), victim);
}
}
}
break;
}
// Success! You killed him
//
{
LBuf buf1 = LBuf_Src("do_kill.succ.1");
LBuf buf2 = LBuf_Src("do_kill.succ.2");
if (Suspect(executor))
{
mux_strncpy(buf1, Moniker(executor), LBUF_SIZE-1);
if (executor == Owner(executor))
{
raw_broadcast(WIZARD, M_("[Suspect] %s killed %s(#%d)."), buf1.get(), Moniker(victim), victim);
}
else
{
mux_strncpy(buf2, Moniker(Owner(executor)), LBUF_SIZE-1);
raw_broadcast(WIZARD, M_("[Suspect] %s <via %s(#%d)> killed %s(#%d)."),
buf2.get(), buf1.get(), executor, Moniker(victim), victim);
}
}
mux_sprintf(buf1, LBUF_SIZE, M_("You killed %s!"), Moniker(victim));
mux_sprintf(buf2, LBUF_SIZE, M_("killed %s!"), Moniker(victim));
if (!isPlayer(victim))
{
if (halt_que(NOTHING, victim) > 0)
{
if (!Quiet(victim))
{
notify(Owner(victim), M_("Halted."));
}
}
}
did_it(executor, victim, A_KILL, buf1, A_OKILL, buf2, A_AKILL, 0,
nullptr, 0);
// notify victim
//
mux_sprintf(buf1, LBUF_SIZE, M_("%s killed you!"), Moniker(executor));
notify_with_cause_ooc(victim, executor, buf1, MSG_SRC_KILL);
// Pay off the bonus.
//
if (key == KILL_KILL)
{
cost /= 2; // Victim gets half.
if (Pennies(Owner(victim)) < mudconf.paylimit)
{
mux_sprintf(buf1, LBUF_SIZE, M_("Your insurance policy pays %d %s."), cost,
mudconf.many_coins);
notify(victim, buf1);
giveto(Owner(victim), cost);
}
else
{
notify(victim, M_("Your insurance policy has been revoked."));
}
}
}
// Send him home.
//
move_via_generic(victim, HOME, NOTHING, 0);
divest_object(victim);
break;
}
}
/*
* ---------------------------------------------------------------------------
* * give_thing, give_money, do_give: Give away money or things.
*/
static void give_thing(dbref giver, dbref recipient, int key, UTF8 *what)
{
init_match(giver, what, TYPE_THING);
match_possession();
match_me();
dbref thing = match_result();
switch (thing)
{
case NOTHING:
notify(giver, M_("You dont have that!"));
return;
case AMBIGUOUS:
notify(giver, M_("I dont know which you mean!"));
return;
}
if (thing == giver)
{
notify(giver, M_("You cant give yourself away!"));
return;
}
if (thing == recipient)
{
notify(giver, M_("You cant give an object to itself."));
return;
}
if ( (!isThing(thing) && !isPlayer(thing))
|| !( Enter_ok(recipient)
|| Controls(giver, recipient))
|| isGarbage(recipient))
{
notify(giver, NOPERM_MESSAGE);
return;
}
if (!could_doit(giver, thing, A_LGIVE))
{
LBuf str = LBuf_Src("do_give.gfail");
UTF8 *sp = str.get();
safe_str(T("You cant give "), str, &sp);
safe_str(Moniker(thing), str, &sp);
safe_str(T(" away."), str, &sp);
*sp = '\0';
2018-10-03 17:54:51 +00:00
did_it(giver, thing, A_GFAIL, str, A_OGFAIL, nullptr, A_AGFAIL, 0,
nullptr, 0);
return;
}
if (!could_doit(thing, recipient, A_LRECEIVE))
{
LBuf str = LBuf_Src("do_give.rfail");
UTF8 *sp = str.get();
safe_str(Moniker(recipient), str, &sp);
safe_str(T(" doesnt want "), str, &sp);
safe_str(Moniker(thing), str, &sp);
safe_chr('.', str, &sp);
*sp = '\0';
2018-10-03 17:54:51 +00:00
did_it(giver, recipient, A_RFAIL, str, A_ORFAIL, nullptr, A_ARFAIL, 0,
nullptr, 0);
return;
}
move_via_generic(thing, recipient, giver, 0);
divest_object(thing);
if (!(key & GIVE_QUIET))
{
LBuf str = LBuf_Src("do_give.thing.ok");
mux_strncpy(str, Moniker(giver), LBUF_SIZE-1);
notify_with_cause_ooc(recipient, giver, tprintf(M_("%s gave you %s."), str.get(), Moniker(thing)), MSG_SRC_GIVE);
notify(giver, M_("Given."));
notify_with_cause_ooc(thing, giver, tprintf(M_("%s gave you to %s."), str.get(), Moniker(recipient)), MSG_SRC_GIVE);
}
2018-10-03 17:54:51 +00:00
did_it(giver, thing, A_DROP, nullptr, A_ODROP, nullptr, A_ADROP, 0, nullptr, 0);
did_it(recipient, thing, A_SUCC, nullptr, A_OSUCC, nullptr, A_ASUCC, 0,
nullptr, 0);
}
static void give_money(dbref giver, dbref recipient, int key, int amount)
{
// Do amount consistency check.
//
if ( amount < 0
&& !Steal(giver))
{
notify(giver, tprintf(M_("You look through your pockets. Nope, no negative %s."),
mudconf.many_coins));
return;
}
if (!amount)
{
notify(giver, tprintf(M_("You must specify a positive number of %s."),
mudconf.many_coins));
return;
}
if (!Wizard(giver))
{
if ( isPlayer(recipient)
&& (Pennies(recipient) + amount > mudconf.paylimit))
{
notify(giver, tprintf(M_("That player doesnt need that many %s!"),
mudconf.many_coins));
return;
}
if (!could_doit(giver, recipient, A_LUSE))
{
notify(giver, tprintf(M_("%s wont take your money."), Moniker(recipient)));
return;
}
}
// Try to do the give.
//
if (!payfor(giver, amount))
{
notify(giver, tprintf(M_("You dont have that many %s to give!"),
mudconf.many_coins));
return;
}
// Find out cost if an object.
//
int cost;
if (isThing(recipient))
{
dbref aowner;
int aflags;
LBuf str = LBuf_Adopt(atr_pget(recipient, A_COST, &aowner, &aflags));
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373) Completes the sweep the issue called for. mux_atol returns long, which is 32-bit on LLP64, so every caller silently truncated on Windows. Two of those were real defects (the truthiness family and cf_size, fixed in the preceding commits); the rest were latent, waiting for a value large enough to matter. Rather than audit 290 sites for whether each can reach 2^31 today, use the 64-bit parser everywhere and remove the class. A dbref cannot overflow now, but nothing stops a later caller passing that same site a timestamp or a byte count. Pure 1:1 substitution: 285 lines changed, and every removed line contained mux_atol while every added line contains mux_atoi64. No control flow, no types, no behaviour beyond the wider parse. This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so the generated code there is unchanged. It only widens the parse on Windows. Narrowing destinations are unaffected either way: `int x = mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both models. Left alone: mux_atol itself in mathutil, its declaration, and three comments that name it. Callers that genuinely want 32-bit semantics can still ask for them; none appear to. Verified on Windows: full solution builds clean with no new warnings, smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched -- identical to before the sweep, with the same 16 build-configuration failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST). Spot checks after the change: the boolean family returns 1 for multiples of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited, and arithmetic, string and list functions are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
cost = mux_atoi64(str);
// Can't afford it?
//
if (amount < cost)
{
notify(giver, M_("Feeling poor today?"));
giveto(giver, amount);
return;
}
// Negative cost
//
if (cost < 0)
{
return;
}
}
else
{
cost = amount;
}
if (!(key & GIVE_QUIET))
{
if (amount == 1)
{
notify(giver, tprintf(M_("You give a %s to %s."), mudconf.one_coin, Moniker(recipient)));
notify_with_cause_ooc(recipient, giver, tprintf(M_("%s gives you a %s."), Moniker(giver), mudconf.one_coin), MSG_SRC_GIVE);
}
else
{
notify(giver, tprintf(M_("You give %d %s to %s."), amount, mudconf.many_coins, Moniker(recipient)));
notify_with_cause_ooc(recipient, giver, tprintf(M_("%s gives you %d %s."),
Moniker(giver), amount, mudconf.many_coins), MSG_SRC_GIVE);
}
}
// Report change given
//
if ((amount - cost) == 1)
{
notify(giver, tprintf(M_("You get 1 %s in change."), mudconf.one_coin));
giveto(giver, 1);
}
else if (amount != cost)
{
notify(giver, tprintf(M_("You get %d %s in change."), (amount - cost), mudconf.many_coins));
giveto(giver, (amount - cost));
}
// Transfer the money and run PAY attributes
//
giveto(recipient, cost);
2018-10-03 17:54:51 +00:00
did_it(giver, recipient, A_PAY, nullptr, A_OPAY, nullptr, A_APAY, 0, nullptr, 0);
return;
}
void do_give
(
dbref executor,
dbref caller,
dbref enactor,
int eval,
int key,
int nargs,
UTF8 *who,
UTF8 *amnt,
const UTF8 *cargs[],
int ncargs
)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(nargs);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
// Check recipient.
//
init_match(executor, who, TYPE_PLAYER);
match_neighbor();
match_possession();
match_me();
if (Long_Fingers(executor))
{
match_player();
match_absolute();
}
dbref recipient = match_result();
switch (recipient)
{
case NOTHING:
notify(executor, M_("Give to whom?"));
return;
case AMBIGUOUS:
notify(executor, M_("I dont know who you mean!"));
return;
}
if (isExit(recipient))
{
notify(executor, M_("You cant give anything to an exit."));
return;
}
if (Guest(recipient))
{
notify(executor, M_("Guests really dont need money or anything."));
return;
}
if (is_rational(amnt))
{
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373) Completes the sweep the issue called for. mux_atol returns long, which is 32-bit on LLP64, so every caller silently truncated on Windows. Two of those were real defects (the truthiness family and cf_size, fixed in the preceding commits); the rest were latent, waiting for a value large enough to matter. Rather than audit 290 sites for whether each can reach 2^31 today, use the 64-bit parser everywhere and remove the class. A dbref cannot overflow now, but nothing stops a later caller passing that same site a timestamp or a byte count. Pure 1:1 substitution: 285 lines changed, and every removed line contained mux_atol while every added line contains mux_atoi64. No control flow, no types, no behaviour beyond the wider parse. This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so the generated code there is unchanged. It only widens the parse on Windows. Narrowing destinations are unaffected either way: `int x = mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both models. Left alone: mux_atol itself in mathutil, its declaration, and three comments that name it. Callers that genuinely want 32-bit semantics can still ask for them; none appear to. Verified on Windows: full solution builds clean with no new warnings, smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched -- identical to before the sweep, with the same 16 build-configuration failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST). Spot checks after the change: the boolean family returns 1 for multiples of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited, and arithmetic, string and list functions are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
give_money(executor, recipient, key, mux_atoi64(amnt));
}
else
{
give_thing(executor, recipient, key, amnt);
}
}