1 /*
2 * This code is released under the GNU General Public License. See COPYING for
3 * details. Copyright 2003 John Spray: spray_john@users.sourceforge.net
4 */
5
6
7 #include <string.h>
8 #include <SDL_opengl.h>
9 #include <math.h>
10 #include <sys/time.h>
11 #include <unistd.h>
12
13 #include "Game.h"
14 #include "Events.h"
15 #include "SoundCore.h"
16 #include "Particle.h"
17 #include "Visual.h"
18 #include "Player.h"
19 #include "Missile.h"
20 #include "Config.h"
21 #include "Ufo.h"
22 #include "Menu.h"
23 #include "Obstacle.h"
24 #include "Vector.h"
25 #include "ScoreBoard.h"
26 #include "PowerUp.h"
27
28
29 extern Config GLOB_conf;
30 extern ScoreBoard GLOB_scores;
31
Game(SDL_Surface * newscreen)32 Game::Game(SDL_Surface* newscreen)
33 {
34 quit=0;
35 verbose=GLOB_conf.verbose;
36 screen=newscreen;
37 timescale=1.0f;
38 score=0;
39 g.y=-0.0001;
40 spawnticker=0;
41 spawndelay=5000;
42 }
43
~Game()44 Game::~Game()
45 {
46 }
47
Happen()48 int Game::Happen()
49 {
50 long frames=0;
51
52 struct timeval tv;
53 struct timeval oldtv;
54
55 player=new Player(this);
56 if(GLOB_conf.twoplayer)
57 player2=new Player(this);
58
59 visual=new Visual(this);
60 visual->InitGL();
61
62 sound=new SoundCore;
63 sound->SetEar(&visual->GetMainCam()->s,&visual->GetMainCam()->v,
64 &visual->GetMainCam()->face,&visual->GetMainCam()->up);
65
66 visual->ShowLoading(0.1f);
67 visual->LoadTexture("arenamesh.png");
68 visual->LoadTexture("arenabase.png");
69 visual->LoadTexture("arenawall.png");
70 visual->LoadTexture("skyup.png");
71 visual->LoadTexture("skydown.png");
72 visual->LoadTexture("skyleft.png");
73 visual->LoadTexture("skyright.png");
74 visual->LoadTexture("skyback.png");
75 visual->LoadTexture("skyfront.png");
76 visual->ShowLoading(0.5f);
77 visual->LoadTexture("shield.png");
78 visual->LoadTexture("explosion1.png");
79 visual->LoadTexture("missiletrail.png");
80 visual->LoadTexture("shadow.png");
81 visual->ShowLoading(0.65f);
82 visual->LoadTexture("driveparticle.png");
83 visual->LoadTexture("lifeicon.png");
84 visual->LoadTexture("skyscraper.png");
85 visual->LoadTexture("menucursor.png");
86 visual->LoadTexture("smoke1.png");
87 visual->LoadTexture("powerupshell.png");
88 visual->LoadTexture("tracer.png");
89 visual->LoadTexture("muzzleflash.png");
90 visual->LoadTexture("halfdriveparticle.png");
91 visual->ShowLoading(0.8f);
92
93 char *soundfiles[]={ "fire.wav","boom.wav","engine.wav","ufo.wav",
94 "ufodie.wav","gotthelife.wav","cannon.wav",
95 "spotted.wav","playerdie.wav","shield.wav",
96 "mine.wav",NULL};
97
98 for(int i=0;soundfiles[i];i++){
99 if(verbose) printf("Game::Happen: Loading wavefile: %s\n",soundfiles[i]);
100 sound->LoadSample(soundfiles[i]);
101 }
102
103 //Note that textures corresponding to meshes
104 //don't need to be preloaded, LoadMesh does it
105 visual->LoadMesh("player.mesh");
106 visual->LoadMesh("ufo.mesh");
107 visual->LoadMesh("mine.mesh");
108
109 evs=new Events;
110 evs->game=this;
111 evs->keyboardtarget=player;
112
113 arena=new Arena(this);
114 arena->MakeObstacles();
115 arena->GenerateGeometry();
116
117 missilelist= new LList<Missile>;
118
119 ufolist=new LList<Ufo>;
120
121 minelist=new LList<Mine>;
122
123 poweruplist=new LList<PowerUp>;
124
125 visual->ShowLoading(0.9f);
126
127 evs->targetcam=visual->GetMainCam();
128
129 player->Respawn();
130 if(GLOB_conf.twoplayer)
131 player2->Respawn();
132
133 #ifdef RELEASE
134 SDL_WM_GrabInput(SDL_GRAB_ON);
135 #endif
136
137 visual->ShowLoading(1.0f);//yes,yes, it's pointless
138
139 #ifdef LINUX
140 gettimeofday(&oldtv,NULL);
141 #else
142 ticks=SDL_GetTicks();
143 #endif
144 startticks=SDL_GetTicks();
145 dtf=0.0f;
146
147 while(!quit){
148 sleep(0);
149
150 evs->HandleEvents();
151
152 visual->UpdateParticles();
153
154 //3x detail on player physics: more detailed
155 //observations by player than other physics
156 dtf/=3.0f;
157 player->Physics();
158 player->Physics();
159 player->Physics();
160 dtf*=3.0f;
161
162 if(GLOB_conf.twoplayer)
163 player2->Physics();
164
165 SpawnUfos();
166
167 UpdateUfos();
168
169 UpdateMines();
170
171 UpdateMissiles();
172
173 UpdatePowerUps();
174
175 sound->Update();
176
177 visual->Draw();
178
179 frames++;
180
181 #ifdef LINUX
182 gettimeofday(&tv,NULL);
183 dtf=(tv.tv_sec-oldtv.tv_sec)*1000.0f+(tv.tv_usec-oldtv.tv_usec)/1000.0f;
184 if(timescale!=1.0f)
185 dtf*=timescale;
186 oldtv=tv;
187 #else
188 dtf=float(SDL_GetTicks()-ticks)*timescale;
189 ticks=SDL_GetTicks();
190 #endif
191 if(GLOB_conf.actslow) SDL_Delay(28);
192 }
193
194 SDL_WM_GrabInput(SDL_GRAB_OFF);
195
196 player->Unspawn();
197
198 printf("Game::Happen: %d frames, %.2ffps, on average.\n",(int)frames,float(1000*frames)/float(SDL_GetTicks()-startticks));
199 printf("Game::Happen: score=%d\n",score);
200
201 delete poweruplist;
202
203 delete missilelist;
204
205 delete ufolist;
206
207 delete minelist;
208
209 delete player;
210 if(GLOB_conf.twoplayer)
211 delete player2;
212
213 delete sound;
214
215 delete evs;
216
217 delete visual;
218
219 delete arena;
220
221 return quit;
222 }
223
NewMissile(Missile * newmissile)224 void Game::NewMissile(Missile* newmissile)
225 {
226 newmissile->game=this;
227 missilelist->Add(newmissile);
228 }
229
NewPowerUp(PowerUp * newpowerup)230 void Game::NewPowerUp(PowerUp* newpowerup)
231 {
232 newpowerup->game=this;
233 poweruplist->Add(newpowerup);
234 }
235
NewUfo(Ufo * newufo)236 void Game::NewUfo(Ufo* newufo)
237 {
238 Ufo* created;
239 newufo->game=this;
240 created=ufolist->Add(newufo);
241 created->Live();
242 }
243
NewMine(Mine * newmine)244 void Game::NewMine(Mine* newmine)
245 {
246 Mine* created;
247 newmine->game=this;
248 created=minelist->Add(newmine);
249 created->Live();
250 }
251
SpawnUfos()252 void Game::SpawnUfos()
253 {
254 int failcount;
255 Vector place;
256 Vector dist;
257
258 spawnticker+=dtf;
259 if(spawnticker<spawndelay) return;
260
261 spawnticker=0;
262 if(spawndelay>1000.0f)
263 spawndelay*=0.95f;
264
265 if(ufolist->count>=20)
266 return;
267
268 Ufo newufo;
269 place.y=3.0f;
270 place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
271 place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
272 dist=place-player->s;
273 failcount=0;
274 //be careful that sniffradius fits in the arena!
275 while(failcount<50&&(dist.Mag2() < newufo.sniffradius*newufo.sniffradius || arena->Blocked(place,0) || !arena->Collision(place,player->s))){
276 failcount++;
277 if(verbose) printf("Game::SpawnUfos: place %f,%f,%f is unacceptable. "
278 "It is %f from the player, at %f,%f,%f\n",place.x,place.y,place.z,
279 dist.Mag(),player->s.x,player->s.y,player->s.z);
280
281 place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
282 place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
283 dist=place-player->s;
284 }
285
286 if(verbose)
287 printf("Game::SpawnUfos: place=%f,%f,%f\n",place.x,place.y,place.z);
288
289 newufo.s=place;
290 NewUfo(&newufo);
291 }
292
UpdateMissiles()293 void Game::UpdateMissiles()
294 {
295 LListItem<Missile> *item;
296 Particle newpart;
297
298
299 Vector temp,temp1,temp2,d;
300 d=10000.0f;
301
302 item=missilelist->head;
303 while(item){
304 if(item->data.collided){
305 item=missilelist->Del(item);
306 continue;
307 }
308
309 if(player->powerup==POWERUP_GUIDANCE && player->poweruptimer>0.0f){
310 Ufo* target=NULL;
311 LListItem<Ufo>* ufoitem;
312 target=NULL;
313 ufoitem=ufolist->head;
314 while(ufoitem){
315 temp=ufoitem->data.s-item->data.s;
316 if(temp.Mag2() < 50.0f*50.0f && temp.Mag2() < d.Mag2()){
317 target=&ufoitem->data;
318 d=temp;
319 }
320 ufoitem=ufoitem->next;
321 }
322
323 if(target){
324 d.Unitize();
325 item->data.a=d*0.001f;
326 //item->data.v*=pow(0.99f,dtf);
327 temp1=item->data.v;
328 temp1.Unitize();
329 temp2=item->data.a;
330 temp2.Unitize();
331 item->data.v*=temp1|temp2;
332 }
333 }
334
335
336 item->data.Physics();
337 /*newpart.s=item->data.s;
338 newpart.v=0.0f;
339 strcpy(newpart.texfile,"missiletrail.png");
340 newpart.rad=1;
341 newpart.blendmode=GL_ONE;
342 newpart.life=300;
343 visual->NewParticle(&newpart);*/
344
345 item=item->next;
346 }
347
348 }
349
UpdatePowerUps()350 void Game::UpdatePowerUps()
351 {
352 LListItem<PowerUp>* item;
353 item=poweruplist->head;
354 while(item){
355 item->data.Update();
356 if(item->data.done){
357 item=poweruplist->Del(item);
358 continue;
359 }
360 item=item->next;
361 }
362 }
363
UpdateUfos()364 void Game::UpdateUfos()
365 {
366 LListItem<Ufo>* ufoitem;
367 ufoitem=ufolist->head;
368 while(ufoitem){
369 if(!ufoitem->data.alive){
370 ufoitem=ufolist->Del(ufoitem);
371 continue;
372 }
373 ufoitem->data.Physics();
374 ufoitem->data.Pilot();
375 ufoitem=ufoitem->next;
376 }
377 }
378
UpdateMines()379 void Game::UpdateMines()
380 {
381 LListItem<Mine>* mineitem;
382 mineitem=minelist->head;
383 while(mineitem){
384 if(!mineitem->data.alive){
385 mineitem=minelist->Del(mineitem);
386 continue;
387 }
388 mineitem->data.Physics();
389 mineitem=mineitem->next;
390 }
391 }
392
393 //blatant repetitousness lies herein....
IncScore(int more)394 void Game::IncScore(int more)
395 {
396 PowerUp newpup;
397 Vector place,dist;
398 int failcount;
399
400 if((score+more)-(score+more)%40000>score-score%40000){
401 newpup.nature=POWERUP_LIFE;
402 place.y=newpup.s.y;
403 place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
404 place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
405 dist=place-player->s;
406 failcount=0;
407 while(failcount<50&&arena->Blocked(place,0)){
408 failcount++;
409 place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
410 place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
411 dist=place-player->s;
412 }
413 newpup.s=place;
414 NewPowerUp(&newpup);
415 }
416
417 if((score+more)-(score+more)%3000>score-score%3000){
418 int rannum=rand();
419 if(rannum<RAND_MAX/3)
420 newpup.nature=POWERUP_GUIDANCE;
421 else if(rannum<(RAND_MAX/3)*2)
422 newpup.nature=POWERUP_TRIPLESHOT;
423 else
424 newpup.nature=POWERUP_SUPERSHIELD;
425 place.y=newpup.s.y;
426 place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
427 place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
428 dist=place-player->s;
429 failcount=0;
430 while(failcount<50&&arena->Blocked(place,0)){
431 failcount++;
432 place.x=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
433 place.z=((float)rand()/(float)RAND_MAX)*arena->halfwidth*2-arena->halfwidth;
434 dist=place-player->s;
435 }
436 newpup.s=place;
437 NewPowerUp(&newpup);
438 }
439
440
441
442 score+=more;
443 }
444
ShowMenu()445 void Game::ShowMenu()
446 {
447 menufunc menureturn;
448
449 SDL_GrabMode grabcache=SDL_WM_GrabInput(SDL_GRAB_QUERY);
450 SDL_WM_GrabInput(SDL_GRAB_OFF);
451
452 float tempvolume=sound->GetVolume();
453
454 long start=SDL_GetTicks();
455 sound->SetPaused(1);
456
457 Menu* menu;
458 char* labels[]={"Continue","Volume:","Abort game","Exit Excido",NULL};
459 menufunc functions[]={MENU_CONTINUE,MENU_FLOATONE,MENU_QUIT,MENU_QUITFULL};
460 void* targets[]={NULL,(void*)&tempvolume,NULL,NULL};
461
462
463 menu=new Menu(labels,functions,targets,visual);
464 menu->solidbackground=0;
465 menureturn=menu->Happen();
466 delete menu;
467 if(menureturn==MENU_QUIT)
468 GameOver(1);
469 else if(menureturn==MENU_QUITFULL)
470 GameOver(2);
471
472
473 sound->SetVolume(tempvolume);
474
475 sound->SetPaused(0);
476
477 startticks+=SDL_GetTicks()-start;//for benefit of avg frame rate calculation in Game::Happen
478 ticks=SDL_GetTicks();//look at where Events::HandleEvents is called in Happen, doing this is sane-ish
479 dtf=0;//ie skip a simulation step after returning - it's that or a real big one :-)*/
480
481 SDL_WM_GrabInput(grabcache);
482 }
483
Resize(int w,int h)484 void Game::Resize(int w,int h)
485 {
486 GLOB_conf.screenx=w;
487 GLOB_conf.screeny=h;
488 screen=SDL_SetVideoMode(GLOB_conf.screenx,GLOB_conf.screeny,GLOB_conf.bpp,SDL_OPENGLBLIT | SDL_RESIZABLE);
489 visual->ResetCams();
490 evs->targetcam=visual->GetMainCam();
491 sound->SetEar(&visual->GetMainCam()->s,&visual->GetMainCam()->v,
492 &visual->GetMainCam()->face,&visual->GetMainCam()->up);
493 }
494
495
GameOver(int quitstate)496 void Game::GameOver(int quitstate)
497 {
498 char name[64]="Player";
499
500 if(GLOB_scores.IsHigh(score)){
501 Menu* menu;
502 char* labels[]= { "Game over! High Score!",
503 "Your Name:",
504 "[Enter your name and press return]",NULL
505 };
506 menufunc functions[]={MENU_NOSELECT,MENU_STRING,MENU_NOSELECT};
507 void* targets[]={NULL,name,NULL};
508
509 menu=new Menu(labels,functions,targets,visual);
510 menu->solidbackground=0;
511 menu->Happen();
512 delete menu;
513
514 GLOB_scores.NewScore(score,name);
515 visual->LoadTexture("menuback.png");
516 GLOB_scores.Display(visual);
517 visual->UnLoadTexture("menuback.png");
518 }
519 else if(quitstate!=2){
520 Menu* menu;
521 char* labels[]={"Game over!",NULL};
522 menufunc functions[]={MENU_CONTINUE};
523 void* targets[]={NULL};
524
525 menu=new Menu(labels,functions,targets,visual);
526 menu->solidbackground=0;
527 menu->Happen();
528 delete menu;
529 }
530
531 quit=quitstate;
532 }
533