1 // Copyright (C) 2000, 2001, 2002, 2003 Michael Bartl
2 // Copyright (C) 2000, Anluan O'Brien
3 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
4 // Copyright (C) 2004 John Farrell
5 // Copyright (C) 2004, 2005 Andrea Paternesi
6 // Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2015,
7 // 2020 Ben Asselstine
8 // Copyright (C) 2007, 2008 Ole Laursen
9 //
10 //  This program is free software; you can redistribute it and/or modify
11 //  it under the terms of the GNU General Public License as published by
12 //  the Free Software Foundation; either version 3 of the License, or
13 //  (at your option) any later version.
14 //
15 //  This program is distributed in the hope that it will be useful,
16 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 //  GNU Library General Public License for more details.
19 //
20 //  You should have received a copy of the GNU General Public License
21 //  along with this program; if not, write to the Free Software
22 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 //  02110-1301, USA.
24 
25 #include <sigc++/functors/mem_fun.h>
26 #include <assert.h>
27 
28 #include "stack.h"
29 #include "playerlist.h"
30 #include "path.h"
31 #include "armysetlist.h"
32 #include "counter.h"
33 #include "army.h"
34 #include "hero.h"
35 #include "GameMap.h"
36 #include "vector.h"
37 #include "xmlhelper.h"
38 #include "FogMap.h"
39 #include "player.h"
40 #include "Backpack.h"
41 #include "AI_Analysis.h"
42 #include "ruin.h"
43 #include "Item.h"
44 
45 Glib::ustring Stack::d_tag = "stack";
46 
47 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
48 #define debug(x)
49 
Stack(Player * player,Vector<int> pos)50 Stack::Stack(Player* player, Vector<int> pos)
51     : UniquelyIdentified(), Movable(pos), Ownable(player), d_defending(false),
52     d_parked(false), d_deleting(false)
53 {
54     d_path = new Path();
55 }
56 
Stack(guint32 id,Player * player,Vector<int> pos)57 Stack::Stack(guint32 id, Player* player, Vector<int> pos)
58     : UniquelyIdentified(id), Movable(pos), Ownable(player),
59     d_defending(false), d_parked(false), d_deleting(false)
60 {
61     d_path = new Path();
62 }
63 
Stack(const Stack & s,bool uniq)64 Stack::Stack(const Stack& s, bool uniq)
65     : UniquelyIdentified(s), Movable(s), Ownable(s), std::list<Army*>(),
66     sigc::trackable(s), d_defending(s.d_defending), d_parked(s.d_parked),
67     d_deleting(false)
68 {
69   d_unique = uniq;
70   if (s.d_path == NULL)
71     {
72       printf("Stack %d has a null path!\n", d_id);
73     }
74     d_path = new Path(*s.d_path);
75 
76     for (const_iterator sit = s.begin(); sit != s.end(); sit++)
77     {
78 	if ((*sit)->isHero())
79           push_back(new Hero(dynamic_cast<Hero&>(**sit)));
80 	else
81           push_back(new Army((**sit), (*sit)->getOwner()));
82     }
83 }
84 
Stack(XML_Helper * helper)85 Stack::Stack(XML_Helper* helper)
86   : UniquelyIdentified(helper), Movable(helper), Ownable(helper),
87     d_deleting(false)
88 {
89   helper->getData(d_defending, "defending");
90   helper->getData(d_parked, "parked");
91 
92   helper->registerTag(Path::d_tag, sigc::mem_fun((*this), &Stack::load));
93   helper->registerTag(Army::d_tag, sigc::mem_fun((*this), &Stack::load));
94   helper->registerTag(Hero::d_tag, sigc::mem_fun((*this), &Stack::load));
95 }
96 
~Stack()97 Stack::~Stack()
98 {
99   d_deleting = true;
100   if (d_unique)
101     sdying.emit(this);
102 
103   delete d_path;
104   flClear();
105 }
106 
setPlayer(Player * p)107 void Stack::setPlayer(Player* p)
108 {
109   // we need to change the armies' loyalties as well!!
110   setOwner(p);
111   for (iterator it = begin(); it != end(); it++)
112     (*it)->setOwner(p);
113 }
114 
moveOneStep(bool skipping)115 void Stack::moveOneStep(bool skipping)
116 {
117   debug("moveOneStep()");
118 
119   Vector<int> dest = getFirstPointInPath();
120   moveToDest(dest, skipping);
121 
122   //now remove first point of the path
123   d_path->eraseFirstPoint();
124 }
125 
isMovingToOrFromAShip(Vector<int> dest,bool & on_ship) const126 bool Stack::isMovingToOrFromAShip(Vector<int> dest, bool &on_ship) const
127 {
128   Vector<int> pos = getPos();
129   Maptile::Building src_building = GameMap::getInstance()->getBuilding(pos);
130   Maptile::Building dst_building = GameMap::getInstance()->getBuilding(dest);
131 
132   bool to_city = dst_building == Maptile::CITY;
133   bool on_city = src_building == Maptile::CITY;
134 
135   bool on_port = src_building == Maptile::PORT;
136   bool on_bridge = src_building == Maptile::BRIDGE;
137   bool to_bridge = dst_building == Maptile::BRIDGE;
138   bool on_water = (GameMap::getInstance()->getTerrainType(pos) == Tile::WATER);
139   bool to_water = (GameMap::getInstance()->getTerrainType(dest) == Tile::WATER);
140   //here we mark the armies as being on or off a boat
141   /* skipping refers to when we have to move over another friendly stack
142    * of a size that's too big to join with. */
143   if ((on_water && to_city && !on_bridge) ||
144       (on_water && on_port && !to_water && on_ship) ||
145       ((on_city || on_port) && to_water && !to_bridge) ||
146       (on_bridge && to_water && !to_bridge) ||
147       (on_bridge && !to_water && on_ship) ||
148       (on_water && to_water && !on_bridge && !on_port && !to_bridge &&
149        on_ship == false) ||
150       (!on_water && !to_water && on_ship == true))
151     {
152       on_ship = !on_ship;
153       return true;
154     }
155   return false;
156 }
157 
drainMovement()158 void Stack::drainMovement()
159 {
160   for (Stack::iterator it = begin(); it != end(); it++)
161     (*it)->decrementMoves((*it)->getMoves());
162 }
163 
moveToDest(Vector<int> dest,bool skipping)164 void Stack::moveToDest(Vector<int> dest, bool skipping)
165 {
166   bool ship_load_unload = false;
167   if (!isFlying())
168     {
169       bool on_ship = hasShip();
170       if (isMovingToOrFromAShip(dest, on_ship) == true)
171 	{
172 	  if (!skipping)
173 	    {
174               updateShipStatus(dest);
175 	      ship_load_unload = true;
176 	    }
177 	}
178     }
179   else
180     {
181       for (Stack::iterator it = begin(); it != end(); it++)
182 	(*it)->setInShip(false);
183     }
184 
185   guint32 maptype = GameMap::getInstance()->getTile(dest.x,dest.y)->getType();
186   //how many moves does the stack need to travel to dest?
187   int needed_moves = calculateTileMovementCost(dest);
188 
189   if (ship_load_unload)
190     drainMovement();
191   else
192     {
193       for (Stack::iterator it = begin(); it != end(); it++)
194         {
195           //maybe the army has a natural movement ability
196           if ((*it)->getStat(Army::MOVE_BONUS) & maptype && needed_moves > 1)
197             (*it)->decrementMoves(2);
198           else
199             (*it)->decrementMoves(needed_moves);
200         }
201     }
202 
203   //update position and status
204   smoving.emit(this);
205   setPos(dest);
206 
207   //update fogmap
208   deFog();
209 
210   smoved.emit(this);
211 
212   setFortified(false);
213   setDefending(false);
214   setParked(false);
215 }
216 
deFog()217 void Stack::deFog()
218 {
219   getOwner()->getFogMap()->alterFogRadius(getPos(), getMaxSight(),
220 					  FogMap::OPEN);
221   return;
222 }
223 
224 // return the maximum moves of this stack by checking the moves of each army
getMoves() const225 guint32 Stack::getMoves() const
226 {
227   if (empty())
228     return 0;
229 
230   assert(!empty());
231 
232   int min = -1;
233 
234   for (const_iterator it = begin(); it != end(); ++it)
235     {
236       if (min == -1)
237 	min = int((*it)->getMoves());
238       else
239 	min = std::min(min, int((*it)->getMoves()));
240     }
241 
242   if (min <= -1)
243     return 0;
244   return min;
245 }
246 
getMinTileMoves() const247 int Stack::getMinTileMoves() const
248 {
249   LwRectangle bounds = GameMap::getInstance()->get_boundary();
250 
251   std::vector<Vector<int> > tiles;
252   tiles.push_back(Vector<int>(getPos().x + 1, getPos().y - 1));
253   tiles.push_back(Vector<int>(getPos().x,     getPos().y - 1));
254   tiles.push_back(Vector<int>(getPos().x - 1, getPos().y - 1));
255   tiles.push_back(Vector<int>(getPos().x + 1, getPos().y + 1));
256   tiles.push_back(Vector<int>(getPos().x,     getPos().y + 1));
257   tiles.push_back(Vector<int>(getPos().x - 1, getPos().y + 1));
258   tiles.push_back(Vector<int>(getPos().x + 1, getPos().y));
259   tiles.push_back(Vector<int>(getPos().x - 1, getPos().y));
260 
261   int min = -1;
262 
263   for (auto tile: tiles)
264     if (is_inside(bounds, tile))
265       {
266 	int v = GameMap::getInstance()->getTile(tile)->getMoves();
267 	if (min == -1)
268 	  min = v;
269 	else
270 	  min = std::min(min, v);
271       }
272 
273   return min;
274 }
275 
276 // decrement each armys moves by needed moves to travel
277 
decrementMoves(guint32 moves)278 void Stack::decrementMoves(guint32 moves)
279 {
280   debug("decrement_moves()");
281 
282   for (iterator it = begin(); it != end(); it++)
283     (*it)->decrementMoves(moves);
284 }
285 
incrementMoves(guint32 moves)286 void Stack::incrementMoves(guint32 moves)
287 {
288   debug("increment_moves()");
289 
290   for (iterator it = begin(); it != end(); it++)
291     (*it)->incrementMoves(moves);
292 }
293 
294 // Purpose: Return the strongest army of a group
295 // Note: If a hero is present return it. If there are two similar armies
296 // (two of the same strength, or two heroes) return the first in the sequence.
297 // heroes in boats must be considered to be less strong than heroes who are
298 // not in boats.
299 
getStrongestArmy() const300 Army* Stack::getStrongestArmy() const
301 {
302   assert(!empty());
303   return getStrongestArmy(false);
304 }
305 
getStrongestHero() const306 Army* Stack::getStrongestHero() const
307 {
308   Army *strongest = 0;
309   guint32 highest_strength = 0;
310   for (const_iterator it = begin(); it != end(); ++it)
311     {
312       if ((*it)->isHero())
313         {
314           if ((*it)->getStat(Army::STRENGTH) > highest_strength)
315 
316             {
317               highest_strength = (*it)->getStat(Army::STRENGTH);
318               strongest = *it;
319             }
320         }
321     }
322   return strongest;
323 }
324 
getStrongestArmy(bool hero) const325 Army* Stack::getStrongestArmy(bool hero) const
326 {
327   Army *strongest = 0;
328   guint32 highest_strength = 0;
329 
330   for (const_iterator it = begin(); it != end(); ++it)
331     {
332       if (((*it)->isHero() && hero) || !hero)
333 	{
334 	  if ((*it)->getStat(Army::STRENGTH) > highest_strength)
335 	    {
336 	      highest_strength = (*it)->getStat(Army::STRENGTH);
337 	      strongest = *it;
338 	    }
339 	}
340     }
341   return strongest;
342 }
343 
getArmyById(guint32 id) const344 Army *Stack::getArmyById(guint32 id) const
345 {
346   for (Stack::const_iterator i = begin(), e = end(); i != e; ++i)
347     if ((*i)->getId() == id)
348       return *i;
349 
350   return 0;
351 }
352 
hasHero() const353 bool Stack::hasHero() const
354 {
355   for (const_iterator it = begin(); it != end(); it++)
356     if ((*it)->isHero())
357       return true;
358 
359   return false;
360 }
361 
getFirstHero() const362 Army* Stack::getFirstHero() const
363 {
364   for (const_iterator it = begin(); it != end(); it++)
365     if ((*it)->isHero())
366       return (*it);
367 
368   return 0;
369 }
370 
getHeroes(std::vector<guint32> & dst) const371 void Stack::getHeroes(std::vector<guint32>& dst) const
372 {
373   debug("getHeroes - stack = " << this)
374     for (const_iterator it = begin(); it != end(); ++it)
375       {
376 	// if hero - add it to the vector
377 	debug("Army type: " << (*it)->getTypeId())
378 	  if ((*it)->isHero() && (*it)->getHP() > 0)
379 	    dst.push_back((*it)->getId());
380       }
381 }
382 
bless()383 int Stack::bless()
384 {
385   int count = 0;
386   for (iterator it = begin(); it != end(); it++)
387     {
388       Temple *temple = GameMap::getTemple(this);
389       if ((*it)->bless(temple))
390 	count++;
391     }
392   return count;
393 }
394 
calculateTileMovementCost(Vector<int> pos) const395 guint32 Stack::calculateTileMovementCost(Vector<int> pos) const
396 {
397   Maptile* tile = GameMap::getInstance()->getTile(pos);
398   guint32 moves = tile->getMoves();
399   guint32 bonus = calculateMoveBonus();
400   if (bonus & tile->getType() && moves > 1)
401     moves = 2;
402   else if (isFlying() && moves > 1)
403     moves = 2;
404   return moves;
405 }
406 
getFirstPointInPath() const407 Vector<int> Stack::getFirstPointInPath() const
408 {
409   if (hasPath() == false)
410     return Vector<int>(-1,-1);
411   Vector<int> p = *(d_path->begin());
412   return p;
413 }
414 
getLastReachablePointInPath() const415 Vector<int> Stack::getLastReachablePointInPath() const
416 {
417   if (d_path->size() == 0)
418     return Vector<int>(-1,-1);
419   unsigned int count = 0;
420   for (Path::iterator it = d_path->begin(); it != d_path->end(); it++)
421     {
422       count++;
423       if (count == d_path->getMovesExhaustedAtPoint())
424 	return (*it);
425     }
426   return Vector<int>(-1,-1);
427 }
428 
getLastPointInPath() const429 Vector<int> Stack::getLastPointInPath() const
430 {
431   if (d_path->size() == 0)
432     return Vector<int>(-1,-1);
433   Vector<int> p = d_path->back();
434   return p;
435 }
436 
enoughMoves() const437 bool Stack::enoughMoves() const
438 {
439   if (hasPath() == false)
440     return true; //we have enough moves to move nowhere!
441 
442   Vector<int> p = getFirstPointInPath();
443   guint32 needed = calculateTileMovementCost(p);
444 
445   if (getMoves() >= needed)
446     return true;
447 
448   return false;
449 }
450 
canMove() const451 bool Stack::canMove() const
452 {
453   int tile_moves = getMinTileMoves();
454   int group_moves = getMoves();
455 
456   assert (tile_moves != -1);
457   return group_moves > 0 && tile_moves >= 0 && group_moves >= tile_moves;
458 }
459 
getMaxSight() const460 guint32 Stack::getMaxSight() const
461 {
462   guint32 max = 0;
463   for (const_iterator it = begin(); it != end(); it++)
464     if ((*it)->getStat(Army::SIGHT) > max)
465       max = (*it)->getStat(Army::SIGHT);
466 
467   return max;
468 }
469 
payUpkeep(Player * p)470 void Stack::payUpkeep(Player *p)
471 {
472   for (iterator it = begin(); it != end(); ++it)
473       p->withdrawGold((*it)->getUpkeep());
474 }
475 
reset(bool recalculate_path)476 void Stack::reset(bool recalculate_path)
477 {
478   guint32 movement_multiplier = 1;
479 
480   //count the number of items that double the movement in the stack.
481   for (const_iterator it = begin(); it != end(); it++)
482     if ((*it)->isHero())
483       {
484 	Hero *hero = dynamic_cast<Hero*>(*it);
485 	guint32 bonus = hero->getBackpack()->countMovementDoublers();
486 	for (guint32 i = 0; i < bonus; i++)
487 	  movement_multiplier*=2;
488       }
489 
490   if (movement_multiplier > 1024)
491     movement_multiplier = 1024;
492 
493   //set the multipler on all armies in the stack
494   for (const_iterator it = begin(); it != end(); it++)
495     (*it)->setStat(Army::MOVES_MULTIPLIER, movement_multiplier);
496 
497   if (d_defending == true)
498     setFortified(true);
499 
500   for (iterator it = begin(); it != end(); ++it)
501     {
502       (*it)->resetMoves();
503       (*it)->heal();
504     }
505 
506   //recalculate paths
507 
508   if (recalculate_path)
509     d_path->recalculate(this);
510   //we need to unpark stacks here (at the end of a round), at the very least
511   //because it makes ai stacks move in the field after they've been parked.
512   //we aren't sending the un-parked action here because this is part of the
513   //reset stacks action.
514   setParked(false);
515 }
516 
save(XML_Helper * helper) const517 bool Stack::save(XML_Helper* helper) const
518 {
519   bool retval = true;
520 
521   retval &= helper->openTag(Stack::d_tag);
522   retval &= helper->saveData("id", d_id);
523   retval &= helper->saveData("x", getPos().x);
524   retval &= helper->saveData("y", getPos().y);
525   if (d_owner)
526     retval &= helper->saveData("owner", d_owner->getId());
527   else
528     retval &= helper->saveData("owner", -1);
529   retval &= helper->saveData("defending", d_defending);
530   retval &= helper->saveData("parked", d_parked);
531 
532 
533   //save path
534   retval &= d_path->save(helper);
535 
536   //save armies
537   for (const_iterator it = begin(); it != end(); it++)
538     retval &= (*it)->save(helper);
539 
540   retval &= helper->closeTag();
541 
542   return retval;
543 }
544 
load(Glib::ustring tag,XML_Helper * helper)545 bool Stack::load(Glib::ustring tag, XML_Helper* helper)
546 {
547   if (tag == Path::d_tag)
548     {
549       d_path = new Path(helper);
550       return true;
551     }
552 
553   if (tag == Army::d_tag)
554     {
555       Army* a = new Army(helper);
556       a->setOwner(d_owner);
557       push_back(a);
558       return true;
559     }
560 
561   if (tag == Hero::d_tag)
562     {
563       Hero* h = new Hero(helper);
564       h->setOwner(d_owner);
565       push_back(h);
566 
567       return true;
568     }
569 
570   return false;
571 }
572 
flClear()573 void Stack::flClear()
574 {
575   for (iterator it = begin(); it != end(); it++)
576     {
577       Army *a = *it;
578       if (a->isHero())
579         {
580           Hero *h = dynamic_cast<Hero*>(a);
581           delete h;
582         }
583       else
584         delete a;
585     }
586   clear();
587 }
588 
flErase(Stack::iterator object)589 Stack::iterator Stack::flErase(Stack::iterator object)
590 {
591   Army *a = *object;
592   if (a->isHero())
593     {
594       Hero *h = dynamic_cast<Hero*>(a);
595       delete h;
596     }
597   else
598     delete (a);
599   return erase(object);
600 }
601 
calculateMoveBonus() const602 guint32 Stack::calculateMoveBonus() const
603 {
604   guint32 d_bonus = 0;
605 
606   bool landed = false;
607   guint32 bonus;
608   // check to see if we're all flying
609   int num_landedhero = 0;
610   int num_flyer = 0;
611   int num_landedother = 0;
612   if (size() == 0)
613     return 0;
614   for (const_iterator it = this->begin(); it != this->end(); it++)
615     {
616       bonus = (*it)->getStat(Army::MOVE_BONUS);
617       if (bonus == Tile::GRASS || (bonus & Tile::WATER) == 0 ||
618 	  (bonus & Tile::FOREST) == 0 || (bonus & Tile::HILLS) == 0 ||
619 	  (bonus & Tile::MOUNTAIN) == 0 || (bonus & Tile::SWAMP) == 0)
620 	{
621 	  landed = true;
622 	  if ((*it)->isHero())
623 	    num_landedhero++;
624 	  else
625 	    num_landedother++;
626 	}
627       else
628 	num_flyer++;
629 
630     }
631   //if we're all flying or we have enough flyers to carry landbound heroes
632   if (landed == false ||
633       (num_landedother == 0 && num_landedhero <= num_flyer))
634     {
635       d_bonus = Tile::isFlying();
636       return d_bonus;
637     }
638 
639   //or maybe we have an item that lets us all fly
640   for (const_iterator it = begin(); it != end(); it++)
641     {
642       if ((*it)->isHero())
643 	{
644 	  Hero *h = dynamic_cast<Hero*>(*it);
645 	  if (h->isFlyer())
646 	    {
647 	      d_bonus = Tile::isFlying();
648 	      return d_bonus;
649 	    }
650 	}
651     }
652 
653   //calculate move bonuses for non-flying stacks
654   for (Stack::const_iterator it = this->begin(); it != this->end(); it++)
655     {
656       bonus = (*it)->getStat(Army::MOVE_BONUS);
657 
658       //only forest and hills extend to all other units in the stack
659       d_bonus |= bonus & (Tile::HILLS | Tile::FOREST);
660 
661     }
662   return d_bonus;
663 }
664 
isFlying() const665 bool Stack::isFlying () const
666 {
667   guint32 d_bonus = calculateMoveBonus();
668   if (d_bonus == Tile::isFlying())
669     return true;
670   else
671     return false;
672 }
673 
674 /*if any stack member is in a boat, then the whole stack appears to be in
675  * a boat */
hasShip() const676 bool Stack::hasShip () const
677 {
678   for (Stack::const_iterator it = this->begin(); it != this->end(); it++)
679     {
680       if ((*it)->getStat(Army::SHIP))
681 	return true;
682     }
683   return false;
684 }
685 
getFightOrder(std::list<guint32> values,guint32 value)686 guint32 getFightOrder(std::list<guint32> values, guint32 value)
687 {
688   guint32 count = 0;
689   for (std::list<guint32>::const_iterator it = values.begin();
690        it != values.end(); it++)
691     {
692       count++;
693       if (*it == value)
694 	return count;
695     }
696   return 0;
697 }
698 
armyCompareStrength(const Army * lhs,const Army * rhs)699 bool Stack::armyCompareStrength (const Army *lhs, const Army *rhs)
700 {
701   return lhs->getStat(Army::STRENGTH) < rhs->getStat(Army::STRENGTH);
702 }
703 
armyCompareFightOrder(const Army * lhs,const Army * rhs)704 bool Stack::armyCompareFightOrder (const Army *lhs, const Army *rhs)
705 {
706   std::list<guint32> lhs_fight_order = lhs->getOwner()->getFightOrder();
707   std::list<guint32> rhs_fight_order = rhs->getOwner()->getFightOrder();
708   guint32 lhs_rank = getFightOrder (lhs_fight_order, lhs->getTypeId());
709   guint32 rhs_rank = getFightOrder (rhs_fight_order, rhs->getTypeId());
710   if (lhs_rank == rhs_rank)
711     return lhs->getId() < rhs->getId();
712   return lhs_rank < rhs_rank;
713 }
714 
sortByStrength(bool rev)715 void Stack::sortByStrength(bool rev)
716 {
717   sort(armyCompareStrength);
718   if (rev)
719     std::reverse(begin(), end());
720 }
721 
sortForViewing(bool rev)722 void Stack::sortForViewing (bool rev)
723 {
724   sort(armyCompareFightOrder);
725   if (rev)
726     std::reverse(begin(), end());
727 }
728 
setFortified(bool fortified)729 void Stack::setFortified(bool fortified)
730 {
731   if (empty())
732     return;
733   for (iterator it = begin(); it != end(); it++)
734     (*it)->setFortified(false);
735 
736   (*begin())->setFortified(fortified);
737 }
738 
getFortified() const739 bool Stack::getFortified() const
740 {
741   if (empty())
742     return false;
743   for (const_iterator it = begin(); it != end(); it++)
744     {
745       if ((*it)->getFortified())
746 	return true;
747     }
748   return false;
749 }
750 
getUpkeep() const751 guint32 Stack::getUpkeep() const
752 {
753   guint32 upkeep = 0;
754   for (const_iterator it = begin(); it != end(); it++)
755     upkeep += (*it)->getUpkeep();
756   return upkeep;
757 }
758 
getMaxArmiesToJoin() const759 guint32 Stack::getMaxArmiesToJoin() const
760 {
761   return MAX_STACK_SIZE - size();
762 }
763 
canJoin(const Stack * stack) const764 bool Stack::canJoin(const Stack *stack) const
765 {
766   if ((stack->size() + size()) > MAX_STACK_SIZE)
767     return false;
768 
769   return true;
770 
771 }
772 
773 //take the weakest units where their strengths add up to strength.
determineArmiesByStrength(float strength) const774 std::list<guint32> Stack::determineArmiesByStrength(float strength) const
775 {
776   std::list<guint32> armies;
777   float remaining = strength;
778   Stack *stack = new Stack(*this);
779   stack->sortByStrength(false);
780   for (iterator it = stack->begin(); it != stack->end(); it++)
781     {
782       float score = AI_Analysis::assessArmyStrength(*it);
783       if (score > remaining)
784         continue;
785       else
786         {
787           remaining -= score;
788           armies.push_back((*it)->getId());
789         }
790     }
791   delete stack;
792   return armies;
793 }
794 
determineStrongArmies(float strength) const795 std::list<guint32> Stack::determineStrongArmies(float strength) const
796 {
797   return determineArmiesByStrength(strength);
798 }
799 
determineWeakArmies(float strength) const800 std::list<guint32> Stack::determineWeakArmies(float strength) const
801 {
802   return determineArmiesByStrength(strength);
803 }
804 
determineReachableArmies(Vector<int> dest) const805 std::list<guint32> Stack::determineReachableArmies(Vector<int> dest) const
806 {
807   std::list<guint32> ids;
808   //try each army individually to see if it reaches
809 
810   Stack *stack = Stack::createNonUniqueStack(getOwner(), getPos());
811   for (const_iterator it = begin(); it != end(); it++)
812     {
813       if ((*it)->getMoves() > 0)
814 	{
815 	  stack->push_back(*it);
816 	  if (stack->getMoves() >= stack->getPath()->calculate(stack, dest))
817 	    ids.push_back((*it)->getId());
818 	  stack->clear();
819 	}
820     }
821   delete stack;
822   if (ids.size() == 0)
823     return ids;
824 
825   //now try to see if any army units can tag along
826   stack = Stack::createNonUniqueStack(getOwner(), getPos());
827   for (const_iterator it = begin(); it != end(); it++)
828     {
829       //skip over armies that are already known to be reachable
830       if (find(ids.begin(), ids.end(), (*it)->getId()) != ids.end())
831 	continue;
832       if ((*it)->getMoves() > 0)
833 	{
834 	  stack->push_back(*it);
835 	  //also push back the rest of the known reachables
836 	  std::list<guint32>::iterator iit = ids.begin();
837 	  for (; iit != ids.end(); iit++)
838 	    {
839 	      Army *army = getArmyById(*iit);
840 	      if (army)
841 		stack->push_back(army);
842 	    }
843 	  if (stack->getMoves() >=
844 	      stack->getPath()->calculate(stack, dest))
845 	    ids.push_back((*it)->getId());
846 	  stack->clear();
847 	}
848     }
849   delete stack;
850 
851   return ids;
852 }
853 
countArmiesBlessedAtTemple(guint32 temple_id) const854 guint32 Stack::countArmiesBlessedAtTemple(guint32 temple_id) const
855 {
856   guint32 blessed = 0;
857   for (const_iterator it = begin(); it != end(); it++)
858     {
859       if ((*it)->blessedAtTemple(temple_id))
860 	blessed++;
861     }
862     return blessed;
863 }
864 
createNonUniqueStack(Player * player,Vector<int> pos)865 Stack* Stack::createNonUniqueStack(Player *player, Vector<int> pos)
866 {
867   return new Stack(0, player, pos);
868 }
869 
getMaxMoves() const870 guint32 Stack::getMaxMoves() const
871 {
872   if (GameMap::getInstance()->getTile(getPos())->getType() != Tile::WATER)
873     return getMaxLandMoves();
874   else
875     return getMaxBoatMoves();
876 }
877 
getMaxLandMoves() const878 guint32 Stack::getMaxLandMoves() const
879 {
880   if (empty())
881     return 0;
882 
883   assert(!empty());
884 
885   //copy the stack, reset the moves and return the group moves
886   Stack *copy = new Stack (*this);
887   copy->getPath()->clear(); //this prevents triggering path recalc in reset
888   copy->decrementMoves(copy->getMoves());
889   copy->reset(false);
890   guint32 moves = copy->getMoves();
891   if (isFlying() == true)
892     {
893       delete copy;
894       return moves;
895     }
896 
897   //alright, we're not flying.  what would our group moves be if we were on land
898   //remove ship status from all army units
899   copy->decrementMoves(copy->getMoves());
900   for (Stack::iterator it = copy->begin(); it != copy->end(); it++)
901     (*it)->setInShip(false);
902   copy->reset(false);
903 
904   moves = copy->getMoves();
905   delete copy;
906   return moves;
907 }
908 
getMaxBoatMoves() const909 guint32 Stack::getMaxBoatMoves() const
910 {
911   if (empty())
912     return 0;
913 
914   assert(!empty());
915 
916   //copy the stack, reset the moves and return the group moves
917   Stack *copy = new Stack (*this);
918   copy->getPath()->clear(); //this prevents triggering path recalc in reset
919   copy->reset();
920   guint32 moves = copy->getMoves();
921   if (isFlying() == true)
922     {
923       delete copy;
924       return moves;
925     }
926   //alright, we're not flying.  what would our group moves be if we were on water?
927   copy->decrementMoves(copy->getMoves());
928 
929   for (Stack::iterator it = copy->begin(); it != copy->end(); it++)
930     {
931       if (((*it)->getStat(Army::MOVE_BONUS) & Tile::WATER) == 0)
932 	(*it)->setInShip(true);
933       else
934 	(*it)->setInShip(false);
935     }
936   copy->reset();
937 
938   moves = copy->getMoves();
939   delete copy;
940   return moves;
941 }
942 
setPath(const Path p)943 void Stack::setPath(const Path p)
944 {
945   if (d_path)
946     delete d_path;
947   d_path = new Path(p);
948 }
949 
add(Army * army)950 void Stack::add(Army *army)
951 {
952   push_back(army);
953 }
954 
955 //! split the given army from this stack, into a brand new stack.
splitArmy(Army * army)956 Stack *Stack::splitArmy(Army *army)
957 {
958   if (size() == 1) //we can't split the last army.
959     return NULL;
960 
961   assert (army != NULL);
962   Stack *new_stack = NULL;
963   for (iterator it = begin(); it != end(); it++)
964     {
965       if (*it == army || (*it)->getId() == army->getId())
966 	{
967 	  new_stack = new Stack(getOwner(), getPos());
968 	  new_stack->add(*it);
969 	  it = erase(it);
970 	  break;
971 	}
972     }
973 
974   return new_stack;
975 }
976 
977 //! split the given armies from this stack, into a brand new stack.
splitArmies(std::list<Army * > armies)978 Stack *Stack::splitArmies(std::list<Army*> armies)
979 {
980   std::list<guint32> ids;
981   for (std::list<Army*>::iterator i = armies.begin(); i != armies.end(); i++)
982     ids.push_back((*i)->getId());
983   return splitArmies(ids);
984 }
985 
splitArmies(std::list<guint32> armies)986 Stack *Stack::splitArmies(std::list<guint32> armies)
987 {
988   if (armies.size() == 0) //we can't split 0 armies into a new stack.
989     return NULL;
990   if (armies.size() >= size()) //we can't split everyone into a new stack.
991     return NULL;
992   Stack *new_stack = NULL;
993   for (std::list<guint32>::iterator i = armies.begin(); i != armies.end(); i++)
994     {
995       bool found = false;
996       iterator found_army_it = end();
997       for (iterator it = begin(); it != end(); it++)
998 	{
999 	  if ((*it)->getId() == *i)
1000 	    {
1001 	      found = true;
1002 	      found_army_it = it;
1003 	      break;
1004 	    }
1005 	}
1006       if (found)
1007 	{
1008 	  if (new_stack == NULL)
1009 	    new_stack = new Stack(getOwner(), getPos());
1010 	  new_stack->push_back(*found_army_it);
1011 	  erase(found_army_it);
1012 	}
1013     }
1014   return new_stack;
1015 }
1016 
1017 //! split the armies in the stack that this much mp or more into a new stack.
splitArmiesWithMovement(guint32 mp)1018 Stack *Stack::splitArmiesWithMovement(guint32 mp)
1019 {
1020   std::list<Army*> armies;
1021   for (iterator it = begin(); it != end(); it++)
1022     if ((*it)->getMoves() >= mp)
1023       armies.push_back(*it);
1024   return splitArmies(armies);
1025 }
1026 
join(Stack * s)1027 void Stack::join(Stack *s)
1028 {
1029   for (iterator i = s->begin(); i != s->end(); i++)
1030     push_back(*i);
1031   s->clear();
1032 }
1033 
validate() const1034 bool Stack::validate() const
1035 {
1036   if (size() > MAX_STACK_SIZE)
1037     return false;
1038   if (size() == 0)
1039     return false;
1040   return true;
1041 }
1042 
isFull() const1043 bool Stack::isFull() const
1044 {
1045   if (size() >= MAX_STACK_SIZE)
1046     return true;
1047   return false;
1048 }
1049 
clearPath()1050 bool Stack::clearPath()
1051 {
1052   if (getPath())
1053     {
1054       if (getPath()->size() > 0)
1055 	{
1056 	  getPath()->clear();
1057 	  return true;
1058 	}
1059       else
1060 	return false;
1061     }
1062   else
1063     return false;
1064   return true;
1065 }
1066 
isOnCity() const1067 bool Stack::isOnCity() const
1068 {
1069   if (GameMap::getInstance()->getBuilding(getPos()) == Maptile::CITY)
1070     return true;
1071   return false;
1072 }
1073 
hasPath() const1074 bool Stack::hasPath() const
1075 {
1076   if (getPath() && getPath()->size() > 0)
1077     return true;
1078   return false;
1079 }
1080 
hasQuest() const1081 bool Stack::hasQuest() const
1082 {
1083   for (const_iterator it = begin(); it != end(); it++)
1084     {
1085       if ((*it)->isHero() == true)
1086         {
1087           Hero *hero = dynamic_cast<Hero*>(*it);
1088           if (hero->hasQuest() == true)
1089             return true;
1090 
1091         }
1092     }
1093   return false;
1094 }
1095 
hasArmyType(guint32 army_type) const1096 bool Stack::hasArmyType(guint32 army_type) const
1097 {
1098   for (const_iterator it = begin(); it != end(); it++)
1099     {
1100       if ((*it)->getTypeId() == army_type)
1101         return true;
1102     }
1103   return false;
1104 }
1105 
getFirstHeroWithoutAQuest() const1106 Hero *Stack::getFirstHeroWithoutAQuest() const
1107 {
1108   Hero *hero = NULL;
1109   for (const_iterator it = begin(); it != end(); it++)
1110     {
1111       if ((*it)->isHero() == false)
1112         continue;
1113       hero = dynamic_cast<Hero*>(*it);
1114       if (hero->hasQuest() == false)
1115         return hero;
1116     }
1117   return NULL;
1118 }
1119 
getFirstHeroWithAQuest() const1120 Hero *Stack::getFirstHeroWithAQuest() const
1121 {
1122   Hero *hero = NULL;
1123   for (const_iterator it = begin(); it != end(); it++)
1124     {
1125       if ((*it)->isHero() == false)
1126         continue;
1127       hero = dynamic_cast<Hero*>(*it);
1128       if (hero->hasQuest() == true)
1129         return hero;
1130     }
1131   return NULL;
1132 }
1133 
countItems() const1134 guint32 Stack::countItems() const
1135 {
1136   guint32 count = 0;
1137   Hero *hero = NULL;
1138   for (const_iterator it = begin(); it != end(); it++)
1139     {
1140       if ((*it)->isHero() == false)
1141         continue;
1142       hero = dynamic_cast<Hero*>(*it);
1143       Backpack *backpack = hero->getBackpack();
1144       count += backpack->size();
1145     }
1146   return count;
1147 }
1148 
hasUsableItem() const1149 bool Stack::hasUsableItem() const
1150 {
1151   for (const_iterator it = begin(); it != end(); it++)
1152     {
1153       if ((*it)->isHero() == false)
1154         continue;
1155       Hero *hero = dynamic_cast<Hero*>(*it);
1156       Backpack *backpack = hero->getBackpack();
1157       if (backpack->hasUsableItem() == true)
1158         return true;
1159     }
1160   return false;
1161 }
1162 
getUsableItems(std::list<Item * > & items) const1163 void Stack::getUsableItems(std::list<Item*> &items) const
1164 {
1165   for (const_iterator it = begin(); it != end(); it++)
1166     {
1167       if ((*it)->isHero() == false)
1168         continue;
1169       Hero *hero = dynamic_cast<Hero*>(*it);
1170       Backpack *backpack = hero->getBackpack();
1171       std::list<Item*> backpack_items;
1172       backpack->getUsableItems(backpack_items);
1173       //now we dwindle the items from the backpack, depending on whether or
1174       //not they're actually usable.
1175       for (std::list<Item*>::iterator i = backpack_items.begin();
1176            i !=backpack_items.end(); i++)
1177         {
1178           Maptile::Building b = GameMap::getInstance()->getBuilding(getPos());
1179           Ruin *ruin = GameMap::getInstance()->getRuin(getPos());
1180           bool ruin_has_occupant = false;
1181           if (ruin)
1182             {
1183               if (ruin->isSearched() == false && ruin->getOccupant() != NULL)
1184                 ruin_has_occupant = true;
1185             }
1186           bool victims = Playerlist::getInstance()->countPlayersAlive() > 1;
1187           if ((*i)->isCurrentlyUsable(b, !GameMap::getInstance()->getBackpacks().empty(),
1188                                       victims, ruin_has_occupant,
1189                                       GameMap::friendlyCitiesPresent(),
1190                                       GameMap::enemyCitiesPresent(),
1191                                       GameMap::neutralCitiesPresent()) == false)
1192             i = backpack_items.erase(i);
1193         }
1194       if (backpack_items.size() > 0)
1195         items.insert(std::end(items),
1196                      std::begin(backpack_items), std::end(backpack_items));
1197     }
1198   return;
1199 }
1200 
getHeroWithItem(Item * item) const1201 Hero* Stack::getHeroWithItem(Item *item) const
1202 {
1203   for (const_iterator it = begin(); it != end(); it++)
1204     {
1205       if ((*it)->isHero() == false)
1206         continue;
1207       Hero *hero = dynamic_cast<Hero*>(*it);
1208       Backpack *backpack = hero->getBackpack();
1209       if (backpack->getItemById(item->getId()) != NULL)
1210         return hero;
1211     }
1212   return NULL;
1213 }
1214 
kill()1215 void Stack::kill()
1216 {
1217   for (iterator it = begin(); it != end(); it++)
1218     (*it)->kill();
1219 }
1220 
1221 //! Sink the stack.  return true if we've sunk any part of the stack.
killArmyUnitsInBoats()1222 bool Stack::killArmyUnitsInBoats()
1223 {
1224   bool retval = false;
1225   if (GameMap::getInstance()->getTile(getPos())->getType() != Tile::WATER)
1226     return retval;
1227   if (isFlying())
1228     return retval;
1229   std::list<Army*> flyers;
1230   std::list<Army*> landedother;
1231   std::list<Army*> landedhero;
1232   for (iterator it = this->begin(); it != this->end(); it++)
1233     {
1234       guint32 bonus = (*it)->getStat(Army::MOVE_BONUS);
1235       if (bonus == Tile::GRASS || (bonus & Tile::WATER) == 0 ||
1236 	  (bonus & Tile::FOREST) == 0 || (bonus & Tile::HILLS) == 0 ||
1237 	  (bonus & Tile::MOUNTAIN) == 0 || (bonus & Tile::SWAMP) == 0)
1238 	{
1239 	  if ((*it)->isHero())
1240             landedhero.push_back(*it);
1241 	  else
1242             landedother.push_back(*it);
1243 	}
1244       else
1245         flyers.push_back(*it);
1246     }
1247   //sink the landed others.
1248   for (std::list<Army*>::iterator it = landedother.begin();
1249        it != landedother.end(); it++)
1250     {
1251       retval = true;
1252       (*it)->kill();
1253     }
1254   int num_heroes_to_sink = landedhero.size() - flyers.size();
1255   if (num_heroes_to_sink > 0)
1256     {
1257       //sink the unlucky heroes and any items they might have.
1258       for (std::list<Army*>::reverse_iterator it = landedhero.rbegin();
1259            it != landedhero.rend(); it++)
1260         {
1261           retval = true;
1262           (*it)->kill();
1263           num_heroes_to_sink--;
1264           if (num_heroes_to_sink <= 0)
1265             break;
1266         }
1267     }
1268 
1269   for (std::list<Army*>::iterator it = landedhero.begin();
1270        it != landedhero.end(); it++)
1271     {
1272       if ((*it)->getHP() > 0)
1273         (*it)->setInShip(false); //we're being carried by a flyer
1274     }
1275   return retval;
1276 }
1277 
1278 //! Kill the army units that are the given army type
killArmies(guint32 army_type)1279 bool Stack::killArmies(guint32 army_type)
1280 {
1281   bool killed = false;
1282   for (iterator it = begin(); it != end(); it++)
1283     {
1284       if ((*it)->getTypeId() == army_type)
1285         {
1286           (*it)->kill();
1287           killed = true;
1288         }
1289     }
1290   return killed;
1291 }
1292 
1293 std::list<guint32> compare_ids;
compareIds(const Army * lhs,const Army * rhs)1294 bool Stack::compareIds(const Army *lhs, const Army *rhs)
1295 {
1296   guint32 lhs_rank = MAX_STACK_SIZE + 1;
1297   guint32 rhs_rank = MAX_STACK_SIZE + 1;
1298   int count = 0;
1299   for (std::list<guint32>::iterator i = compare_ids.begin();
1300        i != compare_ids.end(); i++)
1301     {
1302       if (lhs && *i == lhs->getId())
1303         lhs_rank = count;
1304       if (rhs && *i == rhs->getId())
1305         rhs_rank = count;
1306       count++;
1307     }
1308   return lhs_rank < rhs_rank;
1309 }
sortByIds(std::list<guint32> ids)1310 void Stack::sortByIds(std::list<guint32> ids)
1311 {
1312   compare_ids = ids;
1313   sort(compareIds);
1314 }
1315 
updateShipStatus(Vector<int> dest)1316 void Stack::updateShipStatus(Vector<int> dest)
1317 {
1318   bool to_water = (GameMap::getInstance()->getTile(dest)->getType() == Tile::WATER);
1319   bool to_bridge = (GameMap::getBridge(dest) != NULL);
1320   for (Stack::iterator it = begin(); it != end(); it++)
1321     {
1322       if (to_water && !to_bridge &&
1323           ((*it)->getStat(Army::MOVE_BONUS) & Tile::WATER) == 0)
1324         (*it)->setInShip(true);
1325       else
1326         (*it)->setInShip(false);
1327     }
1328 }
1329 
hasDeadArmies() const1330 bool Stack::hasDeadArmies() const
1331 {
1332   for (const_iterator i = begin(); i != end(); i++)
1333     if ((*i)->getHP() == 0)
1334       return true;
1335   return false;
1336 }
1337 
removeArmiesWithoutArmyType(guint32 armyset)1338 bool Stack::removeArmiesWithoutArmyType(guint32 armyset)
1339 {
1340   bool removedArmy = false;
1341   for (iterator i = begin(); i != end(); i++)
1342     {
1343       Armyset *a = Armysetlist::getInstance()->get(armyset);
1344       ArmyProto *armyproto = a->lookupArmyByType((*i)->getTypeId());
1345       if (armyproto == NULL)
1346         {
1347           i = flErase(i);
1348           if (size() > 0)
1349             i--;
1350           removedArmy = true;
1351           continue;
1352         }
1353     }
1354   return removedArmy;
1355 }
1356 
1357 /*
1358  * the idea here is that a hero has a stack flight item, and we don't want
1359  * to split the stack when it is in the precarious position of stranding
1360  * army units in water or mountains.
1361  *
1362  * this routine just detects when splitting a stack would strand an army unit.
1363  *
1364  * we could do a better job detecting the non-flyers in the stack.
1365  */
fliesWithItemAndNonFlyersOverWaterOrMountains() const1366 bool Stack::fliesWithItemAndNonFlyersOverWaterOrMountains() const
1367 {
1368   Maptile *mtile = GameMap::getInstance()->getTile(getPos());
1369   bool on_water = mtile->getType() == Tile::WATER &&
1370     mtile->getBuilding() != Maptile::BRIDGE;
1371   bool on_mountains = mtile->getType() == Tile::MOUNTAIN &&
1372     mtile->getBuilding() != Maptile::ROAD;
1373   if (!on_water && !on_mountains)
1374     return false;
1375   bool flies_with_item = false;
1376   std::vector<guint32> ids;
1377   getHeroes(ids);
1378   for (auto id : ids)
1379     {
1380       Army *a = getArmyById(id);
1381       Hero *h = dynamic_cast<Hero*>(a);
1382       if (h->getBackpack()->countStackFlightGivers() > 0)
1383         flies_with_item = true;
1384     }
1385   return flies_with_item && size() > 1 && (on_water || on_mountains);
1386 }
1387 // End of file
1388