OSDN Git Service

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