ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/treasure.C
Revision: 1.67
Committed: Sat Nov 17 23:40:02 2018 UTC (5 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.66: +1 -0 lines
Log Message:
copyright update 2018

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