1 /*
2 * Copyright (C) 2006-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20 #include "logic/map_objects/tribes/tribes.h"
21
22 #include <memory>
23
24 #include "base/wexception.h"
25 #include "graphic/graphic.h"
26 #include "logic/game_data_error.h"
27 #include "logic/map_objects/tribes/carrier.h"
28 #include "logic/map_objects/tribes/constructionsite.h"
29 #include "logic/map_objects/tribes/dismantlesite.h"
30 #include "logic/map_objects/tribes/ferry.h"
31 #include "logic/map_objects/tribes/market.h"
32 #include "logic/map_objects/tribes/militarysite.h"
33 #include "logic/map_objects/tribes/productionsite.h"
34 #include "logic/map_objects/tribes/ship.h"
35 #include "logic/map_objects/tribes/soldier.h"
36 #include "logic/map_objects/tribes/trainingsite.h"
37 #include "logic/map_objects/tribes/tribe_basic_info.h"
38
39 namespace Widelands {
Tribes()40 Tribes::Tribes()
41 : buildings_(new DescriptionMaintainer<BuildingDescr>()),
42 immovables_(new DescriptionMaintainer<ImmovableDescr>()),
43 ships_(new DescriptionMaintainer<ShipDescr>()),
44 wares_(new DescriptionMaintainer<WareDescr>()),
45 workers_(new DescriptionMaintainer<WorkerDescr>()),
46 tribes_(new DescriptionMaintainer<TribeDescr>()),
47 legacy_lookup_table_(new TribesLegacyLookupTable()),
48 largest_workarea_(0) {
49 }
50
add_constructionsite_type(const LuaTable & table)51 void Tribes::add_constructionsite_type(const LuaTable& table) {
52 i18n::Textdomain td("tribes");
53 buildings_->add(new ConstructionSiteDescr(
54 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
55 table, *this));
56 }
57
add_dismantlesite_type(const LuaTable & table)58 void Tribes::add_dismantlesite_type(const LuaTable& table) {
59 i18n::Textdomain td("tribes");
60 buildings_->add(new DismantleSiteDescr(
61 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
62 table, *this));
63 }
64
add_militarysite_type(const LuaTable & table)65 void Tribes::add_militarysite_type(const LuaTable& table) {
66 i18n::Textdomain td("tribes");
67 buildings_->add(new MilitarySiteDescr(
68 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
69 table, *this));
70 }
71
add_productionsite_type(const LuaTable & table,const World & world)72 void Tribes::add_productionsite_type(const LuaTable& table, const World& world) {
73 i18n::Textdomain td("tribes");
74 const std::string msgctxt = table.get_string("msgctxt");
75 buildings_->add(
76 new ProductionSiteDescr(pgettext_expr(msgctxt.c_str(), table.get_string("descname").c_str()),
77 msgctxt, table, *this, world));
78 }
79
add_trainingsite_type(const LuaTable & table,const World & world)80 void Tribes::add_trainingsite_type(const LuaTable& table, const World& world) {
81 i18n::Textdomain td("tribes");
82 const std::string msgctxt = table.get_string("msgctxt");
83 buildings_->add(
84 new TrainingSiteDescr(pgettext_expr(msgctxt.c_str(), table.get_string("descname").c_str()),
85 msgctxt, table, *this, world));
86 }
87
add_warehouse_type(const LuaTable & table)88 void Tribes::add_warehouse_type(const LuaTable& table) {
89 i18n::Textdomain td("tribes");
90 buildings_->add(new WarehouseDescr(
91 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
92 table, *this));
93 }
94
add_market_type(const LuaTable & table)95 void Tribes::add_market_type(const LuaTable& table) {
96 i18n::Textdomain td("tribes");
97 buildings_->add(new MarketDescr(
98 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
99 table, *this));
100 }
101
add_immovable_type(const LuaTable & table)102 void Tribes::add_immovable_type(const LuaTable& table) {
103 i18n::Textdomain td("tribes");
104 immovables_->add(new ImmovableDescr(
105 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
106 table, *this));
107 }
108
add_ship_type(const LuaTable & table)109 void Tribes::add_ship_type(const LuaTable& table) {
110 i18n::Textdomain td("tribes");
111 ships_->add(new ShipDescr(_(table.get_string("descname")), table));
112 }
113
add_ware_type(const LuaTable & table)114 void Tribes::add_ware_type(const LuaTable& table) {
115 i18n::Textdomain td("tribes");
116 wares_->add(new WareDescr(
117 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
118 table));
119 }
120
add_carrier_type(const LuaTable & table)121 void Tribes::add_carrier_type(const LuaTable& table) {
122 i18n::Textdomain td("tribes");
123 workers_->add(new CarrierDescr(
124 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
125 table, *this));
126 }
127
add_ferry_type(const LuaTable & table)128 void Tribes::add_ferry_type(const LuaTable& table) {
129 i18n::Textdomain td("tribes");
130 workers_->add(new FerryDescr(
131 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
132 table, *this));
133 }
134
add_soldier_type(const LuaTable & table)135 void Tribes::add_soldier_type(const LuaTable& table) {
136 i18n::Textdomain td("tribes");
137 workers_->add(new SoldierDescr(
138 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
139 table, *this));
140 }
141
add_worker_type(const LuaTable & table)142 void Tribes::add_worker_type(const LuaTable& table) {
143 i18n::Textdomain td("tribes");
144 workers_->add(new WorkerDescr(
145 pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()),
146 table, *this));
147 }
148
add_tribe(const LuaTable & table)149 void Tribes::add_tribe(const LuaTable& table) {
150 const std::string name = table.get_string("name");
151 if (Widelands::tribe_exists(name)) {
152 tribes_->add(new TribeDescr(table, Widelands::get_tribeinfo(name), *this));
153 } else {
154 throw GameDataError("The tribe '%s'' has no preload file.", name.c_str());
155 }
156 }
157
add_custom_building(const LuaTable & table)158 void Tribes::add_custom_building(const LuaTable& table) {
159 const std::string tribename = table.get_string("tribename");
160 if (Widelands::tribe_exists(tribename)) {
161 TribeDescr* descr = tribes_->get_mutable(tribe_index(tribename));
162 const std::string buildingname = table.get_string("buildingname");
163 descr->add_building(buildingname);
164 } else {
165 throw GameDataError("The tribe '%s'' has no preload file.", tribename.c_str());
166 }
167 }
168
add_custom_worker(const LuaTable & table)169 void Tribes::add_custom_worker(const LuaTable& table) {
170 const std::string tribename = table.get_string("tribename");
171 if (Widelands::tribe_exists(tribename)) {
172 TribeDescr* descr = tribes_->get_mutable(tribe_index(tribename));
173 descr->add_worker(table.get_string("workername"));
174 } else {
175 throw GameDataError("The tribe '%s'' has no preload file.", tribename.c_str());
176 }
177 }
178
nrbuildings() const179 size_t Tribes::nrbuildings() const {
180 return buildings_->size();
181 }
182
nrtribes() const183 size_t Tribes::nrtribes() const {
184 return tribes_->size();
185 }
186
nrwares() const187 size_t Tribes::nrwares() const {
188 return wares_->size();
189 }
190
nrworkers() const191 size_t Tribes::nrworkers() const {
192 return workers_->size();
193 }
194
ware_exists(const std::string & warename) const195 bool Tribes::ware_exists(const std::string& warename) const {
196 return wares_->exists(warename) != nullptr;
197 }
ware_exists(const DescriptionIndex & index) const198 bool Tribes::ware_exists(const DescriptionIndex& index) const {
199 return wares_->get_mutable(index) != nullptr;
200 }
worker_exists(const std::string & workername) const201 bool Tribes::worker_exists(const std::string& workername) const {
202 return workers_->exists(workername) != nullptr;
203 }
worker_exists(const DescriptionIndex & index) const204 bool Tribes::worker_exists(const DescriptionIndex& index) const {
205 return workers_->get_mutable(index) != nullptr;
206 }
building_exists(const std::string & buildingname) const207 bool Tribes::building_exists(const std::string& buildingname) const {
208 return buildings_->exists(buildingname) != nullptr;
209 }
building_exists(const DescriptionIndex & index) const210 bool Tribes::building_exists(const DescriptionIndex& index) const {
211 return buildings_->get_mutable(index) != nullptr;
212 }
immovable_exists(DescriptionIndex index) const213 bool Tribes::immovable_exists(DescriptionIndex index) const {
214 return immovables_->get_mutable(index) != nullptr;
215 }
ship_exists(DescriptionIndex index) const216 bool Tribes::ship_exists(DescriptionIndex index) const {
217 return ships_->get_mutable(index) != nullptr;
218 }
tribe_exists(DescriptionIndex index) const219 bool Tribes::tribe_exists(DescriptionIndex index) const {
220 return tribes_->get_mutable(index) != nullptr;
221 }
222
safe_building_index(const std::string & buildingname) const223 DescriptionIndex Tribes::safe_building_index(const std::string& buildingname) const {
224 const DescriptionIndex result =
225 building_index(legacy_lookup_table_.get()->lookup_building(buildingname));
226 if (!building_exists(result)) {
227 throw GameDataError("Unknown building type \"%s\"", buildingname.c_str());
228 }
229 return result;
230 }
231
safe_immovable_index(const std::string & immovablename) const232 DescriptionIndex Tribes::safe_immovable_index(const std::string& immovablename) const {
233 const DescriptionIndex result =
234 immovable_index(legacy_lookup_table_.get()->lookup_immovable(immovablename));
235 if (!immovable_exists(result)) {
236 throw GameDataError("Unknown immovable type \"%s\"", immovablename.c_str());
237 }
238 return result;
239 }
240
safe_ship_index(const std::string & shipname) const241 DescriptionIndex Tribes::safe_ship_index(const std::string& shipname) const {
242 const DescriptionIndex result = ship_index(legacy_lookup_table_.get()->lookup_ship(shipname));
243 if (!ship_exists(result)) {
244 throw GameDataError("Unknown ship type \"%s\"", shipname.c_str());
245 }
246 return result;
247 }
248
safe_tribe_index(const std::string & tribename) const249 DescriptionIndex Tribes::safe_tribe_index(const std::string& tribename) const {
250 const DescriptionIndex result = tribe_index(tribename);
251 if (!tribe_exists(result)) {
252 throw GameDataError("Unknown tribe \"%s\"", tribename.c_str());
253 }
254 return result;
255 }
256
safe_ware_index(const std::string & warename) const257 DescriptionIndex Tribes::safe_ware_index(const std::string& warename) const {
258 const DescriptionIndex result = ware_index(legacy_lookup_table_.get()->lookup_ware(warename));
259 if (!ware_exists(result)) {
260 throw GameDataError("Unknown ware type \"%s\"", warename.c_str());
261 }
262 return result;
263 }
264
safe_worker_index(const std::string & workername) const265 DescriptionIndex Tribes::safe_worker_index(const std::string& workername) const {
266 const DescriptionIndex result =
267 worker_index(legacy_lookup_table_.get()->lookup_worker(workername));
268 if (!worker_exists(result)) {
269 throw GameDataError("Unknown worker type \"%s\"", workername.c_str());
270 }
271 return result;
272 }
273
building_index(const std::string & buildingname) const274 DescriptionIndex Tribes::building_index(const std::string& buildingname) const {
275 return buildings_->get_index(buildingname);
276 }
277
immovable_index(const std::string & immovablename) const278 DescriptionIndex Tribes::immovable_index(const std::string& immovablename) const {
279 return immovables_->get_index(immovablename);
280 }
281
ship_index(const std::string & shipname) const282 DescriptionIndex Tribes::ship_index(const std::string& shipname) const {
283 return ships_->get_index(shipname);
284 }
285
tribe_index(const std::string & tribename) const286 DescriptionIndex Tribes::tribe_index(const std::string& tribename) const {
287 return tribes_->get_index(tribename);
288 }
289
ware_index(const std::string & warename) const290 DescriptionIndex Tribes::ware_index(const std::string& warename) const {
291 return wares_->get_index(warename);
292 }
293
worker_index(const std::string & workername) const294 DescriptionIndex Tribes::worker_index(const std::string& workername) const {
295 return workers_->get_index(workername);
296 }
297
get_building_descr(DescriptionIndex buildingindex) const298 const BuildingDescr* Tribes::get_building_descr(DescriptionIndex buildingindex) const {
299 return buildings_->get_mutable(buildingindex);
300 }
301
get_mutable_building_descr(DescriptionIndex buildingindex) const302 BuildingDescr* Tribes::get_mutable_building_descr(DescriptionIndex buildingindex) const {
303 return buildings_->get_mutable(buildingindex);
304 }
305
get_immovable_descr(DescriptionIndex immovableindex) const306 const ImmovableDescr* Tribes::get_immovable_descr(DescriptionIndex immovableindex) const {
307 return immovables_->get_mutable(immovableindex);
308 }
309
get_ship_descr(DescriptionIndex shipindex) const310 const ShipDescr* Tribes::get_ship_descr(DescriptionIndex shipindex) const {
311 return ships_->get_mutable(shipindex);
312 }
313
get_ware_descr(DescriptionIndex wareindex) const314 const WareDescr* Tribes::get_ware_descr(DescriptionIndex wareindex) const {
315 return wares_->get_mutable(wareindex);
316 }
317
get_worker_descr(DescriptionIndex workerindex) const318 const WorkerDescr* Tribes::get_worker_descr(DescriptionIndex workerindex) const {
319 return workers_->get_mutable(workerindex);
320 }
321
get_tribe_descr(DescriptionIndex tribeindex) const322 const TribeDescr* Tribes::get_tribe_descr(DescriptionIndex tribeindex) const {
323 return tribes_->get_mutable(tribeindex);
324 }
325
load_graphics()326 void Tribes::load_graphics() {
327 for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {
328 TribeDescr* tribe = tribes_->get_mutable(tribeindex);
329 for (const std::string& texture_path : tribe->normal_road_paths()) {
330 tribe->add_normal_road_texture(g_gr->images().get(texture_path));
331 }
332 for (const std::string& texture_path : tribe->busy_road_paths()) {
333 tribe->add_busy_road_texture(g_gr->images().get(texture_path));
334 }
335 for (const std::string& texture_path : tribe->waterway_paths()) {
336 tribe->add_waterway_texture(g_gr->images().get(texture_path));
337 }
338 }
339 }
340
postload()341 void Tribes::postload() {
342 largest_workarea_ = 0;
343 for (DescriptionIndex i = 0; i < buildings_->size(); ++i) {
344 BuildingDescr& building_descr = *buildings_->get_mutable(i);
345
346 // Calculate largest possible workarea radius
347 for (const auto& pair : building_descr.workarea_info()) {
348 largest_workarea_ = std::max(largest_workarea_, pair.first);
349 }
350
351 // Add consumers and producers to wares.
352 if (upcast(ProductionSiteDescr, de, &building_descr)) {
353 for (const auto& ware_amount : de->input_wares()) {
354 wares_->get_mutable(ware_amount.first)->add_consumer(i);
355 }
356 for (const DescriptionIndex& wareindex : de->output_ware_types()) {
357 wares_->get_mutable(wareindex)->add_producer(i);
358 }
359 for (const auto& job : de->working_positions()) {
360 workers_->get_mutable(job.first)->add_employer(i);
361 }
362
363 // Check that all workarea overlap hints are valid
364 for (const auto& pair : de->get_highlight_overlapping_workarea_for()) {
365 const DescriptionIndex di = safe_building_index(pair.first);
366 if (upcast(const ProductionSiteDescr, p, get_building_descr(di))) {
367 if (!p->workarea_info().empty()) {
368 continue;
369 }
370 throw GameDataError("Productionsite %s will inform about conflicting building %s "
371 "which doesn’t have a workarea",
372 de->name().c_str(), pair.first.c_str());
373 }
374 throw GameDataError("Productionsite %s will inform about conflicting building %s which "
375 "is not a productionsite",
376 de->name().c_str(), pair.first.c_str());
377 }
378 }
379
380 // Register which buildings buildings can have been enhanced from
381 const DescriptionIndex& enhancement = building_descr.enhancement();
382 if (building_exists(enhancement)) {
383 buildings_->get_mutable(enhancement)->set_enhanced_from(i);
384 }
385 }
386
387 // Calculate the trainingsites proportions.
388 postload_calculate_trainingsites_proportions();
389
390 // Some final checks on the gamedata
391 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {
392 TribeDescr* tribe_descr = tribes_->get_mutable(i);
393
394 // Register which wares and workers have economy demand checks for each tribe
395 for (const DescriptionIndex bi : tribe_descr->buildings()) {
396 postload_register_economy_demand_checks(*buildings_->get_mutable(bi), *tribe_descr);
397 }
398 // Verify that the preciousness has been set for all of the tribe's wares
399 for (const DescriptionIndex wi : tribe_descr->wares()) {
400 if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) ==
401 kInvalidWare) {
402 throw GameDataError("The ware '%s' needs to define a preciousness for tribe '%s'",
403 tribe_descr->get_ware_descr(wi)->name().c_str(),
404 tribe_descr->name().c_str());
405 }
406 }
407 }
408 }
409
410 /// Register wares and workers that have economy demand checks for a building
postload_register_economy_demand_checks(BuildingDescr & building_descr,const TribeDescr & tribe_descr)411 void Tribes::postload_register_economy_demand_checks(BuildingDescr& building_descr,
412 const TribeDescr& tribe_descr) {
413 if (upcast(ProductionSiteDescr, prodsite, &building_descr)) {
414 // This function can be called only once per loading of tribes
415 assert(prodsite->ware_demand_checks() != nullptr);
416
417 for (const DescriptionIndex wi : *prodsite->ware_demand_checks()) {
418 if (!tribe_descr.has_ware(wi)) {
419 throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check "
420 "for ware '%s', but the tribe does not use this ware",
421 prodsite->name().c_str(), tribe_descr.name().c_str(),
422 get_ware_descr(wi)->name().c_str());
423 }
424 wares_->get_mutable(wi)->set_has_demand_check(tribe_descr.name());
425 }
426 for (const DescriptionIndex wi : *prodsite->worker_demand_checks()) {
427 if (!tribe_descr.has_worker(wi)) {
428 throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check "
429 "for worker '%s', but the tribe does not use this worker",
430 prodsite->name().c_str(), tribe_descr.name().c_str(),
431 get_worker_descr(wi)->name().c_str());
432 }
433 workers_->get_mutable(wi)->set_has_demand_check();
434 }
435 prodsite->clear_demand_checks();
436 }
437 }
438
439 /// Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100
postload_calculate_trainingsites_proportions()440 void Tribes::postload_calculate_trainingsites_proportions() {
441 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {
442 TribeDescr* tribe_descr = tribes_->get_mutable(i);
443 unsigned int trainingsites_without_percent = 0;
444 int used_percent = 0;
445 std::vector<BuildingDescr*> traingsites_with_percent;
446 for (const DescriptionIndex& index : tribe_descr->trainingsites()) {
447 BuildingDescr* descr = get_mutable_building_descr(index);
448 if (descr->hints().trainingsites_max_percent() == 0) {
449 ++trainingsites_without_percent;
450 } else {
451 used_percent += descr->hints().trainingsites_max_percent();
452 traingsites_with_percent.push_back(descr);
453 }
454 }
455
456 log("%s trainingsites: We have used up %d%% on %" PRIuS " sites, there are %d without\n",
457 tribe_descr->name().c_str(), used_percent, traingsites_with_percent.size(),
458 trainingsites_without_percent);
459
460 // Adjust used_percent if we don't have at least 5% for each remaining trainingsite
461 const float limit = 100 - trainingsites_without_percent * 5;
462 if (used_percent > limit) {
463 const int deductme = (used_percent - limit) / traingsites_with_percent.size();
464 used_percent = 0;
465 for (BuildingDescr* descr : traingsites_with_percent) {
466 descr->set_hints_trainingsites_max_percent(descr->hints().trainingsites_max_percent() -
467 deductme);
468 used_percent += descr->hints().trainingsites_max_percent();
469 }
470 log("%s trainingsites: Used percent was adjusted to %d%%\n", tribe_descr->name().c_str(),
471 used_percent);
472 }
473
474 // Now adjust for trainingsites that didn't have their max_percent set
475 if (trainingsites_without_percent > 0) {
476 int percent_to_use = std::ceil((100 - used_percent) / trainingsites_without_percent);
477 // We sometimes get below 100% in spite of the ceil call above.
478 // A total sum a bit above 100% is fine though, so we increment until it's big enough.
479 while ((used_percent + percent_to_use * trainingsites_without_percent) < 100) {
480 ++percent_to_use;
481 }
482 log("%s trainingsites: Assigning %d%% to each of the remaining %d sites\n",
483 tribe_descr->name().c_str(), percent_to_use, trainingsites_without_percent);
484 if (percent_to_use < 1) {
485 throw GameDataError(
486 "%s: Training sites without predefined proportions add up to < 1%% and "
487 "will never be built: %d",
488 tribe_descr->name().c_str(), used_percent);
489 }
490 for (const DescriptionIndex& index : tribe_descr->trainingsites()) {
491 BuildingDescr* descr = get_mutable_building_descr(index);
492 if (descr->hints().trainingsites_max_percent() == 0) {
493 descr->set_hints_trainingsites_max_percent(percent_to_use);
494 used_percent += percent_to_use;
495 }
496 }
497 }
498 if (used_percent < 100) {
499 throw GameDataError("%s: Final training sites proportions add up to < 100%%: %d",
500 tribe_descr->name().c_str(), used_percent);
501 }
502 }
503 }
504
get_largest_workarea() const505 uint32_t Tribes::get_largest_workarea() const {
506 return largest_workarea_;
507 }
508 } // namespace Widelands
509