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