OSDN Git Service

[Refactor] ItemEntity::marked を FlagGroup 化する
[hengbandforosx/hengbandosx.git] / src / action / run-execution.cpp
1 /*!
2  * @file run-execution.cpp
3  * @brief プレイヤーの走行処理実装
4  */
5
6 #include "action/run-execution.h"
7 #include "action/movement-execution.h"
8 #include "core/disturbance.h"
9 #include "floor/cave.h"
10 #include "floor/floor-util.h"
11 #include "floor/geometry.h"
12 #include "game-option/disturbance-options.h"
13 #include "grid/grid.h"
14 #include "main/sound-definitions-table.h"
15 #include "main/sound-of-music.h"
16 #include "object/object-mark-types.h"
17 #include "player-status/player-energy.h"
18 #include "player/player-status-flags.h"
19 #include "player/player-status.h"
20 #include "system/floor-type-definition.h"
21 #include "system/grid-type-definition.h"
22 #include "system/item-entity.h"
23 #include "system/monster-entity.h"
24 #include "system/player-type-definition.h"
25 #include "system/terrain-type-definition.h"
26 #include "util/bit-flags-calculator.h"
27 #include "view/display-messages.h"
28
29 bool ignore_avoid_run;
30
31 /* Allow quick "cycling" through the legal directions */
32 byte cycle[MAX_RUN_CYCLES] = { 1, 2, 3, 6, 9, 8, 7, 4, 1, 2, 3, 6, 9, 8, 7, 4, 1 };
33
34 /* Map each direction into the "middle" of the "cycle[]" array */
35 byte chome[MAX_RUN_CHOME] = { 0, 8, 9, 10, 7, 0, 11, 6, 5, 4 };
36
37 /* The direction we are running */
38 static DIRECTION find_current;
39
40 /* The direction we came from */
41 static DIRECTION find_prevdir;
42
43 static bool find_openarea;
44
45 /* We are looking for a break */
46 static bool find_breakright;
47 static bool find_breakleft;
48
49 /*!
50  * @brief ダッシュ移動処理中、移動先のマスが既知の壁かどうかを判定する /
51  * Hack -- Check for a "known wall" (see below)
52  * @param player_ptr    プレイヤーへの参照ポインタ
53  * @param dir 想定する移動方向ID
54  * @param y 移動元のY座標
55  * @param x 移動元のX座標
56  * @return 移動先が既知の壁ならばTRUE
57  */
58 static bool see_wall(PlayerType *player_ptr, DIRECTION dir, POSITION y, POSITION x)
59 {
60     y += ddy[dir];
61     x += ddx[dir];
62     auto *floor_ptr = player_ptr->current_floor_ptr;
63     if (!in_bounds2(floor_ptr, y, x)) {
64         return false;
65     }
66
67     grid_type *g_ptr;
68     g_ptr = &floor_ptr->grid_array[y][x];
69     if (!g_ptr->is_mark()) {
70         return false;
71     }
72
73     int16_t feat = g_ptr->get_feat_mimic();
74     auto *f_ptr = &terrains_info[feat];
75     if (!player_can_enter(player_ptr, feat, 0)) {
76         return f_ptr->flags.has_not(TerrainCharacteristics::DOOR);
77     }
78
79     if (f_ptr->flags.has(TerrainCharacteristics::AVOID_RUN) && !ignore_avoid_run) {
80         return true;
81     }
82
83     if (f_ptr->flags.has_none_of({ TerrainCharacteristics::MOVE, TerrainCharacteristics::CAN_FLY })) {
84         return f_ptr->flags.has_not(TerrainCharacteristics::DOOR);
85     }
86
87     return false;
88 }
89
90 /*!
91  * @brief ダッシュ処理の導入 /
92  * Initialize the running algorithm for a new direction.
93  * @param player_ptr    プレイヤーへの参照ポインタ
94  * @param dir 導入の移動先
95  * @details
96  * Diagonal Corridor -- allow diaginal entry into corridors.\n
97  *\n
98  * Blunt Corridor -- If there is a wall two spaces ahead and\n
99  * we seem to be in a corridor, then force a turn into the side\n
100  * corridor, must be moving straight into a corridor here. ???\n
101  *\n
102  * Diagonal Corridor    Blunt Corridor (?)\n
103  *       \# \#                  \#\n
104  *       \#x\#                  \@x\#\n
105  *       \@\@p.                  p\n
106  */
107 static void run_init(PlayerType *player_ptr, DIRECTION dir)
108 {
109     find_current = dir;
110     find_prevdir = dir;
111     find_openarea = true;
112     find_breakright = find_breakleft = false;
113     bool deepleft = false;
114     bool deepright = false;
115     bool shortright = false;
116     bool shortleft = false;
117     player_ptr->run_py = player_ptr->y;
118     player_ptr->run_px = player_ptr->x;
119     int row = player_ptr->y + ddy[dir];
120     int col = player_ptr->x + ddx[dir];
121     ignore_avoid_run = cave_has_flag_bold(player_ptr->current_floor_ptr, row, col, TerrainCharacteristics::AVOID_RUN);
122     int i = chome[dir];
123     if (see_wall(player_ptr, cycle[i + 1], player_ptr->y, player_ptr->x)) {
124         find_breakleft = true;
125         shortleft = true;
126     } else if (see_wall(player_ptr, cycle[i + 1], row, col)) {
127         find_breakleft = true;
128         deepleft = true;
129     }
130
131     if (see_wall(player_ptr, cycle[i - 1], player_ptr->y, player_ptr->x)) {
132         find_breakright = true;
133         shortright = true;
134     } else if (see_wall(player_ptr, cycle[i - 1], row, col)) {
135         find_breakright = true;
136         deepright = true;
137     }
138
139     if (!find_breakleft || !find_breakright) {
140         return;
141     }
142
143     find_openarea = false;
144     if (dir & 0x01) {
145         if (deepleft && !deepright) {
146             find_prevdir = cycle[i - 1];
147         } else if (deepright && !deepleft) {
148             find_prevdir = cycle[i + 1];
149         }
150
151         return;
152     }
153
154     if (!see_wall(player_ptr, cycle[i], row, col)) {
155         return;
156     }
157
158     if (shortleft && !shortright) {
159         find_prevdir = cycle[i - 2];
160     } else if (shortright && !shortleft) {
161         find_prevdir = cycle[i + 2];
162     }
163 }
164
165 /*!
166  * @brief ダッシュ移動処理中、移動先のマスか未知の地形かどうかを判定する /
167  * Hack -- Check for an "unknown corner" (see below)
168  * @param player_ptr    プレイヤーへの参照ポインタ
169  * @param dir 想定する移動方向ID
170  * @param y 移動元のY座標
171  * @param x 移動元のX座標
172  * @return 移動先が未知の地形ならばTRUE
173  */
174 static bool see_nothing(PlayerType *player_ptr, DIRECTION dir, POSITION y, POSITION x)
175 {
176     y += ddy[dir];
177     x += ddx[dir];
178
179     auto *floor_ptr = player_ptr->current_floor_ptr;
180     if (!in_bounds2(floor_ptr, y, x)) {
181         return true;
182     }
183
184     if (floor_ptr->grid_array[y][x].is_mark()) {
185         return false;
186     }
187
188     if (player_can_see_bold(player_ptr, y, x)) {
189         return false;
190     }
191
192     return true;
193 }
194
195 /*!
196  * @brief ダッシュ移動が継続できるかどうかの判定 /
197  * Update the current "run" path
198  * @param player_ptr    プレイヤーへの参照ポインタ
199  * @return 立ち止まるべき条件が満たされたらTRUE
200  * ダッシュ移動が継続できるならばTRUEを返す。
201  * Return TRUE if the running should be stopped
202  */
203 static bool run_test(PlayerType *player_ptr)
204 {
205     DIRECTION prev_dir = find_prevdir;
206     int max = (prev_dir & 0x01) + 1;
207     auto *floor_ptr = player_ptr->current_floor_ptr;
208     if ((disturb_trap_detect || alert_trap_detect) && player_ptr->dtrap && !(floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_IN_DETECT)) {
209         player_ptr->dtrap = false;
210         if (!(floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_UNSAFE)) {
211             if (alert_trap_detect) {
212                 msg_print(_("* 注意:この先はトラップの感知範囲外です! *", "*Leaving trap detect region!*"));
213             }
214
215             if (disturb_trap_detect) {
216                 /* Break Run */
217                 return true;
218             }
219         }
220     }
221
222     DIRECTION check_dir = 0;
223     int option = 0, option2 = 0;
224     for (int i = -max; i <= max; i++) {
225         DIRECTION new_dir = cycle[chome[prev_dir] + i];
226         int row = player_ptr->y + ddy[new_dir];
227         int col = player_ptr->x + ddx[new_dir];
228         grid_type *g_ptr;
229         g_ptr = &floor_ptr->grid_array[row][col];
230         FEAT_IDX feat = g_ptr->get_feat_mimic();
231         TerrainType *f_ptr;
232         f_ptr = &terrains_info[feat];
233         if (g_ptr->m_idx) {
234             auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
235             if (m_ptr->ml) {
236                 return true;
237             }
238         }
239
240         for (const auto this_o_idx : g_ptr->o_idx_list) {
241             ItemEntity *o_ptr;
242             o_ptr = &floor_ptr->o_list[this_o_idx];
243             if (o_ptr->marked.has(OmType::FOUND)) {
244                 return true;
245             }
246         }
247
248         bool inv = true;
249         if (g_ptr->is_mark()) {
250             bool notice = f_ptr->flags.has(TerrainCharacteristics::NOTICE);
251             if (notice && f_ptr->flags.has(TerrainCharacteristics::MOVE)) {
252                 if (find_ignore_doors && f_ptr->flags.has_all_of({ TerrainCharacteristics::DOOR, TerrainCharacteristics::CLOSE })) {
253                     notice = false;
254                 } else if (find_ignore_stairs && f_ptr->flags.has(TerrainCharacteristics::STAIRS)) {
255                     notice = false;
256                 } else if (f_ptr->flags.has(TerrainCharacteristics::LAVA) && (has_immune_fire(player_ptr) || is_invuln(player_ptr))) {
257                     notice = false;
258                 } else if (f_ptr->flags.has_all_of({ TerrainCharacteristics::WATER, TerrainCharacteristics::DEEP }) && (player_ptr->levitation || player_ptr->can_swim || (calc_inventory_weight(player_ptr) <= calc_weight_limit(player_ptr)))) {
259                     notice = false;
260                 }
261             }
262
263             if (notice) {
264                 return true;
265             }
266
267             inv = false;
268         }
269
270         if (!inv && see_wall(player_ptr, 0, row, col)) {
271             if (find_openarea) {
272                 if (i < 0) {
273                     find_breakright = true;
274                 } else if (i > 0) {
275                     find_breakleft = true;
276                 }
277             }
278
279             continue;
280         }
281
282         if (find_openarea) {
283             continue;
284         }
285
286         if (!option) {
287             option = new_dir;
288             continue;
289         }
290
291         if (option2) {
292             return true;
293         }
294
295         if (option != cycle[chome[prev_dir] + i - 1]) {
296             return true;
297         }
298
299         if (new_dir & 0x01) {
300             check_dir = cycle[chome[prev_dir] + i - 2];
301             option2 = new_dir;
302             continue;
303         }
304
305         check_dir = cycle[chome[prev_dir] + i + 1];
306         option2 = option;
307         option = new_dir;
308     }
309
310     if (find_openarea) {
311         for (int i = -max; i < 0; i++) {
312             if (!see_wall(player_ptr, cycle[chome[prev_dir] + i], player_ptr->y, player_ptr->x)) {
313                 if (find_breakright) {
314                     return true;
315                 }
316             } else {
317                 if (find_breakleft) {
318                     return true;
319                 }
320             }
321         }
322
323         for (int i = max; i > 0; i--) {
324             if (!see_wall(player_ptr, cycle[chome[prev_dir] + i], player_ptr->y, player_ptr->x)) {
325                 if (find_breakleft) {
326                     return true;
327                 }
328             } else {
329                 if (find_breakright) {
330                     return true;
331                 }
332             }
333         }
334
335         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
336     }
337
338     if (!option) {
339         return true;
340     }
341
342     if (!option2) {
343         find_current = option;
344         find_prevdir = option;
345         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
346     } else if (!find_cut) {
347         find_current = option;
348         find_prevdir = option2;
349         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
350     }
351
352     int row = player_ptr->y + ddy[option];
353     int col = player_ptr->x + ddx[option];
354     if (!see_wall(player_ptr, option, row, col) || !see_wall(player_ptr, check_dir, row, col)) {
355         if (see_nothing(player_ptr, option, row, col) && see_nothing(player_ptr, option2, row, col)) {
356             find_current = option;
357             find_prevdir = option2;
358             return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
359         }
360
361         return true;
362     }
363
364     if (find_cut) {
365         find_current = option2;
366         find_prevdir = option2;
367         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
368     }
369
370     find_current = option;
371     find_prevdir = option2;
372     return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
373 }
374
375 /*!
376  * @brief 継続的なダッシュ処理 /
377  * Take one step along the current "run" path
378  * @param player_ptr    プレイヤーへの参照ポインタ
379  * @param dir 移動を試みる方向ID
380  */
381 void run_step(PlayerType *player_ptr, DIRECTION dir)
382 {
383     if (dir) {
384         ignore_avoid_run = true;
385         if (see_wall(player_ptr, dir, player_ptr->y, player_ptr->x)) {
386             sound(SOUND_HITWALL);
387             msg_print(_("その方向には走れません。", "You cannot run in that direction."));
388             disturb(player_ptr, false, false);
389             return;
390         }
391
392         run_init(player_ptr, dir);
393     } else {
394         if (run_test(player_ptr)) {
395             disturb(player_ptr, false, false);
396             return;
397         }
398     }
399
400     if (--player_ptr->running <= 0) {
401         return;
402     }
403
404     PlayerEnergy(player_ptr).set_player_turn_energy(100);
405     exe_movement(player_ptr, find_current, false, false);
406     if (player_bold(player_ptr, player_ptr->run_py, player_ptr->run_px)) {
407         player_ptr->run_py = 0;
408         player_ptr->run_px = 0;
409         disturb(player_ptr, false, false);
410     }
411 }