/*************************************************************************** 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 "units.h" #include "shots.h" extern SDL_Surface *screen; extern SDL_Surface *img_shots; Shot shots_anchor; Shot *shots_tail = &shots_anchor; static Vector grav = { 0, 0, -GRAV }; /* for all projectiles that are influenced by gravity */ static void (*impact_handler)(Shot *shot) = 0; /* used by other modules as well */ void vec2xy( Vector *v, int *sx, int *sy ) { /* convert to screen */ *sx = (int)v->x; *sy = (int)v->y - (((int)v->z)>>1); } static void shot_add( Shot *shot ) { shots_tail->next = shot; shot->prev = shots_tail; shots_tail = shot; } static void shot_delete( Shot *shot ) { /* each shot has a previous one as we use an anchor which may not be deleted */ if ( shot == &shots_anchor ) return; shot->prev->next = shot->next; if ( shot->next ) shot->next->prev = shot->prev; else shots_tail = shot->prev; free( shot ); } void shots_draw( SDL_Surface *dest, int camera_x, int camera_y ) { int sx, sy; SDL_Rect srect, drect; Shot *shot = shots_anchor.next; while ( shot ) { vec2xy( &shot->pos, &sx, &sy ); /* add in camera check? */ /* the position refers to the center of of the projectile * but we have to start drawing at the upper left pixel */ sx -= shot->w>>1; sy -= shot->h>>1; /* center */ /* projectile */ srect.x = shot->img_x_offset; srect.y = shot->img_y_offset; drect.x = sx - camera_x; drect.y = sy - camera_y; srect.w = drect.w = shot->w; srect.h = drect.h = shot->h; SDL_SetAlpha( shot->img, 0, 0 ); SDL_BlitSurface( shot->img, &srect, dest, &drect ); /* shadow */ if ( shot->with_shadow ) { srect.x = shot->img_x_offset; srect.y = shot->img_y_offset + shot->h; drect.x = shot->pos.x - (shot->w>>1); drect.x += ((int)shot->pos.z)>>3; drect.y = shot->pos.y - (shot->h>>1); srect.w = drect.w = shot->w; srect.h = drect.h = shot->h; SDL_SetAlpha( shot->img, SDL_SRCALPHA, 128 ); SDL_BlitSurface( shot->img, &srect, dest, &drect ); } shot = shot->next; } } void shots_update( int ms ) { int i; Shot *shot = shots_anchor.next, *next; while ( shot ) { /* update position at ground */ shot->pos.x += shot->dir.x * ms; shot->pos.y += shot->dir.y * ms; /* as we approach the projectile curve by vectors * the frame rate is essentially for the accuracy of the * height as with each call the tangential speed vector * is added which is instantly out of sync due to * a non-linear movement */ for ( i = 0; i < ms; i++ ) { shot->pos.z += shot->dir.z; shot->dir.z += grav.z; /* apply gravity */ } if ( shot->dir.z < 0 ) /* must be coming down */ if ( shot->pos.z <= shot->dest.z ) { shot->pos.z = shot->dest.z; if ( impact_handler ) impact_handler( shot ); next = shot->next; shot_delete( shot ); shot = next; continue; } shot = shot->next; } } void shots_delete() { while ( shots_anchor.next ) shot_delete( shots_anchor.next ); } void shots_set_impact_handler( void (*callback)(Shot* shot) ) { impact_handler = callback; } void shots_fire_ballistic( int type, SDL_Surface *img, int img_xoff, int img_yoff, int w, int h, Vector *src, Vector *dest, double alpha, double power ) { double len, height, r; Shot *shot = calloc( 1, sizeof( Shot ) ); if ( shot == 0 ) { printf( "out of memory\n" ); return; } /* graphics and type */ shot->type = type; if ( img == 0 ) shot->img = img_shots; else shot->img = img; shot->img_x_offset = img_xoff; shot->img_y_offset = img_yoff; shot->w = w; shot->h = h; shot->with_shadow = 1; /* convert alpha from degree to radiant */ alpha = 3.14 * alpha / 180; /* position and destination */ shot->pos = *src; shot->dest = *dest; shot->dir.x = dest->x - src->x; shot->dir.y = dest->y - src->y; len = sqrt(shot->dir.x*shot->dir.x + shot->dir.y*shot->dir.y); height = dest->z - src->z; if ( power == 0 ) { /* if not specified compute by angle and hit destination exactly */ r = -0.5 * GRAV / ( height - len * tan(alpha) ); if ( r < 0 ) { printf( "impossible movement for ballistic projectile!\n" ); free( shot ); return; } else { power = sqrt(r) * len / cos(alpha); shot->is_precise = 1; } } shot->dir.z = len * tan(alpha); len = sqrt(shot->dir.x*shot->dir.x + shot->dir.y*shot->dir.y + shot->dir.z*shot->dir.z); shot->dir.x /= len; shot->dir.y /= len; shot->dir.z /= len; shot->dir.x *= power; shot->dir.y *= power; shot->dir.z *= power; shot_add( shot ); }