/* * Is (x,y) a good position of mtmp? If mtmp is NULL, then is (x,y) good * for an object? * * This function will only look at mtmp->mdat, so makemon, mplayer, etc can * call it to generate new monster positions with fake monster structures. */ boolean goodpos(struct level *lev, int x, int y, struct monst *mtmp, unsigned gpflags) { const struct permonst *mdat = NULL; boolean ignorewater = ((gpflags & MM_IGNOREWATER) != 0); if (!isok(x, y)) return FALSE; /* in many cases, we're trying to create a new monster, which * can't go on top of the player or any existing monster. * however, occasionally we are relocating engravings or objects, * which could be co-located and thus get restricted a bit too much. * oh well. */ if (mtmp != &youmonst && x == u.ux && y == u.uy && (!u.usteed || mtmp != u.usteed)) return FALSE; if (mtmp) { struct monst *mtmp2 = m_at(lev, x,y); /* Be careful with long worms. A monster may be placed back in * its own location. Normally, if m_at() returns the same monster * that we're trying to place, the monster is being placed in its * own location. However, that is not correct for worm segments, * because all the segments of the worm return the same m_at(). * Actually we overdo the check a little bit--a worm can't be placed * in its own location, period. If we just checked for mtmp->mx * != x || mtmp->my != y, we'd miss the case where we're called * to place the worm segment and the worm's head is at x,y. */ if (mtmp2 && (mtmp2 != mtmp || mtmp->wormno)) return FALSE; mdat = mtmp->data; if (is_pool(lev, x,y) && !ignorewater) { if (mtmp == &youmonst) return !!(HLevitation || Flying || Wwalking || Swimming || Amphibious); else return (is_flyer(mdat) || is_swimmer(mdat) || is_clinger(mdat)); } else if (mdat->mlet == S_EEL && rn2(13) && !ignorewater) { return FALSE; } else if (is_lava(lev, x,y)) { if (mtmp == &youmonst) return !!HLevitation; else return is_flyer(mdat) || likes_lava(mdat); } if (passes_walls(mdat) && may_passwall(lev, x,y)) return TRUE; } if (!ACCESSIBLE(lev->locations[x][y].typ)) { if (!(is_pool(lev, x,y) && ignorewater)) return FALSE; } if (closed_door(lev, x, y) && (!mdat || !amorphous(mdat))) return FALSE; if (sobj_at(BOULDER, lev, x, y) && (!mdat || !throws_rocks(mdat))) return FALSE; return TRUE; }
/* * Simple-minded "can it be here?" routine */ static boolean e_survives_at(struct entity *etmp, int x, int y) { if (noncorporeal(etmp->edata)) return TRUE; if (is_pool(level, x, y)) return (boolean)((is_u(etmp) && (Wwalking || Amphibious || Swimming || Flying || Levitation)) || is_swimmer(etmp->edata) || is_flyer(etmp->edata) || is_floater(etmp->edata)); /* must force call to lava_effects in e_died if is_u */ if (is_lava(level, x, y)) return (boolean)((is_u(etmp) && (Levitation || Flying)) || likes_lava(etmp->edata) || is_flyer(etmp->edata)); if (is_db_wall(x, y)) return((boolean)(is_u(etmp) ? Passes_walls : passes_walls(etmp->edata))); return TRUE; }
/* true iff the type of monster pass through iron bars */ boolean passes_bars(const struct permonst * mptr) { return (boolean) (passes_walls(mptr) || amorphous(mptr) || is_whirly(mptr) || verysmall(mptr) || (slithy(mptr) && !bigmonst(mptr))); }
/* * These are never directly affected by a bridge or portcullis. */ static boolean automiss(struct entity *etmp) { return (boolean)((is_u(etmp) ? Passes_walls : passes_walls(etmp->edata)) || noncorporeal(etmp->edata)); }
/* * Move for priests and shopkeepers. Called from shk_move() and pri_move(). * Valid returns are 1: moved 0: didn't -1: let m_move do it -2: died. */ int move_special(struct monst *mtmp, boolean in_his_shop, schar appr, boolean uondoor, boolean avoid, xchar omx, xchar omy, xchar gx, xchar gy) { xchar nx, ny, nix, niy; schar i; schar chcnt, cnt; coord poss[9]; long info[9]; long allowflags; struct obj *ib = NULL; if (omx == gx && omy == gy) return 0; if (mtmp->mconf) { avoid = FALSE; appr = 0; } nix = omx; niy = omy; if (mtmp->isshk) allowflags = ALLOW_SSM; else allowflags = ALLOW_SSM | ALLOW_SANCT; if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK | ALLOW_WALL); if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK; if (tunnels(mtmp->data)) allowflags |= ALLOW_DIG; if (!nohands(mtmp->data) && !verysmall(mtmp->data)) { allowflags |= OPENDOOR; if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR; } if (is_giant(mtmp->data)) allowflags |= BUSTDOOR; cnt = mfndpos(mtmp, poss, info, allowflags); if (mtmp->isshk && avoid && uondoor) { /* perhaps we cannot avoid him */ for (i = 0; i < cnt; i++) if (!(info[i] & NOTONL)) goto pick_move; avoid = FALSE; } #define GDIST(x,y) (dist2(x,y,gx,gy)) pick_move: chcnt = 0; for (i = 0; i < cnt; i++) { nx = poss[i].x; ny = poss[i].y; if (IS_ROOM(level->locations[nx][ny].typ) || (mtmp->isshk && (!in_his_shop || ESHK(mtmp)->following))) { if (avoid && (info[i] & NOTONL)) continue; if ((!appr && !rn2(++chcnt)) || (appr && GDIST(nx, ny) < GDIST(nix, niy))) { nix = nx; niy = ny; } } } if (mtmp->ispriest && avoid && nix == omx && niy == omy && onlineu(omx, omy)) { /* might as well move closer as long it's going to stay lined up */ avoid = FALSE; goto pick_move; } if (nix != omx || niy != omy) { remove_monster(level, omx, omy); place_monster(mtmp, nix, niy); newsym(nix, niy); if (mtmp->isshk && !in_his_shop && inhishop(mtmp)) check_special_room(FALSE); if (ib) { if (cansee(mtmp->mx, mtmp->my)) pline("%s picks up %s.", Monnam(mtmp), distant_name(ib, doname)); obj_extract_self(ib); mpickobj(mtmp, ib); } return 1; } return 0; }
/* Returns an object slot mask giving all the reasons why the given player/monster might have the given property, limited by "reasons", an object slot mask (W_EQUIP, INTRINSIC, and ANY_PROPERTY are the most likely values here, but you can specify slots individually if you like). */ unsigned m_has_property(const struct monst *mon, enum youprop property, unsigned reasons, boolean even_if_blocked) { unsigned rv = 0; struct obj *otmp; /* The general case for equipment */ rv |= mworn_extrinsic(mon, property); if (mon == &youmonst) { /* Intrinsics */ if (u.uintrinsic[property] & TIMEOUT) rv |= W_MASK(os_timeout); rv |= u.uintrinsic[property] & (INTRINSIC | I_SPECIAL); /* Birth options */ if (property == BLINDED && flags.permablind) rv |= W_MASK(os_birthopt); if (property == HALLUC && flags.permahallu) rv |= W_MASK(os_birthopt); if (property == UNCHANGING && flags.polyinit_mnum != -1) rv |= W_MASK(os_birthopt); } else { /* Monster tempraries are boolean flags. TODO: Monsters with no eyes are not considered blind. This doesn't make much sense. However, changing it would be a major balance change (due to Elbereth), and so it has been left alone for now. */ if (property == BLINDED && (!mon->mcansee || mon->mblinded)) rv |= W_MASK(os_timeout); if (property == FAST && mon->mspeed == MFAST) rv |= (mon->permspeed == FAST ? W_MASK(os_polyform) : W_MASK(os_outside)); if (property == INVIS && mon->perminvis && !pm_invisible(mon->data)) rv |= W_MASK(os_outside); if (property == STUNNED && mon->mstun) rv |= W_MASK(os_timeout); if (property == CONFUSION && mon->mconf) rv |= W_MASK(os_timeout); } /* Polyform / monster intrinsic */ /* TODO: Change the monster data code into something that doesn't require a giant switch statement or ternary chain to get useful information from it. We use a ternary chain here because it cuts down on repetitive code and so is easier to read. */ if (property == FIRE_RES ? resists_fire(mon) : property == COLD_RES ? resists_cold(mon) : property == SLEEP_RES ? resists_sleep(mon) : property == DISINT_RES ? resists_disint(mon) : property == SHOCK_RES ? resists_elec(mon) : property == POISON_RES ? resists_poison(mon) : property == DRAIN_RES ? resists_drli(mon) : property == SICK_RES ? mon->data->mlet == S_FUNGUS || mon->data == &mons[PM_GHOUL] : property == ANTIMAGIC ? resists_magm(mon) : property == ACID_RES ? resists_acid(mon) : property == STONE_RES ? resists_ston(mon) : property == STUNNED ? u.umonnum == PM_STALKER || mon->data->mlet == S_BAT : property == BLINDED ? !haseyes(mon->data) : property == HALLUC ? Upolyd && dmgtype(mon->data, AD_HALU) : property == SEE_INVIS ? perceives(mon->data) : property == TELEPAT ? telepathic(mon->data) : property == INFRAVISION ? infravision(mon->data) : /* Note: This one assumes that there's no way to permanently turn visible when you're in stalker form (i.e. mummy wrappings only). */ property == INVIS ? pm_invisible(mon->data) : property == TELEPORT ? can_teleport(mon->data) : property == LEVITATION ? is_floater(mon->data) : property == FLYING ? is_flyer(mon->data) : property == SWIMMING ? is_swimmer(mon->data) : property == PASSES_WALLS ? passes_walls(mon->data) : property == REGENERATION ? regenerates(mon->data) : property == REFLECTING ? mon->data == &mons[PM_SILVER_DRAGON] : property == TELEPORT_CONTROL ? control_teleport(mon->data) : property == MAGICAL_BREATHING ? amphibious(mon->data) : 0) rv |= W_MASK(os_polyform); if (mon == &youmonst) { /* External circumstances */ if (property == BLINDED && u_helpless(hm_unconscious)) rv |= W_MASK(os_circumstance); /* Riding */ if (property == FLYING && u.usteed && is_flyer(u.usteed->data)) rv |= W_MASK(os_saddle); if (property == SWIMMING && u.usteed && is_swimmer(u.usteed->data)) rv |= W_MASK(os_saddle); } /* Overrides */ if (!even_if_blocked) { if (property == BLINDED) { for (otmp = m_minvent(mon); otmp; otmp = otmp->nobj) if (otmp->oartifact == ART_EYES_OF_THE_OVERWORLD && otmp->owornmask & W_MASK(os_tool)) rv &= (unsigned)(W_MASK(os_circumstance) | W_MASK(os_birthopt)); } if (property == WWALKING && Is_waterlevel(m_mz(mon))) rv &= (unsigned)(W_MASK(os_birthopt)); if (mworn_blocked(mon, property)) rv &= (unsigned)(W_MASK(os_birthopt)); } return rv & reasons; }