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