OSDN Git Service

[Refactor] #3286 Removed player-redraw-types.h
[hengbandforosx/hengbandosx.git] / src / mind / mind-force-trainer.cpp
1 #include "mind/mind-force-trainer.h"
2 #include "avatar/avatar.h"
3 #include "core/disturbance.h"
4 #include "core/stuff-handler.h"
5 #include "effect/attribute-types.h"
6 #include "effect/spells-effect-util.h"
7 #include "floor/cave.h"
8 #include "floor/geometry.h"
9 #include "game-option/disturbance-options.h"
10 #include "grid/grid.h"
11 #include "mind/mind-magic-resistance.h"
12 #include "mind/mind-numbers.h"
13 #include "monster-floor/monster-summon.h"
14 #include "monster-floor/place-monster-types.h"
15 #include "monster-race/monster-race.h"
16 #include "monster-race/race-brightness-mask.h"
17 #include "monster-race/race-flags7.h"
18 #include "monster/monster-describer.h"
19 #include "monster/monster-status.h"
20 #include "monster/monster-update.h"
21 #include "pet/pet-util.h"
22 #include "player-base/player-class.h"
23 #include "player-info/equipment-info.h"
24 #include "player-info/force-trainer-data-type.h"
25 #include "player/player-damage.h"
26 #include "spell-kind/spells-launcher.h"
27 #include "spell-kind/spells-lite.h"
28 #include "spell/summon-types.h"
29 #include "status/temporary-resistance.h"
30 #include "system/floor-type-definition.h"
31 #include "system/grid-type-definition.h"
32 #include "system/monster-entity.h"
33 #include "system/monster-race-info.h"
34 #include "system/player-type-definition.h"
35 #include "system/redrawing-flags-updater.h"
36 #include "target/projection-path-calculator.h"
37 #include "target/target-checker.h"
38 #include "target/target-getter.h"
39 #include "target/target-setter.h"
40 #include "target/target-types.h"
41 #include "view/display-messages.h"
42
43 /*!
44  * @brief 練気術師が「練気」で溜めた気の量を返す
45  * @param player_ptr プレイヤーの参照ポインタ
46  * @return 現在溜まっている気の量
47  */
48 int32_t get_current_ki(PlayerType *player_ptr)
49 {
50     auto data = PlayerClass(player_ptr).get_specific_data<force_trainer_data_type>();
51
52     return data ? data->ki : 0;
53 }
54
55 /*!
56  * @brief 練気術師において、気を溜める
57  * @param player_ptr プレイヤーの参照ポインタ
58  * @param is_reset TRUEなら気の量をkiにセットし、FALSEなら加減算を行う
59  * @param ki 気の量
60  */
61 void set_current_ki(PlayerType *player_ptr, bool is_reset, int32_t ki)
62 {
63     auto data = PlayerClass(player_ptr).get_specific_data<force_trainer_data_type>();
64     if (!data) {
65         return;
66     }
67
68     if (is_reset) {
69         data->ki = ki;
70         return;
71     }
72
73     data->ki += ki;
74 }
75
76 bool clear_mind(PlayerType *player_ptr)
77 {
78     if (total_friends) {
79         msg_print(_("今はペットを操ることに集中していないと。", "Your pets demand all of your attention."));
80         return false;
81     }
82
83     msg_print(_("少し頭がハッキリした。", "You feel your head clear a little."));
84
85     player_ptr->csp += (3 + player_ptr->lev / 20);
86     if (player_ptr->csp >= player_ptr->msp) {
87         player_ptr->csp = player_ptr->msp;
88         player_ptr->csp_frac = 0;
89     }
90
91     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
92     return true;
93 }
94
95 /*!
96  * @brief 光速移動の継続時間をセットする / Set "lightspeed", notice observable changes
97  * @param v 継続時間
98  * @param do_dec 現在の継続時間より長い値のみ上書きする
99  */
100 void set_lightspeed(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
101 {
102     bool notice = false;
103     v = (v > 10000) ? 10000 : (v < 0) ? 0
104                                       : v;
105
106     if (player_ptr->is_dead) {
107         return;
108     }
109
110     if (player_ptr->wild_mode) {
111         v = 0;
112     }
113
114     if (v) {
115         if (player_ptr->lightspeed && !do_dec) {
116             if (player_ptr->lightspeed > v) {
117                 return;
118             }
119         } else if (!player_ptr->lightspeed) {
120             msg_print(_("非常に素早く動けるようになった!", "You feel yourself moving extremely fast!"));
121             notice = true;
122             chg_virtue(player_ptr, Virtue::PATIENCE, -1);
123             chg_virtue(player_ptr, Virtue::DILIGENCE, 1);
124         }
125     } else {
126         if (player_ptr->lightspeed) {
127             msg_print(_("動きの素早さがなくなったようだ。", "You feel yourself slow down."));
128             notice = true;
129         }
130     }
131
132     player_ptr->lightspeed = v;
133
134     if (!notice) {
135         return;
136     }
137
138     if (disturb_state) {
139         disturb(player_ptr, false, false);
140     }
141
142     RedrawingFlagsUpdater::get_instance().set_flag(StatusRedrawingFlag::BONUS);
143     handle_stuff(player_ptr);
144 }
145
146 /*!
147  * @brief 一時的闘気のオーラの継続時間をセットする / Set "tim_sh_touki", notice observable changes
148  * @param v 継続時間
149  * @param do_dec 現在の継続時間より長い値のみ上書きする
150  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
151  */
152 bool set_tim_sh_force(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
153 {
154     bool notice = false;
155     v = (v > 10000) ? 10000 : (v < 0) ? 0
156                                       : v;
157
158     if (player_ptr->is_dead) {
159         return false;
160     }
161
162     if (v) {
163         if (player_ptr->tim_sh_touki && !do_dec) {
164             if (player_ptr->tim_sh_touki > v) {
165                 return false;
166             }
167         } else if (!player_ptr->tim_sh_touki) {
168             msg_print(_("体が闘気のオーラで覆われた。", "You are enveloped by an aura of the Force!"));
169             notice = true;
170         }
171     } else {
172         if (player_ptr->tim_sh_touki) {
173             msg_print(_("闘気が消えた。", "The aura of the Force disappeared."));
174             notice = true;
175         }
176     }
177
178     player_ptr->tim_sh_touki = v;
179     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
180     if (!notice) {
181         return false;
182     }
183
184     if (disturb_state) {
185         disturb(player_ptr, false, false);
186     }
187
188     handle_stuff(player_ptr);
189     return true;
190 }
191
192 /*!
193  * @brief 衝波
194  * @param player_ptr プレイヤーへの参照ポインタ
195  * @return 命中したらTRUE
196  */
197 bool shock_power(PlayerType *player_ptr)
198 {
199     int boost = get_current_ki(player_ptr);
200     if (heavy_armor(player_ptr)) {
201         boost /= 2;
202     }
203
204     project_length = 1;
205     DIRECTION dir;
206     if (!get_aim_dir(player_ptr, &dir)) {
207         return false;
208     }
209
210     POSITION y = player_ptr->y + ddy[dir];
211     POSITION x = player_ptr->x + ddx[dir];
212     PLAYER_LEVEL plev = player_ptr->lev;
213     int dam = damroll(8 + ((plev - 5) / 4) + boost / 12, 8);
214     fire_beam(player_ptr, AttributeType::MISSILE, dir, dam);
215     if (!player_ptr->current_floor_ptr->grid_array[y][x].m_idx) {
216         return true;
217     }
218
219     POSITION ty = y, tx = x;
220     POSITION oy = y, ox = x;
221     MONSTER_IDX m_idx = player_ptr->current_floor_ptr->grid_array[y][x].m_idx;
222     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
223     auto *r_ptr = &monraces_info[m_ptr->r_idx];
224     const auto m_name = monster_desc(player_ptr, m_ptr, 0);
225
226     if (randint1(r_ptr->level * 3 / 2) > randint0(dam / 2) + dam / 2) {
227         msg_format(_("%sは飛ばされなかった。", "%s^ was not blown away."), m_name.data());
228         return true;
229     }
230
231     for (int i = 0; i < 5; i++) {
232         y += ddy[dir];
233         x += ddx[dir];
234         if (is_cave_empty_bold(player_ptr, y, x)) {
235             ty = y;
236             tx = x;
237         } else {
238             break;
239         }
240     }
241
242     bool is_shock_successful = ty != oy;
243     is_shock_successful |= tx != ox;
244     if (is_shock_successful) {
245         return true;
246     }
247
248     msg_format(_("%sを吹き飛ばした!", "You blow %s away!"), m_name.data());
249     player_ptr->current_floor_ptr->grid_array[oy][ox].m_idx = 0;
250     player_ptr->current_floor_ptr->grid_array[ty][tx].m_idx = m_idx;
251     m_ptr->fy = ty;
252     m_ptr->fx = tx;
253
254     update_monster(player_ptr, m_idx, true);
255     lite_spot(player_ptr, oy, ox);
256     lite_spot(player_ptr, ty, tx);
257
258     if (r_ptr->brightness_flags.has_any_of(ld_mask)) {
259         RedrawingFlagsUpdater::get_instance().set_flag(StatusRedrawingFlag::MONSTER_LITE);
260     }
261
262     return true;
263 }
264
265 /*!
266  * @brief 練気術の発動 /
267  * do_cmd_cast calls this function if the player's class is 'ForceTrainer'.
268  * @param spell 発動する特殊技能のID
269  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
270  */
271 bool cast_force_spell(PlayerType *player_ptr, MindForceTrainerType spell)
272 {
273     DIRECTION dir;
274     PLAYER_LEVEL plev = player_ptr->lev;
275     int boost = get_current_ki(player_ptr);
276     if (heavy_armor(player_ptr)) {
277         boost /= 2;
278     }
279
280     auto &rfu = RedrawingFlagsUpdater::get_instance();
281     switch (spell) {
282     case MindForceTrainerType::SMALL_FORCE_BALL:
283         if (!get_aim_dir(player_ptr, &dir)) {
284             return false;
285         }
286
287         fire_ball(player_ptr, AttributeType::MISSILE, dir, damroll(3 + ((plev - 1) / 5) + boost / 12, 4), 0);
288         break;
289     case MindForceTrainerType::FLASH_LIGHT:
290         (void)lite_area(player_ptr, damroll(2, (plev / 2)), (plev / 10) + 1);
291         break;
292     case MindForceTrainerType::FLYING_TECHNIQUE:
293         set_tim_levitation(player_ptr, randint1(30) + 30 + boost / 5, false);
294         break;
295     case MindForceTrainerType::KAMEHAMEHA:
296         project_length = plev / 8 + 3;
297         if (!get_aim_dir(player_ptr, &dir)) {
298             return false;
299         }
300
301         fire_beam(player_ptr, AttributeType::MISSILE, dir, damroll(5 + ((plev - 1) / 5) + boost / 10, 5));
302         break;
303     case MindForceTrainerType::MAGIC_RESISTANCE:
304         set_resist_magic(player_ptr, randint1(20) + 20 + boost / 5, false);
305         break;
306     case MindForceTrainerType::IMPROVE_FORCE:
307         msg_print(_("気を練った。", "You improved the Force."));
308         set_current_ki(player_ptr, false, 70 + plev);
309         rfu.set_flag(StatusRedrawingFlag::BONUS);
310         if (randint1(get_current_ki(player_ptr)) > (plev * 4 + 120)) {
311             msg_print(_("気が暴走した!", "The Force exploded!"));
312             fire_ball(player_ptr, AttributeType::MANA, 0, get_current_ki(player_ptr) / 2, 10);
313             auto data = PlayerClass(player_ptr).get_specific_data<force_trainer_data_type>();
314             take_hit(player_ptr, DAMAGE_LOSELIFE, data->ki / 2, _("気の暴走", "Explosion of the Force"));
315         } else {
316             return true;
317         }
318
319         break;
320     case MindForceTrainerType::AURA_OF_FORCE:
321         set_tim_sh_force(player_ptr, randint1(plev / 2) + 15 + boost / 7, false);
322         break;
323     case MindForceTrainerType::SHOCK_POWER:
324         return shock_power(player_ptr);
325         break;
326     case MindForceTrainerType::LARGE_FORCE_BALL:
327         if (!get_aim_dir(player_ptr, &dir)) {
328             return false;
329         }
330
331         fire_ball(player_ptr, AttributeType::MISSILE, dir, damroll(10, 6) + plev * 3 / 2 + boost * 3 / 5, (plev < 30) ? 2 : 3);
332         break;
333     case MindForceTrainerType::DISPEL_MAGIC: {
334         if (!target_set(player_ptr, TARGET_KILL)) {
335             return false;
336         }
337
338         MONSTER_IDX m_idx = player_ptr->current_floor_ptr->grid_array[target_row][target_col].m_idx;
339         const auto is_projectable = projectable(player_ptr, player_ptr->y, player_ptr->x, target_row, target_col);
340         if ((m_idx == 0) || !player_has_los_bold(player_ptr, target_row, target_col) || !is_projectable) {
341             break;
342         }
343
344         dispel_monster_status(player_ptr, m_idx);
345         break;
346     }
347     case MindForceTrainerType::SUMMON_GHOST: {
348         bool success = false;
349         for (int i = 0; i < 1 + boost / 100; i++) {
350             if (summon_specific(player_ptr, -1, player_ptr->y, player_ptr->x, plev, SUMMON_PHANTOM, PM_FORCE_PET)) {
351                 success = true;
352             }
353         }
354
355         if (success) {
356             msg_print(_("御用でございますが、御主人様?", "'Your wish, master?'"));
357         } else {
358             msg_print(_("何も現れなかった。", "Nothing happens."));
359         }
360
361         break;
362     }
363     case MindForceTrainerType::EXPLODING_FLAME:
364         fire_ball(player_ptr, AttributeType::FIRE, 0, 200 + (2 * plev) + boost * 2, 10);
365         break;
366     case MindForceTrainerType::SUPER_KAMEHAMEHA:
367         if (!get_aim_dir(player_ptr, &dir)) {
368             return false;
369         }
370
371         fire_beam(player_ptr, AttributeType::MANA, dir, damroll(10 + (plev / 2) + boost * 3 / 10, 15));
372         break;
373     case MindForceTrainerType::LIGHT_SPEED:
374         set_lightspeed(player_ptr, randint1(16) + 16 + boost / 20, false);
375         break;
376     default:
377         msg_print(_("なに?", "Zap?"));
378     }
379
380     set_current_ki(player_ptr, true, 0);
381     rfu.set_flag(StatusRedrawingFlag::BONUS);
382     return true;
383 }