OSDN Git Service

Merge pull request #3302 from sikabane-works/release/3.0.0Alpha83
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-teleport.cpp
1 /*!
2  * @brief テレポート魔法全般
3  * @date 2020/06/04
4  * @author Hourier
5  */
6
7 #include "spell-kind/spells-teleport.h"
8 #include "avatar/avatar.h"
9 #include "core/asking-player.h"
10 #include "core/player-update-types.h"
11 #include "core/speed-table.h"
12 #include "effect/attribute-types.h"
13 #include "effect/effect-characteristics.h"
14 #include "floor/cave.h"
15 #include "floor/geometry.h"
16 #include "floor/line-of-sight.h"
17 #include "grid/grid.h"
18 #include "inventory/inventory-slot-types.h"
19 #include "main/sound-definitions-table.h"
20 #include "main/sound-of-music.h"
21 #include "monster-floor/monster-move.h"
22 #include "monster-race/monster-race.h"
23 #include "monster-race/race-brightness-mask.h"
24 #include "monster-race/race-flags-resistance.h"
25 #include "monster-race/race-flags7.h"
26 #include "monster/monster-info.h"
27 #include "monster/monster-status-setter.h"
28 #include "monster/monster-status.h"
29 #include "monster/monster-update.h"
30 #include "mutation/mutation-flag-types.h"
31 #include "object-enchant/tr-types.h"
32 #include "object/object-flags.h"
33 #include "player-base/player-class.h"
34 #include "player/player-move.h"
35 #include "player/player-status.h"
36 #include "spell-kind/spells-launcher.h"
37 #include "system/floor-type-definition.h"
38 #include "system/grid-type-definition.h"
39 #include "system/item-entity.h"
40 #include "system/monster-entity.h"
41 #include "system/monster-race-info.h"
42 #include "system/player-type-definition.h"
43 #include "target/grid-selector.h"
44 #include "target/target-checker.h"
45 #include "util/bit-flags-calculator.h"
46 #include "view/display-messages.h"
47 #include "world/world.h"
48 #include <array>
49
50 /*!
51  * @brief モンスターとの位置交換処理 / Switch position with a monster.
52  * @param player_ptr プレイヤーへの参照ポインタ
53  * @param dir 方向(5ならばグローバル変数 target_col/target_row の座標を目標にする)
54  * @return 作用が実際にあった場合TRUEを返す
55  */
56 bool teleport_swap(PlayerType *player_ptr, DIRECTION dir)
57 {
58     POSITION tx, ty;
59     if ((dir == 5) && target_okay(player_ptr)) {
60         tx = target_col;
61         ty = target_row;
62     } else {
63         tx = player_ptr->x + ddx[dir];
64         ty = player_ptr->y + ddy[dir];
65     }
66
67     if (player_ptr->anti_tele) {
68         msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
69         return false;
70     }
71
72     grid_type *g_ptr;
73     g_ptr = &player_ptr->current_floor_ptr->grid_array[ty][tx];
74     if (!g_ptr->m_idx || (g_ptr->m_idx == player_ptr->riding)) {
75         msg_print(_("それとは場所を交換できません。", "You can't trade places with that!"));
76         return false;
77     }
78
79     if ((g_ptr->is_icky()) || (distance(ty, tx, player_ptr->y, player_ptr->x) > player_ptr->lev * 3 / 2 + 10)) {
80         msg_print(_("失敗した。", "Failed to swap."));
81         return false;
82     }
83
84     MonsterEntity *m_ptr;
85     MonsterRaceInfo *r_ptr;
86     m_ptr = &player_ptr->current_floor_ptr->m_list[g_ptr->m_idx];
87     r_ptr = &monraces_info[m_ptr->r_idx];
88
89     (void)set_monster_csleep(player_ptr, g_ptr->m_idx, 0);
90
91     if (r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_TELEPORT)) {
92         msg_print(_("テレポートを邪魔された!", "Your teleportation is blocked!"));
93         if (is_original_ap_and_seen(player_ptr, m_ptr)) {
94             r_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
95         }
96         return false;
97     }
98
99     sound(SOUND_TELEPORT);
100     (void)move_player_effect(player_ptr, ty, tx, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP);
101     return true;
102 }
103
104 /*!
105  * @brief モンスター用テレポート処理
106  * @param player_ptr プレイヤーへの参照ポインタ
107  * @param dir 方向(5ならばグローバル変数 target_col/target_row の座標を目標にする)
108  * @param distance 移動距離
109  * @return 作用が実際にあった場合TRUEを返す
110  */
111 bool teleport_monster(PlayerType *player_ptr, DIRECTION dir, int distance)
112 {
113     BIT_FLAGS flg = PROJECT_BEAM | PROJECT_KILL;
114     return project_hook(player_ptr, AttributeType::AWAY_ALL, dir, distance, flg);
115 }
116
117 /*!
118  * @brief モンスターのテレポートアウェイ処理 /
119  * Teleport a monster, normally up to "dis" grids away.
120  * @param player_ptr プレイヤーへの参照ポインタ
121  * @param m_idx モンスターID
122  * @param dis テレポート距離
123  * @param mode オプション
124  * @return テレポートが実際に行われたらtrue
125  * @details
126  * Attempt to move the monster at least "dis/2" grids away.
127  * But allow variation to prevent infinite loops.
128  */
129 bool teleport_away(PlayerType *player_ptr, MONSTER_IDX m_idx, POSITION dis, teleport_flags mode)
130 {
131     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
132     if (!m_ptr->is_valid()) {
133         return false;
134     }
135
136     if ((mode & TELEPORT_DEC_VALOUR) && (((player_ptr->chp * 10) / player_ptr->mhp) > 5) && (4 + randint1(5) < ((player_ptr->chp * 10) / player_ptr->mhp))) {
137         chg_virtue(player_ptr, Virtue::VALOUR, -1);
138     }
139
140     POSITION oy = m_ptr->fy;
141     POSITION ox = m_ptr->fx;
142     POSITION min = dis / 2;
143     int tries = 0;
144     POSITION ny = 0, nx = 0;
145     bool look = true;
146     while (look) {
147         tries++;
148         if (dis > 200) {
149             dis = 200;
150         }
151
152         for (int i = 0; i < 500; i++) {
153             while (true) {
154                 ny = rand_spread(oy, dis);
155                 nx = rand_spread(ox, dis);
156                 POSITION d = distance(oy, ox, ny, nx);
157                 if ((d >= min) && (d <= dis)) {
158                     break;
159                 }
160             }
161
162             if (!in_bounds(player_ptr->current_floor_ptr, ny, nx)) {
163                 continue;
164             }
165             if (!cave_monster_teleportable_bold(player_ptr, m_idx, ny, nx, mode)) {
166                 continue;
167             }
168             if (!(inside_quest(player_ptr->current_floor_ptr->quest_number) || player_ptr->current_floor_ptr->inside_arena)) {
169                 if (player_ptr->current_floor_ptr->grid_array[ny][nx].is_icky()) {
170                     continue;
171                 }
172             }
173
174             look = false;
175             break;
176         }
177
178         dis = dis * 2;
179         min = min / 2;
180         const int MAX_TELEPORT_TRIES = 100;
181         if (tries > MAX_TELEPORT_TRIES) {
182             return false;
183         }
184     }
185
186     sound(SOUND_TPOTHER);
187     player_ptr->current_floor_ptr->grid_array[oy][ox].m_idx = 0;
188     player_ptr->current_floor_ptr->grid_array[ny][nx].m_idx = m_idx;
189
190     m_ptr->fy = ny;
191     m_ptr->fx = nx;
192
193     reset_target(m_ptr);
194     update_monster(player_ptr, m_idx, true);
195     lite_spot(player_ptr, oy, ox);
196     lite_spot(player_ptr, ny, nx);
197
198     if (monraces_info[m_ptr->r_idx].brightness_flags.has_any_of(ld_mask)) {
199         player_ptr->update |= (PU_MONSTER_LITE);
200     }
201
202     return true;
203 }
204
205 /*!
206  * @brief モンスターを指定された座標付近にテレポートする /
207  * Teleport monster next to a grid near the given location
208  * @param player_ptr プレイヤーへの参照ポインタ
209  * @param m_idx モンスターID
210  * @param ty 目安Y座標
211  * @param tx 目安X座標
212  * @param power テレポート成功確率
213  * @param mode オプション
214  */
215 void teleport_monster_to(PlayerType *player_ptr, MONSTER_IDX m_idx, POSITION ty, POSITION tx, int power, teleport_flags mode)
216 {
217     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
218     if (!MonsterRace(m_ptr->r_idx).is_valid()) {
219         return;
220     }
221     if (randint1(100) > power) {
222         return;
223     }
224
225     POSITION ny = m_ptr->fy;
226     POSITION nx = m_ptr->fx;
227     POSITION oy = m_ptr->fy;
228     POSITION ox = m_ptr->fx;
229
230     POSITION dis = 2;
231     int min = dis / 2;
232     int attempts = 500;
233     bool look = true;
234     while (look && --attempts) {
235         if (dis > 200) {
236             dis = 200;
237         }
238
239         for (int i = 0; i < 500; i++) {
240             while (true) {
241                 ny = rand_spread(ty, dis);
242                 nx = rand_spread(tx, dis);
243                 int d = distance(ty, tx, ny, nx);
244                 if ((d >= min) && (d <= dis)) {
245                     break;
246                 }
247             }
248
249             if (!in_bounds(player_ptr->current_floor_ptr, ny, nx)) {
250                 continue;
251             }
252             if (!cave_monster_teleportable_bold(player_ptr, m_idx, ny, nx, mode)) {
253                 continue;
254             }
255
256             look = false;
257             break;
258         }
259
260         dis = dis * 2;
261         min = min / 2;
262     }
263
264     if (attempts < 1) {
265         return;
266     }
267
268     sound(SOUND_TPOTHER);
269     player_ptr->current_floor_ptr->grid_array[oy][ox].m_idx = 0;
270     player_ptr->current_floor_ptr->grid_array[ny][nx].m_idx = m_idx;
271
272     m_ptr->fy = ny;
273     m_ptr->fx = nx;
274
275     update_monster(player_ptr, m_idx, true);
276     lite_spot(player_ptr, oy, ox);
277     lite_spot(player_ptr, ny, nx);
278
279     if (monraces_info[m_ptr->r_idx].brightness_flags.has_any_of(ld_mask)) {
280         player_ptr->update |= (PU_MONSTER_LITE);
281     }
282 }
283
284 /*!
285  * @brief プレイヤーのテレポート先選定と移動処理 /
286  * Teleport the player to a location up to "dis" grids away.
287  * @param player_ptr プレイヤーへの参照ポインタ
288  * @param dis 基本移動距離
289  * @param is_quantum_effect 量子的効果 (反テレポ無効)によるテレポートアウェイならばTRUE
290  * @param mode オプション
291  * @return 実際にテレポート処理が行われたらtrue
292  * @details
293  * <pre>
294  * If no such spaces are readily available, the distance may increase.
295  * Try very hard to move the player at least a quarter that distance.
296  *
297  * There was a nasty tendency for a long time; which was causing the
298  * player to "bounce" between two or three different spots because
299  * these are the only spots that are "far enough" way to satisfy the
300  * algorithm.
301  *
302  * But this tendency is now removed; in the new algorithm, a list of
303  * candidates is selected first, which includes at least 50% of all
304  * floor grids within the distance, and any single grid in this list
305  * of candidates has equal possibility to be choosen as a destination.
306  * </pre>
307  */
308 bool teleport_player_aux(PlayerType *player_ptr, POSITION dis, bool is_quantum_effect, teleport_flags mode)
309 {
310     if (player_ptr->wild_mode) {
311         return false;
312     }
313
314     if (!is_quantum_effect && player_ptr->anti_tele && !(mode & TELEPORT_NONMAGICAL)) {
315         msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
316         return false;
317     }
318
319     constexpr auto max_distance = 200;
320     std::array<int, max_distance + 1> candidates_at{};
321     if (dis > max_distance) {
322         dis = max_distance;
323     }
324
325     const auto &floor_ref = *player_ptr->current_floor_ptr;
326     const auto left = std::max(1, player_ptr->x - dis);
327     const auto right = std::min(floor_ref.width - 2, player_ptr->x + dis);
328     const auto top = std::max(1, player_ptr->y - dis);
329     const auto bottom = std::min(floor_ref.height - 2, player_ptr->y + dis);
330     auto total_candidates = 0;
331     for (auto y = top; y <= bottom; y++) {
332         for (auto x = left; x <= right; x++) {
333             if (!cave_player_teleportable_bold(player_ptr, y, x, mode)) {
334                 continue;
335             }
336
337             int d = distance(player_ptr->y, player_ptr->x, y, x);
338             if (d > dis) {
339                 continue;
340             }
341
342             total_candidates++;
343             candidates_at[d]++;
344         }
345     }
346
347     if (0 == total_candidates) {
348         return false;
349     }
350
351     int cur_candidates;
352     auto min = dis;
353     for (cur_candidates = 0; min >= 0; min--) {
354         cur_candidates += candidates_at[min];
355         if (cur_candidates && (cur_candidates >= total_candidates / 2)) {
356             break;
357         }
358     }
359
360     auto pick = randint1(cur_candidates);
361
362     /* Search again the choosen location */
363     auto yy = top;
364     auto xx = 0;
365     for (; yy <= bottom; yy++) {
366         for (xx = left; xx <= right; xx++) {
367             if (!cave_player_teleportable_bold(player_ptr, yy, xx, mode)) {
368                 continue;
369             }
370
371             int d = distance(player_ptr->y, player_ptr->x, yy, xx);
372             if (d > dis) {
373                 continue;
374             }
375             if (d < min) {
376                 continue;
377             }
378
379             pick--;
380             if (!pick) {
381                 break;
382             }
383         }
384
385         if (!pick) {
386             break;
387         }
388     }
389
390     if (player_bold(player_ptr, yy, xx)) {
391         return false;
392     }
393
394     sound(SOUND_TELEPORT);
395 #ifdef JP
396     if (is_echizen(player_ptr)) {
397         msg_format("『こっちだぁ、%s』", player_ptr->name);
398     }
399 #endif
400     (void)move_player_effect(player_ptr, yy, xx, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP);
401     return true;
402 }
403
404 /*!
405  * @brief プレイヤーのテレポート処理メインルーチン
406  * @param player_ptr プレイヤーへの参照ポインタ
407  * @param dis 基本移動距離
408  * @param mode オプション
409  */
410 void teleport_player(PlayerType *player_ptr, POSITION dis, BIT_FLAGS mode)
411 {
412     const POSITION oy = player_ptr->y;
413     const POSITION ox = player_ptr->x;
414
415     if (!teleport_player_aux(player_ptr, dis, false, i2enum<teleport_flags>(mode))) {
416         return;
417     }
418
419     /* Monsters with teleport ability may follow the player */
420     for (POSITION xx = -1; xx < 2; xx++) {
421         for (POSITION yy = -1; yy < 2; yy++) {
422             MONSTER_IDX tmp_m_idx = player_ptr->current_floor_ptr->grid_array[oy + yy][ox + xx].m_idx;
423             if (tmp_m_idx && (player_ptr->riding != tmp_m_idx)) {
424                 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[tmp_m_idx];
425                 auto *r_ptr = &monraces_info[m_ptr->r_idx];
426
427                 bool can_follow = r_ptr->ability_flags.has(MonsterAbilityType::TPORT);
428                 can_follow &= r_ptr->resistance_flags.has_not(MonsterResistanceType::RESIST_TELEPORT);
429                 can_follow &= !m_ptr->is_asleep();
430                 if (can_follow) {
431                     teleport_monster_to(player_ptr, tmp_m_idx, player_ptr->y, player_ptr->x, r_ptr->level, TELEPORT_SPONTANEOUS);
432                 }
433             }
434         }
435     }
436 }
437
438 /*!
439  * @brief プレイヤーのテレポートアウェイ処理 /
440  * @param m_idx アウェイを試みたモンスターID
441  * @param player_ptr プレイヤーへの参照ポインタ
442  * @param dis テレポート距離
443  * @param is_quantum_effect 量子的効果によるテレポートアウェイならばTRUE
444  */
445 void teleport_player_away(MONSTER_IDX m_idx, PlayerType *player_ptr, POSITION dis, bool is_quantum_effect)
446 {
447     if (player_ptr->phase_out) {
448         return;
449     }
450
451     const POSITION oy = player_ptr->y;
452     const POSITION ox = player_ptr->x;
453
454     if (!teleport_player_aux(player_ptr, dis, is_quantum_effect, TELEPORT_PASSIVE)) {
455         return;
456     }
457
458     /* Monsters with teleport ability may follow the player */
459     for (POSITION xx = -1; xx < 2; xx++) {
460         for (POSITION yy = -1; yy < 2; yy++) {
461             MONSTER_IDX tmp_m_idx = player_ptr->current_floor_ptr->grid_array[oy + yy][ox + xx].m_idx;
462             bool is_teleportable = tmp_m_idx > 0;
463             is_teleportable &= player_ptr->riding != tmp_m_idx;
464             is_teleportable &= m_idx != tmp_m_idx;
465             if (!is_teleportable) {
466                 continue;
467             }
468
469             auto *m_ptr = &player_ptr->current_floor_ptr->m_list[tmp_m_idx];
470             auto *r_ptr = &monraces_info[m_ptr->r_idx];
471
472             bool can_follow = r_ptr->ability_flags.has(MonsterAbilityType::TPORT);
473             can_follow &= r_ptr->resistance_flags.has_not(MonsterResistanceType::RESIST_TELEPORT);
474             can_follow &= !m_ptr->is_asleep();
475             if (can_follow) {
476                 teleport_monster_to(player_ptr, tmp_m_idx, player_ptr->y, player_ptr->x, r_ptr->level, TELEPORT_SPONTANEOUS);
477             }
478         }
479     }
480 }
481
482 /*!
483  * @brief プレイヤーを指定位置近辺にテレポートさせる
484  * Teleport player to a grid near the given location
485  * @param player_ptr プレイヤーへの参照ポインタ
486  * @param ny 目標Y座標
487  * @param nx 目標X座標
488  * @param mode オプションフラグ
489  * @details
490  * <pre>
491  * This function is slightly obsessive about correctness.
492  * This function allows teleporting into vaults (!)
493  * </pre>
494  */
495 void teleport_player_to(PlayerType *player_ptr, POSITION ny, POSITION nx, teleport_flags mode)
496 {
497     if (player_ptr->anti_tele && !(mode & TELEPORT_NONMAGICAL)) {
498         msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
499         return;
500     }
501
502     /* Find a usable location */
503     POSITION y, x;
504     POSITION dis = 0, ctr = 0;
505     while (true) {
506         while (true) {
507             y = (POSITION)rand_spread(ny, dis);
508             x = (POSITION)rand_spread(nx, dis);
509             if (in_bounds(player_ptr->current_floor_ptr, y, x)) {
510                 break;
511             }
512         }
513
514         bool is_anywhere = w_ptr->wizard;
515         is_anywhere &= (mode & TELEPORT_PASSIVE) == 0;
516         is_anywhere &= (player_ptr->current_floor_ptr->grid_array[y][x].m_idx > 0) || player_ptr->current_floor_ptr->grid_array[y][x].m_idx == player_ptr->riding;
517         if (is_anywhere) {
518             break;
519         }
520
521         if (cave_player_teleportable_bold(player_ptr, y, x, mode)) {
522             break;
523         }
524
525         if (++ctr > (4 * dis * dis + 4 * dis + 1)) {
526             ctr = 0;
527             dis++;
528         }
529     }
530
531     sound(SOUND_TELEPORT);
532     (void)move_player_effect(player_ptr, y, x, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP);
533 }
534
535 void teleport_away_followable(PlayerType *player_ptr, MONSTER_IDX m_idx)
536 {
537     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
538     POSITION oldfy = m_ptr->fy;
539     POSITION oldfx = m_ptr->fx;
540     bool old_ml = m_ptr->ml;
541     POSITION old_cdis = m_ptr->cdis;
542
543     teleport_away(player_ptr, m_idx, MAX_PLAYER_SIGHT * 2 + 5, TELEPORT_SPONTANEOUS);
544
545     bool is_followable = old_ml;
546     is_followable &= old_cdis <= MAX_PLAYER_SIGHT;
547     is_followable &= w_ptr->timewalk_m_idx == 0;
548     is_followable &= !player_ptr->phase_out;
549     is_followable &= los(player_ptr, player_ptr->y, player_ptr->x, oldfy, oldfx);
550     if (!is_followable) {
551         return;
552     }
553
554     bool follow = false;
555     if (player_ptr->muta.has(PlayerMutationType::VTELEPORT) || PlayerClass(player_ptr).equals(PlayerClassType::IMITATOR)) {
556         follow = true;
557     } else {
558         ItemEntity *o_ptr;
559         INVENTORY_IDX i;
560
561         for (i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
562             o_ptr = &player_ptr->inventory_list[i];
563             if (o_ptr->is_valid() && !o_ptr->is_cursed()) {
564                 auto flags = object_flags(o_ptr);
565                 if (flags.has(TR_TELEPORT)) {
566                     follow = true;
567                     break;
568                 }
569             }
570         }
571     }
572
573     if (!follow) {
574         return;
575     }
576     if (!get_check_strict(player_ptr, _("ついていきますか?", "Do you follow it? "), CHECK_OKAY_CANCEL)) {
577         return;
578     }
579
580     if (one_in_(3)) {
581         teleport_player(player_ptr, 200, TELEPORT_PASSIVE);
582         msg_print(_("失敗!", "Failed!"));
583     } else {
584         teleport_player_to(player_ptr, m_ptr->fy, m_ptr->fx, TELEPORT_SPONTANEOUS);
585     }
586
587     player_ptr->energy_need += ENERGY_NEED();
588 }
589
590 /*!
591  * @brief 次元の扉処理 /
592  * Dimension Door
593  * @param player_ptr プレイヤーへの参照ポインタ
594  * @param x テレポート先のX座標
595  * @param y テレポート先のY座標
596  * @return 目標に指定通りテレポートできたならばTRUEを返す
597  */
598 bool exe_dimension_door(PlayerType *player_ptr, POSITION x, POSITION y)
599 {
600     PLAYER_LEVEL plev = player_ptr->lev;
601
602     player_ptr->energy_need += (int16_t)((int32_t)(60 - plev) * ENERGY_NEED() / 100L);
603     auto is_successful = cave_player_teleportable_bold(player_ptr, y, x, TELEPORT_SPONTANEOUS);
604     is_successful &= distance(y, x, player_ptr->y, player_ptr->x) <= plev / 2 + 10;
605     is_successful &= !one_in_(plev / 10 + 10);
606     if (!is_successful) {
607         player_ptr->energy_need += (int16_t)((int32_t)(60 - plev) * ENERGY_NEED() / 100L);
608         teleport_player(player_ptr, (plev + 2) * 2, TELEPORT_PASSIVE);
609         return false;
610     }
611
612     teleport_player_to(player_ptr, y, x, TELEPORT_SPONTANEOUS);
613     return true;
614 }
615
616 /*!
617  * @brief 次元の扉処理のメインルーチン /
618  * @param player_ptr プレイヤーへの参照ポインタ
619  * Dimension Door
620  * @return ターンを消費した場合TRUEを返す
621  */
622 bool dimension_door(PlayerType *player_ptr)
623 {
624     DEPTH x = 0, y = 0;
625     if (!tgt_pt(player_ptr, &x, &y)) {
626         return false;
627     }
628
629     if (exe_dimension_door(player_ptr, x, y)) {
630         return true;
631     }
632
633     msg_print(_("精霊界から物質界に戻る時うまくいかなかった!", "You fail to exit the astral plane correctly!"));
634     return true;
635 }