1 /***************************************************************************
2     copyright            : (C) 2003 by Michael Speck
3     email                : http://lgames.sf.net
4  ***************************************************************************/
5 
6 /***************************************************************************
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  ***************************************************************************/
14 
15 #include "defs.h"
16 #include "shots.h"
17 #include "units.h"
18 #include "bfield.h"
19 
20 extern SDL_Surface *img_trees, *background, *img_ground, *img_small_crater, *img_crater, *img_gun;
21 extern void vec2xy( Vector *v, int *sx, int *sy ); /* from shots.c */
22 extern SDL_Sound *wav_expl1, *wav_expl2, *wav_cannon1, *wav_cannon2;
23 extern SDL_Cursor *cr_reload, *cr_hourglass, *cr_crosshair;
24 extern int player_ammo, player_score;
25 extern int strip_blocked[STRIP_COUNT];
26 extern int audio_on;
27 extern int cursor_x_offset, cursor_w;
28 extern int gun_w, gun_h;
29 
30 BField bfield;
31 
32 extern void vec2xy( Vector *v, int *sx, int *sy );
33 
34 /* fire positions relative to the gun base point for each gun sprite */
35 Vector fire_offsets[9] = {
36 	{2, -6, 28},
37 	{5, -5, 30},
38 	{7, -3, 30},
39 	{10,-3, 34},
40 	{11, 0, 36},
41 	{10, 1, 34},
42 	{8,  2, 36},
43 	{7,  3, 34},
44 	{3,  4, 34}
45 };
46 
set_tree(Tree * tree,int x,int y,int dead)47 static void set_tree( Tree *tree, int x, int y, int dead )
48 {
49 	tree->img = img_trees;
50 	tree->img_x_offset = 0;
51 	tree->img_y_offset = dead*23;
52 
53 	tree->w = 38; tree->h = 23;
54 	tree->x = x; tree->y = y;
55 	tree->hx = 17; tree->hy = 8; /* hotspot: center for impacts */
56 	tree->burn_time = 0;
57 	tree->dead = dead;
58 }
59 
bfield_init(int type,Vector * gun,Vector * gun_img_offset)60 void bfield_init( int type, Vector *gun, Vector *gun_img_offset )
61 {
62 	int tree_strips[] = { 1, 7, 11, 17, -1 };
63 	int i;
64 
65 	memset( &bfield, 0, sizeof( bfield ) );
66 	bfield.gun = *gun; bfield.gun_img_offset = *gun_img_offset;
67 	bfield.img_gun_x_offset = 4 * gun_w;
68 	bfield.demo = (type==BFIELD_DEMO);
69 	memset( strip_blocked, 0, sizeof( strip_blocked ) );
70 
71 	/* basic ground */
72 	SDL_BlitSurface( img_ground, 0, background, 0 );
73 
74 	/* add burned trees to some strips and mark them unuseable */
75 	i = 0;
76 	while ( tree_strips[i] != -1 ) {
77 		strip_blocked[tree_strips[i]] = 1;
78 		set_tree( &bfield.trees[bfield.tree_count++],
79 			STRIP_OFFSET +
80 			tree_strips[i]*STRIP_SIZE +
81 			RAND(0,STRIP_SIZE-17) - 10 /* shadow must be in strip */,
82 			rand() % 230, 1 );
83 		set_tree( &bfield.trees[bfield.tree_count++],
84 			STRIP_OFFSET +
85 			tree_strips[i]*STRIP_SIZE +
86 			RAND(0,STRIP_SIZE-17) - 10 /*shadow must be in strip */,
87 			230 + rand() % 230, 1 );
88 		i++;
89 	}
90 	/* add normal trees to gun strip */
91 	for ( i = 0; i < 4; i++ )
92 		set_tree( &bfield.trees[bfield.tree_count++],
93 			RAND(0, STRIP_OFFSET-38), RAND(i*50, (i+1)*50-20), 0 );
94 	for ( i = 0; i < 4; i++ )
95 		set_tree( &bfield.trees[bfield.tree_count++],
96 			RAND(0, STRIP_OFFSET-38), RAND(280+i*50, 280+(i+1)*50-20), 0 );
97 
98 	/* set the ranges */
99 	SET_RANGE( bfield.delay_ranges[UT_SOLDIER], 2000, 4000 );
100 	SET_RANGE( bfield.delay_ranges[UT_RECON],   2000, 4000 );
101 	SET_RANGE( bfield.delay_ranges[UT_TANK],    2000, 5000 );
102 	SET_RANGE( bfield.group_sizes[UT_SOLDIER], 2, 4 );
103 	SET_RANGE( bfield.group_sizes[UT_TANK],    1, 1 );
104 	SET_RANGE( bfield.group_sizes[UT_RECON],   1, 1 );
105 
106 	/* use bfield_impact_handler for exploding projectiles */
107 	shots_set_impact_handler( bfield_handle_impact );
108 }
109 
bfield_finalize()110 void bfield_finalize()
111 {
112 	units_delete();
113 	shots_delete();
114 	particles_clear();
115 
116 }
117 
bfield_add_units(int ms)118 static void bfield_add_units( int ms )
119 {
120 	int num, x, y, dir, i, strip;
121 
122 	/* decrease delays */
123 	for ( i = 0; i < UT_LAST; i++ )
124 		if ( (bfield.delays[i] -= ms) < 0 )
125 			bfield.delays[i] = 0;
126 
127 	/* check wether new units appear. as soldiers may interfere
128 	 * they use only even id'd strips to prevent them from blocking
129 	 * all strips. */
130 	if ( bfield.delays[UT_SOLDIER] == 0 ) {
131 		bfield.delays[UT_SOLDIER] = RAND_IN_RANGE( bfield.delay_ranges[UT_SOLDIER] );
132 		strip = units_get_rand_strip( UT_SOLDIER ); /* works always */
133 		dir = rand() % 2;
134 		num = RAND_IN_RANGE( bfield.group_sizes[UT_SOLDIER] );
135 		for ( i = 0, y=0; i < num; i++ ) {
136 			x = RAND(0,STRIP_SIZE-12);
137 			units_add_soldier( strip, x, ((dir)?480:-50)+y, dir );
138 			y += RAND(10,20);
139 		}
140 	}
141 	if ( bfield.delays[UT_TANK] == 0 ) {
142 		strip = units_get_rand_strip( UT_TANK );
143 		if ( strip != -1 ) {
144 			bfield.delays[UT_TANK] = RAND_IN_RANGE( bfield.delay_ranges[UT_TANK] );
145 			x = RAND(0,STRIP_SIZE-13);
146 			y = RAND(480,530);
147 			units_add_dummy_tank( strip, x, y, RAND(30,40) );
148 		}
149 	}
150 	if ( bfield.delays[UT_RECON] == 0 ) {
151 		strip = units_get_rand_strip( UT_RECON );
152 		if ( strip != -1 ) {
153 			bfield.delays[UT_RECON] = RAND_IN_RANGE( bfield.delay_ranges[UT_RECON] );
154 			x = RAND(0,STRIP_SIZE-12);
155 			y = RAND(-50,-20);
156 			units_add_dummy_recon( strip, x, y, RAND(40,50) );
157 		}
158 	}
159 }
160 
161 /* let gun look towards mouse position */
bfield_draw_gun(SDL_Surface * dest,int camera_x,int camera_y)162 static void bfield_draw_gun( SDL_Surface *dest, int camera_x, int camera_y )
163 {
164 	SDL_Rect drect, srect;
165 
166 	srect.w = drect.w = gun_w; srect.h = drect.h = gun_h;
167 	srect.x = bfield.img_gun_x_offset; srect.y = 0;
168 	drect.x = bfield.gun.x + bfield.gun_img_offset.x;
169 	drect.y = bfield.gun.y + bfield.gun_img_offset.y;
170 	SDL_BlitSurface( img_gun, &srect, dest, &drect );
171 }
172 
bfield_draw(SDL_Surface * dest,int camera_x,int camera_y)173 void bfield_draw( SDL_Surface *dest, int camera_x, int camera_y )
174 {
175 	Tree *tree;
176 	int i;
177 	SDL_Rect drect, srect;
178 
179 	SDL_BlitSurface( background, 0, dest, 0 );
180 	bfield_draw_gun( dest, camera_x, camera_y );
181 
182 	units_draw( dest, 0, 0 );
183 
184 	bfield_draw_gun( dest, camera_x, camera_y );
185 
186 	for ( i = 0; i < bfield.tree_count; i++ ) {
187 		tree = &bfield.trees[i];
188 		drect.x = tree->x; drect.y = tree->y;
189 		srect.w = drect.w = tree->w;
190 		srect.h = drect.h = tree->h;
191 		srect.x = tree->img_x_offset;
192 		srect.y = tree->img_y_offset;
193 		SDL_BlitSurface( tree->img, &srect, dest, &drect );
194 	}
195 
196 	particles_draw( dest, 0, 0 );
197 	shots_draw( dest, 0, 0 );
198 }
199 
bfield_update(int ms)200 void bfield_update( int ms )
201 {
202 	int i;
203 	Vector dest;
204 	Unit *unit;
205 
206 	/* add units */
207 	bfield_add_units( ms );
208 
209 	/* move things */
210 	units_update(ms);
211 	shots_update(ms);
212 	particles_update(ms);
213 
214 	/* check wether gun may fire */
215 	if ( bfield.gun_delay > 0 )
216 	if ( (bfield.gun_delay-=ms) <= 0 ) {
217 		bfield.gun_delay = 0;
218 		if ( !bfield.demo )
219 			SET_CURSOR( CURSOR_CROSSHAIR );
220 	}
221 
222 	/* fire cannon automatically in demo mode */
223 	if ( bfield.demo && bfield.gun_delay == 0 && (bfield.demo_delay-=ms) <= 0 ) {
224 		/* soldiers or tanks? */
225 		if ( bfield.demo_hunt_soldiers == 0 )
226 		if ( (rand() % 3) == 0  )
227 			bfield.demo_hunt_soldiers = 4;
228 		/* select target */
229 		if ( bfield.demo_hunt_soldiers > 0 ) {
230 			unit = units_get_first_soldier();
231 			if ( unit ) {
232 				bfield.demo_hunt_soldiers--;
233 				if ( bfield.demo_hunt_soldiers == 0 )
234 					bfield.demo_delay = 2500; /* take a short break */
235 			}
236 		}
237 		else
238 			unit = units_get_first_vehicle();
239 		/* fire! */
240 		if ( unit ) {
241 			if ( unit->type != UT_SOLDIER )
242 				bfield.demo_delay = 2500;
243 			dest.x = unit->pos.x + unit->hx + RAND(-10,10);
244 			dest.y = unit->pos.y + unit->hy + RAND(-20,20);
245 			if ( unit->type != UT_SOLDIER )
246 				dest.y += ((unit->dir.y<0)?-30:40);
247 			dest.z = 0;
248 			bfield_update_gun_dir( (int)dest.x, (int)dest.y );
249 			bfield_fire_gun( (unit->type==UT_SOLDIER)?ST_GRENADE:ST_BOMB, &dest, 60, 0 );
250 		}
251 	}
252 
253 	/* check burning trees */
254 	for ( i = 0; i < bfield.tree_count; i++ )
255 		if ( bfield.trees[i].burn_time > 0 )
256 		if ( (bfield.trees[i].burn_time-=ms) <=0 ) {
257 			bfield.trees[i].burn_time = 0;
258 			bfield.trees[i].dead = 1;
259 			bfield.trees[i].img_y_offset += bfield.trees[i].h;
260 		}
261 }
262 
bfield_gun_is_ready()263 int bfield_gun_is_ready()
264 {
265 	return (bfield.gun_delay==0);
266 }
267 
bfield_fire_gun(int type,Vector * dest,double alpha,double power)268 void bfield_fire_gun( int type /* ST_... */, Vector *dest, double alpha, double power )
269 {
270 	int sx, sy;
271 	Vector pos;
272 
273 	/* must not be too near to gun */
274 	if ( dest->x < STRIP_OFFSET )
275 		return;
276 
277 	pos = bfield.gun;
278 	pos.x += fire_offsets[bfield.gun_dir].x;
279 	pos.y += fire_offsets[bfield.gun_dir].y;
280 	pos.z += fire_offsets[bfield.gun_dir].z;
281 	switch ( type ) {
282 		case ST_GRENADE:
283 			if ( player_ammo >= AMMO_GRENADE || bfield.demo ) {
284 				SET_DELAY(bfield.gun_delay,GRENADE_DELAY);
285 				shots_fire_grenade( &pos, dest, alpha, power );
286 				SDL_PlaySound( wav_cannon1 );
287 				if ( !bfield.demo )
288 					player_ammo -= AMMO_GRENADE;
289 				vec2xy( &pos, &sx, &sy );
290 				particles_set_muzzle_fire( sx, sy, 10 );
291 			}
292 			else
293 				if ( !bfield.demo )
294 					SET_CURSOR( CURSOR_RELOAD );
295 			break;
296 		case ST_BOMB:
297 			if ( player_ammo >= AMMO_BOMB || bfield.demo ) {
298 				SET_DELAY(bfield.gun_delay,BOMB_DELAY);
299 				shots_fire_bomb( &pos, dest, alpha, power );
300 				SDL_PlaySound( wav_cannon2 );
301 				if ( !bfield.demo ) {
302 					player_ammo -= AMMO_BOMB;
303 					SET_CURSOR( CURSOR_WAIT );
304 				}
305 				vec2xy( &pos, &sx, &sy );
306 				particles_set_muzzle_fire( sx, sy, 30 );
307 			}
308 			else
309 				if ( !bfield.demo )
310 					SET_CURSOR( CURSOR_RELOAD );
311 			break;
312 		case ST_CLUSTER:
313 			if ( player_ammo >= AMMO_CLUSTER || bfield.demo ) {
314 				SET_DELAY(bfield.gun_delay,CLUSTER_DELAY);
315 				dest->z = 50; /* detonate above target */
316 				SDL_PlaySound( wav_cannon2 );
317 				shots_fire_cluster( &pos, dest, 70, 0 );
318 				if ( !bfield.demo ) {
319 					player_ammo -= AMMO_CLUSTER;
320 					SET_CURSOR( CURSOR_WAIT );
321 				}
322 			}
323 			else
324 				if ( !bfield.demo )
325 					SET_CURSOR( CURSOR_RELOAD );
326 
327 			break;
328 		case ST_NAPALM:
329 			if ( player_ammo >= AMMO_NAPALM || bfield.demo ) {
330 				SET_DELAY(bfield.gun_delay,NAPALM_DELAY);
331 				SDL_PlaySound( wav_cannon2 );
332 				shots_fire_napalm( &pos, dest, 70, 0 );
333 				if ( !bfield.demo ) {
334 					player_ammo -= AMMO_BOMB;
335 					SET_CURSOR( CURSOR_WAIT );
336 				}
337 			}
338 			else
339 				if ( !bfield.demo )
340 					SET_CURSOR( CURSOR_RELOAD );
341 			break;
342 	}
343 }
344 
bfield_reload_gun()345 void bfield_reload_gun()
346 {
347 	if ( player_ammo < MAX_AMMO ) {
348 		player_ammo = MAX_AMMO;
349 		player_score += SCORE_RELOAD;
350 		if ( bfield.gun_delay == 0 )
351 			SET_CURSOR( CURSOR_CROSSHAIR );
352 	}
353 }
354 
bfield_handle_impact(Shot * shot)355 void bfield_handle_impact( Shot *shot )
356 {
357 	SDL_Rect drect;
358 	Vector dest;
359 	int i;
360 	int sx, sy;
361 
362 	if ( shot->is_precise )
363 		shot->pos = shot->dest; /* impact at exact position */
364 	vec2xy( &shot->pos, &sx, &sy ); /* get screen position */
365 
366 	switch ( shot->type ) {
367 	    case ST_GRENADE:
368 		particles_explode_grenade( sx, sy );
369 		drect.x = sx - (img_small_crater->w>>1);
370 		drect.y = sy - (img_small_crater->h>>1);
371 		drect.w = img_small_crater->w; drect.h = img_small_crater->h;
372 		SDL_BlitSurface( img_small_crater, 0, background, &drect );
373 
374 		SDL_PlaySound( wav_expl1 );
375 
376 		units_check_impact( sx, sy, 20 );
377 		break;
378 	    case ST_BOMB:
379 		particles_explode_bomb( sx, sy );
380 		drect.x = sx - (img_crater->w>>1);
381 		drect.y = sy - (img_crater->h>>1);
382 		drect.w = img_crater->w; drect.h = img_crater->h;
383 		SDL_BlitSurface( img_crater, 0, background, &drect );
384 
385 		SDL_PlaySound( wav_expl2 );
386 
387 		units_check_impact( sx, sy, 40 );
388 		break;
389 	    case ST_CLUSTER:
390 		particles_explode_clusterbomb( sx, sy );
391 		for ( i = 0; i < 16; i++ ) {
392 			dest.x = shot->dest.x + RAND(-50,50);
393 			dest.y = shot->dest.y + RAND(-50,50);
394 			dest.z = 0;
395 			shots_fire_grenade(
396 					&shot->pos, &dest,
397 					RAND(-40,0), 0.06 + (double)RAND(0, 20)/100 );
398 		}
399 		break;
400 	    case ST_NAPALM:
401 		for ( i = 0; i < 10; i++ ) {
402 			dest.x = shot->dest.x + RAND(-50,50);
403 			dest.y = shot->dest.y + RAND(-50,50);
404 			dest.z = 0;
405 			shots_fire_oil(
406 					&shot->pos, &dest,
407 					RAND(20,60), 0.01 + (double)RAND(0, 20)/100 );
408 
409 			units_check_impact( sx, sy, 40 );
410 		}
411 		break;
412 	    case ST_OIL:
413 		particles_add_emitter( PT_FIRE, sx, sy, 5, 0.1, 6000 );
414 		break;
415 	}
416 }
417 
bfield_update_gun_dir(int mx,int my)418 void bfield_update_gun_dir( int mx, int my )
419 {
420 	double alpha;
421 
422 	if ( mx <= bfield.gun.x ) {
423 		if ( my <= bfield.gun.y )
424 			bfield.gun_dir = 0;
425 		else
426 			bfield.gun_dir = 8;
427 	}
428 	else {
429 		alpha = atan( (my - bfield.gun.y) / (mx - bfield.gun.x ) );
430 		alpha = alpha * 180 / 3.14;
431 		bfield.gun_dir = ((alpha + 10) / 20) + 4;
432 	}
433 
434 	bfield.img_gun_x_offset = gun_w * bfield.gun_dir;
435 }
436