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