1 // Copyright 2005 by Anthony Liekens anthony@liekens.net
2
3 #include <math.h>
4
5 #include "planets.h"
6 #include "timer.h"
7 #include "ships.h"
8 #include "extensions.h"
9 #include "settings.h"
10 #include "animations.h"
11 #include "coordinate.h"
12 #include "selection.h"
13 #include "players.h"
14 #include "universe.h"
15 #include "messages.h"
16 #include "canvas.h"
17
18 using namespace std;
19
20 // ##### PLANET #####
21
Planet()22 Planet::Planet() : nearestPlanet(false), owner(), mother()
23 {
24 rotationSpeed = ( ( rand() % 2 ) * 2 - 1 ) * ( rand() % 60000 + 20000 );
25 rotationDistance = 0.05 + 0.4 * ( double )rand() / RAND_MAX;
26 rotationOffset = rand() % 10000000;
27 size = 4 + rand() % 4; // [4 ... 8)
28 isAMoon = false;
29 selected = false;
30 sourceSelected = false;
31 shipCreationSpeed = 12500 - size*1000;
32
33 double angle = rotationOffset / rotationSpeed;
34 locationVector.setX(cos(angle));
35 locationVector.setY(sin(angle));
36
37 buildStartTime = 0;
38 buildEndTime = shipCreationSpeed;
39 }
40
41 void
makeMoon(Planet * mother)42 Planet::makeMoon( Planet* mother ) {
43 rotationSpeed = ( ( rand() % 2 ) * 2 - 1 ) * ( rand() % 2000 + 2000 );
44 rotationDistance = 0.01 + 0.03 * ( double )rand() / RAND_MAX;
45 rotationOffset = rand() % 100000;
46 size = 2 + rand() % 2; // [2 ... 4)
47 isAMoon = true;
48 this->mother = mother;
49 shipCreationSpeed = 15000 - size*1000;
50 }
51
52 void
update(Uint32 time)53 Planet::update(Uint32 time)
54 {
55 double angle = (double)(rotationOffset + time) / rotationSpeed;
56 locationVector.setX(cos(angle));
57 locationVector.setY(sin(angle));
58
59 if (buildEndTime <= time)
60 {
61 createShip(time);
62 buildStartTime = time;
63 buildEndTime = time + shipCreationSpeed;
64 }
65
66 if( residentShips.size() > 0 ) {
67 Coordinate location = getLocation();
68 int counter = 0;
69 double offset = 500 * rotationDistance + (double) time / 10000;
70 for( list< Ship* >::iterator i = residentShips.begin(); i != residentShips.end(); i++ ) {
71 double shipX = location.getX() + 0.02 * cos( offset + counter * 2 * M_PI / residentShips.size() );
72 double shipY = location.getY() + 0.02 * sin( offset + counter * 2 * M_PI / residentShips.size() );
73 (*i)->setLocation( shipX, shipY );
74 counter++;
75 }
76 }
77
78 }
79
80 Coordinate
getLocation() const81 Planet::getLocation() const {
82 Coordinate motherLocation;
83 if( isAMoon ) {
84 motherLocation = mother->getLocation();
85 } else {
86 motherLocation.setX( 0.5 );
87 motherLocation.setY( 0.5 );
88 }
89
90 double x = motherLocation.getX() + rotationDistance * locationVector.getX();
91 double y = motherLocation.getY() + rotationDistance * locationVector.getY();
92 return Coordinate( x, y );
93 }
94
95 void
addResident(Ship * ship,Uint32 time)96 Planet::addResident( Ship* ship, Uint32 time )
97 {
98 if (owner != ship->getOwner())
99 {
100 // Planet is conquered.
101 if (residentShips.size() == 0)
102 {
103 setOwner(ship->getOwner());
104
105 // Plays correspoing animation.
106 // TODO: This should be triggered through player->removePlanet()/addPlanet()
107 Uint32 color = owner->getColor();
108 universe->animationQueue->scheduleAction(time + 1200,
109 new SonarAnimation(this,
110 color,
111 isAMoon ? 50 : 100,
112 time,
113 time + 1000,
114 true));
115
116 residentShips.push_back( ship );
117 }
118 else
119 {
120 ship->die();
121 Ship* resident = *( residentShips.begin() );
122 removeResident( resident );
123 resident->die();
124
125 // show an animation of die'ing ships that are of our interest
126 if( ( ship->getOwner()->getPlayerType() == Player::HUMAN ) || ( owner->getPlayerType() == Player::HUMAN ) )
127 universe->animationQueue->scheduleAction( time + 1000, new SonarAnimation( this, ship->getOwner()->getColor(), 20, time, time + 1000, true ) );
128 }
129
130 }
131 else
132 {
133 residentShips.push_back( ship );
134 }
135
136 }
137
138 void
setOwner(Player * newOwner)139 Planet::setOwner(Player *newOwner)
140 {
141 if (owner == newOwner)
142 return;
143
144 if (owner != NULL)
145 owner->removePlanet(this);
146
147 newOwner->addPlanet(this);
148 owner = newOwner;
149
150 }
151
152 void
removeResident(Ship * ship)153 Planet::removeResident( Ship* ship ) {
154 residentShips.remove( ship );
155 }
156
157 void
render(Uint32 time) const158 Planet::render(Uint32 time) const {
159 if( owner->getPlayerType() == Player::HUMAN )
160 renderBuildProgress(time);
161 Coordinate location = getLocation();
162
163 Canvas::drawPlanet(location, size, owner->getColor() );
164 if( selected )
165 {
166 Canvas::drawSelector(location, -4, 10, 10, 0, 0, 0);
167 Canvas::drawSelector(location, -5, 10, 10, 255, 192, 0);
168 }
169
170 if( sourceSelected )
171 {
172 Canvas::drawSelector(location, -4, 10, 10, 0, 0, 0);
173 Canvas::drawSelector(location, -5, 10, 10, 255, 255, 0);
174 }
175
176 if (nearestPlanet)
177 {
178 Canvas::drawNearestPlanetSelector(location, size);
179 }
180 }
181
182 void
renderOrbit() const183 Planet::renderOrbit( ) const {
184 Coordinate centerLocation;
185 if( isAMoon ) {
186 centerLocation = mother->getLocation();
187 } else {
188 centerLocation.setX( 0.5 );
189 centerLocation.setY( 0.5 );
190 }
191 if( owner != NULL )
192 Canvas::drawOrbit(centerLocation, rotationDistance, owner->getColor());
193 if( getMoon() ) {
194 mother->renderOrbit( );
195 }
196 }
197
198 void
renderBuildProgress(Uint32 time) const199 Planet::renderBuildProgress(Uint32 time) const {
200
201 Coordinate location = getLocation();
202 int x = location.getXMapped();
203 int y = location.getYMapped();
204 double percentage = 100.0 * ( time - buildStartTime ) / ( buildEndTime - buildStartTime );
205
206 Canvas::drawBuildProgress(location, size, percentage);
207 }
208
209 double
distance(Planet * planet)210 Planet::distance( Planet* planet ) {
211 return planet->getLocation().distance( getLocation() );
212 }
213
214 void
setNearestPlanet(bool b)215 Planet::setNearestPlanet(bool b) {
216 nearestPlanet = b;
217 }
218
219 void
setSourceSelected(bool selected)220 Planet::setSourceSelected( bool selected ) {
221 sourceSelected = selected;
222 }
223
224 void
setSelected(bool selected)225 Planet::setSelected( bool selected ) {
226 this->selected = selected;
227 }
228
229 void
setUniverse(Universe * universe)230 Planet::setUniverse( Universe* universe ) {
231 this->universe = universe;
232 }
233
234 void
createShip(const Uint32 & time)235 Planet::createShip(const Uint32& time) {
236 if( owner && owner->getPlayerType() != Player::NEUTRAL )
237 owner->addShip(time, this);
238 }
239
240 void
moveResidentsTo(Uint32 time,Planet * destination,int fleetSelection)241 Planet::moveResidentsTo(Uint32 time, Planet *destination, int fleetSelection) {
242 // Save same fuel ... :)
243 if (destination == this || !residentShips.size()) {
244 return;
245 }
246
247 // fleetSelection of 1 means "send one ship". Otherwise it means
248 // "send fleetSelection percent of the available ships.".
249 int decampeeCount = (fleetSelection == 1)
250 ? ((residentShips.size() > 0) ? 1 : 0)
251 : (fleetSelection * residentShips.size()) / 100;
252 if (!decampeeCount)
253 return;
254
255 // Calling Ship->moveTo will affect the residentShips list so we have to
256 // copy the ships which are going for war into their own list.
257 list <Ship *> decampees;
258 for (list <Ship *>::iterator i = residentShips.begin(); decampeeCount > 0; i++) {
259 decampees.push_back(*i);
260 decampeeCount--;
261 }
262
263 for (list <Ship *>::iterator i = decampees.begin(); i != decampees.end(); i++) {
264 (*i)->moveTo(time + rand() % 500, destination, universe->actionQueue);
265 }
266 }
267
268 // ##### PLANETS #####
269
Planets()270 Planets::Planets() {
271 }
272
Planets(int numberOfPlanets,int numberOfMoons)273 Planets::Planets( int numberOfPlanets, int numberOfMoons ) {
274
275 addPlanets( numberOfPlanets );
276 addMoons( numberOfMoons );
277
278 }
279
~Planets()280 Planets::~Planets()
281 {
282 for (list < Planet *>::iterator i = begin(); i != end(); i++)
283 {
284 delete *i;
285 }
286 }
287
288 void
addPlanets(int numberOfPlanets)289 Planets::addPlanets( int numberOfPlanets ) {
290
291 for( int i = 0; i < numberOfPlanets; i++ ) {
292 push_back( new Planet() );
293 }
294
295 }
296
297 void
addMoons(int numberOfMoons)298 Planets::addMoons( int numberOfMoons ) {
299
300 if( size() == 0 ) {
301 cerr << "Error constructing moons, no planets yet" << endl;
302 exit( 1 );
303 }
304
305 for( int i = 0; i < numberOfMoons; i++ ) {
306 iterator motherPlanet;
307 bool done = false;
308
309 do {
310 int mother = rand() % size();
311 motherPlanet = begin();
312 for( int j = 0; j < mother; j++ ) motherPlanet++;
313 done = !(*motherPlanet)->getMoon();
314 } while( !done );
315
316 Planet *p = new Planet();
317 p->makeMoon(*motherPlanet);
318 push_back( p );
319 }
320
321 }
322
323 void
render(Uint32 time) const324 Planets::render(Uint32 time) const {
325 for( const_iterator i = begin(); i != end(); i++ )
326 (*i)->render(time);
327 }
328
329 void
renderOrbits() const330 Planets::renderOrbits( ) const {
331 for( const_iterator i = begin(); i != end(); i++ )
332 (*i)->renderOrbit( );
333 }
334
335 Planet*
closestToCoordinate(const Coordinate & c)336 Planets::closestToCoordinate( const Coordinate& c ) {
337 double closestDistance = 5000;
338 Planet* closestPlanet = NULL;
339 for( iterator i = begin(); i != end(); i++ ) {
340 double distance = (*i)->getLocation().distance( c );
341 if( distance < closestDistance ) {
342 closestDistance = distance;
343 closestPlanet = (*i);
344 }
345 }
346 return closestPlanet;
347 }
348
349 Planet*
closestToCoordinate(const Coordinate & c,double treshold)350 Planets::closestToCoordinate( const Coordinate& c, double treshold ) {
351 Planet* closest = closestToCoordinate( c );
352 if( closest->getLocation().distance( c ) < treshold )
353 return closest;
354 else
355 return NULL;
356 }
357
358 void
select(Selection selection)359 Planets::select( Selection selection ) {
360
361 double minX = selection.getMinX();
362 double maxX = selection.getMaxX();
363 double minY = selection.getMinY();
364 double maxY = selection.getMaxY();
365
366 for( iterator i = begin(); i != end(); i++ ) {
367 Coordinate location = (*i)->getLocation();
368 if( ( location.getX() > minX ) && ( location.getX() < maxX ) && ( location.getY() > minY ) && ( location.getY() < maxY ) )
369 (*i)->setSelected( true );
370 else
371 (*i)->setSelected( false );
372 }
373
374 }
375
376 void
sourceSelect(Selection * selection,Player * owner)377 Planets::sourceSelect( Selection *selection, Player *owner ) {
378
379 double minX = selection->getMinX();
380 double maxX = selection->getMaxX();
381 double minY = selection->getMinY();
382 double maxY = selection->getMaxY();
383
384 for( iterator i = begin(); i != end(); i++ ) {
385 Coordinate location = (*i)->getLocation();
386 if( ( location.getX() > minX ) && ( location.getX() < maxX )
387 && ( location.getY() > minY ) && ( location.getY() < maxY )
388 && (*i)->getOwner() == owner )
389 (*i)->setSourceSelected( true );
390 else
391 (*i)->setSourceSelected( false );
392 }
393
394 }
395
396 void
update(Uint32 time)397 Planets::update(Uint32 time) {
398 for( iterator i = begin(); i != end(); i++ )
399 (*i)->update(time);
400 }
401
402 void
setUniverse(Universe * universe)403 Planets::setUniverse( Universe* universe ) {
404 for( iterator i = begin(); i != end(); i++ )
405 (*i)->setUniverse( universe );
406 }
407
408 Planet*
getRandomPlanet()409 Planets::getRandomPlanet() {
410 int number = rand() % size();
411 iterator i = begin();
412 for( int k = 0; k < number; k++ )
413 i++;
414 return (*i);
415 }
416
417 Planet*
getRandomEnemyPlanet(Player * player)418 Planets::getRandomEnemyPlanet( Player* player ) {
419 // TODO: This has to be implemented
420 }
421
422 Planet*
getRandomNearbyPlanet(Planet * planet)423 Planets::getRandomNearbyPlanet( Planet* planet ) {
424
425 Planet* result;
426 do {
427 result = getRandomPlanet();
428 } while( result == planet );
429 double distance = planet->distance( result );
430
431 for( int i = 0; i < size() / 2; i++ ) {
432 Planet* otherPlanet;
433 do {
434 otherPlanet = getRandomPlanet();
435 } while( otherPlanet == planet );
436 double otherDistance = planet->distance( otherPlanet );
437
438 // which planets do we prefer?
439
440 if( otherPlanet->getOwner() != planet->getOwner() )
441 otherDistance *= 0.1;
442 if( otherPlanet->getOwner()->getPlayerType() == Player::NEUTRAL )
443 otherDistance *= 0.5;
444 if( ! otherPlanet->getMoon() )
445 otherDistance *= 0.8;
446
447 if( otherDistance < distance ) {
448 result = otherPlanet;
449 distance = otherDistance;
450 }
451 }
452
453 return result;
454
455 }
456
457 // ##### CREATESHIPACTION #####
CreateShipAction(Planet * planet)458 CreateShipAction::CreateShipAction(Planet *planet )
459 {
460 this->planet = planet;
461 actionID = 1;
462 }
463
464 void
execute(const Uint32 & time)465 CreateShipAction::execute( const Uint32& time ) {
466 planet->createShip(time);
467 }
468
469