1 /*
2    Copyright (C) 2004 by James Gregory
3    Part of the GalaxyHack project
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.
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY.
9 
10    See the COPYING file for more details.
11 */
12 
13 #include "Projectile.h"
14 #include "Globals.h"
15 #include "Group.h"
16 #include "Inlines.h"
17 #include "Random.h"
18 
19 #include <stdexcept>
20 #include <cmath>
21 
22 using std::runtime_error;
23 
Projectile(float ix,float iy,CoordsInt target,WeaponType iType,Uint16 iColor)24 Projectile::Projectile(float ix, float iy, CoordsInt target, WeaponType iType, Uint16 iColor) {
25 	if (weaponLookup[iType].category == WCAT_Twin)
26 		hProj = new TwinLaserBolt(ix, iy, target, iType, iColor);
27 
28 	else if (weaponLookup[iType].category == WCAT_Small)
29 		hProj = new SmallLaserBolt(ix, iy, target, iType, iColor);
30 
31 	else if (weaponLookup[iType].category == WCAT_Missile)
32 		hProj = new Missile(ix, iy, target, iType);
33 
34 	else if (weaponLookup[iType].category == WCAT_Torpedo)
35 		hProj = new Torpedo(ix, iy, target, iType);
36 
37 	else throw runtime_error("Projectile constructor didn't recognise iType");
38 }
39 
Projectile(float ix,float iy)40 Projectile::Projectile(float ix, float iy) {
41 	hProj = new LaserExplosion(ix, iy);
42 }
43 
44 ////
45 
Projectile_Base(float ix,float iy,CoordsInt target,WeaponType iType,Uint16 iColor)46 Projectile_Base::Projectile_Base(float ix, float iy, CoordsInt target, WeaponType iType, Uint16 iColor):
47 x(ix), y(iy), color(iColor), timer(frameCounter), myType(iType) {
48 	targetSide = target.x;
49 	targetGroup = target.y;
50 
51 	mySpeed = weaponLookup[myType].speed;
52 	myLength = weaponLookup[myType].length;
53 
54 	sides[targetSide].groups[targetGroup].FiredAt(targetInfo);
55 
56 	targetUnit = targetInfo.whichUnit;
57 
58 	weHit = CheckToHit(weaponLookup[myType].accuracy, targetInfo.difficulty);
59 }
60 
Projectile_Base(float ix,float iy)61 Projectile_Base::Projectile_Base(float ix, float iy):
62 x(ix), y(iy)
63 {}
64 
PropsToSpeedAndLength(float dx,float dy)65 void Projectile_Base::PropsToSpeedAndLength(float dx, float dy) {
66 	float propx;
67 	float propy;
68 
69 	GetMoveProps(propx, propy, dx, dy);
70 
71 	//vital to do this properly, this caused a most annoying bug
72 	float propz = propx*propx + propy*propy;
73 	propz = std::sqrt(propz);
74 	speedx = (propx / propz) * mySpeed;
75 	speedy = (propy / propz) * mySpeed;
76 
77 	//this isn't quite right but who cares, we're in 3 dimensions, right?
78 	//the real answer is:
79 	//length in x = ((speed in x) / (speed in x + speed in y)) * length
80 	lengthx = static_cast<int>(myLength * propx);
81 	lengthy = static_cast<int>(myLength * propy);
82 }
83 
StandardLineDraw()84 void Projectile_Base::StandardLineDraw() {
85 	int x0 = static_cast<int>(x) - viewx;
86 	int y0 = static_cast<int>(y) - viewy;
87 	int x1 = static_cast<int>(x) + lengthx - viewx;
88 	int y1 = static_cast<int>(y) + lengthy - viewy;
89 	if (ClipLine(x0, y0, x1, y1))
90 		DrawLine(x0, y0, x1, y1, color);
91 }
92 
Move()93 bool Projectile_Base::Move() {
94 	if (frameCounter - timer > duration) {
95 		if (weHit)
96 			sides[targetSide].groups[targetGroup].BeenHit(targetUnit, weaponLookup[myType].power);
97 		return false;
98 	}
99 	else {
100 		x += speedx;
101 		y += speedy;
102 
103 		return true;
104 	}
105 }
106 
SetDuration(float distance)107 void Projectile_Base::SetDuration(float distance) {
108 	//+1 because move (and hence erasure) comes before drawing
109 	duration = static_cast<int>(((distance - myLength) / mySpeed) + 1);
110 
111 	//if we're right on top of them..
112 	if (duration < 1) {
113 		//right right on top of them, in which case we need
114 		//to make up arbitrary lengths and speeds
115 		if (speedx == 0 && speedy == 0) {
116 			speedx = mySpeed / 2;
117 			speedy = mySpeed / 2;
118 			lengthx = myLength / 4;
119 			lengthy = myLength / 4;
120 		}
121 
122 		duration = 1;
123 	}
124 }
125 
126 ///
127 
LaserBolt_Base(float ix,float iy,CoordsInt target,WeaponType iType,Uint16 iColor)128 LaserBolt_Base::LaserBolt_Base(float ix, float iy, CoordsInt target, WeaponType iType, Uint16 iColor):
129 Projectile_Base(ix, iy, target, iType, iColor) {
130 	PredictTarget();
131 }
132 
PredictTarget()133 void LaserBolt_Base::PredictTarget() {
134 	/*
135 	Another way to do this would be:
136 	mx = m.x + c, so x = c / (m. - m)
137 	with x being time
138 
139 	int time = distance / (laserSpeed - targetInfo.speed);
140 
141 	this must be done in each dimension, so:
142 
143 	int timex = dx / (laserSpeedx - targetInfo.speedx);
144 	int timey = dy / (laserSpeedy - targetInfo.speedy);
145 
146 	watching out for division by 0 and/or negative values
147 
148 	However, we do not know the proportions of laserSpeed
149 	in x and y, because that's what we're trying to work out!
150 
151 	For this to work, we'd also need to know the direction of
152 	laserSpeed in each dimension, so we could deal with them
153 	moving towards us
154 
155 
156 	what we actually do is two iterations of where they will
157 	be in the amount of time it takes us to reach them
158 	*/
159 
160 	float predx = targetInfo.currentx + targetInfo.weakSpot.x;
161 	float predy = targetInfo.currenty + targetInfo.weakSpot.y;
162 
163 	float dx = predx - x;
164 	float dy = predy - y;
165 	float distance = FastDist(dx, dy);
166 
167 	float time = distance / mySpeed;
168 
169 	predx += targetInfo.speedx * time;
170 	predy += targetInfo.speedy * time;
171 
172 	dx = predx - x;
173 	dy = predy - y;
174 	PropsToSpeedAndLength(dx, dy);
175 
176 	float totalDistance = FastDist(dx, dy);
177 	SetDuration(totalDistance);
178 }
179 
180 ///
181 
SmallLaserBolt(float ix,float iy,CoordsInt target,WeaponType iType,Uint16 iColor)182 SmallLaserBolt::SmallLaserBolt(float ix, float iy, CoordsInt target, WeaponType iType, Uint16 iColor):
183 LaserBolt_Base(ix, iy, target, iType, iColor) {}
184 
DrawSelfPixels()185 void SmallLaserBolt::DrawSelfPixels() {
186 	StandardLineDraw();
187 }
188 
189 
190 ////
191 
TwinLaserBolt(float ix,float iy,CoordsInt target,WeaponType iType,Uint16 iColor)192 TwinLaserBolt::TwinLaserBolt(float ix, float iy, CoordsInt target, WeaponType iType, Uint16 iColor):
193 LaserBolt_Base(ix, iy, target, iType, iColor) {}
194 
DrawSelfPixels()195 void TwinLaserBolt::DrawSelfPixels() {
196 	if (speedy > speedx) {
197 		int x0 = static_cast<int>(x) - viewx - 3;
198 		int y0 = static_cast<int>(y) - viewy;
199 		int x1 = static_cast<int>(x) + lengthx - viewx - 3;
200 		int y1 = static_cast<int>(y) + lengthy - viewy;
201 
202 		if (ClipLine(x0, y0, x1, y1))
203 			DrawLine(x0, y0, x1, y1, color);
204 
205 		x0+= 6;
206 		x1+= 6;
207 
208 		if (ClipLine(x0, y0, x1, y1))
209 			DrawLine(x0, y0, x1, y1, color);
210 	}
211 	else {
212 		int x0 = static_cast<int>(x) - viewx;
213 		int y0 = static_cast<int>(y) - viewy - 3;
214 		int x1 = static_cast<int>(x) + lengthx - viewx;
215 		int y1 = static_cast<int>(y) + lengthy - viewy - 3;
216 
217 		if (ClipLine(x0, y0, x1, y1))
218 			DrawLine(x0, y0, x1, y1, color);
219 
220 		y0+= 6;
221 		y1+= 6;
222 
223 		if (ClipLine(x0, y0, x1, y1))
224 			DrawLine(x0, y0, x1, y1, color);
225 	}
226 }
227 
228 ////
DrawBigLaser(int x0,int y0,int x1,int y1,Uint16 color)229 void Projectile_Base::DrawBigLaser(int x0, int y0, int x1, int y1, Uint16 color) {
230 	x0-= viewx;
231 	y0-= viewy;
232 	x1-= viewx;
233 	y1-= viewy;
234 
235 	if (ClipLine(x0, y0, x1, y1)) {
236 		DrawLine(x0, y0, x1, y1, color);
237 
238 		//only have to cast immediately if not floats
239 		float dx = x1 - x0;
240 		float dy = y1 - y0;
241 		float distance = FastDist(dx, dy);
242 
243 		float propx, propy;
244 
245 		//dont need the sign
246 		if (distance) {
247 			propx = fabs(dx) / distance;
248 			propy = fabs(dy) / distance;
249 		}
250 
251 		if (propy > propx) {
252 			++x0;
253 			++x1;
254 
255 			if (ClipLine(x0, y0, x1, y1))
256 				DrawLine(x0, y0, x1, y1, color);
257 
258 			x0-= 2;
259 			x1-= 2;
260 
261 			if (ClipLine(x0, y0, x1, y1))
262 				DrawLine(x0, y0, x1, y1, color);
263 		}
264 		else {
265 			++y0;
266 			++y1;
267 
268 			if (ClipLine(x0, y0, x1, y1))
269 				DrawLine(x0, y0, x1, y1, color);
270 
271 			y0-= 2;
272 			y1-= 2;
273 
274 			if (ClipLine(x0, y0, x1, y1))
275 				DrawLine(x0, y0, x1, y1, color);
276 		}
277 	}
278 }
279 
280 ////
281 
282 
283 
284 // this function clips the sent line
ClipLine(int & x1,int & y1,int & x2,int & y2)285 int Projectile_Base::ClipLine(int& x1, int& y1,int& x2, int& y2) {
286 	//The screen dimensions to clip to
287 	static const int min_clip_x = 0;
288 	int max_clip_x = globalSettings.screenWidth - 1;
289 	static const int min_clip_y = 0;
290 	int max_clip_y = globalSettings.screenHeight - 1;
291 
292 	// internal clipping codes
293 	#define CLIP_CODE_C  0x0000
294 	#define CLIP_CODE_N  0x0008
295 	#define CLIP_CODE_S  0x0004
296 	#define CLIP_CODE_E  0x0002
297 	#define CLIP_CODE_W  0x0001
298 
299 	#define CLIP_CODE_NE 0x000a
300 	#define CLIP_CODE_SE 0x0006
301 	#define CLIP_CODE_NW 0x0009
302 	#define CLIP_CODE_SW 0x0005
303 
304 	int xc1=x1, yc1=y1, xc2=x2, yc2=y2;
305 
306 	int p1_code=0, p2_code=0;
307 
308 	// determine codes for p1 and p2
309 	if (y1 < min_clip_y)
310 		p1_code|=CLIP_CODE_N;
311 	else if (y1 > max_clip_y)
312 		p1_code|=CLIP_CODE_S;
313 
314 	if (x1 < min_clip_x)
315 		p1_code|=CLIP_CODE_W;
316 	else if (x1 > max_clip_x)
317 		p1_code|=CLIP_CODE_E;
318 
319 	if (y2 < min_clip_y)
320 		p2_code|=CLIP_CODE_N;
321 	else if (y2 > max_clip_y)
322 		p2_code|=CLIP_CODE_S;
323 
324 	if (x2 < min_clip_x)
325 		p2_code|=CLIP_CODE_W;
326 	else if (x2 > max_clip_x)
327 		p2_code|=CLIP_CODE_E;
328 
329 	// try and trivially reject
330 	if ((p1_code & p2_code))
331 		return 0;
332 
333 	// test for totally visible, if so leave points untouched
334 	if (p1_code==0 && p2_code==0)
335 		return 1;
336 
337 	// determine end clip point for p1
338 	switch(p1_code) {
339 	case CLIP_CODE_C:
340 
341 		break;
342 
343 	case CLIP_CODE_N:
344 		yc1 = min_clip_y;
345 		xc1 = x1 + 0.5+(min_clip_y-y1)*(x2-x1)/(y2-y1);
346 		break;
347 
348 	case CLIP_CODE_S:
349 		yc1 = max_clip_y;
350 		xc1 = x1 + 0.5+(max_clip_y - y1)*(x2-x1)/(y2-y1);
351 		break;
352 
353 	case CLIP_CODE_W:
354 		xc1 = min_clip_x;
355 		yc1 = y1 + 0.5+(min_clip_x - x1)*(y2-y1)/(x2-x1);
356 		break;
357 
358 	case CLIP_CODE_E:
359 		xc1 = max_clip_x;
360 		yc1 = y1 + 0.5+(max_clip_x - x1)*(y2-y1)/(x2-x1);
361 		break;
362 
363 		// these cases are more complex, must compute 2 intersections
364 	case CLIP_CODE_NE:
365 		// north hline intersection
366 		yc1 = min_clip_y;
367 		xc1 = x1 + 0.5+(min_clip_y - y1)*(x2 - x1)/(y2 - y1);
368 
369 		// test if intersection is valid, of so then done, else compute next
370 		if (xc1 < min_clip_x || xc1 > max_clip_x) {
371 			// east vline intersection
372 			xc1 = max_clip_x;
373 			yc1 = y1 + 0.5 + (max_clip_x - x1)*(y2 - y1)/(x2 - x1);
374 		}
375 		break;
376 
377 	case CLIP_CODE_SE:
378 		// south hline intersection
379 		yc1 = max_clip_y;
380 		xc1 = x1 + 0.5 + (max_clip_y-y1) * (x2-x1) / (y2-y1);
381 
382 		// test if intersection is valid, of so then done, else compute next
383 		if (xc1 < min_clip_x || xc1 > max_clip_x) {
384 			// east vline intersection
385 			xc1 = max_clip_x;
386 			yc1 = y1 + 0.5+(max_clip_x-x1)*(y2-y1)/(x2-x1);
387 		}
388 		break;
389 
390 	case CLIP_CODE_NW:
391 		// north hline intersection
392 		yc1 = min_clip_y;
393 		xc1 = x1 + 0.5+(min_clip_y-y1)*(x2-x1)/(y2-y1);
394 
395 		// test if intersection is valid, of so then done, else compute next
396 		if (xc1 < min_clip_x || xc1 > max_clip_x) {
397 			xc1 = min_clip_x;
398 			yc1 = y1 + 0.5+(min_clip_x-x1)*(y2-y1)/(x2-x1);
399 		}
400 		break;
401 
402 	case CLIP_CODE_SW:
403 		// south hline intersection
404 		yc1 = max_clip_y;
405 		xc1 = x1 + 0.5+(max_clip_y-y1)*(x2-x1)/(y2-y1);
406 
407 		// test if intersection is valid, of so then done, else compute next
408 		if (xc1 < min_clip_x || xc1 > max_clip_x) {
409 			xc1 = min_clip_x;
410 			yc1 = y1 + 0.5+(min_clip_x-x1)*(y2-y1)/(x2-x1);
411 		}
412 		break;
413 	}
414 
415 	// determine clip point for p2
416 	switch(p2_code) {
417 	case CLIP_CODE_C:
418 
419 		break;
420 
421 	case CLIP_CODE_N:
422 		yc2 = min_clip_y;
423 		xc2 = x2 + (min_clip_y-y2)*(x1-x2)/(y1-y2);
424 		break;
425 
426 	case CLIP_CODE_S: {
427 			yc2 = max_clip_y;
428 			xc2 = x2 + (max_clip_y-y2)*(x1-x2)/(y1-y2);
429 		}
430 		break;
431 
432 	case CLIP_CODE_W: {
433 			xc2 = min_clip_x;
434 			yc2 = y2 + (min_clip_x-x2)*(y1-y2)/(x1-x2);
435 		}
436 		break;
437 
438 	case CLIP_CODE_E: {
439 			xc2 = max_clip_x;
440 			yc2 = y2 + (max_clip_x-x2)*(y1-y2)/(x1-x2);
441 		}
442 		break;
443 
444 		// these cases are more complex, must compute 2 intersections
445 	case CLIP_CODE_NE: {
446 			// north hline intersection
447 			yc2 = min_clip_y;
448 			xc2 = x2 + 0.5+(min_clip_y-y2)*(x1-x2)/(y1-y2);
449 
450 			// test if intersection is valid, of so then done, else compute next
451 			if (xc2 < min_clip_x || xc2 > max_clip_x) {
452 				// east vline intersection
453 				xc2 = max_clip_x;
454 				yc2 = y2 + 0.5+(max_clip_x-x2)*(y1-y2)/(x1-x2);
455 			} // end if
456 
457 		}
458 		break;
459 
460 	case CLIP_CODE_SE: {
461 			// south hline intersection
462 			yc2 = max_clip_y;
463 			xc2 = x2 + 0.5+(max_clip_y-y2)*(x1-x2)/(y1-y2);
464 
465 			// test if intersection is valid, of so then done, else compute next
466 			if (xc2 < min_clip_x || xc2 > max_clip_x) {
467 				// east vline intersection
468 				xc2 = max_clip_x;
469 				yc2 = y2 + 0.5+(max_clip_x-x2)*(y1-y2)/(x1-x2);
470 			} // end if
471 
472 
473 		}
474 		break;
475 
476 	case CLIP_CODE_NW: {
477 			// north hline intersection
478 			yc2 = min_clip_y;
479 			xc2 = x2 + 0.5+(min_clip_y-y2)*(x1-x2)/(y1-y2);
480 
481 			// test if intersection is valid, of so then done, else compute next
482 			if (xc2 < min_clip_x || xc2 > max_clip_x) {
483 				xc2 = min_clip_x;
484 				yc2 = y2 + 0.5+(min_clip_x-x2)*(y1-y2)/(x1-x2);
485 			} // end if
486 
487 		}
488 		break;
489 
490 	case CLIP_CODE_SW: {
491 			// south hline intersection
492 			yc2 = max_clip_y;
493 			xc2 = x2 + 0.5+(max_clip_y-y2)*(x1-x2)/(y1-y2);
494 
495 			// test if intersection is valid, of so then done, else compute next
496 			if (xc2 < min_clip_x || xc2 > max_clip_x) {
497 				xc2 = min_clip_x;
498 				yc2 = y2 + 0.5+(min_clip_x-x2)*(y1-y2)/(x1-x2);
499 			} // end if
500 
501 		}
502 		break;
503 
504 	default:break;
505 
506 	} // end switch
507 
508 	// do bounds check
509 	if ((xc1 < min_clip_x) || (xc1 > max_clip_x) ||
510 	        (yc1 < min_clip_y) || (yc1 > max_clip_y) ||
511 	        (xc2 < min_clip_x) || (xc2 > max_clip_x) ||
512 	        (yc2 < min_clip_y) || (yc2 > max_clip_y) )
513 		return 0;
514 
515 	// store vars back
516 	x1 = xc1;
517 	y1 = yc1;
518 
519 	x2 = xc2;
520 	y2 = yc2;
521 
522 	return 1;
523 }
524 
525 
526 // this function draws a line from xo,yo to x1,y1 using differential error
527 // terms (based on Bresenahams work)
DrawLine(int x0,int y0,int x1,int y1,Uint16 color)528 void Projectile_Base::DrawLine(int x0, int y0, int x1, int y1, Uint16 color) {
529 	int dx,         // difference in x's
530 	dy,             // difference in y's
531 	dx2,            // dx,dy * 2
532 	dy2,
533 	x_inc,          // amount in pixel space to move during drawing
534 	y_inc,          // amount in pixel space to move during drawing
535 	error,          // the discriminant i.e. error i.e. decision variable
536 	index;          // used for looping
537 
538 	static const int lpitch_2 = JSDL.screen->pitch >> 1; // Uint16 strided lpitch
539 
540 	// pre-compute first pixel address in video buffer based on 16bit data
541 	Uint16* vb_start2 = reinterpret_cast<Uint16*>(JSDL.screen->pixels) + x0 + y0 * lpitch_2;
542 
543 	// compute horizontal and vertical deltas
544 	dx = x1 - x0;
545 	dy = y1 - y0;
546 
547 	// test which direction the line is going in i.e. slope angle
548 	if (dx>=0)
549 		x_inc = 1;
550 	else {
551 		x_inc = -1;
552 		dx    = -dx;  // need absolute value
553 	}
554 
555 	// test y component of slope
556 	if (dy>=0)
557 		y_inc = lpitch_2;
558 	else {
559 
560 		y_inc = -lpitch_2;
561 		dy    = -dy;  // need absolute value
562 	}
563 
564 	//compute (dx,dy) * 2
565 	dx2 = dx << 1;
566 	dy2 = dy << 1;
567 
568 	//now based on which delta is greater we can draw the line
569 	//if |slope| <= 1
570 	if (dx > dy) {
571 		// initialize error term
572 		error = dy2 - dx;
573 
574 		// draw the line
575 		for (index=0; index <= dx; index++) {
576 			// set the pixel
577 			*vb_start2 = color;
578 
579 			// test if error has overflowed
580 			if (error >= 0) {
581 				error-=dx2;
582 
583 				// move to next line
584 				vb_start2+=y_inc;
585 			}
586 
587 			// adjust the error term
588 			error+=dy2;
589 
590 			// move to the next pixel
591 			vb_start2+=x_inc;
592 		}
593 	}
594 	else
595 		//else |slope| > 1
596 	{
597 		// initialize error term
598 		error = dx2 - dy;
599 
600 		// draw the line
601 		for (index=0; index <= dy; index++) {
602 			// set the pixel
603 			*vb_start2 = color;
604 
605 			// test if error overflowed
606 			if (error >= 0) {
607 				error-=dy2;
608 
609 				// move to next line
610 				vb_start2+=x_inc;
611 			}
612 
613 			// adjust the error term
614 			error+=dx2;
615 
616 			// move to the next pixel
617 
618 			vb_start2+=y_inc;
619 		}
620 	}
621 }
622 
Missile(float ix,float iy,CoordsInt target,WeaponType iType)623 Missile::Missile(float ix, float iy, CoordsInt target, WeaponType iType):
624 Projectile_Base(ix, iy, target, iType, missileGrey), exploding(0), targetDead(0) {
625 	//get it to sort out some props because it will be drawn before it first gets to move
626 	Move();
627 	x-= speedx;
628 	y-= speedy;
629 }
630 
Move()631 bool Missile::Move() {
632 	if (exploding) {
633 		if (weHit)
634 			sides[targetSide].groups[targetGroup].BeenHit(targetUnit, weaponLookup[myType].power);
635 		return false;
636 	}
637 
638 	if (!targetDead) {
639 		targetCoords = sides[targetSide].groups[targetGroup].GetUnitCenter(targetUnit);
640 
641 		if (!sides[targetSide].groups[targetGroup].GetUnitAlive(targetUnit)) {
642 			targetCoords.x += Random() % static_cast<int>(mySpeed / 2) - static_cast<int>(mySpeed);
643 			targetCoords.y += Random() % static_cast<int>(mySpeed / 2) - static_cast<int>(mySpeed);
644 			targetDead = 1;
645 		}
646 	}
647 
648 	float dx = targetCoords.x - x;
649 	float dy = targetCoords.y - y;
650 	float distance = FastDist(dx, dy);
651 
652 	if (distance < weaponLookup[myType].speed)
653 		exploding = true;
654 
655 	else {
656 		PropsToSpeedAndLength(dx, dy);
657 
658 		x+= speedx;
659 		y+= speedy;
660 	}
661 
662 	return true;
663 }
664 
DrawSelfPixels()665 void Missile::DrawSelfPixels() {
666 	StandardLineDraw();
667 }
668 
Torpedo(float ix,float iy,CoordsInt target,WeaponType iType)669 Torpedo::Torpedo(float ix, float iy, CoordsInt target, WeaponType iType):
670 Projectile_Base(ix, iy, target, iType, torpedoBlue), exploding(false) {
671 	float dx = targetInfo.currentx + targetInfo.weakSpot.x - x;
672 	float dy = targetInfo.currenty + targetInfo.weakSpot.y - y;
673 	float distance = FastDist(dx, dy);
674 
675 	PropsToSpeedAndLength(dx, dy);
676 
677 	SetDuration(distance);
678 }
679 
680 
DrawSelfPixels()681 void Torpedo::DrawSelfPixels() {
682 	if (exploding)
683 		return;
684 
685 	int x0 = static_cast<int>(x) - viewx;
686 	int y0 = static_cast<int>(y) - viewy;
687 	int x1 = static_cast<int>(x) + lengthx - viewx;
688 	int y1 = static_cast<int>(y) + lengthy - viewy;
689 
690 	if (ClipLine(x0, y0, x1, y1)) {
691 		DrawLine(x0, y0, x1, y1, color);
692 
693 		//only have to cast immediately if not floats
694 		float dx = x1 - x0;
695 		float dy = y1 - y0;
696 		float distance = FastDist(dx, dy);
697 
698 		float propx, propy;
699 
700 		//dont need the sign
701 		if (distance) {
702 			propx = fabs(dx) / distance;
703 			propy = fabs(dy) / distance;
704 		}
705 
706 		if (propy > propx) {
707 			++x0;
708 			++x1;
709 
710 			if (ClipLine(x0, y0, x1, y1))
711 				DrawLine(x0, y0, x1, y1, color);
712 		}
713 		else {
714 			++y0;
715 			++y1;
716 
717 			if (ClipLine(x0, y0, x1, y1))
718 				DrawLine(x0, y0, x1, y1, color);
719 		}
720 	}
721 }
722 
Move()723 bool Torpedo::Move() {
724 	if (exploding) {
725 		if (explodeTimer)
726 			--explodeTimer;
727 		else
728 			//already dealt damage
729 			return false;
730 	} else if (!Projectile_Base::Move()) {
731 		exploding = true;
732 		explodeTimer = explosionExplodeFrames * framesPerAnimFrame;
733 	}
734 
735 	return true;
736 }
737 
DrawSelfBitmap()738 void Torpedo::DrawSelfBitmap() {
739 	if (exploding) {
740 		SDL_Rect tempRect = {static_cast<int>(x) - viewx, static_cast<int>(y) - viewy, 0, 0};
741 
742 		if (explodeTimer > framesPerAnimFrame * 5)
743 			JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE1], tempRect);
744 		else if (explodeTimer > framesPerAnimFrame * 4)
745 			JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE2], tempRect);
746 		else if (explodeTimer > framesPerAnimFrame * 3)
747 			JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE3], tempRect);
748 		else if (explodeTimer > framesPerAnimFrame * 2)
749 			JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE4], tempRect);
750 		else if (explodeTimer > framesPerAnimFrame)
751 			JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE5], tempRect);
752 		else
753 			JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE6], tempRect);
754 	}
755 }
756 
CheckToHit(int accuracy,int difficulty)757 bool Projectile_Base::CheckToHit(int accuracy, int difficulty) {
758 	if (difficulty == 0)
759 		return true;
760 	else if (Random() % 100 + accuracy - difficulty > 99)
761 		return true;
762 	else
763 		return false;
764 }
765 
LaserExplosion(float ix,float iy)766 LaserExplosion::LaserExplosion(float ix, float iy):
767 Projectile_Base(ix, iy), explodeTimer(explosionExplodeFrames * framesPerAnimFrame)
768 {}
769 
Move()770 bool LaserExplosion::Move() {
771 	if (explodeTimer)
772 		--explodeTimer;
773 	else
774 		return false;
775 
776 	return true;
777 }
778 
DrawSelfBitmap()779 void LaserExplosion::DrawSelfBitmap() {
780 	SDL_Rect tempRect = {static_cast<int>(x) - viewx, static_cast<int>(y) - viewy, 0, 0};
781 
782 	if (explodeTimer > framesPerAnimFrame * 5)
783 		JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE1], tempRect);
784 	else if (explodeTimer > framesPerAnimFrame * 4)
785 		JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE2], tempRect);
786 	else if (explodeTimer > framesPerAnimFrame * 3)
787 		JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE3], tempRect);
788 	else if (explodeTimer > framesPerAnimFrame * 2)
789 		JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE4], tempRect);
790 	else if (explodeTimer > framesPerAnimFrame)
791 		JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE5], tempRect);
792 	else
793 		JSDL.Blt(genPictures[GENPIC_SMSHEXPLODE6], tempRect);
794 }
795 
796