Merge branch 'NetHack-3.7' into NetHack-3.7-Jan2020
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
29
src/mon.c
29
src/mon.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
95
src/objnam.c
95
src/objnam.c
@@ -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 */
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
84
src/worm.c
84
src/worm.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user