more monster key use

Let monsters use lock picks and credit cards in addition to keys for
opening doors.  And the earlier code to have pets hang on to a key didn't
work as intended.  It worked fine if the key was the only object carried,
but the monsters' item dropping code didn't give any special handling to
keys so they'd be dropped too if the pet carried another droppable item.
This eliminates second set of checks for handling some items specially--
dropping now uses the same routine as is used when pet movement decides
whether there's anything to drop.

     Also, a couple more door message tweaks.  "You see a door open" seems
strange when you watch your pet do the opening.  Previously fixed for the
"unlock and open" case, this does the same for opening already unlocked
doors and for giants smashing down doors--it now gives a more specific
message when you see a monster perform the action.

     Possible change in play balance:  pets capable of picking up the
rogue's Master Key of Thievery or tourist's Platinum Yendorian Express
Card will keep one of them.  So a player might accidentally lose one by
leaving it on the floor in a pet's path, or more significantly, the Card
will yield a means of giving magic resistance to a monster who can't wear
a cloak or dragon scales.  It's neutral and the most interesting high-end
pets are lawful (hence won't pick it up), so that probably won't have much
impact.
This commit is contained in:
nethack.rankin
2005-10-20 03:58:46 +00:00
parent 213e3e4d00
commit 74f08dcb22
4 changed files with 142 additions and 87 deletions

View File

@@ -441,6 +441,7 @@ E void FDECL(wary_dog, (struct monst *, BOOLEAN_P));
/* ### dogmove.c ### */
E struct obj *FDECL(droppables, (struct monst *));
E int FDECL(dog_nutrition, (struct monst *,struct obj *));
E int FDECL(dog_eat, (struct monst *,struct obj *,int,int,BOOLEAN_P));
E int FDECL(dog_move, (struct monst *,int));
@@ -1245,6 +1246,7 @@ E boolean FDECL(olfaction, (struct permonst *));
E boolean FDECL(itsstuck, (struct monst *));
E boolean FDECL(mb_trapped, (struct monst *));
E boolean FDECL(monhaskey, (struct monst *,BOOLEAN_P));
E void FDECL(mon_regen, (struct monst *,BOOLEAN_P));
E int FDECL(dochugw, (struct monst *));
E boolean FDECL(onscary, (int,int,struct monst *));

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)dogmove.c 3.5 2005/10/10 */
/* SCCS Id: @(#)dogmove.c 3.5 2005/10/14 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -13,43 +13,109 @@ STATIC_DCL boolean FDECL(dog_hunger,(struct monst *,struct edog *));
STATIC_DCL int FDECL(dog_invent,(struct monst *,struct edog *,int));
STATIC_DCL int FDECL(dog_goal,(struct monst *,struct edog *,int,int,int));
STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *));
STATIC_DCL boolean FDECL(can_reach_location,(struct monst *,XCHAR_P,XCHAR_P,
XCHAR_P,XCHAR_P));
STATIC_DCL boolean FDECL(could_reach_item,(struct monst *, XCHAR_P,XCHAR_P));
STATIC_DCL void FDECL(quickmimic, (struct monst *));
STATIC_OVL struct obj *
DROPPABLES(mon)
register struct monst *mon;
/* pick a carried item for pet to drop */
struct obj *
droppables(mon)
struct monst *mon;
{
register struct obj *obj;
struct obj *wep = MON_WEP(mon);
boolean item1 = FALSE, item2 = FALSE, item3 = FALSE;
struct obj *obj, *wep,
dummy, *pickaxe, *unihorn, *key;
if (is_animal(mon->data) || mindless(mon->data))
item1 = item2 = item3 = TRUE;
if (!tunnels(mon->data) || !needspick(mon->data))
item1 = TRUE;
if (nohands(mon->data) || verysmall(mon->data))
item3 = TRUE;
for(obj = mon->minvent; obj; obj = obj->nobj) {
if (!item1 && is_pick(obj) && (obj->otyp != DWARVISH_MATTOCK
|| !which_armor(mon, W_ARMS))) {
item1 = TRUE;
continue;
}
if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) {
item2 = TRUE;
continue;
}
if (!item3 && obj->otyp == SKELETON_KEY) {
item3 = TRUE;
continue;
}
if (!obj->owornmask && obj != wep) return obj;
#ifndef TOURIST
#define CREDIT_CARD STRANGE_OBJECT /* avoids messy conditionalization */
#endif
#ifndef GOLDOBJ
if (mon->mgold) return &zeroobj; /* pet has something to drop */
#endif
dummy = zeroobj;
dummy.otyp = GOLD_PIECE; /* not STRANGE_OBJECT or tools of interest */
dummy.oartifact = 1; /* so real artifact won't override "don't keep it" */
pickaxe = unihorn = key = (struct obj *)0;
wep = MON_WEP(mon);
if (is_animal(mon->data) || mindless(mon->data)) {
/* won't hang on to any objects of these types */
pickaxe = unihorn = key = &dummy; /* act as if already have them */
} else {
/* don't hang on to pick-axe if can't use one or don't need one */
if (!tunnels(mon->data) || !needspick(mon->data)) pickaxe = &dummy;
/* don't hang on to key if can't open doors */
if (nohands(mon->data) || verysmall(mon->data)) key = &dummy;
}
if (wep) {
if (is_pick(wep)) pickaxe = wep;
if (wep->otyp == UNICORN_HORN) unihorn = wep;
/* don't need any wielded check for keys... */
}
for (obj = mon->minvent; obj; obj = obj->nobj) {
switch (obj->otyp) {
case DWARVISH_MATTOCK:
/* reject mattock if couldn't wield it */
if (which_armor(mon, W_ARMS)) break;
/* keep mattock in preference to pick unless pick is already
wielded or is an artifact and mattock isn't */
if (pickaxe && pickaxe->otyp == PICK_AXE &&
pickaxe != wep && (!pickaxe->oartifact || obj->oartifact))
return pickaxe; /* drop the one we earlier decided to keep */
/*FALLTHRU*/
case PICK_AXE:
if (!pickaxe || (obj->oartifact && !pickaxe->oartifact)) {
if (pickaxe) return pickaxe;
pickaxe = obj; /* keep this digging tool */
continue;
}
break;
case UNICORN_HORN:
/* reject cursed unicorn horns */
if (obj->cursed) break;
/* keep artifact unihorn in preference to ordinary one */
if (!unihorn || (obj->oartifact && !unihorn->oartifact)) {
if (unihorn) return unihorn;
unihorn = obj; /* keep this unicorn horn */
continue;
}
break;
case SKELETON_KEY:
/* keep key in preference to lock-pick */
if (key && key->otyp == LOCK_PICK &&
(!key->oartifact || obj->oartifact))
return key; /* drop the one we earlier decided to keep */
/*FALLTHRU*/
case LOCK_PICK:
/* keep lock-pick in preference to credit card */
if (key && key->otyp == CREDIT_CARD &&
(!key->oartifact || obj->oartifact))
return key;
/*FALLTHRU*/
case CREDIT_CARD:
if (!key || (obj->oartifact && !key->oartifact)) {
if (key) return key;
key = obj; /* keep this unlocking tool */
continue;
}
break;
default:
break;
}
return (struct obj *)0;
if (!obj->owornmask && obj != wep) return obj;
}
#ifndef TOURIST
#undef CREDIT_CARD
#endif
return (struct obj *)0; /* don't drop anything */
}
static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 };
@@ -288,11 +354,7 @@ int udist;
/* if we are carrying sth then we drop it (perhaps near @) */
/* Note: if apport == 1 then our behaviour is independent of udist */
/* Use udist+1 so steed won't cause divide by zero */
#ifndef GOLDOBJ
if(DROPPABLES(mtmp) || mtmp->mgold) {
#else
if(DROPPABLES(mtmp)) {
#endif
if (droppables(mtmp)) {
if (!rn2(udist+1) || !rn2(edog->apport))
if(rn2(10) < edog->apport){
relobj(mtmp, (int)mtmp->minvis, TRUE);
@@ -363,7 +425,7 @@ int after, udist, whappr;
omy = mtmp->my;
in_masters_sight = couldsee(omx, omy);
dog_has_minvent = (DROPPABLES(mtmp) != 0);
dog_has_minvent = (droppables(mtmp) != 0);
if (!edog || mtmp->mleashed) { /* he's not going anywhere... */
gtyp = APPORT;
@@ -595,7 +657,7 @@ register int after; /* this is extra fast monster movement */
}
if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
allowflags |= OPENDOOR;
if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= UNLOCKDOOR;
if (monhaskey(mtmp, TRUE)) allowflags |= UNLOCKDOOR;
/* note: the Wizard and Riders can unlock doors without a key;
they won't use that ability if someone manages to tame them */
}

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)monmove.c 3.5 2005/10/05 */
/* SCCS Id: @(#)monmove.c 3.5 2005/10/14 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -39,6 +39,18 @@ register struct monst *mtmp;
return(FALSE);
}
/* check whether a monster is carrying a locking/unlocking tool */
boolean
monhaskey(mon, for_unlocking)
struct monst *mon;
boolean for_unlocking; /* true => credit card ok, false => not ok */
{
#ifdef TOURIST
if (for_unlocking && m_carrying(mon, CREDIT_CARD)) return TRUE;
#endif
return m_carrying(mon, SKELETON_KEY) || m_carrying(mon, LOCK_PICK);
}
STATIC_OVL void
watch_on_duty(mtmp)
register struct monst *mtmp;
@@ -676,7 +688,7 @@ register int after;
#endif
can_tunnel = tunnels(ptr);
can_open = !(nohands(ptr) || verysmall(ptr));
can_unlock = ((can_open && m_carrying(mtmp, SKELETON_KEY)) ||
can_unlock = ((can_open && monhaskey(mtmp, TRUE)) ||
mtmp->iswiz || is_rider(ptr));
doorbuster = is_giant(ptr);
if(mtmp->wormno) goto not_special;
@@ -1120,7 +1132,8 @@ postmov:
&& !can_tunnel /* taken care of below */
) {
struct rm *here = &levl[mtmp->mx][mtmp->my];
boolean btrapped = (here->doormask & D_TRAPPED);
boolean btrapped = (here->doormask & D_TRAPPED),
observeit = canseeit && canspotmon(mtmp);
if(here->doormask & (D_LOCKED|D_CLOSED) &&
(amorphous(ptr) || (!amorphous(ptr) && can_fog(mtmp) &&
@@ -1138,7 +1151,7 @@ postmov:
if(mb_trapped(mtmp)) return(2);
} else {
if (flags.verbose) {
if (canseemon(mtmp))
if (observeit)
pline("%s unlocks and opens a door.",
Monnam(mtmp));
else if (canseeit)
@@ -1158,7 +1171,9 @@ postmov:
if(mb_trapped(mtmp)) return(2);
} else {
if (flags.verbose) {
if (canseeit)
if (observeit)
pline("%s opens a door.", Monnam(mtmp));
else if (canseeit)
You_see("a door open.");
else if (!Deaf)
You_hear("a door open.");
@@ -1176,7 +1191,10 @@ postmov:
if(mb_trapped(mtmp)) return(2);
} else {
if (flags.verbose) {
if (canseeit)
if (observeit)
pline("%s smashes down a door.",
Monnam(mtmp));
else if (canseeit)
You_see("a door crash open.");
else if (!Deaf)
You_hear("a door crash open.");

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)steal.c 3.5 2005/07/14 */
/* SCCS Id: @(#)steal.c 3.5 2005/10/14 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -573,59 +573,32 @@ struct monst *mon;
/* release the objects the creature is carrying */
void
relobj(mtmp,show,is_pet)
register struct monst *mtmp;
register int show;
relobj(mtmp, show, is_pet)
struct monst *mtmp;
int show;
boolean is_pet; /* If true, pet should keep wielded/worn items */
{
register struct obj *otmp;
register int omx = mtmp->mx, omy = mtmp->my;
struct obj *keepobj = 0;
struct obj *wep = MON_WEP(mtmp);
boolean item1 = FALSE, item2 = FALSE;
struct obj *otmp;
int omx = mtmp->mx, omy = mtmp->my;
if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data))
item1 = item2 = TRUE;
if (!tunnels(mtmp->data) || !needspick(mtmp->data))
item1 = TRUE;
while ((otmp = mtmp->minvent) != 0) {
obj_extract_self(otmp);
/* special case: pick-axe and unicorn horn are non-worn */
/* items that we also want pets to keep 1 of */
/* (It is a coincidence that these can also be wielded.) */
if (otmp->owornmask || otmp == wep ||
((!item1 && otmp->otyp == PICK_AXE) ||
(!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) {
if (is_pet) { /* dont drop worn/wielded item */
if (otmp->otyp == PICK_AXE)
item1 = TRUE;
if (otmp->otyp == UNICORN_HORN && !otmp->cursed)
item2 = TRUE;
otmp->nobj = keepobj;
keepobj = otmp;
continue;
}
}
mdrop_obj(mtmp, otmp, is_pet && flags.verbose);
}
/* put kept objects back */
while ((otmp = keepobj) != (struct obj *)0) {
keepobj = otmp->nobj;
(void) add_to_minv(mtmp, otmp);
}
#ifndef GOLDOBJ
/* handle gold first since droppables() would get stuck on it */
if (mtmp->mgold) {
register long g = mtmp->mgold;
long g = mtmp->mgold;
(void) mkgold(g, omx, omy);
if (is_pet && cansee(omx, omy) && flags.verbose)
pline("%s drops %ld gold piece%s.", Monnam(mtmp),
g, plur(g));
pline("%s drops %ld gold piece%s.", Monnam(mtmp),
g, plur(g));
mtmp->mgold = 0L;
}
#endif
while ((otmp = (is_pet ? droppables(mtmp) : mtmp->minvent)) != 0) {
obj_extract_self(otmp);
mdrop_obj(mtmp, otmp, is_pet && flags.verbose);
}
if (show & cansee(omx, omy))
newsym(omx, omy);
}