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