ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.33
Committed: Fri Apr 11 21:09:53 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.32: +16 -31 lines
Log Message:
*** empty log message ***

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