OSDN Git Service

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