diff --git a/doc/fixes36.3 b/doc/fixes36.3 index 6d77daebb..bf389167d 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.30 $ $NHDT-Date: 1559382745 2019/06/01 09:52:25 $ +$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.31 $ $NHDT-Date: 1559422205 2019/06/01 20:50:05 $ 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, @@ -37,6 +37,7 @@ on the Plane of Water, make water go all the way to the edges of the level on the Plane of Air, make clouds disrupt line of sight along the edges have '&' (whatdoes) command describe movement keystrokes instead of reporting them as unknown or showing their number_pad action when that is Off +some improvement to the handling of endgame levels filling up with monsters Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index dd32c6091..dd4ce99d5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1558485640 2019/05/22 00:40:40 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.706 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1559422206 2019/06/01 20:50:06 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.707 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1394,6 +1394,7 @@ E int FDECL(can_carry, (struct monst *, struct obj *)); E int FDECL(mfndpos, (struct monst *, coord *, long *, long)); E boolean FDECL(monnear, (struct monst *, int, int)); E void NDECL(dmonsfree); +E void FDECL(elemental_clog, (struct monst *)); E int FDECL(mcalcmove, (struct monst *)); E void NDECL(mcalcdistress); E void FDECL(replmon, (struct monst *, struct monst *)); diff --git a/include/monst.h b/include/monst.h index f9be37f5d..328bb4996 100644 --- a/include/monst.h +++ b/include/monst.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 monst.h $NHDT-Date: 1550524559 2019/02/18 21:15:59 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.28 $ */ +/* NetHack 3.6 monst.h $NHDT-Date: 1559422218 2019/06/01 20:50:18 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.31 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2016. */ /* NetHack may be freely redistributed. See license for details. */ @@ -50,6 +50,17 @@ enum m_ap_types { M_AP_MONSTER = 3 /* a monster; mostly used for cloned Wizard */ }; +#define MON_FLOOR 0x00 +#define MON_OFFMAP 0x01 +#define MON_DETACH 0x02 +#define MON_MIGRATING 0x04 +#define MON_LIMBO 0x08 +#define MON_BUBBLEMOVE 0x10 +#define MON_ENDGAME_FREE 0x20 +#define MON_ENDGAME_MIGR 0x40 +#define MON_OBLITERATE 0x80 +#define MSTATE_MASK 0xFF + #define M_AP_TYPMASK 0x7 #define M_AP_F_DKNOWN 0x8 #define U_AP_TYPE (youmonst.m_ap_type & M_AP_TYPMASK) @@ -149,8 +160,8 @@ struct monst { long mtrapseen; /* bitmap of traps we've been trapped in */ long mlstmv; /* for catching up with lost time */ long mspare1; +#define mstate mspare1 /* only for debug exam right now, not code flow */ struct obj *minvent; /* mon's inventory */ - struct obj *mw; /* mon's weapon */ long misc_worn_check; /* mon's wornmask */ xchar weapon_check; /* flag for whether to try switching weapons */ diff --git a/src/mkmaze.c b/src/mkmaze.c index bdab684ef..f516e6927 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mkmaze.c $NHDT-Date: 1559299316 2019/05/31 10:41:56 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.73 $ */ +/* NetHack 3.6 mkmaze.c $NHDT-Date: 1559422240 2019/06/01 20:50:40 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.74 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -650,14 +650,14 @@ unsigned long mflags; /* once in a blue moon, he won't be at the very bottom */ if (!rn2(40)) nlev--; - mtmp->mspare1 = MIGR_LEFTOVERS; + mtmp->mspare1 |= MIGR_LEFTOVERS; } else { nlev = rn2((max_depth - cur_depth) + 1) + cur_depth; if (nlev == cur_depth) nlev++; if (nlev > max_depth) nlev = max_depth; - mtmp->mspare1 = 0L; + mtmp->mspare1 = (mtmp->mspare1 & ~MIGR_LEFTOVERS); } get_level(&dest, nlev); migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0); @@ -1466,6 +1466,7 @@ movebubbles() newsym(x, y); /* clean up old position */ mon->mx = mon->my = 0; + mon->mstate |= MON_BUBBLEMOVE; } if (!u.uswallow && x == u.ux && y == u.uy) { cons = (struct container *) alloc(sizeof *cons); @@ -1897,12 +1898,10 @@ boolean ini; case CONS_MON: { struct monst *mon = (struct monst *) cons->list; - /* 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)) { - ; /* ? */ - } + /* mnearto() might fail. We can jump right to elemental_clog + from here rather than deal_with_overcrowding() */ + if (!mnearto(mon, cons->x, cons->y, TRUE)) + elemental_clog(mon); break; } diff --git a/src/mon.c b/src/mon.c index de07a96a6..7aecf116c 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1559227828 2019/05/30 14:50:28 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.286 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1559422247 2019/06/01 20:50:47 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.287 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -28,6 +28,9 @@ STATIC_DCL struct permonst *FDECL(accept_newcham_form, (int)); STATIC_DCL struct obj *FDECL(make_corpse, (struct monst *, unsigned)); STATIC_DCL void FDECL(m_detach, (struct monst *, struct permonst *)); STATIC_DCL void FDECL(lifesaved_monster, (struct monst *)); +STATIC_DCL void FDECL(migrate_mon, (struct monst *, XCHAR_P, XCHAR_P)); +STATIC_DCL boolean FDECL(ok_to_obliterate, (struct monst *)); +STATIC_DCL void FDECL(deal_with_overcrowding, (struct monst *)); /* note: duplicated in dog.c */ #define LEVEL_SPECIFIC_NOCORPSE(mdat) \ @@ -614,7 +617,8 @@ register struct monst *mtmp; xkilled(mtmp, XKILL_NOMSG); if (!DEADMONSTER(mtmp)) { water_damage_chain(mtmp->minvent, FALSE); - (void) rloc(mtmp, FALSE); + if (!rloc(mtmp, TRUE)) + deal_with_overcrowding(mtmp); return 0; } return 1; @@ -1601,7 +1605,9 @@ dmonsfree() { struct monst **mtmp, *freetmp; int count = 0; + char buf[QBUFSZ]; + buf[0] = '\0'; for (mtmp = &fmon; *mtmp;) { freetmp = *mtmp; if (DEADMONSTER(freetmp) && !freetmp->isgd) { @@ -1613,9 +1619,11 @@ dmonsfree() mtmp = &(freetmp->nmon); } - if (count != iflags.purge_monsters) - impossible("dmonsfree: %d removed doesn't match %d pending", - count, iflags.purge_monsters); + if (count != iflags.purge_monsters) { + describe_level(buf); + impossible("dmonsfree: %d removed doesn't match %d pending on %s", + count, iflags.purge_monsters, buf); + } iflags.purge_monsters = 0; } @@ -1791,8 +1799,13 @@ void dealloc_monst(mon) struct monst *mon; { - if (mon->nmon) - panic("dealloc_monst with nmon"); + char buf[QBUFSZ]; + + buf[0] = '\0'; + if (mon->nmon) { + describe_level(buf); + panic("dealloc_monst with nmon on %s", buf); + } if (mon->mextra) dealloc_mextra(mon); free((genericptr_t) mon); @@ -1834,6 +1847,11 @@ struct permonst *mptr; /* reflects mtmp->data _prior_ to mtmp's death */ shkgone(mtmp); if (mtmp->wormno) wormgone(mtmp); + if (In_endgame(&u.uz)) { + mtmp->mx = mtmp->my = 0; + mtmp->mstate |= MON_ENDGAME_FREE; + } + mtmp->mstate |= MON_DETACH; iflags.purge_monsters++; } @@ -2573,10 +2591,125 @@ struct monst *mtmp; void m_into_limbo(mtmp) struct monst *mtmp; +{ + xchar target_lev = ledger_no(&u.uz), xyloc = MIGR_APPROX_XY; + + mtmp->mstate |= MON_LIMBO; + migrate_mon(mtmp, target_lev, xyloc); +} + +STATIC_OVL void +migrate_mon(mtmp, target_lev, xyloc) +struct monst *mtmp; +xchar target_lev, xyloc; { unstuck(mtmp); mdrop_special_objs(mtmp); - migrate_to_level(mtmp, ledger_no(&u.uz), MIGR_APPROX_XY, (coord *) 0); + migrate_to_level(mtmp, target_lev, xyloc, (coord *) 0); + mtmp->mstate |= MON_MIGRATING; +} + +STATIC_OVL boolean +ok_to_obliterate(mtmp) +struct monst *mtmp; +{ + /* + * Add checks for monsters that should not be obliterated + * here (return FALSE). + */ + if (mtmp->data == &mons[PM_WIZARD_OF_YENDOR] || is_rider(mtmp->data) + || has_emin(mtmp) || has_epri(mtmp) || has_eshk(mtmp)) + return FALSE; + return TRUE; +} + +void +elemental_clog(mon) +struct monst *mon; +{ + int m_lev = 0; + static long msgmv = 0L; + struct monst *mtmp, *m1, *m2, *m3, *m4, *m5, *zm; + + if (In_endgame(&u.uz)) { + m1 = m2 = m3 = m4 = m5 = zm = (struct monst *) 0; + if (!msgmv || (moves - msgmv) > 200L) { + if (!msgmv || rn2(2)) + You("feel besieged."); + msgmv = moves; + } + /* + * m1 an elemental from another plane. + * m2 an elemental from this plane. + * m3 the least powerful monst encountered in loop so far. + * m4 some other non-tame monster. + * m5 a pet. + */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) + continue; + if (mtmp->mx == 0 && mtmp->my == 0) { + /* DEBUG ONLY - PLEASE REMOVE LATER */ + if ((mtmp->mstate & 0x7) == MON_DETACH) + pline("skipping m_detach() monster"); + continue; + } + if (mon_has_amulet(mtmp) || !ok_to_obliterate(mtmp)) + continue; + if (mtmp->data->mlet == S_ELEMENTAL) { + if (!is_home_elemental(mtmp->data)) { + if (!m1) + m1 = mtmp; + } else { + if (!m2) + m2 = mtmp; + } + } else { + if (!mtmp->mtame) { + if (!m_lev || mtmp->m_lev < m_lev) { + m_lev = mtmp->m_lev; + m3 = mtmp; + } else if (!m4) { + m4 = mtmp; + } + } else { + if (!m5) + m5 = mtmp; + break; + } + } + } + mtmp = m1 ? m1 : m2 ? m2 : m3 ? m3 : m4 ? m4 : m5 ? m5 : zm; + if (mtmp) { + int mx = mtmp->mx, my = mtmp->my; + + mtmp->mstate |= MON_OBLITERATE; + mongone(mtmp); + mtmp->mx = mtmp->my = 0; + rloc_to(mon, mx, my); + } else { + /* last resort - migrate mon to the next plane */ + if (Is_waterlevel(&u.uz) || Is_firelevel(&u.uz) || Is_earthlevel(&u.uz)) { + /* try sending mon on to the next plane */ + xchar target_lev = 0, xyloc = 0; + struct trap *trap = ftrap; + + while (trap) { + if (trap->ttyp == MAGIC_PORTAL) + break; + trap = trap->ntrap; + } + if (trap) { + target_lev = ledger_no(&trap->dst); + xyloc = MIGR_RANDOM; + } + if (target_lev) { + mon->mstate |= MON_ENDGAME_MIGR; + migrate_mon(mon, target_lev, xyloc); + } + } + } + } } /* make monster mtmp next to you (if possible); @@ -2596,8 +2729,7 @@ struct monst *mtmp; } 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); + deal_with_overcrowding(mtmp); return; } rloc_to(mtmp, mm.x, mm.y); @@ -2610,6 +2742,19 @@ struct monst *mtmp; return; } +void +deal_with_overcrowding(mtmp) +struct monst *mtmp; +{ + if (In_endgame(&u.uz)) { + debugpline1("overcrowding: elemental_clog on %s", m_monnam(mtmp)); + elemental_clog(mtmp); + } else { + debugpline1("overcrowding: sending %s into limbo", m_monnam(mtmp)); + m_into_limbo(mtmp); + } +} + /* like mnexto() but requires destination to be directly accessible */ void maybe_mnexto(mtmp) @@ -2664,6 +2809,7 @@ boolean move_other; /* make sure mtmp gets to x, y! so move m_at(x, y) */ remove_monster(x, y); othermon->mx = othermon->my = 0; /* 'othermon' is not on the map */ + othermon->mstate |= MON_OFFMAP; } newx = x; @@ -2673,8 +2819,16 @@ 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) || !isok(mm.x, mm.y)) + if (!enexto(&mm, newx, newy, mtmp->data) || !isok(mm.x, mm.y)) { + if (othermon) { + /* othermon already had its mx, my set to 0 above + * and this would shortly cause a sanity check to fail + * if we just return 0 here. The caller only possesses + * awareness of mtmp, not othermon. */ + deal_with_overcrowding(othermon); + } return 0; + } newx = mm.x; newy = mm.y; } @@ -2682,10 +2836,8 @@ 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 */ - debugpline1("mnearto: sending %s into limbo", m_monnam(othermon)); - m_into_limbo(othermon); - } + if (!mnearto(othermon, x, y, FALSE)) /* no 'move_other' this time */ + deal_with_overcrowding(othermon); } return res; diff --git a/src/steed.c b/src/steed.c index f7ad61024..c25513306 100644 --- a/src/steed.c +++ b/src/steed.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 steed.c $NHDT-Date: 1545441042 2018/12/22 01:10:42 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.62 $ */ +/* NetHack 3.6 steed.c $NHDT-Date: 1559422254 2019/06/01 20:50:54 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.64 $ */ /* Copyright (c) Kevin Hugo, 1998-1999. */ /* NetHack may be freely redistributed. See license for details. */ @@ -742,23 +742,35 @@ place_monster(mon, x, y) struct monst *mon; int x, y; { + char buf[QBUFSZ]; + + buf[0] = '\0'; /* normal map bounds are <1..COLNO-1,0..ROWNO-1> but sometimes vault guards (either living or dead) are parked at <0,0> */ if (!isok(x, y) && (x != 0 || y != 0 || !mon->isgd)) { - impossible("trying to place monster at <%d,%d>", x, y); + describe_level(buf); + impossible("trying to place monster at <%d,%d> mstate:%x on %s", + x, y, mon->mstate, buf); x = y = 0; } if (mon == u.usteed /* special case is for convoluted vault guard handling */ || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) { - impossible("placing %s onto map?", - (mon == u.usteed) ? "steed" : "defunct monster"); + describe_level(buf); + impossible("placing %s onto map, mstate:%x, on %s?", + (mon == u.usteed) ? "steed" : "defunct monster", + mon->mstate, buf); return; } - if (level.monsters[x][y]) - impossible("placing monster over another at <%d,%d>?", x, y); + if (level.monsters[x][y]) { + describe_level(buf); + impossible("placing monster over another at <%d,%d>, mstates:%x %x on %s?", + x, y, level.monsters[x][y]->mstate, mon->mstate, buf); + } mon->mx = x, mon->my = y; level.monsters[x][y] = mon; + mon->mstate &= ~(MON_OFFMAP | MON_MIGRATING | MON_LIMBO | MON_BUBBLEMOVE + | MON_ENDGAME_FREE | MON_ENDGAME_MIGR); } /*steed.c*/