/** * Monster has innate spells */ bool monster_has_innate_spells(const struct monster *mon) { bitflag innate_spells[RSF_SIZE]; create_mon_spell_mask(innate_spells, RST_INNATE, RST_NONE); rsf_inter(innate_spells, mon->race->spell_flags); return rsf_is_empty(innate_spells) ? false : true; }
/** * Monster has damaging breath */ bool monster_breathes(const struct monster *mon) { bitflag breaths[RSF_SIZE]; create_mon_spell_mask(breaths, RST_BREATH, RST_NONE); rsf_inter(breaths, mon->race->spell_flags); return rsf_is_empty(breaths) ? false : true; }
/** * Monster has frequent and good archery attacks */ bool monster_loves_archery(const struct monster *mon) { bitflag shooting[RSF_SIZE]; create_mon_spell_mask(shooting, RST_ARCHERY, RST_NONE); rsf_inter(shooting, mon->race->spell_flags); if (rsf_is_empty(shooting)) return false; return (mon->race->freq_innate < 4) ? true : false; }
/** * Use various selection criteria (set elsewhere) to restrict monster * generation. * * This function is capable of selecting monsters by: * - racial symbol (may be any of the characters allowed) * - symbol color (may be any of up to four colors). * - racial flag(s) (monster may have any allowed flag) * - breath flag(s) (monster must have exactly the flags specified) * * Uniques may be forbidden, or allowed on rare occasions. * * Some situations (like the elemental war themed level) require special * processing; this is done in helper functions called from this one. */ static bool mon_select(int r_idx) { monster_race *r_ptr = &r_info[r_idx]; bool ok = FALSE; bitflag mon_breath[RSF_SIZE]; /* Require correct breath attack */ rsf_copy(mon_breath, r_ptr->spell_flags); flags_mask(mon_breath, RSF_SIZE, RSF_BREATH_MASK, FLAG_END); /* Special case: Elemental war themed level. */ if (p_ptr->themed_level == THEME_ELEMENTAL) { return (vault_aux_elemental(r_idx)); } /* Special case: Estolad themed level. */ if (p_ptr->themed_level == THEME_ESTOLAD) { if (!(rf_has(r_ptr->flags, RF_RACIAL))) return (FALSE); } /* Require that the monster symbol be correct. */ if (d_char_req[0] != '\0') { if (strchr(d_char_req, r_ptr->d_char) == 0) return (FALSE); } /* Require correct racial type. */ if (racial_flag_mask) { if (!(rf_has(r_ptr->flags, racial_flag_mask))) return (FALSE); /* Hack -- no invisible undead until deep. */ if ((p_ptr->danger < 40) && (rf_has(r_ptr->flags, RF_UNDEAD)) && (rf_has(r_ptr->flags, RF_INVISIBLE))) return (FALSE); } /* Require that monster breaths be exactly those specified. */ /* Exception for winged dragons */ if (!rsf_is_empty(breath_flag_mask)) { /* 'd'ragons */ if (!rsf_is_equal(mon_breath, breath_flag_mask) && (r_ptr->d_char != 'D')) return (FALSE); /* Winged 'D'ragons */ if (r_ptr->d_char == 'D') { if (!rsf_is_subset(mon_breath, breath_flag_mask)) return (FALSE); /* Hack - this deals with all current Ds that need excluding */ if (rsf_has(r_ptr->flags, RSF_BRTH_SOUND)) return (FALSE); } } /* Require that the monster color be correct. */ if (d_attr_req[0]) { /* Check all allowed colors, if given. */ if ((d_attr_req[0]) && (r_ptr->d_attr == d_attr_req[0])) ok = TRUE; if ((d_attr_req[1]) && (r_ptr->d_attr == d_attr_req[1])) ok = TRUE; if ((d_attr_req[2]) && (r_ptr->d_attr == d_attr_req[2])) ok = TRUE; if ((d_attr_req[3]) && (r_ptr->d_attr == d_attr_req[3])) ok = TRUE; /* Hack -- No multihued dragons allowed in the arcane dragon pit. */ if ((strchr(d_char_req, 'd') || strchr(d_char_req, 'D')) && (d_attr_req[0] == TERM_VIOLET) && (rf_has(r_ptr->flags, RSF_BRTH_ACID) || rf_has(r_ptr->flags, RSF_BRTH_ELEC) || rf_has(r_ptr->flags, RSF_BRTH_FIRE) || rf_has(r_ptr->flags, RSF_BRTH_COLD) || rf_has(r_ptr->flags, RSF_BRTH_POIS))) { return (FALSE); } /* Doesn't match any of the given colors? Not good. */ if (!ok) return (FALSE); } /* Usually decline unique monsters. */ if (rf_has(r_ptr->flags, RF_UNIQUE)) { if (!allow_unique) return (FALSE); else if (randint0(5) != 0) return (FALSE); } /* Okay */ return (TRUE); }
/** * 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; }
/** * Monster has spells */ bool monster_has_spells(const struct monster *mon) { return rsf_is_empty(mon->race->spell_flags) ? false : true; }