From 619de1ec0e8f40d33a6a61b5ada14eb69a04a317 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 16 Mar 2026 11:20:34 +0200 Subject: [PATCH] Make monster destroy armor -spell erode armor first Instead of outright destroying the armor, the spell will instead first erode the armor. The spell hits 2-4 times, so if it hits the same armor 4 times, it will get destroyed. This does not hit erodeproof armor. Also change the scroll of destroy armor, so that blessed one will destroy a cursed armor, if hero is only wearing that. --- doc/fixes3-7-0.txt | 5 +++- include/extern.h | 3 ++- include/obj.h | 1 + src/do_wear.c | 65 ++++++++++++++++++++++++++++++++++++++++++++-- src/mcastu.c | 2 +- src/pray.c | 8 +++--- src/read.c | 58 +++++++++++++++++++++++++++++++++-------- src/zap.c | 10 +++---- 8 files changed, 127 insertions(+), 25 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 54dcaa9fb..be8ca2259 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2890,12 +2890,15 @@ enlightenment/attribute disclosure for saving-grace: include a line for have tourists gain experience by "taking photos" of new creatures, and going to new dungeon levels healers gain experience by healing pets -blessed scroll of destroy armor asks which armor to destroy +blessed scroll of destroy armor asks which armor to destroy if wearing more + than one armor; otherwise it'll destroy a cursed armor, or erodes + random armor archeologists' fedora is lucky telepathic hero can discern which particular monster just read a scroll add "make fetch-docs" to download pre-formatted documentation monsters will use throw-and-return weapons such as an aklys archeologists can identify scrolls by deciphering their labels +monster destroy armor -spell first erodes armor Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 2593f9362..0af1dfda7 100644 --- a/include/extern.h +++ b/include/extern.h @@ -766,7 +766,8 @@ extern struct obj *unchanger(void); extern void reset_remarm(void); extern int doddoremarm(void); extern int remarm_swapwep(void); -extern int destroy_arm(struct obj *); +extern int disintegrate_arm(struct obj *); +extern int destroy_arm(void); extern void adj_abon(struct obj *, schar) NONNULLARG1; extern boolean inaccessible_equipment(struct obj *, const char *, boolean); extern int any_worn_armor_ok(struct obj *); diff --git a/include/obj.h b/include/obj.h index 431094918..d9ef83d21 100644 --- a/include/obj.h +++ b/include/obj.h @@ -451,6 +451,7 @@ struct obj { #define BURIED_TOO 0x2 /* object erosion types */ +#define ERODE_NONE -1 #define ERODE_BURN 0 #define ERODE_RUST 1 #define ERODE_ROT 2 diff --git a/src/do_wear.c b/src/do_wear.c index 7a65bbf07..025b097ef 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -52,6 +52,7 @@ staticfn int takeoff_ok(struct obj *); /* maybe_destroy_armor() may return NULL */ staticfn struct obj *maybe_destroy_armor(struct obj *, struct obj *, boolean *) NONNULLARG3; +staticfn int obj_erode_type(struct obj *) NONNULLARG1; staticfn boolean better_not_take_that_off(struct obj *) NONNULLARG1; /* plural "fingers" or optionally "gloves" */ @@ -3191,9 +3192,9 @@ maybe_destroy_armor(struct obj *armor, struct obj *atmp, boolean *resisted) return (struct obj *) 0; } -/* hit by destroy armor scroll/black dragon breath/monster spell */ +/* hit by destroy armor scroll/black dragon breath */ int -destroy_arm(struct obj *atmp) +disintegrate_arm(struct obj *atmp) { struct obj *otmp = (struct obj *) 0; boolean losing_gloves = FALSE, resisted = FALSE, @@ -3250,6 +3251,66 @@ destroy_arm(struct obj *atmp) return 1; } +/* return ERODE_foo erosion type which can apply to object */ +staticfn int +obj_erode_type(struct obj *otmp) +{ + if (is_flammable(otmp)) + return ERODE_BURN; + else if (is_rustprone(otmp)) + return ERODE_RUST; + else if (is_crackable(otmp)) + return ERODE_CRACK; + else if (is_rottable(otmp)) + return ERODE_ROT; + else if (is_corrodeable(otmp)) + return ERODE_CORRODE; + return ERODE_NONE; +} + +/* erode a number of worn armor(s). + if the armor is hit when max eroded, destroys it. */ +int +destroy_arm(void) +{ + struct obj *armors[7] = { NULL }; + struct obj *otmp; + int i, idx = 0, hits = rn2(4) + 1; + int ret = 0; + + /* gather worn armor; include non-erodeable ones */ + if (uarm) armors[idx++] = uarm; + if (uarmc) armors[idx++] = uarmc; + if (uarmh) armors[idx++] = uarmh; + if (uarms) armors[idx++] = uarms; + if (uarmg) armors[idx++] = uarmg; + if (uarmf) armors[idx++] = uarmf; + if (uarmu) armors[idx++] = uarmu; + if (!idx) + return 0; + + for (i = 0; i < hits; i++) { + otmp = armors[rn2(idx)]; + + if (erosion_matters(otmp) && is_damageable(otmp) && !otmp->oerodeproof) { + int erosion = obj_erode_type(otmp); + + if (erosion != ERODE_NONE) { + int r = erode_obj(otmp, xname(otmp), erosion, EF_PAY|EF_DESTROY); + + if (r != ER_NOTHING) + ret = 1; + if (r == ER_DESTROYED) + break; + } + } + } + + if (ret) + stop_occupation(); + return ret; +} + void adj_abon(struct obj *otmp, schar delta) { diff --git a/src/mcastu.c b/src/mcastu.c index 29792429f..bffe7d790 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -530,7 +530,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) shieldeff(u.ux, u.uy); monstseesu(M_SEEN_MAGR); pline("A field of force surrounds you!"); - } else if (!destroy_arm(some_armor(&gy.youmonst))) { + } else if (!destroy_arm()) { Your("skin itches."); } else { /* monsters only realize you aren't magic-protected if armor is diff --git a/src/pray.c b/src/pray.c index 281ce64e7..55e95e096 100644 --- a/src/pray.c +++ b/src/pray.c @@ -660,15 +660,15 @@ god_zaps_you(aligntyp resp_god) */ if (uarms && !(EReflecting & W_ARMS) && !(EDisint_resistance & W_ARMS)) - (void) destroy_arm(uarms); + (void) disintegrate_arm(uarms); if (uarmc && !(EReflecting & W_ARMC) && !(EDisint_resistance & W_ARMC)) - (void) destroy_arm(uarmc); + (void) disintegrate_arm(uarmc); if (uarm && !(EReflecting & W_ARM) && !(EDisint_resistance & W_ARM) && !uarmc) - (void) destroy_arm(uarm); + (void) disintegrate_arm(uarm); if (uarmu && !uarm && !uarmc) - (void) destroy_arm(uarmu); + (void) disintegrate_arm(uarmu); if (!Disint_resistance) { fry_by_god(resp_god, TRUE); monstunseesu(M_SEEN_DISINT); diff --git a/src/read.c b/src/read.c index 8b8f6bc79..2770ee4ed 100644 --- a/src/read.c +++ b/src/read.c @@ -22,6 +22,7 @@ staticfn int maybe_tame(struct monst *, struct obj *); staticfn boolean can_center_cloud(coordxy, coordxy); staticfn void display_stinking_cloud_positions(boolean); staticfn void seffect_enchant_armor(struct obj **); +staticfn boolean disintegrate_cursed_armor(void); staticfn void seffect_destroy_armor(struct obj **); staticfn void seffect_confuse_monster(struct obj **); staticfn void seffect_scare_monster(struct obj **); @@ -1281,6 +1282,29 @@ seffect_enchant_armor(struct obj **sobjp) Blind ? "again" : "unexpectedly"); } +/* destroy a random cursed armor worn by hero */ +staticfn boolean +disintegrate_cursed_armor(void) +{ + struct obj *armors[7] = { NULL }; + int idx = 0; + + if (uarm && uarm->cursed) armors[idx++] = uarm; + if (uarmc && uarmc->cursed) armors[idx++] = uarmc; + if (uarmh && uarmh->cursed) armors[idx++] = uarmh; + if (uarms && uarms->cursed) armors[idx++] = uarms; + if (uarmg && uarmg->cursed) armors[idx++] = uarmg; + if (uarmf && uarmf->cursed) armors[idx++] = uarmf; + if (uarmu && uarmu->cursed) armors[idx++] = uarmu; + if (!idx) + return FALSE; + + if (disintegrate_arm(armors[rn2(idx)])) + return TRUE; + + return FALSE; +} + staticfn void seffect_destroy_armor(struct obj **sobjp) { @@ -1310,7 +1334,21 @@ seffect_destroy_armor(struct obj **sobjp) otmp->oerodeproof = new_erodeproof ? 1 : 0; return; } - if (!scursed || !otmp || !otmp->cursed) { + + if (scursed) { + if (otmp && otmp->cursed) { + /* armor and scroll both cursed */ + pline("%s.", Yobjnam2(otmp, "vibrate")); + if (otmp->spe >= -6) { + otmp->spe += -1; + adj_abon(otmp, -1); + } + make_stunned((HStun & TIMEOUT) + (long) rn1(10, 10), TRUE); + } else if (disintegrate_arm(otmp)) { + gk.known = TRUE; + return; + } + } else { boolean gets_choice = (otmp && sobj && sobj->blessed && count_worn_armor() > 1); @@ -1324,9 +1362,14 @@ seffect_destroy_armor(struct obj **sobjp) /* check the return value, if user picked non-valid obj */ if (any_worn_armor_ok(atmp) == GETOBJ_SUGGEST) otmp = atmp; - } - - if (!destroy_arm(otmp)) { + if (disintegrate_arm(otmp)) { + gk.known = TRUE; + return; + } + } else if (sobj->blessed && disintegrate_cursed_armor()) { + gk.known = TRUE; + return; + } else if (!destroy_arm()) { strange_feeling(sobj, "Your skin itches."); *sobjp = 0; /* useup() in strange_feeling() */ exercise(A_STR, FALSE); @@ -1334,13 +1377,6 @@ seffect_destroy_armor(struct obj **sobjp) return; } else gk.known = TRUE; - } else { /* armor and scroll both cursed */ - pline("%s.", Yobjnam2(otmp, "vibrate")); - if (otmp->spe >= -6) { - otmp->spe += -1; - adj_abon(otmp, -1); - } - make_stunned((HStun & TIMEOUT) + (long) rn1(10, 10), TRUE); } } diff --git a/src/zap.c b/src/zap.c index ec49363f5..96dc3d44b 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4462,21 +4462,21 @@ zhitu( monstunseesu(M_SEEN_DISINT); if (uarms) { /* destroy shield; other possessions are safe */ - (void) destroy_arm(uarms); + (void) disintegrate_arm(uarms); break; } else if (uarm) { /* destroy suit; if present, cloak goes too */ if (uarmc) - (void) destroy_arm(uarmc); - (void) destroy_arm(uarm); + (void) disintegrate_arm(uarmc); + (void) disintegrate_arm(uarm); break; } /* no shield or suit, you're dead; wipe out cloak and/or shirt in case of life-saving or bones */ if (uarmc) - (void) destroy_arm(uarmc); + (void) disintegrate_arm(uarmc); if (uarmu) - (void) destroy_arm(uarmu); + (void) disintegrate_arm(uarmu); } else if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { shieldeff(sx, sy); You("seem unaffected.");