/** * Determines whether the given monster successfully resists the given effect. * * If MON_TMD_FLG_NOFAIL is set in `flag`, this returns FALSE. * Then we determine if the monster resists the effect for some racial * reason. For example, the monster might have the NO_SLEEP flag, in which * case it always resists sleep. Or if it breathes chaos, it always resists * confusion. If the given monster doesn't resist for any of these reasons, * then it makes a saving throw. If MON_TMD_MON_SOURCE is set in `flag`, * indicating that another monster caused this effect, then the chance of * success on the saving throw just depends on the monster's native depth. * Otherwise, the chance of success decreases as `timer` increases. * * Also marks the lore for any appropriate resists. */ static bool mon_resist_effect(const monster_type *m_ptr, int ef_idx, int timer, u16b flag) { mon_timed_effect *effect; int resist_chance; const monster_race *r_ptr; monster_lore *l_ptr; assert(ef_idx >= 0 && ef_idx < MON_TMD_MAX); effect = &effects[ef_idx]; assert(m_ptr); r_ptr = &r_info[m_ptr->r_idx]; l_ptr = &l_list[m_ptr->r_idx]; /* Hasting never fails */ if (ef_idx == MON_TMD_FAST) return (FALSE); /* Some effects are marked to never fail */ if (flag & MON_TMD_FLG_NOFAIL) return (FALSE); /* A sleeping monster resists further sleeping */ if (ef_idx == MON_TMD_SLEEP && m_ptr->m_timed[ef_idx]) return (TRUE); /* If the monster resists innately, learn about it */ if (rf_has(r_ptr->flags, effect->flag_resist)) { if (m_ptr->ml) rf_on(l_ptr->flags, effect->flag_resist); return (TRUE); } /* Monsters with specific breaths resist stunning*/ if (ef_idx == MON_TMD_STUN && (rsf_has(r_ptr->spell_flags, RSF_BR_SOUN) || rsf_has(r_ptr->spell_flags, RSF_BR_WALL))) { /* Add the lore */ if (m_ptr->ml) { if (rsf_has(r_ptr->spell_flags, RSF_BR_SOUN)) rsf_on(l_ptr->spell_flags, RSF_BR_SOUN); if (rsf_has(r_ptr->spell_flags, RSF_BR_WALL)) rsf_on(l_ptr->spell_flags, RSF_BR_WALL); } return (TRUE); } /* Monsters with specific breaths resist confusion */ if ((ef_idx == MON_TMD_CONF) && ((rsf_has(r_ptr->spell_flags, RSF_BR_CHAO)) || (rsf_has(r_ptr->spell_flags, RSF_BR_CONF))) ) { /* Add the lore */ if (m_ptr->ml) { if (rsf_has(r_ptr->spell_flags, RSF_BR_CHAO)) rsf_on(l_ptr->spell_flags, RSF_BR_CHAO); if (rsf_has(r_ptr->spell_flags, RSF_BR_CONF)) rsf_on(l_ptr->spell_flags, RSF_BR_CONF); } return (TRUE); } /* Inertia breathers resist slowing */ if (ef_idx == MON_TMD_SLOW && rsf_has(r_ptr->spell_flags, RSF_BR_INER)) { rsf_on(l_ptr->spell_flags, RSF_BR_INER); return (TRUE); } /* Calculate the chance of the monster making its saving throw. */ if (ef_idx == MON_TMD_SLEEP) timer /= 25; /* Hack - sleep uses much bigger numbers */ if (flag & MON_TMD_MON_SOURCE) resist_chance = r_ptr->level; else resist_chance = r_ptr->level + 40 - (timer / 2); if (randint0(100) < resist_chance) return (TRUE); /* Uniques are doubly hard to affect */ if (rf_has(r_ptr->flags, RF_UNIQUE)) if (randint0(100) < resist_chance) return (TRUE); return (FALSE); }
/** * Determines whether the given monster successfully resists the given effect. * * If MON_TMD_FLG_NOFAIL is set in `flag`, this returns false. * Then we determine if the monster resists the effect for some racial * reason. For example, the monster might have the NO_SLEEP flag, in which * case it always resists sleep. Or if it breathes chaos, it always resists * confusion. If the given monster doesn't resist for any of these reasons, * then it makes a saving throw. If MON_TMD_MON_SOURCE is set in `flag`, * indicating that another monster caused this effect, then the chance of * success on the saving throw just depends on the monster's native depth. * Otherwise, the chance of success decreases as `timer` increases. * * Also marks the lore for any appropriate resists. */ static bool mon_resist_effect(const struct monster *mon, int ef_idx, int timer, u16b flag) { struct mon_timed_effect *effect; int resist_chance; struct monster_lore *lore; assert(ef_idx >= 0 && ef_idx < MON_TMD_MAX); assert(mon); effect = &effects[ef_idx]; lore = get_lore(mon->race); /* Hasting never fails */ if (ef_idx == MON_TMD_FAST) return (false); /* Some effects are marked to never fail */ if (flag & MON_TMD_FLG_NOFAIL) return (false); /* A sleeping monster resists further sleeping */ if (ef_idx == MON_TMD_SLEEP && mon->m_timed[ef_idx]) return (true); /* If the monster resists innately, learn about it */ if (rf_has(mon->race->flags, effect->flag_resist)) { if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, effect->flag_resist); return (true); } /* Monsters with specific breaths resist stunning */ if (ef_idx == MON_TMD_STUN && (rsf_has(mon->race->spell_flags, RSF_BR_SOUN) || rsf_has(mon->race->spell_flags, RSF_BR_WALL))) { /* Add the lore */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { if (rsf_has(mon->race->spell_flags, RSF_BR_SOUN)) rsf_on(lore->spell_flags, RSF_BR_SOUN); if (rsf_has(mon->race->spell_flags, RSF_BR_WALL)) rsf_on(lore->spell_flags, RSF_BR_WALL); } return (true); } /* Monsters with specific breaths resist confusion */ if ((ef_idx == MON_TMD_CONF) && rsf_has(mon->race->spell_flags, RSF_BR_CHAO)) { /* Add the lore */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) if (rsf_has(mon->race->spell_flags, RSF_BR_CHAO)) rsf_on(lore->spell_flags, RSF_BR_CHAO); return (true); } /* Inertia breathers resist slowing */ if (ef_idx == MON_TMD_SLOW && rsf_has(mon->race->spell_flags, RSF_BR_INER)){ rsf_on(lore->spell_flags, RSF_BR_INER); return (true); } /* Calculate the chance of the monster making its saving throw. */ if (ef_idx == MON_TMD_SLEEP) timer /= 25; /* Hack - sleep uses much bigger numbers */ if (flag & MON_TMD_MON_SOURCE) resist_chance = mon->race->level; else resist_chance = mon->race->level + 40 - (timer / 2); if (randint0(100) < resist_chance) return (true); /* Uniques are doubly hard to affect */ if (rf_has(mon->race->flags, RF_UNIQUE)) if (randint0(100) < resist_chance) return (true); return (false); }
/** * Creatures can cast spells, shoot missiles, and breathe. * * Returns "true" if a spell (or whatever) was (successfully) cast. * * XXX XXX XXX This function could use some work, but remember to * keep it as optimized as possible, while retaining generic code. * * Verify the various "blind-ness" checks in the code. * * XXX XXX XXX Note that several effects should really not be "seen" * if the player is blind. * * Perhaps monsters should breathe at locations *near* the player, * since this would allow them to inflict "partial" damage. * * Perhaps smart monsters should decline to use "bolt" spells if * there is a monster in the way, unless they wish to kill it. * * It will not be possible to "correctly" handle the case in which a * monster attempts to attack a location which is thought to contain * the player, but which in fact is nowhere near the player, since this * might induce all sorts of messages about the attack itself, and about * the effects of the attack, which the player might or might not be in * a position to observe. Thus, for simplicity, it is probably best to * only allow "faulty" attacks by a monster if one of the important grids * (probably the initial or final grid) is in fact in view of the player. * It may be necessary to actually prevent spell attacks except when the * monster actually has line of sight to the player. Note that a monster * could be left in a bizarre situation after the player ducked behind a * pillar and then teleported away, for example. * * Note that this function attempts to optimize the use of spells for the * cases in which the monster has no spells, or has spells but cannot use * them, or has spells but they will have no "useful" effect. Note that * this function has been an efficiency bottleneck in the past. * * Note the special "MFLAG_NICE" flag, which prevents a monster from using * any spell attacks until the player has had a single chance to move. */ bool make_attack_spell(struct monster *mon) { int chance, thrown_spell, rlev, failrate; bitflag f[RSF_SIZE]; struct monster_lore *lore = get_lore(mon->race); char m_name[80], m_poss[80], ddesc[80]; /* Player position */ int px = player->px; int py = player->py; /* Extract the blind-ness */ bool blind = (player->timed[TMD_BLIND] ? true : false); /* Extract the "see-able-ness" */ bool seen = (!blind && mflag_has(mon->mflag, MFLAG_VISIBLE)); /* Assume "normal" target */ bool normal = true; /* Cannot cast spells when confused */ if (mon->m_timed[MON_TMD_CONF]) return (false); /* Cannot cast spells when nice */ if (mflag_has(mon->mflag, MFLAG_NICE)) return false; /* Hack -- Extract the spell probability */ chance = (mon->race->freq_innate + mon->race->freq_spell) / 2; /* Not allowed to cast spells */ if (!chance) return false; /* Only do spells occasionally */ if (randint0(100) >= chance) return false; /* Hack -- require projectable player */ if (normal) { /* Check range */ if (mon->cdis > z_info->max_range) return false; /* Check path */ if (!projectable(cave, mon->fy, mon->fx, py, px, PROJECT_NONE)) return false; } /* Extract the monster level */ rlev = ((mon->race->level >= 1) ? mon->race->level : 1); /* Extract the racial spell flags */ rsf_copy(f, mon->race->spell_flags); /* Allow "desperate" spells */ if (rf_has(mon->race->flags, RF_SMART) && mon->hp < mon->maxhp / 10 && randint0(100) < 50) /* Require intelligent spells */ ignore_spells(f, RST_BOLT | RST_BALL | RST_BREATH | RST_ATTACK | RST_INNATE); /* Remove the "ineffective" spells */ remove_bad_spells(mon, f); /* Check whether summons and bolts are worth it. */ if (!rf_has(mon->race->flags, RF_STUPID)) { /* Check for a clean bolt shot */ if (test_spells(f, RST_BOLT) && !projectable(cave, mon->fy, mon->fx, py, px, PROJECT_STOP)) /* Remove spells that will only hurt friends */ ignore_spells(f, RST_BOLT); /* Check for a possible summon */ if (!(summon_possible(mon->fy, mon->fx))) /* Remove summoning spells */ ignore_spells(f, RST_SUMMON); } /* No spells left */ if (rsf_is_empty(f)) return false; /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD); /* Get the monster possessive ("his"/"her"/"its") */ monster_desc(m_poss, sizeof(m_poss), mon, MDESC_PRO_VIS | MDESC_POSS); /* Get the "died from" name */ monster_desc(ddesc, sizeof(ddesc), mon, MDESC_DIED_FROM); /* Choose a spell to cast */ thrown_spell = choose_attack_spell(f); /* Abort if no spell was chosen */ if (!thrown_spell) return false; /* If we see an unaware monster try to cast a spell, become aware of it */ if (mflag_has(mon->mflag, MFLAG_UNAWARE)) become_aware(mon); /* Calculate spell failure rate */ failrate = 25 - (rlev + 3) / 4; if (mon->m_timed[MON_TMD_FEAR]) failrate += 20; /* Stupid monsters will never fail (for jellies and such) */ if (rf_has(mon->race->flags, RF_STUPID)) failrate = 0; /* Check for spell failure (innate attacks never fail) */ if (!mon_spell_is_innate(thrown_spell) && (randint0(100) < failrate)) { /* Message */ msg("%s tries to cast a spell, but fails.", m_name); return true; } /* Cast the spell. */ disturb(player, 1); do_mon_spell(thrown_spell, mon, seen); /* Remember what the monster did to us */ if (seen) { rsf_on(lore->spell_flags, thrown_spell); /* Innate spell */ if (mon_spell_is_innate(thrown_spell)) { if (lore->cast_innate < UCHAR_MAX) lore->cast_innate++; } else { /* Bolt or Ball, or Special spell */ if (lore->cast_spell < UCHAR_MAX) lore->cast_spell++; } } /* Always take note of monsters that kill you */ if (player->is_dead && (lore->deaths < SHRT_MAX)) { lore->deaths++; } /* Record any new info */ lore_update(mon->race, lore); /* A spell was cast */ return true; }