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"
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"
33 * Determine if a "legal" grid is an "naked" floor grid
35 * Line 1 -- forbid non-clean gird
36 * Line 2 -- forbid monsters
37 * Line 3 -- forbid the player
39 static bool cave_naked_bold(PlayerType *player_ptr, const Pos2D &pos)
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);
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
57 * We are called both for "beam" effects and "ball" effects.
59 * The "r" parameter is the "distance from ground zero".
61 * Note that we determine if the player can "see" anything that happens
62 * by taking into account: blindness, line-of-sight, and illumination.
64 * We return "TRUE" if the effect of the projection is "obvious".
66 * We also "see" grids which are "memorized", probably a hack
68 * Perhaps we should affect doors?
71 bool affect_feature(PlayerType *player_ptr, MONSTER_IDX src_idx, POSITION r, POSITION y, POSITION x, int dam, AttributeType typ)
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();
79 auto known = grid.has_los();
81 src_idx = is_monster(src_idx) ? src_idx : 0;
82 dam = (dam + r) / (r + 1);
84 if (terrain.flags.has(TerrainCharacteristics::TREE)) {
87 case AttributeType::POIS:
88 case AttributeType::NUKE:
89 case AttributeType::DEATH_RAY:
90 message = _("枯れた", "was blasted.");
92 case AttributeType::TIME:
93 message = _("縮んだ", "shrank.");
95 case AttributeType::ACID:
96 message = _("溶けた", "melted.");
98 case AttributeType::COLD:
99 case AttributeType::ICE:
100 message = _("凍り、砕け散った", "was frozen and smashed.");
102 case AttributeType::FIRE:
103 case AttributeType::ELEC:
104 case AttributeType::PLASMA:
105 message = _("燃えた", "burns up!");
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.");
120 case AttributeType::VOID_MAGIC:
121 message = _("消滅した", "vanished.");
129 msg_format(_("木は%s。", "A tree %s"), message);
130 cave_set_feat(player_ptr, y, x, one_in_(3) ? feat_brake : feat_grass);
133 if (grid.is_mark()) {
139 /* Analyze the type */
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: {
174 case AttributeType::KILL_TRAP: {
175 if (is_hidden_door(player_ptr, grid)) {
176 disclose_grid(player_ptr, y, x);
182 if (is_trap(player_ptr, grid.feat)) {
184 msg_print(_("まばゆい閃光が走った!", "There is a bright flash of light!"));
188 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::DISARM);
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!"));
200 if (player_ptr->effects()->blindness()->is_blind() || !grid.has_los()) {
204 grid.info &= ~(CAVE_UNSAFE);
205 lite_spot(player_ptr, y, x);
209 case AttributeType::KILL_DOOR: {
210 if (is_trap(player_ptr, grid.feat) || terrain.flags.has(TerrainCharacteristics::DOOR)) {
212 msg_print(_("まばゆい閃光が走った!", "There is a bright flash of light!"));
216 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::TUNNEL);
219 if (player_ptr->effects()->blindness()->is_blind() || !grid.has_los()) {
223 grid.info &= ~(CAVE_UNSAFE);
224 lite_spot(player_ptr, y, x);
228 case AttributeType::JAM_DOOR: {
229 if (terrain.flags.has_not(TerrainCharacteristics::SPIKE)) {
233 int16_t old_mimic = grid.mimic;
234 const auto &terrain_mimic = grid.get_terrain_mimic();
236 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::SPIKE);
237 grid.mimic = old_mimic;
239 note_spot(player_ptr, y, x);
240 lite_spot(player_ptr, y, x);
242 if (!known || terrain_mimic.flags.has_not(TerrainCharacteristics::OPEN)) {
246 msg_format(_("%sに何かがつっかえて開かなくなった。", "The %s seems stuck."), terrain_mimic.name.data());
250 case AttributeType::KILL_WALL: {
251 if (terrain.flags.has_not(TerrainCharacteristics::HURT_ROCK)) {
255 if (known && grid.is_mark()) {
256 msg_format(_("%sが溶けて泥になった!", "The %s turns into mud!"), grid.get_terrain_mimic().name.data());
260 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_ROCK);
261 RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::FLOW);
264 case AttributeType::MAKE_DOOR: {
265 if (!cave_naked_bold(player_ptr, pos)) {
268 if (player_ptr->is_located_at(pos)) {
271 cave_set_feat(player_ptr, y, x, feat_door[DOOR_DOOR].closed);
272 if (grid.is_mark()) {
277 case AttributeType::MAKE_TRAP: {
278 place_trap(&floor, y, x);
281 case AttributeType::MAKE_TREE: {
282 if (!cave_naked_bold(player_ptr, pos)) {
285 if (player_ptr->is_located_at(pos)) {
288 cave_set_feat(player_ptr, y, x, feat_tree);
289 if (grid.is_mark()) {
294 case AttributeType::MAKE_RUNE_PROTECTION: {
295 if (!cave_naked_bold(player_ptr, pos)) {
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);
304 case AttributeType::STONE_WALL: {
305 if (!cave_naked_bold(player_ptr, pos)) {
308 if (player_ptr->is_located_at(pos)) {
311 cave_set_feat(player_ptr, y, x, feat_granite);
314 case AttributeType::LAVA_FLOW: {
315 if (terrain.flags.has(TerrainCharacteristics::PERMANENT)) {
319 if (terrain.flags.has_not(TerrainCharacteristics::FLOOR)) {
322 cave_set_feat(player_ptr, y, x, feat_shallow_lava);
324 cave_set_feat(player_ptr, y, x, feat_deep_lava);
329 case AttributeType::WATER_FLOW: {
330 if (terrain.flags.has(TerrainCharacteristics::PERMANENT)) {
334 if (terrain.flags.has_not(TerrainCharacteristics::FLOOR)) {
337 cave_set_feat(player_ptr, y, x, feat_shallow_water);
339 cave_set_feat(player_ptr, y, x, feat_deep_water);
344 case AttributeType::LITE_WEAK:
345 case AttributeType::LITE: {
346 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
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);
355 if (player_can_see_bold(player_ptr, y, x)) {
358 if (grid.has_monster()) {
359 update_monster(player_ptr, grid.m_idx, false);
362 if (player_ptr->is_located_at(pos)) {
363 set_superstealth(player_ptr, false);
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();
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)) {
383 const auto &grid_neighbor = floor.get_grid(pos_neighbor);
384 if (grid_neighbor.get_terrain_mimic().flags.has(TerrainCharacteristics::GLOW)) {
395 grid.info &= ~(CAVE_GLOW);
397 /* Hack -- Forget "boring" grids */
398 if (terrain.flags.has_not(TerrainCharacteristics::REMEMBER) || has_element_resist(player_ptr, ElementRealmType::DARKNESS, 1)) {
400 grid.info &= ~(CAVE_MARK);
401 note_spot(player_ptr, y, x);
404 lite_spot(player_ptr, y, x);
406 update_local_illumination(player_ptr, y, x);
408 if (player_can_see_bold(player_ptr, y, x)) {
411 if (grid.has_monster()) {
412 update_monster(player_ptr, grid.m_idx, false);
417 case AttributeType::SHARDS:
418 case AttributeType::ROCKET: {
419 if (grid.is_mirror()) {
420 msg_print(_("鏡が割れた!", "The mirror was shattered!"));
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));
427 if (terrain.flags.has_not(TerrainCharacteristics::GLASS) || terrain.flags.has(TerrainCharacteristics::PERMANENT) || (dam < 50)) {
431 if (known && (grid.is_mark())) {
432 msg_format(_("%sが割れた!", "The %s crumbled!"), grid.get_terrain_mimic().name.data());
436 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_ROCK);
437 RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::FLOW);
440 case AttributeType::SOUND: {
441 if (grid.is_mirror() && player_ptr->lev < 40) {
442 msg_print(_("鏡が割れた!", "The mirror was shattered!"));
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));
449 if (terrain.flags.has_not(TerrainCharacteristics::GLASS) || terrain.flags.has(TerrainCharacteristics::PERMANENT) || (dam < 200)) {
453 if (known && (grid.is_mark())) {
454 msg_format(_("%sが割れた!", "The %s crumbled!"), grid.get_terrain_mimic().name.data());
458 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_ROCK);
459 RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::FLOW);
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);
467 if (terrain.flags.has_not(TerrainCharacteristics::HURT_DISI) || terrain.flags.has(TerrainCharacteristics::PERMANENT)) {
471 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_DISI);
472 RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::FLOW);
479 lite_spot(player_ptr, y, x);