//=========================================================================================
// Project : UT2004 HackBase
// Version : 1.0
// Coder   : CheatatOr
// Website : http://cT.cheatz.to
//
// File    : main.cpp
// ++      : The main bot class with general functions, global initializations and the hook
// Credits : Please read the readme.html for getting the credits
//=========================================================================================

//=========================================================================================
// 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.
//
// 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, see <http://www.gnu.org/licenses/>.
//=========================================================================================

#include "hook.h"
#include "cRadar.h"
#include "cHacks.h"

//=====================================================================================
// Global Settings:
//=====================================================================================

bool Global::bHook = true; 
bool Global::bHealthbars = true; 
bool Global::bFF = false; 
bool Global::bRadar = true;
bool Global::b2DRadar = true;
int  Global::iWallHack = 1;

float Global::TickDeltaTime = 0;

int Global::RadarPosX = 10;
int Global::RadarPosY = 167;
int Global::RadarWidth = 134;
int Global::RadarHeight = 138;
int	Global::RadarDistance = 60;

FVector Global::MyCameraLocation = FVector(0,0,0); 
FRotator Global::MyCameraRotation = FRotator(0,0,0); 

UFont* Global::MySmallFont = NULL;
UTexture* Global::WhiteTex = NULL;
APlayerController* Global::PC = NULL;

TArray<APawn*> PawnList; //TArray for storing APawn data

Bot ABot;

Bot::Bot()
{
	LoadFont(Global::MySmallFont, "Engine.DefaultFont");
	LoadTexture(Global::WhiteTex, "Engine.WhiteTexture");

	//TODO: Adding other initializations here
}

Bot::~Bot(){
};

//=====================================================================================
// The bot class main function, this is where all the magic happens:
//=====================================================================================

void Bot::Main (UCanvas* Canvas)
{
	if (ValidRender(Canvas))
	{
		Global::PC = Canvas->Viewport->Actor;

		GetCameraLocation(Canvas,Global::MyCameraLocation,Global::MyCameraRotation);

		//Make sure that our Controller is valid
		if ((Global::PC != NULL) && (Global::PC->Pawn != NULL) && (Global::bHook) )
		{
			//Everything not related to pawns has to be drawn outside the loop
			if(Global::b2DRadar)
				MyRadar->Draw2DRadarBox(Canvas);

			for(int i = 0; i < PawnList.Num(); i++)
			{
				APawn *Target = PawnList(i);
				if(!Target)
					continue;

				if (ValidTarget(Target))
				{
					Canvas->DrawColor = GetTeamColor(Target);
					if (Global::b2DRadar)
						MyRadar->DrawPlayer2DRadar(Canvas, Target);
					if (Global::bRadar)
						MyRadar->DrawBoundingBox(Canvas, Target);
					switch (Global::iWallHack)
					{
						case 1: //normal
							MyHacks->WallHack(Target, Canvas, false);
							break;
						case 2: //wireframe
							MyHacks->WallHack(Target, Canvas, true);
							break;
					}
				}
			}
		}
	}
}

//=====================================================================================
// Player / Canvas Validation:
//=====================================================================================

bool inline Bot::ValidTarget (APawn* Target)
{
	return	(Target != NULL) &&
		    (Target != Global::PC->Pawn) &&
			(!Target->bHidden) &&
			(!Target->bDeleteMe) &&
			(Target->Health > 0) &&
			(Target->Visibility != 0) &&
			(Target->PlayerReplicationInfo != NULL) &&
			(!Target->PlayerReplicationInfo->bIsSpectator) &&
			(!Target->PlayerReplicationInfo->bWaitingPlayer) &&
			(Target->Level == Global::PC->Level);
}

bool inline Bot::ValidRender (UCanvas* Canvas)
{
	return	(Canvas != NULL) &&
			(Canvas->Viewport != NULL) &&
			(Canvas->Viewport->Actor != NULL) &&
			(Canvas->Viewport->Actor->PlayerReplicationInfo != NULL) &&
			(Canvas->Viewport->Actor->XLevel != NULL);
}

//=====================================================================================
// Team Colors:
//=====================================================================================

FColor inline Bot::GetTeamColor (APawn * Target)
{
	if (Target->PlayerReplicationInfo->Team != NULL)
	{
		switch (Target->PlayerReplicationInfo->Team->TeamIndex)
		{
			case 0:
				return ColorLightBlue;
				break;

			case 1:
				return ColorRed;
				break;

			default:
				return ColorGreen;
				break;
		}
	}
	return ColorWhite;
}

//=====================================================================================
// Maths:
//=====================================================================================

void inline Bot::GetAxes (FRotator R, FVector &X, FVector &Y, FVector &Z)
{
	X = R.Vector();

	FRotator R2 = R;
	R2.Yaw += 16384;
	R2.Pitch = 0.f;

	Y = R2.Vector();
	Y.Z = 0.f;

	R.Pitch += 16384;
	Z = R.Vector();
}

float inline Bot::Dot (const FVector& V1, const FVector& V2)
{
	return (V1.X*V2.X + V1.Y*V2.Y + V1.Z*V2.Z);
}

//=====================================================================================
// Unreal Engine 1 like drawing:
//=====================================================================================

void Bot::Draw(UCanvas *Canvas, float PosX, float PosY, float Width, float Height, FColor Color)
{
	Canvas->pCanvasUtil->DrawTile(PosX, PosY, PosX+Width, PosY+Height, 0.0,0.0,92.0,92.0,0.0,Global::WhiteTex,Color);
}

//=====================================================================================
// Enemy identifying:
//=====================================================================================

bool inline Bot::IsEnemyz (APawn* Target)
{
	if (Target->PlayerReplicationInfo->Team)
	{
		if (Global::bFF) //Frendly Fire: returns teammates on team based game type
			return (Target->PlayerReplicationInfo->Team->TeamIndex == Global::PC->PlayerReplicationInfo->Team->TeamIndex);
		else if (Target->PlayerReplicationInfo->Team->TeamIndex > 1)
			return false;
	   	return Target->PlayerReplicationInfo->Team->TeamIndex != Global::PC->PlayerReplicationInfo->Team->TeamIndex;
	}
    return true;
}

//=====================================================================================
// Unreal world to screen co-ordinates:
//=====================================================================================

FVector Bot::WorldToScreen (UCanvas* Canvas, FVector WorldLocation)
{
	FVector X,Y,Z,D,Out;
	GetAxes(Global::MyCameraRotation,X,Y,Z);
	D = WorldLocation - Global::MyCameraLocation;
	Out.X = (Canvas->ClipX/2)+( Dot(D,Y))*((Canvas->ClipX/2)/tan(Global::PC->FovAngle*PI/360))/Dot(D,X);
	Out.Y = (Canvas->ClipY/2)+(-Dot(D,Z))*((Canvas->ClipX/2)/tan(Global::PC->FovAngle*PI/360))/Dot(D,X);
	Out.Z = 0;
	return Out;
}

//=====================================================================================
// Black shadowed text drawing:
//=====================================================================================

void Bot::DrawShadowedText(UCanvas* Canvas, FString Str, float PosX, float PosY, FColor Color)
{
	static TCHAR StrTemp[MAX_PATH];
	appSprintf(StrTemp, TEXT("%s"), Str);

	Canvas->pCanvasUtil->DrawString(PosX-1,PosY,(const unsigned short *)StrTemp,Global::MySmallFont,  ColorBlack,1);
	Canvas->pCanvasUtil->DrawString(PosX+1,PosY,(const unsigned short *)StrTemp,Global::MySmallFont,  ColorBlack,1);
	Canvas->pCanvasUtil->DrawString(PosX,PosY-1,(const unsigned short *)StrTemp,Global::MySmallFont,  ColorBlack,1);
	Canvas->pCanvasUtil->DrawString(PosX,PosY+1,(const unsigned short *)StrTemp,Global::MySmallFont,  ColorBlack,1);

	Canvas->pCanvasUtil->DrawString(PosX,PosY,(const unsigned short *)StrTemp,Global::MySmallFont, Color,1);
}

//=====================================================================================
// Get camera location without hooking by [Dē]Keytonic:
//=====================================================================================

void Bot::GetCameraLocation (UCanvas* Canvas, FVector &InputLocation, FRotator &InputRotation) 
{
	struct UCanvas_execGetCameraLocation_Parms 
	{ 
		class FVector CameraLocation; 
		class FRotator CameraRotation; 
	}; 

	static UCanvas_execGetCameraLocation_Parms Parms; 
	
	UFunction* GetCameraLocation = Canvas->FindFunction(TEXT("GetCameraLocation"));  
	
	if(GetCameraLocation == NULL)return;
	
	Canvas->ProcessEvent(GetCameraLocation, &Parms, 0);  
	
	InputLocation = Parms.CameraLocation;  
	InputRotation = Parms.CameraRotation; 
}

//=====================================================================================
// Begin of the detour hook:
//=====================================================================================

typedef void(__stdcall *typedef_PostRender) (class FSceneNode *);	
typedef_PostRender original_PostRender = NULL;

typedef void(__stdcall *typedef_APawnCon) ();	
typedef_APawnCon original_APawnCon = NULL;

typedef void(__stdcall *typedef_APawnDestr) ();	
typedef_APawnDestr original_APawnDestr = NULL;

void WINAPI xPostRender(class FSceneNode *FS)
{
	original_PostRender(FS);
	ABot.Main(FS->Viewport->Canvas);
}
//=====================================================================================
// APawn constructor/deconstructor hooking by [Dē]Keytonic:
//=====================================================================================

void __declspec(naked) xAPawn_constructor()
{
	_asm pushad
	APawn* pThis;
	_asm mov pThis,ecx
	PawnList.AddItem(pThis);
	_asm popad
	_asm jmp original_APawnCon
}

void __declspec(naked) xAPawn_deconstructor()
{
	_asm pushad
	APawn* pThis;
	_asm mov pThis,ecx
	PawnList.RemoveItem(pThis);
	_asm popad
	_asm jmp original_APawnDestr
}

//=====================================================================================
// Loader Thread for hooking needed functions:
//=====================================================================================

DWORD WINAPI LoaderThread( LPVOID lpParam )
{
	while(GetModuleHandle(L"Engine") == NULL)
	{
		Sleep(1000);
	}

	PBYTE iOffset = (PBYTE)GetProcAddress(GetModuleHandleA("Engine.dll"),"?PostRender@APlayerController@@UAEXPAVFSceneNode@@@Z");
	original_PostRender = (typedef_PostRender)DetourFunction(iOffset,(PBYTE)xPostRender);

	original_APawnCon = (typedef_APawnCon)DetourFunction((PBYTE)GetProcAddress(GetModuleHandleA("Engine.dll"),"??0APawn@@QAE@XZ"),(PBYTE)xAPawn_constructor);
	original_APawnDestr = (typedef_APawnDestr)DetourFunction((PBYTE)GetProcAddress(GetModuleHandleA("Engine.dll"),"??1APawn@@UAE@XZ"),(PBYTE)xAPawn_deconstructor);

	return 1;
}

//=====================================================================================
// The "heart" of the DLL:
//=====================================================================================

BOOL __stdcall DllMain(HMODULE hModule, DWORD reason, PVOID)
{
	switch(reason)
	{
		case DLL_PROCESS_ATTACH:
		{
			DisableThreadLibraryCalls(hModule);
			CreateThread(0,0,LoaderThread,0,0,0);
			break;
		}
		default:
			return false;
	}
	return true;
}