OSDN Git Service

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