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