1 // Copyright (C) 2000, 2001, 2002, 2003 Michael Bartl
2 // Copyright (C) 2001, 2002, 2003, 2004, 2005 Ulf Lorenz
3 // Copyright (C) 2004 John Farrell
4 // Copyright (C) 2005 Andrea Paternesi
5 // Copyright (C) 2006, 2007, 2008, 2009, 2010, 2014, 2015, 2020 Ben Asselstine
6 // Copyright (C) 2007 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 <sigc++/functors/mem_fun.h>
24
25 #include "citylist.h"
26 #include "city.h"
27 #include "playerlist.h"
28 #include <limits.h>
29 #include "xmlhelper.h"
30 #include "hero.h"
31 #include "stack.h"
32 #include "armyprodbase.h"
33 #include "GameMap.h"
34 #include "cityset.h"
35 #include "citysetlist.h"
36 #include "PathCalculator.h"
37 #include "rnd.h"
38
39 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
40 #define debug(x)
41
42 Glib::ustring Citylist::d_tag = "citylist";
43
44 Citylist* Citylist::s_instance = 0;
45
getInstance()46 Citylist* Citylist::getInstance()
47 {
48 if (s_instance == 0)
49 s_instance = new Citylist();
50
51 return s_instance;
52 }
53
getInstance(XML_Helper * helper)54 Citylist* Citylist::getInstance(XML_Helper* helper)
55 {
56 if (s_instance)
57 deleteInstance();
58
59 s_instance = new Citylist(helper);
60 return s_instance;
61 }
62
deleteInstance()63 void Citylist::deleteInstance()
64 {
65 if (s_instance)
66 delete s_instance;
67
68 s_instance = 0;
69 }
70
Citylist()71 Citylist::Citylist()
72 {
73 }
74
Citylist(XML_Helper * helper)75 Citylist::Citylist(XML_Helper* helper)
76 {
77 // simply ask the helper to inform us when a city tag is opened
78 helper->registerTag(City::d_tag, sigc::mem_fun(this, &Citylist::load));
79 }
80
countCities() const81 int Citylist::countCities() const
82 {
83 int cities = 0;
84
85 for (const_iterator it = begin(); it != end(); it++)
86 {
87 if ((*it)->isBurnt())
88 continue;
89 cities++;
90 }
91
92 return cities;
93 }
94
countCities(Player * player) const95 int Citylist::countCities(Player* player) const
96 {
97 int cities = 0;
98
99 for (const_iterator it = begin(); it != end(); it++)
100 {
101 if ((*it)->isBurnt())
102 continue;
103 if ((*it)->getOwner() == player) cities++;
104 }
105
106 return cities;
107 }
108
collectTaxes(Player * p) const109 void Citylist::collectTaxes(Player* p) const
110 {
111 // Collect the taxes
112 for (const_iterator it = begin(); it != end(); it++)
113 if ((*it)->getOwner() == p && (*it)->isBurnt() == false)
114 p->addGold((*it)->getGold());
115
116 }
117
118 //calculate the amount of money new armies will cost in the upcoming turn.
calculateUpcomingUpkeep(Player * p) const119 guint32 Citylist::calculateUpcomingUpkeep(Player *p) const
120 {
121 guint32 total = 0;
122 for (const_iterator it = begin(); it != end(); it++)
123 if ((*it)->getOwner() == p && (*it)->isBurnt() == false)
124 {
125 if ((*it)->getDuration() == 1)
126 {
127 int slot =(*it)->getActiveProductionSlot();
128 if (slot == -1)
129 continue;
130 const ArmyProdBase *a = (*it)->getProductionBase(slot);
131 total += a->getUpkeep();
132 total += a->getProductionCost();
133 }
134 }
135 return total;
136 }
137
nextTurn(Player * p)138 void Citylist::nextTurn(Player* p)
139 {
140 debug("next_turn(" <<p->getName() <<")");
141
142 // Because players are nextTurn'd before cities, the income, treasury,
143 // and upkeep are calculated already for the upcoming round.
144
145 //we've already collected taxes this round, so hopefully our
146 //treasury has enough money to pay our city upkeep.
147
148 guint32 cost_of_new_armies = calculateUpcomingUpkeep(p);
149 if (p->getGold() < (int)cost_of_new_armies)
150 {
151 int diff = cost_of_new_armies - p->getGold();
152 //then we have to turn off enough production to make up for diff
153 //gold pieces.
154 for (iterator it = begin(); it != end(); it++)
155 {
156 if ((*it)->isBurnt() == true)
157 continue;
158 if ((*it)->getOwner() != p)
159 continue;
160 int slot =(*it)->getActiveProductionSlot();
161 if (slot == -1)
162 continue;
163 const ArmyProdBase *a = (*it)->getProductionBase(slot);
164 diff -= a->getUpkeep();
165
166 p->cityTooPoorToProduce((*it), slot);
167 if (diff < 0)
168 break;
169 }
170 }
171
172 // This iteration adds the city production to the player
173 for (iterator it = begin(); it != end(); it++)
174 {
175 if ((*it)->getOwner() == p)
176 (*it)->nextTurn();
177 }
178
179 }
180
isFogged(void * object)181 static bool isFogged(void *object)
182 {
183 return ((City*)object)->isVisible(Playerlist::getViewingplayer()) == false;
184 }
185
isBurnt(void * object)186 static bool isBurnt(void *object)
187 {
188 return ((City*)object)->isBurnt();
189 }
190
isNotOwnedByNeutral(void * object)191 static bool isNotOwnedByNeutral(void *object)
192 {
193 return ((City*)object)->getOwner() != Playerlist::getInstance()->getNeutral();
194 }
195
isNotOwnedByActivePlayer(void * object)196 static bool isNotOwnedByActivePlayer(void *object)
197 {
198 return ((City*)object)->getOwner() != Playerlist::getActiveplayer();
199 }
200
isOwnedByActivePlayer(void * object)201 static bool isOwnedByActivePlayer(void *object)
202 {
203 return ((City*)object)->getOwner() == Playerlist::getActiveplayer();
204 }
205
isNotOwnedByEnemy(void * object)206 static bool isNotOwnedByEnemy(void *object)
207 {
208 Player *p = Playerlist::getActiveplayer();
209 if (!p)
210 return false;
211 City *city = ((City*)object);
212 if (city->getOwner() != p &&
213 p->getDiplomaticState(city->getOwner()) == Player::AT_WAR)
214 return false;
215 return true;
216 }
217
canNotAcceptMoreVectoring(void * object)218 static bool canNotAcceptMoreVectoring(void *object)
219 {
220 City *c = ((City*)object);
221 guint32 num = Citylist::getInstance()->countCitiesVectoringTo(c);
222 if (num < MAX_CITIES_VECTORED_TO_ONE_CITY)
223 return false;
224 return true;
225 }
226
getNearestEnemyCity(const Vector<int> & pos) const227 City* Citylist::getNearestEnemyCity(const Vector<int>& pos) const
228 {
229 std::list<bool (*)(void *)> filters;
230 filters.push_back(isBurnt);
231 filters.push_back(isNotOwnedByEnemy);
232 return getNearestObject(pos, &filters);
233 }
234
getClosestEnemyCity(const Stack * stack) const235 City* Citylist::getClosestEnemyCity(const Stack *stack) const
236 {
237 std::list<bool (*)(void *)> filters;
238 filters.push_back(isBurnt);
239 filters.push_back(isNotOwnedByEnemy);
240 return getClosestObject(stack, &filters);
241 }
242
getClosestForeignCity(const Stack * stack) const243 City* Citylist::getClosestForeignCity(const Stack *stack) const
244 {
245 std::list<bool (*)(void *)> filters;
246 filters.push_back(isBurnt);
247 filters.push_back(isOwnedByActivePlayer);
248 return getClosestObject(stack, &filters);
249 }
250
getNearestForeignCity(const Vector<int> & pos) const251 City* Citylist::getNearestForeignCity(const Vector<int>& pos) const
252 {
253 std::list<bool (*)(void *)> filters;
254 filters.push_back(isBurnt);
255 filters.push_back(isOwnedByActivePlayer);
256 return getNearestObject(pos, &filters);
257 }
258
getNearestCity(const Vector<int> & pos,int dist) const259 City* Citylist::getNearestCity(const Vector<int>& pos, int dist) const
260 {
261 City *c = getNearestCity(pos);
262 if (!c)
263 return c;
264 if (c->getPos().x <= pos.x + dist && c->getPos().x >= pos.x - dist &&
265 c->getPos().y <= pos.y + dist && c->getPos().y >= pos.y - dist)
266 return c;
267 return NULL;
268 }
269
getNearestFriendlyCity(const Vector<int> & pos,int dist) const270 City* Citylist::getNearestFriendlyCity(const Vector<int>& pos, int dist) const
271 {
272 City *c = getNearestFriendlyCity(pos);
273 if (!c)
274 return c;
275 if (c->getPos().x <= pos.x + dist && c->getPos().x >= pos.x - dist &&
276 c->getPos().y <= pos.y + dist && c->getPos().y >= pos.y - dist)
277 return c;
278 return NULL;
279 }
280
getNearestFriendlyCity(const Vector<int> & pos) const281 City* Citylist::getNearestFriendlyCity(const Vector<int>& pos) const
282 {
283 Player* p = Playerlist::getInstance()->getActiveplayer();
284 return getNearestCity (pos, p);
285 }
286
getNearestCity(const Vector<int> & pos,Player * player) const287 City* Citylist::getNearestCity(const Vector<int>& pos, Player *player) const
288 {
289 int diff = -1;
290 const_iterator diffit;
291
292 for (const_iterator it = begin(); it != end(); ++it)
293 {
294 if ((*it)->isBurnt())
295 continue;
296
297 if ((*it)->getOwner() == player)
298 {
299 Vector<int> p = (*it)->getPos();
300 int delta = abs(p.x - pos.x);
301 if (delta < abs(p.y - pos.y))
302 delta = abs(p.y - pos.y);
303
304 if ((diff > delta) || (diff == -1))
305 {
306 diff = delta;
307 diffit = it;
308 }
309 }
310 }
311
312 if (diff == -1) return 0;
313 return (*diffit);
314 }
315
getClosestCity(const Stack * stack,Player * p) const316 City* Citylist::getClosestCity(const Stack *stack, Player *p) const
317 {
318 int diff = -1;
319 const_iterator diffit;
320 PathCalculator pc(stack, true, 0, 0);
321
322 for (const_iterator it = begin(); it != end(); ++it)
323 {
324 if ((*it)->isBurnt())
325 continue;
326
327 if ((*it)->getOwner() == p)
328 {
329 int delta = pc.calculate((*it)->getPos());
330 if (delta <= 0)
331 continue;
332
333 if ((diff > delta) || (diff == -1))
334 {
335 diff = delta;
336 diffit = it;
337 }
338 }
339 }
340
341 if (diff == -1) return 0;
342 return (*diffit);
343 }
344
getClosestFriendlyCity(const Stack * stack) const345 City* Citylist::getClosestFriendlyCity(const Stack *stack) const
346 {
347 Player* p = Playerlist::getInstance()->getActiveplayer();
348 return getClosestCity (stack, p);
349 }
getClosestCity(const Stack * stack) const350 City* Citylist::getClosestCity(const Stack *stack) const
351 {
352 std::list<bool (*)(void *)> filters;
353 filters.push_back(isBurnt);
354 return getClosestObject(stack, &filters);
355 }
356
getNearestCity(const Vector<int> & pos) const357 City* Citylist::getNearestCity(const Vector<int>& pos) const
358 {
359 std::list<bool (*)(void *)> filters;
360 filters.push_back(isBurnt);
361 return getNearestObject(pos, &filters);
362 }
363
getNearestVisibleCity(const Vector<int> & pos) const364 City* Citylist::getNearestVisibleCity(const Vector<int>& pos) const
365 {
366 std::list<bool (*)(void *)> filters;
367 filters.push_back(isBurnt);
368 filters.push_back(isFogged);
369 return getNearestObject(pos, &filters);
370 }
371
getNearestVisibleCity(const Vector<int> & pos,int dist) const372 City* Citylist::getNearestVisibleCity(const Vector<int>& pos, int dist) const
373 {
374 City *c = getNearestVisibleCity(pos);
375 if (!c)
376 return c;
377 if (c->getPos().x <= pos.x + dist && c->getPos().x >= pos.x - dist &&
378 c->getPos().y <= pos.y + dist && c->getPos().y >= pos.y - dist)
379 return c;
380 return NULL;
381 }
382
getNearestVisibleFriendlyCity(const Vector<int> & pos,int dist) const383 City* Citylist::getNearestVisibleFriendlyCity(const Vector<int>& pos, int dist) const
384 {
385 City *c = getNearestFriendlyCity(pos);
386 if (!c)
387 return c;
388 if (c->getPos().x <= pos.x + dist && c->getPos().x >= pos.x - dist &&
389 c->getPos().y <= pos.y + dist && c->getPos().y >= pos.y - dist)
390 return c;
391 return NULL;
392 }
393
394
getNearestVisibleFriendlyCity(const Vector<int> & pos) const395 City* Citylist::getNearestVisibleFriendlyCity(const Vector<int>& pos) const
396 {
397 std::list<bool (*)(void *)> filters;
398 filters.push_back(isBurnt);
399 filters.push_back(isFogged);
400 filters.push_back(isNotOwnedByActivePlayer);
401 return getNearestObject(pos, &filters);
402 }
403
getNearestNeutralCity(const Vector<int> & pos) const404 City* Citylist::getNearestNeutralCity(const Vector<int>& pos) const
405 {
406 std::list<bool (*)(void *)> filters;
407 filters.push_back(isBurnt);
408 filters.push_back(isNotOwnedByNeutral);
409 return getNearestObject(pos, &filters);
410 }
411
getNearestFriendlyVectorableCity(const Vector<int> & pos) const412 City* Citylist::getNearestFriendlyVectorableCity(const Vector<int>& pos) const
413 {
414 std::list<bool (*)(void *)> filters;
415 filters.push_back(isBurnt);
416 filters.push_back(isNotOwnedByActivePlayer);
417 filters.push_back(canNotAcceptMoreVectoring);
418 return getNearestObject(pos, &filters);
419 }
420
save(XML_Helper * helper) const421 bool Citylist::save(XML_Helper* helper) const
422 {
423 bool retval = true;
424
425 retval &= helper->openTag(Citylist::d_tag);
426
427 for (const_iterator it = begin(); it != end(); it++)
428 (*it)->save(helper);
429
430 retval &= helper->closeTag();
431
432 return retval;
433 }
434
load(Glib::ustring tag,XML_Helper * helper)435 bool Citylist::load(Glib::ustring tag, XML_Helper* helper)
436 {
437 if (tag == City::d_tag)
438 {
439 Cityset *cs = GameMap::getCityset();
440 City *c = new City(helper, cs->getCityTileWidth());
441 add(c);
442 return true;
443 }
444 return false;
445 }
446
changeOwnership(Player * old_owner,Player * new_owner)447 void Citylist::changeOwnership(Player *old_owner, Player *new_owner)
448 {
449 for (iterator it = begin(); it != end(); it++)
450 if ((*it)->getOwner() == old_owner)
451 {
452 stopVectoringTo(*it);
453 (*it)->setVectoring(Vector<int>(-1,-1));
454 (*it)->setOwner(new_owner);
455 if ((*it)->isCapital())
456 if ((*it)->getCapitalOwner() == old_owner)
457 (*it)->setCapitalOwner(new_owner);
458 if (new_owner == Playerlist::getInstance()->getNeutral())
459 (*it)->setActiveProductionSlot(-1); //hmm, what about neutral policy.
460 }
461 }
462
stopVectoringTo(City * c)463 void Citylist::stopVectoringTo(City *c)
464 {
465 for (iterator it = begin(); it != end(); it++)
466 {
467 if ((*it)->isBurnt() == true)
468 continue;
469 if ((*it)->getVectoring() == Vector<int>(-1,-1))
470 continue;
471 if (c->contains((*it)->getVectoring()))
472 (*it)->setVectoring(Vector<int>(-1,-1));
473 }
474 return;
475 }
476
isVectoringTarget(City * target) const477 bool Citylist::isVectoringTarget(City *target) const
478 {
479 for (const_iterator it = begin(); it != end(); it++)
480 {
481 if ((*it)->getOwner() != target->getOwner())
482 continue;
483 if (target->contains((*it)->getVectoring()))
484 return true;
485 }
486 return false;
487 }
488
getCitiesVectoringTo(Vector<int> target) const489 std::list<City*> Citylist::getCitiesVectoringTo(Vector<int> target) const
490 {
491 std::list<City*> cities;
492 for (const_iterator it = begin(); it != end(); it++)
493 {
494 if (target == (*it)->getVectoring())
495 cities.push_back((*it));
496 }
497 return cities;
498 }
499
getCitiesVectoringTo(City * target) const500 std::list<City*> Citylist::getCitiesVectoringTo(City *target) const
501 {
502 std::list<City*> cities;
503 for (const_iterator it = begin(); it != end(); it++)
504 {
505 if ((*it)->getOwner() != target->getOwner())
506 continue;
507 if (target->contains((*it)->getVectoring()))
508 cities.push_back((*it));
509 }
510 return cities;
511 }
512
getNearestCityPast(const Vector<int> & pos,int dist) const513 City* Citylist::getNearestCityPast(const Vector<int>& pos, int dist) const
514 {
515 std::list<bool (*)(void *)> filters;
516 filters.push_back(isBurnt);
517 return getNearestObjectAfter(pos, dist, &filters);
518 }
519
countCitiesVectoringTo(const City * dest) const520 guint32 Citylist::countCitiesVectoringTo(const City *dest) const
521 {
522 guint32 count = 0;
523 for (const_iterator it = begin(); it != end(); it++)
524 {
525 City *c = *it;
526 if (c->getOwner() != dest->getOwner())
527 continue;
528 if (c->getVectoring() == dest->getPos())
529 count++;
530 }
531
532 return count;
533 }
534
getNearestFriendlyCities(Player * player,Vector<int> pos) const535 std::list<City*> Citylist::getNearestFriendlyCities(Player *player, Vector<int> pos) const
536 {
537 std::list<City*> cities;
538 for (const_iterator it = begin(); it != end(); it++)
539 {
540 City *c = *it;
541 if (c->getOwner() != player)
542 continue;
543 if (c->isBurnt() == true)
544 continue;
545 cities.push_back(c);
546 }
547 if (cities.size() == 0)
548 return cities;
549 std::list<int> distances;
550
551 if (pos == Vector<int>(-1,-1))
552 pos = player->getFirstCity()->getPos();
553
554 for (std::list<City*>::iterator it = cities.begin(); it != cities.end(); it++)
555 distances.push_back(dist((*it)->getNearestPos(pos), pos));
556
557 bool sorted = false;
558
559 while (!sorted)
560 {
561 sorted = true;
562
563 // setup
564 std::list<int>::iterator dit = distances.begin();
565 std::list<int>::iterator dnextit = distances.begin();
566 dnextit++;
567
568 std::list<City*>::iterator it = cities.begin();
569 std::list<City*>::iterator nextit = it;
570 nextit++;
571
572 for (; nextit != cities.end(); it++, nextit++, dit++, dnextit++)
573 if ((*dit) > (*dnextit))
574 {
575 // exchange the items in both lists
576 sorted = false;
577
578 City* tmp = (*nextit);
579 cities.erase(nextit);
580 nextit = it;
581 it = cities.insert(nextit, tmp);
582
583 int val = (*dnextit);
584 distances.erase(dnextit);
585 dnextit = dit;
586 dit = distances.insert(dnextit, val);
587 }
588 }
589 return cities;
590 }
591
getCapitalCity(Player * player) const592 City *Citylist::getCapitalCity(Player *player) const
593 {
594 for (const_iterator it = begin(); it != end(); it++)
595 {
596 City *c = *it;
597 if (c->isCapital() && c->getCapitalOwner() &&
598 c->getCapitalOwner()->getId() == player->getId())
599 return c;
600 }
601 return NULL;
602 }
603
getRandomCityForHero(Player * player) const604 City *Citylist::getRandomCityForHero(Player *player) const
605 {
606 std::vector<City*> cities;
607 for (const_iterator it = begin(); it != end(); it++)
608 if (!(*it)->isBurnt() && (*it)->getOwner() == player)
609 cities.push_back((*it));
610 if (cities.empty())
611 return NULL;
612 return cities[Rnd::rand() % cities.size()];
613 }
614
countUnamedCities() const615 guint32 Citylist::countUnamedCities () const
616 {
617 guint32 count = 0;
618 for (const_iterator it = begin(); it != end(); it++)
619 if ((*it)->getName () == DEFAULT_CITY_NAME)
620 count++;
621 return count;
622 }
623 // End of file
624