/***************************************************************************
                          shots.c  -  description
                             -------------------
    begin                : Sat Sep 8 2001
    copyright            : (C) 2001 by Michael Speck
    email                : kulkanie@gmx.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 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "gamedefs.h"
#include "mathfuncs.h"
#include "balls.h"
#include "bricks.h"

int shot_w = 10;
int shot_h = 10;
float shot_v_y = 0.2;
extern Game *cur_game;

int last_shot_fire_x = -1; /* HACK: used to play local sound */

/*
====================================================================
Locals
====================================================================
*/

/*
====================================================================
Compute target of shot.
====================================================================
*/
void shot_get_target( Shot *shot ) {
    int mx = (int)(shot->x + 3) / BRICK_WIDTH;
    int my = (int)(shot->y + 3 + ((shot->dir==1)?5:0) ) / BRICK_HEIGHT;
    memset( &shot->target, 0, sizeof(Target) );
    while ( cur_game->bricks[mx][my].id == -1 ) 
        my += shot->dir;
    shot->target.mx = mx; shot->target.my = my;
    mx = (int)(shot->x + 6) / BRICK_WIDTH;
    if (mx != shot->target.mx) {
        my = (int)(shot->y + 3 + ((shot->dir==1)?5:0) ) / BRICK_HEIGHT;
        while(cur_game->bricks[mx][my].id == -1) 
            my += shot->dir;
        if (my == shot->target.my)
            shot->next_too = 1;
        else
            if ( ( shot->dir == -1 && my > shot->target.my ) ||
                 ( shot->dir ==  1 && my < shot->target.my ) ) {
                shot->target.mx = mx;
                shot->target.my = my;
                shot->next_too = 0;
            }
    }
    shot->target.cur_tm = 0;
    shot->target.time = abs( 
		(int)((shot->y + 3 + 
		((shot->dir==1)?5:0) - 
		(shot->target.my * BRICK_HEIGHT + ((shot->dir==-1)?(BRICK_HEIGHT - 1):0) )) / 
		fabs(shot_v_y)) );
}

/*
====================================================================
Publics
====================================================================
*/

/*
====================================================================
Create new shot at position (centered).
'signum' of direction determines into which direction the shot 
vertically goes.
====================================================================
*/
void shot_create( Paddle *paddle )
{
    Shot *shot = calloc( 1, sizeof( Shot ) );
    shot->cur_fr = 0;
    shot->paddle = paddle;
    shot->dir = (paddle->type == PADDLE_TOP) ? 1 : -1;
    shot->x = paddle->x + ( paddle->w >> 1 ) - (shot_w >> 1);
    shot->y = paddle->y + ( paddle->h >> 1 ) - (shot_h >> 1);
    shot->get_target = 1;
    list_add( cur_game->shots, shot );

    cur_game->mod.fired_shot_count++;
    last_shot_fire_x = shot->x; /* HACK: used to play local sound */
}

/*
====================================================================
Set 'get_target' flag so target is updated next time 
'shots_update' is called. -1 means to update all shots.
====================================================================
*/
void shots_check_targets( int mx, int my )
{
    Shot        *shot;
    list_reset( cur_game->shots );
    while ( ( shot = list_next( cur_game->shots ) ) )
        if ( mx == -1 || (shot->target.mx == mx && shot->target.my == my) )
            shot->get_target = 1;
}

/*
====================================================================
Update position of shots and check if bricks get destroyed.
A list of all hit bricks is returned (at maximum 
PADDLE_WEAPON_AMMO * 4)
====================================================================
*/
void shots_update( int ms )
{
	int i;
	ListEntry  *entry = cur_game->shots->head->next;
	Shot        *shot;

	while ( entry != cur_game->shots->tail ) {
		shot = entry->item;
		if ( shot->get_target ) { /* new target? */
			shot_get_target(shot);
			shot->get_target = 0;
		}
		shot->y += shot->dir * ms * shot_v_y;
		shot->target.cur_tm += ms;
		entry = entry->next;
		/* kill 'out of screen' shots */
		if ( shot->y + shot_h < 0 || shot->y > 480 ) {
			shot->paddle->weapon_ammo++; /* give back used shot */
			list_delete_entry( cur_game->shots, entry->prev );
			continue;
		}
		/* check hits */
		if (shot->target.cur_tm > shot->target.time) {
			if ( brick_hit( shot->target.mx, shot->target.my, 
					0, SHR_BY_SHOT, vector_get( 0, shot->dir ), 
					shot->paddle ) ) {
				shots_check_targets( shot->target.mx, shot->target.my );
                		balls_check_targets( shot->target.mx, shot->target.my );
			}
			if (shot->next_too)
			if ( brick_hit( shot->target.mx + 1, shot->target.my, 
					0, SHR_BY_SHOT, vector_get( 0, shot->dir ), 
					shot->paddle ) ) {
				shots_check_targets(shot->target.mx + 1, shot->target.my);
                		balls_check_targets( shot->target.mx, shot->target.my );
			}
			shot->paddle->weapon_ammo++; /* give back used shot */
			list_delete_entry( cur_game->shots, entry->prev );
			continue;
		}
		/* in multiplayer we check if we hit the opponent if so we steal
		   him a 1000 points */
		for ( i = 0; i < cur_game->paddle_count; i++ )
			if ( cur_game->paddles[i] != shot->paddle )
			if ( shot->x + shot_w > cur_game->paddles[i]->x )
			if ( shot->x < cur_game->paddles[i]->x + cur_game->paddles[i]->w )
			if ( shot->y + shot_h > cur_game->paddles[i]->y )
			if ( shot->y < cur_game->paddles[i]->y + cur_game->paddles[i]->h ) {
				if ( (cur_game->paddles[i]->score -= 1000) < 0 )
					cur_game->paddles[i]->score = 0;
				shot->paddle->score += 1000;
				shot->paddle->weapon_ammo++;
				list_delete_entry( cur_game->shots, entry->prev );
				break;
			}
	}
}

