diff --git a/dat/opthelp b/dat/opthelp index 109454fa0..f542d9da5 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -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 diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 74d649b07..3a5c91424 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -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: diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 2959aaff0..98f38da92 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -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: diff --git a/include/extern.h b/include/extern.h index 6076f585d..0c982c6be 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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); diff --git a/include/flag.h b/include/flag.h index 590dc927a..a17e67439 100644 --- a/include/flag.h +++ b/include/flag.h @@ -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 */ diff --git a/include/optlist.h b/include/optlist.h index 33a7e2ec7..058c3f9eb 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -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") diff --git a/src/mon.c b/src/mon.c index e1552e6e5..f3cf246fa 100644 --- a/src/mon.c +++ b/src/mon.c @@ -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) { diff --git a/src/teleport.c b/src/teleport.c index def2138b4..1f98f8b0c 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -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 :" */ + 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) {