ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.22
Committed: Fri Jan 19 21:49:58 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.21: +7 -10 lines
Log Message:
- fix a crashing bug inside the random map generator
- do not decrease dungeon size with random map depth

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