ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.17
Committed: Mon Jan 15 15:54:19 2007 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.16: +10 -5 lines
Log Message:
experimental stability and correctness changes

File Contents

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