From 966145a61d167d8cac0624e3a5b42026f02af07a Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 20 Oct 2025 13:29:42 -0700 Subject: [PATCH] item action 'T' against covered armor Using 'i'+menu choice for suit+'T' to try to take off a suit that is covered by a cloak (or shirt covered by suit and/or cloak) wouldn't do anything. It should report that you need to take off the outer garment first and then not take the chosen item off. There is probably a simpler fix. It took me a long time to figure where things were going wrong and them cobble this together. A big chunk of the diff for invent.c is just identation, surrounding a one-line change there. --- include/decl.h | 1 + include/extern.h | 1 + src/cmd.c | 3 +- src/decl.c | 1 + src/do_wear.c | 41 ++++++-- src/invent.c | 266 +++++++++++++++++++++++------------------------ 6 files changed, 168 insertions(+), 145 deletions(-) diff --git a/include/decl.h b/include/decl.h index 72f85eb90..8487ae6fc 100644 --- a/include/decl.h +++ b/include/decl.h @@ -475,6 +475,7 @@ struct instance_globals_i { /* invent.c */ char *invbuf; unsigned invbufsiz; + boolean item_action_in_progress; int in_sync_perminvent; /* mon.c */ diff --git a/include/extern.h b/include/extern.h index 34d20129c..247cc8e15 100644 --- a/include/extern.h +++ b/include/extern.h @@ -746,6 +746,7 @@ extern void Ring_gone(struct obj *) NONNULLARG1; extern void Blindf_on(struct obj *) NONNULLARG1; extern void Blindf_off(struct obj *); extern int dotakeoff(void); +extern int ia_dotakeoff(void); extern int doremring(void); extern int cursed(struct obj *); extern int armoroff(struct obj *); diff --git a/src/cmd.c b/src/cmd.c index 3eb8a634e..cd82e234d 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2032,8 +2032,9 @@ struct ext_func_tab extcmdlist[] = { /* internal commands: only used by game core, not available for user */ { '\0', "clicklook", NULL, doclicklook, INTERNALCMD | MOUSECMD, NULL }, { '\0', "mouseaction", NULL, domouseaction, INTERNALCMD | MOUSECMD, NULL }, - { '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL }, { '\0', "altadjust", NULL, adjust_split, INTERNALCMD, NULL }, + { '\0', "altdip", NULL, dip_into, INTERNALCMD, NULL }, + { '\0', "alttakeoff", NULL, ia_dotakeoff, INTERNALCMD, NULL }, { '\0', "altunwield", NULL, remarm_swapwep, INTERNALCMD, NULL }, { '\0', (char *) 0, (char *) 0, donull, 0, (char *) 0 } /* sentinel */ }; diff --git a/src/decl.c b/src/decl.c index a7e52dd80..04f55eb99 100644 --- a/src/decl.c +++ b/src/decl.c @@ -413,6 +413,7 @@ static const struct instance_globals_i g_init_i = { /* invent.c */ NULL, /* invbuf */ 0U, /* invbufsize */ + FALSE, /* item_action_in_progress */ 0, /* in_sync_perminvent */ /* mon.c */ NULL, /* itermonarr */ diff --git a/src/do_wear.c b/src/do_wear.c index 058d903d7..2cb86fa6d 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1783,7 +1783,7 @@ armor_or_accessory_off(struct obj *obj) Strcat(what, " and "); Strcat(what, suit_simple_name(uarm)); } - Snprintf(why, sizeof(why), " without taking off your %s first", + Snprintf(why, sizeof why, " without taking off your %s first", what); } else { Strcpy(why, "; it's embedded"); @@ -1841,7 +1841,7 @@ dotakeoff(void) pline("Not wearing any armor or accessories."); return ECMD_OK; } - if (Narmorpieces != 1 || ParanoidRemove || cmdq_peek(CQ_CANNED)) + if (Narmorpieces != 1 || ParanoidRemove || gi.item_action_in_progress) otmp = getobj("take off", takeoff_ok, GETOBJ_NOFLAGS); if (!otmp) return ECMD_CANCEL; @@ -1849,6 +1849,21 @@ dotakeoff(void) return armor_or_accessory_off(otmp); } +/* 'i' or 'I[' followed by and then 'T'; + plain dotakeoff() would not give any feedback when picking suit + covered by cloak or shirt covered by suit and/or cloak due to the + default behavior of equip_ok() (skipping inaccessible items) */ +int +ia_dotakeoff(void) +{ + int res; + + gi.item_action_in_progress = TRUE; + res = dotakeoff(); + gi.item_action_in_progress = FALSE; + return res; +} + /* the #remove command - take off ring or other accessory */ int doremring(void) @@ -3256,14 +3271,14 @@ adj_abon(struct obj *otmp, schar delta) } /* decide whether a worn item is covered up by some other worn item, - used for dipping into liquid and applying grease; + used for dipping into liquid and applying grease and takeoff_ok(); some criteria are different than select_off()'s */ boolean -inaccessible_equipment(struct obj *obj, - const char *verb, /* "dip" or "grease", or null to - avoid messages */ - boolean only_if_known_cursed) /* ignore covering unless - known to be cursed */ +inaccessible_equipment( + struct obj *obj, + const char *verb, /* "dip" or "grease", or null to avoid messages */ + boolean only_if_known_cursed) /* ignore covering unless it is known to + * be cursed */ { static NEARDATA const char need_to_take_off_outer_armor[] = "need to take off %s to %s %s."; @@ -3315,6 +3330,8 @@ inaccessible_equipment(struct obj *obj, } /* item is not inaccessible */ return FALSE; + +#undef BLOCKSACCESS } /* not a getobj callback - unifies code among the other 4 getobj callbacks */ @@ -3354,9 +3371,11 @@ equip_ok(struct obj *obj, boolean removing, boolean accessory) * can't be worn because the slot is filled with something else. */ /* removing inaccessible equipment */ - if (removing && inaccessible_equipment(obj, (const char *) 0, - (obj->oclass == RING_CLASS))) - return GETOBJ_EXCLUDE_INACCESS; + if (removing && !gi.item_action_in_progress) { + if (inaccessible_equipment(obj, (const char *) 0, + (obj->oclass == RING_CLASS))) + return GETOBJ_EXCLUDE_INACCESS; + } /* all good to go */ return GETOBJ_SUGGEST; diff --git a/src/invent.c b/src/invent.c index 507cd1c49..913bc353f 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2980,142 +2980,142 @@ ia_addmenu(winid win, int act, char let, const char *txt) ATR_NONE, clr, txt, MENU_ITEMFLAGS_NONE); } +/* set up a command to execute on a specific item next */ staticfn void itemactions_pushkeys(struct obj *otmp, int act) { - switch (act) { - default: - impossible("Unknown item action"); - break; - case IA_NONE: - break; - case IA_UNWIELD: - cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield - : (otmp == uswapwep) ? remarm_swapwep - : (otmp == uquiver) ? dowieldquiver - : donull); /* can't happen */ - cmdq_add_key(CQ_CANNED, '-'); - break; - case IA_APPLY_OBJ: - cmdq_add_ec(CQ_CANNED, doapply); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DIP_OBJ: - /* #altdip instead of normal #dip - takes potion to dip into - first (the inventory item instigating this) and item to - be dipped second, also ignores floor features such as - fountain/sink so we don't need to force m-prefix here */ - cmdq_add_ec(CQ_CANNED, dip_into); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_NAME_OBJ: - case IA_NAME_OTYP: - cmdq_add_ec(CQ_CANNED, docallcmd); - cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_DROP_OBJ: - cmdq_add_ec(CQ_CANNED, dodrop); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_EAT_OBJ: - /* start with m-prefix; for #eat, it means ignore floor food - if present and eat food from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, doeat); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ENGRAVE_OBJ: - cmdq_add_ec(CQ_CANNED, doengrave); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_FIRE_OBJ: - cmdq_add_ec(CQ_CANNED, dofire); - break; - case IA_ADJUST_OBJ: - cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_ADJUST_STACK: - cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SACRIFICE: - cmdq_add_ec(CQ_CANNED, dosacrifice); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_BUY_OBJ: - cmdq_add_ec(CQ_CANNED, dopay); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUAFF_OBJ: - /* start with m-prefix; for #quaff, it means ignore fountain - or sink if present and drink a potion from invent */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dodrink); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_QUIVER_OBJ: - cmdq_add_ec(CQ_CANNED, dowieldquiver); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_READ_OBJ: - cmdq_add_ec(CQ_CANNED, doread); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_RUB_OBJ: - cmdq_add_ec(CQ_CANNED, dorub); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_THROW_OBJ: - cmdq_add_ec(CQ_CANNED, dothrow); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TAKEOFF_OBJ: - cmdq_add_ec(CQ_CANNED, dotakeoff); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_TIP_CONTAINER: - /* start with m-prefix to skip floor containers; - for menustyle:Traditional when more than one floor - container is present, player will get a #tip menu and - have to pick the "tip something being carried" choice, - then this item will be already chosen from inventory; - suboptimal but possibly an acceptable tradeoff since - combining item actions with use of traditional ggetobj() - is an unlikely scenario */ - cmdq_add_ec(CQ_CANNED, do_reqmenu); - cmdq_add_ec(CQ_CANNED, dotip); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_INVOKE_OBJ: - cmdq_add_ec(CQ_CANNED, doinvoke); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WIELD_OBJ: - cmdq_add_ec(CQ_CANNED, dowield); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WEAR_OBJ: - cmdq_add_ec(CQ_CANNED, dowear); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_SWAPWEAPON: - cmdq_add_ec(CQ_CANNED, doswapweapon); - break; - case IA_TWOWEAPON: - cmdq_add_ec(CQ_CANNED, dotwoweapon); - break; - case IA_ZAP_OBJ: - cmdq_add_ec(CQ_CANNED, dozap); - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - case IA_WHATIS_OBJ: - cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ - cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ - cmdq_add_key(CQ_CANNED, otmp->invlet); - break; - } + switch (act) { + default: + impossible("Unknown item action %d", act); + break; + case IA_NONE: + break; + case IA_UNWIELD: + cmdq_add_ec(CQ_CANNED, (otmp == uwep) ? dowield + : (otmp == uswapwep) ? remarm_swapwep + : (otmp == uquiver) ? dowieldquiver + : donull); /* can't happen */ + cmdq_add_key(CQ_CANNED, HANDS_SYM); + break; + case IA_APPLY_OBJ: + cmdq_add_ec(CQ_CANNED, doapply); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DIP_OBJ: + /* #altdip instead of normal #dip - takes potion to dip into + first (the inventory item instigating this) and item to + be dipped second, also ignores floor features such as + fountain/sink so we don't need to force m-prefix here */ + cmdq_add_ec(CQ_CANNED, dip_into); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_NAME_OBJ: + case IA_NAME_OTYP: + cmdq_add_ec(CQ_CANNED, docallcmd); + cmdq_add_key(CQ_CANNED, (act == IA_NAME_OBJ) ? 'i' : 'o'); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_DROP_OBJ: + cmdq_add_ec(CQ_CANNED, dodrop); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_EAT_OBJ: + /* start with m-prefix; for #eat, it means ignore floor food + if present and eat food from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, doeat); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ENGRAVE_OBJ: + cmdq_add_ec(CQ_CANNED, doengrave); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_FIRE_OBJ: + cmdq_add_ec(CQ_CANNED, dofire); + break; + case IA_ADJUST_OBJ: + cmdq_add_ec(CQ_CANNED, doorganize); /* #adjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_ADJUST_STACK: + cmdq_add_ec(CQ_CANNED, adjust_split); /* #altadjust */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SACRIFICE: + cmdq_add_ec(CQ_CANNED, dosacrifice); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_BUY_OBJ: + cmdq_add_ec(CQ_CANNED, dopay); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUAFF_OBJ: + /* start with m-prefix; for #quaff, it means ignore fountain + or sink if present and drink a potion from invent */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dodrink); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_QUIVER_OBJ: + cmdq_add_ec(CQ_CANNED, dowieldquiver); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_READ_OBJ: + cmdq_add_ec(CQ_CANNED, doread); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_RUB_OBJ: + cmdq_add_ec(CQ_CANNED, dorub); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_THROW_OBJ: + cmdq_add_ec(CQ_CANNED, dothrow); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TAKEOFF_OBJ: + cmdq_add_ec(CQ_CANNED, ia_dotakeoff); /* #altdotakeoff */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_TIP_CONTAINER: + /* start with m-prefix to skip floor containers; + for menustyle:Traditional when more than one floor container + is present, player will get a #tip menu and have to pick + the "tip something being carried" choice, then this item + will be already chosen from inventory; suboptimal but + possibly an acceptable tradeoff since combining item actions + with use of traditional ggetobj() is an unlikely scenario */ + cmdq_add_ec(CQ_CANNED, do_reqmenu); + cmdq_add_ec(CQ_CANNED, dotip); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_INVOKE_OBJ: + cmdq_add_ec(CQ_CANNED, doinvoke); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WIELD_OBJ: + cmdq_add_ec(CQ_CANNED, dowield); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WEAR_OBJ: + cmdq_add_ec(CQ_CANNED, dowear); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_SWAPWEAPON: + cmdq_add_ec(CQ_CANNED, doswapweapon); + break; + case IA_TWOWEAPON: + cmdq_add_ec(CQ_CANNED, dotwoweapon); + break; + case IA_ZAP_OBJ: + cmdq_add_ec(CQ_CANNED, dozap); + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + case IA_WHATIS_OBJ: + cmdq_add_ec(CQ_CANNED, dowhatis); /* "/" command */ + cmdq_add_key(CQ_CANNED, 'i'); /* "i" == item from inventory */ + cmdq_add_key(CQ_CANNED, otmp->invlet); + break; + } } /* Show menu of possible actions hero could do with item otmp */