OSDN Git Service

290bd8026ad927d9fa3d483f3a2fb82e5b4ddf83
[hengbandforosx/hengbandosx.git] / src / monster / monster-util.cpp
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 "spell/summon-types.h"
14 #include "system/alloc-entries.h"
15 #include "system/dungeon-info.h"
16 #include "system/floor-type-definition.h"
17 #include "system/grid-type-definition.h"
18 #include "system/monster-race-definition.h"
19 #include "system/player-type-definition.h"
20 #include "system/terrain-type-definition.h"
21 #include "util/bit-flags-calculator.h"
22 #include "view/display-messages.h"
23 #include <algorithm>
24 #include <iterator>
25
26 enum dungeon_mode_type {
27     DUNGEON_MODE_AND = 1,
28     DUNGEON_MODE_NAND = 2,
29     DUNGEON_MODE_OR = 3,
30     DUNGEON_MODE_NOR = 4,
31 };
32
33 MONSTER_IDX hack_m_idx = 0; /* Hack -- see "process_monsters()" */
34 MONSTER_IDX hack_m_idx_ii = 0;
35
36 /*!
37  * @var chameleon_change_m_idx
38  * @brief カメレオンの変身先モンスターIDを受け渡すためのグローバル変数
39  * @todo 変数渡しの問題などもあるができればchameleon_change_m_idxのグローバル変数を除去し、関数引き渡しに移行すること
40  */
41 int chameleon_change_m_idx = 0;
42
43 /*!
44  * @var summon_specific_type
45  * @brief 召喚条件を指定するグローバル変数 / Hack -- the "type" of the current "summon specific"
46  * @todo summon_specific_typeグローバル変数の除去と関数引数への代替を行う
47  */
48 summon_type summon_specific_type = SUMMON_NONE;
49
50 /**
51  * @brief モンスターがダンジョンに出現できる条件を満たしているかのフラグ判定関数(AND)
52  *
53  * @param r_flags モンスター側のフラグ
54  * @param d_flags ダンジョン側の判定フラグ
55  * @return 出現可能かどうか
56  */
57 template <class T>
58 static bool is_possible_monster_and(const EnumClassFlagGroup<T> &r_flags, const EnumClassFlagGroup<T> &d_flags)
59 {
60     return r_flags.has_all_of(d_flags);
61 }
62
63 /**
64  * @brief モンスターがダンジョンに出現できる条件を満たしているかのフラグ判定関数(OR)
65  *
66  * @param r_flags モンスター側のフラグ
67  * @param d_flags ダンジョン側の判定フラグ
68  * @return 出現可能かどうか
69  */
70 template <class T>
71 static bool is_possible_monster_or(const EnumClassFlagGroup<T> &r_flags, const EnumClassFlagGroup<T> &d_flags)
72 {
73     return r_flags.has_any_of(d_flags);
74 }
75
76 /*!
77  * @brief 指定されたモンスター種族がダンジョンの制限にかかるかどうかをチェックする / Some dungeon types restrict the possible monsters.
78  * @param player_ptr プレイヤーへの参照ポインタ
79  * @param r_idx チェックするモンスター種族ID
80  * @return 召喚条件が一致するならtrue / Return TRUE is the monster is OK and FALSE otherwise
81  */
82 static bool restrict_monster_to_dungeon(PlayerType *player_ptr, MonsterRaceId r_idx)
83 {
84     const auto *d_ptr = &dungeons_info[player_ptr->dungeon_idx];
85     const auto *r_ptr = &monraces_info[r_idx];
86     if (d_ptr->flags.has(DungeonFeatureType::CHAMELEON)) {
87         if (chameleon_change_m_idx) {
88             return true;
89         }
90     }
91
92     if (d_ptr->flags.has(DungeonFeatureType::NO_MAGIC)) {
93         if (r_idx != MonsterRaceId::CHAMELEON && r_ptr->freq_spell && r_ptr->ability_flags.has_none_of(RF_ABILITY_NOMAGIC_MASK)) {
94             return false;
95         }
96     }
97
98     if (d_ptr->flags.has(DungeonFeatureType::NO_MELEE)) {
99         if (r_idx == MonsterRaceId::CHAMELEON) {
100             return true;
101         }
102         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(
103                                                                                                                         { MonsterAbilityType::CAUSE_1, MonsterAbilityType::CAUSE_2, MonsterAbilityType::CAUSE_3, MonsterAbilityType::CAUSE_4, MonsterAbilityType::MIND_BLAST, MonsterAbilityType::BRAIN_SMASH })) {
104             return false;
105         }
106     }
107
108     auto *floor_ptr = player_ptr->current_floor_ptr;
109     if (d_ptr->flags.has(DungeonFeatureType::BEGINNER)) {
110         if (r_ptr->level > floor_ptr->dun_level) {
111             return false;
112         }
113     }
114
115     if (d_ptr->special_div >= 64) {
116         return true;
117     }
118     if (summon_specific_type && d_ptr->flags.has_not(DungeonFeatureType::CHAMELEON)) {
119         return true;
120     }
121
122     switch (d_ptr->mode) {
123     case DUNGEON_MODE_AND:
124     case DUNGEON_MODE_NAND: {
125         std::vector<bool> is_possible = {
126             all_bits(r_ptr->flags1, d_ptr->mflags1),
127             all_bits(r_ptr->flags2, d_ptr->mflags2),
128             all_bits(r_ptr->flags3, d_ptr->mflags3),
129             all_bits(r_ptr->flags7, d_ptr->mflags7),
130             all_bits(r_ptr->flags8, d_ptr->mflags8),
131             is_possible_monster_and(r_ptr->ability_flags, d_ptr->mon_ability_flags),
132             is_possible_monster_and(r_ptr->behavior_flags, d_ptr->mon_behavior_flags),
133             is_possible_monster_and(r_ptr->resistance_flags, d_ptr->mon_resistance_flags),
134             is_possible_monster_and(r_ptr->drop_flags, d_ptr->mon_drop_flags),
135             is_possible_monster_and(r_ptr->kind_flags, d_ptr->mon_kind_flags),
136             is_possible_monster_and(r_ptr->wilderness_flags, d_ptr->mon_wilderness_flags),
137             is_possible_monster_and(r_ptr->feature_flags, d_ptr->mon_feature_flags),
138             is_possible_monster_and(r_ptr->population_flags, d_ptr->mon_population_flags),
139             is_possible_monster_and(r_ptr->speak_flags, d_ptr->mon_speak_flags),
140         };
141
142         auto result = std::all_of(is_possible.begin(), is_possible.end(), [](const auto &v) { return v; });
143         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; });
144
145         return d_ptr->mode == DUNGEON_MODE_AND ? result : !result;
146     }
147     case DUNGEON_MODE_OR:
148     case DUNGEON_MODE_NOR: {
149         std::vector<bool> is_possible = {
150             any_bits(r_ptr->flags1, d_ptr->mflags1),
151             any_bits(r_ptr->flags2, d_ptr->mflags2),
152             any_bits(r_ptr->flags3, d_ptr->mflags3),
153             any_bits(r_ptr->flags7, d_ptr->mflags7),
154             any_bits(r_ptr->flags8, d_ptr->mflags8),
155             is_possible_monster_or(r_ptr->ability_flags, d_ptr->mon_ability_flags),
156             is_possible_monster_or(r_ptr->behavior_flags, d_ptr->mon_behavior_flags),
157             is_possible_monster_or(r_ptr->resistance_flags, d_ptr->mon_resistance_flags),
158             is_possible_monster_or(r_ptr->drop_flags, d_ptr->mon_drop_flags),
159             is_possible_monster_or(r_ptr->kind_flags, d_ptr->mon_kind_flags),
160             is_possible_monster_or(r_ptr->wilderness_flags, d_ptr->mon_wilderness_flags),
161             is_possible_monster_or(r_ptr->feature_flags, d_ptr->mon_feature_flags),
162             is_possible_monster_or(r_ptr->population_flags, d_ptr->mon_population_flags),
163             is_possible_monster_or(r_ptr->speak_flags, d_ptr->mon_speak_flags),
164         };
165
166         auto result = std::any_of(is_possible.begin(), is_possible.end(), [](const auto &v) { return v; });
167         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; });
168
169         return d_ptr->mode == DUNGEON_MODE_OR ? result : !result;
170     }
171     }
172
173     return true;
174 }
175
176 /*!
177  * @brief プレイヤーの現在の広域マップ座標から得た地勢を元にモンスターの生成条件関数を返す
178  * @param player_ptr プレイヤーへの参照ポインタ
179  * @return 地勢にあったモンスターの生成条件関数
180  */
181 monsterrace_hook_type get_monster_hook(PlayerType *player_ptr)
182 {
183     if ((player_ptr->current_floor_ptr->dun_level > 0) || (inside_quest(player_ptr->current_floor_ptr->quest_number))) {
184         return (monsterrace_hook_type)mon_hook_dungeon;
185     }
186
187     switch (wilderness[player_ptr->wilderness_y][player_ptr->wilderness_x].terrain) {
188     case TERRAIN_TOWN:
189         return (monsterrace_hook_type)mon_hook_town;
190     case TERRAIN_DEEP_WATER:
191         return (monsterrace_hook_type)mon_hook_ocean;
192     case TERRAIN_SHALLOW_WATER:
193     case TERRAIN_SWAMP:
194         return (monsterrace_hook_type)mon_hook_shore;
195     case TERRAIN_DIRT:
196     case TERRAIN_DESERT:
197         return (monsterrace_hook_type)mon_hook_waste;
198     case TERRAIN_GRASS:
199         return (monsterrace_hook_type)mon_hook_grass;
200     case TERRAIN_TREES:
201         return (monsterrace_hook_type)mon_hook_wood;
202     case TERRAIN_SHALLOW_LAVA:
203     case TERRAIN_DEEP_LAVA:
204         return (monsterrace_hook_type)mon_hook_volcano;
205     case TERRAIN_MOUNTAIN:
206         return (monsterrace_hook_type)mon_hook_mountain;
207     default:
208         return (monsterrace_hook_type)mon_hook_dungeon;
209     }
210 }
211
212 /*!
213  * @brief 指定された広域マップ座標の地勢を元にモンスターの生成条件関数を返す
214  * @return 地勢にあったモンスターの生成条件関数
215  */
216 monsterrace_hook_type get_monster_hook2(PlayerType *player_ptr, POSITION y, POSITION x)
217 {
218     auto *f_ptr = &terrains_info[player_ptr->current_floor_ptr->grid_array[y][x].feat];
219     if (f_ptr->flags.has(TerrainCharacteristics::WATER)) {
220         return f_ptr->flags.has(TerrainCharacteristics::DEEP) ? (monsterrace_hook_type)mon_hook_deep_water : (monsterrace_hook_type)mon_hook_shallow_water;
221     }
222
223     if (f_ptr->flags.has(TerrainCharacteristics::LAVA)) {
224         return (monsterrace_hook_type)mon_hook_lava;
225     }
226
227     return (monsterrace_hook_type)mon_hook_floor;
228 }
229
230 /*!
231  * @brief モンスター生成テーブルの重みを指定条件に従って変更する。
232  * @param player_ptr
233  * @param hook1 生成制約関数1 (nullptr の場合、制約なし)
234  * @param hook2 生成制約関数2 (nullptr の場合、制約なし)
235  * @param restrict_to_dungeon 現在プレイヤーのいるダンジョンの制約を適用するか
236  * @return 常に 0
237  *
238  * モンスター生成テーブル alloc_race_table の各要素の基本重み prob1 を指定条件
239  * に従って変更し、結果を prob2 に書き込む。
240  */
241 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)
242 {
243     const floor_type *const floor_ptr = player_ptr->current_floor_ptr;
244
245     // デバッグ用統計情報。
246     int mon_num = 0; // 重み(prob2)が正の要素数
247     DEPTH lev_min = MAX_DEPTH; // 重みが正の要素のうち最小階
248     DEPTH lev_max = 0; // 重みが正の要素のうち最大階
249     int prob2_total = 0; // 重みの総和
250
251     // モンスター生成テーブルの各要素について重みを修正する。
252     for (auto i = 0U; i < alloc_race_table.size(); i++) {
253         alloc_entry *const entry = &alloc_race_table[i];
254         const auto entry_r_idx = i2enum<MonsterRaceId>(entry->index);
255         const monster_race *const r_ptr = &monraces_info[entry_r_idx];
256
257         // 生成を禁止する要素は重み 0 とする。
258         entry->prob2 = 0;
259
260         // 基本重みが 0 以下なら生成禁止。
261         // テーブル内の無効エントリもこれに該当する(alloc_race_table は生成時にゼロクリアされるため)。
262         if (entry->prob1 <= 0) {
263             continue;
264         }
265
266         // いずれかの生成制約関数が偽を返したら生成禁止。
267         if ((hook1 && !hook1(player_ptr, entry_r_idx)) || (hook2 && !hook2(player_ptr, entry_r_idx))) {
268             continue;
269         }
270
271         // 原則生成禁止するものたち(フェイズアウト状態 / カメレオンの変身先 / ダンジョンの主召喚 は例外)。
272         if (!player_ptr->phase_out && !chameleon_change_m_idx && summon_specific_type != SUMMON_GUARDIANS) {
273             // クエストモンスターは生成禁止。
274             if (r_ptr->flags1 & RF1_QUESTOR) {
275                 continue;
276             }
277
278             // ダンジョンの主は生成禁止。
279             if (r_ptr->flags7 & RF7_GUARDIAN) {
280                 continue;
281             }
282
283             // RF1_FORCE_DEPTH フラグ持ちは指定階未満では生成禁止。
284             if ((r_ptr->flags1 & RF1_FORCE_DEPTH) && (r_ptr->level > floor_ptr->dun_level)) {
285                 continue;
286             }
287
288             // クエスト内でRES_ALLの生成を禁止する (殲滅系クエストの詰み防止)
289             if (inside_quest(player_ptr->current_floor_ptr->quest_number) && r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL)) {
290                 continue;
291             }
292         }
293
294         // 生成を許可するものは基本重みをそのまま引き継ぐ。
295         entry->prob2 = entry->prob1;
296
297         // 引数で指定されていればさらにダンジョンによる制約を試みる。
298         if (restrict_to_dungeon) {
299             // ダンジョンによる制約を適用する条件:
300             //
301             //   * フェイズアウト状態でない
302             //   * 1階かそれより深いところにいる
303             //   * ランダムクエスト中でない
304             const bool in_random_quest = inside_quest(floor_ptr->quest_number) && !quest_type::is_fixed(floor_ptr->quest_number);
305             const bool cond = !player_ptr->phase_out && floor_ptr->dun_level > 0 && !in_random_quest;
306
307             if (cond && !restrict_monster_to_dungeon(player_ptr, entry_r_idx)) {
308                 // ダンジョンによる制約に掛かった場合、重みを special_div/64 倍する。
309                 // 丸めは確率的に行う。
310                 const int numer = entry->prob2 * dungeons_info[player_ptr->dungeon_idx].special_div;
311                 const int q = numer / 64;
312                 const int r = numer % 64;
313                 entry->prob2 = (PROB)(randint0(64) < r ? q + 1 : q);
314             }
315         }
316
317         // 統計情報更新。
318         if (entry->prob2 > 0) {
319             mon_num++;
320             if (lev_min > entry->level) {
321                 lev_min = entry->level;
322             }
323             if (lev_max < entry->level) {
324                 lev_max = entry->level;
325             }
326             prob2_total += entry->prob2;
327         }
328     }
329
330     // チートオプションが有効なら統計情報を出力。
331     if (cheat_hear) {
332         msg_format(_("モンスター第2次候補数:%d(%d-%dF)%d ", "monster second selection:%d(%d-%dF)%d "), mon_num, lev_min, lev_max, prob2_total);
333     }
334
335     return 0;
336 }
337
338 /*!
339  * @brief モンスター生成テーブルの重み修正
340  * @param player_ptr
341  * @param hook1 生成制約関数1 (nullptr の場合、制約なし)
342  * @param hook2 生成制約関数2 (nullptr の場合、制約なし)
343  * @return 常に 0
344  *
345  * get_mon_num() を呼ぶ前に get_mon_num_prep() 系関数のいずれかを呼ぶこと。
346  */
347 errr get_mon_num_prep(PlayerType *player_ptr, const monsterrace_hook_type hook1, const monsterrace_hook_type hook2)
348 {
349     return do_get_mon_num_prep(player_ptr, hook1, hook2, true);
350 }
351
352 /*!
353  * @brief モンスター生成テーブルの重み修正(賞金首選定用)
354  * @return 常に 0
355  *
356  * get_mon_num() を呼ぶ前に get_mon_num_prep 系関数のいずれかを呼ぶこと。
357  */
358 errr get_mon_num_prep_bounty(PlayerType *player_ptr)
359 {
360     return do_get_mon_num_prep(player_ptr, nullptr, nullptr, false);
361 }