ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.35
Committed: Mon Apr 14 22:41:17 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.34: +1 -1 lines
Log Message:
refactor random map gen more

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