OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / mind / mind-mirror-master.cpp
1 /*!
2  * @brief 鏡使いの鏡魔法コマンド処理
3  * @date 2022/03/07
4  * @author Hourier
5  * @todo 作りかけの部分複数あり
6  */
7
8 #include "mind/mind-mirror-master.h"
9 #include "core/disturbance.h"
10 #include "core/stuff-handler.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/disturbance-options.h"
21 #include "game-option/map-screen-options.h"
22 #include "game-option/special-options.h"
23 #include "grid/feature.h"
24 #include "grid/grid.h"
25 #include "io/cursor.h"
26 #include "io/screen-util.h"
27 #include "mind/mind-magic-resistance.h"
28 #include "mind/mind-numbers.h"
29 #include "pet/pet-util.h"
30 #include "spell-class/spells-mirror-master.h"
31 #include "spell-kind/spells-detection.h"
32 #include "spell-kind/spells-floor.h"
33 #include "spell-kind/spells-launcher.h"
34 #include "spell-kind/spells-lite.h"
35 #include "spell-kind/spells-sight.h"
36 #include "spell-kind/spells-teleport.h"
37 #include "spell-kind/spells-world.h"
38 #include "status/body-improvement.h"
39 #include "status/buff-setter.h"
40 #include "status/sight-setter.h"
41 #include "system/floor-type-definition.h"
42 #include "system/grid-type-definition.h"
43 #include "system/player-type-definition.h"
44 #include "system/redrawing-flags-updater.h"
45 #include "target/grid-selector.h"
46 #include "target/projection-path-calculator.h"
47 #include "target/target-getter.h"
48 #include "timed-effect/player-blindness.h"
49 #include "timed-effect/timed-effects.h"
50 #include "util/bit-flags-calculator.h"
51 #include "view/display-messages.h"
52 #include "world/world.h"
53
54 /*
55  * @brief Multishadow effects is determined by turn
56  */
57 bool check_multishadow(PlayerType *player_ptr)
58 {
59     return (player_ptr->multishadow != 0) && ((w_ptr->game_turn & 1) != 0);
60 }
61
62 /*!
63  * @brief 鏡魔法「封魔結界」の効果処理
64  * @param dam ダメージ量
65  * @return 効果があったらTRUEを返す
66  */
67 bool binding_field(PlayerType *player_ptr, int dam)
68 {
69     POSITION mirror_x[10], mirror_y[10]; /* 鏡はもっと少ない */
70     int mirror_num = 0; /* 鏡の数 */
71
72     /* 三角形の頂点 */
73     POSITION point_x[3];
74     POSITION point_y[3];
75
76     /* Default target of monsterspell is player */
77     monster_target_y = player_ptr->y;
78     monster_target_x = player_ptr->x;
79
80     for (POSITION x = 0; x < player_ptr->current_floor_ptr->width; x++) {
81         for (POSITION y = 0; y < player_ptr->current_floor_ptr->height; y++) {
82             if (!player_ptr->current_floor_ptr->grid_array[y][x].is_mirror()) {
83                 continue;
84             }
85
86             const auto dist = distance(player_ptr->y, player_ptr->x, y, x);
87             const auto is_projectable = projectable(player_ptr, player_ptr->y, player_ptr->x, y, x);
88             if ((dist == 0) || (dist > get_max_range(player_ptr)) || !player_has_los_bold(player_ptr, y, x) || !is_projectable) {
89                 continue;
90             }
91
92             mirror_y[mirror_num] = y;
93             mirror_x[mirror_num] = x;
94             mirror_num++;
95         }
96     }
97
98     if (mirror_num < 2) {
99         return false;
100     }
101
102     point_x[0] = randint0(mirror_num);
103     do {
104         point_x[1] = randint0(mirror_num);
105     } while (point_x[0] == point_x[1]);
106
107     point_y[0] = mirror_y[point_x[0]];
108     point_x[0] = mirror_x[point_x[0]];
109     point_y[1] = mirror_y[point_x[1]];
110     point_x[1] = mirror_x[point_x[1]];
111     point_y[2] = player_ptr->y;
112     point_x[2] = player_ptr->x;
113
114     POSITION x = point_x[0] + point_x[1] + point_x[2];
115     POSITION y = point_y[0] + point_y[1] + point_y[2];
116
117     POSITION centersign = (point_x[0] * 3 - x) * (point_y[1] * 3 - y) - (point_y[0] * 3 - y) * (point_x[1] * 3 - x);
118     if (centersign == 0) {
119         return false;
120     }
121
122     POSITION x1 = point_x[0] < point_x[1] ? point_x[0] : point_x[1];
123     x1 = x1 < point_x[2] ? x1 : point_x[2];
124     POSITION y1 = point_y[0] < point_y[1] ? point_y[0] : point_y[1];
125     y1 = y1 < point_y[2] ? y1 : point_y[2];
126
127     POSITION x2 = point_x[0] > point_x[1] ? point_x[0] : point_x[1];
128     x2 = x2 > point_x[2] ? x2 : point_x[2];
129     POSITION y2 = point_y[0] > point_y[1] ? point_y[0] : point_y[1];
130     y2 = y2 > point_y[2] ? y2 : point_y[2];
131
132     for (y = y1; y <= y2; y++) {
133         for (x = x1; x <= x2; x++) {
134             if ((centersign * ((point_x[0] - x) * (point_y[1] - y) - (point_y[0] - y) * (point_x[1] - x)) < 0)) {
135                 continue;
136             }
137
138             if ((centersign * ((point_x[1] - x) * (point_y[2] - y) - (point_y[1] - y) * (point_x[2] - x)) < 0)) {
139                 continue;
140             }
141
142             if ((centersign * ((point_x[2] - x) * (point_y[0] - y) - (point_y[2] - y) * (point_x[0] - x)) < 0)) {
143                 continue;
144             }
145
146             if (player_has_los_bold(player_ptr, y, x) && projectable(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
147                 if (!(player_ptr->effects()->blindness()->is_blind()) && panel_contains(y, x)) {
148                     print_bolt_pict(player_ptr, y, x, y, x, AttributeType::MANA);
149                     move_cursor_relative(y, x);
150                     term_fresh();
151                     term_xtra(TERM_XTRA_DELAY, delay_factor);
152                 }
153             }
154         }
155     }
156
157     for (y = y1; y <= y2; y++) {
158         for (x = x1; x <= x2; x++) {
159             if (centersign * ((point_x[0] - x) * (point_y[1] - y) - (point_y[0] - y) * (point_x[1] - x)) < 0) {
160                 continue;
161             }
162
163             if (centersign * ((point_x[1] - x) * (point_y[2] - y) - (point_y[1] - y) * (point_x[2] - x)) < 0) {
164                 continue;
165             }
166
167             if (centersign * ((point_x[2] - x) * (point_y[0] - y) - (point_y[2] - y) * (point_x[0] - x)) < 0) {
168                 continue;
169             }
170
171             if (player_has_los_bold(player_ptr, y, x) && projectable(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
172                 (void)affect_feature(player_ptr, 0, 0, y, x, dam, AttributeType::MANA);
173             }
174         }
175     }
176
177     for (y = y1; y <= y2; y++) {
178         for (x = x1; x <= x2; x++) {
179             if (centersign * ((point_x[0] - x) * (point_y[1] - y) - (point_y[0] - y) * (point_x[1] - x)) < 0) {
180                 continue;
181             }
182
183             if (centersign * ((point_x[1] - x) * (point_y[2] - y) - (point_y[1] - y) * (point_x[2] - x)) < 0) {
184                 continue;
185             }
186
187             if (centersign * ((point_x[2] - x) * (point_y[0] - y) - (point_y[2] - y) * (point_x[0] - x)) < 0) {
188                 continue;
189             }
190
191             if (player_has_los_bold(player_ptr, y, x) && projectable(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
192                 (void)affect_item(player_ptr, 0, 0, y, x, dam, AttributeType::MANA);
193             }
194         }
195     }
196
197     for (y = y1; y <= y2; y++) {
198         for (x = x1; x <= x2; x++) {
199             if (centersign * ((point_x[0] - x) * (point_y[1] - y) - (point_y[0] - y) * (point_x[1] - x)) < 0) {
200                 continue;
201             }
202
203             if (centersign * ((point_x[1] - x) * (point_y[2] - y) - (point_y[1] - y) * (point_x[2] - x)) < 0) {
204                 continue;
205             }
206
207             if (centersign * ((point_x[2] - x) * (point_y[0] - y) - (point_y[2] - y) * (point_x[0] - x)) < 0) {
208                 continue;
209             }
210
211             if (player_has_los_bold(player_ptr, y, x) && projectable(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
212                 constexpr auto flags = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP;
213                 (void)affect_monster(player_ptr, 0, 0, y, x, dam, AttributeType::MANA, flags, true);
214             }
215         }
216     }
217
218     if (one_in_(7)) {
219         msg_print(_("鏡が結界に耐えきれず、壊れてしまった。", "The field broke a mirror"));
220         SpellsMirrorMaster(player_ptr).remove_mirror(point_y[0], point_x[0]);
221     }
222
223     return true;
224 }
225
226 /*!
227  * 幻惑の光 @ 鏡使いだけでなく混沌の戦士も使える
228  * @param player_ptr プレイヤーへの参照ポインタ
229  * @return 常にTRUE
230  */
231 bool confusing_light(PlayerType *player_ptr)
232 {
233     msg_print(_("辺りを睨んだ...", "You glare at nearby monsters..."));
234     slow_monsters(player_ptr, player_ptr->lev);
235     stun_monsters(player_ptr, player_ptr->lev * 4);
236     confuse_monsters(player_ptr, player_ptr->lev * 4);
237     turn_monsters(player_ptr, player_ptr->lev * 4);
238     stasis_monsters(player_ptr, player_ptr->lev * 4);
239     return true;
240 }
241
242 /*
243  * Set "multishadow", notice observable changes
244  */
245 bool set_multishadow(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
246 {
247     bool notice = false;
248     v = (v > 10000) ? 10000 : (v < 0) ? 0
249                                       : v;
250
251     if (player_ptr->is_dead) {
252         return false;
253     }
254
255     if (v) {
256         if (player_ptr->multishadow && !do_dec) {
257             if (player_ptr->multishadow > v) {
258                 return false;
259             }
260         } else if (!player_ptr->multishadow) {
261             msg_print(_("あなたの周りに幻影が生まれた。", "Your Shadow enveloped you."));
262             notice = true;
263         }
264     } else {
265         if (player_ptr->multishadow) {
266             msg_print(_("幻影が消えた。", "Your Shadow disappears."));
267             notice = true;
268         }
269     }
270
271     player_ptr->multishadow = v;
272     auto &rfu = RedrawingFlagsUpdater::get_instance();
273     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
274     if (!notice) {
275         return false;
276     }
277
278     if (disturb_state) {
279         disturb(player_ptr, false, false);
280     }
281
282     rfu.set_flag(StatusRecalculatingFlag::BONUS);
283     handle_stuff(player_ptr);
284     return true;
285 }
286
287 /*!
288  * @brief 一時的破片のオーラの継続時間をセットする / Set "dustrobe", notice observable changes
289  * @param v 継続時間
290  * @param do_dec 現在の継続時間より長い値のみ上書きする
291  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
292  */
293 bool set_dustrobe(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
294 {
295     bool notice = false;
296     v = (v > 10000) ? 10000 : (v < 0) ? 0
297                                       : v;
298
299     if (player_ptr->is_dead) {
300         return false;
301     }
302
303     if (v) {
304         if (player_ptr->dustrobe && !do_dec) {
305             if (player_ptr->dustrobe > v) {
306                 return false;
307             }
308         } else if (!player_ptr->dustrobe) {
309             msg_print(_("体が鏡のオーラで覆われた。", "You are enveloped by mirror shards."));
310             notice = true;
311         }
312     } else {
313         if (player_ptr->dustrobe) {
314             msg_print(_("鏡のオーラが消えた。", "The mirror shards disappear."));
315             notice = true;
316         }
317     }
318
319     player_ptr->dustrobe = v;
320     auto &rfu = RedrawingFlagsUpdater::get_instance();
321     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
322     if (!notice) {
323         return false;
324     }
325
326     if (disturb_state) {
327         disturb(player_ptr, false, false);
328     }
329
330     rfu.set_flag(StatusRecalculatingFlag::BONUS);
331     handle_stuff(player_ptr);
332     return true;
333 }
334
335 /*!
336  * @brief 現在フロアに存在している鏡の数を数える / calculate mirrors
337  * @return 鏡の枚数
338  */
339 static int number_of_mirrors(FloorType *floor_ptr)
340 {
341     int val = 0;
342     for (POSITION x = 0; x < floor_ptr->width; x++) {
343         for (POSITION y = 0; y < floor_ptr->height; y++) {
344             if (floor_ptr->grid_array[y][x].is_mirror()) {
345                 val++;
346             }
347         }
348     }
349
350     return val;
351 }
352
353 /*!
354  * @brief 鏡魔法の発動 /
355  * do_cmd_cast calls this function if the player's class is 'Mirror magic'.
356  * @param spell 発動する特殊技能のID
357  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
358  */
359 bool cast_mirror_spell(PlayerType *player_ptr, MindMirrorMasterType spell)
360 {
361     DIRECTION dir;
362     PLAYER_LEVEL plev = player_ptr->lev;
363     int tmp;
364     TIME_EFFECT t;
365     POSITION x, y;
366     auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[player_ptr->y][player_ptr->x];
367     switch (spell) {
368     case MindMirrorMasterType::MIRROR_SEEING:
369         tmp = g_ptr->is_mirror() ? 4 : 0;
370         if (plev + tmp > 4) {
371             detect_monsters_normal(player_ptr, DETECT_RAD_DEFAULT);
372         }
373         if (plev + tmp > 18) {
374             detect_monsters_invis(player_ptr, DETECT_RAD_DEFAULT);
375         }
376         if (plev + tmp > 28) {
377             set_tim_esp(player_ptr, (TIME_EFFECT)plev, false);
378         }
379         if (plev + tmp > 38) {
380             map_area(player_ptr, DETECT_RAD_MAP);
381         }
382         if (tmp == 0 && plev < 5) {
383             msg_print(_("鏡がなくて集中できなかった!", "You need a mirror to concentrate!"));
384         }
385         break;
386     case MindMirrorMasterType::MAKE_MIRROR:
387         if (number_of_mirrors(player_ptr->current_floor_ptr) < 4 + plev / 10) {
388             SpellsMirrorMaster(player_ptr).place_mirror();
389         } else {
390             msg_format(_("これ以上鏡は制御できない!", "There are too many mirrors to control!"));
391         }
392
393         break;
394     case MindMirrorMasterType::DRIP_LIGHT:
395         if (!get_aim_dir(player_ptr, &dir)) {
396             return false;
397         }
398
399         if (plev > 9 && g_ptr->is_mirror()) {
400             fire_beam(player_ptr, AttributeType::LITE, dir, damroll(3 + ((plev - 1) / 5), 4));
401         } else {
402             fire_bolt(player_ptr, AttributeType::LITE, dir, damroll(3 + ((plev - 1) / 5), 4));
403         }
404
405         break;
406     case MindMirrorMasterType::WRAPPED_MIRROR:
407         teleport_player(player_ptr, 10, TELEPORT_SPONTANEOUS);
408         break;
409     case MindMirrorMasterType::MIRROR_LIGHT:
410         (void)lite_area(player_ptr, damroll(2, (plev / 2)), (plev / 10) + 1);
411         break;
412     case MindMirrorMasterType::WANDERING_MIRROR:
413         teleport_player(player_ptr, plev * 5, TELEPORT_SPONTANEOUS);
414         break;
415     case MindMirrorMasterType::ROBE_DUST:
416         set_dustrobe(player_ptr, 20 + randint1(20), false);
417         break;
418     case MindMirrorMasterType::BANISHING_MIRROR:
419         if (!get_aim_dir(player_ptr, &dir)) {
420             return false;
421         }
422
423         (void)fire_beam(player_ptr, AttributeType::AWAY_ALL, dir, plev);
424         break;
425     case MindMirrorMasterType::MIRROR_CRASHING:
426         if (!get_aim_dir(player_ptr, &dir)) {
427             return false;
428         }
429
430         fire_ball(player_ptr, AttributeType::SHARDS, dir, damroll(8 + ((plev - 5) / 4), 8), (plev > 20 ? (plev - 20) / 8 + 1 : 0));
431         break;
432     case MindMirrorMasterType::SLEEPING_MIRROR:
433         for (x = 0; x < player_ptr->current_floor_ptr->width; x++) {
434             for (y = 0; y < player_ptr->current_floor_ptr->height; y++) {
435                 if (player_ptr->current_floor_ptr->grid_array[y][x].is_mirror()) {
436                     project(player_ptr, 0, 2, y, x, (int)plev, AttributeType::OLD_SLEEP,
437                         (PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI));
438                 }
439             }
440         }
441
442         break;
443     case MindMirrorMasterType::SEEKER_RAY:
444         if (!get_aim_dir(player_ptr, &dir)) {
445             return false;
446         }
447
448         SpellsMirrorMaster(player_ptr).seeker_ray(dir, damroll(11 + (plev - 5) / 4, 8));
449         break;
450     case MindMirrorMasterType::SEALING_MIRROR:
451         SpellsMirrorMaster(player_ptr).seal_of_mirror(plev * 4 + 100);
452         break;
453     case MindMirrorMasterType::WATER_SHIELD:
454         t = 20 + randint1(20);
455         set_shield(player_ptr, t, false);
456         if (plev > 31) {
457             set_tim_reflect(player_ptr, t, false);
458         }
459
460         if (plev > 39) {
461             set_resist_magic(player_ptr, t, false);
462         }
463
464         break;
465     case MindMirrorMasterType::SUPER_RAY:
466         if (!get_aim_dir(player_ptr, &dir)) {
467             return false;
468         }
469
470         SpellsMirrorMaster(player_ptr).super_ray(dir, damroll(11 + (plev - 5) / 4, 8));
471         break;
472     case MindMirrorMasterType::ILLUSION_LIGHT:
473         tmp = g_ptr->is_mirror() ? 4 : 3;
474         slow_monsters(player_ptr, plev);
475         stun_monsters(player_ptr, plev * tmp * 2);
476         confuse_monsters(player_ptr, plev * tmp);
477         turn_monsters(player_ptr, plev * tmp);
478         stasis_monsters(player_ptr, plev * tmp);
479         break;
480     case MindMirrorMasterType::MIRROR_SHIFT:
481         if (!g_ptr->is_mirror()) {
482             msg_print(_("鏡の国の場所がわからない!", "You cannot find out where the mirror is!"));
483             break;
484         }
485
486         reserve_alter_reality(player_ptr, randint0(21) + 15);
487         break;
488     case MindMirrorMasterType::MIRROR_TUNNEL:
489         msg_print(_("鏡の世界を通り抜け…  ", "You try to enter the mirror..."));
490         return SpellsMirrorMaster(player_ptr).mirror_tunnel();
491     case MindMirrorMasterType::RECALL_MIRROR:
492         return recall_player(player_ptr, randint0(21) + 15);
493     case MindMirrorMasterType::MULTI_SHADOW:
494         set_multishadow(player_ptr, 6 + randint1(6), false);
495         break;
496     case MindMirrorMasterType::BINDING_FIELD:
497         if (!binding_field(player_ptr, plev * 11 + 5)) {
498             msg_print(_("適当な鏡を選べなかった!", "You were not able to choose suitable mirrors!"));
499         }
500
501         break;
502     case MindMirrorMasterType::RUFFNOR_MIRROR:
503         (void)set_invuln(player_ptr, randint1(4) + 4, false);
504         break;
505     default:
506         msg_print(_("なに?", "Zap?"));
507         break;
508     }
509
510     return true;
511 }