OSDN Git Service

[Chore] UTF-8エンコーディングのファイルからBOMを削除
[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/dungeon-info.h"
30 #include "system/floor-type-definition.h"
31 #include "system/grid-type-definition.h"
32 #include "system/monster-entity.h"
33 #include "system/player-type-definition.h"
34 #include "system/redrawing-flags-updater.h"
35 #include "system/terrain-type-definition.h"
36 #include "target/grid-selector.h"
37 #include "target/projection-path-calculator.h"
38 #include "target/target-checker.h"
39 #include "timed-effect/player-blindness.h"
40 #include "timed-effect/player-hallucination.h"
41 #include "timed-effect/timed-effects.h"
42 #include "util/bit-flags-calculator.h"
43 #include "view/display-messages.h"
44 #include <algorithm>
45 #include <map>
46
47 SpellsMirrorMaster::SpellsMirrorMaster(PlayerType *player_ptr)
48     : player_ptr(player_ptr)
49 {
50 }
51
52 void SpellsMirrorMaster::remove_mirror(int y, int x)
53 {
54     auto &floor = *this->player_ptr->current_floor_ptr;
55     auto *g_ptr = &floor.grid_array[y][x];
56     reset_bits(g_ptr->info, CAVE_OBJECT);
57     g_ptr->mimic = 0;
58     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
59         reset_bits(g_ptr->info, CAVE_GLOW);
60         if (!view_torch_grids) {
61             reset_bits(g_ptr->info, CAVE_MARK);
62         }
63
64         if (g_ptr->m_idx) {
65             update_monster(this->player_ptr, g_ptr->m_idx, false);
66         }
67
68         update_local_illumination(this->player_ptr, y, x);
69     }
70
71     note_spot(this->player_ptr, y, x);
72     lite_spot(this->player_ptr, y, x);
73 }
74
75 /*!
76  * @brief 全鏡の消去 / Remove all mirrors in this floor
77  * @param player_ptr プレイヤーへの参照ポインタ
78  * @param explode 爆発処理を伴うならばTRUE
79  */
80 void SpellsMirrorMaster::remove_all_mirrors(bool explode)
81 {
82     const auto *floor_ptr = this->player_ptr->current_floor_ptr;
83     for (auto x = 0; x < floor_ptr->width; x++) {
84         for (auto y = 0; y < floor_ptr->height; y++) {
85             if (!floor_ptr->grid_array[y][x].is_mirror()) {
86                 continue;
87             }
88
89             this->remove_mirror(y, x);
90             if (!explode) {
91                 continue;
92             }
93
94             constexpr BIT_FLAGS projection = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI;
95             project(this->player_ptr, 0, 2, y, x, this->player_ptr->lev / 2 + 5, AttributeType::SHARDS, projection);
96         }
97     }
98 }
99
100 /*!
101  * @brief 鏡抜け処理のメインルーチン /
102  * Mirror Master's Dimension Door
103  * @param player_ptr プレイヤーへの参照ポインタ
104  * @return ターンを消費した場合TRUEを返す
105  */
106 bool SpellsMirrorMaster::mirror_tunnel()
107 {
108     int x;
109     int y;
110     if (!tgt_pt(this->player_ptr, &x, &y)) {
111         return false;
112     }
113
114     if (exe_dimension_door(this->player_ptr, x, y)) {
115         return true;
116     }
117
118     msg_print(_("鏡の世界をうまく通れなかった!", "You could not enter the mirror!"));
119     return true;
120 }
121
122 /*!
123  * @brief 鏡設置処理
124  * @return 実際に設置が行われた場合TRUEを返す
125  */
126 bool SpellsMirrorMaster::place_mirror()
127 {
128     auto y = this->player_ptr->y;
129     auto x = this->player_ptr->x;
130     auto *floor_ptr = this->player_ptr->current_floor_ptr;
131     if (!cave_clean_bold(floor_ptr, y, x)) {
132         msg_print(_("床上のアイテムが呪文を跳ね返した。", "The object resists the spell."));
133         return false;
134     }
135
136     /* Create a mirror */
137     auto *g_ptr = &floor_ptr->grid_array[y][x];
138     set_bits(g_ptr->info, CAVE_OBJECT);
139     g_ptr->mimic = feat_mirror;
140
141     /* Turn on the light */
142     set_bits(g_ptr->info, CAVE_GLOW);
143
144     note_spot(this->player_ptr, y, x);
145     lite_spot(this->player_ptr, y, x);
146     update_local_illumination(this->player_ptr, y, x);
147     return true;
148 }
149
150 /*!
151  * @brief 静水
152  * @param player_ptr プレイヤーへの参照ポインタ
153  * @return ペットを操っている場合を除きTRUE
154  */
155 bool SpellsMirrorMaster::mirror_concentration()
156 {
157     if (total_friends) {
158         msg_print(_("今はペットを操ることに集中していないと。", "Your pets demand all of your attention."));
159         return false;
160     }
161
162     if (!this->player_ptr->current_floor_ptr->grid_array[this->player_ptr->y][this->player_ptr->x].is_mirror()) {
163         msg_print(_("鏡の上でないと集中できない!", "There's no mirror here!"));
164         return true;
165     }
166
167     msg_print(_("少し頭がハッキリした。", "You feel your head clear a little."));
168     this->player_ptr->csp += (5 + this->player_ptr->lev * this->player_ptr->lev / 100);
169     if (this->player_ptr->csp >= this->player_ptr->msp) {
170         this->player_ptr->csp = this->player_ptr->msp;
171         this->player_ptr->csp_frac = 0;
172     }
173
174     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
175     return true;
176 }
177
178 /*!
179  * @brief 鏡魔法「鏡の封印」の効果処理
180  * @param dam ダメージ量
181  * @return 効果があったらTRUEを返す
182  */
183 void SpellsMirrorMaster::seal_of_mirror(const int dam)
184 {
185     const auto *floor_ptr = this->player_ptr->current_floor_ptr;
186     for (auto x = 0; x < floor_ptr->width; x++) {
187         for (auto y = 0; y < floor_ptr->height; y++) {
188             const auto &g_ref = floor_ptr->grid_array[y][x];
189             if (!g_ref.is_mirror()) {
190                 continue;
191             }
192
193             constexpr BIT_FLAGS flags = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP;
194             if (!affect_monster(this->player_ptr, 0, 0, y, x, dam, AttributeType::GENOCIDE, flags, true)) {
195                 continue;
196             }
197
198             if (g_ref.m_idx == 0) {
199                 this->remove_mirror(y, x);
200             }
201         }
202     }
203 }
204
205 void SpellsMirrorMaster::seeker_ray(int dir, int dam)
206 {
207     POSITION tx = this->player_ptr->x + ddx[dir];
208     POSITION ty = this->player_ptr->y + ddy[dir];
209     if ((dir == 5) && target_okay(this->player_ptr)) {
210         tx = target_col;
211         ty = target_row;
212     }
213
214     project_seeker_ray(tx, ty, dam);
215 }
216
217 void SpellsMirrorMaster::super_ray(int dir, int dam)
218 {
219     POSITION tx = this->player_ptr->x + ddx[dir];
220     POSITION ty = this->player_ptr->y + ddy[dir];
221     if ((dir == 5) && target_okay(this->player_ptr)) {
222         tx = target_col;
223         ty = target_row;
224     }
225
226     project_super_ray(tx, ty, dam);
227 }
228
229 /*!
230  * @brief 配置した鏡リストの次を取得する /
231  * Get another mirror. for SEEKER
232  * @param next_y 次の鏡のy座標を返す参照ポインタ
233  * @param next_x 次の鏡のx座標を返す参照ポインタ
234  * @param cury 現在の鏡のy座標
235  * @param curx 現在の鏡のx座標
236  */
237 void SpellsMirrorMaster::next_mirror(int *next_y, int *next_x, int cury, int curx)
238 {
239     POSITION mirror_x[10], mirror_y[10]; /* 鏡はもっと少ない */
240     int mirror_num = 0; /* 鏡の数 */
241
242     auto *floor_ptr = this->player_ptr->current_floor_ptr;
243
244     for (POSITION x = 0; x < floor_ptr->width; x++) {
245         for (POSITION y = 0; y < floor_ptr->height; y++) {
246             if (floor_ptr->grid_array[y][x].is_mirror()) {
247                 mirror_y[mirror_num] = y;
248                 mirror_x[mirror_num] = x;
249                 mirror_num++;
250             }
251         }
252     }
253
254     if (mirror_num) {
255         int num = randint0(mirror_num);
256         *next_y = mirror_y[num];
257         *next_x = mirror_x[num];
258         return;
259     }
260
261     do {
262         *next_y = cury + randint0(5) - 2;
263         *next_x = curx + randint0(5) - 2;
264     } while ((*next_y == cury) && (*next_x == curx));
265 }
266
267 void SpellsMirrorMaster::project_seeker_ray(int target_x, int target_y, int dam)
268 {
269     POSITION y1;
270     POSITION x1;
271     POSITION y2;
272     POSITION x2;
273     constexpr auto typ = AttributeType::SEEKER;
274     BIT_FLAGS flag = PROJECT_BEAM | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM | PROJECT_THRU | PROJECT_MIRROR;
275     rakubadam_p = 0;
276     rakubadam_m = 0;
277     monster_target_y = this->player_ptr->y;
278     monster_target_x = this->player_ptr->x;
279     auto *floor_ptr = this->player_ptr->current_floor_ptr;
280
281     ProjectResult res;
282
283     x1 = this->player_ptr->x;
284     y1 = this->player_ptr->y;
285
286     y2 = target_y;
287     x2 = target_x;
288
289     if ((x1 == x2) && (y1 == y2)) {
290         reset_bits(flag, PROJECT_THRU);
291     }
292
293     /* Calculate the projection path */
294     handle_stuff(this->player_ptr);
295
296     project_m_n = 0;
297     project_m_x = 0;
298     project_m_y = 0;
299     auto visual = false;
300
301     while (true) {
302         projection_path path_g(this->player_ptr, (project_length ? project_length : get_max_range(this->player_ptr)), y1, x1, y2, x2, flag);
303
304         if (path_g.path_num() == 0) {
305             break;
306         }
307
308         for (auto path_g_ite = path_g.begin(); path_g_ite != path_g.end(); path_g_ite++) {
309             const auto [oy, ox] = *(path_g_ite == path_g.begin() ? path_g.begin() : path_g_ite - 1);
310             const auto [ny, nx] = *path_g_ite;
311
312             if (delay_factor > 0 && !this->player_ptr->effects()->blindness()->is_blind()) {
313                 if (panel_contains(ny, nx) && player_has_los_bold(this->player_ptr, ny, nx)) {
314                     print_bolt_pict(this->player_ptr, oy, ox, ny, nx, typ);
315                     move_cursor_relative(ny, nx);
316                     term_fresh();
317                     term_xtra(TERM_XTRA_DELAY, delay_factor);
318                     lite_spot(this->player_ptr, ny, nx);
319                     term_fresh();
320
321                     print_bolt_pict(this->player_ptr, ny, nx, ny, nx, typ);
322
323                     visual = true;
324                 } else if (visual) {
325                     term_xtra(TERM_XTRA_DELAY, delay_factor);
326                 }
327             }
328
329             if (affect_item(this->player_ptr, 0, 0, ny, nx, dam, typ)) {
330                 res.notice = true;
331             }
332         }
333
334         for (const auto &[py, px] : path_g) {
335             if (affect_monster(this->player_ptr, 0, 0, py, px, dam, typ, flag, true)) {
336                 res.notice = true;
337             }
338             auto *g_ptr = &floor_ptr->grid_array[project_m_y][project_m_x];
339             auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
340             if (project_m_n == 1 && g_ptr->m_idx > 0 && m_ptr->ml) {
341                 if (!this->player_ptr->effects()->hallucination()->is_hallucinated()) {
342                     monster_race_track(this->player_ptr, m_ptr->ap_r_idx);
343                 }
344                 health_track(this->player_ptr, g_ptr->m_idx);
345             }
346
347             (void)affect_feature(this->player_ptr, 0, 0, py, px, dam, typ);
348         }
349
350         const auto [y, x] = path_g.back();
351
352         if (!floor_ptr->grid_array[y][x].is_mirror()) {
353             break;
354         }
355
356         monster_target_y = y;
357         monster_target_x = x;
358         this->remove_mirror(y, x);
359         this->next_mirror(&y2, &x2, y, x);
360         y1 = y;
361         x1 = x;
362     }
363 }
364
365 static void draw_super_ray_pict(PlayerType *player_ptr, const std::map<int, std::vector<projection_path::const_iterator>> &pos_list_map,
366     const std::vector<projection_path> &second_path_g_list, const std::pair<int, int> &center)
367 {
368     if (delay_factor <= 0) {
369         return;
370     }
371
372     constexpr auto typ = AttributeType::SUPER_RAY;
373
374     // 8方向のスーパーレイの軌道上の最後の到達点の座標のリスト
375     std::vector<std::pair<int, int>> last_pos_list;
376     std::transform(second_path_g_list.begin(), second_path_g_list.end(), std::back_inserter(last_pos_list),
377         [](const auto &path_g) { return path_g.back(); });
378
379     // スーパーレイの描画を行った座標のリスト。スーパーレイの全ての描画完了後に描画を消去するのに使用する。
380     std::vector<std::pair<int, int>> drawn_pos_list;
381
382     for (const auto &[n, pos_list] : pos_list_map) {
383         // スーパーレイの最終到達点の座標の描画を行った座標のリスト。最終到達点の描画を '*' で上書きするのに使用する。
384         std::vector<std::pair<int, int>> drawn_last_pos_list;
385
386         for (const auto &it : pos_list) {
387             const auto [y, x] = (n == 1) ? center : *std::next(it, -1);
388             const auto [ny, nx] = *it;
389
390             if (panel_contains(y, x) && player_has_los_bold(player_ptr, y, x)) {
391                 print_bolt_pict(player_ptr, y, x, y, x, typ);
392                 drawn_pos_list.emplace_back(y, x);
393             }
394             if (panel_contains(ny, nx) && player_has_los_bold(player_ptr, ny, nx)) {
395                 print_bolt_pict(player_ptr, y, x, ny, nx, typ);
396                 if (std::find(last_pos_list.begin(), last_pos_list.end(), *it) != last_pos_list.end()) {
397                     drawn_last_pos_list.emplace_back(ny, nx);
398                 }
399             }
400         }
401         term_fresh();
402         term_xtra(TERM_XTRA_DELAY, delay_factor);
403
404         for (const auto &[y, x] : drawn_last_pos_list) {
405             if (panel_contains(y, x) && player_has_los_bold(player_ptr, y, x)) {
406                 print_bolt_pict(player_ptr, y, x, y, x, typ);
407                 drawn_pos_list.emplace_back(y, x);
408             }
409         }
410     }
411
412     term_fresh();
413     term_xtra(TERM_XTRA_DELAY, delay_factor);
414
415     for (const auto &[y, x] : drawn_pos_list) {
416         lite_spot(player_ptr, y, x);
417     }
418 }
419
420 static bool activate_super_ray_effect(PlayerType *player_ptr, int y, int x, int dam, BIT_FLAGS flag)
421 {
422     constexpr auto typ = AttributeType::SUPER_RAY;
423     auto notice = false;
424
425     (void)affect_feature(player_ptr, 0, 0, y, x, dam, typ);
426
427     if (affect_item(player_ptr, 0, 0, y, x, dam, typ)) {
428         notice = true;
429     }
430
431     (void)affect_monster(player_ptr, 0, 0, y, x, dam, typ, flag, true);
432
433     const auto *floor_ptr = player_ptr->current_floor_ptr;
434     const auto *g_ptr = &floor_ptr->grid_array[project_m_y][project_m_x];
435     const auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
436     if (project_m_n == 1 && g_ptr->m_idx > 0 && m_ptr->ml) {
437         if (!player_ptr->effects()->hallucination()->is_hallucinated()) {
438             monster_race_track(player_ptr, m_ptr->ap_r_idx);
439         }
440         health_track(player_ptr, g_ptr->m_idx);
441     }
442
443     return notice;
444 }
445
446 void SpellsMirrorMaster::project_super_ray(int target_x, int target_y, int dam)
447 {
448     POSITION y1;
449     POSITION x1;
450     POSITION y2;
451     POSITION x2;
452     constexpr auto typ = AttributeType::SUPER_RAY;
453     BIT_FLAGS flag = PROJECT_BEAM | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM | PROJECT_THRU | PROJECT_MIRROR;
454     rakubadam_p = 0;
455     rakubadam_m = 0;
456     monster_target_y = this->player_ptr->y;
457     monster_target_x = this->player_ptr->x;
458     auto *floor_ptr = this->player_ptr->current_floor_ptr;
459
460     ProjectResult res;
461
462     x1 = this->player_ptr->x;
463     y1 = this->player_ptr->y;
464
465     y2 = target_y;
466     x2 = target_x;
467
468     if ((x1 == x2) && (y1 == y2)) {
469         flag &= ~(PROJECT_THRU);
470     }
471
472     /* Calculate the projection path */
473     projection_path path_g(this->player_ptr, (project_length ? project_length : get_max_range(this->player_ptr)), y1, x1, y2, x2, flag);
474     std::vector<projection_path> second_path_g_list;
475     handle_stuff(this->player_ptr);
476
477     if (path_g.path_num() == 0) {
478         return;
479     }
480
481     project_m_n = 0;
482     project_m_x = 0;
483     project_m_y = 0;
484     auto oy = y1;
485     auto ox = x1;
486     auto visual = false;
487     std::vector<std::pair<int, int>> drawn_pos_list;
488     for (const auto &[ny, nx] : path_g) {
489         if (delay_factor > 0) {
490             if (panel_contains(ny, nx) && player_has_los_bold(this->player_ptr, ny, nx)) {
491                 print_bolt_pict(this->player_ptr, oy, ox, ny, nx, typ);
492                 move_cursor_relative(ny, nx);
493                 term_fresh();
494                 term_xtra(TERM_XTRA_DELAY, delay_factor);
495                 lite_spot(this->player_ptr, ny, nx);
496                 term_fresh();
497
498                 print_bolt_pict(this->player_ptr, ny, nx, ny, nx, typ);
499
500                 drawn_pos_list.emplace_back(ny, nx);
501                 visual = true;
502             } else if (visual) {
503                 term_xtra(TERM_XTRA_DELAY, delay_factor);
504             }
505         }
506
507         if (!cave_has_flag_bold(floor_ptr, ny, nx, TerrainCharacteristics::PROJECT)) {
508             break;
509         }
510         oy = ny;
511         ox = nx;
512     }
513
514     for (const auto &[y, x] : drawn_pos_list) {
515         lite_spot(player_ptr, y, x);
516     }
517
518     {
519         const auto [y, x] = path_g.back();
520         if (floor_ptr->grid_array[y][x].is_mirror()) {
521             this->remove_mirror(y, x);
522             auto project_flag = flag;
523             reset_bits(project_flag, PROJECT_MIRROR);
524
525             const auto length = project_length ? project_length : get_max_range(this->player_ptr);
526             for (auto i : cdd) {
527                 const auto dy = ddy[i];
528                 const auto dx = ddx[i];
529                 second_path_g_list.emplace_back(this->player_ptr, length, y, x, y + dy, x + dx, project_flag);
530             }
531         }
532     }
533
534     for (const auto &[py, px] : path_g) {
535         res.notice |= activate_super_ray_effect(this->player_ptr, py, px, dam, flag);
536     }
537
538     // 起点の鏡からの距離 → 8方向へのスーパーレイの軌道上のその距離にある座標のイテレータのリストの map
539     std::map<int, std::vector<projection_path::const_iterator>> pos_list_map;
540     for (const auto &second_path_g : second_path_g_list) {
541         for (auto it = second_path_g.begin(); it != second_path_g.end(); ++it) {
542             const auto [o_y, o_x] = path_g.back();
543             const auto [y, x] = *it;
544             auto d = distance(o_y, o_x, y, x);
545             pos_list_map[d].push_back(it);
546         }
547     }
548
549     draw_super_ray_pict(this->player_ptr, pos_list_map, second_path_g_list, path_g.back());
550
551     for (auto &&[n, pos_list] : pos_list_map) {
552         rand_shuffle(pos_list.begin(), pos_list.end());
553
554         for (const auto &it : pos_list) {
555             const auto [y, x] = *it;
556             res.notice |= activate_super_ray_effect(player_ptr, y, x, dam, flag);
557         }
558     }
559 }