ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.45
Committed: Mon Oct 12 14:00:58 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_82, rel-2_81
Changes since 1.44: +7 -6 lines
Log Message:
clarify license

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 it under
9 * the terms of the Affero GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * 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 Affero GNU General Public License
19 * and the GNU General Public License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 *
22 * The authors can be reached via e-mail to <support@deliantra.net>
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 /* a macro to get a strongly centered random distribution,
47 from 0 to x, centered at x/2 */
48 static int
49 bc_random (int x)
50 {
51 return (rmg_rndm (x) + rmg_rndm (x) + rmg_rndm (x)) / 3;
52 }
53
54 static object *
55 gen_key (const shstr &keycode)
56 {
57 /* get a key and set its keycode */
58 object *key = archetype::get (shstr_key_random_map);
59 key->slaying = keycode;
60 return key;
61 }
62
63 /* places keys in the map, preferably in something alive.
64 keycode is the key's code,
65 door_flag is either PASS_DOORS or NO_PASS_DOORS.
66 NO_PASS_DOORS won't cross doors or walls to keyplace, PASS_DOORS will.
67 if n_keys is 1, it will place 1 key. if n_keys >1, it will place 2-4 keys:
68 it will place 2-4 keys regardless of what nkeys is provided nkeys > 1.
69
70 The idea is that you call keyplace on x,y where a door is, and it'll make
71 sure a key is placed on both sides of the door.
72 */
73 static int
74 keyplace (maptile *map, int x, int y, const shstr &keycode, int door_flag, int n_keys, random_map_params *RP)
75 {
76 int i, j;
77 int kx = 0, ky = 0;
78 object *the_keymaster; /* the monster that gets the key. */
79 object *the_key = gen_key (keycode);
80
81 if (door_flag == PASS_DOORS)
82 {
83 int tries = 0;
84
85 the_keymaster = 0;
86 while (tries < 15 && !the_keymaster)
87 {
88 i = rmg_rndm (RP->Xsize - 2) + 1;
89 j = rmg_rndm (RP->Ysize - 2) + 1;
90 tries++;
91 the_keymaster = find_closest_monster (map, i, j, RP);
92 }
93
94 /* if we don't find a good keymaster, drop the key on the ground. */
95 if (!the_keymaster)
96 {
97 int freeindex;
98
99 freeindex = -1;
100 for (tries = 0; tries < 15 && freeindex == -1; tries++)
101 {
102 kx = rmg_rndm (RP->Xsize - 2) + 1;
103 ky = rmg_rndm (RP->Ysize - 2) + 1;
104 freeindex = find_free_spot (the_key, map, kx, ky, 1, SIZEOFFREE1 + 1);
105 }
106
107 // can freeindex ever be < 0?
108 if (freeindex >= 0)
109 {
110 kx += freearr_x [freeindex];
111 ky += freearr_y [freeindex];
112 }
113 }
114 }
115 else
116 { /* NO_PASS_DOORS --we have to work harder. */
117 /* don't try to keyplace if we're sitting on a blocked square and
118 NO_PASS_DOORS is set. */
119 if (n_keys == 1)
120 {
121 if (wall_blocked (map, x, y))
122 {
123 the_key->destroy ();
124 return 0;
125 }
126
127 the_keymaster = find_monster_in_room (map, x, y, RP);
128 if (!the_keymaster) /* if fail, find a spot to drop the key. */
129 find_spot_in_room (map, x, y, &kx, &ky, RP);
130 }
131 else
132 {
133 int sum = 0; /* count how many keys we actually place */
134
135 /* I'm lazy, so just try to place in all 4 directions. */
136 sum += keyplace (map, x + 1, y, keycode, NO_PASS_DOORS, 1, RP);
137 sum += keyplace (map, x, y + 1, keycode, NO_PASS_DOORS, 1, RP);
138 sum += keyplace (map, x - 1, y, keycode, NO_PASS_DOORS, 1, RP);
139 sum += keyplace (map, x, y - 1, keycode, NO_PASS_DOORS, 1, RP);
140
141 if (sum < 2) /* we might have made a disconnected map-place more keys. */
142 { /* diagonally this time. */
143 keyplace (map, x + 1, y + 1, keycode, NO_PASS_DOORS, 1, RP);
144 keyplace (map, x + 1, y - 1, keycode, NO_PASS_DOORS, 1, RP);
145 keyplace (map, x - 1, y + 1, keycode, NO_PASS_DOORS, 1, RP);
146 keyplace (map, x - 1, y - 1, keycode, NO_PASS_DOORS, 1, RP);
147 }
148
149 the_key->destroy ();
150 return 1;
151 }
152 }
153
154 if (the_keymaster)
155 the_keymaster->head_ ()->insert (the_key);
156 else
157 {
158 the_key->x = kx;
159 the_key->y = ky;
160 insert_ob_in_map (the_key, map, NULL, 0);
161 }
162
163 return 1;
164 }
165
166 /* returns true if square x,y has P_NO_PASS set, which is true for walls
167 * and doors but not monsters.
168 * This function is not map tile aware.
169 */
170 int
171 wall_blocked (maptile *m, int x, int y)
172 {
173 if (OUT_OF_REAL_MAP (m, x, y))
174 return 1;
175
176 m->at (x, y).update ();
177 return GET_MAP_MOVE_BLOCK (m, x, y) & MOVE_WALK;
178 }
179
180 /* place treasures in the map, given the
181 map, (required)
182 layout, (required)
183 treasure style (may be empty or NULL, or "none" to cause no treasure.)
184 treasureoptions (may be 0 for random choices or positive)
185 */
186 void
187 place_treasure (maptile *map, char **layout, char *treasure_style, int treasureoptions, random_map_params *RP)
188 {
189 char styledirname[1024];
190 char stylefilepath[1024];
191 maptile *style_map = 0;
192 int num_treasures;
193
194 /* bail out if treasure isn't wanted. */
195 if (treasure_style)
196 if (!strcmp (treasure_style, "none"))
197 return;
198
199 if (treasureoptions <= 0)
200 treasureoptions = rmg_rndm (2 * LAST_OPTION);
201
202 /* filter out the mutually exclusive options */
203 if ((treasureoptions & RICH) && (treasureoptions & SPARSE))
204 {
205 if (rmg_rndm (2))
206 treasureoptions -= 1;
207 else
208 treasureoptions -= 2;
209 }
210
211 /* pick the number of treasures */
212 if (treasureoptions & SPARSE)
213 num_treasures = bc_random (RP->total_map_hp / 600 + RP->difficulty / 2 + 1);
214 else if (treasureoptions & RICH)
215 num_treasures = bc_random (RP->total_map_hp / 150 + 2 * RP->difficulty + 1);
216 else
217 num_treasures = bc_random (RP->total_map_hp / 300 + RP->difficulty + 1);
218
219 if (num_treasures <= 0)
220 return;
221
222 /* get the style map */
223 sprintf (styledirname, "%s", "/styles/treasurestyles");
224 sprintf (stylefilepath, "%s/%s", styledirname, treasure_style);
225 style_map = find_style (styledirname, treasure_style, -1);
226
227 if (!style_map)
228 {
229 LOG (llevError, "unable to load style map %s %s.\n", styledirname, treasure_style);
230 return;
231 }
232
233 /* all the treasure at one spot in the map. */
234 if (treasureoptions & CONCENTRATED)
235 {
236 /* map_layout_style global, and is previously set */
237 switch (RP->map_layout_style)
238 {
239 case LAYOUT_ONION:
240 case LAYOUT_SPIRAL:
241 case LAYOUT_SQUARE_SPIRAL:
242 {
243 int i, j;
244
245 /* search the onion for C's or '>', and put treasure there. */
246 for (i = 0; i < RP->Xsize; i++)
247 {
248 for (j = 0; j < RP->Ysize; j++)
249 {
250 if (layout[i][j] == 'C' || layout[i][j] == '>')
251 {
252 int tdiv = RP->symmetry_used;
253 object *chest;
254
255 if (tdiv == 3)
256 tdiv = 2; /* this symmetry uses a divisor of 2 */
257
258 /* don't put a chest on an exit. */
259 chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures / tdiv, RP);
260
261 if (!chest)
262 continue; /* if no chest was placed NEXT */
263
264 if (treasureoptions & (DOORED | HIDDEN))
265 {
266 object **doorlist = find_doors_in_room (map, i, j, RP);
267 lock_and_hide_doors (doorlist, map, treasureoptions, RP);
268 free (doorlist);
269 }
270 }
271 }
272 }
273 break;
274 }
275
276 default:
277 {
278 int i, j, tries;
279 object *chest;
280 object **doorlist;
281
282 i = j = -1;
283 tries = 0;
284 while (i == -1 && tries < 100)
285 {
286 i = rmg_rndm (RP->Xsize - 2) + 1;
287 j = rmg_rndm (RP->Ysize - 2) + 1;
288 find_enclosed_spot (map, &i, &j, RP);
289
290 if (wall_blocked (map, i, j))
291 i = -1;
292
293 tries++;
294 }
295
296 chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures, RP);
297
298 if (!chest)
299 return;
300
301 i = chest->x;
302 j = chest->y;
303 if (treasureoptions & (DOORED | HIDDEN))
304 {
305 doorlist = surround_by_doors (map, layout, i, j, treasureoptions);
306 lock_and_hide_doors (doorlist, map, treasureoptions, RP);
307 free (doorlist);
308 }
309 }
310 }
311 }
312 else
313 { /* DIFFUSE treasure layout */
314 int ti, i, j;
315
316 for (ti = 0; ti < num_treasures; ti++)
317 {
318 i = rmg_rndm (RP->Xsize - 2) + 1;
319 j = rmg_rndm (RP->Ysize - 2) + 1;
320 place_chest (treasureoptions, i, j, map, style_map, 1, RP);
321 }
322 }
323 }
324
325 /* put a chest into the map, near x and y, with the treasure style
326 determined (may be null, or may be a treasure list from lib/treasures,
327 if the global variable "treasurestyle" is set to that treasure list's name */
328 object *
329 place_chest (int treasureoptions, int x, int y, maptile *map, maptile *style_map, int n_treasures, random_map_params *RP)
330 {
331 object *the_chest = archetype::get (shstr_chest); /* was "chest_2" */
332
333 /* first, find a place to put the chest. */
334 int i = find_first_free_spot (the_chest, map, x, y); // this call uses the main rng
335 if (i == -1)
336 {
337 the_chest->destroy ();
338 return NULL;
339 }
340
341 int xl = x + freearr_x[i];
342 int yl = y + freearr_y[i];
343
344 /* if the placement is blocked, return a fail. */
345 if (wall_blocked (map, xl, yl))
346 return 0;
347
348 /* put the treasures in the chest. */
349 /* if(style_map) { */
350 #if 0 /* don't use treasure style maps for now! */
351 int ti;
352
353 /* if treasurestyle lists a treasure list, use it. */
354 treasurelist *tlist = find_treasurelist (RP->treasurestyle);
355
356 if (tlist != NULL)
357 for (ti = 0; ti < n_treasures; ti++)
358 { /* use the treasure list */
359 object *new_treasure = style_map->pick_random_object (rmg_rndm);
360
361 insert_ob_in_ob (arch_to_object (new_treasure->arch), the_chest);
362 }
363 else
364 { /* use the style map */
365 the_chest->randomitems = tlist;
366 the_chest->stats.hp = n_treasures;
367 }
368 #endif
369 { /* neither style_map no treasure list given */
370 treasurelist *tlist = treasurelist::find ("chest");
371
372 the_chest->randomitems = tlist;
373 the_chest->stats.hp = n_treasures;
374 }
375
376 /* stick a trap in the chest if required */
377 if (treasureoptions & TRAPPED)
378 {
379 maptile *trap_map = find_style ("/styles/trapstyles", "traps", -1);
380
381 if (trap_map)
382 {
383 object *the_trap = trap_map->pick_random_object (rmg_rndm);
384
385 the_trap->stats.Cha = 10 + RP->difficulty;
386 the_trap->level = bc_random ((3 * RP->difficulty) / 2);
387
388 if (the_trap)
389 {
390 object *new_trap = the_trap->arch->instance ();//TODO: why not clone?
391
392 new_trap->x = x;
393 new_trap->y = y;
394 insert_ob_in_ob (new_trap, the_chest);
395 }
396 }
397 }
398
399 /* set the chest lock code, and call the keyplacer routine with
400 the lockcode. It's not worth bothering to lock the chest if
401 there's only 1 treasure.... */
402 if ((treasureoptions & KEYREQUIRED) && n_treasures > 1)
403 {
404 the_chest->slaying = format ("RMG-%d-%d", (int)rmg_rndm (1000000000), (int)rmg_rndm (1000000000));
405 keyplace (map, x, y, the_chest->slaying, PASS_DOORS, 1, RP);
406 }
407
408 /* actually place the chest. */
409 the_chest->x = xl;
410 the_chest->y = yl;
411 insert_ob_in_map (the_chest, map, NULL, 0);
412 return the_chest;
413 }
414
415
416 /* finds the closest monster and returns him, regardless of doors
417 or walls */
418 object *
419 find_closest_monster (maptile *map, int x, int y, random_map_params *RP)
420 {
421 int i;
422
423 for (i = 0; i < SIZEOFFREE; i++)
424 {
425 int lx, ly;
426
427 lx = x + freearr_x[i];
428 ly = y + freearr_y[i];
429 /* boundscheck */
430 if (lx >= 0 && ly >= 0 && lx < RP->Xsize && ly < RP->Ysize)
431 /* don't bother searching this square unless the map says life exists. */
432 if (GET_MAP_FLAGS (map, lx, ly) & P_IS_ALIVE)
433 {
434 object *the_monster = GET_MAP_OB (map, lx, ly);
435
436 for (; the_monster != NULL && (!QUERY_FLAG (the_monster, FLAG_MONSTER)); the_monster = the_monster->above);
437 if (the_monster && QUERY_FLAG (the_monster, FLAG_MONSTER))
438 return the_monster;
439 }
440 }
441 return NULL;
442 }
443
444 /* both find_monster_in_room routines need to have access to this. */
445
446 object *theMonsterToFind;
447
448 /* a recursive routine which will return a monster, eventually,if there is one.
449 it does a check-off on the layout, converting 0's to 1's */
450
451 object *
452 find_monster_in_room_recursive (char **layout, maptile *map, int x, int y, random_map_params *RP)
453 {
454 int i, j;
455
456 /* if we've found a monster already, leave */
457 if (theMonsterToFind != NULL)
458 return theMonsterToFind;
459
460 /* bounds check x and y */
461 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
462 return theMonsterToFind;
463
464 /* if the square is blocked or searched already, leave */
465 if (layout[x][y] != 0)
466 return theMonsterToFind; /* might be NULL, that's fine. */
467
468 /* check the current square for a monster. If there is one,
469 set theMonsterToFind and return it. */
470 layout[x][y] = 1;
471 if (GET_MAP_FLAGS (map, x, y) & P_IS_ALIVE)
472 {
473 object *the_monster = GET_MAP_OB (map, x, y);
474
475 /* check off this point */
476 for (; the_monster != NULL && (!QUERY_FLAG (the_monster, FLAG_ALIVE)); the_monster = the_monster->above);
477 if (the_monster && QUERY_FLAG (the_monster, FLAG_ALIVE))
478 {
479 theMonsterToFind = the_monster;
480 return theMonsterToFind;
481 }
482 }
483
484 /* now search all the 8 squares around recursively for a monster,in random order */
485 for (i = rmg_rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
486 {
487 theMonsterToFind = find_monster_in_room_recursive (layout, map, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
488 if (theMonsterToFind != NULL)
489 return theMonsterToFind;
490 }
491
492 return theMonsterToFind;
493 }
494
495 /* sets up some data structures: the _recursive form does the
496 real work. */
497 object *
498 find_monster_in_room (maptile *map, int x, int y, random_map_params *RP)
499 {
500 Layout layout2 (RP);
501
502 layout2->clear ();
503
504 /* allocate and copy the layout, converting C to 0. */
505 for (int i = 0; i < layout2->w; i++)
506 for (int j = 0; j < layout2->h; j++)
507 if (wall_blocked (map, i, j))
508 layout2[i][j] = '#';
509
510 theMonsterToFind = 0;
511 theMonsterToFind = find_monster_in_room_recursive (layout2, map, x, y, RP);
512
513 layout2.free ();
514
515 return theMonsterToFind;
516 }
517
518 /* a datastructure needed by find_spot_in_room and find_spot_in_room_recursive */
519 int *room_free_spots_x;
520 int *room_free_spots_y;
521 int number_of_free_spots_in_room;
522
523 /* the workhorse routine, which finds the free spots in a room:
524 a datastructure of free points is set up, and a position chosen from
525 that datastructure. */
526 void
527 find_spot_in_room_recursive (char **layout, int x, int y, random_map_params *RP)
528 {
529 int i, j;
530
531 /* bounds check x and y */
532 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
533 return;
534
535 /* if the square is blocked or searched already, leave */
536 if (layout[x][y] != 0)
537 return;
538
539 /* set the current square as checked, and add it to the list.
540 set theMonsterToFind and return it. */
541 /* check off this point */
542 layout[x][y] = 1;
543 room_free_spots_x[number_of_free_spots_in_room] = x;
544 room_free_spots_y[number_of_free_spots_in_room] = y;
545 number_of_free_spots_in_room++;
546
547 /* now search all the 8 squares around recursively for free spots,in random order */
548 for (i = rmg_rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
549 find_spot_in_room_recursive (layout, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
550
551 }
552
553 /* find a random non-blocked spot in this room to drop a key. */
554 void
555 find_spot_in_room (maptile *map, int x, int y, int *kx, int *ky, random_map_params *RP)
556 {
557 char **layout2;
558 int i, j;
559
560 number_of_free_spots_in_room = 0;
561 room_free_spots_x = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
562 room_free_spots_y = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
563
564 layout2 = (char **) calloc (sizeof (char *), RP->Xsize);
565 /* allocate and copy the layout, converting C to 0. */
566 for (i = 0; i < RP->Xsize; i++)
567 {
568 layout2[i] = (char *) calloc (sizeof (char), RP->Ysize);
569 for (j = 0; j < RP->Ysize; j++)
570 if (wall_blocked (map, i, j))
571 layout2[i][j] = '#';
572 }
573
574 /* setup num_free_spots and room_free_spots */
575 find_spot_in_room_recursive (layout2, x, y, RP);
576
577 if (number_of_free_spots_in_room > 0)
578 {
579 i = rmg_rndm (number_of_free_spots_in_room);
580 *kx = room_free_spots_x[i];
581 *ky = room_free_spots_y[i];
582 }
583
584 /* deallocate the temp. layout */
585 for (i = 0; i < RP->Xsize; i++)
586 free (layout2[i]);
587
588 free (layout2);
589 free (room_free_spots_x);
590 free (room_free_spots_y);
591 }
592
593
594 /* searches the map for a spot with walls around it. The more
595 walls the better, but it'll settle for 1 wall, or even 0, but
596 it'll return 0 if no FREE spots are found.*/
597 void
598 find_enclosed_spot (maptile *map, int *cx, int *cy, random_map_params *RP)
599 {
600 int x, y;
601 int i;
602
603 x = *cx;
604 y = *cy;
605
606 for (i = 0; i <= SIZEOFFREE1; i++)
607 {
608 int lx, ly, sindex;
609
610 lx = x + freearr_x[i];
611 ly = y + freearr_y[i];
612 sindex = surround_flag3 (map, lx, ly, RP);
613 /* if it's blocked on 3 sides, it's enclosed */
614 if (sindex == 7 || sindex == 11 || sindex == 13 || sindex == 14)
615 {
616 *cx = lx;
617 *cy = ly;
618 return;
619 }
620 }
621
622 /* OK, if we got here, we're obviously someplace where there's no enclosed
623 spots--try to find someplace which is 2x enclosed. */
624 for (i = 0; i <= SIZEOFFREE1; i++)
625 {
626 int lx, ly, sindex;
627
628 lx = x + freearr_x[i];
629 ly = y + freearr_y[i];
630 sindex = surround_flag3 (map, lx, ly, RP);
631 /* if it's blocked on 3 sides, it's enclosed */
632 if (sindex == 3 || sindex == 5 || sindex == 9 || sindex == 6 || sindex == 10 || sindex == 12)
633 {
634 *cx = lx;
635 *cy = ly;
636 return;
637 }
638 }
639
640 /* settle for one surround point */
641 for (i = 0; i <= SIZEOFFREE1; i++)
642 {
643 int lx, ly, sindex;
644
645 lx = x + freearr_x[i];
646 ly = y + freearr_y[i];
647 sindex = surround_flag3 (map, lx, ly, RP);
648 /* if it's blocked on 3 sides, it's enclosed */
649 if (sindex)
650 {
651 *cx = lx;
652 *cy = ly;
653 return;
654 }
655 }
656 /* give up and return the closest free spot. */
657 i = find_free_spot (archetype::find (shstr_chest), map, x, y, 1, SIZEOFFREE1 + 1);
658
659 if (i != -1)
660 {
661 *cx = x + freearr_x[i];
662 *cy = y + freearr_y[i];
663 }
664 else
665 {
666 /* indicate failure */
667 *cx = -1;
668 *cy = -1;
669 }
670 }
671
672 void
673 remove_monsters (int x, int y, maptile *map)
674 {
675 for (object *tmp = GET_MAP_OB (map, x, y); tmp; )
676 {
677 object *next = tmp->above;
678
679 if (tmp->flag [FLAG_ALIVE])
680 tmp->head_ ()->destroy ();
681
682 tmp = next;
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]; i++)
823 {
824 object *new_door = get_archetype (shstr_locked_door1);
825
826 door = doorlist[i];
827 new_door->face = door->face;
828 new_door->x = door->x;
829 new_door->y = door->y;
830 door->destroy ();
831 doorlist[i] = new_door;
832 insert_ob_in_map (new_door, map, NULL, 0);
833 new_door->slaying = format ("RMG-%d-%d", (int)rmg_rndm (1000000000), (int)rmg_rndm (1000000000));
834 keyplace (map, new_door->x, new_door->y, new_door->slaying, NO_PASS_DOORS, 2, RP);
835 }
836 }
837
838 /* change the faces of the doors and surrounding walls to hide them. */
839 if (opts & HIDDEN)
840 {
841 for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
842 {
843 object *wallface;
844
845 door = doorlist[i];
846 wallface = retrofit_joined_wall (map, door->x, door->y, 1, RP);
847 if (wallface != NULL)
848 {
849 retrofit_joined_wall (map, door->x - 1, door->y, 0, RP);
850 retrofit_joined_wall (map, door->x + 1, door->y, 0, RP);
851 retrofit_joined_wall (map, door->x, door->y - 1, 0, RP);
852 retrofit_joined_wall (map, door->x, door->y + 1, 0, RP);
853
854 door->face = wallface->face;
855
856 wallface->destroy ();
857 }
858 }
859 }
860 }