portal: combating the space-detection issue.
authorsheepluva
Thu, 18 Aug 2011 00:14:43 +0200
changeset 5594 3ab68a93434b
parent 5592 662ebb46642f
child 5596 555c4fc1f3bd
child 5601 92a7336043d6
portal: combating the space-detection issue. what needs to be tweaked next is how far away objects are spawned. it should either be a trial&error adjustment or depend on the object's moving direction and the portal's angle in order to avoid issues with box collisions. if the spawn distance to the portal is just a fixed value (as it was before this patch) or a constant factor (it is now) it can be too big for "nice" portal angles (in tight gaps with still enough space, but -> denied) and too small for bad angles (box collision esp. on big objects like barrels -> denied)
hedgewars/GSHandlers.inc
hedgewars/uGears.pas
--- a/hedgewars/GSHandlers.inc	Wed Aug 17 21:26:34 2011 +0400
+++ b/hedgewars/GSHandlers.inc	Thu Aug 18 00:14:43 2011 +0200
@@ -3731,9 +3731,10 @@
 procedure doStepPortal(Gear: PGear);
 var 
     iterator, conPortal: PGear;
-    s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed: hwFloat;
-    o_x,o_y,r_x,r_y,rr_x,rr_y: LongInt;
-    hasdxy, isbullet, iscake: Boolean;
+    s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed,
+    resetx, resety, resetdx, resetdy: hwFloat;
+    sx, sy, rh, resetr: LongInt;
+    hasdxy, isbullet, iscake, isCollision: Boolean;
 begin
     doPortalColorSwitch();
 
@@ -3803,67 +3804,6 @@
            or (iterator^.Y < Gear^.Y - r)
            or (iterator^.Y > Gear^.Y + r) then
             continue;
-(*
-Square check causes fail on many innocent cases. the 3/4s and 1.5 fudge factors... help.
-Might still need to remove this section
-*)
-        //Will if fit through?
-        //set r to be portal distance
-        r := Int2hwFloat(Gear^.Radius * 3 div 4);
-        o_x := hwRound(conPortal^.X + (conPortal^.dX*_1_5));
-        o_y := hwRound(conPortal^.Y + (conPortal^.dY*_1_5));
-        //r := Int2hwFloat(Gear^.Radius +1);
-
-        //o_x := hwRound(conPortal^.X + conPortal^.dX);
-        //o_y := hwRound(conPortal^.Y + conPortal^.dY);
-        r_x := hwRound(conPortal^.X+r*conPortal^.dX);
-        r_y := hwRound(conPortal^.Y+r*conPortal^.dY);
-        rr_x := hwRound(conPortal^.X+r*conPortal^.dX*2);
-        rr_y := hwRound(conPortal^.Y+r*conPortal^.dY*2);
-
-        //check outer edge
-        if (((rr_y and LAND_HEIGHT_MASK) <> 0) or ((rr_x and LAND_WIDTH_MASK) <> 0)) then
-            begin
-            if hasBorder then continue;
-            end
-        else
-            if ((Land[rr_y,rr_x] and $FF00) <> 0) then
-                continue;
-        //check middle bound
-        if (((r_y and LAND_HEIGHT_MASK) <> 0) or ((r_x and LAND_WIDTH_MASK) <> 0)) then
-            begin
-            if hasBorder then continue;
-            end
-        else
-        if ((Land[r_y, r_x] and $FF00) <> 0) then
-                continue;
-        //check inner bound
-        if (((o_y and LAND_HEIGHT_MASK) <> 0) or ((o_x and LAND_WIDTH_MASK) <> 0)) then
-            begin
-            if hasBorder then continue;
-            end
-        else
-        if ((Land[o_y, o_x] and $FF00) <> 0) then
-                continue;
-        //check left bound
-        if (((rr_y and LAND_HEIGHT_MASK) <> 0) or ((o_x and LAND_WIDTH_MASK) <> 0)) then
-            begin
-            if hasBorder then continue;
-            end
-        else
-        if ((Land[rr_y, o_x] and $FF00) <> 0) then
-                continue;
-        //Check Right Bound
-        if (((o_y and LAND_HEIGHT_MASK) <> 0) or ((rr_x and LAND_WIDTH_MASK) <> 0)) then
-            begin
-            if hasBorder then continue;
-            end
-        else
-        if ((Land[o_y, rr_x] and $FF00) <> 0) then
-                continue;
-
-        //Okay reset r in case something uses it
-        r := Int2hwFloat(iterator^.Radius+Gear^.Radius);
 
         hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0))
                     or ((iterator^.State or gstMoving) = 0));
@@ -3929,6 +3869,15 @@
         //
         // gears that make it till here will definately be ported
         //
+        // (but old position/movement vector might be restored in case there's
+        // not enough space on the other side)
+        //
+
+        resetr  := iterator^.Radius;
+        resetx  := iterator^.X;
+        resety  := iterator^.Y;
+        resetdx := iterator^.dX;
+        resetdy := iterator^.dY;
 
         // create a normal of the portal vector, but ...
         nx := Gear^.dY;
@@ -3948,9 +3897,6 @@
             and (iterator^.PortalCounter > 0) then
              continue;
 
-        // Until loops are reliably broken
-        inc(iterator^.PortalCounter);
-
         // calc gear speed along to the vector and the normal vector of the portal
         if hasdxy then
         begin
@@ -3988,7 +3934,7 @@
          if iscake then
              ox:= (r - _0_7)
          else
-             ox:= (r + _1_9);
+             ox:= (r * _1_5);
          s:= ox / poffs;
          poffs:= ox;
          if (nspeed.QWordValue <> 0) and (pspeed > _0) then
@@ -4010,15 +3956,57 @@
             iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y))
         end;
 
+        // see if the space on the exit side actually is enough
+
+        if not (isBullet or isCake) then
+        begin
+            // TestCollisionXwithXYShift requires a hwFloat for xShift
+            ox.QWordValue := _1.QWordValue;
+            ox.isNegative := not iterator^.dX.isNegative;
+
+            sx := hwSign(iterator^.dX);
+            sy := hwSign(iterator^.dY);
+
+            if iterator^.Radius > 1 then
+                iterator^.Radius := iterator^.Radius - 1;
+
+            // check front
+            isCollision := TestCollisionYwithGear(iterator, sy)
+                        or TestCollisionXwithGear(iterator, sx);
+
+            if not isCollision then
+            begin
+                // check center area (with half the radius so that the
+                // the square check won't check more pixels than we want to)
+                iterator^.Radius := 1 + resetr div 2;
+                rh := resetr div 4;
+                isCollision := TestCollisionYwithXYShift(iterator,       0, -sy * rh, sy)
+                            or TestCollisionXwithXYShift(iterator, ox * rh,        0, sx);
+            end;
+
+            iterator^.Radius := resetr;
+
+            if isCollision then
+            begin
+                // collision! oh crap! go back!
+                iterator^.X  := resetx;
+                iterator^.Y  := resety;
+                iterator^.dX := resetdx;
+                iterator^.dY := resetdy;
+                continue;
+            end;
+        end;
+
+        //
+        // You're now officially portaled!
+        //
+
+        // Until loops are reliably broken
+        inc(iterator^.PortalCounter);
+
         if not isbullet and (iterator^.Kind <> gtFlake) then
             FollowGear := iterator;
-//AddFileLog('portal''d');
-
-{
-        s := _0_2 + _0_008 * Gear^.Health;
-        iterator^.dX := s * iterator^.dX;
-        iterator^.dY := s * iterator^.dY;
-}
+
         // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
         iterator:= GearsList;
         while iterator <> nil do
@@ -4035,17 +4023,6 @@
             end;
 
         if Gear^.Health > 1 then dec(Gear^.Health);
-
-{        // breaks (some) loops
-        if Distance(iterator^.dX, iterator^.dY) > _0_96 then
-            begin
-            iterator^.dX := iterator^.dX + signAs(cGravity * getRandom(1000),iterator^.dX);
-            iterator^.dY := iterator^.dY + signAs(cGravity * getRandom(1000),iterator^.dY);
-            s := _0_96 / Distance(iterator^.dX, iterator^.dY);
-            iterator^.dX := s * iterator^.dX;
-            iterator^.dY := s * iterator^.dX;
-            end;
-}
     end;
 end;
 
--- a/hedgewars/uGears.pas	Wed Aug 17 21:26:34 2011 +0400
+++ b/hedgewars/uGears.pas	Thu Aug 18 00:14:43 2011 +0200
@@ -509,7 +509,7 @@
                 gear^.ImpactSound:= sndMelonImpact;
                 gear^.nImpactSounds:= 1;
                 gear^.AdvBounce:= 0;
-                gear^.Radius:= 16;
+                gear^.Radius:= 17;
                 // set color
                 gear^.Tag:= 2 * gear^.Timer;
                 gear^.Timer:= 15000;