ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.39
Committed: Thu May 8 11:39:24 2008 UTC (16 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.38: +119 -115 lines
Log Message:
*** empty log message ***

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