OSDN Git Service

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