From 4ea7abca56e1201e23aac17746a0a3ccf8640c77 Mon Sep 17 00:00:00 2001 From: kinther Date: Wed, 17 Dec 2025 12:23:20 -0800 Subject: [PATCH] WORN items update --- lib/world/obj/1.obj | 18 ++++- src/act.h | 6 +- src/act.informative.c | 36 +++++++-- src/act.item.c | 165 +++++++++++++++++++++++++++++++++++------- src/act.movement.c | 42 ++++++++++- src/interpreter.c | 4 +- src/oedit.c | 34 +++++++++ src/structs.h | 12 ++- src/utils.c | 66 +++++++++++++++++ src/utils.h | 2 + 10 files changed, 344 insertions(+), 41 deletions(-) diff --git a/lib/world/obj/1.obj b/lib/world/obj/1.obj index 57c074a..ae616a4 100644 --- a/lib/world/obj/1.obj +++ b/lib/world/obj/1.obj @@ -175,7 +175,7 @@ A small bag made of cloth lies here.~ 15 0 0 0 1 25 0 0 0 #122 -large bag~ +bag large~ a large bag~ A large bag made of cloth is lying here.~ ~ @@ -563,7 +563,7 @@ to form a receptacle that can hold water. ~ 17 0 0 0 0 ap 0 0 0 0 0 0 0 4 4 0 0 -1 40 0 0 0 +9 40 0 0 0 #161 waterpouch pouch leather~ a leather waterpouch~ @@ -574,5 +574,17 @@ prevent water spilling out. ~ 17 0 0 0 0 ap 0 0 0 0 0 0 0 4 4 0 0 -1 40 0 0 0 +9 40 0 0 0 +#162 +cloak dark hooded~ +a dark, hooded cloak~ +A piece of dark fabric lays here in a heap.~ + This piece of fabric is made of linen and has been dyed a dark, blackish-blue +color. It has a few small pockets sewn inside and a clasp near the neck to +close it. A large hood has been attached, allowing the wearer to partially +cover their face. +~ +11 0 0 0 0 al 0 0 0 0 0 0 0 +1 1 0 10 +1 10 0 0 0 $~ diff --git a/src/act.h b/src/act.h index ae94b03..079b3c0 100644 --- a/src/act.h +++ b/src/act.h @@ -144,6 +144,11 @@ ACMD(do_eat); ACMD(do_pour); #define SCMD_POUR 0 #define SCMD_FILL 1 +/* do_raise_lower_hood */ +ACMD(do_raise_lower_hood); +#define SCMD_RAISE_HOOD 0 +#define SCMD_LOWER_HOOD 1 + /* functions without subcommands */ ACMD(do_drink); ACMD(do_get); @@ -154,7 +159,6 @@ ACMD(do_remove); ACMD(do_wear); ACMD(do_wield); - /***************************************************************************** * Begin Functions and defines for act.movement.c ****************************************************************************/ diff --git a/src/act.informative.c b/src/act.informative.c index 0949f5a..a633197 100644 --- a/src/act.informative.c +++ b/src/act.informative.c @@ -803,12 +803,14 @@ static void look_in_obj(struct char_data *ch, char *arg) FIND_OBJ_EQUIP, ch, &dummy, &obj))) { send_to_char(ch, "There doesn't seem to be %s %s here.\r\n", AN(arg), arg); } else if ((GET_OBJ_TYPE(obj) != ITEM_DRINKCON) && - (GET_OBJ_TYPE(obj) != ITEM_FOUNTAIN) && - (GET_OBJ_TYPE(obj) != ITEM_CONTAINER)) + (GET_OBJ_TYPE(obj) != ITEM_FOUNTAIN) && + !obj_is_storage(obj)) { send_to_char(ch, "There's nothing inside that!\r\n"); - else { - if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER) { - if (OBJVAL_FLAGGED(obj, CONT_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) + } else { + /* Storage-like objects: containers + storage-capable worn items */ + if (obj_is_storage(obj) && GET_OBJ_TYPE(obj) != ITEM_DRINKCON && GET_OBJ_TYPE(obj) != ITEM_FOUNTAIN) { + if (obj_storage_is_closed(obj) && + (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) send_to_char(ch, "It is closed.\r\n"); else { /* Choose a label for the container: @@ -817,11 +819,31 @@ static void look_in_obj(struct char_data *ch, char *arg) * - Otherwise, fall back to the first keyword (fname(obj->name)) */ const char *label; + const char *sd; - if (GET_OBJ_VAL(obj, 3) == 1 && obj->short_description && *obj->short_description) + /* Containers: keep corpse behavior exactly as-is */ + if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER && + GET_OBJ_VAL(obj, 3) == 1 && + obj->short_description && *obj->short_description) { label = obj->short_description; - else + } + /* WORN storage: use short_description, strip leading article */ + else if (GET_OBJ_TYPE(obj) == ITEM_WORN && + obj->short_description && *obj->short_description) { + + sd = obj->short_description; + + if (!strn_cmp(sd, "a ", 2)) + label = sd + 2; + else if (!strn_cmp(sd, "an ", 3)) + label = sd + 3; + else + label = sd; + } + /* Fallback */ + else { label = fname(obj->name); + } send_to_char(ch, "%s", label); diff --git a/src/act.item.c b/src/act.item.c index c1d9da4..fd24ddf 100644 --- a/src/act.item.c +++ b/src/act.item.c @@ -50,6 +50,7 @@ static void wear_message(struct char_data *ch, struct obj_data *obj, int where); static void perform_put(struct char_data *ch, struct obj_data *obj, struct obj_data *cont) { long object_id = obj_script_id(obj); + int cap = 0; if (!drop_otrigger(obj, ch)) return; @@ -57,8 +58,26 @@ static void perform_put(struct char_data *ch, struct obj_data *obj, struct obj_d if (!has_obj_by_uid_in_lookup_table(object_id)) /* object might be extracted by drop_otrigger */ return; - if ((GET_OBJ_VAL(cont, 0) > 0) && - (GET_OBJ_WEIGHT(cont) + GET_OBJ_WEIGHT(obj) > GET_OBJ_VAL(cont, 0))) + /* --- Storage target validation (containers + storage WORN) --- */ + if (!obj_is_storage(cont)) { + act("$P is not a container.", FALSE, ch, obj, cont, TO_CHAR); + return; + } + + /* Prevent putting items into closed storage (matches container UX). */ + if (obj_storage_is_closed(cont) && + (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) { + act("$P seems to be closed.", FALSE, ch, obj, cont, TO_CHAR); + return; + } + + /* Capacity: containers use value[0]; worn storage uses WORN_CAPACITY. */ + if (GET_OBJ_TYPE(cont) == ITEM_WORN) + cap = GET_OBJ_VAL(cont, WORN_CAPACITY); + else + cap = GET_OBJ_VAL(cont, 0); + + if ((cap > 0) && (GET_OBJ_WEIGHT(cont) + GET_OBJ_WEIGHT(obj) > cap)) act("$p won't fit in $P.", FALSE, ch, obj, cont, TO_CHAR); else if (OBJ_FLAGGED(obj, ITEM_NODROP) && IN_ROOM(cont) != NOWHERE) act("You can't get $p out of your hand.", FALSE, ch, obj, NULL, TO_CHAR); @@ -142,12 +161,13 @@ ACMD(do_put) generic_find(thecont, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &tmp_char, &cont); if (!cont) send_to_char(ch, "You don't see %s %s here.\r\n", AN(thecont), thecont); - else if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER) { - /* Handle container logic */ - if (OBJVAL_FLAGGED(cont, CONT_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) + else if (obj_is_storage(cont)) { + /* Handle container-like logic (containers + storage WORN) */ + if (obj_storage_is_closed(cont) && + (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) { send_to_char(ch, "You'd better open it first!\r\n"); - else { - if (obj_dotmode == FIND_INDIV) { /* put */ + } else { + if (obj_dotmode == FIND_INDIV) { /* put */ if (!(obj = get_obj_in_list_vis(ch, theobj, NULL, ch->carrying))) send_to_char(ch, "You aren't carrying %s %s.\r\n", AN(theobj), theobj); else if (obj == cont && howmany == 1) @@ -157,7 +177,7 @@ ACMD(do_put) next_obj = obj->next_content; if (obj != cont) { howmany--; - perform_put(ch, obj, cont); + perform_put(ch, obj, cont); /* must be updated to accept storage WORN */ } obj = get_obj_in_list_vis(ch, theobj, NULL, next_obj); } @@ -168,13 +188,13 @@ ACMD(do_put) if (obj != cont && CAN_SEE_OBJ(ch, obj) && (obj_dotmode == FIND_ALL || isname(theobj, obj->name))) { found = 1; - perform_put(ch, obj, cont); + perform_put(ch, obj, cont); /* must be updated to accept storage WORN */ } } if (!found) - send_to_char(ch, "You don't seem to have %s %s.\r\n", - obj_dotmode == FIND_ALL ? "any" : "any", - obj_dotmode == FIND_ALL ? "items" : theobj); + send_to_char(ch, "You don't seem to have %s %s.\r\n", + obj_dotmode == FIND_ALL ? "any" : "any", + obj_dotmode == FIND_ALL ? "items" : theobj); } } } else if (GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { @@ -273,14 +293,21 @@ static void perform_get_from_container(struct char_data *ch, struct obj_data *ob } void get_from_container(struct char_data *ch, struct obj_data *cont, - char *arg, int mode, int howmany) + char *arg, int mode, int howmany) { struct obj_data *obj, *next_obj; int obj_dotmode, found = 0; obj_dotmode = find_all_dots(arg); - if (OBJVAL_FLAGGED(cont, CONT_CLOSED) && (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) + /* Allow both ITEM_CONTAINER and storage-capable ITEM_WORN */ + if (!obj_is_storage(cont)) { + act("$p is not a container.", FALSE, ch, cont, 0, TO_CHAR); + return; + } + + if (obj_storage_is_closed(cont) && + (GET_LEVEL(ch) < LVL_IMMORT || !PRF_FLAGGED(ch, PRF_NOHASSLE))) act("$p is closed.", FALSE, ch, cont, 0, TO_CHAR); else if (obj_dotmode == FIND_INDIV) { if (!(obj = get_obj_in_list_vis(ch, arg, NULL, cont->contains))) { @@ -304,19 +331,19 @@ void get_from_container(struct char_data *ch, struct obj_data *cont, for (obj = cont->contains; obj; obj = next_obj) { next_obj = obj->next_content; if (CAN_SEE_OBJ(ch, obj) && - (obj_dotmode == FIND_ALL || isname(arg, obj->name))) { - found = 1; - perform_get_from_container(ch, obj, cont, mode); + (obj_dotmode == FIND_ALL || isname(arg, obj->name))) { + found = 1; + perform_get_from_container(ch, obj, cont, mode); } } if (!found) { if (obj_dotmode == FIND_ALL) - act("$p seems to be empty.", FALSE, ch, cont, 0, TO_CHAR); + act("$p seems to be empty.", FALSE, ch, cont, 0, TO_CHAR); else { char buf[MAX_STRING_LENGTH]; - snprintf(buf, sizeof(buf), "You can't seem to find any %ss in $p.", arg); - act(buf, FALSE, ch, cont, 0, TO_CHAR); + snprintf(buf, sizeof(buf), "You can't seem to find any %ss in $p.", arg); + act(buf, FALSE, ch, cont, 0, TO_CHAR); } } } @@ -409,11 +436,11 @@ ACMD(do_get) if (cont_dotmode == FIND_INDIV) { mode = generic_find(arg2, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &tmp_char, &cont); if (!cont) - send_to_char(ch, "You don't have %s %s.\r\n", AN(arg2), arg2); - else if (GET_OBJ_TYPE(cont) != ITEM_CONTAINER && GET_OBJ_TYPE(cont) != ITEM_FURNITURE) - act("$p is not a container or furniture.", FALSE, ch, cont, 0, TO_CHAR); + send_to_char(ch, "You don't have %s %s.\r\n", AN(arg2), arg2); + else if (!obj_is_storage(cont) && GET_OBJ_TYPE(cont) != ITEM_FURNITURE) + act("$p is not a container or furniture.", FALSE, ch, cont, 0, TO_CHAR); else - get_from_container(ch, cont, arg1, mode, amount); + get_from_container(ch, cont, arg1, mode, amount); /* must be updated */ } else { if (cont_dotmode == FIND_ALLDOT && !*arg2) { send_to_char(ch, "Get from all of what?\r\n"); @@ -421,7 +448,7 @@ ACMD(do_get) } for (cont = ch->carrying; cont; cont = cont->next_content) if (CAN_SEE_OBJ(ch, cont) && (cont_dotmode == FIND_ALL || isname(arg2, cont->name))) { - if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER || GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { + if (obj_is_storage(cont) || GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { found = 1; get_from_container(ch, cont, arg1, FIND_OBJ_INV, amount); } else if (cont_dotmode == FIND_ALLDOT) { @@ -452,7 +479,7 @@ ACMD(do_get) for (cont = world[IN_ROOM(ch)].contents; cont; cont = cont->next_content) if (CAN_SEE_OBJ(ch, cont) && (cont_dotmode == FIND_ALL || isname(arg2, cont->name))) { - if (GET_OBJ_TYPE(cont) == ITEM_CONTAINER || GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { + if (obj_is_storage(cont) || GET_OBJ_TYPE(cont) == ITEM_FURNITURE) { get_from_container(ch, cont, arg1, FIND_OBJ_ROOM, amount); found = 1; } else if (cont_dotmode == FIND_ALLDOT) { @@ -1814,3 +1841,89 @@ ACMD(do_remove) perform_remove(ch, i); } } + +ACMD(do_raise_lower_hood) +{ + char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH]; + struct obj_data *obj = NULL; + int j; + + two_arguments(argument, arg1, arg2); + + /* Must be exactly: "raise hood" or "lower hood" */ + if (!*arg1 || str_cmp(arg1, "hood")) { + send_to_char(ch, "Usage: %s hood\r\n", (subcmd == SCMD_RAISE_HOOD) ? "raise" : "lower"); + return; + } + + /* Find a hooded worn item in equipment. Prefer: + - for lower: one that is currently up + - for raise: one that is currently down + */ + if (subcmd == SCMD_LOWER_HOOD) { + for (j = 0; j < NUM_WEARS; j++) { + obj = GET_EQ(ch, j); + if (!obj) continue; + if (GET_OBJ_TYPE(obj) != ITEM_WORN) continue; + if (GET_OBJ_VAL(obj, WORN_CAN_HOOD) != 1) continue; + if (IS_SET_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP)) + break; + obj = NULL; + } + } + + if (!obj && subcmd == SCMD_RAISE_HOOD) { + for (j = 0; j < NUM_WEARS; j++) { + obj = GET_EQ(ch, j); + if (!obj) continue; + if (GET_OBJ_TYPE(obj) != ITEM_WORN) continue; + if (GET_OBJ_VAL(obj, WORN_CAN_HOOD) != 1) continue; + if (!IS_SET_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP)) + break; + obj = NULL; + } + } + + /* If we didn’t find an ideal target, fall back to any hooded worn item. */ + if (!obj) { + for (j = 0; j < NUM_WEARS; j++) { + obj = GET_EQ(ch, j); + if (!obj) continue; + if (GET_OBJ_TYPE(obj) != ITEM_WORN) continue; + if (GET_OBJ_VAL(obj, WORN_CAN_HOOD) != 1) continue; + break; + } + if (j >= NUM_WEARS) obj = NULL; + } + + if (!obj) { + send_to_char(ch, "You aren't wearing anything with a hood.\r\n"); + return; + } + + if (subcmd == SCMD_RAISE_HOOD) { + if (GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) == 1) { + send_to_char(ch, "Your hood is already up.\r\n"); + return; + } + + GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) = 1; + SET_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP); /* optional mirror */ + + send_to_char(ch, "You raise your hood.\r\n"); + act("$n raises $s hood.", FALSE, ch, 0, 0, TO_ROOM); + return; + } + + /* SCMD_LOWER_HOOD */ + if (GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) == 0) { + send_to_char(ch, "Your hood is already down.\r\n"); + return; + } + + GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) = 0; + REMOVE_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_HOOD_UP); /* optional mirror */ + + send_to_char(ch, "You lower your hood.\r\n"); + act("$n lowers $s hood.", FALSE, ch, 0, 0, TO_ROOM); +} diff --git a/src/act.movement.c b/src/act.movement.c index f228d51..08f8743 100644 --- a/src/act.movement.c +++ b/src/act.movement.c @@ -785,6 +785,32 @@ static int ok_pick(struct char_data *ch, obj_vnum keynum, int pickproof, int scm #define DOOR_KEY(ch, obj, door) ((obj) ? (GET_OBJ_VAL(obj, 2)) : \ (EXIT(ch, door)->key)) +/* For worn items like backpacks, cloaks, etc */ +static void do_worn_openclose(struct char_data *ch, struct obj_data *obj, int subcmd) +{ + if (subcmd == SCMD_OPEN) { + if (GET_OBJ_VAL(obj, WORN_IS_CLOSED) == 0) { + send_to_char(ch, "But it's currently open!\r\n"); + return; + } + GET_OBJ_VAL(obj, WORN_IS_CLOSED) = 0; + send_to_char(ch, "%s", CONFIG_OK); + act("$n opens $p.", FALSE, ch, obj, 0, TO_ROOM); + return; + } + + if (subcmd == SCMD_CLOSE) { + if (GET_OBJ_VAL(obj, WORN_IS_CLOSED) == 1) { + send_to_char(ch, "But it's already closed!\r\n"); + return; + } + GET_OBJ_VAL(obj, WORN_IS_CLOSED) = 1; + send_to_char(ch, "%s", CONFIG_OK); + act("$n closes $p.", FALSE, ch, obj, 0, TO_ROOM); + return; + } +} + ACMD(do_gen_door) { int door = -1; @@ -799,9 +825,23 @@ ACMD(do_gen_door) return; } two_arguments(argument, type, dir); - if (!generic_find(type, FIND_OBJ_INV | FIND_OBJ_ROOM, ch, &victim, &obj)) + if (!generic_find(type, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &victim, &obj)) door = find_door(ch, type, dir, cmd_door[subcmd]); + if (obj && GET_OBJ_TYPE(obj) == ITEM_WORN && + GET_OBJ_VAL(obj, WORN_CAN_OPEN_CLOSE) == 1) { + + if (subcmd == SCMD_OPEN || subcmd == SCMD_CLOSE) { + do_worn_openclose(ch, obj, subcmd); + return; + } + + /* lock/unlock/pick on closable worn items: treat as not applicable */ + /* fall through to door search by discarding obj */ + obj = NULL; + door = find_door(ch, type, dir, cmd_door[subcmd]); + } + if ((obj) && (GET_OBJ_TYPE(obj) != ITEM_CONTAINER)) { obj = NULL; door = find_door(ch, type, dir, cmd_door[subcmd]); diff --git a/src/interpreter.c b/src/interpreter.c index 4bb70db..a6be498 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -187,6 +187,7 @@ cpp_extern const struct command_info cmd_info[] = { { "kick" , "ki" , POS_FIGHTING, do_kick , 1, 0 }, { "look" , "l" , POS_RESTING , do_look , 0, SCMD_LOOK }, + { "lower" , "low" , POS_SITTING , do_raise_lower_hood, 0, SCMD_LOWER_HOOD }, { "last" , "last" , POS_DEAD , do_last , LVL_GOD, 0 }, { "leave" , "lea" , POS_STANDING, do_leave , 0, 0 }, { "list" , "lis" , POS_STANDING, do_not_here , 0, 0 }, @@ -246,6 +247,7 @@ cpp_extern const struct command_info cmd_info[] = { { "qui" , "qui" , POS_DEAD , do_quit , 0, 0 }, { "quit" , "quit" , POS_DEAD , do_quit , 0, SCMD_QUIT }, + { "raise" , "rai" , POS_SITTING , do_raise_lower_hood, 0, SCMD_RAISE_HOOD }, { "reply" , "r" , POS_SLEEPING, do_reply , LVL_IMMORT, 0 }, { "rest" , "res" , POS_RESTING , do_rest , 0, 0 }, { "read" , "rea" , POS_RESTING , do_look , 0, SCMD_READ }, @@ -267,7 +269,7 @@ cpp_extern const struct command_info cmd_info[] = { { "say" , "s" , POS_RESTING , do_say , 0, 0 }, { "score" , "sc" , POS_DEAD , do_score , 0, 0 }, -{ "scan" , "sca" , POS_RESTING , do_scan , 0, 0 }, + { "scan" , "sca" , POS_RESTING , do_scan , 0, 0 }, { "scopy" , "scopy" , POS_DEAD , do_oasis_copy, LVL_GOD, CON_SEDIT }, { "sit" , "si" , POS_RESTING , do_sit , 0, 0 }, { "'" , "'" , POS_RESTING , do_say , 0, 0 }, diff --git a/src/oedit.c b/src/oedit.c index 1b17c56..897979b 100644 --- a/src/oedit.c +++ b/src/oedit.c @@ -992,6 +992,40 @@ void oedit_parse(struct descriptor_data *d, char *arg) } } + /* --- Worn-specific semantics (clothing, rings, etc) --- */ + if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_WORN) { + + /* oval 1: closable (0/1) */ + if (i == WORN_CAN_OPEN_CLOSE) { + if (number != 0 && number != 1) { + write_to_output(d, "Enter 0 or 1 to set closable: "); + return; /* stay in OEDIT_VALUE_X */ + } + GET_OBJ_VAL(OLC_OBJ(d), i) = number; + OLC_DIRTY(d) = 1; + oedit_disp_values_menu(d); + return; + } + + /* oval 2: hooded (0/1) */ + if (i == WORN_CAN_HOOD) { + if (number != 0 && number != 1) { + write_to_output(d, "Enter 0 or 1 to set hooded: "); + return; /* stay in OEDIT_VALUE_X */ + } + GET_OBJ_VAL(OLC_OBJ(d), i) = number; + + /* If it can no longer have a hood, force hood state down. */ + if (number == 0) { + REMOVE_BIT_AR(GET_OBJ_EXTRA(OLC_OBJ(d)), ITEM_HOOD_UP); + } + + OLC_DIRTY(d) = 1; + oedit_disp_values_menu(d); + return; + } + } + /* --- Existing special cases (weapon/liquid/spells/container) remain here --- */ if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_WEAPON && i == 2) { if (number < 0 || number >= NUM_ATTACK_TYPES) { diff --git a/src/structs.h b/src/structs.h index a0c14c0..c1a98e3 100644 --- a/src/structs.h +++ b/src/structs.h @@ -444,8 +444,9 @@ #define ITEM_ANTI_DRUID 19 /**< Not usable by druids */ #define ITEM_NOSELL 20 /**< Shopkeepers won't touch it */ #define ITEM_QUEST 21 /**< Item is a quest item */ +#define ITEM_HOOD_UP 22 /**< WORN item hood is currently up */ /** Total number of item flags */ -#define NUM_ITEM_FLAGS 22 +#define NUM_ITEM_FLAGS 23 /* Modifier constants used with obj affects ('A' fields) */ #define APPLY_NONE 0 /**< No effect */ @@ -475,7 +476,7 @@ #define APPLY_SAVE_CHA 24 /**< Apply to CHA saving throws */ /** Total number of applies */ -#define NUM_APPLIES 25 +#define NUM_APPLIES 25 /* Equals the total number of SAVING_* defines in spells.h */ #define NUM_OF_SAVING_THROWS 5 @@ -486,6 +487,13 @@ #define CONT_CLOSED (1 << 2) /**< Container is closed */ #define CONT_LOCKED (1 << 3) /**< Container is locked */ +/* ITEM_WORN value[] layout */ +#define WORN_CAN_OPEN_CLOSE 0 /* 0/1: supports open/close */ +#define WORN_CAN_HOOD 1 /* 0/1: supports hood up/down */ +#define WORN_IS_CLOSED 2 /* runtime: 0/1 */ +#define WORN_CAPACITY 3 /* max weight/units it can hold; 0 = cannot hold */ +#define WORN_HOOD_UP_STATE 4 /* runtime 0/1, persisted */ + /* Some different kind of liquids for use in values of drink containers */ #define LIQ_WATER 0 /**< Liquid type water */ #define LIQ_BEER 1 /**< Liquid type beer */ diff --git a/src/utils.c b/src/utils.c index a73bb5a..2471e5c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -478,6 +478,11 @@ static const char *const furniture_val_labels[NUM_OBJ_VAL_POSITIONS] = { "Value[4]", "Value[5]", "Value[6]", "Value[7]" }; +static const char *const worn_val_labels[NUM_OBJ_VAL_POSITIONS] = { + "closable", "hooded", "is_closed", "capacity", + "hood_raised", "Value[5]", "Value[6]", "Value[7]" +}; + static const char *const generic_val_labels[NUM_OBJ_VAL_POSITIONS] = { "Value[0]", "Value[1]", "Value[2]", "Value[3]", "Value[4]", "Value[5]", "Value[6]", "Value[7]" @@ -494,6 +499,7 @@ const char *const *obj_value_labels(int item_type) case ITEM_WEAPON: return weapon_val_labels; case ITEM_ARMOR: return armor_val_labels; case ITEM_CONTAINER: return container_val_labels; + case ITEM_WORN: return worn_val_labels; case ITEM_DRINKCON: case ITEM_FOUNTAIN: return drink_val_labels; case ITEM_FOOD: return food_val_labels; @@ -1644,11 +1650,43 @@ void remove_from_string(char *string, const char *to_remove) } } +static struct obj_data *find_raised_hood_item(const struct char_data *ch) +{ + int j; + + if (!ch) + return NULL; + + for (j = 0; j < NUM_WEARS; j++) { + struct obj_data *obj = GET_EQ((struct char_data *)ch, j); /* GET_EQ not const-safe */ + if (!obj) + continue; + + if (GET_OBJ_TYPE(obj) == ITEM_WORN && + GET_OBJ_VAL(obj, WORN_CAN_HOOD) == 1 && + GET_OBJ_VAL(obj, WORN_HOOD_UP_STATE) == 1) { + return obj; + } + } + + return NULL; +} + const char *get_char_sdesc(const struct char_data *ch) { + static char buf[MAX_STRING_LENGTH]; + struct obj_data *hood; + if (!ch) return "someone"; + /* Hood override: temporary display only (does not mutate stored sdesc). */ + hood = find_raised_hood_item(ch); + if (hood && hood->short_description && *hood->short_description) { + snprintf(buf, sizeof(buf), "the figure in %s", hood->short_description); + return buf; + } + if (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) return GET_SHORT_DESC(ch); @@ -1953,3 +1991,31 @@ struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src) { } return head; } + +/* Worn item helpers */ +int obj_is_storage(const struct obj_data *obj) +{ + if (!obj) return 0; + + if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER) + return 1; + + if (GET_OBJ_TYPE(obj) == ITEM_WORN && GET_OBJ_VAL(obj, WORN_CAPACITY) > 0) + return 1; + + return 0; +} + +int obj_storage_is_closed(const struct obj_data *obj) +{ + if (!obj) return 0; + + if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER) + return IS_SET(GET_OBJ_VAL(obj, 1), CONT_CLOSED); + + if (GET_OBJ_TYPE(obj) == ITEM_WORN && + GET_OBJ_VAL(obj, WORN_CAN_OPEN_CLOSE) == 1) + return (GET_OBJ_VAL(obj, WORN_IS_CLOSED) == 1); + + return 0; +} diff --git a/src/utils.h b/src/utils.h index 0afd22f..18462b0 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); +int obj_is_storage(const struct obj_data *obj); +int obj_storage_is_closed(const struct obj_data *obj); /* 5e system helpers */