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