ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.52
Committed: Sun Apr 11 00:34:06 2010 UTC (14 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.51: +4 -4 lines
Log Message:
get rid of QUERY_FLAG/SET_FLAG/CLEAR_FLAG macros that I always hated

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.50 * Copyright (©) 2005,2006,2007,2008,2009,2010 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 elmex 1.1 int num_treasures;
200    
201     /* bail out if treasure isn't wanted. */
202 root 1.4 if (treasure_style)
203     if (!strcmp (treasure_style, "none"))
204     return;
205 root 1.22
206 root 1.4 if (treasureoptions <= 0)
207 root 1.38 treasureoptions = rmg_rndm (2 * LAST_OPTION);
208 elmex 1.1
209     /* filter out the mutually exclusive options */
210 root 1.4 if ((treasureoptions & RICH) && (treasureoptions & SPARSE))
211     {
212 root 1.38 if (rmg_rndm (2))
213 root 1.4 treasureoptions -= 1;
214     else
215     treasureoptions -= 2;
216     }
217 elmex 1.1
218     /* pick the number of treasures */
219 root 1.4 if (treasureoptions & SPARSE)
220 root 1.24 num_treasures = bc_random (RP->total_map_hp / 600 + RP->difficulty / 2 + 1);
221 root 1.4 else if (treasureoptions & RICH)
222 root 1.24 num_treasures = bc_random (RP->total_map_hp / 150 + 2 * RP->difficulty + 1);
223 root 1.4 else
224 root 1.24 num_treasures = bc_random (RP->total_map_hp / 300 + RP->difficulty + 1);
225 root 1.4
226     if (num_treasures <= 0)
227     return;
228 elmex 1.1
229     /* get the style map */
230 root 1.51 maptile *style_map = find_style ("/styles/treasurestyles", treasure_style, -1);
231 elmex 1.1
232 root 1.23 if (!style_map)
233     {
234 root 1.51 LOG (llevError, "unable to load style map %s %s.\n", "/styles/treasurestyles", treasure_style);
235 root 1.23 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.47 static 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.44 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 root 1.48 insert_ob_in_ob (new_treasure->arch->instance (), the_chest);
367 root 1.4 }
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 root 1.46 /* finds the closest monster and returns him, regardless of doors or walls */
421     static object *
422 root 1.12 find_closest_monster (maptile *map, int x, int y, random_map_params *RP)
423 root 1.4 {
424 elmex 1.1 int i;
425 root 1.4
426     for (i = 0; i < SIZEOFFREE; i++)
427     {
428     int lx, ly;
429    
430     lx = x + freearr_x[i];
431     ly = y + freearr_y[i];
432     /* boundscheck */
433     if (lx >= 0 && ly >= 0 && lx < RP->Xsize && ly < RP->Ysize)
434     /* don't bother searching this square unless the map says life exists. */
435     if (GET_MAP_FLAGS (map, lx, ly) & P_IS_ALIVE)
436     {
437 root 1.10 object *the_monster = GET_MAP_OB (map, lx, ly);
438 root 1.4
439 root 1.52 for (; the_monster != NULL && (!the_monster->flag [FLAG_MONSTER]); the_monster = the_monster->above);
440     if (the_monster && the_monster->flag [FLAG_MONSTER])
441 root 1.4 return the_monster;
442     }
443     }
444 elmex 1.1 return NULL;
445     }
446 root 1.4
447     /* both find_monster_in_room routines need to have access to this. */
448 elmex 1.1
449 root 1.46 static object *theMonsterToFind;
450 elmex 1.1
451     /* a recursive routine which will return a monster, eventually,if there is one.
452 root 1.4 it does a check-off on the layout, converting 0's to 1's */
453 root 1.46 static object *
454 root 1.12 find_monster_in_room_recursive (char **layout, maptile *map, int x, int y, random_map_params *RP)
455 root 1.4 {
456     int i, j;
457 elmex 1.1
458     /* if we've found a monster already, leave */
459 root 1.4 if (theMonsterToFind != NULL)
460     return theMonsterToFind;
461 elmex 1.1
462     /* bounds check x and y */
463 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
464     return theMonsterToFind;
465 elmex 1.1
466     /* if the square is blocked or searched already, leave */
467 root 1.4 if (layout[x][y] != 0)
468     return theMonsterToFind; /* might be NULL, that's fine. */
469 elmex 1.1
470     /* check the current square for a monster. If there is one,
471     set theMonsterToFind and return it. */
472 root 1.4 layout[x][y] = 1;
473     if (GET_MAP_FLAGS (map, x, y) & P_IS_ALIVE)
474     {
475 root 1.10 object *the_monster = GET_MAP_OB (map, x, y);
476 root 1.4
477     /* check off this point */
478 root 1.52 for (; the_monster != NULL && (!the_monster->flag [FLAG_ALIVE]); the_monster = the_monster->above);
479     if (the_monster && the_monster->flag [FLAG_ALIVE])
480 root 1.4 {
481     theMonsterToFind = the_monster;
482     return theMonsterToFind;
483     }
484 elmex 1.1 }
485 root 1.4
486 elmex 1.1 /* now search all the 8 squares around recursively for a monster,in random order */
487 root 1.38 for (i = rmg_rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
488 root 1.4 {
489     theMonsterToFind = find_monster_in_room_recursive (layout, map, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
490     if (theMonsterToFind != NULL)
491     return theMonsterToFind;
492     }
493 root 1.36
494 elmex 1.1 return theMonsterToFind;
495     }
496    
497     /* sets up some data structures: the _recursive form does the
498     real work. */
499 root 1.47 static object *
500 root 1.12 find_monster_in_room (maptile *map, int x, int y, random_map_params *RP)
501 root 1.4 {
502 root 1.36 Layout layout2 (RP);
503    
504     layout2->clear ();
505 root 1.4
506 elmex 1.1 /* allocate and copy the layout, converting C to 0. */
507 root 1.36 for (int i = 0; i < layout2->w; i++)
508     for (int j = 0; j < layout2->h; j++)
509     if (wall_blocked (map, i, j))
510     layout2[i][j] = '#';
511 root 1.33
512 root 1.36 theMonsterToFind = 0;
513 root 1.4 theMonsterToFind = find_monster_in_room_recursive (layout2, map, x, y, RP);
514    
515 root 1.36 layout2.free ();
516 elmex 1.1
517     return theMonsterToFind;
518     }
519    
520 root 1.4 /* a datastructure needed by find_spot_in_room and find_spot_in_room_recursive */
521 root 1.46 static int *room_free_spots_x;
522     static int *room_free_spots_y;
523     static int number_of_free_spots_in_room;
524 elmex 1.1
525     /* the workhorse routine, which finds the free spots in a room:
526     a datastructure of free points is set up, and a position chosen from
527     that datastructure. */
528 root 1.46 static void
529 root 1.12 find_spot_in_room_recursive (char **layout, int x, int y, random_map_params *RP)
530 root 1.4 {
531     int i, j;
532 elmex 1.1
533     /* bounds check x and y */
534 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
535     return;
536 elmex 1.1
537     /* if the square is blocked or searched already, leave */
538 root 1.4 if (layout[x][y] != 0)
539     return;
540 elmex 1.1
541     /* set the current square as checked, and add it to the list.
542     set theMonsterToFind and return it. */
543     /* check off this point */
544 root 1.4 layout[x][y] = 1;
545     room_free_spots_x[number_of_free_spots_in_room] = x;
546     room_free_spots_y[number_of_free_spots_in_room] = y;
547 elmex 1.1 number_of_free_spots_in_room++;
548 root 1.19
549 elmex 1.1 /* now search all the 8 squares around recursively for free spots,in random order */
550 root 1.38 for (i = rmg_rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
551 root 1.19 find_spot_in_room_recursive (layout, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
552 elmex 1.1
553     }
554    
555     /* find a random non-blocked spot in this room to drop a key. */
556 root 1.46 static void
557 root 1.12 find_spot_in_room (maptile *map, int x, int y, int *kx, int *ky, random_map_params *RP)
558 root 1.4 {
559 elmex 1.1 char **layout2;
560 root 1.4 int i, j;
561 elmex 1.1
562 root 1.4 number_of_free_spots_in_room = 0;
563     room_free_spots_x = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
564     room_free_spots_y = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
565    
566     layout2 = (char **) calloc (sizeof (char *), RP->Xsize);
567 elmex 1.1 /* allocate and copy the layout, converting C to 0. */
568 root 1.4 for (i = 0; i < RP->Xsize; i++)
569     {
570     layout2[i] = (char *) calloc (sizeof (char), RP->Ysize);
571     for (j = 0; j < RP->Ysize; j++)
572 root 1.19 if (wall_blocked (map, i, j))
573     layout2[i][j] = '#';
574 root 1.4 }
575    
576     /* setup num_free_spots and room_free_spots */
577     find_spot_in_room_recursive (layout2, x, y, RP);
578    
579     if (number_of_free_spots_in_room > 0)
580     {
581 root 1.38 i = rmg_rndm (number_of_free_spots_in_room);
582 root 1.4 *kx = room_free_spots_x[i];
583     *ky = room_free_spots_y[i];
584 elmex 1.1 }
585    
586     /* deallocate the temp. layout */
587 root 1.4 for (i = 0; i < RP->Xsize; i++)
588 root 1.19 free (layout2[i]);
589    
590 root 1.4 free (layout2);
591     free (room_free_spots_x);
592     free (room_free_spots_y);
593 elmex 1.1 }
594    
595    
596     /* searches the map for a spot with walls around it. The more
597 root 1.2 walls the better, but it'll settle for 1 wall, or even 0, but
598     it'll return 0 if no FREE spots are found.*/
599 root 1.46 static void
600 root 1.12 find_enclosed_spot (maptile *map, int *cx, int *cy, random_map_params *RP)
601 root 1.4 {
602     int x, y;
603 elmex 1.1 int i;
604    
605 root 1.4 x = *cx;
606     y = *cy;
607    
608     for (i = 0; i <= SIZEOFFREE1; i++)
609     {
610     int lx, ly, sindex;
611    
612     lx = x + freearr_x[i];
613     ly = y + freearr_y[i];
614     sindex = surround_flag3 (map, lx, ly, RP);
615     /* if it's blocked on 3 sides, it's enclosed */
616     if (sindex == 7 || sindex == 11 || sindex == 13 || sindex == 14)
617     {
618     *cx = lx;
619     *cy = ly;
620     return;
621     }
622 elmex 1.1 }
623    
624     /* OK, if we got here, we're obviously someplace where there's no enclosed
625     spots--try to find someplace which is 2x enclosed. */
626 root 1.4 for (i = 0; i <= SIZEOFFREE1; i++)
627     {
628     int lx, ly, sindex;
629    
630     lx = x + freearr_x[i];
631     ly = y + freearr_y[i];
632     sindex = surround_flag3 (map, lx, ly, RP);
633     /* if it's blocked on 3 sides, it's enclosed */
634     if (sindex == 3 || sindex == 5 || sindex == 9 || sindex == 6 || sindex == 10 || sindex == 12)
635     {
636     *cx = lx;
637     *cy = ly;
638     return;
639     }
640 elmex 1.1 }
641    
642     /* settle for one surround point */
643 root 1.4 for (i = 0; i <= SIZEOFFREE1; i++)
644     {
645     int lx, ly, sindex;
646    
647     lx = x + freearr_x[i];
648     ly = y + freearr_y[i];
649     sindex = surround_flag3 (map, lx, ly, RP);
650     /* if it's blocked on 3 sides, it's enclosed */
651     if (sindex)
652     {
653     *cx = lx;
654     *cy = ly;
655     return;
656     }
657     }
658     /* give up and return the closest free spot. */
659 root 1.38 i = find_free_spot (archetype::find (shstr_chest), map, x, y, 1, SIZEOFFREE1 + 1);
660 root 1.17
661     if (i != -1)
662 root 1.4 {
663     *cx = x + freearr_x[i];
664     *cy = y + freearr_y[i];
665 elmex 1.1 }
666 root 1.17 else
667     {
668     /* indicate failure */
669     *cx = -1;
670     *cy = -1;
671     }
672 elmex 1.1 }
673    
674 root 1.47 static void
675 root 1.7 remove_monsters (int x, int y, maptile *map)
676 root 1.4 {
677 root 1.43 for (object *tmp = GET_MAP_OB (map, x, y); tmp; )
678     {
679     object *next = tmp->above;
680    
681     if (tmp->flag [FLAG_ALIVE])
682 root 1.44 tmp->head_ ()->destroy ();
683 elmex 1.1
684 root 1.43 tmp = next;
685     }
686 elmex 1.1 }
687    
688     /* surrounds the point x,y by doors, so as to enclose something, like
689 root 1.2 a chest. It only goes as far as the 8 squares surrounding, and
690     it'll remove any monsters it finds.*/
691 root 1.46 static object **
692 root 1.7 surround_by_doors (maptile *map, char **layout, int x, int y, int opts)
693 root 1.4 {
694 elmex 1.1 int i;
695 root 1.26 const char *doors[2];
696 elmex 1.1 object **doorlist;
697 root 1.4 int ndoors_made = 0;
698     doorlist = (object **) calloc (9, sizeof (object *)); /* 9 doors so we can hold termination null */
699 elmex 1.1
700     /* this is a list we pick from, for horizontal and vertical doors */
701 root 1.4 if (opts & DOORED)
702     {
703     doors[0] = "locked_door2";
704     doors[1] = "locked_door1";
705     }
706     else
707     {
708     doors[0] = "door_1";
709     doors[1] = "door_2";
710     }
711 elmex 1.1
712     /* place doors in all the 8 adjacent unblocked squares. */
713 root 1.4 for (i = 1; i < 9; i++)
714     {
715     int x1 = x + freearr_x[i], y1 = y + freearr_y[i];
716    
717 root 1.22 if (!wall_blocked (map, x1, y1) && layout[x1][y1] == '>')
718 root 1.4 { /* place a door */
719 root 1.22 remove_monsters (x1, y1, map);
720 root 1.4
721 root 1.22 object *new_door = get_archetype (freearr_x[i] == 0 ? doors[1] : doors[0]);
722     map->insert (new_door, x1, y1);
723 root 1.4 doorlist[ndoors_made] = new_door;
724     ndoors_made++;
725     }
726 elmex 1.1 }
727 root 1.22
728 elmex 1.1 return doorlist;
729     }
730    
731     /* returns the first door in this square, or NULL if there isn't a door. */
732 root 1.46 static object *
733 root 1.7 door_in_square (maptile *map, int x, int y)
734 root 1.4 {
735 root 1.46 for (object *tmp = GET_MAP_OB (map, x, y); tmp; tmp = tmp->above)
736 root 1.4 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
737     return tmp;
738 root 1.46
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.46 static 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.46 static 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.46 static 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     }