OSDN Git Service

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