OSDN Git Service

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