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

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

View File

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

View File

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

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