OSDN Git Service

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