1 #include "spell-realm/spells-hex.h"
2 #include "core/asking-player.h"
3 #include "core/window-redrawer.h"
4 #include "effect/effect-characteristics.h"
5 #include "effect/effect-processor.h"
6 #include "monster-attack/monster-attack-player.h"
7 #include "monster-race/monster-race.h"
8 #include "player-base/player-class.h"
9 #include "player-info/spell-hex-data-type.h"
10 #include "player/attack-defense-types.h"
11 #include "player/player-skill.h"
12 #include "realm/realm-hex-numbers.h"
13 #include "spell-kind/spells-teleport.h"
14 #include "spell-realm/spells-crusade.h"
15 #include "spell-realm/spells-song.h"
16 #include "spell/spell-info.h"
17 #include "spell/spells-execution.h"
18 #include "spell/technic-info-table.h"
19 #include "status/action-setter.h"
20 #include "system/angband-exceptions.h"
21 #include "system/floor-type-definition.h"
22 #include "system/monster-entity.h"
23 #include "system/monster-race-info.h"
24 #include "system/player-type-definition.h"
25 #include "system/redrawing-flags-updater.h"
26 #include "term/screen-processor.h"
27 #include "util/bit-flags-calculator.h"
28 #include "util/int-char-converter.h"
29 #include "view/display-messages.h"
32 #include "monster/monster-describer.h"
33 #include "monster/monster-description-types.h"
37 constexpr int MAX_KEEP = 4;
39 SpellHex::SpellHex(PlayerType *player_ptr)
40 : player_ptr(player_ptr)
41 , spell_hex_data(PlayerClass(player_ptr).get_specific_data<spell_hex_data_type>())
43 if (!this->spell_hex_data) {
47 HexSpellFlagGroup::get_flags(this->spell_hex_data->casting_spells, std::back_inserter(this->casting_spells));
49 if (this->casting_spells.size() > MAX_KEEP) {
50 THROW_EXCEPTION(std::logic_error, "Invalid numbers of hex magics keep!");
54 SpellHex::SpellHex(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
55 : player_ptr(player_ptr)
56 , monap_ptr(monap_ptr)
61 * @brief プレイヤーが詠唱中の全呪術を停止する
63 void SpellHex::stop_all_spells()
65 for (auto spell : this->casting_spells) {
66 exe_spell(this->player_ptr, REALM_HEX, spell, SpellProcessType::STOP);
69 this->spell_hex_data->casting_spells.clear();
70 if (this->player_ptr->action == ACTION_SPELL) {
71 set_action(this->player_ptr, ACTION_NONE);
74 auto &rfu = RedrawingFlagsUpdater::get_instance();
75 static constexpr auto flags_srf = {
76 StatusRecalculatingFlag::BONUS,
77 StatusRecalculatingFlag::HP,
78 StatusRecalculatingFlag::MP,
79 StatusRecalculatingFlag::SPELLS,
81 rfu.set_flags(flags_srf);
82 static constexpr auto flags_mwrf = {
83 MainWindowRedrawingFlag::EXTRA,
84 MainWindowRedrawingFlag::HP,
85 MainWindowRedrawingFlag::MP,
87 rfu.set_flags(flags_mwrf);
91 * @brief プレイヤーが詠唱中の呪術から選択式で一つまたは全てを停止する
92 * @return 停止したらtrue、停止をキャンセルしたらfalse
94 bool SpellHex::stop_spells_with_selection()
96 if (!this->is_spelling_any()) {
97 msg_print(_("呪文を詠唱していません。", "You are not casting a spell."));
101 auto casting_num = this->get_casting_num();
102 if ((casting_num == 1) || (this->player_ptr->lev < 35)) {
103 this->stop_all_spells();
107 constexpr auto fmt = _("どの呪文の詠唱を中断しますか?(呪文 %c-%c, 'l'全て, ESC)", "Which spell do you stop casting? (Spell %c-%c, 'l' to all, ESC)");
108 const auto prompt = format(fmt, I2A(0), I2A(casting_num - 1));
110 const auto [is_all, choice] = select_spell_stopping(prompt);
116 const auto is_selected = choice.has_value();
118 auto n = this->casting_spells[A2I(choice.value())];
119 exe_spell(this->player_ptr, REALM_HEX, n, SpellProcessType::STOP);
120 this->reset_casting_flag(i2enum<spell_hex_type>(n));
123 auto &rfu = RedrawingFlagsUpdater::get_instance();
124 static constexpr auto flags_srf = {
125 StatusRecalculatingFlag::BONUS,
126 StatusRecalculatingFlag::HP,
127 StatusRecalculatingFlag::MP,
128 StatusRecalculatingFlag::SPELLS,
130 rfu.set_flags(flags_srf);
131 static constexpr auto flags_mwrf = {
132 MainWindowRedrawingFlag::EXTRA,
133 MainWindowRedrawingFlag::HP,
134 MainWindowRedrawingFlag::MP,
136 rfu.set_flags(flags_mwrf);
144 * Item1: 全ての呪文を中断するならばtrue、1つの呪文を中断するならばfalse
145 * Item2: 選択が完了したらtrue、キャンセルならばfalse
146 * Item3: 選択した呪文番号 (a~d、lの5択)
148 std::pair<bool, std::optional<char>> SpellHex::select_spell_stopping(std::string_view prompt)
151 this->display_casting_spells_list();
152 const auto choice_opt = input_command(prompt, true);
153 if (!choice_opt.has_value()) {
154 return { false, std::nullopt };
157 auto choice = choice_opt.value();
158 if (isupper(choice)) {
159 choice = static_cast<char>(tolower(choice));
164 this->stop_all_spells();
165 return { true, choice };
168 if ((choice < I2A(0)) || (choice > I2A(this->get_casting_num() - 1))) {
172 return { false, choice };
176 void SpellHex::display_casting_spells_list()
178 constexpr auto y = 1;
179 constexpr auto x = 20;
182 prt(_(" 名前", " Name"), y, x + 5);
183 for (auto spell : this->casting_spells) {
184 term_erase(x, y + n + 1);
185 const auto spell_name = exe_spell(this->player_ptr, REALM_HEX, spell, SpellProcessType::NAME);
186 put_str(format("%c) %s", I2A(n), spell_name->data()), y + n + 1, x + 2);
192 * @brief 一定時間毎に呪術で消費するMPを処理する
194 void SpellHex::decrease_mana()
196 if (!this->spell_hex_data) {
200 if (this->spell_hex_data->casting_spells.none() && this->spell_hex_data->interrupting_spells.none()) {
204 auto need_restart = this->check_restart();
205 if (this->player_ptr->anti_magic) {
206 this->stop_all_spells();
210 if (!this->process_mana_cost(need_restart)) {
215 for (auto spell : this->casting_spells) {
216 exe_spell(this->player_ptr, REALM_HEX, spell, SpellProcessType::CONTNUATION);
221 * @brief 継続的な呪文の詠唱が可能な程度にMPが残っているか確認し、残量に応じて継続・中断を行う
222 * @param need_restart 詠唱を再開するか否か
223 * @return MPが足りているか否か
224 * @todo 64ビットの割り算をしなければいけない箇所には見えない. 調査の後不要ならば消すこと.
226 bool SpellHex::process_mana_cost(const bool need_restart)
228 auto need_mana = this->calc_need_mana();
229 uint need_mana_frac = 0;
230 s64b_div(&need_mana, &need_mana_frac, 0, 3); /* Divide by 3 */
231 need_mana += this->get_casting_num() - 1;
233 auto enough_mana = s64b_cmp(this->player_ptr->csp, this->player_ptr->csp_frac, need_mana, need_mana_frac) >= 0;
235 this->stop_all_spells();
239 s64b_sub(&(this->player_ptr->csp), &(this->player_ptr->csp_frac), need_mana, need_mana_frac);
240 auto &rfu = RedrawingFlagsUpdater::get_instance();
241 rfu.set_flag(MainWindowRedrawingFlag::MP);
246 msg_print(_("詠唱を再開した。", "You restart casting."));
247 this->player_ptr->action = ACTION_SPELL;
248 static constexpr auto flags_srf = {
249 StatusRecalculatingFlag::BONUS,
250 StatusRecalculatingFlag::HP,
251 StatusRecalculatingFlag::MONSTER_STATUSES,
253 rfu.set_flags(flags_srf);
254 static constexpr auto flags_mwrf = {
255 MainWindowRedrawingFlag::MAP,
256 MainWindowRedrawingFlag::TIMED_EFFECT,
257 MainWindowRedrawingFlag::ACTION,
259 rfu.set_flags(flags_mwrf);
260 static constexpr auto flags_swrf = {
261 SubWindowRedrawingFlag::OVERHEAD,
262 SubWindowRedrawingFlag::DUNGEON,
264 rfu.set_flags(flags_swrf);
268 bool SpellHex::check_restart()
270 if (this->spell_hex_data->interrupting_spells.none()) {
274 this->spell_hex_data->casting_spells = this->spell_hex_data->interrupting_spells;
275 this->spell_hex_data->interrupting_spells.clear();
279 int SpellHex::calc_need_mana()
282 for (auto spell : this->casting_spells) {
283 const auto *s_ptr = &technic_info[REALM_HEX - MIN_TECHNIC][spell];
284 need_mana += mod_need_mana(this->player_ptr, s_ptr->smana, spell, REALM_HEX);
290 void SpellHex::gain_exp()
292 PlayerSkill ps(player_ptr);
293 for (auto spell : this->casting_spells) {
294 if (!this->is_spelling_specific(spell)) {
298 ps.gain_continuous_spell_skill_exp(REALM_HEX, spell);
303 * @brief プレイヤーの呪術詠唱枠がすでに最大かどうかを返す
304 * @return すでに全枠を利用しているならTRUEを返す
306 bool SpellHex::is_casting_full_capacity() const
308 auto k_max = (this->player_ptr->lev / 15) + 1;
309 k_max = std::min(k_max, MAX_KEEP);
310 return this->get_casting_num() >= k_max;
314 * @brief 一定ゲームターン毎に復讐処理の残り期間の判定を行う
316 void SpellHex::continue_revenge()
318 if (!this->spell_hex_data || (this->get_revenge_turn() == 0)) {
322 switch (this->get_revenge_type()) {
323 case SpellHexRevengeType::PATIENCE:
324 exe_spell(this->player_ptr, REALM_HEX, HEX_PATIENCE, SpellProcessType::CONTNUATION);
326 case SpellHexRevengeType::REVENGE:
327 exe_spell(this->player_ptr, REALM_HEX, HEX_REVENGE, SpellProcessType::CONTNUATION);
335 * @brief 復讐ダメージの追加を行う
336 * @param dam 蓄積されるダメージ量
338 void SpellHex::store_vengeful_damage(int dam)
340 if (!this->spell_hex_data || (this->get_revenge_turn() == 0)) {
344 this->set_revenge_power(dam, false);
349 * @param m_idx 判定の対象となるモンスターID
350 * @return 呪術の効果が適用されるならTRUEを返す
351 * @details v3.0.0現在は反テレポート・反魔法・反増殖の3種類
353 bool SpellHex::check_hex_barrier(MONSTER_IDX m_idx, spell_hex_type type) const
355 const auto *m_ptr = &this->player_ptr->current_floor_ptr->m_list[m_idx];
356 const auto *r_ptr = &monraces_info[m_ptr->r_idx];
357 return this->is_spelling_specific(type) && ((this->player_ptr->lev * 3 / 2) >= randint1(r_ptr->level));
360 bool SpellHex::is_spelling_specific(int hex) const
362 return this->spell_hex_data && this->spell_hex_data->casting_spells.has(i2enum<spell_hex_type>(hex));
365 bool SpellHex::is_spelling_any() const
367 return this->spell_hex_data && (this->get_casting_num() > 0);
370 void SpellHex::interrupt_spelling()
372 this->spell_hex_data->interrupting_spells = this->spell_hex_data->casting_spells;
373 this->spell_hex_data->casting_spells.clear();
377 * @brief 呪術「目には目を」の効果処理
378 * @param this->player_ptr プレイヤーへの参照ポインタ
379 * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
381 void SpellHex::eyes_on_eyes()
383 if (this->monap_ptr == nullptr) {
384 THROW_EXCEPTION(std::logic_error, "Invalid constructor was used!");
387 const auto is_eyeeye_finished = (this->player_ptr->tim_eyeeye == 0) && !this->is_spelling_specific(HEX_EYE_FOR_EYE);
388 if (is_eyeeye_finished || (this->monap_ptr->get_damage == 0) || this->player_ptr->is_dead) {
393 msg_format("攻撃が%s自身を傷つけた!", this->monap_ptr->m_name);
395 const auto m_name_self = monster_desc(this->player_ptr, this->monap_ptr->m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE | MD_OBJECTIVE);
396 msg_format("The attack of %s has wounded %s!", this->monap_ptr->m_name, m_name_self.data());
398 const auto y = this->monap_ptr->m_ptr->fy;
399 const auto x = this->monap_ptr->m_ptr->fx;
400 project(this->player_ptr, 0, 0, y, x, this->monap_ptr->get_damage, AttributeType::MISSILE, PROJECT_KILL);
401 if (this->player_ptr->tim_eyeeye) {
402 set_tim_eyeeye(this->player_ptr, this->player_ptr->tim_eyeeye - 5, true);
406 void SpellHex::thief_teleport()
408 if (this->monap_ptr == nullptr) {
409 THROW_EXCEPTION(std::logic_error, "Invalid constructor was used!");
412 if (!this->monap_ptr->blinked || !this->monap_ptr->alive || this->player_ptr->is_dead) {
416 if (this->check_hex_barrier(this->monap_ptr->m_idx, HEX_ANTI_TELE)) {
417 msg_print(_("泥棒は笑って逃げ...ようとしたがバリアに防がれた。", "The thief flees laughing...? But a magic barrier obstructs it."));
419 msg_print(_("泥棒は笑って逃げた!", "The thief flees laughing!"));
420 teleport_away(this->player_ptr, this->monap_ptr->m_idx, MAX_PLAYER_SIGHT * 2 + 5, TELEPORT_SPONTANEOUS);
424 void SpellHex::set_casting_flag(spell_hex_type type)
426 this->spell_hex_data->casting_spells.set(type);
429 void SpellHex::reset_casting_flag(spell_hex_type type)
431 this->spell_hex_data->casting_spells.reset(type);
434 int32_t SpellHex::get_casting_num() const
436 return this->spell_hex_data->casting_spells.count();
439 int32_t SpellHex::get_revenge_power() const
441 return this->spell_hex_data->revenge_power;
444 void SpellHex::set_revenge_power(int32_t power, bool substitution)
447 this->spell_hex_data->revenge_power = power;
449 this->spell_hex_data->revenge_power += power;
453 byte SpellHex::get_revenge_turn() const
455 return this->spell_hex_data->revenge_turn;
459 * @brief 復讐の残りターンをセットするか、残りターン数を減らす
460 * @param turn 残りターン (非負整数であること)
461 * @param substitution セットならtrue、ターン減少ならfalse
463 void SpellHex::set_revenge_turn(byte turn, bool substitution)
466 this->spell_hex_data->revenge_turn = turn;
468 this->spell_hex_data->revenge_turn -= turn;
472 SpellHexRevengeType SpellHex::get_revenge_type() const
474 return this->spell_hex_data->revenge_type;
477 void SpellHex::set_revenge_type(SpellHexRevengeType type)
479 this->spell_hex_data->revenge_type = type;