1 // Copyright (C) 2000, 2001, 2003 Michael Bartl
2 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
3 // Copyright (C) 2002 Mark L. Amidon
4 // Copyright (C) 2005 Andrea Paternesi
5 // Copyright (C) 2006, 2007, 2008, 2009, 2011, 2014, 2015 Ben Asselstine
6 // Copyright (C) 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 <stdio.h>
24 #include <sstream>
25 #include "city.h"
26 #include "path.h"
27 #include "army.h"
28 #include "armyprodbase.h"
29 #include "hero.h"
30 #include "stacklist.h"
31 #include "stack.h"
32 #include "playerlist.h"
33 #include "armysetlist.h"
34 #include "citylist.h"
35 #include "GameMap.h"
36 #include "vectoredunitlist.h"
37 #include "vectoredunit.h"
38 #include "action.h"
39 #include "xmlhelper.h"
40 #include "rnd.h"
41 #include "GameScenarioOptions.h"
42
43 Glib::ustring City::d_tag = "city";
44
45 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
46 #define debug(x)
47
City(Vector<int> pos,guint32 width,Glib::ustring name,guint32 gold,guint32 numslots)48 City::City(Vector<int> pos, guint32 width, Glib::ustring name, guint32 gold,
49 guint32 numslots)
50 :Ownable((Player *)0), Location(pos, width), Renamable(name),
51 ProdSlotlist(numslots), d_gold(gold), d_defense_level(1), d_burnt(false),
52 d_vectoring(false), d_vector(Vector<int>(-1,-1)),
53 d_capital(false), d_capital_owner(0), d_build_production (true)
54
55 {
56 // set the tiles to city type
57 for (unsigned int i = 0; i < getSize(); i++)
58 for (unsigned int j = 0; j < getSize(); j++)
59 {
60 Vector<int> npos = getPos() + Vector<int>(i, j);
61 GameMap::getInstance()->getTile(npos)->setBuilding(Maptile::CITY);
62 }
63 }
64
City(XML_Helper * helper,guint32 width)65 City::City(XML_Helper* helper, guint32 width)
66 :Ownable(helper), Location(helper, width), Renamable(helper),
67 ProdSlotlist(helper)
68 {
69 //initialize the city
70
71 helper->getData(d_defense_level, "defense");
72
73 helper->getData(d_gold, "gold");
74 helper->getData(d_burnt, "burnt");
75 helper->getData(d_build_production, "build_production");
76 helper->getData(d_capital, "capital");
77 if (d_capital)
78 {
79 guint32 ui;
80 helper->getData(ui, "capital_owner");
81 d_capital_owner = Playerlist::getInstance()->getPlayer(ui);
82 }
83 else
84 d_capital_owner = NULL;
85
86
87 std::istringstream svect;
88 Glib::ustring s;
89
90 helper->getData(s, "vectoring");
91 svect.str(s);
92 svect >> d_vector.x;
93 svect >> d_vector.y;
94
95 if (d_vector.x!=-1 && d_vector.y!=-1)
96 d_vectoring=true;
97 else
98 d_vectoring=false;
99
100 //mark the positions on the map as being occupied by a city
101 for (unsigned int i = 0; i < d_size; i++)
102 for (unsigned int j = 0; j < d_size; j++)
103 GameMap::getInstance()->getTile(getPos().x+i, getPos().y+j)
104 ->setBuilding(Maptile::CITY);
105 }
106
City(const City & c)107 City::City(const City& c)
108 :Ownable(c), Location(c), Renamable(c), ProdSlotlist(c),
109 d_gold(c.d_gold), d_defense_level(c.d_defense_level), d_burnt(c.d_burnt),
110 d_vectoring(c.d_vectoring),d_vector(c.d_vector), d_capital(c.d_capital),
111 d_capital_owner(c.d_capital_owner), d_build_production(c.d_build_production)
112 {
113 }
114
City(const City & c,Vector<int> pos)115 City::City(const City& c, Vector<int> pos)
116 :Ownable(c), Location(c, pos), Renamable(c), ProdSlotlist(c),
117 d_gold(c.d_gold), d_defense_level(c.d_defense_level), d_burnt(c.d_burnt),
118 d_vectoring(c.d_vectoring),d_vector(c.d_vector), d_capital(c.d_capital),
119 d_capital_owner(c.d_capital_owner), d_build_production(c.d_build_production)
120 {
121 }
122
save(XML_Helper * helper) const123 bool City::save(XML_Helper* helper) const
124 {
125 bool retval = true;
126
127 std::stringstream svect;
128
129 svect << d_vector.x << " " << d_vector.y;
130
131 retval &= helper->openTag(City::d_tag);
132 retval &= helper->saveData("id", d_id);
133 retval &= helper->saveData("x", getPos().x);
134 retval &= helper->saveData("y", getPos().y);
135 retval &= helper->saveData("name", getName(false));
136 retval &= helper->saveData("owner", d_owner->getId());
137 retval &= helper->saveData("defense", d_defense_level);
138 retval &= helper->saveData("gold", d_gold);
139 retval &= helper->saveData("burnt", d_burnt);
140 retval &= helper->saveData("build_production", d_build_production);
141 retval &= helper->saveData("capital", d_capital);
142 if (d_capital)
143 retval &= helper->saveData("capital_owner", d_capital_owner->getId());
144 retval &= helper->saveData("vectoring", svect.str());
145
146 retval &= ProdSlotlist::save(helper);
147 retval &= helper->closeTag();
148 return retval;
149 }
150
conquer(Player * newowner)151 void City::conquer(Player* newowner)
152 {
153 Citylist::getInstance()->stopVectoringTo(this);
154
155 setOwner(newowner);
156
157 // remove vectoring info
158 setVectoring(Vector<int>(-1,-1));
159
160 deFog(newowner);
161
162 VectoredUnitlist::getInstance()->removeVectoredUnitsGoingTo(this);
163 VectoredUnitlist::getInstance()->removeVectoredUnitsComingFrom(this);
164 }
165
produceStrongestProductionBase()166 void City::produceStrongestProductionBase()
167 {
168 debug("produceStrongestProductionBase()");
169
170 if (getNoOfProductionBases() == 0)
171 return;
172
173 if (!isFull(d_owner))
174 {
175 unsigned int max_strength = 0;
176 int strong_idx = -1;
177 for (unsigned int i = 0; i < getMaxNoOfProductionBases(); i++)
178 {
179 if ((*this)[i]->getArmyProdBase() == NULL)
180 continue;
181 if (getProductionBase(i)->getStrength() > max_strength)
182 {
183 strong_idx = i;
184 max_strength = getProductionBase(i)->getStrength();
185 }
186 }
187 if (strong_idx == -1)
188 return;
189
190 int savep = d_active_production_slot;
191 setActiveProductionSlot(strong_idx);
192 Stack *s = NULL;
193 produceArmy(s);
194 setActiveProductionSlot(savep);
195 return;
196 }
197 }
198
produceWeakestQuickestArmyInArmyset()199 void City::produceWeakestQuickestArmyInArmyset()
200 {
201 guint32 set = d_owner->getArmyset();
202 ArmyProto *scout = Armysetlist::getInstance()->lookupWeakestQuickestArmy(set);
203 Army *a = new Army(*scout, d_owner);
204 GameMap::getInstance()->addArmy(this, a);
205 }
206
produceWeakestProductionBase()207 void City::produceWeakestProductionBase()
208 {
209 debug("produceWeakestProductionBase()");
210
211 if (getNoOfProductionBases() == 0)
212 return;
213
214 if (!isFull(d_owner))
215 {
216 unsigned int min_strength = 100;
217 int weak_idx = -1;
218 for (unsigned int i = 0; i < getMaxNoOfProductionBases(); i++)
219 {
220 if ((*this)[i]->getArmyProdBase() == NULL)
221 continue;
222 if (getProductionBase(i)->getStrength() < min_strength)
223 {
224 weak_idx = i;
225 min_strength = getProductionBase(i)->getStrength();
226 }
227 }
228 if (weak_idx == -1)
229 return;
230
231 int savep = d_active_production_slot;
232 setActiveProductionSlot(weak_idx);
233 Stack *s = NULL;
234 produceArmy(s);
235 setActiveProductionSlot(savep);
236 return;
237 }
238 }
239
armyArrives(Stack * & stack)240 const Army *City::armyArrives(Stack *& stack)
241 {
242 // vector the army to the new spot
243 if (d_vectoring)
244 {
245 int turns = VectoredUnit::get_travel_turns (getPos(), d_vector);
246 VectoredUnit *v =
247 new VectoredUnit (getPos(), d_vector,
248 (*this)[d_active_production_slot]->getArmyProdBase(),
249 turns, d_owner);
250 VectoredUnitlist::getInstance()->push_back(v);
251 d_owner->cityChangeProduction(this, d_active_production_slot);
252 //we don't return an army when we've vectored it.
253 //it doesn't really exist until it lands at the destination.
254 return NULL;
255 }
256 else //or make it here
257 {
258 return produceArmy(stack);
259 }
260 return NULL;
261 }
262
nextTurn()263 void City::nextTurn()
264 {
265 if (d_burnt)
266 return;
267
268 // check if an army should be produced
269 if (d_active_production_slot >= 0 && --d_duration == 0)
270 {
271 if (d_owner->getGold() <= 0)
272 {
273 //dont make or vector the unit
274 //and also stop production
275 d_owner->cityChangeProduction(this, -1);
276 d_owner->vectorFromCity(this, Vector<int>(-1,-1));
277 return;
278 }
279 d_owner->cityProducesArmy(this);
280 }
281 }
282
setVectoring(Vector<int> p)283 void City::setVectoring(Vector<int> p)
284 {
285 d_vector = p;
286 d_vectoring = true;
287
288 if (p.x == -1 || p.y == -1)
289 {
290 d_vectoring=false;
291 d_vector.x = -1;
292 d_vector.y = -1;
293 }
294 }
295
produceArmy(Stack * & stack)296 Army *City::produceArmy(Stack *& stack)
297 {
298 // add produced army to stack
299 if (d_active_production_slot == -1)
300 return NULL;
301
302 debug("produce_army()\n");
303
304 // do not produce an army if the player has no gold.
305 // unless it's the neutrals
306 if (d_owner != Playerlist::getInstance()->getNeutral() &&
307 d_owner->getGold() < 0)
308 return NULL;
309
310 Army *a = new Army(*(getProductionBase(d_active_production_slot)), d_owner);
311 stack = GameMap::getInstance()->addArmy(this, a);
312
313 if (d_owner == Playerlist::getInstance()->getNeutral())
314 {
315 //we're an active neutral city
316 //check to see if we've made 5 or not.
317 //stop producing if we've made 5 armies in our neutral city
318 if (countDefenders() >= MAX_ARMIES_PRODUCED_IN_NEUTRAL_CITY)
319 setActiveProductionSlot(-1);
320 else
321 setActiveProductionSlot(d_active_production_slot);
322 }
323 else // start producing next army of same type
324 setActiveProductionSlot(d_active_production_slot);
325 return a;
326 }
327
canAcceptMoreVectoring() const328 bool City::canAcceptMoreVectoring() const
329 {
330 return canAcceptMoreVectoring(0);
331 }
332
canAcceptMoreVectoring(guint32 number_of_cities) const333 bool City::canAcceptMoreVectoring(guint32 number_of_cities) const
334 {
335 //here we presume that it's one unit per city
336 guint32 num = Citylist::getInstance()->countCitiesVectoringTo(this);
337 if (num + number_of_cities >= MAX_CITIES_VECTORED_TO_ONE_CITY)
338 return false;
339 return true;
340 }
341
changeVectorDestination(Vector<int> dest)342 bool City::changeVectorDestination(Vector<int> dest)
343 {
344 setVectoring(dest);
345 VectoredUnitlist::getInstance()->changeDestination(this, dest);
346 return true;
347 }
348
getDefenders() const349 std::vector<Stack *> City::getDefenders() const
350 {
351 if (isBurnt() == true)
352 {
353 std::vector<Stack *> e;
354 return e;
355 }
356 return getOwner()->getStacklist()->getDefendersInCity(this);
357 }
358
countDefenders() const359 guint32 City::countDefenders() const
360 {
361 std::vector<Stack*> defenders;
362 defenders = getDefenders();
363
364 guint32 armies = 0;
365 std::vector<Stack*>::iterator it = defenders.begin();
366 for (;it != defenders.end(); it++)
367 armies += (*it)->size();
368
369 return armies;
370 }
371
randomlyImproveOrDegradeArmy(ArmyProdBase * army)372 void City::randomlyImproveOrDegradeArmy(ArmyProdBase *army)
373 {
374 if (Rnd::rand() % 30 == 0) //random chance of improving strength
375 army->setStrength(army->getStrength() + 1);
376 if (Rnd::rand() % 25 == 0) //random chance of improving turns
377 {
378 if (army->getProduction() > 1)
379 army->setProduction(army->getProduction() - 1);
380 }
381 if (Rnd::rand() % 50 == 0) //random chance of degrading strength
382 {
383 if (army->getStrength() > 1)
384 army->setStrength(army->getStrength() - 1);
385 }
386 if (Rnd::rand() % 45 == 0) //random chance of improving turns
387 {
388 if (army->getProduction() < 5)
389 army->setProduction(army->getProduction() + 1);
390 }
391 }
392
armyCompareStrength(const ArmyProdBase * lhs,const ArmyProdBase * rhs)393 bool armyCompareStrength (const ArmyProdBase *lhs, const ArmyProdBase *rhs)
394 {
395 guint32 lhs_strength = lhs->getStrength();
396 guint32 rhs_strength = rhs->getStrength();
397 return lhs_strength < rhs_strength;
398 }
399
sortProduction()400 void City::sortProduction()
401 {
402 //sort them by strength
403 if (getNoOfProductionBases() > 1)
404 {
405 std::list<ArmyProdBase*> productibles;
406 unsigned int j;
407 for (j = 0; j < getMaxNoOfProductionBases(); j++)
408 {
409 if ((*this)[j]->getArmyProdBase())
410 productibles.push_back((*this)[j]->getArmyProdBase());
411 }
412 productibles.sort(armyCompareStrength);
413 j = 0;
414 for (std::list<ArmyProdBase*>::iterator it = productibles.begin();
415 it != productibles.end(); it++, j++)
416 (*this)[j]->setArmyProdBase(*it);
417 }
418 return;
419 }
420
setRandomArmytypes(bool produce_allies,int likely)421 void City::setRandomArmytypes(bool produce_allies, int likely)
422 {
423 //remove armies any that happen to be being produced
424 for (unsigned int i = 0; i < getMaxNoOfProductionBases(); i++)
425 removeProductionBase(i);
426
427 guint32 set = d_owner->getArmyset();
428
429 int army_type;
430 int num = Rnd::rand() % 10;
431 if (num < 7)
432 army_type = 1;
433 else if (num < 9 && likely == 0)
434 army_type = 0;
435 else
436 army_type = 1 + likely + (Rnd::rand () % 11);
437 ArmyProto *template_army = Armysetlist::getInstance()->getArmy(set, army_type);
438 if (!template_army ||
439 (template_army->getAwardable() == true && produce_allies == false) ||
440 template_army->isHero())
441 {
442 produceWeakestQuickestArmyInArmyset();
443 return;
444 }
445 ArmyProdBase *army = new ArmyProdBase (*template_army);
446 randomlyImproveOrDegradeArmy(army);
447 addProductionBase(0, army);
448
449 if (((Rnd::rand() % 10) < 3 && !isCapital() && likely < 1) ||
450 template_army->getAwardable())
451 {
452 sortProduction();
453 return;
454 }
455
456 army_type += 1 + (Rnd::rand() % (2 + (produce_allies ? 2 : 0)));
457 template_army = Armysetlist::getInstance()->getArmy(set, army_type);
458 if (!template_army ||
459 (template_army->getAwardable() == true && produce_allies == false) ||
460 template_army->isHero())
461 {
462 sortProduction();
463 return;
464 }
465 army = new ArmyProdBase (*template_army);
466 randomlyImproveOrDegradeArmy(army);
467 addProductionBase(1, army);
468
469 if (((Rnd::rand() % 10) < 4 && !isCapital() && likely < 2) ||
470 template_army->getAwardable())
471 {
472 sortProduction();
473 return;
474 }
475
476 if (army_type < 5)
477 army_type += 1 + (Rnd::rand() % (7 + (produce_allies ? 2 : 0)));
478 else
479 army_type += 1 + (Rnd::rand() % (2 + (produce_allies ? 2 : 0)));
480 template_army = Armysetlist::getInstance()->getArmy(set, army_type);
481 if (!template_army ||
482 (template_army->getAwardable() == true && produce_allies == false) ||
483 template_army->isHero())
484 {
485 sortProduction();
486 return;
487 }
488 army = new ArmyProdBase (*template_army);
489 randomlyImproveOrDegradeArmy(army);
490 addProductionBase(2, army);
491
492 if (((Rnd::rand() % 10) < 6 && !isCapital() && likely < 3) ||
493 template_army->getAwardable())
494 {
495 sortProduction();
496 return;
497 }
498
499 army_type += 1 + (Rnd::rand() % (3 + (produce_allies ? 2 : 0)));
500 template_army = Armysetlist::getInstance()->getArmy(set, army_type);
501 if (!template_army ||
502 (template_army->getAwardable() == true && produce_allies == false) ||
503 template_army->isHero())
504 {
505 sortProduction();
506 return;
507 }
508 army = new ArmyProdBase (*template_army);
509 randomlyImproveOrDegradeArmy(army);
510 addProductionBase(3, army);
511 sortProduction();
512 }
513
getDefenseLevel() const514 int City::getDefenseLevel() const
515 {
516 int num_production_bases = getNoOfProductionBases();
517 if (isBurnt())
518 return 0;
519 else if (num_production_bases <= 2 &&
520 getOwner() == Playerlist::getInstance()->getNeutral())
521 return 1;
522 else if (num_production_bases <= 2)
523 return 2;
524 else if (num_production_bases > 2 &&
525 getOwner() == Playerlist::getInstance()->getNeutral())
526 return 2;
527 else if (num_production_bases > 2)
528 return 3;
529 return 0;
530 }
531
diseaseDefenders(double percent_to_kill)532 std::list<Stack*> City::diseaseDefenders(double percent_to_kill)
533 {
534 std::list<Stack*> affected;
535 std::vector<Stack*> stacks =
536 getOwner()->getStacklist()->getDefendersInCity(this);
537 double percent;
538 if (percent_to_kill > 100.0)
539 percent = 100;
540 else if (percent_to_kill <= 0.0)
541 percent = 0;
542 else
543 percent = percent_to_kill;
544 guint32 num_armies_to_kill = (double)countDefenders() * (percent / 100.0);
545 std::vector<guint32> ids;
546 for (unsigned int i = 0; i < stacks.size(); i++)
547 {
548 for (Stack::iterator j = stacks[i]->begin(); j != stacks[i]->end(); j++)
549 ids.push_back((*j)->getId());
550 }
551 std::random_shuffle(ids.begin(), ids.end());
552 for (unsigned int i = 0; i < num_armies_to_kill; i++)
553 {
554 Stack *s = getOwner()->getStacklist()->getArmyStackById(ids[i]);
555 if (s)
556 {
557 Army *a = s->getArmyById(ids[i]);
558 if (a)
559 a->kill();
560 }
561 }
562 for (unsigned int i = 0; i < stacks.size(); i++)
563 {
564 if (stacks[i]->hasDeadArmies())
565 affected.push_back(stacks[i]);
566 }
567 return affected;
568 }
569
persuadeDefenders(Player * new_owner)570 void City::persuadeDefenders(Player *new_owner)
571 {
572 std::vector<Stack*> stacks = getDefenders();
573 for (unsigned int i = 0; i < stacks.size(); i++)
574 getOwner()->getStacklist()->changeOwnership(stacks[i], new_owner);
575 switch (GameScenarioOptions::s_build_production_mode)
576 {
577 case GameParameters::BUILD_PRODUCTION_ALWAYS:
578 case GameParameters::BUILD_PRODUCTION_NEVER:
579 break;
580 case GameParameters::BUILD_PRODUCTION_USUALLY:
581 case GameParameters::BUILD_PRODUCTION_SELDOM:
582 setBuildProduction(true);
583 break;
584 }
585 conquer(new_owner);
586 }
587 // End of file
588