OSDN Git Service

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