Merge remote-tracking branch 'origin/NetHack-3.6.0'

This commit is contained in:
keni
2016-07-04 20:17:43 -04:00
25 changed files with 358 additions and 134 deletions
+9
View File
@@ -3445,11 +3445,20 @@ boolean initial;
Cmd.move_SW = Cmd.dirchars[7];
}
/* non-movement commands which accept 'm' prefix to request menu operation */
STATIC_OVL boolean
accept_menu_prefix(cmd_func)
int NDECL((*cmd_func));
{
if (cmd_func == dopickup || cmd_func == dotip
/* eat, #offer, and apply tinning-kit all use floorfood() to pick
an item on floor or in invent; 'm' skips picking from floor
(ie, inventory only) rather than request use of menu operation */
|| cmd_func == doeat || cmd_func == dosacrifice || cmd_func == doapply
/* 'm' for removing saddle from adjacent monster without checking
for containers at <u.ux,u.uy> */
|| cmd_func == doloot
/* 'm' prefix allowed for some extended commands */
|| cmd_func == doextcmd || cmd_func == doextlist)
return TRUE;
return FALSE;
+2 -2
View File
@@ -814,8 +814,8 @@ register struct obj *obj;
case CARROT:
return herbi ? DOGFOOD : starving ? ACCFOOD : MANFOOD;
case BANANA:
return (mptr->mlet == S_YETI)
? DOGFOOD
return (mptr->mlet == S_YETI && herbi)
? DOGFOOD /* for monkey and ape (tameable), sasquatch */
: (herbi || starving)
? ACCFOOD
: MANFOOD;
+44 -11
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 eat.c $NHDT-Date: 1466289475 2016/06/18 22:37:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.169 $ */
/* NetHack 3.6 eat.c $NHDT-Date: 1467028559 2016/06/27 11:55:59 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.171 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -13,6 +13,7 @@ STATIC_PTR int NDECL(unfaint);
STATIC_DCL const char *FDECL(food_xname, (struct obj *, BOOLEAN_P));
STATIC_DCL void FDECL(choke, (struct obj *));
STATIC_DCL void NDECL(recalc_wt);
STATIC_DCL unsigned FDECL(obj_nutrition, (struct obj *));
STATIC_DCL struct obj *FDECL(touchfood, (struct obj *));
STATIC_DCL void NDECL(do_reset_eat);
STATIC_DCL void FDECL(done_eating, (BOOLEAN_P));
@@ -296,6 +297,32 @@ reset_eat()
return;
}
/* base nutrition of a food-class object */
STATIC_OVL unsigned
obj_nutrition(otmp)
struct obj *otmp;
{
unsigned nut = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit
: otmp->globby ? otmp->owt
: (unsigned) objects[otmp->otyp].oc_nutrition;
if (otmp->otyp == LEMBAS_WAFER) {
if (maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF)))
nut += nut / 4; /* 800 -> 1000 */
else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC)))
nut -= nut / 4; /* 800 -> 600 */
/* prevent polymorph making a partly eaten wafer
become more nutritious than an untouched one */
if (otmp->oeaten >= nut)
otmp->oeaten = (otmp->oeaten < objects[LEMBAS_WAFER].oc_nutrition)
? (nut - 1) : nut;
} else if (otmp->otyp == CRAM_RATION) {
if (maybe_polyd(is_dwarf(youmonst.data), Race_if(PM_DWARF)))
nut += nut / 6; /* 600 -> 700 */
}
return nut;
}
STATIC_OVL struct obj *
touchfood(otmp)
struct obj *otmp;
@@ -310,9 +337,7 @@ struct obj *otmp;
if (!otmp->oeaten) {
costly_alteration(otmp, COST_BITE);
otmp->oeaten = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit
: otmp->globby ? otmp->owt
: (unsigned) objects[otmp->otyp].oc_nutrition;
otmp->oeaten = obj_nutrition(otmp);
}
if (carried(otmp)) {
@@ -1741,6 +1766,15 @@ struct obj *otmp;
make_vomiting((long) rn1(context.victual.reqtime, 14), FALSE);
}
break;
case LEMBAS_WAFER:
if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC))) {
pline("%s", "!#?&* elf kibble!");
break;
} else if (maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF))) {
pline("A little goes a long way.");
break;
}
goto give_feedback;
case MEATBALL:
case MEAT_STICK:
case HUGE_CHUNK_OF_MEAT:
@@ -2368,6 +2402,7 @@ doeat()
if (u.uedibility) {
int res = edibility_prompts(otmp);
if (res) {
Your(
"%s stops tingling and your sense of smell returns to normal.",
@@ -2577,9 +2612,7 @@ doeat()
}
/* re-calc the nutrition */
basenutrit = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit
: otmp->globby ? otmp->owt
: (unsigned) objects[otmp->otyp].oc_nutrition;
basenutrit = (int) obj_nutrition(otmp);
debugpline3(
"before rounddiv: victual.reqtime == %d, oeaten == %d, basenutrit == %d",
@@ -2974,7 +3007,8 @@ int corpsecheck; /* 0, no check, 1, corpses, 2, tinnable corpses */
offering = !strcmp(verb, "sacrifice"); /* corpsecheck==1 */
/* if we can't touch floor objects then use invent food only */
if (!can_reach_floor(TRUE) || (feeding && u.usteed)
if (iflags.menu_requested /* command was preceded by 'm' prefix */
|| !can_reach_floor(TRUE) || (feeding && u.usteed)
|| (is_pool_or_lava(u.ux, u.uy)
&& (Wwalking || is_clinger(youmonst.data)
|| (Flying && !Breathless))))
@@ -3085,10 +3119,9 @@ struct obj *obj;
{
long uneaten_amt, full_amount;
/* get full_amount first; obj_nutrition() might modify obj->oeaten */
full_amount = (long) obj_nutrition(obj);
uneaten_amt = (long) obj->oeaten;
full_amount = (long) ((obj->otyp == CORPSE) ? mons[obj->corpsenm].cnutrit
: obj->globby ? obj->owt
: (unsigned) objects[obj->otyp].oc_nutrition);
if (uneaten_amt > full_amount) {
impossible(
"partly eaten food (%ld) more nutritious than untouched food (%ld)",
+10 -4
View File
@@ -979,15 +979,21 @@ not_special:
* mpickstuff() as well.
*/
if (xx >= lmx && xx <= oomx && yy >= lmy && yy <= oomy) {
/* don't get stuck circling around an object that's
underneath
an immobile or hidden monster; paralysis victims
excluded */
/* don't get stuck circling around object that's
underneath an immobile or hidden monster;
paralysis victims excluded */
if ((mtoo = m_at(xx, yy)) != 0
&& (mtoo->msleeping || mtoo->mundetected
|| (mtoo->mappearance && !mtoo->iswiz)
|| !mtoo->data->mmove))
continue;
/* the mfndpos() test for whether to allow a move to a
water location accepts flyers, but they can't reach
underwater objects, so being able to move to a spot
is insufficient for deciding whether to do so */
if ((is_pool(xx, yy) && !is_swimmer(ptr))
|| (is_lava(xx, yy) && !likes_lava(ptr)))
continue;
if (((likegold && otmp->oclass == COIN_CLASS)
|| (likeobjs && index(practical, otmp->oclass)
+4 -2
View File
@@ -1923,16 +1923,18 @@ struct permonst _mons2[] = {
/*
* Apelike beasts
*/
/* tameable via banana; does not grow up into ape...
not flagged as domestic, so no guilt penalty for eating non-pet one */
MON("monkey", S_YETI, LVL(2, 12, 6, 0, 0), (G_GENO | 1),
A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK,
NO_ATTK, NO_ATTK, NO_ATTK),
SIZ(100, 50, MS_GROWL, MZ_SMALL), 0, 0,
M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, 0, M3_INFRAVISIBLE, CLR_GRAY),
M1_ANIMAL | M1_HUMANOID | M1_OMNIVORE, 0, M3_INFRAVISIBLE, CLR_GRAY),
MON("ape", S_YETI, LVL(4, 12, 6, 0, 0), (G_GENO | G_SGROUP | 2),
A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3),
ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK),
SIZ(1100, 500, MS_GROWL, MZ_LARGE), 0, 0,
M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, M2_STRONG, M3_INFRAVISIBLE,
M1_ANIMAL | M1_HUMANOID | M1_OMNIVORE, M2_STRONG, M3_INFRAVISIBLE,
CLR_BROWN),
MON("owlbear", S_YETI, LVL(5, 12, 5, 0, 0), (G_GENO | 3),
A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6),
+22 -25
View File
@@ -414,6 +414,24 @@ boolean verbose; /* give message(s) even when you can't see what happened */
return 0;
}
#define MT_FLIGHTCHECK(pre) \
(/* missile hits edge of screen */ \
!isok(bhitpos.x + dx, bhitpos.y + dy) \
/* missile hits the wall */ \
|| IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ) \
/* missile hit closed door */ \
|| closed_door(bhitpos.x + dx, bhitpos.y + dy) \
/* missile might hit iron bars */ \
/* the random chance for small objects hitting bars is */ \
/* skipped when reaching them at point blank range */ \
|| (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS \
&& hits_bars(&singleobj, \
bhitpos.x, bhitpos.y, \
bhitpos.x + dx, bhitpos.y + dy, \
((pre) ? 0 : !rn2(5)), 0)) \
/* Thrown objects "sink" */ \
|| (!(pre) && IS_SINK(levl[bhitpos.x][bhitpos.y].typ)))
void
m_throw(mon, x, y, dx, dy, range, obj)
struct monst *mon; /* launching monster */
@@ -471,17 +489,7 @@ struct obj *obj; /* missile (or stack providing it) */
}
}
/* pre-check for doors, walls and boundaries.
Also need to pre-check for bars regardless of direction;
the random chance for small objects hitting bars is
skipped when reaching them at point blank range */
if (!isok(bhitpos.x + dx, bhitpos.y + dy)
|| IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ)
|| closed_door(bhitpos.x + dx, bhitpos.y + dy)
|| (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS
&& hits_bars(&singleobj,
bhitpos.x, bhitpos.y,
bhitpos.x + dx, bhitpos.y + dy, 0, 0))) {
if (MT_FLIGHTCHECK(TRUE)) {
(void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
return;
}
@@ -611,20 +619,7 @@ struct obj *obj; /* missile (or stack providing it) */
}
}
if (!range /* reached end of path */
/* missile hits edge of screen */
|| !isok(bhitpos.x + dx, bhitpos.y + dy)
/* missile hits the wall */
|| IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ)
/* missile hit closed door */
|| closed_door(bhitpos.x + dx, bhitpos.y + dy)
/* missile might hit iron bars */
|| (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS
&& hits_bars(&singleobj,
bhitpos.x, bhitpos.y,
bhitpos.x + dx, bhitpos.y + dy,
!rn2(5), 0))
/* Thrown objects "sink" */
|| IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) {
|| MT_FLIGHTCHECK(FALSE)) {
if (singleobj) { /* hits_bars might have destroyed it */
if (m_shot.n > 1 && (cansee(bhitpos.x, bhitpos.y)
|| (archer && canseemon(archer))))
@@ -648,6 +643,8 @@ struct obj *obj; /* missile (or stack providing it) */
}
}
#undef MT_FLIGHTCHECK
/* Monster throws item at another monster */
int
thrwmm(mtmp, mtarg)
+30 -1
View File
@@ -1,4 +1,4 @@
/* NetHack 3.6 pager.c $NHDT-Date: 1465114189 2016/06/05 08:09:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.107 $ */
/* NetHack 3.6 pager.c $NHDT-Date: 1466638086 2016/06/22 23:28:06 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.111 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1307,6 +1307,35 @@ doidtrap()
return 0;
}
/*
Implements a rudimentary if/elif/else/endif interpretor and use
conditionals in dat/cmdhelp to describe what command each keystroke
currently invokes, so that there isn't a lot of "(debug mode only)"
and "(if number_pad is off)" cluttering the feedback that the user
sees. (The conditionals add quite a bit of clutter to the raw data
but users don't see that. number_pad produces a lot of conditional
commands: basic letters vs digits, 'g' vs 'G' for '5', phone
keypad vs normal layout of digits, and QWERTZ keyboard swap between
y/Y/^Y/M-y/M-Y/M-^Y and z/Z/^Z/M-z/M-Z/M-^Z.)
The interpretor understands
'&#' for comment,
'&? option' for 'if' (also '&? !option'
or '&? option=value[,value2,...]'
or '&? !option=value[,value2,...]'),
'&: option' for 'elif' (with argument variations same as 'if';
any number of instances for each 'if'),
'&:' for 'else' (also '&: #comment';
0 or 1 instance for a given 'if'), and
'&.' for 'endif' (also '&. #comment'; required for each 'if').
The option handling is a bit of a mess, with no generality for
which options to deal with and only a comma separated list of
integer values for the '=value' part. number_pad is the only
supported option that has a value; the few others (wizard/debug,
rest_on_space, #if SHELL, #if SUSPEND) are booleans.
*/
STATIC_DCL void
whatdoes_help()
{
+59 -22
View File
@@ -1630,7 +1630,7 @@ doloot()
char qbuf[BUFSZ];
int prev_inquiry = 0;
boolean prev_loot = FALSE;
int num_conts;
int num_conts = 0;
abort_looting = FALSE;
@@ -1653,13 +1653,30 @@ doloot()
cc.x = u.ux;
cc.y = u.uy;
lootcont:
if (iflags.menu_requested)
goto lootmon;
lootcont:
if ((num_conts = container_at(cc.x, cc.y, TRUE)) > 0) {
boolean anyfound = FALSE;
if (!able_to_loot(cc.x, cc.y, TRUE))
return 0;
if (Blind && !uarmg) {
/* if blind and without gloves, attempting to #loot at the
location of a cockatrice corpse is fatal before asking
whether to manipulate any containers */
for (nobj = sobj_at(CORPSE, cc.x, cc.y); nobj;
nobj = nxtobj(nobj, CORPSE, TRUE))
if (will_feel_cockatrice(nobj, FALSE)) {
feel_cockatrice(nobj, FALSE);
/* if life-saved (or poly'd into stone golem),
terminate attempt to loot */
return 1;
}
}
if (num_conts > 1) {
/* use a menu to loot many containers */
int n, i;
@@ -1722,9 +1739,11 @@ lootcont:
} else if (IS_GRAVE(levl[cc.x][cc.y].typ)) {
You("need to dig up the grave to effectively loot it...");
}
/*
* 3.3.1 introduced directional looting for some things.
*/
lootmon:
if (c != 'y' && mon_beside(u.ux, u.uy)) {
if (!get_adjacent_loc("Loot in what direction?",
"Invalid loot location", u.ux, u.uy, &cc))
@@ -2883,20 +2902,27 @@ tipcontainer(box)
struct obj *box; /* or bag */
{
xchar ox = u.ux, oy = u.uy; /* #tip only works at hero's location */
boolean empty_it = FALSE,
/* Shop handling: can't rely on the container's own unpaid
or no_charge status because contents might differ with it.
A carried container's contents will be flagged as unpaid
or not, as appropriate, and need no special handling here.
Items owned by the hero get sold to the shop without
confirmation as with other uncontrolled drops. A floor
container's contents will be marked no_charge if owned by
hero, otherwise they're owned by the shop. By passing
the contents through shop billing, they end up getting
treated the same as in the carried case. We do so one
item at a time instead of doing whole container at once
to reduce the chance of exhausting shk's billing capacity. */
maybeshopgoods = !carried(box) && costly_spot(ox, oy);
boolean empty_it = FALSE, maybeshopgoods;
/* box is either held or on floor at hero's spot; no need to check for
nesting; when held, we need to update its location to match hero's;
for floor, the coordinate updating is redundant */
if (get_obj_location(box, &ox, &oy, 0))
box->ox = ox, box->oy = oy;
/* Shop handling: can't rely on the container's own unpaid
or no_charge status because contents might differ with it.
A carried container's contents will be flagged as unpaid
or not, as appropriate, and need no special handling here.
Items owned by the hero get sold to the shop without
confirmation as with other uncontrolled drops. A floor
container's contents will be marked no_charge if owned by
hero, otherwise they're owned by the shop. By passing
the contents through shop billing, they end up getting
treated the same as in the carried case. We do so one
item at a time instead of doing whole container at once
to reduce the chance of exhausting shk's billing capacity. */
maybeshopgoods = !carried(box) && costly_spot(box->ox, box->oy);
/* caveat: this assumes that cknown, lknown, olocked, and otrapped
fields haven't been overloaded to mean something special for the
@@ -2959,7 +2985,7 @@ struct obj *box; /* or bag */
if (empty_it) {
struct obj *otmp, *nobj;
boolean verbose = FALSE, highdrop = !can_reach_floor(TRUE),
boolean terse, highdrop = !can_reach_floor(TRUE),
altarizing = IS_ALTAR(levl[ox][oy].typ),
cursed_mbag = (Is_mbag(box) && box->cursed);
int held = carried(box);
@@ -2967,20 +2993,27 @@ struct obj *box; /* or bag */
if (u.uswallow)
highdrop = altarizing = FALSE;
terse = !(highdrop || altarizing || costly_spot(box->ox, box->oy));
box->cknown = 1;
/* Terse formatting is
* "Objects spill out: obj1, obj2, obj3, ..., objN."
* If any other messages intervene between objects, we revert to
* "ObjK drops to the floor.", "ObjL drops to the floor.", &c.
*/
pline("%s out%c",
box->cobj->nobj ? "Objects spill" : "An object spills",
!(highdrop || altarizing) ? ':' : '.');
terse ? ':' : '.');
for (otmp = box->cobj; otmp; otmp = nobj) {
nobj = otmp->nobj;
obj_extract_self(otmp);
otmp->ox = box->ox, otmp->oy = box->oy;
if (box->otyp == ICE_BOX) {
removed_from_icebox(otmp); /* resume rotting for corpse */
} else if (cursed_mbag && !rn2(13)) {
loss += mbag_item_gone(held, otmp);
/* abbreviated drop format is no longer appropriate */
verbose = TRUE;
terse = FALSE;
continue;
}
@@ -2993,14 +3026,18 @@ struct obj *box; /* or bag */
/* might break or fall down stairs; handles altars itself */
hitfloor(otmp);
} else {
if (altarizing)
if (altarizing) {
doaltarobj(otmp);
else if (verbose)
} else if (!terse) {
pline("%s %s to the %s.", Doname2(otmp),
otense(otmp, "drop"), surface(ox, oy));
else
} else {
pline("%s%c", doname(otmp), nobj ? ',' : '.');
iflags.last_msg = PLNMSG_OBJNAM_ONLY;
}
dropy(otmp);
if (iflags.last_msg != PLNMSG_OBJNAM_ONLY)
terse = FALSE; /* terse formatting has been interrupted */
}
if (maybeshopgoods)
iflags.suppress_price--; /* reset */
+4 -3
View File
@@ -2130,9 +2130,10 @@ boolean unpaid_only;
return price;
}
/* count amount of gold inside container 'obj' and any nested containers */
long
contained_gold(obj)
register struct obj *obj;
struct obj *obj;
{
register struct obj *otmp;
register long value = 0L;
@@ -2867,12 +2868,12 @@ xchar x, y;
boolean isgold = (obj->oclass == COIN_CLASS);
boolean only_partially_your_contents = FALSE;
if (!*u.ushops) /* do cheapest exclusion test first */
return;
if (!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) || !inhishop(shkp))
return;
if (!costly_spot(x, y))
return;
if (!*u.ushops)
return;
if (obj->unpaid && !container && !isgold) {
sub_one_frombill(obj, shkp);
+7 -5
View File
@@ -43,7 +43,7 @@ STATIC_DCL int NDECL(throwspell);
STATIC_DCL void NDECL(cast_protection);
STATIC_DCL void FDECL(spell_backfire, (int));
STATIC_DCL const char *FDECL(spelltypemnemonic, (int));
STATIC_DCL boolean FDECL(spell_hurtle_step, (genericptr_t, int, int));
STATIC_DCL boolean FDECL(spell_aim_step, (genericptr_t, int, int));
/* The roles[] table lists the role-specific values for tuning
* percent_success().
@@ -1181,12 +1181,14 @@ boolean atme;
return 1;
}
/*ARGSUSED*/
STATIC_OVL boolean
spell_hurtle_step(arg, x, y)
genericptr_t arg;
spell_aim_step(arg, x, y)
genericptr_t arg UNUSED;
int x, y;
{
if (!isok(x,y)) return FALSE;
if (!isok(x,y))
return FALSE;
if (!ZAP_POS(levl[x][y].typ)
&& !(IS_DOOR(levl[x][y].typ) && (levl[x][y].doormask & D_ISOPEN)))
return FALSE;
@@ -1233,7 +1235,7 @@ throwspell()
uc.x = u.ux;
uc.y = u.uy;
walk_path(&uc, &cc, spell_hurtle_step, NULL);
walk_path(&uc, &cc, spell_aim_step, (genericptr_t) 0);
u.dx = cc.x;
u.dy = cc.y;