//{$EVENT evtTick:OnTickHero}
//const PLAYER = 0;

{$EVENT evtUnitWounded:OnUnitWoundedHero}
{$EVENT evtUnitAttacked:OnUnitAttackedHero}
{$EVENT evtMissionStart:OnMissionStartHero}
{$EVENT evtHouseDamaged:OnHouseDamagedHero}
{$EVENT evtBeacon:OnBeaconHero}

type 	
	YG_HeroClass = (hcNone, hcMichel, hcNicholas, hcMathew);
	
	YG_Hero = record
		ID,aGroupID, SelectionID,
		Attack,
		Defense, 
		Health,
		HPmax,
		Haste, 
		MountedBonus,
		HouseBonus,
		AttackedTime, 
		AttackTime, RegPoints, aType, Reveal, Owner, SpecialAttackTime : Integer;
		Attacked, IsDead, FeedHero: Boolean;
		Pos : TKMPoint;
		Name : String;
		aClass : YG_HeroClass;
	end;
	
//const PLAYER = 4;
	
var aHeros : array of YG_Hero;
	HeroAdded : Boolean;
	UnitDefAtt : array[0..2] of array of Integer; // special information about unit data	
	aHeroSelected : Integer;
	showOwnerOnly : Boolean;
	CanSelectHero : Boolean;
	MoreHeros : Boolean;


function GetHeroIndex(aUnit : Integer) : integer;
var I : Integer;

begin
	Result := -1;
	
	If (aHeros[0].ID = aUnit) or (aHeros[0].aGroupID = aUnit)   then 
		Result := 0;
	
	If MoreHeros then 
	for I := 1 to high(aHeros) do 
		If (aHeros[I].ID = aUnit) or (aHeros[I].aGroupID = aUnit)  then 
			Result := I;
end;
	
function UnitIsHero(aUnit : Integer) : Boolean;
begin
	Result := false;
	
	If GetHeroIndex(aUnit) <> -1 then 
		Result := true;
end;	

function GetHeroIndexFromPos(aPos : TKMPoint) : integer;
var I : Integer;

begin
	Result := -1;
	If aHeros[0].Pos = aPos then 
		Result := 0;
	
	If MoreHeros then 
		for I := 1 to high(aHeros) do 
			If (aHeros[I].Pos = aPos) then 
				Result := I;
end;

function GetHeroName(aHero : YG_Hero) : String;
begin
	case aHero.aClass of 
		hcMichel : Result := '[$FA7C6B]King Michel';
		hcNicholas : Result := '[$FA7C6B]Sir Nicholas';
		hcMathew : Result := '[$FA7C6B]Sir Mathew';
	end;
end;

procedure MoveCamera(aPos : TKMPoint);
begin

	A.CinematicStart(PLAYER);
	A.CinematicPanTo(PLAYER, aPos.X, aPos.Y, 0);
	A.CinematicEnd(PLAYER);	
	
end;

procedure TeleportHero(var aHero : YG_Hero; aPos : TKMPoint);
begin

	
	A.UnitKill(aHero.ID, true);
	with aHero do 
	begin
		aGroupID := A.GiveGroup(Owner, aType, aPos.X,aPos.Y, 0, 1, 1);
		ID := GroupLeader(aGroupID);

	end;	
end;

procedure SelectNextHero;
begin
	If not CanSelectHero then 
		Exit;
	If length(aHeros) = 0 then 
		Exit;
	If length(aHeros) = 1 then 
		aHeroSelected := 0
	else
	begin
	
		aHeroSelected := aHeroSelected + 1;
		If aHeroSelected > high(aHeros) then 
			aHeroSelected := 0;
		If aHeros[aHeroSelected].Owner <> PLAYER then 
		begin
			SelectNextHero;
			Exit;
		end;
	end;
	MoveCamera(aHeros[aHeroSelected].Pos);
	
end;


Procedure SetMainStats(var aHero : YG_Hero; A,D,H,HS,MO,R : Integer);
begin
	aHero.Attack := A;
	aHero.Defense := D;
	aHero.Health := H;
	aHero.HPmax := H;
	
	aHero.Haste := HS;
	aHero.MountedBonus := MO;
	aHero.Reveal := R;
end;



Procedure SetHeroIDStats(var aHero : YG_Hero; A,D,H,HS,MO,R : Integer; aClass : YG_HeroClass);
begin
	If length(aHeros) = 0 then 	
		Exit;
		
	SetMainStats(aHero, A,D,H,HS,MO,R);
	
	aHero.aClass := aClass;
	aHero.Name := GetHeroName(aHero);
end;

Procedure SetHeroStats(A,D,H,HS,MO,R : Integer; aClass : YG_HeroClass);
begin
	If length(aHeros) = 0 then 	
		Exit;
	SetHeroIDStats(aHeros[0], A,D,H,HS,MO,R, aClass)

end;

procedure AddHero(aGroup : Integer);
var J, I : Integer;
	aHero : YG_Hero;
begin
	J := Length(aHeros);
	SetLength(aHeros, J + 1);
	
	aHero.aGroupID := aGroup;
	aHero.ID := S.GroupMember(aGroup, 0);
	aHero.aType:= S.UnitType(aHero.ID);
	aHero.Owner:= S.UnitOwner(aHero.ID);
	A.UnitHPSetInvulnerable(aHero.ID, true);
	//A.ShowMSG(-1, 'hero added')
	HeroAdded := true;
	aHeros[J] := aHero;
	aHeros[J].SelectionID := J;
		
end;

procedure AddAutoHeros;
var aGroups :array of  Integer;
	I : Integer;
begin
	aGroups := S.PlayerGetAllGroups(PLAYER);
	If length(aGroups) = 0  then 
		Exit;
	for I := 0 to high(aGroups) do
		If S.GroupMemberCount(aGroups[I]) = 1 then 
		begin
			AddHero(aGroups[I]);
		end;
end;

procedure RemoveEmptyFromHeros;
var aNewHeros : array of YG_Hero;
	I, J : Integer;
begin
	for I := 0 to high(aHeros) do
		If aHeros[I].Attack > 0 then 
		begin
			J := length(aNewHeros);
			SetLength(aNewHeros, J + 1)
			aNewHeros[J] := aHeros[I];
	
		end;
	aHeros := aNewHeros;
end;

procedure ChangeHeroHP(var aHero : YG_Hero; aHPc : Integer);
begin
	with aHero do 
	begin
		if aHPc > 0 then 			
			Health := Health + aHPc + RegPoints
		else
			Health := Health + aHPc;
			
		If Health = HPmax then 
			Exit
		else	
		If Health <= 0 then 
		begin
			Health := 0;
			IsDead:= true;
			Actions.UnitHpSetInvulnerable(ID,false);
			A.UnitKill(ID,false);	
		end	else
		If Health > HPmax then 
			Health := HPmax;
		
	end;
end;

Procedure ShowHeroOverlay(aHero : YG_Hero);
var addArrow : String;
	aPlayerTo : Integer;
begin
	aPlayerTo := 0;
	If aHero.SelectionID = aHeroSelected then 
		addArrow := '[$0000FF] <--[$ffffff]'
	else
		addArrow := '';
		
	If showOwnerOnly then 
		aPlayerTo := aHero.Owner
	else
		aPlayerTo := -1;
		
	If showOwnerOnly then 
		A.OverlayTextSet(aPlayerTo, '');
		
	
		
	A.OverlayTextAppend(aPlayerTo, '|' + aHero.Name + ' [$ffffff];'+addArrow+'|');
	A.OverlayTextAppend(aPlayerTo, '[$00FF00]Health[$ffffff] : ' + IntToStr(aHero.Health) +' / ' + IntToStr(aHero.HPmax) + ' ;|');
	A.OverlayTextAppend(aPlayerTo, '[$00FF00]Attack[$ffffff] : ' + IntToStr(aHero.Attack) + ' ;|');
	A.OverlayTextAppend(aPlayerTo, '[$00FF00]Defense[$ffffff] : ' + IntToStr(aHero.Defense) + ' ;|');
	A.OverlayTextAppend(aPlayerTo, '[$00FF00]Haste[$ffffff] : ' + IntToStr(aHero.Haste) + ' ;|');
	A.OverlayTextAppend(aPlayerTo, '[$00FF00]Mounted Bonus[$ffffff] : ' + IntToStr(aHero.MountedBonus) + ' ;|');
end;


procedure HeroWounded(var aHero : YG_Hero; aKiller : integer);
var Random, aKType, I  : Integer;
begin
	aKType := S.UnitType(aKiller);
	
	if aKType > 13 then 
		Random := UnitDefAtt[1][aKType]
	else
		Random := 130;
	with aHero do 
		If States.KaMRandomI(Random) > Defense then 
		begin
			Attacked := true;
			AttackedTime := States.GameTime + 300;
			Actions.UnitHpSetInvulnerable(ID,true); //it's gonna make sure that hero won't die for some reason
			If aKType = 13 then 
				ChangeHeroHP(aHero, -20 - S.KaMRandomI(20))
			else
				ChangeHeroHP(aHero, - UnitDefAtt[2][aKType]);
			//A.ShowMSG(0, '')
		end;


end;


Procedure OnUnitWoundedHero(aUnit,aKiller:Integer);
var Random, aPlayer  : Integer;
begin
	If not HeroAdded then 
		Exit;
	If not UnitIsHero(aUnit) then 
		Exit;
		
	HeroWounded(aHeros[GetHeroIndex(aUnit)], aKiller)	
end;

procedure KillNotAllied(aUnit : Integer; KillAllied : Boolean);
var UU : KD_UnitStats;
begin
	If SetUnit(aUnit, UU) then 
		If (UU.Owner <> PLAYER) then
		begin
			If KillAllied then 			
				A.UnitKill(aUnit, false)
			else
			If not S.PlayerAllianceCheck(PLAYER, UU.Owner) then 
				A.UnitKill(aUnit, false);
		end;
			
end;

procedure MathewEffect(aPos : TKMPoint);
begin
	KillNotAllied(S.UnitAt(aPos.X - 1, aPos.Y), false);
	KillNotAllied(S.UnitAt(aPos.X + 1, aPos.Y), false);
	KillNotAllied(S.UnitAt(aPos.X, aPos.Y + 1), false);
	KillNotAllied(S.UnitAt(aPos.X, aPos.Y - 1), false);
end;

procedure HeroAttacked(var aHero : YG_Hero; aUnit : integer);
var Random, aKType, I  : Integer;
begin
	with aHero do 
		If GT < AttackTime then 
			Exit
		else
		begin
			If Haste > 1 then 
				AttackTime := GT + Haste;
				
			aKType := S.UnitType(aUnit);
			
			if aKType > 0 then 
				Random := UnitDefAtt[0][aKType];
				
			If States.UnitType(aUnit) in [21,22,27] then 
			begin
				If S.KaMRandomI(Random) >= Random - Attack - MountedBonus then 
				begin			
					If aClass = hcMathew then 
						if GT > SpecialAttackTime then 
						begin	
							SpecialAttackTime := GT + 200 + S.KaMRandomI(100);
							MathewEffect(S.UnitPosition(aUnit));
						end;
					KillInvulnerableUnit(aUnit, false);	

				end;
			end else
			If S.KaMRandomI(Random) >= Random - Attack then 
			begin
				If aClass = hcMathew then 
					if GT > SpecialAttackTime then 
					begin	
						SpecialAttackTime := GT + 200 + S.KaMRandomI(100);
						MathewEffect(S.UnitPosition(aUnit));
					end;
				KillInvulnerableUnit(aUnit, false);
				
			end;
		end;


end;


Procedure OnUnitAttackedHero(aUnit,aKiller:Integer);
var Random, aPlayer, aType, I  : Integer;
	aHero : YG_Hero;
begin
	If not HeroAdded then 
		Exit;
	If not UnitIsHero(aKiller) then 
		Exit;

	HeroAttacked(aHeros[GetHeroIndex(aKiller)], aUnit)	
	
end;


Procedure OnHouseDamagedHero(aHouse,aKiller:Integer);
var aPlayer, aOwner, I  : Integer;
	aHero : YG_Hero;
begin
	If not HeroAdded then 
		Exit;
		
	If not UnitIsHero(aKiller) then 
		Exit;
	
	for I := 0 to high(aHeros) do 
	begin
		aHero := aHeros[I];
		If aHero.ID = aKiller then
		begin
			Actions.HouseAddDamage(aHouse, (aHero.Attack div 3) + aHero.HouseBonus);
			break;
		end else
			Continue;
	end;
	
end;

Procedure OnBeaconHero(aPlayer,X,Y : Integer);
var  aIndex  : Integer;
begin
	If aPlayer = PLAYER then 
		If S.UnitAt(X,Y) < 0 then 
			SelectNextHero;
end;


procedure RefreshHero(var aHero : YG_Hero);
begin
	with aHero do 
	begin
			
		Pos := S.UnitPosition(ID);
		If IsDead then
			A.PlayerDefeat(PLAYER);
		
		If Attacked then 
			If States.GameTime >= AttackedTime then 
				Attacked := not Attacked;
		
		//If States.GameTime mod 20 = 0 then
			
		If States.GameTime mod 20 = 0 then 	
			If Reveal > 0 then  
				If not States.UnitDead(ID) then
					If not States.UnitIdle(ID) then
						If States.GameTime mod 20 = 0 then 
							A.FogRevealCircle(0, Pos.X, Pos.Y, 9 + Reveal);
							
		If FeedHero then 
			A.UnitHungerSet(ID, 27000);
			
	end;
	If States.GameTime >= aHero.AttackedTime + 300 then
		If S.UnitIdle(aHero.ID) then
			ChangeHeroHP(aHero, 1);
	If GT > aTimeToReset then
		ShowHeroOverlay(aHero);
	
end;

Procedure OnTickHero;
var I :integer;
begin
	
	If not HeroAdded then 
		Exit;
	If States.GameTime mod 10 = 0 then 
	begin
		If GT > aTimeToReset then 
			A.OverlayTextSet(-1, '');
			
		RefreshHero(aHeros[0]);
		
		If MoreHeros then 
			for I := 1 to high(aHeros) do 
				RefreshHero(aHeros[I]);
	end;
	
end;

Procedure OnMissionStartHero;
//var aGroups :array of  Integer;
//	I : Integer;
begin
	CanSelectHero := true;
	UnitDefAtt[0] := [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 130,
						45,75,85,65,80,80,90,78,100,100,40,30,110,50];
						
	UnitDefAtt[1] := [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 130,
						45,75,85,70,75,75,80,78,85,90,40,30,100,50];
						
	UnitDefAtt[2] := [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 30,
						1,2,3,2,3,2,3,2,3,4,1,1,4,1];
	//RemoveEmptyFromHeros;	
	showOwnerOnly := true;
	If length(aHeros) > 1 then 
		MoreHeros := true;	
end;