OSDN Git Service

Merge pull request #1739 from sikabane-works/release/3.0.0Alpha40
[hengbandforosx/hengbandosx.git] / src / effect / effect-feature.cpp
1 #include "effect/effect-feature.h"
2 #include "core/player-update-types.h"
3 #include "dungeon/dungeon-flag-types.h"
4 #include "dungeon/dungeon.h"
5 #include "effect/effect-characteristics.h"
6 #include "effect/effect-processor.h" // 暫定、後で消す.
7 #include "floor/cave.h"
8 #include "floor/geometry.h"
9 #include "grid/feature.h"
10 #include "grid/grid.h"
11 #include "grid/trap.h"
12 #include "main/sound-definitions-table.h"
13 #include "main/sound-of-music.h"
14 #include "mind/mind-elementalist.h"
15 #include "mind/mind-ninja.h"
16 #include "monster/monster-update.h"
17 #include "player/special-defense-types.h"
18 #include "room/door-definition.h"
19 #include "spell/spell-types.h"
20 #include "system/floor-type-definition.h"
21 #include "system/grid-type-definition.h"
22 #include "system/player-type-definition.h"
23 #include "util/bit-flags-calculator.h"
24 #include "view/display-messages.h"
25 #include "world/world.h"
26
27 /*
28  * Determine if a "legal" grid is an "naked" floor grid
29  *
30  * Line 1 -- forbid non-clean gird
31  * Line 2 -- forbid monsters
32  * Line 3 -- forbid the player
33  */
34 static bool cave_naked_bold(player_type *player_ptr, POSITION y, POSITION x)
35 {
36     floor_type *floor_ptr = player_ptr->current_floor_ptr;
37     return cave_clean_bold(floor_ptr, y, x) && (floor_ptr->grid_array[y][x].m_idx == 0) && !player_bold(player_ptr, y, x);
38 }
39
40 /*!
41  * @brief 汎用的なビーム/ボルト/ボール系による地形効果処理 / We are called from "project()" to "damage" terrain features
42  * @param player_ptr プレイヤーへの参照ポインタ
43  * @param who 魔法を発動したモンスター(0ならばプレイヤー) / Index of "source" monster (zero for "player")
44  * @param r 効果半径(ビーム/ボルト = 0 / ボール = 1以上) / Radius of explosion (0 = beam/bolt, 1 to 9 = ball)
45  * @param y 目標Y座標 / Target y location (or location to travel "towards")
46  * @param x 目標X座標 / Target x location (or location to travel "towards")
47  * @param dam 基本威力 / Base damage roll to apply to affected monsters (or player)
48  * @param typ 効果属性 / Type of damage to apply to monsters (and objects)
49  * @return 何か一つでも効力があればTRUEを返す / TRUE if any "effects" of the projection were observed, else FALSE
50  * @details
51  * <pre>
52  * We are called both for "beam" effects and "ball" effects.
53  *
54  * The "r" parameter is the "distance from ground zero".
55  *
56  * Note that we determine if the player can "see" anything that happens
57  * by taking into account: blindness, line-of-sight, and illumination.
58  *
59  * We return "TRUE" if the effect of the projection is "obvious".
60  *
61  * We also "see" grids which are "memorized", probably a hack
62  *
63  * Perhaps we should affect doors?
64  * </pre>
65  */
66 bool affect_feature(player_type *player_ptr, MONSTER_IDX who, POSITION r, POSITION y, POSITION x, HIT_POINT dam, EFFECT_ID typ)
67 {
68     floor_type *floor_ptr = player_ptr->current_floor_ptr;
69     grid_type *g_ptr = &floor_ptr->grid_array[y][x];
70     feature_type *f_ptr = &f_info[g_ptr->feat];
71
72     bool obvious = false;
73     bool known = player_has_los_bold(player_ptr, y, x);
74
75     who = who ? who : 0;
76     dam = (dam + r) / (r + 1);
77
78     if (f_ptr->flags.has(FF::TREE)) {
79         concptr message;
80         switch (typ) {
81         case GF_POIS:
82         case GF_NUKE:
83         case GF_DEATH_RAY:
84             message = _("枯れた", "was blasted.");
85             break;
86         case GF_TIME:
87             message = _("縮んだ", "shrank.");
88             break;
89         case GF_ACID:
90             message = _("溶けた", "melted.");
91             break;
92         case GF_COLD:
93         case GF_ICE:
94             message = _("凍り、砕け散った", "was frozen and smashed.");
95             break;
96         case GF_FIRE:
97         case GF_ELEC:
98         case GF_PLASMA:
99             message = _("燃えた", "burns up!");
100             break;
101         case GF_METEOR:
102         case GF_CHAOS:
103         case GF_MANA:
104         case GF_SEEKER:
105         case GF_SUPER_RAY:
106         case GF_SHARDS:
107         case GF_ROCKET:
108         case GF_SOUND:
109         case GF_DISENCHANT:
110         case GF_FORCE:
111         case GF_GRAVITY:
112             message = _("粉砕された", "was crushed.");
113             break;
114         case GF_VOID:
115             message = _("消滅した", "vanished.");
116             break;
117         default:
118             message = nullptr;
119             break;
120         }
121
122         if (message) {
123             msg_format(_("木は%s。", "A tree %s"), message);
124             cave_set_feat(player_ptr, y, x, one_in_(3) ? feat_brake : feat_grass);
125
126             /* Observe */
127             if (g_ptr->is_mark())
128                 obvious = true;
129         }
130     }
131
132     /* Analyze the type */
133     switch (typ) {
134         /* Ignore most effects */
135     case GF_CAPTURE:
136     case GF_HAND_DOOM:
137     case GF_CAUSE_1:
138     case GF_CAUSE_2:
139     case GF_CAUSE_3:
140     case GF_CAUSE_4:
141     case GF_MIND_BLAST:
142     case GF_BRAIN_SMASH:
143     case GF_DRAIN_MANA:
144     case GF_PSY_SPEAR:
145     case GF_FORCE:
146     case GF_HOLY_FIRE:
147     case GF_HELL_FIRE:
148     case GF_PSI:
149     case GF_PSI_DRAIN:
150     case GF_TELEKINESIS:
151     case GF_DOMINATION:
152     case GF_IDENTIFY:
153     case GF_ATTACK:
154     case GF_ACID:
155     case GF_ELEC:
156     case GF_COLD:
157     case GF_ICE:
158     case GF_FIRE:
159     case GF_PLASMA:
160     case GF_METEOR:
161     case GF_CHAOS:
162     case GF_MANA:
163     case GF_SEEKER:
164     case GF_SUPER_RAY: {
165         break;
166     }
167     case GF_KILL_TRAP: {
168         if (is_hidden_door(player_ptr, g_ptr)) {
169             disclose_grid(player_ptr, y, x);
170             if (known) {
171                 obvious = true;
172             }
173         }
174
175         if (is_trap(player_ptr, g_ptr->feat)) {
176             if (known) {
177                 msg_print(_("まばゆい閃光が走った!", "There is a bright flash of light!"));
178                 obvious = true;
179             }
180
181             cave_alter_feat(player_ptr, y, x, FF::DISARM);
182         }
183
184         if (is_closed_door(player_ptr, g_ptr->feat) && f_ptr->power && f_ptr->flags.has(FF::OPEN)) {
185             FEAT_IDX old_feat = g_ptr->feat;
186             cave_alter_feat(player_ptr, y, x, FF::DISARM);
187             if (known && (old_feat != g_ptr->feat)) {
188                 msg_print(_("カチッと音がした!", "Click!"));
189                 obvious = true;
190             }
191         }
192
193         if (player_ptr->blind || !player_has_los_bold(player_ptr, y, x))
194             break;
195
196         g_ptr->info &= ~(CAVE_UNSAFE);
197         lite_spot(player_ptr, y, x);
198         obvious = true;
199         break;
200     }
201     case GF_KILL_DOOR: {
202         if (is_trap(player_ptr, g_ptr->feat) || f_ptr->flags.has(FF::DOOR)) {
203             if (known) {
204                 msg_print(_("まばゆい閃光が走った!", "There is a bright flash of light!"));
205                 obvious = true;
206             }
207
208             cave_alter_feat(player_ptr, y, x, FF::TUNNEL);
209         }
210
211         if (player_ptr->blind || !player_has_los_bold(player_ptr, y, x))
212             break;
213
214         g_ptr->info &= ~(CAVE_UNSAFE);
215         lite_spot(player_ptr, y, x);
216         obvious = true;
217         break;
218     }
219     case GF_JAM_DOOR: {
220         if (f_ptr->flags.has_not(FF::SPIKE))
221             break;
222
223         int16_t old_mimic = g_ptr->mimic;
224         feature_type *mimic_f_ptr = &f_info[g_ptr->get_feat_mimic()];
225
226         cave_alter_feat(player_ptr, y, x, FF::SPIKE);
227         g_ptr->mimic = old_mimic;
228
229         note_spot(player_ptr, y, x);
230         lite_spot(player_ptr, y, x);
231
232         if (!known || mimic_f_ptr->flags.has_not(FF::OPEN))
233             break;
234
235         msg_format(_("%sに何かがつっかえて開かなくなった。", "The %s seems stuck."), mimic_f_ptr->name.c_str());
236         obvious = true;
237         break;
238     }
239     case GF_KILL_WALL: {
240         if (f_ptr->flags.has_not(FF::HURT_ROCK))
241             break;
242
243         if (known && g_ptr->is_mark()) {
244             msg_format(_("%sが溶けて泥になった!", "The %s turns into mud!"), f_info[g_ptr->get_feat_mimic()].name.c_str());
245             obvious = true;
246         }
247
248         cave_alter_feat(player_ptr, y, x, FF::HURT_ROCK);
249         player_ptr->update |= (PU_FLOW);
250         break;
251     }
252     case GF_MAKE_DOOR: {
253         if (!cave_naked_bold(player_ptr, y, x))
254             break;
255         if (player_bold(player_ptr, y, x))
256             break;
257         cave_set_feat(player_ptr, y, x, feat_door[DOOR_DOOR].closed);
258         if (g_ptr->is_mark())
259             obvious = true;
260         break;
261     }
262     case GF_MAKE_TRAP: {
263         place_trap(player_ptr, y, x);
264         break;
265     }
266     case GF_MAKE_TREE: {
267         if (!cave_naked_bold(player_ptr, y, x))
268             break;
269         if (player_bold(player_ptr, y, x))
270             break;
271         cave_set_feat(player_ptr, y, x, feat_tree);
272         if (g_ptr->is_mark())
273             obvious = true;
274         break;
275     }
276     case GF_MAKE_RUNE_PROTECTION: {
277         if (!cave_naked_bold(player_ptr, y, x))
278             break;
279         g_ptr->info |= CAVE_OBJECT;
280         g_ptr->mimic = feat_rune_protection;
281         note_spot(player_ptr, y, x);
282         lite_spot(player_ptr, y, x);
283         break;
284     }
285     case GF_STONE_WALL: {
286         if (!cave_naked_bold(player_ptr, y, x))
287             break;
288         if (player_bold(player_ptr, y, x))
289             break;
290         cave_set_feat(player_ptr, y, x, feat_granite);
291         break;
292     }
293     case GF_LAVA_FLOW: {
294         if (f_ptr->flags.has(FF::PERMANENT))
295             break;
296         if (dam == 1) {
297             if (f_ptr->flags.has_not(FF::FLOOR))
298                 break;
299             cave_set_feat(player_ptr, y, x, feat_shallow_lava);
300         } else if (dam) {
301             cave_set_feat(player_ptr, y, x, feat_deep_lava);
302         }
303
304         break;
305     }
306     case GF_WATER_FLOW: {
307         if (f_ptr->flags.has(FF::PERMANENT))
308             break;
309         if (dam == 1) {
310             if (f_ptr->flags.has_not(FF::FLOOR))
311                 break;
312             cave_set_feat(player_ptr, y, x, feat_shallow_water);
313         } else if (dam) {
314             cave_set_feat(player_ptr, y, x, feat_deep_water);
315         }
316
317         break;
318     }
319     case GF_LITE_WEAK:
320     case GF_LITE: {
321         if (d_info[player_ptr->dungeon_idx].flags.has(DF::DARKNESS))
322             break;
323
324         g_ptr->info |= (CAVE_GLOW);
325         note_spot(player_ptr, y, x);
326         lite_spot(player_ptr, y, x);
327         update_local_illumination(player_ptr, y, x);
328
329         if (player_can_see_bold(player_ptr, y, x))
330             obvious = true;
331         if (g_ptr->m_idx)
332             update_monster(player_ptr, g_ptr->m_idx, false);
333
334         if (player_bold(player_ptr, y, x)) {
335             set_superstealth(player_ptr, false);
336         }
337
338         break;
339     }
340     case GF_DARK_WEAK:
341     case GF_DARK:
342     case GF_ABYSS: {
343         bool do_dark = !player_ptr->phase_out && !g_ptr->is_mirror();
344         if (!do_dark)
345             break;
346
347         if ((floor_ptr->dun_level > 0) || !is_daytime()) {
348             for (int j = 0; j < 9; j++) {
349                 int by = y + ddy_ddd[j];
350                 int bx = x + ddx_ddd[j];
351
352                 if (!in_bounds2(floor_ptr, by, bx))
353                     continue;
354
355                 grid_type *cc_ptr = &floor_ptr->grid_array[by][bx];
356                 if (f_info[cc_ptr->get_feat_mimic()].flags.has(FF::GLOW)) {
357                     do_dark = false;
358                     break;
359                 }
360             }
361
362             if (!do_dark)
363                 break;
364         }
365
366         g_ptr->info &= ~(CAVE_GLOW);
367
368         /* Hack -- Forget "boring" grids */
369         if (f_ptr->flags.has_not(FF::REMEMBER) || has_element_resist(player_ptr, ElementRealm::DARKNESS, 1)) {
370             /* Forget */
371             g_ptr->info &= ~(CAVE_MARK);
372             note_spot(player_ptr, y, x);
373         }
374
375         lite_spot(player_ptr, y, x);
376
377         update_local_illumination(player_ptr, y, x);
378
379         if (player_can_see_bold(player_ptr, y, x))
380             obvious = true;
381         if (g_ptr->m_idx)
382             update_monster(player_ptr, g_ptr->m_idx, false);
383
384         break;
385     }
386     case GF_SHARDS:
387     case GF_ROCKET: {
388         if (g_ptr->is_mirror()) {
389             msg_print(_("鏡が割れた!", "The mirror was shattered!"));
390             sound(SOUND_GLASS);
391             remove_mirror(player_ptr, y, x);
392             project(player_ptr, 0, 2, y, x, player_ptr->lev / 2 + 5, GF_SHARDS,
393                 (PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI));
394         }
395
396         if (f_ptr->flags.has_not(FF::GLASS) || f_ptr->flags.has(FF::PERMANENT) || (dam < 50))
397             break;
398
399         if (known && (g_ptr->is_mark())) {
400             msg_format(_("%sが割れた!", "The %s crumbled!"), f_info[g_ptr->get_feat_mimic()].name.c_str());
401             sound(SOUND_GLASS);
402         }
403
404         cave_alter_feat(player_ptr, y, x, FF::HURT_ROCK);
405         player_ptr->update |= (PU_FLOW);
406         break;
407     }
408     case GF_SOUND: {
409         if (g_ptr->is_mirror() && player_ptr->lev < 40) {
410             msg_print(_("鏡が割れた!", "The mirror was shattered!"));
411             sound(SOUND_GLASS);
412             remove_mirror(player_ptr, y, x);
413             project(player_ptr, 0, 2, y, x, player_ptr->lev / 2 + 5, GF_SHARDS,
414                 (PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | PROJECT_JUMP | PROJECT_NO_HANGEKI));
415         }
416
417         if (f_ptr->flags.has_not(FF::GLASS) || f_ptr->flags.has(FF::PERMANENT) || (dam < 200))
418             break;
419
420         if (known && (g_ptr->is_mark())) {
421             msg_format(_("%sが割れた!", "The %s crumbled!"), f_info[g_ptr->get_feat_mimic()].name.c_str());
422             sound(SOUND_GLASS);
423         }
424
425         cave_alter_feat(player_ptr, y, x, FF::HURT_ROCK);
426         player_ptr->update |= (PU_FLOW);
427         break;
428     }
429     case GF_DISINTEGRATE: {
430         if (g_ptr->is_mirror() || g_ptr->is_rune_protection() || g_ptr->is_rune_explosion())
431             remove_mirror(player_ptr, y, x);
432
433         if (f_ptr->flags.has_not(FF::HURT_DISI) || f_ptr->flags.has(FF::PERMANENT))
434             break;
435
436         cave_alter_feat(player_ptr, y, x, FF::HURT_DISI);
437         player_ptr->update |= (PU_FLOW);
438         break;
439     }
440     }
441
442     lite_spot(player_ptr, y, x);
443     return (obvious);
444 }