hedgewars/uCollisions.pas
branchui-scaling
changeset 15288 c4fd2813b127
parent 14536 e0af4ce7d8bc
child 15306 361e79c6c428
--- a/hedgewars/uCollisions.pas	Wed May 16 18:22:28 2018 +0200
+++ b/hedgewars/uCollisions.pas	Wed Jul 31 23:14:27 2019 +0200
@@ -24,6 +24,7 @@
 
 const cMaxGearArrayInd = 1023;
 const cMaxGearHitOrderInd = 1023;
+const cMaxGearProximityCacheInd = 1023;
 
 type PGearArray = ^TGearArray;
     TGearArray = record
@@ -40,6 +41,12 @@
         Count: Longword
         end;
 
+type PGearProximityCache = ^TGearProximityCache;
+    TGearProximityCache = record
+        ar: array[0..cMaxGearProximityCacheInd] of PGear;
+        Count: Longword
+        end;
+
 type TLineCollision = record
         hasCollision: Boolean;
         cX, cY: LongInt; //for visual effects only
@@ -53,6 +60,7 @@
 
 function  CheckGearsCollision(Gear: PGear): PGearArray;
 function  CheckAllGearsCollision(SourceGear: PGear): PGearArray;
+function  CheckCacheCollision(SourceGear: PGear): PGearArray;
 
 function  CheckGearsLineCollision(Gear: PGear; oX, oY, tX, tY: hwFloat): PGearArray;
 function  CheckAllGearsLineCollision(SourceGear: PGear; oX, oY, tX, tY: hwFloat): PGearArray;
@@ -61,6 +69,10 @@
 procedure ClearHitOrderLeq(MinOrder: LongInt);
 procedure ClearHitOrder();
 
+procedure RefillProximityCache(SourceGear: PGear; radius: LongInt);
+procedure RemoveFromProximityCache(Gear: PGear);
+procedure ClearProximityCache();
+
 function  TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word;
 function  TestCollisionYwithGear(Gear: PGear; Dir: LongInt): Word;
 
@@ -97,6 +109,7 @@
     cinfos: array[0..MAXRECTSINDEX] of TCollisionEntry;
     ga: TGearArray;
     ordera: TGearHitOrder;
+    proximitya: TGearProximityCache;
 
 procedure AddCI(Gear: PGear);
 begin
@@ -132,7 +145,7 @@
 function CheckCoordInWater(X, Y: LongInt): boolean; inline;
 begin
     CheckCoordInWater:= (Y > cWaterLine)
-        or ((WorldEdge = weSea) and ((X < LongInt(leftX)) or (X > LongInt(rightX))));
+        or ((WorldEdge = weSea) and ((X < leftX) or (X > rightX)));
 end;
 
 function CheckGearsCollision(Gear: PGear): PGearArray;
@@ -180,8 +193,8 @@
                (sqr(mx - hwRound(Gear^.x)) + sqr(my - hwRound(Gear^.y)) <= sqr(Gear^.Radius + tr))then
             begin
                 ga.ar[ga.Count]:= Gear;
-                ga.cX[ga.Count]:= hwround(SourceGear^.X);
-                ga.cY[ga.Count]:= hwround(SourceGear^.Y);
+                ga.cX[ga.Count]:= mx;
+                ga.cY[ga.Count]:= my;
                 inc(ga.Count)
             end;
 
@@ -287,6 +300,33 @@
     end;
 end;
 
+function CheckCacheCollision(SourceGear: PGear): PGearArray;
+var mx, my, tr, i: LongInt;
+    Gear: PGear;
+begin
+    CheckCacheCollision:= @ga;
+    ga.Count:= 0;
+
+    mx:= hwRound(SourceGear^.X);
+    my:= hwRound(SourceGear^.Y);
+
+    tr:= SourceGear^.Radius + 2;
+
+    for i:= 0 to proximitya.Count - 1 do
+    begin
+        Gear:= proximitya.ar[i];
+        // Assuming the cache has been filled correctly, it will not contain SourceGear
+        // and other gears won't be far enough for sqr overflow
+        if (sqr(mx - hwRound(Gear^.X)) + sqr(my - hwRound(Gear^.Y)) <= sqr(Gear^.Radius + tr)) then
+        begin
+            ga.ar[ga.Count]:= Gear;
+            ga.cX[ga.Count]:= mx;
+            ga.cY[ga.Count]:= my;
+            inc(ga.Count)
+        end;
+    end;
+end;
+
 function UpdateHitOrder(Gear: PGear; Order: LongInt): boolean;
 var i: LongInt;
 begin
@@ -337,14 +377,58 @@
     ordera.Count:= 0;
 end;
 
+procedure RefillProximityCache(SourceGear: PGear; radius: LongInt);
+var cx, cy, dx, dy, r: LongInt;
+    Gear: PGear;
+begin
+    proximitya.Count:= 0;
+    cx:= hwRound(SourceGear^.X);
+    cy:= hwRound(SourceGear^.Y);
+    Gear:= GearsList;
+
+    while (Gear <> nil) and (proximitya.Count <= cMaxGearProximityCacheInd) do
+    begin
+        dx:= abs(hwRound(Gear^.X) - cx);
+        dy:= abs(hwRound(Gear^.Y) - cy);
+        r:= radius + Gear^.radius + 2;
+        if (Gear <> SourceGear) and (max(dx, dy) <= r) and (sqr(dx) + sqr(dy) <= sqr(r)) then
+        begin
+            proximitya.ar[proximitya.Count]:= Gear;
+            inc(proximitya.Count)
+        end;
+        Gear := Gear^.NextGear
+    end;
+end;
+
+procedure RemoveFromProximityCache(Gear: PGear);
+var i: LongInt;
+begin
+    i := 0;
+    while i < proximitya.Count do
+        begin
+        if proximitya.ar[i] = Gear then
+            begin
+                proximitya.ar[i]:= proximitya.ar[proximitya.Count - 1];
+                dec(proximitya.Count);
+            end
+        else
+            inc(i);
+        end;
+end;
+
+procedure ClearProximityCache();
+begin
+    proximitya.Count:= 0;
+end;
+
 function TestCollisionXwithGear(Gear: PGear; Dir: LongInt): Word;
 var x, y, i: LongInt;
 begin
 // Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = lfNotCurrentMask) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = lfNotCurHogCrate) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
     ((hwRound(Gear^.Hedgehog^.Gear^.X) + Gear^.Hedgehog^.Gear^.Radius + 16 < hwRound(Gear^.X) - Gear^.Radius) or
      (hwRound(Gear^.Hedgehog^.Gear^.X) - Gear^.Hedgehog^.Gear^.Radius - 16 > hwRound(Gear^.X) + Gear^.Radius)) then
-    Gear^.CollisionMask:= $FFFF;
+    Gear^.CollisionMask:= lfAll;
 
 x:= hwRound(Gear^.X);
 if Dir < 0 then
@@ -370,10 +454,10 @@
 var x, y, i: LongInt;
 begin
 // Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = lfNotCurrentMask) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = lfNotCurHogCrate) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
     ((hwRound(Gear^.Hedgehog^.Gear^.Y) + Gear^.Hedgehog^.Gear^.Radius + 16 < hwRound(Gear^.Y) - Gear^.Radius) or
      (hwRound(Gear^.Hedgehog^.Gear^.Y) - Gear^.Hedgehog^.Gear^.Radius - 16 > hwRound(Gear^.Y) + Gear^.Radius)) then
-    Gear^.CollisionMask:= $FFFF;
+    Gear^.CollisionMask:= lfAll;
 
 y:= hwRound(Gear^.Y);
 if Dir < 0 then
@@ -417,7 +501,7 @@
             begin
             if Land[y, x] and Gear^.CollisionMask <> 0 then
                 begin
-                if Land[y, x] and Gear^.CollisionMask > 255 then
+                if ((Land[y, x] and Gear^.CollisionMask) and lfLandMask) <> 0 then
                     exit(Land[y, x] and Gear^.CollisionMask)
                 else
                     pixel:= Land[y, x] and Gear^.CollisionMask;
@@ -483,7 +567,7 @@
     if (x and LAND_WIDTH_MASK) = 0 then
         if Land[y, x] > 0 then
             begin
-            if Land[y, x] and Gear^.CollisionMask > 255 then
+            if ((Land[y, x] and Gear^.CollisionMask) and lfLandMask) <> 0 then
                 exit(Land[y, x] and Gear^.CollisionMask)
             else // if Land[y, x] <> 0 then
                 pixel:= Land[y, x] and Gear^.CollisionMask;
@@ -558,7 +642,7 @@
     i:= y + Gear^.Radius * 2 - 2;
     repeat
         if (y and LAND_HEIGHT_MASK) = 0 then
-            if Land[y, x] and Gear^.CollisionMask > 255 then
+            if ((Land[y, x] and Gear^.CollisionMask) and lfLandMask) <> 0 then
                 exit(Land[y, x] and Gear^.CollisionMask);
     inc(y)
     until (y > i);
@@ -581,7 +665,7 @@
     i:= x + Gear^.Radius * 2 - 2;
     repeat
         if (x and LAND_WIDTH_MASK) = 0 then
-            if Land[y, x] and Gear^.CollisionMask > 255 then
+            if ((Land[y, x] and Gear^.CollisionMask) and lfLandMask) <> 0 then
                 exit(Land[y, x] and Gear^.CollisionMask);
     inc(x)
     until (x > i);
@@ -897,7 +981,7 @@
     i:= x + Gear^.Radius * 2 - 2;
     repeat
     if (x and LAND_WIDTH_MASK) = 0 then
-        if Land[y, x] > 255 then
+        if (Land[y, x] and lfLandMask) <> 0 then
             if (not isColl) or (abs(x-gx) < abs(collX-gx)) then
                 begin
                 isColl:= true;