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 <cmath>
20
21 #include "game/data/player/player.h"
22
23 #include "game/data/units/building.h"
24 #include "game/logic/client.h"
25 #include "utility/listhelpers.h"
26 #include "netmessage.h"
27 #include "game/logic/server.h"
28 #include "game/logic/serverevents.h"
29 #include "game/data/units/vehicle.h"
30 #include "game/data/report/savedreport.h"
31 #include "game/logic/turnclock.h"
32
33 using namespace std;
34
35 //------------------------------------------------------------------------------
36 // Implementation cPlayer class
37 //------------------------------------------------------------------------------
38
39 //------------------------------------------------------------------------------
cPlayer(const cPlayerBasicData & splayer_)40 cPlayer::cPlayer (const cPlayerBasicData& splayer_) :
41 splayer (splayer_),
42 landingPosX (-1),
43 landingPosY (-1),
44 numEcos (0),
45 lastDeletedUnit (0),
46 clan (-1),
47 hasFinishedTurn (false),
48 isRemovedFromGame (false)
49 {
50 // get the default (no clan) unit data
51 VehicleData = UnitsData.getUnitData_Vehicles (-1);
52 BuildingData = UnitsData.getUnitData_Buildings (-1);
53
54 researchCentersWorkingTotal = 0;
55 for (int i = 0; i < cResearch::kNrResearchAreas; i++)
56 researchCentersWorkingOnArea[i] = 0;
57 credits = 0;
58
59 isDefeated = false;
60
61 splayer.nameChanged.connect ([this]() { nameChanged(); });
62 splayer.colorChanged.connect ([this]() { colorChanged(); });
63 }
64
65 //------------------------------------------------------------------------------
~cPlayer()66 cPlayer::~cPlayer()
67 {}
68
69 //------------------------------------------------------------------------------
setClan(int newClan)70 void cPlayer::setClan (int newClan)
71 {
72 if (newClan == clan || newClan < -1 || 7 < newClan)
73 return;
74
75 clan = newClan;
76
77 VehicleData = UnitsData.getUnitData_Vehicles (clan);
78 BuildingData = UnitsData.getUnitData_Buildings (clan);
79 }
80
81 //------------------------------------------------------------------------------
getCredits() const82 int cPlayer::getCredits() const
83 {
84 return credits;
85 }
86
87 //------------------------------------------------------------------------------
setCredits(int credits_)88 void cPlayer::setCredits (int credits_)
89 {
90 std::swap (credits, credits_);
91 if (credits != credits_) creditsChanged();
92 }
93
94 //------------------------------------------------------------------------------
getUnitDataCurrentVersion(const sID & id)95 sUnitData* cPlayer::getUnitDataCurrentVersion (const sID& id)
96 {
97 const cPlayer* constMe = this;
98 return const_cast<sUnitData*> (constMe->getUnitDataCurrentVersion (id));
99 }
100
101 //------------------------------------------------------------------------------
getUnitDataCurrentVersion(const sID & id) const102 const sUnitData* cPlayer::getUnitDataCurrentVersion (const sID& id) const
103 {
104 if (id.isAVehicle())
105 {
106 for (size_t i = 0; i != VehicleData.size(); ++i)
107 {
108 if (VehicleData[i].ID == id) return &VehicleData[i];
109 }
110 }
111 else if (id.isABuilding())
112 {
113 for (unsigned int i = 0; i < BuildingData.size(); ++i)
114 {
115 if (BuildingData[i].ID == id) return &BuildingData[i];
116 }
117 }
118 return nullptr;
119 }
120
121 //------------------------------------------------------------------------------
122 /** initialize the maps */
123 //------------------------------------------------------------------------------
initMaps(cMap & map)124 void cPlayer::initMaps (cMap& map)
125 {
126 mapSize = map.getSize();
127 const int size = mapSize.x() * mapSize.y();
128 // Scanner-Map:
129 ScanMap.clear();
130 ScanMap.resize (size, 0);
131 // Ressource-Map
132 ResourceMap.clear();
133 ResourceMap.resize (size, 0);
134
135 base.map = ↦
136 // Sentry-Map:
137 SentriesMapAir.clear();
138 SentriesMapAir.resize (size, 0);
139 SentriesMapGround.clear();
140 SentriesMapGround.resize (size, 0);
141
142 // Detect-Maps:
143 DetectLandMap.clear();
144 DetectLandMap.resize (size, 0);
145 DetectSeaMap.clear();
146 DetectSeaMap.resize (size, 0);
147 DetectMinesMap.clear();
148 DetectMinesMap.resize (size, 0);
149 }
150
getMapSize() const151 const cPosition& cPlayer::getMapSize() const
152 {
153 return mapSize;
154 }
155
156 //------------------------------------------------------------------------------
addNewVehicle(const cPosition & position,const sID & id,unsigned int uid)157 cVehicle& cPlayer::addNewVehicle (const cPosition& position, const sID& id, unsigned int uid)
158 {
159 const sUnitData& unitData = *id.getUnitDataOriginalVersion (this);
160 auto vehicle = std::make_shared<cVehicle> (unitData, this, uid);
161 vehicle->setPosition (position);
162
163 drawSpecialCircle (vehicle->getPosition(), vehicle->data.getScan(), ScanMap, mapSize);
164 if (vehicle->data.canDetectStealthOn & TERRAIN_GROUND) drawSpecialCircle (vehicle->getPosition(), vehicle->data.getScan(), DetectLandMap, mapSize);
165 if (vehicle->data.canDetectStealthOn & TERRAIN_SEA) drawSpecialCircle (vehicle->getPosition(), vehicle->data.getScan(), DetectSeaMap, mapSize);
166 if (vehicle->data.canDetectStealthOn & AREA_EXP_MINE)
167 {
168 const int minx = std::max (vehicle->getPosition().x() - 1, 0);
169 const int maxx = std::min (vehicle->getPosition().x() + 1, mapSize.x() - 1);
170 const int miny = std::max (vehicle->getPosition().y() - 1, 0);
171 const int maxy = std::min (vehicle->getPosition().y() + 1, mapSize.y() - 1);
172 for (int x = minx; x <= maxx; ++x)
173 for (int y = miny; y <= maxy; ++y)
174 DetectMinesMap[x + mapSize.x() * y] = 1;
175 }
176
177 auto result = vehicles.insert (std::move (vehicle));
178 assert (result.second);
179
180 return * (*result.first);
181 }
182
183 //------------------------------------------------------------------------------
addNewBuilding(const cPosition & position,const sID & id,unsigned int uid)184 cBuilding& cPlayer::addNewBuilding (const cPosition& position, const sID& id, unsigned int uid)
185 {
186 const sUnitData* unitData = id.getUnitDataOriginalVersion (this);
187 auto building = std::make_shared<cBuilding> (unitData, this, uid);
188
189 building->setPosition (position);
190
191 if (building->data.getScan())
192 {
193 if (building->data.isBig) drawSpecialCircleBig (building->getPosition(), building->data.getScan(), ScanMap, mapSize);
194 else drawSpecialCircle (building->getPosition(), building->data.getScan(), ScanMap, mapSize);
195 }
196
197 auto result = buildings.insert (std::move (building));
198 assert (result.second);
199 return * (*result.first);
200 }
201
202 //------------------------------------------------------------------------------
addUnit(std::shared_ptr<cVehicle> vehicle)203 void cPlayer::addUnit (std::shared_ptr<cVehicle> vehicle)
204 {
205 vehicles.insert (std::move (vehicle));
206 }
207
208 //------------------------------------------------------------------------------
addUnit(std::shared_ptr<cBuilding> building)209 void cPlayer::addUnit (std::shared_ptr<cBuilding> building)
210 {
211 buildings.insert (std::move (building));
212 }
213
214 //------------------------------------------------------------------------------
removeUnit(const cBuilding & building)215 std::shared_ptr<cBuilding> cPlayer::removeUnit (const cBuilding& building)
216 {
217 auto iter = buildings.find (building);
218 if (iter == buildings.end()) return nullptr;
219
220 auto removed = *iter;
221 buildings.erase (iter);
222 return removed;
223 }
224
225 //------------------------------------------------------------------------------
removeUnit(const cVehicle & vehicle)226 std::shared_ptr<cVehicle> cPlayer::removeUnit (const cVehicle& vehicle)
227 {
228 auto iter = vehicles.find (vehicle);
229 if (iter == vehicles.end()) return nullptr;
230
231 auto removed = *iter;
232 vehicles.erase (iter);
233 return removed;
234 }
235
236 //------------------------------------------------------------------------------
removeAllUnits()237 void cPlayer::removeAllUnits()
238 {
239 vehicles.clear();
240 buildings.clear();
241 }
242
243 //------------------------------------------------------------------------------
getVehicleFromId(unsigned int id) const244 cVehicle* cPlayer::getVehicleFromId (unsigned int id) const
245 {
246 auto iter = vehicles.find (id);
247 return iter == vehicles.end() ? nullptr : (*iter).get();
248 }
249
250 //------------------------------------------------------------------------------
getBuildingFromId(unsigned int id) const251 cBuilding* cPlayer::getBuildingFromId (unsigned int id) const
252 {
253 auto iter = buildings.find (id);
254 return iter == buildings.end() ? nullptr : (*iter).get();
255 }
256
257 //------------------------------------------------------------------------------
getVehicles() const258 const cFlatSet<std::shared_ptr<cVehicle>, sUnitLess<cVehicle>>& cPlayer::getVehicles() const
259 {
260 return vehicles;
261 }
262
263 //------------------------------------------------------------------------------
getBuildings() const264 const cFlatSet<std::shared_ptr<cBuilding>, sUnitLess<cBuilding>>& cPlayer::getBuildings() const
265 {
266 return buildings;
267 }
268
269 //------------------------------------------------------------------------------
addSentry(cUnit & u)270 void cPlayer::addSentry (cUnit& u)
271 {
272 u.setSentryActive (true);
273 if (u.data.canAttack & TERRAIN_AIR)
274 {
275 drawSpecialCircle (u.getPosition(), u.data.getRange(), SentriesMapAir, mapSize);
276 }
277 if ((u.data.canAttack & TERRAIN_GROUND) || (u.data.canAttack & TERRAIN_SEA))
278 {
279 drawSpecialCircle (u.getPosition(), u.data.getRange(), SentriesMapGround, mapSize);
280 }
281 }
282
283 //------------------------------------------------------------------------------
deleteSentry(cUnit & u)284 void cPlayer::deleteSentry (cUnit& u)
285 {
286 u.setSentryActive (false);
287 if (u.data.canAttack & TERRAIN_AIR)
288 {
289 refreshSentryAir();
290 }
291 else if ((u.data.canAttack & TERRAIN_GROUND) || (u.data.canAttack & TERRAIN_SEA))
292 {
293 refreshSentryGround();
294 }
295 }
296
297 //------------------------------------------------------------------------------
refreshSentryAir()298 void cPlayer::refreshSentryAir()
299 {
300 std::fill (SentriesMapAir.begin(), SentriesMapAir.end(), 0);
301
302 for (auto i = vehicles.begin(); i != vehicles.end(); ++i)
303 {
304 const auto& unit = *i;
305 if (unit->isSentryActive() && unit->data.canAttack & TERRAIN_AIR)
306 {
307 drawSpecialCircle (unit->getPosition(), unit->data.getRange(), SentriesMapAir, mapSize);
308 }
309 }
310
311 for (auto i = buildings.begin(); i != buildings.end(); ++i)
312 {
313 const auto& unit = *i;
314 if (unit->isSentryActive() && unit->data.canAttack & TERRAIN_AIR)
315 {
316 drawSpecialCircle (unit->getPosition(), unit->data.getRange(), SentriesMapAir, mapSize);
317 }
318 }
319 }
320
321 //------------------------------------------------------------------------------
refreshSentryGround()322 void cPlayer::refreshSentryGround()
323 {
324 std::fill (SentriesMapGround.begin(), SentriesMapGround.end(), 0);
325
326 for (auto i = vehicles.begin(); i != vehicles.end(); ++i)
327 {
328 const auto& unit = *i;
329 if (unit->isSentryActive() && ((unit->data.canAttack & TERRAIN_GROUND) || (unit->data.canAttack & TERRAIN_SEA)))
330 {
331 drawSpecialCircle (unit->getPosition(), unit->data.getRange(), SentriesMapGround, mapSize);
332 }
333 }
334 for (auto i = buildings.begin(); i != buildings.end(); ++i)
335 {
336 const auto& unit = *i;
337 if (unit->isSentryActive() && ((unit->data.canAttack & TERRAIN_GROUND) || (unit->data.canAttack & TERRAIN_SEA)))
338 {
339 drawSpecialCircle (unit->getPosition(), unit->data.getRange(), SentriesMapGround, mapSize);
340 }
341 }
342 }
343
344 //------------------------------------------------------------------------------
345 /** Does a scan for all units of the player */
346 //------------------------------------------------------------------------------
doScan()347 void cPlayer::doScan()
348 {
349 if (isDefeated) return;
350 std::fill (ScanMap.begin(), ScanMap.end(), 0);
351 std::fill (DetectLandMap.begin(), DetectLandMap.end(), 0);
352 std::fill (DetectSeaMap.begin(), DetectSeaMap.end(), 0);
353 std::fill (DetectMinesMap.begin(), DetectMinesMap.end(), 0);
354
355 // iterate the vehicle list
356 for (auto i = vehicles.begin(); i != vehicles.end(); ++i)
357 {
358 const auto& vp = *i;
359 if (vp->isUnitLoaded()) continue;
360
361 if (vp->isDisabled())
362 ScanMap[getOffset (vp->getPosition())] = 1;
363 else
364 {
365 if (vp->data.isBig)
366 drawSpecialCircleBig (vp->getPosition(), vp->data.getScan(), ScanMap, mapSize);
367 else
368 drawSpecialCircle (vp->getPosition(), vp->data.getScan(), ScanMap, mapSize);
369
370 //detection maps
371 if (vp->data.canDetectStealthOn & TERRAIN_GROUND) drawSpecialCircle (vp->getPosition(), vp->data.getScan(), DetectLandMap, mapSize);
372 else if (vp->data.canDetectStealthOn & TERRAIN_SEA) drawSpecialCircle (vp->getPosition(), vp->data.getScan(), DetectSeaMap, mapSize);
373 if (vp->data.canDetectStealthOn & AREA_EXP_MINE)
374 {
375 const int minx = std::max (vp->getPosition().x() - 1, 0);
376 const int maxx = std::min (vp->getPosition().x() + 1, mapSize.x() - 1);
377 const int miny = std::max (vp->getPosition().y() - 1, 0);
378 const int maxy = std::min (vp->getPosition().y() + 1, mapSize.y() - 1);
379 for (int x = minx; x <= maxx; ++x)
380 {
381 for (int y = miny; y <= maxy; ++y)
382 {
383 DetectMinesMap[x + mapSize.x() * y] = 1;
384 }
385 }
386 }
387 }
388 }
389
390 // iterate the building list
391 for (auto i = buildings.begin(); i != buildings.end(); ++i)
392 {
393 const auto& bp = *i;
394 if (bp->isDisabled())
395 ScanMap[getOffset (bp->getPosition())] = 1;
396 else if (bp->data.getScan())
397 {
398 if (bp->data.isBig)
399 drawSpecialCircleBig (bp->getPosition(), bp->data.getScan(), ScanMap, mapSize);
400 else
401 drawSpecialCircle (bp->getPosition(), bp->data.getScan(), ScanMap, mapSize);
402 }
403 }
404 }
405
revealMap()406 void cPlayer::revealMap()
407 {
408 std::fill (ScanMap.begin(), ScanMap.end(), 1);
409 }
410
revealPosition(const cPosition & position)411 void cPlayer::revealPosition (const cPosition& position)
412 {
413 if (position.x() < 0 || position.x() >= mapSize.x() || position.y() < 0 || position.y() >= mapSize.y()) return;
414
415 ScanMap[getOffset (position)] = 1;
416 }
417
revealResource()418 void cPlayer::revealResource()
419 {
420 std::fill (ResourceMap.begin(), ResourceMap.end(), 1);
421 }
422
canSeeAnyAreaUnder(const cUnit & unit) const423 bool cPlayer::canSeeAnyAreaUnder (const cUnit& unit) const
424 {
425 if (canSeeAt (unit.getPosition())) return true;
426 if (!unit.data.isBig) return false;
427
428 return canSeeAt (unit.getPosition() + cPosition (0, 1)) ||
429 canSeeAt (unit.getPosition() + cPosition (1, 1)) ||
430 canSeeAt (unit.getPosition() + cPosition (1, 0));
431 }
432
getNextVehicle(cVehicle * start) const433 cVehicle* cPlayer::getNextVehicle (cVehicle* start) const
434 {
435 if (vehicles.empty()) return nullptr;
436
437 auto it = (start == nullptr) ? vehicles.begin() : vehicles.find (*start);
438 if (start != nullptr && it != vehicles.end()) ++it;
439 for (; it != vehicles.end(); ++it)
440 {
441 if (! (*it)->isMarkedAsDone() && (! (*it)->isUnitBuildingABuilding() || (*it)->getBuildTurns() == 0)
442 && ! (*it)->isUnitClearing() && ! (*it)->isSentryActive() && ! (*it)->isUnitLoaded()
443 && ((*it)->data.getSpeed() || (*it)->data.getShots()))
444 {
445 return it->get();
446 }
447 }
448 return nullptr;
449 }
450
getNextBuilding(cBuilding * start) const451 cBuilding* cPlayer::getNextBuilding (cBuilding* start) const
452 {
453 if (buildings.empty()) return nullptr;
454
455 auto it = (start == nullptr) ? buildings.begin() : buildings.find (*start);
456 if (start != nullptr && it != buildings.end()) ++it;
457 for (; it != buildings.end(); ++it)
458 {
459 if (! (*it)->isMarkedAsDone() && ! (*it)->isUnitWorking() && ! (*it)->isSentryActive()
460 && (! (*it)->data.canBuild.empty() || (*it)->data.getShots()
461 || (*it)->data.canMineMaxRes > 0 || (*it)->data.convertsGold > 0
462 || (*it)->data.canResearch))
463 {
464 return it->get();
465 }
466 }
467 return nullptr;
468 }
469
getNextMiningStation(cBuilding * start) const470 cBuilding* cPlayer::getNextMiningStation (cBuilding* start) const
471 {
472 if (buildings.empty()) return nullptr;
473
474 auto it = (start == nullptr) ? buildings.begin() : buildings.find (*start);
475 if (start != nullptr && it != buildings.end()) ++it;
476 for (; it != buildings.end(); ++it)
477 {
478 if ((*it)->data.canMineMaxRes > 0)
479 {
480 return it->get();
481 }
482 }
483 return nullptr;
484 }
485
486 //------------------------------------------------------------------------------
487 /** Returns the next unit that can still fire/shoot */
488 //------------------------------------------------------------------------------
getNextUnit(cUnit * start) const489 cUnit* cPlayer::getNextUnit (cUnit* start) const
490 {
491 if (start == nullptr || start->getOwner() != this)
492 {
493 cVehicle* nextVehicle = getNextVehicle (nullptr);
494 if (nextVehicle) return nextVehicle;
495 cBuilding* nextBuilding = getNextBuilding (nullptr);
496 if (nextBuilding) return nextBuilding;
497 }
498 else if (start->isAVehicle())
499 {
500 cVehicle* nextVehicle = getNextVehicle (static_cast<cVehicle*> (start));
501 if (nextVehicle) return nextVehicle;
502 cBuilding* nextBuilding = getNextBuilding (nullptr);
503 if (nextBuilding) return nextBuilding;
504 nextVehicle = getNextVehicle (nullptr);
505 if (nextVehicle) return nextVehicle;
506 }
507 else
508 {
509 assert (start->isABuilding());
510 cBuilding* building = static_cast<cBuilding*> (start);
511 cBuilding* nextBuilding = getNextBuilding (building);
512 if (nextBuilding) return nextBuilding;
513 cVehicle* nextVehicle = getNextVehicle (nullptr);
514 if (nextVehicle) return nextVehicle;
515 nextBuilding = getNextBuilding (nullptr);
516 if (nextBuilding) return nextBuilding;
517 }
518 // finally, return the more recent built Mining station.
519 // since list order is by increasing age, take the first in list.
520 return getNextMiningStation (nullptr);
521 }
522
getPrevVehicle(cVehicle * start) const523 cVehicle* cPlayer::getPrevVehicle (cVehicle* start) const
524 {
525 if (vehicles.empty()) return nullptr;
526
527 auto it = (start == nullptr) ? vehicles.end() - 1 : vehicles.find (*start);
528 if (start != nullptr && it != vehicles.begin() && it != vehicles.end()) --it;
529 for (; it != vehicles.end(); --it)
530 {
531 if (! (*it)->isMarkedAsDone() && (! (*it)->isUnitBuildingABuilding() || (*it)->getBuildTurns() == 0)
532 && ! (*it)->isUnitClearing() && ! (*it)->isSentryActive() && ! (*it)->isUnitLoaded()
533 && ((*it)->data.getSpeed() || (*it)->data.getShots()))
534 {
535 return it->get();
536 }
537 if (it == vehicles.begin()) break;
538 }
539 return nullptr;
540 }
541
getPrevBuilding(cBuilding * start) const542 cBuilding* cPlayer::getPrevBuilding (cBuilding* start) const
543 {
544 if (buildings.empty()) return nullptr;
545
546 auto it = (start == nullptr) ? buildings.end() - 1 : buildings.find (*start);
547 if (start != nullptr && it != buildings.begin() && it != buildings.end()) --it;
548 for (; it != buildings.end(); --it)
549 {
550 if (! (*it)->isMarkedAsDone() && ! (*it)->isUnitWorking() && ! (*it)->isSentryActive()
551 && (! (*it)->data.canBuild.empty() || (*it)->data.getShots()
552 || (*it)->data.canMineMaxRes > 0 || (*it)->data.convertsGold > 0
553 || (*it)->data.canResearch))
554 {
555 return it->get();
556 }
557 if (it == buildings.begin()) break;
558 }
559 return nullptr;
560 }
561
getPrevMiningStation(cBuilding * start) const562 cBuilding* cPlayer::getPrevMiningStation (cBuilding* start) const
563 {
564 if (buildings.empty()) return nullptr;
565
566 auto it = (start == nullptr) ? buildings.end() - 1 : buildings.find (*start);
567 for (; it != buildings.end(); --it)
568 {
569 if ((*it)->data.canMineMaxRes > 0)
570 {
571 return it->get();
572 }
573 if (it == buildings.begin()) break;
574 }
575 return nullptr;
576 }
577
578 //------------------------------------------------------------------------------
579 /** Returns the previous vehicle, that can still move / shoot */
580 //------------------------------------------------------------------------------
getPrevUnit(cUnit * start) const581 cUnit* cPlayer::getPrevUnit (cUnit* start) const
582 {
583 if (start == nullptr || start->getOwner() != this)
584 {
585 cVehicle* prevVehicle = getPrevVehicle (nullptr);
586 if (prevVehicle) return prevVehicle;
587 cBuilding* prevBuilding = getPrevBuilding (nullptr);
588 if (prevBuilding) return prevBuilding;
589 }
590 else if (start->isAVehicle())
591 {
592 cVehicle* prevVehicle = getPrevVehicle (static_cast<cVehicle*> (start));
593 if (prevVehicle) return prevVehicle;
594 cBuilding* prevBuilding = getPrevBuilding (nullptr);
595 if (prevBuilding) return prevBuilding;
596 prevVehicle = getPrevVehicle (nullptr);
597 if (prevVehicle) return prevVehicle;
598 }
599 else
600 {
601 assert (start->isABuilding());
602 cBuilding* building = static_cast<cBuilding*> (start);
603 cBuilding* prevBuilding = getPrevBuilding (building);
604 if (prevBuilding) return prevBuilding;
605 cVehicle* prevVehicle = getPrevVehicle (nullptr);
606 if (prevVehicle) return prevVehicle;
607 prevBuilding = getPrevBuilding (nullptr);
608 if (prevBuilding) return prevBuilding;
609 }
610 // finally, return the more recent built Mining station.
611 // since list order is by increasing age, take the first in list.
612 return getNextMiningStation (nullptr);
613 }
614
615 //------------------------------------------------------------------------------
hasUnits() const616 bool cPlayer::hasUnits() const
617 {
618 return !vehicles.empty() || !buildings.empty();
619 }
620
621 //------------------------------------------------------------------------------
622 /** Starts a research center. */
623 //------------------------------------------------------------------------------
startAResearch(cResearch::ResearchArea researchArea)624 void cPlayer::startAResearch (cResearch::ResearchArea researchArea)
625 {
626 if (0 <= researchArea && researchArea <= cResearch::kNrResearchAreas)
627 {
628 ++researchCentersWorkingTotal;
629 ++researchCentersWorkingOnArea[researchArea];
630
631 researchCentersWorkingOnAreaChanged (researchArea);
632 researchCentersWorkingTotalChanged();
633 }
634 }
635
636 //------------------------------------------------------------------------------
637 /** Stops a research center. */
638 //------------------------------------------------------------------------------
stopAResearch(cResearch::ResearchArea researchArea)639 void cPlayer::stopAResearch (cResearch::ResearchArea researchArea)
640 {
641 if (0 <= researchArea && researchArea <= cResearch::kNrResearchAreas)
642 {
643 --researchCentersWorkingTotal;
644 if (researchCentersWorkingOnArea[researchArea] > 0)
645 {
646 --researchCentersWorkingOnArea[researchArea];
647 researchCentersWorkingOnAreaChanged (researchArea);
648 }
649 researchCentersWorkingTotalChanged();
650 }
651 }
652
653 //------------------------------------------------------------------------------
654 /** At turn end update the research level */
655 //------------------------------------------------------------------------------
doResearch(cServer & server)656 void cPlayer::doResearch (cServer& server)
657 {
658 bool researchFinished = false;
659 std::vector<sUnitData*> upgradedUnitDatas;
660 std::vector<int> areasReachingNextLevel;
661 currentTurnResearchAreasFinished.clear();
662 for (int area = 0; area < cResearch::kNrResearchAreas; ++area)
663 {
664 if (researchCentersWorkingOnArea[area] > 0 &&
665 researchState.doResearch (researchCentersWorkingOnArea[area], area))
666 {
667 // next level reached
668 areasReachingNextLevel.push_back (area);
669 currentTurnResearchAreasFinished.push_back (area);
670 researchFinished = true;
671 }
672 }
673 if (researchFinished)
674 {
675 upgradeUnitTypes (areasReachingNextLevel, upgradedUnitDatas);
676
677 for (size_t i = 0; i != upgradedUnitDatas.size(); ++i)
678 sendUnitUpgrades (server, *upgradedUnitDatas[i], *this);
679 }
680 sendResearchLevel (server, researchState, *this);
681 sendFinishedResearchAreas (server, currentTurnResearchAreasFinished, *this);
682 }
683
accumulateScore(cServer & server)684 void cPlayer::accumulateScore (cServer& server)
685 {
686 const int now = server.getTurnClock()->getTurn();
687 int deltaScore = 0;
688
689 for (auto i = buildings.begin(); i != buildings.end(); ++i)
690 {
691 const auto& bp = *i;
692 if (bp->data.canScore && bp->isUnitWorking())
693 {
694 bp->points++;
695 deltaScore++;
696
697 sendUnitScore (server, *bp);
698 }
699 }
700 setScore (getScore (now) + deltaScore, now);
701 sendScore (server, *this, now);
702 }
703
countEcoSpheres()704 void cPlayer::countEcoSpheres()
705 {
706 numEcos = 0;
707
708 for (auto i = buildings.begin(); i != buildings.end(); ++i)
709 {
710 const auto& bp = *i;
711 if (bp->data.canScore && bp->isUnitWorking())
712 ++numEcos;
713 }
714 }
715
setScore(int s,int turn)716 void cPlayer::setScore (int s, int turn)
717 {
718 // turn begins at 1.
719 unsigned int t = turn;
720
721 if (pointsHistory.size() < t)
722 pointsHistory.resize (t);
723 pointsHistory[t - 1] = s;
724 }
725
clearDone()726 void cPlayer::clearDone()
727 {
728 for (auto i = vehicles.begin(); i != vehicles.end(); ++i)
729 {
730 const auto& unit = *i;
731 unit->setMarkedAsDone (false);
732 }
733
734 for (auto i = buildings.begin(); i != buildings.end(); ++i)
735 {
736 const auto& unit = *i;
737 unit->setMarkedAsDone (false);
738 }
739 }
740
getScore(int turn) const741 int cPlayer::getScore (int turn) const
742 {
743 // turn begins at 1.
744 unsigned int t = turn;
745
746 if (pointsHistory.size() < t)
747 {
748 const int score = pointsHistory.empty() ? 0 : pointsHistory.back();
749 pointsHistory.resize (t, score);
750 }
751 return pointsHistory[t - 1];
752 }
753
getScore() const754 int cPlayer::getScore() const
755 {
756 return pointsHistory.back();
757 }
758
759 //------------------------------------------------------------------------------
upgradeUnitTypes(const std::vector<int> & areasReachingNextLevel,std::vector<sUnitData * > & resultUpgradedUnitDatas)760 void cPlayer::upgradeUnitTypes (const std::vector<int>& areasReachingNextLevel, std::vector<sUnitData*>& resultUpgradedUnitDatas)
761 {
762 for (unsigned int i = 0; i < UnitsData.getNrVehicles(); i++)
763 {
764 const sUnitData& originalData = UnitsData.getVehicle (i, getClan());
765 bool incrementVersion = false;
766 for (size_t areaCounter = 0; areaCounter != areasReachingNextLevel.size(); areaCounter++)
767 {
768 const int researchArea = areasReachingNextLevel[areaCounter];
769 const int newResearchLevel = researchState.getCurResearchLevel (researchArea);
770 int startValue = 0;
771 switch (researchArea)
772 {
773 case cResearch::kAttackResearch: startValue = originalData.getDamage(); break;
774 case cResearch::kShotsResearch: startValue = originalData.getShotsMax(); break;
775 case cResearch::kRangeResearch: startValue = originalData.getRange(); break;
776 case cResearch::kArmorResearch: startValue = originalData.getArmor(); break;
777 case cResearch::kHitpointsResearch: startValue = originalData.getHitpointsMax(); break;
778 case cResearch::kScanResearch: startValue = originalData.getScan(); break;
779 case cResearch::kSpeedResearch: startValue = originalData.getSpeedMax(); break;
780 case cResearch::kCostResearch: startValue = originalData.buildCosts; break;
781 }
782 int oldResearchBonus = cUpgradeCalculator::instance().calcChangeByResearch (startValue, newResearchLevel - 10,
783 researchArea == cResearch::kCostResearch ? cUpgradeCalculator::kCost : -1,
784 originalData.isHuman ? cUpgradeCalculator::kInfantry : cUpgradeCalculator::kStandardUnit);
785 int newResearchBonus = cUpgradeCalculator::instance().calcChangeByResearch (startValue, newResearchLevel,
786 researchArea == cResearch::kCostResearch ? cUpgradeCalculator::kCost : -1,
787 originalData.isHuman ? cUpgradeCalculator::kInfantry : cUpgradeCalculator::kStandardUnit);
788 if (oldResearchBonus != newResearchBonus)
789 {
790 switch (researchArea)
791 {
792 case cResearch::kAttackResearch: VehicleData[i].setDamage (VehicleData[i].getDamage() + newResearchBonus - oldResearchBonus); break;
793 case cResearch::kShotsResearch: VehicleData[i].setShotsMax (VehicleData[i].getShotsMax() + newResearchBonus - oldResearchBonus); break;
794 case cResearch::kRangeResearch: VehicleData[i].setRange (VehicleData[i].getRange() + newResearchBonus - oldResearchBonus); break;
795 case cResearch::kArmorResearch: VehicleData[i].setArmor (VehicleData[i].getArmor() + newResearchBonus - oldResearchBonus); break;
796 case cResearch::kHitpointsResearch: VehicleData[i].setHitpointsMax (VehicleData[i].getHitpointsMax() + newResearchBonus - oldResearchBonus); break;
797 case cResearch::kScanResearch: VehicleData[i].setScan (VehicleData[i].getScan() + newResearchBonus - oldResearchBonus); break;
798 case cResearch::kSpeedResearch: VehicleData[i].setSpeedMax (VehicleData[i].getSpeedMax() + newResearchBonus - oldResearchBonus); break;
799 case cResearch::kCostResearch: VehicleData[i].buildCosts += newResearchBonus - oldResearchBonus; break;
800 }
801 if (researchArea != cResearch::kCostResearch) // don't increment the version, if the only change are the costs
802 incrementVersion = true;
803 if (!Contains (resultUpgradedUnitDatas, & (VehicleData[i])))
804 resultUpgradedUnitDatas.push_back (& (VehicleData[i]));
805 }
806 }
807 if (incrementVersion)
808 VehicleData[i].setVersion (VehicleData[i].getVersion() + 1);
809 }
810
811 for (unsigned int i = 0; i < UnitsData.getNrBuildings(); i++)
812 {
813 const sUnitData& originalData = UnitsData.getBuilding (i, getClan());
814 bool incrementVersion = false;
815 for (size_t areaCounter = 0; areaCounter != areasReachingNextLevel.size(); areaCounter++)
816 {
817 const int researchArea = areasReachingNextLevel[areaCounter];
818 const int newResearchLevel = researchState.getCurResearchLevel (researchArea);
819
820 int startValue = 0;
821 switch (researchArea)
822 {
823 case cResearch::kAttackResearch: startValue = originalData.getDamage(); break;
824 case cResearch::kShotsResearch: startValue = originalData.getShotsMax(); break;
825 case cResearch::kRangeResearch: startValue = originalData.getRange(); break;
826 case cResearch::kArmorResearch: startValue = originalData.getArmor(); break;
827 case cResearch::kHitpointsResearch: startValue = originalData.getHitpointsMax(); break;
828 case cResearch::kScanResearch: startValue = originalData.getScan(); break;
829 case cResearch::kCostResearch: startValue = originalData.buildCosts; break;
830 }
831 int oldResearchBonus = cUpgradeCalculator::instance().calcChangeByResearch (startValue, newResearchLevel - 10,
832 researchArea == cResearch::kCostResearch ? cUpgradeCalculator::kCost : -1,
833 cUpgradeCalculator::kBuilding);
834 int newResearchBonus = cUpgradeCalculator::instance().calcChangeByResearch (startValue, newResearchLevel,
835 researchArea == cResearch::kCostResearch ? cUpgradeCalculator::kCost : -1,
836 cUpgradeCalculator::kBuilding);
837 if (oldResearchBonus != newResearchBonus)
838 {
839 switch (researchArea)
840 {
841 case cResearch::kAttackResearch: BuildingData[i].setDamage (BuildingData[i].getDamage() + newResearchBonus - oldResearchBonus); break;
842 case cResearch::kShotsResearch: BuildingData[i].setShotsMax (BuildingData[i] .getShotsMax() + newResearchBonus - oldResearchBonus); break;
843 case cResearch::kRangeResearch: BuildingData[i].setRange (BuildingData[i].getRange() + newResearchBonus - oldResearchBonus); break;
844 case cResearch::kArmorResearch: BuildingData[i].setArmor (BuildingData[i].getArmor() + newResearchBonus - oldResearchBonus); break;
845 case cResearch::kHitpointsResearch: BuildingData[i].setHitpointsMax (BuildingData[i].getHitpointsMax() + newResearchBonus - oldResearchBonus); break;
846 case cResearch::kScanResearch: BuildingData[i].setScan (BuildingData[i].getScan() + newResearchBonus - oldResearchBonus); break;
847 case cResearch::kCostResearch: BuildingData[i].buildCosts += newResearchBonus - oldResearchBonus; break;
848 }
849 if (researchArea != cResearch::kCostResearch) // don't increment the version, if the only change are the costs
850 incrementVersion = true;
851 if (!Contains (resultUpgradedUnitDatas, & (BuildingData[i])))
852 resultUpgradedUnitDatas.push_back (& (BuildingData[i]));
853 }
854 }
855 if (incrementVersion)
856 BuildingData[i].setVersion (BuildingData[i].getVersion() + 1);
857 }
858 }
859
860 //------------------------------------------------------------------------------
refreshResearchCentersWorkingOnArea()861 void cPlayer::refreshResearchCentersWorkingOnArea()
862 {
863 int oldResearchCentersWorkingOnArea[cResearch::kNrResearchAreas];
864
865 int newResearchCount = 0;
866 for (int i = 0; i < cResearch::kNrResearchAreas; i++)
867 {
868 oldResearchCentersWorkingOnArea[i] = researchCentersWorkingOnArea[i];
869 researchCentersWorkingOnArea[i] = 0;
870 }
871
872 for (auto i = buildings.begin(); i != buildings.end(); ++i)
873 {
874 const auto& building = *i;
875 if (building->data.canResearch && building->isUnitWorking())
876 {
877 researchCentersWorkingOnArea[building->getResearchArea()] += 1;
878 newResearchCount++;
879 }
880 }
881 std::swap (researchCentersWorkingTotal, newResearchCount);
882
883 for (int i = 0; i < cResearch::kNrResearchAreas; i++)
884 {
885 if (oldResearchCentersWorkingOnArea[i] != researchCentersWorkingOnArea[i])
886 {
887 researchCentersWorkingOnAreaChanged ((cResearch::ResearchArea)i);
888 }
889 }
890 if (researchCentersWorkingTotal != newResearchCount) researchCentersWorkingTotalChanged();
891 }
892
893 //------------------------------------------------------------------------------
mayHaveOffensiveUnit() const894 bool cPlayer::mayHaveOffensiveUnit() const
895 {
896 for (auto i = vehicles.begin(); i != vehicles.end(); ++i)
897 {
898 const auto& vehicle = *i;
899 if (vehicle->data.canAttack || !vehicle->data.canBuild.empty()) return true;
900 }
901 for (auto i = buildings.begin(); i != buildings.end(); ++i)
902 {
903 const auto& building = *i;
904 if (building->data.canAttack || !building->data.canBuild.empty()) return true;
905 }
906 return false;
907 }
908
909 //------------------------------------------------------------------------------
addTurnReportUnit(const sID & unitTypeId)910 void cPlayer::addTurnReportUnit (const sID& unitTypeId)
911 {
912 auto iter = std::find_if (currentTurnUnitReports.begin(), currentTurnUnitReports.end(), [unitTypeId] (const sTurnstartReport & entry) { return entry.type == unitTypeId; });
913 if (iter != currentTurnUnitReports.end())
914 {
915 ++iter->count;
916 }
917 else
918 {
919 sTurnstartReport entry;
920 entry.type = unitTypeId;
921 entry.count = 1;
922 currentTurnUnitReports.push_back (entry);
923 }
924 }
925
926 //------------------------------------------------------------------------------
resetTurnReportData()927 void cPlayer::resetTurnReportData()
928 {
929 currentTurnUnitReports.clear();
930 }
931
932 //------------------------------------------------------------------------------
getCurrentTurnUnitReports() const933 const std::vector<sTurnstartReport>& cPlayer::getCurrentTurnUnitReports() const
934 {
935 return currentTurnUnitReports;
936 }
937
938 //------------------------------------------------------------------------------
getCurrentTurnResearchAreasFinished() const939 const std::vector<int>& cPlayer::getCurrentTurnResearchAreasFinished() const
940 {
941 return currentTurnResearchAreasFinished;
942 }
943
944 //------------------------------------------------------------------------------
setCurrentTurnResearchAreasFinished(std::vector<int> areas)945 void cPlayer::setCurrentTurnResearchAreasFinished (std::vector<int> areas)
946 {
947 currentTurnResearchAreasFinished = std::move (areas);
948 }
949
950 //------------------------------------------------------------------------------
isCurrentTurnResearchAreaFinished(cResearch::ResearchArea area) const951 bool cPlayer::isCurrentTurnResearchAreaFinished (cResearch::ResearchArea area) const
952 {
953 return std::find (currentTurnResearchAreasFinished.begin(), currentTurnResearchAreasFinished.end(), area) != currentTurnResearchAreasFinished.end();
954 }
955
956 //------------------------------------------------------------------------------
getResearchState() const957 const cResearch& cPlayer::getResearchState() const
958 {
959 return researchState;
960 }
961
962 //------------------------------------------------------------------------------
getResearchState()963 cResearch& cPlayer::getResearchState()
964 {
965 return researchState;
966 }
967
968 //------------------------------------------------------------------------------
getResearchCentersWorkingTotal() const969 int cPlayer::getResearchCentersWorkingTotal() const
970 {
971 return researchCentersWorkingTotal;
972 }
973
974 //------------------------------------------------------------------------------
getResearchCentersWorkingOnArea(cResearch::ResearchArea area) const975 int cPlayer::getResearchCentersWorkingOnArea (cResearch::ResearchArea area) const
976 {
977 return researchCentersWorkingOnArea[area];
978 }
979
980 //------------------------------------------------------------------------------
canSeeAt(const cPosition & position) const981 bool cPlayer::canSeeAt (const cPosition& position) const
982 {
983 if (position.x() < 0 || position.x() >= mapSize.x() || position.y() < 0 || position.y() >= mapSize.y()) return false;
984
985 return ScanMap[getOffset (position)] != 0;
986 }
987
988 //------------------------------------------------------------------------------
drawSpecialCircle(const cPosition & position,int iRadius,std::vector<char> & map,const cPosition & mapsize)989 void cPlayer::drawSpecialCircle (const cPosition& position, int iRadius, std::vector<char>& map, const cPosition& mapsize)
990 {
991 const float PI_ON_180 = 0.017453f;
992 const float PI_ON_4 = PI_ON_180 * 45;
993 if (iRadius <= 0) return;
994
995 iRadius *= 10;
996 const float step = (PI_ON_180 * 90 - acosf (1.0f / iRadius)) / 2;
997
998 for (float angle = 0; angle <= PI_ON_4; angle += step)
999 {
1000 int rx = (int) (cosf (angle) * iRadius);
1001 int ry = (int) (sinf (angle) * iRadius);
1002 rx /= 10;
1003 ry /= 10;
1004
1005 int x1 = rx + position.x();
1006 int x2 = -rx + position.x();
1007 for (int k = x2; k <= x1; k++)
1008 {
1009 if (k < 0) continue;
1010 if (k >= mapsize.x()) break;
1011 if (position.y() + ry >= 0 && position.y() + ry < mapsize.y())
1012 map[k + (position.y() + ry) * mapsize.x()] |= 1;
1013 if (position.y() - ry >= 0 && position.y() - ry < mapsize.y())
1014 map[k + (position.y() - ry) * mapsize.x()] |= 1;
1015 }
1016
1017 x1 = ry + position.x();
1018 x2 = -ry + position.x();
1019 for (int k = x2; k <= x1; k++)
1020 {
1021 if (k < 0) continue;
1022 if (k >= mapsize.x()) break;
1023 if (position.y() + rx >= 0 && position.y() + rx < mapsize.y())
1024 map[k + (position.y() + rx) *mapsize.x()] |= 1;
1025 if (position.y() - rx >= 0 && position.y() - rx < mapsize.y())
1026 map[k + (position.y() - rx) *mapsize.x()] |= 1;
1027 }
1028 }
1029 }
1030
1031 //------------------------------------------------------------------------------
drawSpecialCircleBig(const cPosition & position,int iRadius,std::vector<char> & map,const cPosition & mapsize)1032 void cPlayer::drawSpecialCircleBig (const cPosition& position, int iRadius, std::vector<char>& map, const cPosition& mapsize)
1033 {
1034 const float PI_ON_180 = 0.017453f;
1035 const float PI_ON_4 = PI_ON_180 * 45;
1036 if (iRadius <= 0) return;
1037
1038 --iRadius;
1039 iRadius *= 10;
1040 const float step = (PI_ON_180 * 90 - acosf (1.0f / iRadius)) / 2;
1041 for (float angle = 0; angle <= PI_ON_4; angle += step)
1042 {
1043 int rx = (int) (cosf (angle) * iRadius);
1044 int ry = (int) (sinf (angle) * iRadius);
1045 rx /= 10;
1046 ry /= 10;
1047
1048 int x1 = rx + position.x();
1049 int x2 = -rx + position.x();
1050 for (int k = x2; k <= x1 + 1; k++)
1051 {
1052 if (k < 0) continue;
1053 if (k >= mapsize.x()) break;
1054 if (position.y() + ry >= 0 && position.y() + ry < mapsize.y())
1055 map[k + (position.y() + ry) *mapsize.x()] |= 1;
1056 if (position.y() - ry >= 0 && position.y() - ry < mapsize.y())
1057 map[k + (position.y() - ry) *mapsize.x()] |= 1;
1058
1059 if (position.y() + ry + 1 >= 0 && position.y() + ry + 1 < mapsize.y())
1060 map[k + (position.y() + ry + 1) *mapsize.x()] |= 1;
1061 if (position.y() - ry + 1 >= 0 && position.y() - ry + 1 < mapsize.y())
1062 map[k + (position.y() - ry + 1) *mapsize.x()] |= 1;
1063 }
1064
1065 x1 = ry + position.x();
1066 x2 = -ry + position.x();
1067 for (int k = x2; k <= x1 + 1; k++)
1068 {
1069 if (k < 0) continue;
1070 if (k >= mapsize.x()) break;
1071 if (position.y() + rx >= 0 && position.y() + rx < mapsize.y())
1072 map[k + (position.y() + rx) *mapsize.x()] |= 1;
1073 if (position.y() - rx >= 0 && position.y() - rx < mapsize.y())
1074 map[k + (position.y() - rx) *mapsize.x()] |= 1;
1075
1076 if (position.y() + rx + 1 >= 0 && position.y() + rx + 1 < mapsize.y())
1077 map[k + (position.y() + rx + 1) *mapsize.x()] |= 1;
1078 if (position.y() - rx + 1 >= 0 && position.y() - rx + 1 < mapsize.y())
1079 map[k + (position.y() - rx + 1) *mapsize.x()] |= 1;
1080 }
1081 }
1082 }
1083
1084 //------------------------------------------------------------------------------
addSavedReport(std::unique_ptr<cSavedReport> savedReport)1085 void cPlayer::addSavedReport (std::unique_ptr<cSavedReport> savedReport)
1086 {
1087 if (savedReport == nullptr) return;
1088
1089 savedReportsList.push_back (std::move (savedReport));
1090
1091 reportAdded (*savedReportsList.back());
1092 }
1093
1094 //------------------------------------------------------------------------------
getSavedReports() const1095 const std::vector<std::unique_ptr<cSavedReport>>& cPlayer::getSavedReports() const
1096 {
1097 return savedReportsList;
1098 }
1099
1100 //------------------------------------------------------------------------------
getHasFinishedTurn() const1101 bool cPlayer::getHasFinishedTurn() const
1102 {
1103 return hasFinishedTurn;
1104 }
1105
1106 //------------------------------------------------------------------------------
setHasFinishedTurn(bool value)1107 void cPlayer::setHasFinishedTurn (bool value)
1108 {
1109 std::swap (hasFinishedTurn, value);
1110 if (hasFinishedTurn != value) hasFinishedTurnChanged();
1111 }
1112
1113 //------------------------------------------------------------------------------
getIsRemovedFromGame() const1114 bool cPlayer::getIsRemovedFromGame() const
1115 {
1116 return isRemovedFromGame;
1117 }
1118
1119 //------------------------------------------------------------------------------
setIsRemovedFromGame(bool value)1120 void cPlayer::setIsRemovedFromGame (bool value)
1121 {
1122 std::swap (isRemovedFromGame, value);
1123 if (isRemovedFromGame != value) isRemovedFromGameChanged();
1124 }
1125