예제 #1
0
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);
}
예제 #2
0
bool UnitBase::isInWeaponRange(const ObjectBase* object) const {
    if(object == NULL) {
        return false;
    }

    Coord targetLocation = target.getObjPointer()->getClosestPoint(location);

    return (blockDistance(location, targetLocation) <= getWeaponRange());
}
예제 #3
0
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);
}
예제 #4
0
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++;
            }
        }

	}


}