Sdesc update

This commit is contained in:
kinther 2025-12-12 07:14:11 -08:00
parent b306f243ac
commit 2e1d7816f6
10 changed files with 503 additions and 272 deletions

View file

@ -296,6 +296,25 @@ to list all forms. i. e. the keyword rumble could be used to cover anyone who
types <action> rumble rumbl rumb rum ru.
#31
AC ARMOR-CLASS ASCENDING-AC DEFENSE
Summary: We use an ascending AC system (higher is better). A typical
unarmored character has AC 10. Attacks roll 1d20 + attack modifiers and
hit if the total your AC. Natural 1 always misses; natural 20 always hits.
How AC is built:
Base: 10
Armor pieces: each worn slot contributes 03 AC (clamped by slot caps)
Armor magic: total armor magic across all pieces is capped at +3
Dexterity: add min(DEX modifier, Dex cap); Dex cap depends on total armor bulk
Shield: base +2 (tower +3 if present), plus shield magic (capped at +3), plus
Shield Use skill proficiency
Situational: cover (+2/+5), spells (Shield, Haste, etc.)
See Also: SCORE
#0
ACAUDIT ARMOR-AUDIT AUDIT-ARMOR IMMORTAL
Summary: Imm-only tool that scans all ITEM\_ARMOR prototypes and reports
@ -338,117 +357,6 @@ Heavy : AC \~1620
See Also: ARMOR PIECES, BULK, SHIELDS, MAGIC CAPS, SCORE, OEDIT ARMOR
#32
AC ARMOR-CLASS ASCENDING-AC DEFENSE
Summary: We use an ascending AC system (higher is better). A typical
unarmored character has AC 10. Attacks roll 1d20 + attack modifiers and
hit if the total your AC. Natural 1 always misses; natural 20 always hits.
How AC is built:
Base: 10
Armor pieces: each worn slot contributes 03 AC (clamped by slot caps)
Armor magic: total armor magic across all pieces is capped at +3
Dexterity: add min(DEX modifier, Dex cap); Dex cap depends on total armor bulk
Shield: base +2 (tower +3 if present), plus shield magic (capped at +3), plus
Shield Use skill proficiency
Situational: cover (+2/+5), spells (Shield, Haste, etc.)
See Also: SCORE
#0
ARMOR SLOTS
Summary: Armor is split across six slots: head, body, legs, arms, hands,
feet. Each piece has:
Piece AC (value[0]): 03 (per-slot hard cap)
Bulk (value[1]): 03 (drives Dex cap & stealth)
Magic (value[2]): 03 (per-slot cap; global armor magic cap +3)
Flags (value[3]): special rules (e.g., Stealth Disadvantage)
Slot caps (defaults)
Head: AC ≤2, Magic ≤1
Body: AC ≤3, Magic ≤3
Legs: AC ≤2, Magic ≤1
Arms/Hands/Feet: AC ≤1, Magic ≤1
Shield is handled separately
SEE ALSO: SHIELDS
#0
BULK DEX-CAP LIGHT MEDIUM HEAVY
Summary: Armor bulk limits how much of your Dex modifier you can apply
to AC.
Light (bulk ≤ 5): Dex cap +5
Medium (bulk 610): Dex cap +2
Heavy (bulk ≥ 11): Dex cap +0 and imposes Stealth Disadvantage
Bulk is computed by summing each pieces bulk × slot weight.
Slot weights: head 1, body 3, legs 2, arms 1, hands 1, feet 1.
SEE ALSO: ARMOR
#0
SHIELD SHIELDS SHIELD-USE
Summary: A shield adds to AC:
Base +2 (tower +3 if applicable)
Shield magic (capped at +3)
Shield Use proficiency (based on your skill%)
Shield Use proficiency (from skill%)
<=14:+0
<=29:+1
<=44:+2
<=59:+3
<=74:+4
<=90:+5
<=100:+6
SEE ALSO: AC ARMOR SLOTS
#0
MAGIC-CAP ENCHANTED-ITEMS
Summary: Sum of magic across all worn pieces is capped at +3 (after
slot caps)
Shield: magic is capped at +3 (separate from armor)
Weapons: magic is capped at +3
Total attack bonus (stats + proficiency + magic + situational) is gently
capped around +10 for balance
These caps are enforced automatically in calculations.
SEE ALSO: AC ARMOR SLOTS SHIELDS
#0
PROFICIENCY WEAPON SKILL SAVING THROWS
Summary: We map your skill % to a 5e-like proficiency bonus:
<=14:+0
<=29:+1
<=44:+2
<=59:+3
<=74:+4
<=90:+5
<=100:+6
This is used for weapon attacks, shields, and (when applicable) saving throws.
SEE ALSO: SHIELDS
#0
STEALTH SNEAK HIDE DISADVTANGE
Summary: You have Stealth Disadvantage if certain conditions are met,
such as:
Any worn piece has the Stealth Disadvantage armor flag, or
Your total armor bulk puts you in Heavy (Dex cap 0)
With Stealth Disadvantage, Sneak and Hide roll twice and take the worse result.
Both success and failure can grant training progress.
#0
ACRONYMS TERMINOLOGY VOCABULARY
Here are some common terms used in building, and TBA zone:
@ -777,6 +685,24 @@ Any areas that 'overlap' the specified range are shown in red.
See Also: ZONES
#0
ARMOR SLOTS
Summary: Armor is split across six slots: head, body, legs, arms, hands,
feet. Each piece has:
Piece AC (value[0]): 03 (per-slot hard cap)
Bulk (value[1]): 03 (drives Dex cap & stealth)
Magic (value[2]): 03 (per-slot cap; global armor magic cap +3)
Flags (value[3]): special rules (e.g., Stealth Disadvantage)
Slot caps (defaults)
Head: AC ≤2, Magic ≤1
Body: AC ≤3, Magic ≤3
Legs: AC ≤2, Magic ≤1
Arms/Hands/Feet: AC ≤1, Magic ≤1
Shield is handled separately
SEE ALSO: SHIELDS
#0
ARMOR-SPELL
Usage : cast 'armor' [target]
@ -1303,6 +1229,20 @@ NOTE: Buildwalk does not autosave on exit. You must type SAVEALL. This way if
See also: TOGGLE, SAVEALL, REDIT, OLC, RLIST, DIG
#31
BULK DEX-CAP LIGHT MEDIUM HEAVY
Summary: Armor bulk limits how much of your Dex modifier you can apply
to AC.
Light (bulk ≤ 5): Dex cap +5
Medium (bulk 610): Dex cap +2
Heavy (bulk ≥ 11): Dex cap +0 and imposes Stealth Disadvantage
Bulk is computed by summing each pieces bulk × slot weight.
Slot weights: head 1, body 3, legs 2, arms 1, hands 1, feet 1.
SEE ALSO: ARMOR
#0
BULLETINS BOARDS BULLETIN-BOARDS MESSAGE-BOARDS POSTING
Bulletin boards are the forum of inter-player communication on the MUD.
@ -4801,6 +4741,20 @@ LOVE MARRIAGE WEDDINGS SEX
Seek professional counseling if you need help on these topics.
#0
MAGIC-CAP ENCHANTED-ITEMS
Summary: Sum of magic across all worn pieces is capped at +3 (after
slot caps)
Shield: magic is capped at +3 (separate from armor)
Weapons: magic is capped at +3
Total attack bonus (stats + proficiency + magic + situational) is gently
capped around +10 for balance
These caps are enforced automatically in calculations.
SEE ALSO: AC ARMOR SLOTS SHIELDS
#0
MAGIC-MISSILE
@ -4982,20 +4936,6 @@ be carrying 100 gold. Well, maybe it is a carrier pigeon, ;-) be reasonable.
There are other ways to reward players than with gold or experience. Try
objects and quests.
See also: AUTOROLL
#31
MEDIT-NUMDAMDICE MEDIT-SIZEDAMDICE MEDIT-BHD
Bare Hand Damage (xdy+z):
(6) BHD NumDice: [ 5]
(7) BHD SizeDice: [ 5]
For BHD (bare hand damage), xdy specifies the dice rolls and z is the
strength bonus added both to BHD and weapon-inflicted damage. For example,
a monster with a BHD of 1d4+10 will do between 11 and 14 hit points each
round without a weapon. If the monster picks up and wields a tiny stick
which gives 1d2 damage, then the monster will do 1d2 + 10 points of damage
per round with the stick.
See also: AUTOROLL
#31
MEDIT-KEYWORDS MEDIT-ALIAS MEDIT-SEX MEDITNAME MOB-NAME MEDIT-ALIAS MOB-SEX
@ -5157,6 +5097,20 @@ Instead of gold reward the player with a pelt, teeth, claws, etc. Always have
a mob carry *something*. It might be low-grade piece of food, trash, or a bad
rash. Be creative.
#31
MEDIT-NUMDAMDICE MEDIT-SIZEDAMDICE MEDIT-BHD
Bare Hand Damage (xdy+z):
(6) BHD NumDice: [ 5]
(7) BHD SizeDice: [ 5]
For BHD (bare hand damage), xdy specifies the dice rolls and z is the
strength bonus added both to BHD and weapon-inflicted damage. For example,
a monster with a BHD of 1d4+10 will do between 11 and 14 hit points each
round without a weapon. If the monster picks up and wields a tiny stick
which gives 1d2 damage, then the monster will do 1d2 + 10 points of damage
per round with the stick.
See also: AUTOROLL
#31
MEDIT-POSITIONS MEDIT-DEFAULT MOB-POSITIONS MOBILE-POSITIONS
@ -7112,6 +7066,22 @@ AUTOASSIST - Player has Autoassist enabled.
See also: FLAGS, PLR
#31
PROFICIENCY WEAPON SKILL SAVING THROWS
Summary: We map your skill % to a 5e-like proficiency bonus:
<=14:+0
<=29:+1
<=44:+2
<=59:+3
<=74:+4
<=90:+5
<=100:+6
This is used for weapon attacks, shields, and (when applicable) saving throws.
SEE ALSO: SHIELDS
#0
PROMOTE PROMOTIONS ADVANCEMENTS RAISES LVLS LEVELS GAINS 31 32 33 34 RANKS RANKING HIRING JOBS STAFFING
Here at The Builder Academy, the level of an immortal generally reflects that
@ -8642,6 +8612,25 @@ bit further along. I am oftentimes in another window or near my computer so
page me if you really need something, if I don't respond I'm probably sleeping
or in class or just don't want to be bothered :-)
#0
SHIELD SHIELDS SHIELD-USE
Summary: A shield adds to AC:
Base +2 (tower +3 if applicable)
Shield magic (capped at +3)
Shield Use proficiency (based on your skill%)
Shield Use proficiency (from skill%)
<=14:+0
<=29:+1
<=44:+2
<=59:+3
<=74:+4
<=90:+5
<=100:+6
SEE ALSO: AC ARMOR SLOTS
#0
SHOCKING-GRASP
@ -9258,6 +9247,17 @@ Examples:
See also: FLAGS
#0
STEALTH SNEAK HIDE DISADVTANGE
Summary: You have Stealth Disadvantage if certain conditions are met,
such as:
Any worn piece has the Stealth Disadvantage armor flag, or
Your total armor bulk puts you in Heavy (Dex cap 0)
With Stealth Disadvantage, Sneak and Hide roll twice and take the worse result.
Both success and failure can grant training progress.
#0
STOCK
This is a listing of stock areas.
@ -13524,4 +13524,60 @@ zlock - Locks one or all zones. Type zlock for usage.
zunlock - Locks one or all zones. Type zunlock for usage.
#31
EMOTE EMOTING
Emoting allows you to describe your characters actions, expressions, and
nuances in freeform text, bringing scenes to life in ways that predefined
commands cannot. These tools let you craft rich, reactive roleplay that
feels immediate and personal to everyone who sees it.
The emoting engine in this game supports dynamic references which can be used
in your emotes to help you make the world feel vibrant:
+----------+----------------------------+----------------+-----------------+
| Operator | Actor Types | Target Sees | Others See |
+----------+----------------------------+----------------+-----------------+
| ~ | target's name | you | target's name |
| ! | him/her/them | you | him/her/them |
| % | target's possessive | your | name's |
| ^ | his/her/their | your | his/her/their |
| # | he/she/they | you | he/she/they |
| & | himself/herself/themself | yourself | himself/etc. |
| = | target's possessive (abs) | yours | name's |
| + | his/hers/theirs (abs) | yours | his/hers/theirs |
| @ | actor's name or possessive | actor name/pos | actor name/pos |
+----------+----------------------------+----------------+-----------------+
Examples of using these operators in an emote:
>emote scans the room, looking for ~amos
The room sees:
The bald, pudgy man scans the room, looking for the tall, muscular man.
Amos sees:
The bald, pudgy man scans the room, looking for you.
>pemote after scanning the room, @ eyes drop to ~mug in his hand as he sighs.
The room sees:
After scanning the room, the bald, pudgy man's eyes drop to a clay mug in his
hand as he sighs.
>emote gesturing to %amos empty mug, @ waves !amos over with %me right hand,
pointing to an empty seat at ^me table.
The room sees:
Gesturing to the tall, muscular man's empty mug, the bald, budgy man waves
him over with his right hand, pointing to an empty seat at his table.
Amos sees:
Gesturing to your empty mug, the bald, pudgy man waves you over with his right
hand, pointing to an empty seat at his table.
#0
$~

View file

@ -19,21 +19,21 @@ Skill 142 60
Skill 143 60
Skill 145 60
E
L 3 118 1
L 5 131 1
L 6 110 1
L 7 108 1
L 8 115 1
L 9 124 1
L 10 107 1
L 11 111 1
L 15 117 1
L 16 117 1
L 17 127 1
L 16 117 1
L 15 117 1
L 11 111 1
L 10 107 1
L 9 124 1
L 8 115 1
L 7 108 1
L 6 110 1
L 5 131 1
L 3 118 1
#101
Sally~
slim lanky human soldier guard~
a slim, lanky human soldier~
the slim, lanky human soldier~
A slim, lanky human soldier stands here eyeing passerbys.
~
This woman looks rather thin, with her darkly tanned skin hugging her frame
@ -50,17 +50,17 @@ Skill 142 60
Skill 143 60
Skill 145 60
E
L 17 127 1
L 16 117 1
L 15 117 1
L 11 111 1
L 10 107 1
L 9 124 1
L 8 115 1
L 7 108 1
L 6 110 1
L 5 131 1
L 3 118 1
L 5 131 1
L 6 110 1
L 7 108 1
L 8 115 1
L 9 124 1
L 10 107 1
L 11 111 1
L 15 117 1
L 16 117 1
L 17 127 1
#102
Baldy~
barkeep stocky bald~
@ -77,8 +77,8 @@ others.
1 3d12+60
8 8 1
E
L 14 113 1
L 9 112 1
L 14 113 1
#103
Lanky~
woman lanky scarred~

View file

@ -388,6 +388,7 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
CCNRM(ch, C_NRM));
}
/* NPCs with a full long description at default position: print that and bail. */
if (IS_NPC(i) && i->player.long_descr && GET_POS(i) == GET_DEFAULT_POS(i)) {
if (AFF_FLAGGED(i, AFF_INVISIBLE))
send_to_char(ch, "*");
@ -398,6 +399,7 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
else if (IS_GOOD(i))
send_to_char(ch, "(Blue Aura) ");
}
send_to_char(ch, "%s", i->player.long_descr);
if (AFF_FLAGGED(i, AFF_SANCTUARY))
@ -408,10 +410,16 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
return;
}
if (IS_NPC(i))
send_to_char(ch, "%c%s", UPPER(*i->player.short_descr), i->player.short_descr + 1);
else
send_to_char(ch, "%s", i->player.name);
/* Otherwise, use short description (PC or NPC) if present, else name. */
{
const char *sdesc = GET_SHORT_DESC(i);
if (sdesc && *sdesc) {
/* Capitalize first letter for room list */
send_to_char(ch, "%c%s", UPPER(*sdesc), sdesc + 1);
} else {
send_to_char(ch, "%s", GET_NAME(i));
}
}
if (AFF_FLAGGED(i, AFF_INVISIBLE))
send_to_char(ch, " (invisible)");
@ -456,6 +464,7 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
else if (IS_GOOD(i))
send_to_char(ch, " (Blue Aura)");
}
send_to_char(ch, "\r\n");
if (AFF_FLAGGED(i, AFF_SANCTUARY))
@ -654,7 +663,20 @@ static void look_in_obj(struct char_data *ch, char *arg)
if (OBJVAL_FLAGGED(obj, CONT_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE)))
send_to_char(ch, "It is closed.\r\n");
else {
send_to_char(ch, "%s", fname(obj->name));
/* Choose a label for the container:
* - For corpses (GET_OBJ_VAL(obj,3) == 1), use the short_description,
* e.g. "the corpse of the tall, muscular man"
* - Otherwise, fall back to the first keyword (fname(obj->name))
*/
const char *label;
if (GET_OBJ_VAL(obj, 3) == 1 && obj->short_description && *obj->short_description)
label = obj->short_description;
else
label = fname(obj->name);
send_to_char(ch, "%s", label);
switch (bits) {
case FIND_OBJ_INV:
send_to_char(ch, " (carried): \r\n");

View file

@ -145,9 +145,20 @@ static void collapse_spaces(char *s) {
*dst = '\0';
}
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);
static void build_actor_name(struct char_data *actor,
bool possessive,
char *out, size_t outsz)
{
/* Prefer short description if present; fall back to personal name */
const char *base =
(GET_SHORT_DESC(actor) && *GET_SHORT_DESC(actor))
? GET_SHORT_DESC(actor)
: GET_NAME(actor);
if (!possessive)
strlcpy(out, base, outsz);
else
make_possessive(base, out, outsz);
}
/* Replace all occurrences of 'needle' in 'hay' with 'repl'. */
@ -273,23 +284,69 @@ static void build_replacement(const struct emote_tok *tok,
out[0] = '\0';
if (tok->op == '@') {
if (!actor_possessive_for_at) strlcpy(out, GET_NAME(actor), outsz);
else make_possessive(GET_NAME(actor), out, outsz);
/* Use actor's sdesc if present, otherwise their name */
const char *ref = (GET_SHORT_DESC(actor) && *GET_SHORT_DESC(actor))
? GET_SHORT_DESC(actor)
: GET_NAME(actor);
if (!actor_possessive_for_at)
strlcpy(out, ref, outsz);
else
make_possessive(ref, out, outsz);
return;
}
if (tok->tch) {
bool you = (viewer == tok->tch);
/* For non-you views, prefer sdesc over name */
const char *ref = (GET_SHORT_DESC(tok->tch) && *GET_SHORT_DESC(tok->tch))
? GET_SHORT_DESC(tok->tch)
: GET_NAME(tok->tch);
switch (tok->op) {
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;
case '~':
if (you) strlcpy(out, "you", outsz);
else strlcpy(out, ref, 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(ref, 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(ref, out, outsz);
break;
case '+':
if (you) strlcpy(out, "yours", outsz);
else strlcpy(out, pron_pos_pron(tok->tch), outsz);
break;
default:
strlcpy(out, ref, outsz);
break;
}
return;
}

View file

@ -242,14 +242,22 @@ static void make_corpse(struct char_data *ch)
IN_ROOM(corpse) = NOWHERE;
corpse->name = strdup("corpse");
snprintf(buf2, sizeof(buf2), "The corpse of %s is lying here.", GET_NAME(ch));
/* Use short description if available, otherwise fall back to name */
const char *who = NULL;
if (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch))
who = GET_SHORT_DESC(ch);
else
who = GET_NAME(ch);
snprintf(buf2, sizeof(buf2), "The corpse of %s is lying here.", who);
corpse->description = strdup(buf2);
snprintf(buf2, sizeof(buf2), "the corpse of %s", GET_NAME(ch));
snprintf(buf2, sizeof(buf2), "the corpse of %s", who);
corpse->short_description = strdup(buf2);
GET_OBJ_TYPE(corpse) = ITEM_CONTAINER;
for(x = y = 0; x < EF_ARRAY_MAX || y < TW_ARRAY_MAX; x++, y++) {
for (x = y = 0; x < EF_ARRAY_MAX || y < TW_ARRAY_MAX; x++, y++) {
if (x < EF_ARRAY_MAX)
GET_OBJ_EXTRA_AR(corpse, x) = 0;
if (y < TW_ARRAY_MAX)
@ -257,8 +265,8 @@ static void make_corpse(struct char_data *ch)
}
SET_BIT_AR(GET_OBJ_WEAR(corpse), ITEM_WEAR_TAKE);
SET_BIT_AR(GET_OBJ_EXTRA(corpse), ITEM_NODONATE);
GET_OBJ_VAL(corpse, 0) = 0; /* You can't store stuff in a corpse */
GET_OBJ_VAL(corpse, 3) = 1; /* corpse identifier */
GET_OBJ_VAL(corpse, 0) = 0; /* You can't store stuff in a corpse */
GET_OBJ_VAL(corpse, 3) = 1; /* corpse identifier */
GET_OBJ_WEIGHT(corpse) = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
GET_OBJ_RENT(corpse) = 100000;
if (IS_NPC(ch))
@ -281,11 +289,6 @@ static void make_corpse(struct char_data *ch)
/* transfer gold */
if (GET_GOLD(ch) > 0) {
/* following 'if' clause added to fix gold duplication loophole. The above
* line apparently refers to the old "partially log in, kill the game
* character, then finish login sequence" duping bug. The duplication has
* been fixed (knock on wood) but the test below shall live on, for a
* while. -gg 3/3/2002 */
if (IS_NPC(ch) || ch->desc) {
money = create_money(GET_GOLD(ch));
obj_to_obj(money, corpse);

View file

@ -1078,35 +1078,66 @@ struct char_data *get_char_room_vis(struct char_data *ch, char *name, int *numbe
/* JE */
if (!str_cmp(name, "self") || !str_cmp(name, "me"))
return (ch);
return ch;
/* 0.<name> means PC with name */
if (*number == 0)
return (get_player_vis(ch, name, NULL, FIND_CHAR_ROOM));
return get_player_vis(ch, name, NULL, FIND_CHAR_ROOM);
for (i = world[IN_ROOM(ch)].people; i && *number; i = i->next_in_room) {
const char *namelist = NULL;
bool match = FALSE;
if (IS_NPC(i)) {
/* NPCs match either keywords or their name */
/* NPCs: match either keywords or their name (unchanged) */
const char *keywords = GET_KEYWORDS(i);
const char *proper = GET_NAME(i);
if ((keywords && isname(name, keywords)) || (proper && isname(name, proper)))
if ((keywords && isname(name, keywords)) ||
(proper && isname(name, proper)))
match = TRUE;
} else {
/* PCs match only their name */
namelist = GET_NAME(i);
if (namelist && isname(name, namelist))
/* PCs: match against name + sanitized short description */
const char *proper = GET_NAME(i);
const char *sdesc = GET_SHORT_DESC(i);
if (sdesc && *sdesc) {
char clean_sdesc[MAX_INPUT_LENGTH];
char tmp[MAX_INPUT_LENGTH * 2];
int w = 0;
/* Turn punctuation etc. into spaces so "tall," -> "tall" */
for (int r = 0; sdesc[r] && w < (int)sizeof(clean_sdesc) - 1; r++) {
unsigned char c = (unsigned char)sdesc[r];
if (isalnum(c) || c == '\'' || c == '-') {
clean_sdesc[w++] = c;
} else {
/* normalize anything else (spaces, commas, etc.) to a single space */
clean_sdesc[w++] = ' ';
}
}
clean_sdesc[w] = '\0';
if (proper && *proper)
snprintf(tmp, sizeof(tmp), "%s %s", proper, clean_sdesc);
else
snprintf(tmp, sizeof(tmp), "%s", clean_sdesc);
if (isname(name, tmp))
match = TRUE;
} else if (proper && isname(name, proper)) {
/* Fallback: no sdesc yet, use name only */
match = TRUE;
}
}
if (match && CAN_SEE(ch, i))
if (--(*number) == 0)
return (i);
return i;
}
return (NULL);
return NULL;
}
struct char_data *get_char_world_vis(struct char_data *ch, char *name, int *number)

View file

@ -1608,58 +1608,108 @@ void nanny(struct descriptor_data *d, char *arg)
STATE(d) = CON_QCLASS;
break;
case CON_QCLASS:
load_result = parse_class(*arg);
if (load_result == CLASS_UNDEFINED) {
write_to_output(d, "\r\nThat's not a class.\r\nClass: ");
return;
} else {
GET_CLASS(d->character) = load_result;
}
/* Create player entry and initialize character now so file exists */
if (d->olc) {
free(d->olc);
d->olc = NULL;
}
if (GET_PFILEPOS(d->character) < 0)
GET_PFILEPOS(d->character) = create_entry(GET_PC_NAME(d->character));
/* Initialize base stats, starting level, etc. */
init_char(d->character);
save_char(d->character);
save_player_index();
/* Log and register early so new players are tracked immediately */
GET_PREF(d->character) = rand_number(1, 128000);
GET_HOST(d->character) = strdup(d->host);
mudlog(NRM, LVL_GOD, TRUE, "%s [%s] new player created (awaiting description).",
GET_NAME(d->character), d->host);
if (AddRecentPlayer(GET_NAME(d->character), d->host, TRUE, FALSE) == FALSE)
mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE,
"Failure to AddRecentPlayer (returned FALSE).");
/* Now move to mandatory description entry */
write_to_output(d,
"\r\nBefore entering the world, please describe your character.\r\n"
"Focus on what others can immediately see — height, build, complexion,\r\n"
"facial structure, hair, eyes, and other physical details. Avoid names,\r\n"
"clothing, or personal information that someone would not know meeting\r\n"
"you for the first time.\r\n\r\n"
"Example:\r\n"
" This broad-shouldered human stands with a relaxed but watchful bearing.\r\n"
" Weather and sun have darkened their skin, and faint scars trace the backs\r\n"
" of their hands. Their eyes are a pale, gray-green hue, steady and alert\r\n"
" beneath a low brow. Thick, uneven hair falls around a strong jaw and\r\n"
" angular features.\r\n\r\n");
d->backstr = NULL;
d->str = &d->character->player.description;
d->max_str = PLR_DESC_LENGTH;
STATE(d) = CON_PLR_DESC;
send_editor_help(d);
case CON_QCLASS:
load_result = parse_class(*arg);
if (load_result == CLASS_UNDEFINED) {
write_to_output(d, "\r\nThat's not a class.\r\nClass: ");
return;
} else {
GET_CLASS(d->character) = load_result;
}
/* Create player entry and initialize character now so file exists */
if (d->olc) {
free(d->olc);
d->olc = NULL;
}
if (GET_PFILEPOS(d->character) < 0)
GET_PFILEPOS(d->character) = create_entry(GET_PC_NAME(d->character));
/* Initialize base stats, starting level, etc. */
init_char(d->character);
save_char(d->character);
save_player_index();
/* Log and register early so new players are tracked immediately */
GET_PREF(d->character) = rand_number(1, 128000);
GET_HOST(d->character) = strdup(d->host);
mudlog(NRM, LVL_GOD, TRUE, "%s [%s] new player created (awaiting description).",
GET_NAME(d->character), d->host);
if (AddRecentPlayer(GET_NAME(d->character), d->host, TRUE, FALSE) == FALSE)
mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE,
"Failure to AddRecentPlayer (returned FALSE).");
/* === NEW: mandatory short description before main description === */
write_to_output(d,
"\r\nBefore entering the world, you must choose a short description.\r\n"
"This is what others see in the room list and messages instead of your name.\r\n"
"It should describe your appearance, not identity.\r\n\r\n"
"Examples:\r\n"
" the tall, muscular man\r\n"
" the lanky, sharp-eyed elf\r\n"
" the short, bald dwarf\r\n\r\n"
"Do not include your character's name here.\r\n"
"Short description: ");
STATE(d) = CON_QSHORTDESC;
return;
case CON_QSHORTDESC: {
skip_spaces(&arg);
if (!*arg) {
write_to_output(d, "\r\nA short description cannot be empty.\r\n"
"Short description: ");
return;
}
/* Do not allow their character name in the sdesc */
if (str_str(arg, GET_NAME(d->character))) {
write_to_output(d, "\r\nYour short description must not include your name.\r\n"
"Short description: ");
return;
}
/* Enforce length limit (optional, but recommended) */
if (strlen(arg) > MAX_NAME_LENGTH * 2) {
write_to_output(d, "\r\nThat description is too long.\r\n"
"Short description: ");
return;
}
/* Store as player's short description */
if (GET_SHORT_DESC(d->character))
free(GET_SHORT_DESC(d->character));
GET_SHORT_DESC(d->character) = strdup(arg);
/* Immediately save it to disk */
save_char(d->character);
save_player_index();
/* Now transition to full description input */
write_to_output(d,
"\r\nBefore entering the world, please describe your character.\r\n"
"Focus on what others can immediately see — height, build, complexion,\r\n"
"facial structure, hair, eyes, and other physical details. Avoid names,\r\n"
"clothing, or personal information that someone would not know meeting\r\n"
"you for the first time.\r\n\r\n"
"Example:\r\n"
" This broad-shouldered human stands with a relaxed but watchful bearing.\r\n"
" Weather and sun have darkened their skin, and faint scars trace the backs\r\n"
" of their hands. Their eyes are a pale, gray-green hue, steady and alert\r\n"
" beneath a low brow. Thick, uneven hair falls around a strong jaw and\r\n"
" angular features.\r\n\r\n");
d->backstr = NULL;
d->str = &d->character->player.description;
d->max_str = PLR_DESC_LENGTH;
STATE(d) = CON_PLR_DESC;
send_editor_help(d);
return;
}
case CON_PLR_DESC:
/* If the player canceled or has no description, prompt again */

View file

@ -247,6 +247,7 @@ int load_char(const char *name, struct char_data *ch)
/* Character initializations. Necessary to keep some things straight. */
ch->affected = NULL;
ch->player.short_descr = NULL; /* ensure a clean start */
for (i = 1; i <= MAX_SKILLS; i++)
if (IS_NPC(ch))
ch->mob_specials.skills[i] = 0;
@ -430,6 +431,13 @@ int load_char(const char *name, struct char_data *ch)
case 'S':
if (!strcmp(tag, "Sex ")) GET_SEX(ch) = atoi(line);
else if (!strcmp(tag, "Sdsc")) {
/* Clear any existing sdesc to avoid leaks */
if (GET_SHORT_DESC(ch))
free(GET_SHORT_DESC(ch));
/* 'line' is the remainder of the line after "Sdsc" + space */
GET_SHORT_DESC(ch) = strdup(line);
}
else if (!strcmp(tag, "ScrW")) GET_SCREEN_WIDTH(ch) = atoi(line);
else if (!strcmp(tag, "Skil")) load_skills(fl, ch);
else if (!strcmp(tag, "SkGt")) { /* Skill Gain Timers */
@ -570,6 +578,7 @@ void save_char(struct char_data * ch)
/* end char_to_store code */
if (GET_NAME(ch)) fprintf(fl, "Name: %s\n", GET_NAME(ch));
if (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) fprintf(fl, "Sdsc: %s\n", GET_SHORT_DESC(ch));
if (GET_PASSWD(ch)) fprintf(fl, "Pass: %s\n", GET_PASSWD(ch));
if (ch->player.description && *ch->player.description) {
strcpy(buf, ch->player.description);

View file

@ -314,30 +314,31 @@
#define CON_CNFPASSWD 6 /**< New character, confirm password */
#define CON_QSEX 7 /**< Choose character sex */
#define CON_QCLASS 8 /**< Choose character class */
#define CON_RMOTD 9 /**< Reading the message of the day */
#define CON_MENU 10 /**< At the main menu */
#define CON_PLR_DESC 11 /**< Enter a new character description prompt */
#define CON_CHPWD_GETOLD 12 /**< Changing passwd: Get old */
#define CON_CHPWD_GETNEW 13 /**< Changing passwd: Get new */
#define CON_CHPWD_VRFY 14 /**< Changing passwd: Verify new password */
#define CON_DELCNF1 15 /**< Character Delete: Confirmation 1 */
#define CON_DELCNF2 16 /**< Character Delete: Confirmation 2 */
#define CON_DISCONNECT 17 /**< In-game link loss (leave character) */
#define CON_OEDIT 18 /**< OLC mode - object editor */
#define CON_REDIT 19 /**< OLC mode - room editor */
#define CON_ZEDIT 20 /**< OLC mode - zone info editor */
#define CON_MEDIT 21 /**< OLC mode - mobile editor */
#define CON_SEDIT 22 /**< OLC mode - shop editor */
#define CON_TEDIT 23 /**< OLC mode - text editor */
#define CON_CEDIT 24 /**< OLC mode - conf editor */
#define CON_AEDIT 25 /**< OLC mode - social (action) edit */
#define CON_TRIGEDIT 26 /**< OLC mode - trigger edit */
#define CON_HEDIT 27 /**< OLC mode - help edit */
#define CON_QEDIT 28 /**< OLC mode - quest edit */
#define CON_PREFEDIT 29 /**< OLC mode - preference edit */
#define CON_IBTEDIT 30 /**< OLC mode - idea/bug/typo edit */
#define CON_MSGEDIT 31 /**< OLC mode - message editor */
#define CON_GET_PROTOCOL 32 /**< Used at log-in while attempting to get protocols > */
#define CON_QSHORTDESC 9 /**< Enter a new character short description prompt */
#define CON_RMOTD 10 /**< Reading the message of the day */
#define CON_MENU 11 /**< At the main menu */
#define CON_PLR_DESC 12 /**< Enter a new character description prompt */
#define CON_CHPWD_GETOLD 13 /**< Changing passwd: Get old */
#define CON_CHPWD_GETNEW 14 /**< Changing passwd: Get new */
#define CON_CHPWD_VRFY 15 /**< Changing passwd: Verify new password */
#define CON_DELCNF1 16 /**< Character Delete: Confirmation 1 */
#define CON_DELCNF2 17 /**< Character Delete: Confirmation 2 */
#define CON_DISCONNECT 18 /**< In-game link loss (leave character) */
#define CON_OEDIT 19 /**< OLC mode - object editor */
#define CON_REDIT 20 /**< OLC mode - room editor */
#define CON_ZEDIT 21 /**< OLC mode - zone info editor */
#define CON_MEDIT 22 /**< OLC mode - mobile editor */
#define CON_SEDIT 23 /**< OLC mode - shop editor */
#define CON_TEDIT 24 /**< OLC mode - text editor */
#define CON_CEDIT 25 /**< OLC mode - conf editor */
#define CON_AEDIT 26 /**< OLC mode - social (action) edit */
#define CON_TRIGEDIT 27 /**< OLC mode - trigger edit */
#define CON_HEDIT 28 /**< OLC mode - help edit */
#define CON_QEDIT 29 /**< OLC mode - quest edit */
#define CON_PREFEDIT 30 /**< OLC mode - preference edit */
#define CON_IBTEDIT 31 /**< OLC mode - idea/bug/typo edit */
#define CON_MSGEDIT 32 /**< OLC mode - message editor */
#define CON_GET_PROTOCOL 33 /**< Used at log-in while attempting to get protocols > */
/* OLC States range - used by IS_IN_OLC and IS_PLAYING */
#define FIRST_OLC_STATE CON_OEDIT /**< The first CON_ state that is an OLC */
@ -876,9 +877,9 @@ struct char_player_data
char passwd[MAX_PWD_LENGTH+1]; /**< PC's password */
char *name; /**< Display name (PC/NPC personal name) */
char *keywords; /**< Parsing keywords (for NPCs and parsing lookup) */
char *short_descr; /**< NPC 'actions' */
char *long_descr; /**< PC / NPC look description */
char *description; /**< NPC Extra descriptions */
char *short_descr; /**< PC / NPC short description */
char *long_descr; /**< PC / NPC long description */
char *description; /**< PC / NPC main descriptions */
byte sex; /**< PC / NPC sex */
byte chclass; /**< PC / NPC class */
byte level; /**< PC / NPC level */

View file

@ -881,15 +881,17 @@ do \
(CAN_WEAR((obj), ITEM_WEAR_TAKE) && CAN_CARRY_OBJ((ch),(obj)) && \
CAN_SEE_OBJ((ch),(obj)))
/**
* If vict can see ch, return visible name.
* For NPCs: use short_descr (e.g. "a burly guard").
* For PCs: use proper name.
/* Display name for a character as seen by 'vict'.
* - If vict cant see ch: "someone"
* - If NPC: use short_descr if set, else personal name
* - If PC: use short_descr if set, else personal name
*/
#define PERS(ch, vict) \
(CAN_SEE((vict), (ch)) ? \
(IS_NPC(ch) ? GET_SHORT_DESC(ch) : GET_NAME(ch)) : \
((GET_LEVEL(ch) > LVL_IMMORT) ? "an immortal" : "someone"))
#define PERS(ch, vict) \
(CAN_SEE((vict), (ch)) ? \
((GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? \
GET_SHORT_DESC(ch) : \
GET_NAME(ch)) : \
"someone")
/** If vict can see obj, return obj short description, else return
* "something". */