From 89c785e366cdc9c63b4c1ae73ae390ea170bd692 Mon Sep 17 00:00:00 2001 From: "nethack.allison" Date: Sun, 16 Nov 2003 20:10:30 +0000 Subject: [PATCH] monsters moving other monsters (trunk only) For now, the code is conditional on BARGETHROUGH being defined, while it gets tested further. While behavior is different with and without BARGETHROUGH defined, savefiles are the same either way. After this patch is applied, only the riders have the M3_DISPLACES bit set, but the Wizard and Vlad probably should too. Any others? --- include/config.h | 1 + include/extern.h | 11 +++++ include/mfndpos.h | 3 ++ include/mondata.h | 3 ++ include/monflag.h | 3 ++ include/patchlevel.h | 2 +- src/dogmove.c | 33 ++++++++++++- src/mhitm.c | 109 ++++++++++++++++++++++++++++++++++++++++++- src/mon.c | 35 ++++++++++++++ src/monmove.c | 106 ++++++++++++++++++++++++++++++++++++++++- src/monst.c | 6 +-- src/teleport.c | 3 ++ util/makedefs.c | 3 ++ 13 files changed, 310 insertions(+), 8 deletions(-) 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,114 @@ fightm(mtmp) /* have monsters fight each other */ 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 @@ -184,6 +288,7 @@ fightm(mtmp) /* have monsters fight each other */ * 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 -- 2.40.0