1 (*
2  * Hedgewars, a free turn based strategy game
3  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *)
18 
19 {$INCLUDE "options.inc"}
20 unit uGearsList;
21 
22 interface
23 uses uFloat, uTypes, SDLh;
24 
AddGearnull25 function  AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
AddGearnull26 function  AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer, newUid: LongWord): PGear;
27 procedure DeleteGear(Gear: PGear);
28 procedure InsertGearToList(Gear: PGear);
29 procedure RemoveGearFromList(Gear: PGear);
30 
31 var curHandledGear: PGear;
32 
33 implementation
34 
35 uses uRandom, uUtils, uConsts, uVariables, uAmmos, uTeams, uStats,
36     uTextures, uScript, uRenderUtils, uAI, uCollisions,
37     uGearsRender, uGearsUtils, uDebug;
38 
39 const
40     GearKindAmmoTypeMap : array [TGearType] of TAmmoType = (
41 (*          gtFlame *)   amNothing
42 (*       gtHedgehog *) , amNothing
43 (*           gtMine *) , amMine
44 (*           gtCase *) , amNothing
45 (*        gtAirMine *) , amAirMine
46 (*     gtExplosives *) , amNothing
47 (*        gtGrenade *) , amGrenade
48 (*          gtShell *) , amBazooka
49 (*          gtGrave *) , amNothing
50 (*            gtBee *) , amBee
51 (*    gtShotgunShot *) , amShotgun
52 (*     gtPickHammer *) , amPickHammer
53 (*           gtRope *) , amRope
54 (*     gtDEagleShot *) , amDEagle
55 (*       gtDynamite *) , amDynamite
56 (*    gtClusterBomb *) , amClusterBomb
57 (*        gtCluster *) , amClusterBomb
58 (*         gtShover *) , amBaseballBat  // Shover is only used for baseball bat right now
59 (*      gtFirePunch *) , amFirePunch
60 (*    gtATStartGame *) , amNothing
61 (*   gtATFinishGame *) , amNothing
62 (*      gtParachute *) , amParachute
63 (*      gtAirAttack *) , amAirAttack
64 (*        gtAirBomb *) , amAirAttack
65 (*      gtBlowTorch *) , amBlowTorch
66 (*         gtGirder *) , amGirder
67 (*       gtTeleport *) , amTeleport
68 (*       gtSwitcher *) , amSwitch
69 (*         gtTarget *) , amNothing
70 (*         gtMortar *) , amMortar
71 (*           gtWhip *) , amWhip
72 (*       gtKamikaze *) , amKamikaze
73 (*           gtCake *) , amCake
74 (*      gtSeduction *) , amSeduction
75 (*     gtWatermelon *) , amWatermelon
76 (*     gtMelonPiece *) , amWatermelon
77 (*    gtHellishBomb *) , amHellishBomb
78 (*        gtWaterUp *) , amNothing
79 (*          gtDrill *) , amDrill
80 (*        gtBallGun *) , amBallgun
81 (*           gtBall *) , amBallgun
82 (*        gtRCPlane *) , amRCPlane
83 (*gtSniperRifleShot *) , amSniperRifle
84 (*        gtJetpack *) , amJetpack
85 (*        gtMolotov *) , amMolotov
86 (*          gtBirdy *) , amBirdy
87 (*            gtEgg *) , amBirdy
88 (*         gtPortal *) , amPortalGun
89 (*          gtPiano *) , amPiano
90 (*        gtGasBomb *) , amGasBomb
91 (*    gtSineGunShot *) , amSineGun
92 (*   gtFlamethrower *) , amFlamethrower
93 (*          gtSMine *) , amSMine
94 (*    gtPoisonCloud *) , amNothing
95 (*         gtHammer *) , amHammer
96 (*      gtHammerHit *) , amHammer
97 (*    gtResurrector *) , amResurrector
98 (*    gtPoisonCloud *) , amNothing
99 (*       gtSnowball *) , amSnowball
100 (*          gtFlake *) , amNothing
101 (*        gtLandGun *) , amLandGun
102 (*         gtTardis *) , amTardis
103 (*         gtIceGun *) , amIceGun
104 (*        gtAddAmmo *) , amNothing
105 (*  gtGenericFaller *) , amNothing
106 (*          gtKnife *) , amKnife
107 (*        gtCreeper *) , amCreeper
108 (*        gtMinigun *) , amMinigun
109 (*  gtMinigunBullet *) , amMinigun
110     );
111 
112 
113 var GCounter: LongWord = 0; // this does not get re-initialized, but should be harmless
114 
115 const
116     cUsualZ = 500;
117     cOnHHZ = 2000;
118 
119 procedure InsertGearToList(Gear: PGear);
120 var tmp, ptmp: PGear;
121 begin
122     tmp:= GearsList;
123     ptmp:= GearsList;
124     while (tmp <> nil) and (tmp^.Z < Gear^.Z) do
125         begin
126         ptmp:= tmp;
127         tmp:= tmp^.NextGear
128         end;
129 
130     if ptmp <> tmp then
131         begin
132         Gear^.NextGear:= ptmp^.NextGear;
133         Gear^.PrevGear:= ptmp;
134         if ptmp^.NextGear <> nil then
135             ptmp^.NextGear^.PrevGear:= Gear;
136         ptmp^.NextGear:= Gear
137         end
138     else
139         begin
140         Gear^.NextGear:= GearsList;
141         if Gear^.NextGear <> nil then
142             Gear^.NextGear^.PrevGear:= Gear;
143         GearsList:= Gear;
144         end;
145 end;
146 
147 
148 procedure RemoveGearFromList(Gear: PGear);
149 begin
150 if (Gear <> GearsList) and (Gear <> nil) and (Gear^.NextGear = nil) and (Gear^.PrevGear = nil) then
151     begin
152     AddFileLog('Attempted to remove Gear #'+inttostr(Gear^.uid)+' from the list twice.');
153     exit
154     end;
155 
156 checkFails((Gear = nil) or (curHandledGear = nil) or (Gear = curHandledGear), 'You''re doing it wrong', true);
157 
158 if Gear^.NextGear <> nil then
159     Gear^.NextGear^.PrevGear:= Gear^.PrevGear;
160 if Gear^.PrevGear <> nil then
161     Gear^.PrevGear^.NextGear:= Gear^.NextGear
162 else
163     GearsList:= Gear^.NextGear;
164 
165 Gear^.NextGear:= nil;
166 Gear^.PrevGear:= nil
167 end;
168 
169 
AddGearnull170 function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
171 begin
172 AddGear:= AddGear(X, Y, Kind, State, dX, dY, Timer, 0);
173 end;
AddGearnull174 function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer, newUid: LongWord): PGear;
175 var gear: PGear;
176     //c: byte;
177     cakeData: PCakeData;
178 begin
179 if newUid = 0 then
180     inc(GCounter);
181 
182 AddFileLog('AddGear: #' + inttostr(GCounter) + ' (' + inttostr(x) + ',' + inttostr(y) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + EnumToStr(Kind));
183 
184 
185 New(gear);
186 FillChar(gear^, sizeof(TGear), 0);
187 gear^.X:= int2hwFloat(X);
188 gear^.Y:= int2hwFloat(Y);
189 gear^.Target.X:= NoPointX;
190 gear^.Kind := Kind;
191 gear^.State:= State;
192 gear^.Active:= true;
193 gear^.dX:= dX;
194 gear^.dY:= dY;
195 gear^.doStep:= doStepHandlers[Kind];
196 gear^.CollisionIndex:= -1;
197 gear^.Timer:= Timer;
198 if newUid = 0 then
199      gear^.uid:= GCounter
200 else gear^.uid:= newUid;
201 gear^.SoundChannel:= -1;
202 gear^.ImpactSound:= sndNone;
203 gear^.Density:= _1;
204 // Define ammo association, if any.
205 gear^.AmmoType:= GearKindAmmoTypeMap[Kind];
206 gear^.CollisionMask:= lfAll;
207 gear^.Tint:= $FFFFFFFF;
208 gear^.Data:= nil;
209 gear^.Sticky:= false;
210 
211 if CurrentHedgehog <> nil then
212     begin
213     gear^.Hedgehog:= CurrentHedgehog;
214     if (CurrentHedgehog^.Gear <> nil) and (hwRound(CurrentHedgehog^.Gear^.X) = X) and (hwRound(CurrentHedgehog^.Gear^.Y) = Y) then
215         gear^.CollisionMask:= lfNotCurHogCrate
216     end;
217 
218 if (Ammoz[Gear^.AmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then
219     gear^.Z:= cHHZ+1
220 else gear^.Z:= cUsualZ;
221 
222 // set gstInBounceEdge if gear spawned inside the bounce world edge
223 if WorldEdge = weBounce then
224     if (hwRound(gear^.X) - Gear^.Radius < leftX) or (hwRound(gear^.X) + Gear^.Radius > rightX) then
225         case gear^.Kind of
226             // list all gears here that could collide with the bounce world edge
227             gtHedgehog,
228             gtFlame,
229             gtMine,
230             gtAirBomb,
231             gtDrill,
232             gtNapalmBomb,
233             gtCase,
234             gtAirMine,
235             gtExplosives,
236             gtGrenade,
237             gtShell,
238             gtBee,
239             gtDynamite,
240             gtClusterBomb,
241             gtMelonPiece,
242             gtCluster,
243             gtMortar,
244             gtKamikaze,
245             gtCake,
246             gtWatermelon,
247             gtGasBomb,
248             gtHellishBomb,
249             gtBall,
250             gtRCPlane,
251             gtSniperRifleShot,
252             gtShotgunShot,
253             gtDEagleShot,
254             gtSineGunShot,
255             gtMinigunBullet,
256             gtEgg,
257             gtPiano,
258             gtSMine,
259             gtSnowball,
260             gtKnife,
261             gtCreeper,
262             gtMolotov,
263             gtFlake,
264             gtGrave,
265             gtPortal,
266             gtTarget:
267             gear^.State := gear^.State or gstInBounceEdge;
268         end;
269 
270 case Kind of
271           gtFlame: Gear^.Boom := 2;  // some additional expl in there are x3, x4 this
272        gtHedgehog: Gear^.Boom := 30;
273            gtMine: Gear^.Boom := 50;
274            gtCase: Gear^.Boom := 25;
275         gtAirMine: Gear^.Boom := 30;
276      gtExplosives: Gear^.Boom := 75;
277         gtGrenade: Gear^.Boom := 50;
278           gtShell: Gear^.Boom := 50;
279             gtBee: Gear^.Boom := 50;
280     gtShotgunShot: Gear^.Boom := 25;
281      gtPickHammer: Gear^.Boom := 6;
282 //           gtRope: Gear^.Boom := 2; could be funny to have rope attaching to hog deal small amount of dmg?
283      gtDEagleShot: Gear^.Boom := 7;
284        gtDynamite: Gear^.Boom := 75;
285     gtClusterBomb: Gear^.Boom := 20;
286      gtMelonPiece,
287         gtCluster: Gear^.Boom := Timer;
288          gtShover: Gear^.Boom := 30;
289       gtFirePunch: Gear^.Boom := 30;
290         gtAirBomb: Gear^.Boom := 30;
291       gtBlowTorch: Gear^.Boom := 2;
292          gtMortar: Gear^.Boom := 20;
293            gtWhip: Gear^.Boom := 30;
294        gtKamikaze: Gear^.Boom := 30; // both shove and explosion
295            gtCake: Gear^.Boom := cakeDmg; // why is cake damage a global constant
296      gtWatermelon: Gear^.Boom := 75;
297     gtHellishBomb: Gear^.Boom := 90;
298           gtDrill: if Gear^.State and gsttmpFlag = 0 then
299                         Gear^.Boom := 50
300                    else Gear^.Boom := 30;
301            gtBall: Gear^.Boom := 40;
302         gtRCPlane: Gear^.Boom := 25;
303 // sniper rifle is distance linked, this Boom is just an arbitrary scaling factor applied to timer-based-damage
304 // because, eh, why not..
305 gtSniperRifleShot: Gear^.Boom := 100000;
306             gtEgg: Gear^.Boom := 10;
307           gtPiano: Gear^.Boom := 80;
308         gtGasBomb: Gear^.Boom := 20;
309     gtSineGunShot: Gear^.Boom := 35;
310           gtSMine: Gear^.Boom := 30;
311     gtSnowball: Gear^.Boom := 200000; // arbitrary scaling for the shove
312          gtHammer: if cDamageModifier > _1 then // scale it based on cDamageModifier?
313                          Gear^.Boom := 2
314                     else Gear^.Boom := 3;
315     gtPoisonCloud: Gear^.Boom := 20;
316           gtKnife: Gear^.Boom := 40000; // arbitrary scaling factor since impact-based
317         gtCreeper: Gear^.Boom := 100;
318     gtMinigunBullet: Gear^.Boom := 2;
319     end;
320 
321 case Kind of
322      gtGrenade,
323      gtClusterBomb,
324      gtGasBomb: begin
325                 gear^.ImpactSound:= sndGrenadeImpact;
326                 gear^.nImpactSounds:= 1;
327                 gear^.AdvBounce:= 1;
328                 gear^.Radius:= 5;
329                 gear^.Elasticity:= _0_8;
330                 gear^.Friction:= _0_8;
331                 gear^.Density:= _1_5;
332                 gear^.RenderTimer:= true;
333                 if gear^.Timer = 0 then
334                     gear^.Timer:= 3000
335                 end;
336   gtWatermelon: begin
337                 gear^.ImpactSound:= sndMelonImpact;
338                 gear^.nImpactSounds:= 1;
339                 gear^.AdvBounce:= 1;
340                 gear^.Radius:= 6;
341                 gear^.Elasticity:= _0_8;
342                 gear^.Friction:= _0_995;
343                 gear^.Density:= _2;
344                 gear^.RenderTimer:= true;
345                 if gear^.Timer = 0 then
346                     gear^.Timer:= 3000
347                 end;
348   gtMelonPiece: begin
349                 gear^.AdvBounce:= 1;
350                 gear^.Density:= _2;
351                 gear^.Elasticity:= _0_8;
352                 gear^.Friction:= _0_995;
353                 gear^.Radius:= 4
354                 end;
355     gtHedgehog: begin
356                 gear^.AdvBounce:= 1;
357                 gear^.Radius:= cHHRadius;
358                 gear^.Elasticity:= _0_35;
359                 gear^.Friction:= _0_999;
360                 gear^.Angle:= cMaxAngle div 2;
361                 gear^.Density:= _3;
362                 gear^.Z:= cHHZ;
363                 if (GameFlags and gfAISurvival) <> 0 then
364                     if gear^.Hedgehog^.BotLevel > 0 then
365                         gear^.Hedgehog^.Effects[heResurrectable] := 1;
366                 if (GameFlags and gfArtillery) <> 0 then
367                     gear^.Hedgehog^.Effects[heArtillery] := 1;
368                 // this would presumably be set in the frontend
369                 // if we weren't going to do that yet, would need to reinit GetRandom
370                 // oh, and, randomising slightly R and B might be nice too.
371                 //gear^.Tint:= $fa00efff or ((random(80)+128) shl 16)
372                 //gear^.Tint:= $faa4efff
373                 //gear^.Tint:= (($e0+random(32)) shl 24) or
374                 //             ((random(80)+128) shl 16) or
375                 //             (($d5+random(32)) shl 8) or $ff
376                 {c:= GetRandom(32);
377                 gear^.Tint:= (($e0+c) shl 24) or
378                              ((GetRandom(90)+128) shl 16) or
379                              (($d5+c) shl 8) or $ff}
380                 end;
381    gtParachute: begin
382                 gear^.Tag:= 1; // hog face dir. 1 = right, -1 = left
383                 gear^.Z:= cCurrHHZ;
384                 end;
385        gtShell: begin
386                 gear^.Elasticity:= _0_8;
387                 gear^.Friction:= _0_8;
388                 gear^.Radius:= 4;
389                 gear^.Density:= _1;
390                 gear^.AdvBounce:= 1;
391                 end;
392        gtSnowball: begin
393                 gear^.ImpactSound:= sndMudballImpact;
394                 gear^.nImpactSounds:= 1;
395                 gear^.Radius:= 4;
396                 gear^.Density:= _0_5;
397                 gear^.AdvBounce:= 1;
398                 gear^.Elasticity:= _0_8;
399                 gear^.Friction:= _0_8;
400                 end;
401 
402      gtFlake: begin
403                 with Gear^ do
404                     begin
405                     Pos:= 0;
406                     Radius:= 1;
407                     DirAngle:= random(360);
408                     Sticky:= true;
409                     if State and gstTmpFlag = 0 then
410                         begin
411                         dx.isNegative:= GetRandom(2) = 0;
412                         dx.QWordValue:= QWord($40DA) * GetRandom(10000) * 8;
413                         dy.isNegative:= false;
414                         dy.QWordValue:= QWord($3AD3) * GetRandom(7000) * 8;
415                         if GetRandom(2) = 0 then
416                             dx := -dx;
417                         Tint:= $FFFFFFFF
418                         end
419                     else
420                         Tint:= (ExplosionBorderColor shr RShift and $FF shl 24) or
421                                (ExplosionBorderColor shr GShift and $FF shl 16) or
422                                (ExplosionBorderColor shr BShift and $FF shl 8) or $FF;
423                     State:= State or gstInvisible;
424                     // use health field to store current frameticks
425                     if vobFrameTicks > 0 then
426                         Health:= random(vobFrameTicks)
427                     else
428                         Health:= 0;
429                     // use timer to store currently displayed frame index
430                     if gear^.Timer = 0 then Timer:= random(vobFramesCount);
431                     Damage:= (random(2) * 2 - 1) * (vobVelocity + random(vobVelocity)) * 8
432                     end
433                 end;
434        gtGrave: begin
435                 gear^.ImpactSound:= sndGraveImpact;
436                 gear^.nImpactSounds:= 1;
437                 gear^.Radius:= 10;
438                 gear^.Elasticity:= _0_6;
439                 gear^.Z:= 1;
440                 end;
441          gtBee: begin
442                 gear^.Radius:= 5;
443                 if gear^.Timer = 0 then gear^.Timer:= 500;
444                 gear^.RenderTimer:= true;
445                 gear^.Elasticity:= _0_9;
446                 gear^.Tag:= 0;
447                 gear^.State:= Gear^.State or gstSubmersible
448                 end;
449    gtSeduction: begin
450                 gear^.Radius:= cSeductionDist;
451                 end;
452  gtShotgunShot: begin
453                 if gear^.Timer = 0 then gear^.Timer:= 900;
454                 gear^.Radius:= 2
455                 end;
456   gtPickHammer: begin
457                 gear^.Radius:= 10;
458                 if gear^.Timer = 0 then gear^.Timer:= 4000
459                 end;
460    gtHammerHit: begin
461                 gear^.Radius:= 8;
462                 if gear^.Timer = 0 then gear^.Timer:= 125
463                 end;
464         gtRope: begin
465                 gear^.Radius:= 3;
466                 gear^.Friction:= _450 * _0_01 * cRopePercent;
467                 RopePoints.Count:= 0;
468                 gear^.Tint:= $D8D8D8FF;
469                 gear^.Tag:= 0; // normal rope render
470                 gear^.CollisionMask:= lfNotCurHogCrate //lfNotObjMask or lfNotHHObjMask;
471                 end;
472         gtMine: begin
473                 gear^.ImpactSound:= sndMineImpact;
474                 gear^.nImpactSounds:= 1;
475                 gear^.Health:= 10;
476                 gear^.State:= gear^.State or gstMoving;
477                 gear^.Radius:= 2;
478                 gear^.Elasticity:= _0_55;
479                 gear^.Friction:= _0_995;
480                 gear^.Density:= _1;
481                 if gear^.Timer = 0 then
482                     begin
483                     if cMinesTime < 0 then
484                         begin
485                         gear^.Timer:= getrandom(51)*100;
486                         gear^.Karma:= 1;
487                         end
488                     else
489                         gear^.Timer:= cMinesTime;
490                     end;
491                 gear^.RenderTimer:= true;
492                 end;
493      gtAirMine: begin
494                 gear^.AdvBounce:= 1;
495                 gear^.ImpactSound:= sndAirMineImpact;
496                 gear^.nImpactSounds:= 1;
497                 gear^.Health:= 30;
498                 gear^.State:= gear^.State or gstMoving or gstNoGravity or gstSubmersible;
499                 gear^.Radius:= 8;
500                 gear^.Elasticity:= _0_55;
501                 gear^.Friction:= _0_995;
502                 gear^.Density:= _1;
503                 gear^.Angle:= 175; // Radius at which air bombs will start "seeking". $FFFFFFFF = unlimited. check is skipped.
504                 gear^.Power:= cMaxWindSpeed.QWordValue div 2; // hwFloat converted. 1/2 g default. defines the "seek" speed when a gear is in range.
505                 gear^.Pos:= cMaxWindSpeed.QWordValue * 3 div 2; // air friction. slows it down when not hitting stuff
506                 gear^.Tag:= 0;
507                 if gear^.Timer = 0 then
508                     begin
509                     if cMinesTime < 0 then
510                         begin
511                         gear^.Timer:= getrandom(13)*100;
512                         gear^.Karma:= 1;
513                         end
514                     else
515                         gear^.Timer:= cMinesTime div 4;
516                     end;
517                 gear^.RenderTimer:= true;
518                 gear^.WDTimer:= gear^.Timer
519                 end;
520        gtSMine: begin
521                 gear^.Health:= 10;
522                 gear^.State:= gear^.State or gstMoving;
523                 gear^.Radius:= 2;
524                 gear^.Elasticity:= _0_55;
525                 gear^.Friction:= _0_995;
526                 gear^.Density:= _1_6;
527                 gear^.AdvBounce:= 1;
528                 gear^.Sticky:= true;
529                 if gear^.Timer = 0 then gear^.Timer:= 500;
530                 gear^.RenderTimer:= true;
531                 end;
532        gtKnife: begin
533                 gear^.ImpactSound:= sndKnifeImpact;
534                 gear^.AdvBounce:= 1;
535                 gear^.Elasticity:= _0_8;
536                 gear^.Friction:= _0_8;
537                 gear^.Density:= _4;
538                 gear^.Radius:= 7;
539                 gear^.Sticky:= true;
540                 end;
541         gtCase: begin
542                 gear^.ImpactSound:= sndCaseImpact;
543                 gear^.nImpactSounds:= 1;
544                 gear^.Radius:= 16;
545                 gear^.Elasticity:= _0_3;
546                 if gear^.Timer = 0 then gear^.Timer:= 500
547                 end;
548   gtExplosives: begin
549                 gear^.AdvBounce:= 1;
550                 if GameType in [gmtDemo, gmtRecord] then
551                     gear^.RenderHealth:= true;
552                 gear^.ImpactSound:= sndGrenadeImpact;
553                 gear^.nImpactSounds:= 1;
554                 gear^.Radius:= 16;
555                 gear^.Elasticity:= _0_4;
556                 gear^.Friction:= _0_995;
557                 gear^.Density:= _6;
558                 gear^.Health:= cBarrelHealth;
559                 gear^.Z:= cHHZ-1
560                 end;
561   gtDEagleShot: begin
562                 gear^.Radius:= 1;
563                 gear^.Health:= 50
564                 end;
565   gtSniperRifleShot: begin
566                 gear^.Radius:= 1;
567                 gear^.Health:= 50
568                 end;
569     gtDynamite: begin
570                 gear^.Radius:= 3;
571                 gear^.Elasticity:= _0_55;
572                 gear^.Friction:= _0_03;
573                 gear^.Density:= _2;
574                 if gear^.Timer = 0 then gear^.Timer:= 5000;
575                 end;
576      gtCluster: begin
577                 gear^.AdvBounce:= 1;
578                 gear^.Elasticity:= _0_8;
579                 gear^.Friction:= _0_8;
580                 gear^.Radius:= 2;
581                 gear^.Density:= _1_5;
582                 gear^.RenderTimer:= true
583                 end;
584       gtShover: begin
585                 gear^.Radius:= 20;
586                 gear^.Tag:= 0;
587                 gear^.Timer:= 50;
588                 end;
589        gtFlame: begin
590                 gear^.Tag:= GetRandom(32);
591                 gear^.Radius:= 1;
592                 gear^.Health:= 5;
593                 gear^.Density:= _1;
594                 gear^.FlightTime:= 9999999; // determines whether in-air flames do damage. disabled by default
595                 if (gear^.dY.QWordValue = 0) and (gear^.dX.QWordValue = 0) then
596                     begin
597                     gear^.dY:= (getrandomf - _0_8) * _0_03;
598                     gear^.dX:= (getrandomf - _0_5) * _0_4
599                     end
600                 end;
601    gtFirePunch: begin
602                 if gear^.Timer = 0 then gear^.Timer:= 3000;
603                 gear^.Radius:= 15;
604                 gear^.Tag:= Y
605                 end;
606    gtAirAttack: begin
607                 gear^.Health:= 6;
608                 gear^.Damage:= 30;
609                 gear^.Z:= cHHZ+2;
610                 gear^.Karma:= 0; // for sound effect: 0 = normal, 1 = underwater
611                 gear^.Radius:= 150;
612                 gear^.FlightTime:= 0; // for timeout in weWrap
613                 gear^.Power:= 0; // count number of wraps in weWrap
614                 gear^.WDTimer:= 0; // number of required wraps
615                 gear^.Density:= _19;
616                 gear^.Tint:= gear^.Hedgehog^.Team^.Clan^.Color shl 8 or $FF
617                 end;
618      gtAirBomb: begin
619                 gear^.AdvBounce:= 1;
620                 gear^.Radius:= 5;
621                 gear^.Density:= _2;
622                 gear^.Elasticity:= _0_55;
623                 gear^.Friction:= _0_995
624                 end;
625    gtBlowTorch: begin
626                 gear^.Radius:= cHHRadius + cBlowTorchC;
627                 if gear^.Timer = 0 then gear^.Timer:= 7500
628                 end;
629     gtSwitcher: begin
630                 gear^.Z:= cCurrHHZ
631                 end;
632       gtTarget: begin
633                 gear^.ImpactSound:= sndGrenadeImpact;
634                 gear^.nImpactSounds:= 1;
635                 gear^.Radius:= 10;
636                 gear^.Elasticity:= _0_3;
637                 end;
638       gtTardis: begin
639                 gear^.Pos:= 1; // tardis phase
640                 gear^.Tag:= 0; // 1 = hedgehog died, disappeared, took damage or moved
641                 gear^.Z:= cCurrHHZ+1;
642                 end;
643       gtMortar: begin
644                 gear^.AdvBounce:= 1;
645                 gear^.Radius:= 4;
646                 gear^.Elasticity:= _0_2;
647                 gear^.Friction:= _0_08;
648                 gear^.Density:= _1;
649                 end;
650         gtWhip: gear^.Radius:= 20;
651       gtHammer: gear^.Radius:= 20;
652     gtKamikaze: begin
653                 gear^.Health:= 2048;
654                 gear^.Radius:= 20
655                 end;
656         gtCake: begin
657                 gear^.Health:= 2048;
658                 gear^.Radius:= 7;
659                 gear^.Z:= cOnHHZ;
660                 gear^.RenderTimer:= false;
661                 gear^.DirAngle:= -90 * hwSign(Gear^.dX);
662                 gear^.FlightTime:= 100; // (roughly) ticks spent dropping, used to skip getting up anim when stuck.
663                                         // Initially set to a high value so cake has at least one getting up anim.
664                 if not dX.isNegative then
665                     gear^.Angle:= 1
666                 else
667                     gear^.Angle:= 3;
668                 New(cakeData);
669                 gear^.Data:= Pointer(cakeData);
670                 end;
671  gtHellishBomb: begin
672                 gear^.ImpactSound:= sndHellishImpact1;
673                 gear^.nImpactSounds:= 4;
674                 gear^.AdvBounce:= 1;
675                 gear^.Radius:= 4;
676                 gear^.Elasticity:= _0_5;
677                 gear^.Friction:= _0_96;
678                 gear^.Density:= _1_5;
679                 gear^.RenderTimer:= true;
680                 if gear^.Timer = 0 then gear^.Timer:= 5000
681                 end;
682        gtDrill: begin
683                 gear^.AdvBounce:= 1;
684                 gear^.Elasticity:= _0_8;
685                 gear^.Friction:= _0_8;
686                 if gear^.Timer = 0 then
687                     gear^.Timer:= 5000;
688                 // Tag for drill strike. if 1 then first impact occured already
689                 gear^.Tag := 0;
690                 // Pos for state. If 1, drill is drilling
691                 gear^.Pos := 0;
692                 gear^.Radius:= 4;
693                 gear^.Density:= _1;
694                 end;
695         gtBall: begin
696                 gear^.ImpactSound:= sndGrenadeImpact;
697                 gear^.nImpactSounds:= 1;
698                 gear^.AdvBounce:= 1;
699                 gear^.Radius:= 5;
700                 gear^.Tag:= random(8);
701                 if gear^.Timer = 0 then gear^.Timer:= 5000;
702                 gear^.Elasticity:= _0_7;
703                 gear^.Friction:= _0_995;
704                 gear^.Density:= _1_5;
705                 end;
706      gtBallgun: begin
707                 if gear^.Timer = 0 then gear^.Timer:= 5001;
708                 end;
709      gtRCPlane: begin
710                 if gear^.Timer = 0 then gear^.Timer:= 15000;
711                 gear^.Health:= 3;
712                 gear^.Radius:= 8;
713                 gear^.Tint:= gear^.Hedgehog^.Team^.Clan^.Color shl 8 or $FF
714                 end;
715      gtJetpack: begin
716                 gear^.Health:= 2000;
717                 gear^.Damage:= 100;
718                 gear^.State:= Gear^.State or gstSubmersible
719                 end;
720      gtMolotov: begin
721                 gear^.AdvBounce:= 1;
722                 gear^.Radius:= 6;
723                 gear^.Elasticity:= _0_8;
724                 gear^.Friction:= _0_8;
725                 gear^.Density:= _2
726                 end;
727        gtBirdy: begin
728                 gear^.Radius:= 16; // todo: check
729                 gear^.Health := 2000;
730                 gear^.FlightTime := 2;
731                 gear^.Z:= cCurrHHZ;
732                 end;
733          gtEgg: begin
734                 gear^.AdvBounce:= 1;
735                 gear^.Radius:= 4;
736                 gear^.Elasticity:= _0_6;
737                 gear^.Friction:= _0_96;
738                 gear^.Density:= _1;
739                 if gear^.Timer = 0 then
740                     gear^.Timer:= 3000
741                 end;
742       gtPortal: begin
743                 gear^.ImpactSound:= sndMelonImpact;
744                 gear^.nImpactSounds:= 1;
745                 gear^.Radius:= 17;
746                 // set color
747                 gear^.Tag:= 2 * gear^.Timer;
748                 gear^.Timer:= 15000;
749                 gear^.RenderTimer:= false;
750                 gear^.Health:= 100;
751                 gear^.Sticky:= true;
752                 end;
753        gtPiano: begin
754                 gear^.Radius:= 32;
755                 gear^.Density:= _50;
756                 end;
757  gtSineGunShot: begin
758                 gear^.Radius:= 5;
759                 gear^.Health:= 6000;
760                 end;
761 gtFlamethrower: begin
762                 gear^.Tag:= 10;
763                 if gear^.Timer = 0 then gear^.Timer:= 10;
764                 gear^.Health:= 500;
765                 gear^.Damage:= 100;
766                 end;
767      gtLandGun: begin
768                 gear^.Tag:= 10;
769                 if gear^.Timer = 0 then gear^.Timer:= 10;
770                 gear^.Health:= 1000;
771                 gear^.Damage:= 100;
772                 end;
773  gtPoisonCloud: begin
774                 if gear^.Timer = 0 then gear^.Timer:= 5000;
775                 gear^.dY:= int2hwfloat(-4 + longint(getRandom(8))) / 1000;
776                 gear^.Tint:= $C0C000C0
777                 end;
778  gtResurrector: begin
779                 gear^.Radius := cResurrectorDist;
780                 gear^.Tag := 0;
781                 gear^.Tint:= $F5DB35FF
782                 end;
783      gtWaterUp: begin
784                 gear^.Tag := 47;
785                 end;
786   gtNapalmBomb: begin
787                 gear^.Elasticity:= _0_8;
788                 gear^.Friction:= _0_8;
789                 if gear^.Timer = 0 then gear^.Timer:= 1000;
790                 gear^.Radius:= 5;
791                 gear^.Density:= _1_5;
792                 end;
793       gtIceGun: begin
794                 gear^.Health:= 1000;
795                 gear^.Radius:= 8;
796                 gear^.Density:= _0;
797                 gear^.Tag:= 0; // sound state: 0 = no sound, 1 = ice beam sound, 2 = idle sound
798                 end;
799      gtCreeper: begin
800                 // TODO: Finish creeper initialization implementation
801                 gear^.Radius:= cHHRadius;
802                 gear^.Elasticity:= _0_35;
803                 gear^.Friction:= _0_93;
804                 gear^.Density:= _5;
805 
806                 gear^.AdvBounce:= 1;
807                 gear^.ImpactSound:= sndAirMineImpact;
808                 gear^.nImpactSounds:= 1;
809                 gear^.Health:= 30;
810                 gear^.Radius:= 8;
811                 gear^.Angle:= 175; // Radius at which it will start "seeking". $FFFFFFFF = unlimited. check is skipped.
812                 gear^.Power:= cMaxWindSpeed.QWordValue div 2; // hwFloat converted. 1/2 g default. defines the "seek" speed when a gear is in range.
813                 gear^.Pos:= cMaxWindSpeed.QWordValue * 3 div 2; // air friction. slows it down when not hitting stuff
814                 if gear^.Timer = 0 then
815                     gear^.Timer:= 5000;
816                 gear^.WDTimer:= gear^.Timer
817                 end;
818      gtMinigun: begin
819                 // Timer. First, it's the timer before shooting. Then it will become the shooting timer and is set to Karma
820                 if gear^.Timer = 0 then
821                     gear^.Timer:= 601;
822                 // minigun shooting time. 1 bullet is fired every 50ms
823                 gear^.Karma:= 3451;
824                 end;
825  gtMinigunBullet: begin
826                 gear^.Radius:= 1;
827                 gear^.Health:= 2;
828                 end;
829 gtGenericFaller:begin
830                 gear^.AdvBounce:= 1;
831                 gear^.Radius:= 1;
832                 gear^.Elasticity:= _0_9;
833                 gear^.Friction:= _0_995;
834                 gear^.Density:= _1;
835                 end;
836     end;
837 
838 InsertGearToList(gear);
839 AddGear:= gear;
840 
841 ScriptCall('onGearAdd', gear^.uid);
842 end;
843 
844 procedure DeleteGear(Gear: PGear);
845 var team: PTeam;
846     t,i: Longword;
847     cakeData: PCakeData;
848     iterator: PGear;
849 begin
850 
851 ScriptCall('onGearDelete', gear^.uid);
852 
853 DeleteCI(Gear);
854 RemoveFromProximityCache(Gear);
855 
856 FreeAndNilTexture(Gear^.Tex);
857 
858 // remove potential links to this gear
859 // currently relevant to: gears linked by hammer
860 if (Gear^.Kind = gtHedgehog) or (Gear^.Kind = gtMine) or (Gear^.Kind = gtExplosives) then
861     begin
862     // check all gears for stuff to port through
863     iterator := nil;
864     while true do
865         begin
866 
867         // iterate through GearsList
868         if iterator = nil then
869             iterator := GearsList
870         else
871             iterator := iterator^.NextGear;
872 
873         // end of list?
874         if iterator = nil then
875             break;
876 
877         if iterator^.LinkedGear = Gear then
878             iterator^.LinkedGear:= nil;
879         end;
880 
881     end;
882 
883 // make sure that portals have their link removed before deletion
884 if (Gear^.Kind = gtPortal) then
885     begin
886     if (Gear^.LinkedGear <> nil) then
887         if (Gear^.LinkedGear^.LinkedGear = Gear) then
888             Gear^.LinkedGear^.LinkedGear:= nil;
889     end
890 else if Gear^.Kind = gtCake then
891     begin
892         cakeData:= PCakeData(Gear^.Data);
893         Dispose(cakeData);
894         cakeData:= nil;
895     end
896 else if Gear^.Kind = gtHedgehog then
897     (*
898     This behaviour dates back to revision 4, and I accidentally encountered it with TARDIS.  I don't think it must apply to any modern weapon, since if it was actually hit, the best the gear could do would be to destroy itself immediately, and you'd still end up with two graves.  I believe it should be removed
899      if (CurAmmoGear <> nil) and (CurrentHedgehog^.Gear = Gear) then
900         begin
901         AttackBar:= 0;
902         Gear^.Message:= gmDestroy;
903         CurAmmoGear^.Message:= gmDestroy;
904         exit
905         end
906     else*)
907         begin
908         if ((CurrentHedgehog = nil) or (Gear <> CurrentHedgehog^.Gear)) or (CurAmmoGear = nil) or (CurAmmoGear^.Kind <> gtKamikaze) then
909             Gear^.Hedgehog^.Team^.Clan^.Flawless:= false;
910         if CheckCoordInWater(hwRound(Gear^.X), hwRound(Gear^.Y)) then
911             begin
912             t:= max(Gear^.Damage, Gear^.Health);
913             Gear^.Damage:= t;
914             // Display hedgehog damage in water
915             spawnHealthTagForHH(Gear, t);
916             end;
917 
918         team:= Gear^.Hedgehog^.Team;
919         if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear = Gear) then
920             begin
921             AttackBar:= 0;
922             FreeActionsList; // to avoid ThinkThread on drawned gear
923             if ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0)
924             and (CurrentHedgehog^.MultiShootAttacks > 0) then
925                 OnUsedAmmo(CurrentHedgehog^);
926             end;
927 
928         Gear^.Hedgehog^.Gear:= nil;
929 
930         if Gear^.Hedgehog^.King then
931             // If king died, kill the rest of the team
932             begin
933             with Gear^.Hedgehog^.Team^ do
934                 begin
935                 Gear^.Hedgehog^.Team^.hasKing:= false;
936                 for t:= 0 to cMaxHHIndex do
937                     if Hedgehogs[t].Gear <> nil then
938                         Hedgehogs[t].Gear^.Health:= 0
939                     else if (Hedgehogs[t].GearHidden <> nil) then
940                         Hedgehogs[t].GearHidden^.Health:= 0  // Hog is still hidden. If tardis should return though, Lua, eh ...
941                 end;
942             end;
943 
944         // Update passive status of clan
945         if (not Gear^.Hedgehog^.Team^.Clan^.Passive) then
946             begin
947             Gear^.Hedgehog^.Team^.Clan^.Passive:= true;
948             for i:= 0 to Pred(team^.Clan^.TeamsNumber) do
949                 begin
950                 with team^.Clan^.Teams[i]^ do
951                     if (not Passive) then
952                         for t:= 0 to cMaxHHIndex do
953                             if (Hedgehogs[t].Gear <> nil) or (Hedgehogs[t].GearHidden <> nil) then
954                                 begin
955                                 Gear^.Hedgehog^.Team^.Clan^.Passive:= false;
956                                 break;
957                                 end;
958                 if (not Gear^.Hedgehog^.Team^.Clan^.Passive) then
959                     break;
960                 end;
961             end;
962 
963         // should be not CurrentHedgehog, but hedgehog of the last gear which caused damage to this hog
964         // same stand for CheckHHDamage
965         if (Gear^.LastDamage <> nil) and (CurrentHedgehog <> nil) then
966             uStats.HedgehogDamaged(Gear, Gear^.LastDamage, 0, true)
967         else if CurrentHedgehog <> nil then
968             uStats.HedgehogDamaged(Gear, CurrentHedgehog, 0, true);
969 
970         inc(KilledHHs);
971         RecountTeamHealth(team);
972         if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Effects[heResurrectable] <> 0)  and
973         //(Gear^.Hedgehog^.Effects[heResurrectable] = 0) then
974         (Gear^.Hedgehog^.Team^.Clan <> CurrentHedgehog^.Team^.Clan) then
975             with CurrentHedgehog^ do
976                 begin
977                 inc(Team^.stats.AIKills);
978                 FreeAndNilTexture(Team^.AIKillsTex);
979                 Team^.AIKillsTex := RenderStringTex(ansistring(inttostr(Team^.stats.AIKills)), Team^.Clan^.Color, fnt16);
980                 end
981         end;
982 with Gear^ do
983     begin
984     AddFileLog('Delete: #' + inttostr(uid) + ' (' + inttostr(hwRound(x)) + ',' + inttostr(hwRound(y)) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + EnumToStr(Kind));
985     AddRandomness(X.round xor X.frac xor dX.round xor dX.frac xor Y.round xor Y.frac xor dY.round xor dY.frac)
986     end;
987 if CurAmmoGear = Gear then
988     CurAmmoGear:= nil;
989 if FollowGear = Gear then
990     FollowGear:= nil;
991 if lastGearByUID = Gear then
992     lastGearByUID := nil;
993 if (Gear^.Hedgehog = nil) or (Gear^.Hedgehog^.GearHidden <> Gear) then // hidden hedgehogs shouldn't be in the list
994      RemoveGearFromList(Gear)
995 else Gear^.Hedgehog^.GearHidden:= nil;
996 
997 Dispose(Gear)
998 end;
999 
1000 end.
1001