OSDN Git Service

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