~ubuntu-branches/ubuntu/wily/hedgewars/wily

« back to all changes in this revision

Viewing changes to hedgewars/uGears.pas

  • Committer: Package Import Robot
  • Author(s): Dmitry E. Oboukhov
  • Date: 2011-09-23 10:16:55 UTC
  • mfrom: (1.2.11 upstream)
  • Revision ID: package-import@ubuntu.com-20110923101655-3977th2gc5n0a3pv
Tags: 0.9.16-1
* New upstream version.
 + Downloadable content! Simply click to install any content.
   New voices, hats, maps, themes, translations, music, scripts...
   Hedgewars is now more customisable than ever before! As time goes
   by we will be soliciting community content to feature on this page,
   so remember to check it from time to time. If you decide you want
   to go back to standard Hedgewars, just remove the Data directory
   from your Hedgewars config directory.
 + 3-D rendering! Diorama-like rendering of the game in a variety
   of 3D modes. Let us know which ones work best for you, we didn't
   really have the equipment to test them all.
 + Resizable game window.
 + New utilities! The Time Box will remove one of your hedgehogs
   from the game for a while, protecting from attack until it returns,
   somewhere else on the map. Land spray will allow you to build bridges,
   seal up holes, or just make life unpleasant for your enemies.
 + New single player: Bamboo Thicket, That Sinking Feeling, Newton and
   the Tree and multi-player: The Specialists, Space Invaders,
   Racer - scripts! And a ton more script hooks for scripters
 + New twists on old weapons. Drill strike, seduction and fire have
   been adjusted. Defective mines have been added, rope can attach to
   hogs/crates/barrels again, grenades now have variable bounce (use
   precise key + 1-5). Portal gun is now more usable in flight and
   all game actions are a lot faster.
 + New theme - Golf, dozens of new community hats and a new
   localised Default voice, Ukranian.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
(*
2
2
 * Hedgewars, a free turn based strategy game
3
 
 * Copyright (c) 2004-2010 Andrey Korotaev <unC0Rr@gmail.com>
 
3
 * Copyright (c) 2004-2011 Andrey Korotaev <unC0Rr@gmail.com>
4
4
 *
5
5
 * This program is free software; you can redistribute it and/or modify
6
6
 * it under the terms of the GNU General Public License as published by
19
19
{$INCLUDE "options.inc"}
20
20
 
21
21
unit uGears;
 
22
(*
 
23
 * This unit defines the behavior of gears.
 
24
 *
 
25
 * Gears are "things"/"objects" that may be visible to the player or not,
 
26
 * but always have an effect on the course of the game.
 
27
 *
 
28
 * E.g.: weapons, hedgehogs, etc.
 
29
 *
 
30
 * Note: The visual appearance of gears is defined in the unit "uGearsRender".
 
31
 *
 
32
 * Note: Gears that do not have an effect on the game but are just visual
 
33
 *       effects are called "Visual Gears" and defined in the respective unit!
 
34
 *)
22
35
interface
23
36
uses SDLh, uConsts, uFloat, uTypes;
24
37
 
25
38
procedure initModule;
26
39
procedure freeModule;
27
40
function  AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
28
 
function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content: Longword ): PGear;
 
41
function  SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content: Longword ): PGear;
 
42
function  SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean ): PGear;
 
43
function  GetAmmo: TAmmoType;
 
44
function  GetUtility: TAmmoType;
29
45
procedure ResurrectHedgehog(gear: PGear);
 
46
procedure HideHog(HH: PHedgehog);
 
47
procedure RestoreHog(HH: PHedgehog);
30
48
procedure ProcessGears;
31
49
procedure EndTurnCleanup;
32
 
procedure ApplyDamage(Gear: PGear; Damage: Longword; Source: TDamageSource);
 
50
procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
33
51
procedure SetAllToActive;
34
52
procedure SetAllHHToActive;
35
53
procedure DrawGears;
50
68
     uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture;
51
69
 
52
70
 
53
 
procedure doMakeExplosion(X, Y, Radius: LongInt; Mask: LongWord); forward;
54
 
procedure doMakeExplosion(X, Y, Radius: LongInt; Mask, Tint: LongWord); forward;
 
71
procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord = $FFFFFFFF); forward;
55
72
procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward;
56
73
//procedure AmmoFlameWork(Ammo: PGear); forward;
57
74
function  GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray; forward;
60
77
procedure AfterAttack; forward;
61
78
procedure HedgehogStep(Gear: PGear); forward;
62
79
procedure doStepHedgehogMoving(Gear: PGear); forward;
63
 
procedure HedgehogChAngle(Gear: PGear); forward;
 
80
procedure HedgehogChAngle(HHGear: PGear); forward;
64
81
procedure ShotgunShot(Gear: PGear); forward;
65
82
procedure PickUp(HH, Gear: PGear); forward;
66
 
procedure HHSetWeapon(Gear: PGear); forward;
 
83
procedure HHSetWeapon(HHGear: PGear); forward;
67
84
procedure doStepCase(Gear: PGear); forward;
68
85
 
 
86
// For better maintainability the step handlers of gears are stored in
 
87
// separate files.
 
88
// Note: step handlers of gears that are hedgehogs are in a different file
 
89
//       than the handlers for all other gears.
69
90
{$INCLUDE "GSHandlers.inc"}
70
91
{$INCLUDE "HHHandlers.inc"}
71
92
 
89
110
            @doStepFirePunch,
90
111
            @doStepActionTimer,
91
112
            @doStepActionTimer,
92
 
            @doStepActionTimer,
93
113
            @doStepParachute,
94
114
            @doStepAirAttack,
95
115
            @doStepAirBomb,
103
123
            @doStepKamikaze,
104
124
            @doStepCake,
105
125
            @doStepSeduction,
106
 
            @doStepWatermelon,
 
126
            @doStepBomb,
107
127
            @doStepCluster,
108
128
            @doStepBomb,
109
129
            @doStepWaterUp,
129
149
            @doStepResurrector,
130
150
            @doStepNapalmBomb,
131
151
            @doStepSnowball,
132
 
            @doStepSnowflake
133
 
            );
 
152
            @doStepSnowflake,
 
153
            @doStepStructure,
 
154
            @doStepLandGun,
 
155
            @doStepTardis);
134
156
 
135
157
procedure InsertGearToList(Gear: PGear);
136
158
var tmp, ptmp: PGear;
182
204
var gear: PGear;
183
205
begin
184
206
inc(Counter);
185
 
{$IFDEF DEBUGFILE}
186
207
AddFileLog('AddGear: #' + inttostr(Counter) + ' (' + inttostr(x) + ',' + inttostr(y) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + EnumToStr(Kind));
187
 
{$ENDIF}
188
208
 
189
209
New(gear);
190
210
FillChar(gear^, sizeof(TGear), 0);
191
211
gear^.X:= int2hwFloat(X);
192
212
gear^.Y:= int2hwFloat(Y);
 
213
gear^.Target.X:= NoPointX;
193
214
gear^.Kind := Kind;
194
215
gear^.State:= State;
195
216
gear^.Active:= true;
198
219
gear^.doStep:= doStepHandlers[Kind];
199
220
gear^.CollisionIndex:= -1;
200
221
gear^.Timer:= Timer;
201
 
gear^.Z:= cUsualZ;
202
222
gear^.FlightTime:= 0;
203
223
gear^.uid:= Counter;
204
224
gear^.SoundChannel:= -1;
205
225
gear^.ImpactSound:= sndNone;
206
226
gear^.nImpactSounds:= 0;
 
227
// Define ammo association, if any.
 
228
gear^.AmmoType:= GearKindAmmoTypeMap[Kind];
 
229
if Ammoz[Gear^.AmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0 then gear^.Z:= cHHZ+1
 
230
else gear^.Z:= cUsualZ;
207
231
 
208
 
if CurrentTeam <> nil then
 
232
if CurrentHedgehog <> nil then
209
233
    begin
210
234
    gear^.Hedgehog:= CurrentHedgehog;
211
235
    gear^.IntersectGear:= CurrentHedgehog^.Gear
212
236
    end;
213
 
 
 
237
    
214
238
case Kind of
215
 
     gtBomb,
 
239
     gtGrenade,
216
240
     gtClusterBomb,
217
241
     gtGasBomb: begin
218
242
                gear^.ImpactSound:= sndGrenadeImpact;
256
280
                gear^.Density:= _1;
257
281
                end;
258
282
       gtSnowball: begin
 
283
                gear^.ImpactSound:= sndMudballImpact;
 
284
                gear^.nImpactSounds:= 1;
259
285
                gear^.Radius:= 4;
260
286
                gear^.Elasticity:= _1;
261
287
                gear^.Friction:= _1;
265
291
     gtFlake: begin
266
292
                with Gear^ do
267
293
                    begin
 
294
                    Pos:= 0;
 
295
                    Radius:= 1;
268
296
                    DirAngle:= random * 360;
269
297
                    dx.isNegative:= GetRandom(2) = 0;
270
298
                    dx.QWordValue:= GetRandom(100000000);
271
299
                    dy.isNegative:= false;
272
300
                    dy.QWordValue:= GetRandom(70000000);
 
301
                    State:= State or gstInvisible;
273
302
                    if GetRandom(2) = 0 then dx := -dx;
274
303
                    Health:= random(vobFrameTicks);
275
304
                    Timer:= random(vobFramesCount);
289
318
                gear^.Elasticity:= _0_9;
290
319
                gear^.Tag:= getRandom(32);
291
320
                end;
 
321
   gtSeduction: begin
 
322
                gear^.Radius:= 250;
 
323
                end;
292
324
 gtShotgunShot: begin
293
325
                gear^.Timer:= 900;
294
326
                gear^.Radius:= 2
307
339
                RopePoints.Count:= 0;
308
340
                end;
309
341
        gtMine: begin
 
342
                gear^.ImpactSound:= sndMineImpact;
 
343
                gear^.nImpactSounds:= 1;
310
344
                gear^.Health:= 10;
311
345
                gear^.State:= gear^.State or gstMoving;
312
346
                gear^.Radius:= 2;
340
374
                gear^.Elasticity:= _0_4;
341
375
                gear^.Friction:= _0_995;
342
376
                gear^.Density:= _6;
343
 
                gear^.Health:= cBarrelHealth
 
377
                gear^.Health:= cBarrelHealth;
 
378
                gear^.Z:= cHHZ-1
344
379
                end;
345
380
  gtDEagleShot: begin
346
381
                gear^.Radius:= 1;
396
431
                gear^.Elasticity:= _0_3;
397
432
                gear^.Timer:= 0
398
433
                end;
 
434
      gtTardis: begin
 
435
                gear^.Timer:= 0;
 
436
                gear^.Pos:= 1;
 
437
                gear^.Z:= cCurrHHZ+1;
 
438
                end;
399
439
      gtMortar: begin
400
440
                gear^.Radius:= 4;
401
441
                gear^.Elasticity:= _0_2;
428
468
                gear^.Timer:= 5000
429
469
                end;
430
470
       gtDrill: begin
431
 
                gear^.Timer:= 5000;
 
471
                if gear^.Timer = 0 then gear^.Timer:= 5000;
 
472
                // Tag for drill strike. if 1 then first impact occured already
 
473
                gear^.Tag := 0;
432
474
                gear^.Radius:= 4;
433
475
                gear^.Density:= _1;
434
476
                end;
476
518
                gear^.ImpactSound:= sndMelonImpact;
477
519
                gear^.nImpactSounds:= 1;
478
520
                gear^.AdvBounce:= 0;
479
 
                gear^.Radius:= 16;
480
 
                gear^.Tag:= 0;
 
521
                gear^.Radius:= 17;
 
522
                // set color
 
523
                gear^.Tag:= 2 * gear^.Timer;
481
524
                gear^.Timer:= 15000;
482
525
                gear^.RenderTimer:= false;
483
526
                gear^.Health:= 100;
496
539
                gear^.Health:= 500;
497
540
                gear^.Damage:= 100;
498
541
                end;
 
542
     gtLandGun: begin
 
543
                gear^.Tag:= 10;
 
544
                gear^.Timer:= 10;
 
545
                gear^.Health:= 1000;
 
546
                gear^.Damage:= 100;
 
547
                end;
499
548
 gtPoisonCloud: begin
500
549
                gear^.Timer:= 5000;
501
550
                gear^.dY:= int2hwfloat(-4 + longint(getRandom(8))) / 1000;
512
561
                gear^.Radius:= 5;
513
562
                gear^.Density:= _1_5;
514
563
                end;
 
564
   gtStructure: begin
 
565
                gear^.Elasticity:= _0_55;
 
566
                gear^.Friction:= _0_995;
 
567
                gear^.Density:= _0_9;
 
568
                gear^.Radius:= 13;
 
569
                gear^.Health:= 200;
 
570
                gear^.Tag:= 3;
 
571
                end;
515
572
    end;
516
573
 
517
574
InsertGearToList(gear);
546
603
else if Gear^.Kind = gtHedgehog then
547
604
    if (CurAmmoGear <> nil) and (CurrentHedgehog^.Gear = Gear) then
548
605
        begin
 
606
        AttackBar:= 0;
549
607
        Gear^.Message:= gmDestroy;
550
608
        CurAmmoGear^.Message:= gmDestroy;
551
609
        exit
556
614
            begin
557
615
            t:= max(Gear^.Damage, Gear^.Health);
558
616
            Gear^.Damage:= t;
559
 
            if (cWaterOpacity < $FF) and (hwRound(Gear^.Y) < cWaterLine + 256) then
 
617
            if ((not SuddenDeathDmg and (cWaterOpacity < $FF)) or (SuddenDeathDmg and (cWaterOpacity < $FF))) and (hwRound(Gear^.Y) < cWaterLine + 256) then
560
618
                spawnHealthTagForHH(Gear, t);
561
 
            uStats.HedgehogDamaged(Gear)
562
619
            end;
563
620
 
564
621
        team:= Gear^.Hedgehog^.Team;
565
622
        if CurrentHedgehog^.Gear = Gear then
 
623
            begin
 
624
            AttackBar:= 0;
566
625
            FreeActionsList; // to avoid ThinkThread on drawned gear
 
626
            if ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) and (CurrentHedgehog^.MultiShootAttacks > 0) then OnUsedAmmo(CurrentHedgehog^);
 
627
            end;
567
628
 
568
629
        Gear^.Hedgehog^.Gear:= nil;
569
630
        if Gear^.Hedgehog^.King then
579
640
                    TeamGoneEffect(team^.Clan^.Teams[i]^)
580
641
                    end
581
642
            end;
 
643
 
 
644
        // should be not CurrentHedgehog, but hedgehog of the last gear which caused damage to this hog
 
645
        // same stand for CheckHHDamage
 
646
        if (Gear^.LastDamage <> nil) then
 
647
            uStats.HedgehogDamaged(Gear, Gear^.LastDamage, 0, true)
 
648
        else
 
649
            uStats.HedgehogDamaged(Gear, CurrentHedgehog, 0, true);
 
650
 
582
651
        inc(KilledHHs);
583
652
        RecountTeamHealth(team);
584
653
        if (CurrentHedgehog <> nil) and CurrentHedgehog^.Effects[heResurrectable] and not Gear^.Hedgehog^.Effects[heResurrectable] then
589
658
                Team^.AIKillsTex := RenderStringTex(inttostr(Team^.stats.AIKills), Team^.Clan^.Color, fnt16);
590
659
                end
591
660
        end;
592
 
{$IFDEF DEBUGFILE}
593
 
with Gear^ do AddFileLog('Delete: #' + inttostr(uid) + ' (' + inttostr(hwRound(x)) + ',' + inttostr(hwRound(y)) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + EnumToStr(Kind));
594
 
{$ENDIF}
 
661
with Gear^ do
 
662
    AddFileLog('Delete: #' + inttostr(uid) + ' (' + inttostr(hwRound(x)) + ',' + inttostr(hwRound(y)) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + EnumToStr(Kind));
595
663
 
596
664
if CurAmmoGear = Gear then CurAmmoGear:= nil;
597
665
if FollowGear = Gear then FollowGear:= nil;
 
666
if lastGearByUID = Gear then lastGearByUID := nil;
598
667
RemoveGearFromList(Gear);
599
668
Dispose(Gear)
600
669
end;
614
683
        (not Gear^.Invulnerable) then
615
684
            begin
616
685
            CheckNoDamage:= false;
617
 
            uStats.HedgehogDamaged(Gear);
 
686
 
618
687
            dmg:= Gear^.Damage;
619
688
            if Gear^.Health < dmg then
620
689
                begin
791
860
    stHealth: begin
792
861
            if (cWaterRise <> 0) or (cHealthDecrease <> 0) then
793
862
                begin
794
 
                if (TotalRounds = cSuddenDTurns) and not SuddenDeathDmg and not isInMultiShoot then
 
863
                if (TotalRounds = cSuddenDTurns) and not SuddenDeath and not isInMultiShoot then
795
864
                    begin
796
 
                    SuddenDeathDmg:= true;
 
865
                    SuddenDeath:= true;
 
866
                    if cHealthDecrease <> 0 then
 
867
                        begin
 
868
                        SuddenDeathDmg:= true;
 
869
                        
 
870
                        // flash
 
871
                        ScreenFade:= sfFromWhite;
 
872
                        ScreenFadeValue:= sfMax;
 
873
                        ScreenFadeSpeed:= 1;
 
874
                        
 
875
                        ChangeToSDClouds;
 
876
                        ChangeToSDFlakes;
 
877
                        glClearColor(SDSkyColor.r * (SDTint/255) / 255, SDSkyColor.g * (SDTint/255) / 255, SDSkyColor.b * (SDTint/255) / 255, 0.99);
 
878
                        end;
797
879
                    AddCaption(trmsg[sidSuddenDeath], cWhiteColor, capgrpGameState);
798
 
                    playSound(sndSuddenDeath)
 
880
                    playSound(sndSuddenDeath);
 
881
                    MusicFN:= SDMusic;
 
882
                    ChangeMusic
799
883
                    end
800
884
                else if (TotalRounds < cSuddenDTurns) and not isInMultiShoot then
801
885
                    begin
848
932
else if ((GameFlags and gfInfAttack) <> 0) then
849
933
    begin
850
934
    if delay2 = 0 then
851
 
        delay2:= cInactDelay * 4
 
935
        delay2:= cInactDelay * 50
852
936
    else
853
937
        begin
854
938
        dec(delay2);
855
939
 
856
 
        if ((delay2 mod cInactDelay) = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) then 
 
940
        if ((delay2 mod cInactDelay) = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and not CurrentHedgehog^.Unplaced then
 
941
            begin
 
942
            if (CurrentHedgehog^.Gear^.State and gstAttacked <> 0) and (Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then
 
943
                begin
 
944
                CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstHHChooseTarget;
 
945
                isCursorVisible := true
 
946
                end;
857
947
            CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State and not gstAttacked;
 
948
            end;
858
949
        if delay2 = 0 then
859
950
            begin
860
 
            SweepDirty;
 
951
            if (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.State and gstAttacked = 0) and (CurAmmoGear = nil) then SweepDirty;
861
952
            CheckNoDamage;
862
953
            AliveCount:= 0; // shorter version of check for win to allow typical step activity to proceed
863
954
            for i:= 0 to Pred(ClansCount) do
865
956
            if (AliveCount <= 1) and ((GameFlags and gfOneClanMode) = 0) then
866
957
                begin
867
958
                step:= stChDmg;
 
959
                if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
868
960
                TurnTimeLeft:= 0
869
961
                end
870
962
            end
881
973
                    and (not PlacingHogs)
882
974
                    and (CurrentHedgehog^.Gear <> nil)
883
975
                    and ((CurrentHedgehog^.Gear^.State and gstAttacked) = 0) then
884
 
                        PlaySound(sndHurry, CurrentTeam^.voicepack);
 
976
                        AddVoice(sndHurry, CurrentTeam^.voicepack);
885
977
                if ReadyTimeLeft > 0 then
886
978
                    begin
887
979
                    if ReadyTimeLeft = 2000 then
888
 
                        PlaySound(sndComeonthen, CurrentTeam^.voicepack);
 
980
                        AddVoice(sndComeonthen, CurrentTeam^.voicepack);
889
981
                    dec(ReadyTimeLeft)
890
982
                    end
891
983
                else
894
986
 
895
987
if skipFlag then
896
988
    begin
 
989
    if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft;
897
990
    TurnTimeLeft:= 0;
898
991
    skipFlag:= false;
899
992
    inc(CurrentHedgehog^.Team^.stats.TurnSkips);
902
995
if ((GameTicks and $FFFF) = $FFFF) then
903
996
    begin
904
997
    if (not CurrentTeam^.ExtDriven) then
905
 
        SendIPCTimeInc;
 
998
        begin
 
999
        SendIPC('#');
 
1000
        AddFileLog('hiTicks increment message sent')
 
1001
        end;
906
1002
 
907
1003
    if (not CurrentTeam^.ExtDriven) or CurrentTeam^.hasGone then
908
1004
        inc(hiTicks) // we do not recieve a message for this
976
1072
            RecountTeamHealth(TeamsArray[i])
977
1073
end;
978
1074
 
979
 
procedure ApplyDamage(Gear: PGear; Damage: Longword; Source: TDamageSource);
 
1075
procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
980
1076
var s: shortstring;
981
1077
    vampDmg, tmpDmg, i: Longword;
982
1078
    vg: PVisualGear;
983
1079
begin
984
 
    if (Gear^.Kind = gtHedgehog) and (Damage>=1) then
 
1080
  if Damage = 0 then exit; // nothing to apply
 
1081
 
 
1082
    if (Gear^.Kind = gtHedgehog) then
985
1083
    begin
 
1084
    Gear^.LastDamage := AttackerHog;
 
1085
 
 
1086
    Gear^.Hedgehog^.Team^.Clan^.Flawless:= false;
986
1087
    HHHurt(Gear^.Hedgehog, Source);
987
1088
    AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), Damage, Gear^.Hedgehog^.Team^.Clan^.Color);
988
1089
    tmpDmg:= min(Damage, max(0,Gear^.Health-Gear^.Damage));
1004
1105
                i:= 0;
1005
1106
                while i < vampDmg do
1006
1107
                    begin
1007
 
                    vg:= AddVisualGear(hwRound(CurrentHedgehog^.Gear^.X), hwRound(CurrentHedgehog^.Gear^.Y), vgtHealth);
1008
 
                    if vg <> nil then vg^.Frame:= 10;
 
1108
                    vg:= AddVisualGear(hwRound(CurrentHedgehog^.Gear^.X), hwRound(CurrentHedgehog^.Gear^.Y), vgtStraightShot);
 
1109
                    if vg <> nil then
 
1110
                        with vg^ do
 
1111
                            begin
 
1112
                            Tint:= $FF0000FF;
 
1113
                            State:= ord(sprHealth)
 
1114
                            end;
1009
1115
                    inc(i, 5);
1010
1116
                    end;
1011
1117
                end
1015
1121
           not CurrentHedgehog^.Gear^.Invulnerable then
1016
1122
           begin // this cannot just use Damage or it interrupts shotgun and gets you called stupid
1017
1123
           inc(CurrentHedgehog^.Gear^.Karma, tmpDmg);
 
1124
           CurrentHedgehog^.Gear^.LastDamage := CurrentHedgehog;
1018
1125
           spawnHealthTagForHH(CurrentHedgehog^.Gear, tmpDmg);
1019
1126
           end;
1020
 
        end;
1021
 
    end;
 
1127
        uStats.HedgehogDamaged(Gear, AttackerHog, Damage, false);    
 
1128
        end;
 
1129
    end else if Gear^.Kind <> gtStructure then // not gtHedgehog nor gtStructure
 
1130
        begin
 
1131
        Gear^.Hedgehog:= AttackerHog;
 
1132
        end;
1022
1133
    inc(Gear^.Damage, Damage);
 
1134
    
1023
1135
    ScriptCall('onGearDamage', Gear^.UID, Damage);
1024
1136
end;
1025
1137
 
1042
1154
t:= GearsList;
1043
1155
while t <> nil do
1044
1156
    begin
1045
 
    if t^.Kind = gtHedgehog then t^.Active:= true;
 
1157
    if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then t^.Active:= true;
1046
1158
    t:= t^.NextGear
1047
1159
    end
1048
1160
end;
1055
1167
Gear:= GearsList;
1056
1168
while Gear <> nil do
1057
1169
    begin
1058
 
    x:= hwRound(Gear^.X) + WorldDx;
1059
 
    y:= hwRound(Gear^.Y) + WorldDy;
1060
 
    RenderGear(Gear, x, y);
 
1170
    if Gear^.State and gstInvisible = 0 then
 
1171
        begin
 
1172
        x:= hwRound(Gear^.X) + WorldDx;
 
1173
        y:= hwRound(Gear^.Y) + WorldDy;
 
1174
        RenderGear(Gear, x, y);
 
1175
        end;
1061
1176
    Gear:= Gear^.NextGear
1062
1177
    end;
1063
1178
end;
1076
1191
end;
1077
1192
 
1078
1193
procedure AddMiscGears;
1079
 
var i: LongInt;
 
1194
var i: Longword;
1080
1195
    Gear: PGear;
1081
1196
begin
1082
1197
AddGear(0, 0, gtATStartGame, 0, _0, _0, 2000);
1083
1198
 
1084
 
if (TrainingFlags and tfSpawnTargets) <> 0 then
1085
 
    begin
1086
 
    TrainingTargetGear:= AddGear(0, 0, gtTarget, 0, _0, _0, 0);
1087
 
    FindPlace(TrainingTargetGear, false, 0, LAND_WIDTH);
1088
 
    end;
1089
 
 
1090
 
for i:= 0 to Pred(cLandMines) do
 
1199
i:= 0;
 
1200
Gear:= PGear(1);
 
1201
while (i < cLandMines) {and (Gear <> nil)} do // disable this check until better solution found
1091
1202
    begin
1092
1203
    Gear:= AddGear(0, 0, gtMine, 0, _0, _0, 0);
1093
1204
    FindPlace(Gear, false, 0, LAND_WIDTH);
 
1205
    inc(i)
1094
1206
    end;
1095
 
for i:= 0 to Pred(cExplosives) do
 
1207
 
 
1208
i:= 0;
 
1209
Gear:= PGear(1);
 
1210
while (i < cExplosives){ and (Gear <> nil)} do
1096
1211
    begin
1097
1212
    Gear:= AddGear(0, 0, gtExplosives, 0, _0, _0, 0);
1098
1213
    FindPlace(Gear, false, 0, LAND_WIDTH);
 
1214
    inc(i)
1099
1215
    end;
1100
1216
 
1101
1217
if (GameFlags and gfLowGravity) <> 0 then
1124
1240
if not hasBorder and ((Theme = 'Snow') or (Theme = 'Christmas')) then
1125
1241
    begin
1126
1242
    for i:= 0 to Pred(vobCount*2) do
1127
 
        AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(1024), gtFlake, 0, _0, _0, 0);
1128
 
    disableLandBack:= true
 
1243
        AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(LAND_HEIGHT div 2), gtFlake, 0, _0, _0, 0);
 
1244
    //disableLandBack:= true
1129
1245
    end
1130
1246
end;
1131
1247
 
1132
 
procedure doMakeExplosion(X, Y, Radius: LongInt; Mask: LongWord);
1133
 
begin
1134
 
doMakeExplosion(X, Y, Radius, Mask, $FFFFFFFF);
1135
 
end;
1136
 
 
1137
 
procedure doMakeExplosion(X, Y, Radius: LongInt; Mask, Tint: LongWord);
 
1248
procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
1138
1249
var Gear: PGear;
1139
1250
    dmg, dmgRadius, dmgBase: LongInt;
1140
1251
    fX, fY: hwFloat;
1141
1252
    vg: PVisualGear;
1142
1253
    i, cnt: LongInt;
1143
1254
begin
1144
 
TargetPoint.X:= NoPointX;
1145
 
{$IFDEF DEBUGFILE}if Radius > 4 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')');{$ENDIF}
 
1255
if Radius > 4 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')');
1146
1256
if Radius > 25 then KickFlakes(Radius, X, Y);
1147
1257
 
1148
1258
if ((Mask and EXPLNoGfx) = 0) then
1177
1287
                gtCase,
1178
1288
                gtTarget,
1179
1289
                gtFlame,
1180
 
                gtExplosives: begin
 
1290
                gtExplosives,
 
1291
                gtStructure: begin
1181
1292
// Run the calcs only once we know we have a type that will need damage
1182
1293
                        if hwRound(hwAbs(Gear^.X-fX)+hwAbs(Gear^.Y-fY)) < dmgBase then
1183
 
                            dmg:= dmgBase - hwRound(Distance(Gear^.X - fX, Gear^.Y - fY));
 
1294
                            dmg:= dmgBase - max(hwRound(Distance(Gear^.X - fX, Gear^.Y - fY)),Gear^.Radius);
1184
1295
                        if dmg > 1 then
1185
1296
                            begin
1186
1297
                            dmg:= ModifyDamage(min(dmg div 2, Radius), Gear);
1187
 
                            //{$IFDEF DEBUGFILE}AddFileLog('Damage: ' + inttostr(dmg));{$ENDIF}
 
1298
                            //AddFileLog('Damage: ' + inttostr(dmg));
1188
1299
                            if (Mask and EXPLNoDamage) = 0 then
1189
1300
                                begin
1190
1301
                                if not Gear^.Invulnerable then
1191
 
                                    ApplyDamage(Gear, dmg, dsExplosion)
 
1302
                                    ApplyDamage(Gear, AttackingHog, dmg, dsExplosion)
1192
1303
                                else
1193
1304
                                    Gear^.State:= Gear^.State or gstWinner;
1194
1305
                                end;
1203
1314
                                Gear^.Active:= true;
1204
1315
                                if Gear^.Kind <> gtFlame then FollowGear:= Gear
1205
1316
                                end;
1206
 
                            if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) then
 
1317
                            if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and not Gear^.Invulnerable then
1207
1318
                                Gear^.Hedgehog^.Effects[hePoisoned] := true;
1208
1319
                            end;
1209
1320
 
1228
1339
    if (GameFlags and gfSolidLand) = 0 then
1229
1340
        begin
1230
1341
        cnt:= DrawExplosion(X, Y, Radius) div 1608; // approx 2 16x16 circles to erase per chunk
1231
 
        if cnt > 0 then
 
1342
        if (cnt > 0) and (SpritesData[sprChunk].Texture <> nil) then
1232
1343
            for i:= 0 to cnt do
1233
1344
                AddVisualGear(X, Y, vgtChunk)
1234
1345
        end;
1238
1349
 
1239
1350
procedure ShotgunShot(Gear: PGear);
1240
1351
var t: PGear;
1241
 
    dmg: LongInt;
 
1352
    dmg, r, dist: LongInt;
 
1353
    dx, dy: hwFloat;
1242
1354
begin
1243
1355
Gear^.Radius:= cShotgunRadius;
1244
1356
t:= GearsList;
1245
1357
while t <> nil do
1246
1358
    begin
1247
 
    dmg:= ModifyDamage(min(Gear^.Radius + t^.Radius - hwRound(Distance(Gear^.X - t^.X, Gear^.Y - t^.Y)), 25), t);
1248
 
    if dmg > 0 then
1249
1359
    case t^.Kind of
1250
1360
        gtHedgehog,
1251
1361
            gtMine,
1252
1362
            gtSMine,
1253
1363
            gtCase,
1254
1364
            gtTarget,
1255
 
            gtExplosives: begin
1256
 
                    if (not t^.Invulnerable) then
1257
 
                        ApplyDamage(t, dmg, dsBullet)
1258
 
                    else
1259
 
                        Gear^.State:= Gear^.State or gstWinner;
 
1365
            gtExplosives,
 
1366
            gtStructure: begin
 
1367
//addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg));
 
1368
                    dmg:= 0;
 
1369
                    r:= Gear^.Radius + t^.Radius;
 
1370
                    dx:= Gear^.X-t^.X;
 
1371
                    dx.isNegative:= false;
 
1372
                    dy:= Gear^.Y-t^.Y;
 
1373
                    dy.isNegative:= false;
 
1374
                    if r-hwRound(dx+dy) > 0 then
 
1375
                        begin
 
1376
                        dist:= hwRound(Distance(dx, dy));
 
1377
                        dmg:= ModifyDamage(min(r - dist, 25), t);
 
1378
                        end;
 
1379
                    if dmg > 0 then
 
1380
                        begin
 
1381
                        if (not t^.Invulnerable) then
 
1382
                            ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet)
 
1383
                        else
 
1384
                            Gear^.State:= Gear^.State or gstWinner;
1260
1385
 
1261
 
                    DeleteCI(t);
1262
 
                    t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
1263
 
                    t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
1264
 
                    t^.State:= t^.State or gstMoving;
1265
 
                    t^.Active:= true;
1266
 
                    FollowGear:= t
 
1386
                        DeleteCI(t);
 
1387
                        t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
 
1388
                        t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
 
1389
                        t^.State:= t^.State or gstMoving;
 
1390
                        t^.Active:= true;
 
1391
                        FollowGear:= t
 
1392
                        end
1267
1393
                    end;
1268
1394
            gtGrave: begin
1269
 
                    t^.dY:= - _0_1;
1270
 
                    t^.Active:= true
 
1395
                    dmg:= 0;
 
1396
                    r:= Gear^.Radius + t^.Radius;
 
1397
                    dx:= Gear^.X-t^.X;
 
1398
                    dx.isNegative:= false;
 
1399
                    dy:= Gear^.Y-t^.Y;
 
1400
                    dy.isNegative:= false;
 
1401
                    if r-hwRound(dx+dy) > 0 then
 
1402
                        begin
 
1403
                        dist:= hwRound(Distance(dx, dy));
 
1404
                        dmg:= ModifyDamage(min(r - dist, 25), t);
 
1405
                        end;
 
1406
                    if dmg > 0 then
 
1407
                        begin
 
1408
                        t^.dY:= - _0_1;
 
1409
                        t^.Active:= true
 
1410
                        end
1271
1411
                    end;
1272
1412
        end;
1273
1413
    t:= t^.NextGear
1283
1423
begin
1284
1424
t:= CheckGearsCollision(Ammo);
1285
1425
// Just to avoid hogs on rope dodging fire.
1286
 
if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtRope) and
 
1426
if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) and
1287
1427
   (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) and
1288
1428
   (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then
1289
1429
    begin
1301
1441
    tmpDmg:= ModifyDamage(Damage, Gear);
1302
1442
    if (Gear^.State and gstNoDamage) = 0 then
1303
1443
        begin
1304
 
        
 
1444
 
1305
1445
        if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then 
1306
1446
            begin
1307
1447
            VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit);
1308
1448
            if VGear <> nil then VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY);
1309
1449
            end;
1310
 
        
 
1450
 
1311
1451
        if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then Gear^.FlightTime:= 1;
1312
1452
 
1313
1453
        case Gear^.Kind of
1316
1456
            gtSMine,
1317
1457
            gtTarget,
1318
1458
            gtCase,
1319
 
            gtExplosives: begin
 
1459
            gtExplosives,
 
1460
            gtStructure: begin
1320
1461
                    if (Ammo^.Kind = gtDrill) then begin Ammo^.Timer:= 0; exit; end;
1321
1462
                    if (not Gear^.Invulnerable) then
1322
 
                        ApplyDamage(Gear, tmpDmg, dsShove)
 
1463
                        ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove)
1323
1464
                    else
1324
1465
                        Gear^.State:= Gear^.State or gstWinner;
1325
 
                    if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then ApplyDamage(Gear, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch
 
1466
                    if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then 
 
1467
                        begin
 
1468
                        if (Ammo^.Hedgehog^.Gear <> nil) then Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and not gstNotKickable;
 
1469
                        ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch
 
1470
                        end;
1326
1471
 
1327
1472
                    DeleteCI(Gear);
1328
1473
                    if (Gear^.Kind = gtHedgehog) and Gear^.Hedgehog^.King then
1423
1568
function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray;
1424
1569
var
1425
1570
    t: PGear;
 
1571
    l: Longword;
1426
1572
begin
 
1573
    r:= r*r;
1427
1574
    GearsNear := nil;
1428
1575
    t := GearsList;
1429
 
    while t <> nil do begin
1430
 
        if (t^.Kind = Kind) then begin
1431
 
            if (X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) <
1432
 
                int2hwFloat(r)*int2hwFloat(r) then
 
1576
    while t <> nil do 
 
1577
        begin
 
1578
        if (t^.Kind = Kind) 
 
1579
            and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then
1433
1580
            begin
1434
 
                SetLength(GearsNear, Length(GearsNear)+1);
1435
 
                GearsNear[High(GearsNear)] := t;
 
1581
            l:= Length(GearsNear);
 
1582
            SetLength(GearsNear, l + 1);
 
1583
            GearsNear[l] := t;
1436
1584
            end;
1437
 
        end;
1438
1585
        t := t^.NextGear;
1439
1586
    end;
1440
1587
end;
1510
1657
procedure ResurrectHedgehog(gear: PGear);
1511
1658
var tempTeam : PTeam;
1512
1659
begin
 
1660
    AttackBar:= 0;
1513
1661
    gear^.dX := _0;
1514
1662
    gear^.dY := _0;
1515
 
    gear^.State := gstWait;
1516
 
    uStats.HedgehogDamaged(gear);
1517
1663
    gear^.Damage := 0;
1518
1664
    gear^.Health := gear^.Hedgehog^.InitialHealth;
1519
1665
    gear^.Hedgehog^.Effects[hePoisoned] := false;
1530
1676
    if gear <> nil then begin
1531
1677
        RenderHealth(gear^.Hedgehog^);
1532
1678
        ScriptCall('onGearResurrect', gear^.uid);
 
1679
        gear^.State := gstWait;
1533
1680
    end;
1534
1681
    RecountTeamHealth(tempTeam);
1535
1682
end;
1539
1686
    FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0);
1540
1687
    cCaseFactor := 0;
1541
1688
 
1542
 
    if (content > ord(High(TAmmoType))) then content := ord(High(TAmmoType));
 
1689
    if (crate <> HealthCrate) and (content > ord(High(TAmmoType))) then content := ord(High(TAmmoType));
1543
1690
 
1544
1691
    case crate of
1545
1692
        HealthCrate: begin
1546
 
            FollowGear^.Health := cHealthCaseAmount;
1547
1693
            FollowGear^.Pos := posCaseHealth;
 
1694
            FollowGear^.Health := content;
1548
1695
            AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
1549
1696
            end;
1550
1697
        AmmoCrate: begin
1564
1711
    SpawnCustomCrateAt := FollowGear;
1565
1712
end;
1566
1713
 
 
1714
function SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean): PGear;
 
1715
begin
 
1716
    FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0);
 
1717
    cCaseFactor := 0;
 
1718
    FollowGear^.Pos := posCaseDummy;
 
1719
    
 
1720
    if explode then FollowGear^.Pos := FollowGear^.Pos + posCaseExplode;
 
1721
    if poison then FollowGear^.Pos := FollowGear^.Pos + posCasePoison;
 
1722
 
 
1723
    case crate of
 
1724
        HealthCrate: begin
 
1725
            FollowGear^.Pos := FollowGear^.Pos + posCaseHealth;
 
1726
            AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
 
1727
            end;
 
1728
        AmmoCrate: begin
 
1729
            FollowGear^.Pos := FollowGear^.Pos + posCaseAmmo;
 
1730
            AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
 
1731
            end;
 
1732
        UtilityCrate: begin
 
1733
            FollowGear^.Pos := FollowGear^.Pos + posCaseUtility;
 
1734
            AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
 
1735
            end;
 
1736
    end;
 
1737
 
 
1738
    if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH);
 
1739
 
 
1740
    SpawnFakeCrateAt := FollowGear;
 
1741
end;
 
1742
 
 
1743
function GetAmmo: TAmmoType;
 
1744
var t, aTot: LongInt;
 
1745
    i: TAmmoType;
 
1746
begin
 
1747
 
 
1748
aTot:= 0;
 
1749
for i:= Low(TAmmoType) to High(TAmmoType) do
 
1750
    if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
 
1751
        inc(aTot, Ammoz[i].Probability);
 
1752
 
 
1753
t:= aTot;
 
1754
i:= Low(TAmmoType);
 
1755
if (t > 0) then
 
1756
    begin
 
1757
    t:= GetRandom(t);
 
1758
    while t >= 0 do
 
1759
      begin
 
1760
      inc(i);
 
1761
      if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
 
1762
          dec(t, Ammoz[i].Probability)
 
1763
      end
 
1764
    end;
 
1765
GetAmmo:= i
 
1766
end;
 
1767
 
 
1768
function GetUtility: TAmmoType;
 
1769
var t, uTot: LongInt;
 
1770
    i: TAmmoType;
 
1771
begin
 
1772
 
 
1773
uTot:= 0;
 
1774
for i:= Low(TAmmoType) to High(TAmmoType) do
 
1775
    if (Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0 then
 
1776
        inc(uTot, Ammoz[i].Probability);
 
1777
 
 
1778
t:= uTot;
 
1779
i:= Low(TAmmoType);
 
1780
if (t > 0) then
 
1781
    begin
 
1782
    t:= GetRandom(t);
 
1783
    while t >= 0 do
 
1784
      begin
 
1785
      inc(i);
 
1786
      if (Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0 then
 
1787
          dec(t, Ammoz[i].Probability)
 
1788
      end
 
1789
    end;
 
1790
GetUtility:= i
 
1791
end;
 
1792
 
 
1793
 
 
1794
 
1567
1795
procedure SpawnBoxOfSmth;
1568
1796
var t, aTot, uTot, a, h: LongInt;
1569
1797
    i: TAmmoType;
1615
1843
        FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
1616
1844
        t:= GetRandom(t);
1617
1845
        i:= Low(TAmmoType);
1618
 
        while t >= 0 do
1619
 
          begin
1620
 
          inc(i);
1621
 
          if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
1622
 
              dec(t, Ammoz[i].Probability)
1623
 
          end;
1624
1846
        FollowGear^.Pos:= posCaseAmmo;
1625
1847
        FollowGear^.AmmoType:= i;
1626
1848
        AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
1634
1856
        FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
1635
1857
        t:= GetRandom(t);
1636
1858
        i:= Low(TAmmoType);
1637
 
        while t >= 0 do
1638
 
          begin
1639
 
          inc(i);
1640
 
          if (Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0 then
1641
 
              dec(t, Ammoz[i].Probability)
1642
 
          end;
1643
1859
        FollowGear^.Pos:= posCaseUtility;
1644
1860
        FollowGear^.AmmoType:= i;
1645
1861
        AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
1652
1868
    FindPlace(FollowGear, true, 0, LAND_WIDTH);
1653
1869
 
1654
1870
    if (FollowGear <> nil) then
1655
 
        PlaySound(sndReinforce, CurrentTeam^.voicepack)
 
1871
        AddVoice(sndReinforce, CurrentTeam^.voicepack)
1656
1872
    end
1657
1873
end;
1658
1874
 
1744
1960
        begin
1745
1961
        Gear^.X:= int2hwFloat(x);
1746
1962
        Gear^.Y:= int2hwFloat(y);
1747
 
        {$IFDEF DEBUGFILE}
1748
1963
        AddFileLog('Assigned Gear coordinates (' + inttostr(x) + ',' + inttostr(y) + ')');
1749
 
        {$ENDIF}
1750
1964
        end
1751
1965
    else
1752
1966
    begin
1776
1990
var gear: PGear;
1777
1991
begin
1778
1992
GearByUID:= nil;
 
1993
if uid = 0 then exit;
 
1994
if (lastGearByUID <> nil) and (lastGearByUID^.uid = uid) then
 
1995
    begin
 
1996
    GearByUID:= lastGearByUID;
 
1997
    exit
 
1998
    end;
1779
1999
gear:= GearsList;
1780
2000
while gear <> nil do
1781
2001
    begin
1782
2002
    if gear^.uid = uid then
1783
2003
        begin
1784
 
            GearByUID:= gear;
1785
 
            exit
 
2004
        lastGearByUID:= gear;
 
2005
        GearByUID:= gear;
 
2006
        exit
1786
2007
        end;
1787
2008
    gear:= gear^.NextGear
1788
2009
    end
1830
2051
    if (x < 4) and (TeamsArray[t] <> nil) then
1831
2052
        begin
1832
2053
            // if team matches current hedgehog team, default to current hedgehog
1833
 
            if (i = 0) and (CurrentHedgehog^.Team = TeamsArray[t]) then hh:= CurrentHedgehog
 
2054
            if (i = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Team = TeamsArray[t]) then hh:= CurrentHedgehog
1834
2055
            else 
1835
2056
                begin
1836
2057
            // otherwise use the first living hog or the hog amongs the remaining ones indicated by i
1847
2068
                    inc(j)
1848
2069
                    end
1849
2070
                end;
1850
 
        if hh <> nil then Gear:= AddVisualGear(0, 0, vgtSpeechBubble);
1851
 
        if Gear <> nil then
 
2071
        if hh <> nil then 
1852
2072
            begin
1853
 
            Gear^.Hedgehog:= hh;
1854
 
            Gear^.Text:= text;
1855
 
            Gear^.FrameTicks:= x
 
2073
            Gear:= AddVisualGear(0, 0, vgtSpeechBubble);
 
2074
            if Gear <> nil then
 
2075
                begin
 
2076
                Gear^.Hedgehog:= hh;
 
2077
                Gear^.Text:= text;
 
2078
                Gear^.FrameTicks:= x
 
2079
                end
1856
2080
            end
1857
2081
        //else ParseCommand('say ' + text, true)
1858
2082
        end
1871
2095
    CurAmmoGear:= nil;
1872
2096
    GearsList:= nil;
1873
2097
    KilledHHs:= 0;
 
2098
    SuddenDeath:= false;
1874
2099
    SuddenDeathDmg:= false;
1875
2100
    SpeechType:= 1;
1876
 
    TrainingTargetGear:= nil;
1877
2101
    skipFlag:= false;
1878
2102
 
1879
2103
    AllInactive:= false;