#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>
#include <dhooks>

#define FVPHYSICS_NO_IMPACT_DMG			0x0400		// don't do impact damage to anything

public Plugin myinfo =
{
	name 			= "FixVPhysicsCrush",
	author 			= "BotoX",
	description 	= "",
	version 		= "0.0",
	url 			= ""
};

Handle g_hAcceptInput;
Handle g_hSetGameFlags;
Handle g_hGetGameFlags;
int g_hPhysicsObject;


public void OnPluginStart()
{
	Handle hGameData = LoadGameConfigFile("sdktools.games");
	if(!hGameData)
		SetFailState("Couldn't load sdktools gamedata.");

	// CBaseEntity::AcceptInput( const char *szInputName, CBaseEntity *pActivator, CBaseEntity *pCaller, variant_t Value, int outputID )
	int Offset = GameConfGetOffset(hGameData, "AcceptInput");
	if(Offset == -1)
		SetFailState("Failed to find AcceptInput offset.");

	g_hAcceptInput = DHookCreate(Offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity, OnAcceptInput);
	if(g_hAcceptInput == INVALID_HANDLE)
		SetFailState("Failed to DHook AcceptInput.");

	DHookAddParam(g_hAcceptInput, HookParamType_CharPtr);
	DHookAddParam(g_hAcceptInput, HookParamType_CBaseEntity);
	DHookAddParam(g_hAcceptInput, HookParamType_CBaseEntity);
	DHookAddParam(g_hAcceptInput, HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); //varaint_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20
	DHookAddParam(g_hAcceptInput, HookParamType_Int);

	hGameData = LoadGameConfigFile("FixVPhysicsCrush.games");
	if(!hGameData)
		SetFailState("Couldn't load FixVPhysicsCrush gamedata.");

	// CPhysicsObject::SetGameFlags(unsigned short)
	StartPrepSDKCall(SDKCall_Raw);
	if(!PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "SetGameFlags"))
	{
		delete hGameData;
		SetFailState("PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, \"SetGameFlags\") failed!");
	}
	PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
	g_hSetGameFlags = EndPrepSDKCall();

	// CPhysicsObject::GetGameFlags()
	StartPrepSDKCall(SDKCall_Raw);
	if(!PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, "GetGameFlags"))
	{
		delete hGameData;
		SetFailState("PrepSDKCall_SetFromConf(hGameData, SDKConf_Virtual, \"GetGameFlags\") failed!");
	}
	PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
	g_hGetGameFlags = EndPrepSDKCall();

	int hCollisionGroup = FindSendPropInfo("CBaseEntity", "m_CollisionGroup");
	if (hCollisionGroup == -1)
		SetFailState("Couldn't find CBaseEntity::m_CollisionGroup");

	// CNetworkVar( int, m_CollisionGroup );
	// IPhysicsObject	*m_pPhysicsObject;
	g_hPhysicsObject = hCollisionGroup + 4;

	// Late load.
	int entity = INVALID_ENT_REFERENCE;
	while((entity = FindEntityByClassname(entity, "phys_thruster")) != INVALID_ENT_REFERENCE)
	{
		OnEntityCreated(entity, "phys_thruster");
	}
}

public void OnEntityCreated(int entity, const char[] classname)
{
	if(StrEqual(classname, "phys_thruster"))
	{
		DHookEntity(g_hAcceptInput, true, entity);
	}
}

public MRESReturn OnAcceptInput(int entity, Handle hReturn, Handle hParams)
{
	if(!IsValidEntity(entity))
		return;

	char sCommand[128];
	DHookGetParamString(hParams, 1, sCommand, sizeof(sCommand));

	if(StrEqual(sCommand, "Activate", false))
	{
		int attachedObject = GetEntPropEnt(entity, Prop_Data, "m_attachedObject");
		if(attachedObject != INVALID_ENT_REFERENCE)
		{
			int vphysObj = GetVPhysicsObject(attachedObject);
			if(vphysObj)
			{
				int flags = VPhysicsGetGameFlags(vphysObj);
				flags |= FVPHYSICS_NO_IMPACT_DMG;
				VPhysicsSetGameFlags(vphysObj, flags);
			}
		}
	}
}

int VPhysicsGetGameFlags(int vphysicsObj)
{
	return SDKCall(g_hGetGameFlags, vphysicsObj);
}

void VPhysicsSetGameFlags(int vphysicsObj, int flags)
{
	SDKCall(g_hSetGameFlags, vphysicsObj, flags);
}

int GetVPhysicsObject(int entity)
{
	return GetEntData(entity, g_hPhysicsObject, 4);
}
