ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.41
Committed: Thu Jul 24 20:35:37 2008 UTC (15 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_7, rel-2_71, rel-2_61
Changes since 1.40: +3 -8 lines
Log Message:
use key_random_map

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 static object *
54 gen_key (const shstr &keycode)
55 {
56 /* get a key and set its keycode */
57 object *key = archetype::get (shstr_key_random_map);
58 key->slaying = keycode;
59 return key;
60 }
61
62 /* places keys in the map, preferably in something alive.
63 keycode is the key's code,
64 door_flag is either PASS_DOORS or NO_PASS_DOORS.
65 NO_PASS_DOORS won't cross doors or walls to keyplace, PASS_DOORS will.
66 if n_keys is 1, it will place 1 key. if n_keys >1, it will place 2-4 keys:
67 it will place 2-4 keys regardless of what nkeys is provided nkeys > 1.
68
69 The idea is that you call keyplace on x,y where a door is, and it'll make
70 sure a key is placed on both sides of the door.
71 */
72 static int
73 keyplace (maptile *map, int x, int y, const shstr &keycode, int door_flag, int n_keys, random_map_params *RP)
74 {
75 int i, j;
76 int kx = 0, ky = 0;
77 object *the_keymaster; /* the monster that gets the key. */
78 object *the_key = gen_key (keycode);
79
80 if (door_flag == PASS_DOORS)
81 {
82 int tries = 0;
83
84 the_keymaster = 0;
85 while (tries < 15 && !the_keymaster)
86 {
87 i = rmg_rndm (RP->Xsize - 2) + 1;
88 j = rmg_rndm (RP->Ysize - 2) + 1;
89 tries++;
90 the_keymaster = find_closest_monster (map, i, j, RP);
91 }
92
93 /* if we don't find a good keymaster, drop the key on the ground. */
94 if (!the_keymaster)
95 {
96 int freeindex;
97
98 freeindex = -1;
99 for (tries = 0; tries < 15 && freeindex == -1; tries++)
100 {
101 kx = rmg_rndm (RP->Xsize - 2) + 1;
102 ky = rmg_rndm (RP->Ysize - 2) + 1;
103 freeindex = find_free_spot (the_key, map, kx, ky, 1, SIZEOFFREE1 + 1);
104 }
105
106 // can freeindex ever be < 0?
107 if (freeindex >= 0)
108 {
109 kx += freearr_x [freeindex];
110 ky += freearr_y [freeindex];
111 }
112 }
113 }
114 else
115 { /* NO_PASS_DOORS --we have to work harder. */
116 /* don't try to keyplace if we're sitting on a blocked square and
117 NO_PASS_DOORS is set. */
118 if (n_keys == 1)
119 {
120 if (wall_blocked (map, x, y))
121 {
122 the_key->destroy ();
123 return 0;
124 }
125
126 the_keymaster = find_monster_in_room (map, x, y, RP);
127 if (!the_keymaster) /* if fail, find a spot to drop the key. */
128 find_spot_in_room (map, x, y, &kx, &ky, RP);
129 }
130 else
131 {
132 int sum = 0; /* count how many keys we actually place */
133
134 /* I'm lazy, so just try to place in all 4 directions. */
135 sum += keyplace (map, x + 1, y, keycode, NO_PASS_DOORS, 1, RP);
136 sum += keyplace (map, x, y + 1, keycode, NO_PASS_DOORS, 1, RP);
137 sum += keyplace (map, x - 1, y, keycode, NO_PASS_DOORS, 1, RP);
138 sum += keyplace (map, x, y - 1, keycode, NO_PASS_DOORS, 1, RP);
139
140 if (sum < 2) /* we might have made a disconnected map-place more keys. */
141 { /* diagonally this time. */
142 keyplace (map, x + 1, y + 1, keycode, NO_PASS_DOORS, 1, RP);
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 }
147
148 the_key->destroy ();
149 return 1;
150 }
151 }
152
153 LOG(llevError, "key %d,%d %p\n", the_keymaster ? the_keymaster->x : x, the_keymaster ? the_keymaster->y : y, the_keymaster+0);//D
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 object *tmp;
676
677 for (tmp = GET_MAP_OB (map, x, y); tmp; tmp = tmp->above)
678 if (QUERY_FLAG (tmp, FLAG_ALIVE))
679 {
680 if (tmp->head)
681 tmp = tmp->head;
682 tmp->remove ();
683 tmp->destroy ();
684 tmp = GET_MAP_OB (map, x, y);
685 if (tmp == NULL)
686 break;
687 };
688 }
689
690 /* surrounds the point x,y by doors, so as to enclose something, like
691 a chest. It only goes as far as the 8 squares surrounding, and
692 it'll remove any monsters it finds.*/
693 object **
694 surround_by_doors (maptile *map, char **layout, int x, int y, int opts)
695 {
696 int i;
697 const char *doors[2];
698 object **doorlist;
699 int ndoors_made = 0;
700 doorlist = (object **) calloc (9, sizeof (object *)); /* 9 doors so we can hold termination null */
701
702 /* this is a list we pick from, for horizontal and vertical doors */
703 if (opts & DOORED)
704 {
705 doors[0] = "locked_door2";
706 doors[1] = "locked_door1";
707 }
708 else
709 {
710 doors[0] = "door_1";
711 doors[1] = "door_2";
712 }
713
714 /* place doors in all the 8 adjacent unblocked squares. */
715 for (i = 1; i < 9; i++)
716 {
717 int x1 = x + freearr_x[i], y1 = y + freearr_y[i];
718
719 if (!wall_blocked (map, x1, y1) && layout[x1][y1] == '>')
720 { /* place a door */
721 remove_monsters (x1, y1, map);
722
723 object *new_door = get_archetype (freearr_x[i] == 0 ? doors[1] : doors[0]);
724 map->insert (new_door, x1, y1);
725 doorlist[ndoors_made] = new_door;
726 ndoors_made++;
727 }
728 }
729
730 return doorlist;
731 }
732
733
734 /* returns the first door in this square, or NULL if there isn't a door. */
735 object *
736 door_in_square (maptile *map, int x, int y)
737 {
738 object *tmp;
739
740 for (tmp = GET_MAP_OB (map, x, y); tmp != NULL; tmp = tmp->above)
741 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
742 return tmp;
743 return NULL;
744 }
745
746 /* the workhorse routine, which finds the doors in a room */
747 void
748 find_doors_in_room_recursive (char **layout, maptile *map, int x, int y, object **doorlist, int *ndoors, random_map_params *RP)
749 {
750 int i, j;
751 object *door;
752
753 /* bounds check x and y */
754 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
755 return;
756
757 /* if the square is blocked or searched already, leave */
758 if (layout[x][y] == 1)
759 return;
760
761 /* check off this point */
762 if (layout[x][y] == '#')
763 { /* there could be a door here */
764 layout[x][y] = 1;
765 door = door_in_square (map, x, y);
766 if (door)
767 {
768 doorlist[*ndoors] = door;
769
770 if (*ndoors > 1022) /* eek! out of memory */
771 {
772 LOG (llevError, "find_doors_in_room_recursive:Too many doors for memory allocated!\n");
773 return;
774 }
775
776 *ndoors = *ndoors + 1;
777 }
778 }
779 else
780 {
781 layout[x][y] = 1;
782
783 /* now search all the 8 squares around recursively for free spots,in random order */
784 for (i = rmg_rndm (8), j = 0; j < 8 && !theMonsterToFind; i++, j++)
785 find_doors_in_room_recursive (layout, map,
786 x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1],
787 doorlist, ndoors, RP);
788 }
789 }
790
791 /* find a random non-blocked spot in this room to drop a key. */
792 object **
793 find_doors_in_room (maptile *map, int x, int y, random_map_params *RP)
794 {
795 int i, j;
796 int ndoors = 0;
797
798 object **doorlist = (object **)calloc (sizeof (int), 1024);
799
800 LayoutData layout2 (RP->Xsize, RP->Ysize);
801 layout2.clear ();
802
803 /* allocate and copy the layout, converting C to 0. */
804 for (i = 0; i < RP->Xsize; i++)
805 for (j = 0; j < RP->Ysize; j++)
806 layout2[i][j] = wall_blocked (map, i, j) ? '#' : 0;
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 return doorlist;
812 }
813
814 /* locks and/or hides all the doors in doorlist, or does nothing if
815 opts doesn't say to lock/hide doors. */
816 void
817 lock_and_hide_doors (object **doorlist, maptile *map, int opts, random_map_params *RP)
818 {
819 object *door;
820 int i;
821
822 /* lock the doors and hide the keys. */
823
824 if (opts & DOORED)
825 {
826 for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
827 {
828 object *new_door = get_archetype (shstr_locked_door1);
829
830 door = doorlist[i];
831 new_door->face = door->face;
832 new_door->x = door->x;
833 new_door->y = door->y;
834 door->remove ();
835 door->destroy ();
836 doorlist[i] = new_door;
837 insert_ob_in_map (new_door, map, NULL, 0);
838 new_door->slaying = format ("RMG-%d-%d", (int)rmg_rndm (1000000000), (int)rmg_rndm (1000000000));
839 keyplace (map, new_door->x, new_door->y, new_door->slaying, NO_PASS_DOORS, 2, RP);
840 }
841 }
842
843 /* change the faces of the doors and surrounding walls to hide them. */
844 if (opts & HIDDEN)
845 {
846 for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
847 {
848 object *wallface;
849
850 door = doorlist[i];
851 wallface = retrofit_joined_wall (map, door->x, door->y, 1, RP);
852 if (wallface != NULL)
853 {
854 retrofit_joined_wall (map, door->x - 1, door->y, 0, RP);
855 retrofit_joined_wall (map, door->x + 1, door->y, 0, RP);
856 retrofit_joined_wall (map, door->x, door->y - 1, 0, RP);
857 retrofit_joined_wall (map, door->x, door->y + 1, 0, RP);
858
859 door->face = wallface->face;
860
861 if (!QUERY_FLAG (wallface, FLAG_REMOVED))
862 wallface->remove ();
863
864 wallface->destroy ();
865 }
866 }
867 }
868 }