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