// lander.cxx
// Written by Dmitry and Michael in the week of Feb 24, 2010.
// This program is a simple lunar lander game.

//---------------------------------------------------------------------------
#include <graphics.h>
#include <cstdlib>
#include <iostream>
using namespace std;
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Function prototypes
// The draw_background function draws a picture of the moon. The landing
// surface should be at world coordinate y=0.
void draw_background( );

// Draw little red flames to show which thrusters are on. The lander is
// at position (x,y) and the current thruster thrust is (jx, jy).
void draw_jets(double x, double y, double jx, double jy);

// The draw_lander function draws the lunar lander with the center at (x,y).
void draw_lander(double x, double y);

// Draw a small vector in a certain color. The starting point of the vector
// is (x,y) in world coordinates, and the length of the vector is (ax, ay).
void draw_vector(double x, double y, double ax, double ay, int color);

// Allow the pilot to control the lander. The pilot can raise or lower the
// thrust of the thrusters, which effects the jx and jy (jet acceleration)
// values.
void interact(double& jx, double& jy);

// pixel(v, v0, v1, pmax) converts v from an interval that ranges from v0 to v1
// into an integer interval that ranges from 0 to pmax. Note that when v = v0,
// the answer is always 0, and when v = v1, the answer is always pmax.
int pixel(double v, double v0, double v1, int pmax);

// Simulate the passage of DT seconds of time. This could change the position
// (x, y) or the velocity (vx, vy) of the lander.
void simulate_time
(double& x, double& y, double& vx, double& vy, double jx, double jy);
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// The game's world coordinates are a Cartesian coordinates system in meters
// with the ground level at y=0.
const double G = -1.6;       // Acceleration of gravity on the moon m/sec^2
const double THRUST = 0.5;   // Acceleration from kicking up thruster m/sec^2
const double DT = 0.05;      // Seconds to simulate in animation loop
const int SX = 851;          // Width of the screen in pixels
const int SY = 608;          // Height of the screen in pixels
const double WXMIN = -100.0; // Left side of screen in world coordinates 
const double WXMAX = 560.0;  // Right side of screen in world coordinates
const double WYMIN = -17.0;  // Bottom of screen in world coordinates
const double WYMAX = 373.0;  // Top of screen in world coordinates

// Radius of the ship in world coordinates and pixel coordinates:
const double WR = 8.0;
const int R = pixel(WXMIN + WR, WXMIN, WXMAX, SX);

// The next group of constants are for drawing the lander
const int X1 = int(R*0.15);  // Right edge of small inner circle
const int X2 = int(R*0.27);  // Right edge of the deck
const int X3 = int(R*0.70);  // Left edge of the right thruster
const int X4 = int(R*1.00);  // Right edge of the right thruster
const int Y1 = int(R*1.10);  // Top edge of the thrusters
const int Y2 = int(R*1.25);  // Top of strut connector to deck
const int Y3 = int(R*1.45);  // Bottom of strut connector to deck
const int Y4 = int(R*1.60);  // Top of strut connector to thruster
const int Y5 = int(R*2.05);  // Bottom of strut connector to thruster
const int Y6 = int(R*2.00);  // Where the decks start to curve in
const int Y7 = int(R*2.20);  // Bottom edge of the decks
const int Y8 = int(R*2.90);  // Bottom edge of the thrusters
const int SR = int(R*0.2);   // Radius of the small circle inside the saucer
const int TW = int(R*0.3);   // Width of the two thrusters
const int DH = int(R*2.2);   // Height of the decks
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
int main( )
{
    double x, y;    // Location of the ship in world coordinates (meters)
    double vx, vy;  // Velocity of the ship (meters/sec)
    double jx, jy;  // Acceleration on the ship due to jets (meters/sec^2)

    // Open the window with double buffering, and initialize variables:
    initwindow(SX, SY, "That's one small step...", 20, 20, true);
    x = WXMIN + 0.8*(WXMAX - WXMIN); // 80% of the way across the screen
    y = WYMIN + 0.8*(WYMAX - WYMIN); // 80% of the way up the screen
    vx = vy = jx = jy = 0;           // Not moving, jets off.

    // The animation loop simulatates DT seconds on each iteration.
    while (y > 0)
    {
	// Draw the next frame:
	draw_background( );
	draw_lander(x, y);
	draw_jets(x, y, jx, jy);
	
	// Double buffer and a delay DT seconds:
	swapbuffers( );
	delay(int(DT*1000));

	// Make changes by interacting with the pilot and simulating time:
	interact(jx, jy);
	simulate_time(x, y, vx, vy, jx, jy);
    }

    // Ten second delay before ending:
    delay(10000);
    return EXIT_SUCCESS;
}
//---------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Note: The first time this function is called, it reads the image from a
// file called moon.jpg, and it stores that image in a memory location pointed
// to by stored_image. Subsequent calls will just restore the image from that
// memory location rather than rereading it from the file.
void draw_background( )
{
    static void* stored_image = NULL;
    unsigned int storage_size;
    
    if (stored_image == NULL)
    {   // Draw the image and make a oopy in stored_image:
	readimagefile("moon.jpg", 0, 0, SX, SY);
	storage_size = imagesize(0, 0, SX, SY);
	stored_image = malloc(storage_size);
	getimage(0, 0, SX-1, SY-1, stored_image);
    }
    else
    {
	putimage(0, 0, stored_image, COPY_PUT);
    }
}
//-----------------------------------------------------------------------------


//----------------------------------------------------------------------------
void draw_vector(double x, double y, double ax, double ay, int color) 
{
    const int DOT_SIZE = 2;
    int px1, py1, px2, py2;

    // Convert the starting point and ending points:
    px1 = pixel(x, WXMIN, WXMAX, SX);
    py1 = pixel(y, WYMAX, WYMIN, SY);
    px2 = pixel(x + ax, WXMIN, WXMAX, SX);
    py2 = pixel(y + ay, WYMAX, WYMIN, SY);

    // Connect the points and put a dot at the endpoint:
    setcolor(color);
    line(px1, py1, px2, py2);
    setfillstyle(SOLID_FILL, color);
    fillellipse(px2, py2, DOT_SIZE, DOT_SIZE);
}
//----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
void draw_jets(double x, double y, double jx, double jy)
{
    if (jy > 0)
    {
	draw_vector(x, (y - 2.2*WR - jy), 0, jy, RED);
    }
    if (jx > 0)
    {
	draw_vector((x - 1.1*WR - jx), y, jx, 0, RED);
    }
    else if (jx < 0)
    {
	draw_vector((x + 1.1*WR - jx), y, jx, 0, RED);
    }
}
//-----------------------------------------------------------------------------



//-----------------------------------------------------------------------------
// Constants for drawing the lander:
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
void draw_lander(double x, double y)
{
    // Pixel coordinates for the saucer center:
    int px = pixel(x, WXMIN, WXMAX, SX);
    int py = pixel(y, WYMAX, WYMIN, SY);

    // Pixel coordinates for the deck:
    int deck[12] = {
	px-X2, py, px-X2, py+Y6,    // left edge
	px-X1, py+Y7, px+X1, py+Y7, // bottom edge
	px+X2, py+Y6, px+X2, py     // right edge
    };

    // Pixel coordinates for the left and right struts:
    int ls[8] = {px-X1, py+Y2, px-X1, py+Y3, px-X3, py+Y5, px-X3, py+Y4};
    int rs[8] = {px+X1, py+Y2, px+X1, py+Y3, px+X3, py+Y5, px+X3, py+Y4};
    
    // Set the color for all the edges:
    setcolor(BLACK);

    // Draw the lower deck and struts::
    setfillstyle(SOLID_FILL, COLOR(115, 115, 115));
    fillpoly(6, deck);
    setfillstyle(SOLID_FILL, COLOR(156, 156, 156));
    fillpoly(4, ls);
    fillpoly(4, rs);

    // Draw the saucer and the tiny circle inside of it:
    setfillstyle(SOLID_FILL, COLOR(156, 156, 156));
    fillellipse(px, py, R, R);
    setfillstyle(SOLID_FILL, COLOR(184, 184, 184));
    fillellipse(px, py, X1, X1);

    // Draw the two thrusters:
    setfillstyle(SOLID_FILL, COLOR(184, 184, 184));
    bar3d(px - X3, py + Y1, px - X4, py + Y8, 0, true);
    bar3d(px + X3, py + Y1, px + X4, py + Y8, 0, true);
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
void interact(double& jx, double& jy)
{
    if (ismouseclick(WM_LBUTTONDOWN))
    {
	jx -= THRUST;
	clearmouseclick(WM_LBUTTONDOWN);
    }
    if (ismouseclick(WM_RBUTTONDOWN))
    {
	jx += THRUST;
	clearmouseclick(WM_RBUTTONDOWN);
    }
    if (kbhit( ))
    {
	switch(getch( ))
	{
	case KEY_UP:
	    jy += THRUST;
	    break;
	case KEY_DOWN:
	    if (jy > 0)
	    {
		jy -= THRUST;
	    }
	    break;
	}
    }
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
int pixel(double v, double v0, double v1, int pmax)
{
    double scale = pmax/(v1-v0);
    return int(scale*(v - v0));
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
void simulate_time
(double& x, double& y, double& vx, double& vy, double jx, double jy)
{
    x = x + vx*DT;
    vx = vx + jx*DT;
    
    y = y + vy*DT;
    vy = vy + (jy + G)*DT;
}
//-----------------------------------------------------------------------------
