Fix portal graphic name, continuing work on portal movement
(* * Hedgewars, a free turn based strategy game * Copyright (c) 2004-2010 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 * the Free Software Foundation; version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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 *)procedure makeHogsWorry(x, y: hwFloat; r: LongInt);var gi: PGear; d: LongInt;begin gi:= GearsList; while gi <> nil do begin if (gi^.Kind = gtHedgehog) then begin d:= r - hwRound(Distance(gi^.X - x, gi^.Y - y)); if (d > 1) and not gi^.Invulnerable and (GetRandom(2) = 0) then begin if (CurrentHedgehog^.Gear = gi) then PlaySound(sndOops, PHedgehog(gi^.Hedgehog)^.Team^.voicepack) else begin if (gi^.State and gstMoving) = 0 then gi^.State:= gi^.State or gstLoser; if d > r div 2 then PlaySound(sndNooo, PHedgehog(gi^.Hedgehog)^.Team^.voicepack) else PlaySound(sndUhOh, PHedgehog(gi^.Hedgehog)^.Team^.voicepack); end; end; end; gi:= gi^.NextGear end;end;////////////////////////////////////////////////////////////////////////////////procedure doStepDrowningGear(Gear: PGear); forward;function CheckGearDrowning(Gear: PGear): boolean;var skipSpeed, skipAngle, skipDecay: hwFloat; i, maxDrops: LongInt; particle: PVisualGear;begin// probably needs tweaking. might need to be in a case statement based upon gear type//(not Gear^.dY.isNegative) and this should not be necessaryif cWaterLine < hwRound(Gear^.Y) + Gear^.Radius then begin skipSpeed:= _0_25; // was 0.36 - couldn't manage baseball bat. Tiy's build is 0.36... skipAngle:= _1 + _0_9; // these should perhaps also be constants, once work out what proper values are skipDecay:= _0_87; // this could perhaps be a tiny bit higher. if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed) and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)) then begin Gear^.dY.isNegative:= true; Gear^.dY:= Gear^.dY * skipDecay; Gear^.dX:= Gear^.dX * skipDecay; CheckGearDrowning:= false; PlaySound(sndSkip) end else begin CheckGearDrowning:= true; Gear^.State:= gstDrowning; Gear^.RenderTimer:= false; if (Gear^.Kind <> gtSniperRifleShot) and (Gear^.Kind <> gtShotgunShot) and (Gear^.Kind <> gtDEagleShot) and (Gear^.Kind <> gtSineGunShot) then Gear^.doStep:= @doStepDrowningGear; if Gear^.Kind = gtHedgehog then begin Gear^.State:= Gear^.State and (not gstHHDriven); AddCaption(Format(GetEventString(eidDrowned), PHedgehog(Gear^.Hedgehog)^.Name), cWhiteColor, capgrpMessage); end; PlaySound(sndSplash) end; if not cReducedQuality then begin AddVisualGear(hwRound(Gear^.X), cWaterLine, vgtSplash); maxDrops := (Gear^.Radius div 2) + hwRound(Gear^.dX * Gear^.Radius * 2) + hwRound(Gear^.dY * Gear^.Radius * 2); for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do begin particle := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), cWaterLine, vgtDroplet); if particle <> nil then begin particle^.dX := particle^.dX - (Gear^.dX / 10); particle^.dY := particle^.dY - (Gear^.dY / 5) end end end; endelse CheckGearDrowning:= falseend;procedure CheckCollision(Gear: PGear);beginif TestCollisionXwithGear(Gear, hwSign(Gear^.X)) or TestCollisionYwithGear(Gear, hwSign(Gear^.Y)) then Gear^.State:= Gear^.State or gstCollision else Gear^.State:= Gear^.State and not gstCollisionend;procedure CheckHHDamage(Gear: PGear);var dmg: Longword; i: LongInt; particle: PVisualGear;beginif _0_4 < Gear^.dY then begin dmg:= ModifyDamage(1 + hwRound((hwAbs(Gear^.dY) - _0_4) * 70), Gear); if dmg < 1 then exit; 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 particle^.dX := particle^.dX + (Gear^.dX / 5); end; if(Gear^.Invulnerable) then exit; if _0_6 < Gear^.dY then PlaySound(sndOw4, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack) else PlaySound(sndOw1, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack); ApplyDamage(Gear, dmg); endend;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////procedure CalcRotationDirAngle(Gear: PGear);var dAngle: real;begindAngle:= (Gear^.dX.QWordValue + Gear^.dY.QWordValue) / $80000000;if not Gear^.dX.isNegative then Gear^.DirAngle:= Gear^.DirAngle + dAngleelse Gear^.DirAngle:= Gear^.DirAngle - dAngle;if Gear^.DirAngle < 0 then Gear^.DirAngle:= Gear^.DirAngle + 360else if 360 < Gear^.DirAngle then Gear^.DirAngle:= Gear^.DirAngle - 360end;////////////////////////////////////////////////////////////////////////////////procedure doStepDrowningGear(Gear: PGear);beginAllInactive:= false;Gear^.Y:= Gear^.Y + cDrownSpeed;Gear^.X:= Gear^.X + Gear^.dX * cDrownSpeed;if (cWaterOpacity > $FE) or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then DeleteGear(Gear);// Create some bubbles (0.5% might be better but causes too few bubbles sometimes)if (cWaterOpacity < $FF) and ((GameTicks and $1F) = 0) then if (Gear^.Kind = gtHedgehog) and (Random(4) = 0) then AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble) else if Random(12) = 0 then AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble)end;////////////////////////////////////////////////////////////////////////////////procedure doStepFallingGear(Gear: PGear);var isFalling: boolean; //tmp: QWord; tdX, tdY: hwFloat; collV, collH: LongInt;beginGear^.State:= Gear^.State and not gstCollision;collV:= 0; collH:= 0;tdX:= Gear^.dX;tdY:= Gear^.dY;// might need some testing/adjustments - just to avoid projectiles to fly forever (accelerated by wind/skips)if (hwRound(Gear^.X) < LAND_WIDTH div -2) or (hwRound(Gear^.X) > LAND_WIDTH * 3 div 2) then begin Gear^.State:= Gear^.State or gstCollision; exit end;if Gear^.dY.isNegative then begin isFalling:= true; if TestCollisionYwithGear(Gear, -1) then begin collV:= -1; Gear^.dX:= Gear^.dX * Gear^.Friction; Gear^.dY:= - Gear^.dY * Gear^.Elasticity; Gear^.State:= Gear^.State or gstCollision end else if (Gear^.AdvBounce=1) and TestCollisionYwithGear(Gear, 1) then collV:= 1; end else if TestCollisionYwithGear(Gear, 1) then begin collV:= 1; isFalling:= false; Gear^.dX:= Gear^.dX * Gear^.Friction; Gear^.dY:= - Gear^.dY * Gear^.Elasticity; Gear^.State:= Gear^.State or gstCollision endelse begin isFalling:= true; if (Gear^.AdvBounce=1) and not Gear^.dY.isNegative and TestCollisionYwithGear(Gear, -1) then collV:= -1; end;if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then begin collH:= hwSign(Gear^.dX); Gear^.dX:= - Gear^.dX * Gear^.Elasticity; Gear^.dY:= Gear^.dY * Gear^.Elasticity; Gear^.State:= Gear^.State or gstCollision end else if (Gear^.AdvBounce=1) and TestCollisionXwithGear(Gear, -hwSign(Gear^.dX)) then collH:= -hwSign(Gear^.dX);//if Gear^.AdvBounce and (collV <>0) and (collH <> 0) and (hwSqr(tdX) + hwSqr(tdY) > _0_08) thenif (Gear^.AdvBounce=1) and (collV <>0) and (collH <> 0) and ((collV=-1) or ((tdX.QWordValue + tdY.QWordValue) > _0_2.QWordValue)) then begin Gear^.dX:= tdY*Gear^.Elasticity*Gear^.Friction; Gear^.dY:= tdX*Gear^.Elasticity;//*Gear^.Friction; Gear^.dY.isNegative:= not tdY.isNegative; isFalling:= false; Gear^.AdvBounce:= 10; end;if Gear^.AdvBounce > 1 then dec(Gear^.AdvBounce);if isFalling then Gear^.dY:= Gear^.dY + cGravity;Gear^.X:= Gear^.X + Gear^.dX;Gear^.Y:= Gear^.Y + Gear^.dY;CheckGearDrowning(Gear);//if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) andif ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) and (not isFalling) then Gear^.State:= Gear^.State and not gstMovingelse Gear^.State:= Gear^.State or gstMoving;if (Gear^.nImpactSounds > 0) then if ((Gear^.Damage <> 0) or ((Gear^.State and (gstCollision or gstMoving)) = (gstCollision or gstMoving))) and ((Gear^.dX.QWordValue > _0_1.QWordValue) or (Gear^.dY.QWordValue > _0_1.QWordValue)) then PlaySound(TSound(ord(Gear^.ImpactSound) + LongInt(GetRandom(Gear^.nImpactSounds))), true);end;////////////////////////////////////////////////////////////////////////////////procedure doStepBomb(Gear: PGear);var i, x, y: LongInt; dX, dY: hwFloat; Fire: PGear;beginAllInactive:= false;doStepFallingGear(Gear);dec(Gear^.Timer);if Gear^.Timer = 1000 then // might need adjustments case Gear^.Kind of gtAmmo_Bomb: makeHogsWorry(Gear^.X, Gear^.Y, 50); gtClusterBomb: makeHogsWorry(Gear^.X, Gear^.Y, 20); gtWatermelon: makeHogsWorry(Gear^.X, Gear^.Y, 75); gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, 90); gtGasBomb: makeHogsWorry(Gear^.X, Gear^.Y, 50); end;if (Gear^.Kind = gtBall) and ((Gear^.State and gstTmpFlag) <> 0) then begin CheckCollision(Gear); if (Gear^.State and gstCollision) <> 0 then doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, EXPLDontDraw or EXPLNoGfx); end;if Gear^.Timer = 0 then begin case Gear^.Kind of gtAmmo_Bomb: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, EXPLAutoSound); gtClusterBomb: begin x:= hwRound(Gear^.X); y:= hwRound(Gear^.Y); doMakeExplosion(x, y, 20, EXPLAutoSound); for i:= 0 to 4 do begin dX:= rndSign(GetRandom * _0_1); dY:= (GetRandom - _3) * _0_08; AddGear(x, y, gtCluster, 0, dX, dY, 25); end end; gtWatermelon: begin x:= hwRound(Gear^.X); y:= hwRound(Gear^.Y); doMakeExplosion(x, y, 75, EXPLAutoSound); for i:= 0 to 5 do begin dX:= rndSign(GetRandom * _0_1); dY:= (GetRandom - _1_5) * _0_3; AddGear(x, y, gtMelonPiece, 0, dX, dY, 75)^.DirAngle:= i * 60; end end; gtHellishBomb: begin x:= hwRound(Gear^.X); y:= hwRound(Gear^.Y); doMakeExplosion(x, y, 90, EXPLAutoSound); for i:= 0 to 127 do begin dX:= AngleCos(i * 16) * _0_5 * (GetRandom + _1); dY:= AngleSin(i * 16) * _0_5 * (GetRandom + _1); Fire:= AddGear(x, y, gtFlame, 0, dX, dY, 0); if i mod 2 = 0 then Fire^.State:= Fire^.State or gsttmpFlag; Fire:= AddGear(x, y, gtFlame, 0, dX, -dY, 0); if i mod 2 <> 0 then Fire^.State:= Fire^.State or gsttmpFlag; end end; gtGasBomb: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound or EXPLPoisoned); end; DeleteGear(Gear); exit end;CalcRotationDirAngle(Gear);if Gear^.Kind = gtHellishBomb then begin if Gear^.Timer = 3000 then begin Gear^.nImpactSounds:= 0; PlaySound(sndHellish); end; if (GameTicks and $3F) = 0 then if (Gear^.State and gstCollision) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtEvilTrace, 0, _0, _0, 0); end;end;////////////////////////////////////////////////////////////////////////////////procedure doStepMolotov(Gear: PGear);var i, gX, gY: LongInt; dX, dY: hwFloat; Fire: PGear;begin AllInactive:= false; doStepFallingGear(Gear); CalcRotationDirAngle(Gear); if (Gear^.State and gstCollision) <> 0 then begin PlaySound(sndMolotov); gX:= hwRound(Gear^.X); gY:= hwRound(Gear^.Y); //doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 5, EXPLAutoSound); for i:= 0 to 20 do begin dX:= AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandom + _1); dY:= AngleSin(i * 8) * _0_5 * (GetRandom + _1); Fire:= AddGear(gX, gY, gtFlame, 0, dX, dY, 0); Fire^.State:= Fire^.State or gsttmpFlag; Fire:= AddGear(gX, gY, gtFlame, 0, dX, -dY, 0); Fire^.State:= Fire^.State or gsttmpFlag; Fire:= AddGear(gX, gY, gtFlame, 0, -dX, dY, 0); Fire^.State:= Fire^.State or gsttmpFlag; Fire:= AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0); Fire^.State:= Fire^.State or gsttmpFlag; end; DeleteGear(Gear); exit end;end;procedure doStepWatermelon(Gear: PGear);beginAllInactive:= false;Gear^.doStep:= @doStepBombend;procedure doStepCluster(Gear: PGear);beginAllInactive:= false;doStepFallingGear(Gear);if (Gear^.State and gstCollision) <> 0 then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, EXPLAutoSound); DeleteGear(Gear); exit end;if (Gear^.Kind = gtMelonPiece) or (Gear^.Kind = gtBall) then CalcRotationDirAngle(Gear)else if (GameTicks and $1F) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)end;////////////////////////////////////////////////////////////////////////////////procedure doStepGrenade(Gear: PGear);beginAllInactive:= false;Gear^.dX:= Gear^.dX + cWindSpeed;doStepFallingGear(Gear);if (Gear^.State and gstCollision) <> 0 then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); DeleteGear(Gear); exit end;if (GameTicks and $3F) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)end;////////////////////////////////////////////////////////////////////////////////procedure doStepHealthTagWork(Gear: PGear);beginif Gear^.Kind = gtHealthTag then AllInactive:= false;dec(Gear^.Timer);Gear^.Y:= Gear^.Y + Gear^.dY;if Gear^.Timer = 0 then begin if (Gear^.Kind = gtHealthTag) and (PHedgehog(Gear^.Hedgehog)^.Gear <> nil) then PHedgehog(Gear^.Hedgehog)^.Gear^.Active:= true; // to let current hh die DeleteGear(Gear) endend;procedure doStepHealthTagWorkUnderWater(Gear: PGear);beginAllInactive:= false;Gear^.Y:= Gear^.Y - _0_08;if hwRound(Gear^.Y) < cWaterLine + 10 then DeleteGear(Gear)end;procedure doStepHealthTag(Gear: PGear);var s: shortstring;beginAllInactive:= false;Gear^.dY:= -_0_08;str(Gear^.State, s);Gear^.Tex:= RenderStringTex(s, PHedgehog(Gear^.Hedgehog)^.Team^.Clan^.Color, fnt16);if hwRound(Gear^.Y) < cWaterLine then Gear^.doStep:= @doStepHealthTagWorkelse Gear^.doStep:= @doStepHealthTagWorkUnderWater;Gear^.Y:= Gear^.Y - int2hwFloat(Gear^.Tex^.h)end;////////////////////////////////////////////////////////////////////////////////procedure doStepGrave(Gear: PGear);beginAllInactive:= false;if Gear^.dY.isNegative then if TestCollisionY(Gear, -1) then Gear^.dY:= _0;if not Gear^.dY.isNegative then if TestCollisionY(Gear, 1) then begin Gear^.dY:= - Gear^.dY * Gear^.Elasticity; if Gear^.dY > - _1div1024 then begin Gear^.Active:= false; exit end else if Gear^.dY < - _0_03 then PlaySound(Gear^.ImpactSound) end;Gear^.Y:= Gear^.Y + Gear^.dY;CheckGearDrowning(Gear);Gear^.dY:= Gear^.dY + cGravityend;////////////////////////////////////////////////////////////////////////////////procedure doStepBeeWork(Gear: PGear);var t: hwFloat; gX,gY: LongInt; nuw: boolean;const uw: boolean = false;beginAllInactive:= false;gX:= hwRound(Gear^.X);gY:= hwRound(Gear^.Y);nuw:= (cWaterLine < hwRound(Gear^.Y) + Gear^.Radius);if nuw and not uw then begin AddVisualGear(gX, cWaterLine, vgtSplash); AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); StopSound(Gear^.SoundChannel); Gear^.SoundChannel:= LoopSound(sndBeeWater); uw:= nuw endelse if not nuw and uw then begin AddVisualGear(gX, cWaterLine, vgtSplash); StopSound(Gear^.SoundChannel); Gear^.SoundChannel:= LoopSound(sndBee); uw:= nuw end;t:= Distance(Gear^.dX, Gear^.dY);Gear^.dX:= Gear^.Elasticity * (Gear^.dX + _0_000004 * (TargetPoint.X - gX));Gear^.dY:= Gear^.Elasticity * (Gear^.dY + _0_000004 * (TargetPoint.Y - gY));t:= t / Distance(Gear^.dX, Gear^.dY);Gear^.dX:= Gear^.dX * t;Gear^.dY:= Gear^.dY * t;Gear^.X:= Gear^.X + Gear^.dX;Gear^.Y:= Gear^.Y + Gear^.dY;if (GameTicks and $3F) = 0 then begin AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBeeTrace); end;CheckCollision(Gear);dec(Gear^.Timer);if ((Gear^.State and gstCollision) <> 0) or (Gear^.Timer = 0) then begin StopSound(Gear^.SoundChannel); doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); DeleteGear(Gear); end;end;procedure doStepBee(Gear: PGear);beginAllInactive:= false;Gear^.X:= Gear^.X + Gear^.dX;Gear^.Y:= Gear^.Y + Gear^.dY;Gear^.dY:= Gear^.dY + cGravity;CheckCollision(Gear);if (Gear^.State and gstCollision) <> 0 then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); DeleteGear(Gear); exit end;dec(Gear^.Timer);if Gear^.Timer = 0 then begin Gear^.SoundChannel:= LoopSound(sndBee); Gear^.Timer:= 5000; Gear^.doStep:= @doStepBeeWork end;end;////////////////////////////////////////////////////////////////////////////////procedure doStepShotIdle(Gear: PGear);beginAllInactive:= false;inc(Gear^.Timer);if Gear^.Timer > 75 then begin DeleteGear(Gear); AfterAttack endend;procedure doStepShotgunShot(Gear: PGear);var i: LongWord; shell: PVisualGear;beginAllInactive:= false;if ((Gear^.State and gstAnimation) = 0) then begin dec(Gear^.Timer); if Gear^.Timer = 0 then begin PlaySound(sndShotgunFire); shell:= AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); if shell <> nil then begin shell^.dX:= gear^.dX / -4; shell^.dY:= gear^.dY / -4; shell^.Frame:= 0 end; Gear^.State:= Gear^.State or gstAnimation end; exit end else inc(Gear^.Timer);i:= 200;repeatGear^.X:= Gear^.X + Gear^.dX;Gear^.Y:= Gear^.Y + Gear^.dY;CheckCollision(Gear);if (Gear^.State and gstCollision) <> 0 then begin Gear^.X:= Gear^.X + Gear^.dX * 8; Gear^.Y:= Gear^.Y + Gear^.dY * 8; ShotgunShot(Gear); Gear^.doStep:= @doStepShotIdle; exit end;CheckGearDrowning(Gear);if (Gear^.State and gstDrowning) <> 0 then begin Gear^.doStep:= @doStepShotIdle; exit end;dec(i)until i = 0;if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then Gear^.doStep:= @doStepShotIdleend;////////////////////////////////////////////////////////////////////////////////procedure doStepBulletWork(Gear: PGear);var i, x, y: LongWord; oX, oY: hwFloat;beginAllInactive:= false;inc(Gear^.Timer);i:= 80;oX:= Gear^.X;oY:= Gear^.Y;repeat Gear^.X:= Gear^.X + Gear^.dX; Gear^.Y:= Gear^.Y + Gear^.dY; x:= hwRound(Gear^.X); y:= hwRound(Gear^.Y); if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then inc(Gear^.Damage); if Gear^.Damage > 5 then if Gear^.Ammo^.AmmoType = amDEagle then AmmoShove(Gear, 7, 20) else AmmoShove(Gear, Gear^.Timer, 20); CheckGearDrowning(Gear); dec(i)until (i = 0) or (Gear^.Damage > Gear^.Health) or ((Gear^.State and gstDrowning) <> 0);if Gear^.Damage > 0 then begin DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 82 - i, 1); dec(Gear^.Health, Gear^.Damage); Gear^.Damage:= 0 end;if ((Gear^.State and gstDrowning) <> 0) and (Gear^.Damage < Gear^.Health) and (cWaterOpacity < $FF) then begin for i:=(Gear^.Health - Gear^.Damage) * 4 downto 0 do begin if Random(6) = 0 then AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble); Gear^.X:= Gear^.X + Gear^.dX; Gear^.Y:= Gear^.Y + Gear^.dY; end; end;if (Gear^.Health <= 0) or (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then begin if (Gear^.Kind = gtSniperRifleShot) and ((GameFlags and gfLaserSight) = 0) then cLaserSighting:= false; if (Gear^.Ammo^.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and ((GameFlags and gfArtillery) = 0) then cArtillery:= false; Gear^.doStep:= @doStepShotIdle end;end;procedure doStepDEagleShot(Gear: PGear);beginPlaySound(sndGun);Gear^.doStep:= @doStepBulletWorkend;procedure doStepSniperRifleShot(Gear: PGear);var HHGear: PGear; shell: PVisualGear;begincArtillery:= true;HHGear:=PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.State:= HHGear^.State or gstNotKickable;HedgehogChAngle(HHGear);if not cLaserSighting then // game does not have default laser sight. turn it on and give them a chance to aim begin cLaserSighting:= true; HHGear^.Message:= 0; if(HHGear^.Angle - 32 >= 0) then dec(HHGear^.Angle,32) end;if (HHGear^.Message and gm_Attack) <> 0 then begin shell:= AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); if shell <> nil then begin shell^.dX:= gear^.dX / -2; shell^.dY:= gear^.dY / -2; shell^.Frame:= 1 end; Gear^.State:= Gear^.State or gstAnimation; Gear^.dX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _0_5; Gear^.dY:= -AngleCos(HHGear^.Angle) * _0_5; PlaySound(sndGun); Gear^.doStep:= @doStepBulletWork; endelse if (GameTicks mod 32) = 0 then if (GameTicks mod 4096) < 2048 then begin if(HHGear^.Angle + 1 <= cMaxAngle) then inc(HHGear^.Angle) end else if(HHGear^.Angle - 1 >= 0) then dec(HHGear^.Angle);if (TurnTimeLeft > 0) then dec(TurnTimeLeft)else begin DeleteGear(Gear); AfterAttack end;end;////////////////////////////////////////////////////////////////////////////////procedure doStepActionTimer(Gear: PGear);begindec(Gear^.Timer);case Gear^.Kind of gtATStartGame: begin AllInactive:= false; if Gear^.Timer = 0 then begin AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState); end end; gtATSmoothWindCh: begin if Gear^.Timer = 0 then begin if WindBarWidth < Gear^.Tag then inc(WindBarWidth) else if WindBarWidth > Gear^.Tag then dec(WindBarWidth); if WindBarWidth <> Gear^.Tag then Gear^.Timer:= 10; end end; gtATFinishGame: begin AllInactive:= false; if Gear^.Timer = 1000 then begin ScreenFade:= sfToBlack; ScreenFadeValue:= 0; ScreenFadeSpeed:= 1; end; if Gear^.Timer = 0 then begin SendIPC('N'); SendIPC('q'); GameState:= gsExit end end; end;if Gear^.Timer = 0 then DeleteGear(Gear)end;////////////////////////////////////////////////////////////////////////////////procedure doStepPickHammerWork(Gear: PGear);var i, ei: LongInt; HHGear: PGear;beginAllInactive:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;dec(Gear^.Timer);if (Gear^.Timer = 0)or((Gear^.Message and gm_Destroy) <> 0)or((HHGear^.State and gstHHDriven) = 0) then begin StopSound(Gear^.SoundChannel); DeleteGear(Gear); AfterAttack; exit end;if (Gear^.Timer mod 33) = 0 then begin HHGear^.State:= HHGear^.State or gstNoDamage; doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y) + 7, 6, EXPLDontDraw); HHGear^.State:= HHGear^.State and not gstNoDamage end;if (Gear^.Timer mod 47) = 0 then begin i:= hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2)); ei:= hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2)); while i <= ei do begin DrawExplosion(i, hwRound(Gear^.Y) + 3, 3); inc(i, 1) end; if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9), COLOR_INDESTRUCTIBLE) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.Y:= Gear^.Y + _1_9; end; SetAllHHToActive; end;if TestCollisionYwithGear(Gear, 1) then begin Gear^.dY:= _0; SetLittle(HHGear^.dX); HHGear^.dY:= _0; end else begin Gear^.dY:= Gear^.dY + cGravity; Gear^.Y:= Gear^.Y + Gear^.dY; if hwRound(Gear^.Y) > cWaterLine then Gear^.Timer:= 1 end;Gear^.X:= Gear^.X + HHGear^.dX;HHGear^.X:= Gear^.X;HHGear^.Y:= Gear^.Y - int2hwFloat(cHHRadius);if (Gear^.Message and gm_Attack) <> 0 then if (Gear^.State and gsttmpFlag) <> 0 then Gear^.Timer:= 1 else else if (Gear^.State and gsttmpFlag) = 0 then Gear^.State:= Gear^.State or gsttmpFlag;if ((Gear^.Message and gm_Left) <> 0) then Gear^.dX:= - _0_3 else if ((Gear^.Message and gm_Right) <> 0) then Gear^.dX:= _0_3 else Gear^.dX:= _0;end;procedure doStepPickHammer(Gear: PGear);var i, y: LongInt; ar: TRangeArray; HHGear: PGear;begini:= 0;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;y:= hwRound(Gear^.Y) - cHHRadius * 2;while y < hwRound(Gear^.Y) do begin ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2)); ar[i].Right:= hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2)); inc(y, 2); inc(i) end;DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i));Gear^.dY:= HHGear^.dY;DeleteCI(HHGear);Gear^.SoundChannel:= LoopSound(sndPickhammer);doStepPickHammerWork(Gear);Gear^.doStep:= @doStepPickHammerWorkend;////////////////////////////////////////////////////////////////////////////////var BTPrevAngle, BTSteps: LongInt;procedure doStepBlowTorchWork(Gear: PGear);var HHGear: PGear; b: boolean; prevX: LongInt;beginAllInactive:= false;dec(Gear^.Timer);HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HedgehogChAngle(HHGear);b:= false;if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7 then begin Gear^.dX:= SignAs(AngleSin(HHGear^.Angle) * _0_5, HHGear^.dX); Gear^.dY:= AngleCos(HHGear^.Angle) * ( - _0_5); BTPrevAngle:= HHGear^.Angle; b:= true end;if ((HHGear^.State and gstMoving) <> 0) then begin doStepHedgehogMoving(HHGear); if (HHGear^.State and gstHHDriven) = 0 then Gear^.Timer:= 0 end;if Gear^.Timer mod cHHStepTicks = 0 then begin b:= true; if Gear^.dX.isNegative then HHGear^.Message:= (HHGear^.Message and (gm_Attack or gm_Up or gm_Down)) or gm_Left else HHGear^.Message:= (HHGear^.Message and (gm_Attack or gm_Up or gm_Down)) or gm_Right; if ((HHGear^.State and gstMoving) = 0) then begin HHGear^.State:= HHGear^.State and not gstAttacking; prevX:= hwRound(HHGear^.X);// why the call to HedgehogStep then a further increment of X? if (prevX = hwRound(HHGear^.X)) and CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), COLOR_INDESTRUCTIBLE) then HedgehogStep(HHGear); if (prevX = hwRound(HHGear^.X)) and CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), COLOR_INDESTRUCTIBLE) then HHGear^.X:= HHGear^.X + SignAs(_1, HHGear^.dX); HHGear^.State:= HHGear^.State or gstAttacking end; inc(BTSteps); if BTSteps = 7 then begin BTSteps:= 0; if CheckLandValue(hwRound(HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC) + SignAs(_6,Gear^.dX)), hwRound(HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC)), COLOR_INDESTRUCTIBLE) then begin Gear^.X:= HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC); Gear^.Y:= HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC); end; HHGear^.State:= HHGear^.State or gstNoDamage; AmmoShove(Gear, 2, 15); HHGear^.State:= HHGear^.State and not gstNoDamage end; end;if b then DrawTunnel(HHGear^.X - Gear^.dX * cHHRadius, HHGear^.Y - _4 - Gear^.dY * cHHRadius + hwAbs(Gear^.dY) * 7, Gear^.dX, Gear^.dY, cHHRadius * 5, cHHRadius * 2 + 7);if (Gear^.Timer = 0) or ((HHGear^.Message and gm_Attack) <> 0) then begin HHGear^.Message:= 0; HHGear^.State:= HHGear^.State and (not gstNotKickable); DeleteGear(Gear); AfterAttack endend;procedure doStepBlowTorch(Gear: PGear);var HHGear: PGear;beginBTPrevAngle:= High(LongInt);BTSteps:= 0;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.Message:= 0;HHGear^.State:= HHGear^.State or gstNotKickable;Gear^.doStep:= @doStepBlowTorchWorkend;////////////////////////////////////////////////////////////////////////////////procedure doStepRope(Gear: PGear); forward;procedure doStepRopeAfterAttack(Gear: PGear);var HHGear: PGear;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;if ((HHGear^.State and gstHHDriven) = 0) or (CheckGearDrowning(HHGear)) or TestCollisionYwithGear(HHGear, 1) then begin DeleteGear(Gear); isCursorVisible:= false; ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^); exit end;HedgehogChAngle(HHGear);if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX);if HHGear^.dY.isNegative and TestCollisionYwithGear(HHGear, -1) then HHGear^.dY:= _0;HHGear^.X:= HHGear^.X + HHGear^.dX;HHGear^.Y:= HHGear^.Y + HHGear^.dY;HHGear^.dY:= HHGear^.dY + cGravity;if (Gear^.Message and gm_Attack) <> 0 then begin Gear^.X:= HHGear^.X; Gear^.Y:= HHGear^.Y; ApplyAngleBounds(PHedgehog(Gear^.Hedgehog)^, amRope); Gear^.dX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX); Gear^.dY:= -AngleCos(HHGear^.Angle); Gear^.Friction:= _450; Gear^.Elasticity:= _0; Gear^.State:= Gear^.State and not gsttmpflag; Gear^.doStep:= @doStepRope; endend;procedure doStepRopeWork(Gear: PGear);var HHGear: PGear; len, tx, ty, nx, ny, ropeDx, ropeDy, mdX, mdY: hwFloat; lx, ly: LongInt; haveCollision, haveDivided: boolean; procedure DeleteMe; begin with HHGear^ do begin Message:= Message and not gm_Attack; State:= (State or gstMoving) and not gstWinner; end; DeleteGear(Gear) end; procedure WaitCollision; begin with HHGear^ do begin Message:= Message and not gm_Attack; State:= State or gstMoving; end; RopePoints.Count:= 0; Gear^.Elasticity:= _0; Gear^.doStep:= @doStepRopeAfterAttack end;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;if ((HHGear^.State and gstHHDriven) = 0) or (CheckGearDrowning(HHGear)) then begin PlaySound(sndRopeRelease); DeleteMe; exit end;if (Gear^.Message and gm_Left <> 0) then HHGear^.dX:= HHGear^.dX - _0_0002 elseif (Gear^.Message and gm_Right <> 0) then HHGear^.dX:= HHGear^.dX + _0_0002;if not TestCollisionYwithGear(HHGear, 1) then HHGear^.dY:= HHGear^.dY + cGravity;ropeDx:= HHGear^.X - Gear^.X; // vector between hedgehog and rope attaching pointropeDy:= HHGear^.Y - Gear^.Y;mdX:= ropeDx + HHGear^.dX;mdY:= ropeDy + HHGear^.dY;len:= _1 / Distance(mdX, mdY);mdX:= mdX * len; // rope vector plus hedgehog direction vector normalizedmdY:= mdY * len;Gear^.dX:= mdX; // for visual purposes onlyGear^.dY:= mdY;///// tx:= HHGear^.X; ty:= HHGear^.Y; if ((Gear^.Message and gm_Down) <> 0) and (Gear^.Elasticity < Gear^.Friction) then if not (TestCollisionXwithGear(HHGear, hwSign(ropeDx)) or TestCollisionYwithGear(HHGear, hwSign(ropeDy))) then Gear^.Elasticity:= Gear^.Elasticity + _0_3; if ((Gear^.Message and gm_Up) <> 0) and (Gear^.Elasticity > _30) then if not (TestCollisionXwithGear(HHGear, -hwSign(ropeDx)) or TestCollisionYwithGear(HHGear, -hwSign(ropeDy))) then Gear^.Elasticity:= Gear^.Elasticity - _0_3; HHGear^.X:= Gear^.X + mdX * Gear^.Elasticity; HHGear^.Y:= Gear^.Y + mdY * Gear^.Elasticity; HHGear^.dX:= HHGear^.X - tx; HHGear^.dY:= HHGear^.Y - ty;//// haveDivided:= false; // check whether rope needs dividing len:= _1 / Distance(ropeDx, ropeDy); // old rope pos nx:= ropeDx * len; ny:= ropeDy * len; len:= Gear^.Elasticity - _5; while len > _3 do begin lx:= hwRound(Gear^.X + mdX * len); ly:= hwRound(Gear^.Y + mdY * len); if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and (Land[ly, lx] <> 0) then begin with RopePoints.ar[RopePoints.Count] do begin X:= Gear^.X; Y:= Gear^.Y; if RopePoints.Count = 0 then RopePoints.HookAngle:= DxDy2Angle(Gear^.dY, Gear^.dX); b:= (nx * HHGear^.dY) > (ny * HHGear^.dX); dLen:= len end; with RopePoints.rounded[RopePoints.Count] do begin X:= hwRound(Gear^.X); Y:= hwRound(Gear^.Y); end; Gear^.X:= Gear^.X + nx * len; Gear^.Y:= Gear^.Y + ny * len; inc(RopePoints.Count); TryDo(RopePoints.Count <= MAXROPEPOINTS, 'Rope points overflow', true); Gear^.Elasticity:= Gear^.Elasticity - len; Gear^.Friction:= Gear^.Friction - len; haveDivided:= true; break end; len:= len - _0_3 // should be the same as increase step end;if not haveDivided then if RopePoints.Count > 0 then // check whether the last dividing point could be removed begin tx:= RopePoints.ar[Pred(RopePoints.Count)].X; ty:= RopePoints.ar[Pred(RopePoints.Count)].Y; mdX:= tx - Gear^.X; mdY:= ty - Gear^.Y; if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * (ty - HHGear^.Y) > (tx - HHGear^.X) * mdY) then begin dec(RopePoints.Count); Gear^.X:= RopePoints.ar[RopePoints.Count].X; Gear^.Y:= RopePoints.ar[RopePoints.Count].Y; Gear^.Elasticity:= Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen; Gear^.Friction:= Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen; // restore hog position len:= _1 / Distance(mdX, mdY); mdX:= mdX * len; mdY:= mdY * len; HHGear^.X:= Gear^.X - mdX * Gear^.Elasticity; HHGear^.Y:= Gear^.Y - mdY * Gear^.Elasticity; end end;haveCollision:= false;if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then begin HHGear^.dX:= -_0_6 * HHGear^.dX; haveCollision:= true end;if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) then begin HHGear^.dY:= -_0_6 * HHGear^.dY; haveCollision:= true end;if haveCollision and (Gear^.Message and (gm_Left or gm_Right) <> 0) and (Gear^.Message and (gm_Up or gm_Down) <> 0) then begin HHGear^.dX:= SignAs(hwAbs(HHGear^.dX) + _0_2, HHGear^.dX); HHGear^.dY:= SignAs(hwAbs(HHGear^.dY) + _0_2, HHGear^.dY) end;len:= Distance(HHGear^.dX, HHGear^.dY);if len > _0_8 then begin len:= _0_8 / len; HHGear^.dX:= HHGear^.dX * len; HHGear^.dY:= HHGear^.dY * len; end;if (Gear^.Message and gm_Attack) <> 0 then if (Gear^.State and gsttmpFlag) <> 0 then with PHedgehog(Gear^.Hedgehog)^ do begin PlaySound(sndRopeRelease); if Ammo^[CurSlot, CurAmmo].AmmoType <> amParachute then WaitCollision else DeleteMe end elseelse if (Gear^.State and gsttmpFlag) = 0 then Gear^.State:= Gear^.State or gsttmpFlag;end;procedure doStepRopeAttach(Gear: PGear);var HHGear: PGear; tx, ty, tt: hwFloat; procedure RemoveFromAmmo; begin if (Gear^.State and gstAttacked) = 0 then begin OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^); Gear^.State:= Gear^.State or gstAttacked end; ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^) end;beginGear^.X:= Gear^.X - Gear^.dX;Gear^.Y:= Gear^.Y - Gear^.dY;Gear^.Elasticity:= Gear^.Elasticity + _1;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;DeleteCI(HHGear);if (HHGear^.State and gstMoving) <> 0 then begin if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX); if HHGear^.dY.isNegative and TestCollisionYwithGear(HHGear, -1) then HHGear^.dY:= _0; HHGear^.X:= HHGear^.X + HHGear^.dX; Gear^.X:= Gear^.X + HHGear^.dX; if TestCollisionYwithGear(HHGear, 1) then begin CheckHHDamage(HHGear); HHGear^.dY:= _0; //HHGear^.State:= HHGear^.State and not (gstHHJumping or gstHHHJump); end else begin HHGear^.Y:= HHGear^.Y + HHGear^.dY; Gear^.Y:= Gear^.Y + HHGear^.dY; HHGear^.dY:= HHGear^.dY + cGravity; end; tt:= Gear^.Elasticity; tx:= _0; ty:= _0; while tt > _20 do begin if TestCollisionXwithXYShift(Gear, tx, hwRound(ty), -hwSign(Gear^.dX)) or TestCollisionYwithXYShift(Gear, hwRound(tx), hwRound(ty), -hwSign(Gear^.dY)) then begin Gear^.X:= Gear^.X + tx; Gear^.Y:= Gear^.Y + ty; Gear^.Elasticity:= tt; Gear^.doStep:= @doStepRopeWork; PlaySound(sndRopeAttach); with HHGear^ do State:= State and not (gstAttacking or gstHHJumping or gstHHHJump); RemoveFromAmmo; tt:= _0; exit end; tx:= tx + Gear^.dX + Gear^.dX; ty:= ty + Gear^.dY + Gear^.dY; tt:= tt - _2; end; end;CheckCollision(Gear);if (Gear^.State and gstCollision) <> 0 then if Gear^.Elasticity < _10 then Gear^.Elasticity:= _10000 else begin Gear^.doStep:= @doStepRopeWork; PlaySound(sndRopeAttach); with HHGear^ do State:= State and not (gstAttacking or gstHHJumping or gstHHHJump); RemoveFromAmmo; exit end;if (Gear^.Elasticity > Gear^.Friction)or ((Gear^.Message and gm_Attack) = 0)or ((HHGear^.State and gstHHDriven) = 0)or (HHGear^.Damage > 0) then begin with PHedgehog(Gear^.Hedgehog)^.Gear^ do begin State:= State and not gstAttacking; Message:= Message and not gm_Attack end; DeleteGear(Gear) endend;procedure doStepRope(Gear: PGear);beginGear^.dX:= - Gear^.dX;Gear^.dY:= - Gear^.dY;Gear^.doStep:= @doStepRopeAttach;PlaySound(sndRopeShot)end;////////////////////////////////////////////////////////////////////////////////procedure doStepSmokeTrace(Gear: PGear);begininc(Gear^.Timer);if Gear^.Timer > 64 then begin Gear^.Timer:= 0; dec(Gear^.State) end;Gear^.dX:= Gear^.dX + cWindSpeed;Gear^.X:= Gear^.X + Gear^.dX;if Gear^.State = 0 then DeleteGear(Gear)end;////////////////////////////////////////////////////////////////////////////////procedure doStepExplosionWork(Gear: PGear);begininc(Gear^.Timer);if Gear^.Timer > 75 then begin inc(Gear^.State); Gear^.Timer:= 0; if Gear^.State > 5 then DeleteGear(Gear) end;end;procedure doStepExplosion(Gear: PGear);var i: LongWord;beginfor i:= 0 to 31 do AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire);for i:= 0 to 8 do AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplPart);for i:= 0 to 8 do AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplPart2);Gear^.doStep:= @doStepExplosionWorkend;////////////////////////////////////////////////////////////////////////////////procedure doStepMine(Gear: PGear);beginif (Gear^.State and gstMoving) <> 0 then begin DeleteCI(Gear); doStepFallingGear(Gear); if (Gear^.State and gstMoving) = 0 then begin AddGearCI(Gear); Gear^.dX:= _0; Gear^.dY:= _0 end; CalcRotationDirAngle(Gear); AllInactive:= false end else if ((GameTicks and $3F) = 25) then doStepFallingGear(Gear);if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then if ((Gear^.State and gstAttacking) = 0) then begin if ((GameTicks and $1F) = 0) then if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then Gear^.State:= Gear^.State or gstAttacking end else // gstAttacking <> 0 begin AllInactive:= false; if (Gear^.Timer and $FF) = 0 then PlaySound(sndMineTick); if Gear^.Timer = 0 then begin if ((Gear^.State and gstWait) <> 0) or (cMineDudPercent = 0) or (getRandom(100) > cMineDudPercent) then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); DeleteGear(Gear) end else begin AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); PlaySound(sndVaporize); Gear^.Health:= 0; end; exit end; dec(Gear^.Timer); end else // gsttmpFlag = 0 if TurnTimeLeft = 0 then Gear^.State:= Gear^.State or gsttmpFlag;end;////////////////////////////////////////////////////////////////////////////////procedure doStepDynamite(Gear: PGear);begindoStepFallingGear(Gear);AllInactive:= false;if Gear^.Timer mod 166 = 0 then inc(Gear^.Tag);if Gear^.Timer = 1000 then // might need better timing makeHogsWorry(Gear^.X, Gear^.Y, 75);if Gear^.Timer = 0 then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, EXPLAutoSound); DeleteGear(Gear); exit end;dec(Gear^.Timer);end;///////////////////////////////////////////////////////////////////////////////(*TODOIncrease damage as barrel smokes?Try tweaking friction some more*)procedure doStepRollingBarrel(Gear: PGear);var i: LongInt; particle: PVisualGear;beginGear^.State:= Gear^.State or gstAnimation;if ((Gear^.dX.QWordValue <> 0) or (Gear^.dY.QWordValue <> 0)) then begin DeleteCI(Gear); AllInactive:= false; if not Gear^.dY.isNegative and (Gear^.dY > _0_03) and TestCollisionYwithGear(Gear, 1) then begin Gear^.State:= Gear^.State or gsttmpFlag; inc(Gear^.Damage, hwRound(Gear^.dY * _50)); for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do begin particle:= AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); if particle <> nil then particle^.dX := particle^.dX + (Gear^.dX / 5) end end else if not Gear^.dX.isNegative and (Gear^.dX > _0_03) and TestCollisionXwithGear(Gear, 1) then inc(Gear^.Damage, hwRound(Gear^.dX * _50)) else if Gear^.dY.isNegative and (Gear^.dY < -_0_03) and TestCollisionYwithGear(Gear, -1) then inc(Gear^.Damage, hwRound(Gear^.dY * -_50)) else if Gear^.dX.isNegative and (Gear^.dX < -_0_03) and TestCollisionXwithGear(Gear, -1) then inc(Gear^.Damage, hwRound(Gear^.dX * -_50)); doStepFallingGear(Gear); CalcRotationDirAngle(Gear); //CheckGearDrowning(Gear) endelse begin Gear^.State:= Gear^.State or gsttmpFlag; AddGearCI(Gear) end;(*Attempt to make a barrel knock itself over an edge. Would need more checks to avoid issues like burn damage begin x:= hwRound(Gear^.X); y:= hwRound(Gear^.Y); if (((y+1) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then if (Land[y+1, x] = 0) then begin if (((y+1) and LAND_HEIGHT_MASK) = 0) and (((x+Gear^.Radius-2) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x+Gear^.Radius-2] = 0) then Gear^.dX:= -_0_08 else if (((y+1 and LAND_HEIGHT_MASK)) = 0) and (((x-(Gear^.Radius-2)) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x-(Gear^.Radius-2)] = 0) then Gear^.dX:= _0_08; end; if Gear^.dX.QWordValue = 0 then AddGearCI(Gear) end; *)if not Gear^.dY.isNegative and (Gear^.dY < _0_001) and TestCollisionYwithGear(Gear, 1) then Gear^.dY:= _0;if hwAbs(Gear^.dX) < _0_001 then Gear^.dX:= _0;if ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then if (cBarrelHealth div Gear^.Health) > 2 then AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke) else AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite);dec(Gear^.Health, Gear^.Damage);Gear^.Damage:= 0;if Gear^.Health <= 0 then Gear^.doStep:= @doStepCase; // Hand off to doStepCase for the explosionend;procedure doStepCase(Gear: PGear);var i, x, y: LongInt; k: TGearType; exBoom: boolean; dX, dY: HWFloat;begink:= Gear^.Kind;exBoom:= false;if (Gear^.Message and gm_Destroy) > 0 then begin DeleteGear(Gear); FreeActionsList; SetAllToActive; // something (hh, mine, etc...) could be on top of the case with CurrentHedgehog^ do if Gear <> nil then Gear^.Message:= Gear^.Message and not (gm_LJump or gm_HJump); exit end;if k = gtExplosives then begin //if V > _0_03 then Gear^.State:= Gear^.State or gstAnimation; if (hwAbs(Gear^.dX) > _0_15) or ((hwAbs(Gear^.dY) > _0_15) and (hwAbs(Gear^.dX) > _0_02)) then Gear^.doStep:= @doStepRollingBarrel; if ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then if (cBarrelHealth div Gear^.Health) > 2 then AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke) else AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite); dec(Gear^.Health, Gear^.Damage); Gear^.Damage:= 0; if Gear^.Health <= 0 then exBoom:= true; end;if (Gear^.Damage > 0) or exBoom then begin x:= hwRound(Gear^.X); y:= hwRound(Gear^.Y); DeleteGear(Gear); // <-- delete gear! if k = gtCase then begin doMakeExplosion(x, y, 25, EXPLAutoSound); for i:= 0 to 63 do AddGear(x, y, gtFlame, 0, _0, _0, 0); end else if k = gtExplosives then begin doMakeExplosion(x, y, 75, EXPLAutoSound); for i:= 0 to 31 do begin dX:= AngleCos(i * 64) * _0_5 * (getrandom + _1); dY:= AngleSin(i * 64) * _0_5 * (getrandom + _1); AddGear(x, y, gtFlame, 0, dX, dY, 0); AddGear(x, y, gtFlame, 0, -dX, -dY, 0)^.State:= gsttmpFlag; end end; exit end;if (Gear^.dY.QWordValue <> 0) or (not TestCollisionYwithGear(Gear, 1)) then begin AllInactive:= false; Gear^.dY:= Gear^.dY + cGravity; Gear^.Y:= Gear^.Y + Gear^.dY; if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then SetAllHHToActive; if (Gear^.dY.isNegative) and TestCollisionYwithGear(Gear, -1) then Gear^.dY:= _0; if (not Gear^.dY.isNegative) and TestCollisionYwithGear(Gear, 1) then begin if (Gear^.dY > _0_02) and (k = gtExplosives) then inc(Gear^.Damage, hwRound(Gear^.dY * _40)); if Gear^.dY > _0_2 then for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); Gear^.dY:= - Gear^.dY * Gear^.Elasticity; if Gear^.dY > - _0_001 then Gear^.dY:= _0 else if Gear^.dY < - _0_03 then PlaySound(Gear^.ImpactSound); end; //if Gear^.dY > - _0_001 then Gear^.dY:= _0 CheckGearDrowning(Gear); end;if (Gear^.dY.QWordValue = 0) then AddGearCI(Gear) else if (Gear^.dY.QWordValue <> 0) then DeleteCI(Gear)end;////////////////////////////////////////////////////////////////////////////////procedure doStepTarget(Gear: PGear);beginif (Gear^.Timer = 0) and (Gear^.Tag = 0) then PlaySound(sndWarp);if (Gear^.Tag = 0) and (Gear^.Timer < 1000) then inc(Gear^.Timer)else if Gear^.Tag = 1 then begin Gear^.Tag:= 2; if (TrainingFlags and tfTimeTrial) <> 0 then begin inc(TurnTimeLeft, TrainingTimeInc); if TrainingTimeInc > TrainingTimeInM then dec(TrainingTimeInc, TrainingTimeInD); if TurnTimeLeft > TrainingTimeMax then TurnTimeLeft:= TrainingTimeMax; end; endelse if Gear^.Tag = 2 then if Gear^.Timer > 0 then dec(Gear^.Timer) else begin if (TrainingFlags and tfTargetRespawn) <> 0 then begin TrainingTargetGear:= AddGear(0, 0, gtTarget, 0, _0, _0, 0); FindPlace(TrainingTargetGear, false, 0, LAND_WIDTH); end; DeleteGear(Gear); exit; end;doStepCase(Gear)end;////////////////////////////////////////////////////////////////////////////////procedure doStepIdle(Gear: PGear);beginAllInactive:= false;dec(Gear^.Timer);if Gear^.Timer = 0 then begin DeleteGear(Gear); AfterAttack endend;procedure doStepShover(Gear: PGear);var HHGear: PGear;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.State:= HHGear^.State or gstNoDamage;DeleteCI(HHGear);AmmoShove(Gear, 30, 115);HHGear^.State:= HHGear^.State and not gstNoDamage;Gear^.Timer:= 250;Gear^.doStep:= @doStepIdleend;////////////////////////////////////////////////////////////////////////////////procedure doStepWhip(Gear: PGear);var HHGear: PGear; i: LongInt;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.State:= HHGear^.State or gstNoDamage;DeleteCI(HHGear);for i:= 0 to 3 do begin AmmoShove(Gear, 30, 25); Gear^.X:= Gear^.X + Gear^.dX * 5 end;HHGear^.State:= HHGear^.State and not gstNoDamage;Gear^.Timer:= 250;Gear^.doStep:= @doStepIdleend;////////////////////////////////////////////////////////////////////////////////procedure doStepFlame(Gear: PGear);var gX,gY,i: LongInt;begin if (Gear^.State and gsttmpFlag) = 0 then AllInactive:= false;if not TestCollisionYwithGear(Gear, 1) then begin AllInactive:= false; if Gear^.dX.QWordValue > _0_01.QWordValue then Gear^.dX:= Gear^.dX * _0_995; Gear^.dY:= Gear^.dY + cGravity; if (Gear^.State and gsttmpFlag) <> 0 then Gear^.dY:= Gear^.dY + cGravity; if Gear^.dY.QWordValue > _0_2.QWordValue then Gear^.dY:= Gear^.dY * _0_995; if (Gear^.State and gsttmpFlag) <> 0 then Gear^.X:= Gear^.X + Gear^.dX else Gear^.X:= Gear^.X + Gear^.dX + cWindSpeed * 640; Gear^.Y:= Gear^.Y + Gear^.dY; if (hwRound(Gear^.Y) > cWaterLine) then begin gX:= hwRound(Gear^.X); for i:= 0 to 3 do AddVisualGear(gX - 16 + Random(32), cWaterLine - 16 + Random(16), vgtSteam); PlaySound(sndVaporize); DeleteGear(Gear); exit end end else begin if (Gear^.State and gsttmpFlag) <> 0 then begin Gear^.Radius:= 9; AmmoShove(Gear, 2, 30); Gear^.Radius:= 1 end; if Gear^.Timer > 0 then begin dec(Gear^.Timer); inc(Gear^.Damage) end else begin// Standard fire if (Gear^.State and gsttmpFlag) = 0 then begin Gear^.Radius:= 9; AmmoShove(Gear, 4, 100); gX:= hwRound(Gear^.X); gY:= hwRound(Gear^.Y); Gear^.Radius:= 1; doMakeExplosion(gX, gY, 4, EXPLNoDamage); if ((GameTicks and $7) = 0) and (Random(2) = 0) then for i:= 1 to Random(2)+1 do AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); if Gear^.Health > 0 then dec(Gear^.Health); Gear^.Timer:= 450 - Gear^.Tag * 8 end else begin// Modified fire if ((GameTicks and $7FF) = 0) and ((GameFlags and gfSolidLand) = 0) then begin DrawExplosion(gX, gY, 4); for i:= 0 to Random(3) do AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); end; // This one is interesting. I think I understand the purpose, but I wonder if a bit more fuzzy of kicking could be done with getrandom. Gear^.Timer:= 100 - Gear^.Tag * 3; if (Gear^.Damage > 3000+Gear^.Tag*1500) then Gear^.Health:= 0 end end end;if Gear^.Health = 0 then begin gX:= hwRound(Gear^.X); gY:= hwRound(Gear^.Y); if (Gear^.State and gsttmpFlag) = 0 then begin if ((GameTicks and $3) = 0) and (Random(1) = 0) then begin for i:= 1 to Random(2)+1 do begin AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); end; end; end else begin for i:= 0 to Random(3) do begin AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); end; end; DeleteGear(Gear) end;end;////////////////////////////////////////////////////////////////////////////////procedure doStepFirePunchWork(Gear: PGear);var HHGear: PGear;beginAllInactive:= false;if ((Gear^.Message and gm_Destroy) <> 0) then begin DeleteGear(Gear); AfterAttack; exit end;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;if hwRound(HHGear^.Y) <= Gear^.Tag - 2 then begin Gear^.Tag:= hwRound(HHGear^.Y); DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y - _1, _0_5, _0, cHHRadius * 4, 2); HHGear^.State:= HHGear^.State or gstNoDamage; Gear^.Y:= HHGear^.Y; AmmoShove(Gear, 30, 40); HHGear^.State:= HHGear^.State and not gstNoDamage end;HHGear^.dY:= HHGear^.dY + cGravity;if not (HHGear^.dY.isNegative) then begin HHGear^.State:= HHGear^.State or gstMoving; DeleteGear(Gear); AfterAttack; exit end;if CheckLandValue(hwRound(HHGear^.X), hwRound(HHGear^.Y + HHGear^.dY + SignAs(_6,Gear^.dY)), COLOR_INDESTRUCTIBLE) then HHGear^.Y:= HHGear^.Y + HHGear^.dYend;procedure doStepFirePunch(Gear: PGear);var HHGear: PGear;beginAllInactive:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;DeleteCI(HHGear);HHGear^.X:= int2hwFloat(hwRound(HHGear^.X)) - _0_5;HHGear^.dX:= SignAs(cLittle, Gear^.dX);HHGear^.dY:= - _0_3;Gear^.X:= HHGear^.X;Gear^.dX:= SignAs(_0_45, Gear^.dX);Gear^.dY:= - _0_9;Gear^.doStep:= @doStepFirePunchWork;DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y + _1, _0_5, _0, cHHRadius * 4, 5);PlaySound(TSound(ord(sndFirePunch1) + GetRandom(6)), PHedgehog(HHGear^.Hedgehog)^.Team^.voicepack)end;////////////////////////////////////////////////////////////////////////////////procedure doStepParachuteWork(Gear: PGear);var HHGear: PGear;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;inc(Gear^.Timer);if TestCollisionYwithGear(HHGear, 1) or ((HHGear^.State and gstHHDriven) = 0) or CheckGearDrowning(HHGear) or ((Gear^.Message and gm_Attack) <> 0) then begin with HHGear^ do begin Message:= 0; SetLittle(dX); dY:= _0; State:= State or gstMoving; end; DeleteGear(Gear); isCursorVisible:= false; ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^); exit end;if not TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then HHGear^.X:= HHGear^.X + cWindSpeed * 200;if (Gear^.Message and gm_Left) <> 0 then HHGear^.X:= HHGear^.X - cMaxWindSpeed * 80else if (Gear^.Message and gm_Right) <> 0 then HHGear^.X:= HHGear^.X + cMaxWindSpeed * 80;if (Gear^.Message and gm_Up) <> 0 then HHGear^.Y:= HHGear^.Y - cGravity * 40else if (Gear^.Message and gm_Down) <> 0 then HHGear^.Y:= HHGear^.Y + cGravity * 40;HHGear^.Y:= HHGear^.Y + cGravity * 100;Gear^.X:= HHGear^.X;Gear^.Y:= HHGear^.Yend;procedure doStepParachute(Gear: PGear);var HHGear: PGear;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;DeleteCI(HHGear);OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^);ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^);HHGear^.State:= HHGear^.State and not (gstAttacking or gstAttacked or gstMoving);HHGear^.Message:= HHGear^.Message and not gm_Attack;Gear^.doStep:= @doStepParachuteWork;Gear^.Message:= HHGear^.Message;doStepParachuteWork(Gear)end;////////////////////////////////////////////////////////////////////////////////procedure doStepAirAttackWork(Gear: PGear);var i: Longint;beginAllInactive:= false;Gear^.X:= Gear^.X + cAirPlaneSpeed * Gear^.Tag;if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then begin dec(Gear^.Health); case Gear^.State of 0: FollowGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0); 1: FollowGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine, 0, cBombsSpeed * Gear^.Tag, _0, 0); 2: for i:= -19 to 19 do FollowGear:= AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0); end; Gear^.dX:= Gear^.dX + int2hwFloat(30 * Gear^.Tag) end;if (GameTicks and $3F) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0);if (hwRound(Gear^.X) > (LAND_WIDTH+1024)) or (hwRound(Gear^.X) < -1024) then DeleteGear(Gear)end;procedure doStepAirAttack(Gear: PGear);beginAllInactive:= false;if Gear^.X.QWordValue = 0 then begin Gear^.Tag:= 1; Gear^.X:= -_1024; endelse begin Gear^.Tag:= -1; Gear^.X:= int2hwFloat(LAND_WIDTH + 1024); end;Gear^.Y:= int2hwFloat(topY-300);Gear^.dX:= int2hwFloat(TargetPoint.X - 5 * Gear^.Tag * 15);if (int2hwFloat(TargetPoint.Y) - Gear^.Y > _0) and (Gear^.State <> 2) then Gear^.dX:= Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(TargetPoint.Y) - Gear^.Y) * 2 / cGravity) * Gear^.Tag;Gear^.Health:= 6;Gear^.doStep:= @doStepAirAttackWork;end;////////////////////////////////////////////////////////////////////////////////procedure doStepAirBomb(Gear: PGear);beginAllInactive:= false;doStepFallingGear(Gear);if (Gear^.State and gstCollision) <> 0 then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, EXPLAutoSound); DeleteGear(Gear); exit end;if (GameTicks and $3F) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)end;////////////////////////////////////////////////////////////////////////////////procedure doStepGirder(Gear: PGear);var HHGear: PGear; x, y, tx, ty: hwFloat;beginAllInactive:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;tx:= int2hwFloat(TargetPoint.X);ty:= int2hwFloat(TargetPoint.Y);x:= HHGear^.X;y:= HHGear^.Y;if (Distance(tx - x, ty - y) > _256) or not TryPlaceOnLand(TargetPoint.X - SpritesData[sprAmGirder].Width div 2, TargetPoint.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true) then begin PlaySound(sndDenied); HHGear^.Message:= HHGear^.Message and not gm_Attack; HHGear^.State:= HHGear^.State and not gstAttacking; HHGear^.State:= HHGear^.State or gstHHChooseTarget; isCursorVisible:= true; DeleteGear(Gear) endelse begin PlaySound(sndPlaced); DeleteGear(Gear); OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^); ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^) end;HHGear^.State:= HHGear^.State and not (gstAttacking or gstAttacked);HHGear^.Message:= HHGear^.Message and not gm_Attack;TargetPoint.X:= NoPointXend;////////////////////////////////////////////////////////////////////////////////procedure doStepTeleportAfter(Gear: PGear);var HHGear: PGear;beginPHedgehog(Gear^.Hedgehog)^.Unplaced:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.Y:= HHGear^.Y + HHGear^.dY; // hedgehog falling to collect casesHHGear^.dY:= HHGear^.dY + cGravity;if TestCollisionYwithGear(HHGear, 1) or CheckGearDrowning(HHGear) then begin DeleteGear(Gear); AfterAttack endend;procedure doStepTeleportAnim(Gear: PGear);begininc(Gear^.Timer);if Gear^.Timer = 65 then begin Gear^.Timer:= 0; inc(Gear^.Pos); if Gear^.Pos = 11 then Gear^.doStep:= @doStepTeleportAfter end;end;procedure doStepTeleport(Gear: PGear);var HHGear: PGear;beginAllInactive:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;if not TryPlaceOnLand(TargetPoint.X - SpritesData[sprHHTelepMask].Width div 2, TargetPoint.Y - SpritesData[sprHHTelepMask].Height div 2, sprHHTelepMask, 0, false) then begin HHGear^.Message:= HHGear^.Message and not gm_Attack; HHGear^.State:= HHGear^.State and not gstAttacking; HHGear^.State:= HHGear^.State or gstHHChooseTarget; DeleteGear(Gear); isCursorVisible:= true; PlaySound(sndDenied) end else begin DeleteCI(HHGear); SetAllHHToActive; Gear^.doStep:= @doStepTeleportAnim; // copy old HH position and direction to Gear (because we need them for drawing the vanishing hog) Gear^.dX:= HHGear^.dX; // retrieve the cursor direction (it was previously copied to X so it doesn't get lost) HHGear^.dX.isNegative := (Gear^.X.QWordValue <> 0); Gear^.X:= HHGear^.X; Gear^.Y:= HHGear^.Y; HHGear^.X:= int2hwFloat(TargetPoint.X); HHGear^.Y:= int2hwFloat(TargetPoint.Y); HHGear^.State:= HHGear^.State or gstMoving; playSound(sndWarp) end;TargetPoint.X:= NoPointX;end;////////////////////////////////////////////////////////////////////////////////procedure doStepSwitcherWork(Gear: PGear);var HHGear: PGear; Msg, State: Longword;beginAllInactive:= false;if ((Gear^.Message and not gm_Switch) <> 0) or (TurnTimeLeft = 0) then begin HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear; Msg:= Gear^.Message and not gm_Switch; DeleteGear(Gear); OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^); ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^); HHGear:= CurrentHedgehog^.Gear; ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^); HHGear^.Message:= Msg; exit end;if (Gear^.Message and gm_Switch) <> 0 then begin HHGear:= CurrentHedgehog^.Gear; HHGear^.Message:= HHGear^.Message and not gm_Switch; Gear^.Message:= Gear^.Message and not gm_Switch; State:= HHGear^.State; HHGear^.State:= 0; HHGear^.Active:= false; HHGear^.Z:= cHHZ; RemoveGearFromList(HHGear); InsertGearToList(HHGear); PlaySound(sndSwitchHog); repeat CurrentTeam^.CurrHedgehog:= Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber); until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil); CurrentHedgehog:= @CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]; HHGear:= CurrentHedgehog^.Gear; HHGear^.State:= State; HHGear^.Active:= true; FollowGear:= HHGear; HHGear^.Z:= cCurrHHZ; RemoveGearFromList(HHGear); InsertGearToList(HHGear); Gear^.X:= HHGear^.X; Gear^.Y:= HHGear^.Y end;end;procedure doStepSwitcher(Gear: PGear);var HHGear: PGear;beginGear^.doStep:= @doStepSwitcherWork;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;with HHGear^ do begin State:= State and not gstAttacking; Message:= Message and not gm_Attack endend;////////////////////////////////////////////////////////////////////////////////procedure doStepMortar(Gear: PGear);var dX, dY: hwFloat; i: LongInt; dxn, dyn: boolean;beginAllInactive:= false;dxn:= Gear^.dX.isNegative;dyn:= Gear^.dY.isNegative;doStepFallingGear(Gear);if (Gear^.State and gstCollision) <> 0 then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, EXPLAutoSound); Gear^.dX.isNegative:= not dxn; Gear^.dY.isNegative:= not dyn; for i:= 0 to 4 do begin dX:= Gear^.dX + (GetRandom - _0_5) * _0_03; dY:= Gear^.dY + (GetRandom - _0_5) * _0_03; AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtCluster, 0, dX, dY, 25); end; DeleteGear(Gear); exit end;if (GameTicks and $3F) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0)end;////////////////////////////////////////////////////////////////////////////////procedure doStepKamikazeWork(Gear: PGear);const upd: Longword = 0;var i: LongWord; HHGear: PGear;beginAllInactive:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.State:= HHGear^.State or gstNoDamage;DeleteCI(HHGear);i:= 2;repeat Gear^.X:= Gear^.X + HHGear^.dX; Gear^.Y:= Gear^.Y + HHGear^.dY; HHGear^.X:= Gear^.X; HHGear^.Y:= Gear^.Y; inc(Gear^.Damage, 2);// if TestCollisionXwithGear(HHGear, hwSign(Gear^.dX))// or TestCollisionYwithGear(HHGear, hwSign(Gear^.dY)) then inc(Gear^.Damage, 3); dec(i)until (i = 0) or (Gear^.Damage > Gear^.Health);inc(upd);if upd > 3 then begin if Gear^.Health < 1500 then Gear^.Pos:= 2; AmmoShove(Gear, 30, 40); DrawTunnel(HHGear^.X - HHGear^.dX * 10, HHGear^.Y - _2 - HHGear^.dY * 10 + hwAbs(HHGear^.dY) * 2, HHGear^.dX, HHGear^.dY, 20 + cHHRadius * 2, cHHRadius * 2 + 6); upd:= 0 end;if Gear^.Health < Gear^.Damage then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, EXPLAutoSound); AfterAttack; DeleteGear(Gear); DeleteGear(HHGear); end else begin dec(Gear^.Health, Gear^.Damage); Gear^.Damage:= 0 endend;procedure doStepKamikazeIdle(Gear: PGear);beginAllInactive:= false;dec(Gear^.Timer);if Gear^.Timer = 0 then begin Gear^.Pos:= 1; PlaySound(sndKamikaze, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack); Gear^.doStep:= @doStepKamikazeWork endend;procedure doStepKamikaze(Gear: PGear);var HHGear: PGear;beginAllInactive:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.dX:= Gear^.dX;HHGear^.dY:= Gear^.dY;Gear^.dX:= SignAs(_0_45, Gear^.dX);Gear^.dY:= - _0_9;Gear^.Timer:= 550;Gear^.doStep:= @doStepKamikazeIdleend;////////////////////////////////////////////////////////////////////////////////const cakeh = 27; cakeDmg = 75;var CakePoints: array[0..Pred(cakeh)] of record x, y: hwFloat; end; CakeI: Longword;procedure doStepCakeExpl(Gear: PGear);beginAllInactive:= false;inc(Gear^.Tag);if Gear^.Tag < 2250 then exit;doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, EXPLAutoSound);AfterAttack;DeleteGear(Gear)end;procedure doStepCakeDown(Gear: PGear);var gi: PGear; dmg: LongInt;beginAllInactive:= false;inc(Gear^.Tag);if Gear^.Tag < 100 then exit;Gear^.Tag:= 0;if Gear^.Pos = 0 then begin gi:= GearsList; while gi <> nil do begin dmg:= cakeDmg * 2 - hwRound(Distance(gi^.X - Gear^.X, gi^.Y - Gear^.Y)); if (dmg > 1) and (gi^.Kind = gtHedgehog) then if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then gi^.State:= gi^.State or gstLoser else gi^.State:= gi^.State or gstWinner; gi:= gi^.NextGear end; Gear^.doStep:= @doStepCakeExpl; PlaySound(sndCake) end else dec(Gear^.Pos)end;procedure doStepCakeWork(Gear: PGear);const dirs: array[0..3] of TPoint = ((x: 0; y: -1), (x: 1; y: 0),(x: 0; y: 1),(x: -1; y: 0));var xx, yy, xxn, yyn: LongInt; da: LongInt; tdx, tdy: hwFloat; procedure PrevAngle; begin Gear^.Angle:= (LongInt(Gear^.Angle) + 4 - dA) mod 4 end; procedure NextAngle; begin Gear^.Angle:= (LongInt(Gear^.Angle) + 4 + dA) mod 4 end;beginAllInactive:= false;inc(Gear^.Tag);if Gear^.Tag < 7 then exit;dA:= hwSign(Gear^.dX);xx:= dirs[Gear^.Angle].x;yy:= dirs[Gear^.Angle].y;xxn:= dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].x;yyn:= dirs[(LongInt(Gear^.Angle) + 4 + dA) mod 4].y;if (xx = 0) then if TestCollisionYwithGear(Gear, yy) then PrevAngle else begin Gear^.Tag:= 0; Gear^.Y:= Gear^.Y + int2hwFloat(yy); if not TestCollisionXwithGear(Gear, xxn) then begin Gear^.X:= Gear^.X + int2hwFloat(xxn); NextAngle end; end;if (yy = 0) then if TestCollisionXwithGear(Gear, xx) then PrevAngle else begin Gear^.Tag:= 0; Gear^.X:= Gear^.X + int2hwFloat(xx); if not TestCollisionYwithGear(Gear, yyn) then begin Gear^.Y:= Gear^.Y + int2hwFloat(yyn); NextAngle end; end;if Gear^.Tag = 0 then begin CakeI:= (CakeI + 1) mod cakeh; tdx:= CakePoints[CakeI].x - Gear^.X; tdy:= - CakePoints[CakeI].y + Gear^.Y; CakePoints[CakeI].x:= Gear^.X; CakePoints[CakeI].y:= Gear^.Y; Gear^.DirAngle:= DxDy2Angle(tdx, tdy); end;dec(Gear^.Health);Gear^.Timer:= Gear^.Health*10; // This is not seconds, but at least it is *some* feedbackif (Gear^.Health = 0) or ((Gear^.Message and gm_Attack) <> 0) then begin FollowGear:= Gear; Gear^.RenderTimer:= false; Gear^.doStep:= @doStepCakeDown endend;procedure doStepCakeUp(Gear: PGear);var i: Longword;beginAllInactive:= false;inc(Gear^.Tag);if Gear^.Tag < 100 then exit;Gear^.Tag:= 0;if Gear^.Pos = 6 then begin for i:= 0 to Pred(cakeh) do begin CakePoints[i].x:= Gear^.X; CakePoints[i].y:= Gear^.Y end; CakeI:= 0; Gear^.doStep:= @doStepCakeWork end else inc(Gear^.Pos)end;procedure doStepCakeFall(Gear: PGear);beginAllInactive:= false;Gear^.dY:= Gear^.dY + cGravity;if TestCollisionYwithGear(Gear, 1) then Gear^.doStep:= @doStepCakeUpelse begin Gear^.Y:= Gear^.Y + Gear^.dY; if CheckGearDrowning(Gear) then AfterAttack endend;procedure doStepCake(Gear: PGear);var HHGear: PGear;beginAllInactive:= false;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.Message:= HHGear^.Message and (not gm_Attack);DeleteCI(HHGear);FollowGear:= Gear;Gear^.doStep:= @doStepCakeFallend;////////////////////////////////////////////////////////////////////////////////procedure doStepSeductionWork(Gear: PGear);var x, y: LongInt;beginAllInactive:= false;Gear^.X:= Gear^.X + Gear^.dX;Gear^.Y:= Gear^.Y + Gear^.dY;x:= hwRound(Gear^.X);y:= hwRound(Gear^.Y);if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then if (Land[y, x] <> 0) then begin Gear^.dX.isNegative:= not Gear^.dX.isNegative; Gear^.dY.isNegative:= not Gear^.dY.isNegative; Gear^.dX:= Gear^.dX * _1_5; Gear^.dY:= Gear^.dY * _1_5 - _0_3; AmmoShove(Gear, 0, 40); AfterAttack; DeleteGear(Gear) end elseelse begin AfterAttack; DeleteGear(Gear) endend;procedure doStepSeductionWear(Gear: PGear);beginAllInactive:= false;inc(Gear^.Timer);if Gear^.Timer > 250 then begin Gear^.Timer:= 0; inc(Gear^.Pos); if Gear^.Pos = 5 then PlaySound(sndYoohoo, PHedgehog(Gear^.Hedgehog)^.Team^.voicepack) end;if Gear^.Pos = 14 then Gear^.doStep:= @doStepSeductionWorkend;procedure doStepSeduction(Gear: PGear);beginAllInactive:= false;DeleteCI(PHedgehog(Gear^.Hedgehog)^.Gear);Gear^.doStep:= @doStepSeductionWearend;////////////////////////////////////////////////////////////////////////////////procedure doStepWaterUp(Gear: PGear);var i: LongWord;beginAllInactive:= false;inc(Gear^.Timer);if Gear^.Timer = 17 then Gear^.Timer:= 0else exit;if cWaterLine > 0 then begin dec(cWaterLine); for i:= 0 to LAND_WIDTH - 1 do Land[cWaterLine, i]:= 0; SetAllToActive end;inc(Gear^.Tag);if (Gear^.Tag = 47) or (cWaterLine = 0) then DeleteGear(Gear)end;////////////////////////////////////////////////////////////////////////////////procedure doStepDrillDrilling(Gear: PGear);var t: PGearArray; ox, oy: hwFloat;beginAllInactive:= false;if (Gear^.Timer > 0) and ((Gear^.Timer mod 10) = 0) then begin ox:= Gear^.X; oy:= Gear^.Y; Gear^.X:= Gear^.X + Gear^.dX; Gear^.Y:= Gear^.Y + Gear^.dY; DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 2, 6); if(CheckGearDrowning(Gear)) then begin StopSound(Gear^.SoundChannel); exit end end;t:= CheckGearsCollision(Gear); //fixes drill not exploding when touching HH bugif (Gear^.Timer = 0)or (t^.Count <> 0)or (not TestCollisionYWithGear(Gear, hwSign(Gear^.dY))and not TestCollisionXWithGear(Gear, hwSign(Gear^.dX)))or (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] = COLOR_INDESTRUCTIBLE) then begin //out of time or exited ground StopSound(Gear^.SoundChannel); doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); DeleteGear(Gear); exit end;dec(Gear^.Timer);end;procedure doStepDrill(Gear: PGear);var t: PGearArray; oldDx, oldDy: hwFloat; t2: hwFloat;beginAllInactive:= false;Gear^.dX:= Gear^.dX + cWindSpeed;oldDx:= Gear^.dX;oldDy:= Gear^.dY;doStepFallingGear(Gear);if (GameTicks and $3F) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0);if ((Gear^.State and gstCollision) <> 0) then begin //hit Gear^.dX:= oldDx; Gear^.dY:= oldDy; t:= CheckGearsCollision(Gear); if (t^.Count = 0) then begin //hit the ground not the HH t2 := _0_5 / Distance(Gear^.dX, Gear^.dY); Gear^.dX:= Gear^.dX * t2; Gear^.dY:= Gear^.dY * t2; end else begin //explode right on contact with HH doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound); DeleteGear(Gear); exit; end; Gear^.SoundChannel:= LoopSound(sndDrillRocket); Gear^.doStep:= @doStepDrillDrilling; dec(Gear^.Timer) endend;////////////////////////////////////////////////////////////////////////////////procedure doStepBallgunWork(Gear: PGear);var HHGear: PGear; rx, ry: hwFloat; gX, gY: LongInt;begin AllInactive:= false; dec(Gear^.Timer); HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear; HedgehogChAngle(HHGear); gX:= hwRound(Gear^.X); gY:= hwRound(Gear^.Y); if (Gear^.Timer mod 100) = 0 then begin rx:= rndSign(getRandom * _0_1); ry:= rndSign(getRandom * _0_1); AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0); PlaySound(sndGun); end; if (Gear^.Timer = 0) or (HHGear^.Damage <> 0) then begin DeleteGear(Gear); AfterAttack endend;procedure doStepBallgun(Gear: PGear);var HHGear: PGear;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.Message:= HHGear^.Message and not (gm_Up or gm_Down);HHGear^.State:= HHGear^.State or gstNotKickable;Gear^.doStep:= @doStepBallgunWorkend;////////////////////////////////////////////////////////////////////////////////procedure doStepRCPlaneWork(Gear: PGear);const cAngleSpeed = 3;var HHGear: PGear; i: LongInt; dX, dY: hwFloat; fChanged: boolean; trueAngle: Longword; t: PGear;beginAllInactive:= false;if ((TrainingFlags and tfRCPlane) = 0) and (Gear^.Timer > 0) then dec(Gear^.Timer);if ((TrainingFlags and tfRCPlane) <> 0) and ((TrainingFlags and tfTimeTrial) <> 0 ) and (TimeTrialStartTime = 0) then TimeTrialStartTime:= RealTicks;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;FollowGear:= Gear;fChanged:= false;if ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then begin fChanged:= true; if Gear^.Angle > 2048 then dec(Gear^.Angle) else if Gear^.Angle < 2048 then inc(Gear^.Angle) else fChanged:= false endelse begin if ((Gear^.Message and gm_Left) <> 0) then begin fChanged:= true; Gear^.Angle:= (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096 end; if ((Gear^.Message and gm_Right) <> 0) then begin fChanged:= true; Gear^.Angle:= (Gear^.Angle + cAngleSpeed) mod 4096 end end;if fChanged then begin Gear^.dX.isNegative:= (Gear^.Angle > 2048); if Gear^.dX.isNegative then trueAngle:= 4096 - Gear^.Angle else trueAngle:= Gear^.Angle; Gear^.dX:= SignAs(AngleSin(trueAngle), Gear^.dX) * _0_25; Gear^.dY:= AngleCos(trueAngle) * -_0_25; end;Gear^.X:= Gear^.X + Gear^.dX;Gear^.Y:= Gear^.Y + Gear^.dY;if (TrainingFlags and tfRCPlane) = 0 then begin if (GameTicks and $FF) = 0 then if Gear^.Timer < 3500 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtEvilTrace, 0, _0, _0, 0) else AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0); if ((HHGear^.Message and gm_Attack) <> 0) and (Gear^.Health <> 0) then begin HHGear^.Message := HHGear^.Message and not gm_Attack; AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, Gear^.dX * _0_5, Gear^.dY * _0_5, 0); dec(Gear^.Health) end; if ((HHGear^.Message and gm_LJump) <> 0) and ((Gear^.State and gsttmpFlag) = 0) then begin Gear^.State:= Gear^.State or gsttmpFlag; PauseMusic; playSound(sndRideOfTheValkyries); end; // pickup bonuses t:= CheckGearNear(Gear, gtCase, 36, 36); if t <> nil then PickUp(HHGear, t); endelse begin if (GameTicks and $FF) = 0 then AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtSmokeTrace, 0, _0, _0, 0); // pickup targets t:= CheckGearNear(Gear, gtTarget, 36, 36); if t <> nil then begin if t^.Tag <> 0 then // collect it only once exit; PlaySound(sndShotgunReload); t^.Tag:= 1; TrainingTargetGear:= nil; // remove target cursor exit; end; if (TurnTimeLeft > 0) then dec(TurnTimeLeft) end;CheckCollision(Gear);if ((Gear^.State and gstCollision) <> 0) or (((TrainingFlags and tfRCPlane) <> 0) and (TurnTimeLeft = 0)) or CheckGearDrowning(Gear) then begin if ((TrainingFlags and tfRCPlane) <> 0) and ((TrainingFlags and tfTimeTrial) <> 0 ) and (TimeTrialStopTime = 0) then TimeTrialStopTime:= RealTicks; StopSound(Gear^.SoundChannel); StopSound(sndRideOfTheValkyries); ResumeMusic; if ((Gear^.State and gstCollision) <> 0) or (((TrainingFlags and tfRCPlane) <> 0) and (TurnTimeLeft = 0)) then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, EXPLAutoSound); for i:= 0 to 32 do begin dX:= AngleCos(i * 64) * _0_5 * (GetRandom + _1); dY:= AngleSin(i * 64) * _0_5 * (GetRandom + _1); AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, dY, 0); AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, -dY, 0); end; DeleteGear(Gear) end; AfterAttack; CurAmmoGear:= nil; TurnTimeLeft:= 14 * 125; if (TrainingFlags and tfRCPlane) <> 0 then TurnTimeLeft:= 0; // HACK: RCPlane training allows unlimited plane starts in last 2 seconds HHGear^.Message:= 0; ParseCommand('/taunt '#1, true) endend;procedure doStepRCPlane(Gear: PGear);var HHGear: PGear;beginHHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;HHGear^.Message:= 0;HHGear^.State:= HHGear^.State or gstNotKickable;Gear^.Angle:= HHGear^.Angle;Gear^.Tag:= hwSign(HHGear^.dX);if HHGear^.dX.isNegative then Gear^.Angle:= 4096 - Gear^.Angle;Gear^.doStep:= @doStepRCPlaneWorkend;procedure doStepJetpackWork(Gear: PGear);var HHGear: PGear; fuel: LongInt; move: hwFloat;beginAllInactive:= false;HHGear:=PHedgehog(Gear^.Hedgehog)^.Gear;//dec(Gear^.Timer);move:= _0_1;fuel:= 50;(*if (HHGear^.Message and gm_Precise) <> 0 then begin move:= _0_02; fuel:= 5; end;*)if (HHGear^.Message and gm_Up) <> 0 then begin if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then HHGear^.dY:= HHGear^.dY - move; HHGear^.dY:= HHGear^.dY - move; dec(Gear^.Health, fuel); Gear^.MsgParam:= Gear^.MsgParam or gm_Up; Gear^.Timer:= GameTicks end;if (HHGear^.Message and gm_Left) <> 0 then move.isNegative:= true;if (HHGear^.Message and (gm_Left or gm_Right)) <> 0 then begin HHGear^.dX:= HHGear^.dX + (move * _0_2); dec(Gear^.Health, fuel div 5); Gear^.MsgParam:= Gear^.MsgParam or (HHGear^.Message and (gm_Left or gm_Right)); Gear^.Timer:= GameTicks end;// erases them all at once :-/if (Gear^.Timer <> 0) and (GameTicks - Gear^.Timer > 250) then begin Gear^.Timer:= 0; Gear^.MsgParam:= 0 end;if Gear^.Health < 0 then Gear^.Health:= 0;if (GameTicks and $3F) = 0 then begin//AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); if Gear^.Tex <> nil then FreeTexture(Gear^.Tex); Gear^.Tex:= RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) + '%', cWhiteColor, fntSmall) end;if HHGear^.Message and (gm_Attack or gm_Up or gm_Precise or gm_Left or gm_Right) <> 0 then Gear^.State:= Gear^.State and not gsttmpFlag;HHGear^.Message:= HHGear^.Message and not (gm_Up or gm_Precise or gm_Left or gm_Right);HHGear^.State:= HHGear^.State or gstMoving;Gear^.X:= HHGear^.X;Gear^.Y:= HHGear^.Y;// For some reason I need to reapply followgear here, something else grabs it otherwise.if not bShowAmmoMenu then FollowGear:= HHGear;if ((Gear^.State and gsttmpFlag) = 0) or (HHGear^.dY < _0) then doStepHedgehogMoving(HHGear);if (Gear^.Health = 0) or (HHGear^.Damage <> 0) or CheckGearDrowning(HHGear) or (TurnTimeLeft = 0) // allow brief ground touches - to be fair on this, might need another counter or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and TestCollisionYwithGear(HHGear, 1)) or ((Gear^.Message and gm_Attack) <> 0) then begin with HHGear^ do begin Message:= 0; Active:= true; State:= State or gstMoving end; DeleteGear(Gear); isCursorVisible:= false; ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^);// if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);// Gear^.Tex:= RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) + '%', cWhiteColor, fntSmall) //AddCaption(trmsg[sidFuel]+': '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); endend;////////////////////////////////////////////////////////////////////////////////procedure doStepJetpack(Gear: PGear);var HHGear: PGear;beginGear^.doStep:= @doStepJetpackWork;HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;FollowGear:= HHGear;OnUsedAmmo(PHedgehog(HHGear^.Hedgehog)^);ApplyAmmoChanges(PHedgehog(HHGear^.Hedgehog)^);with HHGear^ do begin State:= State and not gstAttacking; Message:= Message and not (gm_Attack or gm_Up or gm_Precise or gm_Left or gm_Right); if (dY < _0_1) and (dY > -_0_1) then begin Gear^.State:= Gear^.State or gsttmpFlag; dY:= dY - _0_2 end endend;////////////////////////////////////////////////////////////////////////////////procedure doStepBirdyDisappear(Gear: PGear);beginAllInactive:= false;Gear^.Pos:= 0;if Gear^.Timer < 2000 then inc(Gear^.Timer, 1)else begin DeleteGear(Gear); end;end;////////////////////////////////////////////////////////////////////////////////procedure doStepBirdyFly(Gear: PGear);var HHGear: PGear; fuel, i: LongInt; move: hwFloat;beginHHGear:= CurrentHedgehog^.Gear;move:= _0_1;fuel:= 50;if Gear^.Pos > 0 then dec(Gear^.Pos, 1)else if (HHGear^.Message and (gm_Left or gm_Right or gm_Up)) <> 0 then Gear^.Pos:= 500;if HHGear^.dX.isNegative then Gear^.Tag:= -1else Gear^.Tag:= 1;if (HHGear^.Message and gm_Up) <> 0 then begin if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then HHGear^.dY:= HHGear^.dY - move; HHGear^.dY:= HHGear^.dY - move; dec(Gear^.Health, fuel); Gear^.MsgParam:= Gear^.MsgParam or gm_Up; end;if (HHGear^.Message and gm_Left) <> 0 then move.isNegative:= true;if (HHGear^.Message and (gm_Left or gm_Right)) <> 0 then begin HHGear^.dX:= HHGear^.dX + (move * _0_2); dec(Gear^.Health, fuel div 5); Gear^.MsgParam:= Gear^.MsgParam or (HHGear^.Message and (gm_Left or gm_Right)); end;if Gear^.Health < 0 then Gear^.Health:= 0;if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then for i:= ((500-Gear^.Health) div 250) downto 0 do AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather);if (HHGear^.Message and gm_Attack <> 0) then begin HHGear^.Message := HHGear^.Message and not gm_Attack; if Gear^.FlightTime > 0 then begin AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0); PlaySound(sndBirdyLay); dec(Gear^.FlightTime) end;end;if HHGear^.Message and (gm_Up or gm_Precise or gm_Left or gm_Right) <> 0 then Gear^.State:= Gear^.State and not gsttmpFlag;HHGear^.Message:= HHGear^.Message and not (gm_Up or gm_Precise or gm_Left or gm_Right);HHGear^.State:= HHGear^.State or gstMoving;Gear^.X:= HHGear^.X;Gear^.Y:= HHGear^.Y - int2hwFloat(32);// For some reason I need to reapply followgear here, something else grabs it otherwise.if not bShowAmmoMenu then FollowGear:= HHGear;if ((Gear^.State and gsttmpFlag) = 0) or (HHGear^.dY < _0) then doStepHedgehogMoving(HHGear);if (Gear^.Health = 0) or (HHGear^.Damage <> 0) or CheckGearDrowning(HHGear) or (TurnTimeLeft = 0) // allow brief ground touches - to be fair on this, might need another counter or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and TestCollisionYwithGear(HHGear, 1)) or ((Gear^.Message and gm_Attack) <> 0) then begin with HHGear^ do begin Message:= 0; Active:= true; State:= State or gstMoving end; Gear^.State:= Gear^.State or gstAnimation or gstTmpFlag; if HHGear^.dY < _0 then begin Gear^.dX:= HHGear^.dX; Gear^.dY:= HHGear^.dY; end; Gear^.Timer:= 0; Gear^.doStep:= @doStepBirdyDisappear; CurAmmoGear:= nil; isCursorVisible:= false; AfterAttack; endend;////////////////////////////////////////////////////////////////////////////////procedure doStepBirdyDescend(Gear: PGear);var HHGear: PGear;beginif Gear^.Timer > 0 then dec(Gear^.Timer, 1)else if CurrentHedgehog = nil then begin DeleteGear(Gear); AfterAttack; exit end;HHGear:= CurrentHedgehog^.Gear;HHGear^.Message:= HHGear^.Message and not (gm_Up or gm_Precise or gm_Left or gm_Right);if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then begin if Gear^.Timer = 0 then Gear^.Y:= Gear^.Y + _0_1 endelse if Gear^.Timer = 0 then begin Gear^.doStep:= @doStepBirdyFly; HHGear^.dY:= -_0_2 endend;procedure doStepBirdyAppear(Gear: PGear);beginGear^.Pos:= 0;if Gear^.Timer < 2000 then inc(Gear^.Timer, 1)else begin Gear^.Timer:= 500; Gear^.dX:= _0; Gear^.dY:= _0; Gear^.State:= Gear^.State and not gstAnimation; Gear^.doStep:= @doStepBirdyDescend; endend;////////////////////////////////////////////////////////////////////////////////procedure doStepBirdy(Gear: PGear);var HHGear: PGear;begingear^.State:= gear^.State or gstAnimation and not gstTmpFlag;Gear^.doStep:= @doStepBirdyAppear;if CurrentHedgehog = nil then begin DeleteGear(Gear); exit end;HHGear:= CurrentHedgehog^.Gear;if HHGear^.dX.isNegative then Gear^.Tag:= -1else Gear^.Tag:= 1;Gear^.Pos:= 0;AllInactive:= false;FollowGear:= HHGear;with HHGear^ do begin State:= State and not gstAttacking; Message:= Message and not (gm_Attack or gm_Up or gm_Precise or gm_Left or gm_Right) endend;////////////////////////////////////////////////////////////////////////////////procedure doStepBigExplosionWork(Gear: PGear);var maxMovement: LongInt;begininc(Gear^.Timer);if (Gear^.Timer and 5) = 0 then begin maxMovement := max(1, 13 - ((Gear^.Timer * 15) div 250)); ShakeCamera(maxMovement); end;if Gear^.Timer > 250 then DeleteGear(Gear);end;procedure doStepBigExplosion(Gear: PGear);var i: LongWord;gX,gY: LongInt;begingX:= hwRound(Gear^.X);gY:= hwRound(Gear^.Y);AddVisualGear(gX, gY, vgtSmokeRing);for i:= 0 to 46 do AddVisualGear(gX, gY, vgtFire);for i:= 0 to 15 do AddVisualGear(gX, gY, vgtExplPart);for i:= 0 to 15 do AddVisualGear(gX, gY, vgtExplPart2);Gear^.doStep:= @doStepBigExplosionWorkend;////////////////////////////////////////////////////////////////////////////////procedure doStepEggWork(Gear: PGear);var vg: PVisualGear; i: LongInt;begin AllInactive:= false; Gear^.dX:= Gear^.dX; doStepFallingGear(Gear);// CheckGearDrowning(Gear); // already checked for in doStepFallingGear CalcRotationDirAngle(Gear); if (Gear^.State and gstCollision) <> 0 then begin doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, EXPLPoisoned or EXPLNoGfx); doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, EXPLPoisoned or EXPLNoGfx); PlaySound(sndEggBreak); AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg); vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg); if vg <> nil then vg^.Frame:= 2; for i:= 10 downto 0 do begin vg := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), hwRound(Gear^.Y) - 3 + Random(6), vgtDust); if vg <> nil then vg^.dX := vg^.dX + (Gear^.dX / 5); end; DeleteGear(Gear); exit end;end;procedure doStepPortal(Gear: PGear);begin(*A portal will have a few things it does.1) At first, it will move through the air until it collides with a surface. Once it does, it will stop. At this point we might try a check to verify there is enough terrain for it to be spawned against, and delete. Or we could just let it kinda stick out for now.2) From then on, if doStepPortal is called and a gear of a radius less than or equal to the portal is within X pixels of the portal (we could also check on moving toward the portal I guess, depends how accurate this needs to be) the portal will then locate the first other portal of the opposite type (there should only be one other one), and move that gear's X/Y to that other portal's location, and modify dX/dY to be relative to that other portal's orientation relative to this portal's orientation. This might require some tweaking with offsets of a few pixels to avoid getting gears stuck in land.3) At end of turn, all gtPortal will be deleted.*)(*Ok. Here's where I plan to go with this.1) Restrict portal gun to X shots.2) If on first shot, delete all existing gtPortal3) On any other shot, delete any existing portals of type X%2, and spawn a new portal of type X%2 oriented at angle 180° from the portal gun. It might possibly be worth linking portals with a Gear reference, to save time on scanning through the Gear list every time we need a portal.*)end;procedure doStepMovingPortal(Gear: PGear);var i, x, y: LongWord; oX, oY: hwFloat;beginoX:= Gear^.X;oY:= Gear^.Y;Gear^.X:= Gear^.X + Gear^.dX;Gear^.Y:= Gear^.Y + Gear^.dY;x:= hwRound(Gear^.X);y:= hwRound(Gear^.Y);(* Potential issue, portals embedded in land?Also, will need checks on how well portal is placedThought is possibly doing it based on midpoint and two ends, so a bit of rough terrain is still permitted, but not curves.*)if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then begin inc(Gear^.Tag); Gear^.doStep:= @doStepPortal; AfterAttack end// How laser checks for infiniteelse if (y > LAND_HEIGHT + LAND_WIDTH div 4) or (y < - LAND_WIDTH div 4) or (x > LAND_WIDTH + LAND_WIDTH div 4) or (x < - LAND_WIDTH div 4) then begin DeleteGear(Gear); AfterAttack end;end;procedure doStepPiano(Gear: PGear);var r0, r1: LongInt;beginAllInactive:= false;if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.Message and gm_Slot) <> 0) then begin case CurrentHedgehog^.Gear^.MsgParam of 0: PlaySound(sndPiano0); 1: PlaySound(sndPiano1); 2: PlaySound(sndPiano2); 3: PlaySound(sndPiano3); 4: PlaySound(sndPiano4); 5: PlaySound(sndPiano5); 6: PlaySound(sndPiano6); 7: PlaySound(sndPiano7); else PlaySound(sndPiano8); end; CurrentHedgehog^.Gear^.MsgParam:= 0; CurrentHedgehog^.Gear^.Message:= CurrentHedgehog^.Gear^.Message and not gm_Slot; end;if ((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or (Gear^.Pos = 20) then // bounce up to 20 times (3 times on gameflagged solid land) before dropping past landscape begin Gear^.dY:= Gear^.dY + cGravity * 3; Gear^.Y:= Gear^.Y + Gear^.dY; CheckGearDrowning(Gear); exit end;doStepFallingGear(Gear);if (Gear^.State and gstDrowning) <> 0 then ResumeMusicelse if (Gear^.State and gstCollision) <> 0 then begin r0:= GetRandom(21); r1:= GetRandom(21); doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, EXPLAutoSound); doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, EXPLAutoSound); doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, EXPLAutoSound); Gear^.dY:= -_1; Gear^.Pos:= Gear^.Pos + 1; endelse Gear^.dY:= Gear^.dY + cGravity * 2; // let it fall faster so itdoesn't take too long for the whole attackend;////////////////////////////////////////////////////////////////////////////////procedure doStepSineGunShotWork(Gear: PGear);var x, y, rX, rY, t, tmp, initHealth: LongWord; oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat; justCollided: boolean;beginAllInactive:= false;initHealth:= Gear^.Health;lX:= Gear^.X;lY:= Gear^.Y;ldX:= Gear^.dX;ldY:= Gear^.dY;sdy:= _0_5/Distance(Gear^.dX,Gear^.dY);ldX:= ldX * sdy;ldY:= ldY * sdy;sdY:= hwAbs(ldX) + hwAbs(ldY);sdX:= _1 - hwAbs(ldX/sdY);sdY:= _1 - hwAbs(ldY/sdY);if (ldX.isNegative = ldY.isNegative) then sdY:= -sdY;// initial angle depends on current GameTickst:= GameTicks mod 4096;// used for a work-around detection of area that is within land array, but outside bordersjustCollided:= false;repeat lX:= lX + ldX; lY:= lY + ldY; oX:= Gear^.X; oY:= Gear^.Y; rX:= hwRound(oX); rY:= hwRound(oY); tmp:= t mod 4096; amp:= _128 * (_1 - hwSqr(int2hwFloat(Gear^.Health)/initHealth)); sine:= amp * AngleSin(tmp mod 2048); sine.isNegative:= (tmp < 2048); inc(t,Gear^.Health div 313); Gear^.X:= lX + (sine * sdX); Gear^.Y:= ly + (sine * sdY); Gear^.dX:= Gear^.X - oX; Gear^.dY:= Gear^.Y - oY; x:= hwRound(Gear^.X); y:= hwRound(Gear^.Y); // if borders are on, stop outside land array if hasBorder and (((x and LAND_WIDTH_MASK) <> 0) or ((y and LAND_HEIGHT_MASK) <> 0)) then begin Gear^.Damage:= 0; Gear^.Health:= 0; end else begin if (rY <= cWaterLine) or (y <= cWaterLine) then begin if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then begin if justCollided then begin Gear^.Damage:= 0; Gear^.Health:= 0; end else begin inc(Gear^.Damage,3); justCollided:= true; end; end else justCollided:= false; // kick nearby hogs, dig tunnel and add some fire // if at least 5 collisions occured if Gear^.Damage > 0 then begin DrawExplosion(rX,rY,Gear^.Radius); // kick nearby hogs AmmoShove(Gear, 35, 50); dec(Gear^.Health, Gear^.Damage); Gear^.Damage:= 0; // add some fire to the tunnel if getRandom(6) = 0 then AddGear(x-Gear^.Radius+getRandom(2*Gear^.Radius), y-getRandom(Gear^.Radius+1), gtFlame, gsttmpFlag, _0, _0, 0); end; if getRandom(100) = 0 then AddGear(x, y, gtSmokeTrace, 0, _0, _0, 0); end // if underwater get additional damage else dec(Gear^.Health, 5); end; dec(Gear^.Health); // decrease bullet size towards the end if (Gear^.Radius > 4) then begin if (Gear^.Health <= (initHealth div 3)) then dec(Gear^.Radius) end else if (Gear^.Radius > 3) then begin if (Gear^.Health <= (initHealth div 4)) then dec(Gear^.Radius) end else if (Gear^.Radius > 2) then begin if (Gear^.Health <= (initHealth div 5)) then dec(Gear^.Radius) end else if (Gear^.Radius > 1) then begin if (Gear^.Health <= (initHealth div 6)) then dec(Gear^.Radius) end;until (Gear^.Health <= 0);DeleteGear(Gear);AfterAttack;end;procedure doStepSineGunShot(Gear: PGear);var HHGear: PGear;beginPlaySound(sndSineGun);// push the shooting Hedgehog backHHGear:= CurrentHedgehog^.Gear;Gear^.dX.isNegative:= not Gear^.dX.isNegative;Gear^.dY.isNegative:= not Gear^.dY.isNegative;HHGear^.dX:= Gear^.dX;HHGear^.dY:= Gear^.dY;AmmoShove(Gear, 0, 80);Gear^.dX.isNegative:= not Gear^.dX.isNegative;Gear^.dY.isNegative:= not Gear^.dY.isNegative;Gear^.doStep:= @doStepSineGunShotWorkend;