Merge branch 'NetHack-3.6.2'

This commit is contained in:
nhmall
2018-06-11 16:36:27 -04:00
42 changed files with 641 additions and 335 deletions

14
src/.gitattributes vendored Normal file
View File

@@ -0,0 +1,14 @@
* NH_filestag=(file%s_for_all_versions)
..files NH_filegenerated=Makefile,Makefile.bcc,Makefile.gcc,qt_kde0.moc,qt_win.moc,qttableview.moc,tile.c,monstr.c,vis_tab.c
Makefile.bcc NH_filesgentag=(file%s_for_win32_that_are_moved_into_src_at_compile_time)
Makefile.gcc NH_filesgentag=>Makefile.bcc
Makefile NH_filesgentag=>Makefile.bcc
qt_kde0.moc NH_filesgentag=(file%s_generated_by_'moc'_for_Qt_interface_at_compile_time)
qt_win.moc NH_filesgentag=>qt_kde0.moc
qttableview.moc NH_filesgentag=>qt_kde0.moc
tile.c NH_filesgentag=(file%s_optionally_generated_by_tilemap_at_compile_time)
monstr.c NH_filesgentag=(file%s_generated_by_makedefs_at_compile_time)
vis_tab.c NH_filesgentag=>monstr.c

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 end.c $NHDT-Date: 1512803167 2017/12/09 07:06:07 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.137 $ */
/* NetHack 3.6 end.c $NHDT-Date: 1528332335 2018/06/07 00:45:35 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.141 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -985,7 +985,7 @@ winid endwin;
if (counting) {
nowrap_add(u.urexp, points);
} else {
makeknown(otmp->otyp);
discover_object(otmp->otyp, TRUE, FALSE);
otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
/* assumes artifacts don't have quan > 1 */
Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
@@ -1178,7 +1178,7 @@ int how;
* it in both of those places.
*/
for (obj = invent; obj; obj = obj->nobj) {
makeknown(obj->otyp);
discover_object(obj->otyp, TRUE, FALSE);
obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
if (Is_container(obj) || obj->otyp == STATUE)
obj->cknown = obj->lknown = 1;
@@ -1400,7 +1400,7 @@ int how;
continue;
if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
otmp = mksobj(typ, FALSE, FALSE);
makeknown(otmp->otyp);
discover_object(otmp->otyp, TRUE, FALSE);
otmp->known = 1; /* for fake amulets */
otmp->dknown = 1; /* seen it (blindness fix) */
if (has_oname(otmp))
@@ -1498,18 +1498,20 @@ boolean identified, all_containers, reportempty;
continue; /* wrong type of container */
} else if (box->cobj) {
winid tmpwin = create_nhwindow(NHW_MENU);
Loot *sortedcobj, *srtc;
unsigned sortflags;
sortloot(&box->cobj,
(((flags.sortloot == 'l' || flags.sortloot == 'f')
? SORTLOOT_LOOT : 0)
| (flags.sortpack ? SORTLOOT_PACK : 0)),
FALSE);
Sprintf(buf, "Contents of %s:", the(xname(box)));
putstr(tmpwin, 0, buf);
putstr(tmpwin, 0, "");
for (obj = box->cobj; obj; obj = obj->nobj) {
sortflags = (((flags.sortloot == 'l' || flags.sortloot == 'f')
? SORTLOOT_LOOT : 0)
| (flags.sortpack ? SORTLOOT_PACK : 0));
sortedcobj = sortloot(&box->cobj, sortflags, FALSE,
(boolean FDECL((*), (OBJ_P))) 0);
for (srtc = sortedcobj; ((obj = srtc->obj) != 0); ++srtc) {
if (identified) {
makeknown(obj->otyp);
discover_object(obj->otyp, TRUE, FALSE);
obj->known = obj->bknown = obj->dknown
= obj->rknown = 1;
if (Is_container(obj) || obj->otyp == STATUE)
@@ -1517,6 +1519,7 @@ boolean identified, all_containers, reportempty;
}
putstr(tmpwin, 0, doname(obj));
}
unsortloot(&sortedcobj);
if (cat)
putstr(tmpwin, 0, "Schroedinger's cat");
else if (deadcat)

View File

@@ -46,10 +46,6 @@ static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */
*/
static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */
struct sortloot_item {
struct obj *obj;
int indx;
};
unsigned sortlootmode = 0;
/* qsort comparison routine for sortloot() */
@@ -211,6 +207,100 @@ tiebreak:
return (sli1->indx - sli2->indx);
}
/*
* sortloot() - the story so far...
*
* The original implementation constructed and returned an array
* of pointers to objects in the requested order. Callers had to
* count the number of objects, allocate the array, pass one
* object at a time to the routine which populates it, traverse
* the objects via stepping through the array, then free the
* array. The ordering process used a basic insertion sort which
* is fine for short lists but inefficient for long ones.
*
* 3.6.0 (and continuing with 3.6.1) changed all that so that
* sortloot was self-contained as far as callers were concerned.
* It reordered the linked list into the requested order and then
* normal list traversal was used to process it. It also switched
* to qsort() on the assumption that the C library implementation
* put some effort into sorting efficiently. It also checked
* whether the list was already sorted as it got ready to do the
* sorting, so re-examining inventory or a pile of objects without
* having changed anything would gobble up less CPU than a full
* sort. But it had as least two problems (aside from the ordinary
* complement of bugs):
* 1) some players wanted to get the original order back when they
* changed the 'sortloot' option back to 'none', but the list
* reordering made that infeasible;
* 2) object identification giving the 'ID whole pack' result
* would call makeknown() on each newly ID'd object, that would
* call update_inventory() to update the persistent inventory
* window if one existed, the interface would call the inventory
* display routine which would call sortloot() which might change
* the order of the list being traversed by the identify code,
* possibly skipping the ID of some objects. That could have been
* avoided by suppressing 'perm_invent' during identification
* (fragile) or by avoiding sortloot() during inventory display
* (more robust).
*
* 3.6.2 reverts to the temporary array of ordered obj pointers
* but has sortloot() do the counting and allocation. Callers
* need to use array traversal instead of linked list traversal
* and need to free the temporary array when done. And the
* array contains 'struct sortloot_item' (aka 'Loot') entries
* instead of simple 'struct obj *' entries.
*/
Loot *
sortloot(olist, mode, by_nexthere, filterfunc)
struct obj **olist; /* previous version might have changed *olist, we don't */
unsigned mode; /* flags for sortloot_cmp() */
boolean by_nexthere; /* T: traverse via obj->nexthere, F: via obj->nobj */
boolean FDECL((*filterfunc), (OBJ_P));
{
Loot *sliarray;
struct obj *o;
unsigned n, i;
boolean augment_filter;
for (n = 0, o = *olist; o; o = by_nexthere ? o->nexthere : o->nobj)
++n;
/* note: if there is a filter function, this might overallocate */
sliarray = (Loot *) alloc((n + 1) * sizeof *sliarray);
augment_filter = (mode & SORTLOOT_PETRIFY) ? TRUE : FALSE;
/* populate aliarray[0..n-1] */
for (i = 0, o = *olist; o; ++i, o = by_nexthere ? o->nexthere : o->nobj) {
if (filterfunc && !(*filterfunc)(o)
&& (!augment_filter || o->otyp != CORPSE
|| !touch_petrifies(&mons[o->corpsenm])))
continue;
sliarray[i].obj = o, sliarray[i].indx = (int) i;
}
n = i;
/* add a terminator so that we don't have to pass 'n' back to caller */
sliarray[n].obj = (struct obj *) 0, sliarray[n].indx = -1;
mode &= ~SORTLOOT_PETRIFY;
/* do the sort; if no sorting is requested, we'll just return
a sortloot_item array reflecting the current ordering */
if (mode) {
sortlootmode = mode; /* extra input for sortloot_cmp() */
qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp);
sortlootmode = 0; /* reset static mode flags */
}
return sliarray;
}
/* sortloot() callers should use this to free up memory it allocates */
void
unsortloot(loot_array_p)
Loot **loot_array_p;
{
if (*loot_array_p)
free((genericptr_t) *loot_array_p), *loot_array_p = (Loot *) 0;
}
#if 0 /* 3.6.0 'revamp' */
void
sortloot(olist, mode, by_nexthere)
struct obj **olist;
@@ -248,6 +338,7 @@ boolean by_nexthere; /* T: traverse via obj->nexthere, F: via obj->nobj */
}
sortlootmode = 0;
}
#endif /*0*/
void
assigninvlet(otmp)
@@ -1097,6 +1188,7 @@ register const char *let, *word;
boolean msggiven = FALSE;
boolean oneloop = FALSE;
long dummymask;
Loot *sortedinvent, *srtinv;
if (*let == ALLOW_COUNT)
let++, allowcnt = 1;
@@ -1133,15 +1225,13 @@ register const char *let, *word;
if (!flags.invlet_constant)
reassign();
else
/* in case invent is in packorder, force it to be in invlet
order before collecing candidate inventory letters;
if player responds with '?' or '*' it will be changed
back by display_pickinv(), but by then we'll have 'lets'
and so won't have to re-sort in the for(;;) loop below */
sortloot(&invent, SORTLOOT_INVLET, FALSE);
for (otmp = invent; otmp; otmp = otmp->nobj) {
/* force invent to be in invlet order before collecting candidate
inventory letters */
sortedinvent = sortloot(&invent, SORTLOOT_INVLET, FALSE,
(boolean FDECL((*), (OBJ_P))) 0);
for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) {
if (&bp[foo] == &buf[sizeof buf - 1]
|| ap == &altlets[sizeof altlets - 1]) {
/* we must have a huge number of NOINVSYM items somehow */
@@ -1285,6 +1375,7 @@ register const char *let, *word;
allowall = usegold = TRUE;
}
}
unsortloot(&sortedinvent);
bp[foo] = 0;
if (foo == 0 && bp > buf && bp[-1] == ' ')
@@ -1763,16 +1854,17 @@ unsigned *resultflags;
*/
int
askchain(objchn, olets, allflag, fn, ckfn, mx, word)
struct obj **objchn;
struct obj **objchn; /* *objchn might change */
int allflag, mx;
const char *olets, *word; /* olets is an Obj Class char array */
int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P));
{
struct obj *otmp, *otmpo;
register char sym, ilet;
register int cnt = 0, dud = 0, tmp;
int cnt = 0, dud = 0, tmp;
boolean takeoff, nodot, ident, take_out, put_in, first, ininv, bycat;
char qbuf[QBUFSZ], qpfx[QBUFSZ];
Loot *sortedchn = 0;
takeoff = taking_off(word);
ident = !strcmp(word, "identify");
@@ -1787,7 +1879,8 @@ int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P));
/* someday maybe we'll sort by 'olets' too (temporarily replace
flags.packorder and pass SORTLOOT_PACK), but not yet... */
sortloot(objchn, SORTLOOT_INVLET, FALSE);
sortedchn = sortloot(objchn, SORTLOOT_INVLET, FALSE,
(boolean FDECL((*), (OBJ_P))) 0);
first = TRUE;
/*
@@ -1812,7 +1905,7 @@ nextclass:
* track of which list elements have already been processed.
*/
bypass_objlist(*objchn, FALSE); /* clear chain's bypass bits */
while ((otmp = nxt_unbypassed_obj(*objchn)) != 0) {
while ((otmp = nxt_unbypassed_loot(sortedchn, *objchn)) != 0) {
if (ilet == 'z')
ilet = 'A';
else if (ilet == 'Z')
@@ -1900,11 +1993,13 @@ nextclass:
}
if (olets && *olets && *++olets)
goto nextclass;
if (!takeoff && (dud || cnt))
pline("That was all.");
else if (!dud && !cnt)
pline("No applicable objects.");
ret:
unsortloot(&sortedchn);
bypass_objlist(*objchn, FALSE);
return cnt;
}
@@ -2195,6 +2290,8 @@ long *out_cnt;
winid win; /* windows being used */
anything any;
menu_item *selected;
unsigned sortflags;
Loot *sortedinvent, *srtinv;
if (lets && !*lets)
lets = 0; /* simplify tests: (lets) instead of (lets && *lets) */
@@ -2268,10 +2365,11 @@ long *out_cnt;
return ret;
}
sortloot(&invent,
(((flags.sortloot == 'f') ? SORTLOOT_LOOT : SORTLOOT_INVLET)
| (flags.sortpack ? SORTLOOT_PACK : 0)),
FALSE);
sortflags = (flags.sortloot == 'f') ? SORTLOOT_LOOT : SORTLOOT_INVLET;
if (flags.sortpack)
sortflags |= SORTLOOT_PACK;
sortedinvent = sortloot(&invent, sortflags, FALSE,
(boolean FDECL((*), (OBJ_P))) 0);
start_menu(win);
any = zeroany;
@@ -2296,7 +2394,7 @@ long *out_cnt;
}
nextclass:
classcount = 0;
for (otmp = invent; otmp; otmp = otmp->nobj) {
for (srtinv = sortedinvent; (otmp = srtinv->obj) != 0; ++srtinv) {
if (lets && !index(lets, otmp->invlet))
continue;
if (!flags.sortpack || otmp->oclass == *invlet) {
@@ -2330,6 +2428,7 @@ nextclass:
add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE,
"(list everything)", MENU_UNSELECTED);
}
unsortloot(&sortedinvent);
/* for permanent inventory where we intend to show everything but
nothing has been listed (because there isn't anyhing to list;
recognized via any.a_char still being zero; the n==0 case above

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 o_init.c $NHDT-Date: 1450318588 2015/12/17 02:16:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.22 $ */
/* NetHack 3.6 o_init.c $NHDT-Date: 1528332336 2018/06/07 00:45:36 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.24 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
@@ -361,7 +361,8 @@ boolean credit_hero;
if (credit_hero)
exercise(A_WIS, TRUE);
}
if (moves > 1L)
/* moves==1L => initial inventory, gameover => final disclosure */
if (moves > 1L && !program_state.gameover)
update_inventory();
}
}

View File

@@ -577,7 +577,8 @@ int what; /* should be a long */
if (flags.menu_style != MENU_TRADITIONAL || iflags.menu_requested) {
/* use menus exclusively */
traverse_how |= AUTOSELECT_SINGLE | INVORDER_SORT;
traverse_how |= AUTOSELECT_SINGLE
| (flags.sortpack ? INVORDER_SORT : 0);
if (count) { /* looking for N of something */
char qbuf[QBUFSZ];
@@ -849,6 +850,8 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
boolean printed_type_name, first,
sorted = (qflags & INVORDER_SORT) != 0,
engulfer = (qflags & INCLUDE_HERO) != 0;
unsigned sortflags;
Loot *sortedolist, *srtoli;
*pick_list = (menu_item *) 0;
if (!olist && !engulfer)
@@ -876,16 +879,14 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
return 1;
}
if (sorted || flags.sortloot != 'n') {
sortloot(&olist,
(((flags.sortloot == 'f'
|| (flags.sortloot == 'l' && !(qflags & USE_INVLET)))
? SORTLOOT_LOOT
: (qflags & USE_INVLET) ? SORTLOOT_INVLET : 0)
| (flags.sortpack ? SORTLOOT_PACK : 0)),
(qflags & BY_NEXTHERE) ? TRUE : FALSE);
*olist_p = olist;
}
sortflags = (((flags.sortloot == 'f'
|| (flags.sortloot == 'l' && !(qflags & USE_INVLET)))
? SORTLOOT_LOOT
: ((qflags & USE_INVLET) ? SORTLOOT_INVLET : 0))
| (flags.sortpack ? SORTLOOT_PACK : 0)
| ((qflags & FEEL_COCKATRICE) ? SORTLOOT_PETRIFY : 0));
sortedolist = sortloot(&olist, sortflags,
(qflags & BY_NEXTHERE) ? TRUE : FALSE, allow);
win = create_nhwindow(NHW_MENU);
start_menu(win);
@@ -893,14 +894,14 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
/*
* Run through the list and add the objects to the menu. If
* INVORDER_SORT is set, we'll run through the list once for
* each type so we can group them. The allow function will only
* be called once per object in the list.
* each type so we can group them. The allow function was
* called by sortloot() and will be called once per item here.
*/
pack = flags.inv_order;
first = TRUE;
do {
printed_type_name = FALSE;
for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
for (srtoli = sortedolist; ((curr = srtoli->obj) != 0); ++srtoli) {
if (sorted && curr->oclass != *pack)
continue;
if ((qflags & FEEL_COCKATRICE) && curr->otyp == CORPSE
@@ -932,6 +933,7 @@ boolean FDECL((*allow), (OBJ_P)); /* allow function */
}
pack++;
} while (sorted && *pack);
unsortloot(&sortedolist);
if (engulfer) {
char buf[BUFSZ];

View File

@@ -777,6 +777,30 @@ struct obj *objchain;
return objchain;
}
/* like nxt_unbypassed_obj() but operates on sortloot_item array rather
than an object linked list; the array contains obj==Null terminator;
there's an added complication that the array may have stale pointers
for deleted objects (see Multiple-Drop case in askchain(invent.c)) */
struct obj *
nxt_unbypassed_loot(lootarray, listhead)
Loot *lootarray;
struct obj *listhead;
{
struct obj *o, *obj;
while ((obj = lootarray->obj) != 0) {
for (o = listhead; o; o = o->nobj)
if (o == obj)
break;
if (o && !obj->bypass) {
bypass_obj(obj);
break;
}
++lootarray;
}
return obj;
}
void
mon_break_armor(mon, polyspot)
struct monst *mon;