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