1 #include "BuildingType.h"
2 
3 #include <boost/algorithm/string/case_conv.hpp>
4 #include "../util/CheckSums.h"
5 #include "../util/GameRules.h"
6 #include "../util/Logger.h"
7 #include "../Empire/EmpireManager.h"
8 #include "Condition.h"
9 #include "Effect.h"
10 #include "ValueRef.h"
11 
12 
13 namespace {
AddRules(GameRules & rules)14     void AddRules(GameRules& rules) {
15         // makes all buildings cost 1 PP and take 1 turn to produce
16         rules.Add<bool>("RULE_CHEAP_AND_FAST_BUILDING_PRODUCTION",
17                         "RULE_CHEAP_AND_FAST_BUILDING_PRODUCTION_DESC",
18                         "", false, true);
19     }
20     bool temp_bool = RegisterGameRules(&AddRules);
21 }
22 
23 
BuildingType(const std::string & name,const std::string & description,CommonParams & common_params,CaptureResult capture_result,const std::string & icon)24 BuildingType::BuildingType(const std::string& name,
25                            const std::string& description,
26                            CommonParams& common_params,
27                            CaptureResult capture_result,
28                            const std::string& icon) :
29     m_name(name),
30     m_description(description),
31     m_production_cost(std::move(common_params.production_cost)),
32     m_production_time(std::move(common_params.production_time)),
33     m_producible(common_params.producible),
34     m_capture_result(capture_result),
35     m_production_meter_consumption(std::move(common_params.production_meter_consumption)),
36     m_production_special_consumption(std::move(common_params.production_special_consumption)),
37     m_location(std::move(common_params.location)),
38     m_enqueue_location(std::move(common_params.enqueue_location)),
39     m_icon(icon)
40 {
41     for (auto&& effect : common_params.effects)
42         m_effects.emplace_back(std::move(effect));
43     Init();
44     for (const std::string& tag : common_params.tags)
45         m_tags.insert(boost::to_upper_copy<std::string>(tag));
46 }
47 
~BuildingType()48 BuildingType::~BuildingType()
49 {}
50 
Init()51 void BuildingType::Init() {
52     if (m_production_cost)
53         m_production_cost->SetTopLevelContent(m_name);
54     if (m_production_time)
55         m_production_time->SetTopLevelContent(m_name);
56     if (m_location)
57         m_location->SetTopLevelContent(m_name);
58     if (m_enqueue_location)
59         m_enqueue_location->SetTopLevelContent(m_name);
60     for (auto& effect : m_effects)
61         effect->SetTopLevelContent(m_name);
62 }
63 
Dump(unsigned short ntabs) const64 std::string BuildingType::Dump(unsigned short ntabs) const {
65     std::string retval = DumpIndent(ntabs) + "BuildingType\n";
66     retval += DumpIndent(ntabs+1) + "name = \"" + m_name + "\"\n";
67     retval += DumpIndent(ntabs+1) + "description = \"" + m_description + "\"\n";
68     if (m_production_cost)
69         retval += DumpIndent(ntabs+1) + "buildcost = " + m_production_cost->Dump(ntabs+1) + "\n";
70     if (m_production_time)
71         retval += DumpIndent(ntabs+1) + "buildtime = " + m_production_time->Dump(ntabs+1) + "\n";
72     retval += DumpIndent(ntabs+1) + (m_producible ? "Producible" : "Unproducible") + "\n";
73     retval += DumpIndent(ntabs+1) + "captureresult = " +
74         boost::lexical_cast<std::string>(m_capture_result) + "\n";
75 
76     if (!m_tags.empty()) {
77         if (m_tags.size() == 1) {
78             retval += DumpIndent(ntabs+1) + "tags = \"" + *m_tags.begin() + "\"\n";
79         } else {
80             retval += DumpIndent(ntabs+1) + "tags = [ ";
81             for (const std::string& tag : m_tags)
82                retval += "\"" + tag + "\" ";
83             retval += " ]\n";
84         }
85     }
86 
87     if (m_location) {
88         retval += DumpIndent(ntabs+1) + "location = \n";
89         retval += m_location->Dump(ntabs+2);
90     }
91     if (m_enqueue_location) {
92         retval += DumpIndent(ntabs+1) + "enqueue location = \n";
93         retval += m_enqueue_location->Dump(ntabs+2);
94     }
95 
96     if (m_effects.size() == 1) {
97         retval += DumpIndent(ntabs+1) + "effectsgroups =\n";
98         retval += m_effects[0]->Dump(ntabs+2);
99     } else {
100         retval += DumpIndent(ntabs+1) + "effectsgroups = [\n";
101         for (auto& effect : m_effects)
102             retval += effect->Dump(ntabs+2);
103         retval += DumpIndent(ntabs+1) + "]\n";
104     }
105     retval += DumpIndent(ntabs+1) + "icon = \"" + m_icon + "\"\n";
106     return retval;
107 }
108 
ProductionCostTimeLocationInvariant() const109 bool BuildingType::ProductionCostTimeLocationInvariant() const {
110     // if rule is active, then scripted costs and times are ignored and actual costs are invariant
111     if (GetGameRules().Get<bool>("RULE_CHEAP_AND_FAST_BUILDING_PRODUCTION"))
112         return true;
113 
114     // if cost or time are specified and not invariant, result is non-invariance
115     if (m_production_cost && !(m_production_cost->TargetInvariant() && m_production_cost->SourceInvariant()))
116         return false;
117     if (m_production_time && !(m_production_time->TargetInvariant() && m_production_time->SourceInvariant()))
118         return false;
119     // if both cost and time are not specified, result is invariance
120     return true;
121 }
122 
ProductionCost(int empire_id,int location_id) const123 float BuildingType::ProductionCost(int empire_id, int location_id) const {
124     if (GetGameRules().Get<bool>("RULE_CHEAP_AND_FAST_BUILDING_PRODUCTION") || !m_production_cost)
125         return 1.0f;
126 
127     if (m_production_cost->ConstantExpr())
128         return m_production_cost->Eval();
129     else if (m_production_cost->SourceInvariant() && m_production_cost->TargetInvariant())
130         return m_production_cost->Eval();
131 
132     const auto ARBITRARY_LARGE_COST = 999999.9f;
133 
134     auto location = Objects().get(location_id);
135     if (!location && !m_production_cost->TargetInvariant())
136         return ARBITRARY_LARGE_COST;
137 
138     auto source = Empires().GetSource(empire_id);
139     if (!source && !m_production_cost->SourceInvariant())
140         return ARBITRARY_LARGE_COST;
141 
142     ScriptingContext context(source, location);
143 
144     return m_production_cost->Eval(context);
145 }
146 
PerTurnCost(int empire_id,int location_id) const147 float BuildingType::PerTurnCost(int empire_id, int location_id) const
148 { return ProductionCost(empire_id, location_id) / std::max(1, ProductionTime(empire_id, location_id)); }
149 
ProductionTime(int empire_id,int location_id) const150 int BuildingType::ProductionTime(int empire_id, int location_id) const {
151     if (GetGameRules().Get<bool>("RULE_CHEAP_AND_FAST_BUILDING_PRODUCTION") || !m_production_time)
152         return 1;
153 
154     if (m_production_time->ConstantExpr())
155         return m_production_time->Eval();
156     else if (m_production_time->SourceInvariant() && m_production_time->TargetInvariant())
157         return m_production_time->Eval();
158 
159     const int ARBITRARY_LARGE_TURNS = 9999;
160 
161     auto location = Objects().get(location_id);
162     if (!location && !m_production_time->TargetInvariant())
163         return ARBITRARY_LARGE_TURNS;
164 
165     auto source = Empires().GetSource(empire_id);
166     if (!source && !m_production_time->SourceInvariant())
167         return ARBITRARY_LARGE_TURNS;
168 
169     ScriptingContext context(source, location);
170 
171     return m_production_time->Eval(context);
172 }
173 
ProductionLocation(int empire_id,int location_id) const174 bool BuildingType::ProductionLocation(int empire_id, int location_id) const {
175     if (!m_location)
176         return true;
177 
178     auto location = Objects().get(location_id);
179     if (!location)
180         return false;
181 
182     auto source = Empires().GetSource(empire_id);
183     if (!source)
184         return false;
185 
186     return m_location->Eval(ScriptingContext(source), location);
187 }
188 
EnqueueLocation(int empire_id,int location_id) const189 bool BuildingType::EnqueueLocation(int empire_id, int location_id) const {
190     if (!m_enqueue_location)
191         return true;
192 
193     auto location = Objects().get(location_id);
194     if (!location)
195         return false;
196 
197     auto source = Empires().GetSource(empire_id);
198     if (!source)
199         return false;
200 
201     return m_enqueue_location->Eval(ScriptingContext(source), location);
202 }
203 
GetCheckSum() const204 unsigned int BuildingType::GetCheckSum() const {
205     unsigned int retval{0};
206 
207     CheckSums::CheckSumCombine(retval, m_name);
208     CheckSums::CheckSumCombine(retval, m_description);
209     CheckSums::CheckSumCombine(retval, m_production_cost);
210     CheckSums::CheckSumCombine(retval, m_production_time);
211     CheckSums::CheckSumCombine(retval, m_producible);
212     CheckSums::CheckSumCombine(retval, m_capture_result);
213     CheckSums::CheckSumCombine(retval, m_tags);
214     CheckSums::CheckSumCombine(retval, m_production_meter_consumption);
215     CheckSums::CheckSumCombine(retval, m_production_special_consumption);
216     CheckSums::CheckSumCombine(retval, m_location);
217     CheckSums::CheckSumCombine(retval, m_enqueue_location);
218     CheckSums::CheckSumCombine(retval, m_effects);
219     CheckSums::CheckSumCombine(retval, m_icon);
220 
221     return retval;
222 }
223 
224 
225 BuildingTypeManager* BuildingTypeManager::s_instance = nullptr;
226 
BuildingTypeManager()227 BuildingTypeManager::BuildingTypeManager() {
228     if (s_instance)
229         throw std::runtime_error("Attempted to create more than one BuildingTypeManager.");
230 
231     // Only update the global pointer on sucessful construction.
232     s_instance = this;
233 }
234 
GetBuildingType(const std::string & name) const235 const BuildingType* BuildingTypeManager::GetBuildingType(const std::string& name) const {
236     CheckPendingBuildingTypes();
237     const auto& it = m_building_types.find(name);
238     return it != m_building_types.end() ? it->second.get() : nullptr;
239 }
240 
begin() const241 BuildingTypeManager::iterator BuildingTypeManager::begin() const {
242     CheckPendingBuildingTypes();
243     return m_building_types.begin();
244 }
245 
end() const246 BuildingTypeManager::iterator BuildingTypeManager::end() const {
247     CheckPendingBuildingTypes();
248     return m_building_types.end();
249 }
250 
GetBuildingTypeManager()251 BuildingTypeManager& BuildingTypeManager::GetBuildingTypeManager() {
252     static BuildingTypeManager manager;
253     return manager;
254 }
255 
GetCheckSum() const256 unsigned int BuildingTypeManager::GetCheckSum() const {
257     CheckPendingBuildingTypes();
258     unsigned int retval{0};
259     for (auto const& name_type_pair : m_building_types)
260         CheckSums::CheckSumCombine(retval, name_type_pair);
261     CheckSums::CheckSumCombine(retval, m_building_types.size());
262 
263 
264     DebugLogger() << "BuildingTypeManager checksum: " << retval;
265     return retval;
266 }
267 
SetBuildingTypes(Pending::Pending<container_type> && pending_building_types)268 void BuildingTypeManager::SetBuildingTypes(Pending::Pending<container_type>&& pending_building_types)
269 { m_pending_building_types = std::move(pending_building_types); }
270 
CheckPendingBuildingTypes() const271 void BuildingTypeManager::CheckPendingBuildingTypes() const
272 { Pending::SwapPending(m_pending_building_types, m_building_types); }
273 
274 
GetBuildingTypeManager()275 BuildingTypeManager& GetBuildingTypeManager()
276 { return BuildingTypeManager::GetBuildingTypeManager(); }
277 
GetBuildingType(const std::string & name)278 const BuildingType* GetBuildingType(const std::string& name)
279 { return GetBuildingTypeManager().GetBuildingType(name); }
280