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