1 #ifndef _Tech_h_
2 #define _Tech_h_
3 
4 #include "EnumsFwd.h"
5 #include "../util/Export.h"
6 #include "../util/Pending.h"
7 
8 #include <boost/multi_index_container.hpp>
9 #include <boost/multi_index/key_extractors.hpp>
10 #include <boost/multi_index/ordered_index.hpp>
11 #include <boost/algorithm/string/case_conv.hpp>
12 #include <boost/optional/optional.hpp>
13 
14 #include <set>
15 #include <string>
16 #include <vector>
17 #include <map>
18 
19 #include <GG/Clr.h>
20 
21 namespace Effect
22 { class EffectsGroup; }
23 namespace ValueRef {
24     template <typename T>
25     struct ValueRef;
26 }
27 class TechManager;
28 struct UnlockableItem;
29 
30 /** encasulates the data for a single FreeOrion technology */
31 class FO_COMMON_API Tech {
32 public:
33     /** Helper struct for parsing tech definitions */
34     struct TechInfo {
35         TechInfo();
36         TechInfo(const std::string& name_, const std::string& description_,
37                  const std::string& short_description_, const std::string& category_,
38                  std::unique_ptr<ValueRef::ValueRef<double>>&& research_cost_,
39                  std::unique_ptr<ValueRef::ValueRef<int>>&& research_turns_,
40                  bool researchable_,
41                  const std::set<std::string>& tags_);
42         ~TechInfo();
43 
44         std::string                     name;
45         std::string                     description;
46         std::string                     short_description;
47         std::string                     category;
48         std::unique_ptr<ValueRef::ValueRef<double>> research_cost;
49         std::unique_ptr<ValueRef::ValueRef<int>>    research_turns;
50         bool                            researchable;
51         std::set<std::string>           tags;
52     };
53 
54     /** \name Structors */ //@{
55     Tech(const std::string& name, const std::string& description,
56          const std::string& short_description, const std::string& category,
57          std::unique_ptr<ValueRef::ValueRef<double>>&& research_cost,
58          std::unique_ptr<ValueRef::ValueRef<int>>&& research_turns,
59          bool researchable,
60          const std::set<std::string>& tags,
61          const std::vector<std::shared_ptr<Effect::EffectsGroup>>& effects,
62          const std::set<std::string>& prerequisites,
63          const std::vector<UnlockableItem>& unlocked_items,
64          const std::string& graphic);
65 
66     /** basic ctor taking helper struct to reduce number of direct parameters
67       * in order to making parsing work. */
68     Tech(TechInfo& tech_info,
69          std::vector<std::unique_ptr<Effect::EffectsGroup>>&& effects,
70          const std::set<std::string>& prerequisites,
71          const std::vector<UnlockableItem>& unlocked_items,
72          const std::string& graphic);
73 
74     ~Tech();
75     //@}
76 
77     /** \name Accessors */ //@{
Name()78     const std::string&  Name() const                { return m_name; }              //!< returns name of this tech
Description()79     const std::string&  Description() const         { return m_description; }       //!< Returns the text description of this tech
ShortDescription()80     const std::string&  ShortDescription() const    { return m_short_description; } //!< Returns the single-line short text description of this tech
81     std::string         Dump(unsigned short ntabs = 0) const;                                               //!< Returns a text representation of this object
Category()82     const std::string&  Category() const            { return m_category; }          //!< retursn the name of the category to which this tech belongs
83     float               ResearchCost(int empire_id) const;                          //!< returns the total research cost in RPs required to research this tech
84     float               PerTurnCost(int empire_id) const;                           //!< returns the maximum number of RPs per turn allowed to be spent on researching this tech
85     int                 ResearchTime(int empire_id) const;                          //!< returns the number of turns required to research this tech, if ResearchCost() RPs are spent per turn
Researchable()86     bool                Researchable() const        { return m_researchable; }      //!< returns whether this tech is researchable by players and appears on the tech tree
87 
Tags()88     const std::set<std::string>&    Tags() const    { return m_tags; }
89 
90     /** returns the effects that are applied to the discovering empire's capital
91       * when this tech is researched; not all techs have effects, in which case
92       * this returns 0 */
Effects()93     const std::vector<std::shared_ptr<Effect::EffectsGroup>>& Effects() const
94     { return m_effects; }
95 
Prerequisites()96     const std::set<std::string>&    Prerequisites() const { return m_prerequisites; }   //!< returns the set of names of all techs required before this one can be researched
Graphic()97     const std::string&              Graphic() const       { return m_graphic; }         //!< returns the name of the grapic file for this tech
98 
99     //! Returns the set all items that are unlocked by researching this tech
UnlockedItems()100     const std::vector<UnlockableItem>&    UnlockedItems() const
101     { return m_unlocked_items; }
102 
UnlockedTechs()103     const std::set<std::string>&    UnlockedTechs() const { return m_unlocked_techs; }  //!< returns the set of names of all techs for which this one is a prerequisite
104 
105     /** Returns a number, calculated from the contained data, which should be
106       * different for different contained data, and must be the same for
107       * the same contained data, and must be the same on different platforms
108       * and executions of the program and the function. Useful to verify that
109       * the parsed content is consistent without sending it all between
110       * clients and server. */
111     unsigned int                    GetCheckSum() const;
112     //@}
113 
114 private:
115     Tech(const Tech&);                  // disabled
116     const Tech& operator=(const Tech&); // disabled
117     void Init();
118 
119     std::string                     m_name;
120     std::string                     m_description;
121     std::string                     m_short_description;
122     std::string                     m_category;
123     std::unique_ptr<ValueRef::ValueRef<double>> m_research_cost;
124     std::unique_ptr<ValueRef::ValueRef<int>>    m_research_turns;
125     bool                            m_researchable;
126     std::set<std::string>           m_tags;
127     std::vector<std::shared_ptr<Effect::EffectsGroup>> m_effects;
128     std::set<std::string>           m_prerequisites;
129     std::vector<UnlockableItem>     m_unlocked_items;
130     std::string                     m_graphic;
131     std::set<std::string>           m_unlocked_techs;
132 
133     friend class TechManager;
134 };
135 
136 
137 /** specifies a category of techs, with associated \a name, \a graphic (icon), and \a colour.*/
138 struct FO_COMMON_API TechCategory {
TechCategoryTechCategory139     TechCategory() :
140         name(""),
141         graphic(""),
142         colour(GG::Clr(255, 255, 255, 255))
143     {}
TechCategoryTechCategory144     TechCategory(const std::string& name_, const std::string& graphic_,
145                  const GG::Clr& colour_):
146         name(name_),
147         graphic(graphic_),
148         colour(colour_)
149     {}
150     std::string name;       ///< name of category
151     std::string graphic;    ///< icon that represents catetegory
152     GG::Clr     colour;     ///< colour associatied with category
153 };
154 
155 namespace CheckSums {
156     FO_COMMON_API void CheckSumCombine(unsigned int& sum, const TechCategory& cat);
157 }
158 
159 /** holds all FreeOrion techs.  Techs may be looked up by name and by category, and the next researchable techs can be querried,
160     given a set of currently-known techs. */
161 class FO_COMMON_API TechManager {
162 public:
163     struct CategoryIndex {};
164     struct NameIndex {};
165     typedef boost::multi_index_container<
166         std::unique_ptr<Tech>,
167         boost::multi_index::indexed_by<
168             boost::multi_index::ordered_non_unique<
169                 boost::multi_index::tag<CategoryIndex>,
170                 boost::multi_index::const_mem_fun<
171                     Tech,
172                     const std::string&,
173                     &Tech::Category
174                 >
175             >,
176             boost::multi_index::ordered_unique<
177                 boost::multi_index::tag<NameIndex>,
178                 boost::multi_index::const_mem_fun<
179                     Tech,
180                     const std::string&,
181                     &Tech::Name
182                 >
183             >
184         >
185     > TechContainer;
186 
187     using TechCategoryMap = std::map<std::string, std::unique_ptr<TechCategory>>;
188 
189     /** iterator that runs over techs within a category */
190     typedef TechContainer::index<CategoryIndex>::type::const_iterator category_iterator;
191 
192     /** iterator that runs over all techs */
193     typedef TechContainer::index<NameIndex>::type::const_iterator     iterator;
194 
195     /** \name Accessors */ //@{
196     /** returns the tech with the name \a name; you should use the free function GetTech() instead */
197     const Tech*                     GetTech(const std::string& name) const;
198 
199     /** returns the tech category with the name \a name; you should use the free function GetTechCategory() instead */
200     const TechCategory*             GetTechCategory(const std::string& name) const;
201 
202     /** returns the list of category names */
203     std::vector<std::string>        CategoryNames() const;
204 
205     /** returns list of all tech names */
206     std::vector<std::string>        TechNames() const;
207 
208     /** returns list of names of techs in specified category */
209     std::vector<std::string>        TechNames(const std::string& name) const;
210 
211     /** returns all researchable techs */
212     std::vector<const Tech*>        AllNextTechs(const std::set<std::string>& known_techs);
213 
214     /** returns the cheapest researchable tech */
215     const Tech*                     CheapestNextTech(const std::set<std::string>& known_techs, int empire_id);
216 
217     /** returns all researchable techs that progress from the given known techs to the given desired tech */
218     std::vector<const Tech*>        NextTechsTowards(const std::set<std::string>& known_techs,
219                                                      const std::string& desired_tech,
220                                                      int empire_id);
221 
222     /** returns the cheapest researchable tech that progresses from the given known techs to the given desired tech */
223     const Tech*                     CheapestNextTechTowards(const std::set<std::string>& known_techs,
224                                                             const std::string& desired_tech,
225                                                             int empire_id);
226 
227     /** iterator to the first tech */
228     iterator                        begin() const;
229 
230     /** iterator to the last + 1th tech */
231     iterator                        end() const;
232 
233     /** iterator to the first tech in category \a name */
234     category_iterator               category_begin(const std::string& name) const;
235 
236     /** iterator to the last + 1th tech in category \a name */
237     category_iterator               category_end(const std::string& name) const;
238 
239     /** Returns names of indicated tech's prerequisites, and all prereqs of
240       * those techs, etc. recursively. If \a min_required is false then prereqs
241       * will be included and recursed into even if already known to the empire. */
242     std::vector<std::string>        RecursivePrereqs(const std::string& tech_name, int empire_id, bool min_required = true) const;
243 
244     /** Returns a number, calculated from the contained data, which should be
245       * different for different contained data, and must be the same for
246       * the same contained data, and must be the same on different platforms
247       * and executions of the program and the function. Useful to verify that
248       * the parsed content is consistent without sending it all between
249       * clients and server. */
250     unsigned int                    GetCheckSum() const;
251     //@}
252 
253     using TechParseTuple = std::tuple<
254         TechManager::TechContainer, // techs_
255         std::map<std::string, std::unique_ptr<TechCategory>>, // tech_categories,
256         std::set<std::string> // categories_seen
257         >;
258     /** Sets types to the value of \p future. */
259     FO_COMMON_API void SetTechs(Pending::Pending<TechParseTuple>&& future);
260 
261 
262     /** returns the instance of this singleton class; you should use the free function GetTechManager() instead */
263     static TechManager& GetTechManager();
264 
265 private:
266     TechManager();
267 
268     /** Assigns any m_pending_types to m_techs. */
269     void CheckPendingTechs() const;
270 
271     /** returns an error string indicating the first instance of an illegal prerequisite relationship between
272         two techs in m_techs, or an empty string if there are no illegal dependencies  */
273     std::string FindIllegalDependencies() const;
274 
275     /** returns an error string indicating the first prerequisite dependency cycle found in m_techs, or an
276         empty string if there are no dependency cycles */
277     std::string FindFirstDependencyCycle() const;
278 
279     /** returns an error string indicating the first instance of a redundant dependency, or an empty string if there
280         are no redundant dependencies.  An example of a redundant dependency is A --> C, if A --> B and B --> C. */
281     std::string FindRedundantDependency() const;
282 
283     void AllChildren(const Tech* tech, std::map<std::string, std::string>& children) const;
284 
285     /** Future types being parsed by parser.  mutable so that it can
286         be assigned to m_species_types when completed.*/
287     mutable boost::optional<Pending::Pending<TechParseTuple>> m_pending_types = boost::none;
288 
289     mutable TechCategoryMap m_categories;
290     mutable TechContainer   m_techs;
291 
292     static TechManager*     s_instance;
293 };
294 
295 /** returns the singleton tech manager */
296 FO_COMMON_API TechManager& GetTechManager();
297 
298 //! @brief Returns the ::Tech identified by @p name
299 //!
300 //! @param name
301 //! The identifying name of the requested ::Tech.
302 //!
303 //! @return
304 //! A pointer to the ::Tech matching @p name or nullptr if no ::Tech with that
305 //! name was found.
306 FO_COMMON_API const Tech* GetTech(const std::string& name);
307 
308 /** returns a pointer to the tech category with the name \a name, or 0 if no such category exists */
309 FO_COMMON_API const TechCategory* GetTechCategory(const std::string& name);
310 
311 #endif // _Tech_h_
312