cleaned up autoroute code

svn-id: r9318
This commit is contained in:
Robert Göffringmann 2003-07-31 02:09:24 +00:00
parent e5343ac038
commit c9f060573f
3 changed files with 207 additions and 321 deletions

View File

@ -26,354 +26,238 @@
#define ROUTE_GRID_SIZE (ROUTE_GRID_WIDTH*ROUTE_GRID_HEIGHT*2)
#define WALK_JUMP 8 // walk in blocks of 8
const int16 SkyAutoRoute::_routeDirections[4] = { -1, 1, -ROUTE_GRID_WIDTH, ROUTE_GRID_WIDTH };
const uint16 SkyAutoRoute::_logicCommands[4] = { RIGHTY, LEFTY, DOWNY, UPY };
SkyAutoRoute::SkyAutoRoute(SkyGrid *pGrid) {
_grid = pGrid;
_routeGrid = (uint16 *)malloc(ROUTE_GRID_SIZE);
_routeBuf = (uint16 *)malloc(ROUTE_SPACE);
}
SkyAutoRoute::~SkyAutoRoute(void) {
free(_routeGrid);
free(_routeBuf);
}
uint16 SkyAutoRoute::checkBlock(uint16 *blockPos) {
uint16 fieldVal, retVal = 0xFFFF;
fieldVal = blockPos[1]; // field to the right
if ((!(fieldVal & 0x8000)) && (fieldVal != 0)) retVal = fieldVal;
fieldVal = (blockPos - 1)[0]; // field to the left
if ((!(fieldVal & 0x8000)) && (fieldVal != 0)) {
if ((fieldVal < retVal) || (retVal == 0xFFFF)) retVal = fieldVal;
}
fieldVal = (blockPos + ROUTE_GRID_WIDTH)[0]; // upper field
if ((!(fieldVal & 0x8000)) && (fieldVal != 0)) {
if ((fieldVal < retVal) || (retVal == 0xFFFF)) retVal = fieldVal;
}
fieldVal = (blockPos - ROUTE_GRID_WIDTH)[0]; // upper field
if ((!(fieldVal & 0x8000)) && (fieldVal != 0)) {
if ((fieldVal < retVal) || (retVal == 0xFFFF)) retVal = fieldVal;
uint16 retVal = 0xFFFF;
for (uint8 cnt = 0; cnt < 4; cnt++) {
uint16 fieldVal = *(blockPos + _routeDirections[cnt]);
if (fieldVal && (fieldVal < retVal))
retVal = fieldVal;
}
return retVal;
}
#undef ARDEBUG
uint16 SkyAutoRoute::autoRoute(Compact *cpt, uint16 **pSaveRoute) {
if (!cpt->extCompact)
error("SkyAutoRoute::autoRoute: fatal error. cpt->extCompact == NULL");
uint16* routeData = (uint16 *)cpt->extCompact->animScratch;
uint8* screenGrid = _grid->giveGrid(cpt->screen);
screenGrid += GRID_SIZE-4; // all arrays are processed from behind, so make
// screenGrid point to the last element of our grid.
uint16 *routeCalc = _routeGrid + (ROUTE_GRID_SIZE >> 1) - ROUTE_GRID_WIDTH - 2;
uint8 stretch1, stretch2; // bl / bh
stretch1 = 0;
MegaSet *mega = SkyCompact::getMegaSet(cpt, cpt->extCompact->megaSet);
stretch2 = (uint8)(mega->gridWidth & 0xff);
uint16 cnt;
//First clear the bottom line and right hand edge of next line
for (cnt = 0; cnt < ROUTE_GRID_WIDTH + 1; cnt++)
_routeGrid[(ROUTE_GRID_SIZE >> 1) - 1 - cnt] = 0;
uint16 gridCntX = ROUTE_GRID_WIDTH - 2; // ch
uint16 gridCntY = ROUTE_GRID_HEIGHT - 2; // ebp
uint16 bitsLeft = 32;
uint32 gridData = screenGrid[0] | (screenGrid[1] << 8) |
(screenGrid[2] << 16) | (screenGrid[3] << 24);
screenGrid -= 4;
do {
//stretch:
uint8 shiftBit = (uint8)gridData&1;
gridData >>= 1;
if (shiftBit) {
//bit_set:
routeCalc[0] = 0xFFFF;
stretch1 = stretch2; // set up stretch factor
} else {
if (stretch1) {
//still_stretching:
stretch1--;
routeCalc[0] = 0xFFFF;
} else {
routeCalc[0] = 0; // this block is free
}
}
// next_stretch:
routeCalc--;
bitsLeft--;
// still bits:
gridCntX--;
if (gridCntX == 0) {
routeCalc--;
routeCalc[0] = routeCalc[1] = 0; // do edges
routeCalc--;
gridCntX = ROUTE_GRID_WIDTH - 2;
stretch1 = 0; // clear stretch factor
gridCntY--;
}
if (gridCntY && (!bitsLeft)) {
gridData = screenGrid[0] | (screenGrid[1] << 8) |
(screenGrid[2] << 16) | (screenGrid[3] << 24);
screenGrid -= 4;
bitsLeft = 32;
}
} while(gridCntY);
for (cnt = 0; cnt < ROUTE_GRID_WIDTH - 1; cnt++)
_routeGrid[cnt] = 0; // clear top line (right hand edge already done
// the grid has been initialised
// calculate start and end points
int16 initX = 0, initY = 0, postX = 0, postY = 0;
uint8 initBlockY; // bh
uint8 initBlockX; // bl
uint8 postBlockY; // ch
uint8 postBlockX; // cl
if (cpt->ycood < TOP_LEFT_Y) {
initY = cpt->ycood - TOP_LEFT_Y;
initBlockY = 0;
} else if (cpt->ycood - TOP_LEFT_Y >= GAME_SCREEN_HEIGHT) { // no_init_y1
initY = cpt->ycood - TOP_LEFT_Y - GAME_SCREEN_HEIGHT;
initBlockY = (GAME_SCREEN_HEIGHT - 1) >> 3; // convert to blocks
} else { // no_init_y2
initBlockY = (cpt->ycood - TOP_LEFT_Y) >> 3; // convert to blocks
}
if (cpt->xcood < TOP_LEFT_X) {
initX = cpt->xcood - TOP_LEFT_X;
initBlockX = 0;
} else if (cpt->xcood - TOP_LEFT_X >= GAME_SCREEN_WIDTH) { // no_init_x1
initX = cpt->xcood - TOP_LEFT_X - (GAME_SCREEN_WIDTH - 1); // -1 to match amiga
initBlockX = (GAME_SCREEN_WIDTH - 1) >> 3;
} else { // no_init_x2
initBlockX = (cpt->xcood - TOP_LEFT_X) >> 3;
}
// destination coords:
if (cpt->extCompact->arTargetY < TOP_LEFT_Y) {
postY = cpt->extCompact->arTargetY - TOP_LEFT_Y;
postBlockY = 0;
} else if (cpt->extCompact->arTargetY - TOP_LEFT_Y >= GAME_SCREEN_HEIGHT) { // no_post_y1
postY = cpt->extCompact->arTargetY - TOP_LEFT_Y - (GAME_SCREEN_HEIGHT - 1);
postBlockY = (GAME_SCREEN_HEIGHT - 1) >> 3;
} else { // no_post_y2
postBlockY = (cpt->extCompact->arTargetY - TOP_LEFT_Y) >> 3;
}
if (cpt->extCompact->arTargetX < TOP_LEFT_X) {
postX = cpt->extCompact->arTargetX - TOP_LEFT_X;
postBlockX = 0;
} else if (cpt->extCompact->arTargetX - TOP_LEFT_X >= GAME_SCREEN_WIDTH) {
postX = cpt->extCompact->arTargetX - TOP_LEFT_X - (GAME_SCREEN_WIDTH - 1);
postBlockX = (GAME_SCREEN_WIDTH - 1) >> 3;
void SkyAutoRoute::clipCoordX(uint16 x, uint8 &blkX, int16 &initX) {
if (x < TOP_LEFT_X) {
blkX = 0;
initX = x - TOP_LEFT_X;
} else if (x >= TOP_LEFT_X + GAME_SCREEN_WIDTH) {
blkX = (GAME_SCREEN_WIDTH - 1) >> 3;
initX = x - (TOP_LEFT_X + GAME_SCREEN_WIDTH);
} else {
postBlockX = (cpt->extCompact->arTargetX - TOP_LEFT_X) >> 3;
}
if ((postBlockX == initBlockX) && (postBlockY == initBlockY)) {
// empty route
routeData[0] = 0;
return 1;
blkX = (x - TOP_LEFT_X) >> 3;
initX = 0;
}
}
int32 directionX, directionY;
uint8 numLines, numCols; // number of lines / columns to go
if (initBlockY > postBlockY) {
directionY = -ROUTE_GRID_WIDTH;
numLines = initBlockY;
} else { // go_down:
directionY = ROUTE_GRID_WIDTH;
numLines = (ROUTE_GRID_HEIGHT-1)-initBlockY;
void SkyAutoRoute::clipCoordY(uint16 y, uint8 &blkY, int16 &initY) {
if (y < TOP_LEFT_Y) {
blkY = 0;
initY = y - TOP_LEFT_Y;
} else if (y >= TOP_LEFT_Y + GAME_SCREEN_HEIGHT) {
blkY = (GAME_SCREEN_HEIGHT - 1) >> 3;
initY = y - (TOP_LEFT_Y + GAME_SCREEN_WIDTH);
} else {
blkY = (y - TOP_LEFT_Y) >> 3;
initY = 0;
}
if (initBlockX > postBlockX) {
}
void SkyAutoRoute::initWalkGrid(uint8 screen, uint8 width) {
uint16 *wGridPos;
uint8 stretch = 0;
uint8 *screenGrid = _grid->giveGrid(screen);
screenGrid += GRID_SIZE;
wGridPos = _routeGrid + (ROUTE_GRID_SIZE >> 1) - ROUTE_GRID_WIDTH - 2;
memset(_routeGrid, 0, ROUTE_GRID_SIZE);
uint8 bitsLeft = 0; uint32 gridData = 0;
for (uint8 gridCntY = 0; gridCntY < ROUTE_GRID_HEIGHT - 2; gridCntY++) {
for (uint8 gridCntX = 0; gridCntX < ROUTE_GRID_WIDTH - 2; gridCntX++) {
if (!bitsLeft) {
screenGrid -= 4;
gridData = READ_LE_UINT32(screenGrid);
bitsLeft = 32;
}
if (gridData & 1) {
*wGridPos = 0xFFFF; // block is not accessible
stretch = width;
} else if (stretch) {
*wGridPos = 0xFFFF;
stretch--;
}
wGridPos--;
bitsLeft--;
gridData >>= 1;
}
wGridPos -= 2;
stretch = 0;
}
}
bool SkyAutoRoute::calcWalkGrid(uint8 startX, uint8 startY, uint8 destX, uint8 destY) {
int16 directionX, directionY;
uint8 roiX, roiY; // Rectangle Of Interest in the walk grid
if (startY > destY) {
directionY = -ROUTE_GRID_WIDTH;
roiY = startY;
} else {
directionY = ROUTE_GRID_WIDTH;
roiY = (ROUTE_GRID_HEIGHT-1) - startY;
}
if (startX > destX) {
directionX = -1;
numCols = initBlockX+2;
roiX = startX + 2;
} else {
directionX = 1;
numCols = (ROUTE_GRID_WIDTH - 1) - initBlockX;
roiX = (ROUTE_GRID_WIDTH - 1) - startX;
}
// calculate destination address
uint16 *routeDestCalc;
routeDestCalc = (postBlockY + 1) * ROUTE_GRID_WIDTH + postBlockX + 1 + _routeGrid;
uint16 *routeSrcCalc;
routeSrcCalc = (initBlockY + 1) * ROUTE_GRID_WIDTH + initBlockX + 1 + _routeGrid;
routeSrcCalc[0] = 1; //start this one off
// means: mark the block we start from as accessible
#ifdef ARDEBUG
uint16 dcnt1, dcnt2;
for (dcnt1 = 0; dcnt1 < ROUTE_GRID_HEIGHT; dcnt1++) {
for (dcnt2 = 0; dcnt2 < ROUTE_GRID_WIDTH; dcnt2++) {
if (!_routeGrid[dcnt1*ROUTE_GRID_WIDTH + dcnt2]) printf("_");
else if (_routeGrid[dcnt1*ROUTE_GRID_WIDTH + dcnt2] == 1) printf("S");
else printf("X");
}
printf("\n");
}
getchar();
#endif
uint16 *walkDest = _routeGrid + (destY + 1) * ROUTE_GRID_WIDTH + destX + 1;
uint16 *walkStart = _routeGrid + (startY + 1) * ROUTE_GRID_WIDTH + startX + 1;
*walkStart = 1;
uint16 *walkPos = walkStart;
// if we are on the edge, move diagonally from start
if (numLines < ROUTE_GRID_HEIGHT-3)
routeSrcCalc -= directionY;
if (roiY < ROUTE_GRID_HEIGHT-3)
walkStart -= directionY;
if (numCols < ROUTE_GRID_WIDTH-2)
routeSrcCalc -= directionX;
if (roiX < ROUTE_GRID_WIDTH-2)
walkStart -= directionX;
bool gridChanged = true;
bool foundRoute = false;
if (routeDestCalc[0]) {
// If destination is a wall then we have no route
// By the way, we could improve this algorithm by moving as close to the
// wall as possible. The original pathfinding of SKY sucked, if I remember correctly
return 2;
}
uint8 cnty; // ch
uint8 cntx; // cl
// numLines = dh, numCols = dl
uint16 blockRet;
bool gridChanged, foundRoute;
do { // wallow_y
while ((!foundRoute) && gridChanged) {
gridChanged = false;
cnty = numLines;
uint16 *yPushedSrc = routeSrcCalc;
do { // wallow_x
cntx = numCols;
uint16 *xPushedSrc = routeSrcCalc;
do { // wallow
if (!routeSrcCalc[0]) {
// block wasn't yet done
blockRet = checkBlock(routeSrcCalc);
if (blockRet != 0xFFFF) {
// this block is accessible
routeSrcCalc[0] = blockRet+1;
uint16 *yWalkCalc = walkStart;
for (uint8 cnty = 0; cnty < roiY; cnty++) {
uint16 *xWalkCalc = yWalkCalc;
for (uint8 cntx = 0; cntx < roiX; cntx++) {
if (!*xWalkCalc) { // block wasn't done, yet
uint16 blockRet = checkBlock(xWalkCalc);
if (blockRet < 0xFFFF) {
*xWalkCalc = blockRet + 1;
gridChanged = true;
}
}
routeSrcCalc += directionX;
cntx--;
} while (cntx);
routeSrcCalc = xPushedSrc + directionY;
cnty--;
} while (cnty);
routeSrcCalc = yPushedSrc;
foundRoute = false;
if (!routeDestCalc[0]) {
// we have done a section, see if we want to shift backwards (what?)
if (numLines < ROUTE_GRID_HEIGHT - 4) {
routeSrcCalc -= directionY;
numLines++;
xWalkCalc += directionX;
}
if (numCols < ROUTE_GRID_WIDTH - 4) {
routeSrcCalc -= directionX;
numCols++;
}
} else foundRoute = true;
} while ((!foundRoute) && gridChanged);
#ifdef ARDEBUG
for (dcnt1 = 0; dcnt1 < ROUTE_GRID_HEIGHT; dcnt1++) {
for (dcnt2 = 0; dcnt2 < ROUTE_GRID_WIDTH; dcnt2++) {
printf(" %02X",_routeGrid[dcnt1*ROUTE_GRID_WIDTH + dcnt2]&0xFF);
yWalkCalc += directionY;
}
printf("\n");
}
#endif
if (!routeDestCalc[0]) {
// no route exists from routeSrc to routeDest
return 2;
}
// ok, we know now that it's possible to get from the start position to the desired
// destination. Let's see how.
uint16 *saveRoute = routeData + (ROUTE_SPACE >> 1) - 1; // route_space is given in bytes so >> 1
saveRoute[0] = 0; // route is null terminated
uint16 lastVal;
lastVal = routeDestCalc[0];
lastVal--;
bool routeDone = false;
do {
// check_dir:
if (lastVal == (routeDestCalc-1)[0]) {
// look_left
saveRoute -= 2;
saveRoute[1] = RIGHTY;
saveRoute[0] = 0;
while ((lastVal == (routeDestCalc-1)[0]) && (!routeDone)) {
routeDestCalc--; // keep checking left
saveRoute[0] += WALK_JUMP;
#ifdef ARDEBUG
printf("left\n");
#endif
lastVal--;
if (lastVal == 0) routeDone = true;
if (*walkDest) { // okay, finished
foundRoute = true;
} else { // we couldn't find the route, let's extend the ROI
if (roiY < ROUTE_GRID_HEIGHT - 4) {
walkStart -= directionY;
roiY++;
}
} else if (lastVal == routeDestCalc[1]) {
// look_right
saveRoute -= 2;
saveRoute[1] = LEFTY;
saveRoute[0] = 0;
while ((lastVal == routeDestCalc[1]) && (!routeDone)) {
routeDestCalc++; // keep checking right
saveRoute[0] += WALK_JUMP;
#ifdef ARDEBUG
printf("right\n");
#endif
lastVal--;
if (lastVal == 0) routeDone = true;
if (roiX < ROUTE_GRID_WIDTH - 4) {
walkStart -= directionX;
roiX++;
}
} else if (lastVal == (routeDestCalc - ROUTE_GRID_WIDTH)[0]) {
// look_up
saveRoute -= 2;
saveRoute[1] = DOWNY;
saveRoute[0] = 0;
while ((lastVal == (routeDestCalc - ROUTE_GRID_WIDTH)[0]) && (!routeDone)) {
routeDestCalc -= ROUTE_GRID_WIDTH; // keep checking up
saveRoute[0] += WALK_JUMP;
#ifdef ARDEBUG
printf("up\n");
#endif
lastVal--;
if (lastVal == 0) routeDone = true;
}
} else if (lastVal == (routeDestCalc + ROUTE_GRID_WIDTH)[0]) {
// look_down
saveRoute -= 2;
saveRoute[1] = UPY;
saveRoute[0] = 0;
while ((lastVal == (routeDestCalc + ROUTE_GRID_WIDTH)[0]) && (!routeDone)) {
routeDestCalc += ROUTE_GRID_WIDTH; // keep checking right
saveRoute[0] += WALK_JUMP;
#ifdef ARDEBUG
printf("down\n");
#endif
lastVal--;
if (lastVal == 0) routeDone = true;
}
} else {
error("AutoRoute:: Can't find way backwards through _routeGrid");
}
} while (!routeDone);
#ifdef ARDEBUG
getchar();
#endif
// the route is done. if there was an initial x/y movement tag it onto the start
if (initX < 0) {
saveRoute -= 2;
saveRoute[1] = RIGHTY;
saveRoute[0] = ((-initX) + 7) & 0xFFF8;
} else if (initX > 0) {
saveRoute -= 2;
saveRoute[1] = LEFTY;
saveRoute[0] = (initX + 7) & 0xFFF8;
}
// I wonder why initY isn't checked
// saveRoute should now point to routeData
if (routeData > saveRoute) error("Autoroute: Internal pointer error! routeData overflow.");
*pSaveRoute = saveRoute;
return 1;
return foundRoute;
}
uint16 *SkyAutoRoute::makeRouteData(uint8 startX, uint8 startY, uint8 destX, uint8 destY) {
memset(_routeBuf, 0, ROUTE_SPACE);
uint16 *routePos = _routeGrid + (destY + 1) * ROUTE_GRID_WIDTH + destX + 1;
uint16 *dataTrg = _routeBuf + (ROUTE_SPACE >> 1) - 2;
uint16 lastVal = (*routePos) - 1;
while (lastVal) { // lastVal == 0 means route is done.
dataTrg -= 2;
int16 walkDirection = 0;
for (uint8 cnt = 0; cnt < 4; cnt++)
if (lastVal == *(routePos + _routeDirections[cnt])) {
*(dataTrg + 1) = _logicCommands[cnt];
walkDirection = _routeDirections[cnt];
break;
}
if (!walkDirection)
error("makeRouteData:: can't find way through walkGrid (pos %d)", lastVal);
while (lastVal && (lastVal == *(routePos + walkDirection))) {
*dataTrg += WALK_JUMP;
lastVal--;
routePos += walkDirection;
}
}
return dataTrg;
}
uint16 *SkyAutoRoute::checkInitMove(uint16 *data, int16 initStaX) {
if (initStaX < 0) {
data -= 2;
*(data + 1) = RIGHTY;
*data = ((-initStaX) + 7) & 0xFFF8;
} else if (initStaX > 0) {
data -= 2;
*(data + 1) = LEFTY;
*data = (initStaX + 7) & 0xFFF8;
}
return data;
}
uint16 SkyAutoRoute::autoRoute(Compact *cpt) {
uint8 cptScreen = (uint8)cpt->screen;
uint8 cptWidth = (uint8)SkyCompact::getMegaSet(cpt, cpt->extCompact->megaSet)->gridWidth;
initWalkGrid(cptScreen, cptWidth);
uint8 startX, startY, destX, destY;
int16 initStaX, initStaY, initDestX, initDestY;
clipCoordX(cpt->xcood, startX, initStaX);
clipCoordY(cpt->ycood, startY, initStaY);
clipCoordX(cpt->extCompact->arTargetX, destX, initDestX);
clipCoordY(cpt->extCompact->arTargetY, destY, initDestY);
((uint16*)cpt->extCompact->animScratch)[0] = 0;
if ((startX == destX) && (startY == destY))
return 1;
if (_routeGrid[(destY + 1) * ROUTE_GRID_WIDTH + destX + 1])
return 2; // AR destination is an unaccessible block
if (!calcWalkGrid(startX, startY, destX, destY))
return 2; // can't find route to block
uint16 *routeData = makeRouteData(startX, startY, destX, destY);
// the route is done.
// if there was an initial x movement (due to clipping) tag it onto the start
routeData = checkInitMove(routeData, initStaX);
uint8 cnt = 0;
do {
((uint16*)cpt->extCompact->animScratch)[cnt] = routeData[cnt];
((uint16*)cpt->extCompact->animScratch)[cnt + 1] = routeData[cnt + 1];
cnt += 2;
} while (routeData[cnt - 2]);
return 0;
}

View File

@ -35,11 +35,20 @@ class SkyAutoRoute {
public:
SkyAutoRoute(SkyGrid *pGrid);
~SkyAutoRoute(void);
uint16 autoRoute(Compact *cpt, uint16 **pSaveRoute);
uint16 autoRoute(Compact *cpt);
private:
uint16 checkBlock(uint16 *blockPos);
void clipCoordX(uint16 x, uint8 &blkX, int16 &initX);
void clipCoordY(uint16 y, uint8 &blkY, int16 &initY);
void initWalkGrid(uint8 screen, uint8 width);
bool calcWalkGrid(uint8 startX, uint8 startY, uint8 destX, uint8 destY);
uint16 *makeRouteData(uint8 startX, uint8 startY, uint8 destX, uint8 destY);
uint16 *checkInitMove(uint16 *data, int16 initStaX);
SkyGrid *_grid;
uint16 *_routeGrid;
uint16 *_routeBuf;
static const int16 _routeDirections[4];
static const uint16 _logicCommands[4];
};
#endif // AUTOROUTE_H

View File

@ -144,20 +144,13 @@ void SkyLogic::logicScript() {
}
void SkyLogic::autoRoute() {
uint16 *route = 0;
uint16 ret = _skyAutoRoute->autoRoute(_compact, &route);
_compact->downFlag = _skyAutoRoute->autoRoute(_compact);
if (!_compact->downFlag) // route ok
_compact->grafixProg = (uint16*)_compact->extCompact->animScratch;
_compact->logic = L_SCRIPT; // continue the script
if (ret != 1) // route failed
_compact->downFlag = 1; // return fail to script
else if (!route) // zero route
_compact->downFlag = 2; // return fail to script
else {
_compact->grafixProg = route; // put graphic prog in
_compact->downFlag = 0; // route ok
}
logicScript();
return;
}