Subversion Repositories forest

Rev

Blame | Last modification | View Log | RSS feed

  1. library MapGen32;
  2.  
  3. uses
  4.   SysUtils,
  5.   Classes,
  6.   Windows,
  7.   IniFiles;
  8.  
  9. {$R *.res}
  10.  
  11. type
  12.   TFreeSpot = record
  13.     X: integer;
  14.     Y: integer;
  15.     A: integer;
  16.   end;
  17.  
  18. var
  19.   FreeSpots: array of TFreeSpot;
  20.  
  21. var
  22.   cMaxBranchLength: integer = 200;
  23.   cPathFlexibility: integer = 15;
  24.   cMapBorderSize: single = 5;
  25.   cPathWidthFactor: single = 1;
  26.   cPathLength: single = 0.20;
  27.   cTreePlacementMaxAttempts: integer = 1000;
  28.   iniLoaded: boolean = false;
  29.   cPathAcceptCheckCornerCircle: boolean = false;
  30.   cPathAcceptCheckCornerCircleRadius: integer = 500;
  31.   cPathAcceptCheckSquares: boolean = true;
  32.   cPathAcceptCheckSquaresHorizontal: integer = 5;
  33.   cPathAcceptCheckSquaresVertical: integer = 5;
  34.   cPathBuildMaxAttempts: integer = 100;
  35.  
  36. const
  37.   ERR_OK = 0;
  38.   ERR_NO_INI_LOADED = 2;
  39.  
  40. function LoadParametersFromINI(filename: PAnsiChar): DWORD; cdecl;
  41. var
  42.   ini: TMemIniFile;
  43. begin
  44.   ini := TMemIniFile.Create(filename);
  45.   try
  46.     DecimalSeparator := '.';
  47.  
  48.     cMaxBranchLength                   := ini.ReadInteger('MapGen32', 'MaxBranchLength',                   200   );
  49.     cPathFlexibility                   := ini.ReadInteger('MapGen32', 'PathFlexibility',                    15   );
  50.     cMapBorderSize                     := ini.ReadFloat  ('MapGen32', 'MapBorderSize',                       5   );
  51.     cPathWidthFactor                   := ini.ReadFloat  ('MapGen32', 'PathWidthFactor',                     1   );
  52.     cPathLength                        := ini.ReadFloat  ('MapGen32', 'PathLength',                          0.20);
  53.     cTreePlacementMaxAttempts          := ini.ReadInteger('MapGen32', 'TreePlacementMaxAttempts',         1000   );
  54.  
  55.     cPathAcceptCheckCornerCircle       := ini.ReadBool   ('MapGen32', 'PathAcceptCheckCornerCircle',       false);
  56.     cPathAcceptCheckCornerCircleRadius := ini.ReadInteger('MapGen32', 'PathAcceptCheckCornerCircleRadius', 500);
  57.  
  58.     cPathAcceptCheckSquares            := ini.readBool   ('MapGen32', 'PathAcceptCheckSquares',           true);
  59.     cPathAcceptCheckSquaresHorizontal  := ini.ReadInteger('MapGen32', 'PathAcceptCheckSquaresHorizontal', 5);
  60.     cPathAcceptCheckSquaresVertical    := ini.ReadInteger('MapGen32', 'PathAcceptCheckSquaresVertical',   5);
  61.  
  62.     cPathBuildMaxAttempts              := ini.ReadInteger('MapGen32', 'PathBuildMaxAttempts', 100);
  63.   finally
  64.     ini.Free;
  65.   end;
  66.  
  67.   iniLoaded := true;
  68.   result := ERR_OK;
  69. end;
  70.  
  71. function RandBetween(a, b: integer): integer;
  72. begin
  73.   result := Random(b-a+1)+a;
  74. end;
  75.  
  76. function IsInCircle(p, q: TPoint; r: integer): boolean;
  77. begin
  78.   result := sqrt((p.x-q.x)*(p.x-q.x) + (p.y-q.y)*(p.y-q.y)) <= r;
  79. end;
  80.  
  81. function _AcceptedPath(maxX, maxY: integer): boolean;
  82.  
  83.   function __CheckCircle(x, y: integer): boolean;
  84.   var
  85.     i: integer;
  86.     p, q: TPoint;
  87.   begin
  88.     p := Point(x, y);
  89.     result := false;
  90.     for i := Low(FreeSpots) to High(FreeSpots) do
  91.     begin
  92.       q := Point(FreeSpots[i].X, FreeSpots[i].Y);
  93.       if IsInCircle(p, q, cPathAcceptCheckCornerCircleRadius) then
  94.       begin
  95.         result := true;
  96.         Break;
  97.       end;
  98.     end;
  99.   end;
  100.  
  101.   function __CheckSquare(left, top, width, height: integer): boolean;
  102.   var
  103.     i: integer;
  104.   begin
  105.     result := false;
  106.     for i := Low(FreeSpots) to High(FreeSpots) do
  107.     begin
  108.       if (FreeSpots[i].X >= left) and
  109.          (FreeSpots[i].Y >= top) and
  110.          (FreeSpots[i].X <= left+width) and
  111.          (FreeSpots[i].Y <= top+height) then
  112.       begin
  113.         result := true;
  114.         break;
  115.       end;
  116.     end;
  117.   end;
  118.  
  119.   function __AcceptCircleCheck: boolean;
  120.   begin
  121.     result :=
  122.       __CheckCircle(     cPathAcceptCheckCornerCircleRadius,      cPathAcceptCheckCornerCircleRadius) and // oben links
  123.       __CheckCircle(maxX-cPathAcceptCheckCornerCircleRadius,      cPathAcceptCheckCornerCircleRadius) and // oben rechts
  124.       __CheckCircle(     cPathAcceptCheckCornerCircleRadius, maxY-cPathAcceptCheckCornerCircleRadius) and // unten links
  125.       __CheckCircle(maxX-cPathAcceptCheckCornerCircleRadius, maxY-cPathAcceptCheckCornerCircleRadius);    // unten rechts
  126.   end;
  127.  
  128.   function __AcceptSquaresCheck: boolean;
  129.   var
  130.     ix, iy: integer;
  131.     left, top: integer;
  132.     squareWidth, squareHeight: integer;
  133.   begin
  134.     squareWidth  := maxX div cPathAcceptCheckSquaresHorizontal;
  135.     squareHeight := maxY div cPathAcceptCheckSquaresVertical;
  136.     for ix := 0 to cPathAcceptCheckSquaresHorizontal-1 do
  137.     begin
  138.       left := ix * squareWidth;
  139.       for iy := 0 to cPathAcceptCheckSquaresVertical-1 do
  140.       begin
  141.         top := iy * squareHeight;
  142.         if not __CheckSquare(left, top, squareWidth, squareHeight) then
  143.         begin
  144.           result := false;
  145.           exit;
  146.         end;
  147.       end;
  148.     end;
  149.     result := true;
  150.   end;
  151.  
  152. begin
  153.   result := true;
  154.  
  155.   if result and cPathAcceptCheckCornerCircle then
  156.   begin
  157.     result := result and __AcceptCircleCheck;
  158.   end;
  159.  
  160.   if result and cPathAcceptCheckSquares then
  161.   begin
  162.     result := result and __AcceptSquaresCheck;
  163.   end;
  164. end;
  165.  
  166. procedure _BuildPaths(nFreespots, maxX, maxY, nTrees, r, startX, startY, angle: integer);
  167. var
  168.   i: integer;
  169.   x: integer;
  170.   maxdrawn: integer;
  171.   iBeginBranch: integer;
  172. //  midDistance: extended;
  173.   locFlexibility: integer;
  174. //  midDeltaX, midDeltaY: integer;
  175. begin
  176.   SetLength(FreeSpots, nFreespots);
  177.  
  178.   FreeSpots[0].A := angle;
  179.   FreeSpots[0].X := startX;
  180.   FreeSpots[0].Y := startY;
  181.  
  182.   iBeginBranch := 1;
  183.  
  184.   while true do
  185.   begin
  186.     maxdrawn := 0;
  187.     for i := iBeginBranch to iBeginBranch+cMaxBranchLength do
  188.     begin
  189.       if i > High(FreeSpots) then Exit;
  190.  
  191. //      midDeltaX := FreeSpots[i-1].X - startX;
  192. //      midDeltaY := FreeSpots[i-1].Y - startY;
  193. //      midDistance := sqrt(midDeltaX*midDeltaX + midDeltaY*midDeltaY);
  194.  
  195. (*
  196.       if midDistance > 4000 then
  197.         locFlexibility := Round(cPathFlexibility + midDistance/5000)
  198.       else
  199.         locFlexibility := cPathFlexibility;
  200. *)
  201.       locFlexibility := cPathFlexibility;
  202.  
  203.       FreeSpots[i].A := RandBetween(FreeSpots[i-1].A-locFlexibility, FreeSpots[i-1].A+locFlexibility);
  204.       FreeSpots[i].X := FreeSpots[i-1].X + round(r*cos(FreeSpots[i].A/360*2*pi));
  205.       FreeSpots[i].Y := FreeSpots[i-1].Y + round(r*sin(FreeSpots[i].A/360*2*pi));
  206.  
  207.       if FreeSpots[i].X > maxX-cMapBorderSize*r then break;
  208.       if FreeSpots[i].Y > maxY-cMapBorderSize*r then break;
  209.       if FreeSpots[i].X < cMapBorderSize*r then break;
  210.       if FreeSpots[i].Y < cMapBorderSize*r then break;
  211.  
  212.       maxdrawn := i;
  213.     end;
  214.  
  215.     x := RandBetween(maxdrawn div 2, maxdrawn); // RandBetween(0, maxdrawn);
  216.     FreeSpots[maxdrawn+1] := FreeSpots[x]; // virtual free spot
  217.  
  218.     if RandBetween(0,1) = 0 then
  219.       FreeSpots[maxdrawn+1].A := FreeSpots[x].A-90
  220.     else
  221.       FreeSpots[maxdrawn+1].A := FreeSpots[x].A+90;
  222.  
  223.     iBeginBranch := maxdrawn+2;
  224.   end;
  225. end;
  226.  
  227. type
  228.   TDWordArray = array of DWord;
  229.   PDWordArray = ^TDWordArray;
  230.  
  231. function GetWaypointArrayElements(nTrees: integer): DWORD; cdecl;
  232. begin
  233.   result := Round(cPathLength*nTrees);
  234. end;
  235.  
  236. function GenerateMap(nTrees, treeRadius, mapX, mapY: integer;
  237.   memblockTrees, memblockWayPoints: PDWordArray): DWORD; cdecl;
  238. var
  239.   pathSize: integer;
  240.   aryTrees, aryWaypoints: TDWordArray;
  241.   i, j, k: integer;
  242.   p, q: TPoint;
  243.   ok: boolean;
  244.   nFreespots: integer;
  245. begin
  246.   if not iniLoaded then
  247.   begin
  248.     result := ERR_NO_INI_LOADED;
  249.     Exit;
  250.   end;
  251.  
  252.   if memblockTrees <> nil then
  253.     aryTrees := TDWordArray(memblockTrees)
  254.   else
  255.     SetLength(aryTrees, 0); // prevent compiler warning
  256.  
  257.   if memblockWayPoints <> nil then
  258.     aryWaypoints := TDWordArray(memblockWayPoints)
  259.   else
  260.     SetLength(aryWaypoints, 0); // prevent compiler warning
  261.  
  262.   pathSize := Round(cPathWidthFactor * treeRadius);
  263.   nFreespots := GetWaypointArrayElements(nTrees);
  264.  
  265.   k := 0;
  266.   repeat
  267.     Inc(k);
  268.     _BuildPaths(nFreespots, mapx, mapy, nTrees, treeradius, mapX div 2, mapY div 2, RandBetween(0,359));
  269.   until (k >= cPathBuildMaxAttempts) or _AcceptedPath(mapx, mapy);
  270.  
  271.   if memblockTrees <> nil then
  272.   begin
  273.     for i := 0 to nTrees-1 do
  274.     begin
  275.       k := 0;
  276.       repeat
  277.         inc(k);
  278.         ok := true;
  279.         p := Point(Random(mapX+1), Random(mapY+1));
  280.  
  281.         // Bäume nicht in den Weg bauen
  282.         for j := Low(FreeSpots) to High(FreeSpots) do
  283.         begin
  284.           q := Point(FreeSpots[j].X, FreeSpots[j].Y);
  285.           if IsInCircle(p, q, treeRadius + pathSize div 2) then
  286.           begin
  287.             ok := false;
  288.             break;
  289.           end;
  290.         end;
  291.  
  292.         // Bäume sollen sich nicht überschneiden
  293.         for j := Low(FreeSpots) to High(FreeSpots) do
  294.         begin
  295.           q := Point(FreeSpots[j].X, FreeSpots[j].Y);
  296.           if IsInCircle(p, q, treeRadius + pathSize div 2) then
  297.           begin
  298.             ok := false;
  299.             break;
  300.           end;
  301.         end;
  302.       until ok or (k > cTreePlacementMaxAttempts);
  303.  
  304.       aryTrees[i*2]   := p.x;
  305.       aryTrees[i*2+1] := p.y;
  306.     end;
  307.   end;
  308.  
  309.   if memblockWayPoints <> nil then
  310.   begin
  311.     for i := 0 to nFreespots-1 do
  312.     begin
  313.       aryWaypoints[i*2]   := FreeSpots[i].X;
  314.       aryWaypoints[i*2+1] := FreeSpots[i].Y;
  315.     end;
  316.   end;
  317.  
  318.   result := ERR_OK;
  319. end;
  320.  
  321. function RandomSeed: integer; cdecl;
  322. begin
  323.   Randomize;
  324.   result := RandSeed;
  325. end;
  326.  
  327. procedure UseSeed(seed: integer); cdecl;
  328. begin
  329.   RandSeed := seed;
  330. end;
  331.  
  332. exports
  333.   GenerateMap name 'GenerateMap',
  334.   LoadParametersFromINI name 'LoadParametersFromINI',
  335.   RandomSeed name 'RandomSeed',
  336.   UseSeed name 'UseSeed',
  337.   GetWaypointArrayElements name 'GetWaypointArrayElements';
  338.  
  339. end.
  340.