Fix emoting system

This commit is contained in:
kinther 2025-08-20 09:24:27 -07:00
parent 603b82fc0f
commit 41742a168a
3 changed files with 272 additions and 347 deletions

View file

@ -569,11 +569,6 @@ ACMD(do_drop)
}
}
if (amount && (subcmd == SCMD_JUNK)) {
send_to_char(ch, "You have been rewarded by the gods!\r\n");
act("$n has been rewarded by the gods!", TRUE, ch, 0, 0, TO_ROOM);
GET_GOLD(ch) += amount;
}
}
static void perform_give(struct char_data *ch, struct char_data *vict,

View file

@ -89,277 +89,359 @@ static int purge_room(room_rnum room)
return 1;
}
/* =======================
* Emote helpers & engine
* ======================= */
/* ===================== Emote engine ===================== */
/* Operators:
*
* ~ (name) / target sees "you"
* ! him/her/them / target sees "you"
* % (name)'s / target sees "your"
* ^ his/her/their / target sees "your"
* # he/she/they / target sees "you"
* & himself/herself/themself / target sees "yourself"
* = (name)'s / target sees "yours"
* + his/hers/theirs / target sees "yours"
* @ moves actor name (or actor's possessive for pemote) to that position
*/
#ifndef MAX_EMOTE_TOKENS
#define MAX_EMOTE_TOKENS 16
#define MAX_EMOTE_TOKENS 16
#endif
/* ---- Pronouns for characters ---- */
/* --- Pronoun & string helpers --- */
static const char *pron_obj(struct char_data *tch) { /* him/her/them */
switch (GET_SEX(tch)) {
case SEX_MALE: return "him";
case SEX_FEMALE: return "her";
default: return "them";
}
switch (GET_SEX(tch)) { case SEX_MALE: return "him"; case SEX_FEMALE: return "her"; default: return "them"; }
}
static const char *pron_pos_adj(struct char_data *tch) { /* his/her/their */
switch (GET_SEX(tch)) {
case SEX_MALE: return "his";
case SEX_FEMALE: return "her";
default: return "their";
}
switch (GET_SEX(tch)) { case SEX_MALE: return "his"; case SEX_FEMALE: return "her"; default: return "their"; }
}
static const char *pron_pos_pron(struct char_data *tch) { /* his/hers/theirs */
switch (GET_SEX(tch)) {
case SEX_MALE: return "his";
case SEX_FEMALE: return "hers";
default: return "theirs";
}
switch (GET_SEX(tch)) { case SEX_MALE: return "his"; case SEX_FEMALE: return "hers"; default: return "theirs"; }
}
static const char *pron_subj(struct char_data *tch) { /* he/she/they */
switch (GET_SEX(tch)) {
case SEX_MALE: return "he";
case SEX_FEMALE: return "she";
default: return "they";
}
switch (GET_SEX(tch)) { case SEX_MALE: return "he"; case SEX_FEMALE: return "she"; default: return "they"; }
}
static const char *pron_refl(struct char_data *tch) { /* himself/herself/themself */
switch (GET_SEX(tch)) {
case SEX_MALE: return "himself";
case SEX_FEMALE: return "herself";
default: return "themself";
}
switch (GET_SEX(tch)) { case SEX_MALE: return "himself"; case SEX_FEMALE: return "herself"; default: return "themself"; }
}
/* ---- Possessive form of a name: James → James', Patrick → Patrick's ---- */
static void make_possessive(const char *name, char *out, size_t outsz) {
size_t n = strlcpy(out, name, outsz);
if (n > 0 && out[n-1] == 's') strlcat(out, "'", outsz);
else strlcat(out, "'s", outsz);
}
/* ---- Collapse repeated spaces (prevents “Name does…”) ---- */
static bool is_end_punct(char c) { return (c == '.' || c == '!' || c == '?'); }
static void collapse_spaces(char *s) {
char *src = s, *dst = s;
bool last_space = false;
char *src = s, *dst = s; bool last_space = false;
while (*src) {
if (isspace((unsigned char)*src)) {
if (!last_space) *dst++ = ' ';
last_space = true;
} else {
*dst++ = *src;
last_space = false;
}
if (isspace((unsigned char)*src)) { if (!last_space) *dst++ = ' '; last_space = true; }
else { *dst++ = *src; last_space = false; }
src++;
}
*dst = '\0';
}
/* ---- Is punctuation we consider as sentence end? ---- */
static bool is_end_punct(char c) {
return (c == '.' || c == '!' || c == '?');
static void build_actor_name(struct char_data *actor, bool possessive, char *out, size_t outsz) {
if (!possessive) strlcpy(out, GET_NAME(actor), outsz);
else make_possessive(GET_NAME(actor), out, outsz);
}
/* ---- Resolve a reference token (character or object) from room/inv/eq ---- */
/* Replace all occurrences of 'needle' in 'hay' with 'repl'. */
static void replace_all_tokens(char *hay, size_t haysz, const char *needle, const char *repl) {
char work[MAX_STRING_LENGTH]; work[0] = '\0';
const char *src = hay; size_t nlen = strlen(needle);
while (*src) {
const char *pos = strstr(src, needle);
if (!pos) { strlcat(work, src, sizeof(work)); break; }
size_t head = (size_t)(pos - src);
if (head) {
char chunk[2048];
if (head >= sizeof(chunk)) head = sizeof(chunk) - 1;
memcpy(chunk, src, head); chunk[head] = '\0';
strlcat(work, chunk, sizeof(work));
}
strlcat(work, repl, sizeof(work));
src = pos + nlen;
}
strlcpy(hay, work, haysz);
}
/* Capitalize the first alphabetic character of every sentence (start and after .?!).
Skips any number of spaces and common closers (quotes/brackets) between sentences. */
static void capitalize_sentences(char *s) {
bool new_sent = true;
for (char *p = s; *p; ++p) {
unsigned char c = (unsigned char)*p;
if (new_sent) {
if (isalpha(c)) {
*p = toupper(c);
new_sent = false;
} else if (!isspace(c) && c != '"' && c != '\'' && c != ')' && c != ']' && c != '}') {
/* Non-space symbol at sentence start; don't flip flag yet unless it's a letter later. */
}
}
if (*p == '.' || *p == '!' || *p == '?')
new_sent = true;
}
}
/* --- Numbered target parsing --- */
/* Parse "2.corpse" => out_num=2, out_name="corpse". "corpse" => out_num=1, out_name="corpse". */
static void split_numbered_name(const char *raw, int *out_num, char *out_name, size_t outsz) {
char tmp[MAX_NAME_LENGTH];
strlcpy(tmp, raw, sizeof(tmp));
char *p = tmp;
int num = get_number(&p); /* advances p if leading "N." is present */
if (num <= 0) num = 1;
if (*p) strlcpy(out_name, p, outsz);
else strlcpy(out_name, tmp, outsz);
*out_num = num;
}
/* Case-insensitive strcmp helper (tiny) */
static bool ieq(const char *a, const char *b) {
while (*a && *b) {
char ca = tolower((unsigned char)*a++);
char cb = tolower((unsigned char)*b++);
if (ca != cb) return false;
}
return *a == '\0' && *b == '\0';
}
/* Resolve a char/obj with support for numbered refs, searching room chars, room objs, inventory, equipment.
Special: "me"/"self" (any operator) resolves to the actor. */
static bool resolve_reference(struct char_data *actor,
const char *raw, /* e.g., "2.club" */
const char *raw,
struct char_data **out_ch,
struct obj_data **out_obj) {
char search[MAX_NAME_LENGTH];
strlcpy(search, raw, sizeof(search));
char name[MAX_NAME_LENGTH];
int ordinal = 1;
/* Characters: use number for nth match in room */
char *pchar = search;
int cnum = get_number(&pchar);
struct char_data *tch = get_char_room_vis(actor, pchar, &cnum);
if (tch) {
*out_ch = tch;
*out_obj = NULL;
return true;
*out_ch = NULL; *out_obj = NULL;
if (ieq(raw, "me") || ieq(raw, "self")) { *out_ch = actor; return true; }
split_numbered_name(raw, &ordinal, name, sizeof(name));
/* Characters in room (ordinal applies to this list) */
{
int num = ordinal;
struct char_data *tch = get_char_room_vis(actor, name, &num);
if (tch) { *out_ch = tch; return true; }
}
/* Objects: run number for each list independently */
struct obj_data *obj = NULL;
char *pobj;
int onum;
/* Room contents */
pobj = search;
onum = get_number(&pobj);
obj = get_obj_in_list_vis(actor, pobj, &onum, world[IN_ROOM(actor)].contents);
if (!obj) {
/* Inventory */
pobj = search;
onum = get_number(&pobj);
obj = get_obj_in_list_vis(actor, pobj, &onum, actor->carrying);
/* Objects in room */
{
int num = ordinal;
struct obj_data *obj = get_obj_in_list_vis(actor, name, &num, world[IN_ROOM(actor)].contents);
if (obj) { *out_obj = obj; return true; }
}
if (!obj) {
/* Equipment */
pobj = search;
onum = get_number(&pobj);
obj = get_obj_in_equip_vis(actor, pobj, &onum, actor->equipment);
/* Objects carried */
{
int num = ordinal;
struct obj_data *obj = get_obj_in_list_vis(actor, name, &num, actor->carrying);
if (obj) { *out_obj = obj; return true; }
}
if (obj) {
*out_ch = NULL;
*out_obj = obj;
return true;
/* Objects equipped */
{
int num = ordinal;
struct obj_data *obj = get_obj_in_equip_vis(actor, name, &num, actor->equipment);
if (obj) { *out_obj = obj; return true; }
}
return false;
}
/* ---- Token extracted from the emote string ---- */
/* --- Token model --- */
struct emote_tok {
char op; /* one of ~ ! % ^ # & = + or '@' */
char name[MAX_NAME_LENGTH]; /* raw token text after operator (empty for '@') */
char name[MAX_NAME_LENGTH]; /* raw token text (empty for '@') */
struct char_data *tch; /* resolved character (if any) */
struct obj_data *tobj; /* resolved object (if any) */
};
/* ---- Build (actor) name or possessive for @ / default prefix ---- */
static void build_actor_name(struct char_data *actor, bool possessive, char *out, size_t outsz) {
if (!possessive) {
strlcpy(out, GET_NAME(actor), outsz);
} else {
make_possessive(GET_NAME(actor), out, outsz);
}
}
/* ---- Replacement text per viewer for a single token ---- */
/* Build replacement text for a token as seen by 'viewer'. */
static void build_replacement(const struct emote_tok *tok,
struct char_data *actor,
struct char_data *viewer,
bool actor_possessive_for_at, /* true for pemote when op=='@' */
bool actor_possessive_for_at,
char *out, size_t outsz) {
out[0] = '\0';
/* '@' just inserts the actors (maybe possessive) name (not personalized as "you") */
if (tok->op == '@') {
if (!actor_possessive_for_at)
strlcpy(out, GET_NAME(actor), outsz);
else
make_possessive(GET_NAME(actor), out, outsz);
if (!actor_possessive_for_at) strlcpy(out, GET_NAME(actor), outsz);
else make_possessive(GET_NAME(actor), out, outsz);
return;
}
/* Character target? */
if (tok->tch) {
bool is_target_viewer = (viewer == tok->tch);
bool you = (viewer == tok->tch);
switch (tok->op) {
case '~': /* (name) / you */
if (is_target_viewer) strlcpy(out, "you", outsz);
else strlcpy(out, GET_NAME(tok->tch), outsz);
break;
case '!': /* him/her/them / you */
if (is_target_viewer) strlcpy(out, "you", outsz);
else strlcpy(out, pron_obj(tok->tch), outsz);
break;
case '%': /* (name)'s / your */
if (is_target_viewer) strlcpy(out, "your", outsz);
else make_possessive(GET_NAME(tok->tch), out, outsz);
break;
case '^': /* his/her/their / your */
if (is_target_viewer) strlcpy(out, "your", outsz);
else strlcpy(out, pron_pos_adj(tok->tch), outsz);
break;
case '#': /* he/she/they / you */
if (is_target_viewer) strlcpy(out, "you", outsz);
else strlcpy(out, pron_subj(tok->tch), outsz);
break;
case '&': /* himself/herself/themself / yourself */
if (is_target_viewer) strlcpy(out, "yourself", outsz);
else strlcpy(out, pron_refl(tok->tch), outsz);
break;
case '=': /* (name)'s / yours */
if (is_target_viewer) strlcpy(out, "yours", outsz);
else make_possessive(GET_NAME(tok->tch), out, outsz);
break;
case '+': /* his/hers/theirs / yours */
if (is_target_viewer) strlcpy(out, "yours", outsz);
else strlcpy(out, pron_pos_pron(tok->tch), outsz);
break;
default:
strlcpy(out, GET_NAME(tok->tch), outsz);
break;
case '~': if (you) strlcpy(out, "you", outsz); else strlcpy(out, GET_NAME(tok->tch), outsz); break;
case '!': if (you) strlcpy(out, "you", outsz); else strlcpy(out, pron_obj(tok->tch), outsz); break;
case '%': if (you) strlcpy(out, "your", outsz); else make_possessive(GET_NAME(tok->tch), out, outsz); break;
case '^': if (you) strlcpy(out, "your", outsz); else strlcpy(out, pron_pos_adj(tok->tch), outsz); break;
case '#': if (you) strlcpy(out, "you", outsz); else strlcpy(out, pron_subj(tok->tch), outsz); break;
case '&': if (you) strlcpy(out, "yourself", outsz); else strlcpy(out, pron_refl(tok->tch), outsz); break;
case '=': if (you) strlcpy(out, "yours", outsz); else make_possessive(GET_NAME(tok->tch), out, outsz); break;
case '+': if (you) strlcpy(out, "yours", outsz); else strlcpy(out, pron_pos_pron(tok->tch), outsz); break;
default: strlcpy(out, GET_NAME(tok->tch), outsz); break;
}
return;
}
/* Object target? Provide sensible object pronouns where needed */
if (tok->tobj) {
const char *sdesc = (tok->tobj->short_description && *tok->tobj->short_description)
? tok->tobj->short_description
: "something";
? tok->tobj->short_description : "something";
char posbuf[MAX_INPUT_LENGTH];
switch (tok->op) {
case '~': /* (name) */
strlcpy(out, sdesc, outsz);
break;
case '!': /* it */
case '#': /* it (subject) */
strlcpy(out, "it", outsz);
break;
case '%': /* (name)'s */
case '=': /* (name)'s for others side too */
make_possessive(sdesc, posbuf, sizeof(posbuf));
strlcpy(out, posbuf, outsz);
break;
case '^': /* its */
case '+': /* its */
strlcpy(out, "its", outsz);
break;
case '&': /* itself */
strlcpy(out, "itself", outsz);
break;
default:
strlcpy(out, sdesc, outsz);
break;
case '~': strlcpy(out, sdesc, outsz); break;
case '!': strlcpy(out, "it", outsz); break;
case '#': strlcpy(out, "it", outsz); break;
case '%':
case '=': make_possessive(sdesc, posbuf, sizeof(posbuf)); strlcpy(out, posbuf, outsz); break;
case '^':
case '+': strlcpy(out, "its", outsz); break;
case '&': strlcpy(out, "itself", outsz); break;
default: strlcpy(out, sdesc, outsz); break;
}
return;
}
/* Fallback */
strlcpy(out, "something", outsz);
}
/* ---- Replace every occurrence of needle in hay with repl (safe, single buffer) ---- */
static void replace_all_tokens(char *hay, size_t haysz, const char *needle, const char *repl) {
char work[MAX_STRING_LENGTH];
work[0] = '\0';
/* ===================== Main entry ===================== */
void perform_emote(struct char_data *ch, char *argument, bool possessive) {
char base[MAX_STRING_LENGTH];
char with_placeholders[MAX_STRING_LENGTH];
int at_count = 0;
const char *src = hay;
size_t nlen = strlen(needle);
struct emote_tok toks[MAX_EMOTE_TOKENS];
int tokc = 0;
while (*src) {
const char *pos = strstr(src, needle);
if (!pos) {
strlcat(work, src, sizeof(work));
break;
}
/* copy up to pos */
size_t head = (size_t)(pos - src);
char chunk[1024];
if (head >= sizeof(chunk)) head = sizeof(chunk) - 1;
memcpy(chunk, src, head);
chunk[head] = '\0';
skip_spaces(&argument);
if (!*argument) { send_to_char(ch, "Yes... but what?\r\n"); return; }
strlcat(work, chunk, sizeof(work));
strlcat(work, repl, sizeof(work));
/* Only one '@' allowed (inserts actor name/possessive at that spot) */
for (const char *c = argument; *c; ++c) if (*c == '@') at_count++;
if (at_count > 1) { send_to_char(ch, "You can only use '@' once in an emote.\r\n"); return; }
bool has_at = (at_count == 1);
src = pos + nlen;
/* Prefix actor name unless '@' is used */
if (!has_at) {
char who[MAX_INPUT_LENGTH];
build_actor_name(ch, possessive, who, sizeof(who));
snprintf(base, sizeof(base), "%s %s", who, argument);
} else {
strlcpy(base, argument, sizeof(base));
}
strlcpy(hay, work, haysz);
/* Parse operators to placeholders ($Tn) and capture tokens in order */
{
char out[MAX_STRING_LENGTH]; out[0] = '\0';
const char *p = base;
while (*p && tokc < MAX_EMOTE_TOKENS) {
if (*p == '@' || *p == '~' || *p == '!' || *p == '%' ||
*p == '^' || *p == '#' || *p == '&' || *p == '=' || *p == '+') {
char op = *p++;
char name[MAX_NAME_LENGTH]; int ni = 0;
if (op == '@') {
name[0] = '\0';
} else {
/* Allow numbered refs like "2.corpse" but strip trailing punctuation. */
const char *q = p;
/* optional leading digits */
while (*q && isdigit((unsigned char)*q) && ni < (int)sizeof(name) - 1)
name[ni++] = *q++;
/* include a single '.' only if we had digits (for "2.corpse") */
if (ni > 0 && *q == '.' && ni < (int)sizeof(name) - 1)
name[ni++] = *q++;
/* base name (letters/digits/underscore only) */
while (*q && (isalnum((unsigned char)*q) || *q == '_') && ni < (int)sizeof(name) - 1)
name[ni++] = *q++;
name[ni] = '\0';
p = q;
}
toks[tokc].op = op;
toks[tokc].name[0] = '\0';
toks[tokc].tch = NULL;
toks[tokc].tobj = NULL;
if (op != '@') {
strlcpy(toks[tokc].name, name, sizeof(toks[tokc].name));
if (!resolve_reference(ch, name, &toks[tokc].tch, &toks[tokc].tobj)) {
send_to_char(ch, "You can't find one of the references here.\r\n");
return;
}
}
char ph[16]; snprintf(ph, sizeof(ph), "$T%d", tokc + 1);
strlcat(out, ph, sizeof(out));
tokc++;
continue;
}
char buf[2] = { *p++, '\0' };
strlcat(out, buf, sizeof(out));
}
strlcat(out, p, sizeof(out));
strlcpy(with_placeholders, out, sizeof(with_placeholders));
}
/* Replace literal '@' with a placeholder if present */
if (has_at && tokc < MAX_EMOTE_TOKENS) {
toks[tokc].op = '@'; toks[tokc].name[0] = '\0'; toks[tokc].tch = NULL; toks[tokc].tobj = NULL;
char ph[16]; snprintf(ph, sizeof(ph), "$T%d", tokc + 1);
replace_all_tokens(with_placeholders, sizeof(with_placeholders), "@", ph);
tokc++;
}
/* Polish: ensure final punctuation (only if the string lacks any .?! at the very end), collapse spaces */
{
size_t n = strlen(with_placeholders);
bool ends_with_punct = (n > 0 && is_end_punct(with_placeholders[n - 1]));
if (!ends_with_punct) strlcat(with_placeholders, ".", sizeof(with_placeholders));
collapse_spaces(with_placeholders);
}
/* Deliver personalized message to everyone in the room (including actor) */
for (struct descriptor_data *d = descriptor_list; d; d = d->next) {
if (STATE(d) != CON_PLAYING || !d->character) continue;
if (IN_ROOM(d->character) != IN_ROOM(ch)) continue;
char msg[MAX_STRING_LENGTH];
strlcpy(msg, with_placeholders, sizeof(msg));
bool actor_poss_for_at = possessive;
/* Replace each $Tn with viewer-specific text */
for (int i = 0; i < tokc; i++) {
char token[16], repl[MAX_INPUT_LENGTH];
snprintf(token, sizeof(token), "$T%d", i + 1);
build_replacement(&toks[i], ch, d->character, actor_poss_for_at, repl, sizeof(repl));
replace_all_tokens(msg, sizeof(msg), token, repl);
}
/* Final per-viewer cleanup: spaces + multi-sentence capitalization */
collapse_spaces(msg);
capitalize_sentences(msg);
if (d->character == ch) act(msg, FALSE, ch, NULL, NULL, TO_CHAR);
else act(msg, FALSE, ch, NULL, d->character, TO_VICT);
}
}
/* =================== End emote engine =================== */
ACMD(do_wizhelp)
{
@ -423,150 +505,6 @@ ACMD(do_pemote)
perform_emote(ch, argument, TRUE);
}
/* ---- Main emote engine ---- */
void perform_emote(struct char_data *ch, char *argument, bool possessive) {
char base[MAX_STRING_LENGTH];
char with_placeholders[MAX_STRING_LENGTH];
int at_count = 0;
struct emote_tok toks[MAX_EMOTE_TOKENS];
int tokc = 0;
skip_spaces(&argument);
if (!*argument) {
send_to_char(ch, "Yes... but what?\r\n");
return;
}
/* Count @ (enforce at most one) */
for (const char *c = argument; *c; ++c) if (*c == '@') at_count++;
if (at_count > 1) {
send_to_char(ch, "You can only use '@' once in an emote.\r\n");
return;
}
bool has_at = (at_count == 1);
/* If '@' is present, do not auto-prefix the actor name;
otherwise prefix with Name or Name's at the start. */
if (!has_at) {
char who[MAX_INPUT_LENGTH];
build_actor_name(ch, possessive, who, sizeof(who));
snprintf(base, sizeof(base), "%s %s", who, argument);
} else {
strlcpy(base, argument, sizeof(base));
}
/* Parse tokens and replace them with $Tn placeholders */
{
char out[MAX_STRING_LENGTH];
out[0] = '\0';
const char *p = base;
while (*p && tokc < MAX_EMOTE_TOKENS) {
if (*p == '@' || *p == '~' || *p == '!' || *p == '%' ||
*p == '^' || *p == '#' || *p == '&' || *p == '=' || *p == '+') {
char op = *p++;
char name[MAX_NAME_LENGTH];
int ni = 0;
/* '@' carries no name; others do (accept digits and '.' for 2.club) */
if (op == '@') {
name[0] = '\0';
} else {
while (*p && !isspace((unsigned char)*p) && ni < (int)sizeof(name) - 1) {
if (isalnum((unsigned char)*p) || *p == '.' || *p == '_')
name[ni++] = *p++;
else
break;
}
name[ni] = '\0';
}
/* Resolve references for non-@ operators */
toks[tokc].op = op;
toks[tokc].name[0] = '\0';
toks[tokc].tch = NULL;
toks[tokc].tobj = NULL;
if (op != '@') {
strlcpy(toks[tokc].name, name, sizeof(toks[tokc].name));
if (!resolve_reference(ch, name, &toks[tokc].tch, &toks[tokc].tobj)) {
send_to_char(ch, "You can't find one of the references here.\r\n");
return;
}
}
/* Emit placeholder */
char ph[16];
snprintf(ph, sizeof(ph), "$T%d", tokc + 1);
strlcat(out, ph, sizeof(out));
tokc++;
continue;
}
/* copy through normal chars */
char buf[2] = { *p++, '\0' };
strlcat(out, buf, sizeof(out));
}
/* copy rest (if we stopped due to MAX_EMOTE_TOKENS) */
strlcat(out, p, sizeof(out));
strlcpy(with_placeholders, out, sizeof(with_placeholders));
}
/* If '@' was present, replace its literal with a synthetic placeholder */
if (has_at) {
if (tokc < MAX_EMOTE_TOKENS) {
toks[tokc].op = '@';
toks[tokc].name[0] = '\0';
toks[tokc].tch = NULL;
toks[tokc].tobj = NULL;
char ph[16]; snprintf(ph, sizeof(ph), "$T%d", tokc + 1);
replace_all_tokens(with_placeholders, sizeof(with_placeholders), "@", ph);
tokc++;
}
}
/* Normalize: ensure end punctuation; capitalize first char; collapse spaces */
{
size_t n = strlen(with_placeholders);
if (n > 0 && !is_end_punct(with_placeholders[n - 1]))
strlcat(with_placeholders, ".", sizeof(with_placeholders));
if (with_placeholders[0])
with_placeholders[0] = toupper((unsigned char)with_placeholders[0]);
collapse_spaces(with_placeholders);
}
/* Deliver a personalized message to everyone in the room (including actor) */
for (struct descriptor_data *d = descriptor_list; d; d = d->next) {
if (STATE(d) != CON_PLAYING || !d->character) continue;
if (IN_ROOM(d->character) != IN_ROOM(ch)) continue;
char msg[MAX_STRING_LENGTH];
strlcpy(msg, with_placeholders, sizeof(msg));
/* For pemote, when op=='@', we insert actor possessive; for emote, non-possessive */
bool actor_poss_for_at = possessive;
/* Substitute each placeholder for this viewer */
for (int i = 0; i < tokc; i++) {
char token[16], repl[MAX_INPUT_LENGTH];
snprintf(token, sizeof(token), "$T%d", i + 1);
build_replacement(&toks[i], ch, d->character, actor_poss_for_at, repl, sizeof(repl));
replace_all_tokens(msg, sizeof(msg), token, repl);
}
/* Final space normalization */
collapse_spaces(msg);
if (d->character == ch)
act(msg, FALSE, ch, NULL, NULL, TO_CHAR);
else
act(msg, FALSE, ch, NULL, d->character, TO_VICT);
}
}
ACMD(do_send)
{
char arg[MAX_INPUT_LENGTH], buf[MAX_INPUT_LENGTH];

View file

@ -124,10 +124,9 @@ void list_skills(struct char_data *ch)
SPECIAL(dump)
{
struct obj_data *k;
int value = 0;
for (k = world[IN_ROOM(ch)].contents; k; k = world[IN_ROOM(ch)].contents) {
act("$p vanishes in a puff of smoke!", FALSE, 0, k, 0, TO_ROOM);
act("$p is covered in a pile of ever-growing debris.", FALSE, 0, k, 0, TO_ROOM);
extract_obj(k);
}
@ -137,17 +136,10 @@ SPECIAL(dump)
do_drop(ch, argument, cmd, SCMD_DROP);
for (k = world[IN_ROOM(ch)].contents; k; k = world[IN_ROOM(ch)].contents) {
act("$p vanishes in a puff of smoke!", FALSE, 0, k, 0, TO_ROOM);
value += MAX(1, MIN(50, GET_OBJ_COST(k) / 10));
act("$p vis covered in a pile of ever-growing debris.", FALSE, 0, k, 0, TO_ROOM);
extract_obj(k);
}
if (value) {
send_to_char(ch, "You are awarded for outstanding performance.\r\n");
act("$n has been awarded for being a good citizen.", TRUE, ch, 0, 0, TO_ROOM);
increase_gold(ch, value);
}
return (TRUE);
}