mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Maintainer direction: prefer int64_t over long/int for parsed softcode and attribute integers so LLP64 and LP64 behave the same. This first slice covers the leftovers called out on the issue: - quota: store/show/set paths (avoids @quota/all 2^32 writing 0) - A_CHARGES in set/levels/predicates (RUNOUT vs wrap-to-positive) - semaphore counts and @ps/queue kick/warp parsing - IDLETIMEOUT attribute seconds Bounded API edges (RunTasks(int), optional int *pnum) clamp after the full-width parse. pot: quota notify formats use %lld.
2508 lines
62 KiB
C++
2508 lines
62 KiB
C++
/*! \file set.cpp
|
||
* \brief Commands which modify objects.
|
||
*
|
||
* These functions primarily implement commands like \@set and
|
||
* \@name that modify attributes or basic properties of an object,
|
||
* but this file also includes \@trigger and the use command.
|
||
*/
|
||
|
||
#include "copyright.h"
|
||
#include "autoconf.h"
|
||
#include "config.h"
|
||
#include "externs.h"
|
||
#include "routing.h"
|
||
|
||
void set_modified(dbref thing)
|
||
{
|
||
CLinearTimeAbsolute ltaNow;
|
||
ltaNow.GetLocal();
|
||
atr_add_raw(thing, A_MODIFIED, ltaNow.ReturnDateString(7));
|
||
}
|
||
|
||
dbref match_controlled_handler(dbref executor, const UTF8 *name, bool bQuiet)
|
||
{
|
||
dbref mat;
|
||
init_match(executor, name, NOTYPE);
|
||
match_everything(MAT_EXIT_PARENTS);
|
||
if (bQuiet)
|
||
{
|
||
mat = match_result();
|
||
}
|
||
else
|
||
{
|
||
mat = noisy_match_result();
|
||
}
|
||
if (!Good_obj(mat))
|
||
{
|
||
return mat;
|
||
}
|
||
|
||
if (Controls(executor, mat))
|
||
{
|
||
if (NoModify(mat) && !WizRoy(executor))
|
||
{
|
||
if (!bQuiet)
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
return NOTHING;
|
||
}
|
||
return mat;
|
||
}
|
||
if (!bQuiet)
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
return NOTHING;
|
||
}
|
||
|
||
void do_chzone
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *newobj,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
if (!mudconf.have_zones)
|
||
{
|
||
notify(executor, M_("Zones disabled."));
|
||
return;
|
||
}
|
||
init_match(executor, name, NOTYPE);
|
||
match_everything(0);
|
||
dbref thing = noisy_match_result();
|
||
if (thing == NOTHING)
|
||
{
|
||
return;
|
||
}
|
||
|
||
dbref zone;
|
||
if ( newobj[0] == '\0'
|
||
|| !mux_stricmp(newobj, T("none")))
|
||
{
|
||
zone = NOTHING;
|
||
}
|
||
else
|
||
{
|
||
init_match(executor, newobj, NOTYPE);
|
||
match_everything(0);
|
||
zone = noisy_match_result();
|
||
if (zone == NOTHING)
|
||
{
|
||
return;
|
||
}
|
||
if ( !isThing(zone)
|
||
&& !isRoom(zone))
|
||
{
|
||
notify(executor, M_("Invalid zone object type."));
|
||
return;
|
||
}
|
||
}
|
||
|
||
if ( !Wizard(executor)
|
||
&& !Controls(executor, thing)
|
||
&& !check_zone_handler(executor, thing, true)
|
||
&& db[executor].owner != db[thing].owner)
|
||
{
|
||
notify(executor, M_("You don’t have the power to shift reality."));
|
||
return;
|
||
}
|
||
|
||
if (NoModify(thing) && !WizRoy(executor))
|
||
{
|
||
notify(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
// A player may change an object's zone to NOTHING or to an object he owns.
|
||
//
|
||
if ( zone != NOTHING
|
||
&& !Wizard(executor)
|
||
&& !Controls(executor, zone)
|
||
&& db[executor].owner != db[zone].owner)
|
||
{
|
||
notify(executor, M_("You cannot move that object to that zone."));
|
||
return;
|
||
}
|
||
|
||
// Only rooms may be zoned to other rooms.
|
||
//
|
||
if ( zone != NOTHING
|
||
&& isRoom(zone)
|
||
&& !isRoom(thing))
|
||
{
|
||
notify(executor, M_("Only rooms may be zoned to other rooms."));
|
||
return;
|
||
}
|
||
|
||
// Everything is okay, do the change.
|
||
//
|
||
dbref old_zone = Zone(thing);
|
||
s_Zone(thing, zone);
|
||
|
||
// If a room changed zones, invalidate both the old and new zone
|
||
// routing tables plus the inter-zone meta-table.
|
||
//
|
||
if (isRoom(thing) && Navigable(thing) && old_zone != zone)
|
||
{
|
||
route_invalidate_zone(old_zone);
|
||
route_invalidate_zone(zone);
|
||
route_invalidate_meta();
|
||
}
|
||
|
||
if (!isPlayer(thing))
|
||
{
|
||
// If the object is a player, resetting these flags is rather
|
||
// inconvenient -- although this may pose a bit of a security risk. Be
|
||
// careful when @chzone'ing wizard or royal players.
|
||
//
|
||
SetClearFlags(thing, mudconf.stripped_flags.word, nullptr);
|
||
|
||
// Wipe out all powers.
|
||
//
|
||
Powers(thing) = 0;
|
||
Powers2(thing) = 0;
|
||
}
|
||
notify(executor, M_("Zone changed."));
|
||
}
|
||
|
||
void do_name
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *newname,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing = match_controlled(executor, name);
|
||
if (NOTHING == thing)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Check for bad name.
|
||
//
|
||
if ( nargs < 2
|
||
|| '\0' == newname[0])
|
||
{
|
||
notify_quiet(executor, M_("Give it what new name?"));
|
||
return;
|
||
}
|
||
|
||
// Check for renaming a player.
|
||
//
|
||
if (isPlayer(thing))
|
||
{
|
||
UTF8 *buff = trim_spaces(newname);
|
||
if ( !ValidatePlayerName(buff)
|
||
|| !badname_check(buff))
|
||
{
|
||
notify_quiet(executor, M_("You can’t use that name."));
|
||
free_lbuf(buff);
|
||
return;
|
||
}
|
||
if (!protectname_check(buff, thing))
|
||
{
|
||
notify_quiet(executor, M_("That name is protected by another player."));
|
||
free_lbuf(buff);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
bool bAlias = false;
|
||
dbref jwho = lookup_player_name(buff, bAlias);
|
||
if ( NOTHING != jwho
|
||
&& ( jwho != thing
|
||
|| bAlias))
|
||
{
|
||
notify_quiet(executor, M_("That name is already in use."));
|
||
free_lbuf(buff);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Everything ok, notify.
|
||
//
|
||
STARTLOG(LOG_SECURITY, "SEC", "CNAME");
|
||
log_name(thing),
|
||
log_text(T(" renamed to "));
|
||
log_text(buff);
|
||
ENDLOG;
|
||
if (Suspect(thing))
|
||
{
|
||
raw_broadcast(WIZARD, M_("[Suspect] %s renamed to %s"), Name(thing), buff);
|
||
}
|
||
delete_player_name(thing, Name(thing), false);
|
||
s_Name(thing, buff);
|
||
set_modified(thing);
|
||
add_player_name(thing, Name(thing), false);
|
||
if (!Quiet(executor) && !Quiet(thing))
|
||
{
|
||
notify_quiet(executor, M_("Name set."));
|
||
}
|
||
free_lbuf(buff);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
size_t nValidName;
|
||
bool bValid;
|
||
UTF8 *pValidName;
|
||
|
||
if (isExit(thing))
|
||
{
|
||
pValidName = MakeCanonicalExitName(newname, &nValidName, &bValid);
|
||
}
|
||
else if (isThing(thing))
|
||
{
|
||
pValidName = MakeCanonicalObjectName(newname, &nValidName, &bValid, mudconf.thing_name_charset);
|
||
}
|
||
else
|
||
{
|
||
pValidName = MakeCanonicalObjectName(newname, &nValidName, &bValid, mudconf.room_name_charset);
|
||
}
|
||
|
||
if (!bValid)
|
||
{
|
||
notify_quiet(executor, M_("That is not a reasonable name."));
|
||
return;
|
||
}
|
||
|
||
// Everything ok, change the name.
|
||
//
|
||
s_Name(thing, pValidName);
|
||
set_modified(thing);
|
||
if (!Quiet(executor) && !Quiet(thing))
|
||
{
|
||
notify_quiet(executor, M_("Name set."));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * do_alias: Make an alias for a player or object.
|
||
*/
|
||
|
||
void do_alias
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *alias,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing = match_controlled(executor, name);
|
||
if (NOTHING == thing)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Check for renaming a player.
|
||
//
|
||
dbref aowner;
|
||
int aflags;
|
||
ATTR *ap = atr_num(A_ALIAS);
|
||
if (isPlayer(thing))
|
||
{
|
||
// Fetch the old alias.
|
||
//
|
||
LBuf oldalias = LBuf_Adopt(atr_pget(thing, A_ALIAS, &aowner, &aflags));
|
||
UTF8 *trimalias = trim_spaces(alias);
|
||
dbref nPlayer;
|
||
bool bAlias = false;
|
||
|
||
if (!Controls(executor, thing))
|
||
{
|
||
// Make sure we have rights to do it. We can't do the
|
||
// normal Set_attr check because ALIAS is only
|
||
// writable by GOD and we want to keep people from
|
||
// doing &ALIAS and bypassing the player name checks.
|
||
//
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else if (!*trimalias)
|
||
{
|
||
// New alias is null, just clear it.
|
||
//
|
||
delete_player_name(thing, oldalias, true);
|
||
atr_clr(thing, A_ALIAS);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Alias removed."));
|
||
}
|
||
}
|
||
else if ( (nPlayer = lookup_player_name(trimalias, bAlias)) != NOTHING
|
||
&& ( nPlayer != thing
|
||
|| !bAlias))
|
||
{
|
||
// Make sure new alias isn't already in use.
|
||
//
|
||
notify_quiet(executor, M_("That name is already in use."));
|
||
}
|
||
else if ( !( badname_check(trimalias)
|
||
&& ValidatePlayerName(trimalias)))
|
||
{
|
||
notify_quiet(executor, M_("That’s a silly name for a player!"));
|
||
}
|
||
else
|
||
{
|
||
// Remove the old name and add the new name.
|
||
//
|
||
delete_player_name(thing, oldalias, true);
|
||
atr_add(thing, A_ALIAS, trimalias, Owner(executor), aflags);
|
||
if (add_player_name(thing, trimalias, true))
|
||
{
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Alias set."));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor,
|
||
T("That name is already in use or is illegal, alias cleared."));
|
||
atr_clr(thing, A_ALIAS);
|
||
}
|
||
}
|
||
free_lbuf(trimalias);
|
||
}
|
||
else
|
||
{
|
||
atr_pget_info(thing, A_ALIAS, &aowner, &aflags);
|
||
|
||
// Make sure we have rights to do it.
|
||
//
|
||
if (!bCanSetAttr(executor, thing, ap))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else
|
||
{
|
||
atr_add(thing, A_ALIAS, alias, Owner(executor), aflags);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Set."));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_forwardlist: Set a forwardlist.
|
||
// ---------------------------------------------------------------------------
|
||
void do_forwardlist
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *target,
|
||
UTF8 *newlist,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing = match_controlled(executor, target);
|
||
if (thing == NOTHING)
|
||
{
|
||
return;
|
||
}
|
||
dbref aowner, aflags;
|
||
atr_pget_info(thing, A_FORWARDLIST, &aowner, &aflags);
|
||
|
||
if (!Controls(executor, thing))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
else if (!*newlist)
|
||
{
|
||
// New forwardlist is null, just clear it.
|
||
//
|
||
atr_clr(thing, A_FORWARDLIST);
|
||
set_modified(thing);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Forwardlist removed."));
|
||
}
|
||
}
|
||
else if (!fwdlist_ck(executor, thing, A_FORWARDLIST, newlist))
|
||
{
|
||
notify_quiet(executor, M_("Invalid forwardlist."));
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
atr_add(thing, A_FORWARDLIST, newlist, Owner(executor), aflags);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Set."));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * do_lock: Set a lock on an object or attribute.
|
||
*/
|
||
|
||
void do_lock
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *keytext,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing;
|
||
ATTR *ap;
|
||
|
||
if ( parse_attrib(executor, name, &thing, &ap)
|
||
&& ap)
|
||
{
|
||
dbref aowner;
|
||
int aflags;
|
||
if (!atr_get_info(thing, ap->number, &aowner, &aflags))
|
||
{
|
||
notify_quiet(executor, M_("Attribute not present on object."));
|
||
return;
|
||
}
|
||
|
||
if (bCanLockAttr(executor, thing, ap))
|
||
{
|
||
aflags |= AF_LOCK;
|
||
atr_set_flags(thing, ap->number, aflags);
|
||
if ( !Quiet(executor)
|
||
&& !Quiet(thing))
|
||
{
|
||
notify_quiet(executor, M_("Attribute locked."));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
return;
|
||
}
|
||
init_match(executor, name, NOTYPE);
|
||
match_everything(MAT_EXIT_PARENTS);
|
||
thing = match_result();
|
||
|
||
switch (thing)
|
||
{
|
||
case NOTHING:
|
||
notify_quiet(executor, M_("I don’t see what you want to lock!"));
|
||
return;
|
||
|
||
case AMBIGUOUS:
|
||
notify_quiet(executor, M_("I don’t know which one you want to lock!"));
|
||
return;
|
||
|
||
default:
|
||
if (!Controls(executor, thing))
|
||
{
|
||
notify_quiet(executor, M_("You can’t lock that!"));
|
||
return;
|
||
}
|
||
if (NoModify(thing) && !WizRoy(executor))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
}
|
||
|
||
thread_local UTF8 pRestrictedKeyText[LBUF_SIZE];
|
||
StripTabsAndTruncate(keytext, pRestrictedKeyText, LBUF_SIZE-1, LBUF_SIZE-1);
|
||
struct boolexp *okey = parse_boolexp(executor, pRestrictedKeyText, false);
|
||
if (okey == TRUE_BOOLEXP)
|
||
{
|
||
notify_quiet(executor, M_("I don’t understand that key."));
|
||
}
|
||
else
|
||
{
|
||
// Everything ok, do it.
|
||
//
|
||
if (!key)
|
||
{
|
||
key = A_LOCK;
|
||
}
|
||
atr_add_raw(thing, key, unparse_boolexp_quiet(executor, okey));
|
||
if ( !Quiet(executor)
|
||
&& !Quiet(thing))
|
||
{
|
||
notify_quiet(executor, M_("Locked."));
|
||
}
|
||
}
|
||
free_boolexp(okey);
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * Remove a lock from an object of attribute.
|
||
*/
|
||
|
||
void do_unlock(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing;
|
||
ATTR *ap;
|
||
|
||
if ( parse_attrib(executor, name, &thing, &ap)
|
||
&& ap)
|
||
{
|
||
// We have been asked to unlock an attribute.
|
||
//
|
||
dbref aowner;
|
||
int aflags;
|
||
if (!atr_get_info(thing, ap->number, &aowner, &aflags))
|
||
{
|
||
notify_quiet(executor, M_("Attribute not present on object."));
|
||
return;
|
||
}
|
||
|
||
if (bCanLockAttr(executor, thing, ap))
|
||
{
|
||
aflags &= ~AF_LOCK;
|
||
atr_set_flags(thing, ap->number, aflags);
|
||
if ( !Quiet(executor)
|
||
&& !Quiet(thing))
|
||
{
|
||
notify_quiet(executor, M_("Attribute unlocked."));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// We have been asked to change the ownership of an object.
|
||
//
|
||
if (!key)
|
||
{
|
||
key = A_LOCK;
|
||
}
|
||
thing = match_controlled(executor, name);
|
||
if (thing != NOTHING)
|
||
{
|
||
atr_clr(thing, key);
|
||
set_modified(thing);
|
||
if (!Quiet(executor) && !Quiet(thing))
|
||
{
|
||
notify_quiet(executor, M_("Unlocked."));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * do_unlink: Unlink an exit from its destination or remove a dropto.
|
||
*/
|
||
|
||
void do_unlink(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *name, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref exit;
|
||
|
||
init_match(executor, name, TYPE_EXIT);
|
||
match_everything(0);
|
||
exit = match_result();
|
||
|
||
switch (exit)
|
||
{
|
||
case NOTHING:
|
||
|
||
notify_quiet(executor, M_("Unlink what?"));
|
||
break;
|
||
|
||
case AMBIGUOUS:
|
||
|
||
notify_quiet(executor, M_("I don’t know which one you mean!"));
|
||
break;
|
||
|
||
default:
|
||
|
||
if (!Controls(executor, exit))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else if (NoModify(exit) && !WizRoy(executor))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else
|
||
{
|
||
switch (Typeof(exit))
|
||
{
|
||
case TYPE_EXIT:
|
||
|
||
s_Location(exit, NOTHING);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Unlinked."));
|
||
}
|
||
giveto(Owner(exit), mudconf.linkcost);
|
||
route_invalidate();
|
||
break;
|
||
|
||
case TYPE_ROOM:
|
||
|
||
s_Dropto(exit, NOTHING);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Dropto removed."));
|
||
}
|
||
break;
|
||
|
||
default:
|
||
|
||
notify_quiet(executor, M_("You can’t unlink that!"));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void TranslateFlags_Clone
|
||
(
|
||
FLAG aClearFlags[3],
|
||
dbref executor,
|
||
int key
|
||
)
|
||
{
|
||
if (key & CLONE_NOSTRIP)
|
||
{
|
||
if (God(executor))
|
||
{
|
||
// #1 using /nostrip causes nothing to be stripped.
|
||
//
|
||
aClearFlags[FLAG_WORD1] = 0;
|
||
}
|
||
else
|
||
{
|
||
// With #1 powers, /nostrip still strips the WIZARD bit.
|
||
//
|
||
aClearFlags[FLAG_WORD1] = WIZARD;
|
||
}
|
||
aClearFlags[FLAG_WORD2] = 0;
|
||
aClearFlags[FLAG_WORD3] = 0;
|
||
}
|
||
else
|
||
{
|
||
if ( (key & CLONE_INHERIT)
|
||
&& Inherits(executor))
|
||
{
|
||
// The /inherit switch specifically allows INHERIT powers through.
|
||
//
|
||
aClearFlags[FLAG_WORD1] = (WIZARD | mudconf.stripped_flags.word[FLAG_WORD1]) & ~(INHERIT);
|
||
}
|
||
else
|
||
{
|
||
// Normally WIZARD and the other stripped_flags are removed.
|
||
//
|
||
aClearFlags[FLAG_WORD1] = WIZARD | mudconf.stripped_flags.word[FLAG_WORD1];
|
||
}
|
||
aClearFlags[FLAG_WORD2] = mudconf.stripped_flags.word[FLAG_WORD2];
|
||
aClearFlags[FLAG_WORD3] = mudconf.stripped_flags.word[FLAG_WORD3];
|
||
}
|
||
}
|
||
|
||
void TranslateFlags_Chown
|
||
(
|
||
FLAG aClearFlags[3],
|
||
FLAG aSetFlags[3],
|
||
bool *bClearPowers,
|
||
dbref executor,
|
||
int key
|
||
)
|
||
{
|
||
if (key & CHOWN_NOSTRIP)
|
||
{
|
||
if (God(executor))
|
||
{
|
||
// #1 using /nostrip only clears CHOWN_OK and sets HALT.
|
||
//
|
||
aClearFlags[FLAG_WORD1] = CHOWN_OK;
|
||
*bClearPowers = false;
|
||
}
|
||
else
|
||
{
|
||
// With /nostrip, CHOWN_OK and WIZARD are cleared, HALT is
|
||
// set, and powers are cleared.
|
||
//
|
||
aClearFlags[FLAG_WORD1] = CHOWN_OK | WIZARD;
|
||
*bClearPowers = true;
|
||
}
|
||
aClearFlags[FLAG_WORD2] = 0;
|
||
aClearFlags[FLAG_WORD3] = 0;
|
||
}
|
||
else
|
||
{
|
||
// Without /nostrip, CHOWN_OK, WIZARD, and stripped_flags are
|
||
// cleared, HALT is set, powers are cleared.
|
||
//
|
||
aClearFlags[FLAG_WORD1] = CHOWN_OK | WIZARD
|
||
| mudconf.stripped_flags.word[FLAG_WORD1];
|
||
aClearFlags[FLAG_WORD2] = mudconf.stripped_flags.word[FLAG_WORD2];
|
||
aClearFlags[FLAG_WORD3] = mudconf.stripped_flags.word[FLAG_WORD3];
|
||
*bClearPowers = true;
|
||
}
|
||
aSetFlags[FLAG_WORD1] = HALT;
|
||
aSetFlags[FLAG_WORD2] = 0;
|
||
aSetFlags[FLAG_WORD3] = 0;
|
||
}
|
||
|
||
void SetClearFlags
|
||
(
|
||
dbref thing,
|
||
FLAG aClearFlags[3],
|
||
FLAG aSetFlags[3]
|
||
)
|
||
{
|
||
int j;
|
||
for (j = FLAG_WORD1; j <= FLAG_WORD3; j++)
|
||
{
|
||
FLAG newval = db[thing].fs.word[j];
|
||
if (nullptr != aClearFlags)
|
||
{
|
||
newval &= ~aClearFlags[j];
|
||
}
|
||
|
||
if (nullptr != aSetFlags)
|
||
{
|
||
newval |= aSetFlags[j];
|
||
}
|
||
s_Flags(thing, j, newval);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * do_chown: Change ownership of an object or attribute.
|
||
*/
|
||
|
||
void do_chown
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *newown,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref nOwnerOrig, nOwnerNew, thing;
|
||
bool bDoit;
|
||
ATTR *ap;
|
||
|
||
if ( parse_attrib(executor, name, &thing, &ap)
|
||
&& ap
|
||
&& See_attr(executor, thing, ap))
|
||
{
|
||
// An attribute was given, so we worry about changing the owner of the
|
||
// attribute.
|
||
//
|
||
nOwnerOrig = Owner(thing);
|
||
if (!*newown)
|
||
{
|
||
nOwnerNew = nOwnerOrig;
|
||
}
|
||
else if (!string_compare(newown, T("me")))
|
||
{
|
||
nOwnerNew = Owner(executor);
|
||
}
|
||
else
|
||
{
|
||
nOwnerNew = lookup_player(executor, newown, true);
|
||
}
|
||
|
||
// You may chown an attr to yourself if you own the object and the attr
|
||
// is not locked. You may chown an attr to the owner of the object if
|
||
// you own the attribute. To do anything else you must be a wizard.
|
||
// Only #1 can chown attributes on #1.
|
||
//
|
||
dbref aowner;
|
||
int aflags;
|
||
if (!atr_get_info(thing, ap->number, &aowner, &aflags))
|
||
{
|
||
notify_quiet(executor, M_("Attribute not present on object."));
|
||
return;
|
||
}
|
||
bDoit = false;
|
||
if (nOwnerNew == NOTHING)
|
||
{
|
||
notify_quiet(executor, M_("I couldn’t find that player."));
|
||
}
|
||
else if ( God(thing)
|
||
&& !God(executor))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else if (Wizard(executor))
|
||
{
|
||
bDoit = true;
|
||
}
|
||
else if (nOwnerNew == Owner(executor))
|
||
{
|
||
// Chown to me: only if I own the obj and !locked
|
||
//
|
||
if ( !Controls(executor, thing)
|
||
|| (aflags & AF_LOCK))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else
|
||
{
|
||
bDoit = true;
|
||
}
|
||
}
|
||
else if (nOwnerNew == nOwnerOrig)
|
||
{
|
||
// chown to obj owner: only if I own attr and !locked
|
||
//
|
||
if ( Owner(executor) != aowner
|
||
|| (aflags & AF_LOCK))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else
|
||
{
|
||
bDoit = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
|
||
if (!bDoit)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!bCanSetAttr(executor, executor, ap))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
LBuf buff = LBuf_Adopt(atr_get("do_chown.887", thing, ap->number, &aowner, &aflags));
|
||
atr_add(thing, ap->number, buff, nOwnerNew, aflags);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Attribute owner changed."));
|
||
}
|
||
return;
|
||
}
|
||
|
||
// An attribute was not specified, so we are being asked to change the
|
||
// owner of the object.
|
||
//
|
||
init_match(executor, name, TYPE_THING);
|
||
match_possession();
|
||
match_here();
|
||
match_exit();
|
||
match_me();
|
||
if (Chown_Any(executor))
|
||
{
|
||
match_player();
|
||
match_absolute();
|
||
}
|
||
switch (thing = match_result())
|
||
{
|
||
case NOTHING:
|
||
|
||
notify_quiet(executor, M_("You don’t have that!"));
|
||
return;
|
||
|
||
case AMBIGUOUS:
|
||
|
||
notify_quiet(executor, M_("I don’t know which you mean!"));
|
||
return;
|
||
}
|
||
nOwnerOrig = Owner(thing);
|
||
|
||
if (NoModify(thing) && !WizRoy(executor))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
if (!*newown || !(string_compare(newown, T("me"))))
|
||
{
|
||
nOwnerNew = Owner(executor);
|
||
}
|
||
else
|
||
{
|
||
nOwnerNew = lookup_player(executor, newown, true);
|
||
}
|
||
|
||
int cost = 1, quota = 1;
|
||
|
||
switch (Typeof(thing))
|
||
{
|
||
case TYPE_ROOM:
|
||
|
||
cost = mudconf.digcost;
|
||
quota = mudconf.room_quota;
|
||
break;
|
||
|
||
case TYPE_THING:
|
||
|
||
cost = OBJECT_DEPOSIT(Pennies(thing));
|
||
quota = mudconf.thing_quota;
|
||
break;
|
||
|
||
case TYPE_EXIT:
|
||
|
||
cost = mudconf.opencost;
|
||
quota = mudconf.exit_quota;
|
||
break;
|
||
|
||
case TYPE_PLAYER:
|
||
|
||
cost = mudconf.robotcost;
|
||
quota = mudconf.player_quota;
|
||
break;
|
||
}
|
||
|
||
bool bPlayerControlsThing = Controls(executor, thing);
|
||
if ( isGarbage(thing)
|
||
&& bPlayerControlsThing)
|
||
{
|
||
notify_quiet(executor, M_("You shouldn’t be rummaging through the garbage."));
|
||
}
|
||
else if (nOwnerNew == NOTHING)
|
||
{
|
||
notify_quiet(executor, M_("I couldn’t find that player."));
|
||
}
|
||
else if ( isPlayer(thing)
|
||
&& !God(executor))
|
||
{
|
||
notify_quiet(executor, M_("Players always own themselves."));
|
||
}
|
||
else if ( ( !bPlayerControlsThing
|
||
&& !Chown_Any(executor)
|
||
&& !Chown_ok(thing))
|
||
|| ( isThing(thing)
|
||
&& Location(thing) != executor
|
||
&& !Chown_Any(executor))
|
||
|| !Controls(executor, nOwnerNew)
|
||
|| God(thing))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
}
|
||
else if (canpayfees(executor, nOwnerNew, cost, quota))
|
||
{
|
||
giveto(nOwnerOrig, cost);
|
||
if (mudconf.quotas)
|
||
{
|
||
add_quota(nOwnerOrig, quota);
|
||
}
|
||
|
||
if (!God(executor))
|
||
{
|
||
nOwnerNew = Owner(nOwnerNew);
|
||
}
|
||
|
||
s_Owner(thing, nOwnerNew);
|
||
atr_chown(thing);
|
||
|
||
FLAGSET clearflags;
|
||
FLAGSET setflags;
|
||
bool bClearPowers;
|
||
|
||
TranslateFlags_Chown(clearflags.word, setflags.word, &bClearPowers, executor, key);
|
||
|
||
SetClearFlags(thing, clearflags.word, setflags.word);
|
||
if (bClearPowers)
|
||
{
|
||
s_Powers(thing, 0);
|
||
s_Powers2(thing, 0);
|
||
}
|
||
|
||
// Always halt the queue.
|
||
//
|
||
halt_que(NOTHING, thing);
|
||
|
||
// Warn if ROYALTY is left set.
|
||
//
|
||
if (ROYALTY & db[thing].fs.word[FLAG_WORD1])
|
||
{
|
||
notify_quiet(executor,
|
||
tprintf(M_("Warning: @chown/nostrip on %s(#%d) leaves ROYALTY privilege intact."),
|
||
Moniker(thing), thing));
|
||
}
|
||
|
||
// Warn if INHERIT is left set.
|
||
//
|
||
if (INHERIT & db[thing].fs.word[FLAG_WORD1])
|
||
{
|
||
notify_quiet(executor,
|
||
tprintf(M_("Warning: @chown/nostrip on %s(#%d) leaves INHERIT privilege intact."),
|
||
Moniker(thing), thing));
|
||
}
|
||
|
||
if (!Quiet(executor))
|
||
{
|
||
LBuf buff = LBuf_Src("do_chown.notify");
|
||
UTF8 *bp = buff;
|
||
|
||
safe_tprintf_str(buff, &bp, T("Owner of %s(#%d) changed from "), Moniker(thing), thing);
|
||
safe_tprintf_str(buff, &bp, T("%s(#%d) to "), Moniker(nOwnerOrig), nOwnerOrig);
|
||
safe_tprintf_str(buff, &bp, T("%s(#%d)."), Moniker(nOwnerNew), nOwnerNew);
|
||
*bp = '\0';
|
||
notify_quiet(executor, buff);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * do_set: Set flags or attributes on objects, or flags on attributes.
|
||
*/
|
||
|
||
static void set_attr_internal(dbref player, dbref thing, int attrnum, const UTF8 *attrtext, int key)
|
||
{
|
||
dbref aowner;
|
||
int aflags;
|
||
ATTR *pattr = atr_num(attrnum);
|
||
atr_pget_info(thing, attrnum, &aowner, &aflags);
|
||
if ( pattr
|
||
&& bCanSetAttr(player, thing, pattr))
|
||
{
|
||
bool could_hear = Hearer(thing);
|
||
atr_add(thing, attrnum, attrtext, Owner(player), aflags);
|
||
handle_ears(thing, could_hear, Hearer(thing));
|
||
if ( !(key & SET_QUIET)
|
||
&& !Quiet(player)
|
||
&& !Quiet(thing))
|
||
{
|
||
notify_quiet(player, M_("Set."));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(player, NOPERM_MESSAGE);
|
||
}
|
||
}
|
||
|
||
bool copy_attr(dbref executor, attr_info &src, attr_info &dest, int key)
|
||
{
|
||
if ( !Good_obj(src.m_object)
|
||
|| !Good_obj(dest.m_object)
|
||
|| nullptr == src.m_attr)
|
||
{
|
||
notify_quiet(executor, M_("No match."));
|
||
return false;
|
||
}
|
||
if ( nullptr == dest.m_attr
|
||
|| !See_attr(executor, src.m_object, src.m_attr)
|
||
|| !bCanSetAttr(executor, dest.m_object, dest.m_attr))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return false;
|
||
}
|
||
LBuf buff = LBuf_Src("copy_attr");
|
||
atr_pget_str(buff, src.m_object, src.m_attr->number, &src.m_aowner, &src.m_aflags);
|
||
src.m_bHaveInfo = true;
|
||
|
||
set_attr_internal(executor, dest.m_object, dest.m_attr->number, buff, key);
|
||
return true;
|
||
}
|
||
|
||
void do_set
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *flagname,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing, aowner;
|
||
int aflags;
|
||
ATTR *pattr;
|
||
|
||
// See if we have the <obj>/<attr> form, which is how you set
|
||
// attribute flags.
|
||
//
|
||
if (parse_attrib(executor, name, &thing, &pattr))
|
||
{
|
||
if ( pattr
|
||
&& See_attr(executor, thing, pattr))
|
||
{
|
||
// You must specify a flag name.
|
||
//
|
||
if ( !flagname
|
||
|| flagname[0] == '\0')
|
||
{
|
||
notify_quiet(executor, M_("I don’t know what you want to set!"));
|
||
return;
|
||
}
|
||
|
||
// Check for clearing.
|
||
//
|
||
bool clear = false;
|
||
if (flagname[0] == NOT_TOKEN)
|
||
{
|
||
flagname++;
|
||
clear = true;
|
||
}
|
||
|
||
// Make sure player specified a valid attribute flag.
|
||
//
|
||
int flagvalue;
|
||
if (!search_nametab(executor, indiv_attraccess_nametab, flagname, &flagvalue))
|
||
{
|
||
notify_quiet(executor, M_("You can’t set that!"));
|
||
return;
|
||
}
|
||
|
||
// Make sure the object has the attribute present.
|
||
//
|
||
if (!atr_get_info(thing, pattr->number, &aowner, &aflags))
|
||
{
|
||
notify_quiet(executor, M_("Attribute not present on object."));
|
||
return;
|
||
}
|
||
|
||
// Make sure we can write to the attribute.
|
||
//
|
||
if (!bCanSetAttr(executor, thing, pattr))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
// Check if the attribute flag is already in the desired
|
||
// state.
|
||
//
|
||
if (clear ? ((aflags & flagvalue) == 0) : ((aflags & flagvalue) == flagvalue))
|
||
{
|
||
if ( !(key & SET_QUIET)
|
||
&& !Quiet(executor)
|
||
&& !Quiet(thing))
|
||
{
|
||
if (clear)
|
||
{
|
||
notify_quiet(executor, M_("Already cleared."));
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("Already set."));
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Go do it.
|
||
//
|
||
if (clear)
|
||
{
|
||
aflags &= ~flagvalue;
|
||
}
|
||
else
|
||
{
|
||
aflags |= flagvalue;
|
||
}
|
||
bool could_hear = Hearer(thing);
|
||
|
||
atr_set_flags(thing, pattr->number, aflags);
|
||
|
||
// Tell the player about it.
|
||
//
|
||
handle_ears(thing, could_hear, Hearer(thing));
|
||
|
||
if ( !(key & SET_QUIET)
|
||
&& !Quiet(executor)
|
||
&& !Quiet(thing))
|
||
{
|
||
if (clear)
|
||
{
|
||
notify_quiet(executor, M_("Cleared."));
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("Set."));
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Find thing.
|
||
//
|
||
thing = match_controlled(executor, name);
|
||
if (!Good_obj(thing))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Check for attribute set first.
|
||
//
|
||
UTF8 *p;
|
||
for (p = flagname; *p && (*p != ':'); p++)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
|
||
if (*p)
|
||
{
|
||
*p++ = '\0';
|
||
int atr = mkattr(executor, flagname);
|
||
if (atr <= 0)
|
||
{
|
||
notify_quiet(executor, M_("Couldn’t create attribute."));
|
||
return;
|
||
}
|
||
pattr = atr_num(atr);
|
||
if (!pattr)
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
if (!bCanSetAttr(executor, thing, pattr))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
// Check for _
|
||
//
|
||
if (*p == '_')
|
||
{
|
||
p++;
|
||
|
||
attr_info src(executor, p, false, false);
|
||
attr_info dest(thing, pattr);
|
||
copy_attr(executor, src, dest, key);
|
||
return;
|
||
}
|
||
|
||
// Go set it.
|
||
//
|
||
set_attr_internal(executor, thing, atr, p, key);
|
||
return;
|
||
}
|
||
|
||
// Set or clear a flag.
|
||
//
|
||
flag_set(thing, executor, flagname, key);
|
||
}
|
||
|
||
void do_power
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *flag,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
if ( !flag
|
||
|| !*flag)
|
||
{
|
||
notify_quiet(executor, M_("I don’t know what you want to set!"));
|
||
return;
|
||
}
|
||
|
||
// Find thing.
|
||
//
|
||
dbref thing = match_controlled(executor, name);
|
||
if (thing == NOTHING)
|
||
{
|
||
return;
|
||
}
|
||
power_set(thing, executor, flag, key);
|
||
}
|
||
|
||
void do_setattr
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int attrnum,
|
||
int nargs,
|
||
UTF8 *name,
|
||
UTF8 *attrtext,
|
||
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, name, NOTYPE);
|
||
match_everything(MAT_EXIT_PARENTS);
|
||
dbref thing = noisy_match_result();
|
||
|
||
if (!Good_obj(thing))
|
||
{
|
||
return;
|
||
}
|
||
set_attr_internal(executor, thing, attrnum, attrtext, 0);
|
||
}
|
||
|
||
void do_cpattr(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
||
UTF8 *oldpair, UTF8 *newpair[], int nargs, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
if ( isEmpty(oldpair)
|
||
|| isEmpty(newpair[0])
|
||
|| nargs < 1)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Check if source has wildcard in attribute portion.
|
||
//
|
||
bool bWild = false;
|
||
const UTF8 *p = oldpair;
|
||
while (*p && *p != '/')
|
||
{
|
||
p++;
|
||
}
|
||
if (*p == '/')
|
||
{
|
||
p++;
|
||
while (*p)
|
||
{
|
||
if (*p == '*' || *p == '?')
|
||
{
|
||
bWild = true;
|
||
break;
|
||
}
|
||
p++;
|
||
}
|
||
}
|
||
|
||
if (bWild)
|
||
{
|
||
// Wildcard path: iterate all matching attributes on source object.
|
||
//
|
||
dbref thing;
|
||
olist_push();
|
||
if (!parse_attrib_wild(executor, oldpair, &thing, false, false, false))
|
||
{
|
||
olist_pop();
|
||
notify_quiet(executor, M_("No match."));
|
||
return;
|
||
}
|
||
|
||
int atr;
|
||
for (atr = olist_first(); atr != NOTHING; atr = olist_next())
|
||
{
|
||
ATTR *ap = atr_num(atr);
|
||
if (ap)
|
||
{
|
||
attr_info src(thing, ap);
|
||
for (int i = 0; i < nargs; i++)
|
||
{
|
||
attr_info dest(executor, newpair[i], true, false);
|
||
if (!dest.m_bValid)
|
||
{
|
||
if (Good_obj(dest.m_object))
|
||
{
|
||
dest.m_attr = src.m_attr;
|
||
dest.m_bValid = true;
|
||
}
|
||
}
|
||
if (dest.m_bValid)
|
||
{
|
||
copy_attr(executor, src, dest, key);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
olist_pop();
|
||
}
|
||
else
|
||
{
|
||
// Single-attribute path.
|
||
//
|
||
attr_info src(executor, oldpair, false, true);
|
||
if (src.m_bValid)
|
||
{
|
||
for (int i = 0; i < nargs; i++)
|
||
{
|
||
attr_info dest(executor, newpair[i], true, false);
|
||
if (!dest.m_bValid)
|
||
{
|
||
if (Good_obj(dest.m_object))
|
||
{
|
||
dest.m_attr = src.m_attr;
|
||
dest.m_bValid = true;
|
||
}
|
||
}
|
||
if (dest.m_bValid)
|
||
{
|
||
copy_attr(executor, src, dest, key);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("No match."));
|
||
}
|
||
}
|
||
}
|
||
|
||
void do_mvattr(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
||
UTF8 *what, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
// Make sure we have something to do.
|
||
//
|
||
if (nargs < 2)
|
||
{
|
||
notify_quiet(executor, M_("Nothing to do."));
|
||
return;
|
||
}
|
||
|
||
// Find and make sure we control the target object.
|
||
//
|
||
dbref thing = match_controlled(executor, what);
|
||
if (thing == NOTHING)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Look up the source attribute. If it either doesn't exist or isn't
|
||
// readable, use an empty string.
|
||
//
|
||
int in_anum = -1;
|
||
LBuf astr = LBuf_Src("do_mvattr");
|
||
ATTR *in_attr = atr_str(args[0]);
|
||
int aflags = 0;
|
||
if (in_attr == nullptr)
|
||
{
|
||
*astr = '\0';
|
||
}
|
||
else
|
||
{
|
||
dbref aowner;
|
||
atr_get_str(astr, thing, in_attr->number, &aowner, &aflags);
|
||
if (See_attr(executor, thing, in_attr))
|
||
{
|
||
in_anum = in_attr->number;
|
||
}
|
||
else
|
||
{
|
||
*astr = '\0';
|
||
}
|
||
}
|
||
|
||
// Copy the attribute to each target in turn.
|
||
//
|
||
bool bCanDelete = true;
|
||
int nCopied = 0;
|
||
for (int i = 1; i < nargs; i++)
|
||
{
|
||
int anum = mkattr(executor, args[i]);
|
||
if (anum <= 0)
|
||
{
|
||
notify_quiet(executor, tprintf(M_("%s: That’s not a good name for an attribute."), args[i]));
|
||
continue;
|
||
}
|
||
ATTR *out_attr = atr_num(anum);
|
||
if (!out_attr)
|
||
{
|
||
notify_quiet(executor, tprintf(M_("%s: Permission denied."), args[i]));
|
||
}
|
||
else if (out_attr->number == in_anum)
|
||
{
|
||
// It doesn't make sense to delete a source attribute if it's also
|
||
// included as a destination.
|
||
//
|
||
bCanDelete = false;
|
||
}
|
||
else
|
||
{
|
||
if (!bCanSetAttr(executor, thing, out_attr))
|
||
{
|
||
notify_quiet(executor, tprintf(M_("%s: Permission denied."), args[i]));
|
||
}
|
||
else
|
||
{
|
||
nCopied++;
|
||
atr_add(thing, out_attr->number, astr, Owner(executor), aflags);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, tprintf(M_("%s: Set."), out_attr->name));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Remove the source attribute if we were able to copy it successfully to
|
||
// even one destination object.
|
||
//
|
||
if (nCopied <= 0)
|
||
{
|
||
if (in_attr)
|
||
{
|
||
notify_quiet(executor, tprintf(M_("%s: Not copied anywhere. Not cleared."), in_attr->name));
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("Not copied anywhere. Non-existent attribute."));
|
||
}
|
||
}
|
||
else if ( in_anum > 0
|
||
&& bCanDelete)
|
||
{
|
||
in_attr = atr_num(in_anum);
|
||
if (in_attr)
|
||
{
|
||
if (bCanSetAttr(executor, thing, in_attr))
|
||
{
|
||
atr_clr(thing, in_attr->number);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, tprintf(M_("%s: Cleared."), in_attr->name));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor,
|
||
tprintf(M_("%s: Could not remove old attribute. Permission denied."),
|
||
in_attr->name));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("Could not remove old attribute. Non-existent attribute."));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * parse_attrib, parse_attrib_wild: parse <obj>/<attr> tokens.
|
||
*/
|
||
|
||
bool parse_attrib(dbref player, const UTF8 *str, dbref *thing, ATTR **attr)
|
||
{
|
||
ATTR *tattr = nullptr;
|
||
*thing = NOTHING;
|
||
|
||
if (!str)
|
||
{
|
||
*attr = tattr;
|
||
return false;
|
||
}
|
||
|
||
// Break apart string into obj and attr.
|
||
//
|
||
const UTF8 *AttrName;
|
||
bool retval = parse_thing_slash(player, str, &AttrName, thing);
|
||
|
||
// Get the named attribute from the object if we can.
|
||
//
|
||
if (retval)
|
||
{
|
||
tattr = atr_str(AttrName);
|
||
}
|
||
|
||
*attr = tattr;
|
||
return retval;
|
||
}
|
||
|
||
void find_wild_attrs(dbref player, dbref thing, const UTF8 *str, bool check_exclude, bool hash_insert, bool get_locks)
|
||
{
|
||
ATTR *pattr;
|
||
dbref aowner;
|
||
int ca, ok, aflags;
|
||
|
||
// Walk the attribute list of the object.
|
||
//
|
||
atr_push();
|
||
unsigned char *as;
|
||
for (ca = atr_head(thing, &as); ca; ca = atr_next(&as))
|
||
{
|
||
pattr = atr_num(ca);
|
||
|
||
// Discard bad attributes and ones we've seen before.
|
||
//
|
||
if (!pattr)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if ( check_exclude
|
||
&& ( (pattr->flags & AF_PRIVATE)
|
||
|| mudstate.parent_htab.find(ca) != mudstate.parent_htab.end()))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
// If we aren't the top level remember this attr so we exclude it in
|
||
// any parents.
|
||
//
|
||
atr_get_info(thing, ca, &aowner, &aflags);
|
||
if ( check_exclude
|
||
&& (aflags & AF_PRIVATE))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (get_locks)
|
||
{
|
||
ok = bCanReadAttr(player, thing, pattr, false);
|
||
}
|
||
else
|
||
{
|
||
ok = See_attr(player, thing, pattr);
|
||
}
|
||
|
||
mudstate.wild_invk_ctr = 0;
|
||
if ( ok
|
||
&& quick_wild(str, pattr->name))
|
||
{
|
||
olist_add(ca);
|
||
if (hash_insert)
|
||
{
|
||
mudstate.parent_htab.emplace(ca, pattr);
|
||
}
|
||
}
|
||
}
|
||
atr_pop();
|
||
}
|
||
|
||
bool parse_attrib_wild(dbref player, const UTF8 *str, dbref *thing,
|
||
bool check_parents, bool get_locks, bool df_star)
|
||
{
|
||
if (!str)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
dbref parent;
|
||
int lev;
|
||
bool check_exclude, hash_insert;
|
||
const UTF8 *after = str;
|
||
|
||
// Separate name and attr portions at the first /.
|
||
//
|
||
if (!parse_thing_slash(player, str, &after, thing))
|
||
{
|
||
// Not in obj/attr format, return if not defaulting to.
|
||
//
|
||
if (!df_star)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// Look for the object, return failure if not found.
|
||
//
|
||
init_match(player, str, NOTYPE);
|
||
match_everything(MAT_EXIT_PARENTS);
|
||
*thing = match_result();
|
||
|
||
if (!Good_obj(*thing))
|
||
{
|
||
return false;
|
||
}
|
||
after = T("*");
|
||
}
|
||
|
||
// Check the object (and optionally all parents) for attributes.
|
||
//
|
||
if (check_parents)
|
||
{
|
||
check_exclude = false;
|
||
hash_insert = check_parents;
|
||
mudstate.parent_htab.clear();
|
||
ITER_PARENTS(*thing, parent, lev)
|
||
{
|
||
if (!Good_obj(Parent(parent)))
|
||
{
|
||
hash_insert = false;
|
||
}
|
||
find_wild_attrs(player, parent, after, check_exclude, hash_insert, get_locks);
|
||
check_exclude = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
find_wild_attrs(player, *thing, after, false, false, get_locks);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * edit_string, edit_string_ansi, do_edit: Modify attributes.
|
||
*/
|
||
|
||
void edit_string(UTF8 *src, UTF8 **dst, UTF8 *from, UTF8 *to)
|
||
{
|
||
UTF8 *cp;
|
||
|
||
// Do the substitution. Idea for prefix/suffix from R'nice@TinyTIM.
|
||
//
|
||
if (!strcmp(reinterpret_cast<char *>(from), "^"))
|
||
{
|
||
// Prepend 'to' to string.
|
||
//
|
||
*dst = alloc_lbuf("edit_string.^");
|
||
cp = *dst;
|
||
safe_str(to, *dst, &cp);
|
||
safe_str(src, *dst, &cp);
|
||
*cp = '\0';
|
||
}
|
||
else if (!strcmp(reinterpret_cast<char *>(from), "$"))
|
||
{
|
||
// Append 'to' to string.
|
||
//
|
||
*dst = alloc_lbuf("edit_string.$");
|
||
cp = *dst;
|
||
safe_str(src, *dst, &cp);
|
||
safe_str(to, *dst, &cp);
|
||
*cp = '\0';
|
||
}
|
||
else
|
||
{
|
||
// Replace all occurances of 'from' with 'to'. Handle the special
|
||
// cases of from = \$ and \^.
|
||
//
|
||
if ( ( from[0] == '\\'
|
||
|| from[0] == '%')
|
||
&& ( from[1] == '$'
|
||
|| from[1] == '^')
|
||
&& from[2] == '\0')
|
||
{
|
||
from++;
|
||
}
|
||
*dst = replace_string(from, to, src);
|
||
}
|
||
}
|
||
|
||
static void edit_string_ansi(UTF8 *src, UTF8 **dst, UTF8 **returnstr, UTF8 *from, UTF8 *to)
|
||
{
|
||
UTF8 *cp, *rp;
|
||
|
||
// Do the substitution. Idea for prefix/suffix from R'nice@TinyTIM
|
||
//
|
||
if (!strcmp(reinterpret_cast<char *>(from), "^"))
|
||
{
|
||
// Prepend 'to' to string.
|
||
//
|
||
*dst = alloc_lbuf("edit_string.^");
|
||
cp = *dst;
|
||
safe_str(to, *dst, &cp);
|
||
safe_str(src, *dst, &cp);
|
||
*cp = '\0';
|
||
|
||
// Do the ansi string used to notify.
|
||
//
|
||
*returnstr = alloc_lbuf("edit_string_ansi.^");
|
||
rp = *returnstr;
|
||
safe_str(reinterpret_cast<const UTF8 *>(COLOR_INTENSE), *returnstr, &rp);
|
||
safe_str(to, *returnstr, &rp);
|
||
safe_str(reinterpret_cast<const UTF8 *>(COLOR_RESET), *returnstr, &rp);
|
||
safe_str(src, *returnstr, &rp);
|
||
*rp = '\0';
|
||
|
||
}
|
||
else if (!strcmp(reinterpret_cast<char *>(from), "$"))
|
||
{
|
||
// Append 'to' to string
|
||
//
|
||
*dst = alloc_lbuf("edit_string.$");
|
||
cp = *dst;
|
||
safe_str(src, *dst, &cp);
|
||
safe_str(to, *dst, &cp);
|
||
*cp = '\0';
|
||
|
||
// Do the ansi string used to notify.
|
||
//
|
||
*returnstr = alloc_lbuf("edit_string_ansi.$");
|
||
rp = *returnstr;
|
||
safe_str(src, *returnstr, &rp);
|
||
safe_str(reinterpret_cast<const UTF8 *>(COLOR_INTENSE), *returnstr, &rp);
|
||
safe_str(to, *returnstr, &rp);
|
||
safe_str(reinterpret_cast<const UTF8 *>(COLOR_RESET), *returnstr, &rp);
|
||
*rp = '\0';
|
||
|
||
}
|
||
else
|
||
{
|
||
// Replace all occurances of 'from' with 'to'. Handle the special
|
||
// cases of from = \$ and \^.
|
||
//
|
||
if ( ((from[0] == '\\') || (from[0] == '%'))
|
||
&& ((from[1] == '$') || (from[1] == '^'))
|
||
&& ( from[2] == '\0'))
|
||
{
|
||
from++;
|
||
}
|
||
|
||
*dst = replace_string(from, to, src);
|
||
*returnstr = replace_string(from, tprintf(T("%s%s%s"), COLOR_INTENSE,
|
||
to, COLOR_RESET), src);
|
||
}
|
||
}
|
||
|
||
void do_edit(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
||
UTF8 *it, UTF8 *args[], int nargs, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing, aowner;
|
||
int atr, aflags;
|
||
bool bGotOne;
|
||
UTF8 *from, *to, *result, *returnstr, *atext;
|
||
ATTR *ap;
|
||
|
||
// Make sure we have something to do.
|
||
//
|
||
if ( nargs < 1
|
||
|| !*args[0])
|
||
{
|
||
notify_quiet(executor, M_("Nothing to do."));
|
||
return;
|
||
}
|
||
from = args[0];
|
||
to = (nargs >= 2) ? args[1] : const_cast<UTF8 *>(T(""));
|
||
|
||
// Look for the object and get the attribute (possibly wildcarded)
|
||
//
|
||
olist_push();
|
||
if ( !it
|
||
|| !*it
|
||
|| !parse_attrib_wild(executor, it, &thing, false, false, false))
|
||
{
|
||
notify_quiet(executor, M_("No match."));
|
||
return;
|
||
}
|
||
|
||
// Iterate through matching attributes, performing edit.
|
||
//
|
||
bGotOne = 0;
|
||
atext = alloc_lbuf("do_edit.atext");
|
||
bool could_hear = Hearer(thing);
|
||
|
||
for (atr = olist_first(); atr != NOTHING; atr = olist_next())
|
||
{
|
||
ap = atr_num(atr);
|
||
if (ap)
|
||
{
|
||
// Get the attr and make sure we can modify it.
|
||
//
|
||
atr_get_str(atext, thing, ap->number, &aowner, &aflags);
|
||
if (bCanSetAttr(executor, thing, ap))
|
||
{
|
||
// Do the edit and save the result
|
||
//
|
||
bGotOne = true;
|
||
edit_string_ansi(atext, &result, &returnstr, from, to);
|
||
atr_add(thing, ap->number, result, Owner(executor), aflags);
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, tprintf(M_("Set - %s: %s"), ap->name,
|
||
returnstr));
|
||
}
|
||
free_lbuf(result);
|
||
free_lbuf(returnstr);
|
||
}
|
||
else
|
||
{
|
||
// No rights to change the attr.
|
||
//
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, tprintf(M_("%s: Permission denied."), ap->name));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Clean up.
|
||
//
|
||
free_lbuf(atext);
|
||
olist_pop();
|
||
|
||
if (!bGotOne)
|
||
{
|
||
notify_quiet(executor, M_("No matching attributes."));
|
||
}
|
||
else
|
||
{
|
||
handle_ears(thing, could_hear, Hearer(thing));
|
||
}
|
||
}
|
||
|
||
void do_wipe(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *it, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing;
|
||
|
||
olist_push();
|
||
if ( !it
|
||
|| !*it
|
||
|| !parse_attrib_wild(executor, it, &thing, false, false, true))
|
||
{
|
||
notify_quiet(executor, M_("No match."));
|
||
return;
|
||
}
|
||
if ( mudconf.safe_wipe
|
||
&& has_flag(NOTHING, thing, T("SAFE")))
|
||
{
|
||
notify_quiet(executor, M_("SAFE objects may not be @wiped."));
|
||
return;
|
||
}
|
||
|
||
// Iterate through matching attributes, zapping the writable ones
|
||
//
|
||
int atr;
|
||
ATTR *ap;
|
||
bool bGotOne = false, could_hear = Hearer(thing);
|
||
|
||
for (atr = olist_first(); atr != NOTHING; atr = olist_next())
|
||
{
|
||
ap = atr_num(atr);
|
||
if (ap)
|
||
{
|
||
// Get the attr and make sure we can modify it.
|
||
//
|
||
if (bCanSetAttr(executor, thing, ap))
|
||
{
|
||
atr_clr(thing, ap->number);
|
||
bGotOne = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Clean up
|
||
//
|
||
olist_pop();
|
||
|
||
if (!bGotOne)
|
||
{
|
||
notify_quiet(executor, M_("No matching attributes."));
|
||
}
|
||
else
|
||
{
|
||
set_modified(thing);
|
||
handle_ears(thing, could_hear, Hearer(thing));
|
||
if (!Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Wiped."));
|
||
}
|
||
}
|
||
}
|
||
|
||
void do_trigger(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
||
UTF8 *object, UTF8 *argv[], int nargs, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing;
|
||
ATTR *pattr;
|
||
|
||
if (!( parse_attrib(executor, object, &thing, &pattr)
|
||
&& pattr))
|
||
{
|
||
notify_quiet(executor, M_("No match."));
|
||
return;
|
||
}
|
||
|
||
if (!Controls(executor, thing))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
if (key & TRIG_NOW)
|
||
{
|
||
// Execute the attribute inline instead of enqueuing.
|
||
//
|
||
dbref aowner;
|
||
int aflags;
|
||
UTF8 *act = atr_pget(thing, pattr->number, &aowner, &aflags);
|
||
if (*act)
|
||
{
|
||
bool bRun = true;
|
||
dbref aowner2;
|
||
int aflags2;
|
||
UTF8 *charges = atr_pget(thing, A_CHARGES, &aowner2, &aflags2);
|
||
if (*charges)
|
||
{
|
||
// int64_t, not int (#1402): large charges must not wrap
|
||
// to a small positive and skip RUNOUT.
|
||
//
|
||
int64_t num = mux_atoi64(charges);
|
||
if (num > 0)
|
||
{
|
||
UTF8 *buff = alloc_sbuf("trigger.now.charges");
|
||
mux_i64toa(num - 1, buff);
|
||
atr_add_raw(thing, A_CHARGES, buff);
|
||
free_sbuf(buff);
|
||
}
|
||
else
|
||
{
|
||
UTF8 *buff = atr_pget(thing, A_RUNOUT, &aowner2, &aflags2);
|
||
if (*buff)
|
||
{
|
||
free_lbuf(act);
|
||
act = buff;
|
||
}
|
||
else
|
||
{
|
||
free_lbuf(buff);
|
||
bRun = false;
|
||
}
|
||
}
|
||
}
|
||
free_lbuf(charges);
|
||
if (bRun)
|
||
{
|
||
if ((aflags & AF_NOEVAL) || NoEval(thing))
|
||
{
|
||
// NOEVAL content is data, not code -- skip execution.
|
||
}
|
||
else
|
||
{
|
||
process_command(thing, executor, executor,
|
||
AttrTrace(aflags, 0), false, act,
|
||
(const UTF8 **)argv, nargs);
|
||
}
|
||
}
|
||
}
|
||
free_lbuf(act);
|
||
}
|
||
else
|
||
{
|
||
did_it(executor, thing, 0, nullptr, 0, nullptr, pattr->number, 0, (const UTF8 **)argv, nargs);
|
||
}
|
||
|
||
if (key & TRIG_NOTIFY)
|
||
{
|
||
LBuf tbuf = LBuf_Src("trigger.notify_cmd");
|
||
mux_strncpy(tbuf, T("@notify/quiet me"), LBUF_SIZE-1);
|
||
CLinearTimeAbsolute lta;
|
||
wait_que(executor, caller, enactor, eval, false, lta, NOTHING, A_SEMAPHORE,
|
||
tbuf,
|
||
0, nullptr,
|
||
mudstate.global_regs);
|
||
}
|
||
|
||
if ( !(key & TRIG_QUIET)
|
||
&& !Quiet(executor))
|
||
{
|
||
notify_quiet(executor, M_("Triggered."));
|
||
}
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// do_include: Inline-expand the contents of an attribute into the current
|
||
// action list. Unlike @trigger, this does not create a new queue entry --
|
||
// the included commands execute immediately in the caller's context, and
|
||
// @break/@assert propagate to the parent action list (unless /nobreak).
|
||
//
|
||
// Syntax: @include[/nobreak][/localize][/clearregs] obj/attr[=arg0,arg1,...]
|
||
//
|
||
// Without arguments, the parent's %0-%9 pass through unchanged.
|
||
// With arguments, %0-%9 are replaced for the duration of the include.
|
||
//
|
||
void do_include(dbref executor, dbref caller, dbref enactor, int eval, int key,
|
||
UTF8 *object, UTF8 *argv[], int nargs, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
dbref thing;
|
||
ATTR *pattr;
|
||
|
||
if (!( parse_attrib(executor, object, &thing, &pattr)
|
||
&& pattr))
|
||
{
|
||
notify_quiet(executor, M_("No match."));
|
||
return;
|
||
}
|
||
|
||
// @include requires read access, not control: the attribute text is
|
||
// expanded into the *includer's* action list (executor stays the caller).
|
||
// Running as the source object with only See_attr would let a player
|
||
// execute with foreign privileges by including any VISUAL attr (#1279).
|
||
//
|
||
if (!See_attr(executor, thing, pattr))
|
||
{
|
||
notify_quiet(executor, NOPERM_MESSAGE);
|
||
return;
|
||
}
|
||
|
||
dbref aowner;
|
||
int aflags;
|
||
LBuf act = LBuf_Adopt(atr_pget(thing, pattr->number, &aowner, &aflags));
|
||
if (!*act.get())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if ((aflags & AF_NOEVAL) || NoEval(thing))
|
||
{
|
||
// NOEVAL content is data, not code -- skip execution.
|
||
//
|
||
return;
|
||
}
|
||
|
||
// Strip $command: or ^listen: prefix if present, so that the same
|
||
// attribute that serves as a $-command can be @included as a
|
||
// subroutine.
|
||
//
|
||
UTF8 *body = act;
|
||
if ( '$' == act[0]
|
||
|| '^' == act[0])
|
||
{
|
||
UTF8 *colon = (UTF8 *)strchr((char *)act.get(), ':');
|
||
if (colon)
|
||
{
|
||
body = colon + 1;
|
||
}
|
||
}
|
||
|
||
// Determine effective environment: if caller supplies args, use them;
|
||
// otherwise pass through the parent's environment.
|
||
//
|
||
const UTF8 **env;
|
||
int nenv;
|
||
if ( nargs > 0
|
||
&& argv[0]
|
||
&& argv[0][0] != '\0')
|
||
{
|
||
env = (const UTF8 **)argv;
|
||
nenv = nargs;
|
||
}
|
||
else
|
||
{
|
||
env = cargs;
|
||
nenv = ncargs;
|
||
}
|
||
|
||
// Save/clear %q-registers if requested.
|
||
//
|
||
reg_ref *preserve[MAX_GLOBAL_REGS];
|
||
bool bLocalize = (key & INCLUDE_LOCALIZE) != 0;
|
||
if (bLocalize)
|
||
{
|
||
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
||
{
|
||
preserve[i] = mudstate.global_regs[i];
|
||
if (preserve[i])
|
||
{
|
||
RegAddRef(preserve[i]);
|
||
}
|
||
}
|
||
}
|
||
if (key & INCLUDE_CLEARREGS)
|
||
{
|
||
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
||
{
|
||
if (mudstate.global_regs[i])
|
||
{
|
||
RegRelease(mudstate.global_regs[i]);
|
||
mudstate.global_regs[i] = nullptr;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Save break state so /nobreak can isolate it.
|
||
//
|
||
bool save_break = break_called;
|
||
if (key & INCLUDE_NOBREAK)
|
||
{
|
||
break_called = false;
|
||
}
|
||
|
||
// Walk the included text, splitting on semicolons and executing
|
||
// each command inline as the includer (not the source object —
|
||
// #1279), with ;| piping honored like the queued runner (#788).
|
||
//
|
||
process_command_list_inline(executor, caller, enactor,
|
||
AttrTrace(aflags, 0), body, env, nenv);
|
||
|
||
// Restore break state for /nobreak.
|
||
//
|
||
if (key & INCLUDE_NOBREAK)
|
||
{
|
||
break_called = save_break;
|
||
}
|
||
|
||
// Restore %q-registers if /localize.
|
||
//
|
||
if (bLocalize)
|
||
{
|
||
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
||
{
|
||
if (mudstate.global_regs[i])
|
||
{
|
||
RegRelease(mudstate.global_regs[i]);
|
||
}
|
||
mudstate.global_regs[i] = preserve[i];
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
void do_use(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *object, const UTF8 *cargs[], int ncargs)
|
||
{
|
||
UNUSED_PARAMETER(caller);
|
||
UNUSED_PARAMETER(enactor);
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
dbref thing, aowner;
|
||
int aflags;
|
||
|
||
init_match(executor, object, NOTYPE);
|
||
match_neighbor();
|
||
match_possession();
|
||
if (Wizard(executor))
|
||
{
|
||
match_absolute();
|
||
match_player();
|
||
}
|
||
match_me();
|
||
match_here();
|
||
thing = noisy_match_result();
|
||
if (thing == NOTHING)
|
||
return;
|
||
|
||
// Make sure player can use it.
|
||
//
|
||
if (!could_doit(executor, thing, A_LUSE))
|
||
{
|
||
did_it(executor, thing, A_UFAIL,
|
||
M_("You can’t figure out how to use that."),
|
||
A_OUFAIL, nullptr, A_AUFAIL, 0, nullptr, 0);
|
||
return;
|
||
}
|
||
LBuf temp = LBuf_Src("do_use");
|
||
bool doit = false;
|
||
if (*atr_pget_str(temp, thing, A_USE, &aowner, &aflags))
|
||
{
|
||
doit = true;
|
||
}
|
||
else if (*atr_pget_str(temp, thing, A_OUSE, &aowner, &aflags))
|
||
{
|
||
doit = true;
|
||
}
|
||
else if (*atr_pget_str(temp, thing, A_AUSE, &aowner, &aflags))
|
||
{
|
||
doit = true;
|
||
}
|
||
|
||
if (doit)
|
||
{
|
||
LBuf df_use = LBuf_Src("do_use.use");
|
||
LBuf df_ouse = LBuf_Src("do_use.ouse");
|
||
mux_sprintf(df_use, LBUF_SIZE, M_("You use %s"), Moniker(thing));
|
||
mux_sprintf(df_ouse, LBUF_SIZE, M_("uses %s"), Moniker(thing));
|
||
did_it(executor, thing, A_USE, df_use, A_OUSE, df_ouse, A_AUSE, 0,
|
||
nullptr, 0);
|
||
}
|
||
else
|
||
{
|
||
notify_quiet(executor, M_("You can’t figure out how to use that."));
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ---------------------------------------------------------------------------
|
||
* * do_setvattr: Set a user-named (or possibly a predefined) attribute.
|
||
*/
|
||
|
||
void do_setvattr
|
||
(
|
||
dbref executor,
|
||
dbref caller,
|
||
dbref enactor,
|
||
int eval,
|
||
int key,
|
||
int nargs,
|
||
UTF8 *arg1,
|
||
UTF8 *arg2,
|
||
const UTF8 *cargs[],
|
||
int ncargs
|
||
)
|
||
{
|
||
UNUSED_PARAMETER(eval);
|
||
UNUSED_PARAMETER(key);
|
||
UNUSED_PARAMETER(nargs);
|
||
UNUSED_PARAMETER(cargs);
|
||
UNUSED_PARAMETER(ncargs);
|
||
|
||
UTF8 *s;
|
||
int anum;
|
||
|
||
// Skip the '&'
|
||
//
|
||
arg1++;
|
||
|
||
// Take to the space
|
||
//
|
||
for (s = arg1; *s && !mux_isspace(*s); s++)
|
||
{
|
||
; // Nothing.
|
||
}
|
||
|
||
// Split it
|
||
//
|
||
if (*s)
|
||
{
|
||
*s++ = '\0';
|
||
}
|
||
|
||
// Get or make attribute
|
||
//
|
||
anum = mkattr(executor, reinterpret_cast<UTF8 *>(arg1));
|
||
|
||
if (anum <= 0)
|
||
{
|
||
notify_quiet(executor, M_("That’s not a good name for an attribute."));
|
||
return;
|
||
}
|
||
do_setattr(executor, caller, enactor, 0, anum, 2, s, arg2, nullptr, 0);
|
||
}
|