ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.36
Committed: Tue Apr 15 03:00:24 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_5, rel-2_52
Changes since 1.35: +16 -21 lines
Log Message:
new logging thread, fix bugs in random map generator

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2001,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Deliantra is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <support@deliantra.net>
22 */
23
24 /* placing treasure in maps, where appropriate. */
25
26 #include <global.h>
27 #include <random_map.h>
28 #include <rproto.h>
29
30 /* some defines for various options which can be set. */
31
32 #define CONCENTRATED 1 /* all the treasure is at the C's for onions. */
33 #define HIDDEN 2 /* doors to treasure are hidden. */
34 #define KEYREQUIRED 4 /* chest has a key, which is placed randomly in the map. */
35 #define DOORED 8 /* treasure has doors around it. */
36 #define TRAPPED 16 /* trap dropped in same location as chest. */
37 #define SPARSE 32 /* 1/2 as much treasure as default */
38 #define RICH 64 /* 2x as much treasure as default */
39 #define FILLED 128 /* Fill/tile the entire map with treasure */
40 #define LAST_OPTION 64 /* set this to the last real option, for random */
41
42 #define NO_PASS_DOORS 0
43 #define PASS_DOORS 1
44
45 /* a macro to get a strongly centered random distribution,
46 from 0 to x, centered at x/2 */
47 static int
48 bc_random (int x)
49 {
50 return (rndm (x) + rndm (x) + rndm (x)) / 3;
51 }
52
53 /* returns true if square x,y has P_NO_PASS set, which is true for walls
54 * and doors but not monsters.
55 * This function is not map tile aware.
56 */
57 int
58 wall_blocked (maptile *m, int x, int y)
59 {
60 if (OUT_OF_REAL_MAP (m, x, y))
61 return 1;
62
63 m->at (x, y).update ();
64 return GET_MAP_MOVE_BLOCK (m, x, y) & MOVE_WALK;
65 }
66
67 /* place treasures in the map, given the
68 map, (required)
69 layout, (required)
70 treasure style (may be empty or NULL, or "none" to cause no treasure.)
71 treasureoptions (may be 0 for random choices or positive)
72 */
73 void
74 place_treasure (maptile *map, char **layout, char *treasure_style, int treasureoptions, random_map_params *RP)
75 {
76 char styledirname[1024];
77 char stylefilepath[1024];
78 maptile *style_map = 0;
79 int num_treasures;
80
81 /* bail out if treasure isn't wanted. */
82 if (treasure_style)
83 if (!strcmp (treasure_style, "none"))
84 return;
85
86 if (treasureoptions <= 0)
87 treasureoptions = rndm (2 * LAST_OPTION);
88
89 /* filter out the mutually exclusive options */
90 if ((treasureoptions & RICH) && (treasureoptions & SPARSE))
91 {
92 if (rndm (2))
93 treasureoptions -= 1;
94 else
95 treasureoptions -= 2;
96 }
97
98 /* pick the number of treasures */
99 if (treasureoptions & SPARSE)
100 num_treasures = bc_random (RP->total_map_hp / 600 + RP->difficulty / 2 + 1);
101 else if (treasureoptions & RICH)
102 num_treasures = bc_random (RP->total_map_hp / 150 + 2 * RP->difficulty + 1);
103 else
104 num_treasures = bc_random (RP->total_map_hp / 300 + RP->difficulty + 1);
105
106 if (num_treasures <= 0)
107 return;
108
109 /* get the style map */
110 sprintf (styledirname, "%s", "/styles/treasurestyles");
111 sprintf (stylefilepath, "%s/%s", styledirname, treasure_style);
112 style_map = find_style (styledirname, treasure_style, -1);
113
114 if (!style_map)
115 {
116 LOG (llevError, "unable to load style map %s %s.\n", styledirname, treasure_style);
117 return;
118 }
119
120 /* all the treasure at one spot in the map. */
121 if (treasureoptions & CONCENTRATED)
122 {
123 /* map_layout_style global, and is previously set */
124 switch (RP->map_layout_style)
125 {
126 case LAYOUT_ONION:
127 case LAYOUT_SPIRAL:
128 case LAYOUT_SQUARE_SPIRAL:
129 {
130 int i, j;
131
132 /* search the onion for C's or '>', and put treasure there. */
133 for (i = 0; i < RP->Xsize; i++)
134 {
135 for (j = 0; j < RP->Ysize; j++)
136 {
137 if (layout[i][j] == 'C' || layout[i][j] == '>')
138 {
139 int tdiv = RP->symmetry_used;
140 object *chest;
141
142 if (tdiv == 3)
143 tdiv = 2; /* this symmetry uses a divisor of 2 */
144
145 /* don't put a chest on an exit. */
146 chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures / tdiv, RP);
147
148 if (!chest)
149 continue; /* if no chest was placed NEXT */
150
151 if (treasureoptions & (DOORED | HIDDEN))
152 {
153 object **doorlist = find_doors_in_room (map, i, j, RP);
154 lock_and_hide_doors (doorlist, map, treasureoptions, RP);
155 free (doorlist);
156 }
157 }
158 }
159 }
160 break;
161 }
162 default:
163 {
164 int i, j, tries;
165 object *chest;
166 object **doorlist;
167
168 i = j = -1;
169 tries = 0;
170 while (i == -1 && tries < 100)
171 {
172 i = rndm (RP->Xsize - 2) + 1;
173 j = rndm (RP->Ysize - 2) + 1;
174 find_enclosed_spot (map, &i, &j, RP);
175
176 if (wall_blocked (map, i, j))
177 i = -1;
178
179 tries++;
180 }
181
182 chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures, RP);
183
184 if (!chest)
185 return;
186
187 i = chest->x;
188 j = chest->y;
189 if (treasureoptions & (DOORED | HIDDEN))
190 {
191 doorlist = surround_by_doors (map, layout, i, j, treasureoptions);
192 lock_and_hide_doors (doorlist, map, treasureoptions, RP);
193 free (doorlist);
194 }
195 }
196 }
197 }
198 else
199 { /* DIFFUSE treasure layout */
200 int ti, i, j;
201
202 for (ti = 0; ti < num_treasures; ti++)
203 {
204 i = rndm (RP->Xsize - 2) + 1;
205 j = rndm (RP->Ysize - 2) + 1;
206 place_chest (treasureoptions, i, j, map, style_map, 1, RP);
207 }
208 }
209 }
210
211 /* put a chest into the map, near x and y, with the treasure style
212 determined (may be null, or may be a treasure list from lib/treasures,
213 if the global variable "treasurestyle" is set to that treasure list's name */
214 object *
215 place_chest (int treasureoptions, int x, int y, maptile *map, maptile *style_map, int n_treasures, random_map_params *RP)
216 {
217 object *the_chest;
218 int i, xl, yl;
219
220 the_chest = get_archetype ("chest"); /* was "chest_2" */
221
222 /* first, find a place to put the chest. */
223 i = find_first_free_spot (the_chest, map, x, y);
224 if (i == -1)
225 {
226 the_chest->destroy ();
227 return NULL;
228 }
229
230 xl = x + freearr_x[i];
231 yl = y + freearr_y[i];
232
233 /* if the placement is blocked, return a fail. */
234 if (wall_blocked (map, xl, yl))
235 return 0;
236
237 /* put the treasures in the chest. */
238 /* if(style_map) { */
239 #if 0 /* don't use treasure style maps for now! */
240 int ti;
241
242 /* if treasurestyle lists a treasure list, use it. */
243 treasurelist *tlist = find_treasurelist (RP->treasurestyle);
244
245 if (tlist != NULL)
246 for (ti = 0; ti < n_treasures; ti++)
247 { /* use the treasure list */
248 object *new_treasure = style_map->pick_random_object ();
249
250 insert_ob_in_ob (arch_to_object (new_treasure->arch), the_chest);
251 }
252 else
253 { /* use the style map */
254 the_chest->randomitems = tlist;
255 the_chest->stats.hp = n_treasures;
256 }
257 #endif
258 { /* neither style_map no treasure list given */
259 treasurelist *tlist = treasurelist::find ("chest");
260
261 the_chest->randomitems = tlist;
262 the_chest->stats.hp = n_treasures;
263 }
264
265 /* stick a trap in the chest if required */
266 if (treasureoptions & TRAPPED)
267 {
268 maptile *trap_map = find_style ("/styles/trapstyles", "traps", -1);
269 object *the_trap;
270
271 if (trap_map)
272 {
273 the_trap = trap_map->pick_random_object ();
274 the_trap->stats.Cha = 10 + RP->difficulty;
275 the_trap->level = bc_random ((3 * RP->difficulty) / 2);
276 if (the_trap)
277 {
278 object *new_trap;
279
280 new_trap = arch_to_object (the_trap->arch);
281 new_trap->copy_to (the_trap);
282 new_trap->x = x;
283 new_trap->y = y;
284 insert_ob_in_ob (new_trap, the_chest);
285 }
286 }
287 }
288
289 /* set the chest lock code, and call the keyplacer routine with
290 the lockcode. It's not worth bothering to lock the chest if
291 there's only 1 treasure.... */
292 if ((treasureoptions & KEYREQUIRED) && n_treasures > 1)
293 {
294 char keybuf[1024];
295
296 sprintf (keybuf, "%d", rndm (1000000000));
297 the_chest->slaying = keybuf;
298 keyplace (map, x, y, keybuf, PASS_DOORS, 1, RP);
299 }
300
301 /* actually place the chest. */
302 the_chest->x = xl;
303 the_chest->y = yl;
304 insert_ob_in_map (the_chest, map, NULL, 0);
305 return the_chest;
306 }
307
308
309 /* finds the closest monster and returns him, regardless of doors
310 or walls */
311 object *
312 find_closest_monster (maptile *map, int x, int y, random_map_params *RP)
313 {
314 int i;
315
316 for (i = 0; i < SIZEOFFREE; i++)
317 {
318 int lx, ly;
319
320 lx = x + freearr_x[i];
321 ly = y + freearr_y[i];
322 /* boundscheck */
323 if (lx >= 0 && ly >= 0 && lx < RP->Xsize && ly < RP->Ysize)
324 /* don't bother searching this square unless the map says life exists. */
325 if (GET_MAP_FLAGS (map, lx, ly) & P_IS_ALIVE)
326 {
327 object *the_monster = GET_MAP_OB (map, lx, ly);
328
329 for (; the_monster != NULL && (!QUERY_FLAG (the_monster, FLAG_MONSTER)); the_monster = the_monster->above);
330 if (the_monster && QUERY_FLAG (the_monster, FLAG_MONSTER))
331 return the_monster;
332 }
333 }
334 return NULL;
335 }
336
337
338
339 /* places keys in the map, preferably in something alive.
340 keycode is the key's code,
341 door_flag is either PASS_DOORS or NO_PASS_DOORS.
342 NO_PASS_DOORS won't cross doors or walls to keyplace, PASS_DOORS will.
343 if n_keys is 1, it will place 1 key. if n_keys >1, it will place 2-4 keys:
344 it will place 2-4 keys regardless of what nkeys is provided nkeys > 1.
345
346 The idea is that you call keyplace on x,y where a door is, and it'll make
347 sure a key is placed on both sides of the door.
348 */
349 int
350 keyplace (maptile *map, int x, int y, char *keycode, int door_flag, int n_keys, random_map_params *RP)
351 {
352 int i, j;
353 int kx = 0, ky = 0;
354 object *the_keymaster; /* the monster that gets the key. */
355 object *the_key;
356
357 /* get a key and set its keycode */
358 the_key = get_archetype ("key2");
359 the_key->slaying = keycode;
360
361 if (door_flag == PASS_DOORS)
362 {
363 int tries = 0;
364
365 the_keymaster = 0;
366 while (tries < 15 && !the_keymaster)
367 {
368 i = rndm (RP->Xsize - 2) + 1;
369 j = rndm (RP->Ysize - 2) + 1;
370 tries++;
371 the_keymaster = find_closest_monster (map, i, j, RP);
372 }
373
374 /* if we don't find a good keymaster, drop the key on the ground. */
375 if (!the_keymaster)
376 {
377 int freeindex;
378
379 freeindex = -1;
380 for (tries = 0; tries < 15 && freeindex == -1; tries++)
381 {
382 kx = rndm (RP->Xsize - 2) + 1;
383 ky = rndm (RP->Ysize - 2) + 1;
384 freeindex = find_free_spot (the_key, map, kx, ky, 1, SIZEOFFREE1 + 1);
385 }
386
387 // can freeindex ever be < 0?
388 if (freeindex >= 0)
389 {
390 kx += freearr_x [freeindex];
391 ky += freearr_y [freeindex];
392 }
393 }
394 }
395 else
396 { /* NO_PASS_DOORS --we have to work harder. */
397 /* don't try to keyplace if we're sitting on a blocked square and
398 NO_PASS_DOORS is set. */
399 if (n_keys == 1)
400 {
401 if (wall_blocked (map, x, y))
402 return 0;
403
404 the_keymaster = find_monster_in_room (map, x, y, RP);
405 if (!the_keymaster) /* if fail, find a spot to drop the key. */
406 find_spot_in_room (map, x, y, &kx, &ky, RP);
407 }
408 else
409 {
410 int sum = 0; /* count how many keys we actually place */
411
412 /* I'm lazy, so just try to place in all 4 directions. */
413 sum += keyplace (map, x + 1, y, keycode, NO_PASS_DOORS, 1, RP);
414 sum += keyplace (map, x, y + 1, keycode, NO_PASS_DOORS, 1, RP);
415 sum += keyplace (map, x - 1, y, keycode, NO_PASS_DOORS, 1, RP);
416 sum += keyplace (map, x, y - 1, keycode, NO_PASS_DOORS, 1, RP);
417
418 if (sum < 2) /* we might have made a disconnected map-place more keys. */
419 { /* diagonally this time. */
420 keyplace (map, x + 1, y + 1, keycode, NO_PASS_DOORS, 1, RP);
421 keyplace (map, x + 1, y - 1, keycode, NO_PASS_DOORS, 1, RP);
422 keyplace (map, x - 1, y + 1, keycode, NO_PASS_DOORS, 1, RP);
423 keyplace (map, x - 1, y - 1, keycode, NO_PASS_DOORS, 1, RP);
424 }
425
426 return 1;
427 }
428 }
429
430 if (!the_keymaster)
431 {
432 the_key->x = kx;
433 the_key->y = ky;
434 insert_ob_in_map (the_key, map, NULL, 0);
435 return 1;
436 }
437
438 insert_ob_in_ob (the_key, the_keymaster->head_ ());
439 return 1;
440 }
441
442
443
444 /* both find_monster_in_room routines need to have access to this. */
445
446 object *theMonsterToFind;
447
448 /* a recursive routine which will return a monster, eventually,if there is one.
449 it does a check-off on the layout, converting 0's to 1's */
450
451 object *
452 find_monster_in_room_recursive (char **layout, maptile *map, int x, int y, random_map_params *RP)
453 {
454 int i, j;
455
456 /* if we've found a monster already, leave */
457 if (theMonsterToFind != NULL)
458 return theMonsterToFind;
459
460 /* bounds check x and y */
461 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
462 return theMonsterToFind;
463
464 /* if the square is blocked or searched already, leave */
465 if (layout[x][y] != 0)
466 return theMonsterToFind; /* might be NULL, that's fine. */
467
468 /* check the current square for a monster. If there is one,
469 set theMonsterToFind and return it. */
470 layout[x][y] = 1;
471 if (GET_MAP_FLAGS (map, x, y) & P_IS_ALIVE)
472 {
473 object *the_monster = GET_MAP_OB (map, x, y);
474
475 /* check off this point */
476 for (; the_monster != NULL && (!QUERY_FLAG (the_monster, FLAG_ALIVE)); the_monster = the_monster->above);
477 if (the_monster && QUERY_FLAG (the_monster, FLAG_ALIVE))
478 {
479 theMonsterToFind = the_monster;
480 return theMonsterToFind;
481 }
482 }
483
484 /* now search all the 8 squares around recursively for a monster,in random order */
485 for (i = rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
486 {
487 theMonsterToFind = find_monster_in_room_recursive (layout, map, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
488 if (theMonsterToFind != NULL)
489 return theMonsterToFind;
490 }
491
492 return theMonsterToFind;
493 }
494
495 /* sets up some data structures: the _recursive form does the
496 real work. */
497 object *
498 find_monster_in_room (maptile *map, int x, int y, random_map_params *RP)
499 {
500 Layout layout2 (RP);
501
502 layout2->clear ();
503
504 /* allocate and copy the layout, converting C to 0. */
505 for (int i = 0; i < layout2->w; i++)
506 for (int j = 0; j < layout2->h; j++)
507 if (wall_blocked (map, i, j))
508 layout2[i][j] = '#';
509
510 theMonsterToFind = 0;
511 theMonsterToFind = find_monster_in_room_recursive (layout2, map, x, y, RP);
512
513 layout2.free ();
514
515 return theMonsterToFind;
516 }
517
518 /* a datastructure needed by find_spot_in_room and find_spot_in_room_recursive */
519 int *room_free_spots_x;
520 int *room_free_spots_y;
521 int number_of_free_spots_in_room;
522
523 /* the workhorse routine, which finds the free spots in a room:
524 a datastructure of free points is set up, and a position chosen from
525 that datastructure. */
526 void
527 find_spot_in_room_recursive (char **layout, int x, int y, random_map_params *RP)
528 {
529 int i, j;
530
531 /* bounds check x and y */
532 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
533 return;
534
535 /* if the square is blocked or searched already, leave */
536 if (layout[x][y] != 0)
537 return;
538
539 /* set the current square as checked, and add it to the list.
540 set theMonsterToFind and return it. */
541 /* check off this point */
542 layout[x][y] = 1;
543 room_free_spots_x[number_of_free_spots_in_room] = x;
544 room_free_spots_y[number_of_free_spots_in_room] = y;
545 number_of_free_spots_in_room++;
546
547 /* now search all the 8 squares around recursively for free spots,in random order */
548 for (i = rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
549 find_spot_in_room_recursive (layout, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
550
551 }
552
553 /* find a random non-blocked spot in this room to drop a key. */
554 void
555 find_spot_in_room (maptile *map, int x, int y, int *kx, int *ky, random_map_params *RP)
556 {
557 char **layout2;
558 int i, j;
559
560 number_of_free_spots_in_room = 0;
561 room_free_spots_x = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
562 room_free_spots_y = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
563
564 layout2 = (char **) calloc (sizeof (char *), RP->Xsize);
565 /* allocate and copy the layout, converting C to 0. */
566 for (i = 0; i < RP->Xsize; i++)
567 {
568 layout2[i] = (char *) calloc (sizeof (char), RP->Ysize);
569 for (j = 0; j < RP->Ysize; j++)
570 if (wall_blocked (map, i, j))
571 layout2[i][j] = '#';
572 }
573
574 /* setup num_free_spots and room_free_spots */
575 find_spot_in_room_recursive (layout2, x, y, RP);
576
577 if (number_of_free_spots_in_room > 0)
578 {
579 i = rndm (number_of_free_spots_in_room);
580 *kx = room_free_spots_x[i];
581 *ky = room_free_spots_y[i];
582 }
583
584 /* deallocate the temp. layout */
585 for (i = 0; i < RP->Xsize; i++)
586 free (layout2[i]);
587
588 free (layout2);
589 free (room_free_spots_x);
590 free (room_free_spots_y);
591 }
592
593
594 /* searches the map for a spot with walls around it. The more
595 walls the better, but it'll settle for 1 wall, or even 0, but
596 it'll return 0 if no FREE spots are found.*/
597 void
598 find_enclosed_spot (maptile *map, int *cx, int *cy, random_map_params *RP)
599 {
600 int x, y;
601 int i;
602
603 x = *cx;
604 y = *cy;
605
606 for (i = 0; i <= SIZEOFFREE1; i++)
607 {
608 int lx, ly, sindex;
609
610 lx = x + freearr_x[i];
611 ly = y + freearr_y[i];
612 sindex = surround_flag3 (map, lx, ly, RP);
613 /* if it's blocked on 3 sides, it's enclosed */
614 if (sindex == 7 || sindex == 11 || sindex == 13 || sindex == 14)
615 {
616 *cx = lx;
617 *cy = ly;
618 return;
619 }
620 }
621
622 /* OK, if we got here, we're obviously someplace where there's no enclosed
623 spots--try to find someplace which is 2x enclosed. */
624 for (i = 0; i <= SIZEOFFREE1; i++)
625 {
626 int lx, ly, sindex;
627
628 lx = x + freearr_x[i];
629 ly = y + freearr_y[i];
630 sindex = surround_flag3 (map, lx, ly, RP);
631 /* if it's blocked on 3 sides, it's enclosed */
632 if (sindex == 3 || sindex == 5 || sindex == 9 || sindex == 6 || sindex == 10 || sindex == 12)
633 {
634 *cx = lx;
635 *cy = ly;
636 return;
637 }
638 }
639
640 /* settle for one surround point */
641 for (i = 0; i <= SIZEOFFREE1; i++)
642 {
643 int lx, ly, sindex;
644
645 lx = x + freearr_x[i];
646 ly = y + freearr_y[i];
647 sindex = surround_flag3 (map, lx, ly, RP);
648 /* if it's blocked on 3 sides, it's enclosed */
649 if (sindex)
650 {
651 *cx = lx;
652 *cy = ly;
653 return;
654 }
655 }
656 /* give up and return the closest free spot. */
657 i = find_free_spot (archetype::find ("chest"), map, x, y, 1, SIZEOFFREE1 + 1);
658
659 if (i != -1)
660 {
661 *cx = x + freearr_x[i];
662 *cy = y + freearr_y[i];
663 }
664 else
665 {
666 /* indicate failure */
667 *cx = -1;
668 *cy = -1;
669 }
670 }
671
672 void
673 remove_monsters (int x, int y, maptile *map)
674 {
675 object *tmp;
676
677 for (tmp = GET_MAP_OB (map, x, y); tmp; tmp = tmp->above)
678 if (QUERY_FLAG (tmp, FLAG_ALIVE))
679 {
680 if (tmp->head)
681 tmp = tmp->head;
682 tmp->remove ();
683 tmp->destroy ();
684 tmp = GET_MAP_OB (map, x, y);
685 if (tmp == NULL)
686 break;
687 };
688 }
689
690 /* surrounds the point x,y by doors, so as to enclose something, like
691 a chest. It only goes as far as the 8 squares surrounding, and
692 it'll remove any monsters it finds.*/
693 object **
694 surround_by_doors (maptile *map, char **layout, int x, int y, int opts)
695 {
696 int i;
697 const char *doors[2];
698 object **doorlist;
699 int ndoors_made = 0;
700 doorlist = (object **) calloc (9, sizeof (object *)); /* 9 doors so we can hold termination null */
701
702 /* this is a list we pick from, for horizontal and vertical doors */
703 if (opts & DOORED)
704 {
705 doors[0] = "locked_door2";
706 doors[1] = "locked_door1";
707 }
708 else
709 {
710 doors[0] = "door_1";
711 doors[1] = "door_2";
712 }
713
714 /* place doors in all the 8 adjacent unblocked squares. */
715 for (i = 1; i < 9; i++)
716 {
717 int x1 = x + freearr_x[i], y1 = y + freearr_y[i];
718
719 if (!wall_blocked (map, x1, y1) && layout[x1][y1] == '>')
720 { /* place a door */
721 remove_monsters (x1, y1, map);
722
723 object *new_door = get_archetype (freearr_x[i] == 0 ? doors[1] : doors[0]);
724 map->insert (new_door, x1, y1);
725 doorlist[ndoors_made] = new_door;
726 ndoors_made++;
727 }
728 }
729
730 return doorlist;
731 }
732
733
734 /* returns the first door in this square, or NULL if there isn't a door. */
735 object *
736 door_in_square (maptile *map, int x, int y)
737 {
738 object *tmp;
739
740 for (tmp = GET_MAP_OB (map, x, y); tmp != NULL; tmp = tmp->above)
741 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
742 return tmp;
743 return NULL;
744 }
745
746 /* the workhorse routine, which finds the doors in a room */
747 void
748 find_doors_in_room_recursive (char **layout, maptile *map, int x, int y, object **doorlist, int *ndoors, random_map_params *RP)
749 {
750 int i, j;
751 object *door;
752
753 /* bounds check x and y */
754 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
755 return;
756
757 /* if the square is blocked or searched already, leave */
758 if (layout[x][y] == 1)
759 return;
760
761 /* check off this point */
762 if (layout[x][y] == '#')
763 { /* there could be a door here */
764 layout[x][y] = 1;
765 door = door_in_square (map, x, y);
766 if (door)
767 {
768 doorlist[*ndoors] = door;
769
770 if (*ndoors > 1022) /* eek! out of memory */
771 {
772 LOG (llevError, "find_doors_in_room_recursive:Too many doors for memory allocated!\n");
773 return;
774 }
775
776 *ndoors = *ndoors + 1;
777 }
778 }
779 else
780 {
781 layout[x][y] = 1;
782
783 /* now search all the 8 squares around recursively for free spots,in random order */
784 for (i = rndm (8), j = 0; j < 8 && !theMonsterToFind; i++, j++)
785 find_doors_in_room_recursive (layout, map,
786 x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1],
787 doorlist, ndoors, RP);
788 }
789 }
790
791 /* find a random non-blocked spot in this room to drop a key. */
792 object **
793 find_doors_in_room (maptile *map, int x, int y, random_map_params *RP)
794 {
795 int i, j;
796 int ndoors = 0;
797
798 object **doorlist = (object **)calloc (sizeof (int), 1024);
799
800 LayoutData layout2 (RP->Xsize, RP->Ysize);
801 layout2.clear ();
802
803 /* allocate and copy the layout, converting C to 0. */
804 for (i = 0; i < RP->Xsize; i++)
805 for (j = 0; j < RP->Ysize; j++)
806 layout2[i][j] = wall_blocked (map, i, j) ? '#' : 0;
807
808 /* setup num_free_spots and room_free_spots */
809 find_doors_in_room_recursive (layout2, map, x, y, doorlist, &ndoors, RP);
810
811 return doorlist;
812 }
813
814 /* locks and/or hides all the doors in doorlist, or does nothing if
815 opts doesn't say to lock/hide doors. */
816 void
817 lock_and_hide_doors (object **doorlist, maptile *map, int opts, random_map_params *RP)
818 {
819 object *door;
820 int i;
821
822 /* lock the doors and hide the keys. */
823
824 if (opts & DOORED)
825 {
826 for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
827 {
828 object *new_door = get_archetype ("locked_door1");
829 char keybuf[1024];
830
831 door = doorlist[i];
832 new_door->face = door->face;
833 new_door->x = door->x;
834 new_door->y = door->y;
835 door->remove ();
836 door->destroy ();
837 doorlist[i] = new_door;
838 insert_ob_in_map (new_door, map, NULL, 0);
839 sprintf (keybuf, "%d", rndm (1000000000));
840 new_door->slaying = keybuf;
841 keyplace (map, new_door->x, new_door->y, keybuf, NO_PASS_DOORS, 2, RP);
842 }
843 }
844
845 /* change the faces of the doors and surrounding walls to hide them. */
846 if (opts & HIDDEN)
847 {
848 for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
849 {
850 object *wallface;
851
852 door = doorlist[i];
853 wallface = retrofit_joined_wall (map, door->x, door->y, 1, RP);
854 if (wallface != NULL)
855 {
856 retrofit_joined_wall (map, door->x - 1, door->y, 0, RP);
857 retrofit_joined_wall (map, door->x + 1, door->y, 0, RP);
858 retrofit_joined_wall (map, door->x, door->y - 1, 0, RP);
859 retrofit_joined_wall (map, door->x, door->y + 1, 0, RP);
860
861 door->face = wallface->face;
862
863 if (!QUERY_FLAG (wallface, FLAG_REMOVED))
864 wallface->remove ();
865
866 wallface->destroy ();
867 }
868 }
869 }
870 }