//Random storehouse placement script. With possibilities to place storehouses mirrored.
//The original script was written by Thunder.
//Rewriten by Ritter des Todes.
//27.07.2024.
//Version 1.1.
//Developed for r15122.

//To import script use {$INCLUDE RandomStorehouse.script}

//----------------------------------------------------------------------------------------------------------------------------------------------------
//DESCRIPTION OF USER INTERFACE.
//----------------------------------------------------------------------------------------------------------------------------------------------------

//Initialize player, that will be affected by script and set randomising limits, serfs and workers count.
//RNST_InitPlayer(playerId, xMin, xMax, yMin, yMax : integer);

//Same as RNST_InitPlayer. Also adds starting serfs and workers count for player.
//RNST_InitPlayerEx(playerId, xMin, xMax, yMin, yMax, workersCount, serfsCount: integer);

//Bind two players. Same Bound players have mirrored locations of storehouses.
//Players should have same random coordinates range and it should be possible to place storehouse mirrored at map. Actually map should be mirrored. 
//Good examples of maps will be Mercury mission/Chlyssich in the Desert.
//RNST_BindPlayers(first, second: integer);

//Creates a player who will have same settings as other player except randomization rectangle. This players will be bound and will have mirrored storehouse placement.
//RNST_InitBoundPlayer(playerId, boundPlayerId, xMin, xMax, yMin, yMax: integer);

//Creates setting for script to add some units at start after storehouse will be placed. Works only foe citizens!!! Don't add any warriors here.
//RNST_SetPlayerCitizensAtStart(playerId: integer; unitType: TKMUnitType; count: integer);

//Creates setting for script to add some resources to starting store house for all players if they have no custom settings.
//RNST_SetWaresAtStart(wareType: TKMWareType; count: integer);

//Creates custom setting for script to add some resources to starting store house for exact player.
//RNST_SetPlayerWaresAtStart(playerId: integer; wareType: TKMWareType; count: integer);

//Adds count to existing custom setting for player starting storehouse resources. If setting does not exists works as RNST_SetPlayerWaresAtStart().
//RNST_AddPlayerWaresAtStart(playerId: integer; wareType: TKMWareType; count: integer);

//Unlock building types for player at start.
//RNST_SetUnlockedBuildingTypes(playerId: integer; houseTypes : TKMHouseTypeSet);

//Set custom sight radius for player.
//RNST_SetPlayerSightRadius(playerId, radius: integer);

//Custom sight radius for all players. Default value is 20.
//RNST_SightRadius : integer;

//Custom cinematic length. In ticks. Default value is 60. Don't make it lower then 30. It's pointless and script will not work.
//RNST_CinematicLength : integer;

//Is cinematics needed.
//RNST_CinematicDisabled : boolean;

//Is allowed to place storehouse at coal.
//RNST_IsAllowedToPlaceAtCoal : boolean;

//----------------------------------------------------------------------------------------------------------------------------------------------------
//USING EXAMPLE.
//----------------------------------------------------------------------------------------------------------------------------------------------------

//procedure OnMissionStart;
//begin

  //Init 4 players randomization will be from (15;2) to (68;234).
  //RNST_InitPlayer(0, 15,68,2,234, false);
  //RNST_InitPlayer(1, 15,68,2,234, false);

  //Init 4 players randomization will be from (15;2) to (68;234). Also giving 5 serfs and workers at start. They will have slow build order.
  //RNST_InitPlayerEx(2, 15,68,2,234,5,5, true);
  //RNST_InitPlayerEx(3, 15,68,2,234,5,5, true);

  //Players 1 and 2 will have mirrored location of storehouse.
  //RNST_BindPlayers(0,1);

  //Setting starting resources for storehouse for all players.
  //RNST_SetWaresAtStart(wtStone,80);
  //RNST_SetWaresAtStart(wtTimber, 50);
  //RNST_SetWaresAtStart(wtGold, 60);
  //RNST_SetWaresAtStart(wtWine, 40);
  //RNST_SetWaresAtStart(wtBread, 30);
  //RNST_SetWaresAtStart(wtSausage, 15);
  //RNST_SetWaresAtStart(wtFish, 10);

  //Setting custom resource amount for player 0. He will have 100 fish instead of 10.
  //RNST_SetPlayerWaresAtStart(0, wtFish, 100);

  //Adding more fish. He will have 120 fish instead of 100.
  //RNST_AddPlayerWaresAtStart(0, wtFish, 20);

  //Giving player 0 two additional farmers at start.
  //RNST_SetPlayerCitizensAtStart(0, utFarmer,2);

  //Unlock farm and mill at start for player 0.
  //RNST_SetUnlockedBuildingTypes(0, [htFarm, htMill]);

  //All players will have 15 tiles sight radius around storehouse at start.
  //RNST_SightRadius := 15;

  //Player 0 will have 40 tiles instead of 15 sight radius around storehouse at start.
  //RNST_SetPlayerSightRadius(0, 40);

  //7 seconds of cinematic length at start.
  //RNST_CinematicLength := 70;

  //Disable starting cinematics.
  //RNST_CinematicDisabled := true;

  //Disable killing starting units. You need at least 1 unit to define player. And this starting unit will be removed if you will not turn off this flag.
  //RNST_KillingUnitsDisabled := true;

//end;


//That's it. Enjoy.

//----------------------------------------------------------------------------------------------------------------------------------------------------
//CODE.
//----------------------------------------------------------------------------------------------------------------------------------------------------


{$EVENT evtMissionStart:RNST_OnMissionStart}
{$EVENT evtHouseBuilt:RNST_OnHouseBuilt}
{$EVENT evtTick:RNST_OnTick}

type RNST_WareSetting =
record
  WareType : TKMWareType;
  Count : integer;
end;

type RNST_UnitSetting =
record
  UnitType : TKMUnitType;
  Count : integer;
end;

type RNST_Player =
record
  Id: integer;

  XMIN: integer;
  XMAX: integer;
  YMIN: integer;
  YMAX: integer;

  StartingUnits: array of RNST_UnitSetting;
  StartingResourses: array of RNST_WareSetting;
  UnlockedBuildings: TKMHouseTypeSet;

  BoundPlayers: TIntegerArray;
  SlowBuildOrder: boolean;

  SightRadius: integer;
end;

const
  //Maximum coal amount tile where can be storehouse placed.
  RNST_MaxCoalAmount = 2;
  //Default sight radius for all players.
  RNST_DefaultSightRadius = 20;
  //Default cinematics length. In ticks.
  RNST_DefaultCinematicLength = 60;

var
  RNST_Players: array of RNST_Player;
  RNST_WareSettings: array of RNST_WareSetting;

  //Is allowed to place randomly storehouse at coal tiles.
  RNST_IsAllowedToPlaceAtCoal : boolean;
  //Custom sight radius for all players.
  RNST_SightRadius : integer;
  //Custom cinematic length. In ticks.
  RNST_CinematicLength : integer;
  //Is cinematics needed.
  RNST_CinematicDisabled : boolean;
  //Should starting units be killed.
  RNST_KillingUnitsDisabled: boolean;
  //Do we need open fog around storehouse.
  RNST_FogAroundStoreHouseDisabled: boolean;


function RNST_AddTIntegerElement(arr: TIntegerArray; element: integer): TIntegerArray;
begin
  result := arr;
  setLength(result, Length(result) + 1);
  result[High(result)] := element;
end;

function RNST_FindPlayerIndex(playerId: integer): integer;
var
  i: integer;

begin
  result := -1;

  for i := 0 to Length(RNST_Players) - 1 do
  begin
    if (RNST_Players[i].Id = playerId)then
    begin
      result:= i;
      exit;
    end;
  end;
end;

function RNST_FindPlayerStoreHouseId(playerId : integer): integer;
var
  i: integer;
  houseIds : array of integer;

begin

  result := -1;
  houseIds := States.PlayerGetAllHouses(playerId);

  for i:= 0 to Length(houseIds) -1 do
  begin
    if (S.HouseTypeEx(houseIds[i]) = htStore) then
    begin
      result := houseIds[i];
      exit;
    end;
  end;
end;

function RNST_FindInitializedBoundPlayer(playerId: integer): integer;
var
  i: integer;
  index : integer;

begin
  result := -1;

  index := RNST_FindPlayerIndex(playerId);

  if (index = -1) then
  begin
    exit;
  end;

  for i := 0 to High(RNST_Players[index].BoundPlayers) do
  begin

    if (RNST_FindPlayerStoreHouseId(RNST_Players[index].BoundPlayers[i]) <> -1) then
    begin
      result := RNST_Players[index].BoundPlayers[i];
    end;
  end;

end;

function RNST_FindUnitSettingIndex(settings: array of RNST_UnitSetting; unitType: TKMUnitType) : integer;
var
  i: integer;

begin
  result := -1;

  for i := 0 to Length(settings) - 1 do
  begin
    if (settings[i].UnitType = unitType)then
    begin
      result:= i;
      exit;
    end;
  end;
end;

function RNST_FindWareSettingIndex(settings: array of RNST_WareSetting; wareType: TKMWareType) : integer;
var
  i: integer;

begin
  result := -1;

  for i := 0 to Length(settings) - 1 do
  begin
    if (settings[i].WareType = wareType)then
    begin
      result:= i;
      exit;
    end;
  end;
end;

function RNST_GetStartingUnitsCount(unitsSettings: array of RNST_UnitSetting): integer;
var
  i: integer;
begin
  for i:=0 to Length(unitsSettings)-1 do
  begin
    result := result + unitsSettings[i].Count;
  end;
end;

function RNST_GetNextStartingUnitTypeToAdd(unitsSettings: array of RNST_UnitSetting; addedCount: integer) : TKMUnitType;
var
  i: integer;
  count: integer;
begin
  result := utNone;
  for i:=0 to Length(unitsSettings)-1 do
  begin
    count := count + unitsSettings[i].Count;
    if (count > addedCount) then
    begin
      result:= unitsSettings[i].UnitType;
      exit;
    end;
  end;
end;

function RNST_IsPossibleToMakeStoreHouse(x,y: integer): boolean;
var
  i: integer;
  j: integer;
begin

  result := false;
  for i:= (x-1) to (x+1) do
  begin

    for j:= (y-2) to y+1 do
    begin

      if (S.MapTileIsInMapCoords(i, j) = false) then
      begin
        exit;
      end;

      //If we can build, then we have no problems.
      if (S.MapTilePassabilityEx(i, j, tpBuild) = true) then
  		begin
  			continue;
  		end;

      //If we can't build without object on tile there is nothing to think about.
  		if (S.MapTilePassabilityEx(i, j, tpBuildNoObj) = false) then
  		begin
  			exit;
  		end;

      //If object that not alows to make building is a tree then we will take it away later.
      if (S.MapTilePassabilityEx(i, j, tpCutTree) = false) then
      begin
        exit;
      end;

    end;
	end;

  result:= true;

end;

function RNST_IsPossibleToMakeRoad(playerId,x,y: integer):boolean;
var
  isRoadPlaced: boolean;
begin
  isRoadPlaced := Actions.GiveRoad(playerId,x,y);
  if (isRoadPlaced) then
  begin
    result := true;
    Actions.RemoveRoad(x,y);
  end else
  begin
    result := false;
  end;
end;

function RNST_IsPossibleToPlaceRoadAndMove(x,y: integer) : boolean;
begin
  result := S.MapTilePassabilityEx(x,y,tpWalk) and S.MapTilePassabilityEx(x,y,tpMakeRoads);
end;

function RNST_IsResourceMountainTile(x,y: integer): boolean;
begin
  result := S.MapTileHasTerrainKind(x,y, tkGoldMount) or S.MapTileHasTerrainKind(x,y, tkIronMount) or S.MapTileHasTerrainKind(x,y, tkStone);
end;

function RNST_IsLargeCoalTile(x,y: integer): boolean;
begin
  //Low amount of coal is allowed.
  result := S.MapTileIsCoal(x,y) > RNST_MaxCoalAmount;
end;

//Go spiral way and add citizens. (Actually it's not spiral but I like the result).
procedure RNST_AddCitizens(player : RNST_Player; x,y: integer);
var
  unitsCount: integer;
  nextUnitType: TKMUnitType;

  axis : string;
  xStep : integer;
  yStep : integer;
  radius : integer;
  currentX : integer;
  currentY: integer;

  dirrectionCounter: integer;
begin

  unitsCount := RNST_GetStartingUnitsCount(player.StartingUnits);
  currentX := x;
  currentY := y;

  xStep := 1;
  yStep := 1;

  axis:= 'Y';
  dirrectionCounter:= 0;

  //27 is just wonderful number. After all citizens will be added loop will do nothing and end fast anyway.
  for radius := 1 to 27 do
  begin
    while (S.StatCitizenCount(player.Id) < unitsCount) do
    begin

      if (axis = 'Y') then
      begin

        currentY := currentY + yStep;
        if ((currentY >= (y + radius)) or (currentY <= (y - radius))) then
        begin

          xStep := -xStep;
          axis := 'X';
          Inc(dirrectionCounter)
        end;

      end else
      begin

        currentX := currentX + xStep;
        if ((currentX >= (x + radius)) or (currentX <= (x - radius))) then
        begin

          yStep := -yStep;
          axis := 'Y';
          Inc(dirrectionCounter)
        end;
      end;


      nextUnitType := RNST_GetNextStartingUnitTypeToAdd(player.StartingUnits, S.StatCitizenCount(player.Id));
      Actions.GiveUnitEx(player.Id, nextUnitType, currentX, currentY, 6);

      //We go to 4 dirrections before we increse radius.
      if (dirrectionCounter > 4) then
      begin
        dirrectionCounter:= 0;
        break;
      end;
    end;
  end;

end;

procedure RNST_AddWares(houseId: integer; settings : array of RNST_WareSetting);
var
  index : integer;
  wareType: TKMWareType;
begin

  for wareType := wtTrunk to wtFish do
  begin
    index := RNST_FindWareSettingIndex(settings, wareType);
    if (index <> -1) then
    begin
      A.HouseAddWaresToEx(houseId, wareType, settings[index].Count);
      continue;
    end;

    index := RNST_FindWareSettingIndex(RNST_WareSettings, wareType);
    if (index <> -1) then
    begin
      A.HouseAddWaresToEx(houseId, wareType, RNST_WareSettings[index].Count);
      continue;
    end;
  end;
end;

function RNST_GetTreesCount(x,y: integer): integer;
var
  i: integer;
  j: integer;
begin
	for i:= (x-1) to (x+1) do
	begin

    for j:= (y-2) to y+1 do
	  begin

      if (S.MapTilePassabilityEx(i, j, tpCutTree) = true) then
			begin
				Inc(result);
			end;
    end;
	end;
end;

function RNST_GetPlayerSightRadius(player : RNST_Player): integer;
begin
  result := U.IfThenI(player.SightRadius > 0,
            player.SightRadius,
            U.IfThenI(RNST_SightRadius > 0,
                RNST_SightRadius,
                RNST_DefaultSightRadius));
end;

procedure ShareFog(playerId, x, y: integer);
var
  i: integer;
begin
  for i:= 0 to High(RNST_Players) do
  begin
    if ((i <> playerId) and S.PlayerEnabled(i) and (S.PlayerAllianceCheck(i, playerId))) then
    begin
      A.FogRevealCircle(i, x, y, RNST_GetPlayerSightRadius(RNST_Players[playerId]));
    end;
  end;
end;

//TODO Remove when A.HouseUnlockEx will apear.
function RNST_GetHouseType(houseType : TKMHouseType):integer;
begin
  case houseType of
    htArmorSmithy   : result := 10;
    htArmorWorkshop : result := 20;
    htBakery        : result := 7;
    htBarracks      : result := 21;
    htButchers      : result := 24;
    htCoalMine      : result := 3;
    htFarm          : result := 8;
    htFishermans    : result := 6;
    htGoldMine      : result := 5;
    htInn           : result := 27;
    htIronMine      : result := 4;
    htIronSmithy    : result := 1;
    htMarket        : result := 29;
    htMetallurgists : result := 15;
    htMill          : result := 22;
    htQuarry        : result := 14;
    htSawmill       : result := 0;
    htSchool        : result := 13;
    htSiegeWorkshop : result := 23;
    htStables       : result := 12;
    htStore         : result := 11;
    htSwine         : result := 16;
    htTannery       : result := 25;
    htTownHall      : result := 18;
    htWatchTower    : result := 17;
    htWeaponSmithy  : result := 2;
    htWeaponWorkshop: result := 19;
    htVineyard      : result := 28;
    htWoodcutters   : result := 9;
  end;
end;

procedure RNST_UnlockBuildingTypes(player: RNST_Player);
var
  houseType: TKMHouseType;

begin

  for houseType := htArmorSmithy to htWoodcutters do
  begin
    if(houseType in player.UnlockedBuildings) then
    begin
      A.HouseAllow(player.Id, RNST_GetHouseType(houseType), true);
      A.HouseUnlock(player.Id, RNST_GetHouseType(houseType));
    end;
  end;
end;

function RNST_TryToSetStoreHouse(playerId,x,y : integer): integer;
var
  treesCount: integer;
  index: integer;
begin

  if (not RNST_IsPossibleToMakeStoreHouse(x,y)) then
  begin
    exit;
  end;

  //Check is it possible to walk and make roads around storehouse.
  if(not RNST_IsPossibleToPlaceRoadAndMove(x  ,y+1) or  not RNST_IsPossibleToPlaceRoadAndMove(x-1,y+1) or
     not RNST_IsPossibleToPlaceRoadAndMove(x-2,y+2) or  not RNST_IsPossibleToPlaceRoadAndMove(x-2,y  ) or
     not RNST_IsPossibleToPlaceRoadAndMove(x-2,y-1) or  not RNST_IsPossibleToPlaceRoadAndMove(x-2,y-2) or
     not RNST_IsPossibleToPlaceRoadAndMove(x-2,y-3) or  not RNST_IsPossibleToPlaceRoadAndMove(x-1,y-3) or
     not RNST_IsPossibleToPlaceRoadAndMove(x  ,y-3) or  not RNST_IsPossibleToPlaceRoadAndMove(x+1,y-3) or
     not RNST_IsPossibleToPlaceRoadAndMove(x+2,y-3) or  not RNST_IsPossibleToPlaceRoadAndMove(x+2,y-2) or
     not RNST_IsPossibleToPlaceRoadAndMove(x+2,y-1) or  not RNST_IsPossibleToPlaceRoadAndMove(x+2,y  ) or
     not RNST_IsPossibleToPlaceRoadAndMove(x+2,y+1) or  not RNST_IsPossibleToPlaceRoadAndMove(x+1,y+1)) then
  begin
    exit;
  end;

  if (not RNST_IsAllowedToPlaceAtCoal) then
  begin
    //Check that storehouse is not locatated deep in coal.
    if (RNST_IsLargeCoalTile(x-1,y) or RNST_IsLargeCoalTile(x-1,y-2) or RNST_IsLargeCoalTile(x-1,y-1)) then
    begin
      exit;
    end;
  end;

  //Check top of storhouse for gold/iron/stone mountains.
  if (RNST_IsResourceMountainTile(x-2,y-3) or RNST_IsResourceMountainTile(x-1,y-3) or
      RNST_IsResourceMountainTile(x  ,y-3) or RNST_IsResourceMountainTile(x+1,y-3) or
      RNST_IsResourceMountainTile(x+2,y-3))then
  begin
    exit;
  end;

  index := RNST_FindPlayerIndex(playerId);

  //Count trees before placing storehouse.
  treesCount := RNST_GetTreesCount(x,y);

  //This place is ok. We start placing storehouse here.
  result := A.GiveHouseEx(playerId,htStore,x,y);

  //The bonus tree trunks for trees that are gone cause we place storehouse on them.
	A.HouseAddWaresToEx(result,wtTrunk,treesCount);

  //Remove all objects in front of the storehouse.
  A.MapTileObjectSet(x,(y+1),255);
	A.MapTileObjectSet(x+1,(y+1),255);
	A.MapTileObjectSet(x-1,(y+1),255);

  //Roads in front of storehouse.
  A.GiveRoad(playerId,x  ,y+1);
	A.GiveRoad(playerId,x+1,y+1);
	A.GiveRoad(playerId,x-1,y+1);
end;

procedure RNST_PlaceRandomStorehouse(player : RNST_Player);
var
  storeHouseId: integer;
  boundPlayerStoreHouseId: integer;
  x: integer;
  y: integer;
  boundPlayer: integer;
  boundPlayerIndex : integer;
begin

  while (storeHouseId = 0) do
  begin

    x := 0;
    y := 0;
    boundPlayer := RNST_FindInitializedBoundPlayer(player.Id);
    boundPlayerIndex := RNST_FindPlayerIndex(boundPlayer);
    //If there is a bound player and storehouse for this player was placed we place storehouse using this coordinates.
    if (boundPlayerIndex <> -1) then
    begin

      boundPlayerStoreHouseId := RNST_FindPlayerStoreHouseId(boundPlayer);

      if (boundPlayerStoreHouseId > 0)then
      begin

        x := player.XMIN + (S.HousePositionX(boundPlayerStoreHouseId) - RNST_Players[boundPlayerIndex].XMIN);
        y := player.YMIN + (S.HousePositionY(boundPlayerStoreHouseId) - RNST_Players[boundPlayerIndex].YMIN);
      end;
    end;

    //If there is no bound player or bound player still has no storehouse.
    if ((x = 0) and (y = 0))then
    begin
      x := player.XMIN + (States.KamRandomI((player.XMAX-player.XMIN)));
      y := player.YMIN + (States.KamRandomI((player.YMAX-player.YMIN)));
    end;

    storeHouseId := RNST_TryToSetStoreHouse(player.Id,x,y);
    if (storeHouseId <> 0) then
    begin
      //Adding wares to storehouse.
      RNST_AddWares(storeHouseId, player.StartingResourses);

      if (player.SlowBuildOrder) then
      begin
        A.HouseAllow(player.Id, RNST_GetHouseType(htInn), false);
        A.HouseAllow(player.Id, RNST_GetHouseType(htQuarry), false);
        A.HouseAllow(player.Id, RNST_GetHouseType(htWoodcutters), false);
      end;

      RNST_UnlockBuildingTypes(player);
      RNST_AddCitizens(player, x, y);
    end;
  end;
end;

//----------------------------------------------------------------------------------------------------------------------------------------------------
//Public interface------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------

//Bind two players. Same Bound players have mirrored locations of storehouses.
//first - first player id.
//second - second player id.
procedure RNST_BindPlayers(first, second: integer);
var
  firstPlayerIndex: integer;
  secondPlayerIndex: integer;
begin

  //Look for players. If one of them is not found binding can't be added.
  firstPlayerIndex := RNST_FindPlayerIndex(first);
  if (firstPlayerIndex = -1) then
  begin
    exit;
  end;

  secondPlayerIndex := RNST_FindPlayerIndex(second);
  if (secondPlayerIndex = -1) then
  begin
    exit;
  end;

  RNST_Players[firstPlayerIndex].BoundPlayers := RNST_AddTIntegerElement(RNST_Players[firstPlayerIndex].BoundPlayers, second);
  RNST_Players[secondPlayerIndex].BoundPlayers := RNST_AddTIntegerElement(RNST_Players[secondPlayerIndex].BoundPlayers, first);
end;

//Creates setting for script to add some units at start after storehouse will be placed. Works only foe citizens!!! Don't add any warriors here.
//playerId - id of player, that will have this units at start.
//unitType - type of units you want to add.
//count - the amount of units.
procedure RNST_SetPlayerCitizensAtStart(playerId: integer; unitType: TKMUnitType; count: integer);
var
  playerIndex : integer;
  settingIndex : integer;
  settingsCount : integer;
  newSetting : RNST_UnitSetting;

begin

  playerIndex := RNST_FindPlayerIndex(playerId);

  if (playerIndex = -1) then
  begin
    exit;
  end;

  settingIndex := RNST_FindUnitSettingIndex(RNST_PLayers[playerIndex].StartingUnits, unitType);

  if (settingIndex >= 0) then
  begin
    RNST_Players[playerIndex].StartingUnits[settingIndex].Count := count;
  end else
  begin
    settingsCount := Length(RNST_Players[playerIndex].StartingUnits);
    newSetting.UnitType := unitType;
    newSetting.Count := count;
    setLength(RNST_Players[playerIndex].StartingUnits, settingsCount + 1);
    RNST_Players[playerIndex].StartingUnits[settingsCount] := newSetting;
  end;
end;

//Creates custom setting for script to add some resources to starting store house for exact player.
//playerId - id of player, that will have this units at start.
//wareType - type of resource you want to add.
//count - the amount of units.
procedure RNST_SetPlayerWaresAtStart(playerId: integer; wareType: TKMWareType; count: integer);
var
  playerIndex : integer;
  settingIndex : integer;
  settingsCount : integer;
  newSetting : RNST_WareSetting;

begin

  playerIndex := RNST_FindPlayerIndex(playerId);

  if (playerIndex = -1) then
  begin
    exit;
  end;

  settingIndex := RNST_FindWareSettingIndex(RNST_PLayers[playerIndex].StartingResourses, wareType);

  if (settingIndex >= 0) then
  begin
    RNST_Players[playerIndex].StartingResourses[settingIndex].Count := count;
  end else
  begin
    settingsCount := Length(RNST_Players[playerIndex].StartingResourses);
    newSetting.WareType := wareType;
    newSetting.Count := count;
    setLength(RNST_Players[playerIndex].StartingResourses, settingsCount + 1);
    RNST_Players[playerIndex].StartingResourses[settingsCount] := newSetting;
  end;
end;

//Adds count to existing custom setting for player starting storehouse resources. If setting does not exists works as RNST_SetPlayerWaresAtStart().
//playerId - id of player, that will have this units at start.
//wareType - type of resource you want to add.
//count - the amount of units.
procedure RNST_AddPlayerWaresAtStart(playerId: integer; wareType: TKMWareType; count: integer);
var
  playerIndex : integer;
  settingIndex : integer;
  settingsCount : integer;
  newSetting : RNST_WareSetting;

begin

  playerIndex := RNST_FindPlayerIndex(playerId);

  if (playerIndex = -1) then
  begin
    exit;
  end;

  settingIndex := RNST_FindWareSettingIndex(RNST_PLayers[playerIndex].StartingResourses, wareType);

  if (settingIndex >= 0) then
  begin
    RNST_Players[playerIndex].StartingResourses[settingIndex].Count := RNST_Players[playerIndex].StartingResourses[settingIndex].Count + count;
  end else
  begin
    settingsCount := Length(RNST_Players[playerIndex].StartingResourses);
    newSetting.WareType := wareType;
    newSetting.Count := count;
    setLength(RNST_Players[playerIndex].StartingResourses, settingsCount + 1);
    RNST_Players[playerIndex].StartingResourses[settingsCount] := newSetting;
  end;
end;

//Creates setting for script to add some resources to starting store house for all players if they have no custom settings.
//wareType - type of resource you want to add.
//count - the amount of units.
procedure RNST_SetWaresAtStart(wareType: TKMWareType; count: integer);
var
  settingIndex : integer;
  settingsCount : integer;
  newSetting : RNST_WareSetting;

begin

  settingIndex := RNST_FindWareSettingIndex(RNST_WareSettings, wareType);

  if (settingIndex >= 0) then
  begin
    RNST_WareSettings[settingIndex].Count := count;
  end else
  begin
    settingsCount := Length(RNST_WareSettings);
    newSetting.WareType := wareType;
    newSetting.Count := count;
    setLength(RNST_WareSettings, settingsCount + 1);
    RNST_WareSettings[settingsCount] := newSetting;
  end;
end;

//Unlock building types for player at start.
//playerId - id of player, that will get this bonus.
//houseTypes - set of building types, that will be unlocked.
procedure RNST_SetUnlockedBuildingTypes(playerId: integer; houseTypes : TKMHouseTypeSet);
var
  index: integer;

begin

  index:= RNST_FindPlayerIndex(playerId);
  if (index = -1) then
  begin
    exit;
  end;

  RNST_Players[index].UnlockedBuildings := RNST_Players[index].UnlockedBuildings + houseTypes;
end;

//Set custom sight radius for player.
//playerId - id of player, that will have this radius.
//radius - sight radius
procedure RNST_SetPlayerSightRadius(playerId, radius: integer);
var
  index: integer;
begin
  index:= RNST_FindPlayerIndex(playerId);
  if (index = -1) then
  begin
    exit;
  end;

  RNST_Players[index].SightRadius := radius;
end;

//Initializing player for script.
//playerId - id of player.
//xMin - minimum X value.
//xMax - maximum X value.
//yMin - minimum Y value.
//yMax - maximum Y value.
//slowBuildOrder - defines of player have to make Inn before quarries and quarries before woodcutters.
procedure RNST_InitPlayer(playerId, xMin, xMax, yMin, yMax : integer; slowBuildOrder: boolean);
var
  index: integer;
  count: integer;
  newPlayer: RNST_Player;
begin

  index:= RNST_FindPlayerIndex(playerId);
  if (index >= 0) then
  begin
    exit;
  end;
  count := Length(RNST_Players);

  newPlayer.Id := playerId;
  newPlayer.XMIN := xMin;
  newPlayer.XMAX := xMax;
  newPlayer.YMIN := yMin;
  newPlayer.YMAX := yMax;
  newPlayer.SlowBuildOrder := slowBuildOrder;

  setLength(RNST_Players, count + 1);
  RNST_Players[count] := newPlayer;
end;

//Initializing player for script.
//Same as RNST_InitPlayer, but here you can also add starting serfs and workers count.
//workersCount - the amount of workers at start.
//serfsCount - the amount of serfs at start.
//slowBuildOrder - defines of player have to make Inn before quarries and quarries before woodcutters.
procedure RNST_InitPlayerEx(playerId, xMin, xMax, yMin, yMax, workersCount, serfsCount: integer; slowBuildOrder: boolean);
begin

  RNST_InitPlayer(playerId, xMin, xMax, yMin, yMax, slowBuildOrder);
  RNST_SetPlayerCitizensAtStart(playerId, utSerf, serfsCount);
  RNST_SetPlayerCitizensAtStart(playerId, utBuilder, workersCount);
end;

//Initializing player for script. This player will have same settings as bound player except randomization rectangle.
//playerId - id of player.
//boundPlayerId - from this player all settings will be copied and player will be bound to it.
//xMin - minimum X value.
//xMax - maximum X value.
//yMin - minimum Y value.
//yMax - maximum Y value.
procedure RNST_InitBoundPlayer(playerId, boundPlayerId, xMin, xMax, yMin, yMax: integer);
var
  i: integer;
  boundIndex: integer;

begin

  boundIndex := RNST_FindPlayerIndex(boundPlayerId);

  if (boundIndex = -1) then
  begin
    exit;
  end;

  RNST_InitPlayer(playerId, xMin, xMax, yMin, yMax, RNST_Players[boundIndex].SlowBuildOrder);

  for i:= 0 to High(RNST_Players[boundIndex].StartingUnits) do
  begin
    RNST_SetPlayerCitizensAtStart(playerId, RNST_Players[boundIndex].StartingUnits[i].UnitType, RNST_Players[boundIndex].StartingUnits[i].Count)
  end;

  for i:= 0 to High(RNST_Players[boundIndex].StartingResourses) do
  begin
    RNST_SetPlayerWaresAtStart(playerId, RNST_Players[boundIndex].StartingResourses[i].WareType, RNST_Players[boundIndex].StartingResourses[i].Count)
  end;

  RNST_SetUnlockedBuildingTypes(playerId, RNST_Players[boundIndex].UnlockedBuildings);
  RNST_SetPlayerSightRadius(playerId, RNST_Players[boundIndex].SightRadius);

  RNST_BindPlayers(playerId, boundPlayerId);
end;

//----------------------------------------------------------------------------------------------------------------------------------------------------
//Events----------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------

procedure RNST_OnMissionStart;
var
  i: integer;
  j: integer;
  storeHouseId: integer;
  x: integer;
  y: integer;
  groupsIds: array of integer;
begin

  for i:= 0 to High(RNST_Players) do
  begin
    if (S.PlayerEnabled(RNST_Players[i].Id)) then
    begin

      groupsIds := S.PlayerGetAllGroups(RNST_Players[i].Id);

      RNST_PlaceRandomStorehouse(RNST_Players[i]);

      if (not RNST_KillingUnitsDisabled) then
      begin
        for j:=0 to High(groupsIds) do
        begin
          A.GroupKillAll(groupsIds[j], true);
        end;
      end;
    end;
  end;

  if (not RNST_FogAroundStoreHouseDisabled) then
  begin
    for i:= 0 to High(RNST_Players) do
    begin
      if (S.PlayerEnabled(RNST_Players[i].Id)) then
      begin
        A.FogCoverAll(i);
      end;
    end;
  end;

  for i:= 0 to High(RNST_Players) do
  begin
    if (S.PlayerEnabled(RNST_Players[i].Id)) then
    begin
      storeHouseId := RNST_FindPlayerStoreHouseId(RNST_Players[i].Id);
      x := S.HousePositionX(storeHouseId);
      y := S.HousePositionY(storeHouseId);
      if (not RNST_FogAroundStoreHouseDisabled) then
      begin
        A.FogRevealCircle(i, x, y,RNST_GetPlayerSightRadius(RNST_Players[i]));
        ShareFog(i, x, y);
      end;
    end;
  end;
end;

procedure RNST_OnTick;
var
  i: Integer;
  storeHouseId: integer;
begin

  if (RNST_CinematicDisabled)then
  begin
    exit;
  end;

	if S.GameTime = 2 then
	begin

		for i := 0 to High(RNST_Players) do
    begin

			if S.PlayerEnabled(RNST_Players[i].Id) then
      begin
				A.CinematicStart(i);
      end;
    end;
	end;


	if States.GameTime = 15 then
	begin

		for i := 0 to High(RNST_Players) do
    begin

			if (S.PlayerEnabled(RNST_Players[i].Id)) then
      begin
        storeHouseId := RNST_FindPlayerStorehouseId(RNST_Players[i].Id);
				A.CinematicPanTo(i,S.HousePositionX(storeHouseId),S.HousePositionY(storeHouseId),4);
      end;
    end;
	end;

	if States.GameTime = U.IfThenI(RNST_CinematicLength > 0, RNST_CinematicLength, RNST_DefaultCinematicLength) then
	begin

		for i := 0 to High(RNST_Players) do
    begin

			if S.PlayerEnabled(i) then
			begin
      	A.CinematicEnd(i);
      end;
    end;
    end;
end;

procedure RNST_OnHouseBuilt(aHouse: Integer);
var
  playerIndex: integer;

begin

  playerIndex := RNST_FindPlayerIndex(S.HouseOwner(aHouse));

  if (not RNST_Players[playerIndex].SlowBuildOrder) then
  begin
    exit;
  end;

  case States.HouseTypeEx(aHouse) of
    htSchool: A.HouseAllow(States.HouseOwner(aHouse), RNST_GetHouseType(htInn), True);
    htInn:    A.HouseAllow(States.HouseOwner(aHouse), RNST_GetHouseType(htQuarry), True);
    htQuarry: A.HouseAllow(States.HouseOwner(aHouse), RNST_GetHouseType(htWoodcutters), True);
  end;
end;
