BWAPI::Unitset WorkerData::getMineralPatchesNearDepot(BWAPI::Unit depot) { // if there are minerals near the depot, add them to the set BWAPI::Unitset mineralsNearDepot; int radius = 300; for (auto & unit : BWAPI::Broodwar->getAllUnits()) { if ((unit->getType() == BWAPI::UnitTypes::Resource_Mineral_Field) && unit->getDistance(depot) < radius) { mineralsNearDepot.insert(unit); } } // if we didn't find any, use the whole map if (mineralsNearDepot.empty()) { for (auto & unit : BWAPI::Broodwar->getAllUnits()) { if ((unit->getType() == BWAPI::UnitTypes::Resource_Mineral_Field)) { mineralsNearDepot.insert(unit); } } } return mineralsNearDepot; }
BWAPI::Position CombatCommander::getMainAttackLocation() { BWTA::BaseLocation * enemyBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->enemy()); // First choice: Attack an enemy region if we can see units inside it if (enemyBaseLocation) { BWAPI::Position enemyBasePosition = enemyBaseLocation->getPosition(); // get all known enemy units in the area BWAPI::Unitset enemyUnitsInArea; MapGrid::Instance().GetUnits(enemyUnitsInArea, enemyBasePosition, 800, false, true); bool onlyOverlords = true; for (auto & unit : enemyUnitsInArea) { if (unit->getType() != BWAPI::UnitTypes::Zerg_Overlord) { onlyOverlords = false; } } if (!BWAPI::Broodwar->isExplored(BWAPI::TilePosition(enemyBasePosition)) || !enemyUnitsInArea.empty()) { if (!onlyOverlords) { return enemyBaseLocation->getPosition(); } } } // Second choice: Attack known enemy buildings for (const auto & kv : InformationManager::Instance().getUnitInfo(BWAPI::Broodwar->enemy())) { const UnitInfo & ui = kv.second; if (ui.type.isBuilding() && ui.lastPosition != BWAPI::Positions::None) { return ui.lastPosition; } } // Third choice: Attack visible enemy units that aren't overlords for (auto & unit : BWAPI::Broodwar->enemy()->getUnits()) { if (unit->getType() == BWAPI::UnitTypes::Zerg_Overlord) { continue; } if (UnitUtil::IsValidUnit(unit) && unit->isVisible()) { return unit->getPosition(); } } // Fourth choice: We can't see anything so explore the map attacking along the way return MapGrid::Instance().getLeastExplored(); }
// get a target for the zealot to attack BWAPI::Unit TankManager::getTarget(BWAPI::Unit tank, const BWAPI::Unitset & targets) { int bestPriorityDistance = 1000000; int bestPriority = 0; double bestLTD = 0; BWAPI::Unit bestTargetThreatInRange = nullptr; double bestTargetThreatInRangeLTD = 0; int highPriority = 0; double closestDist = std::numeric_limits<double>::infinity(); BWAPI::Unit closestTarget = nullptr; int siegeTankRange = BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode.groundWeapon().maxRange() - 32; BWAPI::Unitset targetsInSiegeRange; for (auto & target : targets) { if (target->getDistance(tank) < siegeTankRange && UnitUtil::CanAttack(tank, target)) { targetsInSiegeRange.insert(target); } } const BWAPI::Unitset & newTargets = targetsInSiegeRange.empty() ? targets : targetsInSiegeRange; // check first for units that are in range of our attack that can cause damage // choose the highest priority one from them at the lowest health for (const auto & target : newTargets) { if (!UnitUtil::CanAttack(tank, target)) { continue; } double distance = tank->getDistance(target); double LTD = UnitUtil::CalculateLTD(target, tank); int priority = getAttackPriority(tank, target); bool targetIsThreat = LTD > 0; BWAPI::Broodwar->drawTextMap(target->getPosition(), "%d", priority); if (!closestTarget || (priority > highPriority) || (priority == highPriority && distance < closestDist)) { closestDist = distance; highPriority = priority; closestTarget = target; } } if (bestTargetThreatInRange) { return bestTargetThreatInRange; } return closestTarget; }
void InterceptorManager::assignTargetsOld(const BWAPI::Unitset & targets) { const BWAPI::Unitset & rangedUnits = getUnits(); // figure out targets BWAPI::Unitset rangedUnitTargets; for (auto & target : targets) { // conditions for targeting if (!(target->getType() == BWAPI::UnitTypes::Zerg_Larva) && !(target->getType() == BWAPI::UnitTypes::Zerg_Egg) && !(target->getType() == BWAPI::UnitTypes::Buildings) && (target->isTargetable()) && target->isVisible() && target->getType() != BWAPI::UnitTypes::Resource_Vespene_Geyser) { rangedUnitTargets.insert(target); } } auto attacker2target = assignEnemy(rangedUnits, rangedUnitTargets); BWAPI::Position shome = BWAPI::Position(BWTA::getStartLocation(BWAPI::Broodwar->self())->getPosition()); for (auto & rangedUnit : rangedUnits) { // train sub units such as scarabs or interceptors //trainSubUnits(rangedUnit); // if the order is to attack or defend if (order.getType() == SquadOrderTypes::Attack || order.getType() == SquadOrderTypes::Defend) { // if there are targets if (!rangedUnitTargets.empty()) { // find the best target for this zealot auto targetIdx = attacker2target.find(rangedUnit); BWAPI::Unit target = targetIdx == attacker2target.end() ? getTarget(rangedUnit, rangedUnitTargets) : targetIdx->first; if (target && Config::Debug::DrawUnitTargetInfo) { BWAPI::Broodwar->drawLineMap(rangedUnit->getPosition(), rangedUnit->getTargetPosition(), BWAPI::Colors::Purple); } Micro::SmartAttackUnit(rangedUnit, target); } // if there are no targets else { // if we're not near the order position if (rangedUnit->getDistance(order.getPosition()) > 100) { // move to it Micro::SmartAttackMove(rangedUnit, order.getPosition()); } } } } }
void ECGStarcraftManager::move(Message* message, bool* blocking) { UnitDescriptor commandedUnits = message->readCommandedUnit(); BWAPI::Position landmark = message->readLandmark(); Region region = message->readRegion(); BWAPI::Unitset movers = ECGUtil::resolveUnitDescriptor(commandedUnits); BWAPI::Position destination = ECGUtil::resolveLocation(region, landmark); if (!movers.empty()) movers.move(destination); // TODO: register boolean army event to make sure they moved if (blocking != nullptr) *blocking = false; }
void DevAIModule::onSendText(std::string text) { if (text == "/morph") { BWAPI::Unitset larvae = self->getUnits(); larvae.erase_if(Filter::GetType != UnitTypes::Zerg_Larva); if (!larvae.empty()) { if (!(*larvae.begin())->morph(UnitTypes::Zerg_Mutalisk)) { Broodwar << bw->getLastError() << ":" << self->incompleteUnitCount(UnitTypes::Zerg_Greater_Spire) << ":" << self->incompleteUnitCount(UnitTypes::Zerg_Spire) << std::endl; } } } Broodwar->sendText("%s", text.c_str()); }
// still has bug in it somewhere, use Old version void RangedManager::assignTargetsNew(const BWAPI::Unitset & targets) { const BWAPI::Unitset & rangedUnits = getUnits(); // figure out targets BWAPI::Unitset rangedUnitTargets; std::copy_if(targets.begin(), targets.end(), std::inserter(rangedUnitTargets, rangedUnitTargets.end()), [](BWAPI::Unit u){ return u->isVisible(); }); BWAPI::Unitset rangedUnitsToAssign(rangedUnits); std::map<BWAPI::Unit, int> attackersAssigned; for (auto & unit : rangedUnitTargets) { attackersAssigned[unit] = 0; } // keep assigning targets while we have attackers and targets remaining while (!rangedUnitsToAssign.empty() && !rangedUnitTargets.empty()) { auto attackerAssignment = findClosestUnitPair(rangedUnitsToAssign, rangedUnitTargets); BWAPI::Unit & attacker = attackerAssignment.first; BWAPI::Unit & target = attackerAssignment.second; UAB_ASSERT_WARNING(attacker, "We should have chosen an attacker!"); if (!attacker) { break; } if (!target) { Micro::SmartAttackMove(attacker, order.getPosition()); continue; } if (Config::Micro::KiteWithRangedUnits) { if (attacker->getType() == BWAPI::UnitTypes::Zerg_Mutalisk || attacker->getType() == BWAPI::UnitTypes::Terran_Vulture) { Micro::SmartAttackUnit(attacker, target); } else { //Micro::MutaDanceTarget(attacker, target); } } else { Micro::SmartAttackUnit(attacker, target); } // update the number of units assigned to attack the target we found int & assigned = attackersAssigned[attackerAssignment.second]; assigned++; // if it's a small / fast unit and there's more than 2 things attacking it already, don't assign more if ((target->getType().isWorker() || target->getType() == BWAPI::UnitTypes::Zerg_Zergling) && (assigned > 2)) { rangedUnitTargets.erase(target); } // if it's a building and there's more than 10 things assigned to it already, don't assign more else if (target->getType().isBuilding() && (assigned > 10)) { rangedUnitTargets.erase(target); } rangedUnitsToAssign.erase(attacker); } // if there's no targets left, attack move to the order destination if (rangedUnitTargets.empty()) { for (auto & unit : rangedUnitsToAssign) { if (unit->getDistance(order.getPosition()) > 100) { // move to it Micro::SmartAttackMove(unit, order.getPosition()); } } } }
void RangedManager::assignTargetsOld(const BWAPI::Unitset & targets) { const BWAPI::Unitset & rangedUnits = getUnits(); // figure out targets BWAPI::Unitset rangedUnitTargets; for (auto & target : targets) { // conditions for targeting if (!(target->getType() == BWAPI::UnitTypes::Zerg_Larva) && !(target->getType() == BWAPI::UnitTypes::Zerg_Egg) && !(target->getType() == BWAPI::UnitTypes::Buildings) && (target->isTargetable()) && target->isVisible() && target->getType() != BWAPI::UnitTypes::Resource_Vespene_Geyser) { rangedUnitTargets.insert(target); } } auto attacker2target = assignEnemy(rangedUnits, rangedUnitTargets); BWAPI::Position shome = BWAPI::Position(BWTA::getStartLocation(BWAPI::Broodwar->self())->getPosition()); BWAPI::Unitset _rangedUnits = rangedUnits; auto _rangedUnitTargets = rangedUnitTargets; int maxChoices = rangedUnits.size() / 3 + 1, i = 0; std::map<BWAPI::Unit, std::pair<BWAPI::Unit, BWAPI::Unit> > dodgeUnits; BWAPI::Unitset threatens; for (BWAPI::Unit threatenClosest = NULL, unitClosetEnemy = NULL;;) { if (i++ > maxChoices) break; double dist = BWAPI::Broodwar->self()->getUpgradeLevel(BWAPI::UpgradeTypes::Singularity_Charge)?96:96; double enerange = BWAPI::Broodwar->self()->getUpgradeLevel(BWAPI::UpgradeTypes::Singularity_Charge) ? 80 : 80; for (auto & rangedUnit : _rangedUnits) { for (auto target : _rangedUnitTargets) { if (target->getType().isWorker() || (target->getType().isBuilding())) continue; if (target->getType().groundWeapon().maxRange() <= enerange) { if (rangedUnit->getDistance(target) < dist) { threatenClosest = target; unitClosetEnemy = rangedUnit; dist = rangedUnit->getDistance(target); } } } } if (threatenClosest != NULL) { threatens.insert(threatenClosest); BWAPI::Broodwar->drawCircleMap(threatenClosest->getPosition(), 2, BWAPI::Colors::Red, true); auto vec1 = unitClosetEnemy->getPosition() - threatenClosest->getPosition(); dodgeUnits[unitClosetEnemy] = std::make_pair(unitClosetEnemy, threatenClosest); auto __rangedUnits = rangedUnits; for (auto rangedUnit:__rangedUnits) { if (rangedUnit->getDistance(unitClosetEnemy) > unitClosetEnemy->getDistance(threatenClosest)+64) continue; auto vec2 = rangedUnit->getPosition() - threatenClosest->getPosition(); int judge = (vec1.x*vec2.x + vec1.y*vec2.y)*(vec1.x*vec2.x + vec1.y*vec2.y); if (judge * 6 > (vec1.x*vec1.x + vec1.y*vec1.y)*(vec2.x*vec2.x + vec2.y*vec2.y) * 5) { dodgeUnits[rangedUnit] = std::make_pair(unitClosetEnemy, threatenClosest); _rangedUnits.erase(rangedUnit); } } } else break; } BWAPI::Unitset withoutBuildingsAndWorkers = rangedUnitTargets; auto _withoutBuildingsAndWorkers = withoutBuildingsAndWorkers; for (auto target : _withoutBuildingsAndWorkers) if (target->getType().isBuilding() || target->getType().isWorker()) withoutBuildingsAndWorkers.erase(target); for (auto & rangedUnit : rangedUnits) { // train sub units such as scarabs or interceptors //trainSubUnits(rangedUnit); // if the order is to attack or defend int chokeZealot = 0; for (const auto & zealot : BWAPI::Broodwar->self()->getUnits()) { // trivial case: unit which exists matches the type if (zealot->getType() != BWAPI::UnitTypes::Protoss_Zealot) continue; if (zealot->isCompleted() == false) continue; auto ckpt = BWTA::getNearestChokepoint(zealot->getPosition()); if (zealot->getDistance(rangedUnit->getPosition()) < 300) { if (ckpt && ckpt->getWidth()<256 && ckpt->getCenter().getDistance(zealot->getPosition()) < 128) chokeZealot += 3; else chokeZealot += 1; } } bool danceMode; if (order.getType() == SquadOrderTypes::Defend) danceMode = chokeZealot < 3 && rangedUnits.size()<20; else danceMode = chokeZealot < 3 && rangedUnits.size()<20; if (order.getType() == SquadOrderTypes::Attack || order.getType() == SquadOrderTypes::Defend) { // if there are targets if (!rangedUnitTargets.empty()) { // find the best target for this zealot auto targetIdx = attacker2target.find(rangedUnit); BWAPI::Unit target = targetIdx == attacker2target.end() ? getTarget(rangedUnit, rangedUnitTargets) : targetIdx->second; // attack it if (dodgeUnits.find(rangedUnit) == dodgeUnits.end()) { Micro::SmartAttackUnit(rangedUnit, target); } else { if (danceMode) Micro::SmartKiteTarget(rangedUnit, target, dodgeUnits[rangedUnit].second, withoutBuildingsAndWorkers); else Micro::SmartAttackUnit(rangedUnit, target); //modified //BWAPI::Broodwar->sendText("%s", "old kite"); //Micro::SmartKiteTarget(rangedUnit, dodgeUnits[rangedUnit].second); } } // if there are no targets else { Micro::SmartAttackMove(rangedUnit, order.getPosition()); } } } }
void CombatCommander::updateDefenseSquads() { if (_combatUnits.empty() || BWAPI::Broodwar->self()->allUnitCount(BWAPI::UnitTypes::Terran_Science_Vessel) == 1) { return; } BWTA::BaseLocation * enemyBaseLocation = InformationManager::Instance().getMainBaseLocation(BWAPI::Broodwar->enemy()); BWTA::Region * enemyRegion = nullptr; if (enemyBaseLocation) { enemyRegion = BWTA::getRegion(enemyBaseLocation->getPosition()); } // for each of our occupied regions for (BWTA::Region * myRegion : InformationManager::Instance().getOccupiedRegions(BWAPI::Broodwar->self())) { // don't defend inside the enemy region, this will end badly when we are stealing gas if (myRegion == enemyRegion) { continue; } BWAPI::Position regionCenter = myRegion->getCenter(); if (!regionCenter.isValid()) { continue; } // start off assuming all enemy units in region are just workers int numDefendersPerEnemyUnit = 2; // all of the enemy units in this region BWAPI::Unitset enemyUnitsInRegion; for (auto & unit : BWAPI::Broodwar->enemy()->getUnits()) { // if it's an overlord, don't worry about it for defense, we don't care what they see if (unit->getType() == BWAPI::UnitTypes::Zerg_Overlord) { continue; } if (BWTA::getRegion(BWAPI::TilePosition(unit->getPosition())) == myRegion) { enemyUnitsInRegion.insert(unit); } } // we can ignore the first enemy worker in our region since we assume it is a scout for (auto & unit : enemyUnitsInRegion) { if (unit->getType().isWorker()) { enemyUnitsInRegion.erase(unit); break; } } int numEnemyFlyingInRegion = std::count_if(enemyUnitsInRegion.begin(), enemyUnitsInRegion.end(), [](BWAPI::Unit u) { return u->isFlying(); }); int numEnemyGroundInRegion = std::count_if(enemyUnitsInRegion.begin(), enemyUnitsInRegion.end(), [](BWAPI::Unit u) { return !u->isFlying(); }); std::stringstream squadName; squadName << "Base Defense " << regionCenter.x << " " << regionCenter.y; // if there's nothing in this region to worry about if (enemyUnitsInRegion.empty()) { // if a defense squad for this region exists, remove it if (_squadData.squadExists(squadName.str())) { _squadData.getSquad(squadName.str()).clear(); } // and return, nothing to defend here continue; } else { // if we don't have a squad assigned to this region already, create one if (!_squadData.squadExists(squadName.str())) { SquadOrder defendRegion(SquadOrderTypes::Defend, regionCenter, 32 * 25, "Defend Region!"); _squadData.addSquad(squadName.str(), Squad(squadName.str(), defendRegion, BaseDefensePriority)); } } // assign units to the squad if (_squadData.squadExists(squadName.str())) { Squad & defenseSquad = _squadData.getSquad(squadName.str()); // figure out how many units we need on defense int flyingDefendersNeeded = numDefendersPerEnemyUnit * numEnemyFlyingInRegion; int groundDefensersNeeded = numDefendersPerEnemyUnit * numEnemyGroundInRegion; updateDefenseSquadUnits(defenseSquad, flyingDefendersNeeded, groundDefensersNeeded); } else { UAB_ASSERT_WARNING(false, "Squad should have existed: %s", squadName.str().c_str()); } } // for each of our defense squads, if there aren't any enemy units near the position, remove the squad std::set<std::string> uselessDefenseSquads; for (const auto & kv : _squadData.getSquads()) { const Squad & squad = kv.second; const SquadOrder & order = squad.getSquadOrder(); if (order.getType() != SquadOrderTypes::Defend) { continue; } bool enemyUnitInRange = false; for (auto & unit : BWAPI::Broodwar->enemy()->getUnits()) { if (unit->getPosition().getDistance(order.getPosition()) < order.getRadius()) { enemyUnitInRange = true; break; } } if (!enemyUnitInRange) { _squadData.getSquad(squad.getName()).clear(); } } }
// still has bug in it somewhere, use Old version void MeleeManager::assignTargetsNew(const BWAPI::Unitset & targets) { const BWAPI::Unitset & meleeUnits = getUnits(); // figure out targets BWAPI::Unitset meleeUnitTargets; for (auto & target : targets) { // conditions for targeting if (!(target->getType().isFlyer()) && !(target->isLifted()) && !(target->getType() == BWAPI::UnitTypes::Zerg_Larva) && !(target->getType() == BWAPI::UnitTypes::Zerg_Egg) && target->isVisible()) { meleeUnitTargets.insert(target); } } BWAPI::Unitset meleeUnitsToAssign(meleeUnits); std::map<BWAPI::Unit, int> attackersAssigned; for (auto & unit : meleeUnitTargets) { attackersAssigned[unit] = 0; } int smallThreshold = BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Zerg ? 3 : 1; int bigThreshold = BWAPI::Broodwar->self()->getRace() == BWAPI::Races::Zerg ? 12 : 3; // keep assigning targets while we have attackers and targets remaining while (!meleeUnitsToAssign.empty() && !meleeUnitTargets.empty()) { auto attackerAssignment = findClosestUnitPair(meleeUnitsToAssign, meleeUnitTargets); BWAPI::Unit & attacker = attackerAssignment.first; BWAPI::Unit & target = attackerAssignment.second; UAB_ASSERT_WARNING(attacker, "We should have chosen an attacker!"); if (!attacker) { break; } if (!target) { Micro::SmartMove(attacker, order.getPosition()); continue; } Micro::SmartAttackUnit(attacker, target); // update the number of units assigned to attack the target we found int & assigned = attackersAssigned[attackerAssignment.second]; assigned++; // if it's a small / fast unit and there's more than 2 things attacking it already, don't assign more if ((target->getType().isWorker() || target->getType() == BWAPI::UnitTypes::Zerg_Zergling) && (assigned >= smallThreshold)) { meleeUnitTargets.erase(target); } // if it's a building and there's more than 10 things assigned to it already, don't assign more else if (assigned > bigThreshold) { meleeUnitTargets.erase(target); } meleeUnitsToAssign.erase(attacker); } // if there's no targets left, attack move to the order destination if (meleeUnitTargets.empty()) { for (auto & unit : meleeUnitsToAssign) { if (unit->getDistance(order.getPosition()) > 100) { // move to it Micro::SmartMove(unit, order.getPosition()); BWAPI::Broodwar->drawLineMap(unit->getPosition(), order.getPosition(), BWAPI::Colors::Yellow); } } } }
void MeleeManager::assignTargetsOld(const BWAPI::Unitset & targets) { const BWAPI::Unitset & meleeUnits = getUnits(); // figure out targets BWAPI::Unitset meleeUnitTargets; for (auto & target : targets) { // conditions for targeting if (!(target->getType().isFlyer()) && !(target->isLifted()) && !(target->getType() == BWAPI::UnitTypes::Zerg_Larva) && !(target->getType() == BWAPI::UnitTypes::Zerg_Egg) && !(target->getType() == BWAPI::UnitTypes::Buildings)&& (target->isTargetable())&& target->isVisible()) { meleeUnitTargets.insert(target); } } std::unordered_map<BWAPI::Unit, BWAPI::Unit> attacker2target = assignEnemy(meleeUnits,meleeUnitTargets); // for each meleeUnit for (auto & meleeUnit : meleeUnits) { // if the order is to attack or defend if (order.getType() == SquadOrderTypes::Attack || order.getType() == SquadOrderTypes::Defend) { // run away if we meet the retreat critereon if (meleeUnitShouldRetreat(meleeUnit, targets) && meleeUnit->isUnderAttack()) { BWAPI::Position fleeTo(pullPosition); fleeTo.x = (fleeTo.x - meleeUnit->getPosition().x)*2 + meleeUnit->getPosition().x; fleeTo.y = (fleeTo.y - meleeUnit->getPosition().y)*2 + meleeUnit->getPosition().y; Micro::SmartMove(meleeUnit, fleeTo); } // if there are targets else if (!meleeUnitTargets.empty()) { BWAPI::Unit target; if (attacker2target.find(meleeUnit) != attacker2target.end()) { target = attacker2target[meleeUnit]; } else target = getTarget(meleeUnit, meleeUnitTargets); Micro::SmartAttackUnit(meleeUnit, target); } // if there are no targets else { // if we're not near the order position if (meleeUnit->getDistance(order.getPosition()) > 100) { // move to it Micro::SmartMove(meleeUnit, order.getPosition()); } } } if (Config::Debug::DrawUnitTargetInfo) { BWAPI::Broodwar->drawLineMap(meleeUnit->getPosition().x, meleeUnit->getPosition().y, meleeUnit->getTargetPosition().x, meleeUnit->getTargetPosition().y, Config::Debug::ColorLineTarget); } } }
void TankManager::executeMicro(const BWAPI::Unitset & targets) { const BWAPI::Unitset & tanks = getUnits(); // figure out targets BWAPI::Unitset tankTargets; std::copy_if(targets.begin(), targets.end(), std::inserter(tankTargets, tankTargets.end()), [](BWAPI::Unit u){ return u->isVisible() && !u->isFlying(); }); int siegeTankRange = BWAPI::UnitTypes::Terran_Siege_Tank_Siege_Mode.groundWeapon().maxRange() - 32; bool haveSiege = BWAPI::Broodwar->self()->hasResearched(BWAPI::TechTypes::Tank_Siege_Mode); // for each zealot for (auto & tank : tanks) { // train sub units such as scarabs or interceptors //trainSubUnits(rangedUnit); bool tankNearChokepoint = false; for (auto & choke : BWTA::getChokepoints()) { if (choke->getCenter().getDistance(tank->getPosition()) < 64) { tankNearChokepoint = true; break; } } // if the order is to attack or defend if (order.getType() == SquadOrderTypes::Attack || order.getType() == SquadOrderTypes::Defend) { // if there are targets if (!tankTargets.empty()) { // find the best target for this zealot BWAPI::Unit target = getTarget(tank, tankTargets); if (target && Config::Debug::DrawUnitTargetInfo) { BWAPI::Broodwar->drawLineMap(tank->getPosition(), tank->getTargetPosition(), BWAPI::Colors::Purple); } // if we are within siege range, siege up if (tank->getDistance(target) < siegeTankRange && tank->canSiege() && !tankNearChokepoint) { tank->siege(); } // otherwise unsiege and move in else if ((!target || tank->getDistance(target) > siegeTankRange) && tank->canUnsiege()) { tank->unsiege(); } // if we're in siege mode just attack the target if (tank->isSieged()) { Micro::SmartAttackUnit(tank, target); } // if we're not in siege mode kite the target else { Micro::SmartKiteTarget(tank, target); } } // if there are no targets else { // if we're not near the order position if (tank->getDistance(order.getPosition()) > 100) { if (tank->canUnsiege()) { tank->unsiege(); } else { // move to it Micro::SmartAttackMove(tank, order.getPosition()); } } } } } }