OSDN Git Service

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