Merge branch 'NetHack-3.6.2-beta01' into NetHack-3.6.2
This commit is contained in:
341
src/invent.c
341
src/invent.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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."
|
||||
|
||||
Reference in New Issue
Block a user