add 'montelecontrol' option (wizard-mode only)

Add a new debugging option, 'montelecontrol', that allows a wizard-
mode player to choose a teleporting monster's destination.  If player
picks a bad spot, confirmation will be requested.  If accepted, the
spot will be used even though the consequences could be bad; that's
on the player.  If rejected, the destination will be assigned as if
no control had been attempted rather than try again.

The fuzzer isn't allowed to override a bad spot if it tries to pick
one.  That would probably trigger a sanity_check warning; the fuzzer
causes impossible warnings to behave as if panic, so accepting a bad
spot would just be fuzzer suicide.  It is allowed to randomly set the
option and maybe--though extremely unlikely--randomly pick a valid
controlled destination.
This commit is contained in:
PatR
2023-06-09 00:56:53 -07:00
parent 62eacb11c4
commit e475dca209
8 changed files with 77 additions and 2 deletions

View File

@@ -135,6 +135,7 @@ tiled_map show map as tiles, forces ascii_map Off; Qt, X11
Boolean options available when running in debug mode (aka wizard mode):
menu_tab_sep menu formatting--do not touch
monpolycontrol have player choose shape-changing monsters' new shapes
montelecontrol let player choose destination for teleported monsters
travel_debug show state of travel pathfinding algorithm on the map
wizweight include item weights in inventory display

View File

@@ -37,7 +37,7 @@
.ds f0 "\*(vr
.ds f1
.\"DO NOT REMOVE NH_DATESUB .ds f2 "DATE(%B %-d, %Y)
.ds f2 "May 7, 2023
.ds f2 "June 8, 2023
.
.\" A note on some special characters:
.\" \(lq = left double quote
@@ -4121,6 +4121,9 @@ Default \(oq}\(cq.
.lp monpolycontrol
Prompt for new form whenever any monster changes shape (default off).
Debug mode only.
.lp montelecontrol
Prompt for destination whenever any monster gets teleported (default off).
Debug mode only.
.lp mouse_support
Allow use of the mouse for input and travel.
Valid settings are:

View File

@@ -46,7 +46,7 @@
\author{Original version - Eric S. Raymond\\
(Edited and expanded for 3.7.0 by Mike Stephenson and others)}
%DO NOT REMOVE NH_DATESUB \date{DATE(%B %-d, %Y)}
\date{May 7, 2023}
\date{June 8, 2023}
\maketitle
@@ -4511,6 +4511,10 @@ Default `{\tt \verb+}+}'.
Prompt for new form whenever any monster changes shape (default off).
Debug mode only.
%.lp
\item[\ib{montelecontrol}]
Prompt for destination whenever any monster gets teleported (default off).
Debug mode only.
%.lp
\item[\ib{mouse\verb+_+support}]
Allow use of the mouse for input and travel.
Valid settings are:

View File

@@ -2820,6 +2820,7 @@ extern void level_tele_trap(struct trap *, unsigned);
extern void rloc_to(struct monst *, coordxy, coordxy);
extern void rloc_to_flag(struct monst *, coordxy, coordxy, unsigned);
extern boolean rloc(struct monst *, unsigned);
extern boolean control_mon_tele(struct monst *, coord *cc, unsigned, boolean);
extern boolean tele_restrict(struct monst *);
extern void mtele_trap(struct monst *, struct trap *, int);
extern int mlevel_tele_trap(struct monst *, struct trap *, boolean, int);

View File

@@ -236,6 +236,7 @@ struct instance_flags {
boolean debug_mongen; /* debug: prevent monster generation */
boolean debug_hunger; /* debug: prevent hunger */
boolean mon_polycontrol; /* debug: control monster polymorphs */
boolean mon_telecontrol; /* debug: control monster teleports */
boolean in_dumplog; /* doing the dumplog right now? */
boolean in_parse; /* is a command being parsed? */
/* suppress terminate during options parsing, for --showpaths */

View File

@@ -454,6 +454,9 @@ static int optfn_##a(int, int, boolean, char *, char *);
NHOPTB(monpolycontrol, Advanced, 0, opt_in, set_wizonly,
Off, Yes, No, No, NoAlias, &iflags.mon_polycontrol, Term_False,
"control monster polymorphs")
NHOPTB(montelecontrol, Advanced, 0, opt_in, set_wizonly,
Off, Yes, No, No, NoAlias, &iflags.mon_telecontrol, Term_False,
"control monster teleport destinations")
NHOPTC(monsters, Advanced, MAXMCLASSES, opt_in, set_in_config,
No, Yes, No, No, NoAlias,
"list of symbols to use for monsters")

View File

@@ -3564,6 +3564,17 @@ mnexto(struct monst *mtmp, unsigned int rlocflags)
deal_with_overcrowding(mtmp);
return;
}
/* wizard-mode player can choose destination by setting 'montelecontrol'
option; enexto()'s value for 'mm' will be the default; 'savemm' is
used to make sure player doesn't choose hero's location and then
answer 'y' to the 'override invalid spot' prompt */
if (iflags.mon_telecontrol) {
coord savemm = mm;
if (!control_mon_tele(mtmp, &mm, rlocflags, FALSE))
mm = savemm;
}
rloc_to_flag(mtmp, mm.x, mm.y, rlocflags);
return;
}
@@ -3595,6 +3606,7 @@ maybe_mnexto(struct monst *mtmp)
if (couldsee(mm.x, mm.y)
/* don't move grid bugs diagonally */
&& (diagok || mm.x == mtmp->mx || mm.y == mtmp->my)) {
/* [this doesn't honor the 'montelecontrol' option] */
rloc_to(mtmp, mm.x, mm.y);
return;
}
@@ -3658,6 +3670,7 @@ mnearto(
newx = mm.x;
newy = mm.y;
}
/* [this doesn't honor the 'montelecontrol' option] */
rloc_to_flag(mtmp, newx, newy, rlocflags);
if (move_other && othermon) {

View File

@@ -1735,6 +1735,16 @@ rloc(
goto found_xy;
}
/* wizard-mode player can choose destination by setting 'montelecontrol'
option; ignored if/when this is arrival of a migrating monster */
if (iflags.mon_telecontrol && mtmp->mx) {
cc.x = mtmp->mx, cc.y = mtmp->my;
if (control_mon_tele(mtmp, &cc, rlocflags, TRUE)) {
x = cc.x, y = cc.y;
goto found_xy;
}
}
/* this used to try randomly 1000 times, then fallback to left-to-right
top-to-bottom exhaustive check; now that the exhaustive check uses
randomized order, reduce the number of random attempts to 50;
@@ -1788,6 +1798,45 @@ rloc(
return TRUE;
}
/* let wizard-mode player choose a teleporting monster's destination */
boolean
control_mon_tele(
struct monst *mon,
coord *cc_p, /* input: default spot; output: player selected spot */
unsigned rlocflags,
boolean via_rloc)
{
char tcbuf[BUFSZ];
if (!isok(cc_p->x, cc_p->y)) {
cc_p->x = mon->mx, cc_p->y = mon->my;
if (!isok(cc_p->x, cc_p->y))
cc_p->x = u.ux, cc_p->y = u.uy;
}
if (!wizard || !iflags.mon_telecontrol)
return FALSE;
pline("Teleport %s @ <%d,%d> where?",
noit_mon_nam(mon), mon->mx, mon->my);
/* getpos '?' will show "Move the cursor to <where to teleport Foo>:" */
Sprintf(tcbuf, "where to teleport %s", noit_mon_nam(mon));
if (getpos(cc_p, FALSE, tcbuf) >= 0 && !u_at(cc_p->x, cc_p->y)) {
if (via_rloc
? rloc_pos_ok(cc_p->x, cc_p->y, mon)
: goodpos(cc_p->x, cc_p->y, mon, rlocflags))
return TRUE;
if (!iflags.debug_fuzzer) {
Sprintf(tcbuf, "<%d,%d> is not considered viable; force anyway?",
mon->mx, mon->my);
if (y_n(tcbuf) == 'y')
return TRUE;
}
}
pline("%s destination.", via_rloc ? "Picking random" : "Using derived");
return FALSE;
}
static void
mvault_tele(struct monst* mtmp)
{