ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.38
Committed: Sun May 4 14:12:38 2008 UTC (16 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_53
Changes since 1.37: +25 -29 lines
Log Message:
lotsa

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