Logo Search packages:      
Sourcecode: warzone2100 version File versions

gateway.c

/*
      This file is part of Warzone 2100.
      Copyright (C) 1999-2004  Eidos Interactive
      Copyright (C) 2005-2007  Warzone Resurrection Project

      Warzone 2100 is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      Warzone 2100 is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with Warzone 2100; if not, write to the Free Software
      Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * Gateway.c
 *
 * Routing gateway code.
 *
 */

#include "lib/framework/frame.h"

#include "lib/framework/listmacs.h"
#include "map.h"
#include "wrappers.h"

#include "gateway.h"
#include "astar.h"
#include "fpath.h"

// the list of gateways on the current map
GATEWAY           *psGateways;

// the RLE map zones for each tile
UBYTE       **apRLEZones;

// the number of map zones
SDWORD            gwNumZones;

// The zone equivalence tables - shows which land zones
// border on a water zone
UBYTE       *aNumEquiv;
UBYTE       **apEquivZones;

// note which zones have a gateway to them and can therefore be reached
UBYTE       *aZoneReachable;


// Initialise the gateway system
BOOL gwInitialise(void)
{
      ASSERT( psGateways == NULL, "gwInitialise: gateway list has not been reset" );

      psGateways = NULL;

      return true;
}


// Shutdown the gateway system
void gwShutDown(void)
{
      GATEWAY *psNext;

      while (psGateways != NULL)
      {
            psNext = psGateways->psNext;
            gwFreeGateway(psGateways);
            psGateways = psNext;
      }

      gwFreeZoneMap();
      gwFreeEquivTable();

      if (aZoneReachable != NULL)
      {
            free(aZoneReachable);
            aZoneReachable = NULL;
      }
}


// Add a gateway to the system
BOOL gwNewGateway(SDWORD x1, SDWORD y1, SDWORD x2, SDWORD y2)
{
      GATEWAY           *psNew;
      SDWORD            pos, temp;

      if ((x1 < 0) || (x1 >= gwMapWidth())  ||
            (y1 < 0) || (y1 >= gwMapHeight()) ||
            (x2 < 0) || (x2 >= gwMapWidth())  ||
            (y2 < 0) || (y2 >= gwMapHeight()) ||
            ((x1 != x2) && (y1 != y2)))
      {
            ASSERT( false,"gwNewGateway: invalid coordinates" );
            return false;
      }

      psNew = (GATEWAY*)malloc(sizeof(GATEWAY));
      if (!psNew)
      {
            debug( LOG_ERROR, "gwNewGateway: out of memory" );
            abort();
            return false;
      }

      // make sure the first coordinate is always the smallest
      if (x2 < x1)
      {
            // y is the same, swap x
            temp = x2;
            x2 = x1;
            x1 = temp;
      }
      else if (y2 < y1)
      {
            // x is the same, swap y
            temp = y2;
            y2 = y1;
            y1 = temp;
      }

      // initialise the gateway
      psNew->x1 = (UBYTE)x1;
      psNew->y1 = (UBYTE)y1;
      psNew->x2 = (UBYTE)x2;
      psNew->y2 = (UBYTE)y2;
      psNew->zone1 = 0;
      psNew->zone2 = 0;
      psNew->psLinks = NULL;
      psNew->flags = 0;

      // add the gateway to the list
      psNew->psNext = psGateways;
      psGateways = psNew;

      // set the map flags
      if (x1 == x2)
      {
            // vertical gateway
            for(pos = y1; pos <= y2; pos++)
            {
                  gwSetGatewayFlag(x1, pos);
            }
      }
      else
      {
            // horizontal gateway
            for(pos = x1; pos <= x2; pos++)
            {
                  gwSetGatewayFlag(pos, y1);
            }
      }

      return true;
}


// Add a land/water link gateway to the system
BOOL gwNewLinkGateway(SDWORD x, SDWORD y)
{
      GATEWAY           *psNew;

      if ((x < 0) || (x >= gwMapWidth())  ||
            (y < 0) || (y >= gwMapHeight()))
      {
            ASSERT( false,"gwNewLinkGateway: invalid coordinates" );
            return false;
      }

      psNew = (GATEWAY*)malloc(sizeof(GATEWAY));
      if (!psNew)
      {
            debug( LOG_ERROR, "gwNewGateway: out of memory" );
            abort();
            return false;
      }

      // initialise the gateway
      psNew->x1 = (UBYTE)x;
      psNew->y1 = (UBYTE)y;
      psNew->x2 = (UBYTE)x;
      psNew->y2 = (UBYTE)y;
      psNew->zone1 = 0;
      psNew->zone2 = 0;
      psNew->psLinks = NULL;
      psNew->flags = GWR_WATERLINK;

      // add the gateway to the list
      psNew->psNext = psGateways;
      psGateways = psNew;

      return true;
}


static BOOL gwBlockingTile(SDWORD x,SDWORD y)
{
      MAPTILE     *psTile;

      if (x <1 || y < 1 || x >= (SDWORD)mapWidth-1 || y >= (SDWORD)mapHeight-1)
      {
            // coords off map - auto blocking tile
            return true;
      }

      psTile = mapTile((UDWORD)x, (UDWORD)y);
      if (terrainType(psTile) == TER_CLIFFFACE)
      {
            return true;
      }

      return false;
}


// scan for a particular zone on the map
// given a start point
static BOOL gwFindZone(SDWORD zone, SDWORD cx, SDWORD cy,
                                 SDWORD *px, SDWORD *py)
{
      SDWORD      x,y, dist, maxDist;

      maxDist = gwMapWidth() > gwMapHeight() ? gwMapWidth() : gwMapHeight();

      for(dist = 0; dist < maxDist; dist += 1)
      {
            // scan accross the top
            y = cy - dist;
            for(x = cx - dist; x <= cx + dist; x += 1)
            {
                  if (x >= 0 && x < gwMapWidth() &&
                        y >= 0 && y < gwMapHeight() &&
                        gwGetZone(x,y) == zone)
                  {
                        *px = x;
                        *py = y;
                        return true;
                  }
            }

            // scan down the left
            x = cx - dist;
            for(y = cy - dist; y <= cy + dist; y += 1)
            {
                  if (x >= 0 && x < gwMapWidth() &&
                        y >= 0 && y < gwMapHeight() &&
                        gwGetZone(x,y) == zone)
                  {
                        *px = x;
                        *py = y;
                        return true;
                  }
            }

            // scan down the right
            x = cx + dist;
            for(y = cy - dist; y <= cy + dist; y += 1)
            {
                  if (x >= 0 && x < gwMapWidth() &&
                        y >= 0 && y < gwMapHeight() &&
                        gwGetZone(x,y) == zone)
                  {
                        *px = x;
                        *py = y;
                        return true;
                  }
            }

            // scan accross the bottom
            y = cy + dist;
            for(x = cx - dist; x <= cx + dist; x += 1)
            {
                  if (x >= 0 && x < gwMapWidth() &&
                        y >= 0 && y < gwMapHeight() &&
                        gwGetZone(x,y) == zone)
                  {
                        *px = x;
                        *py = y;
                        return true;
                  }
            }
      }

      return false;
}


// find a rough center position for a zone
static void gwCalcZoneCenter(SDWORD zone, SDWORD *px, SDWORD *py)
{
      SDWORD            xsum,ysum, numtiles;
      SDWORD            x,y;

      xsum = ysum = numtiles = 0;
      for(y=0; y<gwMapHeight(); y+= 1)
      {
            for(x=0; x<gwMapWidth(); x+= 1)
            {
                  if (gwGetZone(x,y) == zone)
                  {
                        xsum += x;
                        ysum += y;
                        numtiles += 1;
                  }
            }
      }

      ASSERT( numtiles != 0,
            "gwCalcZoneCenter: zone not found on map" );

      x = xsum / numtiles;
      y = ysum / numtiles;

      if (!gwFindZone(zone, x,y, px,py))
      {
            *px = x;
            *py = y;
      }
}


// check all the zones are of reasonable sizes
void gwCheckZoneSizes(void)
{
      SDWORD            zone, xsum,ysum, numtiles, inzone;
      SDWORD            x,y, cx,cy;

      for(zone=1; zone < UBYTE_MAX; zone += 1)
      {
            xsum = ysum = numtiles = inzone = 0;
            for(y=0; y<gwMapHeight(); y+= 1)
            {
                  for(x=0; x<gwMapWidth(); x+= 1)
                  {
                        if (gwGetZone(x,y) == zone)
                        {
                              xsum += x;
                              ysum += y;
                              numtiles += 1;
                              if (!gwBlockingTile(x,y))
                              {
                                    inzone += 1;
                              }
                        }
                  }
            }

            if (numtiles > 0)
            {
                  x = xsum / numtiles;
                  y = ysum / numtiles;

                  if (!gwFindZone(zone, x,y, &cx,&cy))
                  {
                        cx = x;
                        cy = y;
                  }

                  if (inzone > FPATH_LOOP_LIMIT)
                  {
                        debug(LOG_GATEWAY, "gwCheckZoneSizes: warning zone %d at (%d,%d) is too large %d tiles (max %d)",
                              zone, cx, cy, inzone, FPATH_LOOP_LIMIT);
                  }
            }
      }
}


// add the land/water link gateways
BOOL gwGenerateLinkGates(void)
{
      SDWORD            zone, cx,cy;

      ASSERT( apEquivZones != NULL,
            "gwGenerateLinkGates: no zone equivalence table" );

      debug( LOG_NEVER, "Generating water link Gateways...." );

      for(zone=1; zone<gwNumZones; zone += 1)
      {

            if (aNumEquiv[zone] > 0)
            {
                  LOADBARCALLBACK();      //                loadingScreenCallback();

                  // got a water zone that borders on land
                  // find it's center
                  gwCalcZoneCenter(zone, &cx,&cy);
                  if (!gwNewLinkGateway(cx,cy))
                  {
                        return false;
                  }
                  debug(LOG_GATEWAY, "new water link gateway at (%d,%d) for zone %d", cx, cy, zone);
            }
      }

      debug( LOG_NEVER, "Done\n" );

      return true;
}


// Return the number of gateways.
UDWORD gwNumGateways(void)
{
      GATEWAY           *psCurr;
      UDWORD NumGateways = 0;

      for(psCurr = psGateways; psCurr; psCurr = psCurr->psNext)
      {
            NumGateways++;
      }

      return NumGateways;
}


GATEWAY *gwGetGateways(void)
{
      return psGateways;
}


// Release a gateway
void gwFreeGateway(GATEWAY *psDel)
{
      SDWORD      pos;

      LIST_REMOVE(psGateways, psDel, GATEWAY);

      if (psMapTiles) // this lines fixes the bug where we were closing the gateways after freeing the map
      {
            // clear the map flags
            if (psDel->x1 == psDel->x2)
            {
                  // vertical gateway
                  for(pos = psDel->y1; pos <= psDel->y2; pos++)
                  {
                        gwClearGatewayFlag(psDel->x1, pos);
                  }
            }
            else
            {
                  // horizontal gateway
                  for(pos = psDel->x1; pos <= psDel->x2; pos++)
                  {
                        gwClearGatewayFlag(pos, psDel->y1);
                  }
            }

      }

      if (psDel->psLinks != NULL)
      {
            free(psDel->psLinks);
      }
      free(psDel);
}


// load a gateway list
BOOL gwLoadGateways(char *pFileBuffer, UDWORD fileSize)
{
      SDWORD      numGW, x1,y1, x2,y2;
      char  *pPos;

      // get the number of gateways
      pPos = pFileBuffer;
      sscanf((char *)pPos, "%d", &numGW);
      for (; *pPos != '\n' && pPos < (pFileBuffer + fileSize); pPos += 1)
            ;
      pPos += 1;

      while ((pPos < (pFileBuffer + fileSize)) && (numGW > 0))
      {
            sscanf((char *)pPos, "%d %d %d %d", &x1,&y1, &x2, &y2);

            if (!gwNewGateway(x1,y1, x2,y2))
            {
                  return false;
            }

            for (; *pPos != '\n' && pPos < (pFileBuffer + fileSize); pPos += 1)
                  ;
            pPos += 1;
            numGW -= 1;
      }

      return true;
}


// check if a zone is in the equivalence table for a water zone
BOOL gwZoneInEquiv(SDWORD mainZone, SDWORD checkZone)
{
      SDWORD i;

      if (apEquivZones == NULL)
      {
            return false;
      }

      for(i=0; i<aNumEquiv[mainZone]; i+= 1)
      {
            if (apEquivZones[mainZone][i] == checkZone)
            {
                  return true;
            }
      }

      return false;
}

// find a route between two gateways and return
// its length
static SDWORD gwRouteLength(GATEWAY *psStart, GATEWAY *psEnd)
{
      SDWORD                  ret, sx,sy, ex,ey, xdiff,ydiff, i;
      ASTAR_ROUTE       sRoute;
      SDWORD                  routeMode, dist;
#ifdef DEBUG
      SDWORD                  zone;
#endif

      fpathBlockingTile = gwBlockingTile;

      sx = (psStart->x1 + psStart->x2)/2;
      sy = (psStart->y1 + psStart->y2)/2;
      ex = (psEnd->x1 + psEnd->x2)/2;
      ey = (psEnd->y1 + psEnd->y2)/2;

      // force the router to finish a route
      routeMode = ASR_NEWROUTE;
      sRoute.asPos[0].x = -1;
      sRoute.asPos[0].y = -1;
      do
      {
            astarResetCounters();
            sRoute.numPoints = 0;
            ret = fpathAStarRoute(routeMode, &sRoute,
                              world_coord(sx), world_coord(sy),
                              world_coord(ex), world_coord(ey));
            if (ret == ASR_PARTIAL)
            {
                  routeMode = ASR_CONTINUE;
            }
      } while (ret == ASR_PARTIAL);

      ASSERT( ret != ASR_FAILED,
            "gwRouteLength: no route between gateways at (%d,%d) and (%d,%d)",
            sx,sy, ex,ey );

#ifdef DEBUG
      if (ret == ASR_NEAREST)
      {
            zone = (psStart->zone1 == psEnd->zone1) || (psStart->zone1 == psEnd->zone2) ? psStart->zone1 : psStart->zone2;
            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 );
      }
#endif

      // calculate the length of the route
      dist = 0;
      for(i=0; i < sRoute.numPoints; i+= 1)
      {
            xdiff = sx - sRoute.asPos[i].x;
            ydiff = sy - sRoute.asPos[i].y;
            dist += (SDWORD)sqrtf(xdiff*xdiff + ydiff*ydiff);
            sx = sRoute.asPos[i].x;
            sy = sRoute.asPos[i].y;
      }
      xdiff = sx - ex;
      ydiff = sy - ey;
      dist += (SDWORD)sqrtf(xdiff*xdiff + ydiff*ydiff);

      fpathBlockingTile = fpathGroundBlockingTile;

      return dist;
}


// check that the initial flood fill tiles are not on a blocking tile
static BOOL gwCheckFloodTiles(GATEWAY *psGate)
{
      SDWORD      floodX,floodY;

      // first zone is left/above
      if (psGate->x1 == psGate->x2)
      {
            // vertical - go left
            floodX = psGate->x1 - 1;
            floodY = (psGate->y2 - psGate->y1)/2 + psGate->y1;
      }
      else
      {
            // horizontal - go above
            floodX = (psGate->x2 - psGate->x1)/2 + psGate->x1;
            floodY = psGate->y1 - 1;
      }

      if (gwBlockingTile(floodX,floodY))
      {
            return false;
      }

      // second zone is right/below
      if (psGate->x1 == psGate->x2)
      {
            // vertical - go right
            floodX = psGate->x1 + 1;
            floodY = (psGate->y2 - psGate->y1)/2 + psGate->y1;
      }
      else
      {
            // horizontal - go below
            floodX = (psGate->x2 - psGate->x1)/2 + psGate->x1;
            floodY = psGate->y1 + 1;
      }

      if (gwBlockingTile(floodX,floodY))
      {
            return false;
      }

      return true;
}


// link all the gateways together
BOOL gwLinkGateways(void)
{
      GATEWAY           *psCurr, *psLink;
      SDWORD            x,y, gwX,gwY, zone1Links,zone2Links, link, zone, otherZone;
      SDWORD            zoneLinks;
      BOOL        bZone1, bAddLink;


      // note which zones have a gateway
      aZoneReachable = (UBYTE*)malloc( sizeof(UBYTE) * gwNumZones );
      if (aZoneReachable == NULL)
      {
            debug( LOG_ERROR, "gwLinkGateways: out of memory" );
            abort();
            return false;
      }
      memset(aZoneReachable, 0, sizeof(UBYTE) * gwNumZones);

      // initialise the zones for the gateways
      for(psCurr = psGateways; psCurr; psCurr = psCurr->psNext)
      {
            // a gateway is always in it's own zone1
            psCurr->zone1 = (UBYTE)gwGetZone(psCurr->x1,psCurr->y1);

            if (psCurr->flags & GWR_WATERLINK)
            {
                  // a water link gateway is only in one zone
                  x = psCurr->x1;
                  y = psCurr->y1;
            }
            else if (psCurr->x1 == psCurr->x2)
            {
                  // vertical - go right
                  x = psCurr->x1 + 1;
                  y = (psCurr->y2 - psCurr->y1)/2 + psCurr->y1;
            }
            else
            {
                  // horizontal - go below
                  x = (psCurr->x2 - psCurr->x1)/2 + psCurr->x1;
                  y = psCurr->y1 + 1;
            }
            psCurr->zone2 = (UBYTE)gwGetZone(x,y);

            ASSERT( (psCurr->flags & GWR_WATERLINK) || gwCheckFloodTiles(psCurr),
                  "gwLinkGateways: Gateway at (%d,%d)->(%d,%d) is too close to a blocking tile. Zones %d, %d",
                  psCurr->x1,psCurr->y1, psCurr->x2,psCurr->y2,
                  psCurr->zone1, psCurr->zone2 );

            aZoneReachable[psCurr->zone1] = true;
            aZoneReachable[psCurr->zone2] = true;
      }

      // now link all the gateways together
      for(psCurr = psGateways; psCurr; psCurr = psCurr->psNext)
      {
            LOADBARCALLBACK();      //          loadingScreenCallback();

            gwX = (psCurr->x1 + psCurr->x2)/2;
            gwY = (psCurr->y1 + psCurr->y2)/2;

            // count the number of links
            zone1Links = 0;
            zone2Links = 0;
            for(psLink=psGateways; psLink; psLink=psLink->psNext)
            {
                  if (psLink == psCurr)
                  {
                        // don't link a gateway to itself
                        continue;
                  }
                  if ((psLink->zone1 == psCurr->zone1) || (psLink->zone2 == psCurr->zone1) ||
                        ((psLink->flags & GWR_WATERLINK) &&
                         gwZoneInEquiv(psLink->zone1, psCurr->zone1) &&
                         !gwZoneInEquiv(psLink->zone1, psCurr->zone2) ))
                  {
                        zone1Links += 1;
                  }
                  if (psCurr->flags & GWR_WATERLINK)
                  {
                        // calculating links for a water link gateway
                        if (gwZoneInEquiv(psCurr->zone1, psLink->zone1) ||
                              gwZoneInEquiv(psCurr->zone1, psLink->zone2))
                        {
                              zone2Links += 1;
                        }
                  }
                  else if ((psLink->zone1 == psCurr->zone2) || (psLink->zone2 == psCurr->zone2) ||
                        ((psLink->flags & GWR_WATERLINK) &&
                         gwZoneInEquiv(psLink->zone1, psCurr->zone2) &&
                         !gwZoneInEquiv(psLink->zone1, psCurr->zone1) ))
                  {
                        zone2Links += 1;
                  }
            }
            if (zone1Links+zone2Links > 0)
            {
                  psCurr->psLinks = (GATEWAY_LINK*)malloc(sizeof(GATEWAY_LINK) * (zone1Links+zone2Links));
                  if (psCurr->psLinks == NULL)
                  {
                        debug( LOG_ERROR, "gwLinkGateways: out of memory" );
                        abort();
                        return false;
                  }
            }
            else
            {
                  psCurr->psLinks = NULL;
            }
            psCurr->zone1Links = (UBYTE)zone1Links;
            psCurr->zone2Links = (UBYTE)zone2Links;

            // generate the links starting with all those through zone1
            link = 0;
            zone = psCurr->zone1;
            otherZone = psCurr->zone2;
            zoneLinks = zone1Links;
            bZone1 = true;
            while (link < (zone1Links + zone2Links))
            {
                  for(psLink=psGateways; psLink && (link < zoneLinks); psLink=psLink->psNext)
                  {
                        if (psLink == psCurr)
                        {
                              // don't link a gateway to itself
                              continue;
                        }
                        bAddLink = false;
                        if (!bZone1 && (psCurr->flags & GWR_WATERLINK))
                        {
                              // calculating links for a water link gateway
                              if (gwZoneInEquiv(psCurr->zone1, psLink->zone1) ||
                                    gwZoneInEquiv(psCurr->zone1, psLink->zone2))
                              {
                                    bAddLink = true;
                              }
                        }
                        else if ((psLink->zone1 == zone) || (psLink->zone2 == zone) ||
                                     ((psLink->flags & GWR_WATERLINK) &&
                                      gwZoneInEquiv(psLink->zone1, zone) &&
                                      !gwZoneInEquiv(psLink->zone1, otherZone) ))
                        {
                              bAddLink = true;
                        }

                        if (bAddLink)
                        {
//                            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 );
                              psCurr->psLinks[link].psGateway = psLink;
                              psCurr->psLinks[link].flags = 0;

                              psCurr->psLinks[link].dist = (SWORD)gwRouteLength(psCurr, psLink);

                              link += 1;
                        }
                  }

                  // found all the links to zone1, now do it for zone2
                  zone = psCurr->zone2;
                  otherZone = psCurr->zone1;
                  zoneLinks = zone1Links + zone2Links;
                  bZone1 = false;
            }
      }

      return true;
}


/******************************************************************************************************/
/*                            RLE Zone data access functions                                          */


// Get number of zone lines.
UDWORD gwNumZoneLines(void)
{
      return gwMapHeight();
}


// Get the size of a zone line.
UDWORD gwZoneLineSize(UDWORD Line)
{
      UBYTE *pCode;
      UDWORD pos = 0;
      UDWORD x = 0;

      ASSERT( Line < (UDWORD)gwMapHeight(),"gwNewZoneLine : Invalid line requested" );
      ASSERT( apRLEZones != NULL,"gwNewZoneLine : NULL Zone map" );

      pCode = apRLEZones[Line];

      while (x < (UDWORD)gwMapWidth()) {
            x += pCode[pos];
            pos += 2;
      }

      return pos;
}


// Create a new empty zone map but don't allocate the actual zones yet.
//
BOOL gwNewZoneMap(void)
{
      UWORD i;

      if (apRLEZones != NULL)
      {
            gwFreeZoneMap();
      }

      apRLEZones = (UBYTE**)malloc(sizeof(UBYTE *) * gwMapHeight());
      if (apRLEZones == NULL)
      {
            debug( LOG_ERROR, "gwNewZoneMap: Out of memory" );
            abort();
            return false;
      }

      for(i=0; i< gwMapHeight(); i++)
      {
            apRLEZones[i] = NULL;
      }

      return true;
}

// Create a new empty zone map line in the zone map.
//
UBYTE * gwNewZoneLine(UDWORD Line,UDWORD Size)
{
      ASSERT( Line < (UDWORD)gwMapHeight(),"gwNewZoneLine : Invalid line requested" );
      ASSERT( apRLEZones != NULL,"gwNewZoneLine : NULL Zone map" );

      if(apRLEZones[Line] != NULL) {
            free(apRLEZones[Line]);
      }

      apRLEZones[Line] = (UBYTE*)malloc(Size);
      if (apRLEZones[Line] == NULL)
      {
            debug( LOG_ERROR, "gwNewZoneLine: Out of memory" );
            abort();
            return NULL;
      }

      return apRLEZones[Line];
}


// Create a NULL zone map for when there is no zone info loaded
BOOL gwCreateNULLZoneMap(void)
{
      SDWORD      y;
      UBYTE *pBuf;

      if (!gwNewZoneMap())
      {
            return false;
      }

      for(y=0; y<gwMapHeight(); y++)
      {
            pBuf = gwNewZoneLine(y, 2);
            if (!pBuf)
            {
                  return false;
            }
            pBuf[0] = (UBYTE)gwMapWidth();
            pBuf[1] = 0;
      }

      return true;
}


// release the RLE Zone map
void gwFreeZoneMap(void)
{
      SDWORD      i;

      if (apRLEZones)
      {
            for(i=0; i<gwMapHeight(); i++)
            {
                  free(apRLEZones[i]);
            }
            free(apRLEZones);
            apRLEZones = NULL;
      }
}


// Look up the zone for a coordinate
SDWORD gwGetZone(SDWORD x, SDWORD y)
{
      ASSERT( x >= 0 && x < gwMapWidth() && y >= 0 && y < gwMapHeight(), "gwGetZone: invalid coordinates" );

      if ( x >= 0 && x < gwMapWidth() && y >= 0 && y < gwMapHeight() )
      {
            SDWORD xPos = 0, zone = 0, rlePos = 0;

            do
            {
                  xPos += apRLEZones[y][rlePos];
                  zone  = apRLEZones[y][rlePos + 1];
                  rlePos += 2;
            } while (xPos <= x); // xPos is where the next zone starts

            return zone;
      }
      else
      {
            return 0;
      }
}


/******************************************************************************************************/
/*                   Zone equivalence data access functions                                           */


// create an empty equivalence table
BOOL gwNewEquivTable(SDWORD numZones)
{
      SDWORD      i;

      ASSERT( numZones < UBYTE_MAX,
            "gwNewEquivTable: invalid number of zones" );

      gwNumZones = numZones;
      aNumEquiv = (UBYTE*)malloc(sizeof(UBYTE) * numZones);
      if (aNumEquiv == NULL)
      {
            debug( LOG_ERROR, "gwNewEquivTable: out of memory" );
            abort();
            return false;
      }
      for(i=0; i<numZones; i+=1)
      {
            aNumEquiv[i] = 0;
      }

      apEquivZones = (UBYTE**)malloc(sizeof(UBYTE *) * numZones);
      if (apEquivZones == NULL)
      {
            debug( LOG_ERROR, "gwNewEquivTable: out of memory" );
            abort();
            return false;
      }
      for(i=0; i<numZones; i+=1)
      {
            apEquivZones[i] = NULL;
      }

      return true;
}

// release the equivalence table
void gwFreeEquivTable(void)
{
      SDWORD i;

      if (aNumEquiv)
      {
            free(aNumEquiv);
            aNumEquiv = NULL;
      }

      if (apEquivZones)
      {
            for(i=0; i<gwNumZones; i+=1)
            {
                  if (apEquivZones[i])
                  {
                        free(apEquivZones[i]);
                  }
            }
            free(apEquivZones);
            apEquivZones = NULL;
      }
      gwNumZones = 0;
}


// set the zone equivalence for a zone
BOOL gwSetZoneEquiv(SDWORD zone, SDWORD numEquiv, UBYTE *pEquiv)
{
      SDWORD i;

      ASSERT( aNumEquiv != NULL && apEquivZones != NULL,
            "gwSetZoneEquiv: equivalence arrays not initialised" );
      ASSERT( zone < gwNumZones,
            "gwSetZoneEquiv: invalid zone" );
      ASSERT( numEquiv <= gwNumZones,
            "gwSetZoneEquiv: invalid number of zone equivalents" );

      apEquivZones[zone] = (UBYTE*)malloc(sizeof(UBYTE) * numEquiv);
      if (apEquivZones[zone] == NULL)
      {
            debug( LOG_ERROR, "gwSetZoneEquiv: out of memory" );
            abort();
            return false;
      }

      aNumEquiv[zone] = (UBYTE)numEquiv;
      for(i=0; i<numEquiv; i+=1)
      {
            apEquivZones[zone][i] = pEquiv[i];
      }

      return true;
}


/******************************************************************************************************/
/*                   Gateway data access functions                                                    */

// get the size of the map
SDWORD gwMapWidth(void)
{
      return (SDWORD)mapWidth;
}

SDWORD gwMapHeight(void)
{
      return (SDWORD)mapHeight;
}

// set the gateway flag on a tile
void gwSetGatewayFlag(SDWORD x, SDWORD y)
{
      mapTile((UDWORD)x,(UDWORD)y)->tileInfoBits |= BITS_GATEWAY;
}

// clear the gateway flag on a tile
void gwClearGatewayFlag(SDWORD x, SDWORD y)
{
      mapTile((UDWORD)x,(UDWORD)y)->tileInfoBits &= ~BITS_GATEWAY;
}

// check whether a tile is water
BOOL gwTileIsWater(UDWORD x, UDWORD y)
{
      return terrainType(mapTile(x ,y)) == TER_WATER;
}

// see if a zone is reachable
BOOL gwZoneReachable(SDWORD zone)
{
      ASSERT( zone >= 0 && zone < gwNumZones,
            "gwZoneReachable: invalid zone" );

      return aZoneReachable[zone];
}

Generated by  Doxygen 1.6.0   Back to index