ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.23
Committed: Sat Jan 27 00:56:48 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.22: +6 -0 lines
Log Message:
proactive

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