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 <stdlib.h>
8 #include <math.h>
9 #include <string.h>
10 #include <SDL_opengl.h>
11 
12 #include "Player.h"
13 #include "Missile.h"
14 #include "Particle.h"
15 #include "Visual.h"
16 #include "SoundCore.h"
17 #include "Menu.h"
18 
19 #ifndef M_2PI
20 #define M_2PI 6.28318
21 #endif
22 
23 #ifndef M_PI
24 #define M_PI 3.14159
25 #endif
26 
Player(Game * newgame)27 Player::Player(Game* newgame)
28 {
29 	game=newgame;
30 	alive=0;
31 	collideradius=7;
32 	collideradius2=25;
33 	lives=3;
34 	highlightlives=0;
35 	cannonperiod=50.0f;
36 	//most of the init happens in Respawn
37 }
38 
Respawn()39 void Player::Respawn()
40 {
41 
42 	if(!lives)
43 		return;
44 	if(alive)
45 		return;
46 	if(!SafeToRespawn())
47 		return;
48 
49 	lives--;
50 
51 	s.x=s.z=0;
52 	s.y=50.1f;
53 	v=0;
54 	a=0;
55 	thrust=0.0f;
56 	bear=0.0f;
57 	bearv=0.0f;
58 	beara=0.0f;
59 	strafea=0.0f;
60 	soundid = game->sound->Play("engine.wav",-1,&s,&v);
61 	alive=1;
62 	hp=1.0f;
63 	shieldeffect=0;
64 	bouncecycle=((float)rand()/(float)RAND_MAX)*M_2PI;
65 	powerup=POWERUP_NONE;
66 	poweruptimer=0.0f;
67 	maxpoweruptimer=0.0f;
68 	cannonfiring=0;
69 	radarrange=200.0f;
70 
71 	TriggerShield();
72 }
73 
74 
75 
76 
Physics()77 void Player::Physics()
78 {
79 	if(!alive) return;
80 
81 	//hacky way of dealing with fundamental flaws in keyboard event handling:
82 	//basically this lets the user fix 'stuck' keys by pressing the opposite key
83 	if(thrust<-1.0f)
84 		thrust=-1.0f;
85 	if(thrust>1.0f)
86 		thrust=1.0f;
87 	if(strafea<-1.0f)
88 		strafea=-1.0f;
89 	if(strafea>1.0f)
90 		strafea=1.0f;
91 
92 	float afactor;
93 	float maxv=0.18f;
94 	Particle newpart;
95 
96 	if(shieldeffect){
97 		shieldeffect-=game->dtf;
98 		if(shieldeffect<0.0f)
99 			shieldeffect=0.0f;
100 	}
101 
102 	if(highlightlives){
103 		highlightlives-=game->dtf;
104 		if(highlightlives<0.0f)
105 			highlightlives=0.0f;
106 	}
107 
108 	if(poweruptimer){
109 		poweruptimer-=game->dtf;
110 		if(poweruptimer<0.0f)
111 			poweruptimer=0.0f;
112 	}
113 
114 	bouncecycle+=game->dtf/100.0f;
115 	//printf("%f\n",bouncecycle);
116 	if(bouncecycle>M_2PI)
117 		bouncecycle-=M_2PI;
118 	if(cannonfiring){
119 		if(cannonwait) cannonwait -= game->dtf;
120 		while(cannonwait<0){
121 			cannonwait+= cannonperiod;
122 			ShootCannon();
123 		}
124 	}
125 
126 	s.y=3+0.3f*sin((float)bouncecycle);
127 	a.y=0.0f;
128 	//thrust should be (0|-1.0f|1.0f)
129 	a.z=cos(bear)*thrust*0.005f;
130 	a.x=sin(bear)*thrust*0.005f;
131 
132 	a.z+=cos(bear+M_PI/2)*strafea*0.005;
133 	a.x+=sin(bear+M_PI/2)*strafea*0.005;
134 
135 	a.Unitize();
136 	a*=0.005f;
137 
138 	bearv+=beara*game->dtf;
139 	bearv*=pow(0.99f,game->dtf);
140 	bear+=bearv*game->dtf;
141 	if(bear>M_2PI)
142 		bear-=M_2PI;
143 
144 	afactor=1.0-(v.Mag()/maxv);
145 
146 	v+=(game->dtf*afactor)*a;
147 	v*=pow(0.99f,game->dtf);
148 	Collide();
149 	//Collide();
150 	//no, it's not a typo.  To understand why we call that twice, imagine
151 	//what happens when the player is in a 90 degree corner of two quads
152 	// - we need to collide with BOTH: deflect the vector off one, and then
153 	//deflect the resulting vector off the other
154 	s+=v*game->dtf;
155 
156 	//Smoke when damaged
157 	if(hp<0.25f){
158 		Particle newpart;
159 		newpart.s=s;
160 		newpart.s.x-=sin(bear)*2.0f;
161 		newpart.s.z-=cos(bear)*2.0f;
162 		newpart.v=v*0.5f;
163 		newpart.a.y=0.0001f;
164 		newpart.diffuse=0.000001f;
165 		newpart.res=0.99f;
166 		newpart.life=750.0f;
167 		newpart.rad=3.0f;
168 		newpart.blendmode=GL_ONE_MINUS_SRC_ALPHA;
169 		strcpy(newpart.texfile,"smoke1.png");
170 		game->visual->NewParticle(&newpart);
171 	}
172 }
173 
SafeToRespawn()174 int Player::SafeToRespawn()
175 {
176 	LListItem<Ufo>* item;
177 
178 	item=game->ufolist->head;
179 	while(item){
180 		if(item->data.s.Mag2()<item->data.sniffradius*item->data.sniffradius)
181 			{
182 				/*s.x=s.z=0.0f;
183 				bear=-asin(item->data.s.x/item->data.s.Mag())+M_PI;*/
184 				return 0;
185 			}
186 		item=item->next;
187 	}
188 
189 	return 1;
190 }
191 
~Player()192 Player::~Player()
193 {
194 	if(alive)
195 		Unspawn();
196 }
197 
Unspawn()198 void Player::Unspawn()
199 {
200 	if(!alive) return;
201 	game->sound->StopEffect(soundid);
202 	if(cannonfiring)
203 		StopCannon();
204 	alive=0;
205 }
206 
FireMissile()207 void Player::FireMissile()
208 {
209 	if(!alive) return;
210 
211 	game->sound->Play("fire.wav",0,s,v);
212 
213 	Missile newmis;
214 	newmis.s=s;
215 	newmis.v=v;
216 	newmis.a.y=-0.0001;
217 	newmis.v.y+=0.015f;
218 	newmis.v.z+=0.5f*cos(bear);
219 	newmis.v.x+=0.5f*sin(bear);
220 	newmis.res=0.999f;
221 	newmis.owner=this;
222 	game->NewMissile(&newmis);
223 
224 	if(powerup==POWERUP_TRIPLESHOT && poweruptimer>0){
225 		newmis.v=v;
226 		newmis.v.y+=0.01f;
227 		newmis.v.z+=0.5f*cos(bear+M_PI/20.0f);
228 		newmis.v.x+=0.5f*sin(bear+M_PI/20.0f);
229 
230 		game->NewMissile(&newmis);
231 
232 		newmis.v=v;
233 		newmis.v.y+=0.01f;
234 		newmis.v.z+=0.5f*cos(bear-M_PI/20.0f);
235 		newmis.v.x+=0.5f*sin(bear-M_PI/20.0f);
236 		game->NewMissile(&newmis);
237 	}
238 
239 	Particle newpart;
240 	for(int i=0;i<5;i++){
241 		newpart.s=newmis.s;
242 		newpart.v=v+newmis.v*0.5f;
243 		newpart.a.y=0.0001f;
244 		newpart.res=0.99f;
245 		newpart.rad=2.5f;
246 		newpart.diffuse=0.000003f;
247 		newpart.life=400;
248 		strcpy(newpart.texfile,"smoke1.png");
249 		game->visual->NewParticle(&newpart);
250 	}
251 }
252 
ShootCannon()253 void Player::ShootCannon()
254 {
255  if(!alive) return;
256  Vector shoot,hit,path;
257  Vector tempv;
258 
259  shoot.x=sin(bear)*1000.0f+100.0f*float(rand())/float(RAND_MAX)-50.0f;
260  shoot.z=cos(bear)*1000.0f+100.0f*float(rand())/float(RAND_MAX)-50.0f;
261  shoot.y=0.0f+50.0f*float(rand())/float(RAND_MAX)-25.0f;
262 
263  tracersource=s;
264  tracersource.x+=sin(bear)*collideradius;
265  tracersource.z+=cos(bear)*collideradius;
266 
267  hit=game->arena->CollisionNearest(tracersource,tracersource+shoot);
268 
269  path=hit-tracersource;
270 
271  tempv=path;
272  tempv.Unitize();
273  v-=tempv*0.01f; //recoil
274 
275  Vector hittemp,pathtemp;
276  Vector testpoint,dist;
277 
278  LListItem<Ufo>* ufoitem;
279  Ufo* ufohit=NULL;
280  ufoitem=game->ufolist->head;
281  while(ufoitem){
282   if(!ufoitem->data.alive){
283    ufoitem=ufoitem->next;
284    continue;
285   }
286 
287   testpoint=PerpLinePoint(tracersource,tracersource+path,ufoitem->data.s);
288 
289   dist=testpoint-ufoitem->data.s;
290 
291   if(dist.Mag2()<ufoitem->data.collideradius2 && PointOnLine(tracersource,tracersource+path,testpoint)){
292    tempv=path;
293    tempv.Unitize();
294 
295 	 hittemp=testpoint-tempv*ufoitem->data.collideradius*
296 					cos(dist.Mag()/ufoitem->data.collideradius);
297    pathtemp=hittemp-tracersource;
298 
299    if(pathtemp.Mag2()<path.Mag2()){
300 	  hit=hittemp;
301 	  path=pathtemp;
302 	  ufohit=&ufoitem->data;
303 	 }
304   }
305   ufoitem=ufoitem->next;
306  }
307 
308  LListItem<Mine>* mineitem;
309  Mine* minehit=NULL;
310  mineitem=game->minelist->head;
311  while(mineitem){
312   if(!mineitem->data.alive){
313    mineitem=mineitem->next;
314    continue;
315   }
316 
317   testpoint=PerpLinePoint(tracersource,tracersource+path,mineitem->data.s);
318 
319   dist=testpoint-mineitem->data.s;
320 
321   if(dist.Mag2()<mineitem->data.collideradius2 && PointOnLine(tracersource,tracersource+path,testpoint)){
322    tempv=path;
323    tempv.Unitize();
324 
325 	 hittemp=testpoint-tempv*mineitem->data.collideradius*
326 					cos(dist.Mag()/mineitem->data.collideradius);
327    pathtemp=hittemp-tracersource;
328 
329    if(pathtemp.Mag2()<path.Mag2()){
330 		 hit=hittemp;
331 		 path=pathtemp;
332 		 minehit=&mineitem->data;
333 		 ufohit=NULL;
334 	 }
335   }
336   mineitem=mineitem->next;
337  }
338 
339  tempv=path;
340  tempv.y=0.0f;
341  tempv.Unitize();
342 
343  if(ufohit){
344 	 ufohit->v+=tempv*0.15f;
345 	 ufohit->Hurt(0.15f);
346  }
347 
348  tracer=path;
349 
350  Particle newpart;
351  if(ufohit){
352   for(int i=0;i<5;i++){
353    newpart.s=hit;
354    newpart.v=0.0f;
355    newpart.v.x+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
356    newpart.v.y+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
357    newpart.v.z+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
358    newpart.a=game->g;
359    newpart.rad=1;
360    newpart.life=200.0f;
361    newpart.blendmode=GL_ONE;
362    strcpy(newpart.texfile,"explosion1.png");
363 		 game->visual->NewParticle(&newpart);
364   }
365  }
366  else{
367   for(int i=0;i<5;i++){
368    newpart.s=hit;
369    newpart.v=game->arena->GetLastQuad()->n*0.02;
370    newpart.v.x+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
371    newpart.v.y+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
372    newpart.v.z+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
373    newpart.a=game->g;
374    newpart.rad=0.25;
375    newpart.life=200.0f;
376    newpart.blendmode=GL_ONE;
377    strcpy(newpart.texfile,"explosion1.png");
378    game->visual->NewParticle(&newpart);
379   }
380 
381   for(int i=0;i<5;i++){
382    newpart.s=hit;
383    newpart.v=game->arena->GetLastQuad()->n*0.02;
384    newpart.v.x+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
385    newpart.v.y+=0.02*((float)rand()/(float)RAND_MAX)-0.01+0.01f;
386    newpart.v.z+=0.02*((float)rand()/(float)RAND_MAX)-0.01;
387    newpart.a=game->g*0.1f;
388    newpart.rad=2;
389    newpart.diffuse=0.000003;
390    newpart.res=0.99f;
391    newpart.life=500.0f;
392    newpart.blendmode=GL_ONE_MINUS_SRC_ALPHA;
393    strcpy(newpart.texfile,"smoke1.png");
394    game->visual->NewParticle(&newpart);
395   }
396  }
397 
398 
399 
400 	newpart.s=tracersource;
401 	newpart.v=v;
402 	newpart.a.y=0.0001f;
403 	newpart.res=0.99f;
404 	newpart.rad=1.5f;
405 	newpart.diffuse=0.000003f;
406 	newpart.life=400;
407 	strcpy(newpart.texfile,"smoke1.png");
408 	game->visual->NewParticle(&newpart);
409 
410 	//teh riskeh
411 	game->sound->Play("cannon.wav",0,&s,&v);
412 }
413 
TriggerShield()414 void Player::TriggerShield()
415 {
416 	shieldeffect=maxshieldeffect=800.0f;
417 	game->sound->Play("shield.wav",0,s,v);
418 }
419 
Hurt(float pain)420 void Player::Hurt(float pain)
421 {
422 	if(hp<=0) return;
423 	if(powerup==POWERUP_SUPERSHIELD&&poweruptimer>0.0f) return;
424 
425 	hp-=pain;
426 
427 	if(hp<=0){
428 		Explode();
429 		Unspawn();
430 		if(lives==0){
431 			game->GameOver(1);
432 		}
433 	}
434 	else
435 		TriggerShield();
436 }
437 
GivePowerUp(poweruptype newpup)438 void Player::GivePowerUp(poweruptype newpup)
439 {
440 	string pupdesc;
441 	if(newpup==POWERUP_LIFE){
442 		lives++;
443 		highlightlives=1000.0f;
444 		pupdesc="Extra Life!";
445 	}
446 	else if(newpup==POWERUP_TRIPLESHOT){
447 		powerup=newpup;
448 		maxpoweruptimer=poweruptimer=15000.0f;
449 		pupdesc="TripleShot!";
450 	}
451 	else if(newpup==POWERUP_GUIDANCE){
452 		powerup=newpup;
453 		maxpoweruptimer=poweruptimer=15000.0f;
454 		pupdesc="Guidance!";
455 	}
456 	else if(newpup==POWERUP_SUPERSHIELD){
457 		powerup=newpup;
458 		maxpoweruptimer=poweruptimer=15000.0f;
459 		pupdesc="Super Shield!";
460 	}
461 	else{
462 		printf("Player::GivePowerUp: unhandled powerup!  newpup=%d\n",newpup);
463 		return;
464 	}
465 
466   game->visual->GetMainCam()->SetMessage(pupdesc);
467 	game->sound->Play("gotthelife.wav");
468 }
469 
Collide()470 void Player::Collide()
471 {
472 	Vector path;
473 	float part;
474 
475 	if(game->arena->Collision(s,s+v*game->dtf,collideradius)){
476 		path=game->arena->GetLastQuad()->PlaneIntersectionPoint(s,s+v*game->dtf)-s;
477 		part=path.Mag()-collideradius;
478 	    	path.Unitize();
479 		s+=path*part;
480 		//s=game->arena->GetLastQuad()->PlaneIntersectionPoint(s,s+v*game->dtf);
481 		//printf("Collided: v=(%f,%f,%f),",v.x,v.y,v.z );
482 		/*printf("Collided with %f,%f,%f\n",game->arena->GetLastQuad()->n.x,
483 																		game->arena->GetLastQuad()->n.y,
484 																		game->arena->GetLastQuad()->n.z);*/
485 
486 		v=game->arena->GetLastQuad()->SlideVector(v);
487 		//v=game->arena->GetLastQuad()->BounceVector(v);
488 		//printf("v_new=(%f,%f,%f)\n",v.x,v.y,v.z );
489 		collideflag=1;
490 	}
491 	else{
492 		collideflag=0;
493 	}
494 }
495 
Explode()496 void Player::Explode()
497 {
498 	Particle newpart;
499 	newpart.s=s;
500 	newpart.rad=collideradius;
501 	newpart.diffuse=0.000003;
502 	newpart.res=0.999f;
503 	newpart.blendmode=GL_ONE;
504 	strcpy(newpart.texfile,"explosion1.png");
505 	newpart.life=400;
506 	for(int i=0;i<6;i++)
507 		game->visual->NewParticle(&newpart);
508 
509 
510 
511 	strcpy(newpart.texfile,"smoke1.png");
512 	//newpart.v=v;
513 	newpart.life=2000;
514 	newpart.rad=collideradius*1.2;
515 	newpart.diffuse=0.0000005;
516 	newpart.res=0.99f;
517 	newpart.a=game->g*0.1f;
518 	newpart.blendmode=GL_ONE_MINUS_SRC_ALPHA;
519 	for(int i=0;i<8;i++){
520 		newpart.rad=collideradius*(1.0f+1.0f*((float)rand()/(float)RAND_MAX));
521 		game->visual->NewParticle(&newpart);
522 	}
523 
524 	strcpy(newpart.texfile,"explosion1.png");
525 	newpart.blendmode=GL_ONE;
526 	newpart.rad=0.25f;
527 	newpart.life=2000;
528 	newpart.diffuse=0.0f;
529 	newpart.res=1.0f;
530 	newpart.a=game->g;
531 	newpart.bounce=PARTICLE_BOUNCE_ONE;
532 	newpart.collide=1;
533 	for(int i=0;i<30;i++){
534 		newpart.v.Randomize();
535 		newpart.v+=v;
536 		newpart.v.Unitize();
537 		newpart.v*=0.1f;
538 		game->visual->NewParticle(&newpart);
539 	}
540 
541 	game->sound->Play("playerdie.wav",0,s,v);
542 }
543 
StartCannon()544 void Player::StartCannon()
545 {
546 	if(cannonfiring) return;
547 	cannonfiring=1;
548 	cannonwait=cannonperiod;
549 	ShootCannon();
550 }
551 
StopCannon()552 void Player::StopCannon()
553 {
554 	if(!cannonfiring) return;
555 	cannonfiring=0;
556 }
557