OSDN Git Service

[Refactor] #56 Renamed d_info.txt to DungeonDefinitions.txt
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-world.cpp
1 #include "spell-kind/spells-world.h"
2 #include "cmd-io/cmd-save.h"
3 #include "core/asking-player.h"
4 #include "core/player-redraw-types.h"
5 #include "dungeon/dungeon.h"
6 #include "dungeon/quest-completion-checker.h"
7 #include "dungeon/quest.h"
8 #include "floor/cave.h"
9 #include "floor/floor-mode-changer.h"
10 #include "floor/floor-town.h"
11 #include "floor/geometry.h"
12 #include "floor/wild.h"
13 #include "game-option/birth-options.h"
14 #include "game-option/play-record-options.h"
15 #include "game-option/special-options.h"
16 #include "io/input-key-acceptor.h"
17 #include "io/write-diary.h"
18 #include "main/sound-definitions-table.h"
19 #include "main/sound-of-music.h"
20 #include "market/building-util.h"
21 #include "monster-floor/monster-remover.h"
22 #include "monster-race/monster-race.h"
23 #include "monster-race/race-ability-mask.h"
24 #include "monster-race/race-flags-resistance.h"
25 #include "monster-race/race-flags1.h"
26 #include "monster-race/race-resistance-mask.h"
27 #include "monster/monster-describer.h"
28 #include "monster/monster-description-types.h"
29 #include "monster/monster-info.h"
30 #include "player/player-status.h"
31 #include "system/floor-type-definition.h"
32 #include "system/grid-type-definition.h"
33 #include "system/monster-race-definition.h"
34 #include "system/monster-type-definition.h"
35 #include "system/player-type-definition.h"
36 #include "target/projection-path-calculator.h"
37 #include "target/target-checker.h"
38 #include "target/target-setter.h"
39 #include "target/target-types.h"
40 #include "term/screen-processor.h"
41 #include "util/int-char-converter.h"
42 #include "view/display-messages.h"
43 #include "world/world.h"
44
45 /*!
46  * @brief テレポート・レベルが効かないモンスターであるかどうかを判定する
47  * @param player_ptr プレイヤーへの参照ポインタ
48  * @param idx テレポート・レベル対象のモンスター
49  * @todo 変数名が実態と合っているかどうかは要確認
50  */
51 bool is_teleport_level_ineffective(PlayerType *player_ptr, MONSTER_IDX idx)
52 {
53     auto *floor_ptr = player_ptr->current_floor_ptr;
54     bool is_special_floor = floor_ptr->inside_arena || player_ptr->phase_out || (inside_quest(floor_ptr->quest_number) && !inside_quest(random_quest_number(player_ptr, floor_ptr->dun_level)));
55     bool is_invalid_floor = idx <= 0;
56     is_invalid_floor &= inside_quest(quest_number(player_ptr, floor_ptr->dun_level)) || (floor_ptr->dun_level >= dungeons_info[player_ptr->dungeon_idx].maxdepth);
57     is_invalid_floor &= player_ptr->current_floor_ptr->dun_level >= 1;
58     is_invalid_floor &= ironman_downward;
59     return is_special_floor || is_invalid_floor;
60 }
61
62 /*!
63  * @brief プレイヤー及びモンスターをレベルテレポートさせる /
64  * Teleport the player one level up or down (random when legal)
65  * @param player_ptr プレイヤーへの参照ポインタ
66  * @param m_idx テレポートの対象となるモンスターID(0ならばプレイヤー) / If m_idx <= 0, target is player.
67  * @todo cmd-save.h への依存あり。コールバックで何とかしたい
68  */
69 void teleport_level(PlayerType *player_ptr, MONSTER_IDX m_idx)
70 {
71     GAME_TEXT m_name[160];
72     bool see_m = true;
73     if (m_idx <= 0) {
74         strcpy(m_name, _("あなた", "you"));
75     } else {
76         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
77         monster_desc(player_ptr, m_name, m_ptr, 0);
78         see_m = is_seen(player_ptr, m_ptr);
79     }
80
81     if (is_teleport_level_ineffective(player_ptr, m_idx)) {
82         if (see_m) {
83             msg_print(_("効果がなかった。", "There is no effect."));
84         }
85         return;
86     }
87
88     if ((m_idx <= 0) && player_ptr->anti_tele) {
89         msg_print(_("不思議な力がテレポートを防いだ!", "A mysterious force prevents you from teleporting!"));
90         return;
91     }
92
93     bool go_up;
94     if (randint0(100) < 50) {
95         go_up = true;
96     } else {
97         go_up = false;
98     }
99
100     if ((m_idx <= 0) && w_ptr->wizard) {
101         if (get_check("Force to go up? ")) {
102             go_up = true;
103         } else if (get_check("Force to go down? ")) {
104             go_up = false;
105         }
106     }
107
108     if ((ironman_downward && (m_idx <= 0)) || (player_ptr->current_floor_ptr->dun_level <= dungeons_info[player_ptr->dungeon_idx].mindepth)) {
109 #ifdef JP
110         if (see_m) {
111             msg_format("%^sは床を突き破って沈んでいく。", m_name);
112         }
113 #else
114         if (see_m) {
115             msg_format("%^s sink%s through the floor.", m_name, (m_idx <= 0) ? "" : "s");
116         }
117 #endif
118         if (m_idx <= 0) {
119             if (!is_in_dungeon(player_ptr)) {
120                 player_ptr->dungeon_idx = ironman_downward ? DUNGEON_ANGBAND : player_ptr->recall_dungeon;
121                 player_ptr->oldpy = player_ptr->y;
122                 player_ptr->oldpx = player_ptr->x;
123             }
124
125             if (record_stair) {
126                 exe_write_diary(player_ptr, DIARY_TELEPORT_LEVEL, 1, nullptr);
127             }
128
129             if (autosave_l) {
130                 do_cmd_save_game(player_ptr, true);
131             }
132
133             if (!is_in_dungeon(player_ptr)) {
134                 player_ptr->current_floor_ptr->dun_level = dungeons_info[player_ptr->dungeon_idx].mindepth;
135                 prepare_change_floor_mode(player_ptr, CFM_RAND_PLACE);
136             } else {
137                 prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_DOWN | CFM_RAND_PLACE | CFM_RAND_CONNECT);
138             }
139
140             player_ptr->leaving = true;
141         }
142     } else if (inside_quest(quest_number(player_ptr, player_ptr->current_floor_ptr->dun_level)) || (player_ptr->current_floor_ptr->dun_level >= dungeons_info[player_ptr->dungeon_idx].maxdepth)) {
143 #ifdef JP
144         if (see_m) {
145             msg_format("%^sは天井を突き破って宙へ浮いていく。", m_name);
146         }
147 #else
148         if (see_m) {
149             msg_format("%^s rise%s up through the ceiling.", m_name, (m_idx <= 0) ? "" : "s");
150         }
151 #endif
152
153         if (m_idx <= 0) {
154             if (record_stair) {
155                 exe_write_diary(player_ptr, DIARY_TELEPORT_LEVEL, -1, nullptr);
156             }
157
158             if (autosave_l) {
159                 do_cmd_save_game(player_ptr, true);
160             }
161
162             prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_UP | CFM_RAND_PLACE | CFM_RAND_CONNECT);
163
164             leave_quest_check(player_ptr);
165             player_ptr->current_floor_ptr->quest_number = QuestId::NONE;
166             player_ptr->leaving = true;
167         }
168     } else if (go_up) {
169 #ifdef JP
170         if (see_m) {
171             msg_format("%^sは天井を突き破って宙へ浮いていく。", m_name);
172         }
173 #else
174         if (see_m) {
175             msg_format("%^s rise%s up through the ceiling.", m_name, (m_idx <= 0) ? "" : "s");
176         }
177 #endif
178
179         if (m_idx <= 0) {
180             if (record_stair) {
181                 exe_write_diary(player_ptr, DIARY_TELEPORT_LEVEL, -1, nullptr);
182             }
183
184             if (autosave_l) {
185                 do_cmd_save_game(player_ptr, true);
186             }
187
188             prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_UP | CFM_RAND_PLACE | CFM_RAND_CONNECT);
189             player_ptr->leaving = true;
190         }
191     } else {
192 #ifdef JP
193         if (see_m) {
194             msg_format("%^sは床を突き破って沈んでいく。", m_name);
195         }
196 #else
197         if (see_m) {
198             msg_format("%^s sink%s through the floor.", m_name, (m_idx <= 0) ? "" : "s");
199         }
200 #endif
201
202         if (m_idx <= 0) {
203             if (record_stair) {
204                 exe_write_diary(player_ptr, DIARY_TELEPORT_LEVEL, 1, nullptr);
205             }
206             if (autosave_l) {
207                 do_cmd_save_game(player_ptr, true);
208             }
209
210             prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_DOWN | CFM_RAND_PLACE | CFM_RAND_CONNECT);
211             player_ptr->leaving = true;
212         }
213     }
214
215     if (m_idx <= 0) {
216         sound(SOUND_TPLEVEL);
217         return;
218     }
219
220     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
221     QuestCompletionChecker(player_ptr, m_ptr).complete();
222     if (record_named_pet && m_ptr->is_pet() && m_ptr->nickname) {
223         char m2_name[MAX_NLEN];
224
225         monster_desc(player_ptr, m2_name, m_ptr, MD_INDEF_VISIBLE);
226         exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_TELE_LEVEL, m2_name);
227     }
228
229     delete_monster_idx(player_ptr, m_idx);
230     if (see_m) {
231         sound(SOUND_TPLEVEL);
232     }
233 }
234
235 bool teleport_level_other(PlayerType *player_ptr)
236 {
237     if (!target_set(player_ptr, TARGET_KILL)) {
238         return false;
239     }
240     MONSTER_IDX target_m_idx = player_ptr->current_floor_ptr->grid_array[target_row][target_col].m_idx;
241     if (!target_m_idx) {
242         return true;
243     }
244     if (!player_has_los_bold(player_ptr, target_row, target_col)) {
245         return true;
246     }
247     if (!projectable(player_ptr, player_ptr->y, player_ptr->x, target_row, target_col)) {
248         return true;
249     }
250
251     monster_type *m_ptr;
252     monster_race *r_ptr;
253     m_ptr = &player_ptr->current_floor_ptr->m_list[target_m_idx];
254     r_ptr = &r_info[m_ptr->r_idx];
255     GAME_TEXT m_name[MAX_NLEN];
256     monster_desc(player_ptr, m_name, m_ptr, 0);
257     msg_format(_("%^sの足を指さした。", "You gesture at %^s's feet."), m_name);
258
259     auto has_immune = r_ptr->resistance_flags.has_any_of(RFR_EFF_RESIST_NEXUS_MASK) || r_ptr->resistance_flags.has(MonsterResistanceType::RESIST_TELEPORT);
260
261     if (has_immune || (r_ptr->flags1 & RF1_QUESTOR) || (r_ptr->level + randint1(50) > player_ptr->lev + randint1(60))) {
262         msg_format(_("しかし効果がなかった!", "%^s is unaffected!"), m_name);
263     } else {
264         teleport_level(player_ptr, target_m_idx);
265     }
266
267     return true;
268 }
269
270 /*!
271  * @brief 町間のテレポートを行うメインルーチン
272  * @param player_ptr プレイヤーへの参照ポインタ
273  * @return テレポート処理を決定したか否か
274  */
275 bool tele_town(PlayerType *player_ptr)
276 {
277     if (player_ptr->current_floor_ptr->dun_level) {
278         msg_print(_("この魔法は地上でしか使えない!", "This spell can only be used on the surface!"));
279         return false;
280     }
281
282     if (player_ptr->current_floor_ptr->inside_arena || player_ptr->phase_out) {
283         msg_print(_("この魔法は外でしか使えない!", "This spell can only be used outside!"));
284         return false;
285     }
286
287     screen_save();
288     clear_bldg(4, 10);
289
290     int i;
291     int num = 0;
292     for (i = 1; i < max_towns; i++) {
293         char buf[80];
294
295         if ((i == NO_TOWN) || (i == SECRET_TOWN) || (i == player_ptr->town_num) || !(player_ptr->visit & (1UL << (i - 1)))) {
296             continue;
297         }
298
299         sprintf(buf, "%c) %-20s", I2A(i - 1), town_info[i].name);
300         prt(buf, 5 + i, 5);
301         num++;
302     }
303
304     if (num == 0) {
305         msg_print(_("まだ行けるところがない。", "You have not yet visited any town."));
306         msg_print(nullptr);
307         screen_load();
308         return false;
309     }
310
311     prt(_("どこに行きますか:", "Where do you want to go: "), 0, 0);
312     while (true) {
313         i = inkey();
314
315         if (i == ESCAPE) {
316             screen_load();
317             return false;
318         }
319
320         else if ((i < 'a') || (i > ('a' + max_towns - 2))) {
321             continue;
322         } else if (((i - 'a' + 1) == player_ptr->town_num) || ((i - 'a' + 1) == NO_TOWN) || ((i - 'a' + 1) == SECRET_TOWN) || !(player_ptr->visit & (1UL << (i - 'a')))) {
323             continue;
324         }
325         break;
326     }
327
328     for (POSITION y = 0; y < w_ptr->max_wild_y; y++) {
329         for (POSITION x = 0; x < w_ptr->max_wild_x; x++) {
330             if (wilderness[y][x].town == (i - 'a' + 1)) {
331                 player_ptr->wilderness_y = y;
332                 player_ptr->wilderness_x = x;
333             }
334         }
335     }
336
337     player_ptr->leaving = true;
338     player_ptr->leave_bldg = true;
339     player_ptr->teleport_town = true;
340     screen_load();
341     return true;
342 }
343
344 /*!
345  * @brief 現実変容処理
346  * @param player_ptr プレイヤーへの参照ポインタ
347  */
348 void reserve_alter_reality(PlayerType *player_ptr, TIME_EFFECT turns)
349 {
350     if (player_ptr->current_floor_ptr->inside_arena || ironman_downward) {
351         msg_print(_("何も起こらなかった。", "Nothing happens."));
352         return;
353     }
354
355     if (player_ptr->alter_reality || turns == 0) {
356         player_ptr->alter_reality = 0;
357         msg_print(_("景色が元に戻った...", "The view around you returns to normal..."));
358         player_ptr->redraw |= PR_STATUS;
359         return;
360     }
361
362     player_ptr->alter_reality = turns;
363     msg_print(_("回りの景色が変わり始めた...", "The view around you begins to change..."));
364     player_ptr->redraw |= PR_STATUS;
365 }
366
367 /*!
368  * @brief プレイヤーの帰還発動及び中止処理 /
369  * Recall the player to town or dungeon
370  * @param player_ptr プレイヤーへの参照ポインタ
371  * @param turns 発動までのターン数
372  * @return 常にTRUEを返す
373  */
374 bool recall_player(PlayerType *player_ptr, TIME_EFFECT turns)
375 {
376     /*
377      * TODO: Recall the player to the last
378      * visited town when in the wilderness
379      */
380     if (player_ptr->current_floor_ptr->inside_arena || ironman_downward) {
381         msg_print(_("何も起こらなかった。", "Nothing happens."));
382         return true;
383     }
384
385     bool is_special_floor = is_in_dungeon(player_ptr);
386     is_special_floor &= max_dlv[player_ptr->dungeon_idx] > player_ptr->current_floor_ptr->dun_level;
387     is_special_floor &= !inside_quest(player_ptr->current_floor_ptr->quest_number);
388     is_special_floor &= !player_ptr->word_recall;
389     if (is_special_floor) {
390         if (get_check(_("ここは最深到達階より浅い階です。この階に戻って来ますか? ", "Reset recall depth? "))) {
391             max_dlv[player_ptr->dungeon_idx] = player_ptr->current_floor_ptr->dun_level;
392             if (record_maxdepth) {
393                 exe_write_diary(player_ptr, DIARY_TRUMP, player_ptr->dungeon_idx, _("帰還のときに", "when recalled from dungeon"));
394             }
395         }
396     }
397
398     if (player_ptr->word_recall || turns == 0) {
399         player_ptr->word_recall = 0;
400         msg_print(_("張りつめた大気が流れ去った...", "A tension leaves the air around you..."));
401         player_ptr->redraw |= (PR_STATUS);
402         return true;
403     }
404
405     if (!is_in_dungeon(player_ptr)) {
406         DUNGEON_IDX select_dungeon;
407         select_dungeon = choose_dungeon(_("に帰還", "recall"), 2, 14);
408         if (!select_dungeon) {
409             return false;
410         }
411         player_ptr->recall_dungeon = select_dungeon;
412     }
413
414     player_ptr->word_recall = turns;
415     msg_print(_("回りの大気が張りつめてきた...", "The air about you becomes charged..."));
416     player_ptr->redraw |= (PR_STATUS);
417     return true;
418 }
419
420 bool free_level_recall(PlayerType *player_ptr)
421 {
422     DUNGEON_IDX select_dungeon = choose_dungeon(_("にテレポート", "teleport"), 4, 0);
423     if (!select_dungeon) {
424         return false;
425     }
426
427     const auto &dungeon = dungeons_info[select_dungeon];
428     auto max_depth = dungeon.maxdepth;
429     if (select_dungeon == DUNGEON_ANGBAND) {
430         const auto &quest_list = QuestList::get_instance();
431         if (quest_list[QuestId::OBERON].status != QuestStatusType::FINISHED) {
432             max_depth = 98;
433         } else if (quest_list[QuestId::SERPENT].status != QuestStatusType::FINISHED) {
434             max_depth = 99;
435         }
436     }
437
438     const auto mes = _("%sの何階にテレポートしますか?", "Teleport to which level of %s? ");
439     QUANTITY amt = get_quantity(format(mes, dungeon.name.c_str()), (QUANTITY)max_depth);
440     if (amt <= 0) {
441         return false;
442     }
443
444     player_ptr->word_recall = 1;
445     player_ptr->recall_dungeon = select_dungeon;
446     max_dlv[player_ptr->recall_dungeon] = ((amt > dungeon.maxdepth) ? dungeon.maxdepth
447                                                                     : ((amt < dungeon.mindepth) ? dungeon.mindepth : amt));
448     if (record_maxdepth) {
449         exe_write_diary(player_ptr, DIARY_TRUMP, select_dungeon, _("トランプタワーで", "at Trump Tower"));
450     }
451
452     msg_print(_("回りの大気が張りつめてきた...", "The air about you becomes charged..."));
453
454     player_ptr->redraw |= PR_STATUS;
455     return true;
456 }
457
458 /*!
459  * @brief フロア・リセット処理
460  * @param player_ptr プレイヤーへの参照ポインタ
461  * @return リセット処理が実際に行われたらTRUEを返す
462  */
463 bool reset_recall(PlayerType *player_ptr)
464 {
465     int select_dungeon, dummy = 0;
466     char ppp[80];
467     char tmp_val[160];
468
469     select_dungeon = choose_dungeon(_("をセット", "reset"), 2, 14);
470     if (ironman_downward) {
471         msg_print(_("何も起こらなかった。", "Nothing happens."));
472         return true;
473     }
474
475     if (!select_dungeon) {
476         return false;
477     }
478     sprintf(ppp, _("何階にセットしますか (%d-%d):", "Reset to which level (%d-%d): "), (int)dungeons_info[select_dungeon].mindepth, (int)max_dlv[select_dungeon]);
479     sprintf(tmp_val, "%d", (int)std::max(player_ptr->current_floor_ptr->dun_level, 1));
480
481     if (!get_string(ppp, tmp_val, 10)) {
482         return false;
483     }
484
485     dummy = atoi(tmp_val);
486     if (dummy < 1) {
487         dummy = 1;
488     }
489     if (dummy > max_dlv[select_dungeon]) {
490         dummy = max_dlv[select_dungeon];
491     }
492     if (dummy < dungeons_info[select_dungeon].mindepth) {
493         dummy = dungeons_info[select_dungeon].mindepth;
494     }
495
496     max_dlv[select_dungeon] = dummy;
497
498     if (record_maxdepth) {
499         exe_write_diary(player_ptr, DIARY_TRUMP, select_dungeon, _("フロア・リセットで", "using a scroll of reset recall"));
500     }
501 #ifdef JP
502     msg_format("%sの帰還レベルを %d 階にセット。", dungeons_info[select_dungeon].name.c_str(), dummy, dummy * 50);
503 #else
504     msg_format("Recall depth set to level %d (%d').", dummy, dummy * 50);
505 #endif
506     return true;
507 }