ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.17
Committed: Mon Jan 15 15:54:19 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.16: +10 -5 lines
Log Message:
experimental stability and correctness changes

File Contents

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