1 // Copyright (C) 2000, 2001, 2003 Michael Bartl
2 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
3 // Copyright (C) 2004, 2005 Andrea Paternesi
4 // Copyright (C) 2007, 2008, 2011, 2014, 2015, 2017 Ben Asselstine
5 // Copyright (C) 2007, 2008 Ole Laursen
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License as published by
9 //  the Free Software Foundation; either version 3 of the License, or
10 //  (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU Library General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 //  02110-1301, USA.
21 
22 #include <iostream>
23 #include <sstream>
24 #include "army.h"
25 #include "armyprodbase.h"
26 #include "armyproto.h"
27 #include "armysetlist.h"
28 #include "counter.h"
29 #include "xmlhelper.h"
30 #include "stacklist.h"
31 #include "templelist.h"
32 #include "ucompose.hpp"
33 #include "Tile.h"
34 #include "player.h"
35 
36 Glib::ustring Army::d_tag = "army";
37 
38 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
39 #define debug(x)
40 
41 sigc::signal<void, Army*> Army::sdying;
42 
Army(const Army & a,Player * p)43 Army::Army(const Army& a, Player* p)
44     : ArmyBase(a), UniquelyIdentified(a), Ownable(p), sigc::trackable(a),
45     d_type_id(a.d_type_id), d_armyset(a.d_armyset), d_max_hp(a.d_max_hp),
46     d_max_moves_multiplier(a.d_max_moves_multiplier),
47     d_max_moves_rest_bonus(a.d_max_moves_rest_bonus),
48     d_ship(a.d_ship), d_hp(a.d_hp), d_moves(a.d_moves), d_xp(a.d_xp),
49     d_level(a.d_level), d_battles_number(a.d_battles_number),
50     d_number_hashit(a.d_number_hashit),
51     d_number_hasbeenhit(a.d_number_hasbeenhit),
52     d_visitedTemples(a.d_visitedTemples)
53 {
54   for(int i = 0; i < 3; i++)
55     d_medal_bonus[i] = a.d_medal_bonus[i];
56 }
57 
Army(const ArmyProto & a,Player * p)58 Army::Army(const ArmyProto& a, Player* p)
59     :ArmyBase(a), UniquelyIdentified(), Ownable(p),
60     d_type_id(a.getId()), d_armyset(a.getArmyset()),
61     d_max_hp(2), d_max_moves_multiplier(1), d_max_moves_rest_bonus(0),
62     d_ship(false), d_hp(2), d_moves(a.getMaxMoves()), d_xp(0), d_level(0),
63     d_battles_number(0), d_number_hashit(0), d_number_hasbeenhit(0)
64 {
65   for(int i = 0; i < 3; i++)
66     d_medal_bonus[i] = 0;
67   d_visitedTemples.clear();
68 }
69 
Army(const ArmyProto & a,guint32 id,Player * p)70 Army::Army(const ArmyProto& a, guint32 id, Player *p)
71     :ArmyBase(a), UniquelyIdentified(id), Ownable(p),
72     d_type_id(a.getId()), d_armyset(a.getArmyset()),
73     d_max_hp(2), d_max_moves_multiplier(1), d_max_moves_rest_bonus(0),
74     d_ship(false), d_hp(2), d_moves(a.getMaxMoves()), d_xp(0), d_level(0),
75     d_battles_number(0), d_number_hashit(0), d_number_hasbeenhit(0)
76 {
77   for(int i = 0; i < 3; i++)
78     d_medal_bonus[i] = 0;
79   d_visitedTemples.clear();
80 }
81 
Army(const ArmyProdBase & a,guint32 id,Player * p)82 Army::Army(const ArmyProdBase& a, guint32 id, Player *p)
83     :ArmyBase(a), UniquelyIdentified(id), Ownable(p),
84     d_type_id(a.getTypeId()), d_armyset(a.getArmyset()),
85     d_max_hp(2), d_max_moves_multiplier(1), d_max_moves_rest_bonus(0),
86     d_ship(false), d_hp(2), d_moves(a.getMaxMoves()), d_xp(0), d_level(0),
87     d_battles_number(0), d_number_hashit(0), d_number_hasbeenhit(0)
88 {
89   for(int i = 0; i < 3; i++)
90     d_medal_bonus[i] = 0;
91   d_visitedTemples.clear();
92 }
93 
createNonUniqueArmy(const ArmyProto & a,Player * player)94 Army* Army::createNonUniqueArmy(const ArmyProto& a, Player *player)
95 {
96   return new Army(a, (guint32) 0, player);
97 }
98 
createNonUniqueArmy(const ArmyProdBase & a,Player * player)99 Army* Army::createNonUniqueArmy(const ArmyProdBase& a, Player *player)
100 {
101   return new Army(a, (guint32) 0, player);
102 }
103 
Army(const ArmyProdBase & a,Player * p)104 Army::Army(const ArmyProdBase& a, Player* p)
105     :ArmyBase(a), UniquelyIdentified(), Ownable(p),
106     d_type_id(a.getTypeId()), d_armyset(a.getArmyset()),
107     d_max_hp(2), d_max_moves_multiplier(1), d_max_moves_rest_bonus(0),
108     d_ship(false), d_hp(2), d_moves(a.getMaxMoves()), d_xp(0), d_level(0),
109     d_battles_number(0), d_number_hashit(0), d_number_hasbeenhit(0)
110 {
111   for(int i = 0; i < 3; i++)
112     d_medal_bonus[i] = 0;
113   d_visitedTemples.clear();
114 }
115 
Army()116 Army::Army()
117   :ArmyBase(), UniquelyIdentified(), Ownable((Player *)0),
118     d_type_id(0), d_armyset(0), d_max_hp(2), d_max_moves_multiplier(1),
119     d_max_moves_rest_bonus(0), d_ship(false), d_hp(2), d_moves(0), d_xp(0),
120     d_level(0), d_battles_number(0), d_number_hashit(0), d_number_hasbeenhit(0)
121 {
122   d_visitedTemples.clear();
123 }
124 
Army(XML_Helper * helper)125 Army::Army(XML_Helper* helper)
126   :ArmyBase(helper), UniquelyIdentified(helper), Ownable((XML_Helper*) 0),
127     d_type_id(0), d_armyset(0), d_max_hp(2), d_max_moves_multiplier(1),
128     d_max_moves_rest_bonus(0), d_ship(false), d_hp(2), d_moves(0), d_xp(0),
129     d_level(0), d_battles_number(0), d_number_hashit(0), d_number_hasbeenhit(0)
130 {
131   //d_owner is not read in here.  it is set to the owner of the stack
132   //in stack.cpp
133   d_visitedTemples.clear();
134 
135   int ival = -1;
136   //get the information which army we are
137   helper->getData(d_type_id, "type");
138   helper->getData(d_armyset, "armyset");
139 
140   helper->getData(d_hp, "hp");
141   helper->getData(d_ship, "ship");
142   helper->getData(d_moves, "moves");
143   helper->getData(d_max_moves_multiplier, "max_moves_multiplier");
144   helper->getData(d_xp, "xp");
145   helper->getData(d_level, "level");
146 
147   Glib::ustring medals;
148   std::stringstream smedals;
149   bool val;
150 
151   helper->getData(medals, "medals");
152   smedals.str(medals);
153 
154   for(int i=0;i<3;i++)
155     {
156       smedals >> val;
157       d_medal_bonus[i]=val;
158       debug("ARMY-XML-CONSTRUCTOR medalsbonus[" << i << "]=" << d_medal_bonus[i])
159     }
160 
161   helper->getData(d_battles_number, "battlesnumber");
162 
163   Glib::ustring temples;
164   std::stringstream stemples;
165   helper->getData(temples, "visited_temples");
166   stemples.str(temples);
167 
168   while (stemples.eof() == false)
169     {
170       ival = -1;
171       stemples >> ival;
172       if (ival != -1)
173 	d_visitedTemples.push_front(ival);
174     }
175 }
176 
~Army()177 Army::~Army()
178 {
179   if (d_unique)
180     sdying.emit(this);
181 }
182 
setStat(Army::Stat stat,guint32 value)183 void Army::setStat(Army::Stat stat, guint32 value)
184 {
185   switch (stat)
186     {
187     case STRENGTH:
188       d_strength = value;
189       if (d_strength > MAX_ARMY_STRENGTH)
190 	d_strength = MAX_ARMY_STRENGTH;
191       break;
192     case HP:
193       d_max_hp = value;
194       if (d_hp > d_max_hp)
195 	d_hp = value;
196       break;
197     case MOVES:
198       d_max_moves = value;
199       if (d_moves > d_max_moves)
200 	d_moves = value;
201       break;
202     case MOVES_MULTIPLIER:
203       d_max_moves_multiplier = value;
204       break;
205     case MOVE_BONUS:    d_move_bonus = value;
206 			break;
207     case ARMY_BONUS:    d_army_bonus = value;
208 			break;
209     case SIGHT:         d_sight = value;
210 			break;
211     case SHIP:          value == 0 ? d_ship = false : d_ship = true;
212 			break;
213     case BOAT_STRENGTH:
214                         break;
215     }
216 }
217 
getStat(Stat stat,bool modified) const218 guint32 Army::getStat(Stat stat, bool modified) const
219 {
220   switch (stat)
221     {
222     case STRENGTH:
223       return d_strength;
224     case HP:
225       return d_max_hp;
226     case MOVES:
227 	{
228 	  if (modified)
229 	    return (d_max_moves + d_max_moves_rest_bonus) * d_max_moves_multiplier;
230 	  else
231 	    return d_max_moves;
232 	}
233     case MOVE_BONUS:
234       return d_move_bonus;
235     case ARMY_BONUS:
236       return d_army_bonus;
237     case SIGHT:
238       return d_sight;
239     case SHIP:
240       return d_ship;
241     case MOVES_MULTIPLIER:
242       return d_max_moves_multiplier;
243     case BOAT_STRENGTH:
244         {
245           if (d_ship)
246             {
247               if (d_strength >= MAX_BOAT_STRENGTH)
248                 return MAX_BOAT_STRENGTH;
249               else
250                 return d_strength;
251             }
252           else
253             return d_strength;
254         }
255     }
256 
257   // should never come to this
258   return 0;
259 }
260 
resetMoves()261 void Army::resetMoves()
262 {
263   switch (d_moves)
264     {
265     case 0: d_max_moves_rest_bonus = 0; break;
266     case 1: d_max_moves_rest_bonus = 1; break;
267     case 2: d_max_moves_rest_bonus = 2; break;
268     default: d_max_moves_rest_bonus = 2; break;
269     }
270   if (d_ship)
271     d_moves = MAX_BOAT_MOVES;
272   else
273     d_moves = getStat(MOVES);
274 }
275 
276 /* is this temple one we've already visited? */
bless(Temple * temple)277 bool Army::bless(Temple *temple)
278 {
279   bool visited = false;
280 
281   if (!temple)
282     return false;
283 
284   for (auto it: d_visitedTemples)
285     if (it == temple->getId())
286       visited = true;
287 
288   if (visited == false)  /* no?  increase strength */
289     {
290       d_visitedTemples.push_back(temple->getId());
291       setStat(STRENGTH, d_strength + 1);
292     }
293   return !visited;
294 }
295 
296 
heal(guint32 hp)297 void Army::heal(guint32 hp)
298 {
299   if (hp == 0)
300     {
301       // if no hp are specified, we assume that the healing at the end of
302       // the turn takes place. In this case the algorithm is: Heal 10%
303       // plus 1HP for each point of vitality above 5 (or one less for each
304       // point below 5), heal a minimum of 1 HP per turn
305       hp = getStat(HP)/10;
306       if (hp <= 5)
307 	hp = 1;
308       else
309 	hp += 5;
310     }
311 
312   d_hp += hp;
313   if (d_hp > getStat(HP))
314     d_hp = getStat(HP);
315 }
316 
damage(guint32 damageDone)317 bool Army::damage(guint32 damageDone)
318 {
319   if (damageDone >= d_hp)
320     d_hp = 0;
321   else
322     d_hp -= damageDone;
323   return (d_hp == 0);
324 }
325 
decrementMoves(guint32 moves)326 void Army::decrementMoves(guint32 moves)
327 {
328   if (moves >= d_moves)
329     d_moves = 0;
330   else
331     d_moves -= moves;
332 }
333 
incrementMoves(guint32 moves)334 void Army::incrementMoves(guint32 moves)
335 {
336   d_moves += moves;
337 }
338 
gainXp(double n)339 void Army::gainXp(double n)
340 {
341   d_xp += n;
342 }
343 
save(XML_Helper * helper) const344 bool Army::save(XML_Helper* helper) const
345 {
346   bool retval = true;
347 
348   retval &= helper->openTag(Army::d_tag);
349   retval &= saveData(helper);
350   retval &= helper->closeTag();
351 
352   return retval;
353 }
354 
saveData(XML_Helper * helper) const355 bool Army::saveData(XML_Helper* helper) const
356 {
357   bool retval = true;
358 
359   retval &= ArmyBase::saveData(helper);
360   retval &= helper->saveData("id", d_id);
361   retval &= helper->saveData("armyset", d_armyset);
362   retval &= helper->saveData("type", d_type_id);
363   retval &= helper->saveData("hp", d_hp);
364   retval &= helper->saveData("ship", d_ship);
365   retval &= helper->saveData("moves", d_moves);
366   retval &= helper->saveData("xp", d_xp);
367   retval &= helper->saveData("max_moves_multiplier",
368 			     d_max_moves_multiplier);
369   retval &= helper->saveData("level", d_level);
370 
371   std::stringstream medals;
372   for (int i=0;i<3;i++)
373     {
374       medals << d_medal_bonus[i] << " ";
375     }
376   retval &= helper->saveData("medals", medals.str());
377   retval &= helper->saveData("battlesnumber",d_battles_number);
378 
379   std::stringstream temples;
380   for (auto it: d_visitedTemples)
381     temples << it << " ";
382   retval &= helper->saveData("visited_temples", temples.str());
383 
384   return retval;
385 }
386 
setInShip(bool s)387 void Army::setInShip (bool s)
388 {
389   if (s == true && isFlyer() == true)
390     s = false;
391   d_ship = s;
392 }
393 
isFlyer() const394 bool Army::isFlyer() const
395 {
396   return d_move_bonus == Tile::isFlying();
397 }
398 
399 //! Sets this army as being fortified (+1 to stack)
setFortified(bool f)400 void Army::setFortified (bool f)
401 {
402   if (getFortified() == true && f == false)
403     d_army_bonus ^= Army::FORTIFY;
404   else if (getFortified() == false && f == true)
405     d_army_bonus |= Army::FORTIFY;
406 }
407 
408 //! get the fortify flag for this army
getFortified() const409 bool Army::getFortified () const
410 {
411   return (d_army_bonus & Army::FORTIFY) == Army::FORTIFY;
412 }
413 
blessedAtTemple(guint32 temple_id) const414 bool Army::blessedAtTemple(guint32 temple_id) const
415 {
416   unsigned int id = temple_id;
417   if (std::find (d_visitedTemples.begin(), d_visitedTemples.end(), id) ==
418       d_visitedTemples.end())
419     return false;
420 
421   return true;
422 }
423 
getDefendsRuins() const424 bool Army::getDefendsRuins() const
425 {
426   ArmyProto *a = Armysetlist::getInstance()->getArmy(d_armyset, d_type_id);
427   if (a)
428     return a->getDefendsRuins();
429   else
430     return false;
431 }
432 
getAwardable() const433 bool Army::getAwardable() const
434 {
435   ArmyProto *a = Armysetlist::getInstance()->getArmy(d_armyset, d_type_id);
436   if (a)
437     return a->getAwardable();
438   else
439     return false;
440 }
441 
getName() const442 Glib::ustring Army::getName() const
443 {
444   ArmyProto *a = Armysetlist::getInstance()->getArmy(d_armyset, d_type_id);
445   if (a)
446     return a->getName();
447   else
448     return "";
449 }
450 
morph(const ArmyProto * army)451 void Army::morph(const ArmyProto *army)
452 {
453   setStat(Army::STRENGTH, army->getStrength());
454   setStat(Army::MOVES, army->getMaxMoves());
455   setStat(Army::MOVE_BONUS, army->getMoveBonus());
456   setStat(Army::ARMY_BONUS, army->getArmyBonus());
457   d_type_id = army->getId();
458   d_armyset = army->getArmyset();
459 }
460