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