tinymux/mux/modules/engine/look.cpp
Stephen Dennis 1862b248d7 nls: mark speech pose formats, look UI, engine/db set notifies (#1685)
Phase 3 coverage:

  speech   pose/semipose composition (%s %s / %s%s), shout glue
  look     exit destination and command-scan lines (not decompile)
  engine   @timecheck counted-objects summary (screen + log)
  db       @stats field sets, forward/permission errors

Decompile @create/@set templates, HTML, and softcode #dbref args stay T().
Regenerates pot; rebuilds xx.po for complete catalogue.
2026-07-28 15:16:04 +00:00

3239 lines
87 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 look.cpp
* \brief Commands which look at things.
*
*/
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
#define NORMAL_REALM 0
#define UMBRA_REALM 1
#define SHROUD_REALM 2
#define MATRIX_REALM 3
#define FAE_REALM 4
#define CHIMERA_REALM 5
#define BLIND_REALM 6
#define STAFF_REALM 7
#define NUMBER_OF_REALMS 8
static int RealmActions[NUMBER_OF_REALMS] =
{
REALM_DO_NORMALLY_SEEN,
REALM_DO_SHOW_UMBRADESC,
REALM_DO_SHOW_WRAITHDESC,
REALM_DO_SHOW_MATRIXDESC,
REALM_DO_SHOW_FAEDESC,
REALM_DO_SHOW_FAEDESC,
REALM_DO_HIDDEN_FROM_YOU,
REALM_DO_NORMALLY_SEEN
};
// Umbra and Matrix are realms unto themselves, so if you aren't in the same
// realm as what you're looking at, you can't see it.
//
// Normal things can't see shroud things, but shroud things can see normal things.
//
// Only Fae and Chimera can see Chimera.
//
#define MAP_SEEN 0 // Show this to that.
#define MAP_HIDE 1 // Always hide this from that.
#define MAP_NO_ADESC 2 // Don't trigger DESC actions on that.
#define MAP_MEDIUM 4 // Hide this from that unless that is a medium and this is moving or talking.
static int RealmHiddenMap[NUMBER_OF_REALMS][NUMBER_OF_REALMS] =
{
/* NORMAL LOOKER */ { MAP_SEEN, MAP_HIDE, MAP_MEDIUM, MAP_HIDE, MAP_SEEN, MAP_HIDE, MAP_NO_ADESC, MAP_SEEN},
/* UMBRA LOOKER */ { MAP_HIDE, MAP_SEEN, MAP_MEDIUM, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_SEEN},
/* SHROUD LOOKER */ { MAP_NO_ADESC, MAP_HIDE, MAP_SEEN, MAP_HIDE, MAP_NO_ADESC, MAP_HIDE, MAP_NO_ADESC, MAP_SEEN},
/* MATRIX LOOKER */ { MAP_HIDE, MAP_HIDE, MAP_MEDIUM, MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_SEEN},
/* FAE LOOKER */ { MAP_SEEN, MAP_HIDE, MAP_MEDIUM, MAP_HIDE, MAP_SEEN, MAP_SEEN, MAP_NO_ADESC, MAP_SEEN},
/* CHIMERA LOOKER */ { MAP_NO_ADESC, MAP_HIDE, MAP_MEDIUM, MAP_HIDE, MAP_SEEN, MAP_SEEN, MAP_NO_ADESC, MAP_SEEN},
/* BLIND LOOKER */ { MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_SEEN},
/* STAFF LOOKER */ { MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN}
};
static int RealmExitsMap[NUMBER_OF_REALMS][NUMBER_OF_REALMS] =
{
/* NORMAL LOOKER */ { MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE},
/* UMBRA LOOKER */ { MAP_SEEN, MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE},
/* SHROUD LOOKER */ { MAP_SEEN, MAP_HIDE, MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE},
/* MATRIX LOOKER */ { MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE},
/* FAE LOOKER */ { MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_SEEN, MAP_SEEN, MAP_HIDE, MAP_HIDE},
/* CHIMERA LOOKER */ { MAP_SEEN, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_SEEN, MAP_SEEN, MAP_HIDE, MAP_HIDE},
/* BLIND LOOKER */ { MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE, MAP_HIDE},
/* STAFF LOOKER */ { MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN, MAP_SEEN}
};
static int WhichRealm(dbref what, bool bPeering)
{
int realm = NORMAL_REALM;
if (isMatrix(what)) realm = MATRIX_REALM;
else if (isUmbra(what)) realm = UMBRA_REALM;
else if (isShroud(what)) realm = SHROUD_REALM;
else if (isChimera(what)) realm = CHIMERA_REALM;
else if (isFae(what)) realm = FAE_REALM;
if (bPeering)
{
UTF8 *buff;
dbref owner;
int flags;
int iPeeringRealm = get_atr(T("PEERING_REALM"));
if (0 < iPeeringRealm)
{
buff = atr_get("WhichRealm.99", what, iPeeringRealm, &owner, &flags);
if (*buff)
{
if (mux_stricmp(buff, T("FAE")) == 0) realm = FAE_REALM;
else if (mux_stricmp(buff, T("CHIMERA")) == 0) realm = CHIMERA_REALM;
else if (mux_stricmp(buff, T("SHROUD")) == 0) realm = SHROUD_REALM;
else if (mux_stricmp(buff, T("UMBRA")) == 0) realm = UMBRA_REALM;
else if (mux_stricmp(buff, T("MATRIX")) == 0) realm = MATRIX_REALM;
else if (mux_stricmp(buff, T("NORMAL")) == 0) realm = NORMAL_REALM;
else if (mux_stricmp(buff, T("BLIND")) == 0) realm = BLIND_REALM;
else if (mux_stricmp(buff, T("STAFF")) == 0) realm = STAFF_REALM;
}
free_lbuf(buff);
}
}
return realm;
}
static int GetRealmLevel(dbref executor, dbref thing, const UTF8 *attrname)
{
int atr = get_atr(attrname);
if (atr <= 0)
{
return 0;
}
dbref aowner;
int aflags;
UTF8 *buff = atr_get("GetRealmLevel", thing, atr, &aowner, &aflags);
if (!*buff)
{
free_lbuf(buff);
return 0;
}
int result;
if (is_integer(buff, nullptr))
{
// Fast path: plain integer, no evaluation needed.
//
result = mux_atoi64(buff);
}
else
{
// Evaluate as softcode expression.
//
UTF8 *ebuf = alloc_lbuf("GetRealmLevel.eval");
UTF8 *ep = ebuf;
mux_exec(buff, LBUF_SIZE-1, ebuf, &ep, executor, thing, executor,
AttrTrace(aflags, EV_FCHECK|EV_EVAL|EV_TOP),
nullptr, 0);
*ep = '\0';
result = mux_atoi64(ebuf);
free_lbuf(ebuf);
}
free_lbuf(buff);
return result;
}
static int HandleObfuscation(dbref looker, dbref lookee, int threshhold)
{
int iReturn = REALM_DO_NORMALLY_SEEN;
if (isObfuscate(lookee))
{
int iObfuscateLevel = GetRealmLevel(looker, lookee, T("OBF_LEVEL"));
// For OBF_LEVELS of 0, 1, and 2, we show the regular description.
// 3 and above start showing a different OBFDESC.
//
if (3 <= iObfuscateLevel)
{
iReturn = REALM_DO_SHOW_OBFDESC;
}
if (threshhold < iObfuscateLevel)
{
int iHeightenSensesLevel = 0;
if (isHeightenedSenses(looker))
{
iHeightenSensesLevel = GetRealmLevel(looker, looker, T("HSS_LEVEL"));
}
if (iHeightenSensesLevel < iObfuscateLevel)
{
iReturn = REALM_DO_HIDDEN_FROM_YOU;
}
else if (iObfuscateLevel == iHeightenSensesLevel)
{
if (RandomINT32(0,1))
{
iReturn = REALM_DO_HIDDEN_FROM_YOU;
}
}
}
}
return iReturn;
}
int DoThingToThingVisibility(dbref looker, dbref lookee, int action_state)
{
// If the looker is a room, then there is some contents/recursion stuff
// that happens in the rest of the game code. We'll be called later for
// each item in the room, things that are nearby the room, etc.
//
if (isRoom(looker))
{
return REALM_DO_NORMALLY_SEEN;
}
if (Staff(looker))
{
if (Connected(looker))
{
// Staff players can see everything
//
return REALM_DO_NORMALLY_SEEN;
}
if (isThing(looker))
{
// Wizard things in the master room can see everything.
//
if (Location(looker) == mudconf.master_room)
{
return REALM_DO_NORMALLY_SEEN;
}
}
}
int realmLooker = WhichRealm(looker, isPeering(looker));
int realmLookee = WhichRealm(lookee, false);
// You can always see yourself.
//
if (looker == lookee)
{
return RealmActions[realmLooker];
}
bool bDisableADESC = false;
if (isRoom(lookee) || isExit(lookee))
{
// All realms see normal rooms and exits, however, if a realm
// specific description exists, they use that one. If a room or exit
// is flagged with a specific realm, then -only- players and things of
// that realm can see it.
//
// Fae and Chimera are treated as the same realm for this purpose.
//
if (RealmExitsMap[realmLooker][realmLookee] == MAP_HIDE)
{
return REALM_DO_HIDDEN_FROM_YOU;
}
}
else
{
if (Staff(lookee))
{
// Staff players are seen in every realm.
//
if (Connected(lookee))
{
return REALM_DO_NORMALLY_SEEN;
}
if (isThing(lookee))
{
// Everyone can use wizard things in the master room.
//
// Wizard things that aren't in the master room follow the same
// realm-rules as everything else.
//
if (Location(lookee) == mudconf.master_room)
{
return REALM_DO_NORMALLY_SEEN;
}
}
}
int iMap = RealmHiddenMap[realmLooker][realmLookee];
if (iMap & MAP_HIDE)
{
return REALM_DO_HIDDEN_FROM_YOU;
}
if (iMap & MAP_MEDIUM)
{
if (isMedium(looker))
{
if (action_state == ACTION_IS_STATIONARY)
{
// Even Mediums can't hear it if the Wraith is just standing still.
//
return REALM_DO_HIDDEN_FROM_YOU;
}
}
else
{
return REALM_DO_HIDDEN_FROM_YOU;
}
}
if (iMap & MAP_NO_ADESC)
{
bDisableADESC = true;
}
}
// Do default see rules.
//
int iReturn = RealmActions[realmLooker];
if (iReturn == REALM_DO_HIDDEN_FROM_YOU)
{
return iReturn;
}
// Do Obfuscate/Heighten Senses rules.
//
int threshhold = 0;
switch (action_state)
{
case ACTION_IS_STATIONARY:
threshhold = 0;
break;
case ACTION_IS_MOVING:
threshhold = 1;
break;
case ACTION_IS_TALKING:
if (bDisableADESC)
{
iReturn |= REALM_DISABLE_ADESC;
}
return iReturn;
}
int iObfReturn = HandleObfuscation(looker, lookee, threshhold);
switch (iObfReturn)
{
case REALM_DO_SHOW_OBFDESC:
case REALM_DO_HIDDEN_FROM_YOU:
iReturn = iObfReturn;
break;
}
// Decide whether to disable ADESC or not. If we can look at them,
// and ADESC isn't -already- disabled via SHROUD looking at NORMAL (see above).
// then, ADESC may be disabled here if the looker is Obfuscated to the lookee.
//
if (iReturn != REALM_DO_HIDDEN_FROM_YOU && !bDisableADESC)
{
if (REALM_DO_HIDDEN_FROM_YOU == HandleObfuscation(lookee, looker, 0))
{
bDisableADESC = true;
}
}
if (bDisableADESC)
{
iReturn |= REALM_DISABLE_ADESC;
}
return iReturn;
}
static void LetDescriptionsDefault(dbref thing, int *piDESC, int *piADESC, int RealmDirective)
{
int iDesc = 0;
dbref owner;
int flags;
*piDESC = A_DESC;
*piADESC = A_ADESC;
if (RealmDirective & REALM_DISABLE_ADESC)
{
*piADESC = 0;
}
switch (RealmDirective & REALM_DO_MASK)
{
case REALM_DO_SHOW_OBFDESC:
iDesc = get_atr(T("OBFDESC"));
break;
case REALM_DO_SHOW_WRAITHDESC:
iDesc = get_atr(T("WRAITHDESC"));
*piADESC = 0;
break;
case REALM_DO_SHOW_UMBRADESC:
iDesc = get_atr(T("UMBRADESC"));
break;
case REALM_DO_SHOW_MATRIXDESC:
iDesc = get_atr(T("MATRIXDESC"));
break;
case REALM_DO_SHOW_FAEDESC:
iDesc = get_atr(T("FAEDESC"));
break;
}
if (iDesc > 0)
{
UTF8 *buff = atr_pget(thing, iDesc, &owner, &flags);
if (buff)
{
if (*buff)
{
*piDESC = iDesc;
}
free_lbuf(buff);
}
}
}
#endif
static void look_exits(dbref player, dbref loc, const UTF8 *exit_name)
{
// Make sure location has exits.
//
if ( !Good_obj(loc)
|| !Has_exits(loc))
{
return;
}
dbref thing, parent;
UTF8 *buff, *e, *buff1, *e1;
const UTF8 *s;
// make sure there is at least one visible exit.
//
bool bFoundAnyDisplayable = false;
bool bFoundAny = false;
int key = 0;
int lev;
#ifdef REALITY_LVLS
if (Dark(loc) || !IsReal(player, loc))
#else
if (Dark(loc))
#endif // REALITY_LVLS
{
key |= VE_BASE_DARK;
}
ITER_PARENTS(loc, parent, lev)
{
key &= ~VE_LOC_DARK;
if (Dark(parent))
{
key |= VE_LOC_DARK;
}
DOLIST(thing, Exits(parent))
{
bFoundAny = true;
if (exit_displayable(thing, player, key))
{
bFoundAnyDisplayable = true;
break;
}
}
if (bFoundAnyDisplayable)
{
break;
}
}
if (!bFoundAny)
{
return;
}
// Retrieve the ExitFormat attribute from the location, evaluate and display
// the results in lieu of the traditional exits list if it exists.
//
dbref aowner;
int aflags;
UTF8 *ExitFormat = atr_pget(loc, A_EXITFORMAT, &aowner, &aflags);
bool bDisplayExits = bFoundAnyDisplayable;
if (*ExitFormat)
{
UTF8 *VisibleObjectList = alloc_lbuf("look_exits.VOL");
UTF8 *tPtr = VisibleObjectList;
ITL pContext;
ItemToList_Init(&pContext, VisibleObjectList, &tPtr, '#');
ITER_PARENTS(loc, parent, lev)
{
key &= ~VE_LOC_DARK;
if (Dark(parent))
{
key |= VE_LOC_DARK;
}
bool bShortCircuit = false;
DOLIST(thing, Exits(parent))
{
if ( exit_displayable(thing, player, key)
&& !ItemToList_AddInteger(&pContext, thing))
{
bShortCircuit = true;
break;
}
}
if (bShortCircuit) break;
}
ItemToList_Final(&pContext);
UTF8 *FormatOutput = alloc_lbuf("look_exits.FO");
tPtr = FormatOutput;
reg_ref **preserve = nullptr;
preserve = PushRegisters(MAX_GLOBAL_REGS);
save_and_clear_global_regs(preserve);
if ((aflags & AF_NOEVAL) || NoEval(loc))
{
mux_strncpy(FormatOutput, ExitFormat, LBUF_SIZE-1);
tPtr = FormatOutput + strlen((const char *)FormatOutput);
}
else
{
mux_exec(ExitFormat, LBUF_SIZE-1, FormatOutput, &tPtr, loc, player, player,
AttrTrace(aflags, EV_FCHECK|EV_EVAL|EV_TOP),
(const UTF8 **)&VisibleObjectList, 1);
}
*tPtr = '\0';
restore_global_regs(preserve);
PopRegisters(preserve, MAX_GLOBAL_REGS);
notify(player, FormatOutput);
free_lbuf(FormatOutput);
free_lbuf(VisibleObjectList);
bDisplayExits = 0;
}
free_lbuf(ExitFormat);
if (!bDisplayExits)
{
return;
}
// Display the list of exit names
//
notify(player, exit_name);
e = buff = alloc_lbuf("look_exits");
e1 = buff1 = alloc_lbuf("look_exits2");
ITER_PARENTS(loc, parent, lev)
{
key &= ~VE_LOC_DARK;
if (Dark(parent))
{
key |= VE_LOC_DARK;
}
if (Transparent(loc))
{
DOLIST(thing, Exits(parent))
{
if (exit_displayable(thing, player, key))
{
mux_strncpy(buff, Moniker(thing), LBUF_SIZE-1);
for (e = buff; *e && *e != ';'; e++)
{
; // Nothing.
}
*e = '\0';
notify(player, tprintf(M_("%s leads to %s."), buff, Moniker(Location(thing))));
}
}
}
else
{
DOLIST(thing, Exits(parent))
{
if (exit_displayable(thing, player, key))
{
e1 = buff1;
// Put the exit name in buff1.
//
// chop off first exit alias to display
//
if (buff != e)
{
safe_str(T(" "), buff, &e);
}
for (s = Moniker(thing); *s && (*s != ';'); s++)
{
safe_chr(*s, buff1, &e1);
}
*e1 = 0;
/* Copy the exit name into 'buff' */
if (Html(player))
{
safe_str(T("<a xch_cmd=\""), buff, &e);
html_escape(buff1, buff, &e);
safe_str(T("\"> "), buff, &e);
html_escape(buff1, buff, &e);
safe_str(T(" </a>"), buff, &e);
}
else
{
/* Append this exit to the list */
safe_str(buff1, buff, &e);
}
}
}
}
}
if (!Transparent(loc))
{
if (Html(player))
{
safe_str(T("\r\n"), buff, &e);
*e = 0;
notify_html(player, buff);
}
else
{
*e = 0;
notify(player, buff);
}
}
free_lbuf(buff);
free_lbuf(buff1);
}
#define CONTENTS_LOCAL 0
#define CONTENTS_NESTED 1
#define CONTENTS_REMOTE 2
static void look_contents(dbref player, dbref loc, const UTF8 *contents_name, int style)
{
dbref thing;
UTF8 *buff;
UTF8 *html_buff, *html_cp;
UTF8 remote_num[I32BUF_SIZE+1];
// Check to see if he can see the location.
//
#ifdef REALITY_LVLS
bool can_see_loc = ( !Dark(loc) && IsReal(player, loc)
#else
bool can_see_loc = ( !Dark(loc)
#endif // REALITY_LVLS
|| (mudconf.see_own_dark && Examinable(player, loc)));
dbref aowner;
int aflags;
UTF8 *ContentsFormat = atr_pget(loc, A_CONFORMAT, &aowner, &aflags);
bool bDisplayContents = true;
if (*ContentsFormat)
{
UTF8 *VisibleObjectList = alloc_lbuf("look_contents.VOL");
UTF8 *tPtr = VisibleObjectList;
ITL pContext;
ItemToList_Init(&pContext, VisibleObjectList, &tPtr, '#');
DOLIST(thing, Contents(loc))
{
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
if ( can_see(player, thing, can_see_loc)
&& (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(player,
thing, ACTION_IS_STATIONARY)) )
#else
if (can_see(player, thing, can_see_loc))
#endif
{
if (!ItemToList_AddInteger(&pContext, thing))
{
break;
}
}
}
ItemToList_Final(&pContext);
UTF8 *ContentsNameScratch = alloc_lbuf("look_contents.CNS");
tPtr = ContentsNameScratch;
safe_str(contents_name, ContentsNameScratch, &tPtr);
*tPtr = '\0';
UTF8 *FormatOutput = alloc_lbuf("look_contents.FO");
tPtr = FormatOutput;
const UTF8 *ParameterList[] =
{ VisibleObjectList, ContentsNameScratch };
reg_ref **preserve = nullptr;
preserve = PushRegisters(MAX_GLOBAL_REGS);
save_and_clear_global_regs(preserve);
if ((aflags & AF_NOEVAL) || NoEval(loc))
{
mux_strncpy(FormatOutput, ContentsFormat, LBUF_SIZE-1);
tPtr = FormatOutput + strlen((const char *)FormatOutput);
}
else
{
mux_exec(ContentsFormat, LBUF_SIZE-1, FormatOutput, &tPtr, loc, player, player,
AttrTrace(aflags, EV_FCHECK|EV_EVAL|EV_TOP),
ParameterList, 2);
}
*tPtr = '\0';
restore_global_regs(preserve);
PopRegisters(preserve, MAX_GLOBAL_REGS);
notify(player, FormatOutput);
free_lbuf(FormatOutput);
free_lbuf(ContentsNameScratch);
free_lbuf(VisibleObjectList);
bDisplayContents = false;
}
free_lbuf(ContentsFormat);
if (!bDisplayContents)
{
return;
}
html_buff = html_cp = alloc_lbuf("look_contents");
// Check to see if there is anything there.
//
DOLIST(thing, Contents(loc))
{
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
if ( can_see(player, thing, can_see_loc)
&& (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(player, thing, ACTION_IS_STATIONARY)))
#else
if (can_see(player, thing, can_see_loc))
#endif
{
// Something exists! Show him everything.
//
notify(player, contents_name);
DOLIST(thing, Contents(loc))
{
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
if ( can_see(player, thing, can_see_loc)
&& (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(player, thing, ACTION_IS_STATIONARY)))
#else
if (can_see(player, thing, can_see_loc))
#endif
{
buff = unparse_object(player, thing, true);
html_cp = html_buff;
if (Html(player))
{
safe_str(T("<a xch_cmd=\"look "), html_buff, &html_cp);
switch (style)
{
// Monikers land inside the xch_cmd="..." attribute, so
// they need the same escaping look_exits() gives exit
// names. Inserted raw, a moniker containing a double
// quote closed the attribute early and let anything
// after it become further Pueblo/HTML markup for
// HTML-capable clients (#1186). The anchor text below
// was already escaped; only these were not.
//
case CONTENTS_LOCAL:
html_escape(Moniker(thing), html_buff, &html_cp);
break;
case CONTENTS_NESTED:
html_escape(Moniker(Location(thing)), html_buff, &html_cp);
safe_str(T("s "), html_buff, &html_cp);
html_escape(Moniker(thing), html_buff, &html_cp);
break;
case CONTENTS_REMOTE:
remote_num[0] = '#';
mux_ltoa(thing, remote_num+1);
safe_str(remote_num, html_buff, &html_cp);
break;
default:
break;
}
safe_str(T("\">"), html_buff, &html_cp);
html_escape(buff, html_buff, &html_cp);
safe_str(T("</a>\r\n"), html_buff, &html_cp);
*html_cp = 0;
notify_html(player, html_buff);
}
else
{
notify(player, buff);
}
free_lbuf(buff);
}
}
break; // we're done.
}
}
free_lbuf(html_buff);
}
typedef struct
{
int mask;
UTF8 letter;
const UTF8 *name;
} ATTR_DECODE_ENTRY, *PATTR_DECODE_ENTRY;
static ATTR_DECODE_ENTRY attr_decode_table[NUM_ATTRIBUTE_CODES+1] =
{
{ AF_LOCK, '+', T("LOCK") },
{ AF_NOPROG, '$', T("NO_COMMAND") },
{ AF_CASE, 'C', T("CASE") },
{ AF_HTML, 'H', T("HTML") },
{ AF_PRIVATE, 'I', T("NO_INHERIT") },
{ AF_NOEVAL, 'E', T("NO_EVAL") },
{ AF_NONAME, 'N', T("NO_NAME") },
{ AF_NOPARSE, 'P', T("NO_PARSE") },
{ AF_REGEXP, 'R', T("REGEXP") },
{ AF_TRACE, 'T', T("TRACE") },
{ AF_VISUAL, 'V', T("VISUAL") },
{ AF_MDARK, 'M', T("DARK") },
{ AF_WIZARD, 'W', T("WIZARD") },
{ 0, 0, nullptr }
};
size_t decode_attr_flags(int aflags, UTF8 buff[NUM_ATTRIBUTE_CODES+1])
{
size_t n = 0;
PATTR_DECODE_ENTRY pEntry;
for (pEntry = attr_decode_table; pEntry->mask && n < NUM_ATTRIBUTE_CODES; pEntry++)
{
if (aflags & pEntry->mask)
{
buff[n++] = pEntry->letter;
}
}
buff[n] = '\0';
return n;
}
void decode_attr_flag_names(int aflags, UTF8 *buf, UTF8 **bufc)
{
PATTR_DECODE_ENTRY pEntry;
bool bFirst = true;
for (pEntry = attr_decode_table; pEntry->mask; pEntry++)
{
if (aflags & pEntry->mask)
{
if (!bFirst)
{
safe_chr(' ', buf, bufc);
}
bFirst = false;
safe_str(pEntry->name, buf, bufc);
}
}
}
static void view_atr
(
dbref player,
dbref thing,
ATTR *ap,
const UTF8 *text,
dbref aowner,
int aflags,
bool skip_tag
)
{
const UTF8 *buf;
if (ap->flags & AF_IS_LOCK)
{
BOOLEXP *pBoolExp = parse_boolexp(player, text, true);
text = unparse_boolexp(player, pBoolExp);
free_boolexp(pBoolExp);
}
// If we don't control the object or own the attribute, hide the
// attr owner and flag info.
//
if ( !Controls(player, thing)
&& Owner(player) != aowner)
{
if ( skip_tag
&& ap->number == A_DESC)
{
buf = text;
}
else
{
buf = tprintf(T("%s%s:%s %s"), COLOR_INTENSE, ap->name, COLOR_RESET, text);
}
notify(player, buf);
return;
}
// Generate flags. decode_attr_flags() can emit up to NUM_ATTRIBUTE_CODES
// letters plus a NUL terminator, so the buffer must match its contract.
//
UTF8 xbuf[NUM_ATTRIBUTE_CODES+1];
decode_attr_flags(aflags, xbuf);
if ( aowner != Owner(thing)
&& aowner != NOTHING)
{
buf = tprintf(T("%s%s [#%d%s]:%s %s"), COLOR_INTENSE,
ap->name, aowner, xbuf, COLOR_RESET, text);
}
else if (*xbuf)
{
buf = tprintf(T("%s%s [%s]:%s %s"), COLOR_INTENSE, ap->name,
xbuf, COLOR_RESET, text);
}
else if ( !skip_tag
|| ap->number != A_DESC)
{
buf = tprintf(T("%s%s:%s %s"), COLOR_INTENSE, ap->name, COLOR_RESET, text);
}
else
{
buf = text;
}
notify(player, buf);
}
static void look_atrs1
(
dbref player,
dbref thing,
dbref othing,
bool check_exclude,
bool hash_insert
)
{
bool bFoundCommands = false;
bool bFoundListens = false;
// Collect attribute numbers, then sort by name for alphabetical output.
//
unsigned char *as;
std::vector<int> atrs;
for (int ca = atr_head(thing, &as); ca; ca = atr_next(&as))
{
atrs.push_back(ca);
}
std::sort(atrs.begin(), atrs.end(), [](int a, int b)
{
ATTR *pa = atr_num(a);
ATTR *pb = atr_num(b);
if (!pa) return false;
if (!pb) return true;
return mux_stricmp(pa->name, pb->name) < 0;
});
for (int ca : atrs)
{
if ( ca == A_DESC
|| ca == A_LOCK)
{
continue;
}
ATTR *pattr = atr_num(ca);
if (!pattr)
{
continue;
}
ATTR cattr;
memcpy(&cattr, pattr, sizeof(ATTR));
// Should we exclude this attr?
//
if ( check_exclude
&& ( (pattr->flags & AF_PRIVATE)
|| mudstate.parent_htab.find(ca) != mudstate.parent_htab.end()))
{
continue;
}
int aflags;
dbref aowner;
UTF8 *buf = atr_get("look_atrs1.916", thing, ca, &aowner, &aflags);
if (!(aflags & AF_NOPROG))
{
if ( AMATCH_CMD == buf[0]
|| AMATCH_LISTEN == buf[0])
{
UTF8 *s = find_pattern_delimiter(buf + 1);
if (s)
{
if (AMATCH_CMD == buf[0])
{
bFoundCommands = true;
}
else
{
bFoundListens = true;
}
}
}
}
if (bCanReadAttr(player, othing, &cattr, false))
{
if (!(check_exclude && (aflags & AF_PRIVATE)))
{
if (hash_insert)
{
mudstate.parent_htab.emplace(ca, pattr);
}
view_atr(player, thing, &cattr, buf, aowner, aflags, false);
}
}
free_lbuf(buf);
}
if (bFoundCommands)
{
mudstate.bfNoCommands.Clear(thing);
mudstate.bfCommands.Set(thing);
}
else
{
mudstate.bfCommands.Clear(thing);
mudstate.bfNoCommands.Set(thing);
}
if (bFoundListens)
{
mudstate.bfNoListens.Clear(thing);
mudstate.bfListens.Set(thing);
}
else
{
mudstate.bfListens.Clear(thing);
mudstate.bfNoListens.Set(thing);
}
}
static void look_atrs(dbref player, dbref thing, bool check_parents)
{
dbref parent;
int lev;
bool check_exclude, hash_insert;
if (!check_parents)
{
look_atrs1(player, thing, thing, false, false);
}
else
{
hash_insert = true;
check_exclude = false;
mudstate.parent_htab.clear();
ITER_PARENTS(thing, parent, lev)
{
if (!Good_obj(Parent(parent)))
{
hash_insert = false;
}
look_atrs1(player, parent, thing, check_exclude, hash_insert);
check_exclude = true;
}
}
}
static bool show_a_desc(dbref player, dbref loc)
{
int iDescDefault = A_DESC;
int iADescDefault = A_ADESC;
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
int iRealmDirective = DoThingToThingVisibility(player, loc, ACTION_IS_STATIONARY);
if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective)
{
return true;
}
LetDescriptionsDefault(loc, &iDescDefault, &iADescDefault, iRealmDirective);
#endif
bool ret = false;
dbref aowner1;
int aflags1;
bool indent = (isRoom(loc) && mudconf.indent_desc && atr_get_raw(loc, A_DESC));
UTF8 *DescFormat = atr_pget(loc, A_DESCFORMAT, &aowner1, &aflags1);
if (*DescFormat)
{
reg_ref **preserve = nullptr;
preserve = PushRegisters(MAX_GLOBAL_REGS);
save_global_regs(preserve);
UTF8 *FormatOutput = alloc_lbuf("look_description.FO");
UTF8 *tPtr = FormatOutput;
int aflags2 = 0;
int iDescUsed = iDescDefault;
#ifdef REALITY_LVLS
UTF8 *tbuf1 = get_rlevel_desc(player, loc, &iDescUsed);
#else
dbref aowner2;
UTF8 *tbuf1 = atr_pget(loc, iDescDefault, &aowner2, &aflags2);
#endif
ATTR *cattr = atr_num(iDescUsed);
UTF8 *temp = alloc_lbuf("look_description.ET");
UTF8 *bp = temp;
#ifdef REALITY_LVLS
// get_rlevel_desc() already evaluates the description.
//
mux_strncpy(temp, tbuf1, LBUF_SIZE-1);
#else
if ((aflags2 & AF_NOEVAL) || NoEval(loc))
{
mux_strncpy(temp, tbuf1, LBUF_SIZE-1);
}
else
{
mux_exec(tbuf1, LBUF_SIZE-1, temp, &bp, loc, player, player,
AttrTrace(aflags2, EV_FCHECK|EV_EVAL|EV_TOP),
nullptr, 0);
}
*bp = '\0';
#endif
UTF8 *attrname = alloc_lbuf("look_description.AN");
UTF8 *cp = attrname;
safe_str(cattr->name, attrname, &cp);
*cp = '\0';
const UTF8 *ParameterList[] =
{ temp, attrname };
if ((aflags1 & AF_NOEVAL) || NoEval(loc))
{
mux_strncpy(FormatOutput, DescFormat, LBUF_SIZE-1);
tPtr = FormatOutput + strlen((const char *)FormatOutput);
}
else
{
mux_exec(DescFormat, LBUF_SIZE-1, FormatOutput, &tPtr, loc, player, player,
AttrTrace(aflags1, EV_FCHECK|EV_EVAL|EV_TOP),
ParameterList, 2);
}
*tPtr = '\0';
notify(player, FormatOutput);
#ifdef REALITY_LVLS
did_it_rlevel(player, loc, 0, nullptr, A_ODESC, nullptr, iADescDefault, 0,
nullptr, 0);
#else
did_it(player, loc, 0, nullptr, A_ODESC, nullptr, iADescDefault, 0,
nullptr, 0);
#endif // REALITY_LVLS
free_lbuf(tbuf1);
free_lbuf(attrname);
free_lbuf(FormatOutput);
free_lbuf(temp);
restore_global_regs(preserve);
PopRegisters(preserve, MAX_GLOBAL_REGS);
ret = true;
}
else
{
UTF8 *got;
if (Html(player))
{
got = atr_pget(loc, A_HTDESC, &aowner1, &aflags1);
if (*got)
{
#ifdef REALITY_LVLS
did_it_rlevel(player, loc, A_HTDESC, nullptr, A_ODESC, nullptr,
A_ADESC, 0, nullptr, 0);
#else
did_it(player, loc, A_HTDESC, nullptr, A_ODESC, nullptr, A_ADESC,
0, nullptr, 0);
#endif // REALITY_LVLS
ret = true;
}
else
{
free_lbuf(got);
got = atr_pget(loc, iDescDefault, &aowner1, &aflags1);
if (*got)
{
if (indent)
{
raw_notify_newline(player);
}
#ifdef REALITY_LVLS
did_it_rlevel(player, loc, iDescDefault, nullptr, A_ODESC,
nullptr, iADescDefault, 0, nullptr, 0);
#else
did_it(player, loc, iDescDefault, nullptr, A_ODESC, nullptr,
iADescDefault, 0, nullptr, 0);
#endif // REALITY_LVLS
if (indent)
{
raw_notify_newline(player);
}
ret = true;
}
}
}
else if (*(got = atr_pget(loc, iDescDefault, &aowner1, &aflags1)))
{
if (indent)
{
raw_notify_newline(player);
}
#ifdef REALITY_LVLS
did_it_rlevel(player, loc, iDescDefault, nullptr, A_ODESC, nullptr,
iADescDefault, 0, nullptr, 0);
#else
did_it(player, loc, iDescDefault, nullptr, A_ODESC, nullptr,
iADescDefault, 0, nullptr, 0);
#endif // REALITY_LVLS
if (indent)
{
raw_notify_newline(player);
}
ret = true;
}
free_lbuf(got);
}
free_lbuf(DescFormat);
return ret;
}
static void look_simple(dbref player, dbref thing, bool obey_terse)
{
// Only makes sense for things that can hear.
//
if (!Hearer(player))
{
return;
}
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
int iRealmDirective = DoThingToThingVisibility(player, thing, ACTION_IS_STATIONARY);
if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective)
{
notify(player, NOMATCH_MESSAGE);
return;
}
#endif
if (isPlayer(thing))
{
// Players use @moniker, not @nameformat.
//
if (Examinable(player, thing))
{
UTF8 *buff = unparse_object(player, thing, true);
notify(player, buff);
free_lbuf(buff);
}
}
else
{
// Use @nameformat if present on things and exits.
//
dbref aowner;
int aflags;
UTF8 *NameFormat = atr_pget(thing, A_NAMEFORMAT, &aowner, &aflags);
if (*NameFormat)
{
UTF8 *FormatOutput = alloc_lbuf("look_simple.FO");
UTF8 *tPtr = FormatOutput;
reg_ref **preserve = nullptr;
preserve = PushRegisters(MAX_GLOBAL_REGS);
save_and_clear_global_regs(preserve);
if ((aflags & AF_NOEVAL) || NoEval(thing))
{
mux_strncpy(FormatOutput, NameFormat, LBUF_SIZE-1);
tPtr = FormatOutput + strlen((const char *)FormatOutput);
}
else
{
mux_exec(NameFormat, LBUF_SIZE-1, FormatOutput, &tPtr, thing,
player, player,
AttrTrace(aflags, EV_FCHECK|EV_EVAL|EV_TOP),
0, 0);
}
*tPtr = '\0';
restore_global_regs(preserve);
PopRegisters(preserve, MAX_GLOBAL_REGS);
notify(player, FormatOutput);
free_lbuf(FormatOutput);
}
else if (Examinable(player, thing))
{
UTF8 *buff = unparse_object(player, thing, true);
notify(player, buff);
free_lbuf(buff);
}
free_lbuf(NameFormat);
}
int iDescDefault = A_DESC;
int iADescDefault = A_ADESC;
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
LetDescriptionsDefault(thing, &iDescDefault, &iADescDefault, iRealmDirective);
#endif
int pattr = (obey_terse && Terse(player)) ? 0 : iDescDefault;
if (!show_a_desc(player, thing))
{
notify(player, M_("You see nothing special."));
#ifdef REALITY_LVLS
did_it_rlevel(player, thing, 0, nullptr, A_ODESC, nullptr, iADescDefault,
0, nullptr, 0);
#else
did_it(player, thing, pattr, nullptr, A_ODESC, nullptr, iADescDefault,
0, nullptr, 0);
#endif // REALITY_LVLS
}
if ( !mudconf.quiet_look
&& ( !Terse(player)
|| mudconf.terse_look))
{
look_atrs(player, thing, false);
}
}
static void show_desc(dbref player, dbref loc, int key)
{
UTF8 *got;
dbref aowner;
int aflags;
if ( (key & LK_OBEYTERSE)
&& Terse(player))
{
#ifdef REALITY_LVLS
did_it_rlevel(player, loc, 0, nullptr, A_ODESC, nullptr, A_ADESC, 0,
nullptr, 0);
#else
did_it(player, loc, 0, nullptr, A_ODESC, nullptr, A_ADESC, 0,
nullptr, 0);
#endif // REALITY_LVLS
}
else if ( !isRoom(loc)
&& (key & LK_IDESC))
{
if (*(got = atr_pget(loc, A_IDESC, &aowner, &aflags)))
{
#ifdef REALITY_LVLS
did_it_rlevel(player, loc, A_IDESC, nullptr, A_ODESC, nullptr, A_ADESC,
0, nullptr, 0);
#else
did_it(player, loc, A_IDESC, nullptr, A_ODESC, nullptr, A_ADESC, 0,
nullptr, 0);
#endif // REALITY_LVLS
}
else
{
show_a_desc(player, loc);
}
free_lbuf(got);
}
else
{
show_a_desc(player, loc);
}
}
void look_in(dbref player, dbref loc, int key)
{
// Only makes sense for things that can hear.
//
if (!Hearer(player))
{
return;
}
// If he needs the VMRL URL, send it:
//
if (key & LK_SHOWVRML)
{
show_vrml_url(player, loc);
}
// Use @nameformat (by Marlek) if it's present, otherwise, use the
// name and if the player can link to it, it's dbref as well.
//
dbref aowner;
int aflags;
UTF8 *NameFormat = atr_pget(loc, A_NAMEFORMAT, &aowner, &aflags);
if (*NameFormat)
{
UTF8 *FormatOutput = alloc_lbuf("look_name.FO");
UTF8 *tPtr = FormatOutput;
reg_ref **preserve = nullptr;
preserve = PushRegisters(MAX_GLOBAL_REGS);
save_and_clear_global_regs(preserve);
if ((aflags & AF_NOEVAL) || NoEval(loc))
{
mux_strncpy(FormatOutput, NameFormat, LBUF_SIZE-1);
tPtr = FormatOutput + strlen((const char *)FormatOutput);
}
else
{
mux_exec(NameFormat, LBUF_SIZE-1, FormatOutput, &tPtr, loc, player, player,
AttrTrace(aflags, EV_FCHECK|EV_EVAL|EV_TOP),
0, 0);
}
*tPtr = '\0';
restore_global_regs(preserve);
PopRegisters(preserve, MAX_GLOBAL_REGS);
notify(player, FormatOutput);
free_lbuf(FormatOutput);
}
else
{
// Okay, no @NameFormat. Show the normal name.
//
UTF8 *buff = unparse_object(player, loc, true);
if (Html(player))
{
notify_html(player, T("<center><h3>"));
notify(player, buff);
notify_html(player, T("</h3></center>"));
}
else
{
notify(player, buff);
}
free_lbuf(buff);
}
free_lbuf(NameFormat);
if (!Good_obj(loc))
{
// If we went to NOTHING et al, then skip the rest.
//
return;
}
// Tell him the description.
//
int showkey = 0;
if (loc == Location(player))
{
showkey |= LK_IDESC;
}
if (key & LK_OBEYTERSE)
{
showkey |= LK_OBEYTERSE;
}
show_desc(player, loc, showkey);
bool is_terse = (key & LK_OBEYTERSE) ? Terse(player) : false;
// Tell him the appropriate messages if he has the key.
//
if (isRoom(loc))
{
int pattr, oattr, aattr;
if (could_doit(player, loc, A_LOCK))
{
pattr = A_SUCC;
oattr = A_OSUCC;
aattr = A_ASUCC;
}
else
{
pattr = A_FAIL;
oattr = A_OFAIL;
aattr = A_AFAIL;
}
if (is_terse)
{
pattr = 0;
}
did_it(player, loc, pattr, nullptr, oattr, nullptr, aattr, 0,
nullptr, 0);
}
// Tell him the attributes, contents and exits.
//
if ( (key & LK_SHOWATTR)
&& !mudconf.quiet_look
&& !is_terse)
{
look_atrs(player, loc, false);
}
if ( !is_terse
|| mudconf.terse_contents)
{
look_contents(player, loc, M_("Contents:"), CONTENTS_LOCAL);
}
if ( (key & LK_SHOWEXIT)
&& ( !is_terse
|| mudconf.terse_exits))
{
look_exits(player, loc, T("Obvious exits:"));
}
}
static void look_here
(
dbref executor,
dbref thing,
int key,
int look_key
)
{
if (Good_obj(thing))
{
if (key & LOOK_OUTSIDE)
{
if ( isRoom(thing)
|| Opaque(thing))
{
notify_quiet(executor, M_("You cant look outside."));
return;
}
thing = Location(thing);
}
look_in(executor, thing, look_key);
}
}
void do_look(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);
int look_key = LK_SHOWATTR | LK_SHOWEXIT;
if (!mudconf.terse_look)
{
look_key |= LK_OBEYTERSE;
}
dbref loc = Location(executor);
dbref thing;
if ( nullptr == name
|| '\0' == name[0])
{
look_here(executor, loc, key, look_key);
return;
}
// Look for the target locally.
//
thing = (key & LOOK_OUTSIDE) ? loc : executor;
init_match(thing, name, NOTYPE);
match_exit_with_parents();
match_neighbor();
match_possession();
if (Long_Fingers(executor))
{
match_absolute();
match_player();
}
match_here();
match_me();
match_master_exit();
thing = match_result();
// Not found locally, check possessive.
//
if (!Good_obj(thing))
{
thing = match_status(executor, match_possessed(executor,
((key & LOOK_OUTSIDE) ? loc : executor), name, thing, false));
}
// If the matching picked our current location, that's handled
// differently.
//
if (thing == loc)
{
look_here(executor, loc, key, look_key);
return;
}
// If we found something, go handle it.
//
if (Good_obj(thing))
{
#ifdef REALITY_LVLS
if (!IsReal(executor, thing))
return;
#endif // REALITY_LVLS
switch (Typeof(thing))
{
case TYPE_ROOM:
look_in(executor, thing, look_key);
break;
case TYPE_THING:
case TYPE_PLAYER:
look_simple(executor, thing, !mudconf.terse_look);
if ( !Opaque(thing)
&& ( mudconf.terse_contents
|| !Terse(executor)))
{
look_contents(executor, thing,M_("Carrying:"), CONTENTS_NESTED);
}
break;
case TYPE_EXIT:
look_simple(executor, thing, !mudconf.terse_look);
if ( Transparent(thing)
&& Location(thing) != NOTHING)
{
look_key &= ~LK_SHOWATTR;
look_in(executor, Location(thing), look_key);
}
break;
default:
look_simple(executor, thing, !mudconf.terse_look);
break;
}
}
}
static void debug_examine(dbref player, dbref thing)
{
dbref aowner;
UTF8 *buf;
int aflags, ca;
BOOLEXP *pBoolExp;
ATTR *pattr;
UTF8 *cp;
notify(player, tprintf(M_("Number = %d"), thing));
if (!Good_obj(thing))
{
return;
}
notify(player, tprintf(M_("Name = %s"), Name(thing)));
notify(player, tprintf(M_("Location= %d"), Location(thing)));
notify(player, tprintf(M_("Contents= %d"), Contents(thing)));
notify(player, tprintf(M_("Exits = %d"), Exits(thing)));
notify(player, tprintf(M_("Link = %d"), Link(thing)));
notify(player, tprintf(M_("Next = %d"), Next(thing)));
notify(player, tprintf(M_("Owner = %d"), Owner(thing)));
notify(player, tprintf(M_("Pennies = %d"), Pennies(thing)));
notify(player, tprintf(M_("Zone = %d"), Zone(thing)));
buf = flag_description(player, thing);
notify(player, tprintf(M_("Flags = %s"), buf));
free_mbuf(buf);
buf = powers_list(player, thing);
notify(player, tprintf(M_("Powers = %s"), buf));
free_lbuf(buf);
#ifdef REALITY_LVLS
buf = rxlevel_description(player, thing);
notify(player, tprintf(M_("RxLevel = %s"), buf));
free_lbuf(buf);
buf = txlevel_description(player, thing);
notify(player, tprintf(M_("TxLevel = %s"), buf));
free_lbuf(buf);
#endif // REALITY_LVLS
buf = atr_get("debug_examine.1530", thing, A_LOCK, &aowner, &aflags);
pBoolExp = parse_boolexp(player, buf, true);
free_lbuf(buf);
notify(player, tprintf(M_("Lock = %s"), unparse_boolexp(player, pBoolExp)));
free_boolexp(pBoolExp);
buf = alloc_lbuf("debug_dexamine");
cp = buf;
safe_str(M_("Attr list: "), buf, &cp);
unsigned char *as;
for (ca = atr_head(thing, &as); ca; ca = atr_next(&as))
{
pattr = atr_num(ca);
if (!pattr)
{
continue;
}
atr_get_info(thing, ca, &aowner, &aflags);
if (bCanReadAttr(player, thing, pattr, false))
{
if (pattr)
{
// Valid attr.
//
safe_str(pattr->name, buf, &cp);
safe_chr(' ', buf, &cp);
}
else
{
safe_tprintf_str(buf, &cp, T("%d "), ca);
}
}
}
*cp = '\0';
notify(player, buf);
free_lbuf(buf);
for (ca = atr_head(thing, &as); ca; ca = atr_next(&as))
{
pattr = atr_num(ca);
if (!pattr)
{
continue;
}
buf = atr_get("debug_examine.1576", thing, ca, &aowner, &aflags);
if (bCanReadAttr(player, thing, pattr, false))
{
view_atr(player, thing, pattr, buf, aowner, aflags, 0);
}
free_lbuf(buf);
}
}
static void exam_wildattrs
(
dbref player,
dbref thing,
bool do_parent
)
{
int atr;
bool got_any = false;
for (atr = olist_first(); atr != NOTHING; atr = olist_next())
{
ATTR *ap = atr_num(atr);
if (!ap)
{
continue;
}
int aflags;
dbref aowner;
UTF8 *buf;
if ( do_parent
&& !(ap->flags & AF_PRIVATE))
{
buf = atr_pget(thing, atr, &aowner, &aflags);
}
else
{
buf = atr_get("exam_wildattrs.1611", thing, atr, &aowner, &aflags);
}
// Decide if the player should see the attr: If obj is
// Examinable and has rights to see, yes. If a player and has
// rights to see, yes... except if faraway, attr=DESC, and
// remote DESC-reading is not turned on. If I own the attrib
// and have rights to see, yes... except if faraway, attr=DESC,
// and remote DESC-reading is not turned on.
//
if ( Examinable(player, thing)
&& bCanReadAttr(player, thing, ap, do_parent))
{
got_any = true;
view_atr(player, thing, ap, buf, aowner, aflags, 0);
}
else if (bCanReadAttr(player, thing, ap, isPlayer(thing) ? do_parent : false))
{
got_any = true;
if (aowner == Owner(player))
{
view_atr(player, thing, ap, buf, aowner, aflags, 0);
}
else if ( atr == A_DESC
&& ( mudconf.read_rem_desc
|| nearby(player, thing)))
{
show_desc(player, thing, 0);
}
else if (atr != A_DESC)
{
view_atr(player, thing, ap, buf, aowner, aflags, 0);
}
else
{
notify(player, M_("<Too far away to get a good look>"));
}
}
free_lbuf(buf);
}
if (!got_any)
{
notify_quiet(player, M_("No matching attributes found."));
}
}
void do_examine(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);
// This command is pointless if the player can't hear.
//
if (!Hearer(executor))
{
return;
}
dbref content, exit, aowner, loc;
UTF8 *temp, *buf, *buf2;
BOOLEXP *pBoolExp;
int aflags;
bool control;
bool do_parent = ((key & EXAM_PARENT) ? true : false);
dbref thing = NOTHING;
if ( !name
|| !*name)
{
thing = Location(executor);
if (thing == NOTHING)
{
return;
}
}
else
{
// Check for obj/attr first.
//
olist_push();
if (parse_attrib_wild(executor, name, &thing, do_parent, true, false))
{
if (NoExamine(thing) && !WizRoy(executor))
{
notify_quiet(executor, NOPERM_MESSAGE);
olist_pop();
return;
}
exam_wildattrs(executor, thing, do_parent);
olist_pop();
return;
}
olist_pop();
// Look it up.
//
init_match(executor, name, NOTYPE);
match_everything(MAT_EXIT_PARENTS);
thing = noisy_match_result();
if (!Good_obj(thing))
{
return;
}
}
if (NoExamine(thing) && !WizRoy(executor))
{
notify_quiet(executor, NOPERM_MESSAGE);
return;
}
#if defined(WOD_REALMS) || defined(REALITY_LVLS)
if (REALM_DO_HIDDEN_FROM_YOU == DoThingToThingVisibility(executor, thing, ACTION_IS_STATIONARY))
{
notify(executor, NOMATCH_MESSAGE);
return;
}
#endif
// Check for the /debug switch.
//
if (key & EXAM_DEBUG)
{
if (!Examinable(executor, thing))
{
notify_quiet(executor, NOPERM_MESSAGE);
}
else
{
debug_examine(executor, thing);
}
return;
}
control = ( Examinable(executor, thing)
|| Link_exit(executor, thing));
if (control)
{
buf2 = unparse_object(executor, thing, false);
notify(executor, buf2);
free_lbuf(buf2);
if (mudconf.ex_flags)
{
buf2 = flag_description(executor, thing);
notify(executor, buf2);
free_mbuf(buf2);
}
}
else
{
if ( key == EXAM_DEFAULT
&& !mudconf.exam_public)
{
if (mudconf.read_rem_name)
{
buf2 = alloc_lbuf("do_examine.pub_name");
mux_strncpy(buf2, Moniker(thing), LBUF_SIZE-1);
notify(executor,
tprintf(M_("%s is owned by %s"),
buf2, Moniker(Owner(thing))));
free_lbuf(buf2);
}
else
{
notify(executor, tprintf(M_("Owned by %s"), Moniker(Owner(thing))));
}
return;
}
}
temp = alloc_lbuf("do_examine.info");
if ( control
|| mudconf.read_rem_desc
|| nearby(executor, thing))
{
temp = atr_get_str(temp, thing, A_DESC, &aowner, &aflags);
if (*temp)
{
if ( Examinable(executor, thing)
|| (aowner == Owner(executor)))
{
view_atr(executor, thing, atr_num(A_DESC), temp,
aowner, aflags, true);
}
else
{
show_desc(executor, thing, 0);
}
}
}
else
{
notify(executor, M_("<Too far away to get a good look>"));
}
if (control)
{
// Print owner, key, and value.
//
// Build a capitalized copy of many_coins (Unicode-aware)
// instead of modifying the global in-place.
//
size_t nCoinsUpper;
UTF8 *pCoinsUpper = mux_strupr(mudconf.many_coins, nCoinsUpper);
UTF8 capCoins[SBUF_SIZE];
size_t nCoins = mux_strlen(mudconf.many_coins);
if (nCoins >= SBUF_SIZE) nCoins = SBUF_SIZE - 1;
memcpy(capCoins, mudconf.many_coins, nCoins);
capCoins[nCoins] = '\0';
UTF8 *pCapEnd = capCoins + nCoins;
mux_toupper_first(capCoins, &pCapEnd, SBUF_SIZE);
*pCapEnd = '\0';
buf2 = atr_get("do_examine.1803", thing, A_LOCK, &aowner, &aflags);
pBoolExp = parse_boolexp(executor, buf2, true);
buf = unparse_boolexp(executor, pBoolExp);
free_boolexp(pBoolExp);
mux_strncpy(buf2, Moniker(Owner(thing)), LBUF_SIZE-1);
notify(executor, tprintf(M_("Owner: %s Key: %s %s: %d"), buf2, buf, capCoins, Pennies(thing)));
free_lbuf(buf2);
// Print the zone
//
if (mudconf.have_zones)
{
buf2 = unparse_object(executor, Zone(thing), false);
notify(executor, tprintf(M_("Zone: %s"), buf2));
free_lbuf(buf2);
}
// Print parent
//
loc = Parent(thing);
if (loc != NOTHING)
{
buf2 = unparse_object(executor, loc, false);
notify(executor, tprintf(M_("Parent: %s"), buf2));
free_lbuf(buf2);
}
buf2 = powers_list(executor, thing);
notify(executor, tprintf(M_("Powers: %s"), buf2));
free_lbuf(buf2);
#ifdef REALITY_LVLS
// Show Rx and Tx levels.
//
buf2 = rxlevel_description(executor, thing);
notify(executor, tprintf(M_("RxLevel: %s"), buf2));
free_mbuf(buf2);
buf2 = txlevel_description(executor, thing);
notify(executor, tprintf(M_("TxLevel: %s"), buf2));
free_mbuf(buf2);
#endif // REALITY_LVLS
}
if (!(key & EXAM_BRIEF))
{
look_atrs(executor, thing, do_parent);
}
// Show him interesting stuff
//
if (control)
{
// Contents
//
if (Contents(thing) != NOTHING)
{
notify(executor, M_("Contents:"));
DOLIST(content, Contents(thing))
{
buf2 = unparse_object(executor, content, false);
notify(executor, buf2);
free_lbuf(buf2);
}
}
// Show stuff that depends on the object type.
//
switch (Typeof(thing))
{
case TYPE_ROOM:
// Tell him about exits
//
if (Exits(thing) != NOTHING)
{
notify(executor, M_("Exits:"));
DOLIST(exit, Exits(thing))
{
buf2 = unparse_object(executor, exit, false);
notify(executor, buf2);
free_lbuf(buf2);
}
}
else
{
notify(executor, M_("No exits."));
}
// print dropto if present
//
if (Dropto(thing) != NOTHING)
{
buf2 = unparse_object(executor, Dropto(thing), false);
notify(executor, tprintf(M_("Dropped objects go to: %s"), buf2));
free_lbuf(buf2);
}
break;
case TYPE_THING:
case TYPE_PLAYER:
// Tell him about exits
//
if (Exits(thing) != NOTHING)
{
notify(executor, M_("Exits:"));
DOLIST(exit, Exits(thing))
{
buf2 = unparse_object(executor, exit, false);
notify(executor, buf2);
free_lbuf(buf2);
}
}
else
{
notify(executor, M_("No exits."));
}
// Print home
//
loc = Home(thing);
buf2 = unparse_object(executor, loc, false);
notify(executor, tprintf(M_("Home: %s"), buf2));
free_lbuf(buf2);
// print location if player can link to it
//
loc = Location(thing);
if ( Location(thing) != NOTHING
&& ( Examinable(executor, loc)
|| Examinable(executor, thing)
|| Linkable(executor, loc)))
{
buf2 = unparse_object(executor, loc, false);
notify(executor, tprintf(M_("Location: %s"), buf2));
free_lbuf(buf2);
}
break;
case TYPE_EXIT:
buf2 = unparse_object(executor, Exits(thing), false);
notify(executor, tprintf(M_("Source: %s"), buf2));
free_lbuf(buf2);
// print destination.
//
switch (Location(thing))
{
case NOTHING:
// Special case. unparse_object() normally returns -1 as '*NOTHING*'.
//
notify(executor, M_("Destination: *UNLINKED*"));
break;
default:
buf2 = unparse_object(executor, Location(thing), false);
notify(executor, tprintf(M_("Destination: %s"), buf2));
free_lbuf(buf2);
break;
}
break;
default:
break;
}
}
else if ( !Opaque(thing)
&& nearby(executor, thing))
{
if (Has_contents(thing))
{
look_contents(executor, thing, M_("Contents:"), CONTENTS_REMOTE);
}
if (!isExit(thing))
{
look_exits(executor, thing, T("Obvious exits:"));
}
}
free_lbuf(temp);
if (!control)
{
if (mudconf.read_rem_name)
{
buf2 = alloc_lbuf("do_examine.pub_name");
mux_strncpy(buf2, Moniker(thing), LBUF_SIZE-1);
notify(executor, tprintf(M_("%s is owned by %s"), buf2, Moniker(Owner(thing))));
free_lbuf(buf2);
}
else
{
notify(executor, tprintf(M_("Owned by %s"), Moniker(Owner(thing))));
}
}
}
void do_score(dbref executor, dbref caller, dbref enactor, int eval, int key)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(key);
int nPennies = Pennies(executor);
notify(executor, tprintf(M_("You have %d %s."), nPennies,
(1 == nPennies) ? mudconf.one_coin : mudconf.many_coins));
}
void do_inventory(dbref executor, dbref caller, dbref enactor, int eval, int key)
{
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(key);
dbref thing;
UTF8 *buff, *e;
const UTF8 *s;
thing = Contents(executor);
if (thing == NOTHING)
{
notify(executor, M_("You arent carrying anything."));
}
else
{
notify(executor, M_("You are carrying:"));
DOLIST(thing, thing)
{
buff = unparse_object(executor, thing, true);
notify(executor, buff);
free_lbuf(buff);
}
}
thing = Exits(executor);
if (thing != NOTHING)
{
notify(executor, M_("Exits:"));
e = buff = alloc_lbuf("look_exits");
DOLIST(thing, thing)
{
// Chop off first exit alias to display.
//
for (s = Moniker(thing); *s && (*s != ';'); s++)
{
safe_chr(*s, buff, &e);
}
safe_str(T(" "), buff, &e);
}
*e = 0;
notify(executor, buff);
free_lbuf(buff);
}
do_score(executor, caller, executor, 0, 0);
}
void do_entrances(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(key);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
dbref thing, i, j;
UTF8 *exit, *message;
int control_thing, count, low_bound, high_bound;
FWDLIST *fp;
parse_range(&name, &low_bound, &high_bound);
if ( !name
|| !*name)
{
if (Has_location(executor))
{
thing = Location(executor);
}
else
{
thing = executor;
}
if (!Good_obj(thing))
{
return;
}
}
else
{
init_match(executor, name, NOTYPE);
match_everything(MAT_EXIT_PARENTS);
thing = noisy_match_result();
if (!Good_obj(thing))
{
return;
}
}
if (!payfor(executor, mudconf.searchcost))
{
notify(executor, tprintf(M_("You dont have enough %s."),
mudconf.many_coins));
return;
}
message = alloc_lbuf("do_entrances");
control_thing = Examinable(executor, thing);
count = 0;
for (i = low_bound; i <= high_bound; i++)
{
if (control_thing || Examinable(executor, i))
{
switch (Typeof(i))
{
case TYPE_EXIT:
if (Location(i) == thing)
{
exit = unparse_object(executor, Exits(i), false);
notify(executor, tprintf(M_("%s (%s)"), exit, Moniker(i)));
free_lbuf(exit);
count++;
}
break;
case TYPE_ROOM:
if (Dropto(i) == thing)
{
exit = unparse_object(executor, i, false);
notify(executor, tprintf(M_("%s [dropto]"), exit));
free_lbuf(exit);
count++;
}
break;
case TYPE_THING:
case TYPE_PLAYER:
if (Home(i) == thing)
{
exit = unparse_object(executor, i, false);
notify(executor, tprintf(M_("%s [home]"), exit));
free_lbuf(exit);
count++;
}
break;
}
// Check for parents.
//
if (Parent(i) == thing)
{
exit = unparse_object(executor, i, false);
notify(executor, tprintf(M_("%s [parent]"), exit));
free_lbuf(exit);
count++;
}
// Check for forwarding.
//
if (H_Fwdlist(i))
{
fp = fwdlist_get(i);
if (!fp)
{
continue;
}
for (j = 0; j < fp->count; j++)
{
if (fp->data[j] != thing)
{
continue;
}
exit = unparse_object(executor, i, false);
notify(executor, tprintf(M_("%s [forward]"), exit));
free_lbuf(exit);
count++;
}
}
}
}
free_lbuf(message);
notify(executor, tprintf(MN_("%d entrance found.",
"%d entrances found.", count), count));
}
// Check the current location for bugs.
//
static void sweep_check(dbref player, dbref what, int key, bool is_loc)
{
if ( Can_Hide(what)
&& Hidden(what)
&& !See_Hidden(player))
{
return;
}
bool canhear = false;
bool cancom = false;
bool isplayer = false;
bool ispuppet = false;
bool isconnected = false;
bool is_parent = false;
if (key & SWEEP_LISTEN)
{
if ( ( ( isExit(what)
|| is_loc)
&& Audible(what))
|| H_Listen(what)
|| mudstate.bfListens.IsSet(what))
{
canhear = true;
}
else if ( !mudstate.bfNoListens.IsSet(what)
&& Monitor(what))
{
bool bFoundCommands = false;
unsigned char *as;
UTF8 *buff = alloc_lbuf("sweep_check.Hearer");
for (int atr = atr_head(what, &as); atr; atr = atr_next(&as))
{
ATTR *ap = atr_num(atr);
if ( !ap
|| (ap->flags & AF_NOPROG))
{
continue;
}
int aflags;
dbref aowner;
atr_get_str(buff, what, atr, &aowner, &aflags);
if (aflags & AF_NOPROG)
{
continue;
}
UTF8 *s = nullptr;
if ( AMATCH_CMD == buff[0]
|| AMATCH_LISTEN == buff[0])
{
s = find_pattern_delimiter(buff + 1);
if (s)
{
if (AMATCH_CMD == buff[0])
{
bFoundCommands = true;
}
else
{
canhear = true;
break;
}
}
}
}
free_lbuf(buff);
if (canhear)
{
mudstate.bfListens.Set(what);
}
else
{
mudstate.bfNoListens.Set(what);
}
if (bFoundCommands)
{
mudstate.bfNoCommands.Clear(what);
mudstate.bfCommands.Set(what);
}
else
{
mudstate.bfCommands.Clear(what);
mudstate.bfNoCommands.Set(what);
}
}
}
if ( (key & SWEEP_COMMANDS)
&& !isExit(what))
{
// Look for commands on the object and parents too.
//
dbref parent;
int lev;
ITER_PARENTS(what, parent, lev)
{
if (Commer(parent))
{
cancom = true;
if (lev)
{
is_parent = true;
break;
}
}
}
}
if (key & SWEEP_CONNECT)
{
if ( Connected(what)
|| ( Puppet(what)
&& Connected(Owner(what)))
|| ( mudconf.player_listen
&& isPlayer(what)
&& canhear
&& Connected(Owner(what))))
{
isconnected = true;
}
}
if ( (key & SWEEP_PLAYER)
|| isconnected)
{
if (isPlayer(what))
{
isplayer = true;
}
if (Puppet(what))
{
ispuppet = true;
}
}
if ( canhear
|| cancom
|| isplayer
|| ispuppet
|| isconnected)
{
UTF8 *buf = alloc_lbuf("sweep_check.types");
UTF8 *bp = buf;
if (cancom)
{
safe_str(T("commands "), buf, &bp);
}
if (canhear)
{
safe_str(T("messages "), buf, &bp);
}
if (isplayer)
{
safe_str(T("player "), buf, &bp);
}
if (ispuppet)
{
safe_str(T("puppet("), buf, &bp);
safe_str(Moniker(Owner(what)), buf, &bp);
safe_str(T(") "), buf, &bp);
}
if (isconnected)
{
safe_str(T("connected "), buf, &bp);
}
if (is_parent)
{
safe_str(T("parent "), buf, &bp);
}
bp[-1] = '\0';
if (!isExit(what))
{
notify(player, tprintf(M_(" %s is listening. [%s]"),
Moniker(what), buf));
}
else
{
UTF8 *buf2 = alloc_lbuf("sweep_check.name");
mux_strncpy(buf2, Moniker(what), LBUF_SIZE-1);
for (bp = buf2; *bp && (*bp != ';'); bp++)
{
; // Nothing.
}
*bp = '\0';
notify(player, tprintf(M_(" %s is listening. [%s]"), buf2, buf));
free_lbuf(buf2);
}
free_lbuf(buf);
}
}
void do_sweep(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *where, const UTF8 *cargs[], int ncargs)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
dbref here, sweeploc;
int where_key, what_key;
where_key = key & (SWEEP_ME | SWEEP_HERE | SWEEP_EXITS);
what_key = key & (SWEEP_COMMANDS | SWEEP_LISTEN | SWEEP_PLAYER | SWEEP_CONNECT);
if (where && *where)
{
sweeploc = match_controlled(executor, where);
if (!Good_obj(sweeploc))
{
return;
}
}
else
{
sweeploc = executor;
}
if (!where_key)
{
where_key = -1;
}
if (!what_key)
{
what_key = -1;
}
else if (what_key == SWEEP_VERBOSE)
{
what_key = SWEEP_VERBOSE | SWEEP_COMMANDS;
}
// Check my location. If I have none or it is dark, check just me.
//
if (where_key & SWEEP_HERE)
{
notify(executor, M_("Sweeping location..."));
if (Has_location(sweeploc))
{
here = Location(sweeploc);
if ( here == NOTHING
|| ( Dark(here)
&& !mudconf.sweep_dark
&& !Examinable(executor, here)))
{
notify_quiet(executor,
T("Sorry, it is dark here and you cant search for bugs"));
sweep_check(executor, sweeploc, what_key, false);
}
else
{
sweep_check(executor, here, what_key, true);
for (here = Contents(here); here != NOTHING; here = Next(here))
{
sweep_check(executor, here, what_key, false);
}
}
}
else
{
sweep_check(executor, sweeploc, what_key, false);
}
}
// Check exits in my location
//
if ( (where_key & SWEEP_EXITS)
&& Has_location(sweeploc))
{
notify(executor, M_("Sweeping exits..."));
for (here = Exits(Location(sweeploc)); here != NOTHING; here = Next(here))
{
sweep_check(executor, here, what_key, false);
}
}
// Check my inventory
//
if ( (where_key & SWEEP_ME)
&& Has_contents(sweeploc))
{
notify(executor, M_("Sweeping inventory..."));
for (here = Contents(sweeploc); here != NOTHING; here = Next(here))
{
sweep_check(executor, here, what_key, false);
}
}
// Check carried exits
//
if ( (where_key & SWEEP_EXITS)
&& Has_exits(sweeploc))
{
notify(executor, M_("Sweeping carried exits..."));
for (here = Exits(sweeploc); here != NOTHING; here = Next(here))
{
sweep_check(executor, here, what_key, false);
}
}
notify(executor, M_("Sweep complete."));
}
// scan_check: For a single object, find all $-command attrs whose pattern
// matches the given command string and report them to the executor.
//
static void scan_check
(
dbref executor,
dbref thing,
UTF8 *lcmd,
UTF8 *cmd
)
{
if ( !Good_obj(thing)
|| isExit(thing)
|| No_Command(thing))
{
return;
}
if ( !Controls(executor, thing)
&& !Examinable(executor, thing))
{
return;
}
UTF8 *attrlist = alloc_lbuf("scan_check.list");
UTF8 *ap2 = attrlist;
int count = 0;
atr_push();
unsigned char *as;
LBuf buff = LBuf_Src("find_wild_attrs");
for (int atr = atr_head(thing, &as); atr; atr = atr_next(&as))
{
ATTR *ap = atr_num(atr);
if ( !ap
|| (ap->flags & AF_NOPROG))
{
continue;
}
dbref aowner;
int aflags;
atr_get_str(buff, thing, atr, &aowner, &aflags);
if (aflags & AF_NOPROG)
{
continue;
}
if (AMATCH_CMD != buff[0])
{
continue;
}
UTF8 *s = find_pattern_delimiter(buff + 1);
if (!s)
{
continue;
}
*s = '\0';
unescape_pattern_colons(buff + 1);
bool matched = false;
if (aflags & AF_REGEXP)
{
matched = regexp_match(buff + 1, cmd,
(aflags & AF_CASE) ? PCRE2_CASELESS : 0,
nullptr, 0);
}
else
{
matched = quick_wild(buff + 1, lcmd);
}
if (matched)
{
if (count > 0)
{
safe_chr(' ', attrlist, &ap2);
}
safe_str(ap->name, attrlist, &ap2);
count++;
}
}
atr_pop();
if (count > 0)
{
*ap2 = '\0';
notify(executor, tprintf(M_(" %s [%d: %s]"),
unparse_object(executor, thing, false), count, attrlist));
}
free_lbuf(attrlist);
}
void do_scan(dbref executor, dbref caller, dbref enactor, int eval, int key, UTF8 *command, const UTF8 *cargs[], int ncargs)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
if (!command || !*command)
{
notify(executor, M_("What command do you want to scan for?"));
return;
}
// Lowercase the command for case-insensitive wildcard matching.
//
size_t nLower;
UTF8 *pLower = mux_strlwr(command, nLower);
LBuf lcmd = LBuf_Src("find_wild_attrs_cmd");
if (nLower >= LBUF_SIZE)
{
nLower = LBUF_SIZE - 1;
}
memcpy(lcmd, pLower, nLower);
lcmd[nLower] = '\0';
// If no switches given, scan everything.
//
if (0 == key)
{
key = SCAN_SELF | SCAN_ROOM | SCAN_ZONE | SCAN_GLOBALS;
}
dbref obj;
// Check executor and inventory.
//
if (key & SCAN_SELF)
{
notify(executor, M_("Check yourself and your inventory:"));
scan_check(executor, executor, lcmd, command);
if (Has_contents(executor))
{
for (obj = Contents(executor); obj != NOTHING; obj = Next(obj))
{
scan_check(executor, obj, lcmd, command);
}
}
}
// Check location and its contents.
//
if (key & SCAN_ROOM)
{
notify(executor, M_("Check your location and its contents:"));
if (Has_location(executor))
{
dbref loc = Location(executor);
if (Good_obj(loc))
{
scan_check(executor, loc, lcmd, command);
for (obj = Contents(loc); obj != NOTHING; obj = Next(obj))
{
if (obj != executor)
{
scan_check(executor, obj, lcmd, command);
}
}
}
}
}
// Check zones.
//
if ( (key & SCAN_ZONE)
&& mudconf.have_zones)
{
notify(executor, M_("Checking zone:"));
if (Has_location(executor))
{
dbref loc = Location(executor);
if (Good_obj(loc))
{
dbref zone_loc = Zone(loc);
if (Good_obj(zone_loc))
{
if (isRoom(zone_loc))
{
for (obj = Contents(zone_loc); obj != NOTHING; obj = Next(obj))
{
scan_check(executor, obj, lcmd, command);
}
}
else
{
scan_check(executor, zone_loc, lcmd, command);
}
}
}
}
// Personal zone.
//
dbref zone = Zone(executor);
if (Good_obj(zone))
{
dbref loc = Location(executor);
dbref zone_loc = NOTHING;
if (Good_obj(loc))
{
zone_loc = Zone(loc);
}
if (zone != zone_loc)
{
scan_check(executor, zone, lcmd, command);
}
}
}
// Check master room.
//
if (key & SCAN_GLOBALS)
{
notify(executor, M_("Checking master room:"));
if ( Good_obj(mudconf.master_room)
&& Has_contents(mudconf.master_room))
{
for (obj = Contents(mudconf.master_room); obj != NOTHING; obj = Next(obj))
{
scan_check(executor, obj, lcmd, command);
}
if (!No_Command(mudconf.master_room))
{
scan_check(executor, mudconf.master_room, lcmd, command);
}
}
}
notify(executor, M_("Scan complete."));
}
/* Output the sequence of commands needed to duplicate the specified
* object. If you're moving things to another system, your mileage
* will almost certainly vary. (i.e. different flags, etc.)
*/
void do_decomp
(
dbref executor,
dbref caller,
dbref enactor,
int eval,
int key,
int nargs,
UTF8 *name,
UTF8 *qual,
const UTF8 *cargs[],
int ncargs
)
{
UNUSED_PARAMETER(caller);
UNUSED_PARAMETER(enactor);
UNUSED_PARAMETER(eval);
UNUSED_PARAMETER(nargs);
UNUSED_PARAMETER(cargs);
UNUSED_PARAMETER(ncargs);
// Check for obj/attr first.
//
bool fWildDecomp;
olist_push();
dbref thing;
if (parse_attrib_wild(executor, name, &thing, false, true, false))
{
fWildDecomp = true;
}
else
{
fWildDecomp = false;
init_match(executor, name, TYPE_THING);
match_everything(MAT_EXIT_PARENTS);
thing = noisy_match_result();
}
// get result
//
if (NOTHING == thing)
{
olist_pop();
return;
}
if (NoExamine(thing) && !WizRoy(executor))
{
notify_quiet(executor, NOPERM_MESSAGE);
olist_pop();
return;
}
if (!Examinable(executor, thing))
{
notify_quiet(executor,
T("You can only decompile things you can examine."));
olist_pop();
return;
}
dbref aowner;
int aflags;
UTF8 *pDefaultLock = atr_get("do_decomp.2573", thing, A_LOCK, &aowner, &aflags);
BOOLEXP *pBoolExp = parse_boolexp(executor, pDefaultLock, true);
free_lbuf(pDefaultLock);
pDefaultLock = nullptr;
UTF8 *got = nullptr;
const UTF8 *pName = nullptr;
UTF8 *pShortName = nullptr;
if ( nullptr != qual
&& '\0' != qual[0])
{
pName = qual;
}
else
{
if (key == DECOMP_DBREF)
{
pName = tprintf(T("#%d"), thing);
}
else
{
if ( TYPE_PLAYER == Typeof(thing)
&& executor == thing)
{
pName = T("me");
}
else
{
pName = Moniker(thing);
if (TYPE_EXIT == Typeof(thing))
{
pShortName = alloc_lbuf("do_decomp.2606");
mux_strncpy(pShortName, pName, LBUF_SIZE-1);
for (got = pShortName; *got; got++)
{
if (EXIT_DELIMITER == *got)
{
*got = '\0';
break;
}
}
}
}
}
}
UTF8 *pShortAndStrippedName = alloc_lbuf("do_decomp.2620");
UTF8 *pTranslatedFullName = alloc_lbuf("do_decomp.2621");
// Strip and translate color in one place.
//
size_t len;
UTF8 *p = strip_color((nullptr != pShortName) ? pShortName : pName, &len);
memcpy(pShortAndStrippedName, p, len+1);
mux_strncpy(pTranslatedFullName, translate_string(pName, true), LBUF_SIZE-1);
// Determine the name of the thing to use in reporting and then
// report the command to make the thing.
//
if (DECOMP_DBREF != key)
{
switch (Typeof(thing))
{
case TYPE_THING:
{
int val = OBJECT_DEPOSIT(Pennies(thing));
notify(executor, tprintf(T("@create %s=%d"), pTranslatedFullName, val));
}
break;
case TYPE_ROOM:
notify(executor, tprintf(T("@dig/teleport %s"), pTranslatedFullName));
break;
case TYPE_EXIT:
notify(executor, tprintf(T("@open %s"), pTranslatedFullName));
if (Good_obj(Location(thing)))
{
notify(executor, tprintf(T("@@ @link %s=#%d"), pShortAndStrippedName, Location(thing)));
}
else if (HOME == Location(thing))
{
notify(executor, tprintf(T("@@ @link %s=home"), pShortAndStrippedName));
}
break;
}
}
// Report the lock (if any).
//
if ( !fWildDecomp
&& TRUE_BOOLEXP != pBoolExp)
{
notify(executor, tprintf(T("@lock %s=%s"), pShortAndStrippedName,
translate_string(unparse_boolexp_decompile(executor, pBoolExp), true)));
}
free_boolexp(pBoolExp);
pBoolExp = nullptr;
// Report attributes. Collect and sort by name for alphabetical output.
//
int ca;
unsigned char *as;
std::vector<int> decomp_atrs;
if (fWildDecomp)
{
for (ca = olist_first(); NOTHING != ca; ca = olist_next())
{
decomp_atrs.push_back(ca);
}
}
else
{
for (ca = atr_head(thing, &as); 0 != ca; ca = atr_next(&as))
{
decomp_atrs.push_back(ca);
}
}
std::sort(decomp_atrs.begin(), decomp_atrs.end(), [](int a, int b)
{
ATTR *pa = atr_num(a);
ATTR *pb = atr_num(b);
if (!pa) return false;
if (!pb) return true;
return mux_stricmp(pa->name, pb->name) < 0;
});
UTF8 *buff = alloc_mbuf("do_decomp.attr_name");
for (int ca : decomp_atrs)
{
ATTR *pattr = atr_num(ca);
if (!pattr)
{
continue;
}
if (pattr->flags & AF_NODECOMP)
{
continue;
}
got = atr_get("do_decomp.2653", thing, ca, &aowner, &aflags);
if (bCanReadAttr(executor, thing, pattr, false))
{
if (pattr->flags & AF_IS_LOCK)
{
pBoolExp = parse_boolexp(executor, got, true);
UTF8 *ltext = unparse_boolexp_decompile(executor, pBoolExp);
free_boolexp(pBoolExp);
notify(executor, tprintf(M_("@lock/%s %s=%s"), pattr->name,
pShortAndStrippedName, ltext));
}
else
{
mux_strncpy(buff, pattr->name, MBUF_SIZE-1);
if (IsDecompFriendly(got))
{
notify(executor, tprintf(T("%c%s %s=%s"), ((ca < A_USER_START) ?
'@' : '&'), buff, pShortAndStrippedName, got));
}
else if (A_MONIKER == pattr->number)
{
notify(executor, tprintf(T("%c%s %s=%s"), ((ca < A_USER_START) ?
'@' : '&'), buff, pShortAndStrippedName, translate_string(got, true)));
}
else
{
notify(executor, tprintf(M_("@wait 0={%c%s %s=%s}"), ((ca < A_USER_START) ?
'@' : '&'), buff, pShortAndStrippedName, translate_string(got, true)));
}
for (NAMETAB *np = indiv_attraccess_nametab; np->name; np++)
{
if ( (aflags & np->flag)
&& check_access(executor, np->perm)
&& (!(np->perm & CA_NO_DECOMP)))
{
notify(executor, tprintf(T("@set %s/%s = %s"), pShortAndStrippedName,
buff, np->name));
}
}
if (aflags & AF_LOCK)
{
notify(executor, tprintf(T("@lock %s/%s"), pShortAndStrippedName, buff));
}
}
}
free_lbuf(got);
}
free_mbuf(buff);
buff = nullptr;
if (!fWildDecomp)
{
decompile_flags(executor, thing, pShortAndStrippedName);
decompile_powers(executor, thing, pShortAndStrippedName);
#ifdef REALITY_LVLS
decompile_rlevels(executor, thing, pShortAndStrippedName);
#endif // REALITY_LVLS
}
// If the object has a parent, report it.
//
if ( !fWildDecomp
&& (Parent(thing) != NOTHING))
{
notify(executor, tprintf(T("@parent %s=#%d"), pShortAndStrippedName, Parent(thing)));
}
// If the object has a zone, report it.
//
int zone;
if ( !fWildDecomp
&& Good_obj(zone = Zone(thing)))
{
notify(executor, tprintf(T("@chzone %s=#%d"), pShortAndStrippedName, zone));
}
free_lbuf(pShortAndStrippedName);
pShortAndStrippedName = nullptr;
free_lbuf(pTranslatedFullName);
pTranslatedFullName = nullptr;
if (nullptr != pShortName)
{
free_lbuf(pShortName);
pShortName = nullptr;
}
olist_pop();
}
// show_vrml_url
//
void show_vrml_url(dbref thing, dbref loc)
{
UTF8 *vrml_url;
dbref aowner;
int aflags;
// If they don't care about HTML, just return.
//
if (!Html(thing))
{
return;
}
vrml_url = atr_pget(loc, A_VRML_URL, &aowner, &aflags);
if (*vrml_url)
{
UTF8 *vrml_message, *vrml_cp;
vrml_message = vrml_cp = alloc_lbuf("show_vrml_url");
safe_str(T("<img xch_graph=load href=\""), vrml_message, &vrml_cp);
safe_str(vrml_url, vrml_message, &vrml_cp);
safe_str(T("\">"), vrml_message, &vrml_cp);
*vrml_cp = 0;
notify_html(thing, vrml_message);
free_lbuf(vrml_message);
}
else
{
notify_html(thing, T("<img xch_graph=hide>"));
}
free_lbuf(vrml_url);
}