OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / spell-class / spells-mirror-master.cpp
1 /*!
2  * @brief 鏡使いの鏡魔法効果処理
3  * @date 2022/03/07
4  * @author Hourier
5  * @todo 作りかけの部分複数あり
6  */
7
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"
43 #include <algorithm>
44 #include <map>
45
46 SpellsMirrorMaster::SpellsMirrorMaster(PlayerType *player_ptr)
47     : player_ptr(player_ptr)
48 {
49 }
50
51 void SpellsMirrorMaster::remove_mirror(int y, int x)
52 {
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);
56     g_ptr->mimic = 0;
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);
61         }
62
63         if (g_ptr->has_monster()) {
64             update_monster(this->player_ptr, g_ptr->m_idx, false);
65         }
66
67         update_local_illumination(this->player_ptr, y, x);
68     }
69
70     note_spot(this->player_ptr, y, x);
71     lite_spot(this->player_ptr, y, x);
72 }
73
74 /*!
75  * @brief 全鏡の消去 / Remove all mirrors in this floor
76  * @param player_ptr プレイヤーへの参照ポインタ
77  * @param explode 爆発処理を伴うならばTRUE
78  */
79 void SpellsMirrorMaster::remove_all_mirrors(bool explode)
80 {
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()) {
85                 continue;
86             }
87
88             this->remove_mirror(y, x);
89             if (!explode) {
90                 continue;
91             }
92
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);
95         }
96     }
97 }
98
99 /*!
100  * @brief 鏡抜け処理のメインルーチン /
101  * Mirror Master's Dimension Door
102  * @param player_ptr プレイヤーへの参照ポインタ
103  * @return ターンを消費した場合TRUEを返す
104  */
105 bool SpellsMirrorMaster::mirror_tunnel()
106 {
107     int x;
108     int y;
109     if (!tgt_pt(this->player_ptr, &x, &y)) {
110         return false;
111     }
112
113     if (exe_dimension_door(this->player_ptr, x, y)) {
114         return true;
115     }
116
117     msg_print(_("鏡の世界をうまく通れなかった!", "You could not enter the mirror!"));
118     return true;
119 }
120
121 /*!
122  * @brief 鏡設置処理
123  * @return 実際に設置が行われた場合TRUEを返す
124  */
125 bool SpellsMirrorMaster::place_mirror()
126 {
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."));
132         return false;
133     }
134
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;
139
140     /* Turn on the light */
141     set_bits(g_ptr->info, CAVE_GLOW);
142
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);
146     return true;
147 }
148
149 /*!
150  * @brief 静水
151  * @param player_ptr プレイヤーへの参照ポインタ
152  * @return ペットを操っている場合を除きTRUE
153  */
154 bool SpellsMirrorMaster::mirror_concentration()
155 {
156     if (total_friends) {
157         msg_print(_("今はペットを操ることに集中していないと。", "Your pets demand all of your attention."));
158         return false;
159     }
160
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!"));
163         return true;
164     }
165
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;
171     }
172
173     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
174     return true;
175 }
176
177 /*!
178  * @brief 鏡魔法「鏡の封印」の効果処理
179  * @param dam ダメージ量
180  * @return 効果があったらTRUEを返す
181  */
182 void SpellsMirrorMaster::seal_of_mirror(const int dam)
183 {
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()) {
189                 continue;
190             }
191
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)) {
194                 continue;
195             }
196
197             if (!g_ref.has_monster()) {
198                 this->remove_mirror(y, x);
199             }
200         }
201     }
202 }
203
204 void SpellsMirrorMaster::seeker_ray(int dir, int dam)
205 {
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);
208 }
209
210 void SpellsMirrorMaster::super_ray(int dir, int dam)
211 {
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);
214 }
215
216 /*!
217  * @brief 配置した鏡リストの次を取得する
218  * @param pos_current 現在の鏡位置
219  * @return 次の鏡位置
220  */
221 Pos2D SpellsMirrorMaster::get_next_mirror_position(const Pos2D &pos_current) const
222 {
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);
230             }
231         }
232     }
233
234     if (!mirror_positions.empty()) {
235         return rand_choice(mirror_positions);
236     }
237
238     while (true) {
239         const Pos2D next_mirror(pos_current.y + randint0(5) - 2, pos_current.x + randint0(5) - 2);
240         if (next_mirror != pos_current) {
241             return next_mirror;
242         }
243     }
244 }
245
246 void SpellsMirrorMaster::project_seeker_ray(int target_x, int target_y, int dam)
247 {
248     constexpr auto typ = AttributeType::SEEKER;
249     BIT_FLAGS flag = PROJECT_BEAM | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM | PROJECT_THRU | PROJECT_MIRROR;
250     rakubadam_p = 0;
251     rakubadam_m = 0;
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;
255
256     ProjectResult res;
257
258     auto x1 = this->player_ptr->x;
259     auto y1 = this->player_ptr->y;
260
261     auto y2 = target_y;
262     auto x2 = target_x;
263
264     if ((x1 == x2) && (y1 == y2)) {
265         reset_bits(flag, PROJECT_THRU);
266     }
267
268     /* Calculate the projection path */
269     handle_stuff(this->player_ptr);
270
271     project_m_n = 0;
272     project_m_x = 0;
273     project_m_y = 0;
274     auto visual = false;
275     const auto max_range = AngbandSystem::get_instance().get_max_range();
276     while (true) {
277         ProjectionPath path_g(this->player_ptr, (project_length ? project_length : max_range), { y1, x1 }, { y2, x2 }, flag);
278
279         if (path_g.path_num() == 0) {
280             break;
281         }
282
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;
286
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);
291                     term_fresh();
292                     term_xtra(TERM_XTRA_DELAY, delay_factor);
293                     lite_spot(this->player_ptr, ny, nx);
294                     term_fresh();
295
296                     print_bolt_pict(this->player_ptr, ny, nx, ny, nx, typ);
297
298                     visual = true;
299                 } else if (visual) {
300                     term_xtra(TERM_XTRA_DELAY, delay_factor);
301                 }
302             }
303
304             if (affect_item(this->player_ptr, 0, 0, ny, nx, dam, typ)) {
305                 res.notice = true;
306             }
307         }
308
309         for (const auto &[py, px] : path_g) {
310             if (affect_monster(this->player_ptr, 0, 0, py, px, dam, typ, flag, true)) {
311                 res.notice = true;
312             }
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);
318                 }
319                 health_track(this->player_ptr, grid.m_idx);
320             }
321
322             (void)affect_feature(this->player_ptr, 0, 0, py, px, dam, typ);
323         }
324
325         const auto &[y, x] = path_g.back();
326
327         if (!floor.get_grid({ y, x }).is_mirror()) {
328             break;
329         }
330
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 });
335         y2 = pos.y;
336         x2 = pos.x;
337         y1 = y;
338         x1 = x;
339     }
340 }
341
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 &center)
344 {
345     if (delay_factor <= 0) {
346         return;
347     }
348
349     constexpr auto typ = AttributeType::SUPER_RAY;
350
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(); });
355
356     // スーパーレイの描画を行った座標のリスト。スーパーレイの全ての描画完了後に描画を消去するのに使用する。
357     std::vector<Pos2D> drawn_pos_list;
358
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;
363
364         for (const auto &it : pos_list) {
365             const auto &pos = (n == 1) ? center : *std::next(it, -1);
366             const auto &pos_new = *it;
367
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);
371             }
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);
376                 }
377             }
378         }
379         term_fresh();
380         term_xtra(TERM_XTRA_DELAY, delay_factor);
381
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);
386             }
387         }
388     }
389
390     term_fresh();
391     term_xtra(TERM_XTRA_DELAY, delay_factor);
392
393     for (const auto &pos : drawn_pos_list) {
394         lite_spot(player_ptr, pos.y, pos.x);
395     }
396 }
397
398 static bool activate_super_ray_effect(PlayerType *player_ptr, int y, int x, int dam, BIT_FLAGS flag)
399 {
400     constexpr auto typ = AttributeType::SUPER_RAY;
401     auto notice = false;
402
403     (void)affect_feature(player_ptr, 0, 0, y, x, dam, typ);
404
405     if (affect_item(player_ptr, 0, 0, y, x, dam, typ)) {
406         notice = true;
407     }
408
409     (void)affect_monster(player_ptr, 0, 0, y, x, dam, typ, flag, true);
410
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);
417         }
418         health_track(player_ptr, g_ptr->m_idx);
419     }
420
421     return notice;
422 }
423
424 void SpellsMirrorMaster::project_super_ray(int target_x, int target_y, int dam)
425 {
426     POSITION y1;
427     POSITION x1;
428     POSITION y2;
429     POSITION x2;
430     constexpr auto typ = AttributeType::SUPER_RAY;
431     BIT_FLAGS flag = PROJECT_BEAM | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM | PROJECT_THRU | PROJECT_MIRROR;
432     rakubadam_p = 0;
433     rakubadam_m = 0;
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;
437
438     ProjectResult res;
439
440     x1 = this->player_ptr->x;
441     y1 = this->player_ptr->y;
442
443     y2 = target_y;
444     x2 = target_x;
445
446     if ((x1 == x2) && (y1 == y2)) {
447         flag &= ~(PROJECT_THRU);
448     }
449
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);
455
456     if (path_g.path_num() == 0) {
457         return;
458     }
459
460     project_m_n = 0;
461     project_m_x = 0;
462     project_m_y = 0;
463     auto oy = y1;
464     auto ox = x1;
465     auto visual = false;
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);
472                 term_fresh();
473                 term_xtra(TERM_XTRA_DELAY, delay_factor);
474                 lite_spot(this->player_ptr, ny, nx);
475                 term_fresh();
476
477                 print_bolt_pict(this->player_ptr, ny, nx, ny, nx, typ);
478
479                 drawn_pos_list.emplace_back(ny, nx);
480                 visual = true;
481             } else if (visual) {
482                 term_xtra(TERM_XTRA_DELAY, delay_factor);
483             }
484         }
485
486         if (!cave_has_flag_bold(&floor, ny, nx, TerrainCharacteristics::PROJECT)) {
487             break;
488         }
489         oy = ny;
490         ox = nx;
491     }
492
493     for (const auto &[y, x] : drawn_pos_list) {
494         lite_spot(this->player_ptr, y, x);
495     }
496
497     {
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);
503
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);
508             }
509         }
510     }
511
512     for (const auto &[py, px] : path_g) {
513         res.notice |= activate_super_ray_effect(this->player_ptr, py, px, dam, flag);
514     }
515
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);
524         }
525     }
526
527     draw_super_ray_pict(this->player_ptr, pos_list_map, second_path_g_list, path_g.back());
528
529     for (auto &&[n, pos_list] : pos_list_map) {
530         rand_shuffle(pos_list.begin(), pos_list.end());
531
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);
535         }
536     }
537 }