hedgewars/uLand.pas
changeset 6490 531bf083e8db
parent 6453 11c578d30bd3
child 6491 736479f3d348
equal deleted inserted replaced
6489:e1f0058cfedd 6490:531bf083e8db
    20 
    20 
    21 unit uLand;
    21 unit uLand;
    22 interface
    22 interface
    23 uses SDLh, uLandTemplates, uFloat, uConsts, GLunit, uTypes;
    23 uses SDLh, uLandTemplates, uFloat, uConsts, GLunit, uTypes;
    24 
    24 
    25 type direction = record x, y: LongInt; end;
       
    26 const DIR_N: direction = (x: 0; y: -1);
       
    27     DIR_E: direction = (x: 1; y: 0);
       
    28     DIR_S: direction = (x: 0; y: 1);
       
    29     DIR_W: direction = (x: -1; y: 0);
       
    30 
       
    31 procedure initModule;
    25 procedure initModule;
    32 procedure freeModule;
    26 procedure freeModule;
    33 procedure DrawBottomBorder;
    27 procedure DrawBottomBorder;
    34 procedure GenMap;
    28 procedure GenMap;
    35 function  GenPreview: TPreview;
    29 function  GenPreview: TPreview;
    36 
    30 
    37 implementation
    31 implementation
    38 uses uConsole, uStore, uRandom, uLandObjects, uIO, uLandTexture, sysutils,
    32 uses uConsole, uStore, uRandom, uLandObjects, uIO, uLandTexture, sysutils,
    39      uVariables, uUtils, uCommands, Adler32, uDebug, uLandPainted, uTextures;
    33      uVariables, uUtils, uCommands, Adler32, uDebug, uLandPainted, uTextures,
    40 
    34      uLandGraphics, uLandGenMaze, uLandOutline;
    41 operator = (const a, b: direction) c: Boolean;
    35 
    42 begin
       
    43     c := (a.x = b.x) and (a.y = b.y);
       
    44 end;
       
    45 
       
    46 type TPixAr = record
       
    47               Count: Longword;
       
    48               ar: array[0..Pred(cMaxEdgePoints)] of TPoint;
       
    49               end;
       
    50 
       
    51 procedure DrawLine(X1, Y1, X2, Y2: LongInt; Color: Longword);
       
    52 var
       
    53   eX, eY, dX, dY: LongInt;
       
    54   i, sX, sY, x, y, d: LongInt;
       
    55 begin
       
    56 eX:= 0;
       
    57 eY:= 0;
       
    58 dX:= X2 - X1;
       
    59 dY:= Y2 - Y1;
       
    60 
       
    61 if (dX > 0) then sX:= 1
       
    62 else
       
    63   if (dX < 0) then
       
    64      begin
       
    65      sX:= -1;
       
    66      dX:= -dX
       
    67      end else sX:= dX;
       
    68 
       
    69 if (dY > 0) then sY:= 1
       
    70   else
       
    71   if (dY < 0) then
       
    72      begin
       
    73      sY:= -1;
       
    74      dY:= -dY
       
    75      end else sY:= dY;
       
    76 
       
    77 if (dX > dY) then d:= dX
       
    78              else d:= dY;
       
    79 
       
    80 x:= X1;
       
    81 y:= Y1;
       
    82 
       
    83 for i:= 0 to d do
       
    84     begin
       
    85     inc(eX, dX);
       
    86     inc(eY, dY);
       
    87     if (eX > d) then
       
    88        begin
       
    89        dec(eX, d);
       
    90        inc(x, sX);
       
    91        end;
       
    92     if (eY > d) then
       
    93        begin
       
    94        dec(eY, d);
       
    95        inc(y, sY);
       
    96        end;
       
    97 
       
    98     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
       
    99        Land[y, x]:= Color;
       
   100     end
       
   101 end;
       
   102 
       
   103 procedure DrawEdge(var pa: TPixAr; Color: Longword);
       
   104 var i: LongInt;
       
   105 begin
       
   106 i:= 0;
       
   107 with pa do
       
   108 while i < LongInt(Count) - 1 do
       
   109     if (ar[i + 1].X = NTPX) then inc(i, 2)
       
   110        else begin
       
   111        DrawLine(ar[i].x, ar[i].y, ar[i + 1].x, ar[i + 1].y, Color);
       
   112        inc(i)
       
   113        end
       
   114 end;
       
   115 
       
   116 procedure Vector(p1, p2, p3: TPoint; var Vx, Vy: hwFloat);
       
   117 var d1, d2, d: hwFloat;
       
   118 begin
       
   119 Vx:= int2hwFloat(p1.X - p3.X);
       
   120 Vy:= int2hwFloat(p1.Y - p3.Y);
       
   121 d:= DistanceI(p2.X - p1.X, p2.Y - p1.Y);
       
   122 d1:= DistanceI(p2.X - p3.X, p2.Y - p3.Y);
       
   123 d2:= Distance(Vx, Vy);
       
   124 if d1 < d then d:= d1;
       
   125 if d2 < d then d:= d2;
       
   126 d:= d * _1div3;
       
   127 if d2.QWordValue = 0 then
       
   128    begin
       
   129    Vx:= _0;
       
   130    Vy:= _0
       
   131    end else
       
   132    begin
       
   133    d2:= _1 / d2;
       
   134    Vx:= Vx * d2;
       
   135    Vy:= Vy * d2;
       
   136 
       
   137    Vx:= Vx * d;
       
   138    Vy:= Vy * d
       
   139    end
       
   140 end;
       
   141 
       
   142 procedure AddLoopPoints(var pa, opa: TPixAr; StartI, EndI: LongInt; Delta: hwFloat);
       
   143 var i, pi, ni: LongInt;
       
   144     NVx, NVy, PVx, PVy: hwFloat;
       
   145     x1, x2, y1, y2: LongInt;
       
   146     tsq, tcb, t, r1, r2, r3, cx1, cx2, cy1, cy2: hwFloat;
       
   147     X, Y: LongInt;
       
   148 begin
       
   149 pi:= EndI;
       
   150 i:= StartI;
       
   151 ni:= Succ(StartI);
       
   152 {$HINTS OFF}
       
   153 Vector(opa.ar[pi], opa.ar[i], opa.ar[ni], NVx, NVy);
       
   154 {$HINTS ON}
       
   155 repeat
       
   156     inc(pi);
       
   157     if pi > EndI then pi:= StartI;
       
   158     inc(i);
       
   159     if i > EndI then i:= StartI;
       
   160     inc(ni);
       
   161     if ni > EndI then ni:= StartI;
       
   162     PVx:= NVx;
       
   163     PVy:= NVy;
       
   164     Vector(opa.ar[pi], opa.ar[i], opa.ar[ni], NVx, NVy);
       
   165 
       
   166     x1:= opa.ar[pi].x;
       
   167     y1:= opa.ar[pi].y;
       
   168     x2:= opa.ar[i].x;
       
   169     y2:= opa.ar[i].y;
       
   170     cx1:= int2hwFloat(x1) - PVx;
       
   171     cy1:= int2hwFloat(y1) - PVy;
       
   172     cx2:= int2hwFloat(x2) + NVx;
       
   173     cy2:= int2hwFloat(y2) + NVy;
       
   174     t:= _0;
       
   175     while t.Round = 0 do
       
   176           begin
       
   177           tsq:= t * t;
       
   178           tcb:= tsq * t;
       
   179           r1:= (_1 - t*3 + tsq*3 - tcb);
       
   180           r2:= (     t*3 - tsq*6 + tcb*3);
       
   181           r3:= (           tsq*3 - tcb*3);
       
   182           X:= hwRound(r1 * x1 + r2 * cx1 + r3 * cx2 + tcb * x2);
       
   183           Y:= hwRound(r1 * y1 + r2 * cy1 + r3 * cy2 + tcb * y2);
       
   184           t:= t + Delta;
       
   185           pa.ar[pa.Count].x:= X;
       
   186           pa.ar[pa.Count].y:= Y;
       
   187           inc(pa.Count);
       
   188           TryDo(pa.Count <= cMaxEdgePoints, 'Edge points overflow', true)
       
   189           end;
       
   190 until i = StartI;
       
   191 pa.ar[pa.Count].x:= opa.ar[StartI].X;
       
   192 pa.ar[pa.Count].y:= opa.ar[StartI].Y;
       
   193 inc(pa.Count)
       
   194 end;
       
   195 
       
   196 procedure BezierizeEdge(var pa: TPixAr; Delta: hwFloat);
       
   197 var i, StartLoop: LongInt;
       
   198     opa: TPixAr;
       
   199 begin
       
   200 opa:= pa;
       
   201 pa.Count:= 0;
       
   202 i:= 0;
       
   203 StartLoop:= 0;
       
   204 while i < LongInt(opa.Count) do
       
   205     if (opa.ar[i + 1].X = NTPX) then
       
   206        begin
       
   207        AddLoopPoints(pa, opa, StartLoop, i, Delta);
       
   208        inc(i, 2);
       
   209        StartLoop:= i;
       
   210        pa.ar[pa.Count].X:= NTPX;
       
   211        pa.ar[pa.Count].Y:= 0;
       
   212        inc(pa.Count);
       
   213        end else inc(i)
       
   214 end;
       
   215 
       
   216 procedure FillLand(x, y: LongInt);
       
   217 var Stack: record
       
   218            Count: Longword;
       
   219            points: array[0..8192] of record
       
   220                                      xl, xr, y, dir: LongInt;
       
   221                                      end
       
   222            end;
       
   223 
       
   224     procedure Push(_xl, _xr, _y, _dir: LongInt);
       
   225     begin
       
   226     TryDo(Stack.Count <= 8192, 'FillLand: stack overflow', true);
       
   227     _y:= _y + _dir;
       
   228     if (_y < 0) or (_y >= LAND_HEIGHT) then exit;
       
   229     with Stack.points[Stack.Count] do
       
   230          begin
       
   231          xl:= _xl;
       
   232          xr:= _xr;
       
   233          y:= _y;
       
   234          dir:= _dir
       
   235          end;
       
   236     inc(Stack.Count)
       
   237     end;
       
   238 
       
   239     procedure Pop(var _xl, _xr, _y, _dir: LongInt);
       
   240     begin
       
   241     dec(Stack.Count);
       
   242     with Stack.points[Stack.Count] do
       
   243          begin
       
   244          _xl:= xl;
       
   245          _xr:= xr;
       
   246          _y:= y;
       
   247          _dir:= dir
       
   248          end
       
   249     end;
       
   250 
       
   251 var xl, xr, dir: LongInt;
       
   252 begin
       
   253 Stack.Count:= 0;
       
   254 xl:= x - 1;
       
   255 xr:= x;
       
   256 Push(xl, xr, y, -1);
       
   257 Push(xl, xr, y,  1);
       
   258 dir:= 0;
       
   259 while Stack.Count > 0 do
       
   260       begin
       
   261       Pop(xl, xr, y, dir);
       
   262       while (xl > 0) and (Land[y, xl] <> 0) do dec(xl);
       
   263       while (xr < LAND_WIDTH - 1) and (Land[y, xr] <> 0) do inc(xr);
       
   264       while (xl < xr) do
       
   265             begin
       
   266             while (xl <= xr) and (Land[y, xl] = 0) do inc(xl);
       
   267             x:= xl;
       
   268             while (xl <= xr) and (Land[y, xl] <> 0) do
       
   269                   begin
       
   270                   Land[y, xl]:= 0;
       
   271                   inc(xl)
       
   272                   end;
       
   273             if x < xl then
       
   274                begin
       
   275                Push(x, Pred(xl), y, dir);
       
   276                Push(x, Pred(xl), y,-dir);
       
   277                end;
       
   278             end;
       
   279       end;
       
   280 end;
       
   281 
    36 
   282 procedure ColorizeLand(Surface: PSDL_Surface);
    37 procedure ColorizeLand(Surface: PSDL_Surface);
   283 var tmpsurf: PSDL_Surface;
    38 var tmpsurf: PSDL_Surface;
   284     r, rr: TSDL_Rect;
    39     r, rr: TSDL_Rect;
   285     x, yd, yu: LongInt;
    40     x, yd, yu: LongInt;
   415                FillPoints^[i].y:= LAND_HEIGHT - 1 - FillPoints^[i].y;
   170                FillPoints^[i].y:= LAND_HEIGHT - 1 - FillPoints^[i].y;
   416            end;
   171            end;
   417      end
   172      end
   418 end;
   173 end;
   419 
   174 
   420 function CheckIntersect(V1, V2, V3, V4: TPoint): boolean;
       
   421 var c1, c2, dm: LongInt;
       
   422 begin
       
   423 dm:= (V4.y - V3.y) * (V2.x - V1.x) - (V4.x - V3.x) * (V2.y - V1.y);
       
   424 c1:= (V4.x - V3.x) * (V1.y - V3.y) - (V4.y - V3.y) * (V1.x - V3.x);
       
   425 if dm = 0 then exit(false);
       
   426 
       
   427 c2:= (V2.x - V3.x) * (V1.y - V3.y) - (V2.y - V3.y) * (V1.x - V3.x);
       
   428 if dm > 0 then
       
   429    begin
       
   430    if (c1 < 0) or (c1 > dm) then exit(false);
       
   431    if (c2 < 0) or (c2 > dm) then exit(false)
       
   432    end else
       
   433    begin
       
   434    if (c1 > 0) or (c1 < dm) then exit(false);
       
   435    if (c2 > 0) or (c2 < dm) then exit(false)
       
   436    end;
       
   437 
       
   438 //AddFileLog('1  (' + inttostr(V1.x) + ',' + inttostr(V1.y) + ')x(' + inttostr(V2.x) + ',' + inttostr(V2.y) + ')');
       
   439 //AddFileLog('2  (' + inttostr(V3.x) + ',' + inttostr(V3.y) + ')x(' + inttostr(V4.x) + ',' + inttostr(V4.y) + ')');
       
   440 CheckIntersect:= true
       
   441 end;
       
   442 
       
   443 function CheckSelfIntersect(var pa: TPixAr; ind: Longword): boolean;
       
   444 var i: Longword;
       
   445 begin
       
   446 if (ind <= 0) or (ind >= Pred(pa.Count)) then exit(false);
       
   447 for i:= 1 to pa.Count - 3 do
       
   448     if (i <= ind - 1) or (i >= ind + 2) then
       
   449       begin
       
   450       if (i <> ind - 1) and
       
   451          CheckIntersect(pa.ar[ind], pa.ar[ind - 1], pa.ar[i], pa.ar[i - 1]) then exit(true);
       
   452       if (i <> ind + 2) and
       
   453          CheckIntersect(pa.ar[ind], pa.ar[ind + 1], pa.ar[i], pa.ar[i - 1]) then exit(true);
       
   454       end;
       
   455 CheckSelfIntersect:= false
       
   456 end;
       
   457 
       
   458 procedure RandomizePoints(var pa: TPixAr);
       
   459 const cEdge = 55;
       
   460       cMinDist = 8;
       
   461 var radz: array[0..Pred(cMaxEdgePoints)] of LongInt;
       
   462     i, k, dist, px, py: LongInt;
       
   463 begin
       
   464 for i:= 0 to Pred(pa.Count) do
       
   465   begin
       
   466   radz[i]:= 0;
       
   467   with pa.ar[i] do
       
   468     if x <> NTPX then
       
   469       begin
       
   470       radz[i]:= Min(Max(x - cEdge, 0), Max(LAND_WIDTH - cEdge - x, 0));
       
   471       radz[i]:= Min(radz[i], Min(Max(y - cEdge, 0), Max(LAND_HEIGHT - cEdge - y, 0)));
       
   472       if radz[i] > 0 then
       
   473         for k:= 0 to Pred(i) do
       
   474           begin
       
   475           dist:= Max(abs(x - pa.ar[k].x), abs(y - pa.ar[k].y));
       
   476           radz[k]:= Max(0, Min((dist - cMinDist) div 2, radz[k]));
       
   477           radz[i]:= Max(0, Min(dist - radz[k] - cMinDist, radz[i]))
       
   478         end
       
   479       end;
       
   480   end;
       
   481 
       
   482 for i:= 0 to Pred(pa.Count) do
       
   483   with pa.ar[i] do
       
   484     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
       
   485       begin
       
   486       px:= x;
       
   487       py:= y;
       
   488       x:= x + LongInt(GetRandom(7) - 3) * (radz[i] * 5 div 7) div 3;
       
   489       y:= y + LongInt(GetRandom(7) - 3) * (radz[i] * 5 div 7) div 3;
       
   490       if CheckSelfIntersect(pa, i) then
       
   491          begin
       
   492          x:= px;
       
   493          y:= py
       
   494          end;
       
   495       end
       
   496 end;
       
   497 
       
   498 
   175 
   499 procedure GenBlank(var Template: TEdgeTemplate);
   176 procedure GenBlank(var Template: TEdgeTemplate);
   500 var pa: TPixAr;
   177 var pa: TPixAr;
   501     i: Longword;
   178     i: Longword;
   502     y, x: Longword;
   179     y, x: Longword;
   608 
   285 
   609 if SDL_MustLock(Surface) then
   286 if SDL_MustLock(Surface) then
   610     SDL_UnlockSurface(Surface);
   287     SDL_UnlockSurface(Surface);
   611 end;
   288 end;
   612 
   289 
   613 procedure GenMaze;
       
   614 const small_cell_size = 128;
       
   615     medium_cell_size = 192;
       
   616     large_cell_size = 256;
       
   617     braidness = 10;
       
   618 
       
   619 var x, y: LongInt;
       
   620     cellsize: LongInt; //selected by the user in the gui
       
   621     seen_cells_x, seen_cells_y: LongInt; //number of cells that can be visited by the generator, that is every second cell in x and y direction. the cells between there are walls that will be removed when we move from one cell to another
       
   622     num_edges_x, num_edges_y: LongInt; //number of resulting edges that need to be vertexificated
       
   623     num_cells_x, num_cells_y: LongInt; //actual number of cells, depending on cell size
       
   624     seen_list: array of array of LongInt;
       
   625     xwalls: array of array of Boolean;
       
   626     ywalls: array of array of Boolean;
       
   627     x_edge_list: array of array of Boolean;
       
   628     y_edge_list: array of array of Boolean;
       
   629     maze: array of array of Boolean;
       
   630     pa: TPixAr;
       
   631     num_vertices: LongInt;
       
   632     off_y: LongInt;
       
   633     num_steps: LongInt;
       
   634     current_step: LongInt;
       
   635     step_done: array of Boolean;
       
   636     done: Boolean;
       
   637     last_cell: array of record x, y: LongInt; end;
       
   638     came_from: array of array of record x, y: LongInt; end;
       
   639     came_from_pos: array of LongInt;
       
   640     maze_inverted: Boolean;
       
   641 
       
   642 function when_seen(x: LongInt; y: LongInt): LongInt;
       
   643 begin
       
   644 if (x < 0) or (x >= seen_cells_x) or (y < 0) or (y >= seen_cells_y) then
       
   645     when_seen := current_step
       
   646 else
       
   647     when_seen := seen_list[x, y];
       
   648 end;
       
   649 
       
   650 function is_x_edge(x, y: LongInt): Boolean;
       
   651 begin
       
   652 if (x < 0) or (x > num_edges_x) or (y < 0) or (y > num_cells_y) then
       
   653     is_x_edge := false
       
   654 else
       
   655     is_x_edge := x_edge_list[x, y];
       
   656 end;
       
   657 
       
   658 function is_y_edge(x, y: LongInt): Boolean;
       
   659 begin
       
   660 if (x < 0) or (x > num_cells_x) or (y < 0) or (y > num_edges_y) then
       
   661     is_y_edge := false
       
   662 else
       
   663     is_y_edge := y_edge_list[x, y];
       
   664 end;
       
   665 
       
   666 procedure see_cell;
       
   667 var dir: direction;
       
   668     tries: LongInt;
       
   669     x, y: LongInt;
       
   670     found_cell: Boolean;
       
   671     next_dir_clockwise: Boolean;
       
   672 
       
   673 begin
       
   674 x := last_cell[current_step].x;
       
   675 y := last_cell[current_step].y;
       
   676 seen_list[x, y] := current_step;
       
   677 case GetRandom(4) of
       
   678     0: dir := DIR_N;
       
   679     1: dir := DIR_E;
       
   680     2: dir := DIR_S;
       
   681     3: dir := DIR_W;
       
   682 end;
       
   683 tries := 0;
       
   684 found_cell := false;
       
   685 if getrandom(2) = 1 then next_dir_clockwise := true
       
   686 else next_dir_clockwise := false;
       
   687 
       
   688 while (tries < 5) and (not found_cell) do
       
   689 begin
       
   690     if when_seen(x + dir.x, y + dir.y) = current_step then //we are seeing ourselves, try another direction
       
   691     begin
       
   692         //we have already seen the target cell, decide if we should remove the wall anyway
       
   693         //(or put a wall there if maze_inverted, but we are not doing that right now)
       
   694         if not maze_inverted and (GetRandom(braidness) = 0) then
       
   695         //or just warn that inverted+braid+indestructible terrain != good idea
       
   696         begin
       
   697             case dir.x of
       
   698                 -1: if x > 0 then ywalls[x-1, y] := false;
       
   699                 1: if x < seen_cells_x - 1 then ywalls[x, y] := false;
       
   700             end;
       
   701             case dir.y of
       
   702                 -1: if y > 0 then xwalls[x, y-1] := false;
       
   703                 1: if y < seen_cells_y - 1 then xwalls[x, y] := false;
       
   704             end;
       
   705         end;
       
   706         if next_dir_clockwise then
       
   707         begin
       
   708             if dir = DIR_N then
       
   709                 dir := DIR_E
       
   710             else if dir = DIR_E then
       
   711                 dir := DIR_S
       
   712             else if dir = DIR_S then
       
   713                 dir := DIR_W
       
   714             else
       
   715                 dir := DIR_N;
       
   716         end
       
   717         else
       
   718         begin
       
   719             if dir = DIR_N then
       
   720                 dir := DIR_W
       
   721             else if dir = DIR_E then
       
   722                 dir := DIR_N
       
   723             else if dir = DIR_S then
       
   724                 dir := DIR_E
       
   725             else
       
   726                 dir := DIR_S;
       
   727         end
       
   728     end
       
   729     else if when_seen(x + dir.x, y + dir.y) = -1 then //cell was not seen yet, go there
       
   730     begin
       
   731         case dir.y of
       
   732             -1: xwalls[x, y-1] := false;
       
   733             1: xwalls[x, y] := false;
       
   734         end;
       
   735         case dir.x of
       
   736             -1: ywalls[x-1, y] := false;
       
   737             1: ywalls[x, y] := false;
       
   738         end;
       
   739         last_cell[current_step].x := x+dir.x;
       
   740         last_cell[current_step].y := y+dir.y;
       
   741         came_from_pos[current_step] := came_from_pos[current_step] + 1;
       
   742         came_from[current_step, came_from_pos[current_step]].x := x;
       
   743         came_from[current_step, came_from_pos[current_step]].y := y;
       
   744         found_cell := true;
       
   745     end
       
   746     else //we are seeing someone else, quit
       
   747     begin
       
   748         step_done[current_step] := true;
       
   749         found_cell := true;
       
   750     end;
       
   751 
       
   752     tries := tries + 1;
       
   753 end;
       
   754 if not found_cell then
       
   755 begin
       
   756     last_cell[current_step].x := came_from[current_step, came_from_pos[current_step]].x;
       
   757     last_cell[current_step].y := came_from[current_step, came_from_pos[current_step]].y;
       
   758     came_from_pos[current_step] := came_from_pos[current_step] - 1;
       
   759     if came_from_pos[current_step] >= 0 then see_cell
       
   760     else step_done[current_step] := true;
       
   761 end;
       
   762 end;
       
   763 
       
   764 procedure add_vertex(x, y: LongInt);
       
   765 var tmp_x, tmp_y: LongInt;
       
   766 begin
       
   767 if x = NTPX then
       
   768 begin
       
   769     if pa.ar[num_vertices - 6].x = NTPX then
       
   770     begin
       
   771         num_vertices := num_vertices - 6;
       
   772     end
       
   773     else
       
   774     begin
       
   775         pa.ar[num_vertices].x := NTPX;
       
   776         pa.ar[num_vertices].y := 0;
       
   777     end
       
   778 end
       
   779 else
       
   780 begin
       
   781     if maze_inverted or (x mod 2 = 0) then tmp_x := cellsize
       
   782     else tmp_x := cellsize * 2 div 3;
       
   783     if maze_inverted or (y mod 2 = 0) then tmp_y := cellsize
       
   784     else tmp_y := cellsize * 2 div 3;
       
   785 
       
   786     pa.ar[num_vertices].x := (x-1)*cellsize + tmp_x;
       
   787     pa.ar[num_vertices].y := (y-1)*cellsize + tmp_y + off_y;
       
   788 end;
       
   789 num_vertices := num_vertices + 1;
       
   790 end;
       
   791 
       
   792 procedure add_edge(x, y: LongInt; dir: direction);
       
   793 var i: LongInt;
       
   794 begin
       
   795 if dir = DIR_N then
       
   796 begin
       
   797     dir := DIR_W
       
   798 end
       
   799 else if dir = DIR_E then
       
   800 begin
       
   801     dir := DIR_N
       
   802 end
       
   803 else if dir = DIR_S then
       
   804 begin
       
   805     dir := DIR_E
       
   806 end
       
   807 else
       
   808 begin
       
   809     dir := DIR_S;
       
   810 end;
       
   811 
       
   812 for i := 0 to 3 do
       
   813 begin
       
   814         if dir = DIR_N then
       
   815             dir := DIR_E
       
   816         else if dir = DIR_E then
       
   817             dir := DIR_S
       
   818         else if dir = DIR_S then
       
   819             dir := DIR_W
       
   820         else
       
   821             dir := DIR_N;
       
   822 
       
   823     if (dir = DIR_N) and is_x_edge(x, y) then
       
   824         begin
       
   825             x_edge_list[x, y] := false;
       
   826             add_vertex(x+1, y);
       
   827             add_edge(x, y-1, DIR_N);
       
   828             break;
       
   829         end;
       
   830 
       
   831     if (dir = DIR_E) and is_y_edge(x+1, y) then
       
   832         begin
       
   833             y_edge_list[x+1, y] := false;
       
   834             add_vertex(x+2, y+1);
       
   835             add_edge(x+1, y, DIR_E);
       
   836             break;
       
   837         end;
       
   838 
       
   839     if (dir = DIR_S) and is_x_edge(x, y+1) then
       
   840         begin
       
   841             x_edge_list[x, y+1] := false;
       
   842             add_vertex(x+1, y+2);
       
   843             add_edge(x, y+1, DIR_S);
       
   844             break;
       
   845         end;
       
   846 
       
   847     if (dir = DIR_W) and is_y_edge(x, y) then
       
   848         begin
       
   849             y_edge_list[x, y] := false;
       
   850             add_vertex(x, y+1);
       
   851             add_edge(x-1, y, DIR_W);
       
   852             break;
       
   853         end;
       
   854 end;
       
   855 
       
   856 end;
       
   857 
       
   858 begin
       
   859 case cTemplateFilter of
       
   860     0: begin
       
   861         cellsize := small_cell_size;
       
   862         maze_inverted := false;
       
   863     end;
       
   864     1: begin
       
   865         cellsize := medium_cell_size;
       
   866         maze_inverted := false;
       
   867     end;
       
   868     2: begin
       
   869         cellsize := large_cell_size;
       
   870         maze_inverted := false;
       
   871     end;
       
   872     3: begin
       
   873         cellsize := small_cell_size;
       
   874         maze_inverted := true;
       
   875     end;
       
   876     4: begin
       
   877         cellsize := medium_cell_size;
       
   878         maze_inverted := true;
       
   879     end;
       
   880     5: begin
       
   881         cellsize := large_cell_size;
       
   882         maze_inverted := true;
       
   883     end;
       
   884 end;
       
   885 
       
   886 num_cells_x := LAND_WIDTH div cellsize;
       
   887 if not odd(num_cells_x) then num_cells_x := num_cells_x - 1; //needs to be odd
       
   888 num_cells_y := LAND_HEIGHT div cellsize;
       
   889 if not odd(num_cells_y) then num_cells_y := num_cells_y - 1;
       
   890 num_edges_x := num_cells_x - 1;
       
   891 num_edges_y := num_cells_y - 1;
       
   892 seen_cells_x := num_cells_x div 2;
       
   893 seen_cells_y := num_cells_y div 2;
       
   894 
       
   895 if maze_inverted then
       
   896     num_steps := 3 //TODO randomize, between 3 and 5?
       
   897 else
       
   898     num_steps := 1;
       
   899 SetLength(step_done, num_steps);
       
   900 SetLength(last_cell, num_steps);
       
   901 SetLength(came_from_pos, num_steps);
       
   902 SetLength(came_from, num_steps, num_cells_x*num_cells_y);
       
   903 done := false;
       
   904 for current_step := 0 to num_steps - 1 do
       
   905     step_done[current_step] := false;
       
   906     came_from_pos[current_step] := 0;
       
   907 current_step := 0;
       
   908 
       
   909 SetLength(seen_list, seen_cells_x, seen_cells_y);
       
   910 SetLength(xwalls, seen_cells_x, seen_cells_y - 1);
       
   911 SetLength(ywalls, seen_cells_x - 1, seen_cells_y);
       
   912 SetLength(x_edge_list, num_edges_x, num_cells_y);
       
   913 SetLength(y_edge_list, num_cells_x, num_edges_y);
       
   914 SetLength(maze, num_cells_x, num_cells_y);
       
   915 
       
   916 num_vertices := 0;
       
   917 
       
   918 playHeight := num_cells_y * cellsize;
       
   919 playWidth := num_cells_x * cellsize;
       
   920 off_y := LAND_HEIGHT - playHeight;
       
   921 
       
   922 for x := 0 to playWidth do
       
   923     for y := 0 to off_y - 1 do
       
   924         Land[y, x] := 0;
       
   925 
       
   926 for x := 0 to playWidth do
       
   927     for y := off_y to LAND_HEIGHT - 1 do
       
   928         Land[y, x] := lfBasic;
       
   929 
       
   930 for y := 0 to num_cells_y - 1 do
       
   931     for x := 0 to num_cells_x - 1 do
       
   932         maze[x, y] := false;
       
   933 
       
   934 for x := 0 to seen_cells_x - 1 do
       
   935     for y := 0 to seen_cells_y - 2 do
       
   936         xwalls[x, y] := true;
       
   937 
       
   938 for x := 0 to seen_cells_x - 2 do
       
   939     for y := 0 to seen_cells_y - 1 do
       
   940         ywalls[x, y] := true;
       
   941 
       
   942 for x := 0 to seen_cells_x - 1 do
       
   943     for y := 0 to seen_cells_y - 1 do
       
   944         seen_list[x, y] := -1;
       
   945 
       
   946 for x := 0 to num_edges_x - 1 do
       
   947     for y := 0 to num_cells_y - 1 do
       
   948         x_edge_list[x, y] := false;
       
   949 
       
   950 for x := 0 to num_cells_x - 1 do
       
   951     for y := 0 to num_edges_y - 1 do
       
   952         y_edge_list[x, y] := false;
       
   953 
       
   954 for current_step := 0 to num_steps-1 do
       
   955 begin
       
   956     x := GetRandom(seen_cells_x - 1) div LongWord(num_steps);
       
   957     last_cell[current_step].x := x + current_step * seen_cells_x div num_steps;
       
   958     last_cell[current_step].y := GetRandom(seen_cells_y);
       
   959 end;
       
   960 
       
   961 while not done do
       
   962 begin
       
   963     done := true;
       
   964     for current_step := 0 to num_steps-1 do
       
   965     begin
       
   966         if not step_done[current_step] then
       
   967         begin
       
   968             see_cell;
       
   969             done := false;
       
   970         end;
       
   971     end;
       
   972 end;
       
   973 
       
   974 for x := 0 to seen_cells_x - 1 do
       
   975     for y := 0 to seen_cells_y - 1 do
       
   976         if seen_list[x, y] > -1 then
       
   977             maze[(x+1)*2-1, (y+1)*2-1] := true;
       
   978 
       
   979 for x := 0 to seen_cells_x - 1 do
       
   980     for y := 0 to seen_cells_y - 2 do
       
   981         if not xwalls[x, y] then
       
   982             maze[x*2 + 1, y*2 + 2] := true;
       
   983 
       
   984 
       
   985 for x := 0 to seen_cells_x - 2 do
       
   986      for y := 0 to seen_cells_y - 1 do
       
   987         if not ywalls[x, y] then
       
   988             maze[x*2 + 2, y*2 + 1] := true;
       
   989 
       
   990 for x := 0 to num_edges_x - 1 do
       
   991     for y := 0 to num_cells_y - 1 do
       
   992         if maze[x, y] xor maze[x+1, y] then
       
   993             x_edge_list[x, y] := true
       
   994         else
       
   995             x_edge_list[x, y] := false;
       
   996 
       
   997 for x := 0 to num_cells_x - 1 do
       
   998     for y := 0 to num_edges_y - 1 do
       
   999         if maze[x, y] xor maze[x, y+1] then
       
  1000             y_edge_list[x, y] := true
       
  1001         else
       
  1002             y_edge_list[x, y] := false;
       
  1003 
       
  1004 for x := 0 to num_edges_x - 1 do
       
  1005     for y := 0 to num_cells_y - 1 do
       
  1006         if x_edge_list[x, y] then
       
  1007         begin
       
  1008             x_edge_list[x, y] := false;
       
  1009             add_vertex(x+1, y+1);
       
  1010             add_vertex(x+1, y);
       
  1011             add_edge(x, y-1, DIR_N);
       
  1012             add_vertex(NTPX, 0);
       
  1013         end;
       
  1014 
       
  1015 pa.count := num_vertices;
       
  1016 
       
  1017 RandomizePoints(pa);
       
  1018 BezierizeEdge(pa, _0_25);
       
  1019 RandomizePoints(pa);
       
  1020 BezierizeEdge(pa, _0_25);
       
  1021 
       
  1022 DrawEdge(pa, 0);
       
  1023 
       
  1024 if maze_inverted then
       
  1025     FillLand(1, 1+off_y)
       
  1026 else
       
  1027 begin
       
  1028     x := 0;
       
  1029     while Land[cellsize div 2 + cellsize + off_y, x] = lfBasic do
       
  1030         x := x + 1;
       
  1031     while Land[cellsize div 2 + cellsize + off_y, x] = 0 do
       
  1032         x := x + 1;
       
  1033     FillLand(x+1, cellsize div 2 + cellsize + off_y);
       
  1034 end;
       
  1035 
       
  1036 MaxHedgehogs:= 32;
       
  1037 if (GameFlags and gfDisableGirders) <> 0 then hasGirders:= false
       
  1038 else hasGirders := true;
       
  1039 leftX:= 0;
       
  1040 rightX:= playWidth;
       
  1041 topY:= off_y;
       
  1042 hasBorder := false;
       
  1043 end;
       
  1044 
   290 
  1045 procedure GenLandSurface;
   291 procedure GenLandSurface;
  1046 var tmpsurf: PSDL_Surface;
   292 var tmpsurf: PSDL_Surface;
  1047     x,y: Longword;
   293     x,y: Longword;
  1048 begin
   294 begin