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