ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.67
Committed: Sat Nov 17 23:40:02 2018 UTC (5 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.66: +1 -0 lines
Log Message:
copyright update 2018

File Contents

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