From: Pasi Kallinen Date: Wed, 28 Jul 2021 16:11:30 +0000 (+0300) Subject: Start of movement tests X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=65b320de39f9d9d268f92c5ebcceaa44eee9737e;p=nethack Start of movement tests Adds the following lua functions: - nh.pushkey("x") Pushes a key into the command queue. Support is spotty, currently only the keys handled in rhack. - nh.doturn() Runs one turn of main loop, or if optional boolean param is true, until g.multi == 0 - nh.monster_generation(false) Disable monster generation, and kill off all monsters. Adds a testmove.lua script to test hero movement. Currently covers only hjklyubn and HJKLYUBN. --- diff --git a/include/extern.h b/include/extern.h index f512bd581..f0d3292e0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -18,6 +18,7 @@ extern char *fmt_ptr(const void *); /* ### allmain.c ### */ +extern void moveloop_core(void); extern void moveloop(boolean); extern void stop_occupation(void); extern void display_gamewindows(void); diff --git a/include/flag.h b/include/flag.h index f403569de..c8a756714 100644 --- a/include/flag.h +++ b/include/flag.h @@ -204,6 +204,7 @@ struct instance_flags { boolean window_inited; /* true if init_nhwindows() completed */ boolean vision_inited; /* true if vision is ready */ boolean sanity_check; /* run sanity checks */ + boolean debug_mongen; /* debug: prevent monster generation */ boolean mon_polycontrol; /* debug: control monster polymorphs */ boolean in_dumplog; /* doing the dumplog right now? */ boolean in_parse; /* is a command being parsed? */ diff --git a/src/allmain.c b/src/allmain.c index 2e77900f1..1363915ec 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -14,7 +14,6 @@ static void moveloop_preamble(boolean); static void u_calc_moveamt(int); -static void moveloop_core(void); #ifdef POSITIONBAR static void do_positionbar(void); #endif @@ -133,17 +132,17 @@ u_calc_moveamt(int wtcap) g.youmonst.movement = 0; } -static void -moveloop_core(void) -{ #if defined(MICRO) || defined(WIN32) - char ch; - int abort_lev; +static int mvl_abort_lev; #endif - int wtcap = 0, change = 0; +static int mvl_wtcap = 0; +static int mvl_change = 0; + +void +moveloop_core(void) +{ boolean monscanmove = FALSE; - for (;;) { #ifdef SAFERHANGUP if (g.program_state.done_hup) end_of_input(); @@ -158,7 +157,7 @@ moveloop_core(void) g.youmonst.movement -= NORMAL_SPEED; do { /* hero can't move this turn loop */ - wtcap = encumber_msg(); + mvl_wtcap = encumber_msg(); g.context.mon_moving = TRUE; do { @@ -190,7 +189,7 @@ moveloop_core(void) (void) makemon((struct permonst *) 0, 0, 0, NO_MM_FLAGS); - u_calc_moveamt(wtcap); + u_calc_moveamt(mvl_wtcap); settrack(); g.monstermoves++; /* [obsolete (for a long time...)] */ @@ -236,24 +235,24 @@ moveloop_core(void) */ if (u.uinvulnerable) { /* for the moment at least, you're in tiptop shape */ - wtcap = UNENCUMBERED; + mvl_wtcap = UNENCUMBERED; } else if (!Upolyd ? (u.uhp < u.uhpmax) : (u.mh < u.mhmax || g.youmonst.data->mlet == S_EEL)) { /* maybe heal */ - regen_hp(wtcap); + regen_hp(mvl_wtcap); } /* moving around while encumbered is hard work */ - if (wtcap > MOD_ENCUMBER && u.umoved) { - if (!(wtcap < EXT_ENCUMBER ? g.moves % 30 + if (mvl_wtcap > MOD_ENCUMBER && u.umoved) { + if (!(mvl_wtcap < EXT_ENCUMBER ? g.moves % 30 : g.moves % 10)) { overexert_hp(); } } if (u.uen < u.uenmax - && ((wtcap < MOD_ENCUMBER + && ((mvl_wtcap < MOD_ENCUMBER && (!(g.moves % ((MAXULEV + 8 - u.ulevel) * (Role_if(PM_WIZARD) ? 3 : 4) / 6)))) || Energy_regeneration)) { @@ -281,22 +280,22 @@ moveloop_core(void) } } /* delayed change may not be valid anymore */ - if ((change == 1 && !Polymorph) - || (change == 2 && u.ulycn == NON_PM)) - change = 0; + if ((mvl_change == 1 && !Polymorph) + || (mvl_change == 2 && u.ulycn == NON_PM)) + mvl_change = 0; if (Polymorph && !rn2(100)) - change = 1; + mvl_change = 1; else if (u.ulycn >= LOW_PM && !Upolyd && !rn2(80 - (20 * night()))) - change = 2; - if (change && !Unchanging) { + mvl_change = 2; + if (mvl_change && !Unchanging) { if (g.multi >= 0) { stop_occupation(); - if (change == 1) + if (mvl_change == 1) polyself(0); else you_were(); - change = 0; + mvl_change = 0; } } } @@ -418,21 +417,23 @@ moveloop_core(void) if (g.multi >= 0 && g.occupation) { #if defined(MICRO) || defined(WIN32) - abort_lev = 0; + mvl_abort_lev = 0; if (kbhit()) { + char ch; + if ((ch = pgetchar()) == ABORT) - abort_lev++; + mvl_abort_lev++; else pushch(ch); } - if (!abort_lev && (*g.occupation)() == 0) + if (!mvl_abort_lev && (*g.occupation)() == 0) #else if ((*g.occupation)() == 0) #endif g.occupation = 0; if ( #if defined(MICRO) || defined(WIN32) - abort_lev || + mvl_abort_lev || #endif monster_nearby()) { stop_occupation(); @@ -442,7 +443,7 @@ moveloop_core(void) if (!(++g.occtime % 7)) display_nhwindow(WIN_MAP, FALSE); #endif - continue; + return; } if (iflags.sanity_check || iflags.debug_fuzzer) @@ -460,7 +461,7 @@ moveloop_core(void) if (!g.multi) { /* lookaround may clear multi */ g.context.move = 0; - continue; + return; } if (g.context.mv) { if (g.multi < COLNO && !--g.multi) @@ -492,14 +493,15 @@ moveloop_core(void) /* [should this be flush_screen() instead?] */ display_nhwindow(WIN_MAP, FALSE); } - } } void moveloop(boolean resuming) { moveloop_preamble(resuming); - moveloop_core(); + for (;;) { + moveloop_core(); + } } #define U_CAN_REGEN() (Regeneration || (Sleepy && u.usleep)) diff --git a/src/makemon.c b/src/makemon.c index 5ed63ab64..067ef4381 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1123,6 +1123,9 @@ makemon(register struct permonst *ptr, fakemon = cg.zeromonst; cc.x = cc.y = 0; + if (iflags.debug_mongen) + return (struct monst *) 0; + /* if caller wants random location, do it here */ if (x == 0 && y == 0) { fakemon.data = ptr; /* set up for goodpos */ diff --git a/src/nhlua.c b/src/nhlua.c index 96aca7484..e2704c3a5 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -19,6 +19,9 @@ static int nhl_dump_fmtstr(lua_State *); #endif /* DUMPLOG */ static int nhl_dnum_name(lua_State *); static int nhl_stairways(lua_State *); +static int nhl_pushkey(lua_State *); +static int nhl_doturn(lua_State *); +static int nhl_monster_generation(lua_State *); static int nhl_test(lua_State *); static int nhl_getmap(lua_State *); static void nhl_add_table_entry_bool(lua_State *, const char *, boolean); @@ -936,6 +939,68 @@ nhl_test(lua_State *L) return 1; } +/* push a key into command queue */ +/* nh.pushkey("i"); */ +static int +nhl_pushkey(lua_State *L) +{ + int argc = lua_gettop(L); + + if (argc == 1) { + const char *key = luaL_checkstring(L, 1); + + cmdq_add_key(key[0]); + } + + return 0; +} + +/* do a turn of moveloop, or until g.multi is done if param is true. */ +/* nh.doturn(); nh.doturn(true); */ +static int +nhl_doturn(lua_State *L) +{ + int argc = lua_gettop(L); + boolean domulti = FALSE; + + if (argc == 1) + domulti = lua_toboolean(L, 1); + + do { + moveloop_core(); + } while (domulti && g.multi); + + return 0; +} + +/* disable or enable monster generation. debugging use only. */ +/* disabling also kills all monsters on current level. */ +/* local prevvalue = nh.monster_generation(false); */ +static int +nhl_monster_generation(lua_State *L) +{ + int argc = lua_gettop(L); + boolean val = !iflags.debug_mongen; /* value in lua is negated */ + + if (argc == 1) { + iflags.debug_mongen = !lua_toboolean(L, 1); + if (iflags.debug_mongen) { + register struct monst *mtmp, *mtmp2; + + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp)) + continue; + mongone(mtmp); + } + } + } + + lua_pushboolean(L, val); + return 1; +} + + static const struct luaL_Reg nhl_functions[] = { {"test", nhl_test}, @@ -967,6 +1032,9 @@ static const struct luaL_Reg nhl_functions[] = { #endif /* DUMPLOG */ {"dnum_name", nhl_dnum_name}, {"stairways", nhl_stairways}, + {"pushkey", nhl_pushkey}, + {"doturn", nhl_doturn}, + {"monster_generation", nhl_monster_generation}, {NULL, NULL} }; diff --git a/test/testmove.lua b/test/testmove.lua new file mode 100644 index 000000000..434b816a2 --- /dev/null +++ b/test/testmove.lua @@ -0,0 +1,65 @@ + +-- Tests for moving the hero + +-- TODO: running stops if hero walks over stairs -> test fails. +-- prevent stair generation? check where the stairs are? + +nh.parse_config("OPTIONS=number_pad:0"); +nh.parse_config("OPTIONS=runmode:teleport"); + +local POS = { x = 10, y = 05 }; + +function initlev() + nh.monster_generation(false); + des.level_flags("noflip"); + des.reset_level(); + des.level_init({ style = "solidfill", fg = ".", lit = true }); + des.teleport_region({ region = {POS.x,POS.y,POS.x,POS.y}, region_islev = true, dir="both" }); + des.finalize_level(); +end + +local basicmoves = { + h = { dx = -1, dy = 0 }, + j = { dx = 0, dy = 1 }, + k = { dx = 0, dy = -1 }, + l = { dx = 1, dy = 0 }, + y = { dx = -1, dy = -1 }, + u = { dx = 1, dy = -1 }, + b = { dx = -1, dy = 1 }, + n = { dx = 1, dy = 1 }, + H = { x = 2, y = POS.y }, + J = { x = POS.x, y = nhc.ROWNO-1 }, + K = { x = POS.x, y = 0 }, + L = { x = nhc.COLNO-2, y = POS.y }, + Y = { x = POS.x - POS.y, y = 0 }, + U = { x = POS.x + POS.y, y = 0 }, + B = { x = 2, y = 13 }, + N = { x = 25, y = nhc.ROWNO-1 }, +}; + + +for k, v in pairs(basicmoves) do + initlev(); + + local x = u.ux; + local y = u.uy; + + nh.pushkey(k); + nh.doturn(true); + + if (v.dx ~= nil) then + if (not (x == u.ux - v.dx and y == u.uy - v.dy)) then + error(string.format("Move: key '%s' gave (%i,%i), should have been (%i,%i)", + k, u.ux, u.uy, u.ux - v.dx, u.uy - v.dy)); + return; + end + elseif (v.x ~= nil) then + if (not (u.ux == v.x and u.uy == v.y)) then + error(string.format("Move: key '%s' gave (%i,%i), should have been (%i,%i)", + k, u.ux, u.uy, v.x, v.y)); + return; + end + end +end + +initlev();