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