OSDN Git Service

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