Fix bee targeting fail across wrap world edge
authorWuzzy <Wuzzy2@mail.ru>
Thu, 03 Jan 2019 19:46:48 +0100
changeset 14519 5ac181cb2396
parent 14518 5230c063214a
child 14520 b54ba97c02d7
Fix bee targeting fail across wrap world edge Previously, the bee always aimed for the light area, no matter where you actually put the target. It also got confused whenever it flew across the wrap world edge. How the bee works now: 1) The placed bee target is *not* recalculated when it was placed in the "gray" part of the wrap world edge. This allows for more fine-tuning. 1a) Place target in light area: bee aims for target light area 1b) Place target in gray area: bee aims for target, but flies to gray area first 2) Bee target is recalculated whenever bee passes the wrap world edge.
ChangeLog.txt
hedgewars/uConsts.pas
hedgewars/uGearsHandlersMess.pas
hedgewars/uGearsUtils.pas
hedgewars/uIO.pas
hedgewars/uVariables.pas
--- a/ChangeLog.txt	Thu Jan 03 17:57:31 2019 +0100
+++ b/ChangeLog.txt	Thu Jan 03 19:46:48 2019 +0100
@@ -13,6 +13,7 @@
  + Don't show crate spawn message for initial crates in missions
  * Use player-chosen team identity in campaigns and singleplayer missions
  * Fix last 2 characters in demo chat being missing
+ * Fix homing bee flying weird if passing wrap world edge or target was placed beyond it
  * King Mode: Fix team sometimes not being killed properly if king drowned
  * King Mode: Kill resurrected minions if king is not alive
  * Fix poison damage not working in first round
--- a/hedgewars/uConsts.pas	Thu Jan 03 17:57:31 2019 +0100
+++ b/hedgewars/uConsts.pas	Thu Jan 03 19:46:48 2019 +0100
@@ -336,6 +336,7 @@
                           = $00100000; // doesn't stop timer while Attacking gear msg is set and inf. attack mode is on
     ammoprop_ForceTurnEnd = $00200000; // always ends turn after usage, ignoring inf. attack
     ammoprop_NoTargetAfter= $00400000; // disable target selection after attack
+    ammoprop_NoWrapTarget = $00800000; // allow to select target beyond wrap world edge limits
     ammoprop_NoRoundEnd   = $10000000; // ammo doesn't end turn
 
     AMMO_INFINITE = 100;               // internal representation of infinite ammo count
--- a/hedgewars/uGearsHandlersMess.pas	Thu Jan 03 17:57:31 2019 +0100
+++ b/hedgewars/uGearsHandlersMess.pas	Thu Jan 03 19:46:48 2019 +0100
@@ -1046,11 +1046,14 @@
 var
     t: hwFloat;
     gX,gY,i: LongInt;
-    uw, nuw: boolean;
+    uw, nuw, wrapped: boolean;
     flower: PVisualGear;
 
 begin
-    WorldWrap(Gear);
+    wrapped:= WorldWrap(Gear);
+    if wrapped then
+        HomingWrap(Gear);
+
     AllInactive := false;
     gX := hwRound(Gear^.X);
     gY := hwRound(Gear^.Y);
--- a/hedgewars/uGearsUtils.pas	Thu Jan 03 17:57:31 2019 +0100
+++ b/hedgewars/uGearsUtils.pas	Thu Jan 03 19:46:48 2019 +0100
@@ -64,6 +64,7 @@
 function  GetUtility(Hedgehog: PHedgehog): TAmmoType;
 
 function WorldWrap(var Gear: PGear): boolean;
+function HomingWrap(var Gear: PGear): boolean;
 
 function IsHogLocal(HH: PHedgehog): boolean;
 
@@ -1809,6 +1810,47 @@
     end;
 end;
 
+(*
+Applies wrap-around logic for the target of homing gears.
+
+In wrap-around world edge, the shortest way may to the target might
+be across the border, so the X value of the target would lead the
+gear to the wrong direction across the whole map. This procedure
+changes the target X in this case.
+This function must be called after the gear passed through
+the wrap-around world edge (WorldWrap returned true).
+
+No-op for other world edges.
+
+Returns true if target has been changed.
+*)
+function HomingWrap(var Gear: PGear): boolean;
+var dist_center, dist_right, dist_left: hwFloat;
+begin
+    if WorldEdge = weWrap then
+        begin
+        HomingWrap:= false;
+        // We just check the same target 3 times:
+        // 1) in current section (no change)
+        // 2) clone in the right section
+        // 3) clone in the left section
+        // The gear will go for the target with the shortest distance to the gear.
+        // For simplicity, we only check distance on the X axis.
+        dist_center:= hwAbs(Gear^.X - int2hwFloat(Gear^.Target.X));
+        dist_right:= hwAbs(Gear^.X - int2hwFloat(Gear^.Target.X + (RightX-LeftX)));
+        dist_left:= hwAbs(Gear^.X - int2hwFloat(Gear^.Target.X - (RightX-LeftX)));
+        if (dist_left < dist_right) and (dist_left < dist_center) then
+            begin
+            dec(Gear^.Target.X, RightX-LeftX);
+            HomingWrap:= true;
+            end
+        else if (dist_right < dist_left) and (dist_right < dist_center) then
+            begin
+            inc(Gear^.Target.X, RightX-LeftX);
+            HomingWrap:= true;
+            end;
+        end;
+end;
 
 // Add an audiovisual bounce effect for gear after it bounced from bouncy material.
 // Graphical effect is based on speed.
--- a/hedgewars/uIO.pas	Thu Jan 03 17:57:31 2019 +0100
+++ b/hedgewars/uIO.pas	Thu Jan 03 19:46:48 2019 +0100
@@ -527,13 +527,14 @@
                 TargetPoint.X:= CursorPoint.X - WorldDx;
                 TargetPoint.Y:= cScreenHeight - CursorPoint.Y - WorldDy;
                 end;
-            if (WorldEdge <> weBounce) then
+            if (WorldEdge <> weBounce) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoWrapTarget) = 0) then
                 TargetPoint.X:= CalcWorldWrap(TargetPoint.X, 0);
             SendIPCXY('p', TargetPoint.X, TargetPoint.Y);
             end
         else
             begin
-            TargetPoint.X:= CalcWorldWrap(TargetPoint.X, 0);
+            if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoWrapTarget) = 0 then
+                TargetPoint.X:= CalcWorldWrap(TargetPoint.X, 0);
             TargetPoint.X:= putX;
             TargetPoint.Y:= putY
             end;
--- a/hedgewars/uVariables.pas	Thu Jan 03 17:57:31 2019 +0100
+++ b/hedgewars/uVariables.pas	Thu Jan 03 19:46:48 2019 +0100
@@ -951,6 +951,7 @@
             Ammo: (Propz: ammoprop_Power or
                           ammoprop_NeedTarget or
                           ammoprop_NoTargetAfter or
+                          ammoprop_NoWrapTarget or
                           ammoprop_DontHold or
                           ammoprop_NeedUpDown;
                 Count: 2;