BWAPI
|
00001 #pragma once 00002 #include <BWAPI.h> 00003 #include <Util/Foreach.h> 00004 namespace BWAPI 00005 { 00006 namespace Templates 00007 { 00008 //--------------------------------------------- HAS POWER ------------------------------------------------ 00009 const bool bPsiFieldMask[10][16] = { 00010 { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, 00011 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, 00012 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }, 00013 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 00014 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 00015 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 00016 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 00017 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }, 00018 { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, 00019 { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 } 00020 }; 00021 template < class UnitImpl > 00022 bool hasPower(int x, int y, UnitType unitType, const std::set<UnitImpl*> &pylons) 00023 { 00024 if ( unitType >= 0 && unitType < UnitTypes::None && (!unitType.requiresPsi() || !unitType.isBuilding()) ) 00025 return true; 00026 00027 /* Loop through all pylons for the current player */ 00028 foreach (UnitImpl* i, pylons) 00029 { 00030 if ( !i->exists() || !i->isCompleted() ) 00031 continue; 00032 00033 Position p = i->getPosition(); 00034 if ( abs(p.x() - x) >= 256 ) 00035 continue; 00036 00037 if ( abs(p.y() - y) >= 160 ) 00038 continue; 00039 00040 if ( bPsiFieldMask[(y - p.y() + 160) / 32][(x - p.x() + 256) / 32] ) 00041 return true; 00042 } 00043 return false; 00044 } 00045 //---------------------------------------------- GENERAL ------------------------------------------------- 00046 template <class _T> 00047 void swapIfLarger(_T &smaller, _T &larger) 00048 { 00049 if ( smaller > larger ) 00050 std::swap(smaller, larger); 00051 } 00052 //-------------------------------------------- UNIT FINDER ----------------------------------------------- 00053 template <class finder> 00054 int getUnitFinderIndex(const finder *uf, int value, int start = 0) 00055 { 00056 unsigned int i = start; 00057 while ( uf[i].searchValue < value && uf[i].unitIndex && i < 1700*2 ) 00058 ++i; 00059 return i; 00060 } 00061 00062 template <class finder> 00063 void manageUnitFinder(finder *finder_x, finder *finder_y, DWORD *pdwFinderFlags, int left, int top, int right, int bottom, bool (__fastcall *callback)(Unit *uIterator), std::set<Unit*> &finderSet) 00064 { 00065 // Clear the set 00066 finderSet.clear(); 00067 00068 Templates::swapIfLarger<int>(left, right); 00069 Templates::swapIfLarger<int>(top, bottom); 00070 00071 // Declare some variables 00072 int r = right, b = bottom; 00073 bool isWidthExtended = right - left + 1 < UnitTypes::maxUnitWidth(); 00074 bool isHeightExtended = top - bottom + 1 < UnitTypes::maxUnitHeight(); 00075 00076 // Check if the location is smaller than the largest unit 00077 if ( isWidthExtended ) 00078 r += UnitTypes::maxUnitWidth(); 00079 if ( isHeightExtended ) 00080 b += UnitTypes::maxUnitHeight(); 00081 00082 // Obtain finder indexes for all bounds 00083 int iLeft = Templates::getUnitFinderIndex<finder>(finder_x, left); 00084 int iTop = Templates::getUnitFinderIndex<finder>(finder_y, top); 00085 int iRight = Templates::getUnitFinderIndex<finder>(finder_x, r + 1, iLeft); 00086 int iBottom = Templates::getUnitFinderIndex<finder>(finder_y, b + 1, iTop); 00087 00088 // Iterate the X entries of the finder 00089 for ( int x = iLeft; x < iRight; ++x ) 00090 { 00091 int iUnitIndex = finder_x[x].unitIndex; 00092 if ( pdwFinderFlags[iUnitIndex] == 1 ) 00093 continue; 00094 if ( isWidthExtended ) 00095 { 00096 Unit *u = ((GameImpl*)Broodwar)->_unitFromIndex(iUnitIndex); 00097 if ( u && u->getLeft() <= right ) 00098 pdwFinderFlags[iUnitIndex] = 1; 00099 } 00100 else 00101 pdwFinderFlags[iUnitIndex] = 1; 00102 } 00103 // Iterate the Y entries of the finder 00104 for ( int y = iTop; y < iBottom; ++y ) 00105 { 00106 int iUnitIndex = finder_y[y].unitIndex; 00107 if ( pdwFinderFlags[iUnitIndex] != 1 ) 00108 continue; 00109 if ( isHeightExtended ) 00110 { 00111 Unit *u = ((GameImpl*)Broodwar)->_unitFromIndex(iUnitIndex); 00112 if ( u && u->getTop() <= bottom ) 00113 pdwFinderFlags[iUnitIndex] = 2; 00114 } 00115 else 00116 pdwFinderFlags[iUnitIndex] = 2; 00117 } 00118 // Final Iteration 00119 for ( int x = iLeft; x < iRight; ++x ) 00120 { 00121 int iUnitIndex = finder_x[x].unitIndex; 00122 if ( pdwFinderFlags[iUnitIndex] == 2 ) 00123 { 00124 Unit *u = ((GameImpl*)Broodwar)->_unitFromIndex(iUnitIndex); 00125 if ( u && u->exists() && callback(u) ) 00126 finderSet.insert(u); 00127 } 00128 // Reset finderFlags so it can be reused without incident 00129 pdwFinderFlags[iUnitIndex] = 0; 00130 } 00131 } 00132 //------------------------------------------- CAN BUILD HERE --------------------------------------------- 00133 static inline bool canBuildHere(const Unit* builder, TilePosition position, UnitType type, bool checkExplored) 00134 { 00135 Broodwar->setLastError(Errors::Unbuildable_Location); 00136 int width = type.tileWidth(); 00137 int height = type.tileHeight(); 00138 00139 int left = position.x(); 00140 int top = position.y(); 00141 int right = left + width; 00142 int bottom = top + height; 00143 00144 /* Map limit Check */ 00145 if (left < 0) // left 00146 return false; 00147 if (top < 0) // top 00148 return false; 00149 if (right > Broodwar->mapWidth()) // right 00150 return false; 00151 if (bottom >= Broodwar->mapHeight()) // bottom 00152 return false; 00153 00154 //if the unit is a refinery, we just need to check the set of geysers to see if the position 00155 //matches one of them (and the type is still vespene geyser) 00156 if ( type.isRefinery() ) 00157 { 00158 foreach (Unit* g, Broodwar->getGeysers()) 00159 { 00160 if (g->getTilePosition() == position) 00161 { 00162 if (g->isVisible() && g->getType() != UnitTypes::Resource_Vespene_Geyser) 00163 return false; 00164 return Broodwar->setLastError(Errors::None); 00165 } 00166 } 00167 return false; 00168 } 00169 00170 /* Tile buildability check */ 00171 for(int ix = left; ix < right; ++ix) 00172 { 00173 for(int iy = top; iy < bottom; ++iy) 00174 { 00175 // Check if tile is buildable and explored 00176 if ( !Broodwar->isBuildable(ix, iy, true) || ( checkExplored && !Broodwar->isExplored(ix, iy)) ) 00177 return false; // @TODO: Error code for !isExplored ?? 00178 } 00179 } 00180 00181 // Check if builder is capable of reaching the building site 00182 if ( builder && !builder->getType().isFlagBeacon() && !builder->hasPath( (Position)TilePosition(left + width/2, top + height/2) ) ) 00183 return false; 00184 00185 /* Ground unit dimension check */ 00186 int targetX = left * 32 + type.tileWidth() * 32 / 2; 00187 int targetY = top * 32 + type.tileHeight() * 32 / 2; 00188 for(int ix = left; ix < right; ++ix) 00189 { 00190 for(int iy = top; iy < bottom; ++iy) 00191 { 00192 foreach(Unit *u, Broodwar->getUnitsOnTile(ix,iy)) 00193 { 00194 BWAPI::UnitType iterType = u->getType(); 00195 if ( !iterType.isBuilding() && 00196 !iterType.isFlyer() && 00197 !u->isLoaded() && 00198 u != builder && 00199 u->getLeft() <= targetX + type.dimensionRight() && 00200 u->getTop() <= targetY + type.dimensionDown() && 00201 u->getRight() >= targetX - type.dimensionLeft() && 00202 u->getBottom() >= targetY - type.dimensionUp() ) 00203 { 00204 if ( !type.isAddon() ) 00205 return false; 00206 else if ( !iterType.canMove() ) 00207 return false; 00208 } 00209 } 00210 } 00211 } 00212 00213 /* Creep Check */ 00214 if ( type.getRace() == Races::Zerg ) 00215 { // Creep requirement, or ignore creep if there isn't one 00216 if ( type.requiresCreep() ) 00217 for(int ix = left; ix < right; ++ix) 00218 for(int iy = top; iy < bottom; ++iy) 00219 if (!Broodwar->hasCreep(ix, iy)) 00220 return false; 00221 } 00222 else 00223 { // Can't build on the creep 00224 for(int ix = left; ix < right; ++ix) 00225 for(int iy = top; iy < bottom; ++iy) 00226 if (Broodwar->hasCreep(ix, iy)) 00227 return false; 00228 } 00229 00230 /* Power Check */ 00231 if ( type.requiresPsi() && !Broodwar->hasPower(left, top, width, height) ) 00232 return false; 00233 00234 /* Resource Check (CC, Nex, Hatch) */ 00235 if (type.isResourceDepot()) 00236 { 00237 foreach (BWAPI::Unit* m, Broodwar->getStaticMinerals()) 00238 { 00239 if (Broodwar->isVisible(m->getInitialTilePosition()) || 00240 Broodwar->isVisible(m->getInitialTilePosition().x() + 1, m->getInitialTilePosition().y())) 00241 if (!m->isVisible()) 00242 continue; // tile position is visible, but mineral is not => mineral does not exist 00243 if (m->getInitialTilePosition().x() > left - 5 && 00244 m->getInitialTilePosition().y() > top - 4 && 00245 m->getInitialTilePosition().x() < left + 7 && 00246 m->getInitialTilePosition().y() < top + 6) 00247 return false; 00248 } 00249 foreach (BWAPI::Unit* g, Broodwar->getStaticGeysers()) 00250 { 00251 if (g->getInitialTilePosition().x() > left - 7 && 00252 g->getInitialTilePosition().y() > top - 5 && 00253 g->getInitialTilePosition().x() < left + 7 && 00254 g->getInitialTilePosition().y() < top + 6) 00255 return false; 00256 } 00257 } 00258 //if the build site passes all these tests, return true. 00259 return Broodwar->setLastError(Errors::None); 00260 } 00261 //------------------------------------------- CAN MAKE --------------------------------------------------- 00262 static inline bool canMake(const Unit* builder, UnitType type) 00263 { 00264 /* Error checking */ 00265 Broodwar->setLastError(Errors::None); 00266 if ( !Broodwar->self() ) 00267 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00268 00269 BWAPI::UnitType requiredType = type.whatBuilds().first; 00270 00271 if ( builder ) 00272 { 00273 /* Check if the owner of the unit is you */ 00274 if (builder->getPlayer() != Broodwar->self()) 00275 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00276 00277 BWAPI::UnitType builderType = builder->getType(); 00278 if ( type == UnitTypes::Zerg_Nydus_Canal && builderType == UnitTypes::Zerg_Nydus_Canal ) 00279 { 00280 if ( !builder->isCompleted() ) 00281 return Broodwar->setLastError(Errors::Unit_Busy); 00282 00283 if ( builder->getNydusExit() ) 00284 return Broodwar->setLastError(Errors::Unknown); 00285 00286 return true; 00287 } 00288 00289 /* Check if this unit can actually build the unit type */ 00290 if ( requiredType == UnitTypes::Zerg_Larva && builderType.producesLarva() ) 00291 { 00292 if ( builder->getLarva().size() == 0 ) 00293 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00294 } 00295 else if ( builderType != requiredType ) 00296 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00297 00298 /* Carrier space */ 00299 if ( builderType == UnitTypes::Protoss_Carrier ) 00300 { 00301 int max_amt = 4; 00302 if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Carrier_Capacity) > 0) 00303 max_amt += 4; 00304 if (builder->getInterceptorCount() + (int)builder->getTrainingQueue().size() >= max_amt) 00305 return Broodwar->setLastError(Errors::Insufficient_Space); 00306 } 00307 else if ( builderType == UnitTypes::Hero_Gantrithor ) 00308 { 00309 if (builder->getInterceptorCount() + (int)builder->getTrainingQueue().size() >= 8) 00310 return Broodwar->setLastError(Errors::Insufficient_Space); 00311 } 00312 00313 /* Reaver Space */ 00314 if ( builderType == UnitTypes::Protoss_Reaver ) 00315 { 00316 int max_amt = 5; 00317 if (Broodwar->self()->getUpgradeLevel(UpgradeTypes::Reaver_Capacity) > 0) 00318 max_amt += 5; 00319 if (builder->getScarabCount() + (int)builder->getTrainingQueue().size() >= max_amt) 00320 return Broodwar->setLastError(Errors::Insufficient_Space); 00321 } 00322 else if ( builderType == UnitTypes::Hero_Warbringer ) 00323 { 00324 if (builder->getScarabCount() + (int)builder->getTrainingQueue().size() >= 10) 00325 return Broodwar->setLastError(Errors::Insufficient_Space); 00326 } 00327 } // builder 00328 00329 /* Check if player has enough minerals */ 00330 if ( Broodwar->self()->minerals() < type.mineralPrice() ) 00331 return Broodwar->setLastError(Errors::Insufficient_Minerals); 00332 00333 /* Check if player has enough gas */ 00334 if ( Broodwar->self()->gas() < type.gasPrice() ) 00335 return Broodwar->setLastError(Errors::Insufficient_Gas); 00336 00337 /* Check if player has enough supplies */ 00338 BWAPI::Race typeRace = type.getRace(); 00339 if ( type.supplyRequired() > 0 && Broodwar->self()->supplyTotal(typeRace) < Broodwar->self()->supplyUsed(typeRace) + type.supplyRequired() - (requiredType.getRace() == typeRace ? requiredType.supplyRequired() : 0) ) 00340 return Broodwar->setLastError(Errors::Insufficient_Supply); 00341 00342 UnitType addon = UnitTypes::None; 00343 std::map<UnitType, int>::const_iterator requiredEnd = type.requiredUnits().end(); 00344 for(std::map<UnitType, int>::const_iterator i = type.requiredUnits().begin(); i != requiredEnd; ++i) 00345 { 00346 if (i->first.isAddon()) 00347 addon = i->first; 00348 00349 bool pass = false; 00350 if (Broodwar->self()->completedUnitCount(i->first) >= i->second) 00351 pass = true; 00352 if ( i->first == UnitTypes::Zerg_Hatchery && 00353 Broodwar->self()->completedUnitCount(UnitTypes::Zerg_Hatchery) + 00354 Broodwar->self()->completedUnitCount(UnitTypes::Zerg_Lair) + 00355 Broodwar->self()->completedUnitCount(UnitTypes::Zerg_Hive) >= i->second ) 00356 pass = true; 00357 if ( i->first == UnitTypes::Zerg_Lair && 00358 Broodwar->self()->completedUnitCount(UnitTypes::Zerg_Lair) + 00359 Broodwar->self()->completedUnitCount(UnitTypes::Zerg_Hive) >= i->second) 00360 pass = true; 00361 if ( i->first == UnitTypes::Zerg_Spire && 00362 Broodwar->self()->completedUnitCount(UnitTypes::Zerg_Spire) + 00363 Broodwar->self()->completedUnitCount(UnitTypes::Zerg_Greater_Spire) >= i->second ) 00364 pass = true; 00365 00366 if ( !pass ) 00367 return Broodwar->setLastError(Errors::Insufficient_Tech); 00368 } 00369 00370 if (type.requiredTech() != TechTypes::None && !Broodwar->self()->hasResearched(type.requiredTech())) 00371 return Broodwar->setLastError(Errors::Insufficient_Tech); 00372 00373 if ( builder && 00374 addon != UnitTypes::None && 00375 addon.whatBuilds().first == type.whatBuilds().first && 00376 (!builder->getAddon() || builder->getAddon()->getType() != addon) ) 00377 return Broodwar->setLastError(Errors::Insufficient_Tech); 00378 return true; 00379 } 00380 //------------------------------------------- CAN RESEARCH ----------------------------------------------- 00381 static inline bool canResearch(const Unit* unit, TechType type) 00382 { 00383 /* Error checking */ 00384 Broodwar->setLastError(Errors::None); 00385 if ( !Broodwar->self() ) 00386 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00387 00388 if ( unit ) 00389 { 00390 if (unit->getPlayer() != Broodwar->self()) 00391 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00392 00393 if (unit->getType() != type.whatResearches()) 00394 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00395 00396 if ( unit->isLifted() || !unit->isIdle() || !unit->isCompleted() ) 00397 return Broodwar->setLastError(Errors::Unit_Busy); 00398 } 00399 if (Broodwar->self()->isResearching(type)) 00400 return Broodwar->setLastError(Errors::Currently_Researching); 00401 00402 if (Broodwar->self()->hasResearched(type)) 00403 return Broodwar->setLastError(Errors::Already_Researched); 00404 00405 if (Broodwar->self()->minerals() < type.mineralPrice()) 00406 return Broodwar->setLastError(Errors::Insufficient_Minerals); 00407 00408 if (Broodwar->self()->gas() < type.gasPrice()) 00409 return Broodwar->setLastError(Errors::Insufficient_Gas); 00410 00411 return true; 00412 } 00413 //------------------------------------------- CAN UPGRADE ------------------------------------------------ 00414 static inline bool canUpgrade(const Unit* unit, UpgradeType type) 00415 { 00416 Broodwar->setLastError(Errors::None); 00417 Player *self = Broodwar->self(); 00418 if ( !self ) 00419 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00420 00421 if ( unit ) 00422 { 00423 if (unit->getPlayer() != self) 00424 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00425 00426 if (unit->getType() != type.whatUpgrades()) 00427 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00428 00429 if ( unit->isLifted() || !unit->isIdle() || !unit->isCompleted() ) 00430 return Broodwar->setLastError(Errors::Unit_Busy); 00431 } 00432 int nextLvl = self->getUpgradeLevel(type)+1; 00433 00434 UnitType what = type.whatUpgrades(); 00435 if ( what != UnitTypes::None ) 00436 { 00437 if ( what == UnitTypes::Zerg_Hatchery && 00438 !self->completedUnitCount(UnitTypes::Zerg_Hatchery) && 00439 !self->completedUnitCount(UnitTypes::Zerg_Lair) && 00440 !self->completedUnitCount(UnitTypes::Zerg_Hive) ) 00441 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00442 else if ( what == UnitTypes::Zerg_Lair && 00443 !self->completedUnitCount(UnitTypes::Zerg_Lair) && 00444 !self->completedUnitCount(UnitTypes::Zerg_Hive) ) 00445 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00446 else if ( what == UnitTypes::Zerg_Spire && 00447 !self->completedUnitCount(UnitTypes::Zerg_Spire) && 00448 !self->completedUnitCount(UnitTypes::Zerg_Greater_Spire) ) 00449 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00450 else if ( !self->completedUnitCount(what) ) 00451 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00452 } 00453 00454 UnitType req = type.whatsRequired(nextLvl); 00455 if ( req != UnitTypes::None ) 00456 { 00457 if ( req == UnitTypes::Zerg_Hatchery && !self->completedUnitCount(UnitTypes::Zerg_Hatchery) ) 00458 { 00459 if ( !self->allUnitCount(UnitTypes::Zerg_Lair) && 00460 !self->allUnitCount(UnitTypes::Zerg_Hive) ) 00461 return Broodwar->setLastError(Errors::Insufficient_Tech); 00462 } 00463 else if ( req == UnitTypes::Zerg_Lair && !self->completedUnitCount(UnitTypes::Zerg_Lair) ) 00464 { 00465 if ( !self->allUnitCount(UnitTypes::Zerg_Hive) ) 00466 return Broodwar->setLastError(Errors::Insufficient_Tech); 00467 } 00468 else if ( req == UnitTypes::Zerg_Spire && !self->completedUnitCount(UnitTypes::Zerg_Spire) ) 00469 { 00470 if ( !self->allUnitCount(UnitTypes::Zerg_Greater_Spire) ) 00471 return Broodwar->setLastError(Errors::Insufficient_Tech); 00472 } 00473 else if ( !self->completedUnitCount(req) ) 00474 return Broodwar->setLastError(Errors::Insufficient_Tech); 00475 } 00476 00477 if (self->isUpgrading(type)) 00478 return Broodwar->setLastError(Errors::Currently_Upgrading); 00479 00480 if (self->getUpgradeLevel(type) >= type.maxRepeats()) 00481 return Broodwar->setLastError(Errors::Fully_Upgraded); 00482 00483 if ( self->minerals() < type.mineralPrice(nextLvl) ) 00484 return Broodwar->setLastError(Errors::Insufficient_Minerals); 00485 00486 if ( self->gas() < type.gasPrice(nextLvl) ) 00487 return Broodwar->setLastError(Errors::Insufficient_Gas); 00488 00489 return true; 00490 } 00491 00492 //------------------------------------------- CAN ISSUE COMMAND ------------------------------------------ 00493 static inline bool canIssueCommand(const Unit* thisUnit, UnitCommand c) 00494 { 00495 c.unit = (Unit*)thisUnit; 00496 BWAPI::UnitCommandType ct = c.type; 00497 // train/morph helper, get first larva 00498 if (UnitCommandTypes::Train == ct || 00499 UnitCommandTypes::Morph == ct) 00500 { 00501 if (thisUnit->getType().producesLarva() && c.getUnitType().whatBuilds().first == UnitTypes::Zerg_Larva ) 00502 { 00503 if (thisUnit->getLarva().empty()) 00504 { 00505 Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00506 return false; 00507 } 00508 c.unit = (UnitImpl*)(*thisUnit->getLarva().begin()); 00509 thisUnit = c.unit; 00510 } 00511 } 00512 00513 // Basic header 00514 Broodwar->setLastError(Errors::None); 00515 if (thisUnit->getPlayer() != Broodwar->self()) 00516 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00517 00518 if (!thisUnit->exists()) 00519 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00520 00521 // Global can be ordered check 00522 if ( thisUnit->isLockedDown() || 00523 thisUnit->isMaelstrommed() || 00524 thisUnit->isStasised() || 00525 thisUnit->isUnpowered() || 00526 thisUnit->getOrder() == Orders::ZergBirth || 00527 thisUnit->isLoaded() ) 00528 return Broodwar->setLastError(Errors::Unit_Busy); 00529 00530 // Hallucination check 00531 if ( thisUnit->isHallucination() && 00532 UnitCommandTypes::Attack_Move != ct && 00533 UnitCommandTypes::Attack_Unit != ct && 00534 UnitCommandTypes::Move != ct && 00535 UnitCommandTypes::Patrol != ct && 00536 UnitCommandTypes::Hold_Position != ct && 00537 UnitCommandTypes::Stop != ct && 00538 UnitCommandTypes::Follow != ct && 00539 UnitCommandTypes::Load != ct && 00540 UnitCommandTypes::Right_Click_Position != ct && 00541 UnitCommandTypes::Right_Click_Unit != ct ) 00542 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00543 00544 // Can be ordered check 00545 if ( !thisUnit->getType().isBuilding() && 00546 !thisUnit->isInterruptible() && 00547 (UnitCommandTypes::Attack_Unit == ct || 00548 UnitCommandTypes::Attack_Move == ct || 00549 UnitCommandTypes::Build == ct || 00550 UnitCommandTypes::Follow == ct || 00551 UnitCommandTypes::Gather == ct || 00552 UnitCommandTypes::Load == ct || 00553 UnitCommandTypes::Move == ct || 00554 UnitCommandTypes::Patrol == ct || 00555 UnitCommandTypes::Repair == ct || 00556 UnitCommandTypes::Return_Cargo == ct || 00557 UnitCommandTypes::Right_Click_Position == ct || 00558 UnitCommandTypes::Right_Click_Unit == ct || 00559 UnitCommandTypes::Unload == ct || 00560 UnitCommandTypes::Unload_All == ct || 00561 UnitCommandTypes::Unload_All_Position == ct || 00562 UnitCommandTypes::Use_Tech == ct || 00563 UnitCommandTypes::Use_Tech_Unit == ct || 00564 UnitCommandTypes::Use_Tech_Position == ct ) ) 00565 return Broodwar->setLastError(Errors::Unit_Busy); 00566 00567 // valid target check 00568 if ((!c.target || 00569 !c.target->exists()) && 00570 (UnitCommandTypes::Attack_Unit == ct || 00571 UnitCommandTypes::Set_Rally_Unit == ct || 00572 UnitCommandTypes::Follow == ct || 00573 UnitCommandTypes::Gather == ct || 00574 UnitCommandTypes::Repair == ct || 00575 UnitCommandTypes::Load == ct || 00576 UnitCommandTypes::Unload == ct || 00577 UnitCommandTypes::Right_Click_Unit == ct || 00578 UnitCommandTypes::Use_Tech_Unit == ct) ) 00579 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00580 00581 // Attack Unit requirements 00582 if ( UnitCommandTypes::Attack_Unit == ct || 00583 (UnitCommandTypes::Right_Click_Unit == ct && !c.target->getPlayer()->isNeutral() && thisUnit->getPlayer()->isEnemy(c.target->getPlayer()))) 00584 { 00585 WeaponType weapon = thisUnit->getType().groundWeapon(); 00586 bool targetInAir = (c.target->isLifted() || c.target->getType().isFlyer()); 00587 if (targetInAir) 00588 weapon = thisUnit->getType().airWeapon(); 00589 00590 bool canAttack = (weapon != WeaponTypes::None); 00591 00592 if ( ( (thisUnit->getType() == UnitTypes::Protoss_Reaver || thisUnit->getType() == UnitTypes::Hero_Warbringer) && thisUnit->getScarabCount() > 0 && !targetInAir) || 00593 ((thisUnit->getType() == UnitTypes::Protoss_Carrier || thisUnit->getType() == UnitTypes::Hero_Gantrithor) && thisUnit->getInterceptorCount() > 0) ) 00594 canAttack = true; 00595 00596 if (!canAttack) 00597 return Broodwar->setLastError(Errors::Unable_To_Hit); 00598 00599 if ( !thisUnit->getType().canMove() && !thisUnit->isInWeaponRange(c.target) ) 00600 return Broodwar->setLastError(Errors::Out_Of_Range); 00601 } 00602 00603 // Build/Train requirements 00604 if ( UnitCommandTypes::Build == ct) 00605 { 00606 if (TilePosition(c.x,c.y).isValid()==false) 00607 return Broodwar->setLastError(Errors::Invalid_Tile_Position); 00608 } 00609 00610 if ( UnitCommandTypes::Morph == ct || 00611 UnitCommandTypes::Train == ct) 00612 { 00613 UnitType uType = c.getUnitType(); 00614 if ( thisUnit->getType().producesLarva() && uType.whatBuilds().first == UnitTypes::Zerg_Larva ) 00615 { 00616 if (thisUnit->getLarva().empty()) 00617 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00618 UnitImpl *larva = (UnitImpl*)(*thisUnit->getLarva().begin()); 00619 return canIssueCommand( larva, UnitCommand::train(larva,uType) ); 00620 } 00621 } 00622 if ( UnitCommandTypes::Build == ct || 00623 UnitCommandTypes::Build_Addon == ct || 00624 UnitCommandTypes::Morph == ct || 00625 UnitCommandTypes::Train == ct ) 00626 { 00627 UnitType uType = c.getUnitType(); 00628 if ( !Broodwar->canMake(thisUnit, uType) ) 00629 return false; 00630 00631 if ( thisUnit->isConstructing() || 00632 !thisUnit->isCompleted() || 00633 (thisUnit->getType().isFlyingBuilding() && thisUnit->isLifted()) || 00634 (UnitCommandTypes::Train != ct && thisUnit->getType().isBuilding() && !thisUnit->isIdle()) ) 00635 return Broodwar->setLastError(Errors::Unit_Busy); 00636 00637 if ( UnitCommandTypes::Build == ct ) 00638 { 00639 if ( !uType.isBuilding() ) 00640 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00641 00642 if ( !uType.isAddon() && !Broodwar->canBuildHere(thisUnit, BWAPI::TilePosition(c.x, c.y), uType, true) ) 00643 return false; 00644 } 00645 else if ( UnitCommandTypes::Build_Addon == ct ) 00646 { 00647 if ( !uType.isAddon() ) 00648 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00649 00650 if ( thisUnit->getAddon() ) 00651 return Broodwar->setLastError(Errors::Incompatible_State); 00652 00653 if ( !Broodwar->canBuildHere(thisUnit, BWAPI::TilePosition(thisUnit->getTilePosition().x() + 4, thisUnit->getTilePosition().y() + 1), uType) ) 00654 return false; 00655 } 00656 else 00657 { 00658 if ( thisUnit->getType().producesLarva() && uType.whatBuilds().first == UnitTypes::Zerg_Larva && thisUnit->getLarva().size() == 0 ) 00659 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00660 } 00661 } // build/train 00662 00663 // Research/Upgrade requirements 00664 if ( UnitCommandTypes::Research == ct && !Broodwar->canResearch(thisUnit, c.getTechType()) ) 00665 return false; 00666 if ( UnitCommandTypes::Upgrade == ct && !Broodwar->canUpgrade(thisUnit, c.getUpgradeType()) ) 00667 return false; 00668 00669 // Set Rally 00670 if ( (UnitCommandTypes::Set_Rally_Position == ct || UnitCommandTypes::Set_Rally_Unit == ct) && !thisUnit->getType().canProduce() ) 00671 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00672 00673 /* Commented out because it breaks RallyTest. 00674 if ( UnitCommandTypes::Set_Rally_Position == ct && thisUnit->getRallyPosition().x() == c.x && thisUnit->getRallyPosition().y() == c.y ) 00675 return false; 00676 00677 if ( UnitCommandTypes::Set_Rally_Unit == ct && thisUnit == c.target ) 00678 return false; 00679 */ 00680 // Move/stop/standard 00681 if ( (UnitCommandTypes::Move == ct || 00682 UnitCommandTypes::Patrol == ct || 00683 UnitCommandTypes::Hold_Position == ct || 00684 UnitCommandTypes::Stop == ct || 00685 UnitCommandTypes::Follow == ct) && 00686 thisUnit->getType().isBuilding() && 00687 !thisUnit->isLifted() && 00688 ct != UnitCommandTypes::Hold_Position && 00689 ct != UnitCommandTypes::Stop ) 00690 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00691 00692 // Gather 00693 if ( UnitCommandTypes::Gather == ct ) 00694 { 00695 UnitType uType = c.target->getType(); 00696 if ( !thisUnit->getType().isWorker() || 00697 !uType.isResourceContainer() || 00698 uType == UnitTypes::Resource_Vespene_Geyser ) 00699 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00700 00701 if ( thisUnit->getPowerUp() || !c.target->isCompleted() ) 00702 return Broodwar->setLastError(Errors::Unit_Busy); 00703 } // gather 00704 00705 // return cargo 00706 if ( UnitCommandTypes::Return_Cargo == ct ) 00707 { 00708 if ( !thisUnit->getType().isWorker() ) 00709 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00710 00711 if ( !thisUnit->isCarryingGas() && !thisUnit->isCarryingMinerals() ) 00712 return Broodwar->setLastError(Errors::Insufficient_Ammo); 00713 } // return 00714 00715 // Repair 00716 if ( UnitCommandTypes::Repair == ct ) 00717 { 00718 UnitType targType = c.target->getType(); 00719 if ( thisUnit->getType() != BWAPI::UnitTypes::Terran_SCV || 00720 targType.getRace() != BWAPI::Races::Terran || 00721 !targType.isMechanical() ) 00722 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00723 } // repair 00724 00725 // Ability requirements 00726 if ( UnitCommandTypes::Use_Tech == ct || 00727 UnitCommandTypes::Use_Tech_Position == ct || 00728 UnitCommandTypes::Use_Tech_Unit == ct || 00729 UnitCommandTypes::Burrow == ct || 00730 UnitCommandTypes::Cloak == ct || 00731 UnitCommandTypes::Siege == ct ) 00732 { 00733 // Retrieve the tech type 00734 BWAPI::TechType tech = TechType(c.extra); 00735 if ( UnitCommandTypes::Use_Tech == ct) 00736 { 00737 if (tech.targetsUnit() || tech.targetsPosition() || tech == TechTypes::None || tech == TechTypes::Unknown || tech == TechTypes::Lurker_Aspect) 00738 return Broodwar->setLastError(Errors::Incompatible_TechType); 00739 } 00740 else if ( UnitCommandTypes::Use_Tech_Position == ct) 00741 { 00742 if ( !tech.targetsPosition() ) 00743 return Broodwar->setLastError(Errors::Incompatible_TechType); 00744 } 00745 else if ( UnitCommandTypes::Use_Tech_Unit == ct) 00746 { 00747 if ( !tech.targetsUnit() ) 00748 return Broodwar->setLastError(Errors::Incompatible_TechType); 00749 } 00750 if ( UnitCommandTypes::Burrow == ct ) 00751 tech = BWAPI::TechTypes::Burrowing; 00752 else if ( UnitCommandTypes::Cloak == ct ) 00753 tech = thisUnit->getType().cloakingTech(); 00754 else if ( UnitCommandTypes::Siege == ct ) 00755 tech = BWAPI::TechTypes::Tank_Siege_Mode; 00756 00757 // researched check 00758 if ( !thisUnit->getType().isHero() && !Broodwar->self()->hasResearched(tech) && thisUnit->getType() != UnitTypes::Zerg_Lurker ) 00759 return Broodwar->setLastError(Errors::Insufficient_Tech); 00760 00761 // energy check 00762 if ( thisUnit->getEnergy() < tech.energyUsed() ) 00763 return Broodwar->setLastError(Errors::Insufficient_Energy); 00764 00765 // unit check 00766 if ( tech != TechTypes::Burrowing && tech.whatUses().find(thisUnit->getType()) == tech.whatUses().end() ) 00767 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00768 00769 // spider mine check 00770 if ( tech == TechTypes::Spider_Mines && thisUnit->getSpiderMineCount() <= 0 ) 00771 return Broodwar->setLastError(Errors::Insufficient_Ammo); 00772 00773 // nuclear missile check 00774 if ( tech == TechTypes::Nuclear_Strike && thisUnit->getPlayer()->completedUnitCount(UnitTypes::Terran_Nuclear_Missile) == 0 ) 00775 return Broodwar->setLastError(Errors::Insufficient_Ammo); 00776 00777 // state checks 00778 if (tech == TechTypes::Burrowing) 00779 { 00780 if ( !thisUnit->getType().isBurrowable() ) 00781 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00782 00783 if ( thisUnit->isBurrowed() || thisUnit->getOrder() == Orders::Burrowing || thisUnit->getOrder() == Orders::Unburrowing ) 00784 return Broodwar->setLastError(Errors::Incompatible_State); 00785 } 00786 else if (tech == TechTypes::Tank_Siege_Mode) 00787 { 00788 if ( thisUnit->isSieged() ) 00789 return Broodwar->setLastError(Errors::Incompatible_State); 00790 00791 if ( thisUnit->getOrder() == Orders::Sieging || thisUnit->getOrder() == Orders::Unsieging ) 00792 return Broodwar->setLastError(Errors::Unit_Busy); 00793 } 00794 else if (tech == TechTypes::Personnel_Cloaking || tech == TechTypes::Cloaking_Field) 00795 { 00796 if ( thisUnit->getSecondaryOrder() == Orders::Cloak ) 00797 return Broodwar->setLastError(Errors::Incompatible_State); 00798 } 00799 00800 if ( tech == TechTypes::None || tech == TechTypes::Unknown ) 00801 return Broodwar->setLastError(Errors::Incompatible_TechType); 00802 } // ability 00803 00804 // Unburrow 00805 if ( UnitCommandTypes::Unburrow == ct ) 00806 { 00807 if ( !thisUnit->getType().isBurrowable() ) 00808 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00809 if ( !thisUnit->isBurrowed() || thisUnit->getOrder() == Orders::Unburrowing ) 00810 return Broodwar->setLastError(Errors::Incompatible_State); 00811 } // unburrow 00812 00813 // Decloak 00814 if ( UnitCommandTypes::Decloak == ct ) 00815 { 00816 if ( thisUnit->getType().cloakingTech() == TechTypes::None ) 00817 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00818 00819 if ( thisUnit->getSecondaryOrder() != Orders::Cloak ) 00820 return Broodwar->setLastError(Errors::Incompatible_State); 00821 } // decloak 00822 00823 // Unsiege 00824 if ( UnitCommandTypes::Unsiege == ct ) 00825 { 00826 if ( !thisUnit->isSieged() ) 00827 return Broodwar->setLastError(Errors::Incompatible_State); 00828 00829 if ( thisUnit->getOrder() == Orders::Sieging || thisUnit->getOrder() == Orders::Unsieging ) 00830 return Broodwar->setLastError(Errors::Unit_Busy); 00831 } 00832 00833 // lift/land 00834 if ( UnitCommandTypes::Lift == ct || UnitCommandTypes::Land == ct ) 00835 { 00836 if ( !thisUnit->getType().isFlyingBuilding() ) 00837 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00838 00839 if ( UnitCommandTypes::Lift == ct ? thisUnit->isLifted() : !thisUnit->isLifted() ) 00840 return Broodwar->setLastError(Errors::Incompatible_State); 00841 } // lift/land 00842 00843 // load 00844 if ( UnitCommandTypes::Load == ct) 00845 { 00846 //target must also be owned by self 00847 if (c.target->getPlayer() != Broodwar->self()) 00848 return Broodwar->setLastError(Errors::Unit_Not_Owned); 00849 00850 if ( c.target->isLoaded() || 00851 !c.target->isCompleted() || 00852 !thisUnit->isCompleted() ) 00853 return Broodwar->setLastError(Errors::Unit_Busy); 00854 00855 // verify upgrade for Zerg Overlord 00856 int thisUnitSpaceProvided = thisUnit->getType().spaceProvided(); 00857 if ( thisUnit->getType() == UnitTypes::Zerg_Overlord && Broodwar->self()->getUpgradeLevel(UpgradeTypes::Ventral_Sacs) == 0 ) 00858 return Broodwar->setLastError(Errors::Insufficient_Tech); 00859 00860 int targetSpaceProvided = c.target->getType().spaceProvided(); 00861 if ( c.target->getType() == UnitTypes::Zerg_Overlord && Broodwar->self()->getUpgradeLevel(UpgradeTypes::Ventral_Sacs) == 0 ) 00862 return Broodwar->setLastError(Errors::Insufficient_Tech); 00863 00864 // check if there is space available 00865 int thisUnitFreeSpace = thisUnitSpaceProvided; 00866 int targetFreeSpace = targetSpaceProvided; 00867 std::set<Unit*> loadedUnits = thisUnit->getLoadedUnits(); 00868 foreach(Unit* u, loadedUnits) 00869 { 00870 int r = u->getType().spaceRequired(); 00871 if (r > 0 && r < 8) 00872 thisUnitFreeSpace -= r; 00873 } 00874 std::set<Unit*> targetLoadedUnits = c.target->getLoadedUnits(); 00875 foreach(Unit* u, targetLoadedUnits) 00876 { 00877 int r = u->getType().spaceRequired(); 00878 if (r > 0 && r < 8) 00879 targetFreeSpace -= r; 00880 } 00881 if (thisUnitSpaceProvided > 0) 00882 { 00883 if (c.target->getType().canMove() == false || c.target->getType().isFlyer() || c.target->getType().spaceRequired() > 8) 00884 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00885 if (c.target->getType().spaceRequired() > thisUnitFreeSpace) 00886 return Broodwar->setLastError(Errors::Insufficient_Space); 00887 } 00888 else if (targetSpaceProvided > 0) 00889 { 00890 if (thisUnit->getType().canMove() == false || thisUnit->getType().isFlyer() || thisUnit->getType().spaceRequired() > 8) 00891 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00892 if (thisUnit->getType().spaceRequired() > targetFreeSpace) 00893 return Broodwar->setLastError(Errors::Insufficient_Space); 00894 } 00895 else 00896 { 00897 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00898 } 00899 } 00900 00901 // unload 00902 if ( UnitCommandTypes::Unload == ct || 00903 UnitCommandTypes::Unload_All == ct || 00904 UnitCommandTypes::Unload_All_Position == ct ) 00905 { 00906 if ( thisUnit->getLoadedUnits().size() == 0 ) 00907 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00908 00909 // Check overlord tech 00910 int thisUnitSpaceProvided = thisUnit->getType().spaceProvided(); 00911 if ( thisUnit->getType() == UnitTypes::Zerg_Overlord && Broodwar->self()->getUpgradeLevel(UpgradeTypes::Ventral_Sacs) == 0) 00912 return Broodwar->setLastError(Errors::Insufficient_Tech); 00913 00914 if ( thisUnitSpaceProvided <= 0 ) 00915 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00916 00917 if ( UnitCommandTypes::Unload == ct ) 00918 { 00919 if ( !c.target ) 00920 return Broodwar->setLastError(Errors::Unit_Does_Not_Exist); 00921 if ( !c.target->isLoaded() ) 00922 return Broodwar->setLastError(Errors::Incompatible_State); 00923 } 00924 00925 if ( thisUnit->getType() != UnitTypes::Terran_Bunker && thisUnit ) 00926 { 00927 BWAPI::Position targDropPos = (UnitCommandTypes::Unload_All_Position == ct ? c.getTargetPosition() : thisUnit->getPosition()); 00928 if ( !Broodwar->isWalkable(targDropPos.x()/8, targDropPos.y()/8) ) 00929 return Broodwar->setLastError(Errors::Unreachable_Location); 00930 } 00931 } // unload 00932 00933 // Halt construction 00934 if ( UnitCommandTypes::Halt_Construction == ct && thisUnit->getOrder() != Orders::ConstructingBuilding ) 00935 return false; 00936 00937 // Cancel construction 00938 if ( UnitCommandTypes::Cancel_Construction == ct ) 00939 { 00940 if ( !thisUnit->getType().isBuilding() ) 00941 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00942 00943 if ( thisUnit->isCompleted() || (!thisUnit->isCompleted() && thisUnit->getType() == UnitTypes::Zerg_Nydus_Canal && thisUnit->getNydusExit()) ) 00944 return false; 00945 } 00946 00947 // cancel addon 00948 if ( UnitCommandTypes::Cancel_Addon == ct && (!thisUnit->getAddon() || thisUnit->getAddon()->isCompleted()) ) 00949 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00950 00951 // cancel train 00952 if ( UnitCommandTypes::Cancel_Train == ct && !thisUnit->isTraining() ) 00953 return false; 00954 00955 if ( UnitCommandTypes::Cancel_Train_Slot == ct && (!thisUnit->isTraining() || (thisUnit->getTrainingQueue().size() <= (unsigned int)c.extra && c.extra >= 0)) ) 00956 return false; 00957 00958 // cancel morph 00959 if ( UnitCommandTypes::Cancel_Morph == ct && (!thisUnit->isMorphing() || (!thisUnit->isCompleted() && thisUnit->getType() == UnitTypes::Zerg_Nydus_Canal && thisUnit->getNydusExit())) ) 00960 return false; 00961 00962 // cancel research 00963 if ( UnitCommandTypes::Cancel_Research == ct && thisUnit->getOrder() != Orders::ResearchTech ) 00964 return false; 00965 00966 // cancel upgrade 00967 if ( UnitCommandTypes::Cancel_Upgrade == ct && thisUnit->getOrder() != Orders::Upgrade ) 00968 return false; 00969 00970 // place COP 00971 if ( UnitCommandTypes::Place_COP == ct ) 00972 { 00973 if ( !thisUnit->getType().isFlagBeacon() ) 00974 return Broodwar->setLastError(Errors::Incompatible_UnitType); 00975 00976 if ( ((UnitImpl*)thisUnit)->self->buttonset == 228 || thisUnit->getOrder() != BWAPI::Orders::CTFCOPInit ) 00977 return Broodwar->setLastError(Errors::Incompatible_State); 00978 00979 if ( !Broodwar->canBuildHere(thisUnit, BWAPI::TilePosition(c.x, c.y), thisUnit->getType(), true) ) 00980 return false; 00981 } 00982 return true; 00983 } 00984 } 00985 //--------------------------------------------- COMPUTE DISTANCE ------------------------------------------- 00986 static inline int computeDistance(const Unit *src, const Unit *targ) 00987 { 00988 if ( src == targ || !src || !targ ) 00989 return 0; 00990 00991 int xDist = src->getLeft() - (targ->getRight() + 1); 00992 if ( xDist < 0 ) 00993 { 00994 xDist = targ->getLeft() - (src->getRight() + 1); 00995 if ( xDist < 0 ) 00996 xDist = 0; 00997 } 00998 int yDist = src->getTop() - (targ->getBottom() + 1); 00999 if ( yDist < 0 ) 01000 { 01001 yDist = targ->getTop() - (src->getBottom() + 1); 01002 if ( yDist < 0 ) 01003 yDist = 0; 01004 } 01005 return Position(0, 0).getApproxDistance(Position(xDist, yDist)); 01006 } 01007 //--------------------------------------------- COMPUTE DISTANCE ------------------------------------------- 01008 static inline int computeDistance(const Unit* src, Position targ) 01009 { 01010 if ( !src ) 01011 return 0; 01012 01013 int xDist = src->getLeft() - (targ.x() + 1); 01014 if ( xDist < 0 ) 01015 { 01016 xDist = targ.x() - (src->getRight() + 1); 01017 if ( xDist < 0 ) 01018 xDist = 0; 01019 } 01020 int yDist = src->getTop() - (targ.y() + 1); 01021 if ( yDist < 0 ) 01022 { 01023 yDist = targ.y() - (src->getBottom() + 1); 01024 if ( yDist < 0 ) 01025 yDist = 0; 01026 } 01027 return Position(0, 0).getApproxDistance(Position(xDist, yDist)); 01028 } 01029 }