1 #include "target/target-preparation.h"
2 #include "floor/cave.h"
3 #include "game-option/input-options.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"
30 * Determine is a monster makes a reasonable target
32 * The concept of "targeting" was stolen from "Morgul" (?)
34 * The player can target any location, or any "target-able" monster.
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.
40 * Future versions may restrict the ability to target "trappers"
41 * and "mimics", but the semantics is a little bit weird.
43 bool target_able(PlayerType *player_ptr, MONSTER_IDX m_idx)
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()) {
51 if (player_ptr->effects()->hallucination()->is_hallucinated()) {
59 if (player_ptr->riding && (player_ptr->riding == m_idx)) {
63 if (!projectable(player_ptr, player_ptr->y, player_ptr->x, m_ptr->fy, m_ptr->fx)) {
71 * Determine if a given location is "interesting"
73 static bool target_set_accept(PlayerType *player_ptr, const Pos2D &pos)
75 auto &floor = *player_ptr->current_floor_ptr;
76 if (!(in_bounds(&floor, pos.y, pos.x))) {
80 if (player_ptr->is_located_at(pos)) {
84 if (player_ptr->effects()->hallucination()->is_hallucinated()) {
88 const auto &grid = floor.get_grid(pos);
89 if (grid.has_monster()) {
90 auto &monster = floor.m_list[grid.m_idx];
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)) {
103 if (!grid.is_mark()) {
107 if (grid.is_object()) {
111 return grid.get_terrain_mimic().flags.has(TerrainCharacteristics::NOTICE);
115 * @brief "interesting" な座標たちを ys, xs に返す。
117 * @param ys y座標たちを格納する配列 (POSITION 型)
118 * @param xs x座標たちを格納する配列 (POSITION 型)
121 * ys, xs は処理開始時にクリアされる。
123 void target_set_prepare(PlayerType *player_ptr, std::vector<POSITION> &ys, std::vector<POSITION> &xs, const BIT_FLAGS mode)
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);
133 min_hgt = panel_row_min;
134 max_hgt = panel_row_max;
135 min_wid = panel_col_min;
136 max_wid = panel_col_max;
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)) {
149 const auto &grid = floor.get_grid(pos);
150 if ((mode & (TARGET_KILL)) && !target_able(player_ptr, grid.m_idx)) {
154 const auto &monster = floor.m_list[grid.m_idx];
155 if ((mode & (TARGET_KILL)) && !target_pet && monster.is_pet()) {
164 if (mode & (TARGET_KILL)) {
165 ang_sort(player_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_distance, ang_sort_swap_position);
167 ang_sort(player_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_importance, ang_sort_swap_position);
170 // 乗っているモンスターがターゲットリストの先頭にならないようにする調整。
172 if (player_ptr->riding == 0 || !target_pet || (size(ys) <= 1) || !(mode & (TARGET_KILL))) {
177 std::swap(ys[0], ys[1]);
178 std::swap(xs[0], xs[1]);
181 void target_sensing_monsters_prepare(PlayerType *player_ptr, std::vector<MONSTER_IDX> &monster_list)
183 monster_list.clear();
186 if (player_ptr->effects()->hallucination()->is_hallucinated()) {
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()) {
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)) {
201 monster_list.push_back(i);
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];
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);
215 /* Shadowers first (あやしい影) */
216 if (monster1.mflag2.has(MonsterConstantFlagType::KAGE) != monster2.mflag2.has(MonsterConstantFlagType::KAGE)) {
217 return monster1.mflag2.has(MonsterConstantFlagType::KAGE);
220 /* Unknown monsters first */
221 if ((monrace1.r_tkills == 0) != (monrace2.r_tkills == 0)) {
222 return monrace1.r_tkills == 0;
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;
230 /* Sort by index if all conditions are same */
231 return monster1.ap_r_idx > monster2.ap_r_idx;
234 std::sort(monster_list.begin(), monster_list.end(), comp_importance);
238 * @brief プレイヤーのペットの一覧を得る
240 * プレイヤーのペットのモンスターIDのリストを取得する。
241 * リストは以下の通り、重要なペットと考えられる順にソートされる。
249 * @return ペットのモンスターIDのリスト
251 std::vector<MONSTER_IDX> target_pets_prepare(PlayerType *player_ptr)
253 std::vector<MONSTER_IDX> pets;
254 const auto &floor = *player_ptr->current_floor_ptr;
256 for (short i = 1; i < floor.m_max; ++i) {
257 const auto &monster = floor.m_list[i];
259 if (monster.is_valid() && monster.is_pet()) {
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];
270 if ((riding_idx == idx1) != (riding_idx == idx2)) {
271 return riding_idx == idx1;
274 if (monster1.is_named_pet() != monster2.is_named_pet()) {
275 return monster1.is_named_pet();
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);
282 if (ap_monrace1.r_tkills && ap_monrace2.r_tkills && (ap_monrace1.level != ap_monrace2.level)) {
283 return ap_monrace1.level > ap_monrace2.level;
289 std::sort(pets.begin(), pets.end(), comp_importance);