1 //  Copyright (C) 2007, 2008, 2009, 2010, 2011, 2014, 2015, 2020 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 <gtkmm.h>
20 #include "rectangle.h"
21 #include <sigc++/functors/mem_fun.h>
22 
23 #include "armyset.h"
24 #include "File.h"
25 #include "shield.h"
26 #include "gui/image-helpers.h"
27 #include "armysetlist.h"
28 #include "armyprodbase.h"
29 #include "tarhelper.h"
30 #include "Configuration.h"
31 #include "file-compat.h"
32 #include "ucompose.hpp"
33 #include "xmlhelper.h"
34 #include "rnd.h"
35 #include "player.h"
36 
37 Glib::ustring Armyset::d_tag = "armyset";
38 Glib::ustring Armyset::file_extension = ARMYSET_EXT;
39 
40 #define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
41 //#define debug(x)
42 
43 #define DEFAULT_ARMY_TILE_SIZE 40
Armyset(guint32 id,Glib::ustring name)44 Armyset::Armyset(guint32 id, Glib::ustring name)
45  : Set(ARMYSET_EXT, id, name, DEFAULT_ARMY_TILE_SIZE),
46         d_ship(0), d_shipmask(0), d_standard(0), d_standard_mask(0), d_bag(0)
47 {
48   d_bag_name = "";
49   d_stackship_name = "";
50   d_standard_name = "";
51 }
52 
Armyset(XML_Helper * helper,Glib::ustring directory)53 Armyset::Armyset(XML_Helper *helper, Glib::ustring directory)
54  : Set(ARMYSET_EXT, helper), d_ship(0), d_shipmask(0), d_standard(0),
55     d_standard_mask(0), d_bag(0)
56 {
57   d_bag_name = "";
58   d_stackship_name = "";
59   d_standard_name = "";
60   setDirectory(directory);
61   guint32 ts;
62   helper->getData(ts, "tilesize");
63   setTileSize(ts);
64   helper->getData(d_stackship_name, "stackship");
65   File::add_png_if_no_ext (d_stackship_name);
66   helper->getData(d_standard_name, "plantedstandard");
67   File::add_png_if_no_ext (d_standard_name);
68   helper->getData(d_bag_name, "bag");
69   File::add_png_if_no_ext (d_bag_name);
70   helper->registerTag(ArmyProto::d_tag,
71 		      sigc::mem_fun((*this), &Armyset::loadArmyProto));
72 }
73 
Armyset(const Armyset & a)74 Armyset::Armyset(const Armyset& a)
75  : std::list<ArmyProto*>(), sigc::trackable(a), Set(a), d_ship(0),
76     d_shipmask(0), d_standard(0), d_standard_mask(0), d_bag(0)
77 {
78 
79   if (a.d_ship)
80     d_ship = a.d_ship->copy();
81 
82   if (a.d_shipmask)
83     d_shipmask = a.d_shipmask->copy();
84 
85   if (a.d_standard)
86     d_standard = a.d_standard->copy();
87 
88   if (a.d_standard_mask)
89     d_standard_mask = a.d_standard_mask->copy();
90 
91   if (a.d_bag)
92     d_bag = a.d_bag->copy();
93 
94   d_standard_name = a.d_standard_name;
95   d_stackship_name = a.d_stackship_name;
96   d_bag_name = a.d_bag_name;
97 
98   for (const_iterator i = a.begin(); i != a.end(); i++)
99     push_back(new ArmyProto(*(*i)));
100 }
101 
~Armyset()102 Armyset::~Armyset()
103 {
104   uninstantiateImages();
105   for (iterator it = begin(); it != end(); it++)
106     delete *it;
107   clear();
108   clean_tmp_dir();
109 }
110 
loadArmyProto(Glib::ustring tag,XML_Helper * helper)111 bool Armyset::loadArmyProto(Glib::ustring tag, XML_Helper* helper)
112 {
113     if (tag == ArmyProto::d_tag)
114       {
115         ArmyProto *a = new ArmyProto(helper);
116         a->setArmyset(getId());
117         push_back(a);
118       }
119     return true;
120 }
121 
save(Glib::ustring filename,Glib::ustring ext) const122 bool Armyset::save(Glib::ustring filename, Glib::ustring ext) const
123 {
124   bool broken = false;
125   Glib::ustring goodfilename = File::add_ext_if_necessary(filename, ext);
126 
127   Glib::ustring tmpfile = File::get_tmp_file();
128   XML_Helper helper(tmpfile, std::ios::out);
129   helper.begin(LORDSAWAR_ARMYSET_VERSION);
130   broken = !save(&helper);
131   helper.close();
132   if (broken == true)
133     return false;
134   return saveTar(tmpfile, tmpfile + ".tar", goodfilename);
135 }
136 
save(XML_Helper * helper) const137 bool Armyset::save(XML_Helper* helper) const
138 {
139     bool retval = true;
140 
141     retval &= helper->openTag(d_tag);
142 
143     retval &= Set::save(helper);
144     retval &= helper->saveData("tilesize", getUnscaledTileSize());
145     retval &= helper->saveData("stackship", d_stackship_name);
146     retval &= helper->saveData("plantedstandard", d_standard_name);
147     retval &= helper->saveData("bag", d_bag_name);
148 
149     for (const_iterator it = begin(); it != end(); it++)
150       (*it)->save(helper);
151 
152     retval &= helper->closeTag();
153 
154     return retval;
155 }
156 
lookupSimilarArmy(ArmyProto * army) const157 ArmyProto * Armyset::lookupSimilarArmy(ArmyProto *army) const
158 {
159   for (const_iterator it = begin(); it != end(); it++)
160     {
161       if ((*it)->getGender() == army->getGender() &&
162 	  (*it)->getStrength() == army->getStrength() &&
163 	  (*it)->getProduction() == army->getProduction() &&
164 	  (*it)->getArmyBonus() == army->getArmyBonus() &&
165 	  (*it)->getMoveBonus() == army->getMoveBonus() &&
166 	  (*it)->getMaxMoves() == army->getMaxMoves() &&
167 	  (*it)->getAwardable() == army->getAwardable() &&
168 	  (*it)->getDefendsRuins() == army->getDefendsRuins())
169 	return *it;
170     }
171   for (const_iterator it = begin(); it != end(); it++)
172     {
173       if ((*it)->getGender() == army->getGender() &&
174 	  (*it)->getStrength() == army->getStrength() &&
175 	  (*it)->getProduction() == army->getProduction() &&
176 	  (*it)->getArmyBonus() == army->getArmyBonus() &&
177 	  (*it)->getMoveBonus() == army->getMoveBonus() &&
178 	  (*it)->getMaxMoves() == army->getMaxMoves())
179 	return *it;
180     }
181   for (const_iterator it = begin(); it != end(); it++)
182     {
183       if ((*it)->getGender() == army->getGender() &&
184 	  (*it)->getStrength() == army->getStrength() &&
185 	  (*it)->getProduction() == army->getProduction() &&
186 	  (*it)->getMaxMoves() == army->getMaxMoves())
187 	return *it;
188     }
189   return NULL;
190 }
191 
lookupArmyByGender(Hero::Gender gender) const192 ArmyProto * Armyset::lookupArmyByGender(Hero::Gender gender) const
193 {
194   for (const_iterator it = begin(); it != end(); it++)
195     {
196       if ((*it)->getGender() == gender)
197 	return *it;
198     }
199   return  NULL;
200 }
lookupArmyByStrengthAndTurns(guint32 str,guint32 turns) const201 ArmyProto * Armyset::lookupArmyByStrengthAndTurns(guint32 str, guint32 turns) const
202 {
203   for (const_iterator it = begin(); it != end(); it++)
204     {
205       if (str && turns)
206 	{
207 	  if ((*it)->getStrength() == str && (*it)->getProduction() == turns)
208 	    return *it;
209 	}
210       else if (str && !turns)
211 	{
212 	  if ((*it)->getStrength() == str)
213 	    return *it;
214 	}
215       else if (turns && !str)
216 	{
217 	  if ((*it)->getProduction() == turns)
218 	    return *it;
219 	}
220     }
221   return NULL;
222 }
223 
lookupArmyByName(Glib::ustring name) const224 ArmyProto * Armyset::lookupArmyByName(Glib::ustring name) const
225 {
226   for (const_iterator it = begin(); it != end(); it++)
227     {
228       if ((*it)->getName().uppercase() == name.uppercase())
229 	return *it;
230     }
231   return NULL;
232 }
233 
lookupArmyByType(guint32 army_type_id) const234 ArmyProto * Armyset::lookupArmyByType(guint32 army_type_id) const
235 {
236   for (const_iterator it = begin(); it != end(); it++)
237     {
238       if ((*it)->getId() == army_type_id)
239 	return *it;
240     }
241   return NULL;
242 }
243 
validateHero()244 bool Armyset::validateHero()
245 {
246   bool found = false;
247   //do we have a hero?
248   for (iterator it = begin(); it != end(); it++)
249     {
250       if ((*it)->isHero() == true)
251         {
252           found = true;
253           break;
254         }
255     }
256   if (!found)
257     return false;
258   return true;
259 }
260 
validatePurchasables()261 bool Armyset::validatePurchasables()
262 {
263   bool found = false;
264   for (iterator it = begin(); it != end(); it++)
265     {
266       if ((*it)->getNewProductionCost() > 0 )
267 	{
268 	  found = true;
269 	  break;
270 	}
271     }
272   if (!found)
273     return false;
274   return true;
275 }
276 
validateRuinDefenders()277 bool Armyset::validateRuinDefenders()
278 {
279   bool found = false;
280   for (iterator it = begin(); it != end(); it++)
281     {
282       if ((*it)->getDefendsRuins() == true)
283 	{
284 	  found = true;
285 	  break;
286 	}
287     }
288   if (!found)
289     return false;
290   return true;
291 }
292 
validateAwardables()293 bool Armyset::validateAwardables()
294 {
295   bool found = false;
296   for (iterator it = begin(); it != end(); it++)
297     {
298       if ((*it)->getAwardable() == true)
299 	{
300 	  found = true;
301 	  break;
302 	}
303     }
304   if (!found)
305     return false;
306   return true;
307 }
validateShip()308 bool Armyset::validateShip()
309 {
310   if (getShipImageName() == "")
311     return false;
312   return true;
313 }
314 
validateBag()315 bool Armyset::validateBag()
316 {
317   if (getBagImageName() == "")
318     return false;
319   return true;
320 }
321 
validateStandard()322 bool Armyset::validateStandard()
323 {
324   if (getStandardImageName() == "")
325     return false;
326   return true;
327 }
328 
validateArmyUnitImage(ArmyProto * army,Shield::Colour & c)329 bool Armyset::validateArmyUnitImage(ArmyProto *army, Shield::Colour &c)
330 {
331   for (unsigned int i = Shield::WHITE; i <= Shield::NEUTRAL; i++)
332     if (army->getImageName(Shield::Colour(i)) == "")
333       {
334 	c = Shield::Colour(i);
335 	return false;
336       }
337   return true;
338 }
validateArmyUnitImages()339 bool Armyset::validateArmyUnitImages()
340 {
341   Shield::Colour c;
342   for (iterator it = begin(); it != end(); it++)
343     {
344       if (validateArmyUnitImage(*it, c) == false)
345 	return false;
346     }
347   return true;
348 }
349 
validateArmyUnitName(ArmyProto * army)350 bool Armyset::validateArmyUnitName(ArmyProto *army)
351 {
352   if (army->getName() == "")
353     return false;
354   return true;
355 }
validateArmyUnitNames()356 bool Armyset::validateArmyUnitNames()
357 {
358   for (iterator it = begin(); it != end(); it++)
359     {
360       if (validateArmyUnitName(*it) == false)
361 	return false;
362     }
363   return true;
364 }
validateArmyTypeIds()365 bool Armyset::validateArmyTypeIds()
366 {
367   std::list<guint32> ids = std::list<guint32>();
368   for (iterator it = begin(); it != end(); it++)
369     {
370       if (std::find(ids.begin(), ids.end(), (*it)->getId()) == ids.end())
371         ids.push_back((*it)->getId());
372       else
373         return false;
374     }
375   return true;
376 }
validate()377 bool Armyset::validate()
378 {
379   if (String::utrim (getName ()) == "")
380     return false;
381 
382   bool valid = true;
383   valid = validateHero();
384   if (!valid)
385     return false;
386   valid = validatePurchasables();
387   if (!valid)
388     return false;
389   //do we have any units that defend ruins?
390   valid = validateRuinDefenders();
391   if (!valid)
392     return false;
393   //do we have any units that can be awarded?
394   valid = validateAwardables();
395   if (!valid)
396     return false;
397   //is the stackship set?
398   valid = validateShip();
399   if (!valid)
400     return false;
401   //is the standard set?
402   valid = validateStandard();
403   if (!valid)
404     return false;
405   //is the bag set?
406   valid = validateBag();
407   if (!valid)
408     return false;
409   //is there an image set for each army unit?
410   valid = validateArmyUnitImages();
411   if (!valid)
412     return false;
413   //is there a name set for each army unit?
414   valid = validateArmyUnitNames();
415   if (!valid)
416     return false;
417   //unique Ids per army unit?
418   valid = validateArmyTypeIds();
419   if (!valid)
420     return false;
421 
422   return valid;
423 }
424 
425 //! Helper class for making a new Armyset object from an armyset file.
426 class ArmysetLoader
427 {
428 public:
ArmysetLoader(Glib::ustring filename,bool & broken,bool & unsupported)429     ArmysetLoader(Glib::ustring filename, bool &broken, bool &unsupported)
430       {
431         unsupported_version = false;
432 	armyset = NULL;
433 	dir = File::get_dirname(filename);
434         file = File::get_basename(filename);
435 	if (File::nameEndsWith(filename, Armyset::file_extension) == false)
436 	  filename += Armyset::file_extension;
437         Tar_Helper t(filename, std::ios::in, broken);
438         if (broken)
439           return;
440         Glib::ustring lwafilename =
441           t.getFirstFile(Armyset::file_extension, broken);
442         if (broken)
443           return;
444 	XML_Helper helper(lwafilename, std::ios::in);
445 	helper.registerTag(Armyset::d_tag, sigc::mem_fun((*this), &ArmysetLoader::load));
446 	if (!helper.parseXML())
447 	  {
448             unsupported = unsupported_version;
449             std::cerr << String::ucompose(_("Error!  can't load armyset `%1'."), filename) << std::endl;
450 	    if (armyset != NULL)
451 	      delete armyset;
452 	    armyset = NULL;
453 	  }
454         helper.close();
455         File::erase(lwafilename);
456         t.Close();
457       };
load(Glib::ustring tag,XML_Helper * helper)458     bool load(Glib::ustring tag, XML_Helper* helper)
459       {
460 	if (tag == Armyset::d_tag)
461 	  {
462             if (helper->getVersion() == LORDSAWAR_ARMYSET_VERSION)
463               {
464                 armyset = new Armyset(helper, dir);
465                 armyset->setBaseName(file);
466                 return true;
467               }
468             else
469               {
470                 unsupported_version = true;
471                 return false;
472               }
473 	  }
474 	return false;
475       };
476     Glib::ustring dir;
477     Glib::ustring file;
478     Armyset *armyset;
479     bool unsupported_version;
480 };
481 
create(Glib::ustring filename,bool & unsupported_version)482 Armyset *Armyset::create(Glib::ustring filename, bool &unsupported_version)
483 {
484   bool broken = false;
485   ArmysetLoader d(filename, broken, unsupported_version);
486   if (broken)
487     return NULL;
488   return d.armyset;
489 }
490 
instantiateImages(bool scale,bool & broken)491 void Armyset::instantiateImages(bool scale, bool &broken)
492 {
493   uninstantiateImages();
494   broken = false;
495   Tar_Helper t(getConfigurationFile(), std::ios::in, broken);
496   if (broken)
497     return;
498 
499   for (iterator it = begin(); it != end(); ++it)
500     (*it)->instantiateImages(getUnscaledTileSize(), &t, scale, broken);
501 
502   Glib::ustring ship_filename = "";
503   Glib::ustring flag_filename = "";
504   Glib::ustring bag_filename = "";
505   if (getShipImageName().empty() == false && !broken)
506     ship_filename = t.getFile(getShipImageName(), broken);
507   if (getStandardImageName().empty() == false && !broken)
508     flag_filename = t.getFile(getStandardImageName(), broken);
509   if (getBagImageName().empty() == false && !broken)
510     bag_filename = t.getFile(getBagImageName(), broken);
511 
512   if (!broken)
513     {
514       if (ship_filename.empty() == false)
515         loadShipPic(ship_filename, scale, broken);
516       if (flag_filename.empty() == false)
517         loadStandardPic(flag_filename, scale, broken);
518       if (bag_filename.empty() == false)
519         loadBagPic(bag_filename, broken);
520     }
521 
522   if (ship_filename.empty() == false)
523     File::erase(ship_filename);
524   if (flag_filename.empty() == false)
525     File::erase(flag_filename);
526   if (bag_filename.empty() == false)
527     File::erase(bag_filename);
528   t.Close();
529 }
530 
uninstantiateImages()531 void Armyset::uninstantiateImages()
532 {
533   for (iterator it = begin(); it != end(); it++)
534     (*it)->uninstantiateImages();
535 
536   if (d_ship)
537     delete d_ship;
538 
539   if (d_shipmask)
540     delete d_shipmask;
541 
542   if (d_standard)
543     delete d_standard;
544 
545   if (d_standard_mask)
546     delete d_standard_mask;
547 
548   if (d_bag)
549     delete d_bag;
550 
551   d_ship = NULL;
552   d_shipmask = NULL;
553   d_standard = NULL;
554   d_standard_mask = NULL;
555   d_bag = NULL;
556 }
557 
loadShipPic(Glib::ustring image_filename,bool scale,bool & broken)558 void Armyset::loadShipPic(Glib::ustring image_filename, bool scale,
559                           bool &broken)
560 {
561   if (image_filename.empty() == true)
562     {
563       broken = true;
564       return;
565     }
566   std::vector<PixMask*> half;
567   half = disassemble_row(image_filename, 2, broken);
568   if (!broken)
569     {
570       if (scale)
571         {
572           int s = getUnscaledTileSize();
573           PixMask::scale(half[0], s, s);
574           PixMask::scale(half[1], s, s);
575         }
576       setShipImage(half[0]);
577       setShipMask(half[1]);
578     }
579 }
580 
loadBagPic(Glib::ustring image_filename,bool & broken)581 void Armyset::loadBagPic(Glib::ustring image_filename, bool &broken)
582 {
583   if (image_filename.empty() == true)
584     {
585       broken = true;
586       return;
587     }
588   if (!broken)
589     setBagPic(PixMask::create(image_filename, broken));
590 }
591 
loadStandardPic(Glib::ustring image_filename,bool scale,bool & broken)592 void Armyset::loadStandardPic(Glib::ustring image_filename, bool scale,
593                               bool &broken)
594 {
595   if (image_filename.empty() == true)
596     {
597       broken = true;
598       return;
599     }
600   std::vector<PixMask*> half = disassemble_row(image_filename, 2, broken);
601   if (!broken)
602     {
603       if (scale)
604         {
605           int s = getUnscaledTileSize();
606           PixMask::scale(half[0], s, s);
607           PixMask::scale(half[1], s, s);
608         }
609       setStandardPic(half[0]);
610       setStandardMask(half[1]);
611     }
612 }
613 
switchArmysetForRuinKeeper(Army * army,const Armyset * armyset)614 void Armyset::switchArmysetForRuinKeeper(Army *army, const Armyset *armyset)
615 {
616   //do our best to change the armyset for the given ruin keeper.
617 
618   //go find an equivalent type in the new armyset.
619   Armyset *old_armyset
620     = Armysetlist::getInstance()->get(army->getOwner()->getArmyset());
621   ArmyProto *old_armyproto = old_armyset->lookupArmyByType(army->getTypeId());
622   if (old_armyproto == NULL)
623     return;
624   const ArmyProto *new_armyproto = armyset->lookupArmyByType(army->getTypeId());
625 
626   //try looking at the same id first
627   if (new_armyproto != NULL &&
628       old_armyproto->getName() == new_armyproto->getName() &&
629       old_armyproto->getDefendsRuins() == new_armyproto->getDefendsRuins())
630     {
631       army->morph(new_armyproto);
632       return;
633     }
634 
635   //try finding an army by the same name
636   new_armyproto = armyset->lookupArmyByName(old_armyproto->getName());
637   if (new_armyproto != NULL &&
638       old_armyproto->getDefendsRuins() == new_armyproto->getDefendsRuins())
639     {
640       army->morph(new_armyproto);
641       return;
642     }
643 
644   //failing that, any ruin keeper will do.
645   new_armyproto = armyset->getRandomRuinKeeper();
646   if (new_armyproto != NULL)
647     {
648       army->morph(new_armyproto);
649       return;
650     }
651 }
652 
switchArmyset(ArmyProdBase * army,const Armyset * armyset)653 void Armyset::switchArmyset(ArmyProdBase *army, const Armyset *armyset)
654 {
655   //do our best to change the armyset for the given armyprodbase.
656 
657   //go find an equivalent type in the new armyset.
658   Armyset *old_armyset
659     = Armysetlist::getInstance()->get(army->getArmyset());
660   ArmyProto *old_armyproto = old_armyset->lookupArmyByType(army->getTypeId());
661   if (old_armyproto == NULL)
662     return;
663   ArmyProto *new_armyproto = armyset->lookupArmyByType(army->getTypeId());
664 
665   //try looking at the same id first
666   if (new_armyproto != NULL &&
667       old_armyproto->getName() == new_armyproto->getName())
668     {
669       army->morph(new_armyproto);
670       return;
671     }
672 
673   //try finding an army by the same name
674   new_armyproto = armyset->lookupArmyByName(old_armyproto->getName());
675   if (new_armyproto != NULL)
676     {
677       army->morph(new_armyproto);
678       return;
679     }
680 
681   //failing that, any army with similar characteristics will do.
682   new_armyproto = armyset->lookupSimilarArmy(old_armyproto);
683   if (new_armyproto != NULL)
684     {
685       army->morph(new_armyproto);
686       return;
687     }
688 
689   //failing that, any army with the same strength and turns will do.
690   new_armyproto =
691     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(),
692 					  old_armyproto->getProduction());
693   if (new_armyproto != NULL)
694     {
695       army->morph(new_armyproto);
696       return;
697     }
698 
699   //failing that, any army with the same strength will do.
700   new_armyproto =
701     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(), 0);
702   if (new_armyproto != NULL)
703     {
704       army->morph(new_armyproto);
705       return;
706     }
707 
708   //failing that, any army with the same turns will do.
709   new_armyproto =
710     armyset->lookupArmyByStrengthAndTurns(0, old_armyproto->getProduction());
711   if (new_armyproto != NULL)
712     {
713       army->morph(new_armyproto);
714       return;
715     }
716 
717   //failing that, any army will do.
718   new_armyproto = armyset->lookupArmyByGender(old_armyproto->getGender());
719   if (new_armyproto != NULL)
720     {
721       army->morph(new_armyproto);
722       return;
723     }
724 }
725 
switchArmyset(Army * army,const Armyset * armyset)726 void Armyset::switchArmyset(Army *army, const Armyset *armyset)
727 {
728   //do our best to change the armyset for the given army.
729 
730   //go find an equivalent type in the new armyset.
731   Armyset *old_armyset
732     = Armysetlist::getInstance()->get(army->getOwner()->getArmyset());
733   ArmyProto *old_armyproto = old_armyset->lookupArmyByType(army->getTypeId());
734   if (!old_armyproto)
735     return;
736   ArmyProto *new_armyproto = armyset->lookupArmyByType(army->getTypeId());
737 
738   //try looking at the same id first
739   if (new_armyproto != NULL &&
740       old_armyproto->getId() == new_armyproto->getId())
741     {
742       army->morph(new_armyproto);
743       return;
744     }
745 
746   //try finding an army by the same name
747   new_armyproto = armyset->lookupArmyByName(old_armyproto->getName());
748   if (new_armyproto != NULL)
749     {
750       army->morph(new_armyproto);
751       return;
752     }
753 
754   //failing that, an army with the same gender (heroes).
755   if (army->isHero() == true)
756     {
757       new_armyproto = armyset->lookupArmyByGender(old_armyproto->getGender());
758       if (new_armyproto != NULL)
759 	{
760 	  army->morph(new_armyproto);
761 	  return;
762 	}
763     }
764 
765   //failing that, any army with similar characteristics will do.
766   new_armyproto = armyset->lookupSimilarArmy(old_armyproto);
767   if (new_armyproto != NULL)
768     {
769       army->morph(new_armyproto);
770       return;
771     }
772 
773   //failing that, any army with the same strength and turns will do.
774   new_armyproto =
775     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(),
776 					  old_armyproto->getProduction());
777   if (new_armyproto != NULL)
778     {
779       army->morph(new_armyproto);
780       return;
781     }
782 
783   //failing that, any army with the same strength will do.
784   new_armyproto =
785     armyset->lookupArmyByStrengthAndTurns(old_armyproto->getStrength(), 0);
786   if (new_armyproto != NULL)
787     {
788       army->morph(new_armyproto);
789       return;
790     }
791 
792   //failing that, any army with the same turns will do.
793   new_armyproto =
794     armyset->lookupArmyByStrengthAndTurns(0, old_armyproto->getProduction());
795   if (new_armyproto != NULL)
796     {
797       army->morph(new_armyproto);
798       return;
799     }
800 
801   //failing that, any army will do.
802   new_armyproto = armyset->lookupArmyByGender(old_armyproto->getGender());
803   if (new_armyproto != NULL)
804     {
805       army->morph(new_armyproto);
806       return;
807     }
808 
809 }
810 
getRandomRuinKeeper() const811 const ArmyProto * Armyset::getRandomRuinKeeper() const
812 {
813   // list all the army types that can be a sentinel.
814   std::vector<const ArmyProto*> occupants;
815   for (const_iterator i = begin(); i != end(); i++)
816     {
817       const ArmyProto *a = *i;
818       if (a->getDefendsRuins())
819 	occupants.push_back(a);
820     }
821 
822   if (!occupants.empty())
823     return occupants[Rnd::rand() % occupants.size()];
824 
825   return NULL;
826 }
827 
getRandomAwardableAlly() const828 const ArmyProto *Armyset::getRandomAwardableAlly() const
829 {
830   // list all the army types that can be given out as a reward.
831   std::vector<const ArmyProto*> allies;
832   for (const_iterator i = begin(); i != end(); i++)
833     {
834       const ArmyProto *a = *i;
835       if (a->getAwardable() == true)
836 	allies.push_back(a);
837     }
838 
839   if (!allies.empty())
840     return allies[Rnd::rand() % allies.size()];
841 
842   return NULL;
843 }
844 
reload(bool & broken)845 void Armyset::reload(bool &broken)
846 {
847   broken = false;
848   bool unsupported = false;
849   ArmysetLoader d(getConfigurationFile(), broken, unsupported);
850   if (!broken && d.armyset && d.armyset->validate())
851     {
852       uninstantiateImages();
853       for (iterator it = begin(); it != end(); it++)
854         delete *it;
855       clear();
856       for (iterator it = d.armyset->begin(); it != d.armyset->end(); it++)
857         push_back(new ArmyProto(*(*it)));
858       *this = *d.armyset;
859       instantiateImages(true, broken);
860     }
861 }
862 
calculate_preferred_tile_size(guint32 & ts) const863 bool Armyset::calculate_preferred_tile_size(guint32 &ts) const
864 {
865   guint32 tilesize = 0;
866   std::map<guint32, guint32> sizecounts;
867 
868   if (d_ship)
869     sizecounts[d_ship->get_unscaled_width()]++;
870   if (d_standard)
871     sizecounts[d_standard->get_unscaled_width()]++;
872   if (d_bag)
873     sizecounts[d_bag->get_unscaled_width()]++;
874   for (const_iterator it = begin(); it != end(); it++)
875     {
876       ArmyProto *a = (*it);
877       if (a->getImage(Shield::NEUTRAL) != NULL)
878         sizecounts[a->getImage(Shield::NEUTRAL)->get_unscaled_width()]++;
879     }
880 
881   guint32 maxcount = 0;
882   for (std::map<guint32, guint32>::iterator it = sizecounts.begin();
883        it != sizecounts.end(); it++)
884     {
885       if ((*it).second > maxcount)
886         {
887           maxcount = (*it).second;
888           tilesize = (*it).first;
889         }
890     }
891   bool ret = true;
892   if (tilesize == 0)
893     {
894       ts = DEFAULT_ARMY_TILE_SIZE;
895       ret = false;
896     }
897   else
898     ts = tilesize;
899   return ret;
900 }
901 
upgrade(Glib::ustring filename,Glib::ustring old_version,Glib::ustring new_version)902 bool Armyset::upgrade(Glib::ustring filename, Glib::ustring old_version, Glib::ustring new_version)
903 {
904   return FileCompat::getInstance()->upgrade(filename, old_version, new_version,
905                                             FileCompat::ARMYSET, d_tag);
906 }
907 
support_backward_compatibility()908 void Armyset::support_backward_compatibility()
909 {
910   FileCompat::getInstance()->support_type(FileCompat::ARMYSET, file_extension,
911                                           d_tag, true);
912   FileCompat::getInstance()->support_version
913     (FileCompat::ARMYSET, "0.2.1", "0.3.0",
914      sigc::ptr_fun(&Armyset::upgrade));
915 }
916 
copy(const Armyset * armyset)917 Armyset * Armyset::copy(const Armyset *armyset)
918 {
919   if (!armyset)
920     return NULL;
921   return new Armyset(*armyset);
922 }
923 
getMaxId() const924 guint32 Armyset::getMaxId() const
925 {
926   guint32 max = 0;
927   for (const_iterator i = begin(); i != end(); i++)
928     if ((*i)->getId() > max)
929       max = (*i)->getId();
930   return max;
931 }
932 
weakest_quickest(const ArmyProto * first,const ArmyProto * second)933 bool weakest_quickest (const ArmyProto* first, const ArmyProto* second)
934 {
935   int ffly = first->getMoveBonus() == Tile::isFlying();
936   int sfly = second->getMoveBonus() == Tile::isFlying();
937   int f = (first->getStrength() * 100) + (first->getProduction() * 101) + (ffly * 1000);
938   int s = (second->getStrength() * 100) + (second->getProduction() * 101) + (sfly * 1000);
939   if (f < s)
940     return true;
941   return false;
942 }
943 
lookupWeakestQuickestArmy() const944 ArmyProto *Armyset::lookupWeakestQuickestArmy() const
945 {
946   Armyset *a = new Armyset(*this);
947   a->sort(weakest_quickest);
948   guint32 type_id = (*(a->begin()))->getId();
949   ArmyProto *p = Armysetlist::getInstance()->getArmy(getId(), type_id);
950   delete a;
951   return p;
952 }
953 
clearStandardImage(bool clear_name)954 void Armyset::clearStandardImage (bool clear_name)
955 {
956   if (clear_name)
957     setStandardImageName ("");
958 
959   PixMask *p = getStandardPic ();
960   if (p)
961     delete p;
962   setStandardPic (NULL);
963 
964   p = getStandardMask ();
965   if (p)
966     delete p;
967   setStandardMask (NULL);
968 }
969 
clearBagImage(bool clear_name)970 void Armyset::clearBagImage (bool clear_name)
971 {
972   if (clear_name)
973     setBagImageName ("");
974 
975   PixMask *p = getBagPic ();
976   if (p)
977     delete p;
978   setBagPic (NULL);
979 }
980 
clearShipImage(bool clear_name)981 void Armyset::clearShipImage (bool clear_name)
982 {
983   if (clear_name)
984     setShipImageName ("");
985 
986   PixMask *p = getShipPic ();
987   if (p)
988     delete p;
989   setShipImage (NULL);
990 
991   p = getShipMask ();
992   if (p)
993     delete p;
994   setShipMask (NULL);
995 }
996 
instantiateBagImage()997 bool Armyset::instantiateBagImage ()
998 {
999   bool broken = false;
1000   Tar_Helper t(getConfigurationFile(), std::ios::in, broken);
1001   if (broken)
1002     return broken;
1003   Glib::ustring imgname = getBagImageName();
1004   if (imgname.empty() == false)
1005     {
1006       Glib::ustring filename = t.getFile(imgname, broken);
1007       if (!broken)
1008         {
1009           clearBagImage (false);
1010           loadBagPic(filename, broken);
1011         }
1012     }
1013   return broken;
1014 }
1015 
instantiateStandardImage()1016 bool Armyset::instantiateStandardImage ()
1017 {
1018   bool broken = false;
1019   Tar_Helper t(getConfigurationFile(), std::ios::in, broken);
1020   if (broken)
1021     return broken;
1022   Glib::ustring imgname = getStandardImageName();
1023   if (imgname.empty() == false)
1024     {
1025       Glib::ustring filename = t.getFile(imgname, broken);
1026       if (!broken)
1027         {
1028           clearStandardImage (false);
1029           loadStandardPic(filename, false, broken);
1030         }
1031     }
1032   return broken;
1033 }
1034 
instantiateShipImage()1035 bool Armyset::instantiateShipImage ()
1036 {
1037   bool broken = false;
1038   Tar_Helper t(getConfigurationFile(), std::ios::in, broken);
1039   if (broken)
1040     return broken;
1041   Glib::ustring imgname = getShipImageName();
1042   if (imgname.empty() == false)
1043     {
1044       Glib::ustring filename = t.getFile(imgname, broken);
1045       if (!broken)
1046         {
1047           clearShipImage (false);
1048           loadShipPic(filename, false, broken);
1049         }
1050     }
1051   return broken;
1052 }
1053 
get_default_tile_size()1054 guint32 Armyset::get_default_tile_size ()
1055 {
1056   Armyset *a = new Armyset (1, "");
1057   guint32 ts = a->getUnscaledTileSize ();
1058   delete a;
1059   return ts;
1060 }
1061 
uninstantiateSameNamedImages(Glib::ustring name)1062 void Armyset::uninstantiateSameNamedImages (Glib::ustring name)
1063 {
1064   if (getBagImageName() == name)
1065     clearBagImage ();
1066   if (getStandardImageName() == name)
1067     clearStandardImage ();
1068   if (getShipImageName() == name)
1069     clearShipImage ();
1070   for (iterator i = begin (); i != end (); i++)
1071     {
1072       for (guint32 cc = Shield::WHITE; cc <= Shield::NEUTRAL; cc++)
1073         {
1074           Shield::Colour c = Shield::Colour (cc);
1075           if ((*i)->getImageName (c) == name)
1076             (*i)->clearImage (c);
1077         }
1078     }
1079 }
1080