Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List

Battle.cpp

00001 /*
00002 Copyright 2003 - 2005 Elliott Kleinrock, Dan Neely, Kurt W. Over, Damon Domjan
00003 
00004 This file is part of FreeStars, a free clone of the Stars! game.
00005 
00006 FreeStars 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 FreeStars 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 FreeStars; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 The full GPL Copyright notice should be in the file COPYING.txt
00021 
00022 Contact:
00023 Email Elliott at 9jm0tjj02@sneakemail.com
00024 */
00025 
00026 #include "FSServer.h"
00027 
00028 #include "Battle.h"
00029 #include "Hull.h"
00030 
00031 #ifdef _DEBUG
00032 #define new DEBUG_NEW
00033 #endif
00034 
00035 long** Battle::StartX = NULL;
00036 long** Battle::StartY = NULL;
00037 
00038 
00039 Battle::Battle(const Location & loc)
00040 :       Location(loc), mBaseFight(false)
00041 {
00042         if (StartX == NULL) {
00043                 SetStartPos();
00044         }
00045 }
00046 
00047 Battle::~Battle()
00048 {
00049 }
00050 
00051 void Battle::Cleanup()
00052 {
00053         if (StartX != NULL) {
00054                 for (unsigned int i = 0; i < TheGame->NumberPlayers() - 1; ++i) {
00055                         delete StartX[i];
00056                         delete StartY[i];
00057                 }
00058 
00059                 delete [] StartX;
00060                 delete [] StartY;
00061         }
00062 }
00063 
00064 void Battle::SetStartPos()
00065 {
00066         StartX = new long*[TheGame->NumberPlayers() - 1];
00067         StartY = new long*[TheGame->NumberPlayers() - 1];
00068         for (unsigned int i = 0; i < TheGame->NumberPlayers() - 1; ++i) {
00069                 StartX[i] = new long[i+2];
00070                 StartY[i] = new long[i+2];
00071                 for (unsigned int j = 0; j < i+2; ++j) {
00072                         StartX[i][j] = 1;
00073                         StartY[i][j] = 1;
00074                 }
00075         }
00076 // leave as 'unfinished' will generate a warning to this line, easier for ide to jump right here
00077 #pragma unfinished(__FILE__ " : SetStartPos needs to be written still!")
00078 }
00079 
00080 void Battle::AddFleet(Fleet * fleet)
00081 {
00082         mThere.push_back(fleet);
00083         fleet->SetInBattle(false);
00084         fleet->SetAlreadyFought();
00085 }
00086 
00087 void Battle::AddFleets()
00088 {
00089         Player * player;
00090         for (int i = 1; i <= TheGame->NumberPlayers(); ++i) {
00091                 player = TheGame->NCGetPlayer(i);
00092                 player->AddBattleFleets(this);
00093         }
00094 }
00095 
00096 void Battle::Resolve()
00097 {
00098         bool Combat = false;    // will combat happen?
00099         // First, see who is fighting
00100         deque<Fleet *>::iterator if1, if2;
00101         unsigned long i, j;
00102         for (i = 1; i <= TheGame->NumberPlayers(); ++i)
00103                 TheGame->NCGetPlayer(i)->ClearBattleEnemies();
00104 
00105         for (if1 = mThere.begin(); if1 != mThere.end(); ++if1) {
00106                 if ((*if1)->CanShoot() && (*if1)->GetBattlePlan()->GetEnemy() != BPE_NONE) {
00107                         if (mBPlanet && mBPlanet->GetBaseNumber() >= 0) {
00108                                 if ((*if1)->GetBattlePlan()->WillFight((*if1)->GetOwner(), mBPlanet->GetOwner())) {
00109                                         // set both races to fight each other
00110                                         (*if1)->NCGetOwner()->SetBattleEnemy(mBPlanet->GetOwner()->GetID());
00111                                         mBPlanet->NCGetOwner()->SetBattleEnemy((*if1)->GetOwner()->GetID());
00112                                         Combat = true;
00113                                         mBaseFight = true;
00114                                         mBPlanet->SetInBattle();
00115                                 }
00116                         }
00117 
00118                         for (if2 = mThere.begin(); if2 != mThere.end(); ++if2) {
00119                                 if (*if1 == *if2)
00120                                         continue;
00121 
00122                                 if ((*if1)->GetBattlePlan()->WillFight((*if1)->GetOwner(), (*if2)->GetOwner())) {
00123                                         // set both races to fight each other
00124                                         (*if1)->NCGetOwner()->SetBattleEnemy((*if2)->GetOwner()->GetID());
00125                                         (*if2)->NCGetOwner()->SetBattleEnemy((*if1)->GetOwner()->GetID());
00126                                         Combat = true;
00127                                 }
00128                         }
00129                 }
00130         }
00131 
00132         if (!Combat)    // if no combat here, we're done
00133                 return;
00134 
00135         // Battle report xml object
00136         bReport = new TiXmlElement("BattleReport");
00137 
00138         long Races = 0; // number of races in this battle
00139         for (i = 0; i < TheGame->NumberPlayers(); ++i) {
00140                 if (TheGame->GetPlayer(i)->InThisBattle())
00141                         Races++;
00142         }
00143 
00144         double Dampener = 0;
00145         long Race = 0;
00146         Player * p1 = NULL;
00147 
00148         // if this player is in the battle, set all their fleets at the same location to be there
00149         // Note: you're in the battle if you attack anyone there, or if anyone there attacks you
00150         // also setup stacks
00151         for (if1 = mThere.end(); if1 != mThere.begin(); --if1) {
00152                 if ((*if1)->GetOwner()->InThisBattle()) {
00153                         if (p1 != (*if1)->GetOwner()) {
00154                                 p1 = (*if1)->NCGetOwner();
00155                                 ++Race;
00156                         }
00157 
00158                         (*if1)->SetInBattle(true);
00159                         if ((*if1)->GetMaxDampener() > Dampener)
00160                                 Dampener = (*if1)->GetMaxDampener();
00161 
00162                         if (mBaseFight && mBPlanet && mBPlanet->GetOwner() == p1) {
00163                                 // add a 'stack' for the base
00164                                 Stack * s = new Stack();
00165 
00166                                 s->SetupBase(mBPlanet);
00167                                 s->bx = StartX[Races-2][Race-1];
00168                                 s->by = StartY[Races-2][Race-1];
00169                                 mFighting.push_back(s);
00170                         }
00171 
00172                         // add this fleets stacks to the list, set battle variables
00173                         for (j = 0; j < (*if1)->mStacks.size(); ++j) {
00174                                 Stack * s = &((*if1)->mStacks[i]);
00175                                 s->SetupShips(p1, (*if1)->GetCargoMass() / (*if1)->GetCargoCapacity() * s->mShip->GetCargoCapacity());
00176                                 s->bx = StartX[Races-2][Race-1];
00177                                 s->by = StartY[Races-2][Race-1];
00178                                 mFighting.push_back(s);
00179                         }
00180                 }
00181         }
00182         assert(Races == Race);
00183 
00184         // Sort by mass
00185         sort(mFighting.begin(), mFighting.end(), StackLighter);
00186 
00187         TiXmlElement Stacks("StartingStacks");
00188 
00189         // Apply energy dampener
00190         for (j = 0; j < mFighting.size(); ++j) {
00191                 if (!mFighting[j]->IsBase()) {
00192                         mFighting[j]->bSpeed -= long(Dampener * 4);
00193                         if (mFighting[j]->bSpeed < 2)
00194                                 mFighting[j]->bSpeed = 2;
00195                 }
00196 
00197                 TiXmlElement stack("Stack");
00198                 stack.SetAttribute("IDNumber", j);
00199                 mFighting[j]->WriteNode(&stack);
00200                 Stacks.InsertEndChild(stack);
00201         }
00202 
00203         bReport->InsertEndChild(Stacks);
00204 
00205         // everyone in the battle gets to see all designs
00206         for (i = 0; i < TheGame->NumberPlayers(); ++i) {
00207                 p1 = TheGame->NCGetPlayer(i);
00208                 if (p1->InThisBattle()) {
00209                         for (j = 0; j < mFighting.size(); ++j) {
00210                                 Player * o = mFighting[j]->GetFleetIn()->NCGetOwner();
00211                                 if (o != p1)
00212                                         o->SetSeenDesign(p1->GetID(), o->GetShipNumber(mFighting[j]->mShip), mFighting[j]->IsBase());
00213                         }
00214                 }
00215         }
00216 
00217         // setup the slot firing order
00218         for (j = 0; j < mFighting.size(); ++j) {
00219                 for (i = mFighting[j]->mShip->GetHull()->GetNumberSlots(); i >= 0; --i) {
00220                         if (mFighting[j]->mShip->GetSlot(i).GetComp()->GetType() == CT_WEAPON) {
00221                                 mSlots.push_back(bSlot(&(mFighting[j]->mShip->GetSlot(i)), mFighting[j]));
00222                         }
00223                 }
00224         }
00225 
00226         // Sort by init
00227         sort(mSlots.begin(), mSlots.end(), HighInit);
00228 
00229         // fight the battle
00230         for (mRound = 1; mRound <= 16; ++mRound) {
00231                 if (!FightRound())
00232                         break;
00233         }
00234 
00235 
00236 
00237         // finally, send a message to all players at the location
00238         for (i = 0; i < TheGame->NumberPlayers(); ++i) {
00239                 p1 = TheGame->NCGetPlayer(i);
00240                 if (p1->InThisBattle()) {
00241                         Message * mess = p1->AddMessage("Battle");
00242                         if (mBPlanet != NULL)
00243                                 mess->AddItem("Battle Location", mBPlanet);
00244                         else
00245                                 mess->AddItem("Battle Location", this);
00246                 } else {
00247                         Message * mess = p1->AddMessage("Battle, Not involved");
00248                         if (mBPlanet != NULL)
00249                                 mess->AddItem("Battle Location", mBPlanet);
00250                         else
00251                                 mess->AddItem("Battle Location", this);
00252                 }
00253         }
00254 
00255         // delete the base 'stack'
00256         if (mBaseFight) {
00257                 for (j = 0; j < mFighting.size(); ++j) {
00258                         if (mFighting[j]->IsBase()) {
00259                                 delete mFighting[j];
00260                                 mFighting[j] = NULL;
00261                         }
00262                 }
00263         }
00264 }
00265 
00266 bool Battle::HighInit(const bSlot& s1, const bSlot& s2)
00267 {
00268         if (s1.init > s2.init)
00269                 return true;
00270         else if (s1.init == s2.init) {
00271                 if (s1.stack == s2.stack)
00272                         return s1.slot->GetPosition() < s2.slot->GetPosition();
00273                 else {
00274                         while (s1.stack->bRandInit == 0)
00275                                 s1.stack->bRandInit = genrand_int32();
00276 
00277                         if (s2.stack->bRandInit == 0) {
00278                                 while (s2.stack->bRandInit == 0 || s1.stack->bRandInit == s2.stack->bRandInit)
00279                                         s2.stack->bRandInit = genrand_int32();
00280                         }
00281 
00282                         return s1.stack->bRandInit > s2.stack->bRandInit;       // ties go to s1
00283                 }
00284         } else
00285                 return false;
00286 }
00287 
00288 bool Battle::FightRound()
00289 {
00290         bool fought = false;
00291         int i, j;
00292 
00293         // Get MAT (Most Attractive Target) for every stack
00294         for (j = 0; j < mFighting.size(); ++j) {
00295                 mFighting[j]->bMAT = NULL;
00296                 if (mFighting[j]->bShips > 0 && mFighting[j]->bPlan != BPT_DISENGAGE) {
00297                         mFighting[j]->bMAT = GetTarget(mFighting[j], mFighting[j]->GetFleetIn()->GetBattlePlan()->GetPrimary(), 0, NULL);
00298                         if (mFighting[j]->bMAT == NULL)
00299                                 mFighting[j]->bMAT = GetTarget(mFighting[j], mFighting[j]->GetFleetIn()->GetBattlePlan()->GetSecondary(), 0, NULL);
00300                 }
00301         }
00302 
00303         // Move first
00304         for (i = 3; i > 0; --i) {
00305                 for (j = 0; j < mFighting.size(); ++j) {
00306                         if (mFighting[j]->bShips > 0 && GetSpeed(mFighting[j]->bSpeed, mRound) >= i)
00307                                 if (MoveStack(j))
00308                                         fought = true;
00309                 }
00310         }
00311         
00312         if (!fought)    // no one has a target, stop fighting
00313                 return fought;
00314 
00315         for (j = 0; j < mSlots.size(); ++j) {
00316                 if (mSlots[j].stack->bShips > 0)
00317                         ShootSlot(mSlots[j]);
00318         }
00319 
00320         return fought;
00321 }
00322 
00323 long Battle::GetRange(const Stack * s1, const Stack * s2, int dx/* = 0*/, int dy/* = 0*/) const
00324 {
00325         return max(abs(s1->bx + dx - s2->bx), abs(s1->by + dy - s2->by));
00326 }
00327 
00328 long Battle::GetSpeed(long speed, long round)
00329 {
00330         switch (speed % 4) {
00331         case 0:
00332         default:        // default should never happen, added to remove warning
00333                 return speed / 4;
00334         case 1:
00335                 return speed / 4 + round % 4 == 1 ? 1 : 0;
00336         case 2:
00337                 return speed / 4 + round % 2 == 1 ? 1 : 0;
00338         case 3:
00339                 return speed / 4 + round % 4 == 3 ? 0 : 1;
00340         }
00341 }
00342 
00343 // move ship, return true if this ship is trying to shoot someone, and wants the battle to continue
00344 bool Battle::MoveStack(long s)
00345 {
00346         int dx, dy;
00347         long DamDone[3][3] = {{0,0,0},{0,0,0},{0,0,0}};
00348         long DamTake[3][3] = {{0,0,0},{0,0,0},{0,0,0}};
00349         long Range;
00350         long DDRange = 0;       // Damage done at this range, to limit recalcing damage at the same range
00351         long DTRange = 0;       // Damage taken at this range, to limit recalcing damage at the same range
00352 
00353         // For every stack in the battle
00354         for (int j = 0; j < mFighting.size(); ++j) {
00355                 // check moving +- 1
00356                 Range = -1;
00357                 for (dx = -1; dx <= 1; ++dx) for (dy = -1; dy < 1; ++dy) if (dx+mFighting[s]->bx >= 0 && dx+mFighting[s]->bx <= 9 && dy+mFighting[s]->by >= 0 && dy+mFighting[s]->by <= 9) {
00358                         // if we can shoot and want to shoot this stack
00359                         if (mFighting[s]->mShip->CanShoot() &&
00360                                 mFighting[s]->GetFleetIn()->GetOwner()->GetBattleEnemy(mFighting[j]->GetFleetIn()->GetOwner()->GetID()) &&
00361                                 (mFighting[j]->mShip->IsBattleTarget(mFighting[s]->GetFleetIn()->GetBattlePlan()->GetPrimary()) ||
00362                                         mFighting[j]->mShip->IsBattleTarget(mFighting[s]->GetFleetIn()->GetBattlePlan()->GetSecondary())))
00363                         {
00364                                 if (Range != GetRange(mFighting[s], mFighting[j], dx, dy)) {
00365                                         Range = GetRange(mFighting[s], mFighting[j], dx, dy);
00366                                         DDRange = PotentialDamage(mFighting[s], mFighting[j], Range, NULL);
00367                                         DDRange /= mFighting[j]->mShip->IsBattleTarget(mFighting[s]->GetFleetIn()->GetBattlePlan()->GetPrimary()) ? 1 : 10;
00368                                         DDRange *= mFighting[j]->mShip->GetAttractiveCost(mFighting[j]->GetFleetIn()->GetOwner()) * mFighting[j]->mShips / mFighting[j]->DP();
00369                                 }
00370                                 DamDone[dx+1][dy+1] = max(DamDone[dx+1][dy+1], DDRange);
00371                         }
00372                         // if they can shoot and want to shoot us
00373                         if (mFighting[j]->mShip->CanShoot() &&
00374                                 mFighting[j]->GetFleetIn()->GetOwner()->GetBattleEnemy(mFighting[s]->GetFleetIn()->GetOwner()->GetID()) &&
00375                                 (mFighting[s]->mShip->IsBattleTarget(mFighting[j]->GetFleetIn()->GetBattlePlan()->GetPrimary()) ||
00376                                         mFighting[s]->mShip->IsBattleTarget(mFighting[j]->GetFleetIn()->GetBattlePlan()->GetSecondary())))
00377                         {
00378                                 if (Range != GetRange(mFighting[s], mFighting[j], dx, dy)) {
00379                                         Range = GetRange(mFighting[s], mFighting[j], dx, dy);
00380                                         DTRange = PotentialDamage(mFighting[j], mFighting[s], Range, NULL);
00381                                         DTRange *= mFighting[s]->mShip->GetAttractiveCost(mFighting[s]->GetFleetIn()->GetOwner()) * mFighting[s]->mShips / mFighting[s]->DP();
00382                                 }
00383                                 DamTake[dx+1][dy+1] += DTRange;
00384                         }
00385                 }
00386         }
00387 
00388         double bestP, bestD;
00389         int count = 0;
00390         long mdx = 0, mdy = 0;
00391         double di;
00392         switch (mFighting[s]->bPlan) {
00393         // do disengage later because other orders can become disengage
00394         default:
00395                 break;  // unknown plan, don't move
00396         case BPT_DISIFHIT:
00397         case BPT_MINDAM:
00398                 bestP = DamTake[0][0];
00399                 bestD = DamDone[0][0];
00400                 for (dx = -1; dx <= 1; ++dx) for (dy = -1; dy < 1; ++dy) {
00401                         if ((bestD == 0 && DamDone[dx+1][dy+1] > 0) ||
00402                                 (bestD > 0 && DamDone[dx+1][dy+1] > 0 && bestP < DamTake[dx+1][dy+1]) ||
00403                                 (bestD < DamDone[dx+1][dy+1] && bestP == DamTake[dx+1][dy+1]))
00404                         {
00405                                 bestP = DamTake[dx+1][dy+1];
00406                                 bestD = DamDone[dx+1][dy+1];
00407                                 count = 0;
00408                         } else if (dx > -1 || dy > -1 && (bestD == DamDone[dx+1][dy+1] && bestP == DamTake[dx+1][dy+1])) {
00409                                 ++count;
00410                                 if (Random(count) == 0) {
00411                                         mdx = dx;
00412                                         mdy = dy;
00413                                 }
00414                         }
00415                 }
00416                 break;
00417         case BPT_MAXNET:
00418                 bestP = DamDone[0][0] - DamTake[0][0];
00419                 for (dx = -1; dx <= 1; ++dx) for (dy = -1; dy < 1; ++dy) {
00420                         di = DamDone[dx+1][dy+1] - DamTake[dx+1][dy+1];
00421                         if (bestP > di) {
00422                                 bestP = di;
00423                                 count = 0;
00424                         } else if (dx > -1 || dy > -1 && bestP == di) {
00425                                 ++count;
00426                                 if (Random(count) == 0) {
00427                                         mdx = dx;
00428                                         mdy = dy;
00429                                 }
00430                         }
00431                 }
00432                 break;
00433         case BPT_MAXRATIO:
00434                 bestP = double(DamDone[0][0]) / double(DamTake[0][0]);
00435                 for (dx = -1; dx <= 1; ++dx) for (dy = -1; dy < 1; ++dy) {
00436                         di = double(DamDone[dx+1][dy+1]) / double(DamTake[dx+1][dy+1]);
00437                         if (bestP > di) {
00438                                 bestP = di;
00439                                 count = 0;
00440                         } else if (dx > -1 || dy > -1 && bestP == di) {
00441                                 ++count;
00442                                 if (Random(count) == 0) {
00443                                         mdx = dx;
00444                                         mdy = dy;
00445                                 }
00446                         }
00447                 }
00448                 break;
00449         case BPT_MAXDAM:
00450                 bestP = DamDone[0][0];
00451                 for (dx = -1; dx <= 1; ++dx) for (dy = -1; dy < 1; ++dy) {
00452                         if (bestP > DamDone[dx+1][dy+1]) {
00453                                 bestP = DamDone[dx+1][dy+1];
00454                                 count = 0;
00455                         } else if (dx > -1 || dy > -1 && bestP == DamDone[dx+1][dy+1]) {
00456                                 ++count;
00457                                 if (Random(count) == 0) {
00458                                         mdx = dx;
00459                                         mdy = dy;
00460                                 }
00461                         }
00462                 }
00463                 break;
00464         }
00465 
00466         if (mFighting[s]->bPlan != BPT_DISENGAGE && DamDone[mdx+1][mdy+1] == 0 && mFighting[s]->mShip->CanShoot()) {
00467                 // alternate move: if we can't yet do damage and want to, find most attractive target, and close with it
00468                 count = 1;
00469                 const Stack * target;
00470                 target = GetTarget(mFighting[s], mFighting[s]->GetFleetIn()->GetBattlePlan()->GetPrimary(), 0, NULL);
00471                 if (target == NULL)
00472                         target = GetTarget(mFighting[s], mFighting[s]->GetFleetIn()->GetBattlePlan()->GetSecondary(), 0, NULL);
00473                 if (target != NULL) {
00474                         if (target->bx < mFighting[s]->bx)
00475                                 mdx = -1;
00476                         else if (target->bx > mFighting[s]->bx)
00477                                 mdx = 1;
00478                         else
00479                                 mdx = 0;
00480 
00481                         if (target->by < mFighting[s]->by)
00482                                 mdy = -1;
00483                         else if (target->by > mFighting[s]->by)
00484                                 mdy = 1;
00485                         else
00486                                 mdy = 0;
00487                 } else {
00488                         mFighting[s]->bPlan = BPT_DISENGAGE;    // if there are no targets, flee
00489                         AddLong(bReport, "StackDisengages", s);
00490                 }
00491         }
00492 
00493         // disengage here because other orders can become disengage
00494         if (mFighting[s]->bPlan == BPT_DISENGAGE) {
00495                 bestP = DamTake[0][0];
00496                 for (dx = -1; dx <= 1; ++dx) for (dy = -1; dy < 1; ++dy) {
00497                         if (bestP < DamTake[dx+1][dy+1]) {
00498                                 bestP = DamTake[dx+1][dy+1];
00499                                 count = 0;
00500                         } else if (dx > -1 || dy > -1 && bestP == DamTake[dx+1][dy+1]) {
00501                                 ++count;
00502                                 if (Random(count) == 0) {
00503                                         mdx = dx;
00504                                         mdy = dy;
00505                                 }
00506                         }
00507                 }
00508         }
00509 
00510         mFighting[s]->bx += mdx;
00511         mFighting[s]->by += mdy;
00512 
00513         // add move to battle report
00514         TiXmlElement move("StackMove");
00515         AddLong(&move, "XCoord", mFighting[s]->bx);
00516         AddLong(&move, "YCoord", mFighting[s]->by);
00517         bReport->InsertEndChild(move);
00518 
00519         // stop fighting if no one has a target
00520         return  (mFighting[s]->bPlan == BPT_DISENGAGE);
00521 }
00522 
00523 const Stack * Battle::GetTarget(const Stack * hunter, HullType hc, long Range, const Slot * slot) const
00524 {
00525         double attract = 0.0;
00526         double attemp;
00527         const Stack * Result = NULL;
00528         long count = 0;
00529         long tRange = Range;
00530 
00531         for (int j = 0; j < mFighting.size(); ++j) {
00532                 // if I don't like the owner, and it's a valid target, calc attractiveness
00533                 if (hunter->GetFleetIn()->GetOwner()->GetBattleEnemy(mFighting[j]->GetFleetIn()->GetOwner()->GetID()) &&
00534                         mFighting[j]->mShip->IsBattleTarget(hc))
00535                 {
00536                         if (Range == -1)
00537                                 tRange = this->GetRange(hunter, mFighting[j]);
00538 
00539                         // check attractiveness: damage / dp * cost of stack
00540                         attemp = mFighting[j]->mShip->GetAttractiveCost(mFighting[j]->GetFleetIn()->GetOwner())
00541                                         * mFighting[j]->mShips
00542                                         * PotentialDamage(hunter, mFighting[j], tRange, slot)
00543                                         / mFighting[j]->DP();
00544 
00545                         if (attemp > attract) {
00546                                 attract = attemp;
00547                                 count = 1;
00548                                 Result = mFighting[j];
00549                         } else if (attemp == attract) {
00550                                 ++count;
00551                                 if (Random(count) == 0)
00552                                         Result = mFighting[j];
00553                         }
00554                 }
00555         }
00556 
00557         return Result;
00558 }
00559 
00560 // calculate the potential damage when one stack shoots at another,
00561 // this function doesn't consider other ships firing
00562 // If slot is null it calcs the whole ship, if slot is specified, it just calcs for that slot
00563 // Gatling flag: false-> return damage from all weapons, including gatlings, true-> return just gatling damage
00564 long Battle::PotentialDamage(const Stack * shoot, const Stack * target, long Range, const Slot * slot, bool Gatling/*= false*/) const
00565 {
00566         long Damage = 0;
00567         long Sapper = 0;
00568         double temp;
00569         const Slot * curSlot;
00570 
00571         for (int i = 0; i < mSlots.size(); ++i) {
00572                 if (slot == NULL)
00573                         curSlot = mSlots[i].slot;
00574                 else
00575                         curSlot = slot;
00576 
00577                 if (slot != NULL || mSlots[i].stack == shoot) {
00578                         if (curSlot->GetComp()->GetType() == CT_WEAPON && (curSlot->GetComp()->GetRange() + shoot->IsBase()?1:0) <= Range) {
00579                                 if (Gatling && curSlot->GetComp()->GetWeaponType() != WT_GATLING)
00580                                         continue;
00581 
00582                                 temp = curSlot->GetComp()->GetPower() * curSlot->GetCount() * shoot->bShips;
00583                                 switch (curSlot->GetComp()->GetWeaponType()) {
00584                                 case WT_BEAM:
00585                                 case WT_SAPPER:
00586                                 case WT_GATLING:
00587                                         temp *= shoot->mShip->GetCapacitors() * target->mShip->GetDeflection();
00588                                         temp *= 1.0 - 0.1 * double(Range) / (curSlot->GetComp()->GetRange() + shoot->IsBase()?1:0);
00589                                         if (curSlot->GetComp()->GetWeaponType() == WT_SAPPER)
00590                                                 Sapper += max(long(temp), target->bShield - Sapper);
00591                                         else {
00592                                                 if (target->bShield <= Sapper)
00593                                                         Damage += long(temp);
00594                                                 else {
00595                                                         Damage += max(0L, long(temp) - (target->bShield - Sapper));
00596                                                         Sapper += min(long(temp), (target->bShield - Sapper));
00597                                                 }
00598                                         }
00599                                         break;
00600                                 case WT_TORP:
00601                                 case WT_MISSILE:
00602                                         {
00603                                                 long temp2;
00604                                                 double Acc = shoot->mShip->GetAccuracy(*curSlot->GetComp(), target->mShip);
00605                                                 temp *= Acc;
00606                                                 if (curSlot->GetComp()->GetWeaponType() == WT_MISSILE && target->bShield <= Sapper)
00607                                                         Damage += long(temp);
00608                                                 temp2 = max(long(temp/2), (target->bShield - Sapper));
00609                                                 Sapper += temp2;
00610                                                 Sapper += long((1.0 - Acc) * temp / 8); // misses act like sappers
00611                                                 Damage += long(temp) - temp2;
00612                                         }
00613                                         break;
00614                                 default:
00615                                         break;
00616                                 }
00617                         }
00618                 }
00619 
00620                 Sapper = min(Sapper, target->bShield);  // don't claim more sapper damage then the target has shields
00621                 if (slot != NULL)
00622                         break;
00623         }
00624 
00625         return min(Sapper+Damage, target->DP());        // don't claim more total damage then the target has
00626 }
00627 
00628 void Battle::ShootSlot(bSlot & shooter)
00629 {
00630         /*
00631         bool done = false;
00632 
00633 
00634         const Stack * target;
00635         long Range;
00636 
00637         long damage;
00638         if (curSlot->GetComp()->GetWeaponType() & (WT_BEAM | WT_SAPPER)) {
00639                 // apply capacitors
00640                 damage = shooter.slot->GetComp()->GetPower() * shooter.slot->GetCount() * shooter.stack->bShips;
00641                 damage *= shooter.stack->mShip->GetCapacitors();
00642         }
00643 
00644         while (!done) {
00645                 target = GetTarget(shooter.stack, shooter.stack->GetFleetIn()->GetBattlePlan()->GetPrimary(), -1, shooter.slot);
00646                 if (target == NULL)
00647                         target = GetTarget(shooter.stack, shooter.stack->GetFleetIn()->GetBattlePlan()->GetSecondary(), -1, shooter.slot);
00648                 if (target == NULL) {
00649                         done = true;
00650                         break;
00651                 }
00652 
00653                 Range = GetRange(shooter.stack, target);
00654                 assert(shooter.slot->GetComp()->GetRange() + shooter.stack->IsBase()?1:0) <= Range);
00655 
00656                 if (curSlot->GetComp()->GetWeaponType() & (WT_BEAM | WT_SAPPER | WT_GATLING)) {
00657                         // apply deflectors of target ship
00658                         long tdam = damage * target->mShip->GetDeflection();
00659 
00660                         // first hit shields
00661                         sdam = min(tdam, target->bShield);
00662                         target->bShield -= sdam;
00663                         tdam -= sdam;
00664 
00665                         // then hit armor
00666                         if (curSlot->GetComp()->GetWeaponType() & (WT_BEAM | WT_SAPPER | WT_GATLING)) {
00667                 }
00668 
00669                 
00670                 switch (curSlot->GetComp()->GetWeaponType()) {
00671                 case WT_BEAM:
00672                 case WT_SAPPER:
00673                 case WT_GATLING:
00674                         temp *= shoot->mShip->GetCapacitors() * target->mShip->GetDeflection();
00675                         temp *= 1.0 - 0.1 * double(Range) / (curSlot->GetComp()->GetRange() + shoot->IsBase()?1:0);
00676                         if (curSlot->GetComp()->GetWeaponType() == WT_SAPPER)
00677                                 Sapper += long(temp);
00678                         else
00679                                 Damage += long(temp);
00680                         break;
00681                 case WT_TORP:
00682                 case WT_MISSILE:
00683                         {
00684                                 double Acc = shoot->mShip->GetAccuracy(*curSlot->GetComp(), target->mShip);
00685                                 Sapper += long((1.0 - Acc) * temp / 8); // misses act like sappers
00686                                 temp *= Acc;
00687                                 if (curSlot->GetComp()->GetWeaponType() == WT_MISSILE && target->bShield == 0)
00688                                         temp *= 2;
00689                                 Damage += long(temp);
00690                         }
00691                         break;
00692                 default:
00693                         break;
00694                 }
00695         }
00696         */
00697 }

Generated on Mon Aug 8 21:33:42 2005 for Freestars by  doxygen 1.4.2-20050421