OSDN Git Service

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