ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.41
Committed: Thu Jul 24 20:35:37 2008 UTC (15 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_7, rel-2_71, rel-2_61
Changes since 1.40: +3 -8 lines
Log Message:
use key_random_map

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.32 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.30 *
4 root 1.33 * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.30 * Copyright (©) 2001,2007 Mark Wedel & Crossfire Development Team
6     * Copyright (©) 1992,2007 Frank Tore Johansen
7     *
8 root 1.32 * Deliantra is free software: you can redistribute it and/or modify
9 root 1.19 * it under the terms of the GNU General Public License as published by
10 root 1.30 * the Free Software Foundation, either version 3 of the License, or
11 root 1.19 * (at your option) any later version.
12 root 1.30 *
13 root 1.19 * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 root 1.30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 root 1.19 * GNU General Public License for more details.
17 root 1.30 *
18 root 1.19 * You should have received a copy of the GNU General Public License
19 root 1.30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20     *
21 root 1.32 * The authors can be reached via e-mail to <support@deliantra.net>
22 root 1.19 */
23 elmex 1.1
24     /* placing treasure in maps, where appropriate. */
25    
26     #include <global.h>
27     #include <random_map.h>
28     #include <rproto.h>
29    
30     /* some defines for various options which can be set. */
31    
32 root 1.4 #define CONCENTRATED 1 /* all the treasure is at the C's for onions. */
33     #define HIDDEN 2 /* doors to treasure are hidden. */
34     #define KEYREQUIRED 4 /* chest has a key, which is placed randomly in the map. */
35     #define DOORED 8 /* treasure has doors around it. */
36     #define TRAPPED 16 /* trap dropped in same location as chest. */
37     #define SPARSE 32 /* 1/2 as much treasure as default */
38     #define RICH 64 /* 2x as much treasure as default */
39     #define FILLED 128 /* Fill/tile the entire map with treasure */
40     #define LAST_OPTION 64 /* set this to the last real option, for random */
41 elmex 1.1
42     #define NO_PASS_DOORS 0
43     #define PASS_DOORS 1
44    
45 root 1.24 /* a macro to get a strongly centered random distribution,
46     from 0 to x, centered at x/2 */
47     static int
48     bc_random (int x)
49     {
50 root 1.38 return (rmg_rndm (x) + rmg_rndm (x) + rmg_rndm (x)) / 3;
51 root 1.24 }
52 elmex 1.1
53 root 1.39 static object *
54     gen_key (const shstr &keycode)
55     {
56     /* get a key and set its keycode */
57 root 1.41 object *key = archetype::get (shstr_key_random_map);
58 root 1.39 key->slaying = keycode;
59     return key;
60     }
61    
62     /* places keys in the map, preferably in something alive.
63     keycode is the key's code,
64     door_flag is either PASS_DOORS or NO_PASS_DOORS.
65     NO_PASS_DOORS won't cross doors or walls to keyplace, PASS_DOORS will.
66     if n_keys is 1, it will place 1 key. if n_keys >1, it will place 2-4 keys:
67     it will place 2-4 keys regardless of what nkeys is provided nkeys > 1.
68    
69     The idea is that you call keyplace on x,y where a door is, and it'll make
70     sure a key is placed on both sides of the door.
71     */
72     static int
73     keyplace (maptile *map, int x, int y, const shstr &keycode, int door_flag, int n_keys, random_map_params *RP)
74     {
75     int i, j;
76     int kx = 0, ky = 0;
77     object *the_keymaster; /* the monster that gets the key. */
78 root 1.40 object *the_key = gen_key (keycode);
79 root 1.39
80     if (door_flag == PASS_DOORS)
81     {
82     int tries = 0;
83    
84     the_keymaster = 0;
85     while (tries < 15 && !the_keymaster)
86     {
87     i = rmg_rndm (RP->Xsize - 2) + 1;
88     j = rmg_rndm (RP->Ysize - 2) + 1;
89     tries++;
90     the_keymaster = find_closest_monster (map, i, j, RP);
91     }
92    
93     /* if we don't find a good keymaster, drop the key on the ground. */
94     if (!the_keymaster)
95     {
96     int freeindex;
97    
98     freeindex = -1;
99     for (tries = 0; tries < 15 && freeindex == -1; tries++)
100     {
101     kx = rmg_rndm (RP->Xsize - 2) + 1;
102     ky = rmg_rndm (RP->Ysize - 2) + 1;
103 root 1.40 freeindex = find_free_spot (the_key, map, kx, ky, 1, SIZEOFFREE1 + 1);
104 root 1.39 }
105    
106     // can freeindex ever be < 0?
107     if (freeindex >= 0)
108     {
109     kx += freearr_x [freeindex];
110     ky += freearr_y [freeindex];
111     }
112     }
113     }
114     else
115     { /* NO_PASS_DOORS --we have to work harder. */
116     /* don't try to keyplace if we're sitting on a blocked square and
117     NO_PASS_DOORS is set. */
118     if (n_keys == 1)
119     {
120     if (wall_blocked (map, x, y))
121 root 1.40 {
122     the_key->destroy ();
123     return 0;
124     }
125 root 1.39
126     the_keymaster = find_monster_in_room (map, x, y, RP);
127     if (!the_keymaster) /* if fail, find a spot to drop the key. */
128     find_spot_in_room (map, x, y, &kx, &ky, RP);
129     }
130     else
131     {
132     int sum = 0; /* count how many keys we actually place */
133    
134     /* I'm lazy, so just try to place in all 4 directions. */
135     sum += keyplace (map, x + 1, y, keycode, NO_PASS_DOORS, 1, RP);
136     sum += keyplace (map, x, y + 1, keycode, NO_PASS_DOORS, 1, RP);
137     sum += keyplace (map, x - 1, y, keycode, NO_PASS_DOORS, 1, RP);
138     sum += keyplace (map, x, y - 1, keycode, NO_PASS_DOORS, 1, RP);
139    
140     if (sum < 2) /* we might have made a disconnected map-place more keys. */
141     { /* diagonally this time. */
142     keyplace (map, x + 1, y + 1, keycode, NO_PASS_DOORS, 1, RP);
143     keyplace (map, x + 1, y - 1, keycode, NO_PASS_DOORS, 1, RP);
144     keyplace (map, x - 1, y + 1, keycode, NO_PASS_DOORS, 1, RP);
145     keyplace (map, x - 1, y - 1, keycode, NO_PASS_DOORS, 1, RP);
146     }
147    
148 root 1.40 the_key->destroy ();
149 root 1.39 return 1;
150     }
151     }
152    
153 root 1.41 LOG(llevError, "key %d,%d %p\n", the_keymaster ? the_keymaster->x : x, the_keymaster ? the_keymaster->y : y, the_keymaster+0);//D
154 root 1.39 if (the_keymaster)
155     the_keymaster->head_ ()->insert (the_key);
156     else
157     {
158     the_key->x = kx;
159     the_key->y = ky;
160     insert_ob_in_map (the_key, map, NULL, 0);
161     }
162    
163     return 1;
164     }
165    
166 elmex 1.1 /* returns true if square x,y has P_NO_PASS set, which is true for walls
167     * and doors but not monsters.
168     * This function is not map tile aware.
169     */
170 root 1.4 int
171 root 1.7 wall_blocked (maptile *m, int x, int y)
172 root 1.4 {
173     if (OUT_OF_REAL_MAP (m, x, y))
174     return 1;
175 root 1.19
176 elmex 1.27 m->at (x, y).update ();
177     return GET_MAP_MOVE_BLOCK (m, x, y) & MOVE_WALK;
178 elmex 1.1 }
179    
180     /* place treasures in the map, given the
181     map, (required)
182     layout, (required)
183     treasure style (may be empty or NULL, or "none" to cause no treasure.)
184     treasureoptions (may be 0 for random choices or positive)
185     */
186 root 1.4 void
187 root 1.12 place_treasure (maptile *map, char **layout, char *treasure_style, int treasureoptions, random_map_params *RP)
188 root 1.4 {
189 root 1.13 char styledirname[1024];
190     char stylefilepath[1024];
191 root 1.7 maptile *style_map = 0;
192 elmex 1.1 int num_treasures;
193    
194     /* bail out if treasure isn't wanted. */
195 root 1.4 if (treasure_style)
196     if (!strcmp (treasure_style, "none"))
197     return;
198 root 1.22
199 root 1.4 if (treasureoptions <= 0)
200 root 1.38 treasureoptions = rmg_rndm (2 * LAST_OPTION);
201 elmex 1.1
202     /* filter out the mutually exclusive options */
203 root 1.4 if ((treasureoptions & RICH) && (treasureoptions & SPARSE))
204     {
205 root 1.38 if (rmg_rndm (2))
206 root 1.4 treasureoptions -= 1;
207     else
208     treasureoptions -= 2;
209     }
210 elmex 1.1
211     /* pick the number of treasures */
212 root 1.4 if (treasureoptions & SPARSE)
213 root 1.24 num_treasures = bc_random (RP->total_map_hp / 600 + RP->difficulty / 2 + 1);
214 root 1.4 else if (treasureoptions & RICH)
215 root 1.24 num_treasures = bc_random (RP->total_map_hp / 150 + 2 * RP->difficulty + 1);
216 root 1.4 else
217 root 1.24 num_treasures = bc_random (RP->total_map_hp / 300 + RP->difficulty + 1);
218 root 1.4
219     if (num_treasures <= 0)
220     return;
221 elmex 1.1
222     /* get the style map */
223 root 1.4 sprintf (styledirname, "%s", "/styles/treasurestyles");
224     sprintf (stylefilepath, "%s/%s", styledirname, treasure_style);
225     style_map = find_style (styledirname, treasure_style, -1);
226 elmex 1.1
227 root 1.23 if (!style_map)
228     {
229     LOG (llevError, "unable to load style map %s %s.\n", styledirname, treasure_style);
230     return;
231     }
232    
233 elmex 1.1 /* all the treasure at one spot in the map. */
234 root 1.4 if (treasureoptions & CONCENTRATED)
235     {
236     /* map_layout_style global, and is previously set */
237     switch (RP->map_layout_style)
238     {
239 root 1.12 case LAYOUT_ONION:
240     case LAYOUT_SPIRAL:
241     case LAYOUT_SQUARE_SPIRAL:
242     {
243     int i, j;
244    
245     /* search the onion for C's or '>', and put treasure there. */
246     for (i = 0; i < RP->Xsize; i++)
247     {
248     for (j = 0; j < RP->Ysize; j++)
249     {
250     if (layout[i][j] == 'C' || layout[i][j] == '>')
251     {
252     int tdiv = RP->symmetry_used;
253     object *chest;
254    
255     if (tdiv == 3)
256     tdiv = 2; /* this symmetry uses a divisor of 2 */
257 root 1.34
258 root 1.12 /* don't put a chest on an exit. */
259     chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures / tdiv, RP);
260 root 1.33
261 root 1.12 if (!chest)
262     continue; /* if no chest was placed NEXT */
263 root 1.33
264 root 1.12 if (treasureoptions & (DOORED | HIDDEN))
265     {
266 root 1.33 object **doorlist = find_doors_in_room (map, i, j, RP);
267 root 1.12 lock_and_hide_doors (doorlist, map, treasureoptions, RP);
268     free (doorlist);
269     }
270     }
271     }
272     }
273     break;
274     }
275 root 1.37
276 root 1.12 default:
277     {
278     int i, j, tries;
279     object *chest;
280     object **doorlist;
281    
282     i = j = -1;
283     tries = 0;
284     while (i == -1 && tries < 100)
285     {
286 root 1.38 i = rmg_rndm (RP->Xsize - 2) + 1;
287     j = rmg_rndm (RP->Ysize - 2) + 1;
288 root 1.12 find_enclosed_spot (map, &i, &j, RP);
289 root 1.34
290 root 1.12 if (wall_blocked (map, i, j))
291     i = -1;
292 root 1.34
293 root 1.12 tries++;
294     }
295 root 1.34
296 root 1.12 chest = place_chest (treasureoptions, i, j, map, style_map, num_treasures, RP);
297 root 1.34
298 root 1.12 if (!chest)
299     return;
300 root 1.34
301 root 1.12 i = chest->x;
302     j = chest->y;
303     if (treasureoptions & (DOORED | HIDDEN))
304     {
305     doorlist = surround_by_doors (map, layout, i, j, treasureoptions);
306     lock_and_hide_doors (doorlist, map, treasureoptions, RP);
307     free (doorlist);
308     }
309     }
310 elmex 1.1 }
311 root 1.4 }
312     else
313     { /* DIFFUSE treasure layout */
314     int ti, i, j;
315    
316     for (ti = 0; ti < num_treasures; ti++)
317     {
318 root 1.38 i = rmg_rndm (RP->Xsize - 2) + 1;
319     j = rmg_rndm (RP->Ysize - 2) + 1;
320 root 1.4 place_chest (treasureoptions, i, j, map, style_map, 1, RP);
321 elmex 1.1 }
322     }
323     }
324    
325     /* put a chest into the map, near x and y, with the treasure style
326 root 1.2 determined (may be null, or may be a treasure list from lib/treasures,
327     if the global variable "treasurestyle" is set to that treasure list's name */
328 root 1.4 object *
329 root 1.12 place_chest (int treasureoptions, int x, int y, maptile *map, maptile *style_map, int n_treasures, random_map_params *RP)
330 root 1.4 {
331 root 1.38 object *the_chest = archetype::get (shstr_chest); /* was "chest_2" */
332 elmex 1.1
333     /* first, find a place to put the chest. */
334 root 1.38 int i = find_first_free_spot (the_chest, map, x, y); // this call uses the main rng
335 root 1.4 if (i == -1)
336     {
337 root 1.9 the_chest->destroy ();
338 root 1.4 return NULL;
339     }
340 root 1.17
341 root 1.38 int xl = x + freearr_x[i];
342     int yl = y + freearr_y[i];
343 elmex 1.1
344     /* if the placement is blocked, return a fail. */
345 root 1.4 if (wall_blocked (map, xl, yl))
346     return 0;
347    
348 elmex 1.1 /* put the treasures in the chest. */
349     /* if(style_map) { */
350 root 1.4 #if 0 /* don't use treasure style maps for now! */
351 elmex 1.1 int ti;
352 root 1.4
353 elmex 1.1 /* if treasurestyle lists a treasure list, use it. */
354 root 1.4 treasurelist *tlist = find_treasurelist (RP->treasurestyle);
355    
356     if (tlist != NULL)
357     for (ti = 0; ti < n_treasures; ti++)
358     { /* use the treasure list */
359 root 1.38 object *new_treasure = style_map->pick_random_object (rmg_rndm);
360 root 1.4
361     insert_ob_in_ob (arch_to_object (new_treasure->arch), the_chest);
362     }
363     else
364     { /* use the style map */
365     the_chest->randomitems = tlist;
366     the_chest->stats.hp = n_treasures;
367 elmex 1.1 }
368     #endif
369 root 1.4 { /* neither style_map no treasure list given */
370 root 1.28 treasurelist *tlist = treasurelist::find ("chest");
371 root 1.4
372     the_chest->randomitems = tlist;
373 elmex 1.1 the_chest->stats.hp = n_treasures;
374     }
375    
376     /* stick a trap in the chest if required */
377 root 1.4 if (treasureoptions & TRAPPED)
378     {
379 root 1.7 maptile *trap_map = find_style ("/styles/trapstyles", "traps", -1);
380 root 1.4
381     if (trap_map)
382     {
383 root 1.38 object *the_trap = trap_map->pick_random_object (rmg_rndm);
384 root 1.37
385 root 1.4 the_trap->stats.Cha = 10 + RP->difficulty;
386 root 1.24 the_trap->level = bc_random ((3 * RP->difficulty) / 2);
387 root 1.37
388 root 1.4 if (the_trap)
389     {
390 root 1.37 object *new_trap = the_trap->arch->instance ();//TODO: why not clone?
391 root 1.4
392     new_trap->x = x;
393     new_trap->y = y;
394     insert_ob_in_ob (new_trap, the_chest);
395     }
396     }
397 elmex 1.1 }
398    
399     /* set the chest lock code, and call the keyplacer routine with
400     the lockcode. It's not worth bothering to lock the chest if
401 root 1.4 there's only 1 treasure.... */
402     if ((treasureoptions & KEYREQUIRED) && n_treasures > 1)
403     {
404 root 1.39 the_chest->slaying = format ("RMG-%d-%d", (int)rmg_rndm (1000000000), (int)rmg_rndm (1000000000));
405     keyplace (map, x, y, the_chest->slaying, PASS_DOORS, 1, RP);
406 root 1.4 }
407 elmex 1.1
408     /* actually place the chest. */
409 root 1.4 the_chest->x = xl;
410     the_chest->y = yl;
411     insert_ob_in_map (the_chest, map, NULL, 0);
412 elmex 1.1 return the_chest;
413     }
414    
415    
416     /* finds the closest monster and returns him, regardless of doors
417 root 1.2 or walls */
418 root 1.4 object *
419 root 1.12 find_closest_monster (maptile *map, int x, int y, random_map_params *RP)
420 root 1.4 {
421 elmex 1.1 int i;
422 root 1.4
423     for (i = 0; i < SIZEOFFREE; i++)
424     {
425     int lx, ly;
426    
427     lx = x + freearr_x[i];
428     ly = y + freearr_y[i];
429     /* boundscheck */
430     if (lx >= 0 && ly >= 0 && lx < RP->Xsize && ly < RP->Ysize)
431     /* don't bother searching this square unless the map says life exists. */
432     if (GET_MAP_FLAGS (map, lx, ly) & P_IS_ALIVE)
433     {
434 root 1.10 object *the_monster = GET_MAP_OB (map, lx, ly);
435 root 1.4
436     for (; the_monster != NULL && (!QUERY_FLAG (the_monster, FLAG_MONSTER)); the_monster = the_monster->above);
437     if (the_monster && QUERY_FLAG (the_monster, FLAG_MONSTER))
438     return the_monster;
439     }
440     }
441 elmex 1.1 return NULL;
442     }
443 root 1.4
444     /* both find_monster_in_room routines need to have access to this. */
445 elmex 1.1
446     object *theMonsterToFind;
447    
448     /* a recursive routine which will return a monster, eventually,if there is one.
449 root 1.4 it does a check-off on the layout, converting 0's to 1's */
450    
451     object *
452 root 1.12 find_monster_in_room_recursive (char **layout, maptile *map, int x, int y, random_map_params *RP)
453 root 1.4 {
454     int i, j;
455 elmex 1.1
456     /* if we've found a monster already, leave */
457 root 1.4 if (theMonsterToFind != NULL)
458     return theMonsterToFind;
459 elmex 1.1
460     /* bounds check x and y */
461 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
462     return theMonsterToFind;
463 elmex 1.1
464     /* if the square is blocked or searched already, leave */
465 root 1.4 if (layout[x][y] != 0)
466     return theMonsterToFind; /* might be NULL, that's fine. */
467 elmex 1.1
468     /* check the current square for a monster. If there is one,
469     set theMonsterToFind and return it. */
470 root 1.4 layout[x][y] = 1;
471     if (GET_MAP_FLAGS (map, x, y) & P_IS_ALIVE)
472     {
473 root 1.10 object *the_monster = GET_MAP_OB (map, x, y);
474 root 1.4
475     /* check off this point */
476     for (; the_monster != NULL && (!QUERY_FLAG (the_monster, FLAG_ALIVE)); the_monster = the_monster->above);
477     if (the_monster && QUERY_FLAG (the_monster, FLAG_ALIVE))
478     {
479     theMonsterToFind = the_monster;
480     return theMonsterToFind;
481     }
482 elmex 1.1 }
483 root 1.4
484 elmex 1.1 /* now search all the 8 squares around recursively for a monster,in random order */
485 root 1.38 for (i = rmg_rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
486 root 1.4 {
487     theMonsterToFind = find_monster_in_room_recursive (layout, map, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
488     if (theMonsterToFind != NULL)
489     return theMonsterToFind;
490     }
491 root 1.36
492 elmex 1.1 return theMonsterToFind;
493     }
494    
495     /* sets up some data structures: the _recursive form does the
496     real work. */
497 root 1.4 object *
498 root 1.12 find_monster_in_room (maptile *map, int x, int y, random_map_params *RP)
499 root 1.4 {
500 root 1.36 Layout layout2 (RP);
501    
502     layout2->clear ();
503 root 1.4
504 elmex 1.1 /* allocate and copy the layout, converting C to 0. */
505 root 1.36 for (int i = 0; i < layout2->w; i++)
506     for (int j = 0; j < layout2->h; j++)
507     if (wall_blocked (map, i, j))
508     layout2[i][j] = '#';
509 root 1.33
510 root 1.36 theMonsterToFind = 0;
511 root 1.4 theMonsterToFind = find_monster_in_room_recursive (layout2, map, x, y, RP);
512    
513 root 1.36 layout2.free ();
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.38 for (i = rmg_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 root 1.38 i = rmg_rndm (number_of_free_spots_in_room);
580 root 1.4 *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.38 i = find_free_spot (archetype::find (shstr_chest), map, x, y, 1, SIZEOFFREE1 + 1);
658 root 1.17
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 root 1.26 const char *doors[2];
698 elmex 1.1 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 /* the workhorse routine, which finds the doors in a room */
747 root 1.4 void
748 root 1.12 find_doors_in_room_recursive (char **layout, maptile *map, int x, int y, object **doorlist, int *ndoors, random_map_params *RP)
749 root 1.4 {
750     int i, j;
751 elmex 1.1 object *door;
752    
753     /* bounds check x and y */
754 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
755     return;
756 elmex 1.1
757     /* if the square is blocked or searched already, leave */
758 root 1.4 if (layout[x][y] == 1)
759     return;
760 elmex 1.1
761     /* check off this point */
762 root 1.4 if (layout[x][y] == '#')
763     { /* there could be a door here */
764     layout[x][y] = 1;
765     door = door_in_square (map, x, y);
766 root 1.13 if (door)
767 elmex 1.1 {
768 root 1.4 doorlist[*ndoors] = door;
769 root 1.33
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 root 1.24
783 root 1.4 /* now search all the 8 squares around recursively for free spots,in random order */
784 root 1.38 for (i = rmg_rndm (8), j = 0; j < 8 && !theMonsterToFind; i++, j++)
785 root 1.24 find_doors_in_room_recursive (layout, map,
786     x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1],
787     doorlist, ndoors, RP);
788 elmex 1.1 }
789     }
790    
791     /* find a random non-blocked spot in this room to drop a key. */
792 root 1.4 object **
793 root 1.12 find_doors_in_room (maptile *map, int x, int y, random_map_params *RP)
794 root 1.4 {
795     int i, j;
796     int ndoors = 0;
797 elmex 1.1
798 root 1.35 object **doorlist = (object **)calloc (sizeof (int), 1024);
799 root 1.33
800 root 1.36 LayoutData layout2 (RP->Xsize, RP->Ysize);
801     layout2.clear ();
802 elmex 1.1
803     /* allocate and copy the layout, converting C to 0. */
804 root 1.4 for (i = 0; i < RP->Xsize; i++)
805 root 1.33 for (j = 0; j < RP->Ysize; j++)
806 root 1.36 layout2[i][j] = wall_blocked (map, i, j) ? '#' : 0;
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     return doorlist;
812     }
813    
814     /* locks and/or hides all the doors in doorlist, or does nothing if
815 root 1.2 opts doesn't say to lock/hide doors. */
816 root 1.4 void
817 root 1.12 lock_and_hide_doors (object **doorlist, maptile *map, int opts, random_map_params *RP)
818 root 1.4 {
819 elmex 1.1 object *door;
820     int i;
821 root 1.4
822 elmex 1.1 /* lock the doors and hide the keys. */
823 root 1.4
824     if (opts & DOORED)
825     {
826     for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
827     {
828 root 1.41 object *new_door = get_archetype (shstr_locked_door1);
829 root 1.4
830     door = doorlist[i];
831     new_door->face = door->face;
832     new_door->x = door->x;
833     new_door->y = door->y;
834 root 1.8 door->remove ();
835 root 1.9 door->destroy ();
836 root 1.4 doorlist[i] = new_door;
837     insert_ob_in_map (new_door, map, NULL, 0);
838 root 1.39 new_door->slaying = format ("RMG-%d-%d", (int)rmg_rndm (1000000000), (int)rmg_rndm (1000000000));
839     keyplace (map, new_door->x, new_door->y, new_door->slaying, NO_PASS_DOORS, 2, RP);
840 root 1.4 }
841 elmex 1.1 }
842    
843     /* change the faces of the doors and surrounding walls to hide them. */
844 root 1.4 if (opts & HIDDEN)
845     {
846     for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
847     {
848     object *wallface;
849    
850     door = doorlist[i];
851     wallface = retrofit_joined_wall (map, door->x, door->y, 1, RP);
852     if (wallface != NULL)
853     {
854     retrofit_joined_wall (map, door->x - 1, door->y, 0, RP);
855     retrofit_joined_wall (map, door->x + 1, door->y, 0, RP);
856     retrofit_joined_wall (map, door->x, door->y - 1, 0, RP);
857     retrofit_joined_wall (map, door->x, door->y + 1, 0, RP);
858 root 1.36
859 root 1.4 door->face = wallface->face;
860 root 1.36
861 root 1.4 if (!QUERY_FLAG (wallface, FLAG_REMOVED))
862 root 1.8 wallface->remove ();
863 root 1.36
864 root 1.9 wallface->destroy ();
865 root 1.4 }
866     }
867 elmex 1.1 }
868     }