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 
11 #include "Ufo.h"
12 #include "Game.h"
13 #include "SoundCore.h"
14 #include "Player.h"
15 #include "Particle.h"
16 #include "Visual.h"
17 #include "LList.h"
18 #include "Missile.h"
19 
20 
21 #ifndef M_2PI
22 #define M_2PI 6.28318
23 #endif
24 
25 #ifndef M_PI
26 #define M_PI 3.14159
27 #endif
28 
Ufo()29 Ufo::Ufo()
30 {
31 	thrust=0.0f;
32 	bear=0.0f;
33 	bearv=0.0f;
34 	beara=0.0f;
35 	s.x=s.z=0;
36 	s.y=3.0;
37 	v=0.0f;
38 	a=0.0f;
39 	alive=0;
40 	soundid=-1;
41 	collideradius=5.0f;
42 	collideradius2=25.0f;
43 	sniffradius=250.0f;
44 	spawneffect=0.0f;
45 	hp=1.0f;
46 	pilotstate=UFO_POTTER;
47 	thinkwait=0.0f;
48 }
49 
50 
Live()51 void Ufo::Live()
52 {
53 	TriggerShield();
54 	alive=1;
55 	soundid = game->sound->Play("ufo.wav",-1,&s,&v);
56 }
57 
~Ufo()58 Ufo::~Ufo()
59 {
60 	if(alive){
61 		alive=0;
62 		game->sound->StopEffect(soundid);
63 	}
64 }
65 
TriggerShield()66 void Ufo::TriggerShield()
67 {
68 	spawneffect=maxspawneffect=400.0f;
69 }
70 
Physics()71 void Ufo::Physics()
72 {
73 	float afactor;
74 	float maxv=1.0f;
75 	float targetbear;
76 
77 	if(!alive) return;
78 
79 	if(s.y!=3.0f||a.y!=0.0f||v.y!=0.0f){
80 		printf("Ufo::Physics: My y is not static!  My a.y=%f,v.y=%f,s.y=%f\n",a.y,v.y,s.y);
81 	}
82 
83 	if(spawneffect){
84 		spawneffect-=game->dtf;
85 		if(spawneffect<0.0f)
86 			spawneffect=0.0f;
87 	}
88 
89 	//bear isn't really used properly:
90 	if(fabs(v.x)>0.001f||fabs(v.z)>0.001f){
91 		targetbear=atan(a.x/a.z);
92 		if(a.z<0.0f)
93 			targetbear+=M_PI;
94 	}
95 	else
96 		targetbear=0.0f;
97 
98 	while(bear<targetbear-M_PI)
99 					bear+=M_PI*2;
100 	while(bear>targetbear+M_PI)
101 					bear-=M_PI*2;
102 
103 	bearv=(targetbear-bear)*0.01f;
104 
105 	bearv+=beara*game->dtf;
106 	bearv*=pow(0.999f,game->dtf);
107 	bear+=bearv*game->dtf;
108 	if(bear>M_2PI)
109 		bear-=M_2PI;
110 
111 	/*a=0.0f;
112 	a.x=sin(bear);
113 	a.z=cos(bear);
114 	a*=0.001f;
115 	printf("bear=%f,a.x=%f,a.z=%f\n",bear,a.x,a.z);*/
116 
117 	afactor=1.0-(v.Mag()/maxv);
118 
119 	v+=(game->dtf*afactor)*a;
120 	v*=pow(0.99f,game->dtf);
121 	Collide();
122 	s+=v*game->dtf;
123 
124 
125 	//Collision detection may allow ufo to escape in concave corners
126 	//Cope with this by bringing it back.  Player should never see this
127 	//happen up close and personal.
128 	if(fabs(s.x)>game->arena->halfwidth){
129 		if(game->verbose) printf("Ufo::Physics: out of arena, s.x=%f\n",s.x);
130 		s.x*=0.98f;
131 	}
132 	else if(fabs(s.z)>game->arena->halfwidth){
133 		if(game->verbose) printf("Ufo::Physics: out of arena, s.z=%f\n",s.z);
134 		s.z*=0.98f;
135 	}
136 
137 	//Smoke for damaged ufos
138 	if(hp<0.5f){
139 		Particle newpart;
140 		newpart.s=s;
141 		newpart.v=v*0.5f;
142 		newpart.a.y=0.0001f;
143 		newpart.diffuse=0.000001f;
144 		newpart.res=0.99f;
145 		newpart.life=1000.0f;
146 		newpart.rad=3.0f;
147 		newpart.blendmode=GL_ONE_MINUS_SRC_ALPHA;
148 		strcpy(newpart.texfile,"smoke1.png");
149 		game->visual->NewParticle(&newpart);
150 	}
151 
152 }
153 
154 
Collide()155 void Ufo::Collide()
156 {
157 	Vector dist;
158 	dist=s-game->player->s;
159 	if(dist.Mag2()<collideradius2+game->player->collideradius2+game->player->collideradius*collideradius && game->player->alive){
160 		Die();
161 		game->player->Hurt(0.21f);
162 		return;
163 	}
164 
165 	if(game->arena->Collision(s,s+v*game->dtf,collideradius)){
166 		v=game->arena->GetLastQuad()->SlideVector(v);
167 	}
168 
169 	Vector h,d;
170 	LListItem<Missile>* item;
171 	item=game->missilelist->head;
172 	while(item){
173 
174 		h=PerpLinePoint(item->data.s,item->data.s+item->data.v*game->dtf,s);
175 		d=h-s;
176 		if(d.Mag2()<collideradius2 && PointOnLine(item->data.s,item->data.s+item->data.v*game->dtf,h)){
177 			Hurt(1.1f);
178 			return;
179 		}
180 
181 		d=item->data.s-s;
182 		if(d.Mag2()<collideradius2 && PointOnLine(item->data.s,item->data.s+item->data.v*game->dtf,h)){
183 			Hurt(1.1f);
184 			return;
185 		}
186 
187 		d=item->data.s+item->data.v*game->dtf-s;
188 		if(d.Mag2()<collideradius2 && PointOnLine(item->data.s,item->data.s+item->data.v*game->dtf,h)){
189 			Hurt(1.1f);
190 			return;
191 		}
192 
193 		item=item->next;
194 	}
195 }
196 
197 
Pilot()198 void Ufo::Pilot()
199 {
200 	Vector temp;
201 	Vector d;
202 
203 	temp=s-game->player->s;
204 
205 	thinkwait-=game->dtf;
206 	if(thinkwait<=0){
207 		if(game->player->alive && temp.Mag2() < sniffradius*sniffradius
208 		&& !game->arena->Collision(s,game->player->s)){
209 			if(pilotstate!=UFO_SEEKPLAYER)
210 				game->sound->Play("spotted.wav",0,s,v);
211 			pilotstate=UFO_SEEKPLAYER;
212 		}
213 		else if(game->player->alive){
214 			pilotstate=UFO_SEARCH;
215 		}
216 		else
217 			pilotstate=UFO_POTTER;
218 		thinkwait+=500.0f;
219 	}
220 
221 	if(pilotstate==UFO_SEEKPLAYER){
222 		temp.y=0.0f;
223 		temp.Unitize();
224 		a=-temp;
225 	}
226 	else if(pilotstate==UFO_POTTER){
227 		temp.Randomize();
228 		a+=temp;
229 		a.Unitize();
230 		a*=0.01f;
231 	}
232 	else if(pilotstate==UFO_SEARCH){
233 		temp.Randomize();
234 		a+=temp;
235 		a.Unitize();
236 		a*=0.01f;
237 	}
238 
239 	Ufo* nearest=Nearest();
240 
241 	d=nearest->s-s;
242 	temp=d;
243 	temp.Unitize();
244 	if(d.Mag2()!=0.0f)
245 		temp*=(collideradius*2)/d.Mag(); //ie ufos avoiding each other has equal precedence to homing
246 															//on player where temp.Mag=collideradius*2, ie the ufos are collideradius*2 apart.
247 	a+=-temp;
248 
249 	a+=(nearest->v-v)*10.0f; //a more orderly formation
250 
251 /*	if(nearest->pilotstate==UFO_SEEKPLAYER)
252 	{
253 		temp=d;
254 		temp.Unitize();
255 		a=-temp;
256 	}*/
257 
258 	//clear the respawn if the player's waiting to spawn
259 	if(!game->player->alive){
260 		d=s;//IE distance from center, assuming spawn at ccenter
261 		if(d.Mag2()<sniffradius*sniffradius*1.2f){
262 			temp=s;
263 			temp.Unitize();
264 			temp*=(sniffradius)/s.Mag();
265 			a+=temp;
266 		}
267 	}
268 
269 	a.Unitize();
270 	a.y=0.0;
271 	a*=0.001+0.0005*(1.0f-game->spawndelay/5000.0f);
272 
273 #ifdef RELEASE
274 	//FIXME: Do this to cover up an unfound bug
275 	s.y=3.0f;
276 #endif
277 }
278 
279 //Find the closest Ufo in game->ufolist to this
280 //if game->ufolist->count==1 then return this
Nearest()281 Ufo* Ufo::Nearest()
282 {
283 	Ufo* retval;
284 	LListItem<Ufo> *ufoitem;
285 	Vector d;
286 	Vector temp;
287 
288 	ufoitem=game->ufolist->head;
289 	temp=ufoitem->data.s-s;
290 	d=temp;
291 	retval=&ufoitem->data;
292 	ufoitem=ufoitem->next;
293 
294 	while(ufoitem){
295 		temp=ufoitem->data.s-s;
296 		if(temp.Mag2()<d.Mag2() && &ufoitem->data!=this){
297 			d=temp;
298 			retval=&ufoitem->data;
299 		}
300 		ufoitem=ufoitem->next;
301 	}
302 
303 	return retval;
304 }
305 
Die()306 void Ufo::Die()
307 {
308 	if(!alive){//general paranoia checking
309 		printf("Ufo::Die: Warning, (!alive) is true!\n");
310 		return;
311 	}
312 	game->IncScore(200);
313 	Explode();
314 	alive=0;
315 	game->sound->StopEffect(soundid);
316 }
317 
Explode()318 void Ufo::Explode()
319 {
320 	Particle newpart;
321 	newpart.s=s;
322 	newpart.rad=collideradius;
323 	newpart.diffuse=0.000003;
324 	newpart.blendmode=GL_ONE;
325 	strcpy(newpart.texfile,"explosion1.png");
326 	newpart.life=200;
327 	for(int i=0;i<3;i++)
328 		game->visual->NewParticle(&newpart);
329 	strcpy(newpart.texfile,"smoke1.png");
330 	newpart.life=1000;
331 	newpart.rad=collideradius*1.2;
332 	newpart.diffuse=0.0000005;
333 	newpart.res=0.999f;
334 	newpart.a=game->g*0.1f;
335 	newpart.blendmode=GL_ONE_MINUS_SRC_ALPHA;
336 	for(int i=0;i<8;i++)
337 		game->visual->NewParticle(&newpart);
338 	strcpy(newpart.texfile,"explosion1.png");
339 	newpart.life=1500.0f;
340 	newpart.bounce=PARTICLE_BOUNCE_ONE;
341 	newpart.drawmode=PARTICLE_DRAW_SIMPLE;
342 	newpart.rad=0.5;
343 	newpart.diffuse=0.000000;
344 	newpart.res=1.0f;
345 	newpart.a=game->g;
346 	newpart.collide=1;
347 	for(int i=0;i<8;i++){
348 		newpart.v.Randomize();
349 		newpart.v.y+=1.0f;
350 		newpart.v*=0.05f;
351 		game->visual->NewParticle(&newpart);
352 	}
353 	newpart.drawmode=PARTICLE_DRAW_MOTION;
354 	newpart.rad=0.25;
355 	newpart.life=400.0f;
356 	newpart.collide=1;
357 	for(int i=0;i<8;i++){
358 		newpart.v.Randomize();
359 		newpart.v*=1.0f;
360 		game->visual->NewParticle(&newpart);
361 	}
362 
363 
364 	game->sound->Play("ufodie.wav",0,s,v);
365 }
366 
Hurt(float pain)367 int Ufo::Hurt(float pain)
368 {
369 	if(hp<=0) return 1;
370 
371 	hp-=pain;
372 	if(hp<=0){
373 		Die();
374 		return 1;
375 	}
376 	else
377 		return 0;
378 }
379