hedgewars/uGearsHandlersMess.pas
changeset 9285 8e8b908970c2
parent 9277 ad1ec7212ac7
child 9289 6bc1df062f04
equal deleted inserted replaced
9283:76e68c136a11 9285:8e8b908970c2
       
     1 (*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2004-2013 Andrey Korotaev <unC0Rr@gmail.com>
       
     4  *
       
     5  * This program is free software; you can redistribute it and/or modify
       
     6  * it under the terms of the GNU General Public License as published by
       
     7  * the Free Software Foundation; version 2 of the License
       
     8  *
       
     9  * This program is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12  * GNU General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU General Public License
       
    15  * along with this program; if not, write to the Free Software
       
    16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
       
    17  *)
       
    18 
       
    19 (*
       
    20  * This file contains the step handlers for gears.
       
    21  *
       
    22  * Important: Since gears change the course of the game, calculations that
       
    23  *            lead to different results for different clients/players/machines
       
    24  *            should NOT occur!
       
    25  *            Use safe functions and data types! (e.g. GetRandom() and hwFloat)
       
    26  *)
       
    27  
       
    28  {$INCLUDE "options.inc"}
       
    29 
       
    30 unit uGearsHandlersMess;
       
    31 interface
       
    32 uses uTypes, uFloat;
       
    33 
       
    34 procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean);
       
    35 procedure makeHogsWorry(x, y: hwFloat; r: LongInt);
       
    36 procedure HideHog(HH: PHedgehog);
       
    37 procedure doStepDrowningGear(Gear: PGear);
       
    38 procedure doStepFallingGear(Gear: PGear);
       
    39 procedure doStepBomb(Gear: PGear);
       
    40 procedure doStepMolotov(Gear: PGear);
       
    41 procedure doStepCluster(Gear: PGear);
       
    42 procedure doStepShell(Gear: PGear);
       
    43 procedure doStepSnowball(Gear: PGear);
       
    44 procedure doStepSnowflake(Gear: PGear);
       
    45 procedure doStepGrave(Gear: PGear);
       
    46 procedure doStepBeeWork(Gear: PGear);
       
    47 procedure doStepBee(Gear: PGear);
       
    48 procedure doStepShotIdle(Gear: PGear);
       
    49 procedure doStepShotgunShot(Gear: PGear);
       
    50 procedure spawnBulletTrail(Bullet: PGear);
       
    51 procedure doStepBulletWork(Gear: PGear);
       
    52 procedure doStepDEagleShot(Gear: PGear);
       
    53 procedure doStepSniperRifleShot(Gear: PGear);
       
    54 procedure doStepActionTimer(Gear: PGear);
       
    55 procedure doStepPickHammerWork(Gear: PGear);
       
    56 procedure doStepPickHammer(Gear: PGear);
       
    57 procedure doStepBlowTorchWork(Gear: PGear);
       
    58 procedure doStepBlowTorch(Gear: PGear);
       
    59 procedure doStepMine(Gear: PGear);
       
    60 procedure doStepSMine(Gear: PGear);
       
    61 procedure doStepDynamite(Gear: PGear);
       
    62 procedure doStepRollingBarrel(Gear: PGear);
       
    63 procedure doStepCase(Gear: PGear);
       
    64 procedure doStepTarget(Gear: PGear);
       
    65 procedure doStepIdle(Gear: PGear);
       
    66 procedure doStepShover(Gear: PGear);
       
    67 procedure doStepWhip(Gear: PGear);
       
    68 procedure doStepFlame(Gear: PGear);
       
    69 procedure doStepFirePunchWork(Gear: PGear);
       
    70 procedure doStepFirePunch(Gear: PGear);
       
    71 procedure doStepParachuteWork(Gear: PGear);
       
    72 procedure doStepParachute(Gear: PGear);
       
    73 procedure doStepAirAttackWork(Gear: PGear);
       
    74 procedure doStepAirAttack(Gear: PGear);
       
    75 procedure doStepAirBomb(Gear: PGear);
       
    76 procedure doStepGirder(Gear: PGear);
       
    77 procedure doStepTeleportAfter(Gear: PGear);
       
    78 procedure doStepTeleportAnim(Gear: PGear);
       
    79 procedure doStepTeleport(Gear: PGear);
       
    80 procedure doStepSwitcherWork(Gear: PGear);
       
    81 procedure doStepSwitcher(Gear: PGear);
       
    82 procedure doStepMortar(Gear: PGear);
       
    83 procedure doStepKamikazeWork(Gear: PGear);
       
    84 procedure doStepKamikazeIdle(Gear: PGear);
       
    85 procedure doStepKamikaze(Gear: PGear);
       
    86 procedure doStepCakeExpl(Gear: PGear);
       
    87 procedure doStepCakeDown(Gear: PGear);
       
    88 procedure doStepCakeWork(Gear: PGear);
       
    89 procedure doStepCakeUp(Gear: PGear);
       
    90 procedure doStepCakeFall(Gear: PGear);
       
    91 procedure doStepCake(Gear: PGear);
       
    92 procedure doStepSeductionWork(Gear: PGear);
       
    93 procedure doStepSeductionWear(Gear: PGear);
       
    94 procedure doStepSeduction(Gear: PGear);
       
    95 procedure doStepWaterUp(Gear: PGear);
       
    96 procedure doStepDrillDrilling(Gear: PGear);
       
    97 procedure doStepDrill(Gear: PGear);
       
    98 procedure doStepBallgunWork(Gear: PGear);
       
    99 procedure doStepBallgun(Gear: PGear);
       
   100 procedure doStepRCPlaneWork(Gear: PGear);
       
   101 procedure doStepRCPlane(Gear: PGear);
       
   102 procedure doStepJetpackWork(Gear: PGear);
       
   103 procedure doStepJetpack(Gear: PGear);
       
   104 procedure doStepBirdyDisappear(Gear: PGear);
       
   105 procedure doStepBirdyFly(Gear: PGear);
       
   106 procedure doStepBirdyDescend(Gear: PGear);
       
   107 procedure doStepBirdyAppear(Gear: PGear);
       
   108 procedure doStepBirdy(Gear: PGear);
       
   109 procedure doStepEggWork(Gear: PGear);
       
   110 procedure doPortalColorSwitch();
       
   111 procedure doStepPortal(Gear: PGear);
       
   112 procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean);
       
   113 procedure doStepMovingPortal_real(Gear: PGear);
       
   114 procedure doStepMovingPortal(Gear: PGear);
       
   115 procedure doStepPortalShot(newPortal: PGear);
       
   116 procedure doStepPiano(Gear: PGear);
       
   117 procedure doStepSineGunShotWork(Gear: PGear);
       
   118 procedure doStepSineGunShot(Gear: PGear);
       
   119 procedure doStepFlamethrowerWork(Gear: PGear);
       
   120 procedure doStepFlamethrower(Gear: PGear);
       
   121 procedure doStepLandGunWork(Gear: PGear);
       
   122 procedure doStepLandGun(Gear: PGear);
       
   123 procedure doStepPoisonCloud(Gear: PGear);
       
   124 procedure doStepHammer(Gear: PGear);
       
   125 procedure doStepHammerHitWork(Gear: PGear);
       
   126 procedure doStepHammerHit(Gear: PGear);
       
   127 procedure doStepResurrectorWork(Gear: PGear);
       
   128 procedure doStepResurrector(Gear: PGear);
       
   129 procedure doStepNapalmBomb(Gear: PGear);
       
   130 procedure doStepStructure(Gear: PGear);
       
   131 procedure doStepTardisWarp(Gear: PGear);
       
   132 procedure doStepTardis(Gear: PGear);
       
   133 procedure updateFuel(Gear: PGear);
       
   134 procedure updateTarget(Gear:PGear; newX, newY:HWFloat);
       
   135 procedure doStepIceGun(Gear: PGear);
       
   136 procedure doStepAddAmmo(Gear: PGear);
       
   137 procedure doStepGenericFaller(Gear: PGear);
       
   138 procedure doStepCreeper(Gear: PGear);
       
   139 procedure doStepKnife(Gear: PGear);
       
   140 
       
   141 var
       
   142     upd: Longword;
       
   143     snowLeft,snowRight: LongInt;
       
   144 
       
   145 implementation
       
   146 uses uConsts, uVariables, uVisualGearsList, uRandom, uCollisions, uGearsList, uUtils, uSound
       
   147     , SDLh, uScript, uGearsHedgehog, uGearsUtils, uIO, uCaptions, uLandGraphics
       
   148     , uGearsHandlers, uTextures, uRenderUtils, uAmmos, uTeams, uLandTexture, uCommands
       
   149     , uStore, uAI, uStats;
       
   150 
       
   151 procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean);
       
   152 var
       
   153     dX, dY, sX, sY: hwFloat;
       
   154     i, steps: LongWord;
       
   155     caller: TGearStepProcedure;
       
   156 begin
       
   157     dX:= Gear^.dX;
       
   158     dY:= Gear^.dY;
       
   159     steps:= max(abs(hwRound(Gear^.X+dX)-hwRound(Gear^.X)), abs(hwRound(Gear^.Y+dY)-hwRound(Gear^.Y)));
       
   160 
       
   161     // Gear is still on the same Pixel it was before
       
   162     if steps < 1 then
       
   163         begin
       
   164         if onlyCheckIfChanged then
       
   165             begin
       
   166             Gear^.X := Gear^.X + dX;
       
   167             Gear^.Y := Gear^.Y + dY;
       
   168             EXIT;
       
   169             end
       
   170         else
       
   171             steps := 1;
       
   172         end;
       
   173 
       
   174     if steps > 1 then
       
   175         begin
       
   176         sX:= dX / steps;
       
   177         sY:= dY / steps;
       
   178         end
       
   179 
       
   180     else
       
   181         begin
       
   182         sX:= dX;
       
   183         sY:= dY;
       
   184         end;
       
   185 
       
   186     caller:= Gear^.doStep;
       
   187 
       
   188     for i:= 1 to steps do
       
   189         begin
       
   190         Gear^.X := Gear^.X + sX;
       
   191         Gear^.Y := Gear^.Y + sY;
       
   192         step(Gear);
       
   193         if (Gear^.doStep <> caller)
       
   194         or ((Gear^.State and gstCollision) <> 0)
       
   195         or ((Gear^.State and gstMoving) = 0) then
       
   196             break;
       
   197         end;
       
   198 end;
       
   199 
       
   200 procedure makeHogsWorry(x, y: hwFloat; r: LongInt);
       
   201 var
       
   202     gi: PGear;
       
   203     d: LongInt;
       
   204 begin
       
   205     gi := GearsList;
       
   206     while gi <> nil do
       
   207         begin
       
   208         if (gi^.Kind = gtHedgehog) then
       
   209             begin
       
   210             d := r - hwRound(Distance(gi^.X - x, gi^.Y - y));
       
   211             if (d > 1) and (not gi^.Invulnerable) and (GetRandom(2) = 0) then
       
   212                 begin
       
   213                 if (CurrentHedgehog^.Gear = gi) then
       
   214                     PlaySoundV(sndOops, gi^.Hedgehog^.Team^.voicepack)
       
   215 
       
   216                 else
       
   217                     begin
       
   218                     if ((gi^.State and gstMoving) = 0) and (gi^.Hedgehog^.Effects[heFrozen] = 0) then
       
   219                         begin
       
   220                         gi^.dX.isNegative:= X<gi^.X;
       
   221                         gi^.State := gi^.State or gstLoser;
       
   222                         end;
       
   223 
       
   224                     if d > r div 2 then
       
   225                         PlaySoundV(sndNooo, gi^.Hedgehog^.Team^.voicepack)
       
   226                     else
       
   227                         PlaySoundV(sndUhOh, gi^.Hedgehog^.Team^.voicepack);
       
   228                     end;
       
   229                 end;
       
   230             end;
       
   231 
       
   232         gi := gi^.NextGear
       
   233         end;
       
   234 end;
       
   235 
       
   236 procedure HideHog(HH: PHedgehog);
       
   237 begin
       
   238     ScriptCall('onHogHide', HH^.Gear^.Uid);
       
   239     DeleteCI(HH^.Gear);
       
   240     if FollowGear = HH^.Gear then
       
   241         FollowGear:= nil;
       
   242 
       
   243     if lastGearByUID = HH^.Gear then
       
   244         lastGearByUID := nil;
       
   245 
       
   246     HH^.Gear^.Message:= HH^.Gear^.Message or gmRemoveFromList;
       
   247     with HH^.Gear^ do
       
   248         begin
       
   249         Z := cHHZ;
       
   250         HH^.Gear^.Active:= false;
       
   251         State:= State and (not (gstHHDriven or gstAttacking or gstAttacked));
       
   252         Message := Message and (not gmAttack);
       
   253     end;
       
   254     HH^.GearHidden:= HH^.Gear;
       
   255     HH^.Gear:= nil
       
   256 end;
       
   257 
       
   258 
       
   259 ////////////////////////////////////////////////////////////////////////////////
       
   260 procedure doStepDrowningGear(Gear: PGear);
       
   261     begin
       
   262     AllInactive := false;
       
   263     Gear^.Y := Gear^.Y + cDrownSpeed;
       
   264     Gear^.X := Gear^.X + Gear^.dX * cDrownSpeed;
       
   265     // Create some bubbles (0.5% might be better but causes too few bubbles sometimes)
       
   266     if ((not SuddenDeathDmg and (WaterOpacity < $FF))
       
   267     or (SuddenDeathDmg and (SDWaterOpacity < $FF))) and ((GameTicks and $1F) = 0) then
       
   268         if (Gear^.Kind = gtHedgehog) and (Random(4) = 0) then
       
   269             AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble)
       
   270     else if Random(12) = 0 then
       
   271              AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble);
       
   272     if (not SuddenDeathDmg and (WaterOpacity > $FE))
       
   273     or (SuddenDeathDmg and (SDWaterOpacity > $FE))
       
   274     or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then
       
   275         DeleteGear(Gear);
       
   276     end;
       
   277 
       
   278 ////////////////////////////////////////////////////////////////////////////////
       
   279 procedure doStepFallingGear(Gear: PGear);
       
   280 var
       
   281     isFalling: boolean;
       
   282     //tmp: QWord;
       
   283     tdX, tdY: hwFloat;
       
   284     collV, collH: LongInt;
       
   285     land: word;
       
   286 begin
       
   287     // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems.
       
   288     if Gear^.dX.Round > 2 then
       
   289         Gear^.dX.QWordValue:= 8589934592;
       
   290     if Gear^.dY.Round > 2 then
       
   291         Gear^.dY.QWordValue:= 8589934592;
       
   292 
       
   293     if (Gear^.State and gstSubmersible <> 0) and (hwRound(Gear^.Y) > cWaterLine) then
       
   294         begin
       
   295         Gear^.dX:= Gear^.dX * _0_999;
       
   296         Gear^.dY:= Gear^.dY * _0_999
       
   297         end;
       
   298 
       
   299     Gear^.State := Gear^.State and (not gstCollision);
       
   300     collV := 0;
       
   301     collH := 0;
       
   302     tdX := Gear^.dX;
       
   303     tdY := Gear^.dY;
       
   304 
       
   305 
       
   306 
       
   307 // might need some testing/adjustments - just to avoid projectiles to fly forever (accelerated by wind/skips)
       
   308     if (hwRound(Gear^.X) < min(LAND_WIDTH div -2, -2048))
       
   309     or (hwRound(Gear^.X) > max(LAND_WIDTH * 3 div 2, 6144)) then
       
   310         Gear^.State := Gear^.State or gstCollision;
       
   311 
       
   312     if Gear^.dY.isNegative then
       
   313         begin
       
   314         isFalling := true;
       
   315         land:= TestCollisionYwithGear(Gear, -1);
       
   316         if land <> 0 then
       
   317             begin
       
   318             collV := -1;
       
   319             if land and lfIce <> 0 then
       
   320                 Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1)
       
   321             else
       
   322                 Gear^.dX := Gear^.dX * Gear^.Friction;
       
   323 
       
   324             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
   325             Gear^.State := Gear^.State or gstCollision
       
   326             end
       
   327         else if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, 1) <> 0) then
       
   328             collV := 1;
       
   329         end
       
   330     else
       
   331         begin // Gear^.dY.isNegative is false
       
   332         land:= TestCollisionYwithGear(Gear, 1);
       
   333         if land <> 0 then
       
   334             begin
       
   335             collV := 1;
       
   336             isFalling := false;
       
   337             if land and lfIce <> 0 then
       
   338                 Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1)
       
   339             else
       
   340                 Gear^.dX := Gear^.dX * Gear^.Friction;
       
   341 
       
   342             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
   343             Gear^.State := Gear^.State or gstCollision
       
   344             end
       
   345         else
       
   346             begin
       
   347             isFalling := true;
       
   348             if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, -1) <> 0) then
       
   349                 collV := -1
       
   350             end
       
   351         end;
       
   352 
       
   353 
       
   354     if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
       
   355         begin
       
   356         collH := hwSign(Gear^.dX);
       
   357         Gear^.dX := - Gear^.dX * Gear^.Elasticity;
       
   358         Gear^.dY :=   Gear^.dY * Gear^.Elasticity;
       
   359         Gear^.State := Gear^.State or gstCollision
       
   360         end
       
   361     else if (Gear^.AdvBounce=1) and TestCollisionXwithGear(Gear, -hwSign(Gear^.dX)) then
       
   362         collH := -hwSign(Gear^.dX);
       
   363     //if Gear^.AdvBounce and (collV <>0) and (collH <> 0) and (hwSqr(tdX) + hwSqr(tdY) > _0_08) then
       
   364     if (Gear^.AdvBounce=1) and (collV <>0) and (collH <> 0) and ((collV=-1)
       
   365     or ((tdX.QWordValue + tdY.QWordValue) > _0_2.QWordValue)) then
       
   366         begin
       
   367         Gear^.dX := tdY*Gear^.Elasticity*Gear^.Friction;
       
   368         Gear^.dY := tdX*Gear^.Elasticity;
       
   369         //*Gear^.Friction;
       
   370         Gear^.dY.isNegative := not tdY.isNegative;
       
   371         isFalling := false;
       
   372         Gear^.AdvBounce := 10;
       
   373         end;
       
   374 
       
   375     if Gear^.AdvBounce > 1 then
       
   376         dec(Gear^.AdvBounce);
       
   377 
       
   378     if isFalling then
       
   379         begin
       
   380         Gear^.dY := Gear^.dY + cGravity;
       
   381         if (GameFlags and gfMoreWind) <> 0 then
       
   382             Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density
       
   383             end;
       
   384 
       
   385     Gear^.X := Gear^.X + Gear^.dX;
       
   386     Gear^.Y := Gear^.Y + Gear^.dY;
       
   387     if Gear^.Kind <> gtBee then
       
   388         CheckGearDrowning(Gear);
       
   389     //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and
       
   390     if (not isFalling) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) then
       
   391         Gear^.State := Gear^.State and (not gstMoving)
       
   392     else
       
   393         Gear^.State := Gear^.State or gstMoving;
       
   394 
       
   395     if (Gear^.nImpactSounds > 0) and
       
   396         (Gear^.State and gstCollision <> 0) and
       
   397         (((Gear^.Kind <> gtMine) and (Gear^.Damage <> 0)) or (Gear^.State and gstMoving <> 0)) and
       
   398         (((Gear^.Radius < 3) and (Gear^.dY < -_0_1)) or
       
   399             ((Gear^.Radius >= 3) and
       
   400                 ((Gear^.dX.QWordValue > _0_1.QWordValue) or (Gear^.dY.QWordValue > _0_1.QWordValue)))) then
       
   401         PlaySound(TSound(ord(Gear^.ImpactSound) + LongInt(GetRandom(Gear^.nImpactSounds))), true);
       
   402 end;
       
   403 
       
   404 ////////////////////////////////////////////////////////////////////////////////
       
   405 procedure doStepBomb(Gear: PGear);
       
   406 var
       
   407     i, x, y: LongInt;
       
   408     dX, dY, gdX: hwFloat;
       
   409     vg: PVisualGear;
       
   410 begin
       
   411     AllInactive := false;
       
   412 
       
   413     doStepFallingGear(Gear);
       
   414 
       
   415     dec(Gear^.Timer);
       
   416     if Gear^.Timer = 1000 then // might need adjustments
       
   417         case Gear^.Kind of
       
   418             gtGrenade: makeHogsWorry(Gear^.X, Gear^.Y, 50);
       
   419             gtClusterBomb: makeHogsWorry(Gear^.X, Gear^.Y, 20);
       
   420             gtWatermelon: makeHogsWorry(Gear^.X, Gear^.Y, 75);
       
   421             gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, 90);
       
   422             gtGasBomb: makeHogsWorry(Gear^.X, Gear^.Y, 50);
       
   423         end;
       
   424 
       
   425     if (Gear^.Kind = gtBall) and ((Gear^.State and gstTmpFlag) <> 0) then
       
   426         begin
       
   427         CheckCollision(Gear);
       
   428         if (Gear^.State and gstCollision) <> 0 then
       
   429             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx);
       
   430         end;
       
   431 
       
   432     if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then
       
   433         begin
       
   434         vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite);
       
   435         if vg <> nil then
       
   436             vg^.Tint:= $FFC0C000;
       
   437         end;
       
   438 
       
   439     if Gear^.Timer = 0 then
       
   440         begin
       
   441         case Gear^.Kind of
       
   442             gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   443             gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound);
       
   444             gtClusterBomb:
       
   445                 begin
       
   446                 x := hwRound(Gear^.X);
       
   447                 y := hwRound(Gear^.Y);
       
   448                 gdX:= Gear^.dX;
       
   449                 doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound);
       
   450                 for i:= 0 to 4 do
       
   451                     begin
       
   452                     dX := rndSign(GetRandomf * _0_1) + gdX / 5;
       
   453                     dY := (GetRandomf - _3) * _0_08;
       
   454                     FollowGear := AddGear(x, y, gtCluster, 0, dX, dY, 25)
       
   455                     end
       
   456                 end;
       
   457             gtWatermelon:
       
   458                 begin
       
   459                 x := hwRound(Gear^.X);
       
   460                 y := hwRound(Gear^.Y);
       
   461                 gdX:= Gear^.dX;
       
   462                 doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound);
       
   463                 for i:= 0 to 5 do
       
   464                     begin
       
   465                     dX := rndSign(GetRandomf * _0_1) + gdX / 5;
       
   466                     dY := (GetRandomf - _1_5) * _0_3;
       
   467                     FollowGear:= AddGear(x, y, gtMelonPiece, 0, dX, dY, 75);
       
   468                     FollowGear^.DirAngle := i * 60
       
   469                     end
       
   470                 end;
       
   471             gtHellishBomb:
       
   472                 begin
       
   473                 x := hwRound(Gear^.X);
       
   474                 y := hwRound(Gear^.Y);
       
   475                 doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound);
       
   476 
       
   477                 for i:= 0 to 127 do
       
   478                     begin
       
   479                     dX := AngleCos(i * 16) * _0_5 * (GetRandomf + _1);
       
   480                     dY := AngleSin(i * 16) * _0_5 * (GetRandomf + _1);
       
   481                     if i mod 2 = 0 then
       
   482                         begin
       
   483                         AddGear(x, y, gtFlame, gstTmpFlag, dX, dY, 0);
       
   484                         AddGear(x, y, gtFlame, 0, dX, -dY, 0)
       
   485                         end
       
   486                     else
       
   487                         begin
       
   488                         AddGear(x, y, gtFlame, 0, dX, dY, 0);
       
   489                         AddGear(x, y, gtFlame, gstTmpFlag, dX, -dY, 0)
       
   490                         end;
       
   491                     end
       
   492                 end;
       
   493             gtGasBomb:
       
   494                 begin
       
   495                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);
       
   496                 for i:= 0 to 2 do
       
   497                     begin
       
   498                     x:= GetRandom(60);
       
   499                     y:= GetRandom(40);
       
   500                     FollowGear:= AddGear(hwRound(Gear^.X) - 30 + x, hwRound(Gear^.Y) - 20 + y, gtPoisonCloud, 0, _0, _0, 0);
       
   501                     end
       
   502                 end;
       
   503             end;
       
   504         DeleteGear(Gear);
       
   505         exit
       
   506         end;
       
   507 
       
   508     CalcRotationDirAngle(Gear);
       
   509 
       
   510     if Gear^.Kind = gtHellishBomb then
       
   511         begin
       
   512 
       
   513         if Gear^.Timer = 3000 then
       
   514             begin
       
   515             Gear^.nImpactSounds := 0;
       
   516             PlaySound(sndHellish);
       
   517             end;
       
   518 
       
   519         if (GameTicks and $3F) = 0 then
       
   520             if (Gear^.State and gstCollision) = 0 then
       
   521                 AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace);
       
   522         end;
       
   523 end;
       
   524 
       
   525 ////////////////////////////////////////////////////////////////////////////////
       
   526 procedure doStepMolotov(Gear: PGear);
       
   527 var
       
   528     s: Longword;
       
   529     i, gX, gY: LongInt;
       
   530     dX, dY: hwFloat;
       
   531     smoke, glass: PVisualGear;
       
   532 begin
       
   533     AllInactive := false;
       
   534 
       
   535     doStepFallingGear(Gear);
       
   536     CalcRotationDirAngle(Gear);
       
   537 
       
   538     // let's add some smoke depending on speed
       
   539     s:= max(32,152 - round((abs(hwFloat2FLoat(Gear^.dX))+abs(hwFloat2Float(Gear^.dY)))*120))+random(10);
       
   540     if (GameTicks mod s) = 0 then
       
   541         begin
       
   542         // adjust angle to match the texture
       
   543         if Gear^.dX.isNegative then
       
   544              i:= 130
       
   545         else i:= 50;
       
   546 
       
   547         smoke:= AddVisualGear(hwRound(Gear^.X)-round(cos((Gear^.DirAngle+i) * pi / 180)*20), hwRound(Gear^.Y)-round(sin((Gear^.DirAngle+i) * pi / 180)*20), vgtSmoke);
       
   548         if smoke <> nil then
       
   549             smoke^.Scale:= 0.75;
       
   550         end;
       
   551 
       
   552     if (Gear^.State and gstCollision) <> 0 then
       
   553         begin
       
   554         PlaySound(sndMolotov);
       
   555         gX := hwRound(Gear^.X);
       
   556         gY := hwRound(Gear^.Y);
       
   557         for i:= 0 to 4 do
       
   558             begin
       
   559             (*glass:= AddVisualGear(gx+random(7)-3, gy+random(5)-2, vgtEgg);
       
   560             if glass <> nil then
       
   561                 begin
       
   562                 glass^.Frame:= 2;
       
   563                 glass^.Tint:= $41B83ED0 - i * $10081000;
       
   564                 glass^.dX:= 1/(10*(random(11)-5));
       
   565                 glass^.dY:= -1/(random(4)+5);
       
   566                 end;*)
       
   567             glass:= AddVisualGear(gx+random(7)-3, gy+random(7)-3, vgtStraightShot);
       
   568             if glass <> nil then
       
   569                 with glass^ do
       
   570                     begin
       
   571                     Frame:= 2;
       
   572                     Tint:= $41B83ED0 - i * $10081000;
       
   573                     Angle:= random(360);
       
   574                     dx:= 0.0000001;
       
   575                     dy:= 0;
       
   576                     if random(2) = 0 then
       
   577                         dx := -dx;
       
   578                     FrameTicks:= 750;
       
   579                     State:= ord(sprEgg)
       
   580                     end;
       
   581             end;
       
   582         for i:= 0 to 24 do
       
   583             begin
       
   584             dX := AngleCos(i * 2) * ((_0_15*(i div 5))) * (GetRandomf + _1);
       
   585             dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1);
       
   586             AddGear(gX, gY, gtFlame, gstTmpFlag, dX, dY, 0);
       
   587             AddGear(gX, gY, gtFlame, gstTmpFlag, dX,-dY, 0);
       
   588             AddGear(gX, gY, gtFlame, gstTmpFlag,-dX, dY, 0);
       
   589             AddGear(gX, gY, gtFlame, gstTmpFlag,-dX,-dY, 0);
       
   590             end;
       
   591         DeleteGear(Gear);
       
   592         exit
       
   593         end;
       
   594 end;
       
   595 
       
   596 ////////////////////////////////////////////////////////////////////////////////
       
   597 
       
   598 procedure doStepCluster(Gear: PGear);
       
   599 begin
       
   600     AllInactive := false;
       
   601     doStepFallingGear(Gear);
       
   602     if (Gear^.State and gstCollision) <> 0 then
       
   603         begin
       
   604         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound);
       
   605         DeleteGear(Gear);
       
   606         exit
       
   607     end;
       
   608 
       
   609     if (Gear^.Kind = gtMelonPiece)
       
   610     or (Gear^.Kind = gtBall) then
       
   611         CalcRotationDirAngle(Gear)
       
   612     else if (GameTicks and $1F) = 0 then
       
   613         begin
       
   614         if hwRound(Gear^.Y) > cWaterLine then
       
   615              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
   616         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
   617         end
       
   618 end;
       
   619 
       
   620 ////////////////////////////////////////////////////////////////////////////////
       
   621 procedure doStepShell(Gear: PGear);
       
   622 begin
       
   623     AllInactive := false;
       
   624     if (GameFlags and gfMoreWind) = 0 then
       
   625         Gear^.dX := Gear^.dX + cWindSpeed;
       
   626     doStepFallingGear(Gear);
       
   627     if (Gear^.State and gstCollision) <> 0 then
       
   628         begin
       
   629         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   630         DeleteGear(Gear);
       
   631         exit
       
   632         end;
       
   633     if (GameTicks and $3F) = 0 then
       
   634         begin
       
   635         if hwRound(Gear^.Y) > cWaterLine then
       
   636              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
   637         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
   638         end
       
   639 end;
       
   640 
       
   641 ////////////////////////////////////////////////////////////////////////////////
       
   642 procedure doStepSnowball(Gear: PGear);
       
   643 var kick, i: LongInt;
       
   644     particle: PVisualGear;
       
   645     gdX, gdY: hwFloat;
       
   646 begin
       
   647     AllInactive := false;
       
   648     if (GameFlags and gfMoreWind) = 0 then
       
   649         Gear^.dX := Gear^.dX + cWindSpeed;
       
   650     gdX := Gear^.dX;
       
   651     gdY := Gear^.dY;
       
   652     doStepFallingGear(Gear);
       
   653     CalcRotationDirAngle(Gear);
       
   654     if (Gear^.State and gstCollision) <> 0 then
       
   655         begin
       
   656         kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * _20);
       
   657         Gear^.dX:= gdX;
       
   658         Gear^.dY:= gdY;
       
   659         AmmoShove(Gear, 0, kick);
       
   660         for i:= 15 + kick div 10 downto 0 do
       
   661             begin
       
   662             particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust);
       
   663             if particle <> nil then
       
   664                 particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
       
   665             end;
       
   666         DeleteGear(Gear);
       
   667         exit
       
   668         end;
       
   669     if ((GameTicks and $1F) = 0) and (Random(3) = 0) then
       
   670         begin
       
   671         particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust);
       
   672         if particle <> nil then
       
   673             particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
       
   674         end
       
   675 end;
       
   676 
       
   677 ////////////////////////////////////////////////////////////////////////////////
       
   678 procedure doStepSnowflake(Gear: PGear);
       
   679 var xx, yy, px, py, rx, ry, lx, ly: LongInt;
       
   680     move, draw, allpx, gun: Boolean;
       
   681     s: PSDL_Surface;
       
   682     p: PLongwordArray;
       
   683     lf: LongWord;
       
   684 begin
       
   685 inc(Gear^.Pos);
       
   686 gun:= (Gear^.State and gstTmpFlag) <> 0;
       
   687 move:= false;
       
   688 draw:= false;
       
   689 if gun then
       
   690     begin
       
   691     Gear^.State:= Gear^.State and (not gstInvisible);
       
   692     doStepFallingGear(Gear);
       
   693     CheckCollision(Gear);
       
   694     if ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then
       
   695         draw:= true;
       
   696     xx:= hwRound(Gear^.X);
       
   697     yy:= hwRound(Gear^.Y);
       
   698     end
       
   699 else if GameTicks and $7 = 0 then
       
   700     begin
       
   701     with Gear^ do
       
   702         begin
       
   703         State:= State and (not gstInvisible);
       
   704         X:= X + cWindSpeed * 3200 + dX;
       
   705         Y:= Y + dY + cGravity * vobFallSpeed * 8;  // using same value as flakes to try and get similar results
       
   706         xx:= hwRound(X);
       
   707         yy:= hwRound(Y);
       
   708         if vobVelocity <> 0 then
       
   709             begin
       
   710             DirAngle := DirAngle + (Damage / 1000);
       
   711             if DirAngle < 0 then
       
   712                 DirAngle := DirAngle + 360
       
   713             else if 360 < DirAngle then
       
   714                 DirAngle := DirAngle - 360;
       
   715             end;
       
   716 (*
       
   717 We aren't using frametick right now, so just a waste of cycles.
       
   718         inc(Health, 8);
       
   719         if longword(Health) > vobFrameTicks then
       
   720             begin
       
   721             dec(Health, vobFrameTicks);
       
   722             inc(Timer);
       
   723             if Timer = vobFramesCount then
       
   724                 Timer:= 0
       
   725             end;
       
   726 *)
       
   727     // move back to cloud layer
       
   728         if yy > cWaterLine then
       
   729             move:= true
       
   730         else if (xx > snowRight) or (xx < snowLeft) then
       
   731             move:=true
       
   732         // Solid pixel encountered
       
   733         else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
       
   734             begin
       
   735             lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible);
       
   736             if lf = 0 then lf:= lfObject;
       
   737             // If there's room below keep falling
       
   738             if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then
       
   739                 begin
       
   740                 X:= X - cWindSpeed * 1600 - dX;
       
   741                 end
       
   742             // If there's room below, on the sides, fill the gaps
       
   743             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(1*hwSign(cWindSpeed)))] = 0) then
       
   744                 begin
       
   745                 X:= X - _0_8 * hwSign(cWindSpeed);
       
   746                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   747                 end
       
   748             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(2*hwSign(cWindSpeed)))] = 0) then
       
   749                 begin
       
   750                 X:= X - _0_8 * 2 * hwSign(cWindSpeed);
       
   751                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   752                 end
       
   753             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(1*hwSign(cWindSpeed)))] = 0) then
       
   754                 begin
       
   755                 X:= X + _0_8 * hwSign(cWindSpeed);
       
   756                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   757                 end
       
   758             else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(2*hwSign(cWindSpeed)))] = 0) then
       
   759                 begin
       
   760                 X:= X + _0_8 * 2 * hwSign(cWindSpeed);
       
   761                 Y:= Y - dY - cGravity * vobFallSpeed * 8;
       
   762                 end
       
   763             // if there's an hog/object below do nothing
       
   764             else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0))
       
   765                 then move:=true
       
   766             else draw:= true
       
   767             end
       
   768         end
       
   769     end;
       
   770 if draw then
       
   771     with Gear^ do
       
   772         begin
       
   773         // we've collided with land. draw some stuff and get back into the clouds
       
   774         move:= true;
       
   775         if (Pos > 20) and ((CurAmmoGear = nil)
       
   776         or (CurAmmoGear^.Kind <> gtRope)) then
       
   777             begin
       
   778 ////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS ////////////////////////////////////
       
   779             if not gun then
       
   780                 begin
       
   781                 dec(yy,3);
       
   782                 dec(xx,1)
       
   783                 end;
       
   784             s:= SpritesData[sprSnow].Surface;
       
   785             p:= s^.pixels;
       
   786             allpx:= true;
       
   787             for py:= 0 to Pred(s^.h) do
       
   788                 begin
       
   789                 for px:= 0 to Pred(s^.w) do
       
   790                     begin
       
   791                     lx:=xx + px; ly:=yy + py;
       
   792                     if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then
       
   793                         begin
       
   794                         rx:= lx;
       
   795                         ry:= ly;
       
   796                         if cReducedQuality and rqBlurryLand <> 0 then
       
   797                             begin
       
   798                             rx:= rx div 2;ry:= ry div 2;
       
   799                             end;
       
   800                         if Land[yy + py, xx + px] <= lfAllObjMask then
       
   801                             if gun then
       
   802                                 begin
       
   803                                 LandDirty[yy div 32, xx div 32]:= 1;
       
   804                                 if LandPixels[ry, rx] = 0 then
       
   805                                     Land[ly, lx]:=  lfDamaged or lfObject
       
   806                                 else Land[ly, lx]:=  lfDamaged or lfBasic
       
   807                                 end
       
   808                             else Land[ly, lx]:= lf;
       
   809                         if gun then
       
   810                             LandPixels[ry, rx]:= (ExplosionBorderColor and (not AMask)) or (p^[px] and AMask)
       
   811                         else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]);
       
   812                         end
       
   813                     else allpx:= false
       
   814                     end;
       
   815                 p:= @(p^[s^.pitch shr 2])
       
   816                 end;
       
   817 
       
   818             // Why is this here.  For one thing, there's no test on +1 being safe.
       
   819             //Land[py, px+1]:= lfBasic;
       
   820 
       
   821             if allpx then
       
   822                 UpdateLandTexture(xx, Pred(s^.h), yy, Pred(s^.w), true)
       
   823             else
       
   824                 begin
       
   825                 UpdateLandTexture(
       
   826                     max(0, min(LAND_WIDTH, xx)),
       
   827                     min(LAND_WIDTH - xx, Pred(s^.w)),
       
   828                     max(0, min(LAND_WIDTH, yy)),
       
   829                     min(LAND_HEIGHT - yy, Pred(s^.h)), false // could this be true without unnecessarily creating blanks?
       
   830                 );
       
   831                 end;
       
   832 ////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS ////////////////////////////////////
       
   833             end
       
   834         end;
       
   835 
       
   836 if move then
       
   837     begin
       
   838     if gun then
       
   839         begin
       
   840         DeleteGear(Gear);
       
   841         exit
       
   842         end;
       
   843     Gear^.Pos:= 0;
       
   844     Gear^.X:= int2hwFloat(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft);
       
   845     Gear^.Y:= int2hwFloat(LAND_HEIGHT + LongInt(GetRandom(50)) - 1325);
       
   846     Gear^.State:= Gear^.State or gstInvisible;
       
   847     end
       
   848 end;
       
   849 
       
   850 ////////////////////////////////////////////////////////////////////////////////
       
   851 procedure doStepGrave(Gear: PGear);
       
   852 begin
       
   853     if (Gear^.Message and gmDestroy) <> 0 then
       
   854         begin
       
   855         DeleteGear(Gear);
       
   856         exit
       
   857         end;
       
   858 
       
   859     AllInactive := false;
       
   860 
       
   861     if Gear^.dY.isNegative then
       
   862         if TestCollisionY(Gear, -1) then
       
   863             Gear^.dY := _0;
       
   864 
       
   865     if not Gear^.dY.isNegative then
       
   866         if TestCollisionY(Gear, 1) then
       
   867         begin
       
   868             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
   869             if Gear^.dY > - _1div1024 then
       
   870             begin
       
   871                 Gear^.Active := false;
       
   872                 exit
       
   873             end
       
   874             else if Gear^.dY < - _0_03 then
       
   875                 PlaySound(Gear^.ImpactSound)
       
   876         end;
       
   877 
       
   878     Gear^.Y := Gear^.Y + Gear^.dY;
       
   879     CheckGearDrowning(Gear);
       
   880     Gear^.dY := Gear^.dY + cGravity
       
   881 end;
       
   882 
       
   883 ////////////////////////////////////////////////////////////////////////////////
       
   884 procedure doStepBeeWork(Gear: PGear);
       
   885 var
       
   886     t: hwFloat;
       
   887     gX,gY,i: LongInt;
       
   888     uw, nuw: boolean;
       
   889     flower: PVisualGear;
       
   890 
       
   891 begin
       
   892     AllInactive := false;
       
   893     gX := hwRound(Gear^.X);
       
   894     gY := hwRound(Gear^.Y);
       
   895     uw := (Gear^.Tag <> 0); // was bee underwater last tick?
       
   896     nuw := (cWaterLine < gy + Gear^.Radius); // is bee underwater now?
       
   897 
       
   898     // if water entered or left
       
   899     if nuw <> uw then
       
   900         begin
       
   901         AddVisualGear(gX, cWaterLine, vgtSplash);
       
   902         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   903         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   904         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   905         AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet);
       
   906         StopSoundChan(Gear^.SoundChannel);
       
   907         if nuw then
       
   908             begin
       
   909             Gear^.SoundChannel := LoopSound(sndBeeWater);
       
   910             Gear^.Tag := 1;
       
   911         end
       
   912         else
       
   913             begin
       
   914             Gear^.SoundChannel := LoopSound(sndBee);
       
   915             Gear^.Tag := 0;
       
   916             end;
       
   917         end;
       
   918 
       
   919 
       
   920     if Gear^.Timer = 0 then
       
   921         Gear^.RenderTimer:= false
       
   922     else
       
   923         begin
       
   924         if (GameTicks and $F) = 0 then
       
   925             begin
       
   926             if (GameTicks and $30) = 0 then
       
   927                 AddVisualGear(gX, gY, vgtBeeTrace);
       
   928             Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX));
       
   929             Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY));
       
   930             // make sure new speed isn't higher than original one (which we stored in Friction variable)
       
   931             t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY);
       
   932             Gear^.dX := Gear^.dX * t;
       
   933             Gear^.dY := Gear^.dY * t;
       
   934             end;
       
   935 
       
   936         Gear^.X := Gear^.X + Gear^.dX;
       
   937         Gear^.Y := Gear^.Y + Gear^.dY;
       
   938 
       
   939         end;
       
   940 
       
   941 
       
   942     CheckCollision(Gear);
       
   943     if ((Gear^.State and gstCollision) <> 0) then
       
   944         begin
       
   945         StopSoundChan(Gear^.SoundChannel);
       
   946         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   947         for i:= 0 to 31 do
       
   948             begin
       
   949             flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
       
   950             if flower <> nil then
       
   951                 with flower^ do
       
   952                     begin
       
   953                     Scale:= 0.75;
       
   954                     dx:= 0.001 * (random(200));
       
   955                     dy:= 0.001 * (random(200));
       
   956                     if random(2) = 0 then
       
   957                         dx := -dx;
       
   958                     if random(2) = 0 then
       
   959                         dy := -dy;
       
   960                     FrameTicks:= random(250) + 250;
       
   961                     State:= ord(sprTargetBee);
       
   962                     end;
       
   963             end;
       
   964         DeleteGear(Gear);
       
   965     end;
       
   966 
       
   967     if (Gear^.Timer > 0) then
       
   968         dec(Gear^.Timer)
       
   969     else
       
   970         begin
       
   971         if nuw then
       
   972            begin
       
   973             StopSoundChan(Gear^.SoundChannel);
       
   974             CheckGearDrowning(Gear);
       
   975             end
       
   976         else
       
   977             doStepFallingGear(Gear);
       
   978         end;
       
   979 end;
       
   980 
       
   981 procedure doStepBee(Gear: PGear);
       
   982 begin
       
   983     AllInactive := false;
       
   984     Gear^.X := Gear^.X + Gear^.dX;
       
   985     Gear^.Y := Gear^.Y + Gear^.dY;
       
   986     Gear^.dY := Gear^.dY + cGravity;
       
   987     CheckCollision(Gear);
       
   988     if (Gear^.State and gstCollision) <> 0 then
       
   989         begin
       
   990         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
   991         DeleteGear(Gear);
       
   992         exit
       
   993     end;
       
   994     dec(Gear^.Timer);
       
   995     if Gear^.Timer = 0 then
       
   996         begin
       
   997         Gear^.Hedgehog^.Gear^.Message:= Gear^.Hedgehog^.Gear^.Message and (not gmAttack);
       
   998         Gear^.Hedgehog^.Gear^.State:= Gear^.Hedgehog^.Gear^.State and (not gstAttacking);
       
   999         AttackBar:= 0;
       
  1000 
       
  1001         Gear^.SoundChannel := LoopSound(sndBee);
       
  1002         Gear^.Timer := 5000;
       
  1003         // save initial speed in otherwise unused Friction variable
       
  1004         Gear^.Friction := Distance(Gear^.dX, Gear^.dY);
       
  1005         Gear^.doStep := @doStepBeeWork
       
  1006         end;
       
  1007 end;
       
  1008 
       
  1009 ////////////////////////////////////////////////////////////////////////////////
       
  1010 procedure doStepShotIdle(Gear: PGear);
       
  1011 begin
       
  1012     AllInactive := false;
       
  1013     inc(Gear^.Timer);
       
  1014     if Gear^.Timer > 75 then
       
  1015         begin
       
  1016         DeleteGear(Gear);
       
  1017         AfterAttack
       
  1018         end
       
  1019 end;
       
  1020 
       
  1021 procedure doStepShotgunShot(Gear: PGear);
       
  1022 var
       
  1023     i: LongWord;
       
  1024     shell: PVisualGear;
       
  1025 begin
       
  1026     AllInactive := false;
       
  1027 
       
  1028     if ((Gear^.State and gstAnimation) = 0) then
       
  1029         begin
       
  1030         dec(Gear^.Timer);
       
  1031         if Gear^.Timer = 0 then
       
  1032             begin
       
  1033             PlaySound(sndShotgunFire);
       
  1034             shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell);
       
  1035             if shell <> nil then
       
  1036                 begin
       
  1037                 shell^.dX := gear^.dX.QWordValue / -17179869184;
       
  1038                 shell^.dY := gear^.dY.QWordValue / -17179869184;
       
  1039                 shell^.Frame := 0
       
  1040                 end;
       
  1041             Gear^.State := Gear^.State or gstAnimation
       
  1042             end;
       
  1043             exit
       
  1044         end else
       
  1045         if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then
       
  1046             begin
       
  1047             DeleteGear(Gear);
       
  1048             AfterAttack;
       
  1049             exit
       
  1050             end
       
  1051     else
       
  1052         inc(Gear^.Timer);
       
  1053 
       
  1054         i := 200;
       
  1055     repeat
       
  1056         Gear^.X := Gear^.X + Gear^.dX;
       
  1057         Gear^.Y := Gear^.Y + Gear^.dY;
       
  1058         CheckCollision(Gear);
       
  1059         if (Gear^.State and gstCollision) <> 0 then
       
  1060             begin
       
  1061             Gear^.X := Gear^.X + Gear^.dX * 8;
       
  1062             Gear^.Y := Gear^.Y + Gear^.dY * 8;
       
  1063             ShotgunShot(Gear);
       
  1064             Gear^.doStep := @doStepShotIdle;
       
  1065             exit
       
  1066             end;
       
  1067 
       
  1068         CheckGearDrowning(Gear);
       
  1069         if (Gear^.State and gstDrowning) <> 0 then
       
  1070             begin
       
  1071             Gear^.doStep := @doStepShotIdle;
       
  1072             exit
       
  1073             end;
       
  1074         dec(i)
       
  1075     until i = 0;
       
  1076     if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
       
  1077         Gear^.doStep := @doStepShotIdle
       
  1078 end;
       
  1079 
       
  1080 ////////////////////////////////////////////////////////////////////////////////
       
  1081 procedure spawnBulletTrail(Bullet: PGear);
       
  1082 var oX, oY: hwFloat;
       
  1083     VGear: PVisualGear;
       
  1084 begin
       
  1085     if Bullet^.PortalCounter = 0 then
       
  1086         begin
       
  1087         ox:= CurrentHedgehog^.Gear^.X + Int2hwFloat(GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle));
       
  1088         oy:= CurrentHedgehog^.Gear^.Y + Int2hwFloat(GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle));
       
  1089         end
       
  1090     else
       
  1091         begin
       
  1092         ox:= Bullet^.Elasticity;
       
  1093         oy:= Bullet^.Friction;
       
  1094         end;
       
  1095 
       
  1096         // Bullet trail
       
  1097         VGear := AddVisualGear(hwRound(ox), hwRound(oy), vgtLineTrail);
       
  1098 
       
  1099         if VGear <> nil then
       
  1100             begin
       
  1101             VGear^.X:= hwFloat2Float(ox);
       
  1102             VGear^.Y:= hwFloat2Float(oy);
       
  1103             VGear^.dX:= hwFloat2Float(Bullet^.X);
       
  1104             VGear^.dY:= hwFloat2Float(Bullet^.Y);
       
  1105 
       
  1106             // reached edge of land. assume infinite beam. Extend it way out past camera
       
  1107             if (hwRound(Bullet^.X) and LAND_WIDTH_MASK <> 0)
       
  1108             or (hwRound(Bullet^.Y) and LAND_HEIGHT_MASK <> 0) then
       
  1109                     // only extend if not under water
       
  1110                     if hwRound(Bullet^.Y) < cWaterLine then
       
  1111                         begin
       
  1112                         VGear^.dX := VGear^.dX + max(LAND_WIDTH,4096) * (VGear^.dX - VGear^.X);
       
  1113                         VGear^.dY := VGear^.dY + max(LAND_WIDTH,4096) * (VGear^.dY - VGear^.Y);
       
  1114                         end;
       
  1115 
       
  1116             VGear^.Timer := 200;
       
  1117             end;
       
  1118 end;
       
  1119 
       
  1120 procedure doStepBulletWork(Gear: PGear);
       
  1121 var
       
  1122     i, x, y: LongWord;
       
  1123     oX, oY: hwFloat;
       
  1124     VGear: PVisualGear;
       
  1125 begin
       
  1126     AllInactive := false;
       
  1127     inc(Gear^.Timer);
       
  1128     i := 80;
       
  1129     oX := Gear^.X;
       
  1130     oY := Gear^.Y;
       
  1131     repeat
       
  1132         Gear^.X := Gear^.X + Gear^.dX;
       
  1133         Gear^.Y := Gear^.Y + Gear^.dY;
       
  1134         x := hwRound(Gear^.X);
       
  1135         y := hwRound(Gear^.Y);
       
  1136 
       
  1137         if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then
       
  1138             inc(Gear^.Damage);
       
  1139         // let's interrupt before a collision to give portals a chance to catch the bullet
       
  1140         if (Gear^.Damage = 1) and (Gear^.Tag = 0) and not(CheckLandValue(x, y, lfLandMask)) then
       
  1141             begin
       
  1142             Gear^.Tag := 1;
       
  1143             Gear^.Damage := 0;
       
  1144             Gear^.X := Gear^.X - Gear^.dX;
       
  1145             Gear^.Y := Gear^.Y - Gear^.dY;
       
  1146             CheckGearDrowning(Gear);
       
  1147             break;
       
  1148             end
       
  1149         else
       
  1150             Gear^.Tag := 0;
       
  1151 
       
  1152         if Gear^.Damage > 5 then
       
  1153             if Gear^.AmmoType = amDEagle then
       
  1154                 AmmoShove(Gear, 7, 20)
       
  1155         else
       
  1156             AmmoShove(Gear, Gear^.Timer, 20);
       
  1157         CheckGearDrowning(Gear);
       
  1158         dec(i)
       
  1159     until (i = 0) or (Gear^.Damage > Gear^.Health) or ((Gear^.State and gstDrowning) <> 0);
       
  1160 
       
  1161     if Gear^.Damage > 0 then
       
  1162         begin
       
  1163         DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 82 - i, 1);
       
  1164         dec(Gear^.Health, Gear^.Damage);
       
  1165         Gear^.Damage := 0
       
  1166         end;
       
  1167     if ((Gear^.State and gstDrowning) <> 0) and (Gear^.Damage < Gear^.Health) and ((not SuddenDeathDmg and (WaterOpacity < $FF)) or (SuddenDeathDmg and (SDWaterOpacity < $FF))) then
       
  1168         begin
       
  1169         for i:=(Gear^.Health - Gear^.Damage) * 4 downto 0 do
       
  1170             begin
       
  1171             if Random(6) = 0 then
       
  1172                 AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble);
       
  1173             Gear^.X := Gear^.X + Gear^.dX;
       
  1174             Gear^.Y := Gear^.Y + Gear^.dY;
       
  1175             end;
       
  1176         end;
       
  1177 
       
  1178     if (Gear^.Health <= 0)
       
  1179         or (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0)
       
  1180         or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
       
  1181             begin
       
  1182             if (Gear^.Kind = gtSniperRifleShot) and ((GameFlags and gfLaserSight) = 0) then
       
  1183                 cLaserSighting := false;
       
  1184             if (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and ((GameFlags and gfArtillery) = 0) then
       
  1185                 cArtillery := false;
       
  1186 
       
  1187         // Bullet Hit
       
  1188             if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then
       
  1189                 begin
       
  1190                 VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit);
       
  1191                 if VGear <> nil then
       
  1192                     begin
       
  1193                     VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY);
       
  1194                     end;
       
  1195                 end;
       
  1196 
       
  1197             spawnBulletTrail(Gear);
       
  1198             Gear^.doStep := @doStepShotIdle
       
  1199             end;
       
  1200 end;
       
  1201 
       
  1202 procedure doStepDEagleShot(Gear: PGear);
       
  1203 begin
       
  1204     PlaySound(sndGun);
       
  1205     // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just plain old weird angles
       
  1206     Gear^.X := Gear^.X + Gear^.dX * 3;
       
  1207     Gear^.Y := Gear^.Y + Gear^.dY * 3;
       
  1208     Gear^.doStep := @doStepBulletWork
       
  1209 end;
       
  1210 
       
  1211 procedure doStepSniperRifleShot(Gear: PGear);
       
  1212 var
       
  1213     HHGear: PGear;
       
  1214     shell: PVisualGear;
       
  1215 begin
       
  1216     cArtillery := true;
       
  1217     HHGear := Gear^.Hedgehog^.Gear;
       
  1218     HHGear^.State := HHGear^.State or gstNotKickable;
       
  1219     HedgehogChAngle(HHGear);
       
  1220     if not cLaserSighting then
       
  1221         // game does not have default laser sight. turn it on and give them a chance to aim
       
  1222         begin
       
  1223         cLaserSighting := true;
       
  1224         HHGear^.Message := 0;
       
  1225         if (HHGear^.Angle >= 32) then
       
  1226             dec(HHGear^.Angle,32)
       
  1227         end;
       
  1228 
       
  1229     if (HHGear^.Message and gmAttack) <> 0 then
       
  1230         begin
       
  1231         shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell);
       
  1232         if shell <> nil then
       
  1233             begin
       
  1234             shell^.dX := gear^.dX.QWordValue / -8589934592;
       
  1235             shell^.dY := gear^.dY.QWordValue / -8589934592;
       
  1236             shell^.Frame := 1
       
  1237             end;
       
  1238         Gear^.State := Gear^.State or gstAnimation;
       
  1239         Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _0_5;
       
  1240         Gear^.dY := -AngleCos(HHGear^.Angle) * _0_5;
       
  1241         PlaySound(sndGun);
       
  1242         // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just weird angles
       
  1243         Gear^.X := Gear^.X + Gear^.dX * 3;
       
  1244         Gear^.Y := Gear^.Y + Gear^.dY * 3;
       
  1245         Gear^.doStep := @doStepBulletWork;
       
  1246         end
       
  1247     else
       
  1248         if (GameTicks mod 32) = 0 then
       
  1249             if (GameTicks mod 4096) < 2048 then
       
  1250                 begin
       
  1251                 if (HHGear^.Angle + 1 <= cMaxAngle) then
       
  1252                     inc(HHGear^.Angle)
       
  1253                 end
       
  1254     else
       
  1255         if (HHGear^.Angle >= 1) then
       
  1256             dec(HHGear^.Angle);
       
  1257 
       
  1258     if (TurnTimeLeft > 0) then
       
  1259         dec(TurnTimeLeft)
       
  1260     else
       
  1261         begin
       
  1262         DeleteGear(Gear);
       
  1263         AfterAttack
       
  1264         end;
       
  1265 end;
       
  1266 
       
  1267 ////////////////////////////////////////////////////////////////////////////////
       
  1268 procedure doStepActionTimer(Gear: PGear);
       
  1269 begin
       
  1270 dec(Gear^.Timer);
       
  1271 case Gear^.Kind of
       
  1272     gtATStartGame:
       
  1273         begin
       
  1274         AllInactive := false;
       
  1275         if Gear^.Timer = 0 then
       
  1276             begin
       
  1277             AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState);
       
  1278             end
       
  1279         end;
       
  1280     gtATFinishGame:
       
  1281         begin
       
  1282         AllInactive := false;
       
  1283         if Gear^.Timer = 1000 then
       
  1284             begin
       
  1285             ScreenFade := sfToBlack;
       
  1286             ScreenFadeValue := 0;
       
  1287             ScreenFadeSpeed := 1;
       
  1288             end;
       
  1289         if Gear^.Timer = 0 then
       
  1290             begin
       
  1291             SendIPC(_S'N');
       
  1292             SendIPC(_S'q');
       
  1293             GameState := gsExit
       
  1294             end
       
  1295         end;
       
  1296     end;
       
  1297 if Gear^.Timer = 0 then
       
  1298     DeleteGear(Gear)
       
  1299 end;
       
  1300 
       
  1301 ////////////////////////////////////////////////////////////////////////////////
       
  1302 procedure doStepPickHammerWork(Gear: PGear);
       
  1303 var
       
  1304     i, ei, x, y: LongInt;
       
  1305     HHGear: PGear;
       
  1306 begin
       
  1307     AllInactive := false;
       
  1308     HHGear := Gear^.Hedgehog^.Gear;
       
  1309     dec(Gear^.Timer);
       
  1310     if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then
       
  1311         dec(TurnTimeLeft);
       
  1312     if (TurnTimeLeft = 0) or (Gear^.Timer = 0)
       
  1313     or((Gear^.Message and gmDestroy) <> 0)
       
  1314     or((HHGear^.State and gstHHDriven) =0) then
       
  1315         begin
       
  1316         StopSoundChan(Gear^.SoundChannel);
       
  1317         DeleteGear(Gear);
       
  1318         AfterAttack;
       
  1319         doStepHedgehogMoving(HHGear);  // for gfInfAttack
       
  1320         exit
       
  1321         end;
       
  1322 
       
  1323     x:= hwRound(Gear^.X);
       
  1324     y:= hwRound(Gear^.Y);
       
  1325     if (Gear^.Timer mod 33) = 0 then
       
  1326         begin
       
  1327         HHGear^.State := HHGear^.State or gstNoDamage;
       
  1328         doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw);
       
  1329         HHGear^.State := HHGear^.State and (not gstNoDamage)
       
  1330         end;
       
  1331 
       
  1332     if (Gear^.Timer mod 47) = 0 then
       
  1333         begin
       
  1334         // ok. this was an attempt to turn off dust if not actually drilling land.  I have no idea why it isn't working as expected
       
  1335         if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then
       
  1336             for i:= 0 to 1 do
       
  1337                 AddVisualGear(x - 5 + Random(10), y + 12, vgtDust);
       
  1338 
       
  1339         i := x - Gear^.Radius - LongInt(GetRandom(2));
       
  1340         ei := x + Gear^.Radius + LongInt(GetRandom(2));
       
  1341         while i <= ei do
       
  1342             begin
       
  1343             DrawExplosion(i, y + 3, 3);
       
  1344             inc(i, 1)
       
  1345             end;
       
  1346 
       
  1347         if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9), lfIndestructible) then
       
  1348             begin
       
  1349             Gear^.X := Gear^.X + Gear^.dX;
       
  1350             Gear^.Y := Gear^.Y + _1_9;
       
  1351             end;
       
  1352         SetAllHHToActive;
       
  1353         end;
       
  1354     if TestCollisionYwithGear(Gear, 1) <> 0 then
       
  1355         begin
       
  1356         Gear^.dY := _0;
       
  1357         SetLittle(HHGear^.dX);
       
  1358         HHGear^.dY := _0;
       
  1359         end
       
  1360     else
       
  1361         begin
       
  1362         if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), lfLandMask) then
       
  1363             begin
       
  1364             Gear^.dY := Gear^.dY + cGravity;
       
  1365             Gear^.Y := Gear^.Y + Gear^.dY
       
  1366             end;
       
  1367         if hwRound(Gear^.Y) > cWaterLine then
       
  1368             Gear^.Timer := 1
       
  1369         end;
       
  1370 
       
  1371     Gear^.X := Gear^.X + HHGear^.dX;
       
  1372     if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, lfLandMask) then
       
  1373         begin
       
  1374         HHGear^.X := Gear^.X;
       
  1375         HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius)
       
  1376         end;
       
  1377 
       
  1378     if (Gear^.Message and gmAttack) <> 0 then
       
  1379         if (Gear^.State and gsttmpFlag) <> 0 then
       
  1380             Gear^.Timer := 1
       
  1381     else //there would be a mistake.
       
  1382     else
       
  1383         if (Gear^.State and gsttmpFlag) = 0 then
       
  1384             Gear^.State := Gear^.State or gsttmpFlag;
       
  1385     if ((Gear^.Message and gmLeft) <> 0) then
       
  1386         Gear^.dX := - _0_3
       
  1387     else
       
  1388         if ((Gear^.Message and gmRight) <> 0) then
       
  1389             Gear^.dX := _0_3
       
  1390     else Gear^.dX := _0;
       
  1391 end;
       
  1392 
       
  1393 procedure doStepPickHammer(Gear: PGear);
       
  1394 var
       
  1395     i, y: LongInt;
       
  1396     ar: TRangeArray;
       
  1397     HHGear: PGear;
       
  1398 begin
       
  1399     i := 0;
       
  1400     HHGear := Gear^.Hedgehog^.Gear;
       
  1401 
       
  1402     y := hwRound(Gear^.Y) - cHHRadius * 2;
       
  1403     while y < hwRound(Gear^.Y) do
       
  1404         begin
       
  1405         ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2));
       
  1406         ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
       
  1407         inc(y, 2);
       
  1408         inc(i)
       
  1409         end;
       
  1410 
       
  1411     DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i));
       
  1412     Gear^.dY := HHGear^.dY;
       
  1413     DeleteCI(HHGear);
       
  1414 
       
  1415     Gear^.SoundChannel := LoopSound(sndPickhammer);
       
  1416     doStepPickHammerWork(Gear);
       
  1417     Gear^.doStep := @doStepPickHammerWork
       
  1418 end;
       
  1419 
       
  1420 ////////////////////////////////////////////////////////////////////////////////
       
  1421 var
       
  1422     BTPrevAngle, BTSteps: LongInt;
       
  1423 
       
  1424 procedure doStepBlowTorchWork(Gear: PGear);
       
  1425 var
       
  1426     HHGear: PGear;
       
  1427     b: boolean;
       
  1428     prevX: LongInt;
       
  1429 begin
       
  1430     AllInactive := false;
       
  1431     dec(Gear^.Timer);
       
  1432     if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then
       
  1433         dec(TurnTimeLeft);
       
  1434 
       
  1435     HHGear := Gear^.Hedgehog^.Gear;
       
  1436 
       
  1437     HedgehogChAngle(HHGear);
       
  1438 
       
  1439     b := false;
       
  1440 
       
  1441     if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7  then
       
  1442         begin
       
  1443         Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX);
       
  1444         Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5);
       
  1445         BTPrevAngle := HHGear^.Angle;
       
  1446         b := true
       
  1447         end;
       
  1448 
       
  1449     if ((HHGear^.State and gstMoving) <> 0) then
       
  1450         begin
       
  1451         doStepHedgehogMoving(HHGear);
       
  1452         if (HHGear^.State and gstHHDriven) = 0 then
       
  1453             Gear^.Timer := 0
       
  1454         end;
       
  1455 
       
  1456     if Gear^.Timer mod cHHStepTicks = 0 then
       
  1457         begin
       
  1458         b := true;
       
  1459         if Gear^.dX.isNegative then
       
  1460             HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmLeft
       
  1461         else
       
  1462             HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmRight;
       
  1463 
       
  1464         if ((HHGear^.State and gstMoving) = 0) then
       
  1465             begin
       
  1466             HHGear^.State := HHGear^.State and (not gstAttacking);
       
  1467             prevX := hwRound(HHGear^.X);
       
  1468 
       
  1469             // why the call to HedgehogStep then a further increment of X?
       
  1470             if (prevX = hwRound(HHGear^.X)) and
       
  1471                CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y),
       
  1472                lfIndestructible) then HedgehogStep(HHGear);
       
  1473 
       
  1474             if (prevX = hwRound(HHGear^.X)) and
       
  1475                CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y),
       
  1476                lfIndestructible) then HHGear^.X := HHGear^.X + SignAs(_1, HHGear^.dX);
       
  1477             HHGear^.State := HHGear^.State or gstAttacking
       
  1478             end;
       
  1479 
       
  1480         inc(BTSteps);
       
  1481         if BTSteps = 7 then
       
  1482             begin
       
  1483             BTSteps := 0;
       
  1484             if CheckLandValue(hwRound(HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC) + SignAs(_6,Gear^.dX)), hwRound(HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC)),lfIndestructible) then
       
  1485                 begin
       
  1486                 Gear^.X := HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC);
       
  1487                 Gear^.Y := HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC);
       
  1488                 end;
       
  1489             HHGear^.State := HHGear^.State or gstNoDamage;
       
  1490             AmmoShove(Gear, 2, 15);
       
  1491             HHGear^.State := HHGear^.State and (not gstNoDamage)
       
  1492             end;
       
  1493         end;
       
  1494 
       
  1495     if b then
       
  1496     begin
       
  1497         DrawTunnel(HHGear^.X + Gear^.dX * cHHRadius,
       
  1498         HHGear^.Y + Gear^.dY * cHHRadius - _1 -
       
  1499         ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7),
       
  1500         Gear^.dX, Gear^.dY,
       
  1501         cHHStepTicks, cHHRadius * 2 + 7);
       
  1502     end;
       
  1503 
       
  1504     if (TurnTimeLeft = 0) or (Gear^.Timer = 0)
       
  1505     or ((HHGear^.Message and gmAttack) <> 0) then
       
  1506         begin
       
  1507         HHGear^.Message := 0;
       
  1508         HHGear^.State := HHGear^.State and (not gstNotKickable);
       
  1509         DeleteGear(Gear);
       
  1510         AfterAttack
       
  1511         end
       
  1512 end;
       
  1513 
       
  1514 procedure doStepBlowTorch(Gear: PGear);
       
  1515 var
       
  1516     HHGear: PGear;
       
  1517 begin
       
  1518     BTPrevAngle := High(LongInt);
       
  1519     BTSteps := 0;
       
  1520     HHGear := Gear^.Hedgehog^.Gear;
       
  1521     HedgehogChAngle(HHGear);
       
  1522     Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX);
       
  1523     Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5);
       
  1524     DrawTunnel(HHGear^.X,
       
  1525         HHGear^.Y + Gear^.dY * cHHRadius - _1 -
       
  1526         ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7),
       
  1527         Gear^.dX, Gear^.dY,
       
  1528         cHHStepTicks, cHHRadius * 2 + 7);
       
  1529     HHGear^.Message := 0;
       
  1530     HHGear^.State := HHGear^.State or gstNotKickable;
       
  1531     Gear^.doStep := @doStepBlowTorchWork
       
  1532 end;
       
  1533 
       
  1534 ////////////////////////////////////////////////////////////////////////////////
       
  1535 procedure doStepMine(Gear: PGear);
       
  1536 var vg: PVisualGear;
       
  1537     dxdy: hwFloat;
       
  1538 begin
       
  1539     if Gear^.Health = 0 then dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY);
       
  1540     if (Gear^.State and gstMoving) <> 0 then
       
  1541         begin
       
  1542         DeleteCI(Gear);
       
  1543         doStepFallingGear(Gear);
       
  1544         if (Gear^.State and gstMoving) = 0 then
       
  1545             begin
       
  1546             AddGearCI(Gear);
       
  1547             Gear^.dX := _0;
       
  1548             Gear^.dY := _0
       
  1549             end;
       
  1550         CalcRotationDirAngle(Gear);
       
  1551         AllInactive := false
       
  1552         end
       
  1553     else if (GameTicks and $3F) = 25 then
       
  1554         doStepFallingGear(Gear);
       
  1555     if (Gear^.Health = 0) then
       
  1556         begin
       
  1557         if (dxdy > _0_4) and (Gear^.State and gstCollision <> 0) then
       
  1558             inc(Gear^.Damage, hwRound(dxdy * _50));
       
  1559 
       
  1560         if ((GameTicks and $FF) = 0) and (Gear^.Damage > random(30)) then
       
  1561             begin
       
  1562             vg:= AddVisualGear(hwRound(Gear^.X) - 4  + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke);
       
  1563             if vg <> nil then
       
  1564                 vg^.Scale:= 0.5
       
  1565             end;
       
  1566 
       
  1567         if (Gear^.Damage > 35) then
       
  1568             begin
       
  1569             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  1570             DeleteGear(Gear);
       
  1571             exit
       
  1572             end
       
  1573         end;
       
  1574 
       
  1575     if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then
       
  1576         if ((Gear^.State and gstAttacking) = 0) then
       
  1577             begin
       
  1578             if ((GameTicks and $1F) = 0) then
       
  1579                 if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then
       
  1580                     Gear^.State := Gear^.State or gstAttacking
       
  1581             end
       
  1582         else // gstAttacking <> 0
       
  1583             begin
       
  1584             AllInactive := false;
       
  1585             if (Gear^.Timer and $FF) = 0 then
       
  1586                 PlaySound(sndMineTick);
       
  1587             if Gear^.Timer = 0 then
       
  1588                 begin
       
  1589                 if ((Gear^.State and gstWait) <> 0)
       
  1590                 or (cMineDudPercent = 0)
       
  1591                 or (getRandom(100) > cMineDudPercent) then
       
  1592                     begin
       
  1593                     doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  1594                     DeleteGear(Gear)
       
  1595                     end
       
  1596                 else
       
  1597                     begin
       
  1598                     vg:= AddVisualGear(hwRound(Gear^.X) - 4  + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke);
       
  1599                     if vg <> nil then
       
  1600                         vg^.Scale:= 0.5;
       
  1601                     PlaySound(sndVaporize);
       
  1602                     Gear^.Health := 0;
       
  1603                     Gear^.Damage := 0;
       
  1604                     Gear^.State := Gear^.State and (not gstAttacking)
       
  1605                     end;
       
  1606                 exit
       
  1607                 end;
       
  1608             dec(Gear^.Timer);
       
  1609             end
       
  1610     else // gsttmpFlag = 0
       
  1611         if (TurnTimeLeft = 0)
       
  1612         or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime))
       
  1613         or (Gear^.Hedgehog^.Gear = nil) then
       
  1614             Gear^.State := Gear^.State or gsttmpFlag;
       
  1615 end;
       
  1616 
       
  1617 ////////////////////////////////////////////////////////////////////////////////
       
  1618 procedure doStepSMine(Gear: PGear);
       
  1619 begin
       
  1620     // TODO: do real calculation?
       
  1621     if TestCollisionXwithGear(Gear, 2)
       
  1622     or (TestCollisionYwithGear(Gear, -2) <> 0)
       
  1623     or TestCollisionXwithGear(Gear, -2)
       
  1624     or (TestCollisionYwithGear(Gear, 2) <> 0) then
       
  1625         begin
       
  1626         if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then
       
  1627             begin
       
  1628             PlaySound(sndRopeAttach);
       
  1629             Gear^.dX:= _0;
       
  1630             Gear^.dY:= _0;
       
  1631             AddGearCI(Gear);
       
  1632             end;
       
  1633         end
       
  1634     else
       
  1635         begin
       
  1636         DeleteCI(Gear);
       
  1637         doStepFallingGear(Gear);
       
  1638         AllInactive := false;
       
  1639         CalcRotationDirAngle(Gear);
       
  1640         end;
       
  1641 
       
  1642     if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then
       
  1643         begin
       
  1644         if ((Gear^.State and gstAttacking) = 0) then
       
  1645             begin
       
  1646             if ((GameTicks and $1F) = 0) then
       
  1647                 if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then
       
  1648                     Gear^.State := Gear^.State or gstAttacking
       
  1649             end
       
  1650         else // gstAttacking <> 0
       
  1651         begin
       
  1652             AllInactive := false;
       
  1653             if Gear^.Timer = 0 then
       
  1654                 begin
       
  1655                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  1656                 DeleteGear(Gear);
       
  1657                 exit
       
  1658             end else
       
  1659                 if (Gear^.Timer and $FF) = 0 then
       
  1660                     PlaySound(sndMineTick);
       
  1661 
       
  1662             dec(Gear^.Timer);
       
  1663                 end
       
  1664         end
       
  1665     else // gsttmpFlag = 0
       
  1666         if (TurnTimeLeft = 0)
       
  1667         or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime))
       
  1668         or (Gear^.Hedgehog^.Gear = nil) then
       
  1669             Gear^.State := Gear^.State or gsttmpFlag;
       
  1670 end;
       
  1671 
       
  1672 ////////////////////////////////////////////////////////////////////////////////
       
  1673 procedure doStepDynamite(Gear: PGear);
       
  1674 begin
       
  1675     doStepFallingGear(Gear);
       
  1676     AllInactive := false;
       
  1677     if Gear^.Timer mod 166 = 0 then
       
  1678         inc(Gear^.Tag);
       
  1679     if Gear^.Timer = 1000 then // might need better timing
       
  1680         makeHogsWorry(Gear^.X, Gear^.Y, 75);
       
  1681     if Gear^.Timer = 0 then
       
  1682         begin
       
  1683         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, Gear^.Hedgehog, EXPLAutoSound);
       
  1684         DeleteGear(Gear);
       
  1685         exit
       
  1686         end;
       
  1687     dec(Gear^.Timer);
       
  1688 end;
       
  1689 
       
  1690 ///////////////////////////////////////////////////////////////////////////////
       
  1691 
       
  1692 procedure doStepRollingBarrel(Gear: PGear);
       
  1693 var
       
  1694     i: LongInt;
       
  1695     particle: PVisualGear;
       
  1696     dxdy: hwFloat;
       
  1697 begin
       
  1698     if (Gear^.dY.QWordValue = 0) and (Gear^.dY.QWordValue = 0) and (TestCollisionYwithGear(Gear, 1) = 0) then
       
  1699         SetLittle(Gear^.dY);
       
  1700     Gear^.State := Gear^.State or gstAnimation;
       
  1701     if Gear^.Health < cBarrelHealth then Gear^.State:= Gear^.State and not gstFrozen;
       
  1702 
       
  1703     if ((Gear^.dX.QWordValue <> 0)
       
  1704     or (Gear^.dY.QWordValue <> 0))  then
       
  1705         begin
       
  1706         DeleteCI(Gear);
       
  1707         AllInactive := false;
       
  1708         dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY);
       
  1709         doStepFallingGear(Gear);
       
  1710         if (Gear^.State and gstCollision <> 0) and(dxdy > _0_4) then
       
  1711             begin
       
  1712             if (TestCollisionYwithGear(Gear, 1) <> 0) then
       
  1713                 begin
       
  1714                 Gear^.State := Gear^.State or gsttmpFlag;
       
  1715                 for i:= min(12, hwRound(dxdy*_10)) downto 0 do
       
  1716                     begin
       
  1717                     particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12,vgtDust);
       
  1718                     if particle <> nil then
       
  1719                         particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480)
       
  1720                     end
       
  1721                 end;
       
  1722             inc(Gear^.Damage, hwRound(dxdy * _50))
       
  1723             end;
       
  1724         CalcRotationDirAngle(Gear);
       
  1725         //CheckGearDrowning(Gear)
       
  1726         end
       
  1727     else
       
  1728         begin
       
  1729         Gear^.State := Gear^.State or gsttmpFlag;
       
  1730         AddGearCI(Gear)
       
  1731         end;
       
  1732 
       
  1733 (*
       
  1734 Attempt to make a barrel knock itself over an edge.  Would need more checks to avoid issues like burn damage
       
  1735     begin
       
  1736     x:= hwRound(Gear^.X);
       
  1737     y:= hwRound(Gear^.Y);
       
  1738     if (((y+1) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
       
  1739         if (Land[y+1, x] = 0) then
       
  1740             begin
       
  1741             if (((y+1) and LAND_HEIGHT_MASK) = 0) and (((x+Gear^.Radius-2) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x+Gear^.Radius-2] = 0) then
       
  1742                 Gear^.dX:= -_0_08
       
  1743             else if (((y+1 and LAND_HEIGHT_MASK)) = 0) and (((x-(Gear^.Radius-2)) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x-(Gear^.Radius-2)] = 0) then
       
  1744                 Gear^.dX:= _0_08;
       
  1745             end;
       
  1746     if Gear^.dX.QWordValue = 0 then AddGearCI(Gear)
       
  1747     end; *)
       
  1748 
       
  1749     if not Gear^.dY.isNegative and (Gear^.dY < _0_001) and (TestCollisionYwithGear(Gear, 1) <> 0) then
       
  1750         Gear^.dY := _0;
       
  1751     if hwAbs(Gear^.dX) < _0_001 then
       
  1752         Gear^.dX := _0;
       
  1753 
       
  1754     if (Gear^.Health > 0) and ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then
       
  1755         if (cBarrelHealth div Gear^.Health) > 2 then
       
  1756             AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke)
       
  1757     else
       
  1758         AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite);
       
  1759     dec(Gear^.Health, Gear^.Damage);
       
  1760     Gear^.Damage := 0;
       
  1761     if Gear^.Health <= 0 then
       
  1762         doStepCase(Gear);
       
  1763 end;
       
  1764 
       
  1765 procedure doStepCase(Gear: PGear);
       
  1766 var
       
  1767     i, x, y: LongInt;
       
  1768     k: TGearType;
       
  1769     dX, dY: HWFloat;
       
  1770     hog: PHedgehog;
       
  1771     sparkles: PVisualGear;
       
  1772     gi: PGear;
       
  1773 begin
       
  1774     k := Gear^.Kind;
       
  1775 
       
  1776     if (Gear^.Message and gmDestroy) > 0 then
       
  1777         begin
       
  1778         DeleteGear(Gear);
       
  1779         FreeActionsList;
       
  1780         SetAllToActive;
       
  1781         // something (hh, mine, etc...) could be on top of the case
       
  1782         with CurrentHedgehog^ do
       
  1783             if Gear <> nil then
       
  1784                 Gear^.Message := Gear^.Message and (not (gmLJump or gmHJump));
       
  1785         exit
       
  1786         end;
       
  1787     if (k = gtExplosives) and (Gear^.Health < cBarrelHealth) then Gear^.State:= Gear^.State and not gstFrozen;
       
  1788 
       
  1789     if ((k <> gtExplosives) and (Gear^.Damage > 0)) or ((k = gtExplosives) and (Gear^.Health<=0)) then
       
  1790         begin
       
  1791         x := hwRound(Gear^.X);
       
  1792         y := hwRound(Gear^.Y);
       
  1793         hog:= Gear^.Hedgehog;
       
  1794 
       
  1795         DeleteGear(Gear);
       
  1796         // <-- delete gear!
       
  1797 
       
  1798         if k = gtCase then
       
  1799             begin
       
  1800             doMakeExplosion(x, y, 25, hog, EXPLAutoSound);
       
  1801             for i:= 0 to 63 do
       
  1802                 AddGear(x, y, gtFlame, 0, _0, _0, 0);
       
  1803             end
       
  1804         else if k = gtExplosives then
       
  1805                 begin
       
  1806                 doMakeExplosion(x, y, 75, hog, EXPLAutoSound);
       
  1807                 for i:= 0 to 31 do
       
  1808                     begin
       
  1809                     dX := AngleCos(i * 64) * _0_5 * (getrandomf + _1);
       
  1810                     dY := AngleSin(i * 64) * _0_5 * (getrandomf + _1);
       
  1811                     AddGear(x, y, gtFlame, 0, dX, dY, 0);
       
  1812                     AddGear(x, y, gtFlame, gstTmpFlag, -dX, -dY, 0);
       
  1813                     end
       
  1814                 end;
       
  1815             exit
       
  1816         end;
       
  1817 
       
  1818     if k = gtExplosives then
       
  1819         begin
       
  1820         //if V > _0_03 then Gear^.State:= Gear^.State or gstAnimation;
       
  1821         if (hwAbs(Gear^.dX) > _0_15) or ((hwAbs(Gear^.dY) > _0_15) and (hwAbs(Gear^.dX) > _0_02)) then
       
  1822             begin
       
  1823             Gear^.doStep := @doStepRollingBarrel;
       
  1824             exit;
       
  1825             end
       
  1826         else Gear^.dX:= _0;
       
  1827 
       
  1828         if ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then
       
  1829             if (cBarrelHealth div Gear^.Health) > 2 then
       
  1830                 AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke)
       
  1831             else
       
  1832                 AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite);
       
  1833         dec(Gear^.Health, Gear^.Damage);
       
  1834         Gear^.Damage := 0;
       
  1835         end
       
  1836     else
       
  1837         begin
       
  1838         if (Gear^.Pos <> posCaseHealth) and (GameTicks and $1FFF = 0) then // stir 'em up periodically
       
  1839             begin
       
  1840             gi := GearsList;
       
  1841             while gi <> nil do
       
  1842                 begin
       
  1843                 if gi^.Kind = gtGenericFaller then
       
  1844                     begin
       
  1845                     gi^.Active:= true;
       
  1846                     gi^.X:=  int2hwFloat(GetRandom(rightX-leftX)+leftX);
       
  1847                     gi^.Y:=  int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY);
       
  1848                     gi^.dX:= _90-(GetRandomf*_360);
       
  1849                     gi^.dY:= _90-(GetRandomf*_360)
       
  1850                     end;
       
  1851                 gi := gi^.NextGear
       
  1852                 end
       
  1853             end;
       
  1854 
       
  1855         if Gear^.Timer = 500 then
       
  1856             begin
       
  1857 (* Can't make sparkles team coloured without working out what the next team is going to be. This should be solved, really, since it also screws up
       
  1858    voices. Reinforcements voices is heard for active team, not team-to-be.  Either that or change crate spawn from end of turn to start, although that
       
  1859    has its own complexities. *)
       
  1860             // Abuse a couple of gear values to track origin
       
  1861             Gear^.Angle:= hwRound(Gear^.Y);
       
  1862             Gear^.Tag:= random(2);
       
  1863             inc(Gear^.Timer)
       
  1864             end;
       
  1865         if Gear^.Timer < 1833 then inc(Gear^.Timer);
       
  1866         if Gear^.Timer = 1000 then
       
  1867             begin
       
  1868             sparkles:= AddVisualGear(hwRound(Gear^.X), Gear^.Angle, vgtDust, 1);
       
  1869             if sparkles <> nil then
       
  1870                 begin
       
  1871                 sparkles^.dX:= 0;
       
  1872                 sparkles^.dY:= 0;
       
  1873                 sparkles^.Angle:= 270;
       
  1874                 if Gear^.Tag = 1 then
       
  1875                     sparkles^.Tint:= $3744D7FF
       
  1876                 else sparkles^.Tint:= $FAB22CFF
       
  1877                 end;
       
  1878             end;
       
  1879         if Gear^.Timer < 1000 then
       
  1880             begin
       
  1881             AllInactive:= false;
       
  1882             exit
       
  1883             end
       
  1884         end;
       
  1885 
       
  1886 
       
  1887     if (Gear^.dY.QWordValue <> 0)
       
  1888     or (TestCollisionYwithGear(Gear, 1) = 0) then
       
  1889         begin
       
  1890         AllInactive := false;
       
  1891 
       
  1892         Gear^.dY := Gear^.dY + cGravity;
       
  1893 
       
  1894         if (Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, -1) <> 0) then
       
  1895             Gear^.dY := _0;
       
  1896 
       
  1897         Gear^.Y := Gear^.Y + Gear^.dY;
       
  1898 
       
  1899         if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then
       
  1900             SetAllHHToActive(false);
       
  1901 
       
  1902         if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then
       
  1903             begin
       
  1904             if (Gear^.dY > _0_2) and (k = gtExplosives) then
       
  1905                 inc(Gear^.Damage, hwRound(Gear^.dY * _70));
       
  1906 
       
  1907             if Gear^.dY > _0_2 then
       
  1908                 for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do
       
  1909                     AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
       
  1910 
       
  1911             Gear^.dY := - Gear^.dY * Gear^.Elasticity;
       
  1912             if Gear^.dY > - _0_001 then
       
  1913                 Gear^.dY := _0
       
  1914             else if Gear^.dY < - _0_03 then
       
  1915                 PlaySound(Gear^.ImpactSound);
       
  1916             end;
       
  1917         //if Gear^.dY > - _0_001 then Gear^.dY:= _0
       
  1918         CheckGearDrowning(Gear);
       
  1919         end;
       
  1920 
       
  1921     if (Gear^.dY.QWordValue = 0) then
       
  1922         AddGearCI(Gear)
       
  1923     else if (Gear^.dY.QWordValue <> 0) then
       
  1924         DeleteCI(Gear)
       
  1925 end;
       
  1926 
       
  1927 ////////////////////////////////////////////////////////////////////////////////
       
  1928 
       
  1929 procedure doStepTarget(Gear: PGear);
       
  1930 begin
       
  1931     if (Gear^.Timer = 0) and (Gear^.Tag = 0) then
       
  1932         PlaySound(sndWarp);
       
  1933 
       
  1934     if (Gear^.Tag = 0) and (Gear^.Timer < 1000) then
       
  1935         inc(Gear^.Timer)
       
  1936     else if Gear^.Tag = 1 then
       
  1937         Gear^.Tag := 2
       
  1938     else if Gear^.Tag = 2 then
       
  1939             if Gear^.Timer > 0 then
       
  1940                 dec(Gear^.Timer)
       
  1941     else
       
  1942         begin
       
  1943         DeleteGear(Gear);
       
  1944         exit;
       
  1945         end;
       
  1946 
       
  1947     doStepCase(Gear)
       
  1948 end;
       
  1949 
       
  1950 ////////////////////////////////////////////////////////////////////////////////
       
  1951 procedure doStepIdle(Gear: PGear);
       
  1952 begin
       
  1953     AllInactive := false;
       
  1954     dec(Gear^.Timer);
       
  1955     if Gear^.Timer = 0 then
       
  1956         begin
       
  1957         DeleteGear(Gear);
       
  1958         AfterAttack
       
  1959         end
       
  1960 end;
       
  1961 
       
  1962 ////////////////////////////////////////////////////////////////////////////////
       
  1963 procedure doStepShover(Gear: PGear);
       
  1964 var
       
  1965     HHGear: PGear;
       
  1966 begin
       
  1967     HHGear := Gear^.Hedgehog^.Gear;
       
  1968     HHGear^.State := HHGear^.State or gstNoDamage;
       
  1969     DeleteCI(HHGear);
       
  1970 
       
  1971     AmmoShove(Gear, 30, 115);
       
  1972 
       
  1973     HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving;
       
  1974     Gear^.Timer := 250;
       
  1975     Gear^.doStep := @doStepIdle
       
  1976 end;
       
  1977 
       
  1978 ////////////////////////////////////////////////////////////////////////////////
       
  1979 procedure doStepWhip(Gear: PGear);
       
  1980 var
       
  1981     HHGear: PGear;
       
  1982     i: LongInt;
       
  1983 begin
       
  1984     HHGear := Gear^.Hedgehog^.Gear;
       
  1985     HHGear^.State := HHGear^.State or gstNoDamage;
       
  1986     DeleteCI(HHGear);
       
  1987 
       
  1988     for i:= 0 to 3 do
       
  1989         begin
       
  1990         AmmoShove(Gear, 30, 25);
       
  1991         Gear^.X := Gear^.X + Gear^.dX * 5
       
  1992         end;
       
  1993 
       
  1994     HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving;
       
  1995 
       
  1996     Gear^.Timer := 250;
       
  1997     Gear^.doStep := @doStepIdle
       
  1998 end;
       
  1999 
       
  2000 ////////////////////////////////////////////////////////////////////////////////
       
  2001 procedure doStepFlame(Gear: PGear);
       
  2002 var
       
  2003     gX,gY,i: LongInt;
       
  2004     sticky: Boolean;
       
  2005     vgt: PVisualGear;
       
  2006     tdX,tdY: HWFloat;
       
  2007 begin
       
  2008     sticky:= (Gear^.State and gsttmpFlag) <> 0;
       
  2009     if not sticky then AllInactive := false;
       
  2010 
       
  2011     if TestCollisionYwithGear(Gear, 1) = 0 then
       
  2012         begin
       
  2013         AllInactive := false;
       
  2014 
       
  2015         if ((GameTicks mod 100) = 0) then
       
  2016             begin
       
  2017             vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire, gstTmpFlag);
       
  2018             if vgt <> nil then
       
  2019                 begin
       
  2020                 vgt^.dx:= 0;
       
  2021                 vgt^.dy:= 0;
       
  2022                 vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2);
       
  2023                 end;
       
  2024             end;
       
  2025 
       
  2026 
       
  2027         if Gear^.dX.QWordValue > _0_01.QWordValue then
       
  2028             Gear^.dX := Gear^.dX * _0_995;
       
  2029 
       
  2030         Gear^.dY := Gear^.dY + cGravity;
       
  2031         // if sticky then Gear^.dY := Gear^.dY + cGravity;
       
  2032 
       
  2033         if Gear^.dY.QWordValue > _0_2.QWordValue then
       
  2034             Gear^.dY := Gear^.dY * _0_995;
       
  2035 
       
  2036         //if sticky then Gear^.X := Gear^.X + Gear^.dX else
       
  2037         Gear^.X := Gear^.X + Gear^.dX + cWindSpeed * 640;
       
  2038         Gear^.Y := Gear^.Y + Gear^.dY;
       
  2039 
       
  2040         if (hwRound(Gear^.Y) > cWaterLine) then
       
  2041             begin
       
  2042             gX := hwRound(Gear^.X);
       
  2043             for i:= 0 to 3 do
       
  2044                 AddVisualGear(gX - 16 + Random(32), cWaterLine - 16 + Random(16), vgtSteam);
       
  2045             PlaySound(sndVaporize);
       
  2046             DeleteGear(Gear);
       
  2047             exit
       
  2048             end
       
  2049         end
       
  2050     else
       
  2051         begin
       
  2052         if sticky and (GameTicks and $F = 0) then
       
  2053             begin
       
  2054             Gear^.Radius := 7;
       
  2055             tdX:= Gear^.dX;
       
  2056             tdY:= Gear^.dY;
       
  2057             Gear^.dX.QWordValue:= 120000000;
       
  2058             Gear^.dY.QWordValue:= 429496730;
       
  2059             Gear^.dX.isNegative:= getrandom(2)<>1;
       
  2060             Gear^.dY.isNegative:= true;
       
  2061             AmmoShove(Gear, 2, 125);
       
  2062             Gear^.dX:= tdX;
       
  2063             Gear^.dY:= tdY;
       
  2064             Gear^.Radius := 1
       
  2065             end;
       
  2066         if Gear^.Timer > 0 then
       
  2067             begin
       
  2068             dec(Gear^.Timer);
       
  2069             inc(Gear^.Damage)
       
  2070             end
       
  2071         else
       
  2072             begin
       
  2073             gX := hwRound(Gear^.X);
       
  2074             gY := hwRound(Gear^.Y);
       
  2075             // Standard fire
       
  2076             if not sticky then
       
  2077                 begin
       
  2078                 if ((GameTicks and $1) = 0) then
       
  2079                     begin
       
  2080                     Gear^.Radius := 7;
       
  2081                     tdX:= Gear^.dX;
       
  2082                     tdY:= Gear^.dY;
       
  2083                     Gear^.dX.QWordValue:= 214748365;
       
  2084                     Gear^.dY.QWordValue:= 429496730;
       
  2085                     Gear^.dX.isNegative:= getrandom(2)<>1;
       
  2086                     Gear^.dY.isNegative:= true;
       
  2087                     AmmoShove(Gear, 6, 100);
       
  2088                     Gear^.dX:= tdX;
       
  2089                     Gear^.dY:= tdY;
       
  2090                     Gear^.Radius := 1;
       
  2091                     end
       
  2092                 else if ((GameTicks and $3) = 3) then
       
  2093                     doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage);
       
  2094                 //DrawExplosion(gX, gY, 4);
       
  2095 
       
  2096                 if ((GameTicks and $7) = 0) and (Random(2) = 0) then
       
  2097                     for i:= Random(2) downto 0 do
       
  2098                         AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  2099 
       
  2100                 if Gear^.Health > 0 then
       
  2101                     dec(Gear^.Health);
       
  2102                 Gear^.Timer := 450 - Gear^.Tag * 8
       
  2103                 end
       
  2104             else
       
  2105                 begin
       
  2106                 // Modified fire
       
  2107                 if ((GameTicks and $7FF) = 0) and ((GameFlags and gfSolidLand) = 0) then
       
  2108                     begin
       
  2109                     DrawExplosion(gX, gY, 4);
       
  2110 
       
  2111                     for i:= Random(3) downto 0 do
       
  2112                         AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  2113                     end;
       
  2114 
       
  2115 // This one is interesting.  I think I understand the purpose, but I wonder if a bit more fuzzy of kicking could be done with getrandom.
       
  2116                 Gear^.Timer := 100 - Gear^.Tag * 3;
       
  2117                 if (Gear^.Damage > 3000+Gear^.Tag*1500) then
       
  2118                     Gear^.Health := 0
       
  2119                 end
       
  2120             end
       
  2121         end;
       
  2122     if Gear^.Health = 0 then
       
  2123         begin
       
  2124         gX := hwRound(Gear^.X);
       
  2125         gY := hwRound(Gear^.Y);
       
  2126         if not sticky then
       
  2127             begin
       
  2128             if ((GameTicks and $3) = 0) and (Random(1) = 0) then
       
  2129                 for i:= Random(2) downto 0 do
       
  2130                     AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  2131             end
       
  2132         else
       
  2133             for i:= Random(3) downto 0 do
       
  2134                 AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke);
       
  2135 
       
  2136         DeleteGear(Gear)
       
  2137         end;
       
  2138 end;
       
  2139 
       
  2140 ////////////////////////////////////////////////////////////////////////////////
       
  2141 procedure doStepFirePunchWork(Gear: PGear);
       
  2142 var
       
  2143     HHGear: PGear;
       
  2144 begin
       
  2145     AllInactive := false;
       
  2146     if ((Gear^.Message and gmDestroy) <> 0) then
       
  2147         begin
       
  2148         DeleteGear(Gear);
       
  2149         AfterAttack;
       
  2150         exit
       
  2151         end;
       
  2152 
       
  2153     HHGear := Gear^.Hedgehog^.Gear;
       
  2154     if hwRound(HHGear^.Y) <= Gear^.Tag - 2 then
       
  2155         begin
       
  2156         Gear^.Tag := hwRound(HHGear^.Y);
       
  2157         DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y - _1, _0_5, _0, cHHRadius * 4, 2);
       
  2158         HHGear^.State := HHGear^.State or gstNoDamage;
       
  2159         Gear^.Y := HHGear^.Y;
       
  2160         AmmoShove(Gear, 30, 40);
       
  2161         HHGear^.State := HHGear^.State and (not gstNoDamage)
       
  2162         end;
       
  2163 
       
  2164     HHGear^.dY := HHGear^.dY + cGravity;
       
  2165     if not (HHGear^.dY.isNegative) then
       
  2166         begin
       
  2167         HHGear^.State := HHGear^.State or gstMoving;
       
  2168         DeleteGear(Gear);
       
  2169         AfterAttack;
       
  2170         exit
       
  2171         end;
       
  2172 
       
  2173     if CheckLandValue(hwRound(HHGear^.X), hwRound(HHGear^.Y + HHGear^.dY + SignAs(_6,Gear^.dY)),
       
  2174         lfIndestructible) then
       
  2175             HHGear^.Y := HHGear^.Y + HHGear^.dY
       
  2176 end;
       
  2177 
       
  2178 procedure doStepFirePunch(Gear: PGear);
       
  2179 var
       
  2180     HHGear: PGear;
       
  2181 begin
       
  2182     AllInactive := false;
       
  2183     HHGear := Gear^.Hedgehog^.Gear;
       
  2184     DeleteCI(HHGear);
       
  2185     //HHGear^.X := int2hwFloat(hwRound(HHGear^.X)) - _0_5; WTF?
       
  2186     HHGear^.dX := SignAs(cLittle, Gear^.dX);
       
  2187 
       
  2188     HHGear^.dY := - _0_3;
       
  2189 
       
  2190     Gear^.X := HHGear^.X;
       
  2191     Gear^.dX := SignAs(_0_45, Gear^.dX);
       
  2192     Gear^.dY := - _0_9;
       
  2193     Gear^.doStep := @doStepFirePunchWork;
       
  2194     DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y + _1, _0_5, _0, cHHRadius * 4, 5);
       
  2195 
       
  2196     PlaySoundV(TSound(ord(sndFirePunch1) + GetRandom(6)), HHGear^.Hedgehog^.Team^.voicepack)
       
  2197 end;
       
  2198 
       
  2199 ////////////////////////////////////////////////////////////////////////////////
       
  2200 
       
  2201 procedure doStepParachuteWork(Gear: PGear);
       
  2202 var
       
  2203     HHGear: PGear;
       
  2204 begin
       
  2205     HHGear := Gear^.Hedgehog^.Gear;
       
  2206 
       
  2207     inc(Gear^.Timer);
       
  2208 
       
  2209     if (TestCollisionYwithGear(HHGear, 1) <> 0)
       
  2210     or ((HHGear^.State and gstHHDriven) = 0)
       
  2211     or CheckGearDrowning(HHGear)
       
  2212     or ((Gear^.Message and gmAttack) <> 0) then
       
  2213         begin
       
  2214         with HHGear^ do
       
  2215             begin
       
  2216             Message := 0;
       
  2217             SetLittle(dX);
       
  2218             dY := _0;
       
  2219             State := State or gstMoving;
       
  2220             end;
       
  2221         DeleteGear(Gear);
       
  2222         isCursorVisible := false;
       
  2223         ApplyAmmoChanges(HHGear^.Hedgehog^);
       
  2224         exit
       
  2225         end;
       
  2226 
       
  2227     HHGear^.X := HHGear^.X + cWindSpeed * 200;
       
  2228 
       
  2229     if (Gear^.Message and gmLeft) <> 0 then
       
  2230         HHGear^.X := HHGear^.X - cMaxWindSpeed * 80
       
  2231 
       
  2232     else if (Gear^.Message and gmRight) <> 0 then
       
  2233         HHGear^.X := HHGear^.X + cMaxWindSpeed * 80;
       
  2234 
       
  2235     if (Gear^.Message and gmUp) <> 0 then
       
  2236         HHGear^.Y := HHGear^.Y - cGravity * 40
       
  2237 
       
  2238     else if (Gear^.Message and gmDown) <> 0 then
       
  2239         HHGear^.Y := HHGear^.Y + cGravity * 40;
       
  2240 
       
  2241     // don't drift into obstacles
       
  2242     if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then
       
  2243         HHGear^.X := HHGear^.X - int2hwFloat(hwSign(HHGear^.dX));
       
  2244     HHGear^.Y := HHGear^.Y + cGravity * 100;
       
  2245     Gear^.X := HHGear^.X;
       
  2246     Gear^.Y := HHGear^.Y
       
  2247 end;
       
  2248 
       
  2249 procedure doStepParachute(Gear: PGear);
       
  2250 var
       
  2251     HHGear: PGear;
       
  2252 begin
       
  2253     HHGear := Gear^.Hedgehog^.Gear;
       
  2254 
       
  2255     DeleteCI(HHGear);
       
  2256 
       
  2257     AfterAttack;
       
  2258 
       
  2259     HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked or gstMoving));
       
  2260     HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2261 
       
  2262     Gear^.doStep := @doStepParachuteWork;
       
  2263 
       
  2264     Gear^.Message := HHGear^.Message;
       
  2265     doStepParachuteWork(Gear)
       
  2266 end;
       
  2267 
       
  2268 ////////////////////////////////////////////////////////////////////////////////
       
  2269 procedure doStepAirAttackWork(Gear: PGear);
       
  2270 begin
       
  2271     AllInactive := false;
       
  2272     Gear^.X := Gear^.X + cAirPlaneSpeed * Gear^.Tag;
       
  2273 
       
  2274     if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then
       
  2275         begin
       
  2276         dec(Gear^.Health);
       
  2277             case Gear^.State of
       
  2278                 0: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0);
       
  2279                 1: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine,    0, cBombsSpeed * Gear^.Tag, _0, 0);
       
  2280                 2: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0);
       
  2281                 3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed * Gear^.Tag, _0, Gear^.Timer + 1);
       
  2282             //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed *
       
  2283             //                 Gear^.Tag, _0, 5000);
       
  2284             end;
       
  2285         Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag);
       
  2286         StopSoundChan(Gear^.SoundChannel, 4000);
       
  2287         end;
       
  2288 
       
  2289     if (GameTicks and $3F) = 0 then
       
  2290         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
       
  2291 
       
  2292     if (hwRound(Gear^.X) > (max(LAND_WIDTH,4096)+2048)) or (hwRound(Gear^.X) < -2048) then
       
  2293         begin
       
  2294         // avoid to play forever (is this necessary?)
       
  2295         StopSoundChan(Gear^.SoundChannel);
       
  2296         DeleteGear(Gear)
       
  2297         end;
       
  2298 end;
       
  2299 
       
  2300 procedure doStepAirAttack(Gear: PGear);
       
  2301 begin
       
  2302     AllInactive := false;
       
  2303 
       
  2304     if Gear^.X.QWordValue = 0 then
       
  2305         begin
       
  2306         Gear^.Tag :=  1;
       
  2307         Gear^.X := -_2048;
       
  2308         end
       
  2309     else
       
  2310         begin
       
  2311         Gear^.Tag := -1;
       
  2312         Gear^.X := int2hwFloat(max(LAND_WIDTH,4096) + 2048);
       
  2313         end;
       
  2314 
       
  2315     Gear^.Y := int2hwFloat(topY-300);
       
  2316     Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15);
       
  2317 
       
  2318     // calcs for Napalm Strike, so that it will hit the target (without wind at least :P)
       
  2319     if (Gear^.State = 2) then
       
  2320         Gear^.dX := Gear^.dX - cBombsSpeed * Gear^.Tag * 900
       
  2321     // calcs for regular falling gears
       
  2322     else if (int2hwFloat(Gear^.Target.Y) - Gear^.Y > _0) then
       
  2323             Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 /
       
  2324                 cGravity) * Gear^.Tag;
       
  2325 
       
  2326     Gear^.Health := 6;
       
  2327     Gear^.doStep := @doStepAirAttackWork;
       
  2328     Gear^.SoundChannel := LoopSound(sndPlane, 4000);
       
  2329 
       
  2330 end;
       
  2331 
       
  2332 ////////////////////////////////////////////////////////////////////////////////
       
  2333 
       
  2334 procedure doStepAirBomb(Gear: PGear);
       
  2335 begin
       
  2336     AllInactive := false;
       
  2337     doStepFallingGear(Gear);
       
  2338     if (Gear^.State and gstCollision) <> 0 then
       
  2339         begin
       
  2340         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  2341         DeleteGear(Gear);
       
  2342         with mobileRecord do
       
  2343             if (performRumble <> nil) and (not fastUntilLag) then
       
  2344                 performRumble(kSystemSoundID_Vibrate);
       
  2345         exit
       
  2346         end;
       
  2347     if (GameTicks and $3F) = 0 then
       
  2348         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
  2349 end;
       
  2350 
       
  2351 ////////////////////////////////////////////////////////////////////////////////
       
  2352 
       
  2353 procedure doStepGirder(Gear: PGear);
       
  2354 var
       
  2355     HHGear: PGear;
       
  2356     x, y, tx, ty: hwFloat;
       
  2357 begin
       
  2358     AllInactive := false;
       
  2359 
       
  2360     HHGear := Gear^.Hedgehog^.Gear;
       
  2361     tx := int2hwFloat(Gear^.Target.X);
       
  2362     ty := int2hwFloat(Gear^.Target.Y);
       
  2363     x := HHGear^.X;
       
  2364     y := HHGear^.Y;
       
  2365 
       
  2366     if (Distance(tx - x, ty - y) > _256)
       
  2367     or (not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprAmGirder].Width div 2, Gear^.Target.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true, false)) then
       
  2368         begin
       
  2369         PlaySound(sndDenied);
       
  2370         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2371         HHGear^.State := HHGear^.State and (not gstAttacking);
       
  2372         HHGear^.State := HHGear^.State or gstHHChooseTarget;
       
  2373         isCursorVisible := true;
       
  2374         DeleteGear(Gear)
       
  2375         end
       
  2376     else
       
  2377         begin
       
  2378         PlaySound(sndPlaced);
       
  2379         DeleteGear(Gear);
       
  2380         AfterAttack;
       
  2381         end;
       
  2382 
       
  2383     HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked));
       
  2384     HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2385 end;
       
  2386 
       
  2387 ////////////////////////////////////////////////////////////////////////////////
       
  2388 procedure doStepTeleportAfter(Gear: PGear);
       
  2389 var
       
  2390     HHGear: PGear;
       
  2391 begin
       
  2392     HHGear := Gear^.Hedgehog^.Gear;
       
  2393     doStepHedgehogMoving(HHGear);
       
  2394     // if not infattack mode wait for hedgehog finish falling to collect cases
       
  2395     if ((GameFlags and gfInfAttack) <> 0)
       
  2396     or ((HHGear^.State and gstMoving) = 0)
       
  2397     or (Gear^.Hedgehog^.Gear^.Damage > 0)
       
  2398     or ((HHGear^.State and gstDrowning) = 1) then
       
  2399         begin
       
  2400         DeleteGear(Gear);
       
  2401         AfterAttack
       
  2402         end
       
  2403 end;
       
  2404 
       
  2405 procedure doStepTeleportAnim(Gear: PGear);
       
  2406 begin
       
  2407     if (Gear^.Hedgehog^.Gear^.Damage > 0) then
       
  2408         begin
       
  2409         DeleteGear(Gear);
       
  2410         AfterAttack;
       
  2411         end;
       
  2412     inc(Gear^.Timer);
       
  2413     if Gear^.Timer = 65 then
       
  2414         begin
       
  2415         Gear^.Timer := 0;
       
  2416         inc(Gear^.Pos);
       
  2417         if Gear^.Pos = 11 then
       
  2418             Gear^.doStep := @doStepTeleportAfter
       
  2419         end;
       
  2420 end;
       
  2421 
       
  2422 procedure doStepTeleport(Gear: PGear);
       
  2423 var
       
  2424     HHGear: PGear;
       
  2425 begin
       
  2426     AllInactive := false;
       
  2427 
       
  2428     HHGear := Gear^.Hedgehog^.Gear;
       
  2429     if not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprHHTelepMask].Width div 2,
       
  2430         Gear^.Target.Y - SpritesData[sprHHTelepMask].Height div 2,
       
  2431         sprHHTelepMask, 0, false, false) then
       
  2432         begin
       
  2433         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2434         HHGear^.State := HHGear^.State and (not gstAttacking);
       
  2435         HHGear^.State := HHGear^.State or gstHHChooseTarget;
       
  2436         DeleteGear(Gear);
       
  2437         isCursorVisible := true;
       
  2438         PlaySound(sndDenied)
       
  2439         end
       
  2440     else
       
  2441         begin
       
  2442         DeleteCI(HHGear);
       
  2443         SetAllHHToActive;
       
  2444         Gear^.doStep := @doStepTeleportAnim;
       
  2445 
       
  2446   // copy old HH position and direction to Gear (because we need them for drawing the vanishing hog)
       
  2447         Gear^.dX := HHGear^.dX;
       
  2448         // retrieve the cursor direction (it was previously copied to X so it doesn't get lost)
       
  2449         HHGear^.dX.isNegative := (Gear^.X.QWordValue <> 0);
       
  2450         Gear^.X := HHGear^.X;
       
  2451         Gear^.Y := HHGear^.Y;
       
  2452         HHGear^.X := int2hwFloat(Gear^.Target.X);
       
  2453         HHGear^.Y := int2hwFloat(Gear^.Target.Y);
       
  2454         HHGear^.State := HHGear^.State or gstMoving;
       
  2455         Gear^.Hedgehog^.Unplaced := false;
       
  2456         isCursorVisible := false;
       
  2457         playSound(sndWarp)
       
  2458         end;
       
  2459     Gear^.Target.X:= NoPointX
       
  2460 end;
       
  2461 
       
  2462 ////////////////////////////////////////////////////////////////////////////////
       
  2463 procedure doStepSwitcherWork(Gear: PGear);
       
  2464 var
       
  2465     HHGear: PGear;
       
  2466     hedgehog: PHedgehog;
       
  2467     State: Longword;
       
  2468 begin
       
  2469     AllInactive := false;
       
  2470 
       
  2471     if ((Gear^.Message and (not gmSwitch)) <> 0) or (TurnTimeLeft = 0) then
       
  2472         begin
       
  2473         hedgehog := Gear^.Hedgehog;
       
  2474         //Msg := Gear^.Message and (not gmSwitch);
       
  2475         DeleteGear(Gear);
       
  2476         ApplyAmmoChanges(hedgehog^);
       
  2477 
       
  2478         HHGear := CurrentHedgehog^.Gear;
       
  2479         ApplyAmmoChanges(HHGear^.Hedgehog^);
       
  2480         //HHGear^.Message := Msg;
       
  2481         exit
       
  2482         end;
       
  2483 
       
  2484     if (Gear^.Message and gmSwitch) <> 0 then
       
  2485         begin
       
  2486         HHGear := CurrentHedgehog^.Gear;
       
  2487         HHGear^.Message := HHGear^.Message and (not gmSwitch);
       
  2488         Gear^.Message := Gear^.Message and (not gmSwitch);
       
  2489         State := HHGear^.State;
       
  2490         HHGear^.State := 0;
       
  2491         HHGear^.Z := cHHZ;
       
  2492         HHGear^.Active := false;
       
  2493         HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList;
       
  2494 
       
  2495         PlaySound(sndSwitchHog);
       
  2496 
       
  2497         repeat
       
  2498             CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber);
       
  2499         until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and
       
  2500               (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0) and
       
  2501               (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen]=0);
       
  2502 
       
  2503         SwitchCurrentHedgehog(@CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]);
       
  2504         AmmoMenuInvalidated:= true;
       
  2505 
       
  2506         HHGear := CurrentHedgehog^.Gear;
       
  2507         HHGear^.State := State;
       
  2508         HHGear^.Active := true;
       
  2509         FollowGear := HHGear;
       
  2510         HHGear^.Z := cCurrHHZ;
       
  2511         HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList;
       
  2512         Gear^.X := HHGear^.X;
       
  2513         Gear^.Y := HHGear^.Y
       
  2514         end;
       
  2515 end;
       
  2516 
       
  2517 procedure doStepSwitcher(Gear: PGear);
       
  2518 var
       
  2519     HHGear: PGear;
       
  2520 begin
       
  2521     Gear^.doStep := @doStepSwitcherWork;
       
  2522 
       
  2523     HHGear := Gear^.Hedgehog^.Gear;
       
  2524     OnUsedAmmo(HHGear^.Hedgehog^);
       
  2525     with HHGear^ do
       
  2526         begin
       
  2527         State := State and (not gstAttacking);
       
  2528         Message := Message and (not gmAttack)
       
  2529         end
       
  2530 end;
       
  2531 
       
  2532 ////////////////////////////////////////////////////////////////////////////////
       
  2533 procedure doStepMortar(Gear: PGear);
       
  2534 var
       
  2535     dX, dY, gdX, gdY: hwFloat;
       
  2536     i: LongInt;
       
  2537 begin
       
  2538     AllInactive := false;
       
  2539     gdX := Gear^.dX;
       
  2540     gdY := Gear^.dY;
       
  2541 
       
  2542     doStepFallingGear(Gear);
       
  2543     if (Gear^.State and gstCollision) <> 0 then
       
  2544         begin
       
  2545         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);
       
  2546         gdX.isNegative := not gdX.isNegative;
       
  2547         gdY.isNegative := not gdY.isNegative;
       
  2548         gdX:= gdX*_0_2;
       
  2549         gdY:= gdY*_0_2;
       
  2550 
       
  2551         for i:= 0 to 4 do
       
  2552             begin
       
  2553             dX := gdX + rndSign(GetRandomf) * _0_03;
       
  2554             dY := gdY + rndSign(GetRandomf) * _0_03;
       
  2555             AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtCluster, 0, dX, dY, 25);
       
  2556             end;
       
  2557 
       
  2558         DeleteGear(Gear);
       
  2559         exit
       
  2560         end;
       
  2561 
       
  2562     if (GameTicks and $3F) = 0 then
       
  2563         begin
       
  2564         if hwRound(Gear^.Y) > cWaterLine then
       
  2565              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
  2566         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
  2567         end
       
  2568 end;
       
  2569 
       
  2570 ////////////////////////////////////////////////////////////////////////////////
       
  2571 procedure doStepKamikazeWork(Gear: PGear);
       
  2572 var
       
  2573     i: LongWord;
       
  2574     HHGear: PGear;
       
  2575     sparkles: PVisualGear;
       
  2576     hasWishes: boolean;
       
  2577 begin
       
  2578     AllInactive := false;
       
  2579     hasWishes:= ((Gear^.Message and (gmPrecise or gmSwitch)) = (gmPrecise or gmSwitch));
       
  2580     if hasWishes then
       
  2581         Gear^.AdvBounce:= 1;
       
  2582 
       
  2583     HHGear := Gear^.Hedgehog^.Gear;
       
  2584     if HHGear = nil then
       
  2585         begin
       
  2586         DeleteGear(Gear);
       
  2587         exit
       
  2588         end;
       
  2589 
       
  2590     HHGear^.State := HHGear^.State or gstNoDamage;
       
  2591     DeleteCI(HHGear);
       
  2592 
       
  2593     Gear^.X := HHGear^.X;
       
  2594     Gear^.Y := HHGear^.Y;
       
  2595     if (GameTicks mod 2 = 0) and hasWishes then
       
  2596         begin
       
  2597         sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1);
       
  2598         if sparkles <> nil then
       
  2599             begin
       
  2600             sparkles^.Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF;
       
  2601             sparkles^.Angle:= random(360);
       
  2602             end
       
  2603         end;
       
  2604 
       
  2605     i := 2;
       
  2606     repeat
       
  2607 
       
  2608         Gear^.X := Gear^.X + HHGear^.dX;
       
  2609         Gear^.Y := Gear^.Y + HHGear^.dY;
       
  2610         HHGear^.X := Gear^.X;
       
  2611         HHGear^.Y := Gear^.Y;
       
  2612 
       
  2613         inc(Gear^.Damage, 2);
       
  2614 
       
  2615         //  if TestCollisionXwithGear(HHGear, hwSign(Gear^.dX))
       
  2616         //      or TestCollisionYwithGear(HHGear, hwSign(Gear^.dY)) then inc(Gear^.Damage, 3);
       
  2617 
       
  2618         dec(i)
       
  2619     until (i = 0)
       
  2620     or (Gear^.Damage > Gear^.Health);
       
  2621 
       
  2622     inc(upd);
       
  2623     if upd > 3 then
       
  2624         begin
       
  2625         if Gear^.Health < 1500 then
       
  2626             begin
       
  2627             if Gear^.AdvBounce <> 0 then
       
  2628                 Gear^.Pos := 3
       
  2629             else
       
  2630                 Gear^.Pos := 2;
       
  2631             end;
       
  2632 
       
  2633         AmmoShove(Gear, 30, 40);
       
  2634 
       
  2635         DrawTunnel(HHGear^.X - HHGear^.dX * 10,
       
  2636                     HHGear^.Y - _2 - HHGear^.dY * 10 + hwAbs(HHGear^.dY) * 2,
       
  2637         HHGear^.dX,
       
  2638         HHGear^.dY,
       
  2639         20 + cHHRadius * 2,
       
  2640         cHHRadius * 2 + 7);
       
  2641 
       
  2642         upd := 0
       
  2643         end;
       
  2644 
       
  2645     if Gear^.Health < Gear^.Damage then
       
  2646         begin
       
  2647         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  2648         if hasWishes then
       
  2649             for i:= 0 to 31 do
       
  2650                 begin
       
  2651                 sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
       
  2652                 if sparkles <> nil then
       
  2653                     with sparkles^ do
       
  2654                         begin
       
  2655                         Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF;
       
  2656                         Angle:= random(360);
       
  2657                         dx:= 0.001 * (random(200));
       
  2658                         dy:= 0.001 * (random(200));
       
  2659                         if random(2) = 0 then
       
  2660                             dx := -dx;
       
  2661                         if random(2) = 0 then
       
  2662                             dy := -dy;
       
  2663                         FrameTicks:= random(400) + 250
       
  2664                         end
       
  2665                 end;
       
  2666         AfterAttack;
       
  2667         HHGear^.Message:= HHGear^.Message or gmDestroy;
       
  2668         DeleteGear(Gear);
       
  2669     end
       
  2670     else
       
  2671         begin
       
  2672         dec(Gear^.Health, Gear^.Damage);
       
  2673         Gear^.Damage := 0
       
  2674         end
       
  2675 end;
       
  2676 
       
  2677 procedure doStepKamikazeIdle(Gear: PGear);
       
  2678 begin
       
  2679     AllInactive := false;
       
  2680     dec(Gear^.Timer);
       
  2681     if Gear^.Timer = 0 then
       
  2682         begin
       
  2683         Gear^.Pos := 1;
       
  2684         PlaySoundV(sndKamikaze, Gear^.Hedgehog^.Team^.voicepack);
       
  2685         Gear^.doStep := @doStepKamikazeWork
       
  2686         end
       
  2687 end;
       
  2688 
       
  2689 procedure doStepKamikaze(Gear: PGear);
       
  2690 var
       
  2691     HHGear: PGear;
       
  2692 begin
       
  2693     AllInactive := false;
       
  2694 
       
  2695     HHGear := Gear^.Hedgehog^.Gear;
       
  2696 
       
  2697     HHGear^.dX := Gear^.dX;
       
  2698     HHGear^.dY := Gear^.dY;
       
  2699 
       
  2700     Gear^.dX := SignAs(_0_45, Gear^.dX);
       
  2701     Gear^.dY := - _0_9;
       
  2702 
       
  2703     Gear^.Timer := 550;
       
  2704 
       
  2705     Gear^.doStep := @doStepKamikazeIdle
       
  2706 end;
       
  2707 
       
  2708 ////////////////////////////////////////////////////////////////////////////////
       
  2709 
       
  2710 const cakeh =   27;
       
  2711 var
       
  2712     CakePoints: array[0..Pred(cakeh)] of record
       
  2713         x, y: hwFloat;
       
  2714     end;
       
  2715     CakeI: Longword;
       
  2716 
       
  2717 procedure doStepCakeExpl(Gear: PGear);
       
  2718 begin
       
  2719     AllInactive := false;
       
  2720 
       
  2721     inc(Gear^.Tag);
       
  2722     if Gear^.Tag < 2250 then
       
  2723         exit;
       
  2724 
       
  2725     doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound);
       
  2726     AfterAttack;
       
  2727     DeleteGear(Gear)
       
  2728 end;
       
  2729 
       
  2730 procedure doStepCakeDown(Gear: PGear);
       
  2731 var
       
  2732     gi: PGear;
       
  2733     dmg, dmgBase: LongInt;
       
  2734     fX, fY, tdX, tdY: hwFloat;
       
  2735 begin
       
  2736     AllInactive := false;
       
  2737 
       
  2738     inc(Gear^.Tag);
       
  2739     if Gear^.Tag < 100 then
       
  2740         exit;
       
  2741     Gear^.Tag := 0;
       
  2742 
       
  2743     if Gear^.Pos = 0 then
       
  2744         begin
       
  2745 ///////////// adapted from doMakeExplosion ///////////////////////////
       
  2746         //fX:= Gear^.X;
       
  2747         //fY:= Gear^.Y;
       
  2748         //fX.QWordValue:= fX.QWordValue and $FFFFFFFF00000000;
       
  2749         //fY.QWordValue:= fY.QWordValue and $FFFFFFFF00000000;
       
  2750         fX:= int2hwFloat(hwRound(Gear^.X));
       
  2751         fY:= int2hwFloat(hwRound(Gear^.Y));
       
  2752         dmgBase:= cakeDmg shl 1 + cHHRadius div 2;
       
  2753         gi := GearsList;
       
  2754         while gi <> nil do
       
  2755             begin
       
  2756             if gi^.Kind = gtHedgehog then
       
  2757                 begin
       
  2758                 dmg:= 0;
       
  2759                 tdX:= gi^.X-fX;
       
  2760                 tdY:= gi^.Y-fY;
       
  2761                 if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then
       
  2762                     dmg:= dmgBase - max(hwRound(Distance(tdX, tdY)),gi^.Radius);
       
  2763                 if (dmg > 1) then dmg:= ModifyDamage(min(dmg div 2, cakeDmg), gi);
       
  2764                 if (dmg > 1) then
       
  2765                     if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then
       
  2766                         gi^.State := gi^.State or gstLoser
       
  2767                     else
       
  2768                         gi^.State := gi^.State or gstWinner;
       
  2769                 end;
       
  2770             gi := gi^.NextGear
       
  2771             end;
       
  2772 //////////////////////////////////////////////////////////////////////
       
  2773         Gear^.doStep := @doStepCakeExpl;
       
  2774         PlaySound(sndCake)
       
  2775         end
       
  2776     else dec(Gear^.Pos)
       
  2777 end;
       
  2778 
       
  2779 
       
  2780 procedure doStepCakeWork(Gear: PGear);
       
  2781 var
       
  2782     tdx, tdy: hwFloat;
       
  2783 begin
       
  2784     AllInactive := false;
       
  2785 
       
  2786     inc(Gear^.Tag);
       
  2787     if Gear^.Tag < 7 then
       
  2788         exit;
       
  2789 
       
  2790     dec(Gear^.Health);
       
  2791     Gear^.Timer := Gear^.Health*10;
       
  2792     if Gear^.Health mod 100 = 0 then
       
  2793         Gear^.PortalCounter:= 0;
       
  2794     // This is not seconds, but at least it is *some* feedback
       
  2795     if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then
       
  2796         begin
       
  2797         FollowGear := Gear;
       
  2798         Gear^.RenderTimer := false;
       
  2799         Gear^.doStep := @doStepCakeDown;
       
  2800         exit
       
  2801         end;
       
  2802 
       
  2803     cakeStep(Gear);
       
  2804 
       
  2805     if Gear^.Tag = 0 then
       
  2806         begin
       
  2807         CakeI := (CakeI + 1) mod cakeh;
       
  2808         tdx := CakePoints[CakeI].x - Gear^.X;
       
  2809         tdy := - CakePoints[CakeI].y + Gear^.Y;
       
  2810         CakePoints[CakeI].x := Gear^.X;
       
  2811         CakePoints[CakeI].y := Gear^.Y;
       
  2812         Gear^.DirAngle := DxDy2Angle(tdx, tdy);
       
  2813         end;
       
  2814 end;
       
  2815 
       
  2816 procedure doStepCakeUp(Gear: PGear);
       
  2817 var
       
  2818     i: Longword;
       
  2819 begin
       
  2820     AllInactive := false;
       
  2821 
       
  2822     inc(Gear^.Tag);
       
  2823     if Gear^.Tag < 100 then
       
  2824         exit;
       
  2825     Gear^.Tag := 0;
       
  2826 
       
  2827     if Gear^.Pos = 6 then
       
  2828         begin
       
  2829         for i:= 0 to Pred(cakeh) do
       
  2830             begin
       
  2831             CakePoints[i].x := Gear^.X;
       
  2832             CakePoints[i].y := Gear^.Y
       
  2833             end;
       
  2834         CakeI := 0;
       
  2835         Gear^.doStep := @doStepCakeWork
       
  2836         end
       
  2837     else
       
  2838         inc(Gear^.Pos)
       
  2839 end;
       
  2840 
       
  2841 procedure doStepCakeFall(Gear: PGear);
       
  2842 begin
       
  2843     AllInactive := false;
       
  2844 
       
  2845     Gear^.dY := Gear^.dY + cGravity;
       
  2846     if TestCollisionYwithGear(Gear, 1) <> 0 then
       
  2847         Gear^.doStep := @doStepCakeUp
       
  2848     else
       
  2849         begin
       
  2850         Gear^.Y := Gear^.Y + Gear^.dY;
       
  2851         if CheckGearDrowning(Gear) then
       
  2852             AfterAttack
       
  2853         end
       
  2854 end;
       
  2855 
       
  2856 procedure doStepCake(Gear: PGear);
       
  2857 var
       
  2858     HHGear: PGear;
       
  2859 begin
       
  2860     AllInactive := false;
       
  2861 
       
  2862     HHGear := Gear^.Hedgehog^.Gear;
       
  2863     HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  2864     Gear^.CollisionMask:= lfNotCurrentMask;
       
  2865 
       
  2866     FollowGear := Gear;
       
  2867 
       
  2868     Gear^.doStep := @doStepCakeFall
       
  2869 end;
       
  2870 
       
  2871 ////////////////////////////////////////////////////////////////////////////////
       
  2872 procedure doStepSeductionWork(Gear: PGear);
       
  2873 var i: LongInt;
       
  2874     hogs: PGearArrayS;
       
  2875 begin
       
  2876     AllInactive := false;
       
  2877     hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius);
       
  2878     if hogs.size > 0 then
       
  2879         begin
       
  2880         for i:= 0 to hogs.size - 1 do
       
  2881             with hogs.ar^[i]^ do
       
  2882                 begin
       
  2883                 if hogs.ar^[i] <> CurrentHedgehog^.Gear then
       
  2884                     begin
       
  2885                     dX:= _50 * cGravity * (Gear^.X - X) / _25;
       
  2886                     dY:= -_450 * cGravity;
       
  2887                     Active:= true;
       
  2888                     end
       
  2889                 end;
       
  2890         end ;
       
  2891         AfterAttack;
       
  2892         DeleteGear(Gear);
       
  2893 (*
       
  2894     Gear^.X := Gear^.X + Gear^.dX;
       
  2895     Gear^.Y := Gear^.Y + Gear^.dY;
       
  2896     x := hwRound(Gear^.X);
       
  2897     y := hwRound(Gear^.Y);
       
  2898 
       
  2899     if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then
       
  2900         if (Land[y, x] <> 0) then
       
  2901             begin
       
  2902             Gear^.dX.isNegative := not Gear^.dX.isNegative;
       
  2903             Gear^.dY.isNegative := not Gear^.dY.isNegative;
       
  2904             Gear^.dX := Gear^.dX * _1_5;
       
  2905             Gear^.dY := Gear^.dY * _1_5 - _0_3;
       
  2906             AmmoShove(Gear, 0, 40);
       
  2907             AfterAttack;
       
  2908             DeleteGear(Gear)
       
  2909             end
       
  2910         else
       
  2911     else
       
  2912         begin
       
  2913         AfterAttack;
       
  2914         DeleteGear(Gear)
       
  2915         end*)
       
  2916 end;
       
  2917 
       
  2918 procedure doStepSeductionWear(Gear: PGear);
       
  2919 var heart: PVisualGear;
       
  2920 begin
       
  2921     AllInactive := false;
       
  2922     inc(Gear^.Timer);
       
  2923     if Gear^.Timer > 250 then
       
  2924         begin
       
  2925         Gear^.Timer := 0;
       
  2926         inc(Gear^.Pos);
       
  2927         if Gear^.Pos = 5 then
       
  2928             PlaySoundV(sndYoohoo, Gear^.Hedgehog^.Team^.voicepack)
       
  2929         end;
       
  2930 
       
  2931     if (Gear^.Pos = 14) and (RealTicks and $3 = 0) then
       
  2932         begin
       
  2933         heart:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
       
  2934         if heart <> nil then
       
  2935             with heart^ do
       
  2936                 begin
       
  2937                 dx:= 0.001 * (random(200));
       
  2938                 dy:= 0.001 * (random(200));
       
  2939                 if random(2) = 0 then
       
  2940                     dx := -dx;
       
  2941                 if random(2) = 0 then
       
  2942                     dy := -dy;
       
  2943                 FrameTicks:= random(750) + 1000;
       
  2944                 State:= ord(sprSeduction)
       
  2945                 end;
       
  2946         end;
       
  2947 
       
  2948     if Gear^.Pos = 15 then
       
  2949         Gear^.doStep := @doStepSeductionWork
       
  2950 end;
       
  2951 
       
  2952 procedure doStepSeduction(Gear: PGear);
       
  2953 begin
       
  2954     AllInactive := false;
       
  2955     //DeleteCI(Gear^.Hedgehog^.Gear);
       
  2956     Gear^.doStep := @doStepSeductionWear
       
  2957 end;
       
  2958 
       
  2959 ////////////////////////////////////////////////////////////////////////////////
       
  2960 procedure doStepWaterUp(Gear: PGear);
       
  2961 var
       
  2962     i: LongWord;
       
  2963 begin
       
  2964     if (Gear^.Tag = 0)
       
  2965     or (cWaterLine = 0) then
       
  2966         begin
       
  2967         DeleteGear(Gear);
       
  2968         exit
       
  2969         end;
       
  2970 
       
  2971     AllInactive := false;
       
  2972 
       
  2973     inc(Gear^.Timer);
       
  2974     if Gear^.Timer = 17 then
       
  2975         Gear^.Timer := 0
       
  2976     else
       
  2977         exit;
       
  2978 
       
  2979     if cWaterLine > 0 then
       
  2980         begin
       
  2981         dec(cWaterLine);
       
  2982         for i:= 0 to LAND_WIDTH - 1 do
       
  2983             Land[cWaterLine, i] := 0;
       
  2984         SetAllToActive
       
  2985         end;
       
  2986 
       
  2987     dec(Gear^.Tag);
       
  2988 end;
       
  2989 
       
  2990 ////////////////////////////////////////////////////////////////////////////////
       
  2991 procedure doStepDrillDrilling(Gear: PGear);
       
  2992 var
       
  2993     t: PGearArray;
       
  2994     tempColl: Word;
       
  2995 begin
       
  2996     AllInactive := false;
       
  2997     if (Gear^.Timer > 0) and (Gear^.Timer mod 10 <> 0) then
       
  2998         begin
       
  2999         dec(Gear^.Timer);
       
  3000         exit;
       
  3001         end;
       
  3002 
       
  3003     DrawTunnel(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 2, 6);
       
  3004     Gear^.X := Gear^.X + Gear^.dX;
       
  3005     Gear^.Y := Gear^.Y + Gear^.dY;
       
  3006     if (Gear^.Timer mod 30) = 0 then
       
  3007         AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust);
       
  3008     if (CheckGearDrowning(Gear)) then
       
  3009         begin
       
  3010         StopSoundChan(Gear^.SoundChannel);
       
  3011         exit
       
  3012     end;
       
  3013 
       
  3014     tempColl:= Gear^.CollisionMask;
       
  3015     Gear^.CollisionMask:= $007F;
       
  3016     if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) <> 0) or TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) or (GameTicks > Gear^.FlightTime) then
       
  3017         t := CheckGearsCollision(Gear)
       
  3018     else t := nil;
       
  3019     Gear^.CollisionMask:= tempColl;
       
  3020     //fixes drill not exploding when touching HH bug
       
  3021 
       
  3022     if (Gear^.Timer = 0) or ((t <> nil) and (t^.Count <> 0))
       
  3023     or ( ((Gear^.State and gsttmpFlag) = 0) and (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))))
       
  3024 // CheckLandValue returns true if the type isn't matched
       
  3025     or (not CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y), lfIndestructible)) then
       
  3026         begin
       
  3027         //out of time or exited ground
       
  3028         StopSoundChan(Gear^.SoundChannel);
       
  3029         if (Gear^.State and gsttmpFlag) <> 0 then
       
  3030             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
       
  3031         else
       
  3032             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  3033         DeleteGear(Gear);
       
  3034         exit
       
  3035         end
       
  3036 
       
  3037     else if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))) then
       
  3038         begin
       
  3039         StopSoundChan(Gear^.SoundChannel);
       
  3040         Gear^.Tag := 1;
       
  3041         Gear^.doStep := @doStepDrill
       
  3042         end;
       
  3043 
       
  3044     dec(Gear^.Timer);
       
  3045 end;
       
  3046 
       
  3047 procedure doStepDrill(Gear: PGear);
       
  3048 var
       
  3049     t: PGearArray;
       
  3050     oldDx, oldDy: hwFloat;
       
  3051     t2: hwFloat;
       
  3052 begin
       
  3053     AllInactive := false;
       
  3054 
       
  3055     if (Gear^.State and gsttmpFlag) = 0 then
       
  3056         Gear^.dX := Gear^.dX + cWindSpeed;
       
  3057 
       
  3058     oldDx := Gear^.dX;
       
  3059     oldDy := Gear^.dY;
       
  3060 
       
  3061     doStepFallingGear(Gear);
       
  3062 
       
  3063     if (GameTicks and $3F) = 0 then
       
  3064         begin
       
  3065         if hwRound(Gear^.Y) > cWaterLine then
       
  3066              AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble)
       
  3067         else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace)
       
  3068         end;
       
  3069 
       
  3070     if ((Gear^.State and gstCollision) <> 0) then
       
  3071         begin
       
  3072         //hit
       
  3073         Gear^.dX := oldDx;
       
  3074         Gear^.dY := oldDy;
       
  3075 
       
  3076         if GameTicks > Gear^.FlightTime then
       
  3077             t := CheckGearsCollision(Gear)
       
  3078         else
       
  3079             t := nil;
       
  3080         if (t = nil) or (t^.Count = 0) then
       
  3081             begin
       
  3082             //hit the ground not the HH
       
  3083             t2 := _0_5 / Distance(Gear^.dX, Gear^.dY);
       
  3084             Gear^.dX := Gear^.dX * t2;
       
  3085             Gear^.dY := Gear^.dY * t2;
       
  3086             end
       
  3087 
       
  3088         else if (t <> nil) then
       
  3089             begin
       
  3090             //explode right on contact with HH
       
  3091             if (Gear^.State and gsttmpFlag) <> 0 then
       
  3092                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
       
  3093             else
       
  3094                 doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
       
  3095             DeleteGear(Gear);
       
  3096             exit;
       
  3097             end;
       
  3098 
       
  3099         Gear^.SoundChannel := LoopSound(sndDrillRocket);
       
  3100         Gear^.doStep := @doStepDrillDrilling;
       
  3101 
       
  3102         if (Gear^.State and gsttmpFlag) <> 0 then
       
  3103             gear^.RenderTimer:= true;
       
  3104         if Gear^.Timer > 0 then dec(Gear^.Timer)
       
  3105         end
       
  3106     else if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Tag <> 0) then
       
  3107         begin
       
  3108         if Gear^.Timer > 0 then
       
  3109             dec(Gear^.Timer)
       
  3110         else
       
  3111             begin
       
  3112             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
       
  3113             DeleteGear(Gear);
       
  3114             end
       
  3115         end;
       
  3116 end;
       
  3117 
       
  3118 ////////////////////////////////////////////////////////////////////////////////
       
  3119 procedure doStepBallgunWork(Gear: PGear);
       
  3120 var
       
  3121     HHGear, ball: PGear;
       
  3122     rx, ry: hwFloat;
       
  3123     gX, gY: LongInt;
       
  3124 begin
       
  3125     AllInactive := false;
       
  3126     dec(Gear^.Timer);
       
  3127     HHGear := Gear^.Hedgehog^.Gear;
       
  3128     HedgehogChAngle(HHGear);
       
  3129     gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
       
  3130     gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
       
  3131     if (Gear^.Timer mod 100) = 0 then
       
  3132         begin
       
  3133         rx := rndSign(getRandomf * _0_1);
       
  3134         ry := rndSign(getRandomf * _0_1);
       
  3135 
       
  3136         ball:= AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0);
       
  3137         ball^.CollisionMask:= lfNotCurrentMask;
       
  3138 
       
  3139         PlaySound(sndGun);
       
  3140         end;
       
  3141 
       
  3142     if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then
       
  3143         begin
       
  3144         DeleteGear(Gear);
       
  3145         AfterAttack
       
  3146         end
       
  3147 end;
       
  3148 
       
  3149 procedure doStepBallgun(Gear: PGear);
       
  3150 var
       
  3151     HHGear: PGear;
       
  3152 begin
       
  3153     HHGear := Gear^.Hedgehog^.Gear;
       
  3154     HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown));
       
  3155     HHGear^.State := HHGear^.State or gstNotKickable;
       
  3156     Gear^.doStep := @doStepBallgunWork
       
  3157 end;
       
  3158 
       
  3159 ////////////////////////////////////////////////////////////////////////////////
       
  3160 procedure doStepRCPlaneWork(Gear: PGear);
       
  3161 
       
  3162 const cAngleSpeed =   3;
       
  3163 var
       
  3164     HHGear: PGear;
       
  3165     i: LongInt;
       
  3166     dX, dY: hwFloat;
       
  3167     fChanged: boolean;
       
  3168     trueAngle: Longword;
       
  3169     t: PGear;
       
  3170 begin
       
  3171     AllInactive := false;
       
  3172 
       
  3173     HHGear := Gear^.Hedgehog^.Gear;
       
  3174     FollowGear := Gear;
       
  3175 
       
  3176     if Gear^.Timer > 0 then
       
  3177         dec(Gear^.Timer);
       
  3178 
       
  3179     fChanged := false;
       
  3180     if ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then
       
  3181         begin
       
  3182         fChanged := true;
       
  3183         if Gear^.Angle > 2048 then
       
  3184             dec(Gear^.Angle)
       
  3185         else if Gear^.Angle < 2048 then
       
  3186             inc(Gear^.Angle)
       
  3187         else fChanged := false
       
  3188     end
       
  3189     else
       
  3190         begin
       
  3191         if ((Gear^.Message and gmLeft) <> 0) then
       
  3192             begin
       
  3193             fChanged := true;
       
  3194             Gear^.Angle := (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096
       
  3195             end;
       
  3196 
       
  3197         if ((Gear^.Message and gmRight) <> 0) then
       
  3198             begin
       
  3199             fChanged := true;
       
  3200             Gear^.Angle := (Gear^.Angle + cAngleSpeed) mod 4096
       
  3201             end
       
  3202         end;
       
  3203 
       
  3204     if fChanged then
       
  3205         begin
       
  3206         Gear^.dX.isNegative := (Gear^.Angle > 2048);
       
  3207         if Gear^.dX.isNegative then
       
  3208             trueAngle := 4096 - Gear^.Angle
       
  3209         else
       
  3210             trueAngle := Gear^.Angle;
       
  3211 
       
  3212         Gear^.dX := SignAs(AngleSin(trueAngle), Gear^.dX) * _0_25;
       
  3213         Gear^.dY := AngleCos(trueAngle) * -_0_25;
       
  3214         end;
       
  3215 
       
  3216     Gear^.X := Gear^.X + Gear^.dX;
       
  3217     Gear^.Y := Gear^.Y + Gear^.dY;
       
  3218 
       
  3219     if (GameTicks and $FF) = 0 then
       
  3220         if Gear^.Timer < 3500 then
       
  3221             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace)
       
  3222     else
       
  3223         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
       
  3224 
       
  3225     if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then
       
  3226         begin
       
  3227         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  3228         AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, Gear^.dX * _0_5, Gear^.dY *
       
  3229         _0_5, 0);
       
  3230         dec(Gear^.Health)
       
  3231         end;
       
  3232 
       
  3233     if ((HHGear^.Message and gmLJump) <> 0) and ((Gear^.State and gsttmpFlag) = 0) then
       
  3234         begin
       
  3235         Gear^.State := Gear^.State or gsttmpFlag;
       
  3236         PauseMusic;
       
  3237         playSound(sndRideOfTheValkyries);
       
  3238         end;
       
  3239 
       
  3240     // pickup bonuses
       
  3241     t := CheckGearNear(Gear, gtCase, 36, 36);
       
  3242     if t <> nil then
       
  3243         PickUp(HHGear, t);
       
  3244 
       
  3245     CheckCollision(Gear);
       
  3246 
       
  3247     if ((Gear^.State and gstCollision) <> 0) or CheckGearDrowning(Gear) then
       
  3248         begin
       
  3249         StopSoundChan(Gear^.SoundChannel);
       
  3250         StopSound(sndRideOfTheValkyries);
       
  3251         ResumeMusic;
       
  3252 
       
  3253         if ((Gear^.State and gstCollision) <> 0) then
       
  3254             begin
       
  3255             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound);
       
  3256             for i:= 0 to 15 do
       
  3257                 begin
       
  3258                 dX := AngleCos(i * 64) * _0_5 * (GetRandomf + _1);
       
  3259                 dY := AngleSin(i * 64) * _0_5 * (GetRandomf + _1);
       
  3260                 AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, dY, 0);
       
  3261                 AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, -dY, 0);
       
  3262                 end;
       
  3263             DeleteGear(Gear)
       
  3264             end;
       
  3265 
       
  3266         AfterAttack;
       
  3267         CurAmmoGear := nil;
       
  3268         if (GameFlags and gfInfAttack) = 0 then
       
  3269             begin
       
  3270             if TagTurnTimeLeft = 0 then
       
  3271                 TagTurnTimeLeft:= TurnTimeLeft;
       
  3272 
       
  3273             TurnTimeLeft:= 14 * 125;
       
  3274             end;
       
  3275 
       
  3276         HHGear^.Message := 0;
       
  3277         ParseCommand('/taunt ' + #1, true)
       
  3278         end
       
  3279 end;
       
  3280 
       
  3281 procedure doStepRCPlane(Gear: PGear);
       
  3282 var
       
  3283     HHGear: PGear;
       
  3284 begin
       
  3285     HHGear := Gear^.Hedgehog^.Gear;
       
  3286     HHGear^.Message := 0;
       
  3287     HHGear^.State := HHGear^.State or gstNotKickable;
       
  3288     Gear^.Angle := HHGear^.Angle;
       
  3289     Gear^.Tag := hwSign(HHGear^.dX);
       
  3290 
       
  3291     if HHGear^.dX.isNegative then
       
  3292         Gear^.Angle := 4096 - Gear^.Angle;
       
  3293     Gear^.doStep := @doStepRCPlaneWork
       
  3294 end;
       
  3295 
       
  3296 ////////////////////////////////////////////////////////////////////////////////
       
  3297 procedure doStepJetpackWork(Gear: PGear);
       
  3298 var
       
  3299     HHGear: PGear;
       
  3300     fuel, i: LongInt;
       
  3301     move: hwFloat;
       
  3302     isUnderwater: Boolean;
       
  3303     bubble: PVisualGear;
       
  3304 begin
       
  3305     isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius;
       
  3306     if Gear^.Pos > 0 then
       
  3307         dec(Gear^.Pos);
       
  3308     AllInactive := false;
       
  3309     HHGear := Gear^.Hedgehog^.Gear;
       
  3310     //dec(Gear^.Timer);
       
  3311     move := _0_2;
       
  3312     fuel := 50;
       
  3313 (*if (HHGear^.Message and gmPrecise) <> 0 then
       
  3314     begin
       
  3315     move:= _0_02;
       
  3316     fuel:= 5;
       
  3317     end;*)
       
  3318     if HHGear^.Message and gmPrecise <> 0 then
       
  3319         HedgehogChAngle(HHGear)
       
  3320     else if Gear^.Health > 0 then
       
  3321         begin
       
  3322         if HHGear^.Message and gmUp <> 0 then
       
  3323             begin
       
  3324             if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then
       
  3325                 begin
       
  3326                 if isUnderwater then
       
  3327                     begin
       
  3328                     HHGear^.dY := HHGear^.dY - (move * _0_7);
       
  3329                     for i:= random(10)+10 downto 0 do
       
  3330                         begin
       
  3331                         bubble := AddVisualGear(hwRound(HHGear^.X) - 8 + random(16), hwRound(HHGear^.Y) + 16 + random(8), vgtBubble);
       
  3332                         if bubble <> nil then
       
  3333                             bubble^.dY:= random(20)/10+0.1;
       
  3334                         end
       
  3335                     end
       
  3336                 else HHGear^.dY := HHGear^.dY - move;
       
  3337                 end;
       
  3338             dec(Gear^.Health, fuel);
       
  3339             Gear^.MsgParam := Gear^.MsgParam or gmUp;
       
  3340             Gear^.Timer := GameTicks
       
  3341             end;
       
  3342         move.isNegative := (HHGear^.Message and gmLeft) <> 0;
       
  3343         if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
       
  3344             begin
       
  3345             HHGear^.dX := HHGear^.dX + (move * _0_1);
       
  3346             if isUnderwater then
       
  3347                 begin
       
  3348                 for i:= random(5)+5 downto 0 do
       
  3349                     begin
       
  3350                     bubble := AddVisualGear(hwRound(HHGear^.X)+random(8), hwRound(HHGear^.Y) - 8 + random(16), vgtBubble);
       
  3351                     if bubble <> nil then
       
  3352                         begin
       
  3353                         bubble^.dX:= (random(10)/10 + 0.02) * -1;
       
  3354                         if (move.isNegative) then
       
  3355                             begin
       
  3356                             bubble^.X := bubble^.X + 28;
       
  3357                             bubble^.dX:= bubble^.dX * (-1)
       
  3358                             end
       
  3359                         else bubble^.X := bubble^.X - 28;
       
  3360                         end;
       
  3361                     end
       
  3362                 end;
       
  3363             dec(Gear^.Health, fuel div 5);
       
  3364             Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
       
  3365             Gear^.Timer := GameTicks
       
  3366             end
       
  3367         end;
       
  3368 
       
  3369     // erases them all at once :-/
       
  3370     if (Gear^.Timer <> 0) and (GameTicks - Gear^.Timer > 250) then
       
  3371         begin
       
  3372         Gear^.Timer := 0;
       
  3373         Gear^.MsgParam := 0
       
  3374         end;
       
  3375 
       
  3376     if Gear^.Health < 0 then
       
  3377         Gear^.Health := 0;
       
  3378 
       
  3379     i:= Gear^.Health div 20;
       
  3380 
       
  3381     if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  3382         begin
       
  3383         Gear^.Damage:= i;
       
  3384         //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate);
       
  3385         FreeTexture(Gear^.Tex);
       
  3386         Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + '%', cWhiteColor, fntSmall)
       
  3387         end;
       
  3388 
       
  3389     if (HHGear^.Message and (gmAttack or gmUp or gmLeft or gmRight) <> 0) and
       
  3390        (HHGear^.Message and gmPrecise = 0) then
       
  3391         Gear^.State := Gear^.State and (not gsttmpFlag);
       
  3392 
       
  3393     if HHGear^.Message and gmPrecise = 0 then
       
  3394         HHGear^.Message := HHGear^.Message and (not (gmUp or gmLeft or gmRight));
       
  3395     HHGear^.State := HHGear^.State or gstMoving;
       
  3396 
       
  3397     Gear^.X := HHGear^.X;
       
  3398     Gear^.Y := HHGear^.Y;
       
  3399 
       
  3400     if not isUnderWater and hasBorder and ((HHGear^.X < _0)
       
  3401     or (hwRound(HHGear^.X) > LAND_WIDTH)) then
       
  3402         HHGear^.dY.isNegative:= false;
       
  3403 
       
  3404     if ((Gear^.State and gsttmpFlag) = 0)
       
  3405     or (HHGear^.dY < _0) then
       
  3406         doStepHedgehogMoving(HHGear);
       
  3407 
       
  3408     if // (Gear^.Health = 0)
       
  3409         (HHGear^.Damage <> 0)
       
  3410         //or CheckGearDrowning(HHGear)
       
  3411         or (cWaterLine + cVisibleWater * 4 < hwRound(HHGear^.Y))
       
  3412         or (TurnTimeLeft = 0)
       
  3413         // allow brief ground touches - to be fair on this, might need another counter
       
  3414         or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0))
       
  3415         or ((Gear^.Message and gmAttack) <> 0) then
       
  3416             begin
       
  3417             with HHGear^ do
       
  3418                 begin
       
  3419                 Message := 0;
       
  3420                 Active := true;
       
  3421                 State := State or gstMoving
       
  3422                 end;
       
  3423             DeleteGear(Gear);
       
  3424             isCursorVisible := false;
       
  3425             ApplyAmmoChanges(HHGear^.Hedgehog^);
       
  3426         //    if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
       
  3427 
       
  3428 //    Gear^.Tex:= RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) + '%', cWhiteColor, fntSmall)
       
  3429 
       
  3430 //AddCaption(trmsg[sidFuel]+': '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate);
       
  3431             end
       
  3432 end;
       
  3433 
       
  3434 procedure doStepJetpack(Gear: PGear);
       
  3435 var
       
  3436     HHGear: PGear;
       
  3437 begin
       
  3438     Gear^.Pos:= 0;
       
  3439     Gear^.doStep := @doStepJetpackWork;
       
  3440 
       
  3441     HHGear := Gear^.Hedgehog^.Gear;
       
  3442     FollowGear := HHGear;
       
  3443     AfterAttack;
       
  3444     with HHGear^ do
       
  3445         begin
       
  3446         State := State and (not gstAttacking);
       
  3447         Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight));
       
  3448 
       
  3449         if (dY < _0_1) and (dY > -_0_1) then
       
  3450             begin
       
  3451             Gear^.State := Gear^.State or gsttmpFlag;
       
  3452             dY := dY - _0_2
       
  3453             end
       
  3454         end
       
  3455 end;
       
  3456 
       
  3457 ////////////////////////////////////////////////////////////////////////////////
       
  3458 procedure doStepBirdyDisappear(Gear: PGear);
       
  3459 begin
       
  3460     AllInactive := false;
       
  3461     Gear^.Pos := 0;
       
  3462     if Gear^.Timer < 2000 then
       
  3463         inc(Gear^.Timer, 1)
       
  3464     else
       
  3465         begin
       
  3466         DeleteGear(Gear);
       
  3467         end;
       
  3468 end;
       
  3469 
       
  3470 procedure doStepBirdyFly(Gear: PGear);
       
  3471 var
       
  3472     HHGear: PGear;
       
  3473     fuel, i: LongInt;
       
  3474     move: hwFloat;
       
  3475 begin
       
  3476     HHGear := Gear^.Hedgehog^.Gear;
       
  3477     if HHGear = nil then
       
  3478         begin
       
  3479         DeleteGear(Gear);
       
  3480         exit
       
  3481         end;
       
  3482 
       
  3483     move := _0_2;
       
  3484     fuel := 50;
       
  3485 
       
  3486     if Gear^.Pos > 0 then
       
  3487         dec(Gear^.Pos, 1)
       
  3488     else if (HHGear^.Message and (gmLeft or gmRight or gmUp)) <> 0 then
       
  3489             Gear^.Pos := 500;
       
  3490 
       
  3491     if HHGear^.dX.isNegative then
       
  3492         Gear^.Tag := -1
       
  3493     else
       
  3494         Gear^.Tag := 1;
       
  3495 
       
  3496     if (HHGear^.Message and gmUp) <> 0 then
       
  3497         begin
       
  3498         if (not HHGear^.dY.isNegative)
       
  3499         or (HHGear^.Y > -_256) then
       
  3500             HHGear^.dY := HHGear^.dY - move;
       
  3501 
       
  3502         dec(Gear^.Health, fuel);
       
  3503         Gear^.MsgParam := Gear^.MsgParam or gmUp;
       
  3504         end;
       
  3505 
       
  3506     if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true;
       
  3507     if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
       
  3508         begin
       
  3509         HHGear^.dX := HHGear^.dX + (move * _0_1);
       
  3510         dec(Gear^.Health, fuel div 5);
       
  3511         Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
       
  3512         end;
       
  3513 
       
  3514     if Gear^.Health < 0 then
       
  3515         Gear^.Health := 0;
       
  3516 
       
  3517     if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then
       
  3518         for i:= ((500-Gear^.Health) div 250) downto 0 do
       
  3519             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather);
       
  3520 
       
  3521     if (HHGear^.Message and gmAttack <> 0) then
       
  3522         begin
       
  3523         HHGear^.Message := HHGear^.Message and (not gmAttack);
       
  3524         if Gear^.FlightTime > 0 then
       
  3525             begin
       
  3526             AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0);
       
  3527             PlaySound(sndBirdyLay);
       
  3528             dec(Gear^.FlightTime)
       
  3529             end;
       
  3530         end;
       
  3531 
       
  3532     if HHGear^.Message and (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then
       
  3533         Gear^.State := Gear^.State and (not gsttmpFlag);
       
  3534 
       
  3535     HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight));
       
  3536     HHGear^.State := HHGear^.State or gstMoving;
       
  3537 
       
  3538     Gear^.X := HHGear^.X;
       
  3539     Gear^.Y := HHGear^.Y - int2hwFloat(32);
       
  3540     // For some reason I need to reapply followgear here, something else grabs it otherwise.
       
  3541     // this is probably not needed anymore
       
  3542     if not CurrentTeam^.ExtDriven then FollowGear := HHGear;
       
  3543 
       
  3544     if ((Gear^.State and gsttmpFlag) = 0)
       
  3545     or (HHGear^.dY < _0) then
       
  3546         doStepHedgehogMoving(HHGear);
       
  3547 
       
  3548     if  (Gear^.Health = 0)
       
  3549         or (HHGear^.Damage <> 0)
       
  3550         or CheckGearDrowning(HHGear)
       
  3551         or (TurnTimeLeft = 0)
       
  3552         // allow brief ground touches - to be fair on this, might need another counter
       
  3553         or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0))
       
  3554         or ((Gear^.Message and gmAttack) <> 0) then
       
  3555             begin
       
  3556             with HHGear^ do
       
  3557                 begin
       
  3558                 Message := 0;
       
  3559                 Active := true;
       
  3560                 State := State or gstMoving
       
  3561                 end;
       
  3562             Gear^.State := Gear^.State or gstAnimation or gstTmpFlag;
       
  3563             if HHGear^.dY < _0 then
       
  3564                 begin
       
  3565                 Gear^.dX := HHGear^.dX;
       
  3566                 Gear^.dY := HHGear^.dY;
       
  3567                 end;
       
  3568             Gear^.Timer := 0;
       
  3569             Gear^.doStep := @doStepBirdyDisappear;
       
  3570             CurAmmoGear := nil;
       
  3571             isCursorVisible := false;
       
  3572             AfterAttack;
       
  3573             end
       
  3574 end;
       
  3575 
       
  3576 procedure doStepBirdyDescend(Gear: PGear);
       
  3577 var
       
  3578     HHGear: PGear;
       
  3579 begin
       
  3580     if Gear^.Timer > 0 then
       
  3581         dec(Gear^.Timer, 1)
       
  3582     else if Gear^.Hedgehog^.Gear = nil then
       
  3583         begin
       
  3584         DeleteGear(Gear);
       
  3585         AfterAttack;
       
  3586         exit
       
  3587         end;
       
  3588     HHGear := Gear^.Hedgehog^.Gear;
       
  3589     HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight));
       
  3590     if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then
       
  3591         begin
       
  3592         if Gear^.Timer = 0 then
       
  3593             Gear^.Y := Gear^.Y + _0_1
       
  3594         end
       
  3595     else if Gear^.Timer = 0 then
       
  3596         begin
       
  3597         Gear^.doStep := @doStepBirdyFly;
       
  3598         HHGear^.dY := -_0_2
       
  3599         end
       
  3600 end;
       
  3601 
       
  3602 procedure doStepBirdyAppear(Gear: PGear);
       
  3603 begin
       
  3604     Gear^.Pos := 0;
       
  3605     if Gear^.Timer < 2000 then
       
  3606         inc(Gear^.Timer, 1)
       
  3607     else
       
  3608         begin
       
  3609         Gear^.Timer := 500;
       
  3610         Gear^.dX := _0;
       
  3611         Gear^.dY := _0;
       
  3612         Gear^.State :=  Gear^.State and (not gstAnimation);
       
  3613         Gear^.doStep := @doStepBirdyDescend;
       
  3614         end
       
  3615 end;
       
  3616 
       
  3617 procedure doStepBirdy(Gear: PGear);
       
  3618 var
       
  3619     HHGear: PGear;
       
  3620 begin
       
  3621     gear^.State :=  gear^.State or gstAnimation and (not gstTmpFlag);
       
  3622     Gear^.doStep := @doStepBirdyAppear;
       
  3623 
       
  3624     if CurrentHedgehog = nil then
       
  3625         begin
       
  3626         DeleteGear(Gear);
       
  3627         exit
       
  3628         end;
       
  3629 
       
  3630     HHGear := CurrentHedgehog^.Gear;
       
  3631 
       
  3632     if HHGear^.dX.isNegative then
       
  3633         Gear^.Tag := -1
       
  3634     else
       
  3635         Gear^.Tag := 1;
       
  3636     Gear^.Pos := 0;
       
  3637     AllInactive := false;
       
  3638     FollowGear := HHGear;
       
  3639     with HHGear^ do
       
  3640         begin
       
  3641         State := State and (not gstAttacking);
       
  3642         Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight))
       
  3643         end
       
  3644 end;
       
  3645 
       
  3646 ////////////////////////////////////////////////////////////////////////////////
       
  3647 procedure doStepEggWork(Gear: PGear);
       
  3648 var
       
  3649     vg: PVisualGear;
       
  3650     i: LongInt;
       
  3651 begin
       
  3652     AllInactive := false;
       
  3653     Gear^.dX := Gear^.dX;
       
  3654     doStepFallingGear(Gear);
       
  3655     //    CheckGearDrowning(Gear); // already checked for in doStepFallingGear
       
  3656     CalcRotationDirAngle(Gear);
       
  3657 
       
  3658     if (Gear^.State and gstCollision) <> 0 then
       
  3659         begin
       
  3660         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0);
       
  3661         PlaySound(sndEggBreak);
       
  3662         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
       
  3663         vg := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
       
  3664         if vg <> nil then
       
  3665             vg^.Frame := 2;
       
  3666 
       
  3667         for i:= 10 downto 0 do
       
  3668             begin
       
  3669             vg := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), hwRound(Gear^.Y) - 3 + Random(6),
       
  3670                   vgtDust);
       
  3671             if vg <> nil then
       
  3672                 vg^.dX := vg^.dX + (Gear^.dX.QWordValue / 21474836480);
       
  3673             end;
       
  3674 
       
  3675         DeleteGear(Gear);
       
  3676         exit
       
  3677         end;
       
  3678 end;
       
  3679 
       
  3680 ////////////////////////////////////////////////////////////////////////////////
       
  3681 procedure doPortalColorSwitch();
       
  3682 var CurWeapon: PAmmo;
       
  3683 begin
       
  3684     if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.Message and gmSwitch) <> 0) then
       
  3685             with CurrentHedgehog^ do
       
  3686                 if (CurAmmoType = amPortalGun) then
       
  3687                     begin
       
  3688                     CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSwitch);
       
  3689 
       
  3690                     CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^);
       
  3691                     if CurWeapon^.Pos <> 0 then
       
  3692                         CurWeapon^.Pos := 0
       
  3693 
       
  3694                     else
       
  3695                     CurWeapon^.Pos := 1;
       
  3696                     end;
       
  3697 end;
       
  3698 
       
  3699 procedure doStepPortal(Gear: PGear);
       
  3700 var
       
  3701     iterator, conPortal: PGear;
       
  3702     s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed,
       
  3703     resetx, resety, resetdx, resetdy: hwFloat;
       
  3704     sx, sy, rh, resetr: LongInt;
       
  3705     hasdxy, isbullet, iscake, isCollision: Boolean;
       
  3706 begin
       
  3707     doPortalColorSwitch();
       
  3708 
       
  3709     // destroy portal if ground it was attached too is gone
       
  3710     if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask)
       
  3711     or (Gear^.Timer < 1)
       
  3712     or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team)
       
  3713     or (hwRound(Gear^.Y) > cWaterLine) then
       
  3714         begin
       
  3715         deleteGear(Gear);
       
  3716         EXIT;
       
  3717         end;
       
  3718 
       
  3719     if (TurnTimeLeft < 1)
       
  3720     or (Gear^.Health < 1) then
       
  3721         dec(Gear^.Timer);
       
  3722 
       
  3723     if Gear^.Timer < 10000 then
       
  3724         gear^.RenderTimer := true;
       
  3725 
       
  3726     // abort if there is no other portal connected to this one
       
  3727     if (Gear^.LinkedGear = nil) then
       
  3728         exit;
       
  3729     if ((Gear^.LinkedGear^.Tag and 1) = 0) then // or if it's still moving;
       
  3730         exit;
       
  3731 
       
  3732     conPortal := Gear^.LinkedGear;
       
  3733 
       
  3734     // check all gears for stuff to port through
       
  3735     iterator := nil;
       
  3736     while true do
       
  3737     begin
       
  3738 
       
  3739         // iterate through GearsList
       
  3740         if iterator = nil then
       
  3741             iterator := GearsList
       
  3742         else
       
  3743             iterator := iterator^.NextGear;
       
  3744 
       
  3745         // end of list?
       
  3746         if iterator = nil then
       
  3747             break;
       
  3748 
       
  3749         // don't port portals or other gear that wouldn't make sense
       
  3750         if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack, gtIceGun])
       
  3751         or (iterator^.PortalCounter > 32) then
       
  3752             continue;
       
  3753 
       
  3754         // don't port hogs on rope
       
  3755         // TODO: this will also prevent hogs while falling after rope use from
       
  3756         //       falling through portals... fix that!
       
  3757 
       
  3758         // check if gear fits through portal
       
  3759         if (iterator^.Radius > Gear^.Radius) then
       
  3760             continue;
       
  3761 
       
  3762         // this is the max range we accept incoming gears in
       
  3763         r := Int2hwFloat(iterator^.Radius+Gear^.Radius);
       
  3764 
       
  3765         // too far away?
       
  3766         if (iterator^.X < Gear^.X - r)
       
  3767         or (iterator^.X > Gear^.X + r)
       
  3768         or (iterator^.Y < Gear^.Y - r)
       
  3769         or (iterator^.Y > Gear^.Y + r) then
       
  3770             continue;
       
  3771 
       
  3772         hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0)) or ((iterator^.State or gstMoving) = 0));
       
  3773 
       
  3774         // in case the object is not moving, let's asume it's falling towards the portal
       
  3775         if not hasdxy then
       
  3776             begin
       
  3777             if Gear^.Y < iterator^.Y then
       
  3778                 continue;
       
  3779             ox:= Gear^.X - iterator^.X;
       
  3780             oy:= Gear^.Y - iterator^.Y;
       
  3781             end
       
  3782         else
       
  3783             begin
       
  3784             ox:= iterator^.dX;
       
  3785             oy:= iterator^.dY;
       
  3786             end;
       
  3787 
       
  3788         // cake will need extra treatment... it's so delicious and moist!
       
  3789         iscake:= (iterator^.Kind = gtCake);
       
  3790 
       
  3791         // won't port stuff that does not move towards the front/portal entrance
       
  3792         if iscake then
       
  3793             begin
       
  3794             if not (((iterator^.X - Gear^.X)*ox + (iterator^.Y - Gear^.Y)*oy).isNegative) then
       
  3795                 continue;
       
  3796             end
       
  3797         else
       
  3798             if not ((Gear^.dX*ox + Gear^.dY*oy).isNegative) then
       
  3799                 continue;
       
  3800 
       
  3801         isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]);
       
  3802 
       
  3803         r:= int2hwFloat(iterator^.Radius);
       
  3804 
       
  3805         if not (isbullet or iscake) then
       
  3806             begin
       
  3807             // wow! good candidate there, let's see if the distance and direction is okay!
       
  3808             if hasdxy then
       
  3809                 begin
       
  3810                 s := Distance(iterator^.dX, iterator^.dY);
       
  3811                 // if the resulting distance is 0 skip this gear
       
  3812                 if s.QWordValue = 0 then
       
  3813                     continue;
       
  3814                 s := r / s;
       
  3815                 ox:= iterator^.X + s * iterator^.dX;
       
  3816                 oy:= iterator^.Y + s * iterator^.dY;
       
  3817                 end
       
  3818             else
       
  3819                 begin
       
  3820                 ox:= iterator^.X;
       
  3821                 oy:= iterator^.Y + r;
       
  3822                 end;
       
  3823 
       
  3824             if (hwRound(Distance(Gear^.X-ox,Gear^.Y-oy)) > Gear^.Radius + 1 ) then
       
  3825                 continue;
       
  3826             end;
       
  3827 
       
  3828         // draw bullet trail
       
  3829         if isbullet then
       
  3830             spawnBulletTrail(iterator);
       
  3831 
       
  3832         // calc gear offset in portal vector direction
       
  3833         ox := (iterator^.X - Gear^.X);
       
  3834         oy := (iterator^.Y - Gear^.Y);
       
  3835         poffs:= (Gear^.dX * ox + Gear^.dY * oy);
       
  3836 
       
  3837         if not isBullet and poffs.isNegative then
       
  3838             continue;
       
  3839 
       
  3840         // only port bullets close to the portal
       
  3841         if isBullet and (not (hwAbs(poffs) < _3)) then
       
  3842             continue;
       
  3843 
       
  3844         //
       
  3845         // gears that make it till here will definately be ported
       
  3846         //
       
  3847         // (but old position/movement vector might be restored in case there's
       
  3848         // not enough space on the other side)
       
  3849         //
       
  3850 
       
  3851         resetr  := iterator^.Radius;
       
  3852         resetx  := iterator^.X;
       
  3853         resety  := iterator^.Y;
       
  3854         resetdx := iterator^.dX;
       
  3855         resetdy := iterator^.dY;
       
  3856 
       
  3857         // create a normal of the portal vector, but ...
       
  3858         nx := Gear^.dY;
       
  3859         ny := Gear^.dX;
       
  3860         // ... decide where the top is based on the hog's direction when firing the portal
       
  3861         if Gear^.Elasticity.isNegative then
       
  3862             nx.isNegative := (not nx.isNegative)
       
  3863         else
       
  3864             ny.isNegative := not ny.isNegative;
       
  3865 
       
  3866         // calc gear offset in portal normal vector direction
       
  3867         noffs:= (nx * ox + ny * oy);
       
  3868 
       
  3869         if isBullet and (noffs.Round >= Longword(Gear^.Radius)) then
       
  3870             continue;
       
  3871 
       
  3872         // avoid gravity related loops of not really moving gear
       
  3873         if not (iscake or isbullet)
       
  3874         and (Gear^.dY.isNegative)
       
  3875         and (conPortal^.dY.isNegative)
       
  3876         and ((iterator^.dX.QWordValue + iterator^.dY.QWordValue) < _0_08.QWordValue)
       
  3877         and (iterator^.PortalCounter > 0) then
       
  3878             continue;
       
  3879 
       
  3880         // calc gear speed along to the vector and the normal vector of the portal
       
  3881         if hasdxy then
       
  3882             begin
       
  3883             pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY);
       
  3884             nspeed:= (nx * iterator^.dX + ny * iterator^.dY);
       
  3885             end
       
  3886         else
       
  3887             begin
       
  3888             pspeed:= hwAbs(cGravity * oy);
       
  3889             nspeed:= _0;
       
  3890             end;
       
  3891 
       
  3892         // creating normal vector of connected (exit) portal
       
  3893         nx := conPortal^.dY;
       
  3894         ny := conPortal^.dX;
       
  3895         if conPortal^.Elasticity.isNegative then
       
  3896             nx.isNegative := (not nx.isNegative)
       
  3897         else
       
  3898             ny.isNegative := not ny.isNegative;
       
  3899 
       
  3900         // inverse cake's normal movement direction,
       
  3901         // as if it just walked through a hole
       
  3902         //if iscake then nspeed.isNegative:= not nspeed.isNegative;
       
  3903 
       
  3904 //AddFileLog('poffs:'+cstr(poffs)+' noffs:'+cstr(noffs)+' pspeed:'+cstr(pspeed)+' nspeed:'+cstr(nspeed));
       
  3905         iterator^.dX := -pspeed * conPortal^.dX + nspeed * nx;
       
  3906         iterator^.dY := -pspeed * conPortal^.dY + nspeed * ny;
       
  3907 
       
  3908         // make the gear's exit position close to the portal while
       
  3909         // still respecting the movement direction
       
  3910 
       
  3911         // determine the distance (in exit vector direction)
       
  3912         // that we want the gear at
       
  3913         if iscake then
       
  3914             ox:= (r - _0_7)
       
  3915         else
       
  3916             ox:= (r * _1_5);
       
  3917         s:= ox / poffs;
       
  3918         poffs:= ox;
       
  3919         if (nspeed.QWordValue <> 0)
       
  3920         and (pspeed > _0) then
       
  3921             noffs:= noffs * s * (nspeed / pspeed);
       
  3922 
       
  3923         // move stuff with high normal offset closer to the portal's center
       
  3924         if not isbullet then
       
  3925             begin
       
  3926             s := hwAbs(noffs) + r - int2hwFloat(Gear^.Radius);
       
  3927             if s > _0 then
       
  3928                 noffs:= noffs - SignAs(s,noffs)
       
  3929             end;
       
  3930 
       
  3931         iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx;
       
  3932         iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny;
       
  3933 
       
  3934         if not hasdxy and (not (conPortal^.dY.isNegative)) then
       
  3935             begin
       
  3936             iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y))
       
  3937             end;
       
  3938 
       
  3939         // see if the space on the exit side actually is enough
       
  3940 
       
  3941         if not (isBullet or isCake) then
       
  3942             begin
       
  3943             // TestCollisionXwithXYShift requires a hwFloat for xShift
       
  3944             ox.QWordValue := _1.QWordValue;
       
  3945             ox.isNegative := not iterator^.dX.isNegative;
       
  3946 
       
  3947             sx := hwSign(iterator^.dX);
       
  3948             sy := hwSign(iterator^.dY);
       
  3949 
       
  3950             if iterator^.Radius > 1 then
       
  3951                 iterator^.Radius := iterator^.Radius - 1;
       
  3952 
       
  3953             // check front
       
  3954             isCollision := TestCollisionY(iterator, sy)
       
  3955                         or TestCollisionX(iterator, sx);
       
  3956 
       
  3957             if not isCollision then
       
  3958                 begin
       
  3959                 // check center area (with half the radius so that the
       
  3960                 // the square check won't check more pixels than we want to)
       
  3961                 iterator^.Radius := 1 + resetr div 2;
       
  3962                 rh := resetr div 4;
       
  3963                 isCollision := TestCollisionYwithXYShift(iterator,       0, -sy * rh, sy, false)
       
  3964                             or TestCollisionXwithXYShift(iterator, ox * rh,        0, sx, false);
       
  3965                 end;
       
  3966 
       
  3967             iterator^.Radius := resetr;
       
  3968 
       
  3969             if isCollision then
       
  3970                 begin
       
  3971                 // collision! oh crap! go back!
       
  3972                 iterator^.X  := resetx;
       
  3973                 iterator^.Y  := resety;
       
  3974                 iterator^.dX := resetdx;
       
  3975                 iterator^.dY := resetdy;
       
  3976                 continue;
       
  3977                 end;
       
  3978             end;
       
  3979 
       
  3980         //
       
  3981         // You're now officially portaled!
       
  3982         //
       
  3983 
       
  3984         // Until loops are reliably broken
       
  3985         if iscake then
       
  3986             iterator^.PortalCounter:= 33
       
  3987         else
       
  3988             begin
       
  3989             inc(iterator^.PortalCounter);
       
  3990             iterator^.Active:= true;
       
  3991             iterator^.State:= iterator^.State and (not gstHHHJump) or gstMoving;
       
  3992             end;
       
  3993 
       
  3994         // is it worth adding an arcsin table?  Just how often would we end up doing something like this?
       
  3995         // SYNCED ANGLE UPDATE
       
  3996         if iterator^.Kind = gtRCPlane then
       
  3997             begin
       
  3998             // recycling as temp vars
       
  3999             resety.isNegative:= false;
       
  4000             resety.QWordValue:= 4294967296 * 112;
       
  4001             resetx.isNegative:= false;
       
  4002             resetx.QWordValue:= 4294967296 * 35;
       
  4003             resetdx.isNegative:= false;
       
  4004             resetdx.QWordValue:= 4294967296 * 1152;
       
  4005 
       
  4006             resetdy:=hwAbs(iterator^.dX*4);
       
  4007             resetdy:= resetdy + hwPow(resetdy,3)/_6 + _3 * hwPow(resetdy,5) / _40 + _5 * hwPow(resetdy,7) / resety + resetx * hwPow(resetdy,9) / resetdx;
       
  4008             iterator^.Angle:= hwRound(resetdy*_2048 / _PI);
       
  4009             if not iterator^.dY.isNegative then iterator^.Angle:= 2048-iterator^.Angle;
       
  4010             if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle;
       
  4011             end
       
  4012         // VISUAL USE OF ANGLE ONLY
       
  4013         else if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtKamikaze) and (CurAmmoGear^.Hedgehog = iterator^.Hedgehog) then
       
  4014             begin
       
  4015             iterator^.Angle:= DxDy2AttackAngle(iterator^.dX, iterator^.dY);
       
  4016             iterator^.Angle:= 2048-iterator^.Angle;
       
  4017             if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle;
       
  4018             end;
       
  4019 
       
  4020         if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil)
       
  4021         and (iterator = CurrentHedgehog^.Gear)
       
  4022         and (CurAmmoGear <> nil)
       
  4023         and (CurAmmoGear^.Kind =gtRope) then
       
  4024                CurAmmoGear^.PortalCounter:= 1;
       
  4025 
       
  4026         if not isbullet and (iterator^.State and gstInvisible = 0)
       
  4027         and (iterator^.Kind <> gtFlake) then
       
  4028             FollowGear := iterator;
       
  4029 
       
  4030         // store X/Y values of exit for net bullet trail
       
  4031         if isbullet then
       
  4032             begin
       
  4033             iterator^.Elasticity:= iterator^.X;
       
  4034             iterator^.Friction  := iterator^.Y;
       
  4035             end;
       
  4036 
       
  4037         if Gear^.Health > 1 then
       
  4038             dec(Gear^.Health);
       
  4039     end;
       
  4040 end;
       
  4041 
       
  4042 
       
  4043 
       
  4044 procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean);
       
  4045 var
       
  4046     CurWeapon: PAmmo;
       
  4047 begin
       
  4048     if CurrentHedgehog <> nil then
       
  4049         with CurrentHedgehog^ do
       
  4050             begin
       
  4051             CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^);
       
  4052             if (CurAmmoType = amPortalGun) then
       
  4053                 begin
       
  4054                 if not destroyGear then
       
  4055                     begin
       
  4056                     // switch color of ball to opposite of oldPortal
       
  4057                     if (oldPortal^.Tag and 2) = 0 then
       
  4058                         CurWeapon^.Pos:= 1
       
  4059                     else
       
  4060                         CurWeapon^.Pos:= 0;
       
  4061                     end;
       
  4062 
       
  4063                 // make the ball visible
       
  4064                 CurWeapon^.Timer := 0;
       
  4065                 end
       
  4066             end;
       
  4067     if destroyGear then
       
  4068         oldPortal^.Timer:= 0;
       
  4069 end;
       
  4070 
       
  4071 procedure doStepMovingPortal_real(Gear: PGear);
       
  4072 var
       
  4073     x, y, tx, ty: LongInt;
       
  4074     s: hwFloat;
       
  4075 begin
       
  4076     x := hwRound(Gear^.X);
       
  4077     y := hwRound(Gear^.Y);
       
  4078     tx := 0;
       
  4079     ty := 0;
       
  4080     // avoid compiler hints
       
  4081 
       
  4082     if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then
       
  4083         begin
       
  4084         Gear^.State := Gear^.State or gstCollision;
       
  4085         Gear^.State := Gear^.State and (not gstMoving);
       
  4086 
       
  4087         if (Land[y, x] and lfBouncy <> 0)
       
  4088         or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255))
       
  4089         or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain
       
  4090             begin
       
  4091             loadNewPortalBall(Gear, true);
       
  4092             EXIT;
       
  4093             end;
       
  4094 
       
  4095         // making a normalized normal vector
       
  4096         s := _1/DistanceI(tx,ty);
       
  4097         Gear^.dX :=  s * ty;
       
  4098         Gear^.dY := -s * tx;
       
  4099 
       
  4100         Gear^.DirAngle := DxDy2Angle(-Gear^.dY,Gear^.dX);
       
  4101         if not Gear^.dX.isNegative then
       
  4102             Gear^.DirAngle := 180-Gear^.DirAngle;
       
  4103 
       
  4104         if ((Gear^.LinkedGear = nil)
       
  4105         or (hwRound(Distance(Gear^.X - Gear^.LinkedGear^.X,Gear^.Y-Gear^.LinkedGear^.Y)) >=Gear^.Radius*2)) then
       
  4106             begin
       
  4107             loadNewPortalBall(Gear, false);
       
  4108             inc(Gear^.Tag);
       
  4109             Gear^.doStep := @doStepPortal;
       
  4110         end
       
  4111         else
       
  4112             loadNewPortalBall(Gear, true);
       
  4113     end
       
  4114 
       
  4115     else if (y > cWaterLine)
       
  4116     or (y < -max(LAND_WIDTH,4096))
       
  4117     or (x > 2*max(LAND_WIDTH,4096))
       
  4118     or (x < -max(LAND_WIDTH,4096)) then
       
  4119         loadNewPortalBall(Gear, true);
       
  4120 end;
       
  4121 
       
  4122 procedure doStepMovingPortal(Gear: PGear);
       
  4123 begin
       
  4124     doPortalColorSwitch();
       
  4125     doStepPerPixel(Gear, @doStepMovingPortal_real, true);
       
  4126     if (Gear^.Timer < 1)
       
  4127     or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) then
       
  4128         deleteGear(Gear);
       
  4129 end;
       
  4130 
       
  4131 procedure doStepPortalShot(newPortal: PGear);
       
  4132 var
       
  4133     iterator: PGear;
       
  4134     s: hwFloat;
       
  4135     CurWeapon: PAmmo;
       
  4136 begin
       
  4137     s:= Distance (newPortal^.dX, newPortal^.dY);
       
  4138 
       
  4139     // Adds the hog speed (only that part in/directly against shot direction)
       
  4140     // to the shot speed (which we triple previously btw)
       
  4141     // (This is done my projecting the hog movement vector onto the shot movement vector and then adding the resulting length
       
  4142     // to the scaler)
       
  4143     s := (_2 * s + (newPortal^.dX * CurrentHedgehog^.Gear^.dX + newPortal^.dY * CurrentHedgehog^.Gear^.dY ) / s) / s;
       
  4144     newPortal^.dX := newPortal^.dX * s;
       
  4145     newPortal^.dY := newPortal^.dY * s;
       
  4146 
       
  4147     newPortal^.LinkedGear := nil;
       
  4148 
       
  4149     if CurrentHedgehog <> nil then
       
  4150         with CurrentHedgehog^ do
       
  4151             begin
       
  4152             CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^);
       
  4153             // let's save the HH's dX's direction so we can decide where the "top" of the portal hole
       
  4154             newPortal^.Elasticity.isNegative := CurrentHedgehog^.Gear^.dX.isNegative;
       
  4155             // when doing a backjump the dx is the opposite of the facing direction
       
  4156             if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then
       
  4157                 newPortal^.Elasticity.isNegative := not newPortal^.Elasticity.isNegative;
       
  4158 
       
  4159             // make portal gun look unloaded
       
  4160             if (CurWeapon <> nil) and (CurAmmoType = amPortalGun) then
       
  4161                 CurWeapon^.Timer := CurWeapon^.Timer or 2;
       
  4162 
       
  4163             iterator := GearsList;
       
  4164             while iterator <> nil do
       
  4165                 begin
       
  4166                 if (iterator^.Kind = gtPortal) then
       
  4167                     if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then
       
  4168                         begin
       
  4169                         if ((iterator^.Tag and 2) = (newPortal^.Tag and 2)) then
       
  4170                             begin
       
  4171                             iterator^.Timer:= 0;
       
  4172                             end
       
  4173                         else
       
  4174                             begin
       
  4175                             // link portals with each other
       
  4176                             newPortal^.LinkedGear := iterator;
       
  4177                             iterator^.LinkedGear := newPortal;
       
  4178                             iterator^.Health := newPortal^.Health;
       
  4179                             end;
       
  4180                         end;
       
  4181                 iterator^.PortalCounter:= 0;
       
  4182                 iterator := iterator^.NextGear
       
  4183                 end;
       
  4184 
       
  4185             if newPortal^.LinkedGear <> nil then
       
  4186                 begin
       
  4187                 // This jiggles gears, to ensure a portal connection just placed under a gear takes effect.
       
  4188                 iterator:= GearsList;
       
  4189                 while iterator <> nil do
       
  4190                     begin
       
  4191                     if not (iterator^.Kind in [gtPortal, gtAirAttack, gtKnife]) and ((iterator^.Hedgehog <> CurrentHedgehog)
       
  4192                     or ((iterator^.Message and gmAllStoppable) = 0)) then
       
  4193                             begin
       
  4194                             iterator^.Active:= true;
       
  4195                             if iterator^.dY.QWordValue = 0 then
       
  4196                                 iterator^.dY.isNegative:= false;
       
  4197                             iterator^.State:= iterator^.State or gstMoving;
       
  4198                             DeleteCI(iterator);
       
  4199                         //inc(iterator^.dY.QWordValue,10);
       
  4200                             end;
       
  4201                     iterator:= iterator^.NextGear
       
  4202                     end
       
  4203                 end
       
  4204             end;
       
  4205     newPortal^.State := newPortal^.State and (not gstCollision);
       
  4206     newPortal^.State := newPortal^.State or gstMoving;
       
  4207     newPortal^.doStep := @doStepMovingPortal;
       
  4208 end;
       
  4209 
       
  4210 ////////////////////////////////////////////////////////////////////////////////
       
  4211 procedure doStepPiano(Gear: PGear);
       
  4212 var
       
  4213     r0, r1: LongInt;
       
  4214     odY: hwFloat;
       
  4215 begin
       
  4216     AllInactive := false;
       
  4217     if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and
       
  4218         ((CurrentHedgehog^.Gear^.Message and gmSlot) <> 0) then
       
  4219             begin
       
  4220                 case CurrentHedgehog^.Gear^.MsgParam of
       
  4221                 0: PlaySound(sndPiano0);
       
  4222                 1: PlaySound(sndPiano1);
       
  4223                 2: PlaySound(sndPiano2);
       
  4224                 3: PlaySound(sndPiano3);
       
  4225                 4: PlaySound(sndPiano4);
       
  4226                 5: PlaySound(sndPiano5);
       
  4227                 6: PlaySound(sndPiano6);
       
  4228                 7: PlaySound(sndPiano7);
       
  4229                 else PlaySound(sndPiano8);
       
  4230             end;
       
  4231         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote);
       
  4232         CurrentHedgehog^.Gear^.MsgParam := 0;
       
  4233         CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSlot);
       
  4234         end;
       
  4235 
       
  4236     if (*((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or*) (Gear^.Pos = 5) then
       
  4237         begin
       
  4238         Gear^.dY := Gear^.dY + cGravity * 2;
       
  4239         Gear^.Y := Gear^.Y + Gear^.dY;
       
  4240         if CheckGearDrowning(Gear) then
       
  4241             begin
       
  4242             Gear^.Y:= Gear^.Y + _50;
       
  4243             OnUsedAmmo(CurrentHedgehog^);
       
  4244             if CurrentHedgehog^.Gear <> nil then
       
  4245                 begin
       
  4246                 // Drown the hedgehog.  Could also just delete it, but hey, this gets a caption
       
  4247                 CurrentHedgehog^.Gear^.Active := true;
       
  4248                 CurrentHedgehog^.Gear^.X := Gear^.X;
       
  4249                 CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128;
       
  4250                 CurrentHedgehog^.Unplaced := false;
       
  4251                 if TagTurnTimeLeft = 0 then
       
  4252                     TagTurnTimeLeft:= TurnTimeLeft;
       
  4253                 TurnTimeLeft:= 0
       
  4254                 end;
       
  4255             ResumeMusic
       
  4256             end;
       
  4257         exit
       
  4258         end;
       
  4259 
       
  4260     odY:= Gear^.dY;
       
  4261     doStepFallingGear(Gear);
       
  4262 
       
  4263     if (Gear^.State and gstDrowning) <> 0 then
       
  4264         begin
       
  4265         Gear^.Y:= Gear^.Y + _50;
       
  4266         OnUsedAmmo(CurrentHedgehog^);
       
  4267         if CurrentHedgehog^.Gear <> nil then
       
  4268             begin
       
  4269             // Drown the hedgehog.  Could also just delete it, but hey, this gets a caption
       
  4270             CurrentHedgehog^.Gear^.Active := true;
       
  4271             CurrentHedgehog^.Gear^.X := Gear^.X;
       
  4272             CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128;
       
  4273             CurrentHedgehog^.Unplaced := false;
       
  4274             if TagTurnTimeLeft = 0 then
       
  4275                 TagTurnTimeLeft:= TurnTimeLeft;
       
  4276             TurnTimeLeft:= 0
       
  4277             end;
       
  4278         ResumeMusic
       
  4279         end
       
  4280     else if (Gear^.State and gstCollision) <> 0 then
       
  4281         begin
       
  4282         r0 := GetRandom(21);
       
  4283         r1 := GetRandom(21);
       
  4284         doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0);
       
  4285         doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0);
       
  4286         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound);
       
  4287         for r0:= 0 to 4 do
       
  4288             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote);
       
  4289         Gear^.dY := cGravity * 2 - odY;
       
  4290         Gear^.Pos := Gear^.Pos + 1;
       
  4291         end
       
  4292     else
       
  4293         Gear^.dY := Gear^.dY + cGravity * 2;
       
  4294     // let it fall faster so itdoesn't take too long for the whole attack
       
  4295 end;
       
  4296 
       
  4297 
       
  4298 ////////////////////////////////////////////////////////////////////////////////
       
  4299 procedure doStepSineGunShotWork(Gear: PGear);
       
  4300 var
       
  4301     x, y, rX, rY, t, tmp, initHealth: LongInt;
       
  4302     oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat;
       
  4303     justCollided: boolean;
       
  4304 begin
       
  4305     AllInactive := false;
       
  4306     initHealth := Gear^.Health;
       
  4307     lX := Gear^.X;
       
  4308     lY := Gear^.Y;
       
  4309     ldX := Gear^.dX;
       
  4310     ldY := Gear^.dY;
       
  4311     sdy := _0_5/Distance(Gear^.dX,Gear^.dY);
       
  4312     ldX := ldX * sdy;
       
  4313     ldY := ldY * sdy;
       
  4314     sdY := hwAbs(ldX) + hwAbs(ldY);
       
  4315     sdX := _1 - hwAbs(ldX/sdY);
       
  4316     sdY := _1 - hwAbs(ldY/sdY);
       
  4317     if (ldX.isNegative = ldY.isNegative) then
       
  4318         sdY := -sdY;
       
  4319 
       
  4320     // initial angle depends on current GameTicks
       
  4321     t := getRandom(4096);
       
  4322 
       
  4323 
       
  4324     // used for a work-around detection of area that is within land array, but outside borders
       
  4325     justCollided := false;
       
  4326 
       
  4327     repeat
       
  4328         lX := lX + ldX;
       
  4329         lY := lY + ldY;
       
  4330         oX := Gear^.X;
       
  4331         oY := Gear^.Y;
       
  4332         rX := hwRound(oX);
       
  4333         rY := hwRound(oY);
       
  4334         tmp := t mod 4096;
       
  4335         amp := _128 * (_1 - hwSqr(int2hwFloat(Gear^.Health)/initHealth));
       
  4336         sine := amp * AngleSin(tmp mod 2048);
       
  4337         sine.isNegative := (tmp < 2048);
       
  4338         inc(t,Gear^.Health div 313);
       
  4339         Gear^.X := lX + (sine * sdX);
       
  4340         Gear^.Y := ly + (sine * sdY);
       
  4341         Gear^.dX := Gear^.X - oX;
       
  4342         Gear^.dY := Gear^.Y - oY;
       
  4343 
       
  4344         x := hwRound(Gear^.X);
       
  4345         y := hwRound(Gear^.Y);
       
  4346 
       
  4347         // if borders are on, stop outside land array
       
  4348         if hasBorder and (((x and LAND_WIDTH_MASK) <> 0) or ((y and LAND_HEIGHT_MASK) <> 0)) then
       
  4349             begin
       
  4350             Gear^.Damage := 0;
       
  4351             Gear^.Health := 0;
       
  4352             end
       
  4353         else
       
  4354             begin
       
  4355             if (rY <= cWaterLine) or (y <= cWaterLine) then
       
  4356                 begin
       
  4357                 if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0)
       
  4358                     and (Land[y, x] <> 0) then
       
  4359                         begin
       
  4360                             if justCollided then
       
  4361                                 begin
       
  4362                                 Gear^.Damage := 0;
       
  4363                                 Gear^.Health := 0;
       
  4364                                 end
       
  4365                             else
       
  4366                                 begin
       
  4367                                 inc(Gear^.Damage,3);
       
  4368                                 justCollided := true;
       
  4369                                 end;
       
  4370                         end
       
  4371                 else
       
  4372                     justCollided := false;
       
  4373 
       
  4374                 // kick nearby hogs, dig tunnel and add some fire
       
  4375                 // if at least 5 collisions occured
       
  4376                 if Gear^.Damage > 0 then
       
  4377                     begin
       
  4378                     DrawExplosion(rX,rY,Gear^.Radius);
       
  4379 
       
  4380                     // kick nearby hogs
       
  4381                     AmmoShove(Gear, 35, 50);
       
  4382 
       
  4383                     dec(Gear^.Health, Gear^.Damage);
       
  4384                     Gear^.Damage := 0;
       
  4385 
       
  4386                     // add some fire to the tunnel
       
  4387                     if getRandom(6) = 0 then
       
  4388                         begin
       
  4389                         tmp:= GetRandom(2 * Gear^.Radius);
       
  4390                         AddGear(x - Gear^.Radius + tmp, y - GetRandom(Gear^.Radius + 1), gtFlame, gsttmpFlag, _0, _0, 0)
       
  4391                         end
       
  4392                     end;
       
  4393 
       
  4394                 if random(100) = 0 then
       
  4395                     AddVisualGear(x, y, vgtSmokeTrace);
       
  4396                 end
       
  4397                 else dec(Gear^.Health, 5); // if underwater get additional damage
       
  4398             end;
       
  4399 
       
  4400         dec(Gear^.Health);
       
  4401 
       
  4402         // decrease bullet size towards the end
       
  4403         if (Gear^.Radius > 4) then
       
  4404             begin
       
  4405             if (Gear^.Health <= (initHealth div 3)) then
       
  4406                 dec(Gear^.Radius)
       
  4407             end
       
  4408         else if (Gear^.Radius > 3) then
       
  4409             begin
       
  4410             if (Gear^.Health <= (initHealth div 4)) then
       
  4411                 dec(Gear^.Radius)
       
  4412             end
       
  4413         else if (Gear^.Radius > 2) then begin
       
  4414             if (Gear^.Health <= (initHealth div 5)) then
       
  4415                 dec(Gear^.Radius)
       
  4416             end
       
  4417         else if (Gear^.Radius > 1) then
       
  4418             begin
       
  4419             if (Gear^.Health <= (initHealth div 6)) then
       
  4420                 dec(Gear^.Radius)
       
  4421             end;
       
  4422 
       
  4423     until (Gear^.Health <= 0);
       
  4424 
       
  4425     DeleteGear(Gear);
       
  4426     AfterAttack;
       
  4427 end;
       
  4428 
       
  4429 procedure doStepSineGunShot(Gear: PGear);
       
  4430 var
       
  4431     HHGear: PGear;
       
  4432 begin
       
  4433     PlaySound(sndSineGun);
       
  4434 
       
  4435     // push the shooting Hedgehog back
       
  4436     HHGear := CurrentHedgehog^.Gear;
       
  4437     Gear^.dX.isNegative := not Gear^.dX.isNegative;
       
  4438     Gear^.dY.isNegative := not Gear^.dY.isNegative;
       
  4439     HHGear^.dX := Gear^.dX;
       
  4440     HHGear^.dY := Gear^.dY;
       
  4441     AmmoShove(Gear, 0, 80);
       
  4442     Gear^.dX.isNegative := not Gear^.dX.isNegative;
       
  4443     Gear^.dY.isNegative := not Gear^.dY.isNegative;
       
  4444 
       
  4445     Gear^.doStep := @doStepSineGunShotWork;
       
  4446     with mobileRecord do
       
  4447         if (performRumble <> nil) and (not fastUntilLag) then
       
  4448             performRumble(kSystemSoundID_Vibrate);
       
  4449 end;
       
  4450 
       
  4451 ////////////////////////////////////////////////////////////////////////////////
       
  4452 procedure doStepFlamethrowerWork(Gear: PGear);
       
  4453 var
       
  4454     HHGear, flame: PGear;
       
  4455     rx, ry, speed: hwFloat;
       
  4456     i, gX, gY: LongInt;
       
  4457 begin
       
  4458     AllInactive := false;
       
  4459     HHGear := Gear^.Hedgehog^.Gear;
       
  4460     HedgehogChAngle(HHGear);
       
  4461     gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
       
  4462     gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
       
  4463 
       
  4464     if (GameTicks and $FF) = 0 then
       
  4465         begin
       
  4466         if (HHGear^.Message and gmRight) <> 0 then
       
  4467             begin
       
  4468             if HHGear^.dX.isNegative and (Gear^.Tag < 20) then
       
  4469                 inc(Gear^.Tag)
       
  4470             else if Gear^.Tag > 5 then
       
  4471                 dec(Gear^.Tag);
       
  4472             end
       
  4473         else if (HHGear^.Message and gmLeft) <> 0 then
       
  4474             begin
       
  4475             if HHGear^.dX.isNegative and (Gear^.Tag > 5) then
       
  4476                 dec(Gear^.Tag)
       
  4477             else if Gear^.Tag < 20 then
       
  4478                 inc(Gear^.Tag);
       
  4479             end
       
  4480         end;
       
  4481 
       
  4482     dec(Gear^.Timer);
       
  4483     if Gear^.Timer = 0 then
       
  4484         begin
       
  4485         dec(Gear^.Health);
       
  4486         if (Gear^.Health mod 5) = 0 then
       
  4487             begin
       
  4488             rx := rndSign(getRandomf * _0_1);
       
  4489             ry := rndSign(getRandomf * _0_1);
       
  4490             speed := _0_5 * (_10 / Gear^.Tag);
       
  4491 
       
  4492             flame:= AddGear(gx, gy, gtFlame, gstTmpFlag,
       
  4493                     SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
       
  4494                     AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
       
  4495             flame^.CollisionMask:= lfNotCurrentMask;
       
  4496 
       
  4497             if (Gear^.Health mod 30) = 0 then
       
  4498                 begin
       
  4499                 flame:= AddGear(gx, gy, gtFlame, 0,
       
  4500                         SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
       
  4501                         AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
       
  4502                 flame^.CollisionMask:= lfNotCurrentMask;
       
  4503                 end
       
  4504             end;
       
  4505         Gear^.Timer:= Gear^.Tag
       
  4506         end;
       
  4507 
       
  4508     if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then
       
  4509         begin
       
  4510         DeleteGear(Gear);
       
  4511         AfterAttack
       
  4512         end
       
  4513     else
       
  4514         begin
       
  4515         i:= Gear^.Health div 5;
       
  4516         if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  4517             begin
       
  4518             Gear^.Damage:= i;
       
  4519             FreeTexture(Gear^.Tex);
       
  4520             Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) +
       
  4521                          '%', cWhiteColor, fntSmall)
       
  4522             end
       
  4523         end
       
  4524 end;
       
  4525 
       
  4526 procedure doStepFlamethrower(Gear: PGear);
       
  4527 var
       
  4528     HHGear: PGear;
       
  4529 begin
       
  4530     HHGear := Gear^.Hedgehog^.Gear;
       
  4531     HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight));
       
  4532     HHGear^.State := HHGear^.State or gstNotKickable;
       
  4533     Gear^.doStep := @doStepFlamethrowerWork
       
  4534 end;
       
  4535 
       
  4536 ////////////////////////////////////////////////////////////////////////////////
       
  4537 procedure doStepLandGunWork(Gear: PGear);
       
  4538 var
       
  4539     HHGear, land: PGear;
       
  4540     rx, ry, speed: hwFloat;
       
  4541     i, gX, gY: LongInt;
       
  4542 begin
       
  4543     AllInactive := false;
       
  4544     HHGear := Gear^.Hedgehog^.Gear;
       
  4545     HedgehogChAngle(HHGear);
       
  4546     gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle);
       
  4547     gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle);
       
  4548 
       
  4549     if (GameTicks and $FF) = 0 then
       
  4550         begin
       
  4551         if (HHGear^.Message and gmRight) <> 0 then
       
  4552             begin
       
  4553             if HHGear^.dX.isNegative and (Gear^.Tag < 20) then
       
  4554                 inc(Gear^.Tag)
       
  4555             else if Gear^.Tag > 5 then
       
  4556                 dec(Gear^.Tag);
       
  4557             end
       
  4558         else if (HHGear^.Message and gmLeft) <> 0 then
       
  4559             begin
       
  4560             if HHGear^.dX.isNegative and (Gear^.Tag > 5) then
       
  4561                 dec(Gear^.Tag)
       
  4562             else if Gear^.Tag < 20 then
       
  4563                 inc(Gear^.Tag);
       
  4564             end
       
  4565         end;
       
  4566 
       
  4567     dec(Gear^.Timer);
       
  4568     if Gear^.Timer = 0 then
       
  4569         begin
       
  4570         dec(Gear^.Health);
       
  4571 
       
  4572         rx := rndSign(getRandomf * _0_1);
       
  4573         ry := rndSign(getRandomf * _0_1);
       
  4574         speed := (_3 / Gear^.Tag);
       
  4575 
       
  4576         land:= AddGear(gx, gy, gtFlake, gstTmpFlag,
       
  4577                 SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
       
  4578                 AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
       
  4579         land^.CollisionMask:= lfNotCurrentMask;
       
  4580 
       
  4581         Gear^.Timer:= Gear^.Tag
       
  4582         end;
       
  4583 
       
  4584     if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then
       
  4585         begin
       
  4586         HHGear^.Message:= HHGear^.Message and (not gmAttack);
       
  4587         DeleteGear(Gear);
       
  4588         AfterAttack
       
  4589         end
       
  4590     else
       
  4591         begin
       
  4592         i:= Gear^.Health div 10;
       
  4593         if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  4594             begin
       
  4595             Gear^.Damage:= i;
       
  4596             FreeTexture(Gear^.Tex);
       
  4597             Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) +
       
  4598                          '%', cWhiteColor, fntSmall)
       
  4599             end
       
  4600         end
       
  4601 end;
       
  4602 
       
  4603 procedure doStepLandGun(Gear: PGear);
       
  4604 var
       
  4605     HHGear: PGear;
       
  4606 begin
       
  4607     HHGear := Gear^.Hedgehog^.Gear;
       
  4608     HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight or gmAttack));
       
  4609     HHGear^.State := HHGear^.State or gstNotKickable;
       
  4610     Gear^.doStep := @doStepLandGunWork
       
  4611 end;
       
  4612 
       
  4613 ////////////////////////////////////////////////////////////////////////////////
       
  4614 procedure doStepPoisonCloud(Gear: PGear);
       
  4615 begin
       
  4616     if Gear^.Timer = 0 then
       
  4617         begin
       
  4618         DeleteGear(Gear);
       
  4619         exit
       
  4620         end;
       
  4621     dec(Gear^.Timer);
       
  4622     Gear^.X:= Gear^.X + Gear^.dX;
       
  4623     Gear^.Y:= Gear^.Y + Gear^.dY;
       
  4624     Gear^.dX := Gear^.dX + cWindSpeed / 4;
       
  4625     Gear^.dY := Gear^.dY + cGravity / 100;
       
  4626     if (GameTicks and $FF) = 0 then
       
  4627         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned);
       
  4628     AllInactive:= false;
       
  4629 end;
       
  4630 
       
  4631 ////////////////////////////////////////////////////////////////////////////////
       
  4632 procedure doStepHammer(Gear: PGear);
       
  4633 var HHGear, tmp, tmp2: PGear;
       
  4634          t: PGearArray;
       
  4635          i: LongInt;
       
  4636 begin
       
  4637 HHGear:= Gear^.Hedgehog^.Gear;
       
  4638 HHGear^.State:= HHGear^.State or gstNoDamage;
       
  4639 DeleteCI(HHGear);
       
  4640 
       
  4641 t:= CheckGearsCollision(Gear);
       
  4642 
       
  4643 for i:= 5 downto 0 do
       
  4644     AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
       
  4645 
       
  4646 i:= t^.Count;
       
  4647 while i > 0 do
       
  4648     begin
       
  4649     dec(i);
       
  4650     tmp:= t^.ar[i];
       
  4651     if (tmp^.State and gstNoDamage) = 0 then
       
  4652         if (tmp^.Kind = gtHedgehog) or (tmp^.Kind = gtMine) or (tmp^.Kind = gtExplosives) then
       
  4653             begin
       
  4654             //tmp^.State:= tmp^.State or gstFlatened;
       
  4655             if not tmp^.Invulnerable then
       
  4656                 ApplyDamage(tmp, CurrentHedgehog, tmp^.Health div 3, dsUnknown);
       
  4657             //DrawTunnel(tmp^.X, tmp^.Y - _1, _0, _0_5, cHHRadius * 6, cHHRadius * 3);
       
  4658             tmp2:= AddGear(hwRound(tmp^.X), hwRound(tmp^.Y), gtHammerHit, 0, _0, _0, 0);
       
  4659             tmp2^.LinkedGear:= tmp;
       
  4660             SetAllToActive
       
  4661             end
       
  4662         else
       
  4663             begin
       
  4664             end
       
  4665     end;
       
  4666 
       
  4667 HHGear^.State:= HHGear^.State and (not gstNoDamage);
       
  4668 Gear^.Timer:= 250;
       
  4669 Gear^.doStep:= @doStepIdle
       
  4670 end;
       
  4671 
       
  4672 procedure doStepHammerHitWork(Gear: PGear);
       
  4673 var
       
  4674     i, j, ei: LongInt;
       
  4675     HitGear: PGear;
       
  4676 begin
       
  4677     AllInactive := false;
       
  4678     HitGear := Gear^.LinkedGear;
       
  4679     dec(Gear^.Timer);
       
  4680     if (HitGear = nil) or (Gear^.Timer = 0) or ((Gear^.Message and gmDestroy) <> 0) then
       
  4681         begin
       
  4682         DeleteGear(Gear);
       
  4683         exit
       
  4684         end;
       
  4685 
       
  4686     if (Gear^.Timer mod 5) = 0 then
       
  4687         begin
       
  4688         AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
       
  4689 
       
  4690         i := hwRound(Gear^.X) - HitGear^.Radius + 2;
       
  4691         ei := hwRound(Gear^.X) + HitGear^.Radius - 2;
       
  4692         for j := 1 to 4 do DrawExplosion(i - GetRandom(5), hwRound(Gear^.Y) + 6*j, 3);
       
  4693         for j := 1 to 4 do DrawExplosion(ei + LongInt(GetRandom(5)), hwRound(Gear^.Y) + 6*j, 3);
       
  4694         while i <= ei do
       
  4695             begin
       
  4696             for j := 1 to 11 do DrawExplosion(i, hwRound(Gear^.Y) + 3*j, 3);
       
  4697             inc(i, 1)
       
  4698             end;
       
  4699 
       
  4700         if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9)
       
  4701            , lfIndestructible) then
       
  4702             begin
       
  4703             //Gear^.X := Gear^.X + Gear^.dX;
       
  4704             Gear^.Y := Gear^.Y + _1_9
       
  4705             end;
       
  4706         end;
       
  4707     if TestCollisionYwithGear(Gear, 1) <> 0 then
       
  4708         begin
       
  4709         Gear^.dY := _0;
       
  4710         SetLittle(HitGear^.dX);
       
  4711         HitGear^.dY := _0;
       
  4712         end
       
  4713     else
       
  4714         begin
       
  4715         //Gear^.dY := Gear^.dY + cGravity;
       
  4716         //Gear^.Y := Gear^.Y + Gear^.dY;
       
  4717         if hwRound(Gear^.Y) > cWaterLine then
       
  4718             Gear^.Timer := 1
       
  4719         end;
       
  4720 
       
  4721     //Gear^.X := Gear^.X + HitGear^.dX;
       
  4722     HitGear^.X := Gear^.X;
       
  4723     HitGear^.Y := Gear^.Y;
       
  4724     SetLittle(HitGear^.dY);
       
  4725     HitGear^.Active:= true;
       
  4726 end;
       
  4727 
       
  4728 procedure doStepHammerHit(Gear: PGear);
       
  4729 var
       
  4730     i, y: LongInt;
       
  4731     ar: TRangeArray;
       
  4732     HHGear: PGear;
       
  4733 begin
       
  4734     i := 0;
       
  4735     HHGear := Gear^.Hedgehog^.Gear;
       
  4736 
       
  4737     y := hwRound(Gear^.Y) - cHHRadius * 2;
       
  4738     while y < hwRound(Gear^.Y) do
       
  4739         begin
       
  4740         ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2));
       
  4741         ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2));
       
  4742         inc(y, 2);
       
  4743         inc(i)
       
  4744         end;
       
  4745 
       
  4746     DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i));
       
  4747     Gear^.dY := HHGear^.dY;
       
  4748     DeleteCI(HHGear);
       
  4749 
       
  4750     doStepHammerHitWork(Gear);
       
  4751     Gear^.doStep := @doStepHammerHitWork
       
  4752 end;
       
  4753 
       
  4754 ////////////////////////////////////////////////////////////////////////////////
       
  4755 procedure doStepResurrectorWork(Gear: PGear);
       
  4756 var
       
  4757     graves: PGearArrayS;
       
  4758     resgear: PGear;
       
  4759     hh: PHedgehog;
       
  4760     i: LongInt;
       
  4761 begin
       
  4762     if (TurnTimeLeft > 0) then
       
  4763         dec(TurnTimeLeft);
       
  4764 
       
  4765     AllInactive := false;
       
  4766     hh := Gear^.Hedgehog;
       
  4767 
       
  4768     // no, you can't do that here
       
  4769     {DrawCentered(hwRound(hh^.Gear^.X) + WorldDx, hwRound(hh^.Gear^.Y) + WorldDy -
       
  4770             cHHRadius - 14 - hh^.HealthTagTex^.h, hh^.HealthTagTex);
       
  4771     }
       
  4772     (*DrawCircle(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, 1.5, 0, 0, $FF,
       
  4773             $FF);*)
       
  4774 
       
  4775     if ((Gear^.Message and gmUp) <> 0) then
       
  4776         begin
       
  4777         if (GameTicks and $F) <> 0 then
       
  4778         exit;
       
  4779         end
       
  4780     else if (GameTicks and $1FF) <> 0 then
       
  4781         exit;
       
  4782 
       
  4783     if Gear^.Power < 45 then
       
  4784         begin
       
  4785         inc(Gear^.Power);
       
  4786         if TestCollisionYwithGear(hh^.Gear, -1) = 0 then
       
  4787             hh^.Gear^.Y := hh^.Gear^.Y - _1;
       
  4788         end;
       
  4789 
       
  4790     graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius);
       
  4791 
       
  4792     if graves.size = 0 then
       
  4793         begin
       
  4794         StopSoundChan(Gear^.SoundChannel);
       
  4795         Gear^.Timer := 250;
       
  4796         Gear^.doStep := @doStepIdle;
       
  4797         exit;
       
  4798         end;
       
  4799 
       
  4800     if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then
       
  4801         begin
       
  4802         if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0;
       
  4803         dec(hh^.Gear^.Health);
       
  4804         if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then
       
  4805             hh^.Gear^.Damage:= 1;
       
  4806         RenderHealth(hh^);
       
  4807         RecountTeamHealth(hh^.Team);
       
  4808         inc(graves.ar^[Gear^.Tag]^.Health);
       
  4809         inc(Gear^.Tag)
       
  4810 {-for i:= 0 to High(graves) do begin
       
  4811             if hh^.Gear^.Health > 0 then begin
       
  4812                 dec(hh^.Gear^.Health);
       
  4813                 inc(graves[i]^.Health);
       
  4814             end;
       
  4815         end; -}
       
  4816         end
       
  4817     else
       
  4818         begin
       
  4819         // now really resurrect the hogs with the hp saved in the graves
       
  4820         for i:= 0 to graves.size - 1 do
       
  4821             if graves.ar^[i]^.Health > 0 then
       
  4822                 begin
       
  4823                 resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0);
       
  4824                 resgear^.Hedgehog := graves.ar^[i]^.Hedgehog;
       
  4825                 resgear^.Health := graves.ar^[i]^.Health;
       
  4826                 PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := resgear;
       
  4827                 graves.ar^[i]^.Message:= graves.ar^[i]^.Message or gmDestroy;
       
  4828                 graves.ar^[i]^.Active:= true;
       
  4829                 RenderHealth(resgear^.Hedgehog^);
       
  4830                 RecountTeamHealth(resgear^.Hedgehog^.Team);
       
  4831                 resgear^.Hedgehog^.Effects[heResurrected]:= 1;
       
  4832                 // only make hat-less hedgehogs look like zombies, preserve existing hats
       
  4833 
       
  4834                 if resgear^.Hedgehog^.Hat = 'NoHat' then
       
  4835                     LoadHedgehogHat(resgear^.Hedgehog^, 'Reserved/Zombie');
       
  4836                 end;
       
  4837 
       
  4838         hh^.Gear^.dY := _0;
       
  4839         hh^.Gear^.dX := _0;
       
  4840         doStepHedgehogMoving(hh^.Gear);
       
  4841         StopSoundChan(Gear^.SoundChannel);
       
  4842         Gear^.Timer := 250;
       
  4843         Gear^.doStep := @doStepIdle;
       
  4844         end
       
  4845     //if hh^.Gear^.Health = 0 then doStepHedgehogFree(hh^.Gear);
       
  4846 end;
       
  4847 
       
  4848 procedure doStepResurrector(Gear: PGear);
       
  4849 var
       
  4850     graves: PGearArrayS;
       
  4851     hh: PHedgehog;
       
  4852     i: LongInt;
       
  4853 begin
       
  4854     AllInactive := false;
       
  4855     graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius);
       
  4856 
       
  4857     if graves.size > 0 then
       
  4858         begin
       
  4859         hh := Gear^.Hedgehog;
       
  4860         for i:= 0 to graves.size - 1 do
       
  4861             begin
       
  4862             PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := nil;
       
  4863             graves.ar^[i]^.Health := 0;
       
  4864             end;
       
  4865         Gear^.doStep := @doStepResurrectorWork;
       
  4866         if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then
       
  4867             begin
       
  4868             if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0;
       
  4869             dec(hh^.Gear^.Health);
       
  4870             if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then
       
  4871                 hh^.Gear^.Damage:= 1;
       
  4872             RenderHealth(hh^);
       
  4873             RecountTeamHealth(hh^.Team);
       
  4874             inc(graves.ar^[Gear^.Tag]^.Health);
       
  4875             inc(Gear^.Tag)
       
  4876             end
       
  4877         end
       
  4878     else
       
  4879         begin
       
  4880         StopSoundChan(Gear^.SoundChannel);
       
  4881         Gear^.Timer := 250;
       
  4882         Gear^.doStep := @doStepIdle;
       
  4883         end
       
  4884 end;
       
  4885 
       
  4886 ////////////////////////////////////////////////////////////////////////////////
       
  4887 procedure doStepNapalmBomb(Gear: PGear);
       
  4888 var
       
  4889     i, gX, gY: LongInt;
       
  4890     dX, dY: hwFloat;
       
  4891 begin
       
  4892     AllInactive := false;
       
  4893     doStepFallingGear(Gear);
       
  4894     if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then
       
  4895     begin
       
  4896         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound);
       
  4897         gX := hwRound(Gear^.X);
       
  4898         gY := hwRound(Gear^.Y);
       
  4899         for i:= 0 to 10 do
       
  4900         begin
       
  4901             dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandomf + _1);
       
  4902             dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1);
       
  4903             AddGear(gX, gY, gtFlame, 0, dX, dY, 0);
       
  4904             AddGear(gX, gY, gtFlame, 0, dX, -dY, 0);
       
  4905             AddGear(gX, gY, gtFlame, 0, -dX, dY, 0);
       
  4906             AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0);
       
  4907         end;
       
  4908         DeleteGear(Gear);
       
  4909         exit
       
  4910     end;
       
  4911     if (Gear^.Timer = 0) then
       
  4912         begin
       
  4913         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound);
       
  4914         for i:= -19 to 19 do
       
  4915            FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0);
       
  4916         DeleteGear(Gear);
       
  4917         exit
       
  4918         end;
       
  4919     if (GameTicks and $3F) = 0 then
       
  4920         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
       
  4921     dec(Gear^.Timer)
       
  4922 end;
       
  4923 
       
  4924 ////////////////////////////////////////////////////////////////////////////////
       
  4925 procedure doStepStructure(Gear: PGear);
       
  4926 var
       
  4927     x, y: LongInt;
       
  4928     HH: PHedgehog;
       
  4929     t: PGear;
       
  4930 begin
       
  4931     HH:= Gear^.Hedgehog;
       
  4932 
       
  4933     if (Gear^.State and gstMoving) <> 0 then
       
  4934         begin
       
  4935         AddGearCI(Gear);
       
  4936         Gear^.dX:= _0;
       
  4937         Gear^.dY:= _0;
       
  4938         Gear^.State:= Gear^.State and (not gstMoving);
       
  4939         end;
       
  4940 
       
  4941     dec(Gear^.Health, Gear^.Damage);
       
  4942     Gear^.Damage:= 0;
       
  4943 
       
  4944     if Gear^.Pos = 1 then
       
  4945         begin
       
  4946         AddGearCI(Gear);
       
  4947         AfterAttack;
       
  4948         if Gear = CurAmmoGear then
       
  4949             CurAmmoGear:= nil;
       
  4950         if HH^.Gear <> nil then
       
  4951             HideHog(HH);
       
  4952         Gear^.Pos:= 2
       
  4953         end;
       
  4954 
       
  4955     if Gear^.Pos = 2 then
       
  4956         begin
       
  4957         if ((GameTicks mod 100) = 0) and (Gear^.Timer < 1000) then
       
  4958             begin
       
  4959             if (Gear^.Timer mod 10) = 0 then
       
  4960                 begin
       
  4961                 DeleteCI(Gear);
       
  4962                 Gear^.Y:= Gear^.Y - _0_5;
       
  4963                 AddGearCI(Gear);
       
  4964                 end;
       
  4965             inc(Gear^.Timer);
       
  4966             end;
       
  4967         if Gear^.Tag <= TotalRounds then
       
  4968             Gear^.Pos:= 3;
       
  4969         end;
       
  4970 
       
  4971     if Gear^.Pos = 3 then
       
  4972         if Gear^.Timer < 1000 then
       
  4973             begin
       
  4974             if (Gear^.Timer mod 10) = 0 then
       
  4975                 begin
       
  4976                 DeleteCI(Gear);
       
  4977                 Gear^.Y:= Gear^.Y - _0_5;
       
  4978                 AddGearCI(Gear);
       
  4979                 end;
       
  4980             inc(Gear^.Timer);
       
  4981             end
       
  4982         else
       
  4983             begin
       
  4984             if HH^.GearHidden <> nil then
       
  4985                 RestoreHog(HH);
       
  4986             Gear^.Pos:= 4;
       
  4987             end;
       
  4988 
       
  4989     if Gear^.Pos = 4 then
       
  4990         if ((GameTicks mod 1000) = 0) and ((GameFlags and gfInvulnerable) = 0) then
       
  4991             begin
       
  4992             t:= GearsList;
       
  4993             while t <> nil do
       
  4994                 begin
       
  4995                 if (t^.Kind = gtHedgehog) and (t^.Hedgehog^.Team^.Clan = HH^.Team^.Clan) then
       
  4996                     t^.Invulnerable:= true;
       
  4997                 t:= t^.NextGear;
       
  4998                 end;
       
  4999             end;
       
  5000 
       
  5001     if Gear^.Health <= 0 then
       
  5002         begin
       
  5003         if HH^.GearHidden <> nil then
       
  5004             RestoreHog(HH);
       
  5005 
       
  5006         x := hwRound(Gear^.X);
       
  5007         y := hwRound(Gear^.Y);
       
  5008 
       
  5009         DeleteCI(Gear);
       
  5010         DeleteGear(Gear);
       
  5011 
       
  5012         doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound);
       
  5013         end;
       
  5014 end;
       
  5015 
       
  5016 ////////////////////////////////////////////////////////////////////////////////
       
  5017 (*
       
  5018  TARDIS needs
       
  5019  Warp in.  Pos = 1
       
  5020  Pause.    Pos = 2
       
  5021  Hide gear  (TARDIS hedgehog was nil)
       
  5022  Warp out. Pos = 3
       
  5023  ... idle active for some time period ...  Pos = 4
       
  5024  Warp in.  Pos = 1
       
  5025  Pause.    Pos = 2
       
  5026  Restore gear  (TARDIS hedgehog was not nil)
       
  5027  Warp out. Pos = 3
       
  5028 *)
       
  5029 
       
  5030 procedure doStepTardisWarp(Gear: PGear);
       
  5031 var HH: PHedgehog;
       
  5032     i,j,cnt: LongWord;
       
  5033 begin
       
  5034 HH:= Gear^.Hedgehog;
       
  5035 if Gear^.Pos = 2 then
       
  5036     begin
       
  5037     StopSoundChan(Gear^.SoundChannel);
       
  5038     if (Gear^.Timer = 0) then
       
  5039         begin
       
  5040         if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible = 0) then
       
  5041             begin
       
  5042             AfterAttack;
       
  5043             if Gear = CurAmmoGear then CurAmmoGear := nil;
       
  5044             if (HH^.Gear^.Damage = 0) and  (HH^.Gear^.Health > 0) and
       
  5045             ((Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then
       
  5046                 HideHog(HH)
       
  5047             end
       
  5048         //else if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible <> 0) then
       
  5049         else if (HH^.GearHidden <> nil) then// and (HH^.Gear^.State and gstInvisible <> 0) then
       
  5050             RestoreHog(HH)
       
  5051         end;
       
  5052 
       
  5053     inc(Gear^.Timer);
       
  5054     if (Gear^.Timer > 2000) and ((GameTicks mod 2000) = 1000) then
       
  5055         begin
       
  5056         Gear^.SoundChannel := LoopSound(sndTardis);
       
  5057         Gear^.Pos:= 3
       
  5058         end
       
  5059     end;
       
  5060 
       
  5061 if (Gear^.Pos = 1) and (GameTicks and $1F = 0) and (Gear^.Power < 255) then
       
  5062     begin
       
  5063     inc(Gear^.Power);
       
  5064     if (Gear^.Power = 172) and (HH^.Gear <> nil) and
       
  5065         (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and
       
  5066         ((HH^.Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then
       
  5067             with HH^.Gear^ do
       
  5068                 begin
       
  5069                 State:= State or gstAnimation;
       
  5070                 Tag:= 2;
       
  5071                 Timer:= 0;
       
  5072                 Pos:= 0
       
  5073                 end
       
  5074     end;
       
  5075 if (Gear^.Pos = 3) and (GameTicks and $1F = 0) and (Gear^.Power > 0) then
       
  5076     dec(Gear^.Power);
       
  5077 if (Gear^.Pos = 1) and (Gear^.Power = 255) and ((GameTicks mod 2000) = 1000) then
       
  5078     Gear^.Pos:= 2;
       
  5079 if (Gear^.Pos = 3) and (Gear^.Power = 0) then
       
  5080     begin
       
  5081     StopSoundChan(Gear^.SoundChannel);
       
  5082     if HH^.GearHidden = nil then
       
  5083         begin
       
  5084         DeleteGear(Gear);
       
  5085         exit
       
  5086         end;
       
  5087     Gear^.Pos:= 4;
       
  5088     // This condition might need tweaking
       
  5089     Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime
       
  5090     end;
       
  5091 
       
  5092 if (Gear^.Pos = 4) then
       
  5093     begin
       
  5094     cnt:= 0;
       
  5095     for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do
       
  5096         for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do
       
  5097             if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil)
       
  5098             and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0)
       
  5099             and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then
       
  5100                 inc(cnt);
       
  5101     if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then
       
  5102         begin
       
  5103         if HH^.GearHidden <> nil then
       
  5104             FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH,true);
       
  5105 
       
  5106         if HH^.GearHidden <> nil then
       
  5107             begin
       
  5108             Gear^.X:= HH^.GearHidden^.X;
       
  5109             Gear^.Y:= HH^.GearHidden^.Y;
       
  5110             end;
       
  5111         Gear^.Timer:= 0;
       
  5112 
       
  5113         if (HH^.GearHidden <> nil) and (cnt = 0) then // do an emergency jump back in this case. the team needs you!
       
  5114             begin
       
  5115             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplosion);
       
  5116             Gear^.Pos:= 2;
       
  5117             Gear^.Power:= 255;
       
  5118             end
       
  5119         else begin
       
  5120             Gear^.SoundChannel := LoopSound(sndTardis);
       
  5121             Gear^.Pos:= 1;
       
  5122             Gear^.Power:= 0;
       
  5123             end
       
  5124         end
       
  5125     else if (CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan) then dec(Gear^.Timer)
       
  5126     end;
       
  5127 
       
  5128 end;
       
  5129 
       
  5130 procedure doStepTardis(Gear: PGear);
       
  5131 var i,j,cnt: LongWord;
       
  5132     HH: PHedgehog;
       
  5133 begin
       
  5134 (*
       
  5135     Conditions for not activating.
       
  5136     1. Hog is last of his clan
       
  5137     2. Sudden Death is in play
       
  5138     3. Hog is a king
       
  5139 *)
       
  5140     HH:= Gear^.Hedgehog;
       
  5141     if HH^.Gear <> nil then
       
  5142     if (HH^.Gear = nil) or (HH^.King) or (SuddenDeathDmg) then
       
  5143         begin
       
  5144         if HH^.Gear <> nil then
       
  5145             begin
       
  5146             HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack);
       
  5147             HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking);
       
  5148             end;
       
  5149         PlaySound(sndDenied);
       
  5150         DeleteGear(gear);
       
  5151         exit
       
  5152         end;
       
  5153     cnt:= 0;
       
  5154     for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do
       
  5155         for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do
       
  5156             if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil)
       
  5157             and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0)
       
  5158             and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then
       
  5159                 inc(cnt);
       
  5160     if cnt < 2 then
       
  5161         begin
       
  5162         if HH^.Gear <> nil then
       
  5163             begin
       
  5164             HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack);
       
  5165             HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking);
       
  5166             end;
       
  5167             PlaySound(sndDenied);
       
  5168             DeleteGear(gear);
       
  5169             exit
       
  5170         end;
       
  5171     Gear^.SoundChannel := LoopSound(sndTardis);
       
  5172     Gear^.doStep:= @doStepTardisWarp
       
  5173 end;
       
  5174 
       
  5175 ////////////////////////////////////////////////////////////////////////////////
       
  5176 
       
  5177 (*
       
  5178 WIP. The ice gun will have the following effects.  It has been proposed by sheepluva that it take the appearance of a large freezer
       
  5179 spewing ice cubes.  The cubes will be visual gears only.  The scatter from them and the impact snow dust should help hide imprecisions in things like the GearsNear effect.
       
  5180 For now we assume a "ray" like a deagle projected out from the gun.
       
  5181 All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks.  This is a simplifying assumption for "gun was applying freezing effect to the same target".
       
  5182   * When fired at water a layer of ice textured land is added above the water.
       
  5183   * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed.
       
  5184   * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen.  As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head.  If the effect is interrupted before reaching the top, the freezing state is cleared.
       
  5185 A frozen hog will animate differently.  To be decided, but possibly in a similar fashion to a grave when it comes to explosions.  The hog might (possibly) not be damaged by explosions.  This might make freezing potentially useful for friendlies in a bad position.  It might be better to allow damage though.
       
  5186 A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again.
       
  5187 *)
       
  5188 
       
  5189 
       
  5190 procedure updateFuel(Gear: PGear);
       
  5191 var
       
  5192   t:LongInt;
       
  5193 begin
       
  5194     t:= Gear^.Health div 10;
       
  5195     if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
       
  5196     begin
       
  5197     Gear^.Damage:= t;
       
  5198     FreeTexture(Gear^.Tex);
       
  5199     Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) +
       
  5200               '%', cWhiteColor, fntSmall)
       
  5201     end;
       
  5202     if Gear^.Message and (gmUp or gmDown) <> 0 then
       
  5203         begin
       
  5204         StopSoundChan(Gear^.SoundChannel);
       
  5205         Gear^.SoundChannel:= -1;
       
  5206         if GameTicks mod 40 = 0 then dec(Gear^.Health)
       
  5207         end
       
  5208     else 
       
  5209         begin
       
  5210         if Gear^.SoundChannel = -1 then
       
  5211             Gear^.SoundChannel := LoopSound(sndIceBeam);
       
  5212         if GameTicks mod 10 = 0 then dec(Gear^.Health)
       
  5213         end
       
  5214 end;
       
  5215 
       
  5216 
       
  5217 procedure updateTarget(Gear:PGear; newX, newY:HWFloat);
       
  5218 //    var
       
  5219 //    iter:PGear;
       
  5220 begin
       
  5221   with Gear^ do
       
  5222   begin
       
  5223     dX:= newX;
       
  5224     dY:= newY;
       
  5225     Pos:= 0;
       
  5226     Target.X:= NoPointX;
       
  5227     LastDamage:= nil;
       
  5228     X:= Hedgehog^.Gear^.X;
       
  5229     Y:= Hedgehog^.Gear^.Y;
       
  5230   end;
       
  5231 end;
       
  5232 
       
  5233 procedure doStepIceGun(Gear: PGear);
       
  5234 const iceWaitCollision = 0;
       
  5235 const iceCollideWithGround = 1;
       
  5236 //const iceWaitNextTarget:Longint = 2;
       
  5237 //const iceCollideWithHog:Longint = 4;
       
  5238 const iceCollideWithWater = 5;
       
  5239 //const waterFreezingTime:Longint = 500;
       
  5240 const groundFreezingTime = 1000;
       
  5241 const iceRadius = 32;
       
  5242 const iceHeight = 40;
       
  5243 var
       
  5244     HHGear, iter: PGear;
       
  5245     landRect: TSDL_Rect;
       
  5246     ndX, ndY: hwFloat;
       
  5247     i, t, gX, gY: LongInt;
       
  5248     hogs: PGearArrayS;
       
  5249     vg: PVisualGear;
       
  5250 begin
       
  5251     HHGear := Gear^.Hedgehog^.Gear;
       
  5252     if (Gear^.Message and gmAttack <> 0) or (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) or (HHGear^.dX.QWordValue > 4294967)  then
       
  5253         begin
       
  5254         StopSoundChan(Gear^.SoundChannel);
       
  5255         DeleteGear(Gear);
       
  5256         AfterAttack;
       
  5257         exit
       
  5258         end;
       
  5259     updateFuel(Gear);
       
  5260 
       
  5261     with Gear^ do
       
  5262         begin
       
  5263         HedgehogChAngle(HHGear);
       
  5264         ndX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _4;
       
  5265         ndY:= -AngleCos(HHGear^.Angle) * _4;
       
  5266         if (ndX <> dX) or (ndY <> dY) or
       
  5267            ((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and
       
  5268              (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0))) then
       
  5269             begin
       
  5270             updateTarget(Gear, ndX, ndY);
       
  5271             Timer := iceWaitCollision;
       
  5272             end
       
  5273         else
       
  5274             begin
       
  5275             X:= X + dX;
       
  5276             Y:= Y + dY;
       
  5277             gX:= hwRound(X);
       
  5278             gY:= hwRound(Y);
       
  5279             if Target.X = NoPointX then t:= hwRound(hwSqr(X-HHGear^.X)+hwSqr(Y-HHGear^.Y));
       
  5280 
       
  5281             if Target.X <> NoPointX then
       
  5282                 begin
       
  5283                 CheckCollision(Gear);
       
  5284                 if (State and gstCollision) <> 0 then
       
  5285                     begin
       
  5286                     if Timer = iceWaitCollision then
       
  5287                         begin
       
  5288                         Timer := iceCollideWithGround;
       
  5289                         Power := GameTicks;
       
  5290                         end
       
  5291                     end
       
  5292                 else if (target.y >= cWaterLine) then
       
  5293                     begin
       
  5294                     if Timer = iceWaitCollision then
       
  5295                         begin
       
  5296                         Timer := iceCollideWithWater;
       
  5297                         Power := GameTicks;
       
  5298                         end;
       
  5299                     end;
       
  5300 
       
  5301                 if (abs(gX-Target.X) < 2) and (abs(gY-Target.Y) < 2) then
       
  5302                     begin
       
  5303                     X:= HHGear^.X;
       
  5304                     Y:= HHGear^.Y
       
  5305                     end;
       
  5306 
       
  5307                 if (Timer = iceCollideWithGround) and ((GameTicks - Power) > groundFreezingTime) then
       
  5308                     begin
       
  5309                     FillRoundInLand(target.x, target.y, iceRadius, icePixel);
       
  5310                     landRect.x := min(max(target.x - iceRadius, 0), LAND_WIDTH - 1);
       
  5311                     landRect.y := min(max(target.y - iceRadius, 0), LAND_HEIGHT - 1);
       
  5312                     landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1);
       
  5313                     landRect.h := min(2*iceRadius, LAND_HEIGHT - landRect.y - 1);
       
  5314                     UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true);
       
  5315                     
       
  5316                     // Freeze nearby mines/explosives/cases too
       
  5317                     iter := GearsList;
       
  5318                     while iter <> nil do
       
  5319                         begin
       
  5320                         if (iter^.State and gstFrozen = 0) and
       
  5321                            ((iter^.Kind = gtExplosives) or (iter^.Kind = gtCase) or (iter^.Kind = gtMine)) and 
       
  5322                            (abs(iter^.X.Round-target.x)+abs(iter^.Y.Round-target.y)+2<2*iceRadius) and (Distance(iter^.X-int2hwFloat(target.x),iter^.Y-int2hwFloat(target.y))<int2hwFloat(iceRadius*2)) then
       
  5323                             begin
       
  5324                             for t:= 0 to 5 do
       
  5325                                 begin
       
  5326                                 vg:= AddVisualGear(hwRound(iter^.X)+random(4)-8, hwRound(iter^.Y)+random(8), vgtDust, 1);
       
  5327                                 if vg <> nil then
       
  5328                                     begin
       
  5329                                     i:= random(100) + 155;
       
  5330                                     vg^.Tint:= (i shl 24) or (i shl 16) or ($FF shl 8) or (random(200) + 55);
       
  5331                                     vg^.Angle:= random(360);
       
  5332                                     vg^.dx:= 0.001 * random(80);
       
  5333                                     vg^.dy:= 0.001 * random(80)
       
  5334                                     end
       
  5335                                 end;
       
  5336                             PlaySound(sndHogFreeze);
       
  5337                             if iter^.Kind = gtMine then // dud mine block
       
  5338                                 begin
       
  5339                                 iter^.State:= iter^.State or gstFrozen;
       
  5340                                 vg:= AddVisualGear(hwRound(iter^.X) - 4  + Random(8), hwRound(iter^.Y) - 4 - Random(4), vgtSmoke);
       
  5341                                 if vg <> nil then
       
  5342                                     vg^.Scale:= 0.5;
       
  5343                                 PlaySound(sndVaporize);
       
  5344                                 iter^.Health := 0;
       
  5345                                 iter^.Damage := 0;
       
  5346                                 iter^.State := iter^.State and (not gstAttacking)
       
  5347                                 end
       
  5348                             else if iter^.Kind = gtCase then
       
  5349                                 begin
       
  5350                                 DeleteCI(iter);
       
  5351                                 iter^.State:= iter^.State or gstFrozen;
       
  5352                                 AddGearCI(iter)
       
  5353                                 end
       
  5354                             else // gtExplosives
       
  5355                                 begin
       
  5356                                 iter^.State:= iter^.State or gstFrozen;
       
  5357                                 iter^.Health:= iter^.Health + cBarrelHealth
       
  5358                                 end
       
  5359                             end;
       
  5360                         iter:= iter^.NextGear
       
  5361                         end;
       
  5362 
       
  5363                     // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius);
       
  5364                     SetAllHHToActive;
       
  5365                     Timer := iceWaitCollision;
       
  5366                     end;
       
  5367 
       
  5368                 if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then
       
  5369                     begin
       
  5370                     PlaySound(sndHogFreeze);
       
  5371                     DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight);
       
  5372                     SetAllHHToActive;
       
  5373                     Timer := iceWaitCollision;
       
  5374                     end;
       
  5375 (*
       
  5376  Any ideas for something that would look good here?
       
  5377                 if (Target.X <> NoPointX) and ((Timer = iceCollideWithGround) or (Timer = iceCollideWithWater)) and (GameTicks mod max((groundFreezingTime-((GameTicks - Power)*2)),2) = 0) then //and CheckLandValue(Target.X, Target.Y, lfIce) then
       
  5378                     begin
       
  5379                         vg:= AddVisualGear(Target.X+random(20)-10, Target.Y+random(40)-10, vgtDust, 1);
       
  5380                         if vg <> nil then
       
  5381                             begin
       
  5382                             i:= random(100) + 155;
       
  5383                             vg^.Tint:= IceColor or $FF;
       
  5384                             vg^.Angle:= random(360);
       
  5385                             vg^.dx:= 0.001 * random(80);
       
  5386                             vg^.dy:= 0.001 * random(80)
       
  5387                             end
       
  5388                     end;
       
  5389 *)
       
  5390 
       
  5391 // freeze nearby hogs
       
  5392                 hogs := GearsNear(int2hwFloat(Target.X), int2hwFloat(Target.Y), gtHedgehog, Gear^.Radius*2);
       
  5393                 if hogs.size > 0 then
       
  5394                     for i:= 0 to hogs.size - 1 do
       
  5395                         if hogs.ar^[i] <> HHGear then
       
  5396                             if GameTicks mod 5 = 0 then
       
  5397                                 begin
       
  5398                                 hogs.ar^[i]^.Active:= true;
       
  5399                                 if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] < 256 then
       
  5400                                     hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] := hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] + 1
       
  5401                                 else if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] = 256 then
       
  5402                                     begin
       
  5403                                     hogs.ar^[i]^.Hedgehog^.Effects[heFrozen]:= 200000-1;//cHedgehogTurnTime + cReadyDelay
       
  5404                                     PlaySound(sndHogFreeze);
       
  5405                                     end;
       
  5406                                 end;
       
  5407                 inc(Pos)
       
  5408                 end
       
  5409             else if (t > 400) and ((gY > cWaterLine) or
       
  5410                     (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0))
       
  5411                         and (Land[gY, gX] <> 0))) then
       
  5412                 begin
       
  5413                 Target.X:= gX;
       
  5414                 Target.Y:= gY;
       
  5415                 X:= HHGear^.X;
       
  5416                 Y:= HHGear^.Y
       
  5417                 end;
       
  5418             if (gX > max(LAND_WIDTH,4096)*2) or
       
  5419                     (gX < -max(LAND_WIDTH,4096)) or
       
  5420                     (gY < -max(LAND_HEIGHT,4096)) or
       
  5421                     (gY > max(LAND_HEIGHT,4096)+512) then
       
  5422                 begin
       
  5423                 //X:= HHGear^.X;
       
  5424                 //Y:= HHGear^.Y
       
  5425                 Target.X:= gX;
       
  5426                 Target.Y:= gY;
       
  5427                 end
       
  5428         end
       
  5429     end;
       
  5430 end;
       
  5431 
       
  5432 procedure doStepAddAmmo(Gear: PGear);
       
  5433 var a: TAmmoType;
       
  5434     gi: PGear;
       
  5435 begin
       
  5436 if Gear^.Timer > 0 then dec(Gear^.Timer)
       
  5437 else
       
  5438     begin
       
  5439     if Gear^.Pos = posCaseUtility then
       
  5440         a:= GetUtility(Gear^.Hedgehog)
       
  5441     else
       
  5442         a:= GetAmmo(Gear^.Hedgehog);
       
  5443     CheckSum:= CheckSum xor GameTicks;
       
  5444     gi := GearsList;
       
  5445     while gi <> nil do
       
  5446         begin
       
  5447         with gi^ do CheckSum:= CheckSum xor X.round xor X.frac xor dX.round xor dX.frac xor Y.round xor Y.frac xor dY.round xor dY.frac;
       
  5448         AddRandomness(CheckSum);
       
  5449         if gi^.Kind = gtGenericFaller then gi^.State:= gi^.State and not gstTmpFlag;
       
  5450         gi := gi^.NextGear
       
  5451         end;
       
  5452     AddPickup(Gear^.Hedgehog^, a, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y));
       
  5453     DeleteGear(Gear)
       
  5454     end;
       
  5455 end;
       
  5456 
       
  5457 procedure doStepGenericFaller(Gear: PGear);
       
  5458 begin
       
  5459 if Gear^.Timer < $FFFFFFFF then
       
  5460     if Gear^.Timer > 0 then
       
  5461         dec(Gear^.Timer)
       
  5462     else
       
  5463         begin
       
  5464         DeleteGear(Gear);
       
  5465         exit
       
  5466         end;
       
  5467 if (Gear^.State and gstTmpFlag <> 0) or (GameTicks and $7 = 0) then
       
  5468     begin
       
  5469     doStepFallingGear(Gear);
       
  5470     if (Gear^.State and gstInvisible <> 0) and (GameTicks and $FF = 0) and (hwRound(Gear^.X) < LongInt(leftX)) or (hwRound(Gear^.X) > LongInt(rightX)) or (hwRound(Gear^.Y) < LongInt(topY)) then
       
  5471         begin
       
  5472         Gear^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX);
       
  5473         Gear^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY);
       
  5474         Gear^.dX:= _90-(GetRandomf*_360);
       
  5475         Gear^.dY:= _90-(GetRandomf*_360)
       
  5476         end;
       
  5477     end
       
  5478 end;
       
  5479 
       
  5480 procedure doStepCreeper(Gear: PGear);
       
  5481 var hogs: PGearArrayS;
       
  5482     HHGear: PGear;
       
  5483     tdX: hwFloat;
       
  5484     dir: LongInt;
       
  5485 begin
       
  5486 doStepFallingGear(Gear);
       
  5487 if Gear^.Timer > 0 then dec(Gear^.Timer);
       
  5488 // creeper sleep phase
       
  5489 if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit;
       
  5490 
       
  5491 if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear
       
  5492 else HHGear:= nil;
       
  5493 
       
  5494 // creeper boom phase
       
  5495 if (Gear^.State and gstTmpFlag <> 0) then
       
  5496     begin
       
  5497     if (Gear^.Timer = 0) then
       
  5498         begin
       
  5499         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound);
       
  5500         DeleteGear(Gear)
       
  5501         end;
       
  5502     // ssssss he essssscaped
       
  5503     if (Gear^.Timer > 250) and ((HHGear = nil) or
       
  5504             (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) >  180) and
       
  5505             (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then
       
  5506         begin
       
  5507         Gear^.State:= Gear^.State and (not gstTmpFlag);
       
  5508         Gear^.Timer:= 0
       
  5509         end;
       
  5510     exit
       
  5511     end;
       
  5512 
       
  5513 // Search out a new target, as target seek time has expired, target is dead, target is out of range, or we did not have a target
       
  5514 if (HHGear = nil) or (Gear^.Timer = 0) or
       
  5515    (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) >  Gear^.Angle) and
       
  5516         (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle)))
       
  5517     then
       
  5518     begin
       
  5519     hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle);
       
  5520     if hogs.size > 1 then
       
  5521         Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog
       
  5522     else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog
       
  5523     else Gear^.Hedgehog:= nil;
       
  5524     if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000;
       
  5525     exit
       
  5526     end;
       
  5527 
       
  5528 // we have a target. move the creeper.
       
  5529 if HHGear <> nil then
       
  5530     begin
       
  5531     // GOTCHA
       
  5532     if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) <  50) and
       
  5533          (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then
       
  5534         begin
       
  5535         // hisssssssssss
       
  5536         Gear^.State:= Gear^.State or gstTmpFlag;
       
  5537         Gear^.Timer:= 1500;
       
  5538         exit
       
  5539         end;
       
  5540     if (Gear^.State and gstMoving <> 0) then
       
  5541         begin
       
  5542         Gear^.dY:= _0;
       
  5543         Gear^.dX:= _0;
       
  5544         end
       
  5545     else if (GameTicks and $FF = 0) then
       
  5546         begin
       
  5547         tdX:= HHGear^.X-Gear^.X;
       
  5548         dir:= hwSign(tdX);
       
  5549         if not TestCollisionX(Gear, dir) then
       
  5550             Gear^.X:= Gear^.X + signAs(_1,tdX);
       
  5551         if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then
       
  5552             begin
       
  5553             Gear^.dX:= SignAs(_0_15, tdX);
       
  5554             Gear^.dY:= -_0_3;
       
  5555             Gear^.State:= Gear^.State or gstMoving
       
  5556             end
       
  5557         end;
       
  5558     end;
       
  5559 end;
       
  5560 
       
  5561 ////////////////////////////////////////////////////////////////////////////////
       
  5562 procedure doStepKnife(Gear: PGear);
       
  5563 //var ox, oy: LongInt;
       
  5564 //    la: hwFloat;
       
  5565 var   a: real;
       
  5566 begin
       
  5567     // Gear is shrunk so it can actually escape the hog without carving into the terrain
       
  5568     if (Gear^.Radius = 4) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 7;
       
  5569     if Gear^.Damage > 100 then Gear^.CollisionMask:= 0
       
  5570     else if Gear^.Damage > 30 then
       
  5571         if GetRandom(max(4,18-Gear^.Damage div 10)) < 3 then Gear^.CollisionMask:= 0;
       
  5572     Gear^.Damage:= 0;
       
  5573     if Gear^.Timer > 0 then dec(Gear^.Timer);
       
  5574     if (Gear^.State and gstMoving <> 0) and (Gear^.State and gstCollision = 0) then
       
  5575         begin
       
  5576         DeleteCI(Gear);
       
  5577         Gear^.Radius:= 7;
       
  5578         // used for damage and impact calc. needs balancing I think
       
  5579         Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4));
       
  5580         doStepFallingGear(Gear);
       
  5581         AllInactive := false;
       
  5582         a:= Gear^.DirAngle;
       
  5583         CalcRotationDirAngle(Gear);
       
  5584         Gear^.DirAngle:= a+(Gear^.DirAngle-a)*2*hwSign(Gear^.dX) // double rotation
       
  5585         end
       
  5586     else if (Gear^.CollisionIndex = -1) and (Gear^.Timer = 0) then
       
  5587         begin
       
  5588         (*ox:= 0; oy:= 0;
       
  5589         if TestCollisionYwithGear(Gear, -1) <> 0 then oy:= -1;
       
  5590         if TestCollisionXwithGear(Gear, 1)       then ox:=  1;
       
  5591         if TestCollisionXwithGear(Gear, -1)      then ox:= -1;
       
  5592         if TestCollisionYwithGear(Gear, 1) <> 0  then oy:=  1;
       
  5593         if Gear^.Health > 0 then
       
  5594             PlaySound(sndRopeAttach);
       
  5595 
       
  5596         la:= _10000;
       
  5597         if (ox <> 0) or (oy <> 0) then
       
  5598             la:= CalcSlopeNearGear(Gear, ox, oy);
       
  5599         if la = _10000 then
       
  5600             begin
       
  5601             // debug for when we couldn't get an angle
       
  5602             //AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite);
       
  5603 *)
       
  5604             Gear^.DirAngle:= DxDy2Angle(Gear^.dX, Gear^.dY) + (random(30)-15);
       
  5605             if (Gear^.dX.isNegative and Gear^.dY.isNegative) or
       
  5606              ((not Gear^.dX.isNegative) and (not Gear^.dY.isNegative)) then Gear^.DirAngle:= Gear^.DirAngle-90;
       
  5607  //           end
       
  5608  //       else Gear^.DirAngle:= hwFloat2Float(la)*90; // sheepluva's comment claims 45deg = 0.5 - yet orientation doesn't seem consistent?
       
  5609  //       AddFileLog('la: '+floattostr(la)+' DirAngle: '+inttostr(round(Gear^.DirAngle)));
       
  5610         Gear^.dX:= _0;
       
  5611         Gear^.dY:= _0;
       
  5612         Gear^.State:= Gear^.State and (not gstMoving) or gstCollision;
       
  5613         Gear^.Radius:= 16;
       
  5614         if Gear^.Health > 0 then AmmoShove(Gear, Gear^.Health, 0);
       
  5615         Gear^.Health:= 0;
       
  5616         Gear^.Timer:= 500;
       
  5617         AddGearCI(Gear)
       
  5618         end
       
  5619     else if GameTicks and $3F = 0 then
       
  5620         begin
       
  5621         if  (TestCollisionYwithGear(Gear, -1) = 0)
       
  5622         and (not TestCollisionXwithGear(Gear, 1))
       
  5623         and (not TestCollisionXwithGear(Gear, -1))
       
  5624         and (TestCollisionYwithGear(Gear, 1) = 0) then Gear^.State:= Gear^.State and (not gstCollision) or gstMoving;
       
  5625         end
       
  5626 end;
       
  5627 (*
       
  5628  This didn't end up getting used, but, who knows, might be reasonable for javellin or something
       
  5629 // Make the knife initial angle based on the hog attack angle, or is that too hard?
       
  5630 procedure doStepKnife(Gear: PGear);
       
  5631 var t,
       
  5632     gx, gy, ga,  // gear x,y,angle
       
  5633     lx, ly, la, // land x,y,angle
       
  5634     ox, oy, // x,y offset
       
  5635     w, h,   // wXh of clip area
       
  5636     tx, ty  // tip position in sprite
       
  5637     : LongInt;
       
  5638     surf: PSDL_Surface;
       
  5639     s: hwFloat;
       
  5640 
       
  5641 begin
       
  5642     Gear^.dY := Gear^.dY + cGravity;
       
  5643     if (GameFlags and gfMoreWind) <> 0 then
       
  5644         Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density;
       
  5645     Gear^.X := Gear^.X + Gear^.dX;
       
  5646     Gear^.Y := Gear^.Y + Gear^.dY;
       
  5647     CheckGearDrowning(Gear);
       
  5648     gx:= hwRound(Gear^.X);
       
  5649     gy:= hwRound(Gear^.Y);
       
  5650     if Gear^.State and gstDrowning <> 0 then exit;
       
  5651     with Gear^ do
       
  5652         begin
       
  5653         if CheckLandValue(gx, gy, lfLandMask) then
       
  5654             begin
       
  5655             t:= Angle + hwRound((hwAbs(dX)+hwAbs(dY)) * _10);
       
  5656 
       
  5657             if t < 0 then inc(t, 4096)
       
  5658             else if 4095 < t then dec(t, 4096);
       
  5659             Angle:= t;
       
  5660 
       
  5661             DirAngle:= Angle / 4096 * 360
       
  5662             end
       
  5663         else
       
  5664             begin
       
  5665 //This is the set of postions for the knife.
       
  5666 //Using FlipSurface and copyToXY the knife can be written to the LandPixels at 32 positions, and an appropriate line drawn in Land.
       
  5667             t:= Angle mod 1024;
       
  5668             case t div 128 of
       
  5669                 0:  begin
       
  5670                     ox:=   2; oy:= 5;
       
  5671                     w :=  25;  h:= 5;
       
  5672                     tx:=   0; ty:= 2
       
  5673                     end;
       
  5674                 1:  begin
       
  5675                     ox:=   2; oy:= 15;
       
  5676                      w:=  24;  h:=  8;
       
  5677                     tx:=   0; ty:=  7
       
  5678                     end;
       
  5679                 2:  begin
       
  5680                     ox:=   2; oy:= 27;
       
  5681                      w:=  23;  h:= 12;
       
  5682                     tx:= -12; ty:= -5
       
  5683                     end;
       
  5684                 3:  begin
       
  5685                     ox:=   2; oy:= 43;
       
  5686                      w:=  21;  h:= 15;
       
  5687                     tx:=   0; ty:= 14
       
  5688                     end;
       
  5689                 4:  begin
       
  5690                     ox:= 29; oy:=  8;
       
  5691                      w:= 19;  h:= 19;
       
  5692                     tx:=  0; ty:= 17
       
  5693                     end;
       
  5694                 5:  begin
       
  5695                     ox:= 29; oy:=  32;
       
  5696                      w:= 15;  h:=  21;
       
  5697                     tx:=  0; ty:=  20
       
  5698                     end;
       
  5699                 6:  begin
       
  5700                     ox:= 51; oy:=   3;
       
  5701                      w:= 11;  h:=  23;
       
  5702                     tx:=  0; ty:=  22
       
  5703                     end;
       
  5704                 7:  begin
       
  5705                     ox:= 51; oy:=  34;
       
  5706                      w:=  7;  h:=  24;
       
  5707                     tx:=  0; ty:=  23
       
  5708                     end
       
  5709                 end;
       
  5710 
       
  5711             surf:= SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RMask, GMask, BMask, AMask);
       
  5712             copyToXYFromRect(SpritesData[sprKnife].Surface, surf, ox, oy, w, h, 0, 0);
       
  5713             // try to make the knife hit point first
       
  5714             lx := 0;
       
  5715             ly := 0;
       
  5716             if CalcSlopeTangent(Gear, gx, gy, lx, ly, 255) then
       
  5717                 begin
       
  5718                 la:= vector2Angle(int2hwFloat(lx), int2hwFloat(ly));
       
  5719                 ga:= vector2Angle(dX, dY);
       
  5720                 AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle));
       
  5721                 // change  to 0 to 4096 forced by LongWord in Gear
       
  5722                 if la < 0 then la:= 4096+la;
       
  5723                 if ga < 0 then ga:= 4096+ga;
       
  5724                 if ((Angle > ga) and (Angle < la)) or ((Angle < ga) and (Angle > la)) then
       
  5725                     begin
       
  5726                     if Angle >= 2048 then dec(Angle, 2048)
       
  5727                     else if Angle < 2048 then inc(Angle, 2048)
       
  5728                     end;
       
  5729                 AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle))
       
  5730                 end;
       
  5731             case Angle div 1024 of
       
  5732                 0:  begin
       
  5733                     flipSurface(surf, true);
       
  5734                     flipSurface(surf, true);
       
  5735                     BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-(h-ty), w, surf)
       
  5736                     end;
       
  5737                 1:  begin
       
  5738                     flipSurface(surf, false);
       
  5739                     BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-ty, w, surf)
       
  5740                     end;
       
  5741                 2:  begin // knife was actually drawn facing this way...
       
  5742                     BlitImageAndGenerateCollisionInfo(gx-tx, gy-ty, w, surf)
       
  5743                     end;
       
  5744                 3:  begin
       
  5745                     flipSurface(surf, true);
       
  5746                     BlitImageAndGenerateCollisionInfo(gx-tx, gy-(h-ty), w, surf)
       
  5747                     end
       
  5748                 end;
       
  5749             SDL_FreeSurface(surf);
       
  5750             // this needs to calculate actual width/height + land clipping since update texture doesn't.
       
  5751             // i.e. this will crash if you fire near sides of map, but until I get the blit right, not going to put real values
       
  5752             UpdateLandTexture(hwRound(X)-32, 64, hwRound(Y)-32, 64, true);
       
  5753             DeleteGear(Gear);
       
  5754             exit
       
  5755             end
       
  5756         end;
       
  5757 end;
       
  5758 *)
       
  5759 
       
  5760 end.