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