8 #include "spell-class/spells-mirror-master.h"
9 #include "core/stuff-handler.h"
10 #include "dungeon/dungeon-flag-types.h"
11 #include "effect/attribute-types.h"
12 #include "effect/effect-characteristics.h"
13 #include "effect/effect-feature.h"
14 #include "effect/effect-item.h"
15 #include "effect/effect-monster.h"
16 #include "effect/effect-processor.h"
17 #include "effect/spells-effect-util.h"
18 #include "floor/cave.h"
19 #include "floor/geometry.h"
20 #include "game-option/map-screen-options.h"
21 #include "game-option/special-options.h"
22 #include "grid/feature.h"
23 #include "grid/grid.h"
24 #include "io/cursor.h"
25 #include "io/screen-util.h"
26 #include "monster/monster-update.h"
27 #include "pet/pet-util.h"
28 #include "spell-kind/spells-teleport.h"
29 #include "system/angband-system.h"
30 #include "system/dungeon-info.h"
31 #include "system/floor-type-definition.h"
32 #include "system/grid-type-definition.h"
33 #include "system/monster-entity.h"
34 #include "system/player-type-definition.h"
35 #include "system/redrawing-flags-updater.h"
36 #include "system/terrain-type-definition.h"
37 #include "target/grid-selector.h"
38 #include "target/projection-path-calculator.h"
39 #include "target/target-checker.h"
40 #include "timed-effect/timed-effects.h"
41 #include "util/bit-flags-calculator.h"
42 #include "view/display-messages.h"
46 SpellsMirrorMaster::SpellsMirrorMaster(PlayerType *player_ptr)
47 : player_ptr(player_ptr)
51 void SpellsMirrorMaster::remove_mirror(int y, int x)
53 auto &floor = *this->player_ptr->current_floor_ptr;
54 auto *g_ptr = &floor.grid_array[y][x];
55 reset_bits(g_ptr->info, CAVE_OBJECT);
57 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
58 reset_bits(g_ptr->info, CAVE_GLOW);
59 if (!view_torch_grids) {
60 reset_bits(g_ptr->info, CAVE_MARK);
63 if (g_ptr->has_monster()) {
64 update_monster(this->player_ptr, g_ptr->m_idx, false);
67 update_local_illumination(this->player_ptr, y, x);
70 note_spot(this->player_ptr, y, x);
71 lite_spot(this->player_ptr, y, x);
75 * @brief 全鏡の消去 / Remove all mirrors in this floor
76 * @param player_ptr プレイヤーへの参照ポインタ
77 * @param explode 爆発処理を伴うならばTRUE
79 void SpellsMirrorMaster::remove_all_mirrors(bool explode)
81 const auto *floor_ptr = this->player_ptr->current_floor_ptr;
82 for (auto x = 0; x < floor_ptr->width; x++) {
83 for (auto y = 0; y < floor_ptr->height; y++) {
84 if (!floor_ptr->grid_array[y][x].is_mirror()) {
88 this->remove_mirror(y, x);
93 constexpr BIT_FLAGS projection = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI;
94 project(this->player_ptr, 0, 2, y, x, this->player_ptr->lev / 2 + 5, AttributeType::SHARDS, projection);
100 * @brief 鏡抜け処理のメインルーチン /
101 * Mirror Master's Dimension Door
102 * @param player_ptr プレイヤーへの参照ポインタ
103 * @return ターンを消費した場合TRUEを返す
105 bool SpellsMirrorMaster::mirror_tunnel()
109 if (!tgt_pt(this->player_ptr, &x, &y)) {
113 if (exe_dimension_door(this->player_ptr, x, y)) {
117 msg_print(_("鏡の世界をうまく通れなかった!", "You could not enter the mirror!"));
123 * @return 実際に設置が行われた場合TRUEを返す
125 bool SpellsMirrorMaster::place_mirror()
127 auto y = this->player_ptr->y;
128 auto x = this->player_ptr->x;
129 auto *floor_ptr = this->player_ptr->current_floor_ptr;
130 if (!cave_clean_bold(floor_ptr, y, x)) {
131 msg_print(_("床上のアイテムが呪文を跳ね返した。", "The object resists the spell."));
135 /* Create a mirror */
136 auto *g_ptr = &floor_ptr->grid_array[y][x];
137 set_bits(g_ptr->info, CAVE_OBJECT);
138 g_ptr->mimic = feat_mirror;
140 /* Turn on the light */
141 set_bits(g_ptr->info, CAVE_GLOW);
143 note_spot(this->player_ptr, y, x);
144 lite_spot(this->player_ptr, y, x);
145 update_local_illumination(this->player_ptr, y, x);
151 * @param player_ptr プレイヤーへの参照ポインタ
152 * @return ペットを操っている場合を除きTRUE
154 bool SpellsMirrorMaster::mirror_concentration()
157 msg_print(_("今はペットを操ることに集中していないと。", "Your pets demand all of your attention."));
161 if (!this->player_ptr->current_floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].is_mirror()) {
162 msg_print(_("鏡の上でないと集中できない!", "There's no mirror here!"));
166 msg_print(_("少し頭がハッキリした。", "You feel your head clear a little."));
167 this->player_ptr->csp += (5 + this->player_ptr->lev * this->player_ptr->lev / 100);
168 if (this->player_ptr->csp >= this->player_ptr->msp) {
169 this->player_ptr->csp = this->player_ptr->msp;
170 this->player_ptr->csp_frac = 0;
173 RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
178 * @brief 鏡魔法「鏡の封印」の効果処理
180 * @return 効果があったらTRUEを返す
182 void SpellsMirrorMaster::seal_of_mirror(const int dam)
184 const auto *floor_ptr = this->player_ptr->current_floor_ptr;
185 for (auto x = 0; x < floor_ptr->width; x++) {
186 for (auto y = 0; y < floor_ptr->height; y++) {
187 const auto &g_ref = floor_ptr->grid_array[y][x];
188 if (!g_ref.is_mirror()) {
192 constexpr BIT_FLAGS flags = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP;
193 if (!affect_monster(this->player_ptr, 0, 0, y, x, dam, AttributeType::GENOCIDE, flags, true)) {
197 if (!g_ref.has_monster()) {
198 this->remove_mirror(y, x);
204 void SpellsMirrorMaster::seeker_ray(int dir, int dam)
206 const auto pos = ((dir == 5) && target_okay(this->player_ptr)) ? Pos2D(target_row, target_col) : this->player_ptr->get_neighbor(dir);
207 project_seeker_ray(pos.x, pos.y, dam);
210 void SpellsMirrorMaster::super_ray(int dir, int dam)
212 const auto pos = ((dir == 5) && target_okay(this->player_ptr)) ? Pos2D(target_row, target_col) : this->player_ptr->get_neighbor(dir);
213 project_super_ray(pos.x, pos.y, dam);
217 * @brief 配置した鏡リストの次を取得する
218 * @param pos_current 現在の鏡位置
221 Pos2D SpellsMirrorMaster::get_next_mirror_position(const Pos2D &pos_current) const
223 std::vector<Pos2D> mirror_positions;
224 const auto &floor = *this->player_ptr->current_floor_ptr;
225 for (auto x = 0; x < floor.width; x++) {
226 for (auto y = 0; y < floor.height; y++) {
227 const Pos2D pos(y, x);
228 if (floor.get_grid(pos).is_mirror()) {
229 mirror_positions.push_back(pos);
234 if (!mirror_positions.empty()) {
235 return rand_choice(mirror_positions);
239 const Pos2D next_mirror(pos_current.y + randint0(5) - 2, pos_current.x + randint0(5) - 2);
240 if (next_mirror != pos_current) {
246 void SpellsMirrorMaster::project_seeker_ray(int target_x, int target_y, int dam)
248 constexpr auto typ = AttributeType::SEEKER;
249 BIT_FLAGS flag = PROJECT_BEAM | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM | PROJECT_THRU | PROJECT_MIRROR;
252 monster_target_y = this->player_ptr->y;
253 monster_target_x = this->player_ptr->x;
254 auto &floor = *this->player_ptr->current_floor_ptr;
258 auto x1 = this->player_ptr->x;
259 auto y1 = this->player_ptr->y;
264 if ((x1 == x2) && (y1 == y2)) {
265 reset_bits(flag, PROJECT_THRU);
268 /* Calculate the projection path */
269 handle_stuff(this->player_ptr);
275 const auto max_range = AngbandSystem::get_instance().get_max_range();
277 ProjectionPath path_g(this->player_ptr, (project_length ? project_length : max_range), { y1, x1 }, { y2, x2 }, flag);
279 if (path_g.path_num() == 0) {
283 for (auto path_g_ite = path_g.begin(); path_g_ite != path_g.end(); path_g_ite++) {
284 const auto &[oy, ox] = *(path_g_ite == path_g.begin() ? path_g.begin() : path_g_ite - 1);
285 const auto &[ny, nx] = *path_g_ite;
287 if (delay_factor > 0 && !this->player_ptr->effects()->blindness().is_blind()) {
288 if (panel_contains(ny, nx) && floor.has_los({ ny, nx })) {
289 print_bolt_pict(this->player_ptr, oy, ox, ny, nx, typ);
290 move_cursor_relative(ny, nx);
292 term_xtra(TERM_XTRA_DELAY, delay_factor);
293 lite_spot(this->player_ptr, ny, nx);
296 print_bolt_pict(this->player_ptr, ny, nx, ny, nx, typ);
300 term_xtra(TERM_XTRA_DELAY, delay_factor);
304 if (affect_item(this->player_ptr, 0, 0, ny, nx, dam, typ)) {
309 for (const auto &[py, px] : path_g) {
310 if (affect_monster(this->player_ptr, 0, 0, py, px, dam, typ, flag, true)) {
313 const auto &grid = floor.grid_array[project_m_y][project_m_x];
314 const auto &monster = floor.m_list[grid.m_idx];
315 if (project_m_n == 1 && grid.has_monster() && monster.ml) {
316 if (!this->player_ptr->effects()->hallucination().is_hallucinated()) {
317 monster_race_track(this->player_ptr, monster.ap_r_idx);
319 health_track(this->player_ptr, grid.m_idx);
322 (void)affect_feature(this->player_ptr, 0, 0, py, px, dam, typ);
325 const auto &[y, x] = path_g.back();
327 if (!floor.get_grid({ y, x }).is_mirror()) {
331 monster_target_y = y;
332 monster_target_x = x;
333 this->remove_mirror(y, x);
334 const auto pos = this->get_next_mirror_position({ y, x });
342 static void draw_super_ray_pict(PlayerType *player_ptr, const std::map<int, std::vector<ProjectionPath::pp_const_iterator>> &pos_list_map,
343 const std::vector<ProjectionPath> &second_path_g_list, const Pos2D ¢er)
345 if (delay_factor <= 0) {
349 constexpr auto typ = AttributeType::SUPER_RAY;
351 // 8方向のスーパーレイの軌道上の最後の到達点の座標のリスト
352 std::vector<Pos2D> last_pos_list;
353 std::transform(second_path_g_list.begin(), second_path_g_list.end(), std::back_inserter(last_pos_list),
354 [](const auto &path_g) { return path_g.back(); });
356 // スーパーレイの描画を行った座標のリスト。スーパーレイの全ての描画完了後に描画を消去するのに使用する。
357 std::vector<Pos2D> drawn_pos_list;
359 const auto &floor = *player_ptr->current_floor_ptr;
360 for (const auto &[n, pos_list] : pos_list_map) {
361 // スーパーレイの最終到達点の座標の描画を行った座標のリスト。最終到達点の描画を '*' で上書きするのに使用する。
362 std::vector<Pos2D> drawn_last_pos_list;
364 for (const auto &it : pos_list) {
365 const auto &pos = (n == 1) ? center : *std::next(it, -1);
366 const auto &pos_new = *it;
368 if (panel_contains(pos.y, pos.x) && floor.has_los(pos)) {
369 print_bolt_pict(player_ptr, pos.y, pos.x, pos.y, pos.x, typ);
370 drawn_pos_list.push_back(pos);
372 if (panel_contains(pos_new.y, pos_new.x) && floor.has_los(pos_new)) {
373 print_bolt_pict(player_ptr, pos.y, pos.x, pos_new.y, pos_new.x, typ);
374 if (std::find(last_pos_list.begin(), last_pos_list.end(), *it) != last_pos_list.end()) {
375 drawn_last_pos_list.push_back(pos_new);
380 term_xtra(TERM_XTRA_DELAY, delay_factor);
382 for (const auto &pos : drawn_last_pos_list) {
383 if (panel_contains(pos.y, pos.x) && floor.has_los(pos)) {
384 print_bolt_pict(player_ptr, pos.y, pos.x, pos.y, pos.x, typ);
385 drawn_pos_list.push_back(pos);
391 term_xtra(TERM_XTRA_DELAY, delay_factor);
393 for (const auto &pos : drawn_pos_list) {
394 lite_spot(player_ptr, pos.y, pos.x);
398 static bool activate_super_ray_effect(PlayerType *player_ptr, int y, int x, int dam, BIT_FLAGS flag)
400 constexpr auto typ = AttributeType::SUPER_RAY;
403 (void)affect_feature(player_ptr, 0, 0, y, x, dam, typ);
405 if (affect_item(player_ptr, 0, 0, y, x, dam, typ)) {
409 (void)affect_monster(player_ptr, 0, 0, y, x, dam, typ, flag, true);
411 const auto *floor_ptr = player_ptr->current_floor_ptr;
412 const auto *g_ptr = &floor_ptr->grid_array[project_m_y][project_m_x];
413 const auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
414 if (project_m_n == 1 && g_ptr->has_monster() && m_ptr->ml) {
415 if (!player_ptr->effects()->hallucination().is_hallucinated()) {
416 monster_race_track(player_ptr, m_ptr->ap_r_idx);
418 health_track(player_ptr, g_ptr->m_idx);
424 void SpellsMirrorMaster::project_super_ray(int target_x, int target_y, int dam)
430 constexpr auto typ = AttributeType::SUPER_RAY;
431 BIT_FLAGS flag = PROJECT_BEAM | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM | PROJECT_THRU | PROJECT_MIRROR;
434 monster_target_y = this->player_ptr->y;
435 monster_target_x = this->player_ptr->x;
436 const auto &floor = *this->player_ptr->current_floor_ptr;
440 x1 = this->player_ptr->x;
441 y1 = this->player_ptr->y;
446 if ((x1 == x2) && (y1 == y2)) {
447 flag &= ~(PROJECT_THRU);
450 /* Calculate the projection path */
451 const auto &system = AngbandSystem::get_instance();
452 ProjectionPath path_g(this->player_ptr, (project_length ? project_length : system.get_max_range()), { y1, x1 }, { y2, x2 }, flag);
453 std::vector<ProjectionPath> second_path_g_list;
454 handle_stuff(this->player_ptr);
456 if (path_g.path_num() == 0) {
466 std::vector<std::pair<int, int>> drawn_pos_list;
467 for (const auto &[ny, nx] : path_g) {
468 if (delay_factor > 0) {
469 if (panel_contains(ny, nx) && floor.has_los({ ny, nx })) {
470 print_bolt_pict(this->player_ptr, oy, ox, ny, nx, typ);
471 move_cursor_relative(ny, nx);
473 term_xtra(TERM_XTRA_DELAY, delay_factor);
474 lite_spot(this->player_ptr, ny, nx);
477 print_bolt_pict(this->player_ptr, ny, nx, ny, nx, typ);
479 drawn_pos_list.emplace_back(ny, nx);
482 term_xtra(TERM_XTRA_DELAY, delay_factor);
486 if (!cave_has_flag_bold(&floor, ny, nx, TerrainCharacteristics::PROJECT)) {
493 for (const auto &[y, x] : drawn_pos_list) {
494 lite_spot(this->player_ptr, y, x);
498 const auto &[y, x] = path_g.back();
499 if (floor.get_grid({ y, x }).is_mirror()) {
500 this->remove_mirror(y, x);
501 auto project_flag = flag;
502 reset_bits(project_flag, PROJECT_MIRROR);
504 const auto length = project_length ? project_length : system.get_max_range();
505 for (const auto &dd : CCW_DD) {
506 const Pos2D pos(y, x);
507 second_path_g_list.emplace_back(this->player_ptr, length, pos, pos + dd, project_flag);
512 for (const auto &[py, px] : path_g) {
513 res.notice |= activate_super_ray_effect(this->player_ptr, py, px, dam, flag);
516 // 起点の鏡からの距離 → 8方向へのスーパーレイの軌道上のその距離にある座標のイテレータのリストの map
517 std::map<int, std::vector<ProjectionPath::pp_const_iterator>> pos_list_map;
518 for (const auto &second_path_g : second_path_g_list) {
519 for (auto it = second_path_g.begin(); it != second_path_g.end(); ++it) {
520 const auto &[o_y, o_x] = path_g.back();
521 const auto &[y, x] = *it;
522 auto d = distance(o_y, o_x, y, x);
523 pos_list_map[d].push_back(it);
527 draw_super_ray_pict(this->player_ptr, pos_list_map, second_path_g_list, path_g.back());
529 for (auto &&[n, pos_list] : pos_list_map) {
530 rand_shuffle(pos_list.begin(), pos_list.end());
532 for (const auto &it : pos_list) {
533 const auto &[y, x] = *it;
534 res.notice |= activate_super_ray_effect(player_ptr, y, x, dam, flag);