ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.55
Committed: Thu Jul 1 01:22:44 2010 UTC (13 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.54: +6 -11 lines
Log Message:
got rid of Layout, better memory management etc.

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.53 freeindex = rmg_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.54 place_treasure (maptile *map, char **layout, const 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.53 maptile *style_map = find_style ("/styles/treasurestyles", treasure_style, RP->difficulty);
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.53 maptile *trap_map = find_style ("/styles/trapstyles", "traps", RP->difficulty);
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.55 Layout layout2 (map->width, map->height);
503 root 1.36
504 root 1.55 // find walls
505     for (int i = 0; i < layout2.w; i++)
506     for (int j = 0; j < layout2.h; j++)
507     layout2[i][j] = wall_blocked (map, i, j) ? '#' : 0;
508 root 1.33
509 root 1.36 theMonsterToFind = 0;
510 root 1.4 theMonsterToFind = find_monster_in_room_recursive (layout2, map, x, y, RP);
511    
512 elmex 1.1 return theMonsterToFind;
513     }
514    
515 root 1.4 /* a datastructure needed by find_spot_in_room and find_spot_in_room_recursive */
516 root 1.46 static int *room_free_spots_x;
517     static int *room_free_spots_y;
518     static int number_of_free_spots_in_room;
519 elmex 1.1
520     /* the workhorse routine, which finds the free spots in a room:
521     a datastructure of free points is set up, and a position chosen from
522     that datastructure. */
523 root 1.46 static void
524 root 1.12 find_spot_in_room_recursive (char **layout, int x, int y, random_map_params *RP)
525 root 1.4 {
526     int i, j;
527 elmex 1.1
528     /* bounds check x and y */
529 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
530     return;
531 elmex 1.1
532     /* if the square is blocked or searched already, leave */
533 root 1.4 if (layout[x][y] != 0)
534     return;
535 elmex 1.1
536     /* set the current square as checked, and add it to the list.
537     set theMonsterToFind and return it. */
538     /* check off this point */
539 root 1.4 layout[x][y] = 1;
540     room_free_spots_x[number_of_free_spots_in_room] = x;
541     room_free_spots_y[number_of_free_spots_in_room] = y;
542 elmex 1.1 number_of_free_spots_in_room++;
543 root 1.19
544 elmex 1.1 /* now search all the 8 squares around recursively for free spots,in random order */
545 root 1.38 for (i = rmg_rndm (8), j = 0; j < 8 && theMonsterToFind == NULL; i++, j++)
546 root 1.19 find_spot_in_room_recursive (layout, x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1], RP);
547 elmex 1.1
548     }
549    
550     /* find a random non-blocked spot in this room to drop a key. */
551 root 1.46 static void
552 root 1.12 find_spot_in_room (maptile *map, int x, int y, int *kx, int *ky, random_map_params *RP)
553 root 1.4 {
554 elmex 1.1 char **layout2;
555 root 1.4 int i, j;
556 elmex 1.1
557 root 1.4 number_of_free_spots_in_room = 0;
558     room_free_spots_x = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
559     room_free_spots_y = (int *) calloc (sizeof (int), RP->Xsize * RP->Ysize);
560    
561     layout2 = (char **) calloc (sizeof (char *), RP->Xsize);
562 elmex 1.1 /* allocate and copy the layout, converting C to 0. */
563 root 1.4 for (i = 0; i < RP->Xsize; i++)
564     {
565     layout2[i] = (char *) calloc (sizeof (char), RP->Ysize);
566     for (j = 0; j < RP->Ysize; j++)
567 root 1.19 if (wall_blocked (map, i, j))
568     layout2[i][j] = '#';
569 root 1.4 }
570    
571     /* setup num_free_spots and room_free_spots */
572     find_spot_in_room_recursive (layout2, x, y, RP);
573    
574     if (number_of_free_spots_in_room > 0)
575     {
576 root 1.38 i = rmg_rndm (number_of_free_spots_in_room);
577 root 1.4 *kx = room_free_spots_x[i];
578     *ky = room_free_spots_y[i];
579 elmex 1.1 }
580    
581     /* deallocate the temp. layout */
582 root 1.4 for (i = 0; i < RP->Xsize; i++)
583 root 1.19 free (layout2[i]);
584    
585 root 1.4 free (layout2);
586     free (room_free_spots_x);
587     free (room_free_spots_y);
588 elmex 1.1 }
589    
590    
591     /* searches the map for a spot with walls around it. The more
592 root 1.2 walls the better, but it'll settle for 1 wall, or even 0, but
593     it'll return 0 if no FREE spots are found.*/
594 root 1.46 static void
595 root 1.12 find_enclosed_spot (maptile *map, int *cx, int *cy, random_map_params *RP)
596 root 1.4 {
597     int x, y;
598 elmex 1.1 int i;
599    
600 root 1.4 x = *cx;
601     y = *cy;
602    
603     for (i = 0; i <= SIZEOFFREE1; i++)
604     {
605     int lx, ly, sindex;
606    
607     lx = x + freearr_x[i];
608     ly = y + freearr_y[i];
609     sindex = surround_flag3 (map, lx, ly, RP);
610     /* if it's blocked on 3 sides, it's enclosed */
611     if (sindex == 7 || sindex == 11 || sindex == 13 || sindex == 14)
612     {
613     *cx = lx;
614     *cy = ly;
615     return;
616     }
617 elmex 1.1 }
618    
619     /* OK, if we got here, we're obviously someplace where there's no enclosed
620     spots--try to find someplace which is 2x enclosed. */
621 root 1.4 for (i = 0; i <= SIZEOFFREE1; i++)
622     {
623     int lx, ly, sindex;
624    
625     lx = x + freearr_x[i];
626     ly = y + freearr_y[i];
627     sindex = surround_flag3 (map, lx, ly, RP);
628     /* if it's blocked on 3 sides, it's enclosed */
629     if (sindex == 3 || sindex == 5 || sindex == 9 || sindex == 6 || sindex == 10 || sindex == 12)
630     {
631     *cx = lx;
632     *cy = ly;
633     return;
634     }
635 elmex 1.1 }
636    
637     /* settle for one surround point */
638 root 1.4 for (i = 0; i <= SIZEOFFREE1; i++)
639     {
640     int lx, ly, sindex;
641    
642     lx = x + freearr_x[i];
643     ly = y + freearr_y[i];
644     sindex = surround_flag3 (map, lx, ly, RP);
645     /* if it's blocked on 3 sides, it's enclosed */
646     if (sindex)
647     {
648     *cx = lx;
649     *cy = ly;
650     return;
651     }
652     }
653     /* give up and return the closest free spot. */
654 root 1.53 i = rmg_find_free_spot (archetype::find (shstr_chest), map, x, y, 1, SIZEOFFREE1 + 1);
655 root 1.17
656     if (i != -1)
657 root 1.4 {
658     *cx = x + freearr_x[i];
659     *cy = y + freearr_y[i];
660 elmex 1.1 }
661 root 1.17 else
662     {
663     /* indicate failure */
664     *cx = -1;
665     *cy = -1;
666     }
667 elmex 1.1 }
668    
669 root 1.47 static void
670 root 1.7 remove_monsters (int x, int y, maptile *map)
671 root 1.4 {
672 root 1.43 for (object *tmp = GET_MAP_OB (map, x, y); tmp; )
673     {
674     object *next = tmp->above;
675    
676     if (tmp->flag [FLAG_ALIVE])
677 root 1.44 tmp->head_ ()->destroy ();
678 elmex 1.1
679 root 1.43 tmp = next;
680     }
681 elmex 1.1 }
682    
683     /* surrounds the point x,y by doors, so as to enclose something, like
684 root 1.2 a chest. It only goes as far as the 8 squares surrounding, and
685     it'll remove any monsters it finds.*/
686 root 1.46 static object **
687 root 1.7 surround_by_doors (maptile *map, char **layout, int x, int y, int opts)
688 root 1.4 {
689 elmex 1.1 int i;
690 root 1.26 const char *doors[2];
691 elmex 1.1 object **doorlist;
692 root 1.4 int ndoors_made = 0;
693     doorlist = (object **) calloc (9, sizeof (object *)); /* 9 doors so we can hold termination null */
694 elmex 1.1
695     /* this is a list we pick from, for horizontal and vertical doors */
696 root 1.4 if (opts & DOORED)
697     {
698     doors[0] = "locked_door2";
699     doors[1] = "locked_door1";
700     }
701     else
702     {
703     doors[0] = "door_1";
704     doors[1] = "door_2";
705     }
706 elmex 1.1
707     /* place doors in all the 8 adjacent unblocked squares. */
708 root 1.4 for (i = 1; i < 9; i++)
709     {
710     int x1 = x + freearr_x[i], y1 = y + freearr_y[i];
711    
712 root 1.22 if (!wall_blocked (map, x1, y1) && layout[x1][y1] == '>')
713 root 1.4 { /* place a door */
714 root 1.22 remove_monsters (x1, y1, map);
715 root 1.4
716 root 1.22 object *new_door = get_archetype (freearr_x[i] == 0 ? doors[1] : doors[0]);
717     map->insert (new_door, x1, y1);
718 root 1.4 doorlist[ndoors_made] = new_door;
719     ndoors_made++;
720     }
721 elmex 1.1 }
722 root 1.22
723 elmex 1.1 return doorlist;
724     }
725    
726     /* returns the first door in this square, or NULL if there isn't a door. */
727 root 1.46 static object *
728 root 1.7 door_in_square (maptile *map, int x, int y)
729 root 1.4 {
730 root 1.46 for (object *tmp = GET_MAP_OB (map, x, y); tmp; tmp = tmp->above)
731 root 1.4 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
732     return tmp;
733 root 1.46
734 elmex 1.1 return NULL;
735     }
736 root 1.4
737 elmex 1.1 /* the workhorse routine, which finds the doors in a room */
738 root 1.46 static void
739 root 1.12 find_doors_in_room_recursive (char **layout, maptile *map, int x, int y, object **doorlist, int *ndoors, random_map_params *RP)
740 root 1.4 {
741     int i, j;
742 elmex 1.1 object *door;
743    
744     /* bounds check x and y */
745 root 1.4 if (!(x >= 0 && y >= 0 && x < RP->Xsize && y < RP->Ysize))
746     return;
747 elmex 1.1
748     /* if the square is blocked or searched already, leave */
749 root 1.4 if (layout[x][y] == 1)
750     return;
751 elmex 1.1
752     /* check off this point */
753 root 1.4 if (layout[x][y] == '#')
754     { /* there could be a door here */
755     layout[x][y] = 1;
756     door = door_in_square (map, x, y);
757 root 1.13 if (door)
758 elmex 1.1 {
759 root 1.4 doorlist[*ndoors] = door;
760 root 1.33
761 root 1.13 if (*ndoors > 1022) /* eek! out of memory */
762 root 1.4 {
763     LOG (llevError, "find_doors_in_room_recursive:Too many doors for memory allocated!\n");
764     return;
765     }
766 root 1.13
767 root 1.4 *ndoors = *ndoors + 1;
768 elmex 1.1 }
769     }
770 root 1.4 else
771     {
772     layout[x][y] = 1;
773 root 1.24
774 root 1.4 /* now search all the 8 squares around recursively for free spots,in random order */
775 root 1.38 for (i = rmg_rndm (8), j = 0; j < 8 && !theMonsterToFind; i++, j++)
776 root 1.24 find_doors_in_room_recursive (layout, map,
777     x + freearr_x[i % 8 + 1], y + freearr_y[i % 8 + 1],
778     doorlist, ndoors, RP);
779 elmex 1.1 }
780     }
781    
782     /* find a random non-blocked spot in this room to drop a key. */
783 root 1.46 static object **
784 root 1.12 find_doors_in_room (maptile *map, int x, int y, random_map_params *RP)
785 root 1.4 {
786     int i, j;
787     int ndoors = 0;
788 elmex 1.1
789 root 1.35 object **doorlist = (object **)calloc (sizeof (int), 1024);
790 root 1.33
791 root 1.55 Layout layout2 (RP->Xsize, RP->Ysize);
792 root 1.36 layout2.clear ();
793 elmex 1.1
794     /* allocate and copy the layout, converting C to 0. */
795 root 1.4 for (i = 0; i < RP->Xsize; i++)
796 root 1.33 for (j = 0; j < RP->Ysize; j++)
797 root 1.36 layout2[i][j] = wall_blocked (map, i, j) ? '#' : 0;
798 root 1.4
799     /* setup num_free_spots and room_free_spots */
800     find_doors_in_room_recursive (layout2, map, x, y, doorlist, &ndoors, RP);
801 elmex 1.1
802     return doorlist;
803     }
804    
805     /* locks and/or hides all the doors in doorlist, or does nothing if
806 root 1.2 opts doesn't say to lock/hide doors. */
807 root 1.46 static void
808 root 1.12 lock_and_hide_doors (object **doorlist, maptile *map, int opts, random_map_params *RP)
809 root 1.4 {
810 elmex 1.1 object *door;
811     int i;
812 root 1.4
813 elmex 1.1 /* lock the doors and hide the keys. */
814 root 1.4
815     if (opts & DOORED)
816     {
817 root 1.43 for (i = 0, door = doorlist[0]; doorlist[i]; i++)
818 root 1.4 {
819 root 1.41 object *new_door = get_archetype (shstr_locked_door1);
820 root 1.4
821     door = doorlist[i];
822     new_door->face = door->face;
823     new_door->x = door->x;
824     new_door->y = door->y;
825 root 1.44 door->destroy ();
826 root 1.4 doorlist[i] = new_door;
827     insert_ob_in_map (new_door, map, NULL, 0);
828 root 1.39 new_door->slaying = format ("RMG-%d-%d", (int)rmg_rndm (1000000000), (int)rmg_rndm (1000000000));
829     keyplace (map, new_door->x, new_door->y, new_door->slaying, NO_PASS_DOORS, 2, RP);
830 root 1.4 }
831 elmex 1.1 }
832    
833     /* change the faces of the doors and surrounding walls to hide them. */
834 root 1.4 if (opts & HIDDEN)
835     {
836     for (i = 0, door = doorlist[0]; doorlist[i] != NULL; i++)
837     {
838     object *wallface;
839    
840     door = doorlist[i];
841     wallface = retrofit_joined_wall (map, door->x, door->y, 1, RP);
842     if (wallface != NULL)
843     {
844     retrofit_joined_wall (map, door->x - 1, door->y, 0, RP);
845     retrofit_joined_wall (map, door->x + 1, door->y, 0, RP);
846     retrofit_joined_wall (map, door->x, door->y - 1, 0, RP);
847     retrofit_joined_wall (map, door->x, door->y + 1, 0, RP);
848 root 1.36
849 root 1.4 door->face = wallface->face;
850 root 1.36
851 root 1.44 wallface->destroy ();
852 root 1.4 }
853     }
854 elmex 1.1 }
855     }