version 1.18 | | version 1.19 |
---|
| | |
/* | | /* |
* static char *rcsid_map_c = | | * static char *rcsid_map_c = |
* "$Id: map.c,v 1.18 2001/06/04 06:41:02 mwedel Exp $"; | | * "$Id: map.c,v 1.19 2001/07/14 04:04:53 mwedel Exp $"; |
*/ | | */ |
| | |
/* | | /* |
| | |
| | |
extern int nrofallocobjects,nroffreeobjects; | | extern int nrofallocobjects,nroffreeobjects; |
| | |
| | #if 0 |
| | /* If 0 this block because I don't know if it is still needed. |
| | * if it is, it really should be done via autoconf now days |
| | * and not by specific machine checks. |
| | */ |
| | |
#if defined(sgi) | | #if defined(sgi) |
/* popen_local is defined in porting.c */ | | /* popen_local is defined in porting.c */ |
#define popen popen_local | | #define popen popen_local |
| | |
#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) | | #define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) |
#endif | | #endif |
#endif | | #endif |
| | #endif |
/* This is a list of the suffix, uncompress and compress functions. Thus, | | |
* if you have some other compress program you want to use, the only thing | | |
* that needs to be done is to extended this. | | |
* The first entry must be NULL - this is what is used for non | | |
* compressed files. | | |
*/ | | |
#define NROF_COMPRESS_METHODS 4 | | |
char *uncomp[4][3] = { | | |
{NULL, NULL, NULL}, | | |
{".Z", UNCOMPRESS, COMPRESS}, | | |
{".gz", GUNZIP, GZIP}, | | |
{".bz2", BUNZIP, BZIP} | | |
}; | | |
| | |
| | |
/* | | /* |
* Returns the mapstruct which has a name matching the given argument. | | * Returns the mapstruct which has a name matching the given argument. |
| | * return NULL if no match is found. |
*/ | | */ |
| | |
mapstruct *has_been_loaded (char *name) { | | mapstruct *has_been_loaded (char *name) { |
| | |
* If prepend_dir is set, then we call create_pathname (which prepends | | * If prepend_dir is set, then we call create_pathname (which prepends |
* libdir & mapdir). Otherwise, we assume the name given is fully | | * libdir & mapdir). Otherwise, we assume the name given is fully |
* complete. | | * complete. |
| | * Only the editor actually cares about the writablity of this - |
| | * the rest of the code only cares that the file is readable. |
| | * when the editor goes away, the call to stat should probably be |
| | * replaced by an access instead (similar to the windows one, but |
| | * that seems to be missing the prepend_dir processing |
*/ | | */ |
| | |
int check_path (char *name, int prepend_dir) | | int check_path (char *name, int prepend_dir) |
| | |
else | | else |
strcpy(buf, name); | | strcpy(buf, name); |
| | |
if ((endbuf = strchr(buf, '\0')) == NULL) | | /* old method (strchr(buf, '\0')) seemd very odd to me - |
return (-1); | | * this method should be equivalant and is clearer. |
| | * Can not use strcat because we need to cycle through |
| | * all the names. |
| | */ |
| | endbuf = buf + strlen(buf); |
| | |
for (i = 0; i < NROF_COMPRESS_METHODS; i++) { | | for (i = 0; i < NROF_COMPRESS_METHODS; i++) { |
if (uncomp[i][0]) | | if (uncomp[i][0]) |
| | |
#endif | | #endif |
} | | } |
| | |
void dump_map_lights(mapstruct *m) { | | |
objectlink *obl=m->light; | | |
object *tmp=NULL; | | |
while(obl) { | | |
if((tmp=obl->ob)) | | |
LOG(llevDebug,"%s (%d) radius:%d\n",tmp->name,tmp->count,tmp->glow_radius); | | |
obl=obl->next; | | |
} | | |
} | | |
| | |
/* | | /* |
* Prints out debug-information about a map. | | * Prints out debug-information about a map. |
| | * Dumping these at llevError doesn't seem right, but is |
| | * necessary to make sure the information is in fact logged. |
*/ | | */ |
| | |
void dump_map(mapstruct *m) { | | void dump_map(mapstruct *m) { |
LOG(llevError,"Map %s status: %d.\n",m->path,m->in_memory); | | LOG(llevError,"Map %s status: %d.\n",m->path,m->in_memory); |
LOG(llevError,"Size: %dx%d Start: %d,%d\n", | | LOG(llevError,"Size: %dx%d Start: %d,%d\n", |
m->map_object->x,m->map_object->y, | | MAP_WIDTH(m), MAP_HEIGHT(m), |
EXIT_X(m->map_object), EXIT_Y(m->map_object)); | | MAP_ENTER_X(m), MAP_ENTER_Y(m)); |
if(m->map_object->msg!=NULL) | | |
LOG(llevError,"Message:\n%s",m->map_object->msg); | | if(m->msg!=NULL) |
| | LOG(llevError,"Message:\n%s",m->msg); |
| | |
if(m->tmpname!=NULL) | | if(m->tmpname!=NULL) |
LOG(llevError,"Tmpname: %s\n",m->tmpname); | | LOG(llevError,"Tmpname: %s\n",m->tmpname); |
| | |
LOG(llevError,"Difficulty: %d\n",m->difficulty); | | LOG(llevError,"Difficulty: %d\n",m->difficulty); |
#ifdef USE_LIGHTING | | |
LOG(llevError,"Darkness: %d\n",m->darkness); | | LOG(llevError,"Darkness: %d\n",m->darkness); |
#endif | | |
} | | } |
| | |
/* | | /* |
* Prints out debug-information about all maps. | | * Prints out debug-information about all maps. |
| | * This basically just goes through all the maps and calls |
| | * dump_map on each one. |
*/ | | */ |
| | |
void dump_all_maps() { | | void dump_all_maps() { |
mapstruct *m; | | mapstruct *m; |
for(m=first_map;m!=NULL;m=m->next) { | | for(m=first_map;m!=NULL;m=m->next) { |
LOG(llevError,"Map %s status %d.\n",m->path,m->in_memory); | | dump_map(m); |
LOG(llevError,"Tmpname: %s\n",m->tmpname); | | |
} | | } |
} | | } |
| | |
| | |
*/ | | */ |
| | |
int wall(mapstruct *m, int x,int y) { | | int wall(mapstruct *m, int x,int y) { |
| | |
if (out_of_map(m,x,y)) | | if (out_of_map(m,x,y)) |
return 1; | | return 1; |
return (get_map(m,x,y))->flags & P_NO_PASS; | | return (GET_MAP_FLAGS(m,x,y) & P_NO_PASS); |
} | | } |
| | |
/* | | /* |
| | |
*/ | | */ |
| | |
int blocks_view(mapstruct *m, int x, int y) { | | int blocks_view(mapstruct *m, int x, int y) { |
if(out_of_map(m,x,y)) | | mapstruct *nm; |
| | |
| | nm = get_map_from_coord(m, &x, &y); |
| | if(!nm) |
return 1; | | return 1; |
return (get_map(m,x,y))->flags & P_BLOCKSVIEW; | | return (GET_MAP_FLAGS(nm,x,y) & P_BLOCKSVIEW); |
} | | } |
| | |
/* | | /* |
| | |
int blocks_magic(mapstruct *m, int x, int y) { | | int blocks_magic(mapstruct *m, int x, int y) { |
if(out_of_map(m,x,y)) | | if(out_of_map(m,x,y)) |
return 1; | | return 1; |
return (get_map(m,x,y))->flags & P_NO_MAGIC; | | return (GET_MAP_FLAGS(m,x,y) & P_NO_MAGIC); |
| | |
} | | } |
| | |
| | |
int blocks_cleric(mapstruct *m, int x, int y) { | | int blocks_cleric(mapstruct *m, int x, int y) { |
if(out_of_map(m,x,y)) | | if(out_of_map(m,x,y)) |
return 1; | | return 1; |
return (get_map(m,x,y))->flags & P_NO_CLERIC; | | return (GET_MAP_FLAGS(m,x,y) & P_NO_CLERIC); |
} | | } |
| | |
/* | | /* |
* Returns true if the given coordinate in the given map blocks passage. | | * Returns true if the given coordinate in the given map blocks passage. |
| | * either alive or no_pass means the space is blocked. |
*/ | | */ |
| | |
int blocked(mapstruct *m, int x, int y) { | | int blocked(mapstruct *m, int x, int y) { |
int r; | | |
MapLook *f; | | |
if(out_of_map(m,x,y)) | | if(out_of_map(m,x,y)) |
return 1; | | return 1; |
f = get_map(m,x,y); | | return (GET_MAP_FLAGS(m,x,y) & (P_NO_PASS | P_IS_ALIVE)); |
r = f->flags & (P_NO_PASS | P_IS_ALIVE); | | |
return r; | | |
} | | } |
| | |
/* | | /* |
* Returns true if the given coordinate in the map where the given object | | * Returns true if the given coordinate is blocked by the |
* is, blocks the given object (which may be multi-part) | | * object passed is not blocking. This is used with |
| | * multipart monsters - if we want to see if a 2x2 monster |
| | * can move 1 space to the left, we don't want its own area |
| | * to block it from moving there. |
| | * Returns TRUE if the space is blocked by something other than the |
| | * monster. |
*/ | | */ |
| | |
int blocked_link(object *ob, int x, int y) { | | int blocked_link(object *ob, int x, int y) { |
object *tmp; | | object *tmp; |
| | |
if(out_of_map(ob->map,x,y)) | | if(out_of_map(ob->map,x,y)) |
return 1; | | return 1; |
if (!blocked(ob->map,x,y)) /* no need to go further */ | | |
return 0; | | /* If space is currently not blocked by anything, no need to |
| | * go further. Same logic as blocked_above. |
| | */ |
| | if (! (GET_MAP_FLAGS(ob->map, x,y) & (P_NO_PASS | P_IS_ALIVE))) return 0; |
| | |
| | |
if(ob->head != NULL) | | if(ob->head != NULL) |
ob=ob->head; | | ob=ob->head; |
for(tmp = get_map_ob(ob->map,x,y); tmp!= NULL; tmp = tmp->above) | | |
| | /* We basically go through the stack of objects, and if there is |
| | * some other object that has NO_PASS or FLAG_ALIVE set, return |
| | * true. If we get through the entire stack, that must mean |
| | * ob is blocking it, so return 0. |
| | */ |
| | for(tmp = GET_MAP_OB(ob->map,x,y); tmp!= NULL; tmp = tmp->above) |
if (QUERY_FLAG(tmp,FLAG_NO_PASS) || (QUERY_FLAG(tmp,FLAG_ALIVE) && | | if (QUERY_FLAG(tmp,FLAG_NO_PASS) || (QUERY_FLAG(tmp,FLAG_ALIVE) && |
tmp->head != ob && tmp != ob)) | | tmp->head != ob && tmp != ob)) |
return 1; | | return 1; |
| | |
return 0; | | return 0; |
} | | } |
| | |
| | |
* Eneq(@csd.uu.se): This is a new version of blocked, this one handles objects | | * Eneq(@csd.uu.se): This is a new version of blocked, this one handles objects |
* that can be passed through by monsters with the CAN_PASS_THRU defined. | | * that can be passed through by monsters with the CAN_PASS_THRU defined. |
* Returns 1 if the object can not pass through that space. | | * Returns 1 if the object can not pass through that space. |
* This code really needs to be rewritten so it would at least be somewhat | | * This is a fairly inefficient check - it needs to look at every object |
* reasonable to read. | | * on the space. A call to blocked should be made first, and only if the |
| | * space is blocked should this function be called. |
*/ | | */ |
| | |
int blocked_two(object *op, int x,int y) { | | int blocked_two(object *op, int x,int y) { |
object *tmp; | | object *tmp; |
| | |
if(out_of_map(op->map,x,y)) | | if(out_of_map(op->map,x,y)) |
return 1; | | return 1; |
tmp=get_map_ob(op->map,x,y); | | |
for(tmp=get_map_ob(op->map,x,y);tmp!=NULL;tmp=tmp->above){ | | for(tmp=GET_MAP_OB(op->map,x,y);tmp!=NULL;tmp=tmp->above){ |
if((QUERY_FLAG(tmp,FLAG_ALIVE)&&tmp->type!=DOOR)|| | | |
(QUERY_FLAG(tmp,FLAG_NO_PASS)&&!QUERY_FLAG(tmp,FLAG_PASS_THRU)&&tmp->type!=CHECK_INV)|| | | /* I broke this into multiple if statements to make it |
(QUERY_FLAG(tmp,FLAG_NO_PASS)&&QUERY_FLAG(tmp,FLAG_PASS_THRU)&& | | * clearer. Logic is the same, and a good compiler will take |
!QUERY_FLAG(op,FLAG_CAN_PASS_THRU)&&tmp->type!=CHECK_INV)) | | * care of any optimizations for us. |
return 1; | | |
/* check inv doors by Florian Beck */ | | |
/* if check_inv has last_sp=1, the player can only move if he has the | | |
* object | | |
*/ | | */ |
| | |
| | /* Can not pass through doors */ |
| | if (QUERY_FLAG(tmp,FLAG_ALIVE) && tmp->type!=DOOR) return 1; |
| | |
| | /* This must be before the checks below. Code for inventory checkers. */ |
if (tmp->type==CHECK_INV&&QUERY_FLAG(tmp,FLAG_NO_PASS)) { | | if (tmp->type==CHECK_INV&&QUERY_FLAG(tmp,FLAG_NO_PASS)) { |
if (tmp->last_sp){ /* player needs object */ | | /* If last_sp is set, the player/monster needs an object, |
if (check_inv_recursive(op,tmp)==NULL) /* player doesn't have object | | * so we check for it. If they don't have it, they can't |
so no pass */ | | * pass through this space. |
| | */ |
| | if (tmp->last_sp) { |
| | if (check_inv_recursive(op,tmp)==NULL) |
return 1; | | return 1; |
} else | | } else { |
/* player must *not* have object */ | | /* In this case, the player must not have the object - |
| | * if they do, they can't pass through. |
| | */ |
if (check_inv_recursive(op,tmp)!=NULL) /* player has object */ | | if (check_inv_recursive(op,tmp)!=NULL) /* player has object */ |
return 1; | | return 1; |
} | | } |
| | } /* if check_inv */ |
| | |
| | /* Can't get through this space */ |
| | if (QUERY_FLAG(tmp,FLAG_NO_PASS) && !QUERY_FLAG(tmp,FLAG_PASS_THRU)) |
| | return 1; |
| | |
| | /* slightly different than above case */ |
| | if (QUERY_FLAG(tmp,FLAG_NO_PASS) && QUERY_FLAG(tmp,FLAG_PASS_THRU) && |
| | !QUERY_FLAG(op,FLAG_CAN_PASS_THRU)) return 1; |
} | | } |
/* end fb */ | | |
return 0; | | return 0; |
} | | } |
| | |
/* | | /* |
* Returns true if the given archetype can't fit in the given spot. | | * Returns true if the given archetype can't fit in the given spot. |
| | * Unlike most of the above functions, this one does not check for |
| | * out_of_map status. arch_out_of_map should be called before |
| | * this in all cases. |
*/ | | */ |
| | |
int arch_blocked(archetype *at,mapstruct *m,int x,int y) { | | int arch_blocked(archetype *at,mapstruct *m,int x,int y) { |
archetype *tmp; | | archetype *tmp; |
| | |
if(at==NULL) | | if(at==NULL) |
return blocked(m,x,y); | | return blocked(m,x,y); |
for(tmp=at;tmp!=NULL;tmp=tmp->more) | | for(tmp=at;tmp!=NULL;tmp=tmp->more) |
| | |
| | |
int arch_out_of_map(archetype *at,mapstruct *m,int x,int y) { | | int arch_out_of_map(archetype *at,mapstruct *m,int x,int y) { |
archetype *tmp; | | archetype *tmp; |
| | |
if(at==NULL) | | if(at==NULL) |
return out_of_map(m,x,y); | | return out_of_map(m,x,y); |
| | |
for(tmp=at;tmp!=NULL;tmp=tmp->more) | | for(tmp=at;tmp!=NULL;tmp=tmp->more) |
if(out_of_map(m,x+tmp->clone.x,y+tmp->clone.y)) | | if(out_of_map(m,x+tmp->clone.x,y+tmp->clone.y)) |
return 1; | | return 1; |
return 0; | | return 0; |
} | | } |
| | |
/* | | |
* Goes through all objects in the given map, and does a sanity-check | | |
* on all pointers. | | |
*/ | | |
| | |
void refresh_map(mapstruct *m) { | | |
int x,y; | | |
object *tmp,*tmp2=NULL,*active=NULL; | | |
| | |
if(m==NULL || m->in_memory != MAP_IN_MEMORY) | | |
return; | | |
for(x=0;x<m->map_object->x;x++) | | |
for(y=0;y<m->map_object->y;y++) { | | |
/* Eneq(@csd.uu.se): Hunting down inappropriate objects in the map. The game | | |
sometime hangs and tries to remove_removed objects etc. */ | | |
for(active=tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp2) | | |
{ /* how can we get objects with pointer<1024, mta */ | | |
if (tmp < (object *) 1024) /* map is uninitialized? */ | | |
break; | | |
tmp2=tmp->above; | | |
active=tmp; | | |
if (QUERY_FLAG(tmp,FLAG_REMOVED)) { | | |
LOG(llevError,"Crossfire: Found removed object in map.\n"); | | |
active=(tmp->above==NULL?tmp->below:tmp->above); | | |
if (tmp->below==NULL&&tmp2==NULL) | | |
m->map_ob[x+m->map_object->x*y]=NULL; | | |
else if (tmp->below==NULL&&tmp2!=NULL) { | | |
tmp2->below=NULL; | | |
m->map_ob[x+m->map_object->x*y]=tmp2; | | |
} else if (tmp->below!=NULL&&tmp2==NULL) | | |
tmp->below->above=NULL; | | |
else if (tmp->below!=NULL&&tmp2!=NULL) { | | |
tmp->below->above=tmp2; | | |
tmp2->below=tmp->below; | | |
} | | |
if (!QUERY_FLAG(tmp,FLAG_FREED)) | | |
free_object(tmp); | | |
} | | |
if (QUERY_FLAG(tmp,FLAG_FREED)&&!QUERY_FLAG(tmp,FLAG_REMOVED)) { | | |
LOG(llevError, "Crossfire: Found freed object in map.\n"); | | |
remove_ob(tmp); | | |
} | | |
} | | |
| | |
if(active != NULL) | | |
update_object(active); | | |
} | | |
} | | |
| | |
/* | | |
* open_and_uncompress() first searches for the original filename. | | |
* if it exist, then it opens it and returns the file-pointer. | | |
* if not, it does two things depending on the flag. If the flag | | |
* is set, it tries to create the original file by uncompressing a .Z file. | | |
* If the flag is not set, it creates a pipe that is used for | | |
* reading the file (NOTE - you can not use fseek on pipes) | | |
* | | |
* The compressed pointer is set to nonzero if the file is | | |
* compressed (and thus, fp is actually a pipe.) It returns 0 | | |
* if it is a normal file | | |
* | | |
* (Note, the COMPRESS_SUFFIX is used instead of ".Z", thus it can easily | | |
* be changed in the config file.) | | |
*/ | | |
| | |
FILE *open_and_uncompress(char *name,int flag, int *compressed) { | | |
FILE *fp; | | |
char buf[MAX_BUF],buf2[MAX_BUF], *bufend; | | |
int try_once = 0; | | |
| | |
strcpy(buf, name); | | |
bufend = buf + strlen(buf); | | |
| | |
/* LOG(llevDebug, "open_and_uncompress(%s)\n", name); | | |
*/ | | |
| | |
/* strip off any compression prefixes that may exist */ | | |
for (*compressed = 0; *compressed < NROF_COMPRESS_METHODS; (*compressed)++) { | | |
if ((uncomp[*compressed][0]) && | | |
(!strcmp(uncomp[*compressed][0], bufend - strlen(uncomp[*compressed][0])))) { | | |
buf[strlen(buf) - strlen(uncomp[*compressed][0])] = '\0'; | | |
bufend = buf + strlen(buf); | | |
} | | |
} | | |
for (*compressed = 0; *compressed < NROF_COMPRESS_METHODS; (*compressed)++) { | | |
struct stat statbuf; | | |
| | |
if (uncomp[*compressed][0]) | | |
strcpy(bufend, uncomp[*compressed][0]); | | |
if (stat(buf, &statbuf)) { | | |
| | |
/* LOG(llevDebug, "Failed to stat %s\n", buf); | | |
*/ | | |
continue; | | |
} | | |
/* LOG(llevDebug, "Found file %s\n", buf); | | |
*/ | | |
if (uncomp[*compressed][0]) { | | |
strcpy(buf2, uncomp[*compressed][1]); | | |
strcat(buf2, " < "); | | |
strcat(buf2, buf); | | |
if (flag) { | | |
int i; | | |
if (try_once) { | | |
LOG(llevError, "Failed to open %s after decompression.\n", name); | | |
return NULL; | | |
} | | |
try_once = 1; | | |
strcat(buf2, " > "); | | |
strcat(buf2, name); | | |
LOG(llevDebug, "system(%s)\n", buf2); | | |
if ((i=system(buf2))) { | | |
LOG(llevError, "system(%s) returned %d\n", buf2, i); | | |
return NULL; | | |
} | | |
unlink(buf); /* Delete the original */ | | |
*compressed = '\0'; /* Restart the loop from the beginning */ | | |
chmod(name, statbuf.st_mode); | | |
continue; | | |
} | | |
if ((fp = popen(buf2, "r")) != NULL) | | |
return fp; | | |
} else if((fp=fopen(name,"r"))!=NULL) { | | |
struct stat statbuf; | | |
if (fstat (fileno (fp), &statbuf) || ! S_ISREG (statbuf.st_mode)) { | | |
LOG (llevDebug, "Can't open %s - not a regular file\n", name); | | |
(void) fclose (fp); | | |
errno = EISDIR; | | |
return NULL; | | |
} | | |
return fp; | | |
} | | |
} | | |
LOG(llevDebug, "Can't open %s\n", name); | | |
return NULL; | | |
} | | |
| | |
/* | | |
* See open_and_uncompress(). | | |
*/ | | |
| | |
void close_and_delete(FILE *fp, int compressed) { | | |
if (compressed) | | |
pclose(fp); | | |
else | | |
fclose(fp); | | |
} | | |
| | |
/* When the map is loaded, load_object does not actually insert objects | | /* When the map is loaded, load_object does not actually insert objects |
* into inventory, but just links them. What this does is go through | | * into inventory, but just links them. What this does is go through |
* and insert them properly. | | * and insert them properly. |
| | |
sum_weight(container); | | sum_weight(container); |
} | | } |
| | |
| | /* link_multipart_objects go through all the objects on the map looking |
| | * for objects whose arch says they are multipart yet according to the |
| | * info we have, they only have the head (as would be expected when |
| | * they are saved). We do have to look for the old maps that did save |
| | * the more sections and not re-add sections for them. |
| | */ |
| | |
| | static void link_multipart_objects(mapstruct *m) |
| | { |
| | int x,y; |
| | object *tmp, *op, *last, *above; |
| | archetype *at; |
| | |
| | for(x=0;x<MAP_WIDTH(m);x++) |
| | for(y=0;y<MAP_HEIGHT(m);y++) |
| | for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=above) { |
| | above=tmp->above; |
| | |
| | /* already multipart - don't do anything more */ |
| | if (tmp->head || tmp->more) continue; |
| | |
| | /* If there is nothing more to this object, this for loop |
| | * won't do anything. |
| | */ |
| | for (at = tmp->arch->more, last=tmp; at != NULL; at=at->more, last=op) { |
| | op = arch_to_object(at); |
| | |
| | /* update x,y coordinates */ |
| | op->x += tmp->x; |
| | op->y += tmp->y; |
| | op->head = tmp; |
| | op->map = m; |
| | last->more = op; |
| | /* we could link all the parts onto tmp, and then just |
| | * call insert_ob_in_map once, but the effect is the same, |
| | * as insert_ob_in_map will call itself with each part, and |
| | * the coding is simpler to just to it here with each part. |
| | */ |
| | insert_ob_in_map(op, op->map, tmp,INS_NO_MERGE|INS_ABOVE_FLOOR_ONLY|INS_NO_WALK_ON); |
| | } /* for at = tmp->arch->more */ |
| | } /* for objects on this space */ |
| | } |
| | |
| | |
| | |
/* | | /* |
* Loads (ands parses) the objects into a given map from the specified | | * Loads (ands parses) the objects into a given map from the specified |
* file pointer. | | * file pointer. |
| | |
*/ | | */ |
| | |
void load_objects (mapstruct *m, FILE *fp, int mapflags) { | | void load_objects (mapstruct *m, FILE *fp, int mapflags) { |
int i; | | int i,bufstate=LO_NEWFILE; |
object *op, *prev=NULL,*last_more=NULL; | | object *op, *prev=NULL,*last_more=NULL; |
| | |
op=get_object(); | | op=get_object(); |
op->map = m; /* To handle buttons correctly */ | | op->map = m; /* To handle buttons correctly */ |
| | |
while((i=load_object(fp,op,LO_REPEAT, mapflags))) { | | while((i=load_object(fp,op,bufstate, mapflags))) { |
| | /* Since the loading of the map header does not load an object |
| | * anymore, we need to pass LO_NEWFILE for the first object loaded, |
| | * and then switch to LO_REPEAT for faster loading. |
| | */ |
| | bufstate = LO_REPEAT; |
| | |
/* if the archetype for the object is null, means that we | | /* if the archetype for the object is null, means that we |
* got an invalid object. Don't do anythign with it - the game | | * got an invalid object. Don't do anythign with it - the game |
* or editor will not be able to do anything with it either. | | * or editor will not be able to do anything with it either. |
| | |
} | | } |
switch(i) { | | switch(i) { |
case LL_NORMAL: | | case LL_NORMAL: |
insert_ob_in_map_simple(op,m); | | insert_ob_in_map(op,m,op,INS_NO_MERGE | INS_NO_WALK_ON); |
if (op->inv) sum_weight(op); | | if (op->inv) sum_weight(op); |
prev=op,last_more=op; | | prev=op,last_more=op; |
break; | | break; |
| | |
case LL_MORE: | | case LL_MORE: |
insert_ob_in_map_simple(op,m); | | insert_ob_in_map(op,m, op, INS_NO_MERGE | INS_NO_WALK_ON | INS_ABOVE_FLOOR_ONLY); |
op->head=prev,last_more->more=op,last_more=op; | | op->head=prev,last_more->more=op,last_more=op; |
break; | | break; |
} | | } |
| | |
op->map = m; | | op->map = m; |
} | | } |
free_object(op); | | free_object(op); |
| | link_multipart_objects(m); |
} | | } |
| | |
| | /* This saves all the objects on the map in a non destructive fashion. |
| | * Modified by MSW 2001-07-01 to do in a single pass - reduces code, |
| | * and we only save the head of multi part objects - this is needed |
| | * in order to do map tiling properly. |
| | */ |
void save_objects (mapstruct *m, FILE *fp, FILE *fp2) { | | void save_objects (mapstruct *m, FILE *fp, FILE *fp2) { |
int i, j = 0,unique=0; | | int i, j = 0,unique=0; |
object *op, *tmp, *otmp; | | object *op, *otmp; |
/* first pass - save one-part objects */ | | /* first pass - save one-part objects */ |
for(i = 0; i < m->map_object->x; i++) | | for(i = 0; i < MAP_WIDTH(m); i++) |
for (j = 0; j < m->map_object->y; j++) { | | for (j = 0; j < MAP_HEIGHT(m); j++) { |
unique=0; | | unique=0; |
for(op = get_map_ob (m, i, j); op; op = otmp) { | | for(op = get_map_ob (m, i, j); op; op = otmp) { |
otmp = op->above; | | otmp = op->above; |
| | |
continue; | | continue; |
} | | } |
| | |
if (op->head || op->more || op->owner) | | if (op->head || op->owner) |
continue; | | continue; |
| | |
if (unique || QUERY_FLAG(op, FLAG_UNIQUE)) | | if (unique || QUERY_FLAG(op, FLAG_UNIQUE)) |
| | |
else | | else |
save_object(fp, op, 3); | | save_object(fp, op, 3); |
| | |
| | #if 0 |
| | for (tmp = op->more; tmp; tmp = tmp->more) { |
| | if (unique || QUERY_FLAG(op, FLAG_UNIQUE)) { |
| | fprintf ( fp2, "More\n"); |
| | save_object( fp2 , tmp, 3); |
| | } else { |
| | fprintf ( fp, "More\n"); |
| | save_object(fp, tmp, 3); |
| | } |
| | } |
| | #endif |
| | } /* for this space */ |
| | } /* for this j */ |
| | } |
| | |
| | /* |
| | * Allocates, initialises, and returns a pointer to a mapstruct. |
| | * Modified to no longer take a path option which was not being |
| | * used anyways. MSW 2001-07-01 |
| | */ |
| | |
| | mapstruct *get_linked_map() { |
| | mapstruct *map=(mapstruct *) calloc(1,sizeof(mapstruct)); |
| | mapstruct *mp; |
| | |
| | if(map==NULL) |
| | fatal(OUT_OF_MEMORY); |
| | |
| | for(mp=first_map;mp!=NULL&&mp->next!=NULL;mp=mp->next); |
| | if(mp==NULL) |
| | first_map=map; |
| | else |
| | mp->next=map; |
| | |
| | map->in_memory=MAP_SWAPPED; |
| | /* The maps used to pick up default x and y values from the |
| | * map archetype. Mimic that behaviour. |
| | */ |
| | MAP_WIDTH(map)=16; |
| | MAP_HEIGHT(map)=16; |
| | MAP_RESET_TIMEOUT(map)=7200; |
| | MAP_TIMEOUT(map)=300; |
| | /* Gah - these should really have a zero default! */ |
| | MAP_ENTER_X(map)=1; |
| | MAP_ENTER_Y(map)=1; |
| | #if 0 |
| | /* the calloc should already clear all this */ |
| | map->next=NULL; |
| | map->path[0]='\0'; |
| | map->tmpname=NULL; |
| | map->players=0; |
| | map->timeout=0; |
| | map->light = (objectlink *) NULL; |
| | map->darkness = 0; |
| | map->do_los=0; |
| | map->map_object = NULL; |
| | map->buttons = NULL; |
| | map->compressed = 0; |
| | #endif |
| | return map; |
} | | } |
| | |
| | /* |
| | * Allocates the arrays contained in a mapstruct. |
| | * This basically allocates the dynamic array of spaces for the |
| | * map. |
| | */ |
| | |
| | void allocate_map(mapstruct *m) { |
| | #if 0 |
| | /* These are obnoxious - presumably the caller of this function knows what it is |
| | * doing. Instead of checking for load status, lets check instead to see |
| | * if the data has already been allocated. |
| | */ |
| | if(m->in_memory != MAP_SWAPPED ) |
| | return; |
| | #endif |
| | m->in_memory = MAP_IN_MEMORY; |
| | /* Log this condition and free the storage. We could I suppose |
| | * realloc, but if the caller is presuming the data will be intact, |
| | * that is their poor assumption. |
| | */ |
| | if (m->spaces) { |
| | LOG(llevError,"allocate_map callled with already allocated map (%s)\n", m->path); |
| | free(m->spaces); |
} | | } |
/* second pass - save multi-part objects */ | | |
for(i = 0; i < m->map_object->x; i++) | | |
for (j = 0; j < m->map_object->y; j++) { | | |
unique=0; | | |
for(op = get_map_ob (m, i, j); op; op = otmp) { | | |
otmp = op->above; | | |
| | |
if (QUERY_FLAG(op,FLAG_IS_FLOOR) && QUERY_FLAG(op, FLAG_UNIQUE)) | | m->spaces = calloc(1, MAP_WIDTH(m) * MAP_HEIGHT(m) * sizeof(MapSpace)); |
unique=1; | | |
| | |
if(op->type == PLAYER) { | | if(m->spaces==NULL) |
LOG(llevDebug, "Player on map that is being saved\n"); | | fatal(OUT_OF_MEMORY); |
continue; | | |
} | | } |
| | |
if (op->head || !op->more || op->owner) | | /* Creatures and returns a map of the specific size. Used |
continue; | | * in random map code and the editor. |
| | */ |
| | mapstruct *get_empty_map(int sizex, int sizey) { |
| | mapstruct *m = get_linked_map(); |
| | m->width = sizex; |
| | m->height = sizey; |
| | m->in_memory = MAP_SWAPPED; |
| | allocate_map(m); |
| | return m; |
| | } |
| | |
if (unique || QUERY_FLAG(op, FLAG_UNIQUE)) | | /* This loads the header information of the map. The header |
save_object( fp2 , op, 3); | | * contains things like difficulty, size, timeout, etc. |
else | | * this used to be stored in the map object, but with the |
save_object(fp, op, 3); | | * addition of tiling, fields beyond that easily named in an |
| | * object structure were needed, so it just made sense to |
| | * put all the stuff in the map object so that names actually make |
| | * sense. |
| | * This could be done in lex (like the object loader), but I think |
| | * currently, there are few enough fields this is not a big deal. |
| | * MSW 2001-07-01 |
| | * return 0 on success, 1 on failure. |
| | */ |
| | |
for (tmp = op->more; tmp; tmp = tmp->more) { | | static int load_map_header(FILE *fp, mapstruct *m) |
if (unique || QUERY_FLAG(op, FLAG_UNIQUE)) { | | { |
fprintf ( fp2, "More\n"); | | char buf[HUGE_BUF], msgbuf[HUGE_BUF], *key, *value, *end; |
save_object( fp2 , tmp, 3); | | int msgpos=0; |
| | |
| | while (fgets(buf, HUGE_BUF-1, fp)!=NULL) { |
| | buf[HUGE_BUF-1] = 0; |
| | key = buf; |
| | while (isspace(*key)) key++; |
| | if (*key == 0) continue; /* empty line */ |
| | value = strchr(key, ' '); |
| | if (!value) { |
| | end = strchr(key, '\n'); |
| | *end=0; |
} else { | | } else { |
fprintf ( fp, "More\n"); | | *value = 0; |
save_object(fp, tmp, 3); | | value++; |
| | while (isspace(*value)) value++; |
| | end = strchr(value, '\n'); |
| | } |
| | |
| | /* key is the field name, value is what it should be set |
| | * to. We've already done the work to null terminate key, |
| | * and strip off any leading spaces for both of these. |
| | * We have not touched the newline at the end of the line - |
| | * these are needed for some values. the end pointer |
| | * points to the first of the newlines. |
| | * value could be NULL! It would be easy enough to just point |
| | * this to "" to prevent cores, but that would let more errors slide |
| | * through. |
| | */ |
| | |
| | if (!strcmp(key, "arch")) { |
| | /* This is an oddity, but not something we care about much. */ |
| | if (strcmp(value,"map\n")) |
| | LOG(llevError,"loading map and got a non 'arch map' line(%s %s)?\n",key,value); |
| | } |
| | else if (!strcmp(key,"name")) { |
| | *end=0; |
| | m->name = strdup_local(value); |
| | } else if (!strcmp(key,"msg")) { |
| | while (fgets(buf, HUGE_BUF-1, fp)!=NULL) { |
| | if (!strcmp(buf,"endmsg\n")) break; |
| | else { |
| | /* slightly more efficient than strcat */ |
| | strcpy(msgbuf+msgpos, buf); |
| | msgpos += strlen(buf); |
| | } |
| | } |
| | m->msg = strdup_local(msgbuf); |
| | } |
| | /* first strcmp value on these are old names supported |
| | * for compatibility reasons. The new values (second) are |
| | * what really should be used. |
| | */ |
| | else if (!strcmp(key,"hp") || !strcmp(key, "enter_x")) { |
| | m->enter_x = atoi(value); |
| | } else if (!strcmp(key,"sp") || !strcmp(key, "enter_y")) { |
| | m->enter_y = atoi(value); |
| | } else if (!strcmp(key,"x") || !strcmp(key, "width")) { |
| | m->width = atoi(value); |
| | } else if (!strcmp(key,"y") || !strcmp(key, "height")) { |
| | m->height = atoi(value); |
| | } else if (!strcmp(key,"weight") || !strcmp(key, "reset_timeout")) { |
| | m->reset_timeout = atoi(value); |
| | } else if (!strcmp(key,"value") || !strcmp(key, "swap_time")) { |
| | m->timeout = atoi(value); |
| | } else if (!strcmp(key,"level") || !strcmp(key, "difficulty")) { |
| | m->difficulty = atoi(value); |
| | } else if (!strcmp(key,"invisible") || !strcmp(key, "darkness")) { |
| | m->darkness = atoi(value); |
| | } else if (!strcmp(key,"stand_still") || !strcmp(key, "fixed_resettime")) { |
| | m->fixed_resettime = atoi(value); |
| | } else if (!strcmp(key,"unique")) { |
| | m->unique = atoi(value); |
| | } |
| | else if (!strncmp(key,"tile_path_", 10)) { |
| | int tile=atoi(key+10); |
| | |
| | if (tile<1 || tile>4) { |
| | LOG(llevError,"load_map_header: tile location %d out of bounds (%s)\n", |
| | tile, m->path); |
| | } else { |
| | *end = 0; |
| | if (m->tile_path[tile]) { |
| | LOG(llevError,"load_map_header: tile location %d duplicated (%s)\n", |
| | tile, m->path); |
| | free(m->tile_path[tile]); |
| | } |
| | if (!editor) { |
| | if (check_path(value, 1)==-1) { |
| | int i; |
| | /* Need to try and normalize the path. msgbuf is safe to use, |
| | * as that is all read in at one time. |
| | */ |
| | strcpy(msgbuf, m->path); |
| | for (i=0; msgbuf[i]!=0; i++) |
| | if (msgbuf[i] == '/') end = msgbuf + i; |
| | if (!end) { |
| | LOG(llevError,"get_map_header: Can not normalize tile path %s %s\n", |
| | m->path, value); |
| | value = NULL; |
| | } else { |
| | strcpy(end+1, value); |
| | if (check_path(msgbuf,1)==-1) { |
| | LOG(llevError,"get_map_header: Can not normalize tile path %s %s\n", |
| | m->path, value); |
| | value = NULL; |
| | } else |
| | value = msgbuf; |
| | } |
| | } /* if unable to load path as given */ |
| | } /* if not editor */ |
| | if (value) |
| | m->tile_path[tile-1] = strdup_local(value); |
} | | } |
} | | } |
| | else if (!strcmp(key,"end")) break; |
| | else { |
| | LOG(llevError,"Got unknown value in map header: %s %s\n", key, value); |
| | } |
} | | } |
| | if (strcmp(key,"end")) { |
| | LOG(llevError,"Got premature eof on map header!\n"); |
| | return 1; |
} | | } |
| | return 0; |
} | | } |
| | |
/* | | /* |
| | |
* mapstruct. A pointer to this structure is returned, or NULL on failure. | | * mapstruct. A pointer to this structure is returned, or NULL on failure. |
* flags correspond to those in map.h. Main ones used are | | * flags correspond to those in map.h. Main ones used are |
* MAP_PLAYER_UNIQUE, in which case we don't do any name changes, and | | * MAP_PLAYER_UNIQUE, in which case we don't do any name changes, and |
* MAP_BLOCK, in which case we block on this load. | | * MAP_BLOCK, in which case we block on this load. This happens in all |
| | * cases, no matter if this flag is set or not. |
* MAP_STYLE: style map - don't add active objects, don't add to server | | * MAP_STYLE: style map - don't add active objects, don't add to server |
* managed map list. | | * managed map list. |
*/ | | */ |
| | |
mapstruct *load_original_map(char *filename, int flags) { | | mapstruct *load_original_map(char *filename, int flags) { |
FILE *fp; | | FILE *fp; |
mapstruct *m; | | mapstruct *m; |
object *op; | | |
int comp; | | int comp; |
char pathname[MAX_BUF]; | | char pathname[MAX_BUF]; |
| | |
| | |
return (NULL); | | return (NULL); |
} | | } |
| | |
op = get_object(); | | m = get_linked_map(); |
| | |
if (!load_object(fp, op,LO_NEWFILE, flags) || op->type != MAP) { | | strcpy (m->path, filename); |
LOG(llevError,"Error in map (%s) - map object not found\n", filename); | | if (load_map_header(fp, m)) { |
close_and_delete(fp, comp); | | LOG(llevError,"Error loading map header for %s, flags=%d\n", |
return (NULL); | | filename, flags); |
} | | delete_map(m); |
if (op->arch == NULL) { | | return NULL; |
LOG(llevDebug, "Mapobject had no archetype\n"); | | |
op->arch = find_archetype("map"); | | |
if (op->arch == NULL) | | |
LOG(llevError, "Archetype map lacks."); | | |
if (op->arch->clone.type != MAP) | | |
LOG(llevDebug, "Archetype map has wrong type."); | | |
} | | } |
m = get_empty_map (op->x, op->y); | | |
| | |
strcpy (m->path, filename); | | allocate_map(m); |
m->compressed = comp; | | m->compressed = comp; |
| | |
if (m->map_object) | | |
free_object (m->map_object); | | |
m->map_object = op; | | |
op->map = m; | | |
| | |
m->in_memory=MAP_LOADING; | | m->in_memory=MAP_LOADING; |
load_objects (m, fp, flags & (MAP_BLOCK|MAP_STYLE)); | | load_objects (m, fp, flags & (MAP_BLOCK|MAP_STYLE)); |
close_and_delete(fp, comp); | | close_and_delete(fp, comp); |
m->in_memory=MAP_IN_MEMORY; | | m->in_memory=MAP_IN_MEMORY; |
m->difficulty=calculate_difficulty(m); | | if (!MAP_DIFFICULTY(m)) |
m->darkness=m->map_object->invisible; | | MAP_DIFFICULTY(m)=calculate_difficulty(m); |
m->do_los=0; | | |
set_map_reset_time(m); | | set_map_reset_time(m); |
| | |
return (m); | | return (m); |
} | | } |
| | |
| | |
| | |
static mapstruct *load_temporary_map(mapstruct *m) { | | static mapstruct *load_temporary_map(mapstruct *m) { |
FILE *fp; | | FILE *fp; |
object *op; | | |
int comp; | | int comp; |
char buf[MAX_BUF]; | | char buf[MAX_BUF]; |
| | |
| | |
return m; | | return m; |
} | | } |
| | |
op = get_object(); | | |
| | |
load_object(fp,op,LO_NEWFILE,0); | | if (load_map_header(fp, m)) { |
if (op->arch == NULL || op->type != MAP) { | | LOG(llevError,"Error loading map header for %s (%s)\n", |
LOG(llevError,"Error in temporary map '%s'\n", m->path); | | m->path, m->tmpname); |
| | delete_map(m); |
m = load_original_map(m->path,0); | | m = load_original_map(m->path,0); |
return m; | | return NULL; |
} | | } |
| | |
m->compressed = comp; | | m->compressed = comp; |
| | |
| | |
if (m->map_object) | | |
free_object (m->map_object); | | |
m->map_object = op; | | |
| | |
allocate_map(m); | | allocate_map(m); |
clear_map(m); | | |
| | |
m->in_memory=MAP_LOADING; | | m->in_memory=MAP_LOADING; |
load_objects (m, fp, 0); | | load_objects (m, fp, 0); |
| | |
int i,j,unique=0; | | int i,j,unique=0; |
object *op, *next; | | object *op, *next; |
| | |
for(i=0; i<m->map_object->x; i++) | | for(i=0; i<MAP_WIDTH(m); i++) |
for(j=0; j<m->map_object->y; j++) { | | for(j=0; j<MAP_HEIGHT(m); j++) { |
unique=0; | | unique=0; |
for (op=get_map_ob(m, i, j); op; op=next) { | | for (op=get_map_ob(m, i, j); op; op=next) { |
next = op->above; | | next = op->above; |
| | |
int new_save_map(mapstruct *m, int flag) { | | int new_save_map(mapstruct *m, int flag) { |
FILE *fp, *fp2; | | FILE *fp, *fp2; |
char filename[MAX_BUF],buf[MAX_BUF]; | | char filename[MAX_BUF],buf[MAX_BUF]; |
| | int i; |
| | |
if (flag && !*m->path) { | | if (flag && !*m->path) { |
LOG(llevError,"Tried to save map without path.\n"); | | LOG(llevError,"Tried to save map without path.\n"); |
return -1; | | return -1; |
} | | } |
| | |
if (flag || QUERY_FLAG(m->map_object, FLAG_UNIQUE)) { | | if (flag || (m->unique)) { |
if (!QUERY_FLAG(m->map_object, FLAG_UNIQUE)) | | if (!m->unique) |
strcpy (filename, create_pathname (m->path)); | | strcpy (filename, create_pathname (m->path)); |
else | | else /* means flag must be set */ |
strcpy (filename, m->path); | | strcpy (filename, m->path); |
| | |
/* If the compression suffix already exists on the filename, don't | | /* If the compression suffix already exists on the filename, don't |
| | |
LOG(llevDebug,"Saving map %s\n",m->path); | | LOG(llevDebug,"Saving map %s\n",m->path); |
m->in_memory = MAP_SAVING; | | m->in_memory = MAP_SAVING; |
| | |
if (!m->map_object) { | | |
LOG(llevError, "no map object for map %s!\n", m->path); | | |
return -1; | | |
} | | |
| | |
/* Compress if it isn't a temporary save. Do compress if unique */ | | /* Compress if it isn't a temporary save. Do compress if unique */ |
if (m->compressed && (QUERY_FLAG(m->map_object, FLAG_UNIQUE) || flag)) { | | if (m->compressed && (m->unique || flag)) { |
char buf[MAX_BUF]; | | char buf[MAX_BUF]; |
strcpy(buf, uncomp[m->compressed][2]); | | strcpy(buf, uncomp[m->compressed][2]); |
strcat(buf, " > "); | | strcat(buf, " > "); |
| | |
return -1; | | return -1; |
} | | } |
| | |
save_object (fp, m->map_object, 3); | | /* legacy */ |
| | fprintf(fp,"arch map\n"); |
| | if (m->name) fprintf(fp,"name %s\n", m->name); |
| | if (!flag) fprintf(fp,"swap_time %d\n", m->swap_time); |
| | if (m->reset_timeout) fprintf(fp,"reset_timeout %d\n", m->reset_timeout); |
| | if (m->fixed_resettime) fprintf(fp,"fixed_resettime %d\n", m->fixed_resettime); |
| | /* we unfortunately have no idea if this is a value the creator set |
| | * or a difficulty value we generated when the map was first loaded |
| | */ |
| | if (m->difficulty) fprintf(fp,"difficulty %d\n", m->difficulty); |
| | if (m->darkness) fprintf(fp,"darkness %d\n", m->darkness); |
| | if (m->width) fprintf(fp,"width %d\n", m->width); |
| | if (m->height) fprintf(fp,"height %d\n", m->height); |
| | if (m->enter_x) fprintf(fp,"enter_x %d\n", m->enter_x); |
| | if (m->enter_y) fprintf(fp,"enter_y %d\n", m->enter_y); |
| | if (m->msg) fprintf(fp,"msg\n%sendmsg\n", m->msg); |
| | if (m->unique) fprintf(fp,"unique %d\n", m->unique); |
| | /* Save any tiling information */ |
| | for (i=0; i<4; i++) |
| | if (m->tile_path[i]) |
| | fprintf(fp,"tile_path_%d %s\n", i+1, m->tile_path[i]); |
| | |
| | fprintf(fp,"end\n"); |
| | |
/* In the game save unique items in the different file, but | | /* In the game save unique items in the different file, but |
* in the editor save them to the normal map file. | | * in the editor save them to the normal map file. |
| | |
* player) | | * player) |
*/ | | */ |
fp2 = fp; /* save unique items into fp2 */ | | fp2 = fp; /* save unique items into fp2 */ |
if (flag == 0 && !QUERY_FLAG(m->map_object, FLAG_UNIQUE)) { | | if (flag == 0 && !m->unique) { |
sprintf (buf,"%s.v00",create_items_path (m->path)); | | sprintf (buf,"%s.v00",create_items_path (m->path)); |
if ((fp2 = fopen (buf, "w")) == NULL) { | | if ((fp2 = fopen (buf, "w")) == NULL) { |
LOG(llevError, "Can't open unique items file %s\n", buf); | | LOG(llevError, "Can't open unique items file %s\n", buf); |
| | |
return 0; | | return 0; |
} | | } |
| | |
/* | | |
* If any directories in the given path doesn't exist, they are created. | | |
*/ | | |
| | |
void make_path_to_file (char *filename) | | |
{ | | |
char buf[MAX_BUF], *cp = buf; | | |
struct stat statbuf; | | |
| | |
if (!filename || !*filename) | | |
return; | | |
strcpy (buf, filename); | | |
LOG(llevDebug, "make_path_tofile %s...", filename); | | |
while ((cp = strchr (cp + 1, (int) '/'))) { | | |
*cp = '\0'; | | |
#if 0 | | |
LOG(llevDebug, "\n Checking %s...", buf); | | |
#endif | | |
if (stat(buf, &statbuf) || !S_ISDIR (statbuf.st_mode)) { | | |
LOG(llevDebug, "Was not dir..."); | | |
if (mkdir (buf, 0777)) { | | |
perror ("Couldn't make path to file"); | | |
return; | | |
} | | |
#if 0 | | |
LOG(llevDebug, "Made dir."); | | |
} else | | |
LOG(llevDebug, "Was dir"); | | |
#else | | |
} | | |
#endif | | |
*cp = '/'; | | |
} | | |
LOG(llevDebug,"\n"); | | |
} | | |
| | |
| | |
/* | | |
* Clears the arrays containing object-pointers and outlook of a map. | | |
*/ | | |
| | |
void clear_map(mapstruct *m) { | | |
MapLook *aptr, *endptr; | | |
| | |
endptr=m->map+m->map_object->x*m->map_object->y; | | |
for(aptr=m->map; aptr < endptr; ++aptr) | | |
*aptr = blank_look; | | |
endptr=m->floor+m->map_object->x*m->map_object->y; | | |
for(aptr=m->floor; aptr < endptr; ++aptr) | | |
*aptr = blank_look; | | |
endptr=m->floor2+m->map_object->x*m->map_object->y; | | |
for(aptr=m->floor2; aptr < endptr; ++aptr) | | |
*aptr = blank_look; | | |
memset(m->map_ob, 0,sizeof(object *)*m->map_object->x*m->map_object->y); | | |
} | | |
| | |
/* | | |
* This function relinks all _pointers_ to the objects from | | |
* one map to another. | | |
* Note: You can _not_ free the objects in the original map | | |
* after this function has been called. | | |
* (What happened to this function? It no longer copies the pointers! -Frank) | | |
*/ | | |
| | |
void copy_map(mapstruct *m1, mapstruct *m2) { | | |
int x,y; | | |
| | |
strncpy(m2->path,m1->path,BIG_NAME); | | |
x = m2->map_object->x; | | |
y = m2->map_object->y; | | |
copy_object (m1->map_object, m2->map_object); | | |
m2->map_object->x = x; | | |
m2->map_object->y = y; | | |
| | |
for(x=0;x<m1->map_object->x&&x<m2->map_object->x;x++) | | |
for(y=0;y<m1->map_object->y&&y<m2->map_object->y;y++) { | | |
set_map(m2,x,y,get_map(m1,x,y)); | | |
} | | |
} | | |
| | |
/* | | |
* This function, like copy_map(), relinks all _pointers_ from | | |
* one map to the other map. | | |
*/ | | |
| | |
void relink_objs_offset(mapstruct *m1, mapstruct *m2, int dx, int dy) { | | |
int x,y; | | |
object *ob; | | |
| | |
for (x = 0; x < m1->map_object->x && x < m2->map_object->x + dx; x++) | | |
for (y = 0; y < m1->map_object->y && y < m2->map_object->y + dy; y++) { | | |
set_map(m2, x + dx, y + dy, get_map(m1, x, y)); | | |
set_map_ob(m2, x + dx, y + dy, get_map_ob(m1, x, y)); | | |
set_map_ob(m1, x, y, (object *) NULL); | | |
for (ob = get_map_ob(m2, x + dx, y + dy); ob; ob = ob->above) | | |
{ | | |
ob->x = x + dx; | | |
ob->y = y + dy; | | |
ob->map = m2; | | |
} | | |
} | | |
} | | |
| | |
/* | | /* |
* Remove and free all objects in the inventory of the given object. | | * Remove and free all objects in the inventory of the given object. |
| | |
int i,j; | | int i,j; |
object *op; | | object *op; |
| | |
for(i=0;i<m->map_object->x;i++) | | for(i=0;i<MAP_WIDTH(m);i++) |
for(j=0;j<m->map_object->y;j++) { | | for(j=0;j<MAP_HEIGHT(m);j++) { |
object *previous_obj=NULL; | | object *previous_obj=NULL; |
while((op=get_map_ob(m,i,j))!=NULL) { | | while((op=GET_MAP_OB(m,i,j))!=NULL) { |
if (op==previous_obj) | | if (op==previous_obj) { |
{ | | |
LOG(llevDebug, "free_all_objects: Link error, bailing out.\n"); | | LOG(llevDebug, "free_all_objects: Link error, bailing out.\n"); |
break; | | break; |
} | | } |
| | |
} | | } |
| | |
/* | | /* |
* This function moves all objects from one map to another. | | * Frees everything allocated by the given mapstructure. |
* | | * don't free tmpname - our caller is left to do that |
* move_all_objects(): Only used from the editor(s?) | | |
* Yes. -Frank | | |
*/ | | */ |
| | |
void move_all_objects(mapstruct *m1, mapstruct *m2) { | | void free_map(mapstruct *m,int flag) { |
int i,j; | | int i; |
object *op; | | |
| | |
for(i=0;i<m1->map_object->x&&i<m2->map_object->x;i++) | | if (!m->in_memory) { |
for(j=0;j<m1->map_object->y&&j<m2->map_object->y;j++) { | | LOG(llevError,"Trying to free freed map.\n"); |
while((op=get_map_ob(m1,i,j))!=NULL&&op->head==NULL) { | | return; |
remove_ob(op); | | |
op->x=i,op->y=j; /* Not really needed */ | | |
insert_ob_in_map_simple(op,m2); | | |
} | | |
} | | } |
free_all_objects(m1); | | if (flag && m->spaces) free_all_objects(m); |
| | if (m->name) FREE_AND_CLEAR(m->name); |
| | if (m->spaces) FREE_AND_CLEAR(m->spaces); |
| | if (m->msg) FREE_AND_CLEAR(m->msg); |
| | if (m->buttons) |
| | free_objectlinkpt(m->buttons); |
| | m->buttons = NULL; |
| | for (i=0; i<4; i++) |
| | if (m->tile_path[i]) FREE_AND_CLEAR(m->tile_path[i]); |
} | | } |
| | |
/* | | /* |
* function: vanish mapstruct | | * function: vanish mapstruct |
* m : pointer to mapstruct, if NULL no action | | * m : pointer to mapstruct, if NULL no action |
| | * this deletes all the data on the map (freeing pointers) |
| | * and then removes this map from the global linked list of maps. |
*/ | | */ |
| | |
void delete_map(mapstruct *m) { | | void delete_map(mapstruct *m) { |
mapstruct *tmp; | | mapstruct *tmp, *last; |
| | int i; |
| | |
if (!m) | | if (!m) |
return; | | return; |
| | |
free(m->tmpname); | | free(m->tmpname); |
m->tmpname=NULL; | | m->tmpname=NULL; |
} | | } |
if (m == first_map) | | last = NULL; |
first_map = m->next; | | /* We need to look through all the maps and see if any maps |
else { | | * are pointing at this one for tiling information. Since |
for (tmp = first_map; tmp && tmp->next != m; tmp = tmp->next); | | * tiling can be assymetric, we just can not look to see which |
if(tmp) | | * maps this map tiles with and clears those. |
tmp->next = m->next; | | */ |
} | | for (tmp = first_map; tmp != m; tmp = tmp->next) { |
free (m); | | if (tmp->next == m) last = tmp; |
| | |
| | /* This should hopefully get unrolled on a decent compiler */ |
| | for (i=0; i<4; i++) |
| | if (tmp->tile_map[i] == m) tmp->tile_map[i]=NULL; |
} | | } |
| | |
/* | | /* If last is null, then this should be the first map in the list */ |
* Frees everything allocated by the given mapstructure. | | if (!last) { |
| | if (m == first_map) |
| | first_map = m->next; |
| | else |
| | /* m->path is a static char, so should hopefully still have |
| | * some useful data in it. |
*/ | | */ |
| | LOG(llevError,"delete_map: Unable to find map %s in list\n", |
void free_map(mapstruct *m,int flag) { | | m->path); |
if (!m->in_memory) { | | |
LOG(llevError,"Trying to free freed map.\n"); | | |
return; | | |
} | | } |
if(flag) | | |
free_all_objects(m); | | |
m->in_memory=MAP_SWAPPED; | | |
if (m->map != NULL) | | |
CFREE(m->map); | | |
if (m->map_ob != NULL) | | |
CFREE(m->map_ob); | | |
if (m->floor !=NULL) | | |
CFREE(m->floor); | | |
if (m->floor2 !=NULL) | | |
CFREE(m->floor2); | | |
m->map = NULL;/* It's being accessed again, incorrectly. Maybe this will */ | | |
/* make it get loaded back in. */ | | |
m->map_ob = NULL; | | |
m->floor = NULL; | | |
m->floor2 = NULL; | | |
if (m->map_object) | | |
free_object (m->map_object); | | |
m->map_object=NULL; | | |
if (m->buttons) | | |
free_objectlinkpt(m->buttons); | | |
m->buttons = NULL; | | |
} | | |
| | |
/* | | |
* Allocates, initialises, and returns a pointer to a mapstruct. | | |
* takes a path argument it doesn't look like it uses. | | |
*/ | | |
| | |
mapstruct *get_linked_map(char *path) { | | |
mapstruct *map=(mapstruct *) CALLOC(1,sizeof(mapstruct)); | | |
mapstruct *mp; | | |
| | |
if(map==NULL) | | |
fatal(OUT_OF_MEMORY); | | |
for(mp=first_map;mp!=NULL&&mp->next!=NULL;mp=mp->next); | | |
if(mp==NULL) | | |
first_map=map; | | |
else | | else |
mp->next=map; | | last->next = m->next; |
map->next=NULL; | | |
map->path[0]='\0'; | | |
map->tmpname=NULL; | | |
map->encounter = 0; | | |
map->players=0; | | |
map->in_memory=MAP_SWAPPED; | | |
/* map->read_only=0;*/ | | |
map->timeout=0; | | |
map->light = (objectlink *) NULL; | | |
map->darkness = 0; | | |
map->do_los=0; | | |
map->map_object = NULL; | | |
map->buttons = NULL; | | |
map->compressed = 0; | | |
| | |
return map; | | |
} | | |
| | |
/* | | free (m); |
* Allocates the arrays contained in a mapstruct. | | |
*/ | | |
| | |
void allocate_map(mapstruct *m) { | | |
if(m->in_memory != MAP_SWAPPED ) | | |
return; | | |
m->in_memory = MAP_IN_MEMORY; | | |
m->map=(MapLook *) CALLOC(m->map_object->x*m->map_object->y,sizeof(MapLook)); | | |
m->floor=(MapLook *) CALLOC(m->map_object->x*m->map_object->y,sizeof(MapLook)); | | |
m->map_ob=(object **) CALLOC(m->map_object->x*m->map_object->y,sizeof(object *)); | | |
m->floor2=(MapLook *) CALLOC(m->map_object->x*m->map_object->y,sizeof(MapLook)); | | |
if(m->map==NULL||m->map_ob==NULL || m->floor==NULL || m->floor2==NULL) | | |
fatal(OUT_OF_MEMORY); | | |
} | | } |
| | |
/* | | |
* Creates an empty map of the given size, and returns a pointer to it. | | |
*/ | | |
| | |
mapstruct *get_empty_map (int sizex, int sizey) { | | |
mapstruct *m = get_linked_map (NULL); | | |
archetype *tmp; | | |
| | |
for (tmp = first_archetype; tmp; tmp = tmp->next) | | |
if (tmp->clone.type == MAP) | | |
break; | | |
if (tmp) { | | |
m->map_object = ObjectCreateArch (tmp); | | |
m->map_object->x = sizex; | | |
m->map_object->y = sizey; | | |
allocate_map (m); | | |
clear_map (m); | | |
} else { | | |
m->map_object = NULL; | | |
LOG (llevError, "no map archetypes\n"); | | |
} | | |
return m; | | |
} | | |
| | |
/* | | /* |
* Makes sure the given map is loaded and swapped in. | | * Makes sure the given map is loaded and swapped in. |
| | |
/* Have we been at this level before? */ | | /* Have we been at this level before? */ |
m = has_been_loaded (name); | | m = has_been_loaded (name); |
| | |
| | /* Map is good to go, so just return it */ |
if (m && (m->in_memory == MAP_LOADING || m->in_memory == MAP_IN_MEMORY)) | | if (m && (m->in_memory == MAP_LOADING || m->in_memory == MAP_IN_MEMORY)) |
return m; | | return m; |
| | |
if ((flags & (MAP_FLUSH|MAP_PLAYER_UNIQUE)) || !m || (m->reset_time != -1 && seconds() > m->reset_time)) { | | /* unique maps always get loaded from their original location, and never |
| | * a temp location. Likewise, if map_flush is set, or we have never loaded |
| | * this map, load it now. I removed the reset checking from here - |
| | * it seems the probability of a player trying to enter a map that should |
| | * reset but hasn't yet is quite low, and removing that makes this function |
| | * a bit cleaner (and players probably shouldn't rely on exact timing for |
| | * resets in any case - if they really care, they should use the 'maps command. |
| | */ |
| | if ((flags & (MAP_FLUSH|MAP_PLAYER_UNIQUE)) || !m) { |
| | |
/* first visit or time to reset */ | | /* first visit or time to reset */ |
if (m) { | | if (m) { |
| | |
} | | } |
| | |
| | |
| | |
void no_maps_file(char *filename) { | | |
LOG(llevError,"Can't open the %s file.\n",filename); | | |
LOG(llevError,"If you have no maps, you must either make them yourself\n"); | | |
LOG(llevError,"with the mapeditor, or fetch them from ftp.ifi.uio.no\n"); | | |
LOG(llevError,"in the /pub/crossfire directory. Be sure not to fetch\n"); | | |
LOG(llevError,"maps belonging to a later version than this version.\n"); | | |
exit(-1); | | |
} | | |
| | |
| | |
void set_map_reset_time(mapstruct *map) { | | |
#ifdef MAP_RESET | | |
#ifdef MAP_MAXRESET | | |
if (MAP_RESETTIME(map)>MAP_MAXRESET) | | |
map->reset_time = seconds() + MAP_MAXRESET; | | |
else | | |
#endif /* MAP_MAXRESET */ | | |
map->reset_time = seconds() + MAP_RESETTIME (map); | | |
#else | | |
map->reset_time = (-1); /* Will never be reset */ | | |
#endif | | |
} | | |
| | |
/* | | /* |
* This routine is supposed to find out which level the players should have | | * This routine is supposed to find out the difficulty of the map. |
* before visiting this map. It is used to calculate which bonuses to put | | * difficulty does not have a lot to do with character level, |
* on magic items. | | * but does have a lot to do with treasure on the map. |
* | | * |
* Difficulty can now be set by the map creature. If the value stored | | * Difficulty can now be set by the map creature. If the value stored |
* in the map is zero, then use this routine. | | * in the map is zero, then use this routine. Maps should really |
| | * have a difficulty set than using this function - human calculation |
| | * is much better than this functions guesswork. |
*/ | | */ |
| | |
int calculate_difficulty(mapstruct *m) { | | int calculate_difficulty(mapstruct *m) { |
| | |
LOG(llevDebug, "Using stored map difficulty: %d\n", MAP_DIFFICULTY(m)); | | LOG(llevDebug, "Using stored map difficulty: %d\n", MAP_DIFFICULTY(m)); |
return MAP_DIFFICULTY(m); | | return MAP_DIFFICULTY(m); |
} | | } |
for(x=0;x<m->map_object->x;x++) | | |
for(y=0;y<m->map_object->y;y++) | | for(x=0;x<MAP_WIDTH(m);x++) |
| | for(y=0;y<MAP_HEIGHT(m);y++) |
for(op=get_map_ob(m,x,y);op!=NULL;op=op->above) { | | for(op=get_map_ob(m,x,y);op!=NULL;op=op->above) { |
if(QUERY_FLAG(op,FLAG_MONSTER)) | | if(QUERY_FLAG(op,FLAG_MONSTER)) |
total_exp+=op->stats.exp; | | total_exp+=op->stats.exp; |
| | |
break; | | break; |
} | | } |
#else | | #else |
exp_pr_sq=((double)1000*total_exp)/(m->map_object->x*m->map_object->y+1); | | exp_pr_sq=((double)1000*total_exp)/(MAP_WIDTH(m)*MAP_HEIGHT(m)+1); |
diff=20; | | diff=20; |
for(i=1;i<20;i++) | | for(i=1;i<20;i++) |
if(exp_pr_sq<=level_exp(i,1.0)) { | | if(exp_pr_sq<=level_exp(i,1.0)) { |
| | |
(void) unlink(m->tmpname); | | (void) unlink(m->tmpname); |
} | | } |
| | |
/* | | void free_all_maps() |
* member: copy by translate objects from source to a new map | | |
* source: -map | | |
* width : width of target map | | |
* height: height of target map | | |
* dx : positive translate to right | | |
* dy : positive translate to down | | |
*/ | | |
mapstruct *MapMoveScrollResize(mapstruct *source, | | |
int width, int height, int dx, int dy) | | |
{ | | { |
mapstruct *target; | | int real_maps=0; |
object *obj,*prt; /* PaRT of obj */ | | |
int x,y,sx = source->map_object->x, sy = source->map_object->y; | | while (first_map) { |
int linked = 0, link=0; | | /* I think some of the callers above before it gets here set this to be |
| | * saving, but we still want to free this data |
if (!width) width = sx; | | */ |
if (!height) height = sy; | | if (first_map->in_memory == MAP_SAVING) first_map->in_memory = MAP_IN_MEMORY; |
target = get_empty_map (width, height); | | delete_map(first_map); |
| | real_maps++; |
strncpy (target->path, source->path, BIG_NAME); | | } |
| | LOG(llevDebug,"free_all_maps: Freed %d maps\n", real_maps); |
copy_object (source->map_object, target->map_object); | | |
target->map_object->x = width; | | |
target->map_object->y = height; | | |
| | |
if(dx < 0) dx += target->map_object->x; | | |
if(dy < 0) dy += target->map_object->y; | | |
| | |
for(y=0; y < sy && y < target->map_object->y; y++) | | |
for(x=0; x < sx && x < target->map_object->x; x++) | | |
while((obj = get_map_ob(source,x,y)) && !obj->head) { | | |
if ((linked = QUERY_FLAG (obj,FLAG_IS_LINKED))) { | | |
link = get_button_value (obj); | | |
remove_button_link (obj); | | |
} | | |
remove_ob(obj); | | |
for(prt = obj; prt; prt = prt->more) { | | |
prt->x += dx; | | |
prt->x %= target->map_object->x; /* it can be split by edge */ | | |
prt->y += dy; /* designers problem to fix */ | | |
prt->y %= target->map_object->y; | | |
} | | |
insert_ob_in_map_simple(obj,target); | | |
if (linked) | | |
add_button_link(obj, target, link); | | |
} | | |
/*free_all_objects(source);*/ | | |
free_map (source, 1); | | |
delete_map (source); | | |
return target; | | |
} | | } |
| | |
/* | | /* change_map_light() - used to change map light level (darkness) |
* member: copy by translate objects from source to target | | * up or down by *1*. This fctn is not designed to change by |
* target: -map | | * more than that! Returns true if successful. -b.t. |
* source: -map | | * Move this from los.c to map.c since this is more related |
* dx : positive translate to right | | * to maps than los. |
* dy : positive translate to down | | |
*/ | | */ |
void MapMoveScroll(mapstruct *target, mapstruct *source, int dx, int dy) | | |
{ | | |
object *obj,*prt; /* PaRT of obj */ | | |
int x,y; | | |
| | |
if(dx < 0) dx += target->map_object->x; | | int change_map_light(mapstruct *m, int change) { |
if(dy < 0) dy += target->map_object->y; | | int new_level = m->darkness + change; |
| | |
for(y=0; y < source->map_object->y && y < target->map_object->y; y++) | | if(new_level<=0) { |
for(x=0; x < source->map_object->x && x < target->map_object->x; x++) | | m->darkness = 0; |
while((obj = get_map_ob(source,x,y)) && !obj->head) { | | return 0; |
remove_ob(obj); | | |
for(prt = obj; prt; prt = prt->more) { | | |
prt->x += dx; | | |
prt->x %= target->map_object->x; /* it can be split by edge */ | | |
prt->y += dy; /* designers problem to fix */ | | |
prt->y %= target->map_object->y; | | |
} | | } |
insert_ob_in_map_simple(obj,target); | | |
| | if(new_level>MAX_DARKNESS) return 0; |
| | |
| | if(change) { |
| | /* inform all players on the map */ |
| | if (change>0) |
| | (info_map_func)(NDI_BLACK, m,"It becomes darker."); |
| | else |
| | (info_map_func)(NDI_BLACK, m,"It becomes brighter."); |
| | |
| | m->darkness=new_level; |
| | /* All clients need to get re-updated for the change */ |
| | update_all_map_los(m); |
| | return 1; |
} | | } |
free_all_objects(source); | | return 0; |
} | | } |
| | |
object * MapGetRealObject (mapstruct * emap, int x, int y, int z) | | |
{ | | /* |
object *tmp = MapGetObjectZ (emap, x, y, z); | | * This function updates various attributes about a specific space |
return tmp ? (tmp->head ? tmp->head : tmp) : tmp; | | * on the map (what it looks like, whether it blocks magic, |
| | * has a living creatures, prevents people from passing |
| | * through, etc) |
| | */ |
| | void update_position (mapstruct *m, int x, int y) { |
| | object *tmp, *last = NULL; |
| | uint8 flags = 0, oldflags, light=0, anywhere=0; |
| | New_Face *top,*floor, *middle; |
| | |
| | oldflags = GET_MAP_FLAGS(m,x,y); |
| | if (!(oldflags & P_NEED_UPDATE)) { |
| | LOG(llevDebug,"update_position called with P_NEED_UPDATE not set: %s (%d, %d)\n", |
| | m->path, x, y); |
| | return; |
} | | } |
| | |
int MapInsertObjectZ(mapstruct *emap,object *o,int x, int y, int z) | | middle=blank_face; |
{ | | top=blank_face; |
object *op, *above, *below; | | floor=blank_face; |
| | |
| | for (tmp = get_map_ob (m, x, y); tmp; last = tmp, tmp = tmp->above) { |
| | |
| | /* This could be made additive I guess (two lights better than |
| | * one). But if so, it shouldn't be a simple additive - 2 |
| | * light bulbs do not illuminate twice as far as once since |
| | * it is a disapation factor that is squared (or is it cubed?) |
| | */ |
| | if (tmp->glow_radius > light) light = tmp->glow_radius; |
| | |
| | /* This call is needed in order to update objects the player |
| | * is standign in that have animations (ie, grass, fire, etc). |
| | * However, it also causes the look window to be re-drawn |
| | * 3 times each time the player moves, because many of the |
| | * functions the move_player calls eventualy call this. |
| | * |
| | * Always put the player down for drawing. |
| | */ |
| | if ((tmp->type==PLAYER || QUERY_FLAG(tmp, FLAG_MONSTER)) && !tmp->invisible) { |
| | top = tmp->face; |
| | } |
| | else if (QUERY_FLAG(tmp,FLAG_IS_FLOOR)) { |
| | /* If we got a floor, that means middle and top were below it, |
| | * so should not be visible, so we clear them. |
| | */ |
| | middle=blank_face; |
| | top=blank_face; |
| | if (!tmp->invisible) |
| | floor = tmp->face; |
| | } |
| | /* Flag anywhere have high priority */ |
| | else if (QUERY_FLAG(tmp, FLAG_SEE_ANYWHERE)) { |
| | middle = tmp->face; |
| | anywhere =1; |
| | } |
| | /* Find the highest visible face around */ |
| | else if (tmp->face->visibility > middle->visibility && !anywhere) |
| | middle = tmp->face; |
| | |
if (o->more) | | if (tmp==tmp->above) { |
MapInsertObjectZ (emap,o->more, x, y, z); | | LOG(llevError, "Error in structure of map\n"); |
| | exit (-1); |
| | } |
| | |
o->x += x; | | if (QUERY_FLAG(tmp,FLAG_ALIVE)) |
o->y += y; | | flags |= P_IS_ALIVE; |
o->map = emap; | | if (QUERY_FLAG(tmp,FLAG_NO_PASS)) |
CLEAR_FLAG(o,FLAG_REMOVED); | | flags |= P_NO_PASS; |
| | if (QUERY_FLAG(tmp,FLAG_NO_MAGIC)) |
op = get_map_ob (emap, o->x, o->y); | | flags |= P_NO_MAGIC; |
if (z < 0) { | | if (QUERY_FLAG(tmp,FLAG_DAMNED)) |
above = op; | | flags |= P_NO_CLERIC; |
below = NULL; | | #if 0 |
} else { | | /* P_PASS_THRU doesn't seem to do anything so commented out for now */ |
while (op && op->above) | | if (QUERY_FLAG(tmp,FLAG_PASS_THRU)) |
op = op->above; | | flags |= P_PASS_THRU; |
| | #endif |
| | if (QUERY_FLAG(tmp,FLAG_BLOCKSVIEW)) |
| | flags |= P_BLOCKSVIEW; |
| | } /* for stack of objects */ |
| | |
| | /* we don't want to rely on this function to have accurate flags, but |
| | * since we're already doing the work, we calculate them here. |
| | * if they don't match, logic is broken someplace. |
| | */ |
| | if (((oldflags & ~(P_NEED_UPDATE|P_NO_ERROR)) != flags) && |
| | (!(oldflags & P_NO_ERROR))) { |
| | LOG(llevDebug,"update_position: updated flags do not match old flags: %s (old=%d,new=%d) %x != %x\n", |
| | m->path, x, y, |
| | (oldflags & ~P_NEED_UPDATE), flags); |
| | } |
| | SET_MAP_FLAGS(m, x, y, flags); |
| | |
| | /* At this point, we have a floor face (if there is a floor), |
| | * and the floor is set - we are not going to touch it at |
| | * this point. |
| | * middle contains the highest visibility face. |
| | * top contains a player/monster face, if there is one. |
| | * |
| | * We now need to fill in top.face and/or middle.face. |
| | */ |
| | |
| | /* If the top face also happens to be high visibility, re-do our |
| | * middle face. This should not happen, as we already have the |
| | * else statement above so middle should not get set. OTOH, it |
| | * may be possible for the faces to match but be different objects. |
| | */ |
| | if (top == middle) middle=blank_face; |
| | |
| | /* There are three posibilities at this point: |
| | * 1) top face is set, need middle to be set. |
| | * 2) middle is set, need to set top. |
| | * 3) neither middle or top is set - need to set both. |
| | */ |
| | |
| | for (tmp=last; tmp; tmp=tmp->below) { |
| | /* Once we get to a floor, stop, since we already have a floor object */ |
| | if (QUERY_FLAG(tmp,FLAG_IS_FLOOR)) break; |
| | |
| | /* If two top faces are already set, quit processing */ |
| | if ((top != blank_face) && (middle != blank_face)) break; |
| | |
above = NULL; | | /* Only show visible faces, unless its the editor - show all */ |
below = op; | | if (!tmp->invisible || editor) { |
while (op && z-- > 0) { | | /* Fill in top if needed */ |
above = op; | | if (top == blank_face) { |
below = op = op->below; | | top = tmp->face; |
| | } else { |
| | /* top is already set - we should only get here if |
| | * middle is not set |
| | * |
| | * Set the middle face and break out, since there is nothing |
| | * more to fill in. We don't check visiblity here, since |
| | * |
| | */ |
| | if (tmp->face != top ) { |
| | middle = tmp->face; |
| | break; |
} | | } |
} | | } |
o->below = below; | | |
o->above = above; | | |
| | |
if (above) | | |
above->below = o; | | |
else { | | |
MapLook f; | | |
f.flags = 0; | | |
f.face = o->face; | | |
set_map (emap, o->x, o->y, &f); | | |
} | | } |
if (below) | | } |
below->above = o; | | if (middle == floor) middle = blank_face; |
else | | if (top == middle) middle = blank_face; |
set_map_ob (emap, o->x, o->y, o); | | SET_MAP_FACE(m,x,y,top,0); |
| | SET_MAP_FACE(m,x,y,middle,1); |
return (0); | | SET_MAP_FACE(m,x,y,floor,2); |
| | SET_MAP_LIGHT(m,x,y,light); |
} | | } |
| | |
int MapObjectOut (mapstruct *target, object *obj, int x, int y) { | | |
object *tmp; | | void set_map_reset_time(mapstruct *map) { |
for(tmp = obj; tmp; tmp = tmp->more) | | #ifdef MAP_RESET |
if(out_of_map(target,x + tmp->x,y + tmp->y)) return 1; | | #ifdef MAP_MAXRESET |
return 0; | | if (MAP_RESET_TIMEOUT(map)>MAP_MAXRESET) |
| | MAP_WHEN_RESET(map) = seconds() + MAP_MAXRESET; |
| | else |
| | #endif /* MAP_MAXRESET */ |
| | MAP_WHEN_RESET(map) = seconds() + MAP_RESET_TIMEOUT (map); |
| | #else |
| | MAP_WHEN_RESET(map) = (-1); /* Will never be reset */ |
| | #endif |
} | | } |
| | |
object * MapGetObjectZ (mapstruct * emap, int x, int y, int z) | | /* this returns TRUE if the coordinates (x,y) are out of |
| | * map m. This function also takes into account any |
| | * tiling considerations, loading adjacant maps as needed. |
| | * This is the function should always be used when it |
| | * necessary to check for valid coordinates. |
| | * This function will recursively call itself for the |
| | * tiled maps. |
| | * |
| | * |
| | */ |
| | int out_of_map(mapstruct *m, int x, int y) |
{ | | { |
object *op; | | |
| | |
if (!emap || out_of_map (emap, x, y)) | | /* If we get passed a null map, this is obviously the |
return (NULL); | | * case. This generally shouldn't happen, but if the |
op = get_map_ob (emap, x, y); | | * map loads fail below, it could happen. |
while (op && op->above) | | */ |
op = op->above; | | if (!m) return 0; |
while (op && z-- > 0) | | |
op = op->below; | | /* Simple case - coordinates are within this local |
return (op); | | * map. |
| | */ |
| | if ( x>=0 && x<MAP_WIDTH(m) && y>=0 && y < MAP_HEIGHT(m)) |
| | return 0; |
| | |
| | if (x<0) { |
| | if (!m->tile_path[3]) return 1; |
| | if (!m->tile_map[3] || m->tile_map[3]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[3] = ready_map_name(m->tile_path[3], 0); |
| | return (out_of_map(m->tile_map[3], x + MAP_WIDTH(m->tile_map[3]), y)); |
| | } |
| | if (x>=MAP_WIDTH(m)) { |
| | if (!m->tile_path[1]) return 1; |
| | if (!m->tile_map[1] || m->tile_map[1]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[1] = ready_map_name(m->tile_path[1], 0); |
| | return (out_of_map(m->tile_map[1], x - MAP_WIDTH(m), y)); |
| | } |
| | if (y<0) { |
| | if (!m->tile_path[0]) return 1; |
| | if (!m->tile_map[0] || m->tile_map[0]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[0] = ready_map_name(m->tile_path[0], 0); |
| | return (out_of_map(m->tile_map[0], x, y + MAP_HEIGHT(m->tile_map[0]))); |
| | } |
| | if (y>=MAP_HEIGHT(m)) { |
| | if (!m->tile_path[2]) return 1; |
| | if (!m->tile_map[2] || m->tile_map[2]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[2] = ready_map_name(m->tile_path[2], 0); |
| | return (out_of_map(m->tile_map[2], x, y - MAP_HEIGHT(m))); |
| | } |
| | return 1; |
} | | } |
| | |
void free_all_maps() | | /* This is basically the same as out_of_map above, but |
| | * instead we return NULL if no map is valid (coordinates |
| | * out of bounds and no tiled map), otherwise it returns |
| | * the map as that the coordinates are really on, and |
| | * updates x and y to be the localized coordinates. |
| | * Using this is more efficient of calling out_of_map |
| | * and then figuring out what the real map is |
| | */ |
| | mapstruct *get_map_from_coord(mapstruct *m, int *x, int *y) |
{ | | { |
int real_maps=0; | | |
| | |
while (first_map) { | | /* Simple case - coordinates are within this local |
/* I think some of the callers above before it gets here set this to be | | * map. |
* saving, but we still want to free this data | | |
*/ | | */ |
if (first_map->in_memory == MAP_SAVING) first_map->in_memory = MAP_IN_MEMORY; | | if (*x>=0 && *x<MAP_WIDTH(m) && *y>=0 && *y < MAP_HEIGHT(m)) |
delete_map(first_map); | | return m; |
real_maps++; | | |
| | if (*x<0) { |
| | if (!m->tile_path[3]) return NULL; |
| | if (!m->tile_map[3] || m->tile_map[3]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[3] = ready_map_name(m->tile_path[3], 0); |
| | *x += MAP_WIDTH(m->tile_map[3]); |
| | return (get_map_from_coord(m->tile_map[3], x, y)); |
| | } |
| | if (*x>=MAP_WIDTH(m)) { |
| | if (!m->tile_path[1]) return NULL; |
| | if (!m->tile_map[1] || m->tile_map[1]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[1] = ready_map_name(m->tile_path[1], 0); |
| | *x -= MAP_WIDTH(m); |
| | return (get_map_from_coord(m->tile_map[1], x, y)); |
| | } |
| | if (*y<0) { |
| | if (!m->tile_path[0]) return NULL; |
| | if (!m->tile_map[0] || m->tile_map[0]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[0] = ready_map_name(m->tile_path[0], 0); |
| | *y += MAP_HEIGHT(m->tile_map[0]); |
| | return (get_map_from_coord(m->tile_map[0], x, y)); |
| | } |
| | if (*y>=MAP_HEIGHT(m)) { |
| | if (!m->tile_path[2]) return NULL; |
| | if (!m->tile_map[2] || m->tile_map[2]->in_memory != MAP_IN_MEMORY) |
| | m->tile_map[2] = ready_map_name(m->tile_path[2], 0); |
| | *y -= MAP_HEIGHT(m); |
| | return (get_map_from_coord(m->tile_map[2], x, y)); |
} | | } |
LOG(llevDebug,"free_all_maps: Freed %d maps\n", real_maps); | | return NULL; /* Shouldn't get here */ |
} | | } |