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

This commit is contained in:
nhmall
2020-01-24 17:27:48 -05:00
40 changed files with 555 additions and 241 deletions
+8 -3
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 attrib.c $NHDT-Date: 1575245050 2019/12/02 00:04:10 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.66 $ */
/* NetHack 3.6 attrib.c $NHDT-Date: 1579655026 2020/01/22 01:03:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.74 $ */
/* Copyright 1988, 1989, 1990, 1992, M. Stephenson */
/* NetHack may be freely redistributed. See license for details. */
@@ -277,11 +277,12 @@ int typ, fatal; /* if fatal is 0, limit damage to adjattrib */
boolean thrown_weapon; /* thrown weapons are less deadly */
{
int i, loss, kprefix = KILLED_BY_AN;
boolean blast = !strcmp(reason, "blast");
/* inform player about being poisoned unless that's already been done;
"blast" has given a "blast of poison gas" message; "poison arrow",
"poison dart", etc have implicitly given poison messages too... */
if (strcmp(reason, "blast") && !strstri(reason, "poison")) {
if (!blast && !strstri(reason, "poison")) {
boolean plural = (reason[strlen(reason) - 1] == 's') ? 1 : 0;
/* avoid "The" Orcus's sting was poisoned... */
@@ -290,7 +291,7 @@ boolean thrown_weapon; /* thrown weapons are less deadly */
plural ? "were" : "was");
}
if (Poison_resistance) {
if (!strcmp(reason, "blast"))
if (blast)
shieldeff(u.ux, u.uy);
pline_The("poison doesn't seem to affect you.");
return;
@@ -315,8 +316,12 @@ boolean thrown_weapon; /* thrown weapons are less deadly */
g.context.botl = TRUE;
pline_The("poison was deadly...");
} else if (i > 5) {
boolean cloud = !strcmp(reason, "gas cloud");
/* HP damage; more likely--but less severe--with missiles */
loss = thrown_weapon ? rnd(6) : rn1(10, 6);
if ((blast || cloud) && Half_gas_damage) /* worn towel */
loss = (loss + 1) / 2;
losehp(loss, pkiller, kprefix); /* poison damage */
} else {
/* attribute loss; if typ is A_STR, reduction in current and
+4 -8
View File
@@ -165,14 +165,10 @@ boolean restore;
if (mnum == PM_DOPPELGANGER && otmp->otyp == CORPSE)
set_corpsenm(otmp, mnum);
}
} else if ((otmp->otyp == iflags.mines_prize_type
&& !Is_mineend_level(&u.uz))
|| ((otmp->otyp == iflags.soko_prize_type1
|| otmp->otyp == iflags.soko_prize_type2)
&& !Is_sokoend_level(&u.uz))) {
/* "special prize" in this game becomes ordinary object
if loaded into another game */
otmp->record_achieve_special = NON_PM;
} else if (is_mines_prize(otmp) || is_soko_prize(otmp)) {
/* achievement tracking; in case prize was moved off its
original level (which is always a no-bones level) */
otmp->nomerge = 0;
} else if (otmp->otyp == AMULET_OF_YENDOR) {
/* no longer the real Amulet */
otmp->otyp = FAKE_AMULET_OF_YENDOR;
+22 -1
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 cmd.c $NHDT-Date: 1578764033 2020/01/11 17:33:53 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.391 $ */
/* NetHack 3.6 cmd.c $NHDT-Date: 1579655026 2020/01/22 01:03:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.392 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -806,6 +806,24 @@ boolean pre, wiztower;
continue;
if (mtmp->isshk)
setpaid(mtmp);
/* achievement tracking */
{
static const char Unachieve[] = "%s achievement revoked.";
if (Is_mineend_level(&u.uz)) {
if (u.uachieve.mines_luckstone) {
pline(Unachieve, "Mine's end");
u.uachieve.mines_luckstone = 0;
}
g.context.achieveo.mines_prize_oid = 0;
} else if (Is_sokoend_level(&u.uz)) {
if (u.uachieve.finish_sokoban) {
pline(Unachieve, "Sokoban end");
u.uachieve.finish_sokoban = 0;
}
g.context.achieveo.soko_prize_oid = 0;
}
}
/* TODO?
* Reduce 'born' tally for each monster about to be discarded
* by savelev(), otherwise replacing heavily populated levels
@@ -877,6 +895,7 @@ wiz_makemap(VOID_ARGS)
{
if (wizard) {
boolean was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz);
makemap_prepost(TRUE, was_in_W_tower);
/* create a new level; various things like bestowing a guardian
angel on Astral or setting off alarm on Ft.Ludios are handled
@@ -2984,6 +3003,8 @@ int final;
enlght_halfdmg(HALF_PHDAM, final);
if (Half_spell_damage)
enlght_halfdmg(HALF_SPDAM, final);
if (Half_gas_damage)
enl_msg(You_, "take", "took", " reduced poison gas damage", "");
/* polymorph and other shape change */
if (Protection_from_shape_changers)
you_are("protected from shape changers",
-2
View File
@@ -638,8 +638,6 @@ const struct instance_globals g_init = {
UNDEFINED_VALUE, /* ysize */
FALSE, /* splev_init_present */
FALSE, /* icedpools */
0, /* mines_prize_count */
0, /* soki_prize_count */
{ UNDEFINED_PTR }, /* container_obj */
0, /* container_idx */
NULL, /* invent_carrying_monster */
+22 -4
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 do_wear.c $NHDT-Date: 1575768410 2019/12/08 01:26:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.126 $ */
/* NetHack 3.6 do_wear.c $NHDT-Date: 1579649788 2020/01/21 23:36:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.127 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1159,15 +1159,19 @@ void
Blindf_off(otmp)
struct obj *otmp;
{
boolean was_blind = Blind, changed = FALSE;
boolean was_blind = Blind, changed = FALSE,
nooffmsg = !otmp;
if (!otmp)
otmp = ublindf;
if (!otmp) {
impossible("Blindf_off without otmp");
impossible("Blindf_off without eyewear?");
return;
}
g.context.takeoff.mask &= ~W_TOOL;
setworn((struct obj *) 0, otmp->owornmask);
off_msg(otmp);
if (!nooffmsg)
off_msg(otmp);
if (Blind) {
if (was_blind) {
@@ -1895,6 +1899,15 @@ struct obj *obj;
return 1;
}
} else {
/*
* FIXME:
* except for the rings/nolimbs case, this allows you to put on
* accessories without having any hands to manipulate them, and
* to put them on when poly'd into a tiny or huge form where
* they shouldn't fit. [If the latter situation changes, make
* comparable change to break_armor(polyself.c).]
*/
/* accessory */
if (ring) {
char answer, qbuf[QBUFSZ];
@@ -1965,6 +1978,11 @@ struct obj *obj;
return 0;
}
} else if (eyewear) {
if (!has_head(g.youmonst.data)) {
You("have no head to wear %s on.", ansimpleoname(obj));
return 0;
}
if (ublindf) {
if (ublindf->otyp == TOWEL)
Your("%s is already covered by a towel.",
+3 -2
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 dothrow.c $NHDT-Date: 1573688688 2019/11/13 23:44:48 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.164 $ */
/* NetHack 3.6 dothrow.c $NHDT-Date: 1579655027 2020/01/22 01:03:47 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.181 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1977,7 +1977,8 @@ boolean from_invent;
explode_oil(obj, x, y);
} else if (distu(x, y) <= 2) {
if (!breathless(g.youmonst.data) || haseyes(g.youmonst.data)) {
if (obj->otyp != POT_WATER) {
/* wet towel protects both eyes and breathing */
if (obj->otyp != POT_WATER && !Half_gas_damage) {
if (!breathless(g.youmonst.data)) {
/* [what about "familiar odor" when known?] */
You("smell a peculiar odor...");
+14 -15
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 hack.c $NHDT-Date: 1578916008 2020/01/13 11:46:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.243 $ */
/* NetHack 3.6 hack.c $NHDT-Date: 1579261288 2020/01/17 11:41:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.245 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Derek S. Ray, 2015. */
/* NetHack may be freely redistributed. See license for details. */
@@ -750,7 +750,7 @@ int mode;
return FALSE;
}
if (!(Passes_walls || passes_bars(g.youmonst.data))) {
if (mode == DO_MOVE && iflags.mention_walls)
if (mode == DO_MOVE && flags.mention_walls)
You("cannot pass through the bars.");
return FALSE;
}
@@ -772,7 +772,7 @@ int mode;
else if (Passes_walls && !may_passwall(x, y)
&& In_sokoban(&u.uz))
pline_The("Sokoban walls resist your ability.");
else if (iflags.mention_walls)
else if (flags.mention_walls)
pline("It's %s.",
(IS_WALL(tmpr->typ) || tmpr->typ == SDOOR) ? "a wall"
: IS_TREE(tmpr->typ) ? "a tree"
@@ -842,7 +842,7 @@ int mode;
if (mode == DO_MOVE) {
if (Blind)
feel_location(x, y);
if (Underwater || iflags.mention_walls)
if (Underwater || flags.mention_walls)
You_cant("move diagonally into an intact doorway.");
}
return FALSE;
@@ -896,7 +896,7 @@ int mode;
if (dx && dy && !Passes_walls && IS_DOOR(ust->typ)
&& (!doorless_door(ux, uy) || block_entry(x, y))) {
/* Can't move at a diagonal out of a doorway with door. */
if (mode == DO_MOVE && iflags.mention_walls)
if (mode == DO_MOVE && flags.mention_walls)
You_cant("move diagonally out of an intact doorway.");
return FALSE;
}
@@ -904,7 +904,7 @@ int mode;
if (sobj_at(BOULDER, x, y) && (Sokoban || !Passes_walls)) {
if (!(Blind || Hallucination) && (g.context.run >= 2)
&& mode != TEST_TRAV) {
if (mode == DO_MOVE && iflags.mention_walls)
if (mode == DO_MOVE && flags.mention_walls)
pline("A boulder blocks your path.");
return FALSE;
}
@@ -1495,7 +1495,7 @@ domove_core()
}
}
if (!isok(x, y)) {
if (iflags.mention_walls) {
if (flags.mention_walls) {
int dx = u.dx, dy = u.dy;
if (dx && dy) { /* diagonal */
@@ -1511,13 +1511,14 @@ domove_core()
directionname(xytod(dx, dy)));
}
nomul(0);
g.context.move = 0;
return;
}
if (((trap = t_at(x, y)) && trap->tseen)
|| (Blind && !Levitation && !Flying && !is_clinger(g.youmonst.data)
&& is_pool_or_lava(x, y) && levl[x][y].seenv)) {
if (g.context.run >= 2) {
if (iflags.mention_walls) {
if (flags.mention_walls) {
if (trap && trap->tseen) {
You("stop in front of %s.",
an(trapname(trap->ttyp, FALSE)));
@@ -2728,7 +2729,7 @@ lookaround()
if ((g.context.run != 1 && !mtmp->mtame)
|| (x == u.ux + u.dx && y == u.uy + u.dy
&& !g.context.travel)) {
if (iflags.mention_walls)
if (flags.mention_walls)
pline("%s blocks your path.", upstart(a_monnam(mtmp)));
goto stop;
}
@@ -2746,7 +2747,7 @@ lookaround()
if (x != u.ux && y != u.uy)
continue;
if (g.context.run != 1) {
if (iflags.mention_walls)
if (flags.mention_walls)
You("stop in front of the door.");
goto stop;
}
@@ -2775,11 +2776,9 @@ lookaround()
if (g.context.run == 1)
goto bcorr; /* if you must */
if (x == u.ux + u.dx && y == u.uy + u.dy) {
if (iflags.mention_walls) {
if (flags.mention_walls)
You("stop in front of %s.",
an(trapname(trap->ttyp, FALSE)));
}
goto stop;
}
continue;
@@ -2793,7 +2792,7 @@ lookaround()
* to test boots by trying to SHIFT-direction
* into a pool and seeing if the game allowed it
*/
if (iflags.mention_walls)
if (flags.mention_walls)
You("stop at the edge of the %s.",
hliquid(is_pool(x,y) ? "water" : "lava"));
goto stop;
@@ -2816,7 +2815,7 @@ lookaround()
} /* end for loops */
if (corrct > 1 && g.context.run == 2) {
if (iflags.mention_walls)
if (flags.mention_walls)
pline_The("corridor widens here.");
goto stop;
}
+3 -9
View File
@@ -835,20 +835,14 @@ struct obj *obj;
}
/* "special achievements" aren't discoverable during play, they
end up being recorded in XLOGFILE at end of game, nowhere else;
record_achieve_special overloads corpsenm which is ordinarily
initialized to NON_PM (-1) rather than to 0; any special prize
must never be a corpse, egg, tin, figurine, or statue because
their use of obj->corpsenm for monster type would conflict,
nor be a leash (corpsenm overloaded for m_id of leashed
monster) or a novel (corpsenm overloaded for novel index) */
end up being recorded in XLOGFILE at end of game, nowhere else */
if (is_mines_prize(obj)) {
u.uachieve.mines_luckstone = 1;
obj->record_achieve_special = NON_PM;
g.context.achieveo.mines_prize_oid = 0;
obj->nomerge = 0;
} else if (is_soko_prize(obj)) {
u.uachieve.finish_sokoban = 1;
obj->record_achieve_special = NON_PM;
g.context.achieveo.soko_prize_oid = 0;
obj->nomerge = 0;
}
}
+11 -4
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 monmove.c $NHDT-Date: 1575245074 2019/12/02 00:04:34 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.116 $ */
/* NetHack 3.6 monmove.c $NHDT-Date: 1579616424 2020/01/21 14:20:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.128 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2006. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1286,20 +1286,27 @@ register int after;
if (!m_in_out_region(mtmp, nix, niy))
return 3;
/* move a normal monster; for a long worm, remove_monster() and
place_monster() only manipulate the head; they leave tail as-is */
remove_monster(omx, omy);
place_monster(mtmp, nix, niy);
/* for a long worm, insert a new segment to reconnect the head
with the tail; worm_move() keeps the end of the tail if worm
is scheduled to grow, removes that for move-without-growing */
if (mtmp->wormno)
worm_move(mtmp);
for (j = MTSZ - 1; j > 0; j--)
mtmp->mtrack[j] = mtmp->mtrack[j - 1];
mtmp->mtrack[0].x = omx;
mtmp->mtrack[0].y = omy;
/* Place a segment at the old position. */
if (mtmp->wormno)
worm_move(mtmp);
} else {
if (is_unicorn(ptr) && rn2(2) && !tele_restrict(mtmp)) {
(void) rloc(mtmp, TRUE);
return 1;
}
/* for a long worm, shrink it (by discarding end of tail) when
it has failed to move */
if (mtmp->wormno)
worm_nomove(mtmp);
}
+124 -15
View File
@@ -1,4 +1,4 @@
/* NetHack 3.7 nhlua.c $NHDT-Date: 1575246766 2019/12/02 00:32:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.16 $ */
/* NetHack 3.7 nhlua.c $NHDT-Date: 1579901146 2020/01/24 21:25:46 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.29 $ */
/* Copyright (c) 2018 by Pasi Kallinen */
/* NetHack may be freely redistributed. See license for details. */
@@ -44,8 +44,16 @@ const char *msg;
lua_getstack(L, 1, &ar);
lua_getinfo(L, "lS", &ar);
Sprintf(buf, "%s (line %i%s)", msg, ar.currentline, ar.source);
Sprintf(buf, "%s (line %d ", msg, ar.currentline);
Sprintf(eos(buf), "%.*s)",
/* (max length of ar.short_src is actually LUA_IDSIZE
so this is overkill for it, but crucial for ar.source) */
(int) (sizeof buf - (strlen(buf) + sizeof ")")),
ar.short_src); /* (used to be 'ar.source' here) */
lua_pushstring(L, buf);
#if 0 /* defined(PANICTRACE) && !defined(NO_SIGNALS) */
panictrace_setsignals(FALSE);
#endif
(void) lua_error(L);
/*NOTREACHED*/
}
@@ -923,16 +931,21 @@ lua_State *L;
return 1;
}
/* read lua code/data from a dlb module or an external file, insert the
file name as new first record so that we aren't at the mercy of whoever
edits it to maintain that, and since we're forced to muck with messy
details, replace comments with empty lines so that Lua won't need to */
boolean
nhl_loadlua(L, fname)
lua_State *L;
const char *fname;
{
boolean ret = TRUE;
#define LOADCHUNKSIZE (1L << 13) /* 8K */
boolean ret = TRUE, is_comment;
dlb *fh;
char *buf = (char *) 0;
long buflen;
int cnt, llret;
char *buf = (char *) 0, *bufin, *bufout, *p, *nl;
long buflen, ct, cnt, fnamesiz;
int llret, first = 0, spanlines = 0;
fh = dlb_fopen(fname, "r");
if (!fh) {
@@ -941,20 +954,117 @@ const char *fname;
goto give_up;
}
fnamesiz = strlen(fname) + 7L; /* 7: "--{" + "}--\n" (no '\0') */
dlb_fseek(fh, 0L, SEEK_END);
buflen = dlb_ftell(fh);
buf = (char *) alloc(buflen + 1);
dlb_fseek(fh, 0L, SEEK_SET);
if ((cnt = dlb_fread(buf, 1, buflen, fh)) != buflen) {
impossible("nhl_loadlua: Error loading %s, got %i/%li bytes",
fname, cnt, buflen);
ret = FALSE;
goto give_up;
/* extra +1: room to add final '\n' if missing */
buf = bufout = (char *) alloc(fnamesiz + buflen + 1 + 1);
/* insert file name as first record so nhl_error() can report it;
leading dashes make it be a comment, trailing ones are for
symmetry; delimit file name with braces instead of spaces to
avoid an undesireable line split during error feedback */
Sprintf(bufout, "--{%s}--\n", fname);
bufin = bufout = eos(buf);
ct = 0L;
while (buflen > 0 || ct) {
/*
* Semi-arbitrarily limit reads to 8K at a time. That's big
* enough to cover the majority of our Lua files in one bite
* but small enough to fully exercise the partial record
* handling (when processing the castle's level description).
*
* [For an external file (non-DLB), VMS may only be able to
* read at most 32K-1 at a time depending on the file format
* in use, and fseek(SEEK_END) only yields an upper bound on
* the actual amount of data in that situation.]
*/
if ((cnt = dlb_fread(bufin, 1, min(buflen, LOADCHUNKSIZE), fh)) < 0L)
break;
buflen -= cnt; /* set up for next iteration, if any */
if (cnt == 0L) {
*bufin = '\n'; /* very last line is unterminated? */
cnt = 1;
}
bufin[cnt] = '\0'; /* fread() doesn't do this */
/* in case partial line was leftover from previous fread */
bufin -= ct, cnt += ct, ct = 0;
while (cnt > 0) {
if ((nl = index(bufin, '\n')) != 0) {
/* normal case, newline is present */
ct = (long) (nl - bufin + 1L); /* +1: keep the newline */
for (p = bufin; *p == ' '; ++p)
continue;
is_comment = (p[0] == '-' && p[1] == '-' && !spanlines);
if (spanlines && index("])}", *p))
--spanlines;
if (!first++ && (*p == '\n' || is_comment)) {
/* if first line is blank or a comment, omit it so that
our "--{fname}--\n" line doesn't make the line count
be out of sync; note: when first line is code, line
number reported in error messages will be off by one */
bufin = nl + 1;
} else if (is_comment) {
/* discard comment to shorten the string Lua will deal
with but keep the line to maintain Lua line counter */
#if 0
Strcpy(bufout, "--\n"), bufout += 3;
#else
Strcpy(bufout, "\n"), bufout += 1;
#endif
bufin += ct;
} else {
/* normal case; some text terminated by newline */
for (p = bufin; p <= nl; ++p)
*bufout++ = *bufin++;
for (p = &bufout[-2]; *p == ' '; --p) /* [-1] is '\n' */
continue;
if (index("[({", *p))
++spanlines;
}
if (*bufin == '\r')
++bufin, ++ct;
/* update for next loop iteration */
cnt -= ct;
ct = 0;
} else {
/* no newline => partial record; move unprocessed chars
to front of input buffer (bufin portion of buf[]) */
ct = cnt = (long) (eos(bufin) - bufin);
for (p = bufout; cnt > 0; --cnt)
*p++ = *bufin++;
*p = '\0';
bufin = p; /* next fread() populates buf[] starting here */
/* cnt==0 so inner loop will terminate */
}
}
}
buf[buflen] = '\0';
*bufout = '\0';
(void) dlb_fclose(fh);
#ifdef DEBUG
if (explicitdebug("loadlua")) {
FILE *fp;
char oname[QBUFSZ];
(void) strsubst(strcpy(oname, fname), ".lua", ".txt");
if ((fp = fopen(oname, "w")) != 0) {
for (buflen = strlen(buf), p = buf;
buflen > 0;
buflen -= cnt, p += cnt) {
cnt = min(buflen, LOADCHUNKSIZE);
if ((long) fwrite(p, 1, cnt, fp) < cnt)
break;
}
(void) fclose(fp);
}
}
#endif
llret = luaL_loadstring(L, buf);
if (llret != LUA_OK) {
impossible("luaL_loadstring: Error loading %s (errcode %i)",
@@ -973,8 +1083,7 @@ const char *fname;
give_up:
if (buf) {
free(buf);
buf = (char *) 0;
free((genericptr_t) buf);
buflen = 0;
}
return ret;
+2 -2
View File
@@ -1,4 +1,4 @@
/* NetHack 3.7 objnam.c $NHDT-Date: 1578896203 2020/01/13 06:16:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.287 $ */
/* NetHack 3.7 objnam.c $NHDT-Date: 1579261291 2020/01/17 11:41:31 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.288 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
@@ -998,7 +998,7 @@ unsigned doname_flags;
Strcat(prefix, "cursed ");
else if (obj->blessed)
Strcat(prefix, "blessed ");
else if (!iflags.implicit_uncursed
else if (!flags.implicit_uncursed
/* For most items with charges or +/-, if you know how many
* charges are left or what the +/- is, then you must have
* totally identified the item, so "uncursed" is unnecessary,
+7 -7
View File
@@ -1,4 +1,4 @@
/* NetHack 3.7 options.c $NHDT-Date: 1578996303 2020/01/14 10:05:03 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.396 $ */
/* NetHack 3.7 options.c $NHDT-Date: 1579261293 2020/01/17 11:41:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.428 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2008. */
/* NetHack may be freely redistributed. See license for details. */
@@ -148,7 +148,7 @@ static const struct Bool_Opt {
#else
{ "ignintr", (boolean *) 0, FALSE, SET_IN_FILE },
#endif
{ "implicit_uncursed", &iflags.implicit_uncursed, TRUE, SET_IN_GAME },
{ "implicit_uncursed", &flags.implicit_uncursed, TRUE, SET_IN_GAME },
{ "large_font", &iflags.obsolete, FALSE, SET_IN_FILE }, /* OBSOLETE */
{ "legacy", &flags.legacy, TRUE, DISP_IN_GAME },
{ "lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME },
@@ -158,7 +158,7 @@ static const struct Bool_Opt {
#else
{ "mail", (boolean *) 0, TRUE, SET_IN_FILE },
#endif
{ "mention_walls", &iflags.mention_walls, FALSE, SET_IN_GAME },
{ "mention_walls", &flags.mention_walls, FALSE, SET_IN_GAME },
{ "menucolors", &iflags.use_menu_color, FALSE, SET_IN_GAME },
/* for menu debugging only*/
{ "menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_WIZGAME },
@@ -739,9 +739,9 @@ initoptions_init()
/* for "special achievement" tracking (see obj.h,
create_object(sp_lev.c), addinv_core1(invent.c) */
iflags.mines_prize_type = LUCKSTONE;
iflags.soko_prize_type1 = BAG_OF_HOLDING;
iflags.soko_prize_type2 = AMULET_OF_REFLECTION;
g.context.achieveo.mines_prize_type = LUCKSTONE;
g.context.achieveo.soko_prize_typ1 = BAG_OF_HOLDING;
g.context.achieveo.soko_prize_typ2 = AMULET_OF_REFLECTION;
/* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
(void) memcpy((genericptr_t) flags.inv_order,
@@ -4086,7 +4086,7 @@ boolean tinitial, tfrom_file;
g.context.botl = TRUE;
} else if (boolopt[i].addr == &flags.invlet_constant
|| boolopt[i].addr == &flags.sortpack
|| boolopt[i].addr == &iflags.implicit_uncursed) {
|| boolopt[i].addr == &flags.implicit_uncursed) {
if (!flags.invlet_constant)
reassign();
update_inventory();
+25 -5
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 polyself.c $NHDT-Date: 1573290419 2019/11/09 09:06:59 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.135 $ */
/* NetHack 3.6 polyself.c $NHDT-Date: 1579660157 2020/01/22 02:29:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.149 $ */
/* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */
/* NetHack may be freely redistributed. See license for details. */
@@ -462,14 +462,19 @@ int psflags;
pline("I've never heard of such monsters.");
else
You_cant("polymorph into any of those.");
} else if (wizard && Upolyd && mntmp == u.umonster) {
/* in wizard mode, picking own role while poly'd reverts to
normal without newman()'s chance of level or sex change */
rehumanize();
goto made_change;
} else if (iswere && (were_beastie(mntmp) == u.ulycn
|| mntmp == counter_were(u.ulycn)
|| (Upolyd && mntmp == PM_HUMAN))) {
goto do_shift;
/* Note: humans are illegal as monsters, but an
* illegal monster forces newman(), which is what we
* want if they specified a human.... */
} else if (!polyok(&mons[mntmp])
/* Note: humans are illegal as monsters, but an
illegal monster forces newman(), which is what
we want if they specified a human.... */
&& !(mntmp == PM_HUMAN || your_race(&mons[mntmp])
|| mntmp == g.urole.malenum
|| mntmp == g.urole.femalenum)) {
@@ -934,7 +939,7 @@ break_armor()
useup(uarmu);
}
} else if (sliparm(g.youmonst.data)) {
if (((otmp = uarm) != 0) && (racial_exception(&g.youmonst, otmp) < 1)) {
if ((otmp = uarm) != 0 && racial_exception(&g.youmonst, otmp) < 1) {
if (donning(otmp))
cancel_don();
Your("armor falls around you!");
@@ -1016,6 +1021,21 @@ break_armor()
dropp(otmp);
}
}
/* not armor, but eyewear shouldn't stay worn without a head to wear
it/them on (should also come off if head is too tiny or too huge,
but putting accessories on doesn't reject those cases [yet?]);
amulet stays worn */
if ((otmp = ublindf) != 0 && !has_head(g.youmonst.data)) {
int l;
const char *eyewear = simpleonames(otmp); /* blindfold|towel|lenses */
if (!strncmp(eyewear, "pair of ", l = 8)) /* lenses */
eyewear += l;
Your("%s %s off!", eyewear, vtense(eyewear, "fall"));
(void) Blindf_off((struct obj *) 0); /* Null: skip usual off mesg */
dropp(otmp);
}
/* rings stay worn even when no hands */
}
static void
+10 -4
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 potion.c $NHDT-Date: 1573848199 2019/11/15 20:03:19 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.167 $ */
/* NetHack 3.6 potion.c $NHDT-Date: 1579655028 2020/01/22 01:03:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.179 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1627,13 +1627,19 @@ register struct obj *obj;
remains in inventory where our caller expects it to be */
obj->in_use = 1;
switch (obj->otyp) {
/* wearing a wet towel protects both eyes and breathing, even when
the breath effect might be beneficial; we still pass down to the
naming opportunity in case potion was thrown at hero by a monster */
switch (Half_gas_damage ? TOWEL : obj->otyp) {
case TOWEL:
pline("Some vapor passes harmlessly around you.");
break;
case POT_RESTORE_ABILITY:
case POT_GAIN_ABILITY:
if (obj->cursed) {
if (!breathless(g.youmonst.data))
if (!breathless(g.youmonst.data)) {
pline("Ulch! That potion smells terrible!");
else if (haseyes(g.youmonst.data)) {
} else if (haseyes(g.youmonst.data)) {
const char *eyes = body_part(EYE);
if (eyecount(g.youmonst.data) != 1)
+8 -6
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 pray.c $NHDT-Date: 1578895347 2020/01/13 06:02:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.138 $ */
/* NetHack 3.6 pray.c $NHDT-Date: 1579401997 2020/01/19 02:46:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.139 $ */
/* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */
/* NetHack may be freely redistributed. See license for details. */
@@ -484,7 +484,7 @@ int trouble;
what = rightglow;
else if (otmp == uleft)
what = leftglow;
decurse:
decurse:
if (!otmp) {
impossible("fix_worst_trouble: nothing to uncurse.");
return;
@@ -1496,7 +1496,7 @@ dosacrifice()
if (otmp->otyp == AMULET_OF_YENDOR) {
if (!highaltar) {
too_soon:
too_soon:
if (altaralign == A_NONE && Inhell)
/* hero has left Moloch's Sanctum so is in the process
of getting away with the Amulet (outside of Gehennom,
@@ -1526,7 +1526,8 @@ dosacrifice()
/* Moloch's high altar */
if (u.ualign.record > -99)
u.ualign.record = -99;
pline("An invisible choir chants in Latin, and you are bathed in darkness...");
pline(
"An invisible choir chants, and you are bathed in darkness...");
/*[apparently shrug/snarl can be sensed without being seen]*/
pline("%s shrugs and retains dominion over %s,", Moloch,
u_gname());
@@ -1593,7 +1594,7 @@ dosacrifice()
}
if (altaralign != u.ualign.type && highaltar) {
desecrate_high_altar:
desecrate_high_altar:
/*
* REAL BAD NEWS!!! High altars cannot be converted. Even an attempt
* gets the god who owns it truly pissed off.
@@ -1830,7 +1831,8 @@ boolean praying; /* false means no messages should be given */
}
if (is_undead(g.youmonst.data) && !Inhell
&& (g.p_aligntyp == A_LAWFUL || (g.p_aligntyp == A_NEUTRAL && !rn2(10))))
&& (g.p_aligntyp == A_LAWFUL
|| (g.p_aligntyp == A_NEUTRAL && !rn2(10))))
g.p_type = -1;
/* Note: when !praying, the random factor for neutrals makes the
return value a non-deterministic approximation for enlightenment.
+36 -11
View File
@@ -432,9 +432,10 @@ boolean common UNUSED;
}
boolean
com_pager_core(section, msgid)
com_pager_core(section, msgid, showerror)
const char *section;
const char *msgid;
boolean showerror;
{
const char *const howtoput[] = { "pline", "window", "text", "menu", "default", NULL };
const int howtoput2i[] = { 1, 2, 2, 3, 0, 0 };
@@ -442,6 +443,7 @@ const char *msgid;
lua_State *L;
char *synopsis;
char *text;
char *fallback_msgid = NULL;
if (skip_pager(TRUE))
return FALSE;
@@ -449,7 +451,8 @@ const char *msgid;
L = nhl_init();
if (!nhl_loadlua(L, QTEXT_FILE)) {
impossible("com_pager: %s not found.", QTEXT_FILE);
if (showerror)
impossible("com_pager: %s not found.", QTEXT_FILE);
lua_close(L);
return FALSE;
}
@@ -457,21 +460,40 @@ const char *msgid;
lua_settop(L, 0);
lua_getglobal(L, "questtext");
if (!lua_istable(L, -1)) {
impossible("com_pager: questtext in %s is not a lua table", QTEXT_FILE);
if (showerror)
impossible("com_pager: questtext in %s is not a lua table",
QTEXT_FILE);
lua_close(L);
return FALSE;
}
lua_getfield(L, -1, section);
if (!lua_istable(L, -1)) {
impossible("com_pager: questtext[%s] in %s is not a lua table", section, QTEXT_FILE);
if (showerror)
impossible("com_pager: questtext[%s] in %s is not a lua table",
section, QTEXT_FILE);
lua_close(L);
return FALSE;
}
lua_getfield(L, -1, msgid);
tryagain:
lua_getfield(L, -1, fallback_msgid ? fallback_msgid : msgid);
if (!lua_istable(L, -1)) {
impossible("com_pager: questtext[%s][%s] in %s is not a lua table", section, msgid, QTEXT_FILE);
if (!fallback_msgid) {
/* Do we have questtxt[msg_fallbacks][<msgid>]? */
lua_getfield(L, -3, "msg_fallbacks");
if (lua_istable(L, -1)) {
fallback_msgid = get_table_str_opt(L, msgid, NULL);
lua_pop(L, 2);
if (fallback_msgid)
goto tryagain;
}
}
if (showerror)
impossible("com_pager: questtext[%s][%s] in %s is not a lua table",
section, msgid, QTEXT_FILE);
free(fallback_msgid);
lua_close(L);
return FALSE;
}
@@ -487,9 +509,11 @@ const char *msgid;
nelems = (int) lua_tointeger(L, -1);
lua_pop(L, 1);
if (nelems < 2) {
impossible(
"com_pager: questtext[%s][%s] in %s in not an array of strings",
if (showerror)
impossible(
"com_pager: questtext[%s][%s] in %s in not an array of strings",
section, msgid, QTEXT_FILE);
free(fallback_msgid);
lua_close(L);
return FALSE;
}
@@ -518,6 +542,7 @@ const char *msgid;
free(synopsis);
}
free(fallback_msgid);
free(text);
lua_close(L);
return TRUE;
@@ -527,15 +552,15 @@ void
com_pager(msgid)
const char *msgid;
{
com_pager_core("common", msgid);
com_pager_core("common", msgid, TRUE);
}
void
qt_pager(msgid)
const char *msgid;
{
if (!com_pager_core(g.urole.filecode, msgid))
com_pager_core("common", msgid);
if (!com_pager_core(g.urole.filecode, msgid, FALSE))
com_pager_core("common", msgid, TRUE);
}
struct permonst *
+5 -2
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 region.c $NHDT-Date: 1573957877 2019/11/17 02:31:17 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.45 $ */
/* NetHack 3.6 region.c $NHDT-Date: 1579655029 2020/01/22 01:03:49 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.60 $ */
/* Copyright (c) 1996 by Jean-Christophe Collet */
/* NetHack may be freely redistributed. See license for details. */
@@ -1024,7 +1024,10 @@ genericptr_t p2;
pline("%s is burning your %s!", Something,
makeplural(body_part(LUNG)));
You("cough and spit blood!");
losehp(Maybe_Half_Phys(rnd(dam) + 5), "gas cloud", KILLED_BY_AN);
dam = Maybe_Half_Phys(rnd(dam) + 5);
if (Half_gas_damage) /* worn towel */
dam = (dam + 1) / 2;
losehp(dam, "gas cloud", KILLED_BY_AN);
return FALSE;
} else {
You("cough!");
+9 -5
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 role.c $NHDT-Date: 1574648943 2019/11/25 02:29:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.65 $ */
/* NetHack 3.6 role.c $NHDT-Date: 1578947634 2020/01/13 20:33:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.68 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1047,8 +1047,8 @@ int racenum, gendnum, alignnum, pickhow;
gendnum, alignnum)
&& ok_gend(i, racenum,
(gendnum >= 0) ? gendnum : ROLE_RANDOM, alignnum)
&& ok_race(i, racenum,
gendnum, (alignnum >= 0) ? alignnum : ROLE_RANDOM))
&& ok_align(i, racenum,
gendnum, (alignnum >= 0) ? alignnum : ROLE_RANDOM))
set[roles_ok++] = i;
}
if (roles_ok == 0 || (roles_ok > 1 && pickhow == PICK_RIGID))
@@ -1221,7 +1221,7 @@ int alignnum;
/* random; check whether any selection is possible */
for (i = 0; i < ROLE_ALIGNS; i++) {
if (g.rfilter.mask & aligns[i].allow)
return FALSE;
continue;
allow = aligns[i].allow;
if (rolenum >= 0 && rolenum < SIZE(roles) - 1
&& !(allow & roles[rolenum].allow & ROLE_ALIGNMASK))
@@ -1909,9 +1909,13 @@ boolean preselect;
add_menu(where, NO_GLYPH, &any, RS_menu_let[which], 0, ATR_NONE, buf,
MENU_ITEMFLAGS_NONE);
} else if (which == RS_filter) {
char setfiltering[40];
any.a_int = RS_menu_arg(RS_filter);
Sprintf(setfiltering, "%s role/race/&c filtering",
gotrolefilter() ? "Reset" : "Set");
add_menu(where, NO_GLYPH, &any, '~', 0, ATR_NONE,
"Reset role/race/&c filtering", MENU_ITEMFLAGS_NONE);
setfiltering, MENU_ITEMFLAGS_NONE);
} else if (which == ROLE_RANDOM) {
any.a_int = ROLE_RANDOM;
add_menu(where, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
+40 -30
View File
@@ -1569,32 +1569,34 @@ struct mkroom *croom;
if (o->id != -1) {
static const char prize_warning[] = "multiple prizes on %s level";
/* 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
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) */
if (Is_mineend_level(&u.uz)) {
if (otmp->otyp == iflags.mines_prize_type) {
if (!g.mines_prize_count++) {
/* Note: the first luckstone on lev will become the prize
even if its not the explicit one, but random */
otmp->record_achieve_special = MINES_PRIZE;
/* prevent stacking; cleared when achievement is recorded */
otmp->nomerge = 1;
}
/*
* 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
* 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).
*
* Random items of the appropriate type won't trigger a false
* match--they'll fail the (id != -1) test above--but the level
* definition should not include a second instance of any prize.
*/
if (Is_mineend_level(&u.uz)
&& otmp->otyp == g.context.achieveo.mines_prize_type) {
if (!g.context.achieveo.mines_prize_oid) {
g.context.achieveo.mines_prize_oid = otmp->o_id;
/* prevent stacking; cleared when achievement is recorded */
otmp->nomerge = 1;
} else {
impossible(prize_warning, "mines end");
}
} else if (Is_sokoend_level(&u.uz)) {
if (otmp->otyp == iflags.soko_prize_type1) {
otmp->record_achieve_special = SOKO_PRIZE1;
} else if (Is_sokoend_level(&u.uz)
&& (otmp->otyp == g.context.achieveo.soko_prize_typ1
|| otmp->otyp == g.context.achieveo.soko_prize_typ2)) {
if (!g.context.achieveo.soko_prize_oid) {
g.context.achieveo.soko_prize_oid = otmp->o_id;
otmp->nomerge = 1; /* redundant; Sokoban prizes don't stack */
if (++g.soko_prize_count > 1)
impossible(prize_warning, "sokoban end");
} else if (otmp->otyp == iflags.soko_prize_type2) {
otmp->record_achieve_special = SOKO_PRIZE2;
otmp->nomerge = 1; /* redundant; Sokoban prizes don't stack */
if (++g.soko_prize_count > 1)
impossible(prize_warning, "sokoban end");
} else {
impossible(prize_warning, "sokoban end");
}
}
}
@@ -2590,14 +2592,26 @@ const char *s;
/* find by object name */
for (i = 0; i < NUM_OBJECTS; i++) {
objname = obj_descr[i].oc_name;
objname = OBJ_NAME(objects[i]);
if (objname && !strcmpi(s, objname))
return i;
}
/*
* FIXME:
* If the file specifies "orange potion", the actual object
* description is just "orange" and won't match. [There's a
* reason that wish handling is insanely complicated.] And
* even if that gets fixed, if the file specifies "gray stone"
* it will start matching but would always pick the first one.
*
* "orange potion" is an unlikely thing to have in a special
* level description but "gray stone" is not....
*/
/* find by object description */
for (i = 0; i < NUM_OBJECTS; i++) {
objname = obj_descr[i].oc_descr;
objname = OBJ_DESCR(objects[i]);
if (objname && !strcmpi(s, objname))
return i;
}
@@ -5419,10 +5433,6 @@ sp_level_coder_init()
g.splev_init_present = FALSE;
g.icedpools = FALSE;
/* achievement tracking; static init would suffice except we need to
reset if #wizmakemap is used to recreate mines' end or sokoban end;
once either level is created, these values can be forgotten */
g.mines_prize_count = g.soko_prize_count = 0;
for (tmpi = 0; tmpi <= MAX_NESTED_ROOMS; tmpi++) {
coder->tmproomlist[tmpi] = (struct mkroom *) 0;
+10 -6
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 weapon.c $NHDT-Date: 1559998716 2019/06/08 12:58:36 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.70 $ */
/* NetHack 3.6 weapon.c $NHDT-Date: 1579648295 2020/01/21 23:11:35 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.82 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
@@ -95,13 +95,17 @@ struct obj *obj;
switch (skill) {
case P_NONE:
/* not a weapon or weptool: use item class name;
override class name "food" for corpses, tins, and eggs,
"large rock" for statues and boulders, and "tool" for towels */
override class name for things where it sounds strange and
for things that aren't unexpected to find being wielded:
corpses, tins, eggs, and globs avoid "food",
statues and boulders avoid "large rock",
and towels and tin openers avoid "tool" */
descr = (obj->otyp == CORPSE || obj->otyp == TIN || obj->otyp == EGG
|| obj->otyp == STATUE || obj->otyp == BOULDER
|| obj->otyp == TOWEL)
? OBJ_NAME(objects[obj->otyp])
: def_oc_syms[(int) obj->oclass].name;
|| obj->otyp == TOWEL || obj->otyp == TIN_OPENER)
? OBJ_NAME(objects[obj->otyp])
: obj->globby ? "glob"
: def_oc_syms[(int) obj->oclass].name;
break;
case P_SLING:
if (is_ammo(obj))
+8 -1
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 worm.c $NHDT-Date: 1561340880 2019/06/24 01:48:00 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.30 $ */
/* NetHack 3.6 worm.c $NHDT-Date: 1579616437 2020/01/21 14:20:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.40 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2009. */
/* NetHack may be freely redistributed. See license for details. */
@@ -218,6 +218,13 @@ 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])
wgrowtime[wnum] = g.moves + rnd(5);