hedgewars/uGearsHandlersMess.pas
changeset 12898 8a40ce061d94
parent 12837 31dd78cbf729
child 12914 0de553fb7e89
--- a/hedgewars/uGearsHandlersMess.pas	Mon Jan 15 12:15:56 2018 -0500
+++ b/hedgewars/uGearsHandlersMess.pas	Wed Jan 31 13:42:52 2018 -0500
@@ -139,6 +139,9 @@
 //procedure doStepCreeper(Gear: PGear);
 procedure doStepKnife(Gear: PGear);
 procedure doStepDuck(Gear: PGear);
+procedure doStepMinigunWork(Gear: PGear);
+procedure doStepMinigun(Gear: PGear);
+procedure doStepMinigunBullet(Gear: PGear);
 
 var
     upd: Longword;
@@ -1234,16 +1237,45 @@
             end;
 end;
 
+procedure LineShoveHelp(Gear: PGear; oX, oY, tX, tY, dX, dY: hwFloat; count: LongWord);
+var dmg,power: LongInt;
+begin
+    if ((Gear^.Kind = gtMinigunBullet) or (Gear^.Damage > 0))
+    and (hwSqr(tX - oX) + hwSqr(tY - oY) > _0_25) then
+    begin
+        if (Gear^.AmmoType = amDEagle) or (Gear^.AmmoType = amMinigun) then
+            dmg:= Gear^.Boom
+        else
+            dmg:= Gear^.Timer * Gear^.Boom div 100000;
+        if (Gear^.AmmoType = amMinigun) then
+            power:= 10
+        else
+            power:= 20;
+        AmmoShoveLine(Gear, dmg, power, oX, oY, tX, tY);
+    end;
+    if Gear^.Damage > 0 then
+    begin
+        DrawTunnel(oX, oY, dX, dY, count, 1);
+        dec(Gear^.Health, Gear^.Damage);
+        Gear^.Damage := 0
+    end;
+end;
+
 procedure doStepBulletWork(Gear: PGear);
 var
     i, x, y, iInit: LongWord;
     oX, oY, tX, tY, tDx, tDy: hwFloat;
     VGear: PVisualGear;
+    LandFlags: Word;
+    isDigging: Boolean;
+    isDead: Boolean;
 begin
     AllInactive := false;
     inc(Gear^.Timer);
-    iInit := 80;
+    iInit := 100;
     i := iInit;
+    isDigging := false;
+    isDead := false;
     oX := Gear^.X;
     oY := Gear^.Y;
     repeat
@@ -1255,7 +1287,7 @@
         tDy:= Gear^.dY;
         if (Gear^.PortalCounter < 30) and WorldWrap(Gear) then
             begin
-            DrawTunnel(oX, oY, tDx, tDy, iInit + 2 - i, 1);
+            LineShoveHelp(Gear, oX, oY, tX, tY, tDx, tDy, iInit + 2 - i);
             SpawnBulletTrail(Gear, tX, tY);
             iInit:= i;
             oX:= Gear^.X;
@@ -1268,38 +1300,45 @@
         x := hwRound(Gear^.X);
         y := hwRound(Gear^.Y);
 
-        if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then
-            inc(Gear^.Damage);
-        // let's interrupt before a collision to give portals a chance to catch the bullet
-        if (Gear^.Damage = 1) and (Gear^.Tag = 0) and (not CheckLandValue(x, y, lfLandMask)) then
+        if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
+        begin
+            LandFlags:= Land[y, x];
+            if LandFlags <> 0 then inc(Gear^.Damage);
+            isDigging:= (LandFlags and lfLandMask) <> 0;
+        end;
+        // let's interrupt before a collision with land to give portals a chance to catch the bullet
+        if isDigging and (Gear^.Tag = 0) then
             begin
             Gear^.Tag := 1;
-            Gear^.Damage := 0;
+            dec(Gear^.Damage);
             Gear^.X := Gear^.X - Gear^.dX;
             Gear^.Y := Gear^.Y - Gear^.dY;
             CheckGearDrowning(Gear);
             break;
             end
-        else
+        else if (not isDigging) then
             Gear^.Tag := 0;
 
-        if Gear^.Damage > 5 then
-            begin
-            if Gear^.AmmoType = amDEagle then
-                AmmoShove(Gear, Gear^.Boom, 20)
-            else
-                AmmoShove(Gear, Gear^.Timer * Gear^.Boom div 100000, 20);
+        //Shove static gears to remove the mask and stop damaging the bullet
+        if (not isDigging) and (Gear^.Damage > 5) and (Gear^.Kind <> gtMinigunBullet) then
+            begin
+            LineShoveHelp(Gear, oX, oY, tX, tY, tDx, tDy, iInit + 2 - i);
+            SpawnBulletTrail(Gear, tX, tY);
+            iInit:= i;
+            oX:= Gear^.X;
+            oY:= Gear^.Y;
             end;
+
         CheckGearDrowning(Gear);
+        case Gear^.Kind of
+            gtMinigunBullet: isDead:= isDigging;
+            gtDEagleShot, gtSniperRifleShot: isDead:= Gear^.Damage >= Gear^.Health;
+        end;
         dec(i)
-    until (i = 0) or (Gear^.Damage > Gear^.Health) or ((Gear^.State and gstDrowning) <> 0);
-
-    if Gear^.Damage > 0 then
-        begin
-        DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, iInit + 2 - i, 1);
-        dec(Gear^.Health, Gear^.Damage);
-        Gear^.Damage := 0
-        end;
+    until (i = 0) or (isDead) or ((Gear^.State and gstDrowning) <> 0);
+
+    LineShoveHelp(Gear, oX, oY, Gear^.X, Gear^.Y,
+                  Gear^.dX, Gear^.dY, iInit + 2 - i);
 
     if ((Gear^.State and gstDrowning) <> 0) and (Gear^.Health > 0) then
         begin
@@ -1318,7 +1357,7 @@
         Gear^.Health:= 0;
         end;
 
-    if (Gear^.Health <= 0)
+    if (isDead)
         or (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0)
         or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
             begin
@@ -1330,7 +1369,15 @@
         // Bullet Hit
             if ((Gear^.State and gstDrowning) = 0) and (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then
                 begin
-                VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit);
+                if Gear^.Kind = gtMinigunBullet then
+                    begin
+                    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 5,
+                                    Gear^.Hedgehog, EXPLNoDamage{ or EXPLDontDraw or EXPLNoGfx});
+                    VGear := AddVisualGear(hwRound(Gear^.X + Gear^.dX * 5), hwRound(Gear^.Y + Gear^.dY * 5), vgtBulletHit);
+                    end
+                else
+                    VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit);
+
                 if VGear <> nil then
                     begin
                     VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY);
@@ -1338,6 +1385,8 @@
                 end;
 
             spawnBulletTrail(Gear, Gear^.X, Gear^.Y);
+            if Gear^.Kind = gtMinigunBullet then
+                ClearHitOrderLeq(Gear^.Tag);
             Gear^.doStep := @doStepShotIdle
             end;
 end;
@@ -1357,8 +1406,7 @@
 end;
 
 procedure doStepSniperRifleShot(Gear: PGear);
-var
-    HHGear: PGear;
+var HHGear: PGear;
     shell: PVisualGear;
 begin
 
@@ -4454,7 +4502,7 @@
             // Make duck go into “falling” mode again
             iterator^.Pos:= 0;
 
-        isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]);
+        isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot, gtMinigunBullet]);
 
         r:= int2hwFloat(iterator^.Radius);
 
@@ -4481,7 +4529,7 @@
                 continue;
             end;
 
-        if (iterator^.Kind = gtDEagleShot) or (iterator^.Kind = gtSniperRifleShot) then
+        if (iterator^.Kind in [gtDEagleShot, gtSniperRifleShot, gtMinigunBullet]) then
             begin
             // draw bullet trail
             spawnBulletTrail(iterator, iterator^.X, iterator^.Y);
@@ -6564,6 +6612,95 @@
     dec(Gear^.Timer);
 end;
 
+////////////////////////////////////////////////////////////////////////////////
+procedure doStepMinigunWork(Gear: PGear);
+var HHGear: PGear;
+    i: LongWord;
+    shell: PVisualGear;
+    bullet: PGear;
+    rx, ry: hwFloat;
+    gX, gY: LongInt;
+begin
+    AllInactive:= false;
+    HHGear := Gear^.Hedgehog^.Gear;
+    if HHGear = nil then
+    begin
+        ClearHitOrder();
+        DeleteGear(gear);
+        exit
+    end;
+
+    HedgehogChAngle(HHGear);
+
+    dec(Gear^.Timer);
+    if (Gear^.Timer mod 50) = 0 then
+    begin
+        Gear^.Tag := ((Gear^.Tag - 1) and 1) + 2;
+
+        gX := hwRound(Gear^.X) + GetLaunchX(amMinigun, hwSign(HHGear^.dX), HHGear^.Angle);
+        gY := hwRound(Gear^.Y) + GetLaunchY(amMinigun, HHGear^.Angle);
+        rx := rndSign(getRandomf * _0_2);
+        ry := rndSign(getRandomf * _0_2);
+
+        bullet:= AddGear(gx, gy, gtMinigunBullet, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0);
+        bullet^.CollisionMask:= lfNotCurrentMask;
+        bullet^.WDTimer := Gear^.WDTimer;
+        Inc(Gear^.WDTimer);
+
+        shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell);
+        if shell <> nil then
+        begin
+            shell^.dX := gear^.dX.QWordValue / -17179869184;
+            shell^.dY := gear^.dY.QWordValue / -17179869184;
+            shell^.Frame := 0
+        end;
+    end;
+
+    if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then
+    begin
+        HHGear^.State := HHGear^.State and (not gstNotKickable);
+        ClearHitOrder();
+        DeleteGear(Gear);
+        AfterAttack
+    end
+end;
+
+procedure doStepMinigun(Gear: PGear);
+var HHGear: PGear;
+begin
+    dec(Gear^.Timer);
+    if (Gear^.Timer mod 100) = 0 then
+        Gear^.Tag := (Gear^.Tag + 1) and 1;
+
+    if Gear^.Timer = 0 then
+        begin
+        Gear^.Tag := 2;
+        HHGear := Gear^.Hedgehog^.Gear;
+        HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown));
+        HHGear^.State := HHGear^.State or gstNotKickable;
+
+        Gear^.Timer := 3501;
+        Gear^.WDTimer := 0; // Order of the next bullet;
+        ClearHitOrder();
+        Gear^.doStep := @doStepMinigunWork
+        end;
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+
+procedure doStepMinigunBullet(Gear: PGear);
+begin
+    Gear^.Data:= nil;
+    // remember who fired this
+    if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) then
+        Gear^.Data:= Pointer(Gear^.Hedgehog^.Gear);
+
+    PlaySound(sndGun);
+    Gear^.X := Gear^.X + Gear^.dX * 2;
+    Gear^.Y := Gear^.Y + Gear^.dY * 2;
+    Gear^.doStep := @doStepBulletWork
+end;
+
 (*
  This didn't end up getting used, but, who knows, might be reasonable for javellin or something
 // Make the knife initial angle based on the hog attack angle, or is that too hard?