/**
 *
 * Sniper Crosshairs
 *  by Numb
 *
 *
 * Description:
 *  Gives CS-Style non-zoom crosshairs to sniper rifles (not the small yellow one - the real
 *  crosshairs as in galil, ak47, sg552). See the "awp" screenshot for awp example.
 *
 *
 * Requires:
 *  FakeMeta
 *  HamSandWich
 *
 *
 * Cvars:
 *
 *  + "amx_sniper_crosshairs" - what weapons have crosshairs (just add the numbers).
 *  - "1" - awp
 *  - "2" - scout
 *  - "4" - g3sg1
 *  - "8" - sg550
 *
 *
 * Additional Info:
 *  Tested in Counter-Strike 1.6 with amxmodx 1.8.2 (dev build hg21).
 *
 *
 * Notes:
 *  Such a plugin already was made by ConnorMcLeod ( http://forums.alliedmods.net/member.php?u=18946 )
 *  and is called Real Snipers Crosshairs ( http://forums.alliedmods.net/showthread.php?t=88926 ).
 *  However there have been bug reports. Thanks to some research I found a bug-free method, and
 *  decided to make it publicly available. After some talk with Arkshine 
 *  ( http://forums.alliedmods.net/member.php?u=7779 ), we decided the best thing to do is post the
 *  plugin here of course.
 *
 *
 * Warnings:
 *  Only known issue is if you have awp currently out and you happen to have galil in your inventory
 *  at the same time. In that case the galil green-bullet-bar (in your inventory) will be showing
 *  the same bullet-percentage-filled as awp. See the "glitch_awp_out" & "glitch_galil_out"
 *  screenshots to understand better.
 *
 *
 * Credits:
 *  Special thanks to Arkshine ( http://forums.alliedmods.net/member.php?u=7779 ) for help in
 *  WeaponList ( http://forums.alliedmods.net/showthread.php?t=175632 ) related things. Also I'd like
 *  to thank ConnorMcLeod ( http://forums.alliedmods.net/member.php?u=18946 ) for his Real Snipers
 *  Crosshairs ( http://forums.alliedmods.net/showthread.php?t=88926 ) plugin, which allowed me to
 *  understand the basic method of how to add a crosshair to the weapon.
 *
 *
 * ChangeLog:
 *
 *  + 1.3
 *  - Fixed: Error "invalid player index".
 *
 *  + 1.2
 *  - Fixed: Hud glitches while having 'opposed' weapons.
 *  - Changed: Official release.
 *
 *  + 1.1
 *  - Fixed: Glitch with hud when having sniper and its 'opposed' weapon.
 *  - Fixed: Glitch when having m249 and awp, m249 bullet type isn't shown when buying them.
 *  - Changed: 'Opposed' for awp is now galil - not m249.
 *
 *  + 1.0
 *  - First release.
 *
 *
 * Downloads:
 *  Amx Mod X forums: http://forums.alliedmods.net/showthread.php?p=1741181#post1741181
 *
**/


#include <amxmodx>
#include <fakemeta>
#include <hamsandwich>

#define PLUGIN_NAME	"Sniper Crosshairs"
#define PLUGIN_VERSION	"1.3"
#define PLUGIN_AUTHOR	"Numb"

#define SetPlayerBit(%1,%2)    ( %1 |=  ( 1 << ( %2 & 31 ) ) )
#define ClearPlayerBit(%1,%2)  ( %1 &= ~( 1 << ( %2 & 31 ) ) )
#define CheckPlayerBit(%1,%2)  ( %1 &   ( 1 << ( %2 & 31 ) ) )


#define m_iFOV 363
#define MAX_ITEM_TYPES 6
#define m_rgpPlayerItems 367
#define m_pActiveItem 373

#define m_pNext 42
#define m_iId 43
#define m_iClip 51

new g_iConnected;
new g_iAlive;
new g_iChangedGalil;
new g_iMaxPlayers;

new g_iMsgId_WeaponList;

new g_iCvarSniperCrosshairs;

public plugin_precache()
{
	if( !file_exists("sprites/weapon_galil_338magnum.txt") )
	{
		#define SPRITE_CONFIG "10^n\
			weapon 320 320hud1 0 0 80 20^n\
			weapon_s 320 320hud1 0 20 80 20^n\
			ammo 320 640hud7 24 96 24 24^n\
			crosshair 320 crosshairs 24 0 24 24^n\
			autoaim 320 crosshairs 0 72 24 24^n\
			weapon 640 640hud17 0 45 170 45^n\
			weapon_s 640 640hud18 0 45 170 45^n\
			ammo 640 640hud7 24 96 24 24^n\
			crosshair 640 crosshairs 24 0 24 24^n\
			autoaim 640 crosshairs 0 72 24 24"
		
		if( !write_file("sprites/weapon_galil_338magnum.txt", SPRITE_CONFIG) )
			return;
	}
	precache_generic("sprites/weapon_galil_338magnum.txt");
}

/*
10
weapon			320 320hud1	0	0	80	20
weapon_s		320 320hud1	0	20	80	20
ammo			320 640hud7	24	96	24	24
crosshair		320 crosshairs	24	0	24	24
autoaim			320 crosshairs	0	72	24	24
weapon			640 640hud17	0	45	170     45
weapon_s		640 640hud18	0	45	170	45
ammo			320 640hud7	24	96	24	24
crosshair		640 crosshairs	24	0	24	24
autoaim			640 crosshairs	0	72	24	24

*/

public plugin_init()
{
	register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR);
	
	register_clcmd("weapon_galil_338magnum", "fix_galil_toggle");
	
	g_iCvarSniperCrosshairs = register_cvar("amx_sniper_crosshairs", "15"); // 1 = awp, 2 = scout, 4 = g3sg1, 8 = sg550
	
	register_message(get_user_msgid("CurWeapon"), "Message_CurWeapon");
	
	register_event("ResetHUD", "Event_ResetHUD", "be");
	register_event("Health",   "Event_Health",   "bd");
	
	RegisterHam(Ham_Item_AttachToPlayer, "weapon_galil", "Ham_AttachToPlr_338magnum_Post", 1);
	RegisterHam(Ham_Item_AttachToPlayer, "weapon_awp",   "Ham_AttachToPlr_338magnum_Post", 1);
	
	RegisterHam(Ham_Item_AttachToPlayer, "weapon_ak47",  "Ham_AttachToPlr_762nato_Post", 1);
	RegisterHam(Ham_Item_AttachToPlayer, "weapon_scout", "Ham_AttachToPlr_762nato_Post", 1);
	RegisterHam(Ham_Item_AttachToPlayer, "weapon_g3sg1", "Ham_AttachToPlr_762nato_Post", 1);
	
	RegisterHam(Ham_Item_AttachToPlayer, "weapon_sg552", "Ham_AttachToPlr_556nato_Post", 1);
	RegisterHam(Ham_Item_AttachToPlayer, "weapon_sg550", "Ham_AttachToPlr_556nato_Post", 1);
	
	g_iMsgId_WeaponList = get_user_msgid("WeaponList");
	g_iMaxPlayers = clamp(get_maxplayers(), 1, 32);
}

public fix_galil_toggle(iPlrId)
{
	if( CheckPlayerBit(g_iChangedGalil, iPlrId) )
		engclient_cmd(iPlrId, "weapon_galil");
}

public client_connect(iPlrId)
{
	ClearPlayerBit(g_iConnected, iPlrId);
	ClearPlayerBit(g_iAlive, iPlrId);
	ClearPlayerBit(g_iChangedGalil, iPlrId);
}

public client_putinserver(iPlrId)
	SetPlayerBit(g_iConnected, iPlrId);

public client_disconnect(iPlrId)
	ClearPlayerBit(g_iConnected, iPlrId);

public plugin_pause()
{
	new iPlayers[32], iPlayerNum;
	get_players(iPlayers, iPlayerNum);
	for( new iLoop; iLoop<=iPlayerNum; iLoop++ )
	{
		if( CheckPlayerBit(g_iChangedGalil, iPlayers[iLoop]) )
		{
			message_begin(MSG_ONE, g_iMsgId_WeaponList, _, iPlayers[iLoop]);
			write_string("weapon_galil");
			write_byte(3);
			write_byte(200);
			write_byte(-1);
			write_byte(-1);
			write_byte(0);
			write_byte(4);
			write_byte(CSW_GALIL);
			write_byte(0);
			message_end();
		}
	}
	g_iChangedGalil = 0;
}

public plugin_unpause()
{
	g_iAlive = 0;
	g_iConnected = 0;
	
	for( new iPlrId=1; iPlrId<=g_iMaxPlayers; iPlrId++ )
	{
		if( is_user_alive(iPlrId) )
		{
			SetPlayerBit(g_iConnected, iPlrId);
			SetPlayerBit(g_iAlive, iPlrId);
			fix_weapon_pattern(iPlrId, CSW_AWP, CSW_GALIL);
			fix_weapon_pattern(iPlrId, CSW_SCOUT, CSW_AK47, CSW_G3SG1);
			fix_weapon_pattern(iPlrId, CSW_SG550, CSW_SG552);
		}
		else if( is_user_connected(iPlrId) )
			SetPlayerBit(g_iConnected, iPlrId);
	}
}

public Message_CurWeapon(iMsgId, iMsgType, iPlrId)
{
	if( !CheckPlayerBit(g_iConnected, iPlrId) )
		return PLUGIN_CONTINUE;
	
	static s_iWeaponType;
	s_iWeaponType = get_msg_arg_int(2);
	
	if( !get_msg_arg_int(1) )
	{
		if( !CheckPlayerBit(g_iAlive, iPlrId) || get_pdata_int(iPlrId, m_iFOV, 5)!=90 )
			return PLUGIN_CONTINUE;
		
		static s_iActiveItem;
		s_iActiveItem = get_pdata_cbase(iPlrId, m_pActiveItem, 5);
		if( s_iActiveItem<=0 )
			return PLUGIN_CONTINUE;
		
		switch( get_pdata_int(s_iActiveItem, m_iId, 4) )
		{
			case CSW_AWP:
			{
				if( s_iWeaponType==CSW_GALIL )
					set_msg_arg_int(3, ARG_BYTE, get_pdata_int(s_iActiveItem, m_iClip, 4));
			}
			case CSW_SCOUT, CSW_G3SG1:
			{
				if( s_iWeaponType==CSW_AK47 )
					set_msg_arg_int(3, ARG_BYTE, get_pdata_int(s_iActiveItem, m_iClip, 4));
			}
			case CSW_SG550:
			{
				if( s_iWeaponType==CSW_SG552 )
					set_msg_arg_int(3, ARG_BYTE, get_pdata_int(s_iActiveItem, m_iClip, 4));
			}
		}
		
		return PLUGIN_CONTINUE;
	}
	
	if( s_iWeaponType==CSW_AWP )
	{
		if( is_user_unzoomed(iPlrId) && get_pcvar_num(g_iCvarSniperCrosshairs)&1 )
		{
			if( !CheckPlayerBit(g_iChangedGalil, iPlrId) )
			{
				SetPlayerBit(g_iChangedGalil, iPlrId);
				
				message_begin(MSG_ONE, g_iMsgId_WeaponList, _, iPlrId);
				write_string("weapon_galil_338magnum");
				write_byte(1);
				write_byte(30);
				write_byte(-1);
				write_byte(-1);
				write_byte(0);
				write_byte(17);
				write_byte(CSW_GALIL);
				write_byte(0);
				message_end();
			}
			
			message_begin(MSG_ONE, iMsgId, _, iPlrId);
			write_byte(1);
			write_byte(CSW_AWP);
			write_byte(get_msg_arg_int(3));
			message_end();
			
			set_msg_arg_int(2, ARG_BYTE, CSW_GALIL);
		}
		
		return PLUGIN_CONTINUE;
	}
	else if( CheckPlayerBit(g_iChangedGalil, iPlrId) )
	{
		ClearPlayerBit(g_iChangedGalil, iPlrId);
		
		message_begin(MSG_ONE, g_iMsgId_WeaponList, _, iPlrId);
		write_string("weapon_galil");
		write_byte(4);
		write_byte(90);
		write_byte(-1);
		write_byte(-1);
		write_byte(0);
		write_byte(17);
		write_byte(CSW_GALIL);
		write_byte(0);
		message_end();
	}
	
	if( s_iWeaponType==CSW_SCOUT )
	{
		if( is_user_unzoomed(iPlrId) && get_pcvar_num(g_iCvarSniperCrosshairs)&2 )
		{
			message_begin(MSG_ONE, iMsgId, _, iPlrId);
			write_byte(1);
			write_byte(CSW_SCOUT);
			write_byte(get_msg_arg_int(3));
			message_end();
			
			set_msg_arg_int(2, ARG_BYTE, CSW_AK47);
		}
	}
	else if( s_iWeaponType==CSW_G3SG1 )
	{
		if( is_user_unzoomed(iPlrId) && get_pcvar_num(g_iCvarSniperCrosshairs)&4 )
		{
			message_begin(MSG_ONE, iMsgId, _, iPlrId);
			write_byte(1);
			write_byte(CSW_G3SG1);
			write_byte(get_msg_arg_int(3));
			message_end();
			
			set_msg_arg_int(2, ARG_BYTE, CSW_AK47);
		}
	}
	else if( s_iWeaponType==CSW_SG550 )
	{
		if( is_user_unzoomed(iPlrId) && get_pcvar_num(g_iCvarSniperCrosshairs)&8 )
		{
			message_begin(MSG_ONE, iMsgId, _, iPlrId);
			write_byte(1);
			write_byte(CSW_SG550);
			write_byte(get_msg_arg_int(3));
			message_end();
			
			set_msg_arg_int(2, ARG_BYTE, CSW_SG552);
		}
	}
	
	return PLUGIN_CONTINUE; // yes, we must make sure that original message gets to client, before we can modify stuff
}

public Event_ResetHUD(iPlrId)
{
	if( is_user_alive(iPlrId) )
		SetPlayerBit(g_iAlive, iPlrId);
	else
		ClearPlayerBit(g_iAlive, iPlrId);
}

public Event_Health(iPlrId)
{
	if( is_user_alive(iPlrId) )
		SetPlayerBit(g_iAlive, iPlrId);
	else
		ClearPlayerBit(g_iAlive, iPlrId);
}

public Ham_AttachToPlr_338magnum_Post(iEnt, iPlrId)
	fix_weapon_pattern(iPlrId, CSW_AWP, CSW_GALIL);

public Ham_AttachToPlr_762nato_Post(iEnt, iPlrId)
	fix_weapon_pattern(iPlrId, CSW_SCOUT, CSW_AK47, CSW_G3SG1);

public Ham_AttachToPlr_556nato_Post(iEnt, iPlrId)
	fix_weapon_pattern(iPlrId, CSW_SG550, CSW_SG552);

bool:fix_weapon_pattern(iPlrId, iScopeWpn, iNonScopeWpn, iSecondaryScopeWpn=0) // returns true if fix was made
{
	new iEnt, iPrev, iNext, bool:bFoundScope, iInfo[3], iType;
	
	for( new iSlot; iSlot<MAX_ITEM_TYPES; iSlot++ )
	{
		iEnt = get_pdata_cbase(iPlrId, (m_rgpPlayerItems+iSlot), 5);
		iPrev = (iSlot*-1);
		
		while( iEnt>0 )
		{
			iNext = get_pdata_cbase(iEnt, m_pNext, 4);
			iType = get_pdata_int(iEnt, m_iId, 4);
			
			if( iType==iScopeWpn || (iSecondaryScopeWpn && iType==iSecondaryScopeWpn) )
			{
				if( !bFoundScope )
				{
					bFoundScope = true;
					
					iInfo[0] = iPrev;
					iInfo[1] = iEnt;
					iInfo[2] = iNext;
				}
			}
			else if( iType==iNonScopeWpn )
			{
				if( bFoundScope )
				{
					if( iPrev>0 )
					{
						if( iPrev!=iInfo[1] )
							set_pdata_cbase(iPrev, m_pNext, iInfo[1], 4);
					}
					else
						set_pdata_cbase(iPlrId, (m_rgpPlayerItems+(iPrev*-1)), iInfo[1], 5);
					set_pdata_cbase(iInfo[1], m_pNext, iNext, 4);
					
					if( iInfo[0]>0 )
						set_pdata_cbase(iInfo[0], m_pNext, iEnt, 4);
					else
						set_pdata_cbase(iPlrId, (m_rgpPlayerItems+(iInfo[0]*-1)), iEnt, 5);
					
					if( iInfo[2]!=iEnt )
						set_pdata_cbase(iEnt, m_pNext, iInfo[2], 4);
					else
						set_pdata_cbase(iEnt, m_pNext, iInfo[1], 4);
					return true;
				}
			}
			
			iPrev = iEnt;
			iEnt = iNext;
		}
	}
	
	return false;
}

bool:is_user_unzoomed(iPlrId) // check to find out is player zoomed or no (with spectator support)
{
	static s_iFov;
	if( CheckPlayerBit(g_iAlive, iPlrId) )
		s_iFov = get_pdata_int(iPlrId, m_iFOV, 5); //pev(iPlrId, pev_fov, s_fFov);
	else if( pev(iPlrId, pev_iuser1)==4 )
	{
		static s_iSpectated;
		s_iSpectated = pev(iPlrId, pev_iuser2);
		if( 0<s_iSpectated<=g_iMaxPlayers )
		{
			if( CheckPlayerBit(g_iAlive, s_iSpectated) )
				s_iFov = get_pdata_int(s_iSpectated, m_iFOV, 5); //pev(iSpectated, pev_fov, s_fFov);
			else
				return true;
		}
		else
			return true;
	}
	else
		return true;
	
	if( s_iFov==90 )
		return true;
	
	return false;
}
