/*************************************************************************** copyright : (C) 2003 by Michael Speck email : http://lgames.sf.net ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "defs.h" #include "shots.h" #include "units.h" #include "bfield.h" extern SDL_Surface *img_trees, *background, *img_ground, *img_small_crater, *img_crater, *img_gun; extern void vec2xy( Vector *v, int *sx, int *sy ); /* from shots.c */ extern SDL_Sound *wav_expl1, *wav_expl2, *wav_cannon1, *wav_cannon2; extern SDL_Cursor *cr_reload, *cr_hourglass, *cr_crosshair; extern int player_ammo, player_score; extern int strip_blocked[STRIP_COUNT]; extern int audio_on; extern int cursor_x_offset, cursor_w; extern int gun_w, gun_h; BField bfield; extern void vec2xy( Vector *v, int *sx, int *sy ); /* fire positions relative to the gun base point for each gun sprite */ Vector fire_offsets[9] = { {2, -6, 28}, {5, -5, 30}, {7, -3, 30}, {10,-3, 34}, {11, 0, 36}, {10, 1, 34}, {8, 2, 36}, {7, 3, 34}, {3, 4, 34} }; static void set_tree( Tree *tree, int x, int y, int dead ) { tree->img = img_trees; tree->img_x_offset = 0; tree->img_y_offset = dead*23; tree->w = 38; tree->h = 23; tree->x = x; tree->y = y; tree->hx = 17; tree->hy = 8; /* hotspot: center for impacts */ tree->burn_time = 0; tree->dead = dead; } void bfield_init( int type, Vector *gun, Vector *gun_img_offset ) { int tree_strips[] = { 1, 7, 11, 17, -1 }; int i; memset( &bfield, 0, sizeof( bfield ) ); bfield.gun = *gun; bfield.gun_img_offset = *gun_img_offset; bfield.img_gun_x_offset = 4 * gun_w; bfield.demo = (type==BFIELD_DEMO); memset( strip_blocked, 0, sizeof( strip_blocked ) ); /* basic ground */ SDL_BlitSurface( img_ground, 0, background, 0 ); /* add burned trees to some strips and mark them unuseable */ i = 0; while ( tree_strips[i] != -1 ) { strip_blocked[tree_strips[i]] = 1; set_tree( &bfield.trees[bfield.tree_count++], STRIP_OFFSET + tree_strips[i]*STRIP_SIZE + RAND(0,STRIP_SIZE-17) - 10 /* shadow must be in strip */, rand() % 230, 1 ); set_tree( &bfield.trees[bfield.tree_count++], STRIP_OFFSET + tree_strips[i]*STRIP_SIZE + RAND(0,STRIP_SIZE-17) - 10 /*shadow must be in strip */, 230 + rand() % 230, 1 ); i++; } /* add normal trees to gun strip */ for ( i = 0; i < 4; i++ ) set_tree( &bfield.trees[bfield.tree_count++], RAND(0, STRIP_OFFSET-38), RAND(i*50, (i+1)*50-20), 0 ); for ( i = 0; i < 4; i++ ) set_tree( &bfield.trees[bfield.tree_count++], RAND(0, STRIP_OFFSET-38), RAND(280+i*50, 280+(i+1)*50-20), 0 ); /* set the ranges */ SET_RANGE( bfield.delay_ranges[UT_SOLDIER], 2000, 4000 ); SET_RANGE( bfield.delay_ranges[UT_RECON], 2000, 4000 ); SET_RANGE( bfield.delay_ranges[UT_TANK], 2000, 5000 ); SET_RANGE( bfield.group_sizes[UT_SOLDIER], 2, 4 ); SET_RANGE( bfield.group_sizes[UT_TANK], 1, 1 ); SET_RANGE( bfield.group_sizes[UT_RECON], 1, 1 ); /* use bfield_impact_handler for exploding projectiles */ shots_set_impact_handler( bfield_handle_impact ); } void bfield_finalize() { units_delete(); shots_delete(); particles_clear(); } static void bfield_add_units( int ms ) { int num, x, y, dir, i, strip; /* decrease delays */ for ( i = 0; i < UT_LAST; i++ ) if ( (bfield.delays[i] -= ms) < 0 ) bfield.delays[i] = 0; /* check wether new units appear. as soldiers may interfere * they use only even id'd strips to prevent them from blocking * all strips. */ if ( bfield.delays[UT_SOLDIER] == 0 ) { bfield.delays[UT_SOLDIER] = RAND_IN_RANGE( bfield.delay_ranges[UT_SOLDIER] ); strip = units_get_rand_strip( UT_SOLDIER ); /* works always */ dir = rand() % 2; num = RAND_IN_RANGE( bfield.group_sizes[UT_SOLDIER] ); for ( i = 0, y=0; i < num; i++ ) { x = RAND(0,STRIP_SIZE-12); units_add_soldier( strip, x, ((dir)?480:-50)+y, dir ); y += RAND(10,20); } } if ( bfield.delays[UT_TANK] == 0 ) { strip = units_get_rand_strip( UT_TANK ); if ( strip != -1 ) { bfield.delays[UT_TANK] = RAND_IN_RANGE( bfield.delay_ranges[UT_TANK] ); x = RAND(0,STRIP_SIZE-13); y = RAND(480,530); units_add_dummy_tank( strip, x, y, RAND(30,40) ); } } if ( bfield.delays[UT_RECON] == 0 ) { strip = units_get_rand_strip( UT_RECON ); if ( strip != -1 ) { bfield.delays[UT_RECON] = RAND_IN_RANGE( bfield.delay_ranges[UT_RECON] ); x = RAND(0,STRIP_SIZE-12); y = RAND(-50,-20); units_add_dummy_recon( strip, x, y, RAND(40,50) ); } } } /* let gun look towards mouse position */ static void bfield_draw_gun( SDL_Surface *dest, int camera_x, int camera_y ) { SDL_Rect drect, srect; srect.w = drect.w = gun_w; srect.h = drect.h = gun_h; srect.x = bfield.img_gun_x_offset; srect.y = 0; drect.x = bfield.gun.x + bfield.gun_img_offset.x; drect.y = bfield.gun.y + bfield.gun_img_offset.y; SDL_BlitSurface( img_gun, &srect, dest, &drect ); } void bfield_draw( SDL_Surface *dest, int camera_x, int camera_y ) { Tree *tree; int i; SDL_Rect drect, srect; SDL_BlitSurface( background, 0, dest, 0 ); bfield_draw_gun( dest, camera_x, camera_y ); units_draw( dest, 0, 0 ); bfield_draw_gun( dest, camera_x, camera_y ); for ( i = 0; i < bfield.tree_count; i++ ) { tree = &bfield.trees[i]; drect.x = tree->x; drect.y = tree->y; srect.w = drect.w = tree->w; srect.h = drect.h = tree->h; srect.x = tree->img_x_offset; srect.y = tree->img_y_offset; SDL_BlitSurface( tree->img, &srect, dest, &drect ); } particles_draw( dest, 0, 0 ); shots_draw( dest, 0, 0 ); } void bfield_update( int ms ) { int i; Vector dest; Unit *unit; /* add units */ bfield_add_units( ms ); /* move things */ units_update(ms); shots_update(ms); particles_update(ms); /* check wether gun may fire */ if ( bfield.gun_delay > 0 ) if ( (bfield.gun_delay-=ms) <= 0 ) { bfield.gun_delay = 0; if ( !bfield.demo ) SET_CURSOR( CURSOR_CROSSHAIR ); } /* fire cannon automatically in demo mode */ if ( bfield.demo && bfield.gun_delay == 0 && (bfield.demo_delay-=ms) <= 0 ) { /* soldiers or tanks? */ if ( bfield.demo_hunt_soldiers == 0 ) if ( (rand() % 3) == 0 ) bfield.demo_hunt_soldiers = 4; /* select target */ if ( bfield.demo_hunt_soldiers > 0 ) { unit = units_get_first_soldier(); if ( unit ) { bfield.demo_hunt_soldiers--; if ( bfield.demo_hunt_soldiers == 0 ) bfield.demo_delay = 2500; /* take a short break */ } } else unit = units_get_first_vehicle(); /* fire! */ if ( unit ) { if ( unit->type != UT_SOLDIER ) bfield.demo_delay = 2500; dest.x = unit->pos.x + unit->hx + RAND(-10,10); dest.y = unit->pos.y + unit->hy + RAND(-20,20); if ( unit->type != UT_SOLDIER ) dest.y += ((unit->dir.y<0)?-30:40); dest.z = 0; bfield_update_gun_dir( (int)dest.x, (int)dest.y ); bfield_fire_gun( (unit->type==UT_SOLDIER)?ST_GRENADE:ST_BOMB, &dest, 60, 0 ); } } /* check burning trees */ for ( i = 0; i < bfield.tree_count; i++ ) if ( bfield.trees[i].burn_time > 0 ) if ( (bfield.trees[i].burn_time-=ms) <=0 ) { bfield.trees[i].burn_time = 0; bfield.trees[i].dead = 1; bfield.trees[i].img_y_offset += bfield.trees[i].h; } } int bfield_gun_is_ready() { return (bfield.gun_delay==0); } void bfield_fire_gun( int type /* ST_... */, Vector *dest, double alpha, double power ) { int sx, sy; Vector pos; /* must not be too near to gun */ if ( dest->x < 70 && abs(dest->y-bfield.gun.y) < 50 ) return; pos = bfield.gun; pos.x += fire_offsets[bfield.gun_dir].x; pos.y += fire_offsets[bfield.gun_dir].y; pos.z += fire_offsets[bfield.gun_dir].z; switch ( type ) { case ST_GRENADE: if ( player_ammo >= AMMO_GRENADE || bfield.demo ) { SET_DELAY(bfield.gun_delay,GRENADE_DELAY); shots_fire_grenade( &pos, dest, alpha, power ); SDL_PlaySound( wav_cannon1 ); if ( !bfield.demo ) player_ammo -= AMMO_GRENADE; vec2xy( &pos, &sx, &sy ); particles_set_muzzle_fire( sx, sy, 10 ); } else if ( !bfield.demo ) SET_CURSOR( CURSOR_RELOAD ); break; case ST_BOMB: if ( player_ammo >= AMMO_BOMB || bfield.demo ) { SET_DELAY(bfield.gun_delay,BOMB_DELAY); shots_fire_bomb( &pos, dest, alpha, power ); SDL_PlaySound( wav_cannon2 ); if ( !bfield.demo ) { player_ammo -= AMMO_BOMB; SET_CURSOR( CURSOR_WAIT ); } vec2xy( &pos, &sx, &sy ); particles_set_muzzle_fire( sx, sy, 30 ); } else if ( !bfield.demo ) SET_CURSOR( CURSOR_RELOAD ); break; case ST_CLUSTER: if ( player_ammo >= AMMO_CLUSTER || bfield.demo ) { SET_DELAY(bfield.gun_delay,CLUSTER_DELAY); dest->z = 50; /* detonate above target */ SDL_PlaySound( wav_cannon2 ); shots_fire_cluster( &pos, dest, 70, 0 ); if ( !bfield.demo ) { player_ammo -= AMMO_CLUSTER; SET_CURSOR( CURSOR_WAIT ); } } else if ( !bfield.demo ) SET_CURSOR( CURSOR_RELOAD ); break; case ST_NAPALM: if ( player_ammo >= AMMO_NAPALM || bfield.demo ) { SET_DELAY(bfield.gun_delay,NAPALM_DELAY); SDL_PlaySound( wav_cannon2 ); shots_fire_napalm( &pos, dest, 70, 0 ); if ( !bfield.demo ) { player_ammo -= AMMO_BOMB; SET_CURSOR( CURSOR_WAIT ); } } else if ( !bfield.demo ) SET_CURSOR( CURSOR_RELOAD ); break; } } void bfield_reload_gun() { if ( player_ammo < MAX_AMMO ) { player_ammo = MAX_AMMO; player_score += SCORE_RELOAD; if ( bfield.gun_delay == 0 ) SET_CURSOR( CURSOR_CROSSHAIR ); } } static void trees_check_impact( int x, int y, int radius ) { Tree *tree; int i; for ( i = 0; i < bfield.tree_count; i++ ) { tree = &bfield.trees[i]; if ( tree->dead ) continue; if ( DIST( tree->x+tree->hx, tree->y+tree->hy, x, y ) < radius ) { /* it burns! */ particles_add_emitter( PT_FIRE, tree->x+tree->hx, tree->y+tree->hy, 5, 0.1, 10000 ); tree->burn_time = 10000; } } } void bfield_handle_impact( Shot *shot ) { SDL_Rect drect; Vector dest; int i; int sx, sy; if ( shot->is_precise ) shot->pos = shot->dest; /* impact at exact position */ vec2xy( &shot->pos, &sx, &sy ); /* get screen position */ switch ( shot->type ) { case ST_GRENADE: particles_explode_grenade( sx, sy ); drect.x = sx - (img_small_crater->w>>1); drect.y = sy - (img_small_crater->h>>1); drect.w = img_small_crater->w; drect.h = img_small_crater->h; SDL_BlitSurface( img_small_crater, 0, background, &drect ); SDL_PlaySound( wav_expl1 ); units_check_impact( sx, sy, 20 ); trees_check_impact( sx, sy, 20 ); break; case ST_BOMB: particles_explode_bomb( sx, sy ); drect.x = sx - (img_crater->w>>1); drect.y = sy - (img_crater->h>>1); drect.w = img_crater->w; drect.h = img_crater->h; SDL_BlitSurface( img_crater, 0, background, &drect ); SDL_PlaySound( wav_expl2 ); units_check_impact( sx, sy, 40 ); trees_check_impact( sx, sy, 30 ); break; case ST_CLUSTER: particles_explode_clusterbomb( sx, sy ); for ( i = 0; i < 16; i++ ) { dest.x = shot->dest.x + RAND(-50,50); dest.y = shot->dest.y + RAND(-50,50); dest.z = 0; shots_fire_grenade( &shot->pos, &dest, RAND(-40,0), 0.06 + (double)RAND(0, 20)/100 ); } break; case ST_NAPALM: for ( i = 0; i < 10; i++ ) { dest.x = shot->dest.x + RAND(-50,50); dest.y = shot->dest.y + RAND(-50,50); dest.z = 0; shots_fire_oil( &shot->pos, &dest, RAND(20,60), 0.01 + (double)RAND(0, 20)/100 ); units_check_impact( sx, sy, 40 ); trees_check_impact( sx, sy, 40 ); } break; case ST_OIL: particles_add_emitter( PT_FIRE, sx, sy, 5, 0.1, 6000 ); break; } } void bfield_update_gun_dir( int mx, int my ) { double alpha; if ( mx <= bfield.gun.x ) { if ( my <= bfield.gun.y ) bfield.gun_dir = 0; else bfield.gun_dir = 8; } else { alpha = atan( (my - bfield.gun.y) / (mx - bfield.gun.x ) ); alpha = alpha * 180 / 3.14; bfield.gun_dir = ((alpha + 10) / 20) + 4; } bfield.img_gun_x_offset = gun_w * bfield.gun_dir; }