1 /***************************************************************************
2  *      Mechanized Assault and Exploration Reloaded Projectfile            *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (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                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 #include <cassert>
20 #include "game/data/base/base.h"
21 
22 #include "game/data/units/building.h"
23 #include "game/logic/clientevents.h"
24 #include "utility/listhelpers.h"
25 #include "utility/log.h"
26 #include "game/data/map/map.h"
27 #include "netmessage.h"
28 #include "game/data/player/player.h"
29 #include "game/logic/server.h"
30 #include "game/logic/serverevents.h"
31 #include "game/data/report/savedreportsimple.h"
32 #include "game/data/report/special/savedreportresourcechanged.h"
33 
34 using namespace std;
35 
sSubBase(cPlayer * owner_)36 sSubBase::sSubBase (cPlayer* owner_) :
37 	buildings(),
38 	owner (owner_),
39 	MaxMetal(),
40 	MaxOil(),
41 	MaxGold(),
42 	MaxEnergyProd(),
43 	EnergyProd(),
44 	MaxEnergyNeed(),
45 	EnergyNeed(),
46 	MetalNeed(),
47 	OilNeed(),
48 	GoldNeed(),
49 	MaxMetalNeed(),
50 	MaxOilNeed(),
51 	MaxGoldNeed(),
52 	HumanProd(),
53 	HumanNeed(),
54 	MaxHumanNeed(),
55 	MetalProd(),
56 	OilProd(),
57 	GoldProd(),
58 	metal(),
59 	oil(),
60 	gold()
61 {}
62 
sSubBase(const sSubBase & other)63 sSubBase::sSubBase (const sSubBase& other) :
64 	buildings (other.buildings),
65 	owner (other.owner),
66 	MaxMetal (other.MaxMetal),
67 	MaxOil (other.MaxOil),
68 	MaxGold (other.MaxGold),
69 	MaxEnergyProd (other.MaxEnergyProd),
70 	EnergyProd (other.EnergyProd),
71 	MaxEnergyNeed (other.MaxEnergyNeed),
72 	EnergyNeed (other.EnergyNeed),
73 	MetalNeed (other.MetalNeed),
74 	OilNeed (other.OilNeed),
75 	GoldNeed (other.GoldNeed),
76 	MaxMetalNeed (other.MaxMetalNeed),
77 	MaxOilNeed (other.MaxOilNeed),
78 	MaxGoldNeed (other.MaxGoldNeed),
79 	HumanProd (other.HumanProd),
80 	HumanNeed (other.HumanNeed),
81 	MaxHumanNeed (other.MaxHumanNeed),
82 	MetalProd (other.MetalProd),
83 	OilProd (other.OilProd),
84 	GoldProd (other.GoldProd),
85 	metal (other.metal),
86 	oil (other.oil),
87 	gold (other.gold)
88 {}
89 
~sSubBase()90 sSubBase::~sSubBase()
91 {
92 	destroyed();
93 }
94 
getMaxMetalProd() const95 int sSubBase::getMaxMetalProd() const
96 {
97 	return calcMaxProd (RES_METAL);
98 }
99 
getMaxGoldProd() const100 int sSubBase::getMaxGoldProd() const
101 {
102 	return calcMaxProd (RES_GOLD);
103 }
104 
getMaxOilProd() const105 int sSubBase::getMaxOilProd() const
106 {
107 	return calcMaxProd (RES_OIL);
108 }
109 
getMaxAllowedMetalProd() const110 int sSubBase::getMaxAllowedMetalProd() const
111 {
112 	return calcMaxAllowedProd (RES_METAL);
113 }
114 
getMaxAllowedGoldProd() const115 int sSubBase::getMaxAllowedGoldProd() const
116 {
117 	return calcMaxAllowedProd (RES_GOLD);
118 }
119 
getMaxAllowedOilProd() const120 int sSubBase::getMaxAllowedOilProd() const
121 {
122 	return calcMaxAllowedProd (RES_OIL);
123 }
124 
getMetalProd() const125 int sSubBase::getMetalProd() const
126 {
127 	return MetalProd;
128 }
129 
getGoldProd() const130 int sSubBase::getGoldProd() const
131 {
132 	return GoldProd;
133 }
134 
getOilProd() const135 int sSubBase::getOilProd() const
136 {
137 	return OilProd;
138 }
139 
setMetalProd(int value)140 void sSubBase::setMetalProd (int value)
141 {
142 	const int max = getMaxAllowedMetalProd();
143 
144 	value = std::max (0, value);
145 	value = std::min (value, max);
146 
147 	MetalProd = value;
148 }
149 
setGoldProd(int value)150 void sSubBase::setGoldProd (int value)
151 {
152 	const int max = getMaxAllowedGoldProd();
153 
154 	value = std::max (0, value);
155 	value = std::min (value, max);
156 
157 	GoldProd = value;
158 }
159 
setOilProd(int value)160 void sSubBase::setOilProd (int value)
161 {
162 	const int max = getMaxAllowedOilProd();
163 
164 	value = std::max (0, value);
165 	value = std::min (value, max);
166 
167 	OilProd = value;
168 }
169 
getMetal() const170 int sSubBase::getMetal() const
171 {
172 	return metal;
173 }
174 
setMetal(int value)175 void sSubBase::setMetal (int value)
176 {
177 	std::swap (metal, value);
178 	if (metal != value) metalChanged();
179 }
180 
getOil() const181 int sSubBase::getOil() const
182 {
183 	return oil;
184 }
185 
setOil(int value)186 void sSubBase::setOil (int value)
187 {
188 	std::swap (oil, value);
189 	if (oil != value) oilChanged();
190 }
191 
getGold() const192 int sSubBase::getGold() const
193 {
194 	return gold;
195 }
196 
setGold(int value)197 void sSubBase::setGold (int value)
198 {
199 	std::swap (gold, value);
200 	if (gold != value) goldChanged();
201 }
202 
changeMetalProd(int value)203 void sSubBase::changeMetalProd (int value)
204 {
205 	setMetalProd (MetalProd + value);
206 }
207 
changeOilProd(int value)208 void sSubBase::changeOilProd (int value)
209 {
210 	setOilProd (OilProd + value);
211 }
212 
changeGoldProd(int value)213 void sSubBase::changeGoldProd (int value)
214 {
215 	setGoldProd (GoldProd + value);
216 }
217 
calcMaxProd(int ressourceType) const218 int sSubBase::calcMaxProd (int ressourceType) const
219 {
220 	int maxProd = 0;
221 	for (size_t i = 0; i != buildings.size(); ++i)
222 	{
223 		const cBuilding& building = *buildings[i];
224 
225 		if (building.data.canMineMaxRes <= 0 || !building.isUnitWorking()) continue;
226 
227 		switch (ressourceType)
228 		{
229 			case RES_METAL: maxProd += building.MaxMetalProd; break;
230 			case RES_OIL: maxProd += building.MaxOilProd; break;
231 			case RES_GOLD: maxProd += building.MaxGoldProd; break;
232 		}
233 	}
234 	return maxProd;
235 }
236 
calcMaxAllowedProd(int ressourceType) const237 int sSubBase::calcMaxAllowedProd (int ressourceType) const
238 {
239 	// initialise needed Variables and element pointers,
240 	// so the algorithm itself is independent from the ressouce type
241 	int maxAllowedProd;
242 	int ressourceToDistributeB;
243 	int ressourceToDistributeC;
244 
245 	int cBuilding::* ressourceProdA;
246 	int cBuilding::* ressourceProdB;
247 	int cBuilding::* ressourceProdC;
248 
249 	switch (ressourceType)
250 	{
251 		case RES_METAL:
252 			maxAllowedProd = getMaxMetalProd();
253 			ressourceToDistributeB = GoldProd;
254 			ressourceToDistributeC = OilProd;
255 			ressourceProdA = &cBuilding::MaxMetalProd;
256 			ressourceProdB = &cBuilding::MaxGoldProd;
257 			ressourceProdC = &cBuilding::MaxOilProd;
258 			break;
259 		case RES_OIL:
260 			maxAllowedProd = getMaxOilProd();
261 			ressourceToDistributeB = MetalProd;
262 			ressourceToDistributeC = GoldProd;
263 			ressourceProdA = &cBuilding::MaxOilProd;
264 			ressourceProdB = &cBuilding::MaxMetalProd;
265 			ressourceProdC = &cBuilding::MaxGoldProd;
266 			break;
267 		case RES_GOLD:
268 			maxAllowedProd = getMaxGoldProd();
269 			ressourceToDistributeB = MetalProd;
270 			ressourceToDistributeC = OilProd;
271 			ressourceProdA = &cBuilding::MaxGoldProd;
272 			ressourceProdB = &cBuilding::MaxMetalProd;
273 			ressourceProdC = &cBuilding::MaxOilProd;
274 			break;
275 		default:
276 			return 0;
277 	}
278 
279 	// when calculating the maximum allowed production for ressource A,
280 	// the algo tries to distribute the ressources B and C
281 	// so that the maximum possible production capacity is left over for A.
282 	// the actual production values of each mine are not saved,
283 	// because they are not needed.
284 
285 	// step one:
286 	// distribute ressources,
287 	// that do not decrease the possible production of the others
288 	for (size_t i = 0; i != buildings.size(); ++i)
289 	{
290 		const cBuilding& building = *buildings[i];
291 
292 		if (building.data.canMineMaxRes <= 0 || !building.isUnitWorking()) continue;
293 
294 		// how much of B can be produced in this mine,
295 		// without decreasing the possible production of A and C?
296 		int amount = min (building.*ressourceProdB, building.data.canMineMaxRes - building.*ressourceProdA - building.*ressourceProdC);
297 		if (amount > 0) ressourceToDistributeB -= amount;
298 
299 		// how much of C can be produced in this mine,
300 		// without decreasing the possible production of A and B?
301 		amount = min (building.*ressourceProdC, building.data.canMineMaxRes - building.*ressourceProdA - building.*ressourceProdB);
302 		if (amount > 0) ressourceToDistributeC -= amount;
303 	}
304 
305 	ressourceToDistributeB = std::max (ressourceToDistributeB, 0);
306 	ressourceToDistributeC = std::max (ressourceToDistributeC, 0);
307 
308 	// step two:
309 	// distribute ressources, that do not decrease the possible production of A
310 	for (size_t i = 0; i != buildings.size(); ++i)
311 	{
312 		const cBuilding& building = *buildings[i];
313 
314 		if (building.data.canMineMaxRes <= 0 || !building.isUnitWorking()) continue;
315 
316 		int freeB = min (building.data.canMineMaxRes - building.*ressourceProdA, building.*ressourceProdB);
317 		int freeC = min (building.data.canMineMaxRes - building.*ressourceProdA, building.*ressourceProdC);
318 
319 		// subtract values from step 1
320 		freeB -= min (max (building.data.canMineMaxRes - building.*ressourceProdA - building.*ressourceProdC, 0), building.*ressourceProdB);
321 		freeC -= min (max (building.data.canMineMaxRes - building.*ressourceProdA - building.*ressourceProdB, 0), building.*ressourceProdC);
322 
323 		if (ressourceToDistributeB > 0)
324 		{
325 			const int value = min (freeB, ressourceToDistributeB);
326 			freeC -= value;
327 			ressourceToDistributeB -= value;
328 		}
329 		if (ressourceToDistributeC > 0)
330 		{
331 			ressourceToDistributeC -= min (freeC, ressourceToDistributeC);
332 		}
333 	}
334 
335 	// step three:
336 	// the remaining amount of B and C have to be subtracted
337 	// from the maximum allowed production of A
338 	maxAllowedProd -= ressourceToDistributeB + ressourceToDistributeC;
339 
340 	maxAllowedProd = std::max (maxAllowedProd, 0);
341 
342 	return maxAllowedProd;
343 }
344 
345 
isAOnlineStation(const cBuilding & building)346 static bool isAOnlineStation (const cBuilding& building)
347 {
348 	return building.data.produceEnergy > 1 && building.isUnitWorking();
349 }
350 
isAOfflineStation(const cBuilding & building)351 static bool isAOfflineStation (const cBuilding& building)
352 {
353 	return building.data.produceEnergy > 1 && !building.isUnitWorking();
354 }
355 
isAOnlineGenerator(const cBuilding & building)356 static bool isAOnlineGenerator (const cBuilding& building)
357 {
358 	return building.data.produceEnergy == 1 && building.isUnitWorking();
359 }
360 
isAOfflineGenerator(const cBuilding & building)361 static bool isAOfflineGenerator (const cBuilding& building)
362 {
363 	return building.data.produceEnergy == 1 && !building.isUnitWorking();
364 }
365 
366 template <typename T>
getFilteredBuildings(std::vector<cBuilding * > & buildings,T filter)367 static std::vector<cBuilding*> getFilteredBuildings (std::vector<cBuilding*>& buildings, T filter)
368 {
369 	std::vector<cBuilding*> res;
370 	for (size_t i = 0; i != buildings.size(); ++i)
371 	{
372 		cBuilding& building = *buildings[i];
373 
374 		if (filter (building)) res.push_back (&building);
375 	}
376 	return res;
377 }
378 
increaseEnergyProd(cServer & server,int value)379 bool sSubBase::increaseEnergyProd (cServer& server, int value)
380 {
381 	// TODO: the energy production and fuel consumption
382 	// of generators and stations are hardcoded in this function
383 	std::vector<cBuilding*> onlineStations = getFilteredBuildings (buildings, &isAOnlineStation);
384 	std::vector<cBuilding*> onlineGenerators = getFilteredBuildings (buildings, &isAOnlineGenerator);
385 	std::vector<cBuilding*> offlineStations = getFilteredBuildings (buildings, &isAOfflineStation);
386 	std::vector<cBuilding*> offlineGenerators = getFilteredBuildings (buildings, &isAOfflineGenerator);
387 	const int availableStations = onlineStations.size() + offlineStations.size();
388 	const int availableGenerators = onlineGenerators.size() + offlineGenerators.size();
389 
390 	// calc the optimum amount of energy stations and generators
391 	const int energy = EnergyProd + value;
392 
393 	int stations   = min ((energy + 3) / 6, availableStations);
394 	int generators = max (energy - stations * 6, 0);
395 
396 	if (generators > availableGenerators)
397 	{
398 		stations++;
399 		generators = 0;
400 	}
401 
402 	if (stations > availableStations)
403 	{
404 		return false; // not enough free energy production capacity
405 	}
406 
407 	// check available fuel
408 	int neededFuel = stations * 6 + generators * 2;
409 	if (neededFuel > getOil() + getMaxOilProd())
410 	{
411 		// not possible to produce enough fuel
412 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::FuelInsufficient), owner);
413 		return false;
414 	}
415 
416 	// stop unneeded buildings
417 	for (int i = (int) onlineStations.size() - stations; i > 0; --i)
418 	{
419 		onlineStations[0]->ServerStopWork (server, true);
420 		onlineStations.erase (onlineStations.begin());
421 	}
422 	for (int i = (int) onlineGenerators.size() - generators; i > 0; --i)
423 	{
424 		onlineGenerators[0]->ServerStopWork (server, true);
425 		onlineGenerators.erase (onlineGenerators.begin());
426 	}
427 
428 	// start needed buildings
429 	for (int i = stations - (int) onlineStations.size(); i > 0; --i)
430 	{
431 		offlineStations[0]->ServerStartWork (server);
432 		offlineStations.erase (offlineStations.begin());
433 	}
434 	for (int i = generators - (int) onlineGenerators.size(); i > 0; --i)
435 	{
436 		offlineGenerators[0]->ServerStartWork (server);
437 		offlineGenerators.erase (offlineGenerators.begin());
438 	}
439 	return true;
440 }
441 
addMetal(cServer & server,int value)442 void sSubBase::addMetal (cServer& server, int value)
443 {
444 	addRessouce (server, sUnitData::STORE_RES_METAL, value);
445 }
446 
addOil(cServer & server,int value)447 void sSubBase::addOil (cServer& server, int value)
448 {
449 	addRessouce (server, sUnitData::STORE_RES_OIL, value);
450 }
451 
addGold(cServer & server,int value)452 void sSubBase::addGold (cServer& server, int value)
453 {
454 	addRessouce (server, sUnitData::STORE_RES_GOLD, value);
455 }
456 
getResource(sUnitData::eStorageResType storeResType) const457 int sSubBase::getResource (sUnitData::eStorageResType storeResType) const
458 {
459 	switch (storeResType)
460 	{
461 		case sUnitData::STORE_RES_METAL:
462 			return getMetal();
463 		case sUnitData::STORE_RES_OIL:
464 			return getOil();
465 		case sUnitData::STORE_RES_GOLD:
466 			return getGold();
467 		default:
468 			assert (0);
469 	}
470 	return 0;
471 }
472 
setResource(sUnitData::eStorageResType storeResType,int value)473 void sSubBase::setResource (sUnitData::eStorageResType storeResType, int value)
474 {
475 	switch (storeResType)
476 	{
477 		case sUnitData::STORE_RES_METAL:
478 			setMetal (value);
479 			break;
480 		case sUnitData::STORE_RES_OIL:
481 			setOil (value);
482 			break;
483 		case sUnitData::STORE_RES_GOLD:
484 			setGold (value);
485 			break;
486 		default:
487 			assert (0);
488 	}
489 }
490 
addRessouce(cServer & server,sUnitData::eStorageResType storeResType,int value)491 void sSubBase::addRessouce (cServer& server, sUnitData::eStorageResType storeResType, int value)
492 {
493 	int storedRessources = getResource (storeResType);
494 	value = std::max (value, -storedRessources);
495 	if (value == 0) return;
496 	setResource (storeResType, storedRessources + value);
497 
498 	for (size_t i = 0; i != buildings.size(); ++i)
499 	{
500 		cBuilding& b = *buildings[i];
501 		if (b.data.storeResType != storeResType) continue;
502 		const int iStartValue = value;
503 		if (value < 0)
504 		{
505 			if (b.data.getStoredResources() > -value)
506 			{
507 				b.data.setStoredResources (b.data.getStoredResources() + value);
508 				value = 0;
509 			}
510 			else
511 			{
512 				value += b.data.getStoredResources();
513 				b.data.setStoredResources (0);
514 			}
515 		}
516 		else
517 		{
518 			if (b.data.storageResMax - b.data.getStoredResources() > value)
519 			{
520 				b.data.setStoredResources (b.data.getStoredResources() + value);
521 				value = 0;
522 			}
523 			else
524 			{
525 				value -= b.data.storageResMax - b.data.getStoredResources();
526 				b.data.setStoredResources (b.data.storageResMax);
527 			}
528 		}
529 		if (iStartValue != value) sendUnitData (server, b, *owner);
530 		if (value == 0) break;
531 	}
532 	sendSubbaseValues (server, *this, *owner);
533 }
534 
refresh()535 void sSubBase::refresh()
536 {
537 	// copy buildings list
538 	const std::vector<cBuilding*> buildingsCopy = buildings;
539 
540 	// reset subbase
541 	buildings.clear();
542 	MaxMetal = 0;
543 	setMetal (0);
544 	MaxOil = 0;
545 	setOil (0);
546 	MaxGold = 0;
547 	setGold (0);
548 	MaxEnergyProd = 0;
549 	EnergyProd = 0;
550 	MaxEnergyNeed = 0;
551 	EnergyNeed = 0;
552 	MetalNeed = 0;
553 	OilNeed = 0;
554 	GoldNeed = 0;
555 	MaxMetalNeed = 0;
556 	MaxOilNeed = 0;
557 	MaxGoldNeed = 0;
558 	MetalProd = 0;
559 	OilProd = 0;
560 	GoldProd = 0;
561 	HumanProd = 0;
562 	HumanNeed = 0;
563 	MaxHumanNeed = 0;
564 
565 	// read all buildings
566 	for (size_t i = 0; i != buildingsCopy.size(); ++i)
567 	{
568 		addBuilding (buildingsCopy[i]);
569 	}
570 }
571 
checkHumanConsumer(cServer & server)572 bool sSubBase::checkHumanConsumer (cServer& server)
573 {
574 	if (HumanNeed <= HumanProd) return false;
575 
576 	for (size_t i = 0; i != buildings.size(); ++i)
577 	{
578 		cBuilding& building = *buildings[i];
579 		if (!building.data.needsHumans || !building.isUnitWorking()) continue;
580 
581 		building.ServerStopWork (server, false);
582 
583 		if (HumanNeed <= HumanProd) break;
584 	}
585 	return true;
586 }
587 
checkGoldConsumer(cServer & server)588 bool sSubBase::checkGoldConsumer (cServer& server)
589 {
590 	if (GoldNeed <= GoldProd + getGold()) return false;
591 
592 	for (size_t i = 0; i != buildings.size(); ++i)
593 	{
594 		cBuilding& building = *buildings[i];
595 		if (!building.data.convertsGold || !building.isUnitWorking()) continue;
596 
597 		building.ServerStopWork (server, false);
598 
599 		if (GoldNeed <= GoldProd + getGold()) break;
600 	}
601 	return true;
602 }
603 
checkMetalConsumer(cServer & server)604 bool sSubBase::checkMetalConsumer (cServer& server)
605 {
606 	if (MetalNeed <= MetalProd + getMetal()) return false;
607 
608 	for (size_t i = 0; i != buildings.size(); ++i)
609 	{
610 		cBuilding& building = *buildings[i];
611 		if (!building.data.needsMetal || !building.isUnitWorking()) continue;
612 
613 		building.ServerStopWork (server, false);
614 
615 		if (MetalNeed <= MetalProd + getMetal()) break;
616 	}
617 	return true;
618 }
619 
checkOil(cServer & server)620 bool sSubBase::checkOil (cServer& server)
621 {
622 	// TODO: the energy production and fuel consumption of generators and
623 	// stations are hardcoded in this function
624 	std::vector<cBuilding*> onlineStations = getFilteredBuildings (buildings, &isAOnlineStation);
625 	std::vector<cBuilding*> onlineGenerators = getFilteredBuildings (buildings, &isAOnlineGenerator);
626 	std::vector<cBuilding*> offlineStations = getFilteredBuildings (buildings, &isAOfflineStation);
627 	std::vector<cBuilding*> offlineGenerators = getFilteredBuildings (buildings, &isAOfflineGenerator);
628 	const int availableStations = onlineStations.size() + offlineStations.size();
629 	const int availableGenerators = onlineGenerators.size() + offlineGenerators.size();
630 
631 	// calc the optimum amount of energy stations and generators
632 	int stations   = min ((EnergyNeed + 3) / 6, availableStations);
633 	int generators = max (EnergyNeed - stations * 6, 0);
634 
635 	if (generators > availableGenerators)
636 	{
637 		if (stations < availableStations)
638 		{
639 			stations++;
640 			generators = 0;
641 		}
642 		else
643 		{
644 			generators = availableGenerators;
645 		}
646 	}
647 
648 	// check needed oil
649 	int neededOil = stations * 6 + generators * 2;
650 	const int availableOil = getMaxOilProd() + getOil();
651 	bool oilMissing = false;
652 	if (neededOil > availableOil)
653 	{
654 		// reduce energy production to maximum possible value
655 		stations = min ((availableOil) / 6, availableStations);
656 		generators = min ((availableOil - (stations * 6)) / 2, availableGenerators);
657 
658 		oilMissing = true;
659 	}
660 
661 	// increase oil production, if necessary
662 	neededOil = stations * 6 + generators * 2;
663 	if (neededOil > OilProd + getOil())
664 	{
665 		// temporary decrease gold and metal production
666 		const int missingOil = neededOil - OilProd - getOil();
667 		const int gold = GoldProd;
668 		const int metal = MetalProd;
669 		setMetalProd (0);
670 		setGoldProd (0);
671 
672 		changeOilProd (missingOil);
673 
674 		setGoldProd (gold);
675 		setMetalProd (metal);
676 
677 		sendSavedReport (server, cSavedReportResourceChanged (RES_OIL, missingOil, true), owner);
678 		if (getMetalProd() < metal)
679 			sendSavedReport (server, cSavedReportResourceChanged (RES_METAL, metal - MetalProd, false), owner);
680 		if (getGoldProd() < gold)
681 			sendSavedReport (server, cSavedReportResourceChanged (RES_GOLD, gold - GoldProd, false), owner);
682 	}
683 
684 	// stop unneeded buildings
685 	for (int i = (int) onlineStations.size() - stations; i > 0; i--)
686 	{
687 		onlineStations[0]->ServerStopWork (server, true);
688 		onlineStations.erase (onlineStations.begin());
689 	}
690 	for (int i = (int) onlineGenerators.size() - generators; i > 0; i--)
691 	{
692 		onlineGenerators[0]->ServerStopWork (server, true);
693 		onlineGenerators.erase (onlineGenerators.begin());
694 	}
695 
696 	// start needed buildings
697 	for (int i = stations - (int) onlineStations.size(); i > 0; i--)
698 	{
699 		offlineStations[0]->ServerStartWork (server);
700 		offlineStations.erase (offlineStations.begin());
701 	}
702 	for (int i = generators - (int) onlineGenerators.size(); i > 0; i--)
703 	{
704 		offlineGenerators[0]->ServerStartWork (server);
705 		offlineGenerators.erase (offlineGenerators.begin());
706 	}
707 
708 	// temporary debug check
709 	if (isDitributionMaximized() == false)
710 	{
711 		Log.write (" Server: Mine distribution values are not a maximum", cLog::eLOG_TYPE_NET_WARNING);
712 	}
713 
714 	return oilMissing;
715 }
716 
checkEnergy(cServer & server)717 bool sSubBase::checkEnergy (cServer& server)
718 {
719 	if (EnergyNeed <= EnergyProd) return false;
720 
721 	for (size_t i = 0; i != buildings.size(); ++i)
722 	{
723 		cBuilding& building = *buildings[i];
724 		if (!building.data.needsEnergy || !building.isUnitWorking()) continue;
725 
726 		// do not shut down ressource producers in the first run
727 		if (building.MaxOilProd > 0 ||
728 			building.MaxGoldProd > 0 ||
729 			building.MaxMetalProd > 0) continue;
730 
731 		building.ServerStopWork (server, false);
732 
733 		if (EnergyNeed <= EnergyProd) return true;
734 	}
735 
736 	for (size_t i = 0; i != buildings.size(); ++i)
737 	{
738 		cBuilding& building = *buildings[i];
739 		if (!building.data.needsEnergy || !building.isUnitWorking()) continue;
740 
741 		// do not shut down oil producers in the second run
742 		if (building.MaxOilProd > 0) continue;
743 
744 		building.ServerStopWork (server, false);
745 
746 		if (EnergyNeed <= EnergyProd) return true;
747 	}
748 
749 	// if energy is still missing, shut down also oil producers
750 	for (size_t i = 0; i < buildings.size(); i++)
751 	{
752 		cBuilding& building = *buildings[i];
753 		if (!building.data.needsEnergy || !building.isUnitWorking()) continue;
754 
755 		building.ServerStopWork (server, false);
756 
757 		if (EnergyNeed <= EnergyProd) return true;
758 	}
759 	return true;
760 }
761 
checkTurnEnd(cServer & server)762 bool sSubBase::checkTurnEnd (cServer& server)
763 {
764 	bool changedSomething = false;
765 
766 	if (checkMetalConsumer (server))
767 	{
768 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::MetalLow), owner);
769 		changedSomething = true;
770 	}
771 
772 	if (checkHumanConsumer (server))
773 	{
774 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::TeamLow), owner);
775 		changedSomething = true;
776 	}
777 
778 	if (checkGoldConsumer (server))
779 	{
780 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::GoldLow), owner);
781 		changedSomething = true;
782 	}
783 
784 	// there is a loop around checkOil/checkEnergy,
785 	// because a lack of energy can lead to less fuel,
786 	// that can lead to less energy, etc...
787 	bool oilMissing = false;
788 	bool energyMissing = false;
789 	bool changed = true;
790 	while (changed)
791 	{
792 		changed = false;
793 		if (checkOil (server))
794 		{
795 			changed = true;
796 			oilMissing = true;
797 			changedSomething = true;
798 		}
799 
800 		if (checkEnergy (server))
801 		{
802 			changed = true;
803 			energyMissing = true;
804 			changedSomething = true;
805 		}
806 	}
807 	if (oilMissing)
808 	{
809 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::FuelLow), owner);
810 		changedSomething = true;
811 	}
812 
813 	if (energyMissing)
814 	{
815 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::EnergyLow), owner);
816 		changedSomething = true;
817 	}
818 
819 	// recheck metal and gold,
820 	// because metal and gold producers could have been shut down,
821 	// due to a lack of energy
822 	if (checkMetalConsumer (server))
823 	{
824 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::MetalLow), owner);
825 		changedSomething = true;
826 	}
827 
828 	if (checkGoldConsumer (server))
829 	{
830 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::GoldLow), owner);
831 		changedSomething = true;
832 	}
833 
834 	return changedSomething;
835 }
836 
makeTurnStartRepairs(cServer & server,cBuilding & building)837 void sSubBase::makeTurnStartRepairs (cServer& server, cBuilding& building)
838 {
839 	// repair (do not repair buildings that have been attacked in this turn):
840 	if (building.data.getHitpoints() >= building.data.getHitpointsMax()
841 		|| getMetal() <= 0 || building.hasBeenAttacked())
842 	{
843 		return;
844 	}
845 	// calc new hitpoints
846 	const auto newHitPoints = building.data.getHitpoints() + Round (((float)building.data.getHitpointsMax() / building.data.buildCosts) * 4);
847 	building.data.setHitpoints (std::min (building.data.getHitpointsMax(), newHitPoints));
848 	addMetal (server, -1);
849 	sendUnitData (server, building);
850 }
851 
makeTurnStartReload(cServer & server,cBuilding & building)852 void sSubBase::makeTurnStartReload (cServer& server, cBuilding& building)
853 {
854 	// reload:
855 	if (building.data.canAttack && building.data.getAmmo() == 0 && getMetal() > 0)
856 	{
857 		building.data.setAmmo (building.data.getAmmoMax());
858 		addMetal (server, -1);
859 		// ammo is not visible to enemies. So only send to the owner
860 		sendUnitData (server, building, *owner);
861 	}
862 }
863 
makeTurnStartBuild(cServer & server,cBuilding & building)864 void sSubBase::makeTurnStartBuild (cServer& server, cBuilding& building)
865 {
866 	// build:
867 	if (!building.isUnitWorking() || building.data.canBuild.empty() || building.isBuildListEmpty())
868 	{
869 		return;
870 	}
871 
872 	cBuildListItem& buildListItem = building.getBuildListItem (0);
873 	if (buildListItem.getRemainingMetal() > 0)
874 	{
875 		// in this block the metal consumption of the factory
876 		// in the next round can change
877 		// so we first subtract the old value from MetalNeed and
878 		// then add the new one, to hold the base up to date
879 		MetalNeed -= min (building.getMetalPerRound(), buildListItem.getRemainingMetal());
880 
881 		auto value = buildListItem.getRemainingMetal() - std::min (building.getMetalPerRound(), buildListItem.getRemainingMetal());
882 		value = std::max (value, 0);
883 		buildListItem.setRemainingMetal (value);
884 
885 		MetalNeed += min (building.getMetalPerRound(), buildListItem.getRemainingMetal());
886 		sendBuildList (server, building);
887 		sendSubbaseValues (server, *this, *owner);
888 	}
889 	if (buildListItem.getRemainingMetal() <= 0)
890 	{
891 		owner->addTurnReportUnit (buildListItem.getType());
892 		building.ServerStopWork (server, false);
893 	}
894 }
895 
makeTurnStart(cServer & server)896 void sSubBase::makeTurnStart (cServer& server)
897 {
898 	// produce ressources
899 	addOil (server, OilProd - OilNeed);
900 	addMetal (server, MetalProd - MetalNeed);
901 	addGold (server, GoldProd - GoldNeed);
902 
903 	// produce credits
904 	if (GoldNeed)
905 	{
906 		owner->setCredits (owner->getCredits() + GoldNeed);
907 		sendCredits (server, owner->getCredits(), *owner);
908 	}
909 
910 	// make repairs/build/reload
911 	for (size_t i = 0; i != buildings.size(); ++i)
912 	{
913 		cBuilding& building = *buildings[i];
914 
915 		makeTurnStartRepairs (server, building);
916 		building.setHasBeenAttacked (false);
917 		makeTurnStartReload (server, building);
918 		makeTurnStartBuild (server, building);
919 	}
920 
921 	// check maximum storage limits
922 	auto newMetal = std::min (this->MaxMetal, this->getMetal());
923 	auto newOil = std::min (this->MaxOil, this->getOil());
924 	auto newGold = std::min (this->MaxGold, this->getGold());
925 
926 	// should not happen, but to be sure:
927 	newMetal = std::max (newMetal, 0);
928 	newOil = std::max (newOil, 0);
929 	newGold = std::max (newGold, 0);
930 
931 	setMetal (newMetal);
932 	setOil (newOil);
933 	setGold (newGold);
934 
935 	sendSubbaseValues (server, *this, *owner);
936 }
937 
merge(sSubBase * sb)938 void sSubBase::merge (sSubBase* sb)
939 {
940 	// merge ressource allocation
941 	int metal = MetalProd;
942 	int oil = OilProd;
943 	int gold = GoldProd;
944 
945 	metal += sb->getMetalProd();
946 	gold  += sb->getGoldProd();
947 	oil   += sb->getOilProd();
948 
949 	// merge buildings
950 	for (size_t i = 0; i != sb->buildings.size(); ++i)
951 	{
952 		cBuilding* building = sb->buildings[i];
953 		addBuilding (building);
954 		building->SubBase = this;
955 	}
956 	sb->buildings.clear();
957 
958 	// set ressource allocation
959 	setMetalProd (0);
960 	setOilProd (0);
961 	setGoldProd (0);
962 
963 	setMetalProd (metal);
964 	setGoldProd (gold);
965 	setOilProd (oil);
966 
967 	// delete the subbase from the subbase list
968 	Remove (owner->base.SubBases, sb);
969 }
970 
getID() const971 int sSubBase::getID() const
972 {
973 	assert (!buildings.empty());
974 
975 	return buildings[0]->iID;
976 }
977 
addBuilding(cBuilding * b)978 void sSubBase::addBuilding (cBuilding* b)
979 {
980 	buildings.push_back (b);
981 	// calculate storage level
982 	switch (b->data.storeResType)
983 	{
984 		case sUnitData::STORE_RES_METAL:
985 			MaxMetal += b->data.storageResMax;
986 			setMetal (getMetal() + b->data.getStoredResources());
987 			break;
988 		case sUnitData::STORE_RES_OIL:
989 			MaxOil += b->data.storageResMax;
990 			setOil (getOil() + b->data.getStoredResources());
991 			break;
992 		case sUnitData::STORE_RES_GOLD:
993 			MaxGold += b->data.storageResMax;
994 			setGold (getGold() + b->data.getStoredResources());
995 			break;
996 		case sUnitData::STORE_RES_NONE:
997 			break;
998 	}
999 	// calculate energy
1000 	if (b->data.produceEnergy)
1001 	{
1002 		MaxEnergyProd += b->data.produceEnergy;
1003 		MaxOilNeed += b->data.needsOil;
1004 		if (b->isUnitWorking())
1005 		{
1006 			EnergyProd += b->data.produceEnergy;
1007 			OilNeed += b->data.needsOil;
1008 		}
1009 	}
1010 	else if (b->data.needsEnergy)
1011 	{
1012 		MaxEnergyNeed += b->data.needsEnergy;
1013 		if (b->isUnitWorking())
1014 		{
1015 			EnergyNeed += b->data.needsEnergy;
1016 		}
1017 	}
1018 	// calculate ressource consumption
1019 	if (b->data.needsMetal)
1020 	{
1021 		MaxMetalNeed += b->data.needsMetal * 12;
1022 		if (b->isUnitWorking())
1023 		{
1024 			MetalNeed += min (b->getMetalPerRound(), b->getBuildListItem (0).getRemainingMetal());
1025 		}
1026 	}
1027 	// calculate gold
1028 	if (b->data.convertsGold)
1029 	{
1030 		MaxGoldNeed += b->data.convertsGold;
1031 		if (b->isUnitWorking())
1032 		{
1033 			GoldNeed += b->data.convertsGold;
1034 		}
1035 	}
1036 	// calculate ressource production
1037 	if (b->data.canMineMaxRes > 0 && b->isUnitWorking())
1038 	{
1039 		int mineFree = b->data.canMineMaxRes;
1040 		changeMetalProd (b->MaxMetalProd);
1041 		mineFree -= b->MaxMetalProd;
1042 
1043 		changeOilProd (min (b->MaxOilProd, mineFree));
1044 		mineFree -= min (b->MaxOilProd, mineFree);
1045 
1046 		changeGoldProd (min (b->MaxGoldProd, mineFree));
1047 	}
1048 	// calculate humans
1049 	if (b->data.produceHumans)
1050 	{
1051 		HumanProd += b->data.produceHumans;
1052 	}
1053 	if (b->data.needsHumans)
1054 	{
1055 		MaxHumanNeed += b->data.needsHumans;
1056 		if (b->isUnitWorking())
1057 		{
1058 			HumanNeed += b->data.needsHumans;
1059 		}
1060 	}
1061 
1062 	// temporary debug check
1063 	if (isDitributionMaximized() == false)
1064 	{
1065 		Log.write (" Server: Mine distribution values are not a maximum", cLog::eLOG_TYPE_NET_WARNING);
1066 	}
1067 }
1068 
isDitributionMaximized() const1069 bool sSubBase::isDitributionMaximized() const
1070 {
1071 	return (getGoldProd() == getMaxAllowedGoldProd() &&
1072 			getMetalProd() == getMaxAllowedMetalProd() &&
1073 			getOilProd() == getMaxAllowedOilProd());
1074 }
1075 
pushInto(cNetMessage & message) const1076 void sSubBase::pushInto (cNetMessage& message) const
1077 {
1078 	message.pushInt16 (EnergyProd);
1079 	message.pushInt16 (EnergyNeed);
1080 	message.pushInt16 (MaxEnergyProd);
1081 	message.pushInt16 (MaxEnergyNeed);
1082 	message.pushInt16 (getMetal());
1083 	message.pushInt16 (MaxMetal);
1084 	message.pushInt16 (MetalNeed);
1085 	message.pushInt16 (MaxMetalNeed);
1086 	message.pushInt16 (getMetalProd());
1087 	message.pushInt16 (getGold());
1088 	message.pushInt16 (MaxGold);
1089 	message.pushInt16 (GoldNeed);
1090 	message.pushInt16 (MaxGoldNeed);
1091 	message.pushInt16 (getGoldProd());
1092 	message.pushInt16 (getOil());
1093 	message.pushInt16 (MaxOil);
1094 	message.pushInt16 (OilNeed);
1095 	message.pushInt16 (MaxOilNeed);
1096 	message.pushInt16 (getOilProd());
1097 	message.pushInt16 (HumanNeed);
1098 	message.pushInt16 (MaxHumanNeed);
1099 	message.pushInt16 (HumanProd);
1100 }
1101 
popFrom(cNetMessage & message)1102 void sSubBase::popFrom (cNetMessage& message)
1103 {
1104 	HumanProd = message.popInt16();
1105 	MaxHumanNeed = message.popInt16();
1106 	HumanNeed = message.popInt16();
1107 	OilProd = message.popInt16();
1108 	MaxOilNeed = message.popInt16();
1109 	OilNeed = message.popInt16();
1110 	MaxOil = message.popInt16();
1111 	setOil (message.popInt16());
1112 	GoldProd = message.popInt16();
1113 	MaxGoldNeed = message.popInt16();
1114 	GoldNeed = message.popInt16();
1115 	MaxGold = message.popInt16();
1116 	setGold (message.popInt16());
1117 	MetalProd = message.popInt16();
1118 	MaxMetalNeed = message.popInt16();
1119 	MetalNeed = message.popInt16();
1120 	MaxMetal = message.popInt16();
1121 	setMetal (message.popInt16());
1122 	MaxEnergyNeed = message.popInt16();
1123 	MaxEnergyProd = message.popInt16();
1124 	EnergyNeed = message.popInt16();
1125 	EnergyProd = message.popInt16();
1126 }
1127 
cBase()1128 cBase::cBase() : map()
1129 {}
1130 
~cBase()1131 cBase::~cBase()
1132 {
1133 	for (size_t i = 0; i != SubBases.size(); ++i)
1134 	{
1135 		delete SubBases[i];
1136 	}
1137 }
1138 
checkNeighbour(const cPosition & position,const cBuilding & building)1139 sSubBase* cBase::checkNeighbour (const cPosition& position, const cBuilding& building)
1140 {
1141 	if (map->isValidPosition (position) == false) return nullptr;
1142 	cBuilding* b = map->getField (position).getBuilding();
1143 
1144 	if (b && b->getOwner() == building.getOwner() && b->SubBase)
1145 	{
1146 		b->CheckNeighbours (*map);
1147 		return b->SubBase;
1148 	}
1149 	return nullptr;
1150 }
1151 
addBuilding(cBuilding * building,cServer * server)1152 void cBase::addBuilding (cBuilding* building, cServer* server)
1153 {
1154 	if (!building->data.connectsToBase) return;
1155 	std::vector<sSubBase*> NeighbourList;
1156 
1157 	// check for neighbours
1158 	if (building->data.isBig)
1159 	{
1160 		// big building
1161 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (0, -1), *building)) NeighbourList.push_back (SubBase);
1162 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (1, -1), *building)) NeighbourList.push_back (SubBase);
1163 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (2, 0), *building)) NeighbourList.push_back (SubBase);
1164 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (2, 1), *building)) NeighbourList.push_back (SubBase);
1165 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (0, 2), *building)) NeighbourList.push_back (SubBase);
1166 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (1, 2), *building)) NeighbourList.push_back (SubBase);
1167 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (-1, 0), *building)) NeighbourList.push_back (SubBase);
1168 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (-1, 1), *building)) NeighbourList.push_back (SubBase);
1169 	}
1170 	else
1171 	{
1172 		// small building
1173 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (0, -1), *building)) NeighbourList.push_back (SubBase);
1174 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (1, 0), *building)) NeighbourList.push_back (SubBase);
1175 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (0, 1), *building)) NeighbourList.push_back (SubBase);
1176 		if (sSubBase* SubBase = checkNeighbour (building->getPosition() + cPosition (-1, 0), *building)) NeighbourList.push_back (SubBase);
1177 	}
1178 	building->CheckNeighbours (*map);
1179 
1180 	RemoveDuplicates (NeighbourList);
1181 
1182 	if (NeighbourList.empty())
1183 	{
1184 		// no neighbours found, just generate new subbase and add the building
1185 		sSubBase* NewSubBase = new sSubBase (building->getOwner());
1186 		building->SubBase = NewSubBase;
1187 		NewSubBase->addBuilding (building);
1188 		SubBases.push_back (NewSubBase);
1189 
1190 		if (server) sendSubbaseValues (*server, *NewSubBase, *NewSubBase->owner);
1191 
1192 		return;
1193 	}
1194 
1195 	// found neighbours, so add the building to the first neighbour subbase
1196 	sSubBase* const firstNeighbour = NeighbourList[0];
1197 	firstNeighbour->addBuilding (building);
1198 	building->SubBase = firstNeighbour;
1199 	NeighbourList.erase (NeighbourList.begin());
1200 
1201 	// now merge the other neighbours to the first one, if necessary
1202 	for (size_t i = 0; i != NeighbourList.size(); ++i)
1203 	{
1204 		sSubBase* const SubBase = NeighbourList[i];
1205 		firstNeighbour->merge (SubBase);
1206 
1207 		delete SubBase;
1208 	}
1209 	NeighbourList.clear();
1210 	if (server) sendSubbaseValues (*server, *firstNeighbour, *building->getOwner());
1211 }
1212 
deleteBuilding(cBuilding * building,cServer * server)1213 void cBase::deleteBuilding (cBuilding* building, cServer* server)
1214 {
1215 	if (!building->data.connectsToBase) return;
1216 	sSubBase* sb = building->SubBase;
1217 
1218 	// remove the current subbase
1219 	for (size_t i = 0; i != sb->buildings.size(); ++i)
1220 	{
1221 		sb->buildings[i]->SubBase = nullptr;
1222 	}
1223 	Remove (SubBases, sb);
1224 
1225 	// save ressource allocation
1226 	int metal = sb->getMetalProd();
1227 	int gold = sb->getGoldProd();
1228 	int oil = sb->getOilProd();
1229 
1230 	// add all the buildings again
1231 	for (size_t i = 0; i != sb->buildings.size(); ++i)
1232 	{
1233 		cBuilding* n = sb->buildings[i];
1234 		if (n == building) continue;
1235 		addBuilding (n, nullptr);
1236 	}
1237 
1238 	//generate list, with the new subbases
1239 	std::vector<sSubBase*> newSubBases;
1240 	for (size_t i = 0; i != sb->buildings.size(); ++i)
1241 	{
1242 		cBuilding* n = sb->buildings[i];
1243 		if (n == building) continue;
1244 		newSubBases.push_back (n->SubBase);
1245 	}
1246 	RemoveDuplicates (newSubBases);
1247 
1248 	// try to restore ressource allocation
1249 	for (size_t i = 0; i != newSubBases.size(); ++i)
1250 	{
1251 		sSubBase& subBase = *newSubBases[i];
1252 
1253 		subBase.setMetalProd (metal);
1254 		subBase.setGoldProd (gold);
1255 		subBase.setOilProd (oil);
1256 
1257 		metal -= subBase.getMetalProd();
1258 		gold  -= subBase.getGoldProd();
1259 		oil   -= subBase.getOilProd();
1260 
1261 		subBase.setMetalProd (subBase.getMaxAllowedMetalProd());
1262 		subBase.setGoldProd (subBase.getMaxAllowedGoldProd());
1263 		subBase.setOilProd (subBase.getMaxAllowedOilProd());
1264 	}
1265 
1266 	if (building->isUnitWorking() && building->data.canResearch)
1267 		building->getOwner()->stopAResearch (building->getResearchArea());
1268 
1269 	if (server)
1270 	{
1271 		// send subbase values to client
1272 		for (size_t i = 0; i != newSubBases.size(); ++i)
1273 		{
1274 			sendSubbaseValues (*server, *newSubBases[i], *building->getOwner());
1275 		}
1276 	}
1277 
1278 	delete sb;
1279 }
1280 
checkTurnEnd(cServer & server)1281 bool cBase::checkTurnEnd (cServer& server)
1282 {
1283 	bool changed = false;
1284 	for (size_t i = 0; i != SubBases.size(); ++i)
1285 	{
1286 		if (SubBases[i]->checkTurnEnd (server))
1287 		{
1288 			changed = true;
1289 		}
1290 	}
1291 	return changed;
1292 }
1293 
makeTurnStart(cServer & server)1294 void cBase::makeTurnStart (cServer& server)
1295 {
1296 	for (size_t i = 0; i != SubBases.size(); ++i)
1297 	{
1298 		SubBases[i]->makeTurnStart (server);
1299 	}
1300 }
1301 
1302 // recalculates all subbase values (after a load)
refreshSubbases()1303 void cBase::refreshSubbases()
1304 {
1305 	for (size_t i = 0; i != SubBases.size(); ++i)
1306 	{
1307 		SubBases[i]->refresh();
1308 	}
1309 }
1310