OSDN Git Service

[Refactor] #1372 AngbandWorld の乱数に関するフィールド変数をAngbandSystem へ移した
[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/score-util.h"
22 #include "core/scores.h"
23 #include "core/speed-table.h"
24 #include "core/stuff-handler.h"
25 #include "core/visuals-reseter.h"
26 #include "core/window-redrawer.h"
27 #include "dungeon/dungeon-processor.h"
28 #include "dungeon/quest.h"
29 #include "floor/cave.h"
30 #include "floor/floor-changer.h"
31 #include "floor/floor-events.h"
32 #include "floor/floor-leaver.h"
33 #include "floor/floor-mode-changer.h"
34 #include "floor/floor-save.h"
35 #include "floor/floor-util.h"
36 #include "floor/wild.h"
37 #include "game-option/cheat-options.h"
38 #include "game-option/input-options.h"
39 #include "game-option/play-record-options.h"
40 #include "game-option/runtime-arguments.h"
41 #include "grid/feature.h"
42 #include "grid/grid.h"
43 #include "info-reader/fixed-map-parser.h"
44 #include "io/files-util.h"
45 #include "io/input-key-acceptor.h"
46 #include "io/input-key-processor.h"
47 #include "io/read-pref-file.h"
48 #include "io/record-play-movie.h"
49 #include "io/screen-util.h"
50 #include "io/signal-handlers.h"
51 #include "io/write-diary.h"
52 #include "item-info/flavor-initializer.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-base/player-class.h"
66 #include "player-base/player-race.h"
67 #include "player-info/class-info.h"
68 #include "player-info/race-types.h"
69 #include "player/player-personality-types.h"
70 #include "player/player-skill.h"
71 #include "player/player-status.h"
72 #include "player/process-name.h"
73 #include "racial/racial-android.h"
74 #include "realm/realm-names-table.h"
75 #include "save/save.h"
76 #include "spell/spells-status.h"
77 #include "spell/technic-info-table.h"
78 #include "status/buff-setter.h"
79 #include "store/home.h"
80 #include "store/store-util.h"
81 #include "store/store.h"
82 #include "sv-definition/sv-weapon-types.h"
83 #include "system/angband-system.h"
84 #include "system/angband-version.h"
85 #include "system/floor-type-definition.h"
86 #include "system/item-entity.h"
87 #include "system/monster-entity.h"
88 #include "system/monster-race-info.h"
89 #include "system/player-type-definition.h"
90 #include "system/redrawing-flags-updater.h"
91 #include "target/target-checker.h"
92 #include "term/gameterm.h"
93 #include "term/screen-processor.h"
94 #include "term/z-form.h"
95 #include "util/angband-files.h"
96 #include "view/display-messages.h"
97 #include "view/display-player.h"
98 #include "window/main-window-util.h"
99 #include "wizard/wizard-special-process.h"
100 #include "world/world.h"
101
102 static void restore_windows(PlayerType *player_ptr)
103 {
104     player_ptr->hack_mutation = false;
105     w_ptr->character_icky_depth = 1;
106     term_activate(angband_terms[0]);
107     angband_terms[0]->resize_hook = resize_map;
108     for (auto i = 1U; i < angband_terms.size(); ++i) {
109         if (angband_terms[i]) {
110             angband_terms[i]->resize_hook = redraw_window;
111         }
112     }
113
114     (void)term_set_cursor(0);
115 }
116
117 static void send_waiting_record(PlayerType *player_ptr)
118 {
119     if (!player_ptr->wait_report_score) {
120         return;
121     }
122
123     if (!input_check_strict(player_ptr, _("待機していたスコア登録を今行ないますか?", "Do you register score now? "), UserCheck::NO_HISTORY)) {
124         quit(0);
125     }
126
127     static constexpr auto flags = {
128         StatusRecalculatingFlag::BONUS,
129         StatusRecalculatingFlag::HP,
130         StatusRecalculatingFlag::MP,
131         StatusRecalculatingFlag::SPELLS,
132     };
133     RedrawingFlagsUpdater::get_instance().set_flags(flags);
134     update_creature(player_ptr);
135     player_ptr->is_dead = true;
136     w_ptr->start_time = (uint32_t)time(nullptr);
137     signals_ignore_tstp();
138     w_ptr->character_icky_depth = 1;
139     const auto &path = path_build(ANGBAND_DIR_APEX, "scores.raw");
140     highscore_fd = fd_open(path, O_RDWR);
141
142     /* 町名消失バグ対策(#38205)のためここで世界マップ情報を読み出す */
143     parse_fixed_map(player_ptr, WILDERNESS_DEFINITION, 0, 0, w_ptr->max_wild_y, w_ptr->max_wild_x);
144     bool success = send_world_score(player_ptr, true);
145     if (!success && !input_check_strict(player_ptr, _("スコア登録を諦めますか?", "Do you give up score registration? "), UserCheck::NO_HISTORY)) {
146         prt(_("引き続き待機します。", "standing by for future registration..."), 0, 0);
147         (void)inkey();
148     } else {
149         player_ptr->wait_report_score = false;
150         top_twenty(player_ptr);
151         if (!save_player(player_ptr, SaveType::CLOSE_GAME)) {
152             msg_print(_("セーブ失敗!", "death save failed!"));
153         }
154     }
155
156     (void)fd_close(highscore_fd);
157     highscore_fd = -1;
158     signals_handle_tstp();
159     quit(0);
160 }
161
162 static void init_random_seed(PlayerType *player_ptr, bool new_game)
163 {
164     bool init_random_seed = false;
165     if (!w_ptr->character_loaded) {
166         new_game = true;
167         w_ptr->character_dungeon = false;
168         init_random_seed = true;
169         init_saved_floors(player_ptr, false);
170     } else if (new_game) {
171         init_saved_floors(player_ptr, true);
172     }
173
174     if (!new_game) {
175         process_player_name(player_ptr);
176     }
177
178     if (init_random_seed) {
179         Rand_state_init();
180     }
181 }
182
183 static void init_world_floor_info(PlayerType *player_ptr)
184 {
185     w_ptr->character_dungeon = false;
186     auto *floor_ptr = player_ptr->current_floor_ptr;
187     floor_ptr->reset_dungeon_index();
188     floor_ptr->dun_level = 0;
189     floor_ptr->quest_number = QuestId::NONE;
190     floor_ptr->inside_arena = false;
191     AngbandSystem::get_instance().set_phase_out(false);
192     write_level = true;
193     auto &system = AngbandSystem::get_instance();
194     system.seed_flavor = randint0(0x10000000);
195     system.seed_town = randint0(0x10000000);
196     player_birth(player_ptr);
197     counts_write(player_ptr, 2, 0);
198     player_ptr->count = 0;
199     load = false;
200     determine_bounty_uniques(player_ptr);
201     determine_daily_bounty(player_ptr, false);
202     wipe_o_list(floor_ptr);
203 }
204
205 /*!
206  * @brief フロア情報をゲームロード時に復帰
207  * @todo 3.0.Xで削除予定
208  * 1.0.9 以前はセーブ前に player_ptr->riding = -1 としていたので、再設定が必要だった。
209  * もう不要だが、以前のセーブファイルとの互換のために残しておく。
210  */
211 static void restore_world_floor_info(PlayerType *player_ptr)
212 {
213     write_level = false;
214     constexpr auto mes = _("                            ----ゲーム再開----", "                            --- Restarted Game ---");
215     exe_write_diary(player_ptr, DiaryKind::GAMESTART, 1, mes);
216
217     if (player_ptr->riding != -1) {
218         return;
219     }
220
221     player_ptr->riding = 0;
222     auto *floor_ptr = player_ptr->current_floor_ptr;
223     for (short i = floor_ptr->m_max; i > 0; i--) {
224         const auto &monster = floor_ptr->m_list[i];
225         if (player_ptr->is_located_at({ monster.fy, monster.fx })) {
226             player_ptr->riding = i;
227             break;
228         }
229     }
230 }
231
232 static void reset_world_info(PlayerType *player_ptr)
233 {
234     w_ptr->creating_savefile = false;
235     player_ptr->teleport_town = false;
236     player_ptr->sutemi = false;
237     w_ptr->timewalk_m_idx = 0;
238     player_ptr->now_damaged = false;
239     now_message = 0;
240     w_ptr->start_time = time(nullptr) - 1;
241     record_o_name[0] = '\0';
242 }
243
244 static void generate_wilderness(PlayerType *player_ptr)
245 {
246     auto *floor_ptr = player_ptr->current_floor_ptr;
247     if ((floor_ptr->dun_level == 0) && floor_ptr->is_in_quest()) {
248         return;
249     }
250
251     parse_fixed_map(player_ptr, WILDERNESS_DEFINITION, 0, 0, w_ptr->max_wild_y, w_ptr->max_wild_x);
252     init_flags = INIT_ONLY_BUILDINGS;
253     parse_fixed_map(player_ptr, TOWN_DEFINITION_LIST, 0, 0, MAX_HGT, MAX_WID);
254     select_floor_music(player_ptr);
255 }
256
257 static void change_floor_if_error(PlayerType *player_ptr)
258 {
259     if (!w_ptr->character_dungeon) {
260         change_floor(player_ptr);
261         return;
262     }
263
264     if (player_ptr->panic_save == 0) {
265         return;
266     }
267
268     if (!player_ptr->y || !player_ptr->x) {
269         msg_print(_("プレイヤーの位置がおかしい。フロアを再生成します。", "What a strange player location, regenerate the dungeon floor."));
270         change_floor(player_ptr);
271     }
272
273     if (!player_ptr->y || !player_ptr->x) {
274         player_ptr->y = player_ptr->x = 10;
275     }
276
277     player_ptr->panic_save = 0;
278 }
279
280 static void generate_world(PlayerType *player_ptr, bool new_game)
281 {
282     reset_world_info(player_ptr);
283     auto *floor_ptr = player_ptr->current_floor_ptr;
284     panel_row_min = floor_ptr->height;
285     panel_col_min = floor_ptr->width;
286
287     set_floor_and_wall(floor_ptr->dungeon_idx);
288     initialize_items_flavor();
289     prt(_("お待ち下さい...", "Please wait..."), 0, 0);
290     term_fresh();
291     generate_wilderness(player_ptr);
292     change_floor_if_error(player_ptr);
293     w_ptr->character_generated = true;
294     w_ptr->character_icky_depth = 0;
295     if (!new_game) {
296         return;
297     }
298
299     const auto mes = format(_("%sに降り立った。", "arrived in %s."), map_name(player_ptr).data());
300     exe_write_diary(player_ptr, DiaryKind::DESCRIPTION, 0, mes);
301 }
302
303 static void init_io(PlayerType *player_ptr)
304 {
305     term_xtra(TERM_XTRA_REACT, 0);
306     RedrawingFlagsUpdater::get_instance().fill_up_sub_flags();
307     handle_stuff(player_ptr);
308     if (arg_force_original) {
309         rogue_like_commands = false;
310     }
311
312     if (arg_force_roguelike) {
313         rogue_like_commands = true;
314     }
315 }
316
317 static void init_riding_pet(PlayerType *player_ptr, bool new_game)
318 {
319     PlayerClass pc(player_ptr);
320     if (!new_game || !pc.is_tamer()) {
321         return;
322     }
323
324     MonsterRaceId pet_r_idx = pc.equals(PlayerClassType::CAVALRY) ? MonsterRaceId::HORSE : MonsterRaceId::YASE_HORSE;
325     auto *r_ptr = &monraces_info[pet_r_idx];
326     place_specific_monster(player_ptr, 0, player_ptr->y, player_ptr->x - 1, pet_r_idx, (PM_FORCE_PET | PM_NO_KAGE));
327     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[hack_m_idx_ii];
328     m_ptr->mspeed = r_ptr->speed;
329     m_ptr->maxhp = r_ptr->hdice * (r_ptr->hside + 1) / 2;
330     m_ptr->max_maxhp = m_ptr->maxhp;
331     m_ptr->hp = r_ptr->hdice * (r_ptr->hside + 1) / 2;
332     m_ptr->dealt_damage = 0;
333     m_ptr->energy_need = ENERGY_NEED() + ENERGY_NEED();
334 }
335
336 static void decide_arena_death(PlayerType *player_ptr)
337 {
338     if (!player_ptr->playing || !player_ptr->is_dead) {
339         return;
340     }
341
342     auto *floor_ptr = player_ptr->current_floor_ptr;
343     if (!floor_ptr->inside_arena) {
344         if ((w_ptr->wizard || cheat_live) && !input_check(_("死にますか? ", "Die? "))) {
345             cheat_death(player_ptr);
346         }
347
348         return;
349     }
350
351     floor_ptr->inside_arena = false;
352     if (player_ptr->arena_number > MAX_ARENA_MONS) {
353         player_ptr->arena_number++;
354     } else {
355         player_ptr->arena_number = -1 - player_ptr->arena_number;
356     }
357
358     player_ptr->is_dead = false;
359     player_ptr->chp = 0;
360     player_ptr->chp_frac = 0;
361     w_ptr->set_arena(true);
362     reset_tim_flags(player_ptr);
363     prepare_change_floor_mode(player_ptr, CFM_SAVE_FLOORS | CFM_RAND_CONNECT);
364     leave_floor(player_ptr);
365 }
366
367 static void process_game_turn(PlayerType *player_ptr)
368 {
369     bool load_game = true;
370     auto *floor_ptr = player_ptr->current_floor_ptr;
371     while (true) {
372         process_dungeon(player_ptr, load_game);
373         w_ptr->character_xtra = true;
374         handle_stuff(player_ptr);
375         w_ptr->character_xtra = false;
376         target_who = 0;
377         health_track(player_ptr, 0);
378         forget_lite(floor_ptr);
379         forget_view(floor_ptr);
380         clear_mon_lite(floor_ptr);
381         if (!player_ptr->playing && !player_ptr->is_dead) {
382             break;
383         }
384
385         wipe_o_list(floor_ptr);
386         if (!player_ptr->is_dead) {
387             wipe_monsters_list(player_ptr);
388         }
389
390         msg_print(nullptr);
391         load_game = false;
392         decide_arena_death(player_ptr);
393         if (player_ptr->is_dead) {
394             break;
395         }
396
397         change_floor(player_ptr);
398     }
399 }
400
401 /*!
402  * @brief 1ゲームプレイの主要ルーチン / Actually play a game
403  * @param player_ptr プレイヤーへの参照ポインタ
404  * @param new_game 新規にゲームを始めたかどうか
405  * @param browsing_movie ムービーモードか
406  * @note
407  * If the "new_game" parameter is true, then, after loading the
408  * savefile, we will commit suicide, if necessary, to allow the
409  * player to start a new game.
410  */
411 void play_game(PlayerType *player_ptr, bool new_game, bool browsing_movie)
412 {
413     if (browsing_movie) {
414         reset_visuals(player_ptr);
415         browse_movie();
416         return;
417     }
418
419     restore_windows(player_ptr);
420     if (!load_savedata(player_ptr, &new_game)) {
421         quit(_("セーブファイルが壊れています", "broken savefile"));
422     }
423
424     extract_option_vars();
425     send_waiting_record(player_ptr);
426     w_ptr->creating_savefile = new_game;
427     init_random_seed(player_ptr, new_game);
428     if (new_game) {
429         init_world_floor_info(player_ptr);
430     } else {
431         restore_world_floor_info(player_ptr);
432     }
433
434     generate_world(player_ptr, new_game);
435     player_ptr->playing = true;
436     reset_visuals(player_ptr);
437     load_all_pref_files(player_ptr);
438     if (new_game) {
439         player_outfit(player_ptr);
440     }
441
442     init_io(player_ptr);
443     if (player_ptr->chp < 0 && !cheat_immortal) {
444         player_ptr->is_dead = true;
445     }
446
447     if (PlayerRace(player_ptr).equals(PlayerRaceType::ANDROID)) {
448         calc_android_exp(player_ptr);
449     }
450
451     init_riding_pet(player_ptr, new_game);
452     (void)combine_and_reorder_home(player_ptr, StoreSaleType::HOME);
453     (void)combine_and_reorder_home(player_ptr, StoreSaleType::MUSEUM);
454     select_floor_music(player_ptr);
455     process_game_turn(player_ptr);
456     close_game(player_ptr);
457     quit(nullptr);
458 }