/* cutworm() * * Check for mon->wormno before calling this function! * * When hitting a worm (worm) at position x, y, with a weapon (weap), * there is a chance that the worm will be cut in half, and a chance * that both halves will survive. */ void cutworm(struct monst *worm, xchar x, xchar y, struct obj *weap) { struct wseg *curr, *new_tail; struct monst *new_worm; int wnum = worm->wormno; int cut_chance, new_wnum; if (!wnum) return; /* bullet proofing */ if (x == worm->mx && y == worm->my) return; /* hit on head */ /* cutting goes best with a bladed weapon */ cut_chance = rnd(20); /* Normally 1-16 does not cut */ /* Normally 17-20 does */ if (weap && objects[weap->otyp].oc_dir & SLASH) cut_chance += 10; /* With a slashing weapon 1- 6 does not cut */ /* 7-20 does */ if (cut_chance < 17) return; /* not good enough */ /* Find the segment that was attacked. */ curr = level->wtails[wnum]; while ((curr->wx != x) || (curr->wy != y)) { curr = curr->nseg; if (!curr) { impossible("cutworm: no segment at (%d,%d)", (int)x, (int)y); return; } } /* If this is the tail segment, then the worm just loses it. */ if (curr == level->wtails[wnum]) { shrink_worm(wnum); return; } /* * Split the worm. The tail for the new worm is the old worm's tail. * The tail for the old worm is the segment that follows "curr", * and "curr" becomes the dummy segment under the new head. */ new_tail = level->wtails[wnum]; level->wtails[wnum] = curr->nseg; curr->nseg = NULL; /* split the worm */ /* * At this point, the old worm is correct. Any new worm will have * it's head at "curr" and its tail at "new_tail". */ /* Sometimes the tail end dies. */ if (rn2(3) || !(new_wnum = get_wormno(level)) || !worm->m_lev) { cutoff(worm, new_tail); return; } remove_monster(level, x, y); /* clone_mon puts new head here */ if (!(new_worm = clone_mon(worm, x, y))) { cutoff(worm, new_tail); return; } new_worm->wormno = new_wnum; /* affix new worm number */ /* Devalue the monster level of both halves of the worm. */ worm->m_lev = ((unsigned)worm->m_lev <= 3) ? (unsigned)worm->m_lev : max((unsigned)worm->m_lev - 2, 3); new_worm->m_lev = worm->m_lev; /* Calculate the mhp on the new_worm for the (lower) monster level. */ new_worm->mhpmax = new_worm->mhp = dice((int)new_worm->m_lev, 8); /* Calculate the mhp on the old worm for the (lower) monster level. */ if (worm->m_lev > 3) { worm->mhpmax = dice((int)worm->m_lev, 8); if (worm->mhpmax < worm->mhp) worm->mhp = worm->mhpmax; } level->wtails[new_wnum] = new_tail; /* We've got all the info right now */ level->wheads[new_wnum] = curr; /* so we can do this faster than */ level->wgrowtime[new_wnum] = 0L; /* trying to call initworm(). */ /* Place the new monster at all the segment locations. */ place_wsegs(new_worm); if (flags.mon_moving) pline(msgc_monneutral, "%s is cut in half.", Monnam(worm)); else pline(msgc_combatgood, "You cut %s in half.", mon_nam(worm)); }
/* 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; }