OSDN Git Service

[Refactor] #2807 Renamed monster-race-definition.h to monster-race-info.h
[hengbandforosx/hengbandosx.git] / src / floor / floor-leaver.cpp
1 #include "floor/floor-leaver.h"
2 #include "cmd-building/cmd-building.h"
3 #include "floor/cave.h"
4 #include "floor/floor-events.h"
5 #include "floor/floor-mode-changer.h"
6 #include "floor/floor-save-util.h"
7 #include "floor/floor-save.h"
8 #include "floor/geometry.h"
9 #include "floor/line-of-sight.h"
10 #include "game-option/birth-options.h"
11 #include "game-option/play-record-options.h"
12 #include "grid/grid.h"
13 #include "inventory/inventory-slot-types.h"
14 #include "io/write-diary.h"
15 #include "mind/mind-ninja.h"
16 #include "monster-floor/monster-lite.h"
17 #include "monster-floor/monster-remover.h"
18 #include "monster-race/monster-race.h"
19 #include "monster/monster-describer.h"
20 #include "monster/monster-description-types.h"
21 #include "pet/pet-util.h"
22 #include "save/floor-writer.h"
23 #include "spell-class/spells-mirror-master.h"
24 #include "system/artifact-type-definition.h"
25 #include "system/dungeon-info.h"
26 #include "system/floor-type-definition.h"
27 #include "system/grid-type-definition.h"
28 #include "system/item-entity.h"
29 #include "system/monster-race-info.h"
30 #include "system/player-type-definition.h"
31 #include "system/terrain-type-definition.h"
32 #include "target/projection-path-calculator.h"
33 #include "util/bit-flags-calculator.h"
34 #include "view/display-messages.h"
35 #include "world/world.h"
36
37 static void check_riding_preservation(PlayerType *player_ptr)
38 {
39     if (!player_ptr->riding) {
40         return;
41     }
42
43     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->riding];
44     if (m_ptr->parent_m_idx) {
45         player_ptr->riding = 0;
46         player_ptr->pet_extra_flags &= ~(PF_TWO_HANDS);
47         player_ptr->riding_ryoute = player_ptr->old_riding_ryoute = false;
48     } else {
49         party_mon[0] = *m_ptr;
50         delete_monster_idx(player_ptr, player_ptr->riding);
51     }
52 }
53
54 static bool check_pet_preservation_conditions(PlayerType *player_ptr, MonsterEntity *m_ptr)
55 {
56     if (reinit_wilderness) {
57         return false;
58     }
59
60     POSITION dis = distance(player_ptr->y, player_ptr->x, m_ptr->fy, m_ptr->fx);
61     if (m_ptr->is_confused() || m_ptr->is_stunned() || m_ptr->is_asleep() || (m_ptr->parent_m_idx != 0)) {
62         return true;
63     }
64
65     if (m_ptr->nickname && ((player_has_los_bold(player_ptr, m_ptr->fy, m_ptr->fx) && projectable(player_ptr, player_ptr->y, player_ptr->x, m_ptr->fy, m_ptr->fx)) || (los(player_ptr, m_ptr->fy, m_ptr->fx, player_ptr->y, player_ptr->x) && projectable(player_ptr, m_ptr->fy, m_ptr->fx, player_ptr->y, player_ptr->x)))) {
66         if (dis > 3) {
67             return true;
68         }
69     } else if (dis > 1) {
70         return true;
71     }
72
73     return false;
74 }
75
76 static void sweep_preserving_pet(PlayerType *player_ptr)
77 {
78     if (player_ptr->wild_mode || player_ptr->current_floor_ptr->inside_arena || player_ptr->phase_out) {
79         return;
80     }
81
82     for (MONSTER_IDX i = player_ptr->current_floor_ptr->m_max - 1, party_monster_num = 1; (i >= 1) && (party_monster_num < MAX_PARTY_MON); i--) {
83         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
84         if (!m_ptr->is_valid() || !m_ptr->is_pet() || (i == player_ptr->riding) || check_pet_preservation_conditions(player_ptr, m_ptr)) {
85             continue;
86         }
87
88         party_mon[party_monster_num] = player_ptr->current_floor_ptr->m_list[i];
89         party_monster_num++;
90         delete_monster_idx(player_ptr, i);
91     }
92 }
93
94 static void record_pet_diary(PlayerType *player_ptr)
95 {
96     if (!record_named_pet) {
97         return;
98     }
99
100     for (MONSTER_IDX i = player_ptr->current_floor_ptr->m_max - 1; i >= 1; i--) {
101         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
102         GAME_TEXT m_name[MAX_NLEN];
103         if (!m_ptr->is_valid() || !m_ptr->is_pet() || !m_ptr->nickname || (player_ptr->riding == i)) {
104             continue;
105         }
106
107         monster_desc(player_ptr, m_name, m_ptr, MD_ASSUME_VISIBLE | MD_INDEF_VISIBLE);
108         exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_MOVED, m_name);
109     }
110 }
111
112 /*!
113  * @brief フロア移動時のペット保存処理 / Preserve_pets
114  * @param player_ptr プレイヤーへの参照ポインタ
115  */
116 static void preserve_pet(PlayerType *player_ptr)
117 {
118     for (auto &mon : party_mon) {
119         mon.r_idx = MonsterRace::empty_id();
120     }
121
122     check_riding_preservation(player_ptr);
123     sweep_preserving_pet(player_ptr);
124     record_pet_diary(player_ptr);
125     for (MONSTER_IDX i = player_ptr->current_floor_ptr->m_max - 1; i >= 1; i--) {
126         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
127         const auto parent_r_idx = player_ptr->current_floor_ptr->m_list[m_ptr->parent_m_idx].r_idx;
128         if ((m_ptr->parent_m_idx == 0) || MonsterRace(parent_r_idx).is_valid()) {
129             continue;
130         }
131
132         if (is_seen(player_ptr, m_ptr)) {
133             GAME_TEXT m_name[MAX_NLEN];
134             monster_desc(player_ptr, m_name, m_ptr, 0);
135             msg_format(_("%sは消え去った!", "%^s disappears!"), m_name);
136         }
137
138         delete_monster_idx(player_ptr, i);
139     }
140 }
141
142 /*!
143  * @brief 新フロアに移動元フロアに繋がる階段を配置する / Virtually teleport onto the stairs that is connecting between two floors.
144  * @param sf_ptr 移動元の保存フロア構造体参照ポインタ
145  */
146 static void locate_connected_stairs(PlayerType *player_ptr, FloorType *floor_ptr, saved_floor_type *sf_ptr, BIT_FLAGS floor_mode)
147 {
148     POSITION sx = 0;
149     POSITION sy = 0;
150     POSITION x_table[20];
151     POSITION y_table[20];
152     int num = 0;
153     for (POSITION y = 0; y < floor_ptr->height; y++) {
154         for (POSITION x = 0; x < floor_ptr->width; x++) {
155             auto *g_ptr = &floor_ptr->grid_array[y][x];
156             auto *f_ptr = &terrains_info[g_ptr->feat];
157             bool ok = false;
158             if (floor_mode & CFM_UP) {
159                 if (f_ptr->flags.has_all_of({ TerrainCharacteristics::LESS, TerrainCharacteristics::STAIRS }) && f_ptr->flags.has_not(TerrainCharacteristics::SPECIAL)) {
160                     ok = true;
161                     if (g_ptr->special && g_ptr->special == sf_ptr->upper_floor_id) {
162                         sx = x;
163                         sy = y;
164                     }
165                 }
166             } else if (floor_mode & CFM_DOWN) {
167                 if (f_ptr->flags.has_all_of({ TerrainCharacteristics::MORE, TerrainCharacteristics::STAIRS }) && f_ptr->flags.has_not(TerrainCharacteristics::SPECIAL)) {
168                     ok = true;
169                     if (g_ptr->special && g_ptr->special == sf_ptr->lower_floor_id) {
170                         sx = x;
171                         sy = y;
172                     }
173                 }
174             } else {
175                 if (f_ptr->flags.has(TerrainCharacteristics::BLDG)) {
176                     ok = true;
177                 }
178             }
179
180             if (ok && (num < 20)) {
181                 x_table[num] = x;
182                 y_table[num] = y;
183                 num++;
184             }
185         }
186     }
187
188     if (sx) {
189         player_ptr->y = sy;
190         player_ptr->x = sx;
191         return;
192     }
193
194     if (num == 0) {
195         prepare_change_floor_mode(player_ptr, CFM_RAND_PLACE | CFM_NO_RETURN);
196         if (!feat_uses_special(floor_ptr->grid_array[player_ptr->y][player_ptr->x].feat)) {
197             floor_ptr->grid_array[player_ptr->y][player_ptr->x].special = 0;
198         }
199
200         return;
201     }
202
203     int i = randint0(num);
204     player_ptr->y = y_table[i];
205     player_ptr->x = x_table[i];
206 }
207
208 /*!
209  * @brief フロア移動時、プレイヤーの移動先モンスターが既にいた場合ランダムな近隣に移動させる / When a monster is at a place where player will return,
210  */
211 static void get_out_monster(PlayerType *player_ptr)
212 {
213     int tries = 0;
214     POSITION dis = 1;
215     POSITION oy = player_ptr->y;
216     POSITION ox = player_ptr->x;
217     auto *floor_ptr = player_ptr->current_floor_ptr;
218     MONSTER_IDX m_idx = floor_ptr->grid_array[oy][ox].m_idx;
219     if (m_idx == 0) {
220         return;
221     }
222
223     while (true) {
224         MonsterEntity *m_ptr;
225         POSITION ny = rand_spread(oy, dis);
226         POSITION nx = rand_spread(ox, dis);
227         tries++;
228         if (tries > 10000) {
229             return;
230         }
231
232         if (tries > 20 * dis * dis) {
233             dis++;
234         }
235
236         if (!in_bounds(floor_ptr, ny, nx) || !is_cave_empty_bold(player_ptr, ny, nx) || floor_ptr->grid_array[ny][nx].is_rune_protection() || floor_ptr->grid_array[ny][nx].is_rune_explosion() || pattern_tile(floor_ptr, ny, nx)) {
237             continue;
238         }
239
240         m_ptr = &floor_ptr->m_list[m_idx];
241         floor_ptr->grid_array[oy][ox].m_idx = 0;
242         floor_ptr->grid_array[ny][nx].m_idx = m_idx;
243         m_ptr->fy = ny;
244         m_ptr->fx = nx;
245         return;
246     }
247 }
248
249 /*!
250  * @brief クエスト・フロア内のモンスター・インベントリ情報を保存する
251  * @param player_ptr プレイヤーへの参照ポインタ
252  */
253 static void preserve_info(PlayerType *player_ptr)
254 {
255     auto quest_r_idx = MonsterRace::empty_id();
256     const auto &quest_list = QuestList::get_instance();
257     for (const auto &[q_idx, q_ref] : quest_list) {
258         auto quest_relating_monster = (q_ref.status == QuestStatusType::TAKEN);
259         quest_relating_monster &= ((q_ref.type == QuestKindType::KILL_LEVEL) || (q_ref.type == QuestKindType::RANDOM));
260         quest_relating_monster &= (q_ref.level == player_ptr->current_floor_ptr->dun_level);
261         quest_relating_monster &= (player_ptr->dungeon_idx == q_ref.dungeon);
262         quest_relating_monster &= !(q_ref.flags & QUEST_FLAG_PRESET);
263         if (quest_relating_monster) {
264             quest_r_idx = q_ref.r_idx;
265         }
266     }
267
268     for (DUNGEON_IDX i = 1; i < player_ptr->current_floor_ptr->m_max; i++) {
269         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
270         if (!m_ptr->is_valid() || (quest_r_idx != m_ptr->r_idx)) {
271             continue;
272         }
273
274         const auto &r_ref = m_ptr->get_real_r_ref();
275         if (r_ref.kind_flags.has(MonsterKindType::UNIQUE) || (r_ref.population_flags.has(MonsterPopulationType::NAZGUL))) {
276             continue;
277         }
278
279         delete_monster_idx(player_ptr, i);
280     }
281
282     for (DUNGEON_IDX i = 0; i < INVEN_PACK; i++) {
283         auto *o_ptr = &player_ptr->inventory_list[i];
284         if (!o_ptr->is_valid()) {
285             continue;
286         }
287
288         if (o_ptr->is_fixed_artifact()) {
289             artifacts_info.at(o_ptr->fixed_artifact_idx).floor_id = 0;
290         }
291     }
292 }
293
294 static void set_grid_by_leaving_floor(PlayerType *player_ptr, grid_type **g_ptr)
295 {
296     if ((player_ptr->change_floor_mode & CFM_SAVE_FLOORS) == 0) {
297         return;
298     }
299
300     *g_ptr = &player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x];
301     auto *f_ptr = &terrains_info[(*g_ptr)->feat];
302     if ((*g_ptr)->special && f_ptr->flags.has_not(TerrainCharacteristics::SPECIAL) && get_sf_ptr((*g_ptr)->special)) {
303         new_floor_id = (*g_ptr)->special;
304     }
305
306     if (f_ptr->flags.has_all_of({ TerrainCharacteristics::STAIRS, TerrainCharacteristics::SHAFT })) {
307         prepare_change_floor_mode(player_ptr, CFM_SHAFT);
308     }
309 }
310
311 static void jump_floors(PlayerType *player_ptr)
312 {
313     const auto mode = player_ptr->change_floor_mode;
314     if (none_bits(mode, CFM_DOWN | CFM_UP)) {
315         return;
316     }
317
318     auto move_num = 0;
319     if (any_bits(mode, CFM_DOWN)) {
320         move_num = 1;
321     } else if (any_bits(mode, CFM_UP)) {
322         move_num = -1;
323     }
324
325     if (any_bits(mode, CFM_SHAFT)) {
326         move_num *= 2;
327     }
328
329     auto &floor_ref = *player_ptr->current_floor_ptr;
330     if (any_bits(mode, CFM_DOWN)) {
331         if (!floor_ref.is_in_dungeon()) {
332             move_num = dungeons_info[player_ptr->dungeon_idx].mindepth;
333         }
334     } else if (any_bits(mode, CFM_UP)) {
335         if (floor_ref.dun_level + move_num < dungeons_info[player_ptr->dungeon_idx].mindepth) {
336             move_num = -floor_ref.dun_level;
337         }
338     }
339
340     floor_ref.dun_level += move_num;
341 }
342
343 static void exit_to_wilderness(PlayerType *player_ptr)
344 {
345     if (player_ptr->current_floor_ptr->is_in_dungeon() || (player_ptr->dungeon_idx == 0)) {
346         return;
347     }
348
349     player_ptr->leaving_dungeon = true;
350     if (!vanilla_town && !lite_town) {
351         player_ptr->wilderness_y = dungeons_info[player_ptr->dungeon_idx].dy;
352         player_ptr->wilderness_x = dungeons_info[player_ptr->dungeon_idx].dx;
353     }
354
355     player_ptr->recall_dungeon = player_ptr->dungeon_idx;
356     player_ptr->word_recall = 0;
357     player_ptr->dungeon_idx = 0;
358     player_ptr->change_floor_mode &= ~CFM_SAVE_FLOORS; // TODO
359 }
360
361 static void kill_saved_floors(PlayerType *player_ptr, saved_floor_type *sf_ptr)
362 {
363     if (!(player_ptr->change_floor_mode & CFM_SAVE_FLOORS)) {
364         for (DUNGEON_IDX i = 0; i < MAX_SAVED_FLOORS; i++) {
365             kill_saved_floor(player_ptr, &saved_floors[i]);
366         }
367
368         latest_visit_mark = 1;
369         return;
370     }
371     if (player_ptr->change_floor_mode & CFM_NO_RETURN) {
372         kill_saved_floor(player_ptr, sf_ptr);
373     }
374 }
375
376 static void refresh_new_floor_id(PlayerType *player_ptr, grid_type *g_ptr)
377 {
378     if (new_floor_id != 0) {
379         return;
380     }
381
382     new_floor_id = get_new_floor_id(player_ptr);
383     if ((g_ptr != nullptr) && !feat_uses_special(g_ptr->feat)) {
384         g_ptr->special = new_floor_id;
385     }
386 }
387
388 static void update_upper_lower_or_floor_id(PlayerType *player_ptr, saved_floor_type *sf_ptr)
389 {
390     if ((player_ptr->change_floor_mode & CFM_RAND_CONNECT) == 0) {
391         return;
392     }
393
394     if (player_ptr->change_floor_mode & CFM_UP) {
395         sf_ptr->upper_floor_id = new_floor_id;
396     } else if (player_ptr->change_floor_mode & CFM_DOWN) {
397         sf_ptr->lower_floor_id = new_floor_id;
398     }
399 }
400
401 static void exe_leave_floor(PlayerType *player_ptr, saved_floor_type *sf_ptr)
402 {
403     grid_type *g_ptr = nullptr;
404     set_grid_by_leaving_floor(player_ptr, &g_ptr);
405     jump_floors(player_ptr);
406     exit_to_wilderness(player_ptr);
407     kill_saved_floors(player_ptr, sf_ptr);
408     if (player_ptr->floor_id == 0) {
409         return;
410     }
411
412     refresh_new_floor_id(player_ptr, g_ptr);
413     update_upper_lower_or_floor_id(player_ptr, sf_ptr);
414     if (((player_ptr->change_floor_mode & CFM_SAVE_FLOORS) == 0) || ((player_ptr->change_floor_mode & CFM_NO_RETURN) != 0)) {
415         return;
416     }
417
418     get_out_monster(player_ptr);
419     sf_ptr->last_visit = w_ptr->game_turn;
420     forget_lite(player_ptr->current_floor_ptr);
421     forget_view(player_ptr->current_floor_ptr);
422     clear_mon_lite(player_ptr->current_floor_ptr);
423     if (save_floor(player_ptr, sf_ptr, 0)) {
424         return;
425     }
426
427     prepare_change_floor_mode(player_ptr, CFM_NO_RETURN);
428     kill_saved_floor(player_ptr, get_sf_ptr(player_ptr->floor_id));
429 }
430
431 /*!
432  * @brief 現在のフロアを離れるに伴って行なわれる保存処理
433  * / Maintain quest monsters, mark next floor_id at stairs, save current floor, and prepare to enter next floor.
434  * @param player_ptr プレイヤーへの参照ポインタ
435  */
436 void leave_floor(PlayerType *player_ptr)
437 {
438     preserve_pet(player_ptr);
439     SpellsMirrorMaster(player_ptr).remove_all_mirrors(false);
440     set_superstealth(player_ptr, false);
441
442     new_floor_id = 0;
443
444     preserve_info(player_ptr);
445     saved_floor_type *sf_ptr = get_sf_ptr(player_ptr->floor_id);
446     if (player_ptr->change_floor_mode & CFM_RAND_CONNECT) {
447         locate_connected_stairs(player_ptr, player_ptr->current_floor_ptr, sf_ptr, player_ptr->change_floor_mode);
448     }
449
450     exe_leave_floor(player_ptr, sf_ptr);
451 }