/* pet makes "I'm hungry" noises */ void beg(struct monst *mtmp) { if (mtmp->msleeping || !mtmp->mcanmove || !(carnivorous(mtmp->data) || herbivorous(mtmp->data))) return; /* presumably nearness and soundok checks have already been made */ if (!is_silent(mtmp->data) && mtmp->data->msound <= MS_ANIMAL) domonnoise(mtmp); else if (mtmp->data->msound >= MS_HUMANOID) { if (!canspotmon(level, mtmp)) map_invisible(mtmp->mx, mtmp->my); verbalize("I'm hungry."); } }
/* heal monster for time spent elsewhere */ void mon_catchup_elapsed_time(struct monst *mtmp, long nmv) { int imv = 0; /* avoid zillions of casts and lint warnings */ /* Without this, if a monster is deleted during level creation (e.g. statue trap) and the player comes to the level later, it'll have healed itself out of the "deleted" state, thoroughly confusing dmonsfree(). */ if (DEADMONSTER(mtmp)) return; if (nmv >= LARGEST_INT) /* paranoia */ imv = LARGEST_INT - 1; else imv = (int)nmv; /* might stop being afraid, blind or frozen */ /* set to 1 and allow final decrement in movemon() */ if (mtmp->mblinded) { if (imv >= (int)mtmp->mblinded) mtmp->mblinded = 1; else mtmp->mblinded -= imv; } if (mtmp->mfrozen) { if (imv >= (int)mtmp->mfrozen) mtmp->mfrozen = 1; else mtmp->mfrozen -= imv; } if (mtmp->mfleetim) { if (imv >= (int)mtmp->mfleetim) mtmp->mfleetim = 1; else mtmp->mfleetim -= imv; } /* might recover from temporary trouble */ if (mtmp->mtrapped && rn2(imv + 1) > 40 / 2) mtmp->mtrapped = 0; if (mtmp->mconf && rn2(imv + 1) > 50 / 2) mtmp->mconf = 0; if (mtmp->mstun && rn2(imv + 1) > 10 / 2) mtmp->mstun = 0; /* might finish eating or be able to use special ability again */ if (imv > mtmp->meating) mtmp->meating = 0; else mtmp->meating -= imv; if (imv > mtmp->mspec_used) mtmp->mspec_used = 0; else mtmp->mspec_used -= imv; /* reduce tameness for every 150 moves you are separated */ if (mtmp->mtame) { int wilder = (imv + 75) / 150; /* The rng_dog_untame RNG is only semi-synched, because the argument changes. This gives better results than rng_main, and we can't match exactly due to different pet-wrangling habits. Note: not msethostility; we're off-level right now. */ if (mtmp->mtame > wilder) mtmp->mtame -= wilder; /* less tame */ else if (mtmp->mtame > rn2_on_rng(wilder, rng_dog_untame)) mtmp->mtame = 0; /* untame */ else mtmp->mtame = mtmp->mpeaceful = 0; /* hostile! */ } /* check to see if it would have died as a pet; if so, go wild instead of dying the next time we call dog_move() */ if (mtmp->mtame && !mtmp->isminion && (carnivorous(mtmp->data) || herbivorous(mtmp->data))) { const struct edog *edog = CONST_EDOG(mtmp); if ((moves > edog->hungrytime + 500 && mtmp->mhp < 3) || (moves > edog->hungrytime + 750)) mtmp->mtame = mtmp->mpeaceful = 0; } if (!mtmp->mtame && mtmp->mleashed) { /* leashed monsters should always be with hero, consequently never losing any time to be accounted for later */ impossible("catching up for leashed monster?"); m_unleash(mtmp, FALSE); } /* recover lost hit points */ if (!regenerates(mtmp->data)) imv /= 20; if (mtmp->mhp + imv >= mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; else mtmp->mhp += imv; }
/* fungi will eat even tainted food */ int dogfood(const struct monst *mon, struct obj *obj) { boolean carni = carnivorous(mon->data); boolean herbi = herbivorous(mon->data); const struct permonst *fptr = &mons[obj->corpsenm]; boolean starving; if (is_quest_artifact(obj) || obj_resists(obj, 0, 95)) return obj->cursed ? TABU : APPORT; switch (obj->oclass) { case FOOD_CLASS: if (obj->otyp == CORPSE && ((touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) || is_rider(fptr))) return TABU; /* Ghouls only eat old corpses... yum! */ if (mon->data == &mons[PM_GHOUL]) return (obj->otyp == CORPSE && peek_at_iced_corpse_age(obj) + 50L <= moves) ? DOGFOOD : TABU; if (!carni && !herbi) return obj->cursed ? UNDEF : APPORT; /* a starving pet will eat almost anything */ starving = (mon->mtame && !mon->isminion && CONST_EDOG(mon)->mhpmax_penalty); switch (obj->otyp) { case TRIPE_RATION: case MEATBALL: case MEAT_RING: case MEAT_STICK: case HUGE_CHUNK_OF_MEAT: return carni ? DOGFOOD : MANFOOD; case EGG: if (touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) return POISON; return carni ? CADAVER : MANFOOD; case CORPSE: if ((peek_at_iced_corpse_age(obj) + 50L <= moves && obj->corpsenm != PM_LIZARD && obj->corpsenm != PM_LICHEN && mon->data->mlet != S_FUNGUS) || (acidic(&mons[obj->corpsenm]) && !resists_acid(mon)) || (poisonous(&mons[obj->corpsenm]) && !resists_poison(mon))) return POISON; else if (vegan(fptr)) return herbi ? CADAVER : MANFOOD; else return carni ? CADAVER : MANFOOD; case CLOVE_OF_GARLIC: return (is_undead(mon->data) ? TABU : ((herbi || starving) ? ACCFOOD : MANFOOD)); case TIN: return metallivorous(mon->data) ? ACCFOOD : MANFOOD; case APPLE: case CARROT: return herbi ? DOGFOOD : starving ? ACCFOOD : MANFOOD; case BANANA: return ((mon->data->mlet == S_YETI) ? DOGFOOD : ((herbi || starving) ? ACCFOOD : MANFOOD)); case K_RATION: case C_RATION: case CRAM_RATION: case LEMBAS_WAFER: case FOOD_RATION: if (is_human(mon->data) || is_elf(mon->data) || is_dwarf(mon->data) || is_gnome(mon->data) || is_orc(mon->data)) return ACCFOOD; default: if (starving) return ACCFOOD; return (obj->otyp > SLIME_MOLD ? (carni ? ACCFOOD : MANFOOD) : (herbi ? ACCFOOD : MANFOOD)); } default: if (obj->otyp == AMULET_OF_STRANGULATION || obj->otyp == RIN_SLOW_DIGESTION) return TABU; if (hates_silver(mon->data) && objects[obj->otyp].oc_material == SILVER) return TABU; if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj)) return ACCFOOD; if (metallivorous(mon->data) && is_metallic(obj) && (is_rustprone(obj) || mon->data != &mons[PM_RUST_MONSTER])) { /* Non-rustproofed ferrous based metals are preferred. */ return (is_rustprone(obj) && !obj->oerodeproof) ? DOGFOOD : ACCFOOD; } if (!obj->cursed && obj->oclass != BALL_CLASS && obj->oclass != CHAIN_CLASS) return APPORT; /* fall into next case */ case ROCK_CLASS: return UNDEF; } }
/* heal monster for time spent elsewhere */ void mon_catchup_elapsed_time(struct monst *mtmp, long nmv) { int imv = 0; /* avoid zillions of casts and lint warnings */ if (nmv >= LARGEST_INT) /* paranoia */ imv = LARGEST_INT - 1; else imv = (int)nmv; /* might stop being afraid, blind or frozen */ /* set to 1 and allow final decrement in movemon() */ if (mtmp->mblinded) { if (imv >= (int) mtmp->mblinded) mtmp->mblinded = 1; else mtmp->mblinded -= imv; } if (mtmp->mfrozen) { if (imv >= (int) mtmp->mfrozen) mtmp->mfrozen = 1; else mtmp->mfrozen -= imv; } if (mtmp->mfleetim) { if (imv >= (int) mtmp->mfleetim) mtmp->mfleetim = 1; else mtmp->mfleetim -= imv; } /* might recover from temporary trouble */ if (mtmp->mtrapped && rn2(imv + 1) > 40/2) mtmp->mtrapped = 0; if (mtmp->mconf && rn2(imv + 1) > 50/2) mtmp->mconf = 0; if (mtmp->mstun && rn2(imv + 1) > 10/2) mtmp->mstun = 0; /* might finish eating or be able to use special ability again */ if (imv > mtmp->meating) mtmp->meating = 0; else mtmp->meating -= imv; if (imv > mtmp->mspec_used) mtmp->mspec_used = 0; else mtmp->mspec_used -= imv; /* reduce tameness for every 150 moves you are separated */ if (mtmp->mtame) { int wilder = (imv + 75) / 150; if (mtmp->mtame > wilder) mtmp->mtame -= wilder; /* less tame */ else if (mtmp->mtame > rn2(wilder)) mtmp->mtame = 0; /* untame */ else mtmp->mtame = mtmp->mpeaceful = 0; /* hostile! */ } /* check to see if it would have died as a pet; if so, go wild instead * of dying the next time we call dog_move() */ if (mtmp->mtame && !mtmp->isminion && (carnivorous(mtmp->data) || herbivorous(mtmp->data))) { struct edog *edog = EDOG(mtmp); if ((moves > edog->hungrytime + 500 && mtmp->mhp < 3) || (moves > edog->hungrytime + 750)) mtmp->mtame = mtmp->mpeaceful = 0; } if (!mtmp->mtame && mtmp->mleashed) { /* leashed monsters should always be with hero, consequently never losing any time to be accounted for later */ impossible("catching up for leashed monster?"); m_unleash(mtmp, FALSE); } /* recover lost hit points */ if (!regenerates(mtmp->data)) imv /= 20; if (mtmp->mhp + imv >= mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; else mtmp->mhp += imv; }