diff --git a/doc/fixes36.3 b/doc/fixes36.3 index 7f7371c8a..640903d5b 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.26 $ $NHDT-Date: 1559088523 2019/05/29 00:08:43 $ +$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.27 $ $NHDT-Date: 1559130050 2019/05/29 11:40:50 $ This fixes36.3 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.2 in May 2019. Please note, however, @@ -32,6 +32,7 @@ if hero dies while a thrown or kicked object is in transit, put that object on the map in case bones data gets saved fix a memory leak that occurred if player used wizard mode to leave and return to the Plane of Air or Plane of Water (not possible in normal play) +free sortloot data if object handling is short-circuited by cockatrice corpse Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository diff --git a/include/hack.h b/include/hack.h index b8a747119..159abb571 100644 --- a/include/hack.h +++ b/include/hack.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 hack.h $NHDT-Date: 1549327459 2019/02/05 00:44:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.102 $ */ +/* NetHack 3.6 hack.h $NHDT-Date: 1559227823 2019/05/30 14:50:23 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.105 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -17,10 +17,7 @@ #define OFF 0 #define BOLT_LIM 8 /* from this distance ranged attacks will be made */ #define MAX_CARR_CAP 1000 /* so that boulders can be heavier */ -#define DUMMY \ - { \ - 0 \ - } +#define DUMMY { 0 } /* array initializer, letting [1..N-1] default */ /* The UNDEFINED macros are used to initialize variables whose initialized value is not relied upon. @@ -278,24 +275,25 @@ typedef struct sortloot_item Loot; #include "extern.h" #endif /* USE_TRAMPOLI */ -/* flags to control makemon() */ +/* flags to control makemon(); goodpos() uses some plus has some of its own */ #define NO_MM_FLAGS 0x00000 /* use this rather than plain 0 */ -#define NO_MINVENT 0x00001 /* suppress minvent when creating mon */ -#define MM_NOWAIT 0x00002 /* don't set STRAT_WAITMASK flags */ -#define MM_NOCOUNTBIRTH \ - 0x00004 /* don't increment born counter (for revival) */ -#define MM_IGNOREWATER 0x00008 /* ignore water when positioning */ -#define MM_ADJACENTOK \ - 0x00010 /* it is acceptable to use adjacent coordinates */ -#define MM_ANGRY 0x00020 /* monster is created angry */ -#define MM_NONAME 0x00040 /* monster is not christened */ -#define MM_EGD 0x00100 /* add egd structure */ -#define MM_EPRI 0x00200 /* add epri structure */ -#define MM_ESHK 0x00400 /* add eshk structure */ -#define MM_EMIN 0x00800 /* add emin structure */ -#define MM_EDOG 0x01000 /* add edog structure */ -#define MM_ASLEEP 0x02000 /* monsters should be generated asleep */ -#define MM_NOGRP 0x04000 /* suppress creation of monster groups */ +#define NO_MINVENT 0x00001 /* suppress minvent when creating mon */ +#define MM_NOWAIT 0x00002 /* don't set STRAT_WAITMASK flags */ +#define MM_NOCOUNTBIRTH 0x00004 /* don't increment born count (for revival) */ +#define MM_IGNOREWATER 0x00008 /* ignore water when positioning */ +#define MM_ADJACENTOK 0x00010 /* acceptable to use adjacent coordinates */ +#define MM_ANGRY 0x00020 /* monster is created angry */ +#define MM_NONAME 0x00040 /* monster is not christened */ +#define MM_EGD 0x00100 /* add egd structure */ +#define MM_EPRI 0x00200 /* add epri structure */ +#define MM_ESHK 0x00400 /* add eshk structure */ +#define MM_EMIN 0x00800 /* add emin structure */ +#define MM_EDOG 0x01000 /* add edog structure */ +#define MM_ASLEEP 0x02000 /* monsters should be generated asleep */ +#define MM_NOGRP 0x04000 /* suppress creation of monster groups */ +/* if more MM_ flag masks are added, skip or renumber the GP_ one(s) */ +#define GP_ALLOW_XY 0x08000 /* [actually used by enexto() to decide whether + * to make an extra call to goodpos()] */ /* flags for make_corpse() and mkcorpstat() */ #define CORPSTAT_NONE 0x00 @@ -327,27 +325,27 @@ typedef struct sortloot_item Loot; #define ALL_FINISHED 0x01 /* called routine already finished the job */ /* flags to control query_objlist() */ -#define BY_NEXTHERE 0x1 /* follow objlist by nexthere field */ -#define AUTOSELECT_SINGLE 0x2 /* if only 1 object, don't ask */ -#define USE_INVLET 0x4 /* use object's invlet */ -#define INVORDER_SORT 0x8 /* sort objects by packorder */ -#define SIGNAL_NOMENU 0x10 /* return -1 rather than 0 if none allowed */ -#define SIGNAL_ESCAPE 0x20 /* return -2 rather than 0 for ESC */ -#define FEEL_COCKATRICE 0x40 /* engage cockatrice checks and react */ -#define INCLUDE_HERO 0x80 /* show hero among engulfer's inventory */ +#define BY_NEXTHERE 0x01 /* follow objlist by nexthere field */ +#define AUTOSELECT_SINGLE 0x02 /* if only 1 object, don't ask */ +#define USE_INVLET 0x04 /* use object's invlet */ +#define INVORDER_SORT 0x08 /* sort objects by packorder */ +#define SIGNAL_NOMENU 0x10 /* return -1 rather than 0 if none allowed */ +#define SIGNAL_ESCAPE 0x20 /* return -2 rather than 0 for ESC */ +#define FEEL_COCKATRICE 0x40 /* engage cockatrice checks and react */ +#define INCLUDE_HERO 0x80 /* show hero among engulfer's inventory */ /* Flags to control query_category() */ /* BY_NEXTHERE used by query_category() too, so skip 0x01 */ -#define UNPAID_TYPES 0x02 -#define GOLD_TYPES 0x04 -#define WORN_TYPES 0x08 -#define ALL_TYPES 0x10 -#define BILLED_TYPES 0x20 -#define CHOOSE_ALL 0x40 -#define BUC_BLESSED 0x80 -#define BUC_CURSED 0x100 +#define UNPAID_TYPES 0x002 +#define GOLD_TYPES 0x004 +#define WORN_TYPES 0x008 +#define ALL_TYPES 0x010 +#define BILLED_TYPES 0x020 +#define CHOOSE_ALL 0x040 +#define BUC_BLESSED 0x080 +#define BUC_CURSED 0x100 #define BUC_UNCURSED 0x200 -#define BUC_UNKNOWN 0x400 +#define BUC_UNKNOWN 0x400 #define BUC_ALLBKNOWN (BUC_BLESSED | BUC_CURSED | BUC_UNCURSED) #define BUCX_TYPES (BUC_ALLBKNOWN | BUC_UNKNOWN) #define ALL_TYPES_SELECTED -2 diff --git a/src/mkmaze.c b/src/mkmaze.c index afcff0027..fc9c24b47 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mkmaze.c $NHDT-Date: 1559088524 2019/05/29 00:08:44 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.71 $ */ +/* NetHack 3.6 mkmaze.c $NHDT-Date: 1559227829 2019/05/30 14:50:29 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.72 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1848,6 +1848,7 @@ boolean ini; for (olist = (struct obj *) cons->list; olist; olist = otmp) { otmp = olist->nexthere; place_object(olist, cons->x, cons->y); + stackobj(olist); } break; } @@ -1855,7 +1856,12 @@ boolean ini; case CONS_MON: { struct monst *mon = (struct monst *) cons->list; - (void) mnearto(mon, cons->x, cons->y, TRUE); + /* mnearto() might fail, and putting the monster into limbo + to try next time hero comes to this level makes no sense + because we can't leave and return (outside wizard mode) */ + if (!mnearto(mon, cons->x, cons->y, TRUE)) { + ; /* ? */ + } break; } diff --git a/src/mon.c b/src/mon.c index 20ee07b74..7a8ec71bf 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1556139724 2019/04/24 21:02:04 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.284 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1559227828 2019/05/30 14:50:28 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.286 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2593,12 +2593,11 @@ struct monst *mtmp; return; } - if (!enexto(&mm, u.ux, u.uy, mtmp->data)) { + if (!enexto(&mm, u.ux, u.uy, mtmp->data) || !isok(mm.x, mm.y)) { + debugpline1("mnexto: sending %s into limbo", m_monnam(mtmp)); m_into_limbo(mtmp); return; } - if (!isok(mm.x, mm.y)) - return; rloc_to(mtmp, mm.x, mm.y); if (!g.in_mklev && (mtmp->mstrategy & STRAT_APPEARMSG)) { mtmp->mstrategy &= ~STRAT_APPEARMSG; /* one chance only */ @@ -2672,9 +2671,7 @@ boolean move_other; /* make sure mtmp gets to x, y! so move m_at(x, y) */ * Migrating_mons that need to be placed will cause * no end of trouble. */ - if (!enexto(&mm, newx, newy, mtmp->data)) - return 0; - if (!isok(mm.x, mm.y)) + if (!enexto(&mm, newx, newy, mtmp->data) || !isok(mm.x, mm.y)) return 0; newx = mm.x; newy = mm.y; @@ -2683,8 +2680,10 @@ boolean move_other; /* make sure mtmp gets to x, y! so move m_at(x, y) */ if (move_other && othermon) { res = 2; /* moving another monster out of the way */ - if (!mnearto(othermon, x, y, FALSE)) /* no 'move_other' this time */ + if (!mnearto(othermon, x, y, FALSE)) { /* no 'move_other' this time */ + debugpline1("mnearto: sending %s into limbo", m_monnam(othermon)); m_into_limbo(othermon); + } } return res; diff --git a/src/pickup.c b/src/pickup.c index 84ac8e448..19be7a71b 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pickup.c $NHDT-Date: 1545785547 2018/12/26 00:52:27 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.222 $ */ +/* NetHack 3.6 pickup.c $NHDT-Date: 1559130050 2019/05/29 11:40:50 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.226 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -182,7 +182,7 @@ int *menu_on_demand; const char *where = 0; char sym, oc_of_sym, *p; - ask_again: + ask_again: oclasses[oclassct = 0] = '\0'; *one_at_a_time = *everything = FALSE; not_everything = filtered = FALSE; @@ -574,7 +574,7 @@ int what; /* should be a long */ &pick_list, PICK_ANY, all_but_uchain); } - menu_pickup: + menu_pickup: n_tried = n; for (n_picked = i = 0; i < n; i++) { res = pickup_object(pick_list[i].item.a_obj, pick_list[i].count, @@ -678,7 +678,7 @@ int what; /* should be a long */ break; n_picked += res; } - end_query: + end_query: ; /* statement required after label */ } @@ -887,6 +887,7 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */ && will_feel_cockatrice(curr, FALSE)) { destroy_nhwindow(win); /* stop the menu and revert */ (void) look_here(0, FALSE); + unsortloot(&sortedolist); return 0; } if ((*allow)(curr)) { @@ -1925,8 +1926,7 @@ reverse_loot() if (g_at(x, y)) pline("Ok, now there is loot here."); } else { - /* find original coffers chest if present, otherwise use nearest one - */ + /* find original coffers chest if present, otherwise use nearest one */ otmp = 0; for (coffers = fobj; coffers; coffers = coffers->nobj) if (coffers->otyp == CHEST) { @@ -2556,7 +2556,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */ * or * is empty. Do what with it? [:irs nq or ?] */ - for (;;) { /* repeats iff '?' or ":' gets chosen */ + for (;;) { /* repeats iff '?' or ':' gets chosen */ outmaybe = (outokay || !g.current_container->cknown); if (!outmaybe) (void) safe_qbuf(qbuf, (char *) 0, " is empty. Do what with it?", @@ -2684,7 +2684,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */ } } -containerdone: + containerdone: if (used) { /* Not completely correct; if we put something in without knowing whatever was already inside, now we suddenly do. That can't diff --git a/src/teleport.c b/src/teleport.c index 956d19453..24a4bd186 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 teleport.c $NHDT-Date: 1553885439 2019/03/29 18:50:39 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.86 $ */ +/* NetHack 3.6 teleport.c $NHDT-Date: 1559227830 2019/05/30 14:50:30 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.87 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -36,7 +36,8 @@ unsigned gpflags; * which could be co-located and thus get restricted a bit too much. * oh well. */ - if (mtmp != &g.youmonst && x == u.ux && y == u.uy + if (x == u.ux && y == u.uy + && mtmp != &g.youmonst && (mtmp != u.ustuck || !u.uswallow) && (!u.usteed || mtmp != u.usteed)) return FALSE; @@ -105,22 +106,24 @@ coord *cc; register xchar xx, yy; struct permonst *mdat; { - return enexto_core(cc, xx, yy, mdat, 0); + return enexto_core(cc, xx, yy, mdat, NO_MM_FLAGS); } boolean enexto_core(cc, xx, yy, mdat, entflags) coord *cc; -register xchar xx, yy; +xchar xx, yy; struct permonst *mdat; unsigned entflags; { #define MAX_GOOD 15 coord good[MAX_GOOD], *good_ptr; int x, y, range, i; - int xmin, xmax, ymin, ymax; + int xmin, xmax, ymin, ymax, rangemax; struct monst fakemon; /* dummy monster */ + boolean allow_xx_yy = (boolean) ((entflags & GP_ALLOW_XY) != 0); + entflags &= ~GP_ALLOW_XY; if (!mdat) { debugpline0("enexto() called with null mdat"); /* default to player's original monster type */ @@ -129,6 +132,13 @@ unsigned entflags; fakemon = cg.zeromonst; set_mon_data(&fakemon, mdat); /* set up for goodpos */ + /* used to use 'if (range > ROWNO && range > COLNO) return FALSE' below, + so effectively 'max(ROWNO, COLNO)' which performs useless iterations + (possibly many iterations if is in the center of the map) */ + xmax = max(xx - 1, (COLNO - 1) - xx); + ymax = max(yy - 0, (ROWNO - 1) - yy); + rangemax = max(xmax, ymax); + /* setup: no suitable spots yet, first iteration checks adjacent spots */ good_ptr = good; range = 1; /* @@ -141,7 +151,7 @@ unsigned entflags; ymin = max(0, yy - range); ymax = min(ROWNO - 1, yy + range); - for (x = xmin; x <= xmax; x++) + for (x = xmin; x <= xmax; x++) { if (goodpos(x, ymin, &fakemon, entflags)) { good_ptr->x = x; good_ptr->y = ymin; @@ -149,38 +159,46 @@ unsigned entflags; if (good_ptr++ == &good[MAX_GOOD - 1]) goto full; } - for (x = xmin; x <= xmax; x++) if (goodpos(x, ymax, &fakemon, entflags)) { good_ptr->x = x; good_ptr->y = ymax; - /* beware of accessing beyond segment boundaries.. */ if (good_ptr++ == &good[MAX_GOOD - 1]) goto full; } - for (y = ymin + 1; y < ymax; y++) + } + /* 3.6.3: this used to use 'ymin+1' which left top row unchecked */ + for (y = ymin; y < ymax; y++) { if (goodpos(xmin, y, &fakemon, entflags)) { good_ptr->x = xmin; good_ptr->y = y; - /* beware of accessing beyond segment boundaries.. */ if (good_ptr++ == &good[MAX_GOOD - 1]) goto full; } - for (y = ymin + 1; y < ymax; y++) if (goodpos(xmax, y, &fakemon, entflags)) { good_ptr->x = xmax; good_ptr->y = y; - /* beware of accessing beyond segment boundaries.. */ if (good_ptr++ == &good[MAX_GOOD - 1]) goto full; } - range++; + } + } while (++range <= rangemax && good_ptr == good); - /* return if we've grown too big (nothing is valid) */ - if (range > ROWNO && range > COLNO) + /* return False if we exhausted 'range' without finding anything */ + if (good_ptr == good) { + /* 3.6.3: earlier versions didn't have the option to try , + and left 'cc' uninitialized when returning False */ + cc->x = xx, cc->y = yy; + /* if every spot other than has failed, try itself */ + if (allow_xx_yy && goodpos(xx, yy, &fakemon, entflags)) { + return TRUE; /* 'cc' is set */ + } else { + debugpline3("enexto(\"%s\",%d,%d) failed", mdat->mname, xx, yy); return FALSE; - } while (good_ptr == good); + } + } -full: + full: + /* we've got between 1 and SIZE(good) candidates; choose one */ i = rn2((int) (good_ptr - good)); cc->x = good[i].x; cc->y = good[i].y; @@ -423,7 +441,7 @@ boolean force_it; return FALSE; } else { Your("leash goes slack."); - release_it: + release_it: m_unleash(mtmp, FALSE); return TRUE; } @@ -792,7 +810,7 @@ level_tele() schar destlev; xchar destdnum; - levTport_menu: + levTport_menu: destlev = 0; destdnum = 0; newlev = (int) print_dungeon(TRUE, &destlev, &destdnum); @@ -860,7 +878,7 @@ level_tele() if (In_quest(&u.uz) && newlev > 0) newlev = newlev + g.dungeons[u.uz.dnum].depth_start - 1; } else { /* involuntary level tele */ - random_levtport: + random_levtport: newlev = random_teleport_level(); if (newlev == depth(&u.uz)) { You1(shudder_for_moment); @@ -1143,7 +1161,7 @@ register int x, y; register int oldx = mtmp->mx, oldy = mtmp->my; boolean resident_shk = mtmp->isshk && inhishop(mtmp); - if (x == mtmp->mx && y == mtmp->my && m_at(x,y) == mtmp) + if (x == mtmp->mx && y == mtmp->my && m_at(x, y) == mtmp) return; /* that was easy */ if (oldx) { /* "pick up" monster */ @@ -1164,11 +1182,11 @@ register int x, y; if (u.ustuck == mtmp) { if (u.uswallow) { - u.ux = x; - u.uy = y; + u_on_newpos(mtmp->mx, mtmp->my); docrt(); - } else - u.ustuck = 0; + } else if (distu(mtmp->mx, mtmp->my) > 2) { + unstuck(mtmp); + } } newsym(x, y); /* update new location */ @@ -1229,7 +1247,7 @@ boolean suppress_impossible; impossible("rloc(): couldn't relocate monster"); return FALSE; -found_xy: + found_xy: rloc_to(mtmp, x, y); return TRUE; } @@ -1238,7 +1256,7 @@ STATIC_OVL void mvault_tele(mtmp) struct monst *mtmp; { - register struct mkroom *croom = search_special(VAULT); + struct mkroom *croom = search_special(VAULT); coord c; if (croom && somexy(croom, &c) && goodpos(c.x, c.y, mtmp, 0)) {