OSDN Git Service

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