OSDN Git Service

[Implement] ウサウサストライカー召喚処理を追加
[hengbandforosx/hengbandosx.git] / src / world / world-turn-processor.cpp
1 #include "world/world-turn-processor.h"
2 #include "cmd-building/cmd-building.h"
3 #include "cmd-io/cmd-save.h"
4 #include "core/disturbance.h"
5 #include "core/magic-effects-timeout-reducer.h"
6 #include "dungeon/quest.h"
7 #include "floor/floor-events.h"
8 #include "floor/floor-mode-changer.h"
9 #include "floor/wild.h"
10 #include "game-option/birth-options.h"
11 #include "game-option/cheat-options.h"
12 #include "game-option/special-options.h"
13 #include "game-option/text-display-options.h"
14 #include "hpmp/hp-mp-processor.h"
15 #include "hpmp/hp-mp-regenerator.h"
16 #include "inventory/inventory-curse.h"
17 #include "inventory/recharge-processor.h"
18 #include "io/write-diary.h"
19 #include "market/arena.h"
20 #include "market/bounty.h"
21 #include "monster-floor/monster-generator.h"
22 #include "monster-floor/monster-summon.h"
23 #include "monster/monster-describer.h"
24 #include "monster/monster-status.h"
25 #include "mutation/mutation-processor.h"
26 #include "object/lite-processor.h"
27 #include "perception/simple-perception.h"
28 #include "player-status/player-energy.h"
29 #include "player/digestion-processor.h"
30 #include "store/store-owners.h"
31 #include "store/store-util.h"
32 #include "store/store.h"
33 #include "system/angband-system.h"
34 #include "system/dungeon-info.h"
35 #include "system/floor-type-definition.h"
36 #include "system/grid-type-definition.h"
37 #include "system/monster-entity.h"
38 #include "system/player-type-definition.h"
39 #include "system/terrain-type-definition.h"
40 #include "term/screen-processor.h"
41 #include "term/term-color-types.h"
42 #include "util/bit-flags-calculator.h"
43 #include "view/display-messages.h"
44 #include "window/main-window-row-column.h"
45 #include "world/world-movement-processor.h"
46 #include "world/world.h"
47
48 WorldTurnProcessor::WorldTurnProcessor(PlayerType *player_ptr)
49     : player_ptr(player_ptr)
50 {
51 }
52
53 /*!
54  * @brief 10ゲームターンが進行する毎にゲーム世界全体の処理を行う。
55  * / Handle certain things once every 10 game turns
56  * @param player_ptr プレイヤーへの参照ポインタ
57  */
58 void WorldTurnProcessor::process_world()
59 {
60     const int a_day = TURNS_PER_TICK * TOWN_DAWN;
61     const int prev_turn_in_today = ((w_ptr->game_turn - TURNS_PER_TICK) % a_day + a_day / 4) % a_day;
62     const int prev_min = (1440 * prev_turn_in_today / a_day) % 60;
63     std::tie(std::ignore, this->hour, this->min) = w_ptr->extract_date_time(this->player_ptr->start_race);
64     update_dungeon_feeling(this->player_ptr);
65     process_downward();
66     process_monster_arena();
67     if (w_ptr->game_turn % TURNS_PER_TICK) {
68         return;
69     }
70
71     decide_auto_save();
72     auto *floor_ptr = this->player_ptr->current_floor_ptr;
73     if (floor_ptr->monster_noise && !ignore_unview) {
74         msg_print(_("何かが聞こえた。", "You hear noise."));
75     }
76
77     process_change_daytime_night();
78     process_world_monsters();
79     if ((this->hour == 0) && (this->min == 0)) {
80         if (this->min != prev_min) {
81             exe_write_diary(this->player_ptr, DiaryKind::DIALY, 0);
82             determine_daily_bounty(this->player_ptr, false);
83         }
84     }
85
86     ring_nightmare_bell(prev_min);
87     starve_player(this->player_ptr);
88     process_player_hp_mp(this->player_ptr);
89     reduce_magic_effects_timeout(this->player_ptr);
90     reduce_lite_life(this->player_ptr);
91     process_world_aux_mutation(this->player_ptr);
92     execute_cursed_items_effect(this->player_ptr);
93     recharge_magic_items(this->player_ptr);
94     sense_inventory1(this->player_ptr);
95     sense_inventory2(this->player_ptr);
96     execute_recall(this->player_ptr);
97     execute_floor_reset(this->player_ptr);
98 }
99
100 /*!
101  * @brief ゲーム時刻を表示する /
102  * Print time
103  */
104 void WorldTurnProcessor::print_time()
105 {
106     const auto &[wid, hgt] = term_get_size();
107     const auto row = hgt + ROW_DAY;
108
109     c_put_str(TERM_WHITE, "             ", row, COL_DAY);
110     auto day = 0;
111     std::tie(day, this->hour, this->min) = w_ptr->extract_date_time(this->player_ptr->start_race);
112     if (day < 1000) {
113         c_put_str(TERM_WHITE, format(_("%2d日目", "Day%3d"), day), row, COL_DAY);
114     } else {
115         c_put_str(TERM_WHITE, _("***日目", "Day***"), row, COL_DAY);
116     }
117
118     c_put_str(TERM_WHITE, format("%2d:%02d", this->hour, this->min), row, COL_DAY + 7);
119 }
120
121 void WorldTurnProcessor::process_downward()
122 {
123     /* 帰還無しモード時のレベルテレポバグ対策 / Fix for level teleport bugs on ironman_downward.*/
124     auto *floor_ptr = this->player_ptr->current_floor_ptr;
125     if (!ironman_downward || (floor_ptr->dungeon_idx == DUNGEON_ANGBAND) || (floor_ptr->dungeon_idx == 0)) {
126         return;
127     }
128
129     floor_ptr->dun_level = 0;
130     floor_ptr->reset_dungeon_index();
131     prepare_change_floor_mode(this->player_ptr, CFM_FIRST_FLOOR | CFM_RAND_PLACE);
132     floor_ptr->inside_arena = false;
133     this->player_ptr->wild_mode = false;
134     this->player_ptr->leaving = true;
135 }
136
137 void WorldTurnProcessor::process_monster_arena()
138 {
139     if (!AngbandSystem::get_instance().is_phase_out() || this->player_ptr->leaving) {
140         return;
141     }
142
143     auto win_m_idx = 0;
144     auto number_mon = 0;
145     auto *floor_ptr = this->player_ptr->current_floor_ptr;
146     for (auto x = 0; x < floor_ptr->width; ++x) {
147         for (auto y = 0; y < floor_ptr->height; y++) {
148             auto *g_ptr = &floor_ptr->grid_array[y][x];
149             if ((g_ptr->m_idx > 0) && (g_ptr->m_idx != this->player_ptr->riding)) {
150                 number_mon++;
151                 win_m_idx = g_ptr->m_idx;
152             }
153         }
154     }
155
156     if (number_mon == 0) {
157         msg_print(_("相打ちに終わりました。", "Nothing survived."));
158         msg_print(nullptr);
159         this->player_ptr->energy_need = 0;
160         update_gambling_monsters(this->player_ptr);
161         return;
162     }
163
164     if (number_mon == 1) {
165         process_monster_arena_winner(win_m_idx);
166         return;
167     }
168
169     process_monster_arena_draw();
170 }
171
172 void WorldTurnProcessor::process_monster_arena_winner(int win_m_idx)
173 {
174     auto *wm_ptr = &this->player_ptr->current_floor_ptr->m_list[win_m_idx];
175     const auto m_name = monster_desc(this->player_ptr, wm_ptr, 0);
176     msg_format(_("%sが勝利した!", "%s won!"), m_name.data());
177     msg_print(nullptr);
178
179     if (win_m_idx == (sel_monster + 1)) {
180         msg_print(_("おめでとうございます。", "Congratulations."));
181         msg_format(_("%d$を受け取った。", "You received %d gold."), battle_odds);
182         this->player_ptr->au += battle_odds;
183     } else {
184         msg_print(_("残念でした。", "You lost gold."));
185     }
186
187     msg_print(nullptr);
188     this->player_ptr->energy_need = 0;
189     update_gambling_monsters(this->player_ptr);
190 }
191
192 void WorldTurnProcessor::process_monster_arena_draw()
193 {
194     auto turn = this->player_ptr->current_floor_ptr->generated_turn;
195     if (w_ptr->game_turn - turn != 150 * TURNS_PER_TICK) {
196         return;
197     }
198
199     msg_print(_("申し訳ありませんが、この勝負は引き分けとさせていただきます。", "Sorry, but this battle ended in a draw."));
200     this->player_ptr->au += kakekin;
201     msg_print(nullptr);
202     this->player_ptr->energy_need = 0;
203     update_gambling_monsters(this->player_ptr);
204 }
205
206 void WorldTurnProcessor::decide_auto_save()
207 {
208     if (autosave_freq == 0) {
209         return;
210     }
211
212     auto should_save = autosave_t;
213     should_save &= !AngbandSystem::get_instance().is_phase_out();
214     should_save &= w_ptr->game_turn % ((int32_t)autosave_freq * TURNS_PER_TICK) == 0;
215     if (should_save) {
216         do_cmd_save_game(this->player_ptr, true);
217     }
218 }
219
220 void WorldTurnProcessor::process_change_daytime_night()
221 {
222     auto *floor_ptr = this->player_ptr->current_floor_ptr;
223     if (!floor_ptr->dun_level && !floor_ptr->is_in_quest() && !AngbandSystem::get_instance().is_phase_out() && !floor_ptr->inside_arena) {
224         if (!(w_ptr->game_turn % ((TURNS_PER_TICK * TOWN_DAWN) / 2))) {
225             auto dawn = w_ptr->game_turn % (TURNS_PER_TICK * TOWN_DAWN) == 0;
226             if (dawn) {
227                 day_break(this->player_ptr);
228             } else {
229                 night_falls(this->player_ptr);
230             }
231         }
232
233         return;
234     }
235
236     auto is_in_dungeon = vanilla_town;
237     is_in_dungeon |= lite_town && !floor_ptr->is_in_quest() && !AngbandSystem::get_instance().is_phase_out() && !floor_ptr->inside_arena;
238     is_in_dungeon &= floor_ptr->dun_level != 0;
239     if (!is_in_dungeon) {
240         return;
241     }
242
243     if ((w_ptr->game_turn % (TURNS_PER_TICK * STORE_TICKS)) != 0) {
244         return;
245     }
246
247     shuffle_shopkeeper();
248 }
249
250 void WorldTurnProcessor::process_world_monsters()
251 {
252     decide_alloc_monster();
253     if (!(w_ptr->game_turn % (TURNS_PER_TICK * 10)) && !AngbandSystem::get_instance().is_phase_out()) {
254         regenerate_monsters(this->player_ptr);
255     }
256
257     if (!(w_ptr->game_turn % (TURNS_PER_TICK * 3))) {
258         regenerate_captured_monsters(this->player_ptr);
259     }
260
261     if (this->player_ptr->leaving) {
262         return;
263     }
264
265     for (auto i = 0; i < MAX_MTIMED; i++) {
266         if (this->player_ptr->current_floor_ptr->mproc_max[i] > 0) {
267             process_monsters_mtimed(this->player_ptr, i);
268         }
269     }
270 }
271
272 void WorldTurnProcessor::shuffle_shopkeeper()
273 {
274     if (!one_in_(STORE_SHUFFLE)) {
275         return;
276     }
277
278     int n;
279     while (true) {
280         n = randint0(MAX_STORES);
281         if ((n == enum2i(StoreSaleType::HOME)) || (n == enum2i(StoreSaleType::MUSEUM))) {
282             break;
283         }
284     }
285
286     for (const auto &terrain : TerrainList::get_instance()) {
287         if (terrain.name.empty() || terrain.flags.has_not(TerrainCharacteristics::STORE)) {
288             continue;
289         }
290
291         if (terrain.subtype != n) {
292             continue;
293         }
294
295         if (cheat_xtra) {
296             msg_format(_("%sの店主をシャッフルします。", "Shuffle a Shopkeeper of %s."), terrain.name.data());
297         }
298
299         store_shuffle(this->player_ptr, i2enum<StoreSaleType>(n));
300         break;
301     }
302 }
303
304 void WorldTurnProcessor::decide_alloc_monster()
305 {
306     auto *floor_ptr = this->player_ptr->current_floor_ptr;
307     auto should_alloc = one_in_(floor_ptr->get_dungeon_definition().max_m_alloc_chance);
308     should_alloc &= !floor_ptr->inside_arena;
309     should_alloc &= !floor_ptr->is_in_quest();
310     should_alloc &= !AngbandSystem::get_instance().is_phase_out();
311     if (should_alloc) {
312         (void)alloc_monster(this->player_ptr, MAX_PLAYER_SIGHT + 5, 0, summon_specific);
313     }
314 }
315
316 /*
317  * Nightmare mode activates the TY_CURSE at midnight
318  * Require exact minute -- Don't activate multiple times in a minute
319  */
320 void WorldTurnProcessor::ring_nightmare_bell(int prev_min)
321 {
322     if (!ironman_nightmare || (this->min == prev_min)) {
323         return;
324     }
325
326     if ((this->hour == 23) && !(this->min % 15)) {
327         disturb(this->player_ptr, false, true);
328         switch (this->min / 15) {
329         case 0:
330             msg_print(_("遠くで不気味な鐘の音が鳴った。", "You hear a distant bell toll ominously."));
331             break;
332
333         case 1:
334             msg_print(_("遠くで鐘が二回鳴った。", "A distant bell sounds twice."));
335             break;
336
337         case 2:
338             msg_print(_("遠くで鐘が三回鳴った。", "A distant bell sounds three times."));
339             break;
340
341         case 3:
342             msg_print(_("遠くで鐘が四回鳴った。", "A distant bell tolls four times."));
343             break;
344         }
345     }
346
347     if ((this->hour > 0) || (this->min > 0)) {
348         return;
349     }
350
351     disturb(this->player_ptr, true, true);
352     msg_print(_("遠くで鐘が何回も鳴り、死んだような静けさの中へ消えていった。", "A distant bell tolls many times, fading into an deathly silence."));
353     if (this->player_ptr->wild_mode) {
354         this->player_ptr->oldpy = randint1(MAX_HGT - 2);
355         this->player_ptr->oldpx = randint1(MAX_WID - 2);
356         change_wild_mode(this->player_ptr, true);
357         PlayerEnergy(this->player_ptr).set_player_turn_energy(100);
358     }
359
360     this->player_ptr->invoking_midnight_curse = true;
361 }