00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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;
00099
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
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
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)
00133 return;
00134
00135
00136 bReport = new TiXmlElement("BattleReport");
00137
00138 long Races = 0;
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
00149
00150
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
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
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
00185 sort(mFighting.begin(), mFighting.end(), StackLighter);
00186
00187 TiXmlElement Stacks("StartingStacks");
00188
00189
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
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
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
00227 sort(mSlots.begin(), mSlots.end(), HighInit);
00228
00229
00230 for (mRound = 1; mRound <= 16; ++mRound) {
00231 if (!FightRound())
00232 break;
00233 }
00234
00235
00236
00237
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
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;
00283 }
00284 } else
00285 return false;
00286 }
00287
00288 bool Battle::FightRound()
00289 {
00290 bool fought = false;
00291 int i, j;
00292
00293
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
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)
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, int dy) 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:
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
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;
00351 long DTRange = 0;
00352
00353
00354 for (int j = 0; j < mFighting.size(); ++j) {
00355
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
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
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
00394 default:
00395 break;
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
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;
00489 AddLong(bReport, "StackDisengages", s);
00490 }
00491 }
00492
00493
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
00514 TiXmlElement move("StackMove");
00515 AddLong(&move, "XCoord", mFighting[s]->bx);
00516 AddLong(&move, "YCoord", mFighting[s]->by);
00517 bReport->InsertEndChild(move);
00518
00519
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
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
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
00561
00562
00563
00564 long Battle::PotentialDamage(const Stack * shoot, const Stack * target, long Range, const Slot * slot, bool Gatling) 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);
00611 Damage += long(temp) - temp2;
00612 }
00613 break;
00614 default:
00615 break;
00616 }
00617 }
00618 }
00619
00620 Sapper = min(Sapper, target->bShield);
00621 if (slot != NULL)
00622 break;
00623 }
00624
00625 return min(Sapper+Damage, target->DP());
00626 }
00627
00628 void Battle::ShootSlot(bSlot & shooter)
00629 {
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697 }