initial commit, lordsawar source, slightly modified
[lordsawar] / src / armyset.cpp
1 //  Copyright (C) 2007, 2008, 2009 Ben Asselstine
2 //
3 //  This program is free software; you can redistribute it and/or modify
4 //  it under the terms of the GNU General Public License as published by
5 //  the Free Software Foundation; either version 3 of the License, or
6 //  (at your option) any later version.
7 //
8 //  This program is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //  GNU Library General Public License for more details.
12 //
13 //  You should have received a copy of the GNU General Public License
14 //  along with this program; if not, write to the Free Software
15 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
16 //  02110-1301, USA.
17
18 #include <iostream>
19 #include <expat.h>
20 #include <gtkmm.h>
21 #include "rectangle.h"
22 #include <sigc++/functors/mem_fun.h>
23 #include <string>
24
25 #include "armyset.h"
26 #include "File.h"
27 #include "GraphicsCache.h"
28 #include "shield.h"
29 #include "gui/image-helpers.h"
30 #include "armysetlist.h"
31 #include "armyprodbase.h"
32
33 std::string Armyset::d_tag = "armyset";
34 std::string Armyset::file_extension = ARMYSET_EXT;
35 using namespace std;
36
37 #define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<endl<<flush;}
38 //#define debug(x)
39
40 #define DEFAULT_ARMY_TILE_SIZE 40
41 Armyset::Armyset(guint32 id, std::string name)
42         : d_id(id), d_name(name), d_copyright(""), d_license(""), d_subdir(""), 
43         d_tilesize(DEFAULT_ARMY_TILE_SIZE), d_ship(0), d_shipmask(0), 
44         d_standard(0), d_standard_mask(0), d_bag(0)
45 {
46   d_bag_name = "";
47   d_stackship_name = "";
48   d_standard_name = "";
49 }
50
51 Armyset::Armyset(XML_Helper *helper, std::string directory)
52     : d_id(0), d_name(""), d_copyright(""), d_license(""), d_subdir(""), 
53     d_tilesize(DEFAULT_ARMY_TILE_SIZE), d_ship(0), d_shipmask(0), 
54     d_standard(0), d_standard_mask(0), d_bag(0)
55 {
56   d_bag_name = "";
57   d_stackship_name = "";
58   d_standard_name = "";
59   setDirectory(directory);
60   helper->getData(d_id, "id");
61   helper->getData(d_name, "name");
62   helper->getData(d_copyright, "copyright");
63   helper->getData(d_license, "license");
64   helper->getData(d_tilesize, "tilesize");
65   helper->getData(d_stackship_name, "stackship");
66   helper->getData(d_standard_name, "plantedstandard");
67   helper->getData(d_bag_name, "bag");
68   helper->registerTag(ArmyProto::d_tag, 
69                       sigc::mem_fun((*this), &Armyset::loadArmyProto));
70 }
71
72 Armyset::~Armyset()
73 {
74   uninstantiateImages();
75   for (iterator it = begin(); it != end(); it++)
76     delete *it;
77 }
78
79 bool Armyset::loadArmyProto(string tag, XML_Helper* helper)
80 {
81     if (tag == ArmyProto::d_tag)
82       {
83         std::string s;
84         ArmyProto* a = new ArmyProto(helper);
85         a->setTypeId(size());
86         a->setArmyset(d_id);
87         push_back(a);
88       }
89     return true;
90 }
91
92 bool Armyset::save(XML_Helper* helper)
93 {
94     bool retval = true;
95
96     retval &= helper->openTag(d_tag);
97
98     retval &= helper->saveData("id", d_id);
99     retval &= helper->saveData("name", d_name);
100     retval &= helper->saveData("copyright", d_copyright);
101     retval &= helper->saveData("license", d_license);
102     retval &= helper->saveData("tilesize", d_tilesize);
103     retval &= helper->saveData("stackship", d_stackship_name);
104     retval &= helper->saveData("plantedstandard", d_standard_name);
105     retval &= helper->saveData("bag", d_bag_name);
106
107     for (const_iterator it = begin(); it != end(); it++)
108       (*it)->save(helper);
109     
110     retval &= helper->closeTag();
111
112     return retval;
113 }
114
115 ArmyProto * Armyset::lookupSimilarArmy(ArmyProto *army) const
116 {
117   for (const_iterator it = begin(); it != end(); it++)
118     {
119       if ((*it)->getGender() == army->getGender() &&
120           (*it)->getStrength() == army->getStrength() &&
121           (*it)->getProduction() == army->getProduction() &&
122           (*it)->getArmyBonus() == army->getArmyBonus() &&
123           (*it)->getMoveBonus() == army->getMoveBonus() &&
124           (*it)->getMaxMoves() == army->getMaxMoves() &&
125           (*it)->getAwardable() == army->getAwardable() &&
126           (*it)->getDefendsRuins() == army->getDefendsRuins())
127         return *it;
128     }
129   for (const_iterator it = begin(); it != end(); it++)
130     {
131       if ((*it)->getGender() == army->getGender() &&
132           (*it)->getStrength() == army->getStrength() &&
133           (*it)->getProduction() == army->getProduction() &&
134           (*it)->getArmyBonus() == army->getArmyBonus() &&
135           (*it)->getMoveBonus() == army->getMoveBonus() &&
136           (*it)->getMaxMoves() == army->getMaxMoves())
137         return *it;
138     }
139   for (const_iterator it = begin(); it != end(); it++)
140     {
141       if ((*it)->getGender() == army->getGender() &&
142           (*it)->getStrength() == army->getStrength() &&
143           (*it)->getProduction() == army->getProduction() &&
144           (*it)->getMaxMoves() == army->getMaxMoves())
145         return *it;
146     }
147   return NULL;
148 }
149
150 ArmyProto * Armyset::lookupArmyByGender(Hero::Gender gender) const
151 {
152   for (const_iterator it = begin(); it != end(); it++)
153     {
154       if ((*it)->getGender() == gender)
155         return *it;
156     }
157   return  NULL;
158 }
159 ArmyProto * Armyset::lookupArmyByStrengthAndTurns(guint32 str, guint32 turns) const
160 {
161   for (const_iterator it = begin(); it != end(); it++)
162     {
163       if (str && turns)
164         {
165           if ((*it)->getStrength() == str && (*it)->getProduction() == turns)
166             return *it;
167         }
168       else if (str)
169         {
170           if ((*it)->getStrength() == str)
171             return *it;
172         }
173       else if (turns)
174         {
175           if ((*it)->getProduction() == turns)
176             return *it;
177         }
178     }
179   return NULL;
180 }
181
182 ArmyProto * Armyset::lookupArmyByName(std::string name) const
183 {
184   for (const_iterator it = begin(); it != end(); it++)
185     {
186       if ((*it)->getName() == name)
187         return *it;
188     }
189   return NULL;
190 }
191         
192 ArmyProto * Armyset::lookupArmyByType(guint32 army_type_id) const
193 {
194   for (const_iterator it = begin(); it != end(); it++)
195     {
196       if ((*it)->getTypeId() == army_type_id)
197         return *it;
198     }
199   return NULL;
200 }
201         
202 bool Armyset::validateSize()
203 {
204   if (size() == 0)
205     return false;
206   return true;
207 }
208
209 bool Armyset::validateHero()
210 {
211   bool found = false;
212   //do we have a hero?
213   for (iterator it = begin(); it != end(); it++)
214     {
215       if ((*it)->isHero() == true)
216         {
217           found = true;
218           break;
219         }
220     }
221   if (!found)
222     return false;
223   return true;
224 }
225 bool Armyset::validatePurchasables()
226 {
227   bool found = false;
228   for (iterator it = begin(); it != end(); it++)
229     {
230       if ((*it)->getNewProductionCost() > 0 )
231         {
232           found = true;
233           break;
234         }
235     }
236   if (!found)
237     return false;
238   return true;
239 }
240
241 bool Armyset::validateRuinDefenders()
242 {
243   bool found = false;
244   for (iterator it = begin(); it != end(); it++)
245     {
246       if ((*it)->getDefendsRuins() == true)
247         {
248           found = true;
249           break;
250         }
251     }
252   if (!found)
253     return false;
254   return true;
255 }
256
257 bool Armyset::validateAwardables()
258 {
259   bool found = false;
260   for (iterator it = begin(); it != end(); it++)
261     {
262       if ((*it)->getAwardable() == true)
263         {
264           found = true;
265           break;
266         }
267     }
268   if (!found)
269     return false;
270   return true;
271 }
272 bool Armyset::validateShip()
273 {
274   if (getShipImageName() == "")
275     return false;
276   return true;
277 }
278
279 bool Armyset::validateBag()
280 {
281   if (getBagImageName() == "")
282     return false;
283   return true;
284 }
285
286 bool Armyset::validateStandard()
287 {
288   if (getStandardImageName() == "")
289     return false;
290   return true;
291 }
292
293 bool Armyset::validateArmyUnitImage(ArmyProto *army, Shield::Colour &c)
294 {
295   for (unsigned int i = Shield::WHITE; i <= Shield::NEUTRAL; i++)
296     if (army->getImageName(Shield::Colour(i)) == "")
297       {
298         c = Shield::Colour(i);
299         return false;
300       }
301   return true;
302 }
303 bool Armyset::validateArmyUnitImages()
304 {
305   Shield::Colour c;
306   for (iterator it = begin(); it != end(); it++)
307     {
308       if (validateArmyUnitImage(*it, c) == false)
309         return false;
310     }
311   return true;
312 }
313
314 bool Armyset::validateArmyUnitName(ArmyProto *army)
315 {
316   if (army->getName() == "")
317     return false;
318   return true;
319 }
320 bool Armyset::validateArmyUnitNames()
321 {
322   for (iterator it = begin(); it != end(); it++)
323     {
324       if (validateArmyUnitName(*it) == false)
325         return false;
326     }
327   return true;
328 }
329 bool Armyset::validate()
330 {
331   bool valid = true;
332   valid = validateSize();
333   if (!valid)
334     return false;
335   valid = validateHero();
336   if (!valid)
337     return false;
338   valid = validatePurchasables();
339   if (!valid)
340     return false;
341   //do we have any units that defend ruins?
342   valid = validateRuinDefenders();
343   if (!valid)
344     return false;
345   //do we have any units that can be awarded?
346   valid = validateAwardables();
347   if (!valid)
348     return false;
349   //is the stackship set?
350   valid = validateShip();
351   if (!valid)
352     return false;
353   //is the standard set?
354   valid = validateStandard();
355   if (!valid)
356     return false;
357   //is the bag set?
358   valid = validateBag();
359   if (!valid)
360     return false;
361   //is there an image set for each army unit?
362   valid = validateArmyUnitImages();
363   if (!valid)
364     return false;
365   //is there a name set for each army unit?
366   valid = validateArmyUnitNames();
367   if (!valid)
368     return false;
369
370   return valid;
371 }
372 class ArmysetLoader
373 {
374 public:
375     ArmysetLoader(std::string filename)
376       {
377         armyset = NULL;
378         dir = File::get_dirname(filename);
379         if (File::nameEndsWith(filename, Armyset::file_extension) == false)
380           filename += Armyset::file_extension;
381         XML_Helper helper(filename, ios::in, false);
382         helper.registerTag(Armyset::d_tag, sigc::mem_fun((*this), &ArmysetLoader::load));
383         if (!helper.parse())
384           {
385             std::cerr << "Error, while loading an armyset. Armyset Name: ";
386             std::cerr <<File::get_basename(File::get_dirname(filename)) <<std::endl <<std::flush;
387             if (armyset != NULL)
388               delete armyset;
389             armyset = NULL;
390           }
391       };
392     bool load(std::string tag, XML_Helper* helper)
393       {
394         if (tag == Armyset::d_tag)
395           {
396             armyset = new Armyset(helper, dir);
397             return true;
398           }
399         return false;
400       };
401     std::string dir;
402     Armyset *armyset;
403 };
404 Armyset *Armyset::create(std::string filename)
405 {
406   ArmysetLoader d(filename);
407   return d.armyset;
408 }
409 void Armyset::getFilenames(std::list<std::string> &files)
410 {
411   for (iterator it = begin(); it != end(); it++)
412     {
413       for (unsigned int i = Shield::WHITE; i <= Shield::NEUTRAL; i++)
414         {
415           std::string file = (*it)->getImageName(Shield::Colour(i));
416           if (std::find(files.begin(), files.end(), file) == files.end())
417             files.push_back(file);
418         }
419     }
420 }
421         
422 void Armyset::instantiateImages()
423 {
424   for (iterator it = begin(); it != end(); it++)
425     (*it)->instantiateImages(this);
426   if (getShipImageName().empty() == false)
427     loadShipPic(getFile(getShipImageName()));
428   if (getStandardImageName().empty() == false)
429     loadStandardPic(getFile(getStandardImageName()));
430   if (getBagImageName().empty() == false)
431     loadBagPic(getFile(getBagImageName()));
432 }
433
434 void Armyset::uninstantiateImages()
435 {
436   for (iterator it = begin(); it != end(); it++)
437     (*it)->uninstantiateImages();
438 }
439
440 void Armyset::loadShipPic(std::string image_filename)
441 {
442   if (image_filename.empty() == true)
443     return;
444   std::vector<PixMask*> half;
445   half = disassemble_row(image_filename, 2);
446   int size = getTileSize();
447   PixMask::scale(half[0], size, size);
448   PixMask::scale(half[1], size, size);
449   setShipImage(half[0]);
450   setShipMask(half[1]);
451 }
452
453 void Armyset::loadBagPic(std::string image_filename)
454 {
455   if (image_filename.empty() == true)
456     return;
457   setBagPic(PixMask::create(image_filename));
458 }
459 void Armyset::loadStandardPic(std::string image_filename)
460 {
461   if (image_filename.empty() == true)
462     return;
463   std::vector<PixMask*> half = disassemble_row(image_filename, 2);
464   int size = getTileSize();
465   PixMask::scale(half[0], size, size);
466   PixMask::scale(half[1], size, size);
467   setStandardPic(half[0]);
468   setStandardMask(half[1]);
469 }
470
471 std::string Armyset::getConfigurationFile()
472 {
473   return getDirectory() + d_subdir + file_extension;
474 }
475
476 std::list<std::string> Armyset::scanUserCollection()
477 {
478   return File::scanFiles(File::getUserArmysetDir(), file_extension);
479 }
480
481 std::list<std::string> Armyset::scanSystemCollection()
482 {
483   std::list<std::string> retlist = File::scanFiles(File::getArmysetDir(), 
484                                                    file_extension);
485   if (retlist.empty())
486     {
487       std::cerr << "Couldn't find any armysets!" << std::endl;
488       std::cerr << "Please check the path settings in /etc/lordsawarrc or ~/.lordsawarrc" << std::endl;
489       std::cerr << "Exiting!" << std::endl;
490       exit(-1);
491     }
492
493   return retlist;
494 }
495
496 void Armyset::switchArmysetForRuinKeeper(Army *army, const Armyset *armyset)
497 {
498   //do our best to change the armyset for the given ruin keeper.
499  
500   //go find an equivalent type in the new armyset.
501   Armyset *old_armyset
502     = Armysetlist::getInstance()->getArmyset(army->getOwner()->getArmyset());
503   ArmyProto *old_armyproto = old_armyset->lookupArmyByType(army->getTypeId());
504   const ArmyProto *new_armyproto = armyset->lookupArmyByType(army->getTypeId());
505
506   //try looking at the same id first
507   if (new_armyproto != NULL && 
508       old_armyproto->getName() == new_armyproto->getName() &&
509       old_armyproto->getDefendsRuins() == new_armyproto->getDefendsRuins())
510     {
511       army->morph(new_armyproto);
512       return;
513     }
514
515   //try finding an army by the same name
516   new_armyproto = armyset->lookupArmyByName(old_armyproto->getName());
517   if (new_armyproto != NULL &&
518       old_armyproto->getDefendsRuins() == new_armyproto->getDefendsRuins())
519     {
520       army->morph(new_armyproto);
521       return;
522     }
523
524   //failing that, any ruin keeper will do.
525   new_armyproto = armyset->getRandomRuinKeeper();
526   if (new_armyproto != NULL)
527     {
528       army->morph(new_armyproto);
529       return;
530     }
531
532 }
533
534 void Armyset::switchArmyset(ArmyProdBase *army, const Armyset *armyset)
535 {
536   //do our best to change the armyset for the given armyprodbase.
537
538   //go find an equivalent type in the new armyset.
539   Armyset *old_armyset
540     = Armysetlist::getInstance()->getArmyset(army->getArmyset());
541   ArmyProto *old_armyproto = old_armyset->lookupArmyByType(army->getTypeId());
542   ArmyProto *new_armyproto = armyset->lookupArmyByType(army->getTypeId());
543
544   //try looking at the same id first
545   if (new_armyproto != NULL && 
546       old_armyproto->getName() == new_armyproto->getName())
547     {
548       army->morph(new_armyproto);
549       return;
550     }
551
552   //try finding an army by the same name
553   new_armyproto = armyset->lookupArmyByName(old_armyproto->getName());
554   if (new_armyproto != NULL)
555     {
556       army->morph(new_armyproto);
557       return;
558     }
559
560   //failing that, any army with similar characteristics will do.
561   new_armyproto = armyset->lookupSimilarArmy(old_armyproto);
562   if (new_armyproto != NULL)
563     {
564       army->morph(new_armyproto);
565       return;
566     }
567
568   //failing that, any army with the same strength and turns will do.
569   new_armyproto = 
570     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(),
571                                           old_armyproto->getProduction());
572   if (new_armyproto != NULL)
573     {
574       army->morph(new_armyproto);
575       return;
576     }
577
578   //failing that, any army with the same strength will do.
579   new_armyproto = 
580     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(), 0);
581   if (new_armyproto != NULL)
582     {
583       army->morph(new_armyproto);
584       return;
585     }
586
587   //failing that, any army with the same turns will do.
588   new_armyproto = 
589     armyset->lookupArmyByStrengthAndTurns(0, old_armyproto->getProduction());
590   if (new_armyproto != NULL)
591     {
592       army->morph(new_armyproto);
593       return;
594     }
595
596   //failing that, any army will do.
597   new_armyproto = armyset->lookupArmyByGender(old_armyproto->getGender());
598   if (new_armyproto != NULL)
599     {
600       army->morph(new_armyproto);
601       return;
602     }
603
604 }
605
606 void Armyset::switchArmyset(Army *army, const Armyset *armyset)
607 {
608   //do our best to change the armyset for the given army.
609
610   //go find an equivalent type in the new armyset.
611   Armyset *old_armyset
612     = Armysetlist::getInstance()->getArmyset(army->getOwner()->getArmyset());
613   ArmyProto *old_armyproto = old_armyset->lookupArmyByType(army->getTypeId());
614   ArmyProto *new_armyproto = armyset->lookupArmyByType(army->getTypeId());
615
616   //try looking at the same id first
617   if (new_armyproto != NULL && 
618       old_armyproto->getName() == new_armyproto->getName())
619     {
620       army->morph(new_armyproto);
621       return;
622     }
623
624   //try finding an army by the same name
625   new_armyproto = armyset->lookupArmyByName(old_armyproto->getName());
626   if (new_armyproto != NULL)
627     {
628       army->morph(new_armyproto);
629       return;
630     }
631
632   //failing that, an army with the same gender (heroes).
633   if (army->isHero() == true)
634     {
635       new_armyproto = armyset->lookupArmyByGender(old_armyproto->getGender());
636       if (new_armyproto != NULL)
637         {
638           army->morph(new_armyproto);
639           return;
640         }
641     }
642
643   //failing that, any army with similar characteristics will do.
644   new_armyproto = armyset->lookupSimilarArmy(old_armyproto);
645   if (new_armyproto != NULL)
646     {
647       army->morph(new_armyproto);
648       return;
649     }
650
651   //failing that, any army with the same strength and turns will do.
652   new_armyproto = 
653     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(),
654                                           old_armyproto->getProduction());
655   if (new_armyproto != NULL)
656     {
657       army->morph(new_armyproto);
658       return;
659     }
660
661   //failing that, any army with the same strength will do.
662   new_armyproto = 
663     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(), 0);
664   if (new_armyproto != NULL)
665     {
666       army->morph(new_armyproto);
667       return;
668     }
669
670   //failing that, any army with the same turns will do.
671   new_armyproto = 
672     armyset->lookupArmyByStrengthAndTurns(0, old_armyproto->getProduction());
673   if (new_armyproto != NULL)
674     {
675       army->morph(new_armyproto);
676       return;
677     }
678
679   //failing that, any army will do.
680   new_armyproto = armyset->lookupArmyByGender(old_armyproto->getGender());
681   if (new_armyproto != NULL)
682     {
683       army->morph(new_armyproto);
684       return;
685     }
686
687 }
688
689 const ArmyProto * Armyset::getRandomRuinKeeper() const
690 {
691   // list all the army types that can be a sentinel.
692   std::vector<const ArmyProto*> occupants;
693   for (const_iterator i = begin(); i != end(); i++)
694     {
695       const ArmyProto *a = *i;
696       if (a->getDefendsRuins())
697         occupants.push_back(a);
698     }
699             
700   if (!occupants.empty())
701     return occupants[rand() % occupants.size()];
702
703   return NULL;
704 }
705
706 const ArmyProto *Armyset::getRandomAwardableAlly() const
707 {
708   // list all the army types that can be given out as a reward.
709   std::vector<const ArmyProto*> allies;
710   for (const_iterator i = begin(); i != end(); i++)
711     {
712       const ArmyProto *a = *i;
713       if (a->getAwardable() == true)
714         allies.push_back(a);
715     }
716             
717   if (!allies.empty())
718     return allies[rand() % allies.size()];
719
720   return NULL;
721 }