OSDN Git Service

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