From be79603e8f32c7fd0c66b317a1c569e63e22726d Mon Sep 17 00:00:00 2001 From: kinther Date: Mon, 29 Dec 2025 10:55:09 -0800 Subject: [PATCH] Change ldesc update --- README.md | 1 + src/act.h | 1 + src/act.informative.c | 32 ++++++++++++-- src/act.movement.c | 16 +++++++ src/act.other.c | 99 ++++++++++++++++++++++++++++++++++++++++++- src/interpreter.c | 6 ++- src/structs.h | 1 + src/utils.c | 68 +++++++++++++++++++++++++++++ src/utils.h | 2 + 9 files changed, 220 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 48ea3a0..adc073c 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Features in Miranthas MUD Alpha release: * Account system for tracking players/characters over long periods of time * Race/species selection and stat ranges (elves have higher dex, dwarves have higher str, etc) * Prioritized stats during character generation + * Ability to change ldesc of PC/NPC's in some situations Features to be implemented in the next few releases: diff --git a/src/act.h b/src/act.h index ef84127..405a7a0 100644 --- a/src/act.h +++ b/src/act.h @@ -257,6 +257,7 @@ ACMD(do_group); ACMD(do_hide); ACMD(do_listen); ACMD(do_not_here); +ACMD(do_change); ACMD(do_report); ACMD(do_save); ACMD(do_skills); diff --git a/src/act.informative.c b/src/act.informative.c index 2053b90..423929e 100644 --- a/src/act.informative.c +++ b/src/act.informative.c @@ -569,8 +569,30 @@ 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)) { + /* Custom ldesc overrides position-based output. */ + if (i->char_specials.custom_ldesc && i->player.long_descr) { + if (AFF_FLAGGED(i, AFF_INVISIBLE)) + send_to_char(ch, "*"); + + if (AFF_FLAGGED(ch, AFF_DETECT_ALIGN)) { + if (IS_EVIL(i)) + send_to_char(ch, "(Red Aura) "); + 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)) + act("...$e glows with a bright light!", FALSE, i, 0, ch, TO_VICT); + if (AFF_FLAGGED(i, AFF_BLIND) && GET_LEVEL(i) < LVL_IMMORT) + act("...$e is groping around blindly!", FALSE, i, 0, ch, TO_VICT); + + return; + } + + /* Characters with a full long description at default position: print that and bail. */ + if (i->player.long_descr && GET_POS(i) == GET_DEFAULT_POS(i)) { if (AFF_FLAGGED(i, AFF_INVISIBLE)) send_to_char(ch, "*"); @@ -892,7 +914,11 @@ static bool look_list_direction_chars(struct char_data *ch, room_rnum room) if (AFF_FLAGGED(tch, AFF_HIDE)) { if (CAN_SEE(ch, tch) || look_can_spot_hidden(ch, tch, room)) { - send_to_char(ch, "a shadowy figure\r\n"); + char hidden_ldesc[MAX_STRING_LENGTH]; + if (build_hidden_ldesc(tch, hidden_ldesc, sizeof(hidden_ldesc))) + send_to_char(ch, "%s", hidden_ldesc); + else + send_to_char(ch, "a shadowy figure\r\n"); found = TRUE; } continue; diff --git a/src/act.movement.c b/src/act.movement.c index 29d0734..224993f 100644 --- a/src/act.movement.c +++ b/src/act.movement.c @@ -492,6 +492,7 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check) /* Only here is the move successful *and* complete. Return success for * calling functions to handle post move operations. */ + clear_custom_ldesc(ch); return (1); } @@ -1000,6 +1001,7 @@ ACMD(do_stand) act(self_msg, TRUE, ch, furniture, 0, TO_CHAR); act(room_msg, TRUE, ch, furniture, 0, TO_ROOM); GET_POS(ch) = FIGHTING(ch) ? POS_FIGHTING : POS_STANDING; + clear_custom_ldesc(ch); return; } @@ -1020,6 +1022,7 @@ ACMD(do_stand) char_from_furniture(ch); /* Will be standing after a successful bash and may still be fighting. */ GET_POS(ch) = FIGHTING(ch) ? POS_FIGHTING : POS_STANDING; + clear_custom_ldesc(ch); break; case POS_RESTING: @@ -1033,6 +1036,7 @@ ACMD(do_stand) GET_POS(ch) = POS_STANDING; /* Were they sitting in something. */ char_from_furniture(ch); + clear_custom_ldesc(ch); break; case POS_SLEEPING: @@ -1050,6 +1054,7 @@ ACMD(do_stand) else act("$n stops floating around, and puts $s feet on the ground.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_STANDING; + clear_custom_ldesc(ch); break; } } @@ -1122,6 +1127,7 @@ ACMD(do_sit) act(self_msg, TRUE, ch, furniture, 0, TO_CHAR); act(room_msg, TRUE, ch, furniture, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; + clear_custom_ldesc(ch); return; } @@ -1130,6 +1136,7 @@ ACMD(do_sit) send_to_char(ch, "You sit down.\r\n"); act("$n sits down.", FALSE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; + clear_custom_ldesc(ch); break; case POS_SITTING: send_to_char(ch, "You're sitting already.\r\n"); @@ -1141,6 +1148,7 @@ ACMD(do_sit) else act("$n stops resting and sits up.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; + clear_custom_ldesc(ch); break; case POS_SLEEPING: send_to_char(ch, "You have to wake up first.\r\n"); @@ -1155,6 +1163,7 @@ ACMD(do_sit) else act("$n stops floating around, and sits down.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SITTING; + clear_custom_ldesc(ch); break; } } @@ -1227,6 +1236,7 @@ ACMD(do_rest) act(self_msg, TRUE, ch, furniture, 0, TO_CHAR); act(room_msg, TRUE, ch, furniture, 0, TO_ROOM); GET_POS(ch) = POS_RESTING; + clear_custom_ldesc(ch); return; } @@ -1240,6 +1250,7 @@ ACMD(do_rest) act("$n sits down and rests.", TRUE, ch, 0, 0, TO_ROOM); } GET_POS(ch) = POS_RESTING; + clear_custom_ldesc(ch); break; case POS_SITTING: @@ -1258,6 +1269,7 @@ ACMD(do_rest) act("$n rests.", TRUE, ch, 0, 0, TO_ROOM); } GET_POS(ch) = POS_RESTING; + clear_custom_ldesc(ch); break; case POS_RESTING: @@ -1279,6 +1291,7 @@ ACMD(do_rest) else act("$n stops floating around, and rests.", FALSE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_RESTING; + clear_custom_ldesc(ch); break; } } @@ -1332,6 +1345,7 @@ ACMD(do_sleep) act("You go to sleep on $p.", TRUE, ch, furniture, 0, TO_CHAR); act("$n lies down and falls asleep on $p.", TRUE, ch, furniture, 0, TO_ROOM); GET_POS(ch) = POS_SLEEPING; + clear_custom_ldesc(ch); return; } @@ -1354,6 +1368,7 @@ ACMD(do_sleep) act("$n lies down and falls asleep.", TRUE, ch, 0, 0, TO_ROOM); } GET_POS(ch) = POS_SLEEPING; + clear_custom_ldesc(ch); break; case POS_SLEEPING: @@ -1371,6 +1386,7 @@ ACMD(do_sleep) else act("$n stops floating around, and lies down to sleep.", TRUE, ch, 0, 0, TO_ROOM); GET_POS(ch) = POS_SLEEPING; + clear_custom_ldesc(ch); break; } } diff --git a/src/act.other.c b/src/act.other.c index 99d611f..a61dae5 100644 --- a/src/act.other.c +++ b/src/act.other.c @@ -36,6 +36,36 @@ static void print_group(struct char_data *ch); static void display_group_list(struct char_data * ch); +static bool change_has_emote_tokens(const char *text) +{ + for (; text && *text; text++) { + switch (*text) { + case '~': case '!': case '%': case '^': + case '#': case '&': case '=': case '+': + case '@': + return TRUE; + } + } + return FALSE; +} + +static bool change_ends_with_punct(const char *text) +{ + size_t len = text ? strlen(text) : 0; + if (len == 0) + return FALSE; + return (text[len - 1] == '.' || text[len - 1] == '!' || text[len - 1] == '?'); +} + +static void change_trim_trailing_spaces(char *text) +{ + size_t len = text ? strlen(text) : 0; + while (len > 0 && isspace((unsigned char)text[len - 1])) { + text[len - 1] = '\0'; + len--; + } +} + ACMD(do_quit) { char first[MAX_INPUT_LENGTH]; @@ -182,6 +212,69 @@ ACMD(do_save) Crash_crashsave(ch); } +ACMD(do_change) +{ + char option[MAX_INPUT_LENGTH]; + char suffix[MAX_INPUT_LENGTH]; + char base_buf[MAX_INPUT_LENGTH]; + char ldesc[MAX_STRING_LENGTH]; + char *rest = argument; + const char *base; + + rest = one_argument(rest, option); + if (!*option) { + send_to_char(ch, "Usage: change ldesc \r\n"); + return; + } + + if (!is_abbrev(option, "ldesc")) { + send_to_char(ch, "Unknown change option. Available: ldesc\r\n"); + return; + } + + skip_spaces(&rest); + if (!*rest) { + send_to_char(ch, "Usage: change ldesc \r\n"); + return; + } + + if (change_has_emote_tokens(rest)) { + send_to_char(ch, "You can't use emote tokens in your ldesc.\r\n"); + return; + } + + strlcpy(suffix, rest, sizeof(suffix)); + change_trim_trailing_spaces(suffix); + if (!*suffix) { + send_to_char(ch, "Usage: change ldesc \r\n"); + return; + } + + if (!change_ends_with_punct(suffix)) + strlcat(suffix, ".", sizeof(suffix)); + + base = (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? GET_SHORT_DESC(ch) : GET_NAME(ch); + if (!base || !*base) + base = "someone"; + + strlcpy(base_buf, base, sizeof(base_buf)); + if (*base_buf) + base_buf[0] = UPPER(*base_buf); + + snprintf(ldesc, sizeof(ldesc), "%s %s\r\n", base_buf, suffix); + + if (ch->player.long_descr) { + if (!IS_NPC(ch) || GET_MOB_RNUM(ch) == NOBODY || + ch->player.long_descr != mob_proto[GET_MOB_RNUM(ch)].player.long_descr) { + free(ch->player.long_descr); + } + } + ch->player.long_descr = strdup(ldesc); + ch->char_specials.custom_ldesc = TRUE; + + send_to_char(ch, "Long description updated.\r\n"); +} + /* Generic function for commands which are normally overridden by special * procedures - i.e., shop commands, mail commands, etc. */ ACMD(do_not_here) @@ -886,7 +979,11 @@ bool perform_scan_sweep(struct char_data *ch) continue; if (total >= scan_target_dc(tch)) { - send_to_char(ch, "A shadowy figure.\r\n"); + char hidden_ldesc[MAX_STRING_LENGTH]; + if (build_hidden_ldesc(tch, hidden_ldesc, sizeof(hidden_ldesc))) + send_to_char(ch, "%s", hidden_ldesc); + else + send_to_char(ch, "A shadowy figure.\r\n"); remember_scan_target(ch, tch); found_any = TRUE; } else { diff --git a/src/interpreter.c b/src/interpreter.c index 51fa507..0748ed4 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -118,6 +118,7 @@ cpp_extern const struct command_info cmd_info[] = { { "cast" , "c" , POS_SITTING , do_cast , 1, 0 }, { "cedit" , "cedit" , POS_DEAD , do_oasis_cedit, LVL_IMPL, 0 }, + { "change" , "chang" , POS_RESTING , do_change , 0, 0 }, { "changelog", "cha" , POS_DEAD , do_changelog, LVL_IMPL, 0 }, { "check" , "ch" , POS_STANDING, do_not_here , 1, 0 }, { "checkload", "checkl" , POS_DEAD , do_checkloadstatus, LVL_GOD, 0 }, @@ -483,8 +484,6 @@ void command_interpreter(struct char_data *ch, char *argument) char *line; char arg[MAX_INPUT_LENGTH]; - REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_HIDE); - /* just drop to next line for hitting CR */ skip_spaces(&argument); if (!*argument) @@ -500,6 +499,9 @@ void command_interpreter(struct char_data *ch, char *argument) } else line = any_one_arg(argument, arg); + if (!is_abbrev(arg, "change")) + REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_HIDE); + /* Since all command triggers check for valid_dg_target before acting, the levelcheck * here has been removed. Otherwise, find the command. */ { diff --git a/src/structs.h b/src/structs.h index 95ca614..febbccf 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1021,6 +1021,7 @@ struct char_special_data byte carry_items; /**< Number of items carried */ int timer; /**< Timer for update */ int stealth_check; /* last rolled Stealth value for Hide; 0 = not hiding/opposed */ + bool custom_ldesc; /* temporary ldesc override from change command */ struct char_special_data_saved saved; /**< Constants saved for PCs. */ }; diff --git a/src/utils.c b/src/utils.c index 1751511..43aaeb1 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1700,6 +1700,74 @@ const char *get_char_sdesc(const struct char_data *ch) return "someone"; } +void clear_custom_ldesc(struct char_data *ch) +{ + char base_buf[MAX_INPUT_LENGTH]; + char ldesc[MAX_STRING_LENGTH]; + const char *base; + + if (!ch || !ch->char_specials.custom_ldesc) + return; + + ch->char_specials.custom_ldesc = FALSE; + + if (ch->player.long_descr) { + if (!IS_NPC(ch) || GET_MOB_RNUM(ch) == NOBODY || + ch->player.long_descr != mob_proto[GET_MOB_RNUM(ch)].player.long_descr) { + free(ch->player.long_descr); + } + ch->player.long_descr = NULL; + } + + base = (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? GET_SHORT_DESC(ch) : GET_NAME(ch); + if (!base || !*base) + base = "someone"; + + strlcpy(base_buf, base, sizeof(base_buf)); + if (*base_buf) + base_buf[0] = UPPER(*base_buf); + + snprintf(ldesc, sizeof(ldesc), "%s is standing here.\r\n", base_buf); + ch->player.long_descr = strdup(ldesc); +} + +bool build_hidden_ldesc(const struct char_data *ch, char *out, size_t outsz) +{ + char base_buf[MAX_INPUT_LENGTH]; + const char *base; + size_t base_len; + const char *suffix; + + if (!out || outsz == 0) return FALSE; + *out = '\0'; + + if (!ch || !ch->char_specials.custom_ldesc || !ch->player.long_descr) + return FALSE; + if (GET_POS(ch) != GET_DEFAULT_POS(ch)) + return FALSE; + + base = (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? GET_SHORT_DESC(ch) : GET_NAME(ch); + if (!base || !*base) + base = "someone"; + + strlcpy(base_buf, base, sizeof(base_buf)); + if (*base_buf) + base_buf[0] = UPPER(*base_buf); + + base_len = strlen(base_buf); + if (strncmp(ch->player.long_descr, base_buf, base_len) != 0) + return FALSE; + + suffix = ch->player.long_descr + base_len; + if (*suffix == ' ') + suffix++; + else + return FALSE; + + snprintf(out, outsz, "A shadowy figure %s", suffix); + return TRUE; +} + /* 5e system helpers */ extern const struct armor_slot armor_slots[]; /* in constants.c */ diff --git a/src/utils.h b/src/utils.h index ecfa653..a674a6d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -80,6 +80,8 @@ char *right_trim_whitespace(const char *string); void remove_from_string(char *string, const char *to_remove); const char *const *obj_value_labels(int item_type); const char *get_char_sdesc(const struct char_data *ch); +void clear_custom_ldesc(struct char_data *ch); +bool build_hidden_ldesc(const struct char_data *ch, char *out, size_t outsz); int obj_is_storage(const struct obj_data *obj); int obj_storage_is_closed(const struct obj_data *obj); int roll_skill_check(struct char_data *ch, int skillnum, int mode, int *out_d20);