OSDN Git Service

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