OSDN Git Service

Merge pull request #3814 from Slimebreath6078/feature/Add_Laffey_II
[hengbandforosx/hengbandosx.git] / src / target / target-preparation.cpp
1 #include "target/target-preparation.h"
2 #include "floor/cave.h"
3 #include "game-option/input-options.h"
4 #include "grid/grid.h"
5 #include "monster-race/monster-race.h"
6 #include "monster/monster-flag-types.h"
7 #include "monster/monster-info.h"
8 #include "monster/monster-status.h"
9 #include "object/object-mark-types.h"
10 #include "system/angband-system.h"
11 #include "system/floor-type-definition.h"
12 #include "system/grid-type-definition.h"
13 #include "system/item-entity.h"
14 #include "system/monster-entity.h"
15 #include "system/monster-race-info.h"
16 #include "system/player-type-definition.h"
17 #include "system/terrain-type-definition.h"
18 #include "target/projection-path-calculator.h"
19 #include "target/target-types.h"
20 #include "timed-effect/player-hallucination.h"
21 #include "timed-effect/timed-effects.h"
22 #include "util/bit-flags-calculator.h"
23 #include "util/sort.h"
24 #include "window/main-window-util.h"
25 #include <algorithm>
26 #include <utility>
27 #include <vector>
28
29 /*
30  * Determine is a monster makes a reasonable target
31  *
32  * The concept of "targeting" was stolen from "Morgul" (?)
33  *
34  * The player can target any location, or any "target-able" monster.
35  *
36  * Currently, a monster is "target_able" if it is visible, and if
37  * the player can hit it with a projection, and the player is not
38  * hallucinating.  This allows use of "use closest target" macros.
39  *
40  * Future versions may restrict the ability to target "trappers"
41  * and "mimics", but the semantics is a little bit weird.
42  */
43 bool target_able(PlayerType *player_ptr, MONSTER_IDX m_idx)
44 {
45     auto *floor_ptr = player_ptr->current_floor_ptr;
46     auto *m_ptr = &floor_ptr->m_list[m_idx];
47     if (!m_ptr->is_valid()) {
48         return false;
49     }
50
51     if (player_ptr->effects()->hallucination()->is_hallucinated()) {
52         return false;
53     }
54
55     if (!m_ptr->ml) {
56         return false;
57     }
58
59     if (player_ptr->riding && (player_ptr->riding == m_idx)) {
60         return true;
61     }
62
63     if (!projectable(player_ptr, player_ptr->y, player_ptr->x, m_ptr->fy, m_ptr->fx)) {
64         return false;
65     }
66
67     return true;
68 }
69
70 /*
71  * Determine if a given location is "interesting"
72  */
73 static bool target_set_accept(PlayerType *player_ptr, const Pos2D &pos)
74 {
75     auto &floor = *player_ptr->current_floor_ptr;
76     if (!(in_bounds(&floor, pos.y, pos.x))) {
77         return false;
78     }
79
80     if (player_ptr->is_located_at(pos)) {
81         return true;
82     }
83
84     if (player_ptr->effects()->hallucination()->is_hallucinated()) {
85         return false;
86     }
87
88     const auto &grid = floor.get_grid(pos);
89     if (grid.has_monster()) {
90         auto &monster = floor.m_list[grid.m_idx];
91         if (monster.ml) {
92             return true;
93         }
94     }
95
96     for (const auto this_o_idx : grid.o_idx_list) {
97         const auto &item = floor.o_list[this_o_idx];
98         if (item.marked.has(OmType::FOUND)) {
99             return true;
100         }
101     }
102
103     if (!grid.is_mark()) {
104         return false;
105     }
106
107     if (grid.is_object()) {
108         return true;
109     }
110
111     return grid.get_terrain_mimic().flags.has(TerrainCharacteristics::NOTICE);
112 }
113
114 /*!
115  * @brief "interesting" な座標たちを ys, xs に返す。
116  * @param player_ptr
117  * @param ys y座標たちを格納する配列 (POSITION 型)
118  * @param xs x座標たちを格納する配列 (POSITION 型)
119  * @param mode
120  *
121  * ys, xs は処理開始時にクリアされる。
122  */
123 void target_set_prepare(PlayerType *player_ptr, std::vector<POSITION> &ys, std::vector<POSITION> &xs, const BIT_FLAGS mode)
124 {
125     POSITION min_hgt, max_hgt, min_wid, max_wid;
126     if (mode & TARGET_KILL) {
127         const auto max_range = AngbandSystem::get_instance().get_max_range();
128         min_hgt = std::max((player_ptr->y - max_range), 0);
129         max_hgt = std::min((player_ptr->y + max_range), player_ptr->current_floor_ptr->height - 1);
130         min_wid = std::max((player_ptr->x - max_range), 0);
131         max_wid = std::min((player_ptr->x + max_range), player_ptr->current_floor_ptr->width - 1);
132     } else {
133         min_hgt = panel_row_min;
134         max_hgt = panel_row_max;
135         min_wid = panel_col_min;
136         max_wid = panel_col_max;
137     }
138
139     ys.clear();
140     xs.clear();
141     const auto &floor = *player_ptr->current_floor_ptr;
142     for (auto y = min_hgt; y <= max_hgt; y++) {
143         for (auto x = min_wid; x <= max_wid; x++) {
144             const Pos2D pos(y, x);
145             if (!target_set_accept(player_ptr, pos)) {
146                 continue;
147             }
148
149             const auto &grid = floor.get_grid(pos);
150             if ((mode & (TARGET_KILL)) && !target_able(player_ptr, grid.m_idx)) {
151                 continue;
152             }
153
154             const auto &monster = floor.m_list[grid.m_idx];
155             if ((mode & (TARGET_KILL)) && !target_pet && monster.is_pet()) {
156                 continue;
157             }
158
159             ys.emplace_back(y);
160             xs.emplace_back(x);
161         }
162     }
163
164     if (mode & (TARGET_KILL)) {
165         ang_sort(player_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_distance, ang_sort_swap_position);
166     } else {
167         ang_sort(player_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_importance, ang_sort_swap_position);
168     }
169
170     // 乗っているモンスターがターゲットリストの先頭にならないようにする調整。
171
172     if (player_ptr->riding == 0 || !target_pet || (size(ys) <= 1) || !(mode & (TARGET_KILL))) {
173         return;
174     }
175
176     // 0 番目と 1 番目を入れ替える。
177     std::swap(ys[0], ys[1]);
178     std::swap(xs[0], xs[1]);
179 }
180
181 void target_sensing_monsters_prepare(PlayerType *player_ptr, std::vector<MONSTER_IDX> &monster_list)
182 {
183     monster_list.clear();
184
185     // 幻覚時は正常に感知できない
186     if (player_ptr->effects()->hallucination()->is_hallucinated()) {
187         return;
188     }
189
190     for (MONSTER_IDX i = 1; i < player_ptr->current_floor_ptr->m_max; i++) {
191         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
192         if (!m_ptr->is_valid() || !m_ptr->ml || m_ptr->is_pet()) {
193             continue;
194         }
195
196         // 感知魔法/スキルやESPで感知していない擬態モンスターはモンスター一覧に表示しない
197         if (m_ptr->is_mimicry() && m_ptr->mflag2.has_none_of({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW }) && m_ptr->mflag.has_not(MonsterTemporaryFlagType::ESP)) {
198             continue;
199         }
200
201         monster_list.push_back(i);
202     }
203
204     auto comp_importance = [floor_ptr = player_ptr->current_floor_ptr](MONSTER_IDX idx1, MONSTER_IDX idx2) {
205         const auto &monster1 = floor_ptr->m_list[idx1];
206         const auto &monster2 = floor_ptr->m_list[idx2];
207         const auto &monrace1 = monraces_info[monster1.ap_r_idx];
208         const auto &monrace2 = monraces_info[monster2.ap_r_idx];
209
210         /* Unique monsters first */
211         if (monrace1.kind_flags.has(MonsterKindType::UNIQUE) != monrace2.kind_flags.has(MonsterKindType::UNIQUE)) {
212             return monrace1.kind_flags.has(MonsterKindType::UNIQUE);
213         }
214
215         /* Shadowers first (あやしい影) */
216         if (monster1.mflag2.has(MonsterConstantFlagType::KAGE) != monster2.mflag2.has(MonsterConstantFlagType::KAGE)) {
217             return monster1.mflag2.has(MonsterConstantFlagType::KAGE);
218         }
219
220         /* Unknown monsters first */
221         if ((monrace1.r_tkills == 0) != (monrace2.r_tkills == 0)) {
222             return monrace1.r_tkills == 0;
223         }
224
225         /* Higher level monsters first (if known) */
226         if (monrace1.r_tkills && monrace2.r_tkills && monrace1.level != monrace2.level) {
227             return monrace1.level > monrace2.level;
228         }
229
230         /* Sort by index if all conditions are same */
231         return monster1.ap_r_idx > monster2.ap_r_idx;
232     };
233
234     std::sort(monster_list.begin(), monster_list.end(), comp_importance);
235 }
236
237 /*!
238  * @brief プレイヤーのペットの一覧を得る
239  *
240  * プレイヤーのペットのモンスターIDのリストを取得する。
241  * リストは以下の通り、重要なペットと考えられる順にソートされる。
242  *
243  * - 乗馬している
244  * - 名前をつけている
245  * - ユニークモンスター
246  * - LV順(降順)
247  * - モンスターID順(昇順)
248  *
249  * @return ペットのモンスターIDのリスト
250  */
251 std::vector<MONSTER_IDX> target_pets_prepare(PlayerType *player_ptr)
252 {
253     std::vector<MONSTER_IDX> pets;
254     const auto &floor = *player_ptr->current_floor_ptr;
255
256     for (short i = 1; i < floor.m_max; ++i) {
257         const auto &monster = floor.m_list[i];
258
259         if (monster.is_valid() && monster.is_pet()) {
260             pets.push_back(i);
261         }
262     }
263
264     auto comp_importance = [riding_idx = player_ptr->riding, &floor](MONSTER_IDX idx1, MONSTER_IDX idx2) {
265         const auto &monster1 = floor.m_list[idx1];
266         const auto &monster2 = floor.m_list[idx2];
267         const auto &ap_monrace1 = monraces_info[monster1.ap_r_idx];
268         const auto &ap_monrace2 = monraces_info[monster2.ap_r_idx];
269
270         if ((riding_idx == idx1) != (riding_idx == idx2)) {
271             return riding_idx == idx1;
272         }
273
274         if (monster1.is_named_pet() != monster2.is_named_pet()) {
275             return monster1.is_named_pet();
276         }
277
278         if (ap_monrace1.kind_flags.has(MonsterKindType::UNIQUE) != ap_monrace2.kind_flags.has(MonsterKindType::UNIQUE)) {
279             return ap_monrace1.kind_flags.has(MonsterKindType::UNIQUE);
280         }
281
282         if (ap_monrace1.r_tkills && ap_monrace2.r_tkills && (ap_monrace1.level != ap_monrace2.level)) {
283             return ap_monrace1.level > ap_monrace2.level;
284         }
285
286         return idx1 < idx2;
287     };
288
289     std::sort(pets.begin(), pets.end(), comp_importance);
290
291     return pets;
292 }