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
back and having a persistent inventory window open when performing
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
@@ -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
they were <= and >= but now behave as conventional less than and
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

View File

@@ -193,8 +193,12 @@ enum hmon_atkmode_types {
/* sortloot() return type; needed before extern.h */
struct sortloot_item {
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
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;

View File

@@ -9,6 +9,8 @@
#define CONTAINED_SYM '>' /* designator for inside a container */
#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,
const genericptr));
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) */
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() */
STATIC_OVL int CFDECLSPEC
@@ -57,57 +271,46 @@ const genericptr vptr2;
struct sortloot_item *sli1 = (struct sortloot_item *) vptr1,
*sli2 = (struct sortloot_item *) vptr2;
struct obj *obj1 = sli1->obj,
*obj2 = sli2->obj,
sav1, sav2;
char *cls1, *cls2, nam1[BUFSZ], nam2[BUFSZ];
*obj2 = sli2->obj;
char *nam1, *nam2;
int val1, val2, c, namcmp;
/* order by object class like inventory display */
if ((sortlootmode & SORTLOOT_PACK) != 0) {
cls1 = index(flags.inv_order, obj1->oclass);
cls2 = index(flags.inv_order, obj2->oclass);
if (cls1 != cls2)
return (int) (cls1 - cls2);
/* order by object class unless we're doing by-invlet without sortpack */
if ((sortlootmode & (SORTLOOT_PACK | SORTLOOT_INVLET))
!= SORTLOOT_INVLET) {
/* Classify each object at most once no matter how many
comparisons it is involved in. */
if (!sli1->class)
loot_classify(sli1, obj1);
if (!sli2->class)
loot_classify(sli2, obj2);
if ((sortlootmode & SORTLOOT_INVLET) != 0) {
; /* skip sub-classes when sorting by packorder+invlet */
/* Sort by class. */
val1 = sli1->class;
val2 = sli2->class;
if (val1 != val2)
return (int) (val1 - val2);
/* for armor, group by sub-category */
} else if (obj1->oclass == ARMOR_CLASS) {
static int armcat[7 + 1];
if (!armcat[7]) {
/* 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];
/* skip sub-classes when ordering by sortpack+invlet */
if ((sortlootmode & SORTLOOT_INVLET) == 0) {
/* Class matches; sort by subclass. */
val1 = sli1->subclass;
val2 = sli2->subclass;
if (val1 != val2)
return val1 - val2;
/* for weapons, group by ammo (arrows, bolts), launcher (bows),
missile (dart, boomerang), stackable (daggers, knives, spears),
'other' (swords, axes, &c), polearm */
} else if (obj1->oclass == WEAPON_CLASS) {
val1 = objects[obj1->otyp].oc_skill;
val1 = (val1 < 0)
? (val1 >= -P_CROSSBOW && val1 <= -P_BOW) ? 1 : 3
: (val1 >= P_BOW && val1 <= P_CROSSBOW) ? 2
: (val1 == P_SPEAR || val1 == P_DAGGER
|| val1 == P_KNIFE) ? 4 : !is_pole(obj1) ? 5 : 6;
val2 = objects[obj2->otyp].oc_skill;
val2 = (val2 < 0)
? (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;
/* Class and subclass match; sort by discovery status:
* first unseen, then seen but not named or discovered,
* then named, lastly discovered.
* 1) potion
* 2) pink potion
* 3) dark green potion called confusion
* 4) potion of healing
* Multiple entries within each group will be put into
* alphabetical order below.
*/
val1 = sli1->disco;
val2 = sli2->disco;
if (val1 != val2)
return val1 - val2;
}
@@ -136,28 +339,16 @@ const genericptr vptr2;
/*
* 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;
obj->odiluted overloads obj->oeroded. */
sav1.odiluted = obj1->odiluted;
sav2.odiluted = obj2->odiluted;
if (obj1->oclass == POTION_CLASS)
obj1->odiluted = 0;
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;
nam1 = sli1->str;
if (!nam1)
nam1 = sli1->str = dupstr(loot_xname(obj1));
nam2 = sli2->str;
if (!nam2)
nam2 = sli2->str = dupstr(loot_xname(obj2));
if ((namcmp = strcmpi(nam1, nam2)) != 0)
return namcmp;
@@ -227,7 +418,7 @@ tiebreak:
* 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
* sort. But it had at 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
@@ -267,26 +458,38 @@ boolean FDECL((*filterfunc), (OBJ_P));
/* note: if there is a filter function, this might overallocate */
sliarray = (Loot *) alloc((n + 1) * sizeof *sliarray);
/* the 'keep cockatrice corpses' flag is overloaded with sort mode */
augment_filter = (mode & SORTLOOT_PETRIFY) ? TRUE : FALSE;
mode &= ~SORTLOOT_PETRIFY; /* remove flag, leaving mode */
/* populate aliarray[0..n-1] */
for (i = 0, o = *olist; o; ++i, o = by_nexthere ? o->nexthere : o->nobj) {
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
|| !touch_petrifies(&mons[o->corpsenm])))
continue;
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;
/* 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;
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
a sortloot_item array reflecting the current ordering */
if (mode) {
if (mode && n > 1) {
sortlootmode = mode; /* extra input for sortloot_cmp() */
qsort((genericptr_t) sliarray, n, sizeof *sliarray, sortloot_cmp);
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;
}

View File

@@ -653,16 +653,26 @@ register struct monst *mtmp;
nhUse(mac); /* suppress 'dead increment' from static analyzer */
if (ptr != &mons[PM_GUARD] && ptr != &mons[PM_WATCHMAN]
&& ptr != &mons[PM_WATCH_CAPTAIN]) {
if (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))
(void) mongets(mtmp, K_RATION);
if (!rn2(2))
(void) mongets(mtmp, C_RATION);
if (ptr != &mons[PM_SOLDIER] && !rn2(3))
(void) mongets(mtmp, BUGLE);
} else if (ptr == &mons[PM_WATCHMAN] && rn2(3))
(void) mongets(mtmp, TIN_WHISTLE);
}
} else if (ptr == &mons[PM_SHOPKEEPER]) {
(void) mongets(mtmp, SKELETON_KEY);
switch (rn2(4)) {

View File

@@ -665,7 +665,10 @@ register struct monst *grd;
grd->mpeaceful = 0;
letknow:
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
You(um_dist(grd->mx, grd->my, 2)
? "see %s approaching."