OSDN Git Service

[Refactor] #3650 Grid::get_feat_mimic() から地形特性を得ていた箇所をget_terrain_mimic() に置換した
[hengbandforosx/hengbandosx.git] / src / target / grid-selector.cpp
1 #include "target/grid-selector.h"
2 #include "core/stuff-handler.h"
3 #include "core/window-redrawer.h"
4 #include "floor/cave.h"
5 #include "floor/geometry.h"
6 #include "game-option/game-play-options.h"
7 #include "game-option/input-options.h"
8 #include "game-option/keymap-directory-getter.h"
9 #include "grid/feature-flag-types.h"
10 #include "io/cursor.h"
11 #include "io/input-key-acceptor.h"
12 #include "io/screen-util.h"
13 #include "system/floor-type-definition.h"
14 #include "system/grid-type-definition.h"
15 #include "system/redrawing-flags-updater.h"
16 #include "target/target-checker.h"
17 #include "term/screen-processor.h"
18 #include "timed-effect/player-hallucination.h"
19 #include "timed-effect/timed-effects.h"
20 #include "util/int-char-converter.h"
21 #include "util/sort.h"
22 #include "view/display-messages.h"
23 #include "window/main-window-util.h"
24 #include <functional>
25 #include <unordered_map>
26 #include <vector>
27
28 /*
29  * XAngband: determine if a given location is "interesting"
30  * based on target_set_accept function.
31  */
32 static bool tgt_pt_accept(PlayerType *player_ptr, POSITION y, POSITION x)
33 {
34     auto *floor_ptr = player_ptr->current_floor_ptr;
35     if (!(in_bounds(floor_ptr, y, x))) {
36         return false;
37     }
38
39     if ((y == player_ptr->y) && (x == player_ptr->x)) {
40         return true;
41     }
42
43     if (player_ptr->effects()->hallucination()->is_hallucinated()) {
44         return false;
45     }
46
47     auto &grid = floor_ptr->grid_array[y][x];
48     if (!grid.is_mark()) {
49         return false;
50     }
51
52     using Tc = TerrainCharacteristics;
53     auto is_acceptable = grid.cave_has_flag(Tc::LESS);
54     is_acceptable |= grid.cave_has_flag(Tc::MORE);
55     is_acceptable |= grid.cave_has_flag(Tc::QUEST_ENTER);
56     is_acceptable |= grid.cave_has_flag(Tc::QUEST_EXIT);
57     is_acceptable |= grid.cave_has_flag(Tc::STORE);
58     is_acceptable |= grid.cave_has_flag(Tc::BLDG);
59     return is_acceptable;
60 }
61
62 /*
63  * XAngband: Prepare the "temp" array for "tget_pt"
64  * based on target_set_prepare funciton.
65  */
66 static void tgt_pt_prepare(PlayerType *player_ptr, std::vector<POSITION> &ys, std::vector<POSITION> &xs)
67 {
68     if (!expand_list) {
69         return;
70     }
71
72     auto *floor_ptr = player_ptr->current_floor_ptr;
73     for (POSITION y = 1; y < floor_ptr->height; y++) {
74         for (POSITION x = 1; x < floor_ptr->width; x++) {
75             if (!tgt_pt_accept(player_ptr, y, x)) {
76                 continue;
77             }
78
79             ys.emplace_back(y);
80             xs.emplace_back(x);
81         }
82     }
83
84     ang_sort(player_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_distance, ang_sort_swap_position);
85 }
86
87 /*!
88  * @brief 指定したシンボルのマスかどうかを判定するための条件式コールバック
89  */
90 std::unordered_map<int, std::function<bool(Grid *)>> tgt_pt_symbol_call_back = {
91     { '<', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STAIRS) && g_ptr->cave_has_flag(TerrainCharacteristics::LESS); } },
92     { '>', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STAIRS) && g_ptr->cave_has_flag(TerrainCharacteristics::MORE); } },
93     { '+', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::BLDG); } },
94     { '0', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('0'); } },
95     { '!', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('1'); } },
96     { '"', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('2'); } },
97     { '#', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('3'); } },
98     { '$', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('4'); } },
99     { '%', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('5'); } },
100     { '&', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('6'); } },
101     { '\'', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('7'); } },
102     { '(', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('8'); } },
103     { ')', [](Grid *g_ptr) { return g_ptr->cave_has_flag(TerrainCharacteristics::STORE) && g_ptr->is_symbol('9'); } },
104 };
105
106 /*!
107  * @brief 位置ターゲット指定情報構造体
108  * @details
109  * ang_sort() を利用する関係上、y/x 座標それぞれについて配列を作る。
110  */
111 struct tgt_pt_info {
112     tgt_pt_info()
113     {
114         std::tie(this->width, this->height) = get_screen_size();
115     };
116
117     int width; //!< 画面サイズ(幅)
118     int height; //!< 画面サイズ(高さ)
119     POSITION y = 0; //!< 現在の指定位置(Y)
120     POSITION x = 0; //!< 現在の指定位置(X)
121     std::vector<POSITION> ys{}; //!< "interesting" な座標たちを記録する配列(Y)
122     std::vector<POSITION> xs{}; //!< "interesting" な座標たちを記録する配列(X)
123     size_t n = 0; //<! シンボル配列の何番目か
124     char ch = '\0'; //<! 入力キー
125     char prev_ch = '\0'; //<! 前回入力キー
126     std::function<bool(Grid *)> callback{}; //<! 条件判定コールバック
127
128     void move_to_symbol(PlayerType *player_ptr);
129 };
130
131 /*!
132  * @brief 指定した記号のシンボルのグリッドにカーソルを移動する
133  * @param player_ptr プレイヤー情報への参照ポインタ
134  * @details 自分 (@)の位置に戻ってくるような処理に見える.
135  * コールバックにも依る?
136  */
137 void tgt_pt_info::move_to_symbol(PlayerType *player_ptr)
138 {
139     if (!expand_list || this->ys.empty()) {
140         return;
141     }
142
143     int dx, dy;
144     int cx = (panel_col_min + panel_col_max) / 2;
145     int cy = (panel_row_min + panel_row_max) / 2;
146     if (this->ch != this->prev_ch) {
147         this->n = 0;
148     }
149     this->prev_ch = this->ch;
150     this->n++;
151
152     for (; this->n < size(this->ys); ++this->n) {
153         const POSITION y_cur = this->ys[this->n];
154         const POSITION x_cur = this->xs[this->n];
155         auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y_cur][x_cur];
156         if (this->callback(g_ptr)) {
157             break;
158         }
159     }
160
161     if (this->n == size(this->ys)) {
162         this->n = 0;
163         this->y = player_ptr->y;
164         this->x = player_ptr->x;
165         verify_panel(player_ptr);
166         auto &rfu = RedrawingFlagsUpdater::get_instance();
167         rfu.set_flag(StatusRecalculatingFlag::MONSTER_STATUSES);
168         rfu.set_flag(MainWindowRedrawingFlag::MAP);
169         rfu.set_flag(SubWindowRedrawingFlag::OVERHEAD);
170         handle_stuff(player_ptr);
171     } else {
172         this->y = this->ys[this->n];
173         this->x = this->xs[this->n];
174         dy = 2 * (this->y - cy) / this->height;
175         dx = 2 * (this->x - cx) / this->width;
176         if (dy || dx) {
177             change_panel(player_ptr, dy, dx);
178         }
179     }
180 }
181
182 /*!
183  * @brief 位置を指定するプロンプト
184  * @param player_ptr プレイヤー情報への参照ポインタ
185  * @param x_ptr x座標への参照ポインタ
186  * @param y_ptr y座標への参照ポインタ
187  * @return 指定したらTRUE、キャンセルしたらFALSE
188  */
189 bool tgt_pt(PlayerType *player_ptr, POSITION *x_ptr, POSITION *y_ptr)
190 {
191     tgt_pt_info info;
192     info.y = player_ptr->y;
193     info.x = player_ptr->x;
194     if (expand_list) {
195         tgt_pt_prepare(player_ptr, info.ys, info.xs);
196     }
197
198     msg_print(_("場所を選んでスペースキーを押して下さい。", "Select a point and press space."));
199     msg_flag = false;
200
201     info.ch = 0;
202     info.n = 0;
203     bool success = false;
204     while ((info.ch != ESCAPE) && !success) {
205         bool move_fast = false;
206         move_cursor_relative(info.y, info.x);
207         info.ch = inkey();
208         switch (info.ch) {
209         case ESCAPE:
210             break;
211         case ' ':
212         case 't':
213         case '.':
214             if (player_ptr->is_located_at({ info.y, info.x })) {
215                 info.ch = 0;
216             } else {
217                 success = true;
218             }
219             break;
220         case '>':
221         case '<':
222         case '+':
223         case '!':
224         case '"':
225         case '#':
226         case '$':
227         case '%':
228         case '&':
229         case '\'':
230         case '(':
231         case ')': {
232             info.callback = tgt_pt_symbol_call_back[info.ch];
233             info.move_to_symbol(player_ptr);
234             break;
235         }
236         default: {
237             if (rogue_like_commands) {
238                 if (info.ch >= '0' && info.ch <= '9') {
239                     if (info.ch != '0') {
240                         info.ch -= 16;
241                     }
242                     info.callback = tgt_pt_symbol_call_back[info.ch];
243                     info.move_to_symbol(player_ptr);
244                     break;
245                 }
246             } else {
247                 if (info.ch == '5' || info.ch == '0') {
248                     if (player_ptr->is_located_at({ info.y, info.x })) {
249                         info.ch = 0;
250                     } else {
251                         success = true;
252                     }
253                     break;
254                 }
255             }
256
257             int d = get_keymap_dir(info.ch);
258             if (isupper(info.ch)) {
259                 move_fast = true;
260             }
261
262             if (d == 0) {
263                 break;
264             }
265
266             int dx = ddx[d];
267             int dy = ddy[d];
268             if (move_fast) {
269                 int mag = std::min(info.width / 2, info.height / 2);
270                 info.x += dx * mag;
271                 info.y += dy * mag;
272             } else {
273                 info.x += dx;
274                 info.y += dy;
275             }
276
277             if (((info.x < panel_col_min + info.width / 2) && (dx > 0)) || ((info.x > panel_col_min + info.width / 2) && (dx < 0))) {
278                 dx = 0;
279             }
280
281             if (((info.y < panel_row_min + info.height / 2) && (dy > 0)) || ((info.y > panel_row_min + info.height / 2) && (dy < 0))) {
282                 dy = 0;
283             }
284
285             if ((info.y >= panel_row_min + info.height) || (info.y < panel_row_min) || (info.x >= panel_col_min + info.width) || (info.x < panel_col_min)) {
286                 change_panel(player_ptr, dy, dx);
287             }
288
289             if (info.x >= player_ptr->current_floor_ptr->width - 1) {
290                 info.x = player_ptr->current_floor_ptr->width - 2;
291             } else if (info.x <= 0) {
292                 info.x = 1;
293             }
294
295             if (info.y >= player_ptr->current_floor_ptr->height - 1) {
296                 info.y = player_ptr->current_floor_ptr->height - 2;
297             } else if (info.y <= 0) {
298                 info.y = 1;
299             }
300
301             break;
302         }
303         }
304     }
305
306     prt("", 0, 0);
307     verify_panel(player_ptr);
308     auto &rfu = RedrawingFlagsUpdater::get_instance();
309     rfu.set_flag(StatusRecalculatingFlag::MONSTER_STATUSES);
310     rfu.set_flag(MainWindowRedrawingFlag::MAP);
311     rfu.set_flag(SubWindowRedrawingFlag::OVERHEAD);
312     handle_stuff(player_ptr);
313     *x_ptr = info.x;
314     *y_ptr = info.y;
315     return success;
316 }