Merge default transitional_engine
authorunC0Rr
Tue, 05 Sep 2023 17:02:08 +0200
branchtransitional_engine
changeset 16037 72c71c385579
parent 16036 96d0e6149d3d (diff)
parent 16007 20adaa127663 (current diff)
child 16038 7544a7d7c819
Merge default
--- a/.gitignore	Thu Aug 24 20:15:40 2023 +0200
+++ b/.gitignore	Tue Sep 05 17:02:08 2023 +0200
@@ -32,7 +32,7 @@
 misc/libphysfs/Xcode/build/
 misc/libphyslayer/Xcode/build/
 moc_*.cxx_parameters
-relre:^release\/
+relre:^release/
 *.log
 *.cmd
 *.diff
--- a/CMakeLists.txt	Thu Aug 24 20:15:40 2023 +0200
+++ b/CMakeLists.txt	Tue Sep 05 17:02:08 2023 +0200
@@ -139,10 +139,10 @@
     add_flag_append(CMAKE_CXX_FLAGS_DEBUG "/Od")    
 else()
     add_flag_append(CMAKE_C_FLAGS "-Wall -pipe")
-    add_flag_append(CMAKE_C_FLAGS_RELEASE "-O2")
+    add_flag_append(CMAKE_C_FLAGS_RELEASE "-O3")
     add_flag_append(CMAKE_C_FLAGS_DEBUG "-Wextra -O0")
     add_flag_append(CMAKE_CXX_FLAGS "-Wall -pipe")
-    add_flag_append(CMAKE_CXX_FLAGS_RELEASE "-O2")
+    add_flag_append(CMAKE_CXX_FLAGS_RELEASE "-O3")
     add_flag_append(CMAKE_CXX_FLAGS_DEBUG "-Wextra -O0")    
 endif()       
 
--- a/hedgewars/SDLh.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/SDLh.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -931,6 +931,9 @@
     TLongWordArray = array[0..16383] of LongWord;
     PLongWordArray = ^TLongWordArray;
 
+    TWordArray = array[0..16383] of Word;
+    PWordArray = ^TWordArray;
+
     PSDL_Thread = Pointer;
     PSDL_mutex = Pointer;
     PSDL_sem = Pointer;
@@ -1033,14 +1036,14 @@
 {$IFDEF WINDOWS}
      TThreadFunction = function (p: pointer): Longword; stdcall;
      pfnSDL_CurrentBeginThread = function (
-        _Security: pointer; 
+        _Security: pointer;
         _StackSize: LongWord;
         _StartAddress: TThreadFunction;
         _ArgList: pointer;
         _InitFlag: Longword;
         _ThrdAddr: PLongword): PtrUInt; cdecl;
     pfnSDL_CurrentEndThread = procedure (_Retval: LongInt); cdecl;
-{$ENDIF} 
+{$ENDIF}
 
 /////////////////////////////////////////////////////////////////
 /////////////////////  FUNCTION DEFINITIONS /////////////////////
@@ -1142,7 +1145,7 @@
 procedure SDL_SetEventFilter(filter: TSDL_EventFilter); cdecl; external SDLLibName;
 
 function  SDL_ShowCursor(toggle: LongInt): LongInt; cdecl; external SDLLibName;
-procedure SDL_WarpMouse(x, y: Word); inline;
+procedure SDL_WarpMouse(x, y: Word);
 
 function  SDL_GetKeyboardState(numkeys: PLongInt): PByteArray; cdecl; external SDLLibName;
 
@@ -1297,7 +1300,7 @@
 // for sdl2 we provide a SDL_WarpMouse() which calls the right SDL_WarpMouseInWindow() function
 // this has the advantage of reducing 'uses' and 'ifdef' statements
 // (SDLwindow is a private member of uStore module)
-procedure SDL_WarpMouse(x, y: Word); inline;
+procedure SDL_WarpMouse(x, y: Word);
 begin
     WarpMouse(x, y);
 end;
@@ -1340,7 +1343,7 @@
 function  SDL_CreateThread(fn: Pointer; name: PChar; data: Pointer): PSDL_Thread; cdecl;
 begin
     SDL_CreateThread:= SDL_CreateThread(fn, name, data, nil, nil)
-end;  
+end;
 {$ENDIF}
 
 end.
--- a/hedgewars/options.inc	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/options.inc	Tue Sep 05 17:02:08 2023 +0200
@@ -65,6 +65,8 @@
 {$DEFINE _S:=}
 {$DEFINE _P:=}
 
+{$optimization autoInline}
+
 //{$DEFINE TRACEAIACTIONS}
 //{$DEFINE COUNTTICKS}
 
--- a/hedgewars/uAIAmmoTests.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uAIAmmoTests.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -25,7 +25,7 @@
     amtest_Rare            = $00000001; // check only several positions
     amtest_NoTarget        = $00000002; // each pos, but no targetting
     amtest_MultipleAttacks = $00000004; // test could result in multiple attacks, set AttacksNum
-    amtest_NoTrackFall     = $00000008; // skip fall tracing.  
+    amtest_NoTrackFall     = $00000008; // skip fall tracing.
     amtest_LaserSight      = $00000010; // supports laser sighting
     amtest_NoVampiric      = $00000020; // don't use vampirism with this ammo
     amtest_NoInvulnerable  = $00000040; // don't use invulnerable with this with ammo
@@ -151,9 +151,9 @@
             );
 
 implementation
-uses uVariables, uUtils, uGearsHandlers;
+uses uVariables, uUtils, uGearsHandlers, uLandUtils;
 
-function Metric(x1, y1, x2, y2: LongInt): LongInt; inline;
+function Metric(x1, y1, x2, y2: LongInt): LongInt;
 begin
 Metric:= abs(x1 - x2) + abs(y1 - y2)
 end;
@@ -1081,7 +1081,7 @@
 
 if ((ix and LAND_WIDTH_MASK) = 0) and ((iy and LAND_HEIGHT_MASK) = 0) then
     repeat
-        if Land[iy, ix] <> 0 then
+        if LandGet(iy, ix) <> 0 then
             inc(d);
         x:= x + vX;
         y:= y + vY;
@@ -1137,7 +1137,7 @@
     x:= x + vX;
     y:= y + vY;
     if ((trunc(x) and LAND_WIDTH_MASK) = 0)and((trunc(y) and LAND_HEIGHT_MASK) = 0)
-    and (Land[trunc(y), trunc(x)] <> 0) then
+    and (LandGet(trunc(y), trunc(x)) <> 0) then
         inc(d);
 until (Abs(Targ.Point.X - trunc(x)) + Abs(Targ.Point.Y - trunc(y)) < 4)
     or (x < 0)
--- a/hedgewars/uAIMisc.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uAIMisc.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -71,20 +71,20 @@
 procedure freeModule;
 
 procedure FillTargets;
-procedure ResetTargets; inline;
-procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
+procedure ResetTargets;
+procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt);
 procedure FillBonuses(isAfterAttack: boolean);
-procedure AwareOfExplosion(x, y, r: LongInt); inline;
+procedure AwareOfExplosion(x, y, r: LongInt);
 
 function  RatePlace(Gear: PGear): LongInt;
-function  CheckWrap(x: real): real; inline;
-function  TestColl(x, y, r: LongInt): boolean; inline;
-function  TestCollHogsOrObjects(x, y, r: LongInt): boolean; inline;
-function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
-function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
+function  CheckWrap(x: real): real;
+function  TestColl(x, y, r: LongInt): boolean;
+function  TestCollHogsOrObjects(x, y, r: LongInt): boolean;
+function  TestCollExcludingObjects(x, y, r: LongInt): boolean;
+function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean;
 
-function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
-function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
+function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt;
+function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
 function  RealRateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
 function  RateShove(Me: PGear; x, y, r, power, kick: LongInt; gdX, gdY: real; Flags: LongWord): LongInt;
 function  RateShotgun(Me: PGear; gdX, gdY: real; x, y: LongInt): LongInt;
@@ -93,8 +93,8 @@
 function  RateHammer(Me: PGear): LongInt;
 
 function  HHGo(Gear, AltGear: PGear; var GoInfo: TGoInfo): boolean;
-function  AIrndSign(num: LongInt): LongInt; inline;
-function  AIrndOffset(targ: TTarget; Level: LongWord): LongInt; inline;
+function  AIrndSign(num: LongInt): LongInt;
+function  AIrndOffset(targ: TTarget; Level: LongWord): LongInt;
 
 var ThinkingHH: PGear;
     Targets: TTargets;
@@ -109,14 +109,14 @@
 var dmgMod: real = 1.0;
 
 implementation
-uses uCollisions, uVariables, uUtils, uGearsUtils, uAIAmmoTests;
+uses uCollisions, uVariables, uUtils, uGearsUtils, uAIAmmoTests, uLandUtils;
 
 var
     KnownExplosion: record
         X, Y, Radius: LongInt
         end = (X: 0; Y: 0; Radius: 0);
 
-procedure ResetTargets; inline;
+procedure ResetTargets;
 var i: LongWord;
 begin
 if Targets.reset then
@@ -200,7 +200,7 @@
 else friendlyfactor:= max(30, 300 - f * 80 div max(1,e))
 end;
 
-procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
+procedure AddBonus(x, y: LongInt; r: Longword; s: LongInt);
 begin
 if(bonuses.Count < MAXBONUS) then
     begin
@@ -212,7 +212,7 @@
     end;
 end;
 
-procedure AddWalkBonus(x, y: LongInt; r: Longword; s: LongInt); inline;
+procedure AddWalkBonus(x, y: LongInt; r: Longword; s: LongInt);
 begin
 if(walkbonuses.Count < MAXBONUS div 8) then
     begin
@@ -334,7 +334,7 @@
     end;
 end;
 
-procedure AwareOfExplosion(x, y, r: LongInt); inline;
+procedure AwareOfExplosion(x, y, r: LongInt);
 begin
     KnownExplosion.X:= x;
     KnownExplosion.Y:= y;
@@ -363,17 +363,17 @@
     RatePlace:= rate;
 end;
 
-function CheckWrap(x: real): real; inline;
+function CheckWrap(x: real): real;
 begin
     if WorldEdge = weWrap then
         if (x < leftX) then
              x:= x + (rightX - leftX)
-        else if x > rightX then    
+        else if x > rightX then
              x:= x - (rightX - leftX);
     CheckWrap:= x;
 end;
 
-function CheckBounds(x, y, r: Longint): boolean; inline;
+function CheckBounds(x, y, r: Longint): boolean;
 begin
     CheckBounds := (((x-r) and LAND_WIDTH_MASK) = 0) and
         (((x+r) and LAND_WIDTH_MASK) = 0) and
@@ -383,60 +383,60 @@
 
 
 // Check for collision with anything
-function TestCollWithEverything(x, y, r: LongInt): boolean; inline;
+function TestCollWithEverything(x, y, r: LongInt): boolean;
 begin
     if not CheckBounds(x, y, r) then
         exit(false);
 
-    if (Land[y-r, x-r] <> 0) or
-       (Land[y+r, x-r] <> 0) or
-       (Land[y-r, x+r] <> 0) or
-       (Land[y+r, x+r] <> 0) then
+    if (LandGet(y-r, x-r) <> 0) or
+       (LandGet(y+r, x-r) <> 0) or
+       (LandGet(y-r, x+r) <> 0) or
+       (LandGet(y+r, x+r) <> 0) then
        exit(true);
 
     TestCollWithEverything := false;
 end;
 
 // Check for collision with non-objects
-function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
+function TestCollExcludingObjects(x, y, r: LongInt): boolean;
 begin
     if not CheckBounds(x, y, r) then
         exit(false);
 
-    if (Land[y-r, x-r] > lfAllObjMask) or
-       (Land[y+r, x-r] > lfAllObjMask) or
-       (Land[y-r, x+r] > lfAllObjMask) or
-       (Land[y+r, x+r] > lfAllObjMask) then
+    if (LandGet(y-r, x-r) > lfAllObjMask) or
+       (LandGet(y+r, x-r) > lfAllObjMask) or
+       (LandGet(y-r, x+r) > lfAllObjMask) or
+       (LandGet(y+r, x+r) > lfAllObjMask) then
        exit(true);
 
     TestCollExcludingObjects:= false;
 end;
 
 // Check for collision with something other than current hedgehog or crate
-function TestColl(x, y, r: LongInt): boolean; inline;
+function TestColl(x, y, r: LongInt): boolean;
 begin
     if not CheckBounds(x, y, r) then
         exit(false);
 
-    if (Land[y-r, x-r] and lfNotCurHogCrate <> 0) or
-       (Land[y+r, x-r] and lfNotCurHogCrate <> 0) or
-       (Land[y-r, x+r] and lfNotCurHogCrate <> 0) or
-       (Land[y+r, x+r] and lfNotCurHogCrate <> 0) then
+    if (LandGet(y-r, x-r) and lfNotCurHogCrate <> 0) or
+       (LandGet(y+r, x-r) and lfNotCurHogCrate <> 0) or
+       (LandGet(y-r, x+r) and lfNotCurHogCrate <> 0) or
+       (LandGet(y+r, x+r) and lfNotCurHogCrate <> 0) then
        exit(true);
 
     TestColl:= false;
 end;
 
 // Check for collision with hedgehog or object
-function TestCollHogsOrObjects(x, y, r: LongInt): boolean; inline;
+function TestCollHogsOrObjects(x, y, r: LongInt): boolean;
 begin
     if not CheckBounds(x, y, r) then
         exit(false);
 
-    if (Land[y-r, x-r] and lfAllObjMask <> 0) or
-       (Land[y+r, x-r] and lfAllObjMask <> 0) or
-       (Land[y-r, x+r] and lfAllObjMask <> 0) or
-       (Land[y+r, x+r] and lfAllObjMask <> 0) then
+    if (LandGet(y-r, x-r) and lfAllObjMask <> 0) or
+       (LandGet(y+r, x-r) and lfAllObjMask <> 0) or
+       (LandGet(y-r, x+r) and lfAllObjMask <> 0) or
+       (LandGet(y+r, x+r) and lfAllObjMask <> 0) then
        exit(true);
 
     TestCollHogsOrObjects:= false;
@@ -445,7 +445,7 @@
 // Check for collision with something other than the given "Me" gear.
 // Wrapper to test various approaches.  If it works reasonably, will just replace.
 // Right now, converting to hwFloat is a tad inefficient since the x/y were hwFloat to begin with...
-function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
+function TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean;
 var MeX, MeY: LongInt;
 begin
     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
@@ -453,7 +453,7 @@
         MeX:= hwRound(Me^.X);
         MeY:= hwRound(Me^.Y);
         // We are still inside the hog. Skip radius test
-        if ((sqr(x-MeX) + sqr(y-MeY)) < 256) and (Land[y, x] and lfObjMask = 0) then
+        if ((sqr(x-MeX) + sqr(y-MeY)) < 256) and (LandGet(y, x) and lfObjMask = 0) then
             exit(false);
     end;
     TestCollExcludingMe:= TestCollWithEverything(x, y, r)
@@ -527,7 +527,7 @@
 
 {        if ((trunc(y) and LAND_HEIGHT_MASK) = 0) and ((trunc(x) and LAND_WIDTH_MASK) = 0) then
             begin
-            LandPixels[trunc(y), trunc(x)]:= v;
+            LandPixelGet(trunc(y), trunc(x)):= v;
             UpdateLandTexture(trunc(X), 1, trunc(Y), 1, true);
             end;}
 
@@ -566,12 +566,12 @@
     end;
 end;
 
-function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
+function RateExplosion(Me: PGear; x, y, r: LongInt): LongInt;
 begin
     RateExplosion:= RealRateExplosion(Me, x, y, r, 0);
     ResetTargets;
 end;
-function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt; inline;
+function RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
 begin
     RateExplosion:= RealRateExplosion(Me, x, y, r, Flags);
     ResetTargets;
@@ -637,7 +637,7 @@
                     if pY - y < 0 then dY:= -dY;
 
                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
-                       (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
+                       (LandGet(y+cHHRadius+2, x) and lfIndestructible <> 0) then
                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
                     end;
@@ -831,7 +831,7 @@
                         ((abs(dY) < 0.15) and (abs(dX) < 0.15))) then
                        dX:= 0;
                     if (x and LAND_WIDTH_MASK = 0) and ((y+cHHRadius+2) and LAND_HEIGHT_MASK = 0) and
-                       (Land[y+cHHRadius+2, x] and lfIndestructible <> 0) then
+                       (LandGet(y+cHHRadius+2, x) and lfIndestructible <> 0) then
                          fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, 0, Targets.ar[i]) * dmgMod)
                     else fallDmg:= trunc(TraceFall(x, y, pX, pY, dX, dY, erasure, Targets.ar[i]) * dmgMod)
                     end;
@@ -1083,7 +1083,7 @@
 repeat
         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
             begin
-            LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= Gear^.Hedgehog^.Team^.Clan^.Color;
+            LandPixelGet(hwRound(Gear^.Y), hwRound(Gear^.X)):= Gear^.Hedgehog^.Team^.Clan^.Color;
             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
             end;}
 
@@ -1149,7 +1149,7 @@
 repeat
         {if ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) then
             begin
-            LandPixels[hwRound(Gear^.Y), hwRound(Gear^.X)]:= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
+            LandPixelGet(hwRound(Gear^.Y), hwRound(Gear^.X)):= random($FFFFFFFF);//Gear^.Hedgehog^.Team^.Clan^.Color;
             UpdateLandTexture(hwRound(Gear^.X), 1, hwRound(Gear^.Y), 1, true);
             end;}
 
@@ -1206,7 +1206,7 @@
 HHJump(AltGear, jmpHJump, GoInfo);
 end;
 
-function AIrndSign(num: LongInt): LongInt; inline;
+function AIrndSign(num: LongInt): LongInt;
 begin
 if random(2) = 0 then
     AIrndSign:=   num
@@ -1214,7 +1214,7 @@
     AIrndSign:= - num
 end;
 
-function AIrndOffset(targ: TTarget; Level: LongWord): LongInt; inline;
+function AIrndOffset(targ: TTarget; Level: LongWord): LongInt;
 begin
 if Level <> 1 then exit(0);
 // at present level 2 doesn't track falls on most things
--- a/hedgewars/uChat.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uChat.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -108,7 +108,7 @@
 procedure UpdateCursorCoords(); forward;
 
 // relevant for UTF-8 handling
-function IsFirstCharByte(c: char): boolean; inline;
+function IsFirstCharByte(c: char): boolean; 
 begin
     // based on https://en.wikipedia.org/wiki/UTF-8#Description
     IsFirstCharByte:= (byte(c) and $C0) <> $80;
--- a/hedgewars/uCollisions.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uCollisions.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -70,11 +70,11 @@
 function  CheckGearsLineCollision(Gear: PGear; oX, oY, tX, tY: hwFloat): PGearArray;
 function  CheckAllGearsLineCollision(SourceGear: PGear; oX, oY, tX, tY: hwFloat): PGearArray;
 
-function  UpdateHitOrder(Gear: PGear; Order: LongInt): boolean; inline;
-function  UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean; inline;
-function  UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean; inline;
-procedure ClearHitOrderLeq(MinOrder: LongInt); inline;
-procedure ClearGlobalHitOrderLeq(MinOrder: LongInt); inline;
+function  UpdateHitOrder(Gear: PGear; Order: LongInt): boolean;
+function  UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean;
+function  UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean;
+procedure ClearHitOrderLeq(MinOrder: LongInt);
+procedure ClearGlobalHitOrderLeq(MinOrder: LongInt);
 procedure ClearHitOrder();
 
 procedure RefillProximityCache(SourceGear: PGear; radius: LongInt);
@@ -84,16 +84,16 @@
 function  TestCollisionXImpl(centerX, centerY, radius, direction: LongInt; collisionMask: Word): Word;
 function  TestCollisionYImpl(centerX, centerY, radius, direction: LongInt; collisionMask: Word): Word;
 
-function  TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word; inline;
-function  TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word; inline;
+function  TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word;
+function  TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word;
 
-function  TestCollisionX(Gear: PGear; Dir: LongInt): Word; inline;
-function  TestCollisionY(Gear: PGear; Dir: LongInt): Word; inline;
+function  TestCollisionX(Gear: PGear; Dir: LongInt): Word;
+function  TestCollisionY(Gear: PGear; Dir: LongInt): Word;
 
-function  TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word; inline;
-function  TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline;
-function  TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word; inline;
-function  TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline;
+function  TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word;
+function  TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word;
+function  TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word;
+function  TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word;
 
 function  TestCollisionXKickImpl(centerX, centerY, radius, direction: LongInt; collisionMask, kickMask: Word): TKickTest;
 function  TestCollisionYKickImpl(centerX, centerY, radius, direction: LongInt; collisionMask, kickMask: Word): TKickTest;
@@ -103,7 +103,7 @@
 
 function  TestRectangleForObstacle(x1, y1, x2, y2: LongInt; landOnly: boolean): boolean;
 
-function  CheckCoordInWater(X, Y: LongInt): boolean; inline;
+function  CheckCoordInWater(X, Y: LongInt): boolean;
 
 // returns: negative sign if going downhill to left, value is steepness (noslope/error = _0, 45 = _0_5)
 function  CalcSlopeBelowGear(Gear: PGear): hwFloat;
@@ -113,7 +113,7 @@
 function CheckGearsUnderSprite(Sprite: TSprite; sprX, sprY, Frame: LongInt): boolean;
 
 implementation
-uses uConsts, uLandGraphics, uVariables, SDLh, uLandTexture, uDebug;
+uses uConsts, uLandGraphics, uVariables, SDLh, uLandTexture, uDebug, uLandUtils;
 
 type TCollisionEntry = record
     X, Y, Radius: LongInt;
@@ -159,7 +159,7 @@
     end;
 end;
 
-function CheckCoordInWater(X, Y: LongInt): boolean; inline;
+function CheckCoordInWater(X, Y: LongInt): boolean;
 begin
     CheckCoordInWater:= (Y > cWaterLine)
         or ((WorldEdge = weSea) and ((X < leftX) or (X > rightX)));
@@ -221,7 +221,7 @@
 
 function LineCollisionTest(oX, oY, dirX, dirY, dirNormSqr, dirNormBound: hwFloat;
         width: LongInt; Gear: PGear):
-    TLineCollision; inline;
+    TLineCollision;
 var toCenterX, toCenterY, r,
     b, bSqr, c, desc, t: hwFloat;
     realT: extended;
@@ -367,12 +367,12 @@
     end
 end;
 
-function UpdateHitOrder(Gear: PGear; Order: LongInt): boolean; inline;
+function UpdateHitOrder(Gear: PGear; Order: LongInt): boolean;
 begin
     UpdateHitOrder := UpdateHitOrderImpl(@ordera, Gear, Order);
 end;
 
-function UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean; inline;
+function UpdateHitOrder(Gear: PGear; Order: LongInt; Global: boolean): boolean;
 begin
     if Global then
         UpdateHitOrder := UpdateHitOrderImpl(@globalordera, Gear, Order)
@@ -380,7 +380,7 @@
         UpdateHitOrder := UpdateHitOrderImpl(@ordera, Gear, Order)
 end;
 
-function UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean; inline;
+function UpdateGlobalHitOrder(Gear: PGear; Order: LongInt): boolean;
 begin
     UpdateGlobalHitOrder := UpdateHitOrderImpl(@globalordera, Gear, Order);
 end;
@@ -408,12 +408,12 @@
     end
 end;
 
-procedure ClearHitOrderLeq(MinOrder: LongInt); inline;
+procedure ClearHitOrderLeq(MinOrder: LongInt);
 begin
     ClearHitOrderLeqImpl(@ordera, MinOrder);
 end;
 
-procedure ClearGlobalHitOrderLeq(MinOrder: LongInt); inline;
+procedure ClearGlobalHitOrderLeq(MinOrder: LongInt);
 begin
     ClearHitOrderLeqImpl(@globalordera, MinOrder);
 end;
@@ -480,8 +480,8 @@
         minY := max(centerY - radius + 1, 0);
         maxY := min(centerY + radius - 1, LAND_HEIGHT - 1);
         for y := minY to maxY do
-            if Land[y, x] and collisionMask <> 0 then
-                exit(Land[y, x] and collisionMask);
+            if LandGet(y, x) and collisionMask <> 0 then
+                exit(LandGet(y, x) and collisionMask);
     end;
     TestCollisionXImpl := 0;
 end;
@@ -499,18 +499,18 @@
         minX := max(centerX - radius + 1, 0);
         maxX := min(centerX + radius - 1, LAND_WIDTH - 1);
         for x := minX to maxX do
-            if Land[y, x] and collisionMask <> 0 then
-                exit(Land[y, x] and collisionMask);
+            if LandGet(y, x) and collisionMask <> 0 then
+                exit(LandGet(y, x) and collisionMask);
     end;
     TestCollisionYImpl := 0;
 end;
 
-function TestCollisionX(Gear: PGear; Dir: LongInt): Word; inline;
+function TestCollisionX(Gear: PGear; Dir: LongInt): Word;
 begin
     TestCollisionX := TestCollisionXImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask and lfLandMask);
 end;
 
-function TestCollisionY(Gear: PGear; Dir: LongInt): Word; inline;
+function TestCollisionY(Gear: PGear; Dir: LongInt): Word;
 begin
     TestCollisionY := TestCollisionYImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask and lfLandMask);
 end;
@@ -533,19 +533,19 @@
         Gear^.CollisionMask:= lfAll;
 end;
 
-function TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word; inline;
+function TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word;
 begin
     LegacyFixupX(Gear);
     TestCollisionXwithGear:= TestCollisionXImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask);
 end;
 
-function TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word; inline;
+function TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word;
 begin
     LegacyFixupY(Gear);
     TestCollisionYwithGear:= TestCollisionYImpl(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, Dir, Gear^.CollisionMask);
 end;
 
-function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline;
+function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word;
 var collisionMask: Word;
 begin
     if withGear then
@@ -559,7 +559,7 @@
     TestCollisionXwithXYShift := TestCollisionXImpl(hwRound(Gear^.X + ShiftX), hwRound(Gear^.Y) + ShiftY, Gear^.Radius, Dir, collisionMask)
 end;
 
-function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word; inline;
+function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt; withGear: boolean): Word;
 var collisionMask: Word;
 begin
     if withGear then
@@ -573,12 +573,12 @@
     TestCollisionYwithXYShift := TestCollisionYImpl(hwRound(Gear^.X) + ShiftX, hwRound(Gear^.Y) + ShiftY, Gear^.Radius, Dir, collisionMask)
 end;
 
-function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word; inline;
+function TestCollisionXwithXYShift(Gear: PGear; ShiftX: hwFloat; ShiftY: LongInt; Dir: LongInt): Word;
 begin
     TestCollisionXwithXYShift:= TestCollisionXwithXYShift(Gear, ShiftX, ShiftY, Dir, true);
 end;
 
-function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word; inline;
+function TestCollisionYwithXYShift(Gear: PGear; ShiftX, ShiftY: LongInt; Dir: LongInt): Word;
 begin
     TestCollisionYwithXYShift:= TestCollisionYwithXYShift(Gear, ShiftX, ShiftY, Dir, true);
 end;
@@ -599,16 +599,16 @@
         minY := max(centerY - radius + 1, 0);
         maxY := min(centerY + radius - 1, LAND_HEIGHT - 1);
         for y := minY to maxY do
-            if Land[y, x] and collisionMask <> 0 then
+            if LandGet(y, x) and collisionMask <> 0 then
             begin
                 TestCollisionXKickImpl.kick := false;
-                TestCollisionXKickImpl.collisionMask := Land[y, x] and collisionMask;
+                TestCollisionXKickImpl.collisionMask := LandGet(y, x) and collisionMask;
                 exit
             end
-            else if Land[y, x] and kickMask <> 0 then
+            else if LandGet(y, x) and kickMask <> 0 then
             begin
                 TestCollisionXKickImpl.kick := true;
-                TestCollisionXKickImpl.collisionMask := Land[y, x] and kickMask;
+                TestCollisionXKickImpl.collisionMask := LandGet(y, x) and kickMask;
             end;
     end;
 end;
@@ -629,16 +629,16 @@
         minX := max(centerX - radius + 1, 0);
         maxX := min(centerX + radius - 1, LAND_WIDTH - 1);
         for x := minX to maxX do
-            if Land[y, x] and collisionMask <> 0 then
+            if LandGet(y, x) and collisionMask <> 0 then
             begin
                 TestCollisionYKickImpl.kick := false;
-                TestCollisionYKickImpl.collisionMask := Land[y, x] and collisionMask;
+                TestCollisionYKickImpl.collisionMask := LandGet(y, x) and collisionMask;
                 exit
             end
-            else if Land[y, x] and kickMask <> 0 then
+            else if LandGet(y, x) and kickMask <> 0 then
             begin
                 TestCollisionYKickImpl.kick := true;
-                TestCollisionYKickImpl.collisionMask := Land[y, x] and kickMask;
+                TestCollisionYKickImpl.collisionMask := LandGet(y, x) and kickMask;
             end;
     end;
 end;
@@ -767,7 +767,7 @@
 
 for y := y1 to y2 do
     for x := x1 to x2 do
-        if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > TestWord) then
+        if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (LandGet(y, x) > TestWord) then
             exit;
 
 TestRectangleForObstacle:= false
@@ -816,7 +816,7 @@
             tmpy:= collisionY + k * my;
 
             if (((tmpy) and LAND_HEIGHT_MASK) = 0) and (((tmpx) and LAND_WIDTH_MASK) = 0) then
-                if (Land[tmpy,tmpx] > TestWord) then
+                if (LandGet(tmpy,tmpx) > TestWord) then
                     begin
                     // remember the index belonging to the first and last collision (if in 1st half)
                     if (i <> 0) then
@@ -867,7 +867,7 @@
                 tmpx:= ldx + k * offset[tmpo,0];
                 tmpy:= ldy + k * offset[tmpo,1];
                 if (((tmpy) and LAND_HEIGHT_MASK) = 0) and (((tmpx) and LAND_WIDTH_MASK)  = 0)
-                and (Land[tmpy,tmpx] > TestWord) then
+                and (LandGet(tmpy,tmpx) > TestWord) then
                     begin
                     ldx:= tmpx;
                     ldy:= tmpy;
@@ -891,7 +891,7 @@
                 tmpx:= rdx + k * offset[tmpo,0];
                 tmpy:= rdy + k * offset[tmpo,1];
                 if (((tmpy) and LAND_HEIGHT_MASK) = 0) and (((tmpx) and LAND_WIDTH_MASK)  = 0)
-                and (Land[tmpy,tmpx] > TestWord) then
+                and (LandGet(tmpy,tmpx) > TestWord) then
                     begin
                     rdx:= tmpx;
                     rdy:= tmpy;
@@ -934,7 +934,7 @@
         i:= x + Gear^.Radius * 2 - 2;
         repeat
         if (x and LAND_WIDTH_MASK) = 0 then
-            if Land[y, x] <> 0 then
+            if LandGet(y, x) <> 0 then
                 if (not isColl) or (abs(x-gx) < abs(collX-gx)) then
                     begin
                     isColl:= true;
@@ -957,7 +957,7 @@
         i:= y + Gear^.Radius * 2 - 2;
         repeat
         if (y and LAND_HEIGHT_MASK) = 0 then
-            if Land[y, x] <> 0 then
+            if LandGet(y, x) <> 0 then
                 if (not isColl) or (abs(y-gy) < abs(collY-gy)) then
                     begin
                     isColl:= true;
@@ -1026,7 +1026,7 @@
     i:= x + Gear^.Radius * 2 - 2;
     repeat
     if (x and LAND_WIDTH_MASK) = 0 then
-        if (Land[y, x] and lfLandMask) <> 0 then
+        if (LandGet(y, x) and lfLandMask) <> 0 then
             if (not isColl) or (abs(x-gx) < abs(collX-gx)) then
                 begin
                 isColl:= true;
--- a/hedgewars/uCommandHandlers.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uCommandHandlers.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -587,7 +587,7 @@
 
             if bShowAmmoMenu then
                 bShowAmmoMenu:= false
-            else if not(CurrentTeam^.Extdriven) and ((Gear = nil) or ((Gear^.State and (gstAttacking or gstAttacked)) <> 0)
+            else if ((Gear = nil) or ((Gear^.State and (gstAttacking or gstAttacked)) <> 0)
             or ((Gear^.State and gstHHDriven) = 0)) then
                 begin
                 end
--- a/hedgewars/uCommands.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uCommands.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -30,7 +30,7 @@
 procedure freeModule;
 procedure RegisterVariable(Name: shortstring; p: TCommandHandler; Trusted: boolean; Rand: boolean);
 procedure RegisterVariable(Name: shortstring; p: TCommandHandler; Trusted: boolean);
-procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); inline;
+procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); 
 procedure ParseCommand(CmdStr: shortstring; TrustedSource, ExternalSource: boolean);
 procedure ParseTeamCommand(s: shortstring);
 procedure StopMessages(Message: Longword);
@@ -76,7 +76,7 @@
 end;
 
 
-procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); inline;
+procedure ParseCommand(CmdStr: shortstring; TrustedSource: boolean); 
 begin
     ParseCommand(CmdStr, TrustedSource, false)
 end;
--- a/hedgewars/uDebug.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uDebug.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -23,7 +23,7 @@
 interface
 
 procedure OutError(Msg: shortstring; isFatalError: boolean);
-//procedure TryDo(Assert: boolean; Msg: shortstring; isFatal: boolean); inline;
+//procedure TryDo(Assert: boolean; Msg: shortstring; isFatal: boolean); 
 function checkFails(Assert: boolean; Msg: shortstring; isFatal: boolean): boolean;
 function SDLCheck(Assert: boolean; Msg: shortstring; isFatal: boolean): boolean;
 
--- a/hedgewars/uFloat.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uFloat.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -56,45 +56,45 @@
 {$ENDIF}
 
 // Returns an hwFloat that represents the value of integer parameter i
-function int2hwFloat (const i: LongInt) : hwFloat; inline;
-function hwFloat2Float (const i: hwFloat) : extended; inline;
+function int2hwFloat (const i: LongInt) : hwFloat; 
+function hwFloat2Float (const i: hwFloat) : extended; 
 
 // The implemented operators
 
-operator = (const z1, z2: hwFloat) z : boolean; inline;
+operator = (const z1, z2: hwFloat) z : boolean; 
 {$IFDEF PAS2C}
-operator <> (const z1, z2: hwFloat) z : boolean; inline;
+operator <> (const z1, z2: hwFloat) z : boolean; 
 {$ENDIF}
-operator + (const z1, z2: hwFloat) z : hwFloat; inline;
-operator - (const z1, z2: hwFloat) z : hwFloat; inline;
-operator - (const z1: hwFloat) z : hwFloat; inline;
+operator + (const z1, z2: hwFloat) z : hwFloat; 
+operator - (const z1, z2: hwFloat) z : hwFloat; 
+operator - (const z1: hwFloat) z : hwFloat; 
 
-operator * (const z1, z2: hwFloat) z : hwFloat; inline;
-operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
-operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline;
-operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
+operator * (const z1, z2: hwFloat) z : hwFloat; 
+operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; 
+operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; 
+operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; 
 
-operator < (const z1, z2: hwFloat) b : boolean; inline;
-operator > (const z1, z2: hwFloat) b : boolean; inline;
+operator < (const z1, z2: hwFloat) b : boolean; 
+operator > (const z1, z2: hwFloat) b : boolean; 
 
 
 // Various functions for hwFloat (some are inlined in the resulting code for better performance)
 
 function cstr(const z: hwFloat): shortstring; // Returns a shortstring representations of the hwFloat.
-function hwRound(const t: hwFloat): LongInt; inline; // Does NOT really round but returns the integer representation of the hwFloat without fractional digits. (-_0_9 -> -0, _1_5 -> _1)
-function hwAbs(const t: hwFloat): hwFloat; inline; // Returns the value of t with positive sign.
-function hwSqr(const t: hwFloat): hwFloat; inline; // Returns the square value of parameter t.
-function hwSqrt1(const t: hwFloat): hwFloat; inline; // Returns the the positive square root of parameter t.
-function hwSqrt(const x: hwFloat): hwFloat; inline; // Returns the the positive square root of parameter t.
+function hwRound(const t: hwFloat): LongInt;  // Does NOT really round but returns the integer representation of the hwFloat without fractional digits. (-_0_9 -> -0, _1_5 -> _1)
+function hwAbs(const t: hwFloat): hwFloat;  // Returns the value of t with positive sign.
+function hwSqr(const t: hwFloat): hwFloat;  // Returns the square value of parameter t.
+function hwSqrt1(const t: hwFloat): hwFloat;  // Returns the the positive square root of parameter t.
+function hwSqrt(const x: hwFloat): hwFloat;  // Returns the the positive square root of parameter t.
 function Distance(const dx, dy: hwFloat): hwFloat; // Returns the distance between two points in 2-dimensional space, of which the parameters are the horizontal and vertical distance.
 function DistanceI(const dx, dy: LongInt): hwFloat; // Same as above for integer parameters.
 function AngleSin(const Angle: Longword): hwFloat;
 function AngleCos(const Angle: Longword): hwFloat;
 function vector2Angle(const x, y: hwFloat): LongInt;
-function SignAs(const num, signum: hwFloat): hwFloat; inline; // Returns an hwFloat with the value of parameter num and the sign of signum.
-function hwSign(r: hwFloat): LongInt; inline; // Returns an integer with value 1 and sign of parameter r.
-function hwSignf(r: real): LongInt; inline; // Returns an integer with value 1 and sign of parameter r.
-function isZero(const z: hwFloat): boolean; inline;
+function SignAs(const num, signum: hwFloat): hwFloat;  // Returns an hwFloat with the value of parameter num and the sign of signum.
+function hwSign(r: hwFloat): LongInt;  // Returns an integer with value 1 and sign of parameter r.
+function hwSignf(r: real): LongInt;  // Returns an integer with value 1 and sign of parameter r.
+function isZero(const z: hwFloat): boolean; 
 
 {$WARNINGS OFF}
 // some hwFloat constants
@@ -195,33 +195,33 @@
 uses uSinTable;
 
 
-function int2hwFloat (const i: LongInt) : hwFloat; inline;
+function int2hwFloat (const i: LongInt) : hwFloat; 
 begin
 int2hwFloat.isNegative:= i < 0;
 int2hwFloat.Round:= abs(i);
 int2hwFloat.Frac:= 0
 end;
 
-function hwFloat2Float (const i: hwFloat) : extended; inline;
+function hwFloat2Float (const i: hwFloat) : extended; 
 begin
 hwFloat2Float:= i.Frac / $100000000 + i.Round;
 if i.isNegative then
     hwFloat2Float:= -hwFloat2Float;
 end;
 
-operator = (const z1, z2: hwFloat) z : boolean; inline;
+operator = (const z1, z2: hwFloat) z : boolean; 
 begin
     z:= (z1.isNegative = z2.isNegative) and (z1.QWordValue = z2.QWordValue);
 end;
 
 {$IFDEF PAS2C}
-operator <> (const z1, z2: hwFloat) z : boolean; inline;
+operator <> (const z1, z2: hwFloat) z : boolean; 
 begin
     z:= (z1.isNegative <> z2.isNegative) or (z1.QWordValue <> z2.QWordValue);
 end;
 {$ENDIF}
 
-operator + (const z1, z2: hwFloat) z : hwFloat; inline;
+operator + (const z1, z2: hwFloat) z : hwFloat; 
 begin
 if z1.isNegative = z2.isNegative then
     begin
@@ -241,7 +241,7 @@
         end
 end;
 
-operator - (const z1, z2: hwFloat) z : hwFloat; inline;
+operator - (const z1, z2: hwFloat) z : hwFloat; 
 begin
 if z1.isNegative = z2.isNegative then
     if z1.QWordValue > z2.QWordValue then
@@ -261,12 +261,12 @@
     end
 end;
 
-function isZero(const z: hwFloat): boolean; inline;
+function isZero(const z: hwFloat): boolean; 
 begin
 isZero := z.QWordValue = 0;
 end;
 
-operator < (const z1, z2: hwFloat) b : boolean; inline;
+operator < (const z1, z2: hwFloat) b : boolean; 
 begin
 if z1.isNegative xor z2.isNegative then
     b:= z1.isNegative
@@ -277,7 +277,7 @@
         b:= (z2.QWordValue < z1.QWordValue) = z1.isNegative
 end;
 
-operator > (const z1, z2: hwFloat) b : boolean; inline;
+operator > (const z1, z2: hwFloat) b : boolean; 
 begin
 if z1.isNegative xor z2.isNegative then
     b:= z2.isNegative
@@ -288,14 +288,14 @@
         b:= (z1.QWordValue > z2.QWordValue) <> z2.isNegative
 end;
 
-operator - (const z1: hwFloat) z : hwFloat; inline;
+operator - (const z1: hwFloat) z : hwFloat; 
 begin
     z:= z1;
     z.isNegative:= not z.isNegative
 end;
 
 
-operator * (const z1, z2: hwFloat) z : hwFloat; inline;
+operator * (const z1, z2: hwFloat) z : hwFloat; 
 begin
     z.isNegative:= z1.isNegative xor z2.isNegative;
     
@@ -310,13 +310,13 @@
     end
 end;
 
-operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
+operator * (const z1: hwFloat; const z2: LongInt) z : hwFloat; 
 begin
     z.isNegative:= z1.isNegative xor (z2 < 0);
     z.QWordValue:= z1.QWordValue * abs(z2)
 end;
 
-operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; inline;
+operator / (const z1: hwFloat; z2: hwFloat) z : hwFloat; 
 var t: QWord;
 begin
     z.isNegative:= z1.isNegative xor z2.isNegative;
@@ -337,7 +337,7 @@
         end
 end;
 
-operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; inline;
+operator / (const z1: hwFloat; const z2: LongInt) z : hwFloat; 
 begin
     z.isNegative:= z1.isNegative xor (z2 < 0);
     z.QWordValue:= z1.QWordValue div abs(z2)
@@ -371,7 +371,7 @@
     hwAbs.isNegative:= false
 end;
 
-function hwSqr(const t: hwFloat): hwFloat; inline;
+function hwSqr(const t: hwFloat): hwFloat; 
 begin
     hwSqr.isNegative:= false;
     hwSqr.QWordValue:= ((QWord(t.Round) * t.Round) shl 32) + QWord(t.Round) * t.Frac * 2 + ((QWord(t.Frac) * t.Frac) shr 32);
--- a/hedgewars/uGearsHandlers.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uGearsHandlers.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -36,13 +36,13 @@
                                      (x: 0;  y:  1),
                                      (x: -1; y:  0));
 
-procedure PrevAngle(Gear: PGear; dA: LongInt); inline;
+procedure PrevAngle(Gear: PGear; dA: LongInt); 
 begin
     inc(Gear^.WDTimer);
     Gear^.Angle := (LongInt(Gear^.Angle) - dA) and 3
 end;
 
-procedure NextAngle(Gear: PGear; dA: LongInt); inline;
+procedure NextAngle(Gear: PGear; dA: LongInt); 
 begin
     inc(Gear^.WDTimer);
     Gear^.Angle := (LongInt(Gear^.Angle) + dA) and 3
--- a/hedgewars/uGearsHandlersMess.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uGearsHandlersMess.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -152,7 +152,7 @@
 uses uConsts, uVariables, uVisualGearsList, uRandom, uCollisions, uGearsList, uUtils, uSound
     , SDLh, uScript, uGearsHedgehog, uGearsUtils, uIO, uCaptions, uLandGraphics
     , uGearsHandlers, uTextures, uRenderUtils, uAmmos, uTeams, uLandTexture
-    , uStore, uAI, uStats, uLocale;
+    , uStore, uAI, uStats, uLocale, uLandUtils;
 
 procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean);
 var
@@ -528,7 +528,7 @@
             end
         else if (collV < 0) and (collH < 0) and tdX.isNegative and tdY.isNegative then
             Gear^.dX.isNegative := false;
-       
+
         isFalling := false;
         Gear^.AdvBounce := 10;
         end;
@@ -884,45 +884,45 @@
         else if (cGravity.isNegative) and (yy < LAND_HEIGHT-1200) then
             move:=true
         // Solid pixel encountered
-        else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
-            begin
-            lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible);
+        else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (LandGet(yy, xx) <> 0) then
+            begin
+            lf:= LandGet(yy, xx) and (lfObject or lfBasic or lfIndestructible);
             if lf = 0 then lf:= lfObject;
             // If there's room below keep falling
-            if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then
+            if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (LandGet(yy-1, xx) = 0) then
                 begin
                 X:= X - cWindSpeed * 1600 - dX;
                 end
             // If there's room below, on the sides, fill the gaps
-            else if (((yy-1) and LAND_HEIGHT_MASK) = 0) then 
+            else if (((yy-1) and LAND_HEIGHT_MASK) = 0) then
 		    begin
-		    if (((xx - 1) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx - 1)] = 0) then
+		    if (((xx - 1) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx - 1)) = 0) then
 		        begin
 		        X:= X - _0_8;
 		        Y:= oldY;
 		        end
-		    else if (((xx - 2) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx - 2)] = 0) then
+		    else if (((xx - 2) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx - 2)) = 0) then
 		        begin
 		        X:= X - _1_6;
 		        Y:= oldY;
 		        end
-		    else if (((xx + 1) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx + 1)] = 0) then
+		    else if (((xx + 1) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx + 1)) = 0) then
 		        begin
 		        X:= X + _0_8;
 		        Y:= oldY;
 		        end
-		    else if (((xx + 2) and LAND_WIDTH_MASK) = 0) and (Land[yy - 1, (xx + 2)] = 0) then
+		    else if (((xx + 2) and LAND_WIDTH_MASK) = 0) and (LandGet(yy - 1, (xx + 2)) = 0) then
 		        begin
 		        X:= X + _1_6;
 		        Y:= oldY;
 		        end else
-		    if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy + 1, xx] and $FF) <> 0)) then 
-		       move:=true 
-		    else 
+		    if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((LandGet(yy + 1, xx) and $FF) <> 0)) then
+		       move:=true
+		    else
 		       draw:= true
 		    end
             // if there's an hog/object below do nothing
-            else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0))
+            else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((LandGet(yy+1, xx) and $FF) <> 0))
                 then move:=true
             else draw:= true
             end
@@ -949,7 +949,7 @@
                 for px:= 0 to Pred(s^.w) do
                     begin
                     lx:=xx + px; ly:=yy + py;
-                    if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then
+                    if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (LandGet(ly, lx) and $FF = 0) then
                         begin
                         rx:= lx;
                         ry:= ly;
@@ -957,21 +957,21 @@
                             begin
                             rx:= rx div 2;ry:= ry div 2;
                             end;
-                        if Land[yy + py, xx + px] <= lfAllObjMask then
+                        if LandGet(yy + py, xx + px) <= lfAllObjMask then
                             if gun then
                                 begin
                                 LandDirty[yy div 32, xx div 32]:= 1;
-                                if LandPixels[ry, rx] = 0 then
-                                    Land[ly, lx]:=  lfDamaged or lfObject
-                                else Land[ly, lx]:=  lfDamaged or lfBasic
+                                if LandPixelGet(ry, rx) = 0 then
+                                    LandSet(ly, lx, lfDamaged or lfObject)
+                                else LandSet(ly, lx, lfDamaged or lfBasic)
                                 end
-                            else Land[ly, lx]:= lf;
+                            else LandSet(ly, lx, lf);
                         if gun then
-                             LandPixels[ry, rx]:= (Gear^.Tint shr 24         shl RShift) or 
-                                                  (Gear^.Tint shr 16 and $FF shl GShift) or 
-                                                  (Gear^.Tint shr  8 and $FF shl BShift) or 
-                                                  (p^[px] and AMask)
-                        else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]);
+                             LandPixelSet(ry, rx, (Gear^.Tint shr 24         shl RShift) or
+                                                  (Gear^.Tint shr 16 and $FF shl GShift) or
+                                                  (Gear^.Tint shr  8 and $FF shl BShift) or
+                                                  (p^[px] and AMask))
+                        else LandPixelSet(ry, rx, addBgColor(LandPixelGet(ry, rx), p^[px]));
                         end
                     else allpx:= false
                     end;
@@ -1520,7 +1520,7 @@
 
         if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
         begin
-            LandFlags:= Land[y, x];
+            LandFlags:= LandGet(y, x);
             if LandFlags <> 0 then inc(Gear^.Damage);
             isDigging:= (LandFlags and lfLandMask) <> 0;
         end;
@@ -1762,7 +1762,7 @@
     if (Gear^.Timer mod 47) = 0 then
         begin
         // ok. this was an attempt to turn off dust if not actually drilling land.  I have no idea why it isn't working as expected
-        if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then
+        if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (LandGet(y + 12, x) > 255) then
             for i:= 0 to 1 do
                 AddVisualGear(x - 5 + Random(10), y + 12, vgtDust);
 
@@ -2194,7 +2194,7 @@
 
     // If in ready timer, or after turn, or in first 5 seconds of turn (really a window due to extra time utility)
     // or hunting is disabled due to seek radius of 0 then we aren't hunting
-    if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or 
+    if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or
         ((TurnTimeLeft < cHedgehogTurnTime) and (cHedgehogTurnTime-TurnTimeLeft < 5000)) or
         (Gear^.Angle = 0) then
         gear^.State:= gear^.State and (not gstChooseTarget)
@@ -3452,7 +3452,7 @@
     begin
         DeleteGear(Gear);
         exit
-    end; 
+    end;
 
     valid:= false;
 
@@ -4246,8 +4246,8 @@
         dec(playWidth, 2);
         for i:= 0 to LAND_HEIGHT - 1 do
             begin
-            Land[i, leftX] := 0;
-            Land[i, rightX] := 0;
+            LandSet(i, leftX, 0);
+            LandSet(i, rightX, 0);
             end;
         end;
 
@@ -4255,7 +4255,7 @@
         begin
         dec(cWaterLine);
         for i:= 0 to LAND_WIDTH - 1 do
-            Land[cWaterLine, i] := 0;
+            LandSet(cWaterLine, i, 0);
         SetAllToActive
         end;
 
@@ -5039,8 +5039,8 @@
     doPortalColorSwitch();
 
     // destroy portal if ground it was attached too is gone
-    if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask)
-    or (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] and lfBouncy <> 0)
+    if (LandGet(hwRound(Gear^.Y), hwRound(Gear^.X)) <= lfAllObjMask)
+    or (LandGet(hwRound(Gear^.Y), hwRound(Gear^.X)) and lfBouncy <> 0)
     or (Gear^.Timer < 1)
     or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team)
     or CheckCoordInWater(hwRound(Gear^.X), hwRound(Gear^.Y)) then
@@ -5407,12 +5407,12 @@
     ty := 0;
     // avoid compiler hints
 
-    if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then
+    if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (LandGet(y, x) > 255) then
         begin
         Gear^.State := Gear^.State or gstCollision;
         Gear^.State := Gear^.State and (not gstMoving);
 
-        if (Land[y, x] and lfBouncy <> 0)
+        if (LandGet(y, x) and lfBouncy <> 0)
         or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255))
         or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain
             begin
@@ -5773,9 +5773,9 @@
             if (not CheckCoordInWater(rX, rY)) or (not CheckCoordInWater(x, y)) then
                 begin
                 if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0)
-                    and (Land[y, x] <> 0) then
+                    and (LandGet(y, x) <> 0) then
                         begin
-                        if ((GameFlags and gfSolidLand) <> 0) and (Land[y, x] > 255) then
+                        if ((GameFlags and gfSolidLand) <> 0) and (LandGet(y, x) > 255) then
                             Gear^.Damage := initHealth
                         else if justCollided then
                             begin
@@ -6683,7 +6683,7 @@
         ndY:= -AngleCos(HHGear^.Angle) * _4;
         if (ndX <> dX) or (ndY <> dY) or (Gear^.Message and (gmUp or gmDown) <> 0) or
            (((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and
-             (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0)) and
+             (Target.Y and LAND_HEIGHT_MASK = 0) and ((LandGet(Target.Y, Target.X) = 0)) and
              (not CheckCoordInWater(Target.X, Target.Y))) and (CheckGearNear(gtAirMine, int2hwFloat(Target.X),int2hwFloat(Target.Y), Gear^.Radius*3, Gear^.Radius*3) = nil) and
              (not ((WorldEdge = weBounce) and ((Target.X > rightX) or (Target.X < leftX))))) then
             begin
@@ -6717,7 +6717,7 @@
                 else if CheckCoordInWater(Target.X, Target.Y) or
                         ((Target.X and LAND_WIDTH_MASK  = 0) and
                          (Target.Y and LAND_HEIGHT_MASK = 0) and
-                         (Land[Target.Y, Target.X] = lfIce) and
+                         (LandGet(Target.Y, Target.X) = lfIce) and
                          ((Target.Y+iceHeight+5 > cWaterLine) or
                           ((WorldEdge = weSea) and
                            ((Target.X+iceHeight+5 > rightX) or
@@ -6799,13 +6799,13 @@
                                 begin
                                 iter^.Damage:= 0;
                                 iter^.State:= iter^.State or gstFrozen;
-                                if (hwRound(iter^.X) < RightX-16) and (hwRound(iter^.X) > LeftX+16) and 
+                                if (hwRound(iter^.X) < RightX-16) and (hwRound(iter^.X) > LeftX+16) and
                                     (hwRound(iter^.Y) > topY+16) and (hwRound(iter^.Y) < LAND_HEIGHT-16) then
                                     begin
                                     AddCI(iter);
                                     iter^.X:= int2hwFloat(min(RightX-16,max(hwRound(iter^.X), LeftX+16)));
                                     iter^.Y:= int2hwFloat(min(LAND_HEIGHT-16,max(hwRound(iter^.Y),TopY+16)));
-                                    ForcePlaceOnLand(hwRound(iter^.X)-16, hwRound(iter^.Y)-16, sprFrozenAirMine, 0, lfIce, $FFFFFFFF, false, false, false);    
+                                    ForcePlaceOnLand(hwRound(iter^.X)-16, hwRound(iter^.Y)-16, sprFrozenAirMine, 0, lfIce, $FFFFFFFF, false, false, false);
                                     iter^.State:= iter^.State or gstInvisible
                                     end
                                 else
@@ -6865,7 +6865,7 @@
                 end
             else if (t > 400) and (CheckCoordInWater(gX, gY) or
                     (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0))
-                        and (Land[gY, gX] <> 0))) then
+                        and (LandGet(gY, gX) <> 0))) then
                 begin
                 Target.X:= gX;
                 Target.Y:= gY;
@@ -6888,7 +6888,7 @@
                     Target.Y:= gY;
                     X:= HHGear^.X;
                     Y:= HHGear^.Y
-                    end 
+                    end
                 end;
         end
     end;
@@ -6996,7 +6996,7 @@
         tX:=Gear^.X-targ^.X;
         tY:=Gear^.Y-targ^.Y;
         // allow escaping - should maybe flag this too
-        if (GameTicks > Gear^.FlightTime+10000) or 
+        if (GameTicks > Gear^.FlightTime+10000) or
             ((tX.Round+tY.Round > Gear^.Angle*6) and
             (hwRound(hwSqr(tX) + hwSqr(tY)) > sqr(Gear^.Angle*6))) then
             targ:= nil
@@ -7005,7 +7005,7 @@
     // If in ready timer, or after turn, or in first 5 seconds of turn (really a window due to extra time utility)
     // or mine is inactive due to lack of gsttmpflag or hunting is disabled due to seek radius of 0
     // then we aren't hunting
-    if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or 
+    if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or
         ((TurnTimeLeft < cHedgehogTurnTime) and (cHedgehogTurnTime-TurnTimeLeft < 5000)) or
         (Gear^.State and gsttmpFlag = 0) or
         (Gear^.Angle = 0) then
@@ -7269,7 +7269,7 @@
         MakeSentryStep := true
     end
 end;
-    
+
 function MakeSentryJump(Sentry: PGear; maxXStep, maxYStep: LongInt): Boolean;
 var x, y, offsetX, offsetY, direction: LongInt;
     jumpTime: hwFloat;
@@ -7322,7 +7322,7 @@
 
     for i := 0 to count - 1 do
     begin
-        if (Land[hwRound(fromY), hwRound(fromX)] and mask) <> 0 then
+        if (LandGet(hwRound(fromY), hwRound(fromX)) and mask) <> 0 then
             Inc(TraceAttackPath);
         fromX := fromX + distX;
         fromY := fromY + distY;
--- a/hedgewars/uGearsHandlersRope.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uGearsHandlersRope.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -26,7 +26,7 @@
 
 implementation
 uses uConsts, uFloat, uCollisions, uVariables, uGearsList, uSound, uGearsUtils,
-    uAmmos, uDebug, uUtils, uGearsHedgehog, uGearsRender;
+    uAmmos, uDebug, uUtils, uGearsHedgehog, uGearsRender, uLandUtils;
 
 const
     IsNilHHFatal = false;
@@ -241,7 +241,7 @@
         begin
         lx := hwRound(nx);
         ly := hwRound(ny);
-        if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and (Land[ly, lx] > lfAllObjMask) then
+        if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and (LandGet(ly, lx) > lfAllObjMask) then
             begin
             tx := _1 / Distance(ropeDx, ropeDy);
             // old rope pos
@@ -358,7 +358,7 @@
         HHGear^.dY := HHGear^.dY * len;
         end;
 
-    haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)]) <> 0);
+    haveCollision:= ((hwRound(Gear^.Y) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X) and LAND_WIDTH_MASK) = 0) and ((LandGet(hwRound(Gear^.Y), hwRound(Gear^.X))) <> 0);
 
     if not haveCollision then
         begin
@@ -474,7 +474,7 @@
         ty := _0;
         while tt > _20 do
             begin
-            if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and (Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] > lfAllObjMask) then
+            if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and (LandGet(hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)) > lfAllObjMask) then
                 begin
                 Gear^.X := Gear^.X + tx;
                 Gear^.Y := Gear^.Y + ty;
--- a/hedgewars/uGearsHedgehog.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uGearsHedgehog.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -29,7 +29,7 @@
 procedure HedgehogChAngle(HHGear: PGear);
 procedure PickUp(HH, Gear: PGear);
 procedure AddPickup(HH: THedgehog; ammo: TAmmoType; cnt, X, Y: LongWord);
-procedure CheckIce(Gear: PGear); inline;
+procedure CheckIce(Gear: PGear); 
 procedure PlayTaunt(taunt: Longword);
 function HHGetTimer(Gear: PGear): LongWord;
 function HHGetTimerMsg(Gear: PGear): LongWord;
@@ -1529,7 +1529,7 @@
 AllInactive:= false
 end;
 
-procedure CheckIce(Gear: PGear); inline;
+procedure CheckIce(Gear: PGear); 
 (*
 var x,y,tx,ty: LongInt;
     tdX, tdY, slope: hwFloat;
--- a/hedgewars/uGearsRender.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uGearsRender.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -56,7 +56,7 @@
                 end;
 
 implementation
-uses uRender, uRenderUtils, uGearsUtils, uUtils, uVariables, uAmmos, Math, uVisualGearsList;
+uses uRender, uRenderUtils, uGearsUtils, uUtils, uVariables, uAmmos, Math, uVisualGearsList, uLandUtils;
 
 procedure DrawRopeLinesRQ(Gear: PGear);
 var n: LongInt;
@@ -72,7 +72,7 @@
 if (RopePoints.Count > 0) or (Gear^.Elasticity.QWordValue > 0) then
     begin
     EnableTexture(false);
-    
+
     Tint(Gear^.Tint shr 24 div 3, Gear^.Tint shr 16 and $FF div 3, Gear^.Tint shr 8 and $FF div 3, Gear^.Tint and $FF);
 
     n:= RopePoints.Count + 2;
@@ -541,7 +541,7 @@
                 hy:= ty;
                 wraps:= 0;
                 inWorldBounds := ((ty and LAND_HEIGHT_MASK) or (tx and LAND_WIDTH_MASK)) = 0;
-                while (inWorldBounds and ((Land[ty, tx] and lfAll) = 0)) or (not inWorldBounds) do
+                while (inWorldBounds and ((LandGet(ty, tx) and lfAll) = 0)) or (not inWorldBounds) do
                     begin
                     if wraps > cMaxLaserSightWraps then
                         break;
@@ -1049,7 +1049,7 @@
                 ty:= hwRound(Gear^.Y) + cHHRadius + 2;
                 if ((tx and LAND_WIDTH_MASK) = 0) and
                     ((ty and LAND_HEIGHT_MASK) = 0) and
-                        (Land[ty, tx] <> 0) then
+                        (LandGet(ty, tx) <> 0) then
                             AddVisualGear(tx - 2 + Random(4), ty - 8, vgtDust);
                 end;
 
@@ -1417,7 +1417,7 @@
                        DrawSpriteRotated(sprMineOn, x, y, 0, Gear^.DirAngle)
                     else DrawSpriteRotated(sprMineDead, x, y, 0, Gear^.DirAngle);
                     end;
-         gtAirMine: 
+         gtAirMine:
                     // render air mine based on its state:
                     // frozen
                     if (Gear^.State and gstFrozen <> 0) then
--- a/hedgewars/uGearsUtils.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uGearsUtils.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -22,7 +22,7 @@
 interface
 uses uTypes, uFloat;
 
-procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline;
+procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword);
 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
 procedure AddSplashForGear(Gear: PGear; justSkipping: boolean);
 procedure AddBounceEffectForGear(Gear: PGear; imageScale: Single);
@@ -39,7 +39,7 @@
 procedure CalcRotationDirAngle(Gear: PGear);
 procedure ResurrectHedgehog(var gear: PGear);
 
-procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); inline;
+procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt);
 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity, deleteOnFail: boolean);
 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right, Bottom: LongInt; skipProximity, deleteOnFail: boolean);
@@ -48,8 +48,8 @@
 function  CheckGearNear(Kind: TGearType; X, Y: hwFloat; rX, rY: LongInt): PGear;
 function  CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
 function  CheckGearDrowning(var Gear: PGear): boolean;
-procedure CheckCollision(Gear: PGear); inline;
-procedure CheckCollisionWithLand(Gear: PGear); inline;
+procedure CheckCollision(Gear: PGear);
+procedure CheckCollisionWithLand(Gear: PGear);
 
 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
 procedure AmmoShoveCache(Ammo: PGear; Damage, Power: LongInt);
@@ -63,7 +63,7 @@
 
 procedure SetAllToActive;
 procedure SetAllHHToActive(Ice: boolean);
-procedure SetAllHHToActive(); inline;
+procedure SetAllHHToActive();
 
 function  GetAmmo(Hedgehog: PHedgehog): TAmmoType;
 function  GetUtility(Hedgehog: PHedgehog): TAmmoType;
@@ -84,9 +84,9 @@
     uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore,
     uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug,
     uGearsList, Math, uVisualGearsList, uGearsHandlersMess,
-    uGearsHedgehog;
+    uGearsHedgehog, uLandUtils;
 
-procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline;
+procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword);
 begin
     doMakeExplosion(X, Y, Radius, AttackingHog, Mask, $FFFFFFFF);
 end;
@@ -873,7 +873,7 @@
 begin
     if (y and LAND_HEIGHT_MASK) = 0 then
         for i:= max(x - r, 0) to min(x + r, LAND_WIDTH - 1) do
-            if (Land[y, i] and mask <> 0) and (Land[y, i] and antimask = 0) then
+            if (LandGet(y, i) and mask <> 0) and (LandGet(y, i) and antimask = 0) then
                 begin
                 inc(count);
                 if count = c then
@@ -895,8 +895,8 @@
     begin
         for i:= r - c + 2 to r do
         begin
-            if (Land[y, x - i] and mask <> 0) then inc(cnt);
-            if (Land[y, x + i] and mask <> 0) then inc(cnt);
+            if (LandGet(y, x - i) and mask <> 0) then inc(cnt);
+            if (LandGet(y, x + i) and mask <> 0) then inc(cnt);
 
             if cnt >= c then
             begin
@@ -925,12 +925,12 @@
 NoGearsToAvoid:= true
 end;
 
-procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); inline;
+procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt);
 begin
     FindPlace(Gear, withFall, Left, Right, false, true);
 end;
 
-procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean); inline;
+procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
 begin
     FindPlace(Gear, withFall, Left, Right, skipProximity, true);
 end;
@@ -965,7 +965,7 @@
     repeat
         if GetRandom(2) = 0 then dir:= -1 else dir:= 1;
         x:= max(LAND_WIDTH div 2048, LongInt(GetRandom(Delta)));
-        if dir = 1 then x:= Left + x else x:= Right - x; 
+        if dir = 1 then x:= Left + x else x:= Right - x;
         repeat
             cnt:= 0;
             y:= min(1024, topY) - Gear^.Radius shl 1;
@@ -984,7 +984,7 @@
                 until (y >= Bottom) or
                         (ignoreOverlap and 
                                 (CountLand(x, y, Gear^.Radius - 1, 1, lfAll, 0) <> 0)) or
-                        (not ignoreOverlap and 
+                        (not ignoreOverlap and
                             (CountLand(x, y, Gear^.Radius - 1, 1, lfLandMask, 0) <> 0));
 
                 if (y - sy > Gear^.Radius * 2) and (y < Bottom)
@@ -1172,7 +1172,7 @@
     CheckGearNear := CheckGearNearImpl(Kind, Gear^.X, Gear^.Y, rX, rY, Gear);
 end;
 
-procedure CheckCollision(Gear: PGear); inline;
+procedure CheckCollision(Gear: PGear);
 begin
     if (TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) <> 0)
     or (TestCollisionYwithGear(Gear, hwSign(Gear^.dY)) <> 0) then
@@ -1181,7 +1181,7 @@
         Gear^.State := Gear^.State and (not gstCollision)
 end;
 
-procedure CheckCollisionWithLand(Gear: PGear); inline;
+procedure CheckCollisionWithLand(Gear: PGear);
 begin
     if (TestCollisionX(Gear, hwSign(Gear^.dX)) <> 0)
     or (TestCollisionY(Gear, hwSign(Gear^.dY)) <> 0) then
@@ -1413,8 +1413,8 @@
                        gtFirePunch, gtKamikaze, gtWhip, gtShover])
         and (((Ammo^.Data <> nil) and (PGear(Ammo^.Data) = Gear))
             or (not UpdateHitOrder(
-                    Gear, 
-                    Ammo^.WDTimer, 
+                    Gear,
+                    Ammo^.WDTimer,
                     (Ammo^.Kind = gtMinigunBullet) and (Ammo^.Pos <> 0)))) then
         continue;
 
@@ -1502,10 +1502,10 @@
             else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then
                 begin
                 if (Ammo^.Kind in [gtMinigunBullet]) then
-                    begin    
+                    begin
                     Gear^.dX:= Gear^.dX + Ammo^.dX * Power * _0_01;
                     Gear^.dY:= Gear^.dY + Ammo^.dY * Power * _0_01
-                    end 
+                    end
                 else
                     begin
                     Gear^.dX:= Ammo^.dX * Power * _0_01;
@@ -1591,7 +1591,7 @@
     end
 end;
 
-procedure SetAllHHToActive; inline;
+procedure SetAllHHToActive;
 begin
 SetAllHHToActive(true)
 end;
--- a/hedgewars/uInputHandler.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uInputHandler.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -25,7 +25,7 @@
 procedure initModule;
 procedure freeModule;
 
-function  KeyNameToCode(name: shortstring): LongInt; inline;
+function  KeyNameToCode(name: shortstring): LongInt; 
 function  KeyNameToCode(name: shortstring; Modifier: shortstring): LongInt;
 
 function  KeyBindToCode(bind: shortstring): LongInt;
@@ -36,7 +36,7 @@
 procedure ProcessMouseMotion(xrel, yrel: LongInt);
 //procedure ProcessMouseWheel(x, y: LongInt);
 procedure ProcessMouseWheel(y: LongInt);
-procedure ProcessKey(event: TSDL_KeyboardEvent); inline;
+procedure ProcessKey(event: TSDL_KeyboardEvent); 
 procedure ProcessKey(code: LongInt; KeyDown: boolean);
 
 {$IFDEF USE_AM_NUMCOLUMN}
@@ -84,7 +84,7 @@
     //ControllerHats: array[0..5] of array[0..19] of Byte;
     //ControllerButtons: array[0..5] of array[0..19] of Byte;
 
-function  KeyNameToCode(name: shortstring): LongInt; inline;
+function  KeyNameToCode(name: shortstring): LongInt; 
 begin
     KeyNameToCode:= KeyNameToCode(name, '');
 end;
@@ -294,7 +294,7 @@
     end
 end;
 
-procedure ProcessKey(event: TSDL_KeyboardEvent); inline;
+procedure ProcessKey(event: TSDL_KeyboardEvent); 
 var code: LongInt;
 begin
     // TODO
--- a/hedgewars/uLand.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLand.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -38,13 +38,12 @@
 var digest: shortstring;
     maskOnly: boolean;
 
-
 procedure PrettifyLandAlpha();
 begin
     if (cReducedQuality and rqBlurryLand) <> 0 then
-        PrettifyAlpha2D(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2)
+        PrettifyAlpha2D(LAND_HEIGHT div 2, LAND_WIDTH div 2)
     else
-        PrettifyAlpha2D(LandPixels, LAND_HEIGHT, LAND_WIDTH);
+        PrettifyAlpha2D(LAND_HEIGHT, LAND_WIDTH);
 end;
 
 procedure DrawBorderFromImage(Surface: PSDL_Surface);
@@ -64,18 +63,18 @@
     begin
         yd:= LAND_HEIGHT - 1;
         repeat
-            while (yd > 0) and ((Land[yd, x] and targetMask) = 0) do dec(yd);
+            while (yd > 0) and ((LandGet(yd, x) and targetMask) = 0) do dec(yd);
 
             if (yd < 0) then
                 yd:= 0;
 
-            while (yd < LAND_HEIGHT) and ((Land[yd, x] and targetMask) <> 0) do
+            while (yd < LAND_HEIGHT) and ((LandGet(yd, x) and targetMask) <> 0) do
                 inc(yd);
             dec(yd);
             yu:= yd;
 
-            while (yu > 0  ) and ((Land[yu, x] and targetMask) <> 0) do dec(yu);
-            while (yu < yd ) and ((Land[yu, x] and targetMask) =  0) do inc(yu);
+            while (yu > 0  ) and ((LandGet(yu, x) and targetMask) <> 0) do dec(yu);
+            while (yu < yd ) and ((LandGet(yu, x) and targetMask) =  0) do inc(yu);
 
             if (yd < LAND_HEIGHT - 1) and ((yd - yu) >= 16) then
                 copyToXYFromRect(tmpsurf, Surface, x mod tmpsurf^.w, 16, 1, 16, x, yd - 15);
@@ -100,7 +99,7 @@
 
     for x:= 0 to LAND_WIDTH - 1 do
         for y:= 0 to LAND_HEIGHT - 1 do
-            if Land[y, x] = 0 then
+            if LandGet(y, x) = 0 then
                 if s < y then
                     begin
                     for i:= max(s, y - 8) to y - 1 do
@@ -108,9 +107,9 @@
                         if ((x + i) and 16) = 0 then c:= c1 else c:= c2;
 
                         if (cReducedQuality and rqBlurryLand) = 0 then
-                            LandPixels[i, x]:= c
+                            LandPixelSet(i, x, c)
                         else
-                            LandPixels[i div 2, x div 2]:= c
+                            LandPixelSet(i div 2, x div 2, c)
                         end;
                     s:= LAND_HEIGHT
                     end
@@ -123,9 +122,9 @@
                     if ((x + y) and 16) = 0 then c:= c1 else c:= c2;
 
                     if (cReducedQuality and rqBlurryLand) = 0 then
-                        LandPixels[y, x]:= c
+                        LandPixelSet(y, x, c)
                     else
-                        LandPixels[y div 2, x div 2]:= c
+                        LandPixelSet(y div 2, x div 2, c)
                     end;
                 end;
 
@@ -134,7 +133,7 @@
 
     for y:= 0 to LAND_HEIGHT - 1 do
         for x:= 0 to LAND_WIDTH - 1 do
-            if Land[y, x] = 0 then
+            if LandGet(y, x) = 0 then
                 if s < x then
                     begin
                     for i:= max(s, x - 8) to x - 1 do
@@ -142,9 +141,9 @@
                         if ((y + i) and 16) = 0 then c:= c1 else c:= c2;
 
                         if (cReducedQuality and rqBlurryLand) = 0 then
-                            LandPixels[y, i]:= c
+                            LandPixelSet(y, i, c)
                         else
-                            LandPixels[y div 2, i div 2]:= c
+                            LandPixelSet(y div 2, i div 2, c)
                         end;
                     s:= LAND_WIDTH
                     end
@@ -157,9 +156,9 @@
                     if ((x + y) and 16) = 0 then c:= c1 else c:= c2;
 
                     if (cReducedQuality and rqBlurryLand) = 0 then
-                        LandPixels[y, x]:= c
+                        LandPixelSet(y, x, c)
                     else
-                        LandPixels[y div 2, x div 2]:= c
+                        LandPixelSet(y div 2, x div 2, c)
                     end;
                 end
 end;
@@ -356,12 +355,12 @@
     uLandPainted.Draw;
 end;
 
-function SelectTemplate: LongInt;
+function SelectTemplate: shortstring;
 var l: LongInt;
 begin
-    SelectTemplate:= 0;
+    SelectTemplate:= '';
     if (cReducedQuality and rqLowRes) <> 0 then
-        SelectTemplate:= SmallTemplates[getrandom(Succ(High(SmallTemplates)))]
+        SelectTemplate:= 'small'
     else
         begin
         if cTemplateFilter = 0 then
@@ -376,20 +375,20 @@
 
             case cTemplateFilter of
             0: OutError('Error selecting TemplateFilter. Ask unC0Rr about what you did wrong', true);
-            1: SelectTemplate:= SmallTemplates[getrandom(TemplateCounts[cTemplateFilter])];
-            2: SelectTemplate:= MediumTemplates[getrandom(TemplateCounts[cTemplateFilter])];
-            3: SelectTemplate:= LargeTemplates[getrandom(TemplateCounts[cTemplateFilter])];
-            4: SelectTemplate:= CavernTemplates[getrandom(TemplateCounts[cTemplateFilter])];
-            5: SelectTemplate:= WackyTemplates[getrandom(TemplateCounts[cTemplateFilter])];
+            1: SelectTemplate:= 'small';
+            2: SelectTemplate:= 'medium';
+            3: SelectTemplate:= 'large';
+            4: SelectTemplate:= 'cavern';
+            5: SelectTemplate:= 'wacky';
     // For lua only!
             6: begin
-               SelectTemplate:= min(LuaTemplateNumber,High(EdgeTemplates));
+               SelectTemplate:= 'small';
                GetRandom(2) // burn 1
                end
             end
         end;
 
-    WriteLnToConsole('Selected template #'+inttostr(SelectTemplate)+' using filter #'+inttostr(cTemplateFilter));
+    WriteLnToConsole('Using template filter '+SelectTemplate);
 end;
 
 procedure LandSurface2LandPixels(Surface: PSDL_Surface);
@@ -405,11 +404,11 @@
 for y:= 0 to LAND_HEIGHT - 1 do
     begin
     for x:= 0 to LAND_WIDTH - 1 do
-    if Land[y, x] <> 0 then
+    if LandGet(y, x) <> 0 then
         if (cReducedQuality and rqBlurryLand) = 0 then
-            LandPixels[y, x]:= p^[x]// or AMask
+            LandPixelSet(y, x, p^[x])// or AMask
         else
-            LandPixels[y div 2, x div 2]:= p^[x];
+            LandPixelSet(y div 2, x div 2, p^[x]);
 
     p:= PLongwordArray(@(p^[Surface^.pitch div 4]));
     end;
@@ -439,38 +438,38 @@
 
     for x:= LongWord(leftX+2) to LongWord(rightX-2) do
         for y:= LongWord(topY+2) to LAND_HEIGHT-3 do
-            if (Land[y, x] = 0) and
-               (((Land[y, x-1] = lfBasic) and ((Land[y+1,x] = lfBasic)) or (Land[y-1,x] = lfBasic)) or
-               ((Land[y, x+1] = lfBasic) and ((Land[y-1,x] = lfBasic) or (Land[y+1,x] = lfBasic)))) then
+            if (LandGet(y, x) = 0) and
+               (((LandGet(y, x-1) = lfBasic) and ((LandGet(y+1,x) = lfBasic)) or (LandGet(y-1,x) = lfBasic)) or
+               ((LandGet(y, x+1) = lfBasic) and ((LandGet(y-1,x) = lfBasic) or (LandGet(y+1,x) = lfBasic)))) then
             begin
                 if (cReducedQuality and rqBlurryLand) = 0 then
                     begin
-                    if (Land[y, x-1] = lfBasic) and (LandPixels[y, x-1] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y, x-1]
+                    if (LandGet(y, x-1) = lfBasic) and (LandPixelGet(y, x-1) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y, x-1))
 
-                    else if (Land[y, x+1] = lfBasic) and (LandPixels[y, x+1] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y, x+1]
+                    else if (LandGet(y, x+1) = lfBasic) and (LandPixelGet(y, x+1) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y, x+1))
 
-                    else if (Land[y-1, x] = lfBasic) and (LandPixels[y-1, x] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y-1, x]
+                    else if (LandGet(y-1, x) = lfBasic) and (LandPixelGet(y-1, x) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y-1, x))
 
-                    else if (Land[y+1, x] = lfBasic) and (LandPixels[y+1, x] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y+1, x];
+                    else if (LandGet(y+1, x) = lfBasic) and (LandPixelGet(y+1, x) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y+1, x));
 
-                    if (((LandPixels[y,x] and AMask) shr AShift) > 10) then
-                        LandPixels[y,x]:= (LandPixels[y,x] and (not AMask)) or (128 shl AShift)
+                    if (((LandPixelGet(y,x) and AMask) shr AShift) > 10) then
+                        LandPixelSet(y, x, (LandPixelGet(y,x) and (not AMask)) or (128 shl AShift))
                     end;
-                Land[y,x]:= lfObject
+                LandSet(y, x, lfObject)
             end
-            else if (Land[y, x] = 0) and
-                    (((Land[y, x-1] = lfBasic) and (Land[y+1,x-1] = lfBasic) and (Land[y+2,x] = lfBasic)) or
-                    ((Land[y, x-1] = lfBasic) and (Land[y-1,x-1] = lfBasic) and (Land[y-2,x] = lfBasic)) or
-                    ((Land[y, x+1] = lfBasic) and (Land[y+1,x+1] = lfBasic) and (Land[y+2,x] = lfBasic)) or
-                    ((Land[y, x+1] = lfBasic) and (Land[y-1,x+1] = lfBasic) and (Land[y-2,x] = lfBasic)) or
-                    ((Land[y+1, x] = lfBasic) and (Land[y+1,x+1] = lfBasic) and (Land[y,x+2] = lfBasic)) or
-                    ((Land[y-1, x] = lfBasic) and (Land[y-1,x+1] = lfBasic) and (Land[y,x+2] = lfBasic)) or
-                    ((Land[y+1, x] = lfBasic) and (Land[y+1,x-1] = lfBasic) and (Land[y,x-2] = lfBasic)) or
-                    ((Land[y-1, x] = lfBasic) and (Land[y-1,x-1] = lfBasic) and (Land[y,x-2] = lfBasic))) then
+            else if (LandGet(y, x) = 0) and
+                    (((LandGet(y, x-1) = lfBasic) and (LandGet(y+1,x-1) = lfBasic) and (LandGet(y+2,x) = lfBasic)) or
+                    ((LandGet(y, x-1) = lfBasic) and (LandGet(y-1,x-1) = lfBasic) and (LandGet(y-2,x) = lfBasic)) or
+                    ((LandGet(y, x+1) = lfBasic) and (LandGet(y+1,x+1) = lfBasic) and (LandGet(y+2,x) = lfBasic)) or
+                    ((LandGet(y, x+1) = lfBasic) and (LandGet(y-1,x+1) = lfBasic) and (LandGet(y-2,x) = lfBasic)) or
+                    ((LandGet(y+1, x) = lfBasic) and (LandGet(y+1,x+1) = lfBasic) and (LandGet(y,x+2) = lfBasic)) or
+                    ((LandGet(y-1, x) = lfBasic) and (LandGet(y-1,x+1) = lfBasic) and (LandGet(y,x+2) = lfBasic)) or
+                    ((LandGet(y+1, x) = lfBasic) and (LandGet(y+1,x-1) = lfBasic) and (LandGet(y,x-2) = lfBasic)) or
+                    ((LandGet(y-1, x) = lfBasic) and (LandGet(y-1,x-1) = lfBasic) and (LandGet(y,x-2) = lfBasic))) then
 
                 begin
 
@@ -478,22 +477,22 @@
 
                     begin
 
-                    if (Land[y, x-1] = lfBasic) and (LandPixels[y,x-1] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y, x-1]
+                    if (LandGet(y, x-1) = lfBasic) and (LandPixelGet(y,x-1) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y, x-1))
 
-                    else if (Land[y, x+1] = lfBasic) and (LandPixels[y,x+1] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y, x+1]
+                    else if (LandGet(y, x+1) = lfBasic) and (LandPixelGet(y,x+1) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y, x+1))
 
-                    else if (Land[y+1, x] = lfBasic) and (LandPixels[y+1,x] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y+1, x]
+                    else if (LandGet(y+1, x) = lfBasic) and (LandPixelGet(y+1,x) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y+1, x))
 
-                    else if (Land[y-1, x] = lfBasic) and (LandPixels[y-1,x] and AMask <> 0) then
-                        LandPixels[y, x]:= LandPixels[y-1, x];
+                    else if (LandGet(y-1, x) = lfBasic) and (LandPixelGet(y-1,x) and AMask <> 0) then
+                        LandPixelSet(y, x, LandPixelGet(y-1, x));
 
-                    if (((LandPixels[y,x] and AMask) shr AShift) > 10) then
-                        LandPixels[y,x]:= (LandPixels[y,x] and (not AMask)) or (64 shl AShift)
+                    if (((LandPixelGet(y,x) and AMask) shr AShift) > 10) then
+                        LandPixelSet(y, x, (LandPixelGet(y,x) and (not AMask)) or (64 shl AShift))
                     end;
-                Land[y,x]:= lfObject
+                LandSet(y, x, lfObject)
             end;
 
     AddProgress();
@@ -527,8 +526,8 @@
             begin
             if (y <= wbm) and ((x - w1) mod (bmWidth * 2) >= bmWidth) then
                 continue;
-            Land[y,x]:= lfBasic;
-            Land[y,lastX-x]:= lfBasic;
+            LandSet(y, x, lfBasic);
+            LandSet(y, lastX - x, lfBasic);
             end;
         end;
 
@@ -545,8 +544,8 @@
             // align battlement on inner edge, because real outer edge could be offscreen
             if (y <= wbm) and ((LAND_WIDTH + x - bmref) mod (bmWidth * 2) >= bmWidth) then
                 continue;
-            Land[y,x]:= lfBasic;
-            Land[y,lastX-x]:= lfBasic;
+            LandSet(y, x, lfBasic);
+            LandSet(y, lastX - x, lfBasic);
             end;
         end;
 end;
@@ -691,7 +690,7 @@
         for y:= 0 to Pred(tmpsurf^.h) do
             begin
             for x:= 0 to Pred(tmpsurf^.w) do
-                SetLand(Land[cpY + y, cpX + x], p^[x]);
+                SetLand(cpY + y, cpX + x, p^[x]);
             p:= PLongwordArray(@(p^[tmpsurf^.pitch div 4]));
             end;
 
@@ -756,16 +755,16 @@
     for x:= LongWord(leftX) to LongWord(rightX) do
         begin
         y:= Longword(cWaterLine) - 1 - w;
-        Land[y, x]:= lfIndestructible;
+        LandSet(y, x, lfIndestructible);
         if (x + y) mod 32 < 16 then
             c:= AMask
         else
             c:= AMask or RMask or GMask; // FF00FFFF
 
         if (cReducedQuality and rqBlurryLand) = 0 then
-            LandPixels[y, x]:= c
+            LandPixelSet(y, x, c)
         else
-            LandPixels[y div 2, x div 2]:= c
+            LandPixelSet(y div 2, x div 2, c)
         end
 end;
 
@@ -794,7 +793,7 @@
         begin
         WriteLnToConsole('Generating land...');
         case cMapGen of
-            mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]);
+            mgRandom: GenerateTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix);
             mgMaze  : begin ResizeLand(4096,2048); GenMaze; end;
             mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end;
             mgDrawn : GenDrawnMap;
@@ -802,7 +801,7 @@
         else
             OutError('Unknown mapgen', true);
         end;
-        if cMapGen <> mgForts then
+        if (cMapGen <> mgForts) then
             GenLandSurface
         end;
 
@@ -815,7 +814,7 @@
 else
     for y:= LongWord(topY) to LongWord(topY + 5) do
         for x:= LongWord(leftX) to LongWord(rightX) do
-            if Land[y, x] <> 0 then
+            if LandGet(y, x) <> 0 then
                 begin
                 inc(c);
                 if c > LongWord((LAND_WIDTH div 2)) then // avoid accidental triggering
@@ -834,13 +833,13 @@
         for y:= 0 to LAND_HEIGHT - 1 do
             for x:= 0 to LAND_WIDTH - 1 do
                 if (y < LongWord(topY)) or (x < LongWord(leftX)) or (x > LongWord(rightX)) then
-                    Land[y, x]:= lfIndestructible;
+                    LandSet(y, x, lfIndestructible);
         end
     else if topY > 0 then
         begin
         for y:= 0 to LongWord(topY - 1) do
             for x:= 0 to LAND_WIDTH - 1 do
-                Land[y, x]:= lfIndestructible;
+                LandSet(y, x, lfIndestructible);
         end;
     // Render map border
     for w:= 0 to (cBorderWidth-1) do
@@ -850,8 +849,8 @@
             for y:= LongWord(topY) to LAND_HEIGHT - 1 do
                     begin
                     // set land flags
-                    Land[y, leftX + w]:= lfIndestructible;
-                    Land[y, rightX - w]:= lfIndestructible;
+                    LandSet(y, leftX + w, lfIndestructible);
+                    LandSet(y, rightX - w, lfIndestructible);
 
                     // paint black and yellow stripes
                     if (y + leftX + w) mod 32 < 16 then
@@ -865,29 +864,29 @@
 
                     if (cReducedQuality and rqBlurryLand) = 0 then
                         begin
-                        LandPixels[y, leftX + w]:= c;
-                        LandPixels[y, rightX - w]:= c2;
+                        LandPixelSet(y, leftX + w, c);
+                        LandPixelSet(y, rightX - w, c2);
                         end
                     else
                         begin
-                        LandPixels[y div 2, (leftX + w) div 2]:= c;
-                        LandPixels[y div 2, (rightX - w) div 2]:= c2;
+                        LandPixelSet(y div 2, (leftX + w) div 2, c);
+                        LandPixelSet(y div 2, (rightX - w) div 2, c2);
                         end;
                     end;
 
         // Top border
         for x:= LongWord(leftX) to LongWord(rightX) do
             begin
-            Land[topY + w, x]:= lfIndestructible;
+            LandSet(topY + w, x, lfIndestructible);
             if (topY + x + w) mod 32 < 16 then
                 c:= AMask // black
             else
                 c:= AMask or RMask or GMask; // yellow
 
             if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[topY + w, x]:= c
+                LandPixelSet(topY + w, x, c)
             else
-                LandPixels[(topY + w) div 2, x div 2]:= c;
+                LandPixelSet((topY + w) div 2, x div 2, c);
             end;
         end;
     end;
@@ -915,23 +914,23 @@
         for x:= LongWord(leftX) to LongWord(rightX) do
             for y:= LongWord(topY) to LAND_HEIGHT-1 do
                 begin
-                w:= LandPixels[y,x];
+                w:= LandPixelGet(y,x);
                 w:= round(((w shr RShift and $FF) * RGB_LUMINANCE_RED +
                       (w shr BShift and $FF) * RGB_LUMINANCE_GREEN +
                       (w shr GShift and $FF) * RGB_LUMINANCE_BLUE));
                 if w > 255 then
                     w:= 255;
-                w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixels[y,x] and AMask);
-                LandPixels[y,x]:= w or (LandPixels[y, x] and AMask)
+                w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixelGet(y,x) and AMask);
+                LandPixelSet(y, x, w or (LandPixelGet(y, x) and AMask))
                 end
     else
         for x:= LongWord(leftX div 2) to LongWord(rightX div 2) do
             for y:= LongWord(topY div 2) to LAND_HEIGHT-1 div 2 do
                 begin
-                w:= LandPixels[y div 2,x div 2];
+                w:= LandPixelGet(y div 2,x div 2);
                 w:= ((w shr RShift and $FF) +  (w shr BShift and $FF) + (w shr GShift and $FF)) div 3;
-                w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixels[y div 2,x div 2] and AMask);
-                LandPixels[y,x]:= w or (LandPixels[y div 2, x div 2] and AMask)
+                w:= (w and $FF shl RShift) or (w and $FF shl BShift) or (w and $FF shl GShift) or (LandPixelGet(y div 2,x div 2) and AMask);
+                LandPixelSet(y, x, w or (LandPixelGet(y div 2, x div 2) and AMask))
                 end
     end;
 
@@ -949,7 +948,7 @@
 begin
     WriteLnToConsole('Generating preview...');
     case cMapGen of
-        mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]);
+        mgRandom: GenerateTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix);
         mgMaze: begin ResizeLand(4096,2048); GenMaze; end;
         mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end;
         mgDrawn: begin GenDrawnMap; end;
@@ -994,7 +993,7 @@
                 for yy:= y * lh to y * lh + 7 do
                     for xx:= x * lw + cbit to x * lw + cbit + 7 do
                         if ((yy-oy) and LAND_HEIGHT_MASK = 0) and ((xx-ox) and LAND_WIDTH_MASK = 0)
-                           and (Land[yy-oy, xx-ox] <> 0) then
+                           and (LandGet(yy-oy, xx-ox) <> 0) then
                             inc(t);
                 if t > 8 then
                     Preview[y, x]:= Preview[y, x] or ($80 shr bit);
@@ -1008,7 +1007,7 @@
 begin
     WriteLnToConsole('Generating preview...');
     case cMapGen of
-        mgRandom: GenTemplated(EdgeTemplates[SelectTemplate]);
+        mgRandom: GenerateTemplatedLand(cFeatureSize, cSeed, SelectTemplate, PathPrefix);
         mgMaze: begin ResizeLand(4096,2048); GenMaze; end;
         mgPerlin: begin ResizeLand(4096,2048); GenPerlin; end;
         mgDrawn: begin GenDrawnMap; end;
@@ -1052,7 +1051,7 @@
             for yy:= y * lh - oy to y * lh + lh - 1 - oy do
                 for xx:= x * lw - ox to x * lw + lw - 1 - ox do
                     if (yy and LAND_HEIGHT_MASK = 0) and (xx and LAND_WIDTH_MASK = 0)
-                        and (Land[yy, xx] <> 0) then
+                        and (LandGet(yy, xx) <> 0) then
                         inc(t);
 
             Preview[y, x]:= t * 255 div (lh * lw);
@@ -1074,7 +1073,7 @@
 begin
     landPixelDigest:= 1;
     for i:= 0 to LAND_HEIGHT-1 do
-        landPixelDigest:= Adler32Update(landPixelDigest, @Land[i,0], LAND_WIDTH*2);
+        landPixelDigest:= Adler32Update(landPixelDigest, LandRow(i), LAND_WIDTH*2);
     s:= 'M' + IntToStr(syncedPixelDigest)+'|'+IntToStr(landPixelDigest);
 
     ScriptSetString('LandDigest',IntToStr(landPixelDigest));
@@ -1093,21 +1092,11 @@
     maskOnly:= false;
     LAND_WIDTH:= 0;
     LAND_HEIGHT:= 0;
-(*
-    if (cReducedQuality and rqBlurryLand) = 0 then
-        SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH)
-    else
-        SetLength(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2);
-
-    SetLength(Land, LAND_HEIGHT, LAND_WIDTH);
-    SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32));
-*)
 end;
 
 procedure freeModule;
 begin
-    SetLength(Land, 0, 0);
-    SetLength(LandPixels, 0, 0);
+    DisposeLand;
     SetLength(LandDirty, 0, 0);
 end;
 
--- a/hedgewars/uLandGenMaze.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandGenMaze.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -8,7 +8,8 @@
 
 implementation
 
-uses uRandom, uLandOutline, uLandTemplates, uVariables, uFloat, uConsts, uLandGenTemplateBased, uUtils;
+uses uRandom, uLandOutline, uLandTemplates, uVariables, uFloat, uConsts,
+     uLandGenTemplateBased, uUtils, uLandUtils;
 
 type direction = record x, y: LongInt; end;
 const DIR_N: direction = (x: 0; y: -1);
@@ -403,11 +404,11 @@
 
 for x := 0 to playWidth do
     for y := 0 to off_y - 1 do
-        Land[y, x] := 0;
+        LandSet(y, x, 0);
 
 for x := 0 to playWidth do
     for y := off_y to LAND_HEIGHT - 1 do
-        Land[y, x] := lfBasic;
+        LandSet(y, x, lfBasic);
 
 for y := 0 to num_cells_y - 1 do
     for x := 0 to num_cells_x - 1 do
@@ -527,9 +528,9 @@
 else
     begin
     x := 0;
-    while Land[cellsize div 2 + cellsize + off_y, x] = lfBasic do
+    while LandGet(cellsize div 2 + cellsize + off_y, x) = lfBasic do
         x := x + 1;
-    while Land[cellsize div 2 + cellsize + off_y, x] = 0 do
+    while LandGet(cellsize div 2 + cellsize + off_y, x) = 0 do
         x := x + 1;
     FillLand(x+1, cellsize div 2 + cellsize + off_y, 0, 0);
     end;
--- a/hedgewars/uLandGenPerlin.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandGenPerlin.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -11,6 +11,7 @@
     , uRandom
     , uLandOutline // FillLand
     , uUtils
+    , uLandUtils
     ;
 
 var p: array[0..511] of LongInt;
@@ -39,7 +40,7 @@
 4086, 4088, 4089, 4091, 4092, 4092, 4093, 4094, 4094, 4095, 4095,
 4095, 4095, 4095, 4095, 4095);
 
-function fade(t: LongInt) : LongInt; inline;
+function fade(t: LongInt) : LongInt;
 var t0, t1: LongInt;
 begin
     t0:= fadear[t shr 8];
@@ -53,13 +54,13 @@
 end;
 
 
-function lerp(t, a, b: LongInt) : LongInt; inline;
+function lerp(t, a, b: LongInt) : LongInt;
 begin
     lerp:= a + ((Int64(b) - a) * t shr 12)
 end;
 
 
-function grad(hash, x, y: LongInt) : LongInt; inline;
+function grad(hash, x, y: LongInt) : LongInt;
 var h, v, u: LongInt;
 begin
     h:= hash and 15;
@@ -74,7 +75,7 @@
 end;
 
 
-function inoise(x, y: LongInt) : LongInt; inline;
+function inoise(x, y: LongInt) : LongInt;
 const N = $10000;
 var xx, yy, u, v, A, AA, AB, B, BA, BB: LongInt;
 begin
@@ -205,24 +206,24 @@
             }
 
             if r < rCutoff then
-                Land[y, x]:= 0
+                LandSet(y, x, 0)
             else if param1 = 0 then
-                Land[y, x]:= lfObjMask
+                LandSet(y, x, lfObjMask)
             else
-                Land[y, x]:= lfBasic
+                LandSet(y, x, lfBasic)
         end;
     end;
 
     if param1 = 0 then
         begin
         for x:= 0 to width do
-            if Land[height - 1, x] = lfObjMask then FillLand(x, height - 1, 0, lfBasic);
+            if LandGet(height - 1, x) = lfObjMask then FillLand(x, height - 1, 0, lfBasic);
 
         // strip all lfObjMask pixels
         for y:= minY to LAND_HEIGHT - 1 do
             for x:= 0 to LAND_WIDTH - 1 do
-                if Land[y, x] = lfObjMask then
-                    Land[y, x]:= 0;
+                if LandGet(y, x) = lfObjMask then
+                    LandSet(y, x, 0);
         end;
 
     playWidth:= width;
--- a/hedgewars/uLandGenTemplateBased.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandGenTemplateBased.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -330,7 +330,7 @@
     ResizeLand(Template.TemplateWidth, Template.TemplateHeight);
     for y:= 0 to LAND_HEIGHT - 1 do
         for x:= 0 to LAND_WIDTH - 1 do
-            Land[y, x]:= lfBasic;
+            LandSet(y, x, lfBasic);
 
     minDistance:= sqr(cFeatureSize) div 8 + 10;
     //dabDiv:= getRandom(41)+60;
@@ -368,13 +368,13 @@
         for y:= 0 to LAND_HEIGHT - 1 do
             for x:= 0 to LAND_WIDTH - 1 do
                 if (y < LongWord(topY)) or (x < LongWord(leftX)) or (x > LongWord(rightX)) then
-                    Land[y, x]:= 0
+                    LandSet(y, x, 0)
                 else
                     begin
-                    if Land[y, x] = 0 then
-                        Land[y, x]:= lfBasic
-                    else if Land[y, x] = lfBasic then
-                        Land[y, x]:= 0;
+                    if LandGet(y, x) = 0 then
+                        LandSet(y, x, lfBasic)
+                    else if LandGet(y, x) = lfBasic then
+                        LandSet(y, x, 0);
                     end;
         end;
 end;
--- a/hedgewars/uLandGraphics.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandGraphics.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -47,19 +47,19 @@
 function  DrawThickLine(X1, Y1, X2, Y2, radius: LongInt; color: Longword): Longword;
 procedure DumpLandToLog(x, y, r: LongInt);
 procedure DrawIceBreak(x, y, iceRadius, iceHeight: Longint);
-function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean; inline;
-function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean; inline;
-function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean; inline;
+function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean;
+function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean;
+function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean;
 function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, outOfMap, force, behind, flipHoriz, flipVert: boolean; LandFlags: Word; Tint: LongWord): boolean;
 procedure EraseLandRectRaw(X, Y, width, height: LongWord);
 procedure EraseLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; eraseOnLFMatch, onlyEraseLF, flipHoriz, flipVert: boolean);
 function GetPlaceCollisionTex(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt): PTexture;
 
 implementation
-uses SDLh, uLandTexture, uTextures, uVariables, uUtils, uDebug, uScript;
+uses SDLh, uLandTexture, uTextures, uVariables, uUtils, uDebug, uScript, uLandUtils;
 
 
-procedure calculatePixelsCoordinates(landX, landY: Longint; var pixelX, pixelY: Longint); inline;
+procedure calculatePixelsCoordinates(landX, landY: Longint; var pixelX, pixelY: Longint);
 begin
 if (cReducedQuality and rqBlurryLand) = 0 then
     begin
@@ -73,33 +73,33 @@
     end;
 end;
 
-function drawPixelBG(landX, landY, pixelX, pixelY: Longint): Longword; inline;
+function drawPixelBG(landX, landY, pixelX, pixelY: Longint): Longword;
 begin
 drawPixelBG := 0;
-if (Land[LandY, landX] and lfIndestructible) = 0 then
+if (LandGet(LandY, landX) and lfIndestructible) = 0 then
     begin
-        if ((Land[landY, landX] and lfBasic) <> 0) and (((LandPixels[pixelY, pixelX] and AMask) shr AShift) = 255) and (not disableLandBack) then
+        if ((LandGet(landY, landX) and lfBasic) <> 0) and (((LandPixelGet(pixelY, pixelX) and AMask) shr AShift) = 255) and (not disableLandBack) then
         begin
-            LandPixels[pixelY, pixelX]:= LandBackPixel(landX, landY);
+            LandPixelSet(pixelY, pixelX, LandBackPixel(landX, landY));
             inc(drawPixelBG);
         end
-        else if ((Land[landY, landX] and lfObject) <> 0) or (((LandPixels[pixelY, pixelX] and AMask) shr AShift) < 255) then
-            LandPixels[pixelY, pixelX]:= ExplosionBorderColorNoA
+        else if ((LandGet(landY, landX) and lfObject) <> 0) or (((LandPixelGet(pixelY, pixelX) and AMask) shr AShift) < 255) then
+            LandPixelSet(pixelY, pixelX, ExplosionBorderColorNoA)
     end;
 end;
 
-procedure drawPixelEBC(landX, landY, pixelX, pixelY: Longint); inline;
+procedure drawPixelEBC(landX, landY, pixelX, pixelY: Longint);
 begin
-if (Land[landY, landX] and lfIndestructible = 0) and 
-    (((Land[landY, landX] and lfBasic) <> 0) or ((Land[landY, landX] and lfObject) <> 0)) then
+if (LandGet(landY, landX) and lfIndestructible = 0) and
+    (((LandGet(landY, landX) and lfBasic) <> 0) or ((LandGet(landY, landX) and lfObject) <> 0)) then
     begin
-    LandPixels[pixelY, pixelX]:= ExplosionBorderColor;
-    Land[landY, landX]:= (Land[landY, landX] or lfDamaged) and (not lfIce);
+    LandPixelSet(pixelY, pixelX, ExplosionBorderColor);
+    LandSet(landY, landX, (LandGet(landY, landX) or lfDamaged) and (not lfIce));
     LandDirty[landY div 32, landX div 32]:= 1;
     end;
 end;
 
-function isLandscapeEdge(weight:Longint):boolean; inline;
+function isLandscapeEdge(weight:Longint):boolean;
 begin
 isLandscapeEdge := (weight < 8) and (weight >= 2);
 end;
@@ -118,7 +118,7 @@
        (j > LAND_HEIGHT -1) then
        exit(9);
 
-    if Land[j, i] and lfLandMask and (not lfIce) = 0 then
+    if LandGet(j, i) and lfLandMask and (not lfIce) = 0 then
        inc(r)
     end;
 
@@ -126,7 +126,7 @@
 end;
 
 
-procedure fillPixelFromIceSprite(pixelX, pixelY:Longint); inline;
+procedure fillPixelFromIceSprite(pixelX, pixelY:Longint);
 var
     iceSurface: PSDL_Surface;
     icePixels: PLongwordArray;
@@ -136,7 +136,7 @@
     // So. 3 parameters here. Ice colour, Ice opacity, and a bias on the greyscaled pixel towards lightness
     iceSurface:= SpritesData[sprIceTexture].Surface;
     icePixels := iceSurface^.pixels;
-    w:= LandPixels[pixelY, pixelX];
+    w:= LandPixelGet(pixelY, pixelX);
     if w > 0 then
         begin
         w:= round(((w shr RShift and $FF) * RGB_LUMINANCE_RED +
@@ -144,37 +144,37 @@
               (w shr GShift and $FF) * RGB_LUMINANCE_BLUE));
         if w < 128 then w:= w+128;
         if w > 255 then w:= 255;
-        w:= (w shl RShift) or (w shl BShift) or (w shl GShift) or (LandPixels[pixelY, pixelX] and AMask);
-        LandPixels[pixelY, pixelX]:= addBgColor(w, IceColor);
-        LandPixels[pixelY, pixelX]:= addBgColor(LandPixels[pixelY, pixelX], icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)])
+        w:= (w shl RShift) or (w shl BShift) or (w shl GShift) or (LandPixelGet(pixelY, pixelX) and AMask);
+        LandPixelSet(pixelY, pixelX, addBgColor(w, IceColor));
+        LandPixelSet(pixelY, pixelX, addBgColor(LandPixelGet(pixelY, pixelX), icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)]))
         end
     else
         begin
-        LandPixels[pixelY, pixelX]:= IceColor and (not AMask) or $E8 shl AShift;
-        LandPixels[pixelY, pixelX]:= addBgColor(LandPixels[pixelY, pixelX], icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)]);
+        LandPixelSet(pixelY, pixelX, IceColor and (not AMask) or $E8 shl AShift);
+        LandPixelSet(pixelY, pixelX, addBgColor(LandPixelGet(pixelY, pixelX), icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)]));
         // silly workaround to avoid having to make background erasure a tadb it smarter about sea ice
-        if LandPixels[pixelY, pixelX] and AMask shr AShift = 255 then
-            LandPixels[pixelY, pixelX]:= LandPixels[pixelY, pixelX] and (not AMask) or 254 shl AShift;
+        if LandPixelGet(pixelY, pixelX) and AMask shr AShift = 255 then
+            LandPixelSet(pixelY, pixelX, LandPixelGet(pixelY, pixelX) and (not AMask) or 254 shl AShift);
         end;
 end;
 
 
-procedure DrawPixelIce(landX, landY, pixelX, pixelY: Longint); inline;
+procedure DrawPixelIce(landX, landY, pixelX, pixelY: Longint);
 begin
-if ((Land[landY, landX] and lfIce) <> 0) then exit;
+if ((LandGet(landY, landX) and lfIce) <> 0) then exit;
 if (pixelX < LeftX) or (pixelX > RightX) or (pixelY < TopY) then exit;
 if isLandscapeEdge(getPixelWeight(landX, landY)) then
     begin
-    if (LandPixels[pixelY, pixelX] and AMask < 255) and (LandPixels[pixelY, pixelX] and AMask > 0) then
-        LandPixels[pixelY, pixelX] := (IceEdgeColor and (not AMask)) or (LandPixels[pixelY, pixelX] and AMask)
-    else if (LandPixels[pixelY, pixelX] and AMask < 255) or (Land[landY, landX] > 255) then
-        LandPixels[pixelY, pixelX] := IceEdgeColor
+    if (LandPixelGet(pixelY, pixelX) and AMask < 255) and (LandPixelGet(pixelY, pixelX) and AMask > 0) then
+        LandPixelSet(pixelY, pixelX, (IceEdgeColor and (not AMask)) or (LandPixelGet(pixelY, pixelX) and AMask))
+    else if (LandPixelGet(pixelY, pixelX) and AMask < 255) or (LandGet(landY, landX) > 255) then
+        LandPixelSet(pixelY, pixelX, IceEdgeColor)
     end
-else if Land[landY, landX] > 255 then
+else if LandGet(landY, landX) > 255 then
     begin
         fillPixelFromIceSprite(pixelX, pixelY);
     end;
-if Land[landY, landX] > 255 then Land[landY, landX] := Land[landY, landX] or lfIce and (not lfDamaged);
+if LandGet(landY, landX) > 255 then LandSet(landY, landX, LandGet(landY, landX) or lfIce and (not lfDamaged));
 end;
 
 
@@ -202,8 +202,8 @@
         for i:= fromPix to toPix do
             begin
             calculatePixelsCoordinates(i, y, px, py);
-            if ((Land[y, i] and lfIndestructible) = 0) and (not disableLandBack or (Land[y, i] > 255))  then
-                LandPixels[py, px]:= ExplosionBorderColorNoA;
+            if ((LandGet(y, i) and lfIndestructible) = 0) and (not disableLandBack or (LandGet(y, i) > 255))  then
+                LandPixelSet(py, px, ExplosionBorderColorNoA);
             end;
     icePixel:
         for i:= fromPix to toPix do
@@ -214,41 +214,41 @@
     addNotHHObj:
         for i:= fromPix to toPix do
             begin
-            if Land[y, i] and lfNotHHObjMask shr lfNotHHObjShift < lfNotHHObjSize then
-                Land[y, i]:= (Land[y, i] and (not lfNotHHObjMask)) or ((Land[y, i] and lfNotHHObjMask shr lfNotHHObjShift + 1) shl lfNotHHObjShift);
+            if LandGet(y, i) and lfNotHHObjMask shr lfNotHHObjShift < lfNotHHObjSize then
+                LandSet(y, i, (LandGet(y, i) and (not lfNotHHObjMask)) or ((LandGet(y, i) and lfNotHHObjMask shr lfNotHHObjShift + 1) shl lfNotHHObjShift));
             end;
     removeNotHHObj:
         for i:= fromPix to toPix do
             begin
-            if Land[y, i] and lfNotHHObjMask <> 0 then
-                Land[y, i]:= (Land[y, i] and (not lfNotHHObjMask)) or ((Land[y, i] and lfNotHHObjMask shr lfNotHHObjShift - 1) shl lfNotHHObjShift);
+            if LandGet(y, i) and lfNotHHObjMask <> 0 then
+                LandSet(y, i, (LandGet(y, i) and (not lfNotHHObjMask)) or ((LandGet(y, i) and lfNotHHObjMask shr lfNotHHObjShift - 1) shl lfNotHHObjShift));
             end;
     addHH:
         for i:= fromPix to toPix do
             begin
-            if Land[y, i] and lfHHMask < lfHHMask then
-                Land[y, i]:= Land[y, i] + 1
+            if LandGet(y, i) and lfHHMask < lfHHMask then
+                LandSet(y, i, LandGet(y, i) + 1)
             end;
     removeHH:
         for i:= fromPix to toPix do
             begin
-            if Land[y, i] and lfHHMask > 0 then
-                Land[y, i]:= Land[y, i] - 1;
+            if LandGet(y, i) and lfHHMask > 0 then
+                LandSet(y, i, LandGet(y, i) - 1);
             end;
     setCurrentHog:
         for i:= fromPix to toPix do
             begin
-            Land[y, i]:= Land[y, i] or lfCurHogCrate
+            LandSet(y, i, LandGet(y, i) or lfCurHogCrate)
             end;
     removeCurrentHog:
         for i:= fromPix to toPix do
             begin
-            Land[y, i]:= Land[y, i] and lfNotCurHogCrate;
+            LandSet(y, i, LandGet(y, i) and lfNotCurHogCrate);
             end;
     end;
 end;
 
-function FillLandCircleSegmentFT(x, y, dx, dy: LongInt; fill : fillType): Longword; inline;
+function FillLandCircleSegmentFT(x, y, dx, dy: LongInt; fill : fillType): Longword;
 begin
     FillLandCircleSegmentFT := 0;
 if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
@@ -261,7 +261,7 @@
     inc(FillLandCircleSegmentFT, FillLandCircleLineFT(y - dx, Max(x - dy, 0), Min(x + dy, LAND_WIDTH - 1), fill));
 end;
 
-function FillRoundInLandFT(X, Y, Radius: LongInt; fill: fillType): Longword; inline;
+function FillRoundInLandFT(X, Y, Radius: LongInt; fill: fillType): Longword;
 var dx, dy, d: LongInt;
 begin
 dx:= 0;
@@ -323,31 +323,31 @@
 
     if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if (Land[y + dy, i] and lfIndestructible) = 0 then
+            if (LandGet(y + dy, i) and lfIndestructible) = 0 then
             begin
-                if Land[y + dy, i] <> Value then inc(FillCircleLines);
-                Land[y + dy, i]:= Value;
+                if LandGet(y + dy, i) <> Value then inc(FillCircleLines);
+                LandSet(y + dy, i, Value);
             end;
     if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if (Land[y - dy, i] and lfIndestructible) = 0 then
+            if (LandGet(y - dy, i) and lfIndestructible) = 0 then
             begin
-                if Land[y - dy, i] <> Value then inc(FillCircleLines);
-                Land[y - dy, i]:= Value;
+                if LandGet(y - dy, i) <> Value then inc(FillCircleLines);
+                LandSet(y - dy, i, Value);
             end;
     if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if (Land[y + dx, i] and lfIndestructible) = 0 then
+            if (LandGet(y + dx, i) and lfIndestructible) = 0 then
             begin
-                if Land[y + dx, i] <> Value then inc(FillCircleLines);
-                Land[y + dx, i]:= Value;
+                if LandGet(y + dx, i) <> Value then inc(FillCircleLines);
+                LandSet(y + dx, i, Value);
             end;
     if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
         for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if (Land[y - dx, i] and lfIndestructible) = 0 then
+            if (LandGet(y - dx, i) and lfIndestructible) = 0 then
             begin
-                if Land[y - dx, i] <> Value then inc(FillCircleLines);
-                Land[y - dx, i]:= Value;
+                if LandGet(y - dx, i) <> Value then inc(FillCircleLines);
+                LandSet(y - dx, i, Value);
             end;
 end;
 
@@ -435,9 +435,9 @@
     begin
     for j := iceT to iceB do
         begin
-        if Land[j, i] = 0 then
+        if LandGet(j, i) = 0 then
             begin
-            Land[j, i] := lfIce;
+            LandSet(j, i, lfIce);
             if (cReducedQuality and rqBlurryLand) = 0 then
                 fillPixelFromIceSprite(i, j)
             else
@@ -478,7 +478,7 @@
     for ty:= Max(y - Radius, 0) to Min(y + Radius, TopY) do
         for tx:= Max(LeftX, ar^[i].Left - Radius) to Min(RightX, ar^[i].Right + Radius) do
             begin
-            if (Land[ty, tx] and lfIndestructible) = 0 then
+            if (LandGet(ty, tx) and lfIndestructible) = 0 then
                 begin
                 if (cReducedQuality and rqBlurryLand) = 0 then
                     begin
@@ -488,10 +488,10 @@
                     begin
                     by:= ty div 2; bx:= tx div 2;
                     end;
-                if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
-                    LandPixels[by, bx]:= LandBackPixel(tx, ty)
-                else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
-                    LandPixels[by, bx]:= LandPixels[by, bx] and (not AMASK)
+                if ((LandGet(ty, tx) and lfBasic) <> 0) and (((LandPixelGet(by,bx) and AMask) shr AShift) = 255) and (not disableLandBack) then
+                    LandPixelSet(by, bx, LandBackPixel(tx, ty))
+                else if ((LandGet(ty, tx) and lfObject) <> 0) or (((LandPixelGet(by,bx) and AMask) shr AShift) < 255) then
+                    LandPixelSet(by, bx, LandPixelGet(by, bx) and (not AMASK))
                 end
             end;
     inc(y, dY)
@@ -504,14 +504,14 @@
     begin
     for ty:= Max(y - Radius, 0) to Min(y + Radius, TopY) do
         for tx:= Max(LeftX, ar^[i].Left - Radius) to Min(RightX, ar^[i].Right + Radius) do
-            if ((Land[ty, tx] and lfBasic) <> 0) or ((Land[ty, tx] and lfObject) <> 0) then
+            if ((LandGet(ty, tx) and lfBasic) <> 0) or ((LandGet(ty, tx) and lfObject) <> 0) then
                 begin
                  if (cReducedQuality and rqBlurryLand) = 0 then
-                    LandPixels[ty, tx]:= ExplosionBorderColor
+                    LandPixelSet(ty, tx, ExplosionBorderColor)
                 else
-                    LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor;
+                    LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor);
 
-                Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and (not lfIce);
+                LandSet(ty, tx, (LandGet(ty, tx) or lfDamaged) and (not lfIce));
                 LandDirty[ty div 32, tx div 32]:= 1;
                 end;
     inc(y, dY)
@@ -533,16 +533,16 @@
     Y:= Y + dY;
     tx:= hwRound(X);
     ty:= hwRound(Y);
-    if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0)
-    or ((Land[ty, tx] and lfObject) <> 0)) then
+    if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((LandGet(ty, tx) and lfBasic) <> 0)
+    or ((LandGet(ty, tx) and lfObject) <> 0)) then
         begin
-        Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and (not lfIce);
+        LandSet(ty, tx, (LandGet(ty, tx) or lfDamaged) and (not lfIce));
         if despeckle then
             LandDirty[ty div 32, tx div 32]:= 1;
         if (cReducedQuality and rqBlurryLand) = 0 then
-            LandPixels[ty, tx]:= ExplosionBorderColor
+            LandPixelSet(ty, tx, ExplosionBorderColor)
         else
-            LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor
+            LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor)
         end
     end;
 end;
@@ -581,18 +581,18 @@
     ty:= hwRound(Y);
     if ((ty and LAND_HEIGHT_MASK) = 0)
     and ((tx and LAND_WIDTH_MASK) = 0)
-    and (((Land[ty, tx] and lfBasic) <> 0) or ((Land[ty, tx] and lfObject) <> 0)) then
+    and (((LandGet(ty, tx) and lfBasic) <> 0) or ((LandGet(ty, tx) and lfObject) <> 0)) then
         begin
-        Land[ty, tx]:= Land[ty, tx] and (not lfIce);
+        LandSet(ty, tx, LandGet(ty, tx) and (not lfIce));
         if despeckle then
             begin
-            Land[ty, tx]:= Land[ty, tx] or lfDamaged;
+            LandSet(ty, tx, LandGet(ty, tx) or lfDamaged);
             LandDirty[ty div 32, tx div 32]:= 1
             end;
         if (cReducedQuality and rqBlurryLand) = 0 then
-            LandPixels[ty, tx]:= ExplosionBorderColor
+            LandPixelSet(ty, tx, ExplosionBorderColor)
         else
-            LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor
+            LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor)
         end
     end;
     nx:= nx - dY;
@@ -612,7 +612,7 @@
         Y:= Y + dY;
         tx:= hwRound(X);
         ty:= hwRound(Y);
-        if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and ((Land[ty, tx] and lfIndestructible) = 0) then
+        if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and ((LandGet(ty, tx) and lfIndestructible) = 0) then
             begin
             if (cReducedQuality and rqBlurryLand) = 0 then
                 begin
@@ -622,11 +622,11 @@
                 begin
                 by:= ty div 2; bx:= tx div 2;
                 end;
-            if ((Land[ty, tx] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
-                LandPixels[by, bx]:= LandBackPixel(tx, ty)
-            else if ((Land[ty, tx] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
-                LandPixels[by, bx]:= LandPixels[by, bx] and (not AMASK);
-            Land[ty, tx]:= 0;
+            if ((LandGet(ty, tx) and lfBasic) <> 0) and (((LandPixelGet(by,bx) and AMask) shr AShift) = 255) and (not disableLandBack) then
+                LandPixelSet(by, bx, LandBackPixel(tx, ty))
+            else if ((LandGet(ty, tx) and lfObject) <> 0) or (((LandPixelGet(by,bx) and AMask) shr AShift) < 255) then
+                LandPixelSet(by, bx, LandPixelGet(by, bx) and (not AMASK));
+            LandSet(ty, tx, 0);
             end
         end;
     DrawExplosionBorder(X, Y, dx, dy, despeckle);
@@ -644,16 +644,16 @@
     Y:= Y + dY;
     tx:= hwRound(X);
     ty:= hwRound(Y);
-    if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0)
-    or ((Land[ty, tx] and lfObject) <> 0)) then
+    if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((LandGet(ty, tx) and lfBasic) <> 0)
+    or ((LandGet(ty, tx) and lfObject) <> 0)) then
         begin
-        Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and (not lfIce);
+        LandSet(ty, tx, (LandGet(ty, tx) or lfDamaged) and (not lfIce));
         if despeckle then
             LandDirty[ty div 32, tx div 32]:= 1;
         if (cReducedQuality and rqBlurryLand) = 0 then
-            LandPixels[ty, tx]:= ExplosionBorderColor
+            LandPixelSet(ty, tx, ExplosionBorderColor)
         else
-            LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor
+            LandPixelSet(ty div 2, tx div 2, ExplosionBorderColor)
         end
     end;
     nx:= nx - dY;
@@ -692,7 +692,7 @@
     end;
 end;
 
-function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean; inline;
+function TryPlaceOnLandSimple(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace, indestructible: boolean): boolean;
 var lf: Word;
 begin
 if indestructible then
@@ -702,12 +702,12 @@
 TryPlaceOnLandSimple:= TryPlaceOnLand(cpX, cpY, Obj, Frame, doPlace, false, false, false, false, false, lf, $FFFFFFFF);
 end;
 
-function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean; inline;
+function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; LandFlags: Word): boolean;
 begin
 TryPlaceOnLand:= TryPlaceOnLand(cpX, cpY, Obj, Frame, doPlace, false, false, false, false, false, LandFlags, $FFFFFFFF);
 end;
 
-function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean; inline;
+function ForcePlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; LandFlags: Word; Tint: LongWord; Behind, flipHoriz, flipVert: boolean): boolean;
 begin
     ForcePlaceOnLand:= TryPlaceOnLand(cpX, cpY, Obj, Frame, true, false, true, behind, flipHoriz, flipVert, LandFlags, Tint)
 end;
@@ -752,12 +752,12 @@
                 if (outOfMap and
                    ((cpY + y) < LAND_HEIGHT) and ((cpY + y) >= 0) and
                    ((cpX + x) < LAND_WIDTH) and ((cpX + x) >= 0) and
-                   ((not force) and (Land[cpY + y, cpX + x] <> 0))) or
+                   ((not force) and (LandGet(cpY + y, cpX + x) <> 0))) or
 
                    (not outOfMap and
                        (((cpY + y) <= topY) or ((cpY + y) >= LAND_HEIGHT) or
                        ((cpX + x) <= leftX) or ((cpX + x) >= rightX) or
-                       ((not force) and (Land[cpY + y, cpX + x] <> 0)))) then
+                       ((not force) and (LandGet(cpY + y, cpX + x) <> 0)))) then
                    begin
                    if SDL_MustLock(Image) then
                        SDL_UnlockSurface(Image);
@@ -793,28 +793,28 @@
                     gX:= (cpX + x) div 2;
                     gY:= (cpY + y) div 2;
                     end;
-                if (not behind) or (Land[cpY + y, cpX + x] and lfLandMask = 0) then
+                if (not behind) or (LandGet(cpY + y, cpX + x) and lfLandMask = 0) then
                     begin
-                    if (LandFlags and lfBasic <> 0) or 
-                       ((LandPixels[gY, gX] and AMask shr AShift > 128) and  // This test assumes lfBasic and lfObject differ only graphically
+                    if (LandFlags and lfBasic <> 0) or
+                       ((LandPixelGet(gY, gX) and AMask shr AShift > 128) and  // This test assumes lfBasic and lfObject differ only graphically
                          (LandFlags and (lfObject or lfIce) = 0)) then
-                         Land[cpY + y, cpX + x]:= lfBasic or LandFlags
+                         LandSet(cpY + y, cpX + x, lfBasic or LandFlags)
                     else if (LandFlags and lfIce = 0) then
-						 Land[cpY + y, cpX + x]:= lfObject or LandFlags
-					else Land[cpY + y, cpX + x]:= LandFlags
+						 LandSet(cpY + y, cpX + x, lfObject or LandFlags)
+					else LandSet(cpY + y, cpX + x, LandFlags)
                     end;
-                if (not behind) or (LandPixels[gY, gX] = 0) then
+                if (not behind) or (LandPixelGet(gY, gX) = 0) then
                     begin
                     if tint = $FFFFFFFF then
-                        LandPixels[gY, gX]:= PLongword(@(p^[x * 4]))^
-                    else 
+                        LandPixelSet(gY, gX, PLongword(@(p^[x * 4]))^)
+                    else
                         begin
                         pixel:= PLongword(@(p^[x * 4]))^;
-                        LandPixels[gY, gX]:= 
+                        LandPixelSet(gY, gX,
                            ceil((pixel shr RShift and $FF) * ((tint shr 24) / 255)) shl RShift or
                            ceil((pixel shr GShift and $FF) * ((tint shr 16 and $ff) / 255)) shl GShift or
                            ceil((pixel shr BShift and $FF) * ((tint shr  8 and $ff) / 255)) shl BShift or
-                           ceil((pixel shr AShift and $FF) * ((tint and $ff) / 255)) shl AShift;
+                           ceil((pixel shr AShift and $FF) * ((tint and $ff) / 255)) shl AShift);
                         end
                     end
                 end;
@@ -847,8 +847,8 @@
 for ty:= 0 to height - 1 do
     for tx:= 0 to width - 1 do
         begin
-        LandPixels[ty, tx]:= 0;
-        Land[Y + ty, X + tx]:= 0;
+        LandPixelSet(ty, tx, 0);
+        LandSet(Y + ty, X + tx, 0);
         end;
 end;
 
@@ -913,15 +913,15 @@
                     gX:= (cpX + x) div 2;
                     gY:= (cpY + y) div 2;
                     end;
-                if (not eraseOnLFMatch or (Land[cpY + y, cpX + x] and LandFlags <> 0)) and
+                if (not eraseOnLFMatch or (LandGet(cpY + y, cpX + x) and LandFlags <> 0)) and
                     ((PLongword(@(p^[x * 4]))^) and AMask <> 0) then
                     begin
                     if not onlyEraseLF then
                         begin
-                        LandPixels[gY, gX]:= 0;
-                        Land[cpY + y, cpX + x]:= 0
+                        LandPixelSet(gY, gX, 0);
+                        LandSet(cpY + y, cpX + x, 0)
                         end
-                    else Land[cpY + y, cpX + x]:= Land[cpY + y, cpX + x] and (not LandFlags)
+                    else LandSet(cpY + y, cpX + x, LandGet(cpY + y, cpX + x) and (not LandFlags))
                     end
                 end;
         p:= PByteArray(@(p^[Image^.pitch]));
@@ -990,7 +990,7 @@
     for x:= 0 to Pred(w) do
         if ((p^[x] and AMask) <> 0)
             and (((cpY + y) < topY) or ((cpY + y) >= LAND_HEIGHT) or
-            ((cpX + x) < leftX) or ((cpX + x) > rightX) or (Land[cpY + y, cpX + x] <> 0)) then
+            ((cpX + x) < leftX) or ((cpX + x) > rightX) or (LandGet(cpY + y, cpX + x) <> 0)) then
                 pt^[x]:= cWhiteColor
         else
             (pt^[x]):= cWhiteColor and (not AMask);
@@ -1028,8 +1028,8 @@
         yy:= Y div 2;
     end;
 
-    pixelsweep:= (Land[Y, X] <= lfAllObjMask) and ((LandPixels[yy, xx] and AMask) <> 0);
-    if (((Land[Y, X] and lfDamaged) <> 0) and ((Land[Y, X] and lfIndestructible) = 0)) or pixelsweep then
+    pixelsweep:= (LandGet(Y, X) <= lfAllObjMask) and ((LandPixelGet(yy, xx) and AMask) <> 0);
+    if (((LandGet(Y, X) and lfDamaged) <> 0) and ((LandGet(Y, X) and lfIndestructible) = 0)) or pixelsweep then
     begin
         c:= 0;
         for i:= -1 to 1 do
@@ -1047,27 +1047,27 @@
                                 ny:= Y div 2 + i;
                                 nx:= X div 2 + j;
                                 if ((ny and (LAND_HEIGHT_MASK div 2)) = 0) and ((nx and (LAND_WIDTH_MASK div 2)) = 0) then
-                                    if (LandPixels[ny, nx] and AMASK) <> 0 then
+                                    if (LandPixelGet(ny, nx) and AMASK) <> 0 then
                                         inc(c);
                             end
-                            else if (LandPixels[ny, nx] and AMASK)  <> 0 then
+                            else if (LandPixelGet(ny, nx) and AMASK)  <> 0 then
                                     inc(c);
                         end
-                    else if Land[ny, nx] > 255 then
+                    else if LandGet(ny, nx) > 255 then
                         inc(c);
                     end
                 end;
 
         if c < 4 then // 0-3 neighbours
         begin
-            if ((Land[Y, X] and lfBasic) <> 0) and (not disableLandBack) then
-                LandPixels[yy, xx]:= LandBackPixel(X, Y)
+            if ((LandGet(Y, X) and lfBasic) <> 0) and (not disableLandBack) then
+                LandPixelSet(yy, xx, LandBackPixel(X, Y))
             else
-                LandPixels[yy, xx]:= LandPixels[yy, xx] and (not AMASK);
+                LandPixelSet(yy, xx, LandPixelGet(yy, xx) and (not AMASK));
 
             if not pixelsweep then
             begin
-                Land[Y, X]:= 0;
+                LandSet(Y, X, 0);
                 exit
             end
         end;
@@ -1083,7 +1083,7 @@
 begin
 
 // only AA inwards
-if (Land[Y, X] and lfDamaged) = 0 then
+if (LandGet(Y, X) and lfDamaged) = 0 then
     exit;
 
 // check location
@@ -1104,9 +1104,9 @@
 for nx:= X-1 to X+1 do
     for ny:= Y-1 to Y+1 do
         // only consider undamaged neighbors (also leads to skipping itself)
-        if (Land[ny, nx] and lfDamaged) = 0 then
+        if (LandGet(ny, nx) and lfDamaged) = 0 then
             begin
-            pixel:= LandPixels[ny, nx];
+            pixel:= LandPixelGet(ny, nx);
             inc(r, (pixel and RMask) shr RShift);
             inc(g, (pixel and GMask) shr GShift);
             inc(b, (pixel and BMask) shr BShift);
@@ -1132,95 +1132,95 @@
 g:= g div 8;
 b:= b div 8;
 a:= a div 8;
-LandPixels[y,x]:= (r shl RShift) or (g shl GShift) or (b shl BShift) or (a shl AShift);
+LandPixelSet(y, x, (r shl RShift) or (g shl GShift) or (b shl BShift) or (a shl AShift));
 
 end;
 
 procedure Smooth_oldImpl(X, Y: LongInt);
 begin
 // a bit of AA for explosions
-if (Land[Y, X] = 0) and (Y > topY + 1) and
+if (LandGet(Y, X) = 0) and (Y > topY + 1) and
     (Y < LAND_HEIGHT-2) and (X > leftX + 1) and (X < rightX - 1) then
     begin
-    if ((((Land[y, x-1] and lfDamaged) <> 0) and (((Land[y+1,x] and lfDamaged) <> 0)) or ((Land[y-1,x] and lfDamaged) <> 0))
-    or (((Land[y, x+1] and lfDamaged) <> 0) and (((Land[y-1,x] and lfDamaged) <> 0) or ((Land[y+1,x] and lfDamaged) <> 0)))) then
+    if ((((LandGet(y, x-1) and lfDamaged) <> 0) and (((LandGet(y+1,x) and lfDamaged) <> 0)) or ((LandGet(y-1,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x+1) and lfDamaged) <> 0) and (((LandGet(y-1,x) and lfDamaged) <> 0) or ((LandGet(y+1,x) and lfDamaged) <> 0)))) then
         begin
         if (cReducedQuality and rqBlurryLand) = 0 then
             begin
-            if ((LandPixels[y,x] and AMask) shr AShift) < 10 then
-                LandPixels[y,x]:= (ExplosionBorderColor and (not AMask)) or (128 shl AShift)
+            if ((LandPixelGet(y,x) and AMask) shr AShift) < 10 then
+                LandPixelSet(y,x, (ExplosionBorderColor and (not AMask)) or (128 shl AShift))
             else
-                LandPixels[y,x]:=
-                                (((((LandPixels[y,x] and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or
-                                (((((LandPixels[y,x] and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or
-                                (((((LandPixels[y,x] and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift)
+                LandPixelSet(y,x,
+                                (((((LandPixelGet(y,x) and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or
+                                (((((LandPixelGet(y,x) and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or
+                                (((((LandPixelGet(y,x) and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift))
             end;
 {
-        if (Land[y, x-1] = lfObject) then
-            Land[y,x]:= lfObject
-        else if (Land[y, x+1] = lfObject) then
-            Land[y,x]:= lfObject
+        if (LandGet(y, x-1) = lfObject) then
+            LandSet(y,x, lfObject)
+        else if (LandGet(y, x+1) = lfObject) then
+            LandSet(y,x, lfObject)
         else
-            Land[y,x]:= lfBasic;
+            LandSet(y,x, lfBasic);
 }
         end
-    else if ((((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0))
-    or (((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0))
-    or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0))
-    or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0))
-    or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0))
-    or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0))
-    or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0))
-    or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0))) then
+    else if ((((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0))
+    or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0))
+    or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0))
+    or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0))
+    or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0))) then
         begin
         if (cReducedQuality and rqBlurryLand) = 0 then
             begin
-            if ((LandPixels[y,x] and AMask) shr AShift) < 10 then
-                LandPixels[y,x]:= (ExplosionBorderColor and (not AMask)) or (64 shl AShift)
+            if ((LandPixelGet(y,x) and AMask) shr AShift) < 10 then
+                LandPixelSet(y,x, (ExplosionBorderColor and (not AMask)) or (64 shl AShift))
             else
-                LandPixels[y,x]:=
-                                (((((LandPixels[y,x] and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or
-                                (((((LandPixels[y,x] and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or
-                                (((((LandPixels[y,x] and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift)
+                LandPixelSet(y,x,
+                                (((((LandPixelGet(y,x) and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or
+                                (((((LandPixelGet(y,x) and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or
+                                (((((LandPixelGet(y,x) and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift))
             end;
 {
-        if (Land[y, x-1] = lfObject) then
-            Land[y, x]:= lfObject
-        else if (Land[y, x+1] = lfObject) then
-            Land[y, x]:= lfObject
-        else if (Land[y+1, x] = lfObject) then
-            Land[y, x]:= lfObject
-        else if (Land[y-1, x] = lfObject) then
-        Land[y, x]:= lfObject
-        else Land[y,x]:= lfBasic
+        if (LandGet(y, x-1) = lfObject) then
+            LandGet(y, x):= lfObject
+        else if (LandGet(y, x+1) = lfObject) then
+            LandGet(y, x):= lfObject
+        else if (LandGet(y+1, x) = lfObject) then
+            LandGet(y, x):= lfObject
+        else if (LandGet(y-1, x) = lfObject) then
+        LandGet(y, x):= lfObject
+        else LandGet(y,x):= lfBasic
 }
         end
     end
-else if ((cReducedQuality and rqBlurryLand) = 0) and ((LandPixels[Y, X] and AMask) = AMask)
-and (Land[Y, X] and (lfDamaged or lfBasic) = lfBasic)
+else if ((cReducedQuality and rqBlurryLand) = 0) and ((LandPixelGet(Y, X) and AMask) = AMask)
+and (LandGet(Y, X) and (lfDamaged or lfBasic) = lfBasic)
 and (Y > topY + 1) and (Y < LAND_HEIGHT-2) and (X > leftX + 1) and (X < rightX - 1) then
     begin
-    if ((((Land[y, x-1] and lfDamaged) <> 0) and (((Land[y+1,x] and lfDamaged) <> 0)) or ((Land[y-1,x] and lfDamaged) <> 0))
-    or (((Land[y, x+1] and lfDamaged) <> 0) and (((Land[y-1,x] and lfDamaged) <> 0) or ((Land[y+1,x] and lfDamaged) <> 0)))) then
+    if ((((LandGet(y, x-1) and lfDamaged) <> 0) and (((LandGet(y+1,x) and lfDamaged) <> 0)) or ((LandGet(y-1,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x+1) and lfDamaged) <> 0) and (((LandGet(y-1,x) and lfDamaged) <> 0) or ((LandGet(y+1,x) and lfDamaged) <> 0)))) then
         begin
-        LandPixels[y,x]:=
-                        (((((LandPixels[y,x] and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or
-                        (((((LandPixels[y,x] and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or
-                        (((((LandPixels[y,x] and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift)
+        LandPixelSet(y,x,
+                        (((((LandPixelGet(y,x) and RMask shr RShift) div 2)+((ExplosionBorderColor and RMask) shr RShift) div 2) and $FF) shl RShift) or
+                        (((((LandPixelGet(y,x) and GMask shr GShift) div 2)+((ExplosionBorderColor and GMask) shr GShift) div 2) and $FF) shl GShift) or
+                        (((((LandPixelGet(y,x) and BMask shr BShift) div 2)+((ExplosionBorderColor and BMask) shr BShift) div 2) and $FF) shl BShift) or ($FF shl AShift))
         end
-    else if ((((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0))
-    or (((Land[y, x-1] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0))
-    or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y+2,x] and lfDamaged) <> 0))
-    or (((Land[y, x+1] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y-2,x] and lfDamaged) <> 0))
-    or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0))
-    or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x+1] and lfDamaged) <> 0) and ((Land[y,x+2] and lfDamaged) <> 0))
-    or (((Land[y+1, x] and lfDamaged) <> 0) and ((Land[y+1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0))
-    or (((Land[y-1, x] and lfDamaged) <> 0) and ((Land[y-1,x-1] and lfDamaged) <> 0) and ((Land[y,x-2] and lfDamaged) <> 0))) then
+    else if ((((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x-1) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y+2,x) and lfDamaged) <> 0))
+    or (((LandGet(y, x+1) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y-2,x) and lfDamaged) <> 0))
+    or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0))
+    or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x+1) and lfDamaged) <> 0) and ((LandGet(y,x+2) and lfDamaged) <> 0))
+    or (((LandGet(y+1, x) and lfDamaged) <> 0) and ((LandGet(y+1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0))
+    or (((LandGet(y-1, x) and lfDamaged) <> 0) and ((LandGet(y-1,x-1) and lfDamaged) <> 0) and ((LandGet(y,x-2) and lfDamaged) <> 0))) then
         begin
-        LandPixels[y,x]:=
-                        (((((LandPixels[y,x] and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or
-                        (((((LandPixels[y,x] and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or
-                        (((((LandPixels[y,x] and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift)
+        LandPixelSet(y,x,
+                        (((((LandPixelGet(y,x) and RMask shr RShift) * 3 div 4)+((ExplosionBorderColor and RMask) shr RShift) div 4) and $FF) shl RShift) or
+                        (((((LandPixelGet(y,x) and GMask shr GShift) * 3 div 4)+((ExplosionBorderColor and GMask) shr GShift) div 4) and $FF) shl GShift) or
+                        (((((LandPixelGet(y,x) and BMask shr BShift) * 3 div 4)+((ExplosionBorderColor and BMask) shr BShift) div 4) and $FF) shl BShift) or ($FF shl AShift))
         end
     end
 end;
@@ -1308,12 +1308,12 @@
 
 
 // Return true if outside of land or not the value tested, used right now for some X/Y movement that does not use normal hedgehog movement in GSHandlers.inc
-function CheckLandValue(X, Y: LongInt; LandFlag: Word): boolean; inline;
+function CheckLandValue(X, Y: LongInt; LandFlag: Word): boolean;
 begin
-    CheckLandValue:= ((X and LAND_WIDTH_MASK <> 0) or (Y and LAND_HEIGHT_MASK <> 0)) or ((Land[Y, X] and LandFlag) = 0)
+    CheckLandValue:= ((X and LAND_WIDTH_MASK <> 0) or (Y and LAND_HEIGHT_MASK <> 0)) or ((LandGet(Y, X) and LandFlag) = 0)
 end;
 
-function LandBackPixel(x, y: LongInt): LongWord; inline;
+function LandBackPixel(x, y: LongInt): LongWord;
 var p: PLongWordArray;
 begin
     if LandBackSurface = nil then
@@ -1382,30 +1382,30 @@
         end;
 
     if ((x and LAND_WIDTH_MASK) = 0) and ((y and LAND_HEIGHT_MASK) = 0) then
-        Land[y, x]:= Color;
+        LandSet(y, x, Color);
     end
 end;
 
-function DrawDots(x, y, xx, yy: Longint; Color: Longword): Longword; inline;
+function DrawDots(x, y, xx, yy: Longint; Color: Longword): Longword;
 begin
     DrawDots:= 0;
 
-    if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (Land[y + yy, x + xx] <> Color) then
-        begin inc(DrawDots); Land[y + yy, x + xx]:= Color; end;
-    if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (Land[y - yy, x + xx] <> Color) then
-        begin inc(DrawDots); Land[y - yy, x + xx]:= Color; end;
-    if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (Land[y + yy, x - xx] <> Color) then
-        begin inc(DrawDots); Land[y + yy, x - xx]:= Color; end;
-    if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (Land[y - yy, x - xx] <> Color) then
-        begin inc(DrawDots); Land[y - yy, x - xx]:= Color; end;
-    if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (Land[y + xx, x + yy] <> Color) then
-        begin inc(DrawDots); Land[y + xx, x + yy]:= Color; end;
-    if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (Land[y - xx, x + yy] <> Color) then
-        begin inc(DrawDots); Land[y - xx, x + yy]:= Color; end;
-    if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (Land[y + xx, x - yy] <> Color) then
-        begin inc(DrawDots); Land[y + xx, x - yy]:= Color; end;
-    if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (Land[y - xx, x - yy] <> Color) then
-        begin inc(DrawDots); Land[y - xx, x - yy]:= Color; end;
+    if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + yy, x + xx) <> Color) then
+        begin inc(DrawDots); LandSet(y + yy, x + xx, Color); end;
+    if (((x + xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - yy, x + xx) <> Color) then
+        begin inc(DrawDots); LandSet(y - yy, x + xx, Color); end;
+    if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y + yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + yy, x - xx) <> Color) then
+        begin inc(DrawDots); LandSet(y + yy, x - xx, Color); end;
+    if (((x - xx) and LAND_WIDTH_MASK) = 0) and (((y - yy) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - yy, x - xx) <> Color) then
+        begin inc(DrawDots); LandSet(y - yy, x - xx, Color); end;
+    if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + xx, x + yy) <> Color) then
+        begin inc(DrawDots); LandSet(y + xx, x + yy, Color); end;
+    if (((x + yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - xx, x + yy) <> Color) then
+        begin inc(DrawDots); LandSet(y - xx, x + yy, Color); end;
+    if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y + xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y + xx, x - yy) <> Color) then
+        begin inc(DrawDots); LandSet(y + xx, x - yy, Color); end;
+    if (((x - yy) and LAND_WIDTH_MASK) = 0) and (((y - xx) and LAND_HEIGHT_MASK) = 0) and (LandGet(y - xx, x - yy) <> Color) then
+        begin inc(DrawDots); LandSet(y - xx, x - yy, Color); end;
 end;
 
 function DrawLines(X1, Y1, X2, Y2, XX, YY: LongInt; color: Longword): Longword;
@@ -1512,9 +1512,9 @@
             xx:= dx - r + x;
             if (xx = x) and (yy = y) then
                 s[dx + 1]:= 'X'
-            else if Land[yy, xx] > 255 then
+            else if LandGet(yy, xx) > 255 then
                 s[dx + 1]:= 'O'
-            else if Land[yy, xx] > 0 then
+            else if LandGet(yy, xx) > 0 then
                 s[dx + 1]:= '*'
             else
                 s[dx + 1]:= '.'
--- a/hedgewars/uLandObjects.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandObjects.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -25,18 +25,18 @@
 procedure AddObjects();
 procedure FreeLandObjects();
 procedure LoadThemeConfig;
-procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface); inline;
-procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word); inline;
+procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface);
+procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word);
 procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word; Flip: boolean);
 procedure BlitOverlayAndGenerateCollisionInfo(cpX, cpY: Longword; Image: PSDL_Surface);
 procedure BlitImageUsingMask(cpX, cpY: Longword;  Image, Mask: PSDL_Surface);
 procedure AddOnLandObjects(Surface: PSDL_Surface);
-procedure SetLand(var LandWord: Word; Pixel: LongWord); inline;
+procedure SetLand(y, x: LongInt; Pixel: LongWord);
 
 implementation
 uses uStore, uConsts, uConsole, uRandom, uSound
      , uTypes, uVariables, uDebug, uUtils
-     , uPhysFSLayer, uRenderUtils;
+     , uPhysFSLayer, uRenderUtils, uLandUtils;
 
 const MaxRects = 512;
       MAXOBJECTRECTS = 16;
@@ -84,37 +84,37 @@
     ThemeObjects: TThemeObjects;
     SprayObjects: TSprayObjects;
 
-procedure SetLand(var LandWord: Word; Pixel: LongWord); inline;
+procedure SetLand(y, x: LongInt; Pixel: LongWord);
 begin
     // this an if instead of masking colours to avoid confusing map creators
     if ((AMask and Pixel) = 0) then
-        LandWord:= 0
+        LandSet(y, x, 0)
     else if (Pixel and AMask > 0) and (Pixel and RMask > 0) and (Pixel and GMask > 0) and (Pixel and BMask > 0) then // whiteish
-        LandWord:= lfObject
+        LandSet(y, x, lfObject)
     else if (Pixel and AMask > 0) and (Pixel and RMask = 0) and (Pixel and GMask = 0) and (Pixel and BMask = 0) then // blackish
         begin
-        LandWord:= lfBasic;
+        LandSet(y, x, lfBasic);
         disableLandBack:= false
         end
     else if (Pixel and AMask > 0) and (Pixel and RMask > 0) and (Pixel and GMask = 0) and (Pixel and BMask = 0) then // reddish
-        LandWord:= lfIndestructible
+        LandSet(y, x, lfIndestructible)
     else if (Pixel and AMask > 0) and (Pixel and RMask = 0) and (Pixel and GMask = 0) and (Pixel and BMask > 0) then // blueish
-        LandWord:= lfObject or lfIce
+        LandSet(y, x, lfObject or lfIce)
     else if (Pixel and AMask > 0) and (Pixel and RMask = 0) and (Pixel and GMask > 0) and (Pixel and BMask = 0) then // greenish
-        LandWord:= lfObject or lfBouncy
+        LandSet(y, x, lfObject or lfBouncy)
 end;
 
-procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface); inline;
+procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface);
 begin
     BlitImageAndGenerateCollisionInfo(cpX, cpY, Width, Image, 0, false);
 end;
 
-procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word); inline;
+procedure BlitImageAndGenerateCollisionInfo(cpX, cpY, Width: Longword; Image: PSDL_Surface; LandFlags: Word);
 begin
     BlitImageAndGenerateCollisionInfo(cpX, cpY, Width, Image, LandFlags, false);
 end;
 
-function LerpByte(src, dst: Byte; l: LongWord): LongWord; inline;
+function LerpByte(src, dst: Byte; l: LongWord): LongWord;
 begin
     LerpByte:= ((255 - l) * src + l * dst) div 255;
 end;
@@ -153,9 +153,9 @@
             color:= p^[x];
 
         if (cReducedQuality and rqBlurryLand) = 0 then
-            pLandColor:= @LandPixels[cpY + y, cpX + x]
+            pLandColor:= @(LandPixelRow(cpY + y)^[cpX + x])
         else
-            pLandColor:= @LandPixels[(cpY + y) div 2, (cpX + x) div 2];
+            pLandColor:= @(LandPixelRow((cpY + y) div 2)^[(cpX + x) div 2]);
 
         landColor:= pLandColor^;
         alpha:= (landColor and AMask) shr AShift;
@@ -173,8 +173,8 @@
 
             end;
 
-        if ((color and AMask) <> 0) and (Land[cpY + y, cpX + x] <= lfAllObjMask) then
-            Land[cpY + y, cpX + x]:= lfObject or LandFlags
+        if ((color and AMask) <> 0) and (LandGet(cpY + y, cpX + x) <= lfAllObjMask) then
+            LandSet(cpY + y, cpX + x, lfObject or LandFlags)
         end;
     p:= PLongwordArray(@(p^[Image^.pitch shr 2]))
     end;
@@ -208,9 +208,9 @@
         if (color and AMask) <> 0 then
             begin
             if (cReducedQuality and rqBlurryLand) = 0 then
-                pLandColor:= @LandPixels[cpY + y, cpX + x]
+                pLandColor:= @(LandPixelRow(cpY + y)^[cpX + x])
             else
-                pLandColor:= @LandPixels[(cpY + y) div 2, (cpX + x) div 2];
+                pLandColor:= @(LandPixelRow((cpY + y) div 2)^[(cpX + x) div 2]);
 
             alpha:= (color and AMask) shr AShift;
             if ((alpha <> $FF) and ((pLandColor^) <> 0)) then
@@ -224,8 +224,8 @@
                 end;
             pLandColor^:= color;
 
-            if Land[cpY + y, cpX + x] <= lfAllObjMask then
-                Land[cpY + y, cpX + x]:= lfObject
+            if LandGet(cpY + y, cpX + x) <= lfAllObjMask then
+                LandSet(cpY + y, cpX + x, lfObject)
             end;
         end;
     p:= PLongwordArray(@(p^[Image^.pitch shr 2]))
@@ -263,9 +263,9 @@
         color:= p^[x];
 
         if (cReducedQuality and rqBlurryLand) = 0 then
-            pLandColor:= @LandPixels[cpY + y, cpX + x]
+            pLandColor:= @(LandPixelRow(cpY + y)^[cpX + x])
         else
-            pLandColor:= @LandPixels[(cpY + y) div 2, (cpX + x) div 2];
+            pLandColor:= @(LandPixelRow((cpY + y) div 2)^[(cpX + x) div 2]);
 
         landColor:= pLandColor^;
         alpha:= (landColor and AMask) shr AShift;
@@ -282,8 +282,8 @@
                    or (LerpByte(alpha, 255, (color and AMask) shr AShift) shl AShift);
         end;
 
-        if (Land[cpY + y, cpX + x] <= lfAllObjMask) or (Land[cpY + y, cpX + x] and lfObject <> 0)  then
-            SetLand(Land[cpY + y, cpX + x], mp^[x]);
+        if (LandGet(cpY + y, cpX + x) <= lfAllObjMask) or (LandGet(cpY + y, cpX + x) and lfObject <> 0)  then
+            SetLand(cpY + y, cpX + x, mp^[x]);
         end;
 
     p:= PLongwordArray(@(p^[Image^.pitch shr 2]));
@@ -341,7 +341,7 @@
 begin
     lRes:= 0;
     for i:= y to Pred(y + h) do
-        if Land[i, x] <> 0 then
+        if LandGet(i, x) <> 0 then
             inc(lRes);
     CountNonZeroz:= lRes;
 end;
@@ -425,8 +425,8 @@
     begin
     bRes:= ((rect.y and LAND_HEIGHT_MASK) = 0) and ((by and LAND_HEIGHT_MASK) = 0)
     and ((tmpx and LAND_WIDTH_MASK) = 0) and ((tmpx2 and LAND_WIDTH_MASK) = 0)
-    and (Land[rect.y, tmpx] = Color) and (Land[by, tmpx] = Color)
-    and (Land[rect.y, tmpx2] = Color) and (Land[by, tmpx2] = Color);
+    and (LandGet(rect.y, tmpx) = Color) and (LandGet(by, tmpx) = Color)
+    and (LandGet(rect.y, tmpx2) = Color) and (LandGet(by, tmpx2) = Color);
     inc(tmpx);
     dec(tmpx2)
     end;
@@ -436,8 +436,8 @@
     begin
     bRes:= ((tmpy and LAND_HEIGHT_MASK) = 0) and ((tmpy2 and LAND_HEIGHT_MASK) = 0)
     and ((rect.x and LAND_WIDTH_MASK) = 0) and ((bx and LAND_WIDTH_MASK) = 0)
-    and (Land[tmpy, rect.x] = Color) and (Land[tmpy, bx] = Color)
-    and (Land[tmpy2, rect.x] = Color) and (Land[tmpy2, bx] = Color);
+    and (LandGet(tmpy, rect.x) = Color) and (LandGet(tmpy, bx) = Color)
+    and (LandGet(tmpy2, rect.x) = Color) and (LandGet(tmpy2, bx) = Color);
     inc(tmpy);
     dec(tmpy2)
     end;
@@ -459,7 +459,7 @@
     begin
         for tmpx := rect.x to bx do
         begin
-            if (((Land[rect.y, tmpx] and LandType) or (Land[by, tmpx] and LandType)) <> 0) then
+            if (((LandGet(rect.y, tmpx) and LandType) or (LandGet(by, tmpx) and LandType)) <> 0) then
             begin
                 CheckLandAny := true;
                 exit;
@@ -467,7 +467,7 @@
         end;
         for tmpy := rect.y to by do
         begin
-            if (((Land[tmpy, rect.x] and LandType) or (Land[tmpy, bx] and LandType)) <> 0) then
+            if (((LandGet(tmpy, rect.x) and LandType) or (LandGet(tmpy, bx) and LandType)) <> 0) then
             begin
                 CheckLandAny := true;
                 exit;
--- a/hedgewars/uLandOutline.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandOutline.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -10,12 +10,11 @@
               end;
 
 procedure DrawEdge(var pa: TPixAr; value: Word);
-procedure FillLand(x, y: LongInt; border, value: Word);
 procedure BezierizeEdge(var pa: TPixAr; Delta: hwFloat);
 
 implementation
 
-uses uLandGraphics, uDebug, uVariables, uLandTemplates;
+uses uLandGraphics, uDebug, uVariables, uLandTemplates, uLandUtils;
 
 
 var Stack: record
@@ -54,41 +53,6 @@
         end
 end;
 
-procedure FillLand(x, y: LongInt; border, value: Word);
-var xl, xr, dir: LongInt;
-begin
-    Stack.Count:= 0;
-    xl:= x - 1;
-    xr:= x;
-    Push(xl, xr, y, -1);
-    Push(xl, xr, y,  1);
-    dir:= 0;
-    while Stack.Count > 0 do
-        begin
-        Pop(xl, xr, y, dir);
-        while (xl > 0) and (Land[y, xl] <> border) and (Land[y, xl] <> value) do
-            dec(xl);
-        while (xr < LAND_WIDTH - 1) and (Land[y, xr] <> border) and (Land[y, xr] <> value) do
-            inc(xr);
-        while (xl < xr) do
-            begin
-            while (xl <= xr) and ((Land[y, xl] = border) or (Land[y, xl] = value)) do
-                inc(xl);
-            x:= xl;
-            while (xl <= xr) and (Land[y, xl] <> border) and (Land[y, xl] <> value) do
-                begin
-                Land[y, xl]:= value;
-                inc(xl)
-                end;
-            if x < xl then
-                begin
-                Push(x, Pred(xl), y, dir);
-                Push(x, Pred(xl), y,-dir);
-                end;
-            end;
-        end;
-end;
-
 procedure DrawEdge(var pa: TPixAr; value: Word);
 var i: LongInt;
 begin
--- a/hedgewars/uLandTexture.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandTexture.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -30,7 +30,7 @@
 procedure SetLandTexture;
 
 implementation
-uses uConsts, GLunit, uTypes, uVariables, uTextures, uDebug, uRender, uUtils;
+uses uConsts, GLunit, uTypes, uVariables, uTextures, uDebug, uRender, uUtils, uLandUtils;
 
 const TEXSIZE = 128;
       // in avoid tile borders stretch the blurry texture by 1 pixel more
@@ -50,7 +50,7 @@
 var ty: Longword;
 begin
 for ty:= 0 to TEXSIZE - 1 do
-    Move(LandPixels[y * TEXSIZE + ty, x * TEXSIZE], tmpPixels[ty, 0], sizeof(Longword) * TEXSIZE);
+    Move(LandPixelRow(y * TEXSIZE + ty)^[x * TEXSIZE], tmpPixels[ty, 0], sizeof(Longword) * TEXSIZE);
 
 Pixels:= @tmpPixels
 end;
@@ -60,7 +60,7 @@
 begin
 for ty:= 0 to TEXSIZE - 1 do
     for tx:= 0 to TEXSIZE - 1 do
-        tmpPixels[ty, tx]:= Land[y * TEXSIZE + ty, x * TEXSIZE + tx] or AMask;
+        tmpPixels[ty, tx]:= LandGet(y * TEXSIZE + ty, x * TEXSIZE + tx) or AMask;
 
 Pixels2:= @tmpPixels
 end;
@@ -129,14 +129,14 @@
                     // first check edges
                     while isEmpty and (ty < TEXSIZE) do
                         begin
-                        isEmpty:= LandPixels[ly + ty, lx] and AMask = 0;
-                        if isEmpty then isEmpty:= LandPixels[ly + ty, Pred(lx + TEXSIZE)] and AMask = 0;
+                        isEmpty:= LandPixelGet(ly + ty, lx) and AMask = 0;
+                        if isEmpty then isEmpty:= LandPixelGet(ly + ty, Pred(lx + TEXSIZE)) and AMask = 0;
                         inc(ty)
                         end;
                     while isEmpty and (tx < TEXSIZE-1) do
                         begin
-                        isEmpty:= LandPixels[ly, lx + tx] and AMask = 0;
-                        if isEmpty then isEmpty:= LandPixels[Pred(ly + TEXSIZE), lx + tx] and AMask = 0;
+                        isEmpty:= LandPixelGet(ly, lx + tx) and AMask = 0;
+                        if isEmpty then isEmpty:= LandPixelGet(Pred(ly + TEXSIZE), lx + tx) and AMask = 0;
                         inc(tx)
                         end;
                     // then search every other remaining. does this sort of stuff defeat compiler opts?
@@ -146,7 +146,7 @@
                         tx:= 2;
                         while isEmpty and (tx < TEXSIZE-1) do
                             begin
-                            isEmpty:= LandPixels[ly + ty, lx + tx] and AMask = 0;
+                            isEmpty:= LandPixelGet(ly + ty, lx + tx) and AMask = 0;
                             inc(tx,2)
                             end;
                         inc(ty,2);
@@ -158,7 +158,7 @@
                         tx:= 1;
                         while isEmpty and (tx < TEXSIZE-1) do
                             begin
-                            isEmpty:= LandPixels[ly + ty, lx + tx] and AMask = 0;
+                            isEmpty:= LandPixelGet(ly + ty, lx + tx) and AMask = 0;
                             inc(tx,2)
                             end;
                         inc(ty,2);
--- a/hedgewars/uLandUtils.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uLandUtils.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -1,12 +1,105 @@
 unit uLandUtils;
 interface
+uses SDLh;
 
+procedure GenerateTemplatedLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring);
 procedure ResizeLand(width, height: LongWord);
+procedure DisposeLand();
 procedure InitWorldEdges();
 
+function  LandGet(y, x: LongInt): Word;
+procedure LandSet(y, x: LongInt; value: Word);
+function  LandRow(row: LongInt): PWordArray;
+
+procedure FillLand(x, y: LongInt; border, value: Word);
+
+function  LandPixelGet(y, x: LongInt): Longword;
+procedure LandPixelSet(y, x: LongInt; value: Longword);
+function  LandPixelRow(row: LongInt): PLongwordArray;
+
 implementation
 uses uUtils, uConsts, uVariables, uTypes;
 
+const LibFutureName = 'hwengine_future';
+
+function  create_empty_game_field(width, height: Longword): pointer; cdecl; external LibFutureName;
+procedure get_game_field_parameters(game_field: pointer; var width: LongInt; var height: LongInt; var play_width: LongInt; var play_height: LongInt); cdecl; external LibFutureName;
+procedure dispose_game_field(game_field: pointer); cdecl; external LibFutureName;
+
+function  land_get(game_field: pointer; x, y: LongInt): Word; cdecl; external LibFutureName;
+procedure land_set(game_field: pointer; x, y: LongInt; value: Word); cdecl; external LibFutureName;
+function  land_row(game_field: pointer; row: LongInt): PWordArray; cdecl; external LibFutureName;
+procedure land_fill(game_field: pointer; x, y: LongInt; border, fill: Word); cdecl; external LibFutureName;
+
+function  land_pixel_get(game_field: pointer; x, y: LongInt): Longword; cdecl; external LibFutureName;
+procedure land_pixel_set(game_field: pointer; x, y: LongInt; value: Longword); cdecl; external LibFutureName;
+function  land_pixel_row(game_field: pointer; row: LongInt): PLongwordArray; cdecl; external LibFutureName;
+
+function  generate_templated_game_field(feature_size: Longword; seed, template_type, data_path: PChar): pointer; cdecl; external LibFutureName;
+procedure apply_theme(game_field: pointer; data_path, theme_name: PChar); cdecl; external LibFutureName;
+
+var gameField: pointer;
+
+function  LandGet(y, x: LongInt): Word;
+begin
+    LandGet:= land_get(gameField, x, y)
+end;
+
+procedure LandSet(y, x: LongInt; value: Word);
+begin
+    land_set(gameField, x, y, value)
+end;
+
+function  LandRow(row: LongInt): PWordArray;
+begin
+    LandRow:= land_row(gameField, row)
+end;
+
+procedure FillLand(x, y: LongInt; border, value: Word);
+begin
+    land_fill(gameField, x, y, border, value)
+end;
+
+function  LandPixelGet(y, x: LongInt): Longword;
+begin
+    LandPixelGet:= land_pixel_get(gameField, x, y)
+end;
+
+procedure LandPixelSet(y, x: LongInt; value: Longword);
+begin
+    land_pixel_set(gameField, x, y, value)
+end;
+
+function  LandPixelRow(row: LongInt): PLongwordArray;
+begin
+    LandPixelRow:= land_pixel_row(gameField, row)
+end;
+
+procedure GenerateTemplatedLand(featureSize: Longword; seed, templateType: shortstring; dataPath: ansistring);
+begin
+    seed[byte(seed[0]) + 1]:= #0;
+    templateType[byte(templateType[0]) + 1]:= #0;
+
+    gameField:= generate_templated_game_field(featureSize, @seed[1], @templateType[1], PChar(dataPath));
+    get_game_field_parameters(gameField, LAND_WIDTH, LAND_HEIGHT, playWidth, playHeight);
+
+    MaxHedgehogs:= 32;
+    hasGirders:= true;
+
+    leftX:= (LAND_WIDTH - playWidth) div 2;
+    rightX:= Pred(leftX + playWidth);
+    topY:= LAND_HEIGHT - playHeight;
+    cWaterLine:= LAND_HEIGHT;
+
+    // let's assume those are powers of two
+    LAND_WIDTH_MASK:= not(LAND_WIDTH-1);
+    LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1);
+
+    SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32));
+
+    initScreenSpaceVars();
+end;
+
 procedure ResizeLand(width, height: LongWord);
 var potW, potH: LongInt;
 begin
@@ -19,12 +112,8 @@
     LAND_WIDTH_MASK:= not(LAND_WIDTH-1);
     LAND_HEIGHT_MASK:= not(LAND_HEIGHT-1);
     cWaterLine:= LAND_HEIGHT;
-    if (cReducedQuality and rqBlurryLand) = 0 then
-        SetLength(LandPixels, LAND_HEIGHT, LAND_WIDTH)
-    else
-        SetLength(LandPixels, LAND_HEIGHT div 2, LAND_WIDTH div 2);
 
-    SetLength(Land, LAND_HEIGHT, LAND_WIDTH);
+    gameField:= create_empty_game_field(LAND_WIDTH, LAND_HEIGHT);
     SetLength(LandDirty, (LAND_HEIGHT div 32), (LAND_WIDTH div 32));
     // 0.5 is already approaching on unplayable
     if (width div 4096 >= 2) or (height div 2048 >= 2) then cMaxZoomLevel:= cMaxZoomLevel/2;
@@ -33,6 +122,11 @@
 initScreenSpaceVars();
 end;
 
+procedure DisposeLand();
+begin
+    dispose_game_field(gameField)
+end;
+
 procedure InitWorldEdges();
 var cy, cx, lx, ly: LongInt;
     found: boolean;
@@ -70,7 +164,7 @@
 for cx:= 0 to lx do
     begin
     for cy:= ly downto 0 do
-        if Land[cy, cx] <> 0 then
+        if LandGet(cy, cx) <> 0 then
             begin
             leftX:= max(0, cx - cWorldEdgeDist);
             // break out of both loops
@@ -85,7 +179,7 @@
 for cx:= lx downto 0 do
     begin
     for cy:= ly downto 0 do
-        if Land[cy, cx] <> 0 then
+        if LandGet(cy, cx) <> 0 then
             begin
             rightX:= min(lx, cx + cWorldEdgeDist);
             // break out of both loops
--- a/hedgewars/uMisc.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uMisc.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -29,10 +29,10 @@
 function  doSurfaceConversion(tmpsurf: PSDL_Surface): PSDL_Surface;
 function MakeScreenshot(filename: shortstring; k: LongInt; dump: LongWord): boolean;
 function  GetTeamStatString(p: PTeam): shortstring;
-function  SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline;
+function  SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect;
 
 implementation
-uses uVariables, uUtils
+uses uVariables, uUtils, uLandUtils
      {$IFDEF PNG_SCREENSHOTS}, PNGh, png {$ENDIF};
 
 type PScreenshot = ^TScreenshot;
@@ -288,18 +288,18 @@
     for y:= 0 to LAND_HEIGHT-1 do
         for x:= 0 to LAND_WIDTH-1 do
             if dump = 2 then
-                PLongWordArray(p)^[y*LAND_WIDTH+x]:= LandPixels[LAND_HEIGHT-1-y, x]
+                PLongWordArray(p)^[y*LAND_WIDTH+x]:= LandPixelGet(LAND_HEIGHT-1-y, x)
             else
                 begin
-                if Land[LAND_HEIGHT-1-y, x] and lfIndestructible = lfIndestructible then
+                if LandGet(LAND_HEIGHT-1-y, x) and lfIndestructible = lfIndestructible then
                     PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or RMask)
-                else if Land[LAND_HEIGHT-1-y, x] and lfIce = lfIce then
+                else if LandGet(LAND_HEIGHT-1-y, x) and lfIce = lfIce then
                     PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or BMask)
-                else if Land[LAND_HEIGHT-1-y, x] and lfBouncy = lfBouncy then
+                else if LandGet(LAND_HEIGHT-1-y, x) and lfBouncy = lfBouncy then
                     PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or GMask)
-                else if Land[LAND_HEIGHT-1-y, x] and lfObject = lfObject then
+                else if LandGet(LAND_HEIGHT-1-y, x) and lfObject = lfObject then
                     PLongWordArray(p)^[y*LAND_WIDTH+x]:= $FFFFFFFF
-                else if Land[LAND_HEIGHT-1-y, x] and lfBasic = lfBasic then
+                else if LandGet(LAND_HEIGHT-1-y, x) and lfBasic = lfBasic then
                     PLongWordArray(p)^[y*LAND_WIDTH+x]:= AMask
                 else
                     PLongWordArray(p)^[y*LAND_WIDTH+x]:= 0
@@ -353,7 +353,7 @@
     end;
 end;
 
-function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline;
+function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect;
 begin
     SDL_RectMake.x:= x;
     SDL_RectMake.y:= y;
--- a/hedgewars/uRandom.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uRandom.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -32,8 +32,8 @@
 
 procedure SetRandomSeed(Seed: shortstring; dropAdditionalPart: boolean); // Sets the seed that should be used for generating pseudo-random values.
 function  GetRandomf: hwFloat; // Returns a pseudo-random hwFloat.
-function  GetRandom(m: LongWord): LongWord; inline; // Returns a positive pseudo-random integer smaller than m.
-procedure AddRandomness(r: LongWord); inline;
+function  GetRandom(m: LongWord): LongWord;  // Returns a positive pseudo-random integer smaller than m.
+procedure AddRandomness(r: LongWord); 
 function  rndSign(num: hwFloat): hwFloat; // Returns num with a random chance of having a inverted sign.
 
 
@@ -42,13 +42,13 @@
 var cirbuf: array[0..63] of Longword;
     n: byte;
 
-procedure AddRandomness(r: LongWord); inline;
+procedure AddRandomness(r: LongWord); 
 begin
 n:= (n + 1) and $3F;
    cirbuf[n]:= cirbuf[n] xor r;
 end;
 
-function GetNext: Longword; inline;
+function GetNext: Longword; 
 begin
     n:= (n + 1) and $3F;
     cirbuf[n]:=
@@ -90,7 +90,7 @@
 GetRandomf.QWordValue:= GetNext
 end;
 
-function GetRandom(m: LongWord): LongWord; inline;
+function GetRandom(m: LongWord): LongWord; 
 begin
 GetNext;
 GetRandom:= GetNext mod m
--- a/hedgewars/uRender.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uRender.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -30,18 +30,18 @@
 
 procedure DrawSprite            (Sprite: TSprite; X, Y, Frame: LongInt);
 procedure DrawSprite            (Sprite: TSprite; X, Y, FrameX, FrameY: LongInt);
-procedure DrawSpriteFromRect    (Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); inline;
+procedure DrawSpriteFromRect    (Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); 
 procedure DrawSpriteClipped     (Sprite: TSprite; X, Y, TopY, RightX, BottomY, LeftX: LongInt);
 procedure DrawSpriteRotated     (Sprite: TSprite; X, Y, Dir: LongInt; Angle: real);
 procedure DrawSpriteRotatedF    (Sprite: TSprite; X, Y, Frame, Dir: LongInt; Angle: real);
 procedure DrawSpriteRotatedFReal(Sprite: TSprite; X, Y: Real; Frame, Dir: LongInt; Angle: real);
 procedure DrawSpritePivotedF(Sprite: TSprite; X, Y, Frame, Dir, PivotX, PivotY: LongInt; Angle: real);
 
-procedure DrawTexture           (X, Y: LongInt; Texture: PTexture); inline;
+procedure DrawTexture           (X, Y: LongInt; Texture: PTexture); 
 procedure DrawTexture           (X, Y: LongInt; Texture: PTexture; Scale: GLfloat);
 procedure DrawTexture2          (X, Y: LongInt; Texture: PTexture; Scale, Overlap: GLfloat);
-procedure DrawTextureFromRect   (X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
-procedure DrawTextureFromRect   (X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
+procedure DrawTextureFromRect   (X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); 
+procedure DrawTextureFromRect   (X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); 
 procedure DrawTextureFromRectDir(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture; Dir: LongInt);
 procedure DrawTextureCentered   (X, Top: LongInt; Source: PTexture);
 procedure DrawTextureF          (Texture: PTexture; Scale: GLfloat; X, Y, Frame, Dir, w, h: LongInt);
@@ -53,9 +53,9 @@
 procedure DrawCircle            (X, Y, Radius, Width: LongInt; color: LongWord);
 procedure DrawCircleFilled      (X, Y, Radius: LongInt; r, g, b, a: Byte);
 
-procedure DrawLine              (X0, Y0, X1, Y1, Width: Single; color: LongWord); inline;
+procedure DrawLine              (X0, Y0, X1, Y1, Width: Single; color: LongWord); 
 procedure DrawLine              (X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte);
-procedure DrawLineWrapped       (X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); inline;
+procedure DrawLineWrapped       (X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); 
 procedure DrawLineWrapped       (X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; r, g, b, a: Byte);
 procedure DrawLineOnScreen      (X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte);
 procedure DrawRect              (rect: TSDL_Rect; r, g, b, a: Byte; Fill: boolean);
@@ -70,19 +70,19 @@
 {$ENDIF}
 procedure RenderSetClearColor   (r, g, b, a: real);
 procedure Tint                  (r, g, b, a: Byte);
-procedure Tint                  (c: Longword); inline;
-procedure untint(); inline;
-procedure setTintAdd            (enable: boolean); inline;
+procedure Tint                  (c: Longword); 
+procedure untint(); 
+procedure setTintAdd            (enable: boolean); 
 
 // call this to finish the rendering of current frame
 procedure FinishRender();
 
-function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; inline;
-function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; inline;
+function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; 
+function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; 
 
 // 0 => not offscreen, <0 => left/top of screen >0 => right/below of screen
-function isDxAreaOffscreen(X, Width: LongInt): LongInt; inline;
-function isDyAreaOffscreen(Y, Height: LongInt): LongInt; inline;
+function isDxAreaOffscreen(X, Width: LongInt): LongInt; 
+function isDyAreaOffscreen(Y, Height: LongInt): LongInt; 
 
 procedure SetScale(f: GLfloat);
 procedure UpdateViewLimits();
@@ -97,15 +97,15 @@
 
 procedure EnableTexture(enable:Boolean);
 
-procedure SetTexCoordPointer(p: Pointer;n: Integer); inline;
-procedure SetVertexPointer(p: Pointer;n: Integer); inline;
-procedure SetColorPointer(p: Pointer;n: Integer); inline;
+procedure SetTexCoordPointer(p: Pointer;n: Integer); 
+procedure SetVertexPointer(p: Pointer;n: Integer); 
+procedure SetColorPointer(p: Pointer;n: Integer); 
 
-procedure UpdateModelviewProjection(); inline;
+procedure UpdateModelviewProjection(); 
 
-procedure openglPushMatrix      (); inline;
-procedure openglPopMatrix       (); inline;
-procedure openglTranslatef      (X, Y, Z: GLfloat); inline;
+procedure openglPushMatrix      (); 
+procedure openglPopMatrix       (); 
+procedure openglTranslatef      (X, Y, Z: GLfloat); 
 
 
 implementation
@@ -147,12 +147,12 @@
 procedure DeleteFramebuffer(var frame, depth, tex: GLuint); forward;
 {$ENDIF}
 
-function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; inline;
+function isAreaOffscreen(X, Y, Width, Height: LongInt): boolean; 
 begin
     isAreaOffscreen:= (isDxAreaOffscreen(X, Width) <> 0) or (isDyAreaOffscreen(Y, Height) <> 0);
 end;
 
-function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; inline;
+function isCircleOffscreen(X, Y, RadiusSquared: LongInt): boolean; 
 var dRightX, dBottomY, dLeftX, dTopY: LongInt;
 begin
     dRightX:= (X - ViewRightX);
@@ -166,14 +166,14 @@
         ((dTopY > 0) and (sqr(dTopY) > RadiusSquared))
 end;
 
-function isDxAreaOffscreen(X, Width: LongInt): LongInt; inline;
+function isDxAreaOffscreen(X, Width: LongInt): LongInt; 
 begin
     if X > ViewRightX then exit(1);
     if X + Width < ViewLeftX then exit(-1);
     isDxAreaOffscreen:= 0;
 end;
 
-function isDyAreaOffscreen(Y, Height: LongInt): LongInt; inline;
+function isDyAreaOffscreen(Y, Height: LongInt): LongInt; 
 begin
     if Y > ViewBottomY then exit(1);
     if Y + Height < ViewTopY then exit(-1);
@@ -658,7 +658,7 @@
     // disable/lower perspective correction (will not need it anyway)
 end;
 
-procedure openglLoadIdentity(); inline;
+procedure openglLoadIdentity(); 
 begin
 {$IFDEF GL2}
     hglLoadIdentity();
@@ -667,7 +667,7 @@
 {$ENDIF}
 end;
 
-procedure openglTranslProjMatrix(X, Y, Z: GLfloat); inline;
+procedure openglTranslProjMatrix(X, Y, Z: GLfloat); 
 begin
 {$IFDEF GL2}
     hglMatrixMode(MATRIX_PROJECTION);
@@ -680,7 +680,7 @@
 {$ENDIF}
 end;
 
-procedure openglPushMatrix(); inline;
+procedure openglPushMatrix(); 
 begin
 {$IFDEF GL2}
     hglPushMatrix();
@@ -689,7 +689,7 @@
 {$ENDIF}
 end;
 
-procedure openglPopMatrix(); inline;
+procedure openglPopMatrix(); 
 begin
 {$IFDEF GL2}
     hglPopMatrix();
@@ -698,7 +698,7 @@
 {$ENDIF}
 end;
 
-procedure openglTranslatef(X, Y, Z: GLfloat); inline;
+procedure openglTranslatef(X, Y, Z: GLfloat); 
 begin
 {$IFDEF GL2}
     hglTranslatef(X, Y, Z);
@@ -707,7 +707,7 @@
 {$ENDIF}
 end;
 
-procedure openglScalef(ScaleX, ScaleY, ScaleZ: GLfloat); inline;
+procedure openglScalef(ScaleX, ScaleY, ScaleZ: GLfloat); 
 begin
 {$IFDEF GL2}
     hglScalef(ScaleX, ScaleY, ScaleZ);
@@ -716,7 +716,7 @@
 {$ENDIF}
 end;
 
-procedure openglRotatef(RotX, RotY, RotZ: GLfloat; dir: LongInt); inline;
+procedure openglRotatef(RotX, RotY, RotZ: GLfloat; dir: LongInt); 
 { workaround for pascal bug https://bugs.freepascal.org/view.php?id=27222 }
 var tmpdir: LongInt;
 begin
@@ -728,7 +728,7 @@
 {$ENDIF}
 end;
 
-procedure openglUseColorOnly(b :boolean); inline;
+procedure openglUseColorOnly(b :boolean); 
 begin
     if b then
         begin
@@ -755,7 +755,7 @@
     EnableTexture(not b);
 end;
 
-procedure UpdateModelviewProjection(); inline;
+procedure UpdateModelviewProjection(); 
 {$IFDEF GL2}
 var
     mvp: TMatrix4x4f;
@@ -770,7 +770,7 @@
 {$ENDIF}
 end;
 
-procedure SetTexCoordPointer(p: Pointer; n: Integer); inline;
+procedure SetTexCoordPointer(p: Pointer; n: Integer); 
 begin
 {$IFDEF GL2}
     glBindBuffer(GL_ARRAY_BUFFER, tBuffer);
@@ -786,7 +786,7 @@
 {$ENDIF}
 end;
 
-procedure SetVertexPointer(p: Pointer; n: Integer); inline;
+procedure SetVertexPointer(p: Pointer; n: Integer); 
 begin
 {$IFDEF GL2}
     glBindBuffer(GL_ARRAY_BUFFER, vBuffer);
@@ -802,7 +802,7 @@
 {$ENDIF}
 end;
 
-procedure SetColorPointer(p: Pointer; n: Integer); inline;
+procedure SetColorPointer(p: Pointer; n: Integer); 
 begin
 {$IFDEF GL2}
     glBindBuffer(GL_ARRAY_BUFFER, cBuffer);
@@ -887,19 +887,19 @@
     UpdateModelviewProjection;
 end;
 
-procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); inline;
+procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); 
 begin
 r.y:= r.y + Height * Position;
 r.h:= Height;
 DrawTextureFromRect(X, Y, @r, SpritesData[Sprite].Texture)
 end;
 
-procedure DrawTextureFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
+procedure DrawTextureFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); 
 begin
 DrawTextureFromRectDir(X, Y, r^.w, r^.h, r, SourceTexture, 1)
 end;
 
-procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
+procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); 
 begin
 DrawTextureFromRectDir(X, Y, W, H, r, SourceTexture, 1)
 end;
@@ -967,7 +967,7 @@
 
 end;
 
-procedure DrawTexture(X, Y: LongInt; Texture: PTexture); inline;
+procedure DrawTexture(X, Y: LongInt; Texture: PTexture); 
 begin
     DrawTexture(X, Y, Texture, 1.0);
 end;
@@ -1375,7 +1375,7 @@
 end;
 
 // Same as below, but with color as LongWord
-procedure DrawLine(X0, Y0, X1, Y1, Width: Single; color: LongWord); inline;
+procedure DrawLine(X0, Y0, X1, Y1, Width: Single; color: LongWord); 
 begin
 DrawLine(X0, Y0, X1, Y1, Width, (color shr 24) and $FF, (color shr 16) and $FF, (color shr 8) and $FF, color and $FF)
 end;
@@ -1400,7 +1400,7 @@
 end;
 
 // Same as below, but with color as a longword
-procedure DrawLineWrapped(X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); inline;
+procedure DrawLineWrapped(X0, Y0, X1, Y1, Width: Single; goesLeft: boolean; Wraps: LongWord; color: LongWord); 
 begin
 DrawLineWrapped(X0, Y0, X1, Y1, Width, goesLeft, Wraps, (color shr 24) and $FF, (color shr 16) and $FF, (color shr 8) and $FF, color and $FF);
 end;
@@ -2073,7 +2073,7 @@
 
 end;
 
-procedure openglTint(r, g, b, a: Byte); inline;
+procedure openglTint(r, g, b, a: Byte); 
 {$IFDEF GL2}
 const
     scale:Real = 1.0/255.0;
@@ -2109,20 +2109,20 @@
     LastTint:= nc;
 end;
 
-procedure Tint(c: Longword); inline;
+procedure Tint(c: Longword); 
 begin
     if c = LastTint then exit;
     Tint(((c shr 24) and $FF), ((c shr 16) and $FF), (c shr 8) and $FF, (c and $FF))
 end;
 
-procedure untint(); inline;
+procedure untint(); 
 begin
     if cWhiteColor = LastTint then exit;
     openglTint($FF, $FF, $FF, $FF);
     LastTint:= cWhiteColor;
 end;
 
-procedure setTintAdd(enable: boolean); inline;
+procedure setTintAdd(enable: boolean); 
 begin
     {$IFDEF GL2}
         if enable then
--- a/hedgewars/uRenderUtils.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uRenderUtils.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -26,13 +26,13 @@
 procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean);
 
 procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL
-procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); inline;
+procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); 
 procedure copyToXYFromRect(src, dest: PSDL_Surface; srcX, srcY, srcW, srcH, destX, destY: LongInt);
 
 function GetSurfaceFrameCoordinateX(Surface: PSDL_Surface; Frame, frameWidth, frameHeight: LongInt): LongInt;
 function GetSurfaceFrameCoordinateY(Surface: PSDL_Surface; Frame, frameHeight: LongInt): LongInt;
 
-procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt); inline;
+procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt);
 procedure DrawSpriteFrame2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt; frame: LongInt);
 procedure DrawLine2Surf(dest: PSDL_Surface; x0,y0,x1,y1:LongInt; r,g,b: byte);
 procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean);
@@ -41,7 +41,7 @@
 function  RenderStringTexLim(s: ansistring; Color: Longword; font: THWFont; maxLength: LongWord): PTexture;
 function  RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture;
 
-function IsTooDarkToRead(TextColor: Longword): boolean; inline;
+function IsTooDarkToRead(TextColor: Longword): boolean; 
 
 implementation
 uses uVariables, uConsts, uTextures, SysUtils, uUtils, uDebug;
@@ -99,7 +99,7 @@
    GetSurfaceFrameCoordinateY:= (Frame mod ny) * frameHeight;
 end;
 
-function IsTooDarkToRead(TextColor: LongWord): boolean; inline;
+function IsTooDarkToRead(TextColor: LongWord): boolean;
 var clr: TSDL_Color;
 begin
     clr.r:= (TextColor shr 16) and $FF;
@@ -178,7 +178,7 @@
     SDL_UnlockSurface(Surface);
 end;
 
-procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); inline;
+procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); 
 begin
     // copy from complete src
     copyToXYFromRect(src, dest, 0, 0, src^.w, src^.h, destX, destY);
@@ -254,7 +254,7 @@
     SDL_UnlockSurface(dest);
 end;
 
-procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt); inline;
+procedure DrawSprite2Surf(sprite: TSprite; dest: PSDL_Surface; x,y: LongInt); 
 begin
    DrawSpriteFrame2Surf(sprite, dest, x, y, 0);
 end;
--- a/hedgewars/uScript.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uScript.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -184,14 +184,14 @@
     LuaError('-- SYNTAX: ' + call + ' ( ' + paramsyntax + ' )');
 end;
 
-procedure LuaParameterCountError(expected, call, paramsyntax: shortstring; wrongcount: LongInt); inline;
+procedure LuaParameterCountError(expected, call, paramsyntax: shortstring; wrongcount: LongInt); 
 begin
     // TODO: i18n?
     LuaCallError('Wrong number of parameters! (is: ' + inttostr(wrongcount) + ', should be: '+ expected + ')', call, paramsyntax);
 end;
 
 // compare with allowed count
-function CheckLuaParamCount(L : Plua_State; count: LongInt; call, paramsyntax: shortstring): boolean; inline;
+function CheckLuaParamCount(L : Plua_State; count: LongInt; call, paramsyntax: shortstring): boolean; 
 var c: LongInt;
 begin
     c:= lua_gettop(L);
@@ -205,7 +205,7 @@
 end;
 
 // check if is either count1 or count2
-function CheckAndFetchParamCount(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; inline;
+function CheckAndFetchParamCount(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; 
 begin
     actual:= lua_gettop(L);
     if (actual <> count1) and (actual <> count2) then
@@ -218,7 +218,7 @@
 end;
 
 // check if is in range of count1 and count2
-function CheckAndFetchParamCountRange(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; inline;
+function CheckAndFetchParamCountRange(L : Plua_State; count1, count2: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; 
 begin
     actual:= lua_gettop(L);
     if (actual < count1) or (actual > count2) then
@@ -231,7 +231,7 @@
 end;
 
 // check if is same or higher as minCount
-function CheckAndFetchLuaParamMinCount(L : Plua_State; minCount: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; inline;
+function CheckAndFetchLuaParamMinCount(L : Plua_State; minCount: LongInt; call, paramsyntax: shortstring; out actual: LongInt): boolean; 
 begin
     actual:= lua_gettop(L);
     if (actual < minCount) then
@@ -243,7 +243,7 @@
     CheckAndFetchLuaParamMinCount:= true;
 end;
 
-function LuaToGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -256,7 +256,7 @@
         LuaToGearTypeOrd:= i;
 end;
 
-function LuaToVisualGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToVisualGearTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -269,7 +269,7 @@
         LuaToVisualGearTypeOrd:= i;
 end;
 
-function LuaToAmmoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToAmmoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -282,7 +282,7 @@
         LuaToAmmoTypeOrd:= i;
 end;
 
-function LuaToStatInfoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToStatInfoTypeOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -295,7 +295,7 @@
         LuaToStatInfoTypeOrd:= i;
 end;
 
-function LuaToSoundOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToSoundOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -334,7 +334,7 @@
         LuaToGoalStrIdOrd:= i;
 end;
 
-function LuaToHogEffectOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToHogEffectOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt;
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -347,7 +347,7 @@
         LuaToHogEffectOrd:= i;
 end;
 
-function LuaToCapGroupOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToCapGroupOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -360,7 +360,7 @@
         LuaToCapGroupOrd:= i;
 end;
 
-function LuaToSpriteOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToSpriteOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
@@ -373,7 +373,7 @@
         LuaToSpriteOrd:= i;
 end;
 
-function LuaToMapGenOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; inline;
+function LuaToMapGenOrd(L : Plua_State; i: LongInt; call, paramsyntax: shortstring): LongInt; 
 begin
     if lua_isnoneornil(L, i) then i:= -1
     else i:= Trunc(lua_tonumber(L, i));
--- a/hedgewars/uStore.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uStore.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -59,7 +59,7 @@
 procedure InitOffscreenOpenGL;
 {$ENDIF}
 
-procedure WarpMouse(x, y: Word); inline;
+procedure WarpMouse(x, y: Word); 
 procedure SwapBuffers; {$IFDEF USE_VIDEO_RECORDING}cdecl{$ELSE}inline{$ENDIF};
 procedure SetSkyColor(r, g, b: real);
 
@@ -1381,7 +1381,7 @@
 // for sdl2 we provide a SDL_WarpMouse() which just calls this function
 // this has the advantage of reducing 'uses' and 'ifdef' statements
 // (SDLwindow is a private member of this module)
-procedure WarpMouse(x, y: Word); inline;
+procedure WarpMouse(x, y: Word); 
 begin
     SDL_WarpMouseInWindow(SDLwindow, x, y);
 end;
--- a/hedgewars/uTextures.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uTextures.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -26,14 +26,14 @@
 procedure Surface2GrayScale(surf: PSDL_Surface);
 function  Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
 procedure PrettifySurfaceAlpha(surf: PSDL_Surface; pixels: PLongwordArray);
-procedure PrettifyAlpha2D(pixels: TLandArray; height, width: LongWord);
+procedure PrettifyAlpha2D(height, width: LongWord);
 procedure FreeAndNilTexture(var tex: PTexture);
 
 procedure initModule;
 procedure freeModule;
 
 implementation
-uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole;
+uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole, uLandUtils;
 
 var TextureList: PTexture;
 
@@ -194,7 +194,7 @@
     PrettifyAlpha(pixels, nil, si, li, w);
 end;
 
-procedure PrettifyAlpha2D(pixels: TLandArray; height, width: LongWord);
+procedure PrettifyAlpha2D(height, width: LongWord);
 var
     // current y; last x, second last y of array;
     y, lx, sly: LongWord;
@@ -203,10 +203,10 @@
     lx:= width - 1;
     for y:= 0 to sly do
         begin
-        PrettifyAlpha(PLongWordArray(pixels[y]), PLongWordArray(pixels[y+1]), 0, lx, 0);
+        PrettifyAlpha(LandPixelRow(y), LandPixelRow(y+1), 0, lx, 0);
         end;
     // don't forget last row
-    PrettifyAlpha(PLongWordArray(pixels[sly+1]), nil, 0, lx, 0);
+    PrettifyAlpha(LandPixelRow(sly+1), nil, 0, lx, 0);
 end;
 
 function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
--- a/hedgewars/uTouch.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uTouch.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -613,7 +613,7 @@
     fingerHasMoved := trunc(sqrt(sqr(finger.X-finger.historicalX) + sqr(finger.y-finger.historicalY))) > 30;
 end;
 
-function calculateDelta(finger1, finger2: TTouch_Data): LongInt; inline;
+function calculateDelta(finger1, finger2: TTouch_Data): LongInt; 
 begin
     calculateDelta := Round(sqrt(sqr(finger2.x-finger1.x) + sqr(finger2.y-finger1.y)));
 end;
--- a/hedgewars/uTypes.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uTypes.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -99,7 +99,7 @@
 
     // Gears that interact with other Gears and/or Land
     // first row of gears (<gtExplosives) should be avoided when searching a spawn place
-    TGearType = (gtFlame, gtHedgehog, gtMine, gtCase, gtAirMine, gtExplosives, 
+    TGearType = (gtFlame, gtHedgehog, gtMine, gtCase, gtAirMine, gtExplosives,
             gtGrenade, gtShell, gtGrave, gtBee, // 9
             gtShotgunShot, gtPickHammer, gtRope,  // 12
             gtDEagleShot, gtDynamite, gtClusterBomb, gtCluster, gtShover, // 17
@@ -545,8 +545,6 @@
             gidInfAttack, gidResetWeps, gidPerHogAmmo, gidTagTeam, gidMoreWind);
 
 
-    TLandArray = packed array of array of LongWord;
-    TCollisionArray = packed array of array of Word;
     TDirtyTag = packed array of array of byte;
 
     TPreview  = packed array[0..127, 0..31] of byte;
--- a/hedgewars/uUtils.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uUtils.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -53,16 +53,16 @@
 function  EnumToStr(const en : TMsgStrId) : shortstring; overload;
 function  EnumToStr(const en : TGoalStrId) : shortstring; overload;
 
-function  Min(a, b: LongInt): LongInt; inline;
-function  MinD(a, b: double) : double; inline;
-function  Max(a, b: LongInt): LongInt; inline;
+function  Min(a, b: LongInt): LongInt; 
+function  MinD(a, b: double) : double; 
+function  Max(a, b: LongInt): LongInt; 
 
 function  IntToStr(n: LongInt): shortstring;
 function  StrToInt(s: shortstring): LongInt;
 //function  StrToInt(s: shortstring; var success: boolean): LongInt;
 function  FloatToStr(n: hwFloat): shortstring;
 
-function  DxDy2Angle(const _dY, _dX: hwFloat): real; inline;
+function  DxDy2Angle(const _dY, _dX: hwFloat): real; 
 function  DxDy2Angle32(const _dY, _dX: hwFloat): LongInt;
 function  DxDy2AttackAngle(const _dY, _dX: hwFloat): LongInt;
 function  DxDy2AttackAnglef(const _dY, _dX: extended): LongInt;
@@ -73,16 +73,16 @@
 function  DecodeBase64(s: shortstring): shortstring;
 
 function  isPowerOf2(i: Longword): boolean;
-function  toPowerOf2(i: Longword): Longword; inline;
+function  toPowerOf2(i: Longword): Longword; 
 
-function  endian(independent: LongWord): LongWord; inline;
+function  endian(independent: LongWord): LongWord; 
 
 function  CheckCJKFont(s: ansistring; font: THWFont): THWFont;
 
 procedure AddFileLog(s: shortstring);
 procedure AddFileLogRaw(s: pchar); cdecl;
 
-function  CheckNoTeamOrHH: boolean; inline;
+function  CheckNoTeamOrHH: boolean; 
 
 function  GetLaunchX(at: TAmmoType; dir: LongInt; angle: LongInt): LongInt;
 function  GetLaunchY(at: TAmmoType; angle: LongInt): LongInt;
@@ -102,7 +102,7 @@
 procedure SetLengthA(var s: ansistring; len: Longword);
 {$ENDIF}
 
-function  isPhone: Boolean; inline;
+function  isPhone: Boolean; 
 
 {$IFDEF IPHONEOS}
 procedure startLoadingIndicator; cdecl; external;
@@ -443,7 +443,7 @@
 end;
 
 
-function DxDy2Angle(const _dY, _dX: hwFloat): real; inline;
+function DxDy2Angle(const _dY, _dX: hwFloat): real; 
 var dY, dX: Extended;
 begin
 dY:= hwFloat2Float(_dY);
@@ -469,7 +469,7 @@
 DxDy2AttackAngle:= trunc(arctan2(dY, dX) * MaxAngleDivPI)
 end;
 
-function DxDy2AttackAnglef(const _dY, _dX: extended): LongInt; inline;
+function DxDy2AttackAnglef(const _dY, _dX: extended): LongInt; 
 begin
 DxDy2AttackAnglef:= trunc(arctan2(_dY, _dX) * (cMaxAngle/pi))
 end;
@@ -539,7 +539,7 @@
 end;
 
 
-function endian(independent: LongWord): LongWord; inline;
+function endian(independent: LongWord): LongWord; 
 begin
 {$IFDEF ENDIAN_LITTLE}
 endian:= independent;
@@ -716,7 +716,7 @@
 {$ENDIF}
 
 // this function is just to determine whether we are running on a limited screen device
-function isPhone: Boolean; inline;
+function isPhone: Boolean; 
 begin
     isPhone:= false;
 {$IFDEF IPHONEOS}
--- a/hedgewars/uVariables.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uVariables.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -303,7 +303,7 @@
 
     SDLwindow: PSDL_Window;
     SDLGLcontext: PSDL_GLContext;
-  
+
 /////////////////////////////////////
 //Buttons
 {$IFDEF USE_TOUCH_INTERFACE}
@@ -2589,8 +2589,6 @@
         );
 
 var
-    Land: TCollisionArray;
-    LandPixels: TLandArray;
     LandDirty: TDirtyTag;
     hasBorder: boolean;
     hasGirders: boolean;
--- a/hedgewars/uVisualGears.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uVisualGears.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -117,7 +117,7 @@
         end
 end;
 
-function GetSprite(sprite, SDsprite: TSprite): TSprite; inline;
+function GetSprite(sprite, SDsprite: TSprite): TSprite; 
 begin
     if SuddenDeathDmg then
         exit(SDsprite)
@@ -125,7 +125,7 @@
         exit(sprite);
 end;
 
-function GetSpriteByWind(sprite, Lsprite: TSprite): TSprite; inline;
+function GetSpriteByWind(sprite, Lsprite: TSprite): TSprite; 
 begin
     if (SpritesData[Lsprite].Texture <> nil) and (cWindSpeedf<0) then
         exit(Lsprite)
@@ -133,7 +133,7 @@
         exit(sprite);
 end;
 
-function GetSpriteData(sprite, SDsprite: TSprite): PSpriteData; inline;
+function GetSpriteData(sprite, SDsprite: TSprite): PSpriteData; 
 begin
     exit(@SpritesData[GetSprite(sprite, SDsprite)]);
 end;
@@ -498,7 +498,7 @@
     end;
 end;
 
-procedure AddFlake; inline;
+procedure AddFlake; 
 begin
     AddVisualGear(cLeftScreenBorder + random(cScreenSpace), LAND_HEIGHT-cCloudOffset+ random(cCloudOffset), vgtFlake);
 end;
--- a/hedgewars/uVisualGearsHandlers.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uVisualGearsHandlers.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -71,7 +71,7 @@
 procedure doStepSmoothWindBar(Gear: PVisualGear; Steps: Longword);
 procedure doStepStraightShot(Gear: PVisualGear; Steps: Longword);
 
-function isSorterActive: boolean; inline;
+function isSorterActive: boolean; 
 procedure initModule;
 
 implementation
@@ -573,7 +573,7 @@
             end;
     currsorter: PVisualGear = nil;
 
-function isSorterActive: boolean; inline;
+function isSorterActive: boolean; 
 begin
     isSorterActive:= currsorter <> nil
 end;
--- a/hedgewars/uVisualGearsList.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uVisualGearsList.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -22,9 +22,9 @@
 interface
 uses uTypes;
 
-function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline;
-function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline;
-function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; inline;
+function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; 
+function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; 
+function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; 
 function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean; Layer: LongInt): PVisualGear;
 procedure DeleteVisualGear(Gear: PVisualGear);
 function  VisualGearByUID(uid : Longword) : PVisualGear;
@@ -39,7 +39,7 @@
 implementation
 uses uCollisions, uFloat, uVariables, uConsts, uTextures, uVisualGearsHandlers, uScript;
 
-function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline;
+function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; 
 begin
     // adjust some visual gear types if underwater
     if CheckCoordInWater(X, Y) and ((Kind = vgtBeeTrace) or (Kind = vgtSmokeTrace) or (Kind = vgtEvilTrace)) then
@@ -48,12 +48,12 @@
     AddVisualGear:= AddVisualGear(X, Y, Kind, 0, false, -1);
 end;
 
-function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline;
+function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; 
 begin
     AddVisualGear:= AddVisualGear(X, Y, Kind, State, false, -1);
 end;
 
-function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; inline;
+function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; 
 begin
     AddVisualGear:= AddVisualGear(X, Y, Kind, State, Critical, -1);
 end;
--- a/hedgewars/uWorld.pas	Thu Aug 24 20:15:40 2023 +0200
+++ b/hedgewars/uWorld.pas	Tue Sep 05 17:02:08 2023 +0200
@@ -1165,14 +1165,14 @@
 
 var preShiftWorldDx: LongInt;
 
-procedure ShiftWorld(Dir: LongInt); inline;
+procedure ShiftWorld(Dir: LongInt); 
 begin
     preShiftWorldDx:= WorldDx;
     Dir := Dir * LongInt(playWidth);
     WorldDx:= WorldDx + Dir;
 end;
 
-procedure UnshiftWorld(); inline;
+procedure UnshiftWorld(); 
 begin
     WorldDx:= preShiftWorldDx;
 end;
--- a/rust/integral-geometry/src/lib.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/integral-geometry/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -164,6 +164,11 @@
     }
 
     #[inline]
+    pub fn is_square(&self) -> bool {
+        self.width == self.height
+    }
+
+    #[inline]
     pub const fn contains(&self, other: Self) -> bool {
         self.width >= other.width && self.height >= other.height
     }
--- a/rust/land2d/Cargo.toml	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/land2d/Cargo.toml	Tue Sep 05 17:02:08 2023 +0200
@@ -2,7 +2,7 @@
 name = "land2d"
 version = "0.1.0"
 authors = ["Andrey Korotaev <a.korotaev@hedgewars.org>"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 vec2d = { path = "../vec2d" }
--- a/rust/land2d/src/lib.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/land2d/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -1,24 +1,25 @@
-use std::{cmp, ops::Index};
-
+use std::{cmp, ops::Index, ops::IndexMut};
+use vec2d::Vec2D;
 use integral_geometry::{ArcPoints, EquidistantPoints, Line, Point, PotSize, Rect, Size, SizeMask};
 
+#[derive(Debug)]
 pub struct Land2D<T> {
     pixels: vec2d::Vec2D<T>,
     play_box: Rect,
     mask: SizeMask,
 }
 
-impl<T: Copy + PartialEq> Land2D<T> {
-    pub fn new(play_size: Size, fill_value: T) -> Self {
+impl<T: Copy + PartialEq + Default> Land2D<T> {
+    pub fn new(play_size: &Size, fill_value: T) -> Self {
         let real_size = play_size.next_power_of_two();
         let top_left = Point::new(
             ((real_size.width() - play_size.width) / 2) as i32,
             (real_size.height() - play_size.height) as i32,
         );
-        let play_box = Rect::from_size(top_left, play_size);
+        let play_box = Rect::from_size(top_left, *play_size);
         Self {
             play_box,
-            pixels: vec2d::Vec2D::new(real_size.size(), fill_value),
+            pixels: vec2d::Vec2D::new(&real_size.size(), fill_value),
             mask: real_size.to_mask(),
         }
     }
@@ -99,6 +100,18 @@
     }
 
     #[inline]
+    pub fn get(&self, y: i32, x: i32) -> T {
+        if self.is_valid_coordinate(x, y) {
+            unsafe {
+                // hey, I just checked that coordinates are valid!
+                *self.pixels.get_unchecked(y as usize, x as usize)
+            }
+        } else {
+            T::default()
+        }
+    }
+
+    #[inline]
     pub fn map_point<U: Default, F: FnOnce(&mut T) -> U>(&mut self, point: Point, f: F) -> U {
         self.map(point.y, point.x, f)
     }
@@ -288,6 +301,30 @@
     }
 }
 
+impl<T> IndexMut<usize> for Land2D<T> {
+    #[inline]
+    fn index_mut(&mut self, row: usize) -> &mut [T] {
+        &mut self.pixels[row]
+    }
+}
+
+impl<T> From<Vec2D<T>> for Land2D<T> {
+    fn from(vec: Vec2D<T>) -> Self {
+        let actual_size = vec.size();
+        let pot_size = actual_size.next_power_of_two();
+
+        assert_eq!(actual_size, pot_size.size());
+
+        let top_left = Point::new(0, 0);
+        let play_box = Rect::from_size(top_left, actual_size);
+        Self {
+            play_box,
+            pixels: vec,
+            mask: pot_size.to_mask(),
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
--- a/rust/landgen/Cargo.toml	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/landgen/Cargo.toml	Tue Sep 05 17:02:08 2023 +0200
@@ -2,9 +2,11 @@
 name = "landgen"
 version = "0.1.0"
 authors = ["Andrey Korotaev <a.korotaev@hedgewars.org>"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 integral-geometry = { path = "../integral-geometry" }
 land2d = { path = "../land2d" }
+vec2d = { path = "../vec2d" }
 itertools = "0.7.8"
+png = "0.17"
--- a/rust/landgen/src/lib.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/landgen/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -1,7 +1,7 @@
-mod outline;
-pub mod outline_template;
-pub mod template_based;
+pub mod outline_template_based;
+pub mod wavefront_collapse;
 
+#[derive(Clone, Copy)]
 pub struct LandGenerationParameters<T> {
     zero: T,
     basic: T,
@@ -10,7 +10,7 @@
     skip_bezier: bool,
 }
 
-impl<T: Copy + PartialEq> LandGenerationParameters<T> {
+impl<T: Copy + PartialEq + Default> LandGenerationParameters<T> {
     pub fn new(
         zero: T,
         basic: T,
@@ -26,20 +26,20 @@
             skip_bezier,
         }
     }
+
+    pub fn zero(&self) -> T {
+        self.zero
+    }
+
+    pub fn basic(&self) -> T {
+        self.basic
+    }
 }
 
 pub trait LandGenerator {
-    fn generate_land<T: Copy + PartialEq, I: Iterator<Item = u32>>(
+    fn generate_land<T: Copy + PartialEq + Default, I: Iterator<Item = u32>>(
         &self,
         parameters: &LandGenerationParameters<T>,
         random_numbers: &mut I,
     ) -> land2d::Land2D<T>;
 }
-
-#[cfg(test)]
-mod tests {
-    #[test]
-    fn it_works() {
-        assert_eq!(2 + 2, 4);
-    }
-}
--- a/rust/landgen/src/outline.rs	Thu Aug 24 20:15:40 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,341 +0,0 @@
-use itertools::Itertools;
-use std::cmp::min;
-
-use integral_geometry::{Line, Point, Polygon, Ray, Rect, Size};
-use land2d::Land2D;
-
-use crate::outline_template::OutlineTemplate;
-
-pub struct OutlinePoints {
-    pub islands: Vec<Polygon>,
-    pub fill_points: Vec<Point>,
-    pub size: Size,
-    pub play_box: Rect,
-    intersections_box: Rect,
-}
-
-impl OutlinePoints {
-    pub fn from_outline_template<I: Iterator<Item = u32>>(
-        outline_template: &OutlineTemplate,
-        play_box: Rect,
-        size: Size,
-        random_numbers: &mut I,
-    ) -> Self {
-        Self {
-            play_box,
-            size,
-            islands: outline_template
-                .islands
-                .iter()
-                .map(|i| {
-                    i.iter()
-                        .zip(random_numbers.tuples())
-                        .map(|(rect, (rnd_a, rnd_b))| {
-                            play_box.top_left() + rect.quotient(rnd_a as usize, rnd_b as usize)
-                        })
-                        .collect::<Vec<_>>()
-                        .into()
-                })
-                .collect(),
-            fill_points: outline_template.fill_points.clone(),
-            intersections_box: Rect::at_origin(size)
-                .with_margin(size.to_square().width as i32 * -2),
-        }
-    }
-
-    pub fn total_len(&self) -> usize {
-        self.islands.iter().map(|i| i.edges_count()).sum::<usize>() + self.fill_points.len()
-    }
-
-    pub fn iter(&self) -> impl Iterator<Item = &Point> {
-        self.islands
-            .iter()
-            .flat_map(|p| p.iter())
-            .chain(self.fill_points.iter())
-    }
-
-    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Point> {
-        self.islands
-            .iter_mut()
-            .flat_map(|i| i.iter_mut())
-            .chain(self.fill_points.iter_mut())
-    }
-
-    fn divide_edge<I: Iterator<Item = u32>>(
-        &self,
-        segment: Line,
-        distance_divisor: u32,
-        random_numbers: &mut I,
-    ) -> Option<Point> {
-        #[inline]
-        fn intersects(ray: &Ray, edge: &Line) -> bool {
-            ray.orientation(edge.start) != ray.orientation(edge.end)
-        }
-
-        #[inline]
-        fn solve_intersection(
-            intersections_box: &Rect,
-            ray: &Ray,
-            edge: &Line,
-        ) -> Option<(i32, u32)> {
-            let edge_dir = edge.scaled_direction();
-            let aqpb = ray.direction.cross(edge_dir) as i64;
-
-            if aqpb != 0 {
-                let mut iy = ((((edge.start.x - ray.start.x) as i64 * ray.direction.y as i64
-                    + ray.start.y as i64 * ray.direction.x as i64)
-                    * edge_dir.y as i64
-                    - edge.start.y as i64 * edge_dir.x as i64 * ray.direction.y as i64)
-                    / aqpb) as i32;
-
-                // is there better way to do it?
-                if iy < intersections_box.top() {
-                    iy = intersections_box.top();
-                } else if iy > intersections_box.bottom() {
-                    iy = intersections_box.bottom();
-                }
-
-                let ix = if ray.direction.y.abs() > edge_dir.y.abs() {
-                    ray.start.x + ray.direction.cotangent_mul(iy - ray.start.y)
-                } else {
-                    edge.start.x + edge_dir.cotangent_mul(iy - edge.start.y)
-                };
-
-                let intersection_point = Point::new(ix, iy).clamp(intersections_box);
-                let diff_point = ray.start - intersection_point;
-                let t = ray.direction.dot(diff_point);
-
-                if diff_point.max_norm() >= std::i16::MAX as i32 {
-                    Some((t, std::i32::MAX as u32))
-                } else {
-                    let d = diff_point.integral_norm();
-
-                    Some((t, d))
-                }
-            } else {
-                None
-            }
-        }
-
-        let min_distance = 40;
-        // new point should fall inside this box
-        let map_box = self.play_box.with_margin(min_distance);
-
-        let normal = segment.scaled_normal();
-        let normal_len = normal.integral_norm();
-        let mid_point = segment.center();
-
-        if (normal_len < min_distance as u32 * 3) || !map_box.contains_inside(mid_point) {
-            return None;
-        }
-
-        let normal_ray = Ray::new(mid_point, normal);
-        let mut dist_left = (self.size.width + self.size.height) as u32;
-        let mut dist_right = dist_left;
-
-        // find distances to map borders
-        if normal.x != 0 {
-            // where the normal line intersects the left map border
-            let left_intersection = Point::new(
-                map_box.left(),
-                mid_point.y + normal.tangent_mul(map_box.left() - mid_point.x),
-            );
-            dist_left = (mid_point - left_intersection).integral_norm();
-
-            // same for the right border
-            let right_intersection = Point::new(
-                map_box.right(),
-                mid_point.y + normal.tangent_mul(map_box.right() - mid_point.x),
-            );
-            dist_right = (mid_point - right_intersection).integral_norm();
-
-            if normal.x > 0 {
-                std::mem::swap(&mut dist_left, &mut dist_right);
-            }
-        }
-
-        if normal.y != 0 {
-            // where the normal line intersects the top map border
-            let top_intersection = Point::new(
-                mid_point.x + normal.cotangent_mul(map_box.top() - mid_point.y),
-                map_box.top(),
-            );
-            let dl = (mid_point - top_intersection).integral_norm();
-
-            // same for the bottom border
-            let bottom_intersection = Point::new(
-                mid_point.x + normal.cotangent_mul(map_box.bottom() - mid_point.y),
-                map_box.bottom(),
-            );
-            let dr = (mid_point - bottom_intersection).integral_norm();
-
-            if normal.y < 0 {
-                dist_left = min(dist_left, dl);
-                dist_right = min(dist_right, dr);
-            } else {
-                dist_left = min(dist_left, dr);
-                dist_right = min(dist_right, dl);
-            }
-        }
-
-        // now go through all other segments
-        for s in self.segments_iter() {
-            if s != segment {
-                if intersects(&normal_ray, &s) {
-                    if let Some((t, d)) =
-                        solve_intersection(&self.intersections_box, &normal_ray, &s)
-                    {
-                        if t > 0 {
-                            dist_right = min(dist_right, d);
-                        } else {
-                            dist_left = min(dist_left, d);
-                        }
-                    }
-                }
-            }
-        }
-
-        // go through all points, including fill points
-        for pi in self.iter().cloned() {
-            if pi != segment.start && pi != segment.end {
-                if intersects(&pi.ray_with_dir(normal), &segment) {
-                    // ray from segment.start
-                    if let Some((t, d)) = solve_intersection(
-                        &self.intersections_box,
-                        &normal_ray,
-                        &segment.start.line_to(pi),
-                    ) {
-                        if t > 0 {
-                            dist_right = min(dist_right, d);
-                        } else {
-                            dist_left = min(dist_left, d);
-                        }
-                    }
-
-                    // ray from segment.end
-                    if let Some((t, d)) = solve_intersection(
-                        &self.intersections_box,
-                        &normal_ray,
-                        &segment.end.line_to(pi),
-                    ) {
-                        if t > 0 {
-                            dist_right = min(dist_right, d);
-                        } else {
-                            dist_left = min(dist_left, d);
-                        }
-                    }
-                }
-            }
-        }
-
-        let max_dist = normal_len * 100 / distance_divisor;
-        dist_left = min(dist_left, max_dist);
-        dist_right = min(dist_right, max_dist);
-
-        if dist_right + dist_left < min_distance as u32 * 2 + 10 {
-            // limits are too narrow, just divide
-            Some(mid_point)
-        } else {
-            // select distance within [-dist_right; dist_left], keeping min_distance in mind
-            let d = -(dist_right as i32)
-                + min_distance
-                + random_numbers.next().unwrap() as i32
-                    % (dist_right as i32 + dist_left as i32 - min_distance * 2);
-
-            Some(mid_point + normal * d / normal_len as i32)
-        }
-    }
-
-    fn divide_edges<I: Iterator<Item = u32>>(
-        &mut self,
-        distance_divisor: u32,
-        random_numbers: &mut I,
-    ) {
-        for is in 0..self.islands.len() {
-            let mut i = 0;
-            while i < self.islands[is].edges_count() {
-                let segment = self.islands[is].get_edge(i);
-                if let Some(new_point) = self.divide_edge(segment, distance_divisor, random_numbers)
-                {
-                    self.islands[is].split_edge(i, new_point);
-                    i += 2;
-                } else {
-                    i += 1;
-                }
-            }
-        }
-    }
-
-    pub fn bezierize(&mut self, segments_number: u32) {
-        for island in &mut self.islands {
-            island.bezierize(segments_number);
-        }
-    }
-
-    pub fn distort<I: Iterator<Item = u32>>(
-        &mut self,
-        distance_divisor: u32,
-        random_numbers: &mut I,
-    ) {
-        loop {
-            let old_len = self.total_len();
-            self.divide_edges(distance_divisor, random_numbers);
-
-            if self.total_len() == old_len {
-                break;
-            }
-        }
-    }
-
-    pub fn draw<T: Copy + PartialEq>(&self, land: &mut Land2D<T>, value: T) {
-        for segment in self.segments_iter() {
-            land.draw_line(segment, value);
-        }
-    }
-
-    fn segments_iter<'a>(&'a self) -> impl Iterator<Item = Line> + 'a {
-        self.islands.iter().flat_map(|p| p.iter_edges())
-    }
-
-    pub fn mirror(&mut self) {
-        let r = self.size.width as i32 - 1;
-
-        self.iter_mut().for_each(|p| p.x = r - p.x);
-    }
-
-    pub fn flip(&mut self) {
-        let t = self.size.height as i32 - 1;
-
-        self.iter_mut().for_each(|p| p.y = t - p.y);
-    }
-}
-
-#[test]
-fn points_test() {
-    let size = Size::square(100);
-    let mut points = OutlinePoints {
-        islands: vec![
-            Polygon::new(&[Point::new(0, 0), Point::new(20, 0), Point::new(30, 30)]),
-            Polygon::new(&[Point::new(10, 15), Point::new(15, 20), Point::new(20, 15)]),
-        ],
-        fill_points: vec![Point::new(1, 1)],
-        play_box: Rect::at_origin(size).with_margin(10),
-        size: Size::square(100),
-        intersections_box: Rect::at_origin(size),
-    };
-
-    let segments: Vec<Line> = points.segments_iter().collect();
-    assert_eq!(
-        segments.first(),
-        Some(&Line::new(Point::new(0, 0), Point::new(20, 0)))
-    );
-    assert_eq!(
-        segments.last(),
-        Some(&Line::new(Point::new(20, 15), Point::new(10, 15)))
-    );
-
-    points.iter_mut().for_each(|p| p.x = 2);
-
-    assert_eq!(points.fill_points[0].x, 2);
-    assert_eq!(points.islands[0].get_edge(0).start.x, 2);
-}
--- a/rust/landgen/src/outline_template.rs	Thu Aug 24 20:15:40 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-use integral_geometry::{Point, Rect, Size};
-
-#[derive(Clone, Debug)]
-pub struct OutlineTemplate {
-    pub islands: Vec<Vec<Rect>>,
-    pub fill_points: Vec<Point>,
-    pub size: Size,
-    pub can_flip: bool,
-    pub can_invert: bool,
-    pub can_mirror: bool,
-    pub is_negative: bool,
-}
-
-impl OutlineTemplate {
-    pub fn new(size: Size) -> Self {
-        OutlineTemplate {
-            size,
-            islands: Vec::new(),
-            fill_points: Vec::new(),
-            can_flip: false,
-            can_invert: false,
-            can_mirror: false,
-            is_negative: false,
-        }
-    }
-
-    pub fn flippable(self) -> Self {
-        Self {
-            can_flip: true,
-            ..self
-        }
-    }
-
-    pub fn mirrorable(self) -> Self {
-        Self {
-            can_mirror: true,
-            ..self
-        }
-    }
-
-    pub fn invertable(self) -> Self {
-        Self {
-            can_invert: true,
-            ..self
-        }
-    }
-
-    pub fn negative(self) -> Self {
-        Self {
-            is_negative: true,
-            ..self
-        }
-    }
-
-    pub fn with_fill_points(self, fill_points: Vec<Point>) -> Self {
-        Self {
-            fill_points,
-            ..self
-        }
-    }
-
-    pub fn with_islands(self, islands: Vec<Vec<Rect>>) -> Self {
-        Self { islands, ..self }
-    }
-
-    pub fn add_fill_points(mut self, points: &[Point]) -> Self {
-        self.fill_points.extend_from_slice(points);
-        self
-    }
-
-    pub fn add_island(mut self, island: &[Rect]) -> Self {
-        self.islands.push(island.into());
-        self
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/outline_template_based/mod.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,3 @@
+mod outline;
+pub mod outline_template;
+pub mod template_based;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/outline_template_based/outline.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,338 @@
+use itertools::Itertools;
+use std::cmp::min;
+
+use integral_geometry::{Line, Point, Polygon, Ray, Rect, Size};
+use land2d::Land2D;
+
+use super::outline_template::OutlineTemplate;
+
+pub struct OutlinePoints {
+    pub islands: Vec<Polygon>,
+    pub fill_points: Vec<Point>,
+    pub size: Size,
+    pub play_box: Rect,
+    intersections_box: Rect,
+}
+
+impl OutlinePoints {
+    pub fn from_outline_template<I: Iterator<Item = u32>>(
+        outline_template: &OutlineTemplate,
+        play_box: Rect,
+        size: Size,
+        random_numbers: &mut I,
+    ) -> Self {
+        Self {
+            play_box,
+            size,
+            islands: outline_template
+                .islands
+                .iter()
+                .map(|i| {
+                    i.iter()
+                        .zip(random_numbers.tuples())
+                        .map(|(rect, (rnd_a, rnd_b))| {
+                            play_box.top_left() + rect.quotient(rnd_a as usize, rnd_b as usize)
+                        })
+                        .collect::<Vec<_>>()
+                        .into()
+                })
+                .collect(),
+            fill_points: outline_template.fill_points.clone(),
+            intersections_box: Rect::at_origin(size)
+                .with_margin(size.to_square().width as i32 * -2),
+        }
+    }
+
+    pub fn total_len(&self) -> usize {
+        self.islands.iter().map(|i| i.edges_count()).sum::<usize>() + self.fill_points.len()
+    }
+
+    pub fn iter(&self) -> impl Iterator<Item = &Point> {
+        self.islands
+            .iter()
+            .flat_map(|p| p.iter())
+            .chain(self.fill_points.iter())
+    }
+
+    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Point> {
+        self.islands
+            .iter_mut()
+            .flat_map(|i| i.iter_mut())
+            .chain(self.fill_points.iter_mut())
+    }
+
+    fn divide_edge<I: Iterator<Item = u32>>(
+        &self,
+        segment: Line,
+        distance_divisor: u32,
+        random_numbers: &mut I,
+    ) -> Option<Point> {
+        #[inline]
+        fn intersects(ray: &Ray, edge: &Line) -> bool {
+            ray.orientation(edge.start) != ray.orientation(edge.end)
+        }
+
+        #[inline]
+        fn solve_intersection(
+            intersections_box: &Rect,
+            ray: &Ray,
+            edge: &Line,
+        ) -> Option<(i32, u32)> {
+            let edge_dir = edge.scaled_direction();
+            let aqpb = ray.direction.cross(edge_dir) as i64;
+
+            if aqpb != 0 {
+                let mut iy = ((((edge.start.x - ray.start.x) as i64 * ray.direction.y as i64
+                    + ray.start.y as i64 * ray.direction.x as i64)
+                    * edge_dir.y as i64
+                    - edge.start.y as i64 * edge_dir.x as i64 * ray.direction.y as i64)
+                    / aqpb) as i32;
+
+                // is there better way to do it?
+                if iy < intersections_box.top() {
+                    iy = intersections_box.top();
+                } else if iy > intersections_box.bottom() {
+                    iy = intersections_box.bottom();
+                }
+
+                let ix = if ray.direction.y.abs() > edge_dir.y.abs() {
+                    ray.start.x + ray.direction.cotangent_mul(iy - ray.start.y)
+                } else {
+                    edge.start.x + edge_dir.cotangent_mul(iy - edge.start.y)
+                };
+
+                let intersection_point = Point::new(ix, iy).clamp(intersections_box);
+                let diff_point = ray.start - intersection_point;
+                let t = ray.direction.dot(diff_point);
+
+                if diff_point.max_norm() >= std::i16::MAX as i32 {
+                    Some((t, std::i32::MAX as u32))
+                } else {
+                    let d = diff_point.integral_norm();
+
+                    Some((t, d))
+                }
+            } else {
+                None
+            }
+        }
+
+        let min_distance = 40;
+        // new point should fall inside this box
+        let map_box = self.play_box.with_margin(min_distance);
+
+        let normal = segment.scaled_normal();
+        let normal_len = normal.integral_norm();
+        let mid_point = segment.center();
+
+        if (normal_len < min_distance as u32 * 3) || !map_box.contains_inside(mid_point) {
+            return None;
+        }
+
+        let normal_ray = Ray::new(mid_point, normal);
+        let mut dist_left = (self.size.width + self.size.height) as u32;
+        let mut dist_right = dist_left;
+
+        // find distances to map borders
+        if normal.x != 0 {
+            // where the normal line intersects the left map border
+            let left_intersection = Point::new(
+                map_box.left(),
+                mid_point.y + normal.tangent_mul(map_box.left() - mid_point.x),
+            );
+            dist_left = (mid_point - left_intersection).integral_norm();
+
+            // same for the right border
+            let right_intersection = Point::new(
+                map_box.right(),
+                mid_point.y + normal.tangent_mul(map_box.right() - mid_point.x),
+            );
+            dist_right = (mid_point - right_intersection).integral_norm();
+
+            if normal.x > 0 {
+                std::mem::swap(&mut dist_left, &mut dist_right);
+            }
+        }
+
+        if normal.y != 0 {
+            // where the normal line intersects the top map border
+            let top_intersection = Point::new(
+                mid_point.x + normal.cotangent_mul(map_box.top() - mid_point.y),
+                map_box.top(),
+            );
+            let dl = (mid_point - top_intersection).integral_norm();
+
+            // same for the bottom border
+            let bottom_intersection = Point::new(
+                mid_point.x + normal.cotangent_mul(map_box.bottom() - mid_point.y),
+                map_box.bottom(),
+            );
+            let dr = (mid_point - bottom_intersection).integral_norm();
+
+            if normal.y < 0 {
+                dist_left = min(dist_left, dl);
+                dist_right = min(dist_right, dr);
+            } else {
+                dist_left = min(dist_left, dr);
+                dist_right = min(dist_right, dl);
+            }
+        }
+
+        // now go through all other segments
+        for s in self.segments_iter() {
+            if s != segment && intersects(&normal_ray, &s) {
+                if let Some((t, d)) = solve_intersection(&self.intersections_box, &normal_ray, &s) {
+                    if t > 0 {
+                        dist_right = min(dist_right, d);
+                    } else {
+                        dist_left = min(dist_left, d);
+                    }
+                }
+            }
+        }
+
+        // go through all points, including fill points
+        for pi in self.iter().cloned() {
+            if pi != segment.start
+                && pi != segment.end
+                && intersects(&pi.ray_with_dir(normal), &segment)
+            {
+                // ray from segment.start
+                if let Some((t, d)) = solve_intersection(
+                    &self.intersections_box,
+                    &normal_ray,
+                    &segment.start.line_to(pi),
+                ) {
+                    if t > 0 {
+                        dist_right = min(dist_right, d);
+                    } else {
+                        dist_left = min(dist_left, d);
+                    }
+                }
+
+                // ray from segment.end
+                if let Some((t, d)) = solve_intersection(
+                    &self.intersections_box,
+                    &normal_ray,
+                    &segment.end.line_to(pi),
+                ) {
+                    if t > 0 {
+                        dist_right = min(dist_right, d);
+                    } else {
+                        dist_left = min(dist_left, d);
+                    }
+                }
+            }
+        }
+
+        let max_dist = normal_len * 100 / distance_divisor;
+        dist_left = min(dist_left, max_dist);
+        dist_right = min(dist_right, max_dist);
+
+        if dist_right + dist_left < min_distance as u32 * 2 + 10 {
+            // limits are too narrow, just divide
+            Some(mid_point)
+        } else {
+            // select distance within [-dist_right; dist_left], keeping min_distance in mind
+            let d = -(dist_right as i32)
+                + min_distance
+                + random_numbers.next().unwrap() as i32
+                    % (dist_right as i32 + dist_left as i32 - min_distance * 2);
+
+            Some(mid_point + normal * d / normal_len as i32)
+        }
+    }
+
+    fn divide_edges<I: Iterator<Item = u32>>(
+        &mut self,
+        distance_divisor: u32,
+        random_numbers: &mut I,
+    ) {
+        for is in 0..self.islands.len() {
+            let mut i = 0;
+            while i < self.islands[is].edges_count() {
+                let segment = self.islands[is].get_edge(i);
+                if let Some(new_point) = self.divide_edge(segment, distance_divisor, random_numbers)
+                {
+                    self.islands[is].split_edge(i, new_point);
+                    i += 2;
+                } else {
+                    i += 1;
+                }
+            }
+        }
+    }
+
+    pub fn bezierize(&mut self, segments_number: u32) {
+        for island in &mut self.islands {
+            island.bezierize(segments_number);
+        }
+    }
+
+    pub fn distort<I: Iterator<Item = u32>>(
+        &mut self,
+        distance_divisor: u32,
+        random_numbers: &mut I,
+    ) {
+        loop {
+            let old_len = self.total_len();
+            self.divide_edges(distance_divisor, random_numbers);
+
+            if self.total_len() == old_len {
+                break;
+            }
+        }
+    }
+
+    pub fn draw<T: Copy + PartialEq + Default>(&self, land: &mut Land2D<T>, value: T) {
+        for segment in self.segments_iter() {
+            land.draw_line(segment, value);
+        }
+    }
+
+    fn segments_iter<'a>(&'a self) -> impl Iterator<Item = Line> + 'a {
+        self.islands.iter().flat_map(|p| p.iter_edges())
+    }
+
+    pub fn mirror(&mut self) {
+        let r = self.size.width as i32 - 1;
+
+        self.iter_mut().for_each(|p| p.x = r - p.x);
+    }
+
+    pub fn flip(&mut self) {
+        let t = self.size.height as i32 - 1;
+
+        self.iter_mut().for_each(|p| p.y = t - p.y);
+    }
+}
+
+#[test]
+fn points_test() {
+    let size = Size::square(100);
+    let mut points = OutlinePoints {
+        islands: vec![
+            Polygon::new(&[Point::new(0, 0), Point::new(20, 0), Point::new(30, 30)]),
+            Polygon::new(&[Point::new(10, 15), Point::new(15, 20), Point::new(20, 15)]),
+        ],
+        fill_points: vec![Point::new(1, 1)],
+        play_box: Rect::at_origin(size).with_margin(10),
+        size: Size::square(100),
+        intersections_box: Rect::at_origin(size),
+    };
+
+    let segments: Vec<Line> = points.segments_iter().collect();
+    assert_eq!(
+        segments.first(),
+        Some(&Line::new(Point::new(0, 0), Point::new(20, 0)))
+    );
+    assert_eq!(
+        segments.last(),
+        Some(&Line::new(Point::new(20, 15), Point::new(10, 15)))
+    );
+
+    points.iter_mut().for_each(|p| p.x = 2);
+
+    assert_eq!(points.fill_points[0].x, 2);
+    assert_eq!(points.islands[0].get_edge(0).start.x, 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/outline_template_based/outline_template.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,75 @@
+use integral_geometry::{Point, Rect, Size};
+
+#[derive(Clone, Debug)]
+pub struct OutlineTemplate {
+    pub islands: Vec<Vec<Rect>>,
+    pub fill_points: Vec<Point>,
+    pub size: Size,
+    pub can_flip: bool,
+    pub can_invert: bool,
+    pub can_mirror: bool,
+    pub is_negative: bool,
+}
+
+impl OutlineTemplate {
+    pub fn new(size: Size) -> Self {
+        OutlineTemplate {
+            size,
+            islands: Vec::new(),
+            fill_points: Vec::new(),
+            can_flip: false,
+            can_invert: false,
+            can_mirror: false,
+            is_negative: false,
+        }
+    }
+
+    pub fn flippable(self) -> Self {
+        Self {
+            can_flip: true,
+            ..self
+        }
+    }
+
+    pub fn mirrorable(self) -> Self {
+        Self {
+            can_mirror: true,
+            ..self
+        }
+    }
+
+    pub fn invertable(self) -> Self {
+        Self {
+            can_invert: true,
+            ..self
+        }
+    }
+
+    pub fn negative(self) -> Self {
+        Self {
+            is_negative: true,
+            ..self
+        }
+    }
+
+    pub fn with_fill_points(self, fill_points: Vec<Point>) -> Self {
+        Self {
+            fill_points,
+            ..self
+        }
+    }
+
+    pub fn with_islands(self, islands: Vec<Vec<Rect>>) -> Self {
+        Self { islands, ..self }
+    }
+
+    pub fn add_fill_points(mut self, points: &[Point]) -> Self {
+        self.fill_points.extend_from_slice(points);
+        self
+    }
+
+    pub fn add_island(mut self, island: &[Rect]) -> Self {
+        self.islands.push(island.into());
+        self
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/outline_template_based/template_based.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,66 @@
+use super::{outline::OutlinePoints, outline_template::OutlineTemplate};
+use crate::{LandGenerationParameters, LandGenerator};
+use land2d::Land2D;
+
+pub struct TemplatedLandGenerator {
+    outline_template: OutlineTemplate,
+}
+
+impl TemplatedLandGenerator {
+    pub fn new(outline_template: OutlineTemplate) -> Self {
+        Self { outline_template }
+    }
+}
+
+impl LandGenerator for TemplatedLandGenerator {
+    fn generate_land<T: Copy + PartialEq + Default, I: Iterator<Item = u32>>(
+        &self,
+        parameters: &LandGenerationParameters<T>,
+        random_numbers: &mut I,
+    ) -> Land2D<T> {
+        let mut land = Land2D::new(&self.outline_template.size, parameters.basic);
+
+        let mut points = OutlinePoints::from_outline_template(
+            &self.outline_template,
+            land.play_box(),
+            land.size().size(),
+            random_numbers,
+        );
+
+        // mirror
+        if self.outline_template.can_mirror {
+            if let Some(b) = random_numbers.next() {
+                if b & 1 != 0 {
+                    points.mirror();
+                }
+            }
+        }
+
+        // flip
+        if self.outline_template.can_flip {
+            if let Some(b) = random_numbers.next() {
+                if b & 1 != 0 {
+                    points.flip();
+                }
+            }
+        }
+
+        if !parameters.skip_distort {
+            points.distort(parameters.distance_divisor, random_numbers);
+        }
+
+        if !parameters.skip_bezier {
+            points.bezierize(5);
+        }
+
+        points.draw(&mut land, parameters.zero);
+
+        for p in &points.fill_points {
+            land.fill(*p, parameters.zero, parameters.zero)
+        }
+
+        points.draw(&mut land, parameters.basic);
+
+        land
+    }
+}
--- a/rust/landgen/src/template_based.rs	Thu Aug 24 20:15:40 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-use crate::{
-    outline::OutlinePoints, outline_template::OutlineTemplate, LandGenerationParameters,
-    LandGenerator,
-};
-use integral_geometry::{Point, Size};
-use land2d::Land2D;
-
-pub struct TemplatedLandGenerator {
-    outline_template: OutlineTemplate,
-}
-
-impl TemplatedLandGenerator {
-    pub fn new(outline_template: OutlineTemplate) -> Self {
-        Self { outline_template }
-    }
-}
-
-impl LandGenerator for TemplatedLandGenerator {
-    fn generate_land<T: Copy + PartialEq, I: Iterator<Item = u32>>(
-        &self,
-        parameters: &LandGenerationParameters<T>,
-        random_numbers: &mut I,
-    ) -> Land2D<T> {
-        let mut land = Land2D::new(self.outline_template.size, parameters.basic);
-
-        let mut points = OutlinePoints::from_outline_template(
-            &self.outline_template,
-            land.play_box(),
-            land.size().size(),
-            random_numbers,
-        );
-
-        // mirror
-        if self.outline_template.can_mirror {
-            if let Some(b) = random_numbers.next() {
-                if b & 1 != 0 {
-                    points.mirror();
-                }
-            }
-        }
-
-        // flip
-        if self.outline_template.can_flip {
-            if let Some(b) = random_numbers.next() {
-                if b & 1 != 0 {
-                    points.flip();
-                }
-            }
-        }
-
-        if !parameters.skip_distort {
-            points.distort(parameters.distance_divisor, random_numbers);
-        }
-
-        if !parameters.skip_bezier {
-            points.bezierize(5);
-        }
-
-        points.draw(&mut land, parameters.zero);
-
-        for p in &points.fill_points {
-            land.fill(*p, parameters.zero, parameters.zero)
-        }
-
-        points.draw(&mut land, parameters.basic);
-
-        land
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/wavefront_collapse/generator.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,336 @@
+use super::tile_image::{Edge, TileImage};
+use super::wavefront_collapse::{CollapseRule, Tile, WavefrontCollapse};
+use crate::{LandGenerationParameters, LandGenerator};
+use integral_geometry::Size;
+use png::Decoder;
+use std::collections::HashSet;
+use std::fs::File;
+use std::io::{BufReader, Result};
+use std::path::Path;
+
+#[derive(Clone)]
+pub struct EdgeDescription {
+    pub name: String,
+    pub reversed: Option<bool>,
+    pub symmetrical: Option<bool>,
+}
+
+#[derive(Clone)]
+pub struct EdgesDescription {
+    pub top: EdgeDescription,
+    pub right: EdgeDescription,
+    pub bottom: EdgeDescription,
+    pub left: EdgeDescription,
+}
+
+#[derive(Clone)]
+pub struct TileDescription {
+    pub name: String,
+    pub edges: EdgesDescription,
+    pub is_negative: Option<bool>,
+    pub can_flip: Option<bool>,
+    pub can_mirror: Option<bool>,
+    pub can_rotate90: Option<bool>,
+    pub can_rotate180: Option<bool>,
+    pub can_rotate270: Option<bool>,
+}
+
+#[derive(Clone)]
+pub struct NonStrictEdgesDescription {
+    pub top: Option<EdgeDescription>,
+    pub right: Option<EdgeDescription>,
+    pub bottom: Option<EdgeDescription>,
+    pub left: Option<EdgeDescription>,
+}
+
+#[derive(Clone)]
+pub struct TemplateDescription {
+    pub size: Size,
+    pub tiles: Vec<TileDescription>,
+    pub edges: NonStrictEdgesDescription,
+    pub wrap: bool,
+}
+
+pub struct WavefrontCollapseLandGenerator {
+    pub template: TemplateDescription,
+}
+
+impl WavefrontCollapseLandGenerator {
+    pub fn new(template: TemplateDescription) -> Self {
+        Self { template }
+    }
+
+    fn load_image_tiles<T: Copy + PartialEq + Default>(
+        parameters: &LandGenerationParameters<T>,
+        tile_description: &TileDescription,
+    ) -> Result<Vec<TileImage<T, String>>> {
+        let mut result = Vec::new();
+
+        let file = File::open(
+            Path::new("../share/hedgewars/Data/Tiles")
+                .join(&tile_description.name)
+                .as_path(),
+        )?;
+        let decoder = Decoder::new(BufReader::new(file));
+        let mut reader = decoder.read_info().unwrap();
+
+        let info = reader.info();
+        let mut tiles_image = vec2d::Vec2D::new(
+            &Size::new(info.width as usize, info.height as usize),
+            parameters.zero,
+        );
+
+        let mut buf = vec![0; reader.output_buffer_size()];
+        let info = reader.next_frame(&mut buf).unwrap();
+        let bytes = &buf[..info.buffer_size()];
+
+        let mut tiles_image_pixels = tiles_image.as_mut_slice().iter_mut();
+
+        let (zero, basic) = if tile_description.is_negative.unwrap_or_default() {
+            (parameters.basic(), parameters.zero())
+        } else {
+            (parameters.zero(), parameters.basic())
+        };
+
+        match info.color_type.samples() {
+            1 => {
+                for line in bytes.chunks_exact(info.line_size) {
+                    for value in line.iter() {
+                        *tiles_image_pixels
+                            .next()
+                            .expect("vec2d size matching image dimensions") =
+                            if *value == 0 { zero } else { basic };
+                    }
+                }
+            }
+            a => {
+                for line in bytes.chunks_exact(info.line_size) {
+                    for value in line.chunks_exact(a) {
+                        print!("{:?},", value);
+                        *tiles_image_pixels
+                            .next()
+                            .expect("vec2d size matching image dimensions") =
+                            if value[0] == 0u8 { zero } else { basic };
+                    }
+                }
+            }
+        }
+
+        let [top_edge, right_edge, bottom_edge, left_edge]: [Edge<String>; 4] = [
+            (&tile_description.edges.top).into(),
+            (&tile_description.edges.right).into(),
+            (&tile_description.edges.bottom).into(),
+            (&tile_description.edges.left).into(),
+        ];
+
+        let tile =
+            TileImage::<T, String>::new(tiles_image, top_edge, right_edge, bottom_edge, left_edge);
+
+        result.push(tile.clone());
+
+        if tile_description.can_flip.unwrap_or_default() {
+            result.push(tile.flipped());
+        }
+        if tile_description.can_mirror.unwrap_or_default() {
+            result.push(tile.mirrored());
+        }
+        if tile_description.can_flip.unwrap_or_default()
+            && tile_description.can_mirror.unwrap_or_default()
+        {
+            result.push(tile.mirrored().flipped());
+        }
+
+        if tile_description.can_rotate90.unwrap_or_default() {
+            result.push(tile.rotated90());
+        }
+        if tile_description.can_rotate180.unwrap_or_default() {
+            result.push(tile.rotated180());
+        }
+        if tile_description.can_rotate270.unwrap_or_default() {
+            result.push(tile.rotated270());
+        }
+
+        Ok(result)
+    }
+
+    pub fn load_template<T: Copy + PartialEq + Default>(
+        &self,
+        parameters: &LandGenerationParameters<T>,
+    ) -> Vec<TileImage<T, String>> {
+        let mut result = Vec::new();
+
+        for tile_description in self.template.tiles.iter() {
+            if let Ok(mut tiles) = Self::load_image_tiles(parameters, tile_description) {
+                result.append(&mut tiles);
+            }
+        }
+
+        result
+    }
+
+    pub fn build_rules<T: Copy + PartialEq + Default>(
+        &self,
+        tiles: &[TileImage<T, String>],
+    ) -> Vec<CollapseRule> {
+        let [grid_top_edge, grid_right_edge, grid_bottom_edge, grid_left_edge]: [Option<
+            Edge<String>,
+        >; 4] = [
+            self.template.edges.top.as_ref(),
+            self.template.edges.right.as_ref(),
+            self.template.edges.bottom.as_ref(),
+            self.template.edges.left.as_ref(),
+        ]
+        .map(|opt| opt.map(|d| d.into()));
+
+        let mut rules = Vec::<CollapseRule>::new();
+
+        let default_connection = HashSet::from_iter(vec![Tile::Empty].into_iter());
+        for (i, tile) in tiles.iter().enumerate() {
+            let mut right = default_connection.clone();
+            let mut bottom = default_connection.clone();
+            let mut left = default_connection.clone();
+            let mut top = default_connection.clone();
+
+            // compatibility with grid edges
+            if grid_top_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.top_edge()))
+                .unwrap_or(true)
+            {
+                top.insert(Tile::Outside);
+            }
+            if grid_right_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.right_edge()))
+                .unwrap_or(true)
+            {
+                right.insert(Tile::Outside);
+            }
+            if grid_bottom_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.bottom_edge()))
+                .unwrap_or(true)
+            {
+                bottom.insert(Tile::Outside);
+            }
+            if grid_left_edge
+                .as_ref()
+                .map(|e| e.is_compatible(tile.left_edge()))
+                .unwrap_or(true)
+            {
+                left.insert(Tile::Outside);
+            }
+
+            // compatibility with itself
+            if tile.left_edge().is_compatible(tile.right_edge()) {
+                left.insert(Tile::Numbered(i));
+                right.insert(Tile::Numbered(i));
+            }
+
+            if tile.top_edge().is_compatible(tile.bottom_edge()) {
+                top.insert(Tile::Numbered(i));
+                bottom.insert(Tile::Numbered(i));
+            }
+
+            // compatibility with previously defined tiles
+            for p in 0..i {
+                if tiles[p].left_edge().is_compatible(tile.right_edge()) {
+                    rules[p].left.insert(Tile::Numbered(i));
+                    right.insert(Tile::Numbered(p));
+                }
+
+                if tiles[p].right_edge().is_compatible(tile.left_edge()) {
+                    rules[p].right.insert(Tile::Numbered(i));
+                    left.insert(Tile::Numbered(p));
+                }
+
+                if tiles[p].top_edge().is_compatible(tile.bottom_edge()) {
+                    rules[p].top.insert(Tile::Numbered(i));
+                    bottom.insert(Tile::Numbered(p));
+                }
+
+                if tiles[p].bottom_edge().is_compatible(tile.top_edge()) {
+                    rules[p].bottom.insert(Tile::Numbered(i));
+                    top.insert(Tile::Numbered(p));
+                }
+            }
+
+            rules.push(CollapseRule {
+                tile: Tile::Numbered(i),
+                top,
+                right,
+                bottom,
+                left,
+            });
+        }
+
+        rules
+    }
+}
+
+impl LandGenerator for WavefrontCollapseLandGenerator {
+    fn generate_land<T: Copy + PartialEq + Default, I: Iterator<Item = u32>>(
+        &self,
+        parameters: &LandGenerationParameters<T>,
+        random_numbers: &mut I,
+    ) -> land2d::Land2D<T> {
+        let tiles = self.load_template(parameters);
+        let rules = self.build_rules(&tiles);
+
+        let mut wfc = WavefrontCollapse::new(self.template.wrap);
+        wfc.set_rules(rules);
+
+        let wfc_size = if let Some(first_tile) = tiles.first() {
+            let tile_size = first_tile.size();
+
+            Size::new(
+                self.template.size.width / tile_size.width,
+                self.template.size.height / tile_size.height,
+            )
+        } else {
+            Size::new(1, 1)
+        };
+
+        wfc.generate_map(&wfc_size, |_| {}, random_numbers);
+
+        // render tiles into resulting land array
+        let mut result = land2d::Land2D::new(&self.template.size, parameters.zero);
+        let offset_y = result.height() - result.play_height();
+        let offset_x = (result.width() - result.play_width()) / 2;
+
+        for row in 0..wfc_size.height {
+            for column in 0..wfc_size.width {
+                if let Some(Tile::Numbered(tile_index)) = wfc.grid().get(row, column) {
+                    let tile = &tiles[*tile_index];
+
+                    for tile_row in 0..tile.size().height {
+                        for tile_column in 0..tile.size().width {
+                            result.map(
+                                (row * tile.size().height + tile_row + offset_y) as i32,
+                                (column * tile.size().width + tile_column + offset_x) as i32,
+                                |p| {
+                                    *p =
+                                        *tile.get(tile_row, tile_column).unwrap_or(&parameters.zero)
+                                },
+                            );
+                        }
+                    }
+                }
+            }
+        }
+
+        result
+    }
+}
+
+impl From<&EdgeDescription> for Edge<String> {
+    fn from(val: &EdgeDescription) -> Self {
+        let edge = Edge::new(val.name.clone(), val.symmetrical.unwrap_or_default());
+
+        if val.reversed.unwrap_or_default() {
+            edge.reversed()
+        } else {
+            edge
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/wavefront_collapse/mod.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,4 @@
+pub mod generator;
+mod tile_image;
+mod transform;
+mod wavefront_collapse;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/wavefront_collapse/tile_image.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,242 @@
+use super::transform::Transform;
+use integral_geometry::Size;
+use std::rc::Rc;
+use vec2d::Vec2D;
+
+#[derive(PartialEq, Clone, Debug)]
+pub struct Edge<I: PartialEq + Clone> {
+    id: I,
+    symmetrical: bool,
+    reverse: bool,
+}
+
+impl<I: PartialEq + Clone> Edge<I> {
+    #[inline]
+    pub fn new(id: I, symmetrical: bool) -> Self {
+        Self {
+            id,
+            symmetrical,
+            reverse: false,
+        }
+    }
+
+    #[inline]
+    pub fn reversed(&self) -> Self {
+        Self {
+            id: self.id.clone(),
+            symmetrical: self.symmetrical,
+            reverse: !self.symmetrical && !self.reverse,
+        }
+    }
+
+    #[inline]
+    pub fn is_compatible(&self, other: &Self) -> bool {
+        self.id == other.id && ((self.reverse != other.reverse) || self.symmetrical)
+    }
+}
+
+#[derive(Clone)]
+pub struct TileImage<T, I: PartialEq + Clone> {
+    image: Rc<Vec2D<T>>,
+    pub transform: Transform,
+    top: Edge<I>,
+    right: Edge<I>,
+    bottom: Edge<I>,
+    left: Edge<I>,
+}
+
+impl<T: Copy, I: PartialEq + Clone> TileImage<T, I> {
+    pub fn new(
+        image: Vec2D<T>,
+        top: Edge<I>,
+        right: Edge<I>,
+        bottom: Edge<I>,
+        left: Edge<I>,
+    ) -> Self {
+        Self {
+            image: Rc::new(image),
+            transform: Transform::default(),
+            top,
+            right,
+            bottom,
+            left,
+        }
+    }
+
+    pub fn mirrored(&self) -> Self {
+        Self {
+            image: self.image.clone(),
+            transform: self.transform.mirror(),
+            top: self.top.reversed(),
+            right: self.left.reversed(),
+            bottom: self.bottom.reversed(),
+            left: self.right.reversed(),
+        }
+    }
+
+    pub fn flipped(&self) -> Self {
+        Self {
+            image: self.image.clone(),
+            transform: self.transform.flip(),
+            top: self.bottom.reversed(),
+            right: self.right.reversed(),
+            bottom: self.top.reversed(),
+            left: self.left.reversed(),
+        }
+    }
+
+    pub fn rotated90(&self) -> Self {
+        Self {
+            image: self.image.clone(),
+            transform: self.transform.rotate90(),
+            top: self.left.clone(),
+            right: self.top.clone(),
+            bottom: self.right.clone(),
+            left: self.bottom.clone(),
+        }
+    }
+
+    pub fn rotated180(&self) -> Self {
+        Self {
+            image: self.image.clone(),
+            transform: self.transform.rotate180(),
+            top: self.bottom.clone(),
+            right: self.left.clone(),
+            bottom: self.top.clone(),
+            left: self.right.clone(),
+        }
+    }
+
+    pub fn rotated270(&self) -> Self {
+        Self {
+            image: self.image.clone(),
+            transform: self.transform.rotate270(),
+            top: self.right.clone(),
+            right: self.bottom.clone(),
+            bottom: self.left.clone(),
+            left: self.top.clone(),
+        }
+    }
+
+    #[inline]
+    pub fn right_edge(&self) -> &Edge<I> {
+        &self.right
+    }
+
+    #[inline]
+    pub fn bottom_edge(&self) -> &Edge<I> {
+        &self.bottom
+    }
+
+    #[inline]
+    pub fn left_edge(&self) -> &Edge<I> {
+        &self.left
+    }
+
+    #[inline]
+    pub fn top_edge(&self) -> &Edge<I> {
+        &self.top
+    }
+
+    #[inline]
+    pub fn size(&self) -> Size {
+        match self.transform {
+            Transform::Rotate0(_) => self.image.size(),
+            Transform::Rotate90(_) => Size::new(self.image.size().height, self.image.size().width),
+        }
+    }
+
+    #[inline]
+    pub fn get(&self, row: usize, column: usize) -> Option<&T> {
+        match self.transform {
+            Transform::Rotate0(_) => {
+                let image_row = if self.transform.is_flipped() {
+                    self.image.height().wrapping_sub(1).wrapping_sub(row)
+                } else {
+                    row
+                };
+
+                let image_column = if self.transform.is_mirrored() {
+                    self.image.width().wrapping_sub(1).wrapping_sub(column)
+                } else {
+                    column
+                };
+
+                self.image.get(image_row, image_column)
+            }
+            Transform::Rotate90(_) => {
+                let image_row = if self.transform.is_mirrored() {
+                    column
+                } else {
+                    self.image.height().wrapping_sub(1).wrapping_sub(column)
+                };
+
+                let image_column = if self.transform.is_flipped() {
+                    self.image.width().wrapping_sub(1).wrapping_sub(row)
+                } else {
+                    row
+                };
+
+                self.image.get(image_row, image_column)
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_edge_new() {
+        let edge = Edge::new(1, true);
+        assert_eq!(edge.id, 1);
+        assert_eq!(edge.symmetrical, true);
+        assert_eq!(edge.reverse, false);
+    }
+
+    #[test]
+    fn test_edge_reversed() {
+        let edge = Edge::new(1, true);
+        let reversed = edge.reversed();
+        assert_eq!(reversed.id, edge.id);
+        assert_eq!(reversed.symmetrical, edge.symmetrical);
+        assert_eq!(reversed.reverse, false);
+
+        let edge = Edge::new(1, false);
+        let reversed = edge.reversed();
+        assert_eq!(reversed.id, edge.id);
+        assert_eq!(reversed.symmetrical, edge.symmetrical);
+        assert_eq!(reversed.reverse, true);
+    }
+
+    #[test]
+    fn test_edge_equality() {
+        let edge1 = Edge::new(1, true);
+        let edge2 = Edge::new(1, true);
+        assert_eq!(edge1, edge2);
+
+        let edge1 = Edge::new(1, false);
+        let edge2 = Edge::new(1, false);
+        assert_eq!(edge1, edge2);
+
+        let edge1 = Edge::new(1, false);
+        let edge2 = Edge::new(2, false);
+        assert_ne!(edge1, edge2);
+    }
+
+    #[test]
+    fn test_edge_equality_with_reverse() {
+        let edge1 = Edge::new(1, true);
+        let edge2 = edge1.reversed();
+        assert_eq!(edge1, edge2);
+
+        let edge1 = Edge::new(1, false);
+        let edge2 = edge1.reversed();
+        assert_ne!(edge1, edge2);
+
+        let edge1 = Edge::new(1, true);
+        let edge2 = edge1.reversed().reversed();
+        assert_eq!(edge1, edge2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/wavefront_collapse/transform.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,214 @@
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub enum SymmetryTransform {
+    Id,
+    Flip,
+    Mirror,
+    FlipMirror,
+}
+
+#[derive(Debug, PartialEq, Clone, Copy)]
+pub enum Transform {
+    Rotate0(SymmetryTransform),
+    Rotate90(SymmetryTransform),
+}
+
+impl Default for Transform {
+    fn default() -> Self {
+        Transform::Rotate0(SymmetryTransform::Id)
+    }
+}
+
+impl SymmetryTransform {
+    pub fn mirror(&self) -> Self {
+        use SymmetryTransform::*;
+        match self {
+            Id => Mirror,
+            Flip => FlipMirror,
+            Mirror => Id,
+            FlipMirror => Flip,
+        }
+    }
+
+    pub fn flip(&self) -> Self {
+        use SymmetryTransform::*;
+        match self {
+            Id => Flip,
+            Flip => Id,
+            Mirror => FlipMirror,
+            FlipMirror => Mirror,
+        }
+    }
+
+    pub fn is_mirrored(&self) -> bool {
+        use SymmetryTransform::*;
+        match self {
+            Id => false,
+            Flip => false,
+            Mirror => true,
+            FlipMirror => true,
+        }
+    }
+
+    pub fn is_flipped(&self) -> bool {
+        use SymmetryTransform::*;
+        match self {
+            Id => false,
+            Flip => true,
+            Mirror => false,
+            FlipMirror => true,
+        }
+    }
+}
+
+impl Transform {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    pub fn mirror(self) -> Transform {
+        match self {
+            Transform::Rotate0(s) => Transform::Rotate0(s.mirror()),
+            Transform::Rotate90(s) => Transform::Rotate90(s.flip()),
+        }
+    }
+
+    pub fn flip(self) -> Transform {
+        match self {
+            Transform::Rotate0(s) => Transform::Rotate0(s.flip()),
+            Transform::Rotate90(s) => Transform::Rotate90(s.mirror()),
+        }
+    }
+
+    pub fn rotate90(self) -> Transform {
+        match self {
+            Transform::Rotate0(s) => Transform::Rotate90(s),
+            Transform::Rotate90(s) => Transform::Rotate0(s.flip().mirror()),
+        }
+    }
+
+    pub fn rotate180(self) -> Transform {
+        match self {
+            Transform::Rotate0(s) => Transform::Rotate0(s.flip().mirror()),
+            Transform::Rotate90(s) => Transform::Rotate90(s.flip().mirror()),
+        }
+    }
+
+    pub fn rotate270(self) -> Transform {
+        match self {
+            Transform::Rotate0(s) => Transform::Rotate90(s.flip().mirror()),
+            Transform::Rotate90(s) => Transform::Rotate0(s),
+        }
+    }
+
+    pub fn is_mirrored(&self) -> bool {
+        match self {
+            Transform::Rotate0(s) => s.is_mirrored(),
+            Transform::Rotate90(s) => s.is_mirrored(),
+        }
+    }
+
+    pub fn is_flipped(&self) -> bool {
+        match self {
+            Transform::Rotate0(s) => s.is_flipped(),
+            Transform::Rotate90(s) => s.is_flipped(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{SymmetryTransform::*, Transform::*, *};
+
+    // I totally wrote all of this myself and didn't use ChatGPT
+    #[test]
+    fn test_default() {
+        let rt = Transform::new();
+        assert_eq!(rt, Rotate0(Id));
+    }
+
+    #[test]
+    fn test_mirror() {
+        let rt = Rotate90(Flip);
+        let mirrored = rt.mirror();
+        assert_eq!(mirrored, Rotate90(Id));
+    }
+
+    #[test]
+    fn test_flip() {
+        let rt = Transform::new().rotate180().mirror();
+        let flipped = rt.flip();
+        assert_eq!(flipped, Rotate0(Id));
+    }
+
+    #[test]
+    fn test_rotate90() {
+        let rt = Rotate0(Id);
+        let rotated = rt.rotate90();
+        assert_eq!(rotated, Rotate90(Id));
+    }
+
+    #[test]
+    fn test_rotate180() {
+        let rt = Rotate90(Mirror);
+        let rotated = rt.rotate180();
+        assert_eq!(rotated, Rotate90(Flip));
+    }
+
+    #[test]
+    fn test_rotate270() {
+        let rt = Transform::new().rotate180().flip();
+        let rotated = rt.rotate270();
+        assert_eq!(rotated, Rotate90(Flip));
+    }
+
+    #[test]
+    fn test_rotate180_2() {
+        let rt = Transform::new().rotate180();
+        assert_eq!(rt, Rotate0(FlipMirror));
+    }
+
+    #[test]
+    fn test_rotation_chain() {
+        assert_eq!(
+            Transform::default(),
+            Transform::default()
+                .rotate90()
+                .rotate90()
+                .rotate90()
+                .rotate90()
+        );
+        assert_eq!(
+            Transform::default().rotate90(),
+            Transform::default().rotate180().rotate90().rotate180()
+        );
+        assert_eq!(
+            Transform::default().rotate180(),
+            Transform::default().rotate180().rotate270().rotate90()
+        );
+    }
+
+    #[test]
+    fn test_combinations_chain() {
+        assert_eq!(
+            Transform::default(),
+            Transform::default().flip().rotate180().flip().rotate180()
+        );
+        assert_eq!(
+            Transform::default(),
+            Transform::default()
+                .mirror()
+                .rotate180()
+                .mirror()
+                .rotate180()
+        );
+        assert_eq!(
+            Transform::default(),
+            Transform::default()
+                .rotate90()
+                .flip()
+                .rotate90()
+                .mirror()
+                .rotate180()
+        );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/landgen/src/wavefront_collapse/wavefront_collapse.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,187 @@
+use integral_geometry::Size;
+use std::collections::HashSet;
+use vec2d::Vec2D;
+
+#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
+pub enum Tile {
+    Empty,
+    Outside,
+    Numbered(usize),
+}
+
+impl Default for Tile {
+    fn default() -> Self {
+        Tile::Outside
+    }
+}
+
+#[derive(Debug)]
+pub struct CollapseRule {
+    pub tile: Tile,
+    pub right: HashSet<Tile>,
+    pub bottom: HashSet<Tile>,
+    pub left: HashSet<Tile>,
+    pub top: HashSet<Tile>,
+}
+
+pub struct WavefrontCollapse {
+    rules: Vec<CollapseRule>,
+    grid: Vec2D<Tile>,
+    wrap: bool,
+}
+
+impl Default for WavefrontCollapse {
+    fn default() -> Self {
+        Self {
+            rules: Vec::new(),
+            grid: Vec2D::new(&Size::new(1, 1), Tile::Empty),
+            wrap: false,
+        }
+    }
+}
+
+impl WavefrontCollapse {
+    pub fn new(wrap: bool) -> Self {
+        Self {
+            rules: Vec::new(),
+            grid: Vec2D::new(&Size::new(1, 1), Tile::Empty),
+            wrap,
+        }
+    }
+
+    pub fn generate_map<I: Iterator<Item = u32>, F: FnOnce(&mut Vec2D<Tile>)>(
+        &mut self,
+        map_size: &Size,
+        seed_fn: F,
+        random_numbers: &mut I,
+    ) {
+        self.grid = Vec2D::new(map_size, Tile::Empty);
+
+        seed_fn(&mut self.grid);
+
+        while self.collapse_step(random_numbers) {}
+    }
+
+    pub fn set_rules(&mut self, rules: Vec<CollapseRule>) {
+        self.rules = rules;
+    }
+
+    fn get_tile(&self, y: usize, x: usize) -> Tile {
+        let x = if self.wrap {
+            if x == usize::MAX {
+                self.grid.width() - 1
+            } else if x == self.grid.width() {
+                0
+            } else {
+                x
+            }
+        } else {
+            x
+        };
+
+        self.grid.get(y, x).copied().unwrap_or_default()
+    }
+
+    fn collapse_step<I: Iterator<Item = u32>>(&mut self, random_numbers: &mut I) -> bool {
+        let mut tiles_to_collapse = (usize::max_value(), Vec::new());
+
+        // Iterate through the tiles in the land
+        for x in 0..self.grid.width() {
+            for y in 0..self.grid.height() {
+                let current_tile = self.get_tile(y, x);
+
+                if let Tile::Empty = current_tile {
+                    // calc entropy
+                    let right_tile = self.get_tile(y, x + 1);
+                    let bottom_tile = self.get_tile(y + 1, x);
+                    let left_tile = self.get_tile(y, x.wrapping_sub(1));
+                    let top_tile = self.get_tile(y.wrapping_sub(1), x);
+
+                    let possibilities: Vec<Tile> = self
+                        .rules
+                        .iter()
+                        .filter_map(|rule| {
+                            if rule.right.contains(&right_tile)
+                                && rule.bottom.contains(&bottom_tile)
+                                && rule.left.contains(&left_tile)
+                                && rule.top.contains(&top_tile)
+                            {
+                                Some(rule.tile)
+                            } else {
+                                None
+                            }
+                        })
+                        .collect();
+
+                    let entropy = possibilities.len();
+                    if entropy > 0 {
+                        if entropy <= tiles_to_collapse.0 {
+                            let entry = (
+                                y,
+                                x,
+                                possibilities
+                                    [random_numbers.next().unwrap_or_default() as usize % entropy],
+                            );
+
+                            if entropy < tiles_to_collapse.0 {
+                                tiles_to_collapse = (entropy, vec![entry])
+                            } else {
+                                tiles_to_collapse.1.push(entry)
+                            }
+                        }
+                    } else {
+                        /*println!("We're here: {}, {}", x, y);
+                        println!(
+                            "Neighbour tiles are: {:?} {:?} {:?} {:?}",
+                            right_tile, bottom_tile, left_tile, top_tile
+                        );
+                        println!("Rules are: {:?}", self.rules);*/
+
+                        //todo!("no collapse possible - what to do?")
+                    }
+                }
+            }
+        }
+
+        let tiles_to_collapse = tiles_to_collapse.1;
+        let possibilities_number = tiles_to_collapse.len();
+
+        if possibilities_number > 0 {
+            let (y, x, tile) = tiles_to_collapse
+                [random_numbers.next().unwrap_or_default() as usize % possibilities_number];
+
+            *self
+                .grid
+                .get_mut(y, x)
+                .expect("correct iteration over grid") = tile;
+
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn grid(&self) -> &Vec2D<Tile> {
+        &self.grid
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{Tile, WavefrontCollapse};
+    use integral_geometry::Size;
+    use vec2d::Vec2D;
+
+    #[test]
+    fn test_wavefront_collapse() {
+        let size = Size::new(4, 4);
+        let mut rnd = [0u32; 64].into_iter().cycle();
+        let mut wfc = WavefrontCollapse::default();
+
+        wfc.generate_map(&size, |_| {}, &mut rnd);
+
+        let empty_land = Vec2D::new(&size, Tile::Empty);
+
+        assert_eq!(empty_land.as_slice(), wfc.grid().as_slice());
+    }
+}
--- a/rust/lfprng/Cargo.toml	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/lfprng/Cargo.toml	Tue Sep 05 17:02:08 2023 +0200
@@ -5,3 +5,4 @@
 edition = "2018"
 
 [dependencies]
+rand = "0.8"
--- a/rust/lfprng/src/lib.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/lfprng/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -1,3 +1,5 @@
+use rand::{Error, RngCore, SeedableRng};
+
 pub struct LaggedFibonacciPRNG {
     circular_buffer: [u32; 64],
     index: usize,
@@ -5,7 +7,7 @@
 
 impl LaggedFibonacciPRNG {
     pub fn new(init_values: &[u8]) -> Self {
-        let mut buf = [0xa98765 + 68; 64];
+        let mut buf = [0xa98765; 64];
 
         for i in 0..std::cmp::min(init_values.len(), 54) {
             buf[i] = init_values[i] as u32;
@@ -30,10 +32,17 @@
 
     #[inline]
     fn get_next(&mut self) -> u32 {
+        const PRIME_NUM: u32 = 2147483629;
+
         self.index = (self.index + 1) & 0x3f;
-        self.circular_buffer[self.index] = (self.circular_buffer[(self.index + 40) & 0x3f]
-            + self.circular_buffer[(self.index + 9) & 0x3f])
-            & 0x7fffffff;
+        let next_value = self.circular_buffer[(self.index + 40) & 0x3f]
+            + self.circular_buffer[(self.index + 9) & 0x3f];
+
+        self.circular_buffer[self.index] = if next_value > PRIME_NUM {
+            next_value - PRIME_NUM
+        } else {
+            next_value
+        };
 
         self.circular_buffer[self.index]
     }
@@ -60,6 +69,32 @@
     }
 }
 
+impl RngCore for LaggedFibonacciPRNG {
+    fn next_u32(&mut self) -> u32 {
+        self.get_next().wrapping_add(self.get_next())
+    }
+
+    fn next_u64(&mut self) -> u64 {
+        ((self.next_u32() as u64) << 32) | self.next_u32() as u64
+    }
+
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        dest.iter_mut().for_each(|x| *x = self.next_u32() as u8);
+    }
+
+    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
+        Ok(self.fill_bytes(dest))
+    }
+}
+
+impl SeedableRng for LaggedFibonacciPRNG {
+    type Seed = [u8; 32];
+
+    fn from_seed(seed: Self::Seed) -> Self {
+        LaggedFibonacciPRNG::new(&seed)
+    }
+}
+
 #[cfg(test)]
 #[test]
 fn compatibility() {
--- a/rust/lib-hedgewars-engine/src/instance.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/lib-hedgewars-engine/src/instance.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -5,7 +5,7 @@
 use hedgewars_engine_messages::queue::*;
 
 use integral_geometry::{Point, Rect, Size};
-use landgen::outline_template::OutlineTemplate;
+use landgen::outline_template_based::outline_template::OutlineTemplate;
 
 use std::path::Path;
 
--- a/rust/lib-hedgewars-engine/src/lib.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/lib-hedgewars-engine/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -5,7 +5,7 @@
 mod world;
 
 use std::{
-    ffi::{CString, CStr},
+    ffi::{CStr, CString},
     io::{Read, Write},
     mem::replace,
     os::raw::{c_char, c_void},
--- a/rust/lib-hedgewars-engine/src/render/gear.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/lib-hedgewars-engine/src/render/gear.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -71,22 +71,10 @@
 }
 
 const SPRITE_LOAD_LIST: &[(SpriteId, &str)] = &[
-    (
-        SpriteId::Mine,
-        "Graphics/MineOn.png",
-    ),
-    (
-        SpriteId::Grenade,
-        "Graphics/Bomb.png",
-    ),
-    (
-        SpriteId::Cheese,
-        "Graphics/cheese.png",
-    ),
-    (
-        SpriteId::Cleaver,
-        "Graphics/cleaver.png",
-    ),
+    (SpriteId::Mine, "Graphics/MineOn.png"),
+    (SpriteId::Grenade, "Graphics/Bomb.png"),
+    (SpriteId::Cheese, "Graphics/cheese.png"),
+    (SpriteId::Cleaver, "Graphics/cleaver.png"),
 ];
 
 const MAX_SPRITES: usize = SpriteId::MaxSprite as usize + 1;
--- a/rust/lib-hedgewars-engine/src/world.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/lib-hedgewars-engine/src/world.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -7,8 +7,9 @@
 use integral_geometry::{Point, Rect, Size};
 use land2d::Land2D;
 use landgen::{
-    outline_template::OutlineTemplate, template_based::TemplatedLandGenerator,
-    LandGenerationParameters, LandGenerator,
+    outline_template_based::outline_template::OutlineTemplate,
+    outline_template_based::template_based::TemplatedLandGenerator, LandGenerationParameters,
+    LandGenerator,
 };
 use lfprng::LaggedFibonacciPRNG;
 use std::path::{Path, PathBuf};
@@ -64,9 +65,14 @@
         if let Some(ref state) = self.game_state {
             self.camera.position = state.land.play_box().center();
 
+            let parameters = LandGenerationParameters::new(0u32, 0x8000u32, 0, false, false);
             let theme =
                 Theme::load(self.data_path.join(Path::new("Themes/Cheese/")).as_path()).unwrap();
-            let texture = MapGenerator::new().make_texture(&state.land, &theme);
+            let texture = MapGenerator::<OutlineTemplate>::new().make_texture(
+                &state.land,
+                &parameters,
+                &theme,
+            );
             if let Some(ref mut renderer) = self.map_renderer {
                 renderer.init(&texture);
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/lib-hwengine-future/Cargo.toml	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,18 @@
+[package]
+name = "lib-hwengine-future"
+version = "0.1.0"
+edition = "2021"
+authors = ["Andrey Korotaev <a.korotaev@hedgewars.org>"]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+land2d = { path = "../land2d" }
+integral-geometry = { path = "../integral-geometry" }
+mapgen = { path = "../mapgen" }
+landgen = { path = "../landgen" }
+lfprng = { path = "../lfprng" }
+
+[lib]
+name = "hwengine_future"
+crate-type = ["dylib"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/lib-hwengine-future/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,163 @@
+use integral_geometry::{Point, Size};
+
+use landgen::{
+    wavefront_collapse::generator::{
+        TemplateDescription as WfcTemplate,
+    },
+    LandGenerationParameters, LandGenerator,
+};
+use lfprng::LaggedFibonacciPRNG;
+use mapgen::{theme::Theme, MapGenerator};
+use std::fs;
+use std::{ffi::CStr, path::Path};
+
+#[repr(C)]
+pub struct GameField {
+    collision: land2d::Land2D<u16>,
+    pixels: land2d::Land2D<u32>,
+    landgen_parameters: Option<LandGenerationParameters<u16>>,
+}
+
+#[no_mangle]
+pub extern "C" fn get_game_field_parameters(
+    game_field: &GameField,
+    width: *mut i32,
+    height: *mut i32,
+    play_width: *mut i32,
+    play_height: *mut i32,
+) {
+    unsafe {
+        *width = game_field.collision.width() as i32;
+        *height = game_field.collision.height() as i32;
+
+        *play_width = game_field.collision.play_width() as i32;
+        *play_height = game_field.collision.play_height() as i32;
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn create_empty_game_field(width: u32, height: u32) -> *mut GameField {
+    let game_field = Box::new(GameField {
+        collision: land2d::Land2D::new(&Size::new(width as usize, height as usize), 0),
+        pixels: land2d::Land2D::new(&Size::new(width as usize, height as usize), 0),
+        landgen_parameters: None,
+    });
+
+    Box::leak(game_field)
+}
+
+#[no_mangle]
+pub extern "C" fn generate_templated_game_field(
+    feature_size: u32,
+    seed: *const i8,
+    template_type: *const i8,
+    data_path: *const i8,
+) -> *mut GameField {
+    let data_path: &str = unsafe { CStr::from_ptr(data_path) }.to_str().unwrap();
+    let data_path = Path::new(&data_path);
+
+    let seed: &str = unsafe { CStr::from_ptr(seed) }.to_str().unwrap();
+    let template_type: &str = unsafe { CStr::from_ptr(template_type) }.to_str().unwrap();
+
+    let mut random_numbers_gen = LaggedFibonacciPRNG::new(seed.as_bytes());
+
+    let yaml_templates =
+        fs::read_to_string(data_path.join(Path::new("wfc_templates.yaml")).as_path())
+            .expect("Error reading map templates file");
+    let mut map_gen = MapGenerator::<WfcTemplate>::new();
+    map_gen.import_yaml_templates(&yaml_templates);
+
+    let distance_divisor = feature_size.pow(2) / 8 + 10;
+    let params = LandGenerationParameters::new(0u16, 0x8000u16, distance_divisor, false, false);
+    let template = map_gen
+        .get_template(template_type, &mut random_numbers_gen)
+        .expect("Error reading templates file")
+        .clone();
+    let landgen = map_gen.build_generator(template);
+    let collision = landgen.generate_land(&params, &mut random_numbers_gen);
+    let size = collision.size().size();
+
+    let game_field = Box::new(GameField {
+        collision,
+        pixels: land2d::Land2D::new(&size, 0),
+        landgen_parameters: Some(params),
+    });
+
+    Box::leak(game_field)
+}
+
+#[no_mangle]
+pub extern "C" fn apply_theme(
+    game_field: &mut GameField,
+    data_path: *const i8,
+    theme_name: *const i8,
+) {
+    let data_path: &str = unsafe { CStr::from_ptr(data_path) }.to_str().unwrap();
+    let data_path = Path::new(&data_path);
+
+    let theme_name: &str = unsafe { CStr::from_ptr(theme_name) }.to_str().unwrap();
+    let map_gen = MapGenerator::<()>::new();
+
+    let theme = Theme::load(
+        data_path
+            .join(Path::new("Themes"))
+            .join(Path::new(theme_name))
+            .as_path(),
+    )
+    .unwrap();
+
+    let params = game_field
+        .landgen_parameters
+        .expect("Land generator parameters specified");
+    let pixels = map_gen.make_texture(&game_field.collision, &params, &theme);
+
+    game_field.pixels = pixels.into();
+}
+
+#[no_mangle]
+pub extern "C" fn land_get(game_field: &GameField, x: i32, y: i32) -> u16 {
+    game_field.collision.get(y, x)
+}
+
+#[no_mangle]
+pub extern "C" fn land_set(game_field: &mut GameField, x: i32, y: i32, value: u16) {
+    game_field.collision.map(y, x, |p| *p = value);
+}
+
+#[no_mangle]
+pub extern "C" fn land_row(game_field: &mut GameField, row: i32) -> *mut u16 {
+    game_field.collision[row as usize].as_mut_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn land_fill(
+    game_field: &mut GameField,
+    x: i32,
+    y: i32,
+    border_value: u16,
+    fill_value: u16,
+) {
+    game_field
+        .collision
+        .fill(Point::new(x, y), border_value, fill_value)
+}
+
+#[no_mangle]
+pub extern "C" fn land_pixel_get(game_field: &GameField, x: i32, y: i32) -> u32 {
+    game_field.pixels.get(y, x)
+}
+
+#[no_mangle]
+pub extern "C" fn land_pixel_set(game_field: &mut GameField, x: i32, y: i32, value: u32) {
+    game_field.pixels.map(y, x, |p| *p = value);
+}
+
+#[no_mangle]
+pub extern "C" fn land_pixel_row(game_field: &mut GameField, row: i32) -> *mut u32 {
+    game_field.pixels[row as usize].as_mut_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn dispose_game_field(game_field: *mut GameField) {
+    unsafe { drop(Box::from_raw(game_field)) };
+}
--- a/rust/mapgen/Cargo.toml	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/mapgen/Cargo.toml	Tue Sep 05 17:02:08 2023 +0200
@@ -11,7 +11,7 @@
 lfprng = { path = "../lfprng" }
 integral-geometry = { path = "../integral-geometry" }
 
-rand = "0.5"
+rand = "0.8"
 serde = "1.0"
 serde_yaml = "0.8"
 serde_derive = "1.0"
--- a/rust/mapgen/src/lib.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/mapgen/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -1,79 +1,24 @@
+mod template;
 pub mod theme;
 
 use self::theme::Theme;
-use integral_geometry::{Point, Rect, Size};
-use land2d::Land2D;
-use landgen::outline_template::OutlineTemplate;
-use rand::{thread_rng, Rng};
-use serde_derive::Deserialize;
-use serde_yaml;
-use std::{borrow::Borrow, collections::hash_map::HashMap, mem::replace};
-use vec2d::Vec2D;
-
-#[derive(Deserialize)]
-struct PointDesc {
-    x: u32,
-    y: u32,
-}
-
-#[derive(Deserialize)]
-struct RectDesc {
-    x: u32,
-    y: u32,
-    w: u32,
-    h: u32,
-}
-
-#[derive(Deserialize)]
-struct TemplateDesc {
-    width: usize,
-    height: usize,
-    can_flip: bool,
-    can_invert: bool,
-    can_mirror: bool,
-    is_negative: bool,
-    put_girders: bool,
-    max_hedgehogs: u8,
-    outline_points: Vec<Vec<RectDesc>>,
-    fill_points: Vec<PointDesc>,
-}
+use crate::template::outline::TemplateCollectionDesc as OutlineTemplateCollectionDesc;
+use crate::template::wavefront_collapse::TemplateCollectionDesc as WfcTemplateCollectionDesc;
 
-#[derive(Deserialize)]
-struct TemplateCollectionDesc {
-    templates: Vec<TemplateDesc>,
-    template_types: HashMap<String, Vec<usize>>,
-}
+use land2d::Land2D;
+use landgen::{
+    outline_template_based::{
+        outline_template::OutlineTemplate, template_based::TemplatedLandGenerator,
+    },
+    wavefront_collapse::generator::{
+        TemplateDescription as WfcTemplate, WavefrontCollapseLandGenerator,
+    },
+    LandGenerationParameters, LandGenerator,
+};
+use rand::{seq::SliceRandom, Rng};
 
-impl From<&TemplateDesc> for OutlineTemplate {
-    fn from(desc: &TemplateDesc) -> Self {
-        OutlineTemplate {
-            islands: desc
-                .outline_points
-                .iter()
-                .map(|v| {
-                    v.iter()
-                        .map(|r| {
-                            Rect::from_size(
-                                Point::new(r.x as i32, r.y as i32),
-                                Size::new(r.w as usize, r.h as usize),
-                            )
-                        })
-                        .collect()
-                })
-                .collect(),
-            fill_points: desc
-                .fill_points
-                .iter()
-                .map(|p| Point::new(p.x as i32, p.y as i32))
-                .collect(),
-            size: Size::new(desc.width, desc.height),
-            can_flip: desc.can_flip,
-            can_invert: desc.can_invert,
-            can_mirror: desc.can_mirror,
-            is_negative: desc.is_negative,
-        }
-    }
-}
+use std::{borrow::Borrow, collections::hash_map::HashMap};
+use vec2d::Vec2D;
 
 #[derive(PartialEq, Eq, Hash, Clone, Debug)]
 struct TemplateType(String);
@@ -85,43 +30,33 @@
 }
 
 #[derive(Debug)]
-pub struct MapGenerator {
-    pub(crate) templates: HashMap<TemplateType, Vec<OutlineTemplate>>,
+pub struct MapGenerator<T> {
+    pub(crate) templates: HashMap<TemplateType, Vec<T>>,
 }
 
-impl MapGenerator {
+impl<T> MapGenerator<T> {
     pub fn new() -> Self {
         Self {
             templates: HashMap::new(),
         }
     }
 
-    pub fn import_yaml_templates(&mut self, text: &str) {
-        let mut desc: TemplateCollectionDesc = serde_yaml::from_str(text).unwrap();
-        let templates = replace(&mut desc.templates, vec![]);
-        self.templates = desc
-            .template_types
-            .into_iter()
-            .map(|(size, indices)| {
-                (
-                    TemplateType(size),
-                    indices.iter().map(|i| (&templates[*i]).into()).collect(),
-                )
-            })
-            .collect();
+    pub fn get_template<R: Rng>(&self, template_type: &str, rng: &mut R) -> Option<&T> {
+        self.templates
+            .get(template_type)
+            .and_then(|t| t.as_slice().choose(rng))
     }
 
-    pub fn get_template(&self, template_type: &str) -> Option<&OutlineTemplate> {
-        self.templates
-            .get(template_type)
-            .and_then(|t| thread_rng().choose(t))
-    }
-
-    pub fn make_texture<LandT>(&self, land: &Land2D<LandT>, theme: &Theme) -> Vec2D<u32>
+    pub fn make_texture<LandT>(
+        &self,
+        land: &Land2D<LandT>,
+        parameters: &LandGenerationParameters<LandT>,
+        theme: &Theme,
+    ) -> Vec2D<u32>
     where
         LandT: Copy + Default + PartialEq,
     {
-        let mut texture = Vec2D::new(land.size().size(), 0);
+        let mut texture = Vec2D::new(&land.size().size(), 0);
 
         if let Some(land_sprite) = theme.land_texture() {
             for (row_index, (land_row, tex_row)) in land.rows().zip(texture.rows_mut()).enumerate()
@@ -131,6 +66,7 @@
                 while sprite_row.len() < land.width() - x_offset {
                     let copy_range = x_offset..x_offset + sprite_row.len();
                     tex_row_copy(
+                        parameters.basic(),
                         &land_row[copy_range.clone()],
                         &mut tex_row[copy_range],
                         sprite_row,
@@ -142,6 +78,7 @@
                 if x_offset < land.width() {
                     let final_range = x_offset..land.width();
                     tex_row_copy(
+                        parameters.basic(),
                         &land_row[final_range.clone()],
                         &mut tex_row[final_range],
                         &sprite_row[..land.width() - x_offset],
@@ -158,6 +95,7 @@
             let mut offsets = vec![255u8; land.width()];
 
             land_border_pass(
+                parameters.basic(),
                 land.rows().rev().zip(texture.rows_mut().rev()),
                 &mut offsets,
                 border_width,
@@ -170,6 +108,7 @@
             offsets.iter_mut().for_each(|v| *v = 255);
 
             land_border_pass(
+                parameters.basic(),
                 land.rows().zip(texture.rows_mut()),
                 &mut offsets,
                 border_width,
@@ -181,13 +120,55 @@
     }
 }
 
+impl MapGenerator<OutlineTemplate> {
+    pub fn import_yaml_templates(&mut self, text: &str) {
+        let mut desc: OutlineTemplateCollectionDesc = serde_yaml::from_str(text).unwrap();
+        let templates = std::mem::take(&mut desc.templates);
+        self.templates = desc
+            .template_types
+            .into_iter()
+            .map(|(size, indices)| {
+                (
+                    TemplateType(size),
+                    indices.iter().map(|i| (&templates[*i]).into()).collect(),
+                )
+            })
+            .collect();
+    }
+
+    pub fn build_generator(&self, template: OutlineTemplate) -> impl LandGenerator {
+        TemplatedLandGenerator::new(template)
+    }
+}
+
+impl MapGenerator<WfcTemplate> {
+    pub fn import_yaml_templates(&mut self, text: &str) {
+        let mut desc: WfcTemplateCollectionDesc = serde_yaml::from_str(text).unwrap();
+        let templates = std::mem::take(&mut desc.templates);
+        self.templates = desc
+            .template_types
+            .into_iter()
+            .map(|(size, indices)| {
+                (
+                    TemplateType(size),
+                    indices.iter().map(|i| (&templates[*i]).into()).collect(),
+                )
+            })
+            .collect();
+    }
+
+    pub fn build_generator(&self, template: WfcTemplate) -> impl LandGenerator {
+        WavefrontCollapseLandGenerator::new(template)
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 struct Color(u32);
 
 impl Color {
     #[inline]
     fn red(self) -> u8 {
-        (self.0 >> 0 & 0xFF) as u8
+        (self.0 & 0xFF) as u8
     }
 
     #[inline]
@@ -219,11 +200,16 @@
     let red = lerp(target.red(), source.red(), source.alpha());
     let green = lerp(target.green(), source.green(), source.alpha());
     let blue = lerp(target.blue(), source.blue(), source.alpha());
-    (red as u32) << 0 | (green as u32) << 8 | (blue as u32) << 16 | (alpha as u32) << 24
+    (red as u32) | (green as u32) << 8 | (blue as u32) << 16 | (alpha as u32) << 24
 }
 
-fn land_border_pass<'a, LandT, T, F>(rows: T, offsets: &mut [u8], border_width: u8, pixel_getter: F)
-where
+fn land_border_pass<'a, LandT, T, F>(
+    basic_value: LandT,
+    rows: T,
+    offsets: &mut [u8],
+    border_width: u8,
+    pixel_getter: F,
+) where
     LandT: Default + PartialEq + 'a,
     T: Iterator<Item = (&'a [LandT], &'a mut [u32])>,
     F: (Fn(usize, usize) -> u32),
@@ -235,7 +221,7 @@
             .zip(offsets.iter_mut())
             .enumerate()
         {
-            *offset_v = if *land_v == LandT::default() {
+            *offset_v = if *land_v == basic_value {
                 if *offset_v < border_width {
                     *tex_v = blend(pixel_getter(x, *offset_v as usize), *tex_v)
                 }
@@ -247,22 +233,23 @@
     }
 }
 
-fn tex_row_copy<LandT>(land_row: &[LandT], tex_row: &mut [u32], sprite_row: &[u32])
-where
+fn tex_row_copy<LandT>(
+    basic_value: LandT,
+    land_row: &[LandT],
+    tex_row: &mut [u32],
+    sprite_row: &[u32],
+) where
     LandT: Default + PartialEq,
 {
     for ((land_v, tex_v), sprite_v) in land_row.iter().zip(tex_row.iter_mut()).zip(sprite_row) {
-        *tex_v = if *land_v == LandT::default() {
-            *sprite_v
-        } else {
-            0
-        }
+        *tex_v = if *land_v == basic_value { *sprite_v } else { 0 }
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use crate::{MapGenerator, TemplateType};
+    use crate::{MapGenerator, OutlineTemplate, TemplateType};
+    use rand::thread_rng;
 
     #[test]
     fn simple_load() {
@@ -296,14 +283,14 @@
     test: [0]
 "#;
 
-        let mut generator = MapGenerator::new();
+        let mut generator = MapGenerator::<OutlineTemplate>::new();
         generator.import_yaml_templates(&text);
 
         assert!(generator
             .templates
             .contains_key(&TemplateType("test".to_string())));
 
-        let template = generator.get_template("test").unwrap();
+        let template = generator.get_template("test", &mut thread_rng()).unwrap();
 
         assert_eq!(template.islands[0].len(), 7);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/mapgen/src/template/mod.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,2 @@
+pub mod outline;
+pub mod wavefront_collapse;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/mapgen/src/template/outline.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,71 @@
+use integral_geometry::{Point, Rect, Size};
+
+use landgen::outline_template_based::outline_template::OutlineTemplate;
+use serde_derive::Deserialize;
+
+use std::collections::hash_map::HashMap;
+
+#[derive(Deserialize)]
+pub struct PointDesc {
+    x: u32,
+    y: u32,
+}
+
+#[derive(Deserialize)]
+pub struct RectDesc {
+    x: u32,
+    y: u32,
+    w: u32,
+    h: u32,
+}
+
+#[derive(Deserialize)]
+pub struct TemplateDesc {
+    width: usize,
+    height: usize,
+    can_flip: bool,
+    can_invert: bool,
+    can_mirror: bool,
+    is_negative: bool,
+    put_girders: bool,
+    max_hedgehogs: u8,
+    outline_points: Vec<Vec<RectDesc>>,
+    fill_points: Vec<PointDesc>,
+}
+
+#[derive(Deserialize)]
+pub struct TemplateCollectionDesc {
+    pub templates: Vec<TemplateDesc>,
+    pub template_types: HashMap<String, Vec<usize>>,
+}
+
+impl From<&TemplateDesc> for OutlineTemplate {
+    fn from(desc: &TemplateDesc) -> Self {
+        OutlineTemplate {
+            islands: desc
+                .outline_points
+                .iter()
+                .map(|v| {
+                    v.iter()
+                        .map(|r| {
+                            Rect::from_size(
+                                Point::new(r.x as i32, r.y as i32),
+                                Size::new(r.w as usize, r.h as usize),
+                            )
+                        })
+                        .collect()
+                })
+                .collect(),
+            fill_points: desc
+                .fill_points
+                .iter()
+                .map(|p| Point::new(p.x as i32, p.y as i32))
+                .collect(),
+            size: Size::new(desc.width, desc.height),
+            can_flip: desc.can_flip,
+            can_invert: desc.can_invert,
+            can_mirror: desc.can_mirror,
+            is_negative: desc.is_negative,
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/mapgen/src/template/wavefront_collapse.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,105 @@
+use integral_geometry::Size;
+
+use landgen::wavefront_collapse::generator::*;
+use serde_derive::Deserialize;
+
+use std::collections::hash_map::HashMap;
+
+#[derive(Deserialize)]
+#[serde(remote = "EdgeDescription")]
+pub struct EdgeDesc {
+    pub name: String,
+    pub reversed: Option<bool>,
+    pub symmetrical: Option<bool>,
+}
+
+#[derive(Deserialize)]
+#[serde(remote = "EdgesDescription")]
+pub struct EdgesDesc {
+    #[serde(with = "EdgeDesc")]
+    pub top: EdgeDescription,
+    #[serde(with = "EdgeDesc")]
+    pub right: EdgeDescription,
+    #[serde(with = "EdgeDesc")]
+    pub bottom: EdgeDescription,
+    #[serde(with = "EdgeDesc")]
+    pub left: EdgeDescription,
+}
+
+#[derive(Deserialize)]
+#[serde(remote = "TileDescription")]
+pub struct TileDesc {
+    pub name: String,
+    #[serde(with = "EdgesDesc")]
+    pub edges: EdgesDescription,
+    pub is_negative: Option<bool>,
+    pub can_flip: Option<bool>,
+    pub can_mirror: Option<bool>,
+    pub can_rotate90: Option<bool>,
+    pub can_rotate180: Option<bool>,
+    pub can_rotate270: Option<bool>,
+}
+
+#[derive(Deserialize)]
+pub struct TileDescriptionHelper(#[serde(with = "TileDesc")] TileDescription);
+#[derive(Deserialize)]
+pub struct EdgeDescriptionHelper(#[serde(with = "EdgeDesc")] EdgeDescription);
+
+#[derive(Deserialize)]
+pub struct NonStrictEdgesDesc {
+    pub top: Option<EdgeDescriptionHelper>,
+    pub right: Option<EdgeDescriptionHelper>,
+    pub bottom: Option<EdgeDescriptionHelper>,
+    pub left: Option<EdgeDescriptionHelper>,
+}
+
+#[derive(Deserialize)]
+pub struct TemplateDesc {
+    pub width: usize,
+    pub height: usize,
+    pub can_invert: bool,
+    pub is_negative: bool,
+    pub put_girders: bool,
+    pub max_hedgehogs: u8,
+    pub wrap: bool,
+    pub edges: Option<NonStrictEdgesDesc>,
+    pub tiles: Vec<TileDescriptionHelper>,
+}
+
+#[derive(Deserialize)]
+pub struct TemplateCollectionDesc {
+    pub templates: Vec<TemplateDesc>,
+    pub template_types: HashMap<String, Vec<usize>>,
+}
+
+impl From<&TemplateDesc> for TemplateDescription {
+    fn from(desc: &TemplateDesc) -> Self {
+        let [top, right, bottom, left] = if let Some(edges) = &desc.edges {
+            [
+                edges.top.as_ref(),
+                edges.right.as_ref(),
+                edges.bottom.as_ref(),
+                edges.left.as_ref(),
+            ]
+            .map(|e| e.map(|EdgeDescriptionHelper(e)| e.clone()))
+        } else {
+            [None, None, None, None]
+        };
+
+        Self {
+            size: Size::new(desc.width, desc.height),
+            tiles: desc
+                .tiles
+                .iter()
+                .map(|TileDescriptionHelper(t)| t.clone())
+                .collect(),
+            wrap: desc.wrap,
+            edges: NonStrictEdgesDescription {
+                top,
+                right,
+                bottom,
+                left,
+            },
+        }
+    }
+}
--- a/rust/mapgen/src/theme.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/mapgen/src/theme.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -48,7 +48,7 @@
 
     pub fn to_transposed(&self) -> ThemeSprite {
         let size = self.size().transpose();
-        let mut pixels = Vec2D::new(size, 0u32);
+        let mut pixels = Vec2D::new(&size, 0u32);
         for (y, row) in self.pixels.rows().enumerate() {
             for (x, v) in row.iter().enumerate() {
                 pixels[x][y] = *v;
@@ -238,7 +238,7 @@
     }
     let size = Size::new(info.width as usize, info.height as usize);
 
-    let mut pixels: Vec2D<u32> = Vec2D::new(size, 0);
+    let mut pixels: Vec2D<u32> = Vec2D::new(&size, 0);
     reader.next_frame(slice_u32_to_u8_mut(pixels.as_mut_slice()))?;
 
     Ok(ThemeSprite { pixels })
--- a/rust/vec2d/src/lib.rs	Thu Aug 24 20:15:40 2023 +0200
+++ b/rust/vec2d/src/lib.rs	Tue Sep 05 17:02:08 2023 +0200
@@ -1,9 +1,10 @@
+use integral_geometry::Size;
 use std::{
     ops::{Index, IndexMut},
-    slice::SliceIndex
+    slice::SliceIndex,
 };
-use integral_geometry::Size;
 
+#[derive(Debug)]
 pub struct Vec2D<T> {
     data: Vec<T>,
     size: Size,
@@ -33,7 +34,7 @@
     }
 }
 
-impl <T> Vec2D<T> {
+impl<T> Vec2D<T> {
     #[inline]
     pub fn width(&self) -> usize {
         self.size.width
@@ -51,8 +52,11 @@
 }
 
 impl<T: Copy> Vec2D<T> {
-    pub fn new(size: Size, value: T) -> Self {
-        Self { size, data: vec![value; size.area()] }
+    pub fn new(size: &Size, value: T) -> Self {
+        Self {
+            size: *size,
+            data: vec![value; size.area()],
+        }
     }
 
     #[inline]
@@ -67,21 +71,41 @@
 
     #[inline]
     pub fn get(&self, row: usize, column: usize) -> Option<&<usize as SliceIndex<[T]>>::Output> {
-        self.data.get(row * self.width() + column)
+        if row < self.height() && column < self.width() {
+            Some(unsafe { self.data.get_unchecked(row * self.width() + column) })
+        } else {
+            None
+        }
     }
 
     #[inline]
-    pub fn get_mut(&mut self, row: usize, column: usize) -> Option<&mut <usize as SliceIndex<[T]>>::Output> {
-        self.data.get_mut(row * self.size.width + column)
+    pub fn get_mut(
+        &mut self,
+        row: usize,
+        column: usize,
+    ) -> Option<&mut <usize as SliceIndex<[T]>>::Output> {
+        if row < self.height() && column < self.width() {
+            Some(unsafe { self.data.get_unchecked_mut(row * self.size.width + column) })
+        } else {
+            None
+        }
     }
 
     #[inline]
-    pub unsafe fn get_unchecked(&self, row: usize, column: usize) -> &<usize as SliceIndex<[T]>>::Output {
+    pub unsafe fn get_unchecked(
+        &self,
+        row: usize,
+        column: usize,
+    ) -> &<usize as SliceIndex<[T]>>::Output {
         self.data.get_unchecked(row * self.width() + column)
     }
 
     #[inline]
-    pub unsafe fn get_unchecked_mut(&mut self, row: usize, column: usize) -> &mut <usize as SliceIndex<[T]>>::Output {
+    pub unsafe fn get_unchecked_mut(
+        &mut self,
+        row: usize,
+        column: usize,
+    ) -> &mut <usize as SliceIndex<[T]>>::Output {
         self.data.get_unchecked_mut(row * self.size.width + column)
     }
 
@@ -98,11 +122,8 @@
 
     #[inline]
     pub unsafe fn as_bytes(&self) -> &[u8] {
-        use std::{
-            slice,
-            mem
-        };
-        
+        use std::{mem, slice};
+
         slice::from_raw_parts(
             self.data.as_ptr() as *const u8,
             self.data.len() * mem::size_of::<T>(),
@@ -122,6 +143,17 @@
     }
 }
 
+impl<T: Clone> Vec2D<T> {
+    pub fn from_iter<I: IntoIterator<Item = T>>(iter: I, size: &Size) -> Option<Vec2D<T>> {
+        let data: Vec<T> = iter.into_iter().collect();
+        if size.width * size.height == data.len() {
+            Some(Vec2D { data, size: *size })
+        } else {
+            None
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
Binary file share/hedgewars/Data/Tiles/120_bar.png has changed
Binary file share/hedgewars/Data/Tiles/120_corner.png has changed
Binary file share/hedgewars/Data/Tiles/120_filled.png has changed
Binary file share/hedgewars/Data/Tiles/120_two_corners.png has changed
--- a/share/hedgewars/Data/map_templates.yaml	Thu Aug 24 20:15:40 2023 +0200
+++ b/share/hedgewars/Data/map_templates.yaml	Tue Sep 05 17:02:08 2023 +0200
@@ -2053,7 +2053,7 @@
         - {x: 3925, y: 2098, w: 75, h: 50}
         - {x: 4050, y: 2173, w: 50, h: 75}
     fill_points:
-      - {x: 4095, y: 0}
+      - {x: 4094, y: 0}
 
 
 
@@ -2086,9 +2086,9 @@
     fill_points:
       - {x: 1, y: 90}
       - {x: 1, y: 500}
-      - {x: 4095, y: 500}
+      - {x: 4094, y: 500}
       - {x: 1, y: 1200}
-      - {x: 4095, y: 1200}
+      - {x: 4094, y: 1200}
       - {x: 1, y: 2010}
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/wfc_templates.yaml	Tue Sep 05 17:02:08 2023 +0200
@@ -0,0 +1,196 @@
+---
+# Templates for wavefront collapse map generator in hedgewars
+
+templates:
+  - &template_00
+    width: 3960
+    height: 1920
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 40
+    wrap: true
+    edges:
+      bottom:
+        name: "ff"
+        symmetrical: true
+    tiles: &template_00_tiles
+      - name: "120_bar.png"
+        edges:
+          top:
+            name: "ff"
+            symmetrical: true
+          right:
+            name: "fe"
+          bottom:
+            name: "ee"
+            symmetrical: true
+          left:
+            name: "fe"
+            reversed: true
+        is_negative: true
+        can_mirror: false
+        can_flip: false
+        can_rotate90: true
+        can_rotate180: true
+        can_rotate270: true
+      - name: "120_corner.png"
+        edges:
+          top:
+            name: "fe"
+          right:
+            name: "ee"
+            symmetrical: true
+          bottom:
+            name: "ee"
+            symmetrical: true
+          left:
+            name: "fe"
+            reversed: true
+        is_negative: true
+        can_mirror: false
+        can_flip: false
+        can_rotate90: true
+        can_rotate180: true
+        can_rotate270: true
+      - name: "120_corner.png"
+        edges:
+          top:
+            name: "fe"
+            reversed: true
+          right:
+            name: "ff"
+            symmetrical: true
+          bottom:
+            name: "ff"
+            symmetrical: true
+          left:
+            name: "fe"
+        is_negative: false
+        can_mirror: false
+        can_flip: false
+        can_rotate90: true
+        can_rotate180: true
+        can_rotate270: true
+      - name: "120_filled.png"
+        edges:
+          top:
+            name: "ff"
+            symmetrical: true
+          right:
+            name: "ff"
+            symmetrical: true
+          bottom:
+            name: "ff"
+            symmetrical: true
+          left:
+            name: "ff"
+            symmetrical: true
+        is_negative: true
+        can_mirror: false
+        can_flip: false
+        can_rotate90: false
+        can_rotate180: false
+        can_rotate270: false
+      - name: "120_filled.png"
+        edges:
+          top:
+            name: "ee"
+            symmetrical: true
+          right:
+            name: "ee"
+            symmetrical: true
+          bottom:
+            name: "ee"
+            symmetrical: true
+          left:
+            name: "ee"
+            symmetrical: true
+        is_negative: false
+        can_mirror: false
+        can_flip: false
+        can_rotate90: false
+        can_rotate180: false
+        can_rotate270: false
+      - name: "120_two_corners.png"
+        edges:
+          top:
+            name: "fe"
+          right:
+            name: "fe"
+            reversed: true
+          bottom:
+            name: "fe"
+          left:
+            name: "fe"
+            reversed: true
+        is_negative: true
+        can_mirror: true
+        can_flip: false
+        can_rotate90: false
+        can_rotate180: false
+        can_rotate270: false
+
+  - &template_01
+    width: 3960
+    height: 1920
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 40
+    wrap: false
+    edges: &open_edges
+      top:
+        name: "ee"
+        symmetrical: true
+      right:
+        name: "ee"
+        symmetrical: true
+      bottom:
+        name: "ff"
+        symmetrical: true
+      left:
+        name: "ee"
+        symmetrical: true
+    tiles: *template_00_tiles
+
+  - &template_02
+    width: 1200
+    height: 600
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 24
+    wrap: false
+    edges: *open_edges
+    tiles: *template_00_tiles
+
+  - &template_03
+    width: 720
+    height: 7920
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 64
+    wrap: false
+    edges: *open_edges
+    tiles: *template_00_tiles
+
+
+  - &template_04
+    width: 2200
+    height: 960
+    can_invert: false
+    is_negative: false
+    put_girders: true
+    max_hedgehogs: 24
+    wrap: false
+    edges: *open_edges
+    tiles: *template_00_tiles
+
+template_types:
+  small: [2]
+  medium: [4]
+  large: [1]
+  cavern: [0]
+  wacky: [3]