OSDN Git Service

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