// **************************************************************************
// *				Sporting Clays for the Nintendo 64						*
// *					   (C) 1999 Charles Doty                           	*
// *	 		 Game Source file contains game related code				*
// **************************************************************************

#include <ultra64.h>

#include "game.h"
#include "input.h"

#define MAXROUNDS	5

#define RABBITMULTIPLY	1.0

Clay 			Clays[2];
DWORD			NumClays;
DWORD			ClaysHit[MAXPLAYERS];
DWORD			ClaysTotal[MAXPLAYERS];
DWORD 			Player;
long			Floor;
long			ZLimit;							// ZLimit to consider clay hitting the ground. Scene specific.
long			ZOffscreen;						// ZLimit to consider clay Offscreen. Scene specific.
long			NumShots;						// Number of shots taken
BOOL			OnReport;
BOOL			Shot[2];						// Draw shot (miss) 1/2
float			Flight[2];
float			Flight2[2];
DWORD			NumRounds;
InputStruct		Input;
SceneStruct		*Scene;

extern DWORD 	course;
extern BYTE		Scenes[1][MAXSCENES][MAXSCENESIZE];

inline float ConvertToN64(long degree)
{
    return (float)degree * (PI * 2.0 / 360.0);
}

DWORD GameInitialize()
{
	SetMouseLimits(MOUSESIZE / 2, MOUSESIZE / 2, SCREENWIDTH - MOUSESIZE / 2, SCREENHEIGHT -
		MOUSESIZE / 2);

	return ERR_NOERROR;
}

DWORD Run(DWORD scene)
{
    DWORD	loop;

	NumRounds 	= 0;
    Input.X		= SCREENWIDTH / 2;
    Input.Y		= SCREENHEIGHT / 2;

	for (loop = 0; loop < MAXPLAYERS; loop++)
	{
		ClaysHit[loop] = 0;
	}

	for (loop = 0; loop < MAXPLAYERS; loop++)
	{
		ClaysTotal[loop] = 0;
	}

	Player	 = 0;

	SetInputMousePosition(Input.X, Input.Y);
	SetMousePosition(Input.X - MOUSESIZE / 2, Input.Y - MOUSESIZE / 2);
    SetPadMoveValues(4, 4);

    DrawTextString("SPORTING CLAYS.", 3, 3);
    DrawTextString("BY CHARLES DOTY.", 2, 4);
    DrawTextString("CDOTY@NETZERO.NET", 1, 5);

    // Loads picture
    SelectPicture(0);

    WaitForStart(FALSE);

	LoadScene(scene);

	// Flip display buffers
	while (1)
	{
        ReadInput(&Input, 0);

		if (Input.Type != CON_NONE)
		{
			if (CON_GUN == Input.Type)
			{
			}

			else if (CON_MOUSE == Input.Type || CON_PAD == Input.Type)
			{
				SetMousePosition(Input.X - MOUSESIZE / 2, Input.Y - MOUSESIZE / 2);
			}

			if ((BUT_TRIGGER & Input.Buttons) != 0)
			{
				if (NumShots < 2)
				{
					PlaySound(GUN_SND);

					if (Clays[0].Position.z <= Clays[1].Position.z)
					{
						if (FALSE == CheckHit(0))
						{
							if (FALSE == CheckHit(1))
							{
								Shot[NumShots] = TRUE;
								SetShotPosition(Input.X - SHOTSIZE / 2, Input.Y - SHOTSIZE / 2,
									NumShots);

								PlaySound(MISS_SND);
							}
						}
					}

					else
					{
						if (FALSE == CheckHit(1))
						{
							if (FALSE == CheckHit(0))
							{
								Shot[NumShots] = TRUE;
								SetShotPosition(Input.X - SHOTSIZE / 2, Input.Y - SHOTSIZE / 2,
									NumShots);

								PlaySound(MISS_SND);
							}
						}
					}

					NumShots++;
				}

				else
				{
					PlaySound(EMPTY_SND);
				}
			}
		}

		AnimateClay(0);
		AnimateClay(1);

		Flip();

		if (FALSE == Clays[0].Flying && FALSE == Clays[1].Flying && Clays[1].Delay <= 0)
		{
			if (FALSE == Clays[0].Remove)
			{
				RemoveObject(Clays[0].ObjectID);
			}

			if (FALSE == Clays[1].Remove)
			{
				RemoveObject(Clays[1].ObjectID);
			}

			Flip();

			NumRounds++;

			if (NumRounds >= MAXROUNDS)
			{
				PrintScores();

				for (loop = 0; loop < MAXPLAYERS; loop++)
				{
					ClaysHit[loop] = 0;
				}

				Player	 = 0;

				scene++;

				if (scene >= MAXSCENES)
				{
					scene = 0;
				}

				NumRounds = 0;

				LoadScene(scene);
			}

			else
			{
				LoadScene(scene);
			}
		}
	}

	return ERR_NOERROR;
}

DWORD LoadScene(DWORD scene)
{
	BYTE	*sceneptr;
	long	number1;
	long	number2;

	Flight[0]	= 0.0f;
	Flight[1]	= 0.0f;
	Flight2[0]	= 0.0f;
	Flight2[1]	= 0.0f;
    Shot[0]		= FALSE;
    Shot[1]		= FALSE;
	NumShots	= 0;

	sceneptr	= (BYTE *)Scenes[course][scene];

	if (*(DWORD *)sceneptr != VERSION)
	{
#ifdef DEBUG
        printf("Bad scene version in LoadScene.\n");
#endif

		return ERR_FILEERROR;
	}

	sceneptr 	+= 4;

	NumClays	= *(DWORD *)sceneptr;
	sceneptr	+= 4;

	Floor		= *(long *)sceneptr;
	sceneptr	+= 4;

	ZLimit		= -*(long *)sceneptr;
	sceneptr	+= 4;

	ZOffscreen	= -*(long *)sceneptr;
	sceneptr	+= 4;

	OnReport	= *(long *)sceneptr;
	sceneptr	+= 4;

	Scene 		= (SceneStruct *)sceneptr;

    SelectPicture(scene);

	Flip();

    if (NumClays > 2)
    {
        number1 = rand() % NumClays;
    }

    else
    {
    	number1 = 0;
    }

    Clays[0].Position.x = Scene[number1].Init.x;
    Clays[0].Position.y = Scene[number1].Init.y;
    Clays[0].Position.z = -Scene[number1].Init.z;
    Clays[0].Init.x 	= Scene[number1].Init.x;
    Clays[0].Init.y 	= Scene[number1].Init.y;
    Clays[0].Init.z 	= -Scene[number1].Init.z;
    Clays[0].Rotation.x = 90 - Scene[number1].Rot.x;
    Clays[0].Rotation.y = Scene[number1].Rot.y;
    Clays[0].Rotation.z = Scene[number1].Rot.z;
    Clays[0].Scale.x 	= Scene[number1].Scale.x;
    Clays[0].Scale.y 	= Scene[number1].Scale.y;
    Clays[0].Scale.z 	= Scene[number1].Scale.z;
 	Clays[0].Speed		= Scene[number1].Speed;
 	Clays[0].Delay		= Scene[number1].Delay;
 	Clays[0].AngleY		= Scene[number1].AngleY;
 	Clays[0].AngleZ		= Scene[number1].AngleZ;
 	Clays[0].Rabbit		= Scene[number1].Rabbit;
 	Clays[0].Remove		= Scene[number1].Remove;
 	Clays[0].Water		= Scene[number1].Water;
    Clays[0].Velx       = Scene[number1].Speed * sinf(ConvertToN64(Scene[number1].AngleY));
    Clays[0].Vely       = Scene[number1].Speed * sinf(ConvertToN64(Scene[number1].AngleZ));
    Clays[0].Velz       = Scene[number1].Speed * cosf(ConvertToN64(Scene[number1].AngleZ));
 	Clays[0].Hit		= FALSE;

    AddObject(Scene[number1].Color, &Clays[0].ObjectID, &Clays[0].Position, &Clays[0].Rotation,
    	&Clays[0].Scale);

   	Clays[0].Flying = TRUE;
   	Clays[0].Delay 	= 0;

    if (NumClays > 2)
    {
		number2 = number1;

    	while (number2 == number1)
    	{
            number2 = rand() % NumClays;
    	}
    }

    else
    {
    	number2 = 1;
    }

    Clays[1].Position.x = Scene[number2].Init.x;
    Clays[1].Position.y = Scene[number2].Init.y;
    Clays[1].Position.z = -Scene[number2].Init.z;
    Clays[1].Init.x 	= Scene[number2].Init.x;
    Clays[1].Init.y 	= Scene[number2].Init.y;
    Clays[1].Init.z 	= -Scene[number2].Init.z;
    Clays[1].Rotation.x = 90 - Scene[number2].Rot.x;
    Clays[1].Rotation.y = Scene[number2].Rot.y;
    Clays[1].Rotation.z = Scene[number2].Rot.z;
    Clays[1].Scale.x 	= Scene[number2].Scale.x;
    Clays[1].Scale.y 	= Scene[number2].Scale.y;
    Clays[1].Scale.z 	= Scene[number2].Scale.z;
 	Clays[1].Speed		= Scene[number2].Speed;
 	Clays[1].Delay		= Scene[number2].Delay;
 	Clays[1].AngleY		= Scene[number2].AngleY;
 	Clays[1].AngleZ		= Scene[number2].AngleZ;
 	Clays[1].Rabbit		= Scene[number2].Rabbit;
 	Clays[1].Remove		= Scene[number2].Remove;
 	Clays[1].Water		= Scene[number2].Water;
    Clays[1].Velx       = Scene[number2].Speed * sinf(ConvertToN64(Scene[number2].AngleY));
    Clays[1].Vely       = Scene[number2].Speed * sinf(ConvertToN64(Scene[number2].AngleZ));
    Clays[1].Velz       = Scene[number2].Speed * cosf(ConvertToN64(Scene[number2].AngleZ));
 	Clays[1].Hit		= FALSE;

    AddObject(Scene[number1].Color, &Clays[1].ObjectID, &Clays[1].Position, &Clays[1].Rotation,
    	&Clays[1].Scale);

    if (Clays[1].Delay <= 0)
    {
    	Clays[1].Flying = TRUE;
    }

    WaitForStart(TRUE);

	PlaySound(LAUNCH_SND);
}

DWORD AnimateClay(long clay)
{
	long 	x;
	long 	y;
	long 	z;

	if (TRUE == Clays[clay].Flying)
	{
		if (TRUE == Clays[clay].Rabbit)
		{
			Flight[clay]			+= (float).35;
			Flight2[clay]			+= (float).35;

			Clays[clay].Position.x	= Clays[clay].Init.x + (Clays[clay].Velx * Flight[clay]) / 50.0f;
			Clays[clay].Position.z	= Clays[clay].Init.z;

			if (FALSE == Clays[clay].Hit)
			{
				Clays[clay].Position.y = Clays[clay].Init.y + (Clays[clay].Vely * Flight2[clay] *
					RABBITMULTIPLY - 1.5 * 32.2 * Flight2[clay] * Flight2[clay]) / 50.0f;
			}

			if (Clays[clay].Position.y <= Clays[clay].Init.y)
			{
				Flight2[clay] = 0.0f;

				if (FALSE == Clays[clay].Hit)
				{
					Clays[clay].Speed 	*= .70f;
                    Clays[clay].Vely    = Clays[clay].Speed * sinf(ConvertToN64(Clays[clay].AngleZ));
				}
			}
		}

		else
		{
			Flight[clay] += .35;

			Clays[clay].Position.x = Clays[clay].Init.x + (Clays[clay].Velx * Flight[clay] / 50.0f);
			Clays[clay].Position.y = Clays[clay].Init.y + (Clays[clay].Vely * Flight[clay] - .75 *
				32.2 * Flight[clay] * Flight[clay]) / 50.0f;
			Clays[clay].Position.z = Clays[clay].Init.z - (Clays[clay].Velz * Flight[clay] / 100.0f);
		}

		MoveObject(Clays[clay].ObjectID, &Clays[clay].Position);

		if (TRUE == Clays[clay].Rabbit && TRUE == Clays[clay].Flying)
		{
			if (Clays[clay].Init.x < 0)
			{
				if (Clays[clay].Position.x >= 0 && TRUE == CheckScreen(Clays[clay].ObjectID))
				{
					Clays[clay].Flying = FALSE;

        			if (TRUE == Clays[clay].Remove)
					{
						RemoveObject(Clays[clay].ObjectID);
					}
				}
			}

			else
			{
				if (Clays[clay].Position.x <= 0 && TRUE == CheckScreen(Clays[clay].ObjectID))
				{
					Clays[clay].Flying = FALSE;

        			if (TRUE == Clays[clay].Remove)
					{
						RemoveObject(Clays[clay].ObjectID);
					}
				}
			}
		}

		else if (Clays[clay].Position.z < ZOffscreen && TRUE == CheckScreen(Clays[clay].ObjectID))
		{
			Clays[clay].Flying = FALSE;

        	if (TRUE == Clays[clay].Remove)
			{
				RemoveObject(Clays[clay].ObjectID);
			}
		}

		else if (Clays[clay].Position.z < ZLimit && TRUE == CheckFloor(Clays[clay].ObjectID, Floor))
		{
			Clays[clay].Flying = FALSE;

			if (TRUE == Clays[clay].Remove)
			{
				RemoveObject(Clays[clay].ObjectID);
			}

			if (TRUE == Clays[clay].Water && FALSE == Clays[clay].Hit)
			{
				PlaySound(SPLASH_SND);
			}
		}
	}

	else if (Clays[clay].Delay > 0)
	{
		Clays[clay].Delay--;

		if (0 == Clays[clay].Delay)
		{
			Clays[clay].Flying = TRUE;

			PlaySound(LAUNCH_SND);
		}
	}

	return ERR_NOERROR;
}

BOOL CheckHit(long clay)
{
	BOOL 	RetVal = FALSE;
	long	distance;
	long	xdir;
	long	ydir;

	if (TRUE == Clays[clay].Flying && FALSE == Clays[clay].Hit && TRUE ==
		CheckIntersection(Clays[clay].ObjectID, Input.X, Input.Y, RANGE, &distance, &xdir, &ydir))
	{
		RetVal			= TRUE;
		Clays[clay].Hit	= TRUE;

		ClaysHit[Player]++;

		if (TRUE == Clays[clay].Rabbit)
		{
			Clays[clay].Flying 	= FALSE;

			Clays[clay].Rotation.x = 90 - 10;
			RotateObject(Clays[clay].ObjectID, &Clays[clay].Rotation);

			Clays[clay].Position.y = Clays[clay].Init.y;
			MoveObject(Clays[clay].ObjectID, &Clays[clay].Position);

			SetObjectFrame(Clays[clay].ObjectID, 1);

			if (TRUE == Clays[clay].Remove)
			{
				RemoveObject(Clays[clay].ObjectID);
			}
		}

		else if (distance < INNERRANGE)
		{
			SetObjectFrame(Clays[clay].ObjectID, 1);
		}

		else
		{
			if (xdir < 0)
			{
				if (ydir < 0)
				{
					Clays[clay].Rotation.z	= 180 - 90;
				}

				else
				{
					Clays[clay].Rotation.z	= 270 - 90;
				}
			}

			else
			{
				if (ydir < 0)
				{
					Clays[clay].Rotation.z	= 90 - 90;
				}
			}

			SetObjectFrame(Clays[clay].ObjectID, 2);
			RotateObject(Clays[clay].ObjectID, &Clays[clay].Rotation);
		}

		PlaySound(HIT_SND);
	}

	return RetVal;
}

DWORD PrintScores()
{
	char	string[20];
	DWORD	Player = 0;

	DrawTextString("SCORE BOARD;", 4, 1);
	DrawTextString("\\\\\\\\\\\\\\\\\\\\\\\\", 4, 2);

	ClaysTotal[Player] += ClaysHit[Player];

	sprintf(string, "PLAYER %d;  %2d %3d", Player + 1, ClaysHit[Player], ClaysTotal[Player]);

	DrawTextString(string, 1, 4);
	DrawTextString("SHOOT TO CONTINUE.", 1, 12);

	while (1)
	{
	   	Flip();

        ReadInput(&Input, 0);

		if (Input.Type != CON_NONE)
		{
			if ((BUT_TRIGGER & Input.Buttons) != 0)
			{
				PlaySound(GUN_SND);

				break;
			}
		}
	}

	ClearText();

	return ERR_NOERROR;
}

DWORD WaitForStart(BOOL PullSound)
{
	DrawTextString("PRESS START.", 4, 12);

	while (1)
	{
    	Flip();

        ReadInput(&Input, 0);

		if (Input.Type != CON_NONE)
		{
			if (CON_MOUSE == Input.Type || CON_PAD == Input.Type)
			{
				SetMousePosition(Input.X - MOUSESIZE / 2, Input.Y - MOUSESIZE / 2);
			}

			if ((BUT_START & Input.Buttons) != 0)
			{
                if (TRUE == PullSound)
                {
                    PlaySound(PULL_SND);
                }

				break;
			}
		}
	}

	ClearText();

	return ERR_NOERROR;
}

