ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.39
Committed: Thu May 8 11:39:24 2008 UTC (16 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.38: +119 -115 lines
Log Message:
*** empty log message ***

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