1 /******************************************************************************************
2  *
3  * HighNoon - Duell im All
4  * Copyright (c) 2005, 2006 Patrick Gerdsmeier <patrick@gerdsmeier.net>
5  *
6  * "shoot.cpp"
7  *
8  *
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2, or (at your option)
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  ******************************************************************************************/
25 
26 #include <cmath>
27 
28 #include "sound.hpp"
29 #include "shoot.hpp"
30 
31 extern SDL_Surface* MYSDLSCREEN;
32 extern Soundset *sound;
33 
34 #ifdef __DEBUG__
35 extern int __SHOOTS;
36 extern int __HITS;
37 #endif
38 
39 /************************************************************************
40  *									*
41  * Explosion								*
42  *									*
43  ************************************************************************/
Explosion(double x,double y)44 Explosion::Explosion( double x, double y )
45 :
46 	Spaceobject( x, y ),
47 	exploding(false)
48 {
49 	verbose( "Initializing Explosion" );
50 
51 	explosion_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/explosionanim.gif", 6 );
52 	explosion_sprite->setAlpha(230);
53 	explosion_sprite->setRepeatmode(false);
54 	explosion_sprite->setFramerate(1);
55 }
56 
~Explosion()57 Explosion::~Explosion()
58 {
59 	verbose( "Deleting Explosion" );
60 
61 	delete explosion_sprite;
62 }
63 
is_active() const64 bool Explosion::is_active() const
65 {
66 	return exploding;
67 }
68 
activate(double x,double y)69 void Explosion::activate( double x, double y )
70 {
71 	set_Pos( x, y );
72 	explosion_sprite->setPos( (int)x, (int)y );
73 	explosion_sprite->resetFrames();
74 	exploding = true;
75 
76 	sound->play(SOUND_EXPLOSION);
77 }
78 
check_collision(double x,double y,double width,bool spacing)79 bool Explosion::check_collision( double x, double y, double width, bool spacing )
80 {
81 	return false;
82 }
83 
draw()84 void Explosion::draw()
85 {
86 	if ( is_active() ) {
87 		explosion_sprite->draw();
88 		exploding = !explosion_sprite->is_onLastFrame();
89 	}
90 }
91 
hit(Spaceobject * object)92 void Explosion::hit( Spaceobject *object ) {}
93 
94 /************************************************************************
95  *									*
96  * Shoot 								*
97  *									*
98  ************************************************************************/
Shoot(double x,double y)99 Shoot::Shoot( double x, double y )
100 :
101 	Spaceobject( x, y ),
102 	is_exploding(false),
103 	moving_time(0),
104 	last_shootPos( Vector_2(0,0,K) ),
105 	pre_calculated_Steps(0)
106 {
107 	verbose( "Initializing Shoot" );
108 
109 	weight = 1;
110 	explosion = new Explosion();
111 }
112 
~Shoot()113 Shoot::~Shoot()
114 {
115 	verbose( "Deleting Shoot" );
116 
117 	delete explosion;
118 }
119 
is_active() const120 bool Shoot::is_active() const
121 {
122 	return false;
123 }
124 
will_be_a_Hit(int player_id,double factor,Vector_2 start,Vector_2 direction,Galaxy * galaxy)125 bool Shoot::will_be_a_Hit( int player_id, double factor, Vector_2 start, Vector_2 direction, Galaxy *galaxy )
126 {
127 	calculate_ShootPath( start, direction, galaxy );
128 
129 	for ( int i=0; i < pre_calculated_Steps; i++ )
130 		if ( galaxy->is_Ufo_In_Area( player_id, pre_calculated_Pos[i].x, pre_calculated_Pos[i].y, factor ) )
131 			return true;
132 
133 	return false;
134 }
135 
activate(Vector_2 start,Vector_2 vector)136 void Shoot::activate( Vector_2 start, Vector_2 vector )
137 {
138 	set_Pos( start.getX(), start.getY() );
139 	direction = vector.getAngle();
140 	speed = vector.getLength();
141 	last_shootPos = start;
142 	is_exploding = false;
143 	moving_time = MAXSHOOTRUN;
144 
145 	sound->play(SOUND_SHOOT);
146 }
147 
reset()148 void Shoot::reset() {}
149 
destroy()150 void Shoot::destroy() {}
151 
move(Galaxy * galaxy)152 bool Shoot::move( Galaxy *galaxy ) { return false; }
153 
has_Extra_collision(Extra * extra)154 bool Shoot::has_Extra_collision( Extra *extra )
155 {
156 	return false;
157 }
158 
check_collision(double x,double y,double width,bool spacing)159 bool Shoot::check_collision( double x, double y, double width, bool spacing )
160 {
161 	return false;
162 }
163 
draw()164 void Shoot::draw() {}
165 
draw_hint(Vector_2 start,Vector_2 direction,Galaxy * galaxy)166 void Shoot::draw_hint( Vector_2 start, Vector_2 direction, Galaxy *galaxy )
167 {
168 	static int colorpos = 50;
169 
170 	if ( (colorpos -= 20) < 50 ) colorpos = 255;
171 
172 	calculate_ShootPath( start, direction, galaxy );
173 
174 	int c = colorpos;
175 
176 	SDL_LockSurface( MYSDLSCREEN );
177 
178 	for (int i=0; i < pre_calculated_Steps; i++) {
179 		Sprite::putpixel(
180 			pre_calculated_Pos[i].x + Sprite::x_offset,
181 			pre_calculated_Pos[i].y + Sprite::y_offset,
182 			SDL_MapRGB( MYSDLSCREEN->format, c, c, c ) );
183 
184 		if ( (c += 30) > 255 ) c = 50;
185 	}
186 
187 	SDL_UnlockSurface( MYSDLSCREEN );
188 }
189 
hit(Spaceobject * object)190 void Shoot::hit( Spaceobject *object ) {}
191 
calculate_ShootPath(Vector_2 start,Vector_2 direction,Galaxy * galaxy)192 void Shoot::calculate_ShootPath( Vector_2 start, Vector_2 direction, Galaxy *galaxy )
193 {
194 	static Vector_2 last_angle=Vector_2( 0, 0, K );
195 	static Vector_2 last_shoot=Vector_2( 0, 0, K );
196 
197 	if ( last_angle != direction || last_shoot != start ) {
198 
199 		pre_calculated_Steps = 0;
200 		last_angle = direction;
201 		last_shoot = start;
202 
203 		for ( int i=0; i < MAXPRECALC; i++ ) {
204 			galaxy->calculate_nextPos( start, direction );
205 			double x = start.getX();
206 			double y = start.getY();
207 
208 			if ( !galaxy->check_collision( x, y, get_Width() ) ) {
209 				pre_calculated_Pos[i].x = (int)x;
210 				pre_calculated_Pos[i].y = (int)y;
211 				pre_calculated_Steps++;
212 			} else break;
213 		}
214 	}
215 }
216 
217 /************************************************************************
218  *									*
219  * Laser								*
220  *									*
221  ************************************************************************/
Laser(double x,double y)222 Laser::Laser( double x, double y )
223 :
224 	Shoot( x, y )
225 {
226 	verbose( "Initializing Laser" );
227 
228 	laser_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/shoot.gif" );
229 	laserback_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/shootback.gif" );
230 	laserbackk_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/shootbackk.gif" );
231 
232 	width = laser_sprite->getWidth();
233 }
234 
~Laser()235 Laser::~Laser()
236 {
237 	verbose( "Deleting Laser" );
238 
239 	delete laser_sprite;
240 	delete laserback_sprite;
241 	delete laserbackk_sprite;
242 }
243 
is_active() const244 bool Laser::is_active() const
245 {
246 	return moving_time > 0;
247 }
248 
destroy()249 void Laser::destroy()
250 {
251 	if ( is_active() )
252 		hit( this );
253 }
254 
move(Galaxy * galaxy)255 bool Laser::move( Galaxy *galaxy )
256 {
257 	if ( is_active() ) {
258 		if ( --moving_time == 0 ) return true;
259 
260 		Vector_2 my_shootPos = Vector_2( get_X(), get_Y(), K );
261 		Vector_2 my_shootVector = Vector_2( speed, direction, P );
262 		last_shootPos = my_shootPos;
263 
264 		galaxy->calculate_nextPos( my_shootPos, my_shootVector );
265 
266 		x = my_shootPos.getX();
267 		y = my_shootPos.getY();
268 		speed = my_shootVector.getLength();
269 		direction = my_shootVector.getAngle();
270 
271 		if ( galaxy->has_collision( this ) &&
272 			!is_active() )
273 
274 			return true;
275 	}
276 
277 	return false;
278 }
279 
has_Extra_collision(Extra * extra)280 bool Laser::has_Extra_collision( Extra *extra )
281 {
282 	if ( extra->has_collision( (Spaceobject *)this ) )
283 		return true;
284 
285 	return false;
286 }
287 
check_collision(double x,double y,double width,bool spacing)288 bool Laser::check_collision( double x, double y, double width, bool spacing )
289 {
290 	return check_sphere_collision( x, y, width, spacing );
291 }
292 
draw()293 void Laser::draw()
294 {
295 	if ( is_active() ) {
296 
297 		if ( moving_time > 100 ||
298 			(int)RANDOM(100, 0) < ( moving_time/2 )+25 ) {
299 
300 			double x_anim = RANDOM(2, -2);
301 			double y_anim = RANDOM(2, -2);
302 			int alpha_anim = (int)RANDOM(10, -10);
303 
304 			Vector_2 v = last_shootPos-Vector_2( get_X(), get_Y(), K );
305 			Vector_2 v_1 = v*0.5;
306 			Vector_2 v_2 = v*0.2;
307 
308 			laserbackk_sprite->setAlpha( 120+alpha_anim );
309 			laserbackk_sprite->setPos(
310 				(int)( get_X()+v_1.getX() ),
311 				(int)( get_Y()+v_1.getY() ) );
312 			laserbackk_sprite->draw();
313 
314 			laserback_sprite->setAlpha( 140+alpha_anim );
315 			laserback_sprite->setPos(
316 				(int)( get_X()+v_2.getX() + x_anim ),
317 				(int)( get_Y()+v_2.getY() + y_anim ) );
318 			laserback_sprite->draw();
319 
320 			laser_sprite->setAlpha( 200+alpha_anim );
321 			laser_sprite->setPos( (int)get_X(), (int)get_Y() );
322 			laser_sprite->draw();
323 		}
324 	}
325 
326 	if ( is_exploding ) {
327 		explosion->draw();
328 		is_exploding = explosion->is_active();
329 	}
330 }
331 
hit(Spaceobject * object)332 void Laser::hit( Spaceobject *object )
333 {
334 	explosion->activate( x, y );
335 	is_exploding = true;
336 	moving_time = 0;
337 }
338 
339 /************************************************************************
340  *									*
341  * Heavy								*
342  *									*
343  ************************************************************************/
Heavy(double x,double y)344 Heavy::Heavy( double x, double y )
345 :
346 	Shoot( x, y )
347 {
348 	verbose( "Initializing Heavy" );
349 
350 	weight = 2;
351 
352 	heavy_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/heavy.gif" );
353 	heavyback_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/heavyback.gif" );
354 	heavybackk_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/heavybackk.gif" );
355 
356 	width = heavy_sprite->getWidth();
357 }
358 
~Heavy()359 Heavy::~Heavy()
360 {
361 	verbose( "Deleting Heavy" );
362 
363 	delete heavy_sprite;
364 	delete heavyback_sprite;
365 	delete heavybackk_sprite;
366 }
367 
is_active() const368 bool Heavy::is_active() const
369 {
370 	return moving_time > 0;
371 }
372 
destroy()373 void Heavy::destroy()
374 {
375 	if ( is_active() )
376 		hit( this );
377 }
378 
move(Galaxy * galaxy)379 bool Heavy::move( Galaxy *galaxy )
380 {
381 	if ( is_active() ) {
382 
383 		if ( --moving_time == 0 ) return true;
384 
385 		Vector_2 my_shootPos = Vector_2( get_X(), get_Y(), K );
386 		Vector_2 my_shootVector = Vector_2( speed, direction, P );
387 		last_shootPos = my_shootPos;
388 
389 		galaxy->calculate_nextPos( my_shootPos, my_shootVector );
390 
391 		x = my_shootPos.getX();
392 		y = my_shootPos.getY();
393 		speed = my_shootVector.getLength();
394 		direction = my_shootVector.getAngle();
395 
396 		if ( galaxy->has_collision( this ) &&
397 			!is_active() )
398 
399 			return true;
400 	}
401 
402 	return false;
403 }
404 
has_Extra_collision(Extra * extra)405 bool Heavy::has_Extra_collision( Extra *extra )
406 {
407 	if ( extra->has_collision( (Spaceobject *)this ) )
408 		return true;
409 
410 	return false;
411 }
412 
check_collision(double x,double y,double width,bool spacing)413 bool Heavy::check_collision( double x, double y, double width, bool spacing )
414 {
415 	return check_sphere_collision( x, y, width, spacing );
416 }
417 
draw()418 void Heavy::draw()
419 {
420 	if ( is_active() ) {
421 
422 		if ( moving_time > 100 ||
423 			(int)RANDOM(100, 0) < ( moving_time/2 )+25 ) {
424 
425 			double x_anim = RANDOM(2, -2);
426 			double y_anim = RANDOM(2, -2);
427 			int alpha_anim = (int)RANDOM(10, -10);
428 
429 			Vector_2 v = last_shootPos-Vector_2( get_X(), get_Y(), K );
430 			Vector_2 v_1 = v * 0.5;
431 			Vector_2 v_2 = v * 0.2;
432 
433 			heavy_sprite->setAlpha( 200 + alpha_anim );
434 			heavy_sprite->setPos( (int)get_X(), (int)get_Y() );
435 			heavy_sprite->draw();
436 
437 			heavybackk_sprite->setAlpha( 100 + alpha_anim );
438 			heavybackk_sprite->setPos(
439 				(int)( get_X() + v_1.getX() ),
440 				(int)( get_Y() + v_1.getY() ) );
441 			heavybackk_sprite->draw();
442 
443 			heavyback_sprite->setAlpha( 130 + alpha_anim );
444 			heavyback_sprite->setPos(
445 				(int)( get_X() + v_2.getX() + x_anim ),
446 				(int)( get_Y() + v_2.getY() + y_anim ) );
447 			heavyback_sprite->draw();
448 		}
449 	}
450 
451 	if ( is_exploding ) {
452 		explosion->draw();
453 		is_exploding = explosion->is_active();
454 	}
455 }
456 
hit(Spaceobject * object)457 void Heavy::hit( Spaceobject *object )
458 {
459 	explosion->activate( x, y );
460 	is_exploding = true;
461 	moving_time = 0;
462 }
463 
464 /************************************************************************
465  *									*
466  * Cluster								*
467  *									*
468  ************************************************************************/
Cluster(double x,double y)469 Cluster::Cluster( double x, double y )
470 :
471 	Shoot( x, y ),
472 	lasers_active(false)
473 {
474 	verbose( "Initializing Cluster" );
475 
476 	reset();
477 
478 	cluster_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/shoot.gif" );
479 	clusterback_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/shootback.gif" );
480 	clusterbackk_sprite = new Sprite( (char*)"/usr/local/share/highmoon/gfx/shootbackk.gif" );
481 
482 	width = cluster_sprite->getWidth();
483 
484 	lasers = new Laser[MAXCLUSTERLASER];
485 }
486 
~Cluster()487 Cluster::~Cluster()
488 {
489 	verbose( "Deleting Cluster" );
490 
491 	delete cluster_sprite;
492 	delete clusterback_sprite;
493 	delete clusterbackk_sprite;
494 	delete[] lasers;
495 }
496 
is_active() const497 bool Cluster::is_active() const
498 {
499 	for ( int i=0; i < MAXCLUSTERLASER; i++ )
500 		if ( lasers[i].is_active() )
501 			return true;
502 
503 	return moving_time>0;
504 }
505 
reset()506 void Cluster::reset()
507 {
508 	destroyed = false;
509 	laser_hits = 0;
510 	moving_time = 0;
511 }
512 
destroy()513 void Cluster::destroy()
514 {
515 
516 	for ( int i=0; i < MAXCLUSTERLASER; i++ )
517 		if ( lasers[i].is_active() )
518 			lasers[i].hit(this);
519 
520 	if ( is_active() ) {
521 		destroyed = true;
522 		hit( this );
523 	}
524 }
525 
move(Galaxy * galaxy)526 bool Cluster::move( Galaxy *galaxy )
527 {
528 	if ( destroyed )
529 		return true;
530 
531 	if ( moving_time > 0 ) {
532 
533 		if ( --moving_time == 0 )
534 			return true;
535 
536 		else {
537 			Vector_2 my_shootPos = Vector_2( get_X(), get_Y(), K );
538 			Vector_2 my_shootVector = Vector_2( speed, direction, P );
539 			last_shootPos = my_shootPos;
540 
541 			galaxy->calculate_nextPos( my_shootPos, my_shootVector );
542 
543 			x = my_shootPos.getX();
544 			y = my_shootPos.getY();
545 			speed = my_shootVector.getLength();
546 			direction = my_shootVector.getAngle();
547 
548 			if ( galaxy->has_collision( this ) ) {
549 				laser_hits++;
550 			}
551 		}
552 	}
553 
554 	for ( int i=0; i < MAXCLUSTERLASER; i++ )
555 		if ( lasers[i].is_active() && lasers[i].move( galaxy ) )
556 			laser_hits++;
557 
558 	if ( laser_hits >= MAXCLUSTERLASER + 1 )
559 		return true;
560 
561 	return false;
562 }
563 
has_Extra_collision(Extra * extra)564 bool Cluster::has_Extra_collision( Extra *extra )
565 {
566 	if ( is_active() && extra->has_collision( (Spaceobject *)this ) )
567 		return true;
568 
569 	for ( int i=0; i < MAXCLUSTERLASER; i++ ) {
570 		if ( lasers[i].is_active() && extra->has_collision( (Spaceobject *)&lasers[i] ) )
571 			return true;
572 	}
573 
574 	return false;
575 }
576 
check_collision(double x,double y,double width,bool spacing)577 bool Cluster::check_collision( double x, double y, double width, bool spacing )
578 {
579 	for ( int i=0; i < MAXCLUSTERLASER; i++ )
580 		if ( lasers[i].check_collision( x, y, width, spacing ) )
581 			return true;
582 
583 	return check_sphere_collision( x, y, width, spacing );
584 }
585 
draw()586 void Cluster::draw()
587 {
588 	if ( moving_time > 0 ) {
589 
590 		if ( moving_time > 100 ||
591 			(int)RANDOM(100, 0) < ( moving_time/2 )+25 ) {
592 
593 			double x_anim = RANDOM(2, -2);
594 			double y_anim = RANDOM(2, -2);
595 			int alpha_anim = (int)RANDOM(10, -10);
596 
597 			Vector_2 v = last_shootPos-Vector_2( get_X(), get_Y(), K );
598 			Vector_2 v_1 = v * 0.5;
599 			Vector_2 v_2 = v * 0.2;
600 
601 			clusterbackk_sprite->setAlpha( 120 + alpha_anim );
602 			clusterbackk_sprite->setPos(
603 				(int)( get_X() + v_1.getX() ),
604 				(int)( get_Y() + v_1.getY() ) );
605 			clusterbackk_sprite->draw();
606 
607 			clusterback_sprite->setAlpha( 140 + alpha_anim );
608 			clusterback_sprite->setPos(
609 				(int)( get_X() + v_2.getX() + x_anim ),
610 				(int)( get_Y() + v_2.getY() + y_anim ) );
611 			clusterback_sprite->draw();
612 
613 			cluster_sprite->setAlpha( 200 + alpha_anim );
614 			cluster_sprite->setPos( (int)get_X(), (int)get_Y() );
615 			cluster_sprite->draw();
616 		}
617 	}
618 
619 	for (int i=0; i < MAXCLUSTERLASER; i++)
620 		lasers[i].draw();
621 
622 	if ( is_exploding ) {
623 		explosion->draw();
624 		is_exploding = explosion->is_active();
625 	}
626 }
627 
hit(Spaceobject * object)628 void Cluster::hit( Spaceobject *object )
629 {
630 	if ( moving_time > 0 ) {
631 
632 		explosion->activate( x, y );
633 		is_exploding = true;
634 		moving_time = 0;
635 
636 		if ( !destroyed ) {
637 			Vector_2 v_hit_vector = Vector_2( get_X(), get_Y(), K )-Vector_2( object->get_X(), object->get_Y(), K );
638 			Vector_2 v_hit_start = Vector_2( get_X(), get_Y(), K );
639 			double dir = v_hit_vector.getAngle()-( CLUSTERLASERANGLE*MAXCLUSTERLASER/2 ) * PI / 180;
640 
641 			for ( int i=0; i < MAXCLUSTERLASER; i++ ) {
642 				Vector_2 v_direction = Vector_2( get_Speed() / 5 * 3, dir, P );
643 				Vector_2 v_start = v_hit_start + Vector_2( 10, dir, P );
644 				lasers[i].activate( v_start, v_direction );
645 				dir += CLUSTERLASERANGLE*PI/180;
646 			}
647 		}
648 	}
649 }
650