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