OSDN Git Service

Merge branch 'master' of git.osdn.net:/gitroot/hengband/hengband
[hengband/hengband.git] / src / floor / floor-streams.c
1 /*!
2  * @brief ダンジョン生成に利用する関数群 / Used by dungeon generation.
3  * @date 2014/07/15
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.  Other copyrights may also apply.
9  * @details
10  * Purpose:  This file holds all the
11  * functions that are applied to a level after the rest has been
12  * generated, ie streams and level destruction.
13  */
14
15 #include "floor/floor-streams.h"
16 #include "dungeon/dungeon-flag-types.h"
17 #include "dungeon/dungeon.h"
18 #include "flavor/flavor-describer.h"
19 #include "flavor/object-flavor-types.h"
20 #include "floor/cave.h"
21 #include "floor/floor-generator-util.h"
22 #include "floor/floor-generator.h"
23 #include "floor/floor-object.h"
24 #include "game-option/birth-options.h"
25 #include "game-option/cheat-options.h"
26 #include "game-option/cheat-types.h"
27 #include "grid/feature.h"
28 #include "grid/grid.h"
29 #include "monster-race/monster-race.h"
30 #include "monster/monster-info.h"
31 #include "object-hook/hook-enchant.h"
32 #include "room/lake-types.h"
33 #include "spell-kind/spells-floor.h"
34 #include "system/artifact-type-definition.h"
35 #include "system/dungeon-data-definition.h"
36 #include "system/floor-type-definition.h"
37 #include "util/bit-flags-calculator.h"
38 #include "view/display-messages.h"
39 #include "wizard/wizard-messages.h"
40
41 /*!
42  * @brief 再帰フラクタルアルゴリズムによりダンジョン内に川を配置する /
43  * Recursive fractal algorithm to place water through the dungeon.
44  * @param x1 起点x座標
45  * @param y1 起点y座標
46  * @param x2 終点x座標
47  * @param y2 終点y座標
48  * @param feat1 中央部地形ID
49  * @param feat2 境界部地形ID
50  * @param width 基本幅
51  * @return なし
52  */
53 static void recursive_river(floor_type *floor_ptr, POSITION x1, POSITION y1, POSITION x2, POSITION y2, FEAT_IDX feat1, FEAT_IDX feat2, POSITION width)
54 {
55     POSITION dx, dy, length, l, x, y;
56     POSITION changex, changey;
57     POSITION ty, tx;
58     bool done;
59     grid_type *g_ptr;
60
61     length = distance(x1, y1, x2, y2);
62
63     if (length > 4) {
64         /*
65          * Divide path in half and call routine twice.
66          * There is a small chance of splitting the river
67          */
68         dx = (x2 - x1) / 2;
69         dy = (y2 - y1) / 2;
70
71         if (dy != 0) {
72             /* perturbation perpendicular to path */
73             changex = randint1(abs(dy)) * 2 - abs(dy);
74         } else {
75             changex = 0;
76         }
77
78         if (dx != 0) {
79             /* perturbation perpendicular to path */
80             changey = randint1(abs(dx)) * 2 - abs(dx);
81         } else {
82             changey = 0;
83         }
84
85         if (!in_bounds(floor_ptr, y1 + dy + changey, x1 + dx + changex)) {
86             changex = 0;
87             changey = 0;
88         }
89
90         /* construct river out of two smaller ones */
91         recursive_river(floor_ptr, x1, y1, x1 + dx + changex, y1 + dy + changey, feat1, feat2, width);
92         recursive_river(floor_ptr, x1 + dx + changex, y1 + dy + changey, x2, y2, feat1, feat2, width);
93
94         /* Split the river some of the time - junctions look cool */
95         if (one_in_(DUN_WAT_CHG) && (width > 0)) {
96             recursive_river(floor_ptr, x1 + dx + changex, y1 + dy + changey, x1 + 8 * (dx + changex), y1 + 8 * (dy + changey), feat1, feat2, width - 1);
97         }
98     } else {
99         /* Actually build the river */
100         for (l = 0; l < length; l++) {
101             x = x1 + l * (x2 - x1) / length;
102             y = y1 + l * (y2 - y1) / length;
103
104             done = FALSE;
105
106             while (!done) {
107                 for (ty = y - width - 1; ty <= y + width + 1; ty++) {
108                     for (tx = x - width - 1; tx <= x + width + 1; tx++) {
109                         if (!in_bounds2(floor_ptr, ty, tx))
110                             continue;
111
112                         g_ptr = &floor_ptr->grid_array[ty][tx];
113
114                         if (g_ptr->feat == feat1)
115                             continue;
116                         if (g_ptr->feat == feat2)
117                             continue;
118
119                         if (distance(ty, tx, y, x) > rand_spread(width, 1))
120                             continue;
121
122                         /* Do not convert permanent features */
123                         if (cave_have_flag_grid(g_ptr, FF_PERMANENT))
124                             continue;
125
126                         /*
127                          * Clear previous contents, add feature
128                          * The border mainly gets feat2, while the center gets feat1
129                          */
130                         if (distance(ty, tx, y, x) > width)
131                             g_ptr->feat = feat2;
132                         else
133                             g_ptr->feat = feat1;
134
135                         /* Clear garbage of hidden trap or door */
136                         g_ptr->mimic = 0;
137
138                         /* Lava terrain glows */
139                         if (have_flag(f_info[feat1].flags, FF_LAVA)) {
140                             if (!(d_info[floor_ptr->dungeon_idx].flags1 & DF1_DARKNESS))
141                                 g_ptr->info |= CAVE_GLOW;
142                         }
143
144                         /* Hack -- don't teleport here */
145                         g_ptr->info |= CAVE_ICKY;
146                     }
147                 }
148
149                 done = TRUE;
150             }
151         }
152     }
153 }
154
155 /*!
156  * @brief ランダムに川/溶岩流をダンジョンに配置する /
157  * Places water /lava through dungeon.
158  * @param feat1 中央部地形ID
159  * @param feat2 境界部地形ID
160  * @return なし
161  */
162 void add_river(floor_type *floor_ptr, dun_data_type *dd_ptr)
163 {
164     dungeon_type *dungeon_ptr;
165     POSITION y2, x2;
166     POSITION y1 = 0, x1 = 0;
167     POSITION wid;
168     FEAT_IDX feat1 = 0, feat2 = 0;
169
170     dungeon_ptr = &d_info[floor_ptr->dungeon_idx];
171
172     /* Choose water mainly */
173     if ((randint1(MAX_DEPTH * 2) - 1 > floor_ptr->dun_level) && (dungeon_ptr->flags1 & DF1_WATER_RIVER)) {
174         feat1 = feat_deep_water;
175         feat2 = feat_shallow_water;
176     } else /* others */
177     {
178         FEAT_IDX select_deep_feat[10];
179         FEAT_IDX select_shallow_feat[10];
180         int select_id_max = 0, selected;
181
182         if (dungeon_ptr->flags1 & DF1_LAVA_RIVER) {
183             select_deep_feat[select_id_max] = feat_deep_lava;
184             select_shallow_feat[select_id_max] = feat_shallow_lava;
185             select_id_max++;
186         }
187         if (dungeon_ptr->flags1 & DF1_POISONOUS_RIVER) {
188             select_deep_feat[select_id_max] = feat_deep_poisonous_puddle;
189             select_shallow_feat[select_id_max] = feat_shallow_poisonous_puddle;
190             select_id_max++;
191         }
192         if (dungeon_ptr->flags1 & DF1_ACID_RIVER) {
193             select_deep_feat[select_id_max] = feat_deep_acid_puddle;
194             select_shallow_feat[select_id_max] = feat_shallow_acid_puddle;
195             select_id_max++;
196         }
197
198         if (select_id_max > 0) {
199             selected = randint0(select_id_max);
200             feat1 = select_deep_feat[selected];
201             feat2 = select_shallow_feat[selected];
202         } else {
203             return;
204         }
205     }
206
207     if (feat1) {
208         feature_type *f_ptr = &f_info[feat1];
209
210         /* Only add river if matches lake type or if have no lake at all */
211         if (!(((dd_ptr->laketype == LAKE_T_LAVA) && have_flag(f_ptr->flags, FF_LAVA))
212                 || ((dd_ptr->laketype == LAKE_T_WATER) && have_flag(f_ptr->flags, FF_WATER)) || !dd_ptr->laketype)) {
213             return;
214         }
215     }
216
217     /* Hack -- Choose starting point */
218     y2 = randint1(floor_ptr->height / 2 - 2) + floor_ptr->height / 2;
219     x2 = randint1(floor_ptr->width / 2 - 2) + floor_ptr->width / 2;
220
221     /* Hack -- Choose ending point somewhere on boundary */
222     switch (randint1(4)) {
223     case 1: {
224         /* top boundary */
225         x1 = randint1(floor_ptr->width - 2) + 1;
226         y1 = 1;
227         break;
228     }
229     case 2: {
230         /* left boundary */
231         x1 = 1;
232         y1 = randint1(floor_ptr->height - 2) + 1;
233         break;
234     }
235     case 3: {
236         /* right boundary */
237         x1 = floor_ptr->width - 1;
238         y1 = randint1(floor_ptr->height - 2) + 1;
239         break;
240     }
241     case 4: {
242         /* bottom boundary */
243         x1 = randint1(floor_ptr->width - 2) + 1;
244         y1 = floor_ptr->height - 1;
245         break;
246     }
247     }
248
249     wid = randint1(DUN_WAT_RNG);
250     recursive_river(floor_ptr, x1, y1, x2, y2, feat1, feat2, wid);
251
252     /* Hack - Save the location as a "room" */
253     if (dd_ptr->cent_n < CENT_MAX) {
254         dd_ptr->cent[dd_ptr->cent_n].y = y2;
255         dd_ptr->cent[dd_ptr->cent_n].x = x2;
256         dd_ptr->cent_n++;
257     }
258 }
259
260 /*!
261  * @brief ダンジョンの壁部にストリーマー(地質の変化)を与える /
262  * Places "streamers" of rock through dungeon
263  * @param player_ptr プレーヤーへの参照ポインタ
264  * @param feat ストリーマー地形ID
265  * @param chance 生成密度
266  * @return なし
267  * @details
268  * <pre>
269  * Note that their are actually six different terrain features used
270  * to represent streamers.  Three each of magma and quartz, one for
271  * basic vein, one with hidden gold, and one with known gold.  The
272  * hidden gold types are currently unused.
273  * </pre>
274  */
275 void build_streamer(player_type *player_ptr, FEAT_IDX feat, int chance)
276 {
277     int i;
278     POSITION y, x, tx, ty;
279     DIRECTION dir;
280     int dummy = 0;
281
282     grid_type *g_ptr;
283     feature_type *f_ptr;
284
285     feature_type *streamer_ptr = &f_info[feat];
286     bool streamer_is_wall = have_flag(streamer_ptr->flags, FF_WALL) && !have_flag(streamer_ptr->flags, FF_PERMANENT);
287     bool streamer_may_have_gold = have_flag(streamer_ptr->flags, FF_MAY_HAVE_GOLD);
288
289     /* Hack -- Choose starting point */
290     floor_type *floor_ptr = player_ptr->current_floor_ptr;
291     y = rand_spread(floor_ptr->height / 2, floor_ptr->height / 6);
292     x = rand_spread(floor_ptr->width / 2, floor_ptr->width / 6);
293
294     /* Choose a random compass direction */
295     dir = randint0(8);
296
297     /* Place streamer into dungeon */
298     while (dummy < SAFE_MAX_ATTEMPTS) {
299         dummy++;
300
301         /* One grid per density */
302         for (i = 0; i < DUN_STR_DEN; i++) {
303             int d = DUN_STR_RNG;
304
305             /* Pick a nearby grid */
306             while (TRUE) {
307                 ty = rand_spread(y, d);
308                 tx = rand_spread(x, d);
309                 if (!in_bounds2(floor_ptr, ty, tx))
310                     continue;
311                 break;
312             }
313             g_ptr = &floor_ptr->grid_array[ty][tx];
314             f_ptr = &f_info[g_ptr->feat];
315
316             if (have_flag(f_ptr->flags, FF_MOVE) && (have_flag(f_ptr->flags, FF_WATER) || have_flag(f_ptr->flags, FF_LAVA)))
317                 continue;
318
319             /* Do not convert permanent features */
320             if (have_flag(f_ptr->flags, FF_PERMANENT))
321                 continue;
322
323             /* Only convert "granite" walls */
324             if (streamer_is_wall) {
325                 if (!is_extra_grid(g_ptr) && !is_inner_grid(g_ptr) && !is_outer_grid(g_ptr) && !is_solid_grid(g_ptr))
326                     continue;
327                 if (is_closed_door(player_ptr, g_ptr->feat))
328                     continue;
329             }
330
331             if (g_ptr->m_idx
332                 && !(have_flag(streamer_ptr->flags, FF_PLACE)
333                     && monster_can_cross_terrain(player_ptr, feat, &r_info[floor_ptr->m_list[g_ptr->m_idx].r_idx], 0))) {
334                 /* Delete the monster (if any) */
335                 delete_monster(player_ptr, ty, tx);
336             }
337
338             if (g_ptr->o_idx && !have_flag(streamer_ptr->flags, FF_DROP)) {
339                 OBJECT_IDX this_o_idx, next_o_idx = 0;
340
341                 /* Scan all objects in the grid */
342                 for (this_o_idx = g_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) {
343                     object_type *o_ptr = &floor_ptr->o_list[this_o_idx];
344                     next_o_idx = o_ptr->next_o_idx;
345
346                     /* Hack -- Preserve unknown artifacts */
347                     if (object_is_fixed_artifact(o_ptr)) {
348                         /* Mega-Hack -- Preserve the artifact */
349                         a_info[o_ptr->name1].cur_num = 0;
350
351                         if (cheat_peek) {
352                             GAME_TEXT o_name[MAX_NLEN];
353                             describe_flavor(player_ptr, o_name, o_ptr, (OD_NAME_ONLY | OD_STORE));
354                             msg_format(_("伝説のアイテム (%s) はストリーマーにより削除された。", "Artifact (%s) was deleted by streamer."), o_name);
355                         }
356                     } else if (cheat_peek && o_ptr->art_name) {
357                         msg_print(_("ランダム・アーティファクトの1つはストリーマーにより削除された。", "One of the random artifacts was deleted by streamer."));
358                     }
359                 }
360
361                 delete_all_items_from_floor(player_ptr, ty, tx);
362             }
363
364             /* Clear previous contents, add proper vein type */
365             g_ptr->feat = feat;
366
367             /* Paranoia: Clear mimic field */
368             g_ptr->mimic = 0;
369
370             if (streamer_may_have_gold) {
371                 /* Hack -- Add some known treasure */
372                 if (one_in_(chance)) {
373                     cave_alter_feat(player_ptr, ty, tx, FF_MAY_HAVE_GOLD);
374                 }
375
376                 /* Hack -- Add some hidden treasure */
377                 else if (one_in_(chance / 4)) {
378                     cave_alter_feat(player_ptr, ty, tx, FF_MAY_HAVE_GOLD);
379                     cave_alter_feat(player_ptr, ty, tx, FF_ENSECRET);
380                 }
381             }
382         }
383
384         if (dummy >= SAFE_MAX_ATTEMPTS) {
385             msg_print_wizard(player_ptr, CHEAT_DUNGEON, _("地形のストリーマー処理に失敗しました。", "Failed to place streamer."));
386             return;
387         }
388
389         /* Advance the streamer */
390         y += ddy[cdd[dir]];
391         x += ddx[cdd[dir]];
392
393         if (one_in_(10)) {
394             if (one_in_(2))
395                 dir = (dir + 1) % 8;
396             else
397                 dir = (dir > 0) ? dir - 1 : 7;
398         }
399
400         /* Quit before leaving the dungeon */
401         if (!in_bounds(floor_ptr, y, x))
402             break;
403     }
404 }
405
406 /*!
407  * @brief ダンジョンの指定位置近辺に森林を配置する /
408  * Places "streamers" of rock through dungeon
409  * @param x 指定X座標
410  * @param y 指定Y座標
411  * @return なし
412  * @details
413  * <pre>
414  * Put trees near a hole in the dungeon roof  (rubble on ground + up stairway)
415  * This happens in real world lava tubes.
416  * </pre>
417  */
418 void place_trees(player_type *player_ptr, POSITION x, POSITION y)
419 {
420     int i, j;
421     grid_type *g_ptr;
422
423     /* place trees/ rubble in ovalish distribution */
424     floor_type *floor_ptr = player_ptr->current_floor_ptr;
425     for (i = x - 3; i < x + 4; i++) {
426         for (j = y - 3; j < y + 4; j++) {
427             if (!in_bounds(floor_ptr, j, i))
428                 continue;
429             g_ptr = &floor_ptr->grid_array[j][i];
430
431             if (g_ptr->info & CAVE_ICKY)
432                 continue;
433             if (g_ptr->o_idx)
434                 continue;
435
436             /* Want square to be in the circle and accessable. */
437             if ((distance(j, i, y, x) < 4) && !cave_have_flag_grid(g_ptr, FF_PERMANENT)) {
438                 /*
439                  * Clear previous contents, add feature
440                  * The border mainly gets trees, while the center gets rubble
441                  */
442                 if ((distance(j, i, y, x) > 1) || (randint1(100) < 25)) {
443                     if (randint1(100) < 75)
444                         floor_ptr->grid_array[j][i].feat = feat_tree;
445                 } else {
446                     floor_ptr->grid_array[j][i].feat = feat_rubble;
447                 }
448
449                 /* Clear garbage of hidden trap or door */
450                 g_ptr->mimic = 0;
451
452                 /* Light area since is open above */
453                 if (!(d_info[player_ptr->dungeon_idx].flags1 & DF1_DARKNESS))
454                     floor_ptr->grid_array[j][i].info |= (CAVE_GLOW | CAVE_ROOM);
455             }
456         }
457     }
458
459     /* No up stairs in ironman mode */
460     if (!ironman_downward && one_in_(3)) {
461         /* up stair */
462         floor_ptr->grid_array[y][x].feat = feat_up_stair;
463     }
464 }
465
466 /*!
467  * @brief ダンジョンに*破壊*済み地形ランダムに施す /
468  * Build a destroyed level
469  * @return なし
470  */
471 void destroy_level(player_type *player_ptr)
472 {
473     msg_print_wizard(player_ptr, CHEAT_DUNGEON, _("階に*破壊*の痕跡を生成しました。", "Destroyed Level."));
474
475     /* Drop a few epi-centers (usually about two) */
476     POSITION y1, x1;
477     floor_type *floor_ptr = player_ptr->current_floor_ptr;
478     for (int n = 0; n < randint1(5); n++) {
479         /* Pick an epi-center */
480         x1 = rand_range(5, floor_ptr->width - 1 - 5);
481         y1 = rand_range(5, floor_ptr->height - 1 - 5);
482
483         (void)destroy_area(player_ptr, y1, x1, 15, TRUE);
484     }
485 }