OSDN Git Service

[WIP] [Refcator] #3287 PlayerType::window_flags に関わる処理を、RedrawingFlagsUpdater に集約した
[hengbandforosx/hengbandosx.git] / src / core / player-processor.cpp
1 #include "core/player-processor.h"
2 #include "action/run-execution.h"
3 #include "action/travel-execution.h"
4 #include "core/disturbance.h"
5 #include "core/special-internal-keys.h"
6 #include "core/speed-table.h"
7 #include "core/stuff-handler.h"
8 #include "core/window-redrawer.h"
9 #include "floor/floor-save-util.h"
10 #include "floor/floor-util.h"
11 #include "floor/geometry.h"
12 #include "floor/wild.h"
13 #include "game-option/cheat-options.h"
14 #include "game-option/disturbance-options.h"
15 #include "game-option/map-screen-options.h"
16 #include "grid/grid.h"
17 #include "inventory/pack-overflow.h"
18 #include "io/cursor.h"
19 #include "io/input-key-acceptor.h"
20 #include "io/input-key-processor.h"
21 #include "io/input-key-requester.h"
22 #include "mind/mind-force-trainer.h"
23 #include "mind/mind-sniper.h"
24 #include "monster-floor/monster-generator.h"
25 #include "monster-floor/place-monster-types.h"
26 #include "monster-race/monster-race-hook.h"
27 #include "monster-race/monster-race.h"
28 #include "monster-race/race-flags1.h"
29 #include "monster/monster-describer.h"
30 #include "monster/monster-flag-types.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 "monster/monster-util.h"
36 #include "mutation/mutation-investor-remover.h"
37 #include "player-base/player-class.h"
38 #include "player-info/bluemage-data-type.h"
39 #include "player-info/mane-data-type.h"
40 #include "player-info/samurai-data-type.h"
41 #include "player-info/sniper-data-type.h"
42 #include "player-status/player-energy.h"
43 #include "player/attack-defense-types.h"
44 #include "player/eldritch-horror.h"
45 #include "player/player-skill.h"
46 #include "player/special-defense-types.h"
47 #include "spell-kind/spells-random.h"
48 #include "spell-realm/spells-hex.h"
49 #include "spell-realm/spells-song.h"
50 #include "status/action-setter.h"
51 #include "system/dungeon-info.h"
52 #include "system/floor-type-definition.h"
53 #include "system/grid-type-definition.h"
54 #include "system/monster-race-info.h"
55 #include "system/player-type-definition.h"
56 #include "system/redrawing-flags-updater.h"
57 #include "term/screen-processor.h"
58 #include "timed-effect/player-blindness.h"
59 #include "timed-effect/player-confusion.h"
60 #include "timed-effect/player-cut.h"
61 #include "timed-effect/player-hallucination.h"
62 #include "timed-effect/player-paralysis.h"
63 #include "timed-effect/player-stun.h"
64 #include "timed-effect/timed-effects.h"
65 #include "util/bit-flags-calculator.h"
66 #include "view/display-messages.h"
67 #include "window/display-sub-windows.h"
68 #include "world/world-turn-processor.h"
69
70 bool load = true;
71 bool can_save = false;
72
73 static void process_fishing(PlayerType *player_ptr)
74 {
75     term_xtra(TERM_XTRA_DELAY, 10);
76     if (one_in_(1000)) {
77         bool success = false;
78         get_mon_num_prep(player_ptr, monster_is_fishing_target, nullptr);
79         auto *floor_ptr = player_ptr->current_floor_ptr;
80         const auto wild_level = wilderness[player_ptr->wilderness_y][player_ptr->wilderness_x].level;
81         const auto level = floor_ptr->is_in_dungeon() ? floor_ptr->dun_level : wild_level;
82         const auto r_idx = get_mon_num(player_ptr, 0, level, 0);
83         msg_print(nullptr);
84         if (MonsterRace(r_idx).is_valid() && one_in_(2)) {
85             POSITION y, x;
86             y = player_ptr->y + ddy[player_ptr->fishing_dir];
87             x = player_ptr->x + ddx[player_ptr->fishing_dir];
88             if (place_monster_aux(player_ptr, 0, y, x, r_idx, PM_NO_KAGE)) {
89                 const auto m_name = monster_desc(player_ptr, &floor_ptr->m_list[floor_ptr->grid_array[y][x].m_idx], 0);
90                 msg_print(_(format("%sが釣れた!", m_name.data()), "You have a good catch!"));
91                 success = true;
92             }
93         }
94
95         if (!success) {
96             msg_print(_("餌だけ食われてしまった!くっそ~!", "Damn!  The fish stole your bait!"));
97         }
98
99         disturb(player_ptr, false, true);
100     }
101 }
102
103 bool continuous_action_running(PlayerType *player_ptr)
104 {
105     return player_ptr->running || travel.run || command_rep || (player_ptr->action == ACTION_REST) || (player_ptr->action == ACTION_FISH);
106 }
107
108 /*!
109  * @brief プレイヤーの行動処理 / Process the player
110  * @note
111  * Notice the annoying code to handle "pack overflow", which\n
112  * must come first just in case somebody manages to corrupt\n
113  * the savefiles by clever use of menu commands or something.\n
114  */
115 void process_player(PlayerType *player_ptr)
116 {
117     if (player_ptr->hack_mutation) {
118         msg_print(_("何か変わった気がする!", "You feel different!"));
119         (void)gain_mutation(player_ptr, 0);
120         player_ptr->hack_mutation = false;
121     }
122
123     if (player_ptr->invoking_midnight_curse) {
124         int count = 0;
125         activate_ty_curse(player_ptr, false, &count);
126         player_ptr->invoking_midnight_curse = false;
127     }
128
129     if (player_ptr->phase_out) {
130         for (MONSTER_IDX m_idx = 1; m_idx < player_ptr->current_floor_ptr->m_max; m_idx++) {
131             auto *m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
132             if (!m_ptr->is_valid()) {
133                 continue;
134             }
135
136             m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
137             update_monster(player_ptr, m_idx, false);
138         }
139
140         WorldTurnProcessor(player_ptr).print_time();
141     } else if (!(load && player_ptr->energy_need <= 0)) {
142         player_ptr->energy_need -= speed_to_energy(player_ptr->pspeed);
143     }
144
145     if (player_ptr->energy_need > 0) {
146         return;
147     }
148     if (!command_rep) {
149         WorldTurnProcessor(player_ptr).print_time();
150     }
151
152     if (fresh_once && (continuous_action_running(player_ptr) || !command_rep)) {
153         stop_term_fresh();
154     }
155
156     if (player_ptr->resting < 0) {
157         if (player_ptr->resting == COMMAND_ARG_REST_FULL_HEALING) {
158             if ((player_ptr->chp == player_ptr->mhp) && (player_ptr->csp >= player_ptr->msp)) {
159                 set_action(player_ptr, ACTION_NONE);
160             }
161         } else if (player_ptr->resting == COMMAND_ARG_REST_UNTIL_DONE) {
162             if (player_ptr->is_fully_healthy()) {
163                 set_action(player_ptr, ACTION_NONE);
164             }
165         }
166     }
167
168     if (player_ptr->action == ACTION_FISH) {
169         process_fishing(player_ptr);
170     }
171
172     if (check_abort) {
173         if (continuous_action_running(player_ptr)) {
174             inkey_scan = true;
175             if (inkey()) {
176                 flush();
177                 disturb(player_ptr, false, true);
178                 msg_print(_("中断しました。", "Canceled."));
179             }
180         }
181     }
182
183     const auto effects = player_ptr->effects();
184     if (player_ptr->riding && !effects->confusion()->is_confused() && !effects->blindness()->is_blind()) {
185         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->riding];
186         auto *r_ptr = &monraces_info[m_ptr->r_idx];
187         if (m_ptr->is_asleep()) {
188             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
189             (void)set_monster_csleep(player_ptr, player_ptr->riding, 0);
190             msg_format(_("%s^を起こした。", "You have woken %s up."), m_name.data());
191         }
192
193         if (m_ptr->is_stunned()) {
194             if (set_monster_stunned(player_ptr, player_ptr->riding,
195                     (randint0(r_ptr->level) < player_ptr->skill_exp[PlayerSkillKindType::RIDING]) ? 0 : (m_ptr->get_remaining_stun() - 1))) {
196                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
197                 msg_format(_("%s^を朦朧状態から立ち直らせた。", "%s^ is no longer stunned."), m_name.data());
198             }
199         }
200
201         if (m_ptr->is_confused()) {
202             if (set_monster_confused(player_ptr, player_ptr->riding,
203                     (randint0(r_ptr->level) < player_ptr->skill_exp[PlayerSkillKindType::RIDING]) ? 0 : (m_ptr->get_remaining_confusion() - 1))) {
204                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
205                 msg_format(_("%s^を混乱状態から立ち直らせた。", "%s^ is no longer confused."), m_name.data());
206             }
207         }
208
209         if (m_ptr->is_fearful()) {
210             if (set_monster_monfear(player_ptr, player_ptr->riding,
211                     (randint0(r_ptr->level) < player_ptr->skill_exp[PlayerSkillKindType::RIDING]) ? 0 : (m_ptr->get_remaining_fear() - 1))) {
212                 const auto m_name = monster_desc(player_ptr, m_ptr, 0);
213                 msg_format(_("%s^を恐怖から立ち直らせた。", "%s^ is no longer fearful."), m_name.data());
214             }
215         }
216
217         handle_stuff(player_ptr);
218     }
219
220     load = false;
221     if (player_ptr->lightspeed) {
222         set_lightspeed(player_ptr, player_ptr->lightspeed - 1, true);
223     }
224
225     auto &rfu = RedrawingFlagsUpdater::get_instance();
226     if (PlayerClass(player_ptr).equals(PlayerClassType::FORCETRAINER) && get_current_ki(player_ptr)) {
227         if (get_current_ki(player_ptr) < 40) {
228             set_current_ki(player_ptr, true, 0);
229         } else {
230             set_current_ki(player_ptr, false, -40);
231         }
232         rfu.set_flag(StatusRedrawingFlag::BONUS);
233     }
234
235     if (player_ptr->action == ACTION_LEARN) {
236         int32_t cost = 0L;
237         uint32_t cost_frac = (player_ptr->msp + 30L) * 256L;
238         s64b_lshift(&cost, &cost_frac, 16);
239         if (s64b_cmp(player_ptr->csp, player_ptr->csp_frac, cost, cost_frac) < 0) {
240             player_ptr->csp = 0;
241             player_ptr->csp_frac = 0;
242             set_action(player_ptr, ACTION_NONE);
243         } else {
244             s64b_sub(&(player_ptr->csp), &(player_ptr->csp_frac), cost, cost_frac);
245         }
246
247         rfu.set_flag(MainWindowRedrawingFlag::MP);
248     }
249
250     if (PlayerClass(player_ptr).samurai_stance_is(SamuraiStanceType::MUSOU)) {
251         if (player_ptr->csp < 3) {
252             set_action(player_ptr, ACTION_NONE);
253         } else {
254             player_ptr->csp -= 2;
255             rfu.set_flag(MainWindowRedrawingFlag::MP);
256         }
257     }
258
259     /*** Handle actual user input ***/
260     while (player_ptr->energy_need <= 0) {
261         rfu.set_flag(SubWindowRedrawingFlag::PLAYER);
262         player_ptr->sutemi = false;
263         player_ptr->counter = false;
264         player_ptr->now_damaged = false;
265
266         update_monsters(player_ptr, false);
267         handle_stuff(player_ptr);
268         move_cursor_relative(player_ptr->y, player_ptr->x);
269         if (fresh_before) {
270             term_fresh_force();
271         }
272
273         pack_overflow(player_ptr);
274         if (!command_new) {
275             command_see = false;
276         }
277
278         PlayerEnergy energy(player_ptr);
279         energy.reset_player_turn();
280         auto is_knocked_out = effects->stun()->is_knocked_out();
281         auto is_paralyzed = effects->paralysis()->is_paralyzed();
282         if (player_ptr->phase_out) {
283             move_cursor_relative(player_ptr->y, player_ptr->x);
284             command_cmd = SPECIAL_KEY_BUILDING;
285             process_command(player_ptr);
286         } else if ((is_paralyzed || is_knocked_out) && !cheat_immortal) {
287             energy.set_player_turn_energy(100);
288         } else if (player_ptr->action == ACTION_REST) {
289             if (player_ptr->resting > 0) {
290                 player_ptr->resting--;
291                 if (!player_ptr->resting) {
292                     set_action(player_ptr, ACTION_NONE);
293                 }
294
295                 rfu.set_flag(MainWindowRedrawingFlag::ACTION);
296             }
297
298             energy.set_player_turn_energy(100);
299         } else if (player_ptr->action == ACTION_FISH) {
300             energy.set_player_turn_energy(100);
301         } else if (player_ptr->running) {
302             run_step(player_ptr, 0);
303         } else if (travel.run) {
304             travel_step(player_ptr);
305         } else if (command_rep) {
306             command_rep--;
307             rfu.set_flag(MainWindowRedrawingFlag::ACTION);
308             handle_stuff(player_ptr);
309             msg_flag = false;
310             prt("", 0, 0);
311             process_command(player_ptr);
312         } else {
313             move_cursor_relative(player_ptr->y, player_ptr->x);
314
315             rfu.set_flag(SubWindowRedrawingFlag::SIGHT_MONSTERS);
316             window_stuff(player_ptr);
317
318             can_save = true;
319             InputKeyRequestor(player_ptr, false).request_command();
320             can_save = false;
321             process_command(player_ptr);
322         }
323
324         pack_overflow(player_ptr);
325         if (player_ptr->energy_use) {
326             if (player_ptr->timewalk || player_ptr->energy_use > 400) {
327                 player_ptr->energy_need += player_ptr->energy_use * TURNS_PER_TICK / 10;
328             } else {
329                 player_ptr->energy_need += (int16_t)((int32_t)player_ptr->energy_use * ENERGY_NEED() / 100L);
330             }
331
332             if (effects->hallucination()->is_hallucinated()) {
333                 rfu.set_flag(MainWindowRedrawingFlag::MAP);
334             }
335
336             for (MONSTER_IDX m_idx = 1; m_idx < player_ptr->current_floor_ptr->m_max; m_idx++) {
337                 MonsterEntity *m_ptr;
338                 MonsterRaceInfo *r_ptr;
339                 m_ptr = &player_ptr->current_floor_ptr->m_list[m_idx];
340                 if (!m_ptr->is_valid()) {
341                     continue;
342                 }
343
344                 r_ptr = &monraces_info[m_ptr->ap_r_idx];
345
346                 // モンスターのシンボル/カラーの更新
347                 if (m_ptr->ml && r_ptr->visual_flags.has_any_of({ MonsterVisualType::MULTI_COLOR, MonsterVisualType::SHAPECHANGER })) {
348                     lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
349                 }
350
351                 // 出現して即魔法を使わないようにするフラグを落とす処理
352                 if (m_ptr->mflag.has(MonsterTemporaryFlagType::PREVENT_MAGIC)) {
353                     m_ptr->mflag.reset(MonsterTemporaryFlagType::PREVENT_MAGIC);
354                 }
355
356                 if (m_ptr->mflag.has(MonsterTemporaryFlagType::SANITY_BLAST)) {
357                     m_ptr->mflag.reset(MonsterTemporaryFlagType::SANITY_BLAST);
358                     sanity_blast(player_ptr, m_ptr, false);
359                 }
360
361                 // 感知中のモンスターのフラグを落とす処理
362                 // 感知したターンはMFLAG2_SHOWを落とし、次のターンに感知中フラグのMFLAG2_MARKを落とす
363                 if (m_ptr->mflag2.has(MonsterConstantFlagType::MARK)) {
364                     if (m_ptr->mflag2.has(MonsterConstantFlagType::SHOW)) {
365                         m_ptr->mflag2.reset(MonsterConstantFlagType::SHOW);
366                     } else {
367                         m_ptr->mflag2.reset(MonsterConstantFlagType::MARK);
368                         m_ptr->ml = false;
369                         update_monster(player_ptr, m_idx, false);
370                         if (player_ptr->health_who == m_idx) {
371                             rfu.set_flag(MainWindowRedrawingFlag::HEALTH);
372                         }
373
374                         if (player_ptr->riding == m_idx) {
375                             rfu.set_flag(MainWindowRedrawingFlag::UHEALTH);
376                         }
377
378                         lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
379                     }
380                 }
381             }
382
383             if (PlayerClass(player_ptr).equals(PlayerClassType::IMITATOR)) {
384                 auto mane_data = PlayerClass(player_ptr).get_specific_data<mane_data_type>();
385                 if (static_cast<int>(mane_data->mane_list.size()) > (player_ptr->lev > 44 ? 3 : player_ptr->lev > 29 ? 2
386                                                                                                                      : 1)) {
387                     mane_data->mane_list.pop_front();
388                 }
389
390                 mane_data->new_mane = false;
391                 rfu.set_flag(MainWindowRedrawingFlag::IMITATION);
392             }
393
394             if (player_ptr->action == ACTION_LEARN) {
395                 auto mane_data = PlayerClass(player_ptr).get_specific_data<bluemage_data_type>();
396                 mane_data->new_magic_learned = false;
397                 rfu.set_flag(MainWindowRedrawingFlag::ACTION);
398             }
399
400             if (player_ptr->timewalk && (player_ptr->energy_need > -1000)) {
401                 rfu.set_flag(MainWindowRedrawingFlag::MAP);
402                 rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
403                 const auto flags_swrf = {
404                     SubWindowRedrawingFlag::OVERHEAD,
405                     SubWindowRedrawingFlag::DUNGEON,
406                 };
407                 rfu.set_flags(flags_swrf);
408                 msg_print(_("「時は動きだす…」", "You feel time flowing around you once more."));
409                 msg_print(nullptr);
410                 player_ptr->timewalk = false;
411                 player_ptr->energy_need = ENERGY_NEED();
412
413                 handle_stuff(player_ptr);
414             }
415         }
416
417         if (!player_ptr->playing || player_ptr->is_dead) {
418             player_ptr->timewalk = false;
419             break;
420         }
421
422         auto sniper_data = PlayerClass(player_ptr).get_specific_data<sniper_data_type>();
423         if (player_ptr->energy_use && sniper_data && sniper_data->reset_concent) {
424             reset_concentration(player_ptr, true);
425         }
426
427         if (player_ptr->leaving) {
428             break;
429         }
430     }
431
432     update_smell(player_ptr->current_floor_ptr, player_ptr);
433 }
434
435 /*!
436  * @brief プレイヤーの行動エネルギーが充填される(=プレイヤーのターンが回る)毎に行われる処理  / process the effects per 100 energy at player speed.
437  */
438 void process_upkeep_with_speed(PlayerType *player_ptr)
439 {
440     if (!load && player_ptr->enchant_energy_need > 0 && !player_ptr->leaving) {
441         player_ptr->enchant_energy_need -= speed_to_energy(player_ptr->pspeed);
442     }
443
444     if (player_ptr->enchant_energy_need > 0) {
445         return;
446     }
447
448     while (player_ptr->enchant_energy_need <= 0) {
449         if (!load) {
450             check_music(player_ptr);
451         }
452
453         SpellHex spell_hex(player_ptr);
454         if (!load) {
455             spell_hex.decrease_mana();
456         }
457
458         if (!load) {
459             spell_hex.continue_revenge();
460         }
461
462         player_ptr->enchant_energy_need += ENERGY_NEED();
463     }
464 }