Merge branch 'NetHack-3.7' into NetHack-3.7-Jan2020

This commit is contained in:
nhmall
2020-01-27 08:52:17 -05:00
25 changed files with 1438 additions and 530 deletions

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 allmain.c $NHDT-Date: 1578448653 2020/01/08 01:57:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.137 $ */
/* NetHack 3.6 allmain.c $NHDT-Date: 1580044340 2020/01/26 13:12:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.138 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -125,7 +125,7 @@ boolean resuming;
to skip dead monsters here because they will have
been purged at end of their previous round of moving */
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
mtmp->movement += mcalcmove(mtmp);
mtmp->movement += mcalcmove(mtmp, TRUE);
/* occasionally add another monster; since this takes
place after movement has been allotted, the new
@@ -139,7 +139,7 @@ boolean resuming;
/* calculate how much time passed. */
if (u.usteed && u.umoved) {
/* your speed doesn't augment steed's speed */
moveamt = mcalcmove(u.usteed);
moveamt = mcalcmove(u.usteed, TRUE);
} else {
moveamt = g.youmonst.data->mmove;

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 mon.c $NHDT-Date: 1577759850 2019/12/31 02:37:30 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.319 $ */
/* NetHack 3.6 mon.c $NHDT-Date: 1580044343 2020/01/26 13:12:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.320 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Derek S. Ray, 2015. */
/* NetHack may be freely redistributed. See license for details. */
@@ -633,9 +633,11 @@ register struct monst *mtmp;
return 0;
}
/* calculate 'mon's movement for current turn; called from moveloop() */
int
mcalcmove(mon)
mcalcmove(mon, m_moving)
struct monst *mon;
boolean m_moving; /* True: adjust for moving; False: just adjust for speed */
{
int mmove = mon->data->mmove;
int mmove_adj;
@@ -656,17 +658,18 @@ struct monst *mon;
mmove = ((rn2(2) ? 4 : 5) * mmove) / 3;
}
/* Randomly round the monster's speed to a multiple of NORMAL_SPEED.
This makes it impossible for the player to predict when they'll get
a free turn (thus preventing exploits like "melee kiting"), while
retaining guarantees about shopkeepers not being outsped by a
normal-speed player, normal-speed players being unable to open up
a gap when fleeing a normal-speed monster, etc. */
mmove_adj = mmove % NORMAL_SPEED;
mmove -= mmove_adj;
if (rn2(NORMAL_SPEED) < mmove_adj)
mmove += NORMAL_SPEED;
if (m_moving) {
/* Randomly round the monster's speed to a multiple of NORMAL_SPEED.
This makes it impossible for the player to predict when they'll
get a free turn (thus preventing exploits like "melee kiting"),
while retaining guarantees about shopkeepers not being outsped
by a normal-speed player, normal-speed players being unable
to open up a gap when fleeing a normal-speed monster, etc. */
mmove_adj = mmove % NORMAL_SPEED;
mmove -= mmove_adj;
if (rn2(NORMAL_SPEED) < mmove_adj)
mmove += NORMAL_SPEED;
}
return mmove;
}

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 objnam.c $NHDT-Date: 1579261291 2020/01/17 11:41:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.288 $ */
/* NetHack 3.7 objnam.c $NHDT-Date: 1580070220 2020/01/26 20:23:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.291 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
@@ -2952,8 +2952,8 @@ char *bp, *p;
int locked, trapped;
{
struct rm *lev;
boolean madeterrain = FALSE, badterrain = FALSE;
int trap, x = u.ux, y = u.uy;
boolean madeterrain = FALSE, badterrain = FALSE, didblock;
int trap, oldtyp, x = u.ux, y = u.uy;
for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) {
struct trap *t;
@@ -2978,21 +2978,25 @@ int locked, trapped;
/* furniture and terrain (use at your own risk; can clobber stairs
or place furniture on existing traps which shouldn't be allowed) */
lev = &levl[x][y];
oldtyp = lev->typ;
didblock = does_block(x, y, lev);
p = eos(bp);
if (!BSTRCMPI(bp, p - 8, "fountain")) {
lev->typ = FOUNTAIN;
g.level.flags.nfountains++;
if (!strncmpi(bp, "magic ", 6))
lev->blessedftn = 1;
lev->looted = 0; /* overlays 'flags' */
lev->blessedftn = !strncmpi(bp, "magic ", 6);
pline("A %sfountain.", lev->blessedftn ? "magic " : "");
madeterrain = TRUE;
} else if (!BSTRCMPI(bp, p - 6, "throne")) {
lev->typ = THRONE;
lev->looted = 0; /* overlays 'flags' */
pline("A throne.");
madeterrain = TRUE;
} else if (!BSTRCMPI(bp, p - 4, "sink")) {
lev->typ = SINK;
g.level.flags.nsinks++;
lev->looted = 0; /* overlays 'flags' */
pline("A sink.");
madeterrain = TRUE;
@@ -3000,6 +3004,7 @@ int locked, trapped;
} else if (!BSTRCMPI(bp, p - 4, "pool")
|| !BSTRCMPI(bp, p - 4, "moat")) {
lev->typ = !BSTRCMPI(bp, p - 4, "pool") ? POOL : MOAT;
lev->flags = 0;
del_engr_at(x, y);
pline("A %s.", (lev->typ == POOL) ? "pool" : "moat");
/* Must manually make kelp! */
@@ -3009,6 +3014,7 @@ int locked, trapped;
/* also matches "molten lava" */
} else if (!BSTRCMPI(bp, p - 4, "lava")) {
lev->typ = LAVAPOOL;
lev->flags = 0;
del_engr_at(x, y);
pline("A pool of molten lava.");
if (!(Levitation || Flying))
@@ -3028,29 +3034,51 @@ int locked, trapped;
al = A_NONE;
else /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */
al = !rn2(6) ? A_NONE : (rn2((int) A_LAWFUL + 2) - 1);
lev->altarmask = Align2amask(al);
lev->altarmask = Align2amask(al); /* overlays 'flags' */
pline("%s altar.", An(align_str(al)));
madeterrain = TRUE;
} else if (!BSTRCMPI(bp, p - 5, "grave")
|| !BSTRCMPI(bp, p - 9, "headstone")) {
make_grave(x, y, (char *) 0);
pline("%s.", IS_GRAVE(lev->typ) ? "A grave"
: "Can't place a grave here");
madeterrain = TRUE;
if (IS_GRAVE(lev->typ)) {
lev->looted = 0; /* overlays 'flags' */
lev->disturbed = !strncmpi(bp, "disturbed ", 10);
pline("A %sgrave.", lev->disturbed ? "disturbed " : "");
madeterrain = TRUE;
} else {
pline("Can't place a grave here");
badterrain = TRUE;
}
} else if (!BSTRCMPI(bp, p - 4, "tree")) {
lev->typ = TREE;
lev->looted = 0; /* overlays 'flags' */
pline("A tree.");
block_point(x, y);
madeterrain = TRUE;
} else if (!BSTRCMPI(bp, p - 4, "bars")) {
lev->typ = IRONBARS;
lev->flags = 0;
/* [FIXME: if this isn't a wall or door location where 'horizontal'
is already set up, that should be calculated for this spot.
Unforutnately, it can be tricky; placing one in open space
and then another adjacent might need to recalculate first one.] */
pline("Iron bars.");
madeterrain = TRUE;
} else if (!BSTRCMPI(bp, p - 5, "cloud")) {
lev->typ = CLOUD;
lev->flags = 0;
pline("A cloud.");
madeterrain = TRUE;
} else if (!BSTRCMPI(bp, p - 11, "secret door")) {
if (lev->typ == DOOR
|| (IS_WALL(lev->typ) && lev->typ != DBWALL)) {
/* require door or wall so that the 'horizontal' flag will
already have the correct value (it will matter once the
secret door is discovered and becomes a regular door);
player might choose to put SDOOR on top of existing SDOOR
to control its trapped state; iron bars are surrogate walls */
if (lev->typ == DOOR || lev->typ == SDOOR
|| (IS_WALL(lev->typ) && lev->typ != DBWALL)
|| lev->typ == IRONBARS) {
lev->typ = SDOOR;
lev->wall_info = 0;
lev->wall_info = 0; /* overlays 'flags' */
/* lev->horizontal stays as-is */
/* no special handling for rogue level is necessary;
exposing a secret door there yields a doorless doorway */
@@ -3068,7 +3096,6 @@ int locked, trapped;
#endif
if (trapped)
lev->doormask |= D_TRAPPED;
block_point(x, y);
pline("Secret door.");
madeterrain = TRUE;
} else {
@@ -3078,7 +3105,7 @@ int locked, trapped;
} else if (!BSTRCMPI(bp, p - 15, "secret corridor")) {
if (lev->typ == CORR) {
lev->typ = SCORR;
block_point(x, y);
/* neither CORR nor SCORR uses 'flags' or 'horizontal' */
pline("Secret corridor.");
madeterrain = TRUE;
} else {
@@ -3094,11 +3121,41 @@ int locked, trapped;
if (u.uinwater && !is_pool(u.ux, u.uy)) {
u.uinwater = 0; /* leave the water */
docrt();
g.vision_full_recalc = 1;
} else if (u.utrap && u.utraptype == TT_LAVA
&& !is_lava(u.ux, u.uy)) {
reset_utrap(FALSE);
/* [block/unblock_point was handled by docrt -> vision_recalc] */
} else {
if (u.utrap && u.utraptype == TT_LAVA && !is_lava(u.ux, u.uy))
reset_utrap(FALSE);
if (does_block(x, y, lev)) {
if (!didblock)
block_point(x, y);
} else {
if (didblock)
unblock_point(x, y);
}
}
/* fixups for replaced terrain that aren't handled above;
for fountain placed on fountain or sink placed on sink, the
increment above gets canceled out by the decrement here;
otherwise if fountain or sink was replaced, there's one less */
if (IS_FOUNTAIN(oldtyp))
g.level.flags.nfountains--;
else if (IS_SINK(oldtyp))
g.level.flags.nsinks--;
/* horizontal is overlaid by fountain->blessedftn, grave->disturbed */
if (IS_FOUNTAIN(oldtyp) || IS_GRAVE(oldtyp)
|| IS_WALL(oldtyp) || oldtyp == IRONBARS
|| IS_DOOR(oldtyp) || oldtyp == SDOOR) {
/* when new terrain is a fountain, 'blessedftn' was explicitly
set above; likewise for grave and 'disturbed'; when it's a
secret door, the old type was a wall or a door and we retain
the 'horizontal' value from those */
if (!IS_FOUNTAIN(lev->typ) && !IS_GRAVE(lev->typ)
&& lev->typ != SDOOR)
lev->horizontal = 0; /* also clears blessedftn, disturbed */
}
/* note: lev->lit and lev->nondiggable retain their values even
though those might not make sense with the new terrain */
}
if (madeterrain || badterrain) {
/* cast 'const' away; caller won't modify this */

View File

@@ -3368,8 +3368,31 @@ boolean tinitial, tfrom_file;
return retval;
}
#endif /* VIDEOSHADES */
#ifdef MSDOS
fullname = "video_width";
if (match_optname(opts, fullname, 7, TRUE)) {
if (duplicate)
complain_about_duplicate(opts, 1);
if (negated) {
bad_negation(fullname, FALSE);
return FALSE;
}
op = string_for_opt(opts, negated);
iflags.wc_video_width = strtol(op, NULL, 10);
return FALSE;
}
fullname = "video_height";
if (match_optname(opts, fullname, 7, TRUE)) {
if (duplicate)
complain_about_duplicate(opts, 1);
if (negated) {
bad_negation(fullname, FALSE);
return FALSE;
}
op = string_for_opt(opts, negated);
iflags.wc_video_height = strtol(op, NULL, 10);
return FALSE;
}
#ifdef NO_TERMS
/* video:string -- must be after longer tests */
fullname = "video";

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 sp_lev.c $NHDT-Date: 1574646949 2019/11/25 01:55:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.141 $ */
/* NetHack 3.6 sp_lev.c $NHDT-Date: 1580036285 2020/01/26 10:58:05 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.148 $ */
/* Copyright (c) 1989 by Jean-Christophe Collet */
/* NetHack may be freely redistributed. See license for details. */
@@ -1571,10 +1571,13 @@ struct mkroom *croom;
/*
* If this is a specific item of the right type and it is being
* created on the right level, flag it as the designated item
* created on the right level, record its obj->o_id to be able
* to recognize it as the designated item
* used to detect a special achievement (to whit, reaching and
* exploring the target level, although the exploration part
* might be short-circuited if a monster brings object to hero).
* Achievement is accomplished and the recorded o_id is cleared
* if/when it gets added into hero's inventory.
*
* Random items of the appropriate type won't trigger a false
* match--they'll fail the (id != -1) test above--but the level

View File

@@ -876,7 +876,6 @@ const char *status_fieldnm[MAXBLSTATS];
const char *status_fieldfmt[MAXBLSTATS];
char *status_vals[MAXBLSTATS];
boolean status_activefields[MAXBLSTATS];
NEARDATA winid WIN_STATUS;
void
genl_status_init()

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 worm.c $NHDT-Date: 1579990313 2020/01/25 22:11:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.41 $ */
/* NetHack 3.6 worm.c $NHDT-Date: 1580043421 2020/01/26 12:57:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.42 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2009. */
/* NetHack may be freely redistributed. See license for details. */
@@ -218,26 +218,65 @@ struct monst *worm;
seg->nseg = new_seg; /* attach it to the end of the list */
wheads[wnum] = new_seg; /* move the end pointer */
/*
* [maybe] FIXME?
* scheduling wgrowtime[] seems to be based on normal movement
* speed (12) but long worms move at 1/4 of that (3), so they'll
* reach the scheduled 'moves' more quickly (in terms of their
* actual movement) and grow faster than was probably intended.
*/
if (wgrowtime[wnum] <= g.moves) {
if (!wgrowtime[wnum])
int whplimit, whpcap, wsegs = count_wsegs(worm);
/* first set up for the next time to grow */
if (!wgrowtime[wnum]) {
/* new worm; usually grow a tail segment on its next turn */
wgrowtime[wnum] = g.moves + rnd(5);
else
wgrowtime[wnum] += rn1(15, 3);
worm->mhp += 3;
if (worm->mhp > MHPMAX)
worm->mhp = MHPMAX;
if (worm->mhp > worm->mhpmax)
worm->mhpmax = worm->mhp;
} else
/* The worm doesn't grow, so the last segment goes away. */
} else {
int mmove = mcalcmove(worm, FALSE),
/* prior to 3.7.0, next-grow increment was 3..17 but since
it got checked every 4th turn when the speed 3 worm got
to move, it was effectively 0..5; also, its usage was
'wgrowtime += incr', so often 'wgrowtime' would be
exceeded by 'moves' on consecutive turns for the worm,
resulting in an excessively rapid growth cycle */
incr = rn1(10, 2); /* 2..12; after adjusting for long worn
* speed of 3, effective value is 8..48 */
incr = (incr * NORMAL_SPEED) / max(mmove, 1);
wgrowtime[wnum] = g.moves + incr;
}
/* increase HP based on number of segments; if it has shrunk, it
won't gain new HP until regaining previous peak segment count;
when wounded (whether from damage or from shrinking), the HP
which might have been 'new' will heal */
whplimit = !worm->m_lev ? 4 : (8 * (int) worm->m_lev);
/* note: wsegs includes the hidden segment co-located with the head */
if (wsegs > 33)
whplimit += 2 * (wsegs - 33), wsegs = 33;
if (wsegs > 22)
whplimit += 4 * (wsegs - 22), wsegs = 22;
if (wsegs > 11)
whplimit += 6 * (wsegs - 11), wsegs = 11;
whplimit += 8 * wsegs;
if (whplimit > MHPMAX)
whplimit = MHPMAX;
worm->mhp += d(2, 2); /* 2..4, average 3 */
whpcap = max(whplimit, worm->mhpmax);
if (worm->mhp < whpcap) {
/* can't exceed segment-derived limit unless level increase after
peak tail growth has already done so; when that isn't the case,
if segment growth exceeds current max HP then increase it */
if (worm->mhp > whpcap)
worm->mhp = whpcap;
if (worm->mhp > worm->mhpmax)
worm->mhpmax = worm->mhp;
} else {
if (worm->mhp > worm->mhpmax)
worm->mhp = worm->mhpmax;
}
} else {
/* The worm doesn't grow, so the last segment goes away.
(Done after inserting an extra segment at the head, so it
isn't getting smaller here, just changing location without
having to move any of the intermediate segments.) */
shrink_worm(wnum);
}
}
/*
@@ -253,10 +292,11 @@ struct monst *worm;
{
shrink_worm((int) worm->wormno); /* shrink */
if (worm->mhp > 3)
worm->mhp -= 3; /* mhpmax not changed ! */
else
worm->mhp = 1;
if (worm->mhp > count_wsegs(worm)) {
worm->mhp -= d(2, 2); /* 2..4, average 3; note: mhpmax not changed! */
if (worm->mhp < 1)
worm->mhp = 1;
}
}
/*