OSDN Git Service

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