ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.25
Committed: Sat Feb 17 23:32:11 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_0
Changes since 1.24: +2 -2 lines
Log Message:
- prepare find_style_map rewrite
- move pick_random_object to maptile::
- make pick_random_object fair overe the numbe rof monsters and
  no longer prefer big monsters.

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.19 * CrossFire, A Multiplayer game
3     *
4     * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5     * Copyright (C) 2001 Mark Wedel & Crossfire Development Team
6     * Copyright (C) 1992 Frank Tore Johansen
7     *
8     * This program 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 2 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, write to the Free Software
20     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21     *
22     * The authors can be reached via e-mail at <crossfire@schmorp.de>
23     */
24 elmex 1.1
25     /* placing treasure in maps, where appropriate. */
26    
27     #include <global.h>
28     #include <random_map.h>
29     #include <rproto.h>
30    
31     /* some defines for various options which can be set. */
32    
33 root 1.4 #define CONCENTRATED 1 /* all the treasure is at the C's for onions. */
34     #define HIDDEN 2 /* doors to treasure are hidden. */
35     #define KEYREQUIRED 4 /* chest has a key, which is placed randomly in the map. */
36     #define DOORED 8 /* treasure has doors around it. */
37     #define TRAPPED 16 /* trap dropped in same location as chest. */
38     #define SPARSE 32 /* 1/2 as much treasure as default */
39     #define RICH 64 /* 2x as much treasure as default */
40     #define FILLED 128 /* Fill/tile the entire map with treasure */
41     #define LAST_OPTION 64 /* set this to the last real option, for random */
42 elmex 1.1
43     #define NO_PASS_DOORS 0
44     #define PASS_DOORS 1
45    
46 root 1.24 /* a macro to get a strongly centered random distribution,
47     from 0 to x, centered at x/2 */
48     static int
49     bc_random (int x)
50     {
51     return (rndm (x) + rndm (x) + rndm (x)) / 3;
52     }
53 elmex 1.1
54     /* returns true if square x,y has P_NO_PASS set, which is true for walls
55     * and doors but not monsters.
56     * This function is not map tile aware.
57     */
58 root 1.4 int
59 root 1.7 wall_blocked (maptile *m, int x, int y)
60 root 1.4 {
61     if (OUT_OF_REAL_MAP (m, x, y))
62     return 1;
63 root 1.19
64     int r = GET_MAP_MOVE_BLOCK (m, x, y) & ~MOVE_BLOCK_DEFAULT;
65 root 1.4 return r;
66 elmex 1.1 }
67    
68     /* place treasures in the map, given the
69     map, (required)
70     layout, (required)
71     treasure style (may be empty or NULL, or "none" to cause no treasure.)
72     treasureoptions (may be 0 for random choices or positive)
73     */
74 root 1.4 void
75 root 1.12 place_treasure (maptile *map, char **layout, char *treasure_style, int treasureoptions, random_map_params *RP)
76 root 1.4 {
77 root 1.13 char styledirname[1024];
78     char stylefilepath[1024];
79 root 1.7 maptile *style_map = 0;
80 elmex 1.1 int num_treasures;
81    
82     /* bail out if treasure isn't wanted. */
83 root 1.4 if (treasure_style)
84     if (!strcmp (treasure_style, "none"))
85     return;
86 root 1.22
87 root 1.4 if (treasureoptions <= 0)
88 root 1.24 treasureoptions = rndm (2 * LAST_OPTION);
89 elmex 1.1
90     /* filter out the mutually exclusive options */
91 root 1.4 if ((treasureoptions & RICH) && (treasureoptions & SPARSE))
92     {
93 root 1.21 if (rndm (2))
94 root 1.4 treasureoptions -= 1;
95     else
96     treasureoptions -= 2;
97     }
98 elmex 1.1
99     /* pick the number of treasures */
100 root 1.4 if (treasureoptions & SPARSE)
101 root 1.24 num_treasures = bc_random (RP->total_map_hp / 600 + RP->difficulty / 2 + 1);
102 root 1.4 else if (treasureoptions & RICH)
103 root 1.24 num_treasures = bc_random (RP->total_map_hp / 150 + 2 * RP->difficulty + 1);
104 root 1.4 else
105 root 1.24 num_treasures = bc_random (RP->total_map_hp / 300 + RP->difficulty + 1);
106 root 1.4
107     if (num_treasures <= 0)
108     return;
109 elmex 1.1
110     /* get the style map */
111 root 1.4 sprintf (styledirname, "%s", "/styles/treasurestyles");
112     sprintf (stylefilepath, "%s/%s", styledirname, treasure_style);
113     style_map = find_style (styledirname, treasure_style, -1);
114 elmex 1.1
115 root 1.23 if (!style_map)
116     {
117     LOG (llevError, "unable to load style map %s %s.\n", styledirname, treasure_style);
118     return;
119     }
120    
121 elmex 1.1 /* all the treasure at one spot in the map. */
122 root 1.4 if (treasureoptions & CONCENTRATED)
123     {
124 elmex 1.1
125 root 1.4 /* map_layout_style global, and is previously set */
126     switch (RP->map_layout_style)
127     {
128 root 1.12 case LAYOUT_ONION:
129     case LAYOUT_SPIRAL:
130     case LAYOUT_SQUARE_SPIRAL:
131     {
132     int i, j;
133    
134     /* search the onion for C's or '>', and put treasure there. */
135     for (i = 0; i < RP->Xsize; i++)
136     {
137     for (j = 0; j < RP->Ysize; j++)
138     {
139     if (layout[i][j] == 'C' || layout[i][j] == '>')
140     {
141     int tdiv = RP->symmetry_used;
142     object **doorlist;
143     object *chest;
144    
145     if (tdiv == 3)
146     tdiv = 2; /* this symmetry uses a divisor of 2 */
147     /* don't put a chest on an exit. */
148     chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures / tdiv, RP);
149     if (!chest)
150     continue; /* if no chest was placed NEXT */
151     if (treasureoptions & (DOORED | HIDDEN))
152     {
153     doorlist = find_doors_in_room (map, i, j, RP);
154     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     treasurelist *tlist = find_treasurelist ("chest");
256    
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.4 insert_ob_in_ob (the_key, the_keymaster);
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     {
508     if (wall_blocked (map, i, j))
509     layout2[i][j] = '#';
510     }
511 elmex 1.1 }
512 root 1.4 theMonsterToFind = find_monster_in_room_recursive (layout2, map, x, y, RP);
513    
514 elmex 1.1 /* deallocate the temp. layout */
515 root 1.4 for (i = 0; i < RP->Xsize; i++)
516     {
517     free (layout2[i]);
518     }
519     free (layout2);
520 elmex 1.1
521     return theMonsterToFind;
522     }
523    
524 root 1.4 /* a datastructure needed by find_spot_in_room and find_spot_in_room_recursive */
525 elmex 1.1 int *room_free_spots_x;
526     int *room_free_spots_y;
527 root 1.4 int number_of_free_spots_in_room;
528 elmex 1.1
529     /* the workhorse routine, which finds the free spots in a room:
530     a datastructure of free points is set up, and a position chosen from
531     that datastructure. */
532 root 1.4 void
533 root 1.12 find_spot_in_room_recursive (char **layout, int x, int y, random_map_params *RP)
534 root 1.4 {
535     int i, j;
536 elmex 1.1
537     /* bounds check x and y */
538 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
539     return;
540 elmex 1.1
541     /* if the square is blocked or searched already, leave */
542 root 1.4 if (layout[x][y] != 0)
543     return;
544 elmex 1.1
545     /* set the current square as checked, and add it to the list.
546     set theMonsterToFind and return it. */
547     /* check off this point */
548 root 1.4 layout[x][y] = 1;
549     room_free_spots_x[number_of_free_spots_in_room] = x;
550     room_free_spots_y[number_of_free_spots_in_room] = y;
551 elmex 1.1 number_of_free_spots_in_room++;
552 root 1.19
553 elmex 1.1 /* now search all the 8 squares around recursively for free spots,in random order */
554 root 1.21 for (i = rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
555 root 1.19 find_spot_in_room_recursive (layout, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
556 elmex 1.1
557     }
558    
559     /* find a random non-blocked spot in this room to drop a key. */
560 root 1.4 void
561 root 1.12 find_spot_in_room (maptile *map, int x, int y, int *kx, int *ky, random_map_params *RP)
562 root 1.4 {
563 elmex 1.1 char **layout2;
564 root 1.4 int i, j;
565 elmex 1.1
566 root 1.4 number_of_free_spots_in_room = 0;
567     room_free_spots_x = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
568     room_free_spots_y = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
569    
570     layout2 = (char **) calloc (sizeof (char *), RP->Xsize);
571 elmex 1.1 /* allocate and copy the layout, converting C to 0. */
572 root 1.4 for (i = 0; i < RP->Xsize; i++)
573     {
574     layout2[i] = (char *) calloc (sizeof (char), RP->Ysize);
575     for (j = 0; j < RP->Ysize; j++)
576 root 1.19 if (wall_blocked (map, i, j))
577     layout2[i][j] = '#';
578 root 1.4 }
579    
580     /* setup num_free_spots and room_free_spots */
581     find_spot_in_room_recursive (layout2, x, y, RP);
582    
583     if (number_of_free_spots_in_room > 0)
584     {
585 root 1.24 i = rndm (number_of_free_spots_in_room);
586 root 1.4 *kx = room_free_spots_x[i];
587     *ky = room_free_spots_y[i];
588 elmex 1.1 }
589    
590     /* deallocate the temp. layout */
591 root 1.4 for (i = 0; i < RP->Xsize; i++)
592 root 1.19 free (layout2[i]);
593    
594 root 1.4 free (layout2);
595     free (room_free_spots_x);
596     free (room_free_spots_y);
597 elmex 1.1 }
598    
599    
600     /* searches the map for a spot with walls around it. The more
601 root 1.2 walls the better, but it'll settle for 1 wall, or even 0, but
602     it'll return 0 if no FREE spots are found.*/
603 root 1.4 void
604 root 1.12 find_enclosed_spot (maptile *map, int *cx, int *cy, random_map_params *RP)
605 root 1.4 {
606     int x, y;
607 elmex 1.1 int i;
608    
609 root 1.4 x = *cx;
610     y = *cy;
611    
612     for (i = 0; i <= SIZEOFFREE1; i++)
613     {
614     int lx, ly, sindex;
615    
616     lx = x + freearr_x[i];
617     ly = y + freearr_y[i];
618     sindex = surround_flag3 (map, lx, ly, RP);
619     /* if it's blocked on 3 sides, it's enclosed */
620     if (sindex == 7 || sindex == 11 || sindex == 13 || sindex == 14)
621     {
622     *cx = lx;
623     *cy = ly;
624     return;
625     }
626 elmex 1.1 }
627    
628     /* OK, if we got here, we're obviously someplace where there's no enclosed
629     spots--try to find someplace which is 2x enclosed. */
630 root 1.4 for (i = 0; i <= SIZEOFFREE1; i++)
631     {
632     int lx, ly, sindex;
633    
634     lx = x + freearr_x[i];
635     ly = y + freearr_y[i];
636     sindex = surround_flag3 (map, lx, ly, RP);
637     /* if it's blocked on 3 sides, it's enclosed */
638     if (sindex == 3 || sindex == 5 || sindex == 9 || sindex == 6 || sindex == 10 || sindex == 12)
639     {
640     *cx = lx;
641     *cy = ly;
642     return;
643     }
644 elmex 1.1 }
645    
646     /* settle for one surround point */
647 root 1.4 for (i = 0; i <= SIZEOFFREE1; i++)
648     {
649     int lx, ly, sindex;
650    
651     lx = x + freearr_x[i];
652     ly = y + freearr_y[i];
653     sindex = surround_flag3 (map, lx, ly, RP);
654     /* if it's blocked on 3 sides, it's enclosed */
655     if (sindex)
656     {
657     *cx = lx;
658     *cy = ly;
659     return;
660     }
661     }
662     /* give up and return the closest free spot. */
663 root 1.17 i = find_free_spot (&archetype::find ("chest")->clone, map, x, y, 1, SIZEOFFREE1 + 1);
664    
665     if (i != -1)
666 root 1.4 {
667     *cx = x + freearr_x[i];
668     *cy = y + freearr_y[i];
669 elmex 1.1 }
670 root 1.17 else
671     {
672     /* indicate failure */
673     *cx = -1;
674     *cy = -1;
675     }
676 elmex 1.1 }
677    
678 root 1.4 void
679 root 1.7 remove_monsters (int x, int y, maptile *map)
680 root 1.4 {
681 elmex 1.1 object *tmp;
682    
683 root 1.22 for (tmp = GET_MAP_OB (map, x, y); tmp; tmp = tmp->above)
684 root 1.4 if (QUERY_FLAG (tmp, FLAG_ALIVE))
685     {
686     if (tmp->head)
687     tmp = tmp->head;
688 root 1.8 tmp->remove ();
689 root 1.9 tmp->destroy ();
690 root 1.10 tmp = GET_MAP_OB (map, x, y);
691 root 1.4 if (tmp == NULL)
692     break;
693     };
694 elmex 1.1 }
695    
696     /* surrounds the point x,y by doors, so as to enclose something, like
697 root 1.2 a chest. It only goes as far as the 8 squares surrounding, and
698     it'll remove any monsters it finds.*/
699 root 1.4 object **
700 root 1.7 surround_by_doors (maptile *map, char **layout, int x, int y, int opts)
701 root 1.4 {
702 elmex 1.1 int i;
703     char *doors[2];
704     object **doorlist;
705 root 1.4 int ndoors_made = 0;
706     doorlist = (object **) calloc (9, sizeof (object *)); /* 9 doors so we can hold termination null */
707 elmex 1.1
708     /* this is a list we pick from, for horizontal and vertical doors */
709 root 1.4 if (opts & DOORED)
710     {
711     doors[0] = "locked_door2";
712     doors[1] = "locked_door1";
713     }
714     else
715     {
716     doors[0] = "door_1";
717     doors[1] = "door_2";
718     }
719 elmex 1.1
720     /* place doors in all the 8 adjacent unblocked squares. */
721 root 1.4 for (i = 1; i < 9; i++)
722     {
723     int x1 = x + freearr_x[i], y1 = y + freearr_y[i];
724    
725 root 1.22 if (!wall_blocked (map, x1, y1) && layout[x1][y1] == '>')
726 root 1.4 { /* place a door */
727 root 1.22 remove_monsters (x1, y1, map);
728 root 1.4
729 root 1.22 object *new_door = get_archetype (freearr_x[i] == 0 ? doors[1] : doors[0]);
730     map->insert (new_door, x1, y1);
731 root 1.4 doorlist[ndoors_made] = new_door;
732     ndoors_made++;
733     }
734 elmex 1.1 }
735 root 1.22
736 elmex 1.1 return doorlist;
737     }
738    
739    
740     /* returns the first door in this square, or NULL if there isn't a door. */
741 root 1.4 object *
742 root 1.7 door_in_square (maptile *map, int x, int y)
743 root 1.4 {
744 elmex 1.1 object *tmp;
745 root 1.4
746 root 1.10 for (tmp = GET_MAP_OB (map, x, y); tmp != NULL; tmp = tmp->above)
747 root 1.4 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
748     return tmp;
749 elmex 1.1 return NULL;
750     }
751 root 1.4
752 elmex 1.1
753     /* the workhorse routine, which finds the doors in a room */
754 root 1.4 void
755 root 1.12 find_doors_in_room_recursive (char **layout, maptile *map, int x, int y, object **doorlist, int *ndoors, random_map_params *RP)
756 root 1.4 {
757     int i, j;
758 elmex 1.1 object *door;
759    
760     /* bounds check x and y */
761 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
762     return;
763 elmex 1.1
764     /* if the square is blocked or searched already, leave */
765 root 1.4 if (layout[x][y] == 1)
766     return;
767 elmex 1.1
768     /* check off this point */
769 root 1.4 if (layout[x][y] == '#')
770     { /* there could be a door here */
771     layout[x][y] = 1;
772     door = door_in_square (map, x, y);
773 root 1.13 if (door)
774 elmex 1.1 {
775 root 1.4 doorlist[*ndoors] = door;
776 root 1.13 if (*ndoors > 1022) /* eek! out of memory */
777 root 1.4 {
778     LOG (llevError, "find_doors_in_room_recursive:Too many doors for memory allocated!\n");
779     return;
780     }
781 root 1.13
782 root 1.4 *ndoors = *ndoors + 1;
783 elmex 1.1 }
784     }
785 root 1.4 else
786     {
787     layout[x][y] = 1;
788 root 1.24
789 root 1.4 /* now search all the 8 squares around recursively for free spots,in random order */
790 root 1.24 for (i = rndm (8), j = 0; j < 8 && !theMonsterToFind; i++, j++)
791     find_doors_in_room_recursive (layout, map,
792     x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1],
793     doorlist, ndoors, RP);
794 elmex 1.1 }
795     }
796    
797     /* find a random non-blocked spot in this room to drop a key. */
798 root 1.4 object **
799 root 1.12 find_doors_in_room (maptile *map, int x, int y, random_map_params *RP)
800 root 1.4 {
801 elmex 1.1 char **layout2;
802     object **doorlist;
803 root 1.4 int i, j;
804     int ndoors = 0;
805 elmex 1.1
806 root 1.13 doorlist = (object **) calloc (sizeof (int), 1024);
807 elmex 1.1
808 root 1.4 layout2 = (char **) calloc (sizeof (char *), RP->Xsize);
809 elmex 1.1 /* allocate and copy the layout, converting C to 0. */
810 root 1.4 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 elmex 1.1 }
819 root 1.4
820     /* setup num_free_spots and room_free_spots */
821     find_doors_in_room_recursive (layout2, map, x, y, doorlist, &ndoors, RP);
822 elmex 1.1
823     /* deallocate the temp. layout */
824 root 1.4 for (i = 0; i < RP->Xsize; i++)
825 root 1.13 free (layout2[i]);
826    
827 root 1.4 free (layout2);
828 elmex 1.1 return doorlist;
829     }
830    
831    
832    
833     /* locks and/or hides all the doors in doorlist, or does nothing if
834 root 1.2 opts doesn't say to lock/hide doors. */
835 elmex 1.1
836 root 1.4 void
837 root 1.12 lock_and_hide_doors (object **doorlist, maptile *map, int opts, random_map_params *RP)
838 root 1.4 {
839 elmex 1.1 object *door;
840     int i;
841 root 1.4
842 elmex 1.1 /* lock the doors and hide the keys. */
843 root 1.4
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 root 1.13 char keybuf[1024];
850 root 1.4
851     door = doorlist[i];
852     new_door->face = door->face;
853     new_door->x = door->x;
854     new_door->y = door->y;
855 root 1.8 door->remove ();
856 root 1.9 door->destroy ();
857 root 1.4 doorlist[i] = new_door;
858     insert_ob_in_map (new_door, map, NULL, 0);
859 root 1.24 sprintf (keybuf, "%d", rndm (1000000000));
860 root 1.4 new_door->slaying = keybuf;
861     keyplace (map, new_door->x, new_door->y, keybuf, NO_PASS_DOORS, 2, RP);
862     }
863 elmex 1.1 }
864    
865     /* change the faces of the doors and surrounding walls to hide them. */
866 root 1.4 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 root 1.8 wallface->remove ();
883 root 1.9 wallface->destroy ();
884 root 1.4 }
885     }
886 elmex 1.1 }
887     }