/*
	PostNuke Wasteland Achievements
	Date: 01.06.2013
	Author: NeoPhoenix
*/
#if defined included_pnwl_znpc
	#endinput
#else
	#define included_pnwl_znpc
#endif

#define		FCNPC::						FCNPC_
#include	"FCNPC.inc"
#include	"mapandreas.inc"
#define		MAX_FCNPCS					(MAX_PLAYERS-30)
#define     FCNPC_SKINS_PER_CLASS       (5)
#define     FCNPC_RESPAWN   			(20)
#define     FCNPC_FOLLOW    			(60)
#define     FCNPC_IDLETIME  			(15)

enum (+=1)
{
	FCNPC_ATTACK_TYPE_MELEE = 0,
	FCNPC_ATTACK_TYPE_SHOOT,
	FCNPC_ATTACK_TYPE_EXPLODE
}

enum (<<=1)
{
	FCNPC_FRACTION_PLAYER = 1,
    FCNPC_FRACTION_SOLDIER = (FCNPC_FRACTION_PLAYER|2),//3
	FCNPC_FRACTION_ZOMBIE = 4,
	FCNPC_FRACTION_BANDIT,
}

enum znpc_classes
{
	zcName[24],
	zcSkins[FCNPC_SKINS_PER_CLASS],
	Float:zcHealth,
	Float:zcArmour,
	Float:zcDamage,
	Float:zcAttackRange,
	zcAttackspeed,
	Float:zcViewDistance,
	Float:zcViewAngle,
	Float:zcHearDistance,
	zcRunspeed,
	zcWeapon,
	zcAttackType,
	zcFraction,
}
new ZNPC_Classes[][znpc_classes] =
{   //Name          Skins					Health/Armour/Damage Attackrange Attack/Viewdist-angle  Hear	Speed         		Weapon  Attacktype
	{"Walker",  	{77,200,230,158,159},	50.0,	0.0,	8.0,	1.5,	300,	30.0,	120.0,	30.0,	MOVE_TYPE_RUN,		0,	FCNPC_ATTACK_TYPE_MELEE,	FCNPC_FRACTION_ZOMBIE},
	{"Runner",  	{134,137,196,212},		40.0,   0.0,    8.0,   	1.5,	200,	40.0,	120.0,	40.0,	MOVE_TYPE_SPRINT,   0,	FCNPC_ATTACK_TYPE_MELEE,	FCNPC_FRACTION_ZOMBIE},
	{"Boomer",    	{5,149},				2500.0, 100.0,  40.0,	1.5,	500,	20.0,	120.0,	25.0,	MOVE_TYPE_WALK,     0,	FCNPC_ATTACK_TYPE_MELEE,	FCNPC_FRACTION_ZOMBIE},
	{"Suicider",	{220},					15.0,   0.0,    100.0, 	10.0,	100,    30.0,   120.0,	30.0,	MOVE_TYPE_WALK,     0,	FCNPC_ATTACK_TYPE_EXPLODE,	FCNPC_FRACTION_ZOMBIE},
	{"Cutter",      {162},					250.0,  100.0,  20.0,	2.3,	200,    30.0,   120.0,	30.0,	MOVE_TYPE_RUN,      9,	FCNPC_ATTACK_TYPE_MELEE,	FCNPC_FRACTION_ZOMBIE},
	{"Pyro",		{277,278,279},			100.0,  25.0,   100.0,  10.0,   200,    30.0,   90.0,	0.0,	MOVE_TYPE_WALK,     37, FCNPC_ATTACK_TYPE_SHOOT,	FCNPC_FRACTION_ZOMBIE},
	{"Wachposten",  {285},                  100.0,  0.0,  	20.0,   15.0,   200,    40.0,   360.0,  40.0,   MOVE_TYPE_NONE,     31, FCNPC_ATTACK_TYPE_SHOOT,    FCNPC_FRACTION_SOLDIER},
	{"Bandit",      {1},                    100.0,  0.0,	20.0,   15.0,   200,    40.0,   360.0,  40.0,   MOVE_TYPE_NONE,     30, FCNPC_ATTACK_TYPE_SHOOT,    FCNPC_FRACTION_BANDIT}
};

enum znpc_zones
{
	Float:zzCoords[4],//(Float:minx, Float:miny, Float:maxx, Float:maxy)
	zzPopulation[sizeof(ZNPC_Classes)],
	bool:zzUseZ,
	Float:zzRangeZ[2]//(Float:minz, Float:maxz)
}
new ZNPC_Zones[][znpc_zones] =
{
	{{1846.4708,908.1959,2735.7097,2808.2732},{0,0,0,0,0,0,0,0},true,{0.0,15.0}}
};

enum znpc_data
{
	zState,			//0 - dead, 1 - idle, 2 - moving, 3 - moving back to pos after attack, 4 - following player, 5 - attacking player
	zStateTime,		//Zeit während des Status
	zFocusPlayer,   //fokussierter feindlicher spieler
	zZombieType,    //Zombietype
	zZone,          //Zone
}
new NPC[MAX_FCNPCS][znpc_data];

forward FCNPC::InitZombies();
public FCNPC::InitZombies()
{
	for(new zone; zone<sizeof(ZNPC_Zones); zone++)
	{//Zonen loopen
	    for(new type; type<sizeof(ZNPC_Classes); type++)
	    {//Zombietypen loopen
	        for(new npc; npc<ZNPC_Zones[zone][zzPopulation][type]; npc++)
	        {//Zombies erstellen
	            new name[24];
	            format(name,24,"%s_%d_%d_%d",ZNPC_Classes[type][zcName],zone,type,npc);
	            new npcid = FCNPC_Create(name);
				if(IsPlayerFCNPC(npcid))
				{
				    NPC[MAX_PLAYER_ID-npcid][zState]        =  1;
				    NPC[MAX_PLAYER_ID-npcid][zStateTime]    =  0;
				    NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]	= -1;
				    NPC[MAX_PLAYER_ID-npcid][zZombieType]   =  type;
				    NPC[MAX_PLAYER_ID-npcid][zZone]			=  zone;
				    new Float:p[3];
				    p[0] = ZNPC_Zones[zone][zzCoords][0]+floatrandom(ZNPC_Zones[zone][zzCoords][2]-ZNPC_Zones[zone][zzCoords][0]);
				    p[1] = ZNPC_Zones[zone][zzCoords][1]+floatrandom(ZNPC_Zones[zone][zzCoords][3]-ZNPC_Zones[zone][zzCoords][1]);
				    //zzCoords:(Float:minx, Float:miny, Float:maxx, Float:maxy)
				    FCNPC_GetZGround(p[0],p[1],p[2]);
					FCNPC_Spawn(npcid,0,p[0],p[1],p[2]);
				}
	        }
	    }
 	}
	SetTimer("znpc_GlobalUpdate",333,true);
	SetTimer("znpc_CheckAlert",2000,true);
	return;
}

public FCNPC_OnTakeDamage(npcid,damagerid,weaponid,bodypart,Float:health_loss)
{
	if(!IsPlayerFCNPC(npcid)) return 0;
	new type = NPC[MAX_PLAYER_ID-npcid][zZombieType];
	if(ZNPC_Classes[type][zcFraction] & FCNPC_FRACTION_PLAYER) return 1;
	switch(NPC[MAX_PLAYER_ID-npcid][zState])
	{
	    case 1..3:
	    {
		    NPC[MAX_PLAYER_ID-npcid][zState]        = 4;
		    NPC[MAX_PLAYER_ID-npcid][zStateTime]    = 0;
		    NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]	= damagerid;
	    }
	    case 4: NPC[MAX_PLAYER_ID-npcid][zStateTime] = 0;
	}
	return 1;
}

public FCNPC_OnSpawn(npcid)
{
    if(!IsPlayerFCNPC(npcid)) return 0;
    NPC[MAX_PLAYER_ID-npcid][zState]        =  1;
    NPC[MAX_PLAYER_ID-npcid][zStateTime]    =  0;
    NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]	= -1;
    new type = NPC[MAX_PLAYER_ID-npcid][zZombieType];
    FCNPC_SetHealth(npcid,ZNPC_Classes[type][zcHealth]);
    FCNPC_SetArmour(npcid,ZNPC_Classes[type][zcArmour]);
    FCNPC_SetWeapon(npcid,ZNPC_Classes[type][zcWeapon]);
	FCNPC_SetAmmo(npcid,9999);
	new count = 0;
	for(new i; i<FCNPC_SKINS_PER_CLASS; i++) if(ZNPC_Classes[type][zcSkins][i] != 0) count++;
	FCNPC_SetSkin(npcid,ZNPC_Classes[type][zcSkins][count==1?0:random(count-1)]);
    if(NPC[MAX_PLAYER_ID-npcid][zZone] != -1)
    {
		new Float:p[3];
		p[0] = ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][0]+floatrandom(ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][2]-ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][0]);
		p[1] = ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][1]+floatrandom(ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][3]-ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][1]);
		//zzCoords:(Float:minx, Float:miny, Float:maxx, Float:maxy)
		FCNPC_GetZGround(p[0],p[1],p[2]);
		FCNPC_SetPosition(npcid,p[0],p[1],p[2]);
		FCNPC_MoveToZone(npcid);
	}
    new name[24];
    format(name,sizeof(name),"%s_%d",ZNPC_Classes[type][zcName],npcid);
    SetPlayerName(npcid,name);
	return 1;
}

public FCNPC_OnDeath(npcid,killerid,weaponid)
{
	if(!IsPlayerFCNPC(npcid)) return 0;
	if(weaponid == 0xBEEF)
	{//Killed by killerid due to explosion of Suicider
	    return 1;
	}
    NPC[MAX_PLAYER_ID-npcid][zState]		=  0;
    NPC[MAX_PLAYER_ID-npcid][zStateTime]	=  0;
    NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]	= -1;
	if(ZNPC_Classes[NPC[MAX_PLAYER_ID-npcid][zZombieType]][zcAttackType] == FCNPC_ATTACK_TYPE_EXPLODE)
	{//Explosion
	    new Float:p[3];
	    FCNPC_GetPosition(npcid,p[0],p[1],p[2]);
		CreateExplosion(p[0],p[1],p[2],0,ZNPC_Classes[NPC[MAX_PLAYER_ID-npcid][zZombieType]][zcAttackRange]*2.0);
		for(new i=MAX_PLAYER_ID; i>(MAX_PLAYER_ID-MAX_FCNPCS); i--)
		{
		    if(npcid == i || !IsPlayerFCNPC(i)) continue;
		    if(!FCNPC_IsDead(i) && DistanceBetweenPlayers(npcid,i) <= ZNPC_Classes[NPC[MAX_PLAYER_ID-npcid][zZombieType]][zcAttackRange])
			{
			    new Float:hp = FCNPC_GetHealth(i);
			    hp -= ZNPC_Classes[MAX_PLAYER_ID-npcid][zcDamage];
			    if(hp <= 1.0)
			    {
					FCNPC_Kill(i);
				    NPC[MAX_PLAYER_ID-i][zState]		=  0;
				    NPC[MAX_PLAYER_ID-i][zStateTime]	=  0;
				    NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
				    if(IsPlayerConnected(killerid)) FCNPC_OnDeath(i,killerid,0xBEEF);
				    continue;
				}
				FCNPC_SetHealth(i,hp);
			}
		}
	}
    for(new i=MAX_PLAYER_ID; i>(MAX_PLAYER_ID-MAX_FCNPCS); i--)
    {
        if(NPC[MAX_PLAYER_ID-i][zFocusPlayer] == npcid)
        {
            FCNPC_StopAim(i);
            FCNPC_Stop(i);
            print("STOP ATTACK!");
		    NPC[MAX_PLAYER_ID-i][zState]		=  1;
		    NPC[MAX_PLAYER_ID-i][zStateTime]	=  0;
		    NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
		    FCNPC_MoveToZone(i);
        }
    }
	return 1;
}

public FCNPC_OnReachDestination(npcid)
{
    if(!IsPlayerFCNPC(npcid)) return 0;
    FCNPC_StopAim(npcid);
	FCNPC_StopAttack(npcid);
	new type = NPC[MAX_PLAYER_ID-npcid][zZombieType],
		zone = NPC[MAX_PLAYER_ID-npcid][zZone];
	switch(NPC[MAX_PLAYER_ID-npcid][zState])
	{
	    case 2:
	    {//Moving
			FCNPC_MoveToZone(npcid);
	    }
	    case 3:
	    {//moving back to pos after attack
	        if(FCNPC_IsInZone(npcid))
	        {
	            NPC[MAX_PLAYER_ID-npcid][zState]     	=  1;
	            NPC[MAX_PLAYER_ID-npcid][zStateTime] 	=  0;
	            NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]	= -1;
	        }
	        else FCNPC_MoveToZone(npcid);
	    }
	    case 4,5:
	    {//Following or attacking player
	        if(IsPlayerConnected(NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]))
	        {
	            new Float:p[3];
	            GetPlayerPos(NPC[MAX_PLAYER_ID-npcid][zFocusPlayer],p[0],p[1],p[2]);
				new Float:dist = DistanceBetweenPlayers(npcid,NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]);
				if(dist <= ZNPC_Classes[type][zcAttackRange])
				{
					//FCNPC_AimAt(npcid,p[0],p[1],p[2],0);
                    FCNPC_SetFacingPlayer(npcid,NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]);
					FCNPC_MeleeAttack(npcid,ZNPC_Classes[type][zcAttackspeed]);
				}
				else if(ZNPC_Classes[type][zcRunspeed] != MOVE_TYPE_NONE)
				{//Zum Gegner gehen
				    if(ZNPC_Zones[zone][zzUseZ] && (p[2] < ZNPC_Zones[zone][zzRangeZ][0] || p[2] > ZNPC_Zones[zone][zzRangeZ][1])) return 1;
				    new Float:grad = floatrandom(180.0)-90.0;
		            p[0] += floatsin(grad,degrees)*ZNPC_Classes[type][zcAttackRange]*0.6;
		            p[1] += floatcos(grad,degrees)*ZNPC_Classes[type][zcAttackRange]*0.6;
					FCNPC_GoTo(npcid,p[0],p[1],p[2],.type=ZNPC_Classes[type][zcRunspeed],.UseZMap=ZNPC_Zones[zone][zzUseZ]);
				}
	        }
	        else
	        {//Target is offline
		        NPC[MAX_PLAYER_ID-npcid][zState]		=  1;
	            NPC[MAX_PLAYER_ID-npcid][zStateTime]    =  0;
	            NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]	= -1;
	            if(!FCNPC_IsInZone(npcid)) FCNPC_MoveToZone(npcid);
	        }
	    }
	}
	return 1;
}

forward znpc_GlobalUpdate();
public znpc_GlobalUpdate()
{
	for(new i=MAX_PLAYER_ID; i>(MAX_PLAYER_ID-MAX_FCNPCS); i--)
	{
	    if(!IsPlayerFCNPC(i)) continue;
    	FCNPC_StopAim(i);
		FCNPC_StopAttack(i);
			
		NPC[MAX_PLAYER_ID-i][zStateTime]++;
		new type = NPC[MAX_PLAYER_ID-i][zZombieType],
		    zone = NPC[MAX_PLAYER_ID-i][zZone];
		switch(NPC[MAX_PLAYER_ID-i][zState])
		{
		    case 0://Dead
		    {
		        if(NPC[MAX_PLAYER_ID-i][zStateTime] == (FCNPC_RESPAWN*3))
		        {//Respawn
		            NPC[MAX_PLAYER_ID-i][zState]     	=  1;
		            NPC[MAX_PLAYER_ID-i][zStateTime] 	=  0;
		            NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
		        	FCNPC_Respawn(i);
		        	FCNPC_OnSpawn(i);
		        	continue;
		        }
		    }
		    case 1:
		    {//Idle
		        if(ZNPC_Classes[type][zcRunspeed] == MOVE_TYPE_NONE) continue;
		        if(NPC[MAX_PLAYER_ID-i][zStateTime] == (FCNPC_IDLETIME*3))
		        {
		            NPC[MAX_PLAYER_ID-i][zState]     	=  2;
		            NPC[MAX_PLAYER_ID-i][zStateTime] 	=  0;
		            NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
					FCNPC_MoveToZone(i);
		            continue;
		        }
		    }
		    case 2:
		    {//Moving
	            continue;
		    }
		    case 3:
		    {//moving back to pos after attack
				FCNPC_MoveToZone(i);
	            continue;
		    }
		    case 4,5:
		    {//Following or attacking player
		        if(IsPlayerConnected(NPC[MAX_PLAYER_ID-i][zFocusPlayer]))
		        {
		            new Float:p[3],Float:dist;
		            GetPlayerPos(NPC[MAX_PLAYER_ID-i][zFocusPlayer],p[0],p[1],p[2]);
		            dist = DistanceBetweenPlayers(i,NPC[MAX_PLAYER_ID-i][zFocusPlayer]);
					if(dist > ZNPC_Classes[type][zcAttackRange])
					{
					    if(NPC[MAX_PLAYER_ID-i][zState] == 4 && NPC[MAX_PLAYER_ID-i][zStateTime] == (FCNPC_FOLLOW*3))
					    {//Zombie lässt vom Spieler ab, hat ihn lange genug erfolglos verfolgt
				            NPC[MAX_PLAYER_ID-i][zState]         = 3;
				            NPC[MAX_PLAYER_ID-i][zStateTime]     = 0;
				            NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
				            FCNPC_MoveToZone(i);
					    }
					    else
					    {
						    SendClientMessage(0,-1,"Verfolgen");
							if(NPC[MAX_PLAYER_ID-i][zState] == 5)
						    {//Hat vorher angegriffen, Spieler ist aber außer Reichweite
							    FCNPC_StopAttack(i);
					            NPC[MAX_PLAYER_ID-i][zState]     = 4;
					            NPC[MAX_PLAYER_ID-i][zStateTime] = 0;
						    }
						    
						    if(ZNPC_Classes[type][zcRunspeed] == MOVE_TYPE_NONE)
							{
					            NPC[MAX_PLAYER_ID-i][zState]         = 1;
					            NPC[MAX_PLAYER_ID-i][zStateTime]     = 0;
					            NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
							}
						    else
						    {
						        if(ZNPC_Zones[zone][zzUseZ] && (p[2] < ZNPC_Zones[zone][zzRangeZ][0] || p[2] > ZNPC_Zones[zone][zzRangeZ][1])) return;
							    new Float:grad = floatrandom(180.0)-90.0;
					            p[0] += floatsin(grad,degrees)*ZNPC_Classes[type][zcAttackRange]*0.7;
					            p[1] += floatcos(grad,degrees)*ZNPC_Classes[type][zcAttackRange]*0.7;
								FCNPC_GoTo(i,p[0],p[1],p[2],.type=ZNPC_Classes[type][zcRunspeed],.UseZMap=ZNPC_Zones[zone][zzUseZ]);
							}
						}
						continue;
					}
					else
					{//Zombie greift Spieler an
					    SendClientMessage(0,-1,"Angriff");
						NPC[MAX_PLAYER_ID-i][zState] = 5;
					    if(NPC[MAX_PLAYER_ID-i][zState] == 4) NPC[MAX_PLAYER_ID-i][zStateTime] = 0;
						switch(ZNPC_Classes[type][zcAttackType])
						{
						    case FCNPC_ATTACK_TYPE_MELEE:
							{
							    //FCNPC_AimAt(i,p[0],p[1],p[2],0);
								FCNPC_SetFacingPlayer(i,NPC[MAX_PLAYER_ID-i][zFocusPlayer]);
								FCNPC_MeleeAttack(i,ZNPC_Classes[type][zcAttackspeed]);
							}
							case FCNPC_ATTACK_TYPE_SHOOT: FCNPC_AimAt(i,p[0],p[1],p[2]+0.2,1);
						    case FCNPC_ATTACK_TYPE_EXPLODE:
							{
							    FCNPC_GetPosition(i,p[0],p[1],p[2]);
								CreateExplosion(p[0],p[1],p[2],0,ZNPC_Classes[type][zcAttackRange]*2.0);
							    NPC[MAX_PLAYER_ID-i][zState]		=  0;
							    NPC[MAX_PLAYER_ID-i][zStateTime]	=  0;
							    NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
								FCNPC_Kill(i);
								for(new npc=MAX_PLAYER_ID; npc>(MAX_PLAYER_ID-MAX_FCNPCS); npc--)
								{
								    if(npc == i || !IsPlayerFCNPC(npc)) continue;
								    if(!FCNPC_IsDead(npc) && DistanceBetweenPlayers(npc,i) <= ZNPC_Classes[NPC[MAX_PLAYER_ID-i][zZombieType]][zcAttackRange])
									{
									    new Float:hp = FCNPC_GetHealth(npc);
									    hp -= ZNPC_Classes[type][zcDamage];
									    if(hp <= 1.0)
									    {
											FCNPC_Kill(npc);
										    NPC[MAX_PLAYER_ID-npc][zState]		=  0;
										    NPC[MAX_PLAYER_ID-npc][zStateTime]	=  0;
										    NPC[MAX_PLAYER_ID-npc][zFocusPlayer]= -1;
										    continue;
										}
										FCNPC_SetHealth(npc,hp);
									}
								}
							}
						}
					}
					continue;
		        }
		        else
		        {//Verfolgter Spieler ist nun offline
			        NPC[MAX_PLAYER_ID-i][zState]		=  1;
		            NPC[MAX_PLAYER_ID-i][zStateTime]    =  0;
		            NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
		            if(!FCNPC_IsInZone(i)) FCNPC_MoveToZone(i);
		            continue;
		        }
		    }
		}
	}
	return;
}

forward znpc_CheckAlert();
public znpc_CheckAlert()
{
	for(new i=MAX_PLAYER_ID; i>(MAX_PLAYER_ID-MAX_FCNPCS); i--)
	{
	    if(!IsPlayerFCNPC(i)) continue;
	    if(MAX_PLAYER_ID-i >= MAX_FCNPCS) break;
	    new type = NPC[MAX_PLAYER_ID-i][zZombieType],
			zone = NPC[MAX_PLAYER_ID-i][zZone];
	    switch(NPC[MAX_PLAYER_ID-i][zState])
	    {
	        case 1,2:
	        {//Idle oder läuft planlos rum

	            if(!FCNPC_IsInZone(i))
				{//Zurück in die Zone
					FCNPC_MoveToZone(i);
					continue;
				}

				for(new playerid=0; playerid<=MAX_PLAYER_ID; playerid++)
				{//Gegner suchen
				    if(!IsPlayerConnected(playerid) || i == playerid) continue;
				    if(GetPlayerInterior(playerid) != GetPlayerInterior(i) || GetPlayerVirtualWorld(playerid) != GetPlayerVirtualWorld(i)) continue;
				    if(IsPlayerFCNPC(playerid))
				    {
				        if(ZNPC_Classes[NPC[MAX_PLAYER_ID-playerid][zZombieType]][zcFraction] & ZNPC_Classes[type][zcFraction]) continue;
				        if(FCNPC_IsDead(playerid)) continue;
				    }
				    else if(FCNPC_FRACTION_PLAYER & ZNPC_Classes[type][zcFraction]) continue;
				    else if(IsPlayerNPC(playerid)) continue;
				    
				    new Float:fpX,Float:fpY,Float:fpZ;
				    GetPlayerPos(playerid, fpX,fpY,fpZ);
				    if(zone != -1 && ZNPC_Zones[zone][zzUseZ] && (fpZ < ZNPC_Zones[zone][zzRangeZ][0] || fpZ > ZNPC_Zones[zone][zzRangeZ][1])) return;
				    new Float:dist		= DistanceBetweenPlayers(playerid,i),
						Float:req_dist	= ZNPC_Classes[type][zcHearDistance],
						bool:sneak      = GetPlayerSpecialAction(playerid) == SPECIAL_ACTION_DUCK ? true : false;
					
					if(IsPlayerInAnyVehicle(playerid))                          req_dist *= 2.0;
					if(sneak)													req_dist *= 0.1;
					else if(GetPVarInt(playerid,"running") == 1)                req_dist *= 1.0;
					else                                                        req_dist *= 0.6;

	                if(dist > req_dist)
	                {//Kann nicht hören
	                    req_dist = ZNPC_Classes[type][zcViewDistance];
	                    if(sneak) req_dist *= 0.5;
	                    if(dist > req_dist) continue;
					    new Float:Px,Float:Py,Float:Pa,Float:real_angle;
					    FCNPC_GetPosition(i,Px,Py,Pa);
					    real_angle = FCNPC_GetAngle(i);
					    Pa = floatabs(atan((fpY-Py)/(fpX-Px)));
					    if(fpX <= Px && fpY >= Py)		Pa = floatsub(180,Pa);
					    else if(fpX < Px && fpY < Py)	Pa = floatadd(Pa,180);
					    else if(fpX >= Px && fpY <= Py)	Pa = floatsub(360.0,Pa);
					    Pa = floatsub(Pa,90.0);
					    if(Pa >= 360.0)		Pa = floatsub(Pa,360.0);
					    else if(Pa < 0.0)   Pa = floatadd(Pa,360.0);
						new Float:border[2];
						if(real_angle < 0.0)			real_angle = floatadd(real_angle,360.0);
						else if(real_angle >= 360.0)	real_angle = floatsub(real_angle,360.0);
						border[0] = floatsub(real_angle,ZNPC_Classes[type][zcViewAngle]*0.5);
						border[1] = floatadd(real_angle,ZNPC_Classes[type][zcViewAngle]*0.5);
						if(border[0] <= 0.0)		border[0] = floatadd(border[0],360.0);
						else if(border[0] >= 360.0)	border[0] = floatsub(border[0],360.0);
						else if(border[1] <= 0.0)	border[1] = floatadd(border[1],360.0);
						else if(border[1] >= 360.0)	border[1] = floatsub(border[1],360.0);
						if(
						    !(Pa >= border[0] && Pa <= border[1] ||
							Pa <= border[1] && Pa <= border[0] && border[1] <= border[0] ||
							Pa >= border[0] && Pa >= border[1] && border[1] <= border[0])) continue;//Kann weder hören noch sehen
					}//Kann hören oder sehen
					new Float:p[3], Float:grad = floatrandom(180.0)-90.0;
		            GetPlayerPos(playerid,p[0],p[1],p[2]);
		            NPC[MAX_PLAYER_ID-i][zState]        = 4;
		            NPC[MAX_PLAYER_ID-i][zStateTime]    = 0;
		            NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= playerid;
		            if(ZNPC_Classes[type][zcRunspeed] != MOVE_TYPE_NONE)
		            {
			            p[0] += floatsin(grad,degrees)*ZNPC_Classes[type][zcAttackRange]*0.6;
			            p[1] += floatcos(grad,degrees)*ZNPC_Classes[type][zcAttackRange]*0.6;
			            FCNPC_GoTo(i,p[0],p[1],p[2],.type=ZNPC_Classes[type][zcRunspeed],.UseZMap=ZNPC_Zones[zone][zzUseZ]);
					}
		            break;
				}
	        }
		}
	}
	return;
}

stock FCNPC::UnlinkFollowers(playerid)
{
	for(new i=MAX_PLAYER_ID; i>(MAX_PLAYER_ID-MAX_FCNPCS); i--)
	{
	    if(!IsPlayerFCNPC(i)) continue;
		if(NPC[MAX_PLAYER_ID-i][zFocusPlayer] == playerid)
		{
		    FCNPC::StopAttack(i);
		    FCNPC::Stop(i);
		    NPC[MAX_PLAYER_ID-i][zState]        =  FCNPC::IsInZone(i)?2:3;
		    NPC[MAX_PLAYER_ID-i][zStateTime]    =  0;
		    NPC[MAX_PLAYER_ID-i][zFocusPlayer]	= -1;
		    FCNPC::MoveToZone(i);
		}
	}
	return;
}

stock FCNPC_MoveToZone(npcid)
{
	if(!IsPlayerFCNPC(npcid)) return;
	FCNPC_Stop(npcid);
    new Float:p[3],
		zone	= NPC[MAX_PLAYER_ID-npcid][zZone],
		type	= NPC[MAX_PLAYER_ID-npcid][zZombieType];
    if(zone == -1 || ZNPC_Classes[type][zcRunspeed] == MOVE_TYPE_NONE)
    {
        NPC[MAX_PLAYER_ID-npcid][zState]     	=  1;
        NPC[MAX_PLAYER_ID-npcid][zStateTime] 	=  0;
        NPC[MAX_PLAYER_ID-npcid][zFocusPlayer]	= -1;
        if(zone == -1 && ZNPC_Classes[type][zcRunspeed] != MOVE_TYPE_NONE)
        {//Zur Position zurückkehren
        }
        return;
    }
    p[2] = -0xBEEF;
    while(p[2] == -0xBEEF || (ZNPC_Zones[zone][zzUseZ] && (p[2] < ZNPC_Zones[zone][zzRangeZ][0] || p[2] > ZNPC_Zones[zone][zzRangeZ][1])))
    {
	    p[0] = ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][0]+floatrandom(ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][2]-ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][0]);
	    p[1] = ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][1]+floatrandom(ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][3]-ZNPC_Zones[NPC[MAX_PLAYER_ID-npcid][zZone]][zzCoords][1]);
	    //zzCoords:(Float:minx, Float:miny, Float:maxx, Float:maxy)
	    FCNPC_GetZGround(p[0],p[1],p[2]);
	}
    FCNPC_GoTo(npcid,p[0],p[1],p[2],MOVE_TYPE_WALK,.UseZMap=ZNPC_Zones[zone][zzUseZ]);
	return;
}

stock FCNPC_IsInZone(npcid)
{
	if(!IsPlayerFCNPC(npcid)) return 0;
	new zone = NPC[MAX_PLAYER_ID-npcid][zZone];
	if(zone == -1) return 1;
	new Float:p[3];
	FCNPC_GetPosition(npcid,p[0],p[1],p[2]);
	if(ZNPC_Zones[zone][zzCoords][0] <= p[0] <= ZNPC_Zones[zone][zzCoords][2] && ZNPC_Zones[zone][zzCoords][1] <= p[1] <= ZNPC_Zones[zone][zzCoords][3]) return 1;
	//zzCoords:(Float:minx, Float:miny, Float:maxx, Float:maxy)
	return 1;
}

stock FCNPC::SetFacingPlayer(npcid,playerid)
{
    new Float:Px,Float:Py,Float:Pa;
    GetPlayerPos(npcid,Px,Py,Pa);
    new Float:fpX,Float:fpY,Float:fpZ;
    GetPlayerPos(playerid, fpX,fpY,fpZ);
    Pa = floatabs(atan((fpY-Py)/(fpX-Px)));
    if(fpX <= Px && fpY >= Py)		Pa = floatsub(180,Pa);
    else if(fpX < Px && fpY < Py)	Pa = floatadd(Pa,180);
    else if(fpX >= Px && fpY <= Py)	Pa = floatsub(360.0,Pa);
    Pa = floatsub(Pa,90.0);
    if(Pa >= 360.0) Pa = floatsub(Pa, 360.0);
	FCNPC::SetAngle(npcid,Pa);
	return;
}

stock IsPlayerFCNPC(playerid)
{
	if(playerid >= (MAX_PLAYER_ID-MAX_FCNPCS) && IsPlayerNPC(playerid) && IsPlayerConnected(playerid)) return 1;
	else return 0;
}