void migrate_to_level( struct monst *mtmp, xchar tolev, /* destination level */ xchar xyloc, /* MIGR_xxx destination xy location: */ coord *cc) /* optional destination coordinates */ { struct obj *obj; d_level new_lev; xchar xyflags; int num_segs = 0; /* count of worm segments */ if (mtmp->isshk) set_residency(mtmp, TRUE); if (mtmp->wormno) { int cnt; /* **** NOTE: worm is truncated to # segs = max wormno size **** */ cnt = count_wsegs(mtmp); num_segs = min(cnt, MAX_NUM_WORMS - 1); wormgone(mtmp); } /* set minvent's obj->no_charge to 0 */ for (obj = mtmp->minvent; obj; obj = obj->nobj) { if (Has_contents(obj)) picked_container(obj); /* does the right thing */ obj->no_charge = 0; } if (mtmp->mleashed) { mtmp->mtame--; m_unleash(mtmp, TRUE); } relmon(mtmp); mtmp->nmon = migrating_mons; migrating_mons = mtmp; newsym(mtmp->mx,mtmp->my); new_lev.dnum = ledger_to_dnum((xchar)tolev); new_lev.dlevel = ledger_to_dlev((xchar)tolev); /* overload mtmp->[mx,my], mtmp->[mux,muy], and mtmp->mtrack[] as */ /* destination codes (setup flag bits before altering mx or my) */ xyflags = (depth(&new_lev) < depth(&u.uz)); /* 1 => up */ if (In_W_tower(mtmp->mx, mtmp->my, &u.uz)) xyflags |= 2; mtmp->wormno = num_segs; mtmp->mlstmv = moves; mtmp->mtrack[1].x = cc ? cc->x : mtmp->mx; mtmp->mtrack[1].y = cc ? cc->y : mtmp->my; mtmp->mtrack[0].x = xyloc; mtmp->mtrack[0].y = xyflags; mtmp->mux = new_lev.dnum; mtmp->muy = new_lev.dlevel; mtmp->mx = mtmp->my = 0; /* this implies migration */ }
/* 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; }
/* 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; }
void migrate_to_level(struct monst *mtmp, xchar tolev, /* destination level */ xchar xyloc, /* MIGR_xxx destination xy location: */ coord * cc) { /* optional destination coordinates */ struct obj *obj; d_level new_lev; xchar xyflags; int num_segs = 0; /* count of worm segments */ if (mtmp->isshk) set_residency(mtmp, TRUE); if (mtmp->wormno) { int cnt; /* **** NOTE: worm is truncated to # segs = max wormno size **** */ cnt = count_wsegs(mtmp); num_segs = min(cnt, MAX_NUM_WORMS - 1); wormgone(mtmp); } /* set minvent's obj->no_charge to 0 */ for (obj = mtmp->minvent; obj; obj = obj->nobj) { if (Has_contents(obj)) picked_container(obj); /* does the right thing */ obj->no_charge = 0; } if (mtmp->mleashed) { mtmp->mtame--; m_unleash(mtmp, TRUE); } relmon(mtmp); mtmp->nmon = migrating_mons; migrating_mons = mtmp; if (mtmp->dlevel == level) newsym(mtmp->mx, mtmp->my); /* The dlevel pointer is meaningless for a migrating monster. Set it to NULL so that any uses of it are detected quickly via the resulting segfault. */ mtmp->dlevel = NULL; new_lev.dnum = ledger_to_dnum((xchar) tolev); new_lev.dlevel = ledger_to_dlev((xchar) tolev); /* set migration data */ xyflags = (depth(&new_lev) < depth(&u.uz)); /* 1 => up */ if (In_W_tower(mtmp->mx, mtmp->my, &u.uz)) xyflags |= 2; mtmp->wormno = num_segs; mtmp->mlstmv = moves; mtmp->xlocale = cc ? cc->x : mtmp->mx; mtmp->ylocale = cc ? cc->y : mtmp->my; mtmp->xyloc = xyloc; mtmp->xyflags = xyflags; mtmp->mux = new_lev.dnum; mtmp->muy = new_lev.dlevel; mtmp->mx = COLNO; mtmp->my = ROWNO; /* this implies migration */ }