TankAICurrentMove.cpp

Go to the documentation of this file.
00001 ////////////////////////////////////////////////////////////////////////////////
00002 //    Scorched3D (c) 2000-2009
00003 //
00004 //    This file is part of Scorched3D.
00005 //
00006 //    Scorched3D is free software; you can redistribute it and/or modify
00007 //    it under the terms of the GNU General Public License as published by
00008 //    the Free Software Foundation; either version 2 of the License, or
00009 //    (at your option) any later version.
00010 //
00011 //    Scorched3D is distributed in the hope that it will be useful,
00012 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //    GNU General Public License for more details.
00015 //
00016 //    You should have received a copy of the GNU General Public License
00017 //    along with Scorched3D; if not, write to the Free Software
00018 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 ////////////////////////////////////////////////////////////////////////////////
00020 
00021 #include <tankai/TankAICurrentMove.h>
00022 #include <tankai/TankAIAimGuesser.h>
00023 #include <tankai/TankAISniperGuesser.h>
00024 #include <coms/ComsPlayedMoveMessage.h>
00025 #include <coms/ComsDefenseMessage.h>
00026 #include <common/Logger.h>
00027 #include <server/ServerShotHolder.h>
00028 #include <server/ServerDefenseHandler.h>
00029 #include <server/ScorchedServer.h>
00030 #include <tank/Tank.h>
00031 #include <tank/TankLib.h>
00032 #include <tank/TankPosition.h>
00033 #include <tank/TankAccessories.h>
00034 #include <target/TargetLife.h>
00035 #include <target/TargetShield.h>
00036 #include <target/TargetParachute.h>
00037 #include <landscapemap/LandscapeMaps.h>
00038 #include <landscapemap/MovementMap.h>
00039 #include <landscapemap/GroundMaps.h>
00040 #include <weapons/AccessoryStore.h>
00041 #include <weapons/Shield.h>
00042 #include <weapons/WeaponMoveTank.h>
00043 #include <XML/XMLNode.h>
00044 
00045 TankAICurrentMove::TankAICurrentMove() : 
00046         useResign_(true), useFuel_(true), 
00047         totalDamageBeforeMove_(0.0f),
00048         movementDamage_(300.0f), movementDamageChance_(0.3f), movementLife_(75.0f),
00049         movementRandom_(10.0f), movementCloseness_(15.0f),
00050         groupShotSize_(2), groupShotChance_(0.7f), groupTargetDistance_(25.0f),
00051         resignLife_(10.0f), // Min life before resigning
00052         largeWeaponUseDistance_(10.0f), // The distance under which large weapons will be used
00053         sniperUseDistance_(80.0f), // The max distance to allow sniper shots
00054         sniperStartDistance_(10.0f), sniperEndDistance_(0.0f),
00055         sniperMinDecrement_(2.0f), sniperMaxDecrement_(5.0f),
00056         sniperMovementFactor_(4.0f),
00057         projectileStartDistance_(10.0f), projectileEndDistance_(5.0f),
00058         projectileMinDecrement_(1.0f), projectileMaxDecrement_(4.0f),
00059         projectileMovementFactor_(10.0f), projectileMinDistance_(10.0f)
00060 {
00061 }
00062 
00063 TankAICurrentMove::~TankAICurrentMove()
00064 {
00065 }
00066 
00067 bool TankAICurrentMove::parseConfig(XMLNode *node)
00068 {
00069         {
00070                 XMLNode *targets = 0;
00071                 if (!node->getNamedChild("targets", targets)) return false;
00072                 if (!targets_.parseConfig(targets)) return false;
00073         }
00074         {
00075                 XMLNode *resign = 0;
00076                 if (!node->getNamedChild("resign", resign)) return false;
00077                 if (!resign->getNamedChild("useresign", useResign_)) return false;
00078                 if (!resign->getNamedChild("resignlife", resignLife_)) return false;
00079                 if (!resign->failChildren()) return false;
00080         }
00081         {
00082                 XMLNode *movement = 0;
00083                 if (!node->getNamedChild("movement", movement)) return false;
00084                 if (!movement->getNamedChild("usefuel", useFuel_)) return false;
00085                 if (!movement->getNamedChild("movementdamage", movementDamage_)) return false;
00086                 if (!movement->getNamedChild("movementdamagechance", movementDamageChance_)) return false;
00087                 if (!movement->getNamedChild("movementlife", movementLife_)) return false;
00088                 if (!movement->getNamedChild("movementrandom", movementRandom_)) return false;
00089                 if (!movement->getNamedChild("movementcloseness", movementCloseness_)) return false;
00090                 if (!movement->failChildren()) return false;
00091         }
00092         {
00093                 XMLNode *groupshot = 0;
00094                 if (!node->getNamedChild("groupshot", groupshot)) return false;
00095                 if (!groupshot->getNamedChild("groupshotsize", groupShotSize_)) return false;
00096                 if (!groupshot->getNamedChild("groupshotchance", groupShotChance_)) return false;
00097                 if (!groupshot->getNamedChild("grouptargetdistance", groupTargetDistance_)) return false;
00098                 if (!groupshot->failChildren()) return false;
00099         }
00100         {
00101                 XMLNode *sniper = 0;
00102                 if (!node->getNamedChild("sniper", sniper)) return false;
00103                 if (!sniper->getNamedChild("snipermovementfactor", sniperMovementFactor_)) return false;
00104                 if (!sniper->getNamedChild("sniperusedistance", sniperUseDistance_)) return false;
00105                 if (!sniper->getNamedChild("sniperstartdistance", sniperStartDistance_)) return false;
00106                 if (!sniper->getNamedChild("sniperenddistance", sniperEndDistance_)) return false;
00107                 if (!sniper->getNamedChild("snipermindecrement", sniperMinDecrement_)) return false;
00108                 if (!sniper->getNamedChild("snipermaxdecrement", sniperMaxDecrement_)) return false;
00109                 if (!sniper->failChildren()) return false;
00110         }
00111         {
00112                 XMLNode *projectile = 0;
00113                 if (!node->getNamedChild("projectile", projectile)) return false;
00114                 if (!projectile->getNamedChild("projectilemindistance", projectileMinDistance_)) return false;
00115                 if (!projectile->getNamedChild("projectilemovementfactor", projectileMovementFactor_)) return false;
00116                 if (!projectile->getNamedChild("projectilestartdistance", projectileStartDistance_)) return false;
00117                 if (!projectile->getNamedChild("projectileenddistance", projectileEndDistance_)) return false;
00118                 if (!projectile->getNamedChild("projectilemindecrement", projectileMinDecrement_)) return false;
00119                 if (!projectile->getNamedChild("projectilemaxdecrement", projectileMaxDecrement_)) return false;
00120                 if (!projectile->getNamedChild("largeweaponusedistance", largeWeaponUseDistance_)) return false;
00121                 if (!projectile->failChildren()) return false;
00122         }
00123 
00124         return node->failChildren();
00125 }
00126 
00127 void TankAICurrentMove::clear()
00128 {
00129         totalDamageBeforeMove_ = 0.0f;
00130         shotRecords_.clear();
00131 }
00132 
00133 void TankAICurrentMove::playMove(Tank *tank, 
00134         TankAIWeaponSets::WeaponSet *weapons, bool useBatteries)
00135 {
00136         std::list<Tank *> sortedTanks;  
00137 
00138         // Find the list of tanks we can shoot at 
00139         // In the order we want to shoot at them
00140         targets_.getTargets(tank, sortedTanks);
00141 
00142         // Check if we have taken a lot of damage and we can move
00143         float totalDamage = 
00144                 targets_.getTotalDamageTaken() - totalDamageBeforeMove_;
00145         if (totalDamage > movementDamage_ &&
00146                 RAND <= movementDamageChance_)
00147         {
00148                 // Bring the health back up
00149                 if (useBatteries)
00150                 {
00151                         useAvailableBatteries(tank);
00152                 }
00153 
00154                 if (tank->getLife().getLife().asFloat() > movementLife_)
00155                 {
00156                         // Try to move
00157                         if (makeMoveShot(tank, weapons, sortedTanks)) return;
00158                 }
00159         }
00160 
00161         // Check to see if we can make a huge shot at a number of tanks
00162         if (RAND <= groupShotChance_)
00163         {
00164                 if (makeGroupShot(tank, weapons, sortedTanks)) return;
00165         }
00166 
00167         // Try to shoot at each tank in turn
00168         while (!sortedTanks.empty())
00169         {
00170                 // Get the first tank
00171                 Tank *targetTank = sortedTanks.front();
00172                 sortedTanks.pop_front();
00173 
00174                 // Get the list of weapons that might make sense here
00175                 TankAICurrentMoveWeapons moveWeapons(tank, targetTank, weapons);
00176 
00177                 // Try to shoot at it
00178                 if (shootAtTank(tank, targetTank, moveWeapons)) return;
00179 
00180                 // Keeping trying to shoot at the tanks until we make a shot
00181                 // or run out of tanks
00182 
00183                 // Check if we can use batteries, as perhaps we couldn't shoot 
00184                 // due to a lack of power
00185                 if (useBatteries &&
00186                         tank->getLife().getLife() < tank->getLife().getMaxLife())
00187                 {
00188                         // Bring the health back up
00189                         if (useAvailableBatteries(tank))
00190                         {
00191                                 // Perhaps we can reach now so do this tank again
00192                                 sortedTanks.push_front(targetTank);
00193                         }
00194                 }
00195         }
00196 
00197         // Try to move so we can get a better shot at the targets
00198         // Only move if we have a hope of hitting them
00199         if (tank->getLife().getLife().asFloat() > movementLife_)
00200         {
00201                 targets_.getTargets(tank, sortedTanks);
00202                 if (makeMoveShot(tank, weapons, sortedTanks)) return;
00203         }
00204 
00205         // Is there any point in making a move
00206         // Done after select weapons to allow shields to be raised
00207         if (useResign_ &&
00208                 tank->getLife().getLife().asFloat() < resignLife_) 
00209         {
00210                 resign(tank);
00211                 return;
00212         }
00213 
00214         // By default skip this move if we can't find anything to do
00215         // Perhaps we are burried etc...
00216         skipMove(tank);
00217 }
00218 
00219 bool TankAICurrentMove::shootAtTank(Tank *tank, Tank *targetTank, 
00220         TankAICurrentMoveWeapons &weapons)
00221 {
00222         // Try to make a sniper shot
00223         if (makeSniperShot(tank, targetTank, weapons)) return true;
00224 
00225         // Try to make a laser shot
00226         if (makeLaserSniperShot(tank, targetTank, weapons)) return true;
00227 
00228         // Then a projectile shot
00229         if (makeProjectileShot(tank, targetTank, weapons)) return true;
00230 
00231         // Check if we are burried
00232         if (makeBurriedShot(tank, targetTank, weapons)) return true;
00233 
00234         return false;
00235 }
00236 
00237 bool TankAICurrentMove::makeProjectileShot(Tank *tank, Tank *targetTank, 
00238         TankAICurrentMoveWeapons &weapons)
00239 {
00240         // Check we have any weapons to fire
00241         if (!weapons.roller &&
00242                 !weapons.digger &&
00243                 !weapons.napalm &&
00244                 !weapons.large &&
00245                 !weapons.small) return false;
00246 
00247         // Get the place we want to shoot at
00248         Vector directTarget = targetTank->getPosition().getTankPosition().asVector();
00249 
00250         // Check if the person is in a hole
00251         bool inhole = false;
00252         if (weapons.roller &&
00253                 inHole(directTarget))
00254         {
00255                 inhole = true;
00256 
00257                 // Check for reflective shields
00258                 if (weapons.shield &&
00259                         (weapons.shield->getShieldType() == Shield::ShieldTypeRoundReflective ||
00260                         weapons.shield->getShieldType() == Shield::ShieldTypeSquareReflective))
00261                 {
00262                         // Pick an area outside the shield
00263                         // and make sure its downhill if we can
00264                         directTarget = lowestHighest(weapons, directTarget, false);
00265                 }
00266         }
00267         else
00268         {
00269                 // Check for reflective shields
00270                 if (weapons.shield &&
00271                         (weapons.shield->getShieldType() == Shield::ShieldTypeRoundReflective ||
00272                         weapons.shield->getShieldType() == Shield::ShieldTypeSquareReflective))
00273                 {
00274                         // Pick an area outside the shield
00275                         // and make sure its uphill if we can
00276                         directTarget = lowestHighest(weapons, directTarget, true);
00277                 }
00278         }
00279 
00280         // Get the distance to get to this tank
00281         float tankAimDistance = getShotDistance(targetTank, true);
00282         tankAimDistance -= 5.0f;
00283         if (tankAimDistance < 0.0f) tankAimDistance = 0.0f;
00284         //Logger::log(S3D::formatStringBuffer("Aim Distance %.2f", tankAimDistance));
00285 
00286         // Find a place where we will hit
00287         Vector aimPosition = directTarget;
00288         float a = RAND * 3.14f * 2.0f;
00289         aimPosition[0] += sinf(a) * tankAimDistance;
00290         aimPosition[1] += cosf(a) * tankAimDistance;
00291         float aimDistance = MIN(tankAimDistance + 5.0f, 15.0f);
00292 
00293         // Check for all angles to see if we can shoot at this tank
00294         for (float degs=45.0f; degs<=85.0f; degs+=8.0f)
00295         {
00296                 // Check this angle
00297                 Vector actualPosition;
00298                 TankAIAimGuesser aimGuesser;
00299                 if (aimGuesser.guess(tank, aimPosition, degs, aimDistance, actualPosition))
00300                 {       
00301                         // Check we are not firing too close to us
00302                         float distanceFromTarget = 
00303                                 (actualPosition - directTarget).Magnitude();
00304                         float distanceFromUs = 
00305                                 (actualPosition - tank->getPosition().getTankPosition().asVector()).Magnitude();
00306                         if (distanceFromUs < projectileMinDistance_) continue;
00307 
00308                         // We can fire at this tank
00309                         // ...
00310                         // Check how close we are
00311                         if (distanceFromTarget < largeWeaponUseDistance_)
00312                         {
00313                                 // Check if the tank is in a hole
00314                                 if (inhole)
00315                                 {
00316                                         setWeapon(tank, weapons.roller);
00317                                 }
00318                                 else
00319                                 {
00320                                         // We are close
00321                                         // Choose a suitably good weapon
00322                                         if (weapons.shield)
00323                                         {
00324                                                 // A shield beating weapon
00325                                                 if (weapons.digger) setWeapon(tank, weapons.digger);
00326                                                 else if (weapons.napalm) setWeapon(tank, weapons.napalm);
00327                                                 else if (weapons.large) setWeapon(tank, weapons.large);
00328                                                 else return false;
00329                                         }
00330                                         else
00331                                         {
00332                                                 
00333                                                 // A normal weapon
00334                                                 if (weapons.digger) setWeapon(tank, weapons.digger);
00335                                                 else if (weapons.large) setWeapon(tank, weapons.large);         
00336                                                 else if (weapons.small) setWeapon(tank, weapons.small);                                 
00337                                                 else return false;
00338                                         }
00339                                 }
00340                         }
00341                         else
00342                         {
00343                                 // We are not close, choose a cheap weapon
00344                                 if (weapons.small) setWeapon(tank, weapons.small);      
00345                                 else if (weapons.large) setWeapon(tank, weapons.large);
00346                                 else return false;
00347                         }
00348 
00349                         // Fire the shot
00350                         shotAtTank(targetTank, true, distanceFromTarget);
00351                         fireShot(tank);
00352                         return true;
00353                 }
00354         }
00355 
00356         return false;
00357 }
00358 
00359 bool TankAICurrentMove::makeSniperShot(Tank *tank, Tank *targetTank, 
00360         TankAICurrentMoveWeapons &weapons)
00361 {
00362         // Check if we have any weapons we can use for sniper
00363         if (!weapons.digger &&
00364                 !weapons.laser &&
00365                 !weapons.large &&
00366                 !weapons.small) return false;
00367 
00368         // Get the place we want to shoot at
00369         Vector directTarget = targetTank->getPosition().getTankPosition().asVector();
00370 
00371         // First check to see if we can make a sniper shot that carries all the way
00372         // as this is generaly an easier shot
00373         float offset = getShotDistance(targetTank, false);
00374         TankAISniperGuesser sniperGuesser;
00375         if (sniperGuesser.guess(tank, directTarget, sniperUseDistance_, true, offset))
00376         {
00377                 // We can make a ordinary sniper shot
00378 
00379                 // Does this target have a bouncy shield
00380                 if (!weapons.shield ||
00381                         (weapons.shield->getShieldType() != Shield::ShieldTypeRoundReflective &&
00382                         weapons.shield->getShieldType() != Shield::ShieldTypeSquareReflective))
00383                 {
00384                         // This is good, use a normal sniper shot
00385                         if (weapons.shield)
00386                         {
00387                                 // Use a shield beating weapon                  
00388                                 if (weapons.digger) setWeapon(tank, weapons.digger);
00389                                 else if (weapons.laser) setWeapon(tank, weapons.laser);
00390                                 else if (weapons.large) setWeapon(tank, weapons.large);
00391                                 else return false;
00392                         }
00393                         else
00394                         {
00395                                 // Just use an ordinary weapon
00396                                 if (weapons.digger) setWeapon(tank, weapons.digger);
00397                                 else if (weapons.large) setWeapon(tank, weapons.large);
00398                                 else if (weapons.small) setWeapon(tank, weapons.small);
00399                                 else return false;
00400                         }
00401 
00402                         // Fire the shot
00403                         shotAtTank(targetTank, false, 0.0f);
00404                         fireShot(tank);
00405                         return true;
00406                 }
00407                 else if (weapons.laser)
00408                 {
00409                         // They have a reflective shield but we can use a laser
00410                         // Set and fire the laser
00411                         shotAtTank(targetTank, false, 0.0f);
00412                         fireShot(tank);
00413                         return true;
00414                 }
00415         }
00416 
00417         return false;
00418 }
00419 
00420 bool TankAICurrentMove::makeLaserSniperShot(Tank *tank, Tank *targetTank, 
00421         TankAICurrentMoveWeapons &weapons)
00422 {
00423         // Check if we have any lasers to fire
00424         if (!weapons.laser) return false;
00425 
00426         // Get the place we want to shoot at
00427         Vector directTarget = targetTank->getPosition().getTankPosition().asVector();
00428         
00429         // Second check to see if we can make a sniper shot that is obstructed
00430         // but could use a laser
00431         float offset = getShotDistance(targetTank, false);
00432         TankAISniperGuesser sniperGuesser;
00433         if (sniperGuesser.guess(tank, directTarget, sniperUseDistance_, false, offset))
00434         {
00435                 if (weapons.laser)
00436                 {
00437                         // Set and fire the laser
00438                         shotAtTank(targetTank, false, 0.0f);
00439                         setWeapon(tank, weapons.laser);
00440                         fireShot(tank);
00441                         return true;
00442                 }
00443         }
00444 
00445         return false;
00446 }
00447 
00448 bool TankAICurrentMove::makeBurriedShot(Tank *tank, Tank *targetTank, 
00449         TankAICurrentMoveWeapons &weapons)
00450 {
00451         // Don't check if we can't uncover
00452         if (!weapons.uncover) return false;
00453 
00454         // Find a shot towards a target
00455         fixed xy, yz, power;
00456         TankLib::getSniperShotTowardsPosition(
00457                 ScorchedServer::instance()->getContext(),
00458                 tank->getPosition().getTankPosition(), targetTank->getPosition().getTankPosition(),
00459                 100000, xy, yz, power);
00460 
00461         // Check if this shot is burried
00462         if (TankLib::intersection(
00463                 ScorchedServer::instance()->getContext(), 
00464                 tank->getPosition().getTankGunPosition(), 
00465                 xy, yz, power, 2))
00466         {
00467                 // Try to uncover the tank
00468                 if (weapons.uncover)
00469                 {
00470                         tank->getPosition().rotateGunXY(xy, false);
00471                         tank->getPosition().rotateGunYZ(yz, false);
00472                         tank->getPosition().changePower(power, false);
00473 
00474                         setWeapon(tank, weapons.uncover);
00475                         fireShot(tank);
00476                         return true;
00477                 }
00478         }
00479 
00480         return false;
00481 }
00482 
00483 bool TankAICurrentMove::inHole(Vector &position)
00484 {
00485         // Find the lowest pos around
00486         Vector pos = position;
00487         for (;;)
00488         {
00489                 Vector lowest = pos;
00490                 for (float a=0.0f; a<360.0f; a+=45.0f)
00491                 {
00492                         float offSetX = sinf(a / 180.0f * PI) * 1.25f;
00493                         float offSetY = cosf(a / 180.0f * PI) * 1.25f;
00494 
00495                         Vector newPos(
00496                                 pos[0] + offSetX,
00497                                 pos[1] + offSetY);
00498                         newPos[2] =
00499                                 ScorchedServer::instance()->getLandscapeMaps().
00500                                         getGroundMaps().getInterpHeight(
00501                                                 fixed::fromFloat(newPos[0]), 
00502                                                 fixed::fromFloat(newPos[1])).asFloat();
00503                         if (newPos[2] < lowest[2]) lowest = newPos;
00504                 }
00505 
00506                 if (lowest[2] < pos[2])
00507                 {
00508                         pos = lowest;
00509                         Vector direction = pos - position;
00510                         float dist =
00511                                 float(sqrt(direction[0]*direction[0] + direction[1]*direction[1]));
00512                         if (dist > 6.0f) return false;
00513                 }
00514                 else
00515                 {
00516                         break;
00517                 }
00518         }
00519 
00520         // Then see if this is in a hole
00521         for (float a=0.0f; a<360.0f; a+=22.5f)
00522         {
00523                 bool ok = false;
00524                 for (float radius=2.0f; radius<10.0f; radius+=1.0f)
00525                 {
00526                         float offSetX = sinf(a / 180.0f * PI) * radius;
00527                         float offSetY = cosf(a / 180.0f * PI) * radius;
00528                         
00529                         Vector newPos(
00530                                 pos[0] + offSetX,
00531                                 pos[1] + offSetY);
00532                         newPos[2] =
00533                                 ScorchedServer::instance()->getLandscapeMaps().
00534                                         getGroundMaps().getInterpHeight(
00535                                         fixed::fromFloat(newPos[0]), 
00536                                         fixed::fromFloat(newPos[1])).asFloat();
00537 
00538                         float heightDiff = newPos[2] - pos[2];
00539                         if (heightDiff < -2.0f) 
00540                         {
00541                                 return false; // Its lower
00542                         }
00543                         if (heightDiff > 2.0f) 
00544                         {
00545                                 ok = true;
00546                                 break;
00547                         }
00548                 }
00549                 if (!ok) return false;
00550         }
00551 
00552         return true;
00553 }
00554 
00555 bool TankAICurrentMove::makeMoveShot(Tank *tank, 
00556         TankAIWeaponSets::WeaponSet *weapons,
00557         std::list<Tank *> &sortedTanks)
00558 {
00559         if (!useFuel_) return false;
00560         if (sortedTanks.empty()) return false;
00561 
00562         Accessory *fuel = weapons->getTankAccessoryByType(tank, "fuel");
00563         if (!fuel) return false;
00564 
00565         ScorchedContext &context = ScorchedServer::instance()->getContext();
00566         WeaponMoveTank *moveWeapon = (WeaponMoveTank *)
00567                 context.getAccessoryStore().findAccessoryPartByAccessoryId(
00568                         fuel->getAccessoryId(), "WeaponMoveTank");
00569         if (moveWeapon)
00570         {
00571                 // Try to find a position to move to that we want to move to
00572                 // For the moment, just use the 1st target
00573                 Tank *target = sortedTanks.front();
00574                 Vector targetPos = target->getPosition().getTankPosition().asVector();
00575                 Vector tankPos = tank->getPosition().getTankPosition().asVector();
00576                 float totalDistance = MAX(100.0f, MIN(500.0f, (targetPos - tankPos).Magnitude() * 2.0f));
00577 
00578                 // Can we move to this target at all?
00579                 MovementMap mmap(
00580                         tank, 
00581                         context);
00582                 if (!mmap.calculatePosition(FixedVector::fromVector(targetPos), 
00583                         fixed::fromFloat(totalDistance))) return false;
00584                 float totalFuel = mmap.getFuel(moveWeapon).asFloat();
00585                 if (totalFuel <= 5) return false; // Stop it from moving very small amounts
00586 
00587                 // Calculate the path
00588                 MovementMap::MovementMapEntry entry =
00589                         mmap.getEntry((int) targetPos[0], (int) targetPos[1]);
00590                 if (entry.type != MovementMap::eMovement) return false;
00591 
00592                 // Work backward to the source point finding the nearest point we
00593                 // can actualy move to
00594                 while (entry.srcEntry)
00595                 {
00596                         unsigned int pt = entry.srcEntry;
00597                         unsigned int x = pt >> 16;
00598                         unsigned int y = pt & 0xffff;
00599 
00600                         Vector position((float) x, (float) y,
00601                                 ScorchedServer::instance()->getLandscapeMaps().getGroundMaps().getHeight(
00602                                         (int) x, (int) y).asFloat());
00603                         float distance = (position - targetPos).Magnitude();
00604                         if (distance > movementCloseness_)
00605                         {
00606                                 if (entry.dist.asFloat() < totalFuel)
00607                                 {
00608                                         // Move
00609                                         totalDamageBeforeMove_ = targets_.getTotalDamageTaken();
00610 
00611                                         // Move
00612                                         tank->getPosition().setSelectPosition((int) x, (int) y);
00613                                         setWeapon(tank, fuel);
00614                                         fireShot(tank);
00615 
00616                                         return true;
00617                                 }
00618                         }
00619 
00620                         entry = mmap.getEntry(x, y);
00621                 }       
00622         }
00623 
00624         return false;
00625 }
00626 
00627 struct GroupingEntry
00628 {
00629         Vector position;
00630         std::list<Tank *> targets;
00631         float totalDistance;
00632 };
00633 
00634 bool TankAICurrentMove::makeGroupShot(Tank *tank, 
00635         TankAIWeaponSets::WeaponSet *weapons,
00636         std::list<Tank *> &sortedTanks)
00637 {
00638         if (groupShotSize_ == 0) return false;
00639         Accessory *explosionhuge = weapons->getTankAccessoryByType(tank, "explosionhuge");
00640         if (!explosionhuge) return false;
00641         
00642         std::list<GroupingEntry> foundEntries;
00643         HeightMap &map = 
00644                 ScorchedServer::instance()->getLandscapeMaps().getGroundMaps().getHeightMap();
00645 
00646         int arenaX = ScorchedServer::instance()->getLandscapeMaps().
00647                 getGroundMaps().getArenaX();
00648         int arenaY = ScorchedServer::instance()->getLandscapeMaps().
00649                 getGroundMaps().getArenaY();
00650         int arenaWidth = ScorchedServer::instance()->getLandscapeMaps().
00651                 getGroundMaps().getArenaWidth();
00652         int arenaHeight = ScorchedServer::instance()->getLandscapeMaps().
00653                 getGroundMaps().getArenaHeight();
00654 
00655         // Braindead way of finding groupings
00656         // For each landscape square
00657         for (int y=arenaY; y<arenaY + arenaHeight; y+=4)
00658         {
00659                 for (int x=arenaX; x<arenaX + arenaWidth; x+=4)
00660                 {
00661                         GroupingEntry entry;
00662                         entry.position = Vector(float(x), float(y), map.getHeight(x, y).asFloat());
00663                         entry.totalDistance = 0.0f;
00664 
00665                         // Check this is not too near to us!
00666                         float distance = 
00667                                 (tank->getPosition().getTankPosition().asVector() - 
00668                                 entry.position).Magnitude();
00669                         if (distance < groupTargetDistance_ * 2.0f) continue;
00670                         
00671                         // Find all tanks near this position                    
00672                         std::list<Tank *>::iterator toItor;
00673                         for (toItor = sortedTanks.begin();
00674                                 toItor != sortedTanks.end();
00675                                 toItor++)
00676                         {
00677                                 Tank *to = *toItor;
00678 
00679                                 distance = 
00680                                         (to->getPosition().getTankPosition().asVector() - 
00681                                         entry.position).Magnitude();
00682                                 if (distance < groupTargetDistance_)
00683                                 {
00684                                         entry.totalDistance += distance;
00685                                         entry.targets.push_back(to);
00686                                 }
00687                         }
00688 
00689                         // Are there enough to warrent a shot
00690                         if (entry.targets.size() >= (unsigned int) groupShotSize_)
00691                         {
00692                                 foundEntries.push_back(entry);
00693                         }
00694                 }
00695         }
00696 
00697         // Find the best entry
00698         if (!foundEntries.empty())
00699         {
00700                 GroupingEntry *current = 0;
00701                 std::list<GroupingEntry>::iterator itor;
00702                 for (itor = foundEntries.begin();
00703                         itor != foundEntries.end();
00704                         itor++)
00705                 {
00706                         GroupingEntry &entry = *itor;
00707                         if (!current ||
00708                                 entry.targets.size() > current->targets.size() ||
00709                                 (entry.targets.size() == current->targets.size() &&
00710                                 entry.totalDistance < current->totalDistance))
00711                         {
00712                                 current = &entry;
00713                         }
00714                 }
00715 
00716                 if (current)
00717                 {
00718                         // Check for all angles to see if we can shoot at this tank
00719                         for (float degs=85.0f; degs>=45.0f; degs-=8.0f)
00720                         {
00721                                 // Check this angle
00722                                 Vector actualPosition;
00723                                 TankAIAimGuesser aimGuesser;
00724                                 if (aimGuesser.guess(tank, current->position, 
00725                                         degs, 15.0f, actualPosition))
00726                                 {
00727                                         setWeapon(tank, explosionhuge);
00728                                         fireShot(tank);
00729                                         return true;
00730                                 }
00731                         }
00732                 }
00733         }
00734 
00735         return false;
00736 }
00737 
00738 bool TankAICurrentMove::useAvailableBatteries(Tank *tank)
00739 {
00740         // Use batteries
00741         bool result = false;
00742         while (tank->getLife().getLife() < 
00743                 tank->getLife().getMaxLife() &&
00744                 tank->getAccessories().getBatteries().canUse())
00745         {
00746                 std::list<Accessory *> &entries =
00747                         tank->getAccessories().getAllAccessoriesByType(
00748                                 AccessoryPart::AccessoryBattery);                       
00749                 if (!entries.empty())
00750                 {
00751                         useBattery(tank, entries.front()->getAccessoryId());
00752                         result = true;
00753                 }
00754         }
00755         return result;
00756 }
00757 
00758 Vector TankAICurrentMove::lowestHighest(TankAICurrentMoveWeapons &weapons, 
00759         Vector &directTarget, bool highest)
00760 {
00761         float radius = weapons.shield->getBoundingSize().asFloat() + 2.0f;
00762         Vector bestPos = directTarget;
00763         bestPos[1] += radius;
00764         bestPos[2] = 
00765                 ScorchedServer::instance()->getLandscapeMaps().
00766                         getGroundMaps().getInterpHeight(
00767                                 fixed::fromFloat(bestPos[0]), fixed::fromFloat(bestPos[1])).asFloat();
00768         for (float a=0.0f; a<360.0f; a+=22.5f)
00769         {
00770                 float offSetX = sinf(a / 180.0f * PI) * radius;
00771                 float offSetY = cosf(a / 180.0f * PI) * radius;
00772                 
00773                 Vector newPos(
00774                         directTarget[0] + offSetX,
00775                         directTarget[1] + offSetY);
00776                 newPos[2] =
00777                         ScorchedServer::instance()->getLandscapeMaps().
00778                                 getGroundMaps().getInterpHeight(
00779                                         fixed::fromFloat(newPos[0]), fixed::fromFloat(newPos[1])).asFloat();
00780 
00781                 if (highest)
00782                 {
00783                         if (newPos[2] > bestPos[2]) bestPos = newPos;
00784                 }
00785                 else
00786                 {
00787                         if (newPos[2] < bestPos[2]) bestPos = newPos;
00788                 }
00789         }
00790         return bestPos;
00791 }
00792 
00793 float TankAICurrentMove::getShotDistance(Tank *tank, bool projectile)
00794 {
00795         // Try to find an existing record
00796         std::map<Tank *, ShotRecord>::iterator itor = 
00797                 shotRecords_.find(tank);
00798         if (itor == shotRecords_.end())
00799         {
00800                 if (projectile) return projectileStartDistance_;
00801                 else return sniperStartDistance_;
00802         }
00803         else
00804         {
00805                 if (projectile) return itor->second.projectileCurrentDistance;
00806                 else return itor->second.sniperCurrentDistance;
00807         }
00808 }
00809 
00810 void TankAICurrentMove::shotAtTank(Tank *tank, bool projectile, float newDistance)
00811 {
00812         targets_.shotAt(tank);
00813 
00814         // Try to find an existing record
00815         std::map<Tank *, ShotRecord>::iterator itor = 
00816                 shotRecords_.find(tank);
00817         if (itor == shotRecords_.end())
00818         {
00819                 // Create one
00820                 ShotRecord record;
00821                 record.projectileCurrentDistance = projectileStartDistance_; 
00822                 record.sniperCurrentDistance = sniperStartDistance_;
00823                 record.position = tank->getPosition().getTankPosition().asVector();
00824                 shotRecords_[tank] = record;
00825         }
00826 
00827         // Update the new record with the details about the current shot
00828         ShotRecord &record = shotRecords_[tank];
00829         float distance = (record.position - tank->getPosition().getTankPosition().asVector()).Magnitude();
00830         float distanceDec = 0.0f;
00831         if (distance > 5.0f)
00832         {
00833                 distanceDec = MIN(distance - 5.0f, 20.0f) / 20.0f;
00834         }
00835 
00836         record.position = tank->getPosition().getTankPosition().asVector();
00837         if (projectile)
00838         {
00839                 record.projectileCurrentDistance = newDistance;
00840 
00841                 float decrement = 
00842                         projectileMinDecrement_ +
00843                         RAND * (projectileMaxDecrement_ - projectileMinDecrement_);
00844                 record.projectileCurrentDistance = 
00845                         MAX(projectileEndDistance_, record.projectileCurrentDistance - decrement);                              
00846 
00847                 distanceDec *= projectileMovementFactor_;
00848                 record.projectileCurrentDistance = 
00849                         MIN(projectileStartDistance_, record.projectileCurrentDistance + distanceDec);  
00850         }
00851         else 
00852         {
00853                 float decrement = 
00854                         sniperMinDecrement_ +
00855                         RAND * (sniperMaxDecrement_ - sniperMinDecrement_);
00856                 record.sniperCurrentDistance = 
00857                         MAX(sniperEndDistance_, record.sniperCurrentDistance - decrement);      
00858 
00859                 distanceDec *= sniperMovementFactor_;
00860                 record.sniperCurrentDistance = 
00861                         MIN(sniperStartDistance_, record.sniperCurrentDistance + distanceDec);  
00862         }
00863 }
00864 
00865 void TankAICurrentMove::setWeapon(Tank *tank, Accessory *accessory)
00866 {
00867         tank->getAccessories().getWeapons().setWeapon(accessory);
00868 }
00869 
00870 void TankAICurrentMove::skipMove(Tank *tank)
00871 {
00872         ComsPlayedMoveMessage *message = 
00873                 new ComsPlayedMoveMessage(tank->getPlayerId(), ComsPlayedMoveMessage::eSkip);
00874         ServerShotHolder::instance()->addShot(tank->getPlayerId(), message);
00875 }
00876 
00877 void TankAICurrentMove::resign(Tank *tank)
00878 {
00879         ComsPlayedMoveMessage *message = 
00880                 new ComsPlayedMoveMessage(tank->getPlayerId(), ComsPlayedMoveMessage::eResign);
00881         ServerShotHolder::instance()->addShot(tank->getPlayerId(), message);
00882 }
00883 
00884 void TankAICurrentMove::fireShot(Tank *tank)
00885 {
00886         Accessory *currentWeapon = 
00887                 tank->getAccessories().getWeapons().getCurrent();
00888         if (currentWeapon)
00889         {
00890                 ComsPlayedMoveMessage *message = 
00891                         new ComsPlayedMoveMessage(tank->getPlayerId(), ComsPlayedMoveMessage::eShot);
00892                 message->setShot(
00893                         currentWeapon->getAccessoryId(),
00894                         tank->getPosition().getRotationGunXY(),
00895                         tank->getPosition().getRotationGunYZ(),
00896                         tank->getPosition().getPower(),
00897                         tank->getPosition().getSelectPositionX(),
00898                         tank->getPosition().getSelectPositionY());
00899         
00900                 if (!ServerShotHolder::instance()->addShot(tank->getPlayerId(), message))
00901                 {
00902                         Logger::log(S3D::formatStringBuffer("AI %u failed to make a shot, shot refused",
00903                                 tank->getPlayerId()));
00904                         skipMove(tank);
00905                 }
00906         }
00907 }
00908 
00909 void TankAICurrentMove::useBattery(Tank *tank, unsigned int batteryId)
00910 {
00911         ComsDefenseMessage defenseMessage(
00912                 tank->getPlayerId(),
00913                 ComsDefenseMessage::eBatteryUse,
00914                 batteryId);
00915 
00916         ServerDefenseHandler::instance()->processDefenseMessage(defenseMessage, tank);
00917 }

Generated on Mon Feb 16 15:14:53 2009 for Scorched3D by  doxygen 1.5.3