1 /***************************************************************************
2  *   Copyright (C) 2004-2005 by                                            *
3  *     Paolo Sacconier   <axa1981@tin.it>                                  *
4  *     Francesco Tamagni <minchiahead@hacari.org>                          *
5  *                                                                         *
6  *   This program 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 2 of the License, or     *
9  *   (at your option) any later version.                                   *
10  *                                                                         *
11  *   This program 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 this program; if not, write to the                         *
18  *   Free Software Foundation, Inc.,                                       *
19  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20  ***************************************************************************/
21 #include "game.h"
22 
23 namespace gillo {
24 
25 const float Game::possessionBarLength = 0.50;
26 const float Game::maxScoreBarLength = 0.7;
27 const float Game::maxTimerBarLength = 0.4;
28 const float Game::maxMagnetIntensity  = 100.0;
29 const float Game::scoreEpsilon = 0.0100;
30 const float Game::possessionEpsilon = 0.0100;
31 const float Game::splashCoordSet[][4] = {
32 	{0.0, 0.0, 1.0, 1.0},
33 	{0.0, 0.0, 1.0, 1.0},
34 	{0.25, 0.25, 0.75, 0.75},
35 	{0.52500001, 0.1250000, 0.72500001, 0.3250000},
36 	{0.12500000, 0.12500000, 0.32500000, 0.32500000},
37 	{0.4, 0.12500000, 0.6, 0.32500000}
38 };
39 
Game(Video & v,Sound & s,queue<Event> & eq)40 Game::Game(Video& v, Sound& s, queue<Event>& eq)
41 : video(v), sound(s), equeue(eq), context(s), goalSplashDuration(0.0),
42   ball((SimpleBall&) this->addEntity(new SimpleBall(this->context))),
43   p1(),
44   p2(),
45   goal((Goal&) this->addEntity(new Goal(this->context))),
46   goalkeeper((Goalkeeper&) this->addEntity(new Goalkeeper(this->context, this->goal, this->ball))),
47   status(PAUSED), frameCount(0),
48   goalRedSplash(0.5, 0.5, 0.5, 0.5, TEX_GOAL_RED_SPLASH),
49   goalBlueSplash(0.5, 0.5, 0.5, 0.5, TEX_GOAL_BLUE_SPLASH),
50   tieGoalSplash(0.5, 0.5, 0.5, 0.5, TEX_TIEGOAL_SPLASH),
51   mainView(0, 0, 1, 1, new CameraTrack(this->context, &(this->entities))),
52   playerTwoView(  0, 0.8, 0.2, 0.2, NULL),
53   playerOneView(0.8, 0.8, 0.2, 0.2, NULL),
54   attractionStautsP2(0.00, 0, 0.18, 0.08, TEX_ATTRACT),
55   attractionStautsP1(0.82, 0, 1.00, 0.08, TEX_ATTRACT)
56 {
57 	dInitODE();
58 
59 	this->context.setGravity(0,0,ODE_WORLD_GRAVITY);// @fixme remove this
60 	/* ODE setup */
61 // 	dWorldSetERP (context.getWid(), 0.9);
62 	dWorldSetCFM (context.getWid(), 0.01);
63 // 	dWorldSetContactMax0CorrectingVel (context.getWid(), 1);
64 // 	dWorldSetContactSurfaceLayer (context.getWid(), 0.001);
65 	contactgroup = dJointGroupCreate(0);
66 
67 	// @fixme where to place these so can be read from the menu? public?
68 	p1.keys.up		= SDLK_UP;
69 	p1.keys.down	= SDLK_DOWN;
70 	p1.keys.left	= SDLK_LEFT;
71 	p1.keys.right	= SDLK_RIGHT;
72 	p1.keys.fire	= SDLK_m;
73 	p1.keys.jump	= SDLK_k;
74 
75 	p2.keys.up		= SDLK_d;
76 	p2.keys.down	= SDLK_c;
77 	p2.keys.left	= SDLK_x;
78 	p2.keys.right	= SDLK_v;
79 	p2.keys.fire	= SDLK_z;
80 	p2.keys.jump	= SDLK_a;
81 
82 	this->addEntity(new Field(this->context));
83 // 	this->addEntity(new BoxField(this->context));
84 	// magnetic interation
85 	ball.charge = 10;
86 	magneticEntities.push_back((Entity*) &ball);
87 
88 	video.add(&this->mainView);
89 
90 	// load overlay graphics kept in memory all the time
91 	timerBarPtr = new Img(0.295, 0.009, 0.705, 0.06, TEX_TIMER_BAR);
92 	scoreBarRedPtr  = new Img(       0.009, 0.085, 0.009+0.03, 0.085+0.7, TEX_SCORE_BAR_RED);
93 	scoreBarBluePtr = new Img(1-0.009-0.03, 0.085,    1-0.009, 0.085+0.7, TEX_SCORE_BAR_BLUE);
94 	mainOverlayPtr = new Img(0, 0, 1, 1, TEX_OVERLAY);
95 	possessionCursorPtr = new Img(0.21, 0.91, 0.23, 0.99, TEX_POSSESSION_CURSOR);
96 
97 	// new game
98 // 	this->startNew();
99 	this->changeStatus(PAUSED);
100 }
101 
102 
~Game()103 Game::~Game()
104 {
105 	vector<Entity*>::iterator i = entities.begin();
106 	while (i != entities.end() ) {
107 		delete *i;
108 		i++;
109 	}
110 	printf("Game(%p): destroyed\n",this);
111 }
112 
addEntity(Entity * e)113 Entity& Game::addEntity(Entity* e)
114 {
115 	entities.push_back(e);
116 	return *(entities.back());
117 }
118 
addEntityAtRandomPos(Entity * e,float duration)119 Entity& Game::addEntityAtRandomPos(Entity* e, float duration)
120 {
121 	int signx = rand() % 2;
122 	int signy = rand() % 2;
123 	int posx = ((rand() % 4) + 1) * (signx? -1 : 1);
124 	int posy = ((rand() % 4) + 1) * (signy? -1 : 1);
125 
126 	e->timeOfRemoval = this->time + duration;
127  	this->tempEntities.push_back(e);
128 	this->addEntity(e).setPos(posx,posy,0);
129 	return *e;
130 }
131 
removeMagneticEntity(Entity * e)132 bool Game::removeMagneticEntity(Entity * e) {
133 	vector<Entity*>::iterator i = magneticEntities.begin();
134 	while (i != magneticEntities.end() ) {
135 		if (e == *i) {
136 			magneticEntities.erase(i);
137 			return true;
138 		}
139 		i++;
140 	}
141 	return false;
142 }
143 
144 
removeEntity(Entity * e)145 bool Game::removeEntity(Entity* e)
146 {
147 /*	if (e == NULL)
148 		return false;
149 */
150 	vector<Entity*>::iterator i = entities.begin();
151 	while (i != entities.end() ) {
152 		if (e == *i) {
153 			this->removedEntities.push_back(*i);
154 			this->removeMagneticEntity(*i);
155 			this->entities.erase(i);
156 			return true;
157 		}
158 		i++;
159 	}
160 	return false;
161 }
162 
removeEntitiesOfType(Entity::Type et)163 void Game::removeEntitiesOfType(Entity::Type et)
164 {
165 	vector<Entity*>::iterator i = entities.begin();
166 	while (i != entities.end() ) {
167 		if ((*i)->getType() == et) {
168 			this->removedEntities.push_back(*i);
169 			this->removeMagneticEntity(*i);
170 			i = entities.erase(i);
171 		} else
172 			i++;
173 	}
174 }
175 
startNew()176 void Game::startNew() {
177 	this->randomTimeInterval = 0;
178 	this->goalSplashDuration = 0;
179 	this->gameDuration = 60*3;
180 	this->score        = 0;
181 	this->topScore     = 1;
182 	this->time         = 0;
183 	this->goalStatus      = false;
184 	this->rotatingGravity = false;
185 	this->powerShoot      = false;
186 
187 	this->context.setGravity(0,0,ODE_WORLD_GRAVITY);
188 	this->context.releaseBall(NULL);
189 	this->ball.setSize(2*ODE_BALL_RADIUS);
190 
191 // 	video.overlay.add(timerBarPtr);
192 // 	video.overlay.add(new Img(0.295, 0.009, 0.705, 0.06, TEX_TIMER));
193 
194 // 	video.overlay.add(scoreBarRedPtr);
195 // 	video.overlay.add(scoreBarBluePtr);
196 // 	video.overlay.add(new Img(       0.0085, 0.009, 0.0085+0.04, 0.009+0.08, TEX_SCORE_BAR_TIED));
197 // 	video.overlay.add(new Img(1-0.0085-0.04, 0.009,    1-0.0085, 0.009+0.08, TEX_SCORE_BAR_TIED));
198 
199 // 	video.overlay.add(mainOverlayPtr);
200 
201 	// possession bar graphics setup
202 // 	video.overlay.add(new Img(0.21, 0.91, 0.21+this->possessionBarLength, 0.99, TEX_POSSESSION_BAR));
203 // 	video.overlay.add(possessionCursorPtr);
204 //
205 // 	video.overlay.add(&this->attractionStautsP1);
206 // 	video.overlay.add(&this->attractionStautsP2);
207 
208 	video.overlay.rem(endSplash);
209 	if(endSplash != NULL) {
210 		delete endSplash;
211 		endSplash=NULL;
212 	}
213 
214 	// temporary entities should be removed
215 	vector<Entity*>::iterator i = this->tempEntities.begin();
216 	while (i != tempEntities.end() ) {
217 		this->removeEntity(*i);
218 		i++;
219 	}
220 	this->tempEntities.clear();
221 	this->removeEntitiesOfType(Entity::PWRUP);
222 	this->removeEntity(p2.getEntityPtr());
223 	p2.releaseEntity();
224 	this->removeEntity(p1.getEntityPtr());
225 	p1.releaseEntity();
226 
227 	Entity * ep = p1.getEntityPtr();
228 	if (ep == NULL) {
229 		switch (p1.getCar()) {
230 			case CAR_OMNICAR:
231 				ep = p1.setEntity(this->addEntity(new Omnicar(this->context, this->ball,  -0.07, 1, 3)));
232 				break;
233 			case CAR_FOURCAR:
234 				ep = p1.setEntity(this->addEntity(new Omnicar(this->context, this->ball,  -0.07, 1, 4)));
235 				break;
236 		}
237 		magneticEntities.push_back(ep);
238 		playerOneView.changeCameraPtr(new CameraBall(*p1.getEntityPtr(), (Entity&) ball));
239 	}
240 	ep->setPos(0, 1, -1);
241 
242 	if (status == RUNNING) {
243 		ep = p2.getEntityPtr();
244 		if (ep == NULL) {
245 			switch (p2.getCar()) {
246 				case CAR_OMNICAR:
247 					ep = p2.setEntity(this->addEntity(new Omnicar(this->context, this->ball,  0.07, 2, 3)));
248 					break;
249 				case CAR_FOURCAR:
250 					ep = p2.setEntity(this->addEntity(new Omnicar(this->context, this->ball,  0.07, 2, 4)));
251 					break;
252 			}
253 			magneticEntities.push_back(ep);
254 			playerTwoView.changeCameraPtr(new CameraBall(*p2.getEntityPtr(), (Entity&) ball));
255 
256 			video.overlay.add(&this->attractionStautsP2);
257 			video.overlay.add(scoreBarRedPtr);
258 			video.overlay.add(timerBarPtr);
259 
260 		}
261 		ep->setPos(0, -1, -1);
262 	} else if (status == TRAINING){
263 		video.rem(&this->playerTwoView);
264 
265 		video.overlay.rem(&this->attractionStautsP2);
266 		video.overlay.rem(scoreBarRedPtr);
267 		video.overlay.rem(timerBarPtr);
268 	}
269 
270 	video.overlay.add(&this->attractionStautsP1);
271 	video.overlay.add(scoreBarBluePtr);
272 	video.overlay.add(mainOverlayPtr);
273 	video.overlay.add(possessionCursorPtr);
274 
275 	mainView.changeCameraPtr(new CameraTrack(this->context, &this->entities));
276 
277 	ball.setScore(0);
278 	ball.setPos(0, 2, 1);
279 	ball.setVel(0, 0, 0);
280 	this->updateTimer(0);
281 }
282 
collide(Entity * e1,Entity * e2,dGeomID o1,dGeomID o2,dContact * c)283 dContact* Game::collide(Entity* e1, Entity* e2, dGeomID o1, dGeomID o2, dContact* c)
284 {
285 	Entity* pe = NULL;
286 	Entity* e  = NULL;
287 
288 	if ((p1.getEntityPtr() == e1) || (p2.getEntityPtr() == e1)) {
289 		pe = e1;
290 		e  = e2;
291 	} else if ((p1.getEntityPtr() == e2) || (p2.getEntityPtr() == e2)) {
292 		pe = e2;
293 		e  = e1;
294 	}
295 
296 	if (pe && e) {
297 		if (e->isA(Entity::PWRUP)) {
298 			if (this->removeEntity(e)){
299 				switch (((PwrUp *)e)->getPType()){ // it would be nice to insert the effect into the powerup
300 					case PwrUp::BALL_INFLATOR :
301 						if (ball.getSize() < 4*ODE_BALL_RADIUS) {
302 							ball.setSize(8*ODE_BALL_RADIUS);
303 							this->sound.play(Sound::PWRUP_BALL_INFLATE);
304 						} else {
305 							ball.setSize(2*ODE_BALL_RADIUS);
306 							this->sound.play(Sound::PWRUP_BALL_DEFLATE);
307 						}
308 						break;
309 /*					case PwrUp::GRAVITY_INVERTER :
310 						this->sound.play(Sound::PWRUP_GRAVITY_INVERTER);
311 						this->context.invertGravity();
312 						break;*/
313 					case PwrUp::GRAVITY_ROTATOR :
314 						this->sound.play(Sound::PWRUP_GRAVITY_ROTATOR);
315 						this->rotatingGravity = true;
316 						break;
317 					case PwrUp::BALL_ADDER :
318 						this->sound.play(Sound::PWRUP_BALL_ADD);
319 						this->magneticEntities.push_back(&this->addEntityAtRandomPos(new SimpleBall(this->context, 3*ODE_BALL_RADIUS, this->context.getState(STA_ODDBALL)), 30.0));
320 						break;
321 					case PwrUp::CHARGE_INVERTER :
322 						this->sound.play(Sound::PWRUP_CHARGE_INVERTER);
323 						this->context.releaseBall(NULL);
324 						this->ball.charge = -this->ball.charge;
325 						this->powerShoot = false;
326 						//@fixme error
327 						break;
328 				}
329 
330 			}
331 		} /*else if (e->isA(Entity::BALL)) {
332 			SimpleBall* b = (SimpleBall*) e;
333 			float diff;
334 			if (p1.getEntityPtr() == pe)
335 				diff = -0.01;
336 			else
337 				diff = +0.01;
338 			b->setScore(b->getScore() +diff);
339 		}*/
340 	} else { if (!e && pe ) printf("Wazzap?\n"); }
341 
342 	static bool contactBallGoalHappened;
343 	static bool contactBallFieldHappened;
344 	static int lastContactBallGoalFrameNum;
345 	static int lastContactBallFieldFrameNum;
346 	if (Entity::isPair(e1, e2, Entity::BALL, Entity::GOAL) // this way balls can pass through the goal
347 		&& (goal.isScoreGeom(o1) || goal.isScoreGeom(o2))) {
348 		if (contactBallGoalHappened && (lastContactBallGoalFrameNum < this->frameCount-2) && ((e1 == &ball) || (e2 == &ball))) {
349 			contactBallGoalHappened = false;
350 		}
351 		if (!contactBallGoalHappened) {
352 			this->score += ball.getScore();
353 			this->goalStatus = true;
354 			contactBallGoalHappened = true;
355 		}
356 		lastContactBallGoalFrameNum = this->frameCount;
357 		return NULL;
358 	} else if (Entity::isPair(e1, e2, Entity::BALL, Entity::GOAL) // this way balls can pass through the area
359 		&& (goal.isAreaGeom(o1) || goal.isAreaGeom(o2))) {
360 		return NULL;
361 	} else if (Entity::isPair(e1, e2, Entity::GOALKEEPER, Entity::GOAL) // this way the goalkeeper can pass through the area
362 		&& (goal.isAreaGeom(o1) || goal.isAreaGeom(o2))) {
363 		return NULL;
364 	} else if ((Entity::isPair(e1, e2, Entity::CAR, Entity::GOAL)
365 			|| Entity::isPair(e1, e2, Entity::PWRUP, Entity::GOAL)) // car collides softly with area
366 		&& (goal.isAreaGeom(o1) || goal.isAreaGeom(o2))) {
367 		if (e1->isA(Entity::CAR) && ((Omnicar*)e1)->isPickerGeom(o1) ||
368 		    e2->isA(Entity::CAR) && ((Omnicar*)e2)->isPickerGeom(o2))
369 			return NULL;
370 		c->surface.mode =  dContactSoftERP | dContactSoftCFM ;
371 		c->surface.bounce = 0;
372 		c->surface.bounce_vel = 5;
373 		c->surface.mu = 0.1;
374 		c->surface.soft_erp = 0.5;
375 		c->surface.soft_cfm = 0.5;
376 	} else if (Entity::isPair(e1, e2, Entity::BALL, Entity::CAR)) {
377 		c->surface.mode = dContactBounce;
378 		c->surface.bounce = 0.1;
379 		c->surface.bounce_vel = 5;
380 		c->surface.mu = 100;
381 	} else if (Entity::isPair(e1, e2, Entity::BALL, Entity::BALL)) {
382 		c->surface.mode = dContactBounce;
383 		c->surface.bounce = 0.3;
384 		c->surface.bounce_vel = 5;
385 		c->surface.mu = 100;
386 	} else if (Entity::isPair(e1, e2, Entity::CAR, Entity::CAR) && (e1->isA(Entity::CAR) && ((Omnicar*)e1)->isPickerGeom(o1) ||
387 		    e2->isA(Entity::CAR) && ((Omnicar*)e2)->isPickerGeom(o2))) {
388 		return NULL;
389 	} else if (Entity::isPair(e1, e2, Entity::FIELD, Entity::CAR)
390 			|| Entity::isPair(e1, e2, Entity::FIELD, Entity::CUBE)) {
391 
392 		if (e1->isA(Entity::CAR) && ((Omnicar*)e1)->isPickerGeom(o1) ||
393 		    e2->isA(Entity::CAR) && ((Omnicar*)e2)->isPickerGeom(o2))
394 			return NULL;
395 		c->surface.mode = dContactBounce | dContactApprox1;
396 		c->surface.bounce = 0.0;
397 		c->surface.bounce_vel = 5;
398 		c->surface.mu = 1000;
399 	} else if (Entity::isPair(e1, e2, Entity::FIELD, Entity::BALL)
400 			|| Entity::isPair(e1, e2, Entity::GOAL,  Entity::BALL)) {
401 		c->surface.mode = dContactBounce | dContactApprox1;
402 		c->surface.bounce = 0.3;
403 		c->surface.bounce_vel = 0;
404 		c->surface.mu = 100;
405 		if (contactBallFieldHappened && (lastContactBallFieldFrameNum < this->frameCount-4)) {
406 			contactBallFieldHappened = false;
407 		}
408 		if (!contactBallFieldHappened) {
409 			this->sound.play(Sound::BOING);
410 			contactBallFieldHappened = true;
411 		}
412 		lastContactBallFieldFrameNum = this->frameCount;
413 	} else if (Entity::isPair(e1, e2, Entity::PWRUP, Entity::FIELD)) {
414 		c->surface.mode = 0;
415 		c->surface.mu = 100000000;
416 	} else if (Entity::isPair(e1, e2, Entity::GOALKEEPER, Entity::FIELD)) {
417 		c->surface.mode = 0;
418 		c->surface.mu = 100000000;
419 	} else if ((Entity::isPair(e1, e2, Entity::GOALKEEPER, Entity::GOAL) ||
420 			    Entity::isPair(e1, e2, Entity::CAR, Entity::GOAL))
421 		&& (goal.isScoreGeom(o1) || goal.isScoreGeom(o2))) {
422 		return NULL;
423 	} else if (e1->isA(Entity::PWRUP) || e2->isA(Entity::PWRUP)) {
424 		return NULL;
425 	} else {
426 		c->surface.mode = dContactBounce;
427 		c->surface.bounce = 1;
428 		c->surface.bounce_vel = 5;
429 		c->surface.mu = 100;
430 	}
431 
432 	return c;
433 }
434 
collide_callback(void * data,dGeomID o1,dGeomID o2)435 void collide_callback (void *data, dGeomID o1, dGeomID o2) {
436 	Game* g = (Game*) data;
437 	Entity *e1=NULL, *e2=NULL;
438 	dContact* co = NULL;
439 	dJointID c;
440 
441 	if (dGeomIsSpace (o1) || dGeomIsSpace (o2)) {
442 		// colliding a space with something
443 		dSpaceCollide2 (o1,o2,data, collide_callback);
444 		// collide all geoms internal to the space(s)
445 		//@fixme why was this very necessary?
446 // 		if (dGeomIsSpace (o1))
447 // 			dSpaceCollide ((dSpaceID) o1,data, collide_callback);
448 // 		if (dGeomIsSpace (o2))
449 // 			dSpaceCollide ((dSpaceID) o2,data, collide_callback);
450 	} else if (g->contactcount < ODE_MAX_CONTACTS) { // @fixme check for every contact
451 		// colliding two non-space geoms, so generate contact
452 		// points between o1 and o2
453 		e1 = (Entity*) dGeomGetData(o1);
454 		e2 = (Entity*) dGeomGetData(o2);
455 		if (e1 == NULL || e2 == NULL) {
456 			// this should never happen!
457 			throw Error::Game("Error resolving a collision");
458 			return;
459 		}
460 // 		printf("%10d - %s and %s collided (gids %10d <-> %10d)\n", g->frameCount, e1->toString(), e2->toString(), o1, o2);
461 		if (e1 == e2)
462 			return;
463 
464 		int num_contact = dCollide(o1, o2, ODE_CONTACTS, &g->contacts[g->contactcount].geom, sizeof(dContact));
465 // 		printf("%10d - %s and %s collided %d times\n (gids %10d <-> %10d)\n", g->frameCount, e1->toString(), e2->toString(), num_contact, o1, o2);
466 
467 		if (num_contact) {
468 			dBodyID b1, b2, tmp;
469 			b1 = dGeomGetBody(o1);
470 			b2 = dGeomGetBody(o2);
471 			if (b1 || b2) {
472 				for (int i = 0; i < num_contact; i++) {
473 					co = g->collide(e1, e2, o1, o2, &(g->contacts[g->contactcount]));
474 					if (co) {
475 						c = dJointCreateContact(g->context.getWid(), g->contactgroup, co);
476 						dJointAttach(c, b1, b2);
477 						co = NULL;
478 					}
479 					g->contactcount++;
480 				}
481 			}
482 		}
483 	} else {
484 // 		throw Error::Game("Too many contacts");
485 		e1 = (Entity*) dGeomGetData(o1);
486 		e2 = (Entity*) dGeomGetData(o2);
487 		printf("%10d - %s and %s collided (gids %10d <-> %10d) - IGNORED\n", g->frameCount, e1->toString(), e2->toString(), o1, o2);
488 		printf("Too many contacts, autopausing\n");
489 		g->changeStatus(Game::SELFPAUSED);
490 		return;
491 	}
492 }
493 
update(float dt)494 void Game::update(float dt)
495 {
496 	if (status == RUNNING || status == TRAINING) {
497 		this->ballScore = ball.getScore();
498 		this->time += dt;
499 		this->frameCount++;
500 		if (status == RUNNING)
501 			this->updateTimer(dt);
502 		this->updatePossession(dt);
503 		this->updateScore(dt);
504 		this->updatePlayers(dt);
505 
506 		static float targetAngle = 0;
507 		static float currentAngle = 0;
508 		static int sign = 0;
509 		static sgVec3 savedGravity;
510 		if (this->rotatingGravity) {
511 			if ((targetAngle == 0) && (currentAngle == 0)) {
512 				targetAngle =  90 + 90*(rand() %3);
513 				sign = rand() %2 ? -1 : 1;
514 				sgCopyVec3(savedGravity, this->context.getGravity());
515 			}
516 			float inc = 70.0*dt;
517 			this->context.rotateGravity(sign*inc);
518 			currentAngle += inc;
519 			if (currentAngle > targetAngle) {
520 				this->rotatingGravity = false;
521 				this->context.setGravity(savedGravity);
522 				this->context.rotateGravity(sign*targetAngle);
523 				targetAngle = 0;
524 				currentAngle = 0;
525 			}
526 		}
527 
528 		static float goalTime = 0;
529 		static float possessionAtGoalTime = 0;
530 		static float splashCoord[4];
531 		static Img* splash = NULL;
532 		if (this->goalStatus) {
533 			this->context.releaseBall(NULL);
534 			if (this->powerShoot) {
535 				this->ball.setScore(0.0);
536 				this->powerShoot = false;
537 			}
538 			this->goalStatus = false;
539 			possessionAtGoalTime = this->ballScore;
540 			if (sgAbs(possessionAtGoalTime) < Game::possessionEpsilon) {
541 				if ( splash != &this->tieGoalSplash) {
542 					this->video.overlay.rem(splash);
543 					splash = &this->tieGoalSplash;
544 					for (int i = 0; i < 4; i++)
545 						splashCoord[i] = 0.5;
546 					splash->setCoords(splashCoord);
547 					this->video.overlay.add(splash);
548 					goalTime = 0;
549 					goalSplashDuration = 0;
550 				}
551 				this->sound.play(Sound::TIEGOAL);
552 			} else {
553 				if ((possessionAtGoalTime < -Game::possessionEpsilon) && (splash != &this->goalBlueSplash)) {
554 					this->video.overlay.rem(splash);
555 					splash = &this->goalBlueSplash;
556 					for (int i = 0; i < 4; i++)
557 						splashCoord[i] = 0.5;
558 					splash->setCoords(splashCoord);
559 					this->video.overlay.add(splash);
560 					goalTime = 0;
561 					goalSplashDuration = 0;
562 				} else if ((possessionAtGoalTime > Game::possessionEpsilon) && (splash != &this->goalRedSplash)) {
563 					this->video.overlay.rem(splash);
564 					splash = &this->goalRedSplash;
565 					for (int i = 0; i < 4; i++)
566 						splashCoord[i] = 0.5;
567 					splash->setCoords(splashCoord);
568 					this->video.overlay.add(splash);
569 					goalTime = 0;
570 					goalSplashDuration = 0;
571 				}
572 				this->sound.play(Sound::GOAL);
573 			}
574 			goalSplashDuration += 5;
575 		} else if (goalTime < goalSplashDuration) {
576 			goalTime += dt;
577 			int target = 2;
578 			if (possessionAtGoalTime < -Game::possessionEpsilon) {
579 				target = 0;
580 			} else if (possessionAtGoalTime > Game::possessionEpsilon) {
581 				target = 1;
582 			} else {
583 				target = 2;
584 			}
585 			if (goalTime > 1)
586 				target += 3;
587 // 			else if (goalTime > 5)
588 // 				target +=
589 			for (int i = 0; i < 4; i++)
590 				splashCoord[i] += 4*(splashCoordSet[target][i] -splashCoord[i])*dt;
591 			splash->setCoords(splashCoord);
592 		} else if (goalTime != 0) {
593 			goalTime = 0;
594 			goalSplashDuration = 0;
595 			this->video.overlay.rem(splash);
596 			splash = NULL;
597 		}
598 
599 
600 		// generate some random events
601 // 		printf("random timer %f of %f\n", time, random_time);
602 		if (this->time > randomTimeInterval) {
603 			// random event occurred
604 			randomTimeInterval = this->time + 10 + rand() % 10;
605 			PwrUp::PType ptype;
606 			switch(rand() % 4) { // @fixme we shouldn't remeber the number of powerup types
607 // 			switch(3) {
608 // 				case 0:
609 // 					ptype = PwrUp::GRAVITY_INVERTER;
610 // 					break;
611 				case 0:
612 					ptype = PwrUp::BALL_INFLATOR;
613 					break;
614 				case 1:
615 					ptype = PwrUp::GRAVITY_ROTATOR;
616 					break;
617 				case 2:
618 					ptype = PwrUp::BALL_ADDER;
619 					break;
620 				case 3:
621 					ptype = PwrUp::CHARGE_INVERTER;
622 					break;
623 			}
624 			this->sound.play(Sound::PWRUP_NEW);
625 			this->addEntityAtRandomPos(new PwrUp(this->context, ptype), 15.0);
626 		}
627 
628 		for (int c = 0; c < ODE_PASSES; c++) {
629 			this->contactcount = 0;
630 			dSpaceCollide (context.getSid(), (void *) this, collide_callback);
631 // 			dWorldStep(context.getWid(), dt/ODE_PASSES);
632 			dWorldQuickStep(context.getWid(), dt/ODE_PASSES);
633 			dJointGroupEmpty(contactgroup);
634 	// 		if (this->contactcount)
635 	// 			printf("Created %d joints in this pass\n", this->contactcount);
636 		}
637 		// update active entities
638 		vector<Entity*>::iterator i = this->entities.begin();
639 		while (i != entities.end() ) {
640 			(*i)->update(dt);
641 			i++;
642 		}
643 		// temporary entities disappear automatically
644 		i = this->tempEntities.begin();
645 		while (i != tempEntities.end() ) {
646 			if (this->time > (*i)->timeOfRemoval) {
647 				this->removeEntity(*i);
648 				i = this->tempEntities.erase(i);
649 			} else
650 				i++;
651 		}
652 		// remove entities no longer needed (because destroyed by some collision in previous frame)
653 		i = this->removedEntities.begin();
654 		while (i != this->removedEntities.end()) {
655 			delete *i;
656 			i++;
657 		}
658 		removedEntities.clear();
659 
660 		// magnetic interaction forces generation
661 		sgVec3 zero;
662 		sgZeroVec3(zero);
663 		// @fixme optimize this loop!!!
664 		i = this->magneticEntities.begin();
665 		while (i != this->magneticEntities.end()) {
666 			vector<Entity*>::iterator j = i +1;
667 			while(j != this->magneticEntities.end()) {
668 				sgVec3 posI;
669 				sgVec3 posJ;
670 				sgVec3 dirI;
671 				sgVec3 dirJ;
672 				sgVec3 diff;
673 				(*i)->getMagnetPos(posI);
674 				(*j)->getMagnetPos(posJ);
675 				(*i)->getMagnetDir(dirI);
676 				(*j)->getMagnetDir(dirJ);
677 				sgSubVec3(diff, posJ, posI);
678 				float l = sgLengthVec3(diff);
679 				sgNormalizeVec3(diff);
680 
681 				if (Entity::isPair((*i), (*j), Entity::BALL, Entity::BALL)) {
682 					float intensity = (*i)->charge*(*j)->charge/l/l;
683 					if (sgAbs(intensity) > Game::maxMagnetIntensity)
684 						intensity = intensity > 0 ? Game::maxMagnetIntensity : -Game::maxMagnetIntensity;
685 					sgScaleVec3(diff, intensity);
686 					(*i)->addForce(diff);
687 				}
688 				if     (sgEqualVec3(zero, dirI) &&
689 					sgEqualVec3(zero, dirJ)) {
690 					// do nothing
691 				} else if (sgEqualVec3(zero, dirJ)) {
692 					float directional = sgScalarProductVec3(diff, dirI);
693 					if (directional > 0)
694 						directional = 0;
695 					float intensity = -directional*(*i)->charge*(*j)->charge/l/l;
696 					if (sgAbs(intensity) > Game::maxMagnetIntensity)
697 						intensity = intensity > 0 ? Game::maxMagnetIntensity : -Game::maxMagnetIntensity;
698 					sgScaleVec3(diff, intensity);
699 					(*j)->addForce(diff);
700 				} else if (sgEqualVec3(zero, dirI)) {
701 					float directional = sgScalarProductVec3(diff, dirJ);
702 					if (directional > 0)
703 						directional = 0;
704 					float intensity = -directional*(*i)->charge*(*j)->charge/l/l;
705 					if (sgAbs(intensity) > Game::maxMagnetIntensity)
706 						intensity = intensity > 0 ? Game::maxMagnetIntensity : -Game::maxMagnetIntensity;
707 					sgScaleVec3(diff, intensity);
708 					(*i)->addForce(diff);
709 				}
710 				j++;
711 			}
712 			i++;
713 		}
714 		if (sgAbs(this->ballScore) >= 1) {
715 			powerShoot = true;
716 			this->sound.play(Sound::POWERSHOOT);
717 		}
718 		if (this->powerShoot) {
719 			sgVec3 posB;
720 			sgVec3 posG;
721 			sgVec3 diff;
722 			this->ball.getMagnetPos(posB);
723 			this->goalkeeper.getMagnetPos(posG);
724 			sgSubVec3(diff, posG, posB);
725 			float l = sgLengthVec3(diff);
726 			sgNormalizeVec3(diff);
727 			float intensity = 1000/l;
728 			sgScaleVec3(diff, intensity);
729 			ball.addForce(diff);
730 // 			sgNegateVec3(diff);
731 // 			goalkeeper.addForce(diff);
732 		}
733 	}
734 
735 	video.draw(this->context.getScenePtr(), this->context.getAlphaScenePtr()); // @fixme remove pointers and use references
736 }
737 
updatePossession(float dt)738 void Game::updatePossession(float dt) {
739 	float targetPos = this->possessionBarLength/2*(-this->ballScore+1)+0.24;
740 	float posX = possessionCursorPtr->getPosX();
741 	float diff = targetPos -posX;
742 	if (diff != 0)
743 		this->possessionCursorPtr->setPos(posX +2*diff*dt, 0.915);
744 }
745 
updateScoreBarHeight(Rect * r,float h,float dt)746 void Game::updateScoreBarHeight(Rect* r, float h, float dt) {
747 	float oh = r->getHeight();
748 	float diff = h -oh;
749 	if (diff != 0)
750 		r->setHeight(oh +2*diff*dt);
751 }
752 
updateScore(float dt)753 void Game::updateScore(float dt) {
754 	float abss = sgAbs(this->score);
755 	if (abss > topScore)
756 		topScore = abss;
757 	float targetHeight = Game::maxScoreBarLength*abss/topScore;
758 	if (this->score > 0) {
759 		this->updateScoreBarHeight(this->scoreBarBluePtr,            0, dt);
760 		this->updateScoreBarHeight(this->scoreBarRedPtr,  targetHeight, dt);
761 	} else {
762 		this->updateScoreBarHeight(this->scoreBarRedPtr,             0, dt);
763 		this->updateScoreBarHeight(this->scoreBarBluePtr, targetHeight, dt);
764 	}
765 }
766 
updateTimer(float dt)767 void Game::updateTimer(float dt) {
768 	this->timerBarPtr->setWidth(Game::maxTimerBarLength*time/this->gameDuration);
769 	if (time > this->gameDuration) {
770 		this->end();
771 // 		time = 0;
772 	}
773 }
774 
updatePlayers(float dt)775 void Game::updatePlayers(float dt) {
776 	static ssgTexture attractTex(TEX_ATTRACT, 0, 0, 0);
777 	static ssgTexture repulseTex(TEX_REPULSE, 0, 0, 0);
778 
779 	ssgTexture* newTex;
780 	float ballCharge = this->ball.charge;
781 
782 	float p1Charge   = this->p1.getEntityPtr()->charge;
783 	newTex = ballCharge*p1Charge >= 0 ? &attractTex : &repulseTex;
784 	this->attractionStautsP1.setExternalTexture(newTex);
785 
786 	if (status == RUNNING) {
787 		float p2Charge   = this->p2.getEntityPtr()->charge;
788 		newTex = ballCharge*p2Charge >= 0 ? &attractTex : &repulseTex;
789 		this->attractionStautsP2.setExternalTexture(newTex);
790 	}
791 	if (this->p1.getEntityPtr()->hasBall()) {
792 		video.rem(&this->playerTwoView);
793 		video.add(&this->playerOneView);
794 	} else if (status == RUNNING && this->p2.getEntityPtr()->hasBall()) {
795 		video.rem(&this->playerOneView);
796 		video.add(&this->playerTwoView);
797 	} else {
798 		video.rem(&this->playerOneView);
799 		video.rem(&this->playerTwoView);
800 	}
801 }
802 
end()803 void Game::end() {
804 	this->changeStatus(FINISHED);
805 	printf("SCORE: %f\n", this->score);
806 	if (this->score < -Game::scoreEpsilon) {
807 		this->sound.play(Sound::END);
808 		endSplash = new Img(0,0,1,1,TEX_P1WINS_SPLASH);
809 	} else if (this->score > Game::scoreEpsilon) {
810 		this->sound.play(Sound::END);
811 		endSplash = new Img(0,0,1,1,TEX_P2WINS_SPLASH);
812 	} else {
813 		this->sound.play(Sound::ENDTIE);
814 		endSplash = new Img(0,0,1,1,TEX_PARITY_SPLASH);
815 	}
816 	video.overlay.add(endSplash);
817 }
818 
react(SDL_Event * event)819 void Game::react(SDL_Event *event)
820 {
821 	switch( event->type ) {
822 		case SDL_KEYDOWN:
823 			handleKeyDown(&event->key.keysym);
824 			break;
825 		case SDL_KEYUP:
826 			handleKeyUp(&event->key.keysym);
827 			break;
828 		case SDL_QUIT:
829 			{
830 				Event ev(Event::APP, Event::QUIT);
831 				equeue.push(ev);
832 			}
833 			break;
834 		default:
835 //			throw Error::Event("Unhandled event");
836 			break;
837 	}
838 	p1.react(event);
839 	p2.react(event);
840 }
841 
842 
handleKeyDown(SDL_keysym * keysym)843 void Game::handleKeyDown( SDL_keysym* keysym )
844 {
845 	static GLboolean fullscreen = GL_FALSE;
846 
847 	switch( keysym->sym ) {
848 		case SDLK_ESCAPE:
849 			if (status == RUNNING || status == TRAINING || status == FINISHED) {
850 				Event ev(Event::MENU, Event::SHOW);
851 				equeue.push(ev);
852 				changeStatus(PAUSED);
853 			}
854 			break;
855 		case SDLK_RSHIFT:
856 		case SDLK_LSHIFT:
857 			break;
858 		case SDLK_SPACE:
859 			if (status == RUNNING || status == TRAINING)
860 				this->startNew();
861 			break;
862 		case SDLK_1:
863 			mainView.changeCameraPtr(new CameraTrack(this->context, &this->entities));
864 			break;
865 		case SDLK_2:
866 			mainView.changeCameraPtr(new CameraBall(*p1.getEntityPtr(), (Entity&) ball));
867 			break;
868 		case SDLK_3:
869 			if (status == RUNNING)
870 				mainView.changeCameraPtr(new CameraBall(*p2.getEntityPtr(), (Entity&) ball));
871 			break;
872 		case SDLK_4:
873 			if (status == RUNNING)
874 				mainView.changeCameraPtr(new CameraBall(*p1.getEntityPtr(), *p2.getEntityPtr()));
875 			break;
876 		case SDLK_5:
877 			if (status == RUNNING)
878 				mainView.changeCameraPtr(new CameraBall(*p2.getEntityPtr(), *p1.getEntityPtr()));
879 			break;
880 		case SDLK_6:
881 			mainView.changeCameraPtr(new CameraBall((Entity&) ball, (Entity&) goal));
882 			break;
883 		case SDLK_7:
884 			mainView.changeCameraPtr(new CameraBall((Entity&) goal, (Entity&) ball));
885 			break;
886 		case SDLK_f:
887 			switch (video.getMode()) {
888 				case Video::FULLSCREEN:
889 					video.switchToMode(Video::WINDOW);
890 					break;
891 				default:
892 					video.switchToMode(Video::FULLSCREEN);
893 					break;
894 			}
895 			break;
896 		case SDLK_s:
897 			sound.toggle();
898 			break;
899 		case SDLK_q:
900 			{
901 				Event ev(Event::APP, Event::QUIT);
902 				equeue.push(ev);
903 			}
904 			break;
905 /*		case SDLK_b:
906 			this->context.invertGravity();
907 			break;
908 		case SDLK_r:
909 			this->rotateGravityToggle();
910 			break;*/
911 		case SDLK_p:
912 			if (status == SELFPAUSED)
913 				changeStatus(previousStatus);
914 			else {
915 				changeStatus(SELFPAUSED);
916 			}
917 			break;
918 		case SDLK_h:
919 			this->context.toggleDebug();
920 			break;
921 		default:
922 			break;
923     }
924 }
925 
handleKeyUp(SDL_keysym * keysym)926 void Game::handleKeyUp( SDL_keysym* keysym ) {
927     switch( keysym->sym ) {
928 		default:
929 			break;
930 	}
931 }
932 
react(const Event::Type et)933 void Game::react(const Event::Type et)
934 {
935 	switch (et) {
936 		case Event::START_TRAINING:
937 // 			this->training = true;
938 			printf("starting training\n");
939 			this->changeStatus(TRAINING);
940 			this->startNew();
941 			break;
942 		case Event::START:
943 // 			this->training = false;
944 			printf("starting a new game\n");
945 			this->changeStatus(RUNNING);
946 			this->startNew();
947 			break;
948 		case Event::RESUME:
949 			this->changeStatus(previousStatus);
950 			break;
951 		case Event::PAUSE:
952 			this->changeStatus(PAUSED);
953 			break;
954 		case Event::CV_P1_OMNICAR:
955 			p1.setCar(CAR_OMNICAR);
956 			break;
957 		case Event::CV_P2_OMNICAR:
958 			p2.setCar(CAR_OMNICAR);
959 			break;
960 		case Event::CV_P1_FOURCAR:
961 			p1.setCar(CAR_FOURCAR);
962 			break;
963 		case Event::CV_P2_FOURCAR:
964 			p2.setCar(CAR_FOURCAR);
965 			break;
966 		default:
967 			break;
968 	}
969 }
970 
changeStatus(Status s)971 bool Game::changeStatus(Status s)
972 {
973 	previousStatus = status;
974 	if ((status == PAUSED) && (s == RUNNING)) {
975 		status = s;
976 		sound.music(Sound::GAME1);
977 	} else if ((status == PAUSED) && (s == TRAINING)) {
978 		status = s;
979 		sound.music(Sound::TRAINING);
980 	} else if ((status == RUNNING || status == TRAINING) && (s == SELFPAUSED)) {
981 		status = s;
982 	} else if ((status == SELFPAUSED) && (s == RUNNING || s == TRAINING)) {
983 		status = s;
984 	} else if ((status == RUNNING || status == TRAINING) && (s == PAUSED)) {
985 		status = s;
986 	} else if ((status == RUNNING) && (s == FINISHED)) {
987 		status = s;
988 	} else if ((status == FINISHED) && (s == PAUSED)){
989 		status = s;
990 	} else if ((status == PAUSED) && (s == FINISHED)){
991 		status = s;
992 	}else {
993 		//@fixme throw something
994 	}
995 	return false;
996 }
997 };
998