OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / cmd-action / cmd-mind.cpp
1 /*!
2  * @brief 各職業の特殊技能実装 / Special magics
3  * @date 2014/01/15
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke\n
6  * This software may be copied and distributed for educational, research,\n
7  * and not for profit purposes provided that this copyright and statement\n
8  * are included in all such copies.  Other copyrights may also apply.\n
9  * 2005 henkma \n
10  * 2014 Deskull rearranged comment for Doxygen.\n
11  */
12
13 #include "cmd-action/cmd-mind.h"
14 #include "action/action-limited.h"
15 #include "core/asking-player.h"
16 #include "core/window-redrawer.h"
17 #include "effect/attribute-types.h"
18 #include "effect/effect-characteristics.h"
19 #include "effect/effect-processor.h"
20 #include "game-option/disturbance-options.h"
21 #include "game-option/input-options.h"
22 #include "grid/grid.h"
23 #include "inventory/inventory-slot-types.h"
24 #include "io/input-key-acceptor.h"
25 #include "main/sound-definitions-table.h"
26 #include "main/sound-of-music.h"
27 #include "mind/mind-berserker.h"
28 #include "mind/mind-explanations-table.h"
29 #include "mind/mind-force-trainer.h"
30 #include "mind/mind-mindcrafter.h"
31 #include "mind/mind-mirror-master.h"
32 #include "mind/mind-ninja.h"
33 #include "mind/mind-numbers.h"
34 #include "mind/mind-power-getter.h"
35 #include "mind/mind-types.h"
36 #include "player-base/player-class.h"
37 #include "player-info/class-info.h"
38 #include "player-info/equipment-info.h"
39 #include "player-status/player-energy.h"
40 #include "player/player-damage.h"
41 #include "player/player-status-table.h"
42 #include "player/player-status.h"
43 #include "spell-kind/spells-teleport.h"
44 #include "status/bad-status-setter.h"
45 #include "status/base-status.h"
46 #include "system/floor-type-definition.h"
47 #include "system/grid-type-definition.h"
48 #include "system/player-type-definition.h"
49 #include "system/redrawing-flags-updater.h"
50 #include "term/screen-processor.h"
51 #include "timed-effect/player-stun.h"
52 #include "timed-effect/timed-effects.h"
53 #include "util/enum-converter.h"
54 #include "view/display-messages.h"
55 #include "view/display-util.h"
56
57 /*!
58  * @brief 職業別特殊技能の処理用構造体
59  */
60 struct cm_type {
61     MindKindType use_mind; //!< 使った職業技能構造体
62     concptr mind_explanation; //!< 特殊技能の説明
63     SPELL_IDX n; //!< 職業種別毎ID
64     int b; //!< 失敗時チャート用乱数
65     PERCENTAGE chance; //!< 失敗率
66     PERCENTAGE minfail; //!< 最低失敗率
67     PLAYER_LEVEL plev; //!< 取得レベル
68     int old_csp; //!< 行使前のMP
69     mind_type spell; //!< 職業技能の基本設定構造体
70     bool cast; //!< 行使の成否
71     int mana_cost; //!< 最終算出消費MP
72     bool on_mirror; //!< 鏡の上に乗っているどうかの判定
73 };
74
75 /*!
76  * @brief 職業技能処理構造体の初期化
77  */
78 static cm_type *initialize_cm_type(PlayerType *player_ptr, cm_type *cm_ptr)
79 {
80     cm_ptr->n = 0;
81     cm_ptr->b = 0;
82     cm_ptr->minfail = 0;
83     cm_ptr->plev = player_ptr->lev;
84     cm_ptr->old_csp = player_ptr->csp;
85     cm_ptr->on_mirror = false;
86     return cm_ptr;
87 }
88
89 /*!
90  * @brief 職業別の行使可能な技能種別を構造体に付加する
91  */
92 static void switch_mind_kind(PlayerType *player_ptr, cm_type *cm_ptr)
93 {
94     switch (player_ptr->pclass) {
95     case PlayerClassType::MINDCRAFTER:
96         cm_ptr->use_mind = MindKindType::MINDCRAFTER;
97         cm_ptr->mind_explanation = _("精神", "skill");
98         break;
99     case PlayerClassType::FORCETRAINER:
100         cm_ptr->use_mind = MindKindType::KI;
101         cm_ptr->mind_explanation = _("気", "skill");
102         break;
103     case PlayerClassType::BERSERKER:
104         cm_ptr->use_mind = MindKindType::BERSERKER;
105         cm_ptr->mind_explanation = _("怒り", "skill");
106         break;
107     case PlayerClassType::MIRROR_MASTER:
108         cm_ptr->use_mind = MindKindType::MIRROR_MASTER;
109         cm_ptr->mind_explanation = _("鏡魔法", "skill");
110         break;
111     case PlayerClassType::NINJA:
112         cm_ptr->use_mind = MindKindType::NINJUTSU;
113         cm_ptr->mind_explanation = _("精神", "skill");
114         break;
115     default:
116         cm_ptr->use_mind = (MindKindType)0;
117         cm_ptr->mind_explanation = _("超能力", "skill");
118         break;
119     }
120 }
121
122 static void decide_mind_ki_chance(PlayerType *player_ptr, cm_type *cm_ptr)
123 {
124     if (cm_ptr->use_mind != MindKindType::KI) {
125         return;
126     }
127
128     if (heavy_armor(player_ptr)) {
129         cm_ptr->chance += 20;
130     }
131
132     if (player_ptr->is_icky_wield[0]) {
133         cm_ptr->chance += 20;
134     } else if (has_melee_weapon(player_ptr, INVEN_MAIN_HAND)) {
135         cm_ptr->chance += 10;
136     }
137
138     if (player_ptr->is_icky_wield[1]) {
139         cm_ptr->chance += 20;
140     } else if (has_melee_weapon(player_ptr, INVEN_SUB_HAND)) {
141         cm_ptr->chance += 10;
142     }
143
144     if (cm_ptr->n == 5) {
145         for (int j = 0; j < get_current_ki(player_ptr) / 50; j++) {
146             cm_ptr->mana_cost += (j + 1) * 3 / 2;
147         }
148     }
149 }
150
151 static bool check_mind_hp_mp_sufficiency(PlayerType *player_ptr, cm_type *cm_ptr)
152 {
153     if ((cm_ptr->use_mind == MindKindType::BERSERKER) || (cm_ptr->use_mind == MindKindType::NINJUTSU)) {
154         if (cm_ptr->mana_cost > player_ptr->chp) {
155             msg_print(_("HPが足りません。", "You do not have enough hp to use this power."));
156             return false;
157         }
158
159         return true;
160     }
161
162     if (cm_ptr->mana_cost <= player_ptr->csp) {
163         return true;
164     }
165
166     msg_print(_("MPが足りません。", "You do not have enough mana to use this power."));
167     if (!over_exert) {
168         return false;
169     }
170
171     return input_check(_("それでも挑戦しますか? ", "Attempt it anyway? "));
172 }
173
174 static void decide_mind_chance(PlayerType *player_ptr, cm_type *cm_ptr)
175 {
176     if (cm_ptr->chance == 0) {
177         return;
178     }
179
180     cm_ptr->chance -= 3 * (cm_ptr->plev - cm_ptr->spell.min_lev);
181     cm_ptr->chance += player_ptr->to_m_chance;
182     cm_ptr->chance -= 3 * (adj_mag_stat[player_ptr->stat_index[mp_ptr->spell_stat]] - 1);
183     if ((cm_ptr->mana_cost > player_ptr->csp) && (cm_ptr->use_mind != MindKindType::BERSERKER) && (cm_ptr->use_mind != MindKindType::NINJUTSU)) {
184         cm_ptr->chance += 5 * (cm_ptr->mana_cost - player_ptr->csp);
185     }
186
187     cm_ptr->minfail = adj_mag_fail[player_ptr->stat_index[mp_ptr->spell_stat]];
188     if (cm_ptr->chance < cm_ptr->minfail) {
189         cm_ptr->chance = cm_ptr->minfail;
190     }
191
192     auto player_stun = player_ptr->effects()->stun();
193     cm_ptr->chance += player_stun->get_magic_chance_penalty();
194
195     if (cm_ptr->use_mind != MindKindType::KI) {
196         return;
197     }
198
199     if (heavy_armor(player_ptr)) {
200         cm_ptr->chance += 5;
201     }
202
203     if (player_ptr->is_icky_wield[0]) {
204         cm_ptr->chance += 5;
205     }
206
207     if (player_ptr->is_icky_wield[1]) {
208         cm_ptr->chance += 5;
209     }
210 }
211
212 static void check_mind_mindcrafter(PlayerType *player_ptr, cm_type *cm_ptr)
213 {
214     if (cm_ptr->use_mind != MindKindType::MINDCRAFTER) {
215         return;
216     }
217
218     if (cm_ptr->b < 5) {
219         msg_print(_("なんてこった!頭の中が真っ白になった!", "Oh, no! Your mind has gone blank!"));
220         lose_all_info(player_ptr);
221         return;
222     }
223
224     BadStatusSetter bss(player_ptr);
225     if (cm_ptr->b < 15) {
226         msg_print(_("奇妙な光景が目の前で踊っている...", "Weird visions seem to dance before your eyes..."));
227         (void)bss.mod_hallucination(5 + randint1(10));
228         return;
229     }
230
231     if (cm_ptr->b < 45) {
232         msg_print(_("あなたの頭は混乱した!", "Your brain is addled!"));
233         (void)bss.mod_confusion(randint1(8));
234         return;
235     }
236
237     if (cm_ptr->b < 90) {
238         (void)bss.mod_stun(randint1(8));
239         return;
240     }
241
242     msg_print(_(format("%sの力が制御できない氾流となって解放された!", cm_ptr->mind_explanation), "Your mind unleashes its power in an uncontrollable storm!"));
243     project(player_ptr, PROJECT_WHO_UNCTRL_POWER, 2 + cm_ptr->plev / 10, player_ptr->y, player_ptr->x, cm_ptr->plev * 2, AttributeType::MANA,
244         PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM);
245     player_ptr->csp = std::max(0, player_ptr->csp - cm_ptr->plev * std::max(1, cm_ptr->plev / 10));
246 }
247
248 static void check_mind_mirror_master(PlayerType *player_ptr, cm_type *cm_ptr)
249 {
250     if (cm_ptr->use_mind != MindKindType::MIRROR_MASTER) {
251         return;
252     }
253
254     if (cm_ptr->b < 51) {
255         return;
256     }
257
258     if (cm_ptr->b < 81) {
259         msg_print(_("鏡の世界の干渉を受けた!", "Weird visions seem to dance before your eyes..."));
260         teleport_player(player_ptr, 10, TELEPORT_PASSIVE);
261         return;
262     }
263
264     if (cm_ptr->b < 96) {
265         msg_print(_("まわりのものがキラキラ輝いている!", "Your brain is addled!"));
266         (void)BadStatusSetter(player_ptr).mod_hallucination(5 + randint1(10));
267         return;
268     }
269
270     msg_print(_(format("%sの力が制御できない氾流となって解放された!", cm_ptr->mind_explanation), "Your mind unleashes its power in an uncontrollable storm!"));
271     project(player_ptr, PROJECT_WHO_UNCTRL_POWER, 2 + cm_ptr->plev / 10, player_ptr->y, player_ptr->x, cm_ptr->plev * 2, AttributeType::MANA,
272         PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM);
273     player_ptr->csp = std::max(0, player_ptr->csp - cm_ptr->plev * std::max(1, cm_ptr->plev / 10));
274 }
275
276 static void check_mind_class(PlayerType *player_ptr, cm_type *cm_ptr)
277 {
278     if ((cm_ptr->use_mind == MindKindType::BERSERKER) || (cm_ptr->use_mind == MindKindType::NINJUTSU)) {
279         return;
280     }
281
282     if ((cm_ptr->use_mind == MindKindType::KI) && (cm_ptr->n != 5) && get_current_ki(player_ptr)) {
283         msg_print(_("気が散ってしまった...", "Your improved Force has gone away..."));
284         set_current_ki(player_ptr, true, 0);
285     }
286
287     if (randint1(100) >= (cm_ptr->chance / 2)) {
288         return;
289     }
290
291     cm_ptr->b = randint1(100);
292     check_mind_mindcrafter(player_ptr, cm_ptr);
293     check_mind_mirror_master(player_ptr, cm_ptr);
294 }
295
296 static bool switch_mind_class(PlayerType *player_ptr, cm_type *cm_ptr)
297 {
298     switch (cm_ptr->use_mind) {
299     case MindKindType::MINDCRAFTER:
300         cm_ptr->cast = cast_mindcrafter_spell(player_ptr, i2enum<MindMindcrafterType>(cm_ptr->n));
301         return true;
302     case MindKindType::KI:
303         cm_ptr->cast = cast_force_spell(player_ptr, i2enum<MindForceTrainerType>(cm_ptr->n));
304         return true;
305     case MindKindType::BERSERKER:
306         cm_ptr->cast = cast_berserk_spell(player_ptr, i2enum<MindBerserkerType>(cm_ptr->n));
307         return true;
308     case MindKindType::MIRROR_MASTER:
309         if (player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x].is_mirror()) {
310             cm_ptr->on_mirror = true;
311         }
312
313         cm_ptr->cast = cast_mirror_spell(player_ptr, i2enum<MindMirrorMasterType>(cm_ptr->n));
314         return true;
315     case MindKindType::NINJUTSU:
316         cm_ptr->cast = cast_ninja_spell(player_ptr, i2enum<MindNinjaType>(cm_ptr->n));
317         return true;
318     default:
319         msg_format(_("謎の能力:%d, %d", "Mystery power:%d, %d"), enum2i(cm_ptr->use_mind), cm_ptr->n);
320         return false;
321     }
322 }
323
324 static void mind_turn_passing(PlayerType *player_ptr, cm_type *cm_ptr)
325 {
326     PlayerEnergy energy(player_ptr);
327     if (cm_ptr->on_mirror && PlayerClass(player_ptr).equals(PlayerClassType::MIRROR_MASTER)) {
328         if (cm_ptr->n == 3 || cm_ptr->n == 5 || cm_ptr->n == 7 || cm_ptr->n == 16) {
329             energy.set_player_turn_energy(50);
330             return;
331         }
332     }
333
334     energy.set_player_turn_energy(100);
335 }
336
337 static bool judge_mind_chance(PlayerType *player_ptr, cm_type *cm_ptr)
338 {
339     if (randint0(100) >= cm_ptr->chance) {
340         sound(SOUND_ZAP);
341         return switch_mind_class(player_ptr, cm_ptr) && cm_ptr->cast;
342     }
343
344     if (flush_failure) {
345         flush();
346     }
347
348     msg_format(_("%sの集中に失敗した!", "You failed to concentrate hard enough for %s!"), cm_ptr->mind_explanation);
349     sound(SOUND_FAIL);
350     check_mind_class(player_ptr, cm_ptr);
351     return true;
352 }
353
354 static void mind_reflection(PlayerType *player_ptr, cm_type *cm_ptr)
355 {
356     int oops = cm_ptr->mana_cost - cm_ptr->old_csp;
357     if ((player_ptr->csp - cm_ptr->mana_cost) < 0) {
358         player_ptr->csp_frac = 0;
359     }
360
361     player_ptr->csp = std::max(0, player_ptr->csp - cm_ptr->mana_cost);
362     msg_print(_(format("%sを集中しすぎて気を失ってしまった!", cm_ptr->mind_explanation), "You faint from the effort!"));
363     (void)BadStatusSetter(player_ptr).mod_paralysis(randint1(5 * oops + 1));
364     if (randint0(100) >= 50) {
365         return;
366     }
367
368     bool perm = randint0(100) < 25;
369     msg_print(_("自分の精神を攻撃してしまった!", "You have damaged your mind!"));
370     (void)dec_stat(player_ptr, A_WIS, 15 + randint1(10), perm);
371 }
372
373 static void process_hard_concentration(PlayerType *player_ptr, cm_type *cm_ptr)
374 {
375     if ((cm_ptr->use_mind == MindKindType::BERSERKER) || (cm_ptr->use_mind == MindKindType::NINJUTSU)) {
376         take_hit(player_ptr, DAMAGE_USELIFE, cm_ptr->mana_cost, _("過度の集中", "concentrating too hard"));
377         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::HP);
378         return;
379     }
380
381     if (cm_ptr->mana_cost > cm_ptr->old_csp) {
382         mind_reflection(player_ptr, cm_ptr);
383         return;
384     }
385
386     player_ptr->csp -= cm_ptr->mana_cost;
387     if (player_ptr->csp < 0) {
388         player_ptr->csp = 0;
389     }
390
391     if ((cm_ptr->use_mind == MindKindType::MINDCRAFTER) && (cm_ptr->n == 13)) {
392         player_ptr->csp = 0;
393         player_ptr->csp_frac = 0;
394     }
395 }
396
397 /*!
398  * @brief 特殊技能コマンドのメインルーチン /
399  */
400 void do_cmd_mind(PlayerType *player_ptr)
401 {
402     cm_type tmp_cm;
403     cm_type *cm_ptr = initialize_cm_type(player_ptr, &tmp_cm);
404     if (cmd_limit_confused(player_ptr) || !MindPowerGetter(player_ptr).get_mind_power(&cm_ptr->n, false)) {
405         return;
406     }
407
408     switch_mind_kind(player_ptr, cm_ptr);
409     cm_ptr->spell = mind_powers[enum2i(cm_ptr->use_mind)].info[cm_ptr->n];
410     cm_ptr->chance = cm_ptr->spell.fail;
411     cm_ptr->mana_cost = cm_ptr->spell.mana_cost;
412     decide_mind_ki_chance(player_ptr, cm_ptr);
413     if (!check_mind_hp_mp_sufficiency(player_ptr, cm_ptr)) {
414         return;
415     }
416
417     decide_mind_chance(player_ptr, cm_ptr);
418     if (cm_ptr->chance > 95) {
419         cm_ptr->chance = 95;
420     }
421
422     if (!judge_mind_chance(player_ptr, cm_ptr)) {
423         return;
424     }
425
426     mind_turn_passing(player_ptr, cm_ptr);
427     process_hard_concentration(player_ptr, cm_ptr);
428     auto &rfu = RedrawingFlagsUpdater::get_instance();
429     rfu.set_flag(MainWindowRedrawingFlag::MP);
430     static constexpr auto flags = {
431         SubWindowRedrawingFlag::PLAYER,
432         SubWindowRedrawingFlag::SPELL,
433     };
434     rfu.set_flags(flags);
435 }
436
437 static MindKindType decide_use_mind_browse(PlayerType *player_ptr)
438 {
439     switch (player_ptr->pclass) {
440     case PlayerClassType::MINDCRAFTER:
441         return MindKindType::MINDCRAFTER;
442     case PlayerClassType::FORCETRAINER:
443         return MindKindType::KI;
444     case PlayerClassType::BERSERKER:
445         return MindKindType::BERSERKER;
446     case PlayerClassType::NINJA:
447         return MindKindType::NINJUTSU;
448     case PlayerClassType::MIRROR_MASTER:
449         return MindKindType::MIRROR_MASTER;
450     default:
451         return (MindKindType)0; // 実質PlayerClassType::MINDCRAFTERと同じ.
452     }
453 }
454
455 /*!
456  * @brief 現在プレイヤーが使用可能な特殊技能の一覧表示 /
457  */
458 void do_cmd_mind_browse(PlayerType *player_ptr)
459 {
460     SPELL_IDX n = 0;
461     MindKindType use_mind = decide_use_mind_browse(player_ptr);
462     screen_save();
463     while (true) {
464         if (!MindPowerGetter(player_ptr).get_mind_power(&n, true)) {
465             screen_load();
466             return;
467         }
468
469         term_erase(12, 21);
470         term_erase(12, 20);
471         term_erase(12, 19);
472         term_erase(12, 18);
473         term_erase(12, 17);
474         term_erase(12, 16);
475         display_wrap_around(mind_tips[enum2i(use_mind)][n], 62, 17, 15);
476
477         switch (use_mind) {
478         case MindKindType::MIRROR_MASTER:
479         case MindKindType::NINJUTSU:
480             prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
481             (void)inkey();
482             break;
483         default:
484             break;
485         }
486     }
487 }