ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.40
Committed: Thu May 8 13:47:19 2008 UTC (16 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_6, rel-2_54, rel-2_55, rel-2_56
Changes since 1.39: +7 -4 lines
Log Message:
*** empty log message ***

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