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