165
// Add a land/water link gateway to the system
166
BOOL gwNewLinkGateway(SDWORD x, SDWORD y)
170
if ((x < 0) || (x >= gwMapWidth()) ||
171
(y < 0) || (y >= gwMapHeight()))
173
ASSERT( false,"gwNewLinkGateway: invalid coordinates" );
177
psNew = (GATEWAY*)malloc(sizeof(GATEWAY));
180
debug( LOG_ERROR, "gwNewGateway: out of memory" );
185
// initialise the gateway
186
psNew->x1 = (UBYTE)x;
187
psNew->y1 = (UBYTE)y;
188
psNew->x2 = (UBYTE)x;
189
psNew->y2 = (UBYTE)y;
192
psNew->psLinks = NULL;
193
psNew->flags = GWR_WATERLINK;
195
// add the gateway to the list
196
psNew->psNext = psGateways;
203
static BOOL gwBlockingTile(SDWORD x,SDWORD y)
207
if (x <1 || y < 1 || x >= (SDWORD)mapWidth-1 || y >= (SDWORD)mapHeight-1)
209
// coords off map - auto blocking tile
213
psTile = mapTile((UDWORD)x, (UDWORD)y);
214
if (terrainType(psTile) == TER_CLIFFFACE)
223
// scan for a particular zone on the map
224
// given a start point
225
static BOOL gwFindZone(SDWORD zone, SDWORD cx, SDWORD cy,
226
SDWORD *px, SDWORD *py)
228
SDWORD x,y, dist, maxDist;
230
maxDist = gwMapWidth() > gwMapHeight() ? gwMapWidth() : gwMapHeight();
232
for(dist = 0; dist < maxDist; dist += 1)
234
// scan accross the top
236
for(x = cx - dist; x <= cx + dist; x += 1)
238
if (x >= 0 && x < gwMapWidth() &&
239
y >= 0 && y < gwMapHeight() &&
240
gwGetZone(x,y) == zone)
248
// scan down the left
250
for(y = cy - dist; y <= cy + dist; y += 1)
252
if (x >= 0 && x < gwMapWidth() &&
253
y >= 0 && y < gwMapHeight() &&
254
gwGetZone(x,y) == zone)
262
// scan down the right
264
for(y = cy - dist; y <= cy + dist; y += 1)
266
if (x >= 0 && x < gwMapWidth() &&
267
y >= 0 && y < gwMapHeight() &&
268
gwGetZone(x,y) == zone)
276
// scan accross the bottom
278
for(x = cx - dist; x <= cx + dist; x += 1)
280
if (x >= 0 && x < gwMapWidth() &&
281
y >= 0 && y < gwMapHeight() &&
282
gwGetZone(x,y) == zone)
295
// find a rough center position for a zone
296
static void gwCalcZoneCenter(SDWORD zone, SDWORD *px, SDWORD *py)
298
SDWORD xsum,ysum, numtiles;
301
xsum = ysum = numtiles = 0;
302
for(y=0; y<gwMapHeight(); y+= 1)
304
for(x=0; x<gwMapWidth(); x+= 1)
306
if (gwGetZone(x,y) == zone)
315
ASSERT( numtiles != 0,
316
"gwCalcZoneCenter: zone not found on map" );
321
if (!gwFindZone(zone, x,y, px,py))
329
// check all the zones are of reasonable sizes
330
void gwCheckZoneSizes(void)
332
SDWORD zone, xsum,ysum, numtiles, inzone;
335
for(zone=1; zone < UBYTE_MAX; zone += 1)
337
xsum = ysum = numtiles = inzone = 0;
338
for(y=0; y<gwMapHeight(); y+= 1)
340
for(x=0; x<gwMapWidth(); x+= 1)
342
if (gwGetZone(x,y) == zone)
347
if (!gwBlockingTile(x,y))
360
if (!gwFindZone(zone, x,y, &cx,&cy))
366
if (inzone > FPATH_LOOP_LIMIT)
368
debug(LOG_GATEWAY, "gwCheckZoneSizes: warning zone %d at (%d,%d) is too large %d tiles (max %d)",
369
zone, cx, cy, inzone, FPATH_LOOP_LIMIT);
376
// add the land/water link gateways
377
BOOL gwGenerateLinkGates(void)
381
ASSERT( apEquivZones != NULL,
382
"gwGenerateLinkGates: no zone equivalence table" );
384
debug( LOG_NEVER, "Generating water link Gateways...." );
386
for(zone=1; zone<gwNumZones; zone += 1)
389
if (aNumEquiv[zone] > 0)
391
LOADBARCALLBACK(); // loadingScreenCallback();
393
// got a water zone that borders on land
395
gwCalcZoneCenter(zone, &cx,&cy);
396
if (!gwNewLinkGateway(cx,cy))
400
debug(LOG_GATEWAY, "new water link gateway at (%d,%d) for zone %d", cx, cy, zone);
404
debug( LOG_NEVER, "Done\n" );
410
166
// Return the number of gateways.
411
167
UDWORD gwNumGateways(void)
500
// check if a zone is in the equivalence table for a water zone
501
BOOL gwZoneInEquiv(SDWORD mainZone, SDWORD checkZone)
505
if (apEquivZones == NULL)
510
for(i=0; i<aNumEquiv[mainZone]; i+= 1)
512
if (apEquivZones[mainZone][i] == checkZone)
521
// find a route between two gateways and return
523
static SDWORD gwRouteLength(GATEWAY *psStart, GATEWAY *psEnd)
525
SDWORD ret, sx,sy, ex,ey, xdiff,ydiff, i;
527
SDWORD routeMode, dist;
532
fpathBlockingTile = gwBlockingTile;
534
sx = (psStart->x1 + psStart->x2)/2;
535
sy = (psStart->y1 + psStart->y2)/2;
536
ex = (psEnd->x1 + psEnd->x2)/2;
537
ey = (psEnd->y1 + psEnd->y2)/2;
539
// force the router to finish a route
540
routeMode = ASR_NEWROUTE;
541
sRoute.asPos[0].x = -1;
542
sRoute.asPos[0].y = -1;
545
astarResetCounters();
546
sRoute.numPoints = 0;
547
ret = fpathAStarRoute(routeMode, &sRoute,
548
world_coord(sx), world_coord(sy),
549
world_coord(ex), world_coord(ey));
550
if (ret == ASR_PARTIAL)
552
routeMode = ASR_CONTINUE;
554
} while (ret == ASR_PARTIAL);
556
ASSERT( ret != ASR_FAILED,
557
"gwRouteLength: no route between gateways at (%d,%d) and (%d,%d)",
561
if (ret == ASR_NEAREST)
563
zone = (psStart->zone1 == psEnd->zone1) || (psStart->zone1 == psEnd->zone2) ? psStart->zone1 : psStart->zone2;
564
debug( LOG_ERROR, "gwRouteLength: warning only partial route between gateways at %s(%d,%d) and %s(%d,%d) zone %d\n", psStart->flags & GWR_WATERLINK ? "W" : "", sx,sy, psStart->flags & GWR_WATERLINK ? "W" : "", ex, ey, zone );
568
// calculate the length of the route
570
for(i=0; i < sRoute.numPoints; i+= 1)
572
xdiff = sx - sRoute.asPos[i].x;
573
ydiff = sy - sRoute.asPos[i].y;
574
dist += (SDWORD)sqrtf(xdiff*xdiff + ydiff*ydiff);
575
sx = sRoute.asPos[i].x;
576
sy = sRoute.asPos[i].y;
580
dist += (SDWORD)sqrtf(xdiff*xdiff + ydiff*ydiff);
582
fpathBlockingTile = fpathGroundBlockingTile;
588
// check that the initial flood fill tiles are not on a blocking tile
589
static BOOL gwCheckFloodTiles(GATEWAY *psGate)
591
SDWORD floodX,floodY;
593
// first zone is left/above
594
if (psGate->x1 == psGate->x2)
596
// vertical - go left
597
floodX = psGate->x1 - 1;
598
floodY = (psGate->y2 - psGate->y1)/2 + psGate->y1;
602
// horizontal - go above
603
floodX = (psGate->x2 - psGate->x1)/2 + psGate->x1;
604
floodY = psGate->y1 - 1;
607
if (gwBlockingTile(floodX,floodY))
612
// second zone is right/below
613
if (psGate->x1 == psGate->x2)
615
// vertical - go right
616
floodX = psGate->x1 + 1;
617
floodY = (psGate->y2 - psGate->y1)/2 + psGate->y1;
621
// horizontal - go below
622
floodX = (psGate->x2 - psGate->x1)/2 + psGate->x1;
623
floodY = psGate->y1 + 1;
626
if (gwBlockingTile(floodX,floodY))
635
// link all the gateways together
636
BOOL gwLinkGateways(void)
638
GATEWAY *psCurr, *psLink;
639
SDWORD x,y, gwX,gwY, zone1Links,zone2Links, link, zone, otherZone;
641
BOOL bZone1, bAddLink;
644
// note which zones have a gateway
645
aZoneReachable = (UBYTE*)malloc( sizeof(UBYTE) * gwNumZones );
646
if (aZoneReachable == NULL)
648
debug( LOG_ERROR, "gwLinkGateways: out of memory" );
652
memset(aZoneReachable, 0, sizeof(UBYTE) * gwNumZones);
654
// initialise the zones for the gateways
655
for(psCurr = psGateways; psCurr; psCurr = psCurr->psNext)
657
// a gateway is always in it's own zone1
658
psCurr->zone1 = (UBYTE)gwGetZone(psCurr->x1,psCurr->y1);
660
if (psCurr->flags & GWR_WATERLINK)
662
// a water link gateway is only in one zone
666
else if (psCurr->x1 == psCurr->x2)
668
// vertical - go right
670
y = (psCurr->y2 - psCurr->y1)/2 + psCurr->y1;
674
// horizontal - go below
675
x = (psCurr->x2 - psCurr->x1)/2 + psCurr->x1;
678
psCurr->zone2 = (UBYTE)gwGetZone(x,y);
680
ASSERT( (psCurr->flags & GWR_WATERLINK) || gwCheckFloodTiles(psCurr),
681
"gwLinkGateways: Gateway at (%d,%d)->(%d,%d) is too close to a blocking tile. Zones %d, %d",
682
psCurr->x1,psCurr->y1, psCurr->x2,psCurr->y2,
683
psCurr->zone1, psCurr->zone2 );
685
aZoneReachable[psCurr->zone1] = true;
686
aZoneReachable[psCurr->zone2] = true;
689
// now link all the gateways together
690
for(psCurr = psGateways; psCurr; psCurr = psCurr->psNext)
692
LOADBARCALLBACK(); // loadingScreenCallback();
694
gwX = (psCurr->x1 + psCurr->x2)/2;
695
gwY = (psCurr->y1 + psCurr->y2)/2;
697
// count the number of links
700
for(psLink=psGateways; psLink; psLink=psLink->psNext)
702
if (psLink == psCurr)
704
// don't link a gateway to itself
707
if ((psLink->zone1 == psCurr->zone1) || (psLink->zone2 == psCurr->zone1) ||
708
((psLink->flags & GWR_WATERLINK) &&
709
gwZoneInEquiv(psLink->zone1, psCurr->zone1) &&
710
!gwZoneInEquiv(psLink->zone1, psCurr->zone2) ))
714
if (psCurr->flags & GWR_WATERLINK)
716
// calculating links for a water link gateway
717
if (gwZoneInEquiv(psCurr->zone1, psLink->zone1) ||
718
gwZoneInEquiv(psCurr->zone1, psLink->zone2))
723
else if ((psLink->zone1 == psCurr->zone2) || (psLink->zone2 == psCurr->zone2) ||
724
((psLink->flags & GWR_WATERLINK) &&
725
gwZoneInEquiv(psLink->zone1, psCurr->zone2) &&
726
!gwZoneInEquiv(psLink->zone1, psCurr->zone1) ))
731
if (zone1Links+zone2Links > 0)
733
psCurr->psLinks = (GATEWAY_LINK*)malloc(sizeof(GATEWAY_LINK) * (zone1Links+zone2Links));
734
if (psCurr->psLinks == NULL)
736
debug( LOG_ERROR, "gwLinkGateways: out of memory" );
743
psCurr->psLinks = NULL;
745
psCurr->zone1Links = (UBYTE)zone1Links;
746
psCurr->zone2Links = (UBYTE)zone2Links;
748
// generate the links starting with all those through zone1
750
zone = psCurr->zone1;
751
otherZone = psCurr->zone2;
752
zoneLinks = zone1Links;
754
while (link < (zone1Links + zone2Links))
756
for(psLink=psGateways; psLink && (link < zoneLinks); psLink=psLink->psNext)
758
if (psLink == psCurr)
760
// don't link a gateway to itself
764
if (!bZone1 && (psCurr->flags & GWR_WATERLINK))
766
// calculating links for a water link gateway
767
if (gwZoneInEquiv(psCurr->zone1, psLink->zone1) ||
768
gwZoneInEquiv(psCurr->zone1, psLink->zone2))
773
else if ((psLink->zone1 == zone) || (psLink->zone2 == zone) ||
774
((psLink->flags & GWR_WATERLINK) &&
775
gwZoneInEquiv(psLink->zone1, zone) &&
776
!gwZoneInEquiv(psLink->zone1, otherZone) ))
783
// debug( LOG_NEVER, "Linking %sgateway (%d,%d)->(%d,%d) through %s to gateway (%d,%d)->(%d,%d)\n", (psCurr->flags & GWR_WATERLINK) ? "water " : "", psCurr->x1,psCurr->y1, psCurr->x2, psCurr->y2, bZone1 ? "zone1" : "zone2", psLink->x1, psLink->y1, psLink->x2, psLink->y2 );
784
psCurr->psLinks[link].psGateway = psLink;
785
psCurr->psLinks[link].flags = 0;
787
psCurr->psLinks[link].dist = (SWORD)gwRouteLength(psCurr, psLink);
793
// found all the links to zone1, now do it for zone2
794
zone = psCurr->zone2;
795
otherZone = psCurr->zone1;
796
zoneLinks = zone1Links + zone2Links;
805
/******************************************************************************************************/
806
/* RLE Zone data access functions */
809
// Get number of zone lines.
810
UDWORD gwNumZoneLines(void)
812
return gwMapHeight();
816
// Get the size of a zone line.
817
UDWORD gwZoneLineSize(UDWORD Line)
823
ASSERT( Line < (UDWORD)gwMapHeight(),"gwNewZoneLine : Invalid line requested" );
824
ASSERT( apRLEZones != NULL,"gwNewZoneLine : NULL Zone map" );
826
pCode = apRLEZones[Line];
828
while (x < (UDWORD)gwMapWidth()) {
837
// Create a new empty zone map but don't allocate the actual zones yet.
839
BOOL gwNewZoneMap(void)
843
if (apRLEZones != NULL)
848
apRLEZones = (UBYTE**)malloc(sizeof(UBYTE *) * gwMapHeight());
849
if (apRLEZones == NULL)
851
debug( LOG_ERROR, "gwNewZoneMap: Out of memory" );
856
for(i=0; i< gwMapHeight(); i++)
858
apRLEZones[i] = NULL;
864
// Create a new empty zone map line in the zone map.
866
UBYTE * gwNewZoneLine(UDWORD Line,UDWORD Size)
868
ASSERT( Line < (UDWORD)gwMapHeight(),"gwNewZoneLine : Invalid line requested" );
869
ASSERT( apRLEZones != NULL,"gwNewZoneLine : NULL Zone map" );
871
if(apRLEZones[Line] != NULL) {
872
free(apRLEZones[Line]);
875
apRLEZones[Line] = (UBYTE*)malloc(Size);
876
if (apRLEZones[Line] == NULL)
878
debug( LOG_ERROR, "gwNewZoneLine: Out of memory" );
883
return apRLEZones[Line];
887
// Create a NULL zone map for when there is no zone info loaded
888
BOOL gwCreateNULLZoneMap(void)
898
for(y=0; y<gwMapHeight(); y++)
900
pBuf = gwNewZoneLine(y, 2);
905
pBuf[0] = (UBYTE)gwMapWidth();
913
// release the RLE Zone map
914
void gwFreeZoneMap(void)
920
for(i=0; i<gwMapHeight(); i++)
930
// Look up the zone for a coordinate
931
SDWORD gwGetZone(SDWORD x, SDWORD y)
933
ASSERT( x >= 0 && x < gwMapWidth() && y >= 0 && y < gwMapHeight(), "gwGetZone: invalid coordinates" );
935
if ( x >= 0 && x < gwMapWidth() && y >= 0 && y < gwMapHeight() )
937
SDWORD xPos = 0, zone = 0, rlePos = 0;
941
xPos += apRLEZones[y][rlePos];
942
zone = apRLEZones[y][rlePos + 1];
944
} while (xPos <= x); // xPos is where the next zone starts
955
/******************************************************************************************************/
956
/* Zone equivalence data access functions */
959
// create an empty equivalence table
960
BOOL gwNewEquivTable(SDWORD numZones)
964
ASSERT( numZones < UBYTE_MAX,
965
"gwNewEquivTable: invalid number of zones" );
967
gwNumZones = numZones;
968
aNumEquiv = (UBYTE*)malloc(sizeof(UBYTE) * numZones);
969
if (aNumEquiv == NULL)
971
debug( LOG_ERROR, "gwNewEquivTable: out of memory" );
975
for(i=0; i<numZones; i+=1)
980
apEquivZones = (UBYTE**)malloc(sizeof(UBYTE *) * numZones);
981
if (apEquivZones == NULL)
983
debug( LOG_ERROR, "gwNewEquivTable: out of memory" );
987
for(i=0; i<numZones; i+=1)
989
apEquivZones[i] = NULL;
995
// release the equivalence table
996
void gwFreeEquivTable(void)
1008
for(i=0; i<gwNumZones; i+=1)
1010
if (apEquivZones[i])
1012
free(apEquivZones[i]);
1016
apEquivZones = NULL;
1022
// set the zone equivalence for a zone
1023
BOOL gwSetZoneEquiv(SDWORD zone, SDWORD numEquiv, UBYTE *pEquiv)
1027
ASSERT( aNumEquiv != NULL && apEquivZones != NULL,
1028
"gwSetZoneEquiv: equivalence arrays not initialised" );
1029
ASSERT( zone < gwNumZones,
1030
"gwSetZoneEquiv: invalid zone" );
1031
ASSERT( numEquiv <= gwNumZones,
1032
"gwSetZoneEquiv: invalid number of zone equivalents" );
1034
apEquivZones[zone] = (UBYTE*)malloc(sizeof(UBYTE) * numEquiv);
1035
if (apEquivZones[zone] == NULL)
1037
debug( LOG_ERROR, "gwSetZoneEquiv: out of memory" );
1042
aNumEquiv[zone] = (UBYTE)numEquiv;
1043
for(i=0; i<numEquiv; i+=1)
1045
apEquivZones[zone][i] = pEquiv[i];
1052
/******************************************************************************************************/
1053
/* Gateway data access functions */
1055
// get the size of the map
1056
SDWORD gwMapWidth(void)
1058
return (SDWORD)mapWidth;
1061
SDWORD gwMapHeight(void)
1063
return (SDWORD)mapHeight;
1066
// set the gateway flag on a tile
1067
void gwSetGatewayFlag(SDWORD x, SDWORD y)
1069
mapTile((UDWORD)x,(UDWORD)y)->tileInfoBits |= BITS_GATEWAY;
1072
// clear the gateway flag on a tile
1073
void gwClearGatewayFlag(SDWORD x, SDWORD y)
1075
mapTile((UDWORD)x,(UDWORD)y)->tileInfoBits &= ~BITS_GATEWAY;
1078
// check whether a tile is water
1079
BOOL gwTileIsWater(UDWORD x, UDWORD y)
1081
return terrainType(mapTile(x ,y)) == TER_WATER;
1084
// see if a zone is reachable
1085
BOOL gwZoneReachable(SDWORD zone)
1087
ASSERT( zone >= 0 && zone < gwNumZones,
1088
"gwZoneReachable: invalid zone" );
1090
return aZoneReachable[zone];