// Copyright 1994 Brad Pitzel
//
// Feel free to use/distribute/modify as long as credit/copyrights for myself 
// are included.

// Classes: FastFloat, Trajectory, BitmapObj, Ship

#ifndef __BPObj__
#define __BPObj__

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

#include "Bitmap.h"
#include "bitmaps.h"
#include "ObjTools.h"
#include "Explosion.h"

//////////////////////////////////////////////////////////////////////////////

        
//////////////////////////////////////////////////////////////////////////////
// Note: bitmaps should be square: width=height
// Objects drawn in bitmap should be approx. round
// since the collision detection method used in this prog is
// pretty simple/dumb.  

class BitmapObj : protected Life, protected Trajectory {
    public:
    
    	BitmapObj( int sh, double m, const Bitmap& b) : 
    		shape(sh), mass(m), Vsize(0), Life(), Trajectory()
    		{
    		setBitmap(b);
    		};

    	inline virtual void draw() const 
    		{
		Vbitmap->put( (int)(Vx-Vsize), (int)(Vy-Vsize) );
    		}


	// do the stuff that should happen in one 'frame' of time, i.e.
	// move the obj, etc..
    	inline virtual void tick()		
    		{	
		Life::tick();

		Trajectory::move();
		}

	inline void explode(int size, int dur)
		{
		// add an explosion to the explosion list
		Boom::add( (int)Vx, (int)Vy, size, dur, Vxvel, Vyvel );
		}

	void setBitmap( const Bitmap &b ) 
		{ 
		Vbitmap=&b; 
		Vsize = b.width() >> 1;
		}

	//Bitmap& bitmap() { return b; }
	
	inline int	size() const { return Vsize; }

	inline int	distance( const BitmapObj& b )
			{ return (int)(sqrt( (Vx-b.Vx) * (Vx-b.Vx) +
					     (Vy-b.Vy) * (Vy-b.Vy) )); 
			}

	inline void	towards( const BitmapObj& b, double speed )
			{ Trajectory::towards( (int)b.Vx, (int)b.Vy, speed ); }

    	int shape;
	double mass, rot;
	
    protected:
	const Bitmap *Vbitmap;
	
    private:
	int		Vsize;
     };


//////////////////////////////////////////////////////////////////////////////
// Player's ship.  Only create one instance of it in the game.
//
#define SHIPTURNS 32
#define TURNANGLE 360.0/(double)SHIPTURNS

class Ship : public BitmapObj {
    public:

    	Ship( int sh, double m ) : 
    		BitmapObj(sh,m,ship0), pos(0)
    		{
    		}

	
	// if shield on, returns the 'energy level' (approx to timing
	// remaining) for the shield, between 0 and 1.0
	inline double shieldPercent() const
			{ 
			if (shield.isAlive())
				return ((double)shield.timeLeft())/((double)shieldMax());
			else	return 0.0;
			}				

    	inline void draw() const
    		{
    		BitmapObj::draw();
    		if (shield.isAlive())
    			gl_circle( (int)Vx, (int)Vy, 10, 254 );
		}
		    	
	void tick() 
		{
		BitmapObj::tick();
		shield.tick();

		if (thrustCnt) 
			{
			thrustCnt--;
			if (thrustCnt<=0) 
				{
				thrustCnt=0;
				Ship::setBitmap();
				}
			}
		}
		    	
    	void setBitmap()
    		{
    		if (thrustCnt)  BitmapObj::setBitmap((shipThr[pos]));
    		else		BitmapObj::setBitmap((ship[pos]));
    		}

    	inline void rotLeft( int t )
    		{
		this->rot -= TURNANGLE*(double)t;
		this->pos-=t;
		this->pos %= SHIPTURNS;
		this->Ship::setBitmap();
		}

    	inline void rotRight( int t)
    		{
		this->rot += TURNANGLE*(double)t;
		this->pos+=t;
		this->pos %= SHIPTURNS;
		this->Ship::setBitmap();
		}

	inline void thrust(double scale=1)
		{
		this->Vxvel += FastMath::cos((int)this->rot)*scale;
		this->Vyvel += FastMath::sin((int)this->rot)*scale;
				    
		thrustCnt = 5;
		Ship::setBitmap();
		}

	inline void  shieldOn()
		{
		if (this->shieldCnt)
			{
			shieldCnt--;
			shield.revive( shieldMax() );
			}
		}

	inline void  hyper()
		{
		setPos( (rand()>>5)%WIDTH, (rand()>>5)%HEIGHT );
		}

	// the player ship extends the die function by keeping track
	// how many 'lives' left it has.
	void die()
		{
		if (lives) lives--;
		BitmapObj::die();
		}
	
	void  ships( int x ) 
		{ lives=x; }
		
	int   ships() const 
		{ return lives; }
		
	void  addShip()
		{ lives++; }
	
	void newShip() { Ship::newShip(WIDTH>>1,HEIGHT>>1); }
	
	void newShip(int newX, int newY)
		{
		setPos( newX, newY );
		setVel( 0.0, 0.0 );

		this->rot = 270;
		this->pos = 0;
		
		this->shieldCnt = 1;
		this->shield.die();
		
		this->thrustCnt = 0;
		this->Ship::setBitmap();
		this->revive();
		};

	inline int shielded() const { return shield.isAlive(); }

	static void	init();	// fill ship arrays with data

    private:

	static Bitmap	ship[SHIPTURNS], shipThr[SHIPTURNS];
	
	inline int shieldMax() const { return 200; }  // max shield value
    
	unsigned char pos;
	Life	 shield;
    	int	 shieldCnt;
    	int	 thrustCnt;

	int	lives;    	
    };


//////////////////////////////////////////////////////////////////////////////
// Enemy Ship
//
class Enemy : public BitmapObj 
    {
    public:

    	Enemy( int sh, double m, const Ship& p, BitmapObj& bul ) : 
    		BitmapObj(sh,m,enemy), Vplayer(&p), Vspeed(0), Vbul(&bul)
    		{
    		}

	void tick() 
		{
		int i;
		BitmapObj::tick();

		if (isAlive())
			{	
			i = ((rand()>>2)%256) + (  (Vplayer->y() > Vy )
				? Vspeed>>1 : -(Vspeed>>1) );
			Vyvel += (i>128+6*Vyvel) ? .5 : -.5;

			if (!Vbul->isAlive())
				if ( ((rand()>>2)%(1200)) < VshotsPerMin )
					{
					Vbul->revive();
				    	Vbul->setPos(x(), y());
				    	Vbul->towards( *Vplayer, 3 );
					}
			}
		}

	void newShip(int speed, int shots )
		{
		VshotsPerMin = shots;	// avg # of shots per minutes to fire
					// off at the player
		Vspeed = speed;
		
		setPos( 0, (rand()>>5)%HEIGHT );
		setVel(.4 + ((double) Vspeed)/3., 0);
		
		this->revive();
		};

    private:
	int		Vspeed;
	int		VshotsPerMin;
	const   Ship	*Vplayer;
	BitmapObj	*Vbul;
    };

#endif
