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"
49 * @brief モンスターとの位置交換処理 / Switch position with a monster.
50 * @param player_ptr プレイヤーへの参照ポインタ
51 * @param dir 方向(5ならばグローバル変数 target_col/target_row の座標を目標にする)
52 * @return 作用が実際にあった場合TRUEを返す
54 bool teleport_swap(PlayerType *player_ptr, DIRECTION dir)
57 if ((dir == 5) && target_okay(player_ptr)) {
61 tx = player_ptr->x + ddx[dir];
62 ty = player_ptr->y + ddy[dir];
65 if (player_ptr->anti_tele) {
66 msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
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!"));
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."));
84 m_ptr = &player_ptr->current_floor_ptr->m_list[g_ptr->m_idx];
85 r_ptr = &r_info[m_ptr->r_idx];
87 (void)set_monster_csleep(player_ptr, g_ptr->m_idx, 0);
89 if (r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_TELEPORT)) {
90 msg_print(_("テレポートを邪魔された!", "Your teleportation is blocked!"));
91 if (is_original_ap_and_seen(player_ptr, m_ptr)) {
92 r_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
97 sound(SOUND_TELEPORT);
98 (void)move_player_effect(player_ptr, ty, tx, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP);
103 * @brief モンスター用テレポート処理
104 * @param player_ptr プレイヤーへの参照ポインタ
105 * @param dir 方向(5ならばグローバル変数 target_col/target_row の座標を目標にする)
106 * @param distance 移動距離
107 * @return 作用が実際にあった場合TRUEを返す
109 bool teleport_monster(PlayerType *player_ptr, DIRECTION dir, int distance)
111 BIT_FLAGS flg = PROJECT_BEAM | PROJECT_KILL;
112 return project_hook(player_ptr, AttributeType::AWAY_ALL, dir, distance, flg);
116 * @brief モンスターのテレポートアウェイ処理 /
117 * Teleport a monster, normally up to "dis" grids away.
118 * @param player_ptr プレイヤーへの参照ポインタ
119 * @param m_idx モンスターID
122 * @return テレポートが実際に行われたらtrue
124 * Attempt to move the monster at least "dis/2" grids away.
125 * But allow variation to prevent infinite loops.
127 bool teleport_away(PlayerType *player_ptr, MONSTER_IDX m_idx, POSITION dis, teleport_flags mode)
129 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
130 if (!m_ptr->is_valid()) {
134 if ((mode & TELEPORT_DEC_VALOUR) && (((player_ptr->chp * 10) / player_ptr->mhp) > 5) && (4 + randint1(5) < ((player_ptr->chp * 10) / player_ptr->mhp))) {
135 chg_virtue(player_ptr, V_VALOUR, -1);
138 POSITION oy = m_ptr->fy;
139 POSITION ox = m_ptr->fx;
140 POSITION min = dis / 2;
142 POSITION ny = 0, nx = 0;
150 for (int i = 0; i < 500; i++) {
152 ny = rand_spread(oy, dis);
153 nx = rand_spread(ox, dis);
154 POSITION d = distance(oy, ox, ny, nx);
155 if ((d >= min) && (d <= dis)) {
160 if (!in_bounds(player_ptr->current_floor_ptr, ny, nx)) {
163 if (!cave_monster_teleportable_bold(player_ptr, m_idx, ny, nx, mode)) {
166 if (!(inside_quest(player_ptr->current_floor_ptr->quest_number) || player_ptr->current_floor_ptr->inside_arena)) {
167 if (player_ptr->current_floor_ptr->grid_array[ny][nx].is_icky()) {
178 const int MAX_TELEPORT_TRIES = 100;
179 if (tries > MAX_TELEPORT_TRIES) {
184 sound(SOUND_TPOTHER);
185 player_ptr->current_floor_ptr->grid_array[oy][ox].m_idx = 0;
186 player_ptr->current_floor_ptr->grid_array[ny][nx].m_idx = m_idx;
192 update_monster(player_ptr, m_idx, true);
193 lite_spot(player_ptr, oy, ox);
194 lite_spot(player_ptr, ny, nx);
196 if (r_info[m_ptr->r_idx].flags7 & (RF7_LITE_MASK | RF7_DARK_MASK)) {
197 player_ptr->update |= (PU_MON_LITE);
204 * @brief モンスターを指定された座標付近にテレポートする /
205 * Teleport monster next to a grid near the given location
206 * @param player_ptr プレイヤーへの参照ポインタ
207 * @param m_idx モンスターID
210 * @param power テレポート成功確率
213 void teleport_monster_to(PlayerType *player_ptr, MONSTER_IDX m_idx, POSITION ty, POSITION tx, int power, teleport_flags mode)
215 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
216 if (!MonsterRace(m_ptr->r_idx).is_valid()) {
219 if (randint1(100) > power) {
223 POSITION ny = m_ptr->fy;
224 POSITION nx = m_ptr->fx;
225 POSITION oy = m_ptr->fy;
226 POSITION ox = m_ptr->fx;
232 while (look && --attempts) {
237 for (int i = 0; i < 500; i++) {
239 ny = rand_spread(ty, dis);
240 nx = rand_spread(tx, dis);
241 int d = distance(ty, tx, ny, nx);
242 if ((d >= min) && (d <= dis)) {
247 if (!in_bounds(player_ptr->current_floor_ptr, ny, nx)) {
250 if (!cave_monster_teleportable_bold(player_ptr, m_idx, ny, nx, mode)) {
266 sound(SOUND_TPOTHER);
267 player_ptr->current_floor_ptr->grid_array[oy][ox].m_idx = 0;
268 player_ptr->current_floor_ptr->grid_array[ny][nx].m_idx = m_idx;
273 update_monster(player_ptr, m_idx, true);
274 lite_spot(player_ptr, oy, ox);
275 lite_spot(player_ptr, ny, nx);
277 if (r_info[m_ptr->r_idx].flags7 & (RF7_LITE_MASK | RF7_DARK_MASK)) {
278 player_ptr->update |= (PU_MON_LITE);
283 * @brief プレイヤーのテレポート先選定と移動処理 /
284 * Teleport the player to a location up to "dis" grids away.
285 * @param player_ptr プレイヤーへの参照ポインタ
287 * @param is_quantum_effect 量子的効果 (反テレポ無効)によるテレポートアウェイならばTRUE
289 * @return 実際にテレポート処理が行われたらtrue
292 * If no such spaces are readily available, the distance may increase.
293 * Try very hard to move the player at least a quarter that distance.
295 * There was a nasty tendency for a long time; which was causing the
296 * player to "bounce" between two or three different spots because
297 * these are the only spots that are "far enough" way to satisfy the
300 * But this tendency is now removed; in the new algorithm, a list of
301 * candidates is selected first, which includes at least 50% of all
302 * floor grids within the distance, and any single grid in this list
303 * of candidates has equal possibility to be choosen as a destination.
306 bool teleport_player_aux(PlayerType *player_ptr, POSITION dis, bool is_quantum_effect, teleport_flags mode)
308 if (player_ptr->wild_mode) {
311 if (!is_quantum_effect && player_ptr->anti_tele && !(mode & TELEPORT_NONMAGICAL)) {
312 msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
316 int candidates_at[MAX_TELEPORT_DISTANCE + 1];
317 for (int i = 0; i <= MAX_TELEPORT_DISTANCE; i++) {
318 candidates_at[i] = 0;
321 if (dis > MAX_TELEPORT_DISTANCE) {
322 dis = MAX_TELEPORT_DISTANCE;
325 int left = std::max(1, player_ptr->x - dis);
326 int right = std::min(player_ptr->current_floor_ptr->width - 2, player_ptr->x + dis);
327 int top = std::max(1, player_ptr->y - dis);
328 int bottom = std::min(player_ptr->current_floor_ptr->height - 2, player_ptr->y + dis);
329 int total_candidates = 0;
330 for (POSITION y = top; y <= bottom; y++) {
331 for (POSITION x = left; x <= right; x++) {
332 if (!cave_player_teleportable_bold(player_ptr, y, x, mode)) {
336 int d = distance(player_ptr->y, player_ptr->x, y, x);
346 if (0 == total_candidates) {
352 for (cur_candidates = 0; min >= 0; min--) {
353 cur_candidates += candidates_at[min];
354 if (cur_candidates && (cur_candidates >= total_candidates / 2)) {
359 int pick = randint1(cur_candidates);
361 /* Search again the choosen location */
363 for (yy = top; yy <= bottom; yy++) {
364 for (xx = left; xx <= right; xx++) {
365 if (!cave_player_teleportable_bold(player_ptr, yy, xx, mode)) {
369 int d = distance(player_ptr->y, player_ptr->x, yy, xx);
388 if (player_bold(player_ptr, yy, xx)) {
392 sound(SOUND_TELEPORT);
394 if (is_echizen(player_ptr)) {
395 msg_format("『こっちだぁ、%s』", player_ptr->name);
398 (void)move_player_effect(player_ptr, yy, xx, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP);
403 * @brief プレイヤーのテレポート処理メインルーチン
404 * @param player_ptr プレイヤーへの参照ポインタ
408 void teleport_player(PlayerType *player_ptr, POSITION dis, BIT_FLAGS mode)
410 const POSITION oy = player_ptr->y;
411 const POSITION ox = player_ptr->x;
413 if (!teleport_player_aux(player_ptr, dis, false, i2enum<teleport_flags>(mode))) {
417 /* Monsters with teleport ability may follow the player */
418 for (POSITION xx = -1; xx < 2; xx++) {
419 for (POSITION yy = -1; yy < 2; yy++) {
420 MONSTER_IDX tmp_m_idx = player_ptr->current_floor_ptr->grid_array[oy + yy][ox + xx].m_idx;
421 if (tmp_m_idx && (player_ptr->riding != tmp_m_idx)) {
422 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[tmp_m_idx];
423 auto *r_ptr = &r_info[m_ptr->r_idx];
425 bool can_follow = r_ptr->ability_flags.has(MonsterAbilityType::TPORT);
426 can_follow &= r_ptr->resistance_flags.has_not(MonsterResistanceType::RESIST_TELEPORT);
427 can_follow &= monster_csleep_remaining(m_ptr) == 0;
429 teleport_monster_to(player_ptr, tmp_m_idx, player_ptr->y, player_ptr->x, r_ptr->level, TELEPORT_SPONTANEOUS);
437 * @brief プレイヤーのテレポートアウェイ処理 /
438 * @param m_idx アウェイを試みたモンスターID
439 * @param player_ptr プレイヤーへの参照ポインタ
441 * @param is_quantum_effect 量子的効果によるテレポートアウェイならばTRUE
443 void teleport_player_away(MONSTER_IDX m_idx, PlayerType *player_ptr, POSITION dis, bool is_quantum_effect)
445 if (player_ptr->phase_out) {
449 const POSITION oy = player_ptr->y;
450 const POSITION ox = player_ptr->x;
452 if (!teleport_player_aux(player_ptr, dis, is_quantum_effect, TELEPORT_PASSIVE)) {
456 /* Monsters with teleport ability may follow the player */
457 for (POSITION xx = -1; xx < 2; xx++) {
458 for (POSITION yy = -1; yy < 2; yy++) {
459 MONSTER_IDX tmp_m_idx = player_ptr->current_floor_ptr->grid_array[oy + yy][ox + xx].m_idx;
460 bool is_teleportable = tmp_m_idx > 0;
461 is_teleportable &= player_ptr->riding != tmp_m_idx;
462 is_teleportable &= m_idx != tmp_m_idx;
463 if (!is_teleportable) {
467 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[tmp_m_idx];
468 auto *r_ptr = &r_info[m_ptr->r_idx];
470 bool can_follow = r_ptr->ability_flags.has(MonsterAbilityType::TPORT);
471 can_follow &= r_ptr->resistance_flags.has_not(MonsterResistanceType::RESIST_TELEPORT);
472 can_follow &= monster_csleep_remaining(m_ptr) == 0;
474 teleport_monster_to(player_ptr, tmp_m_idx, player_ptr->y, player_ptr->x, r_ptr->level, TELEPORT_SPONTANEOUS);
481 * @brief プレイヤーを指定位置近辺にテレポートさせる
482 * Teleport player to a grid near the given location
483 * @param player_ptr プレイヤーへの参照ポインタ
486 * @param mode オプションフラグ
489 * This function is slightly obsessive about correctness.
490 * This function allows teleporting into vaults (!)
493 void teleport_player_to(PlayerType *player_ptr, POSITION ny, POSITION nx, teleport_flags mode)
495 if (player_ptr->anti_tele && !(mode & TELEPORT_NONMAGICAL)) {
496 msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
500 /* Find a usable location */
502 POSITION dis = 0, ctr = 0;
505 y = (POSITION)rand_spread(ny, dis);
506 x = (POSITION)rand_spread(nx, dis);
507 if (in_bounds(player_ptr->current_floor_ptr, y, x)) {
512 bool is_anywhere = w_ptr->wizard;
513 is_anywhere &= (mode & TELEPORT_PASSIVE) == 0;
514 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 (cave_player_teleportable_bold(player_ptr, y, x, mode)) {
523 if (++ctr > (4 * dis * dis + 4 * dis + 1)) {
529 sound(SOUND_TELEPORT);
530 (void)move_player_effect(player_ptr, y, x, MPE_FORGET_FLOW | MPE_HANDLE_STUFF | MPE_DONT_PICKUP);
533 void teleport_away_followable(PlayerType *player_ptr, MONSTER_IDX m_idx)
535 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
536 POSITION oldfy = m_ptr->fy;
537 POSITION oldfx = m_ptr->fx;
538 bool old_ml = m_ptr->ml;
539 POSITION old_cdis = m_ptr->cdis;
541 teleport_away(player_ptr, m_idx, MAX_SIGHT * 2 + 5, TELEPORT_SPONTANEOUS);
543 bool is_followable = old_ml;
544 is_followable &= old_cdis <= MAX_SIGHT;
545 is_followable &= w_ptr->timewalk_m_idx == 0;
546 is_followable &= !player_ptr->phase_out;
547 is_followable &= los(player_ptr, player_ptr->y, player_ptr->x, oldfy, oldfx);
548 if (!is_followable) {
553 if (player_ptr->muta.has(PlayerMutationType::VTELEPORT) || PlayerClass(player_ptr).equals(PlayerClassType::IMITATOR)) {
559 for (i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
560 o_ptr = &player_ptr->inventory_list[i];
561 if (o_ptr->k_idx && !o_ptr->is_cursed()) {
562 auto flgs = object_flags(o_ptr);
563 if (flgs.has(TR_TELEPORT)) {
574 if (!get_check_strict(player_ptr, _("ついていきますか?", "Do you follow it? "), CHECK_OKAY_CANCEL)) {
579 teleport_player(player_ptr, 200, TELEPORT_PASSIVE);
580 msg_print(_("失敗!", "Failed!"));
582 teleport_player_to(player_ptr, m_ptr->fy, m_ptr->fx, TELEPORT_SPONTANEOUS);
585 player_ptr->energy_need += ENERGY_NEED();
591 * @param player_ptr プレイヤーへの参照ポインタ
592 * @param x テレポート先のX座標
593 * @param y テレポート先のY座標
594 * @return 目標に指定通りテレポートできたならばTRUEを返す
596 bool exe_dimension_door(PlayerType *player_ptr, POSITION x, POSITION y)
598 PLAYER_LEVEL plev = player_ptr->lev;
600 player_ptr->energy_need += (int16_t)((int32_t)(60 - plev) * ENERGY_NEED() / 100L);
602 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))) {
603 player_ptr->energy_need += (int16_t)((int32_t)(60 - plev) * ENERGY_NEED() / 100L);
604 teleport_player(player_ptr, (plev + 2) * 2, TELEPORT_PASSIVE);
608 teleport_player_to(player_ptr, y, x, TELEPORT_SPONTANEOUS);
613 * @brief 次元の扉処理のメインルーチン /
614 * @param player_ptr プレイヤーへの参照ポインタ
616 * @return ターンを消費した場合TRUEを返す
618 bool dimension_door(PlayerType *player_ptr)
621 if (!tgt_pt(player_ptr, &x, &y)) {
625 if (exe_dimension_door(player_ptr, x, y)) {
629 msg_print(_("精霊界から物質界に戻る時うまくいかなかった!", "You fail to exit the astral plane correctly!"));