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:24:19 +02:00
parent b70da87d3e
commit 619de1ec0e
8 changed files with 127 additions and 25 deletions
+4 -1
View File
@@ -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 tourists gain experience by "taking photos" of new creatures, and
going to new dungeon levels going to new dungeon levels
healers gain experience by healing pets 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 archeologists' fedora is lucky
telepathic hero can discern which particular monster just read a scroll telepathic hero can discern which particular monster just read a scroll
add "make fetch-docs" to download pre-formatted documentation add "make fetch-docs" to download pre-formatted documentation
monsters will use throw-and-return weapons such as an aklys monsters will use throw-and-return weapons such as an aklys
archeologists can identify scrolls by deciphering their labels archeologists can identify scrolls by deciphering their labels
monster destroy armor -spell first erodes armor
Platform- and/or Interface-Specific New Features Platform- and/or Interface-Specific New Features
+2 -1
View File
@@ -766,7 +766,8 @@ extern struct obj *unchanger(void);
extern void reset_remarm(void); extern void reset_remarm(void);
extern int doddoremarm(void); extern int doddoremarm(void);
extern int remarm_swapwep(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 void adj_abon(struct obj *, schar) NONNULLARG1;
extern boolean inaccessible_equipment(struct obj *, const char *, boolean); extern boolean inaccessible_equipment(struct obj *, const char *, boolean);
extern int any_worn_armor_ok(struct obj *); extern int any_worn_armor_ok(struct obj *);
+1
View File
@@ -451,6 +451,7 @@ struct obj {
#define BURIED_TOO 0x2 #define BURIED_TOO 0x2
/* object erosion types */ /* object erosion types */
#define ERODE_NONE -1
#define ERODE_BURN 0 #define ERODE_BURN 0
#define ERODE_RUST 1 #define ERODE_RUST 1
#define ERODE_ROT 2 #define ERODE_ROT 2
+63 -2
View File
@@ -52,6 +52,7 @@ staticfn int takeoff_ok(struct obj *);
/* maybe_destroy_armor() may return NULL */ /* maybe_destroy_armor() may return NULL */
staticfn struct obj *maybe_destroy_armor(struct obj *, struct obj *, staticfn struct obj *maybe_destroy_armor(struct obj *, struct obj *,
boolean *) NONNULLARG3; boolean *) NONNULLARG3;
staticfn int obj_erode_type(struct obj *) NONNULLARG1;
staticfn boolean better_not_take_that_off(struct obj *) NONNULLARG1; staticfn boolean better_not_take_that_off(struct obj *) NONNULLARG1;
/* plural "fingers" or optionally "gloves" */ /* plural "fingers" or optionally "gloves" */
@@ -3191,9 +3192,9 @@ maybe_destroy_armor(struct obj *armor, struct obj *atmp, boolean *resisted)
return (struct obj *) 0; return (struct obj *) 0;
} }
/* hit by destroy armor scroll/black dragon breath/monster spell */ /* hit by destroy armor scroll/black dragon breath */
int int
destroy_arm(struct obj *atmp) disintegrate_arm(struct obj *atmp)
{ {
struct obj *otmp = (struct obj *) 0; struct obj *otmp = (struct obj *) 0;
boolean losing_gloves = FALSE, resisted = FALSE, boolean losing_gloves = FALSE, resisted = FALSE,
@@ -3250,6 +3251,66 @@ destroy_arm(struct obj *atmp)
return 1; 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 void
adj_abon(struct obj *otmp, schar delta) adj_abon(struct obj *otmp, schar delta)
{ {
+1 -1
View File
@@ -530,7 +530,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
shieldeff(u.ux, u.uy); shieldeff(u.ux, u.uy);
monstseesu(M_SEEN_MAGR); monstseesu(M_SEEN_MAGR);
pline("A field of force surrounds you!"); pline("A field of force surrounds you!");
} else if (!destroy_arm(some_armor(&gy.youmonst))) { } else if (!destroy_arm()) {
Your("skin itches."); Your("skin itches.");
} else { } else {
/* monsters only realize you aren't magic-protected if armor is /* monsters only realize you aren't magic-protected if armor is
+4 -4
View File
@@ -660,15 +660,15 @@ god_zaps_you(aligntyp resp_god)
*/ */
if (uarms && !(EReflecting & W_ARMS) if (uarms && !(EReflecting & W_ARMS)
&& !(EDisint_resistance & W_ARMS)) && !(EDisint_resistance & W_ARMS))
(void) destroy_arm(uarms); (void) disintegrate_arm(uarms);
if (uarmc && !(EReflecting & W_ARMC) if (uarmc && !(EReflecting & W_ARMC)
&& !(EDisint_resistance & W_ARMC)) && !(EDisint_resistance & W_ARMC))
(void) destroy_arm(uarmc); (void) disintegrate_arm(uarmc);
if (uarm && !(EReflecting & W_ARM) && !(EDisint_resistance & W_ARM) if (uarm && !(EReflecting & W_ARM) && !(EDisint_resistance & W_ARM)
&& !uarmc) && !uarmc)
(void) destroy_arm(uarm); (void) disintegrate_arm(uarm);
if (uarmu && !uarm && !uarmc) if (uarmu && !uarm && !uarmc)
(void) destroy_arm(uarmu); (void) disintegrate_arm(uarmu);
if (!Disint_resistance) { if (!Disint_resistance) {
fry_by_god(resp_god, TRUE); fry_by_god(resp_god, TRUE);
monstunseesu(M_SEEN_DISINT); monstunseesu(M_SEEN_DISINT);
+47 -11
View File
@@ -22,6 +22,7 @@ staticfn int maybe_tame(struct monst *, struct obj *);
staticfn boolean can_center_cloud(coordxy, coordxy); staticfn boolean can_center_cloud(coordxy, coordxy);
staticfn void display_stinking_cloud_positions(boolean); staticfn void display_stinking_cloud_positions(boolean);
staticfn void seffect_enchant_armor(struct obj **); staticfn void seffect_enchant_armor(struct obj **);
staticfn boolean disintegrate_cursed_armor(void);
staticfn void seffect_destroy_armor(struct obj **); staticfn void seffect_destroy_armor(struct obj **);
staticfn void seffect_confuse_monster(struct obj **); staticfn void seffect_confuse_monster(struct obj **);
staticfn void seffect_scare_monster(struct obj **); staticfn void seffect_scare_monster(struct obj **);
@@ -1281,6 +1282,29 @@ seffect_enchant_armor(struct obj **sobjp)
Blind ? "again" : "unexpectedly"); 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 staticfn void
seffect_destroy_armor(struct obj **sobjp) seffect_destroy_armor(struct obj **sobjp)
{ {
@@ -1310,7 +1334,21 @@ seffect_destroy_armor(struct obj **sobjp)
otmp->oerodeproof = new_erodeproof ? 1 : 0; otmp->oerodeproof = new_erodeproof ? 1 : 0;
return; 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 boolean gets_choice = (otmp && sobj && sobj->blessed
&& count_worn_armor() > 1); && count_worn_armor() > 1);
@@ -1324,9 +1362,14 @@ seffect_destroy_armor(struct obj **sobjp)
/* check the return value, if user picked non-valid obj */ /* check the return value, if user picked non-valid obj */
if (any_worn_armor_ok(atmp) == GETOBJ_SUGGEST) if (any_worn_armor_ok(atmp) == GETOBJ_SUGGEST)
otmp = atmp; otmp = atmp;
} if (disintegrate_arm(otmp)) {
gk.known = TRUE;
if (!destroy_arm(otmp)) { return;
}
} else if (sobj->blessed && disintegrate_cursed_armor()) {
gk.known = TRUE;
return;
} else if (!destroy_arm()) {
strange_feeling(sobj, "Your skin itches."); strange_feeling(sobj, "Your skin itches.");
*sobjp = 0; /* useup() in strange_feeling() */ *sobjp = 0; /* useup() in strange_feeling() */
exercise(A_STR, FALSE); exercise(A_STR, FALSE);
@@ -1334,13 +1377,6 @@ seffect_destroy_armor(struct obj **sobjp)
return; return;
} else } else
gk.known = TRUE; 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);
} }
} }
+5 -5
View File
@@ -4462,21 +4462,21 @@ zhitu(
monstunseesu(M_SEEN_DISINT); monstunseesu(M_SEEN_DISINT);
if (uarms) { if (uarms) {
/* destroy shield; other possessions are safe */ /* destroy shield; other possessions are safe */
(void) destroy_arm(uarms); (void) disintegrate_arm(uarms);
break; break;
} else if (uarm) { } else if (uarm) {
/* destroy suit; if present, cloak goes too */ /* destroy suit; if present, cloak goes too */
if (uarmc) if (uarmc)
(void) destroy_arm(uarmc); (void) disintegrate_arm(uarmc);
(void) destroy_arm(uarm); (void) disintegrate_arm(uarm);
break; break;
} }
/* no shield or suit, you're dead; wipe out cloak /* no shield or suit, you're dead; wipe out cloak
and/or shirt in case of life-saving or bones */ and/or shirt in case of life-saving or bones */
if (uarmc) if (uarmc)
(void) destroy_arm(uarmc); (void) disintegrate_arm(uarmc);
if (uarmu) if (uarmu)
(void) destroy_arm(uarmu); (void) disintegrate_arm(uarmu);
} else if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) { } else if (nonliving(gy.youmonst.data) || is_demon(gy.youmonst.data)) {
shieldeff(sx, sy); shieldeff(sx, sy);
You("seem unaffected."); You("seem unaffected.");