diff --git a/include/config.h b/include/config.h index e1f418064..ab0a15455 100644 --- a/include/config.h +++ b/include/config.h @@ -335,6 +335,7 @@ typedef unsigned char uchar; #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 */ diff --git a/include/extern.h b/include/extern.h index 6df504e5d..720a83d74 100644 --- a/include/extern.h +++ b/include/extern.h @@ -463,6 +463,9 @@ E void FDECL(switch_graphics, (int)); #ifdef REINCARNATION E void FDECL(assign_rogue_graphics, (BOOLEAN_P)); #endif +#ifdef BARGETHROUGH +E boolean FDECL(cursed_object_at, (int, int)); +#endif /* ### dungeon.c ### */ @@ -966,6 +969,9 @@ E int FDECL(buzzmu, (struct monst *,struct attack *)); 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 *)); @@ -1216,6 +1222,11 @@ E boolean FDECL(closed_door, (int,int)); 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 ### */ diff --git a/include/mfndpos.h b/include/mfndpos.h index 01a4bb580..a5643b1f1 100644 --- a/include/mfndpos.h +++ b/include/mfndpos.h @@ -5,6 +5,9 @@ #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 */ diff --git a/include/mondata.h b/include/mondata.h index 7d86d5938..8c37db245 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -129,6 +129,9 @@ #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] || \ diff --git a/include/monflag.h b/include/monflag.h index 8ea8c15a1..5af876dde 100644 --- a/include/monflag.h +++ b/include/monflag.h @@ -160,6 +160,9 @@ #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' */ diff --git a/include/patchlevel.h b/include/patchlevel.h index 4bb7b6388..f1df6c6ac 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -13,7 +13,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" diff --git a/src/dogmove.c b/src/dogmove.c index 987d31f96..fe0b90db2 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -48,13 +48,19 @@ register struct monst *mon; 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; { @@ -489,6 +495,9 @@ register int after; /* this is extra fast monster movement */ 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; @@ -546,6 +555,9 @@ register int after; /* this is extra fast monster movement */ 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) { @@ -590,11 +602,20 @@ register int after; /* this is extra fast monster movement */ 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); @@ -645,9 +666,19 @@ register int after; /* this is extra fast monster movement */ 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 diff --git a/src/mhitm.c b/src/mhitm.c index f5ba47149..1a74dbf83 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -168,10 +168,9 @@ fightm(mtmp) /* have monsters fight each other */ return 0; } +#ifdef BARGETHROUGH /* - * mattackm() -- a monster attacks another monster. - * - * This function returns a result bitfield: + * mattackm() and mdisplacem() below both return a result bitfield: * * --------- aggressor died * / ------- defender died @@ -184,6 +183,112 @@ fightm(mtmp) /* have monsters fight each other */ * 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 + * / / ----- defender was hit + * / / / + * x x x + * + * 0x4 MM_AGR_DIED + * 0x2 MM_DEF_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 ** * diff --git a/src/mon.c b/src/mon.c index ffa2c66d8..18a0d4003 100644 --- a/src/mon.c +++ b/src/mon.c @@ -14,6 +14,9 @@ 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 *)); @@ -1092,11 +1095,24 @@ nexttry: /* eels prefer the water, but if there is no water nearby, 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 */ @@ -1199,6 +1215,25 @@ struct monst *magr, /* monster that is currently deciding where to move */ 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; diff --git a/src/monmove.c b/src/monmove.c index 9f2696a5d..282a1fa4b 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -570,6 +570,52 @@ register struct monst *mtmp; 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. @@ -589,6 +635,9 @@ register int after; 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 */ @@ -909,12 +958,19 @@ not_special: 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++) @@ -1010,6 +1066,19 @@ not_special: 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); @@ -1287,6 +1356,41 @@ found_you: 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; diff --git a/src/monst.c b/src/monst.c index c241f3d3a..db30c2999 100644 --- a/src/monst.c +++ b/src/monst.c @@ -2748,7 +2748,7 @@ struct permonst _mons2[] = { 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), @@ -2757,7 +2757,7 @@ struct permonst _mons2[] = { 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), @@ -2766,7 +2766,7 @@ struct permonst _mons2[] = { 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 diff --git a/src/teleport.c b/src/teleport.c index 6627eebe6..bc0a46f50 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -416,6 +416,9 @@ tele() 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) diff --git a/util/makedefs.c b/util/makedefs.c index 5e221958a..34a3c64ae 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -676,6 +676,9 @@ static const char *build_opts[] = { #ifdef MAIL "mail daemon", #endif +#ifdef BARGETHROUGH + "monsters moving monsters", +#endif #ifdef GNUDOS "MSDOS protected mode", #endif