github PR #259 - paranoid_confirmation:trap

Fairly old pull request from copperwater:  add new paranoid_confirm
setting 'trap'.

The old commit suffered from bit rot and merging needed too much
fixing up despite there not being many bands of change in the commit's
diffs.  I ultimately redid it from scratch, although the two biggest
chunks of code started with copy+paste of the pull request's commit.

It operates like paranoid:pray.  Setting paranoid:trap adds a new
"Really step into <trap>?" y/n prompt when attempting to move
into/onto a known trap, even if an object covers it on the map.
Setting both 'paranoid:Confirm trap' turns that into a yes/no prompt.
(Adding 'Confirm' affects other paranoid confirmations; in addition
to requiring yes<return> rather than just y to accept, it also forces
no<return> to reject.)

However, moving into a known trap that is considered to be harmless
behaves as if no trap was present.  Some of the trap classification
might be out of date; several types of traps have undergone changes
since implementation of the original pull request, notably anti-magic
field.  When the hero is hallucinating, all known traps are considered
harmful since the map no longer reliably describes them.

Preceding a movement command with the 'm' prefix also behaves as if
no trap was present, bypassing confirmation for that move, similar to
how paranoid:swim currently behaves.  Being stunned or confused also
behaves as if no trap was present, taking priority over hallucination.

This updates the documentation.

Supersedes #259
Closes #259
This commit is contained in:
PatR
2023-09-08 15:55:31 -07:00
parent 33d85bfe08
commit 1a64ee1c28
10 changed files with 248 additions and 11 deletions
+2 -1
View File
@@ -214,7 +214,7 @@ packorder a list of default symbols for kinds of [")[%?+!=/(*`0_]
paranoid_confirmation space separated list [paranoid_confirm:pray swim] paranoid_confirmation space separated list [paranoid_confirm:pray swim]
of situations where alternate prompting is desired of situations where alternate prompting is desired
Confirm -- when requiring "yes", also require "no" to reject; Confirm -- when requiring "yes", also require "no" to reject;
also requires yes rather than y for pray, Autoall also requires yes rather than y for pray, trap, Autoall
quit -- yes vs y to confirm quitting or to enter explore mode quit -- yes vs y to confirm quitting or to enter explore mode
die -- yes vs y to confirm dying (for explore or debug mode) die -- yes vs y to confirm dying (for explore or debug mode)
bones -- yes vs y to confirm saving bones data in debug mode bones -- yes vs y to confirm saving bones data in debug mode
@@ -224,6 +224,7 @@ paranoid_confirmation space separated list [paranoid_confirm:pray swim]
Were-change -- yes vs y to confirm changing form due to Were-change -- yes vs y to confirm changing form due to
lycanthropy when hero has polymorph control; lycanthropy when hero has polymorph control;
pray -- y to confirm an attempt to pray; on by default pray -- y to confirm an attempt to pray; on by default
trap -- y to enter a known trap unless it is harmless;
swim -- require m prefix to move into water or lava when swim -- require m prefix to move into water or lava when
hero has seen it and isn't impaired; on by default; hero has seen it and isn't impaired; on by default;
AutoAll -- y to confirm if using menustyle:Full and choice 'A' AutoAll -- y to confirm if using menustyle:Full and choice 'A'
+6 -1
View File
@@ -37,7 +37,7 @@
.ds f0 "\*(vr .ds f0 "\*(vr
.ds f1 .ds f1
.\"DO NOT REMOVE NH_DATESUB .ds f2 "DATE(%B %-d, %Y) .\"DO NOT REMOVE NH_DATESUB .ds f2 "DATE(%B %-d, %Y)
.ds f2 "August 5, 2023 .ds f2 "September 8, 2023
. .
.\" A note on some special characters: .\" A note on some special characters:
.\" \(lq = left double quote .\" \(lq = left double quote
@@ -4246,6 +4246,11 @@ to lycanthropy when hero has polymorph control;
require \(oqy\(cq to confirm an attempt to pray rather require \(oqy\(cq to confirm an attempt to pray rather
than immediately praying; on by default; than immediately praying; on by default;
(to require \(lqyes\(rq rather than just \(oqy\(cq, set Confirm too); (to require \(lqyes\(rq rather than just \(oqy\(cq, set Confirm too);
.PL trap
require \(oqy\(cq to confirm an attempt to move into or onto a known trap,
unless doing so is considered to be harmless;
(to require \(lqyes\(rq rather than just \(oqy\(cq, set Confirm too);
confirmation can be skipped by using the \(oq\f(CRm\fP\(cq movement prefix;
.PL swim .PL swim
prevent walking into water or lava; on by default; (to deliberately step prevent walking into water or lava; on by default; (to deliberately step
onto/into such terrain when this is set, use the \(oq\f(CRm\fP\(cq onto/into such terrain when this is set, use the \(oq\f(CRm\fP\(cq
+7 -2
View File
@@ -46,7 +46,7 @@
\author{Original version - Eric S. Raymond\\ \author{Original version - Eric S. Raymond\\
(Edited and expanded for 3.7.0 by Mike Stephenson and others)} (Edited and expanded for 3.7.0 by Mike Stephenson and others)}
%DO NOT REMOVE NH_DATESUB \date{DATE(%B %-d, %Y)} %DO NOT REMOVE NH_DATESUB \date{DATE(%B %-d, %Y)}
\date{August 5, 2023} \date{September 8, 2023}
\maketitle \maketitle
@@ -4646,7 +4646,7 @@ a peaceful monster;
\item[{\tt wand-break}] \item[{\tt wand-break}]
require ``{\tt yes}'' rather than `{\tt y}' to confirm breaking require ``{\tt yes}'' rather than `{\tt y}' to confirm breaking
a wand with the {\it apply} command; a wand with the {\it apply} command;
\item[{\tt eating}] \item[{\tt eating~}]
require ``{\tt yes}'' rather than `{\tt y}' to confirm whether to require ``{\tt yes}'' rather than `{\tt y}' to confirm whether to
continue eating; continue eating;
\item[{\tt Were-change}] \item[{\tt Were-change}]
@@ -4656,6 +4656,11 @@ to lycanthropy when hero has polymorph control;
require `{\tt y}' to confirm an attempt to pray rather require `{\tt y}' to confirm an attempt to pray rather
than immediately praying; on by default; than immediately praying; on by default;
(to require ``yes'' rather than just `y', set Confirm too); (to require ``yes'' rather than just `y', set Confirm too);
\item[{\tt trap~~~}]
require `{\tt y}' to confirm an attempt to move into or onto a known trap,
unless doing so is considered to be harmless;
(to require ``yes'' rather than just `y', set Confirm too);
confirmation can be skipped by using the `{\tt m}' movement prefix;
\item[{\tt swim~~~}] \item[{\tt swim~~~}]
prevent walking into water or lava; on by default; (to deliberately step prevent walking into water or lava; on by default; (to deliberately step
onto/into such terrain when this is set, use the `{\tt m}' onto/into such terrain when this is set, use the `{\tt m}'
+5
View File
@@ -1234,6 +1234,8 @@ if Magicbane cancelled a shapeshifter, forcing it to 'unshift', subsequent
a pet that was poison resistant but not stoning resistant would eat Medusa's a pet that was poison resistant but not stoning resistant would eat Medusa's
corpse and be turned to stone corpse and be turned to stone
ring of hunger prevents choking on your food ring of hunger prevents choking on your food
paranoid_confirm:pray can be changed to require yes/no response instead of y/n
by also setting paranoid_confirm:Confirm
Fixes to 3.7.0-x General Problems Exposed Via git Repository Fixes to 3.7.0-x General Problems Exposed Via git Repository
@@ -2171,6 +2173,9 @@ reading a blessed scroll of light has a chance to improve bless/curse state
added a chronicle of major events, and optional live logging of those added a chronicle of major events, and optional live logging of those
paranoid_confirm:swim to prevent accidental dunking into dangerous liquids; paranoid_confirm:swim to prevent accidental dunking into dangerous liquids;
joins paranoid_confirm:pray as the default setting joins paranoid_confirm:pray as the default setting
paranoid_confirm:trap to confirm entering a known trap unless it is harmless;
like revised paranoid_confirm:pray, requires y/n response; add
paranoid_confirm:Confirm to require yes/no instead
paranoid_confirm:Autoall to confirm picking 'A' in menustyle:Full filter menu paranoid_confirm:Autoall to confirm picking 'A' in menustyle:Full filter menu
looking at a monster will indicate whether it is asleep, and waking up a looking at a monster will indicate whether it is asleep, and waking up a
monster yields a message monster yields a message
+1
View File
@@ -2913,6 +2913,7 @@ extern struct monst *animate_statue(struct obj *, coordxy, coordxy,
int, int *); int, int *);
extern struct monst *activate_statue_trap(struct trap *, coordxy, coordxy, extern struct monst *activate_statue_trap(struct trap *, coordxy, coordxy,
boolean); boolean);
extern int immune_to_trap(struct monst *, unsigned);
extern void set_utrap(unsigned, unsigned); extern void set_utrap(unsigned, unsigned);
extern void reset_utrap(boolean); extern void reset_utrap(boolean);
extern void dotrap(struct trap *, unsigned); extern void dotrap(struct trap *, unsigned);
+4 -1
View File
@@ -85,7 +85,8 @@ struct flag {
#define PARANOID_WERECHANGE 0x0100 #define PARANOID_WERECHANGE 0x0100
#define PARANOID_EATING 0x0200 #define PARANOID_EATING 0x0200
#define PARANOID_SWIM 0x0400 #define PARANOID_SWIM 0x0400
#define PARANOID_AUTOALL 0x0800 #define PARANOID_TRAP 0x0800
#define PARANOID_AUTOALL 0x1000
int pickup_burden; /* maximum burden before prompt */ int pickup_burden; /* maximum burden before prompt */
int pile_limit; /* controls feedback when walking over objects */ int pile_limit; /* controls feedback when walking over objects */
char discosort; /* order of dodiscovery/doclassdisco output: o,s,c,a */ char discosort; /* order of dodiscovery/doclassdisco output: o,s,c,a */
@@ -482,6 +483,8 @@ enum runmode_types {
#define ParanoidEating ((flags.paranoia_bits & PARANOID_EATING) != 0) #define ParanoidEating ((flags.paranoia_bits & PARANOID_EATING) != 0)
/* Prevent going into lava or water without explicitly forcing it */ /* Prevent going into lava or water without explicitly forcing it */
#define ParanoidSwim ((flags.paranoia_bits & PARANOID_SWIM) != 0) #define ParanoidSwim ((flags.paranoia_bits & PARANOID_SWIM) != 0)
/* Prevent going onto/into known trap unless it is harmless */
#define ParanoidTrap ((flags.paranoia_bits & PARANOID_TRAP) != 0)
/* Require confirmation for choosing 'A' in class menu for menustyle:Full */ /* Require confirmation for choosing 'A' in class menu for menustyle:Full */
#define ParanoidAutoAll ((flags.paranoia_bits & PARANOID_AUTOALL) != 0U) #define ParanoidAutoAll ((flags.paranoia_bits & PARANOID_AUTOALL) != 0U)
+14 -5
View File
@@ -93,13 +93,22 @@ enum trap_types {
}; };
/* some trap-related function return results */ /* some trap-related function return results */
enum { Trap_Effect_Finished = 0, enum trap_result {
Trap_Is_Gone = 0, Trap_Effect_Finished = 0,
Trap_Caught_Mon = 1, Trap_Is_Gone = 0,
Trap_Killed_Mon = 2, Trap_Caught_Mon = 1,
Trap_Moved_Mon = 3, /* new location, or new level */ Trap_Killed_Mon = 2,
Trap_Moved_Mon = 3, /* new location, or new level */
}; };
/* return codes from immune_to_trap() */
enum trap_immunities {
TRAP_NOT_IMMUNE = 0,
TRAP_CLEARLY_IMMUNE = 1,
TRAP_HIDDEN_IMMUNE = 2,
};
#define is_pit(ttyp) ((ttyp) == PIT || (ttyp) == SPIKED_PIT) #define is_pit(ttyp) ((ttyp) == PIT || (ttyp) == SPIKED_PIT)
#define is_hole(ttyp) ((ttyp) == HOLE || (ttyp) == TRAPDOOR) #define is_hole(ttyp) ((ttyp) == HOLE || (ttyp) == TRAPDOOR)
#define unhideable_trap(ttyp) ((ttyp) == HOLE) /* visible traps */ #define unhideable_trap(ttyp) ((ttyp) == HOLE) /* visible traps */
+39
View File
@@ -2459,6 +2459,45 @@ domove_core(void)
if (u_rooted()) if (u_rooted())
return; return;
/* warn maybe player before walking into known traps */
if (ParanoidTrap && (trap = t_at(x, y)) != 0 && trap->tseen
&& (!gc.context.nopick || gc.context.run)
&& !Stunned && !Confusion
&& (immune_to_trap(&gy.youmonst, trap->ttyp) != TRAP_CLEARLY_IMMUNE
/* hallucination: all traps still show as ^, but the
hero can't tell what they are, so treat as dangerous */
|| Hallucination)) {
char qbuf[QBUFSZ];
int traptype = (Hallucination ? rnd(TRAPNUM - 1) : (int) trap->ttyp);
boolean into = FALSE; /* "onto" the trap vs "into" */
switch (traptype) {
case BEAR_TRAP:
case PIT:
case SPIKED_PIT:
case HOLE:
case TELEP_TRAP:
case LEVEL_TELEP:
case MAGIC_PORTAL:
case WEB:
into = TRUE;
break;
}
Snprintf(qbuf, sizeof qbuf, "Really %s %s that %s?",
locomotion(gy.youmonst.data, "step"),
into ? "into" : "onto",
defsyms[trap_to_defsym(traptype)].explanation);
/* handled like paranoid_confirm:pray; when paranoid_confirm:trap
isn't set, don't ask at all but if it is set (checked above),
ask via y/n if parnoid_confirm:confirm isn't also set or via
yes/no if it is */
if (!paranoid_query(ParanoidConfirm, qbuf)) {
nomul(0);
gc.context.move = 0;
return;
}
}
if (u.utrap) { if (u.utrap) {
boolean moved = trapmove(x, y, trap); boolean moved = trapmove(x, y, trap);
+4 -1
View File
@@ -176,9 +176,12 @@ static const struct paranoia_opts {
"yes vs y to continue eating after first bite when satiated" }, "yes vs y to continue eating after first bite when satiated" },
{ PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0, { PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0,
"yes vs y to change form when lycanthropy is controllable" }, "yes vs y to change form when lycanthropy is controllable" },
/* extra y/n questions rather than changing y/n to yes/n[o] */ /* extra y/n questions rather than changing y/n to yes/n[o];
they switch to yes/no if paranoid:confirm is also set */
{ PARANOID_PRAY, "pray", 1, 0, 0, { PARANOID_PRAY, "pray", 1, 0, 0,
"y required to pray (supersedes old \"prayconfirm\" option)" }, "y required to pray (supersedes old \"prayconfirm\" option)" },
{ PARANOID_TRAP, "trap", 1, "move-trap", 1,
"y required to enter known trap unless considered harmless" },
{ PARANOID_AUTOALL, "Autoall", 2, "autoselect-all", 2, { PARANOID_AUTOALL, "Autoall", 2, "autoselect-all", 2,
"y required to pick filter choice 'A' for menustyle:Full" }, "y required to pick filter choice 'A' for menustyle:Full" },
/* not a yes/n[o] vs y/n change nor a y/n addition */ /* not a yes/n[o] vs y/n change nor a y/n addition */
+166
View File
@@ -2544,6 +2544,172 @@ trapeffect_vibrating_square(
return Trap_Effect_Finished; return Trap_Effect_Finished;
} }
/*
* for PR#259 - paranoid_confirm:trap
*
* Will a monster suffer any adverse effects from a certain trap?
* Note: does NOT mean "will a monster trigger a trap in the first place",
* though if it won't that does imply that they'll not suffer adverse effects.
* For example, an elf is considered immune to sleeping gas traps even though
* they'll set the trap off.
* Return value:
* TRAP_NOT_IMMUNE = not immune at the moment;
* TRAP_CLEARLY_IMMUNE = obviously immune (if player is polymorphed, assume
* they know which traps they are immune to in their current form);
* TRAP_HIDDEN_IMMUNE = immune but in non-obvious way such as an unidentified
* item or hidden intrinsic providing a resistance; the player should still
* be warned of this trap, while monsters implicitly know they're immune.
*/
int
immune_to_trap(struct monst *mon, unsigned ttype)
{
struct permonst *pm;
struct obj *obj;
boolean is_you;
if (!mon) {
impossible("immune_to_trap: null monster");
return TRAP_NOT_IMMUNE;
}
pm = mon->data;
is_you = (mon == &gy.youmonst);
switch (ttype) {
case ARROW_TRAP:
case DART_TRAP:
case ROCKTRAP:
/* can hit anything; even noncorporeal monsters might get a blessed
projectile */
return TRAP_NOT_IMMUNE;
case BEAR_TRAP:
if (pm->msize <= MZ_SMALL
|| amorphous(pm) || is_whirly(pm) || unsolid(pm))
return TRAP_CLEARLY_IMMUNE;
/*FALLTHRU*/
case SQKY_BOARD:
case LANDMINE:
case ROLLING_BOULDER_TRAP:
case HOLE:
case TRAPDOOR:
case PIT:
case SPIKED_PIT:
/* ground-based traps, which can be evaded by levitation, flying, or
hanging to the ceiling */
if (Sokoban && (is_pit(ttype) || is_hole(ttype)))
return TRAP_NOT_IMMUNE;
if (is_floater(pm) || is_flyer(pm)
|| (is_clinger(pm) && has_ceiling(&u.uz)))
return TRAP_CLEARLY_IMMUNE;
else if (is_you && (Levitation || Flying))
return TRAP_CLEARLY_IMMUNE;
return TRAP_NOT_IMMUNE;
case SLP_GAS_TRAP:
if (breathless(pm))
return TRAP_CLEARLY_IMMUNE;
else if (!is_you && resists_sleep(mon))
return TRAP_CLEARLY_IMMUNE;
else if (is_you && Sleep_resistance)
return TRAP_HIDDEN_IMMUNE;
return TRAP_NOT_IMMUNE;
case LEVEL_TELEP:
case TELEP_TRAP:
/* consider unintended teleporting to be an adverse effect; if in
the endgame or carrying the Amulet, the teleport trap won't work
anyway, so anything hitting it is immune. */
if (In_endgame(&u.uz) || mon_has_amulet(mon))
return TRAP_CLEARLY_IMMUNE;
return TRAP_NOT_IMMUNE;
case POLY_TRAP:
if (resists_magm(mon))
/* covers Antimagic for player */
return (is_you ? TRAP_HIDDEN_IMMUNE : TRAP_CLEARLY_IMMUNE);
return TRAP_NOT_IMMUNE;
case STATUE_TRAP:
/* no effect on monsters, only affects players; only trap detection
can let player know that this is a statue trap there ahead of time;
in the rare case this happens, do consider it an adverse effect */
if (!is_you)
return TRAP_CLEARLY_IMMUNE;
return TRAP_NOT_IMMUNE;
case WEB:
/* most of this code is lifted from mu_maybe_destroy_web */
if (webmaker(pm) || amorphous(pm) || is_whirly(pm) || flaming(pm)
|| unsolid(pm) || pm == &mons[PM_GELATINOUS_CUBE])
return TRAP_CLEARLY_IMMUNE;
return TRAP_NOT_IMMUNE;
case ANTI_MAGIC:
/* doesn't hurt any non-magic-resistant monster with no magic */
if (is_you) {
if (Antimagic)
return TRAP_NOT_IMMUNE;
else if (u.uenmax == 0)
/* player won't lose HP and can't lose more Pw */
return TRAP_HIDDEN_IMMUNE;
/* following conditional lifted from mintrap ANTI_MAGIC logic */
} else if (!resists_magm(mon)
&& (mon->mcan || (!attacktype(pm, AT_MAGC)
&& !attacktype(pm, AT_BREA)))) {
return TRAP_CLEARLY_IMMUNE;
}
return TRAP_NOT_IMMUNE;
case RUST_TRAP:
/* harmful if wearing anything rustable or if mon is an iron golem */
if (pm == &mons[PM_IRON_GOLEM])
return TRAP_NOT_IMMUNE;
for (obj = is_you ? gi.invent : mon->minvent; obj; obj = obj->nobj) {
/* rust traps can currently hit only worn armor and weapons */
if (is_rustprone(obj) && obj->owornmask) {
if (is_you && (obj == uquiver
|| (obj == uswapwep && !u.twoweap)))
continue;
return TRAP_NOT_IMMUNE;
}
}
return TRAP_CLEARLY_IMMUNE;
case MAGIC_TRAP:
/* for player, any number of bad effects;
for monsters, only replicates fire trap, so fall through */
if (is_you)
return TRAP_NOT_IMMUNE;
/*FALLTHRU*/
case FIRE_TRAP: /* can always destroy items being carried */
/* harmful if not resistant or if carrying anything that could burn */
if (is_you ? !Fire_resistance : !resists_fire(mon))
return TRAP_NOT_IMMUNE;
for (obj = is_you ? gi.invent : mon->minvent; obj; obj = obj->nobj) {
if (obj->oclass == SCROLL_CLASS || obj->oclass == POTION_CLASS
|| obj->oclass == SPBOOK_CLASS
|| (obj->owornmask && is_flammable(obj))) {
if ((obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
/* mon knows scroll of fire or spellbook of fireball
won't be affected; hero knows iff this one has been
seen and its type has been discovered */
&& (!is_you
|| (obj->dknown && objects[obj->otyp].oc_name_known)))
continue;
return TRAP_NOT_IMMUNE;
}
}
return (is_you ? TRAP_HIDDEN_IMMUNE : TRAP_CLEARLY_IMMUNE);
case MAGIC_PORTAL:
/* never hurts anything, but player is considered non-immune so they
can be asked about entering it */
if (!is_you)
return TRAP_CLEARLY_IMMUNE;
return TRAP_NOT_IMMUNE;
case VIBRATING_SQUARE:
/* no adverse effects */
return TRAP_CLEARLY_IMMUNE;
default:
impossible("immune_to_trap: bad ttype %u", ttype);
break;
}
return TRAP_NOT_IMMUNE;
}
static int static int
trapeffect_selector( trapeffect_selector(
struct monst* mtmp, struct monst* mtmp,