
{$EVENT evtMissionStart:OnMissionStartFunctions}
{$EVENT evtHouseBuilt:OnHouseBuiltFunctions}

const PLAYER = 0;

type
	TProcEv  = procedure;
	TProcEvUnit  = procedure(aUnit : Integer);
	TProcEvArray = array of TProcEvUnit;
	
	TKMArea = record
		X1, X2,Y1,Y2 : Integer;
	end;
	TKMDirPoint = record 
		X,Y,Dir : integer;
	end;
		
	KD_HouseStats = record
		aID : integer;
		aPos : TKMPoint;
		aType, aOwner, aWorker : integer;
		Destroyed, isFinished : Boolean;
		Wares : array[0..27] of Integer;
	end;
	KD_UnitStats = record
		aID : integer;
		aPos : TKMPoint;
		aDir, aType,Owner : integer;
		Killed : Boolean;
	end;
	KD_GroupStats = record
		aLeader : KD_UnitStats;
		aID, UID, aOwner : integer;
		aColumns,aMembers, aType : integer;
		Killed : Boolean;
	end;	
	
	KD_HouseArray = record
		IDs : array of Integer;		
	end;
	KD_PlayerStats = record
		Houses : array[0..29] of KD_HouseArray;
		UpdateHouseList, doUnlimitedDeposits : Boolean;
	end;
	
	KD_Direction = (kdBackward, kdForward);	
	
	KD_Event = record
		Create : TProcEv;
		Name, aMessageAfter : String;
		aTime, aFinTime, EvNumber : Integer;
		OvMsg : array of Integer;
		Finished : Boolean;
		ToDo : array of Boolean;
		DoAtEnd : TProcEv;
	end;


	KD_MSGtype = record
		aID, aTime : Integer;	
		isOverlay, DontStopOgg, Done : Boolean;
	end;

	KD_MSGCollection = record
		MSG : array of KD_MSGtype;
		SoundID, aID, lastID,
		aPlayer: Integer;
		DoOverlay : Boolean;
	end;

	
var HKS_Players : array of KD_PlayerStats;
	dMod : Integer;
	aTimeToReset : integer;
	aMessage : KD_MSGCollection; 
	
function GChance(aPercante : Integer) : Boolean;
var aPC : Single;
begin
	Result := false;
	aPC := aPercante div 100;
	If S.KaMRandom < aPC then 
		Result := true;
end;

function GT : Integer;
begin
	Result := S.GameTime;

end;

function IdMsg(aID : Integer) : String;
begin
	Result := '<$'+IntToStr( aID )+ '>';
end;

procedure ShowOVMsg(aMessageID : Integer; IsOverlay : Boolean);
begin	
	If IsOverlay then 
	begin
		A.OverlayTextSet(PLAYER, '');
		A.OverlayTextAppend(PLAYER, IdMsg(aMessageID));
		A.PlayOggFadeMusic(PLAYER, IntToStr(aMessageID), 1 );
		
		aTimeToReset := GT + 200;
	end else
	begin
		A.ShowMsg( PLAYER, IdMsg(aMessageID));
		A.PlayOggFadeMusic(PLAYER, IntToStr(aMessageID), 1 );
	end;

end;

function AddMessage(aID : Integer;aTime : Integer) : integer;
var J : Integer;
begin
	J := length(aMessage.MSG);
	SetLength(aMessage.MSG, J + 1);
	aMessage.MSG[J].aID := aID;
	aMessage.MSG[J].aTime := aTime;
	Result := J;

end;

function AddOvMessage(aID : Integer;aTime : Integer) : Integer;
var J : Integer;
begin
	J := AddMessage(aID, aTime);
	aMessage.MSG[J].isOverlay := true;
	Result := J;
end;


function PointDir(X,Y,Dir : Integer) : TKMDirPoint;
begin
	Result.X := X;
	Result.Y := Y;
	Result.Dir := Dir;
end;

function GroupLeader(aGroup : Integer) : Integer;
begin
	Result := S.GroupMember(aGroup, 0);
end;


function SetHouse(aHouse : Integer; withWares : Boolean) : KD_HouseStats;
var I : Integer;
begin
	with Result do 
	begin
		aID := aHouse;
		If aID = -1 then 
			Exit;
		aPos := S.HousePosition(aID);
		aType := S.HouseType(aID);	
		isFinished := S.HouseIsComplete(aID);
		Destroyed := S.HouseDestroyed(aID);
		aOwner := S.HouseOwner(aID);
		aWorker := S.HouseWorker(aID)
		If withWares then 
			for I := 0 to 27 do 
				Wares[I] := S.HouseResourceAmount(aHouse, I);
	end;

end;
function SetUnit(aUnit : Integer; out aKDUnit : KD_UnitStats) : Boolean;
begin
	Result := true;
	If aUnit = -1 then 
	begin
		aKDUnit.aID := -1;
		aKDUnit.aPos := U.KMPoint(-1,-1);
		aKDUnit.aDir := -1;
		aKDUnit.aType := -1;
		aKDUnit.Owner := -1;
		Result := false;
		Exit;
	end;
	aKDUnit.aID := aUnit;
	aKDUnit.aPos := S.UnitPosition(aUnit);
	aKDUnit.aDir := S.UnitDirection(aUnit);
	aKDUnit.aType := S.UnitType(aUnit);
	aKDUnit.Owner := S.UnitOwner(aUnit);	

end;

function SetGroup( aGroup : Integer) : KD_GroupStats; 
var akGroup : KD_GroupStats;
begin
	with akGroup do 
	begin
		aId := aGroup;
		aColumns := S.GroupColumnCount(aGroup);
		aMembers := S.GroupMemberCount(aGroup);
		aType := S.GroupType(aGroup);
		aOwner := S.GroupOwner(aGroup);
		SetUnit( GroupLeader(aGroup), aLeader );
		UID := aLeader.aID;
	end;
	Result := akGroup;
end;

function GetGroupPos( var aGroup : KD_GroupStats) : TKMPoint; 
begin
	aGroup.aLeader.aPos := S.UnitPosition(aGroup.aLeader.aID);
	Result := aGroup.aLeader.aPos;
end;


function GiveKDUnit(aUnit : KD_UnitStats) : Integer;
begin
	with aUnit do 
		Result := A.GiveUnit(Owner,aType, aPos.X, aPos.Y, aDir);
end;

function GiveKDGroup(aGroup : KD_GroupStats) : Integer;
begin
	with aGroup do 
		Result := A.GiveGroup(aOwner,aLeader.aType, aLeader.aPos.X, aLeader.aPos.Y, aLeader.aDir, aMembers, aColumns);
end;


function GiveKDHouse(aHouse : KD_HouseStats) : Integer;
begin
	with aHouse do 
		Result := A.GiveHouse(aOwner,aType, aPos.X, aPos.Y);
end;


function Power(aNumber, aCount : Integer) : integer;
var I, newRes : Integer;
begin
	newRes := aNumber;
	for I := 0 to aCount - 2 do 
		newRes := newRes * aNumber;
		
	Result := newRes;
	
end;

function PointNearPoint(aPos1, aPos2 : TKMpoint; aRadius : Integer) : Boolean;
var r : Integer;
begin
	Result := false;
	r:=aRadius
	If Power(aPos1.X-aPos2.X, 2) + Power(aPos1.Y-aPos2.Y, 2) <= Power(r, 2) then 
		Result := true;
end;

procedure EvCreate(var aEvent : KD_Event; aName, aMessage : String);
begin
	aEvent.Name := aName;
	aEvent.aMessageAfter := aMessage;
	aEvent.DoAtEnd := nil;
end;

function FinEv(var aEvent : KD_Event) : Boolean;
var I, TT : Integer;
begin
	With aEvent do 
	begin
		Finished := true;
		aFinTime := GT;
		If DoAtEnd <> nil then 
		begin
			DoAtEnd();
		end;
		Result := true;
		If aMessageAfter <> '' then 
		
			A.ShowMSG(PLAYER, aMessageAfter);
		If length(OvMsg) > 0 then 
			for I := 0 to high(OvMsg) do 
			begin
				AddOvMessage(OvMsg[I], GT + TT + 20)
				TT := TT + 150;
			end;
	end;
end;

function IncLArrayI( var aArray :array of Integer) : Integer;
var J : Integer;
begin
	J := Length(aArray);
	SetLength(aArray, J + 1);
	Result := J;
end;

procedure AddHouseToPlayer(aPlayer, aHouse : Integer);
var I, aType : Integer;
begin
	aType := S.HouseType(aHouse);
	with HKS_Players[aPlayer].Houses[aType] do 
	begin
		I := IncLArrayI(IDs);
		IDs[I] := aHouse;
	end;
end;

procedure AddStoneToTowers(aPlayer : Integer);
var I : Integer;
begin
	with HKS_Players[aPlayer].Houses[17] do 
		If length(IDs) > 0 then 
			for I := 0 to high(IDs) do 
				If not S.HouseDestroyed(IDs[I]) then 
					If S.HouseResourceAmount(IDs[I], 1) < 5 then 
						A.HouseAddWaresTo(IDs[I], 1, 1);
end;

procedure MultiAddStoneToTowers(aPlayers :array of integer);
var I : Integer;
begin
	if Length(aPlayers) = 0 then 
		Exit;
		
	for I := 0 to high(aPlayers) do 
		AddStoneToTowers(aPlayers[I]);
	
end;

procedure UnlimitedDeposits(aPlayer : integer);
var I, aType, TileID : Integer;
	aPos : TKMPoint;
begin
	If length(HKS_Players) = 0 then 
		Exit;
	for aType := 3 to 5 do 
		with HKS_Players[aPlayer].Houses[aType] do 
		begin
			If length(IDs) = 0 then 
				Continue;
			case aType of 
				3 : TileID := 155;
				4 : TileID := 260;
				5 : TileID := 307;
			end;
			
			for I := 0 to high(IDs) do	
				If not S.HouseDestroyed(IDs[I]) then 			
				begin
					aPos := S.HousePosition(IDs[I]);
					A.MapTileSet(aPos.X,aPos.Y-1, TileID, 0);
				
				
				end;
		end;
	
end;


procedure MultiUnlimitedDeposits(aPlayers :array of integer);
var I : Integer;
begin
	if Length(aPlayers) = 0 then 
		Exit;
		
	for I := 0 to high(aPlayers) do 
		UnlimitedDeposits(aPlayers[I]);
	
end;

function SingleToInteger(aSingle : Single; aNrCount : Integer) : String;
var I, res : integer;	
begin
	res := U.FloorTo(aSingle, 1);
	If aNrCount > 7 then 
		aNrCount := 7;
		
	I := U.FloorTo( (aSingle - res) * Power( 10, aNrCount) , 1 );
	Result := IntToStr(res) + '.' + IntToStr(I);
end;

procedure ListPlayerHouses(aPlayer : integer);
var I : integer;
	anArray :array of Integer;
begin
	anArray := S.PlayerGetAllHouses(aPlayer);
	If Length(anArray) = 0 then 
		Exit;
	for I := 0 to high(anArray) do 
		AddHouseToPlayer(aPlayer, anArray[I]);

end;

procedure SetDMod;
begin
	Case S.MissionDifficulty of 
		0..2 : dMod := 0;
		3 : dMod := 1;
		5..8 : dMod := 2;
		else dMod := 1;
	end;
end;

procedure OnHouseBuiltFunctions(aHouse : Integer);
begin
	if S.HouseOwner(aHouse) = PLAYER then 
			AddHouseToPlayer(PLAYER, aHouse);
end;

procedure OnMissionStartFunctions;
begin
	SetDMod;	
	SetLength(HKS_Players, 1 );
	ListPlayerHouses(0);
end;

function Maximum(A,B : Integer) : Integer;
begin
	Result := A
	If A > B then 
		Result := B;

end;


function Minimum(A,B : Integer) : Integer;
begin
	Result := A
	If A < B then 
		Result := B;

end;


function InRange(A, B, C : Integer) : Integer;
begin
	Result := A
	If A < B then 
		Result := B;
		
	If A > C then 
		Result := C;

end;

function KillInvulnerableUnit(aUnit : Integer; Silent : Boolean) : Boolean;
begin
	Result := true;;
	If aUnit = -1 then 
	begin
		Result := false;
		Exit;
	end;
	Actions.UnitHpSetInvulnerable(aUnit,false);
	A.UnitKill(aUnit,Silent);
end;


function GetOffset(aDir, aGroup : Integer) : TKMPoint;
var aRaws : Integer;
begin
	aRaws := S.GroupMemberCount(aGroup) div S.GroupColumnCount(aGroup);
	
	case aDir of 
		0 : Result := U.KMPoint(0, +aRaws);
		1 : Result := U.KMPoint(-aRaws, +aRaws);
		2 : Result := U.KMPoint(-aRaws, 0);
		3 : Result := U.KMPoint(-aRaws, -aRaws);
		4 : Result := U.KMPoint(0, -aRaws);
		5 : Result := U.KMPoint(aRaws, -aRaws);
		6 : Result := U.KMPoint(aRaws, 0);
		7 : Result := U.KMPoint(aRaws, aRaws);
	end;

end;

function GetOffsetWAS(aDir, aGroup, Space : Integer) : TKMPoint;
var aRaws : Integer;
begin
	aRaws := (S.GroupMemberCount(aGroup) div S.GroupColumnCount(aGroup)) + Space;
	
	case aDir of 
		0 : Result := U.KMPoint(0, aRaws );
		1 : Result := U.KMPoint(-aRaws, +aRaws);
		2 : Result := U.KMPoint(-aRaws, 0);
		3 : Result := U.KMPoint(-aRaws, -aRaws);
		4 : Result := U.KMPoint(0, -aRaws);
		5 : Result := U.KMPoint(aRaws, -aRaws);
		6 : Result := U.KMPoint(aRaws, 0);
		7 : Result := U.KMPoint(aRaws, aRaws);
	end;

end;


function AddGroupBehindGroup(aGroup, aOwner, aType, aCount, aCCount, Space : integer) : Integer;
var aPos, aGPos : TKMPoint;
	aDir : integer;
begin
	Result := -1;
	
	aDir := S.UnitDirection(GroupLeader(aGroup));
	aGPos := S.UnitPosition(GroupLeader(aGroup));
	aPos := GetOffsetWAS(aDir, aGroup, Space);
	
	Result := A.GiveGroup(aOwner, aType, aGPos.X + aPos.X, aGPos.Y + aPos.Y, aDir, aCount, aCCount);
end;

function AddGBG(aGroup, aOwner, aType, aCount, aCCount, Space : integer) : Integer; //short name of AddGroupBehindGroup;
var aPos, aGPos : TKMPoint;
	aDir : integer;
begin
	Result := -1;
	
	aDir := S.UnitDirection(GroupLeader(aGroup));
	aGPos := S.UnitPosition(GroupLeader(aGroup));
	aPos := GetOffsetWAS(aDir, aGroup, Space);
	
	Result := A.GiveGroup(aOwner, aType, aGPos.X + aPos.X, aGPos.Y + aPos.Y, aDir, aCount, aCCount);
end;

function AddKDGroupBehindKDGroup(aGroup, GG : KD_groupStats; aSpace : integer) : Integer;
begin
	with GG do 
		result := AddGroupBehindGroup(aGroup.aID, aOwner, aLeader.aType, aMembers, aColumns, aSpace);
end;

//{$I HKS_Feed.script}
//{$I HKS_ResHouses.script}
//{$I HKS_Patrol.script}
//{$I HKS_RaisArmy.script}
{$I HKS_Messages.script}

{$IFDEF FEED}
	{$I HKS_Feed.script}
{$ENDIF}
{$IFDEF HERO}
	{$I HKS_Hero.script}
{$ENDIF}

{$IFDEF RES_HOUSES}
	{$I HKS_ResHouses.script}
{$ENDIF}

{$IFDEF PATROL}
	{$I HKS_Patrol.script}
{$ENDIF}

{$IFDEF DIFFICULTY}
	{$I HKS_RaisArmy.script}
	{$I HKS_Difficulty.script}
{$ENDIF}

{$IFDEF AIAttacks}
	{$I HKS_AIAttackPos.script}
{$ENDIF}

{$IFDEF CUSTOM_LOCATION}
	{$I HKS_CustomLocation.script}
{$ENDIF}