/* * Additional functions for the Gateway system. * Only needed for map preprocessing. * */ // segment printf's //#define DEBUG_GROUP1 // stack printf's //#define DEBUG_GROUP2 // gwProcessMap printf's //#define DEBUG_GROUP3 // RLE zone map size //#define DEBUG_GROUP4 // equivalence printf's //#define DEBUG_GROUP5 #ifdef EDITORWORLD #include #define MALLOC(a) malloc(a) #define FREE(a) free(a); a = NULL; #include "typedefs.h" #define MAP_MAXWIDTH 256 #define MAP_MAXHEIGHT 256 #include "gateinterface.h" #include "debugprint.h" #else #include "frame.h" #include "map.h" #endif #include #include "gateway.h" // Structures and defines for SeedFill(). typedef int Pixel; /* 1-channel frame buffer assumed */ struct FRect { /* window: a discrete 2-D rectangle */ int x0, y0; /* xmin and ymin */ int x1, y1; /* xmax and ymax (inclusive) */ }; struct Segment { int y; // int xl; // Filled horizontal segment of scanline y for xl<=x<=xr. int xr; // Parent segment was on line y-dy. dy=1 or -1 int dy; // }; #define MAX 10000 /* max depth of stack */ #define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \ DBP2(("PUSH y %d x %d->%d dy %d\n", Y, XL, XR, DY)); \ if (spy = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;} #define POP(Y, XL, XR, DY) /* pop segment off stack */ \ {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;} \ DBP2(("POP y %d x %d->%d dy %d\n", Y, XL, XR, DY)); // whether the flood fill is running over water BOOL bGwWaterFlood = FALSE;; // check for a blocking tile for the flood fill BOOL gwFloodBlock(SDWORD x, SDWORD y); // generate the zone equivalence tables BOOL gwGenerateZoneEquiv(SDWORD numZones); #define ENABLEFILL // disable this on the psx #ifdef ENABLEFILL struct Segment stack[MAX], *sp = stack; /* stack of filled segments */ #endif // Flood fill a map zone from a given point // stopping at blocking tiles /* * fill: set the pixel at (x,y) and all of its 4-connected neighbors * with the same pixel value to the new pixel value nv. * A 4-connected neighbor is a pixel above, below, left, or right of a pixel. */ void gwSeedFill(SDWORD x, SDWORD y, SDWORD nv) { #ifdef ENABLEFILL int l, x1, x2, dy; Pixel ov; /* old pixel value */ struct Segment stack[MAX], *sp = stack; /* stack of filled segments */ ov = gwGetZone(x, y); /* read pv at seed point */ if (ov==nv) { return; } PUSH(y, x, x, 1); /* needed in some cases */ PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */ while (sp>stack) { /* pop segment off stack and fill a neighboring scan line */ POP(y, x1, x2, dy); /* * segment of scan line y-dy for x1<=x<=x2 was previously filled, * now explore adjacent pixels in scan line y */ DBP1(("-ve (%d,%d)->", x1,y)); for (x=x1; !gwFloodBlock(x,y) && (gwGetZone(x, y)==ov); x--) { gwSetZone(x, y, nv); } DBP1(("(%d,%d) %s\n", x+1,y, x=x1) { goto skip; } l = x+1; if (l", x,y)); for (;!gwFloodBlock(x,y) && (gwGetZone(x, y)==ov); x++) { gwSetZone(x, y, nv); } DBP1(("(%d,%d) %s\n", x-1,y, (x>l)&&(x>x1+1)?"OK":"")); PUSH(y, l, x-1, dy); if (x>x2+1) { PUSH(y, x2+1, x-1, -dy); /* leak on right? */ } skip: for (x++; x<=x2 && (gwFloodBlock(x,y) || gwGetZone(x, y)!=ov); x++); l = x; } while (x<=x2); } #else // GODDAM *#!! LOWERCASE assert IS ABSOLUTELY NO %^$## USE ON THE PC // assert(2+2==5); ASSERT((FALSE, "gwSeedFill disabled")); #endif } // set the tiles a gateway covers to a zone void gwSetGatewayZone(GATEWAY *psGate, SDWORD zone) { SDWORD pos; if (psGate->x1 == psGate->x2) { for(pos = psGate->y1; pos <= psGate->y2; pos++) { gwSetZone(psGate->x1, pos, zone); } } else { for(pos = psGate->x1; pos <= psGate->x2; pos++) { gwSetZone(pos, psGate->y1, zone); } } } // find the first zone on a line SDWORD gwFindFirstZone(SDWORD y) { SDWORD x, zone; zone = 0; while (zone == 0 && y < gwMapHeight()) { for(x=0; xpsNext) { psCurr->zone1 = 0; psCurr->zone2 = 0; } // flood fill from all the gateways psCurr = psGateways; currZone = 1; do { DBP3(("Processing gateway (%d,%d)->(%d,%d)\n", psCurr->x1,psCurr->y1, psCurr->x2,psCurr->y2)); // do a flood fill from the current gateway if (psCurr->zone1 == 0) { // first zone is left/above if (psCurr->x1 == psCurr->x2) { // vertical - go left floodX = psCurr->x1 - 1; floodY = (psCurr->y2 - psCurr->y1)/2 + psCurr->y1; } else { // horizontal - go above floodX = (psCurr->x2 - psCurr->x1)/2 + psCurr->x1; floodY = psCurr->y1 - 1; } } else // psCurr->zone2 == 0 { // second zone is right/below if (psCurr->x1 == psCurr->x2) { // vertical - go right floodX = psCurr->x1 + 1; floodY = (psCurr->y2 - psCurr->y1)/2 + psCurr->y1; } else { // horizontal - go below floodX = (psCurr->x2 - psCurr->x1)/2 + psCurr->x1; floodY = psCurr->y1 + 1; } } // see if a previous flood fill reached this gateway zoneTest = gwGetZone(floodX,floodY); if (zoneTest != 0) { // simple case just have to link the gateway to the zone gatewayZone = zoneTest; } else { // check the zones havn't overflowed if (currZone > UBYTE_MAX) { DBERROR(("gwProcessMap: too many zones\n")); return FALSE; } // floodFill if (gwTileIsWater(floodX,floodY)) { bGwWaterFlood = TRUE; } gwSeedFill(floodX,floodY, currZone); bGwWaterFlood = FALSE; gatewayZone = currZone; currZone += 1; } // set the gateway zone if (psCurr->zone1 == 0) { psCurr->zone1 = (UBYTE)gatewayZone; // the gateway is always in it's own zone1 gwSetGatewayZone(psCurr, gatewayZone); } else { psCurr->zone2 = (UBYTE)gatewayZone; } // see if there are any gateways left to process for(psCurr=psGateways; psCurr; psCurr = psCurr->psNext) { if ((psCurr->zone1 == 0) || (psCurr->zone2 == 0)) { break; } } } while (psCurr != NULL); // fill in any areas that are left for(y=0; y UBYTE_MAX) { DBERROR(("gwProcessMap: too many zones\n")); return FALSE; } bGwWaterFlood = TRUE; gwSeedFill(x,y, currZone); bGwWaterFlood = FALSE; currZone += 1; } else if (!gwFloodBlock(x,y) && gwGetZone(x,y) == 0) { // check the zones havn't overflowed if (currZone > UBYTE_MAX) { DBERROR(("gwProcessMap: too many zones\n")); return FALSE; } gwSeedFill(x,y, currZone); currZone += 1; } } } // now average out the zones so that blocking tiles are in the same zone as their neighbour for(y=0; y= 0) && (x < gwMapWidth()) && (y >= 0) && (y < gwMapHeight()), "gwSetZone: invalid coordinates")); gwDecompressLine(y, aBuffer); aBuffer[x] = (UBYTE)zone; gwCompressLine(y, aBuffer); } /******************************************************************************************************/ /* Gateway data access functions */ #ifdef EDITORWORLD BOOL gwFloodBlock(SDWORD x, SDWORD y) { // MAPTILE *psTile; // SDWORD type; // BOOL gateway; if ((x < 0) || (x >= gwMapWidth()) || (y < 0) || (y >= gwMapHeight())) { return TRUE; } // psTile = mapTile(x,y); // type = TERRAIN_TYPE(psTile); // gateway = (psTile->tileInfoBits & BITS_GATEWAY) != 0; // return (type == TER_CLIFFFACE) || (type == TER_WATER) || gateway; return giIsGateway(x,y) || ( !bGwWaterFlood && (giIsClifface(x,y) || giIsWater(x,y))) || ( bGwWaterFlood && !giIsWater(x,y) ); // return giIsClifface(x,y) || giIsWater(x,y) || giIsGateway(x,y); } #else // check for a blocking tile for the flood fill BOOL gwFloodBlock(SDWORD x, SDWORD y) { MAPTILE *psTile; SDWORD type; BOOL gateway; if ((x < 0) || (x >= gwMapWidth()) || (y < 0) || (y >= gwMapHeight())) { return TRUE; } psTile = mapTile(x,y); type = TERRAIN_TYPE(psTile); gateway = (psTile->tileInfoBits & BITS_GATEWAY) != 0; return gateway || ( !bGwWaterFlood && ((type == TER_CLIFFFACE) || (type == TER_WATER))) || ( bGwWaterFlood && (type != TER_WATER) ); } #endif