1 // Copyright (C) 2000, 2001, 2002, 2003 Michael Bartl
2 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
3 // Copyright (C) 2004, 2005 Andrea Paternesi
4 // Copyright (C) 2004 John Farrell
5 // Copyright (C) 2007, 2008, 2009, 2010, 2011, 2014, 2015 Ben Asselstine
6 // Copyright (C) 2007, 2008 Ole Laursen
7 //
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.
12 //
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.
17 //
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
21 // 02110-1301, USA.
22
23 #include <config.h>
24 #include "signal.h"
25 #include <sigc++/functors/mem_fun.h>
26 #include <sigc++/adaptors/bind.h>
27 #include <assert.h>
28
29 #include "stacklist.h"
30 #include "stack.h"
31 #include "city.h"
32 #include "path.h"
33 #include "playerlist.h"
34 #include "xmlhelper.h"
35 #include "Item.h"
36 #include "hero.h"
37 #include "Backpack.h"
38 #include "LocationList.h"
39 #include "GameMap.h"
40 #include "stacktile.h"
41 #include "stackreflist.h"
42
43 Glib::ustring Stacklist::d_tag = "stacklist";
44
45 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
46 #define debug(x)
47
getPosition(guint32 id)48 Vector<int> Stacklist::getPosition(guint32 id)
49 {
50 for (Playerlist::iterator pit = Playerlist::getInstance()->begin();
51 pit != Playerlist::getInstance()->end(); pit++)
52 {
53 Stacklist* mylist = (*pit)->getStacklist();
54 for (const_iterator it = mylist->begin(); it !=mylist->end(); it++)
55 for (Stack::const_iterator sit = (*it)->begin(); sit != (*it)->end(); sit++)
56 if ((*sit)->getId() == id)
57 return (*it)->getPos();
58 }
59
60 return Vector<int>(-1,-1);
61 }
62
63 //search all player's stacklists to find this stack
deleteStack(Stack * s)64 bool Stacklist::deleteStack(Stack* s)
65 {
66 for (Playerlist::iterator pit = Playerlist::getInstance()->begin();
67 pit != Playerlist::getInstance()->end(); pit++)
68 {
69 Stacklist* mylist = (*pit)->getStacklist();
70 for (const_iterator it = mylist->begin(); it != mylist->end(); it++)
71 if ((*it) == s)
72 return mylist->flRemove(s);
73 }
74 return false;
75 }
76
deleteStack(guint32 id)77 bool Stacklist::deleteStack(guint32 id)
78 {
79 for (Playerlist::iterator pit = Playerlist::getInstance()->begin();
80 pit != Playerlist::getInstance()->end(); pit++)
81 {
82 Stacklist* mylist = (*pit)->getStacklist();
83 for (const_iterator it = mylist->begin(); it != mylist->end(); it++)
84 if ((*it)->getId() == id)
85 return mylist->flRemove(*it);
86 }
87 return false;
88 }
89
calculateUpkeep() const90 guint32 Stacklist::calculateUpkeep() const
91 {
92 guint32 upkeep = 0;
93 for (const_iterator it = begin(); it != end(); it++)
94 upkeep += (*it)->getUpkeep();
95 return upkeep;
96 }
97
payUpkeep(Player * p)98 void Stacklist::payUpkeep(Player *p)
99 {
100 for (iterator it = begin(); it != end(); it++)
101 (*it)->payUpkeep(p);
102 }
103
check()104 bool Stacklist::check()
105 {
106 for (iterator it = begin(); it != end(); it++)
107 {
108 if ((*it)->getOwner()->isComputer() == false)
109 continue;
110 std::vector<Stack*> f = GameMap::getFriendlyStacks((*it)->getPos());
111 if (f.size() > 1)
112 {
113 fprintf (stderr, "%lu stacks found on %d,%d\n", f.size(),
114 (*it)->getPos().x, (*it)->getPos().y);
115 for (std::vector<Stack*>::iterator t = f.begin(); t != f.end(); t++)
116 {
117 Stack *stack = *t;
118 if (stack)
119 {
120 printf("stack id: %d\n", stack->getId());
121 printf("\tsize is %lu\n", stack->size());
122 }
123 else
124 printf("null stack\n");
125 }
126 return false;
127 }
128 }
129 return true;
130 }
131
resetStacks()132 void Stacklist::resetStacks()
133 {
134 for (iterator it = begin(); it != end(); it++)
135 (*it)->reset();
136 }
137
nextTurn()138 void Stacklist::nextTurn()
139 {
140 debug("nextTurn()");
141 resetStacks();
142
143 for (iterator it = begin(); it != end(); it++)
144 for (iterator jit = begin(); jit != end(); jit++)
145 if (*jit != *it)
146 if ((*jit)->getId() == (*it)->getId())
147 {
148 fprintf (stderr, "duplicate army id %d found\n", (*it)->getId());
149 exit (1);
150 }
151 //printf("checking at next-turn time\n");
152 //check();
153 }
154
getDefendersInCity(const City * city)155 std::vector<Stack*> Stacklist::getDefendersInCity(const City *city)
156 {
157 debug("getDefendersInCity()");
158
159 std::vector<Stack*> stackvector;
160 Vector<int> pos = city->getPos();
161
162 for (unsigned int i = pos.x; i < pos.x + city->getSize(); i++)
163 {
164 for (unsigned int j = pos.y; j < pos.y + city->getSize(); j++)
165 {
166 Vector<int> p = Vector<int>(i,j);
167 std::vector<Stack *>stacks =
168 GameMap::getFriendlyStacks(p, city->getOwner());
169 for (std::vector<Stack*>::iterator it = stacks.begin();
170 it != stacks.end(); it++)
171 stackvector.push_back(*it);
172 }
173 }
174
175 return stackvector;
176 }
177
getNoOfStacks()178 unsigned int Stacklist::getNoOfStacks()
179 {
180 unsigned int mysize = 0;
181
182 for (Playerlist::iterator pit = Playerlist::getInstance()->begin();
183 pit != Playerlist::getInstance()->end(); pit++)
184 mysize += (*pit)->getStacklist()->size();
185
186 return mysize;
187 }
188
getNoOfArmies()189 unsigned int Stacklist::getNoOfArmies()
190 {
191 unsigned int mysize = 0;
192
193 for (Playerlist::iterator pit = Playerlist::getInstance()->begin();
194 pit != Playerlist::getInstance()->end(); pit++)
195 mysize += (*pit)->getStacklist()->countArmies();
196
197 return mysize;
198 }
199
countArmies() const200 unsigned int Stacklist::countArmies() const
201 {
202 unsigned int mysize = 0;
203
204 for (const_iterator it = begin(); it != end(); it++)
205 mysize += (*it)->size();
206
207 return mysize;
208 }
209
Stacklist()210 Stacklist::Stacklist()
211 :d_activestack(0)
212 {
213 }
214
Stacklist(Stacklist * stacklist)215 Stacklist::Stacklist(Stacklist *stacklist)
216 :d_activestack(0)
217 {
218 for (iterator it = stacklist->begin(); it != stacklist->end(); it++)
219 add(new Stack(**it));
220 }
221
Stacklist(XML_Helper * helper)222 Stacklist::Stacklist(XML_Helper* helper)
223 :d_activestack(0)
224 {
225 helper->registerTag(Stack::d_tag, sigc::mem_fun((*this), &Stacklist::load));
226 load(Stacklist::d_tag, helper);
227 }
228
~Stacklist()229 Stacklist::~Stacklist()
230 {
231 //disconnect the signals
232 for (auto it: d_connections)
233 for (auto lit: it.second)
234 lit.disconnect();
235 flClear();
236 }
237
getNextMovable() const238 Stack* Stacklist::getNextMovable() const
239 {
240 Player *player = Playerlist::getInstance()->getActiveplayer();
241
242 const_iterator it = begin();
243
244 //first, if we already have an active stack, loop through until we meet it
245 if (d_activestack)
246 {
247 for (; *it != d_activestack; it++);
248 it++; //we want to start with the next stack :)
249 }
250
251 //continue looping until we meet the next not defending stack of this player
252 for (; it != end(); ++it)
253 {
254 Stack *s = *it;
255 if (s->getOwner() == player && !s->getDefending() &&
256 !s->getParked() && s->canMove())
257 return s;
258 }
259
260 //still not found a stack? Then start looping from the beginning until we
261 //meet the activestack again. If there is no activestack, we have already
262 //looped through the whole list, so stop here
263 if (!d_activestack)
264 return 0;
265
266 for (it = begin(); *it != d_activestack; ++it)
267 {
268 Stack *s = *it;
269 if (s->getOwner() == player && !s->getDefending() &&
270 !s->getParked() && s->canMove())
271 return s;
272 }
273
274 //still there? well, then we have only one stack left.
275 if (d_activestack->getDefending() || d_activestack->getParked())
276 return 0;
277 else
278 return d_activestack;
279 }
280
getStackById(guint32 id) const281 Stack *Stacklist::getStackById(guint32 id) const
282 {
283 IdMap::const_iterator it = d_id.find(id);
284 if (it != d_id.end())
285 return (*it).second;
286 else
287 return NULL;
288 }
289
getArmyStackById(guint32 army) const290 Stack *Stacklist::getArmyStackById(guint32 army) const
291 {
292 for (Stacklist::const_iterator i = begin(), e = end(); i != e; ++i)
293 if ((*i)->getArmyById(army))
294 return *i;
295 return NULL;
296 }
297
flClear()298 void Stacklist::flClear()
299 {
300 d_activestack = 0;
301
302 for (iterator it = begin(); it != end(); it++)
303 delete (*it);
304
305 clear();
306 }
307
flErase(iterator object)308 Stacklist::iterator Stacklist::flErase(iterator object)
309 {
310 if (d_activestack == (*object))
311 d_activestack = 0;
312 delete (*object);
313 return erase(object);
314 }
315
flRemove(guint32 id)316 bool Stacklist::flRemove(guint32 id)
317 {
318 Stack *s = getStackById(id);
319 if (s == NULL)
320 return false;
321 return flRemove(s);
322 }
323
flRemove(Stack * object)324 bool Stacklist::flRemove(Stack* object)
325 {
326 if (object == NULL)
327 return false;
328 iterator stackit = find(begin(), end(), object);
329 if (stackit != end())
330 {
331 if (d_activestack == object)
332 d_activestack = 0;
333 assert (object->getId() == (*stackit)->getId());
334 deletePositionFromMap(object);
335 delete object;
336 erase(stackit);
337 return true;
338 }
339
340 return false;
341 }
342
save(XML_Helper * helper) const343 bool Stacklist::save(XML_Helper* helper) const
344 {
345 bool retval = true;
346
347 retval &= helper->openTag(Stacklist::d_tag);
348 if (d_activestack)
349 retval &= helper->saveData("active", d_activestack->getId());
350 else
351 retval &= helper->saveData("active", 0);
352
353 //save stacks
354 for (const_iterator it = begin(); it != end(); it++)
355 retval &= (*it)->save(helper);
356
357 retval &= helper->closeTag();
358
359 return retval;
360 }
361
enoughMoves() const362 bool Stacklist::enoughMoves() const
363 {
364 for (const_iterator it = begin(); it != end(); it++)
365 {
366 Stack* s = *it;
367 if (!s->getPath()->empty() && s->enoughMoves())
368 return true;
369 }
370
371 return false;
372 }
373
load(Glib::ustring tag,XML_Helper * helper)374 bool Stacklist::load(Glib::ustring tag, XML_Helper* helper)
375 {
376 static guint32 active = 0;
377
378 if (tag == Stacklist::d_tag)
379 {
380 helper->getData(active, "active");
381 return true;
382 }
383
384 if (tag == Stack::d_tag)
385 {
386 Stack* s = new Stack(helper);
387 if (active != 0 && s->getId() == active)
388 d_activestack = s;
389
390 add(s);
391 return true;
392 }
393
394 return false;
395 }
396
getHeroes(std::vector<guint32> & dst) const397 void Stacklist::getHeroes(std::vector<guint32>& dst) const
398 {
399 for (Stacklist::const_iterator it = begin(); it != end(); it++)
400 (*it)->getHeroes(dst);
401 }
402
collectTaxes(Player * p,guint32 num_cities) const403 void Stacklist::collectTaxes(Player *p, guint32 num_cities) const
404 {
405 std::vector<guint32> hero_ids;
406 getHeroes(hero_ids);
407
408 //now let's see if we have any items that give us gold per city
409 for (std::vector<guint32>::iterator it = hero_ids.begin();
410 it != hero_ids.end(); it++)
411 {
412 Stack *stack = getArmyStackById(*it);
413 Army *army = stack->getArmyById(*it);
414 Hero *hero = static_cast<Hero*>(army);
415 guint32 bonus = hero->getBackpack()->countGoldBonuses();
416 p->addGold(bonus * num_cities);
417 }
418 }
419
420 // do we have enough movement points to get to a place on our path
421 // where we can drop the stack on a suitable tile?
422 //suitable = empty tile, or
423 //a tile with a friendly stack that has a small enough stack to merge with
424 //we're currently at a tile prior to a stack that's too big.
425 //problem point: getting into a boat.
426
canJumpOverTooLargeStack(Stack * s)427 bool Stacklist::canJumpOverTooLargeStack(Stack *s)
428 {
429 bool found = false;
430 guint32 mp = s->getMoves();
431 for (Path::iterator it = s->getPath()->begin(); it != s->getPath()->end(); it++)
432 {
433 guint32 moves = s->calculateTileMovementCost(*it);
434 if (moves > mp)
435 return false;
436 mp -= moves;
437 City *enemy = GameMap::getEnemyCity(*it);
438 if (enemy != NULL && enemy->isBurnt() == false)
439 return false;
440 if (GameMap::getEnemyStack(*it) != NULL)
441 return false;
442 if (GameMap::canJoin(s, *it) == true)
443 return true;
444 }
445 return found;
446 }
447
getHeroes() const448 std::list<Hero*> Stacklist::getHeroes() const
449 {
450 std::list<Hero*> heroes;
451 std::vector<guint32> hero_ids;
452 getHeroes(hero_ids);
453 for (std::vector<guint32>::const_iterator it = hero_ids.begin();
454 it != hero_ids.end(); it++)
455 {
456 Stack *s = getArmyStackById(*it);
457 if (s)
458 {
459 Hero *h = dynamic_cast<Hero*>(s->getArmyById(*it));
460 if (h)
461 heroes.push_back(h);
462 }
463 }
464 return heroes;
465 }
466
getNearestHero(Vector<int> pos,int dist) const467 Hero *Stacklist::getNearestHero(Vector<int> pos, int dist) const
468 {
469 std::list<Hero*> heroes = getHeroes();
470 LocationList<Location*> hero_locales;
471 for (std::list<Hero*>::iterator it = heroes.begin(); it != heroes.end(); it++)
472 hero_locales.push_back(new Location(getPosition((*it)->getId()), 1));
473 Location *hero_locale = hero_locales.getNearestObjectBefore(pos, dist);
474 if (hero_locale)
475 {
476 for (std::list<Hero*>::iterator it = heroes.begin(); it != heroes.end();
477 it++)
478 {
479 if (getPosition((*it)->getId()) == hero_locale->getPos())
480 return (*it);
481 }
482 }
483 return NULL;
484 }
485
addPositionToMap(Stack * stack) const486 bool Stacklist::addPositionToMap(Stack *stack) const
487 {
488 snewpos.emit(stack, stack->getPos());
489 return true;
490 }
491
deletePositionFromMap(Stack * stack) const492 bool Stacklist::deletePositionFromMap(Stack *stack) const
493 {
494 soldpos.emit(stack, stack->getPos());
495 return true;
496 }
497
add(Stack * stack)498 void Stacklist::add(Stack *stack)
499 {
500 push_back(stack);
501 d_id[stack->getId()] = stack;
502 if (stack->getPos() != Vector<int>(-1,-1))
503 {
504 bool added = addPositionToMap(stack);
505 if (!added)
506 assert(1 == 0);
507 std::list<sigc::connection> conn;
508 conn.push_back(stack->smoving.connect
509 (sigc::mem_fun (this, &Stacklist::on_stack_starts_moving)));
510 conn.push_back(stack->smoved.connect
511 (sigc::mem_fun (this, &Stacklist::on_stack_stops_moving)));
512 conn.push_back(stack->sdying.connect
513 (sigc::mem_fun (this, &Stacklist::on_stack_died)));
514 conn.push_back(stack->sgrouped.connect
515 (sigc::mem_fun (this, &Stacklist::on_stack_grouped)));
516 d_connections[stack] = conn;
517 }
518 }
519
on_stack_grouped(Stack * stack,bool grouped)520 void Stacklist::on_stack_grouped (Stack *stack, bool grouped)
521 {
522 sgrouped.emit(stack, grouped);
523 }
524
on_stack_died(Stack * stack)525 void Stacklist::on_stack_died (Stack *stack)
526 {
527 deletePositionFromMap(stack);
528 ConnectionMap::iterator it = d_connections.find(stack);
529 if (it != d_connections.end())
530 {
531 for (auto lit: (*it).second)
532 lit.disconnect();
533 }
534 d_id.erase(d_id.find(stack->getId()));
535 sstackDied.emit ();
536 return;
537 }
on_stack_starts_moving(Stack * stack)538 void Stacklist::on_stack_starts_moving (Stack *stack)
539 {
540 deletePositionFromMap(stack);
541 return;
542 }
on_stack_stops_moving(Stack * stack)543 void Stacklist::on_stack_stops_moving (Stack *stack)
544 {
545 addPositionToMap(stack);
546 return;
547 }
setActivestack(Stack * activestack)548 void Stacklist::setActivestack(Stack* activestack)
549 {
550 d_activestack = activestack;
551 }
552
drainAllMovement()553 void Stacklist::drainAllMovement()
554 {
555 for (iterator it = begin(); it != end(); it++)
556 (*it)->drainMovement();
557 }
558
changeOwnership(Player * old_owner,Player * new_owner)559 void Stacklist::changeOwnership(Player *old_owner, Player *new_owner)
560 {
561 StackReflist *stacks = new StackReflist(old_owner->getStacklist());
562 for (StackReflist::iterator it = stacks->begin(); it != stacks->end(); it++)
563 Stacklist::changeOwnership (*it, new_owner);
564 delete stacks;
565 }
566
changeOwnership(Stack * stack,Player * new_owner)567 Stack* Stacklist::changeOwnership(Stack *stack, Player *new_owner)
568 {
569 if (new_owner != stack->getOwner())
570 {
571 Stack *new_stack = new Stack(*stack);
572 stack->getOwner()->getStacklist()->flRemove(stack);
573 new_owner->addStack(new_stack);
574 return new_stack;
575 }
576 return stack;
577 }
578
getPositions() const579 std::list<Vector<int> > Stacklist::getPositions() const
580 {
581 std::list<Vector<int> > points;
582 for (const_iterator it = begin(); it != end(); it++)
583 {
584 if (std::find(points.begin(), points.end(), (*it)->getPos()) ==
585 points.end())
586 points.push_back((*it)->getPos());
587 }
588 return points;
589 }
590
getStacksWithItems() const591 std::list<Stack*> Stacklist::getStacksWithItems() const
592 {
593 std::list<Stack*> stacks;
594 for (const_iterator it = begin(); it != end(); it++)
595 {
596 if ((*it)->countItems() > 0)
597 stacks.push_back((*it));
598 }
599 return stacks;
600 }
601
kill()602 std::list<Stack*> Stacklist::kill()
603 {
604 std::list<Stack*> stacks;
605 for (iterator it = begin(); it != end(); it++)
606 {
607 (*it)->kill();
608 stacks.push_back(*it);
609 }
610 return stacks;
611 }
612
killArmyUnitsInBoats()613 std::list<Stack*> Stacklist::killArmyUnitsInBoats()
614 {
615 std::list<Stack*> stacks;
616 for (iterator it = begin(); it != end(); it++)
617 {
618 if ((*it)->hasShip())
619 {
620 if ((*it)->killArmyUnitsInBoats())
621 stacks.push_back(*it);
622 }
623 }
624 return stacks;
625 }
626
killArmies(guint32 army_type)627 std::list<Stack*> Stacklist::killArmies(guint32 army_type)
628 {
629 std::list<Stack*> stacks;
630 for (iterator it = begin(); it != end(); it++)
631 {
632 if ((*it)->killArmies(army_type))
633 stacks.push_back(*it);
634 }
635 return stacks;
636 }
637
getUsableItems() const638 std::list<Item*> Stacklist::getUsableItems() const
639 {
640 std::list<Item*> items;
641 for (const_iterator it = begin(); it != end(); it++)
642 {
643 if ((*it)->hasUsableItem())
644 (*it)->getUsableItems(items);
645 }
646 return items;
647 }
648
hasUsableItem() const649 bool Stacklist::hasUsableItem() const
650 {
651 for (const_iterator it = begin(); it != end(); it++)
652 {
653 if ((*it)->hasUsableItem())
654 return true;
655 }
656 return false;
657 }
658
getItemHolder(Item * item,Stack ** stack,Hero ** hero) const659 bool Stacklist::getItemHolder(Item *item, Stack **stack, Hero **hero) const
660 {
661 for (const_iterator it = begin(); it != end(); it++)
662 {
663 *hero = (*it)->getHeroWithItem(item);
664 if (*hero != NULL)
665 {
666 *stack = *it;
667 return true;
668 }
669 }
670 return false;
671 }
672
countMovableStacks() const673 guint32 Stacklist::countMovableStacks() const
674 {
675 guint32 count = 0;
676 for (const_iterator i = begin(); i != end(); i++)
677 {
678 if (!(*i)->getParked() && (*i)->canMove())
679 {
680 if ((*i)->getPath()->size() > 0 && (*i)->enoughMoves())
681 count++;
682 else if ((*i)->getPath()->size() == 0)
683 count++;
684 }
685 }
686 return count;
687 }
688
689 // End of file
690