Merge branch 'NetHack-3.6.2-beta01' into NetHack-3.6.2

This commit is contained in:
nhmall
2018-06-15 22:16:24 -04:00
5 changed files with 299 additions and 74 deletions

View File

@@ -36,6 +36,8 @@ internals for 'sortloot' option have been changed to not reorder the actual
list of objects, so changing it to 'n'one will get the original order list of objects, so changing it to 'n'one will get the original order
back and having a persistent inventory window open when performing back and having a persistent inventory window open when performing
full-pack identify won't result in possibly skipping some items full-pack identify won't result in possibly skipping some items
give vault guards a cursed tin whistle since there is a shrill whistling
sound if hero teleports out of vault while being confronted by guard
Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository
@@ -80,6 +82,9 @@ status_hilite options which use comparisons may now use <= and >= in
addition to previous < and >; in 3.6.1 the latter operated as if addition to previous < and >; in 3.6.1 the latter operated as if
they were <= and >= but now behave as conventional less than and they were <= and >= but now behave as conventional less than and
greater than; old highlight rules using them should be updated greater than; old highlight rules using them should be updated
sortloot option has been enhanced to improve object ordering; primarily,
items of undiscovered type come out before items of discovered type
within each class or sub-class of objects
Code Cleanup and Reorganization Code Cleanup and Reorganization

View File

@@ -193,8 +193,12 @@ enum hmon_atkmode_types {
/* sortloot() return type; needed before extern.h */ /* sortloot() return type; needed before extern.h */
struct sortloot_item { struct sortloot_item {
struct obj *obj; struct obj *obj;
char *str; /* result of loot_xname(obj) in some cases, otherwise null */
int indx; /* signed int, because sortloot()'s qsort comparison routine int indx; /* signed int, because sortloot()'s qsort comparison routine
assumes (a->indx - b->indx) might yield a negative result */ assumes (a->indx - b->indx) might yield a negative result */
xchar class; /* order rather than object class; 0 => not yet init'd */
xchar subclass; /* subclass for some classes */
xchar disco; /* discovery status */
}; };
typedef struct sortloot_item Loot; typedef struct sortloot_item Loot;

View File

@@ -9,6 +9,8 @@
#define CONTAINED_SYM '>' /* designator for inside a container */ #define CONTAINED_SYM '>' /* designator for inside a container */
#define HANDS_SYM '-' #define HANDS_SYM '-'
STATIC_DCL void FDECL(loot_classify, (Loot *, struct obj *));
STATIC_DCL char *FDECL(loot_xname, (struct obj *));
STATIC_DCL int FDECL(CFDECLSPEC sortloot_cmp, (const genericptr, STATIC_DCL int FDECL(CFDECLSPEC sortloot_cmp, (const genericptr,
const genericptr)); const genericptr));
STATIC_DCL void NDECL(reorder_invent); STATIC_DCL void NDECL(reorder_invent);
@@ -46,7 +48,219 @@ static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */
*/ */
static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */ static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */
unsigned sortlootmode = 0; /* sortloot() classification; called at most once for each object sorted */
STATIC_OVL void
loot_classify(sort_item, obj)
Loot *sort_item;
struct obj *obj;
{
/* we may eventually make this a settable option to always use
with sortloot instead of only when the 'sortpack' option isn't
set; it is similar to sortpack's inv_order but items most
likely to be picked up are moved to the front */
static char def_srt_order[MAXOCLASSES] = {
COIN_CLASS, AMULET_CLASS, RING_CLASS, WAND_CLASS, POTION_CLASS,
SCROLL_CLASS, SPBOOK_CLASS, GEM_CLASS, FOOD_CLASS, TOOL_CLASS,
WEAPON_CLASS, ARMOR_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
};
static char armcat[8];
const char *classorder;
char *p;
int k, otyp = obj->otyp, oclass = obj->oclass;
/*
* For the value types assigned by this classification, sortloot()
* will put lower valued ones before higher valued ones.
*/
if (!Blind)
obj->dknown = 1; /* xname(obj) does this; we want it sooner */
/* class order */
classorder = flags.sortpack ? flags.inv_order : def_srt_order;
p = index(classorder, oclass);
if (p)
k = 1 + (int) (p - classorder);
else
k = 1 + (int) strlen(classorder) + (oclass != VENOM_CLASS);
sort_item->class = (xchar) k;
/* subclass designation; only a few classes have subclasses
and the non-armor ones we use are fairly arbitrary */
switch (oclass) {
case ARMOR_CLASS:
if (!armcat[7]) {
/* one-time init; we use a different order than the subclass
values defined by objclass.h */
armcat[ARM_HELM] = 1; /* [2] */
armcat[ARM_GLOVES] = 2; /* [3] */
armcat[ARM_BOOTS] = 3; /* [4] */
armcat[ARM_SHIELD] = 4; /* [1] */
armcat[ARM_CLOAK] = 5; /* [5] */
armcat[ARM_SHIRT] = 6; /* [6] */
armcat[ARM_SUIT] = 7; /* [0] */
armcat[7] = 8; /* sanity protection */
}
k = objects[otyp].oc_armcat;
/* oc_armcat overloads oc_subtyp which is an 'schar' so guard
against somebody assigning something unexpected to it */
if (k < 0 || k >= 7)
k = 7;
k = armcat[k];
break;
case WEAPON_CLASS:
/* for weapons, group by ammo (arrows, bolts), launcher (bows),
missile (darts, boomerangs), stackable (daggers, knives, spears),
'other' (swords, axes, &c), polearms */
k = objects[otyp].oc_skill;
k = (k < 0) ? ((k >= -P_CROSSBOW && k <= -P_BOW) ? 1 : 3)
: ((k >= P_BOW && k <= P_CROSSBOW) ? 2
: (k == P_SPEAR || k == P_DAGGER || k == P_KNIFE) ? 4
: !is_pole(obj) ? 5 : 6);
break;
case TOOL_CLASS:
if (obj->dknown && objects[otyp].oc_name_known
&& (otyp == BAG_OF_TRICKS || otyp == HORN_OF_PLENTY))
k = 2; /* known pseudo-container */
else if (Is_container(obj))
k = 1; /* regular container or unknown bag of tricks */
else
switch (otyp) {
case WOODEN_FLUTE:
case MAGIC_FLUTE:
case TOOLED_HORN:
case FROST_HORN:
case FIRE_HORN:
case WOODEN_HARP:
case MAGIC_HARP:
case BUGLE:
case LEATHER_DRUM:
case DRUM_OF_EARTHQUAKE:
case HORN_OF_PLENTY: /* not a musical instrument */
k = 3; /* instrument or unknown horn of plenty */
default:
k = 4; /* 'other' tool */
}
break;
case FOOD_CLASS:
/* [what about separating "partly eaten" within each group?] */
switch (otyp) {
case SLIME_MOLD:
k = 1;
break;
default:
/* [maybe separate one-bite foods from rations and such?] */
k = obj->globby ? 6 : 2;
break;
case TIN:
k = 3;
break;
case EGG:
k = 4;
break;
case CORPSE:
k = 5;
break;
}
break;
default:
/* other classes don't have subclasses; we assign a nonzero
value because sortloot() uses 0 to mean 'not yet classified' */
k = 1; /* any non-zero would do */
break;
}
sort_item->subclass = (xchar) k;
/* discovery status */
k = !obj->dknown ? 1 /* unseen */
: (objects[otyp].oc_name_known || !OBJ_DESCR(objects[otyp])) ? 4
: (objects[otyp].oc_uname)? 3 /* named (partially discovered) */
: 2; /* undiscovered */
sort_item->disco = (xchar) k;
}
/* sortloot() formatting routine; for alphabetizing, not shown to user */
STATIC_OVL char *
loot_xname(obj)
struct obj *obj;
{
struct obj saveo;
boolean save_debug;
char *res, *save_oname;
/*
* Deal with things that xname() includes as a prefix. We don't
* want such because they change alphabetical ordering. First,
* remember 'obj's current settings.
*/
saveo.odiluted = obj->odiluted;
saveo.blessed = obj->blessed, saveo.cursed = obj->cursed;
saveo.spe = obj->spe;
saveo.owt = obj->owt;
save_oname = has_oname(obj) ? ONAME(obj) : 0;
save_debug = flags.debug;
/* suppress "diluted" for potions and "holy/unholy" for water;
sortloot() will deal with them using other criteria than name */
if (obj->oclass == POTION_CLASS) {
obj->odiluted = 0;
if (obj->otyp == POT_WATER)
obj->blessed = 0, obj->cursed = 0;
}
/* make "wet towel" and "moist towel" format as "towel" so that all
three group together */
if (obj->otyp == TOWEL)
obj->spe = 0;
/* group "<size> glob of <foo>" by <foo> rather than by <size> */
if (obj->globby)
obj->owt = 200; /* 200: weight of combined glob from ten creatures
(five or fewer is "small", more than fifteen is
"large", in between has no prefix) */
/* suppress user-assigned name */
if (save_oname && !obj->oartifact)
ONAME(obj) = 0;
/* avoid wizard mode formatting variations */
if (wizard) { /* flags.debug */
/* paranoia: before toggling off wizard mode, guard against a
panic in xname() producing a normal mode panic save file */
program_state.something_worth_saving = 0;
flags.debug = FALSE;
}
res = cxname_singular(obj);
if (save_debug) {
flags.debug = TRUE;
program_state.something_worth_saving = 1;
}
/* restore the object */
if (obj->oclass == POTION_CLASS) {
obj->odiluted = saveo.odiluted;
if (obj->otyp == POT_WATER)
obj->blessed = saveo.blessed, obj->cursed = saveo.cursed;
}
if (obj->otyp == TOWEL) {
obj->spe = saveo.spe;
/* give "towel" a suffix that will force wet ones to come first,
moist ones next, and dry ones last regardless of whether
they've been flagged as having spe known */
Strcat(res, is_wet_towel(obj) ? ((obj->spe >= 3) ? "x" : "y") : "z");
}
if (obj->globby) {
obj->owt = saveo.owt;
/* we've suppressed the size prefix (above); there normally won't
be more than one of a given creature type because they coalesce,
but globs with different bless/curse state won't merge so it is
feasible to have multiple at the same location; add a suffix to
get such sorted by size (small first) */
Strcat(res, (obj->owt <= 100) ? "a"
: (obj->owt <= 300) ? "b"
: (obj->owt <= 500) ? "c"
: "d");
}
if (save_oname && !obj->oartifact)
ONAME(obj) = save_oname;
return res;
}
/* set by sortloot() for use by sortloot_cmp(); reset by sortloot when done */
static unsigned sortlootmode = 0;
/* qsort comparison routine for sortloot() */ /* qsort comparison routine for sortloot() */
STATIC_OVL int CFDECLSPEC STATIC_OVL int CFDECLSPEC
@@ -57,57 +271,46 @@ const genericptr vptr2;
struct sortloot_item *sli1 = (struct sortloot_item *) vptr1, struct sortloot_item *sli1 = (struct sortloot_item *) vptr1,
*sli2 = (struct sortloot_item *) vptr2; *sli2 = (struct sortloot_item *) vptr2;
struct obj *obj1 = sli1->obj, struct obj *obj1 = sli1->obj,
*obj2 = sli2->obj, *obj2 = sli2->obj;
sav1, sav2; char *nam1, *nam2;
char *cls1, *cls2, nam1[BUFSZ], nam2[BUFSZ];
int val1, val2, c, namcmp; int val1, val2, c, namcmp;
/* order by object class like inventory display */ /* order by object class unless we're doing by-invlet without sortpack */
if ((sortlootmode & SORTLOOT_PACK) != 0) { if ((sortlootmode & (SORTLOOT_PACK | SORTLOOT_INVLET))
cls1 = index(flags.inv_order, obj1->oclass); != SORTLOOT_INVLET) {
cls2 = index(flags.inv_order, obj2->oclass); /* Classify each object at most once no matter how many
if (cls1 != cls2) comparisons it is involved in. */
return (int) (cls1 - cls2); if (!sli1->class)
loot_classify(sli1, obj1);
if (!sli2->class)
loot_classify(sli2, obj2);
if ((sortlootmode & SORTLOOT_INVLET) != 0) { /* Sort by class. */
; /* skip sub-classes when sorting by packorder+invlet */ val1 = sli1->class;
val2 = sli2->class;
if (val1 != val2)
return (int) (val1 - val2);
/* for armor, group by sub-category */ /* skip sub-classes when ordering by sortpack+invlet */
} else if (obj1->oclass == ARMOR_CLASS) { if ((sortlootmode & SORTLOOT_INVLET) == 0) {
static int armcat[7 + 1]; /* Class matches; sort by subclass. */
val1 = sli1->subclass;
if (!armcat[7]) { val2 = sli2->subclass;
/* one-time init; we want to control the order */
armcat[ARM_HELM] = 1; /* [2] */
armcat[ARM_GLOVES] = 2; /* [3] */
armcat[ARM_BOOTS] = 3; /* [4] */
armcat[ARM_SHIELD] = 4; /* [1] */
armcat[ARM_CLOAK] = 5; /* [5] */
armcat[ARM_SHIRT] = 6; /* [6] */
armcat[ARM_SUIT] = 7; /* [0] */
armcat[7] = 8;
}
val1 = armcat[objects[obj1->otyp].oc_armcat];
val2 = armcat[objects[obj2->otyp].oc_armcat];
if (val1 != val2) if (val1 != val2)
return val1 - val2; return val1 - val2;
/* for weapons, group by ammo (arrows, bolts), launcher (bows), /* Class and subclass match; sort by discovery status:
missile (dart, boomerang), stackable (daggers, knives, spears), * first unseen, then seen but not named or discovered,
'other' (swords, axes, &c), polearm */ * then named, lastly discovered.
} else if (obj1->oclass == WEAPON_CLASS) { * 1) potion
val1 = objects[obj1->otyp].oc_skill; * 2) pink potion
val1 = (val1 < 0) * 3) dark green potion called confusion
? (val1 >= -P_CROSSBOW && val1 <= -P_BOW) ? 1 : 3 * 4) potion of healing
: (val1 >= P_BOW && val1 <= P_CROSSBOW) ? 2 * Multiple entries within each group will be put into
: (val1 == P_SPEAR || val1 == P_DAGGER * alphabetical order below.
|| val1 == P_KNIFE) ? 4 : !is_pole(obj1) ? 5 : 6; */
val2 = objects[obj2->otyp].oc_skill; val1 = sli1->disco;
val2 = (val2 < 0) val2 = sli2->disco;
? (val2 >= -P_CROSSBOW && val2 <= -P_BOW) ? 1 : 3
: (val2 >= P_BOW && val2 <= P_CROSSBOW) ? 2
: (val2 == P_SPEAR || val2 == P_DAGGER
|| val2 == P_KNIFE) ? 4 : !is_pole(obj2) ? 5 : 6;
if (val1 != val2) if (val1 != val2)
return val1 - val2; return val1 - val2;
} }
@@ -136,28 +339,16 @@ const genericptr vptr2;
/* /*
* Sort object names in lexicographical order, ignoring quantity. * Sort object names in lexicographical order, ignoring quantity.
*
* Each obj gets formatted at most once (per sort) no matter how many
* comparisons it gets subjected to.
*/ */
/* Force diluted potions to come out after undiluted of same type; nam1 = sli1->str;
obj->odiluted overloads obj->oeroded. */ if (!nam1)
sav1.odiluted = obj1->odiluted; nam1 = sli1->str = dupstr(loot_xname(obj1));
sav2.odiluted = obj2->odiluted; nam2 = sli2->str;
if (obj1->oclass == POTION_CLASS) if (!nam2)
obj1->odiluted = 0; nam2 = sli2->str = dupstr(loot_xname(obj2));
if (obj1->oclass == POTION_CLASS)
obj2->odiluted = 0;
/* Force holy and unholy water to sort adjacent to water rather
than among 'h's and 'u's. BUCX order will keep them distinct. */
Strcpy(nam1, cxname_singular(obj1));
if (obj1->otyp == POT_WATER && obj1->bknown
&& (obj1->blessed || obj1->cursed))
(void) strsubst(nam1, obj1->blessed ? "holy " : "unholy ", "");
Strcpy(nam2, cxname_singular(obj2));
if (obj2->otyp == POT_WATER && obj2->bknown
&& (obj2->blessed || obj2->cursed))
(void) strsubst(nam2, obj2->blessed ? "holy " : "unholy ", "");
obj1->odiluted = sav1.odiluted;
obj2->odiluted = sav2.odiluted;
if ((namcmp = strcmpi(nam1, nam2)) != 0) if ((namcmp = strcmpi(nam1, nam2)) != 0)
return namcmp; return namcmp;
@@ -227,7 +418,7 @@ tiebreak:
* whether the list was already sorted as it got ready to do the * whether the list was already sorted as it got ready to do the
* sorting, so re-examining inventory or a pile of objects without * sorting, so re-examining inventory or a pile of objects without
* having changed anything would gobble up less CPU than a full * having changed anything would gobble up less CPU than a full
* sort. But it had as least two problems (aside from the ordinary * sort. But it had at least two problems (aside from the ordinary
* complement of bugs): * complement of bugs):
* 1) some players wanted to get the original order back when they * 1) some players wanted to get the original order back when they
* changed the 'sortloot' option back to 'none', but the list * changed the 'sortloot' option back to 'none', but the list
@@ -267,26 +458,38 @@ boolean FDECL((*filterfunc), (OBJ_P));
/* note: if there is a filter function, this might overallocate */ /* note: if there is a filter function, this might overallocate */
sliarray = (Loot *) alloc((n + 1) * sizeof *sliarray); sliarray = (Loot *) alloc((n + 1) * sizeof *sliarray);
/* the 'keep cockatrice corpses' flag is overloaded with sort mode */
augment_filter = (mode & SORTLOOT_PETRIFY) ? TRUE : FALSE; augment_filter = (mode & SORTLOOT_PETRIFY) ? TRUE : FALSE;
mode &= ~SORTLOOT_PETRIFY; /* remove flag, leaving mode */
/* populate aliarray[0..n-1] */ /* populate aliarray[0..n-1] */
for (i = 0, o = *olist; o; ++i, o = by_nexthere ? o->nexthere : o->nobj) { for (i = 0, o = *olist; o; ++i, o = by_nexthere ? o->nexthere : o->nobj) {
if (filterfunc && !(*filterfunc)(o) if (filterfunc && !(*filterfunc)(o)
/* caller may be asking us to override filterfunc (in order
to do a cockatrice corpse touch check during pickup even
if/when the filter rejects food class) */
&& (!augment_filter || o->otyp != CORPSE && (!augment_filter || o->otyp != CORPSE
|| !touch_petrifies(&mons[o->corpsenm]))) || !touch_petrifies(&mons[o->corpsenm])))
continue; continue;
sliarray[i].obj = o, sliarray[i].indx = (int) i; sliarray[i].obj = o, sliarray[i].indx = (int) i;
sliarray[i].str = (char *) 0;
sliarray[i].class = sliarray[i].subclass = sliarray[i].disco = 0;
} }
n = i; n = i;
/* add a terminator so that we don't have to pass 'n' back to caller */ /* 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; sliarray[n].obj = (struct obj *) 0, sliarray[n].indx = -1;
mode &= ~SORTLOOT_PETRIFY; sliarray[n].str = (char *) 0;
sliarray[n].class = sliarray[n].subclass = sliarray[n].disco = 0;
/* do the sort; if no sorting is requested, we'll just return /* do the sort; if no sorting is requested, we'll just return
a sortloot_item array reflecting the current ordering */ a sortloot_item array reflecting the current ordering */
if (mode) { if (mode && n > 1) {
sortlootmode = mode; /* extra input for sortloot_cmp() */ sortlootmode = mode; /* extra input for sortloot_cmp() */
qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp); qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp);
sortlootmode = 0; /* reset static mode flags */ sortlootmode = 0; /* reset static mode flags */
/* if sortloot_cmp formatted any objects, discard their strings now */
for (i = 0; i < n; ++i)
if (sliarray[i].str)
free((genericptr_t) sliarray[i].str), sliarray[i].str = 0;
} }
return sliarray; return sliarray;
} }

View File

@@ -653,16 +653,26 @@ register struct monst *mtmp;
nhUse(mac); /* suppress 'dead increment' from static analyzer */ nhUse(mac); /* suppress 'dead increment' from static analyzer */
if (ptr != &mons[PM_GUARD] && ptr != &mons[PM_WATCHMAN] if (ptr == &mons[PM_WATCH_CAPTAIN]) {
&& ptr != &mons[PM_WATCH_CAPTAIN]) { ; /* better weapon rather than extra gear here */
} else if (ptr == &mons[PM_WATCHMAN]) {
if (rn2(3)) /* most watchmen carry a whistle */
(void) mongets(mtmp, TIN_WHISTLE);
} else if (ptr == &mons[PM_GUARD]) {
/* if hero teleports out of a vault while being confronted
by the vault's guard, there is a shrill whistling sound,
so guard evidently carries a cursed whistle */
otmp = mksobj(TIN_WHISTLE, TRUE, FALSE);
curse(otmp);
(void) mpickobj(mtmp, otmp);
} else { /* soldiers and their officers */
if (!rn2(3)) if (!rn2(3))
(void) mongets(mtmp, K_RATION); (void) mongets(mtmp, K_RATION);
if (!rn2(2)) if (!rn2(2))
(void) mongets(mtmp, C_RATION); (void) mongets(mtmp, C_RATION);
if (ptr != &mons[PM_SOLDIER] && !rn2(3)) if (ptr != &mons[PM_SOLDIER] && !rn2(3))
(void) mongets(mtmp, BUGLE); (void) mongets(mtmp, BUGLE);
} else if (ptr == &mons[PM_WATCHMAN] && rn2(3)) }
(void) mongets(mtmp, TIN_WHISTLE);
} else if (ptr == &mons[PM_SHOPKEEPER]) { } else if (ptr == &mons[PM_SHOPKEEPER]) {
(void) mongets(mtmp, SKELETON_KEY); (void) mongets(mtmp, SKELETON_KEY);
switch (rn2(4)) { switch (rn2(4)) {

View File

@@ -665,7 +665,10 @@ register struct monst *grd;
grd->mpeaceful = 0; grd->mpeaceful = 0;
letknow: letknow:
if (!cansee(grd->mx, grd->my) || !mon_visible(grd)) if (!cansee(grd->mx, grd->my) || !mon_visible(grd))
You_hear("the shrill sound of a guard's whistle."); You_hear("%s.",
m_carrying(grd, TIN_WHISTLE)
? "the shrill sound of a guard's whistle"
: "angry shouting");
else else
You(um_dist(grd->mx, grd->my, 2) You(um_dist(grd->mx, grd->my, 2)
? "see %s approaching." ? "see %s approaching."