From 86f70d9f4ff1715dccbc101b78728c366623fe7b Mon Sep 17 00:00:00 2001 From: Piotr HaƂaczkiewicz Date: Wed, 17 Jul 2013 21:13:21 +0200 Subject: Refactored map_foreach* functions. Removed a lot of duplicated code. Added some documentation & comments. --- src/map/atcommand.c | 4 +- src/map/map.c | 1120 +++++++++++++++++++++++---------------------------- src/map/map.h | 15 +- 3 files changed, 520 insertions(+), 619 deletions(-) diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 29bd43d16..a0a6623cf 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -6152,10 +6152,10 @@ ACMD(cleanarea) int x0 = 0, y0 = 0, x1 = 0, y1 = 0; if (!message || !*message || sscanf(message, "%d %d %d %d", &x0, &y0, &x1, &y1) < 1) { - iMap->foreachinarea(atcommand_cleanfloor_sub, sd->bl.m, sd->bl.x - (AREA_SIZE * 2), sd->bl.y - (AREA_SIZE * 2), sd->bl.x + (AREA_SIZE * 2), sd->bl.y + (AREA_SIZE * 2), BL_ITEM); + iMap->foreachinrange(atcommand_cleanfloor_sub, &sd->bl, AREA_SIZE * 2, BL_ITEM); } else if (sscanf(message, "%d %d %d %d", &x0, &y0, &x1, &y1) == 1) { - iMap->foreachinarea(atcommand_cleanfloor_sub, sd->bl.m, sd->bl.x - x0, sd->bl.y - x0, sd->bl.x + x0, sd->bl.y + x0, BL_ITEM); + iMap->foreachinrange(atcommand_cleanfloor_sub, &sd->bl, x0, BL_ITEM); } else if (sscanf(message, "%d %d %d %d", &x0, &y0, &x1, &y1) == 4) { iMap->foreachinarea(atcommand_cleanfloor_sub, sd->bl.m, x0, y0, x1, y1, BL_ITEM); diff --git a/src/map/map.c b/src/map/map.c index db3bcf001..fe0b93609 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -80,7 +80,7 @@ char log_db_pw[32] = "ragnarok"; char log_db_db[32] = "log"; Sql* logmysql_handle; -// DBMap declaartion +// DBMap declaration static DBMap* id_db=NULL; // int id -> struct block_list* static DBMap* pc_db=NULL; // int id -> struct map_session_data* static DBMap* mobid_db=NULL; // int id -> struct mob_data* @@ -447,6 +447,7 @@ int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick) /*========================================== * Counts specified number of objects on given cell. +* TODO: merge with bl_getall_area *------------------------------------------*/ int map_count_oncell(int16 m, int16 x, int16 y, int type) { @@ -502,546 +503,607 @@ struct skill_unit* map_find_skill_unit_oncell(struct block_list* target,int16 x, return NULL; } -/*========================================== -* Adapted from foreachinarea for an easier invocation. [Skotlex] -*------------------------------------------*/ -int map_foreachinrange(int (*func)(struct block_list*,va_list), struct block_list* center, int16 range, int type, ...) +/** @name Functions for block_list search and manipulation + */ + +/* @{ */ +/** + * Applies func to every block_list in bl_list starting with bl_list[blockcount]. + * Sets bl_list_count back to blockcount. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param blockcount Index of first relevant entry in bl_list + * @param max Maximum sum of values returned by func (usually max number of func calls) + * @param args Extra arguments for func + * @return Sum of the values returned by func + */ +static int bl_vforeach(int (*func)(struct block_list*, va_list), int blockcount, int max, va_list args) { - int bx, by, m; - int returnCount = 0; //total sum of returned values of func() [Skotlex] - struct block_list *bl; - int blockcount = bl_list_count, i; - int x0, x1, y0, y1; - va_list ap; + int i; + int returnCount = 0; - m = center->m; - x0 = max(center->x - range, 0); - y0 = max(center->y - range, 0); - x1 = min(center->x + range, map[ m ].xs - 1); - y1 = min(center->y + range, map[ m ].ys - 1); - - if ( type&~BL_MOB ) - for ( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) { - for( bl = map[m].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->type&type - && bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 -#ifdef CIRCULAR_AREA - && check_distance_bl(center, bl, range) -#endif - && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - } - } + iMap->freeblock_lock(); + for (i = blockcount; i < bl_list_count && returnCount < max; i++) { + if (bl_list[i]->prev) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. + va_list argscopy; + va_copy(argscopy, args); + returnCount += func(bl_list[i], argscopy); + va_end(argscopy); } + } + iMap->freeblock_unlock(); - if( type&BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++) { - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 -#ifdef CIRCULAR_AREA - && check_distance_bl(center, bl, range) -#endif - && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - } - } - } - - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("iMap->foreachinrange: block count too many!\n"); - - iMap->freeblock_lock(); - - for( i = blockcount; i < bl_list_count; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } - - iMap->freeblock_unlock(); + bl_list_count = blockcount; - bl_list_count = blockcount; - return returnCount; //[Skotlex] + return returnCount; } -/*========================================== -* Same as foreachinrange, but there must be a shoot-able range between center and target to be counted in. [Skotlex] -*------------------------------------------*/ -int map_foreachinshootrange(int (*func)(struct block_list*,va_list),struct block_list* center, int16 range, int type,...) +/** + * Applies func to every block_list object of bl_type type on map m. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param m Map id + * @param type enum bl_type + * @param args Extra arguments for func + * @return Sum of the values returned by func + */ +static int map_vforeachinmap(int (*func)(struct block_list*, va_list), int16 m, int type, va_list args) { - int bx, by, m; - int returnCount = 0; //total sum of returned values of func() [Skotlex] + int i; + int returnCount = 0; + int bsize; + va_list argscopy; struct block_list *bl; - int blockcount = bl_list_count, i; - int x0, x1, y0, y1; - va_list ap; + int blockcount = bl_list_count; - m = center->m; - if ( m < 0 ) + if (m < 0) return 0; - x0 = max(center->x-range, 0); - y0 = max(center->y-range, 0); - x1 = min(center->x+range, map[m].xs-1); - y1 = min(center->y+range, map[m].ys-1); - - if ( type&~BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) { - for( bl = map[ m ].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->type&type - && bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 -#ifdef CIRCULAR_AREA - && check_distance_bl(center, bl, range) -#endif - && path_search_long(NULL, center->m, center->x, center->y, bl->x, bl->y, CELL_CHKWALL) - && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; + bsize = map[m].bxs * map[m].bys; + for (i = 0; i < bsize; i++) { + if (type&~BL_MOB) { + for (bl = map[m].block[i]; bl != NULL; bl = bl->next) { + if (bl->type&type && bl_list_count < BL_LIST_MAX) { + bl_list[bl_list_count++] = bl; } } } - if( type&BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for( bx=x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) { - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 -#ifdef CIRCULAR_AREA - && check_distance_bl(center, bl, range) -#endif - && path_search_long(NULL, center->m, center->x, center->y, bl->x, bl->y, CELL_CHKWALL) - && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - } + if (type&BL_MOB) { + for (bl = map[m].block_mob[i]; bl != NULL; bl = bl->next) { + if (bl_list_count < BL_LIST_MAX) { + bl_list[bl_list_count++] = bl; } } + } + } - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("iMap->foreachinrange: block count too many!\n"); + if (bl_list_count >= BL_LIST_MAX) + ShowError("map.c:map_vforeachinmap: bl_list size (%d) exceeded\n", BL_LIST_MAX); - iMap->freeblock_lock(); + va_copy(argscopy, args); + returnCount = bl_vforeach(func, blockcount, INT_MAX, argscopy); + va_end(argscopy); - for( i = blockcount; i < bl_list_count; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } + return returnCount; +} + +/** + * Applies func to every block_list object of bl_type type on map m. + * Returns the sum of values returned by func. + * @see map_vforeachinmap + * @param func Function to be applied + * @param m Map id + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachinmap(int (*func)(struct block_list*, va_list), int16 m, int type, ...) +{ + int returnCount = 0; + va_list ap; - iMap->freeblock_unlock(); + va_start(ap, type); + returnCount = map_vforeachinmap(func, m, type, ap); + va_end(ap); - bl_list_count = blockcount; - return returnCount; //[Skotlex] + return returnCount; } -/*========================================== -* range = map m (x0,y0)-(x1,y1) -* Apply *func with ... arguments for the range. -* @type = BL_PC/BL_MOB etc.. -*------------------------------------------*/ -int map_foreachinarea(int (*func)(struct block_list*,va_list), int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int type, ...) +/** + * Applies func to every block_list object of bl_type type on all maps + * of instance instance_id. + * Returns the sum of values returned by func. + * @see map_vforeachinmap. + * @param func Function to be applied + * @param m Map id + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachininstance(int (*func)(struct block_list*, va_list), int16 instance_id, int type, ...) +{ + int i; + int returnCount = 0; + + for (i = 0; i < instances[instance_id].num_map; i++) { + int m = instances[instance_id].map[i]; + va_list ap; + va_start(ap, type); + returnCount += map_vforeachinmap(func, m, type, ap); + va_end(ap); + } + + return returnCount; +} + +/** + * Retrieves all map objects in area that are matched by the type + * and func. Appends them at the end of global bl_list array. + * @param type Matching enum bl_type + * @param m Map + * @param func Matching function + * @param ... Extra arguments for func + * @return Number of found objects + */ +static int bl_getall_area(int type, int m, int x0, int y0, int x1, int y1, int (*func)(struct block_list*, va_list), ...) { + va_list args; int bx, by; - int returnCount = 0; //total sum of returned values of func() [Skotlex] struct block_list *bl; - int blockcount = bl_list_count, i; - va_list ap; + int found = 0; - if ( m < 0 ) + if (m < 0) return 0; - if ( x1 < x0 ) - swap(x0, x1); - if ( y1 < y0 ) - swap(y0, y1); + if (x1 < x0) swap(x0, x1); + if (y1 < y0) swap(y0, y1); + // Limit search area to map size x0 = max(x0, 0); y0 = max(y0, 0); - x1 = min(x1, map[ m ].xs - 1); - y1 = min(y1, map[ m ].ys - 1); - if ( type&~BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) - for( bl = map[ m ].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) - if( bl->type&type && bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( type&BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) - if( bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachinarea: block count too many!\n"); + x1 = min(x1, map[m].xs - 1); + y1 = min(y1, map[m].ys - 1); + + for (by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) { + for (bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++) { + if (type&~BL_MOB) { + for (bl = map[m].block[bx + by * map[m].bxs]; bl != NULL; bl = bl->next) { + if (bl_list_count < BL_LIST_MAX + && bl->type&type + && bl->x >= x0 && bl->x <= x1 + && bl->y >= y0 && bl->y <= y1) { + if (func) { + va_start(args, func); + if (func(bl, args)) { + bl_list[bl_list_count++] = bl; + found++; + } + va_end(args); + } + else { + bl_list[bl_list_count++] = bl; + found++; + } + } + } + } + if (type&BL_MOB) { // TODO: fix this code duplication + for (bl = map[m].block_mob[bx + by * map[m].bxs]; bl != NULL; bl = bl->next) { + if (bl_list_count < BL_LIST_MAX + //&& bl->type&type // block_mob contains BL_MOBs only + && bl->x >= x0 && bl->x <= x1 + && bl->y >= y0 && bl->y <= y1) { + if (func) { + va_start(args, func); + if (func(bl, args)) { + bl_list[bl_list_count++] = bl; + found++; + } + va_end(args); + } + else { + bl_list[bl_list_count++] = bl; + found++; + } + } + } + } + } + } - iMap->freeblock_lock(); + if (bl_list_count >= BL_LIST_MAX) + ShowError("map.c:bl_getall_area: bl_list size (%d) exceeded\n", BL_LIST_MAX); - for( i = blockcount; i < bl_list_count; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } + return found; +} - iMap->freeblock_unlock(); +/** + * Checks if bl is within range cells from center. + * If CIRCULAR AREA is not used always returns 1, since + * preliminary range selection is already done in bl_getall_area. + * @return 1 if matches, 0 otherwise + */ +static int bl_vgetall_inrange(struct block_list *bl, va_list args) +{ +#ifdef CIRCULAR_AREA + struct block_list *center = va_arg(args, struct block_list*); + int range = va_arg(args, int); + if (!check_distance_bl(center, bl, range)) + return 0; +#endif + return 1; +} + +/** + * Applies func to every block_list object of bl_type type within range cells from center. + * Area is rectangular, unless CIRCULAR_AREA is defined. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param center Center of the selection area + * @param range Range in cells from center + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachinrange(int (*func)(struct block_list*, va_list), struct block_list* center, int16 range, int type, ...) +{ + int returnCount = 0; + int blockcount = bl_list_count; + va_list ap; + + if (range < 0) range *= -1; - bl_list_count = blockcount; - return returnCount; //[Skotlex] + bl_getall_area(type, center->m, center->x - range, center->y - range, center->x + range, center->y + range, bl_vgetall_inrange, center, range); + + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, INT_MAX, ap); + va_end(ap); + + return returnCount; } -/*========================================== -* Adapted from forcountinarea for an easier invocation. [pakpil] -*------------------------------------------*/ -int map_forcountinrange(int (*func)(struct block_list*,va_list), struct block_list* center, int16 range, int count, int type, ...) + +/** + * Applies func to some block_list objects of bl_type type within range cells from center. + * Limit is set by count parameter. + * Area is rectangular, unless CIRCULAR_AREA is defined. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param center Center of the selection area + * @param range Range in cells from center + * @param count Maximum sum of values returned by func (usually max number of func calls) + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_forcountinrange(int (*func)(struct block_list*, va_list), struct block_list* center, int16 range, int count, int type, ...) { - int bx, by, m; - int returnCount = 0; //total sum of returned values of func() [Skotlex] - struct block_list *bl; - int blockcount = bl_list_count, i; - int x0, x1, y0, y1; + int returnCount = 0; + int blockcount = bl_list_count; va_list ap; - m = center->m; - x0 = max(center->x - range, 0); - y0 = max(center->y - range, 0); - x1 = min(center->x + range, map[ m ].xs - 1); - y1 = min(center->y + range, map[ m ].ys - 1); - - if ( type&~BL_MOB ) - for ( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) { - for( bl = map[ m ].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->type&type - && bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 -#ifdef CIRCULAR_AREA - && check_distance_bl(center, bl, range) -#endif - && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - } - } - } - if( type&BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ){ - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 + if (range < 0) range *= -1; + + bl_getall_area(type, center->m, center->x - range, center->y - range, center->x + range, center->y + range, bl_vgetall_inrange, center, range); + + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, count, ap); + va_end(ap); + + return returnCount; +} + +/** + * Checks if bl is within shooting range from center. + * There must be a shootable path between bl and center. + * Does not check for range if CIRCULAR AREA is not defined, since + * preliminary range selection is already done in bl_getall_area. + * @return 1 if matches, 0 otherwise + */ +static int bl_vgetall_inshootrange(struct block_list *bl, va_list args) +{ + struct block_list *center = va_arg(args, struct block_list*); #ifdef CIRCULAR_AREA - && check_distance_bl(center, bl, range) + int range = va_arg(args, int); + if (!check_distance_bl(center, bl, range)) + return 0; #endif - && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - } - } - } + if (!path_search_long(NULL, center->m, center->x, center->y, bl->x, bl->y, CELL_CHKWALL)) + return 0; + return 1; +} - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_forcountinrange: block count too many!\n"); +/** + * Applies func to every block_list object of bl_type type within shootable range from center. + * There must be a shootable path between bl and center. + * Area is rectangular, unless CIRCULAR_AREA is defined. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param center Center of the selection area + * @param range Range in cells from center + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachinshootrange(int (*func)(struct block_list*, va_list), struct block_list* center, int16 range, int type, ...) +{ + int returnCount = 0; + int blockcount = bl_list_count; + va_list ap; - iMap->freeblock_lock(); + if (range < 0) range *= -1; - for( i = blockcount; i < bl_list_count; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - if( count && returnCount >= count ) - break; - } + bl_getall_area(type, center->m, center->x - range, center->y - range, center->x + range, center->y + range, bl_vgetall_inshootrange, center, range); - iMap->freeblock_unlock(); + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, INT_MAX, ap); + va_end(ap); - bl_list_count = blockcount; - return returnCount; //[Skotlex] + return returnCount; } -int map_forcountinarea(int (*func)(struct block_list*,va_list), int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int count, int type, ...) + +/** + * Applies func to every block_list object of bl_type type in + * rectangular area (x0,y0)~(x1,y1) on map m. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param m Map id + * @param x0 Starting X-coordinate + * @param y0 Starting Y-coordinate + * @param x1 Ending X-coordinate + * @param y1 Ending Y-coordinate + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachinarea(int (*func)(struct block_list*, va_list), int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int type, ...) { - int bx, by; - int returnCount = 0; //total sum of returned values of func() [Skotlex] - struct block_list *bl; - int blockcount = bl_list_count, i; + int returnCount = 0; + int blockcount = bl_list_count; va_list ap; - if ( m < 0 ) - return 0; + bl_getall_area(type, m, x0, y0, x1, y1, NULL); - if ( x1 < x0 ) - swap(x0, x1); - if ( y1 < y0 ) - swap(y0, y1); + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, INT_MAX, ap); + va_end(ap); - x0 = max(x0, 0); - y0 = max(y0, 0); - x1 = min(x1, map[ m ].xs - 1); - y1 = min(y1, map[ m ].ys - 1); - - if ( type&~BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) - for( bl = map[ m ].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) - if( bl->type&type && bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( type&BL_MOB ) - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) - if( bl->x >= x0 && bl->x <= x1 && bl->y >= y0 && bl->y <= y1 && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachinarea: block count too many!\n"); + return returnCount; +} - iMap->freeblock_lock(); +/** + * Applies func to some block_list objects of bl_type type in + * rectangular area (x0,y0)~(x1,y1) on map m. + * Limit is set by @count parameter. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param m Map id + * @param x0 Starting X-coordinate + * @param y0 Starting Y-coordinate + * @param x1 Ending X-coordinate + * @param y1 Ending Y-coordinate + * @param count Maximum sum of values returned by func (usually max number of func calls) + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_forcountinarea(int (*func)(struct block_list*,va_list), int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int count, int type, ...) +{ + int returnCount = 0; + int blockcount = bl_list_count; + va_list ap; - for( i = blockcount; i < bl_list_count; i++ ) - if(bl_list[ i ]->prev) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - if( count && returnCount >= count ) - break; - } + bl_getall_area(type, m, x0, y0, x1, y1, NULL); - iMap->freeblock_unlock(); + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, count, ap); + va_end(ap); - bl_list_count = blockcount; - return returnCount; //[Skotlex] + return returnCount; } -/*========================================== -* For what I get -* Move bl and do func* with va_list while moving. -* Mouvement is set by dx dy wich are distance in x and y -*------------------------------------------*/ -int map_foreachinmovearea(int (*func)(struct block_list*,va_list), struct block_list* center, int16 range, int16 dx, int16 dy, int type, ...) +/** + * Checks if bl is inside area that was in range cells from the center + * before it was moved by (dx,dy) cells, but it is not in range cells + * from center after movement is completed. + * In other words, checks if bl is inside area that is no longer covered + * by center's range. + * Preliminary range selection is already done in bl_getall_area. + * @return 1 if matches, 0 otherwise + */ +static int bl_vgetall_inmovearea(struct block_list *bl, va_list args) { - int bx, by, m; - int returnCount = 0; //total sum of returned values of func() [Skotlex] - struct block_list *bl; - int blockcount = bl_list_count, i; - int x0, x1, y0, y1; + int dx = va_arg(args, int); + int dy = va_arg(args, int); + struct block_list *center = va_arg(args, struct block_list*); + int range = va_arg(args, int); + + if ((dx > 0 && bl->x < center->x - range + dx) || + (dx < 0 && bl->x > center->x + range + dx) || + (dy > 0 && bl->y < center->y - range + dy) || + (dy < 0 && bl->y > center->y + range + dy)) + return 1; + return 0; +} + +/** + * Applies func to every block_list object of bl_type type in + * area that was covered by range cells from center, but is no + * longer after center is moved by (dx,dy) cells (i.e. area that + * center has lost sight of). + * If used after center has reached its destination and with + * opposed movement vector (-dx,-dy), selection corresponds + * to new area in center's view). + * Uses rectangular area. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param center Center of the selection area + * @param range Range in cells from center + * @param dx Center's movement on X-axis + * @param dy Center's movement on Y-axis + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachinmovearea(int (*func)(struct block_list*, va_list), struct block_list* center, int16 range, int16 dx, int16 dy, int type, ...) +{ + int returnCount = 0; + int blockcount = bl_list_count; va_list ap; + int m, x0, x1, y0, y1; - if ( !range ) return 0; - if ( !dx && !dy ) return 0; //No movement. + if (!range) return 0; + if (!dx && !dy) return 0; // No movement. - m = center->m; + if (range < 0) range *= -1; + m = center->m; x0 = center->x - range; x1 = center->x + range; y0 = center->y - range; y1 = center->y + range; - if ( x1 < x0 ) - swap(x0, x1); - if ( y1 < y0 ) - swap(y0, y1); - - if( dx == 0 || dy == 0 ) { - //Movement along one axis only. - if( dx == 0 ){ - if( dy < 0 ) //Moving south - y0 = y1 + dy + 1; - else //North - y1 = y0 + dy - 1; + if (dx == 0 || dy == 0) { // Movement along one axis only. + if (dx == 0) { + if (dy < 0) { y0 = y1 + dy + 1; } // Moving south + else { y1 = y0 + dy - 1; } // North } else { //dy == 0 - if( dx < 0 ) //West - x0 = x1 + dx + 1; - else //East - x1 = x0 + dx - 1; - } - - x0 = max(x0, 0); - y0 = max(y0, 0); - x1 = min(x1, map[ m ].xs - 1); - y1 = min(y1, map[ m ].ys - 1); - - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) { - if ( type&~BL_MOB ) { - for( bl = map[m].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->type&type && - bl->x >= x0 && bl->x <= x1 && - bl->y >= y0 && bl->y <= y1 && - bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - } - } - if ( type&BL_MOB ) { - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->x >= x0 && bl->x <= x1 && - bl->y >= y0 && bl->y <= y1 && - bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - } - } - } - } - } else { // Diagonal movement - - x0 = max(x0, 0); - y0 = max(y0, 0); - x1 = min(x1, map[ m ].xs - 1); - y1 = min(y1, map[ m ].ys - 1); - - for( by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++ ) { - for( bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++ ) { - if ( type & ~BL_MOB ) { - for( bl = map[ m ].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->type&type && - bl->x >= x0 && bl->x <= x1 && - bl->y >= y0 && bl->y <= y1 && - bl_list_count < BL_LIST_MAX ) - if( ( dx > 0 && bl->x < x0 + dx) || - ( dx < 0 && bl->x > x1 + dx) || - ( dy > 0 && bl->y < y0 + dy) || - ( dy < 0 && bl->y > y1 + dy) ) - bl_list[ bl_list_count++ ] = bl; - } - } - if ( type&BL_MOB ) { - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->x >= x0 && bl->x <= x1 && - bl->y >= y0 && bl->y <= y1 && - bl_list_count < BL_LIST_MAX) - if( ( dx > 0 && bl->x < x0 + dx) || - ( dx < 0 && bl->x > x1 + dx) || - ( dy > 0 && bl->y < y0 + dy) || - ( dy < 0 && bl->y > y1 + dy) ) - bl_list[ bl_list_count++ ] = bl; - } - } - } + if (dx < 0) { x0 = x1 + dx + 1; } // West + else { x1 = x0 + dx - 1; } // East } - + bl_getall_area(type, m, x0, y0, x1, y1, NULL); + } + else { // Diagonal movement + bl_getall_area(type, m, x0, y0, x1, y1, bl_vgetall_inmovearea, dx, dy, center, range); } - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachinmovearea: block count too many!\n"); - - iMap->freeblock_lock(); // Prohibit the release from memory - - for( i = blockcount; i < bl_list_count; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } - - iMap->freeblock_unlock(); // Allow Free + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, INT_MAX, ap); + va_end(ap); - bl_list_count = blockcount; - return returnCount; + return returnCount; } -// -- moonsoul (added map_foreachincell which is a rework of map_foreachinarea but -// which only checks the exact single x/y passed to it rather than an -// area radius - may be more useful in some instances) -// -int map_foreachincell(int (*func)(struct block_list*,va_list), int16 m, int16 x, int16 y, int type, ...) +/** + * Applies func to every block_list object of bl_type type in + * cell (x,y) on map m. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param m Map id + * @param x Target cell X-coordinate + * @param y Target cell Y-coordinate + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachincell(int (*func)(struct block_list*, va_list), int16 m, int16 x, int16 y, int type, ...) { - int bx, by; - int returnCount = 0; //total sum of returned values of func() [Skotlex] - struct block_list *bl; - int blockcount = bl_list_count, i; + int returnCount = 0; + int blockcount = bl_list_count; va_list ap; - if ( x < 0 || y < 0 || x >= map[ m ].xs || y >= map[ m ].ys ) return 0; + bl_getall_area(type, m, x, y, x, y, NULL); - by = y / BLOCK_SIZE; - bx = x / BLOCK_SIZE; + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, INT_MAX, ap); + va_end(ap); - if( type&~BL_MOB ) - for( bl = map[ m ].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) - if( bl->type&type && bl->x == x && bl->y == y && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - if( type&BL_MOB ) - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs]; bl != NULL; bl = bl->next ) - if( bl->x == x && bl->y == y && bl_list_count < BL_LIST_MAX) - bl_list[ bl_list_count++ ] = bl; + return returnCount; +} - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachincell: block count too many!\n"); +/** + * Helper function for map_foreachinpath() + * Checks if shortest distance from bl to path + * between (x0,y0) and (x1,y1) is shorter than range. + * @see map_foreachinpath + */ +static int bl_vgetall_inpath(struct block_list *bl, va_list args) +{ + int m = va_arg(args, int); + int x0 = va_arg(args, int); + int y0 = va_arg(args, int); + int x1 = va_arg(args, int); + int y1 = va_arg(args, int); + int range = va_arg(args, int); + int len_limit = va_arg(args, int); + int magnitude2 = va_arg(args, int); + + int xi = bl->x; + int yi = bl->y; + int xu, yu; + + int k = ( xi - x0 ) * ( x1 - x0 ) + ( yi - y0 ) * ( y1 - y0 ); + + if ( k < 0 || k > len_limit ) //Since more skills use this, check for ending point as well. + return 0; - iMap->freeblock_lock(); + if ( k > magnitude2 && !path_search_long(NULL, m, x0, y0, xi, yi, CELL_CHKWALL) ) + return 0; //Targets beyond the initial ending point need the wall check. - for( i = blockcount; i < bl_list_count; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } + //All these shifts are to increase the precision of the intersection point and distance considering how it's + //int math. + k = ( k << 4 ) / magnitude2; //k will be between 1~16 instead of 0~1 + xi <<= 4; + yi <<= 4; + xu = ( x0 << 4 ) + k * ( x1 - x0 ); + yu = ( y0 << 4 ) + k * ( y1 - y0 ); - iMap->freeblock_unlock(); +//Avoid needless calculations by not getting the sqrt right away. +#define MAGNITUDE2(x0, y0, x1, y1) ( ( ( x1 ) - ( x0 ) ) * ( ( x1 ) - ( x0 ) ) + ( ( y1 ) - ( y0 ) ) * ( ( y1 ) - ( y0 ) ) ) + + k = MAGNITUDE2(xi, yi, xu, yu); + + //If all dot coordinates were <<4 the square of the magnitude is <<8 + if ( k > range ) + return 0; - bl_list_count = blockcount; - return returnCount; + return 1; } -/*============================================================ -* For checking a path between two points (x0, y0) and (x1, y1) -*------------------------------------------------------------*/ -int map_foreachinpath(int (*func)(struct block_list*,va_list),int16 m,int16 x0,int16 y0,int16 x1,int16 y1,int16 range,int length, int type,...) +/** + * Applies func to every block_list object of bl_type type in + * path on a line between (x0,y0) and (x1,y1) on map m. + * Path starts at (x0,y0) and is \a length cells long and \a range cells wide. + * Objects beyond the initial (x1,y1) ending point are checked + * for walls in the path. + * Returns the sum of values returned by func. + * @param func Function to be applied + * @param m Map id + * @param x Target cell X-coordinate + * @param y Target cell Y-coordinate + * @param type enum bl_type + * @param ... Extra arguments for func + * @return Sum of the values returned by func + */ +int map_foreachinpath(int (*func)(struct block_list*, va_list), int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int16 range, int length, int type, ...) { - int returnCount = 0; //total sum of returned values of func() [Skotlex] - ////////////////////////////////////////////////////////////// - // - // sharp shooting 3 [Skotlex] - // - ////////////////////////////////////////////////////////////// - // problem: - // Same as Sharp Shooting 1. Hits all targets within range of - // the line. - // (t1,t2 t3 and t4 get hit) - // - // target 1 - // x t4 - // t2 - // t3 x - // x - // S - ////////////////////////////////////////////////////////////// - // Methodology: - // My trigonometrics and math are a little rusty... so the approach I am writing - // here is basicly do a double for to check for all targets in the square that + // [Skotlex] + // check for all targets in the square that // contains the initial and final positions (area range increased to match the // radius given), then for each object to test, calculate the distance to the // path and include it if the range fits and the target is in the line (0 mx1 ) { + if (mx0 > mx1) { mx0 += range; mx1 -= range; } else { @@ -1063,191 +1125,19 @@ int map_foreachinpath(int (*func)(struct block_list*,va_list),int16 m,int16 x0,i my0 -= range; my1 += range; } - - //The two fors assume mx0 < mx1 && my0 < my1 - if ( mx0 > mx1 ) - swap(mx0, mx1); - if ( my0 > my1 ) - swap(my0, my1); - - mx0 = max(mx0, 0); - my0 = max(my0, 0); - mx1 = min(mx1, map[ m ].xs - 1); - my1 = min(my1, map[ m ].ys - 1); - range *= range << 8; //Values are shifted later on for higher precision using int math. - if ( type&~BL_MOB ) - for ( by = my0 / BLOCK_SIZE; by <= my1 / BLOCK_SIZE; by++ ) { - for( bx = mx0 / BLOCK_SIZE; bx <= mx1 / BLOCK_SIZE; bx++ ) { - for( bl = map[ m ].block[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->prev && bl->type&type && bl_list_count < BL_LIST_MAX ) { - xi = bl->x; - yi = bl->y; - - k = ( xi - x0 ) * ( x1 - x0 ) + ( yi - y0 ) * ( y1 - y0 ); - - if ( k < 0 || k > len_limit ) //Since more skills use this, check for ending point as well. - continue; - - if ( k > magnitude2 && !path_search_long(NULL, m, x0, y0, xi, yi, CELL_CHKWALL) ) - continue; //Targets beyond the initial ending point need the wall check. - - //All these shifts are to increase the precision of the intersection point and distance considering how it's - //int math. - k = ( k << 4 ) / magnitude2; //k will be between 1~16 instead of 0~1 - xi <<= 4; - yi <<= 4; - xu = ( x0 << 4 ) + k * ( x1 - x0 ); - yu = ( y0 << 4 ) + k * ( y1 - y0 ); - k = MAGNITUDE2(xi, yi, xu, yu); - - //If all dot coordinates were <<4 the square of the magnitude is <<8 - if ( k > range ) - continue; - - bl_list[ bl_list_count++ ] = bl; - } - } - } - } - if( type&BL_MOB ) - for( by = my0 / BLOCK_SIZE; by <= my1 / BLOCK_SIZE; by++ ) { - for( bx = mx0 / BLOCK_SIZE; bx <= mx1 / BLOCK_SIZE; bx++ ) { - for( bl = map[ m ].block_mob[ bx + by * map[ m ].bxs ]; bl != NULL; bl = bl->next ) { - if( bl->prev && bl_list_count < BL_LIST_MAX ) { - xi = bl->x; - yi = bl->y; - k = ( xi - x0 ) * ( x1 - x0 ) + ( yi - y0 ) * ( y1 - y0 ); - - if ( k < 0 || k > len_limit ) - continue; - - if ( k > magnitude2 && !path_search_long(NULL, m, x0, y0, xi, yi, CELL_CHKWALL) ) - continue; //Targets beyond the initial ending point need the wall check. - - k = ( k << 4 ) / magnitude2; //k will be between 1~16 instead of 0~1 - xi <<= 4; - yi <<= 4; - xu = ( x0 << 4 ) + k * ( x1 - x0 ); - yu = ( y0 << 4 ) + k * ( y1 - y0 ); - k = MAGNITUDE2(xi, yi, xu, yu); - - //If all dot coordinates were <<4 the square of the magnitude is <<8 - if ( k > range ) - continue; - - bl_list[ bl_list_count++ ] = bl; - } - } - } - } - - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachinpath: block count too many!\n"); - - iMap->freeblock_lock(); - - for( i = blockcount; i < bl_list_count; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } - - iMap->freeblock_unlock(); + bl_getall_area(type, m, mx0, my0, mx1, my1, bl_vgetall_inpath, m, x0, y0, x1, y1, range, len_limit, magnitude2); - bl_list_count = blockcount; - return returnCount; //[Skotlex] + va_start(ap, type); + returnCount = bl_vforeach(func, blockcount, INT_MAX, ap); + va_end(ap); -} - -// Copy of map_foreachincell, but applied to the whole map. [Skotlex] -int map_foreachinmap(int (*func)(struct block_list*,va_list), int16 m, int type,...) { - int b, bsize; - int returnCount = 0; //total sum of returned values of func() [Skotlex] - struct block_list *bl; - int blockcount = bl_list_count, i; - va_list ap; - - bsize = map[ m ].bxs * map[ m ].bys; - - if( type&~BL_MOB ) - for( b = 0; b < bsize; b++ ) - for( bl = map[ m ].block[ b ]; bl != NULL; bl = bl->next ) - if( bl->type&type && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( type&BL_MOB ) - for( b = 0; b < bsize; b++ ) - for( bl = map[ m ].block_mob[ b ]; bl != NULL; bl = bl->next ) - if( bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachinmap: block count too many!\n"); - - iMap->freeblock_lock(); - - for( i = blockcount; i < bl_list_count ; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } - - iMap->freeblock_unlock(); - - bl_list_count = blockcount; - return returnCount; -} -// Copy of map_foreachinmap, but applied to all maps in a instance id. [Ind/Hercules] -int map_foreachininstance(int (*func)(struct block_list*,va_list), int16 instance_id, int type,...) { - int b, bsize; - int returnCount = 0; //total sum of returned values of func() [Skotlex] - struct block_list *bl; - int blockcount = bl_list_count, i, j; - int16 m; - va_list ap; - - for( j = 0; j < instances[instance_id].num_map; j++ ) { - - m = instances[instance_id].map[j]; - - bsize = map[ m ].bxs * map[ m ].bys; - - if( type&~BL_MOB ) - for( b = 0; b < bsize; b++ ) - for( bl = map[ m ].block[ b ]; bl != NULL; bl = bl->next ) - if( bl->type&type && bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( type&BL_MOB ) - for( b = 0; b < bsize; b++ ) - for( bl = map[ m ].block_mob[ b ]; bl != NULL; bl = bl->next ) - if( bl_list_count < BL_LIST_MAX ) - bl_list[ bl_list_count++ ] = bl; - - if( bl_list_count >= BL_LIST_MAX ) - ShowWarning("map_foreachininstance: block count too many!\n"); - - iMap->freeblock_lock(); - - for( i = blockcount; i < bl_list_count ; i++ ) - if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. - va_start(ap, type); - returnCount += func(bl_list[ i ], ap); - va_end(ap); - } - - iMap->freeblock_unlock(); - - } - - bl_list_count = blockcount; return returnCount; } +#undef MAGNITUDE2 +/** @} */ /// Generates a new flooritem object id from the interval [MIN_FLOORITEM, MAX_FLOORITEM). /// Used for floor items, skill units and chatroom objects. diff --git a/src/map/map.h b/src/map/map.h index 7826e00b3..4bee9c8ea 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -546,8 +546,19 @@ struct map_data { char name[MAP_NAME_LENGTH]; uint16 index; // The map index used by the mapindex* functions. struct mapcell* cell; // Holds the information of each map cell (NULL if the map is not on this map-server). - struct block_list **block; - struct block_list **block_mob; + + /* 2D Orthogonal Range Search: Grid Implementation + "Algorithms in Java, Parts 1-4" 3.18, Robert Sedgewick + Map is divided into squares, called blocks (side length = BLOCK_SIZE). + For each block there is a linked list of objects in that block (block_list). + Array provides capability to access immediately the set of objects close + to a given object. + The linked lists provide the flexibility to store the objects without + knowing ahead how many objects fall into each block. + */ + struct block_list **block; // Grid array of block_lists containing only non-BL_MOB objects + struct block_list **block_mob; // Grid array of block_lists containing only BL_MOB objects + int16 m; int16 xs,ys; // map dimensions (in cells) int16 bxs,bys; // map dimensions (in blocks) -- cgit v1.2.3-70-g09d2