1 #include "target/target-setter.h"
2 #include "core/stuff-handler.h"
3 #include "core/window-redrawer.h"
4 #include "floor/geometry.h"
5 #include "floor/line-of-sight.h"
6 #include "game-option/cheat-options.h"
7 #include "game-option/game-play-options.h"
8 #include "game-option/input-options.h"
10 #include "io/input-key-requester.h"
11 #include "io/screen-util.h"
12 #include "main/sound-of-music.h"
13 #include "system/floor-type-definition.h"
14 #include "system/grid-type-definition.h"
15 #include "system/player-type-definition.h"
16 #include "system/redrawing-flags-updater.h"
17 #include "target/projection-path-calculator.h"
18 #include "target/target-checker.h"
19 #include "target/target-describer.h"
20 #include "target/target-preparation.h"
21 #include "target/target-types.h"
22 #include "term/screen-processor.h"
23 #include "term/z-form.h"
24 #include "util/bit-flags-calculator.h"
25 #include "util/int-char-converter.h"
26 #include "util/string-processor.h"
27 #include "window/display-sub-windows.h"
28 #include "window/main-window-util.h"
31 // "interesting" な座標たちを記録する配列。
32 // ang_sort() を利用する関係上、y/x座標それぞれについて配列を作る。
33 static std::vector<POSITION> ys_interest;
34 static std::vector<POSITION> xs_interest;
41 POSITION y2; // panel_row_min 退避用
42 POSITION x2; // panel_col_min 退避用
44 bool flag; // 移動コマンド入力時、"interesting" な座標へ飛ぶかどうか
49 int m; // "interesting" な座標たちのうち現在ターゲットしているもののインデックス
50 int distance; // カーソルの移動方向 (1,2,3,4,6,7,8,9)
51 int target_num; // target_pick() の結果
52 bool move_fast; // カーソル移動を粗くする(1マスずつ移動しない)
55 static ts_type *initialize_target_set_type(PlayerType *player_ptr, ts_type *ts_ptr, target_type mode)
58 ts_ptr->y = player_ptr->y;
59 ts_ptr->x = player_ptr->x;
62 get_screen_size(&ts_ptr->wid, &ts_ptr->hgt);
68 * @brief フォーカスを当てるべきマップ描画の基準座標を指定する
69 * @param player_ptr プレイヤーへの参照ポインタ
73 * Handle a request to change the current panel
74 * Return TRUE if the panel was changed.
75 * Also used in do_cmd_locate
76 * @return 実際に再描画が必要だった場合TRUEを返す
78 static bool change_panel_xy(PlayerType *player_ptr, POSITION y, POSITION x)
80 POSITION dy = 0, dx = 0;
82 get_screen_size(&wid, &hgt);
83 if (y < panel_row_min) {
87 if (y > panel_row_max) {
91 if (x < panel_col_min) {
95 if (x > panel_col_max) {
103 return change_panel(player_ptr, dy, dx);
107 * @brief "interesting" な座標たちのうち、(y1,x1) から (dy,dx) 方向にある最も近いもののインデックスを得る。
110 * @param dy 現在地からの向きy [-1,1]
111 * @param dx 現在地からの向きx [-1,1]
112 * @return 最も近い座標のインデックス。適切なものがない場合 -1
114 static POSITION_IDX target_pick(const POSITION y1, const POSITION x1, const POSITION dy, const POSITION dx)
116 // 最も近いもののインデックスとその距離。
117 POSITION_IDX b_i = -1, b_v = 9999;
119 for (POSITION_IDX i = 0; i < (POSITION_IDX)size(ys_interest); i++) {
120 const POSITION x2 = xs_interest[i];
121 const POSITION y2 = ys_interest[i];
123 // (y1,x1) から (y2,x2) へ向かうベクトル。
124 const POSITION x3 = (x2 - x1);
125 const POSITION y3 = (y2 - y1);
127 // (dy,dx) 方向にないものを除外する。
129 // dx > 0 のとき、x3 <= 0 なるものは除外。
130 // dx < 0 のとき、x3 >= 0 なるものは除外。
131 if (dx && (x3 * dx <= 0)) {
135 // dy > 0 のとき、y3 <= 0 なるものは除外。
136 // dy < 0 のとき、y3 >= 0 なるものは除外。
137 if (dy && (y3 * dy <= 0)) {
141 const POSITION x4 = std::abs(x3);
142 const POSITION y4 = std::abs(y3);
144 // (dy,dx) が (-1,0) or (1,0) のとき、|x3| > |y3| なるものは除外。
145 if (dy && !dx && (x4 > y4)) {
149 // (dy,dx) が (0,-1) or (0,1) のとき、|y3| > |x3| なるものは除外。
150 if (dx && !dy && (y4 > x4)) {
154 // (y1,x1), (y2,x2) 間の距離を求め、最も近いものを更新する。
156 const POSITION_IDX v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4));
157 if ((b_i >= 0) && (v >= b_v)) {
168 static void describe_projectablity(PlayerType *player_ptr, ts_type *ts_ptr)
170 ts_ptr->y = ys_interest[ts_ptr->m];
171 ts_ptr->x = xs_interest[ts_ptr->m];
172 change_panel_xy(player_ptr, ts_ptr->y, ts_ptr->x);
173 if ((ts_ptr->mode & TARGET_LOOK) == 0) {
174 print_path(player_ptr, ts_ptr->y, ts_ptr->x);
177 ts_ptr->g_ptr = &player_ptr->current_floor_ptr->grid_array[ts_ptr->y][ts_ptr->x];
178 if (target_able(player_ptr, ts_ptr->g_ptr->m_idx)) {
179 angband_strcpy(ts_ptr->info, _("q止 t決 p自 o現 +次 -前", "q,t,p,o,+,-,<dir>"), sizeof(ts_ptr->info));
181 angband_strcpy(ts_ptr->info, _("q止 p自 o現 +次 -前", "q,p,o,+,-,<dir>"), sizeof(ts_ptr->info));
189 strnfmt(cheatinfo, sizeof(cheatinfo), " X:%d Y:%d LOS:%d LOP:%d", ts_ptr->x,
191 los(player_ptr, player_ptr->y, player_ptr->x, ts_ptr->y, ts_ptr->x),
192 projectable(player_ptr, player_ptr->y, player_ptr->x, ts_ptr->y, ts_ptr->x));
193 angband_strcat(ts_ptr->info, cheatinfo, sizeof(ts_ptr->info));
196 static void menu_target(ts_type *ts_ptr)
202 if (ts_ptr->query == '\r') {
207 static void switch_target_input(PlayerType *player_ptr, ts_type *ts_ptr)
209 ts_ptr->distance = 0;
210 switch (ts_ptr->query) {
219 if (!target_able(player_ptr, ts_ptr->g_ptr->m_idx)) {
224 health_track(player_ptr, ts_ptr->g_ptr->m_idx);
225 target_who = ts_ptr->g_ptr->m_idx;
226 target_row = ts_ptr->y;
227 target_col = ts_ptr->x;
233 if (++ts_ptr->m != (int)size(ys_interest)) {
244 if (ts_ptr->m-- != 0) {
248 ts_ptr->m = (int)size(ys_interest) - 1;
255 verify_panel(player_ptr);
256 auto &rfu = RedrawingFlagsUpdater::get_instance();
257 rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
258 rfu.set_flag(MainWindowRedrawingFlag::MAP);
259 player_ptr->window_flags |= PW_OVERHEAD;
260 handle_stuff(player_ptr);
261 target_set_prepare(player_ptr, ys_interest, xs_interest, ts_ptr->mode);
262 ts_ptr->y = player_ptr->y;
263 ts_ptr->x = player_ptr->x;
267 ts_ptr->flag = false;
272 const char queried_command = rogue_like_commands ? 'x' : 'l';
273 if (ts_ptr->query != queried_command) {
274 ts_ptr->distance = get_keymap_dir(ts_ptr->query);
275 if (ts_ptr->distance == 0) {
282 if (++ts_ptr->m != (int)size(ys_interest)) {
297 * @brief カーソル移動に伴い、描画範囲、"interesting" 座標リスト、現在のターゲットを更新する。
298 * @return カーソル移動によって描画範囲が変化したかどうか
300 static bool check_panel_changed(PlayerType *player_ptr, ts_type *ts_ptr)
302 // カーソル移動によって描画範囲が変化しないなら何もせずその旨を返す。
303 if (!change_panel(player_ptr, ddy[ts_ptr->distance], ddx[ts_ptr->distance])) {
307 // 描画範囲が変化した場合、"interesting" 座標リストおよび現在のターゲットを更新する必要がある。
309 // "interesting" 座標を探す起点。
310 // ts_ptr->m が有効な座標を指していればそれを使う。
311 // さもなくば (ts_ptr->y, ts_ptr->x) を使う。
313 if (ts_ptr->m < (int)size(ys_interest)) {
314 v = ys_interest[ts_ptr->m];
315 u = xs_interest[ts_ptr->m];
321 // 新たな描画範囲を用いて "interesting" 座標リストを更新。
322 target_set_prepare(player_ptr, ys_interest, xs_interest, ts_ptr->mode);
324 // 新たな "interesting" 座標リストからターゲットを探す。
326 ts_ptr->target_num = target_pick(v, u, ddy[ts_ptr->distance], ddx[ts_ptr->distance]);
327 if (ts_ptr->target_num >= 0) {
328 ts_ptr->m = ts_ptr->target_num;
335 * @brief カーソル移動方向に "interesting" な座標がなかったとき、画面外まで探す。
337 * 既に "interesting" な座標を発見している場合、この関数は何もしない。
339 static void sweep_targets(PlayerType *player_ptr, ts_type *ts_ptr)
341 auto *floor_ptr = player_ptr->current_floor_ptr;
342 auto &rfu = RedrawingFlagsUpdater::get_instance();
343 while (ts_ptr->flag && (ts_ptr->target_num < 0)) {
344 // カーソル移動に伴い、必要なだけ描画範囲を更新。
345 // "interesting" 座標リストおよび現在のターゲットも更新。
346 if (check_panel_changed(player_ptr, ts_ptr)) {
350 POSITION dx = ddx[ts_ptr->distance];
351 POSITION dy = ddy[ts_ptr->distance];
352 panel_row_min = ts_ptr->y2;
353 panel_col_min = ts_ptr->x2;
354 panel_bounds_center();
355 rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
356 rfu.set_flag(MainWindowRedrawingFlag::MAP);
357 player_ptr->window_flags |= PW_OVERHEAD;
358 handle_stuff(player_ptr);
359 target_set_prepare(player_ptr, ys_interest, xs_interest, ts_ptr->mode);
360 ts_ptr->flag = false;
363 if (((ts_ptr->x < panel_col_min + ts_ptr->wid / 2) && (dx > 0)) || ((ts_ptr->x > panel_col_min + ts_ptr->wid / 2) && (dx < 0))) {
367 if (((ts_ptr->y < panel_row_min + ts_ptr->hgt / 2) && (dy > 0)) || ((ts_ptr->y > panel_row_min + ts_ptr->hgt / 2) && (dy < 0))) {
371 if ((ts_ptr->y >= panel_row_min + ts_ptr->hgt) || (ts_ptr->y < panel_row_min) || (ts_ptr->x >= panel_col_min + ts_ptr->wid) || (ts_ptr->x < panel_col_min)) {
372 if (change_panel(player_ptr, dy, dx)) {
373 target_set_prepare(player_ptr, ys_interest, xs_interest, ts_ptr->mode);
377 if (ts_ptr->x >= floor_ptr->width - 1) {
378 ts_ptr->x = floor_ptr->width - 2;
379 } else if (ts_ptr->x <= 0) {
383 if (ts_ptr->y >= floor_ptr->height - 1) {
384 ts_ptr->y = floor_ptr->height - 2;
385 } else if (ts_ptr->y <= 0) {
391 static bool set_target_grid(PlayerType *player_ptr, ts_type *ts_ptr)
393 if (!ts_ptr->flag || ys_interest.empty()) {
397 describe_projectablity(player_ptr, ts_ptr);
398 fix_floor_item_list(player_ptr, ts_ptr->y, ts_ptr->x);
401 ts_ptr->query = examine_grid(player_ptr, ts_ptr->y, ts_ptr->x, ts_ptr->mode, ts_ptr->info);
408 switch_target_input(player_ptr, ts_ptr);
409 if (ts_ptr->distance == 0) {
413 ts_ptr->y2 = panel_row_min;
414 ts_ptr->x2 = panel_col_min;
416 const POSITION y = ys_interest[ts_ptr->m];
417 const POSITION x = xs_interest[ts_ptr->m];
418 ts_ptr->target_num = target_pick(y, x, ddy[ts_ptr->distance], ddx[ts_ptr->distance]);
420 sweep_targets(player_ptr, ts_ptr);
421 ts_ptr->m = ts_ptr->target_num;
425 static void describe_grid_wizard(PlayerType *player_ptr, ts_type *ts_ptr)
431 constexpr auto fmt = " X:%d Y:%d LOS:%d LOP:%d SPECIAL:%d";
433 const auto is_los = los(player_ptr, player_ptr->y, player_ptr->x, ts_ptr->y, ts_ptr->x);
434 const auto is_projectable = projectable(player_ptr, player_ptr->y, player_ptr->x, ts_ptr->y, ts_ptr->x);
435 strnfmt(cheatinfo, sizeof(cheatinfo), fmt, ts_ptr->x, ts_ptr->y, is_los, is_projectable, ts_ptr->g_ptr->special);
436 angband_strcat(ts_ptr->info, cheatinfo, sizeof(ts_ptr->info));
439 static void switch_next_grid_command(PlayerType *player_ptr, ts_type *ts_ptr)
441 switch (ts_ptr->query) {
451 target_row = ts_ptr->y;
452 target_col = ts_ptr->x;
456 verify_panel(player_ptr);
457 auto &rfu = RedrawingFlagsUpdater::get_instance();
458 rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
459 rfu.set_flag(MainWindowRedrawingFlag::MAP);
460 player_ptr->window_flags |= PW_OVERHEAD;
461 handle_stuff(player_ptr);
462 target_set_prepare(player_ptr, ys_interest, xs_interest, ts_ptr->mode);
463 ts_ptr->y = player_ptr->y;
464 ts_ptr->x = player_ptr->x;
468 // ターゲット時の「m近」「o現」の切り替え
469 // すでに「o現」の時にoを押してもなにも起きない
479 for (size_t i = 0; i < size(ys_interest); i++) {
480 const POSITION y = ys_interest[i];
481 const POSITION x = xs_interest[i];
482 int t = distance(ts_ptr->y, ts_ptr->x, y, x);
490 ts_ptr->flag = false;
496 ts_ptr->distance = get_keymap_dir(ts_ptr->query);
497 if (isupper(ts_ptr->query)) {
498 ts_ptr->move_fast = true;
501 if (!ts_ptr->distance) {
509 static void decide_change_panel(PlayerType *player_ptr, ts_type *ts_ptr)
511 if (ts_ptr->distance == 0) {
515 POSITION dx = ddx[ts_ptr->distance];
516 POSITION dy = ddy[ts_ptr->distance];
517 if (ts_ptr->move_fast) {
518 int mag = std::min(ts_ptr->wid / 2, ts_ptr->hgt / 2);
519 ts_ptr->x += dx * mag;
520 ts_ptr->y += dy * mag;
526 if (((ts_ptr->x < panel_col_min + ts_ptr->wid / 2) && (dx > 0)) || ((ts_ptr->x > panel_col_min + ts_ptr->wid / 2) && (dx < 0))) {
530 if (((ts_ptr->y < panel_row_min + ts_ptr->hgt / 2) && (dy > 0)) || ((ts_ptr->y > panel_row_min + ts_ptr->hgt / 2) && (dy < 0))) {
534 auto should_change_panel = ts_ptr->y >= panel_row_min + ts_ptr->hgt;
535 should_change_panel |= ts_ptr->y < panel_row_min;
536 should_change_panel |= ts_ptr->x >= panel_col_min + ts_ptr->wid;
537 should_change_panel |= ts_ptr->x < panel_col_min;
538 if (should_change_panel && change_panel(player_ptr, dy, dx)) {
539 target_set_prepare(player_ptr, ys_interest, xs_interest, ts_ptr->mode);
542 auto *floor_ptr = player_ptr->current_floor_ptr;
543 if (ts_ptr->x >= floor_ptr->width - 1) {
544 ts_ptr->x = floor_ptr->width - 2;
545 } else if (ts_ptr->x <= 0) {
549 if (ts_ptr->y >= floor_ptr->height - 1) {
550 ts_ptr->y = floor_ptr->height - 2;
551 } else if (ts_ptr->y <= 0) {
556 static void sweep_target_grids(PlayerType *player_ptr, ts_type *ts_ptr)
558 while (!ts_ptr->done) {
559 if (set_target_grid(player_ptr, ts_ptr)) {
563 ts_ptr->move_fast = false;
564 if ((ts_ptr->mode & TARGET_LOOK) == 0) {
565 print_path(player_ptr, ts_ptr->y, ts_ptr->x);
568 ts_ptr->g_ptr = &player_ptr->current_floor_ptr->grid_array[ts_ptr->y][ts_ptr->x];
569 strcpy(ts_ptr->info, _("q止 t決 p自 m近 +次 -前", "q,t,p,m,+,-,<dir>"));
570 describe_grid_wizard(player_ptr, ts_ptr);
571 fix_floor_item_list(player_ptr, ts_ptr->y, ts_ptr->x);
573 /* Describe and Prompt (enable "TARGET_LOOK") */
574 const auto target = i2enum<target_type>(ts_ptr->mode | TARGET_LOOK);
575 while ((ts_ptr->query = examine_grid(player_ptr, ts_ptr->y, ts_ptr->x, target, ts_ptr->info)) == 0) {
579 ts_ptr->distance = 0;
580 if (use_menu && (ts_ptr->query == '\r')) {
584 switch_next_grid_command(player_ptr, ts_ptr);
585 decide_change_panel(player_ptr, ts_ptr);
590 * Handle "target" and "look".
592 bool target_set(PlayerType *player_ptr, target_type mode)
595 ts_type *ts_ptr = initialize_target_set_type(player_ptr, &tmp_ts, mode);
597 target_set_prepare(player_ptr, ys_interest, xs_interest, mode);
598 sweep_target_grids(player_ptr, ts_ptr);
600 verify_panel(player_ptr);
601 auto &rfu = RedrawingFlagsUpdater::get_instance();
602 rfu.set_flag(StatusRedrawingFlag::MONSTER_STATUSES);
603 rfu.set_flag(MainWindowRedrawingFlag::MAP);
604 set_bits(player_ptr->window_flags, PW_OVERHEAD | PW_FLOOR_ITEMS);
605 handle_stuff(player_ptr);
606 return target_who != 0;