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