OSDN Git Service

aeb6d9be7e3936a10875d1b23f074ce417afbebb
[hengbandforosx/hengbandosx.git] / src / floor / floor-changer.cpp
1 #include "floor/floor-changer.h"
2 #include "action/travel-execution.h"
3 #include "dungeon/quest-monster-placer.h"
4 #include "dungeon/quest.h"
5 #include "effect/effect-characteristics.h"
6 #include "floor/floor-generator.h"
7 #include "floor/floor-mode-changer.h"
8 #include "floor/floor-object.h"
9 #include "floor/floor-save-util.h"
10 #include "floor/floor-save.h"
11 #include "floor/floor-util.h"
12 #include "floor/wild.h"
13 #include "game-option/birth-options.h"
14 #include "game-option/play-record-options.h"
15 #include "grid/feature.h"
16 #include "grid/grid.h"
17 #include "io/write-diary.h"
18 #include "load/floor-loader.h"
19 #include "main/sound-of-music.h"
20 #include "monster-floor/monster-generator.h"
21 #include "monster-floor/monster-remover.h"
22 #include "monster-floor/monster-summon.h"
23 #include "monster-race/monster-race.h"
24 #include "monster-race/race-flags1.h"
25 #include "monster-race/race-flags2.h"
26 #include "monster-race/race-flags7.h"
27 #include "monster/monster-describer.h"
28 #include "monster/monster-description-types.h"
29 #include "monster/monster-flag-types.h"
30 #include "monster/monster-info.h"
31 #include "monster/monster-list.h"
32 #include "monster/monster-status-setter.h"
33 #include "monster/monster-status.h"
34 #include "monster/monster-update.h"
35 #include "player-base/player-class.h"
36 #include "spell-kind/spells-floor.h"
37 #include "system/artifact-type-definition.h"
38 #include "system/dungeon-info.h"
39 #include "system/floor-type-definition.h"
40 #include "system/grid-type-definition.h"
41 #include "system/item-entity.h"
42 #include "system/monster-race-info.h"
43 #include "system/player-type-definition.h"
44 #include "system/terrain-type-definition.h"
45 #include "timed-effect/player-blindness.h"
46 #include "timed-effect/timed-effects.h"
47 #include "util/bit-flags-calculator.h"
48 #include "view/display-messages.h"
49 #include "window/main-window-util.h"
50 #include "world/world.h"
51
52 #include <algorithm>
53
54 /*!
55  * @brief 階段移動先のフロアが生成できない時に簡単な行き止まりマップを作成する / Builds the dead end
56  */
57 static void build_dead_end(PlayerType *player_ptr, saved_floor_type *sf_ptr)
58 {
59     msg_print(_("階段は行き止まりだった。", "The staircases come to a dead end..."));
60     clear_cave(player_ptr);
61     player_ptr->x = player_ptr->y = 0;
62     set_floor_and_wall(0);
63     player_ptr->current_floor_ptr->height = SCREEN_HGT;
64     player_ptr->current_floor_ptr->width = SCREEN_WID;
65     for (POSITION y = 0; y < MAX_HGT; y++) {
66         for (POSITION x = 0; x < MAX_WID; x++) {
67             place_bold(player_ptr, y, x, GB_SOLID_PERM);
68         }
69     }
70
71     player_ptr->y = player_ptr->current_floor_ptr->height / 2;
72     player_ptr->x = player_ptr->current_floor_ptr->width / 2;
73     place_bold(player_ptr, player_ptr->y, player_ptr->x, GB_FLOOR);
74     wipe_generate_random_floor_flags(player_ptr->current_floor_ptr);
75
76     if (player_ptr->change_floor_mode & CFM_UP) {
77         sf_ptr->upper_floor_id = 0;
78     } else if (player_ptr->change_floor_mode & CFM_DOWN) {
79         sf_ptr->lower_floor_id = 0;
80     }
81 }
82
83 static MONSTER_IDX decide_pet_index(PlayerType *player_ptr, const int current_monster, POSITION *cy, POSITION *cx)
84 {
85     auto *floor_ptr = player_ptr->current_floor_ptr;
86     if (current_monster == 0) {
87         MONSTER_IDX m_idx = m_pop(floor_ptr);
88         player_ptr->riding = m_idx;
89         if (m_idx) {
90             *cy = player_ptr->y;
91             *cx = player_ptr->x;
92         }
93
94         return m_idx;
95     }
96
97     POSITION d;
98     for (d = 1; d < A_MAX; d++) {
99         int j;
100         for (j = 1000; j > 0; j--) {
101             scatter(player_ptr, cy, cx, player_ptr->y, player_ptr->x, d, PROJECT_NONE);
102             if (monster_can_enter(player_ptr, *cy, *cx, &monraces_info[party_mon[current_monster].r_idx], 0)) {
103                 break;
104             }
105         }
106
107         if (j != 0) {
108             break;
109         }
110     }
111
112     return (d == 6) ? 0 : m_pop(floor_ptr);
113 }
114
115 static MonsterRaceInfo &set_pet_params(PlayerType *player_ptr, const int current_monster, MONSTER_IDX m_idx, const POSITION cy, const POSITION cx)
116 {
117     auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
118     player_ptr->current_floor_ptr->grid_array[cy][cx].m_idx = m_idx;
119     m_ptr->r_idx = party_mon[current_monster].r_idx;
120     *m_ptr = party_mon[current_monster];
121     m_ptr->fy = cy;
122     m_ptr->fx = cx;
123     m_ptr->current_floor_ptr = player_ptr->current_floor_ptr;
124     m_ptr->ml = true;
125     m_ptr->mtimed[MTIMED_CSLEEP] = 0;
126     m_ptr->hold_o_idx_list.clear();
127     m_ptr->target_y = 0;
128     auto &r_ref = m_ptr->get_real_monrace();
129     if (r_ref.behavior_flags.has(MonsterBehaviorType::PREVENT_SUDDEN_MAGIC) && !ironman_nightmare) {
130         m_ptr->mflag.set(MonsterTemporaryFlagType::PREVENT_MAGIC);
131     }
132
133     return r_ref;
134 }
135
136 /*!
137  * @brief 移動先のフロアに伴ったペットを配置する / Place preserved pet monsters on new floor
138  * @param player_ptr プレイヤーへの参照ポインタ
139  */
140 static void place_pet(PlayerType *player_ptr)
141 {
142     int max_num = player_ptr->wild_mode ? 1 : MAX_PARTY_MON;
143     for (int current_monster = 0; current_monster < max_num; current_monster++) {
144         POSITION cy = 0;
145         POSITION cx = 0;
146         if (!MonsterRace(party_mon[current_monster].r_idx).is_valid()) {
147             continue;
148         }
149
150         MONSTER_IDX m_idx = decide_pet_index(player_ptr, current_monster, &cy, &cx);
151         if (m_idx != 0) {
152             auto &r_ref = set_pet_params(player_ptr, current_monster, m_idx, cy, cx);
153             update_monster(player_ptr, m_idx, true);
154             lite_spot(player_ptr, cy, cx);
155             if (any_bits(r_ref.flags2, RF2_MULTIPLY)) {
156                 player_ptr->current_floor_ptr->num_repro++;
157             }
158         } else {
159             auto *m_ptr = &party_mon[current_monster];
160             auto &r_ref = m_ptr->get_real_monrace();
161             msg_format(_("%sとはぐれてしまった。", "You have lost sight of %s."), monster_desc(player_ptr, m_ptr, 0).data());
162             if (record_named_pet && m_ptr->is_named()) {
163                 exe_write_diary(player_ptr, DiaryKind::NAMED_PET, RECORD_NAMED_PET_LOST_SIGHT, monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE));
164             }
165
166             if (r_ref.cur_num) {
167                 r_ref.cur_num--;
168             }
169         }
170     }
171
172     std::fill(std::begin(party_mon), std::end(party_mon), MonsterEntity{});
173 }
174
175 /*!
176  * @brief ユニークモンスターやアーティファクトの所在フロアを更新する
177  * @param floor_ptr 現在フロアへのポインタ
178  * @param cur_floor_id 現在のフロアID
179  * @details
180  * The floor_id and floor_id are not updated correctly
181  * while new floor creation since dungeons may be re-created by
182  * auto-scum option.
183  */
184 static void update_unique_artifact(FloorType *floor_ptr, int16_t cur_floor_id)
185 {
186     for (int i = 1; i < floor_ptr->m_max; i++) {
187         const auto &m_ref = floor_ptr->m_list[i];
188         if (!m_ref.is_valid()) {
189             continue;
190         }
191
192         auto &r_ref = m_ref.get_real_monrace();
193         if (r_ref.kind_flags.has(MonsterKindType::UNIQUE) || (r_ref.population_flags.has(MonsterPopulationType::NAZGUL))) {
194             r_ref.floor_id = cur_floor_id;
195         }
196     }
197
198     for (int i = 1; i < floor_ptr->o_max; i++) {
199         const auto &o_ref = floor_ptr->o_list[i];
200         if (!o_ref.is_valid()) {
201             continue;
202         }
203
204         if (o_ref.is_fixed_artifact()) {
205             o_ref.get_fixed_artifact().floor_id = cur_floor_id;
206         }
207     }
208 }
209
210 static bool is_visited_floor(saved_floor_type *sf_ptr)
211 {
212     return sf_ptr->last_visit != 0;
213 }
214
215 static void check_visited_floor(PlayerType *player_ptr, saved_floor_type *sf_ptr, bool *loaded)
216 {
217     if (!is_visited_floor(sf_ptr) || !load_floor(player_ptr, sf_ptr, 0)) {
218         return;
219     }
220
221     *loaded = true;
222     if ((player_ptr->change_floor_mode & CFM_NO_RETURN) == 0) {
223         return;
224     }
225
226     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x];
227     if (feat_uses_special(g_ptr->feat)) {
228         return;
229     }
230
231     if (player_ptr->change_floor_mode & (CFM_DOWN | CFM_UP)) {
232         g_ptr->feat = rand_choice(feat_ground_type);
233     }
234
235     g_ptr->special = 0;
236 }
237
238 static void update_floor_id(PlayerType *player_ptr, saved_floor_type *sf_ptr)
239 {
240     if (!player_ptr->in_saved_floor()) {
241         if (player_ptr->change_floor_mode & CFM_UP) {
242             sf_ptr->lower_floor_id = 0;
243         } else if (player_ptr->change_floor_mode & CFM_DOWN) {
244             sf_ptr->upper_floor_id = 0;
245         }
246
247         return;
248     }
249
250     saved_floor_type *cur_sf_ptr = get_sf_ptr(player_ptr->floor_id);
251     if (player_ptr->change_floor_mode & CFM_UP) {
252         if (cur_sf_ptr->upper_floor_id == new_floor_id) {
253             sf_ptr->lower_floor_id = player_ptr->floor_id;
254         }
255
256         return;
257     }
258
259     if (((player_ptr->change_floor_mode & CFM_DOWN) != 0) && (cur_sf_ptr->lower_floor_id == new_floor_id)) {
260         sf_ptr->upper_floor_id = player_ptr->floor_id;
261     }
262 }
263
264 static void reset_unique_by_floor_change(PlayerType *player_ptr)
265 {
266     auto *floor_ptr = player_ptr->current_floor_ptr;
267     for (MONSTER_IDX i = 1; i < floor_ptr->m_max; i++) {
268         auto *m_ptr = &floor_ptr->m_list[i];
269         if (!m_ptr->is_valid()) {
270             continue;
271         }
272
273         if (!m_ptr->is_pet()) {
274             m_ptr->hp = m_ptr->maxhp = m_ptr->max_maxhp;
275             (void)set_monster_fast(player_ptr, i, 0);
276             (void)set_monster_slow(player_ptr, i, 0);
277             (void)set_monster_stunned(player_ptr, i, 0);
278             (void)set_monster_confused(player_ptr, i, 0);
279             (void)set_monster_monfear(player_ptr, i, 0);
280             (void)set_monster_invulner(player_ptr, i, 0, false);
281         }
282
283         const auto &r_ref = m_ptr->get_real_monrace();
284         if (r_ref.kind_flags.has_not(MonsterKindType::UNIQUE) && r_ref.population_flags.has_not(MonsterPopulationType::NAZGUL)) {
285             continue;
286         }
287
288         if (r_ref.floor_id != new_floor_id) {
289             delete_monster_idx(player_ptr, i);
290         }
291     }
292 }
293
294 static void new_floor_allocation(PlayerType *player_ptr, saved_floor_type *sf_ptr)
295 {
296     GAME_TURN tmp_last_visit = sf_ptr->last_visit;
297     const auto &floor = *player_ptr->current_floor_ptr;
298     auto alloc_chance = floor.get_dungeon_definition().max_m_alloc_chance;
299     while (tmp_last_visit > w_ptr->game_turn) {
300         tmp_last_visit -= TURNS_PER_TICK * TOWN_DAWN;
301     }
302
303     GAME_TURN absence_ticks = (w_ptr->game_turn - tmp_last_visit) / TURNS_PER_TICK;
304     reset_unique_by_floor_change(player_ptr);
305     for (MONSTER_IDX i = 1; i < floor.o_max; i++) {
306         const auto *o_ptr = &floor.o_list[i];
307         if (!o_ptr->is_valid() || !o_ptr->is_fixed_artifact()) {
308             continue;
309         }
310
311         auto &artifact = o_ptr->get_fixed_artifact();
312         if (artifact.floor_id == new_floor_id) {
313             artifact.is_generated = true;
314         } else {
315             delete_object_idx(player_ptr, i);
316         }
317     }
318
319     (void)place_quest_monsters(player_ptr);
320     GAME_TURN alloc_times = absence_ticks / alloc_chance;
321     if (randint0(alloc_chance) < (absence_ticks % alloc_chance)) {
322         alloc_times++;
323     }
324
325     for (MONSTER_IDX i = 0; i < alloc_times; i++) {
326         (void)alloc_monster(player_ptr, 0, 0, summon_specific);
327     }
328 }
329
330 /*!
331  * @brief プレイヤー足元に階段を設置する
332  * @param player_ptr プレイヤーへの参照ポインタ
333  */
334 static void set_stairs(PlayerType *player_ptr)
335 {
336     auto &floor = *player_ptr->current_floor_ptr;
337     auto *g_ptr = &floor.grid_array[player_ptr->y][player_ptr->x];
338     if ((player_ptr->change_floor_mode & CFM_UP) && !inside_quest(floor.get_quest_id())) {
339         g_ptr->feat = (player_ptr->change_floor_mode & CFM_SHAFT) ? feat_state(&floor, feat_down_stair, TerrainCharacteristics::SHAFT) : feat_down_stair;
340     } else if ((player_ptr->change_floor_mode & CFM_DOWN) && !ironman_downward) {
341         g_ptr->feat = (player_ptr->change_floor_mode & CFM_SHAFT) ? feat_state(&floor, feat_up_stair, TerrainCharacteristics::SHAFT) : feat_up_stair;
342     }
343
344     g_ptr->mimic = 0;
345     g_ptr->special = player_ptr->floor_id;
346 }
347
348 static void update_new_floor_feature(PlayerType *player_ptr, saved_floor_type *sf_ptr, const bool loaded)
349 {
350     if (loaded) {
351         new_floor_allocation(player_ptr, sf_ptr);
352         return;
353     }
354
355     if (!is_visited_floor(sf_ptr)) {
356         generate_floor(player_ptr);
357     } else {
358         build_dead_end(player_ptr, sf_ptr);
359     }
360
361     sf_ptr->last_visit = w_ptr->game_turn;
362     auto &floor = *player_ptr->current_floor_ptr;
363     sf_ptr->dun_level = floor.dun_level;
364     if ((player_ptr->change_floor_mode & CFM_NO_RETURN) != 0) {
365         return;
366     }
367
368     set_stairs(player_ptr);
369 }
370
371 static void cut_off_the_upstair(PlayerType *player_ptr)
372 {
373     if (player_ptr->change_floor_mode & CFM_RAND_PLACE) {
374         (void)new_player_spot(player_ptr);
375         return;
376     }
377
378     if (((player_ptr->change_floor_mode & CFM_NO_RETURN) == 0) || ((player_ptr->change_floor_mode & (CFM_DOWN | CFM_UP)) == 0)) {
379         return;
380     }
381
382     if (!player_ptr->effects()->blindness()->is_blind()) {
383         msg_print(_("突然階段が塞がれてしまった。", "Suddenly the stairs is blocked!"));
384     } else {
385         msg_print(_("ゴトゴトと何か音がした。", "You hear some noises."));
386     }
387 }
388
389 static void update_floor(PlayerType *player_ptr)
390 {
391     if (!(player_ptr->change_floor_mode & CFM_SAVE_FLOORS) && !(player_ptr->change_floor_mode & CFM_FIRST_FLOOR)) {
392         generate_floor(player_ptr);
393         new_floor_id = 0;
394         return;
395     }
396
397     if (new_floor_id == 0) {
398         new_floor_id = get_unused_floor_id(player_ptr);
399     }
400
401     saved_floor_type *sf_ptr;
402     bool loaded = false;
403     sf_ptr = get_sf_ptr(new_floor_id);
404     check_visited_floor(player_ptr, sf_ptr, &loaded);
405     update_floor_id(player_ptr, sf_ptr);
406     update_new_floor_feature(player_ptr, sf_ptr, loaded);
407     cut_off_the_upstair(player_ptr);
408     sf_ptr->visit_mark = latest_visit_mark++;
409 }
410
411 /*!
412  * @brief フロアの切り替え処理 / Enter new floor.
413  * @param player_ptr プレイヤーへの参照ポインタ
414  * @details
415  * If the floor is an old saved floor, it will be\n
416  * restored from the temporary file.  If the floor is new one, new floor\n
417  * will be generated.\n
418  */
419 void change_floor(PlayerType *player_ptr)
420 {
421     w_ptr->character_dungeon = false;
422     player_ptr->dtrap = false;
423     panel_row_min = 0;
424     panel_row_max = 0;
425     panel_col_min = 0;
426     panel_col_max = 0;
427     player_ptr->ambush_flag = false;
428     update_floor(player_ptr);
429     place_pet(player_ptr);
430     forget_travel_flow(player_ptr->current_floor_ptr);
431     update_unique_artifact(player_ptr->current_floor_ptr, new_floor_id);
432     player_ptr->floor_id = new_floor_id;
433     w_ptr->character_dungeon = true;
434     if (player_ptr->ppersonality == PERSONALITY_MUNCHKIN) {
435         wiz_lite(player_ptr, PlayerClass(player_ptr).equals(PlayerClassType::NINJA));
436     }
437
438     player_ptr->current_floor_ptr->generated_turn = w_ptr->game_turn;
439     player_ptr->feeling_turn = player_ptr->current_floor_ptr->generated_turn;
440     player_ptr->feeling = 0;
441     player_ptr->change_floor_mode = 0L;
442     select_floor_music(player_ptr);
443     player_ptr->change_floor_mode = 0;
444 }