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"
37 static void check_riding_preservation(PlayerType *player_ptr)
39 if (!player_ptr->riding) {
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;
49 party_mon[0] = *m_ptr;
50 delete_monster_idx(player_ptr, player_ptr->riding);
54 static bool check_pet_preservation_conditions(PlayerType *player_ptr, MonsterEntity *m_ptr)
56 if (reinit_wilderness) {
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)) {
65 if (m_ptr->is_named() && ((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)))) {
76 static void sweep_preserving_pet(PlayerType *player_ptr)
78 if (player_ptr->wild_mode || player_ptr->current_floor_ptr->inside_arena || player_ptr->phase_out) {
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)) {
88 party_mon[party_monster_num] = player_ptr->current_floor_ptr->m_list[i];
90 delete_monster_idx(player_ptr, i);
94 static void record_pet_diary(PlayerType *player_ptr)
96 if (!record_named_pet) {
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 if (!m_ptr->is_valid() || !m_ptr->is_named_pet() || (player_ptr->riding == i)) {
106 exe_write_diary(player_ptr, DIARY_NAMED_PET, RECORD_NAMED_PET_MOVED, monster_desc(player_ptr, m_ptr, MD_ASSUME_VISIBLE | MD_INDEF_VISIBLE));
111 * @brief フロア移動時のペット保存処理 / Preserve_pets
112 * @param player_ptr プレイヤーへの参照ポインタ
114 static void preserve_pet(PlayerType *player_ptr)
116 for (auto &mon : party_mon) {
117 mon.r_idx = MonsterRace::empty_id();
120 check_riding_preservation(player_ptr);
121 sweep_preserving_pet(player_ptr);
122 record_pet_diary(player_ptr);
123 for (MONSTER_IDX i = player_ptr->current_floor_ptr->m_max - 1; i >= 1; i--) {
124 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
125 const auto parent_r_idx = player_ptr->current_floor_ptr->m_list[m_ptr->parent_m_idx].r_idx;
126 if ((m_ptr->parent_m_idx == 0) || MonsterRace(parent_r_idx).is_valid()) {
130 if (is_seen(player_ptr, m_ptr)) {
131 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
132 msg_format(_("%sは消え去った!", "%s^ disappears!"), m_name.data());
135 delete_monster_idx(player_ptr, i);
140 * @brief 新フロアに移動元フロアに繋がる階段を配置する / Virtually teleport onto the stairs that is connecting between two floors.
141 * @param sf_ptr 移動元の保存フロア構造体参照ポインタ
143 static void locate_connected_stairs(PlayerType *player_ptr, FloorType *floor_ptr, saved_floor_type *sf_ptr, BIT_FLAGS floor_mode)
147 POSITION x_table[20];
148 POSITION y_table[20];
150 for (POSITION y = 0; y < floor_ptr->height; y++) {
151 for (POSITION x = 0; x < floor_ptr->width; x++) {
152 auto *g_ptr = &floor_ptr->grid_array[y][x];
153 auto *f_ptr = &terrains_info[g_ptr->feat];
155 if (floor_mode & CFM_UP) {
156 if (f_ptr->flags.has_all_of({ TerrainCharacteristics::LESS, TerrainCharacteristics::STAIRS }) && f_ptr->flags.has_not(TerrainCharacteristics::SPECIAL)) {
158 if (g_ptr->special && g_ptr->special == sf_ptr->upper_floor_id) {
163 } else if (floor_mode & CFM_DOWN) {
164 if (f_ptr->flags.has_all_of({ TerrainCharacteristics::MORE, TerrainCharacteristics::STAIRS }) && f_ptr->flags.has_not(TerrainCharacteristics::SPECIAL)) {
166 if (g_ptr->special && g_ptr->special == sf_ptr->lower_floor_id) {
172 if (f_ptr->flags.has(TerrainCharacteristics::BLDG)) {
177 if (ok && (num < 20)) {
192 prepare_change_floor_mode(player_ptr, CFM_RAND_PLACE | CFM_NO_RETURN);
193 if (!feat_uses_special(floor_ptr->grid_array[player_ptr->y][player_ptr->x].feat)) {
194 floor_ptr->grid_array[player_ptr->y][player_ptr->x].special = 0;
200 int i = randint0(num);
201 player_ptr->y = y_table[i];
202 player_ptr->x = x_table[i];
206 * @brief フロア移動時、プレイヤーの移動先モンスターが既にいた場合ランダムな近隣に移動させる / When a monster is at a place where player will return,
208 static void get_out_monster(PlayerType *player_ptr)
212 POSITION oy = player_ptr->y;
213 POSITION ox = player_ptr->x;
214 auto *floor_ptr = player_ptr->current_floor_ptr;
215 MONSTER_IDX m_idx = floor_ptr->grid_array[oy][ox].m_idx;
221 MonsterEntity *m_ptr;
222 POSITION ny = rand_spread(oy, dis);
223 POSITION nx = rand_spread(ox, dis);
229 if (tries > 20 * dis * dis) {
233 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 m_ptr = &floor_ptr->m_list[m_idx];
238 floor_ptr->grid_array[oy][ox].m_idx = 0;
239 floor_ptr->grid_array[ny][nx].m_idx = m_idx;
247 * @brief クエスト・フロア内のモンスター・インベントリ情報を保存する
248 * @param player_ptr プレイヤーへの参照ポインタ
250 static void preserve_info(PlayerType *player_ptr)
252 auto quest_r_idx = MonsterRace::empty_id();
253 const auto &quest_list = QuestList::get_instance();
254 const auto &floor = *player_ptr->current_floor_ptr;
255 for (const auto &[q_idx, quest] : quest_list) {
256 auto quest_relating_monster = (quest.status == QuestStatusType::TAKEN);
257 quest_relating_monster &= ((quest.type == QuestKindType::KILL_LEVEL) || (quest.type == QuestKindType::RANDOM));
258 quest_relating_monster &= (quest.level == floor.dun_level);
259 quest_relating_monster &= (floor.dungeon_idx == quest.dungeon);
260 quest_relating_monster &= !(quest.flags & QUEST_FLAG_PRESET);
261 if (quest_relating_monster) {
262 quest_r_idx = quest.r_idx;
266 for (DUNGEON_IDX i = 1; i < floor.m_max; i++) {
267 auto *m_ptr = &floor.m_list[i];
268 if (!m_ptr->is_valid() || (quest_r_idx != m_ptr->r_idx)) {
272 const auto &r_ref = m_ptr->get_real_r_ref();
273 if (r_ref.kind_flags.has(MonsterKindType::UNIQUE) || (r_ref.population_flags.has(MonsterPopulationType::NAZGUL))) {
277 delete_monster_idx(player_ptr, i);
280 for (DUNGEON_IDX i = 0; i < INVEN_PACK; i++) {
281 auto *o_ptr = &player_ptr->inventory_list[i];
282 if (!o_ptr->is_valid()) {
286 if (o_ptr->is_fixed_artifact()) {
287 o_ptr->get_fixed_artifact().floor_id = 0;
292 static void set_grid_by_leaving_floor(PlayerType *player_ptr, grid_type **g_ptr)
294 if ((player_ptr->change_floor_mode & CFM_SAVE_FLOORS) == 0) {
298 *g_ptr = &player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x];
299 auto *f_ptr = &terrains_info[(*g_ptr)->feat];
300 if ((*g_ptr)->special && f_ptr->flags.has_not(TerrainCharacteristics::SPECIAL) && get_sf_ptr((*g_ptr)->special)) {
301 new_floor_id = (*g_ptr)->special;
304 if (f_ptr->flags.has_all_of({ TerrainCharacteristics::STAIRS, TerrainCharacteristics::SHAFT })) {
305 prepare_change_floor_mode(player_ptr, CFM_SHAFT);
309 static void jump_floors(PlayerType *player_ptr)
311 const auto mode = player_ptr->change_floor_mode;
312 if (none_bits(mode, CFM_DOWN | CFM_UP)) {
317 if (any_bits(mode, CFM_DOWN)) {
319 } else if (any_bits(mode, CFM_UP)) {
323 if (any_bits(mode, CFM_SHAFT)) {
327 auto &floor = *player_ptr->current_floor_ptr;
328 const auto &dungeon = dungeons_info[floor.dungeon_idx];
329 if (any_bits(mode, CFM_DOWN)) {
330 if (!floor.is_in_dungeon()) {
331 move_num = dungeon.mindepth;
333 } else if (any_bits(mode, CFM_UP)) {
334 if (floor.dun_level + move_num < dungeon.mindepth) {
335 move_num = -floor.dun_level;
339 floor.dun_level += move_num;
342 static void exit_to_wilderness(PlayerType *player_ptr)
344 auto &floor = *player_ptr->current_floor_ptr;
345 if (floor.is_in_dungeon() || (floor.dungeon_idx == 0)) {
349 player_ptr->leaving_dungeon = true;
350 if (!vanilla_town && !lite_town) {
351 const auto &dungeon = dungeons_info[floor.dungeon_idx];
352 player_ptr->wilderness_y = dungeon.dy;
353 player_ptr->wilderness_x = dungeon.dx;
356 player_ptr->recall_dungeon = floor.dungeon_idx;
357 player_ptr->word_recall = 0;
358 floor.dungeon_idx = 0;
359 player_ptr->change_floor_mode &= ~CFM_SAVE_FLOORS; // TODO
362 static void kill_saved_floors(PlayerType *player_ptr, saved_floor_type *sf_ptr)
364 if (!(player_ptr->change_floor_mode & CFM_SAVE_FLOORS)) {
365 for (DUNGEON_IDX i = 0; i < MAX_SAVED_FLOORS; i++) {
366 kill_saved_floor(player_ptr, &saved_floors[i]);
369 latest_visit_mark = 1;
372 if (player_ptr->change_floor_mode & CFM_NO_RETURN) {
373 kill_saved_floor(player_ptr, sf_ptr);
377 static void refresh_new_floor_id(PlayerType *player_ptr, grid_type *g_ptr)
379 if (new_floor_id != 0) {
383 new_floor_id = get_new_floor_id(player_ptr);
384 if ((g_ptr != nullptr) && !feat_uses_special(g_ptr->feat)) {
385 g_ptr->special = new_floor_id;
389 static void update_upper_lower_or_floor_id(PlayerType *player_ptr, saved_floor_type *sf_ptr)
391 if ((player_ptr->change_floor_mode & CFM_RAND_CONNECT) == 0) {
395 if (player_ptr->change_floor_mode & CFM_UP) {
396 sf_ptr->upper_floor_id = new_floor_id;
397 } else if (player_ptr->change_floor_mode & CFM_DOWN) {
398 sf_ptr->lower_floor_id = new_floor_id;
402 static void exe_leave_floor(PlayerType *player_ptr, saved_floor_type *sf_ptr)
404 grid_type *g_ptr = nullptr;
405 set_grid_by_leaving_floor(player_ptr, &g_ptr);
406 jump_floors(player_ptr);
407 exit_to_wilderness(player_ptr);
408 kill_saved_floors(player_ptr, sf_ptr);
409 if (player_ptr->floor_id == 0) {
413 refresh_new_floor_id(player_ptr, g_ptr);
414 update_upper_lower_or_floor_id(player_ptr, sf_ptr);
415 if (((player_ptr->change_floor_mode & CFM_SAVE_FLOORS) == 0) || ((player_ptr->change_floor_mode & CFM_NO_RETURN) != 0)) {
419 get_out_monster(player_ptr);
420 sf_ptr->last_visit = w_ptr->game_turn;
421 forget_lite(player_ptr->current_floor_ptr);
422 forget_view(player_ptr->current_floor_ptr);
423 clear_mon_lite(player_ptr->current_floor_ptr);
424 if (save_floor(player_ptr, sf_ptr, 0)) {
428 prepare_change_floor_mode(player_ptr, CFM_NO_RETURN);
429 kill_saved_floor(player_ptr, get_sf_ptr(player_ptr->floor_id));
433 * @brief 現在のフロアを離れるに伴って行なわれる保存処理
434 * / Maintain quest monsters, mark next floor_id at stairs, save current floor, and prepare to enter next floor.
435 * @param player_ptr プレイヤーへの参照ポインタ
437 void leave_floor(PlayerType *player_ptr)
439 preserve_pet(player_ptr);
440 SpellsMirrorMaster(player_ptr).remove_all_mirrors(false);
441 set_superstealth(player_ptr, false);
445 preserve_info(player_ptr);
446 saved_floor_type *sf_ptr = get_sf_ptr(player_ptr->floor_id);
447 if (player_ptr->change_floor_mode & CFM_RAND_CONNECT) {
448 locate_connected_stairs(player_ptr, player_ptr->current_floor_ptr, sf_ptr, player_ptr->change_floor_mode);
451 exe_leave_floor(player_ptr, sf_ptr);