OSDN Git Service

Merge pull request #1192 from hengband/feature/3.0.0Alpha26
[hengbandforosx/hengbandosx.git] / src / main / scene-table-monster.cpp
1 /*!
2  * @file scene-table-monster.cpp
3  * @brief モンスターの遭遇状況に応じたBGM設定処理実装
4  */
5
6 #include "main/scene-table-monster.h"
7 #include "dungeon/quest.h"
8 #include "main/music-definitions-table.h"
9 #include "monster-race/monster-race.h"
10 #include "monster-race/race-flags1.h"
11 #include "system/floor-type-definition.h"
12 #include "system/monster-race-definition.h"
13 #include "system/monster-type-definition.h"
14 #include "system/player-type-definition.h"
15 #include "util/bit-flags-calculator.h"
16 #include "world/world.h"
17
18 struct scene_monster_info {
19     MONSTER_IDX m_idx;
20     monster_race *ap_r_ptr;
21     GAME_TURN last_seen; //!< 最後に対象モンスター見たゲームターン
22     u32b mute_until; //!< この時間に到達するまでモンスターBGMは設定しない
23 };
24
25 scene_monster_info scene_target_monster;
26
27 inline static bool has_shadower_flag(monster_type *m_ptr)
28 {
29     return m_ptr->mflag2.has(MFLAG2::KAGE);
30 }
31
32 inline static bool is_unique(monster_race *ap_r_ptr)
33 {
34     return any_bits(ap_r_ptr->flags1, RF1_UNIQUE);
35 }
36
37 inline static bool is_unknown_monster(monster_race *ap_r_ptr)
38 {
39     return (ap_r_ptr->r_tkills == 0);
40 }
41
42 void clear_scene_target_monster()
43 {
44     scene_target_monster.ap_r_ptr = NULL;
45 }
46
47 static GAME_TURN get_game_turn()
48 {
49     GAME_TURN ret = current_world_ptr->game_turn;
50     if (ret == current_world_ptr->game_turn_limit) {
51         ret = 0;
52     }
53     return ret;
54 }
55
56 /*!
57  * @brief モンスターBGMの制限期間を設定する
58  * @details 指定の時間が経過するまでモンスターBGMの再生を制限する
59  * @param msec 制限する時間(秒)
60  */
61 void set_temp_mute_scene_monster(int sec)
62 {
63     scene_target_monster.mute_until = (u32b)time(NULL) + sec;
64 }
65
66 /*!
67  * @brief モンスターBGMの制限期間か判定する
68  * @details ダンジョンターン数がscene_target_monster.mute_untilの値になるまで制限期間。
69  * @return モンスターBGMの制限期間の場合trueを返す
70  */
71 inline static bool can_mute_scene_monster()
72 {
73     return (scene_target_monster.mute_until > time(NULL));
74 }
75
76 /*!
77  * @brief モンスターの優先判定
78  * @details ユニーク、あやしい影、未知のモンスター、レベルの高さ、モンスターIDで優先を付ける。
79  * @param player_ptr プレーヤーへの参照ポインタ
80  * @param m_idx1 モンスターA(新参)
81  * @param m_idx2 モンスターB(現対象)
82  * @retval true モンスターAが優先
83  * @retval false モンスターBが優先
84  */
85 static bool is_high_rate(player_type *player_ptr, MONSTER_IDX m_idx1, MONSTER_IDX m_idx2)
86 {
87     // FIXME 視界内モンスターリストの比較関数と同じ処理
88     auto floor_ptr = player_ptr->current_floor_ptr;
89     auto m_ptr1 = &floor_ptr->m_list[m_idx1];
90     auto m_ptr2 = &floor_ptr->m_list[m_idx2];
91     auto ap_r_ptr1 = &r_info[m_ptr1->ap_r_idx];
92     auto ap_r_ptr2 = &r_info[m_ptr2->ap_r_idx];
93
94     /* Unique monsters first */
95     if (any_bits(ap_r_ptr1->flags1, RF1_UNIQUE) != any_bits(ap_r_ptr2->flags1, RF1_UNIQUE))
96         return any_bits(ap_r_ptr1->flags1, RF1_UNIQUE);
97
98     /* Shadowers first (あやしい影) */
99     if (m_ptr1->mflag2.has(MFLAG2::KAGE) != m_ptr2->mflag2.has(MFLAG2::KAGE))
100         return m_ptr1->mflag2.has(MFLAG2::KAGE);
101
102     /* Unknown monsters first */
103     if ((ap_r_ptr1->r_tkills == 0) != (ap_r_ptr2->r_tkills == 0))
104         return (ap_r_ptr1->r_tkills == 0);
105
106     /* Higher level monsters first (if known) */
107     if (ap_r_ptr1->r_tkills && ap_r_ptr2->r_tkills && ap_r_ptr1->level != ap_r_ptr2->level)
108         return ap_r_ptr1->level > ap_r_ptr2->level;
109
110     /* Sort by index if all conditions are same */
111     return m_ptr1->ap_r_idx > m_ptr2->ap_r_idx;
112 }
113
114 /*!
115  * @brief BGM対象モンスター更新処理
116  * @details 現在の対象と対象候補が同一モンスターの場合、最後に見たゲームターン情報を更新する。
117  * 対象候補が現在の対象よりも上位であれば対象を入れ替える。
118  * ユニーク、あやしい影、未知のモンスター、レベルの高さ、モンスターIDで優先を付ける。
119  * @param player_ptr プレーヤーへの参照ポインタ
120  * @param m_idx BGM対象候補のモンスター
121  */
122 static void update_target_monster(player_type *player_ptr, MONSTER_IDX m_idx)
123 {
124     if (scene_target_monster.ap_r_ptr && (scene_target_monster.m_idx == m_idx)) {
125         // 同一モンスター。最後に見たゲームターンを更新。
126         scene_target_monster.last_seen = get_game_turn();
127     } else {
128         bool do_dwap = false;
129         if (!scene_target_monster.ap_r_ptr) {
130             // 空席
131             do_dwap = true;
132         } else if (is_high_rate(player_ptr, m_idx, scene_target_monster.m_idx)) {
133             // 入れ替え
134             do_dwap = true;
135         }
136
137         if (do_dwap) {
138             monster_type *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
139             monster_race *ap_r_ptr = &r_info[m_ptr->ap_r_idx];
140             scene_target_monster.m_idx = m_idx;
141             scene_target_monster.ap_r_ptr = ap_r_ptr;
142             scene_target_monster.last_seen = get_game_turn();
143         }
144     }
145 }
146
147 using scene_monster_func = bool (*)(player_type *player_ptr, scene_type *value);
148
149 static bool scene_monster(player_type *player_ptr, scene_type *value)
150 {
151     monster_type *m_ptr = &player_ptr->current_floor_ptr->m_list[scene_target_monster.m_idx];
152
153     if (has_shadower_flag(m_ptr)) {
154         value->type = TERM_XTRA_MUSIC_BASIC;
155         value->val = MUSIC_BASIC_SHADOWER;
156         return true;
157     } else {
158         value->type = TERM_XTRA_MUSIC_MONSTER;
159         value->val = m_ptr->ap_r_idx;
160         return true;
161     }
162 }
163
164 static bool scene_unique(player_type *player_ptr, scene_type *value)
165 {
166     (void)player_ptr;
167
168     if (is_unique(scene_target_monster.ap_r_ptr)) {
169         value->type = TERM_XTRA_MUSIC_BASIC;
170         value->val = MUSIC_BASIC_UNIQUE;
171         return true;
172     }
173
174     return false;
175 }
176
177 static bool scene_unknown(player_type *player_ptr, scene_type *value)
178 {
179     (void)player_ptr;
180     if (is_unknown_monster(scene_target_monster.ap_r_ptr)) {
181         value->type = TERM_XTRA_MUSIC_BASIC;
182         value->val = MUSIC_BASIC_UNKNOWN_MONSTER;
183         return true;
184     }
185
186     return false;
187 }
188
189 static bool scene_high_level(player_type *player_ptr, scene_type *value)
190 {
191     if (!is_unknown_monster(scene_target_monster.ap_r_ptr) && (scene_target_monster.ap_r_ptr->level >= player_ptr->lev)) {
192         value->type = TERM_XTRA_MUSIC_BASIC;
193         value->val = MUSIC_BASIC_HIGHER_LEVEL_MONSTER;
194         return true;
195     }
196
197     return false;
198 }
199
200 /*! モンスターBGMのフォールバック設定。
201  * 先頭から適用する(先にある方を優先する)。
202  */
203 std::vector<scene_monster_func> scene_monster_def_list = {
204     // scene_monster : あやしい影 or モンスターID
205     scene_monster,
206     // scene_unique : ユニークモンスター判定
207     scene_unique,
208     // scene_unkown : 未知のモンスター判定
209     scene_unknown,
210     // scene_high_level : 高レベルのモンスター判定
211     scene_high_level,
212 };
213
214 int get_scene_monster_count()
215 {
216     return scene_monster_def_list.size();
217 }
218
219 /*!
220  * @brief 現在の条件でモンスターのBGM選曲をリストに設定する。
221  * @details リストのfrom_indexの位置から、get_scene_monster_count()で得られる個数分設定する。
222  * 視界内モンスターリスト先頭のモンスターを記憶し、以前のモンスターと比較してより上位のモンスターをBGM選曲の対象とする。
223  * 記憶したモンスターが視界内に存在しない場合、一定のゲームターン経過で忘れる。
224  * @param player_ptr プレーヤーへの参照ポインタ
225  * @param monster_list 視界内モンスターリスト
226  * @param list BGM選曲リスト
227  * @param from_index リストの更新開始位置
228  */
229 void refresh_scene_monster(player_type *player_ptr, const std::vector<MONSTER_IDX> &monster_list, scene_type_list &list, int from_index)
230 {
231     const bool mute = can_mute_scene_monster();
232
233     if (mute) {
234         // モンスターBGM制限中
235         clear_scene_target_monster();
236     } else {
237         if (scene_target_monster.ap_r_ptr) {
238             // BGM対象から外すチェック
239             if (get_game_turn() - scene_target_monster.last_seen >= 200) {
240                 // 最後に見かけてから一定のゲームターンが経過した場合、BGM対象から外す
241                 clear_scene_target_monster();
242             } else {
243                 monster_type *m_ptr = &player_ptr->current_floor_ptr->m_list[scene_target_monster.m_idx];
244                 monster_race *ap_r_ptr = &r_info[m_ptr->ap_r_idx];
245                 if (ap_r_ptr != scene_target_monster.ap_r_ptr) {
246                     // 死亡、チェンジモンスター、etc.
247                     clear_scene_target_monster();
248                 }
249             }
250         }
251
252         if (!monster_list.empty()) {
253             // 現在のBGM対象とモンスターリスト先頭を比較し、上位をBGM対象に設定する
254             update_target_monster(player_ptr, monster_list.front());
255         }
256     }
257
258     if (scene_target_monster.ap_r_ptr) {
259         // BGM対象の条件で選曲リストを設定する
260         for (auto func : scene_monster_def_list) {
261             scene_type &item = list[from_index];
262             if (!func(player_ptr, &item)) {
263                 // Note -- 特に定義を設けていないが、type = 0は無効な値とする。
264                 item.type = 0;
265                 item.val = 0;
266             }
267             ++from_index;
268         }
269     } else {
270         // BGM対象なしの場合は0で埋める
271         const int count = get_scene_monster_count();
272         for (int i = 0; i < count; i++) {
273             scene_type &item = list[from_index + i];
274             // Note -- 特に定義を設けていないが、type = 0は無効な値とする。
275             item.type = 0;
276             item.val = 0;
277         }
278     }
279 }