OSDN Git Service

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