#include <amxmodx>
#include <amxmisc>
#include <fakemeta>

#define PLUGIN "Cod Sniper Effect"
#define VERSION "1.13"
#define AUTHOR "R3X"

//Uncomment to +use control
#define CONTROL_BY_IN_USE


#define HEARTBEAT_TASKID 1456

#define FOV_BOUND 45

#define FRAMES_TO_SKIP 60

new const INHALE_SOUND[] = "player/inhale.wav";
new const EXHALE_SOUND[] = "player/exhale.wav";

new const HEARTBEAT_SOUND[] = "player/heartbeat1.wav";

new giCounter[33];
new Float:gfLastUse[33];

new Float:gfOffset[33][3];
new Float:gfCurOffset[33][3];
new Float:gfDistance[33][3];

new giMyMulti[33];

new bool:gbStopNoise[33];
new bool:gbShowInfo[33];

new bool:gbShowInfoStamp[33];


new const giMultipliers[][2] = {
	{ -1, 0 },
	{ 1, 1 },
	{ 0, -1 },
	{ 0, -2 },
	{ 1, 0 },
	{ 1, 1 },
	{ -2, 0}
}

new gcvarDuration, gcvarInterval;
new gcvarNoiseStand, gcvarNoiseDuck;
new gcvarHeartBeat;

public plugin_init() {
	register_plugin(PLUGIN, VERSION, AUTHOR);
	
	register_forward(FM_PlayerPreThink, "fwPlayerPreThink", 1);
	
#if defined CONTROL_BY_IN_USE
	register_forward(FM_EmitSound, "fwEmitSound")
#endif
	
	register_clcmd("+wdech", "wdech");
	register_clcmd("-wdech", "wydech");
	
	gcvarDuration = register_cvar("amx_snipereff_duration", "5.0", 0, 5.0);
	gcvarInterval = register_cvar("amx_snipereff_interval", "2.0", 0, 2.0);
	
	gcvarNoiseStand = register_cvar("amx_snipereff_stand_noise", "0.01", 0, 0.01);
	gcvarNoiseDuck = register_cvar("amx_snipereff_duck_noise", "0.006", 0, 0.006);

	gcvarHeartBeat = register_cvar("amx_snipereff_heart", "1");
}
public plugin_precache(){
	precache_sound(INHALE_SOUND);
	precache_sound(EXHALE_SOUND);
	
	precache_sound(HEARTBEAT_SOUND);
}
public client_putinserver(id){
	giCounter[id] = 0;
	gfLastUse[id] = 0.0;
	
	giMyMulti[id] = -1;
	gbShowInfo[id] = true;
	gbShowInfoStamp[id] = false;
	randomizeOffset(id);
}
public wdech(id){
	if(pev(id, pev_fov) >= FOV_BOUND) return PLUGIN_HANDLED;
	
	gbShowInfo[id] = false;
	new Float:fNow = get_gametime();
	
	if(gfLastUse[id] && (fNow < (get_pcvar_float(gcvarInterval)+gfLastUse[id]+get_pcvar_float(gcvarDuration)))){
		client_print(id, print_center, "Poczekaj chwile...");
		return PLUGIN_HANDLED;
	}
	gfLastUse[id] = fNow;
	gbStopNoise[id] = true;
	client_cmd(id, "spk %s", INHALE_SOUND);
	if(get_pcvar_num(gcvarHeartBeat))
		set_task(1.2, "taskHeartBeat", id + HEARTBEAT_TASKID, _,_, "b");
	return PLUGIN_HANDLED;
}
public wydech(id){
	if(task_exists(id + HEARTBEAT_TASKID))
		remove_task(id + HEARTBEAT_TASKID);

	if(!gbStopNoise[id])
		return PLUGIN_HANDLED;
		
	gbStopNoise[id] = false;
	
	new Float:fDif = gfLastUse[id]+get_pcvar_float(gcvarDuration) - get_gametime();
	if( fDif > 0.0 ){
		gfLastUse[id] -= fDif;
	}
	if(is_user_alive(id))
		client_cmd(id, "spk %s", EXHALE_SOUND);
	return PLUGIN_HANDLED;
}
public taskHeartBeat(tid){
	new id = tid - HEARTBEAT_TASKID;
	if(is_user_alive(id) && (pev(id, pev_fov) < FOV_BOUND))
		client_cmd(id, "spk %s", HEARTBEAT_SOUND);
}
public fwPlayerPreThink(id){
	if(!is_user_alive(id)){
		if(gbStopNoise[id])
			wydech(id);
		else
			return FMRES_IGNORED;
	}
		
#if defined CONTROL_BY_IN_USE
	new button = pev(id, pev_button);
	new oldbutton = pev(id, pev_oldbuttons);
	
	if(button&IN_USE && (oldbutton&IN_USE == 0))
		wdech(id);
	else if(oldbutton&IN_USE && (button&IN_USE == 0))
		wydech(id);
#endif
	if((pev(id, pev_fov) < FOV_BOUND)){
		if(gbShowInfo[id] && !gbShowInfoStamp[id]){
			set_hudmessage(200, 200, 200, -1.0, 0.75,0, 0.0, 2.5,0.0,0.0,2);
#if defined CONTROL_BY_IN_USE
			show_hudmessage(id, "Uzyj +use (E), aby ustabilizowac celownik");
#else
			show_hudmessage(id, "Uzyj +wdech, aby ustabilizowac celownik");
#endif
			gbShowInfoStamp[id] = true;
		}
		if(gbStopNoise[id]){
			if(get_gametime() < gfLastUse[id]+get_pcvar_float(gcvarDuration))
				return FMRES_IGNORED;
			else
				wydech(id);
		}
		
		if(giCounter[id] < FRAMES_TO_SKIP){
			gfCurOffset[id][0] += gfDistance[id][0];
			gfCurOffset[id][1] += gfDistance[id][1];
			
			new Float:fVec[3];
			pev(id, pev_v_angle, fVec);
			
			for(new i=0;i<2;i++){
				fVec[i] += gfCurOffset[id][i];
			}
			set_pev(id, pev_angles, fVec);
			set_pev(id, pev_fixangle, 1);
			giCounter[id]++
			return FMRES_IGNORED;
		}
		
		giCounter[id] = 0;
		randomizeOffset(id);
	}else{
		if(gbStopNoise[id])
			wydech(id);
		gbShowInfoStamp[id] = false;
	}
	
	return FMRES_IGNORED;
}
randomizeOffset(id){
	++giMyMulti[id];
	giMyMulti[id] %= sizeof giMultipliers;
	
	new Float:fBase = get_pcvar_float( (pev(id, pev_button)&IN_DUCK) ? gcvarNoiseDuck: gcvarNoiseStand);
	for(new i=0;i<2;i++){
		gfOffset[id][i] = fBase * giMultipliers[giMyMulti[id]][i];
	}
	
	//Distance betwenn offset
	gfDistance[id][0] = gfOffset[id][0] - gfCurOffset[id][0];
	gfDistance[id][1] = gfOffset[id][1] - gfCurOffset[id][1];
	
	//Step size		
	gfDistance[id][0] /= FRAMES_TO_SKIP-1;
	gfDistance[id][1] /= FRAMES_TO_SKIP-1;	
}

//E using beep fix
#if defined CONTROL_BY_IN_USE
public fwEmitSound(id, iChannel, const szSample[]){
	if(!is_user_alive(id))
		return FMRES_IGNORED;
	if(iChannel == 3 && contain(szSample, "denyselect.wav") > 0){
		if(get_gametime() - gfLastUse[id] < 0.1)
			return FMRES_SUPERCEDE;
	}
	return FMRES_IGNORED;
}
#endif
