| 1 |
elmex |
1.1 |
/* |
| 2 |
|
|
* static char *rcsid_treasure_c = |
| 3 |
|
|
* "$Id$"; |
| 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 |
|
|
} |