OSDN Git Service

[Style] Apply clang-format to all *.cpp files
[hengbandforosx/hengbandosx.git] / src / mind / mind-ninja.cpp
1 #include "mind/mind-ninja.h"
2 #include "cmd-action/cmd-attack.h"
3 #include "cmd-item/cmd-throw.h"
4 #include "combat/combat-options-type.h"
5 #include "core/disturbance.h"
6 #include "core/player-redraw-types.h"
7 #include "effect/attribute-types.h"
8 #include "effect/effect-characteristics.h"
9 #include "effect/effect-processor.h"
10 #include "effect/spells-effect-util.h"
11 #include "floor/cave.h"
12 #include "floor/floor-object.h"
13 #include "floor/floor-util.h"
14 #include "floor/geometry.h"
15 #include "game-option/disturbance-options.h"
16 #include "grid/feature.h"
17 #include "grid/grid.h"
18 #include "inventory/inventory-slot-types.h"
19 #include "mind/mind-mirror-master.h"
20 #include "mind/mind-numbers.h"
21 #include "mind/mind-warrior.h"
22 #include "monster-race/monster-race.h"
23 #include "monster-race/race-flags-resistance.h"
24 #include "monster-race/race-indice-types.h"
25 #include "monster/monster-describer.h"
26 #include "monster/monster-status.h"
27 #include "monster/monster-update.h"
28 #include "object-enchant/trc-types.h"
29 #include "object/object-kind-hook.h"
30 #include "player-attack/player-attack-util.h"
31 #include "player-base/player-class.h"
32 #include "player-info/equipment-info.h"
33 #include "player-info/ninja-data-type.h"
34 #include "player-status/player-energy.h"
35 #include "player/attack-defense-types.h"
36 #include "player/player-status-flags.h"
37 #include "player/special-defense-types.h"
38 #include "spell-kind/spells-detection.h"
39 #include "spell-kind/spells-fetcher.h"
40 #include "spell-kind/spells-floor.h"
41 #include "spell-kind/spells-grid.h"
42 #include "spell-kind/spells-launcher.h"
43 #include "spell-kind/spells-lite.h"
44 #include "spell-kind/spells-perception.h"
45 #include "spell-kind/spells-teleport.h"
46 #include "spell/spells-status.h"
47 #include "status/action-setter.h"
48 #include "status/body-improvement.h"
49 #include "status/element-resistance.h"
50 #include "status/temporary-resistance.h"
51 #include "system/floor-type-definition.h"
52 #include "system/grid-type-definition.h"
53 #include "system/monster-race-definition.h"
54 #include "system/monster-type-definition.h"
55 #include "system/object-type-definition.h"
56 #include "system/player-type-definition.h"
57 #include "target/projection-path-calculator.h"
58 #include "target/target-checker.h"
59 #include "target/target-getter.h"
60 #include "timed-effect/player-stun.h"
61 #include "timed-effect/timed-effects.h"
62 #include "util/bit-flags-calculator.h"
63 #include "view/display-messages.h"
64
65 /*!
66  * @brief 変わり身処理
67  * @param player_ptr プレイヤーへの参照ポインタ
68  * @param success 判定成功上の処理ならばTRUE
69  * @return 作用が実際にあった場合TRUEを返す
70  */
71 bool kawarimi(PlayerType *player_ptr, bool success)
72 {
73     auto ninja_data = PlayerClass(player_ptr).get_specific_data<ninja_data_type>();
74     if (!ninja_data || !ninja_data->kawarimi) {
75         return false;
76     }
77
78     ObjectType forge;
79     auto *q_ptr = &forge;
80     if (player_ptr->is_dead) {
81         return false;
82     }
83
84     auto effects = player_ptr->effects();
85     if (player_ptr->confused || player_ptr->blind || player_ptr->paralyzed || player_ptr->hallucinated) {
86         return false;
87     }
88
89     if (effects->stun()->current() > randint0(200)) {
90         return false;
91     }
92
93     if (!success && one_in_(3)) {
94         msg_print(_("変わり身失敗!逃げられなかった。", "Kawarimi failed! You couldn't run away."));
95         ninja_data->kawarimi = false;
96         player_ptr->redraw |= (PR_STATUS);
97         return false;
98     }
99
100     POSITION y = player_ptr->y;
101     POSITION x = player_ptr->x;
102
103     teleport_player(player_ptr, 10 + randint1(90), TELEPORT_SPONTANEOUS);
104     q_ptr->wipe();
105     const int SV_WOODEN_STATUE = 0;
106     q_ptr->prep(lookup_kind(ItemKindType::STATUE, SV_WOODEN_STATUE));
107
108     q_ptr->pval = MON_NINJA;
109     (void)drop_near(player_ptr, q_ptr, -1, y, x);
110
111     if (success) {
112         msg_print(_("攻撃を受ける前に素早く身をひるがえした。", "You have turned around just before the attack hit you."));
113     } else {
114         msg_print(_("変わり身失敗!攻撃を受けてしまった。", "Kawarimi failed! You are hit by the attack."));
115     }
116
117     ninja_data->kawarimi = false;
118     player_ptr->redraw |= (PR_STATUS);
119     return true;
120 }
121
122 /*!
123  * @brief 入身処理 / "Rush Attack" routine for Samurai or Ninja
124  * @param player_ptr プレイヤーへの参照ポインタ
125  * @param mdeath 目標モンスターが死亡したかを返す
126  * @return 作用が実際にあった場合TRUEを返す /  Return value is for checking "done"
127  */
128 bool rush_attack(PlayerType *player_ptr, bool *mdeath)
129 {
130     if (mdeath) {
131         *mdeath = false;
132     }
133
134     project_length = 5;
135     DIRECTION dir;
136     if (!get_aim_dir(player_ptr, &dir)) {
137         return false;
138     }
139
140     int tx = player_ptr->x + project_length * ddx[dir];
141     int ty = player_ptr->y + project_length * ddy[dir];
142
143     if ((dir == 5) && target_okay(player_ptr)) {
144         tx = target_col;
145         ty = target_row;
146     }
147
148     int tm_idx = 0;
149     auto *floor_ptr = player_ptr->current_floor_ptr;
150     if (in_bounds(floor_ptr, ty, tx)) {
151         tm_idx = floor_ptr->grid_array[ty][tx].m_idx;
152     }
153
154     uint16_t path_g[32];
155     int path_n = projection_path(player_ptr, path_g, project_length, player_ptr->y, player_ptr->x, ty, tx, PROJECT_STOP | PROJECT_KILL);
156     project_length = 0;
157     if (!path_n) {
158         return true;
159     }
160
161     ty = player_ptr->y;
162     tx = player_ptr->x;
163     bool tmp_mdeath = false;
164     bool moved = false;
165     for (int i = 0; i < path_n; i++) {
166         monster_type *m_ptr;
167
168         int ny = get_grid_y(path_g[i]);
169         int nx = get_grid_x(path_g[i]);
170
171         if (is_cave_empty_bold(player_ptr, ny, nx) && player_can_enter(player_ptr, floor_ptr->grid_array[ny][nx].feat, 0)) {
172             ty = ny;
173             tx = nx;
174             continue;
175         }
176
177         if (!floor_ptr->grid_array[ny][nx].m_idx) {
178             if (tm_idx) {
179                 msg_print(_("失敗!", "Failed!"));
180             } else {
181                 msg_print(_("ここには入身では入れない。", "You can't move to that place."));
182             }
183
184             break;
185         }
186
187         if (!player_bold(player_ptr, ty, tx)) {
188             teleport_player_to(player_ptr, ty, tx, TELEPORT_NONMAGICAL);
189         }
190         update_monster(player_ptr, floor_ptr->grid_array[ny][nx].m_idx, true);
191
192         m_ptr = &floor_ptr->m_list[floor_ptr->grid_array[ny][nx].m_idx];
193         if (tm_idx != floor_ptr->grid_array[ny][nx].m_idx) {
194 #ifdef JP
195             msg_format("%s%sが立ちふさがっている!", tm_idx ? "別の" : "", m_ptr->ml ? "モンスター" : "何か");
196 #else
197             msg_format("There is %s in the way!", m_ptr->ml ? (tm_idx ? "another monster" : "a monster") : "someone");
198 #endif
199         } else if (!player_bold(player_ptr, ty, tx)) {
200             GAME_TEXT m_name[MAX_NLEN];
201             monster_desc(player_ptr, m_name, m_ptr, 0);
202             msg_format(_("素早く%sの懐に入り込んだ!", "You quickly jump in and attack %s!"), m_name);
203         }
204
205         if (!player_bold(player_ptr, ty, tx)) {
206             teleport_player_to(player_ptr, ty, tx, TELEPORT_NONMAGICAL);
207         }
208         moved = true;
209         tmp_mdeath = do_cmd_attack(player_ptr, ny, nx, HISSATSU_NYUSIN);
210
211         break;
212     }
213
214     if (!moved && !player_bold(player_ptr, ty, tx)) {
215         teleport_player_to(player_ptr, ty, tx, TELEPORT_NONMAGICAL);
216     }
217
218     if (mdeath) {
219         *mdeath = tmp_mdeath;
220     }
221     return true;
222 }
223
224 /*!
225  * @brief 盗賊と忍者における不意打ち
226  * @param player_ptr プレイヤーへの参照ポインタ
227  * @param pa_ptr 直接攻撃構造体への参照ポインタ
228  */
229 void process_surprise_attack(PlayerType *player_ptr, player_attack_type *pa_ptr)
230 {
231     auto *r_ptr = &r_info[pa_ptr->m_ptr->r_idx];
232     if (!has_melee_weapon(player_ptr, INVEN_MAIN_HAND + pa_ptr->hand) || player_ptr->is_icky_wield[pa_ptr->hand]) {
233         return;
234     }
235
236     int tmp = player_ptr->lev * 6 + (player_ptr->skill_stl + 10) * 4;
237     if (player_ptr->monlite && (pa_ptr->mode != HISSATSU_NYUSIN)) {
238         tmp /= 3;
239     }
240     if (has_aggravate(player_ptr)) {
241         tmp /= 2;
242     }
243     if (r_ptr->level > (player_ptr->lev * player_ptr->lev / 20 + 10)) {
244         tmp /= 3;
245     }
246
247     auto ninja_data = PlayerClass(player_ptr).get_specific_data<ninja_data_type>();
248     if (monster_csleep_remaining(pa_ptr->m_ptr) && pa_ptr->m_ptr->ml) {
249         /* Can't backstab creatures that we can't see, right? */
250         pa_ptr->backstab = true;
251     } else if ((ninja_data && ninja_data->s_stealth) && (randint0(tmp) > (r_ptr->level + 20)) &&
252                pa_ptr->m_ptr->ml && !r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_ALL)) {
253         pa_ptr->surprise_attack = true;
254     } else if (monster_fear_remaining(pa_ptr->m_ptr) && pa_ptr->m_ptr->ml) {
255         pa_ptr->stab_fleeing = true;
256     }
257 }
258
259 void print_surprise_attack(player_attack_type *pa_ptr)
260 {
261     if (pa_ptr->backstab) {
262         msg_format(_("あなたは冷酷にも眠っている無力な%sを突き刺した!", "You cruelly stab the helpless, sleeping %s!"), pa_ptr->m_name);
263     } else if (pa_ptr->surprise_attack) {
264         msg_format(_("不意を突いて%sに強烈な一撃を喰らわせた!", "You make surprise attack, and hit %s with a powerful blow!"), pa_ptr->m_name);
265     } else if (pa_ptr->stab_fleeing) {
266         msg_format(_("逃げる%sを背中から突き刺した!", "You backstab the fleeing %s!"), pa_ptr->m_name);
267     } else if (!pa_ptr->monk_attack) {
268         msg_format(_("%sを攻撃した。", "You hit %s."), pa_ptr->m_name);
269     }
270 }
271
272 /*!
273  * @brief 盗賊と忍者における不意打ちのダメージ計算
274  * @param player_ptr プレイヤーへの参照ポインタ
275  * @param pa_ptr 直接攻撃構造体への参照ポインタ
276  */
277 void calc_surprise_attack_damage(PlayerType *player_ptr, player_attack_type *pa_ptr)
278 {
279     if (pa_ptr->backstab) {
280         pa_ptr->attack_damage *= (3 + (player_ptr->lev / 20));
281         return;
282     }
283
284     if (pa_ptr->surprise_attack) {
285         pa_ptr->attack_damage = pa_ptr->attack_damage * (5 + (player_ptr->lev * 2 / 25)) / 2;
286         return;
287     }
288
289     if (pa_ptr->stab_fleeing) {
290         pa_ptr->attack_damage = (3 * pa_ptr->attack_damage) / 2;
291     }
292 }
293
294 /*!
295  * @brief 速駆け処理
296  * @param player_ptr プレイヤーへの参照ポインタ
297  * @return 常にTRUE
298  */
299 bool hayagake(PlayerType *player_ptr)
300 {
301     PlayerEnergy energy(player_ptr);
302     if (player_ptr->action == ACTION_HAYAGAKE) {
303         set_action(player_ptr, ACTION_NONE);
304         energy.reset_player_turn();
305         return true;
306     }
307
308     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x];
309     auto *f_ptr = &f_info[g_ptr->feat];
310
311     if (f_ptr->flags.has_not(FloorFeatureType::PROJECT) || (!player_ptr->levitation && f_ptr->flags.has(FloorFeatureType::DEEP))) {
312         msg_print(_("ここでは素早く動けない。", "You cannot run in here."));
313     } else {
314         set_action(player_ptr, ACTION_HAYAGAKE);
315     }
316
317     energy.reset_player_turn();
318     return true;
319 }
320
321 /*!
322  * @brief 超隠密状態をセットする
323  * @param set TRUEならば超隠密状態になる。
324  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
325  */
326 bool set_superstealth(PlayerType *player_ptr, bool set)
327 {
328     bool notice = false;
329
330     auto ninja_data = PlayerClass(player_ptr).get_specific_data<ninja_data_type>();
331     if (!ninja_data || player_ptr->is_dead) {
332         return false;
333     }
334
335     if (set) {
336         if (!ninja_data->s_stealth) {
337             if (player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_MNLT) {
338                 msg_print(_("敵の目から薄い影の中に覆い隠された。", "You are mantled in weak shadow from ordinary eyes."));
339                 player_ptr->monlite = player_ptr->old_monlite = true;
340             } else {
341                 msg_print(_("敵の目から影の中に覆い隠された!", "You are mantled in shadow from ordinary eyes!"));
342                 player_ptr->monlite = player_ptr->old_monlite = false;
343             }
344
345             notice = true;
346             ninja_data->s_stealth = true;
347         }
348     } else {
349         if (ninja_data->s_stealth) {
350             msg_print(_("再び敵の目にさらされるようになった。", "You are exposed to common sight once more."));
351             notice = true;
352             ninja_data->s_stealth = false;
353         }
354     }
355
356     if (!notice) {
357         return false;
358     }
359     player_ptr->redraw |= (PR_STATUS);
360
361     if (disturb_state) {
362         disturb(player_ptr, false, false);
363     }
364     return true;
365 }
366
367 /*!
368  * @brief 忍術の発動 /
369  * do_cmd_cast calls this function if the player's class is 'ninja'.
370  * @param player_ptr プレイヤーへの参照ポインタ
371  * @param spell 発動する特殊技能のID
372  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
373  */
374 bool cast_ninja_spell(PlayerType *player_ptr, mind_ninja_type spell)
375 {
376     POSITION x = 0, y = 0;
377     DIRECTION dir;
378     PLAYER_LEVEL plev = player_ptr->lev;
379     auto ninja_data = PlayerClass(player_ptr).get_specific_data<ninja_data_type>();
380     switch (spell) {
381     case DARKNESS_CREATION:
382         (void)unlite_area(player_ptr, 0, 3);
383         break;
384     case DETECT_NEAR:
385         if (plev > 44) {
386             wiz_lite(player_ptr, true);
387         }
388
389         detect_monsters_normal(player_ptr, DETECT_RAD_DEFAULT);
390         if (plev > 4) {
391             detect_traps(player_ptr, DETECT_RAD_DEFAULT, true);
392             detect_doors(player_ptr, DETECT_RAD_DEFAULT);
393             detect_stairs(player_ptr, DETECT_RAD_DEFAULT);
394         }
395
396         if (plev > 14) {
397             detect_objects_normal(player_ptr, DETECT_RAD_DEFAULT);
398         }
399
400         break;
401     case HIDE_LEAVES:
402         teleport_player(player_ptr, 10, TELEPORT_SPONTANEOUS);
403         break;
404     case KAWARIMI:
405         if (ninja_data && !ninja_data->kawarimi) {
406             msg_print(_("敵の攻撃に対して敏感になった。", "You are now prepared to evade any attacks."));
407             ninja_data->kawarimi = true;
408             player_ptr->redraw |= (PR_STATUS);
409         }
410
411         break;
412     case ABSCONDING:
413         teleport_player(player_ptr, player_ptr->lev * 5, TELEPORT_SPONTANEOUS);
414         break;
415     case HIT_AND_AWAY:
416         if (!hit_and_away(player_ptr)) {
417             return false;
418         }
419
420         break;
421     case BIND_MONSTER:
422         if (!get_aim_dir(player_ptr, &dir)) {
423             return false;
424         }
425
426         (void)stasis_monster(player_ptr, dir);
427         break;
428     case ANCIENT_KNOWLEDGE:
429         return ident_spell(player_ptr, false);
430     case FLOATING:
431         set_tim_levitation(player_ptr, randint1(20) + 20, false);
432         break;
433     case HIDE_FLAMES:
434         fire_ball(player_ptr, AttributeType::FIRE, 0, 50 + plev, plev / 10 + 2);
435         teleport_player(player_ptr, 30, TELEPORT_SPONTANEOUS);
436         set_oppose_fire(player_ptr, (TIME_EFFECT)plev, false);
437         break;
438     case NYUSIN:
439         return rush_attack(player_ptr, nullptr);
440     case SYURIKEN_SPREADING: {
441         for (int i = 0; i < 8; i++) {
442             OBJECT_IDX slot;
443
444             for (slot = 0; slot < INVEN_PACK; slot++) {
445                 if (player_ptr->inventory_list[slot].tval == ItemKindType::SPIKE) {
446                     break;
447                 }
448             }
449
450             if (slot == INVEN_PACK) {
451                 if (!i) {
452                     msg_print(_("くさびを持っていない。", "You have no Iron Spikes."));
453                 } else {
454                     msg_print(_("くさびがなくなった。", "You have no more Iron Spikes."));
455                 }
456
457                 return false;
458             }
459
460             (void)ThrowCommand(player_ptr).do_cmd_throw(1, false, slot);
461             PlayerEnergy(player_ptr).set_player_turn_energy(100);
462         }
463
464         break;
465     }
466     case CHAIN_HOOK:
467         (void)fetch_monster(player_ptr);
468         break;
469     case SMOKE_BALL:
470         if (!get_aim_dir(player_ptr, &dir)) {
471             return false;
472         }
473
474         fire_ball(player_ptr, AttributeType::OLD_CONF, dir, plev * 3, 3);
475         break;
476     case SWAP_POSITION:
477         project_length = -1;
478         if (!get_aim_dir(player_ptr, &dir)) {
479             project_length = 0;
480             return false;
481         }
482
483         project_length = 0;
484         (void)teleport_swap(player_ptr, dir);
485         break;
486     case EXPLOSIVE_RUNE:
487         create_rune_explosion(player_ptr, player_ptr->y, player_ptr->x);
488         break;
489     case HIDE_MUD:
490         (void)set_pass_wall(player_ptr, randint1(plev / 2) + plev / 2, false);
491         set_oppose_acid(player_ptr, (TIME_EFFECT)plev, false);
492         break;
493     case HIDE_MIST:
494         fire_ball(player_ptr, AttributeType::POIS, 0, 75 + plev * 2 / 3, plev / 5 + 2);
495         fire_ball(player_ptr, AttributeType::HYPODYNAMIA, 0, 75 + plev * 2 / 3, plev / 5 + 2);
496         fire_ball(player_ptr, AttributeType::CONFUSION, 0, 75 + plev * 2 / 3, plev / 5 + 2);
497         teleport_player(player_ptr, 30, TELEPORT_SPONTANEOUS);
498         break;
499     case PURGATORY_FLAME: {
500         int num = damroll(3, 9);
501         for (int k = 0; k < num; k++) {
502             AttributeType typ = one_in_(2) ? AttributeType::FIRE : one_in_(3) ? AttributeType::NETHER
503                                                                               : AttributeType::PLASMA;
504             int attempts = 1000;
505             while (attempts--) {
506                 scatter(player_ptr, &y, &x, player_ptr->y, player_ptr->x, 4, PROJECT_NONE);
507                 if (!player_bold(player_ptr, y, x)) {
508                     break;
509                 }
510             }
511
512             project(player_ptr, 0, 0, y, x, damroll(6 + plev / 8, 10), typ, (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL));
513         }
514
515         break;
516     }
517     case ALTER_EGO:
518         set_multishadow(player_ptr, 6 + randint1(6), false);
519         break;
520     default:
521         msg_print(_("なに?", "Zap?"));
522         break;
523     }
524     return true;
525 }