hedgewars/uGearsHedgehog.pas
changeset 6468 da1e7fe7cff7
child 6515 74a04089bb56
equal deleted inserted replaced
6467:090269e528df 6468:da1e7fe7cff7
       
     1 (*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2004-2011 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 {$INCLUDE "options.inc"}
       
    20 
       
    21 unit uGearsHedgehog;
       
    22 interface
       
    23 uses uTypes;
       
    24 
       
    25 procedure doStepHedgehog(Gear: PGear);
       
    26 procedure AfterAttack; 
       
    27 procedure HedgehogStep(Gear: PGear); 
       
    28 procedure doStepHedgehogMoving(Gear: PGear); 
       
    29 procedure HedgehogChAngle(HHGear: PGear); 
       
    30 
       
    31 implementation
       
    32 uses uConsts, uVariables, uFloat, uAmmos, uSound, uCaptions, uMisc, 
       
    33     uCommands, uLocale, uUtils, uVisualGears, uStats, uIO, uScript,
       
    34     uGearsList, uGears, uCollisions, uRandom, uStore, uTeams, 
       
    35     uGearsUtils;
       
    36 
       
    37 // Shouldn't more of this ammo switching stuff be moved to uAmmos ?
       
    38 function ChangeAmmo(HHGear: PGear): boolean;
       
    39 var slot, i: Longword;
       
    40     ammoidx: LongInt;
       
    41 begin
       
    42 ChangeAmmo:= false;
       
    43 slot:= HHGear^.MsgParam;
       
    44 
       
    45 with HHGear^.Hedgehog^ do
       
    46     begin
       
    47     HHGear^.Message:= HHGear^.Message and (not gmSlot);
       
    48     ammoidx:= 0;
       
    49     if ((HHGear^.State and (gstAttacking or gstAttacked)) <> 0) or
       
    50        ((MultiShootAttacks > 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) = 0)) or
       
    51        ((HHGear^.State and gstHHDriven) = 0) then exit;
       
    52     ChangeAmmo:= true;
       
    53 
       
    54     while (ammoidx < cMaxSlotAmmoIndex) and (Ammo^[slot, ammoidx].AmmoType <> CurAmmoType) do inc(ammoidx);
       
    55 
       
    56     if ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) and (MultiShootAttacks > 0) then OnUsedAmmo(HHGear^.Hedgehog^);
       
    57 
       
    58     MultiShootAttacks:= 0;
       
    59     HHGear^.Message:= HHGear^.Message and (not (gmLJump or gmHJump));
       
    60     
       
    61     if Ammoz[CurAmmoType].Slot = slot then
       
    62         begin
       
    63         i:= 0;
       
    64         repeat
       
    65         inc(ammoidx);
       
    66         if (ammoidx > cMaxSlotAmmoIndex) then
       
    67             begin
       
    68             inc(i);
       
    69             CurAmmoType:= amNothing;
       
    70             ammoidx:= -1;
       
    71             //TryDo(i < 2, 'Engine bug: no ammo in current slot', true)
       
    72             end;
       
    73         until (i = 1) or ((Ammo^[slot, ammoidx].Count > 0) and (Team^.Clan^.TurnNumber > Ammoz[Ammo^[slot, ammoidx].AmmoType].SkipTurns))
       
    74         end 
       
    75     else
       
    76         begin
       
    77         i:= 0;
       
    78         // check whether there is ammo in slot
       
    79         while (i <= cMaxSlotAmmoIndex)
       
    80           and ((Ammo^[slot, i].Count = 0)
       
    81                or (Team^.Clan^.TurnNumber <= Ammoz[Ammo^[slot, i].AmmoType].SkipTurns)) do inc(i);
       
    82 
       
    83         if i <= cMaxSlotAmmoIndex then ammoidx:= i
       
    84         else ammoidx:= -1
       
    85         end;
       
    86         if ammoidx >= 0 then CurAmmoType:= Ammo^[slot, ammoidx].AmmoType;
       
    87     end
       
    88 end;
       
    89 
       
    90 procedure HHSetWeapon(HHGear: PGear);
       
    91 var t: LongInt;
       
    92     weap: TAmmoType;
       
    93     Hedgehog: PHedgehog;
       
    94     s: boolean;
       
    95 begin
       
    96 s:= false;
       
    97 
       
    98 weap:= TAmmoType(HHGear^.MsgParam);
       
    99 Hedgehog:= HHGear^.Hedgehog;
       
   100 
       
   101 if Hedgehog^.Team^.Clan^.TurnNumber <= Ammoz[weap].SkipTurns then exit; // weapon is not activated yet
       
   102 
       
   103 HHGear^.MsgParam:= Ammoz[weap].Slot;
       
   104 
       
   105 t:= cMaxSlotAmmoIndex;
       
   106 
       
   107 HHGear^.Message:= HHGear^.Message and (not gmWeapon);
       
   108 
       
   109 with Hedgehog^ do
       
   110     while (CurAmmoType <> weap) and (t >= 0) do
       
   111         begin
       
   112         s:= ChangeAmmo(HHGear);
       
   113         dec(t)
       
   114         end;
       
   115 
       
   116 if s then ApplyAmmoChanges(HHGear^.Hedgehog^)
       
   117 end;
       
   118 
       
   119 procedure HHSetTimer(Gear: PGear);
       
   120 var CurWeapon: PAmmo;
       
   121     color: LongWord;
       
   122 begin
       
   123 Gear^.Message:= Gear^.Message and (not gmTimer);
       
   124 CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);
       
   125 with Gear^.Hedgehog^ do
       
   126     if ((Gear^.Message and gmPrecise) <> 0) and ((CurWeapon^.Propz and ammoprop_SetBounce) <> 0) then
       
   127         begin
       
   128         color:= Gear^.Hedgehog^.Team^.Clan^.Color;
       
   129         case Gear^.MsgParam of
       
   130             1: begin
       
   131                AddCaption(format(trmsg[sidBounce], trmsg[sidBounce1]), color, capgrpAmmostate);
       
   132                CurWeapon^.Bounciness:= 350;
       
   133                end;
       
   134             2: begin
       
   135                AddCaption(format(trmsg[sidBounce], trmsg[sidBounce2]), color, capgrpAmmostate);
       
   136                CurWeapon^.Bounciness:= 700;
       
   137                end;
       
   138             3: begin
       
   139                AddCaption(format(trmsg[sidBounce], trmsg[sidBounce3]), color, capgrpAmmostate);
       
   140                CurWeapon^.Bounciness:= 1000;
       
   141                end;
       
   142             4: begin
       
   143                AddCaption(format(trmsg[sidBounce], trmsg[sidBounce4]), color, capgrpAmmostate);
       
   144                CurWeapon^.Bounciness:= 2000;
       
   145                end;
       
   146             5: begin
       
   147                AddCaption(format(trmsg[sidBounce], trmsg[sidBounce5]), color, capgrpAmmostate);
       
   148                CurWeapon^.Bounciness:= 4000;
       
   149                end
       
   150             end
       
   151         end
       
   152     else if (CurWeapon^.Propz and ammoprop_Timerable) <> 0 then
       
   153         begin
       
   154         CurWeapon^.Timer:= 1000 * Gear^.MsgParam;
       
   155         with CurrentTeam^ do
       
   156             ApplyAmmoChanges(Hedgehogs[CurrHedgehog]);
       
   157         end;
       
   158 end;
       
   159 
       
   160 
       
   161 procedure Attack(Gear: PGear);
       
   162 var xx, yy, newDx, newDy, lx, ly: hwFloat;
       
   163     speech: PVisualGear;
       
   164     newGear:  PGear;
       
   165     CurWeapon: PAmmo;
       
   166     altUse: boolean;
       
   167     elastic: hwFloat;
       
   168 begin
       
   169 newGear:= nil;
       
   170 bShowFinger:= false;
       
   171 CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);
       
   172 with Gear^,
       
   173      Gear^.Hedgehog^ do
       
   174      begin
       
   175      if ((State and gstHHDriven) <> 0)and
       
   176         ((State and (gstAttacked or gstHHChooseTarget)) = 0) and
       
   177         (((State and gstMoving) = 0) or
       
   178             (Power > 0) or
       
   179             (CurAmmoType = amTeleport) or 
       
   180             // Allow attacks while moving on ammo with AltAttack
       
   181             ((CurAmmoGear <> nil) and ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0)) or
       
   182             ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackInMove) <> 0)) and
       
   183         ((TargetPoint.X <> NoPointX) or ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) = 0)) then
       
   184         begin
       
   185         State:= State or gstAttacking;
       
   186         if Power = cMaxPower then Message:= Message and (not gmAttack)
       
   187         else if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) = 0 then Message:= Message and (not gmAttack)
       
   188         else begin
       
   189              if Power = 0 then
       
   190                 begin
       
   191                 AttackBar:= CurrentTeam^.AttackBar;
       
   192                 PlaySound(sndThrowPowerUp)
       
   193                 end;
       
   194              inc(Power)
       
   195              end;
       
   196         if ((Message and gmAttack) <> 0) then exit;
       
   197 
       
   198         if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0 then
       
   199            begin
       
   200            StopSound(sndThrowPowerUp);
       
   201            PlaySound(sndThrowRelease);
       
   202            end;
       
   203 
       
   204         xx:= SignAs(AngleSin(Angle), dX);
       
   205         yy:= -AngleCos(Angle);
       
   206 
       
   207         lx:= X + int2hwfloat(round(GetLaunchX(CurAmmoType, hwSign(dX), Angle)));
       
   208         ly:= Y + int2hwfloat(round(GetLaunchY(CurAmmoType, Angle)));
       
   209 
       
   210         if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then xx:= - xx;
       
   211         if Ammoz[CurAmmoType].Ammo.AttackVoice <> sndNone then
       
   212            AddVoice(Ammoz[CurAmmoType].Ammo.AttackVoice, CurrentTeam^.voicepack);
       
   213 
       
   214 // Initiating alt attack
       
   215         if  (CurAmmoGear <> nil) and
       
   216             ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0) and
       
   217             ((Gear^.Message and gmLJump) <> 0) and
       
   218             ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
       
   219             begin
       
   220             newDx:= dX / _2; 
       
   221             newDy:= dY / _2;
       
   222             altUse:= true;
       
   223             end
       
   224         else
       
   225             begin
       
   226             newDx:= xx*Power/cPowerDivisor;
       
   227             newDy:= yy*Power/cPowerDivisor;
       
   228             altUse:= false
       
   229             end;
       
   230 
       
   231              case CurAmmoType of
       
   232                       amGrenade: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGrenade,         0, newDx, newDy, CurWeapon^.Timer);
       
   233                       amMolotov: newGear:= AddGear(hwRound(lx), hwRound(ly), gtMolotov,      0, newDx, newDy, 0);
       
   234                   amClusterBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtClusterBomb,  0, newDx, newDy, CurWeapon^.Timer);
       
   235                       amGasBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGasBomb,      0, newDx, newDy, CurWeapon^.Timer);
       
   236                       amBazooka: newGear:= AddGear(hwRound(lx), hwRound(ly), gtShell,        0, newDx, newDy, 0);
       
   237                      amSnowball: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSnowball,     0, newDx, newDy, 0);
       
   238                           amBee: newGear:= AddGear(hwRound(lx), hwRound(ly), gtBee,          0, newDx, newDy, 0);
       
   239                       amShotgun: begin
       
   240                                  PlaySound(sndShotgunReload);
       
   241                                  newGear:= AddGear(hwRound(lx), hwRound(ly), gtShotgunShot,  0, xx * _0_5, yy * _0_5, 0);
       
   242                                  end;
       
   243                    amPickHammer: newGear:= AddGear(hwRound(lx), hwRound(ly) + cHHRadius, gtPickHammer, 0, _0, _0, 0);
       
   244                          amSkip: ParseCommand('/skip', true);
       
   245                          amRope: newGear:= AddGear(hwRound(lx), hwRound(ly), gtRope, 0, xx, yy, 0);
       
   246                          amMine: if altUse then
       
   247                                     newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, newDx, newDy, 3000)
       
   248                                  else
       
   249                                     newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
       
   250                         amSMine: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSMine,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
       
   251                        amDEagle: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0);
       
   252                       amSineGun: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0);
       
   253                     amPortalGun: begin
       
   254                                  newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtPortal, 0, xx * _0_6, yy * _0_6, 
       
   255                                  // set selected color
       
   256                                  CurWeapon^.Pos);
       
   257                                  end;
       
   258                   amSniperRifle: begin
       
   259                                  PlaySound(sndSniperReload);
       
   260                                  newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSniperRifleShot, 0, xx * _0_5, yy * _0_5, 0);
       
   261                                  end;
       
   262                      amDynamite: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtDynamite, 0, SignAs(_0_03, dX), _0, 5000);
       
   263                     amFirePunch: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtFirePunch, 0, xx, _0, 0);
       
   264                          amWhip: begin
       
   265                                  newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtWhip, 0, SignAs(_1, dX), - _0_8, 0);
       
   266                                  PlaySound(sndWhipCrack)
       
   267                                  end;
       
   268                        amHammer: begin
       
   269                                  newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtHammer, 0, SignAs(_1, dX), - _0_8, 0);
       
   270                                  PlaySound(sndWhack)
       
   271                                  end;
       
   272                   amBaseballBat: begin
       
   273                                  newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtShover, gsttmpFlag, xx * _0_5, yy * _0_5, 0);
       
   274                                  PlaySound(sndBaseballBat) // TODO: Only play if something is hit?
       
   275                                  end;
       
   276                     amParachute: begin
       
   277                                  newGear:= AddGear(hwRound(lx), hwRound(ly), gtParachute, 0, _0, _0, 0);
       
   278                                  PlaySound(sndParachute)
       
   279                                  end;
       
   280                     // we save CurWeapon^.Pos (in this case: cursor direction) by using it as (otherwise irrelevant) X value of the new gear.
       
   281                     amAirAttack: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 0, _0, _0, 0);
       
   282                    amMineStrike: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 1, _0, _0, 0);
       
   283                   amDrillStrike: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 3, _0, _0, CurWeapon^.Timer);
       
   284                        amNapalm: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 2, _0, _0, 0);
       
   285                     amBlowTorch: newGear:= AddGear(hwRound(lx), hwRound(ly), gtBlowTorch, 0, SignAs(_0_5, dX), _0, 0);
       
   286                        amGirder: newGear:= AddGear(0, 0, gtGirder, CurWeapon^.Pos, _0, _0, 0);
       
   287                      amTeleport: newGear:= AddGear(CurWeapon^.Pos, 0, gtTeleport, 0, _0, _0, 0);
       
   288                        amSwitch: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSwitcher, 0, _0, _0, 0);
       
   289                        amMortar: begin
       
   290                                  playSound(sndMortar);
       
   291                                  newGear:= AddGear(hwRound(lx), hwRound(ly), gtMortar,  0, xx*cMaxPower/cPowerDivisor, yy*cMaxPower/cPowerDivisor, 0);
       
   292                                  end;
       
   293                       amRCPlane: begin
       
   294                                  newGear:= AddGear(hwRound(lx), hwRound(ly), gtRCPlane,  0, xx * cMaxPower / cPowerDivisor / 4, yy * cMaxPower / cPowerDivisor / 4, 0);
       
   295                                  newGear^.SoundChannel:= LoopSound(sndRCPlane, nil)
       
   296                                  end;
       
   297                        amKamikaze: newGear:= AddGear(hwRound(lx), hwRound(ly), gtKamikaze, 0, xx * _0_5, yy * _0_5, 0);
       
   298                          amCake: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 3, hwRound(ly), gtCake, 0, xx, _0, 0);
       
   299                     amSeduction: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSeduction, 0, _0, _0, 0);
       
   300                    amWatermelon: newGear:= AddGear(hwRound(lx), hwRound(ly), gtWatermelon,  0, newDx, newDy, CurWeapon^.Timer);
       
   301                   amHellishBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtHellishBomb,    0, newDx, newDy, 0);
       
   302                         amDrill: newGear:= AddGear(hwRound(lx), hwRound(ly), gtDrill, 0, newDx, newDy, 0);
       
   303                       amBallgun: newGear:= AddGear(hwRound(X), hwRound(Y), gtBallgun,  0, xx * _0_5, yy * _0_5, 0);
       
   304                     amJetpack: newGear:= AddGear(hwRound(lx), hwRound(ly), gtJetpack, 0, _0, _0, 0);
       
   305                     amBirdy: begin
       
   306                              PlaySound(sndWhistle);
       
   307                              newGear:= AddGear(hwRound(lx), hwRound(ly) - 32, gtBirdy, 0, _0, _0, 0);
       
   308                              end;
       
   309                       amLowGravity: begin
       
   310                                     PlaySound(sndLowGravity);
       
   311                                     cGravity:= cMaxWindSpeed;
       
   312                                     cGravityf:= 0.00025
       
   313                                     end;
       
   314                       amExtraDamage:begin 
       
   315                                     PlaySound(sndHellishImpact4);
       
   316                                     cDamageModifier:= _1_5
       
   317                                     end;
       
   318                       amInvulnerable: Invulnerable:= true;
       
   319                       amExtraTime:  begin
       
   320                                     PlaySound(sndSwitchHog);
       
   321                                     TurnTimeLeft:= TurnTimeLeft + 30000
       
   322                                     end;
       
   323                       amLaserSight: cLaserSighting:= true;
       
   324                       amVampiric: begin
       
   325                                   PlaySound(sndOw1, Team^.voicepack);
       
   326                                   cVampiric:= true;
       
   327                                   end;
       
   328                       amPiano: begin
       
   329                                // Tuck the hedgehog away until the piano attack is completed
       
   330                                Unplaced:= true;
       
   331                                X:= _0;
       
   332                                Y:= _0;
       
   333                                newGear:= AddGear(TargetPoint.X, 0, gtPiano, 0, _0, _0, 0);
       
   334                                PauseMusic
       
   335                                end;
       
   336                       amFlamethrower: newGear:= AddGear(hwRound(X), hwRound(Y), gtFlamethrower,  0, xx * _0_5, yy * _0_5, 0);
       
   337                       amLandGun: newGear:= AddGear(hwRound(X), hwRound(Y), gtLandGun,  0, xx * _0_5, yy * _0_5, 0);
       
   338                     amResurrector: begin
       
   339                         newGear:= AddGear(hwRound(lx), hwRound(ly),
       
   340                                 gtResurrector, 0, _0, _0, 0);
       
   341                         newGear^.SoundChannel := LoopSound(sndResurrector);
       
   342                     end;
       
   343                    //amMelonStrike: AddGear(CurWeapon^.Pos, 0, gtAirAttack, 4, _0, _0, 0);
       
   344                      amStructure: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtStructure, gstWait, SignAs(_0_02, dX), _0, 3000);
       
   345                         amTardis: newGear:= AddGear(hwRound(X), hwRound(Y), gtTardis, 0, _0, _0, 5000);
       
   346                   end;
       
   347              case CurAmmoType of
       
   348                       amGrenade, amMolotov, 
       
   349                   amClusterBomb, amGasBomb, 
       
   350                       amBazooka, amSnowball, 
       
   351                           amBee, amSMine,
       
   352                        amMortar, amWatermelon,
       
   353                   amHellishBomb, amDrill: FollowGear:= newGear;
       
   354 
       
   355                       amShotgun, amPickHammer,
       
   356                          amRope, amDEagle,
       
   357                       amSineGun, amSniperRifle,
       
   358                     amFirePunch, amWhip,
       
   359                        amHammer, amBaseballBat,
       
   360                     amParachute, amBlowTorch,
       
   361                        amGirder, amTeleport,
       
   362                        amSwitch, amRCPlane,
       
   363                      amKamikaze, amCake,
       
   364                     amSeduction, amBallgun,
       
   365                       amJetpack, amBirdy,
       
   366                  amFlamethrower, amLandGun,
       
   367                   amResurrector, amStructure,
       
   368                        amTardis, amPiano: CurAmmoGear:= newGear;
       
   369                   end;
       
   370               if (CurAmmoType = amMine) or (CurAmmoType = amSMine) and (GameFlags and gfInfAttack <> 0) then newGear^.FlightTime:= GameTicks + 1000;
       
   371         if Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0 then
       
   372             begin
       
   373             newGear^.Target.X:= TargetPoint.X;
       
   374             newGear^.Target.Y:= TargetPoint.Y
       
   375             end;
       
   376 
       
   377         // Clear FollowGear if using on a rope/parachute/saucer etc so focus stays with the hog's movement
       
   378         if altUse then FollowGear:= nil;
       
   379 
       
   380         if (newGear <> nil) and ((Ammoz[newGear^.AmmoType].Ammo.Propz and ammoprop_SetBounce) <> 0) then
       
   381             begin
       
   382             elastic:=  int2hwfloat(CurWeapon^.Bounciness) / _1000;
       
   383 
       
   384             if elastic < _1 then newGear^.Elasticity:= newGear^.Elasticity * elastic
       
   385             else if elastic > _1 then newGear^.Elasticity:= _1 - ((_1-newGear^.Elasticity) / elastic);
       
   386 (* Experimented with friction modifier. Didn't seem helpful 
       
   387             fric:= int2hwfloat(CurWeapon^.Bounciness) / _250;
       
   388             if fric < _1 then newGear^.Friction:= newGear^.Friction * fric
       
   389             else if fric > _1 then newGear^.Friction:= _1 - ((_1-newGear^.Friction) / fric)*)
       
   390             end;
       
   391 
       
   392 
       
   393         uStats.AmmoUsed(CurAmmoType);
       
   394 
       
   395         if not (SpeechText = '') then
       
   396             begin
       
   397             speech:= AddVisualGear(0, 0, vgtSpeechBubble);
       
   398             if speech <> nil then
       
   399                begin
       
   400                speech^.Text:= SpeechText;
       
   401                speech^.Hedgehog:= Gear^.Hedgehog;
       
   402                speech^.FrameTicks:= SpeechType;
       
   403                end;
       
   404             SpeechText:= ''
       
   405             end;
       
   406 
       
   407         Power:= 0;
       
   408         if (CurAmmoGear <> nil)
       
   409            and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) = 0){check for dropping ammo from rope} then
       
   410            begin
       
   411            Message:= Message or gmAttack;
       
   412            CurAmmoGear^.Message:= Message
       
   413            end else begin
       
   414            if not CurrentTeam^.ExtDriven and
       
   415              ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0) then SendIPC('a');
       
   416            AfterAttack;
       
   417            end
       
   418         end else Message:= Message and (not gmAttack);
       
   419      end;
       
   420      TargetPoint.X := NoPointX;
       
   421      ScriptCall('onHogAttack');
       
   422 end;
       
   423 
       
   424 procedure AfterAttack;
       
   425 var s: shortstring;
       
   426     a: TAmmoType;
       
   427 begin
       
   428 with CurrentHedgehog^.Gear^,
       
   429         CurrentHedgehog^ do
       
   430     begin
       
   431     a:= CurAmmoType;
       
   432     State:= State and (not gstAttacking);
       
   433     if (Ammoz[a].Ammo.Propz and ammoprop_Effect) = 0 then
       
   434         begin
       
   435         Inc(MultiShootAttacks);
       
   436         
       
   437         if (Ammoz[a].Ammo.NumPerTurn >= MultiShootAttacks) then
       
   438             begin
       
   439             s:= inttostr(Ammoz[a].Ammo.NumPerTurn - MultiShootAttacks + 1);
       
   440             AddCaption(format(trmsg[sidRemaining], s), cWhiteColor, capgrpAmmostate);
       
   441             end;
       
   442         
       
   443         if (Ammoz[a].Ammo.NumPerTurn >= MultiShootAttacks) or
       
   444             ((GameFlags and gfMultiWeapon) <> 0) then
       
   445             begin
       
   446             isInMultiShoot:= true
       
   447             end
       
   448         else
       
   449             begin
       
   450             OnUsedAmmo(CurrentHedgehog^);
       
   451             if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) and (((GameFlags and gfInfAttack) = 0) or PlacingHogs) then
       
   452                 begin
       
   453                 if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
       
   454                 TurnTimeLeft:=(Ammoz[a].TimeAfterTurn * cGetAwayTime) div 100;
       
   455                 end;
       
   456             if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) then State:= State or gstAttacked;
       
   457             if (Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) <> 0 then ApplyAmmoChanges(CurrentHedgehog^)
       
   458             end;
       
   459         end
       
   460     else
       
   461         begin
       
   462         OnUsedAmmo(CurrentHedgehog^);
       
   463         ApplyAmmoChanges(CurrentHedgehog^);
       
   464         end;
       
   465     AttackBar:= 0
       
   466     end
       
   467 end;
       
   468 
       
   469 ////////////////////////////////////////////////////////////////////////////////
       
   470 procedure doStepHedgehogDead(Gear: PGear);
       
   471 const frametime = 200;
       
   472       timertime = frametime * 6;
       
   473 begin
       
   474 if Gear^.Hedgehog^.Unplaced then exit;
       
   475 if Gear^.Timer > 1 then
       
   476     begin
       
   477     AllInactive:= false;
       
   478     dec(Gear^.Timer);
       
   479     if (Gear^.Timer mod frametime) = 0 then inc(Gear^.Pos)
       
   480     end 
       
   481 else if Gear^.Timer = 1 then
       
   482     begin
       
   483     Gear^.State:= Gear^.State or gstNoDamage;
       
   484     doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, CurrentHedgehog, EXPLAutoSound);
       
   485     AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtGrave, 0, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
       
   486     DeleteGear(Gear);
       
   487     SetAllToActive
       
   488     end 
       
   489 else // Gear^.Timer = 0
       
   490     begin
       
   491     AllInactive:= false;
       
   492     Gear^.Z:= cCurrHHZ;
       
   493     RemoveGearFromList(Gear);
       
   494     InsertGearToList(Gear);
       
   495     PlaySound(sndByeBye, Gear^.Hedgehog^.Team^.voicepack);
       
   496     Gear^.Pos:= 0;
       
   497     Gear^.Timer:= timertime
       
   498     end
       
   499 end;
       
   500 
       
   501 ////////////////////////////////////////////////////////////////////////////////
       
   502 procedure doStepHedgehogGone(Gear: PGear);
       
   503 const frametime = 65;
       
   504       timertime = frametime * 11;
       
   505 begin
       
   506 if Gear^.Hedgehog^.Unplaced then exit;
       
   507 if Gear^.Timer > 1 then
       
   508     begin
       
   509     AllInactive:= false;
       
   510     dec(Gear^.Timer);
       
   511     if (Gear^.Timer mod frametime) = 0 then inc(Gear^.Pos)
       
   512     end else
       
   513 if Gear^.Timer = 1 then
       
   514     begin
       
   515     DeleteGear(Gear);
       
   516     SetAllToActive
       
   517     end else // Gear^.Timer = 0
       
   518     begin
       
   519     AllInactive:= false;
       
   520     Gear^.Z:= cCurrHHZ;
       
   521     RemoveGearFromList(Gear);
       
   522     InsertGearToList(Gear);
       
   523     PlaySound(sndByeBye, Gear^.Hedgehog^.Team^.voicepack);
       
   524     PlaySound(sndWarp);
       
   525     Gear^.Pos:= 0;
       
   526     Gear^.Timer:= timertime
       
   527     end
       
   528 end;
       
   529 
       
   530 ////////////////////////////////////////////////////////////////////////////////
       
   531 procedure PickUp(HH, Gear: PGear);
       
   532 var s: shortstring;
       
   533     a: TAmmoType;
       
   534     i: LongInt;
       
   535     vga: PVisualGear;
       
   536 begin
       
   537 Gear^.Message:= gmDestroy;
       
   538 PlaySound(sndShotgunReload);
       
   539 if (Gear^.Pos and posCaseExplode) <> 0 then
       
   540     if (Gear^.Pos and posCasePoison) <> 0 then
       
   541         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound + EXPLPoisoned)
       
   542     else
       
   543         doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound)
       
   544 else if (Gear^.Pos and posCasePoison) <> 0 then
       
   545     doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound + EXPLPoisoned + EXPLNoDamage)
       
   546 else
       
   547 case Gear^.Pos of
       
   548        posCaseUtility,
       
   549        posCaseAmmo: begin
       
   550                     if Gear^.AmmoType <> amNothing then a:= Gear^.AmmoType 
       
   551                     else
       
   552                         begin
       
   553                         for i:= 0 to GameTicks and $7F do GetRandom(2); // Burn some random numbers
       
   554                         if Gear^.Pos = posCaseUtility then a:= GetUtility
       
   555                         else a:= GetAmmo
       
   556                         end;
       
   557                     AddAmmo(HH^.Hedgehog^, a);
       
   558 // Possibly needs to check shared clan ammo game flag once added.
       
   559 // On the other hand, no obvious reason that clan members shouldn't know what ammo another clan member picked up
       
   560                     if (not (HH^.Hedgehog^.Team^.ExtDriven 
       
   561                       or (HH^.Hedgehog^.BotLevel > 0)))
       
   562                       or (HH^.Hedgehog^.Team^.Clan^.ClanIndex = LocalClan)
       
   563                       or (GameType = gmtDemo)  then
       
   564                         begin
       
   565                         s:= trammo[Ammoz[a].NameId] + ' (+' + IntToStr(Ammoz[a].NumberInCase) + ')';
       
   566                         AddCaption(s, HH^.Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
       
   567 
       
   568                         // show ammo icon
       
   569                         vga:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtAmmo);
       
   570                         if vga <> nil then
       
   571                             vga^.Frame:= Longword(a);
       
   572                         end;
       
   573 
       
   574                     end;
       
   575      posCaseHealth: begin
       
   576                     inc(HH^.Health, Gear^.Health);
       
   577                     HH^.Hedgehog^.Effects[hePoisoned] := false;
       
   578                     str(Gear^.Health, s);
       
   579                     s:= '+' + s;
       
   580                     AddCaption(s, HH^.Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
       
   581                     RenderHealth(HH^.Hedgehog^);
       
   582                     RecountTeamHealth(HH^.Hedgehog^.Team);
       
   583 
       
   584                     i:= 0;
       
   585                     while i < Gear^.Health do
       
   586                         begin
       
   587                         vga:= AddVisualGear(hwRound(HH^.X), hwRound(HH^.Y), vgtStraightShot);
       
   588                         if vga <> nil then
       
   589                             with vga^ do
       
   590                                 begin
       
   591                                 Tint:= $00FF00FF;
       
   592                                 State:= ord(sprHealth)
       
   593                                 end;
       
   594                         inc(i, 5);
       
   595                         end;
       
   596                     end;
       
   597      end
       
   598 end;
       
   599 
       
   600 const StepTicks: LongWord = 0;
       
   601 
       
   602 procedure HedgehogStep(Gear: PGear);
       
   603 var PrevdX: LongInt;
       
   604     CurWeapon: PAmmo;
       
   605 begin
       
   606 CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);
       
   607 if ((Gear^.State and (gstAttacking or gstMoving)) = 0) then
       
   608    begin
       
   609    if isCursorVisible then
       
   610       with Gear^.Hedgehog^ do
       
   611         with CurWeapon^ do
       
   612           begin
       
   613           if (Gear^.Message and gmLeft  ) <> 0 then
       
   614              Pos:= (Pos - 1 + Ammoz[AmmoType].PosCount) mod Ammoz[AmmoType].PosCount
       
   615           else
       
   616           if (Gear^.Message and gmRight ) <> 0 then
       
   617              Pos:= (Pos + 1) mod Ammoz[AmmoType].PosCount
       
   618           else exit;
       
   619           StepTicks:= 200;
       
   620           exit
       
   621           end;
       
   622 
       
   623     if ((Gear^.Message and gmAnimate) <> 0) then
       
   624         begin
       
   625         Gear^.Message:= 0;
       
   626         Gear^.State:= Gear^.State or gstAnimation;
       
   627         Gear^.Tag:= Gear^.MsgParam;
       
   628         Gear^.Timer:= 0;
       
   629         Gear^.Pos:= 0
       
   630         end;
       
   631 
       
   632    if ((Gear^.Message and gmLJump ) <> 0) then
       
   633       begin
       
   634       Gear^.Message:= Gear^.Message and (not gmLJump);
       
   635       DeleteCI(Gear);
       
   636       if TestCollisionYwithGear(Gear, -1) = 0 then
       
   637          if not TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) then Gear^.Y:= Gear^.Y - _2 else
       
   638          if not TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) then Gear^.Y:= Gear^.Y - _1;
       
   639       if not (TestCollisionXwithGear(Gear, hwSign(Gear^.dX))
       
   640          or   (TestCollisionYwithGear(Gear, -1) <> 0)) then
       
   641          begin
       
   642          Gear^.dY:= -_0_15;
       
   643          if not cArtillery then Gear^.dX:= SignAs(_0_15, Gear^.dX);
       
   644          Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
       
   645          PlaySound(sndJump1, Gear^.Hedgehog^.Team^.voicepack);
       
   646          exit
       
   647          end;
       
   648       end;
       
   649 
       
   650    if ((Gear^.Message and gmHJump ) <> 0) then
       
   651       begin
       
   652       DeleteCI(Gear);
       
   653       Gear^.Message:= Gear^.Message and (not gmHJump);
       
   654 
       
   655       Gear^.dY:= -_0_2;
       
   656       SetLittle(Gear^.dX);
       
   657       Gear^.State:= Gear^.State or gstMoving or gstHHJumping;
       
   658       PlaySound(sndJump3, Gear^.Hedgehog^.Team^.voicepack);
       
   659       exit
       
   660       end;
       
   661 
       
   662    PrevdX:= hwSign(Gear^.dX);
       
   663    if (Gear^.Message and gmLeft  )<>0 then Gear^.dX:= -cLittle else
       
   664    if (Gear^.Message and gmRight )<>0 then Gear^.dX:=  cLittle else exit;
       
   665 
       
   666    if (Gear^.Message and (gmLeft or gmRight)) <> 0 then
       
   667       begin
       
   668       StepSoundTimer:= cHHStepTicks;
       
   669       end;
       
   670    
       
   671    StepTicks:= cHHStepTicks;
       
   672    if PrevdX <> hwSign(Gear^.dX) then
       
   673       begin
       
   674       FollowGear:= Gear;
       
   675       exit
       
   676       end;
       
   677    DeleteCI(Gear); // must be after exit!! (see previous line)
       
   678 
       
   679    Gear^.Hedgehog^.visStepPos:= (Gear^.Hedgehog^.visStepPos + 1) and 7;
       
   680    if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
       
   681       begin
       
   682       if not (TestCollisionXwithXYShift(Gear, _0, -6, hwSign(Gear^.dX))
       
   683          or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   684       if not (TestCollisionXwithXYShift(Gear, _0, -5, hwSign(Gear^.dX))
       
   685          or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   686       if not (TestCollisionXwithXYShift(Gear, _0, -4, hwSign(Gear^.dX))
       
   687          or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   688       if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX))
       
   689          or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   690       if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX))
       
   691          or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   692       if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX))
       
   693          or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   694       end;
       
   695 
       
   696    if (not cArtillery) and ((Gear^.Message and gmPrecise) = 0) and (not TestCollisionXwithGear(Gear, hwSign(Gear^.dX))) then
       
   697       Gear^.X:= Gear^.X + SignAs(_1, Gear^.dX);
       
   698 
       
   699    SetAllHHToActive;
       
   700 
       
   701    if TestCollisionYwithGear(Gear, 1) = 0 then
       
   702    begin
       
   703    Gear^.Y:= Gear^.Y + _1;
       
   704    if TestCollisionYwithGear(Gear, 1) = 0 then
       
   705    begin
       
   706    Gear^.Y:= Gear^.Y + _1;
       
   707    if TestCollisionYwithGear(Gear, 1) = 0 then
       
   708    begin
       
   709    Gear^.Y:= Gear^.Y + _1;
       
   710    if TestCollisionYwithGear(Gear, 1) = 0 then
       
   711    begin
       
   712    Gear^.Y:= Gear^.Y + _1;
       
   713    if TestCollisionYwithGear(Gear, 1) = 0 then
       
   714    begin
       
   715    Gear^.Y:= Gear^.Y + _1;
       
   716    if TestCollisionYwithGear(Gear, 1) = 0 then
       
   717    begin
       
   718    Gear^.Y:= Gear^.Y + _1;
       
   719    if TestCollisionYwithGear(Gear, 1) = 0 then
       
   720       begin
       
   721       Gear^.Y:= Gear^.Y - _6;
       
   722       Gear^.dY:= _0;
       
   723       Gear^.State:= Gear^.State or gstMoving;
       
   724       exit
       
   725       end;
       
   726    end
       
   727    end
       
   728    end
       
   729    end
       
   730    end
       
   731    end;
       
   732    AddGearCI(Gear)
       
   733    end
       
   734 end;
       
   735 
       
   736 procedure HedgehogChAngle(HHGear: PGear);
       
   737 var da: LongWord;
       
   738 begin
       
   739 with HHGear^.Hedgehog^ do
       
   740     if ((CurAmmoType = amRope) and 
       
   741         ((HHGear^.State and (gstMoving or gstHHJumping)) = gstMoving)) or
       
   742        ((CurAmmoType = amPortalGun) and 
       
   743         ((HHGear^.State and gstMoving) <> 0)) then da:= 2
       
   744     else da:= 1;
       
   745 
       
   746 if (((HHGear^.Message and gmPrecise) = 0) or ((GameTicks mod 5) = 1)) then
       
   747     if ((HHGear^.Message and gmUp) <> 0) and (HHGear^.Angle >= CurMinAngle + da) then dec(HHGear^.Angle, da)
       
   748     else
       
   749     if ((HHGear^.Message and gmDown) <> 0) and (HHGear^.Angle + da <= CurMaxAngle) then inc(HHGear^.Angle, da)
       
   750 end;
       
   751 
       
   752 
       
   753 ////////////////////////////////////////////////////////////////////////////////
       
   754 procedure doStepHedgehogMoving(Gear: PGear);
       
   755 var isFalling, isUnderwater: boolean;
       
   756     land: Word;
       
   757 begin
       
   758 land:= 0;
       
   759 isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius;
       
   760 if Gear^.dX.QWordValue > 8160437862 then Gear^.dX.QWordValue:= 8160437862;
       
   761 if Gear^.dY.QWordValue > 8160437862 then Gear^.dY.QWordValue:= 8160437862;
       
   762 
       
   763 if Gear^.Hedgehog^.Unplaced then
       
   764    begin
       
   765    Gear^.dY:= _0;
       
   766    Gear^.dX:= _0;
       
   767    Gear^.State:= Gear^.State and (not gstMoving);
       
   768    exit
       
   769    end;
       
   770 isFalling:= (Gear^.dY.isNegative) or not TestCollisionYKick(Gear, 1);
       
   771 if isFalling then
       
   772    begin
       
   773    if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then Gear^.dY:= _0;
       
   774    Gear^.State:= Gear^.State or gstMoving;
       
   775    if (CurrentHedgehog^.Gear = Gear)
       
   776         and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then 
       
   777         begin
       
   778         FollowGear:= Gear;
       
   779         end;
       
   780    if isUnderwater then Gear^.dY:= Gear^.dY + cGravity / _2
       
   781    else
       
   782        begin
       
   783        Gear^.dY:= Gear^.dY + cGravity;
       
   784 // this set of circumstances could be less complex if jumping was more clearly identified
       
   785        if ((GameFlags and gfMoreWind) <> 0) and 
       
   786           (((Gear^.Damage <> 0) or
       
   787           ((CurAmmoGear <> nil) and
       
   788             ((CurAmmoGear^.AmmoType = amJetpack) or
       
   789             (CurAmmoGear^.AmmoType = amBirdy))) or
       
   790           ((Gear^.dY.QWordValue + Gear^.dX.QWordValue) > _0_55.QWordValue)))
       
   791           then Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density
       
   792        end
       
   793    end 
       
   794 else
       
   795    begin
       
   796    land:= TestCollisionYwithGear(Gear, 1);
       
   797    if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_55.QWordValue) and ((land and lfIce) = 0)
       
   798       and ((Gear^.State and gstHHJumping) <> 0) then SetLittle(Gear^.dX);
       
   799 
       
   800    if not Gear^.dY.isNegative then
       
   801       begin
       
   802       CheckHHDamage(Gear);
       
   803 
       
   804       if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) and
       
   805          (Gear^.dX.QWordValue < _0_02.QWordValue) then Gear^.dX.isNegative:= not Gear^.dX.isNegative; // landing after high jump
       
   806 
       
   807       Gear^.State:= Gear^.State and (not (gstHHJumping or gstHHHJump));
       
   808       Gear^.dY:= _0;
       
   809       end else Gear^.dY:= Gear^.dY + cGravity;
       
   810 
       
   811    if ((Gear^.State and gstMoving) <> 0) then
       
   812        begin
       
   813        if land and lfIce <> 0 then
       
   814            begin
       
   815            Gear^.dX:= Gear^.dX * (_1 - (_1 - Gear^.Friction) / _2)
       
   816            end
       
   817        else Gear^.dX:= Gear^.dX * Gear^.Friction;
       
   818        end
       
   819    end;
       
   820 
       
   821 if (Gear^.State <> 0) then DeleteCI(Gear);
       
   822 
       
   823 if isUnderwater then
       
   824    begin
       
   825    Gear^.dY:= Gear^.dY * _0_999;
       
   826    Gear^.dX:= Gear^.dX * _0_999;
       
   827    end;
       
   828 
       
   829 if (Gear^.State and gstMoving) <> 0 then
       
   830    if TestCollisionXKick(Gear, hwSign(Gear^.dX)) then
       
   831       if not isFalling then
       
   832          if hwAbs(Gear^.dX) > _0_01 then
       
   833             if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -1, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_96; Gear^.Y:= Gear^.Y - _1 end else
       
   834             if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -2, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_93; Gear^.Y:= Gear^.Y - _2 end else
       
   835             if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -3, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_9 ; Gear^.Y:= Gear^.Y - _3 end else
       
   836             if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -4, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_87; Gear^.Y:= Gear^.Y - _4 end else
       
   837             if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -5, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_84; Gear^.Y:= Gear^.Y - _5 end else
       
   838             if hwAbs(Gear^.dX) > _0_02 then Gear^.dX:= -Gear^.Elasticity * Gear^.dX
       
   839                                    else begin
       
   840                                         Gear^.State:= Gear^.State and (not gstMoving);
       
   841                                         while TestCollisionYWithGear(Gear,1) = 0 do Gear^.Y:= Gear^.Y+_1;
       
   842                                         SetLittle(Gear^.dX)
       
   843                                         end
       
   844             else begin
       
   845                  Gear^.State:= Gear^.State and (not gstMoving);
       
   846                  while TestCollisionYWithGear(Gear,1) = 0 do Gear^.Y:= Gear^.Y+_1;
       
   847                  SetLittle(Gear^.dX)
       
   848                  end
       
   849          else if (hwAbs(Gear^.dX) > cLittle)
       
   850                 and ((Gear^.State and gstHHJumping) = 0)
       
   851                 then Gear^.dX:= -Gear^.Elasticity * Gear^.dX
       
   852                 else SetLittle(Gear^.dX);
       
   853 
       
   854 if (not isFalling) and
       
   855    (hwAbs(Gear^.dX) + hwAbs(Gear^.dY) < _0_03) then
       
   856    begin
       
   857    Gear^.State:= Gear^.State and (not gstWinner);
       
   858    Gear^.State:= Gear^.State and (not gstMoving);
       
   859    while TestCollisionYWithGear(Gear,1) = 0 do Gear^.Y:= Gear^.Y+_1;
       
   860    SetLittle(Gear^.dX);
       
   861    Gear^.dY:= _0
       
   862    end else Gear^.State:= Gear^.State or gstMoving;
       
   863 
       
   864 if (Gear^.State and gstMoving) <> 0 then
       
   865    begin
       
   866    Gear^.State:= Gear^.State and (not gstAnimation);
       
   867 // ARTILLERY but not being moved by explosions
       
   868    Gear^.X:= Gear^.X + Gear^.dX;
       
   869    Gear^.Y:= Gear^.Y + Gear^.dY;
       
   870    if (not Gear^.dY.isNegative) and
       
   871       (not TestCollisionYKick(Gear, 1)) and
       
   872        TestCollisionYwithXYShift(Gear, 0, 1, 1) then
       
   873       begin
       
   874       CheckHHDamage(Gear);
       
   875       Gear^.dY:= _0;
       
   876       Gear^.Y:= Gear^.Y + _1
       
   877       end;
       
   878    CheckGearDrowning(Gear);
       
   879    // hide target cursor if current hog is drowning
       
   880    if (Gear^.State and gstDrowning) <> 0 then
       
   881        if (CurrentHedgehog^.Gear = Gear) then
       
   882           isCursorVisible:= false
       
   883    end;
       
   884 
       
   885 if (hwAbs(Gear^.dY) > _0) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then
       
   886     begin
       
   887     inc(Gear^.FlightTime);
       
   888     if Gear^.FlightTime = 3000 then
       
   889         begin
       
   890         AddCaption(GetEventString(eidHomerun), cWhiteColor, capgrpMessage);
       
   891         PlaySound(sndHomerun)
       
   892         end;
       
   893     end
       
   894 else
       
   895     begin
       
   896     uStats.hedgehogFlight(Gear, Gear^.FlightTime);
       
   897     Gear^.FlightTime:= 0;
       
   898     end;
       
   899 
       
   900 end;
       
   901 
       
   902 procedure doStepHedgehogDriven(HHGear: PGear);
       
   903 var t: PGear;
       
   904     wasJumping: boolean;
       
   905     Hedgehog: PHedgehog;
       
   906 begin
       
   907 Hedgehog:= HHGear^.Hedgehog;
       
   908 if isInMultiShoot then
       
   909    HHGear^.Message:= 0;
       
   910 
       
   911 if ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_Utility) <> 0) and isInMultiShoot then 
       
   912     AllInactive:= true
       
   913 else if not isInMultiShoot then AllInactive:= false;
       
   914 
       
   915 if (TurnTimeLeft = 0) or (HHGear^.Damage > 0) then
       
   916     begin
       
   917     if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
       
   918     TurnTimeLeft:= 0;
       
   919     isCursorVisible:= false;
       
   920     HHGear^.State:= HHGear^.State and (not (gstHHDriven or gstAnimation or gstAttacking));
       
   921     AttackBar:= 0;
       
   922     if HHGear^.Damage > 0 then
       
   923         HHGear^.State:= HHGear^.State and (not (gstHHJumping or gstHHHJump));
       
   924     exit
       
   925     end;
       
   926 
       
   927 if (HHGear^.State and gstAnimation) <> 0 then
       
   928     begin
       
   929     HHGear^.Message:= 0;
       
   930     if (HHGear^.Pos = Wavez[TWave(HHGear^.Tag)].VoiceDelay) and (HHGear^.Timer = 0) then PlaySound(Wavez[TWave(HHGear^.Tag)].Voice, Hedgehog^.Team^.voicepack);
       
   931     inc(HHGear^.Timer);
       
   932     if HHGear^.Timer = Wavez[TWave(HHGear^.Tag)].Interval then
       
   933         begin
       
   934         HHGear^.Timer:= 0;
       
   935         inc(HHGear^.Pos);
       
   936         if HHGear^.Pos = Wavez[TWave(HHGear^.Tag)].FramesCount then
       
   937             HHGear^.State:= HHGear^.State and (not gstAnimation)
       
   938         end;
       
   939     exit
       
   940     end;
       
   941 
       
   942 if ((HHGear^.State and gstMoving) <> 0)
       
   943     or (StepTicks = cHHStepTicks)
       
   944     or (CurAmmoGear <> nil) then // we are moving
       
   945     begin
       
   946     with Hedgehog^ do
       
   947         if (CurAmmoGear = nil)
       
   948         and (HHGear^.dY > _0_39)
       
   949         and (CurAmmoType = amParachute) then HHGear^.Message:= HHGear^.Message or gmAttack;
       
   950     // check for case with ammo
       
   951     t:= CheckGearNear(HHGear, gtCase, 36, 36);
       
   952     if t <> nil then
       
   953         PickUp(HHGear, t)
       
   954     end;
       
   955 
       
   956 if (CurAmmoGear = nil) then
       
   957     if (((HHGear^.Message and gmAttack) <> 0)
       
   958         or ((HHGear^.State and gstAttacking) <> 0)) then
       
   959         Attack(HHGear) // should be before others to avoid desync with '/put' msg and changing weapon msgs
       
   960     else
       
   961 else 
       
   962     with Hedgehog^ do
       
   963         if ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0)
       
   964             and ((HHGear^.Message and gmLJump) <> 0)
       
   965             and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
       
   966             begin
       
   967             Attack(HHGear);
       
   968             HHGear^.Message:= HHGear^.Message and (not gmLJump)
       
   969             end;
       
   970 
       
   971 if (CurAmmoGear = nil)
       
   972     or ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0) 
       
   973     or ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) then
       
   974     begin
       
   975     if ((HHGear^.Message and gmSlot) <> 0) then
       
   976         if ChangeAmmo(HHGear) then ApplyAmmoChanges(Hedgehog^);
       
   977 
       
   978     if ((HHGear^.Message and gmWeapon) <> 0) then HHSetWeapon(HHGear);
       
   979 
       
   980     if ((HHGear^.Message and gmTimer) <> 0) then HHSetTimer(HHGear);
       
   981     end;
       
   982 
       
   983 if CurAmmoGear <> nil then
       
   984    begin
       
   985    CurAmmoGear^.Message:= HHGear^.Message;
       
   986    exit
       
   987    end;
       
   988 
       
   989 if not isInMultiShoot then
       
   990    HedgehogChAngle(HHGear);
       
   991 
       
   992 if (HHGear^.State and gstMoving) <> 0 then
       
   993     begin
       
   994     wasJumping:= ((HHGear^.State and gstHHJumping) <> 0);
       
   995 
       
   996     if ((HHGear^.Message and gmHJump) <> 0) and
       
   997         wasJumping and
       
   998         ((HHGear^.State and gstHHHJump) = 0) then
       
   999         if (not (hwAbs(HHGear^.dX) > cLittle)) and (HHGear^.dY < -_0_02) then
       
  1000             begin
       
  1001             HHGear^.State:= HHGear^.State or gstHHHJump;
       
  1002             HHGear^.dY:= -_0_25;
       
  1003             if not cArtillery then HHGear^.dX:= -SignAs(_0_02, HHGear^.dX);
       
  1004             PlaySound(sndJump2, Hedgehog^.Team^.voicepack)
       
  1005             end;
       
  1006 
       
  1007     HHGear^.Message:= HHGear^.Message and (not (gmLJump or gmHJump));
       
  1008 
       
  1009     if (not cArtillery) and wasJumping and
       
  1010         TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX);
       
  1011 
       
  1012     if Hedgehog^.Gear <> nil then doStepHedgehogMoving(HHGear);
       
  1013 
       
  1014     if ((HHGear^.State and (gstMoving or gstDrowning)) = 0) then
       
  1015         begin
       
  1016         AddGearCI(HHGear);
       
  1017         if wasJumping then
       
  1018             StepTicks:= 410
       
  1019         else
       
  1020             StepTicks:= 95
       
  1021         end;
       
  1022     exit
       
  1023     end;
       
  1024 
       
  1025     if not isInMultiShoot and (Hedgehog^.Gear <> nil) then
       
  1026         begin
       
  1027         if StepTicks > 0 then dec(StepTicks);
       
  1028         if (StepTicks = 0) then HedgehogStep(HHGear)
       
  1029         end
       
  1030 end;
       
  1031 
       
  1032 ////////////////////////////////////////////////////////////////////////////////
       
  1033 procedure doStepHedgehogFree(Gear: PGear);
       
  1034 var prevState: Longword;
       
  1035 begin
       
  1036 prevState:= Gear^.State;
       
  1037 
       
  1038 doStepHedgehogMoving(Gear);
       
  1039 
       
  1040 if (Gear^.State and (gstMoving or gstDrowning)) <> 0 then
       
  1041     begin
       
  1042     if Gear^.Damage > 0 then CalcRotationDirAngle(Gear);
       
  1043     AllInactive:= false;
       
  1044     exit
       
  1045     end;
       
  1046 
       
  1047 if (Gear^.Health = 0) then
       
  1048     begin
       
  1049     if PrvInactive or ((GameFlags and gfInfAttack) <> 0) then
       
  1050         begin
       
  1051         Gear^.Timer:= 0;
       
  1052         FollowGear:= Gear;
       
  1053         PrvInactive:= false;
       
  1054         AllInactive:= false;
       
  1055 
       
  1056         if (Gear^.State and gstHHGone) = 0 then
       
  1057             begin
       
  1058             Gear^.Hedgehog^.Effects[hePoisoned] := false;
       
  1059             if Gear^.Hedgehog^.Effects[heResurrectable] then begin
       
  1060                 ResurrectHedgehog(Gear);
       
  1061             end else 
       
  1062                 begin
       
  1063                 Gear^.State:= (Gear^.State or gstHHDeath) and (not gstAnimation);
       
  1064                 Gear^.doStep:= @doStepHedgehogDead;
       
  1065                 // Death message
       
  1066                 AddCaption(Format(GetEventString(eidDied), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
       
  1067                 end;
       
  1068             end
       
  1069         else
       
  1070             begin
       
  1071             Gear^.State:= Gear^.State and (not gstAnimation);
       
  1072             Gear^.doStep:= @doStepHedgehogGone;
       
  1073 
       
  1074             // Gone message
       
  1075             AddCaption(Format(GetEventString(eidGone), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
       
  1076             end
       
  1077         end;
       
  1078     exit
       
  1079     end;
       
  1080 
       
  1081 if ((Gear^.State and gstWait) = 0) and
       
  1082     (prevState <> Gear^.State) then
       
  1083     begin
       
  1084     Gear^.State:= Gear^.State or gstWait;
       
  1085     Gear^.Timer:= 150
       
  1086     end else
       
  1087     begin
       
  1088     if Gear^.Timer = 0 then
       
  1089         begin
       
  1090         Gear^.State:= Gear^.State and (not (gstWait or gstLoser or gstWinner or gstAttacked or gstNotKickable or gstHHChooseTarget));
       
  1091         Gear^.Active:= false;
       
  1092         AddGearCI(Gear);
       
  1093         exit
       
  1094         end else dec(Gear^.Timer)
       
  1095     end;
       
  1096 
       
  1097 AllInactive:= false
       
  1098 end;
       
  1099 
       
  1100 ////////////////////////////////////////////////////////////////////////////////
       
  1101 procedure doStepHedgehog(Gear: PGear);
       
  1102 (*
       
  1103 var x,y,tx,ty: LongInt;
       
  1104     tdX, tdY, slope: hwFloat; 
       
  1105     land: Word; *)
       
  1106 var slope: hwFloat; 
       
  1107 begin
       
  1108 if (Gear^.Message and gmDestroy) <> 0 then
       
  1109     begin
       
  1110     DeleteGear(Gear);
       
  1111     exit
       
  1112     end;
       
  1113 
       
  1114 if (Gear^.State and gstHHDriven) = 0 then
       
  1115     doStepHedgehogFree(Gear)
       
  1116 else
       
  1117     begin
       
  1118     with Gear^.Hedgehog^ do
       
  1119         if Team^.hasGone then
       
  1120             TeamGoneEffect(Team^)
       
  1121         else
       
  1122             doStepHedgehogDriven(Gear)
       
  1123     end;
       
  1124 if (Gear^.Message and (gmAllStoppable or gmLJump or gmHJump) = 0) and
       
  1125    (Gear^.State and (gstHHJumping or gstHHHJump or gstAttacking) = 0) and
       
  1126    (not Gear^.dY.isNegative) and
       
  1127    (GameTicks mod (100*LongWOrd(hwRound(cMaxWindSpeed*2/cGravity))) = 0) and
       
  1128    (TestCollisionYwithGear(Gear, 1) and lfIce <> 0) then
       
  1129     begin
       
  1130     slope:= CalcSlopeBelowGear(Gear);
       
  1131     Gear^.dX:=Gear^.dX+slope*_0_07;
       
  1132     if slope.QWordValue <> 0 then Gear^.State:= Gear^.State or gstMoving;
       
  1133 (*
       
  1134     x:= hwRound(Gear^.X);
       
  1135     y:= hwRound(Gear^.Y);
       
  1136     AddVisualGear(x, y, vgtSmokeTrace);
       
  1137     AddVisualGear(x - hwRound(_5*slope), y + hwRound(_5*slope), vgtSmokeTrace);
       
  1138     AddVisualGear(x + hwRound(_5*slope), y - hwRound(_5*slope), vgtSmokeTrace);
       
  1139     AddVisualGear(x - hwRound(_20 * slope), y + hwRound(_20 * slope), vgtSmokeTrace);
       
  1140     AddVisualGear(x + hwRound(_20 * slope), y - hwRound(_20 * slope), vgtSmokeTrace);
       
  1141     AddVisualGear(x - hwRound(_30 * slope), y + hwRound(_30 * slope), vgtSmokeTrace);
       
  1142     AddVisualGear(x + hwRound(_30 * slope), y - hwRound(_30 * slope), vgtSmokeTrace);
       
  1143     AddVisualGear(x - hwRound(_40 * slope), y + hwRound(_40 * slope), vgtSmokeTrace);
       
  1144     AddVisualGear(x + hwRound(_40 * slope), y - hwRound(_40 * slope), vgtSmokeTrace);
       
  1145     AddVisualGear(x - hwRound(_50 * slope), y + hwRound(_50 * slope), vgtSmokeTrace);
       
  1146     AddVisualGear(x + hwRound(_50 * slope), y - hwRound(_50 * slope), vgtSmokeTrace); *)
       
  1147     end
       
  1148 end;
       
  1149 
       
  1150 end.