--- a/hedgewars/uGearsUtils.pas Tue Nov 10 18:16:35 2015 +0100
+++ b/hedgewars/uGearsUtils.pas Tue Nov 10 20:43:13 2015 +0100
@@ -1,6 +1,6 @@
(*
* Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2013 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,7 +13,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*)
{$INCLUDE "options.inc"}
@@ -23,7 +23,9 @@
uses uTypes, uFloat;
procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline;
-procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
+procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
+procedure AddSplashForGear(Gear: PGear; justSkipping: boolean);
+procedure AddBounceEffectForGear(Gear: PGear);
function ModifyDamage(dmg: Longword; Gear: PGear): Longword;
procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
@@ -47,8 +49,8 @@
procedure ShotgunShot(Gear: PGear);
procedure SetAllToActive;
-procedure SetAllHHToActive; inline;
procedure SetAllHHToActive(Ice: boolean);
+procedure SetAllHHToActive(); inline;
function GetAmmo(Hedgehog: PHedgehog): TAmmoType;
function GetUtility(Hedgehog: PHedgehog): TAmmoType;
@@ -64,7 +66,7 @@
implementation
uses uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc,
uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore,
- uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug,
+ uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug,
uGearsList, Math, uVisualGearsList, uGearsHandlersMess,
uGearsHedgehog;
@@ -79,6 +81,8 @@
fX, fY, tdX, tdY: hwFloat;
vg: PVisualGear;
i, cnt: LongInt;
+ wrap: boolean;
+ bubble: PVisualGear;
begin
if Radius > 4 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')');
if Radius > 25 then KickFlakes(Radius, X, Y);
@@ -86,7 +90,17 @@
if ((Mask and EXPLNoGfx) = 0) then
begin
vg:= nil;
- if Radius > 50 then vg:= AddVisualGear(X, Y, vgtBigExplosion)
+ if CheckCoordInWater(X, Y - Radius) then
+ begin
+ cnt:= 2 * Radius;
+ for i:= (Radius * Radius) div 4 downto 0 do
+ begin
+ bubble := AddVisualGear(X - Radius + random(cnt), Y - Radius + random(cnt), vgtBubble);
+ if bubble <> nil then
+ bubble^.dY:= 0.1 + random(20)/10;
+ end
+ end
+ else if Radius > 50 then vg:= AddVisualGear(X, Y, vgtBigExplosion)
else if Radius > 10 then vg:= AddVisualGear(X, Y, vgtExplosion);
if vg <> nil then
vg^.Tint:= Tint;
@@ -99,9 +113,15 @@
dmgRadius:= Radius;
dmgBase:= dmgRadius + cHHRadius div 2;*)
dmgBase:= Radius shl 1 + cHHRadius div 2;
+
+// we might have to run twice if weWrap is enabled
+wrap:= false;
+repeat
+
fX:= int2hwFloat(X);
fY:= int2hwFloat(Y);
Gear:= GearsList;
+
while Gear <> nil do
begin
dmg:= 0;
@@ -118,6 +138,7 @@
gtClusterBomb,
// gtCluster, too game breaking I think
gtSMine,
+ gtAirMine,
gtCase,
gtTarget,
gtFlame,
@@ -135,7 +156,7 @@
//AddFileLog('Damage: ' + inttostr(dmg));
if (Mask and EXPLNoDamage) = 0 then
begin
- if Gear^.Hedgehog^.Effects[heInvulnerable] = 0 then
+ if (Gear^.Kind <> gtHedgehog) or (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) then
ApplyDamage(Gear, AttackingHog, dmg, dsExplosion)
else
Gear^.State:= Gear^.State or gstWinner;
@@ -148,29 +169,30 @@
Gear^.State:= (Gear^.State or gstMoving) and (not gstLoser);
if Gear^.Kind = gtKnife then Gear^.State:= Gear^.State and (not gstCollision);
- if Gear^.Hedgehog^.Effects[heInvulnerable] = 0 then
+ if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) then
Gear^.State:= (Gear^.State or gstMoving) and (not gstWinner);
Gear^.Active:= true;
if Gear^.Kind <> gtFlame then FollowGear:= Gear
end;
if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) and (Gear^.State and gstHHDeath = 0) then
- Gear^.Hedgehog^.Effects[hePoisoned] := 1;
+ Gear^.Hedgehog^.Effects[hePoisoned] := 5;
end;
end;
- gtGrave: begin
+ gtGrave: if Mask and EXPLDoNotTouchAny = 0 then
// Run the calcs only once we know we have a type that will need damage
- tdX:= Gear^.X-fX;
- tdY:= Gear^.Y-fY;
- if LongInt(tdX.Round + tdY.Round + 2) < dmgBase then
- dmg:= dmgBase - hwRound(Distance(tdX, tdY));
- if dmg > 1 then
begin
- dmg:= ModifyDamage(min(dmg div 2, Radius), Gear);
- Gear^.dY:= - _0_004 * dmg;
- Gear^.Active:= true
- end
- end;
+ tdX:= Gear^.X-fX;
+ tdY:= Gear^.Y-fY;
+ if LongInt(tdX.Round + tdY.Round + 2) < dmgBase then
+ dmg:= dmgBase - hwRound(Distance(tdX, tdY));
+ if dmg > 1 then
+ begin
+ dmg:= ModifyDamage(min(dmg div 2, Radius), Gear);
+ Gear^.dY:= - _0_004 * dmg;
+ Gear^.Active:= true
+ end
+ end;
end;
end;
Gear:= Gear^.NextGear
@@ -185,6 +207,27 @@
AddVisualGear(X, Y, vgtChunk)
end;
+if (WorldEdge = weWrap) then
+ begin
+ // already wrapped? let's not wrap again!
+ if wrap then
+ break;
+
+ // Radius + 5 because that's the actual radius the explosion changes graphically
+ if X + (Radius + 5) > LongInt(rightX) then
+ begin
+ dec(X, playWidth);
+ wrap:= true;
+ end
+ else if X - (Radius + 5) < LongInt(leftX) then
+ begin
+ inc(X, playWidth);
+ wrap:= true;
+ end;
+ end;
+
+until (not wrap);
+
uAIMisc.AwareOfExplosion(0, 0, 0)
end;
@@ -198,10 +241,11 @@
i:= _1;
if (CurrentHedgehog <> nil) and CurrentHedgehog^.King then
i:= _1_5;
-if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then
+if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog <> nil) and
+ (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then
ModifyDamage:= hwRound(cDamageModifier * dmg * i * cDamagePercent * _0_5 * _0_01)
else
- ModifyDamage:= hwRound(cDamageModifier * dmg * i * cDamagePercent * _0_01)
+ ModifyDamage:= hwRound(cDamageModifier * dmg * i * cDamagePercent * _0_01);
end;
procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
@@ -232,7 +276,7 @@
inc(CurrentHedgehog^.Gear^.Health,vampDmg);
str(vampDmg, s);
s:= '+' + s;
- AddCaption(s, CurrentHedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
+ AddCaption(ansistring(s), CurrentHedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
RenderHealth(CurrentHedgehog^);
RecountTeamHealth(CurrentHedgehog^.Team);
i:= 0;
@@ -249,20 +293,41 @@
end;
end
end;
- if (GameFlags and gfKarma <> 0) and (GameFlags and gfInvulnerable = 0) and
+ if (GameFlags and gfKarma <> 0) and (GameFlags and gfInvulnerable = 0) and
(CurrentHedgehog^.Effects[heInvulnerable] = 0) then
begin // this cannot just use Damage or it interrupts shotgun and gets you called stupid
inc(CurrentHedgehog^.Gear^.Karma, tmpDmg);
CurrentHedgehog^.Gear^.LastDamage := CurrentHedgehog;
spawnHealthTagForHH(CurrentHedgehog^.Gear, tmpDmg);
end;
- uStats.HedgehogDamaged(Gear, AttackerHog, Damage, false);
+ uStats.HedgehogDamaged(Gear, AttackerHog, Damage, false);
end;
+
+ if AprilOne and (Gear^.Hedgehog^.Hat = 'fr_tomato') and (Damage > 2) then
+ for i := 0 to random(min(Damage,20))+5 do
+ begin
+ vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
+ if vg <> nil then
+ with vg^ do
+ begin
+ dx:= 0.001 * (random(100)+10);
+ dy:= 0.001 * (random(100)+10);
+ tdy:= -cGravityf;
+ if random(2) = 0 then
+ dx := -dx;
+ //if random(2) = 0 then
+ // dy := -dy;
+ FrameTicks:= random(500) + 1000;
+ State:= ord(sprBubbles);
+ //Tint:= $bd2f03ff
+ Tint:= $ff0000ff
+ end
+ end
end else
//else if Gear^.Kind <> gtStructure then // not gtHedgehog nor gtStructure
Gear^.Hedgehog:= AttackerHog;
inc(Gear^.Damage, Damage);
-
+
ScriptCall('onGearDamage', Gear^.UID, Damage);
end;
@@ -275,10 +340,11 @@
AllInactive:= false;
HHGear^.Active:= true;
end;
-
+
procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
begin
if Hedgehog^.Effects[heFrozen] <> 0 then exit;
+
if (Source = dsFall) or (Source = dsExplosion) then
case random(3) of
0: PlaySoundV(sndOoff1, Hedgehog^.Team^.voicepack);
@@ -300,8 +366,8 @@
end;
procedure CheckHHDamage(Gear: PGear);
-var
- dmg: Longword;
+var
+ dmg: LongInt;
i: LongWord;
particle: PVisualGear;
begin
@@ -314,7 +380,7 @@
if dmg < 1 then
exit;
- for i:= min(12, (3 + dmg div 10)) downto 0 do
+ for i:= min(12, 3 + dmg div 10) downto 0 do
begin
particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
if particle <> nil then
@@ -338,7 +404,7 @@
procedure CalcRotationDirAngle(Gear: PGear);
-var
+var
dAngle: real;
begin
// Frac/Round to be kind to JS as of 2012-08-27 where there is yet no int64/uint64
@@ -355,18 +421,182 @@
Gear^.DirAngle := Gear^.DirAngle - 360
end;
+procedure AddSplashForGear(Gear: PGear; justSkipping: boolean);
+var x, y, i, distL, distR, distB, minDist, maxDrops: LongInt;
+ splash, particle: PVisualGear;
+ speed, hwTmp: hwFloat;
+ vi, vs, tmp: real; // impact speed and sideways speed
+ isImpactH, isImpactRight: boolean;
+const dist2surf = 4;
+begin
+x:= hwRound(Gear^.X);
+y:= hwRound(Gear^.Y);
+
+// find position for splash and impact speed
+
+distB:= cWaterline - y;
+
+if WorldEdge <> weSea then
+ minDist:= distB
+else
+ begin
+ distL:= x - leftX;
+ distR:= rightX - x;
+ minDist:= min(distB, min(distL, distR));
+ end;
+
+isImpactH:= (minDist <> distB);
+
+if not isImpactH then
+ begin
+ y:= cWaterline - dist2surf;
+ speed:= hwAbs(Gear^.dY);
+ end
+else
+ begin
+ isImpactRight := minDist = distR;
+ if isImpactRight then
+ x:= rightX - dist2surf
+ else
+ x:= leftX + dist2surf;
+ speed:= hwAbs(Gear^.dX);
+ end;
+
+// splash sound
+
+if justSkipping then
+ PlaySound(sndSkip)
+else
+ begin
+ // adjust water impact sound based on gear speed and density
+ hwTmp:= hwAbs(Gear^.Density * speed);
+
+ if hwTmp > _1 then
+ PlaySound(sndSplash)
+ else if hwTmp > _0_5 then
+ PlaySound(sndSkip)
+ else
+ PlaySound(sndDroplet2);
+ end;
+
+
+// splash visuals
+
+if ((cReducedQuality and rqPlainSplash) <> 0) then
+ exit;
+
+splash:= AddVisualGear(x, y, vgtSplash);
+if splash = nil then
+ exit;
+
+if not isImpactH then
+ vs:= abs(hwFloat2Float(Gear^.dX))
+else
+ begin
+ if isImpactRight then
+ splash^.Angle:= -90
+ else
+ splash^.Angle:= 90;
+ vs:= abs(hwFloat2Float(Gear^.dY));
+ end;
+
+
+vi:= hwFloat2Float(speed);
+
+with splash^ do
+ begin
+ Scale:= abs(hwFloat2Float(Gear^.Density / _3 * speed));
+ if Scale > 1 then Scale:= power(Scale,0.3333)
+ else Scale:= Scale + ((1-Scale) / 2);
+ if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
+ else Timer:= 1;
+ // Low Gravity
+ FrameTicks:= FrameTicks*Timer;
+ end;
+
+
+// eject water drops
+
+maxDrops := (hwRound(Gear^.Density) * 3) div 2 + round((vi + vs) * hwRound(Gear^.Density) * 6);
+for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do
+ begin
+ if isImpactH then
+ particle := AddVisualGear(x, y - 3 + Random(7), vgtDroplet)
+ else
+ particle := AddVisualGear(x - 3 + Random(7), y, vgtDroplet);
+
+ if particle <> nil then
+ with particle^ do
+ begin
+ // dX and dY were initialized to have a random value on creation (see uVisualGearsList)
+ if isImpactH then
+ begin
+ tmp:= dX;
+ if isImpactRight then
+ dX:= dY - vi / 5
+ else
+ dX:= -dy + vi / 5;
+ dY:= tmp * (1 + vs / 10);
+ end
+ else
+ begin
+ dX:= dX * (1 + vs / 10);
+ dY:= dY - vi / 5;
+ end;
+
+ if splash <> nil then
+ begin
+ if splash^.Scale > 1 then
+ begin
+ dX:= dX * power(splash^.Scale, 0.3333); // tone down the droplet height further
+ dY:= dY * power(splash^.Scale, 0.3333);
+ end
+ else
+ begin
+ dX:= dX * splash^.Scale;
+ dY:= dY * splash^.Scale;
+ end;
+ end;
+ end
+ end;
+
+end;
+
+procedure DrownGear(Gear: PGear);
+begin
+Gear^.doStep := @doStepDrowningGear;
+
+Gear^.Timer := 5000; // how long game should wait
+end;
+
function CheckGearDrowning(var Gear: PGear): boolean;
-var
+var
skipSpeed, skipAngle, skipDecay: hwFloat;
- i, maxDrops, X, Y: LongInt;
- vdX, vdY: real;
- particle, splash: PVisualGear;
- isSubmersible: boolean;
+ tmp, X, Y, dist2Water: LongInt;
+ isSubmersible, isDirH, isImpact, isSkip: boolean;
+ s: ansistring;
begin
// probably needs tweaking. might need to be in a case statement based upon gear type
+ X:= hwRound(Gear^.X);
Y:= hwRound(Gear^.Y);
- if cWaterLine < Y + Gear^.Radius then
+
+ dist2Water:= cWaterLine - (Y + Gear^.Radius);
+ isDirH:= false;
+
+ if WorldEdge = weSea then
begin
+ tmp:= dist2Water;
+ dist2Water:= min(dist2Water, min(X - Gear^.Radius - LongInt(leftX), LongInt(rightX) - (X + Gear^.Radius)));
+ // if water on sides is closer than on bottom -> horizontal direction
+ isDirH:= tmp <> dist2Water;
+ end;
+
+ isImpact:= false;
+
+ if dist2Water < 0 then
+ begin
+ // invisible gears will just be deleted
+ // unless they are generic fallers, then they will be "respawned"
if Gear^.State and gstInvisible <> 0 then
begin
if Gear^.Kind = gtGenericFaller then
@@ -377,27 +607,41 @@
Gear^.dY:= _90-(GetRandomf*_360)
end
else DeleteGear(Gear);
- exit
+ exit(true)
end;
isSubmersible:= ((Gear = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.State and gstSubmersible <> 0)) or (Gear^.State and gstSubmersible <> 0);
+
skipSpeed := _0_25;
skipAngle := _1_9;
skipDecay := _0_87;
- X:= hwRound(Gear^.X);
- vdX:= hwFloat2Float(Gear^.dX);
- vdY:= hwFloat2Float(Gear^.dY);
- // this could perhaps be a tiny bit higher.
- if (cWaterLine + 64 + Gear^.Radius > Y) and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed)
- and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)) then
+
+
+ // skipping
+
+ if (not isSubmersible) and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed)
+ and ( ((not isDirH) and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)))
+ or (isDirH and (hwAbs(Gear^.dY) > skipAngle * hwAbs(Gear^.dX))) ) then
begin
- Gear^.dY.isNegative := true;
+ isSkip:= true;
+ // if skipping we move the gear out of water
+ if isDirH then
+ begin
+ Gear^.dX.isNegative := (not Gear^.dX.isNegative);
+ Gear^.X:= Gear^.X + Gear^.dX;
+ end
+ else
+ begin
+ Gear^.dY.isNegative := (not Gear^.dY.isNegative);
+ Gear^.Y:= Gear^.Y + Gear^.dY;
+ end;
Gear^.dY := Gear^.dY * skipDecay;
Gear^.dX := Gear^.dX * skipDecay;
CheckGearDrowning := false;
- PlaySound(sndSkip)
end
- else
+ else // not skipping
begin
+ isImpact:= true;
+ isSkip:= false;
if not isSubmersible then
begin
CheckGearDrowning := true;
@@ -411,85 +655,66 @@
begin
// Gear could become nil after this, just exit to skip splashes
ResurrectHedgehog(Gear);
- exit
+ exit(true)
end
else
begin
- Gear^.doStep := @doStepDrowningGear;
+ DrownGear(Gear);
Gear^.State := Gear^.State and (not gstHHDriven);
- AddCaption(Format(GetEventString(eidDrowned), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
+ s:= ansistring(Gear^.Hedgehog^.Name);
+ AddCaption(FormatA(GetEventString(eidDrowned), s), cWhiteColor, capgrpMessage);
end
end
else
- Gear^.doStep := @doStepDrowningGear;
- if Gear^.Kind = gtFlake then
- exit // skip splashes
+ DrownGear(Gear);
+ if Gear^.Kind = gtFlake then
+ exit(true); // skip splashes
end
- else if (Y > cWaterLine + cVisibleWater*4) and
- ((Gear <> CurrentHedgehog^.Gear) or (CurAmmoGear = nil) or (CurAmmoGear^.State and gstSubmersible = 0)) then
- Gear^.doStep:= @doStepDrowningGear;
- if ((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius))
- or (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and (Gear = CurAmmoGear) and ((CurAmmoGear^.Pos = 0)
- and (CurAmmoGear^.dY < _0_01))) then
- if Gear^.Density * Gear^.dY > _1 then
- PlaySound(sndSplash)
- else if Gear^.Density * Gear^.dY > _0_5 then
- PlaySound(sndSkip)
- else
- PlaySound(sndDroplet2);
- end;
-
- if ((cReducedQuality and rqPlainSplash) = 0)
- and (((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius))
- or (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and (Gear = CurAmmoGear) and ((CurAmmoGear^.Pos = 0)
- and (CurAmmoGear^.dY < _0_01)))) then
- begin
- splash:= AddVisualGear(X, cWaterLine, vgtSplash);
- if splash <> nil then
- with splash^ do
+ else // submersible
begin
- Scale:= hwFloat2Float(Gear^.Density / _3 * Gear^.dY);
- if Scale > 1 then Scale:= power(Scale,0.3333)
- else Scale:= Scale + ((1-Scale) / 2);
- if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
- else Timer:= 1;
- // Low Gravity
- FrameTicks:= FrameTicks*Timer;
- end;
+ // drown submersible grears if far below map
+ if (Y > cWaterLine + cVisibleWater*4) then
+ begin
+ DrownGear(Gear);
+ exit(true); // no splashes needed
+ end;
+
+ CheckGearDrowning := false;
- maxDrops := (hwRound(Gear^.Density) * 3) div 2 + round(vdX * hwRound(Gear^.Density) * 6) + round(vdY * hwRound(Gear^.Density) * 6);
- for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do
- begin
- particle := AddVisualGear(X - 3 + Random(7), cWaterLine, vgtDroplet);
- if particle <> nil then
- with particle^ do
+ // check if surface was penetrated
+
+ // no penetration if center's water distance not smaller than radius
+ if abs(dist2Water + Gear^.Radius) >= Gear^.Radius then
+ isImpact:= false
+ else
+ begin
+ // get distance to water of last tick
+ if isDirH then
begin
- dX := dX - vdX / 10;
- dY := dY - vdY / 5;
- if splash <> nil then
- begin
- if splash^.Scale > 1 then
- begin
- dX:= dX * power(splash^.Scale,0.3333); // tone down the droplet height further
- dY:= dY * power(splash^.Scale, 0.3333)
- end
- else
- begin
- dX:= dX * splash^.Scale;
- dY:= dY * splash^.Scale
- end
- end
+ tmp:= hwRound(Gear^.X - Gear^.dX);
+ tmp:= abs(min(tmp - leftX, rightX - tmp));
end
- end
- end;
- if isSubmersible and (Gear = CurAmmoGear) and (CurAmmoGear^.Pos = 0) then
- CurAmmoGear^.Pos := 1000
+ else
+ begin
+ tmp:= hwRound(Gear^.Y - Gear^.dY);
+ tmp:= abs(cWaterLine - tmp);
+ end;
+
+ // there was an impact if distance was >= radius
+ isImpact:= (tmp >= Gear^.Radius)
+ end;
+ end; // end of submersible
+ end; // end of not skipping
+
+ // splash sound animation and droplets
+ if isImpact or isSkip then
+ addSplashForGear(Gear, isSkip);
+
+ if isSkip then
+ ScriptCall('onGearWaterSkip', Gear^.uid);
end
else
- begin
- if not (Gear^.Kind in [gtJetpack, gtBee]) then Gear^.State:= Gear^.State and not gstSubmersible; // making it temporary for most gears is more attractive I think
CheckGearDrowning := false
- end
end;
@@ -510,11 +735,11 @@
gear^.Hedgehog^.Effects[hePoisoned] := 0;
if (CurrentHedgehog^.Effects[heResurrectable] = 0) or ((CurrentHedgehog^.Effects[heResurrectable] <> 0)
and (Gear^.Hedgehog^.Team^.Clan <> CurrentHedgehog^.Team^.Clan)) then
- with CurrentHedgehog^ do
+ with CurrentHedgehog^ do
begin
inc(Team^.stats.AIKills);
- FreeTexture(Team^.AIKillsTex);
- Team^.AIKillsTex := RenderStringTex(inttostr(Team^.stats.AIKills), Team^.Clan^.Color, fnt16);
+ FreeAndNilTexture(Team^.AIKillsTex);
+ Team^.AIKillsTex := RenderStringTex(ansistring(inttostr(Team^.stats.AIKills)), Team^.Clan^.Color, fnt16);
end;
tempTeam := gear^.Hedgehog^.Team;
DeleteCI(gear);
@@ -527,7 +752,7 @@
sparkles^.Tint:= tempTeam^.Clan^.Color shl 8 or $FF;
//sparkles^.Angle:= random(360);
end;
- FindPlace(gear, false, 0, LAND_WIDTH, true);
+ FindPlace(gear, false, 0, LAND_WIDTH, true);
if gear <> nil then
begin
AddVisualGear(hwRound(gear^.X), hwRound(gear^.Y), vgtExplosion);
@@ -544,7 +769,7 @@
count: LongInt = 0;
begin
if (y and LAND_HEIGHT_MASK) = 0 then
- for i:= max(x - r, 0) to min(x + r, LAND_WIDTH - 4) do
+ for i:= max(x - r, 0) to min(x + r, LAND_WIDTH - 1) do
if Land[y, i] and mask <> 0 then
begin
inc(count);
@@ -557,6 +782,28 @@
CountNonZeroz:= count;
end;
+function isSteadyPosition(x, y, r, c: LongInt; mask: Longword): boolean;
+var cnt, i: LongInt;
+begin
+ cnt:= 0;
+ isSteadyPosition:= false;
+
+ if ((y and LAND_HEIGHT_MASK) = 0) and (x - r >= 0) and (x + r < LAND_WIDTH) then
+ begin
+ for i:= r - c + 2 to r do
+ begin
+ if (Land[y, x - i] and mask <> 0) then inc(cnt);
+ if (Land[y, x + i] and mask <> 0) then inc(cnt);
+
+ if cnt >= c then
+ begin
+ isSteadyPosition:= true;
+ exit
+ end;
+ end;
+ end;
+end;
+
function NoGearsToAvoid(mX, mY: LongInt; rX, rY: LongInt): boolean;
var t: PGear;
@@ -582,9 +829,10 @@
procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
var x: LongInt;
- y, sy: LongInt;
+ y, sy, dir: LongInt;
ar: array[0..1023] of TPoint;
ar2: array[0..2047] of TPoint;
+ temp: TPoint;
cnt, cnt2: Longword;
delta: LongInt;
ignoreNearObjects, ignoreOverlap, tryAgain: boolean;
@@ -592,9 +840,9 @@
ignoreNearObjects:= false; // try not skipping proximity at first
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
tryAgain:= true;
-if WorldEdge <> weNone then
+if WorldEdge <> weNone then
begin
- Left:= max(Left,leftX+Gear^.Radius);
+ Left:= max(Left, LongInt(leftX) + Gear^.Radius);
Right:= min(Right,rightX-Gear^.Radius)
end;
while tryAgain do
@@ -602,17 +850,18 @@
delta:= LAND_WIDTH div 16;
cnt2:= 0;
repeat
- x:= Left + max(LAND_WIDTH div 2048, LongInt(GetRandom(Delta)));
+ if GetRandom(2) = 0 then dir:= -1 else dir:= 1;
+ x:= max(LAND_WIDTH div 2048, LongInt(GetRandom(Delta)));
+ if dir = 1 then x:= Left + x else x:= Right - x;
repeat
- inc(x, Delta);
cnt:= 0;
- y:= min(1024, topY) - 2 * Gear^.Radius;
+ y:= min(1024, topY) - Gear^.Radius shl 1;
while y < cWaterLine do
begin
repeat
inc(y, 2);
until (y >= cWaterLine) or
- (not ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) = 0)) or
+ ((not ignoreOverlap) and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) = 0)) or
(ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, lfLandMask) = 0));
sy:= y;
@@ -620,19 +869,19 @@
repeat
inc(y);
until (y >= cWaterLine) or
- (not ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) <> 0)) or
- (ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, lfLandMask) <> 0));
+ ((not ignoreOverlap) and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) <> 0)) or
+ (ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, lfLandMask) <> 0));
- if (y - sy > Gear^.Radius * 2)
+ if (y - sy > Gear^.Radius * 2) and (y < cWaterLine)
and (((Gear^.Kind = gtExplosives)
- and (y < cWaterLine)
- and (ignoreNearObjects or NoGearsToAvoid(x, y - Gear^.Radius, 60, 60))
- and (CountNonZeroz(x, y+1, Gear^.Radius - 1, Gear^.Radius+1, $FFFF) > Gear^.Radius))
- or
- ((Gear^.Kind <> gtExplosives)
- and (y < cWaterLine)
- and (ignoreNearObjects or NoGearsToAvoid(x, y - Gear^.Radius, 110, 110))
- )) then
+ and (ignoreNearObjects or NoGearsToAvoid(x, y - Gear^.Radius, 60, 60))
+ and (isSteadyPosition(x, y+1, Gear^.Radius - 1, 3, $FFFF)
+ or (CountNonZeroz(x, y+1, Gear^.Radius - 1, Gear^.Radius+1, $FFFF) > Gear^.Radius)
+ ))
+ or
+ ((Gear^.Kind <> gtExplosives)
+ and (ignoreNearObjects or NoGearsToAvoid(x, y - Gear^.Radius, 110, 110))
+ )) then
begin
ar[cnt].X:= x;
if withFall then
@@ -646,13 +895,17 @@
end;
if cnt > 0 then
- with ar[GetRandom(cnt)] do
+ begin
+ temp := ar[GetRandom(cnt)];
+ with temp do
begin
ar2[cnt2].x:= x;
ar2[cnt2].y:= y;
inc(cnt2)
- end
- until (x + Delta > Right);
+ end;
+ end;
+ inc(x, Delta*dir)
+ until ((dir = 1) and (x > Right)) or ((dir = -1) and (x < Left));
dec(Delta, 60)
until (cnt2 > 0) or (Delta < 70);
@@ -665,12 +918,15 @@
end;
if cnt2 > 0 then
- with ar2[GetRandom(cnt2)] do
+ begin
+ temp := ar2[GetRandom(cnt2)];
+ with temp do
begin
Gear^.X:= int2hwFloat(x);
Gear^.Y:= int2hwFloat(y);
AddFileLog('Assigned Gear coordinates (' + inttostr(x) + ',' + inttostr(y) + ')');
end
+ end
else
begin
OutError('Can''t find place for Gear', false);
@@ -716,7 +972,7 @@
if (TestCollisionX(Gear, hwSign(Gear^.dX)) <> 0)
or (TestCollisionY(Gear, hwSign(Gear^.dY)) <> 0) then
Gear^.State := Gear^.State or gstCollision
- else
+ else
Gear^.State := Gear^.State and (not gstCollision)
end;
@@ -822,7 +1078,7 @@
end;
if dmg > 0 then
begin
- if t^.Hedgehog^.Effects[heInvulnerable] = 0 then
+ if (t^.Kind <> gtHedgehog) or (t^.Hedgehog^.Effects[heInvulnerable] = 0) then
ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet)
else
Gear^.State:= Gear^.State or gstWinner;
@@ -885,14 +1141,14 @@
begin
dec(i);
Gear:= t^.ar[i];
- if ((Ammo^.Kind = gtFlame) or (Ammo^.Kind = gtBlowTorch)) and
+ if ((Ammo^.Kind = gtFlame) or (Ammo^.Kind = gtBlowTorch)) and
(Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heFrozen] > 255) then
Gear^.Hedgehog^.Effects[heFrozen]:= max(255,Gear^.Hedgehog^.Effects[heFrozen]-10000);
tmpDmg:= ModifyDamage(Damage, Gear);
if (Gear^.State and gstNoDamage) = 0 then
begin
- if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then
+ if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then
begin
VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit);
if VGear <> nil then
@@ -918,7 +1174,7 @@
Ammo^.Timer:= 0;
exit;
end;
- if Gear^.Hedgehog^.Effects[heInvulnerable] = 0 then
+ if (Gear^.Kind <> gtHedgehog) or (Gear^.Hedgehog^.Effects[heInvulnerable] = 0) then
begin
if (Ammo^.Kind = gtKnife) and (tmpDmg > 0) then
for j:= 1 to max(1,min(3,tmpDmg div 5)) do
@@ -943,7 +1199,7 @@
end
else
Gear^.State:= Gear^.State or gstWinner;
- if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then
+ if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then
begin
if (Ammo^.Hedgehog^.Gear <> nil) then
Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable);
@@ -1054,9 +1310,9 @@
s:= 0;
SetLength(GearsNearArray, s);
t := GearsList;
- while t <> nil do
+ while t <> nil do
begin
- if (t^.Kind = Kind)
+ if (t^.Kind = Kind)
and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then
begin
inc(s);
@@ -1113,6 +1369,8 @@
FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
FollowGear^.Health:= cHealthCaseAmount;
FollowGear^.Pos:= posCaseHealth;
+ // health crate is smaller than the other crates
+ FollowGear^.Radius := cCaseHealthRadius;
AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo);
end
else if (t<a+h) then
@@ -1216,42 +1474,42 @@
Trying to make the checks a little broader than on first pass to catch things that don't move normally.
*)
function WorldWrap(var Gear: PGear): boolean;
-var tdx: hwFloat;
+//var tdx: hwFloat;
begin
WorldWrap:= false;
if WorldEdge = weNone then exit(false);
-if (hwRound(Gear^.X)-Gear^.Radius < leftX) or
- (hwRound(Gear^.X)+Gear^.Radius > rightX) then
+if (hwRound(Gear^.X) < LongInt(leftX)) or
+ (hwRound(Gear^.X) > LongInt(rightX)) then
begin
if WorldEdge = weWrap then
begin
- if (hwRound(Gear^.X)-Gear^.Radius < leftX) then
- Gear^.X:= int2hwfloat(rightX-Gear^.Radius)
- else Gear^.X:= int2hwfloat(leftX+Gear^.Radius);
+ if (hwRound(Gear^.X) < LongInt(leftX)) then
+ Gear^.X:= Gear^.X + int2hwfloat(rightX - leftX)
+ else Gear^.X:= Gear^.X - int2hwfloat(rightX - leftX);
LeftImpactTimer:= 150;
RightImpactTimer:= 150
end
else if WorldEdge = weBounce then
begin
- if (hwRound(Gear^.X)-Gear^.Radius < leftX) then
+ if (hwRound(Gear^.X) - Gear^.Radius < LongInt(leftX)) then
begin
LeftImpactTimer:= 333;
Gear^.dX.isNegative:= false;
- Gear^.X:= int2hwfloat(leftX+Gear^.Radius)
+ Gear^.X:= int2hwfloat(LongInt(leftX) + Gear^.Radius)
end
- else
+ else
begin
RightImpactTimer:= 333;
Gear^.dX.isNegative:= true;
Gear^.X:= int2hwfloat(rightX-Gear^.Radius)
end;
if (Gear^.Radius > 2) and (Gear^.dX.QWordValue > _0_001.QWordValue) then
- PlaySound(sndMelonImpact)
- end
+ AddBounceEffectForGear(Gear);
+ end{
else if WorldEdge = weSea then
begin
if (hwRound(Gear^.Y) > cWaterLine) and (Gear^.State and gstSubmersible <> 0) then
- Gear^.State:= Gear^.State and not gstSubmersible
+ Gear^.State:= Gear^.State and (not gstSubmersible)
else
begin
Gear^.State:= Gear^.State or gstSubmersible;
@@ -1262,12 +1520,12 @@
Gear^.dY:= tdx;
Gear^.dY.isNegative:= true
end
- end;
+ end};
(*
* 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
This one would be really easy to freeze game unless it was flagged unfortunately.
- else
+ else
begin
Gear^.X:= int2hwFloat(PlayWidth)*int2hwFloat(min(max(0,hwRound(Gear^.Y)),PlayHeight))/PlayHeight;
Gear^.Y:= -_2048-_256-_256;
@@ -1281,4 +1539,21 @@
end;
end;
+procedure AddBounceEffectForGear(Gear: PGear);
+var boing: PVisualGear;
+begin
+ boing:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot, 0, false, 1);
+ if boing <> nil then
+ with boing^ do
+ begin
+ Angle:= random(360);
+ dx:= 0;
+ dy:= 0;
+ FrameTicks:= 200;
+ Scale:= hwFloat2Float(Gear^.Density * hwAbs(Gear^.dY) + hwAbs(Gear^.dX)) / 1.5;
+ State:= ord(sprBoing)
+ end;
+ PlaySound(sndMelonImpact, true)
+end;
+
end.