anchor.diff:      YMax := High(aCountArr);
       XMin := Low(aCountArr[YMin]);
       XMax := High(aCountArr[YMin]);
  +    // Distance from bottom
       for Y := Max(YMax-DIST_FROM_BOTTOM, YMin) to YMax do
         for X := XMin to XMax do
           aCountArr[Y,X] := -aCountArr[Y,X];
  +    // 1 shape from map sides
       for Y := YMin to Max(YMax-DIST_FROM_BOTTOM-1, YMin) do
       begin
         aCountArr[Y,XMin] := -aCountArr[Y,XMin];
  
positive.code:     (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,0,0,0),
      (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
    );
  var
    X_0,Y_0,X_1,Y_1,X_2,Y_2,X1,X2,Y1,Y2,sum: Integer;
    H1,H2: TInteger2Array;
  begin
  
    H1 := LinearInterpolation(7,20);
    H2 := LinearInterpolation(5,60);
  	for Y1 := 1 to gTerrain.MapY-1 do
      for X1 := 1 to gTerrain.MapX-1 do
      begin
        TilesPartsArr.Height[Y1,X1] := H1[Y1,X1] + H2[Y1,X1] + fRNG.RandomI(HeightVariance[ A[Y1,X1] ]);
        //TilesPartsArr.Height[Y1,X1] := H1[Y1,X1] + H2[Y1,X1] + HeightMix[ A[Y1,X1] ]*2 + fRNG.RandomI(HeightVariance[ A[Y1,X1] ]);
      end;
  
    //constructor Create(aMinLimit, aMaxLimit: TKMPoint; var aSearchArr: TInteger2Array; var aFillArr: TKMByte2Array; const aScanEightTiles: Boolean = False); reintroduce;
    //procedure QuickFlood(aX,aY,aSearch,aNewSearch,aFill: SmallInt); reintroduce;
    //FillHeight := TKMFillBiome.Create();
    //try
  
    //finally
    //  Fillheight.Free;
    //end;
  
    for Y_1 := 1 to gTerrain.MapY-1 do
    begin
  
negative.code:     end;
    end;
  
    procedure RndPointsInRadius(aObstacle: TObstacleType; aX,aY: Integer);
    const
      ADAPTIVE_RADIUS: array[TObstacleType] of Single = (0.6,0.5,0.8,0.5,0.5); //(otSwamp,otWater,otWetland,otEgold,otEIron);
    var
      X,Y, Cnt, MaxCnt, Overflow, MaxAttempt: Integer;
      Radius, RandomSize: Single;
      Point, MaxP, MinP: TKMPoint;
    begin
      MinP := KMPoint(Low(aPointsArr[0]), Low(aPointsArr));
      MaxP := KMPoint(High(aPointsArr[0]), High(aPointsArr));
      RandomSize := 1 - fRNG.Random() * RMGSettings.Obstacle.Variance * 0.1;
      MaxCnt := Max(1, Round( RMGSettings.Obstacle.Size * RandomSize) );
      Radius := Max(1, Round(sqrt(MaxCnt)) * ADAPTIVE_RADIUS[aObstacle] );
      MaxAttempt := MaxCnt * 5;
      Overflow := 0;
      Cnt := 0;
      while (Cnt < MaxCnt) AND (Overflow < MaxAttempt) do
      begin
        Overflow := Overflow + 1;
        Point := RNDPointInCircle(MinP,MaxP,KMPoint(aX,aY),Radius);
        if (fRNG.Random() < P[Point.Y,Point.X]) AND (aVoronoi[Point.Y,Point.X] <> 0) then
        begin
          X := aPointsArr[ Point.Y, Point.X ].X;
          Y := aPointsArr[ Point.Y, Point.X ].Y;
          FillObstacle.QuickFlood(X, Y, aVoronoi[Y,X], 0, Byte(OBST2BIOME[aObstacle]));
          Cnt := Cnt + 1;
        end;
      end;
    end;
  
    procedure RndWalk(aObstacle: TObstacleType; aX,aY: Integer);
    const
      EXPAND_RESTRICTION: array[TObstacleType] of Byte = (3,1,2,4,4);
    var
      X,Y,X1,Y1,K, MaxIdx, MaxCnt, cntr, finalCnt: Integer;
      check: Boolean;
      ProbIdx: array[0..3] of Byte;
      Prob: array[0..3] of Single;
      Dir: array[0..3] of TKMPoint;
    begin
      X := aX;
      Y := aY;
      // Fill array A with obstacles with using random walk
      cntr := 0;
      finalCnt := Max(1,Round((1 - fRNG.Random() * RMGSettings.Obstacle.Variance * 0.1) * (RMGSettings.Obstacle.Size)));
      if (otWater = aObstacle) then
        finalCnt := Round(finalCnt*1.5);
      check := True;
      while check AND (cntr < finalCnt) do
      begin
        // Scan surrounding points
        cntr := cntr + 1;
        Dir[0] := KMPoint( X, Min(Y+1, High(P)) );
        Dir[1] := KMPoint( X, Max(Y-1, Low(P)) );
        Dir[2] := KMPoint( Min(X+1, High(P[Y])), Y );
        Dir[3] := KMPoint( Max(X-1, Low(P[Y])), Y );
  
        // Get highest probability
        MaxIdx := 0;
        for K := Low(Dir) to High(Dir) do
        begin
          Prob[K] := P[Dir[K].Y,Dir[K].X];
          if (Prob[MaxIdx] < Prob[K]) then
            MaxIdx := K;
        end;
        // Mark all indexes with highest probability
        MaxCnt := 0;
        for K := Low(Dir) to High(Dir) do
          if (Prob[MaxIdx] = Prob[K]) then
          begin
            ProbIdx[MaxCnt] := K;
            MaxCnt := MaxCnt + 1;
          end;
        // Fill several surrounding shapes with our biome
        check := False;
        for K := 1 to Min(MaxCnt, fRNG.RandomI(EXPAND_RESTRICTION[aObstacle]) + 1) do
        begin
          MaxIdx := fRNG.RandomI(MaxCnt);
          if (fRNG.Random < Prob[ ProbIdx[MaxIdx] ]) then
          begin
            X1 := aPointsArr[  Dir[ ProbIdx[MaxIdx] ].Y, Dir[ ProbIdx[MaxIdx] ].X  ].X;
            Y1 := aPointsArr[  Dir[ ProbIdx[MaxIdx] ].Y, Dir[ ProbIdx[MaxIdx] ].X  ].Y;
            if (aVoronoi[Y1,X1] <> 0) then
              FillObstacle.QuickFlood(X1, Y1, aVoronoi[Y1,X1], 0, Byte(OBST2BIOME[aObstacle]));
            // Get coords of next shape
            X := Dir[ ProbIdx[MaxIdx] ].X;
            Y := Dir[ ProbIdx[MaxIdx] ].Y;
            // Set probability to zero (shape will not be longer used)
            P[Y,X] := 0;
            ProbIdx[MaxIdx] := ProbIdx[MaxCnt-1];
            MaxCnt := MaxCnt - 1;
            check := True;
          end;
        end;
      end;
    end;
  
    var
      X,Y,I, MaxCnt: Integer;
      Factor,Probability, ProbabilityReducer: Single;
      MinP, MaxP: TKMPoint;
      Obstacle: TObstacleType;
      ObstacleSeeds: TKMPointArray;
      VisitedArr: TKMByte2Array;
      ShapeFixer: TKMSharpShapeFixer;
      OBST_Probability: array[TObstacleType] of Single;
  begin
  
  // Initialization
  