OSDN Git Service

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