static boolean artifact_hit_drainlife(struct monst *magr, struct monst *mdef, struct obj *otmp, int *dmgptr) { boolean youattack = (magr == &youmonst); boolean youdefend = (mdef == &youmonst); boolean vis = (!youattack && magr && cansee(magr->mx, magr->my)) || (!youdefend && cansee(mdef->mx, mdef->my)) || (youattack && u.uswallow && mdef == u.ustuck && !Blind); if (!youdefend) { if (vis) { if (otmp->oartifact == ART_STORMBRINGER) pline("The %s blade draws the life from %s!", hcolor("black"), mon_nam(mdef)); else pline("%s draws the life from %s!", The(distant_name(otmp, xname)), mon_nam(mdef)); } if (mdef->m_lev == 0) { *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; } else { int drain = rnd(8); *dmgptr += drain; mdef->mhpmax -= drain; mdef->m_lev--; drain /= 2; if (drain) healup(drain, 0, FALSE, FALSE); } return vis; } else { /* youdefend */ int oldhpmax = u.uhpmax; if (Blind) pline("You feel an %s drain your life!", otmp->oartifact == ART_STORMBRINGER ? "unholy blade" : "object"); else if (otmp->oartifact == ART_STORMBRINGER) pline("The %s blade drains your life!", hcolor("black")); else pline("%s drains your life!", The(distant_name(otmp, xname))); losexp("life drainage"); if (magr && magr->mhp < magr->mhpmax) { magr->mhp += (oldhpmax - u.uhpmax)/2; if (magr->mhp > magr->mhpmax) magr->mhp = magr->mhpmax; } return TRUE; } return FALSE; }
static int describe_object(int x, int y, int votyp, char *buf) { int num_objs = 0; struct obj *otmp; if (votyp == -1) return -1; otmp = vobj_at(x,y); if (!otmp || otmp->otyp != votyp) { if (votyp != STRANGE_OBJECT) { otmp = mksobj(level, votyp, FALSE, FALSE); if (otmp->oclass == COIN_CLASS) otmp->quan = 1L; /* to force pluralization off */ else if (otmp->otyp == SLIME_MOLD) otmp->spe = current_fruit; /* give the fruit a type */ strcpy(buf, distant_name(otmp, xname)); dealloc_obj(otmp); otmp = vobj_at(x,y); /* make sure we don't point to the temp obj any more */ } } else strcpy(buf, distant_name(otmp, xname)); if (level->locations[x][y].typ == STONE || level->locations[x][y].typ == SCORR) strcat(buf, " embedded in stone"); else if (IS_WALL(level->locations[x][y].typ) || level->locations[x][y].typ == SDOOR) strcat(buf, " embedded in a wall"); else if (closed_door(level, x,y)) strcat(buf, " embedded in a door"); else if (is_pool(level, x,y)) strcat(buf, " in water"); else if (is_lava(level, x,y)) strcat(buf, " in molten lava"); /* [can this ever happen?] */ if (!cansee(x, y)) return -1; /* don't disclose the number of objects for location out of LOS */ if (!otmp) /* There is no object here. Since the player sees one it must be a mimic */ return 1; if (otmp->otyp != votyp) /* Hero sees something other than the actual top object. Probably a mimic */ num_objs++; for ( ; otmp; otmp = otmp->nexthere) num_objs++; return num_objs; }
/* drop one object taken from a (possibly dead) monster's inventory */ static void mdrop_obj(struct monst *mon, struct obj *obj, boolean verbosely) { int omx = mon->mx, omy = mon->my; if (obj->owornmask) { /* perform worn item handling if the monster is still alive */ if (mon->mhp > 0) { mon->misc_worn_check &= ~obj->owornmask; update_mon_intrinsics(level, mon, obj, FALSE, TRUE); /* obj_no_longer_held(obj); -- done by place_object */ if (obj->owornmask & W_WEP) setmnotwielded(mon, obj); /* don't charge for an owned saddle on dead steed */ } else if (mon->mtame && (obj->owornmask & W_SADDLE) && !obj->unpaid && costly_spot(omx, omy)) { obj->no_charge = 1; } obj->owornmask = 0L; } if (verbosely && cansee(omx, omy)) pline("%s drops %s.", Monnam(mon), distant_name(obj, doname)); if (!flooreffects(obj, omx, omy, "fall")) { place_object(obj, level, omx, omy); stackobj(obj); } }
static void m_dowear_type(struct monst *mon, long flag, boolean creation, boolean racialexception) { struct obj *old, *best, *obj; int m_delay = 0; int unseen = !canseemon(mon); char nambuf[BUFSZ]; if (mon->mfrozen) return; /* probably putting previous item on */ /* Get a copy of monster's name before altering its visibility */ strcpy(nambuf, See_invisible ? Monnam(mon) : mon_nam(mon)); old = which_armor(mon, flag); if (old && old->cursed) return; if (old && flag == W_AMUL) return; /* no such thing as better amulets */ best = old; for (obj = mon->minvent; obj; obj = obj->nobj) { switch(flag) { case W_AMUL: if (obj->oclass != AMULET_CLASS || (obj->otyp != AMULET_OF_LIFE_SAVING && obj->otyp != AMULET_OF_REFLECTION)) continue; best = obj; goto outer_break; /* no such thing as better amulets */ case W_ARMU: if (!is_shirt(obj)) continue; break; case W_ARMC: if (!is_cloak(obj)) continue; break; case W_ARMH: if (!is_helmet(obj)) continue; /* (flimsy exception matches polyself handling) */ if (has_horns(mon->data) && !is_flimsy(obj)) continue; break; case W_ARMS: if (!is_shield(obj)) continue; break; case W_ARMG: if (!is_gloves(obj)) continue; break; case W_ARMF: if (!is_boots(obj)) continue; break; case W_ARM: if (!is_suit(obj)) continue; if (racialexception && (racial_exception(mon, obj) < 1)) continue; break; } if (obj->owornmask) continue; /* I'd like to define a VISIBLE_ARM_BONUS which doesn't assume the * monster knows obj->spe, but if I did that, a monster would keep * switching forever between two -2 caps since when it took off one * it would forget spe and once again think the object is better * than what it already has. */ if (best && (ARM_BONUS(best) + extra_pref(mon,best) >= ARM_BONUS(obj) + extra_pref(mon,obj))) continue; best = obj; } outer_break: if (!best || best == old) return; /* if wearing a cloak, account for the time spent removing and re-wearing it when putting on a suit or shirt */ if ((flag == W_ARM || flag == W_ARMU) && (mon->misc_worn_check & W_ARMC)) m_delay += 2; /* when upgrading a piece of armor, account for time spent taking off current one */ if (old) m_delay += objects[old->otyp].oc_delay; if (old) /* do this first to avoid "(being worn)" */ old->owornmask = 0L; if (!creation) { if (canseemon(mon)) { char buf[BUFSZ]; if (old) sprintf(buf, " removes %s and", distant_name(old, doname)); else buf[0] = '\0'; pline("%s%s puts on %s.", Monnam(mon), buf, distant_name(best,doname)); } /* can see it */ m_delay += objects[best->otyp].oc_delay; mon->mfrozen = m_delay; if (mon->mfrozen) mon->mcanmove = 0; } if (old) update_mon_intrinsics(mon, old, FALSE, creation); mon->misc_worn_check |= flag; best->owornmask |= flag; update_mon_intrinsics(mon, best, TRUE, creation); /* if couldn't see it but now can, or vice versa, */ if (!creation && (unseen ^ !canseemon(mon))) { if (mon->minvis && !See_invisible) { pline("Suddenly you cannot see %s.", nambuf); makeknown(best->otyp); } /* else if (!mon->minvis) pline("%s suddenly appears!", Amonnam(mon)); */ } }
static int describe_object(int x, int y, int votyp, char *buf, int known_embed, boolean *feature_described) { int num_objs = 0; struct obj *otmp; int typ; *feature_described = FALSE; if (votyp == -1) return -1; otmp = vobj_at(x, y); if (!otmp || otmp->otyp != votyp) { /* We have a mimic. */ if (votyp == STRANGE_OBJECT) { strcpy(buf, "strange object"); } else { otmp = mktemp_sobj(level, votyp); otmp->corpsenm = PM_TENGU; /* (basic object only, no random features) */ if (otmp->oclass == COIN_CLASS) otmp->quan = 1L; /* to force pluralization off */ else if (otmp->otyp == SLIME_MOLD) otmp->spe = gamestate.fruits.current;/* give the fruit a type */ strcpy(buf, distant_name(otmp, xname)); dealloc_obj(otmp); otmp = vobj_at(x, y); /* make sure we don't point to the temp obj any more */ } } else strcpy(buf, distant_name(otmp, xname)); typ = level->locations[x][y].typ; if (known_embed && IS_TREE(typ)) strcat(buf, " stuck"); else if (known_embed && (IS_ROCK(typ) || closed_door(level, x, y))) strcat(buf, " embedded"); else if (IS_TREE(typ)) { strcat(buf, " stuck in a tree"); *feature_described = TRUE; } else if (typ == STONE || typ == SCORR) { strcat(buf, " embedded in stone"); *feature_described = TRUE; } else if (IS_WALL(typ) || typ == SDOOR) { strcat(buf, " embedded in a wall"); *feature_described = TRUE; } else if (closed_door(level, x, y)) { strcat(buf, " embedded in a door"); *feature_described = TRUE; } else if (is_pool(level, x, y)) { strcat(buf, " in water"); *feature_described = TRUE; } else if (is_lava(level, x, y)) { strcat(buf, " in molten lava"); /* [can this ever happen?] */ *feature_described = TRUE; } if (!cansee(x, y)) return -1; /* don't disclose the number of objects for location out of LOS */ if (!otmp) /* There is no object here. Since the player sees one it must be a mimic */ return 1; if (otmp->otyp != votyp) /* Hero sees something other than the actual top object. Probably a mimic */ num_objs++; for (; otmp; otmp = otmp->nexthere) num_objs++; return num_objs; }
/* * 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; }
/* an object launched by someone/thing other than player attacks a monster; return 1 if the object has stopped moving (hit or its range used up) */ int ohitmon(struct monst *mtmp, /* accidental target */ struct obj *otmp, /* missile; might be destroyed by drop_throw */ int range, /* how much farther will object travel if it misses */ /* Use -1 to signify to keep going even after hit, unless it's gone (used for rolling_boulder_traps) */ boolean verbose) { /* give message(s) even when you can't see what happened */ int damage, tmp; boolean vis, ismimic; int objgone = 1; ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER; vis = cansee(bhitpos.x, bhitpos.y); tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE); if (tmp < rnd(20)) { if (!ismimic) { if (vis) miss(distant_name(otmp, mshot_xname), mtmp); else if (verbose) pline("It is missed."); } if (!range) { /* Last position; object drops */ if (is_pole(otmp)) return 1; drop_throw(otmp, 0, mtmp->mx, mtmp->my); return 1; } } else if (otmp->oclass == POTION_CLASS) { if (ismimic) seemimic(mtmp); mtmp->msleeping = 0; if (vis) otmp->dknown = 1; potionhit(mtmp, otmp, FALSE); return 1; } else { damage = dmgval(otmp, mtmp); if (otmp->otyp == ACID_VENOM && resists_acid(mtmp)) damage = 0; if (otmp->otyp == VAMPIRE_BLOOD && resists_drli(mtmp)) damage = 0; if (ismimic) seemimic(mtmp); mtmp->msleeping = 0; if (vis) hit(distant_name(otmp, mshot_xname), mtmp, exclam(damage)); else if (verbose) pline("%s is hit%s", Monnam(mtmp), exclam(damage)); if (otmp->opoisoned && is_poisonable(otmp)) { if (resists_poison(mtmp)) { if (vis) pline("The poison doesn't seem to affect %s.", mon_nam(mtmp)); } else { if (rn2(30)) { damage += rnd(6); } else { if (vis) pline("The poison was deadly..."); damage = mtmp->mhp; } } } if (objects[otmp->otyp].oc_material == SILVER && hates_silver(mtmp->data)) { if (vis) pline("The silver sears %s flesh!", s_suffix(mon_nam(mtmp))); else if (verbose) pline("Its flesh is seared!"); } if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx, mtmp->my)) { if (resists_acid(mtmp)) { if (vis || verbose) pline("%s is unaffected.", Monnam(mtmp)); damage = 0; } else { if (vis) pline("The acid burns %s!", mon_nam(mtmp)); else if (verbose) pline("It is burned!"); } } mtmp->mhp -= damage; if (mtmp->mhp < 1) { if (vis || verbose) pline("%s is %s!", Monnam(mtmp), (nonliving(mtmp->data) || !canclassifymon(mtmp)) ? "destroyed" : "killed"); /* don't blame hero for unknown rolling boulder trap */ if (!flags.mon_moving && (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped)) xkilled(mtmp, 0); else mondied(mtmp); } if (can_blnd (NULL, mtmp, (uchar) (otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP), otmp)) { if (vis && mtmp->mcansee) pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp))); mtmp->mcansee = 0; tmp = (int)mtmp->mblinded + rnd(25) + 20; if (tmp > 127) tmp = 127; mtmp->mblinded = tmp; } if (otmp->otyp == VAMPIRE_BLOOD) { if (!resists_drli(mtmp)) { int xtmp = dice(2, 6); if (vis) pline("%s suddenly seems weaker!", Monnam(mtmp)); mtmp->mhpmax -= xtmp; if ((mtmp->mhp -= xtmp) <= 0 || !mtmp->m_lev) { if (vis) pline("%s dies!", Monnam(mtmp)); xkilled(mtmp, 0); } else mtmp->m_lev--; } obfree(otmp, NULL); return 1; } if (is_pole(otmp)) return 1; objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y); if (!objgone && range == -1) { /* special case */ obj_extract_self(otmp); /* free it for motion again */ return 0; } return 1; } return 0; }