/* Wear the best object of each type that the monster has. During creation, * the monster can put everything on at once; otherwise, wearing takes time. * This doesn't affect monster searching for objects--a monster may very well * search for objects it would not want to wear, because we don't want to * check which_armor() each round. * * We'll let monsters put on shirts and/or suits under worn cloaks, but * not shirts under worn suits. This is somewhat arbitrary, but it's * too tedious to have them remove and later replace outer garments, * and preventing suits under cloaks makes it a little bit too easy for * players to influence what gets worn. Putting on a shirt underneath * already worn body armor is too obviously buggy... */ void m_dowear(struct monst *mon, boolean creation) { #define RACE_EXCEPTION TRUE /* Note the restrictions here are the same as in dowear in do_wear.c * except for the additional restriction on intelligence. (Players * are always intelligent, even if polymorphed). */ if (verysmall(mon->data) || nohands(mon->data) || is_animal(mon->data)) return; /* give mummies a chance to wear their wrappings * and let skeletons wear their initial armor */ if (mindless(mon->data) && (!creation || (mon->data->mlet != S_MUMMY && mon->data != &mons[PM_SKELETON]))) return; m_dowear_type(mon, W_AMUL, creation, FALSE); /* can't put on shirt if already wearing suit */ if (!cantweararm(mon->data) || (mon->misc_worn_check & W_ARM)) m_dowear_type(mon, W_ARMU, creation, FALSE); /* treating small as a special case allows hobbits, gnomes, and kobolds to wear cloaks */ if (!cantweararm(mon->data) || mon->data->msize == MZ_SMALL) m_dowear_type(mon, W_ARMC, creation, FALSE); m_dowear_type(mon, W_ARMH, creation, FALSE); if (!MON_WEP(mon) || !bimanual(MON_WEP(mon))) m_dowear_type(mon, W_ARMS, creation, FALSE); m_dowear_type(mon, W_ARMG, creation, FALSE); if (!slithy(mon->data) && mon->data->mlet != S_CENTAUR) m_dowear_type(mon, W_ARMF, creation, FALSE); if (!cantweararm(mon->data)) m_dowear_type(mon, W_ARM, creation, FALSE); else m_dowear_type(mon, W_ARM, creation, RACE_EXCEPTION); }
/* release the objects the creature is carrying */ void relobj(struct monst *mtmp, int show, boolean is_pet) { /* If true, pet should keep wielded/worn items */ struct obj *otmp; int omx = mtmp->mx, omy = mtmp->my; struct obj *keepobj = 0; struct obj *wep = MON_WEP(mtmp), *hwep = attacktype(mtmp->data, AT_WEAP) ? select_hwep(mtmp) : (struct obj *)0, *proj = attacktype(mtmp->data, AT_WEAP) ? select_rwep(mtmp) : (struct obj *)0, *rwep; boolean item1 = FALSE, item2 = FALSE; rwep = attacktype(mtmp->data, AT_WEAP) ? propellor : &zeroobj; if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data)) item1 = item2 = TRUE; if (!tunnels(mtmp->data) || !needspick(mtmp->data)) item1 = TRUE; while ((otmp = mtmp->minvent) != 0) { obj_extract_self(otmp); /* special case: pick-axe and unicorn horn are non-worn */ /* items that we also want pets to keep 1 of */ /* (It is a coincidence that these can also be wielded.) */ if (otmp->owornmask || otmp == wep || otmp == hwep || otmp == rwep || otmp == proj || would_prefer_hwep(mtmp, otmp) || /* cursed item in hand? */ would_prefer_rwep(mtmp, otmp) || could_use_item(mtmp, otmp) || ((!rwep || rwep == &zeroobj) && (is_ammo(otmp) || is_launcher(otmp))) || (rwep && rwep != &zeroobj && ammo_and_launcher(otmp, rwep)) || ((!item1 && otmp->otyp == PICK_AXE) || (!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) { if (is_pet) { /* dont drop worn/wielded item */ if (otmp->otyp == PICK_AXE) item1 = TRUE; if (otmp->otyp == UNICORN_HORN && !otmp->cursed) item2 = TRUE; otmp->nobj = keepobj; keepobj = otmp; continue; } } if (otmp == wep) setmnotwielded(mtmp, otmp); mdrop_obj(mtmp, otmp, is_pet && flags.verbose); } /* put kept objects back */ while ((otmp = keepobj) != NULL) { keepobj = otmp->nobj; add_to_minv(mtmp, otmp); } if (show & cansee(omx, omy)) newsym(omx, omy); }
/* * See if a monst could use this item in an offensive or defensive capacity. */ boolean could_use_item(struct monst *mtmp, struct obj *otmp) { boolean can_use = /* make sure this is an intelligent monster */ (mtmp && !is_animal(mtmp->data) && !mindless(mtmp->data) && !nohands(mtmp->data) && otmp && /* food */ ((dogfood(mtmp, otmp) < APPORT) || /* better weapons */ (attacktype(mtmp->data, AT_WEAP) && (otmp->oclass == WEAPON_CLASS || is_weptool(otmp)) && (would_prefer_hwep(mtmp, otmp) || would_prefer_rwep(mtmp, otmp))) || /* better armor */ (otmp->oclass == ARMOR_CLASS && is_better_armor(mtmp, otmp)) || /* useful amulets */ otmp->otyp == AMULET_OF_LIFE_SAVING || otmp->otyp == AMULET_OF_REFLECTION || /* misc magic items that muse can use */ otmp->otyp == SCR_TELEPORTATION || otmp->otyp == SCR_EARTH || otmp->otyp == SCR_REMOVE_CURSE || otmp->otyp == WAN_DEATH || otmp->otyp == WAN_DIGGING || otmp->otyp == WAN_FIRE || otmp->otyp == WAN_COLD || otmp->otyp == WAN_LIGHTNING || otmp->otyp == WAN_MAGIC_MISSILE || otmp->otyp == WAN_STRIKING || otmp->otyp == WAN_TELEPORTATION || otmp->otyp == POT_HEALING || otmp->otyp == POT_EXTRA_HEALING || otmp->otyp == POT_FULL_HEALING || otmp->otyp == POT_PARALYSIS || otmp->otyp == POT_BLINDNESS || otmp->otyp == POT_CONFUSION || otmp->otyp == POT_ACID || otmp->otyp == FROST_HORN || otmp->otyp == FIRE_HORN || otmp->otyp == UNICORN_HORN)); if (can_use) { /* arbitrary - greedy monsters keep any item you can use */ if (likes_gold(mtmp->data)) return TRUE; if (otmp->oclass == ARMOR_CLASS) { return !is_better_armor(&youmonst, otmp); } else if (otmp->oclass == WAND_CLASS && otmp->spe <= 0) return FALSE; /* used charges or was cancelled? */ else { /* Check if you've got one. If you don't, don't hoard it. */ register struct obj *otmp2; for (otmp2 = invent; otmp2; otmp2 = otmp2->nobj) if (otmp->otyp == otmp2->otyp) return TRUE; } } return FALSE; }
/* release the objects the creature is carrying */ // bool is_pet /* If true, pet should keep wielded/worn items */ void relobj ( struct monst *mtmp, int show, bool is_pet) { struct obj *otmp; int omx = mtmp->mx, omy = mtmp->my; struct obj *keepobj = 0; struct obj *wep = MON_WEP(mtmp); bool item1 = false, item2 = false; if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data)) item1 = item2 = true; if (!tunnels(mtmp->data) || !needspick(mtmp->data)) item1 = true; while ((otmp = mtmp->minvent) != 0) { obj_extract_self(otmp); /* special case: pick-axe and unicorn horn are non-worn */ /* items that we also want pets to keep 1 of */ /* (It is a coincidence that these can also be wielded.) */ if (otmp->owornmask || otmp == wep || ((!item1 && otmp->otyp == PICK_AXE) || (!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) { if (is_pet) { /* dont drop worn/wielded item */ if (otmp->otyp == PICK_AXE) item1 = true; if (otmp->otyp == UNICORN_HORN && !otmp->cursed) item2 = true; otmp->nobj = keepobj; keepobj = otmp; continue; } } mdrop_obj(mtmp, otmp, is_pet && flags.verbose); } /* put kept objects back */ while ((otmp = keepobj) != (struct obj *)0) { keepobj = otmp->nobj; (void) add_to_minv(mtmp, otmp); } if (mtmp->mgold) { long g = mtmp->mgold; (void) mkgold(g, omx, omy); if (is_pet && cansee(omx, omy) && flags.verbose) { message_monster_int(MSG_M_DROPS_X_GOLD_PIECES, mtmp, g); } mtmp->mgold = 0L; } if (show & cansee(omx, omy)) newsym(omx, omy); }
/* release the objects the creature is carrying */ void relobj(struct monst *mtmp, int show, boolean is_pet) /* If true, pet should keep wielded/worn items */ { struct obj *otmp; int omx = mtmp->mx, omy = mtmp->my; struct obj *keepobj = 0; struct obj *wep = MON_WEP(mtmp); boolean item1 = FALSE, item2 = FALSE; if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data)) item1 = item2 = TRUE; if (!tunnels(mtmp->data) || !needspick(mtmp->data)) item1 = TRUE; while ((otmp = mtmp->minvent) != 0) { obj_extract_self(otmp); /* special case: pick-axe and unicorn horn are non-worn */ /* items that we also want pets to keep 1 of */ /* (It is a coincidence that these can also be wielded.) */ if (otmp->owornmask || otmp == wep || ((!item1 && otmp->otyp == PICK_AXE) || (!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) { if (is_pet) { /* dont drop worn/wielded item */ if (otmp->otyp == PICK_AXE) item1 = TRUE; if (otmp->otyp == UNICORN_HORN && !otmp->cursed) item2 = TRUE; otmp->nobj = keepobj; keepobj = otmp; continue; } } mdrop_obj(mtmp, otmp, is_pet && flags.verbose); } /* put kept objects back */ while ((otmp = keepobj) != NULL) { keepobj = otmp->nobj; add_to_minv(mtmp, otmp); } if (show & cansee(omx, omy)) newsym(omx, omy); }
static struct obj * DROPPABLES(struct monst *mon) { struct obj *obj; struct obj *wep = MON_WEP(mon), *hwep = attacktype(mon->data, AT_WEAP) ? select_hwep(mon) : (struct obj *)0, *proj = attacktype(mon->data, AT_WEAP) ? select_rwep(mon) : (struct obj *)0, *rwep; boolean item1 = FALSE, item2 = FALSE; rwep = attacktype(mon->data, AT_WEAP) ? propellor : &zeroobj; if (is_animal(mon->data) || mindless(mon->data)) item1 = item2 = TRUE; if (!tunnels(mon->data) || !needspick(mon->data)) item1 = TRUE; for (obj = mon->minvent; obj; obj = obj->nobj) { if (!item1 && is_pick(obj) && (obj->otyp != DWARVISH_MATTOCK || !which_armor(mon, W_ARMS))) { item1 = TRUE; continue; } if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) { item2 = TRUE; continue; } if (!obj->owornmask && obj != wep && obj != rwep && obj != proj && obj != hwep && !would_prefer_hwep(mon, obj) /* cursed item in hand? */ && !would_prefer_rwep(mon, obj) && ((rwep != &zeroobj) || (!is_ammo(obj) && !is_launcher(obj))) && (rwep == &zeroobj || !ammo_and_launcher(obj, rwep)) && !could_use_item(mon, obj)) return obj; } return NULL; }
/* Returns the bitwise OR of all MSENSE_ values that explain how "viewer" can see "viewee". &youmonst is accepted as either argument. If both arguments are the same, this tests if/how a monster/player can detect itself. */ unsigned msensem(const struct monst *viewer, const struct monst *viewee) { unsigned sensemethod = 0; /* sanity checks, so the caller doesn't have to */ if (viewer != &youmonst) if (!onmap(viewer) || DEADMONSTER(viewer)) return 0; if (viewee != &youmonst) if (!onmap(viewee) || DEADMONSTER(viewee)) return 0; if (!level) { impossible("vision calculations during level creation"); return 0; } /* TODO: once levels rewrite is done, this code can be simplified (and won't work in its present form). */ d_level *sz = m_mz(viewer), *tz = m_mz(viewee); if (sz->dnum != tz->dnum || sz->dlevel != tz->dlevel) return 0; struct level *lev = level; if (viewer != &youmonst) lev = viewer->dlevel; int sx = m_mx(viewer), sy = m_my(viewer), tx = m_mx(viewee), ty = m_my(viewee); int distance = dist2(sx, sy, tx, ty); /* Special case: if either endpoint is an engulfing monster, then we want LOE to the square specifically, ignoring players on that square (because the edge of an engulfing monster blocks LOE to the player). */ char **msensem_vizarray = (Engulfed && (viewer == u.ustuck || viewee == u.ustuck)) ? NULL : viz_array; /* Line of effect. clear_path is like couldsee(), but doesn't require the player to be at either endpoint. (If the player happens to be at one of the endpoints, it just calls couldsee() directly.) */ boolean loe = clear_path(sx, sy, tx, ty, msensem_vizarray); /* A special case for many vision methods: water or the ground blocking vision. A hiding target is also included in these, because hiding is often a case of using an object, part of the floor, a cranny in the ceiling, etc., to block vision (and even when it isn't, it should block vision in the same cases). */ boolean vertical_loe = !(m_mburied(viewer) || m_mburied(viewee) || ((!!m_underwater(viewee)) ^ (!!m_underwater(viewer))) || m_mundetected(viewee)); boolean invisible = !!m_has_property(viewee, INVIS, ANY_PROPERTY, 0); /* For normal vision, one necessary condition is that the target must be adjacent or on a lit square (we assume there's just enough light in the dungeon that even in dark areas, adjacent squares are visible to normal vision). We test "lit" by testing that the square is either temporarily lit, or permanently lit. (We can't use the normal cansee() check because that doesn't work for squares outside the player's LOE, and it's possible that neither the viewer nor the viewee is the player.) TODO: templit off-level. That's very hard to implement because we don't run lighting calculations off-level. */ boolean target_lit = distance <= 2 || (lev == level && templit(tx, ty)) || lev->locations[tx][ty].lit; /* TODO: Maybe infravision (and perhaps even infravisibility) should be properties? */ boolean infravision_ok = infravision(viewer->data) && infravisible(viewee->data); boolean blinded = !!m_has_property(viewer, BLINDED, ANY_PROPERTY, 0); boolean see_invisible = !!m_has_property(viewer, SEE_INVIS, ANY_PROPERTY, 0); if (loe && vertical_loe && !blinded) { if (!invisible && target_lit) sensemethod |= MSENSE_VISION; if (!invisible && infravision_ok) sensemethod |= MSENSE_INFRAVISION; if (invisible && (target_lit || infravision_ok) && see_invisible) sensemethod |= MSENSE_SEEINVIS | MSENSEF_KNOWNINVIS; } /* Telepathy. The viewee needs a mind; the viewer needs either to be blind, or for the telepathy to be extrinsic and the viewer within BOLT_LIM. */ if (!mindless(viewee->data) && !m_helpless(viewer, hm_unconscious)) { unsigned telepathy_reason = m_has_property(viewer, TELEPAT, ANY_PROPERTY, 0); if ((telepathy_reason && blinded) || (telepathy_reason & (W_EQUIP | W_ARTIFACT) && distance <= BOLT_LIM * BOLT_LIM)) sensemethod |= MSENSE_TELEPATHY; } /* Astral vision. Like regular vision, but has a distance check rather than an LOE check. It's unclear whether this pierces blindness, because the only item that gives astral vision also gives blindness immunity; this code assumes not. */ boolean xray = m_has_property(viewer, XRAY_VISION, ANY_PROPERTY, 0) && (!invisible || see_invisible); if (vertical_loe && distance <= XRAY_RANGE * XRAY_RANGE && xray && (target_lit || infravision_ok)) { sensemethod |= MSENSE_XRAY; if (invisible && see_invisible) sensemethod |= MSENSEF_KNOWNINVIS; } /* Ideally scent should work around corners, but not through walls. That's awkward to write, though, because it'd require pathfinding. */ if (vertical_loe && loe && distance <= 5 && has_scent(viewer->data)) sensemethod |= MSENSE_SCENT; /* Monster detection. All that is needed (apart from same-level, which was checked earlier) is the property itself. */ if (m_has_property(viewer, DETECT_MONSTERS, ANY_PROPERTY, 0)) sensemethod |= MSENSE_MONDETECT; /* Warning versus monster class. (Actually implemented as monster /race/.) */ if (mworn_warntype(viewer) & viewee->data->mflags2) sensemethod |= MSENSE_WARNOFMON; /* Covetous sense. Note that the player can benefit from this too, e.g. a player in master lich form will be able to detect the Wizard of Yendor holding the Book of the Dead. */ if (covetous_sense(viewer, viewee)) sensemethod |= MSENSE_COVETOUS; /* Smell of gold, approximating 3.4.3 behaviour (which was previously in set_apparxy in monmove.c). Xorns can sense any monster with gold in their inventory. */ if (viewer->data == &mons[PM_XORN] && money_cnt(m_minvent(viewee))) sensemethod |= MSENSE_GOLDSMELL; /* Warning. This partial-senses monsters that are hostile to the viewer, and have a level of 4 or greater, and a distance of 100 or less. */ if (distance <= 100 && m_mlev(viewee) >= 4 && m_has_property(viewer, WARNING, ANY_PROPERTY, 0) && mm_aggression(viewee, viewer) & ALLOW_M) sensemethod |= MSENSE_WARNING; /* Deducing the existence of a long worm via seeing a segment. Based on the code that was formerly worm_known in worm.c, but expanded to handle monster viewing. Note: assumes that normal vision, possibly modified by astral vision and see invisible, is the only way to see a long worm tail. Infravision doesn't work (they're cold-blooded), and currently no other types of vision are implemented. Detection would find the head. */ if (viewee->wormno && (!invisible || see_invisible) && vertical_loe && !blinded) { struct wseg *curr = viewee->dlevel->wtails[viewee->wormno]; while (curr) { boolean seg_dist = dist2(sx, sy, curr->wx, curr->wy); boolean seg_loe = clear_path(sx, sy, curr->wx, curr->wy, msensem_vizarray) || (xray && seg_dist <= XRAY_RANGE * XRAY_RANGE); boolean seg_lit = seg_dist <= 2 || (lev == level && templit(curr->wx, curr->wy)) || lev->locations[curr->wx][curr->wy].lit; if (seg_loe && seg_lit) sensemethod |= MSENSE_WORM; curr = curr->nseg; } } /* Calculate known invisibility, because we have all the information to hand, and it's a complex calculation without it. We need to be able to see the monster's location with normal vision, but not the monster itself. Also don't include warning in this (because then, we can't match the monster to the message). */ if (loe && vertical_loe && !blinded && sensemethod && target_lit && !(sensemethod & (MSENSE_ANYVISION | MSENSE_WARNING))) sensemethod |= MSENSEF_KNOWNINVIS; /* If the target is in item form, it's not being seen properly. Any vision-style detection of the target is going to not see it as a monster. */ if (m_helpless(viewee, 1 << hr_mimicking) && (lev != level || !Protection_from_shape_changers) && (sensemethod & MSENSE_ANYVISION)) { sensemethod &= ~MSENSE_ANYVISION; sensemethod |= MSENSE_ITEMMIMIC; } return sensemethod; }