1 // Copyright (C) 2001, 2002, 2003 Michael Bartl
2 // Copyright (C) 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
3 // Copyright (C) 2004, 2006 Andrea Paternesi
4 // Copyright (C) 2004 Bryan Duff
5 // Copyright (C) 2006, 2007, 2008 Ben Asselstine
6 // Copyright (C) 2008 Ole Laursen
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 3 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Library General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include <stdlib.h> // for random numbers
27 #include <math.h> // for has_hit()
30 #include "stacklist.h"
32 #include "playerlist.h"
40 //#define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<endl<<flush;}
43 // Helper class; the single units participating in the fight are saved with
44 // additional information. This should be a struct, but I don't know how to
45 // forward the declaration properly.
46 //! A particpant in a Fight.
50 Fighter(Army* a, Vector<int> p);
53 Vector<int> pos; // location on the map (needed to calculate boni)
55 //! needed for sorting
56 //bool operator() ( const Fighter* f1, const Fighter* f2 );
60 Fighter::Fighter(Army* a, Vector<int> p)
66 //take a list of stacks and create an ordered list of armies
67 void Fight::orderArmies(std::list<Stack*> stacks, std::vector<Army*> &armies)
69 std::list<Stack*>::iterator it;
72 for (it = stacks.begin(); it != stacks.end(); it++)
73 for (Stack::iterator sit = (*it)->begin(); sit != (*it)->end(); sit++)
74 armies.push_back((*sit));
76 //okay now sort the army list according to the player's fight order
77 std::sort(armies.begin(), armies.end(), Stack::armyCompareFightOrder);
82 Fight::Fight(Stack* attacker, Stack* defender, FightType type)
83 : d_turn(0), d_result(DRAW), d_type(type)
86 debug("Fight between " <<attacker->getId() <<" and " <<defender->getId())
88 // Duel case: two stacks fight each other; Nothing further to be done
89 // Important: We always assume that the attacking/defending stacks are
90 // the first in the list!!!
91 d_attackers.push_back(attacker);
92 d_defenders.push_back(defender);
94 // What we do here: In the setup, we need to find out all armies that
95 // participate in the fight. If a city is being attacked then the
96 // defender gets any other stacks in the cities.
99 City *city = GameMap::getCity(defender->getPos());
100 Vector<int> p = defender->getPos();
105 std::vector<Stack*> stacks = city->getDefenders();
106 for (std::vector<Stack*>::iterator it = stacks.begin();
107 it != stacks.end(); it++)
110 if (s == d_defenders.front())
112 d_defenders.push_back(s);
116 std::list<Stack*>::iterator it;
119 it = d_defenders.begin();
120 std::vector<Army*> def;
121 orderArmies (d_defenders, def);
122 for (std::vector<Army*>::iterator ait = def.begin(); ait != def.end(); ait++)
124 Fighter* f = new Fighter((*ait), (*it)->getPos());
125 d_def_close.push_back(f);
128 it = d_attackers.begin();
129 std::vector<Army*> att;
130 orderArmies (d_attackers, att);
131 for (std::vector<Army*>::iterator ait = att.begin(); ait != att.end(); ait++)
133 Fighter* f = new Fighter((*ait), (*it)->getPos());
134 d_att_close.push_back(f);
139 // Before the battle starts, calculate the bonuses
140 // bonuses remain even if the unit providing a stackwide bonus dies
146 Fight::Fight(std::list<Stack*> attackers, std::list<Stack*> defenders,
147 std::list<FightItem> history)
149 d_attackers = attackers;
150 d_defenders = defenders;
162 // clear all fighter items in all lists
163 while (!d_att_close.empty())
165 delete (*d_att_close.begin());
166 d_att_close.erase(d_att_close.begin());
169 while (!d_def_close.empty())
171 delete (*d_def_close.begin());
172 d_def_close.erase(d_def_close.begin());
176 void Fight::battle(bool intense)
178 d_intense_combat = intense;
180 // first, fight until the fight is over
181 for (d_turn = 0; doRound(); d_turn++);
183 // Now we have to set the fight result.
185 // First, look if the attacker died; the attacking stack is the first
187 bool survivor = false;
188 Stack* s = d_attackers.front();
189 for (Stack::const_iterator it = s->begin(); it != s->end(); it++)
190 if ((*it)->getHP() > 0)
197 d_result = DEFENDER_WON;
200 // Now look if the defender died; also the first in the list
202 s = d_defenders.front();
203 for (Stack::const_iterator it = s->begin(); it != s->end(); it++)
204 if ((*it)->getHP() > 0)
211 d_result = ATTACKER_WON;
214 if (d_type == FOR_KICKS)
216 //fixme: this will heal armies who happen to have a single hitpoint left.
217 std::list<Stack*>::iterator it;
218 //heal the attackers and defenders to full hit points
219 for (it = d_attackers.begin(); it != d_attackers.end(); it++)
220 for (Stack::iterator sit = (*it)->begin(); sit != (*it)->end(); sit++)
221 (*sit)->heal((*sit)->getStat(Army::HP));
223 for (it = d_defenders.begin(); it != d_defenders.end(); it++)
224 for (Stack::iterator sit = (*it)->begin(); sit != (*it)->end(); sit++)
225 (*sit)->heal((*sit)->getStat(Army::HP));
229 Army *findArmyById(const std::list<Stack *> &l, guint32 id)
231 for (std::list<Stack *>::const_iterator i = l.begin(), end = l.end();
233 Army *a = (*i)->getArmyById(id);
241 Fight::Result Fight::battleFromHistory()
243 for (std::list<FightItem>::iterator i = d_actions.begin(),
244 end = d_actions.end(); i != end; ++i) {
247 Army *a = findArmyById(d_attackers, f.id);
249 a = findArmyById(d_defenders, f.id);
253 printf ("uh oh. army id %d can't be found in the battle.\n", f.id);
254 printf ("do we have an army id of that in the stacklist anywhere?\n");
259 //is there anybody alive in the attackers?
260 for (std::list<Stack*>::iterator it = d_attackers.begin(); it != d_attackers.end(); it++)
262 for (Stack::iterator i = (*it)->begin(); i != (*it)->end(); i++)
264 if ((*i)->getHP() > 0)
265 return Fight::ATTACKER_WON;
269 return Fight::DEFENDER_WON;
272 bool Fight::doRound()
274 if (MAX_ROUNDS && d_turn >= MAX_ROUNDS)
277 debug ("Fight round #" <<d_turn);
279 //fight the first one in attackers with the first one in defenders
280 std::list<Fighter*>::iterator ffit = d_att_close.begin();
281 std::list<Fighter*>::iterator efit = d_def_close.begin();
283 //have the attacker and defender try to hit each other
284 fightArmies(*ffit, *efit);
286 if (*efit && (*efit)->army->getHP() <= 0)
289 if (*ffit && (*ffit)->army->getHP() <= 0)
292 if (d_def_close.empty() || d_att_close.empty())
298 void Fight::calculateBaseStrength(std::list<Fighter*> fighters)
300 std::list<Fighter*>::iterator fit;
301 for (fit = fighters.begin(); fit != fighters.end(); fit++)
303 if ((*fit)->army->getStat(Army::SHIP))
304 (*fit)->terrain_strength = 4;
306 (*fit)->terrain_strength = (*fit)->army->getStat(Army::STRENGTH);
310 void Fight::calculateTerrainModifiers(std::list<Fighter*> fighters)
313 GameMap *gm = GameMap::getInstance();
315 std::list<Fighter*>::iterator fit;
316 for (fit = d_att_close.begin(); fit != d_att_close.end(); fit++)
318 if ((*fit)->army->getStat(Army::SHIP))
321 mtile = gm->getTile((*fit)->pos);
322 army_bonus = (*fit)->army->getStat(Army::ARMY_BONUS);
324 if (army_bonus & Army::ADD1STRINOPEN && mtile->isOpenTerrain())
325 (*fit)->terrain_strength += 1;
327 if (army_bonus & Army::ADD1STRINFOREST &&
328 mtile->getType() == Tile::FOREST && !mtile->isCityTerrain())
329 (*fit)->terrain_strength += 1;
331 if (army_bonus & Army::ADD1STRINHILLS && mtile->isHillyTerrain())
332 (*fit)->terrain_strength += 1;
334 if (army_bonus & Army::ADD1STRINCITY && mtile->isCityTerrain())
335 (*fit)->terrain_strength += 1;
337 if (army_bonus & Army::ADD2STRINCITY && mtile->isCityTerrain())
338 (*fit)->terrain_strength += 2;
340 if (army_bonus & Army::ADD2STRINOPEN && mtile->isOpenTerrain())
341 (*fit)->terrain_strength += 2;
343 if ((*fit)->terrain_strength > 9) //terrain strength can't ever exceed 9
344 (*fit)->terrain_strength = 9;
349 void Fight::calculateModifiedStrengths (std::list<Fighter*>friendly,
350 std::list<Fighter*>enemy,
351 bool friendlyIsDefending,
355 GameMap *gm = GameMap::getInstance();
357 std::list<Fighter*>::iterator fit;
359 //find highest non-hero bonus
360 guint32 highest_non_hero_bonus = 0;
361 for (fit = friendly.begin(); fit != friendly.end(); fit++)
363 guint32 non_hero_bonus = 0;
364 if ((*fit)->army->isHero())
366 mtile = gm->getTile((*fit)->pos);
367 army_bonus = (*fit)->army->getStat(Army::ARMY_BONUS);
369 if (army_bonus & Army::ADD1STACKINHILLS && mtile->isHillyTerrain())
372 if (army_bonus & Army::ADD1STACK)
375 if (army_bonus & Army::ADD2STACK)
378 if (non_hero_bonus > highest_non_hero_bonus)
379 highest_non_hero_bonus = non_hero_bonus;
382 // does the defender cancel our non hero bonus?
383 for (fit = enemy.begin(); fit != enemy.end(); fit++)
385 army_bonus = (*fit)->army->getStat(Army::ARMY_BONUS);
386 if (army_bonus & Army::SUBALLNONHEROBONUS)
388 highest_non_hero_bonus = 0; //yes
393 //find hero bonus of strongest hero
394 guint32 hero_bonus = 0;
397 // first get command items from ALL heroes in the stack
398 for (fit = friendly.begin(); fit != friendly.end(); fit++)
400 if ((*fit)->army->isHero())
402 Hero *h = dynamic_cast<Hero*>((*fit)->army);
403 hero_bonus = h->getBackpack()->countStackStrengthBonuses();
408 //now add on the hero's natural command
411 hero_bonus += strongestHero->calculateNaturalCommand();
414 // does the defender cancel our hero bonus?
415 for (fit = enemy.begin(); fit != enemy.end(); fit++)
417 army_bonus = (*fit)->army->getStat(Army::ARMY_BONUS);
418 if (army_bonus & Army::SUBALLHEROBONUS)
420 hero_bonus = 0; //yep
425 guint32 fortify_bonus = 0;
426 for (fit = friendly.begin(); fit != friendly.end(); fit++)
428 army_bonus = (*fit)->army->getStat(Army::ARMY_BONUS);
429 if (army_bonus & Army::FORTIFY)
436 guint32 city_bonus = 0;
437 if (friendlyIsDefending)
439 // calculate the city bonus
440 fit = friendly.begin();
441 mtile = gm->getTile((*fit)->pos);
442 City *c = Citylist::getInstance()->getNearestCity((*fit)->pos);
443 if (c && mtile->getBuilding() == Maptile::CITY)
448 city_bonus = c->getDefenseLevel() - 1;
452 if (mtile->getBuilding() == Maptile::TEMPLE)
454 else if (mtile->getBuilding() == Maptile::RUIN)
458 // does the attacker cancel our city bonus?
459 for (fit = enemy.begin(); fit != enemy.end(); fit++)
461 army_bonus = (*fit)->army->getStat(Army::ARMY_BONUS);
462 if (army_bonus & Army::SUBALLCITYBONUS)
464 city_bonus = 0; //yep
470 guint32 total_bonus = highest_non_hero_bonus + hero_bonus + fortify_bonus +
473 if (total_bonus > 5) //total bonus can't exceed 5
476 //add it to the terrain strength of each unit
477 for (fit = friendly.begin(); fit != friendly.end(); fit++)
479 (*fit)->terrain_strength += total_bonus;
483 void Fight::calculateFinalStrengths (std::list<Fighter*> friendly, std::list<Fighter*> enemy)
486 std::list<Fighter*>::iterator efit;
487 std::list<Fighter*>::iterator ffit;
488 for (efit = enemy.begin(); efit != enemy.end(); efit++)
490 if ((*efit)->army->getStat(Army::SHIP))
492 army_bonus = (*efit)->army->getStat(Army::ARMY_BONUS);
493 if (army_bonus & Army::SUB1ENEMYSTACK)
495 for (ffit = friendly.begin(); ffit != friendly.end(); ffit++)
497 (*ffit)->terrain_strength -= 1;
498 if ((*ffit)->terrain_strength <= 0)
499 (*ffit)->terrain_strength = 1;
506 void Fight::calculateBonus()
508 // If there is a hero, add a +1 strength bonus
509 std::list<Stack*>::const_iterator it;
510 Stack::const_iterator sit;
511 std::list<Fighter*>::iterator fit;
513 // go get the base strengths of all attackers
514 // this includes items with battle bonuses for the hero
515 // naval units always have strength = 4
516 calculateBaseStrength (d_att_close);
517 calculateBaseStrength (d_def_close);
519 // now determine the terrain strength by adding the terrain modifiers
520 // to the base strength
521 // naval units always have a strength of 4
522 calculateTerrainModifiers (d_att_close);
523 calculateTerrainModifiers (d_def_close);
525 //calculate hero, non-hero, city, and fortify bonuses
526 it = d_attackers.begin();
527 Army *a = (*it)->getStrongestHero();
528 Hero *h = dynamic_cast<Hero*>(a);
529 calculateModifiedStrengths (d_att_close, d_def_close, false, h);
530 Hero *strongestHero = 0;
531 guint32 highest_strength = 0;
532 for (it = d_defenders.begin(); it != d_defenders.end(); it++)
534 a = (*it)->getStrongestHero();
537 h = dynamic_cast<Hero*>(a);
538 if (h->getStat(Army::STRENGTH) > highest_strength)
540 highest_strength = h->getStat(Army::STRENGTH);
544 calculateModifiedStrengths (d_def_close, d_att_close, true, strongestHero);
546 calculateFinalStrengths (d_att_close, d_def_close);
547 calculateFinalStrengths (d_def_close, d_att_close);
551 void Fight::fightArmies(Fighter* attacker, Fighter* defender)
553 static int misses_in_a_row;
556 if (!attacker || !defender)
559 Army *a = attacker->army;
560 Army *d = defender->army;
562 debug("Army " << a->getId() << " attacks " << d->getId())
564 if (d_intense_combat == true)
569 // factor used for some calculation regarding gaining medals
570 double xp_factor = a->getXpReward() / d->getXpReward();
572 // the clash has to be documented for later use in the fight dialog
574 // make a swing at the opponent
575 // take one hit point off, per hit.
580 item.id = d->getId();
584 int attacker_roll = rand() % sides;
585 int defender_roll = rand() % sides;
587 if (attacker_roll <= attacker->terrain_strength &&
588 defender_roll > defender->terrain_strength)
591 if (d_type == FOR_KEEPS)
593 a->setNumberHasHit(a->getNumberHasHit() + (1/xp_factor));
594 d->setNumberHasBeenHit(d->getNumberHasBeenHit() + (1/xp_factor));
598 item.id = d->getId();
601 else if (defender_roll <= defender->terrain_strength &&
602 attacker_roll > attacker->terrain_strength)
605 if (d_type == FOR_KEEPS)
607 d->setNumberHasHit(d->getNumberHasHit() + (1/xp_factor));
608 a->setNumberHasBeenHit(a->getNumberHasBeenHit() + (1/xp_factor));
612 item.id = a->getId();
618 if (misses_in_a_row >= 10000)
620 //defender automatically wins
621 //hit attacker for however much it takes
622 if (d_type == FOR_KEEPS)
624 d->setNumberHasHit(d->getNumberHasHit() + (1/xp_factor));
625 a->setNumberHasBeenHit(a->getNumberHasBeenHit() +
628 item.id = a->getId();
635 // continue documenting the engagement
636 item.damage = damage;
637 d_actions.push_back(item);
641 void Fight::remove(Fighter* f)
643 std::list<Fighter*>::iterator it;
645 // is the fighter in the attacker lists?
646 for (it = d_att_close.begin(); it != d_att_close.end(); it++)
649 d_att_close.erase(it);
654 // or in the defender lists?
655 for (it = d_def_close.begin(); it != d_def_close.end(); it++)
658 d_def_close.erase(it);
663 // if the fighter wa sin no list, we are rather careful and don't do anything
664 debug("Fight: fighter without list!")
667 guint32 Fight::getModifiedStrengthBonus(Army *a)
669 std::list<Fighter*>::iterator it;
670 for (it = d_att_close.begin(); it != d_att_close.end(); it++)
671 if ((*it)->army == a)
672 return (*it)->terrain_strength;
673 for (it = d_def_close.begin(); it != d_def_close.end(); it++)
674 if ((*it)->army == a)
675 return (*it)->terrain_strength;
679 void Fight::fillInInitialHPs()
681 for (std::list<Stack *>::iterator i = d_attackers.begin();
682 i != d_attackers.end(); ++i)
683 for (Stack::iterator j = (*i)->begin(); j != (*i)->end(); ++j)
684 initial_hps[(*j)->getId()] = (*j)->getHP();
686 for (std::list<Stack *>::iterator i = d_defenders.begin();
687 i != d_defenders.end(); ++i)
688 for (Stack::iterator j = (*i)->begin(); j != (*i)->end(); ++j)
689 initial_hps[(*j)->getId()] = (*j)->getHP();
692 LocationBox Fight::calculateFightBox(Fight &fight)
694 Citylist *cl = Citylist::getInstance();
695 Vector<int> dest = fight.getAttackers().front()->getPos();
696 if (cl->getObjectAt(dest) == NULL)
697 return LocationBox(dest);
698 Player *p = fight.getAttackers().front()->getOwner();
699 Stack *s = fight.getAttackers().front();
700 std::list<Vector<int> > tracks = p->getStackTrack(s);
701 if (tracks.size() >= 2)
703 std::list<Vector<int> >::iterator it = tracks.end();
705 return LocationBox(*it, dest);
709 //this shouldn't be the case
710 return LocationBox(s->getPos(), dest);