ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.38
Committed: Sun May 4 14:12:38 2008 UTC (16 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_53
Changes since 1.37: +25 -29 lines
Log Message:
lotsa

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 (rmg_rndm (x) + rmg_rndm (x) + rmg_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 = rmg_rndm (2 * LAST_OPTION);
88
89 /* filter out the mutually exclusive options */
90 if ((treasureoptions & RICH) && (treasureoptions & SPARSE))
91 {
92 if (rmg_rndm (2))
93 treasureoptions -= 1;
94 else
95 treasureoptions -= 2;
96 }
97
98 /* pick the number of treasures */
99 if (treasureoptions & SPARSE)
100 num_treasures = bc_random (RP->total_map_hp / 600 + RP->difficulty / 2 + 1);
101 else if (treasureoptions & RICH)
102 num_treasures = bc_random (RP->total_map_hp / 150 + 2 * RP->difficulty + 1);
103 else
104 num_treasures = bc_random (RP->total_map_hp / 300 + RP->difficulty + 1);
105
106 if (num_treasures <= 0)
107 return;
108
109 /* get the style map */
110 sprintf (styledirname, "%s", "/styles/treasurestyles");
111 sprintf (stylefilepath, "%s/%s", styledirname, treasure_style);
112 style_map = find_style (styledirname, treasure_style, -1);
113
114 if (!style_map)
115 {
116 LOG (llevError, "unable to load style map %s %s.\n", styledirname, treasure_style);
117 return;
118 }
119
120 /* all the treasure at one spot in the map. */
121 if (treasureoptions & CONCENTRATED)
122 {
123 /* map_layout_style global, and is previously set */
124 switch (RP->map_layout_style)
125 {
126 case LAYOUT_ONION:
127 case LAYOUT_SPIRAL:
128 case LAYOUT_SQUARE_SPIRAL:
129 {
130 int i, j;
131
132 /* search the onion for C's or '>', and put treasure there. */
133 for (i = 0; i < RP->Xsize; i++)
134 {
135 for (j = 0; j < RP->Ysize; j++)
136 {
137 if (layout[i][j] == 'C' || layout[i][j] == '>')
138 {
139 int tdiv = RP->symmetry_used;
140 object *chest;
141
142 if (tdiv == 3)
143 tdiv = 2; /* this symmetry uses a divisor of 2 */
144
145 /* don't put a chest on an exit. */
146 chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures / tdiv, RP);
147
148 if (!chest)
149 continue; /* if no chest was placed NEXT */
150
151 if (treasureoptions & (DOORED | HIDDEN))
152 {
153 object **doorlist = find_doors_in_room (map, i, j, RP);
154 lock_and_hide_doors (doorlist, map, treasureoptions, RP);
155 free (doorlist);
156 }
157 }
158 }
159 }
160 break;
161 }
162
163 default:
164 {
165 int i, j, tries;
166 object *chest;
167 object **doorlist;
168
169 i = j = -1;
170 tries = 0;
171 while (i == -1 && tries < 100)
172 {
173 i = rmg_rndm (RP->Xsize - 2) + 1;
174 j = rmg_rndm (RP->Ysize - 2) + 1;
175 find_enclosed_spot (map, &i, &j, RP);
176
177 if (wall_blocked (map, i, j))
178 i = -1;
179
180 tries++;
181 }
182
183 chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures, RP);
184
185 if (!chest)
186 return;
187
188 i = chest->x;
189 j = chest->y;
190 if (treasureoptions & (DOORED | HIDDEN))
191 {
192 doorlist = surround_by_doors (map, layout, i, j, treasureoptions);
193 lock_and_hide_doors (doorlist, map, treasureoptions, RP);
194 free (doorlist);
195 }
196 }
197 }
198 }
199 else
200 { /* DIFFUSE treasure layout */
201 int ti, i, j;
202
203 for (ti = 0; ti < num_treasures; ti++)
204 {
205 i = rmg_rndm (RP->Xsize - 2) + 1;
206 j = rmg_rndm (RP->Ysize - 2) + 1;
207 place_chest (treasureoptions, i, j, map, style_map, 1, RP);
208 }
209 }
210 }
211
212 /* put a chest into the map, near x and y, with the treasure style
213 determined (may be null, or may be a treasure list from lib/treasures,
214 if the global variable "treasurestyle" is set to that treasure list's name */
215 object *
216 place_chest (int treasureoptions, int x, int y, maptile *map, maptile *style_map, int n_treasures, random_map_params *RP)
217 {
218 object *the_chest = archetype::get (shstr_chest); /* was "chest_2" */
219
220 /* first, find a place to put the chest. */
221 int i = find_first_free_spot (the_chest, map, x, y); // this call uses the main rng
222 if (i == -1)
223 {
224 the_chest->destroy ();
225 return NULL;
226 }
227
228 int xl = x + freearr_x[i];
229 int yl = y + freearr_y[i];
230
231 /* if the placement is blocked, return a fail. */
232 if (wall_blocked (map, xl, yl))
233 return 0;
234
235 /* put the treasures in the chest. */
236 /* if(style_map) { */
237 #if 0 /* don't use treasure style maps for now! */
238 int ti;
239
240 /* if treasurestyle lists a treasure list, use it. */
241 treasurelist *tlist = find_treasurelist (RP->treasurestyle);
242
243 if (tlist != NULL)
244 for (ti = 0; ti < n_treasures; ti++)
245 { /* use the treasure list */
246 object *new_treasure = style_map->pick_random_object (rmg_rndm);
247
248 insert_ob_in_ob (arch_to_object (new_treasure->arch), the_chest);
249 }
250 else
251 { /* use the style map */
252 the_chest->randomitems = tlist;
253 the_chest->stats.hp = n_treasures;
254 }
255 #endif
256 { /* neither style_map no treasure list given */
257 treasurelist *tlist = treasurelist::find ("chest");
258
259 the_chest->randomitems = tlist;
260 the_chest->stats.hp = n_treasures;
261 }
262
263 /* stick a trap in the chest if required */
264 if (treasureoptions & TRAPPED)
265 {
266 maptile *trap_map = find_style ("/styles/trapstyles", "traps", -1);
267
268 if (trap_map)
269 {
270 object *the_trap = trap_map->pick_random_object (rmg_rndm);
271
272 the_trap->stats.Cha = 10 + RP->difficulty;
273 the_trap->level = bc_random ((3 * RP->difficulty) / 2);
274
275 if (the_trap)
276 {
277 object *new_trap = the_trap->arch->instance ();//TODO: why not clone?
278
279 new_trap->x = x;
280 new_trap->y = y;
281 insert_ob_in_ob (new_trap, the_chest);
282 }
283 }
284 }
285
286 /* set the chest lock code, and call the keyplacer routine with
287 the lockcode. It's not worth bothering to lock the chest if
288 there's only 1 treasure.... */
289 if ((treasureoptions & KEYREQUIRED) && n_treasures > 1)
290 {
291 char keybuf[1024];
292
293 sprintf (keybuf, "%d", rmg_rndm (1000000000));
294 the_chest->slaying = keybuf;
295 keyplace (map, x, y, keybuf, PASS_DOORS, 1, RP);
296 }
297
298 /* actually place the chest. */
299 the_chest->x = xl;
300 the_chest->y = yl;
301 insert_ob_in_map (the_chest, map, NULL, 0);
302 return the_chest;
303 }
304
305
306 /* finds the closest monster and returns him, regardless of doors
307 or walls */
308 object *
309 find_closest_monster (maptile *map, int x, int y, random_map_params *RP)
310 {
311 int i;
312
313 for (i = 0; i < SIZEOFFREE; i++)
314 {
315 int lx, ly;
316
317 lx = x + freearr_x[i];
318 ly = y + freearr_y[i];
319 /* boundscheck */
320 if (lx >= 0 && ly >= 0 && lx < RP->Xsize && ly < RP->Ysize)
321 /* don't bother searching this square unless the map says life exists. */
322 if (GET_MAP_FLAGS (map, lx, ly) & P_IS_ALIVE)
323 {
324 object *the_monster = GET_MAP_OB (map, lx, ly);
325
326 for (; the_monster != NULL && (!QUERY_FLAG (the_monster, FLAG_MONSTER)); the_monster = the_monster->above);
327 if (the_monster && QUERY_FLAG (the_monster, FLAG_MONSTER))
328 return the_monster;
329 }
330 }
331 return NULL;
332 }
333
334
335
336 /* places keys in the map, preferably in something alive.
337 keycode is the key's code,
338 door_flag is either PASS_DOORS or NO_PASS_DOORS.
339 NO_PASS_DOORS won't cross doors or walls to keyplace, PASS_DOORS will.
340 if n_keys is 1, it will place 1 key. if n_keys >1, it will place 2-4 keys:
341 it will place 2-4 keys regardless of what nkeys is provided nkeys > 1.
342
343 The idea is that you call keyplace on x,y where a door is, and it'll make
344 sure a key is placed on both sides of the door.
345 */
346 int
347 keyplace (maptile *map, int x, int y, char *keycode, int door_flag, int n_keys, random_map_params *RP)
348 {
349 int i, j;
350 int kx = 0, ky = 0;
351 object *the_keymaster; /* the monster that gets the key. */
352
353 /* get a key and set its keycode */
354 object *the_key = archetype::get (shstr_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 = rmg_rndm (RP->Xsize - 2) + 1;
365 j = rmg_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 = rmg_rndm (RP->Xsize - 2) + 1;
379 ky = rmg_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 = rmg_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
488 return theMonsterToFind;
489 }
490
491 /* sets up some data structures: the _recursive form does the
492 real work. */
493 object *
494 find_monster_in_room (maptile *map, int x, int y, random_map_params *RP)
495 {
496 Layout layout2 (RP);
497
498 layout2->clear ();
499
500 /* allocate and copy the layout, converting C to 0. */
501 for (int i = 0; i < layout2->w; i++)
502 for (int j = 0; j < layout2->h; j++)
503 if (wall_blocked (map, i, j))
504 layout2[i][j] = '#';
505
506 theMonsterToFind = 0;
507 theMonsterToFind = find_monster_in_room_recursive (layout2, map, x, y, RP);
508
509 layout2.free ();
510
511 return theMonsterToFind;
512 }
513
514 /* a datastructure needed by find_spot_in_room and find_spot_in_room_recursive */
515 int *room_free_spots_x;
516 int *room_free_spots_y;
517 int number_of_free_spots_in_room;
518
519 /* the workhorse routine, which finds the free spots in a room:
520 a datastructure of free points is set up, and a position chosen from
521 that datastructure. */
522 void
523 find_spot_in_room_recursive (char **layout, int x, int y, random_map_params *RP)
524 {
525 int i, j;
526
527 /* bounds check x and y */
528 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
529 return;
530
531 /* if the square is blocked or searched already, leave */
532 if (layout[x][y] != 0)
533 return;
534
535 /* set the current square as checked, and add it to the list.
536 set theMonsterToFind and return it. */
537 /* check off this point */
538 layout[x][y] = 1;
539 room_free_spots_x[number_of_free_spots_in_room] = x;
540 room_free_spots_y[number_of_free_spots_in_room] = y;
541 number_of_free_spots_in_room++;
542
543 /* now search all the 8 squares around recursively for free spots,in random order */
544 for (i = rmg_rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
545 find_spot_in_room_recursive (layout, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
546
547 }
548
549 /* find a random non-blocked spot in this room to drop a key. */
550 void
551 find_spot_in_room (maptile *map, int x, int y, int *kx, int *ky, random_map_params *RP)
552 {
553 char **layout2;
554 int i, j;
555
556 number_of_free_spots_in_room = 0;
557 room_free_spots_x = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
558 room_free_spots_y = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
559
560 layout2 = (char **) calloc (sizeof (char *), RP->Xsize);
561 /* allocate and copy the layout, converting C to 0. */
562 for (i = 0; i < RP->Xsize; i++)
563 {
564 layout2[i] = (char *) calloc (sizeof (char), RP->Ysize);
565 for (j = 0; j < RP->Ysize; j++)
566 if (wall_blocked (map, i, j))
567 layout2[i][j] = '#';
568 }
569
570 /* setup num_free_spots and room_free_spots */
571 find_spot_in_room_recursive (layout2, x, y, RP);
572
573 if (number_of_free_spots_in_room > 0)
574 {
575 i = rmg_rndm (number_of_free_spots_in_room);
576 *kx = room_free_spots_x[i];
577 *ky = room_free_spots_y[i];
578 }
579
580 /* deallocate the temp. layout */
581 for (i = 0; i < RP->Xsize; i++)
582 free (layout2[i]);
583
584 free (layout2);
585 free (room_free_spots_x);
586 free (room_free_spots_y);
587 }
588
589
590 /* searches the map for a spot with walls around it. The more
591 walls the better, but it'll settle for 1 wall, or even 0, but
592 it'll return 0 if no FREE spots are found.*/
593 void
594 find_enclosed_spot (maptile *map, int *cx, int *cy, random_map_params *RP)
595 {
596 int x, y;
597 int i;
598
599 x = *cx;
600 y = *cy;
601
602 for (i = 0; i <= SIZEOFFREE1; i++)
603 {
604 int lx, ly, sindex;
605
606 lx = x + freearr_x[i];
607 ly = y + freearr_y[i];
608 sindex = surround_flag3 (map, lx, ly, RP);
609 /* if it's blocked on 3 sides, it's enclosed */
610 if (sindex == 7 || sindex == 11 || sindex == 13 || sindex == 14)
611 {
612 *cx = lx;
613 *cy = ly;
614 return;
615 }
616 }
617
618 /* OK, if we got here, we're obviously someplace where there's no enclosed
619 spots--try to find someplace which is 2x enclosed. */
620 for (i = 0; i <= SIZEOFFREE1; i++)
621 {
622 int lx, ly, sindex;
623
624 lx = x + freearr_x[i];
625 ly = y + freearr_y[i];
626 sindex = surround_flag3 (map, lx, ly, RP);
627 /* if it's blocked on 3 sides, it's enclosed */
628 if (sindex == 3 || sindex == 5 || sindex == 9 || sindex == 6 || sindex == 10 || sindex == 12)
629 {
630 *cx = lx;
631 *cy = ly;
632 return;
633 }
634 }
635
636 /* settle for one surround point */
637 for (i = 0; i <= SIZEOFFREE1; i++)
638 {
639 int lx, ly, sindex;
640
641 lx = x + freearr_x[i];
642 ly = y + freearr_y[i];
643 sindex = surround_flag3 (map, lx, ly, RP);
644 /* if it's blocked on 3 sides, it's enclosed */
645 if (sindex)
646 {
647 *cx = lx;
648 *cy = ly;
649 return;
650 }
651 }
652 /* give up and return the closest free spot. */
653 i = find_free_spot (archetype::find (shstr_chest), map, x, y, 1, SIZEOFFREE1 + 1);
654
655 if (i != -1)
656 {
657 *cx = x + freearr_x[i];
658 *cy = y + freearr_y[i];
659 }
660 else
661 {
662 /* indicate failure */
663 *cx = -1;
664 *cy = -1;
665 }
666 }
667
668 void
669 remove_monsters (int x, int y, maptile *map)
670 {
671 object *tmp;
672
673 for (tmp = GET_MAP_OB (map, x, y); tmp; tmp = tmp->above)
674 if (QUERY_FLAG (tmp, FLAG_ALIVE))
675 {
676 if (tmp->head)
677 tmp = tmp->head;
678 tmp->remove ();
679 tmp->destroy ();
680 tmp = GET_MAP_OB (map, x, y);
681 if (tmp == NULL)
682 break;
683 };
684 }
685
686 /* surrounds the point x,y by doors, so as to enclose something, like
687 a chest. It only goes as far as the 8 squares surrounding, and
688 it'll remove any monsters it finds.*/
689 object **
690 surround_by_doors (maptile *map, char **layout, int x, int y, int opts)
691 {
692 int i;
693 const 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 remove_monsters (x1, y1, map);
718
719 object *new_door = get_archetype (freearr_x[i] == 0 ? doors[1] : doors[0]);
720 map->insert (new_door, x1, y1);
721 doorlist[ndoors_made] = new_door;
722 ndoors_made++;
723 }
724 }
725
726 return doorlist;
727 }
728
729
730 /* returns the first door in this square, or NULL if there isn't a door. */
731 object *
732 door_in_square (maptile *map, int x, int y)
733 {
734 object *tmp;
735
736 for (tmp = GET_MAP_OB (map, x, y); tmp != NULL; tmp = tmp->above)
737 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
738 return tmp;
739 return NULL;
740 }
741
742 /* the workhorse routine, which finds the doors in a room */
743 void
744 find_doors_in_room_recursive (char **layout, maptile *map, int x, int y, object **doorlist, int *ndoors, random_map_params *RP)
745 {
746 int i, j;
747 object *door;
748
749 /* bounds check x and y */
750 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
751 return;
752
753 /* if the square is blocked or searched already, leave */
754 if (layout[x][y] == 1)
755 return;
756
757 /* check off this point */
758 if (layout[x][y] == '#')
759 { /* there could be a door here */
760 layout[x][y] = 1;
761 door = door_in_square (map, x, y);
762 if (door)
763 {
764 doorlist[*ndoors] = door;
765
766 if (*ndoors > 1022) /* eek! out of memory */
767 {
768 LOG (llevError, "find_doors_in_room_recursive:Too many doors for memory allocated!\n");
769 return;
770 }
771
772 *ndoors = *ndoors + 1;
773 }
774 }
775 else
776 {
777 layout[x][y] = 1;
778
779 /* now search all the 8 squares around recursively for free spots,in random order */
780 for (i = rmg_rndm (8), j = 0; j < 8 && !theMonsterToFind; i++, j++)
781 find_doors_in_room_recursive (layout, map,
782 x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1],
783 doorlist, ndoors, RP);
784 }
785 }
786
787 /* find a random non-blocked spot in this room to drop a key. */
788 object **
789 find_doors_in_room (maptile *map, int x, int y, random_map_params *RP)
790 {
791 int i, j;
792 int ndoors = 0;
793
794 object **doorlist = (object **)calloc (sizeof (int), 1024);
795
796 LayoutData layout2 (RP->Xsize, RP->Ysize);
797 layout2.clear ();
798
799 /* allocate and copy the layout, converting C to 0. */
800 for (i = 0; i < RP->Xsize; i++)
801 for (j = 0; j < RP->Ysize; j++)
802 layout2[i][j] = wall_blocked (map, i, j) ? '#' : 0;
803
804 /* setup num_free_spots and room_free_spots */
805 find_doors_in_room_recursive (layout2, map, x, y, doorlist, &ndoors, RP);
806
807 return doorlist;
808 }
809
810 /* locks and/or hides all the doors in doorlist, or does nothing if
811 opts doesn't say to lock/hide doors. */
812 void
813 lock_and_hide_doors (object **doorlist, maptile *map, int opts, random_map_params *RP)
814 {
815 object *door;
816 int i;
817
818 /* lock the doors and hide the keys. */
819
820 if (opts & DOORED)
821 {
822 for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
823 {
824 object *new_door = get_archetype ("locked_door1");
825 char keybuf[1024];
826
827 door = doorlist[i];
828 new_door->face = door->face;
829 new_door->x = door->x;
830 new_door->y = door->y;
831 door->remove ();
832 door->destroy ();
833 doorlist[i] = new_door;
834 insert_ob_in_map (new_door, map, NULL, 0);
835 sprintf (keybuf, "%d", rmg_rndm (1000000000));
836 new_door->slaying = keybuf;
837 keyplace (map, new_door->x, new_door->y, keybuf, NO_PASS_DOORS, 2, RP);
838 }
839 }
840
841 /* change the faces of the doors and surrounding walls to hide them. */
842 if (opts & HIDDEN)
843 {
844 for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
845 {
846 object *wallface;
847
848 door = doorlist[i];
849 wallface = retrofit_joined_wall (map, door->x, door->y, 1, RP);
850 if (wallface != NULL)
851 {
852 retrofit_joined_wall (map, door->x - 1, door->y, 0, RP);
853 retrofit_joined_wall (map, door->x + 1, door->y, 0, RP);
854 retrofit_joined_wall (map, door->x, door->y - 1, 0, RP);
855 retrofit_joined_wall (map, door->x, door->y + 1, 0, RP);
856
857 door->face = wallface->face;
858
859 if (!QUERY_FLAG (wallface, FLAG_REMOVED))
860 wallface->remove ();
861
862 wallface->destroy ();
863 }
864 }
865 }
866 }