ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.33
Committed: Fri Apr 11 21:09:53 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.32: +16 -31 lines
Log Message:
*** empty log message ***

File Contents

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