1 /*
2 * Copyright 2010-2014 OpenXcom Developers.
3 *
4 * This file is part of OpenXcom.
5 *
6 * OpenXcom is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OpenXcom is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #define _USE_MATH_DEFINES
20 #include "AlienMission.h"
21 #include "AlienBase.h"
22 #include "Base.h"
23 #include "../fmath.h"
24 #include "../Engine/Exception.h"
25 #include "../Engine/Game.h"
26 #include "../Engine/Logger.h"
27 #include "../Engine/RNG.h"
28 #include "../Geoscape/Globe.h"
29 #include "../Ruleset/RuleAlienMission.h"
30 #include "../Ruleset/RuleRegion.h"
31 #include "../Ruleset/RuleCountry.h"
32 #include "../Ruleset/Ruleset.h"
33 #include "../Ruleset/RuleUfo.h"
34 #include "../Ruleset/City.h"
35 #include "../Ruleset/UfoTrajectory.h"
36 #include "SavedGame.h"
37 #include "TerrorSite.h"
38 #include "Ufo.h"
39 #include "Craft.h"
40 #include "Region.h"
41 #include "Country.h"
42 #include "Waypoint.h"
43 #include <assert.h>
44 #include <algorithm>
45 #include <functional>
46 #include <math.h>
47
48 namespace OpenXcom
49 {
50
AlienMission(const RuleAlienMission & rule)51 AlienMission::AlienMission(const RuleAlienMission &rule) : _rule(rule), _nextWave(0), _nextUfoCounter(0), _spawnCountdown(0), _liveUfos(0), _uniqueID(0), _base(0)
52 {
53 // Empty by design.
54 }
55
~AlienMission()56 AlienMission::~AlienMission()
57 {
58 // Empty by design.
59 }
60
61 class matchById: public std::unary_function<const AlienBase *, bool>
62 {
63 public:
64 /// Remember ID.
matchById(int id)65 matchById(int id) : _id(id) { /* Empty by design. */ }
66 /// Match with stored ID.
operator ()(const AlienBase * ab) const67 bool operator()(const AlienBase *ab) const { return ab->getId() == _id; }
68 private:
69 int _id;
70 };
71
72 /**
73 * @param node The YAML node containing the data.
74 * @param game The game data, required to locate the alien base.
75 */
load(const YAML::Node & node,SavedGame & game)76 void AlienMission::load(const YAML::Node& node, SavedGame &game)
77 {
78 _region = node["region"].as<std::string>(_region);
79 _race = node["race"].as<std::string>(_race);
80 _nextWave = node["nextWave"].as<size_t>(_nextWave);
81 _nextUfoCounter = node["nextUfoCounter"].as<size_t>(_nextUfoCounter);
82 _spawnCountdown = node["spawnCountdown"].as<size_t>(_spawnCountdown);
83 _liveUfos = node["liveUfos"].as<size_t>(_liveUfos);
84 _uniqueID = node["uniqueID"].as<int>(_uniqueID);
85 if (const YAML::Node &base = node["alienBase"])
86 {
87 int id = base.as<int>();
88 std::vector<AlienBase*>::const_iterator found = std::find_if(game.getAlienBases()->begin(), game.getAlienBases()->end(), matchById(id));
89 if (found == game.getAlienBases()->end())
90 {
91 throw Exception("Corrupted save: Invalid base for mission.");
92 }
93 _base = *found;
94 }
95
96 }
97
save() const98 YAML::Node AlienMission::save() const
99 {
100 YAML::Node node;
101 node["type"] = _rule.getType();
102 node["region"] = _region;
103 node["race"] = _race;
104 node["nextWave"] = _nextWave;
105 node["nextUfoCounter"] = _nextUfoCounter;
106 node["spawnCountdown"] = _spawnCountdown;
107 node["liveUfos"] = _liveUfos;
108 node["uniqueID"] = _uniqueID;
109 if (_base)
110 {
111 node["alienBase"] = _base->getId();
112 }
113 return node;
114 }
115
getType() const116 const std::string &AlienMission::getType() const
117 {
118 return _rule.getType();
119 }
120
121 /**
122 * Check if a mission is over and can be safely removed from the game.
123 * A mission is over if it will spawn no more UFOs and it has no UFOs still in
124 * the game.
125 * @return If the mission can be safely removed from game.
126 */
isOver() const127 bool AlienMission::isOver() const
128 {
129 if (_rule.getType() == "STR_ALIEN_INFILTRATION")
130 {
131 //Infiltrations continue for ever.
132 return false;
133 }
134 if (_nextWave == _rule.getWaveCount() && !_liveUfos)
135 {
136 return true;
137 }
138 return false;
139 }
140
141 /**
142 * Find an XCOM base in this region that is marked for retaliation.
143 */
144 class FindMarkedXCOMBase: public std::unary_function<const Base *, bool>
145 {
146 public:
FindMarkedXCOMBase(const RuleRegion & region)147 FindMarkedXCOMBase(const RuleRegion ®ion) : _region(region) { /* Empty by design. */ }
operator ()(const Base * base) const148 bool operator()(const Base *base) const { return (_region.insideRegion(base->getLongitude(), base->getLatitude()) && base->getRetaliationTarget()); }
149 private:
150 const RuleRegion &_region;
151 };
152
think(Game & engine,const Globe & globe)153 void AlienMission::think(Game &engine, const Globe &globe)
154 {
155 const Ruleset &ruleset = *engine.getRuleset();
156 SavedGame &game = *engine.getSavedGame();
157 if (_nextWave >= _rule.getWaveCount())
158 return;
159 if (_spawnCountdown > 30)
160 {
161 _spawnCountdown -= 30;
162 return;
163 }
164 const MissionWave &wave = _rule.getWave(_nextWave);
165 RuleUfo &ufoRule = *ruleset.getUfo(wave.ufoType);
166 const UfoTrajectory &trajectory = *ruleset.getUfoTrajectory(wave.trajectory);
167 Ufo *ufo = spawnUfo(game, ruleset, globe, ufoRule, trajectory);
168 if (ufo)
169 {
170 //Some missions may not spawn a UFO!
171 game.getUfos()->push_back(ufo);
172 }
173 ++_nextUfoCounter;
174 if (_nextUfoCounter == wave.ufoCount)
175 {
176 _nextUfoCounter = 0;
177 ++_nextWave;
178 }
179 if (_rule.getType() == "STR_ALIEN_INFILTRATION" && _nextWave == _rule.getWaveCount())
180 {
181 for (std::vector<Country*>::iterator c = game.getCountries()->begin(); c != game.getCountries()->end(); ++c)
182 {
183 if (!(*c)->getPact() && !(*c)->getNewPact() && ruleset.getRegion(_region)->insideRegion((*c)->getRules()->getLabelLongitude(), (*c)->getRules()->getLabelLatitude()))
184 {
185 (*c)->setNewPact();
186 spawnAlienBase(globe, engine);
187 break;
188 }
189 }
190
191
192 // Infiltrations loop for ever.
193 _nextWave = 0;
194 }
195 if (_rule.getType() == "STR_ALIEN_BASE" && _nextWave == _rule.getWaveCount())
196 {
197 spawnAlienBase(globe, engine);
198 }
199 if (_nextWave != _rule.getWaveCount())
200 {
201 size_t spawnTimer = _rule.getWave(_nextWave).spawnTimer / 30;
202 _spawnCountdown = (spawnTimer/2 + RNG::generate(0, spawnTimer)) * 30;
203 }
204 }
205
206 /**
207 * This function will spawn a UFO according the the mission rules.
208 * Some code is duplicated between cases, that's ok for now. It's on different
209 * code paths and the function is MUCH easier to read written this way.
210 * @param game The saved game information.
211 * @param ruleset The ruleset.
212 * @param globe The globe, for land checks.
213 * @param ufoRule The rule for the desired UFO.
214 * @param trajectory The rule for the desired trajectory.
215 * @return Pointer to the spawned UFO. If the mission does not desire to spawn a UFO, 0 is returned.
216 */
spawnUfo(const SavedGame & game,const Ruleset & ruleset,const Globe & globe,const RuleUfo & ufoRule,const UfoTrajectory & trajectory)217 Ufo *AlienMission::spawnUfo(const SavedGame &game, const Ruleset &ruleset, const Globe &globe, const RuleUfo &ufoRule, const UfoTrajectory &trajectory)
218 {
219 if (_rule.getType() == "STR_ALIEN_RETALIATION")
220 {
221 const RuleRegion ®ionRules = *ruleset.getRegion(_region);
222 std::vector<Base *>::const_iterator found =
223 std::find_if(game.getBases()->begin(), game.getBases()->end(),
224 FindMarkedXCOMBase(regionRules));
225 if (found != game.getBases()->end())
226 {
227 // Spawn a battleship straight for the XCOM base.
228 const RuleUfo &battleshipRule = *ruleset.getUfo("STR_BATTLESHIP");
229 const UfoTrajectory &assaultTrajectory = *ruleset.getUfoTrajectory("__RETALIATION_ASSAULT_RUN");
230 Ufo *ufo = new Ufo(const_cast<RuleUfo*>(&battleshipRule));
231 ufo->setMissionInfo(this, &assaultTrajectory);
232 std::pair<double, double> pos;
233 if (trajectory.getAltitude(0) == "STR_GROUND")
234 {
235 pos = getLandPoint(globe, regionRules, trajectory.getZone(0));
236 }
237 else
238 {
239 pos = regionRules.getRandomPoint(trajectory.getZone(0));
240 }
241 ufo->setAltitude(assaultTrajectory.getAltitude(0));
242 ufo->setSpeed(assaultTrajectory.getSpeedPercentage(0) * ufoRule.getMaxSpeed());
243 ufo->setLongitude(pos.first);
244 ufo->setLatitude(pos.second);
245 Waypoint *wp = new Waypoint();
246 wp->setLongitude((*found)->getLongitude());
247 wp->setLatitude((*found)->getLatitude());
248 ufo->setDestination(wp);
249 return ufo;
250 }
251 }
252 else if (_rule.getType() == "STR_ALIEN_SUPPLY")
253 {
254 if (ufoRule.getType() == "STR_SUPPLY_SHIP" && !_base)
255 {
256 // No base to supply!
257 return 0;
258 }
259 // Our destination is always an alien base.
260 Ufo *ufo = new Ufo(const_cast<RuleUfo*>(&ufoRule));
261 ufo->setMissionInfo(this, &trajectory);
262 const RuleRegion ®ionRules = *ruleset.getRegion(_region);
263 std::pair<double, double> pos;
264 if (trajectory.getAltitude(0) == "STR_GROUND")
265 {
266 pos = getLandPoint(globe, regionRules, trajectory.getZone(0));
267 }
268 else
269 {
270 pos = regionRules.getRandomPoint(trajectory.getZone(0));
271 }
272 ufo->setAltitude(trajectory.getAltitude(0));
273 ufo->setSpeed(trajectory.getSpeedPercentage(0) * ufoRule.getMaxSpeed());
274 ufo->setLongitude(pos.first);
275 ufo->setLatitude(pos.second);
276 Waypoint *wp = new Waypoint();
277 if (trajectory.getAltitude(1) == "STR_GROUND")
278 {
279 if (ufoRule.getType() == "STR_SUPPLY_SHIP")
280 {
281 // Supply ships on supply missions land on bases, ignore trajectory zone.
282 pos.first = _base->getLongitude();
283 pos.second = _base->getLatitude();
284 }
285 else
286 {
287 // Other ships can land where they want.
288 pos = getLandPoint(globe, regionRules, trajectory.getZone(1));
289 }
290 }
291 else
292 {
293 pos = regionRules.getRandomPoint(trajectory.getZone(1));
294 }
295 wp->setLongitude(pos.first);
296 wp->setLatitude(pos.second);
297 ufo->setDestination(wp);
298 return ufo;
299 }
300 // Spawn according to sequence.
301 Ufo *ufo = new Ufo(const_cast<RuleUfo*>(&ufoRule));
302 ufo->setMissionInfo(this, &trajectory);
303 const RuleRegion ®ionRules = *ruleset.getRegion(_region);
304 std::pair<double, double> pos = getWaypoint(trajectory, 0, globe, regionRules);
305 ufo->setAltitude(trajectory.getAltitude(0));
306 if (trajectory.getAltitude(0) == "STR_GROUND")
307 {
308 ufo->setSecondsRemaining(trajectory.groundTimer());
309 }
310 ufo->setSpeed(trajectory.getSpeedPercentage(0) * ufoRule.getMaxSpeed());
311 ufo->setLongitude(pos.first);
312 ufo->setLatitude(pos.second);
313 Waypoint *wp = new Waypoint();
314 pos = getWaypoint(trajectory, 1, globe, regionRules);
315 wp->setLongitude(pos.first);
316 wp->setLatitude(pos.second);
317 ufo->setDestination(wp);
318 return ufo;
319 }
320
start(size_t initialCount)321 void AlienMission::start(size_t initialCount)
322 {
323 _nextWave = 0;
324 _nextUfoCounter = 0;
325 _liveUfos = 0;
326 if (initialCount == 0)
327 {
328 size_t spawnTimer = _rule.getWave(0).spawnTimer / 30;
329 _spawnCountdown = (spawnTimer / 2 + RNG::generate(0, spawnTimer)) * 30;
330 }
331 else
332 {
333 _spawnCountdown = initialCount;
334 }
335 }
336
337 /** @brief Match a base from it's coordinates.
338 * This function object uses coordinates to match a base.
339 */
340 class MatchBaseCoordinates: public std::unary_function<const Base *, bool>
341 {
342 public:
343 /// Remember the query coordinates.
MatchBaseCoordinates(double lon,double lat)344 MatchBaseCoordinates(double lon, double lat) : _lon(lon), _lat(lat) { /* Empty by design. */ }
345 /// Match with base's coordinates.
operator ()(const Base * base) const346 bool operator()(const Base *base) const { return AreSame(base->getLongitude(), _lon) && AreSame(base->getLatitude(), _lat); }
347 private:
348 double _lon, _lat;
349 };
350
351 /**
352 * This function is called when one of the mission's UFOs arrives at it's current destination.
353 * It takes care of sending the UFO to the next waypoint, landing UFOs and
354 * marking them for removal as required. It must set the game data in a way that the rest of the code
355 * understands what to do.
356 * @param ufo The UFO that reached it's waypoint.
357 * @param engine The game engine, required to get access to game data and game rules.
358 * @param globe The earth globe, required to get access to land checks.
359 */
ufoReachedWaypoint(Ufo & ufo,Game & engine,const Globe & globe)360 void AlienMission::ufoReachedWaypoint(Ufo &ufo, Game &engine, const Globe &globe)
361 {
362 const Ruleset &rules = *engine.getRuleset();
363 SavedGame &game = *engine.getSavedGame();
364 const size_t curWaypoint = ufo.getTrajectoryPoint();
365 const size_t nextWaypoint = curWaypoint + 1;
366 const UfoTrajectory &trajectory = ufo.getTrajectory();
367 if (nextWaypoint >= trajectory.getWaypointCount())
368 {
369 ufo.setDetected(false);
370 ufo.setStatus(Ufo::DESTROYED);
371 return;
372 }
373 ufo.setAltitude(trajectory.getAltitude(nextWaypoint));
374 ufo.setTrajectoryPoint(nextWaypoint);
375 std::pair<double, double> pos = getWaypoint(trajectory, nextWaypoint, globe, *rules.getRegion(_region));
376
377 // screw it, we're not taking any chances, use the city's lon/lat info instead of the region's
378 // TODO: find out why there is a discrepency between generated city mission zones and the cities that generated them.
379 // honolulu: 3.6141230952747376, -0.37332941766009109
380 // UFO: lon: 3.61412 lat: -0.373329
381 // Zone: Longitudes: 3.61412 to 3.61412 Lattitudes: -0.373329 to -0.373329
382 // http://openxcom.org/bugs/openxcom/issues/615#comment_3292
383 if (ufo.getRules()->getType() == "STR_TERROR_SHIP" && _rule.getType() == "STR_ALIEN_TERROR" && trajectory.getZone(nextWaypoint) == RuleRegion::CITY_MISSION_ZONE)
384 {
385 while(!rules.locateCity(pos.first, pos.second))
386 {
387 Log(LOG_DEBUG) << "Longitude: " << pos.first << "Lattitude: " << pos.second << " invalid";
388 size_t city = RNG::generate(0, rules.getRegion(_region)->getCities()->size() - 1);
389 pos.first = rules.getRegion(_region)->getCities()->at(city)->getLongitude();
390 pos.second = rules.getRegion(_region)->getCities()->at(city)->getLatitude();
391 }
392 }
393
394 Waypoint *wp = new Waypoint();
395 wp->setLongitude(pos.first);
396 wp->setLatitude(pos.second);
397 ufo.setDestination(wp);
398 if (ufo.getAltitude() != "STR_GROUND")
399 {
400 if (ufo.getLandId() != 0)
401 {
402 ufo.setLandId(0);
403 }
404 // Set next waypoint.
405 ufo.setSpeed((int)(ufo.getRules()->getMaxSpeed() * trajectory.getSpeedPercentage(nextWaypoint)));
406 }
407 else
408 {
409 // UFO landed.
410
411 if (ufo.getRules()->getType() == "STR_TERROR_SHIP" && _rule.getType() == "STR_ALIEN_TERROR" && trajectory.getZone(curWaypoint) == RuleRegion::CITY_MISSION_ZONE)
412 {
413 // Specialized: STR_ALIEN_TERROR
414 // Remove UFO, replace with TerrorSite.
415 addScore(ufo.getLongitude(), ufo.getLatitude(), engine);
416 ufo.setStatus(Ufo::DESTROYED);
417 TerrorSite *terrorSite = new TerrorSite();
418 terrorSite->setLongitude(ufo.getLongitude());
419 terrorSite->setLatitude(ufo.getLatitude());
420 terrorSite->setId(game.getId("STR_TERROR_SITE"));
421 terrorSite->setSecondsRemaining(4 * 3600 + RNG::generate(0, 6) * 3600);
422 terrorSite->setAlienRace(_race);
423 if (!rules.locateCity(ufo.getLongitude(), ufo.getLatitude()))
424 {
425 std::ostringstream error;
426 error << "Mission number: " << getId() << " in region: " << getRegion() << " trying to land at lon: " << ufo.getLongitude() << " lat: " << ufo.getLatitude() << " ufo is on flightpath: " << ufo.getTrajectory().getID() << " at point: " << ufo.getTrajectoryPoint() << ", no city found.";
427 Log(LOG_FATAL) << error.str();
428 std::vector<MissionArea> cityZones = rules.getRegion(getRegion())->getMissionZones().at(RuleRegion::CITY_MISSION_ZONE).areas;
429 for (int i = 0; i != cityZones.size(); ++i)
430 {
431 error.str("");
432 error << "Zone " << i << ": Longitudes: " << cityZones.at(i).lonMin * M_PI / 180 << " to " << cityZones.at(i).lonMax * M_PI / 180 << " Latitudes: " << cityZones.at(i).latMin * M_PI / 180 << " to " << cityZones.at(i).latMax * M_PI / 180;
433 Log(LOG_INFO) << error.str();
434 }
435 for (std::vector<City*>::const_iterator i = rules.getRegion(getRegion())->getCities()->begin(); i != rules.getRegion(getRegion())->getCities()->end(); ++i)
436 {
437 error.str("");
438 error << "City: " << (*i)->getName() << " Longitude: " << (*i)->getLongitude() << " Latitude: " << (*i)->getLatitude();
439 Log(LOG_INFO) << error.str();
440 }
441 assert(0 && "Terror Mission failed to find a city, please check your log file for more details");
442 }
443 game.getTerrorSites()->push_back(terrorSite);
444 for (std::vector<Target*>::iterator t = ufo.getFollowers()->begin(); t != ufo.getFollowers()->end();)
445 {
446 Craft* c = dynamic_cast<Craft*>(*t);
447 if (c && c->getNumSoldiers() != 0)
448 {
449 c->setDestination(terrorSite);
450 t = ufo.getFollowers()->begin();
451 }
452 else
453 {
454 ++t;
455 }
456 }
457 }
458 else if (_rule.getType() == "STR_ALIEN_RETALIATION" && trajectory.getID() == "__RETALIATION_ASSAULT_RUN")
459 {
460 // Ignore what the trajectory might say, this is a base assault.
461 // Remove UFO, replace with Base defense.
462 ufo.setDetected(false);
463 std::vector<Base *>::const_iterator found =
464 std::find_if(game.getBases()->begin(), game.getBases()->end(),
465 MatchBaseCoordinates(ufo.getLongitude(), ufo.getLatitude()));
466 if (found == game.getBases()->end())
467 {
468 ufo.setStatus(Ufo::DESTROYED);
469 // Only spawn mission if the base is still there.
470 return;
471 }
472 ufo.setDestination(*found);
473 }
474 else
475 {
476 // Set timer for UFO on the ground.
477 ufo.setSecondsRemaining(trajectory.groundTimer());
478 if (ufo.getDetected() && ufo.getLandId() == 0)
479 {
480 ufo.setLandId(engine.getSavedGame()->getId("STR_LANDING_SITE"));
481 }
482 }
483 }
484 }
485
486 /**
487 * This function is called when one of the mission's UFOs is shot down (crashed or destroyed).
488 * Currently the only thing that happens is delaying the next UFO in the mission sequence.
489 * @param ufo The UFO that was shot down.
490 * @param engine The game engine, unused for now.
491 * @param globe The earth globe, unused for now.
492 */
ufoShotDown(Ufo & ufo,Game &,const Globe &)493 void AlienMission::ufoShotDown(Ufo &ufo, Game &, const Globe &)
494 {
495 switch (ufo.getStatus())
496 {
497 case Ufo::FLYING:
498 case Ufo::LANDED:
499 assert(0 && "Ufo seems ok!");
500 break;
501 case Ufo::CRASHED:
502 case Ufo::DESTROYED:
503 if (_nextWave != _rule.getWaveCount())
504 {
505 // Delay next wave
506 _spawnCountdown += 30 * (RNG::generate(0, 48) + 400);
507 }
508 break;
509 }
510 }
511
512 /**
513 * This function is called when one of the mission's UFOs has finished it's time on the ground.
514 * It takes care of sending the UFO to the next waypoint and marking them for removal as required.
515 * It must set the game data in a way that the rest of the code understands what to do.
516 * @param ufo The UFO that reached it's waypoint.
517 * @param engine The game engine, required to get access to game data and game rules.
518 * @param globe The earth globe, required to get access to land checks.
519 */
ufoLifting(Ufo & ufo,Game & engine,const Globe & globe)520 void AlienMission::ufoLifting(Ufo &ufo, Game &engine, const Globe &globe)
521 {
522 switch (ufo.getStatus())
523 {
524 case Ufo::FLYING:
525 assert(0 && "Ufo is already on the air!");
526 break;
527 case Ufo::LANDED:
528 {
529 // base missions only get points when they are completed.
530 if (_rule.getPoints() > 0 && _rule.getType() != "STR_ALIEN_BASE")
531 {
532 addScore(ufo.getLongitude(), ufo.getLatitude(), engine);
533 }
534 ufo.setAltitude("STR_VERY_LOW");
535 ufo.setSpeed((int)(ufo.getRules()->getMaxSpeed() * ufo.getTrajectory().getSpeedPercentage(ufo.getTrajectoryPoint())));
536 }
537 break;
538 case Ufo::CRASHED:
539 // Mission expired
540 ufo.setDetected(false);
541 ufo.setStatus(Ufo::DESTROYED);
542 break;
543 case Ufo::DESTROYED:
544 assert(0 && "UFO can't fly!");
545 break;
546 }
547 }
548
549 /**
550 * The new time must be a multiple of 30 minutes, and more than 0.
551 * Calling this on a finished mission has no effect.
552 * @param minutes The minutes until the next UFO wave will spawn.
553 */
setWaveCountdown(size_t minutes)554 void AlienMission::setWaveCountdown(size_t minutes)
555 {
556 assert(minutes != 0 && minutes % 30 == 0);
557 if (isOver())
558 {
559 return;
560 }
561 _spawnCountdown = minutes;
562 }
563
564 /**
565 * Assigns a unique ID to this mission.
566 * It is an error to assign two IDs to the same mission.
567 * @param id The UD to assign.
568 */
setId(int id)569 void AlienMission::setId(int id)
570 {
571 assert(_uniqueID == 0 && "Reassigning ID!");
572 _uniqueID = id;
573 }
574
575 /**
576 * @return The unique ID assigned to this mission.
577 */
getId() const578 int AlienMission::getId() const
579 {
580 assert(_uniqueID != 0 && "Uninitalized mission!");
581 return _uniqueID;
582 }
583
584 /**
585 * Sets the alien base associated with this mission.
586 * Only the alien supply missions care about this.
587 * @param base A pointer to an alien base.
588 */
setAlienBase(const AlienBase * base)589 void AlienMission::setAlienBase(const AlienBase *base)
590 {
591 _base = base;
592 }
593
594 /**
595 * Only alien supply missions ever have a valid pointer.
596 * @return A pointer (possibly 0) of the AlienBase for this mission.
597 */
getAlienBase() const598 const AlienBase *AlienMission::getAlienBase() const
599 {
600 return _base;
601 }
602
603 /**
604 * Add alien points to the country and region at the coordinates given.
605 * @param lon Longitudinal coordinates to check.
606 * @param lat Latitudinal coordinates to check.
607 * @param engine The game engine, required to get access to game data and game rules.
608 */
addScore(const double lon,const double lat,Game & engine)609 void AlienMission::addScore(const double lon, const double lat, Game &engine)
610 {
611 for (std::vector<Region *>::iterator region = engine.getSavedGame()->getRegions()->begin(); region != engine.getSavedGame()->getRegions()->end(); ++region)
612 {
613 if ((*region)->getRules()->insideRegion(lon, lat))
614 {
615 (*region)->addActivityAlien(_rule.getPoints());
616 break;
617 }
618 }
619 for (std::vector<Country *>::iterator country = engine.getSavedGame()->getCountries()->begin(); country != engine.getSavedGame()->getCountries()->end(); ++country)
620 {
621 if ((*country)->getRules()->insideCountry(lon, lat))
622 {
623 (*country)->addActivityAlien(_rule.getPoints());
624 break;
625 }
626 }
627 }
628
629 /**
630 * Spawn an alien base.
631 * @param globe The earth globe, required to get access to land checks.
632 * @param engine The game engine, required to get access to game data and game rules.
633 */
spawnAlienBase(const Globe & globe,Game & engine)634 void AlienMission::spawnAlienBase(const Globe &globe, Game &engine)
635 {
636 SavedGame &game = *engine.getSavedGame();
637 if (game.getAlienBases()->size() >= 8)
638 {
639 return;
640 }
641 const Ruleset &ruleset = *engine.getRuleset();
642 // Once the last UFO is spawned, the aliens build their base.
643 const RuleRegion ®ionRules = *ruleset.getRegion(_region);
644 std::pair<double, double> pos = getLandPoint(globe, regionRules, RuleRegion::ALIEN_BASE_ZONE);
645 AlienBase *ab = new AlienBase();
646 ab->setAlienRace(_race);
647 ab->setId(game.getId("STR_ALIEN_BASE"));
648 ab->setLongitude(pos.first);
649 ab->setLatitude(pos.second);
650 game.getAlienBases()->push_back(ab);
651 addScore(pos.first, pos.second, engine);
652 }
653
654 /*
655 * Sets the mission's region. if the region is incompatible with
656 * actually carrying out an attack, use the "fallback" region as
657 * defined in the ruleset.
658 * (this is a slight difference from the original, which just
659 * defaulted them to zone[0], North America)
660 * @param region the region we want to try to set the mission to.
661 * @param rules the ruleset, in case we need to swap out the region.
662 */
setRegion(const std::string & region,const Ruleset & rules)663 void AlienMission::setRegion(const std::string ®ion, const Ruleset &rules)
664 {
665 if (rules.getRegion(region)->getMissionRegion() != "")
666 {
667 _region = rules.getRegion(region)->getMissionRegion();
668 }
669 else
670 {
671 _region = region;
672 }
673 }
674
675 /**
676 * Select a destination based on the criteria of our trajectory and desired waypoint.
677 * @param trajectory the trajectory in question.
678 * @param nextWaypoint the next logical waypoint in sequence (0 for newly spawned UFOs)
679 * @param globe The earth globe, required to get access to land checks.
680 * @param region the ruleset for the region of our mission.
681 * @return a set of lon and lat coordinates based on the criteria of the trajectory.
682 */
getWaypoint(const UfoTrajectory & trajectory,const size_t nextWaypoint,const Globe & globe,const RuleRegion & region)683 std::pair<double, double> AlienMission::getWaypoint(const UfoTrajectory &trajectory, const size_t nextWaypoint, const Globe &globe, const RuleRegion ®ion)
684 {
685 /* LOOK MA! NO HANDS!
686 if (trajectory.getAltitude(nextWaypoint) == "STR_GROUND")
687 {
688 return getLandPoint(globe, region, trajectory.getZone(nextWaypoint));
689 }
690 else
691 */
692 return region.getRandomPoint(trajectory.getZone(nextWaypoint));
693 }
694
695 /**
696 * Get a random point inside the given region zone.
697 * The point will be used to land a UFO, so it HAS to be on land.
698 */
getLandPoint(const Globe & globe,const RuleRegion & region,size_t zone)699 std::pair<double, double> AlienMission::getLandPoint(const Globe &globe, const RuleRegion ®ion, size_t zone)
700 {
701 int tries = 0;
702 std::pair<double, double> pos;
703 do
704 {
705 pos = region.getRandomPoint(zone);
706 ++tries;
707 }
708 while (!(globe.insideLand(pos.first, pos.second)
709 && region.insideRegion(pos.first, pos.second))
710 && tries < 100);
711 if (tries == 100)
712 {
713 Log(LOG_DEBUG) << "Region: " << region.getType() << " Longitude: " << pos.first << " Lattitude: " << pos.second << " invalid zone: " << zone << " ufo forced to land on water!";
714 }
715 return pos;
716
717 }
718
719 }
720