1 #include "spell-realm/spells-hex.h"
2 #include "core/asking-player.h"
3 #include "core/player-redraw-types.h"
4 #include "core/player-update-types.h"
5 #include "core/window-redrawer.h"
6 #include "effect/effect-characteristics.h"
7 #include "effect/effect-processor.h"
8 #include "monster-attack/monster-attack-util.h"
9 #include "monster-race/monster-race.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/spell-types.h"
18 #include "spell/spells-execution.h"
19 #include "spell/technic-info-table.h"
20 #include "status/action-setter.h"
21 #include "system/floor-type-definition.h"
22 #include "system/monster-race-definition.h"
23 #include "system/monster-type-definition.h"
24 #include "system/player-type-definition.h"
25 #include "term/screen-processor.h"
26 #include "util/bit-flags-calculator.h"
27 #include "util/int-char-converter.h"
28 #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 RealmHex::RealmHex(player_type *player_ptr)
40 : player_ptr(player_ptr)
42 constexpr int max_realm_spells = 32;
43 for (auto spell = 0; spell < max_realm_spells; spell++) {
44 if (this->is_spelling_specific(spell)) {
45 this->casting_spells.push_back(spell);
49 if (this->casting_spells.size() > MAX_KEEP) {
50 throw("Invalid numbers of hex magics keep!");
54 RealmHex::RealmHex(player_type *player_ptr, monap_type *monap_ptr)
55 : player_ptr(player_ptr)
56 , monap_ptr(monap_ptr)
61 * @brief プレイヤーが詠唱中の全呪術を停止する
63 bool RealmHex::stop_all_spells()
65 for (auto spell : this->casting_spells) {
66 exe_spell(this->player_ptr, REALM_HEX, spell, SPELL_STOP);
69 casting_hex_flags(this->player_ptr) = 0;
70 casting_hex_num(this->player_ptr) = 0;
71 if (this->player_ptr->action == ACTION_SPELL) {
72 set_action(this->player_ptr, ACTION_NONE);
75 this->player_ptr->update |= PU_BONUS | PU_HP | PU_MANA | PU_SPELLS;
76 this->player_ptr->redraw |= PR_EXTRA | PR_HP | PR_MANA;
81 * @brief プレイヤーが詠唱中の呪術から一つを選んで停止する
83 bool RealmHex::stop_one_spell()
85 if (!RealmHex(this->player_ptr).is_spelling_any()) {
86 msg_print(_("呪文を詠唱していません。", "You are not casting a spell."));
90 if ((casting_hex_num(this->player_ptr) == 1) || (this->player_ptr->lev < 35)) {
91 return this->stop_all_spells();
95 strnfmt(out_val, 78, _("どの呪文の詠唱を中断しますか?(呪文 %c-%c, 'l'全て, ESC)", "Which spell do you stop casting? (Spell %c-%c, 'l' to all, ESC)"),
96 I2A(0), I2A(casting_hex_num(this->player_ptr) - 1));
99 auto is_selected = select_spell_stopping(out_val, choice);
102 auto n = this->casting_spells[A2I(choice)];
103 exe_spell(this->player_ptr, REALM_HEX, n, SPELL_STOP);
104 casting_hex_flags(this->player_ptr) &= ~(1UL << n);
105 casting_hex_num(this->player_ptr)--;
108 this->player_ptr->update |= PU_BONUS | PU_HP | PU_MANA | PU_SPELLS;
109 this->player_ptr->redraw |= PR_EXTRA | PR_HP | PR_MANA;
115 * @param spells 詠唱中の呪術リスト
117 * @param choice 選択した呪文
118 * @return 選択が完了したらtrue、キャンセルならばfalse
120 bool RealmHex::select_spell_stopping(char *out_val, char &choice)
123 this->display_casting_spells_list();
124 if (!get_com(out_val, &choice, true)) {
128 if (isupper(choice)) {
129 choice = static_cast<char>(tolower(choice));
135 return this->stop_all_spells();
138 if ((choice < I2A(0)) || (choice > I2A(casting_hex_num(this->player_ptr) - 1))) {
146 void RealmHex::display_casting_spells_list()
148 constexpr auto y = 1;
149 constexpr auto x = 20;
151 term_erase(x, y, 255);
152 prt(_(" 名前", " Name"), y, x + 5);
153 for (auto spell : this->casting_spells) {
154 term_erase(x, y + n + 1, 255);
155 auto spell_result = exe_spell(this->player_ptr, REALM_HEX, spell, SPELL_NAME);
156 put_str(format("%c) %s", I2A(n), spell_result), y + n + 1, x + 2);
162 * @brief 一定時間毎に呪術で消費するMPを処理する
164 void RealmHex::decrease_mana()
166 /* Spells spelled by player */
167 if (this->player_ptr->realm1 != REALM_HEX) {
171 if (!casting_hex_flags(this->player_ptr) && !this->player_ptr->magic_num1[1]) {
175 auto need_restart = this->check_restart();
176 if (this->player_ptr->anti_magic) {
177 this->stop_all_spells();
181 if (!this->process_mana_cost(need_restart)) {
185 this->gain_exp_from_hex();
186 for (auto spell : this->casting_spells) {
187 exe_spell(this->player_ptr, REALM_HEX, spell, SPELL_CONT);
192 * @brief 継続的な呪文の詠唱が可能な程度にMPが残っているか確認し、残量に応じて継続・中断を行う
193 * @param need_restart 詠唱を再開するか否か
194 * @return MPが足りているか否か
196 bool RealmHex::process_mana_cost(const bool need_restart)
198 auto need_mana = this->calc_need_mana();
199 uint need_mana_frac = 0;
200 s64b_div(&need_mana, &need_mana_frac, 0, 3); /* Divide by 3 */
201 need_mana += (casting_hex_num(this->player_ptr) - 1);
203 auto enough_mana = s64b_cmp(this->player_ptr->csp, this->player_ptr->csp_frac, need_mana, need_mana_frac) >= 0;
205 this->stop_all_spells();
209 s64b_sub(&(this->player_ptr->csp), &(this->player_ptr->csp_frac), need_mana, need_mana_frac);
210 this->player_ptr->redraw |= PR_MANA;
215 msg_print(_("詠唱を再開した。", "You restart casting."));
216 this->player_ptr->action = ACTION_SPELL;
217 this->player_ptr->update |= PU_BONUS | PU_HP;
218 this->player_ptr->redraw |= PR_MAP | PR_STATUS | PR_STATE;
219 this->player_ptr->update |= PU_MONSTERS;
220 this->player_ptr->window_flags |= PW_OVERHEAD | PW_DUNGEON;
224 bool RealmHex::check_restart()
226 if (this->player_ptr->magic_num1[1] == 0) {
230 this->player_ptr->magic_num1[0] = this->player_ptr->magic_num1[1];
231 this->player_ptr->magic_num1[1] = 0;
235 int RealmHex::calc_need_mana()
238 for (auto spell : this->casting_spells) {
239 const auto *s_ptr = &technic_info[REALM_HEX - MIN_TECHNIC][spell];
240 need_mana += mod_need_mana(this->player_ptr, s_ptr->smana, spell, REALM_HEX);
246 void RealmHex::gain_exp_from_hex()
248 for (auto spell : this->casting_spells) {
249 if (!this->is_spelling_specific(spell)) {
253 if (this->player_ptr->spell_exp[spell] < SPELL_EXP_BEGINNER) {
254 this->player_ptr->spell_exp[spell] += 5;
258 if (this->gain_exp_skilled(spell)) {
262 if (this->gain_exp_expert(spell)) {
266 this->gain_exp_master(spell);
270 bool RealmHex::gain_exp_skilled(const int spell)
272 if (this->player_ptr->spell_exp[spell] >= SPELL_EXP_SKILLED) {
276 auto *floor_ptr = this->player_ptr->current_floor_ptr;
277 auto gain_condition = one_in_(2);
278 gain_condition &= floor_ptr->dun_level > 4;
279 gain_condition &= (floor_ptr->dun_level + 10) > this->player_ptr->lev;
280 if (gain_condition) {
281 this->player_ptr->spell_exp[spell]++;
287 bool RealmHex::gain_exp_expert(const int spell)
289 if (this->player_ptr->spell_exp[spell] >= SPELL_EXP_EXPERT) {
293 const auto *s_ptr = &technic_info[REALM_HEX - MIN_TECHNIC][spell];
294 auto *floor_ptr = this->player_ptr->current_floor_ptr;
295 auto gain_condition = one_in_(5);
296 gain_condition &= (floor_ptr->dun_level + 5) > this->player_ptr->lev;
297 gain_condition &= (floor_ptr->dun_level + 5) > s_ptr->slevel;
298 if (gain_condition) {
299 this->player_ptr->spell_exp[spell]++;
305 void RealmHex::gain_exp_master(const int spell)
307 if (this->player_ptr->spell_exp[spell] >= SPELL_EXP_MASTER) {
311 const auto *s_ptr = &technic_info[REALM_HEX - MIN_TECHNIC][spell];
312 auto *floor_ptr = this->player_ptr->current_floor_ptr;
313 auto gain_condition = one_in_(5);
314 gain_condition &= (floor_ptr->dun_level + 5) > this->player_ptr->lev;
315 gain_condition &= floor_ptr->dun_level > s_ptr->slevel;
316 if (gain_condition) {
317 this->player_ptr->spell_exp[spell]++;
322 * @brief プレイヤーの呪術詠唱枠がすでに最大かどうかを返す
323 * @return すでに全枠を利用しているならTRUEを返す
325 bool RealmHex::is_casting_full_capacity() const
327 auto k_max = (this->player_ptr->lev / 15) + 1;
328 k_max = MIN(k_max, MAX_KEEP);
329 return casting_hex_num(this->player_ptr) >= k_max;
333 * @brief 一定ゲームターン毎に復讐処理の残り期間の判定を行う
335 void RealmHex::continue_revenge()
337 if ((this->player_ptr->realm1 != REALM_HEX) || (hex_revenge_turn(this->player_ptr) <= 0)) {
341 switch (hex_revenge_type(this->player_ptr)) {
343 exe_spell(this->player_ptr, REALM_HEX, HEX_PATIENCE, SPELL_CONT);
346 exe_spell(this->player_ptr, REALM_HEX, HEX_REVENGE, SPELL_CONT);
354 * @brief 復讐ダメージの追加を行う
355 * @param dam 蓄積されるダメージ量
357 void RealmHex::store_vengeful_damage(HIT_POINT dam)
359 if ((this->player_ptr->realm1 != REALM_HEX) || (hex_revenge_turn(this->player_ptr) <= 0)) {
363 hex_revenge_power(this->player_ptr) += dam;
368 * @param m_idx 判定の対象となるモンスターID
369 * @return 呪術の効果が適用されるならTRUEを返す
370 * @details v3.0.0現在は反テレポート・反魔法・反増殖の3種類
372 bool RealmHex::check_hex_barrier(MONSTER_IDX m_idx, realm_hex_type type) const
374 const auto *m_ptr = &this->player_ptr->current_floor_ptr->m_list[m_idx];
375 const auto *r_ptr = &r_info[m_ptr->r_idx];
376 return this->is_spelling_specific(type) && ((this->player_ptr->lev * 3 / 2) >= randint1(r_ptr->level));
379 bool RealmHex::is_spelling_specific(int hex) const
381 auto check = static_cast<uint32_t>(this->player_ptr->magic_num1[0]);
382 return (this->player_ptr->realm1 == REALM_HEX) && any_bits(check, 1U << hex);
385 bool RealmHex::is_spelling_any() const
387 return (player_ptr->realm1 == REALM_HEX) && (player_ptr->magic_num1[0] != 0);
391 * @brief 呪術「目には目を」の効果処理
392 * @param this->player_ptr プレーヤーへの参照ポインタ
393 * @param monap_ptr モンスターからプレーヤーへの直接攻撃構造体への参照ポインタ
395 void RealmHex::eyes_on_eyes()
397 if (this->monap_ptr == nullptr) {
398 throw("Invalid constructor was used!");
401 const auto is_eyeeye_finished = (this->player_ptr->tim_eyeeye == 0) && !this->is_spelling_specific(HEX_EYE_FOR_EYE);
402 if (is_eyeeye_finished || (this->monap_ptr->get_damage == 0) || this->player_ptr->is_dead) {
407 msg_format("攻撃が%s自身を傷つけた!", this->monap_ptr->m_name);
409 GAME_TEXT m_name_self[MAX_MONSTER_NAME];
410 monster_desc(this->player_ptr, m_name_self, this->monap_ptr->m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE | MD_OBJECTIVE);
411 msg_format("The attack of %s has wounded %s!", this->monap_ptr->m_name, m_name_self);
413 const auto y = this->monap_ptr->m_ptr->fy;
414 const auto x = this->monap_ptr->m_ptr->fx;
415 project(this->player_ptr, 0, 0, y, x, this->monap_ptr->get_damage, GF_MISSILE, PROJECT_KILL);
416 if (this->player_ptr->tim_eyeeye) {
417 set_tim_eyeeye(this->player_ptr, this->player_ptr->tim_eyeeye - 5, true);
421 void RealmHex::thief_teleport()
423 if (this->monap_ptr == nullptr) {
424 throw("Invalid constructor was used!");
427 if (!this->monap_ptr->blinked || !this->monap_ptr->alive || this->player_ptr->is_dead) {
431 if (this->check_hex_barrier(this->monap_ptr->m_idx, HEX_ANTI_TELE)) {
432 msg_print(_("泥棒は笑って逃げ...ようとしたがバリアに防がれた。", "The thief flees laughing...? But a magic barrier obstructs it."));
434 msg_print(_("泥棒は笑って逃げた!", "The thief flees laughing!"));
435 teleport_away(this->player_ptr, this->monap_ptr->m_idx, MAX_SIGHT * 2 + 5, TELEPORT_SPONTANEOUS);