#if !defined(MAC)
# define CLIPPING /* allow smaller screens -- ERS */
#endif
+#define BARGETHROUGH /* allow some monster to move others out of their way */
#ifdef REDO
# define DOAGAIN '\001' /* ^A, the "redo" key used in cmd.c and getline.c */
#ifdef REINCARNATION
E void FDECL(assign_rogue_graphics, (BOOLEAN_P));
#endif
+#ifdef BARGETHROUGH
+E boolean FDECL(cursed_object_at, (int, int));
+#endif
/* ### dungeon.c ### */
E int FDECL(fightm, (struct monst *));
E int FDECL(mattackm, (struct monst *,struct monst *));
+#ifdef BARGETHROUGH
+E int FDECL(mdisplacem, (struct monst *,struct monst *,BOOLEAN_P));
+#endif
E int FDECL(noattacks, (struct permonst *));
E int FDECL(sleep_monst, (struct monst *,int,int));
E void FDECL(slept_monst, (struct monst *));
E boolean FDECL(accessible, (int,int));
E void FDECL(set_apparxy, (struct monst *));
E boolean FDECL(can_ooze, (struct monst *));
+#ifdef BARGETHROUGH
+E boolean FDECL(should_displace, (struct monst *,coord *,long *,int,
+ XCHAR_P,XCHAR_P));
+E boolean FDECL(undesirable_disp, (struct monst *,XCHAR_P,XCHAR_P));
+#endif
/* ### monst.c ### */
#ifndef MFNDPOS_H
#define MFNDPOS_H
+#ifdef BARGETHROUGH
+#define ALLOW_MDISP 0x00001000L /* can displace a monster out of its way */
+#endif
#define ALLOW_TRAPS 0x00020000L /* can enter traps */
#define ALLOW_U 0x00040000L /* can attack you */
#define ALLOW_M 0x00080000L /* can attack other monsters */
#define is_covetous(ptr) ((ptr->mflags3 & M3_COVETOUS))
#define infravision(ptr) ((ptr->mflags3 & M3_INFRAVISION))
#define infravisible(ptr) ((ptr->mflags3 & M3_INFRAVISIBLE))
+#ifdef BARGETHROUGH
+#define is_displacer(ptr) (((ptr)->mflags3 & M3_DISPLACES) != 0L)
+#endif
#define is_mplayer(ptr) (((ptr) >= &mons[PM_ARCHEOLOGIST]) && \
((ptr) <= &mons[PM_WIZARD]))
#define is_rider(ptr) ((ptr) == &mons[PM_DEATH] || \
#define M3_INFRAVISION 0x0100 /* has infravision */
#define M3_INFRAVISIBLE 0x0200 /* visible by infravision */
+/* define the bit even if BARGETHROUGH is not defined for savefile compat. */
+#define M3_DISPLACES 0x0400 /* moves monsters out of its way */
+
#define MZ_TINY 0 /* < 2' */
#define MZ_SMALL 1 /* 2-4' */
#define MZ_MEDIUM 2 /* 4-7' */
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
-#define EDITLEVEL 7
+#define EDITLEVEL 8
#define COPYRIGHT_BANNER_A \
"NetHack, Copyright 1985-2003"
static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 };
+#ifndef BARGETHROUGH
STATIC_OVL boolean FDECL(cursed_object_at, (int, int));
+#endif /* not BARGETHROUGH */
STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */
STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t));
+#ifdef BARGETHROUGH
+boolean
+#else
STATIC_OVL boolean
+#endif
cursed_object_at(x, y)
int x, y;
{
struct obj *obj = (struct obj *) 0;
xchar otyp;
boolean has_edog, cursemsg[9], do_eat = FALSE;
+#ifdef BARGETHROUGH
+ boolean better_with_displacing = FALSE;
+#endif
xchar nix, niy; /* position mtmp is (considering) moving to */
register int nx, ny; /* temporary coordinates */
xchar cnt, uncursedcnt, chcnt;
if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK | ALLOW_WALL);
if (passes_bars(mtmp->data)) allowflags |= ALLOW_BARS;
if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK;
+#ifdef BARGETHROUGH
+ if (is_displacer(mtmp->data)) allowflags |= ALLOW_MDISP;
+#endif
if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
allowflags |= ALLOW_U;
if (!has_edog) {
uncursedcnt = 0;
for (i = 0; i < cnt; i++) {
nx = poss[i].x; ny = poss[i].y;
+#ifdef BARGETHROUGH
+ if (MON_AT(nx,ny) && !((info[i] & ALLOW_M) || info[i] & ALLOW_MDISP))
+ continue;
+#else
if (MON_AT(nx,ny) && !(info[i] & ALLOW_M)) continue;
+#endif
if (cursed_object_at(nx, ny)) continue;
uncursedcnt++;
}
+#ifdef BARGETHROUGH
+ better_with_displacing = should_displace(mtmp,poss,info,cnt,gx,gy);
+#endif
+
chcnt = 0;
chi = -1;
nidist = GDIST(nix,niy);
mstatus = mattackm(mtmp2, mtmp); /* return attack */
if (mstatus & MM_DEF_DIED) return 2;
}
-
return 0;
}
+#ifdef BARGETHROUGH
+ if ((info[i] & ALLOW_MDISP) && MON_AT(nx, ny) &&
+ better_with_displacing &&
+ !undesirable_disp(mtmp,nx,ny)) {
+ int mstatus;
+ register struct monst *mtmp2 = m_at(nx,ny);
+ mstatus = mdisplacem(mtmp, mtmp2, FALSE); /* displace monster */
+ if (mstatus && MM_DEF_DIED) return 2;
+ return 0;
+ }
+#endif /* BARGETHROUGH */
{ /* Dog avoids harmful traps, but perhaps it has to pass one
* in order to follow player. (Non-harmful traps do not
return 0;
}
+#ifdef BARGETHROUGH
/*
- * mattackm() -- a monster attacks another monster.
+ * mattackm() and mdisplacem() below both return a result bitfield:
*
- * This function returns a result bitfield:
+ * --------- aggressor died
+ * / ------- defender died
+ * / / ----- defender was hit
+ * / / /
+ * x x x
+ *
+ * 0x4 MM_AGR_DIED
+ * 0x2 MM_DEF_DIED
+ * 0x1 MM_HIT
+ * 0x0 MM_MISS
+ *
+ */
+
+/*
+ * mdisplacem() -- a monster moves another monster out of the way.
+ */
+int
+mdisplacem(magr, mdef, quietly)
+register struct monst *magr,*mdef;
+boolean quietly;
+{
+ struct permonst *pa, *pd;
+ struct monst *mon; /* displaced monster */
+ int tx = mdef->mx, ty = mdef->my; /* destination */
+ int fx = magr->mx, fy = magr->my; /* current location */
+ boolean struck = FALSE;
+
+ pa = magr->data; pd = mdef->data;
+
+ /* The 1 in 7 failure below matches the chance in attack()
+ * for pet displacement.
+ */
+ if (!rn2(7)) return(MM_MISS);
+
+ /* Grid bugs cannot displace at an angle. */
+ if (pa == &mons[PM_GRID_BUG] && magr->mx != mdef->mx
+ && magr->my != mdef->my)
+ return(MM_MISS);
+
+
+ /* undetected monsters become un-hidden if they are displaced */
+ if (mdef->mundetected)
+ mdef->mundetected = 0;
+
+ /*
+ * Set up the visibility of action.
+ * You can observe monster displacement if you can see both of
+ * the monsters involved.
+ */
+ vis = (canspotmon(magr) && canspotmon(mdef));
+
+
+ if (touch_petrifies(pd) && !resists_ston(magr)) {
+ if (which_armor(magr, W_ARMG) != 0) {
+ if (poly_when_stoned(pa)) {
+ mon_to_stone(magr);
+ return MM_HIT; /* no damage during the polymorph */
+ }
+ if (!quietly && canspotmon(magr))
+ pline("%s turns to stone!", Monnam(magr));
+ monstone(magr);
+ if (magr->mhp > 0) return 0;
+ else if (magr->mtame && !vis)
+ You(brief_feeling, "peculiarly sad");
+ return MM_AGR_DIED;
+ }
+ }
+
+ if (m_at(fx, fy) == magr)
+ remove_monster(fx, fy); /* pick up from orig position */
+ if ((mon = m_at(tx, ty)) == mdef) {
+ if (!quietly && (vis))
+ pline("%s moves %s out of %s way!",
+ Monnam(magr), mon_nam(mdef),
+ is_rider(pa) ? "the" : mhis(magr));
+ remove_monster(tx, ty);
+ }
+ place_monster(magr,tx,ty); /* put magr down */
+
+ /* Restore original mon */
+ if (mon) {
+ if ((mon->mx != tx) || (mon->my != ty))
+ place_worm_seg(mon, fx, fy);
+ else
+ place_monster(mon, fx, fy);
+ struck = TRUE;
+ } else
+ remove_monster(fx, fy); /* shouldn't happen */
+ newsym(fx,fy); /* see it */
+ newsym(tx,ty); /* all happen */
+ flush_screen(0); /* make sure it shows up */
+
+ /*
+ * Wake up the displaced defender.
+ */
+ mdef->msleeping = 0;
+
+ return(struck ? MM_HIT : MM_MISS);
+}
+#endif /* BARGETHROUGH */
+
+/*
+ * mattackm() -- a monster attacks another monster.
+#ifndef BARGETHROUGH
*
* --------- aggressor died
* / ------- defender died
* 0x1 MM_HIT
* 0x0 MM_MISS
*
+#endif
* Each successive attack has a lower probability of hitting. Some rely on the
* success of previous attacks. ** this doen't seem to be implemented -dl **
*
STATIC_DCL boolean FDECL(restrap,(struct monst *));
STATIC_DCL long FDECL(mm_aggression, (struct monst *,struct monst *));
+#ifdef BARGETHROUGH
+STATIC_DCL long FDECL(mm_displacement, (struct monst *,struct monst *));
+#endif
STATIC_DCL int NDECL(pick_animal);
STATIC_DCL int FDECL(select_newcham_form, (struct monst *));
STATIC_DCL void FDECL(kill_eggs, (struct obj *));
struct monst *mtmp2 = m_at(nx, ny);
long mmflag = flag | mm_aggression(mon, mtmp2);
+#ifndef BARGETHROUGH
if (!(mmflag & ALLOW_M)) continue;
info[cnt] |= ALLOW_M;
if (mtmp2->mtame) {
if (!(mmflag & ALLOW_TM)) continue;
info[cnt] |= ALLOW_TM;
+#else /* BARGETHROUGH */
+ if (mmflag & ALLOW_M) {
+ info[cnt] |= ALLOW_M;
+ if (mtmp2->mtame) {
+ if (!(mmflag & ALLOW_TM)) continue;
+ info[cnt] |= ALLOW_TM;
+ }
+ } else {
+ mmflag = flag | mm_displacement(mon, mtmp2);
+ if (!(mmflag & ALLOW_MDISP)) continue;
+ info[cnt] |= ALLOW_MDISP;
+#endif /* BARGETHROUGH */
}
}
/* Note: ALLOW_SANCT only prevents movement, not */
return 0L;
}
+#ifdef BARGETHROUGH
+/* Monster displacing another monster out of the way */
+STATIC_OVL long
+mm_displacement(magr, mdef)
+struct monst *magr, /* monster that is currently deciding where to move */
+ *mdef; /* another monster which is next to it */
+{
+ struct permonst *pa = magr->data;
+ struct permonst *pd = mdef->data;
+ if ((pa->mflags3 & M3_DISPLACES) &&
+ !is_longworm(pd) && /* no displacing longworms */
+ !mdef->mtrapped && /* complex to do right */
+ (is_rider(pa) || /* riders can move anything */
+ pa->msize >= pd->msize)) /* same or smaller only */
+ return ALLOW_MDISP;
+ return 0L;
+}
+#endif /* BARGETHROUGH */
+
boolean
monnear(mon, x, y)
register struct monst *mon;
return(FALSE);
}
+#ifdef BARGETHROUGH
+/*
+ * should_displace()
+ *
+ * Displacement of another monster is a last resort and only
+ * used on approach. If there are better ways to get to target,
+ * those should be used instead. This function does that evaluation.
+ */
+boolean
+should_displace(mtmp, poss, info, cnt, gx, gy)
+struct monst *mtmp;
+coord *poss; /* coord poss[9] */
+long *info; /* long info[9] */
+int cnt;
+xchar gx, gy;
+{
+ int shortest_with_displacing = -1;
+ int shortest_without_displacing = -1;
+ int count_without_displacing = 0;
+ register int i, nx, ny;
+ int ndist;
+
+ for (i = 0; i < cnt; i++) {
+ nx = poss[i].x; ny = poss[i].y;
+ ndist = dist2(nx,ny,gx,gy);
+ if (MON_AT(nx,ny) &&
+ (info[i] & ALLOW_MDISP) && !(info[i] & ALLOW_M) &&
+ !undesirable_disp(mtmp,nx,ny)) {
+ if (shortest_with_displacing == -1 ||
+ (ndist < shortest_with_displacing))
+ shortest_with_displacing = ndist;
+ } else {
+ if ((shortest_without_displacing == -1) ||
+ (ndist < shortest_without_displacing))
+ shortest_without_displacing = ndist;
+ count_without_displacing++;
+ }
+ }
+ if (shortest_with_displacing > -1 &&
+ (shortest_with_displacing < shortest_without_displacing ||
+ !count_without_displacing))
+ return TRUE;
+ return FALSE;
+}
+#endif /* BARGETHROUGH */
+
/* Return values:
* 0: did not move, but can still attack and do other stuff.
* 1: moved, possibly can attack.
boolean can_open=0, can_unlock=0, doorbuster=0;
boolean uses_items=0, setlikes=0;
boolean avoid=FALSE;
+#ifdef BARGETHROUGH
+ boolean better_with_displacing = FALSE;
+#endif
struct permonst *ptr;
struct monst *mtoo;
schar mmoved = 0; /* not strictly nec.: chi >= 0 will do */
for(i = 0; i < cnt; i++)
if(!(info[i] & NOTONL)) avoid=TRUE;
}
-
+#ifdef BARGETHROUGH
+ better_with_displacing = should_displace(mtmp,poss,info,cnt,gx,gy);
+#endif
for(i=0; i < cnt; i++) {
if (avoid && (info[i] & NOTONL)) continue;
nx = poss[i].x;
ny = poss[i].y;
+#ifdef BARGETHROUGH
+ if (MON_AT(nx,ny) &&
+ (info[i] & ALLOW_MDISP) && !(info[i] & ALLOW_M) &&
+ !better_with_displacing) continue;
+#endif
if (appr != 0) {
mtrk = &mtmp->mtrack[0];
for(j=0; j < jcnt; mtrk++, j++)
return 3;
}
+#ifdef BARGETHROUGH
+ if((info[chi] & ALLOW_MDISP)) {
+ struct monst *mtmp2;
+ int mstatus;
+ mtmp2 = m_at(nix,niy);
+ mstatus = mdisplacem(mtmp, mtmp2, FALSE);
+ if ((mstatus & MM_AGR_DIED) || (mstatus & MM_DEF_DIED))
+ return 2;
+ if (mstatus & MM_HIT) return 1;
+ return 3;
+ }
+#endif /* BARGETHROUGH */
+
if (!m_in_out_region(mtmp,nix,niy))
return 3;
remove_monster(omx, omy);
mtmp->muy = my;
}
+#ifdef BARGETHROUGH
+/*
+ * mon-to-mon displacement is a deliberate "get out of my way" act,
+ * not an accidental bump, so we don't consider mstun or mconf in
+ * undesired_disp().
+ *
+ * We do consider many other things about the target and its
+ * location however.
+ */
+boolean
+undesirable_disp(mtmp, x, y)
+struct monst *mtmp;
+xchar x,y;
+{
+ struct permonst *mdat = mtmp->data;
+ boolean is_pet = (mtmp && mtmp->mtame && !mtmp->isminion);
+ struct trap *trap = t_at(x,y);
+
+ if (is_pet) {
+ /* Pets avoid a trap if you've seen it usually. */
+ if (trap && trap->tseen && rn2(40))
+ return TRUE;
+ /* Pets avoid cursed locations */
+ if (cursed_object_at(x,y))
+ return TRUE;
+ }
+ /* Monsters avoid a trap if they've seen that type before */
+ else if (trap && rn2(40) &&
+ (mtmp->mtrapseen & (1 << (trap->ttyp -1))) != 0)
+ return TRUE;
+
+ return FALSE;
+}
+#endif /* BARGETHROUGH */
+
boolean
can_ooze(mtmp)
struct monst *mtmp;
MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_STONE, 0,
M1_FLY|M1_HUMANOID|M1_REGEN|M1_SEE_INVIS|M1_TPORT_CNTRL,
M2_NOPOLY|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG|M2_NASTY,
- M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD),
+ M3_INFRAVISIBLE|M3_INFRAVISION|M3_DISPLACES, HI_LORD),
MON("Pestilence", S_DEMON,
LVL(30, 12, -5, 100, 0), (G_UNIQ|G_NOGEN),
A(ATTK(AT_TUCH, AD_PEST, 8, 8), ATTK(AT_TUCH, AD_PEST, 8, 8),
MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_STONE, 0,
M1_FLY|M1_HUMANOID|M1_REGEN|M1_SEE_INVIS|M1_TPORT_CNTRL,
M2_NOPOLY|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG|M2_NASTY,
- M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD),
+ M3_INFRAVISIBLE|M3_INFRAVISION|M3_DISPLACES, HI_LORD),
MON("Famine", S_DEMON,
LVL(30, 12, -5, 100, 0), (G_UNIQ|G_NOGEN),
A(ATTK(AT_TUCH, AD_FAMN, 8, 8), ATTK(AT_TUCH, AD_FAMN, 8, 8),
MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_STONE, 0,
M1_FLY|M1_HUMANOID|M1_REGEN|M1_SEE_INVIS|M1_TPORT_CNTRL,
M2_NOPOLY|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG|M2_NASTY,
- M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD),
+ M3_INFRAVISIBLE|M3_INFRAVISION|M3_DISPLACES, HI_LORD),
/* other demons
*/
#ifdef MAIL
if ((u.uhave.amulet || On_W_tower_level(&u.uz)) && !rn2(3)) {
You_feel("disoriented for a moment.");
+#ifdef WIZARD
+ if (wizard && yn("Override?") != 'y')
+#endif
return;
}
if ((Teleport_control && !Stunned)
#ifdef MAIL
"mail daemon",
#endif
+#ifdef BARGETHROUGH
+ "monsters moving monsters",
+#endif
#ifdef GNUDOS
"MSDOS protected mode",
#endif