ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.21
Committed: Thu Jan 18 19:42:10 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.20: +4 -4 lines
Log Message:
just experimenting

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