OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / mspell / mspell-attack / mspell-breath.cpp
1 #include "mspell/mspell-attack/mspell-breath.h"
2 #include "core/disturbance.h"
3 #include "effect/attribute-types.h"
4 #include "effect/effect-processor.h"
5 #include "main/sound-definitions-table.h"
6 #include "main/sound-of-music.h"
7 #include "mind/drs-types.h"
8 #include "monster-race/race-ability-flags.h"
9 #include "monster-race/race-indice-types.h"
10 #include "monster/monster-info.h"
11 #include "monster/monster-update.h"
12 #include "mspell/mspell-checker.h"
13 #include "mspell/mspell-damage-calculator.h"
14 #include "mspell/mspell-data.h"
15 #include "mspell/mspell-result.h"
16 #include "mspell/mspell-util.h"
17 #include "system/floor-type-definition.h"
18 #include "system/monster-entity.h"
19 #include "system/player-type-definition.h"
20 #include "timed-effect/player-blindness.h"
21 #include "timed-effect/timed-effects.h"
22 #include "view/display-messages.h"
23
24 /*!
25  * @brief ブレスを吐くときにモンスター固有のセリフを表示する
26  * @param r_idx モンスター種族番号
27  * @param GF_TYPE 魔法効果
28  * @return 表示したらTRUE、しなかったらFALSE
29  */
30 static bool spell_RF4_BREATH_special_message(MonsterRaceId r_idx, AttributeType GF_TYPE, concptr m_name)
31 {
32     if (r_idx == MonsterRaceId::JAIAN && GF_TYPE == AttributeType::SOUND) {
33         msg_format(_("%s^「ボォエ~~~~~~」", "%s^ sings, 'Booooeeeeee'"), m_name);
34         return true;
35     }
36     if (r_idx == MonsterRaceId::BOTEI && GF_TYPE == AttributeType::SHARDS) {
37         msg_format(_("%s^「ボ帝ビルカッター!!!」", "%s^ shouts, 'Boty-Build cutter!!!'"), m_name);
38         return true;
39     }
40     if (r_idx == MonsterRaceId::RAOU && (GF_TYPE == AttributeType::FORCE)) {
41         if (one_in_(2)) {
42             msg_format(_("%s^「北斗剛掌波!!」", "%s^ says, 'Hokuto Goh-Sho-Ha!!'"), m_name);
43         } else {
44             msg_format(_("%s^「受けてみい!!天将奔烈!!!」", "%s^ says, 'Tensho-Honretsu!!'"), m_name);
45         }
46         return true;
47     }
48     return false;
49 }
50
51 static void message_breath(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type, std::string_view type_s, AttributeType GF_TYPE)
52 {
53     auto *floor_ptr = player_ptr->current_floor_ptr;
54     auto *m_ptr = &floor_ptr->m_list[m_idx];
55     auto see_either = see_monster(player_ptr, m_idx) || see_monster(player_ptr, t_idx);
56     auto known = monster_near_player(floor_ptr, m_idx, t_idx);
57     auto mon_to_mon = (target_type == MONSTER_TO_MONSTER);
58     auto mon_to_player = (target_type == MONSTER_TO_PLAYER);
59     const auto m_name = monster_name(player_ptr, m_idx);
60     const auto t_name = monster_name(player_ptr, t_idx);
61
62     if (!spell_RF4_BREATH_special_message(m_ptr->r_idx, GF_TYPE, m_name.data())) {
63         if (player_ptr->effects()->blindness()->is_blind()) {
64             if (mon_to_player || (mon_to_mon && known && see_either)) {
65                 msg_format(_("%s^が何かのブレスを吐いた。", "%s^ breathes."), m_name.data());
66             }
67         } else {
68             if (mon_to_player) {
69                 msg_format(_("%s^が%s^のブレスを吐いた。", "%s^ breathes %s^."), m_name.data(), type_s.data());
70             } else if (mon_to_mon && known && see_either) {
71                 _(msg_format("%s^が%s^に%s^のブレスを吐いた。", m_name.data(), t_name.data(), type_s.data()), msg_format("%s^ breathes %s^ at %s^.", m_name.data(), type_s.data(), t_name.data()));
72             }
73         }
74     }
75
76     if (mon_to_mon && known && !see_either) {
77         floor_ptr->monster_noise = true;
78     }
79
80     if (known || see_either) {
81         sound(SOUND_BREATH);
82     }
83 }
84
85 static std::pair<MonsterAbilityType, MSpellData> make_breath_elemental(MonsterAbilityType ms_type, AttributeType GF_TYPE, std::string_view type_s)
86 {
87     return { ms_type, { [type_s, GF_TYPE](auto *player_ptr, auto m_idx, auto t_idx, int target_type) {
88                            message_breath(player_ptr, m_idx, t_idx, target_type, type_s, GF_TYPE);
89                            return false;
90                        },
91                           GF_TYPE } };
92 };
93
94 static std::pair<MonsterAbilityType, MSpellData> make_breath_elemental(MonsterAbilityType ms_type, AttributeType GF_TYPE, std::string_view type_s, MSpellDrsData drs)
95 {
96     return { ms_type, { [type_s, GF_TYPE](auto *player_ptr, auto m_idx, auto t_idx, int target_type) {
97                            message_breath(player_ptr, m_idx, t_idx, target_type, type_s, GF_TYPE);
98                            return false;
99                        },
100                           GF_TYPE, drs } };
101 };
102
103 const std::unordered_map<MonsterAbilityType, MSpellData> breath_list = {
104     make_breath_elemental(MonsterAbilityType::BR_ACID, AttributeType::ACID, _("酸", "acid"), DRS_ACID),
105     make_breath_elemental(MonsterAbilityType::BR_ELEC, AttributeType::ELEC, _("稲妻", "lightning"), DRS_ELEC),
106     make_breath_elemental(MonsterAbilityType::BR_FIRE, AttributeType::FIRE, _("火炎", "fire"), DRS_FIRE),
107     make_breath_elemental(MonsterAbilityType::BR_COLD, AttributeType::COLD, _("冷気", "frost"), DRS_COLD),
108     make_breath_elemental(MonsterAbilityType::BR_POIS, AttributeType::POIS, _("ガス", "gas"), DRS_POIS),
109     make_breath_elemental(MonsterAbilityType::BR_NETH, AttributeType::NETHER, _("地獄", "nether"), DRS_NETH),
110     make_breath_elemental(MonsterAbilityType::BR_LITE, AttributeType::LITE, _("閃光", "light"), DRS_LITE),
111     make_breath_elemental(MonsterAbilityType::BR_DARK, AttributeType::DARK, _("暗黒", "darkness"), DRS_DARK),
112     make_breath_elemental(MonsterAbilityType::BR_CONF, AttributeType::CONFUSION, _("混乱", "confusion"), DRS_CONF),
113     make_breath_elemental(MonsterAbilityType::BR_SOUN, AttributeType::SOUND, _("轟音", "sound"), DRS_SOUND),
114     make_breath_elemental(MonsterAbilityType::BR_CHAO, AttributeType::CHAOS, _("カオス", "chaos"), DRS_CHAOS),
115     make_breath_elemental(MonsterAbilityType::BR_DISE, AttributeType::DISENCHANT, _("劣化", "disenchantment"), DRS_DISEN),
116     make_breath_elemental(MonsterAbilityType::BR_NEXU, AttributeType::NEXUS, _("因果混乱", "nexus"), DRS_NEXUS),
117     make_breath_elemental(MonsterAbilityType::BR_TIME, AttributeType::TIME, _("時間逆転", "time")),
118     make_breath_elemental(MonsterAbilityType::BR_INER, AttributeType::INERTIAL, _("遅鈍", "inertia")),
119     make_breath_elemental(MonsterAbilityType::BR_GRAV, AttributeType::GRAVITY, _("重力", "gravity")),
120     make_breath_elemental(MonsterAbilityType::BR_SHAR, AttributeType::SHARDS, _("破片", "shards"), DRS_SHARD),
121     make_breath_elemental(MonsterAbilityType::BR_PLAS, AttributeType::PLASMA, _("プラズマ", "plasma")),
122     make_breath_elemental(MonsterAbilityType::BR_FORC, AttributeType::FORCE, _("フォース", "force")),
123     make_breath_elemental(MonsterAbilityType::BR_MANA, AttributeType::MANA, _("魔力", "mana")),
124     make_breath_elemental(MonsterAbilityType::BR_NUKE, AttributeType::NUKE, _("放射性廃棄物", "toxic waste"), DRS_POIS),
125     make_breath_elemental(MonsterAbilityType::BR_DISI, AttributeType::DISINTEGRATE, _("分解", "disintegration")),
126     make_breath_elemental(MonsterAbilityType::BR_VOID, AttributeType::VOID_MAGIC, _("虚無", "void")),
127     make_breath_elemental(MonsterAbilityType::BR_ABYSS, AttributeType::ABYSS, _("深淵", "abyss")),
128 };
129
130 /*!
131  * @brief RF4_BR_*の処理。各種ブレス。 /
132  * @param player_ptr プレイヤーへの参照ポインタ
133  * @param GF_TYPE ブレスの属性
134  * @param y 対象の地点のy座標
135  * @param x 対象の地点のx座標
136  * @param m_idx 呪文を唱えるモンスターID
137  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
138  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
139  *
140  * プレイヤーに当たったらラーニング可。
141  */
142 MonsterSpellResult spell_RF4_BREATH(PlayerType *player_ptr, MonsterAbilityType ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
143 {
144     auto dam = 0;
145     auto mon_to_player = (target_type == MONSTER_TO_PLAYER);
146     std::unique_ptr<MSpellData> data;
147
148     if (breath_list.find(ms_type) != breath_list.end()) {
149         dam = monspell_damage(player_ptr, ms_type, m_idx, DAM_ROLL);
150         data = std::make_unique<MSpellData>(breath_list.at(ms_type));
151     } else {
152         dam = 0;
153         data = std::make_unique<MSpellData>([](auto *player_ptr, auto m_idx, auto t_idx, int target_type) {
154             message_breath(player_ptr, m_idx, t_idx, target_type, _("不明", "Unknown"), AttributeType::NONE);
155             return false;
156         },
157             AttributeType::NONE);
158     }
159
160     data->msg.output(player_ptr, m_idx, t_idx, target_type);
161
162     const auto proj_res = breath(player_ptr, y, x, m_idx, data->type, dam, 0, target_type);
163
164     if (mon_to_player) {
165         data->drs.execute(player_ptr, m_idx);
166     }
167
168     auto res = MonsterSpellResult::make_valid(dam);
169     res.learnable = proj_res.affected_player;
170
171     return res;
172 }