OSDN Git Service

[Implement] ウサウサストライカー召喚処理を追加
[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     auto *floor_ptr = player_ptr->current_floor_ptr;
61     const Pos2D pos(y + ddy[dir], x + ddx[dir]);
62     if (!in_bounds2(floor_ptr, pos.y, pos.x)) {
63         return false;
64     }
65
66     const auto &grid = floor_ptr->get_grid(pos);
67     if (!grid.is_mark()) {
68         return false;
69     }
70
71     const auto terrain_id = grid.get_feat_mimic();
72     const auto &terrain = grid.get_terrain_mimic();
73     if (!player_can_enter(player_ptr, terrain_id, 0)) {
74         return terrain.flags.has_not(TerrainCharacteristics::DOOR);
75     }
76
77     if (terrain.flags.has(TerrainCharacteristics::AVOID_RUN) && !ignore_avoid_run) {
78         return true;
79     }
80
81     if (terrain.flags.has_none_of({ TerrainCharacteristics::MOVE, TerrainCharacteristics::CAN_FLY })) {
82         return terrain.flags.has_not(TerrainCharacteristics::DOOR);
83     }
84
85     return false;
86 }
87
88 /*!
89  * @brief ダッシュ処理の導入 /
90  * Initialize the running algorithm for a new direction.
91  * @param player_ptr    プレイヤーへの参照ポインタ
92  * @param dir 導入の移動先
93  * @details
94  * Diagonal Corridor -- allow diaginal entry into corridors.\n
95  *\n
96  * Blunt Corridor -- If there is a wall two spaces ahead and\n
97  * we seem to be in a corridor, then force a turn into the side\n
98  * corridor, must be moving straight into a corridor here. ???\n
99  *\n
100  * Diagonal Corridor    Blunt Corridor (?)\n
101  *       \# \#                  \#\n
102  *       \#x\#                  \@x\#\n
103  *       \@\@p.                  p\n
104  */
105 static void run_init(PlayerType *player_ptr, DIRECTION dir)
106 {
107     find_current = dir;
108     find_prevdir = dir;
109     find_openarea = true;
110     find_breakright = find_breakleft = false;
111     bool deepleft = false;
112     bool deepright = false;
113     bool shortright = false;
114     bool shortleft = false;
115     player_ptr->run_py = player_ptr->y;
116     player_ptr->run_px = player_ptr->x;
117     int row = player_ptr->y + ddy[dir];
118     int col = player_ptr->x + ddx[dir];
119     ignore_avoid_run = cave_has_flag_bold(player_ptr->current_floor_ptr, row, col, TerrainCharacteristics::AVOID_RUN);
120     int i = chome[dir];
121     if (see_wall(player_ptr, cycle[i + 1], player_ptr->y, player_ptr->x)) {
122         find_breakleft = true;
123         shortleft = true;
124     } else if (see_wall(player_ptr, cycle[i + 1], row, col)) {
125         find_breakleft = true;
126         deepleft = true;
127     }
128
129     if (see_wall(player_ptr, cycle[i - 1], player_ptr->y, player_ptr->x)) {
130         find_breakright = true;
131         shortright = true;
132     } else if (see_wall(player_ptr, cycle[i - 1], row, col)) {
133         find_breakright = true;
134         deepright = true;
135     }
136
137     if (!find_breakleft || !find_breakright) {
138         return;
139     }
140
141     find_openarea = false;
142     if (dir & 0x01) {
143         if (deepleft && !deepright) {
144             find_prevdir = cycle[i - 1];
145         } else if (deepright && !deepleft) {
146             find_prevdir = cycle[i + 1];
147         }
148
149         return;
150     }
151
152     if (!see_wall(player_ptr, cycle[i], row, col)) {
153         return;
154     }
155
156     if (shortleft && !shortright) {
157         find_prevdir = cycle[i - 2];
158     } else if (shortright && !shortleft) {
159         find_prevdir = cycle[i + 2];
160     }
161 }
162
163 /*!
164  * @brief ダッシュ移動処理中、移動先のマスか未知の地形かどうかを判定する /
165  * Hack -- Check for an "unknown corner" (see below)
166  * @param player_ptr    プレイヤーへの参照ポインタ
167  * @param dir 想定する移動方向ID
168  * @param y 移動元のY座標
169  * @param x 移動元のX座標
170  * @return 移動先が未知の地形ならばTRUE
171  */
172 static bool see_nothing(PlayerType *player_ptr, DIRECTION dir, POSITION y, POSITION x)
173 {
174     y += ddy[dir];
175     x += ddx[dir];
176
177     auto *floor_ptr = player_ptr->current_floor_ptr;
178     if (!in_bounds2(floor_ptr, y, x)) {
179         return true;
180     }
181
182     if (floor_ptr->grid_array[y][x].is_mark()) {
183         return false;
184     }
185
186     if (player_can_see_bold(player_ptr, y, x)) {
187         return false;
188     }
189
190     return true;
191 }
192
193 /*!
194  * @brief ダッシュ移動が継続できるかどうかの判定 /
195  * Update the current "run" path
196  * @param player_ptr    プレイヤーへの参照ポインタ
197  * @return 立ち止まるべき条件が満たされたらTRUE
198  * ダッシュ移動が継続できるならばTRUEを返す。
199  * Return TRUE if the running should be stopped
200  */
201 static bool run_test(PlayerType *player_ptr)
202 {
203     const auto prev_dir = find_prevdir;
204     const auto max = (prev_dir & 0x01) + 1;
205     const auto &floor = *player_ptr->current_floor_ptr;
206     const auto p_pos = player_ptr->get_position();
207     const auto &p_grid = floor.get_grid(p_pos);
208     if ((disturb_trap_detect || alert_trap_detect) && player_ptr->dtrap && !(p_grid.info & CAVE_IN_DETECT)) {
209         player_ptr->dtrap = false;
210         if (!(p_grid.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     auto check_dir = 0;
223     auto option = 0;
224     auto option2 = 0;
225     for (auto i = -max; i <= max; i++) {
226         int new_dir = cycle[chome[prev_dir] + i];
227         const Pos2D pos(player_ptr->y + ddy[new_dir], player_ptr->x + ddx[new_dir]);
228         const auto &grid = floor.get_grid(pos);
229         if (grid.m_idx) {
230             const auto &monster = floor.m_list[grid.m_idx];
231             if (monster.ml) {
232                 return true;
233             }
234         }
235
236         for (const auto this_o_idx : grid.o_idx_list) {
237             const auto &item = floor.o_list[this_o_idx];
238             if (item.marked.has(OmType::FOUND)) {
239                 return true;
240             }
241         }
242
243         auto inv = true;
244         if (grid.is_mark()) {
245             const auto &terrain = grid.get_terrain_mimic();
246             auto notice = terrain.flags.has(TerrainCharacteristics::NOTICE);
247             if (notice && terrain.flags.has(TerrainCharacteristics::MOVE)) {
248                 if (find_ignore_doors && terrain.flags.has_all_of({ TerrainCharacteristics::DOOR, TerrainCharacteristics::CLOSE })) {
249                     notice = false;
250                 } else if (find_ignore_stairs && terrain.flags.has(TerrainCharacteristics::STAIRS)) {
251                     notice = false;
252                 } else if (terrain.flags.has(TerrainCharacteristics::LAVA) && (has_immune_fire(player_ptr) || is_invuln(player_ptr))) {
253                     notice = false;
254                 } else if (terrain.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)))) {
255                     notice = false;
256                 }
257             }
258
259             if (notice) {
260                 return true;
261             }
262
263             inv = false;
264         }
265
266         if (!inv && see_wall(player_ptr, 0, pos.y, pos.x)) {
267             if (find_openarea) {
268                 if (i < 0) {
269                     find_breakright = true;
270                 } else if (i > 0) {
271                     find_breakleft = true;
272                 }
273             }
274
275             continue;
276         }
277
278         if (find_openarea) {
279             continue;
280         }
281
282         if (!option) {
283             option = new_dir;
284             continue;
285         }
286
287         if (option2) {
288             return true;
289         }
290
291         if (option != cycle[chome[prev_dir] + i - 1]) {
292             return true;
293         }
294
295         if (new_dir & 0x01) {
296             check_dir = cycle[chome[prev_dir] + i - 2];
297             option2 = new_dir;
298             continue;
299         }
300
301         check_dir = cycle[chome[prev_dir] + i + 1];
302         option2 = option;
303         option = new_dir;
304     }
305
306     if (find_openarea) {
307         for (int i = -max; i < 0; i++) {
308             if (!see_wall(player_ptr, cycle[chome[prev_dir] + i], player_ptr->y, player_ptr->x)) {
309                 if (find_breakright) {
310                     return true;
311                 }
312             } else {
313                 if (find_breakleft) {
314                     return true;
315                 }
316             }
317         }
318
319         for (int i = max; i > 0; i--) {
320             if (!see_wall(player_ptr, cycle[chome[prev_dir] + i], player_ptr->y, player_ptr->x)) {
321                 if (find_breakleft) {
322                     return true;
323                 }
324             } else {
325                 if (find_breakright) {
326                     return true;
327                 }
328             }
329         }
330
331         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
332     }
333
334     if (!option) {
335         return true;
336     }
337
338     if (!option2) {
339         find_current = option;
340         find_prevdir = option;
341         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
342     } else if (!find_cut) {
343         find_current = option;
344         find_prevdir = option2;
345         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
346     }
347
348     int row = player_ptr->y + ddy[option];
349     int col = player_ptr->x + ddx[option];
350     if (!see_wall(player_ptr, option, row, col) || !see_wall(player_ptr, check_dir, row, col)) {
351         if (see_nothing(player_ptr, option, row, col) && see_nothing(player_ptr, option2, row, col)) {
352             find_current = option;
353             find_prevdir = option2;
354             return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
355         }
356
357         return true;
358     }
359
360     if (find_cut) {
361         find_current = option2;
362         find_prevdir = option2;
363         return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
364     }
365
366     find_current = option;
367     find_prevdir = option2;
368     return see_wall(player_ptr, find_current, player_ptr->y, player_ptr->x);
369 }
370
371 /*!
372  * @brief 継続的なダッシュ処理 /
373  * Take one step along the current "run" path
374  * @param player_ptr    プレイヤーへの参照ポインタ
375  * @param dir 移動を試みる方向ID
376  */
377 void run_step(PlayerType *player_ptr, DIRECTION dir)
378 {
379     if (dir) {
380         ignore_avoid_run = true;
381         if (see_wall(player_ptr, dir, player_ptr->y, player_ptr->x)) {
382             sound(SOUND_HITWALL);
383             msg_print(_("その方向には走れません。", "You cannot run in that direction."));
384             disturb(player_ptr, false, false);
385             return;
386         }
387
388         run_init(player_ptr, dir);
389     } else {
390         if (run_test(player_ptr)) {
391             disturb(player_ptr, false, false);
392             return;
393         }
394     }
395
396     if (--player_ptr->running <= 0) {
397         return;
398     }
399
400     PlayerEnergy(player_ptr).set_player_turn_energy(100);
401     exe_movement(player_ptr, find_current, false, false);
402     if (player_ptr->is_located_at_running_destination()) {
403         player_ptr->run_py = 0;
404         player_ptr->run_px = 0;
405         disturb(player_ptr, false, false);
406     }
407 }