// vim: set ts=4 sw=4 tw=99 noet:
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
// Copyright (C) 2004 Lukasz Wlasinski.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
//     https://alliedmods.net/amxmodx-license

//
// TFCX Module
//

#include "amxxmodule.h"
#include "tfcx.h"


int damage;
int TA;
int weapon;
int aim;
CPlayer *pAttacker;
edict_t* tempEnt;
int tempInt;
bool ignoreDamage = false;

void Client_ResetHUD(void* mValue){
	if ( mPlayer ){
		mPlayer->clearStats = gpGlobals->time + 0.25f;
	}
}

/*
void Client_WeaponList(void* mValue){
  static int wpnList;
  static int iSlot;
  static const char* wpnName;

  switch (mState++) {
  case 0:
    wpnName = (const char*)mValue;
    break;
  case 1:
    iSlot = *(int*)mValue;
    break;
  case 7:
    int iId = *(int*)mValue;
    if ( (iId < 0 || iId >= MAX_WEAPONS ) || ( wpnList & (1<<iId) ) )
      break;

    wpnList |= (1<<iId);
    weaponData[iId].ammoSlot = iSlot;
    strcpy(weaponData[iId].fullName, wpnName );
	weaponData[iId].name = weaponData[iId].fullName;

	char* wpnPrefix = strstr( weaponData[iId].fullName ,"tf_weapon_");

	if ( wpnPrefix )
		weaponData[iId].name = wpnPrefix + 10;
  } 
}
*/

int get_pdata_ehandle(edict_t* pEntity, int offset)
{
	if (FNullEnt(pEntity) || !pEntity->pvPrivateData)
	{
		return 0;
	}

	edict_t* pEdict = *(edict_t **)((char *)(pEntity->pvPrivateData) + offset);

	if (!pEdict)
	{
		return 0;
	}

	static edict_t* pWorld = nullptr;

	if (!pWorld)
	{
		pWorld = INDEXENT(0);
	}
		
	int index = pEdict - pWorld;

	if (index < 0 || index > gpGlobals->maxEntities)
	{
		return 0;
	}

	if (pEdict->free || !pEdict->pvPrivateData)
	{
		return 0;
	}

	int serialnumber = *(int *)((char *)pEntity->pvPrivateData + offset + 4);

	if (pEdict->serialnumber != serialnumber)
	{
		return 0;
	}

	return index;
}

void Client_Damage(void* mValue){
  switch (mState++) {
  case 1: 
    damage = *(int*)mValue;
    break;

  case 2:
	edict_t* enemy;
    if ( !mPlayer || !damage )  
		break;

	enemy = mPlayer->pEdict->v.dmg_inflictor;

	if ( FNullEnt( enemy ) )
		break;

	if (enemy->v.flags & (FL_CLIENT | FL_FAKECLIENT) ) { // attacker is player and his active weapon

		pAttacker = GET_PLAYER_POINTER(enemy);
		
		aim = pAttacker->aiming;
		weapon = pAttacker->current;

		switch(weapon){
		case TFC_WPN_SPANNER:
		case TFC_WPN_AXE:
		case TFC_WPN_KNIFE:
			pAttacker->saveShot(weapon); // melee , save shot too
			break;

		case TFC_WPN_MEDIKIT:
			if ( pAttacker->teamId == mPlayer->teamId ) // ???
				ignoreDamage = true;
			pAttacker->saveShot(weapon); // melee , save shot too
			break;
		}
	
		pAttacker->saveHit( mPlayer , weapon , damage, aim);

	}
	else if ( !FNullEnt( enemy->v.owner ) ){
		if ( enemy->v.owner->v.flags & (FL_CLIENT | FL_FAKECLIENT)  ){ // caltrop, empgrenade, gasgrenade, napalmgrenade

			pAttacker = GET_PLAYER_POINTER(enemy->v.owner);

			const char *szClass = STRING(enemy->v.classname);
			
			switch(szClass[10]){
				case 'c':
					weapon = TFC_WPN_CALTROP;
					break;
				case 'e':
					weapon = TFC_WPN_EMPGRENADE;
					break;
				case 'g':
					weapon = TFC_WPN_GASGRENADE;
					break;
				case 'm':
					weapon = TFC_WPN_MEDIKIT;
					if ( pAttacker->teamId == mPlayer->teamId ) // ???
						ignoreDamage = true;
					pAttacker->saveShot(weapon);
					break;
				case 'n':
					weapon = TFC_WPN_NAPALMGRENADE;
					break;
				case 'a':
				case 's':
				case '_': // hmm...nailgun_nail, fastswitch and this one may not be true...
					//PRINT_CONSOLE("Nail! Class:%d wpn:%d\n",pAttacker->classId,pAttacker->current);
					weapon = pAttacker->current;
					aim = pAttacker->aiming;
					break;
				case 'r':
					weapon = TFC_WPN_FLAMETHROWER;
					break;
			}
			if ( !weapon ) {
				switch(szClass[3]){
				case 'e':
					weapon = TFC_WPN_TIMER; // TFC_WPN_MEDKIT ??

					tempInt = get_pdata_ehandle(mPlayer->pEdict, pdTimerOwner * 4); // function is char-based.

					if (tempInt < 1 || tempInt > gpGlobals->maxClients)
					{
						break;
					}

					pAttacker = GET_PLAYER_POINTER_I(tempInt);

					if (pAttacker->teamId == mPlayer->teamId) // ???
					{
						ignoreDamage = true;
					}

					pAttacker->saveShot(weapon); // ??? save shot too
					break;
				case 'f':
					weapon = TFC_WPN_FLAMES; // tf_fire
					break;
				}
			}

			if ( weapon )
				pAttacker->saveHit( mPlayer , weapon , damage, aim );
			//else 
			//	PRINT_CONSOLE("*** DMG! Att:%d Vic:%d unknown weapon %s\n",pAttacker->index,mPlayer->index,STRING(enemy->v.classname));
		}
		// nailgrenadenail->nailgrenade->player :))) I'm the best !
		else if ( !FNullEnt(enemy->v.owner->v.owner) && enemy->v.owner->v.owner->v.flags & (FL_CLIENT | FL_FAKECLIENT ) ){
			pAttacker = GET_PLAYER_POINTER(enemy->v.owner->v.owner);
			weapon = TFC_WPN_NAILGRENADE;
			pAttacker->saveHit( mPlayer , weapon , damage, aim );
		}
	}
    else { // nailgrenade , mirvgrenade , normalgrenade , rockets
		if ( strstr("sentrygun",STRING(enemy->v.classname)) )
		{
			tempInt = get_pdata_ehandle(mPlayer->pEdict, pdSentryGunOwner * 4); // function is char-based.

			if (tempInt < 1 || tempInt > gpGlobals->maxClients)
			{
				break;
			}

			pAttacker = GET_PLAYER_POINTER_I(tempInt);
			weapon = TFC_WPN_SENTRYGUN;
			pAttacker->saveShot(weapon); // save shot too
			pAttacker->saveHit( mPlayer , weapon , damage, aim );
		}
		else if( g_grenades.find(enemy , &pAttacker , &weapon ) ){
			if ( weapon == TFC_WPN_RPG && pAttacker->classId == TFC_PC_PYRO ){
				weapon = TFC_WPN_IC;
				pAttacker->saveShot(weapon);
			}
			
			pAttacker->saveHit( mPlayer , weapon , damage, aim );
		}
	}
	break;
  }
  
}

void Client_Damage_End(void* mValue){
	if ( !damage )
		return;

	if ( !pAttacker )
		pAttacker = mPlayer;

	if ( !ignoreDamage ){
		TA = 0;
		if ( (mPlayer->teamId == pAttacker->teamId) && (mPlayer != pAttacker) )
			TA = 1;
		if (g_damage_info > 0)
			MF_ExecuteForward(g_damage_info, pAttacker->index, mPlayer->index, damage, weapon, aim, TA);
	
		if( !mPlayer->IsAlive() ){
			pAttacker->saveKill(mPlayer,weapon,( aim == 1 ) ? 1:0 ,TA);
			
			if (g_death_info > 0)
				MF_ExecuteForward(g_death_info, pAttacker->index, mPlayer->index, weapon, aim, TA);
		}
	}

	damage = 0;
	aim = 0;
	weapon = 0;
	pAttacker = NULL;
	ignoreDamage = false;

}

void Client_CurWeapon(void* mValue){
  
  static int iState;
  static int iId;
  switch (mState++){
  case 0: 
    iState = *(int*)mValue;
    break;
  case 1:
    if (!iState) break; 
    iId = *(int*)mValue;
    break;
  case 2:
	if (!mPlayer || !iState ) break;
    int iClip = *(int*)mValue;
    
	if ((iClip > -1) && (iClip < mPlayer->weapons[iId].clip)){
		mPlayer->saveShot(iId);
	}
	
    mPlayer->weapons[iId].clip = iClip;
	mPlayer->current = iId;
  }
}

void Client_AmmoX(void* mValue){
  
  static int iAmmo;
  switch (mState++){
  case 0:
    iAmmo = *(int*)mValue;
    break;
  case 1:
	if (!mPlayer ) break;

	// SniperRifle, AC and AutoRifle ...
	if ( mPlayer->classId == TFC_PC_HWGUY ){
		if ( mPlayer->current == TFC_WPN_AC && mPlayer->weapons[mPlayer->current].ammo > *(int*)mValue  && iAmmo == weaponData[mPlayer->current].ammoSlot )
			mPlayer->saveShot(mPlayer->current);
	}
	else if ( mPlayer->classId == TFC_PC_SNIPER ){
		if ( (mPlayer->current == TFC_WPN_SNIPERRIFLE || mPlayer->current == TFC_WPN_AUTORIFLE) && mPlayer->weapons[mPlayer->current].ammo > *(int*)mValue && iAmmo == weaponData[mPlayer->current].ammoSlot )
			mPlayer->saveShot(mPlayer->current);
	}
	//

    for(int i = 1; i < MAX_WEAPONS ; ++i) 
      if (iAmmo == weaponData[i].ammoSlot)
        mPlayer->weapons[i].ammo = *(int*)mValue;
  }
}

void Client_AmmoPickup(void* mValue){
  
  static int iSlot;
  switch (mState++){
  case 0:
    iSlot = *(int*)mValue;
    break;
  case 1:
	if (!mPlayer ) break;
    for(int i = 1; i < MAX_WEAPONS ; ++i)
      if (weaponData[i].ammoSlot == iSlot)
        mPlayer->weapons[i].ammo += *(int*)mValue;
  }
}

void Client_ScoreInfo(void* mValue){
  static int iIndex;
  static int iClass;
  switch (mState++){
  case 0:
    iIndex = *(int*)mValue;
    break;
  case 3:
	  iClass = *(int*)mValue;
	  break;
  case 4:
	  if ( iIndex > 0 && iIndex <= gpGlobals->maxClients ){
		  GET_PLAYER_POINTER_I( iIndex )->teamId = *(int*)mValue;
		  GET_PLAYER_POINTER_I( iIndex )->classId = iClass;
	  }

  }
}