static void mvault_tele(struct monst *mtmp) { struct mkroom *croom = search_special(level, VAULT); coord c; if (croom && somexy(level, croom, &c) && goodpos(level, c.x, c.y, mtmp, 0)) { rloc_to(mtmp, c.x, c.y); return; } rloc(mtmp, FALSE); }
/* return TRUE if successful, FALSE if not */ boolean rloc(struct monst *mtmp, /* mx==0 implies migrating monster arrival */ boolean suppress_impossible) { int x, y, trycount; if (mtmp == u.usteed) { tele(); return TRUE; } if (mtmp->iswiz && mtmp->mx) { /* Wizard, not just arriving */ if (!In_W_tower(u.ux, u.uy, &u.uz)) x = level->upstair.sx, y = level->upstair.sy; else if (!level->dnladder.sx) /* bottom level of tower */ x = level->upladder.sx, y = level->upladder.sy; else x = level->dnladder.sx, y = level->dnladder.sy; /* if the wiz teleports away to heal, try the up staircase, to block the player's escaping before he's healed (deliberately use `goodpos' rather than `rloc_pos_ok' here) */ if (goodpos(level, x, y, mtmp, 0)) goto found_xy; } trycount = 0; do { x = rn1(COLNO - 3, 2); y = rn2(ROWNO); if ((trycount < 500) ? rloc_pos_ok(x, y, mtmp) : goodpos(level, x, y, mtmp, 0)) goto found_xy; } while (++trycount < 1000); /* last ditch attempt to find a good place */ for (x = 2; x < COLNO - 1; x++) for (y = 0; y < ROWNO; y++) if (goodpos(level, x, y, mtmp, 0)) goto found_xy; /* level either full of monsters or somehow faulty */ if (!suppress_impossible) impossible("rloc(): couldn't relocate monster"); return FALSE; found_xy: rloc_to(mtmp, x, y); return TRUE; }
/* you teleport a monster (via wand, spell, or poly'd q.mechanic attack); return false iff the attempt fails */ boolean u_teleport_mon(struct monst *mtmp, boolean give_feedback) { coord cc; if (mtmp->ispriest && *in_rooms(level, mtmp->mx, mtmp->my, TEMPLE)) { if (give_feedback) pline("%s resists your magic!", Monnam(mtmp)); return FALSE; } else if (level->flags.noteleport && u.uswallow && mtmp == u.ustuck) { if (give_feedback) pline("You are no longer inside %s!", mon_nam(mtmp)); unstuck(mtmp); rloc(mtmp, FALSE); } else if (is_rider(mtmp->data) && rn2(13) && enexto(&cc, level, u.ux, u.uy, mtmp->data)) rloc_to(mtmp, cc.x, cc.y); else rloc(mtmp, FALSE); return TRUE; }
/* return TRUE if successful, FALSE if not */ boolean rloc(struct monst *mtmp, /* mx==COLNO implies migrating monster arrival */ boolean suppress_impossible) { int x, y, trycount; int relaxed_goodpos; if (mtmp == u.usteed) { tele(); return TRUE; } if (!(mtmp->dlevel)) panic("trying to teleport monster onto which level?"); struct level *mdl = mtmp->dlevel; if (mtmp->iswiz && mtmp->mx != COLNO && mdl == level) { /* Wizard, not just arriving */ if (!In_W_tower(u.ux, u.uy, &u.uz)) x = mdl->upstair.sx, y = mdl->upstair.sy; else if (!isok(mdl->dnladder.sx, mdl->dnladder.sy)) x = mdl->upladder.sx, y = mdl->upladder.sy;/* bottom of tower */ else x = mdl->dnladder.sx, y = mdl->dnladder.sy; /* if the wiz teleports away to heal, try the up staircase, to block the player's escaping before he's healed (deliberately use `goodpos' rather than `rloc_pos_ok' here) */ if (goodpos(mdl, x, y, mtmp, 0)) goto found_xy; } for (relaxed_goodpos = 0; relaxed_goodpos < 2; relaxed_goodpos++) { /* first try sensible terrain; if none exists, ignore water, doors and boulders */ int gpflags = relaxed_goodpos ? MM_IGNOREWATER | MM_IGNOREDOORS : 0; /* try several pairs of positions; try the more restrictive rloc_pos_ok before we use the less restrictive goodpos */ trycount = 0; do { x = rn2(COLNO); y = rn2(ROWNO); if ((trycount < 500) ? rloc_pos_ok(x, y, mtmp) : goodpos(mdl, x, y, mtmp, gpflags)) goto found_xy; } while (++trycount < 1000); /* try every square on the mdl as a fallback */ for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) if (goodpos(mdl, x, y, mtmp, gpflags)) goto found_xy; } /* level either full of monsters or somehow faulty */ if (!suppress_impossible) impossible("rloc(): couldn't relocate monster"); return FALSE; found_xy: rloc_to(mtmp, x, y); return TRUE; }
/* return TRUE if successful, FALSE if not */ boolean rloc(struct monst *mtmp, /* mx==COLNO implies migrating monster arrival */ boolean suppress_impossible) { int x, y, trycount; int relaxed_goodpos; if (mtmp == u.usteed) { tele(); return TRUE; } if (mtmp->iswiz && mtmp->mx != COLNO) { /* Wizard, not just arriving */ if (!In_W_tower(u.ux, u.uy, &u.uz)) x = level->upstair.sx, y = level->upstair.sy; else if (!isok(level->dnladder.sx, level->dnladder.sy)) x = level->upladder.sx, y = level->upladder.sy;/* bottom of tower */ else x = level->dnladder.sx, y = level->dnladder.sy; /* if the wiz teleports away to heal, try the up staircase, to block the player's escaping before he's healed (deliberately use `goodpos' rather than `rloc_pos_ok' here) */ if (goodpos(level, x, y, mtmp, 0)) goto found_xy; } for (relaxed_goodpos = -1; relaxed_goodpos < 2; relaxed_goodpos++) { /* If this is a monster that blinks, try to do that first. */ if (relaxed_goodpos < 0) { if ((isok(mtmp->mx, mtmp->my)) && mtmp->data->mflags3 & M3_BLINKAWAY) { /* We're going to do a polar-to-rectangular conversion here, because it's a convenient way to select positions at the correct distance from where we're starting. We'll try with the maximum radius then back it off. */ int maxradius = 2 * mtmp->data->mlevel; int minradius = 2; int theta[24] = { 0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345 }; int angle, fineangle, swi, sw; coord rectcoord; if (maxradius < minradius + 3) maxradius = minradius + 3; /* Shuffle the order of the angles so we don't always get the same one tried first. */ for (angle = 0; angle < 24; angle++) { swi = rn2(24); sw = theta[swi]; theta[swi] = theta[angle]; theta[angle] = sw; } for (trycount = maxradius; trycount >= minradius; trycount--) { for (angle = 0; angle < 24; angle++) for (fineangle = 0; fineangle < 15; fineangle += 3) { /* theta is shuffled so that the angle isn't the same all the time, but it isn't necessary to shuffle over a hundred different angles; we use fineangle to allow positions that don't line up to the 15-degree increments, but the randomness of the blink direction doesn't need that much precision. */ rectcoord = polartorect(trycount, theta[angle] + fineangle); x = mtmp->mx + rectcoord.x; y = mtmp->my + rectcoord.y; if (isok(x,y) && !m_at(level,x,y) && /* TODO: evaluate whether goodpos() should be * used here */ (level->locations[x][y].typ >= CORR) && /* Blinking only works with line-of-sight, but for now I am not requiring the monster to actually _see_ the tile, so e.g. blinking into the dark works ok. */ clear_path(mtmp->mx, mtmp->my, x, y, viz_array) ) { goto found_xy; } } } } continue; } /* first try sensible terrain; if none exists, ignore water, doors and boulders */ int gpflags = relaxed_goodpos ? MM_IGNOREWATER | MM_IGNOREDOORS : 0; /* try several pairs of positions; try the more restrictive rloc_pos_ok before we use the less restrictive goodpos */ trycount = 0; do { x = rn2(COLNO); y = rn2(ROWNO); if ((trycount < 500) ? rloc_pos_ok(x, y, mtmp) : goodpos(level, x, y, mtmp, gpflags)) goto found_xy; } while (++trycount < 1000); /* try every square on the level as a fallback */ for (x = 0; x < COLNO; x++) for (y = 0; y < ROWNO; y++) if (goodpos(level, x, y, mtmp, gpflags)) goto found_xy; } /* level either full of monsters or somehow faulty */ if (!suppress_impossible) impossible("rloc(): couldn't relocate monster"); return FALSE; found_xy: rloc_to(mtmp, x, y); return TRUE; }
/* called from resurrect() in addition to losedogs() */ void mon_arrive(struct monst *mtmp, boolean with_you) { struct trap *t; struct obj *otmp; xchar xlocale, ylocale, xyloc, xyflags, wander; int num_segs; mtmp->dlevel = level; mtmp->nmon = level->monlist; level->monlist = mtmp; if (mtmp->isshk) set_residency(mtmp, FALSE); num_segs = mtmp->wormno; /* baby long worms have no tail so don't use is_longworm() */ if ((mtmp->data == &mons[PM_LONG_WORM]) && (mtmp->wormno = get_wormno(mtmp->dlevel)) != 0) { initworm(mtmp, num_segs); /* tail segs are not yet initialized or displayed */ } else mtmp->wormno = 0; /* some monsters might need to do something special upon arrival _after_ the current level has been fully set up; see dochug() */ mtmp->mstrategy |= STRAT_ARRIVE; xyloc = mtmp->xyloc; xyflags = mtmp->xyflags; xlocale = mtmp->xlocale; ylocale = mtmp->ylocale; for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) set_obj_level(mtmp->dlevel, otmp); if (mtmp == u.usteed) return; /* don't place steed on the map */ if (with_you) { /* When a monster accompanies you, sometimes it will arrive at your intended destination and you'll end up next to that spot. This code doesn't control the final outcome; goto_level(do.c) decides who ends up at your target spot when there is a monster there too. */ if (!MON_AT(level, u.ux, u.uy) && !rn2(mtmp->mtame ? 10 : mtmp->mpeaceful ? 5 : 2)) rloc_to(mtmp, u.ux, u.uy); else mnexto(mtmp); return; } /* * The monster arrived on this level independently of the player. * Its coordinate fields were overloaded for use as flags that * specify its final destination. */ if (mtmp->mlstmv < moves - 1L) { /* heal monster for time spent in limbo */ long nmv = moves - 1L - mtmp->mlstmv; mon_catchup_elapsed_time(mtmp, nmv); mtmp->mlstmv = moves - 1L; /* let monster move a bit on new level (see placement code below) */ wander = (xchar) min(nmv, 8); } else wander = 0; switch (xyloc) { case MIGR_APPROX_XY: /* {x,y}locale set above */ break; case MIGR_EXACT_XY: wander = 0; break; case MIGR_NEAR_PLAYER: xlocale = u.ux, ylocale = u.uy; break; case MIGR_STAIRS_UP: xlocale = level->upstair.sx, ylocale = level->upstair.sy; break; case MIGR_STAIRS_DOWN: xlocale = level->dnstair.sx, ylocale = level->dnstair.sy; break; case MIGR_LADDER_UP: xlocale = level->upladder.sx, ylocale = level->upladder.sy; break; case MIGR_LADDER_DOWN: xlocale = level->dnladder.sx, ylocale = level->dnladder.sy; break; case MIGR_SSTAIRS: xlocale = level->sstairs.sx, ylocale = level->sstairs.sy; break; case MIGR_PORTAL: if (In_endgame(&u.uz)) { /* there is no arrival portal for endgame levels */ /* BUG[?]: for simplicity, this code relies on the fact that we know that the current endgame levels always build upwards and never have any exclusion subregion inside their TELEPORT_REGION settings. */ xlocale = rn1(level->updest.hx - level->updest.lx + 1, level->updest.lx); ylocale = rn1(level->updest.hy - level->updest.ly + 1, level->updest.ly); break; } /* find the arrival portal */ for (t = level->lev_traps; t; t = t->ntrap) if (t->ttyp == MAGIC_PORTAL) break; if (t) { xlocale = t->tx, ylocale = t->ty; break; } else { impossible("mon_arrive: no corresponding portal?"); } /*FALLTHRU*/ default: case MIGR_RANDOM: xlocale = COLNO; ylocale = ROWNO; break; } if ((xlocale != COLNO) && wander) { /* monster moved a bit; pick a nearby location */ /* mnearto() deals w/stone, et al */ char *r = in_rooms(level, xlocale, ylocale, 0); if (r && *r) { coord c; /* somexy() handles irregular level->rooms */ if (somexy(level, &level->rooms[*r - ROOMOFFSET], &c, rng_main)) xlocale = c.x, ylocale = c.y; else { xlocale = COLNO; ylocale = ROWNO; } } else { /* not in a room */ int i, j; i = max(0, xlocale - wander); j = min(COLNO - 1, xlocale + wander); xlocale = rn1(j - i, i); i = max(0, ylocale - wander); j = min(ROWNO - 1, ylocale + wander); ylocale = rn1(j - i, i); } } /* moved a bit */ mtmp->mx = COLNO; /* (already is 0) */ mtmp->my = xyflags; if (xlocale != COLNO) mnearto(mtmp, xlocale, ylocale, FALSE); else { if (!rloc(mtmp, TRUE)) { /* Failed to place migrating monster, probably because the level is full. Dump the monster's cargo and leave the monster dead. */ struct obj *obj; while ((obj = mtmp->minvent) != 0) { obj_extract_self(obj); obj_no_longer_held(obj); if (obj->owornmask & W_MASK(os_wep)) setmnotwielded(mtmp, obj); obj->owornmask = 0L; if (xlocale != COLNO && ylocale != ROWNO) place_object(obj, level, xlocale, ylocale); else { rloco(obj); get_obj_location(obj, &xlocale, &ylocale, 0); } } mkcorpstat(CORPSE, NULL, mtmp->data, level, xlocale, ylocale, FALSE, rng_main); mongone(mtmp); } } mtmp->mux = COLNO; mtmp->muy = ROWNO; }
/* Stop riding the current steed */ void dismount_steed(int reason) { struct monst *mtmp; struct obj *otmp; coord cc; const char *verb = "fall"; boolean repair_leg_damage = TRUE; unsigned save_utrap = u.utrap; boolean have_spot = landing_spot(&cc, reason, 0); mtmp = u.usteed; /* make a copy of steed pointer */ /* Sanity check */ if (!mtmp) /* Just return silently */ return; /* Check the reason for dismounting */ otmp = which_armor(mtmp, os_saddle); switch (reason) { case DISMOUNT_THROWN: verb = "are thrown"; case DISMOUNT_FELL: pline("You %s off of %s!", verb, mon_nam(mtmp)); if (!have_spot) have_spot = landing_spot(&cc, reason, 1); losehp(rn1(10, 10), "killed in a riding accident"); set_wounded_legs(LEFT_SIDE, (int)LWounded_legs + rn1(5, 5)); set_wounded_legs(RIGHT_SIDE, (int)RWounded_legs + rn1(5, 5)); repair_leg_damage = FALSE; break; case DISMOUNT_POLY: pline("You can no longer ride %s.", mon_nam(u.usteed)); if (!have_spot) have_spot = landing_spot(&cc, reason, 1); break; case DISMOUNT_ENGULFED: /* caller displays message */ break; case DISMOUNT_BONES: /* hero has just died... */ break; case DISMOUNT_GENERIC: /* no messages, just make it so */ break; case DISMOUNT_BYCHOICE: default: if (otmp && otmp->cursed) { pline("You can't. The saddle %s cursed.", otmp->bknown ? "is" : "seems to be"); otmp->bknown = TRUE; return; } if (!have_spot) { pline("You can't. There isn't anywhere for you to stand."); return; } if (!mtmp->mnamelth) { pline("You've been through the dungeon on %s with no name.", an(mtmp->data->mname)); if (Hallucination) pline("It felt good to get out of the rain."); } else pline("You dismount %s.", mon_nam(mtmp)); } /* While riding these refer to the steed's legs so after dismounting they refer to the player's legs once again. */ if (repair_leg_damage) LWounded_legs = RWounded_legs = 0; /* Release the steed and saddle */ u.usteed = 0; u.ugallop = 0L; /* Set player and steed's position. Try moving the player first unless we're in the midst of creating a bones file. */ if (reason == DISMOUNT_BONES) { /* move the steed to an adjacent square */ if (enexto(&cc, level, u.ux, u.uy, mtmp->data)) rloc_to(mtmp, cc.x, cc.y); else /* evidently no room nearby; move steed elsewhere */ rloc(mtmp, FALSE); return; } if (!DEADMONSTER(mtmp)) { place_monster(mtmp, u.ux, u.uy); if (!Engulfed && !u.ustuck && have_spot) { const struct permonst *mdat = mtmp->data; /* The steed may drop into water/lava */ if (!is_flyer(mdat) && !is_floater(mdat) && !is_clinger(mdat)) { if (is_pool(level, u.ux, u.uy)) { if (!Underwater) pline("%s falls into the %s!", Monnam(mtmp), surface(u.ux, u.uy)); if (!is_swimmer(mdat) && !amphibious(mdat)) { killed(mtmp); adjalign(-1); } } else if (is_lava(level, u.ux, u.uy)) { pline("%s is pulled into the lava!", Monnam(mtmp)); if (!likes_lava(mdat)) { killed(mtmp); adjalign(-1); } } } /* Steed dismounting consists of two steps: being moved to another square, and descending to the floor. We have functions to do each of these activities, but they're normally called individually and include an attempt to look at or pick up the objects on the floor: teleds() --> spoteffects() --> pickup() float_down() --> pickup() We use this kludge to make sure there is only one such attempt. Clearly this is not the best way to do it. A full fix would involve having these functions not call pickup() at all, instead calling them first and calling pickup() afterwards. But it would take a lot of work to keep this change from having any unforseen side effects (for instance, you would no longer be able to walk onto a square with a hole, and autopickup before falling into the hole). */ /* [ALI] No need to move the player if the steed died. */ if (!DEADMONSTER(mtmp)) { /* Keep steed here, move the player to cc; teleds() clears u.utrap */ in_steed_dismounting = TRUE; teleds(cc.x, cc.y, TRUE); in_steed_dismounting = FALSE; /* Put your steed in your trap */ if (save_utrap) mintrap(mtmp); } /* Couldn't... try placing the steed */ } else if (enexto(&cc, level, u.ux, u.uy, mtmp->data)) { /* Keep player here, move the steed to cc */ rloc_to(mtmp, cc.x, cc.y); /* Player stays put */ /* Otherwise, kill the steed */ } else { killed(mtmp); adjalign(-1); } } /* Return the player to the floor */ if (reason != DISMOUNT_ENGULFED) { in_steed_dismounting = TRUE; float_down(0L); in_steed_dismounting = FALSE; encumber_msg(); turnstate.vision_full_recalc = TRUE; } else /* polearms behave differently when not mounted */ if (uwep && is_pole(uwep)) u.bashmsg = FALSE; return; }