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