OSDN Git Service

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