1 #include "monster/monster-util.h"
2 #include "dungeon/dungeon-flag-types.h"
3 #include "dungeon/quest.h"
4 #include "floor/wild.h"
5 #include "game-option/cheat-options.h"
6 #include "monster-race/monster-race-hook.h"
7 #include "monster-race/monster-race.h"
8 #include "monster-race/race-ability-mask.h"
9 #include "monster-race/race-flags-resistance.h"
10 #include "monster-race/race-flags1.h"
11 #include "monster-race/race-flags7.h"
12 #include "monster-race/race-indice-types.h"
13 #include "monster-race/race-misc-flags.h"
14 #include "spell/summon-types.h"
15 #include "system/alloc-entries.h"
16 #include "system/angband-system.h"
17 #include "system/dungeon-info.h"
18 #include "system/floor-type-definition.h"
19 #include "system/grid-type-definition.h"
20 #include "system/monster-race-info.h"
21 #include "system/player-type-definition.h"
22 #include "system/terrain-type-definition.h"
23 #include "util/bit-flags-calculator.h"
24 #include "view/display-messages.h"
28 enum dungeon_mode_type {
30 DUNGEON_MODE_NAND = 2,
35 MONSTER_IDX hack_m_idx = 0; /* Hack -- see "process_monsters()" */
36 MONSTER_IDX hack_m_idx_ii = 0;
39 * @var chameleon_change_m_idx
40 * @brief カメレオンの変身先モンスターIDを受け渡すためのグローバル変数
41 * @todo 変数渡しの問題などもあるができればchameleon_change_m_idxのグローバル変数を除去し、関数引き渡しに移行すること
43 int chameleon_change_m_idx = 0;
46 * @var summon_specific_type
47 * @brief 召喚条件を指定するグローバル変数 / Hack -- the "type" of the current "summon specific"
48 * @todo summon_specific_typeグローバル変数の除去と関数引数への代替を行う
50 summon_type summon_specific_type = SUMMON_NONE;
53 * @brief モンスターがダンジョンに出現できる条件を満たしているかのフラグ判定関数(AND)
55 * @param r_flags モンスター側のフラグ
56 * @param d_flags ダンジョン側の判定フラグ
60 static bool is_possible_monster_and(const EnumClassFlagGroup<T> &r_flags, const EnumClassFlagGroup<T> &d_flags)
62 return r_flags.has_all_of(d_flags);
66 * @brief モンスターがダンジョンに出現できる条件を満たしているかのフラグ判定関数(OR)
68 * @param r_flags モンスター側のフラグ
69 * @param d_flags ダンジョン側の判定フラグ
73 static bool is_possible_monster_or(const EnumClassFlagGroup<T> &r_flags, const EnumClassFlagGroup<T> &d_flags)
75 return r_flags.has_any_of(d_flags);
79 * @brief 指定されたモンスター種族がダンジョンの制限にかかるかどうかをチェックする / Some dungeon types restrict the possible monsters.
80 * @param floor_ptr フロアへの参照ポインタ
81 * @param r_idx チェックするモンスター種族ID
82 * @return 召喚条件が一致するならtrue / Return TRUE is the monster is OK and FALSE otherwise
84 static bool restrict_monster_to_dungeon(const FloorType *floor_ptr, MonsterRaceId r_idx)
86 const auto *d_ptr = &floor_ptr->get_dungeon_definition();
87 const auto *r_ptr = &monraces_info[r_idx];
88 if (d_ptr->flags.has(DungeonFeatureType::CHAMELEON)) {
89 if (chameleon_change_m_idx) {
94 if (d_ptr->flags.has(DungeonFeatureType::NO_MAGIC)) {
95 if (r_idx != MonsterRaceId::CHAMELEON && r_ptr->freq_spell && r_ptr->ability_flags.has_none_of(RF_ABILITY_NOMAGIC_MASK)) {
100 if (d_ptr->flags.has(DungeonFeatureType::NO_MELEE)) {
101 if (r_idx == MonsterRaceId::CHAMELEON) {
104 if (r_ptr->ability_flags.has_none_of(RF_ABILITY_BOLT_MASK | RF_ABILITY_BEAM_MASK | RF_ABILITY_BALL_MASK) && r_ptr->ability_flags.has_none_of(
105 { MonsterAbilityType::CAUSE_1, MonsterAbilityType::CAUSE_2, MonsterAbilityType::CAUSE_3, MonsterAbilityType::CAUSE_4, MonsterAbilityType::MIND_BLAST, MonsterAbilityType::BRAIN_SMASH })) {
110 if (d_ptr->flags.has(DungeonFeatureType::BEGINNER)) {
111 if (r_ptr->level > floor_ptr->dun_level) {
116 if (d_ptr->special_div >= 64) {
119 if (summon_specific_type && d_ptr->flags.has_not(DungeonFeatureType::CHAMELEON)) {
123 switch (d_ptr->mode) {
124 case DUNGEON_MODE_AND:
125 case DUNGEON_MODE_NAND: {
126 std::vector<bool> is_possible = {
127 all_bits(r_ptr->flags1, d_ptr->mflags1),
128 all_bits(r_ptr->flags2, d_ptr->mflags2),
129 all_bits(r_ptr->flags3, d_ptr->mflags3),
130 all_bits(r_ptr->flags7, d_ptr->mflags7),
131 all_bits(r_ptr->flags8, d_ptr->mflags8),
132 is_possible_monster_and(r_ptr->ability_flags, d_ptr->mon_ability_flags),
133 is_possible_monster_and(r_ptr->behavior_flags, d_ptr->mon_behavior_flags),
134 is_possible_monster_and(r_ptr->resistance_flags, d_ptr->mon_resistance_flags),
135 is_possible_monster_and(r_ptr->drop_flags, d_ptr->mon_drop_flags),
136 is_possible_monster_and(r_ptr->kind_flags, d_ptr->mon_kind_flags),
137 is_possible_monster_and(r_ptr->wilderness_flags, d_ptr->mon_wilderness_flags),
138 is_possible_monster_and(r_ptr->feature_flags, d_ptr->mon_feature_flags),
139 is_possible_monster_and(r_ptr->population_flags, d_ptr->mon_population_flags),
140 is_possible_monster_and(r_ptr->speak_flags, d_ptr->mon_speak_flags),
141 is_possible_monster_and(r_ptr->brightness_flags, d_ptr->mon_brightness_flags),
142 is_male(d_ptr->mon_sex) ? is_male(r_ptr->sex) : true,
143 is_female(d_ptr->mon_sex) ? is_female(r_ptr->sex) : true,
146 auto result = std::all_of(is_possible.begin(), is_possible.end(), [](const auto &v) { return v; });
147 result &= std::all_of(d_ptr->r_chars.begin(), d_ptr->r_chars.end(), [r_ptr](const auto &v) { return v == r_ptr->d_char; });
149 return d_ptr->mode == DUNGEON_MODE_AND ? result : !result;
151 case DUNGEON_MODE_OR:
152 case DUNGEON_MODE_NOR: {
153 std::vector<bool> is_possible = {
154 any_bits(r_ptr->flags1, d_ptr->mflags1),
155 any_bits(r_ptr->flags2, d_ptr->mflags2),
156 any_bits(r_ptr->flags3, d_ptr->mflags3),
157 any_bits(r_ptr->flags7, d_ptr->mflags7),
158 any_bits(r_ptr->flags8, d_ptr->mflags8),
159 is_possible_monster_or(r_ptr->ability_flags, d_ptr->mon_ability_flags),
160 is_possible_monster_or(r_ptr->behavior_flags, d_ptr->mon_behavior_flags),
161 is_possible_monster_or(r_ptr->resistance_flags, d_ptr->mon_resistance_flags),
162 is_possible_monster_or(r_ptr->drop_flags, d_ptr->mon_drop_flags),
163 is_possible_monster_or(r_ptr->kind_flags, d_ptr->mon_kind_flags),
164 is_possible_monster_or(r_ptr->wilderness_flags, d_ptr->mon_wilderness_flags),
165 is_possible_monster_or(r_ptr->feature_flags, d_ptr->mon_feature_flags),
166 is_possible_monster_or(r_ptr->population_flags, d_ptr->mon_population_flags),
167 is_possible_monster_or(r_ptr->speak_flags, d_ptr->mon_speak_flags),
168 is_possible_monster_or(r_ptr->brightness_flags, d_ptr->mon_brightness_flags),
169 is_male(d_ptr->mon_sex) ? is_male(r_ptr->sex) : true,
170 is_female(d_ptr->mon_sex) ? is_female(r_ptr->sex) : true,
173 auto result = std::any_of(is_possible.begin(), is_possible.end(), [](const auto &v) { return v; });
174 result |= std::any_of(d_ptr->r_chars.begin(), d_ptr->r_chars.end(), [r_ptr](const auto &v) { return v == r_ptr->d_char; });
176 return d_ptr->mode == DUNGEON_MODE_OR ? result : !result;
184 * @brief プレイヤーの現在の広域マップ座標から得た地勢を元にモンスターの生成条件関数を返す
185 * @param player_ptr プレイヤーへの参照ポインタ
186 * @return 地勢にあったモンスターの生成条件関数
188 monsterrace_hook_type get_monster_hook(PlayerType *player_ptr)
190 const auto &floor = *player_ptr->current_floor_ptr;
191 if ((floor.dun_level > 0) || (floor.is_in_quest())) {
192 return (monsterrace_hook_type)mon_hook_dungeon;
195 switch (wilderness[player_ptr->wilderness_y][player_ptr->wilderness_x].terrain) {
197 return (monsterrace_hook_type)mon_hook_town;
198 case TERRAIN_DEEP_WATER:
199 return (monsterrace_hook_type)mon_hook_ocean;
200 case TERRAIN_SHALLOW_WATER:
202 return (monsterrace_hook_type)mon_hook_shore;
205 return (monsterrace_hook_type)mon_hook_waste;
207 return (monsterrace_hook_type)mon_hook_grass;
209 return (monsterrace_hook_type)mon_hook_wood;
210 case TERRAIN_SHALLOW_LAVA:
211 case TERRAIN_DEEP_LAVA:
212 return (monsterrace_hook_type)mon_hook_volcano;
213 case TERRAIN_MOUNTAIN:
214 return (monsterrace_hook_type)mon_hook_mountain;
216 return (monsterrace_hook_type)mon_hook_dungeon;
221 * @brief 指定された広域マップ座標の地勢を元にモンスターの生成条件関数を返す
222 * @return 地勢にあったモンスターの生成条件関数
224 monsterrace_hook_type get_monster_hook2(PlayerType *player_ptr, POSITION y, POSITION x)
226 const Pos2D pos(y, x);
227 const auto &terrain = player_ptr->current_floor_ptr->get_grid(pos).get_terrain();
228 if (terrain.flags.has(TerrainCharacteristics::WATER)) {
229 return terrain.flags.has(TerrainCharacteristics::DEEP) ? (monsterrace_hook_type)mon_hook_deep_water : (monsterrace_hook_type)mon_hook_shallow_water;
232 if (terrain.flags.has(TerrainCharacteristics::LAVA)) {
233 return (monsterrace_hook_type)mon_hook_lava;
236 return (monsterrace_hook_type)mon_hook_floor;
240 * @brief モンスター生成テーブルの重みを指定条件に従って変更する。
242 * @param hook1 生成制約関数1 (nullptr の場合、制約なし)
243 * @param hook2 生成制約関数2 (nullptr の場合、制約なし)
244 * @param restrict_to_dungeon 現在プレイヤーのいるダンジョンの制約を適用するか
247 * モンスター生成テーブル alloc_race_table の各要素の基本重み prob1 を指定条件
248 * に従って変更し、結果を prob2 に書き込む。
250 static errr do_get_mon_num_prep(PlayerType *player_ptr, const monsterrace_hook_type hook1, const monsterrace_hook_type hook2, const bool restrict_to_dungeon)
252 const FloorType *const floor_ptr = player_ptr->current_floor_ptr;
255 int mon_num = 0; // 重み(prob2)が正の要素数
256 DEPTH lev_min = MAX_DEPTH; // 重みが正の要素のうち最小階
257 DEPTH lev_max = 0; // 重みが正の要素のうち最大階
258 int prob2_total = 0; // 重みの総和
260 // モンスター生成テーブルの各要素について重みを修正する。
261 const auto &system = AngbandSystem::get_instance();
262 for (auto i = 0U; i < alloc_race_table.size(); i++) {
263 alloc_entry *const entry = &alloc_race_table[i];
264 const auto entry_r_idx = i2enum<MonsterRaceId>(entry->index);
265 const MonsterRaceInfo *const r_ptr = &monraces_info[entry_r_idx];
267 // 生成を禁止する要素は重み 0 とする。
271 // テーブル内の無効エントリもこれに該当する(alloc_race_table は生成時にゼロクリアされるため)。
272 if (entry->prob1 <= 0) {
276 // いずれかの生成制約関数が偽を返したら生成禁止。
277 if ((hook1 && !hook1(player_ptr, entry_r_idx)) || (hook2 && !hook2(player_ptr, entry_r_idx))) {
281 // 原則生成禁止するものたち(フェイズアウト状態 / カメレオンの変身先 / ダンジョンの主召喚 は例外)。
282 if (!system.is_phase_out() && !chameleon_change_m_idx && summon_specific_type != SUMMON_GUARDIANS) {
284 if (r_ptr->misc_flags.has(MonsterMiscType::QUESTOR)) {
289 if (r_ptr->misc_flags.has(MonsterMiscType::GUARDIAN)) {
293 // FORCE_DEPTH フラグ持ちは指定階未満では生成禁止。
294 if (r_ptr->misc_flags.has(MonsterMiscType::FORCE_DEPTH) && (r_ptr->level > floor_ptr->dun_level)) {
298 // クエスト内でRES_ALL及び指定階未満でのDIMINISH_MAX_DAMAGEの生成を禁止する (殲滅系クエストの詰み防止)
299 if (player_ptr->current_floor_ptr->is_in_quest()) {
300 auto is_indefeatable = r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL);
301 is_indefeatable |= r_ptr->special_flags.has(MonsterSpecialType::DIMINISH_MAX_DAMAGE) && r_ptr->level > floor_ptr->dun_level;
302 if (is_indefeatable) {
308 // 生成を許可するものは基本重みをそのまま引き継ぐ。
309 entry->prob2 = entry->prob1;
311 // 引数で指定されていればさらにダンジョンによる制約を試みる。
312 if (restrict_to_dungeon) {
313 // ダンジョンによる制約を適用する条件:
318 const bool in_random_quest = floor_ptr->is_in_quest() && !QuestType::is_fixed(floor_ptr->quest_number);
319 const bool cond = !system.is_phase_out() && floor_ptr->dun_level > 0 && !in_random_quest;
321 if (cond && !restrict_monster_to_dungeon(floor_ptr, entry_r_idx)) {
322 // ダンジョンによる制約に掛かった場合、重みを special_div/64 倍する。
324 const int numer = entry->prob2 * floor_ptr->get_dungeon_definition().special_div;
325 const int q = numer / 64;
326 const int r = numer % 64;
327 entry->prob2 = (PROB)(randint0(64) < r ? q + 1 : q);
332 if (entry->prob2 > 0) {
334 if (lev_min > entry->level) {
335 lev_min = entry->level;
337 if (lev_max < entry->level) {
338 lev_max = entry->level;
340 prob2_total += entry->prob2;
344 // チートオプションが有効なら統計情報を出力。
346 msg_format(_("モンスター第2次候補数:%d(%d-%dF)%d ", "monster second selection:%d(%d-%dF)%d "), mon_num, lev_min, lev_max, prob2_total);
353 * @brief モンスター生成テーブルの重み修正
355 * @param hook1 生成制約関数1 (nullptr の場合、制約なし)
356 * @param hook2 生成制約関数2 (nullptr の場合、制約なし)
359 * get_mon_num() を呼ぶ前に get_mon_num_prep() 系関数のいずれかを呼ぶこと。
361 errr get_mon_num_prep(PlayerType *player_ptr, const monsterrace_hook_type hook1, const monsterrace_hook_type hook2)
363 return do_get_mon_num_prep(player_ptr, hook1, hook2, true);
367 * @brief モンスター生成テーブルの重み修正(賞金首選定用)
370 * get_mon_num() を呼ぶ前に get_mon_num_prep 系関数のいずれかを呼ぶこと。
372 errr get_mon_num_prep_bounty(PlayerType *player_ptr)
374 return do_get_mon_num_prep(player_ptr, nullptr, nullptr, false);