2 * @file scene-table-monster.cpp
3 * @brief モンスターの遭遇状況に応じたBGM設定処理実装
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-info.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"
18 struct scene_monster_info {
20 MonsterRaceInfo *ap_r_ptr;
21 GAME_TURN last_seen; //!< 最後に対象モンスター見たゲームターン
22 uint32_t mute_until; //!< この時間に到達するまでモンスターBGMは設定しない
25 scene_monster_info scene_target_monster;
27 inline static bool has_shadower_flag(MonsterEntity *m_ptr)
29 return m_ptr->mflag2.has(MonsterConstantFlagType::KAGE);
32 inline static bool is_unique(MonsterRaceInfo *ap_r_ptr)
34 return ap_r_ptr->kind_flags.has(MonsterKindType::UNIQUE);
37 inline static bool is_unknown_monster(MonsterRaceInfo *ap_r_ptr)
39 return ap_r_ptr->r_tkills == 0;
42 void clear_scene_target_monster()
44 scene_target_monster.ap_r_ptr = nullptr;
47 static GAME_TURN get_game_turn()
49 GAME_TURN ret = w_ptr->game_turn;
50 if (ret == w_ptr->game_turn_limit) {
57 * @brief モンスターBGMの制限期間を設定する
58 * @details 指定の時間が経過するまでモンスターBGMの再生を制限する
59 * @param msec 制限する時間(秒)
61 void set_temp_mute_scene_monster(int sec)
63 scene_target_monster.mute_until = (uint32_t)time(nullptr) + sec;
67 * @brief モンスターBGMの制限期間か判定する
68 * @details ダンジョンターン数がscene_target_monster.mute_untilの値になるまで制限期間。
69 * @return モンスターBGMの制限期間の場合trueを返す
71 inline static bool can_mute_scene_monster()
73 return scene_target_monster.mute_until > time(nullptr);
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が優先
85 static bool is_high_rate(PlayerType *player_ptr, MONSTER_IDX m_idx1, MONSTER_IDX m_idx2)
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 = &monraces_info[m_ptr1->ap_r_idx];
92 auto ap_r_ptr2 = &monraces_info[m_ptr2->ap_r_idx];
94 /* Unique monsters first */
95 if (ap_r_ptr1->kind_flags.has(MonsterKindType::UNIQUE) != ap_r_ptr2->kind_flags.has(MonsterKindType::UNIQUE)) {
96 return ap_r_ptr1->kind_flags.has(MonsterKindType::UNIQUE);
99 /* Shadowers first (あやしい影) */
100 if (m_ptr1->mflag2.has(MonsterConstantFlagType::KAGE) != m_ptr2->mflag2.has(MonsterConstantFlagType::KAGE)) {
101 return m_ptr1->mflag2.has(MonsterConstantFlagType::KAGE);
104 /* Unknown monsters first */
105 if ((ap_r_ptr1->r_tkills == 0) != (ap_r_ptr2->r_tkills == 0)) {
106 return ap_r_ptr1->r_tkills == 0;
109 /* Higher level monsters first (if known) */
110 if (ap_r_ptr1->r_tkills && ap_r_ptr2->r_tkills && ap_r_ptr1->level != ap_r_ptr2->level) {
111 return ap_r_ptr1->level > ap_r_ptr2->level;
114 /* Sort by index if all conditions are same */
115 return m_ptr1->ap_r_idx > m_ptr2->ap_r_idx;
119 * @brief BGM対象モンスター更新処理
120 * @details 現在の対象と対象候補が同一モンスターの場合、最後に見たゲームターン情報を更新する。
121 * 対象候補が現在の対象よりも上位であれば対象を入れ替える。
122 * ユニーク、あやしい影、未知のモンスター、レベルの高さ、モンスターIDで優先を付ける。
123 * @param player_ptr プレイヤーへの参照ポインタ
124 * @param m_idx BGM対象候補のモンスター
126 static void update_target_monster(PlayerType *player_ptr, MONSTER_IDX m_idx)
128 if (scene_target_monster.ap_r_ptr && (scene_target_monster.m_idx == m_idx)) {
129 // 同一モンスター。最後に見たゲームターンを更新。
130 scene_target_monster.last_seen = get_game_turn();
132 bool do_dwap = false;
133 if (!scene_target_monster.ap_r_ptr) {
136 } else if (is_high_rate(player_ptr, m_idx, scene_target_monster.m_idx)) {
142 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
143 MonsterRaceInfo *ap_r_ptr = &monraces_info[m_ptr->ap_r_idx];
144 scene_target_monster.m_idx = m_idx;
145 scene_target_monster.ap_r_ptr = ap_r_ptr;
146 scene_target_monster.last_seen = get_game_turn();
151 using scene_monster_func = bool (*)(PlayerType *player_ptr, scene_type *value);
153 static bool scene_monster(PlayerType *player_ptr, scene_type *value)
155 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[scene_target_monster.m_idx];
157 if (has_shadower_flag(m_ptr)) {
158 value->type = TERM_XTRA_MUSIC_BASIC;
159 value->val = MUSIC_BASIC_SHADOWER;
162 value->type = TERM_XTRA_MUSIC_MONSTER;
163 value->val = enum2i(m_ptr->ap_r_idx);
168 static bool scene_unique(PlayerType *player_ptr, scene_type *value)
172 if (is_unique(scene_target_monster.ap_r_ptr)) {
173 value->type = TERM_XTRA_MUSIC_BASIC;
174 value->val = MUSIC_BASIC_UNIQUE;
181 static bool scene_unknown(PlayerType *player_ptr, scene_type *value)
184 if (is_unknown_monster(scene_target_monster.ap_r_ptr)) {
185 value->type = TERM_XTRA_MUSIC_BASIC;
186 value->val = MUSIC_BASIC_UNKNOWN_MONSTER;
193 static bool scene_high_level(PlayerType *player_ptr, scene_type *value)
195 if (!is_unknown_monster(scene_target_monster.ap_r_ptr) && (scene_target_monster.ap_r_ptr->level >= player_ptr->lev)) {
196 value->type = TERM_XTRA_MUSIC_BASIC;
197 value->val = MUSIC_BASIC_HIGHER_LEVEL_MONSTER;
204 /*! モンスターBGMのフォールバック設定。
205 * 先頭から適用する(先にある方を優先する)。
207 std::vector<scene_monster_func> scene_monster_def_list = {
208 // scene_monster : あやしい影 or モンスターID
210 // scene_unique : ユニークモンスター判定
212 // scene_unkown : 未知のモンスター判定
214 // scene_high_level : 高レベルのモンスター判定
218 int get_scene_monster_count()
220 return scene_monster_def_list.size();
224 * @brief 現在の条件でモンスターのBGM選曲をリストに設定する。
225 * @details リストのfrom_indexの位置から、get_scene_monster_count()で得られる個数分設定する。
226 * 視界内モンスターリスト先頭のモンスターを記憶し、以前のモンスターと比較してより上位のモンスターをBGM選曲の対象とする。
227 * 記憶したモンスターが視界内に存在しない場合、一定のゲームターン経過で忘れる。
228 * @param player_ptr プレイヤーへの参照ポインタ
229 * @param monster_list 視界内モンスターリスト
230 * @param list BGM選曲リスト
231 * @param from_index リストの更新開始位置
233 void refresh_scene_monster(PlayerType *player_ptr, const std::vector<MONSTER_IDX> &monster_list, scene_type_list &list, int from_index)
235 const bool mute = can_mute_scene_monster();
239 clear_scene_target_monster();
241 if (scene_target_monster.ap_r_ptr) {
243 if (get_game_turn() - scene_target_monster.last_seen >= 200) {
244 // 最後に見かけてから一定のゲームターンが経過した場合、BGM対象から外す
245 clear_scene_target_monster();
247 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[scene_target_monster.m_idx];
248 MonsterRaceInfo *ap_r_ptr = &monraces_info[m_ptr->ap_r_idx];
249 if (ap_r_ptr != scene_target_monster.ap_r_ptr) {
251 clear_scene_target_monster();
256 if (!monster_list.empty()) {
257 // 現在のBGM対象とモンスターリスト先頭を比較し、上位をBGM対象に設定する
258 update_target_monster(player_ptr, monster_list.front());
262 if (scene_target_monster.ap_r_ptr) {
263 // BGM対象の条件で選曲リストを設定する
264 for (auto func : scene_monster_def_list) {
265 scene_type &item = list[from_index];
266 if (!func(player_ptr, &item)) {
267 // Note -- 特に定義を設けていないが、type = 0は無効な値とする。
275 const int count = get_scene_monster_count();
276 for (int i = 0; i < count; i++) {
277 scene_type &item = list[from_index + i];
278 // Note -- 特に定義を設けていないが、type = 0は無効な値とする。