1 // Copyright (C) 2008, 2010, 2011, 2014, 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 <vector>
19 #include "ItemProto.h"
20 #include "ucompose.hpp"
21 #include "defs.h"
22 #include "maptile.h"
23 #include "playerlist.h"
24 #include "armysetlist.h"
25 #include "armyproto.h"
26 #include "xmlhelper.h"
27 
28 Glib::ustring ItemProto::d_tag = "itemproto";
29 
ItemProto(XML_Helper * helper)30 ItemProto::ItemProto(XML_Helper* helper)
31 	: Renamable (helper)
32 {
33 
34     // Loading of items is a bit complicated, so i'd better loose some words.
35     // In general, items can be loaded from the items description file or
36     // from a savegame. They both differ a bit, more on that when we encounter
37     // such a situation. First, let us deal with the common things.
38 
39     Glib::ustring bonus_str;
40     helper->getData(bonus_str, "bonus");
41     d_bonus = bonusFlagsFromString(bonus_str);
42 
43     d_has_army_type_to_summon = false;
44     d_has_army_type_to_raise = false;
45     d_has_army_type_to_kill = false;
46     d_uses_left = 0;
47     d_steal_gold_percent = 0.0;
48     d_army_type_to_kill = 0;
49     d_army_type_to_summon = 0;
50     d_building_type_to_summon_on = 0;
51     d_percent_armies_to_kill = 0.0;
52     d_mp_to_add = 0;
53     d_army_type_to_raise = 0;
54     d_num_armies_to_raise = 0;
55 
56     if (isUsable())
57       {
58         helper->getData(d_uses_left, "uses_left");
59         if (d_bonus & ItemProto::STEAL_GOLD)
60           helper->getData(d_steal_gold_percent, "steal_gold_percent");
61         if (d_bonus & ItemProto::BANISH_WORMS)
62           {
63             helper->getData(d_army_type_to_kill, "army_type_to_kill");
64             d_has_army_type_to_kill = true;
65           }
66         if (d_bonus & ItemProto::SUMMON_MONSTER)
67           {
68             helper->getData(d_army_type_to_summon, "army_type_to_summon");
69             Glib::ustring str;
70             helper->getData(str, "building_type_to_summon_on");
71             d_building_type_to_summon_on = Maptile::buildingFromString(str);
72             d_has_army_type_to_summon = true;
73           }
74         if (d_bonus & ItemProto::DISEASE_CITY)
75           helper->getData(d_percent_armies_to_kill, "percent_armies_to_kill");
76         if (d_bonus & ItemProto::ADD_2MP_STACK)
77           helper->getData(d_mp_to_add, "mp_to_add");
78         if (d_bonus & ItemProto::RAISE_DEFENDERS)
79           {
80             helper->getData(d_army_type_to_raise, "army_type_to_raise");
81             helper->getData(d_num_armies_to_raise, "num_armies_to_raise");
82             d_has_army_type_to_raise = true;
83           }
84       }
85 }
86 
ItemProto(Glib::ustring name)87 ItemProto::ItemProto(Glib::ustring name)
88 	: Renamable(name)
89 {
90   d_bonus = 0;
91   d_uses_left = 0;
92   d_has_army_type_to_kill = false;
93   d_army_type_to_kill = 0;
94   d_steal_gold_percent = 0.0;
95   d_has_army_type_to_summon = false;
96   d_army_type_to_summon = 0;
97   d_building_type_to_summon_on = 0;
98   d_percent_armies_to_kill = 0.0;
99   d_mp_to_add = 0;
100   d_has_army_type_to_raise = false;
101   d_army_type_to_raise = 0;
102   d_num_armies_to_raise = 0;
103 }
104 
ItemProto(const ItemProto & orig)105 ItemProto::ItemProto(const ItemProto& orig)
106 :Renamable(orig), d_bonus(orig.d_bonus), d_uses_left(orig.d_uses_left),
107     d_army_type_to_kill(orig.d_army_type_to_kill),
108     d_steal_gold_percent(orig.d_steal_gold_percent),
109     d_army_type_to_summon(orig.d_army_type_to_summon),
110     d_building_type_to_summon_on(orig.d_building_type_to_summon_on),
111     d_percent_armies_to_kill(orig.d_percent_armies_to_kill),
112     d_mp_to_add(orig.d_mp_to_add),
113     d_army_type_to_raise(orig.d_army_type_to_raise),
114     d_num_armies_to_raise(orig.d_num_armies_to_raise),
115     d_has_army_type_to_kill (orig.d_has_army_type_to_kill),
116     d_has_army_type_to_summon (orig.d_has_army_type_to_summon),
117     d_has_army_type_to_raise (orig.d_has_army_type_to_raise)
118 {
119 }
120 
saveContents(XML_Helper * helper) const121 bool ItemProto::saveContents(XML_Helper* helper) const
122 {
123   bool retval = true;
124 
125   retval &= helper->saveData("name", getName(false));
126   Glib::ustring bonus_str = bonusFlagsToString(d_bonus);
127   retval &= helper->saveData("bonus", bonus_str);
128   if (isUsable())
129       {
130         retval &= helper->saveData("uses_left", d_uses_left);
131         if (d_bonus & ItemProto::STEAL_GOLD)
132           retval &= helper->saveData("steal_gold_percent",
133                                      d_steal_gold_percent);
134         if (d_bonus & ItemProto::BANISH_WORMS)
135           retval &= helper->saveData("army_type_to_kill", d_army_type_to_kill);
136         if (d_bonus & ItemProto::SUMMON_MONSTER)
137           {
138             retval &= helper->saveData("army_type_to_summon",
139                                        d_army_type_to_summon);
140             Glib::ustring type_str =
141               Maptile::buildingToString
142               (Maptile::Building(d_building_type_to_summon_on));
143             retval &= helper->saveData("building_type_to_summon_on", type_str);
144           }
145         if (d_bonus & ItemProto::DISEASE_CITY)
146           retval &= helper->saveData("percent_armies_to_kill",
147                                      d_percent_armies_to_kill);
148         if (d_bonus & ItemProto::ADD_2MP_STACK)
149           retval &= helper->saveData("mp_to_add", d_mp_to_add);
150         if (d_bonus & ItemProto::RAISE_DEFENDERS)
151           {
152             retval &= helper->saveData("army_type_to_raise",
153                                        d_army_type_to_raise);
154             retval &= helper->saveData("num_armies_to_raise",
155                                        d_num_armies_to_raise);
156           }
157       }
158 
159   return retval;
160 }
161 
save(XML_Helper * helper) const162 bool ItemProto::save(XML_Helper* helper) const
163 {
164   bool retval = true;
165 
166   retval &= helper->openTag(d_tag);
167   retval &= saveContents(helper);
168   retval &= helper->closeTag();
169 
170   return retval;
171 }
172 
getBonus(ItemProto::Bonus bonus) const173 bool ItemProto::getBonus(ItemProto::Bonus bonus) const
174 {
175   return (d_bonus & bonus) == 0 ? false : true;
176 }
177 
addBonus(ItemProto::Bonus bonus)178 void ItemProto::addBonus(ItemProto::Bonus bonus)
179 {
180   d_bonus |= bonus;
181 }
182 
removeBonus(ItemProto::Bonus bonus)183 void ItemProto::removeBonus(ItemProto::Bonus bonus)
184 {
185   d_bonus ^= bonus;
186 }
187 
getBonusDescription() const188 Glib::ustring ItemProto::getBonusDescription() const
189 {
190   guint32 battle = 0;
191   guint32 command = 0;
192   guint32 goldpercity = 0;
193   // the attributes column
194   std::vector<Glib::ustring> s;
195   if (getBonus(ItemProto::ADD1STR))
196     battle++;
197   if (getBonus(ItemProto::ADD2STR))
198     battle+=2;
199   if (getBonus(ItemProto::ADD3STR))
200     battle+=3;
201   if (getBonus(ItemProto::ADD1STACK))
202     command++;
203   if (getBonus(ItemProto::ADD2STACK))
204     command+=2;
205   if (getBonus(ItemProto::ADD3STACK))
206     command+=3;
207   if (getBonus(ItemProto::FLYSTACK))
208     s.push_back(_("Allows Flight"));
209   if (getBonus(ItemProto::DOUBLEMOVESTACK))
210     s.push_back(_("Doubles Movement"));
211   if (getBonus(ItemProto::ADD2GOLDPERCITY))
212     goldpercity+=2;
213   if (getBonus(ItemProto::ADD3GOLDPERCITY))
214     goldpercity+=3;
215   if (getBonus(ItemProto::ADD4GOLDPERCITY))
216     goldpercity+=4;
217   if (getBonus(ItemProto::ADD5GOLDPERCITY))
218     goldpercity+=5;
219   if (getBonus(ItemProto::STEAL_GOLD))
220     s.push_back(_("Steals Gold"));
221   if (getBonus(ItemProto::SINK_SHIPS))
222     s.push_back(_("Sink Ships"));
223   if (getBonus(ItemProto::PICK_UP_BAGS))
224     s.push_back(_("Picks Up Bags"));
225   if (getBonus(ItemProto::ADD_2MP_STACK))
226     s.push_back(_("+2 MP to stack"));
227   if (getBonus(ItemProto::BANISH_WORMS))
228     {
229       ArmyProto *a = Armysetlist::getInstance()->getArmy(Playerlist::getActiveplayer()->getArmyset(), d_army_type_to_kill);
230       s.push_back(String::ucompose(_("Kills all %1"), a->getName()));
231     }
232   if (getBonus(ItemProto::BURN_BRIDGE))
233     s.push_back(_("Destroys a Bridge"));
234   if (getBonus(ItemProto::CAPTURE_KEEPER))
235     s.push_back(_("Removes Monster from Ruin"));
236   if (getBonus(ItemProto::DISEASE_CITY))
237     s.push_back(_("Kills Defenders in a City"));
238   if (getBonus(ItemProto::SUMMON_MONSTER))
239     {
240       ArmyProto *a = Armysetlist::getInstance()->getArmy(Playerlist::getActiveplayer()->getArmyset(), d_army_type_to_summon);
241       if (d_building_type_to_summon_on != Maptile::NONE)
242         s.push_back(String::ucompose(_("Summons %1 at a %2"), a->getName(),
243                                      Maptile::buildingToFriendlyName(d_building_type_to_summon_on)));
244       else
245         s.push_back(String::ucompose(_("Summons %1"), a->getName()));
246     }
247   if (getBonus(ItemProto::RAISE_DEFENDERS))
248     s.push_back(_("Add Defenders to a City"));
249   if (getBonus(ItemProto::PERSUADE_NEUTRALS))
250     s.push_back(_("Take a Neutral City"));
251   if (getBonus(ItemProto::TELEPORT_TO_CITY))
252     s.push_back(_("Teleport Stack to a City"));
253 
254   if (battle > 0)
255     s.push_back(String::ucompose(_("+%1 Battle"), battle));
256   if (command > 0)
257     s.push_back(String::ucompose(_("+%1 Command"), command));
258   if (goldpercity > 0)
259     s.push_back(String::ucompose(_("+%1 Gold per City"), goldpercity));
260 
261   Glib::ustring str;
262   bool first = true;
263   for (std::vector<Glib::ustring>::iterator i = s.begin(), end = s.end();
264        i != end; ++i)
265     {
266       if (first)
267 	first = false;
268       else
269 	str += "\n";
270       str += *i;
271     }
272   return str;
273 }
274 
bonusFlagToString(ItemProto::Bonus bonus)275 Glib::ustring ItemProto::bonusFlagToString(ItemProto::Bonus bonus)
276 {
277   switch (bonus)
278     {
279     case ItemProto::ADD1STR: return "ItemProto::ADD1STR";
280     case ItemProto::ADD2STR: return "ItemProto::ADD2STR";
281     case ItemProto::ADD3STR: return "ItemProto::ADD3STR";
282     case ItemProto::ADD1STACK: return "ItemProto::ADD1STACK";
283     case ItemProto::ADD2STACK: return "ItemProto::ADD2STACK";
284     case ItemProto::ADD3STACK: return "ItemProto::ADD3STACK";
285     case ItemProto::FLYSTACK: return "ItemProto::FLYSTACK";
286     case ItemProto::DOUBLEMOVESTACK: return "ItemProto::DOUBLEMOVESTACK";
287     case ItemProto::ADD2GOLDPERCITY: return "ItemProto::ADD2GOLDPERCITY";
288     case ItemProto::ADD3GOLDPERCITY: return "ADD3GOLDPERCITY";
289     case ItemProto::ADD4GOLDPERCITY: return "ItemProto::ADD4GOLDPERCITY";
290     case ItemProto::ADD5GOLDPERCITY: return "ItemProto::ADD5GOLDPERCITY";
291     case ItemProto::STEAL_GOLD: return "ItemProto::STEAL_GOLD";
292     case ItemProto::SINK_SHIPS: return "ItemProto::SINK_SHIPS";
293     case ItemProto::PICK_UP_BAGS: return "ItemProto::PICK_UP_BAGS";
294     case ItemProto::ADD_2MP_STACK: return "ItemProto::ADD_2MP_STACK";
295     case ItemProto::BANISH_WORMS: return "ItemProto::BANISH_WORMS";
296     case ItemProto::BURN_BRIDGE: return "ItemProto::BURN_BRIDGE";
297     case ItemProto::CAPTURE_KEEPER: return "ItemProto::CAPTURE_KEEPER";
298     case ItemProto::SUMMON_MONSTER: return "ItemProto::SUMMON_MONSTER";
299     case ItemProto::DISEASE_CITY: return "ItemProto::DISEASE_CITY";
300     case ItemProto::RAISE_DEFENDERS: return "ItemProto::RAISE_DEFENDERS";
301     case ItemProto::PERSUADE_NEUTRALS: return "ItemProto::PERSUADE_NEUTRALS";
302     case ItemProto::TELEPORT_TO_CITY: return "ItemProto::TELEPORT_TO_CITY";
303     }
304   return "ItemProto::ADD1STR";
305 }
306 
bonusFlagsToString(guint32 bonus)307 Glib::ustring ItemProto::bonusFlagsToString(guint32 bonus)
308 {
309   Glib::ustring bonuses;
310   if (bonus & ItemProto::ADD1STR)
311     bonuses += " " + bonusFlagToString(ItemProto::ADD1STR);
312   if (bonus & ItemProto::ADD2STR)
313     bonuses += " " + bonusFlagToString(ItemProto::ADD2STR);
314   if (bonus & ItemProto::ADD3STR)
315     bonuses += " " + bonusFlagToString(ItemProto::ADD3STR);
316   if (bonus & ItemProto::ADD1STACK)
317     bonuses += " " + bonusFlagToString(ItemProto::ADD1STACK);
318   if (bonus & ItemProto::ADD2STACK)
319     bonuses += " " + bonusFlagToString(ItemProto::ADD2STACK);
320   if (bonus & ItemProto::ADD3STACK)
321     bonuses += " " + bonusFlagToString(ItemProto::ADD3STACK);
322   if (bonus & ItemProto::FLYSTACK)
323     bonuses += " " + bonusFlagToString(ItemProto::FLYSTACK);
324   if (bonus & ItemProto::DOUBLEMOVESTACK)
325     bonuses += " " + bonusFlagToString(ItemProto::DOUBLEMOVESTACK);
326   if (bonus & ItemProto::ADD2GOLDPERCITY)
327     bonuses += " " + bonusFlagToString(ItemProto::ADD2GOLDPERCITY);
328   if (bonus & ItemProto::ADD3GOLDPERCITY)
329     bonuses += " " + bonusFlagToString(ItemProto::ADD3GOLDPERCITY);
330   if (bonus & ItemProto::ADD4GOLDPERCITY)
331     bonuses += " " + bonusFlagToString(ItemProto::ADD4GOLDPERCITY);
332   if (bonus & ItemProto::ADD5GOLDPERCITY)
333     bonuses += " " + bonusFlagToString(ItemProto::ADD5GOLDPERCITY);
334   if (bonus & ItemProto::STEAL_GOLD)
335     bonuses += " " + bonusFlagToString(ItemProto::STEAL_GOLD);
336   if (bonus & ItemProto::SINK_SHIPS)
337     bonuses += " " + bonusFlagToString(ItemProto::SINK_SHIPS);
338   if (bonus & ItemProto::PICK_UP_BAGS)
339     bonuses += " " + bonusFlagToString(ItemProto::PICK_UP_BAGS);
340   if (bonus & ItemProto::ADD_2MP_STACK)
341     bonuses += " " + bonusFlagToString(ItemProto::ADD_2MP_STACK);
342   if (bonus & ItemProto::BANISH_WORMS)
343     bonuses += " " + bonusFlagToString(ItemProto::BANISH_WORMS);
344   if (bonus & ItemProto::BURN_BRIDGE)
345     bonuses += " " + bonusFlagToString(ItemProto::BURN_BRIDGE);
346   if (bonus & ItemProto::CAPTURE_KEEPER)
347     bonuses += " " + bonusFlagToString(ItemProto::CAPTURE_KEEPER);
348   if (bonus & ItemProto::SUMMON_MONSTER)
349     bonuses += " " + bonusFlagToString(ItemProto::SUMMON_MONSTER);
350   if (bonus & ItemProto::DISEASE_CITY)
351     bonuses += " " + bonusFlagToString(ItemProto::DISEASE_CITY);
352   if (bonus & ItemProto::RAISE_DEFENDERS)
353     bonuses += " " + bonusFlagToString(ItemProto::RAISE_DEFENDERS);
354   if (bonus & ItemProto::PERSUADE_NEUTRALS)
355     bonuses += " " + bonusFlagToString(ItemProto::PERSUADE_NEUTRALS);
356   if (bonus & ItemProto::TELEPORT_TO_CITY)
357     bonuses += " " + bonusFlagToString(ItemProto::TELEPORT_TO_CITY);
358   return bonuses;
359 }
360 
bonusFlagsFromString(Glib::ustring str)361 guint32 ItemProto::bonusFlagsFromString(Glib::ustring str)
362 {
363   return XML_Helper::flagsFromString(str, bonusFlagFromString);
364 }
365 
bonusFlagFromString(Glib::ustring str)366 guint32 ItemProto::bonusFlagFromString(Glib::ustring str)
367 {
368   if (str.size() > 0 && isdigit(str.c_str()[0]))
369     return ItemProto::Bonus(atoi(str.c_str()));
370   if (str == "ItemProto::ADD1STR") return ItemProto::ADD1STR;
371   else if (str == "ItemProto::ADD2STR") return ItemProto::ADD2STR;
372   else if (str == "ItemProto::ADD3STR") return ItemProto::ADD3STR;
373   else if (str == "ItemProto::ADD1STACK") return ItemProto::ADD1STACK;
374   else if (str == "ItemProto::ADD2STACK") return ItemProto::ADD2STACK;
375   else if (str == "ItemProto::ADD3STACK") return ItemProto::ADD3STACK;
376   else if (str == "ItemProto::FLYSTACK") return ItemProto::FLYSTACK;
377   else if (str == "ItemProto::DOUBLEMOVESTACK") return ItemProto::DOUBLEMOVESTACK;
378   else if (str == "ItemProto::ADD2GOLDPERCITY") return ItemProto::ADD2GOLDPERCITY;
379   else if (str == "ItemProto::ADD3GOLDPERCITY") return ItemProto::ADD3GOLDPERCITY;
380   else if (str == "ItemProto::ADD4GOLDPERCITY") return ItemProto::ADD4GOLDPERCITY;
381   else if (str == "ItemProto::ADD5GOLDPERCITY") return ItemProto::ADD5GOLDPERCITY;
382   else if (str == "ItemProto::STEAL_GOLD") return ItemProto::STEAL_GOLD;
383   else if (str == "ItemProto::SINK_SHIPS") return ItemProto::SINK_SHIPS;
384   else if (str == "ItemProto::PICK_UP_BAGS") return ItemProto::PICK_UP_BAGS;
385   else if (str == "ItemProto::ADD_2MP_STACK") return ItemProto::ADD_2MP_STACK;
386   else if (str == "ItemProto::BANISH_WORMS") return ItemProto::BANISH_WORMS;
387   else if (str == "ItemProto::BURN_BRIDGE") return ItemProto::BURN_BRIDGE;
388   else if (str == "ItemProto::CAPTURE_KEEPER") return ItemProto::CAPTURE_KEEPER;
389   else if (str == "ItemProto::SUMMON_MONSTER") return ItemProto::SUMMON_MONSTER;
390   else if (str == "ItemProto::DISEASE_CITY") return ItemProto::DISEASE_CITY;
391   else if (str == "ItemProto::RAISE_DEFENDERS") return ItemProto::RAISE_DEFENDERS;
392   else if (str == "ItemProto::PERSUADE_NEUTRALS") return ItemProto::PERSUADE_NEUTRALS;
393   else if (str == "ItemProto::TELEPORT_TO_CITY") return ItemProto::TELEPORT_TO_CITY;
394   return ItemProto::ADD1STR;
395 }
396 
isCurrentlyUsable(guint32 building,bool bags_on_map,bool victims_left,bool ruin_has_occupant,bool friendly_cities_present,bool enemy_cities_present,bool neutral_cities_present)397 bool ItemProto::isCurrentlyUsable(guint32 building, bool bags_on_map, bool victims_left, bool ruin_has_occupant, bool friendly_cities_present, bool enemy_cities_present, bool neutral_cities_present)
398 {
399   bool usable = false;
400   if (d_bonus & ItemProto::BURN_BRIDGE && building == Maptile::BRIDGE)
401     usable = true;
402   if (d_bonus & ItemProto::SUMMON_MONSTER)
403     {
404       if (getBuildingTypeToSummonOn() == Maptile::NONE ||
405           getBuildingTypeToSummonOn() == building)
406         usable = true;
407     }
408   if (d_bonus & ItemProto::PICK_UP_BAGS && bags_on_map)
409     usable = true;
410   if (d_bonus & ItemProto::CAPTURE_KEEPER && building == Maptile::RUIN &&
411       ruin_has_occupant)
412     usable = true;
413 
414   if (usableOnVictimPlayer() && victims_left)
415     usable = true;
416   if (usableOnFriendlyCity() && friendly_cities_present)
417     usable = true;
418   if (usableOnEnemyCity() && enemy_cities_present)
419     usable = true;
420   if (usableOnNeutralCity() && neutral_cities_present)
421     usable = true;
422   if (usableOnAnyCity())
423     usable = true;
424 
425   return usable;
426 }
427