hedgewars/uGears.pas
changeset 6543 697e9b730189
parent 6531 c938a35588af
child 6580 6155187bf599
equal deleted inserted replaced
6542:936956dfa6c9 6543:697e9b730189
    50 procedure DrawGears;
    50 procedure DrawGears;
    51 procedure FreeGearsList;
    51 procedure FreeGearsList;
    52 procedure AddMiscGears;
    52 procedure AddMiscGears;
    53 procedure AssignHHCoords;
    53 procedure AssignHHCoords;
    54 function  GearByUID(uid : Longword) : PGear;
    54 function  GearByUID(uid : Longword) : PGear;
    55 procedure InsertGearToList(Gear: PGear);
    55 procedure doStepDrowningGear(Gear: PGear);
    56 procedure RemoveGearFromList(Gear: PGear);
       
    57 procedure DeleteGear(Gear: PGear); 
       
    58 
    56 
    59 
    57 
    60 implementation
    58 implementation
    61 uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics,
    59 uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics,
    62      uAIMisc, uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uMobile, uVariables,
    60      uAIMisc, uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uMobile, uVariables,
    63      uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture,
    61      uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture,
    64      uGearsHedgehog;
    62      uGearsHedgehog, uGearsUtils, uGearsList;
    65 
    63 
    66 
    64 
    67 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward;
    65 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward;
    68 //procedure AmmoFlameWork(Ammo: PGear); forward;
    66 //procedure AmmoFlameWork(Ammo: PGear); forward;
    69 function  GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray; forward;
    67 function  GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray; forward;
    70 procedure SpawnBoxOfSmth; forward;
    68 procedure SpawnBoxOfSmth; forward;
    71 procedure ShotgunShot(Gear: PGear); forward;
    69 procedure ShotgunShot(Gear: PGear); forward;
    72 procedure PickUp(HH, Gear: PGear); forward;
       
    73 procedure HHSetWeapon(HHGear: PGear); forward;
       
    74 procedure doStepCase(Gear: PGear); forward;
    70 procedure doStepCase(Gear: PGear); forward;
    75 
    71 
    76 // For better maintainability the step handlers of gears are stored in
    72 // For better maintainability the step handlers of gears are stored in
    77 // separate files.
    73 // separate files.
    78 // Note: step handlers of gears that are hedgehogs are in a different file
    74 // Note: step handlers of gears that are hedgehogs are in a different file
    79 //       than the handlers for all other gears.
    75 //       than the handlers for all other gears.
    80 {$INCLUDE "GSHandlers.inc"}
    76 {$INCLUDE "GSHandlers.inc"}
    81 
    77 
    82 const doStepHandlers: array[TGearType] of TGearStepProcedure = (
    78 function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs
       
    79 var Gear: PGear;
       
    80     dmg: LongInt;
       
    81 begin
       
    82 CheckNoDamage:= true;
       
    83 Gear:= GearsList;
       
    84 while Gear <> nil do
       
    85     begin
       
    86     if (Gear^.Kind = gtHedgehog) and (((GameFlags and gfInfAttack) = 0) or ((Gear^.dX.QWordValue < _0_000004.QWordValue) and (Gear^.dY.QWordValue < _0_000004.QWordValue))) then
       
    87         begin
       
    88         if (not isInMultiShoot) then inc(Gear^.Damage, Gear^.Karma);
       
    89         if (Gear^.Damage <> 0) and
       
    90         (not Gear^.Invulnerable) then
       
    91             begin
       
    92             CheckNoDamage:= false;
       
    93 
       
    94             dmg:= Gear^.Damage;
       
    95             if Gear^.Health < dmg then
       
    96                 begin
       
    97                 Gear^.Active:= true;
       
    98                 Gear^.Health:= 0
       
    99                 end
       
   100             else
       
   101                 dec(Gear^.Health, dmg);
       
   102 
       
   103             if (Gear^.Hedgehog^.Team = CurrentTeam) and
       
   104                (Gear^.Damage <> Gear^.Karma) and
       
   105                 (not Gear^.Hedgehog^.King) and
       
   106                 (not Gear^.Hedgehog^.Effects[hePoisoned]) and
       
   107                 (not SuddenDeathDmg) then
       
   108                 Gear^.State:= Gear^.State or gstLoser;
       
   109 
       
   110             spawnHealthTagForHH(Gear, dmg);
       
   111 
       
   112             RenderHealth(Gear^.Hedgehog^);
       
   113             RecountTeamHealth(Gear^.Hedgehog^.Team);
       
   114 
       
   115             end;
       
   116         if (not isInMultiShoot) then Gear^.Karma:= 0;
       
   117         Gear^.Damage:= 0
       
   118         end;
       
   119     Gear:= Gear^.NextGear
       
   120     end;
       
   121 end;
       
   122 
       
   123 procedure HealthMachine;
       
   124 var Gear: PGear;
       
   125     team: PTeam;
       
   126        i: LongWord;
       
   127     flag: Boolean;
       
   128      tmp: LongWord;
       
   129 begin
       
   130     Gear:= GearsList;
       
   131 
       
   132     while Gear <> nil do
       
   133     begin
       
   134         if Gear^.Kind = gtHedgehog then
       
   135             begin
       
   136             tmp:= 0;
       
   137             if Gear^.Hedgehog^.Effects[hePoisoned] then
       
   138                 begin
       
   139                 inc(tmp, ModifyDamage(5, Gear));
       
   140                 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth)  // does not need a minimum check since <= 1 basically disables it
       
   141                 end;
       
   142             if (TotalRounds > cSuddenDTurns - 1) then
       
   143                 begin
       
   144                 inc(tmp, cHealthDecrease);
       
   145                 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, cHealthDecrease)
       
   146                 end;
       
   147             if Gear^.Hedgehog^.King then
       
   148                 begin
       
   149                 flag:= false;
       
   150                 team:= Gear^.Hedgehog^.Team;
       
   151                 for i:= 0 to Pred(team^.HedgehogsNumber) do
       
   152                     if (team^.Hedgehogs[i].Gear <> nil) and
       
   153                         (not team^.Hedgehogs[i].King) and
       
   154                         (team^.Hedgehogs[i].Gear^.Health > team^.Hedgehogs[i].Gear^.Damage)
       
   155                     then flag:= true;
       
   156                 if not flag then
       
   157                     begin
       
   158                     inc(tmp, 5);
       
   159                     if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, 5)
       
   160                     end
       
   161                 end;
       
   162             if tmp > 0 then 
       
   163                 begin
       
   164                 inc(Gear^.Damage, min(tmp, max(0,Gear^.Health - 1 - Gear^.Damage)));
       
   165                 HHHurt(Gear^.Hedgehog, dsPoison);
       
   166                 end
       
   167             end;
       
   168 
       
   169         Gear:= Gear^.NextGear
       
   170     end;
       
   171 end;
       
   172 
       
   173 procedure ProcessGears;
       
   174 const delay: LongWord = 0;
       
   175       delay2: LongWord = 0;
       
   176     step: (stDelay, stChDmg, stSweep, stTurnReact,
       
   177             stAfterDelay, stChWin, stWater, stChWin2, stHealth,
       
   178             stSpawn, stNTurn) = stDelay;
       
   179 var Gear, t: PGear;
       
   180     i, AliveCount: LongInt;
       
   181     s: shortstring;
       
   182 begin
       
   183 PrvInactive:= AllInactive;
       
   184 AllInactive:= true;
       
   185 
       
   186 if (StepSoundTimer > 0) and (StepSoundChannel < 0) then
       
   187     StepSoundChannel:= LoopSound(sndSteps)
       
   188 else if (StepSoundTimer = 0) and (StepSoundChannel > -1) then
       
   189     begin
       
   190     StopSound(StepSoundChannel);
       
   191     StepSoundChannel:= -1
       
   192     end;
       
   193 
       
   194 if StepSoundTimer > 0 then
       
   195     dec(StepSoundTimer, 1);
       
   196 
       
   197 t:= GearsList;
       
   198 while t <> nil do
       
   199     begin
       
   200     Gear:= t;
       
   201     t:= Gear^.NextGear;
       
   202 
       
   203     if Gear^.Active then
       
   204         begin
       
   205         if Gear^.RenderTimer and (Gear^.Timer > 500) and ((Gear^.Timer mod 1000) = 0) then
       
   206             begin
       
   207             FreeTexture(Gear^.Tex);
       
   208             Gear^.Tex:= RenderStringTex(inttostr(Gear^.Timer div 1000), cWhiteColor, fntSmall);
       
   209             end;
       
   210         Gear^.doStep(Gear);
       
   211         // might be useful later
       
   212         //ScriptCall('onGearStep', Gear^.uid);
       
   213         end
       
   214     end;
       
   215 
       
   216 if AllInactive then
       
   217 case step of
       
   218     stDelay: begin
       
   219         if delay = 0 then
       
   220             delay:= cInactDelay
       
   221         else
       
   222             dec(delay);
       
   223 
       
   224         if delay = 0 then
       
   225             inc(step)
       
   226         end;
       
   227     stChDmg: if CheckNoDamage then inc(step) else step:= stDelay;
       
   228     stSweep: if SweepDirty then
       
   229                 begin
       
   230                 SetAllToActive;
       
   231                 step:= stChDmg
       
   232                 end else inc(step);
       
   233     stTurnReact: begin
       
   234         if (not bBetweenTurns) and (not isInMultiShoot) then
       
   235             begin
       
   236             uStats.TurnReaction;
       
   237             inc(step)
       
   238         end else
       
   239             inc(step, 2);
       
   240         end;
       
   241     stAfterDelay: begin
       
   242         if delay = 0 then
       
   243             delay:= cInactDelay
       
   244         else
       
   245             dec(delay);
       
   246 
       
   247         if delay = 0 then
       
   248         inc(step)
       
   249         end;
       
   250     stChWin: begin
       
   251             CheckForWin;
       
   252             inc(step)
       
   253             end;
       
   254     stWater: if (not bBetweenTurns) and (not isInMultiShoot) then
       
   255                 begin
       
   256                 if TotalRounds = cSuddenDTurns + 1 then bWaterRising:= true;
       
   257 
       
   258                 if bWaterRising and (cWaterRise > 0) then
       
   259                     AddGear(0, 0, gtWaterUp, 0, _0, _0, 0)^.Tag:= cWaterRise;
       
   260 
       
   261                 inc(step)
       
   262                 end else inc(step);
       
   263     stChWin2: begin
       
   264             CheckForWin;
       
   265             inc(step)
       
   266             end;
       
   267     stHealth: begin
       
   268             if (cWaterRise <> 0) or (cHealthDecrease <> 0) then
       
   269                 begin
       
   270                 if (TotalRounds = cSuddenDTurns) and (not SuddenDeath) and (not isInMultiShoot) then
       
   271                     begin
       
   272                     SuddenDeath:= true;
       
   273                     if cHealthDecrease <> 0 then
       
   274                         begin
       
   275                         SuddenDeathDmg:= true;
       
   276                         
       
   277                         // flash
       
   278                         ScreenFade:= sfFromWhite;
       
   279                         ScreenFadeValue:= sfMax;
       
   280                         ScreenFadeSpeed:= 1;
       
   281                         
       
   282                         ChangeToSDClouds;
       
   283                         ChangeToSDFlakes;
       
   284                         glClearColor(SDSkyColor.r * (SDTint/255) / 255, SDSkyColor.g * (SDTint/255) / 255, SDSkyColor.b * (SDTint/255) / 255, 0.99);
       
   285                         Ammoz[amTardis].SkipTurns:= 9999;
       
   286                         Ammoz[amTardis].Probability:= 0;
       
   287                         end;
       
   288                     AddCaption(trmsg[sidSuddenDeath], cWhiteColor, capgrpGameState);
       
   289                     playSound(sndSuddenDeath);
       
   290                     StopMusic //No SDMusic for now
       
   291                     //MusicFN:= SDMusic;
       
   292                     //ChangeMusic
       
   293                     end
       
   294                 else if (TotalRounds < cSuddenDTurns) and (not isInMultiShoot) then
       
   295                     begin
       
   296                     i:= cSuddenDTurns - TotalRounds;
       
   297                     s:= inttostr(i);
       
   298                     if i = 1 then
       
   299                         AddCaption(trmsg[sidRoundSD], cWhiteColor, capgrpGameState)
       
   300                     else if (i = 2) or ((i > 0) and ((i mod 50 = 0) or ((i <= 25) and (i mod 5 = 0)))) then
       
   301                         AddCaption(Format(trmsg[sidRoundsSD], s), cWhiteColor, capgrpGameState);
       
   302                     end;
       
   303                 end;
       
   304             if bBetweenTurns
       
   305                 or isInMultiShoot
       
   306                 or (TotalRounds = -1) then inc(step)
       
   307             else begin
       
   308                 bBetweenTurns:= true;
       
   309                 HealthMachine;
       
   310                 step:= stChDmg
       
   311                 end
       
   312             end;
       
   313     stSpawn: begin
       
   314             if not isInMultiShoot then SpawnBoxOfSmth;
       
   315             inc(step)
       
   316             end;
       
   317     stNTurn: begin
       
   318             if isInMultiShoot then
       
   319                 isInMultiShoot:= false
       
   320             else begin
       
   321                 // delayed till after 0.9.12
       
   322                 // reset to default zoom
       
   323                 //ZoomValue:= ZoomDefault;
       
   324                 with CurrentHedgehog^ do
       
   325                     if (Gear <> nil)
       
   326                         and ((Gear^.State and gstAttacked) = 0)
       
   327                         and (MultiShootAttacks > 0) then OnUsedAmmo(CurrentHedgehog^);
       
   328 
       
   329                 EndTurnCleanup;
       
   330 
       
   331                 FreeActionsList; // could send -left, -right and similar commands, so should be called before /nextturn
       
   332 
       
   333                 ParseCommand('/nextturn', true);
       
   334                 SwitchHedgehog;
       
   335 
       
   336                 AfterSwitchHedgehog;
       
   337                 bBetweenTurns:= false
       
   338                 end;
       
   339             step:= Low(step)
       
   340             end;
       
   341     end
       
   342 else if ((GameFlags and gfInfAttack) <> 0) then
       
   343     begin
       
   344     if delay2 = 0 then
       
   345         delay2:= cInactDelay * 50
       
   346     else
       
   347         begin
       
   348         dec(delay2);
       
   349 
       
   350         if ((delay2 mod cInactDelay) = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and (not CurrentHedgehog^.Unplaced) then
       
   351             begin
       
   352             if (CurrentHedgehog^.Gear^.State and gstAttacked <> 0) and (Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then
       
   353                 begin
       
   354                 CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstHHChooseTarget;
       
   355                 isCursorVisible := true
       
   356                 end;
       
   357             CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State and (not gstAttacked);
       
   358             end;
       
   359         if delay2 = 0 then
       
   360             begin
       
   361             if (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.State and gstAttacked = 0) and (CurAmmoGear = nil) then SweepDirty;
       
   362             CheckNoDamage;
       
   363             AliveCount:= 0; // shorter version of check for win to allow typical step activity to proceed
       
   364             for i:= 0 to Pred(ClansCount) do
       
   365                 if ClansArray[i]^.ClanHealth > 0 then inc(AliveCount);
       
   366             if (AliveCount <= 1) and ((GameFlags and gfOneClanMode) = 0) then
       
   367                 begin
       
   368                 step:= stChDmg;
       
   369                 if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
       
   370                 TurnTimeLeft:= 0
       
   371                 end
       
   372             end
       
   373         end
       
   374     end;
       
   375 
       
   376 if TurnTimeLeft > 0 then
       
   377         if CurrentHedgehog^.Gear <> nil then
       
   378             if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0)
       
   379                 and (not isInMultiShoot) then
       
   380                 begin
       
   381                 if (TurnTimeLeft = 5000)
       
   382                     and (cHedgehogTurnTime >= 10000)
       
   383                     and (not PlacingHogs)
       
   384                     and (CurrentHedgehog^.Gear <> nil)
       
   385                     and ((CurrentHedgehog^.Gear^.State and gstAttacked) = 0) then
       
   386                         AddVoice(sndHurry, CurrentTeam^.voicepack);
       
   387                 if ReadyTimeLeft > 0 then
       
   388                     begin
       
   389                     if ReadyTimeLeft = 2000 then
       
   390                         AddVoice(sndComeonthen, CurrentTeam^.voicepack);
       
   391                     dec(ReadyTimeLeft)
       
   392                     end
       
   393                 else
       
   394                     dec(TurnTimeLeft)
       
   395                 end;
       
   396 
       
   397 if skipFlag then
       
   398     begin
       
   399     if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
       
   400     TurnTimeLeft:= 0;
       
   401     skipFlag:= false;
       
   402     inc(CurrentHedgehog^.Team^.stats.TurnSkips);
       
   403     end;
       
   404 
       
   405 if ((GameTicks and $FFFF) = $FFFF) then
       
   406     begin
       
   407     if (not CurrentTeam^.ExtDriven) then
       
   408         begin
       
   409         SendIPC('#');
       
   410         AddFileLog('hiTicks increment message sent')
       
   411         end;
       
   412 
       
   413     if (not CurrentTeam^.ExtDriven) or CurrentTeam^.hasGone then
       
   414         inc(hiTicks) // we do not recieve a message for this
       
   415     end;
       
   416 
       
   417 ScriptCall('onGameTick');
       
   418 inc(GameTicks)
       
   419 end;
       
   420 
       
   421 //Purpose, to reset all transient attributes toggled by a utility and clean up various gears and effects at end of turn
       
   422 //If any of these are set as permanent toggles in the frontend, that needs to be checked and skipped here.
       
   423 procedure EndTurnCleanup;
       
   424 var  i: LongInt;
       
   425      t: PGear;
       
   426 begin
       
   427     SpeechText:= ''; // in case it has not been consumed
       
   428 
       
   429     if (GameFlags and gfLowGravity) = 0 then
       
   430         begin
       
   431         cGravity:= cMaxWindSpeed * 2;
       
   432         cGravityf:= 0.00025 * 2
       
   433         end;
       
   434 
       
   435     if (GameFlags and gfVampiric) = 0 then
       
   436         cVampiric:= false;
       
   437 
       
   438     cDamageModifier:= _1;
       
   439 
       
   440     if (GameFlags and gfLaserSight) = 0 then
       
   441         cLaserSighting:= false;
       
   442 
       
   443     if (GameFlags and gfArtillery) = 0 then
       
   444         cArtillery:= false;
       
   445     // have to sweep *all* current team hedgehogs since it is theoretically possible if you have enough invulnerabilities and switch turns to make your entire team invulnerable
       
   446     if (CurrentTeam <> nil) then
       
   447         with CurrentTeam^ do
       
   448             for i:= 0 to cMaxHHIndex do
       
   449                 with Hedgehogs[i] do
       
   450                     begin
       
   451 (*
       
   452                     if (SpeechGear <> nil) then
       
   453                         begin
       
   454                         DeleteVisualGear(SpeechGear);  // remove to restore persisting beyond end of turn. Tiy says was too much of a gameplay issue
       
   455                         SpeechGear:= nil
       
   456                         end;
       
   457 *)
       
   458 
       
   459                     if (Gear <> nil) then
       
   460                         begin
       
   461                         if (GameFlags and gfInvulnerable) = 0 then
       
   462                             Gear^.Invulnerable:= false;
       
   463                         end;
       
   464                     end;
       
   465     t:= GearsList;
       
   466     while t <> nil do
       
   467         begin
       
   468         t^.PortalCounter:= 0;
       
   469         if ((GameFlags and gfResetHealth) <> 0) and (t^.Kind = gtHedgehog) and (t^.Health < t^.Hedgehog^.InitialHealth) then
       
   470             begin
       
   471             t^.Health:= t^.Hedgehog^.InitialHealth;
       
   472             RenderHealth(t^.Hedgehog^);
       
   473             end;
       
   474         t:= t^.NextGear
       
   475         end;
       
   476    
       
   477     if ((GameFlags and gfResetWeps) <> 0) and (not PlacingHogs) then
       
   478         ResetWeapons;
       
   479 
       
   480     if (GameFlags and gfResetHealth) <> 0 then
       
   481         for i:= 0 to Pred(TeamsCount) do
       
   482             RecountTeamHealth(TeamsArray[i])
       
   483 end;
       
   484 
       
   485 procedure SetAllToActive;
       
   486 var t: PGear;
       
   487 begin
       
   488 AllInactive:= false;
       
   489 t:= GearsList;
       
   490 while t <> nil do
       
   491     begin
       
   492     t^.Active:= true;
       
   493     t:= t^.NextGear
       
   494     end
       
   495 end;
       
   496 
       
   497 procedure SetAllHHToActive;
       
   498 var t: PGear;
       
   499 begin
       
   500 AllInactive:= false;
       
   501 t:= GearsList;
       
   502 while t <> nil do
       
   503     begin
       
   504     if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then t^.Active:= true;
       
   505     t:= t^.NextGear
       
   506     end
       
   507 end;
       
   508 
       
   509 
       
   510 procedure DrawGears;
       
   511 var Gear: PGear;
       
   512     x, y: LongInt;
       
   513 begin
       
   514 Gear:= GearsList;
       
   515 while Gear <> nil do
       
   516     begin
       
   517     if Gear^.State and gstInvisible = 0 then
       
   518         begin
       
   519         x:= hwRound(Gear^.X) + WorldDx;
       
   520         y:= hwRound(Gear^.Y) + WorldDy;
       
   521         RenderGear(Gear, x, y);
       
   522         end;
       
   523     Gear:= Gear^.NextGear
       
   524     end;
       
   525 end;
       
   526 
       
   527 procedure FreeGearsList;
       
   528 var t, tt: PGear;
       
   529 begin
       
   530     tt:= GearsList;
       
   531     GearsList:= nil;
       
   532     while tt <> nil do
       
   533     begin
       
   534         t:= tt;
       
   535         tt:= tt^.NextGear;
       
   536         Dispose(t)
       
   537     end;
       
   538 end;
       
   539 
       
   540 procedure AddMiscGears;
       
   541 var i: Longword;
       
   542     Gear: PGear;
       
   543 begin
       
   544 AddGear(0, 0, gtATStartGame, 0, _0, _0, 2000);
       
   545 
       
   546 i:= 0;
       
   547 Gear:= PGear(1);
       
   548 while (i < cLandMines) {and (Gear <> nil)} do // disable this check until better solution found
       
   549     begin
       
   550     Gear:= AddGear(0, 0, gtMine, 0, _0, _0, 0);
       
   551     FindPlace(Gear, false, 0, LAND_WIDTH);
       
   552     inc(i)
       
   553     end;
       
   554 
       
   555 i:= 0;
       
   556 Gear:= PGear(1);
       
   557 while (i < cExplosives){ and (Gear <> nil)} do
       
   558     begin
       
   559     Gear:= AddGear(0, 0, gtExplosives, 0, _0, _0, 0);
       
   560     FindPlace(Gear, false, 0, LAND_WIDTH);
       
   561     inc(i)
       
   562     end;
       
   563 
       
   564 if (GameFlags and gfLowGravity) <> 0 then
       
   565     begin
       
   566     cGravity:= cMaxWindSpeed;
       
   567     cGravityf:= 0.00025
       
   568     end;
       
   569 
       
   570 if (GameFlags and gfVampiric) <> 0 then
       
   571     cVampiric:= true;
       
   572 
       
   573 Gear:= GearsList;
       
   574 if (GameFlags and gfInvulnerable) <> 0 then
       
   575    while Gear <> nil do
       
   576        begin
       
   577        Gear^.Invulnerable:= true;  // this is only checked on hogs right now, so no need for gear type check
       
   578        Gear:= Gear^.NextGear
       
   579        end;
       
   580 
       
   581 if (GameFlags and gfLaserSight) <> 0 then
       
   582     cLaserSighting:= true;
       
   583 
       
   584 if (GameFlags and gfArtillery) <> 0 then
       
   585     cArtillery:= true;
       
   586 
       
   587 if not hasBorder and ((Theme = 'Snow') or (Theme = 'Christmas')) then
       
   588     for i:= 0 to Pred(vobCount*2) do
       
   589         AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(LAND_HEIGHT div 2), gtFlake, 0, _0, _0, 0);
       
   590 end;
       
   591 
       
   592 
       
   593 procedure ShotgunShot(Gear: PGear);
       
   594 var t: PGear;
       
   595     dmg, r, dist: LongInt;
       
   596     dx, dy: hwFloat;
       
   597 begin
       
   598 Gear^.Radius:= cShotgunRadius;
       
   599 t:= GearsList;
       
   600 while t <> nil do
       
   601     begin
       
   602     case t^.Kind of
       
   603         gtHedgehog,
       
   604             gtMine,
       
   605             gtSMine,
       
   606             gtCase,
       
   607             gtTarget,
       
   608             gtExplosives,
       
   609             gtStructure: begin
       
   610 //addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg));
       
   611                     dmg:= 0;
       
   612                     r:= Gear^.Radius + t^.Radius;
       
   613                     dx:= Gear^.X-t^.X;
       
   614                     dx.isNegative:= false;
       
   615                     dy:= Gear^.Y-t^.Y;
       
   616                     dy.isNegative:= false;
       
   617                     if r-hwRound(dx+dy) > 0 then
       
   618                         begin
       
   619                         dist:= hwRound(Distance(dx, dy));
       
   620                         dmg:= ModifyDamage(min(r - dist, 25), t);
       
   621                         end;
       
   622                     if dmg > 0 then
       
   623                         begin
       
   624                         if (not t^.Invulnerable) then
       
   625                             ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet)
       
   626                         else
       
   627                             Gear^.State:= Gear^.State or gstWinner;
       
   628 
       
   629                         DeleteCI(t);
       
   630                         t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
       
   631                         t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
       
   632                         t^.State:= t^.State or gstMoving;
       
   633                         t^.Active:= true;
       
   634                         FollowGear:= t
       
   635                         end
       
   636                     end;
       
   637             gtGrave: begin
       
   638                     dmg:= 0;
       
   639                     r:= Gear^.Radius + t^.Radius;
       
   640                     dx:= Gear^.X-t^.X;
       
   641                     dx.isNegative:= false;
       
   642                     dy:= Gear^.Y-t^.Y;
       
   643                     dy.isNegative:= false;
       
   644                     if r-hwRound(dx+dy) > 0 then
       
   645                         begin
       
   646                         dist:= hwRound(Distance(dx, dy));
       
   647                         dmg:= ModifyDamage(min(r - dist, 25), t);
       
   648                         end;
       
   649                     if dmg > 0 then
       
   650                         begin
       
   651                         t^.dY:= - _0_1;
       
   652                         t^.Active:= true
       
   653                         end
       
   654                     end;
       
   655         end;
       
   656     t:= t^.NextGear
       
   657     end;
       
   658 if (GameFlags and gfSolidLand) = 0 then DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius)
       
   659 end;
       
   660 
       
   661 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
       
   662 var t: PGearArray;
       
   663     Gear: PGear;
       
   664     i, tmpDmg: LongInt;
       
   665     VGear: PVisualGear;
       
   666 begin
       
   667 t:= CheckGearsCollision(Ammo);
       
   668 // Just to avoid hogs on rope dodging fire.
       
   669 if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) and
       
   670    (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) and
       
   671    (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then
       
   672     begin
       
   673     t^.ar[t^.Count]:= CurrentHedgehog^.Gear;
       
   674     inc(t^.Count)
       
   675     end;
       
   676 
       
   677 i:= t^.Count;
       
   678 
       
   679 if (Ammo^.Kind = gtFlame) and (i > 0) then Ammo^.Health:= 0;
       
   680 while i > 0 do
       
   681     begin
       
   682     dec(i);
       
   683     Gear:= t^.ar[i];
       
   684     tmpDmg:= ModifyDamage(Damage, Gear);
       
   685     if (Gear^.State and gstNoDamage) = 0 then
       
   686         begin
       
   687 
       
   688         if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then 
       
   689             begin
       
   690             VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit);
       
   691             if VGear <> nil then VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY);
       
   692             end;
       
   693 
       
   694         if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then Gear^.FlightTime:= 1;
       
   695 
       
   696         case Gear^.Kind of
       
   697             gtHedgehog,
       
   698             gtMine,
       
   699             gtSMine,
       
   700             gtTarget,
       
   701             gtCase,
       
   702             gtExplosives,
       
   703             gtStructure: begin
       
   704                     if (Ammo^.Kind = gtDrill) then begin Ammo^.Timer:= 0; exit; end;
       
   705                     if (not Gear^.Invulnerable) then
       
   706                         ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove)
       
   707                     else
       
   708                         Gear^.State:= Gear^.State or gstWinner;
       
   709                     if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then 
       
   710                         begin
       
   711                         if (Ammo^.Hedgehog^.Gear <> nil) then Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable);
       
   712                         ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch
       
   713                         end;
       
   714 
       
   715                     DeleteCI(Gear);
       
   716                     if (Gear^.Kind = gtHedgehog) and Gear^.Hedgehog^.King then
       
   717                         begin
       
   718                         Gear^.dX:= Ammo^.dX * Power * _0_005;
       
   719                         Gear^.dY:= Ammo^.dY * Power * _0_005
       
   720                         end
       
   721                     else
       
   722                         begin
       
   723                         Gear^.dX:= Ammo^.dX * Power * _0_01;
       
   724                         Gear^.dY:= Ammo^.dY * Power * _0_01
       
   725                         end;
       
   726 
       
   727                     Gear^.Active:= true;
       
   728                     Gear^.State:= Gear^.State or gstMoving;
       
   729 
       
   730                     if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
       
   731                         begin
       
   732                         if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX))
       
   733                             or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   734                         if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX))
       
   735                             or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   736                         if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX))
       
   737                             or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   738                         end;
       
   739 
       
   740                     if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then FollowGear:= Gear
       
   741                     end;
       
   742         end
       
   743         end;
       
   744     end;
       
   745 if i <> 0 then SetAllToActive
       
   746 end;
       
   747 
       
   748 procedure AssignHHCoords;
       
   749 var i, t, p, j: LongInt;
       
   750     ar: array[0..Pred(cMaxHHs)] of PHedgehog;
       
   751     Count: Longword;
       
   752 begin
       
   753 if (GameFlags and gfPlaceHog) <> 0 then PlacingHogs:= true;
       
   754 if (GameFlags and gfDivideTeams) <> 0 then
       
   755     begin
       
   756     t:= 0;
       
   757     TryDo(ClansCount = 2, 'More or less than 2 clans on map in divided teams mode!', true);
       
   758     for p:= 0 to 1 do
       
   759         begin
       
   760         with ClansArray[p]^ do
       
   761             for j:= 0 to Pred(TeamsNumber) do
       
   762                 with Teams[j]^ do
       
   763                     for i:= 0 to cMaxHHIndex do
       
   764                         with Hedgehogs[i] do
       
   765                             if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
       
   766                                 begin
       
   767                                 if PlacingHogs then Unplaced:= true
       
   768                                 else FindPlace(Gear, false, t, t + LAND_WIDTH div 2);// could make Gear == nil;
       
   769                                 if Gear <> nil then
       
   770                                     begin
       
   771                                     Gear^.Pos:= GetRandom(49);
       
   772                                     Gear^.dX.isNegative:= p = 1;
       
   773                                     end
       
   774                                 end;
       
   775         t:= LAND_WIDTH div 2
       
   776         end
       
   777     end else // mix hedgehogs
       
   778     begin
       
   779     Count:= 0;
       
   780     for p:= 0 to Pred(TeamsCount) do
       
   781         with TeamsArray[p]^ do
       
   782         begin
       
   783         for i:= 0 to cMaxHHIndex do
       
   784             with Hedgehogs[i] do
       
   785                 if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
       
   786                     begin
       
   787                     ar[Count]:= @Hedgehogs[i];
       
   788                     inc(Count)
       
   789                     end;
       
   790         end;
       
   791     // unC0Rr, while it is true user can watch value on map screen, IMO this (and check above) should be enforced in UI
       
   792     // - is there a good place to put values for the different widgets to check?  Right now they are kind of disconnected.
       
   793     //it would be nice if divide teams, forts mode and hh per map could all be checked by the team widget, or maybe disable start button
       
   794     TryDo(Count <= MaxHedgehogs, 'Too many hedgehogs for this map! (max # is ' + inttostr(MaxHedgehogs) + ')', true);
       
   795     while (Count > 0) do
       
   796         begin
       
   797         i:= GetRandom(Count);
       
   798         if PlacingHogs then ar[i]^.Unplaced:= true
       
   799         else FindPlace(ar[i]^.Gear, false, 0, LAND_WIDTH);
       
   800         if ar[i]^.Gear <> nil then
       
   801             begin
       
   802             ar[i]^.Gear^.dX.isNegative:= hwRound(ar[i]^.Gear^.X) > LAND_WIDTH div 2;
       
   803             ar[i]^.Gear^.Pos:= GetRandom(19)
       
   804             end;
       
   805         ar[i]:= ar[Count - 1];
       
   806         dec(Count)
       
   807         end
       
   808     end
       
   809 end;
       
   810 
       
   811 function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray;
       
   812 var
       
   813     t: PGear;
       
   814     l: Longword;
       
   815 begin
       
   816     r:= r*r;
       
   817     GearsNear := nil;
       
   818     t := GearsList;
       
   819     while t <> nil do 
       
   820         begin
       
   821         if (t^.Kind = Kind) 
       
   822             and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then
       
   823             begin
       
   824             l:= Length(GearsNear);
       
   825             SetLength(GearsNear, l + 1);
       
   826             GearsNear[l] := t;
       
   827             end;
       
   828         t := t^.NextGear;
       
   829     end;
       
   830 end;
       
   831 
       
   832 {procedure AmmoFlameWork(Ammo: PGear);
       
   833 var t: PGear;
       
   834 begin
       
   835 t:= GearsList;
       
   836 while t <> nil do
       
   837     begin
       
   838     if (t^.Kind = gtHedgehog) and (t^.Y < Ammo^.Y) then
       
   839         if not (hwSqr(Ammo^.X - t^.X) + hwSqr(Ammo^.Y - t^.Y - int2hwFloat(cHHRadius)) * 2 > _2) then
       
   840             begin
       
   841             ApplyDamage(t, 5);
       
   842             t^.dX:= t^.dX + (t^.X - Ammo^.X) * _0_02;
       
   843             t^.dY:= - _0_25;
       
   844             t^.Active:= true;
       
   845             DeleteCI(t);
       
   846             FollowGear:= t
       
   847             end;
       
   848     t:= t^.NextGear
       
   849     end;
       
   850 end;}
       
   851 
       
   852 
       
   853 function CountGears(Kind: TGearType): Longword;
       
   854 var t: PGear;
       
   855     count: Longword = 0;
       
   856 begin
       
   857 
       
   858 t:= GearsList;
       
   859 while t <> nil do
       
   860     begin
       
   861     if t^.Kind = Kind then inc(count);
       
   862     t:= t^.NextGear
       
   863     end;
       
   864 CountGears:= count;
       
   865 end;
       
   866 
       
   867 function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content: Longword): PGear;
       
   868 begin
       
   869     FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0);
       
   870     cCaseFactor := 0;
       
   871 
       
   872     if (crate <> HealthCrate) and (content > ord(High(TAmmoType))) then content := ord(High(TAmmoType));
       
   873 
       
   874     case crate of
       
   875         HealthCrate: begin
       
   876             FollowGear^.Pos := posCaseHealth;
       
   877             FollowGear^.Health := content;
       
   878             AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
       
   879             end;
       
   880         AmmoCrate: begin
       
   881             FollowGear^.Pos := posCaseAmmo;
       
   882             FollowGear^.AmmoType := TAmmoType(content);
       
   883             AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
       
   884             end;
       
   885         UtilityCrate: begin
       
   886             FollowGear^.Pos := posCaseUtility;
       
   887             FollowGear^.AmmoType := TAmmoType(content);
       
   888             AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
       
   889             end;
       
   890     end;
       
   891 
       
   892     if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH);
       
   893 
       
   894     SpawnCustomCrateAt := FollowGear;
       
   895 end;
       
   896 
       
   897 function SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean): PGear;
       
   898 begin
       
   899     FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0);
       
   900     cCaseFactor := 0;
       
   901     FollowGear^.Pos := posCaseDummy;
       
   902     
       
   903     if explode then FollowGear^.Pos := FollowGear^.Pos + posCaseExplode;
       
   904     if poison then FollowGear^.Pos := FollowGear^.Pos + posCasePoison;
       
   905 
       
   906     case crate of
       
   907         HealthCrate: begin
       
   908             FollowGear^.Pos := FollowGear^.Pos + posCaseHealth;
       
   909             AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
       
   910             end;
       
   911         AmmoCrate: begin
       
   912             FollowGear^.Pos := FollowGear^.Pos + posCaseAmmo;
       
   913             AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
       
   914             end;
       
   915         UtilityCrate: begin
       
   916             FollowGear^.Pos := FollowGear^.Pos + posCaseUtility;
       
   917             AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
       
   918             end;
       
   919     end;
       
   920 
       
   921     if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH);
       
   922 
       
   923     SpawnFakeCrateAt := FollowGear;
       
   924 end;
       
   925 
       
   926 function GetAmmo(Hedgehog: PHedgehog): TAmmoType;
       
   927 var t, aTot: LongInt;
       
   928     i: TAmmoType;
       
   929 begin
       
   930 
       
   931 aTot:= 0;
       
   932 for i:= Low(TAmmoType) to High(TAmmoType) do
       
   933     if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
       
   934         inc(aTot, Ammoz[i].Probability);
       
   935 
       
   936 t:= aTot;
       
   937 i:= Low(TAmmoType);
       
   938 if (t > 0) then
       
   939     begin
       
   940     t:= GetRandom(t);
       
   941     while t >= 0 do
       
   942       begin
       
   943       inc(i);
       
   944       if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
       
   945           dec(t, Ammoz[i].Probability)
       
   946       end
       
   947     end;
       
   948 GetAmmo:= i
       
   949 end;
       
   950 
       
   951 function GetUtility(Hedgehog: PHedgehog): TAmmoType;
       
   952 var t, uTot: LongInt;
       
   953     i: TAmmoType;
       
   954 begin
       
   955 
       
   956 uTot:= 0;
       
   957 for i:= Low(TAmmoType) to High(TAmmoType) do
       
   958     if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then
       
   959         inc(uTot, Ammoz[i].Probability);
       
   960 
       
   961 t:= uTot;
       
   962 i:= Low(TAmmoType);
       
   963 if (t > 0) then
       
   964     begin
       
   965     t:= GetRandom(t);
       
   966     while t >= 0 do
       
   967       begin
       
   968       inc(i);
       
   969       if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then
       
   970           dec(t, Ammoz[i].Probability)
       
   971       end
       
   972     end;
       
   973 GetUtility:= i
       
   974 end;
       
   975 
       
   976 
       
   977 
       
   978 procedure SpawnBoxOfSmth;
       
   979 var t, aTot, uTot, a, h: LongInt;
       
   980     i: TAmmoType;
       
   981 begin
       
   982 if (PlacingHogs) or
       
   983    (cCaseFactor = 0) or
       
   984    (CountGears(gtCase) >= 5) or
       
   985    (GetRandom(cCaseFactor) <> 0) then exit;
       
   986 
       
   987 FollowGear:= nil;
       
   988 aTot:= 0;
       
   989 uTot:= 0;
       
   990 for i:= Low(TAmmoType) to High(TAmmoType) do
       
   991     if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
       
   992         inc(aTot, Ammoz[i].Probability)
       
   993     else
       
   994         inc(uTot, Ammoz[i].Probability);
       
   995 
       
   996 t:=0;
       
   997 a:=aTot;
       
   998 h:= 1;
       
   999 
       
  1000 if (aTot+uTot) <> 0 then
       
  1001     if ((GameFlags and gfInvulnerable) = 0) then
       
  1002         begin
       
  1003         h:= cHealthCaseProb * 100;
       
  1004         t:= GetRandom(10000);
       
  1005         a:= (10000-h)*aTot div (aTot+uTot)
       
  1006         end
       
  1007     else
       
  1008         begin
       
  1009         t:= GetRandom(aTot+uTot);
       
  1010         h:= 0
       
  1011         end;
       
  1012 
       
  1013 
       
  1014 if t<h then
       
  1015     begin
       
  1016     FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
       
  1017     FollowGear^.Health:= cHealthCaseAmount;
       
  1018     FollowGear^.Pos:= posCaseHealth;
       
  1019     AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
       
  1020     end
       
  1021 else if (t<a+h) then
       
  1022     begin
       
  1023     t:= aTot;
       
  1024     if (t > 0) then
       
  1025         begin
       
  1026         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
       
  1027         t:= GetRandom(t);
       
  1028         i:= Low(TAmmoType);
       
  1029         FollowGear^.Pos:= posCaseAmmo;
       
  1030         FollowGear^.AmmoType:= i;
       
  1031         AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
       
  1032         end
       
  1033     end
       
  1034 else
       
  1035     begin
       
  1036     t:= uTot;
       
  1037     if (t > 0) then
       
  1038         begin
       
  1039         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
       
  1040         t:= GetRandom(t);
       
  1041         i:= Low(TAmmoType);
       
  1042         FollowGear^.Pos:= posCaseUtility;
       
  1043         FollowGear^.AmmoType:= i;
       
  1044         AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
       
  1045         end
       
  1046     end;
       
  1047 
       
  1048 // handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities
       
  1049 if (FollowGear <> nil) then
       
  1050     begin
       
  1051     FindPlace(FollowGear, true, 0, LAND_WIDTH);
       
  1052 
       
  1053     if (FollowGear <> nil) then
       
  1054         AddVoice(sndReinforce, CurrentTeam^.voicepack)
       
  1055     end
       
  1056 end;
       
  1057 
       
  1058 
       
  1059 function GearByUID(uid : Longword) : PGear;
       
  1060 var gear: PGear;
       
  1061 begin
       
  1062 GearByUID:= nil;
       
  1063 if uid = 0 then exit;
       
  1064 if (lastGearByUID <> nil) and (lastGearByUID^.uid = uid) then
       
  1065     begin
       
  1066     GearByUID:= lastGearByUID;
       
  1067     exit
       
  1068     end;
       
  1069 gear:= GearsList;
       
  1070 while gear <> nil do
       
  1071     begin
       
  1072     if gear^.uid = uid then
       
  1073         begin
       
  1074         lastGearByUID:= gear;
       
  1075         GearByUID:= gear;
       
  1076         exit
       
  1077         end;
       
  1078     gear:= gear^.NextGear
       
  1079     end
       
  1080 end;
       
  1081 
       
  1082 
       
  1083 procedure chSkip(var s: shortstring);
       
  1084 begin
       
  1085 s:= s; // avoid compiler hint
       
  1086 if not CurrentTeam^.ExtDriven then SendIPC(',');
       
  1087 uStats.Skipped;
       
  1088 skipFlag:= true
       
  1089 end;
       
  1090 
       
  1091 procedure chHogSay(var s: shortstring);
       
  1092 var Gear: PVisualGear;
       
  1093     text: shortstring;
       
  1094     hh: PHedgehog;
       
  1095     i, x, t, h: byte;
       
  1096     c, j: LongInt;
       
  1097 begin
       
  1098     hh:= nil;
       
  1099     i:= 0;
       
  1100     t:= 0;
       
  1101     x:= byte(s[1]);  // speech type
       
  1102     if x < 4 then
       
  1103         begin
       
  1104         t:= byte(s[2]);  // team
       
  1105         if Length(s) > 2 then h:= byte(s[3])  // target hog
       
  1106         end;
       
  1107     // allow targetting a hog by specifying a number as the first portion of the text
       
  1108     if (x < 4) and (h > byte('0')) and (h < byte('9')) then i:= h - 48;
       
  1109     if i <> 0 then text:= copy(s, 4, Length(s) - 1)
       
  1110     else if x < 4 then text:= copy(s, 3, Length(s) - 1)
       
  1111     else text:= copy(s, 2, Length(s) - 1);
       
  1112 
       
  1113     (*
       
  1114     if CheckNoTeamOrHH then
       
  1115         begin
       
  1116         ParseCommand('say ' + text, true);
       
  1117         exit
       
  1118         end;
       
  1119     *)
       
  1120 
       
  1121     if (x < 4) and (TeamsArray[t] <> nil) then
       
  1122         begin
       
  1123             // if team matches current hedgehog team, default to current hedgehog
       
  1124             if (i = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Team = TeamsArray[t]) then hh:= CurrentHedgehog
       
  1125             else 
       
  1126                 begin
       
  1127             // otherwise use the first living hog or the hog amongs the remaining ones indicated by i
       
  1128                 j:= 0;
       
  1129                 c:= 0;
       
  1130                 while (j <= cMaxHHIndex) and (hh = nil) do
       
  1131                     begin
       
  1132                     if (TeamsArray[t]^.Hedgehogs[j].Gear <> nil) then
       
  1133                         begin
       
  1134                         inc(c);
       
  1135                         if (i=0) or (i=c) then
       
  1136                             hh:= @TeamsArray[t]^.Hedgehogs[j]
       
  1137                         end;
       
  1138                     inc(j)
       
  1139                     end
       
  1140                 end;
       
  1141         if hh <> nil then 
       
  1142             begin
       
  1143             Gear:= AddVisualGear(0, 0, vgtSpeechBubble);
       
  1144             if Gear <> nil then
       
  1145                 begin
       
  1146                 Gear^.Hedgehog:= hh;
       
  1147                 Gear^.Text:= text;
       
  1148                 Gear^.FrameTicks:= x
       
  1149                 end
       
  1150             end
       
  1151         //else ParseCommand('say ' + text, true)
       
  1152         end
       
  1153     else if (x >= 4) then
       
  1154         begin
       
  1155         SpeechType:= x-3;
       
  1156         SpeechText:= text
       
  1157         end;
       
  1158 end;
       
  1159 
       
  1160 procedure initModule;
       
  1161 const handlers: array[TGearType] of TGearStepProcedure = (
    83             @doStepBomb,
  1162             @doStepBomb,
    84             @doStepHedgehog,
  1163             @doStepHedgehog,
    85             @doStepShell,
  1164             @doStepShell,
    86             @doStepGrave,
  1165             @doStepGrave,
    87             @doStepBee,
  1166             @doStepBee,
   140             @doStepSnowball,
  1219             @doStepSnowball,
   141             @doStepSnowflake,
  1220             @doStepSnowflake,
   142             @doStepStructure,
  1221             @doStepStructure,
   143             @doStepLandGun,
  1222             @doStepLandGun,
   144             @doStepTardis);
  1223             @doStepTardis);
   145 
  1224 begin
   146 function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs
  1225     doStepHandlers:= handlers;
   147 var Gear: PGear;
  1226 
   148     dmg: LongInt;
       
   149 begin
       
   150 CheckNoDamage:= true;
       
   151 Gear:= GearsList;
       
   152 while Gear <> nil do
       
   153     begin
       
   154     if (Gear^.Kind = gtHedgehog) and (((GameFlags and gfInfAttack) = 0) or ((Gear^.dX.QWordValue < _0_000004.QWordValue) and (Gear^.dY.QWordValue < _0_000004.QWordValue))) then
       
   155         begin
       
   156         if (not isInMultiShoot) then inc(Gear^.Damage, Gear^.Karma);
       
   157         if (Gear^.Damage <> 0) and
       
   158         (not Gear^.Invulnerable) then
       
   159             begin
       
   160             CheckNoDamage:= false;
       
   161 
       
   162             dmg:= Gear^.Damage;
       
   163             if Gear^.Health < dmg then
       
   164                 begin
       
   165                 Gear^.Active:= true;
       
   166                 Gear^.Health:= 0
       
   167                 end
       
   168             else
       
   169                 dec(Gear^.Health, dmg);
       
   170 
       
   171             if (Gear^.Hedgehog^.Team = CurrentTeam) and
       
   172                (Gear^.Damage <> Gear^.Karma) and
       
   173                 (not Gear^.Hedgehog^.King) and
       
   174                 (not Gear^.Hedgehog^.Effects[hePoisoned]) and
       
   175                 (not SuddenDeathDmg) then
       
   176                 Gear^.State:= Gear^.State or gstLoser;
       
   177 
       
   178             spawnHealthTagForHH(Gear, dmg);
       
   179 
       
   180             RenderHealth(Gear^.Hedgehog^);
       
   181             RecountTeamHealth(Gear^.Hedgehog^.Team);
       
   182 
       
   183             end;
       
   184         if (not isInMultiShoot) then Gear^.Karma:= 0;
       
   185         Gear^.Damage:= 0
       
   186         end;
       
   187     Gear:= Gear^.NextGear
       
   188     end;
       
   189 end;
       
   190 
       
   191 procedure HealthMachine;
       
   192 var Gear: PGear;
       
   193     team: PTeam;
       
   194        i: LongWord;
       
   195     flag: Boolean;
       
   196      tmp: LongWord;
       
   197 begin
       
   198     Gear:= GearsList;
       
   199 
       
   200     while Gear <> nil do
       
   201     begin
       
   202         if Gear^.Kind = gtHedgehog then
       
   203             begin
       
   204             tmp:= 0;
       
   205             if Gear^.Hedgehog^.Effects[hePoisoned] then
       
   206                 begin
       
   207                 inc(tmp, ModifyDamage(5, Gear));
       
   208                 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth)  // does not need a minimum check since <= 1 basically disables it
       
   209                 end;
       
   210             if (TotalRounds > cSuddenDTurns - 1) then
       
   211                 begin
       
   212                 inc(tmp, cHealthDecrease);
       
   213                 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, cHealthDecrease)
       
   214                 end;
       
   215             if Gear^.Hedgehog^.King then
       
   216                 begin
       
   217                 flag:= false;
       
   218                 team:= Gear^.Hedgehog^.Team;
       
   219                 for i:= 0 to Pred(team^.HedgehogsNumber) do
       
   220                     if (team^.Hedgehogs[i].Gear <> nil) and
       
   221                         (not team^.Hedgehogs[i].King) and
       
   222                         (team^.Hedgehogs[i].Gear^.Health > team^.Hedgehogs[i].Gear^.Damage)
       
   223                     then flag:= true;
       
   224                 if not flag then
       
   225                     begin
       
   226                     inc(tmp, 5);
       
   227                     if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, 5)
       
   228                     end
       
   229                 end;
       
   230             if tmp > 0 then 
       
   231                 begin
       
   232                 inc(Gear^.Damage, min(tmp, max(0,Gear^.Health - 1 - Gear^.Damage)));
       
   233                 HHHurt(Gear^.Hedgehog, dsPoison);
       
   234                 end
       
   235             end;
       
   236 
       
   237         Gear:= Gear^.NextGear
       
   238     end;
       
   239 end;
       
   240 
       
   241 procedure ProcessGears;
       
   242 const delay: LongWord = 0;
       
   243       delay2: LongWord = 0;
       
   244     step: (stDelay, stChDmg, stSweep, stTurnReact,
       
   245             stAfterDelay, stChWin, stWater, stChWin2, stHealth,
       
   246             stSpawn, stNTurn) = stDelay;
       
   247 var Gear, t: PGear;
       
   248     i, AliveCount: LongInt;
       
   249     s: shortstring;
       
   250 begin
       
   251 PrvInactive:= AllInactive;
       
   252 AllInactive:= true;
       
   253 
       
   254 if (StepSoundTimer > 0) and (StepSoundChannel < 0) then
       
   255     StepSoundChannel:= LoopSound(sndSteps)
       
   256 else if (StepSoundTimer = 0) and (StepSoundChannel > -1) then
       
   257     begin
       
   258     StopSound(StepSoundChannel);
       
   259     StepSoundChannel:= -1
       
   260     end;
       
   261 
       
   262 if StepSoundTimer > 0 then
       
   263     dec(StepSoundTimer, 1);
       
   264 
       
   265 t:= GearsList;
       
   266 while t <> nil do
       
   267     begin
       
   268     Gear:= t;
       
   269     t:= Gear^.NextGear;
       
   270 
       
   271     if Gear^.Active then
       
   272         begin
       
   273         if Gear^.RenderTimer and (Gear^.Timer > 500) and ((Gear^.Timer mod 1000) = 0) then
       
   274             begin
       
   275             FreeTexture(Gear^.Tex);
       
   276             Gear^.Tex:= RenderStringTex(inttostr(Gear^.Timer div 1000), cWhiteColor, fntSmall);
       
   277             end;
       
   278         Gear^.doStep(Gear);
       
   279         // might be useful later
       
   280         //ScriptCall('onGearStep', Gear^.uid);
       
   281         end
       
   282     end;
       
   283 
       
   284 if AllInactive then
       
   285 case step of
       
   286     stDelay: begin
       
   287         if delay = 0 then
       
   288             delay:= cInactDelay
       
   289         else
       
   290             dec(delay);
       
   291 
       
   292         if delay = 0 then
       
   293             inc(step)
       
   294         end;
       
   295     stChDmg: if CheckNoDamage then inc(step) else step:= stDelay;
       
   296     stSweep: if SweepDirty then
       
   297                 begin
       
   298                 SetAllToActive;
       
   299                 step:= stChDmg
       
   300                 end else inc(step);
       
   301     stTurnReact: begin
       
   302         if (not bBetweenTurns) and (not isInMultiShoot) then
       
   303             begin
       
   304             uStats.TurnReaction;
       
   305             inc(step)
       
   306         end else
       
   307             inc(step, 2);
       
   308         end;
       
   309     stAfterDelay: begin
       
   310         if delay = 0 then
       
   311             delay:= cInactDelay
       
   312         else
       
   313             dec(delay);
       
   314 
       
   315         if delay = 0 then
       
   316         inc(step)
       
   317         end;
       
   318     stChWin: begin
       
   319             CheckForWin;
       
   320             inc(step)
       
   321             end;
       
   322     stWater: if (not bBetweenTurns) and (not isInMultiShoot) then
       
   323                 begin
       
   324                 if TotalRounds = cSuddenDTurns + 1 then bWaterRising:= true;
       
   325 
       
   326                 if bWaterRising and (cWaterRise > 0) then
       
   327                     AddGear(0, 0, gtWaterUp, 0, _0, _0, 0)^.Tag:= cWaterRise;
       
   328 
       
   329                 inc(step)
       
   330                 end else inc(step);
       
   331     stChWin2: begin
       
   332             CheckForWin;
       
   333             inc(step)
       
   334             end;
       
   335     stHealth: begin
       
   336             if (cWaterRise <> 0) or (cHealthDecrease <> 0) then
       
   337                 begin
       
   338                 if (TotalRounds = cSuddenDTurns) and (not SuddenDeath) and (not isInMultiShoot) then
       
   339                     begin
       
   340                     SuddenDeath:= true;
       
   341                     if cHealthDecrease <> 0 then
       
   342                         begin
       
   343                         SuddenDeathDmg:= true;
       
   344                         
       
   345                         // flash
       
   346                         ScreenFade:= sfFromWhite;
       
   347                         ScreenFadeValue:= sfMax;
       
   348                         ScreenFadeSpeed:= 1;
       
   349                         
       
   350                         ChangeToSDClouds;
       
   351                         ChangeToSDFlakes;
       
   352                         glClearColor(SDSkyColor.r * (SDTint/255) / 255, SDSkyColor.g * (SDTint/255) / 255, SDSkyColor.b * (SDTint/255) / 255, 0.99);
       
   353                         Ammoz[amTardis].SkipTurns:= 9999;
       
   354                         Ammoz[amTardis].Probability:= 0;
       
   355                         end;
       
   356                     AddCaption(trmsg[sidSuddenDeath], cWhiteColor, capgrpGameState);
       
   357                     playSound(sndSuddenDeath);
       
   358                     StopMusic //No SDMusic for now
       
   359                     //MusicFN:= SDMusic;
       
   360                     //ChangeMusic
       
   361                     end
       
   362                 else if (TotalRounds < cSuddenDTurns) and (not isInMultiShoot) then
       
   363                     begin
       
   364                     i:= cSuddenDTurns - TotalRounds;
       
   365                     s:= inttostr(i);
       
   366                     if i = 1 then
       
   367                         AddCaption(trmsg[sidRoundSD], cWhiteColor, capgrpGameState)
       
   368                     else if (i = 2) or ((i > 0) and ((i mod 50 = 0) or ((i <= 25) and (i mod 5 = 0)))) then
       
   369                         AddCaption(Format(trmsg[sidRoundsSD], s), cWhiteColor, capgrpGameState);
       
   370                     end;
       
   371                 end;
       
   372             if bBetweenTurns
       
   373                 or isInMultiShoot
       
   374                 or (TotalRounds = -1) then inc(step)
       
   375             else begin
       
   376                 bBetweenTurns:= true;
       
   377                 HealthMachine;
       
   378                 step:= stChDmg
       
   379                 end
       
   380             end;
       
   381     stSpawn: begin
       
   382             if not isInMultiShoot then SpawnBoxOfSmth;
       
   383             inc(step)
       
   384             end;
       
   385     stNTurn: begin
       
   386             if isInMultiShoot then
       
   387                 isInMultiShoot:= false
       
   388             else begin
       
   389                 // delayed till after 0.9.12
       
   390                 // reset to default zoom
       
   391                 //ZoomValue:= ZoomDefault;
       
   392                 with CurrentHedgehog^ do
       
   393                     if (Gear <> nil)
       
   394                         and ((Gear^.State and gstAttacked) = 0)
       
   395                         and (MultiShootAttacks > 0) then OnUsedAmmo(CurrentHedgehog^);
       
   396 
       
   397                 EndTurnCleanup;
       
   398 
       
   399                 FreeActionsList; // could send -left, -right and similar commands, so should be called before /nextturn
       
   400 
       
   401                 ParseCommand('/nextturn', true);
       
   402                 SwitchHedgehog;
       
   403 
       
   404                 AfterSwitchHedgehog;
       
   405                 bBetweenTurns:= false
       
   406                 end;
       
   407             step:= Low(step)
       
   408             end;
       
   409     end
       
   410 else if ((GameFlags and gfInfAttack) <> 0) then
       
   411     begin
       
   412     if delay2 = 0 then
       
   413         delay2:= cInactDelay * 50
       
   414     else
       
   415         begin
       
   416         dec(delay2);
       
   417 
       
   418         if ((delay2 mod cInactDelay) = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and (not CurrentHedgehog^.Unplaced) then
       
   419             begin
       
   420             if (CurrentHedgehog^.Gear^.State and gstAttacked <> 0) and (Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then
       
   421                 begin
       
   422                 CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstHHChooseTarget;
       
   423                 isCursorVisible := true
       
   424                 end;
       
   425             CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State and (not gstAttacked);
       
   426             end;
       
   427         if delay2 = 0 then
       
   428             begin
       
   429             if (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.State and gstAttacked = 0) and (CurAmmoGear = nil) then SweepDirty;
       
   430             CheckNoDamage;
       
   431             AliveCount:= 0; // shorter version of check for win to allow typical step activity to proceed
       
   432             for i:= 0 to Pred(ClansCount) do
       
   433                 if ClansArray[i]^.ClanHealth > 0 then inc(AliveCount);
       
   434             if (AliveCount <= 1) and ((GameFlags and gfOneClanMode) = 0) then
       
   435                 begin
       
   436                 step:= stChDmg;
       
   437                 if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
       
   438                 TurnTimeLeft:= 0
       
   439                 end
       
   440             end
       
   441         end
       
   442     end;
       
   443 
       
   444 if TurnTimeLeft > 0 then
       
   445         if CurrentHedgehog^.Gear <> nil then
       
   446             if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0)
       
   447                 and (not isInMultiShoot) then
       
   448                 begin
       
   449                 if (TurnTimeLeft = 5000)
       
   450                     and (cHedgehogTurnTime >= 10000)
       
   451                     and (not PlacingHogs)
       
   452                     and (CurrentHedgehog^.Gear <> nil)
       
   453                     and ((CurrentHedgehog^.Gear^.State and gstAttacked) = 0) then
       
   454                         AddVoice(sndHurry, CurrentTeam^.voicepack);
       
   455                 if ReadyTimeLeft > 0 then
       
   456                     begin
       
   457                     if ReadyTimeLeft = 2000 then
       
   458                         AddVoice(sndComeonthen, CurrentTeam^.voicepack);
       
   459                     dec(ReadyTimeLeft)
       
   460                     end
       
   461                 else
       
   462                     dec(TurnTimeLeft)
       
   463                 end;
       
   464 
       
   465 if skipFlag then
       
   466     begin
       
   467     if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
       
   468     TurnTimeLeft:= 0;
       
   469     skipFlag:= false;
       
   470     inc(CurrentHedgehog^.Team^.stats.TurnSkips);
       
   471     end;
       
   472 
       
   473 if ((GameTicks and $FFFF) = $FFFF) then
       
   474     begin
       
   475     if (not CurrentTeam^.ExtDriven) then
       
   476         begin
       
   477         SendIPC('#');
       
   478         AddFileLog('hiTicks increment message sent')
       
   479         end;
       
   480 
       
   481     if (not CurrentTeam^.ExtDriven) or CurrentTeam^.hasGone then
       
   482         inc(hiTicks) // we do not recieve a message for this
       
   483     end;
       
   484 
       
   485 ScriptCall('onGameTick');
       
   486 inc(GameTicks)
       
   487 end;
       
   488 
       
   489 //Purpose, to reset all transient attributes toggled by a utility and clean up various gears and effects at end of turn
       
   490 //If any of these are set as permanent toggles in the frontend, that needs to be checked and skipped here.
       
   491 procedure EndTurnCleanup;
       
   492 var  i: LongInt;
       
   493      t: PGear;
       
   494 begin
       
   495     SpeechText:= ''; // in case it has not been consumed
       
   496 
       
   497     if (GameFlags and gfLowGravity) = 0 then
       
   498         begin
       
   499         cGravity:= cMaxWindSpeed * 2;
       
   500         cGravityf:= 0.00025 * 2
       
   501         end;
       
   502 
       
   503     if (GameFlags and gfVampiric) = 0 then
       
   504         cVampiric:= false;
       
   505 
       
   506     cDamageModifier:= _1;
       
   507 
       
   508     if (GameFlags and gfLaserSight) = 0 then
       
   509         cLaserSighting:= false;
       
   510 
       
   511     if (GameFlags and gfArtillery) = 0 then
       
   512         cArtillery:= false;
       
   513     // have to sweep *all* current team hedgehogs since it is theoretically possible if you have enough invulnerabilities and switch turns to make your entire team invulnerable
       
   514     if (CurrentTeam <> nil) then
       
   515         with CurrentTeam^ do
       
   516             for i:= 0 to cMaxHHIndex do
       
   517                 with Hedgehogs[i] do
       
   518                     begin
       
   519 (*
       
   520                     if (SpeechGear <> nil) then
       
   521                         begin
       
   522                         DeleteVisualGear(SpeechGear);  // remove to restore persisting beyond end of turn. Tiy says was too much of a gameplay issue
       
   523                         SpeechGear:= nil
       
   524                         end;
       
   525 *)
       
   526 
       
   527                     if (Gear <> nil) then
       
   528                         begin
       
   529                         if (GameFlags and gfInvulnerable) = 0 then
       
   530                             Gear^.Invulnerable:= false;
       
   531                         end;
       
   532                     end;
       
   533     t:= GearsList;
       
   534     while t <> nil do
       
   535         begin
       
   536         t^.PortalCounter:= 0;
       
   537         if ((GameFlags and gfResetHealth) <> 0) and (t^.Kind = gtHedgehog) and (t^.Health < t^.Hedgehog^.InitialHealth) then
       
   538             begin
       
   539             t^.Health:= t^.Hedgehog^.InitialHealth;
       
   540             RenderHealth(t^.Hedgehog^);
       
   541             end;
       
   542         t:= t^.NextGear
       
   543         end;
       
   544    
       
   545     if ((GameFlags and gfResetWeps) <> 0) and (not PlacingHogs) then
       
   546         ResetWeapons;
       
   547 
       
   548     if (GameFlags and gfResetHealth) <> 0 then
       
   549         for i:= 0 to Pred(TeamsCount) do
       
   550             RecountTeamHealth(TeamsArray[i])
       
   551 end;
       
   552 
       
   553 procedure SetAllToActive;
       
   554 var t: PGear;
       
   555 begin
       
   556 AllInactive:= false;
       
   557 t:= GearsList;
       
   558 while t <> nil do
       
   559     begin
       
   560     t^.Active:= true;
       
   561     t:= t^.NextGear
       
   562     end
       
   563 end;
       
   564 
       
   565 procedure SetAllHHToActive;
       
   566 var t: PGear;
       
   567 begin
       
   568 AllInactive:= false;
       
   569 t:= GearsList;
       
   570 while t <> nil do
       
   571     begin
       
   572     if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then t^.Active:= true;
       
   573     t:= t^.NextGear
       
   574     end
       
   575 end;
       
   576 
       
   577 
       
   578 procedure DrawGears;
       
   579 var Gear: PGear;
       
   580     x, y: LongInt;
       
   581 begin
       
   582 Gear:= GearsList;
       
   583 while Gear <> nil do
       
   584     begin
       
   585     if Gear^.State and gstInvisible = 0 then
       
   586         begin
       
   587         x:= hwRound(Gear^.X) + WorldDx;
       
   588         y:= hwRound(Gear^.Y) + WorldDy;
       
   589         RenderGear(Gear, x, y);
       
   590         end;
       
   591     Gear:= Gear^.NextGear
       
   592     end;
       
   593 end;
       
   594 
       
   595 procedure FreeGearsList;
       
   596 var t, tt: PGear;
       
   597 begin
       
   598     tt:= GearsList;
       
   599     GearsList:= nil;
       
   600     while tt <> nil do
       
   601     begin
       
   602         t:= tt;
       
   603         tt:= tt^.NextGear;
       
   604         Dispose(t)
       
   605     end;
       
   606 end;
       
   607 
       
   608 procedure AddMiscGears;
       
   609 var i: Longword;
       
   610     Gear: PGear;
       
   611 begin
       
   612 AddGear(0, 0, gtATStartGame, 0, _0, _0, 2000);
       
   613 
       
   614 i:= 0;
       
   615 Gear:= PGear(1);
       
   616 while (i < cLandMines) {and (Gear <> nil)} do // disable this check until better solution found
       
   617     begin
       
   618     Gear:= AddGear(0, 0, gtMine, 0, _0, _0, 0);
       
   619     FindPlace(Gear, false, 0, LAND_WIDTH);
       
   620     inc(i)
       
   621     end;
       
   622 
       
   623 i:= 0;
       
   624 Gear:= PGear(1);
       
   625 while (i < cExplosives){ and (Gear <> nil)} do
       
   626     begin
       
   627     Gear:= AddGear(0, 0, gtExplosives, 0, _0, _0, 0);
       
   628     FindPlace(Gear, false, 0, LAND_WIDTH);
       
   629     inc(i)
       
   630     end;
       
   631 
       
   632 if (GameFlags and gfLowGravity) <> 0 then
       
   633     begin
       
   634     cGravity:= cMaxWindSpeed;
       
   635     cGravityf:= 0.00025
       
   636     end;
       
   637 
       
   638 if (GameFlags and gfVampiric) <> 0 then
       
   639     cVampiric:= true;
       
   640 
       
   641 Gear:= GearsList;
       
   642 if (GameFlags and gfInvulnerable) <> 0 then
       
   643    while Gear <> nil do
       
   644        begin
       
   645        Gear^.Invulnerable:= true;  // this is only checked on hogs right now, so no need for gear type check
       
   646        Gear:= Gear^.NextGear
       
   647        end;
       
   648 
       
   649 if (GameFlags and gfLaserSight) <> 0 then
       
   650     cLaserSighting:= true;
       
   651 
       
   652 if (GameFlags and gfArtillery) <> 0 then
       
   653     cArtillery:= true;
       
   654 
       
   655 if not hasBorder and ((Theme = 'Snow') or (Theme = 'Christmas')) then
       
   656     for i:= 0 to Pred(vobCount*2) do
       
   657         AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(LAND_HEIGHT div 2), gtFlake, 0, _0, _0, 0);
       
   658 end;
       
   659 
       
   660 
       
   661 procedure ShotgunShot(Gear: PGear);
       
   662 var t: PGear;
       
   663     dmg, r, dist: LongInt;
       
   664     dx, dy: hwFloat;
       
   665 begin
       
   666 Gear^.Radius:= cShotgunRadius;
       
   667 t:= GearsList;
       
   668 while t <> nil do
       
   669     begin
       
   670     case t^.Kind of
       
   671         gtHedgehog,
       
   672             gtMine,
       
   673             gtSMine,
       
   674             gtCase,
       
   675             gtTarget,
       
   676             gtExplosives,
       
   677             gtStructure: begin
       
   678 //addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg));
       
   679                     dmg:= 0;
       
   680                     r:= Gear^.Radius + t^.Radius;
       
   681                     dx:= Gear^.X-t^.X;
       
   682                     dx.isNegative:= false;
       
   683                     dy:= Gear^.Y-t^.Y;
       
   684                     dy.isNegative:= false;
       
   685                     if r-hwRound(dx+dy) > 0 then
       
   686                         begin
       
   687                         dist:= hwRound(Distance(dx, dy));
       
   688                         dmg:= ModifyDamage(min(r - dist, 25), t);
       
   689                         end;
       
   690                     if dmg > 0 then
       
   691                         begin
       
   692                         if (not t^.Invulnerable) then
       
   693                             ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet)
       
   694                         else
       
   695                             Gear^.State:= Gear^.State or gstWinner;
       
   696 
       
   697                         DeleteCI(t);
       
   698                         t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
       
   699                         t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
       
   700                         t^.State:= t^.State or gstMoving;
       
   701                         t^.Active:= true;
       
   702                         FollowGear:= t
       
   703                         end
       
   704                     end;
       
   705             gtGrave: begin
       
   706                     dmg:= 0;
       
   707                     r:= Gear^.Radius + t^.Radius;
       
   708                     dx:= Gear^.X-t^.X;
       
   709                     dx.isNegative:= false;
       
   710                     dy:= Gear^.Y-t^.Y;
       
   711                     dy.isNegative:= false;
       
   712                     if r-hwRound(dx+dy) > 0 then
       
   713                         begin
       
   714                         dist:= hwRound(Distance(dx, dy));
       
   715                         dmg:= ModifyDamage(min(r - dist, 25), t);
       
   716                         end;
       
   717                     if dmg > 0 then
       
   718                         begin
       
   719                         t^.dY:= - _0_1;
       
   720                         t^.Active:= true
       
   721                         end
       
   722                     end;
       
   723         end;
       
   724     t:= t^.NextGear
       
   725     end;
       
   726 if (GameFlags and gfSolidLand) = 0 then DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius)
       
   727 end;
       
   728 
       
   729 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
       
   730 var t: PGearArray;
       
   731     Gear: PGear;
       
   732     i, tmpDmg: LongInt;
       
   733     VGear: PVisualGear;
       
   734 begin
       
   735 t:= CheckGearsCollision(Ammo);
       
   736 // Just to avoid hogs on rope dodging fire.
       
   737 if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) and
       
   738    (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) and
       
   739    (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then
       
   740     begin
       
   741     t^.ar[t^.Count]:= CurrentHedgehog^.Gear;
       
   742     inc(t^.Count)
       
   743     end;
       
   744 
       
   745 i:= t^.Count;
       
   746 
       
   747 if (Ammo^.Kind = gtFlame) and (i > 0) then Ammo^.Health:= 0;
       
   748 while i > 0 do
       
   749     begin
       
   750     dec(i);
       
   751     Gear:= t^.ar[i];
       
   752     tmpDmg:= ModifyDamage(Damage, Gear);
       
   753     if (Gear^.State and gstNoDamage) = 0 then
       
   754         begin
       
   755 
       
   756         if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then 
       
   757             begin
       
   758             VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit);
       
   759             if VGear <> nil then VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY);
       
   760             end;
       
   761 
       
   762         if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then Gear^.FlightTime:= 1;
       
   763 
       
   764         case Gear^.Kind of
       
   765             gtHedgehog,
       
   766             gtMine,
       
   767             gtSMine,
       
   768             gtTarget,
       
   769             gtCase,
       
   770             gtExplosives,
       
   771             gtStructure: begin
       
   772                     if (Ammo^.Kind = gtDrill) then begin Ammo^.Timer:= 0; exit; end;
       
   773                     if (not Gear^.Invulnerable) then
       
   774                         ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove)
       
   775                     else
       
   776                         Gear^.State:= Gear^.State or gstWinner;
       
   777                     if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then 
       
   778                         begin
       
   779                         if (Ammo^.Hedgehog^.Gear <> nil) then Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable);
       
   780                         ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch
       
   781                         end;
       
   782 
       
   783                     DeleteCI(Gear);
       
   784                     if (Gear^.Kind = gtHedgehog) and Gear^.Hedgehog^.King then
       
   785                         begin
       
   786                         Gear^.dX:= Ammo^.dX * Power * _0_005;
       
   787                         Gear^.dY:= Ammo^.dY * Power * _0_005
       
   788                         end
       
   789                     else
       
   790                         begin
       
   791                         Gear^.dX:= Ammo^.dX * Power * _0_01;
       
   792                         Gear^.dY:= Ammo^.dY * Power * _0_01
       
   793                         end;
       
   794 
       
   795                     Gear^.Active:= true;
       
   796                     Gear^.State:= Gear^.State or gstMoving;
       
   797 
       
   798                     if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then
       
   799                         begin
       
   800                         if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX))
       
   801                             or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   802                         if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX))
       
   803                             or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   804                         if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX))
       
   805                             or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1;
       
   806                         end;
       
   807 
       
   808                     if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then FollowGear:= Gear
       
   809                     end;
       
   810         end
       
   811         end;
       
   812     end;
       
   813 if i <> 0 then SetAllToActive
       
   814 end;
       
   815 
       
   816 procedure AssignHHCoords;
       
   817 var i, t, p, j: LongInt;
       
   818     ar: array[0..Pred(cMaxHHs)] of PHedgehog;
       
   819     Count: Longword;
       
   820 begin
       
   821 if (GameFlags and gfPlaceHog) <> 0 then PlacingHogs:= true;
       
   822 if (GameFlags and gfDivideTeams) <> 0 then
       
   823     begin
       
   824     t:= 0;
       
   825     TryDo(ClansCount = 2, 'More or less than 2 clans on map in divided teams mode!', true);
       
   826     for p:= 0 to 1 do
       
   827         begin
       
   828         with ClansArray[p]^ do
       
   829             for j:= 0 to Pred(TeamsNumber) do
       
   830                 with Teams[j]^ do
       
   831                     for i:= 0 to cMaxHHIndex do
       
   832                         with Hedgehogs[i] do
       
   833                             if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
       
   834                                 begin
       
   835                                 if PlacingHogs then Unplaced:= true
       
   836                                 else FindPlace(Gear, false, t, t + LAND_WIDTH div 2);// could make Gear == nil;
       
   837                                 if Gear <> nil then
       
   838                                     begin
       
   839                                     Gear^.Pos:= GetRandom(49);
       
   840                                     Gear^.dX.isNegative:= p = 1;
       
   841                                     end
       
   842                                 end;
       
   843         t:= LAND_WIDTH div 2
       
   844         end
       
   845     end else // mix hedgehogs
       
   846     begin
       
   847     Count:= 0;
       
   848     for p:= 0 to Pred(TeamsCount) do
       
   849         with TeamsArray[p]^ do
       
   850         begin
       
   851         for i:= 0 to cMaxHHIndex do
       
   852             with Hedgehogs[i] do
       
   853                 if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
       
   854                     begin
       
   855                     ar[Count]:= @Hedgehogs[i];
       
   856                     inc(Count)
       
   857                     end;
       
   858         end;
       
   859     // unC0Rr, while it is true user can watch value on map screen, IMO this (and check above) should be enforced in UI
       
   860     // - is there a good place to put values for the different widgets to check?  Right now they are kind of disconnected.
       
   861     //it would be nice if divide teams, forts mode and hh per map could all be checked by the team widget, or maybe disable start button
       
   862     TryDo(Count <= MaxHedgehogs, 'Too many hedgehogs for this map! (max # is ' + inttostr(MaxHedgehogs) + ')', true);
       
   863     while (Count > 0) do
       
   864         begin
       
   865         i:= GetRandom(Count);
       
   866         if PlacingHogs then ar[i]^.Unplaced:= true
       
   867         else FindPlace(ar[i]^.Gear, false, 0, LAND_WIDTH);
       
   868         if ar[i]^.Gear <> nil then
       
   869             begin
       
   870             ar[i]^.Gear^.dX.isNegative:= hwRound(ar[i]^.Gear^.X) > LAND_WIDTH div 2;
       
   871             ar[i]^.Gear^.Pos:= GetRandom(19)
       
   872             end;
       
   873         ar[i]:= ar[Count - 1];
       
   874         dec(Count)
       
   875         end
       
   876     end
       
   877 end;
       
   878 
       
   879 function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray;
       
   880 var
       
   881     t: PGear;
       
   882     l: Longword;
       
   883 begin
       
   884     r:= r*r;
       
   885     GearsNear := nil;
       
   886     t := GearsList;
       
   887     while t <> nil do 
       
   888         begin
       
   889         if (t^.Kind = Kind) 
       
   890             and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then
       
   891             begin
       
   892             l:= Length(GearsNear);
       
   893             SetLength(GearsNear, l + 1);
       
   894             GearsNear[l] := t;
       
   895             end;
       
   896         t := t^.NextGear;
       
   897     end;
       
   898 end;
       
   899 
       
   900 {procedure AmmoFlameWork(Ammo: PGear);
       
   901 var t: PGear;
       
   902 begin
       
   903 t:= GearsList;
       
   904 while t <> nil do
       
   905     begin
       
   906     if (t^.Kind = gtHedgehog) and (t^.Y < Ammo^.Y) then
       
   907         if not (hwSqr(Ammo^.X - t^.X) + hwSqr(Ammo^.Y - t^.Y - int2hwFloat(cHHRadius)) * 2 > _2) then
       
   908             begin
       
   909             ApplyDamage(t, 5);
       
   910             t^.dX:= t^.dX + (t^.X - Ammo^.X) * _0_02;
       
   911             t^.dY:= - _0_25;
       
   912             t^.Active:= true;
       
   913             DeleteCI(t);
       
   914             FollowGear:= t
       
   915             end;
       
   916     t:= t^.NextGear
       
   917     end;
       
   918 end;}
       
   919 
       
   920 
       
   921 function CountGears(Kind: TGearType): Longword;
       
   922 var t: PGear;
       
   923     count: Longword = 0;
       
   924 begin
       
   925 
       
   926 t:= GearsList;
       
   927 while t <> nil do
       
   928     begin
       
   929     if t^.Kind = Kind then inc(count);
       
   930     t:= t^.NextGear
       
   931     end;
       
   932 CountGears:= count;
       
   933 end;
       
   934 
       
   935 function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content: Longword): PGear;
       
   936 begin
       
   937     FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0);
       
   938     cCaseFactor := 0;
       
   939 
       
   940     if (crate <> HealthCrate) and (content > ord(High(TAmmoType))) then content := ord(High(TAmmoType));
       
   941 
       
   942     case crate of
       
   943         HealthCrate: begin
       
   944             FollowGear^.Pos := posCaseHealth;
       
   945             FollowGear^.Health := content;
       
   946             AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
       
   947             end;
       
   948         AmmoCrate: begin
       
   949             FollowGear^.Pos := posCaseAmmo;
       
   950             FollowGear^.AmmoType := TAmmoType(content);
       
   951             AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
       
   952             end;
       
   953         UtilityCrate: begin
       
   954             FollowGear^.Pos := posCaseUtility;
       
   955             FollowGear^.AmmoType := TAmmoType(content);
       
   956             AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
       
   957             end;
       
   958     end;
       
   959 
       
   960     if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH);
       
   961 
       
   962     SpawnCustomCrateAt := FollowGear;
       
   963 end;
       
   964 
       
   965 function SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean): PGear;
       
   966 begin
       
   967     FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0);
       
   968     cCaseFactor := 0;
       
   969     FollowGear^.Pos := posCaseDummy;
       
   970     
       
   971     if explode then FollowGear^.Pos := FollowGear^.Pos + posCaseExplode;
       
   972     if poison then FollowGear^.Pos := FollowGear^.Pos + posCasePoison;
       
   973 
       
   974     case crate of
       
   975         HealthCrate: begin
       
   976             FollowGear^.Pos := FollowGear^.Pos + posCaseHealth;
       
   977             AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
       
   978             end;
       
   979         AmmoCrate: begin
       
   980             FollowGear^.Pos := FollowGear^.Pos + posCaseAmmo;
       
   981             AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
       
   982             end;
       
   983         UtilityCrate: begin
       
   984             FollowGear^.Pos := FollowGear^.Pos + posCaseUtility;
       
   985             AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
       
   986             end;
       
   987     end;
       
   988 
       
   989     if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH);
       
   990 
       
   991     SpawnFakeCrateAt := FollowGear;
       
   992 end;
       
   993 
       
   994 function GetAmmo(Hedgehog: PHedgehog): TAmmoType;
       
   995 var t, aTot: LongInt;
       
   996     i: TAmmoType;
       
   997 begin
       
   998 
       
   999 aTot:= 0;
       
  1000 for i:= Low(TAmmoType) to High(TAmmoType) do
       
  1001     if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
       
  1002         inc(aTot, Ammoz[i].Probability);
       
  1003 
       
  1004 t:= aTot;
       
  1005 i:= Low(TAmmoType);
       
  1006 if (t > 0) then
       
  1007     begin
       
  1008     t:= GetRandom(t);
       
  1009     while t >= 0 do
       
  1010       begin
       
  1011       inc(i);
       
  1012       if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
       
  1013           dec(t, Ammoz[i].Probability)
       
  1014       end
       
  1015     end;
       
  1016 GetAmmo:= i
       
  1017 end;
       
  1018 
       
  1019 function GetUtility(Hedgehog: PHedgehog): TAmmoType;
       
  1020 var t, uTot: LongInt;
       
  1021     i: TAmmoType;
       
  1022 begin
       
  1023 
       
  1024 uTot:= 0;
       
  1025 for i:= Low(TAmmoType) to High(TAmmoType) do
       
  1026     if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then
       
  1027         inc(uTot, Ammoz[i].Probability);
       
  1028 
       
  1029 t:= uTot;
       
  1030 i:= Low(TAmmoType);
       
  1031 if (t > 0) then
       
  1032     begin
       
  1033     t:= GetRandom(t);
       
  1034     while t >= 0 do
       
  1035       begin
       
  1036       inc(i);
       
  1037       if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then
       
  1038           dec(t, Ammoz[i].Probability)
       
  1039       end
       
  1040     end;
       
  1041 GetUtility:= i
       
  1042 end;
       
  1043 
       
  1044 
       
  1045 
       
  1046 procedure SpawnBoxOfSmth;
       
  1047 var t, aTot, uTot, a, h: LongInt;
       
  1048     i: TAmmoType;
       
  1049 begin
       
  1050 if (PlacingHogs) or
       
  1051    (cCaseFactor = 0) or
       
  1052    (CountGears(gtCase) >= 5) or
       
  1053    (GetRandom(cCaseFactor) <> 0) then exit;
       
  1054 
       
  1055 FollowGear:= nil;
       
  1056 aTot:= 0;
       
  1057 uTot:= 0;
       
  1058 for i:= Low(TAmmoType) to High(TAmmoType) do
       
  1059     if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
       
  1060         inc(aTot, Ammoz[i].Probability)
       
  1061     else
       
  1062         inc(uTot, Ammoz[i].Probability);
       
  1063 
       
  1064 t:=0;
       
  1065 a:=aTot;
       
  1066 h:= 1;
       
  1067 
       
  1068 if (aTot+uTot) <> 0 then
       
  1069     if ((GameFlags and gfInvulnerable) = 0) then
       
  1070         begin
       
  1071         h:= cHealthCaseProb * 100;
       
  1072         t:= GetRandom(10000);
       
  1073         a:= (10000-h)*aTot div (aTot+uTot)
       
  1074         end
       
  1075     else
       
  1076         begin
       
  1077         t:= GetRandom(aTot+uTot);
       
  1078         h:= 0
       
  1079         end;
       
  1080 
       
  1081 
       
  1082 if t<h then
       
  1083     begin
       
  1084     FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
       
  1085     FollowGear^.Health:= cHealthCaseAmount;
       
  1086     FollowGear^.Pos:= posCaseHealth;
       
  1087     AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
       
  1088     end
       
  1089 else if (t<a+h) then
       
  1090     begin
       
  1091     t:= aTot;
       
  1092     if (t > 0) then
       
  1093         begin
       
  1094         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
       
  1095         t:= GetRandom(t);
       
  1096         i:= Low(TAmmoType);
       
  1097         FollowGear^.Pos:= posCaseAmmo;
       
  1098         FollowGear^.AmmoType:= i;
       
  1099         AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
       
  1100         end
       
  1101     end
       
  1102 else
       
  1103     begin
       
  1104     t:= uTot;
       
  1105     if (t > 0) then
       
  1106         begin
       
  1107         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
       
  1108         t:= GetRandom(t);
       
  1109         i:= Low(TAmmoType);
       
  1110         FollowGear^.Pos:= posCaseUtility;
       
  1111         FollowGear^.AmmoType:= i;
       
  1112         AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
       
  1113         end
       
  1114     end;
       
  1115 
       
  1116 // handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities
       
  1117 if (FollowGear <> nil) then
       
  1118     begin
       
  1119     FindPlace(FollowGear, true, 0, LAND_WIDTH);
       
  1120 
       
  1121     if (FollowGear <> nil) then
       
  1122         AddVoice(sndReinforce, CurrentTeam^.voicepack)
       
  1123     end
       
  1124 end;
       
  1125 
       
  1126 
       
  1127 function GearByUID(uid : Longword) : PGear;
       
  1128 var gear: PGear;
       
  1129 begin
       
  1130 GearByUID:= nil;
       
  1131 if uid = 0 then exit;
       
  1132 if (lastGearByUID <> nil) and (lastGearByUID^.uid = uid) then
       
  1133     begin
       
  1134     GearByUID:= lastGearByUID;
       
  1135     exit
       
  1136     end;
       
  1137 gear:= GearsList;
       
  1138 while gear <> nil do
       
  1139     begin
       
  1140     if gear^.uid = uid then
       
  1141         begin
       
  1142         lastGearByUID:= gear;
       
  1143         GearByUID:= gear;
       
  1144         exit
       
  1145         end;
       
  1146     gear:= gear^.NextGear
       
  1147     end
       
  1148 end;
       
  1149 
       
  1150 
       
  1151 procedure chSkip(var s: shortstring);
       
  1152 begin
       
  1153 s:= s; // avoid compiler hint
       
  1154 if not CurrentTeam^.ExtDriven then SendIPC(',');
       
  1155 uStats.Skipped;
       
  1156 skipFlag:= true
       
  1157 end;
       
  1158 
       
  1159 procedure chHogSay(var s: shortstring);
       
  1160 var Gear: PVisualGear;
       
  1161     text: shortstring;
       
  1162     hh: PHedgehog;
       
  1163     i, x, t, h: byte;
       
  1164     c, j: LongInt;
       
  1165 begin
       
  1166     hh:= nil;
       
  1167     i:= 0;
       
  1168     t:= 0;
       
  1169     x:= byte(s[1]);  // speech type
       
  1170     if x < 4 then
       
  1171         begin
       
  1172         t:= byte(s[2]);  // team
       
  1173         if Length(s) > 2 then h:= byte(s[3])  // target hog
       
  1174         end;
       
  1175     // allow targetting a hog by specifying a number as the first portion of the text
       
  1176     if (x < 4) and (h > byte('0')) and (h < byte('9')) then i:= h - 48;
       
  1177     if i <> 0 then text:= copy(s, 4, Length(s) - 1)
       
  1178     else if x < 4 then text:= copy(s, 3, Length(s) - 1)
       
  1179     else text:= copy(s, 2, Length(s) - 1);
       
  1180 
       
  1181     (*
       
  1182     if CheckNoTeamOrHH then
       
  1183         begin
       
  1184         ParseCommand('say ' + text, true);
       
  1185         exit
       
  1186         end;
       
  1187     *)
       
  1188 
       
  1189     if (x < 4) and (TeamsArray[t] <> nil) then
       
  1190         begin
       
  1191             // if team matches current hedgehog team, default to current hedgehog
       
  1192             if (i = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Team = TeamsArray[t]) then hh:= CurrentHedgehog
       
  1193             else 
       
  1194                 begin
       
  1195             // otherwise use the first living hog or the hog amongs the remaining ones indicated by i
       
  1196                 j:= 0;
       
  1197                 c:= 0;
       
  1198                 while (j <= cMaxHHIndex) and (hh = nil) do
       
  1199                     begin
       
  1200                     if (TeamsArray[t]^.Hedgehogs[j].Gear <> nil) then
       
  1201                         begin
       
  1202                         inc(c);
       
  1203                         if (i=0) or (i=c) then
       
  1204                             hh:= @TeamsArray[t]^.Hedgehogs[j]
       
  1205                         end;
       
  1206                     inc(j)
       
  1207                     end
       
  1208                 end;
       
  1209         if hh <> nil then 
       
  1210             begin
       
  1211             Gear:= AddVisualGear(0, 0, vgtSpeechBubble);
       
  1212             if Gear <> nil then
       
  1213                 begin
       
  1214                 Gear^.Hedgehog:= hh;
       
  1215                 Gear^.Text:= text;
       
  1216                 Gear^.FrameTicks:= x
       
  1217                 end
       
  1218             end
       
  1219         //else ParseCommand('say ' + text, true)
       
  1220         end
       
  1221     else if (x >= 4) then
       
  1222         begin
       
  1223         SpeechType:= x-3;
       
  1224         SpeechText:= text
       
  1225         end;
       
  1226 end;
       
  1227 
       
  1228 procedure initModule;
       
  1229 begin
       
  1230     RegisterVariable('skip', vtCommand, @chSkip, false);
  1227     RegisterVariable('skip', vtCommand, @chSkip, false);
  1231     RegisterVariable('hogsay', vtCommand, @chHogSay, true );
  1228     RegisterVariable('hogsay', vtCommand, @chHogSay, true );
  1232 
  1229 
  1233     CurAmmoGear:= nil;
  1230     CurAmmoGear:= nil;
  1234     GearsList:= nil;
  1231     GearsList:= nil;