bool UnitBase::isInAttackRange(const ObjectBase* pObject) const { int checkRange; switch(attackMode) { case GUARD: { checkRange = getWeaponRange(); } break; case AREAGUARD: { checkRange = getAreaGuardRange() + getWeaponRange() + 1; } break; case AMBUSH: { checkRange = getViewRange() + 1; } break; case HUNT: { return true; } break; case STOP: default: { return false; } break; } if(getItemID() == Unit_Sandworm) { checkRange = getViewRange() + 1; } return (blockDistance(guardPoint*TILESIZE + Coord(TILESIZE/2, TILESIZE/2), pObject->getCenterPoint()) <= checkRange*TILESIZE); }
bool UnitBase::isInWeaponRange(const ObjectBase* object) const { if(object == NULL) { return false; } Coord targetLocation = target.getObjPointer()->getClosestPoint(location); return (blockDistance(location, targetLocation) <= getWeaponRange()); }
float closestBlockDistance(UPoint objectLocation, UPoint targetLocation, UPoint size) { UPoint closestPoint; //find the closest cell of a structure from a location if (objectLocation.x <= targetLocation.x) //if we are left of the structure closestPoint.x = targetLocation.x; //set destination, left most point else if (objectLocation.x >= (targetLocation.x + size.x-1)) //vica versa closestPoint.x = targetLocation.x + size.x-1; else closestPoint.x = objectLocation.x; //we are above or below at least on cell of the structure, closest path is straight //same deal but with y if (objectLocation.y <= targetLocation.y) closestPoint.y = targetLocation.y; else if (objectLocation.y >= (targetLocation.y + size.y-1)) closestPoint.y = targetLocation.y + size.y-1; else closestPoint.y = objectLocation.y; return blockDistance(objectLocation, closestPoint); }
void UnitBase::engageTarget() { if(target && (target.getObjPointer() == NULL)) { // the target does not exist anymore releaseTarget(); return; } if(target && (target.getObjPointer()->isActive() == false)) { // the target changed its state to inactive releaseTarget(); return; } if(target && !targetFriendly && !canAttack(target.getObjPointer())) { // the (non-friendly) target cannot be attacked anymore releaseTarget(); return; } if(target && !targetFriendly && !forced && !isInAttackRange(target.getObjPointer())) { // the (non-friendly) target left the attack mode range (and we were not forced to attack it) releaseTarget(); return; } if(target) { // we have a target unit or structure Coord targetLocation = target.getObjPointer()->getClosestPoint(location); if(destination != targetLocation) { // the location of the target has moved // => recalculate path clearPath(); } targetDistance = blockDistance(location, targetLocation); Sint8 newTargetAngle = lround(8.0f/256.0f*destinationAngle(location, targetLocation)); if(newTargetAngle == 8) { newTargetAngle = 0; } if(bFollow) { // we are following someone setDestination(targetLocation); return; } if(targetDistance > getWeaponRange()) { // we are not in attack range // => follow the target setDestination(targetLocation); return; } // we are in attack range if(targetFriendly && !forced) { // the target is friendly and we only attack these if were forced to do so return; } if(goingToRepairYard) { // we are going to the repair yard // => we do not need to change the destination targetAngle = INVALID; } else if(attackMode == CAPTURE) { // we want to capture the target building setDestination(targetLocation); targetAngle = INVALID; } else if(isTracked() && target.getObjPointer()->isInfantry() && currentGameMap->tileExists(targetLocation) && !currentGameMap->getTile(targetLocation)->isMountain() && forced) { // we squash the infantry unit because we are forced to setDestination(targetLocation); targetAngle = INVALID; } else { // we decide to fire on the target thus we can stop moving setDestination(location); targetAngle = newTargetAngle; } if(getCurrentAttackAngle() == newTargetAngle) { attack(); } } else if(attackPos) { // we attack a position targetDistance = blockDistance(location, attackPos); Sint8 newTargetAngle = lround(8.0f/256.0f*destinationAngle(location, attackPos)); if(newTargetAngle == 8) { newTargetAngle = 0; } if(targetDistance <= getWeaponRange()) { // we are in weapon range thus we can stop moving setDestination(location); targetAngle = newTargetAngle; if(getCurrentAttackAngle() == newTargetAngle) { attack(); } } else { targetAngle = INVALID; } } }
AStarSearch::AStarSearch(MapClass* pMap, UnitClass* pUnit, Coord start, Coord destination) { sizeX = pMap->sizeX; sizeY = pMap->sizeY; mapData = (TileData*) calloc(sizeX*sizeY, sizeof(TileData)); if(mapData == NULL) { throw std::bad_alloc(); } float heuristic = blockDistance(start, destination); float smallestHeuristic = heuristic; bestCoord = Coord(INVALID_POS,INVALID_POS); //if the unit is not directly next to its destination or it is and the destination is unblocked if ((heuristic > 1.5) || (pUnit->canPass(destination.x, destination.y) == true)) { putOnOpenListIfBetter(start, Coord(INVALID_POS, INVALID_POS), 0.0, heuristic); std::vector<short> depthCheckCount(std::min(sizeX, sizeY)); int numNodesChecked = 0; while(openList.empty() == false) { Coord currentCoord = extractMin(); if (MapData(currentCoord).h < smallestHeuristic) { smallestHeuristic = MapData(currentCoord).h; bestCoord = currentCoord; if(currentCoord == destination) { // destination found break; } } if (numNodesChecked < MAX_NODES_CHECKED) { //push a node for each direction we could go for (int angle=0; angle<=7; angle++) { Coord nextCoord = pMap->getMapPos(angle, currentCoord); if(pUnit->canPass(nextCoord.x, nextCoord.y)) { TerrainClass& nextTerrainTile = pMap->cell[nextCoord.x][nextCoord.y]; float g = MapData(currentCoord).g; if((nextCoord.x != currentCoord.x) && (nextCoord.y != currentCoord.y)) { //add diagonal movement cost g += DIAGONALCOST*(pUnit->isAFlyingUnit() ? 1.0 : nextTerrainTile.getDifficulty()); } else { g += (pUnit->isAFlyingUnit() ? 1.0 : nextTerrainTile.getDifficulty()); } if(pUnit->isAFlyingUnit()) { int numDirectionChanges = abs((pUnit->getAngle() - angle)%NUM_ANGLES); double distanceFromStart = blockDistance(start, nextCoord); if(numDirectionChanges > 2) { if(distanceFromStart <= DIAGONALCOST) { g += 3.0; } else if(distanceFromStart <= 2*DIAGONALCOST) { g += 2.0; } } else if(numDirectionChanges > 1) { if(distanceFromStart <= DIAGONALCOST) { g += 3.0; } else if(distanceFromStart <= 2*DIAGONALCOST) { g += 2.0; } } } if(MapData(currentCoord).parentCoord.x != INVALID_POS) { //add cost of turning time int posAngle = currentGameMap->getPosAngle(MapData(currentCoord).parentCoord, currentCoord); if (posAngle != angle) g += (1.0/currentGame->objectData.data[pUnit->getItemID()].turnspeed * (double)std::min(abs(angle - posAngle), NUM_ANGLES - std::max(angle, posAngle) + std::min(angle, posAngle)))/((double)BLOCKSIZE); } float h = blockDistance(nextCoord, destination); if(MapData(nextCoord).bClosed == false) { putOnOpenListIfBetter(nextCoord, currentCoord, g, h); } } } } if (MapData(currentCoord).bClosed == false) { int depth = std::max(abs(currentCoord.x - destination.x), abs(currentCoord.y - destination.y)); if(depth < std::min(sizeX,sizeY)) { // calculate maximum number of cells in a square shape // you could look at without success around a destination x,y // with a specific k distance before knowing that it is // imposible to get to the destination. Each time the astar // algorithm pushes a node with a max diff of k, // depthcheckcount(k) is incremented, if it reaches the // value in depthcheckmax(x,y,k), we know we have done a full // square around target, and thus it is impossible to reach // the target, so we should try and get closer if possible, // but otherwise stop // // Examples on 6x4 map: // // ...... // ..###. - k=1 => 3x3 Square // ..# #. - (x,y)=(3,2) => Square completely inside map // ..###. => depthcheckmax(3,2,1) = 8 // // .#.... // ##.... - k=1 => 3x3 Square // ...... - (x,y)=(0,0) => Square only partly inside map // ...... => depthcheckmax(0,0,1) = 3 // // ...#.. // ...#.. - k=2 => 5x5 Square // ...#.. - (x,y)=(0,1) => Square only partly inside map // ####.. => depthcheckmax(0,1,2) = 7 int x = destination.x; int y = destination.y; int k = depth; int horizontal = std::min(sizeX-1, x+(k-1)) - std::max(0, x-(k-1)) + 1; int vertical = std::min(sizeY-1, y+k) - std::max(0, y-k) + 1; int depthCheckMax = ((x-k >= 0) ? vertical : 0) + ((x+k < sizeX) ? vertical : 0) + ((y-k >= 0) ? horizontal : 0) + ((y+k < sizeY) ? horizontal : 0); if (++depthCheckCount[k] >= depthCheckMax) { // we have searched a whole square around destination, it can't be reached break; } } MapData(currentCoord).bClosed = true; numNodesChecked++; } } } }