ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.22
Committed: Fri Jan 19 21:49:58 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.21: +7 -10 lines
Log Message:
- fix a crashing bug inside the random map generator
- do not decrease dungeon size with random map depth

File Contents

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