tinymux/mux/modules/engine/rob.cpp
Stephen Dennis f1df1759ae nls: first format-string slice — rob give/kill templates (#1419)
Phase-2 constant-notify work banned printf conversions from the catalogue
and from M_() at format call sites. Whole-sentence formats like
"You give %d %s to %s." cannot be localised that way, so Phase 3 needs a
different contract:

  * tests/format/check_formats.py accepts M_("…") as a constant format and
    still checks the *source* msgid against mux_vsnprintf's conversions.
  * tests/nls/check_nls.py no longer bans formats in the pot; it requires
    every catalogue msgstr to carry the same conversion sequence (type and
    order) as its msgid. mux_vsnprintf has no positional %1$s yet.

Content: mark rob.cpp's remaining player/staff format templates with M_
(tprintf, mux_sprintf, raw_broadcast for give/kill/suspect). Constants
there were already M_ from the earlier rob notify slice. Regenerate pot,
xx, and the .mo without fuzzy entries.

Verified: make test-format, make test-nls.
2026-07-27 13:56:53 +00:00

443 lines
12 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*! \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)
{
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';
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';
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);
}
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));
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);
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))
{
give_money(executor, recipient, key, mux_atoi64(amnt));
}
else
{
give_thing(executor, recipient, key, amnt);
}
}