Make tardis avoid water during Sudden Death
authorWuzzy <Wuzzy@disroot.org>
Thu, 25 May 2023 17:05:15 +0200
changeset 15986 7125918637e9
parent 15985 a4630009e733
child 15987 e8d94f84d294
Make tardis avoid water during Sudden Death
hedgewars/uGearsHandlersMess.pas
hedgewars/uGearsUtils.pas
--- a/hedgewars/uGearsHandlersMess.pas	Wed May 24 23:58:28 2023 +0200
+++ b/hedgewars/uGearsHandlersMess.pas	Thu May 25 17:05:15 2023 +0200
@@ -6410,6 +6410,7 @@
 procedure doStepTardisWarp(Gear: PGear);
 var HH: PHedgehog;
     i,j,cnt: LongWord;
+    restoreBottomY: LongInt;
     s: ansistring;
 begin
 HH:= Gear^.Hedgehog;
@@ -6506,8 +6507,37 @@
                     inc(cnt);
     if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then
         begin
+        // Place tardis
         if HH^.GearHidden <> nil then
-            FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH, true, false);
+            begin
+            restoreBottomY:= cWaterLine;
+            // Place tardis at a random safe position
+            FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH, restoreBottomY, true, false);
+
+            // If in Sudden Death, rise the minimum possible spawn position to make
+            // it less likely for the hog to drown before its turn
+            if SuddenDeathActive and (cWaterRise > 0) then
+                begin
+                // Enough space to survive the water rise of 1 round.
+                // Also limit the highest spawn height to topY plus a small buffer zone
+                restoreBottomY:= max(topY + cHHRadius * 5, cWaterLine - cWaterRise * (TeamsCount + 1));
+                // If gear is below the safe spawn height, place it again,
+                // but this time with the height limit in place
+                if (HH^.GearHidden <> nil) and (hwRound(HH^.GearHidden^.Y) > restoreBottomY) then
+                    // Due to the reduced Y range, this one might fail for very aggressive SD water rise
+                    begin
+                    FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH, restoreBottomY, true, false);
+                    end;
+                // Still unsafe? Relax the height limit to a third of the map height above cWaterLine
+                if (HH^.GearHidden <> nil) and (hwRound(HH^.GearHidden^.Y) > restoreBottomY) then
+                    begin
+                    restoreBottomY:= cWaterLine - ((cWaterLine - topY) div 3);
+                    // Even this might fail, but it's much less likely. If it fails, we still have the
+                    // position of the first FindPlace as a fallback.
+                    FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH, restoreBottomY, true, false);
+                    end;
+                end;
+            end;
 
         if HH^.GearHidden <> nil then
             begin
--- a/hedgewars/uGearsUtils.pas	Wed May 24 23:58:28 2023 +0200
+++ b/hedgewars/uGearsUtils.pas	Thu May 25 17:05:15 2023 +0200
@@ -42,6 +42,7 @@
 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); inline;
 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);
 function CountLand(x, y, r, c: LongInt; mask, antimask: LongWord): LongInt;
 
 function  CheckGearNear(Kind: TGearType; X, Y: hwFloat; rX, rY: LongInt): PGear;
@@ -934,7 +935,12 @@
     FindPlace(Gear, withFall, Left, Right, skipProximity, true);
 end;
 
-procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity, deleteOnFail: boolean);
+procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity, deleteOnFail: boolean); inline;
+begin
+    FindPlace(Gear, withFall, Left, Right, cWaterLine, skipProximity, deleteOnFail);
+end;
+
+procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right, Bottom: LongInt; skipProximity, deleteOnFail: boolean);
 var x: LongInt;
     y, sy, dir: LongInt;
     ar: array[0..1023] of TPoint;
@@ -963,11 +969,11 @@
         repeat
             cnt:= 0;
             y:= min(1024, topY) - Gear^.Radius shl 1;
-            while y < cWaterLine do
+            while y < Bottom do
                 begin
                 repeat
                     inc(y, 2);
-                until (y >= cWaterLine) or
+                until (y >= Bottom) or
                     (ignoreOverLap and (CountLand(x, y, Gear^.Radius - 1, 1, lfLandMask, 0) = 0)) or
                     (not ignoreOverLap and (CountLand(x, y, Gear^.Radius - 1, 1, lfAll, 0) = 0));
 
@@ -975,13 +981,13 @@
 
                 repeat
                     inc(y);
-                until (y >= cWaterLine) or
+                until (y >= Bottom) or
                         (ignoreOverlap and 
                                 (CountLand(x, y, Gear^.Radius - 1, 1, lfAll, 0) <> 0)) or
                         (not ignoreOverlap and 
                             (CountLand(x, y, Gear^.Radius - 1, 1, lfLandMask, 0) <> 0));
 
-                if (y - sy > Gear^.Radius * 2) and (y < cWaterLine)
+                if (y - sy > Gear^.Radius * 2) and (y < Bottom)
                     and (((Gear^.Kind = gtExplosives)
                         and (ignoreNearObjects or NoGearsToAvoid(x, y - Gear^.Radius, 60, 60))
                         and (isSteadyPosition(x, y+1, Gear^.Radius - 1, 3, lfAll)