ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.20
Committed: Thu Jan 18 00:06:56 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.19: +0 -25 lines
Log Message:
properly use xsize/ysize default as random for random maps. 10/10 was not just wrong but also greatly increased the pressure on the generator, leading to more crashes. those should be solved, obviously...

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