OSDN Git Service

Merge branch 'view-display-util-no-pch' into macos-develop
[hengbandforosx/hengbandosx.git] / src / floor / floor-save.cpp
1 /*!
2  * @brief 保存された階の管理 / management of the saved floor
3  * @date 2014/01/04
4  * @author
5  * Copyright (c) 2002  Mogami \n
6  * This software may be copied and distributed for educational, research, and \n
7  * not for profit purposes provided that this copyright and statement are \n
8  * included in all such copies. \n
9  * 2014 Deskull rearranged comment for Doxygen. \n
10  */
11
12 #include "floor/floor-save.h"
13 #include "core/asking-player.h"
14 #include "floor/floor-save-util.h"
15 #include "io/files-util.h"
16 #include "io/uid-checker.h"
17 #include "monster-race/monster-race.h"
18 #include "monster/monster-info.h"
19 #include "monster/monster-status.h"
20 #include "system/floor-type-definition.h"
21 #include "system/monster-entity.h"
22 #include "system/monster-race-info.h"
23 #include "system/player-type-definition.h"
24 #include "term/z-form.h"
25 #include "util/angband-files.h"
26 #include "view/display-messages.h"
27
28 static std::string get_saved_floor_name(int level)
29 {
30     char ext[32];
31     strnfmt(ext, sizeof(ext), ".F%02d", level);
32     return std::string(savefile).append(ext);
33 }
34
35 static void check_saved_tmp_files(const int fd, bool *force)
36 {
37     if (fd >= 0) {
38         (void)fd_close(fd);
39         return;
40     }
41
42     if (*force) {
43         return;
44     }
45
46     msg_print(_("エラー:古いテンポラリ・ファイルが残っています。", "Error: There are old temporary files."));
47     msg_print(_("変愚蛮怒を二重に起動していないか確認してください。", "Make sure you are not running two game processes simultaneously."));
48     msg_print(_("過去に変愚蛮怒がクラッシュした場合は一時ファイルを", "If the temporary files are garbage from an old crashed process, "));
49     msg_print(_("強制的に削除して実行を続けられます。", "you can delete them safely."));
50     if (!get_check(_("強制的に削除してもよろしいですか?", "Do you delete the old temporary files? "))) {
51         quit(_("実行中止", "Aborted."));
52     }
53
54     *force = true;
55 }
56
57 /*!
58  * @brief 保存フロア配列を初期化する / Initialize saved_floors array.
59  * @param player_ptr プレイヤーへの参照ポインタ
60  * @param force テンポラリファイルが残っていた場合も警告なしで強制的に削除するフラグ
61  * @details Make sure that old temporary files are not remaining as gurbages.
62  */
63 void init_saved_floors(PlayerType *player_ptr, bool force)
64 {
65     int fd = -1;
66     BIT_FLAGS mode = 0644;
67     if (savefile[0]) {
68         for (int i = 0; i < MAX_SAVED_FLOORS; i++) {
69             saved_floor_type *sf_ptr = &saved_floors[i];
70             std::string floor_savefile = get_saved_floor_name(i);
71             safe_setuid_grab(player_ptr);
72             fd = fd_make(floor_savefile.data(), mode);
73             safe_setuid_drop();
74             check_saved_tmp_files(fd, &force);
75             safe_setuid_grab(player_ptr);
76             (void)fd_kill(floor_savefile.data());
77             safe_setuid_drop();
78             sf_ptr->floor_id = 0;
79         }
80     }
81
82     max_floor_id = 1;
83     latest_visit_mark = 1;
84     saved_floor_file_sign = (uint32_t)time(nullptr);
85     new_floor_id = 0;
86     player_ptr->change_floor_mode = 0;
87 }
88
89 /*!
90  * @brief 保存フロア用テンポラリファイルを削除する / Kill temporary files
91  * @details Should be called just before the game quit.
92  * @param player_ptr プレイヤーへの参照ポインタ
93  */
94 void clear_saved_floor_files(PlayerType *player_ptr)
95 {
96     for (int i = 0; i < MAX_SAVED_FLOORS; i++) {
97         saved_floor_type *sf_ptr = &saved_floors[i];
98         if ((sf_ptr->floor_id == 0) || (sf_ptr->floor_id == player_ptr->floor_id)) {
99             continue;
100         }
101
102         safe_setuid_grab(player_ptr);
103         (void)fd_kill(get_saved_floor_name(i).data());
104         safe_setuid_drop();
105     }
106 }
107
108 /*!
109  * @brief 保存フロアIDから参照ポインタを得る / Get a pointer for an item of the saved_floors array.
110  * @param floor_id 保存フロアID
111  * @return IDに対応する保存フロアのポインタ、ない場合はnullptrを返す。
112  */
113 saved_floor_type *get_sf_ptr(FLOOR_IDX floor_id)
114 {
115     if (!floor_id) {
116         return nullptr;
117     }
118
119     for (int i = 0; i < MAX_SAVED_FLOORS; i++) {
120         saved_floor_type *sf_ptr = &saved_floors[i];
121         if (sf_ptr->floor_id == floor_id) {
122             return sf_ptr;
123         }
124     }
125
126     return nullptr;
127 }
128
129 /*!
130  * @brief 参照ポインタ先の保存フロアを抹消する / kill a saved floor and get an empty space
131  * @param player_ptr プレイヤーへの参照ポインタ
132  * @param sf_ptr 保存フロアの参照ポインタ
133  */
134 void kill_saved_floor(PlayerType *player_ptr, saved_floor_type *sf_ptr)
135 {
136     if (!sf_ptr || (sf_ptr->floor_id == 0)) {
137         return;
138     }
139
140     if (sf_ptr->floor_id == player_ptr->floor_id) {
141         player_ptr->floor_id = 0;
142         sf_ptr->floor_id = 0;
143         return;
144     }
145
146     safe_setuid_grab(player_ptr);
147     (void)fd_kill(get_saved_floor_name((int)sf_ptr->savefile_id).data());
148     safe_setuid_drop();
149     sf_ptr->floor_id = 0;
150 }
151
152 static FLOOR_IDX find_oldest_floor_idx(PlayerType *player_ptr)
153 {
154     FLOOR_IDX oldest_floor_idx = 0;
155     uint32_t oldest_visit = 0xffffffffL;
156
157     for (FLOOR_IDX fl_idx = 0; fl_idx < MAX_SAVED_FLOORS; fl_idx++) {
158         const saved_floor_type *sf_ptr = &saved_floors[fl_idx];
159         if ((sf_ptr->floor_id == player_ptr->floor_id) || (sf_ptr->visit_mark > oldest_visit)) {
160             continue;
161         }
162
163         oldest_floor_idx = fl_idx;
164         oldest_visit = sf_ptr->visit_mark;
165     }
166
167     return oldest_floor_idx;
168 }
169
170 /*!
171  * @brief 新規に利用可能な保存フロアを返す / Initialize new saved floor and get its floor id.
172  * @param player_ptr プレイヤーへの参照ポインタ
173  * @return 利用可能な保存フロアID
174  * @details
175  * If number of saved floors are already MAX_SAVED_FLOORS, kill the oldest one.
176  */
177 FLOOR_IDX get_new_floor_id(PlayerType *player_ptr)
178 {
179     saved_floor_type *sf_ptr = nullptr;
180     FLOOR_IDX fl_idx;
181     for (fl_idx = 0; fl_idx < MAX_SAVED_FLOORS; fl_idx++) {
182         sf_ptr = &saved_floors[fl_idx];
183         if (!sf_ptr->floor_id) {
184             break;
185         }
186     }
187
188     if (fl_idx == MAX_SAVED_FLOORS) {
189         fl_idx = find_oldest_floor_idx(player_ptr);
190         sf_ptr = &saved_floors[fl_idx];
191         kill_saved_floor(player_ptr, sf_ptr);
192     }
193
194     sf_ptr->savefile_id = fl_idx;
195     sf_ptr->floor_id = max_floor_id;
196     sf_ptr->last_visit = 0;
197     sf_ptr->upper_floor_id = 0;
198     sf_ptr->lower_floor_id = 0;
199     sf_ptr->visit_mark = latest_visit_mark++;
200     sf_ptr->dun_level = player_ptr->current_floor_ptr->dun_level;
201     if (max_floor_id < MAX_SHORT) {
202         max_floor_id++;
203     } else {
204         max_floor_id = 1;
205     } // 32767 floor_ids are all used up!  Re-use ancient IDs.
206
207     return sf_ptr->floor_id;
208 }
209
210 /*!
211  * @brief フロア移動時にペットを伴った場合の準備処理 / Pre-calculate the racial counters of preserved pets
212  * @param player_ptr プレイヤーへの参照ポインタ
213  * @details
214  * To prevent multiple generation of unique monster who is the minion of player
215  */
216 void precalc_cur_num_of_pet(PlayerType *player_ptr)
217 {
218     MonsterEntity *m_ptr;
219     int max_num = player_ptr->wild_mode ? 1 : MAX_PARTY_MON;
220     for (int i = 0; i < max_num; i++) {
221         m_ptr = &party_mon[i];
222         if (!m_ptr->is_valid()) {
223             continue;
224         }
225
226         m_ptr->get_real_r_ref().cur_num++;
227     }
228 }