OSDN Git Service

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