1 /***************************************************************************
2  *   Copyright (C) 2004 by Murray Evans                                    *
3  *   m.evans@rdg.ac.uk                                                     *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 #include "Ship.h"
21 #include "Galaxy.h"
22 #include "FreeBooter.h"
23 #include "structs.h"
24 #include "SimpleParticle.h"
25 #include "GunParticle.h"
26 
CShip(void)27 CShip::CShip(void)
28 {
29 	extern CFreebooter FB;
30 	CGalaxy *gal = (CGalaxy*)FB.scene;
31 	cargoQuants.SetSize(gal->cargoTypes.Size());
32 	for(int c_i = 0; c_i < cargoQuants.Size(); c_i++)
33 		cargoQuants[c_i] = 0;
34 	cargoLoad = 0;
35 	cargoVol = 0;
36 	shieldDamage_f = 0;
37 	damage_i = 0;
38 	// no shields to start with
39 	mk1Shield_i = 0;
40 	mk2Shield_i = 0;
41 	mk3Shield_i = 0;
42 	inContact_b = false;
43 	dead_b = false;
44 	faction_i = 0;
45 	kills_i = 0;
46 	SetVelocity(0,0,0);
47 	SetRotation(0,0,0);
48 	aiMode_i = AIMD_ROAM;
49 	aiSubMode_i = 0;
50 }
51 
~CShip(void)52 CShip::~CShip(void)
53 {
54 }
55 
56 
57 // control functions
RotateCW()58 void CShip::RotateCW()
59 {
60 	extern CFreebooter FB;
61 	CGalaxy *gal;
62 	gal = (CGalaxy*) FB.scene;
63 	float turnSpd = gal->shipTypes[shipType_i]->rotationSpeed_f;
64 	float zRot = this->ZRot();
65 	SetRotation( 0, 0, zRot - turnSpd);
66 }
67 
RotateCCW()68 void CShip::RotateCCW()
69 {
70 	extern CFreebooter FB;
71 	CGalaxy *gal;
72 	gal = (CGalaxy*) FB.scene;
73 	float turnSpd = -gal->shipTypes[shipType_i]->rotationSpeed_f;
74 	float zRot = this->ZRot();
75 	SetRotation(0,0, zRot - turnSpd);
76 }
77 
FwdThrust()78 void CShip::FwdThrust()
79 {
80 	extern CFreebooter FB;
81 	CGalaxy *gal;
82 	gal = (CGalaxy*) FB.scene;
83 	CVec4f acc_v4f;	// acceleration vector
84 	float accMag = gal->shipTypes[shipType_i]->fwdThrustPow_f;
85 	acc_v4f.SetXYZW(0, accMag, 0, 0);
86 
87 	// transform to world space
88 	CMatrix4f tfm;
89 	tfm.SetIdentity();
90 	glMatrixMode(GL_MODELVIEW);
91 	glPushMatrix();
92 		glLoadIdentity();
93 //		glTranslatef(-pos_v4f.X(), -pos_v4f.Y(), -pos_v4f.Z());
94 		glRotatef(rot_v3f.X(), 1, 0, 0);
95 		glRotatef(rot_v3f.Y(), 0, 1, 0);
96 		glRotatef(rot_v3f.Z(), 0, 0, 1);
97 		glGetFloatv(GL_MODELVIEW_MATRIX, tfm.m_af);
98 	glPopMatrix();
99 	tfm.Transpose();
100 	acc_v4f = acc_v4f.Transform(tfm);
101 	if(inContact_b == false) vel_v4f = vel_v4f + acc_v4f;
102 
103 	// add thruster particles
104 	CVec4f newvel, randVec;
105 	newvel.SetXYZW(0,0,0,0);
106 	float xoff, yoff;
107 	xoff = ((float)(rand()%120))/100;
108 	yoff = ((float)(rand()%120))/100;
109 	randVec.SetXYZW(acc_v4f.X() * xoff, acc_v4f.Y() * yoff, acc_v4f.Z(), 0);
110 	newvel = vel_v4f - (acc_v4f + randVec) * 5;
111 	float red, green, blue;
112 	red = 0.5f;
113 	red   += ((float)(rand()%20))/100;
114 	green = 0.4f;
115 	green += ((float)(rand()%20))/100;
116 	blue  = 0;
117 
118 	for(int thrc_i = 0; thrc_i < gal->shipTypes[this->shipType_i]->thrPositions.Size(); thrc_i++)
119 	{
120 		CVec4f thrpos;
121 		thrpos = gal->shipTypes[this->shipType_i]->thrPositions[thrc_i].Transform(tfm);
122 		thrpos = thrpos + pos_v4f;
123 
124 		CSimpleParticle *newPart = NULL;
125 		try
126 		{
127 			newPart = new(CSimpleParticle);
128 			if(newPart != NULL)
129 			{
130 				newPart->SetPos(thrpos.X(), thrpos.Y(), thrpos.Z());
131 				newPart->SetVelocity(newvel.X(), newvel.Y(), newvel.Z());
132 				newPart->SetRotVel(0,0,0);
133 				newPart->life = 20;
134 				// newPart->SetColour(red, green, blue, 0.25);
135 				newPart->r_f = red;
136 				newPart->g_f = green;
137 				newPart->b_f = blue;
138 				newPart->a_f = 0.3;
139 				newPart->hasTexture_b = false;
140 				thrPartGen.AddParticle(newPart);
141 			}
142 		}
143 		catch(bad_alloc)
144 		{
145 			cout << "couldn't allocate particle. - Fwd Thrust" << endl;
146 		}
147 
148 	}
149 
150 
151 }
152 
RvsThrust()153 void CShip::RvsThrust()
154 {
155 	extern CFreebooter FB;
156 	CGalaxy *gal;
157 	gal = (CGalaxy*) FB.scene;
158 	CVec4f acc_v4f;	// acceleration vector
159 	float accMag = -gal->shipTypes[shipType_i]->rvsTrustPow_f;
160 	acc_v4f.SetXYZW(0, accMag, 0, 0);
161 
162 	// transform to world space
163 	CMatrix4f tfm;
164 	tfm.SetIdentity();
165 	glMatrixMode(GL_MODELVIEW);
166 	glPushMatrix();
167 		glLoadIdentity();
168 //		glTranslatef(-pos_v4f.X(), -pos_v4f.Y(), -pos_v4f.Z());
169 		glRotatef(rot_v3f.X(), 1, 0, 0);
170 		glRotatef(rot_v3f.Y(), 0, 1, 0);
171 		glRotatef(rot_v3f.Z(), 0, 0, 1);
172 		glGetFloatv(GL_MODELVIEW_MATRIX, tfm.m_af);
173 	glPopMatrix();
174 	tfm.Transpose();
175 	acc_v4f = acc_v4f.Transform(tfm);
176 	vel_v4f = vel_v4f + acc_v4f;
177 }
178 
Update()179 void CShip::Update()
180 {
181 	extern CFreebooter FB;
182 	CGalaxy *gal = (CGalaxy*) FB.scene;
183 
184 	vel_v4f.SetW(0);
185 	if(vel_v4f.Magnitude() > gal->shipTypes[this->shipType_i]->fwdThrustPow_f * 50)
186 	{
187 		vel_v4f.Normalise();
188 		vel_v4f = vel_v4f * gal->shipTypes[this->shipType_i]->fwdThrustPow_f * 50;
189 	}
190 	if(vel_v4f.Magnitude() > 100)
191 	{
192 		SetVelocity(0,0,0);
193 	}
194 
195 	if(this->dead_b == true)
196 	{
197 		Die();
198 		return;
199 	}
200 	if(this->damage_i > gal->shipTypes[this->shipType_i]->maxHullHealth_i)
201 	{
202 		this->Die();
203 		return;
204 	}
205 	vel_v4f.SetZ(0.0f);
206 	pos_v4f.SetZ(0.0f);
207 	pos_v4f = pos_v4f + vel_v4f;
208 
209 	// check state of current collision, if any
210 	if(inContact_b == true)
211 	{
212 		// this is fuck arsed. Keep getting entities sent through that don't exist, which
213 		// is quite a bitch, so let's just fuck it.
214 		BSBSCollision(contactThis);
215 		//inContact_b = false;
216 
217 	}
218 	// check for a collision with another ship
219 	else if(inContact_b == false)
220 	{
221 		for(int sc_i = 0; sc_i < gal->ships.Size(); sc_i++)
222 		{
223 			if((gal->ents[gal->ships[sc_i]] != this) && (((CShip*)(gal->ents[gal->ships[sc_i]]))->dead_b == false))
224 				if(this->BSBSCollision((CModelEntity*)gal->ents[gal->ships[sc_i]]) == true)
225 				{
226 					CVec4f tempVel1;
227 					CVec4f tempVel2;
228 					tempVel1 = GetVelocity();
229 					tempVel2 = gal->ents[gal->ships[sc_i]]->GetVelocity();
230 
231 					tempVel1.SetW(0);
232 					tempVel2.SetW(0);
233 
234 					CVec4f entEnt_v4f, negEntEnt;
235 					entEnt_v4f = gal->ents[gal->ships[sc_i]]->GetPos() - pos_v4f;
236 					entEnt_v4f.SetW(0);
237 
238 					CVec4f w;
239 					if(tempVel1.MagSquare() > 0.01)
240 					{
241 						//w = tempVel1 * ((tempVel1.Dot(entEnt_v4f)) / tempVel1.MagSquare());
242 						w = entEnt_v4f * ((tempVel1.Dot(entEnt_v4f)) / entEnt_v4f.MagSquare());
243 					}
244 					else w.SetXYZW(0,0,0,0);
245 
246 					tempVel2 = tempVel2 + (w*2.0f);
247 					tempVel1 = tempVel1 - (w*2.0f);
248 
249 					// how much will this impact hurt? Magnitude of the difference of velocities
250 					CVec4f pain_v4f = GetVelocity() - gal->ents[gal->ships[sc_i]]->GetVelocity();
251 					int pain = pain_v4f.Magnitude() / 2;
252 					this->damage_i += pain;
253 					((CShip*)(gal->ents[gal->ships[sc_i]]))->damage_i += pain;
254 
255 
256 					this->SetVelocity(tempVel1.X(), tempVel1.Y(), 0);
257 					gal->ents[gal->ships[sc_i]]->SetVelocity(tempVel2.X(), tempVel2.Y(), 0);
258 				}
259 		}
260 		// check for a collision with an inert
261 		for(int sc_i = 0; sc_i < gal->inerts.Size(); sc_i++)
262 		{
263 			//if(gal->ents[gal->inerts[sc_i]] != this)
264 				if(this->BSBSCollision((CModelEntity*)gal->ents[gal->inerts[sc_i]]) == true)
265 				{
266 					CVec4f tempVel1;
267 					CVec4f tempVel2;
268 					tempVel1 = GetVelocity();
269 					tempVel2 = gal->ents[gal->inerts[sc_i]]->GetVelocity();
270 
271 					tempVel1.SetW(0);
272 					tempVel2.SetW(0);
273 
274 					CVec4f entEnt_v4f, negEntEnt;
275 					entEnt_v4f = gal->ents[gal->inerts[sc_i]]->GetPos() - pos_v4f;
276 					entEnt_v4f.SetW(0);
277 
278 					CVec4f w;
279 					if(tempVel1.MagSquare() > 0.01)
280 					{
281 						//w = tempVel1 * ((tempVel1.Dot(entEnt_v4f)) / tempVel1.MagSquare());
282 						w = entEnt_v4f * ((tempVel1.Dot(entEnt_v4f)) / entEnt_v4f.MagSquare());
283 					}
284 					else w.SetXYZW(0,0,0,0);
285 
286 					tempVel2 = tempVel2 + w;
287 					tempVel1 = tempVel1 - w;
288 
289 
290 					// how much will this impact hurt? Magnitude of the difference of velocities
291 					CVec4f pain_v4f = GetVelocity() - gal->ents[gal->inerts[sc_i]]->GetVelocity();
292 					float pain = pain_v4f.Magnitude() / 2;
293 					this->shieldDamage_f += pain;
294 
295 
296 					this->SetVelocity(tempVel1.X(), tempVel1.Y(), 0);
297 					gal->ents[gal->inerts[sc_i]]->SetVelocity(tempVel2.X(), tempVel2.Y(), 0);
298 				}
299 		}
300 	}
301 
302 
303 
304 	thrPartGen.Update();
305 	gunPartGen.Update();
306 
307 	for(int gc_i = 0; gc_i < guns.Size(); gc_i++)
308 	{
309 		if(guns[gc_i].gunType_i != -1)
310 		{
311 			guns[gc_i].currentCharge_f += gal->gunTypes[guns[gc_i].gunType_i]->chargeRate_f;
312 			if(guns[gc_i].currentCharge_f > gal->gunTypes[guns[gc_i].gunType_i]->maxCharge_f)
313 				guns[gc_i].currentCharge_f = gal->gunTypes[guns[gc_i].gunType_i]->maxCharge_f;
314 			guns[gc_i].reloadTime_i -= 1;
315 		}
316 	}
317 
318 	if((pos_v4f.X() > gal->width_f) && (vel_v4f.X() > 0)) vel_v4f.SetX(-vel_v4f.X());
319 	if((pos_v4f.X() < 0) && (vel_v4f.X() < 0)) vel_v4f.SetX(-vel_v4f.X());
320 
321 	if((pos_v4f.Y() > gal->height_f) && (vel_v4f.Y() > 0)) vel_v4f.SetY(-vel_v4f.Y());
322 	if((pos_v4f.Y() < 0) && (vel_v4f.Y() < 0)) vel_v4f.SetY(-vel_v4f.Y());
323 
324 	//charge shields
325 	if((this->shieldDamage_f > 0)) this->shieldDamage_f -= 0.25;
326 
327 	// probably more to this (recharge shields, guns, whatever...)
328 
329 
330 }
331 
FireGun()332 void CShip::FireGun()
333 {
334 
335 	extern CFreebooter FB;
336 	CGalaxy *gal = (CGalaxy*)FB.scene;
337 
338 	CVec4f face_v4f;
339 	face_v4f.SetXYZW(0,1,0,0);
340 
341 	CMatrix4f tfm;
342 	tfm.SetIdentity();
343 	glMatrixMode(GL_MODELVIEW);
344 	glPushMatrix();
345 		glLoadIdentity();
346 //		glTranslatef(-pos_v4f.X(), -pos_v4f.Y(), -pos_v4f.Z());
347 		glRotatef(rot_v3f.X(), 1, 0, 0);
348 		glRotatef(rot_v3f.Y(), 0, 1, 0);
349 		glRotatef(rot_v3f.Z(), 0, 0, 1);
350 		glGetFloatv(GL_MODELVIEW_MATRIX, tfm.m_af);
351 	glPopMatrix();
352 	tfm.Transpose();
353 
354 	face_v4f = face_v4f.Transform(tfm);
355 
356 	for(int gc_i = 0; gc_i < this->guns.Size(); gc_i++)
357 	{
358 		bool readyToFire_b;
359 		readyToFire_b = ((guns[gc_i].isActive_b == true) &&
360 					  (guns[gc_i].currentCharge_f > gal->gunTypes[guns[gc_i].gunType_i]->chargeCost_f) &&
361 		              (guns[gc_i].reloadTime_i < 1));
362 
363 		// if this is a burst firing cannon
364 		if(gal->gunTypes[guns[gc_i].gunType_i]->isBurst_b)
365 		{
366 			// has the burst come to an end?
367 			if(guns[gc_i].isBursting_b && !readyToFire_b) guns[gc_i].isBursting_b = false;
368 
369 			// are we ready to burst again?
370 			if(!guns[gc_i].isBursting_b && readyToFire_b && (guns[gc_i].currentCharge_f >= gal->gunTypes[guns[gc_i].gunType_i]->minCharge_f))
371                 guns[gc_i].isBursting_b = true;
372 
373 			// if gun is not bursting, then it's not firing
374 			if(!guns[gc_i].isBursting_b) readyToFire_b = false;
375 		}
376 
377 
378 
379 
380 		if(readyToFire_b)
381 
382 		{
383 
384 
385 			CVec4f gunPos = gal->shipTypes[this->shipType_i]->gunPositions[gc_i];
386 			gunPos = gunPos.Transform(tfm);
387 
388 			gunPos = gunPos + (face_v4f * this->boundSphRadius_f);
389 
390 			CGunParticle *new_part = NULL;
391 			try
392 			{
393 				new_part = new(CGunParticle);
394 				if(new_part != NULL)
395 				{
396 					new_part->r_f = gal->gunTypes[guns[gc_i].gunType_i]->r_f;
397 					new_part->g_f = gal->gunTypes[guns[gc_i].gunType_i]->g_f;
398 					new_part->b_f = gal->gunTypes[guns[gc_i].gunType_i]->b_f;
399 					new_part->a_f = gal->gunTypes[guns[gc_i].gunType_i]->a_f;
400 					// new_part->SetColour(r,g,b,a);
401 					new_part->SetPos(pos_v4f.X() + gunPos.X(), pos_v4f.Y() + gunPos.Y(), 0);
402 					float spd = gal->gunTypes[guns[gc_i].gunType_i]->speed_f;
403 					new_part->SetVelocity((face_v4f.X() * spd) + vel_v4f.X(),
404 										face_v4f.Y() * spd + vel_v4f.Y(), 0);
405 					// new_part->life = gal->gunTypes[guns[gc_i].gunType_i]->life;
406 					new_part->SetRotVel(0,0,3);
407 					new_part->life = 70;
408 					new_part->gunType_i = this->guns[gc_i].gunType_i;
409 
410 					new_part->owner = this;
411 
412 					new_part->size_f = gal->gunTypes[guns[gc_i].gunType_i]->partSize_f;
413 					new_part->boundSphRadius_f = gal->gunTypes[guns[gc_i].gunType_i]->partSize_f;
414 					new_part->hasBoundSph_b = true;
415 
416 					this->gunPartGen.AddParticle(new_part);
417 				}
418 			}
419 			catch(bad_alloc)
420 			{
421 				cout << "couldn't allocate particle (gun-fire)" << endl;
422 			}
423 
424 			this->guns[gc_i].reloadTime_i = gal->gunTypes[guns[gc_i].gunType_i]->fireRate_i;
425 			this->guns[gc_i].currentCharge_f -= gal->gunTypes[guns[gc_i].gunType_i]->chargeCost_f;
426 
427 		}
428 	}
429 }
430 
Die()431 void CShip::Die()
432 {
433 	// for now, just shift the ship to a new random position in the galaxy
434 	extern CFreebooter FB;
435 	CGalaxy *gal = (CGalaxy*) FB.scene;
436 	// and let's have some particles as an explosion, just for fun of course...
437 
438 	if(aiSubMode_i == 1000)
439 	{
440 
441 		extern CTextureManager texMan;
442 		int texInd = texMan.LoadTexture("data/shot.bmp",WIN_BMP);
443 
444 		dead_b = true;
445 		int numNewParts = rand()%100 + 100;
446 		for(int npc_i = 0; npc_i < numNewParts; npc_i++)
447 		{
448 			CSimpleParticle *newPart;
449 			try
450 			{
451 				newPart = new(CSimpleParticle);
452 
453 				newPart->SetPos(X(), Y(), Z());
454 				newPart->SetVelocity(((float) (rand()%1000)/1000)-0.5,
455 									((float) (rand()%1000)/1000)-0.5,
456 									0.0f);
457 				newPart->life = rand()%70 +12;
458 				float r = 1.0f;
459 				//newPart->SetColour(r,r,r,0.7);
460 				newPart->tex_i = texInd;
461 				newPart->hasTexture_b = true;
462 				newPart->r_f = 1.0f;
463 				newPart->g_f = 1.0f;
464 				newPart->b_f = 1.0f;
465 				newPart->a_f = 0.6f;
466 				newPart->size_f = rand()%3+1;
467 				gal->galPartGen.AddParticle(newPart);
468 			}
469 			catch(bad_alloc)
470 			{
471 				cout << "Couldn't allocate particle - AI death" << endl;
472 			}
473 
474 			aiSubMode_i++;
475 		}
476 
477 		aiStartTime_i = SDL_GetTicks();
478 	}
479 	else
480 	{
481 		if((SDL_GetTicks() - aiStartTime_i) > 2000)
482 		{
483 			this->SetPos(-400, -400, 0);
484 			this->SetVelocity(0,0,0);
485 			this->damage_i = 0;
486 			this->shieldDamage_f = 0;
487 			// respawn as a different faction
488 			// this->faction_i = rand()%gal->factions.Size();
489 			dead_b = true;
490 			// also, make sure anything that thinks it's in contact with us
491 			// no longer has that opinion.
492 			if(inContact_b)
493 			{
494 				contactThis->inContact_b == false;
495 				contactThis->contactThis = NULL;
496 				contactThis = NULL;
497 				inContact_b = false;
498 			}
499 		}
500 	}
501 }
502