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