ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.32
Committed: Thu Nov 8 19:43:25 2007 UTC (16 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_4, rel-2_32, rel-2_43, rel-2_42, rel-2_41
Changes since 1.31: +4 -4 lines
Log Message:
update copyrights and other minor stuff to deliantra

File Contents

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