hedgewars/uGearsUtils.pas
branchui-scaling
changeset 15283 c4fd2813b127
parent 15227 263b9850c16d
child 15280 b12f63054c94
equal deleted inserted replaced
13390:0135e64c6c66 15283:c4fd2813b127
    23 uses uTypes, uFloat;
    23 uses uTypes, uFloat;
    24 
    24 
    25 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline;
    25 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline;
    26 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
    26 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
    27 procedure AddSplashForGear(Gear: PGear; justSkipping: boolean);
    27 procedure AddSplashForGear(Gear: PGear; justSkipping: boolean);
       
    28 procedure AddBounceEffectForGear(Gear: PGear; imageScale: Single);
    28 procedure AddBounceEffectForGear(Gear: PGear);
    29 procedure AddBounceEffectForGear(Gear: PGear);
    29 
    30 
    30 function  ModifyDamage(dmg: Longword; Gear: PGear): Longword;
    31 function  ModifyDamage(dmg: Longword; Gear: PGear): Longword;
    31 procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
    32 procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
    32 procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
    33 procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
    33 procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
    34 procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource; Damage: Longword);
    34 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: Longword; showMessage: boolean; vgTint: Longword);
    35 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: LongInt; showMessage: boolean; vgTint: Longword);
    35 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: Longword; showMessage: boolean);
    36 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: LongInt; showMessage: boolean);
       
    37 function IncHogHealth(Hedgehog: PHedgehog; healthBoost: LongInt): LongInt;
    36 procedure CheckHHDamage(Gear: PGear);
    38 procedure CheckHHDamage(Gear: PGear);
    37 procedure CalcRotationDirAngle(Gear: PGear);
    39 procedure CalcRotationDirAngle(Gear: PGear);
    38 procedure ResurrectHedgehog(var gear: PGear);
    40 procedure ResurrectHedgehog(var gear: PGear);
    39 
    41 
    40 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); inline;
    42 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); inline;
    41 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
    43 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
    42 
    44 
       
    45 function  CheckGearNear(Kind: TGearType; X, Y: hwFloat; rX, rY: LongInt): PGear;
    43 function  CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
    46 function  CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
    44 function  CheckGearDrowning(var Gear: PGear): boolean;
    47 function  CheckGearDrowning(var Gear: PGear): boolean;
    45 procedure CheckCollision(Gear: PGear); inline;
    48 procedure CheckCollision(Gear: PGear); inline;
    46 procedure CheckCollisionWithLand(Gear: PGear); inline;
    49 procedure CheckCollisionWithLand(Gear: PGear); inline;
    47 
    50 
    48 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
    51 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
       
    52 procedure AmmoShoveCache(Ammo: PGear; Damage, Power: LongInt);
    49 procedure AmmoShoveLine(Ammo: PGear; Damage, Power: LongInt; oX, oY, tX, tY: hwFloat);
    53 procedure AmmoShoveLine(Ammo: PGear; Damage, Power: LongInt; oX, oY, tX, tY: hwFloat);
    50 function  GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS;
    54 function  GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS;
    51 procedure SpawnBoxOfSmth;
    55 function  SpawnBoxOfSmth: PGear;
       
    56 procedure PlayBoxSpawnTaunt(Gear: PGear);
    52 procedure ShotgunShot(Gear: PGear);
    57 procedure ShotgunShot(Gear: PGear);
    53 function  CanUseTardis(HHGear: PGear): boolean;
    58 function  CanUseTardis(HHGear: PGear): boolean;
    54 
    59 
    55 procedure SetAllToActive;
    60 procedure SetAllToActive;
    56 procedure SetAllHHToActive(Ice: boolean);
    61 procedure SetAllHHToActive(Ice: boolean);
    58 
    63 
    59 function  GetAmmo(Hedgehog: PHedgehog): TAmmoType;
    64 function  GetAmmo(Hedgehog: PHedgehog): TAmmoType;
    60 function  GetUtility(Hedgehog: PHedgehog): TAmmoType;
    65 function  GetUtility(Hedgehog: PHedgehog): TAmmoType;
    61 
    66 
    62 function WorldWrap(var Gear: PGear): boolean;
    67 function WorldWrap(var Gear: PGear): boolean;
    63 
    68 function HomingWrap(var Gear: PGear): boolean;
       
    69 
       
    70 function IsHogFacingLeft(Gear: PGear): boolean;
    64 function IsHogLocal(HH: PHedgehog): boolean;
    71 function IsHogLocal(HH: PHedgehog): boolean;
    65 
    72 
    66 
    73 
    67 function MakeHedgehogsStep(Gear: PGear) : boolean;
    74 function MakeHedgehogsStep(Gear: PGear) : boolean;
    68 
    75 
   111     if vg <> nil then
   118     if vg <> nil then
   112         vg^.Tint:= Tint;
   119         vg^.Tint:= Tint;
   113     end;
   120     end;
   114 if (Mask and EXPLAutoSound) <> 0 then PlaySound(sndExplosion);
   121 if (Mask and EXPLAutoSound) <> 0 then PlaySound(sndExplosion);
   115 
   122 
   116 (*if (Mask and EXPLAllDamageInRadius) = 0 then
       
   117     dmgRadius:= Radius shl 1
       
   118 else
       
   119     dmgRadius:= Radius;
       
   120 dmgBase:= dmgRadius + cHHRadius div 2;*)
       
   121 dmgBase:= Radius shl 1 + cHHRadius div 2;
   123 dmgBase:= Radius shl 1 + cHHRadius div 2;
   122 
   124 
   123 // we might have to run twice if weWrap is enabled
   125 // we might have to run twice if weWrap is enabled
   124 wrap:= false;
   126 wrap:= false;
   125 repeat
   127 repeat
   129 Gear:= GearsList;
   131 Gear:= GearsList;
   130 
   132 
   131 while Gear <> nil do
   133 while Gear <> nil do
   132     begin
   134     begin
   133     dmg:= 0;
   135     dmg:= 0;
   134     //dmg:= dmgRadius  + cHHRadius div 2 - hwRound(Distance(Gear^.X - int2hwFloat(X), Gear^.Y - int2hwFloat(Y)));
       
   135     //if (dmg > 1) and
       
   136     if (Gear^.State and gstNoDamage) = 0 then
   136     if (Gear^.State and gstNoDamage) = 0 then
   137         begin
   137         begin
   138         case Gear^.Kind of
   138         case Gear^.Kind of
   139             gtHedgehog,
   139             gtHedgehog,
   140                 gtMine,
   140                 gtMine,
   141                 gtBall,
   141                 gtBall,
   142                 gtMelonPiece,
   142                 gtMelonPiece,
   143                 gtGrenade,
   143                 gtGrenade,
   144                 gtClusterBomb,
   144                 gtClusterBomb,
   145             //    gtCluster, too game breaking I think
       
   146                 gtSMine,
   145                 gtSMine,
   147                 gtAirMine,
   146                 gtAirMine,
   148                 gtCase,
   147                 gtCase,
   149                 gtTarget,
   148                 gtTarget,
   150                 gtFlame,
   149                 gtFlame,
   151                 gtKnife,
   150                 gtKnife,
   152                 gtExplosives: begin //,
   151                 gtExplosives: begin
   153                 //gtStructure: begin
       
   154 // Run the calcs only once we know we have a type that will need damage
   152 // Run the calcs only once we know we have a type that will need damage
   155                         tdX:= Gear^.X-fX;
   153                         tdX:= Gear^.X-fX;
   156                         tdY:= Gear^.Y-fY;
   154                         tdY:= Gear^.Y-fY;
   157                         if LongInt(tdX.Round + tdY.Round + 2) < dmgBase then
   155                         if LongInt(tdX.Round + tdY.Round + 2) < dmgBase then
   158                             dmg:= dmgBase - hwRound(Distance(tdX, tdY));
   156                             dmg:= dmgBase - hwRound(Distance(tdX, tdY));
   174                                 Gear^.dY:= Gear^.dY + SignAs(_0_005 * dmg + cHHKick, tdY)/(Gear^.Density/_3);
   172                                 Gear^.dY:= Gear^.dY + SignAs(_0_005 * dmg + cHHKick, tdY)/(Gear^.Density/_3);
   175 
   173 
   176                                 Gear^.State:= (Gear^.State or gstMoving) and (not gstLoser);
   174                                 Gear^.State:= (Gear^.State or gstMoving) and (not gstLoser);
   177                                 if Gear^.Kind = gtKnife then Gear^.State:= Gear^.State and (not gstCollision);
   175                                 if Gear^.Kind = gtKnife then Gear^.State:= Gear^.State and (not gstCollision);
   178                                 if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) then
   176                                 if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) then
   179                                     Gear^.State:= (Gear^.State or gstMoving) and (not gstWinner);
   177                                     begin
       
   178                                     Gear^.State:= (Gear^.State or gstMoving) and (not (gstHHJumping or gstHHHJump));
       
   179                                     if (not GameOver) then
       
   180                                         Gear^.State:= (Gear^.State and (not gstWinner));
       
   181                                     end;
   180                                 Gear^.Active:= true;
   182                                 Gear^.Active:= true;
   181                                 if Gear^.Kind <> gtFlame then FollowGear:= Gear
   183                                 if Gear^.Kind <> gtFlame then FollowGear:= Gear;
       
   184                                 if Gear^.Kind = gtAirMine then
       
   185                                     begin
       
   186                                     Gear^.Tag:= 1;
       
   187                                     Gear^.FlightTime:= 5000;
       
   188                                     end
   182                                 end;
   189                                 end;
   183                             if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and
   190                             if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and
   184                                 (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) and (Gear^.Hedgehog^.Effects[heFrozen] = 0) and
   191                                 (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) and (Gear^.Hedgehog^.Effects[heFrozen] = 0) and
   185                                 (Gear^.State and gstHHDeath = 0) then
   192                                 (Gear^.State and gstHHDeath = 0) then
   186                                     begin
   193                                     begin
   187                                     if Gear^.Hedgehog^.Effects[hePoisoned] = 0 then
   194                                     if Gear^.Hedgehog^.Effects[hePoisoned] = 0 then
   188                                         begin
   195                                         begin
   189                                         s:= ansistring(Gear^.Hedgehog^.Name);
   196                                         s:= ansistring(Gear^.Hedgehog^.Name);
   190                                         AddCaption(FormatA(GetEventString(eidPoisoned), s), cWhiteColor, capgrpMessage);
   197                                         AddCaption(FormatA(GetEventString(eidPoisoned), s), capcolDefault, capgrpMessage);
   191                                         uStats.HedgehogPoisoned(Gear, AttackingHog)
   198                                         uStats.HedgehogPoisoned(Gear, AttackingHog)
   192                                         end;
   199                                         end;
   193                                     Gear^.Hedgehog^.Effects[hePoisoned] := 5;
   200                                     Gear^.Hedgehog^.Effects[hePoisoned] := 5;
   194                                     end
   201                                     end
   195                             end;
   202                             end;
   213         end;
   220         end;
   214     Gear:= Gear^.NextGear
   221     Gear:= Gear^.NextGear
   215     end;
   222     end;
   216 
   223 
   217 if (Mask and EXPLDontDraw) = 0 then
   224 if (Mask and EXPLDontDraw) = 0 then
   218     if (GameFlags and gfSolidLand) = 0 then
   225     if ((GameFlags and gfSolidLand) = 0) or ((Mask and EXPLForceDraw) <> 0) then
   219         begin
   226         begin
   220         cnt:= DrawExplosion(X, Y, Radius) div 1608; // approx 2 16x16 circles to erase per chunk
   227         cnt:= DrawExplosion(X, Y, Radius) div 1608; // approx 2 16x16 circles to erase per chunk
   221         if (cnt > 0) and (SpritesData[sprChunk].Texture <> nil) then
   228         if (cnt > 0) and (SpritesData[sprChunk].Texture <> nil) then
   222             for i:= 0 to cnt do
   229             for i:= 0 to cnt do
   223                 AddVisualGear(X, Y, vgtChunk)
   230                 AddVisualGear(X, Y, vgtChunk)
   228     // already wrapped? let's not wrap again!
   235     // already wrapped? let's not wrap again!
   229     if wrap then
   236     if wrap then
   230         break;
   237         break;
   231 
   238 
   232     // Radius + 5 because that's the actual radius the explosion changes graphically
   239     // Radius + 5 because that's the actual radius the explosion changes graphically
   233     if X + (Radius + 5) > LongInt(rightX) then
   240     if X + (Radius + 5) > rightX then
   234         begin
   241         begin
   235         dec(X, playWidth);
   242         dec(X, playWidth);
   236         wrap:= true;
   243         wrap:= true;
   237         end
   244         end
   238     else if X - (Radius + 5) < LongInt(leftX) then
   245     else if X - (Radius + 5) < leftX then
   239         begin
   246         begin
   240         inc(X, playWidth);
   247         inc(X, playWidth);
   241         wrap:= true;
   248         wrap:= true;
   242         end;
   249         end;
   243     end;
   250     end;
   274     if (Gear^.Kind = gtHedgehog) then
   281     if (Gear^.Kind = gtHedgehog) then
   275         begin
   282         begin
   276         Gear^.LastDamage := AttackerHog;
   283         Gear^.LastDamage := AttackerHog;
   277 
   284 
   278         Gear^.Hedgehog^.Team^.Clan^.Flawless:= false;
   285         Gear^.Hedgehog^.Team^.Clan^.Flawless:= false;
   279         HHHurt(Gear^.Hedgehog, Source);
   286 
   280         AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), Damage, Gear^.Hedgehog^.Team^.Clan^.Color);
   287         if (Gear^.State and gstHHDeath) <> 0 then
       
   288             // If hog took damage while dying, explode hog instantly (see doStepHedgehogDead)
       
   289             Gear^.Timer:= 1
       
   290         else
       
   291             begin
       
   292             HHHurt(Gear^.Hedgehog, Source, Damage);
       
   293             AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), Damage, Gear^.Hedgehog^.Team^.Clan^.Color);
       
   294             end;
       
   295 
   281         tmpDmg:= min(Damage, max(0,Gear^.Health-Gear^.Damage));
   296         tmpDmg:= min(Damage, max(0,Gear^.Health-Gear^.Damage));
   282         if (Gear <> CurrentHedgehog^.Gear) and (CurrentHedgehog^.Gear <> nil) and (tmpDmg >= 1) then
   297         if (Gear <> CurrentHedgehog^.Gear) and (CurrentHedgehog^.Gear <> nil) and (tmpDmg >= 1) then
   283             begin
   298             begin
   284             if cVampiric then
   299             if cVampiric then
   285                 begin
   300                 begin
   286                 vampDmg:= hwRound(int2hwFloat(tmpDmg)*_0_8);
   301                 vampDmg:= hwRound(int2hwFloat(tmpDmg)*_0_8);
   287                 if vampDmg >= 1 then
   302                 if vampDmg >= 1 then
   288                     begin
   303                     begin
   289                     // was considering pulsing on attack, Tiy thinks it should be permanent while in play
   304                     // was considering pulsing on attack, Tiy thinks it should be permanent while in play
   290                     //CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstVampiric;
   305                     //CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstVampiric;
   291                     inc(CurrentHedgehog^.Gear^.Health,vampDmg);
   306                     vampDmg:= IncHogHealth(CurrentHedgehog, vampDmg);
   292                     RenderHealth(CurrentHedgehog^);
   307                     RenderHealth(CurrentHedgehog^);
   293                     RecountTeamHealth(CurrentHedgehog^.Team);
   308                     RecountTeamHealth(CurrentHedgehog^.Team);
   294                     HHHeal(CurrentHedgehog, vampDmg, true, $FF0000FF);
   309                     HHHeal(CurrentHedgehog, vampDmg, true, $FF0000FF);
   295                     end
   310                     end
   296                 end;
   311                 end;
   303                 end;
   318                 end;
   304             end;
   319             end;
   305 
   320 
   306         uStats.HedgehogDamaged(Gear, AttackerHog, Damage, false);
   321         uStats.HedgehogDamaged(Gear, AttackerHog, Damage, false);
   307 
   322 
   308 	if AprilOne and (Gear^.Hedgehog^.Hat = 'fr_tomato') and (Damage > 2) then
   323     if AprilOne and (Gear^.Hedgehog^.Hat = 'fr_tomato') and (Damage > 2) then
   309 	    for i := 0 to random(min(Damage,20))+5 do
   324         for i := 0 to random(min(Damage,20))+5 do
   310 		begin
   325         begin
   311 		vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
   326         vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
   312 		if vg <> nil then
   327         if vg <> nil then
   313 		    with vg^ do
   328             with vg^ do
   314 			begin
   329             begin
   315 			dx:= 0.001 * (random(100)+10);
   330             dx:= 0.001 * (random(100)+10);
   316 			dy:= 0.001 * (random(100)+10);
   331             dy:= 0.001 * (random(100)+10);
   317 			tdy:= -cGravityf;
   332             tdy:= -cGravityf;
   318 			if random(2) = 0 then
   333             if random(2) = 0 then
   319 			    dx := -dx;
   334                 dx := -dx;
   320 			//if random(2) = 0 then
   335             FrameTicks:= random(500) + 1000;
   321 			//    dy := -dy;
   336             State:= ord(sprBubbles);
   322 			FrameTicks:= random(500) + 1000;
   337             Tint:= $ff0000ff
   323 			State:= ord(sprBubbles);
   338             end
   324 			//Tint:= $bd2f03ff
   339     end
   325 			Tint:= $ff0000ff
       
   326 			end
       
   327 	end
       
   328     end else
   340     end else
   329     //else if Gear^.Kind <> gtStructure then // not gtHedgehog nor gtStructure
       
   330         Gear^.Hedgehog:= AttackerHog;
   341         Gear^.Hedgehog:= AttackerHog;
   331     inc(Gear^.Damage, Damage);
   342     inc(Gear^.Damage, Damage);
   332 
   343 
   333     ScriptCall('onGearDamage', Gear^.UID, Damage);
   344     ScriptCall('onGearDamage', Gear^.UID, Damage);
   334 end;
   345 end;
   342 AllInactive:= false;
   353 AllInactive:= false;
   343 HHGear^.Active:= true;
   354 HHGear^.Active:= true;
   344 end;
   355 end;
   345 
   356 
   346 // Play effects for hurt hedgehog
   357 // Play effects for hurt hedgehog
   347 procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
   358 procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource; Damage: Longword);
   348 begin
   359 begin
   349 if Hedgehog^.Effects[heFrozen] <> 0 then exit;
   360 if Hedgehog^.Effects[heFrozen] <> 0 then exit;
   350 
   361 
   351 if (Source = dsFall) or (Source = dsExplosion) then
   362 if (Damage >= ouchDmg) and (OuchTauntTimer = 0) and ((Source = dsFall) or (Source = dsBullet) or (Source = dsShove) or (Source = dsHammer)) then
       
   363     begin
       
   364     PlaySoundV(sndOuch, Hedgehog^.Team^.voicepack);
       
   365     // Prevent sndOuch from being played too often in short time
       
   366     OuchTauntTimer:= 1250;
       
   367     end
       
   368 else if (Source = dsFall) or (Source = dsExplosion) then
   352     case random(3) of
   369     case random(3) of
   353         0: PlaySoundV(sndOoff1, Hedgehog^.Team^.voicepack);
   370         0: PlaySoundV(sndOoff1, Hedgehog^.Team^.voicepack);
   354         1: PlaySoundV(sndOoff2, Hedgehog^.Team^.voicepack);
   371         1: PlaySoundV(sndOoff2, Hedgehog^.Team^.voicepack);
   355         2: PlaySoundV(sndOoff3, Hedgehog^.Team^.voicepack);
   372         2: PlaySoundV(sndOoff3, Hedgehog^.Team^.voicepack);
   356     end
   373     end
   371 {-
   388 {-
   372 Show heal particles and message at hog gear.
   389 Show heal particles and message at hog gear.
   373 Hedgehog: Hedgehog which gets the health boost
   390 Hedgehog: Hedgehog which gets the health boost
   374 healthBoost: Amount of added health added
   391 healthBoost: Amount of added health added
   375 showMessage: Whether to show announcer message
   392 showMessage: Whether to show announcer message
   376 vgTint: Tint of heal particle
   393 vgTint: Tint of heal particle (if 0, don't render particles)
   377 -}
   394 -}
   378 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: Longword; showMessage: boolean; vgTint: Longword);
   395 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: LongInt; showMessage: boolean; vgTint: Longword);
   379 var i: LongInt;
   396 var i: LongInt;
   380     vg: PVisualGear;
   397     vg: PVisualGear;
   381     s: ansistring;
   398     s: ansistring;
   382 begin
   399 begin
   383     if healthBoost < 1 then
   400     if healthBoost < 1 then
   389         AddCaption(FormatA(trmsg[sidHealthGain], s), Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo)
   406         AddCaption(FormatA(trmsg[sidHealthGain], s), Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo)
   390         end;
   407         end;
   391 
   408 
   392     i:= 0;
   409     i:= 0;
   393     // One particle for every 5 HP. Max. 200 particles
   410     // One particle for every 5 HP. Max. 200 particles
   394     while (i < healthBoost) and (i < 1000) do
   411     if (vgTint <> 0) then
   395         begin
   412         while (i < healthBoost) and (i < 1000) do
   396         vg:= AddVisualGear(hwRound(Hedgehog^.Gear^.X), hwRound(Hedgehog^.Gear^.Y), vgtStraightShot);
   413             begin
   397         if vg <> nil then
   414             vg:= AddVisualGear(hwRound(Hedgehog^.Gear^.X), hwRound(Hedgehog^.Gear^.Y), vgtStraightShot);
   398             with vg^ do
   415             if vg <> nil then
   399                 begin
   416                 with vg^ do
   400                 Tint:= vgTint;
   417                     begin
   401                 State:= ord(sprHealth)
   418                     Tint:= vgTint;
   402                 end;
   419                     State:= ord(sprHealth)
   403         inc(i, 5)
   420                     end;
   404         end;
   421             inc(i, 5)
       
   422             end;
   405 end;
   423 end;
   406 
   424 
   407 // Shorthand for the same above, but with tint implied
   425 // Shorthand for the same above, but with tint implied
   408 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: Longword; showMessage: boolean);
   426 procedure HHHeal(Hedgehog: PHedgehog; healthBoost: LongInt; showMessage: boolean);
   409 begin
   427 begin
   410     HHHeal(Hedgehog, healthBoost, showMessage, $00FF00FF);
   428     HHHeal(Hedgehog, healthBoost, showMessage, $00FF00FF);
       
   429 end;
       
   430 
       
   431 // Increase hog health by healthBoost (at least 1).
       
   432 // Resulting health is capped at cMaxHogHealth.
       
   433 // Returns actual amount healed.
       
   434 function IncHogHealth(Hedgehog: PHedgehog; healthBoost: LongInt): LongInt;
       
   435 var oldHealth: LongInt;
       
   436 begin
       
   437    if healthBoost < 1 then
       
   438        begin
       
   439        IncHogHealth:= 0;
       
   440        exit;
       
   441        end;
       
   442    oldHealth:= Hedgehog^.Gear^.Health;
       
   443    inc(Hedgehog^.Gear^.Health, healthBoost);
       
   444    // Prevent overflow
       
   445    if (Hedgehog^.Gear^.Health < 1) or (Hedgehog^.Gear^.Health > cMaxHogHealth) then
       
   446        Hedgehog^.Gear^.Health:= cMaxHogHealth;
       
   447    IncHogHealth:= Hedgehog^.Gear^.Health - oldHealth;
   411 end;
   448 end;
   412 
   449 
   413 procedure CheckHHDamage(Gear: PGear);
   450 procedure CheckHHDamage(Gear: PGear);
   414 var
   451 var
   415     dmg: LongInt;
   452     dmg: LongInt;
   433         end;
   470         end;
   434 
   471 
   435     if ((Gear^.Hedgehog^.Effects[heInvulnerable] <> 0)) then
   472     if ((Gear^.Hedgehog^.Effects[heInvulnerable] <> 0)) then
   436         exit;
   473         exit;
   437 
   474 
   438     //if _0_6 < Gear^.dY then
       
   439     //    PlaySound(sndOw4, Gear^.Hedgehog^.Team^.voicepack)
       
   440     //else
       
   441     //    PlaySound(sndOw1, Gear^.Hedgehog^.Team^.voicepack);
       
   442 
       
   443     if Gear^.LastDamage <> nil then
   475     if Gear^.LastDamage <> nil then
   444         ApplyDamage(Gear, Gear^.LastDamage, dmg, dsFall)
   476         ApplyDamage(Gear, Gear^.LastDamage, dmg, dsFall)
   445     else
   477     else
   446         ApplyDamage(Gear, CurrentHedgehog, dmg, dsFall);
   478         ApplyDamage(Gear, CurrentHedgehog, dmg, dsFall);
   447     end
   479     end
   451 procedure CalcRotationDirAngle(Gear: PGear);
   483 procedure CalcRotationDirAngle(Gear: PGear);
   452 var
   484 var
   453     dAngle: real;
   485     dAngle: real;
   454 begin
   486 begin
   455     // Frac/Round to be kind to JS as of 2012-08-27 where there is yet no int64/uint64
   487     // Frac/Round to be kind to JS as of 2012-08-27 where there is yet no int64/uint64
   456     //dAngle := (Gear^.dX.QWordValue + Gear^.dY.QWordValue) / $80000000;
       
   457     dAngle := (Gear^.dX.Round + Gear^.dY.Round) / 2 + (Gear^.dX.Frac/$100000000+Gear^.dY.Frac/$100000000);
   488     dAngle := (Gear^.dX.Round + Gear^.dY.Round) / 2 + (Gear^.dX.Frac/$100000000+Gear^.dY.Frac/$100000000);
   458     if not Gear^.dX.isNegative then
   489     if not Gear^.dX.isNegative then
   459         Gear^.DirAngle := Gear^.DirAngle + dAngle
   490         Gear^.DirAngle := Gear^.DirAngle + dAngle
   460     else
   491     else
   461         Gear^.DirAngle := Gear^.DirAngle - dAngle;
   492         Gear^.DirAngle := Gear^.DirAngle - dAngle;
   553     Scale:= abs(hwFloat2Float(Gear^.Density / _3 * speed));
   584     Scale:= abs(hwFloat2Float(Gear^.Density / _3 * speed));
   554     if Scale > 1 then Scale:= power(Scale,0.3333)
   585     if Scale > 1 then Scale:= power(Scale,0.3333)
   555     else Scale:= Scale + ((1-Scale) / 2);
   586     else Scale:= Scale + ((1-Scale) / 2);
   556     if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
   587     if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
   557     else Timer:= 1;
   588     else Timer:= 1;
       
   589     if Scale > 1 then
       
   590         if (not isImpactH) then
       
   591             Y:= Y + 10
       
   592         else if isImpactRight then
       
   593             X:= X + 10
       
   594         else
       
   595             X:= X - 10;
   558     // Low Gravity
   596     // Low Gravity
   559     FrameTicks:= FrameTicks*Timer;
   597     FrameTicks:= FrameTicks*Timer;
   560     end;
   598     end;
   561 
   599 
   562 
   600 
   629     isDirH:= false;
   667     isDirH:= false;
   630 
   668 
   631     if WorldEdge = weSea then
   669     if WorldEdge = weSea then
   632         begin
   670         begin
   633         tmp:= dist2Water;
   671         tmp:= dist2Water;
   634         dist2Water:= min(dist2Water, min(X - Gear^.Radius - LongInt(leftX), LongInt(rightX) - (X + Gear^.Radius)));
   672         dist2Water:= min(dist2Water, min(X - Gear^.Radius - leftX, rightX - (X + Gear^.Radius)));
   635         // if water on sides is closer than on bottom -> horizontal direction
   673         // if water on sides is closer than on bottom -> horizontal direction
   636         isDirH:= tmp <> dist2Water;
   674         isDirH:= tmp <> dist2Water;
   637         end;
   675         end;
   638 
   676 
   639     isImpact:= false;
   677     isImpact:= false;
   709                             begin
   747                             begin
   710                             DrownGear(Gear);
   748                             DrownGear(Gear);
   711                             Gear^.State := Gear^.State and (not gstHHDriven);
   749                             Gear^.State := Gear^.State and (not gstHHDriven);
   712                             s:= ansistring(Gear^.Hedgehog^.Name);
   750                             s:= ansistring(Gear^.Hedgehog^.Name);
   713                             if Gear^.Hedgehog^.King then
   751                             if Gear^.Hedgehog^.King then
   714                                 AddCaption(FormatA(GetEventString(eidKingDied), s), cWhiteColor, capgrpMessage)
   752                                 AddCaption(FormatA(GetEventString(eidKingDied), s), capcolDefault, capgrpMessage)
   715                             else
   753                             else
   716                                 AddCaption(FormatA(GetEventString(eidDrowned), s), cWhiteColor, capgrpMessage);
   754                                 AddCaption(FormatA(GetEventString(eidDrowned), s), capcolDefault, capgrpMessage);
   717                             end
   755                             end
   718                         end
   756                         end
   719                     else
   757                     else
   720                         DrownGear(Gear);
   758                         DrownGear(Gear);
   721                     if Gear^.Kind = gtFlake then
   759                     if Gear^.Kind = gtFlake then
   722                         exit(true); // skip splashes
   760                         exit(true); // skip splashes
   723                 end
   761                 end
   724             else // submersible
   762             else // submersible
   725                 begin
   763                 begin
   726                 // drown submersible grears if far below map
   764                 // drown submersible gears if far below map
   727                 if (Y > cWaterLine + cVisibleWater*4) then
   765                 if (Y > cWaterLine + cVisibleWater*4) then
   728                     begin
   766                     begin
   729                     DrownGear(Gear);
   767                     DrownGear(Gear);
   730                     exit(true); // no splashes needed
   768                     exit(true); // no splashes needed
   731                     end;
   769                     end;
   760                 end; // end of submersible
   798                 end; // end of submersible
   761             end; // end of not skipping
   799             end; // end of not skipping
   762 
   800 
   763         // splash sound animation and droplets
   801         // splash sound animation and droplets
   764         if isImpact or isSkip then
   802         if isImpact or isSkip then
   765             addSplashForGear(Gear, isSkip);
   803             if (not (((dist2Water + Gear^.Radius div 2) < 0) or (abs(dist2Water + Gear^.Radius) >= Gear^.Radius))) then
       
   804                 addSplashForGear(Gear, isSkip);
   766 
   805 
   767         if isSkip then
   806         if isSkip then
   768             ScriptCall('onGearWaterSkip', Gear^.uid);
   807             ScriptCall('onGearWaterSkip', Gear^.uid);
   769         end
   808         end
   770     else
   809     else
   772 end;
   811 end;
   773 
   812 
   774 
   813 
   775 procedure ResurrectHedgehog(var gear: PGear);
   814 procedure ResurrectHedgehog(var gear: PGear);
   776 var tempTeam : PTeam;
   815 var tempTeam : PTeam;
   777     sparkles: PVisualGear;
   816     sparkles, expl: PVisualGear;
   778     gX, gY: LongInt;
   817     gX, gY: LongInt;
   779 begin
   818 begin
   780     if (Gear^.LastDamage <> nil) then
   819     if (Gear^.LastDamage <> nil) then
   781         uStats.HedgehogDamaged(Gear, Gear^.LastDamage, 0, true)
   820         uStats.HedgehogDamaged(Gear, Gear^.LastDamage, 0, true)
   782     else
   821     else
   783         uStats.HedgehogDamaged(Gear, CurrentHedgehog, 0, true);
   822         uStats.HedgehogDamaged(Gear, CurrentHedgehog, 0, true);
       
   823     // Reset gear state
   784     AttackBar:= 0;
   824     AttackBar:= 0;
   785     gear^.dX := _0;
   825     gear^.dX := _0;
   786     gear^.dY := _0;
   826     gear^.dY := _0;
   787     gear^.Damage := 0;
   827     gear^.Damage := 0;
   788     gear^.Health := gear^.Hedgehog^.InitialHealth;
   828     gear^.Health := gear^.Hedgehog^.InitialHealth;
   797             end;
   837             end;
   798     tempTeam := gear^.Hedgehog^.Team;
   838     tempTeam := gear^.Hedgehog^.Team;
   799     DeleteCI(gear);
   839     DeleteCI(gear);
   800     gX := hwRound(gear^.X);
   840     gX := hwRound(gear^.X);
   801     gY := hwRound(gear^.Y);
   841     gY := hwRound(gear^.Y);
   802     // might need more sparkles for a column
   842     // Spawn a few sparkles at death position.
       
   843     // Might need more sparkles for a column.
   803     sparkles:= AddVisualGear(gX, gY, vgtDust, 1);
   844     sparkles:= AddVisualGear(gX, gY, vgtDust, 1);
   804     if sparkles <> nil then
   845     if sparkles <> nil then
   805         begin
   846         begin
   806         sparkles^.Tint:= tempTeam^.Clan^.Color shl 8 or $FF;
   847         sparkles^.Tint:= tempTeam^.Clan^.Color shl 8 or $FF;
   807         //sparkles^.Angle:= random(360);
       
   808         end;
   848         end;
       
   849     // Set new position of gear (might fail)
   809     FindPlace(gear, false, 0, LAND_WIDTH, true);
   850     FindPlace(gear, false, 0, LAND_WIDTH, true);
   810     if gear <> nil then
   851     if gear <> nil then
   811         begin
   852         begin
   812         AddVisualGear(hwRound(gear^.X), hwRound(gear^.Y), vgtExplosion);
   853         // Visual effect at position of resurrection
       
   854         expl:= AddVisualGear(hwRound(gear^.X), hwRound(gear^.Y), vgtExplosion);
   813         PlaySound(sndWarp);
   855         PlaySound(sndWarp);
   814         RenderHealth(gear^.Hedgehog^);
   856         RenderHealth(gear^.Hedgehog^);
   815         ScriptCall('onGearResurrect', gear^.uid);
   857         if expl <> nil then
       
   858             ScriptCall('onGearResurrect', gear^.uid, expl^.uid)
       
   859         else
       
   860             ScriptCall('onGearResurrect', gear^.uid);
   816         gear^.State := gstWait;
   861         gear^.State := gstWait;
   817         end;
   862         end;
   818     RecountTeamHealth(tempTeam);
   863     RecountTeamHealth(tempTeam);
   819 end;
   864 end;
   820 
   865 
   894 ignoreNearObjects:= false; // try not skipping proximity at first
   939 ignoreNearObjects:= false; // try not skipping proximity at first
   895 ignoreOverlap:= false; // this not only skips proximity, but allows overlapping objects (barrels, mines, hogs, crates).  Saving it for a 3rd pass.  With this active, winning AI Survival goes back to virtual impossibility
   940 ignoreOverlap:= false; // this not only skips proximity, but allows overlapping objects (barrels, mines, hogs, crates).  Saving it for a 3rd pass.  With this active, winning AI Survival goes back to virtual impossibility
   896 tryAgain:= true;
   941 tryAgain:= true;
   897 if WorldEdge <> weNone then
   942 if WorldEdge <> weNone then
   898     begin
   943     begin
   899     Left:= max(Left, LongInt(leftX) + Gear^.Radius);
   944     Left:= max(Left, leftX + Gear^.Radius);
   900     Right:= min(Right,rightX-Gear^.Radius)
   945     Right:= min(Right,rightX-Gear^.Radius)
   901     end;
   946     end;
   902 while tryAgain do
   947 while tryAgain do
   903     begin
   948     begin
   904     delta:= LAND_WIDTH div 16;
   949     delta:= LAND_WIDTH div 16;
  1023         Gear:= nil
  1068         Gear:= nil
  1024         end
  1069         end
  1025     end
  1070     end
  1026 end;
  1071 end;
  1027 
  1072 
       
  1073 function CheckGearNearImpl(Kind: TGearType; X, Y: hwFloat; rX, rY: LongInt; exclude: PGear): PGear;
       
  1074 var t: PGear;
       
  1075     width, bound, dX, dY: hwFloat;
       
  1076     isHit: Boolean;
       
  1077     i, j: LongWord;
       
  1078 begin
       
  1079     bound:= _1_5 * int2hwFloat(max(rX, rY));
       
  1080     rX:= sqr(rX);
       
  1081     rY:= sqr(rY);
       
  1082     width:= int2hwFloat(RightX - LeftX);
       
  1083     if (Kind = gtHedgehog) then
       
  1084         begin
       
  1085         for j:= 0 to Pred(TeamsCount) do
       
  1086             if TeamsArray[j]^.TeamHealth > 0 then // it's impossible for a team to have hogs in game and zero health right?
       
  1087                 with TeamsArray[j]^ do
       
  1088                     for i:= 0 to cMaxHHIndex do
       
  1089                         with Hedgehogs[i] do
       
  1090                             if (Gear <> nil) and (Gear <> exclude) then
       
  1091                                 begin
       
  1092                                 // code duplication - could throw into an inline function I guess
       
  1093                                 dX := X - Gear^.X;
       
  1094                                 dY := Y - Gear^.Y;
       
  1095                                 isHit := (hwAbs(dX) + hwAbs(dY) < bound)
       
  1096                                     and (not ((hwSqr(dX) / rX + hwSqr(dY) / rY) > _1));
       
  1097 
       
  1098                                 if (not isHit) and (WorldEdge = weWrap) then
       
  1099                                     begin
       
  1100                                     if (hwAbs(dX - width) + hwAbs(dY) < bound)
       
  1101                                         and (not ((hwSqr(dX - width) / rX + hwSqr(dY) / rY) > _1)) then
       
  1102                                         isHit := true
       
  1103                                     else if (hwAbs(dX + width) + hwAbs(dY) < bound)
       
  1104                                         and (not ((hwSqr(dX + width) / rX + hwSqr(dY) / rY) > _1)) then
       
  1105                                         isHit := true
       
  1106                                     end;
       
  1107 
       
  1108                                 if isHit then
       
  1109                                     begin
       
  1110                                     CheckGearNearImpl:= Gear;
       
  1111                                     exit;
       
  1112                                     end
       
  1113                                 end;
       
  1114         end
       
  1115     else
       
  1116         begin
       
  1117         t:= GearsList;
       
  1118 
       
  1119         while t <> nil do
       
  1120             begin
       
  1121             if (t <> exclude) and (t^.Kind = Kind) then
       
  1122                 begin
       
  1123                 dX := X - t^.X;
       
  1124                 dY := Y - t^.Y;
       
  1125                 isHit := (hwAbs(dX) + hwAbs(dY) < bound)
       
  1126                     and (not ((hwSqr(dX) / rX + hwSqr(dY) / rY) > _1));
       
  1127 
       
  1128                 if (not isHit) and (WorldEdge = weWrap) then
       
  1129                     begin
       
  1130                     if (hwAbs(dX - width) + hwAbs(dY) < bound)
       
  1131                         and (not ((hwSqr(dX - width) / rX + hwSqr(dY) / rY) > _1)) then
       
  1132                         isHit := true
       
  1133                     else if (hwAbs(dX + width) + hwAbs(dY) < bound)
       
  1134                         and (not ((hwSqr(dX + width) / rX + hwSqr(dY) / rY) > _1)) then
       
  1135                         isHit := true
       
  1136                     end;
       
  1137 
       
  1138                 if isHit then
       
  1139                     begin
       
  1140                     CheckGearNearImpl:= t;
       
  1141                     exit;
       
  1142                     end;
       
  1143                 end;
       
  1144             t:= t^.NextGear
       
  1145             end
       
  1146     end;
       
  1147 
       
  1148     CheckGearNearImpl:= nil
       
  1149 end;
       
  1150 
       
  1151 function CheckGearNear(Kind: TGearType; X, Y: hwFloat; rX, rY: LongInt): PGear;
       
  1152 begin
       
  1153     CheckGearNear := CheckGearNearImpl(Kind, X, Y, rX, rY, nil);
       
  1154 end;
       
  1155 
  1028 function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
  1156 function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
  1029 var t: PGear;
  1157 begin
  1030 begin
  1158     CheckGearNear := CheckGearNearImpl(Kind, Gear^.X, Gear^.Y, rX, rY, Gear);
  1031 t:= GearsList;
       
  1032 rX:= sqr(rX);
       
  1033 rY:= sqr(rY);
       
  1034 
       
  1035 while t <> nil do
       
  1036     begin
       
  1037     if (t <> Gear) and (t^.Kind = Kind) then
       
  1038         if (not ((hwSqr(Gear^.X - t^.X) / rX + hwSqr(Gear^.Y - t^.Y) / rY) > _1)) or
       
  1039         ((WorldEdge = weWrap) and (
       
  1040         (not ((hwSqr(Gear^.X - int2hwFloat(RightX-LeftX) - t^.X) / rX + hwSqr(Gear^.Y - t^.Y) / rY) > _1)) or
       
  1041         (not ((hwSqr(Gear^.X + int2hwFloat(RightX-LeftX) - t^.X) / rX + hwSqr(Gear^.Y - t^.Y) / rY) > _1)))) then
       
  1042         begin
       
  1043             CheckGearNear:= t;
       
  1044             exit;
       
  1045         end;
       
  1046     t:= t^.NextGear
       
  1047     end;
       
  1048 
       
  1049 CheckGearNear:= nil
       
  1050 end;
  1159 end;
  1051 
  1160 
  1052 procedure CheckCollision(Gear: PGear); inline;
  1161 procedure CheckCollision(Gear: PGear); inline;
  1053 begin
  1162 begin
  1054     if (TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) <> 0)
  1163     if (TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) <> 0)
  1148     begin
  1257     begin
  1149     case t^.Kind of
  1258     case t^.Kind of
  1150         gtHedgehog,
  1259         gtHedgehog,
  1151             gtMine,
  1260             gtMine,
  1152             gtSMine,
  1261             gtSMine,
       
  1262             gtAirMine,
  1153             gtKnife,
  1263             gtKnife,
  1154             gtCase,
  1264             gtCase,
  1155             gtTarget,
  1265             gtTarget,
  1156             gtExplosives: begin//,
  1266             gtExplosives: begin
  1157 //            gtStructure: begin
       
  1158 //addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg));
  1267 //addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg));
  1159                     dmg:= 0;
  1268                     dmg:= 0;
  1160                     r:= Gear^.Radius + t^.Radius;
  1269                     r:= Gear^.Radius + t^.Radius;
  1161                     dx:= Gear^.X-t^.X;
  1270                     dx:= Gear^.X-t^.X;
  1162                     dx.isNegative:= false;
  1271                     dx.isNegative:= false;
  1178                         t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
  1287                         t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
  1179                         t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
  1288                         t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
  1180                         t^.State:= t^.State or gstMoving;
  1289                         t^.State:= t^.State or gstMoving;
  1181                         if t^.Kind = gtKnife then t^.State:= t^.State and (not gstCollision);
  1290                         if t^.Kind = gtKnife then t^.State:= t^.State and (not gstCollision);
  1182                         t^.Active:= true;
  1291                         t^.Active:= true;
  1183                         FollowGear:= t
  1292                         FollowGear:= t;
       
  1293 
       
  1294                         if t^.Kind = gtAirmine then
       
  1295                         begin
       
  1296                             t^.Tag:= 1;
       
  1297                             t^.FlightTime:= 5000;
       
  1298                         end
  1184                         end
  1299                         end
  1185                     end;
  1300                     end;
  1186             gtGrave: begin
  1301             gtGrave: begin
  1187                     dmg:= 0;
  1302                     dmg:= 0;
  1188                     r:= Gear^.Radius + t^.Radius;
  1303                     r:= Gear^.Radius + t^.Radius;
  1260     Ammo^.Health:= 0;
  1375     Ammo^.Health:= 0;
  1261 while i > 0 do
  1376 while i > 0 do
  1262     begin
  1377     begin
  1263     dec(i);
  1378     dec(i);
  1264     Gear:= t^.ar[i];
  1379     Gear:= t^.ar[i];
  1265     if (Ammo^.Data <> nil) and (Ammo^.Kind in [gtDEagleShot, gtSniperRifleShot, gtMinigunBullet]) and (PGear(Ammo^.Data) = Gear)
  1380     if (Ammo^.Kind in [gtDEagleShot, gtSniperRifleShot, gtMinigunBullet,
  1266     or ((Ammo^.Kind = gtMinigunBullet) and (not UpdateHitOrder(Gear, Ammo^.WDTimer))) then
  1381                        gtFirePunch, gtKamikaze, gtWhip, gtShover])
       
  1382         and (((Ammo^.Data <> nil) and (PGear(Ammo^.Data) = Gear))
       
  1383              or (not UpdateHitOrder(Gear, Ammo^.WDTimer))) then
  1267         continue;
  1384         continue;
  1268 
  1385 
  1269     if ((Ammo^.Kind = gtFlame) or (Ammo^.Kind = gtBlowTorch)) and
  1386     if ((Ammo^.Kind = gtFlame) or (Ammo^.Kind = gtBlowTorch)) and
  1270     (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heFrozen] > 255) then
  1387     (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heFrozen] > 255) then
  1271         Gear^.Hedgehog^.Effects[heFrozen]:= max(255,Gear^.Hedgehog^.Effects[heFrozen]-10000);
  1388         Gear^.Hedgehog^.Effects[heFrozen]:= max(255,Gear^.Hedgehog^.Effects[heFrozen]-10000);
  1272     tmpDmg:= ModifyDamage(Damage, Gear);
  1389     tmpDmg:= ModifyDamage(Damage, Gear);
  1273     if (Gear^.State and gstNoDamage) = 0 then
  1390     if (Gear^.State and gstNoDamage) = 0 then
  1274         begin
  1391         begin
  1275 
  1392 
  1276         if (Gear^.Kind <> gtMinigun) and
       
  1277             ((Ammo^.Kind = gtDEagleShot)
       
  1278                 or (Ammo^.Kind = gtSniperRifleShot)
       
  1279                 or (Ammo^.Kind = gtMinigunBullet)) then
       
  1280             begin
       
  1281             VGear := AddVisualGear(t^.cX[i], t^.cY[i], vgtBulletHit);
       
  1282             if VGear <> nil then
       
  1283                 VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY);
       
  1284             end;
       
  1285 
       
  1286         if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then
  1393         if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then
  1287             Gear^.FlightTime:= 1;
  1394             Gear^.FlightTime:= 1;
  1288 
       
  1289 
  1395 
  1290         case Gear^.Kind of
  1396         case Gear^.Kind of
  1291             gtHedgehog,
  1397             gtHedgehog,
  1292             gtMine,
  1398             gtMine,
       
  1399             gtAirMine,
  1293             gtSMine,
  1400             gtSMine,
  1294             gtKnife,
  1401             gtKnife,
  1295             gtTarget,
  1402             gtTarget,
  1296             gtCase,
  1403             gtCase,
  1297             gtExplosives: //,
  1404             gtExplosives:
  1298             //gtStructure:
  1405             begin
  1299             begin
  1406             if (Ammo^.Kind in [gtFirePunch, gtKamikaze]) and (Gear^.Kind <> gtSMine) then
       
  1407                 PlaySound(sndFirePunchHit);
       
  1408 
       
  1409             if Ammo^.Kind in [gtDEagleShot, gtSniperRifleShot, gtMinigunBullet] then
       
  1410                 begin
       
  1411                 VGear := AddVisualGear(t^.cX[i], t^.cY[i], vgtBulletHit);
       
  1412                 if VGear <> nil then
       
  1413                     VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY);
       
  1414                 end;
  1300             if (Ammo^.Kind = gtDrill) then
  1415             if (Ammo^.Kind = gtDrill) then
  1301                 begin
  1416                 begin
  1302                 Ammo^.Timer:= 0;
  1417                 Ammo^.Timer:= 0;
  1303                 exit;
  1418                 exit;
  1304                 end;
  1419                 end;
  1324                                     dy := -dy;
  1439                                     dy := -dy;
  1325                                 FrameTicks:= 600+random(200);
  1440                                 FrameTicks:= 600+random(200);
  1326                                 State:= ord(sprStar)
  1441                                 State:= ord(sprStar)
  1327                                 end
  1442                                 end
  1328                         end;
  1443                         end;
  1329                 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove)
  1444                 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove);
       
  1445 
       
  1446                 if Gear^.Kind = gtAirmine then
       
  1447                     begin
       
  1448                         Gear^.Tag:= 1;
       
  1449                         Gear^.FlightTime:= 5000;
       
  1450                     end
  1330                 end
  1451                 end
  1331             else
  1452             else
  1332                 Gear^.State:= Gear^.State or gstWinner;
  1453                 Gear^.State:= Gear^.State or gstWinner;
  1333             if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then
  1454             if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then
  1334                 begin
  1455                 begin
  1335                 if (Ammo^.Hedgehog^.Gear <> nil) then
  1456                 if (Ammo^.Hedgehog^.Gear <> nil) then
  1336                     Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable);
  1457                     Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable);
  1337                 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch
  1458                 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsExplosion); // crank up damage for explosives + blowtorch
  1338                 end;
  1459                 end;
  1339 
  1460 
  1340             if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then
  1461             if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then
  1341                 begin
  1462                 begin
  1342                 Gear^.dX:= Gear^.dX + Ammo^.dX * Power * _0_005;
  1463                 Gear^.dX:= Ammo^.dX * Power * _0_005;
  1343                 Gear^.dY:= Gear^.dY + Ammo^.dY * Power * _0_005
  1464                 Gear^.dY:= Ammo^.dY * Power * _0_005
  1344                 end
  1465                 end
  1345             else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then
  1466             else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then
  1346                 begin
  1467                 begin
  1347                 Gear^.dX:= Gear^.dX + Ammo^.dX * Power * _0_01;
  1468                 Gear^.dX:= Ammo^.dX * Power * _0_01;
  1348                 Gear^.dY:= Gear^.dY + Ammo^.dY * Power * _0_01
  1469                 Gear^.dY:= Ammo^.dY * Power * _0_01
  1349                 end;
  1470                 end;
  1350 
  1471 
  1351             if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then
  1472             if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then
  1352                 begin
  1473                 begin
  1353                 Gear^.Active:= true;
  1474                 Gear^.Active:= true;
  1381 end;
  1502 end;
  1382 
  1503 
  1383 procedure AmmoShoveLine(Ammo: PGear; Damage, Power: LongInt; oX, oY, tX, tY: hwFloat);
  1504 procedure AmmoShoveLine(Ammo: PGear; Damage, Power: LongInt; oX, oY, tX, tY: hwFloat);
  1384 var t: PGearArray;
  1505 var t: PGearArray;
  1385 begin
  1506 begin
  1386     if Ammo^.Kind = gtMinigunBullet then
  1507     t:= CheckAllGearsLineCollision(Ammo, oX, oY, tX, tY);
  1387         t:= CheckAllGearsLineCollision(Ammo, oX, oY, tX, tY)
       
  1388     else
       
  1389         t:= CheckGearsLineCollision(Ammo, oX, oY, tX, tY);
       
  1390     AmmoShoveImpl(Ammo, Damage, Power, t);
  1508     AmmoShoveImpl(Ammo, Damage, Power, t);
  1391 end;
  1509 end;
  1392 
  1510 
  1393 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
  1511 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
  1394 begin
  1512 begin
  1395     AmmoShoveImpl(Ammo, Damage, Power,
  1513     AmmoShoveImpl(Ammo, Damage, Power,
  1396         CheckGearsCollision(Ammo));
  1514         CheckGearsCollision(Ammo));
  1397 end;
  1515 end;
  1398 
  1516 
       
  1517 procedure AmmoShoveCache(Ammo: PGear; Damage, Power: LongInt);
       
  1518 begin
       
  1519     AmmoShoveImpl(Ammo, Damage, Power,
       
  1520         CheckCacheCollision(Ammo));
       
  1521 end;
  1399 
  1522 
  1400 function CountGears(Kind: TGearType): Longword;
  1523 function CountGears(Kind: TGearType): Longword;
  1401 var t: PGear;
  1524 var t: PGear;
  1402     count: Longword = 0;
  1525     count: Longword = 0;
  1403 begin
  1526 begin
  1479 
  1602 
  1480     GearsNear.size:= s;
  1603     GearsNear.size:= s;
  1481     GearsNear.ar:= @GearsNearArray
  1604     GearsNear.ar:= @GearsNearArray
  1482 end;
  1605 end;
  1483 
  1606 
  1484 
  1607 function SpawnBoxOfSmth: PGear;
  1485 procedure SpawnBoxOfSmth;
       
  1486 var t, aTot, uTot, a, h: LongInt;
  1608 var t, aTot, uTot, a, h: LongInt;
  1487     i: TAmmoType;
  1609     i: TAmmoType;
  1488 begin
  1610 begin
  1489 if (PlacingHogs) or
  1611 SpawnBoxOfSmth:= nil;
       
  1612 if (PlacingHogs) or (PlacingKings) or
  1490     (cCaseFactor = 0)
  1613     (cCaseFactor = 0)
  1491     or (CountGears(gtCase) >= 5)
  1614     or (CountGears(gtCase) >= cMaxCaseDrops)
  1492     or (GetRandom(cCaseFactor) <> 0) then
  1615     or (GetRandom(cCaseFactor) <> 0) then
  1493        exit;
  1616        exit;
  1494 
  1617 
  1495 FollowGear:= nil;
  1618 FollowGear:= nil;
  1496 aTot:= 0;
  1619 aTot:= 0;
  1520 
  1643 
  1521 
  1644 
  1522 if t<h then
  1645 if t<h then
  1523     begin
  1646     begin
  1524     FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
  1647     FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
       
  1648     FollowGear^.RenderHealth:= true;
  1525     FollowGear^.Health:= cHealthCaseAmount;
  1649     FollowGear^.Health:= cHealthCaseAmount;
  1526     FollowGear^.Pos:= posCaseHealth;
  1650     FollowGear^.Pos:= posCaseHealth;
  1527     // health crate is smaller than the other crates
  1651     // health crate is smaller than the other crates
  1528     FollowGear^.Radius := cCaseHealthRadius;
  1652     FollowGear^.Radius := cCaseHealthRadius;
  1529     AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
  1653     AddCaption(GetEventString(eidNewHealthPack), capcolDefault, capgrpAmmoInfo);
  1530     end
  1654     end
  1531 else if (t<a+h) then
  1655 else if (t<a+h) then
  1532     begin
  1656     begin
  1533     t:= aTot;
  1657     t:= aTot;
  1534     if (t > 0) then
  1658     if (t > 0) then
  1536         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
  1660         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
  1537         t:= GetRandom(t);
  1661         t:= GetRandom(t);
  1538         i:= Low(TAmmoType);
  1662         i:= Low(TAmmoType);
  1539         FollowGear^.Pos:= posCaseAmmo;
  1663         FollowGear^.Pos:= posCaseAmmo;
  1540         FollowGear^.AmmoType:= i;
  1664         FollowGear^.AmmoType:= i;
  1541         AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo);
  1665         AddCaption(GetEventString(eidNewAmmoPack), capcolDefault, capgrpAmmoInfo);
  1542         end
  1666         end
  1543     end
  1667     end
  1544 else
  1668 else
  1545     begin
  1669     begin
  1546     t:= uTot;
  1670     t:= uTot;
  1549         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
  1673         FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
  1550         t:= GetRandom(t);
  1674         t:= GetRandom(t);
  1551         i:= Low(TAmmoType);
  1675         i:= Low(TAmmoType);
  1552         FollowGear^.Pos:= posCaseUtility;
  1676         FollowGear^.Pos:= posCaseUtility;
  1553         FollowGear^.AmmoType:= i;
  1677         FollowGear^.AmmoType:= i;
  1554         AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo);
  1678         AddCaption(GetEventString(eidNewUtilityPack), capcolDefault, capgrpAmmoInfo);
  1555         end
  1679         end
  1556     end;
  1680     end;
  1557 
  1681 
  1558 // handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities
  1682 // handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities
  1559 if (FollowGear <> nil) then
  1683 if (FollowGear <> nil) then
  1560     begin
  1684     begin
  1561     FindPlace(FollowGear, true, 0, LAND_WIDTH);
  1685     FindPlace(FollowGear, true, 0, LAND_WIDTH);
  1562 
  1686     PlayBoxSpawnTaunt(FollowGear);
  1563     if (FollowGear <> nil) then
  1687     SpawnBoxOfSmth:= FollowGear;
  1564         AddVoice(sndReinforce, CurrentTeam^.voicepack)
       
  1565     end
  1688     end
       
  1689 end;
       
  1690 
       
  1691 procedure PlayBoxSpawnTaunt(Gear: PGear);
       
  1692 const
       
  1693     // Max. distance between hog and crate for sndThisOneIsMine taunt
       
  1694     ThisOneIsMineDistance : LongInt = 130;
       
  1695 var d, minD: LongInt;
       
  1696     gi, closestHog: PGear;
       
  1697 begin
       
  1698     // Taunt
       
  1699     if (Gear <> nil) then
       
  1700         begin
       
  1701         // Look for hog closest to the crate (on the X axis)
       
  1702         gi := GearsList;
       
  1703         minD := LAND_WIDTH + ThisOneIsMineDistance + 1;
       
  1704         closestHog:= nil;
       
  1705         while gi <> nil do
       
  1706             begin
       
  1707             if (gi^.Kind = gtHedgehog) then
       
  1708                 begin
       
  1709                 // Y axis is ignored to simplify calculations
       
  1710                 d := hwRound(hwAbs(gi^.X - Gear^.X));
       
  1711                 if d < minD then
       
  1712                     begin
       
  1713                     minD := d;
       
  1714                     closestHog:= gi;
       
  1715                     end;
       
  1716                 end;
       
  1717             gi := gi^.NextGear;
       
  1718             end;
       
  1719 
       
  1720         // Is closest hog close enough to the crate (on the X axis)?
       
  1721         if (closestHog <> nil) and (closestHog^.Hedgehog <> nil) and (minD <= ThisOneIsMineDistance) then
       
  1722             // If so, there's a chance for a special taunt
       
  1723             if random(3) > 0 then
       
  1724                 AddVoice(sndThisOneIsMine, closestHog^.Hedgehog^.Team^.voicepack)
       
  1725             else
       
  1726                 AddVoice(sndReinforce, CurrentTeam^.voicepack)
       
  1727         else
       
  1728         // Default crate drop taunt
       
  1729             AddVoice(sndReinforce, CurrentTeam^.voicepack);
       
  1730         end;
  1566 end;
  1731 end;
  1567 
  1732 
  1568 
  1733 
  1569 function GetAmmo(Hedgehog: PHedgehog): TAmmoType;
  1734 function GetAmmo(Hedgehog: PHedgehog): TAmmoType;
  1570 var t, aTot: LongInt;
  1735 var t, aTot: LongInt;
  1627 * From the depths (same as from sky, but from sea, with submersible flag set)
  1792 * From the depths (same as from sky, but from sea, with submersible flag set)
  1628 
  1793 
  1629 Trying to make the checks a little broader than on first pass to catch things that don't move normally.
  1794 Trying to make the checks a little broader than on first pass to catch things that don't move normally.
  1630 *)
  1795 *)
  1631 function WorldWrap(var Gear: PGear): boolean;
  1796 function WorldWrap(var Gear: PGear): boolean;
  1632 //var tdx: hwFloat;
  1797 var bounced: boolean;
  1633 begin
  1798 begin
  1634 WorldWrap:= false;
  1799 WorldWrap:= false;
  1635 if WorldEdge = weNone then exit(false);
  1800 if WorldEdge = weNone then exit(false);
  1636 if (hwRound(Gear^.X) < LongInt(leftX)) or
  1801 if (hwRound(Gear^.X) < leftX) or
  1637    (hwRound(Gear^.X) > LongInt(rightX)) then
  1802    (hwRound(Gear^.X) > rightX) then
  1638     begin
  1803     begin
  1639     if WorldEdge = weWrap then
  1804     if WorldEdge = weWrap then
  1640         begin
  1805         begin
  1641         if (hwRound(Gear^.X) < LongInt(leftX)) then
  1806         if (hwRound(Gear^.X) < leftX) then
  1642              Gear^.X:= Gear^.X + int2hwfloat(rightX - leftX)
  1807              Gear^.X:= Gear^.X + int2hwfloat(rightX - leftX)
  1643         else Gear^.X:= Gear^.X - int2hwfloat(rightX - leftX);
  1808         else Gear^.X:= Gear^.X - int2hwfloat(rightX - leftX);
  1644         LeftImpactTimer:= 150;
  1809         LeftImpactTimer:= 150;
  1645         RightImpactTimer:= 150
  1810         RightImpactTimer:= 150;
       
  1811         WorldWrap:= true;
  1646         end
  1812         end
  1647     else if WorldEdge = weBounce then
  1813     else if WorldEdge = weBounce then
  1648         begin
  1814         begin
  1649         if (hwRound(Gear^.X) - Gear^.Radius < LongInt(leftX)) then
  1815         bounced:= false;
       
  1816         // Bounce left
       
  1817         if (hwRound(Gear^.X) - Gear^.Radius < leftX) and (((hwSign(Gear^.dX) = -1) and (not isZero(Gear^.dX))) or (Gear^.Kind = gtHedgehog)) then
  1650             begin
  1818             begin
  1651             LeftImpactTimer:= 333;
  1819             LeftImpactTimer:= 333;
       
  1820             // Set X coordinate to bounce edge, unless the gear spawned inside the bounce edge before
       
  1821             if (Gear^.State and gstInBounceEdge) = 0 then
       
  1822                 Gear^.X:= int2hwfloat(leftX + Gear^.Radius);
       
  1823             // Invert horizontal speed
  1652             Gear^.dX.isNegative:= false;
  1824             Gear^.dX.isNegative:= false;
  1653             Gear^.X:= int2hwfloat(LongInt(leftX) + Gear^.Radius)
  1825             bounced:= true;
  1654             end
  1826             end
  1655         else
  1827         // Bounce right
       
  1828         else if (hwRound(Gear^.X) + Gear^.Radius > rightX) and (((hwSign(Gear^.dX) = 1) and (not isZero(Gear^.dX))) or (Gear^.Kind = gtHedgehog)) then
  1656             begin
  1829             begin
  1657             RightImpactTimer:= 333;
  1830             RightImpactTimer:= 333;
       
  1831             // Set X coordinate to bounce edge, unless the gear spawned inside the bounce edge before
       
  1832             if (Gear^.State and gstInBounceEdge) = 0 then
       
  1833                 Gear^.X:= int2hwfloat(rightX - Gear^.Radius);
       
  1834             // Invert horizontal speed
  1658             Gear^.dX.isNegative:= true;
  1835             Gear^.dX.isNegative:= true;
  1659             Gear^.X:= int2hwfloat(rightX-Gear^.Radius)
  1836             bounced:= true;
  1660             end;
  1837             end;
  1661         if (Gear^.Radius > 2) and (Gear^.dX.QWordValue > _0_001.QWordValue) then
  1838         // Clear gstInBounceEdge when gear is no longer inside a bounce edge area
  1662             AddBounceEffectForGear(Gear);
  1839         if ((Gear^.State and gstInBounceEdge) <> 0) and (hwRound(Gear^.X) - Gear^.Radius >= leftX) and (hwRound(Gear^.X) + Gear^.Radius <= rightX) then
  1663         end{
  1840             Gear^.State:= Gear^.State and (not gstInBounceEdge);
  1664     else if WorldEdge = weSea then
  1841         if (bounced) then
  1665         begin
  1842             begin
  1666         if (hwRound(Gear^.Y) > cWaterLine) and (Gear^.State and gstSubmersible <> 0) then
  1843             WorldWrap:= true;
  1667             Gear^.State:= Gear^.State and (not gstSubmersible)
  1844             if (Gear^.dX.QWordValue > _0_001.QWordValue) then
  1668         else
  1845                AddBounceEffectForGear(Gear);
  1669             begin
  1846             end;
  1670             Gear^.State:= Gear^.State or gstSubmersible;
  1847         end
  1671             Gear^.X:= int2hwFloat(PlayWidth)*int2hwFloat(min(max(0,hwRound(Gear^.Y)),PlayHeight))/PlayHeight;
  1848     else
  1672             Gear^.Y:= int2hwFloat(cWaterLine+cVisibleWater+Gear^.Radius*2);
  1849         WorldWrap:= true;
  1673             tdx:= Gear^.dX;
  1850     end;
  1674             Gear^.dX:= -Gear^.dY;
  1851 end;
  1675             Gear^.dY:= tdx;
  1852 
  1676             Gear^.dY.isNegative:= true
  1853 (*
       
  1854 Applies wrap-around logic for the target of homing gears.
       
  1855 
       
  1856 In wrap-around world edge, the shortest way may to the target might
       
  1857 be across the border, so the X value of the target would lead the
       
  1858 gear to the wrong direction across the whole map. This procedure
       
  1859 changes the target X in this case.
       
  1860 This function must be called after the gear passed through
       
  1861 the wrap-around world edge (WorldWrap returned true).
       
  1862 
       
  1863 No-op for other world edges.
       
  1864 
       
  1865 Returns true if target has been changed.
       
  1866 *)
       
  1867 function HomingWrap(var Gear: PGear): boolean;
       
  1868 var dist_center, dist_right, dist_left: hwFloat;
       
  1869 begin
       
  1870     if WorldEdge = weWrap then
       
  1871         begin
       
  1872         HomingWrap:= false;
       
  1873         // We just check the same target 3 times:
       
  1874         // 1) in current section (no change)
       
  1875         // 2) clone in the right section
       
  1876         // 3) clone in the left section
       
  1877         // The gear will go for the target with the shortest distance to the gear.
       
  1878         // For simplicity, we only check distance on the X axis.
       
  1879         dist_center:= hwAbs(Gear^.X - int2hwFloat(Gear^.Target.X));
       
  1880         dist_right:= hwAbs(Gear^.X - int2hwFloat(Gear^.Target.X + (RightX-LeftX)));
       
  1881         dist_left:= hwAbs(Gear^.X - int2hwFloat(Gear^.Target.X - (RightX-LeftX)));
       
  1882         if (dist_left < dist_right) and (dist_left < dist_center) then
       
  1883             begin
       
  1884             dec(Gear^.Target.X, RightX-LeftX);
       
  1885             HomingWrap:= true;
  1677             end
  1886             end
  1678         end};
  1887         else if (dist_right < dist_left) and (dist_right < dist_center) then
  1679 (*
  1888             begin
  1680 * Window in the sky (Gear moved high into the sky, Y is used to determine X) [unfortunately, not a safe thing to do. shame, I thought aerial bombardment would be kinda neat
  1889             inc(Gear^.Target.X, RightX-LeftX);
  1681 This one would be really easy to freeze game unless it was flagged unfortunately.
  1890             HomingWrap:= true;
  1682 
  1891             end;
  1683     else
  1892         end;
  1684         begin
  1893 end;
  1685         Gear^.X:= int2hwFloat(PlayWidth)*int2hwFloat(min(max(0,hwRound(Gear^.Y)),PlayHeight))/PlayHeight;
  1894 
  1686         Gear^.Y:= -_2048-_256-_256;
  1895 // Add an audiovisual bounce effect for gear after it bounced from bouncy material.
  1687         tdx:= Gear^.dX;
  1896 // Graphical effect is based on speed.
  1688         Gear^.dX:= Gear^.dY;
       
  1689         Gear^.dY:= tdx;
       
  1690         Gear^.dY.isNegative:= false
       
  1691         end
       
  1692 *)
       
  1693     WorldWrap:= true
       
  1694     end;
       
  1695 end;
       
  1696 
       
  1697 procedure AddBounceEffectForGear(Gear: PGear);
  1897 procedure AddBounceEffectForGear(Gear: PGear);
       
  1898 begin
       
  1899     AddBounceEffectForGear(Gear, hwFloat2Float(Gear^.Density * hwAbs(Gear^.dY) + hwAbs(Gear^.dX)) / 1.5);
       
  1900 end;
       
  1901 
       
  1902 // Same as above, but can specify the size of bounce image with imageScale manually.
       
  1903 procedure AddBounceEffectForGear(Gear: PGear; imageScale: Single);
  1698 var boing: PVisualGear;
  1904 var boing: PVisualGear;
  1699 begin
  1905 begin
       
  1906     if (Gear^.Density < _0_01) or (Gear^.Radius < 2) then
       
  1907         exit;
  1700     boing:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot, 0, false, 1);
  1908     boing:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot, 0, false, 1);
  1701     if boing <> nil then
  1909     if boing <> nil then
  1702         with boing^ do
  1910         with boing^ do
  1703             begin
  1911             begin
  1704             Angle:= random(360);
  1912             Angle:= random(360);
  1705             dx:= 0;
  1913             dx:= 0;
  1706             dy:= 0;
  1914             dy:= 0;
  1707             FrameTicks:= 200;
  1915             FrameTicks:= 200;
  1708             Scale:= hwFloat2Float(Gear^.Density * hwAbs(Gear^.dY) + hwAbs(Gear^.dX)) / 1.5;
  1916             Scale:= imageScale;
  1709             State:= ord(sprBoing)
  1917             State:= ord(sprBoing)
  1710             end;
  1918             end;
  1711     if Gear^.Kind = gtDuck then
  1919     PlaySound(sndMelonImpact, true)
  1712         PlaySound(sndDuckDrop, true)
  1920 end;
       
  1921 
       
  1922 function IsHogFacingLeft(Gear: PGear): boolean;
       
  1923 var sign: LongInt;
       
  1924 begin
       
  1925     sign:= hwSign(Gear^.dX);
       
  1926     if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtParachute) then
       
  1927         IsHogFacingLeft:= CurAmmoGear^.Tag = -1
       
  1928     else if ((Gear^.State and gstHHHJump) <> 0) and (Gear^.Hedgehog^.Effects[heArtillery] = 0) then
       
  1929         IsHogFacingLeft:= sign > 0
  1713     else
  1930     else
  1714         PlaySound(sndMelonImpact, true)
  1931         IsHogFacingLeft:= sign < 0;
  1715 end;
  1932 end;
  1716 
  1933 
  1717 function IsHogLocal(HH: PHedgehog): boolean;
  1934 function IsHogLocal(HH: PHedgehog): boolean;
  1718 begin
  1935 begin
  1719     IsHogLocal:= (not (HH^.Team^.ExtDriven or (HH^.BotLevel > 0))) or (HH^.Team^.Clan^.ClanIndex = LocalClan) or (GameType = gmtDemo);
  1936     IsHogLocal:= (not (HH^.Team^.ExtDriven or (HH^.BotLevel > 0))) or (HH^.Team^.Clan^.LocalOrAlly) or (GameType = gmtDemo);
  1720 end;
  1937 end;
  1721 
  1938 
  1722 end.
  1939 end.