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.
This commit is contained in:
Pasi Kallinen
2026-03-16 11:20:34 +02:00
parent b70da87d3e
commit 619de1ec0e
8 changed files with 127 additions and 25 deletions

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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.");