1 #ifndef _ShipDesign_h_
2 #define _ShipDesign_h_
3 
4 
5 #include <boost/functional/hash.hpp>
6 #include <boost/optional/optional.hpp>
7 #include <boost/serialization/access.hpp>
8 #include <boost/uuid/nil_generator.hpp>
9 #include <boost/uuid/uuid.hpp>
10 #include "EnumsFwd.h"
11 #include "../util/Pending.h"
12 
13 
14 FO_COMMON_API extern const int INVALID_OBJECT_ID;
15 FO_COMMON_API extern const int INVALID_DESIGN_ID;
16 FO_COMMON_API extern const int ALL_EMPIRES;
17 FO_COMMON_API extern const int INVALID_GAME_TURN;
18 
19 /** ParsedShipDesign holds the results of a parsed ship design which can be
20     converted to a ShipDesign. */
21 struct FO_COMMON_API ParsedShipDesign {
22     ParsedShipDesign(const std::string& name, const std::string& description,
23                      int designed_on_turn, int designed_by_empire, const std::string& hull,
24                      const std::vector<std::string>& parts,
25                      const std::string& icon, const std::string& model,
26                      bool name_desc_in_stringtable = false, bool monster = false,
27                      const boost::uuids::uuid& uuid = boost::uuids::nil_uuid());
28 
29     std::string                 m_name;
30     std::string                 m_description;
31     boost::uuids::uuid          m_uuid;
32 
33     int                         m_designed_on_turn = INVALID_GAME_TURN;
34     int                         m_designed_by_empire = ALL_EMPIRES;
35 
36     std::string                 m_hull;
37     std::vector<std::string>    m_parts;
38     bool                        m_is_monster = false;
39 
40     std::string                 m_icon;
41     std::string                 m_3D_model;
42 
43     bool                        m_name_desc_in_stringtable = false;
44 };
45 
46 class FO_COMMON_API ShipDesign {
47 public:
48     /** \name Structors */ //@{
49 private:
50     /** The ShipDesign() constructor constructs invalid designs and is only used by boost
51         serialization. */
52     ShipDesign();
53 public:
54     /** The public ShipDesign constructor will only construct valid ship
55         designs, as long as the ShipHullManager has at least one hull.
56 
57         If \p should_throw is not boost::none and the passed in parameters (\p
58         hull and \p parts) would result in an invalid design it generates an
59         explicit log message showing the FOCS corresponding to the passed in
60         parameters and the FOCS corresponding to a corrected valid design and
61         then throws std::invalid_argument.  This can be used to test design
62         validity and provide an explcit log message.
63 
64         should_throw is not used but it is a literal reminder that
65         std::invalid_argument should be caught.
66 
67         If \p should_throw is boost::none it will correct the errors in
68         parameters, print a log message and return a valid ship design.  The
69         only exception is if there are no ship hull in the HullManager.  Then
70         the constructor will not throw, but will return an invalid ship design
71         with a empty "" hull.
72     */
73     ShipDesign(const boost::optional<std::invalid_argument>& should_throw,
74                const std::string& name, const std::string& description,
75                int designed_on_turn, int designed_by_empire, const std::string& hull,
76                const std::vector<std::string>& parts,
77                const std::string& icon, const std::string& model,
78                bool name_desc_in_stringtable = false, bool monster = false,
79                const boost::uuids::uuid& uuid = boost::uuids::nil_uuid());
80 
81     /** Convert a parsed ship design and do any required verification. */
82     ShipDesign(const ParsedShipDesign& design);
83     //@}
84 
85     /** \name Accessors */ //@{
ID()86     int                             ID() const  { return m_id; }    ///< returns id number of design
87     /** returns name of design.  if \a stringtable_lookup is true and the
88       * design was constructed specifying name_desc_in_stringtable true,
89       * the name string is looked up in the stringtable before being returned.
90       * otherwise, the raw name string is returned. */
91     const std::string&              Name(bool stringtable_lookup = true) const;
92     void                            SetName(const std::string& name);
93 
94     /** Return the UUID. */
UUID()95     boost::uuids::uuid              UUID() const { return m_uuid; }
96 
97     /** returns description of design.  if \a stringtable_lookup is true and
98       * the design was constructed specifying name_desc_in_stringtable true,
99       * the description string is looked up in the stringtable before being
100       * returned.  otherwise, the raw name string is returned. */
101     const std::string&              Description(bool stringtable_lookup = true) const;
102     void                            SetDescription(const std::string& description);
103 
DesignedOnTurn()104     int                             DesignedOnTurn() const  { return m_designed_on_turn; };     ///< returns turn on which design was created
DesignedByEmpire()105     int                             DesignedByEmpire() const { return m_designed_by_empire; };  ///< returns id of empire that created this design
106 
107     bool                            ProductionCostTimeLocationInvariant() const;                ///< returns true if the production cost and time are invariant (does not depend on) the location
108     float                           ProductionCost(int empire_id, int location_id) const;       ///< returns the total cost to build a ship of this design
109     float                           PerTurnCost(int empire_id, int location_id) const;          ///< returns the maximum per-turn number of production points that can be spent on building a ship of this design
110     int                             ProductionTime(int empire_id, int location_id) const;       ///< returns the time in turns it takes to build a ship of this design
Producible()111     bool                            Producible() const      { return m_producible; }            ///< returns whether this design is producible by players and appears on the production screen list
112 
Speed()113     float                           Speed() const           { return m_speed; }                 ///< returns design speed along starlanes
114 
Structure()115     float                           Structure() const       { return m_structure; }             ///< returns the max structure of this design
Shields()116     float                           Shields() const         { return m_shields; }               ///< returns the max shields of this design
Fuel()117     float                           Fuel() const            { return m_fuel; }                  ///< returns the max fuel capacity of this design
Detection()118     float                           Detection() const       { return m_detection; }             ///< returns the detection ability of this design
ColonyCapacity()119     float                           ColonyCapacity() const  { return m_colony_capacity; }       ///< returns the colonization capacity of this design
TroopCapacity()120     float                           TroopCapacity() const   { return m_troop_capacity; }        ///< returns the troop capacity of this design
Stealth()121     float                           Stealth() const         { return m_stealth; }               ///< returns the stealth of this design
122 
IndustryGeneration()123     float                           IndustryGeneration() const  { return m_industry_generation; }   ///< returns the industry output from this ship design
ResearchGeneration()124     float                           ResearchGeneration() const  { return m_research_generation; }   ///< returns the research output from this ship design
TradeGeneration()125     float                           TradeGeneration() const     { return m_trade_generation; }      ///< returns the trade output from this ship design
IsProductionLocation()126     bool                            IsProductionLocation() const{ return m_is_production_location;} ///< returns true if this ship design can be a production location
127 
128     bool                            CanColonize() const;
HasTroops()129     bool                            HasTroops() const       { return (m_troop_capacity > 0.0f); }
CanBombard()130     bool                            CanBombard() const      { return m_can_bombard; }
IsArmed()131     bool                            IsArmed() const         { return m_is_armed; }
HasDirectWeapons()132     bool                            HasDirectWeapons() const{ return m_has_direct_weapons; }
HasFighters()133     bool                            HasFighters() const     { return m_has_fighters; }
IsMonster()134     bool                            IsMonster() const       { return m_is_monster; }
135 
136     float                           Attack() const;
137     float                           AdjustedAttack(float shield) const;
138     float                           Defense() const;
139 
Hull()140     const std::string&              Hull() const            { return m_hull; }      ///< returns name of hull on which design is based
141 
Parts()142     const std::vector<std::string>& Parts() const           { return m_parts; }     ///< returns vector of names of all parts in this design, with position in vector corresponding to slot positions
143     std::vector<std::string>        Parts(ShipSlotType slot_type) const;            ///< returns vector of names of parts in slots of indicated type in this design, unrelated to slot positions
144     std::vector<std::string>        Weapons() const;                                ///< returns vector of names of weapon parts in, unrelated to slot positions
145 
Icon()146     const std::string&              Icon() const            { return m_icon; }      ///< returns filename for small-size icon graphic for design
Model()147     const std::string&              Model() const           { return m_3D_model; }  ///< returns filename of 3D model that represents ships of design
LookupInStringtable()148     bool                            LookupInStringtable() const { return m_name_desc_in_stringtable; }
149 
150     //! Returns number of parts in this ship design, indexed by ShipPart name
ShipPartCount()151     const std::map<std::string, int>&   ShipPartCount() const { return m_num_ship_parts; }
152     int                                 PartCount() const;
153 
154     /** returns number of parts in this ship design, indexed by ShipPartClass */
PartClassCount()155     const std::map<ShipPartClass, int>&   PartClassCount() const { return m_num_part_classes; }
156 
157     std::string                     Dump(unsigned short ntabs = 0) const;                                   ///< returns a data file format representation of this object
158 
159     /** Returns a number, calculated from the contained data, which should be
160       * different for different contained data, and must be the same for
161       * the same contained data, and must be the same on different platforms
162       * and executions of the program and the function. Useful to verify that
163       * the parsed content is consistent without sending it all between
164       * clients and server. */
165     unsigned int                    GetCheckSum() const;
166 
167     friend FO_COMMON_API bool operator ==(const ShipDesign& first, const ShipDesign& second);
168     //@}
169 
170     bool                            ProductionLocation(int empire_id, int location_id) const;   ///< returns true iff the empire with ID empire_id can produce this design at the location with location_id
171 
172     /** \name Mutators */ //@{
173     void                            SetID(int id);                          ///< sets the ID number of the design to \a id .  Should only be used by Universe class when inserting new design into Universe.
174     /** Set the UUID. */
175     void                            SetUUID(const boost::uuids::uuid& uuid);
Rename(const std::string & name)176     void                            Rename(const std::string& name) { m_name = name; }  ///< renames this design to \a name
SetMonster(const bool is_monster)177     void                            SetMonster(const bool is_monster) {m_is_monster = is_monster; }
178     //@}
179 
180     /** Return true if \p hull and \p parts would make a valid design. */
181     static bool ValidDesign(const std::string& hull, const std::vector<std::string>& parts);
182 private:
183     /** Return a valid hull and parts pair iff the \p hull and \p parts vectors
184         would not make a valid ShipDesign.
185         Also pad parts with "" if it is shorter than the \p hull number of slots.
186         Otherwise return none. If \p produce_log is true then produce log messages. */
187     static boost::optional<std::pair<std::string, std::vector<std::string>>>
188         MaybeInvalidDesign(const std::string& hull, std::vector<std::string>& parts, bool produce_log);
189 
190     /** Force design invariants to be true. If design invariants are not begin
191         met and \p produce_log is true provide an explicit log message about how it
192         was corrected and throw std::invalid_argument if \p should_throw is not
193         none.
194 
195         \p should_throw is not used but it is a literal reminder that
196         std::invalid_argument should be caught.
197     */
198     void ForceValidDesignOrThrow(const boost::optional<std::invalid_argument>& should_throw, bool produce_log);
199 
200     void BuildStatCaches();
201 
202     int                         m_id = INVALID_OBJECT_ID;
203 
204     std::string                 m_name;
205     std::string                 m_description;
206     boost::uuids::uuid          m_uuid;
207 
208     int                         m_designed_on_turn = INVALID_GAME_TURN;
209     int                         m_designed_by_empire = ALL_EMPIRES;
210 
211     std::string                 m_hull;
212     std::vector<std::string>    m_parts;
213     bool                        m_is_monster = false;
214 
215     std::string                 m_icon;
216     std::string                 m_3D_model;
217 
218     bool                        m_name_desc_in_stringtable = false;
219 
220     // Note that these are fine to compute on demand and cache here -- it is
221     // not necessary to serialize them.
222     bool    m_has_direct_weapons = false;
223     bool    m_has_fighters = false;
224     bool    m_is_armed = false;
225     bool    m_can_bombard = false;
226     float   m_detection = 0.0f;
227     float   m_colony_capacity = 0.0f;
228     float   m_troop_capacity = 0.0f;
229     float   m_stealth = 0.0f;
230     float   m_fuel = 0.0f;
231     float   m_shields = 0.0f;
232     float   m_structure = 0.0f;
233     float   m_speed = 0.0f;
234     float   m_research_generation = 0.0f;
235     float   m_industry_generation = 0.0f;
236     float   m_trade_generation = 0.0f;
237     bool    m_is_production_location = false;
238     std::map<std::string, int>      m_num_ship_parts;
239     std::map<ShipPartClass, int>    m_num_part_classes;
240     bool    m_producible = false;
241 
242     friend class boost::serialization::access;
243     template <typename Archive>
244     void serialize(Archive& ar, const unsigned int version);
245 };
246 
247 ///< Returns true if the two designs have the same hull and parts.
248 FO_COMMON_API bool operator ==(const ShipDesign& first, const ShipDesign& second);
249 
250 /** Returns the ShipDesign specification object with id \a ship_design_id.  If
251   * no such ShipDesign is present in the Universe (because it doesn't exist,
252   * or isn't know to this client), 0 is returned instead. */
253 FO_COMMON_API const ShipDesign* GetShipDesign(int ship_design_id);
254 
255 class FO_COMMON_API PredefinedShipDesignManager {
256 public:
257     using ParsedShipDesignsType = std::pair<
258         std::vector<std::pair<std::unique_ptr<ParsedShipDesign>, boost::filesystem::path>>, // designs_and_paths,
259         std::vector<boost::uuids::uuid> // ordering
260     >;
261 
262     /** Return pointers the ShipDesigns in order.*/
263     std::vector<const ShipDesign*> GetOrderedShipDesigns() const;
264 
265     /** Return pointers the ShipDesigns in order.*/
266     std::vector<const ShipDesign*> GetOrderedMonsterDesigns() const;
267 
268     /** Returns the ID for the design in the Universe for the predefined design
269       * with the specified \a name.  If there is generic design available for
270       * the specified \a name, then INVALID_DESIGN_ID is returned. */
271     int                 GetDesignID(const std::string& name) const;
272 
273     /** Returns a number, calculated from the contained data, which should be
274       * different for different contained data, and must be the same for
275       * the same contained data, and must be the same on different platforms
276       * and executions of the program and the function. Useful to verify that
277       * the parsed content is consistent without sending it all between
278       * clients and server. */
279     unsigned int        GetCheckSum() const;
280     //@}
281 
282     /** Adds designs in this manager to the universe with the design creator
283       * left as no empire. */
284     void AddShipDesignsToUniverse() const;
285 
286     /** Returns the predefined ShipDesign with the name \a name.  If no such
287       * ship design exists, 0 is returned instead. */
288     static PredefinedShipDesignManager& GetPredefinedShipDesignManager();
289 
290     /** Sets ship design types to the future value of \p pending_designs
291         found in \p subdir. */
292     FO_COMMON_API void SetShipDesignTypes(Pending::Pending<ParsedShipDesignsType>&& pending_designs);
293 
294     /** Sets monster design types to the future value of \p
295         pending_design_types found in \p subdir. */
296     FO_COMMON_API void SetMonsterDesignTypes(Pending::Pending<ParsedShipDesignsType>&& pending_designs);
297 
298 private:
299     PredefinedShipDesignManager();
300 
301     /** Assigns any m_pending_designs. */
302     void CheckPendingDesignsTypes() const;
303 
304     /** Future ship design type being parsed by parser.  mutable so that it can
305         be assigned to m_ship design_types when completed.*/
306     mutable boost::optional<Pending::Pending<ParsedShipDesignsType>> m_pending_designs = boost::none;
307     mutable boost::optional<Pending::Pending<ParsedShipDesignsType>> m_pending_monsters = boost::none;
308 
309     mutable std::unordered_map<boost::uuids::uuid, std::unique_ptr<ShipDesign>,
310                                boost::hash<boost::uuids::uuid>>  m_designs;
311 
312     mutable std::unordered_map<std::string, boost::uuids::uuid> m_name_to_ship_design;
313     mutable std::unordered_map<std::string, boost::uuids::uuid> m_name_to_monster_design;
314     // ids of designs from this manager that have been added to the universe with no empire as the creator
315     mutable std::unordered_map<std::string, int> m_design_generic_ids;
316 
317     mutable std::vector<boost::uuids::uuid> m_ship_ordering;
318     mutable std::vector<boost::uuids::uuid> m_monster_ordering;
319 
320     static PredefinedShipDesignManager* s_instance;
321 };
322 
323 /** returns the singleton predefined ship design manager type manager */
324 FO_COMMON_API PredefinedShipDesignManager& GetPredefinedShipDesignManager();
325 
326 /** Returns the predefined ShipDesign with the name \a name.  If no such
327   * ship design exists, 0 is returned instead. */
328 FO_COMMON_API const ShipDesign* GetPredefinedShipDesign(const std::string& name);
329 
330 
331 /** Load all ship designs in \p parsed and return a tuple is_error, the map
332     from uuid to ship design and path and the ship ordering from the
333     manifest. */
334 FO_COMMON_API std::tuple<
335     bool,
336     std::unordered_map<boost::uuids::uuid,
337         std::pair<std::unique_ptr<ShipDesign>, boost::filesystem::path>,
338         boost::hash<boost::uuids::uuid>>,
339     std::vector<boost::uuids::uuid>>
340 LoadShipDesignsAndManifestOrderFromParseResults(PredefinedShipDesignManager::ParsedShipDesignsType& parsed);
341 
342 #endif // _ShipDesign_h_
343