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