/*
 *   ttyvaders     Textmode shoot'em up
 *   Copyright (c) 2002 Sam Hocevar <sam@zoy.org>
 *                 All Rights Reserved
 *
 *   $Id: tarass
 *
 *   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.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <math.h>

#include "common.h"

static void draw_bomb( int x, int y, int vx, int vy );
static void draw_nuke( int x, int y, int frame );
static void draw_beam( int x, int y, int frame );
static void draw_circle( int x, int y, int r, char c );

void init_weapons( game *g, weapons *wp )
{
    int i;

    for( i = 0; i < WEAPONS; i++ )
    {
        wp->type[i] = WEAPON_NONE;
    }
}

void draw_weapons( game *g, weapons *wp )
{
    int i;

    for( i = 0; i < WEAPONS; i++ )
    {
        switch( wp->type[i] )
        {
            case WEAPON_LASER:
                gfx_color( WHITE );
                gfx_goto( wp->x[i] >> 4, wp->y[i] >> 4 );
                gfx_putchar( '|' );
                gfx_color( CYAN );
                gfx_goto( wp->x[i] >> 4, (wp->y[i] >> 4) + 1 );
                gfx_putchar( '|' );
                break;
            case WEAPON_SEEKER:
                gfx_color( CYAN );
                gfx_goto( wp->x3[i] >> 4, wp->y3[i] >> 4 );
                gfx_putchar( '.' );
                gfx_goto( wp->x2[i] >> 4, wp->y2[i] >> 4 );
                gfx_putchar( 'o' );
                gfx_color( WHITE );
                gfx_goto( wp->x[i] >> 4, wp->y[i] >> 4 );
                gfx_putchar( '@' );
                break;
            case WEAPON_BOMB:
                gfx_color( GRAY );
                gfx_goto( (wp->x[i] - wp->vx[i]) >> 4, (wp->y[i] - wp->vy[i]) >> 4 );
                gfx_putchar( '.' );
                gfx_goto( (wp->x3[i] - wp->vx[i]) >> 4, (wp->y3[i] - wp->vy[i]) >> 4 );
                gfx_putchar( '.' );
                gfx_goto( (wp->x2[i] - wp->vx[i]) >> 4, (wp->y2[i] - wp->vy[i]) >> 4 );
                gfx_putchar( '.' );
                gfx_goto( wp->x3[i] >> 4, wp->y3[i] >> 4 );
                gfx_putchar( '.' );
                gfx_goto( wp->x2[i] >> 4, wp->y2[i] >> 4 );
                gfx_putchar( '.' );
                draw_bomb( wp->x[i] >> 4, wp->y[i] >> 4, wp->vx[i], wp->vy[i] );
                break;
            case WEAPON_BEAM:
                draw_beam( wp->x[i] >> 4, wp->y[i] >> 4, wp->n[i] );
                break;
            case WEAPON_NUKE:
                draw_nuke( wp->x[i] >> 4, wp->y[i] >> 4, wp->n[i] );
                break;
            case WEAPON_NONE:
                break;
        }
    }
}

void update_weapons( game *g, weapons *wp )
{
    int i, j, dist, xmin, ymin, dx, dy, xnew, ynew;

    for( i = 0; i < WEAPONS; i++ )
    {
        switch( wp->type[i] )
        {
            case WEAPON_LASER:
                wp->x[i] += wp->vx[i];
                wp->y[i] += wp->vy[i];
                if( wp->y[i] < 0 )
                {
                    wp->type[i] = WEAPON_NONE;
                }
                break;
            case WEAPON_BOMB:
            case WEAPON_SEEKER:
                /* Update tail */
                wp->x3[i] = wp->x2[i];
                wp->y3[i] = wp->y2[i];

                wp->x2[i] = wp->x[i];
                wp->y2[i] = wp->y[i];

                wp->x[i] += wp->vx[i];
                wp->y[i] += wp->vy[i];

                if( wp->y[i] < 0 )
                {
                    wp->type[i] = WEAPON_NONE;
                    break;
                }

                if( wp->n[i] < 0 )
                {
                    /* Stop updating direction */
                    break;
                }

                wp->n[i]--;

                /* Estimate our position in 2 frames */
                xnew = wp->x[i] + 4 * wp->vx[i];
                ynew = wp->y[i] + 4 * wp->vy[i];

                xmin = xnew;
                ymin = - (g->h << 4);
                dist = (xnew - xmin) * (xnew - xmin)
                        + 4 * (ynew - ymin) * (ynew - ymin);

                /* Find the nearest alien */
                for( j = 0; j < ALIENS; j++ )
                {
                    if( g->al->type[j] != ALIEN_NONE )
                    {
                        int alx = g->al->x[j] << 4;
                        int aly = g->al->y[j] << 4;
                        int new = (xnew - alx) * (xnew - alx)
                                   + 4 * (ynew - aly) * (ynew - aly);
                        if( new <= dist )
                        {
                            dist = new;
                            xmin = alx;
                            ymin = aly;
                        }
                    }
                }

                /* Find our new direction */
                dx = xmin - wp->x[i];
                dy = ymin - wp->y[i];

                /* Normalize and update speed */
                wp->vx[i] = (7 * wp->vx[i]
                              + (dx * 48) / sqrt(dx*dx+dy*dy) ) / 8;
                wp->vy[i] = (7 * wp->vy[i]
                              + (dy * 24) / sqrt(dx*dx+dy*dy) ) / 8;

                break;
            case WEAPON_BEAM:
                wp->x[i] = (g->p->x + 2) << 4;
                wp->y[i] = g->p->y << 4;
                wp->n[i]--;
                if( wp->n[i] < 0 )
                {
                    wp->type[i] = WEAPON_NONE;
                }
                break;
            case WEAPON_NUKE:
                wp->n[i]--;
                if( wp->n[i] < 0 )
                {
                    wp->type[i] = WEAPON_NONE;
                }
                break;
            case WEAPON_NONE:
                break;
        }
    }
}

void add_weapon( game *g, weapons *wp, int x, int y, int vx, int vy, int type )
{
    int i;

    for( i = 0; i < WEAPONS; i++ )
    {
        if( wp->type[i] == WEAPON_NONE )
        {
            wp->x[i] = x;
            wp->y[i] = y;
            wp->vx[i] = vx;
            wp->vy[i] = vy;
            wp->type[i] = type;
            switch( type )
            {
                case WEAPON_LASER:
                    break;
                case WEAPON_SEEKER:
                case WEAPON_BOMB:
                    wp->x2[i] = x;
                    wp->y2[i] = y;
                    wp->x3[i] = x;
                    wp->y3[i] = y;
                    wp->n[i] = 20;
                    break;
                case WEAPON_BEAM:
                    wp->n[i] = 25;
                    break;
                case WEAPON_NUKE:
                    wp->n[i] = 25;
                    break;
                case WEAPON_NONE:
                    break;
            }
            break;
        }
    }
}

static void draw_bomb( int x, int y, int vx, int vy )
{
    vy *= 2;
    gfx_color( CYAN );

    if( vx > vy )
    {
        if( vx > -vy ) /* right quarter */
        {
            if( vy > vx/4 )
            {
                /* -1pi/6 */
                gfx_goto( x-4, y-1 );
                gfx_putstr( "/`-." );
                gfx_goto( x-4, y );
                gfx_putstr( "`-._\\" );
                gfx_color( WHITE );
                gfx_goto( x-1, y );
                gfx_putstr( "_\\" );
                gfx_goto( x, y+1 );
                gfx_putchar( '`' );
            }
            else if( vy < -vx/4 )
            {
                /* 1pi/6 */
                gfx_goto( x-4, y );
                gfx_putstr( ",-' " );
                gfx_goto( x-4, y+1 );
                gfx_putstr( "\\,-'" );
                gfx_color( WHITE );
                gfx_goto( x-1, y-1 );
                gfx_putstr( "_," );
                gfx_goto( x, y );
                gfx_putchar( '/' );
            }
            else
            {
                /* 0pi/6 */
                gfx_goto( x-4, y-1 );
                gfx_putstr( "____" );
                gfx_goto( x-5, y );
                gfx_putstr( "|____" );
                gfx_color( WHITE );
                gfx_goto( x, y );
                gfx_putchar( '>' );
            }
        }
        else /* top quarter */
        {
            if( vx > -vy/4 )
            {
                /* 2pi/6 */
                gfx_goto( x-2, y );
                gfx_putstr( "/ " );
                gfx_goto( x-3, y+1 );
                gfx_putstr( "/ /" );
                gfx_goto( x-3, y+2 );
                gfx_putstr( "`'" );
                gfx_color( WHITE );
                gfx_goto( x-1, y-1 );
                gfx_putstr( "_," );
                gfx_goto( x, y );
                gfx_putchar( '|' );
            }
            else if( vx < vy/4 )
            {
                /* 4pi/6 */
                gfx_goto( x+1, y );
                gfx_putstr( " \\" );
                gfx_goto( x+1, y+1 );
                gfx_putstr( "\\ \\" );
                gfx_goto( x+2, y+2 );
                gfx_putstr( "`'" );
                gfx_color( WHITE );
                gfx_goto( x, y-1 );
                gfx_putstr( "._" );
                gfx_goto( x, y );
                gfx_putchar( '|' );
            }
            else
            {
                /* 3pi/6 */
                gfx_goto( x-1, y+1 );
                gfx_putstr( "| |" );
                gfx_goto( x-1, y+2 );
                gfx_putstr( "|_|" );
                gfx_color( WHITE );
                gfx_goto( x-1, y );
                gfx_putstr( ",^." );
            }
        }
    }
    else
    {
        if( vx > -vy ) /* bottom quarter */
        {
            if( vx > vy/4 )
            {
                /* -2pi/6 */
                gfx_goto( x-2, y-2 );
                gfx_putstr( ",." );
                gfx_goto( x-2, y-1 );
                gfx_putstr( "\\ \\" );
                gfx_goto( x-1, y );
                gfx_putchar( '\\' );
                gfx_color( WHITE );
                gfx_goto( x, y );
                gfx_putstr( "_|" );
            }
            else if( vx < -vy/4 )
            {
                /* -4pi/6 */
                gfx_goto( x+1, y-2 );
                gfx_putstr( ",." );
                gfx_goto( x, y-1 );
                gfx_putstr( "/ /" );
                gfx_goto( x+1, y );
                gfx_putchar( '/' );
                gfx_color( WHITE );
                gfx_goto( x-1, y );
                gfx_putstr( "|_/" );
            }
            else
            {
                /* -3pi/6 */
                gfx_goto( x, y-3 );
                gfx_putchar( '_' );
                gfx_goto( x-1, y-2 );
                gfx_putstr( "| |" );
                gfx_goto( x-1, y-1 );
                gfx_putstr( "| |" );
                gfx_color( WHITE );
                gfx_goto( x-1, y );
                gfx_putstr( "`v'" );
            }
        }
        else /* left quarter */
        {
            if( vy > -vx/4 )
            {
                /* -5pi/6 */
                gfx_goto( x+1, y-1 );
                gfx_putstr( ",-'\\" );
                gfx_goto( x+2, y );
                gfx_putstr( ",-'" );
                gfx_goto( x, y+1 );
                gfx_putchar( '\'' );
                gfx_color( WHITE );
                gfx_goto( x, y );
                gfx_putstr( "/_" );
            }
            else if( vy < vx/4 )
            {
                /* 5pi/6 */
                gfx_goto( x+1, y );
                gfx_putstr( " `-." );
                gfx_goto( x+1, y+1 );
                gfx_putstr( "`-./" );
                gfx_color( WHITE );
                gfx_goto( x, y-1 );
                gfx_putstr( "._" );
                gfx_goto( x, y );
                gfx_putchar( '\\' );
            }
            else
            {
                /* 6pi/6 */
                gfx_goto( x+1, y-1 );
                gfx_putstr( "____" );
                gfx_goto( x+1, y );
                gfx_putstr( "____|" );
                gfx_color( WHITE );
                gfx_goto( x, y );
                gfx_putchar( '<' );
            }
        }
    }
}

static void draw_beam( int x, int y, int frame )
{
    int r = (29 - frame) * (29 - frame) / 8;
    int i;

    switch( frame )
    {
        case 24:
            gfx_color( WHITE );
            gfx_goto( x, y-3 );
            gfx_putstr( "__" );
            gfx_goto( x-1, y-2 );
            gfx_putchar( '\'' );
            gfx_goto( x+2, y-2 );
            gfx_putchar( '`' );
            break;
        case 23:
            gfx_color( CYAN );
            gfx_goto( x, y-3 );
            gfx_putstr( "__" );
            gfx_color( WHITE );
            gfx_goto( x-2, y-2 );
            gfx_putstr( "-'" );
            gfx_goto( x+2, y-2 );
            gfx_putstr( "`-" );
            break;
        case 22:
            gfx_color( CYAN );
            gfx_goto( x, y-3 );
            gfx_putstr( "__" );
            gfx_goto( x-1, y-2 );
            gfx_putchar( '\'' );
            gfx_goto( x+2, y-2 );
            gfx_putchar( '`' );
            gfx_color( WHITE );
            gfx_goto( x-3, y-2 );
            gfx_putstr( ",-" );
            gfx_goto( x+3, y-2 );
            gfx_putstr( "-." );
            break;
        case 21:
            gfx_color( CYAN );
            gfx_goto( x-1, y-3 );
            gfx_putstr( "____" );
            gfx_goto( x-2, y-2 );
            gfx_putchar( '\'' );
            gfx_goto( x+3, y-2 );
            gfx_putchar( '`' );
            gfx_color( WHITE );
            gfx_goto( x-4, y-2 );
            gfx_putstr( ",-" );
            gfx_goto( x+4, y-2 );
            gfx_putstr( "-." );
            break;
        case 20:
            gfx_color( WHITE );
            gfx_goto( x, y-3 );
            gfx_putstr( "%%" );
            gfx_goto( x-4, y-2 );
            gfx_putchar( ',' );
            gfx_goto( x+5, y-2 );
            gfx_putchar( '.' );
            gfx_color( CYAN );
            gfx_goto( x-1, y-3 );
            gfx_putchar( ':' );
            gfx_goto( x+2, y-3 );
            gfx_putchar( ':' );
            gfx_goto( x-3, y-2 );
            gfx_putstr( "-'" );
            gfx_goto( x+3, y-2 );
            gfx_putstr( "`-" );
            break;
        case 19:
            gfx_color( WHITE );
            gfx_goto( x, y-4 );
            gfx_putstr( "%%" );
            gfx_goto( x, y-3 );
            gfx_putstr( "##" );
            gfx_color( CYAN );
            gfx_goto( x-1, y-4 );
            gfx_putchar( ':' );
            gfx_goto( x+2, y-4 );
            gfx_putchar( ':' );
            gfx_goto( x-1, y-3 );
            gfx_putchar( '%' );
            gfx_goto( x+2, y-3 );
            gfx_putchar( '%' );
            gfx_goto( x-4, y-2 );
            gfx_putstr( ",-'" );
            gfx_goto( x+3, y-2 );
            gfx_putstr( "`-." );
            gfx_color( BLUE );
            gfx_goto( x-2, y-3 );
            gfx_putchar( ':' );
            gfx_goto( x+3, y-3 );
            gfx_putchar( ':' );
            break;
        case 18:
        default:
            r = (18 - frame) * (18 - frame);
            gfx_color( WHITE );
            gfx_goto( x-1, y-5-r );
            gfx_putstr( ":%%:" );
            gfx_goto( x-1, y-4-r );
            gfx_putstr( "%##%" );
            gfx_color( CYAN );
            gfx_goto( x-2, y-4-r );
            gfx_putchar( ':' );
            gfx_goto( x+3, y-4-r );
            gfx_putchar( ':' );
            gfx_goto( x-2, y-2 );
            gfx_putchar( '\'' );
            gfx_goto( x+3, y-2 );
            gfx_putchar( '`' );
            gfx_color( BLUE );
            gfx_goto( x-3, y-2 );
            gfx_putchar( ':' );
            gfx_goto( x+4, y-2 );
            gfx_putchar( ':' );
            for( i = 0; i <= r; i++ )
            {
                gfx_color( WHITE );
                gfx_goto( x-1, y-3-i );
                gfx_putstr( (i+frame) % 5 ? "####" : "%%%%" );
                gfx_color( CYAN );
                gfx_goto( x-2, y-3-i );
                gfx_putchar( '%' );
                gfx_goto( x+3, y-3-i );
                gfx_putchar( '%' );
                gfx_color( BLUE );
                gfx_goto( x-3, y-3-i );
                gfx_putchar( ':' );
                gfx_goto( x+4, y-3-i );
                gfx_putchar( ':' );
            }
            break;
    }
}

static void draw_nuke( int x, int y, int frame )
{
    int r = (29 - frame) * (29 - frame) / 8;

    /* Lots of duplicate pixels, but we don't care */
    gfx_color( BLUE );
    draw_circle( x, y, r++, ':' );
    gfx_color( CYAN );
    draw_circle( x, y, r++, '%' );
    gfx_color( WHITE );
    draw_circle( x, y, r++, '#' );
    draw_circle( x, y, r++, '#' );
}

static void draw_circle( int x, int y, int r, char c )
{
    int test, dx, dy;

    /* Optimized Bresenham. Kick ass. */
    for( test = 0, dx = 0, dy = r ; dx <= dy ; dx++ )
    {
        gfx_putcharTO( x + dx, y + dy / 2, c );
        gfx_putcharTO( x - dx, y + dy / 2, c );
        gfx_putcharTO( x + dx, y - dy / 2, c );
        gfx_putcharTO( x - dx, y - dy / 2, c );

        gfx_putcharTO( x + dy, y + dx / 2, c );
        gfx_putcharTO( x - dy, y + dx / 2, c );
        gfx_putcharTO( x + dy, y - dx / 2, c );
        gfx_putcharTO( x - dy, y - dx / 2, c );

        test += test > 0 ? dx - dy-- : dx;
    }
}