ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.45
Committed: Mon Oct 12 14:00:58 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_82, rel-2_81
Changes since 1.44: +7 -6 lines
Log Message:
clarify license

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.45 * Deliantra is free software: you can redistribute it and/or modify it under
9     * the terms of the Affero GNU General Public License as published by the
10     * Free Software Foundation, either version 3 of the License, or (at your
11     * 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.45 * You should have received a copy of the Affero GNU General Public License
19     * and the GNU General Public License along with this program. If not, see
20     * <http://www.gnu.org/licenses/>.
21 root 1.30 *
22 root 1.32 * The authors can be reached via e-mail to <support@deliantra.net>
23 root 1.19 */
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 root 1.24 /* a macro to get a strongly centered random distribution,
47     from 0 to x, centered at x/2 */
48     static int
49     bc_random (int x)
50     {
51 root 1.38 return (rmg_rndm (x) + rmg_rndm (x) + rmg_rndm (x)) / 3;
52 root 1.24 }
53 elmex 1.1
54 root 1.39 static object *
55     gen_key (const shstr &keycode)
56     {
57     /* get a key and set its keycode */
58 root 1.41 object *key = archetype::get (shstr_key_random_map);
59 root 1.39 key->slaying = keycode;
60     return key;
61     }
62    
63     /* places keys in the map, preferably in something alive.
64     keycode is the key's code,
65     door_flag is either PASS_DOORS or NO_PASS_DOORS.
66     NO_PASS_DOORS won't cross doors or walls to keyplace, PASS_DOORS will.
67     if n_keys is 1, it will place 1 key. if n_keys >1, it will place 2-4 keys:
68     it will place 2-4 keys regardless of what nkeys is provided nkeys > 1.
69    
70     The idea is that you call keyplace on x,y where a door is, and it'll make
71     sure a key is placed on both sides of the door.
72     */
73     static int
74     keyplace (maptile *map, int x, int y, const shstr &keycode, int door_flag, int n_keys, random_map_params *RP)
75     {
76     int i, j;
77     int kx = 0, ky = 0;
78     object *the_keymaster; /* the monster that gets the key. */
79 root 1.40 object *the_key = gen_key (keycode);
80 root 1.39
81     if (door_flag == PASS_DOORS)
82     {
83     int tries = 0;
84    
85     the_keymaster = 0;
86     while (tries < 15 && !the_keymaster)
87     {
88     i = rmg_rndm (RP->Xsize - 2) + 1;
89     j = rmg_rndm (RP->Ysize - 2) + 1;
90     tries++;
91     the_keymaster = find_closest_monster (map, i, j, RP);
92     }
93    
94     /* if we don't find a good keymaster, drop the key on the ground. */
95     if (!the_keymaster)
96     {
97     int freeindex;
98    
99     freeindex = -1;
100     for (tries = 0; tries < 15 && freeindex == -1; tries++)
101     {
102     kx = rmg_rndm (RP->Xsize - 2) + 1;
103     ky = rmg_rndm (RP->Ysize - 2) + 1;
104 root 1.40 freeindex = find_free_spot (the_key, map, kx, ky, 1, SIZEOFFREE1 + 1);
105 root 1.39 }
106    
107     // can freeindex ever be < 0?
108     if (freeindex >= 0)
109     {
110     kx += freearr_x [freeindex];
111     ky += freearr_y [freeindex];
112     }
113     }
114     }
115     else
116     { /* NO_PASS_DOORS --we have to work harder. */
117     /* don't try to keyplace if we're sitting on a blocked square and
118     NO_PASS_DOORS is set. */
119     if (n_keys == 1)
120     {
121     if (wall_blocked (map, x, y))
122 root 1.40 {
123 root 1.44 the_key->destroy ();
124 root 1.40 return 0;
125     }
126 root 1.39
127     the_keymaster = find_monster_in_room (map, x, y, RP);
128     if (!the_keymaster) /* if fail, find a spot to drop the key. */
129     find_spot_in_room (map, x, y, &kx, &ky, RP);
130     }
131     else
132     {
133     int sum = 0; /* count how many keys we actually place */
134    
135     /* I'm lazy, so just try to place in all 4 directions. */
136     sum += keyplace (map, x + 1, y, keycode, NO_PASS_DOORS, 1, RP);
137     sum += keyplace (map, x, y + 1, keycode, NO_PASS_DOORS, 1, RP);
138     sum += keyplace (map, x - 1, y, keycode, NO_PASS_DOORS, 1, RP);
139     sum += keyplace (map, x, y - 1, keycode, NO_PASS_DOORS, 1, RP);
140    
141     if (sum < 2) /* we might have made a disconnected map-place more keys. */
142     { /* diagonally this time. */
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     keyplace (map, x - 1, y - 1, keycode, NO_PASS_DOORS, 1, RP);
147     }
148    
149 root 1.44 the_key->destroy ();
150 root 1.39 return 1;
151     }
152     }
153    
154     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.44 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 root 1.43 for (object *tmp = GET_MAP_OB (map, x, y); tmp; )
676     {
677     object *next = tmp->above;
678    
679     if (tmp->flag [FLAG_ALIVE])
680 root 1.44 tmp->head_ ()->destroy ();
681 elmex 1.1
682 root 1.43 tmp = next;
683     }
684 elmex 1.1 }
685    
686     /* surrounds the point x,y by doors, so as to enclose something, like
687 root 1.2 a chest. It only goes as far as the 8 squares surrounding, and
688     it'll remove any monsters it finds.*/
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 root 1.26 const char *doors[2];
694 elmex 1.1 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 root 1.22 if (!wall_blocked (map, x1, y1) && layout[x1][y1] == '>')
716 root 1.4 { /* place a door */
717 root 1.22 remove_monsters (x1, y1, map);
718 root 1.4
719 root 1.22 object *new_door = get_archetype (freearr_x[i] == 0 ? doors[1] : doors[0]);
720     map->insert (new_door, x1, y1);
721 root 1.4 doorlist[ndoors_made] = new_door;
722     ndoors_made++;
723     }
724 elmex 1.1 }
725 root 1.22
726 elmex 1.1 return doorlist;
727     }
728    
729    
730     /* returns the first door in this square, or NULL if there isn't a door. */
731 root 1.4 object *
732 root 1.7 door_in_square (maptile *map, int x, int y)
733 root 1.4 {
734 elmex 1.1 object *tmp;
735 root 1.4
736 root 1.10 for (tmp = GET_MAP_OB (map, x, y); tmp != NULL; tmp = tmp->above)
737 root 1.4 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
738     return tmp;
739 elmex 1.1 return NULL;
740     }
741 root 1.4
742 elmex 1.1 /* the workhorse routine, which finds the doors in a room */
743 root 1.4 void
744 root 1.12 find_doors_in_room_recursive (char **layout, maptile *map, int x, int y, object **doorlist, int *ndoors, random_map_params *RP)
745 root 1.4 {
746     int i, j;
747 elmex 1.1 object *door;
748    
749     /* bounds check x and y */
750 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
751     return;
752 elmex 1.1
753     /* if the square is blocked or searched already, leave */
754 root 1.4 if (layout[x][y] == 1)
755     return;
756 elmex 1.1
757     /* check off this point */
758 root 1.4 if (layout[x][y] == '#')
759     { /* there could be a door here */
760     layout[x][y] = 1;
761     door = door_in_square (map, x, y);
762 root 1.13 if (door)
763 elmex 1.1 {
764 root 1.4 doorlist[*ndoors] = door;
765 root 1.33
766 root 1.13 if (*ndoors > 1022) /* eek! out of memory */
767 root 1.4 {
768     LOG (llevError, "find_doors_in_room_recursive:Too many doors for memory allocated!\n");
769     return;
770     }
771 root 1.13
772 root 1.4 *ndoors = *ndoors + 1;
773 elmex 1.1 }
774     }
775 root 1.4 else
776     {
777     layout[x][y] = 1;
778 root 1.24
779 root 1.4 /* now search all the 8 squares around recursively for free spots,in random order */
780 root 1.38 for (i = rmg_rndm (8), j = 0; j < 8 && !theMonsterToFind; i++, j++)
781 root 1.24 find_doors_in_room_recursive (layout, map,
782     x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1],
783     doorlist, ndoors, RP);
784 elmex 1.1 }
785     }
786    
787     /* find a random non-blocked spot in this room to drop a key. */
788 root 1.4 object **
789 root 1.12 find_doors_in_room (maptile *map, int x, int y, random_map_params *RP)
790 root 1.4 {
791     int i, j;
792     int ndoors = 0;
793 elmex 1.1
794 root 1.35 object **doorlist = (object **)calloc (sizeof (int), 1024);
795 root 1.33
796 root 1.36 LayoutData layout2 (RP->Xsize, RP->Ysize);
797     layout2.clear ();
798 elmex 1.1
799     /* allocate and copy the layout, converting C to 0. */
800 root 1.4 for (i = 0; i < RP->Xsize; i++)
801 root 1.33 for (j = 0; j < RP->Ysize; j++)
802 root 1.36 layout2[i][j] = wall_blocked (map, i, j) ? '#' : 0;
803 root 1.4
804     /* setup num_free_spots and room_free_spots */
805     find_doors_in_room_recursive (layout2, map, x, y, doorlist, &ndoors, RP);
806 elmex 1.1
807     return doorlist;
808     }
809    
810     /* locks and/or hides all the doors in doorlist, or does nothing if
811 root 1.2 opts doesn't say to lock/hide doors. */
812 root 1.4 void
813 root 1.12 lock_and_hide_doors (object **doorlist, maptile *map, int opts, random_map_params *RP)
814 root 1.4 {
815 elmex 1.1 object *door;
816     int i;
817 root 1.4
818 elmex 1.1 /* lock the doors and hide the keys. */
819 root 1.4
820     if (opts & DOORED)
821     {
822 root 1.43 for (i = 0, door = doorlist[0]; doorlist[i]; i++)
823 root 1.4 {
824 root 1.41 object *new_door = get_archetype (shstr_locked_door1);
825 root 1.4
826     door = doorlist[i];
827     new_door->face = door->face;
828     new_door->x = door->x;
829     new_door->y = door->y;
830 root 1.44 door->destroy ();
831 root 1.4 doorlist[i] = new_door;
832     insert_ob_in_map (new_door, map, NULL, 0);
833 root 1.39 new_door->slaying = format ("RMG-%d-%d", (int)rmg_rndm (1000000000), (int)rmg_rndm (1000000000));
834     keyplace (map, new_door->x, new_door->y, new_door->slaying, NO_PASS_DOORS, 2, RP);
835 root 1.4 }
836 elmex 1.1 }
837    
838     /* change the faces of the doors and surrounding walls to hide them. */
839 root 1.4 if (opts & HIDDEN)
840     {
841     for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
842     {
843     object *wallface;
844    
845     door = doorlist[i];
846     wallface = retrofit_joined_wall (map, door->x, door->y, 1, RP);
847     if (wallface != NULL)
848     {
849     retrofit_joined_wall (map, door->x - 1, door->y, 0, RP);
850     retrofit_joined_wall (map, door->x + 1, door->y, 0, RP);
851     retrofit_joined_wall (map, door->x, door->y - 1, 0, RP);
852     retrofit_joined_wall (map, door->x, door->y + 1, 0, RP);
853 root 1.36
854 root 1.4 door->face = wallface->face;
855 root 1.36
856 root 1.44 wallface->destroy ();
857 root 1.4 }
858     }
859 elmex 1.1 }
860     }