OSDN Git Service

[Refactor] 熟練度レベルの定数を enum class にする
[hengbandforosx/hengbandosx.git] / src / core / game-play.cpp
1 /*!
2  * @brief ゲームプレイのメインルーチン
3  * @date 2020/05/10
4  * @author Hourier
5  * @details
6  * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
7  * This software may be copied and distributed for educational, research, and
8  * not for profit purposes provided that this copyright and statement are
9  * included in all such copies.
10  * 2013 Deskull rearranged comment for Doxygen.
11  */
12
13 #include "core/game-play.h"
14 #include "autopick/autopick-pref-processor.h"
15 #include "birth/character-builder.h"
16 #include "birth/inventory-initializer.h"
17 #include "cmd-io/cmd-gameoption.h"
18 #include "core/asking-player.h"
19 #include "core/game-closer.h"
20 #include "core/player-processor.h"
21 #include "core/player-update-types.h"
22 #include "core/score-util.h"
23 #include "core/scores.h"
24 #include "core/speed-table.h"
25 #include "core/stuff-handler.h"
26 #include "core/visuals-reseter.h"
27 #include "core/window-redrawer.h"
28 #include "dungeon/dungeon-processor.h"
29 #include "flavor/object-flavor.h"
30 #include "floor/cave.h"
31 #include "floor/floor-changer.h"
32 #include "floor/floor-events.h"
33 #include "floor/floor-leaver.h"
34 #include "floor/floor-mode-changer.h"
35 #include "floor/floor-save.h"
36 #include "floor/floor-util.h"
37 #include "floor/wild.h"
38 #include "game-option/cheat-options.h"
39 #include "game-option/input-options.h"
40 #include "game-option/play-record-options.h"
41 #include "game-option/runtime-arguments.h"
42 #include "grid/feature.h"
43 #include "grid/grid.h"
44 #include "info-reader/fixed-map-parser.h"
45 #include "io/inet.h"
46 #include "io/input-key-acceptor.h"
47 #include "io/input-key-processor.h"
48 #include "io/read-pref-file.h"
49 #include "io/record-play-movie.h"
50 #include "io/screen-util.h"
51 #include "io/signal-handlers.h"
52 #include "io/write-diary.h"
53 #include "load/load.h"
54 #include "main/sound-of-music.h"
55 #include "market/arena-info-table.h"
56 #include "market/bounty.h"
57 #include "market/building-initializer.h"
58 #include "monster-floor/monster-generator.h"
59 #include "monster-floor/monster-lite.h"
60 #include "monster-floor/monster-remover.h"
61 #include "monster-floor/place-monster-types.h"
62 #include "monster-race/monster-race.h"
63 #include "monster-race/race-indice-types.h"
64 #include "monster/monster-util.h"
65 #include "player-info/class-info.h"
66 #include "player-info/race-types.h"
67 #include "player/player-personality-types.h"
68 #include "player/player-skill.h"
69 #include "player/player-status.h"
70 #include "player/process-name.h"
71 #include "racial/racial-android.h"
72 #include "realm/realm-names-table.h"
73 #include "save/save.h"
74 #include "spell/spells-status.h"
75 #include "spell/technic-info-table.h"
76 #include "status/buff-setter.h"
77 #include "store/home.h"
78 #include "store/store-util.h"
79 #include "store/store.h"
80 #include "sv-definition/sv-weapon-types.h"
81 #include "system/angband-version.h"
82 #include "system/floor-type-definition.h"
83 #include "system/monster-race-definition.h"
84 #include "system/monster-type-definition.h"
85 #include "system/object-type-definition.h"
86 #include "system/player-type-definition.h"
87 #include "target/target-checker.h"
88 #include "term/gameterm.h"
89 #include "term/screen-processor.h"
90 #include "util/angband-files.h"
91 #include "view/display-messages.h"
92 #include "view/display-player.h"
93 #include "window/main-window-util.h"
94 #include "wizard/wizard-special-process.h"
95 #include "world/world.h"
96
97 static void restore_windows(player_type *player_ptr)
98 {
99     player_ptr->hack_mutation = false;
100     w_ptr->character_icky_depth = 1;
101     term_activate(angband_term[0]);
102     angband_term[0]->resize_hook = resize_map;
103     for (MONSTER_IDX i = 1; i < 8; i++)
104         if (angband_term[i])
105             angband_term[i]->resize_hook = redraw_window;
106
107     (void)term_set_cursor(0);
108 }
109
110 static void send_waiting_record(player_type *player_ptr)
111 {
112     if (!player_ptr->wait_report_score)
113         return;
114
115     char buf[1024];
116     if (!get_check_strict(player_ptr, _("待機していたスコア登録を今行ないますか?", "Do you register score now? "), CHECK_NO_HISTORY))
117         quit(0);
118
119     player_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS);
120     update_creature(player_ptr);
121     player_ptr->is_dead = true;
122     w_ptr->start_time = (uint32_t)time(nullptr);
123     signals_ignore_tstp();
124     w_ptr->character_icky_depth = 1;
125     path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
126     highscore_fd = fd_open(buf, O_RDWR);
127
128     /* 町名消失バグ対策(#38205)のためここで世界マップ情報を読み出す */
129     parse_fixed_map(player_ptr, "w_info.txt", 0, 0, w_ptr->max_wild_y, w_ptr->max_wild_x);
130     bool success = send_world_score(player_ptr, true, display_player);
131     if (!success && !get_check_strict(player_ptr, _("スコア登録を諦めますか?", "Do you give up score registration? "), CHECK_NO_HISTORY)) {
132         prt(_("引き続き待機します。", "standing by for future registration..."), 0, 0);
133         (void)inkey();
134     } else {
135         player_ptr->wait_report_score = false;
136         top_twenty(player_ptr);
137         if (!save_player(player_ptr, SAVE_TYPE_CLOSE_GAME))
138             msg_print(_("セーブ失敗!", "death save failed!"));
139     }
140
141     (void)fd_close(highscore_fd);
142     highscore_fd = -1;
143     signals_handle_tstp();
144     quit(0);
145 }
146
147 static void init_random_seed(player_type *player_ptr, bool new_game)
148 {
149     bool init_random_seed = false;
150     if (!w_ptr->character_loaded) {
151         new_game = true;
152         w_ptr->character_dungeon = false;
153         init_random_seed = true;
154         init_saved_floors(player_ptr, false);
155     } else if (new_game)
156         init_saved_floors(player_ptr, true);
157
158     if (!new_game)
159         process_player_name(player_ptr);
160
161     if (init_random_seed)
162         Rand_state_init();
163 }
164
165 static void init_world_floor_info(player_type *player_ptr)
166 {
167     w_ptr->character_dungeon = false;
168     floor_type *floor_ptr = player_ptr->current_floor_ptr;
169     floor_ptr->dun_level = 0;
170     floor_ptr->inside_quest = 0;
171     floor_ptr->inside_arena = false;
172     player_ptr->phase_out = false;
173     write_level = true;
174     w_ptr->seed_flavor = randint0(0x10000000);
175     w_ptr->seed_town = randint0(0x10000000);
176     player_birth(player_ptr);
177     counts_write(player_ptr, 2, 0);
178     player_ptr->count = 0;
179     load = false;
180     determine_bounty_uniques(player_ptr);
181     determine_daily_bounty(player_ptr, false);
182     wipe_o_list(floor_ptr);
183 }
184
185 /*!
186  * @brief フロア情報をゲームロード時に復帰
187  * @todo 3.0.Xで削除予定
188  * 1.0.9 以前はセーブ前に player_ptr->riding = -1 としていたので、再設定が必要だった。
189  * もう不要だが、以前のセーブファイルとの互換のために残しておく。
190  */
191 static void restore_world_floor_info(player_type *player_ptr)
192 {
193     write_level = false;
194     exe_write_diary(player_ptr, DIARY_GAMESTART, 1, _("                            ----ゲーム再開----", "                            --- Restarted Game ---"));
195
196     if (player_ptr->riding == -1) {
197         player_ptr->riding = 0;
198         floor_type *floor_ptr = player_ptr->current_floor_ptr;
199         for (MONSTER_IDX i = floor_ptr->m_max; i > 0; i--) {
200             if (player_bold(player_ptr, floor_ptr->m_list[i].fy, floor_ptr->m_list[i].fx)) {
201                 player_ptr->riding = i;
202                 break;
203             }
204         }
205     }
206 }
207
208 static void reset_world_info(player_type *player_ptr)
209 {
210     w_ptr->creating_savefile = false;
211     player_ptr->teleport_town = false;
212     player_ptr->sutemi = false;
213     w_ptr->timewalk_m_idx = 0;
214     player_ptr->now_damaged = false;
215     now_message = 0;
216     w_ptr->start_time = time(nullptr) - 1;
217     record_o_name[0] = '\0';
218 }
219
220 static void generate_wilderness(player_type *player_ptr)
221 {
222     floor_type *floor_ptr = player_ptr->current_floor_ptr;
223     if ((floor_ptr->dun_level == 0) && floor_ptr->inside_quest)
224         return;
225
226     parse_fixed_map(player_ptr, "w_info.txt", 0, 0, w_ptr->max_wild_y, w_ptr->max_wild_x);
227     init_flags = INIT_ONLY_BUILDINGS;
228     parse_fixed_map(player_ptr, "t_info.txt", 0, 0, MAX_HGT, MAX_WID);
229     select_floor_music(player_ptr);
230 }
231
232 static void change_floor_if_error(player_type *player_ptr)
233 {
234     if (!w_ptr->character_dungeon) {
235         change_floor(player_ptr);
236         return;
237     }
238
239     if (player_ptr->panic_save == 0)
240         return;
241
242     if (!player_ptr->y || !player_ptr->x) {
243         msg_print(_("プレイヤーの位置がおかしい。フロアを再生成します。", "What a strange player location, regenerate the dungeon floor."));
244         change_floor(player_ptr);
245     }
246
247     if (!player_ptr->y || !player_ptr->x)
248         player_ptr->y = player_ptr->x = 10;
249
250     player_ptr->panic_save = 0;
251 }
252
253 static void generate_world(player_type *player_ptr, bool new_game)
254 {
255     reset_world_info(player_ptr);
256     floor_type *floor_ptr = player_ptr->current_floor_ptr;
257     panel_row_min = floor_ptr->height;
258     panel_col_min = floor_ptr->width;
259
260     if (player_ptr->pclass != PlayerClassType::SORCERER) {
261         auto pclass = enum2i(player_ptr->pclass);
262         if (player_ptr->ppersonality == PERSONALITY_SEXY)
263             s_info[pclass].w_max[ItemKindType::HAFTED][SV_WHIP] = PlayerSkill::weapon_exp_at(PlayerSkillRank::MASTER);
264         if (player_ptr->prace == PlayerRaceType::MERFOLK) {
265             s_info[pclass].w_max[ItemKindType::POLEARM][SV_TRIDENT] = PlayerSkill::weapon_exp_at(PlayerSkillRank::MASTER);
266             s_info[pclass].w_max[ItemKindType::POLEARM][SV_TRIFURCATE_SPEAR] = PlayerSkill::weapon_exp_at(PlayerSkillRank::MASTER);
267         }
268     }
269
270     set_floor_and_wall(player_ptr->dungeon_idx);
271     flavor_init();
272     prt(_("お待ち下さい...", "Please wait..."), 0, 0);
273     term_fresh();
274     generate_wilderness(player_ptr);
275     change_floor_if_error(player_ptr);
276     w_ptr->character_generated = true;
277     w_ptr->character_icky_depth = 0;
278     if (!new_game)
279         return;
280
281     char buf[80];
282     sprintf(buf, _("%sに降り立った。", "arrived in %s."), map_name(player_ptr));
283     exe_write_diary(player_ptr, DIARY_DESCRIPTION, 0, buf);
284 }
285
286 static void init_io(player_type *player_ptr)
287 {
288     term_xtra(TERM_XTRA_REACT, 0);
289     player_ptr->window_flags = PW_ALL;
290     handle_stuff(player_ptr);
291     if (arg_force_original)
292         rogue_like_commands = false;
293
294     if (arg_force_roguelike)
295         rogue_like_commands = true;
296 }
297
298 static void init_riding_pet(player_type *player_ptr, bool new_game)
299 {
300     if (!new_game || ((player_ptr->pclass != PlayerClassType::CAVALRY) && (player_ptr->pclass != PlayerClassType::BEASTMASTER)))
301         return;
302
303     MONRACE_IDX pet_r_idx = ((player_ptr->pclass == PlayerClassType::CAVALRY) ? MON_HORSE : MON_YASE_HORSE);
304     monster_race *r_ptr = &r_info[pet_r_idx];
305     place_monster_aux(player_ptr, 0, player_ptr->y, player_ptr->x - 1, pet_r_idx, (PM_FORCE_PET | PM_NO_KAGE));
306     monster_type *m_ptr = &player_ptr->current_floor_ptr->m_list[hack_m_idx_ii];
307     m_ptr->mspeed = r_ptr->speed;
308     m_ptr->maxhp = r_ptr->hdice * (r_ptr->hside + 1) / 2;
309     m_ptr->max_maxhp = m_ptr->maxhp;
310     m_ptr->hp = r_ptr->hdice * (r_ptr->hside + 1) / 2;
311     m_ptr->dealt_damage = 0;
312     m_ptr->energy_need = ENERGY_NEED() + ENERGY_NEED();
313 }
314
315 static void decide_arena_death(player_type *player_ptr)
316 {
317     if (!player_ptr->playing || !player_ptr->is_dead)
318         return;
319
320     floor_type *floor_ptr = player_ptr->current_floor_ptr;
321     if (!floor_ptr->inside_arena) {
322         if (cheat_live && !get_check(_("死にますか? ", "Die? "))) {
323             cheat_death(player_ptr);
324         }
325
326         return;
327     }
328
329     floor_ptr->inside_arena = false;
330     if (player_ptr->arena_number > MAX_ARENA_MONS)
331         player_ptr->arena_number++;
332     else
333         player_ptr->arena_number = -1 - player_ptr->arena_number;
334
335     player_ptr->is_dead = false;
336     player_ptr->chp = 0;
337     player_ptr->chp_frac = 0;
338     player_ptr->exit_bldg = true;
339     reset_tim_flags(player_ptr);
340     prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_RAND_CONNECT);
341     leave_floor(player_ptr);
342 }
343
344 static void process_game_turn(player_type *player_ptr)
345 {
346     bool load_game = true;
347     floor_type *floor_ptr = player_ptr->current_floor_ptr;
348     while (true) {
349         process_dungeon(player_ptr, load_game);
350         w_ptr->character_xtra = true;
351         handle_stuff(player_ptr);
352         w_ptr->character_xtra = false;
353         target_who = 0;
354         health_track(player_ptr, 0);
355         forget_lite(floor_ptr);
356         forget_view(floor_ptr);
357         clear_mon_lite(floor_ptr);
358         if (!player_ptr->playing && !player_ptr->is_dead)
359             break;
360
361         wipe_o_list(floor_ptr);
362         if (!player_ptr->is_dead)
363             wipe_monsters_list(player_ptr);
364
365         msg_print(nullptr);
366         load_game = false;
367         decide_arena_death(player_ptr);
368         if (player_ptr->is_dead)
369             break;
370
371         change_floor(player_ptr);
372     }
373 }
374
375 /*!
376  * @brief 1ゲームプレイの主要ルーチン / Actually play a game
377  * @param player_ptr プレイヤーへの参照ポインタ
378  * @param new_game 新規にゲームを始めたかどうか
379  * @param browsing_movie ムービーモードか
380  * @note
381  * If the "new_game" parameter is true, then, after loading the
382  * savefile, we will commit suicide, if necessary, to allow the
383  * player to start a new game.
384  */
385 void play_game(player_type *player_ptr, bool new_game, bool browsing_movie)
386 {
387     if (browsing_movie) {
388         reset_visuals(player_ptr);
389         browse_movie();
390         return;
391     }
392
393     restore_windows(player_ptr);
394     if (!load_savedata(player_ptr, &new_game))
395         quit(_("セーブファイルが壊れています", "broken savefile"));
396
397     extract_option_vars();
398     send_waiting_record(player_ptr);
399     w_ptr->creating_savefile = new_game;
400     init_random_seed(player_ptr, new_game);
401     if (new_game)
402         init_world_floor_info(player_ptr);
403     else
404         restore_world_floor_info(player_ptr);
405
406     generate_world(player_ptr, new_game);
407     player_ptr->playing = true;
408     reset_visuals(player_ptr);
409     load_all_pref_files(player_ptr);
410     if (new_game)
411         player_outfit(player_ptr);
412
413     init_io(player_ptr);
414     if (player_ptr->chp < 0 && !cheat_immortal)
415         player_ptr->is_dead = true;
416
417     if (player_ptr->prace == PlayerRaceType::ANDROID)
418         calc_android_exp(player_ptr);
419
420     init_riding_pet(player_ptr, new_game);
421     (void)combine_and_reorder_home(player_ptr, StoreSaleType::HOME);
422     (void)combine_and_reorder_home(player_ptr, StoreSaleType::MUSEUM);
423     select_floor_music(player_ptr);
424     process_game_turn(player_ptr);
425     close_game(player_ptr);
426     quit(nullptr);
427 }