ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.43
Committed: Mon Sep 29 09:04:51 2008 UTC (15 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.42: +14 -22 lines
Log Message:
Replace all destroy calls by destroy (true) (after checking), add
temporary debugging code for lost objects, fix some minor issues in random_maps.

File Contents

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